aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorMax Horn2006-02-11 22:45:04 +0000
committerMax Horn2006-02-11 22:45:04 +0000
commit26ee630756ebdd7c96bccede0881a8c8b98e8f2b (patch)
tree26e378d5cf990a2b81c2c96e9e683a7f333b62e8 /engines
parent2a9a0d4211b1ea5723f1409d91cb95de8984429e (diff)
downloadscummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.gz
scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.bz2
scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.zip
Moved engines to the new engines/ directory
svn-id: r20582
Diffstat (limited to 'engines')
-rw-r--r--engines/gob/anim.cpp37
-rw-r--r--engines/gob/anim.h41
-rw-r--r--engines/gob/cdrom.cpp240
-rw-r--r--engines/gob/cdrom.h53
-rw-r--r--engines/gob/dataio.cpp367
-rw-r--r--engines/gob/dataio.h70
-rw-r--r--engines/gob/draw.cpp874
-rw-r--r--engines/gob/draw.h139
-rw-r--r--engines/gob/driver_vga.cpp156
-rw-r--r--engines/gob/driver_vga.h44
-rw-r--r--engines/gob/game.cpp1952
-rw-r--r--engines/gob/game.h189
-rw-r--r--engines/gob/global.cpp152
-rw-r--r--engines/gob/global.h173
-rw-r--r--engines/gob/gob.cpp366
-rw-r--r--engines/gob/gob.h129
-rw-r--r--engines/gob/goblin.cpp2381
-rw-r--r--engines/gob/goblin.h231
-rw-r--r--engines/gob/init.cpp272
-rw-r--r--engines/gob/init.h47
-rw-r--r--engines/gob/inter.cpp443
-rw-r--r--engines/gob/inter.h313
-rw-r--r--engines/gob/inter_v1.cpp2624
-rw-r--r--engines/gob/inter_v2.cpp997
-rw-r--r--engines/gob/map.cpp785
-rw-r--r--engines/gob/map.h104
-rw-r--r--engines/gob/module.mk40
-rw-r--r--engines/gob/mult.cpp1214
-rw-r--r--engines/gob/mult.h208
-rw-r--r--engines/gob/music.cpp436
-rw-r--r--engines/gob/music.h96
-rw-r--r--engines/gob/pack.cpp108
-rw-r--r--engines/gob/pack.h36
-rw-r--r--engines/gob/palanim.cpp227
-rw-r--r--engines/gob/palanim.h47
-rw-r--r--engines/gob/parse.cpp420
-rw-r--r--engines/gob/parse.h75
-rw-r--r--engines/gob/parse_v1.cpp883
-rw-r--r--engines/gob/parse_v2.cpp942
-rw-r--r--engines/gob/scenery.cpp784
-rw-r--r--engines/gob/scenery.h148
-rw-r--r--engines/gob/sound.cpp145
-rw-r--r--engines/gob/sound.h104
-rw-r--r--engines/gob/timer.cpp37
-rw-r--r--engines/gob/timer.h43
-rw-r--r--engines/gob/util.cpp486
-rw-r--r--engines/gob/util.h106
-rw-r--r--engines/gob/video.cpp541
-rw-r--r--engines/gob/video.h141
-rw-r--r--engines/kyra/animator.cpp691
-rw-r--r--engines/kyra/animator.h126
-rw-r--r--engines/kyra/debugger.cpp206
-rw-r--r--engines/kyra/debugger.h57
-rw-r--r--engines/kyra/gui.cpp1020
-rw-r--r--engines/kyra/items.cpp982
-rw-r--r--engines/kyra/kyra.cpp1215
-rw-r--r--engines/kyra/kyra.h961
-rw-r--r--engines/kyra/module.mk33
-rw-r--r--engines/kyra/resource.cpp366
-rw-r--r--engines/kyra/resource.h95
-rw-r--r--engines/kyra/saveload.cpp319
-rw-r--r--engines/kyra/scene.cpp1581
-rw-r--r--engines/kyra/screen.cpp2084
-rw-r--r--engines/kyra/screen.h206
-rw-r--r--engines/kyra/script.cpp565
-rw-r--r--engines/kyra/script.h106
-rw-r--r--engines/kyra/script_v1.cpp1713
-rw-r--r--engines/kyra/seqplayer.cpp640
-rw-r--r--engines/kyra/seqplayer.h123
-rw-r--r--engines/kyra/sequences_v1.cpp1630
-rw-r--r--engines/kyra/sound.cpp477
-rw-r--r--engines/kyra/sound.h155
-rw-r--r--engines/kyra/sprites.cpp569
-rw-r--r--engines/kyra/sprites.h95
-rw-r--r--engines/kyra/staticres.cpp978
-rw-r--r--engines/kyra/text.cpp574
-rw-r--r--engines/kyra/text.h69
-rw-r--r--engines/kyra/timer.cpp280
-rw-r--r--engines/kyra/wsamovie.cpp206
-rw-r--r--engines/kyra/wsamovie.h85
-rw-r--r--engines/lure/animseq.cpp148
-rw-r--r--engines/lure/animseq.h56
-rw-r--r--engines/lure/debug-input.cpp133
-rw-r--r--engines/lure/debug-input.h42
-rw-r--r--engines/lure/debug-methods.cpp132
-rw-r--r--engines/lure/debug-methods.h39
-rw-r--r--engines/lure/decode.cpp360
-rw-r--r--engines/lure/decode.h62
-rw-r--r--engines/lure/disk.cpp174
-rw-r--r--engines/lure/disk.h66
-rw-r--r--engines/lure/events.cpp161
-rw-r--r--engines/lure/events.h78
-rw-r--r--engines/lure/game.cpp424
-rw-r--r--engines/lure/game.h64
-rw-r--r--engines/lure/hotspots.cpp806
-rw-r--r--engines/lure/hotspots.h137
-rw-r--r--engines/lure/intro.cpp151
-rw-r--r--engines/lure/intro.h45
-rw-r--r--engines/lure/lure.cpp306
-rw-r--r--engines/lure/lure.h73
-rw-r--r--engines/lure/luredefs.h183
-rw-r--r--engines/lure/memory.cpp107
-rw-r--r--engines/lure/memory.h63
-rw-r--r--engines/lure/menu.cpp416
-rw-r--r--engines/lure/menu.h92
-rw-r--r--engines/lure/module.mk36
-rw-r--r--engines/lure/palette.cpp142
-rw-r--r--engines/lure/palette.h68
-rw-r--r--engines/lure/res.cpp399
-rw-r--r--engines/lure/res.h94
-rw-r--r--engines/lure/res_struct.cpp309
-rw-r--r--engines/lure/res_struct.h401
-rw-r--r--engines/lure/room.cpp485
-rw-r--r--engines/lure/room.h107
-rw-r--r--engines/lure/screen.cpp152
-rw-r--r--engines/lure/screen.h64
-rw-r--r--engines/lure/scripts.cpp576
-rw-r--r--engines/lure/scripts.h117
-rw-r--r--engines/lure/strings.cpp300
-rw-r--r--engines/lure/strings.h67
-rw-r--r--engines/lure/surface.cpp456
-rw-r--r--engines/lure/surface.h83
-rw-r--r--engines/lure/system.cpp41
-rw-r--r--engines/lure/system.h40
-rw-r--r--engines/queen/bankman.cpp145
-rw-r--r--engines/queen/bankman.h87
-rw-r--r--engines/queen/command.cpp1255
-rw-r--r--engines/queen/command.h234
-rw-r--r--engines/queen/credits.cpp145
-rw-r--r--engines/queen/credits.h88
-rw-r--r--engines/queen/cutaway.cpp1324
-rw-r--r--engines/queen/cutaway.h265
-rw-r--r--engines/queen/debug.cpp219
-rw-r--r--engines/queen/debug.h69
-rw-r--r--engines/queen/defs.h322
-rw-r--r--engines/queen/display.cpp1307
-rw-r--r--engines/queen/display.h246
-rw-r--r--engines/queen/graphics.cpp1537
-rw-r--r--engines/queen/graphics.h293
-rw-r--r--engines/queen/grid.cpp255
-rw-r--r--engines/queen/grid.h136
-rw-r--r--engines/queen/input.cpp213
-rw-r--r--engines/queen/input.h178
-rw-r--r--engines/queen/journal.cpp587
-rw-r--r--engines/queen/journal.h208
-rw-r--r--engines/queen/logic.cpp2221
-rw-r--r--engines/queen/logic.h410
-rw-r--r--engines/queen/module.mk34
-rw-r--r--engines/queen/music.cpp344
-rw-r--r--engines/queen/music.h125
-rw-r--r--engines/queen/musicdata.cpp1944
-rw-r--r--engines/queen/queen.cpp454
-rw-r--r--engines/queen/queen.h164
-rw-r--r--engines/queen/resource.cpp287
-rw-r--r--engines/queen/resource.h169
-rw-r--r--engines/queen/restables.cpp1108
-rw-r--r--engines/queen/sound.cpp239
-rw-r--r--engines/queen/sound.h158
-rw-r--r--engines/queen/state.cpp130
-rw-r--r--engines/queen/state.h113
-rw-r--r--engines/queen/structs.h580
-rw-r--r--engines/queen/talk.cpp1812
-rw-r--r--engines/queen/talk.h244
-rw-r--r--engines/queen/walk.cpp564
-rw-r--r--engines/queen/walk.h144
-rw-r--r--engines/queen/xref.txt496
-rw-r--r--engines/saga/actor.cpp3092
-rw-r--r--engines/saga/actor.h778
-rw-r--r--engines/saga/animation.cpp720
-rw-r--r--engines/saga/animation.h198
-rw-r--r--engines/saga/console.cpp158
-rw-r--r--engines/saga/console.h62
-rw-r--r--engines/saga/events.cpp590
-rw-r--r--engines/saga/events.h179
-rw-r--r--engines/saga/font.cpp681
-rw-r--r--engines/saga/font.h205
-rw-r--r--engines/saga/font_map.cpp293
-rw-r--r--engines/saga/game.cpp1790
-rw-r--r--engines/saga/gfx.cpp485
-rw-r--r--engines/saga/gfx.h164
-rw-r--r--engines/saga/ihnm_introproc.cpp331
-rw-r--r--engines/saga/image.cpp439
-rw-r--r--engines/saga/input.cpp163
-rw-r--r--engines/saga/interface.cpp2453
-rw-r--r--engines/saga/interface.h466
-rw-r--r--engines/saga/isomap.cpp1694
-rw-r--r--engines/saga/isomap.h294
-rw-r--r--engines/saga/ite_introproc.cpp1028
-rw-r--r--engines/saga/itedata.cpp484
-rw-r--r--engines/saga/itedata.h113
-rw-r--r--engines/saga/list.h156
-rw-r--r--engines/saga/module.mk44
-rw-r--r--engines/saga/music.cpp524
-rw-r--r--engines/saga/music.h148
-rw-r--r--engines/saga/objectmap.cpp267
-rw-r--r--engines/saga/objectmap.h128
-rw-r--r--engines/saga/palanim.cpp209
-rw-r--r--engines/saga/palanim.h63
-rw-r--r--engines/saga/puzzle.cpp560
-rw-r--r--engines/saga/puzzle.h120
-rw-r--r--engines/saga/render.cpp189
-rw-r--r--engines/saga/render.h93
-rw-r--r--engines/saga/resnames.h253
-rw-r--r--engines/saga/rscfile.cpp615
-rw-r--r--engines/saga/rscfile.h173
-rw-r--r--engines/saga/saga.cpp505
-rw-r--r--engines/saga/saga.h741
-rw-r--r--engines/saga/saveload.cpp299
-rw-r--r--engines/saga/scene.cpp1283
-rw-r--r--engines/saga/scene.h370
-rw-r--r--engines/saga/script.cpp806
-rw-r--r--engines/saga/script.h637
-rw-r--r--engines/saga/sfuncs.cpp2038
-rw-r--r--engines/saga/sndres.cpp297
-rw-r--r--engines/saga/sndres.h68
-rw-r--r--engines/saga/sound.cpp149
-rw-r--r--engines/saga/sound.h97
-rw-r--r--engines/saga/sprite.cpp444
-rw-r--r--engines/saga/sprite.h102
-rw-r--r--engines/saga/sthread.cpp746
-rw-r--r--engines/saga/stream.h57
-rw-r--r--engines/saga/xref.txt139
-rw-r--r--engines/scumm/actor.cpp2253
-rw-r--r--engines/scumm/actor.h294
-rw-r--r--engines/scumm/akos.cpp1869
-rw-r--r--engines/scumm/akos.h125
-rw-r--r--engines/scumm/base-costume.cpp92
-rw-r--r--engines/scumm/base-costume.h168
-rw-r--r--engines/scumm/bomp.cpp393
-rw-r--r--engines/scumm/bomp.h39
-rw-r--r--engines/scumm/boxes.cpp1271
-rw-r--r--engines/scumm/boxes.h54
-rw-r--r--engines/scumm/camera.cpp367
-rw-r--r--engines/scumm/charset.cpp1827
-rw-r--r--engines/scumm/charset.h202
-rw-r--r--engines/scumm/costume.cpp1160
-rw-r--r--engines/scumm/costume.h141
-rw-r--r--engines/scumm/cursor.cpp520
-rw-r--r--engines/scumm/debugger.cpp915
-rw-r--r--engines/scumm/debugger.h78
-rw-r--r--engines/scumm/dialogs.cpp986
-rw-r--r--engines/scumm/dialogs.h211
-rw-r--r--engines/scumm/floodfill_he.cpp293
-rw-r--r--engines/scumm/floodfill_he.h67
-rw-r--r--engines/scumm/gfx.cpp3307
-rw-r--r--engines/scumm/gfx.h301
-rw-r--r--engines/scumm/help.cpp350
-rw-r--r--engines/scumm/help.h45
-rw-r--r--engines/scumm/imuse.cpp2043
-rw-r--r--engines/scumm/imuse.h85
-rw-r--r--engines/scumm/imuse_digi/dimuse.cpp412
-rw-r--r--engines/scumm/imuse_digi/dimuse.h239
-rw-r--r--engines/scumm/imuse_digi/dimuse_bndmgr.cpp344
-rw-r--r--engines/scumm/imuse_digi/dimuse_bndmgr.h115
-rw-r--r--engines/scumm/imuse_digi/dimuse_codecs.cpp655
-rw-r--r--engines/scumm/imuse_digi/dimuse_music.cpp444
-rw-r--r--engines/scumm/imuse_digi/dimuse_script.cpp409
-rw-r--r--engines/scumm/imuse_digi/dimuse_sndmgr.cpp625
-rw-r--r--engines/scumm/imuse_digi/dimuse_sndmgr.h139
-rw-r--r--engines/scumm/imuse_digi/dimuse_tables.cpp881
-rw-r--r--engines/scumm/imuse_digi/dimuse_track.cpp368
-rw-r--r--engines/scumm/imuse_internal.h472
-rw-r--r--engines/scumm/imuse_player.cpp1241
-rw-r--r--engines/scumm/input.cpp467
-rw-r--r--engines/scumm/insane/insane.cpp1466
-rw-r--r--engines/scumm/insane/insane.h456
-rw-r--r--engines/scumm/insane/insane_ben.cpp2009
-rw-r--r--engines/scumm/insane/insane_enemy.cpp2813
-rw-r--r--engines/scumm/insane/insane_iact.cpp558
-rw-r--r--engines/scumm/insane/insane_scenes.cpp1499
-rw-r--r--engines/scumm/instrument.cpp462
-rw-r--r--engines/scumm/instrument.h79
-rw-r--r--engines/scumm/intern.h912
-rw-r--r--engines/scumm/intern_he.h606
-rw-r--r--engines/scumm/logic_he.cpp772
-rw-r--r--engines/scumm/logic_he.h111
-rw-r--r--engines/scumm/midiparser_eup.cpp219
-rw-r--r--engines/scumm/midiparser_ro.cpp143
-rw-r--r--engines/scumm/module.mk110
-rw-r--r--engines/scumm/music.h91
-rw-r--r--engines/scumm/nut_renderer.cpp303
-rw-r--r--engines/scumm/nut_renderer.h64
-rw-r--r--engines/scumm/object.cpp1779
-rw-r--r--engines/scumm/object.h182
-rw-r--r--engines/scumm/palette.cpp969
-rw-r--r--engines/scumm/palette_he.cpp317
-rw-r--r--engines/scumm/player_mod.cpp181
-rw-r--r--engines/scumm/player_mod.h97
-rw-r--r--engines/scumm/player_nes.cpp1085
-rw-r--r--engines/scumm/player_nes.h115
-rw-r--r--engines/scumm/player_v1.cpp608
-rw-r--r--engines/scumm/player_v1.h99
-rw-r--r--engines/scumm/player_v2.cpp1005
-rw-r--r--engines/scumm/player_v2.h167
-rw-r--r--engines/scumm/player_v2a.cpp1420
-rw-r--r--engines/scumm/player_v2a.h74
-rw-r--r--engines/scumm/player_v3a.cpp347
-rw-r--r--engines/scumm/player_v3a.h101
-rw-r--r--engines/scumm/resource.cpp1616
-rw-r--r--engines/scumm/resource.h46
-rw-r--r--engines/scumm/resource_v2.cpp208
-rw-r--r--engines/scumm/resource_v3.cpp121
-rw-r--r--engines/scumm/resource_v4.cpp193
-rw-r--r--engines/scumm/resource_v7he.cpp1912
-rw-r--r--engines/scumm/resource_v7he.h556
-rw-r--r--engines/scumm/room.cpp751
-rw-r--r--engines/scumm/saveload.cpp1648
-rw-r--r--engines/scumm/saveload.h169
-rw-r--r--engines/scumm/script.cpp1355
-rw-r--r--engines/scumm/script.h84
-rw-r--r--engines/scumm/script_c64.cpp849
-rw-r--r--engines/scumm/script_v100he.cpp2958
-rw-r--r--engines/scumm/script_v2.cpp1629
-rw-r--r--engines/scumm/script_v5.cpp2898
-rw-r--r--engines/scumm/script_v6.cpp3183
-rw-r--r--engines/scumm/script_v6he.cpp1315
-rw-r--r--engines/scumm/script_v72he.cpp2352
-rw-r--r--engines/scumm/script_v7he.cpp1133
-rw-r--r--engines/scumm/script_v8.cpp1496
-rw-r--r--engines/scumm/script_v80he.cpp811
-rw-r--r--engines/scumm/script_v90he.cpp2636
-rw-r--r--engines/scumm/scumm-md5.h467
-rw-r--r--engines/scumm/scumm.cpp3472
-rw-r--r--engines/scumm/scumm.h1369
-rw-r--r--engines/scumm/smush/channel.h148
-rw-r--r--engines/scumm/smush/chunk.cpp242
-rw-r--r--engines/scumm/smush/chunk.h110
-rw-r--r--engines/scumm/smush/chunk_type.h60
-rw-r--r--engines/scumm/smush/codec1.cpp62
-rw-r--r--engines/scumm/smush/codec37.cpp588
-rw-r--r--engines/scumm/smush/codec37.h62
-rw-r--r--engines/scumm/smush/codec47.cpp630
-rw-r--r--engines/scumm/smush/codec47.h65
-rw-r--r--engines/scumm/smush/imuse_channel.cpp347
-rw-r--r--engines/scumm/smush/saud_channel.cpp268
-rw-r--r--engines/scumm/smush/smush_font.cpp269
-rw-r--r--engines/scumm/smush/smush_font.h54
-rw-r--r--engines/scumm/smush/smush_mixer.cpp163
-rw-r--r--engines/scumm/smush/smush_mixer.h64
-rw-r--r--engines/scumm/smush/smush_player.cpp1359
-rw-r--r--engines/scumm/smush/smush_player.h148
-rw-r--r--engines/scumm/sound.cpp2364
-rw-r--r--engines/scumm/sound.h185
-rw-r--r--engines/scumm/sound_he.cpp516
-rw-r--r--engines/scumm/sprite_he.cpp1440
-rw-r--r--engines/scumm/sprite_he.h222
-rw-r--r--engines/scumm/string.cpp1247
-rw-r--r--engines/scumm/thumbnail.cpp140
-rw-r--r--engines/scumm/usage_bits.cpp95
-rw-r--r--engines/scumm/usage_bits.h35
-rw-r--r--engines/scumm/util.cpp1818
-rw-r--r--engines/scumm/util.h175
-rw-r--r--engines/scumm/vars.cpp736
-rw-r--r--engines/scumm/verbs.cpp855
-rw-r--r--engines/scumm/verbs.h50
-rw-r--r--engines/scumm/wiz_he.cpp2088
-rw-r--r--engines/scumm/wiz_he.h211
-rw-r--r--engines/simon/charset.cpp1277
-rw-r--r--engines/simon/cursor.cpp246
-rw-r--r--engines/simon/debug.cpp462
-rw-r--r--engines/simon/debug.h1530
-rw-r--r--engines/simon/debugger.cpp206
-rw-r--r--engines/simon/debugger.h56
-rw-r--r--engines/simon/game.cpp1186
-rw-r--r--engines/simon/icons.cpp229
-rw-r--r--engines/simon/intern.h217
-rw-r--r--engines/simon/items.cpp1685
-rw-r--r--engines/simon/midi.cpp546
-rw-r--r--engines/simon/midi.h127
-rw-r--r--engines/simon/midiparser_s1d.cpp155
-rw-r--r--engines/simon/module.mk29
-rw-r--r--engines/simon/res.cpp386
-rw-r--r--engines/simon/saveload.cpp634
-rw-r--r--engines/simon/simon.cpp4292
-rw-r--r--engines/simon/simon.h903
-rw-r--r--engines/simon/sound.cpp555
-rw-r--r--engines/simon/sound.h80
-rw-r--r--engines/simon/verb.cpp608
-rw-r--r--engines/simon/vga.cpp2299
-rw-r--r--engines/simon/vga.h129
-rw-r--r--engines/sky/autoroute.cpp285
-rw-r--r--engines/sky/autoroute.h59
-rw-r--r--engines/sky/compact.cpp452
-rw-r--r--engines/sky/compact.h93
-rw-r--r--engines/sky/control.cpp1665
-rw-r--r--engines/sky/control.h293
-rw-r--r--engines/sky/debug.cpp1487
-rw-r--r--engines/sky/debug.h76
-rw-r--r--engines/sky/disk.cpp375
-rw-r--r--engines/sky/disk.h76
-rw-r--r--engines/sky/grid.cpp260
-rw-r--r--engines/sky/grid.h68
-rw-r--r--engines/sky/hufftext.cpp2046
-rw-r--r--engines/sky/intro.cpp926
-rw-r--r--engines/sky/intro.h74
-rw-r--r--engines/sky/logic.cpp2570
-rw-r--r--engines/sky/logic.h338
-rw-r--r--engines/sky/module.mk37
-rw-r--r--engines/sky/mouse.cpp323
-rw-r--r--engines/sky/mouse.h92
-rw-r--r--engines/sky/music/adlibchannel.cpp344
-rw-r--r--engines/sky/music/adlibchannel.h106
-rw-r--r--engines/sky/music/adlibmusic.cpp121
-rw-r--r--engines/sky/music/adlibmusic.h64
-rw-r--r--engines/sky/music/gmchannel.cpp217
-rw-r--r--engines/sky/music/gmchannel.h82
-rw-r--r--engines/sky/music/gmmusic.cpp118
-rw-r--r--engines/sky/music/gmmusic.h52
-rw-r--r--engines/sky/music/mt32music.cpp169
-rw-r--r--engines/sky/music/mt32music.h53
-rw-r--r--engines/sky/music/musicbase.cpp148
-rw-r--r--engines/sky/music/musicbase.h92
-rw-r--r--engines/sky/rnc_deco.cpp262
-rw-r--r--engines/sky/rnc_deco.h63
-rw-r--r--engines/sky/screen.cpp822
-rw-r--r--engines/sky/screen.h134
-rw-r--r--engines/sky/sky.cpp556
-rw-r--r--engines/sky/sky.h112
-rw-r--r--engines/sky/skydefs.h4314
-rw-r--r--engines/sky/sound.cpp1259
-rw-r--r--engines/sky/sound.h95
-rw-r--r--engines/sky/struc.h175
-rw-r--r--engines/sky/text.cpp703
-rw-r--r--engines/sky/text.h124
-rw-r--r--engines/sword1/animation.cpp307
-rw-r--r--engines/sword1/animation.h120
-rw-r--r--engines/sword1/collision.h50
-rw-r--r--engines/sword1/control.cpp1301
-rw-r--r--engines/sword1/control.h152
-rw-r--r--engines/sword1/credits.cpp346
-rw-r--r--engines/sword1/credits.h72
-rw-r--r--engines/sword1/debug.cpp140
-rw-r--r--engines/sword1/debug.h42
-rw-r--r--engines/sword1/eventman.cpp103
-rw-r--r--engines/sword1/eventman.h51
-rw-r--r--engines/sword1/logic.cpp1803
-rw-r--r--engines/sword1/logic.h239
-rw-r--r--engines/sword1/memman.cpp129
-rw-r--r--engines/sword1/memman.h64
-rw-r--r--engines/sword1/menu.cpp393
-rw-r--r--engines/sword1/menu.h109
-rw-r--r--engines/sword1/module.mk32
-rw-r--r--engines/sword1/mouse.cpp313
-rw-r--r--engines/sword1/mouse.h113
-rw-r--r--engines/sword1/music.cpp352
-rw-r--r--engines/sword1/music.h122
-rw-r--r--engines/sword1/object.h130
-rw-r--r--engines/sword1/objectman.cpp163
-rw-r--r--engines/sword1/objectman.h66
-rw-r--r--engines/sword1/resman.cpp421
-rw-r--r--engines/sword1/resman.h98
-rw-r--r--engines/sword1/router.cpp2606
-rw-r--r--engines/sword1/router.h180
-rw-r--r--engines/sword1/screen.cpp986
-rw-r--r--engines/sword1/screen.h157
-rw-r--r--engines/sword1/sound.cpp382
-rw-r--r--engines/sword1/sound.h127
-rw-r--r--engines/sword1/staticres.cpp7168
-rw-r--r--engines/sword1/sword1.cpp615
-rw-r--r--engines/sword1/sword1.h110
-rw-r--r--engines/sword1/sworddefs.h1606
-rw-r--r--engines/sword1/swordres.h5223
-rw-r--r--engines/sword1/text.cpp191
-rw-r--r--engines/sword1/text.h65
-rw-r--r--engines/sword2/_mouse.cpp247
-rw-r--r--engines/sword2/animation.cpp535
-rw-r--r--engines/sword2/animation.h103
-rw-r--r--engines/sword2/anims.cpp308
-rw-r--r--engines/sword2/build_display.cpp1070
-rw-r--r--engines/sword2/build_display.h441
-rw-r--r--engines/sword2/console.cpp826
-rw-r--r--engines/sword2/console.h127
-rw-r--r--engines/sword2/controls.cpp1416
-rw-r--r--engines/sword2/controls.h183
-rw-r--r--engines/sword2/d_draw.cpp71
-rw-r--r--engines/sword2/debug.cpp377
-rw-r--r--engines/sword2/debug.h33
-rw-r--r--engines/sword2/defs.h205
-rw-r--r--engines/sword2/events.cpp99
-rw-r--r--engines/sword2/function.cpp2479
-rw-r--r--engines/sword2/header.h502
-rw-r--r--engines/sword2/icons.cpp216
-rw-r--r--engines/sword2/icons.h41
-rw-r--r--engines/sword2/interpreter.cpp753
-rw-r--r--engines/sword2/interpreter.h96
-rw-r--r--engines/sword2/layers.cpp191
-rw-r--r--engines/sword2/layers.h29
-rw-r--r--engines/sword2/logic.cpp267
-rw-r--r--engines/sword2/logic.h316
-rw-r--r--engines/sword2/maketext.cpp579
-rw-r--r--engines/sword2/maketext.h119
-rw-r--r--engines/sword2/memory.cpp244
-rw-r--r--engines/sword2/memory.h70
-rw-r--r--engines/sword2/menu.cpp291
-rw-r--r--engines/sword2/module.mk48
-rw-r--r--engines/sword2/mouse.cpp1437
-rw-r--r--engines/sword2/mouse.h257
-rw-r--r--engines/sword2/music.cpp856
-rw-r--r--engines/sword2/object.h342
-rw-r--r--engines/sword2/palette.cpp267
-rw-r--r--engines/sword2/protocol.cpp216
-rw-r--r--engines/sword2/rdwin.cpp118
-rw-r--r--engines/sword2/render.cpp584
-rw-r--r--engines/sword2/resman.cpp633
-rw-r--r--engines/sword2/resman.h134
-rw-r--r--engines/sword2/router.cpp2506
-rw-r--r--engines/sword2/router.h255
-rw-r--r--engines/sword2/save_rest.cpp414
-rw-r--r--engines/sword2/save_rest.h47
-rw-r--r--engines/sword2/scroll.cpp151
-rw-r--r--engines/sword2/sound.cpp319
-rw-r--r--engines/sword2/sound.h272
-rw-r--r--engines/sword2/speech.cpp229
-rw-r--r--engines/sword2/sprite.cpp655
-rw-r--r--engines/sword2/startup.cpp173
-rw-r--r--engines/sword2/sword2.cpp689
-rw-r--r--engines/sword2/sword2.h240
-rw-r--r--engines/sword2/sync.cpp76
-rw-r--r--engines/sword2/walker.cpp454
519 files changed, 286340 insertions, 0 deletions
diff --git a/engines/gob/anim.cpp b/engines/gob/anim.cpp
new file mode 100644
index 0000000000..a51befe3c8
--- /dev/null
+++ b/engines/gob/anim.cpp
@@ -0,0 +1,37 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/video.h"
+#include "gob/anim.h"
+
+namespace Gob {
+
+Anim::Anim() {
+ _areaLeft = 0;
+ _areaTop = 0;
+ _areaWidth = 0;
+ _areaHeight = 0;
+ _animSurf = 0;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/anim.h b/engines/gob/anim.h
new file mode 100644
index 0000000000..f5252364b9
--- /dev/null
+++ b/engines/gob/anim.h
@@ -0,0 +1,41 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_ANIM_H
+#define GOB_ANIM_H
+
+namespace Gob {
+
+class Anim {
+public:
+ int16 _areaLeft;
+ int16 _areaTop;
+ int16 _areaWidth;
+ int16 _areaHeight;
+ Video::SurfaceDesc *_animSurf;
+
+ Anim();
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/cdrom.cpp b/engines/gob/cdrom.cpp
new file mode 100644
index 0000000000..bda2081404
--- /dev/null
+++ b/engines/gob/cdrom.cpp
@@ -0,0 +1,240 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/cdrom.h"
+#include "gob/dataio.h"
+#include "gob/game.h"
+#include "gob/global.h"
+#include "gob/util.h"
+#include "sound/audiocd.h"
+
+namespace Gob {
+
+CDROM::CDROM(GobEngine *vm) : _vm(vm) {
+ _cdPlaying = false;
+ _LICbuffer = 0;
+ for (int i = 0; i < 16; i++)
+ _curTrack[i] = 0;
+ _numTracks = 0;
+ _trackStop = 0;
+ _startTime = 0;
+}
+
+void CDROM::readLIC(const char *fname) {
+ char tmp[80];
+ int handle;
+ uint16 version, startChunk, pos;
+
+ freeLICbuffer();
+
+ *_curTrack = 0;
+
+ strcpy(tmp, fname);
+
+ handle = _vm->_dataio->openData(tmp);
+
+ if (handle == -1)
+ return;
+
+ _vm->_dataio->closeData(handle);
+
+ _vm->_dataio->getUnpackedData(tmp);
+
+ handle = _vm->_dataio->openData(tmp);
+
+ _vm->_dataio->readData(handle, (char *)&version, 2);
+ version = READ_LE_UINT16(&version);
+
+ _vm->_dataio->readData(handle, (char *)&startChunk, 2);
+ startChunk = READ_LE_UINT16(&startChunk);
+
+ _vm->_dataio->readData(handle, (char *)&_numTracks, 2);
+ _numTracks = READ_LE_UINT16(&_numTracks);
+
+ if (version != 3) {
+ error("Wrong file %s (%d)", fname, version);
+ return;
+ }
+
+ _vm->_dataio->seekData(handle, 50, SEEK_SET);
+
+ for (int i = 0; i < startChunk; i++) {
+ _vm->_dataio->readData(handle, (char *)&pos, 2);
+ pos = READ_LE_UINT16(&pos);
+
+ if (!pos)
+ break;
+
+ _vm->_dataio->seekData(handle, pos, SEEK_CUR);
+ }
+
+ _LICbuffer = new byte[_numTracks * 22];
+ _vm->_dataio->readData(handle, (char *)_LICbuffer, _numTracks * 22);
+
+ _vm->_dataio->closeData(handle);
+}
+
+void CDROM::freeLICbuffer(void) {
+ delete[] _LICbuffer;
+ _LICbuffer = 0;
+}
+
+void CDROM::playBgMusic() {
+ static const char *tracks[][2] = {
+ {"avt00.tot", "mine"},
+ {"avt001.tot", "nuit"},
+ {"avt002.tot", "campagne"},
+ {"avt003.tot", "extsor1"},
+ {"avt004.tot", "interieure"},
+ {"avt005.tot", "zombie"},
+ {"avt006.tot", "zombie"},
+ {"avt007.tot", "campagne"},
+ {"avt008.tot", "campagne"},
+ {"avt009.tot", "extsor1"},
+ {"avt010.tot", "extsor1"},
+ {"avt011.tot", "interieure"},
+ {"avt012.tot", "zombie"},
+ {"avt014.tot", "nuit"},
+ {"avt015.tot", "interieure"},
+ {"avt016.tot", "statue"},
+ {"avt017.tot", "zombie"},
+ {"avt018.tot", "statue"},
+ {"avt019.tot", "mine"},
+ {"avt020.tot", "statue"},
+ {"avt021.tot", "mine"},
+ {"avt022.tot", "zombie"}
+ };
+
+ for (int i = 0; i < ARRAYSIZE(tracks); i++)
+ if (!scumm_stricmp(_vm->_game->_curTotFile, tracks[i][0])) {
+ startTrack(tracks[i][1]);
+ break;
+ }
+}
+
+void CDROM::playMultMusic() {
+ static const char *tracks[][6] = {
+ {"avt005.tot", "fra1", "all1", "ang1", "esp1", "ita1"},
+ {"avt006.tot", "fra2", "all2", "ang2", "esp2", "ita2"},
+ {"avt012.tot", "fra3", "all3", "ang3", "esp3", "ita3"},
+ {"avt016.tot", "fra4", "all4", "ang4", "esp4", "ita4"},
+ {"avt019.tot", "fra5", "all5", "ang5", "esp5", "ita5"},
+ {"avt022.tot", "fra6", "all6", "ang6", "esp6", "ita6"}
+ };
+
+ for (int i = 0; i < ARRAYSIZE(tracks); i++)
+ if (!scumm_stricmp(_vm->_game->_curTotFile, tracks[i][0])) {
+ _cdPlaying = true;
+ startTrack(tracks[i][_vm->_global->_language + 1]);
+ break;
+ }
+}
+
+void CDROM::startTrack(const char *trackname) {
+ byte *curPtr, *matchPtr;
+
+ if (!_LICbuffer)
+ return;
+
+ debug(3, "startTrack(%s)", trackname);
+
+ matchPtr = 0;
+ curPtr = _LICbuffer;
+
+ for (int i = 0; i < _numTracks; i++) {
+ if (!scumm_stricmp((char *)curPtr, trackname)) {
+ matchPtr = curPtr;
+ break;
+ }
+ curPtr += 22;
+ }
+
+ if (!matchPtr) {
+ error("Track %s not found", trackname);
+ return;
+ }
+
+ strcpy(_curTrack, trackname);
+
+ stopPlaying();
+
+ while (getTrackPos() != -1);
+
+ uint32 start, end;
+
+ start = READ_LE_UINT32(matchPtr + 12);
+ end = READ_LE_UINT32(matchPtr + 16);
+
+ play(start, end);
+
+ _startTime = _vm->_util->getTimeKey();
+ _trackStop = _startTime + (end - start + 1 + 150) * 40 / 3;
+}
+
+void CDROM::play(uint32 from, uint32 to) {
+ // play from sector [from] to sector [to]
+ //
+ // format is HSG:
+ // HSG encodes frame information into a double word:
+ // minute multiplied by 4500, plus second multiplied by 75,
+ // plus frame, minus 150
+ debug(3, "play(%d, %d)", from, to);
+
+ AudioCD.play(1, 0, from, to - from + 1);
+}
+
+int32 CDROM::getTrackPos(void) {
+ uint32 curPos = _vm->_util->getTimeKey() - _startTime;
+
+ if (AudioCD.isPlaying() && (_vm->_util->getTimeKey() < _trackStop))
+ return curPos * 3 / 40;
+ else
+ return -1;
+}
+
+void CDROM::stopPlaying(void) {
+ stop();
+
+ while (getTrackPos() != -1);
+}
+
+void CDROM::stop(void) {
+ debug(3, "stop()");
+
+ AudioCD.stop();
+}
+
+void CDROM::testCD(int trySubst, const char *label) {
+ if (!trySubst) {
+ error("CDROM track substitution is not supported");
+ return;
+ }
+
+ _LICbuffer = 0;
+ _cdPlaying = false;
+
+ // Original checked CD label here
+ // but will skip it as it will require OSystem extensions of direct
+ // CD secor reading
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/cdrom.h b/engines/gob/cdrom.h
new file mode 100644
index 0000000000..582a4a553a
--- /dev/null
+++ b/engines/gob/cdrom.h
@@ -0,0 +1,53 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+
+namespace Gob {
+
+class CDROM {
+public:
+ bool _cdPlaying;
+
+ void readLIC(const char *fname);
+ void freeLICbuffer(void);
+
+ void startTrack(const char *s);
+ void playBgMusic();
+ void playMultMusic();
+ void play(uint32 from, uint32 to);
+ int32 getTrackPos(void);
+ void stopPlaying(void);
+ void stop(void);
+ void testCD(int trySubst, const char *label);
+
+ CDROM(GobEngine *vm);
+
+protected:
+ byte *_LICbuffer;
+ char _curTrack[16];
+ uint16 _numTracks;
+ uint32 _trackStop;
+ uint32 _startTime;
+ GobEngine *_vm;
+};
+
+} // End of namespace Gob
diff --git a/engines/gob/dataio.cpp b/engines/gob/dataio.cpp
new file mode 100644
index 0000000000..b66de4a497
--- /dev/null
+++ b/engines/gob/dataio.cpp
@@ -0,0 +1,367 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/dataio.h"
+#include "gob/pack.h"
+
+namespace Gob {
+
+DataIO::DataIO(GobEngine *vm) : _vm(vm) {
+}
+
+Common::File *DataIO::file_getHandle(int16 handle) {
+ return &_vm->_global->_filesHandles[handle];
+}
+
+int16 DataIO::file_open(const char *path, Common::File::AccessMode mode) {
+ int16 i;
+
+ for (i = 0; i < MAX_FILES; i++) {
+ if (!file_getHandle(i)->isOpen())
+ break;
+ }
+ if (i == MAX_FILES)
+ return -1;
+
+ file_getHandle(i)->open(path, mode);
+
+ if (file_getHandle(i)->isOpen())
+ return i;
+
+ return -1;
+}
+
+int16 DataIO::getChunk(const char *chunkName) {
+ int16 file;
+ int16 slot;
+ int16 chunk;
+ struct ChunkDesc *dataDesc;
+
+ for (file = 0; file < MAX_DATA_FILES; file++) {
+ if (_vm->_global->_dataFiles[file] == 0)
+ return -1;
+
+ for (slot = 0; slot < MAX_SLOT_COUNT; slot++)
+ if (_vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot] == -1)
+ break;
+
+ if (slot == MAX_SLOT_COUNT)
+ return -1;
+
+ dataDesc = _vm->_global->_dataFiles[file];
+ for (chunk = 0; chunk < _vm->_global->_numDataChunks[file];
+ chunk++, dataDesc++) {
+ if (scumm_stricmp(chunkName, dataDesc->chunkName) != 0)
+ continue;
+
+ _vm->_global->_isCurrentSlot[file * MAX_SLOT_COUNT + slot] = 0;
+ _vm->_global->_chunkSize[file * MAX_SLOT_COUNT + slot] =
+ dataDesc->size;
+ _vm->_global->_chunkOffset[file * MAX_SLOT_COUNT + slot] =
+ dataDesc->offset;
+ _vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot] = 0;
+ return file * 10 + slot + 50;
+ }
+ }
+ return -1;
+}
+
+char DataIO::freeChunk(int16 handle) {
+ if (handle >= 50 && handle < 100) {
+ handle -= 50;
+ _vm->_global->_chunkPos[(handle / 10) * MAX_SLOT_COUNT + (handle % 10)] = -1;
+ return 0;
+ }
+ return 1;
+}
+
+int32 DataIO::readChunk(int16 handle, char *buf, int16 size) {
+ int16 file;
+ int16 slot;
+ int16 i;
+ int32 offset;
+
+ if (handle < 50 || handle >= 100)
+ return -2;
+
+ file = (handle - 50) / 10;
+ slot = (handle - 50) % 10;
+ if (_vm->_global->_isCurrentSlot[file * MAX_SLOT_COUNT + slot] == 0) {
+ for (i = 0; i < MAX_SLOT_COUNT; i++)
+ _vm->_global->_isCurrentSlot[file * MAX_SLOT_COUNT + i] = 0;
+
+ offset =
+ _vm->_global->_chunkOffset[file * MAX_SLOT_COUNT + slot] +
+ _vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot];
+ debug(7, "seek: %ld, %ld", _vm->_global->_chunkOffset[file * MAX_SLOT_COUNT + slot], _vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot]);
+ file_getHandle(_vm->_global->_dataFileHandles[file])->seek(offset, SEEK_SET);
+ }
+
+ _vm->_global->_isCurrentSlot[file * MAX_SLOT_COUNT + slot] = 1;
+ if (_vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot] + size >
+ _vm->_global->_chunkSize[file * MAX_SLOT_COUNT + slot])
+ size =
+ _vm->_global->_chunkSize[file * MAX_SLOT_COUNT + slot] -
+ _vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot];
+
+ file_getHandle(_vm->_global->_dataFileHandles[file])->read(buf, size);
+ _vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot] += size;
+ return size;
+}
+
+int16 DataIO::seekChunk(int16 handle, int32 pos, int16 from) {
+ int16 file;
+ int16 slot;
+
+ if (handle < 50 || handle >= 100)
+ return -1;
+
+ file = (handle - 50) / 10;
+ slot = (handle - 50) % 10;
+ _vm->_global->_isCurrentSlot[file * MAX_SLOT_COUNT + slot] = 0;
+ if (from == SEEK_SET)
+ _vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot] = pos;
+ else
+ _vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot] += pos;
+
+ return _vm->_global->_chunkPos[file * MAX_SLOT_COUNT + slot];
+}
+
+int32 DataIO::getChunkSize(const char *chunkName) {
+ int16 file;
+ int16 chunk;
+ struct ChunkDesc *dataDesc;
+ int16 slot;
+ int32 realSize;
+
+ for (file = 0; file < MAX_DATA_FILES; file++) {
+ if (_vm->_global->_dataFiles[file] == 0)
+ return -1;
+
+ dataDesc = _vm->_global->_dataFiles[file];
+ for (chunk = 0; chunk < _vm->_global->_numDataChunks[file];
+ chunk++, dataDesc++) {
+ if (scumm_stricmp(chunkName, dataDesc->chunkName) != 0)
+ continue;
+
+ if (dataDesc->packed == 0) {
+ _vm->_global->_packedSize = -1;
+ return dataDesc->size;
+ }
+
+ for (slot = 0; slot < MAX_SLOT_COUNT; slot++)
+ _vm->_global->_isCurrentSlot[slot] = 0;
+
+ file_getHandle(_vm->_global->_dataFileHandles[file])->seek(dataDesc->offset, SEEK_SET);
+ realSize = file_getHandle(_vm->_global->_dataFileHandles[file])->readUint32LE();
+ _vm->_global->_packedSize = dataDesc->size;
+ return realSize;
+ }
+ }
+ return -1;
+}
+
+void DataIO::openDataFile(const char *src) {
+ char path[128];
+ int16 i;
+ int16 file;
+ ChunkDesc *dataDesc;
+
+ strcpy(path, src);
+ for (i = 0; path[i] != '.' && path[i] != 0; i++);
+ if (path[i] == 0)
+ strcat(path, ".stk");
+
+ for (file = 0; file < MAX_DATA_FILES; file++)
+ if (_vm->_global->_dataFiles[file] == 0)
+ break;
+
+ if (file == MAX_DATA_FILES)
+ error("dataFileOpen: Data file slots are full\n");
+ _vm->_global->_dataFileHandles[file] = file_open(path);
+
+ if (_vm->_global->_dataFileHandles[file] == -1)
+ error("dataFileOpen: Can't open %s data file\n", path);
+
+ _vm->_global->_numDataChunks[file] = file_getHandle(_vm->_global->_dataFileHandles[file])->readUint16LE();
+
+ debug(7, "DataChunks: %d [for %s]", _vm->_global->_numDataChunks[file], path);
+
+ dataDesc = new ChunkDesc[_vm->_global->_numDataChunks[file]];
+ _vm->_global->_dataFiles[file] = dataDesc;
+
+ for (i = 0; i < _vm->_global->_numDataChunks[file]; i++) {
+ file_getHandle(_vm->_global->_dataFileHandles[file])->read(dataDesc[i].chunkName, 13);
+ dataDesc[i].size = file_getHandle(_vm->_global->_dataFileHandles[file])->readUint32LE();
+ dataDesc[i].offset = file_getHandle(_vm->_global->_dataFileHandles[file])->readUint32LE();
+ dataDesc[i].packed = file_getHandle(_vm->_global->_dataFileHandles[file])->readByte();
+ }
+
+ for (i = 0; i < _vm->_global->_numDataChunks[file]; i++)
+ debug(7, "%d: %s %d", i, dataDesc[i].chunkName, dataDesc[i].size);
+
+ for (i = 0; i < MAX_SLOT_COUNT; i++)
+ _vm->_global->_chunkPos[file * MAX_SLOT_COUNT + i] = -1;
+
+}
+
+void DataIO::closeDataFile() {
+ int16 file;
+ for (file = MAX_DATA_FILES - 1; file >= 0; file--) {
+ if (_vm->_global->_dataFiles[file] != 0) {
+ delete[] _vm->_global->_dataFiles[file];
+ _vm->_global->_dataFiles[file] = 0;
+ file_getHandle(_vm->_global->_dataFileHandles[file])->close();
+ return;
+ }
+ }
+}
+
+char *DataIO::getUnpackedData(const char *name) {
+ int32 realSize;
+ int16 chunk;
+ char *unpackBuf;
+ char *packBuf;
+ char *ptr;
+ int32 sizeLeft;
+
+ realSize = getChunkSize(name);
+ if (_vm->_global->_packedSize == -1 || realSize == -1)
+ return 0;
+
+ chunk = getChunk(name);
+ if (chunk == -1)
+ return 0;
+
+ unpackBuf = new char[realSize];
+ if (unpackBuf == 0)
+ return 0;
+
+ packBuf = new char[_vm->_global->_packedSize];
+ if (packBuf == 0) {
+ delete[] unpackBuf;
+ return 0;
+ }
+
+ sizeLeft = _vm->_global->_packedSize;
+ ptr = packBuf;
+ while (sizeLeft > 0x4000) {
+ readChunk(chunk, ptr, 0x4000);
+ sizeLeft -= 0x4000;
+ ptr += 0x4000;
+ }
+ readChunk(chunk, ptr, sizeLeft);
+ freeChunk(chunk);
+ _vm->_pack->unpackData(packBuf, unpackBuf);
+
+ delete[] packBuf;
+ return unpackBuf;
+}
+
+void DataIO::closeData(int16 handle) {
+ if (freeChunk(handle) != 0)
+ file_getHandle(handle)->close();
+}
+
+int16 DataIO::openData(const char *path, Common::File::AccessMode mode) {
+ int16 handle;
+
+ if (mode != Common::File::kFileReadMode)
+ return file_open(path, mode);
+
+ handle = getChunk(path);
+ if (handle >= 0)
+ return handle;
+
+ return file_open(path, mode);
+}
+
+int32 DataIO::readData(int16 handle, char *buf, int16 size) {
+ int32 res;
+
+ res = readChunk(handle, buf, size);
+ if (res >= 0)
+ return res;
+
+ return file_getHandle(handle)->read(buf, size);
+}
+
+void DataIO::seekData(int16 handle, int32 pos, int16 from) {
+ int32 resPos;
+
+ resPos = seekChunk(handle, pos, from);
+ if (resPos != -1)
+ return;
+
+ file_getHandle(handle)->seek(pos, from);
+}
+
+int32 DataIO::getDataSize(const char *name) {
+ char buf[128];
+ int32 chunkSz;
+ Common::File file;
+
+ strcpy(buf, name);
+ chunkSz = getChunkSize(buf);
+ if (chunkSz >= 0)
+ return chunkSz;
+
+ if (!file.open(buf))
+ error("getDataSize: Can't find data(%s)", name);
+
+ chunkSz = file.size();
+ file.close();
+
+ return chunkSz;
+}
+
+char *DataIO::getData(const char *path) {
+ char *data;
+ char *ptr;
+ int32 size;
+ int16 handle;
+
+ data = getUnpackedData(path);
+ if (data != 0)
+ return data;
+
+ size = getDataSize(path);
+ data = new char[size];
+ if (data == 0)
+ return 0;
+
+ handle = openData(path);
+
+ ptr = data;
+ while (size > 0x4000) {
+ readData(handle, ptr, 0x4000);
+ size -= 0x4000;
+ ptr += 0x4000;
+ }
+ readData(handle, ptr, size);
+ closeData(handle);
+ return data;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/dataio.h b/engines/gob/dataio.h
new file mode 100644
index 0000000000..9b28adfd10
--- /dev/null
+++ b/engines/gob/dataio.h
@@ -0,0 +1,70 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_DATAIO_H
+#define GOB_DATAIO_H
+
+#include "common/file.h"
+#include "gob/gob.h"
+
+namespace Gob {
+
+#define MAX_DATA_FILES 3
+#define MAX_SLOT_COUNT 4
+
+class DataIO {
+public:
+ struct ChunkDesc {
+ char chunkName[13];
+ uint32 size;
+ uint32 offset;
+ byte packed;
+ ChunkDesc() : size(0), offset(0), packed(0) { chunkName[0] = 0; }
+ };
+
+ int16 file_open(const char *path, Common::File::AccessMode mode = Common::File::kFileReadMode);
+ Common::File *file_getHandle(int16 handle);
+ int16 getChunk(const char *chunkName);
+ char freeChunk(int16 handle);
+ int32 readChunk(int16 handle, char *buf, int16 size);
+ int16 seekChunk(int16 handle, int32 pos, int16 from);
+ int32 getChunkSize(const char *chunkName);
+ void openDataFile(const char *src);
+ void closeDataFile(void);
+ char *getUnpackedData(const char *name);
+ void closeData(int16 handle);
+ int16 openData(const char *path, Common::File::AccessMode mode = Common::File::kFileReadMode);
+ int32 readData(int16 handle, char *buf, int16 size);
+ void seekData(int16 handle, int32 pos, int16 from);
+ int32 getDataSize(const char *name);
+ char *getData(const char *path);
+ char *getSmallData(const char *path);
+
+ DataIO(class GobEngine *vm);
+
+protected:
+ class GobEngine *_vm;
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/draw.cpp b/engines/gob/draw.cpp
new file mode 100644
index 0000000000..f0e09a96ae
--- /dev/null
+++ b/engines/gob/draw.cpp
@@ -0,0 +1,874 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/draw.h"
+#include "gob/global.h"
+#include "gob/video.h"
+#include "gob/game.h"
+#include "gob/util.h"
+#include "gob/scenery.h"
+#include "gob/inter.h"
+#include "gob/video.h"
+#include "gob/palanim.h"
+#include "gob/cdrom.h"
+
+namespace Gob {
+
+Draw::Draw(GobEngine *vm) : _vm(vm) {
+ _fontIndex = 0;
+ _spriteLeft = 0;
+ _spriteTop = 0;
+ _spriteRight = 0;
+ _spriteBottom = 0;
+ _destSpriteX = 0;
+ _destSpriteY = 0;
+ _backColor = 0;
+ _frontColor = 0;
+ _letterToPrint = 0;
+
+ _destSurface = 0;
+ _sourceSurface = 0;
+ _renderFlags = 0;
+ _backDeltaX = 0;
+ _backDeltaY = 0;
+
+ int i;
+
+ for (i = 0; i < 4; i++)
+ _fonts[i] = 0;
+
+ _textToPrint = 0;
+ _transparency = 0;
+
+ for (i = 0; i < 50; i++)
+ _spritesArray[i] = 0;
+
+ _invalidatedCount = 0;
+ for (i = 0; i < 30; i++) {
+ _invalidatedTops[i] = 0;
+ _invalidatedLefts[i] = 0;
+ _invalidatedRights[i] = 0;
+ _invalidatedBottoms[i] = 0;
+ }
+
+ _noInvalidated = 0;
+ _applyPal = 0;
+ _paletteCleared = 0;
+
+ _backSurface = 0;
+ _frontSurface = 0;
+
+ for (i = 0; i < 18; i++)
+ _unusedPalette1[i] = 0;
+ for (i = 0; i < 16; i++)
+ _unusedPalette2[i] = 0;
+ for (i = 0; i < 256; i++) {
+ _vgaPalette[i].red = 0;
+ _vgaPalette[i].blue = 0;
+ _vgaPalette[i].green = 0;
+ }
+ for (i = 0; i < 16; i++) {
+ _vgaSmallPalette[i].red = 0;
+ _vgaSmallPalette[i].blue = 0;
+ _vgaSmallPalette[i].green = 0;
+ }
+
+ _cursorX = 0;
+ _cursorY = 0;
+ _cursorWidth = 0;
+ _cursorHeight = 0;
+
+ _cursorXDeltaVar = -1;
+ _cursorYDeltaVar = -1;
+
+ for (i = 0; i < 40; i++) {
+ _cursorAnimLow[i] = 0;
+ _cursorAnimHigh[i] = 0;
+ _cursorAnimDelays[i] = 0;
+ }
+
+ _cursorIndex = 0;
+ _transparentCursor = 0;
+ _cursorSprites = 0;
+ _cursorBack = 0;
+ _cursorAnim = 0;
+
+ _palLoadData1[0] = 0;
+ _palLoadData1[1] = 17;
+ _palLoadData1[2] = 34;
+ _palLoadData1[3] = 51;
+ _palLoadData2[0] = 0;
+ _palLoadData2[1] = 68;
+ _palLoadData2[2] = 136;
+ _palLoadData2[3] = 204;
+
+ _cursorTimeKey = 0;
+}
+
+void Draw::invalidateRect(int16 left, int16 top, int16 right, int16 bottom) {
+ int16 temp;
+ int16 rect;
+ int16 i;
+
+ if (_renderFlags & RENDERFLAG_NOINVALIDATE)
+ return;
+
+ if (left > right) {
+ temp = left;
+ left = right;
+ right = temp;
+ }
+ if (top > bottom) {
+ temp = top;
+ top = bottom;
+ bottom = temp;
+ }
+
+ if (left > 319 || right < 0 || top > 199 || bottom < 0)
+ return;
+
+ _noInvalidated = 0;
+
+ if (_invalidatedCount >= 30) {
+ _invalidatedLefts[0] = 0;
+ _invalidatedTops[0] = 0;
+ _invalidatedRights[0] = 319;
+ _invalidatedBottoms[0] = 199;
+ _invalidatedCount = 1;
+ return;
+ }
+
+ if (left < 0)
+ left = 0;
+
+ if (right > 319)
+ right = 319;
+
+ if (top < 0)
+ top = 0;
+
+ if (bottom > 199)
+ bottom = 199;
+
+ left &= 0xfff0;
+ right |= 0x000f;
+
+ for (rect = 0; rect < _invalidatedCount; rect++) {
+
+ if (_invalidatedTops[rect] > top) {
+ if (_invalidatedTops[rect] > bottom) {
+ for (i = _invalidatedCount; i > rect; i--) {
+ _invalidatedLefts[i] =
+ _invalidatedLefts[i - 1];
+ _invalidatedTops[i] =
+ _invalidatedTops[i - 1];
+ _invalidatedRights[i] =
+ _invalidatedRights[i - 1];
+ _invalidatedBottoms[i] =
+ _invalidatedBottoms[i - 1];
+ }
+ _invalidatedLefts[rect] = left;
+ _invalidatedTops[rect] = top;
+ _invalidatedRights[rect] = right;
+ _invalidatedBottoms[rect] = bottom;
+ _invalidatedCount++;
+ return;
+ }
+ if (_invalidatedBottoms[rect] < bottom)
+ _invalidatedBottoms[rect] = bottom;
+
+ if (_invalidatedLefts[rect] > left)
+ _invalidatedLefts[rect] = left;
+
+ if (_invalidatedRights[rect] < right)
+ _invalidatedRights[rect] = right;
+
+ _invalidatedTops[rect] = top;
+ return;
+ }
+
+ if (_invalidatedBottoms[rect] < top)
+ continue;
+
+ if (_invalidatedBottoms[rect] < bottom)
+ _invalidatedBottoms[rect] = bottom;
+
+ if (_invalidatedLefts[rect] > left)
+ _invalidatedLefts[rect] = left;
+
+ if (_invalidatedRights[rect] < right)
+ _invalidatedRights[rect] = right;
+
+ return;
+ }
+
+ _invalidatedLefts[_invalidatedCount] = left;
+ _invalidatedTops[_invalidatedCount] = top;
+ _invalidatedRights[_invalidatedCount] = right;
+ _invalidatedBottoms[_invalidatedCount] = bottom;
+ _invalidatedCount++;
+ return;
+}
+
+void Draw::blitInvalidated(void) {
+ int16 i;
+
+ if (_cursorIndex == 4)
+ blitCursor();
+
+ if (_vm->_inter->_terminate)
+ return;
+
+ if (_noInvalidated && _applyPal == 0)
+ return;
+
+ if (_noInvalidated) {
+ setPalette();
+ _applyPal = 0;
+ return;
+ }
+
+ if (_applyPal) {
+ clearPalette();
+
+ _vm->_video->drawSprite(_backSurface, _frontSurface, 0, 0, 319,
+ 199, 0, 0, 0);
+ setPalette();
+ _invalidatedCount = 0;
+ _noInvalidated = 1;
+ _applyPal = 0;
+ return;
+ }
+
+ _vm->_global->_doRangeClamp = 0;
+ for (i = 0; i < _invalidatedCount; i++) {
+ _vm->_video->drawSprite(_backSurface, _frontSurface,
+ _invalidatedLefts[i], _invalidatedTops[i],
+ _invalidatedRights[i], _invalidatedBottoms[i],
+ _invalidatedLefts[i], _invalidatedTops[i], 0);
+ }
+ _vm->_global->_doRangeClamp = 1;
+
+ _invalidatedCount = 0;
+ _noInvalidated = 1;
+ _applyPal = 0;
+}
+
+void Draw::setPalette(void) {
+ if (_vm->_global->_videoMode != 0x13)
+ error("setPalette: Video mode 0x%x is not supported!\n",
+ _vm->_global->_videoMode);
+
+ _vm->_global->_pPaletteDesc->unused1 = _unusedPalette1;
+ _vm->_global->_pPaletteDesc->unused2 = _unusedPalette2;
+ _vm->_global->_pPaletteDesc->vgaPal = _vgaPalette;
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ _paletteCleared = 0;
+}
+
+void Draw::clearPalette(void) {
+ if (_paletteCleared == 0) {
+ _paletteCleared = 1;
+ _vm->_util->clearPalette();
+ }
+}
+
+void Draw::blitCursor(void) {
+ if (_cursorIndex == -1)
+ return;
+
+ _cursorIndex = -1;
+
+ if (_noInvalidated) {
+ _vm->_video->drawSprite(_backSurface, _frontSurface,
+ _cursorX, _cursorY,
+ _cursorX + _cursorWidth - 1,
+ _cursorY + _cursorHeight - 1, _cursorX,
+ _cursorY, 0);
+ } else {
+ invalidateRect(_cursorX, _cursorY,
+ _cursorX + _cursorWidth - 1,
+ _cursorY + _cursorHeight - 1);
+ }
+}
+
+void Draw::spriteOperation(int16 operation) {
+ uint16 id;
+ char *dataBuf;
+ Game::TotResItem *itemPtr;
+ int32 offset;
+ int16 len;
+ int16 i;
+ int16 x;
+ int16 y;
+ int16 perLine;
+
+ if (_sourceSurface >= 100)
+ _sourceSurface -= 80;
+
+ if (_destSurface >= 100)
+ _destSurface -= 80;
+
+ if (_renderFlags & RENDERFLAG_USEDELTAS) {
+ if (_sourceSurface == 21) {
+ _spriteLeft += _backDeltaX;
+ _spriteTop += _backDeltaY;
+ }
+
+ if (_destSurface == 21) {
+ _destSpriteX += _backDeltaX;
+ _destSpriteY += _backDeltaY;
+ if (operation == DRAW_DRAWLINE ||
+ (operation >= DRAW_DRAWBAR
+ && operation <= DRAW_FILLRECTABS)) {
+ _spriteRight += _backDeltaX;
+ _spriteBottom += _backDeltaY;
+ }
+ }
+ }
+
+ switch (operation) {
+ case DRAW_BLITSURF:
+ _vm->_video->drawSprite(_spritesArray[_sourceSurface],
+ _spritesArray[_destSurface],
+ _spriteLeft, _spriteTop,
+ _spriteLeft + _spriteRight - 1,
+ _spriteTop + _spriteBottom - 1,
+ _destSpriteX, _destSpriteY, _transparency);
+
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _destSpriteX + _spriteRight - 1,
+ _destSpriteY + _spriteBottom - 1);
+ }
+ break;
+
+ case DRAW_PUTPIXEL:
+ _vm->_video->putPixel(_destSpriteX, _destSpriteY,
+ _frontColor, _spritesArray[_destSurface]);
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _destSpriteX, _destSpriteY);
+ }
+ break;
+
+ case DRAW_FILLRECT:
+ _vm->_video->fillRect(_spritesArray[_destSurface],
+ _destSpriteX, _destSpriteY,
+ _destSpriteX + _spriteRight - 1,
+ _destSpriteY + _spriteBottom - 1, _backColor);
+
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _destSpriteX + _spriteRight - 1,
+ _destSpriteY + _spriteBottom - 1);
+ }
+ break;
+
+ case DRAW_DRAWLINE:
+ _vm->_video->drawLine(_spritesArray[_destSurface],
+ _destSpriteX, _destSpriteY,
+ _spriteRight, _spriteBottom, _frontColor);
+
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _spriteRight, _spriteBottom);
+ }
+ break;
+
+ case DRAW_INVALIDATE:
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX - _spriteRight, _destSpriteY - _spriteBottom, // !!
+ _destSpriteX + _spriteRight,
+ _destSpriteY + _spriteBottom);
+ }
+ break;
+
+ case DRAW_LOADSPRITE:
+ id = _spriteLeft;
+ if (id >= 30000) {
+ dataBuf =
+ _vm->_game->loadExtData(id, &_spriteRight,
+ &_spriteBottom);
+ _vm->_video->drawPackedSprite((byte *)dataBuf, _spriteRight,
+ _spriteBottom, _destSpriteX,
+ _destSpriteY, _transparency,
+ _spritesArray[_destSurface]);
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX,
+ _destSpriteY,
+ _destSpriteX + _spriteRight - 1,
+ _destSpriteY + _spriteBottom - 1);
+ }
+ delete[] dataBuf;
+ break;
+ }
+ // Load from .TOT resources
+ itemPtr = &_vm->_game->_totResourceTable->items[id];
+ offset = itemPtr->offset;
+ if (offset >= 0) {
+ dataBuf =
+ ((char *)_vm->_game->_totResourceTable) +
+ szGame_TotResTable + szGame_TotResItem *
+ _vm->_game->_totResourceTable->itemsCount + offset;
+ } else {
+ dataBuf =
+ _vm->_game->_imFileData +
+ (int32)READ_LE_UINT32(&((int32 *)_vm->_game->_imFileData)[-offset - 1]);
+ }
+
+ _spriteRight = itemPtr->width;
+ _spriteBottom = itemPtr->height;
+ _vm->_video->drawPackedSprite((byte *)dataBuf,
+ _spriteRight, _spriteBottom,
+ _destSpriteX, _destSpriteY,
+ _transparency, _spritesArray[_destSurface]);
+
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _destSpriteX + _spriteRight - 1,
+ _destSpriteY + _spriteBottom - 1);
+ }
+ break;
+
+ case DRAW_PRINTTEXT:
+ len = strlen(_textToPrint);
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _destSpriteX +
+ len * _fonts[_fontIndex]->itemWidth - 1,
+ _destSpriteY +
+ _fonts[_fontIndex]->itemHeight - 1);
+ }
+
+ for (i = 0; i < len; i++) {
+ _vm->_video->drawLetter(_textToPrint[i],
+ _destSpriteX, _destSpriteY,
+ _fonts[_fontIndex],
+ _transparency,
+ _frontColor, _backColor,
+ _spritesArray[_destSurface]);
+
+ _destSpriteX += _fonts[_fontIndex]->itemWidth;
+ }
+ break;
+
+ case DRAW_DRAWBAR:
+ _vm->_video->drawLine(_spritesArray[_destSurface],
+ _destSpriteX, _spriteBottom,
+ _spriteRight, _spriteBottom, _frontColor);
+
+ _vm->_video->drawLine(_spritesArray[_destSurface],
+ _destSpriteX, _destSpriteY,
+ _destSpriteX, _spriteBottom, _frontColor);
+
+ _vm->_video->drawLine(_spritesArray[_destSurface],
+ _spriteRight, _destSpriteY,
+ _spriteRight, _spriteBottom, _frontColor);
+
+ _vm->_video->drawLine(_spritesArray[_destSurface],
+ _destSpriteX, _destSpriteY,
+ _spriteRight, _destSpriteY, _frontColor);
+
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _spriteRight, _spriteBottom);
+ }
+ break;
+
+ case DRAW_CLEARRECT:
+ if (_backColor < 16) {
+ _vm->_video->fillRect(_spritesArray[_destSurface],
+ _destSpriteX, _destSpriteY,
+ _spriteRight, _spriteBottom,
+ _backColor);
+ }
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _spriteRight, _spriteBottom);
+ }
+ break;
+
+ case DRAW_FILLRECTABS:
+ _vm->_video->fillRect(_spritesArray[_destSurface],
+ _destSpriteX, _destSpriteY,
+ _spriteRight, _spriteBottom, _backColor);
+
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _spriteRight, _spriteBottom);
+ }
+ break;
+
+ case DRAW_DRAWLETTER:
+ if (_fontToSprite[_fontIndex].sprite == -1) {
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX,
+ _destSpriteY,
+ _destSpriteX +
+ _fonts[_fontIndex]->itemWidth - 1,
+ _destSpriteY +
+ _fonts[_fontIndex]->itemHeight -
+ 1);
+ }
+ _vm->_video->drawLetter(_letterToPrint,
+ _destSpriteX, _destSpriteY,
+ _fonts[_fontIndex],
+ _transparency,
+ _frontColor, _backColor,
+ _spritesArray[_destSurface]);
+ break;
+ }
+
+ perLine =
+ _spritesArray[(int16)_fontToSprite[_fontIndex].
+ sprite]->width / _fontToSprite[_fontIndex].width;
+
+ y = (_letterToPrint -
+ _fontToSprite[_fontIndex].base) / perLine *
+ _fontToSprite[_fontIndex].height;
+
+ x = (_letterToPrint -
+ _fontToSprite[_fontIndex].base) % perLine *
+ _fontToSprite[_fontIndex].width;
+
+ if (_destSurface == 21) {
+ invalidateRect(_destSpriteX, _destSpriteY,
+ _destSpriteX +
+ _fontToSprite[_fontIndex].width,
+ _destSpriteY +
+ _fontToSprite[_fontIndex].height);
+ }
+
+ _vm->_video->drawSprite(_spritesArray[(int16)_fontToSprite
+ [_fontIndex].sprite],
+ _spritesArray[_destSurface], x, y,
+ x + _fontToSprite[_fontIndex].width,
+ y + _fontToSprite[_fontIndex].height,
+ _destSpriteX, _destSpriteY, _transparency);
+
+ break;
+ }
+
+ if (_renderFlags & RENDERFLAG_USEDELTAS) {
+ if (_sourceSurface == 21) {
+ _spriteLeft -= _backDeltaX;
+ _spriteTop -= _backDeltaY;
+ }
+
+ if (_destSurface == 21) {
+ _destSpriteX -= _backDeltaX;
+ _destSpriteY -= _backDeltaY;
+ }
+ }
+}
+
+void Draw::animateCursor(int16 cursor) {
+ int16 newX = 0;
+ int16 newY = 0;
+ Game::Collision *ptr;
+ int16 minX;
+ int16 minY;
+ int16 maxX;
+ int16 maxY;
+ int16 cursorIndex;
+
+ cursorIndex = cursor;
+
+ if (cursorIndex == -1) {
+ cursorIndex = 0;
+ for (ptr = _vm->_game->_collisionAreas; ptr->left != -1; ptr++) {
+ if (ptr->flags & 0xfff0)
+ continue;
+
+ if (ptr->left > _vm->_global->_inter_mouseX)
+ continue;
+
+ if (ptr->right < _vm->_global->_inter_mouseX)
+ continue;
+
+ if (ptr->top > _vm->_global->_inter_mouseY)
+ continue;
+
+ if (ptr->bottom < _vm->_global->_inter_mouseY)
+ continue;
+
+ if ((ptr->flags & 0xf) < 3)
+ cursorIndex = 1;
+ else
+ cursorIndex = 3;
+ break;
+ }
+ if (_cursorAnimLow[cursorIndex] == -1)
+ cursorIndex = 1;
+ }
+
+ if (_cursorAnimLow[cursorIndex] != -1) {
+ if (cursorIndex == _cursorIndex) {
+ if (_cursorAnimDelays[_cursorIndex] != 0 &&
+ _cursorAnimDelays[_cursorIndex] * 10 +
+ _cursorTimeKey <= _vm->_util->getTimeKey()) {
+ _cursorAnim++;
+ _cursorTimeKey = _vm->_util->getTimeKey();
+ } else {
+/* if (_noInvalidated &&
+ inter_mouseX == _cursorX && inter_mouseY == _cursorY)
+ return;*/
+ }
+ } else {
+ _cursorIndex = cursorIndex;
+ if (_cursorAnimDelays[_cursorIndex] != 0) {
+ _cursorAnim =
+ _cursorAnimLow[_cursorIndex];
+ _cursorTimeKey = _vm->_util->getTimeKey();
+ } else {
+ _cursorAnim = _cursorIndex;
+ }
+ }
+
+ if (_cursorAnimDelays[_cursorIndex] != 0 &&
+ (_cursorAnimHigh[_cursorIndex] < _cursorAnim ||
+ _cursorAnimLow[_cursorIndex] >
+ _cursorAnim)) {
+ _cursorAnim = _cursorAnimLow[_cursorIndex];
+ }
+
+ newX = _vm->_global->_inter_mouseX;
+ newY = _vm->_global->_inter_mouseY;
+ if (_cursorXDeltaVar != -1) {
+ newX -= (uint16)VAR_OFFSET(_cursorIndex * 4 + (_cursorXDeltaVar / 4) * 4);
+ newY -= (uint16)VAR_OFFSET(_cursorIndex * 4 + (_cursorYDeltaVar / 4) * 4);
+ }
+
+ minX = MIN(newX, _cursorX);
+ minY = MIN(newY, _cursorY);
+ maxX = MAX(_cursorX, newX) + _cursorWidth - 1;
+ maxY = MAX(_cursorY, newY) + _cursorHeight - 1;
+ _vm->_video->drawSprite(_backSurface, _cursorBack,
+ newX, newY, newX + _cursorWidth - 1,
+ newY + _cursorHeight - 1, 0, 0, 0);
+
+ _vm->_video->drawSprite(_cursorSprites, _backSurface,
+ _cursorWidth * _cursorAnim, 0,
+ _cursorWidth * (_cursorAnim + 1) - 1,
+ _cursorHeight - 1, newX, newY, _transparentCursor);
+
+ if (_noInvalidated == 0) {
+ cursorIndex = _cursorIndex;
+ _cursorIndex = -1;
+ blitInvalidated();
+ _cursorIndex = cursorIndex;
+ } else {
+ _vm->_video->waitRetrace(_vm->_global->_videoMode);
+ }
+
+ _vm->_video->drawSprite(_backSurface, _frontSurface,
+ minX, minY, maxX, maxY, minX, minY, 0);
+
+ _vm->_video->drawSprite(_cursorBack, _backSurface,
+ 0, 0, _cursorWidth - 1, _cursorHeight - 1,
+ newX, newY, 0);
+ } else {
+ blitCursor();
+ }
+
+ _cursorX = newX;
+ _cursorY = newY;
+}
+
+void Draw::printText(void) {
+ int16 savedFlags;
+ int16 ldestSpriteX;
+ char *dataPtr;
+ char *ptr;
+ char *ptr2;
+ int16 index;
+ int16 destX;
+ int16 destY;
+ char cmd;
+ int16 val;
+ char buf[20];
+
+ index = _vm->_inter->load16();
+
+ _vm->_cdrom->playMultMusic();
+
+ dataPtr = (char *)_vm->_game->_totTextData + _vm->_game->_totTextData->items[index].offset;
+ ptr = dataPtr;
+
+ if (_renderFlags & RENDERFLAG_CAPTUREPUSH) {
+ _destSpriteX = READ_LE_UINT16(ptr);
+ _destSpriteY = READ_LE_UINT16(ptr + 2);
+ _spriteRight = READ_LE_UINT16(ptr + 4) - _destSpriteX + 1;
+ _spriteBottom = READ_LE_UINT16(ptr + 6) - _destSpriteY + 1;
+ _vm->_game->capturePush(_destSpriteX, _destSpriteY,
+ _spriteRight, _spriteBottom);
+ (*_vm->_scenery->_pCaptureCounter)++;
+ }
+ _destSpriteX = READ_LE_UINT16(ptr);
+ destX = _destSpriteX;
+
+ _destSpriteY = READ_LE_UINT16(ptr + 2);
+ destY = _destSpriteY;
+
+ _spriteRight = READ_LE_UINT16(ptr + 4);
+ _spriteBottom = READ_LE_UINT16(ptr + 6);
+ _destSurface = 21;
+
+ ptr += 8;
+
+ _backColor = *ptr++;
+ _transparency = 1;
+ spriteOperation(DRAW_CLEARRECT);
+
+ _backColor = 0;
+ savedFlags = _renderFlags;
+
+ _renderFlags &= ~RENDERFLAG_NOINVALIDATE;
+ for (; (_destSpriteX = READ_LE_UINT16(ptr)) != -1; ptr++) {
+ _destSpriteX += destX;
+ _destSpriteY = READ_LE_UINT16(ptr + 2) + destY;
+ _spriteRight = READ_LE_UINT16(ptr + 4) + destX;
+ _spriteBottom = READ_LE_UINT16(ptr + 6) + destY;
+ ptr += 8;
+
+ cmd = (*ptr & 0xf0) >> 4;
+ if (cmd == 0) {
+ _frontColor = *ptr & 0xf;
+ spriteOperation(DRAW_DRAWLINE);
+ } else if (cmd == 1) {
+ _frontColor = *ptr & 0xf;
+ spriteOperation(DRAW_DRAWBAR);
+ } else if (cmd == 2) {
+ _backColor = *ptr & 0xf;
+ spriteOperation(DRAW_FILLRECTABS);
+ }
+ }
+ ptr += 2;
+
+ for (ptr2 = ptr; *ptr2 != 1; ptr2++) {
+ if (*ptr2 == 3)
+ ptr2++;
+
+ if (*ptr2 == 2)
+ ptr2 += 4;
+ }
+
+ ptr2++;
+
+ while (*ptr != 1) {
+ cmd = *ptr;
+ if (cmd == 3) {
+ ptr++;
+ _fontIndex = (*ptr & 0xf0) >> 4;
+ _frontColor = *ptr & 0xf;
+ ptr++;
+ continue;
+ } else if (cmd == 2) {
+ ptr++;
+ _destSpriteX = destX + READ_LE_UINT16(ptr);
+ _destSpriteY = destY + READ_LE_UINT16(ptr + 2);
+ ptr += 4;
+ continue;
+ }
+
+ if ((byte)*ptr != 0xba) {
+ _letterToPrint = *ptr;
+ spriteOperation(DRAW_DRAWLETTER);
+ _destSpriteX +=
+ _fonts[_fontIndex]->itemWidth;
+ ptr++;
+ } else {
+ cmd = ptr2[17] & 0x7f;
+ if (cmd == 0) {
+ val = READ_LE_UINT16(ptr2 + 18) * 4;
+ sprintf(buf, "%d", VAR_OFFSET(val));
+ } else if (cmd == 1) {
+ val = READ_LE_UINT16(ptr2 + 18) * 4;
+
+ strcpy(buf, _vm->_global->_inter_variables + val);
+ } else {
+ val = READ_LE_UINT16(ptr2 + 18) * 4;
+
+ sprintf(buf, "%d", VAR_OFFSET(val));
+ if (buf[0] == '-') {
+ while (strlen(buf) - 1 < (uint32)ptr2[17]) {
+ _vm->_util->insertStr("0", buf, 1);
+ }
+ } else {
+ while (strlen(buf) - 1 < (uint32)ptr2[17]) {
+ _vm->_util->insertStr("0", buf, 0);
+ }
+ }
+
+ _vm->_util->insertStr(",", buf, strlen(buf) + 1 - ptr2[17]);
+ }
+
+ _textToPrint = buf;
+ ldestSpriteX = _destSpriteX;
+ spriteOperation(DRAW_PRINTTEXT);
+ if (ptr2[17] & 0x80) {
+ if (ptr[1] == ' ') {
+ _destSpriteX += _fonts[_fontIndex]->itemWidth;
+ while (ptr[1] == ' ')
+ ptr++;
+ if (ptr[1] == 2) {
+ if (READ_LE_UINT16(ptr + 4) == _destSpriteY)
+ ptr += 5;
+ }
+ } else if (ptr[1] == 2 && READ_LE_UINT16(ptr + 4) == _destSpriteY) {
+ ptr += 5;
+ _destSpriteX += _fonts[_fontIndex]->itemWidth;
+ }
+ } else {
+ _destSpriteX = ldestSpriteX + _fonts[_fontIndex]->itemWidth;
+ }
+ ptr2 += 23;
+ ptr++;
+ }
+ }
+
+ _renderFlags = savedFlags;
+ if (_renderFlags & 4) {
+ warning("printText: Input not supported!");
+// xor ax, ax
+// loc_436_1391:
+// xor dx, dx
+// push ax
+// push dx
+// push ax
+// push dx
+// push ax
+// mov al, 0
+// push ax
+// call sub_9FF_1E71
+// add sp, 0Ch
+ }
+
+ if ((_renderFlags & RENDERFLAG_CAPTUREPOP) && *_vm->_scenery->_pCaptureCounter != 0) {
+ (*_vm->_scenery->_pCaptureCounter)--;
+ _vm->_game->capturePop(1);
+ }
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/draw.h b/engines/gob/draw.h
new file mode 100644
index 0000000000..77fcd3f349
--- /dev/null
+++ b/engines/gob/draw.h
@@ -0,0 +1,139 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_DRAW_H
+#define GOB_DRAW_H
+
+#include "gob/video.h"
+
+namespace Gob {
+
+#define RENDERFLAG_NOINVALIDATE 1
+#define RENDERFLAG_CAPTUREPUSH 2
+#define RENDERFLAG_CAPTUREPOP 8
+#define RENDERFLAG_USEDELTAS 0x10
+
+class Draw {
+public:
+ struct FontToSprite {
+ int8 sprite;
+ int8 base;
+ int8 width;
+ int8 height;
+ FontToSprite() : sprite(0), base(0), width(0), height() {}
+ };
+
+ int16 _fontIndex;
+ int16 _spriteLeft;
+ int16 _spriteTop;
+ int16 _spriteRight;
+ int16 _spriteBottom;
+ int16 _destSpriteX;
+ int16 _destSpriteY;
+ int16 _backColor;
+ int16 _frontColor;
+ char _letterToPrint;
+ FontToSprite _fontToSprite[4];
+ int16 _destSurface;
+ int16 _sourceSurface;
+ int16 _renderFlags;
+ int16 _backDeltaX;
+ int16 _backDeltaY;
+ Video::FontDesc *_fonts[4];
+ char *_textToPrint;
+ int16 _transparency;
+ Video::SurfaceDesc *_spritesArray[50];
+
+ int16 _invalidatedCount;
+ int16 _invalidatedTops[30];
+ int16 _invalidatedLefts[30];
+ int16 _invalidatedRights[30];
+ int16 _invalidatedBottoms[30];
+
+ int8 _noInvalidated;
+// int8 doFullFlip; // Never used?!?
+ int8 _paletteCleared;
+
+ int16 _cursorIndex;
+ int16 _transparentCursor;
+ uint32 _cursorTimeKey;
+
+ Video::SurfaceDesc *_backSurface;
+ Video::SurfaceDesc *_frontSurface;
+
+ int16 _unusedPalette1[18];
+ int16 _unusedPalette2[16];
+ Video::Color _vgaPalette[256];
+ Video::Color _vgaSmallPalette[16];
+
+ int16 _cursorX;
+ int16 _cursorY;
+ int16 _cursorWidth;
+ int16 _cursorHeight;
+
+ int16 _cursorXDeltaVar;
+ int16 _cursorYDeltaVar;
+
+ Video::SurfaceDesc *_cursorSprites;
+ Video::SurfaceDesc *_cursorBack;
+ int16 _cursorAnim;
+ int8 _cursorAnimLow[40];
+ int8 _cursorAnimHigh[40];
+ int8 _cursorAnimDelays[40];
+ int8 _applyPal;
+
+ int16 _palLoadData1[4];
+ int16 _palLoadData2[4];
+
+ void invalidateRect(int16 left, int16 top, int16 right, int16 bottom);
+ void blitInvalidated(void);
+ void setPalette(void);
+ void clearPalette(void);
+ void blitCursor(void);
+
+ void spriteOperation(int16 operation);
+ void animateCursor(int16 cursor);
+ void printText(void);
+
+ Draw(GobEngine *vm);
+
+protected:
+ GobEngine *_vm;
+};
+
+// Draw operations
+
+#define DRAW_BLITSURF 0
+#define DRAW_PUTPIXEL 1
+#define DRAW_FILLRECT 2
+#define DRAW_DRAWLINE 3
+#define DRAW_INVALIDATE 4
+#define DRAW_LOADSPRITE 5
+#define DRAW_PRINTTEXT 6
+#define DRAW_DRAWBAR 7
+#define DRAW_CLEARRECT 8
+#define DRAW_FILLRECTABS 9
+#define DRAW_DRAWLETTER 10
+
+} // End of namespace Gob
+
+#endif /* __DRAW_H */
diff --git a/engines/gob/driver_vga.cpp b/engines/gob/driver_vga.cpp
new file mode 100644
index 0000000000..3a2f44bc11
--- /dev/null
+++ b/engines/gob/driver_vga.cpp
@@ -0,0 +1,156 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/driver_vga.h"
+#include "graphics/primitives.h"
+
+#if defined (_MSC_VER) || defined (__WINS__)
+#define STUB_FUNC printf("STUB:")
+#else
+#define STUB_FUNC printf("STUB: %s\n", __PRETTY_FUNCTION__)
+#endif
+
+namespace Gob {
+
+void VGAVideoDriver::drawSprite(Video::SurfaceDesc *source, Video::SurfaceDesc *dest, int16 left, int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp) {
+ if (x >= 0 && x < dest->width && y >= 0 && y < dest->height) {
+ int16 width = (right - left) + 1;
+ int16 height = (bottom - top) + 1;
+
+ byte *srcPos = source->vidPtr + (top * source->width) + left;
+ byte *destPos = dest->vidPtr + (y * dest->width) + x;
+ while (height--) {
+ if (transp) {
+ for (int16 i = 0; i < width; ++i) {
+ if (srcPos[i])
+ destPos[i] = srcPos[i];
+ }
+ } else {
+ for (int16 i = 0; i < width; ++i)
+ destPos[i] = srcPos[i];
+ }
+
+ srcPos += source->width; //width ?
+ destPos += dest->width;
+ }
+ }
+}
+
+void VGAVideoDriver::fillRect(Video::SurfaceDesc *dest, int16 left, int16 top, int16 right, int16 bottom, byte color) {
+ if (left < dest->width && right < dest->width && top < dest->height && bottom < dest->height) {
+ byte *pos = dest->vidPtr + (top * dest->width) + left;
+ int16 width = (right - left) + 1;
+ int16 height = (bottom - top) + 1;
+ while (height--) {
+ for (int16 i = 0; i < width; ++i) {
+ pos[i] = color;
+ }
+
+ pos += dest->width;
+ }
+ }
+}
+
+void VGAVideoDriver::putPixel(int16 x, int16 y, byte color, Video::SurfaceDesc *dest) {
+ if (x >= 0 && x < dest->width && y >= 0 && y < dest->height)
+ dest->vidPtr[(y * dest->width) + x] = color;
+}
+
+void VGAVideoDriver::drawLetter(unsigned char item, int16 x, int16 y, Video::FontDesc *fontDesc, byte color1, byte color2, byte transp, Video::SurfaceDesc *dest) {
+ byte *src, *dst;
+ uint16 data;
+ int i, j;
+
+ src = (byte *)fontDesc->dataPtr + (item - fontDesc->startItem) * (fontDesc->itemSize & 0xff);
+ dst = dest->vidPtr + x + dest->width * y;
+
+ for (i = 0; i < fontDesc->itemHeight; i++) {
+ data = READ_BE_UINT16(src);
+ src += 2;
+ if (fontDesc->itemSize <= 8)
+ src--;
+
+ for (j = 0; j < fontDesc->itemWidth; j++) {
+ if (data & 0x8000) {
+ *dst = color2;
+ } else {
+ if (color1 == 0)
+ *dst = transp;
+ }
+ dst++;
+ data <<= 1;
+ }
+ dst += dest->width - fontDesc->itemWidth;
+ }
+}
+
+static void plotPixel(int x, int y, int color, void *data) {
+ Video::SurfaceDesc *dest = (Video::SurfaceDesc *)data;
+ if (x >= 0 && x < dest->width && y >= 0 && y < dest->height)
+ dest->vidPtr[(y * dest->width) + x] = color;
+}
+
+void VGAVideoDriver::drawLine(Video::SurfaceDesc *dest, int16 x0, int16 y0, int16 x1, int16 y1, byte color) {
+ Graphics::drawLine(x0, y0, x1, y1, color, &plotPixel, dest);
+}
+
+void VGAVideoDriver::drawPackedSprite(byte *sprBuf, int16 width, int16 height, int16 x, int16 y, byte transp, Video::SurfaceDesc *dest) {
+ int destRight = x + width;
+ int destBottom = y + height;
+
+ byte* dst = dest->vidPtr + x + dest->width * y;
+
+ int curx = x;
+ int cury = y;
+
+ while (1) {
+ uint8 val = *sprBuf++;
+ unsigned int repeat = val & 7;
+ val &= 0xF8;
+ if (!(val & 8)) {
+ repeat <<= 8;
+ repeat |= *sprBuf++;
+ }
+ repeat++;
+ val >>= 4;
+
+ for (unsigned int i = 0; i < repeat; ++i) {
+ if (curx < dest->width && cury < dest->height)
+ if (!transp || val)
+ *dst = val;
+
+ dst++;
+ curx++;
+ if (curx == destRight) {
+ dst += dest->width + x - curx;
+ curx = x;
+ cury++;
+ if (cury == destBottom)
+ return;
+ }
+ }
+ }
+
+}
+
+}
+
diff --git a/engines/gob/driver_vga.h b/engines/gob/driver_vga.h
new file mode 100644
index 0000000000..de80b61c5f
--- /dev/null
+++ b/engines/gob/driver_vga.h
@@ -0,0 +1,44 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_DRIVER_VGA_H
+#define GOB_DRIVER_VGA_H
+
+#include "gob/video.h"
+
+namespace Gob {
+
+class VGAVideoDriver : public VideoDriver {
+public:
+ VGAVideoDriver() {}
+ virtual ~VGAVideoDriver() {}
+ void drawSprite(Video::SurfaceDesc *source, Video::SurfaceDesc *dest, int16 left, int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp);
+ void fillRect(Video::SurfaceDesc *dest, int16 left, int16 top, int16 right, int16 bottom, byte color);
+ void putPixel(int16 x, int16 y, byte color, Video::SurfaceDesc *dest);
+ void drawLetter(unsigned char item, int16 x, int16 y, Video::FontDesc *fontDesc, byte color1, byte color2, byte transp, Video::SurfaceDesc *dest);
+ void drawLine(Video::SurfaceDesc *dest, int16 x0, int16 y0, int16 x1, int16 y1, byte color);
+ void drawPackedSprite(byte *sprBuf, int16 width, int16 height, int16 x, int16 y, byte transp, Video::SurfaceDesc *dest);
+};
+
+}
+
+#endif
diff --git a/engines/gob/game.cpp b/engines/gob/game.cpp
new file mode 100644
index 0000000000..86544f0906
--- /dev/null
+++ b/engines/gob/game.cpp
@@ -0,0 +1,1952 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/game.h"
+#include "gob/video.h"
+#include "gob/dataio.h"
+#include "gob/pack.h"
+#include "gob/scenery.h"
+#include "gob/inter.h"
+#include "gob/parse.h"
+#include "gob/draw.h"
+#include "gob/mult.h"
+#include "gob/util.h"
+#include "gob/goblin.h"
+#include "gob/cdrom.h"
+#include "gob/music.h"
+
+namespace Gob {
+
+int16 Game::_captureCount = 0;
+Common::Rect Game::_captureStack[20];
+
+Game::Game(GobEngine *vm) : _vm(vm) {
+ _extTable = 0;
+ _totFileData = 0;
+ _totResourceTable = 0;
+ _imFileData = 0;
+ _extHandle = 0;
+ _collisionAreas = 0;
+ _shouldPushColls = 0;
+ _totTextData = 0;
+
+ // Collisions stack
+ _collStackSize = 0;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ _collStack[i] = 0;
+ _collStackElemSizes[i] = 0;
+ }
+
+ for (i = 0; i < 20; i++)
+ _soundSamples[i] = 0;
+
+ _curTotFile[0] = 0;
+ _curExtFile[0] = 0;
+ _totToLoad[0] = 0;
+
+ _startTimeKey = 0;
+ _mouseButtons = 0;
+
+ _lastCollKey = 0;
+ _lastCollAreaIndex = 0;
+ _lastCollId = 0;
+
+ _activeCollResId = 0;
+ _activeCollIndex = 0;
+ _handleMouse = 0;
+ _forceHandleMouse = 0;
+
+ _tempStr[0] = 0;
+ _curImaFile[0] = 0;
+ _soundFromExt[0] = 0;
+ _collStr[0] = 0;
+
+
+ // Capture
+}
+
+char *Game::loadExtData(int16 itemId, int16 *pResWidth, int16 *pResHeight) {
+ int16 commonHandle;
+ int16 itemsCount;
+ int32 offset;
+ uint32 size;
+ ExtItem *item;
+ char isPacked;
+ int16 handle;
+ int32 tableSize;
+ char path[20];
+ char *dataBuf;
+ char *packedBuf;
+ char *dataPtr;
+
+ itemId -= 30000;
+ if (_extTable == 0)
+ return 0;
+
+ commonHandle = -1;
+ itemsCount = _extTable->itemsCount;
+ item = &_extTable->items[itemId];
+ tableSize = szGame_ExtTable + szGame_ExtItem * itemsCount;
+
+ offset = item->offset;
+ size = item->size;
+ if (item->width & 0x8000)
+ isPacked = 1;
+ else
+ isPacked = 0;
+
+ if (pResWidth != 0) {
+ *pResWidth = item->width & 0x7fff;
+ *pResHeight = item->height;
+ debug(7, "loadExtData(%d, %d, %d)", itemId, *pResWidth, *pResHeight);
+ }
+
+ debug(7, "loadExtData(%d, 0, 0)", itemId);
+
+ if (item->height == 0)
+ size += (item->width & 0x7fff) << 16;
+
+ debug(7, "size: %d off: %d", size, offset);
+ if (offset >= 0) {
+ handle = _extHandle;
+ } else {
+ offset = -(offset + 1);
+ tableSize = 0;
+ _vm->_dataio->closeData(_extHandle);
+ strcpy(path, "commun.ex1");
+ path[strlen(path) - 1] = *(_totFileData + 0x3c) + '0';
+ commonHandle = _vm->_dataio->openData(path);
+ handle = commonHandle;
+ }
+
+ debug(7, "off: %ld size: %ld", offset, tableSize);
+ _vm->_dataio->seekData(handle, offset + tableSize, SEEK_SET);
+ // CHECKME: is the below correct?
+ if (isPacked)
+ dataBuf = new char[size];
+ else
+ dataBuf = new char[size];
+
+ dataPtr = dataBuf;
+ while (size > 32000) {
+ // BUG: huge->far conversion. Need normalization?
+ _vm->_dataio->readData(handle, (char *)dataPtr, 32000);
+ size -= 32000;
+ dataPtr += 32000;
+ }
+ _vm->_dataio->readData(handle, (char *)dataPtr, size);
+ if (commonHandle != -1) {
+ _vm->_dataio->closeData(commonHandle);
+ _extHandle = _vm->_dataio->openData(_curExtFile);
+ }
+
+ if (isPacked != 0) {
+ packedBuf = dataBuf;
+ dataBuf = new char[READ_LE_UINT32(packedBuf)];
+ _vm->_pack->unpackData(packedBuf, dataBuf);
+ delete[] packedBuf;
+ }
+
+ return dataBuf;
+
+}
+
+void Game::clearCollisions() {
+ int16 i;
+ for (i = 0; i < 250; i++) {
+ _collisionAreas[i].id = 0;
+ _collisionAreas[i].left = -1;
+ }
+}
+
+void Game::addNewCollision(int16 id, int16 left, int16 top, int16 right, int16 bottom,
+ int16 flags, int16 key, int16 funcEnter, int16 funcLeave) {
+ int16 i;
+ Collision *ptr;
+
+ debug(5, "addNewCollision");
+ debug(5, "id = %x", id);
+ debug(5, "left = %d, top = %d, right = %d, bottom = %d", left, top, right, bottom);
+ debug(5, "flags = %x, key = %x", flags, key);
+ debug(5, "funcEnter = %d, funcLeave = %d", funcEnter, funcLeave);
+
+ for (i = 0; i < 250; i++) {
+ if (_collisionAreas[i].left != -1)
+ continue;
+
+ ptr = &_collisionAreas[i];
+ ptr->id = id;
+ ptr->left = left;
+ ptr->top = top;
+ ptr->right = right;
+ ptr->bottom = bottom;
+ ptr->flags = flags;
+ ptr->key = key;
+ ptr->funcEnter = funcEnter;
+ ptr->funcLeave = funcLeave;
+ return;
+ }
+ error("addNewCollision: Collision array full!\n");
+}
+
+void Game::freeCollision(int16 id) {
+ int16 i;
+
+ for (i = 0; i < 250; i++) {
+ if (_collisionAreas[i].id == id)
+ _collisionAreas[i].left = -1;
+ }
+}
+
+void Game::pushCollisions(char all) {
+ Collision *srcPtr;
+ Collision *destPtr;
+ int16 size;
+
+ debug(4, "pushCollisions");
+ for (size = 0, srcPtr = _collisionAreas; srcPtr->left != -1;
+ srcPtr++) {
+ if (all || (srcPtr->id & 0x8000))
+ size++;
+ }
+
+ destPtr = new Collision[size];
+ _collStack[_collStackSize] = destPtr;
+ _collStackElemSizes[_collStackSize] = size;
+ _collStackSize++;
+
+ for (srcPtr = _collisionAreas; srcPtr->left != -1; srcPtr++) {
+ if (all || (srcPtr->id & 0x8000)) {
+ memcpy(destPtr, srcPtr, sizeof(Collision));
+ srcPtr->left = -1;
+ destPtr++;
+ }
+ }
+}
+
+void Game::popCollisions(void) {
+ Collision *destPtr;
+ Collision *srcPtr;
+
+ debug(4, "popCollision");
+
+ _collStackSize--;
+ for (destPtr = _collisionAreas; destPtr->left != -1; destPtr++);
+
+ srcPtr = _collStack[_collStackSize];
+ memcpy(destPtr, srcPtr,
+ _collStackElemSizes[_collStackSize] *
+ sizeof(Collision));
+
+ free(_collStack[_collStackSize]);
+}
+
+int16 Game::checkMousePoint(int16 all, int16 *resId, int16 *resIndex) {
+ Collision *ptr;
+ int16 i;
+
+ if (resId != 0)
+ *resId = 0;
+
+ *resIndex = 0;
+
+ ptr = _collisionAreas;
+ for (i = 0; ptr->left != -1; ptr++, i++) {
+ if (all) {
+ if ((ptr->flags & 0xf) > 1)
+ continue;
+
+ if ((ptr->flags & 0xff00) != 0)
+ continue;
+
+ if (_vm->_global->_inter_mouseX < ptr->left
+ || _vm->_global->_inter_mouseX > ptr->right
+ || _vm->_global->_inter_mouseY < ptr->top
+ || _vm->_global->_inter_mouseY > ptr->bottom)
+ continue;
+
+ if (resId != 0)
+ *resId = ptr->id;
+
+ *resIndex = i;
+ return ptr->key;
+ } else {
+ if ((ptr->flags & 0xff00) != 0)
+ continue;
+
+ if ((ptr->flags & 0xf) != 1 && (ptr->flags & 0xf) != 2)
+ continue;
+
+ if ((ptr->flags & 0xf0) >> 4 != _mouseButtons - 1
+ && (ptr->flags & 0xf0) >> 4 != 2)
+ continue;
+
+ if (_vm->_global->_inter_mouseX < ptr->left
+ || _vm->_global->_inter_mouseX > ptr->right
+ || _vm->_global->_inter_mouseY < ptr->top
+ || _vm->_global->_inter_mouseY > ptr->bottom)
+ continue;
+
+ if (resId != 0)
+ *resId = ptr->id;
+ *resIndex = i;
+ return ptr->key;
+ }
+ }
+
+ if (_mouseButtons != 1 && all == 0)
+ return 0x11b;
+
+ return 0;
+}
+
+void Game::capturePush(int16 left, int16 top, int16 width, int16 height) {
+ int16 right;
+
+ if (_captureCount == 20)
+ error("capturePush: Capture stack overflow!");
+
+ _captureStack[_captureCount].left = left;
+ _captureStack[_captureCount].top = top;
+ _captureStack[_captureCount].right = left + width;
+ _captureStack[_captureCount].bottom = top + height;
+
+ _vm->_draw->_spriteTop = top;
+ _vm->_draw->_spriteBottom = height;
+
+ right = left + width - 1;
+ left &= 0xfff0;
+ right |= 0xf;
+
+ _vm->_draw->_spritesArray[30 + _captureCount] =
+ _vm->_video->initSurfDesc(_vm->_global->_videoMode, right - left + 1, height, 0);
+
+ _vm->_draw->_sourceSurface = 21;
+ _vm->_draw->_destSurface = 30 + _captureCount;
+
+ _vm->_draw->_spriteLeft = left;
+ _vm->_draw->_spriteRight = right - left + 1;
+ _vm->_draw->_destSpriteX = 0;
+ _vm->_draw->_destSpriteY = 0;
+ _vm->_draw->_transparency = 0;
+ _vm->_draw->spriteOperation(0);
+ _captureCount++;
+}
+
+void Game::capturePop(char doDraw) {
+ if (_captureCount <= 0)
+ return;
+
+ _captureCount--;
+ if (doDraw) {
+ _vm->_draw->_destSpriteX = _captureStack[_captureCount].left;
+ _vm->_draw->_destSpriteY = _captureStack[_captureCount].top;
+ _vm->_draw->_spriteRight =
+ _captureStack[_captureCount].width();
+ _vm->_draw->_spriteBottom =
+ _captureStack[_captureCount].height();
+
+ _vm->_draw->_transparency = 0;
+ _vm->_draw->_sourceSurface = 30 + _captureCount;
+ _vm->_draw->_destSurface = 21;
+ _vm->_draw->_spriteLeft = _vm->_draw->_destSpriteX & 0xf;
+ _vm->_draw->_spriteTop = 0;
+ _vm->_draw->spriteOperation(0);
+ }
+ _vm->_video->freeSurfDesc(_vm->_draw->_spritesArray[30 + _captureCount]);
+}
+
+char *Game::loadTotResource(int16 id) {
+ TotResItem *itemPtr;
+ int32 offset;
+
+ itemPtr = &_totResourceTable->items[id];
+ offset = itemPtr->offset;
+ if (offset >= 0) {
+ return ((char *)_totResourceTable) + szGame_TotResTable +
+ szGame_TotResItem * _totResourceTable->itemsCount + offset;
+ } else {
+ return (char *)(_imFileData + (int32)READ_LE_UINT32(&((int32 *)_imFileData)[-offset - 1]));
+ }
+}
+
+void Game::loadSound(int16 slot, char *dataPtr) {
+ Snd::SoundDesc *soundDesc;
+
+ soundDesc = new Snd::SoundDesc;
+
+ _soundSamples[slot] = soundDesc;
+
+ soundDesc->frequency = (dataPtr[4] << 8) + dataPtr[5];
+ soundDesc->size = (dataPtr[1] << 16) + (dataPtr[2] << 8) + dataPtr[3];
+ soundDesc->data = dataPtr + 6;
+ soundDesc->timerTicks = (int32)1193180 / (int32)soundDesc->frequency;
+
+ soundDesc->inClocks = (soundDesc->frequency * 10) / 182;
+ soundDesc->flag = 0;
+}
+
+void Game::interLoadSound(int16 slot) {
+ char *dataPtr;
+ int16 id;
+
+ if (slot == -1)
+ slot = _vm->_parse->parseValExpr();
+
+ id = _vm->_inter->load16();
+ if (id == -1) {
+ _vm->_global->_inter_execPtr += 9;
+ return;
+ }
+
+ if (id >= 30000) {
+ dataPtr = loadExtData(id, 0, 0);
+ _soundFromExt[slot] = 1;
+ } else {
+ dataPtr = loadTotResource(id);
+ _soundFromExt[slot] = 0;
+ }
+
+ loadSound(slot, dataPtr);
+}
+
+void Game::freeSoundSlot(int16 slot) {
+ if (slot == -1)
+ slot = _vm->_parse->parseValExpr();
+
+ if (_soundSamples[slot] == 0)
+ return;
+
+ if (_soundFromExt[slot] == 1) {
+ delete[] (_soundSamples[slot]->data - 6);
+ _soundFromExt[slot] = 0;
+ }
+
+ delete _soundSamples[slot];
+ _soundSamples[slot] = 0;
+}
+
+int16 Game::checkKeys(int16 *pMouseX, int16 *pMouseY, int16 *pButtons, char handleMouse) {
+ _vm->_util->processInput();
+
+ if (VAR(58) != 0) {
+ if (_vm->_mult->_frameStart != (int)VAR(58) - 1)
+ _vm->_mult->_frameStart++;
+ else
+ _vm->_mult->_frameStart = 0;
+
+ _vm->_mult->playMult(_vm->_mult->_frameStart + VAR(57), _vm->_mult->_frameStart + VAR(57), 1,
+ handleMouse);
+ }
+
+ if (_vm->_inter->_soundEndTimeKey != 0
+ && _vm->_util->getTimeKey() >= _vm->_inter->_soundEndTimeKey) {
+ _vm->_snd->stopSound(_vm->_inter->_soundStopVal);
+ _vm->_inter->_soundEndTimeKey = 0;
+ }
+
+ if (_vm->_global->_useMouse == 0)
+ error("checkKeys: Can't work without mouse!");
+
+ _vm->_util->getMouseState(pMouseX, pMouseY, pButtons);
+
+ if (*pButtons == 3)
+ *pButtons = 0;
+
+ return _vm->_util->checkKey();
+}
+
+int16 Game::checkCollisions(char handleMouse, int16 deltaTime, int16 *pResId,
+ int16 *pResIndex) {
+ char *savedIP;
+ int16 resIndex;
+ int16 key;
+ int16 oldIndex;
+ int16 oldId;
+ uint32 timeKey;
+
+ if (deltaTime >= -1) {
+ _lastCollKey = 0;
+ _lastCollAreaIndex = 0;
+ _lastCollId = 0;
+ }
+
+ if (pResId != 0)
+ *pResId = 0;
+
+ resIndex = 0;
+
+ if (_vm->_draw->_cursorIndex == -1 && handleMouse != 0
+ && _lastCollKey == 0) {
+ _lastCollKey =
+ checkMousePoint(1, &_lastCollId,
+ &_lastCollAreaIndex);
+
+ if (_lastCollKey != 0 && (_lastCollId & 0x8000) != 0) {
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr = (char *)_totFileData +
+ _collisionAreas[_lastCollAreaIndex].funcEnter;
+
+ _vm->_inter->funcBlock(0);
+ _vm->_global->_inter_execPtr = savedIP;
+ }
+ }
+
+ if (handleMouse != 0)
+ _vm->_draw->animateCursor(-1);
+
+ timeKey = _vm->_util->getTimeKey();
+ while (1) {
+ if (_vm->_inter->_terminate) {
+ if (handleMouse)
+ _vm->_draw->blitCursor();
+ return 0;
+ }
+
+ if (_vm->_draw->_noInvalidated == 0) {
+ if (handleMouse)
+ _vm->_draw->animateCursor(-1);
+ else
+ _vm->_draw->blitInvalidated();
+ }
+
+ // NOTE: the original asm does the below checkKeys call
+ // _before_ this check. However, that can cause keypresses to get lost
+ // since there's a return statement in this check.
+ // Additionally, I added a 'deltaTime == -1' check there, since
+ // when this function is called with deltaTime == -1 in inputArea,
+ // and the return value is then discarded.
+ if (deltaTime < 0) {
+ uint32 curtime = _vm->_util->getTimeKey();
+ if (deltaTime == -1 || curtime + deltaTime > timeKey) {
+ if (pResId != 0)
+ *pResId = 0;
+
+ if (pResIndex != 0)
+ *pResIndex = 0;
+
+ return 0;
+ }
+ }
+
+ key = checkKeys(&_vm->_global->_inter_mouseX, &_vm->_global->_inter_mouseY,
+ &_mouseButtons, handleMouse);
+
+ if (handleMouse == 0 && _mouseButtons != 0) {
+ _vm->_util->waitMouseRelease(0);
+ key = 3;
+ }
+
+ if (key != 0) {
+
+ if (handleMouse == 1)
+ _vm->_draw->blitCursor();
+
+ if (pResId != 0)
+ *pResId = 0;
+
+ if (pResIndex != 0)
+ *pResIndex = 0;
+
+ if (_lastCollKey != 0 &&
+ _collisionAreas[_lastCollAreaIndex].funcLeave != 0) {
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr = (char *)_totFileData +
+ _collisionAreas[_lastCollAreaIndex].funcLeave;
+
+ _vm->_inter->funcBlock(0);
+ _vm->_global->_inter_execPtr = savedIP;
+ }
+
+ _lastCollKey = 0;
+ if (key != 0)
+ return key;
+ }
+
+ if (handleMouse != 0) {
+ if (_mouseButtons != 0) {
+ oldIndex = 0;
+
+ _vm->_draw->animateCursor(2);
+ if (deltaTime <= 0) {
+ if (handleMouse == 1)
+ _vm->_util->waitMouseRelease(1);
+ } else if (deltaTime > 0) {
+ _vm->_util->delay(deltaTime);
+ }
+
+ _vm->_draw->animateCursor(-1);
+ if (pResId != 0)
+ *pResId = 0;
+
+ key = checkMousePoint(0, pResId, &resIndex);
+
+ if (pResIndex != 0)
+ *pResIndex = resIndex;
+
+ if (key != 0 || (pResId != 0 && *pResId != 0)) {
+ if (handleMouse == 1 && (deltaTime <= 0
+ || _mouseButtons == 0))
+ _vm->_draw->blitCursor();
+
+ if (_lastCollKey != 0 &&
+ _collisionAreas[_lastCollAreaIndex].funcLeave != 0) {
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr =
+ (char *)_totFileData +
+ _collisionAreas[_lastCollAreaIndex].funcLeave;
+
+ _vm->_inter->funcBlock(0);
+ _vm->_global->_inter_execPtr = savedIP;
+ }
+ _lastCollKey = 0;
+ return key;
+ }
+
+ if (_lastCollKey != 0 &&
+ _collisionAreas[_lastCollAreaIndex].funcLeave != 0) {
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr =
+ (char *)_totFileData +
+ _collisionAreas[_lastCollAreaIndex].funcLeave;
+
+ _vm->_inter->funcBlock(0);
+ _vm->_global->_inter_execPtr = savedIP;
+ }
+
+ _lastCollKey =
+ checkMousePoint(1, &_lastCollId,
+ &_lastCollAreaIndex);
+
+ if (_lastCollKey != 0
+ && (_lastCollId & 0x8000) != 0) {
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr =
+ (char *)_totFileData +
+ _collisionAreas[_lastCollAreaIndex].funcEnter;
+
+ _vm->_inter->funcBlock(0);
+ _vm->_global->_inter_execPtr = savedIP;
+ }
+ } else {
+
+ if (handleMouse != 0 &&
+ (_vm->_global->_inter_mouseX != _vm->_draw->_cursorX
+ || _vm->_global->_inter_mouseY != _vm->_draw->_cursorY)) {
+ oldIndex = _lastCollAreaIndex;
+ oldId = _lastCollId;
+
+ key =
+ checkMousePoint(1,
+ &_lastCollId,
+ &_lastCollAreaIndex);
+
+ if (key != _lastCollKey) {
+ if (_lastCollKey != 0
+ && (oldId & 0x8000) != 0) {
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr = (char *)_totFileData +
+ _collisionAreas[oldIndex].funcLeave;
+
+ _vm->_inter->funcBlock(0);
+ _vm->_global->_inter_execPtr = savedIP;
+ }
+
+ _lastCollKey = key;
+ if (_lastCollKey != 0 && (_lastCollId & 0x8000) != 0) {
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr = (char *)_totFileData +
+ _collisionAreas[_lastCollAreaIndex].funcEnter;
+
+ _vm->_inter->funcBlock(0);
+ _vm->_global->_inter_execPtr = savedIP;
+ }
+ }
+ }
+ }
+ }
+
+ if (handleMouse != 0)
+ _vm->_draw->animateCursor(-1);
+
+ _vm->_util->delay(10);
+
+ _vm->_snd->loopSounds();
+ }
+}
+
+int16 Game::inputArea(int16 xPos, int16 yPos, int16 width, int16 height, int16 backColor,
+ int16 frontColor, char *str, int16 fontIndex, char inpType, int16 *pTotTime) {
+ int16 handleMouse;
+ uint32 editSize;
+ Video::FontDesc *pFont;
+ char curSym;
+ int16 key;
+ const char *str1;
+ const char *str2;
+ int16 i;
+ uint32 pos;
+ int16 flag;
+ int16 savedKey;
+
+ if (_handleMouse != 0 &&
+ (_vm->_global->_useMouse != 0 || _forceHandleMouse != 0))
+ handleMouse = 1;
+ else
+ handleMouse = 0;
+
+ pos = strlen(str);
+ pFont = _vm->_draw->_fonts[fontIndex];
+ editSize = width / pFont->itemWidth;
+
+ while (1) {
+ strcpy(_tempStr, str);
+ strcat(_tempStr, " ");
+ if (strlen(_tempStr) > editSize)
+ strcpy(_tempStr, str);
+
+ _vm->_draw->_destSpriteX = xPos;
+ _vm->_draw->_destSpriteY = yPos;
+ _vm->_draw->_spriteRight = editSize * pFont->itemWidth;
+ _vm->_draw->_spriteBottom = height;
+
+ _vm->_draw->_destSurface = 21;
+ _vm->_draw->_backColor = backColor;
+ _vm->_draw->_frontColor = frontColor;
+ _vm->_draw->_textToPrint = _tempStr;
+ _vm->_draw->_transparency = 1;
+ _vm->_draw->_fontIndex = fontIndex;
+ _vm->_draw->spriteOperation(DRAW_FILLRECT);
+
+ _vm->_draw->_destSpriteY = yPos + (height - 8) / 2;
+
+ _vm->_draw->spriteOperation(DRAW_PRINTTEXT);
+ if (pos == editSize)
+ pos--;
+
+ curSym = _tempStr[pos];
+
+ flag = 1;
+
+ while (1) {
+ _tempStr[0] = curSym;
+ _tempStr[1] = 0;
+
+ _vm->_draw->_destSpriteX = xPos + pFont->itemWidth * pos;
+ _vm->_draw->_destSpriteY = yPos + height - 1;
+ _vm->_draw->_spriteRight = pFont->itemWidth;
+ _vm->_draw->_spriteBottom = 1;
+ _vm->_draw->_destSurface = 21;
+ _vm->_draw->_backColor = frontColor;
+ _vm->_draw->spriteOperation(DRAW_FILLRECT);
+
+ if (flag != 0) {
+ key = checkCollisions(handleMouse, -1,
+ &_activeCollResId,
+ &_activeCollIndex);
+ }
+ flag = 0;
+
+ key = checkCollisions(handleMouse, -300,
+ &_activeCollResId, &_activeCollIndex);
+
+ if (*pTotTime > 0) {
+ *pTotTime -= 300;
+ if (*pTotTime <= 1) {
+ key = 0;
+ _activeCollResId = 0;
+ break;
+ }
+ }
+
+ _tempStr[0] = curSym;
+ _tempStr[1] = 0;
+ _vm->_draw->_destSpriteX = xPos + pFont->itemWidth * pos;
+ _vm->_draw->_destSpriteY = yPos + height - 1;
+ _vm->_draw->_spriteRight = pFont->itemWidth;
+ _vm->_draw->_spriteBottom = 1;
+ _vm->_draw->_destSurface = 21;
+ _vm->_draw->_backColor = backColor;
+ _vm->_draw->_frontColor = frontColor;
+ _vm->_draw->_textToPrint = _tempStr;
+ _vm->_draw->_transparency = 1;
+ _vm->_draw->spriteOperation(DRAW_FILLRECT);
+
+ _vm->_draw->_destSpriteY = yPos + (height - 8) / 2;
+ _vm->_draw->spriteOperation(DRAW_PRINTTEXT);
+
+ if (key != 0 || _activeCollResId != 0)
+ break;
+
+ key = checkCollisions(handleMouse, -300,
+ &_activeCollResId, &_activeCollIndex);
+
+ if (*pTotTime > 0) {
+ *pTotTime -= 300;
+ if (*pTotTime <= 1) {
+ key = 0;
+ _activeCollResId = 0;
+ break;
+ }
+
+ }
+ if (key != 0 || _activeCollResId != 0)
+ break;
+
+ if (_vm->_inter->_terminate)
+ return 0;
+ }
+
+ if (key == 0 || _activeCollResId != 0 || _vm->_inter->_terminate)
+ return 0;
+
+ switch (key) {
+ case 0x4d00: // Right Arrow
+ if (pos < strlen(str) && pos < editSize - 1) {
+ pos++;
+ continue;
+ }
+ return 0x5000;
+
+ case 0x4b00: // Left Arrow
+ if (pos > 0) {
+ pos--;
+ continue;
+ }
+ return 0x4800;
+
+ case 0xe08: // Backspace
+ if (pos > 0) {
+ _vm->_util->cutFromStr(str, pos - 1, 1);
+ pos--;
+ continue;
+ }
+
+ case 0x5300: // Del
+
+ if (pos >= strlen(str))
+ continue;
+
+ _vm->_util->cutFromStr(str, pos, 1);
+ continue;
+
+ case 0x1c0d: // Enter
+ case 0x3b00: // F1
+ case 0x3c00: // F2
+ case 0x3d00: // F3
+ case 0x3e00: // F4
+ case 0x3f00: // F5
+ case 0x4000: // F6
+ case 0x4100: // F7
+ case 0x4200: // F8
+ case 0x4300: // F9
+ case 0x4400: // F10
+ case 0x4800: // Up arrow
+ case 0x5000: // Down arrow
+ return key;
+
+ case 0x11b: // Escape
+ if (_vm->_global->_useMouse != 0)
+ continue;
+
+ _forceHandleMouse = !_forceHandleMouse;
+
+ if (_handleMouse != 0 &&
+ (_vm->_global->_useMouse != 0 || _forceHandleMouse != 0))
+ handleMouse = 1;
+ else
+ handleMouse = 0;
+
+ if (_vm->_global->_pressedKeys[1] == 0)
+ continue;
+
+ while (_vm->_global->_pressedKeys[1] != 0);
+ continue;
+
+ default:
+
+ savedKey = key;
+ key &= 0xff;
+
+ if ((inpType == 9 || inpType == 10) && key >= ' '
+ && key <= 0xff) {
+ str1 = "0123456789-.,+ ";
+ str2 = "0123456789-,,+ ";
+
+ if ((savedKey >> 8) > 1
+ && (savedKey >> 8) < 12)
+ key = ((savedKey >> 8) - 1) % 10 + '0';
+
+ for (i = 0; str1[i] != 0; i++) {
+ if (key == str1[i]) {
+ key = str2[i];
+ break;
+ }
+ }
+
+ if (i == (int16)strlen(str1))
+ key = 0;
+ }
+
+ if (key >= ' ' && key <= 0xff) {
+ if (editSize == strlen(str))
+ _vm->_util->cutFromStr(str, strlen(str) - 1,
+ 1);
+
+ if (key >= 'a' && key <= 'z')
+ key += ('A' - 'a');
+
+ pos++;
+ _tempStr[0] = key;
+ _tempStr[1] = 0;
+
+ _vm->_util->insertStr(_tempStr, str, pos - 1);
+
+ //strupr(str);
+ }
+ }
+ }
+}
+
+int16 Game::multiEdit(int16 time, int16 index, int16 *pCurPos, InputDesc * inpDesc) {
+ Collision *collArea;
+ int16 descInd;
+ int16 key;
+ int16 found = -1;
+ int16 i;
+
+ descInd = 0;
+ for (i = 0; i < 250; i++) {
+ collArea = &_collisionAreas[i];
+
+ if (collArea->left == -1)
+ continue;
+
+ if ((collArea->id & 0x8000) == 0)
+ continue;
+
+ if ((collArea->flags & 0x0f) < 3)
+ continue;
+
+ if ((collArea->flags & 0x0f) > 10)
+ continue;
+
+ strcpy(_tempStr, _vm->_global->_inter_variables + collArea->key);
+
+ _vm->_draw->_destSpriteX = collArea->left;
+ _vm->_draw->_destSpriteY = collArea->top;
+ _vm->_draw->_spriteRight = collArea->right - collArea->left + 1;
+ _vm->_draw->_spriteBottom = collArea->bottom - collArea->top + 1;
+
+ _vm->_draw->_destSurface = 21;
+
+ _vm->_draw->_backColor = inpDesc[descInd].backColor;
+ _vm->_draw->_frontColor = inpDesc[descInd].frontColor;
+ _vm->_draw->_textToPrint = _tempStr;
+ _vm->_draw->_transparency = 1;
+ _vm->_draw->_fontIndex = inpDesc[descInd].fontIndex;
+ _vm->_draw->spriteOperation(DRAW_FILLRECT);
+ _vm->_draw->_destSpriteY +=
+ ((collArea->bottom - collArea->top + 1) - 8) / 2;
+
+ _vm->_draw->spriteOperation(DRAW_PRINTTEXT);
+ descInd++;
+ }
+
+ for (i = 0; i < 40; i++) {
+ WRITE_VAR_OFFSET(i * 4 + 0x44, 0);
+ }
+
+ while (1) {
+ descInd = 0;
+
+ for (i = 0; i < 250; i++) {
+ collArea = &_collisionAreas[i];
+
+ if (collArea->left == -1)
+ continue;
+
+ if ((collArea->id & 0x8000) == 0)
+ continue;
+
+ if ((collArea->flags & 0x0f) < 3)
+ continue;
+
+ if ((collArea->flags & 0x0f) > 10)
+ continue;
+
+ if (descInd == *pCurPos) {
+ found = i;
+ break;
+ }
+
+ descInd++;
+ }
+
+ assert(found != -1);
+
+ collArea = &_collisionAreas[found];
+
+ key = inputArea(collArea->left, collArea->top,
+ collArea->right - collArea->left + 1,
+ collArea->bottom - collArea->top + 1,
+ inpDesc[*pCurPos].backColor, inpDesc[*pCurPos].frontColor,
+ _vm->_global->_inter_variables + collArea->key,
+ inpDesc[*pCurPos].fontIndex, collArea->flags, &time);
+
+ if (_vm->_inter->_terminate)
+ return 0;
+
+ switch (key) {
+ case 0:
+ if (_activeCollResId == 0)
+ return 0;
+
+ if ((_collisionAreas[_activeCollIndex].
+ flags & 0x0f) < 3)
+ return 0;
+
+ if ((_collisionAreas[_activeCollIndex].
+ flags & 0x0f) > 10)
+ return 0;
+
+ *pCurPos = 0;
+ for (i = 0; i < 250; i++) {
+ collArea = &_collisionAreas[i];
+
+ if (collArea->left == -1)
+ continue;
+
+ if ((collArea->id & 0x8000) == 0)
+ continue;
+
+ if ((collArea->flags & 0x0f) < 3)
+ continue;
+
+ if ((collArea->flags & 0x0f) > 10)
+ continue;
+
+ if (i == _activeCollIndex)
+ break;
+
+ pCurPos[0]++;
+ }
+ break;
+
+ case 0x3b00:
+ case 0x3c00:
+ case 0x3d00:
+ case 0x3e00:
+ case 0x3f00:
+ case 0x4000:
+ case 0x4100:
+ case 0x4200:
+ case 0x4300:
+ case 0x4400:
+ return key;
+
+ case 0x1c0d:
+
+ if (index == 1)
+ return key;
+
+ if (*pCurPos == index - 1) {
+ *pCurPos = 0;
+ break;
+ }
+
+ pCurPos[0]++;
+ break;
+
+ case 0x5000:
+ if (index - 1 > *pCurPos)
+ pCurPos[0]++;
+ break;
+
+ case 0x4800:
+ if (*pCurPos > 0)
+ pCurPos[0]--;
+ break;
+ }
+ }
+}
+
+int16 Game::adjustKey(int16 key) {
+ if (key <= 0x60 || key >= 0x7b)
+ return key;
+
+ return key - 0x20;
+}
+
+void Game::collisionsBlock(void) {
+ InputDesc descArray[20];
+ int16 array[250];
+ char count;
+ int16 collResId;
+ char *startIP;
+ int16 curCmd;
+ int16 cmd;
+ int16 cmdHigh;
+ int16 key;
+ int16 flags;
+ int16 left;
+ int16 top;
+ int16 width;
+ int16 height;
+ int16 var_22;
+ int16 index;
+ int16 curEditIndex;
+ int16 deltaTime;
+ int16 descIndex2;
+ int16 stackPos2;
+ int16 descIndex;
+ int16 timeVal;
+ char *str;
+ int16 savedCollStackSize;
+ int16 i;
+ int16 counter;
+ int16 var_24;
+ int16 var_26;
+ int16 _collStackPos;
+ Collision *collPtr;
+ int16 timeKey;
+ char *savedIP;
+
+ if (_shouldPushColls)
+ pushCollisions(1);
+
+ collResId = -1;
+ _vm->_global->_inter_execPtr++;
+ count = *_vm->_global->_inter_execPtr++;
+ _handleMouse = _vm->_global->_inter_execPtr[0];
+ deltaTime = 1000 * (byte)_vm->_global->_inter_execPtr[1];
+ descIndex2 = (byte)_vm->_global->_inter_execPtr[2];
+ stackPos2 = (byte)_vm->_global->_inter_execPtr[3];
+ descIndex = (byte)_vm->_global->_inter_execPtr[4];
+
+ if (stackPos2 != 0 || descIndex != 0)
+ deltaTime /= 100;
+
+ timeVal = deltaTime;
+ _vm->_global->_inter_execPtr += 6;
+
+ startIP = _vm->_global->_inter_execPtr;
+ WRITE_VAR(16, 0);
+ var_22 = 0;
+ index = 0;
+ curEditIndex = 0;
+
+ for (curCmd = 0; curCmd < count; curCmd++) {
+ array[curCmd] = 0;
+ cmd = *_vm->_global->_inter_execPtr++;
+
+ if ((cmd & 0x40) != 0) {
+ cmd -= 0x40;
+ cmdHigh = (byte)*_vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr++;
+ cmdHigh <<= 8;
+ } else {
+ cmdHigh = 0;
+ }
+
+ if ((cmd & 0x80) != 0) {
+ left = _vm->_parse->parseValExpr();
+ top = _vm->_parse->parseValExpr();
+ width = _vm->_parse->parseValExpr();
+ height = _vm->_parse->parseValExpr();
+ } else {
+ left = _vm->_inter->load16();
+ top = _vm->_inter->load16();
+ width = _vm->_inter->load16();
+ height = _vm->_inter->load16();
+ }
+ cmd &= 0x7f;
+
+ debug(4, "collisionsBlock(%d)", cmd);
+
+ switch (cmd) {
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+
+ _vm->_util->waitKey();
+ var_22 = 1;
+ key = _vm->_parse->parseVarIndex();
+ descArray[index].fontIndex = _vm->_inter->load16();
+ descArray[index].backColor = *_vm->_global->_inter_execPtr++;
+ descArray[index].frontColor = *_vm->_global->_inter_execPtr++;
+
+ if (cmd < 5 || cmd > 8) {
+ descArray[index].ptr = 0;
+ } else {
+ descArray[index].ptr = _vm->_global->_inter_execPtr + 2;
+ _vm->_global->_inter_execPtr += _vm->_inter->load16();
+ }
+
+ if (left == -1)
+ break;
+
+ if ((cmd & 1) == 0) {
+ addNewCollision(curCmd + 0x8000, left,
+ top,
+ left +
+ width *
+ _vm->_draw->_fonts[descArray[index].fontIndex]->
+ itemWidth - 1, top + height - 1, cmd, key,
+ 0,
+ _vm->_global->_inter_execPtr - (char *)_totFileData);
+
+ _vm->_global->_inter_execPtr += 2;
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
+ } else {
+ addNewCollision(curCmd + 0x8000, left,
+ top,
+ left +
+ width *
+ _vm->_draw->_fonts[descArray[index].fontIndex]->
+ itemWidth - 1, top + height - 1, cmd, key,
+ 0, 0);
+ }
+ index++;
+ break;
+
+ case 21:
+ key = _vm->_inter->load16();
+ array[curCmd] = _vm->_inter->load16();
+ flags = _vm->_inter->load16() & 3;
+
+ addNewCollision(curCmd + 0x8000, left, top,
+ left + width - 1,
+ top + height - 1,
+ (flags << 4) + cmdHigh + 2, key,
+ _vm->_global->_inter_execPtr - (char *)_totFileData, 0);
+
+ _vm->_global->_inter_execPtr += 2;
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
+ break;
+
+ case 20:
+ collResId = curCmd;
+ // Fall through to case 2
+
+ case 2:
+ key = _vm->_inter->load16();
+ array[curCmd] = _vm->_inter->load16();
+ flags = _vm->_inter->load16() & 3;
+
+ addNewCollision(curCmd + 0x8000, left, top,
+ left + width - 1,
+ top + height - 1,
+ (flags << 4) + cmdHigh + 2, key, 0,
+ _vm->_global->_inter_execPtr - (char *)_totFileData);
+
+ _vm->_global->_inter_execPtr += 2;
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
+ break;
+
+ case 0:
+ _vm->_global->_inter_execPtr += 6;
+ startIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += 2;
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
+ key = curCmd + 0xA000;
+
+ addNewCollision(curCmd + 0x8000, left, top,
+ left + width - 1,
+ top + height - 1,
+ cmd + cmdHigh, key,
+ startIP - (char *)_totFileData,
+ _vm->_global->_inter_execPtr - (char *)_totFileData);
+
+ _vm->_global->_inter_execPtr += 2;
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
+ break;
+
+ case 1:
+ key = _vm->_inter->load16();
+ array[curCmd] = _vm->_inter->load16();
+ flags = _vm->_inter->load16() & 3;
+
+ startIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += 2;
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
+ if (key == 0)
+ key = curCmd + 0xa000;
+
+ addNewCollision(curCmd + 0x8000, left, top,
+ left + width - 1,
+ top + height - 1,
+ (flags << 4) + cmd + cmdHigh, key,
+ startIP - (char *)_totFileData,
+ _vm->_global->_inter_execPtr - (char *)_totFileData);
+
+ _vm->_global->_inter_execPtr += 2;
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr);
+ break;
+ }
+ }
+
+ _forceHandleMouse = 0;
+ _vm->_util->waitKey();
+
+ do {
+ if (var_22 != 0) {
+ key =
+ multiEdit(deltaTime, index, &curEditIndex,
+ descArray);
+
+ if (key == 0x1c0d) {
+ for (i = 0; i < 250; i++) {
+ if (_collisionAreas[i].left == -1)
+ continue;
+
+ if ((_collisionAreas[i].id & 0x8000) == 0)
+ continue;
+
+ if ((_collisionAreas[i].flags & 1) != 0)
+ continue;
+
+ if ((_collisionAreas[i].flags & 0x0f) <= 2)
+ continue;
+
+ collResId = _collisionAreas[i].id;
+ _activeCollResId = collResId;
+ collResId &= 0x7fff;
+ _activeCollIndex = i;
+ break;
+ }
+ break;
+ }
+ } else {
+ key =
+ checkCollisions(_handleMouse, -deltaTime,
+ &_activeCollResId, &_activeCollIndex);
+ }
+
+ if ((key & 0xff) >= ' ' && (key & 0xff) <= 0xff &&
+ (key >> 8) > 1 && (key >> 8) < 12) {
+ key = '0' + (((key >> 8) - 1) % 10) + (key & 0xff00);
+ }
+
+ if (_activeCollResId == 0) {
+ if (key != 0) {
+ for (i = 0; i < 250; i++) {
+ if (_collisionAreas[i].left == -1)
+ continue;
+
+ if ((_collisionAreas[i].
+ id & 0x8000) == 0)
+ continue;
+
+ if (_collisionAreas[i].key == key
+ || _collisionAreas[i].key ==
+ 0x7fff) {
+
+ _activeCollResId =
+ _collisionAreas[i].id;
+ _activeCollIndex = i;
+ break;
+ }
+ }
+
+ if (_activeCollResId == 0) {
+ for (i = 0; i < 250; i++) {
+ if (_collisionAreas[i].left == -1)
+ continue;
+
+ if ((_collisionAreas[i].id & 0x8000) == 0)
+ continue;
+
+ if ((_collisionAreas[i].key & 0xff00) != 0)
+ continue;
+
+ if (_collisionAreas[i].key == 0)
+ continue;
+
+ if (adjustKey(key & 0xff) == adjustKey(_collisionAreas[i].key) || _collisionAreas[i].key == 0x7fff) {
+ _activeCollResId = _collisionAreas[i].id;
+ _activeCollIndex = i;
+ break;
+ }
+ }
+ }
+ } else {
+
+ if (deltaTime != 0 && VAR(16) == 0) {
+ if (stackPos2 != 0) {
+ _collStackPos = 0;
+ collPtr = _collisionAreas;
+
+ for (i = 0, collPtr = _collisionAreas; collPtr->left != -1; i++, collPtr++) {
+ if ((collPtr->id & 0x8000) == 0)
+ continue;
+
+ _collStackPos++;
+ if (_collStackPos != stackPos2)
+ continue;
+
+ _activeCollResId = collPtr->id;
+ _activeCollIndex = i;
+ WRITE_VAR(2, _vm->_global->_inter_mouseX);
+ WRITE_VAR(3, _vm->_global->_inter_mouseY);
+ WRITE_VAR(4, _mouseButtons);
+ WRITE_VAR(16, array[(uint16)_activeCollResId & ~0x8000]);
+
+ if (collPtr->funcLeave != 0) {
+ timeKey = _vm->_util->getTimeKey();
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr = (char *)_totFileData + collPtr->funcLeave;
+ _shouldPushColls = 1;
+ savedCollStackSize = _collStackSize;
+ _vm->_inter->funcBlock(0);
+
+ if (savedCollStackSize != _collStackSize)
+ popCollisions();
+
+ _shouldPushColls = 0;
+ _vm->_global->_inter_execPtr = savedIP;
+ deltaTime = timeVal - (_vm->_util->getTimeKey() - timeKey);
+
+ if (deltaTime < 2)
+ deltaTime = 2;
+ }
+
+ if (VAR(16) == 0)
+ _activeCollResId = 0;
+ break;
+ }
+ } else {
+ if (descIndex != 0) {
+ counter = 0;
+ for (i = 0; i < 250; i++) {
+ if (_collisionAreas[i].left == -1)
+ continue;
+
+ if ((_collisionAreas[i].id & 0x8000) == 0)
+ continue;
+
+ counter++;
+ if (counter != descIndex)
+ continue;
+
+ _activeCollResId = _collisionAreas[i].id;
+ _activeCollIndex = i;
+ break;
+ }
+ } else {
+ for (i = 0; i < 250; i++) {
+ if (_collisionAreas[i].left == -1)
+ continue;
+
+ if ((_collisionAreas[i].id & 0x8000) == 0)
+ continue;
+
+ _activeCollResId = _collisionAreas[i].id;
+ _activeCollIndex = i;
+ break;
+ }
+ }
+ }
+ } else {
+ if (descIndex2 != 0) {
+ counter = 0;
+ for (i = 0; i < 250; i++) {
+ if (_collisionAreas[i].left == -1)
+ continue;
+
+ if ((_collisionAreas[i].id & 0x8000) == 0)
+ continue;
+
+ counter++;
+ if (counter != descIndex2)
+ continue;
+
+ _activeCollResId = _collisionAreas[i].id;
+ _activeCollIndex = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (_activeCollResId == 0)
+ continue;
+
+ if (_collisionAreas[_activeCollIndex].funcLeave != 0)
+ continue;
+
+ WRITE_VAR(2, _vm->_global->_inter_mouseX);
+ WRITE_VAR(3, _vm->_global->_inter_mouseY);
+ WRITE_VAR(4, _mouseButtons);
+ WRITE_VAR(16, array[(uint16)_activeCollResId & ~0x8000]);
+
+ if (_collisionAreas[_activeCollIndex].funcEnter != 0) {
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr = (char *)_totFileData +
+ _collisionAreas[_activeCollIndex].
+ funcEnter;
+
+ _shouldPushColls = 1;
+
+ _collStackPos = _collStackSize;
+ _vm->_inter->funcBlock(0);
+ if (_collStackPos != _collStackSize)
+ popCollisions();
+ _shouldPushColls = 0;
+ _vm->_global->_inter_execPtr = savedIP;
+ }
+
+ WRITE_VAR(16, 0);
+ _activeCollResId = 0;
+ }
+ while (_activeCollResId == 0 && !_vm->_inter->_terminate);
+
+ if (((uint16)_activeCollResId & ~0x8000) == collResId) {
+ _collStackPos = 0;
+ var_24 = 0;
+ var_26 = 1;
+ for (i = 0; i < 250; i++) {
+ if (_collisionAreas[i].left == -1)
+ continue;
+
+ if ((_collisionAreas[i].id & 0x8000) == 0)
+ continue;
+
+ if ((_collisionAreas[i].flags & 0x0f) < 3)
+ continue;
+
+ if ((_collisionAreas[i].flags & 0x0f) > 10)
+ continue;
+
+ if ((_collisionAreas[i].flags & 0x0f) > 8) {
+ char *ptr;
+ strcpy(_tempStr,
+ _vm->_global->_inter_variables + _collisionAreas[i].key);
+ while ((ptr = strchr(_tempStr, ' ')) != 0) {
+ _vm->_util->cutFromStr(_tempStr, (ptr - _tempStr), 1);
+ ptr = strchr(_tempStr, ' ');
+ }
+ strcpy(_vm->_global->_inter_variables + _collisionAreas[i].key, _tempStr);
+ }
+
+ if ((_collisionAreas[i].flags & 0x0f) >= 5 &&
+ (_collisionAreas[i].flags & 0x0f) <= 8) {
+ str = descArray[var_24].ptr;
+
+ strcpy(_tempStr, _vm->_global->_inter_variables + _collisionAreas[i].key);
+
+ if ((_collisionAreas[i].flags & 0x0f) < 7)
+ _vm->_util->prepareStr(_tempStr);
+
+ int16 pos = 0;
+ do {
+ strcpy(_collStr, str);
+ pos += strlen(str) + 1;
+
+ str += strlen(str) + 1;
+
+ if ((_collisionAreas[i].flags & 0x0f) < 7)
+ _vm->_util->prepareStr(_collStr);
+
+ if (strcmp(_tempStr, _collStr) == 0) {
+ VAR(17)++;
+ WRITE_VAR(17 + var_26, 1);
+ break;
+ }
+ } while (READ_LE_UINT16(descArray[var_24].ptr - 2) > pos);
+ _collStackPos++;
+ } else {
+ VAR(17 + var_26) = 2;
+ }
+ var_24++;
+ var_26++;
+ }
+
+ if (_collStackPos != (int16)VAR(17))
+ WRITE_VAR(17, 0);
+ else
+ WRITE_VAR(17, 1);
+ }
+
+ savedIP = 0;
+ if (!_vm->_inter->_terminate) {
+ savedIP = (char *)_totFileData +
+ _collisionAreas[_activeCollIndex].funcLeave;
+
+ WRITE_VAR(2, _vm->_global->_inter_mouseX);
+ WRITE_VAR(3, _vm->_global->_inter_mouseY);
+ WRITE_VAR(4, _mouseButtons);
+
+ if (VAR(16) == 0) {
+ WRITE_VAR(16, array[(uint16)_activeCollResId & ~0x8000]);
+ }
+ }
+
+ for (curCmd = 0; curCmd < count; curCmd++) {
+ freeCollision(curCmd + 0x8000);
+ }
+ _vm->_global->_inter_execPtr = savedIP;
+}
+
+void Game::prepareStart(void) {
+ int16 i;
+
+ clearCollisions();
+
+ _vm->_global->_pPaletteDesc->unused2 = _vm->_draw->_unusedPalette2;
+ _vm->_global->_pPaletteDesc->unused1 = _vm->_draw->_unusedPalette1;
+ _vm->_global->_pPaletteDesc->vgaPal = _vm->_draw->_vgaPalette;
+
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+
+ _vm->_draw->_backSurface = _vm->_video->initSurfDesc(_vm->_global->_videoMode, 320, 200, 0);
+
+ _vm->_video->fillRect(_vm->_draw->_backSurface, 0, 0, 319, 199, 1);
+ _vm->_draw->_frontSurface = _vm->_global->_pPrimarySurfDesc;
+ _vm->_video->fillRect(_vm->_draw->_frontSurface, 0, 0, 319, 199, 1);
+
+ _vm->_util->setMousePos(152, 92);
+
+ _vm->_draw->_cursorX = 152;
+ _vm->_global->_inter_mouseX = 152;
+
+ _vm->_draw->_cursorY = 92;
+ _vm->_global->_inter_mouseY = 92;
+ _vm->_draw->_invalidatedCount = 0;
+ _vm->_draw->_noInvalidated = 1;
+ _vm->_draw->_applyPal = 0;
+ _vm->_draw->_paletteCleared = 0;
+ _vm->_draw->_cursorWidth = 16;
+ _vm->_draw->_cursorHeight = 16;
+ _vm->_draw->_transparentCursor = 1;
+
+ for (i = 0; i < 40; i++) {
+ _vm->_draw->_cursorAnimLow[i] = -1;
+ _vm->_draw->_cursorAnimDelays[i] = 0;
+ _vm->_draw->_cursorAnimHigh[i] = 0;
+ }
+
+ _vm->_draw->_cursorAnimLow[1] = 0;
+ _vm->_draw->_cursorSprites = _vm->_video->initSurfDesc(_vm->_global->_videoMode, 32, 16, 2);
+ _vm->_draw->_cursorBack = _vm->_video->initSurfDesc(_vm->_global->_videoMode, 16, 16, 0);
+ _vm->_draw->_renderFlags = 0;
+ _vm->_draw->_backDeltaX = 0;
+ _vm->_draw->_backDeltaY = 0;
+
+ _startTimeKey = _vm->_util->getTimeKey();
+}
+
+void Game::loadTotFile(char *path) {
+ int16 handle;
+
+ handle = _vm->_dataio->openData(path);
+ if (handle >= 0) {
+ _vm->_dataio->closeData(handle);
+ _totFileData = _vm->_dataio->getData(path);
+ } else {
+ _totFileData = 0;
+ }
+}
+
+void Game::loadExtTable(void) {
+ int16 count, i;
+
+ // Function is correct. [sev]
+
+ _extHandle = _vm->_dataio->openData(_curExtFile);
+ if (_extHandle < 0)
+ return;
+
+ _vm->_dataio->readData(_extHandle, (char *)&count, 2);
+ count = FROM_LE_16(count);
+
+ _vm->_dataio->seekData(_extHandle, 0, 0);
+ _extTable = new ExtTable;
+ _extTable->items = 0;
+ if (count)
+ _extTable->items = new ExtItem[count];
+
+ _vm->_dataio->readData(_extHandle, (char *)&_extTable->itemsCount, 2);
+ _extTable->itemsCount = FROM_LE_16(_extTable->itemsCount);
+ _vm->_dataio->readData(_extHandle, (char *)&_extTable->unknown, 1);
+
+ for (i = 0; i < count; i++) {
+ _vm->_dataio->readData(_extHandle, (char *)&_extTable->items[i].offset, 4);
+ _extTable->items[i].offset = FROM_LE_32(_extTable->items[i].offset);
+ _vm->_dataio->readData(_extHandle, (char *)&_extTable->items[i].size, 2);
+ _extTable->items[i].size = FROM_LE_16(_extTable->items[i].size);
+ _vm->_dataio->readData(_extHandle, (char *)&_extTable->items[i].width, 2);
+ _extTable->items[i].width = FROM_LE_16(_extTable->items[i].width);
+ _vm->_dataio->readData(_extHandle, (char *)&_extTable->items[i].height, 2);
+ _extTable->items[i].height = FROM_LE_16(_extTable->items[i].height);
+ }
+}
+
+void Game::loadImFile(void) {
+ char path[20];
+ int16 handle;
+
+ if (_totFileData[0x3d] != 0 && _totFileData[0x3b] == 0)
+ return;
+
+ strcpy(path, "commun.im1");
+ if (_totFileData[0x3b] != 0)
+ path[strlen(path) - 1] = '0' + _totFileData[0x3b];
+
+ handle = _vm->_dataio->openData(path);
+ if (handle < 0)
+ return;
+
+ _vm->_dataio->closeData(handle);
+ _imFileData = _vm->_dataio->getData(path);
+}
+
+void Game::playTot(int16 skipPlay) {
+ char savedTotName[20];
+ int16 *oldCaptureCounter;
+ int16 *oldBreakFrom;
+ int16 *oldNestLevel;
+ int16 _captureCounter;
+ int16 breakFrom;
+ int16 nestLevel;
+ char needTextFree;
+ char needFreeResTable;
+ char *curPtr;
+ int32 variablesCount;
+ char *filePtr;
+ char *savedIP;
+ int16 i;
+
+ oldNestLevel = _vm->_inter->_nestLevel;
+ oldBreakFrom = _vm->_inter->_breakFromLevel;
+ oldCaptureCounter = _vm->_scenery->_pCaptureCounter;
+ savedIP = _vm->_global->_inter_execPtr;
+
+ _vm->_inter->_nestLevel = &nestLevel;
+ _vm->_inter->_breakFromLevel = &breakFrom;
+ _vm->_scenery->_pCaptureCounter = &_captureCounter;
+ strcpy(savedTotName, _curTotFile);
+
+ if (skipPlay == 0) {
+ while (1) {
+ for (i = 0; i < 4; i++) {
+ _vm->_draw->_fontToSprite[i].sprite = -1;
+ _vm->_draw->_fontToSprite[i].base = -1;
+ _vm->_draw->_fontToSprite[i].width = -1;
+ _vm->_draw->_fontToSprite[i].height = -1;
+ }
+
+ if(_vm->_features & GF_MAC)
+ _vm->_music->stopPlay();
+ else
+ _vm->_cdrom->stopPlaying();
+ _vm->_draw->animateCursor(4);
+ _vm->_inter->initControlVars();
+ _vm->_mult->initAll();
+ _vm->_mult->zeroMultData();
+
+ for (i = 0; i < 20; i++)
+ _vm->_draw->_spritesArray[i] = 0;
+
+ _vm->_draw->_spritesArray[20] = _vm->_draw->_frontSurface;
+ _vm->_draw->_spritesArray[21] = _vm->_draw->_backSurface;
+ _vm->_draw->_spritesArray[23] = _vm->_draw->_cursorSprites;
+
+ for (i = 0; i < 20; i++)
+ _soundSamples[i] = 0;
+
+ _totTextData = 0;
+ _totResourceTable = 0;
+ _imFileData = 0;
+ _extTable = 0;
+ _extHandle = -1;
+
+ needFreeResTable = 1;
+ needTextFree = 1;
+
+ _totToLoad[0] = 0;
+
+ if (_curTotFile[0] == 0 && _totFileData == 0)
+ break;
+
+ loadTotFile(_curTotFile);
+ if (_totFileData == 0) {
+ _vm->_draw->blitCursor();
+ break;
+ }
+
+ strcpy(_curImaFile, _curTotFile);
+ strcpy(_curExtFile, _curTotFile);
+
+ _curImaFile[strlen(_curImaFile) - 4] = 0;
+ strcat(_curImaFile, ".ima");
+
+ _curExtFile[strlen(_curExtFile) - 4] = 0;
+ strcat(_curExtFile, ".ext");
+
+ debug(4, "IMA: %s", _curImaFile);
+ debug(4, "EXT: %s", _curExtFile);
+
+ filePtr = (char *)_totFileData + 0x30;
+
+ if (READ_LE_UINT32(filePtr) != (uint32)-1) {
+ curPtr = _totFileData;
+ _totTextData =
+ (TotTextTable *) (curPtr +
+ READ_LE_UINT32((char *)_totFileData + 0x30));
+
+ _totTextData->itemsCount = (int16)READ_LE_UINT16(&_totTextData->itemsCount);
+
+ for (i = 0; i < _totTextData->itemsCount; ++i) {
+ _totTextData->items[i].offset = (int16)READ_LE_UINT16(&_totTextData->items[i].offset);
+ _totTextData->items[i].size = (int16)READ_LE_UINT16(&_totTextData->items[i].size);
+ }
+
+ needTextFree = 0;
+ }
+
+ filePtr = (char *)_totFileData + 0x34;
+ if (READ_LE_UINT32(filePtr) != (uint32)-1) {
+ curPtr = _totFileData;
+
+ _totResourceTable =
+ (TotResTable *)(curPtr +
+ READ_LE_UINT32((char *)_totFileData + 0x34));
+
+ _totResourceTable->itemsCount = (int16)READ_LE_UINT16(&_totResourceTable->itemsCount);
+
+ for (i = 0; i < _totResourceTable->itemsCount; ++i) {
+ _totResourceTable->items[i].offset = (int32)READ_LE_UINT32(&_totResourceTable->items[i].offset);
+ _totResourceTable->items[i].size = (int16)READ_LE_UINT16(&_totResourceTable->items[i].size);
+ _totResourceTable->items[i].width = (int16)READ_LE_UINT16(&_totResourceTable->items[i].width);
+ _totResourceTable->items[i].height = (int16)READ_LE_UINT16(&_totResourceTable->items[i].height);
+ }
+
+ needFreeResTable = 0;
+ }
+
+ loadImFile();
+ loadExtTable();
+
+ _vm->_global->_inter_animDataSize = READ_LE_UINT16((char *)_totFileData + 0x38);
+ if (_vm->_global->_inter_variables == 0) {
+ variablesCount = READ_LE_UINT32((char *)_totFileData + 0x2c);
+ _vm->_global->_inter_variables = new char[variablesCount * 4];
+ for (i = 0; i < variablesCount; i++)
+ WRITE_VAR(i, 0);
+ }
+
+ _vm->_global->_inter_execPtr = (char *)_totFileData;
+ _vm->_global->_inter_execPtr += READ_LE_UINT32((char *)_totFileData + 0x64);
+
+ _vm->_inter->renewTimeInVars();
+
+ WRITE_VAR(13, _vm->_global->_useMouse);
+ WRITE_VAR(14, _vm->_global->_soundFlags);
+ WRITE_VAR(15, _vm->_global->_videoMode);
+ WRITE_VAR(16, _vm->_global->_language);
+
+ _vm->_inter->callSub(2);
+
+ if (_totToLoad[0] != 0)
+ _vm->_inter->_terminate = false;
+
+ variablesCount = READ_LE_UINT32((char *)_totFileData + 0x2c);
+ _vm->_draw->blitInvalidated();
+ delete[] _totFileData;
+ _totFileData = 0;
+
+ if (needTextFree)
+ delete[] _totTextData;
+ _totTextData = 0;
+
+ if (needFreeResTable)
+ delete[] _totResourceTable;
+ _totResourceTable = 0;
+
+ delete[] _imFileData;
+ _imFileData = 0;
+
+ if (_extTable)
+ delete[] _extTable->items;
+ delete _extTable;
+ _extTable = 0;
+
+ if (_extHandle >= 0)
+ _vm->_dataio->closeData(_extHandle);
+
+ _extHandle = -1;
+
+ for (i = 0; i < *_vm->_scenery->_pCaptureCounter; i++)
+ capturePop(0);
+
+ _vm->_mult->checkFreeMult();
+ _vm->_mult->freeAll();
+
+ for (i = 0; i < 20; i++) {
+ if (_vm->_draw->_spritesArray[i] != 0)
+ _vm->_video->freeSurfDesc(_vm->_draw->_spritesArray[i]);
+ _vm->_draw->_spritesArray[i] = 0;
+ }
+ _vm->_snd->stopSound(0);
+
+ for (i = 0; i < 20; i++)
+ freeSoundSlot(i);
+
+ if (_totToLoad[0] == 0)
+ break;
+
+ strcpy(_curTotFile, _totToLoad);
+ }
+ }
+
+ strcpy(_curTotFile, savedTotName);
+
+ _vm->_inter->_nestLevel = oldNestLevel;
+ _vm->_inter->_breakFromLevel = oldBreakFrom;
+ _vm->_scenery->_pCaptureCounter = oldCaptureCounter;
+ _vm->_global->_inter_execPtr = savedIP;
+}
+
+void Game::start(void) {
+ _collisionAreas = new Collision[250];
+ prepareStart();
+ playTot(0);
+
+ delete[] _collisionAreas;
+
+ _vm->_video->freeSurfDesc(_vm->_draw->_cursorSprites);
+ _vm->_video->freeSurfDesc(_vm->_draw->_cursorBack);
+ _vm->_video->freeSurfDesc(_vm->_draw->_backSurface);
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/game.h b/engines/gob/game.h
new file mode 100644
index 0000000000..f1548d4386
--- /dev/null
+++ b/engines/gob/game.h
@@ -0,0 +1,189 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_GAME_H
+#define GOB_GAME_H
+
+#include "gob/sound.h"
+
+namespace Gob {
+
+class Game {
+public:
+
+#pragma START_PACK_STRUCTS
+#define szGame_TotResItem (4 + 2 + 2 + 2)
+ struct Collision {
+ int16 id;
+ int16 left;
+ int16 top;
+ int16 right;
+ int16 bottom;
+ int16 flags;
+ int16 key;
+ int16 funcEnter;
+ int16 funcLeave;
+ } GCC_PACK;
+
+ struct TotResItem {
+ int32 offset; // if > 0, then offset from end of resource table.
+ // If < 0, then -offset-1 is index in .IM file table
+ int16 size;
+ int16 width;
+ int16 height;
+ } GCC_PACK;
+
+#define szGame_TotResTable (2 + 1)
+ struct TotResTable {
+ int16 itemsCount;
+ byte unknown;
+ TotResItem items[1];
+ } GCC_PACK;
+
+#define szGame_ExtItem (4 + 2 + 2 + 2)
+ struct ExtItem {
+ int32 offset; // offset from the table end
+ uint16 size;
+ int16 width; // width&0x7fff - width, width&0x8000 - pack flag
+ int16 height; // not zero
+ } GCC_PACK;
+
+#define szGame_ExtTable (2 + 1)
+ struct ExtTable {
+ int16 itemsCount;
+ byte unknown;
+ ExtItem* items;
+ } GCC_PACK;
+
+#define szGame_TotTextItem (2 + 2)
+ struct TotTextItem {
+ int16 offset;
+ int16 size;
+ } GCC_PACK;
+
+#define szGame_TotTextTable (2)
+ struct TotTextTable {
+ int16 itemsCount;
+ TotTextItem items[1];
+ } GCC_PACK;
+
+ struct InputDesc {
+ int16 fontIndex;
+ int16 backColor;
+ int16 frontColor;
+ char *ptr;
+ } GCC_PACK;
+#pragma END_PACK_STRUCTS
+
+ TotResTable *_totResourceTable;
+ Collision *_collisionAreas;
+ Collision *_collStack[3];
+
+ TotTextTable *_totTextData;
+
+ char _curTotFile[14];
+ char _curExtFile[14];
+
+ char *_imFileData;
+ char *_totFileData;
+
+ int16 _extHandle;
+
+ Snd::SoundDesc *_soundSamples[20];
+
+ char _totToLoad[20];
+
+ int32 _startTimeKey;
+ int16 _mouseButtons;
+
+ Game(GobEngine *vm);
+
+ char *loadExtData(int16 dataId, int16 *pResWidth, int16 *pResHeight);
+ char *loadTotResource(int16 id);
+
+ void capturePush(int16 left, int16 top, int16 width, int16 height);
+
+ void capturePop(char doDraw);
+ void interLoadSound(int16 slot);
+ void freeSoundSlot(int16 slot);
+ int16 checkKeys(int16 *pMousex, int16 *pMouseY, int16 *pButtons,
+ char handleMouse);
+ int16 checkCollisions(char handleMouse, int16 deltaTime, int16 *pResId,
+ int16 *pResIndex);
+ void clearCollisions(void);
+ void addNewCollision(int16 val_0, int16 left, int16 top, int16 right, int16 bottom,
+ int16 flags, int16 key, int16 val_E, int16 val_10);
+ void freeCollision(int16 id);
+
+ void loadSound(int16 slot, char *dataPtr);
+ int16 inputArea(int16 xPos, int16 yPos, int16 width, int16 height, int16 backColor,
+ int16 frontColor, char *str, int16 fontIndex, char inpType, int16 *pTotTime);
+ int16 multiEdit(int16 time, int16 index, int16 *pCurPos,
+ InputDesc * inpDesc);
+ int16 adjustKey(int16 key);
+ void collisionsBlock(void);
+ void prepareStart(void);
+ void loadTotFile(char *path);
+ void loadExtTable(void);
+ void loadImFile(void);
+ void playTot(int16 skipPlay);
+ void start(void);
+
+protected:
+
+ int16 _lastCollKey;
+ int16 _lastCollAreaIndex;
+ int16 _lastCollId;
+
+ int16 _activeCollResId;
+ int16 _activeCollIndex;
+ char _handleMouse;
+ char _forceHandleMouse;
+
+ char _tempStr[256];
+
+ ExtTable *_extTable;
+ char _curImaFile[18];
+
+ int16 _collStackSize;
+ int16 _collStackElemSizes[3];
+
+ char _soundFromExt[20];
+
+ char _shouldPushColls;
+
+ // Capture
+ static Common::Rect _captureStack[20];
+ static int16 _captureCount;
+
+ char _collStr[256];
+
+ GobEngine *_vm;
+
+ void pushCollisions(char all);
+ void popCollisions(void);
+ int16 checkMousePoint(int16 all, int16 *resId, int16 *resIndex);
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/global.cpp b/engines/gob/global.cpp
new file mode 100644
index 0000000000..0d732e1f2a
--- /dev/null
+++ b/engines/gob/global.cpp
@@ -0,0 +1,152 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+
+namespace Gob {
+
+Global::Global(GobEngine *vm) : _vm(vm) {
+ _useMouse = UNDEF;
+ _mousePresent = UNDEF;
+
+ _presentCGA = UNDEF;
+ _presentEGA = UNDEF;
+ _presentVGA = UNDEF;
+ _presentHER = UNDEF;
+
+ _videoMode = 0;
+
+ /* Sound */
+ _presentSound = 0x8000; /* undefined values */
+ _soundFlags = 0x8000;
+ _blasterPort = 0;
+ _disableSoundCfg = 0;
+
+ //char _playingSound = 0;
+
+ /* Mouse */
+ _disableMouseCfg = 0;
+
+ _mouseXShift = 3;
+ _mouseYShift = 3;
+
+ _mouseMaxCol = 320;
+ _mouseMaxRow = 200;
+
+ /* Language */
+ _disableLangCfg = 0x8000;
+ _language = 0x8000;
+
+ /* Timer variables */
+ _startTime = 0;
+ _timer_delta = 1000;
+
+ _frameWaitTime = 0;
+ _startFrameTime = 0;
+
+ /* Timer and delays */
+ _delayTime = 0;
+
+ /* Joystick */
+ _useJoystick = 1;
+
+ /* Data files */
+ _packedSize = 0;
+ int i;
+
+ for (i = 0; i < MAX_DATA_FILES; i++) {
+ _dataFiles[i] = 0;
+ _numDataChunks[i] = 0;
+ _dataFileHandles[i] = -1;
+ }
+
+ _primaryWidth = 0;
+ _primaryHeight = 0;
+
+ _sprAllocated = 0;
+
+ _doRangeClamp = 0;
+
+ _setAllPalette = 0;
+
+ _oldMode = 3;
+ _dontSetPalette = 0;
+ _pPrimarySurfDesc = 0;
+
+ _pPaletteDesc = 0;
+
+ _unusedPalette1[0] = (int16)0;
+ _unusedPalette1[1] = (int16)0x0b;
+ _unusedPalette1[2] = (int16)0;
+ _unusedPalette1[3] = (int16)0x5555;
+ _unusedPalette1[4] = (int16)0xAAAA;
+ _unusedPalette1[5] = (int16)0xFFFF;
+ _unusedPalette1[6] = (int16)0;
+ _unusedPalette1[7] = (int16)0x5555;
+ _unusedPalette1[8] = (int16)0xAAAA;
+ _unusedPalette1[9] = (int16)0xFFFF;
+ _unusedPalette1[10] = (int16)0;
+ _unusedPalette1[11] = (int16)0x5555;
+ _unusedPalette1[12] = (int16)0xAAAA;
+ _unusedPalette1[13] = (int16)0xFFFF;
+ _unusedPalette1[14] = (int16)0;
+ _unusedPalette1[15] = (int16)0x5555;
+ _unusedPalette1[16] = (int16)0xAAAA;
+ _unusedPalette1[17] = (int16)0xFFFF;
+
+ for (i = 0; i < 16 ;i++)
+ _unusedPalette2[i] = i;
+
+ _vgaPalette[0].red = 0x00; _vgaPalette[0].green = 0x00; _vgaPalette[0].blue = 0x00;
+ _vgaPalette[1].red = 0x00; _vgaPalette[1].green = 0x00; _vgaPalette[1].blue = 0x2a;
+ _vgaPalette[2].red = 0x00; _vgaPalette[2].green = 0x2a; _vgaPalette[2].blue = 0x00;
+ _vgaPalette[3].red = 0x00; _vgaPalette[3].green = 0x2a; _vgaPalette[3].blue = 0x2a;
+ _vgaPalette[4].red = 0x2a; _vgaPalette[4].green = 0x00; _vgaPalette[4].blue = 0x00;
+ _vgaPalette[5].red = 0x2a; _vgaPalette[5].green = 0x00; _vgaPalette[5].blue = 0x2a;
+ _vgaPalette[6].red = 0x2a; _vgaPalette[6].green = 0x15; _vgaPalette[6].blue = 0x00;
+ _vgaPalette[7].red = 0x2a; _vgaPalette[7].green = 0x2a; _vgaPalette[7].blue = 0x2a;
+ _vgaPalette[8].red = 0x15; _vgaPalette[8].green = 0x15; _vgaPalette[8].blue = 0x15;
+ _vgaPalette[9].red = 0x15; _vgaPalette[9].green = 0x15; _vgaPalette[9].blue = 0x3f;
+ _vgaPalette[10].red = 0x15; _vgaPalette[10].green = 0x3f; _vgaPalette[10].blue = 0x15;
+ _vgaPalette[11].red = 0x15; _vgaPalette[11].green = 0x3f; _vgaPalette[11].blue = 0x3f;
+ _vgaPalette[12].red = 0x3f; _vgaPalette[12].green = 0x15; _vgaPalette[12].blue = 0x15;
+ _vgaPalette[13].red = 0x3f; _vgaPalette[13].green = 0x15; _vgaPalette[13].blue = 0x3f;
+ _vgaPalette[14].red = 0x3f; _vgaPalette[14].green = 0x3f; _vgaPalette[14].blue = 0x15;
+ _vgaPalette[15].red = 0x3f; _vgaPalette[15].green = 0x3f; _vgaPalette[15].blue = 0x3f;
+
+ _debugFlag = 0;
+ _inVM = 0;
+ _colorCount = 16;
+
+ _inter_resStr[0] = 0;
+ _inter_resVal = 0;
+
+ _inter_variables = 0;
+ _inter_execPtr = 0;
+ _inter_animDataSize = 10;
+
+ _inter_mouseX = 0;
+ _inter_mouseY = 0;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/global.h b/engines/gob/global.h
new file mode 100644
index 0000000000..1583e711c8
--- /dev/null
+++ b/engines/gob/global.h
@@ -0,0 +1,173 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_GLOBAL_H
+#define GOB_GLOBAL_H
+
+#include "gob/dataio.h"
+#include "gob/video.h"
+
+#include "common/file.h"
+
+namespace Gob {
+
+#define VIDMODE_CGA 0x05
+#define VIDMODE_EGA 0x0d
+#define VIDMODE_VGA 0x13
+#define VIDMODE_HER 7
+
+#define PROAUDIO_FLAG 0x10
+#define ADLIB_FLAG 0x08
+#define BLASTER_FLAG 0x04
+#define INTERSOUND_FLAG 0x02
+#define SPEAKER_FLAG 0x01
+#define MIDI_FLAG 0x4000
+
+#define NO 0
+#define YES 1
+#define UNDEF 2
+
+#define F1_KEY 0x3b00
+#define F2_KEY 0x3c00
+#define F3_KEY 0x3d00
+#define F4_KEY 0x3e00
+#define F5_KEY 0x3f00
+#define F6_KEY 0x4000
+#define ESCAPE 0x001b
+#define ENTER 0x000d
+
+#define MAX_FILES 30
+
+/* Video drivers */
+#define UNK_DRIVER 0
+#define VGA_DRIVER 1
+#define EGA_DRIVER 2
+#define CGA_DRIVER 3
+#define HER_DRIVER 4
+
+class Global {
+public:
+ char _pressedKeys[128];
+
+ char _useMouse;
+ int16 _mousePresent;
+
+ int16 _presentCGA;
+ int16 _presentEGA;
+ int16 _presentVGA;
+ int16 _presentHER;
+
+ int16 _videoMode;
+
+ int16 _disableVideoCfg;
+
+ uint16 _presentSound;
+ uint16 _soundFlags;
+ int16 _disableSoundCfg;
+ int16 _blasterPort;
+
+ uint16 _disableLangCfg;
+ uint16 _language;
+
+ // Timer variables
+ int32 _startTime;
+ int16 _timer_delta;
+
+ int16 _frameWaitTime;
+ int32 _startFrameTime;
+
+ // Mouse
+ int16 _disableMouseCfg;
+
+ int16 _mouseXShift;
+ int16 _mouseYShift;
+ int16 _mouseMaxCol;
+ int16 _mouseMaxRow;
+
+ // Timer and delays
+ int16 _delayTime;
+
+ // Joystick
+ char _useJoystick;
+
+ // Files
+ Common::File _filesHandles[MAX_FILES];
+
+ // Data files
+ struct DataIO::ChunkDesc *_dataFiles[MAX_DATA_FILES];
+ int16 _numDataChunks[MAX_DATA_FILES];
+ int16 _dataFileHandles[MAX_DATA_FILES];
+ int32 _chunkPos[MAX_SLOT_COUNT * MAX_DATA_FILES];
+ int32 _chunkOffset[MAX_SLOT_COUNT * MAX_DATA_FILES];
+ int32 _chunkSize[MAX_SLOT_COUNT * MAX_DATA_FILES];
+ char _isCurrentSlot[MAX_SLOT_COUNT * MAX_DATA_FILES];
+ int32 _packedSize;
+
+ int16 _sprAllocated;
+
+ int16 _primaryWidth;
+ int16 _primaryHeight;
+
+ int16 _doRangeClamp;
+
+ char _redPalette[256];
+ char _greenPalette[256];
+ char _bluePalette[256];
+
+ int16 _setAllPalette;
+
+ Video::SurfaceDesc _primarySurfDesc;
+ Video::SurfaceDesc *_pPrimarySurfDesc;
+
+ int16 _oldMode;
+ char _dontSetPalette;
+
+ Video::PalDesc *_pPaletteDesc;
+
+ int16 _unusedPalette1[18];
+ int16 _unusedPalette2[16];
+ Video::Color _vgaPalette[16];
+ Video::PalDesc _paletteStruct;
+
+ int16 _debugFlag;
+ int16 _inVM;
+ int16 _colorCount;
+
+ char _inter_resStr[200];
+ int32 _inter_resVal;
+
+ char *_inter_variables;
+ char *_inter_execPtr;
+ int16 _inter_animDataSize;
+
+ int16 _inter_mouseX;
+ int16 _inter_mouseY;
+
+ Global(GobEngine *vm);
+
+protected:
+ GobEngine *_vm;
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp
new file mode 100644
index 0000000000..a3285b219f
--- /dev/null
+++ b/engines/gob/gob.cpp
@@ -0,0 +1,366 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "common/stdafx.h"
+
+#include "base/plugins.h"
+#include "backends/fs/fs.h"
+#include "common/md5.h"
+
+#include "gob/gob.h"
+
+#include "gob/global.h"
+#include "gob/game.h"
+#include "gob/sound.h"
+#include "gob/init.h"
+#include "gob/inter.h"
+#include "gob/draw.h"
+#include "gob/anim.h"
+#include "gob/cdrom.h"
+#include "gob/goblin.h"
+#include "gob/map.h"
+#include "gob/mult.h"
+#include "gob/pack.h"
+#include "gob/palanim.h"
+#include "gob/parse.h"
+#include "gob/scenery.h"
+#include "gob/timer.h"
+#include "gob/util.h"
+#include "gob/music.h"
+
+enum {
+ // We only compute MD5 of the first megabyte of our data files.
+ kMD5FileSizeLimit = 1024 * 1024
+};
+
+static const Gob::GobGameSettings gob_games[] = {
+ // Supplied by Florian Zeitz on scummvm-devel
+ {"gob1", "Gobliiins (DOS EGA)", Gob::GF_GOB1, "82aea70ef26f41fa963dfae270993e49"},
+ {"gob1", "Gobliiins (DOS EGA)", Gob::GF_GOB1, "1f499458837008058b8ba6ae07057214"},
+ {"gob1", "Gobliiins (Windows)", Gob::GF_GOB1, "8a5e850c49d7cacdba5f5eb1fcc77b89"},
+
+ // Supplied by Theruler76 in bug report #1201233
+ {"gob1", "Gobliiins (DOS VGA)", Gob::GF_GOB1, "a5e232fcd02733c7dffff107d22d36eb"},
+
+ // CD 1.000 version. Multilingual
+ {"gob1", "Gobliiins (CD)", Gob::GF_GOB1 | Gob::GF_CD, "037db48ebce94bdfe42e2c9510da9211"},
+ // CD 1.02 version. Multilingual
+ {"gob1", "Gobliiins (CD)", Gob::GF_GOB1 | Gob::GF_CD, "45f9c1162dd7040fd05fd013ccc176e2"},
+
+ {"gob1", "Gobliiins (Amiga)", Gob::GF_GOB1, "d9f8736b7dc0ea891cd06592a72e8a72"},
+ {"gob1", "Gobliiins (Amiga)", Gob::GF_GOB1, "69f9ae85252271e7dfa62883e581e5e9"},
+ {"gob1", "Gobliiins (Amiga)", Gob::GF_GOB1, "26de406cb09228d902274446a6a2eceb"},
+ {"gob1", "Gobliiins (Amiga)", Gob::GF_GOB1, "baf88a95928edb3f51067983f2dffa93"},
+
+ {"gob1", "Gobliiins (Interactive Demo)", Gob::GF_GOB1, "4f5bf4b9e4c39ebb93579747fc678e97"},
+
+ {"gob1", "Gobliiins (Mac)", Gob::GF_GOB1 | Gob::GF_MAC, "4c0e8ce4a2f66ee8226952ad3c6c1155"},
+
+#if 0
+ {"gob2", "Gobliins 2 (DOS)", Gob::GF_GOB2, "abb5f762f9979d4253002de20f6e7b56"},
+ {"gob2", "Gobliins 2 (DOS)", Gob::GF_GOB2, "9b6de65d811c08eebf50391b84fcba92"},
+ {"gob2", "Gobliins 2 (DOS)", Gob::GF_GOB2, "54d59c200e3823ad0af11a605a6fd06a"},
+ {"gob2", "Gobliins 2 (DOS Ru)", Gob::GF_GOB2, "b6d47494bf88398ae59c1b5656cafce4"},
+ // CD 1.000.
+ {"gob2", "Gobliins 2 (CD)", Gob::GF_GOB2, "02bf538fd8003b8da23a3546299c3df4"},
+ // CD 1.01
+ {"gob2", "Gobliins 2 (CD)", Gob::GF_GOB2, "410e632682ab11969bc3b3b588066d95"},
+ {"gob2", "Gobliins 2 (Demo)", Gob::GF_GOB2, "be8b111191f965ac9b28fe530580d14e"},
+
+ {"gob3", "Goblins Quest 3", Gob::GF_GOB3, "36d9b4032b39a794c8640e500e98893a"},
+ {"gob3", "Goblins Quest 3", Gob::GF_GOB3, "d129f639f6ca8d6b5f0f4e15edb91058"},
+ {"gob3", "Goblins Quest 3", Gob::GF_GOB3, "8d17b0abc514b1512fdedc6072acd48b"},
+ // CD 1.000
+ {"gob3", "Goblins Quest 3 (CD)", Gob::GF_GOB3, "8d17b0abc514b1512fdedc6072acd48b"},
+ // CD 1.02. Spanish "Computer Gaming World"* distribution in Spain
+ {"gob3", "Goblins Quest 3 (CD)", Gob::GF_GOB3, "7d7ab9a987be7208b9b685846fbd3e82"},
+
+ {"gob3", "Goblins Quest 3 (Interactive Demo)", Gob::GF_GOB3, "4986b44cec309589508d7904f924c217"},
+ {"gob3", "Goblins Quest 3 (Demo)", Gob::GF_GOB3, "5024e7de8d6377fbbeabbaa92e0452bc"},
+ {"gob3", "Goblins Quest 3 (Interactive Demo)", Gob::GF_GOB3, "59ab69dab5fddbbf698c77a84297a5a2"},
+
+ // CD 1.0
+ {"woodruff", "The Bizarre Adventures of Woodruff and the Schnibble", Gob::GF_WOODRUFF, "c27402cee260d2ff1c4cecb2006a630a"},
+
+ // CD 1.00, German release (INTRO.STRK seems to be multilingual, though?)
+ {"woodruff", "The Bizarre Adventures of Woodruff and the Schnibble", Gob::GF_WOODRUFF, "751ba028d215e0db1e0254853de6a7e4"},
+#endif
+ {0, 0, 0, 0}
+};
+
+// Keep list of different supported games
+static const struct GobGameList {
+ const char *gameid;
+ const char *description;
+ uint32 features;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { gameid, description, features };
+ return dummy;
+ }
+} gob_list[] = {
+ {"gob1", "Gobliiins", Gob::GF_GOB1},
+ {"gob2", "Gobliins 2", Gob::GF_GOB2},
+ {0, 0, 0}
+};
+
+
+GameList Engine_GOB_gameList() {
+ GameList games;
+ const GobGameList *g = gob_list;
+
+ while (g->gameid) {
+ games.push_back(g->toGameSettings());
+ g++;
+ }
+
+ return games;
+}
+
+DetectedGameList Engine_GOB_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+ const Gob::GobGameSettings *g;
+ FSList::const_iterator file;
+
+ // Iterate over all files in the given directory
+ for (file = fslist.begin(); file != fslist.end(); file++) {
+ if (file->isDirectory())
+ continue;
+
+ // All the supported games have an intro.stk file.
+ if (scumm_stricmp(file->displayName().c_str(), "intro.stk") == 0)
+ break;
+ }
+
+ if (file == fslist.end())
+ return detectedGames;
+
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+
+ if (Common::md5_file(file->path().c_str(), md5sum, NULL, kMD5FileSizeLimit)) {
+ for (int i = 0; i < 16; i++) {
+ sprintf(md5str + i * 2, "%02x", (int)md5sum[i]);
+ }
+ for (g = gob_games; g->gameid; g++) {
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ detectedGames.push_back(g->toGameSettings());
+ }
+ }
+ if (detectedGames.isEmpty()) {
+ printf("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team\n", md5str);
+
+ const GobGameList *g1 = gob_list;
+ while (g1->gameid) {
+ detectedGames.push_back(g1->toGameSettings());
+ g1++;
+ }
+ }
+ }
+ return detectedGames;
+}
+
+Engine *Engine_GOB_create(GameDetector * detector, OSystem *syst) {
+ // Detect game features based on MD5
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+
+ if (Common::md5_file("intro.stk", md5sum, ConfMan.get("path").c_str(), kMD5FileSizeLimit)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+ } else {
+ error("Engine_GOB_create(): Cannot find intro.stk");
+ }
+
+ const Gob::GobGameSettings *g;
+ bool found = false;
+
+ // TODO
+ // Fallback. Maybe we will be able to determine game type from game
+ // data contents
+ Common::String realGame;
+ uint32 features;
+ if (ConfMan.hasKey("gameid"))
+ realGame = ConfMan.get("gameid");
+ else
+ realGame = detector->_targetName;
+ if (!strcmp(realGame.c_str(), "gob2"))
+ features = Gob::GF_GOB2;
+ else
+ features = Gob::GF_GOB1;
+
+ for (g = gob_games; g->gameid; g++) {
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ features = g->features;
+
+ if (g->description)
+ g_system->setWindowCaption(g->description);
+
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ printf("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team\n", md5str);
+ }
+
+ return new Gob::GobEngine(detector, syst, features);
+}
+
+REGISTER_PLUGIN(GOB, "Gob Engine")
+
+namespace Gob {
+
+#define MAX_TIME_DELTA 100
+
+GobEngine::GobEngine(GameDetector *detector, OSystem * syst, uint32 features)
+ : Engine(syst) {
+ // Setup mixer
+ if (!_mixer->isReady()) {
+ warning("Sound initialization failed.");
+ }
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+
+ _features = features;
+ _copyProtection = ConfMan.getBool("copy_protection");
+}
+
+GobEngine::~GobEngine() {
+ delete _game;
+ delete _snd;
+ delete _video;
+ delete _global;
+ delete _draw;
+ delete _anim;
+ delete _cdrom;
+ delete _dataio;
+ delete _goblin;
+ delete _init;
+ delete _inter;
+ delete _map;
+ delete _mult;
+ delete _pack;
+ delete _palanim;
+ delete _parse;
+ delete _scenery;
+ delete _gtimer;
+ delete _util;
+ delete _inter;
+ delete _music;
+}
+
+void GobEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+}
+
+int GobEngine::go() {
+ _init->initGame(0);
+
+ return 0;
+}
+
+void GobEngine::shutdown() {
+ _system->quit();
+}
+
+int GobEngine::init(GameDetector &detector) {
+ _game = new Game(this);
+ _snd = new Snd(this);
+ _video = new Video(this);
+ _global = new Global(this);
+ _draw = new Draw(this);
+ _anim = new Anim();
+ _cdrom = new CDROM(this);
+ _dataio = new DataIO(this);
+ _goblin = new Goblin(this);
+ _init = new Init(this);
+ _map = new Map(this);
+ _mult = new Mult(this);
+ _pack = new Pack();
+ _palanim = new PalAnim(this);
+ _scenery = new Scenery(this);
+ _gtimer = new GTimer();
+ _util = new Util(this);
+ if (_features & Gob::GF_GOB1) {
+ _inter = new Inter_v1(this);
+ _parse = new Parse_v1(this);
+ }
+ else if (_features & Gob::GF_GOB2) {
+ _inter = new Inter_v2(this);
+ _parse = new Parse_v2(this);
+ }
+ else
+ error("GobEngine::init(): Unknown version of game engine");
+ if ((_features & Gob::GF_MAC) || (_features & Gob::GF_GOB1))
+ _music = new Music(this);
+
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ _system->initSize(320, 200);
+ _system->endGFXTransaction();
+
+ // On some systems it's not safe to run CD audio games from the CD.
+ if (_features & GF_CD)
+ checkCD();
+
+ int cd_num = ConfMan.getInt("cdrom");
+ if (cd_num >= 0)
+ _system->openCD(cd_num);
+
+ _global->_debugFlag = 1;
+ _global->_doRangeClamp = 1;
+
+ _global->_videoMode = 0x13;
+ _global->_useMouse = 1;
+ _global->_soundFlags = 0;
+
+ switch (Common::parseLanguage(ConfMan.get("language"))) {
+ case Common::FR_FRA:
+ _global->_language = 0;
+ break;
+ case Common::DE_DEU:
+ _global->_language = 1;
+ break;
+ case Common::ES_ESP:
+ _global->_language = 3;
+ break;
+ case Common::IT_ITA:
+ _global->_language = 4;
+ break;
+ default:
+ // Default to English
+ _global->_language = 2;
+ break;
+ }
+
+ // FIXME: This is the ugly way of reducing redraw overhead. It works
+ // well for 320x200 but it's unclear how well it will work for
+ // 640x480.
+
+ g_system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, true);
+
+ return 0;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/gob.h b/engines/gob/gob.h
new file mode 100644
index 0000000000..211ec124ad
--- /dev/null
+++ b/engines/gob/gob.h
@@ -0,0 +1,129 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GOB_GOB_H
+#define GOB_GOB_H
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "sound/mixer.h"
+#include "common/config-manager.h"
+
+#include "base/engine.h"
+#include "base/gameDetector.h"
+
+#include "gob/dataio.h"
+#include "gob/video.h"
+#include "common/file.h"
+
+namespace Gob {
+
+class Game;
+class Snd;
+class Video;
+class Global;
+class Draw;
+class Anim;
+class CDROM;
+class DataIO;
+class Goblin;
+class Init;
+class Inter;
+class Map;
+class Mult;
+class Pack;
+class PalAnim;
+class Parse;
+class Scenery;
+class GTimer;
+class Util;
+class Music;
+
+#define VAR_OFFSET(offs) (*(uint32 *)(_vm->_global->_inter_variables + (offs)))
+#define VAR(var) VAR_OFFSET((var) << 2)
+#define VAR_ADDRESS(var) (&VAR(var))
+
+#define WRITE_VAR_OFFSET(offs, val) (VAR_OFFSET(offs) = (val))
+#define WRITE_VAR(var, val) WRITE_VAR_OFFSET((var) << 2, (val))
+
+enum {
+ GF_GOB1 = 1 << 0,
+ GF_GOB2 = 1 << 1,
+ GF_GOB3 = 1 << 2,
+ GF_WOODRUFF = 1 << 3,
+ GF_CD = 1 << 4,
+ GF_MAC = 1 << 5
+};
+
+struct GobGameSettings {
+ const char *gameid;
+ const char *description;
+ uint32 features;
+ const char *md5sum;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { gameid, description, features };
+ return dummy;
+ }
+};
+
+class GobEngine : public Engine {
+ void errorString(const char *buf_input, char *buf_output);
+
+protected:
+ int go();
+ int init(GameDetector &detector);
+
+public:
+ GobEngine(GameDetector * detector, OSystem * syst, uint32 features);
+ virtual ~GobEngine();
+
+ void shutdown();
+
+ Common::RandomSource _rnd;
+
+ int32 _features;
+ bool _copyProtection;
+
+ Game *_game;
+ Snd *_snd;
+ Video *_video;
+ Global *_global;
+ Draw *_draw;
+ Anim *_anim;
+ CDROM *_cdrom;
+ DataIO *_dataio;
+ Goblin *_goblin;
+ Init *_init;
+ Map *_map;
+ Mult *_mult;
+ Pack *_pack;
+ PalAnim *_palanim;
+ Parse *_parse;
+ Scenery *_scenery;
+ GTimer *_gtimer;
+ Util *_util;
+ Inter *_inter;
+ Music *_music;
+};
+
+} // End of namespace Gob
+#endif
diff --git a/engines/gob/goblin.cpp b/engines/gob/goblin.cpp
new file mode 100644
index 0000000000..f434c65a70
--- /dev/null
+++ b/engines/gob/goblin.cpp
@@ -0,0 +1,2381 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/goblin.h"
+#include "gob/inter.h"
+#include "gob/global.h"
+#include "gob/draw.h"
+#include "gob/video.h"
+#include "gob/anim.h"
+#include "gob/scenery.h"
+#include "gob/map.h"
+#include "gob/sound.h"
+#include "gob/game.h"
+#include "gob/dataio.h"
+#include "gob/cdrom.h"
+#include "gob/music.h"
+
+namespace Gob {
+
+Goblin::Goblin(GobEngine *vm) : _vm(vm) {
+ _goesAtTarget = 0;
+ _readyToAct = 0;
+ _gobAction = 0;
+ _itemIndInPocket = 5;
+ _itemIdInPocket = 2;
+ _itemByteFlag = 0;
+ _destItemId = -1;
+ _destActionItem = 0;
+ _actDestItemDesc = 0;
+ _forceNextState[0] = -1;
+ _forceNextState[1] = -1;
+ _forceNextState[2] = -1;
+ _forceNextState[3] = -1;
+ _forceNextState[4] = -1;
+ _forceNextState[5] = -1;
+ _forceNextState[6] = -1;
+ _forceNextState[7] = 0;
+ _forceNextState[8] = 0;
+ _forceNextState[9] = 0;
+
+ _rotStates[0][0] = 0; _rotStates[0][1] = 22; _rotStates[0][2] = 23; _rotStates[0][3] = 24;
+ _rotStates[1][0] = 13; _rotStates[1][1] = 2; _rotStates[1][2] = 12; _rotStates[1][3] = 14;
+ _rotStates[2][0] = 16; _rotStates[2][1] = 15; _rotStates[2][2] = 4; _rotStates[2][3] = 17;
+ _rotStates[3][0] = 27; _rotStates[3][1] = 25; _rotStates[3][2] = 26; _rotStates[3][3] = 6;
+
+ _boreCounter = 0;
+ _positionedGob = 5;
+
+ _noPick = 0;
+ _objList = 0;
+ int i;
+ for (i = 0; i < 4; i++)
+ _goblins[i] = 0;
+ _currentGoblin = 0;
+ for (i = 0; i < 16; i++)
+ _soundData[i] = 0;
+ for (i = 0; i < 3; i++) {
+ _gobPositions[i].x = 0;
+ _gobPositions[i].y = 0;
+ }
+ _gobDestX = 0;
+ _gobDestY = 0;
+ _pressedMapX = 0;
+ _pressedMapY = 0;
+ _pathExistence = 0;
+
+ _some0ValPtr = 0;
+
+ _gobRetVarPtr = 0;
+ _curGobVarPtr = 0;
+ _curGobXPosVarPtr = 0;
+ _curGobYPosVarPtr = 0;
+ _itemInPocketVarPtr = 0;
+
+ _curGobStateVarPtr = 0;
+ _curGobFrameVarPtr = 0;
+ _curGobMultStateVarPtr = 0;
+ _curGobNextStateVarPtr = 0;
+ _curGobScrXVarPtr = 0;
+ _curGobScrYVarPtr = 0;
+ _curGobLeftVarPtr = 0;
+ _curGobTopVarPtr = 0;
+ _curGobRightVarPtr = 0;
+ _curGobBottomVarPtr = 0;
+ _curGobDoAnimVarPtr = 0;
+ _curGobOrderVarPtr = 0;
+ _curGobNoTickVarPtr = 0;
+ _curGobTypeVarPtr = 0;
+ _curGobMaxTickVarPtr = 0;
+ _curGobTickVarPtr = 0;
+ _curGobActStartStateVarPtr = 0;
+ _curGobLookDirVarPtr = 0;
+ _curGobPickableVarPtr = 0;
+ _curGobRelaxVarPtr = 0;
+ _curGobMaxFrameVarPtr = 0;
+
+ _destItemStateVarPtr = 0;
+ _destItemFrameVarPtr = 0;
+ _destItemMultStateVarPtr = 0;
+ _destItemNextStateVarPtr = 0;
+ _destItemScrXVarPtr = 0;
+ _destItemScrYVarPtr = 0;
+ _destItemLeftVarPtr = 0;
+ _destItemTopVarPtr = 0;
+ _destItemRightVarPtr = 0;
+ _destItemBottomVarPtr = 0;
+ _destItemDoAnimVarPtr = 0;
+ _destItemOrderVarPtr = 0;
+ _destItemNoTickVarPtr = 0;
+ _destItemTypeVarPtr = 0;
+ _destItemMaxTickVarPtr = 0;
+ _destItemTickVarPtr = 0;
+ _destItemActStartStVarPtr = 0;
+ _destItemLookDirVarPtr = 0;
+ _destItemPickableVarPtr = 0;
+ _destItemRelaxVarPtr = 0;
+ _destItemMaxFrameVarPtr = 0;
+
+ _destItemType = 0;
+ _destItemState = 0;
+ for (i = 0; i < 20; i++) {
+ _itemToObject[i] = 0;
+ _objects[i] = 0;
+ }
+ _objCount = 0;
+ _gobsCount = 0;
+}
+
+char Goblin::rotateState(int16 from, int16 to) {
+ return _rotStates[from / 2][to / 2];
+}
+
+int16 Goblin::peekGoblin(Gob_Object *_curGob) {
+ Util::ListNode *ptr;
+ Gob_Object *desc;
+ int16 index;
+ int16 i;
+
+ ptr = _objList->pHead;
+ index = 0;
+ while (ptr != 0) {
+ desc = (Gob_Object *) ptr->pData;
+ if (desc != _curGob) {
+ for (i = 0; i < 3; i++) {
+ if (desc != _goblins[i])
+ continue;
+
+ if (_vm->_global->_inter_mouseX < desc->right &&
+ _vm->_global->_inter_mouseX > desc->left &&
+ _vm->_global->_inter_mouseY < desc->bottom &&
+ _vm->_global->_inter_mouseY > desc->top) {
+ index = i + 1;
+ }
+ }
+ }
+ ptr = ptr->pNext;
+ }
+ return index;
+}
+
+void Goblin::initList(void) {
+ _objList = new Util::List;
+ _objList->pHead = 0;
+ _objList->pTail = 0;
+}
+
+void Goblin::sortByOrder(Util::List *list) {
+ Util::ListNode *ptr;
+ Util::ListNode *ptr2;
+
+ ptr = list->pHead;
+ while (ptr->pNext != 0) {
+ for (ptr2 = ptr->pNext; ptr2 != 0; ptr2 = ptr2->pNext) {
+ Gob_Object *objDesc = (Gob_Object *)ptr->pData;
+ Gob_Object *objDesc2 = (Gob_Object *)ptr2->pData;
+
+ if (objDesc->order <= objDesc2->order) {
+ if (objDesc->order != objDesc2->order)
+ continue;
+
+ if (objDesc->bottom <= objDesc2->bottom) {
+ if (objDesc->bottom != objDesc2->bottom)
+ continue;
+
+ if (objDesc != _goblins[_currentGoblin])
+ continue;
+ }
+ }
+
+ SWAP(ptr->pData, ptr2->pData);
+ }
+ ptr = ptr->pNext;
+ }
+}
+
+void Goblin::playSound(Snd::SoundDesc *snd, int16 repCount, int16 freq) {
+ if (snd != 0) {
+ _vm->_snd->stopSound(0);
+ _vm->_snd->playSample(snd, repCount, freq);
+ }
+}
+
+void Goblin::drawObjects(void) {
+ Util::ListNode *ptr;
+ Util::ListNode *ptr2;
+
+ Gob_Object *objDesc;
+ Gob_Object *gobDesc2;
+ int16 layer;
+
+ ptr = _objList->pHead;
+ for (ptr = _objList->pHead; ptr != 0; ptr = ptr->pNext) {
+ objDesc = (Gob_Object *) ptr->pData;
+
+ if (objDesc->type == 3)
+ objDesc->toRedraw = 1;
+ else if (objDesc->type == 1)
+ objDesc->toRedraw = 0;
+ }
+
+ for (ptr = _objList->pHead; ptr != 0; ptr = ptr->pNext) {
+ objDesc = (Gob_Object *) ptr->pData;
+ if (objDesc->toRedraw == 0)
+ continue;
+
+ _vm->_video->drawSprite(_vm->_anim->_animSurf, _vm->_draw->_backSurface,
+ objDesc->left, objDesc->top, objDesc->right,
+ objDesc->bottom, objDesc->left, objDesc->top, 0);
+
+ _vm->_draw->invalidateRect(objDesc->left, objDesc->top,
+ objDesc->right, objDesc->bottom);
+
+ if (objDesc->type != 0)
+ continue;
+
+ layer =
+ objDesc->stateMach[objDesc->state][objDesc->stateColumn]->
+ layer;
+ _vm->_scenery->updateAnim(layer, objDesc->curFrame, objDesc->animation,
+ 0, objDesc->xPos, objDesc->yPos, 0);
+
+ if (_vm->_scenery->_toRedrawLeft == -12345) {
+ objDesc->dirtyLeft = objDesc->left;
+ objDesc->dirtyRight = objDesc->right;
+ objDesc->dirtyTop = objDesc->top;
+ objDesc->dirtyBottom = objDesc->bottom;
+ } else {
+ objDesc->dirtyLeft =
+ MIN(objDesc->left, _vm->_scenery->_toRedrawLeft);
+ objDesc->dirtyRight =
+ MAX(objDesc->right, _vm->_scenery->_toRedrawRight);
+ objDesc->dirtyTop =
+ MIN(objDesc->top, _vm->_scenery->_toRedrawTop);
+ objDesc->dirtyBottom =
+ MAX(objDesc->bottom, _vm->_scenery->_toRedrawBottom);
+ }
+
+ objDesc->dirtyLeft = 0;
+ objDesc->dirtyRight = 319;
+ objDesc->dirtyTop = 0;
+ objDesc->dirtyBottom = 199;
+ }
+
+ sortByOrder(_objList);
+ for (ptr = _objList->pHead; ptr != 0; ptr = ptr->pNext) {
+ objDesc = (Gob_Object *) ptr->pData;
+ if (objDesc->toRedraw) {
+ layer =
+ objDesc->stateMach[objDesc->state][objDesc->
+ stateColumn]->layer;
+
+ if (objDesc->type == 0) {
+ if (objDesc->visible == 0) {
+ _vm->_scenery->updateAnim(layer,
+ objDesc->curFrame,
+ objDesc->animation, 0,
+ objDesc->xPos, objDesc->yPos, 0);
+
+ } else {
+ _vm->_scenery->updateAnim(layer,
+ objDesc->curFrame,
+ objDesc->animation, 2,
+ objDesc->xPos, objDesc->yPos, 1);
+ }
+ if (_vm->_scenery->_toRedrawLeft == -12345) {
+ objDesc->left = 0;
+ objDesc->top = 0;
+ objDesc->right = 0;
+ objDesc->bottom = 0;
+ } else {
+ _vm->_draw->invalidateRect(_vm->_scenery->_toRedrawLeft,
+ _vm->_scenery->_toRedrawTop,
+ _vm->_scenery->_toRedrawRight,
+ _vm->_scenery->_toRedrawBottom);
+
+ objDesc->left = _vm->_scenery->_toRedrawLeft;
+ objDesc->top = _vm->_scenery->_toRedrawTop;
+ objDesc->right = _vm->_scenery->_toRedrawRight;
+ objDesc->bottom = _vm->_scenery->_toRedrawBottom;
+ _vm->_scenery->updateStatic(objDesc->order);
+ }
+ } else {
+ objDesc->left = 0;
+ objDesc->top = 0;
+ objDesc->right = 0;
+ objDesc->bottom = 0;
+ objDesc->type = 1;
+ }
+ continue;
+ }
+
+ if (objDesc->type == 0 && objDesc->visible != 0) {
+ for (ptr2 = _objList->pHead; ptr2 != 0;
+ ptr2 = ptr2->pNext) {
+ gobDesc2 = (Gob_Object *) ptr2->pData;
+
+ if (gobDesc2->toRedraw == 0)
+ continue;
+
+ if (objDesc->right < gobDesc2->dirtyLeft)
+ continue;
+
+ if (gobDesc2->dirtyRight < objDesc->left)
+ continue;
+
+ if (objDesc->bottom < gobDesc2->dirtyTop)
+ continue;
+
+ if (gobDesc2->dirtyBottom < objDesc->top)
+ continue;
+
+ _vm->_scenery->_toRedrawLeft = gobDesc2->dirtyLeft;
+ _vm->_scenery->_toRedrawRight = gobDesc2->dirtyRight;
+ _vm->_scenery->_toRedrawTop = gobDesc2->dirtyTop;
+ _vm->_scenery->_toRedrawBottom = gobDesc2->dirtyBottom;
+
+ layer =
+ objDesc->stateMach[objDesc->
+ state][objDesc->stateColumn]->layer;
+
+ _vm->_scenery->updateAnim(layer, objDesc->curFrame,
+ objDesc->animation, 4, objDesc->xPos,
+ objDesc->yPos, 1);
+
+ _vm->_scenery->updateStatic(objDesc->order);
+ }
+ }
+ }
+
+ for (ptr = _objList->pHead; ptr != 0; ptr = ptr->pNext) {
+ objDesc = (Gob_Object *) ptr->pData;
+ if (objDesc->toRedraw == 0 || objDesc->type == 1)
+ continue;
+
+ Gob_State *state = objDesc->stateMach[objDesc->state][objDesc->stateColumn];
+ int16 sndFrame;
+ int16 sndItem;
+ int16 freq;
+ int16 repCount;
+
+ if (state->sndFrame & 0xff00) {
+ // There are two frames which trigger a sound effect,
+ // so everything has to be encoded in one byte each.
+ // Note that the frequency is multiplied by 100, not -
+ // as I would have thought, 0x100.
+
+ sndFrame = (state->sndFrame >> 8) & 0xff;
+ sndItem = (state->sndItem >> 8) & 0xff;
+ freq = 100 * ((state->freq >> 8) & 0xff);
+ repCount = (state->repCount >> 8) & 0xff;
+
+ if (objDesc->curFrame == sndFrame) {
+ if (sndItem != 0xff) {
+ playSound(_soundData[sndItem],
+ repCount, freq);
+ }
+ }
+
+ sndFrame = state->sndFrame & 0xff;
+ sndItem = state->sndItem & 0xff;
+ freq = 100 * (state->freq & 0xff);
+ repCount = state->repCount & 0xff;
+
+ if (objDesc->curFrame == sndFrame) {
+ if (sndItem != 0xff) {
+ playSound(_soundData[sndItem],
+ repCount, freq);
+ }
+ }
+ } else {
+ // There is only one, so frequency etc. are used as is.
+ sndFrame = state->sndFrame;
+ sndItem = state->sndItem;
+ freq = state->freq;
+ repCount = state->repCount;
+
+ if (objDesc->curFrame == sndFrame) {
+ if (sndItem != -1) {
+ playSound(_soundData[sndItem],
+ repCount, freq);
+ }
+ }
+ }
+ }
+
+// _vm->_scenery->updateAnim(27, 0, 9, 2, 10, 10, 1);
+}
+
+void Goblin::animateObjects(void) {
+ Util::ListNode *node;
+ Gob_Object *objDesc;
+ Scenery::AnimLayer *pLayer;
+ int16 layer;
+
+ for (node = _objList->pHead; node != 0; node = node->pNext) {
+ objDesc = (Gob_Object *) node->pData;
+ if (objDesc->doAnim != 1 || objDesc->type != 0)
+ continue;
+
+ if (objDesc->noTick != 0)
+ continue;
+
+ if (objDesc->tick < objDesc->maxTick)
+ objDesc->tick++;
+
+ if (objDesc->tick >= objDesc->maxTick) {
+ objDesc->tick = 1;
+ objDesc->curFrame++;
+
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+ pLayer =
+ _vm->_scenery->_animations[objDesc->animation].layers[layer];
+
+ if (objDesc->curFrame < pLayer->framesCount)
+ continue;
+
+ objDesc->curFrame = 0;
+
+ objDesc->xPos += pLayer->animDeltaX;
+ objDesc->yPos += pLayer->animDeltaY;
+
+ if (objDesc->nextState == -1
+ && objDesc->multState == -1
+ && objDesc->unk14 == 0) {
+ objDesc->toRedraw = 0;
+ objDesc->curFrame = pLayer->framesCount - 1;
+ }
+
+ if (objDesc->multState != -1) {
+ if (objDesc->multState > 39) {
+ objDesc->stateMach = _goblins[(int)(objDesc->multObjIndex)]->stateMach;
+ objDesc->state = objDesc->multState - 40;
+ } else {
+ objDesc->stateMach = objDesc->realStateMach;
+ objDesc->state = objDesc->multState;
+ }
+ objDesc->animation =
+ objDesc->stateMach[objDesc->state][0]->
+ animation;
+ objDesc->multState = -1;
+ } else {
+ if (objDesc->nextState == -1)
+ continue;
+
+ objDesc->stateMach = objDesc->realStateMach;
+ objDesc->state = objDesc->nextState;
+ objDesc->animation =
+ objDesc->stateMach[objDesc->state][0]->
+ animation;
+ objDesc->nextState = -1;
+ }
+ objDesc->toRedraw = 1;
+ }
+ }
+}
+
+void Goblin::placeObject(Gob_Object *objDesc, char animated) {
+ int16 layer;
+
+ if (objDesc->stateMach[objDesc->state][0] != 0) {
+ objDesc->animation =
+ objDesc->stateMach[objDesc->state][0]->animation;
+
+ objDesc->noTick = 0;
+ objDesc->toRedraw = 1;
+ objDesc->doAnim = animated;
+
+ objDesc->maxTick = 1;
+ objDesc->tick = 1;
+ objDesc->curFrame = 0;
+ objDesc->type = 0;
+ objDesc->actionStartState = 0;
+ objDesc->nextState = -1;
+ objDesc->multState = -1;
+ objDesc->stateColumn = 0;
+ objDesc->curLookDir = 0;
+ objDesc->visible = 1;
+ objDesc->pickable = 0;
+ objDesc->unk14 = 0;
+
+ objDesc->relaxTime = _vm->_util->getRandom(30);
+
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+ _vm->_scenery->updateAnim(layer, 0, objDesc->animation, 0,
+ objDesc->xPos, objDesc->yPos, 0);
+
+ objDesc->order = _vm->_scenery->_toRedrawBottom / 24 + 3;
+
+ objDesc->left = objDesc->xPos;
+ objDesc->right = objDesc->xPos;
+ objDesc->dirtyLeft = objDesc->xPos;
+ objDesc->dirtyRight = objDesc->xPos;
+
+ objDesc->top = objDesc->yPos;
+ objDesc->bottom = objDesc->yPos;
+ objDesc->dirtyTop = objDesc->yPos;
+ objDesc->dirtyBottom = objDesc->yPos;
+
+ _vm->_util->listInsertBack(_objList, objDesc);
+ }
+}
+
+int16 Goblin::getObjMaxFrame(Gob_Object * objDesc) {
+ int16 layer;
+
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+ return _vm->_scenery->_animations[objDesc->animation].layers[layer]->framesCount -
+ 1;
+}
+
+int16 Goblin::objIntersected(Gob_Object *obj1, Gob_Object *obj2) {
+ if (obj1->type == 1 || obj2->type == 1)
+ return 0;
+
+ if (obj1->right < obj2->left)
+ return 0;
+
+ if (obj1->left > obj2->right)
+ return 0;
+
+ if (obj1->bottom < obj2->top)
+ return 0;
+
+ if (obj1->top > obj2->bottom)
+ return 0;
+
+ return 1;
+}
+
+void Goblin::setMultStates(Gob_Object * gobDesc) {
+ gobDesc->stateMach = _goblins[(int)gobDesc->multObjIndex]->stateMach;
+}
+
+int16 Goblin::nextLayer(Gob_Object *gobDesc) {
+ if (gobDesc->nextState == 10)
+ gobDesc->curLookDir = 0;
+
+ if (gobDesc->nextState == 11)
+ gobDesc->curLookDir = 4;
+
+ if (gobDesc->nextState > 39) {
+ setMultStates(gobDesc);
+ } else {
+ gobDesc->stateMach = gobDesc->realStateMach;
+ }
+
+ gobDesc->curFrame = 0;
+ if (gobDesc->nextState > 39)
+ gobDesc->state = gobDesc->nextState - 40;
+ else
+ gobDesc->state = gobDesc->nextState;
+
+ gobDesc->animation = gobDesc->stateMach[gobDesc->state][0]->animation;
+ return gobDesc->stateMach[gobDesc->state][0]->layer;
+}
+
+void Goblin::showBoredom(int16 gobIndex) {
+ Gob_Object *gobDesc;
+ int16 frame;
+ int16 frameCount;
+ int16 layer;
+ int16 state;
+ int16 boreFlag;
+
+ gobDesc = _goblins[gobIndex];
+ layer = gobDesc->stateMach[gobDesc->state][0]->layer;
+
+ frameCount =
+ _vm->_scenery->_animations[gobDesc->animation].layers[layer]->framesCount;
+ state = gobDesc->state;
+ frame = gobDesc->curFrame;
+
+ gobDesc->noTick = 0;
+ gobDesc->toRedraw = 1;
+
+ boreFlag = 1 << _vm->_util->getRandom(7);
+
+ if (gobIndex != _currentGoblin && _vm->_util->getRandom(3) != 0) {
+ if (state == 21) {
+ if ((boreFlag & 16) || (boreFlag & 32)) {
+ gobDesc->multState = 92 + gobIndex;
+ } else if (boreFlag & 1) {
+ gobDesc->multState = 86 + gobIndex;
+ } else if (boreFlag & 2) {
+ gobDesc->multState = 80 + gobIndex;
+ } else if (boreFlag & 4) {
+ gobDesc->multState = 89 + gobIndex;
+ } else if (boreFlag & 8) {
+ gobDesc->multState = 104 + gobIndex;
+ }
+ }
+ gobDesc->nextState = 21;
+ } else if (state >= 18 && state <= 21 && VAR(59) == 0) {
+ if (state == 30 || state == 31) // ???
+ return;
+
+ if (frame != frameCount)
+ return;
+
+ gobDesc->multState = 104 + gobIndex;
+ }
+}
+
+// index - goblin to select+1
+// index==0 - switch to next
+void Goblin::switchGoblin(int16 index) {
+ int16 next;
+ int16 tmp;
+
+ debug(4, "switchGoblin");
+ if (VAR(59) != 0)
+ return;
+
+ if (_goblins[_currentGoblin]->state <= 39 &&
+ _goblins[_currentGoblin]->curFrame != 0)
+ return;
+
+ if (index != 0 && _goblins[index - 1]->type != 0)
+ return;
+
+ if (index == 0)
+ next = (_currentGoblin + 1) % 3;
+ else
+ next = index - 1;
+
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 3 ||
+ _vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 6)
+ return;
+
+ if (_goblins[(_currentGoblin + 1) % 3]->type != 0 &&
+ _goblins[(_currentGoblin + 2) % 3]->type != 0)
+ return;
+
+ _gobPositions[_currentGoblin].x = _vm->_map->_curGoblinX;
+ _gobPositions[_currentGoblin].y = _vm->_map->_curGoblinY;
+
+ _goblins[_currentGoblin]->doAnim = 1;
+ _goblins[_currentGoblin]->nextState = 21;
+
+ nextLayer(_goblins[_currentGoblin]);
+
+ _currentGoblin = next;
+ if (_goblins[_currentGoblin]->type != 0)
+ _currentGoblin = (_currentGoblin + 1) % 3;
+
+ _goblins[_currentGoblin]->doAnim = 0;
+ if (_goblins[_currentGoblin]->curLookDir == 4)
+ _goblins[_currentGoblin]->nextState = 18;
+ else
+ _goblins[_currentGoblin]->nextState = 19;
+
+ _goblins[_currentGoblin]->toRedraw = 1;
+ nextLayer(_goblins[_currentGoblin]);
+
+ tmp = _gobPositions[_currentGoblin].x;
+ _pressedMapX = tmp;
+ _vm->_map->_destX = tmp;
+ _gobDestX = tmp;
+ _vm->_map->_curGoblinX = tmp;
+
+ tmp = _gobPositions[_currentGoblin].y;
+ _pressedMapY = tmp;
+ _vm->_map->_destY = tmp;
+ _gobDestY = tmp;
+ _vm->_map->_curGoblinY = tmp;
+
+ *_curGobVarPtr = _currentGoblin;
+ _pathExistence = 0;
+ _readyToAct = 0;
+}
+
+void Goblin::adjustDest(int16 posX, int16 posY) {
+ int16 resDelta;
+ int16 resDeltaDir;
+ int16 resDeltaPix;
+ int16 deltaPix;
+ int16 i;
+
+ if (_vm->_map->_passMap[_pressedMapY][_pressedMapX] == 0 &&
+ (_gobAction == 0
+ || _vm->_map->_itemsMap[_pressedMapY][_pressedMapX] == 0)) {
+
+ resDelta = -1;
+ resDeltaDir = 0;
+ resDeltaPix = 0;
+
+ for (i = 1;
+ i <= _pressedMapX
+ && _vm->_map->_passMap[_pressedMapY][_pressedMapX - i] == 0;
+ i++);
+
+ if (i <= _pressedMapX) {
+ resDeltaPix = (i - 1) * 12 + (posX % 12) + 1;
+ resDelta = i;
+ }
+
+ for (i = 1;
+ (i + _pressedMapX) < Map::kMapWidth
+ && _vm->_map->_passMap[_pressedMapY][_pressedMapX + i] == 0;
+ i++);
+
+ if (_pressedMapX + i < Map::kMapWidth) {
+ deltaPix = (i * 12) - (posX % 12);
+ if (resDelta == -1 || deltaPix < resDeltaPix) {
+ resDeltaPix = deltaPix;
+ resDelta = i;
+ resDeltaDir = 1;
+ }
+ }
+
+ for (i = 1;
+ (i + _pressedMapY) < Map::kMapHeight
+ && _vm->_map->_passMap[_pressedMapY + i][_pressedMapX] == 0;
+ i++);
+
+ if (_pressedMapY + i < Map::kMapHeight) {
+ deltaPix = (i * 6) - (posY % 6);
+ if (resDelta == -1 || deltaPix < resDeltaPix) {
+ resDeltaPix = deltaPix;
+ resDelta = i;
+ resDeltaDir = 2;
+ }
+ }
+
+ for (i = 1;
+ i <= _pressedMapY
+ && _vm->_map->_passMap[_pressedMapY - i][_pressedMapX] == 0;
+ i++);
+
+ if (i <= _pressedMapY) {
+ deltaPix = (i * 6) + (posY % 6);
+ if (resDelta == -1 || deltaPix < resDeltaPix) {
+ resDeltaPix = deltaPix;
+ resDelta = i;
+ resDeltaDir = 3;
+ }
+ }
+
+ switch (resDeltaDir) {
+ case 0:
+ _pressedMapX -= resDelta;
+ break;
+
+ case 1:
+ _pressedMapX += resDelta;
+ break;
+
+ case 2:
+ _pressedMapY += resDelta;
+ break;
+
+ case 3:
+ _pressedMapY -= resDelta;
+ break;
+ }
+
+ }
+}
+
+void Goblin::adjustTarget(void) {
+ if (_gobAction == 4
+ && _vm->_map->_itemsMap[_pressedMapY][_pressedMapX] == 0) {
+
+ if (_pressedMapY > 0
+ && _vm->_map->_itemsMap[_pressedMapY - 1][_pressedMapX] !=
+ 0) {
+ _pressedMapY--;
+ } else if (_pressedMapX < Map::kMapWidth - 1
+ && _vm->_map->_itemsMap[_pressedMapY][_pressedMapX + 1] !=
+ 0) {
+ _pressedMapX++;
+ } else if (_pressedMapX < Map::kMapWidth - 1 && _pressedMapY > 0
+ && _vm->_map->_itemsMap[_pressedMapY - 1][_pressedMapX +
+ 1] != 0) {
+ _pressedMapY--;
+ _pressedMapX++;
+ }
+ }
+}
+
+void Goblin::targetDummyItem(Gob_Object *gobDesc) {
+ if (_vm->_map->_itemsMap[_pressedMapY][_pressedMapX] == 0 &&
+ _vm->_map->_passMap[_pressedMapY][_pressedMapX] == 1) {
+ if (gobDesc->curLookDir == 0) {
+ _vm->_map->_itemPoses[0].x = _pressedMapX;
+ _vm->_map->_itemPoses[0].y = _pressedMapY;
+ _vm->_map->_itemPoses[0].orient = -4;
+ } else {
+ _vm->_map->_itemPoses[0].x = _pressedMapX;
+ _vm->_map->_itemPoses[0].y = _pressedMapY;
+ _vm->_map->_itemPoses[0].orient = -1;
+ }
+ }
+}
+
+void Goblin::targetItem(void) {
+ int16 tmpX;
+ int16 tmpY;
+ int16 items;
+ int16 layer;
+ int16 tmpPosX;
+ int16 tmpPosY;
+ Gob_Object *itemDesc;
+
+ if (_gobAction == 3 || _gobAction == 4) {
+ items = _vm->_map->_itemsMap[_pressedMapY][_pressedMapX];
+ if (_gobAction == 4 && (items & 0xff00) != 0 &&
+ _objects[_itemToObject[(items & 0xff00) >> 8]]->
+ pickable == 1) {
+ _destItemId = (items & 0xff00) >> 8;
+ _destActionItem = (items & 0xff00) >> 8;
+ _itemByteFlag = 1;
+ } else if ((items & 0xff) == 0) {
+ _destItemId = (items & 0xff00) >> 8;
+ _destActionItem = (items & 0xff00) >> 8;
+ _itemByteFlag = 1;
+ } else if (_gobAction == 3 && _currentGoblin == 2 &&
+ (items & 0xff00) != 0) {
+ _destItemId = (items & 0xff00) >> 8;
+ _destActionItem = (items & 0xff00) >> 8;
+ _itemByteFlag = 1;
+ } else {
+ _destItemId = items & 0xff;
+ _destActionItem = items & 0xff;
+ _itemByteFlag = 0;
+ }
+
+ _pressedMapY = _vm->_map->_itemPoses[_destItemId].y;
+ _vm->_map->_destY = _vm->_map->_itemPoses[_destItemId].y;
+ _gobDestY = _vm->_map->_itemPoses[_destItemId].y;
+
+ if (_gobAction == 3 || _destActionItem == 0) {
+ _pressedMapX = _vm->_map->_itemPoses[_destItemId].x;
+ _vm->_map->_destX = _vm->_map->_itemPoses[_destItemId].x;
+ _gobDestX = _vm->_map->_itemPoses[_destItemId].x;
+ } else if ((items & 0xff00) != 0) {
+ if (_vm->_map->_itemPoses[_destItemId].orient == 4) {
+ if ((_vm->_map->_itemsMap[_pressedMapY]
+ [_pressedMapX - 1] & 0xff00) ==
+ (_vm->_map->_itemsMap[_pressedMapY]
+ [_pressedMapX] & 0xff00)) {
+ _pressedMapX--;
+ _vm->_map->_destX = _pressedMapX;
+ _gobDestX = _pressedMapX;
+ }
+ } else if (_vm->_map->_itemPoses[_destItemId].orient == 0) {
+
+ if ((_vm->_map->_itemsMap[_pressedMapY]
+ [_pressedMapX + 1] & 0xff00) ==
+ (_vm->_map->_itemsMap[_pressedMapY]
+ [_pressedMapX] & 0xff00)) {
+ _pressedMapX++;
+ _vm->_map->_destX = _pressedMapX;
+ _gobDestX = _pressedMapX;
+ }
+ }
+
+ if ((_vm->_map->_itemsMap[_pressedMapY +
+ 1][_pressedMapX] & 0xff00) ==
+ (_vm->_map->_itemsMap[_pressedMapY][_pressedMapX] &
+ 0xff00)) {
+ _pressedMapY++;
+ _vm->_map->_destY = _pressedMapY;
+ _gobDestY = _pressedMapY;
+ }
+ } else {
+ if (_vm->_map->_itemPoses[_destItemId].orient == 4) {
+ if ((_vm->_map->_itemsMap[_pressedMapY]
+ [_pressedMapX - 1]) ==
+ (_vm->_map->_itemsMap[_pressedMapY]
+ [_pressedMapX])) {
+ _pressedMapX--;
+ _vm->_map->_destX = _pressedMapX;
+ _gobDestX = _pressedMapX;
+ }
+ } else if (_vm->_map->_itemPoses[_destItemId].orient == 0) {
+
+ if ((_vm->_map->_itemsMap[_pressedMapY]
+ [_pressedMapX + 1]) ==
+ (_vm->_map->_itemsMap[_pressedMapY]
+ [_pressedMapX])) {
+ _pressedMapX++;
+ _vm->_map->_destX = _pressedMapX;
+ _gobDestX = _pressedMapX;
+ }
+ }
+
+ if ((_vm->_map->_itemsMap[_pressedMapY +
+ 1][_pressedMapX]) ==
+ (_vm->_map->_itemsMap[_pressedMapY][_pressedMapX])) {
+ _pressedMapY++;
+ _vm->_map->_destY = _pressedMapY;
+ _gobDestY = _pressedMapY;
+ }
+
+ }
+
+ if (_gobAction == 4 && _destActionItem != 0 &&
+ _itemToObject[_destActionItem] != -1 &&
+ _objects[_itemToObject[_destActionItem]]->
+ pickable == 1) {
+
+ itemDesc =
+ _objects[_itemToObject[_destActionItem]];
+
+ itemDesc->animation =
+ itemDesc->stateMach[itemDesc->state][0]->animation;
+ layer =
+ itemDesc->stateMach[itemDesc->state][itemDesc->
+ stateColumn]->layer;
+
+ _vm->_scenery->updateAnim(layer, 0, itemDesc->animation, 0,
+ itemDesc->xPos, itemDesc->yPos, 0);
+
+ tmpX = (_vm->_scenery->_toRedrawRight + _vm->_scenery->_toRedrawLeft) / 2;
+ tmpY = _vm->_scenery->_toRedrawBottom;
+
+ tmpPosY = tmpY / 6;
+ if ((tmpY % 3) < 3 && tmpPosY > 0)
+ tmpPosY--;
+
+ tmpPosX = tmpX / 12;
+ if ((tmpX % 12) < 6 && tmpPosX > 0)
+ tmpPosX--;
+
+ if (_vm->_map->_itemPoses[_destActionItem].orient == 0 ||
+ _vm->_map->_itemPoses[_destActionItem].orient == -1) {
+ tmpPosX++;
+ }
+
+ if (_vm->_map->_passMap[tmpPosY][tmpPosX] == 1) {
+ _pressedMapX = tmpPosX;
+ _vm->_map->_destX = tmpPosX;
+ _gobDestX = tmpPosX;
+
+ _pressedMapY = tmpPosY;
+ _vm->_map->_destY = tmpPosY;
+ _gobDestY = tmpPosY;
+ }
+ }
+ }
+}
+
+void Goblin::initiateMove(void) {
+ _vm->_map->findNearestToDest();
+ _vm->_map->findNearestToGob();
+ _vm->_map->optimizePoints();
+
+ _pathExistence = _vm->_map->checkDirectPath(_vm->_map->_curGoblinX, _vm->_map->_curGoblinY,
+ _pressedMapX, _pressedMapY);
+
+ if (_pathExistence == 3) {
+ if (_vm->_map->checkLongPath(_vm->_map->_curGoblinX, _vm->_map->_curGoblinY,
+ _pressedMapX, _pressedMapY,
+ _vm->_map->_nearestWayPoint, _vm->_map->_nearestDest) == 0) {
+ _pathExistence = 0;
+ } else {
+ _vm->_map->_destX = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
+ _vm->_map->_destY = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+ }
+ }
+}
+
+void Goblin::moveFindItem(int16 posX, int16 posY) {
+ int16 i;
+ if (_gobAction == 3 || _gobAction == 4) {
+ for (i = 0; i < 20; i++) {
+ if (_objects[i] == 0)
+ continue;
+
+ if (_objects[i]->type != 0)
+ continue;
+
+ if (_objects[i]->left > posX)
+ continue;
+
+ if (_objects[i]->right < posX)
+ continue;
+
+ if (_objects[i]->top > posY)
+ continue;
+
+ if (_objects[i]->bottom < posY)
+ continue;
+
+ if (_objects[i]->right - _objects[i]->left < 40)
+ posX =
+ (_objects[i]->left +
+ _objects[i]->right) / 2;
+
+ if (_objects[i]->bottom - _objects[i]->top < 40)
+ posY =
+ (_objects[i]->top +
+ _objects[i]->bottom) / 2;
+
+ break;
+ }
+
+ _pressedMapX = posX / 12;
+ _pressedMapY = posY / 6;
+
+ if (_vm->_map->_itemsMap[_pressedMapY][_pressedMapX] == 0
+ && i < 20) {
+
+ if (_vm->_map->_itemsMap[_pressedMapY +
+ 1][_pressedMapX] != 0) {
+ _pressedMapY++;
+ } else if (_vm->_map->_itemsMap[_pressedMapY +
+ 1][_pressedMapX + 1] != 0) {
+ _pressedMapX++;
+ _pressedMapY++;
+ } else
+ if (_vm->_map->_itemsMap[_pressedMapY][_pressedMapX +
+ 1] != 0) {
+ _pressedMapX++;
+ } else if (_vm->_map->_itemsMap[_pressedMapY -
+ 1][_pressedMapX + 1] != 0) {
+ _pressedMapX++;
+ _pressedMapY--;
+ } else if (_vm->_map->_itemsMap[_pressedMapY -
+ 1][_pressedMapX] != 0) {
+ _pressedMapY--;
+ } else if (_vm->_map->_itemsMap[_pressedMapY -
+ 1][_pressedMapX - 1] != 0) {
+ _pressedMapY--;
+ _pressedMapX--;
+ } else
+ if (_vm->_map->_itemsMap[_pressedMapY][_pressedMapX -
+ 1] != 0) {
+ _pressedMapX--;
+ } else if (_vm->_map->_itemsMap[_pressedMapY +
+ 1][_pressedMapX - 1] != 0) {
+ _pressedMapX--;
+ _pressedMapY++;
+ }
+ }
+ } else {
+ _pressedMapX = posX / 12;
+ _pressedMapY = posY / 6;
+ }
+}
+
+void Goblin::moveCheckSelect(int16 framesCount, Gob_Object * gobDesc, int16 *pGobIndex,
+ int16 *nextAct) {
+ if (gobDesc->right > _vm->_global->_inter_mouseX &&
+ gobDesc->left < _vm->_global->_inter_mouseX &&
+ gobDesc->bottom > _vm->_global->_inter_mouseY &&
+ gobDesc->bottom - 10 < _vm->_global->_inter_mouseY && _gobAction == 0) {
+ if (gobDesc->curLookDir & 4)
+ *nextAct = 16;
+ else
+ *nextAct = 23;
+
+ gobDesc->curFrame = framesCount - 1;
+ _pathExistence = 0;
+ } else {
+ *pGobIndex = peekGoblin(gobDesc);
+
+ if (*pGobIndex != 0) {
+ _pathExistence = 0;
+ } else if (_vm->_map->_curGoblinX == _pressedMapX &&
+ _vm->_map->_curGoblinY == _pressedMapY) {
+
+ if (_gobAction != 0)
+ _readyToAct = 1;
+
+ _pathExistence = 0;
+ }
+ }
+}
+
+void Goblin::moveInitStep(int16 framesCount, int16 action, int16 cont,
+ Gob_Object *gobDesc, int16 *pGobIndex, int16 *pNextAct) {
+ int16 posX;
+ int16 posY;
+
+ if (cont != 0 && _goesAtTarget == 0 &&
+ _readyToAct == 0 && VAR(59) == 0 &&
+ gobDesc->type != 1 &&
+ gobDesc->state != 10 && gobDesc->state != 11) {
+ if (gobDesc->state >= 40) {
+ gobDesc->curFrame = framesCount - 1;
+ }
+
+ _gobAction = action;
+ _forceNextState[0] = -1;
+ _forceNextState[1] = -1;
+ _forceNextState[2] = -1;
+
+ if (action == 3) {
+ posX = _vm->_global->_inter_mouseX + 6;
+ posY = _vm->_global->_inter_mouseY + 7;
+ } else if (action == 4) {
+ posX = _vm->_global->_inter_mouseX + 7;
+ posY = _vm->_global->_inter_mouseY + 12;
+ } else {
+ posX = _vm->_global->_inter_mouseX;
+ posY = _vm->_global->_inter_mouseY;
+ }
+
+ moveFindItem(posX, posY);
+ adjustDest(posX, posY);
+ adjustTarget();
+
+ _vm->_map->_destX = _pressedMapX;
+ _gobDestX = _pressedMapX;
+
+ _vm->_map->_destY = _pressedMapY;
+ _gobDestY = _pressedMapY;
+
+ targetDummyItem(gobDesc);
+
+ targetItem();
+ initiateMove();
+
+ moveCheckSelect(framesCount, gobDesc, pGobIndex, pNextAct);
+ } else {
+
+ if (_readyToAct != 0 &&
+ (_vm->_map->_curGoblinX != _pressedMapX ||
+ _vm->_map->_curGoblinY != _pressedMapY))
+ _readyToAct = 0;
+
+ if (gobDesc->type == 1) {
+ *pGobIndex = peekGoblin(gobDesc);
+ }
+ }
+}
+
+void Goblin::moveTreatRopeStairs(Gob_Object *gobDesc) {
+ if (_currentGoblin != 1)
+ return;
+
+ if (gobDesc->nextState == 28
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY - 1][_vm->_map->_curGoblinX] == 6) {
+ _forceNextState[0] = 28;
+ _forceNextState[1] = -1;
+ }
+
+ if (gobDesc->nextState == 29
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY + 1][_vm->_map->_curGoblinX] == 6) {
+ _forceNextState[0] = 29;
+ _forceNextState[1] = -1;
+ }
+
+ if ((gobDesc->nextState == 28 || gobDesc->nextState == 29
+ || gobDesc->nextState == 20)
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 6) {
+ if ((gobDesc->curLookDir == 0 || gobDesc->curLookDir == 4
+ || gobDesc->curLookDir == 2)
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY - 1][_vm->_map->_curGoblinX] == 6) {
+ _forceNextState[0] = 28;
+ _forceNextState[1] = -1;
+ } else if ((gobDesc->curLookDir == 0
+ || gobDesc->curLookDir == 4
+ || gobDesc->curLookDir == 6)
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY + 1][_vm->_map->_curGoblinX] == 6) {
+ _forceNextState[0] = 29;
+ _forceNextState[1] = -1;
+ }
+ }
+
+ if (gobDesc->nextState == 8
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY - 1][_vm->_map->_curGoblinX] == 3) {
+ _forceNextState[0] = 8;
+ _forceNextState[1] = -1;
+ }
+
+ if (gobDesc->nextState == 9
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY + 1][_vm->_map->_curGoblinX] == 3) {
+ _forceNextState[0] = 9;
+ _forceNextState[1] = -1;
+ }
+
+ if (gobDesc->nextState == 20
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 3) {
+ if ((gobDesc->curLookDir == 0 || gobDesc->curLookDir == 4
+ || gobDesc->curLookDir == 2)
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY - 1][_vm->_map->_curGoblinX] == 3) {
+ _forceNextState[0] = 8;
+ _forceNextState[1] = -1;
+ } else if ((gobDesc->curLookDir == 0
+ || gobDesc->curLookDir == 4
+ || gobDesc->curLookDir == 6)
+ && _vm->_map->_passMap[_vm->_map->_curGoblinY + 1][_vm->_map->_curGoblinX] == 3) {
+ _forceNextState[0] = 9;
+ _forceNextState[1] = -1;
+ }
+ }
+
+}
+
+void Goblin::movePathFind(Gob_Object *gobDesc, int16 nextAct) {
+ if (_pathExistence == 1) {
+ _vm->_map->_curGoblinX = _gobPositions[_currentGoblin].x;
+ _vm->_map->_curGoblinY = _gobPositions[_currentGoblin].y;
+
+ if (_vm->_map->_curGoblinX == _pressedMapX &&
+ _vm->_map->_curGoblinY == _pressedMapY && _gobAction != 0) {
+ _readyToAct = 1;
+ _pathExistence = 0;
+ }
+
+ nextAct = _vm->_map->getDirection(_vm->_map->_curGoblinX, _vm->_map->_curGoblinY,
+ _vm->_map->_destX, _vm->_map->_destY);
+
+ if (nextAct == 0)
+ _pathExistence = 0;
+ } else if (_pathExistence == 3) {
+ _vm->_map->_curGoblinX = _gobPositions[_currentGoblin].x;
+ _vm->_map->_curGoblinY = _gobPositions[_currentGoblin].y;
+
+ if (_vm->_map->_curGoblinX == _gobDestX && _vm->_map->_curGoblinY == _gobDestY) {
+ _pathExistence = 1;
+ _vm->_map->_destX = _pressedMapX;
+ _vm->_map->_destY = _pressedMapY;
+ } else {
+
+ if (_vm->_map->checkDirectPath(_vm->_map->_curGoblinX, _vm->_map->_curGoblinY,
+ _gobDestX, _gobDestY) == 1) {
+ _vm->_map->_destX = _gobDestX;
+ _vm->_map->_destY = _gobDestY;
+ } else if (_vm->_map->_curGoblinX == _vm->_map->_destX && _vm->_map->_curGoblinY == _vm->_map->_destY) {
+
+ if (_vm->_map->_nearestWayPoint > _vm->_map->_nearestDest) {
+ _vm->_map->optimizePoints();
+
+ _vm->_map->_destX =
+ _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].
+ x;
+ _vm->_map->_destY =
+ _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].
+ y;
+
+ if (_vm->_map->_nearestWayPoint > _vm->_map->_nearestDest)
+ _vm->_map->_nearestWayPoint--;
+ } else if (_vm->_map->_nearestWayPoint < _vm->_map->_nearestDest) {
+ _vm->_map->optimizePoints();
+
+ _vm->_map->_destX =
+ _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].
+ x;
+ _vm->_map->_destY =
+ _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].
+ y;
+
+ if (_vm->_map->_nearestWayPoint < _vm->_map->_nearestDest)
+ _vm->_map->_nearestWayPoint++;
+ } else {
+ if (_vm->_map->checkDirectPath(_vm->_map->_curGoblinX,
+ _vm->_map->_curGoblinY, _gobDestX,
+ _gobDestY) == 3 && _vm->_map->_passMap[_pressedMapY][_pressedMapX] != 0) {
+ _vm->_map->_destX = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].x;
+ _vm->_map->_destY = _vm->_map->_wayPoints[_vm->_map->_nearestWayPoint].y;
+ } else {
+ _pathExistence = 1;
+ _vm->_map->_destX = _pressedMapX;
+ _vm->_map->_destY = _pressedMapY;
+ }
+ }
+ }
+ nextAct =
+ _vm->_map->getDirection(_vm->_map->_curGoblinX, _vm->_map->_curGoblinY,
+ _vm->_map->_destX, _vm->_map->_destY);
+ }
+ }
+
+ if (_readyToAct != 0 && (_gobAction == 3 || _gobAction == 4))
+ nextAct = 0x4dc8;
+
+ switch (nextAct) {
+ case Map::kDirW:
+ gobDesc->nextState = rotateState(gobDesc->curLookDir, 0);
+ break;
+
+ case Map::kDirE:
+ gobDesc->nextState = rotateState(gobDesc->curLookDir, 4);
+ break;
+
+ case 16:
+ gobDesc->nextState = 16;
+ break;
+
+ case 23:
+ gobDesc->nextState = 23;
+ break;
+
+ case Map::kDirN:
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY - 1][_vm->_map->_curGoblinX] == 6 &&
+ _currentGoblin != 1) {
+ _pathExistence = 0;
+ break;
+ }
+
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 3) {
+ gobDesc->nextState = 8;
+ break;
+ }
+
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 6 &&
+ _currentGoblin == 1) {
+ gobDesc->nextState = 28;
+ break;
+ }
+
+ gobDesc->nextState = rotateState(gobDesc->curLookDir, 2);
+ break;
+
+ case Map::kDirS:
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY + 1][_vm->_map->_curGoblinX] == 6 &&
+ _currentGoblin != 1) {
+ _pathExistence = 0;
+ break;
+ }
+
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 3) {
+ gobDesc->nextState = 9;
+ break;
+ }
+
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 6 &&
+ _currentGoblin == 1) {
+ gobDesc->nextState = 29;
+ break;
+ }
+
+ gobDesc->nextState = rotateState(gobDesc->curLookDir, 6);
+ break;
+
+ case Map::kDirSE:
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY + 1][_vm->_map->_curGoblinX + 1] == 6 &&
+ _currentGoblin != 1) {
+ _pathExistence = 0;
+ break;
+ }
+
+ gobDesc->nextState = 5;
+ if (gobDesc->curLookDir == 4)
+ break;
+
+ gobDesc->nextState = rotateState(gobDesc->curLookDir, 4);
+ break;
+
+ case Map::kDirSW:
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY + 1][_vm->_map->_curGoblinX - 1] == 6 &&
+ _currentGoblin != 1) {
+ _pathExistence = 0;
+ break;
+ }
+
+ gobDesc->nextState = 7;
+ if (gobDesc->curLookDir == 0)
+ break;
+
+ gobDesc->nextState = rotateState(gobDesc->curLookDir, 0);
+ break;
+
+ case Map::kDirNW:
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY - 1][_vm->_map->_curGoblinX - 1] == 6 &&
+ _currentGoblin != 1) {
+ _pathExistence = 0;
+ break;
+ }
+
+ gobDesc->nextState = 1;
+ if (gobDesc->curLookDir == 0)
+ break;
+
+ gobDesc->nextState = rotateState(gobDesc->curLookDir, 0);
+ break;
+
+ case Map::kDirNE:
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY - 1][_vm->_map->_curGoblinX + 1] == 6 &&
+ _currentGoblin != 1) {
+ _pathExistence = 0;
+ break;
+ }
+
+ gobDesc->nextState = 3;
+ if (gobDesc->curLookDir == 4)
+ break;
+
+ gobDesc->nextState = rotateState(gobDesc->curLookDir, 4);
+ break;
+
+ case 0x4dc8:
+
+ if (_currentGoblin == 0 && _gobAction == 3
+ && _itemIndInPocket == -1) {
+ _destItemId = -1;
+ _readyToAct = 0;
+ break;
+ }
+
+ if (_currentGoblin == 0 && _gobAction == 4 &&
+ _itemIndInPocket == -1 && _destActionItem == 0) {
+ gobDesc->multState = 104;
+ _destItemId = -1;
+ _readyToAct = 0;
+ break;
+ }
+
+ if (_currentGoblin == 0 && _gobAction == 4 &&
+ _itemIndInPocket == -1 && _destActionItem != 0 &&
+ _itemToObject[_destActionItem] != -1 &&
+ _objects[_itemToObject[_destActionItem]]->
+ pickable == 0) {
+ gobDesc->multState = 104;
+ _destItemId = -1;
+ _readyToAct = 0;
+ break;
+ }
+
+ switch (_vm->_map->_itemPoses[_destActionItem].orient) {
+ case 0:
+ case -4:
+ gobDesc->nextState = 10;
+ gobDesc->curLookDir = 0;
+ _destItemId = -1;
+ break;
+
+ case -1:
+ case 4:
+ gobDesc->nextState = 11;
+ gobDesc->curLookDir = 4;
+ _destItemId = -1;
+ break;
+ }
+ break;
+
+ default:
+ if (_vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 3 ||
+ (_vm->_map->_passMap[_vm->_map->_curGoblinY][_vm->_map->_curGoblinX] == 6
+ && _currentGoblin == 1)) {
+ gobDesc->nextState = 20;
+ break;
+ }
+
+ switch (gobDesc->curLookDir) {
+ case 2:
+ case 4:
+ gobDesc->nextState = 18;
+ break;
+
+ case 6:
+ case 0:
+ gobDesc->nextState = 19;
+ break;
+ }
+ break;
+ }
+ return;
+}
+
+void Goblin::moveAdvance(Gob_Object *gobDesc, int16 nextAct, int16 framesCount) {
+ int16 i;
+ int16 newX;
+ int16 newY;
+ int16 flag;
+
+ movePathFind(gobDesc, nextAct);
+
+ gobDesc->curFrame++;
+ if (gobDesc->curFrame == 1)
+ gobDesc->actionStartState = gobDesc->state;
+
+ if (_goesAtTarget == 0
+ && gobDesc->stateMach == gobDesc->realStateMach) {
+ switch (gobDesc->state) {
+ case 0:
+ case 1:
+ case 7:
+ case 13:
+ case 16:
+ case 27:
+ gobDesc->curLookDir = 0;
+ break;
+
+ case 3:
+ case 4:
+ case 5:
+ case 12:
+ case 23:
+ case 26:
+ gobDesc->curLookDir = 4;
+ break;
+
+ case 28:
+ if (_currentGoblin != 1)
+ break;
+ gobDesc->curLookDir = 2;
+ break;
+
+ case 2:
+ case 8:
+ case 15:
+ case 22:
+ case 25:
+ gobDesc->curLookDir = 2;
+ break;
+
+ case 29:
+ if (_currentGoblin != 1)
+ break;
+
+ gobDesc->curLookDir = 6;
+ break;
+
+ case 6:
+ case 9:
+ case 14:
+ case 17:
+ case 24:
+ gobDesc->curLookDir = 6;
+ break;
+ }
+ }
+
+ if (gobDesc->state >= 0 && gobDesc->state < 10 &&
+ gobDesc->stateMach == gobDesc->realStateMach &&
+ (gobDesc->curFrame == 3 || gobDesc->curFrame == 6)) {
+ _vm->_snd->speakerOn(10 * _vm->_util->getRandom(3) + 50, 5);
+ }
+
+ if (_currentGoblin == 0
+ && gobDesc->stateMach == gobDesc->realStateMach
+ && (gobDesc->state == 10 || gobDesc->state == 11)
+ && gobDesc->curFrame == 9) {
+ _vm->_snd->stopSound(0);
+ if (_itemIndInPocket != -1) {
+ _vm->_snd->playSample(_soundData[14], 1, 9000);
+ }
+
+ if (_itemIndInPocket == -1) {
+ _vm->_snd->playSample(_soundData[14], 1, 5000);
+ }
+ }
+
+ if (_boreCounter++ == 120) {
+ _boreCounter = 0;
+ for (i = 0; i < 3; i++)
+ showBoredom(i);
+ }
+
+ if (gobDesc->multState != -1 && gobDesc->curFrame == framesCount &&
+ gobDesc->state != gobDesc->multState) {
+ gobDesc->nextState = gobDesc->multState;
+ gobDesc->multState = -1;
+
+ newX =
+ _vm->_scenery->_animations[gobDesc->animation].
+ layers[_gobStateLayer]->animDeltaX + gobDesc->xPos;
+
+ newY =
+ _vm->_scenery->_animations[gobDesc->animation].
+ layers[_gobStateLayer]->animDeltaY + gobDesc->yPos;
+
+ _gobStateLayer = nextLayer(gobDesc);
+
+ gobDesc->xPos = newX;
+ gobDesc->yPos = newY;
+ } else {
+ if (gobDesc->curFrame == 3 &&
+ gobDesc->stateMach == gobDesc->realStateMach &&
+ (gobDesc->state < 10 ||
+ (_currentGoblin == 1 && (gobDesc->state == 28
+ || gobDesc->state == 29))
+ )) {
+ flag = 0;
+ if (_forceNextState[0] != -1) {
+ gobDesc->nextState = _forceNextState[0];
+ for (i = 0; i < 9; i++)
+ _forceNextState[i] =
+ _forceNextState[i + 1];
+ }
+
+ _vm->_map->_curGoblinX = _gobPositions[_currentGoblin].x;
+ _vm->_map->_curGoblinY = _gobPositions[_currentGoblin].y;
+
+ if (gobDesc->nextState != gobDesc->state) {
+ _gobStateLayer = nextLayer(gobDesc);
+ flag = 1;
+ }
+
+ switch (gobDesc->state) {
+ case 0:
+ _gobPositions[_currentGoblin].x--;
+ break;
+
+ case 2:
+ case 8:
+ _gobPositions[_currentGoblin].y--;
+ break;
+
+ case 4:
+ _gobPositions[_currentGoblin].x++;
+ break;
+
+ case 6:
+ case 9:
+ _gobPositions[_currentGoblin].y++;
+ break;
+
+ case 1:
+ _gobPositions[_currentGoblin].x--;
+ _gobPositions[_currentGoblin].y--;
+ break;
+
+ case 3:
+ _gobPositions[_currentGoblin].x++;
+ _gobPositions[_currentGoblin].y--;
+ break;
+
+ case 5:
+ _gobPositions[_currentGoblin].x++;
+ _gobPositions[_currentGoblin].y++;
+ break;
+
+ case 7:
+ _gobPositions[_currentGoblin].x--;
+ _gobPositions[_currentGoblin].y++;
+ break;
+
+ case 38:
+ _gobPositions[_currentGoblin].y++;
+ break;
+ }
+
+ if (_currentGoblin == 1) {
+ if (gobDesc->state == 28)
+ _gobPositions[1].y--;
+
+ if (gobDesc->state == 29)
+ _gobPositions[1].y++;
+ }
+
+ if (flag != 0) {
+ _vm->_scenery->updateAnim(_gobStateLayer, 0,
+ gobDesc->animation, 0, gobDesc->xPos,
+ gobDesc->yPos, 0);
+
+ gobDesc->yPos =
+ (_vm->_map->_curGoblinY + 1) * 6 -
+ (_vm->_scenery->_toRedrawBottom - _vm->_scenery->_animTop);
+ gobDesc->xPos =
+ _vm->_map->_curGoblinX * 12 - (_vm->_scenery->_toRedrawLeft -
+ _vm->_scenery->_animLeft);
+ }
+
+ if ((gobDesc->state == 10 || gobDesc->state == 11)
+ && _currentGoblin != 0)
+ _goesAtTarget = 1;
+ }
+
+ if (gobDesc->curFrame != framesCount)
+ return;
+
+ if (_forceNextState[0] != -1) {
+ gobDesc->nextState = _forceNextState[0];
+ for (i = 0; i < 10; i++)
+ _forceNextState[i] =
+ _forceNextState[i + 1];
+ }
+
+ _vm->_map->_curGoblinX = _gobPositions[_currentGoblin].x;
+ _vm->_map->_curGoblinY = _gobPositions[_currentGoblin].y;
+
+ _gobStateLayer = nextLayer(gobDesc);
+ if (gobDesc->stateMach == gobDesc->realStateMach) {
+
+ switch (gobDesc->nextState) {
+ case 0:
+ _gobPositions[_currentGoblin].x--;
+ break;
+
+ case 2:
+ case 8:
+ _gobPositions[_currentGoblin].y--;
+ break;
+
+ case 4:
+ _gobPositions[_currentGoblin].x++;
+ break;
+
+ case 6:
+ case 9:
+ _gobPositions[_currentGoblin].y++;
+ break;
+
+ case 1:
+ _gobPositions[_currentGoblin].x--;
+ _gobPositions[_currentGoblin].y--;
+ break;
+
+ case 3:
+ _gobPositions[_currentGoblin].x++;
+ _gobPositions[_currentGoblin].y--;
+ break;
+
+ case 5:
+ _gobPositions[_currentGoblin].x++;
+ _gobPositions[_currentGoblin].y++;
+ break;
+
+ case 7:
+ _gobPositions[_currentGoblin].x--;
+ _gobPositions[_currentGoblin].y++;
+ break;
+
+ case 38:
+ _gobPositions[_currentGoblin].y++;
+ break;
+ }
+ if (_currentGoblin == 1) {
+ if (gobDesc->nextState == 28)
+ _gobPositions[1].y--;
+
+ if (gobDesc->nextState == 29)
+ _gobPositions[1].y++;
+ }
+ }
+
+ _vm->_scenery->updateAnim(_gobStateLayer, 0, gobDesc->animation, 0,
+ gobDesc->xPos, gobDesc->yPos, 0);
+
+ gobDesc->yPos =
+ (_vm->_map->_curGoblinY + 1) * 6 - (_vm->_scenery->_toRedrawBottom -
+ _vm->_scenery->_animTop);
+ gobDesc->xPos =
+ _vm->_map->_curGoblinX * 12 - (_vm->_scenery->_toRedrawLeft - _vm->_scenery->_animLeft);
+
+ if ((gobDesc->state == 10 || gobDesc->state == 11)
+ && _currentGoblin != 0)
+ _goesAtTarget = 1;
+ }
+ return;
+}
+
+int16 Goblin::doMove(Gob_Object *gobDesc, int16 cont, int16 action) {
+ int16 framesCount;
+ int16 nextAct;
+ int16 gobIndex;
+ int16 layer;
+
+ nextAct = 0;
+ gobIndex = 0;
+
+ layer = gobDesc->stateMach[gobDesc->state][0]->layer;
+ framesCount =
+ _vm->_scenery->_animations[gobDesc->animation].layers[layer]->framesCount;
+
+ if (VAR(59) == 0 &&
+ gobDesc->state != 30 && gobDesc->state != 31) {
+ gobDesc->order = (gobDesc->bottom) / 24 + 3;
+ }
+
+ if (_positionedGob != _currentGoblin) {
+ _vm->_map->_curGoblinX = _gobPositions[_currentGoblin].x;
+ _vm->_map->_curGoblinY = _gobPositions[_currentGoblin].y;
+ }
+
+ _positionedGob = _currentGoblin;
+
+ gobDesc->animation =
+ gobDesc->stateMach[gobDesc->state][gobDesc->stateColumn]->
+ animation;
+
+ _gobStateLayer =
+ gobDesc->stateMach[gobDesc->state][gobDesc->stateColumn]->layer;
+
+ moveInitStep(framesCount, action, cont, gobDesc, &gobIndex,
+ &nextAct);
+ moveTreatRopeStairs(gobDesc);
+ moveAdvance(gobDesc, nextAct, framesCount);
+
+ return gobIndex;
+}
+
+void Goblin::freeObjects(void) {
+ int16 i;
+ int16 state;
+ int16 col;
+
+ for (i = 0; i < 16; i++) {
+ if (_soundData[i] == 0)
+ continue;
+
+ _vm->_snd->freeSoundData(_soundData[i]);
+ _soundData[i] = 0;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (_goblins[i] == 0)
+ continue;
+
+ _goblins[i]->stateMach = _goblins[i]->realStateMach;
+
+ for (state = 0; state < 40; state++) {
+ for (col = 0; col < 6; col++) {
+ delete _goblins[i]->stateMach[state][col];
+ _goblins[i]->stateMach[state][col] = 0;
+ }
+ }
+
+ if (i == 3) {
+ for (state = 40; state < 70; state++) {
+ delete _goblins[3]->stateMach[state][0];
+ _goblins[3]->stateMach[state][0] = 0;
+ }
+ }
+
+ delete[] _goblins[i]->stateMach;
+ delete _goblins[i];
+ _goblins[i] = 0;
+ }
+
+ for (i = 0; i < 20; i++) {
+ if (_objects[i] == 0)
+ continue;
+
+ _objects[i]->stateMach = _objects[i]->realStateMach;
+
+ for (state = 0; state < 40; state++) {
+ for (col = 0; col < 6; col++) {
+ delete _objects[i]->stateMach[state][col];
+ _objects[i]->stateMach[state][col] = 0;
+ }
+ }
+
+ delete[] _objects[i]->stateMach;
+ delete _objects[i];
+ _objects[i] = 0;
+ }
+}
+
+void Goblin::zeroObjects(void) {
+ int16 i;
+
+ for (i = 0; i < 4; i++)
+ _goblins[i] = 0;
+
+ for (i = 0; i < 20; i++)
+ _objects[i] = 0;
+
+ for (i = 0; i < 16; i++)
+ _soundData[i] = 0;
+}
+
+void Goblin::freeAllObjects(void) {
+ _vm->_util->deleteList(_objList);
+ freeObjects();
+}
+
+void Goblin::loadObjects(char *source) {
+ int16 i;
+
+ zeroObjects();
+ for (i = 0; i < 20; i++)
+ _itemToObject[i] = 100;
+
+ freeObjects();
+ initList();
+ strcpy(_vm->_map->_sourceFile, source);
+
+ _vm->_map->_sourceFile[strlen(_vm->_map->_sourceFile) - 4] = 0;
+ _vm->_map->loadMapObjects(source);
+
+ for (i = 0; i < _gobsCount; i++)
+ placeObject(_goblins[i], 0);
+
+ for (i = 0; i < _objCount; i++) {
+ placeObject(_objects[i], 1);
+ }
+
+ initVarPointers();
+ _actDestItemDesc = 0;
+}
+
+void Goblin::saveGobDataToVars(int16 xPos, int16 yPos, int16 someVal) {
+ Gob_Object *obj;
+ *_some0ValPtr = someVal;
+ *_curGobXPosVarPtr = xPos;
+ *_curGobYPosVarPtr = yPos;
+ *_itemInPocketVarPtr = _itemIndInPocket;
+
+ obj = _goblins[_currentGoblin];
+
+ *_curGobStateVarPtr = obj->state;
+ *_curGobFrameVarPtr = obj->curFrame;
+ *_curGobMultStateVarPtr = obj->multState;
+ *_curGobNextStateVarPtr = obj->nextState;
+ *_curGobScrXVarPtr = obj->xPos;
+ *_curGobScrYVarPtr = obj->yPos;
+ *_curGobLeftVarPtr = obj->left;
+ *_curGobTopVarPtr = obj->top;
+ *_curGobRightVarPtr = obj->right;
+ *_curGobBottomVarPtr = obj->bottom;
+ *_curGobDoAnimVarPtr = obj->doAnim;
+ *_curGobOrderVarPtr = obj->order;
+ *_curGobNoTickVarPtr = obj->noTick;
+ *_curGobTypeVarPtr = obj->type;
+ *_curGobMaxTickVarPtr = obj->maxTick;
+ *_curGobTickVarPtr = obj->tick;
+ *_curGobActStartStateVarPtr = obj->actionStartState;
+ *_curGobLookDirVarPtr = obj->curLookDir;
+ *_curGobPickableVarPtr = obj->pickable;
+ *_curGobRelaxVarPtr = obj->relaxTime;
+ *_curGobMaxFrameVarPtr = getObjMaxFrame(obj);
+
+ if (_actDestItemDesc == 0)
+ return;
+
+ obj = _actDestItemDesc;
+ *_destItemStateVarPtr = obj->state;
+ *_destItemFrameVarPtr = obj->curFrame;
+ *_destItemMultStateVarPtr = obj->multState;
+ *_destItemNextStateVarPtr = obj->nextState;
+ *_destItemScrXVarPtr = obj->xPos;
+ *_destItemScrYVarPtr = obj->yPos;
+ *_destItemLeftVarPtr = obj->left;
+ *_destItemTopVarPtr = obj->top;
+ *_destItemRightVarPtr = obj->right;
+ *_destItemBottomVarPtr = obj->bottom;
+ *_destItemDoAnimVarPtr = obj->doAnim;
+ *_destItemOrderVarPtr = obj->order;
+ *_destItemNoTickVarPtr = obj->noTick;
+ *_destItemTypeVarPtr = obj->type;
+ *_destItemMaxTickVarPtr = obj->maxTick;
+ *_destItemTickVarPtr = obj->tick;
+ *_destItemActStartStVarPtr = obj->actionStartState;
+ *_destItemLookDirVarPtr = obj->curLookDir;
+ *_destItemPickableVarPtr = obj->pickable;
+ *_destItemRelaxVarPtr = obj->relaxTime;
+ *_destItemMaxFrameVarPtr = getObjMaxFrame(obj);
+
+ _destItemState = obj->state;
+ _destItemType = obj->type;
+}
+
+void Goblin::initVarPointers(void) {
+ _gobRetVarPtr = (int32 *)VAR_ADDRESS(59);
+ _curGobStateVarPtr = (int32 *)VAR_ADDRESS(60);
+ _curGobFrameVarPtr = (int32 *)VAR_ADDRESS(61);
+ _curGobMultStateVarPtr = (int32 *)VAR_ADDRESS(62);
+ _curGobNextStateVarPtr = (int32 *)VAR_ADDRESS(63);
+ _curGobScrXVarPtr = (int32 *)VAR_ADDRESS(64);
+ _curGobScrYVarPtr = (int32 *)VAR_ADDRESS(65);
+ _curGobLeftVarPtr = (int32 *)VAR_ADDRESS(66);
+ _curGobTopVarPtr = (int32 *)VAR_ADDRESS(67);
+ _curGobRightVarPtr = (int32 *)VAR_ADDRESS(68);
+ _curGobBottomVarPtr = (int32 *)VAR_ADDRESS(69);
+ _curGobDoAnimVarPtr = (int32 *)VAR_ADDRESS(70);
+ _curGobOrderVarPtr = (int32 *)VAR_ADDRESS(71);
+ _curGobNoTickVarPtr = (int32 *)VAR_ADDRESS(72);
+ _curGobTypeVarPtr = (int32 *)VAR_ADDRESS(73);
+ _curGobMaxTickVarPtr = (int32 *)VAR_ADDRESS(74);
+ _curGobTickVarPtr = (int32 *)VAR_ADDRESS(75);
+ _curGobActStartStateVarPtr = (int32 *)VAR_ADDRESS(76);
+ _curGobLookDirVarPtr = (int32 *)VAR_ADDRESS(77);
+ _curGobPickableVarPtr = (int32 *)VAR_ADDRESS(80);
+ _curGobRelaxVarPtr = (int32 *)VAR_ADDRESS(81);
+ _destItemStateVarPtr = (int32 *)VAR_ADDRESS(82);
+ _destItemFrameVarPtr = (int32 *)VAR_ADDRESS(83);
+ _destItemMultStateVarPtr = (int32 *)VAR_ADDRESS(84);
+ _destItemNextStateVarPtr = (int32 *)VAR_ADDRESS(85);
+ _destItemScrXVarPtr = (int32 *)VAR_ADDRESS(86);
+ _destItemScrYVarPtr = (int32 *)VAR_ADDRESS(87);
+ _destItemLeftVarPtr = (int32 *)VAR_ADDRESS(88);
+ _destItemTopVarPtr = (int32 *)VAR_ADDRESS(89);
+ _destItemRightVarPtr = (int32 *)VAR_ADDRESS(90);
+ _destItemBottomVarPtr = (int32 *)VAR_ADDRESS(91);
+ _destItemDoAnimVarPtr = (int32 *)VAR_ADDRESS(92);
+ _destItemOrderVarPtr = (int32 *)VAR_ADDRESS(93);
+ _destItemNoTickVarPtr = (int32 *)VAR_ADDRESS(94);
+ _destItemTypeVarPtr = (int32 *)VAR_ADDRESS(95);
+ _destItemMaxTickVarPtr = (int32 *)VAR_ADDRESS(96);
+ _destItemTickVarPtr = (int32 *)VAR_ADDRESS(97);
+ _destItemActStartStVarPtr = (int32 *)VAR_ADDRESS(98);
+ _destItemLookDirVarPtr = (int32 *)VAR_ADDRESS(99);
+ _destItemPickableVarPtr = (int32 *)VAR_ADDRESS(102);
+ _destItemRelaxVarPtr = (int32 *)VAR_ADDRESS(103);
+ _destItemMaxFrameVarPtr = (int32 *)VAR_ADDRESS(105);
+ _curGobVarPtr = (int32 *)VAR_ADDRESS(106);
+ _some0ValPtr = (int32 *)VAR_ADDRESS(107);
+ _curGobXPosVarPtr = (int32 *)VAR_ADDRESS(108);
+ _curGobYPosVarPtr = (int32 *)VAR_ADDRESS(109);
+ _curGobMaxFrameVarPtr = (int32 *)VAR_ADDRESS(110);
+
+ _itemInPocketVarPtr = (int32 *)VAR_ADDRESS(114);
+
+ *_itemInPocketVarPtr = -2;
+}
+
+void Goblin::loadGobDataFromVars(void) {
+ Gob_Object *obj;
+
+ _itemIndInPocket = *_itemInPocketVarPtr;
+
+ obj = _goblins[_currentGoblin];
+
+ obj->state = *_curGobStateVarPtr;
+ obj->curFrame = *_curGobFrameVarPtr;
+ obj->multState = *_curGobMultStateVarPtr;
+ obj->nextState = *_curGobNextStateVarPtr;
+ obj->xPos = *_curGobScrXVarPtr;
+ obj->yPos = *_curGobScrYVarPtr;
+ obj->left = *_curGobLeftVarPtr;
+ obj->top = *_curGobTopVarPtr;
+ obj->right = *_curGobRightVarPtr;
+ obj->bottom = *_curGobBottomVarPtr;
+ obj->doAnim = *_curGobDoAnimVarPtr;
+ obj->order = *_curGobOrderVarPtr;
+ obj->noTick = *_curGobNoTickVarPtr;
+ obj->type = *_curGobTypeVarPtr;
+ obj->maxTick = *_curGobMaxTickVarPtr;
+ obj->tick = *_curGobTickVarPtr;
+ obj->actionStartState = *_curGobActStartStateVarPtr;
+ obj->curLookDir = *_curGobLookDirVarPtr;
+ obj->pickable = *_curGobPickableVarPtr;
+ obj->relaxTime = *_curGobRelaxVarPtr;
+
+ if (_actDestItemDesc == 0)
+ return;
+
+ obj = _actDestItemDesc;
+
+ obj->state = *_destItemStateVarPtr;
+ obj->curFrame = *_destItemFrameVarPtr;
+ obj->multState = *_destItemMultStateVarPtr;
+ obj->nextState = *_destItemNextStateVarPtr;
+ obj->xPos = *_destItemScrXVarPtr;
+ obj->yPos = *_destItemScrYVarPtr;
+ obj->left = *_destItemLeftVarPtr;
+ obj->top = *_destItemTopVarPtr;
+ obj->right = *_destItemRightVarPtr;
+ obj->bottom = *_destItemBottomVarPtr;
+ obj->doAnim = *_destItemDoAnimVarPtr;
+ obj->order = *_destItemOrderVarPtr;
+ obj->noTick = *_destItemNoTickVarPtr;
+ obj->type = *_destItemTypeVarPtr;
+ obj->maxTick = *_destItemMaxTickVarPtr;
+ obj->tick = *_destItemTickVarPtr;
+ obj->actionStartState = *_destItemActStartStVarPtr;
+ obj->curLookDir = *_destItemLookDirVarPtr;
+ obj->pickable = *_destItemPickableVarPtr;
+ obj->relaxTime = *_destItemRelaxVarPtr;
+
+ if (obj->type != _destItemType)
+ obj->toRedraw = 1;
+
+ if (obj->state != _destItemState && obj->type == 0)
+ obj->toRedraw = 1;
+}
+
+void Goblin::pickItem(int16 indexToPocket, int16 idToPocket) {
+ int16 x;
+ int16 y;
+
+ if (_objects[indexToPocket]->pickable != 1)
+ return;
+
+ _objects[indexToPocket]->type = 3;
+
+ _itemIndInPocket = indexToPocket;
+ _itemIdInPocket = idToPocket;
+
+ for (y = 0; y < Map::kMapHeight; y++) {
+ for (x = 0; x < Map::kMapWidth; x++) {
+ if (_itemByteFlag == 1) {
+ if (((_vm->_map->_itemsMap[y][x] & 0xff00) >> 8) ==
+ idToPocket)
+ _vm->_map->_itemsMap[y][x] &= 0xff;
+ } else {
+ if ((_vm->_map->_itemsMap[y][x] & 0xff) == idToPocket)
+ _vm->_map->_itemsMap[y][x] &= 0xff00;
+ }
+ }
+ }
+
+ if (idToPocket >= 0 && idToPocket < 20) {
+ _vm->_map->_itemPoses[_itemIdInPocket].x = 0;
+ _vm->_map->_itemPoses[_itemIdInPocket].y = 0;
+ _vm->_map->_itemPoses[_itemIdInPocket].orient = 0;
+ }
+}
+
+void Goblin::placeItem(int16 indexInPocket, int16 idInPocket) {
+ Gob_Object *itemDesc;
+ int16 lookDir;
+ int16 xPos;
+ int16 yPos;
+ int16 layer;
+
+ itemDesc = _objects[indexInPocket];
+ lookDir = _goblins[0]->curLookDir & 4;
+
+ xPos = _gobPositions[0].x;
+ yPos = _gobPositions[0].y;
+
+ _itemIndInPocket = -1;
+ _itemIdInPocket = 0;
+
+ itemDesc->pickable = 1;
+ itemDesc->type = 0;
+ itemDesc->toRedraw = 1;
+ itemDesc->curFrame = 0;
+ itemDesc->order = _goblins[0]->order;
+ itemDesc->animation =
+ itemDesc->stateMach[itemDesc->state][0]->animation;
+ layer =
+ itemDesc->stateMach[itemDesc->state][itemDesc->stateColumn]->layer;
+
+ _vm->_scenery->updateAnim(layer, 0, itemDesc->animation, 0,
+ itemDesc->xPos, itemDesc->yPos, 0);
+
+ itemDesc->yPos +=
+ (_gobPositions[0].y * 6) + 5 - _vm->_scenery->_toRedrawBottom;
+
+ if (lookDir == 4) {
+ itemDesc->xPos += (_gobPositions[0].x * 12 + 14)
+ - (_vm->_scenery->_toRedrawLeft + _vm->_scenery->_toRedrawRight) / 2;
+ } else {
+ itemDesc->xPos += (_gobPositions[0].x * 12)
+ - (_vm->_scenery->_toRedrawLeft + _vm->_scenery->_toRedrawRight) / 2;
+ }
+
+ _vm->_map->placeItem(xPos, yPos, idInPocket);
+
+ if (yPos > 0) {
+ _vm->_map->placeItem(xPos, yPos - 1, idInPocket);
+ }
+
+ if (lookDir == 4) {
+ if (xPos < Map::kMapWidth - 1) {
+ _vm->_map->placeItem(xPos + 1, yPos, idInPocket);
+
+ if (yPos > 0) {
+ _vm->_map->placeItem(xPos + 1, yPos - 1, idInPocket);
+ }
+ }
+ } else {
+ if (xPos > 0) {
+ _vm->_map->placeItem(xPos - 1, yPos, idInPocket);
+
+ if (yPos > 0) {
+ _vm->_map->placeItem(xPos - 1, yPos - 1, idInPocket);
+ }
+ }
+ }
+
+ if (idInPocket >= 0 && idInPocket < 20) {
+ _vm->_map->_itemPoses[idInPocket].x = _gobPositions[0].x;
+ _vm->_map->_itemPoses[idInPocket].y = _gobPositions[0].y;
+ _vm->_map->_itemPoses[idInPocket].orient = lookDir;
+ if (_vm->_map->_itemPoses[idInPocket].orient == 0) {
+// _vm->_map->_itemPoses[idInPocket].x++;
+ if (_vm->_map->_passMap[(int)_vm->_map->_itemPoses[idInPocket].y][_vm->_map->_itemPoses[idInPocket].x + 1] == 1)
+ _vm->_map->_itemPoses[idInPocket].x++;
+ } else {
+ if (_vm->_map->_passMap[(int)_vm->_map->_itemPoses[idInPocket].y][_vm->_map->_itemPoses[idInPocket].x - 1] == 1)
+ _vm->_map->_itemPoses[idInPocket].x--;
+ }
+ }
+}
+
+void Goblin::swapItems(int16 indexToPick, int16 idToPick) {
+ int16 layer;
+ Gob_Object *pickObj;
+ Gob_Object *placeObj;
+ int16 idToPlace;
+ int16 x;
+ int16 y;
+
+ pickObj = _objects[indexToPick];
+ placeObj = _objects[_itemIndInPocket];
+
+ idToPlace = _itemIdInPocket;
+ pickObj->type = 3;
+ _itemIndInPocket = indexToPick;
+ _itemIdInPocket = idToPick;
+
+ if (_itemByteFlag == 0) {
+ for (y = 0; y < Map::kMapHeight; y++) {
+ for (x = 0; x < Map::kMapWidth; x++) {
+ if ((_vm->_map->_itemsMap[y][x] & 0xff) == idToPick)
+ _vm->_map->_itemsMap[y][x] =
+ (_vm->_map->_itemsMap[y][x] & 0xff00) +
+ idToPlace;
+ }
+ }
+ } else {
+
+ for (y = 0; y < Map::kMapHeight; y++) {
+ for (x = 0; x < Map::kMapWidth; x++) {
+ if (((_vm->_map->_itemsMap[y][x] & 0xff00) >> 8) ==
+ idToPick)
+ _vm->_map->_itemsMap[y][x] =
+ (_vm->_map->_itemsMap[y][x] & 0xff) +
+ (idToPlace << 8);
+ }
+ }
+ }
+
+ if (idToPick >= 0 && idToPick < 20) {
+ _vm->_map->_itemPoses[idToPlace].x =
+ _vm->_map->_itemPoses[_itemIdInPocket].x;
+ _vm->_map->_itemPoses[idToPlace].y =
+ _vm->_map->_itemPoses[_itemIdInPocket].y;
+ _vm->_map->_itemPoses[idToPlace].orient =
+ _vm->_map->_itemPoses[_itemIdInPocket].orient;
+
+ _vm->_map->_itemPoses[_itemIdInPocket].x = 0;
+ _vm->_map->_itemPoses[_itemIdInPocket].y = 0;
+ _vm->_map->_itemPoses[_itemIdInPocket].orient = 0;
+ }
+
+ _itemIndInPocket = -1;
+ _itemIdInPocket = 0;
+
+ placeObj->type = 0;
+ placeObj->nextState = -1;
+ placeObj->multState = -1;
+ placeObj->unk14 = 0;
+ placeObj->toRedraw = 1;
+ placeObj->curFrame = 0;
+ placeObj->order = _goblins[0]->order;
+
+ placeObj->animation =
+ placeObj->stateMach[placeObj->state][0]->animation;
+
+ layer =
+ placeObj->stateMach[placeObj->state][placeObj->stateColumn]->layer;
+ _vm->_scenery->updateAnim(layer, 0, placeObj->animation, 0, placeObj->xPos,
+ placeObj->yPos, 0);
+
+ placeObj->yPos +=
+ (_gobPositions[0].y * 6) + 5 - _vm->_scenery->_toRedrawBottom;
+
+ if (_vm->_map->_itemPoses[idToPlace].orient == 4) {
+ placeObj->xPos += (_gobPositions[0].x * 12 + 14)
+ - (_vm->_scenery->_toRedrawLeft + _vm->_scenery->_toRedrawRight) / 2;
+ } else {
+ placeObj->xPos += (_gobPositions[0].x * 12)
+ - (_vm->_scenery->_toRedrawLeft + _vm->_scenery->_toRedrawRight) / 2;
+ }
+}
+
+void Goblin::treatItemPick(int16 itemId) {
+ int16 itemIndex;
+ Gob_Object *gobDesc;
+
+ gobDesc = _goblins[_currentGoblin];
+
+ if (gobDesc->curFrame != 9)
+ return;
+
+ if (gobDesc->stateMach != gobDesc->realStateMach)
+ return;
+
+ _readyToAct = 0;
+ _goesAtTarget = 0;
+
+ itemIndex = _itemToObject[itemId];
+ if (itemId != 0 && itemIndex != -1
+ && _objects[itemIndex]->pickable != 1)
+ itemIndex = -1;
+
+ if (_itemIndInPocket != -1 && _itemIndInPocket == itemIndex)
+ itemIndex = -1;
+
+ if (_itemIndInPocket != -1 && itemIndex != -1
+ && _objects[itemIndex]->pickable == 1) {
+ swapItems(itemIndex, itemId);
+ _itemIndInPocket = itemIndex;
+ _itemIdInPocket = itemId;
+ return;
+ }
+
+ if (_itemIndInPocket != -1 && itemIndex == -1) {
+ placeItem(_itemIndInPocket, _itemIdInPocket);
+ return;
+ }
+
+ if (_itemIndInPocket == -1 && itemIndex != -1) {
+ pickItem(itemIndex, itemId);
+ return;
+ }
+}
+
+int16 Goblin::treatItem(int16 action) {
+ int16 state;
+
+ state = _goblins[_currentGoblin]->state;
+ if ((state == 10 || state == 11) &&
+ _goblins[_currentGoblin]->curFrame == 0) {
+ _readyToAct = 0;
+ }
+
+ if (action == 3 && _currentGoblin == 0 &&
+ (state == 10 || state == 11) && _goblins[0]->curFrame == 0) {
+ saveGobDataToVars(_gobPositions[_currentGoblin].x,
+ _gobPositions[_currentGoblin].y, 0);
+ _goesAtTarget = 1;
+ return -1;
+ }
+
+ if (_noPick == 0 && _currentGoblin == 0 &&
+ (state == 10 || state == 11)) {
+ treatItemPick(_destActionItem);
+
+ saveGobDataToVars(_gobPositions[_currentGoblin].x,
+ _gobPositions[_currentGoblin].y, 0);
+ return 0;
+ }
+
+ if (_goesAtTarget == 0) {
+ saveGobDataToVars(_gobPositions[_currentGoblin].x,
+ _gobPositions[_currentGoblin].y, 0);
+ return 0;
+ } else {
+
+ if (_itemToObject[_destActionItem] != 100 &&
+ _destActionItem != 0) {
+
+ if (_itemToObject[_destActionItem] == -1) {
+ _actDestItemDesc = 0;
+ } else {
+ _actDestItemDesc =
+ _objects[_itemToObject
+ [_destActionItem]];
+ }
+ }
+
+ _goesAtTarget = 0;
+ saveGobDataToVars(_gobPositions[_currentGoblin].x,
+ _gobPositions[_currentGoblin].y, 0);
+ return _destActionItem;
+ }
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/goblin.h b/engines/gob/goblin.h
new file mode 100644
index 0000000000..858bf247ae
--- /dev/null
+++ b/engines/gob/goblin.h
@@ -0,0 +1,231 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_GOBLIN_H
+#define GOB_GOBLIN_H
+
+#include "gob/util.h"
+#include "gob/sound.h"
+
+namespace Gob {
+
+#define TYPE_USUAL 0
+#define TYPE_AMORPHOUS 1
+#define TYPE_MOBILE 3
+
+class Goblin {
+public:
+#pragma START_PACK_STRUCTS
+ struct Gob_State {
+ int16 animation;// +0h
+ int16 layer; // +2h
+ int16 unk0; // +4h
+ int16 unk1; // +6h
+ int16 sndItem; // +8h, high/low byte - sound sample index
+ int16 freq; // +Ah, high/low byte * 100 - frequency
+ int16 repCount; // +Ch high/low byte - repeat count
+ int16 sndFrame; // +Eh
+ } GCC_PACK;
+
+ typedef Gob_State *Gob_PState;
+
+ typedef Gob_PState Gob_StateLine[6];
+
+ struct Gob_Object {
+ int16 animation; // +0h
+ int16 state; // +2h
+ int16 stateColumn; // +4h
+ int16 curFrame; // +6h
+ int16 xPos; // +8h
+ int16 yPos; // +Ah
+ int16 dirtyLeft; // +Ch
+ int16 dirtyTop; // +Eh
+ int16 dirtyRight; // +10h
+ int16 dirtyBottom; // +12h
+ int16 left; // +14h
+ int16 top; // +16h
+ int16 right; // +18h
+ int16 bottom; // +1ah
+ int16 nextState; // +1ch
+ int16 multState; // +1eh
+ int16 actionStartState; // +20h
+ int16 curLookDir; // +22h
+ int16 pickable; // +24h
+ int16 relaxTime; // +26h
+ Gob_StateLine *stateMach; // +28h
+ Gob_StateLine *realStateMach; // +2ch
+ char doAnim; // +30h
+ char order; // +31h
+ char noTick; // +32h
+ char toRedraw; // +33h
+ char type; // +34h
+ char maxTick; // +35h
+ char tick; // +36h
+ char multObjIndex; // +37h, from which play mult animations
+ char unk14; // +38h
+ char visible; // +39h
+ } GCC_PACK;
+
+ struct Gob_Pos {
+ char x;
+ char y;
+ } GCC_PACK;
+#pragma END_PACK_STRUCTS
+
+ Util::List *_objList;
+ Gob_Object *_goblins[4];
+ int16 _currentGoblin;
+ Snd::SoundDesc *_soundData[16];
+ int16 _gobStateLayer;
+ char _goesAtTarget;
+ char _readyToAct;
+ int16 _gobAction; // 0 - move, 3 - do action, 4 - pick
+ // goblins 0 - picker, 1 - fighter, 2 - mage
+ Gob_Pos _gobPositions[3];
+ int16 _gobDestX;
+ int16 _gobDestY;
+ int16 _pressedMapX;
+ int16 _pressedMapY;
+ char _pathExistence;
+
+ // Pointers to interpreter variables
+ int32 *_some0ValPtr;
+
+ int32 *_gobRetVarPtr;
+ int32 *_curGobVarPtr;
+ int32 *_curGobXPosVarPtr;
+ int32 *_curGobYPosVarPtr;
+ int32 *_itemInPocketVarPtr;
+
+ int32 *_curGobStateVarPtr;
+ int32 *_curGobFrameVarPtr;
+ int32 *_curGobMultStateVarPtr;
+ int32 *_curGobNextStateVarPtr;
+ int32 *_curGobScrXVarPtr;
+ int32 *_curGobScrYVarPtr;
+ int32 *_curGobLeftVarPtr;
+ int32 *_curGobTopVarPtr;
+ int32 *_curGobRightVarPtr;
+ int32 *_curGobBottomVarPtr;
+ int32 *_curGobDoAnimVarPtr;
+ int32 *_curGobOrderVarPtr;
+ int32 *_curGobNoTickVarPtr;
+ int32 *_curGobTypeVarPtr;
+ int32 *_curGobMaxTickVarPtr;
+ int32 *_curGobTickVarPtr;
+ int32 *_curGobActStartStateVarPtr;
+ int32 *_curGobLookDirVarPtr;
+ int32 *_curGobPickableVarPtr;
+ int32 *_curGobRelaxVarPtr;
+ int32 *_curGobMaxFrameVarPtr;
+
+ int32 *_destItemStateVarPtr;
+ int32 *_destItemFrameVarPtr;
+ int32 *_destItemMultStateVarPtr;
+ int32 *_destItemNextStateVarPtr;
+ int32 *_destItemScrXVarPtr;
+ int32 *_destItemScrYVarPtr;
+ int32 *_destItemLeftVarPtr;
+ int32 *_destItemTopVarPtr;
+ int32 *_destItemRightVarPtr;
+ int32 *_destItemBottomVarPtr;
+ int32 *_destItemDoAnimVarPtr;
+ int32 *_destItemOrderVarPtr;
+ int32 *_destItemNoTickVarPtr;
+ int32 *_destItemTypeVarPtr;
+ int32 *_destItemMaxTickVarPtr;
+ int32 *_destItemTickVarPtr;
+ int32 *_destItemActStartStVarPtr;
+ int32 *_destItemLookDirVarPtr;
+ int32 *_destItemPickableVarPtr;
+ int32 *_destItemRelaxVarPtr;
+ int32 *_destItemMaxFrameVarPtr;
+
+ int16 _destItemType;
+ int16 _destItemState;
+ int16 _itemToObject[20];
+ Gob_Object *_objects[20];
+ int16 _objCount;
+ int16 _gobsCount;
+ int16 _itemIndInPocket;
+ int16 _itemIdInPocket;
+ char _itemByteFlag;
+ int16 _destItemId;
+ int16 _destActionItem;
+ Gob_Object *_actDestItemDesc;
+ int16 _forceNextState[10];
+ char _boreCounter;
+ int16 _positionedGob;
+ char _noPick;
+
+ // Functions
+ char rotateState(int16 from, int16 to);
+ void playSound(Snd::SoundDesc * snd, int16 repCount, int16 freq);
+ void drawObjects(void);
+ void animateObjects(void);
+ void placeObject(Gob_Object * objDesc, char animated);
+ int16 getObjMaxFrame(Gob_Object * obj);
+ int16 objIntersected(Gob_Object * obj1, Gob_Object * obj2);
+ void setMultStates(Gob_Object * gobDesc);
+ int16 nextLayer(Gob_Object * gobDesc);
+ void showBoredom(int16 gobIndex);
+ void switchGoblin(int16 index);
+ void freeObjects(void);
+ void zeroObjects(void);
+ void freeAllObjects(void);
+ void loadObjects(char *source);
+ void initVarPointers(void);
+ void saveGobDataToVars(int16 xPos, int16 yPos, int16 someVal);
+ void loadGobDataFromVars(void);
+ void pickItem(int16 indexToPocket, int16 idToPocket);
+ void placeItem(int16 indexInPocket, int16 idInPocket);
+ void swapItems(int16 indexToPick, int16 idToPick);
+ void treatItemPick(int16 itemId);
+ int16 treatItem(int16 action);
+ int16 doMove(Gob_Object *gobDesc, int16 cont, int16 action);
+
+ Goblin(GobEngine *vm);
+
+protected:
+ int16 _rotStates[4][4];
+ GobEngine *_vm;
+
+ int16 peekGoblin(Gob_Object *curGob);
+ void initList(void);
+ void sortByOrder(Util::List *list);
+ void adjustDest(int16 posX, int16 posY);
+ void adjustTarget(void);
+ void targetDummyItem(Gob_Object *gobDesc);
+ void targetItem(void);
+ void initiateMove(void);
+ void moveFindItem(int16 posX, int16 posY);
+ void moveCheckSelect(int16 framesCount, Gob_Object * gobDesc, int16 *pGobIndex, int16 *nextAct);
+ void moveInitStep(int16 framesCount, int16 action, int16 cont,
+ Gob_Object *gobDesc, int16 *pGobIndex, int16 *pNextAct);
+ void moveTreatRopeStairs(Gob_Object *gobDesc);
+ void movePathFind(Gob_Object *gobDesc, int16 nextAct);
+ void moveAdvance(Gob_Object *gobDesc, int16 nextAct, int16 framesCount);
+};
+
+} // End of namespace Gob
+
+#endif /* __GOBLIN_H */
diff --git a/engines/gob/init.cpp b/engines/gob/init.cpp
new file mode 100644
index 0000000000..43b6344499
--- /dev/null
+++ b/engines/gob/init.cpp
@@ -0,0 +1,272 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/dataio.h"
+#include "gob/global.h"
+#include "gob/init.h"
+#include "gob/video.h"
+#include "gob/sound.h"
+#include "gob/timer.h"
+#include "gob/sound.h"
+#include "gob/game.h"
+#include "gob/draw.h"
+#include "gob/util.h"
+#include "gob/cdrom.h"
+
+namespace Gob {
+
+void game_start(void);
+
+const char *Init::_fontNames[] = { "jeulet1.let", "jeulet2.let", "jeucar1.let", "jeumath.let" };
+
+Init::Init(GobEngine *vm) : _vm(vm) {
+ _palDesc = 0;
+}
+
+void Init::findBestCfg(void) {
+ _vm->_global->_videoMode = VIDMODE_VGA;
+ _vm->_global->_useMouse = _vm->_global->_mousePresent;
+ if (_vm->_global->_presentSound & BLASTER_FLAG)
+ _vm->_global->_soundFlags = BLASTER_FLAG | SPEAKER_FLAG | MIDI_FLAG;
+ else if (_vm->_global->_presentSound & PROAUDIO_FLAG)
+ _vm->_global->_soundFlags = PROAUDIO_FLAG | SPEAKER_FLAG | MIDI_FLAG;
+ else if (_vm->_global->_presentSound & ADLIB_FLAG)
+ _vm->_global->_soundFlags = ADLIB_FLAG | SPEAKER_FLAG | MIDI_FLAG;
+ else if (_vm->_global->_presentSound & INTERSOUND_FLAG)
+ _vm->_global->_soundFlags = INTERSOUND_FLAG | SPEAKER_FLAG;
+ else if (_vm->_global->_presentSound & SPEAKER_FLAG)
+ _vm->_global->_soundFlags = SPEAKER_FLAG;
+ else
+ _vm->_global->_soundFlags = 0;
+}
+
+void Init::soundVideo(int32 smallHeap, int16 flag) {
+ if (_vm->_global->_videoMode != 0x13 && _vm->_global->_videoMode != 0)
+ error("soundVideo: Video mode 0x%x is not supported!",
+ _vm->_global->_videoMode);
+
+ //if ((flag & 4) == 0)
+ // _vm->_video->findVideo();
+
+ _vm->_global->_mousePresent = 1;
+
+ _vm->_global->_inVM = 0;
+
+ _vm->_global->_presentSound = 0; // FIXME: sound is not supported yet
+
+ _vm->_global->_sprAllocated = 0;
+ _vm->_gtimer->enableTimer();
+
+ // _vm->_snd->setResetTimerFlag(debugFlag); // TODO
+
+ if (_vm->_global->_videoMode == 0x13)
+ _vm->_global->_colorCount = 256;
+
+ _vm->_global->_pPaletteDesc = &_vm->_global->_paletteStruct;
+ _vm->_global->_pPaletteDesc->vgaPal = _vm->_draw->_vgaPalette;
+ _vm->_global->_pPaletteDesc->unused1 = _vm->_global->_unusedPalette1;
+ _vm->_global->_pPaletteDesc->unused2 = _vm->_global->_unusedPalette2;
+ _vm->_global->_pPrimarySurfDesc = &_vm->_global->_primarySurfDesc;
+
+ if (_vm->_global->_videoMode != 0)
+ _vm->_video->initSurfDesc(_vm->_global->_videoMode, 320, 200, PRIMARY_SURFACE);
+
+ if (_vm->_global->_soundFlags & MIDI_FLAG) {
+ _vm->_global->_soundFlags &= _vm->_global->_presentSound;
+ if (_vm->_global->_presentSound & ADLIB_FLAG)
+ _vm->_global->_soundFlags |= MIDI_FLAG;
+ } else {
+ _vm->_global->_soundFlags &= _vm->_global->_presentSound;
+ }
+}
+
+void Init::cleanup(void) {
+ if (_vm->_global->_debugFlag == 0)
+ _vm->_gtimer->disableTimer();
+
+ _vm->_video->freeDriver();
+ _vm->_video->freeSurfDesc(_vm->_global->_pPrimarySurfDesc);
+ _vm->_global->_pPrimarySurfDesc = 0;
+
+ if (_vm->_snd->_cleanupFunc != 0 && _vm->_snd->_playingSound != 0) {
+ (*_vm->_snd->_cleanupFunc) (0);
+ _vm->_snd->_cleanupFunc = 0;
+ }
+ _vm->_snd->speakerOff();
+
+ _vm->_dataio->closeDataFile();
+
+ if (_vm->_global->_sprAllocated != 0)
+ error("cleanup: Error! Allocated sprites left: %d",
+ _vm->_global->_sprAllocated);
+
+ _vm->_snd->stopSound(0);
+ _vm->_util->keyboard_release();
+ g_system->quit();
+}
+
+void Init::initGame(char *totName) {
+ int16 handle2;
+ int16 i;
+ int16 handle;
+ char *infBuf;
+ char *infPtr;
+ char *infEnd;
+ int16 j;
+ char buffer[20];
+ int32 varsCount;
+/*
+src = byte ptr -2Eh
+var_1A = word ptr -1Ah
+var_18 = word ptr -18h
+var_16 = dword ptr -16h
+var_12 = word ptr -12h
+var_10 = word ptr -10h
+handle2 = word ptr -0Eh
+fileHandle = word ptr -0Ch
+numFromTot = word ptr -0Ah
+memAvail = dword ptr -6
+memBlocks = word ptr -2*/
+
+ _vm->_global->_disableVideoCfg = 0x11;
+ _vm->_global->_disableMouseCfg = 0x15;
+ soundVideo(1000, 1);
+
+ handle2 = _vm->_dataio->openData("intro.stk");
+ if (handle2 >= 0) {
+ _vm->_dataio->closeData(handle2);
+ _vm->_dataio->openDataFile("intro.stk");
+ }
+
+ _vm->_util->initInput();
+
+ _vm->_video->setHandlers();
+ _vm->_video->initPrimary(_vm->_global->_videoMode);
+ _vm->_global->_mouseXShift = 1;
+ _vm->_global->_mouseYShift = 1;
+
+ _vm->_game->_totTextData = 0;
+ _vm->_game->_totFileData = 0;
+ _vm->_game->_totResourceTable = 0;
+ _vm->_global->_inter_variables = 0;
+ _palDesc = new Video::PalDesc;
+
+ if (_vm->_global->_videoMode != 0x13)
+ error("initGame: Only 0x13 video mode is supported!");
+
+ _palDesc->vgaPal = _vm->_draw->_vgaPalette;
+ _palDesc->unused1 = _vm->_draw->_unusedPalette1;
+ _palDesc->unused2 = _vm->_draw->_unusedPalette2;
+ _vm->_video->setFullPalette(_palDesc);
+
+ for (i = 0; i < 4; i++)
+ _vm->_draw->_fonts[i] = 0;
+
+ handle = _vm->_dataio->openData("intro.inf");
+
+ if (handle < 0) {
+ for (i = 0; i < 4; i++) {
+ handle2 = _vm->_dataio->openData(_fontNames[i]);
+ if (handle2 >= 0) {
+ _vm->_dataio->closeData(handle2);
+ _vm->_draw->_fonts[i] =
+ _vm->_util->loadFont(_fontNames[i]);
+ }
+ }
+ } else {
+ _vm->_dataio->closeData(handle);
+
+ infPtr = _vm->_dataio->getData("intro.inf");
+ infBuf = infPtr;
+
+ infEnd = infBuf + _vm->_dataio->getDataSize("intro.inf");
+
+ for (i = 0; i < 4; i++, infPtr++) {
+ for (j = 0; *infPtr >= ' ' && infPtr != infEnd;
+ j++, infPtr++)
+ buffer[j] = *infPtr;
+
+ buffer[j] = 0;
+ strcat(buffer, ".let");
+ handle2 = _vm->_dataio->openData(buffer);
+ if (handle2 >= 0) {
+ _vm->_dataio->closeData(handle2);
+ _vm->_draw->_fonts[i] = _vm->_util->loadFont(buffer);
+ }
+
+ if (infPtr == infEnd)
+ break;
+
+ infPtr++;
+ if (infPtr == infEnd)
+ break;
+ }
+ delete[] infBuf;
+ }
+
+ if (totName != 0) {
+ strcpy(buffer, totName);
+ strcat(buffer, ".tot");
+ } else {
+ strcpy(buffer, "intro.tot");
+ }
+
+ handle = _vm->_dataio->openData(buffer);
+
+ if (handle >= 0) {
+ // Get variables count
+ _vm->_dataio->seekData(handle, 0x2c, SEEK_SET);
+ _vm->_dataio->readData(handle, (char *)&varsCount, 4);
+ varsCount = FROM_LE_32(varsCount);
+ _vm->_dataio->closeData(handle);
+
+ _vm->_global->_inter_variables = new char[varsCount * 4];
+ memset(_vm->_global->_inter_variables, 0, varsCount * 4);
+
+ strcpy(_vm->_game->_curTotFile, buffer);
+
+ _vm->_cdrom->testCD(1, "GOB");
+ _vm->_cdrom->readLIC("gob.lic");
+ _vm->_game->start();
+
+ _vm->_cdrom->stopPlaying();
+ _vm->_cdrom->freeLICbuffer();
+
+ delete[] _vm->_global->_inter_variables;
+ delete[] _vm->_game->_totFileData;
+ delete[] _vm->_game->_totTextData;
+ delete[] _vm->_game->_totResourceTable;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (_vm->_draw->_fonts[i] != 0)
+ _vm->_util->freeFont(_vm->_draw->_fonts[i]);
+ }
+
+ delete _palDesc;
+ _vm->_dataio->closeDataFile();
+ _vm->_video->initPrimary(-1);
+ cleanup();
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/init.h b/engines/gob/init.h
new file mode 100644
index 0000000000..62cbb44f63
--- /dev/null
+++ b/engines/gob/init.h
@@ -0,0 +1,47 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_INIT_H
+#define GOB_INIT_H
+
+namespace Gob {
+
+class Init {
+public:
+ void findBestCfg(void);
+ void soundVideo(int32 smallHeapSize, int16 flag);
+
+ void initGame(char *totFile);
+
+ Init(GobEngine *vm);
+
+protected:
+ Video::PalDesc *_palDesc;
+ static const char *_fontNames[4];
+ GobEngine *_vm;
+
+ void cleanup(void);
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/inter.cpp b/engines/gob/inter.cpp
new file mode 100644
index 0000000000..b17067411e
--- /dev/null
+++ b/engines/gob/inter.cpp
@@ -0,0 +1,443 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/inter.h"
+#include "gob/util.h"
+#include "gob/scenery.h"
+#include "gob/parse.h"
+#include "gob/game.h"
+#include "gob/draw.h"
+#include "gob/mult.h"
+#include "gob/goblin.h"
+#include "gob/cdrom.h"
+#include "gob/map.h"
+
+namespace Gob {
+
+Inter::Inter(GobEngine *vm) : _vm(vm) {
+ _terminate = false;
+ _breakFlag = false;
+ _animPalLowIndex = 0;
+ _animPalHighIndex = 0;
+ _animPalDir = 0;
+ _soundEndTimeKey = 0;
+ _soundStopVal = 0;
+ _breakFromLevel = 0;
+ _nestLevel = 0;
+}
+
+int16 Inter::load16(void) {
+ int16 tmp = (int16)READ_LE_UINT16(_vm->_global->_inter_execPtr);
+ _vm->_global->_inter_execPtr += 2;
+ return tmp;
+}
+
+char Inter::evalExpr(int16 *pRes) {
+ byte token;
+
+//
+ _vm->_parse->printExpr(99);
+
+ _vm->_parse->parseExpr(99, &token);
+ if (pRes == 0)
+ return token;
+
+ switch (token) {
+ case 20:
+ *pRes = _vm->_global->_inter_resVal;
+ break;
+
+ case 22:
+ case 23:
+ *pRes = 0;
+ break;
+
+ case 24:
+ *pRes = 1;
+ break;
+ }
+ return token;
+}
+
+char Inter::evalBoolResult() {
+ byte token;
+
+ _vm->_parse->printExpr(99);
+
+ _vm->_parse->parseExpr(99, &token);
+ if (token == 24 || (token == 20 && _vm->_global->_inter_resVal != 0))
+ return 1;
+ else
+ return 0;
+}
+
+void Inter::animPalette(void) {
+ int16 i;
+ Video::Color col;
+
+ if (_animPalDir == 0)
+ return;
+
+ _vm->_video->waitRetrace(_vm->_global->_videoMode);
+
+ if (_animPalDir == -1) {
+ col = _vm->_draw->_vgaSmallPalette[_animPalLowIndex];
+
+ for (i = _animPalLowIndex; i < _animPalHighIndex; i++)
+ _vm->_draw->_vgaSmallPalette[i] = _vm->_draw->_vgaSmallPalette[i + 1];
+
+ _vm->_draw->_vgaSmallPalette[_animPalHighIndex] = col;
+ } else {
+ col = _vm->_draw->_vgaSmallPalette[_animPalHighIndex];
+ for (i = _animPalHighIndex; i > _animPalLowIndex; i--)
+ _vm->_draw->_vgaSmallPalette[i] = _vm->_draw->_vgaSmallPalette[i - 1];
+
+ _vm->_draw->_vgaSmallPalette[_animPalLowIndex] = col;
+ }
+
+ _vm->_global->_pPaletteDesc->vgaPal = _vm->_draw->_vgaSmallPalette;
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+}
+
+void Inter::funcBlock(int16 retFlag) {
+ char cmdCount;
+ int16 counter;
+ byte cmd;
+ byte cmd2;
+
+ if (_vm->_global->_inter_execPtr == 0)
+ return;
+
+ _breakFlag = false;
+ _vm->_global->_inter_execPtr++;
+ cmdCount = *_vm->_global->_inter_execPtr++;
+ _vm->_global->_inter_execPtr += 2;
+
+ if (cmdCount == 0) {
+ _vm->_global->_inter_execPtr = 0;
+ return;
+ }
+
+ counter = 0;
+ do {
+ if (_terminate)
+ break;
+
+ cmd = (byte)*_vm->_global->_inter_execPtr;
+ if ((cmd >> 4) >= 12) {
+ cmd2 = 16 - (cmd >> 4);
+ cmd &= 0xf;
+ } else
+ cmd2 = 0;
+
+ _vm->_global->_inter_execPtr++;
+ counter++;
+
+// debug(4, "funcBlock(%d, %d)", cmd2, cmd);
+
+ if (cmd2 == 0)
+ cmd >>= 4;
+
+ if (executeFuncOpcode(cmd2, cmd, cmdCount, counter, retFlag))
+ return;
+
+ if (_breakFlag) {
+ if (retFlag != 2)
+ break;
+
+ if (*_breakFromLevel == -1)
+ _breakFlag = false;
+ break;
+ }
+ } while (counter != cmdCount);
+
+ _vm->_global->_inter_execPtr = 0;
+ return;
+}
+
+void Inter::storeKey(int16 key) {
+ WRITE_VAR(12, _vm->_util->getTimeKey() - _vm->_game->_startTimeKey);
+
+ WRITE_VAR(2, _vm->_global->_inter_mouseX);
+ WRITE_VAR(3, _vm->_global->_inter_mouseY);
+ WRITE_VAR(4, _vm->_game->_mouseButtons);
+ WRITE_VAR(1, _vm->_snd->_playingSound);
+
+ if (key == 0x4800)
+ key = 0x0b;
+ else if (key == 0x5000)
+ key = 0x0a;
+ else if (key == 0x4d00)
+ key = 0x09;
+ else if (key == 0x4b00)
+ key = 0x08;
+ else if (key == 0x011b)
+ key = 0x1b;
+ else if ((key & 0xff) != 0)
+ key &= 0xff;
+
+ WRITE_VAR(0, key);
+
+ if (key != 0)
+ _vm->_util->waitKey();
+}
+
+void Inter::checkSwitchTable(char **ppExec) {
+ int16 i;
+ int16 len;
+ char found;
+ int32 value;
+ char notFound;
+ char defFlag;
+
+ found = 0;
+ notFound = 1;
+ *ppExec = 0;
+ value = _vm->_parse->parseVarIndex();
+ value = VAR_OFFSET(value);
+
+ do {
+ len = *(int8*)_vm->_global->_inter_execPtr++; // must be a signed char typ and char is not default signed on all platforms.
+
+ if (len == -5)
+ break;
+
+ for (i = 0; i < len; i++) {
+ evalExpr(0);
+
+ if (_terminate)
+ return;
+
+ if (_vm->_global->_inter_resVal == value) {
+ found = 1;
+ notFound = 0;
+ }
+ }
+
+ if (found != 0)
+ *ppExec = _vm->_global->_inter_execPtr;
+
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr + 2) + 2;
+ found = 0;
+ } while (len != -5);
+
+ if (len != -5)
+ _vm->_global->_inter_execPtr++;
+
+ defFlag = *_vm->_global->_inter_execPtr;
+ defFlag >>= 4;
+ if (defFlag != 4)
+ return;
+ _vm->_global->_inter_execPtr++;
+
+ if (notFound)
+ *ppExec = _vm->_global->_inter_execPtr;
+
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr + 2) + 2;
+}
+
+void Inter::callSub(int16 retFlag) {
+ int16 block;
+ while (_vm->_global->_inter_execPtr != 0 && (char *)_vm->_global->_inter_execPtr != _vm->_game->_totFileData) {
+ block = *_vm->_global->_inter_execPtr;
+ if (block == 1) {
+ funcBlock(retFlag);
+ } else if (block == 2) {
+ _vm->_game->collisionsBlock();
+ }
+ }
+
+ if ((char *)_vm->_global->_inter_execPtr == _vm->_game->_totFileData)
+ _terminate = true;
+}
+
+void Inter::initControlVars(void) {
+ *_nestLevel = 0;
+ *_breakFromLevel = -1;
+
+ *_vm->_scenery->_pCaptureCounter = 0;
+
+ _breakFlag = false;
+ _terminate = false;
+ _animPalDir = 0;
+ _soundEndTimeKey = 0;
+}
+
+void Inter::renewTimeInVars(void) {
+ struct tm *t;
+ time_t now = time(NULL);
+
+ t = localtime(&now);
+
+ WRITE_VAR(5, 1900 + t->tm_year);
+ WRITE_VAR(6, t->tm_mon);
+ WRITE_VAR(7, 0);
+ WRITE_VAR(8, t->tm_mday);
+ WRITE_VAR(9, t->tm_hour);
+ WRITE_VAR(10, t->tm_min);
+ WRITE_VAR(11, t->tm_sec);
+}
+
+void Inter::manipulateMap(int16 xPos, int16 yPos, int16 item) {
+ for (int16 y = 0; y < Map::kMapHeight; y++) {
+ for (int16 x = 0; x < Map::kMapWidth; x++) {
+ if ((_vm->_map->_itemsMap[y][x] & 0xff) == item) {
+ _vm->_map->_itemsMap[y][x] &= 0xff00;
+ } else if (((_vm->_map->_itemsMap[y][x] & 0xff00) >> 8)
+ == item) {
+ _vm->_map->_itemsMap[y][x] &= 0xff;
+ }
+ }
+ }
+
+ if (xPos < Map::kMapWidth - 1) {
+ if (yPos > 0) {
+ if ((_vm->_map->_itemsMap[yPos][xPos] & 0xff00) != 0 ||
+ (_vm->_map->_itemsMap[yPos - 1][xPos] & 0xff00) !=
+ 0
+ || (_vm->_map->_itemsMap[yPos][xPos +
+ 1] & 0xff00) != 0
+ || (_vm->_map->_itemsMap[yPos - 1][xPos +
+ 1] & 0xff00) != 0) {
+
+ _vm->_map->_itemsMap[yPos][xPos] =
+ (_vm->_map->_itemsMap[yPos][xPos] & 0xff00)
+ + item;
+
+ _vm->_map->_itemsMap[yPos - 1][xPos] =
+ (_vm->_map->_itemsMap[yPos -
+ 1][xPos] & 0xff00) + item;
+
+ _vm->_map->_itemsMap[yPos][xPos + 1] =
+ (_vm->_map->_itemsMap[yPos][xPos +
+ 1] & 0xff00) + item;
+
+ _vm->_map->_itemsMap[yPos - 1][xPos + 1] =
+ (_vm->_map->_itemsMap[yPos - 1][xPos +
+ 1] & 0xff00) + item;
+ } else {
+ _vm->_map->_itemsMap[yPos][xPos] =
+ (_vm->_map->_itemsMap[yPos][xPos] & 0xff) +
+ (item << 8);
+
+ _vm->_map->_itemsMap[yPos - 1][xPos] =
+ (_vm->_map->_itemsMap[yPos -
+ 1][xPos] & 0xff) + (item << 8);
+
+ _vm->_map->_itemsMap[yPos][xPos + 1] =
+ (_vm->_map->_itemsMap[yPos][xPos +
+ 1] & 0xff) + (item << 8);
+
+ _vm->_map->_itemsMap[yPos - 1][xPos + 1] =
+ (_vm->_map->_itemsMap[yPos - 1][xPos +
+ 1] & 0xff) + (item << 8);
+ }
+ } else {
+ if ((_vm->_map->_itemsMap[yPos][xPos] & 0xff00) != 0 ||
+ (_vm->_map->_itemsMap[yPos][xPos + 1] & 0xff00) !=
+ 0) {
+ _vm->_map->_itemsMap[yPos][xPos] =
+ (_vm->_map->_itemsMap[yPos][xPos] & 0xff00)
+ + item;
+
+ _vm->_map->_itemsMap[yPos][xPos + 1] =
+ (_vm->_map->_itemsMap[yPos][xPos +
+ 1] & 0xff00) + item;
+ } else {
+ _vm->_map->_itemsMap[yPos][xPos] =
+ (_vm->_map->_itemsMap[yPos][xPos] & 0xff) +
+ (item << 8);
+
+ _vm->_map->_itemsMap[yPos][xPos + 1] =
+ (_vm->_map->_itemsMap[yPos][xPos +
+ 1] & 0xff) + (item << 8);
+ }
+ }
+ } else {
+ if (yPos > 0) {
+ if ((_vm->_map->_itemsMap[yPos][xPos] & 0xff00) != 0 ||
+ (_vm->_map->_itemsMap[yPos - 1][xPos] & 0xff00) !=
+ 0) {
+ _vm->_map->_itemsMap[yPos][xPos] =
+ (_vm->_map->_itemsMap[yPos][xPos] & 0xff00)
+ + item;
+
+ _vm->_map->_itemsMap[yPos - 1][xPos] =
+ (_vm->_map->_itemsMap[yPos -
+ 1][xPos] & 0xff00) + item;
+ } else {
+ _vm->_map->_itemsMap[yPos][xPos] =
+ (_vm->_map->_itemsMap[yPos][xPos] & 0xff) +
+ (item << 8);
+
+ _vm->_map->_itemsMap[yPos - 1][xPos] =
+ (_vm->_map->_itemsMap[yPos -
+ 1][xPos] & 0xff) + (item << 8);
+ }
+ } else {
+ if ((_vm->_map->_itemsMap[yPos][xPos] & 0xff00) != 0) {
+ _vm->_map->_itemsMap[yPos][xPos] =
+ (_vm->_map->_itemsMap[yPos][xPos] & 0xff00)
+ + item;
+ } else {
+ _vm->_map->_itemsMap[yPos][xPos] =
+ (_vm->_map->_itemsMap[yPos][xPos] & 0xff) +
+ (item << 8);
+ }
+ }
+ }
+
+ if (item < 0 || item >= 20)
+ return;
+
+ if (xPos > 1 && _vm->_map->_passMap[yPos][xPos - 2] == 1) {
+ _vm->_map->_itemPoses[item].x = xPos - 2;
+ _vm->_map->_itemPoses[item].y = yPos;
+ _vm->_map->_itemPoses[item].orient = 4;
+ return;
+ }
+
+ if (xPos < Map::kMapWidth - 2 && _vm->_map->_passMap[yPos][xPos + 2] == 1) {
+ _vm->_map->_itemPoses[item].x = xPos + 2;
+ _vm->_map->_itemPoses[item].y = yPos;
+ _vm->_map->_itemPoses[item].orient = 0;
+ return;
+ }
+
+ if (xPos < Map::kMapWidth - 1 && _vm->_map->_passMap[yPos][xPos + 1] == 1) {
+ _vm->_map->_itemPoses[item].x = xPos + 1;
+ _vm->_map->_itemPoses[item].y = yPos;
+ _vm->_map->_itemPoses[item].orient = 0;
+ return;
+ }
+
+ if (xPos > 0 && _vm->_map->_passMap[yPos][xPos - 1] == 1) {
+ _vm->_map->_itemPoses[item].x = xPos - 1;
+ _vm->_map->_itemPoses[item].y = yPos;
+ _vm->_map->_itemPoses[item].orient = 4;
+ return;
+ }
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/inter.h b/engines/gob/inter.h
new file mode 100644
index 0000000000..ed55dfbcc3
--- /dev/null
+++ b/engines/gob/inter.h
@@ -0,0 +1,313 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_INTERPRET_H
+#define GOB_INTERPRET_H
+
+#include "gob/goblin.h"
+
+namespace Gob {
+
+// This is to help devices with small memory (PDA, smartphones, ...)
+// to save abit of memory used by opcode names in the Scumm engine.
+#ifndef REDUCE_MEMORY_USAGE
+# define _OPCODE(ver, x) { &ver::x, #x }
+#else
+# define _OPCODE(ver, x) { &ver::x, "" }
+#endif
+
+class Inter {
+public:
+ int16 _animPalLowIndex;
+ int16 _animPalHighIndex;
+ int16 _animPalDir;
+ uint32 _soundEndTimeKey;
+ int16 _soundStopVal;
+ char _terminate;
+ char _breakFlag;
+ int16 *_breakFromLevel;
+ int16 *_nestLevel;
+
+ int16 load16(void);
+ int16 peek16(char *ptr);
+ int32 peek32(char *ptr);
+
+ char evalExpr(int16 *pRes);
+ char evalBoolResult(void);
+ void animPalette(void);
+ void funcBlock(int16 retFlag);
+ void storeKey(int16 key);
+ void checkSwitchTable(char **ppExec);
+ void callSub(int16 retFlag);
+ void initControlVars(void);
+ void renewTimeInVars(void);
+ void manipulateMap(int16 xPos, int16 yPos, int16 item);
+
+ Inter(GobEngine *vm);
+ virtual ~Inter() {};
+
+protected:
+ GobEngine *_vm;
+
+ virtual void setupOpcodes(void) = 0;
+ virtual void executeDrawOpcode(byte i) = 0;
+ virtual bool executeFuncOpcode(byte i, byte j, char &cmdCount, int16 &counter, int16 &retFlag) = 0;
+ virtual void executeGoblinOpcode(int i, int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) = 0;
+ virtual const char *getOpcodeDrawDesc(byte i) = 0;
+ virtual const char *getOpcodeFuncDesc(byte i, byte j) = 0;
+ virtual const char *getOpcodeGoblinDesc(int i) = 0;
+};
+
+class Inter_v1 : public Inter {
+public:
+ Inter_v1(GobEngine *vm);
+ virtual ~Inter_v1() {};
+
+protected:
+ typedef void (Inter_v1::*OpcodeDrawProcV1)(void);
+ typedef bool (Inter_v1::*OpcodeFuncProcV1)(char &, int16 &, int16 &);
+ typedef void (Inter_v1::*OpcodeGoblinProcV1)(int16 &, int32 *, Goblin::Gob_Object *);
+ struct OpcodeDrawEntryV1 {
+ OpcodeDrawProcV1 proc;
+ const char *desc;
+ };
+ struct OpcodeFuncEntryV1 {
+ OpcodeFuncProcV1 proc;
+ const char *desc;
+ };
+ struct OpcodeGoblinEntryV1 {
+ OpcodeGoblinProcV1 proc;
+ const char *desc;
+ };
+ const OpcodeDrawEntryV1 *_opcodesDrawV1;
+ const OpcodeFuncEntryV1 *_opcodesFuncV1;
+ const OpcodeGoblinEntryV1 *_opcodesGoblinV1;
+ static const int _goblinFuncLookUp[][2];
+
+ virtual void setupOpcodes(void);
+ virtual void executeDrawOpcode(byte i);
+ virtual bool executeFuncOpcode(byte i, byte j, char &cmdCount, int16 &counter, int16 &retFlag);
+ virtual void executeGoblinOpcode(int i, int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ virtual const char *getOpcodeDrawDesc(byte i);
+ virtual const char *getOpcodeFuncDesc(byte i, byte j);
+ virtual const char *getOpcodeGoblinDesc(int i);
+
+ void o1_loadMult(void);
+ void o1_playMult(void);
+ void o1_freeMult(void);
+ void o1_initCursor(void);
+ void o1_initCursorAnim(void);
+ void o1_clearCursorAnim(void);
+ void o1_setRenderFlags(void);
+ void o1_loadAnim(void);
+ void o1_freeAnim(void);
+ void o1_updateAnim(void);
+ void o1_initMult(void);
+ void o1_multFreeMult(void);
+ void o1_animate(void);
+ void o1_multLoadMult(void);
+ void o1_storeParams(void);
+ void o1_getObjAnimSize(void);
+ void o1_loadStatic(void);
+ void o1_freeStatic(void);
+ void o1_renderStatic(void);
+ void o1_loadCurLayer(void);
+ void o1_playCDTrack(void);
+ void o1_getCDTrackPos(void);
+ void o1_stopCD(void);
+ void o1_loadFontToSprite(void);
+ void o1_freeFontToSprite(void);
+ bool o1_callSub(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_drawPrintText(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_call(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_callBool(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_loadCursor(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_repeatUntil(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_whileDo(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_evaluateStore(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_loadSpriteToPos(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_printText(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_loadTot(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_palLoad(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_keyFunc(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_capturePush(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_capturePop(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_animPalInit(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_drawOperations(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_renewTimeInVars(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_putPixel(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_createSprite(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_freeSprite(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_loadSpriteContent(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_copySprite(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_fillRect(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_drawLine(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_strToLong(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_invalidate(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_playSound(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_stopSound(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_playComposition(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_getFreeMem(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_checkData(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_prepareStr(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_insertStr(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_cutStr(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_strstr(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_istrlen(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_setMousePos(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_setFrameRate(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_loadFont(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_freeFont(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_readData(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_writeData(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_manageDataFile(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_setcmdCount(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_return(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_speakerOn(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_speakerOff(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_goblinFunc(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_returnTo(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_setBackDelta(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_loadSound(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_freeSoundSlot(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_waitEndPlay(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_animatePalette(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_animateCursor(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o1_blitCursor(char &cmdCount, int16 &counter, int16 &retFlag);
+ void o1_setState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setCurFrame(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setNextState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setMultState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setOrder(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setActionStartState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setCurLookDir(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setType(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setNoTick(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setPickable(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setXPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setYPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setDoAnim(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setRelaxTime(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setMaxTick(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getCurFrame(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getNextState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getMultState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getOrder(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getActionStartState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getCurLookDir(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getType(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getNoTick(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getPickable(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getObjMaxFrame(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getXPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getYPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getDoAnim(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getRelaxTime(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getMaxTick(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_manipulateMap(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getItem(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_manipulateMapIndirect(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getItemIndirect(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setPassMap(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinPosH(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getGoblinPosXH(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getGoblinPosYH(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinMultState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinStateRedraw(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinUnk14(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setItemIdInPocket(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setItemIndInPocket(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getItemIdInPocket(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getItemIndInPocket(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setItemPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_decRelaxTime(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getGoblinPosX(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getGoblinPosY(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_clearPathExistence(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinVisible(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinInvisible(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getObjectIntersect(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_getGoblinIntersect(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_loadObjects(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_freeObjects(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_animateObjects(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_drawObjects(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_loadMap(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_moveGoblin(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_switchGoblin(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_loadGoblin(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_writeTreatItem(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_moveGoblin0(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinTarget(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_setGoblinObjectsPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ void o1_initGoblin(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+};
+
+class Inter_v2 : public Inter_v1 {
+public:
+ Inter_v2(GobEngine *vm);
+ virtual ~Inter_v2() {};
+
+protected:
+ typedef void (Inter_v2::*OpcodeDrawProcV2)(void);
+ typedef bool (Inter_v2::*OpcodeFuncProcV2)(char &, int16 &, int16 &);
+ typedef void (Inter_v2::*OpcodeGoblinProcV2)(int16 &, int32 *, Goblin::Gob_Object *);
+ struct OpcodeDrawEntryV2 {
+ OpcodeDrawProcV2 proc;
+ const char *desc;
+ };
+ struct OpcodeFuncEntryV2 {
+ OpcodeFuncProcV2 proc;
+ const char *desc;
+ };
+ struct OpcodeGoblinEntryV2 {
+ OpcodeGoblinProcV2 proc;
+ const char *desc;
+ };
+ const OpcodeDrawEntryV2 *_opcodesDrawV2;
+ const OpcodeFuncEntryV2 *_opcodesFuncV2;
+ const OpcodeGoblinEntryV2 *_opcodesGoblinV2;
+ static const int _goblinFuncLookUp[][2];
+
+ virtual void setupOpcodes(void);
+ virtual void executeDrawOpcode(byte i);
+ virtual bool executeFuncOpcode(byte i, byte j, char &cmdCount, int16 &counter, int16 &retFlag);
+ virtual void executeGoblinOpcode(int i, int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc);
+ virtual const char *getOpcodeDrawDesc(byte i);
+ virtual const char *getOpcodeFuncDesc(byte i, byte j);
+ virtual const char *getOpcodeGoblinDesc(int i);
+
+ void o2_drawStub(void) { warning("Gob2 stub"); }
+ void o2_stub0x80(void);
+ void o2_stub0x23(void);
+ bool o2_evaluateStore(char &cmdCount, int16 &counter, int16 &retFlag);
+ bool o2_palLoad(char &cmdCount, int16 &counter, int16 &retFlag);
+ void o2_setRenderFlags(void);
+ bool o2_loadTot(char &cmdCount, int16 &counter, int16 &retFlag);
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp
new file mode 100644
index 0000000000..747a852c19
--- /dev/null
+++ b/engines/gob/inter_v1.cpp
@@ -0,0 +1,2624 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/inter.h"
+#include "gob/util.h"
+#include "gob/scenery.h"
+#include "gob/parse.h"
+#include "gob/game.h"
+#include "gob/draw.h"
+#include "gob/mult.h"
+#include "gob/goblin.h"
+#include "gob/cdrom.h"
+#include "gob/music.h"
+#include "gob/map.h"
+#include "gob/palanim.h"
+
+namespace Gob {
+
+#define OPCODE(x) _OPCODE(Inter_v1, x)
+
+const int Inter_v1::_goblinFuncLookUp[][2] = {
+ {1, 0},
+ {2, 1},
+ {3, 2},
+ {4, 3},
+ {5, 4},
+ {6, 5},
+ {7, 6},
+ {8, 7},
+ {9, 8},
+ {10, 9},
+ {12, 10},
+ {13, 11},
+ {14, 12},
+ {15, 13},
+ {16, 14},
+ {21, 15},
+ {22, 16},
+ {23, 17},
+ {24, 18},
+ {25, 19},
+ {26, 20},
+ {27, 21},
+ {28, 22},
+ {29, 23},
+ {30, 24},
+ {32, 25},
+ {33, 26},
+ {34, 27},
+ {35, 28},
+ {36, 29},
+ {37, 30},
+ {40, 31},
+ {41, 32},
+ {42, 33},
+ {43, 34},
+ {44, 35},
+ {50, 36},
+ {52, 37},
+ {53, 38},
+ {150, 39},
+ {152, 40},
+ {200, 41},
+ {201, 42},
+ {202, 43},
+ {203, 44},
+ {204, 45},
+ {250, 46},
+ {251, 47},
+ {252, 48},
+ {500, 49},
+ {502, 50},
+ {503, 51},
+ {600, 52},
+ {601, 53},
+ {602, 54},
+ {603, 55},
+ {604, 56},
+ {605, 57},
+ {1000, 58},
+ {1001, 59},
+ {1002, 60},
+ {1003, 61},
+ {1004, 62},
+ {1005, 63},
+ {1006, 64},
+ {1008, 65},
+ {1009, 66},
+ {1010, 67},
+ {1011, 68},
+ {1015, 69},
+ {2005, 70}
+};
+
+Inter_v1::Inter_v1(GobEngine *vm) : Inter(vm) {
+ setupOpcodes();
+}
+
+void Inter_v1::setupOpcodes(void) {
+ static const OpcodeDrawEntryV1 opcodesDraw[256] = {
+ /* 00 */
+ OPCODE(o1_loadMult),
+ OPCODE(o1_playMult),
+ OPCODE(o1_freeMult),
+ {NULL, ""},
+ /* 04 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ OPCODE(o1_initCursor),
+ /* 08 */
+ OPCODE(o1_initCursorAnim),
+ OPCODE(o1_clearCursorAnim),
+ OPCODE(o1_setRenderFlags),
+ {NULL, ""},
+ /* 0C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 10 */
+ OPCODE(o1_loadAnim),
+ OPCODE(o1_freeAnim),
+ OPCODE(o1_updateAnim),
+ {NULL, ""},
+ /* 14 */
+ OPCODE(o1_initMult),
+ OPCODE(o1_multFreeMult),
+ OPCODE(o1_animate),
+ OPCODE(o1_multLoadMult),
+ /* 18 */
+ OPCODE(o1_storeParams),
+ OPCODE(o1_getObjAnimSize),
+ OPCODE(o1_loadStatic),
+ OPCODE(o1_freeStatic),
+ /* 1C */
+ OPCODE(o1_renderStatic),
+ OPCODE(o1_loadCurLayer),
+ {NULL, ""},
+ {NULL, ""},
+ /* 20 */
+ OPCODE(o1_playCDTrack),
+ OPCODE(o1_getCDTrackPos),
+ OPCODE(o1_stopCD),
+ {NULL, ""},
+ /* 24 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 28 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 2C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 30 */
+ OPCODE(o1_loadFontToSprite),
+ OPCODE(o1_freeFontToSprite),
+ {NULL, ""},
+ {NULL, ""},
+ /* 34 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 38 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 3C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 40 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 44 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 48 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 4C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 50 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 54 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 58 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 5C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 60 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 64 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 68 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 6C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 70 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 74 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 78 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 7C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 80 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 84 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 88 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 8C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 90 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 94 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 98 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 9C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* A0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* A4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* A8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* AC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* B0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* B4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* B8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* BC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* C0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* C4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* C8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* CC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* D0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* D4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* D8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* DC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* E0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* E4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* E8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* EC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* F0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* F4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* F8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* FC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""}
+ };
+
+ static const OpcodeFuncEntryV1 opcodesFunc[80] = {
+ /* 00 */
+ OPCODE(o1_callSub),
+ OPCODE(o1_callSub),
+ OPCODE(o1_drawPrintText),
+ OPCODE(o1_loadCursor),
+ /* 04 */
+ {NULL, ""},
+ OPCODE(o1_call),
+ OPCODE(o1_repeatUntil),
+ OPCODE(o1_whileDo),
+ /* 08 */
+ OPCODE(o1_callBool),
+ OPCODE(o1_evaluateStore),
+ OPCODE(o1_loadSpriteToPos),
+ {NULL, ""},
+ /* 0C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 10 */
+ {NULL, ""},
+ OPCODE(o1_printText),
+ OPCODE(o1_loadTot),
+ OPCODE(o1_palLoad),
+ /* 14 */
+ OPCODE(o1_keyFunc),
+ OPCODE(o1_capturePush),
+ OPCODE(o1_capturePop),
+ OPCODE(o1_animPalInit),
+ /* 18 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 1C */
+ {NULL, ""},
+ {NULL, ""},
+ OPCODE(o1_drawOperations),
+ OPCODE(o1_setcmdCount),
+ /* 20 */
+ OPCODE(o1_return),
+ OPCODE(o1_renewTimeInVars),
+ OPCODE(o1_speakerOn),
+ OPCODE(o1_speakerOff),
+ /* 24 */
+ OPCODE(o1_putPixel),
+ OPCODE(o1_goblinFunc),
+ OPCODE(o1_createSprite),
+ OPCODE(o1_freeSprite),
+ /* 28 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 2C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 30 */
+ OPCODE(o1_returnTo),
+ OPCODE(o1_loadSpriteContent),
+ OPCODE(o1_copySprite),
+ OPCODE(o1_fillRect),
+ /* 34 */
+ OPCODE(o1_drawLine),
+ OPCODE(o1_strToLong),
+ OPCODE(o1_invalidate),
+ OPCODE(o1_setBackDelta),
+ /* 38 */
+ OPCODE(o1_playSound),
+ OPCODE(o1_stopSound),
+ OPCODE(o1_loadSound),
+ OPCODE(o1_freeSoundSlot),
+ /* 3C */
+ OPCODE(o1_waitEndPlay),
+ OPCODE(o1_playComposition),
+ OPCODE(o1_getFreeMem),
+ OPCODE(o1_checkData),
+ /* 40 */
+ {NULL, ""},
+ OPCODE(o1_prepareStr),
+ OPCODE(o1_insertStr),
+ OPCODE(o1_cutStr),
+ /* 44 */
+ OPCODE(o1_strstr),
+ OPCODE(o1_istrlen),
+ OPCODE(o1_setMousePos),
+ OPCODE(o1_setFrameRate),
+ /* 48 */
+ OPCODE(o1_animatePalette),
+ OPCODE(o1_animateCursor),
+ OPCODE(o1_blitCursor),
+ OPCODE(o1_loadFont),
+ /* 4C */
+ OPCODE(o1_freeFont),
+ OPCODE(o1_readData),
+ OPCODE(o1_writeData),
+ OPCODE(o1_manageDataFile),
+ };
+
+ static const OpcodeGoblinEntryV1 opcodesGoblin[71] = {
+ /* 00 */
+ OPCODE(o1_setState),
+ OPCODE(o1_setCurFrame),
+ OPCODE(o1_setNextState),
+ OPCODE(o1_setMultState),
+ /* 04 */
+ OPCODE(o1_setOrder),
+ OPCODE(o1_setActionStartState),
+ OPCODE(o1_setCurLookDir),
+ OPCODE(o1_setType),
+ /* 08 */
+ OPCODE(o1_setNoTick),
+ OPCODE(o1_setPickable),
+ OPCODE(o1_setXPos),
+ OPCODE(o1_setYPos),
+ /* 0C */
+ OPCODE(o1_setDoAnim),
+ OPCODE(o1_setRelaxTime),
+ OPCODE(o1_setMaxTick),
+ OPCODE(o1_getState),
+ /* 10 */
+ OPCODE(o1_getCurFrame),
+ OPCODE(o1_getNextState),
+ OPCODE(o1_getMultState),
+ OPCODE(o1_getOrder),
+ /* 14 */
+ OPCODE(o1_getActionStartState),
+ OPCODE(o1_getCurLookDir),
+ OPCODE(o1_getType),
+ OPCODE(o1_getNoTick),
+ /* 18 */
+ OPCODE(o1_getPickable),
+ OPCODE(o1_getObjMaxFrame),
+ OPCODE(o1_getXPos),
+ OPCODE(o1_getYPos),
+ /* 1C */
+ OPCODE(o1_getDoAnim),
+ OPCODE(o1_getRelaxTime),
+ OPCODE(o1_getMaxTick),
+ OPCODE(o1_manipulateMap),
+ /* 20 */
+ OPCODE(o1_getItem),
+ OPCODE(o1_manipulateMapIndirect),
+ OPCODE(o1_getItemIndirect),
+ OPCODE(o1_setPassMap),
+ /* 24 */
+ OPCODE(o1_setGoblinPosH),
+ OPCODE(o1_getGoblinPosXH),
+ OPCODE(o1_getGoblinPosYH),
+ OPCODE(o1_setGoblinMultState),
+ /* 28 */
+ OPCODE(o1_setGoblinUnk14),
+ OPCODE(o1_setItemIdInPocket),
+ OPCODE(o1_setItemIndInPocket),
+ OPCODE(o1_getItemIdInPocket),
+ /* 2C */
+ OPCODE(o1_getItemIndInPocket),
+ OPCODE(o1_setItemPos),
+ OPCODE(o1_setGoblinPos),
+ OPCODE(o1_setGoblinState),
+ /* 30 */
+ OPCODE(o1_setGoblinStateRedraw),
+ OPCODE(o1_decRelaxTime),
+ OPCODE(o1_getGoblinPosX),
+ OPCODE(o1_getGoblinPosY),
+ /* 34 */
+ OPCODE(o1_clearPathExistence),
+ OPCODE(o1_setGoblinVisible),
+ OPCODE(o1_setGoblinInvisible),
+ OPCODE(o1_getObjectIntersect),
+ /* 38 */
+ OPCODE(o1_getGoblinIntersect),
+ OPCODE(o1_setItemPos),
+ OPCODE(o1_loadObjects),
+ OPCODE(o1_freeObjects),
+ /* 3C */
+ OPCODE(o1_animateObjects),
+ OPCODE(o1_drawObjects),
+ OPCODE(o1_loadMap),
+ OPCODE(o1_moveGoblin),
+ /* 40 */
+ OPCODE(o1_switchGoblin),
+ OPCODE(o1_loadGoblin),
+ OPCODE(o1_writeTreatItem),
+ OPCODE(o1_moveGoblin0),
+ /* 44 */
+ OPCODE(o1_setGoblinTarget),
+ OPCODE(o1_setGoblinObjectsPos),
+ OPCODE(o1_initGoblin)
+ };
+
+ _opcodesDrawV1 = opcodesDraw;
+ _opcodesFuncV1 = opcodesFunc; // EGroupe
+ _opcodesGoblinV1 = opcodesGoblin;
+}
+
+bool Inter_v1::o1_setMousePos(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_global->_inter_mouseX = _vm->_parse->parseValExpr();
+ _vm->_global->_inter_mouseY = _vm->_parse->parseValExpr();
+ if (_vm->_global->_useMouse != 0)
+ _vm->_util->setMousePos(_vm->_global->_inter_mouseX, _vm->_global->_inter_mouseY);
+ return false;
+}
+
+bool Inter_v1::o1_evaluateStore(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char *savedPos;
+ int16 token;
+ int16 result;
+ int16 varOff;
+
+ savedPos = _vm->_global->_inter_execPtr;
+ varOff = _vm->_parse->parseVarIndex();
+ token = evalExpr(&result);
+ switch (savedPos[0]) {
+ case 23:
+ case 26:
+ WRITE_VAR_OFFSET(varOff, _vm->_global->_inter_resVal);
+ break;
+
+ case 25:
+ case 28:
+ if (token == 20)
+ *(_vm->_global->_inter_variables + varOff) = result;
+ else
+ strcpy(_vm->_global->_inter_variables + varOff, _vm->_global->_inter_resStr);
+ break;
+
+ }
+ return false;
+}
+
+bool Inter_v1::o1_capturePush(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 left;
+ int16 top;
+ int16 width;
+ int16 height;
+
+ left = _vm->_parse->parseValExpr();
+ top = _vm->_parse->parseValExpr();
+ width = _vm->_parse->parseValExpr();
+ height = _vm->_parse->parseValExpr();
+ _vm->_game->capturePush(left, top, width, height);
+ (*_vm->_scenery->_pCaptureCounter)++;
+ return false;
+}
+
+bool Inter_v1::o1_capturePop(char &cmdCount, int16 &counter, int16 &retFlag) {
+ if (*_vm->_scenery->_pCaptureCounter != 0) {
+ (*_vm->_scenery->_pCaptureCounter)--;
+ _vm->_game->capturePop(1);
+ }
+ return false;
+}
+
+bool Inter_v1::o1_printText(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char buf[60];
+ int16 i;
+
+ _vm->_draw->_destSpriteX = _vm->_parse->parseValExpr();
+ _vm->_draw->_destSpriteY = _vm->_parse->parseValExpr();
+
+ _vm->_draw->_backColor = _vm->_parse->parseValExpr();
+ _vm->_draw->_frontColor = _vm->_parse->parseValExpr();
+ _vm->_draw->_fontIndex = _vm->_parse->parseValExpr();
+ _vm->_draw->_destSurface = 21;
+ _vm->_draw->_textToPrint = buf;
+ _vm->_draw->_transparency = 0;
+
+ if (_vm->_draw->_backColor >= 16) {
+ _vm->_draw->_backColor = 0;
+ _vm->_draw->_transparency = 1;
+ }
+
+ do {
+ for (i = 0; *_vm->_global->_inter_execPtr != '.' && (byte)*_vm->_global->_inter_execPtr != 200;
+ i++, _vm->_global->_inter_execPtr++) {
+ buf[i] = *_vm->_global->_inter_execPtr;
+ }
+
+ if ((byte)*_vm->_global->_inter_execPtr != 200) {
+ _vm->_global->_inter_execPtr++;
+ switch (*_vm->_global->_inter_execPtr) {
+ case 23:
+ case 26:
+ sprintf(buf + i, "%d", VAR_OFFSET(_vm->_parse->parseVarIndex()));
+ break;
+
+ case 25:
+ case 28:
+ sprintf(buf + i, "%s", _vm->_global->_inter_variables + _vm->_parse->parseVarIndex());
+ break;
+ }
+ _vm->_global->_inter_execPtr++;
+ } else {
+ buf[i] = 0;
+ }
+ _vm->_draw->spriteOperation(DRAW_PRINTTEXT);
+ } while ((byte)*_vm->_global->_inter_execPtr != 200);
+ _vm->_global->_inter_execPtr++;
+
+ return false;
+}
+
+bool Inter_v1::o1_animPalInit(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _animPalDir = load16();
+ _animPalLowIndex = _vm->_parse->parseValExpr();
+ _animPalHighIndex = _vm->_parse->parseValExpr();
+ return false;
+}
+
+void Inter_v1::o1_loadMult(void) {
+ int16 resId;
+
+ resId = load16();
+ _vm->_mult->loadMult(resId);
+}
+
+void Inter_v1::o1_playMult(void) {
+ int16 checkEscape;
+
+ checkEscape = load16();
+ _vm->_mult->playMult(VAR(57), -1, checkEscape, 0);
+}
+
+void Inter_v1::o1_freeMult(void) {
+ load16(); // unused
+ _vm->_mult->freeMultKeys();
+}
+
+void Inter_v1::o1_initCursor(void) {
+ int16 width;
+ int16 height;
+ int16 count;
+ int16 i;
+
+ _vm->_draw->_cursorXDeltaVar = _vm->_parse->parseVarIndex();
+ _vm->_draw->_cursorYDeltaVar = _vm->_parse->parseVarIndex();
+
+ width = load16();
+ if (width < 16)
+ width = 16;
+
+ height = load16();
+ if (height < 16)
+ height = 16;
+
+ count = load16();
+ if (count < 2)
+ count = 2;
+
+ if (width != _vm->_draw->_cursorWidth || height != _vm->_draw->_cursorHeight ||
+ _vm->_draw->_cursorSprites->width != width * count) {
+
+ _vm->_video->freeSurfDesc(_vm->_draw->_cursorSprites);
+ _vm->_video->freeSurfDesc(_vm->_draw->_cursorBack);
+
+ _vm->_draw->_cursorWidth = width;
+ _vm->_draw->_cursorHeight = height;
+
+ if (count < 0x80)
+ _vm->_draw->_transparentCursor = 1;
+ else
+ _vm->_draw->_transparentCursor = 0;
+
+ if (count > 0x80)
+ count -= 0x80;
+
+ _vm->_draw->_cursorSprites =
+ _vm->_video->initSurfDesc(_vm->_global->_videoMode, _vm->_draw->_cursorWidth * count,
+ _vm->_draw->_cursorHeight, 2);
+ _vm->_draw->_spritesArray[23] = _vm->_draw->_cursorSprites;
+
+ _vm->_draw->_cursorBack =
+ _vm->_video->initSurfDesc(_vm->_global->_videoMode, _vm->_draw->_cursorWidth,
+ _vm->_draw->_cursorHeight, 0);
+ for (i = 0; i < 40; i++) {
+ _vm->_draw->_cursorAnimLow[i] = -1;
+ _vm->_draw->_cursorAnimDelays[i] = 0;
+ _vm->_draw->_cursorAnimHigh[i] = 0;
+ }
+ _vm->_draw->_cursorAnimLow[1] = 0;
+ }
+}
+
+void Inter_v1::o1_initCursorAnim(void) {
+ int16 ind;
+
+ ind = _vm->_parse->parseValExpr();
+ _vm->_draw->_cursorAnimLow[ind] = load16();
+ _vm->_draw->_cursorAnimHigh[ind] = load16();
+ _vm->_draw->_cursorAnimDelays[ind] = load16();
+}
+
+void Inter_v1::o1_clearCursorAnim(void) {
+ int16 ind;
+
+ ind = _vm->_parse->parseValExpr();
+ _vm->_draw->_cursorAnimLow[ind] = -1;
+ _vm->_draw->_cursorAnimHigh[ind] = 0;
+ _vm->_draw->_cursorAnimDelays[ind] = 0;
+}
+
+bool Inter_v1::o1_drawOperations(char &cmdCount, int16 &counter, int16 &retFlag) {
+ byte cmd;
+
+ cmd = *_vm->_global->_inter_execPtr++;
+
+ executeDrawOpcode(cmd);
+
+ return false;
+}
+
+bool Inter_v1::o1_getFreeMem(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 freeVar;
+ int16 maxFreeVar;
+
+ freeVar = _vm->_parse->parseVarIndex();
+ maxFreeVar = _vm->_parse->parseVarIndex();
+
+ // HACK
+ WRITE_VAR_OFFSET(freeVar, 1000000);
+ WRITE_VAR_OFFSET(maxFreeVar, 1000000);
+ return false;
+}
+
+bool Inter_v1::o1_manageDataFile(char &cmdCount, int16 &counter, int16 &retFlag) {
+ evalExpr(0);
+
+ if (_vm->_global->_inter_resStr[0] != 0)
+ _vm->_dataio->openDataFile(_vm->_global->_inter_resStr);
+ else
+ _vm->_dataio->closeDataFile();
+ return false;
+}
+
+bool Inter_v1::o1_writeData(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 offset;
+ int16 handle;
+ int16 size;
+ int16 dataVar;
+ int16 retSize;
+
+ evalExpr(0);
+ dataVar = _vm->_parse->parseVarIndex();
+ size = _vm->_parse->parseValExpr();
+ offset = _vm->_parse->parseValExpr();
+
+ WRITE_VAR(1, 1);
+ handle = _vm->_dataio->openData(_vm->_global->_inter_resStr, Common::File::kFileWriteMode);
+
+ if (handle < 0)
+ return false;
+
+ if (offset < 0) {
+ _vm->_dataio->seekData(handle, -offset - 1, 2);
+ } else {
+ _vm->_dataio->seekData(handle, offset, 0);
+ }
+
+ retSize = _vm->_dataio->file_getHandle(handle)->write(_vm->_global->_inter_variables + dataVar, size);
+
+ if (retSize == size)
+ WRITE_VAR(1, 0);
+
+ _vm->_dataio->closeData(handle);
+ return false;
+}
+
+bool Inter_v1::o1_checkData(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 handle;
+ int16 varOff;
+
+ evalExpr(0);
+ varOff = _vm->_parse->parseVarIndex();
+ handle = _vm->_dataio->openData(_vm->_global->_inter_resStr);
+
+ WRITE_VAR_OFFSET(varOff, handle);
+ if (handle >= 0)
+ _vm->_dataio->closeData(handle);
+ return false;
+}
+
+bool Inter_v1::o1_readData(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 retSize;
+ int16 size;
+ int16 dataVar;
+ int16 offset;
+ int16 handle;
+
+ evalExpr(0);
+ dataVar = _vm->_parse->parseVarIndex();
+ size = _vm->_parse->parseValExpr();
+ offset = _vm->_parse->parseValExpr();
+
+ if (_vm->_game->_extHandle >= 0)
+ _vm->_dataio->closeData(_vm->_game->_extHandle);
+
+ WRITE_VAR(1, 1);
+ handle = _vm->_dataio->openData(_vm->_global->_inter_resStr);
+ if (handle >= 0) {
+ _vm->_draw->animateCursor(4);
+ if (offset < 0)
+ _vm->_dataio->seekData(handle, -offset - 1, 2);
+ else
+ _vm->_dataio->seekData(handle, offset, 0);
+
+ retSize = _vm->_dataio->readData(handle, _vm->_global->_inter_variables + dataVar, size);
+ _vm->_dataio->closeData(handle);
+
+ if (retSize == size)
+ WRITE_VAR(1, 0);
+ }
+
+ if (_vm->_game->_extHandle >= 0)
+ _vm->_game->_extHandle = _vm->_dataio->openData(_vm->_game->_curExtFile);
+ return false;
+}
+
+bool Inter_v1::o1_loadFont(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 index;
+
+ evalExpr(0);
+ index = load16();
+
+ if (_vm->_draw->_fonts[index] != 0)
+ _vm->_util->freeFont(_vm->_draw->_fonts[index]);
+
+ _vm->_draw->animateCursor(4);
+ if (_vm->_game->_extHandle >= 0)
+ _vm->_dataio->closeData(_vm->_game->_extHandle);
+
+ _vm->_draw->_fonts[index] = _vm->_util->loadFont(_vm->_global->_inter_resStr);
+
+ if (_vm->_game->_extHandle >= 0)
+ _vm->_game->_extHandle = _vm->_dataio->openData(_vm->_game->_curExtFile);
+ return false;
+}
+
+bool Inter_v1::o1_freeFont(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 index;
+
+ index = load16();
+ if (_vm->_draw->_fonts[index] != 0)
+ _vm->_util->freeFont(_vm->_draw->_fonts[index]);
+
+ _vm->_draw->_fonts[index] = 0;
+ return false;
+}
+
+bool Inter_v1::o1_prepareStr(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 var;
+
+ var = _vm->_parse->parseVarIndex();
+ _vm->_util->prepareStr(_vm->_global->_inter_variables + var);
+ return false;
+}
+
+bool Inter_v1::o1_insertStr(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 pos;
+ int16 strVar;
+
+ strVar = _vm->_parse->parseVarIndex();
+ evalExpr(0);
+ pos = _vm->_parse->parseValExpr();
+ _vm->_util->insertStr(_vm->_global->_inter_resStr, _vm->_global->_inter_variables + strVar, pos);
+ return false;
+}
+
+bool Inter_v1::o1_cutStr(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 var;
+ int16 pos;
+ int16 size;
+
+ var = _vm->_parse->parseVarIndex();
+ pos = _vm->_parse->parseValExpr();
+ size = _vm->_parse->parseValExpr();
+ _vm->_util->cutFromStr(_vm->_global->_inter_variables + var, pos, size);
+ return false;
+}
+
+bool Inter_v1::o1_strstr(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 strVar;
+ int16 resVar;
+ int16 pos;
+
+ strVar = _vm->_parse->parseVarIndex();
+ evalExpr(0);
+ resVar = _vm->_parse->parseVarIndex();
+
+ char *res = strstr(_vm->_global->_inter_variables + strVar, _vm->_global->_inter_resStr);
+ pos = res ? (res - (_vm->_global->_inter_variables + strVar)) : -1;
+ WRITE_VAR_OFFSET(resVar, pos);
+ return false;
+}
+
+bool Inter_v1::o1_setFrameRate(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_util->setFrameRate(_vm->_parse->parseValExpr());
+ return false;
+}
+
+bool Inter_v1::o1_istrlen(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 len;
+ int16 var;
+
+ var = _vm->_parse->parseVarIndex();
+ len = strlen(_vm->_global->_inter_variables + var);
+ var = _vm->_parse->parseVarIndex();
+
+ WRITE_VAR_OFFSET(var, len);
+ return false;
+}
+
+bool Inter_v1::o1_strToLong(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char str[20];
+ int16 strVar;
+ int16 destVar;
+ int32 res;
+
+ strVar = _vm->_parse->parseVarIndex();
+ strcpy(str, _vm->_global->_inter_variables + strVar);
+ res = atol(str);
+
+ destVar = _vm->_parse->parseVarIndex();
+ WRITE_VAR_OFFSET(destVar, res);
+ return false;
+}
+
+bool Inter_v1::o1_invalidate(char &cmdCount, int16 &counter, int16 &retFlag) {
+ warning("invalidate: 'bugged' function!");
+ _vm->_draw->_destSurface = load16();
+ _vm->_draw->_destSpriteX = _vm->_parse->parseValExpr();
+ _vm->_draw->_destSpriteY = _vm->_parse->parseValExpr();
+ _vm->_draw->_spriteRight = _vm->_parse->parseValExpr();
+ _vm->_draw->_frontColor = _vm->_parse->parseValExpr();
+ _vm->_draw->spriteOperation(DRAW_INVALIDATE);
+ return false;
+}
+
+bool Inter_v1::o1_loadSpriteContent(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->_spriteLeft = load16();
+ _vm->_draw->_destSurface = load16();
+ _vm->_draw->_transparency = load16();
+ _vm->_draw->_destSpriteX = 0;
+ _vm->_draw->_destSpriteY = 0;
+ _vm->_draw->spriteOperation(DRAW_LOADSPRITE);
+ return false;
+}
+
+bool Inter_v1::o1_copySprite(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->_sourceSurface = load16();
+ _vm->_draw->_destSurface = load16();
+
+ _vm->_draw->_spriteLeft = _vm->_parse->parseValExpr();
+ _vm->_draw->_spriteTop = _vm->_parse->parseValExpr();
+ _vm->_draw->_spriteRight = _vm->_parse->parseValExpr();
+ _vm->_draw->_spriteBottom = _vm->_parse->parseValExpr();
+
+ _vm->_draw->_destSpriteX = _vm->_parse->parseValExpr();
+ _vm->_draw->_destSpriteY = _vm->_parse->parseValExpr();
+
+ _vm->_draw->_transparency = load16();
+ _vm->_draw->spriteOperation(DRAW_BLITSURF);
+ return false;
+}
+
+bool Inter_v1::o1_putPixel(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->_destSurface = load16();
+
+ _vm->_draw->_destSpriteX = _vm->_parse->parseValExpr();
+ _vm->_draw->_destSpriteY = _vm->_parse->parseValExpr();
+ _vm->_draw->_frontColor = _vm->_parse->parseValExpr();
+ _vm->_draw->spriteOperation(DRAW_PUTPIXEL);
+ return false;
+}
+
+bool Inter_v1::o1_fillRect(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->_destSurface = load16();
+
+ _vm->_draw->_destSpriteX = _vm->_parse->parseValExpr();
+ _vm->_draw->_destSpriteY = _vm->_parse->parseValExpr();
+ _vm->_draw->_spriteRight = _vm->_parse->parseValExpr();
+ _vm->_draw->_spriteBottom = _vm->_parse->parseValExpr();
+
+ _vm->_draw->_backColor = _vm->_parse->parseValExpr();
+ _vm->_draw->spriteOperation(DRAW_FILLRECT);
+ return false;
+}
+
+bool Inter_v1::o1_drawLine(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->_destSurface = load16();
+
+ _vm->_draw->_destSpriteX = _vm->_parse->parseValExpr();
+ _vm->_draw->_destSpriteY = _vm->_parse->parseValExpr();
+ _vm->_draw->_spriteRight = _vm->_parse->parseValExpr();
+ _vm->_draw->_spriteBottom = _vm->_parse->parseValExpr();
+
+ _vm->_draw->_frontColor = _vm->_parse->parseValExpr();
+ _vm->_draw->spriteOperation(DRAW_DRAWLINE);
+ return false;
+}
+
+bool Inter_v1::o1_createSprite(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 index;
+ int16 height;
+ int16 width;
+ int16 flag;
+
+ index = load16();
+ width = load16();
+ height = load16();
+
+ flag = load16();
+ if (flag == 1)
+ _vm->_draw->_spritesArray[index] = _vm->_video->initSurfDesc(_vm->_global->_videoMode, width, height, 2);
+ else
+ _vm->_draw->_spritesArray[index] = _vm->_video->initSurfDesc(_vm->_global->_videoMode, width, height, 0);
+
+ _vm->_video->clearSurf(_vm->_draw->_spritesArray[index]);
+ return false;
+}
+
+bool Inter_v1::o1_freeSprite(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 index;
+
+ index = load16();
+ if (_vm->_draw->_spritesArray[index] == 0)
+ return false;
+
+ _vm->_video->freeSurfDesc(_vm->_draw->_spritesArray[index]);
+ _vm->_draw->_spritesArray[index] = 0;
+ return false;
+}
+
+bool Inter_v1::o1_playComposition(char &cmdCount, int16 &counter, int16 &retFlag) {
+ static int16 composition[50];
+ int16 i;
+ int16 dataVar;
+ int16 freqVal;
+
+ dataVar = _vm->_parse->parseVarIndex();
+ freqVal = _vm->_parse->parseValExpr();
+ for (i = 0; i < 50; i++)
+ composition[i] = (int16)VAR_OFFSET(dataVar + i * 4);
+
+ _vm->_snd->playComposition(_vm->_game->_soundSamples, composition, freqVal);
+ return false;
+}
+
+bool Inter_v1::o1_stopSound(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_snd->stopSound(_vm->_parse->parseValExpr());
+ _soundEndTimeKey = 0;
+ return false;
+}
+
+bool Inter_v1::o1_playSound(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 frequency;
+ int16 freq2;
+ int16 repCount;
+ int16 index;
+
+ index = _vm->_parse->parseValExpr();
+ repCount = _vm->_parse->parseValExpr();
+ frequency = _vm->_parse->parseValExpr();
+
+ _vm->_snd->stopSound(0);
+ _soundEndTimeKey = 0;
+ if (_vm->_game->_soundSamples[index] == 0)
+ return false;
+
+ if (repCount < 0) {
+ if (_vm->_global->_soundFlags < 2)
+ return false;
+
+ repCount = -repCount;
+ _soundEndTimeKey = _vm->_util->getTimeKey();
+
+ if (frequency == 0) {
+ freq2 = _vm->_game->_soundSamples[index]->frequency;
+ } else {
+ freq2 = frequency;
+ }
+ _soundStopVal =
+ (10 * (_vm->_game->_soundSamples[index]->size / 2)) / freq2;
+ _soundEndTimeKey +=
+ ((_vm->_game->_soundSamples[index]->size * repCount -
+ _vm->_game->_soundSamples[index]->size / 2) * 1000) / freq2;
+ }
+ _vm->_snd->playSample(_vm->_game->_soundSamples[index], repCount, frequency);
+ return false;
+}
+
+bool Inter_v1::o1_loadCursor(char &cmdCount, int16 &counter, int16 &retFlag) {
+ Game::TotResItem *itemPtr;
+ int16 width;
+ int16 height;
+ int32 offset;
+ char *dataBuf;
+ int16 id;
+ int8 index;
+
+ id = load16();
+ index = *_vm->_global->_inter_execPtr++;
+ itemPtr = &_vm->_game->_totResourceTable->items[id];
+ offset = itemPtr->offset;
+
+ if (offset >= 0) {
+ dataBuf =
+ ((char *)_vm->_game->_totResourceTable) + szGame_TotResTable +
+ szGame_TotResItem * _vm->_game->_totResourceTable->itemsCount + offset;
+ } else {
+ dataBuf = _vm->_game->_imFileData + (int32)READ_LE_UINT32(&((int32 *)_vm->_game->_imFileData)[-offset - 1]);
+ }
+
+ width = itemPtr->width;
+ height = itemPtr->height;
+
+ _vm->_video->fillRect(_vm->_draw->_cursorSprites, index * _vm->_draw->_cursorWidth, 0,
+ index * _vm->_draw->_cursorWidth + _vm->_draw->_cursorWidth - 1,
+ _vm->_draw->_cursorHeight - 1, 0);
+
+ _vm->_video->drawPackedSprite((byte*)dataBuf, width, height,
+ index * _vm->_draw->_cursorWidth, 0, 0, _vm->_draw->_cursorSprites);
+ _vm->_draw->_cursorAnimLow[index] = 0;
+
+ return false;
+}
+
+bool Inter_v1::o1_loadSpriteToPos(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->_spriteLeft = load16();
+
+ _vm->_draw->_destSpriteX = _vm->_parse->parseValExpr();
+ _vm->_draw->_destSpriteY = _vm->_parse->parseValExpr();
+
+ _vm->_draw->_transparency = _vm->_global->_inter_execPtr[0];
+ _vm->_draw->_destSurface = (_vm->_global->_inter_execPtr[0] / 2) - 1;
+
+ if (_vm->_draw->_destSurface < 0)
+ _vm->_draw->_destSurface = 101;
+ _vm->_draw->_transparency &= 1;
+ _vm->_global->_inter_execPtr += 2;
+ _vm->_draw->spriteOperation(DRAW_LOADSPRITE);
+
+ return false;
+}
+
+bool Inter_v1::o1_loadTot(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char buf[20];
+ int8 size;
+ int16 i;
+
+ if ((*_vm->_global->_inter_execPtr & 0x80) != 0) {
+ _vm->_global->_inter_execPtr++;
+ evalExpr(0);
+ strcpy(buf, _vm->_global->_inter_resStr);
+ } else {
+ size = *_vm->_global->_inter_execPtr++;
+ for (i = 0; i < size; i++)
+ buf[i] = *_vm->_global->_inter_execPtr++;
+
+ buf[size] = 0;
+ }
+
+ strcat(buf, ".tot");
+ _terminate = true;
+ strcpy(_vm->_game->_totToLoad, buf);
+
+ return false;
+}
+
+bool Inter_v1::o1_keyFunc(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 flag;
+ int16 key;
+
+ flag = load16();
+ animPalette();
+ _vm->_draw->blitInvalidated();
+
+ if (flag != 0) {
+
+ if (flag != 1) {
+ if (flag != 2) {
+ _vm->_util->longDelay(flag);
+ return false;
+ }
+
+ key = 0;
+
+ if (_vm->_global->_pressedKeys[0x48])
+ key |= 1;
+
+ if (_vm->_global->_pressedKeys[0x50])
+ key |= 2;
+
+ if (_vm->_global->_pressedKeys[0x4d])
+ key |= 4;
+
+ if (_vm->_global->_pressedKeys[0x4b])
+ key |= 8;
+
+ if (_vm->_global->_pressedKeys[0x1c])
+ key |= 0x10;
+
+ if (_vm->_global->_pressedKeys[0x39])
+ key |= 0x20;
+
+ if (_vm->_global->_pressedKeys[1])
+ key |= 0x40;
+
+ if (_vm->_global->_pressedKeys[0x1d])
+ key |= 0x80;
+
+ if (_vm->_global->_pressedKeys[0x2a])
+ key |= 0x100;
+
+ if (_vm->_global->_pressedKeys[0x36])
+ key |= 0x200;
+
+ if (_vm->_global->_pressedKeys[0x38])
+ key |= 0x400;
+
+ if (_vm->_global->_pressedKeys[0x3b])
+ key |= 0x800;
+
+ if (_vm->_global->_pressedKeys[0x3c])
+ key |= 0x1000;
+
+ if (_vm->_global->_pressedKeys[0x3d])
+ key |= 0x2000;
+
+ if (_vm->_global->_pressedKeys[0x3e])
+ key |= 0x4000;
+
+ WRITE_VAR(0, key);
+ _vm->_util->waitKey();
+ return false;
+ }
+ key = _vm->_game->checkKeys(&_vm->_global->_inter_mouseX, &_vm->_global->_inter_mouseY, &_vm->_game->_mouseButtons, 0);
+
+ storeKey(key);
+ return false;
+ } else {
+ key = _vm->_game->checkCollisions(0, 0, 0, 0);
+ storeKey(key);
+
+ if (flag == 1)
+ return false;
+
+ _vm->_util->waitKey();
+ }
+ return false;
+}
+
+bool Inter_v1::o1_repeatUntil(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char *blockPtr;
+ int16 size;
+ char flag;
+
+ _nestLevel[0]++;
+ blockPtr = _vm->_global->_inter_execPtr;
+
+ do {
+ _vm->_global->_inter_execPtr = blockPtr;
+ size = READ_LE_UINT16(_vm->_global->_inter_execPtr + 2) + 2;
+
+ funcBlock(1);
+ _vm->_global->_inter_execPtr = blockPtr + size + 1;
+ flag = evalBoolResult();
+ } while (flag == 0 && !_breakFlag && !_terminate);
+
+ _nestLevel[0]--;
+
+ if (*_breakFromLevel > -1) {
+ _breakFlag = false;
+ *_breakFromLevel = -1;
+ }
+ return false;
+}
+
+bool Inter_v1::o1_whileDo(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char *blockPtr;
+ char *savedIP;
+ char flag;
+ int16 size;
+
+ _nestLevel[0]++;
+ do {
+ savedIP = _vm->_global->_inter_execPtr;
+ flag = evalBoolResult();
+
+ if (_terminate)
+ return false;
+
+ blockPtr = _vm->_global->_inter_execPtr;
+
+ size = READ_LE_UINT16(_vm->_global->_inter_execPtr + 2) + 2;
+
+ if (flag != 0) {
+ funcBlock(1);
+ _vm->_global->_inter_execPtr = savedIP;
+ } else {
+ _vm->_global->_inter_execPtr += size;
+ }
+
+ if (_breakFlag || _terminate) {
+ _vm->_global->_inter_execPtr = blockPtr;
+ _vm->_global->_inter_execPtr += size;
+ break;
+ }
+ } while (flag != 0);
+
+ _nestLevel[0]--;
+ if (*_breakFromLevel > -1) {
+ _breakFlag = false;
+ *_breakFromLevel = -1;
+ }
+ return false;
+}
+
+void Inter_v1::o1_setRenderFlags(void) {
+ _vm->_draw->_renderFlags = _vm->_parse->parseValExpr();
+}
+
+void Inter_v1::o1_loadAnim(void) {
+ _vm->_scenery->loadAnim(0);
+}
+
+void Inter_v1::o1_freeAnim(void) {
+ _vm->_scenery->freeAnim(-1);
+}
+
+void Inter_v1::o1_updateAnim(void) {
+ _vm->_scenery->interUpdateAnim();
+}
+
+void Inter_v1::o1_initMult(void) {
+ _vm->_mult->interInitMult();
+}
+
+void Inter_v1::o1_multFreeMult(void) {
+ _vm->_mult->freeMult();
+}
+
+void Inter_v1::o1_animate(void) {
+ _vm->_mult->animate();
+}
+
+void Inter_v1::o1_multLoadMult(void) {
+ _vm->_mult->interLoadMult();
+}
+
+void Inter_v1::o1_storeParams(void) {
+ _vm->_scenery->interStoreParams();
+}
+
+void Inter_v1::o1_getObjAnimSize(void) {
+ _vm->_mult->interGetObjAnimSize();
+}
+
+void Inter_v1::o1_loadStatic(void) {
+ _vm->_scenery->loadStatic(0);
+}
+
+void Inter_v1::o1_freeStatic(void) {
+ _vm->_scenery->freeStatic(-1);
+}
+
+void Inter_v1::o1_renderStatic(void) {
+ _vm->_scenery->interRenderStatic();
+}
+
+void Inter_v1::o1_loadCurLayer(void) {
+ _vm->_scenery->interLoadCurLayer();
+}
+
+void Inter_v1::o1_playCDTrack(void) {
+ evalExpr(0);
+ if (_vm->_features & GF_MAC)
+ _vm->_music->playTrack(_vm->_global->_inter_resStr);
+ else
+ // Used in gob1 CD
+ _vm->_cdrom->startTrack(_vm->_global->_inter_resStr);
+}
+
+void Inter_v1::o1_getCDTrackPos(void) {
+ // Used in gob1 CD
+
+ // Some scripts busy-wait while calling this opcode.
+ // This is a very nasty thing to do, so let's add a
+ // short delay here. It's probably a safe thing to do.
+
+ _vm->_util->longDelay(1);
+
+ int pos = _vm->_cdrom->getTrackPos();
+ if (pos == -1)
+ pos = 32767;
+ WRITE_VAR(5, pos);
+}
+
+void Inter_v1::o1_stopCD(void) {
+ if (_vm->_features & GF_MAC)
+ _vm->_music->stopPlay();
+ else
+ // Used in gob1 CD
+ _vm->_cdrom->stopPlaying();
+}
+
+void Inter_v1::o1_loadFontToSprite(void) {
+ int16 i = load16();
+ _vm->_draw->_fontToSprite[i].sprite = load16();
+ _vm->_draw->_fontToSprite[i].base = load16();
+ _vm->_draw->_fontToSprite[i].width = load16();
+ _vm->_draw->_fontToSprite[i].height = load16();
+}
+
+void Inter_v1::o1_freeFontToSprite(void) {
+ int16 i = load16();
+ _vm->_draw->_fontToSprite[i].sprite = -1;
+ _vm->_draw->_fontToSprite[i].base = -1;
+ _vm->_draw->_fontToSprite[i].width = -1;
+ _vm->_draw->_fontToSprite[i].height = -1;
+}
+
+void Inter_v1::executeDrawOpcode(byte i) {
+ debug(4, "opcodeDraw %d (%s)", i, getOpcodeDrawDesc(i));
+
+ OpcodeDrawProcV1 op = _opcodesDrawV1[i].proc;
+
+ if (op == NULL)
+ warning("unimplemented opcodeDraw: %d", i);
+ else
+ (this->*op) ();
+}
+
+bool Inter_v1::executeFuncOpcode(byte i, byte j, char &cmdCount, int16 &counter, int16 &retFlag) {
+ debug(4, "opcodeFunc %d (%s)", i, getOpcodeFuncDesc(i, j));
+
+ if ((i > 4) || (j > 15)) {
+ warning("unimplemented opcodeFunc: %d.%d", i, j);
+ return false;
+ }
+
+ OpcodeFuncProcV1 op = _opcodesFuncV1[i*16 + j].proc;
+
+ if (op == NULL)
+ warning("unimplemented opcodeFunc: %d.%d", i, j);
+ else
+ return (this->*op) (cmdCount, counter, retFlag);
+ return false;
+}
+
+void Inter_v1::executeGoblinOpcode(int i, int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ debug(4, "opcodeGoblin %d (%s)", i, getOpcodeGoblinDesc(i));
+
+ OpcodeGoblinProcV1 op = NULL;
+
+ for (int j = 0; j < ARRAYSIZE(_goblinFuncLookUp); j++)
+ if (_goblinFuncLookUp[j][0] == i) {
+ op = _opcodesGoblinV1[_goblinFuncLookUp[j][1]].proc;
+ break;
+ }
+
+ if (op == NULL) {
+ warning("unimplemented opcodeGoblin: %d", i);
+ _vm->_global->_inter_execPtr -= 2;
+ int16 cmd = load16();
+ _vm->_global->_inter_execPtr += cmd * 2;
+ }
+ else
+ (this->*op) (extraData, retVarPtr, objDesc);
+}
+
+const char *Inter_v1::getOpcodeDrawDesc(byte i) {
+ return _opcodesDrawV1[i].desc;
+}
+
+const char *Inter_v1::getOpcodeFuncDesc(byte i, byte j) {
+ if ((i > 4) || (j > 15))
+ return "";
+
+ return _opcodesFuncV1[i*16 + j].desc;
+}
+
+const char *Inter_v1::getOpcodeGoblinDesc(int i) {
+ for (int j = 0; j < ARRAYSIZE(_goblinFuncLookUp); j++)
+ if (_goblinFuncLookUp[j][0] == i)
+ return _opcodesGoblinV1[_goblinFuncLookUp[j][1]].desc;
+ return "";
+}
+
+bool Inter_v1::o1_callSub(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char *storedIP = _vm->_global->_inter_execPtr;
+
+// _vm->_global->_inter_execPtr = (char *)_vm->_game->_totFileData + READ_LE_UINT16(_vm->_global->_inter_execPtr);
+
+ uint16 offset = READ_LE_UINT16(_vm->_global->_inter_execPtr);
+ debug(5, "tot = \"%s\", offset = %d", _vm->_game->_curTotFile, offset);
+
+ // Skipping the copy protection screen in Gobliiins
+ if (!_vm->_copyProtection && (_vm->_features & GF_GOB1) && (offset == 3905)
+ && !scumm_stricmp(_vm->_game->_curTotFile, "intro.tot")) {
+ debug(2, "Skipping copy protection screen");
+ _vm->_global->_inter_execPtr += 2;
+ return false;
+ }
+
+ _vm->_global->_inter_execPtr = (char *)_vm->_game->_totFileData + offset;
+
+ if (counter == cmdCount && retFlag == 2)
+ return true;
+
+ callSub(2);
+ _vm->_global->_inter_execPtr = storedIP + 2;
+
+ return false;
+}
+
+bool Inter_v1::o1_drawPrintText(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->printText();
+ return false;
+}
+
+bool Inter_v1::o1_call(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char *callAddr;
+
+ checkSwitchTable(&callAddr);
+ char *storedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr = callAddr;
+
+ if (counter == cmdCount && retFlag == 2)
+ return true;
+
+ funcBlock(0);
+ _vm->_global->_inter_execPtr = storedIP;
+
+ return false;
+}
+
+bool Inter_v1::o1_callBool(char &cmdCount, int16 &counter, int16 &retFlag) {
+ byte cmd;
+ bool boolRes = evalBoolResult() != 0;
+ if (boolRes != 0) {
+ if (counter == cmdCount
+ && retFlag == 2)
+ return true;
+
+ char *storedIP = _vm->_global->_inter_execPtr;
+ funcBlock(0);
+ _vm->_global->_inter_execPtr = storedIP;
+
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr + 2) + 2;
+
+ debug(5, "cmd = %d", (int16)*_vm->_global->_inter_execPtr);
+ cmd = (byte)(*_vm->_global->_inter_execPtr) >> 4;
+ _vm->_global->_inter_execPtr++;
+ if (cmd != 12)
+ return false;
+
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr + 2) + 2;
+ } else {
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr + 2) + 2;
+
+ debug(5, "cmd = %d", (int16)*_vm->_global->_inter_execPtr);
+ cmd = (byte)(*_vm->_global->_inter_execPtr) >> 4;
+ _vm->_global->_inter_execPtr++;
+ if (cmd != 12)
+ return false;
+
+ if (counter == cmdCount && retFlag == 2)
+ return true;
+
+ char *storedIP = _vm->_global->_inter_execPtr;
+ funcBlock(0);
+ _vm->_global->_inter_execPtr = storedIP;
+ _vm->_global->_inter_execPtr += READ_LE_UINT16(_vm->_global->_inter_execPtr + 2) + 2;
+ }
+ return false;
+}
+
+bool Inter_v1::o1_palLoad(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 i;
+ int16 ind1;
+ int16 ind2;
+ byte cmd;
+ char *palPtr;
+
+ cmd = *_vm->_global->_inter_execPtr++;
+ _vm->_draw->_applyPal = 0;
+ if (cmd & 0x80)
+ cmd &= 0x7f;
+ else
+ _vm->_draw->_applyPal = 1;
+
+ if (cmd == 49) {
+ warning("o1_palLoad: cmd == 49 is not supported");
+ //var_B = 1;
+ for (i = 0; i < 18; i++, _vm->_global->_inter_execPtr++) {
+ if (i < 2) {
+ if (_vm->_draw->_applyPal == 0)
+ continue;
+
+ _vm->_draw->_unusedPalette1[i] = *_vm->_global->_inter_execPtr;
+ continue;
+ }
+ //if (*inter_execPtr != 0)
+ // var_B = 0;
+
+ ind1 = *_vm->_global->_inter_execPtr >> 4;
+ ind2 = (*_vm->_global->_inter_execPtr & 0xf);
+
+ _vm->_draw->_unusedPalette1[i] =
+ ((_vm->_draw->_palLoadData1[ind1] + _vm->_draw->_palLoadData2[ind2]) << 8) +
+ (_vm->_draw->_palLoadData2[ind1] + _vm->_draw->_palLoadData1[ind2]);
+ }
+
+ _vm->_global->_pPaletteDesc->unused1 = _vm->_draw->_unusedPalette1;
+ }
+
+ switch (cmd) {
+ case 52:
+ for (i = 0; i < 16; i++, _vm->_global->_inter_execPtr += 3) {
+ _vm->_draw->_vgaSmallPalette[i].red = _vm->_global->_inter_execPtr[0];
+ _vm->_draw->_vgaSmallPalette[i].green = _vm->_global->_inter_execPtr[1];
+ _vm->_draw->_vgaSmallPalette[i].blue = _vm->_global->_inter_execPtr[2];
+ }
+ break;
+
+ case 50:
+ for (i = 0; i < 16; i++, _vm->_global->_inter_execPtr++)
+ _vm->_draw->_unusedPalette2[i] = *_vm->_global->_inter_execPtr;
+ break;
+
+ case 53:
+ palPtr = _vm->_game->loadTotResource(_vm->_inter->load16());
+ memcpy((char *)_vm->_draw->_vgaPalette, palPtr, 768);
+ break;
+
+ case 54:
+ memset((char *)_vm->_draw->_vgaPalette, 0, 768);
+ break;
+ }
+ if (!_vm->_draw->_applyPal) {
+ _vm->_global->_pPaletteDesc->unused2 = _vm->_draw->_unusedPalette2;
+ _vm->_global->_pPaletteDesc->unused1 = _vm->_draw->_unusedPalette1;
+
+ if (_vm->_global->_videoMode != 0x13)
+ _vm->_global->_pPaletteDesc->vgaPal = (Video::Color *)_vm->_draw->_vgaSmallPalette;
+ else
+ _vm->_global->_pPaletteDesc->vgaPal = (Video::Color *)_vm->_draw->_vgaPalette;
+
+ _vm->_palanim->fade((Video::PalDesc *) _vm->_global->_pPaletteDesc, 0, 0);
+ }
+ return false;
+}
+
+bool Inter_v1::o1_setcmdCount(char &cmdCount, int16 &counter, int16 &retFlag) {
+ cmdCount = *_vm->_global->_inter_execPtr++;
+ counter = 0;
+ return false;
+}
+
+bool Inter_v1::o1_return(char &cmdCount, int16 &counter, int16 &retFlag) {
+ if (retFlag != 2)
+ _breakFlag = true;
+
+ _vm->_global->_inter_execPtr = 0;
+ return false;
+}
+
+bool Inter_v1::o1_renewTimeInVars(char &cmdCount, int16 &counter, int16 &retFlag) {
+ renewTimeInVars();
+ return false;
+}
+
+bool Inter_v1::o1_speakerOn(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_snd->speakerOn(_vm->_parse->parseValExpr(), -1);
+ return false;
+}
+
+bool Inter_v1::o1_speakerOff(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_snd->speakerOff();
+ return false;
+}
+
+bool Inter_v1::o1_goblinFunc(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 cmd;
+ int16 extraData = 0;
+ Goblin::Gob_Object *objDesc = NULL;
+ int32 *retVarPtr;
+ bool objDescSet = false;
+
+ retVarPtr = (int32 *)VAR_ADDRESS(59);
+
+ cmd = load16();
+ _vm->_global->_inter_execPtr += 2;
+ if (cmd > 0 && cmd < 17) {
+ extraData = load16();
+ objDesc = _vm->_goblin->_objects[extraData];
+ objDescSet = true;
+ extraData = load16();
+ }
+
+ if (cmd > 90 && cmd < 107) {
+ extraData = load16();
+ objDesc = _vm->_goblin->_goblins[extraData];
+ objDescSet = true;
+ extraData = load16();
+ cmd -= 90;
+ }
+
+ if (cmd > 110 && cmd < 128) {
+ extraData = load16();
+ objDesc = _vm->_goblin->_goblins[extraData];
+ objDescSet = true;
+ cmd -= 90;
+ } else if (cmd > 20 && cmd < 38) {
+ extraData = load16();
+ objDesc = _vm->_goblin->_objects[extraData];
+ objDescSet = true;
+ }
+
+/*
+ NB: The original gobliiins engine did not initialize the objDesc
+ variable, so we manually check if objDesc is properly set before
+ checking if it is zero. If it was not set, we do not return. This
+ fixes a crash in the EGA version if the life bar is depleted, because
+ interFunc is called multiple times with cmd == 39.
+ Bug #1324814
+*/
+
+ if (cmd < 40 && objDescSet && objDesc == 0)
+ return false;
+
+ executeGoblinOpcode(cmd, extraData, retVarPtr, objDesc);
+
+ return false;
+}
+
+bool Inter_v1::o1_returnTo(char &cmdCount, int16 &counter, int16 &retFlag) {
+ if (retFlag == 1) {
+ _breakFlag = true;
+ _vm->_global->_inter_execPtr = 0;
+ return true;
+ }
+
+ if (*_nestLevel == 0)
+ return false;
+
+ *_breakFromLevel = *_nestLevel;
+ _breakFlag = true;
+ _vm->_global->_inter_execPtr = 0;
+ return true;
+}
+
+bool Inter_v1::o1_setBackDelta(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->_backDeltaX = _vm->_parse->parseValExpr();
+ _vm->_draw->_backDeltaY = _vm->_parse->parseValExpr();
+ return false;
+}
+
+bool Inter_v1::o1_loadSound(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_game->interLoadSound(-1);
+ return false;
+}
+
+bool Inter_v1::o1_freeSoundSlot(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_game->freeSoundSlot(-1);
+ return false;
+}
+
+bool Inter_v1::o1_waitEndPlay(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_snd->waitEndPlay();
+ return false;
+}
+
+bool Inter_v1::o1_animatePalette(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->blitInvalidated();
+ _vm->_util->waitEndFrame();
+ animPalette();
+ storeKey(_vm->_game->checkKeys(&_vm->_global->_inter_mouseX,
+ &_vm->_global->_inter_mouseY, &_vm->_game->_mouseButtons, 0));
+ return false;
+}
+
+bool Inter_v1::o1_animateCursor(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->animateCursor(1);
+ return false;
+}
+
+bool Inter_v1::o1_blitCursor(char &cmdCount, int16 &counter, int16 &retFlag) {
+ _vm->_draw->blitCursor();
+ return false;
+}
+
+void Inter_v1::o1_setState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->state = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemStateVarPtr = extraData;
+}
+
+void Inter_v1::o1_setCurFrame(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->curFrame = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemFrameVarPtr = extraData;
+}
+
+void Inter_v1::o1_setNextState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->nextState = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemNextStateVarPtr = extraData;
+}
+
+void Inter_v1::o1_setMultState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->multState = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemMultStateVarPtr = extraData;
+}
+
+void Inter_v1::o1_setOrder(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->order = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemOrderVarPtr = extraData;
+}
+
+void Inter_v1::o1_setActionStartState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->actionStartState = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemActStartStVarPtr = extraData;
+}
+
+void Inter_v1::o1_setCurLookDir(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->curLookDir = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemLookDirVarPtr = extraData;
+}
+
+void Inter_v1::o1_setType(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->type = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemTypeVarPtr = extraData;
+
+ if (extraData == 0)
+ objDesc->toRedraw = 1;
+}
+
+void Inter_v1::o1_setNoTick(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->noTick = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemNoTickVarPtr = extraData;
+}
+
+void Inter_v1::o1_setPickable(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->pickable = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemPickableVarPtr = extraData;
+}
+
+void Inter_v1::o1_setXPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->xPos = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemScrXVarPtr = extraData;
+}
+
+void Inter_v1::o1_setYPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->yPos = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemScrYVarPtr = extraData;
+}
+
+void Inter_v1::o1_setDoAnim(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->doAnim = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemDoAnimVarPtr = extraData;
+}
+
+void Inter_v1::o1_setRelaxTime(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->relaxTime = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemRelaxVarPtr = extraData;
+}
+
+void Inter_v1::o1_setMaxTick(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ objDesc->maxTick = extraData;
+ if (objDesc == _vm->_goblin->_actDestItemDesc)
+ *_vm->_goblin->_destItemMaxTickVarPtr = extraData;
+}
+
+void Inter_v1::o1_getState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->state;
+}
+
+void Inter_v1::o1_getCurFrame(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->curFrame;
+}
+
+void Inter_v1::o1_getNextState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->nextState;
+}
+
+void Inter_v1::o1_getMultState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->multState;
+}
+
+void Inter_v1::o1_getOrder(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->order;
+}
+
+void Inter_v1::o1_getActionStartState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->actionStartState;
+}
+
+void Inter_v1::o1_getCurLookDir(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->curLookDir;
+}
+
+void Inter_v1::o1_getType(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->type;
+}
+
+void Inter_v1::o1_getNoTick(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->noTick;
+}
+
+void Inter_v1::o1_getPickable(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->pickable;
+}
+
+void Inter_v1::o1_getObjMaxFrame(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = _vm->_goblin->getObjMaxFrame(objDesc);
+}
+
+void Inter_v1::o1_getXPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->xPos;
+}
+
+void Inter_v1::o1_getYPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->yPos;
+}
+
+void Inter_v1::o1_getDoAnim(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->doAnim;
+}
+
+void Inter_v1::o1_getRelaxTime(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->relaxTime;
+}
+
+void Inter_v1::o1_getMaxTick(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = objDesc->maxTick;
+}
+
+void Inter_v1::o1_manipulateMap(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 xPos = load16();
+ int16 yPos = load16();
+ int16 item = load16();
+
+ manipulateMap(xPos, yPos, item);
+}
+
+void Inter_v1::o1_getItem(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 xPos = load16();
+ int16 yPos = load16();
+
+ if ((_vm->_map->_itemsMap[yPos][xPos] & 0xff00) != 0)
+ *retVarPtr = (_vm->_map->_itemsMap[yPos][xPos] & 0xff00) >> 8;
+ else
+ *retVarPtr = _vm->_map->_itemsMap[yPos][xPos];
+}
+
+void Inter_v1::o1_manipulateMapIndirect(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 xPos = load16();
+ int16 yPos = load16();
+ int16 item = load16();
+
+ xPos = VAR(xPos);
+ yPos = VAR(yPos);
+ item = VAR(item);
+
+ manipulateMap(xPos, yPos, item);
+}
+
+void Inter_v1::o1_getItemIndirect(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 xPos = load16();
+ int16 yPos = load16();
+
+ xPos = VAR(xPos);
+ yPos = VAR(yPos);
+
+ if ((_vm->_map->_itemsMap[yPos][xPos] & 0xff00) != 0)
+ *retVarPtr = (_vm->_map->_itemsMap[yPos][xPos] & 0xff00) >> 8;
+ else
+ *retVarPtr = _vm->_map->_itemsMap[yPos][xPos];
+}
+
+void Inter_v1::o1_setPassMap(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 xPos = load16();
+ int16 yPos = load16();
+ int16 val = load16();
+ _vm->_map->_passMap[yPos][xPos] = val;
+}
+
+void Inter_v1::o1_setGoblinPosH(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 layer;
+ int16 item = load16();
+ int16 xPos = load16();
+ int16 yPos = load16();
+
+ _vm->_goblin->_gobPositions[item].x = xPos * 2;
+ _vm->_goblin->_gobPositions[item].y = yPos * 2;
+
+ objDesc = _vm->_goblin->_goblins[item];
+ objDesc->nextState = 21;
+
+ _vm->_goblin->nextLayer(objDesc);
+
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+
+ _vm->_scenery->updateAnim(layer, 0, objDesc->animation, 0,
+ objDesc->xPos, objDesc->yPos, 0);
+
+ objDesc->yPos =
+ (_vm->_goblin->_gobPositions[item].y * 6 + 6) - (_vm->_scenery->_toRedrawBottom -
+ _vm->_scenery->_animTop);
+ objDesc->xPos =
+ _vm->_goblin->_gobPositions[item].x * 12 - (_vm->_scenery->_toRedrawLeft -
+ _vm->_scenery->_animLeft);
+
+ objDesc->curFrame = 0;
+ objDesc->state = 21;
+ if (_vm->_goblin->_currentGoblin == item) {
+ *_vm->_goblin->_curGobScrXVarPtr = objDesc->xPos;
+ *_vm->_goblin->_curGobScrYVarPtr = objDesc->yPos;
+
+ *_vm->_goblin->_curGobFrameVarPtr = 0;
+ *_vm->_goblin->_curGobStateVarPtr = 18;
+ _vm->_goblin->_pressedMapX = _vm->_goblin->_gobPositions[item].x;
+ _vm->_goblin->_pressedMapY = _vm->_goblin->_gobPositions[item].y;
+ }
+}
+
+void Inter_v1::o1_getGoblinPosXH(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 item = load16();
+ *retVarPtr = _vm->_goblin->_gobPositions[item].x >> 1;
+}
+
+void Inter_v1::o1_getGoblinPosYH(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 item = load16();
+ *retVarPtr = _vm->_goblin->_gobPositions[item].y >> 1;
+}
+
+void Inter_v1::o1_setGoblinMultState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 layer;
+ int16 item = load16();
+ int16 xPos = load16();
+ int16 yPos = load16();
+
+ objDesc = _vm->_goblin->_goblins[item];
+ if (yPos == 0) {
+ objDesc->multState = xPos;
+ objDesc->nextState = xPos;
+ _vm->_goblin->nextLayer(objDesc);
+
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+
+ objDesc->xPos =
+ _vm->_scenery->_animations[objDesc->animation].layers[layer]->
+ posX;
+ objDesc->yPos =
+ _vm->_scenery->_animations[objDesc->animation].layers[layer]->
+ posY;
+
+ *_vm->_goblin->_curGobScrXVarPtr = objDesc->xPos;
+ *_vm->_goblin->_curGobScrYVarPtr = objDesc->yPos;
+ *_vm->_goblin->_curGobFrameVarPtr = 0;
+ *_vm->_goblin->_curGobStateVarPtr = objDesc->state;
+ *_vm->_goblin->_curGobNextStateVarPtr = objDesc->nextState;
+ *_vm->_goblin->_curGobMultStateVarPtr = objDesc->multState;
+ *_vm->_goblin->_curGobMaxFrameVarPtr =
+ _vm->_goblin->getObjMaxFrame(objDesc);
+ _vm->_goblin->_noPick = 1;
+ return;
+ }
+
+ objDesc->multState = 21;
+ objDesc->nextState = 21;
+ objDesc->state = 21;
+ _vm->_goblin->nextLayer(objDesc);
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+
+ _vm->_scenery->updateAnim(layer, 0, objDesc->animation, 0,
+ objDesc->xPos, objDesc->yPos, 0);
+
+ objDesc->yPos =
+ (yPos * 6 + 6) - (_vm->_scenery->_toRedrawBottom - _vm->_scenery->_animTop);
+ objDesc->xPos =
+ xPos * 12 - (_vm->_scenery->_toRedrawLeft - _vm->_scenery->_animLeft);
+
+ _vm->_goblin->_gobPositions[item].x = xPos;
+ _vm->_goblin->_pressedMapX = xPos;
+ _vm->_map->_curGoblinX = xPos;
+
+ _vm->_goblin->_gobPositions[item].y = yPos;
+ _vm->_goblin->_pressedMapY = yPos;
+ _vm->_map->_curGoblinY = yPos;
+
+ *_vm->_goblin->_curGobScrXVarPtr = objDesc->xPos;
+ *_vm->_goblin->_curGobScrYVarPtr = objDesc->yPos;
+ *_vm->_goblin->_curGobFrameVarPtr = 0;
+ *_vm->_goblin->_curGobStateVarPtr = 21;
+ *_vm->_goblin->_curGobNextStateVarPtr = 21;
+ *_vm->_goblin->_curGobMultStateVarPtr = -1;
+ _vm->_goblin->_noPick = 0;
+}
+
+void Inter_v1::o1_setGoblinPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 layer;
+ int16 item = load16();
+ int16 xPos = load16();
+ int16 yPos = load16();
+
+ _vm->_goblin->_gobPositions[item].x = xPos;
+ _vm->_goblin->_gobPositions[item].y = yPos;
+
+ objDesc = _vm->_goblin->_goblins[item];
+ objDesc->nextState = 21;
+ _vm->_goblin->nextLayer(objDesc);
+
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+
+ _vm->_scenery->updateAnim(layer, 0, objDesc->animation, 0,
+ objDesc->xPos, objDesc->yPos, 0);
+
+ objDesc->yPos =
+ (yPos * 6 + 6) - (_vm->_scenery->_toRedrawBottom - _vm->_scenery->_animTop);
+ objDesc->xPos =
+ xPos * 12 - (_vm->_scenery->_toRedrawLeft - _vm->_scenery->_animLeft);
+
+ objDesc->curFrame = 0;
+ objDesc->state = 21;
+
+ if (_vm->_goblin->_currentGoblin == item) {
+ *_vm->_goblin->_curGobScrXVarPtr = objDesc->xPos;
+ *_vm->_goblin->_curGobScrYVarPtr = objDesc->yPos;
+ *_vm->_goblin->_curGobFrameVarPtr = 0;
+ *_vm->_goblin->_curGobStateVarPtr = 18;
+
+ _vm->_goblin->_pressedMapX = _vm->_goblin->_gobPositions[item].x;
+ _vm->_goblin->_pressedMapY = _vm->_goblin->_gobPositions[item].y;
+ }
+}
+
+void Inter_v1::o1_setGoblinState(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 layer;
+ int16 item = load16();
+ int16 state = load16();
+
+ objDesc = _vm->_goblin->_goblins[item];
+ objDesc->nextState = state;
+
+ _vm->_goblin->nextLayer(objDesc);
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+
+ objDesc->xPos =
+ _vm->_scenery->_animations[objDesc->animation].layers[layer]->posX;
+ objDesc->yPos =
+ _vm->_scenery->_animations[objDesc->animation].layers[layer]->posY;
+
+ if (item == _vm->_goblin->_currentGoblin) {
+ *_vm->_goblin->_curGobScrXVarPtr = objDesc->xPos;
+ *_vm->_goblin->_curGobScrYVarPtr = objDesc->yPos;
+ *_vm->_goblin->_curGobFrameVarPtr = 0;
+ *_vm->_goblin->_curGobStateVarPtr = objDesc->state;
+ *_vm->_goblin->_curGobMultStateVarPtr = objDesc->multState;
+ }
+}
+
+void Inter_v1::o1_setGoblinStateRedraw(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 layer;
+ int16 item = load16();
+ int16 state = load16();
+ objDesc = _vm->_goblin->_objects[item];
+
+ objDesc->nextState = state;
+
+ _vm->_goblin->nextLayer(objDesc);
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+ objDesc->xPos =
+ _vm->_scenery->_animations[objDesc->animation].layers[layer]->posX;
+ objDesc->yPos =
+ _vm->_scenery->_animations[objDesc->animation].layers[layer]->posY;
+
+ objDesc->toRedraw = 1;
+ objDesc->type = 0;
+ if (objDesc == _vm->_goblin->_actDestItemDesc) {
+ *_vm->_goblin->_destItemScrXVarPtr = objDesc->xPos;
+ *_vm->_goblin->_destItemScrYVarPtr = objDesc->yPos;
+
+ *_vm->_goblin->_destItemStateVarPtr = objDesc->state;
+ *_vm->_goblin->_destItemNextStateVarPtr = -1;
+ *_vm->_goblin->_destItemMultStateVarPtr = -1;
+ *_vm->_goblin->_destItemFrameVarPtr = 0;
+ }
+}
+
+void Inter_v1::o1_setGoblinUnk14(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 item = load16();
+ int16 val = load16();
+ objDesc = _vm->_goblin->_objects[item];
+ objDesc->unk14 = val;
+}
+
+void Inter_v1::o1_setItemIdInPocket(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_goblin->_itemIdInPocket = load16();
+}
+
+void Inter_v1::o1_setItemIndInPocket(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_goblin->_itemIndInPocket = load16();
+}
+
+void Inter_v1::o1_getItemIdInPocket(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = _vm->_goblin->_itemIdInPocket;
+}
+
+void Inter_v1::o1_getItemIndInPocket(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ *retVarPtr = _vm->_goblin->_itemIndInPocket;
+}
+
+void Inter_v1::o1_setItemPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 item = load16();
+ int16 xPos = load16();
+ int16 yPos = load16();
+ int16 val = load16();
+
+ _vm->_map->_itemPoses[item].x = xPos;
+ _vm->_map->_itemPoses[item].y = yPos;
+ _vm->_map->_itemPoses[item].orient = val;
+}
+
+void Inter_v1::o1_decRelaxTime(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ extraData = load16();
+ objDesc = _vm->_goblin->_objects[extraData];
+
+ objDesc->relaxTime--;
+ if (objDesc->relaxTime < 0 &&
+ _vm->_goblin->getObjMaxFrame(objDesc) == objDesc->curFrame) {
+ objDesc->relaxTime = _vm->_util->getRandom(100) + 50;
+ objDesc->curFrame = 0;
+ objDesc->toRedraw = 1;
+ }
+}
+
+void Inter_v1::o1_getGoblinPosX(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 item = load16();
+ *retVarPtr = _vm->_goblin->_gobPositions[item].x;
+}
+
+void Inter_v1::o1_getGoblinPosY(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 item = load16();
+ *retVarPtr = _vm->_goblin->_gobPositions[item].y;
+}
+
+void Inter_v1::o1_clearPathExistence(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_goblin->_pathExistence = 0;
+}
+
+void Inter_v1::o1_setGoblinVisible(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ extraData = load16();
+ _vm->_goblin->_goblins[extraData]->visible = 1;
+}
+
+void Inter_v1::o1_setGoblinInvisible(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ extraData = load16();
+ _vm->_goblin->_goblins[extraData]->visible = 0;
+}
+
+void Inter_v1::o1_getObjectIntersect(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ extraData = load16();
+ int16 item = load16();
+
+ objDesc = _vm->_goblin->_objects[extraData];
+ if (_vm->_goblin->objIntersected(objDesc, _vm->_goblin->_goblins[item]) != 0)
+ *retVarPtr = 1;
+ else
+ *retVarPtr = 0;
+}
+
+void Inter_v1::o1_getGoblinIntersect(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ extraData = load16();
+ int16 item = load16();
+
+ objDesc = _vm->_goblin->_goblins[extraData];
+ if (_vm->_goblin->objIntersected(objDesc, _vm->_goblin->_goblins[item]) != 0)
+ *retVarPtr = 1;
+ else
+ *retVarPtr = 0;
+}
+
+void Inter_v1::o1_loadObjects(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ extraData = load16();
+ if (_vm->_game->_extHandle >= 0)
+ _vm->_dataio->closeData(_vm->_game->_extHandle);
+
+ _vm->_goblin->loadObjects((char *)VAR_ADDRESS(extraData));
+ _vm->_game->_extHandle = _vm->_dataio->openData(_vm->_game->_curExtFile);
+}
+
+void Inter_v1::o1_freeObjects(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_goblin->freeAllObjects();
+}
+
+void Inter_v1::o1_animateObjects(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_goblin->animateObjects();
+}
+
+void Inter_v1::o1_drawObjects(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_goblin->drawObjects();
+
+ if (_vm->_features & GF_MAC)
+ _vm->_music->playBgMusic();
+ else if (_vm->_cdrom->getTrackPos() == -1)
+ _vm->_cdrom->playBgMusic();
+}
+
+void Inter_v1::o1_loadMap(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_map->loadMapsInitGobs();
+}
+
+void Inter_v1::o1_moveGoblin(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ int16 item;
+ extraData = load16();
+ int16 xPos = load16();
+
+ if ((uint16)VAR(xPos) == 0) {
+ item =
+ _vm->_goblin->doMove(_vm->_goblin->_goblins[_vm->_goblin->_currentGoblin], 1,
+ (uint16)VAR(extraData));
+ } else {
+ item =
+ _vm->_goblin->doMove(_vm->_goblin->_goblins[_vm->_goblin->_currentGoblin], 1, 3);
+ }
+
+ if (item != 0)
+ _vm->_goblin->switchGoblin(item);
+}
+
+void Inter_v1::o1_switchGoblin(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_goblin->switchGoblin(0);
+}
+
+void Inter_v1::o1_loadGoblin(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_goblin->loadGobDataFromVars();
+}
+
+void Inter_v1::o1_writeTreatItem(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ extraData = load16();
+ int16 cmd = load16();
+ int16 xPos = load16();
+
+ if ((uint16)VAR(xPos) == 0) {
+ WRITE_VAR(cmd, _vm->_goblin->treatItem((uint16)VAR(extraData)));
+ return;
+ }
+
+ WRITE_VAR(cmd, _vm->_goblin->treatItem(3));
+}
+
+void Inter_v1::o1_moveGoblin0(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ _vm->_goblin->doMove(_vm->_goblin->_goblins[_vm->_goblin->_currentGoblin], 0, 0);
+}
+
+void Inter_v1::o1_setGoblinTarget(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ extraData = load16();
+ if (VAR(extraData) != 0)
+ _vm->_goblin->_goesAtTarget = 1;
+ else
+ _vm->_goblin->_goesAtTarget = 0;
+}
+
+void Inter_v1::o1_setGoblinObjectsPos(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ extraData = load16();
+ extraData = VAR(extraData);
+ _vm->_goblin->_objects[10]->xPos = extraData;
+
+ extraData = load16();
+ extraData = VAR(extraData);
+ _vm->_goblin->_objects[10]->yPos = extraData;
+}
+
+void Inter_v1::o1_initGoblin(int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ Goblin::Gob_Object *gobDesc = _vm->_goblin->_goblins[0];
+ int16 xPos;
+ int16 yPos;
+ int16 layer;
+
+ if (_vm->_goblin->_currentGoblin != 0) {
+ _vm->_goblin->_goblins[_vm->_goblin->_currentGoblin]->doAnim = 1;
+ _vm->_goblin->_goblins[_vm->_goblin->_currentGoblin]->nextState = 21;
+
+ _vm->_goblin->nextLayer(_vm->_goblin->_goblins[_vm->_goblin->_currentGoblin]);
+ _vm->_goblin->_currentGoblin = 0;
+
+ gobDesc->doAnim = 0;
+ gobDesc->type = 0;
+ gobDesc->toRedraw = 1;
+
+ _vm->_goblin->_pressedMapX = _vm->_goblin->_gobPositions[0].x;
+ _vm->_map->_destX = _vm->_goblin->_gobPositions[0].x;
+ _vm->_goblin->_gobDestX = _vm->_goblin->_gobPositions[0].x;
+
+ _vm->_goblin->_pressedMapY = _vm->_goblin->_gobPositions[0].y;
+ _vm->_map->_destY = _vm->_goblin->_gobPositions[0].y;
+ _vm->_goblin->_gobDestY = _vm->_goblin->_gobPositions[0].y;
+
+ *_vm->_goblin->_curGobVarPtr = 0;
+ _vm->_goblin->_pathExistence = 0;
+ _vm->_goblin->_readyToAct = 0;
+ }
+
+ if (gobDesc->state != 10 && _vm->_goblin->_itemIndInPocket != -1 &&
+ _vm->_goblin->getObjMaxFrame(gobDesc) == gobDesc->curFrame) {
+
+ gobDesc->stateMach = gobDesc->realStateMach;
+ xPos = _vm->_goblin->_gobPositions[0].x;
+ yPos = _vm->_goblin->_gobPositions[0].y;
+
+ gobDesc->nextState = 10;
+ layer = _vm->_goblin->nextLayer(gobDesc);
+
+ _vm->_scenery->updateAnim(layer, 0, gobDesc->animation, 0,
+ gobDesc->xPos, gobDesc->yPos, 0);
+
+ gobDesc->yPos =
+ (yPos * 6 + 6) - (_vm->_scenery->_toRedrawBottom -
+ _vm->_scenery->_animTop);
+ gobDesc->xPos =
+ xPos * 12 - (_vm->_scenery->_toRedrawLeft - _vm->_scenery->_animLeft);
+ }
+
+ if (gobDesc->state != 10)
+ return;
+
+ if (_vm->_goblin->_itemIndInPocket == -1)
+ return;
+
+ if (gobDesc->curFrame != 10)
+ return;
+
+ objDesc = _vm->_goblin->_objects[_vm->_goblin->_itemIndInPocket];
+ objDesc->type = 0;
+ objDesc->toRedraw = 1;
+ objDesc->curFrame = 0;
+
+ objDesc->order = gobDesc->order;
+ objDesc->animation =
+ objDesc->stateMach[objDesc->state][0]->animation;
+
+ layer = objDesc->stateMach[objDesc->state][0]->layer;
+
+ _vm->_scenery->updateAnim(layer, 0, objDesc->animation, 0,
+ objDesc->xPos, objDesc->yPos, 0);
+
+ objDesc->yPos +=
+ (_vm->_goblin->_gobPositions[0].y * 6 + 5) - _vm->_scenery->_toRedrawBottom;
+
+ if (gobDesc->curLookDir == 4) {
+ objDesc->xPos += _vm->_goblin->_gobPositions[0].x * 12 + 14
+ - (_vm->_scenery->_toRedrawLeft + _vm->_scenery->_toRedrawRight) / 2;
+ } else {
+ objDesc->xPos += _vm->_goblin->_gobPositions[0].x * 12
+ - (_vm->_scenery->_toRedrawLeft + _vm->_scenery->_toRedrawRight) / 2;
+ }
+
+ _vm->_goblin->_itemIndInPocket = -1;
+ _vm->_goblin->_itemIdInPocket = -1;
+ _vm->_util->beep(50);
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/inter_v2.cpp b/engines/gob/inter_v2.cpp
new file mode 100644
index 0000000000..672a662670
--- /dev/null
+++ b/engines/gob/inter_v2.cpp
@@ -0,0 +1,997 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/inter.h"
+#include "gob/util.h"
+#include "gob/scenery.h"
+#include "gob/parse.h"
+#include "gob/game.h"
+#include "gob/draw.h"
+#include "gob/mult.h"
+#include "gob/goblin.h"
+#include "gob/cdrom.h"
+#include "gob/palanim.h"
+
+namespace Gob {
+
+#define OPCODE(x) _OPCODE(Inter_v2, x)
+
+const int Inter_v2::_goblinFuncLookUp[][2] = {
+ {1, 0},
+ {2, 1},
+ {3, 2},
+ {4, 3},
+ {5, 4},
+ {6, 5},
+ {7, 6},
+ {8, 7},
+ {9, 8},
+ {10, 9},
+ {12, 10},
+ {13, 11},
+ {14, 12},
+ {15, 13},
+ {16, 14},
+ {21, 15},
+ {22, 16},
+ {23, 17},
+ {24, 18},
+ {25, 19},
+ {26, 20},
+ {27, 21},
+ {28, 22},
+ {29, 23},
+ {30, 24},
+ {32, 25},
+ {33, 26},
+ {34, 27},
+ {35, 28},
+ {36, 29},
+ {37, 30},
+ {40, 31},
+ {41, 32},
+ {42, 33},
+ {43, 34},
+ {44, 35},
+ {50, 36},
+ {52, 37},
+ {53, 38},
+ {150, 39},
+ {152, 40},
+ {200, 41},
+ {201, 42},
+ {202, 43},
+ {203, 44},
+ {204, 45},
+ {250, 46},
+ {251, 47},
+ {252, 48},
+ {500, 49},
+ {502, 50},
+ {503, 51},
+ {600, 52},
+ {601, 53},
+ {602, 54},
+ {603, 55},
+ {604, 56},
+ {605, 57},
+ {1000, 58},
+ {1001, 59},
+ {1002, 60},
+ {1003, 61},
+ {1004, 62},
+ {1005, 63},
+ {1006, 64},
+ {1008, 65},
+ {1009, 66},
+ {1010, 67},
+ {1011, 68},
+ {1015, 69},
+ {2005, 70}
+};
+
+Inter_v2::Inter_v2(GobEngine *vm) : Inter_v1(vm) {
+ setupOpcodes();
+}
+
+void Inter_v2::setupOpcodes(void) {
+ static const OpcodeDrawEntryV2 opcodesDraw[256] = {
+ /* 00 */
+ OPCODE(o1_loadMult),
+ OPCODE(o1_playMult),
+ OPCODE(o1_freeMult),
+ {NULL, ""},
+ /* 04 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ OPCODE(o1_initCursor),
+ /* 08 */
+ OPCODE(o1_initCursorAnim),
+ OPCODE(o1_clearCursorAnim),
+ OPCODE(o2_setRenderFlags),
+ {NULL, ""},
+ /* 0C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 10 */
+ OPCODE(o1_loadAnim),
+ OPCODE(o1_freeAnim),
+ OPCODE(o1_updateAnim),
+ OPCODE(o2_drawStub),
+ /* 14 */
+ OPCODE(o1_initMult),
+ OPCODE(o1_multFreeMult),
+ OPCODE(o1_animate),
+ OPCODE(o1_multLoadMult),
+ /* 18 */
+ OPCODE(o1_storeParams),
+ OPCODE(o1_getObjAnimSize),
+ OPCODE(o1_loadStatic),
+ OPCODE(o1_freeStatic),
+ /* 1C */
+ OPCODE(o1_renderStatic),
+ OPCODE(o1_loadCurLayer),
+ {NULL, ""},
+ {NULL, ""},
+ /* 20 */
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_stub0x23),
+ /* 24 */
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ {NULL, ""},
+ {NULL, ""},
+ /* 28 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 2C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 30 */
+ OPCODE(o1_loadFontToSprite),
+ OPCODE(o1_freeFontToSprite),
+ {NULL, ""},
+ {NULL, ""},
+ /* 34 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 38 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 3C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 40 */
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ /* 44 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 48 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 4C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 50 */
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ /* 54 */
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ {NULL, ""},
+ /* 58 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 5C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 60 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 64 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 68 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 6C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 70 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 74 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 78 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 7C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 80 */
+ OPCODE(o2_stub0x80),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ /* 84 */
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ OPCODE(o2_drawStub),
+ /* 88 */
+ OPCODE(o2_drawStub),
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 8C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 90 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 94 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 98 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 9C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* A0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* A4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* A8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* AC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* B0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* B4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* B8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* BC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* C0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* C4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* C8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* CC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* D0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* D4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* D8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* DC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* E0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* E4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* E8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* EC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* F0 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* F4 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* F8 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* FC */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""}
+ };
+
+ static const OpcodeFuncEntryV2 opcodesFunc[80] = {
+ /* 00 */
+ OPCODE(o1_callSub),
+ OPCODE(o1_callSub),
+ OPCODE(o1_drawPrintText),
+ OPCODE(o1_loadCursor),
+ /* 04 */
+ {NULL, ""},
+ OPCODE(o1_call),
+ OPCODE(o1_repeatUntil),
+ OPCODE(o1_whileDo),
+ /* 08 */
+ OPCODE(o1_callBool),
+ OPCODE(o2_evaluateStore),
+ OPCODE(o1_loadSpriteToPos),
+ {NULL, ""},
+ /* 0C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 10 */
+ {NULL, ""},
+ OPCODE(o1_printText),
+ OPCODE(o2_loadTot),
+ OPCODE(o2_palLoad),
+ /* 14 */
+ OPCODE(o1_keyFunc),
+ OPCODE(o1_capturePush),
+ OPCODE(o1_capturePop),
+ OPCODE(o1_animPalInit),
+ /* 18 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 1C */
+ {NULL, ""},
+ {NULL, ""},
+ OPCODE(o1_drawOperations),
+ OPCODE(o1_setcmdCount),
+ /* 20 */
+ OPCODE(o1_return),
+ OPCODE(o1_renewTimeInVars),
+ OPCODE(o1_speakerOn),
+ OPCODE(o1_speakerOff),
+ /* 24 */
+ OPCODE(o1_putPixel),
+ OPCODE(o1_goblinFunc),
+ OPCODE(o1_createSprite),
+ OPCODE(o1_freeSprite),
+ /* 28 */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 2C */
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ {NULL, ""},
+ /* 30 */
+ OPCODE(o1_returnTo),
+ OPCODE(o1_loadSpriteContent),
+ OPCODE(o1_copySprite),
+ OPCODE(o1_fillRect),
+ /* 34 */
+ OPCODE(o1_drawLine),
+ OPCODE(o1_strToLong),
+ OPCODE(o1_invalidate),
+ OPCODE(o1_setBackDelta),
+ /* 38 */
+ OPCODE(o1_playSound),
+ OPCODE(o1_stopSound),
+ OPCODE(o1_loadSound),
+ OPCODE(o1_freeSoundSlot),
+ /* 3C */
+ OPCODE(o1_waitEndPlay),
+ OPCODE(o1_playComposition),
+ OPCODE(o1_getFreeMem),
+ OPCODE(o1_checkData),
+ /* 40 */
+ {NULL, ""},
+ OPCODE(o1_prepareStr),
+ OPCODE(o1_insertStr),
+ OPCODE(o1_cutStr),
+ /* 44 */
+ OPCODE(o1_strstr),
+ OPCODE(o1_istrlen),
+ OPCODE(o1_setMousePos),
+ OPCODE(o1_setFrameRate),
+ /* 48 */
+ OPCODE(o1_animatePalette),
+ OPCODE(o1_animateCursor),
+ OPCODE(o1_blitCursor),
+ OPCODE(o1_loadFont),
+ /* 4C */
+ OPCODE(o1_freeFont),
+ OPCODE(o1_readData),
+ OPCODE(o1_writeData),
+ OPCODE(o1_manageDataFile),
+ };
+
+ static const OpcodeGoblinEntryV2 opcodesGoblin[71] = {
+ /* 00 */
+ OPCODE(o1_setState),
+ OPCODE(o1_setCurFrame),
+ OPCODE(o1_setNextState),
+ OPCODE(o1_setMultState),
+ /* 04 */
+ OPCODE(o1_setOrder),
+ OPCODE(o1_setActionStartState),
+ OPCODE(o1_setCurLookDir),
+ OPCODE(o1_setType),
+ /* 08 */
+ OPCODE(o1_setNoTick),
+ OPCODE(o1_setPickable),
+ OPCODE(o1_setXPos),
+ OPCODE(o1_setYPos),
+ /* 0C */
+ OPCODE(o1_setDoAnim),
+ OPCODE(o1_setRelaxTime),
+ OPCODE(o1_setMaxTick),
+ OPCODE(o1_getState),
+ /* 10 */
+ OPCODE(o1_getCurFrame),
+ OPCODE(o1_getNextState),
+ OPCODE(o1_getMultState),
+ OPCODE(o1_getOrder),
+ /* 14 */
+ OPCODE(o1_getActionStartState),
+ OPCODE(o1_getCurLookDir),
+ OPCODE(o1_getType),
+ OPCODE(o1_getNoTick),
+ /* 18 */
+ OPCODE(o1_getPickable),
+ OPCODE(o1_getObjMaxFrame),
+ OPCODE(o1_getXPos),
+ OPCODE(o1_getYPos),
+ /* 1C */
+ OPCODE(o1_getDoAnim),
+ OPCODE(o1_getRelaxTime),
+ OPCODE(o1_getMaxTick),
+ OPCODE(o1_manipulateMap),
+ /* 20 */
+ OPCODE(o1_getItem),
+ OPCODE(o1_manipulateMapIndirect),
+ OPCODE(o1_getItemIndirect),
+ OPCODE(o1_setPassMap),
+ /* 24 */
+ OPCODE(o1_setGoblinPosH),
+ OPCODE(o1_getGoblinPosXH),
+ OPCODE(o1_getGoblinPosYH),
+ OPCODE(o1_setGoblinMultState),
+ /* 28 */
+ OPCODE(o1_setGoblinUnk14),
+ OPCODE(o1_setItemIdInPocket),
+ OPCODE(o1_setItemIndInPocket),
+ OPCODE(o1_getItemIdInPocket),
+ /* 2C */
+ OPCODE(o1_getItemIndInPocket),
+ OPCODE(o1_setItemPos),
+ OPCODE(o1_setGoblinPos),
+ OPCODE(o1_setGoblinState),
+ /* 30 */
+ OPCODE(o1_setGoblinStateRedraw),
+ OPCODE(o1_decRelaxTime),
+ OPCODE(o1_getGoblinPosX),
+ OPCODE(o1_getGoblinPosY),
+ /* 34 */
+ OPCODE(o1_clearPathExistence),
+ OPCODE(o1_setGoblinVisible),
+ OPCODE(o1_setGoblinInvisible),
+ OPCODE(o1_getObjectIntersect),
+ /* 38 */
+ OPCODE(o1_getGoblinIntersect),
+ OPCODE(o1_setItemPos),
+ OPCODE(o1_loadObjects),
+ OPCODE(o1_freeObjects),
+ /* 3C */
+ OPCODE(o1_animateObjects),
+ OPCODE(o1_drawObjects),
+ OPCODE(o1_loadMap),
+ OPCODE(o1_moveGoblin),
+ /* 40 */
+ OPCODE(o1_switchGoblin),
+ OPCODE(o1_loadGoblin),
+ OPCODE(o1_writeTreatItem),
+ OPCODE(o1_moveGoblin0),
+ /* 44 */
+ OPCODE(o1_setGoblinTarget),
+ OPCODE(o1_setGoblinObjectsPos),
+ OPCODE(o1_initGoblin)
+ };
+
+ _opcodesDrawV2 = opcodesDraw;
+ _opcodesFuncV2 = opcodesFunc;
+ _opcodesGoblinV2 = opcodesGoblin;
+}
+
+void Inter_v2::executeDrawOpcode(byte i) {
+ debug(4, "opcodeDraw %d (%s)", i, getOpcodeDrawDesc(i));
+
+ OpcodeDrawProcV2 op = _opcodesDrawV2[i].proc;
+
+ if (op == NULL)
+ warning("unimplemented opcodeDraw: %d", i);
+ else
+ (this->*op) ();
+}
+
+bool Inter_v2::executeFuncOpcode(byte i, byte j, char &cmdCount, int16 &counter, int16 &retFlag) {
+ debug(4, "opcodeFunc %d (%s)", i, getOpcodeFuncDesc(i, j));
+
+ if ((i > 4) || (j > 15)) {
+ warning("unimplemented opcodeFunc: %d.%d", i, j);
+ return false;
+ }
+
+ OpcodeFuncProcV2 op = _opcodesFuncV2[i*16 + j].proc;
+
+ if (op == NULL)
+ warning("unimplemented opcodeFunc: %d.%d", i, j);
+ else
+ return (this->*op) (cmdCount, counter, retFlag);
+ return false;
+}
+
+void Inter_v2::executeGoblinOpcode(int i, int16 &extraData, int32 *retVarPtr, Goblin::Gob_Object *objDesc) {
+ debug(4, "opcodeGoblin %d (%s)", i, getOpcodeGoblinDesc(i));
+
+ OpcodeGoblinProcV2 op = NULL;
+
+ for (int j = 0; j < ARRAYSIZE(_goblinFuncLookUp); j++)
+ if (_goblinFuncLookUp[j][0] == i) {
+ op = _opcodesGoblinV2[_goblinFuncLookUp[j][1]].proc;
+ break;
+ }
+
+ if (op == NULL) {
+ warning("unimplemented opcodeGoblin: %d", i);
+ _vm->_global->_inter_execPtr -= 2;
+ _vm->_global->_inter_execPtr += load16() * 2;
+ }
+ else
+ (this->*op) (extraData, retVarPtr, objDesc);
+}
+
+const char *Inter_v2::getOpcodeDrawDesc(byte i) {
+ return _opcodesDrawV2[i].desc;
+}
+
+const char *Inter_v2::getOpcodeFuncDesc(byte i, byte j) {
+ if ((i > 4) || (j > 15))
+ return "";
+
+ return _opcodesFuncV2[i*16 + j].desc;
+}
+
+const char *Inter_v2::getOpcodeGoblinDesc(int i) {
+ for (int j = 0; j < ARRAYSIZE(_goblinFuncLookUp); j++)
+ if (_goblinFuncLookUp[j][0] == i)
+ return _opcodesGoblinV2[_goblinFuncLookUp[j][1]].desc;
+ return "";
+}
+
+void Inter_v2::o2_stub0x80(void) {
+ _vm->_global->_inter_execPtr += 2;
+
+ int16 expr1 = _vm->_parse->parseValExpr();
+ int16 expr2 = _vm->_parse->parseValExpr();
+
+ warning("STUB: Gob2 drawOperation 0x80 (%d %d)", expr1, expr2);
+}
+
+void Inter_v2::o2_stub0x23(void) {
+ byte result;
+ char str[40];
+
+ result = evalExpr(NULL);
+ strcpy(str, _vm->_global->_inter_resStr);
+
+ warning("STUB: Gob2 drawOperation 0x23 (%d, \"%s\")", result, str);
+}
+
+bool Inter_v2::o2_evaluateStore(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char *savedPos;
+ int16 varOff;
+ int16 token;
+ int16 result;
+ byte loopCount;
+
+ savedPos = _vm->_global->_inter_execPtr;
+ varOff = _vm->_parse->parseVarIndex();
+
+ if (*_vm->_global->_inter_execPtr == 99) {
+ _vm->_global->_inter_execPtr++;
+ loopCount = *_vm->_global->_inter_execPtr++;
+ }
+ else
+ loopCount = 1;
+
+ for (int i = 0; i < loopCount; i++) {
+ token = evalExpr(&result);
+ switch (savedPos[0]) {
+ case 16:
+ case 18:
+ *(_vm->_global->_inter_variables + varOff + i) = _vm->_global->_inter_resVal;
+ break;
+
+ case 17:
+ case 27:
+ *(uint16*)(_vm->_global->_inter_variables + varOff + i * 2) = _vm->_global->_inter_resVal;
+ break;
+
+ case 23:
+ case 26:
+ WRITE_VAR_OFFSET(varOff + i * 4, _vm->_global->_inter_resVal);
+ break;
+
+ case 24:
+ *(uint16*)(_vm->_global->_inter_variables + varOff + i * 4) = _vm->_global->_inter_resVal;
+ break;
+
+ case 25:
+ case 28:
+ if (token == 20)
+ *(_vm->_global->_inter_variables + varOff) = result;
+ else
+ strcpy(_vm->_global->_inter_variables + varOff, _vm->_global->_inter_resStr);
+ break;
+ }
+ }
+
+ return false;
+}
+
+bool Inter_v2::o2_palLoad(char &cmdCount, int16 &counter, int16 &retFlag) {
+ int16 i;
+ int16 ind1;
+ int16 ind2;
+ byte cmd;
+ char *palPtr;
+
+ cmd = *_vm->_global->_inter_execPtr++;
+
+ switch(cmd & 0x7f) {
+ case 48:
+ if ((_vm->_global->_videoMode < 0x32) || (_vm->_global->_videoMode > 0x63)) {
+ _vm->_global->_inter_execPtr += 48;
+ return false;
+ }
+ break;
+
+ case 49:
+ if ((_vm->_global->_videoMode != 5) && (_vm->_global->_videoMode != 7)) {
+ _vm->_global->_inter_execPtr += 18;
+ return false;
+ }
+ break;
+
+ case 50:
+ if (_vm->_global->_videoMode != 0x0D) { // || (word_2D479 == 256) {
+ _vm->_global->_inter_execPtr += 16;
+ return false;
+ }
+ break;
+
+ case 51:
+ if (_vm->_global->_videoMode < 0x64) {
+ _vm->_global->_inter_execPtr += 2;
+ return false;
+ }
+ break;
+
+ case 52:
+ if (_vm->_global->_videoMode != 0x0D) { // || (word_2D479 == 256) {
+ _vm->_global->_inter_execPtr += 48;
+ return false;
+ }
+ break;
+
+ case 53:
+ if (_vm->_global->_videoMode < 0x13) {
+ _vm->_global->_inter_execPtr += 2;
+ return false;
+ }
+ break;
+
+ case 54:
+ if (_vm->_global->_videoMode < 0x13) {
+ return false;
+ }
+ break;
+
+ case 61:
+ if (_vm->_global->_videoMode < 0x13) {
+ *_vm->_global->_inter_execPtr += 4;
+ return false;
+ }
+ break;
+ }
+
+ if ((cmd & 0x7f) == 0x30) {
+ _vm->_global->_inter_execPtr += 48;
+ return false;
+ }
+
+ _vm->_draw->_applyPal = 0;
+ if (cmd & 0x80)
+ cmd &= 0x7f;
+ else
+ _vm->_draw->_applyPal = 1;
+
+ if (cmd == 49) {
+ int dl = 0;
+ for (i = 2; i < 18; i++) {
+ dl = 1;
+ if(_vm->_global->_inter_execPtr[i] != 0)
+ dl = 0;
+ }
+ if (dl != 0) {
+ warning("GOB2 Stub! sub_27413");
+/* sub_27413(_draw_frontSurface);
+ byte_2E521 = 0;
+ _vm->_global->_inter_execPtr += 18;
+ break;*/
+ }
+// byte_2E521 = 1;
+
+ for (i = 0; i < 18; i++, _vm->_global->_inter_execPtr++) {
+ if (i < 2) {
+ if (_vm->_draw->_applyPal == 0)
+ continue;
+
+ _vm->_draw->_unusedPalette1[i] = *_vm->_global->_inter_execPtr;
+ continue;
+ }
+
+ ind1 = *_vm->_global->_inter_execPtr >> 4;
+ ind2 = (*_vm->_global->_inter_execPtr & 0xf);
+
+ _vm->_draw->_unusedPalette1[i] =
+ ((_vm->_draw->_palLoadData1[ind1] + _vm->_draw->_palLoadData2[ind2]) << 8) +
+ (_vm->_draw->_palLoadData2[ind1] + _vm->_draw->_palLoadData1[ind2]);
+ }
+
+ _vm->_global->_pPaletteDesc->unused1 = _vm->_draw->_unusedPalette1;
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ return false;
+ }
+
+ switch (cmd) {
+ case 50:
+ for (i = 0; i < 16; i++, _vm->_global->_inter_execPtr++)
+ _vm->_draw->_unusedPalette2[i] = *_vm->_global->_inter_execPtr;
+ break;
+
+ case 52:
+ for (i = 0; i < 16; i++, _vm->_global->_inter_execPtr += 3) {
+ _vm->_draw->_vgaSmallPalette[i].red = _vm->_global->_inter_execPtr[0];
+ _vm->_draw->_vgaSmallPalette[i].green = _vm->_global->_inter_execPtr[1];
+ _vm->_draw->_vgaSmallPalette[i].blue = _vm->_global->_inter_execPtr[2];
+ }
+ _vm->_global->_inter_execPtr += 48;
+ if (_vm->_global->_videoMode >= 0x13)
+ return false;
+ break;
+
+ case 53:
+ palPtr = _vm->_game->loadTotResource(_vm->_inter->load16());
+ memcpy((char *)_vm->_draw->_vgaPalette, palPtr, 768);
+ break;
+
+ case 54:
+ memset((char *)_vm->_draw->_vgaPalette, 0, 768);
+ break;
+
+ case 61:
+ ind1 = *_vm->_global->_inter_execPtr++;
+ ind2 = (*_vm->_global->_inter_execPtr++ - ind1 + 1) * 3;
+ palPtr = _vm->_game->loadTotResource(_vm->_inter->load16());
+ memcpy((char *)_vm->_draw->_vgaPalette + ind1 * 3, palPtr + ind1 * 3, ind2);
+ if (_vm->_draw->_applyPal) {
+ _vm->_draw->_applyPal = 0;
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ return false;
+ }
+ break;
+ }
+
+ if (!_vm->_draw->_applyPal) {
+ _vm->_global->_pPaletteDesc->unused2 = _vm->_draw->_unusedPalette2;
+ _vm->_global->_pPaletteDesc->unused1 = _vm->_draw->_unusedPalette1;
+ if (_vm->_global->_videoMode < 0x13) {
+ _vm->_global->_pPaletteDesc->vgaPal = (Video::Color *)_vm->_draw->_vgaSmallPalette;
+ _vm->_palanim->fade((Video::PalDesc *) _vm->_global->_pPaletteDesc, 0, 0);
+ return false;
+ }
+ if ((_vm->_global->_videoMode < 0x32) || (_vm->_global->_videoMode >= 0x64)) {
+ _vm->_global->_pPaletteDesc->vgaPal = (Video::Color *)_vm->_draw->_vgaPalette;
+ _vm->_palanim->fade((Video::PalDesc *) _vm->_global->_pPaletteDesc, 0, 0);
+ return false;
+ }
+ _vm->_global->_pPaletteDesc->vgaPal = (Video::Color *)_vm->_draw->_vgaSmallPalette;
+ _vm->_palanim->fade((Video::PalDesc *) _vm->_global->_pPaletteDesc, 0, 0);
+ }
+
+ return false;
+}
+
+void Inter_v2::o2_setRenderFlags(void) {
+ int16 expr;
+
+ expr = _vm->_parse->parseValExpr();
+
+ if (expr & 0x8000) {
+ if (expr & 0x4000)
+ _vm->_draw->_renderFlags = _vm->_parse->parseValExpr();
+ else
+ _vm->_draw->_renderFlags &= expr & 0x3fff;
+ }
+ else
+ _vm->_draw->_renderFlags |= expr & 0x3fff;
+}
+
+bool Inter_v2::o2_loadTot(char &cmdCount, int16 &counter, int16 &retFlag) {
+ char buf[20];
+ int8 size;
+ int16 i;
+
+ if ((*_vm->_global->_inter_execPtr & 0x80) != 0) {
+ _vm->_global->_inter_execPtr++;
+ evalExpr(0);
+ strcpy(buf, _vm->_global->_inter_resStr);
+ } else {
+ size = *_vm->_global->_inter_execPtr++;
+ for (i = 0; i < size; i++)
+ buf[i] = *_vm->_global->_inter_execPtr++;
+
+ buf[size] = 0;
+ }
+
+ if (strcmp(buf, "INSTALL") == 0) {
+ warning("GOB2 Stub! word_2E515 = _inter_variables[0E8h]");
+ }
+
+ strcat(buf, ".tot");
+ if (_terminate != 2)
+ _terminate = true;
+ strcpy(_vm->_game->_totToLoad, buf);
+
+ return false;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/map.cpp b/engines/gob/map.cpp
new file mode 100644
index 0000000000..ed08d1d401
--- /dev/null
+++ b/engines/gob/map.cpp
@@ -0,0 +1,785 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/map.h"
+#include "gob/video.h"
+#include "gob/util.h"
+#include "gob/dataio.h"
+#include "gob/inter.h"
+#include "gob/goblin.h"
+#include "gob/sound.h"
+#include "gob/scenery.h"
+
+namespace Gob {
+
+Map::Map(GobEngine *vm) : _vm(vm) {
+ int i;
+
+ for (i = 0; i < kMapHeight; i++)
+ for (int j = 0; j < kMapWidth; j++) {
+ _passMap[i][j] = 0;
+ _itemsMap[i][j] = 0;
+ }
+ for (i = 0; i < 40; i++) {
+ _wayPoints[i].x = 0;
+ _wayPoints[i].y = 0;
+ }
+ for (i = 0; i < 40; i++) {
+ _itemPoses[i].x = 0;
+ _itemPoses[i].y = 0;
+ _itemPoses[i].orient = 0;
+ }
+
+ _nearestWayPoint = 0;
+ _nearestDest = 0;
+ _curGoblinX = 0;
+ _curGoblinY = 0;
+ _destX = 0;
+ _destY = 0;
+ _loadFromAvo = 0;
+ _sourceFile[0] = 0;
+ _avoDataPtr = 0;
+}
+
+void Map::placeItem(int16 x, int16 y, int16 id) {
+ if ((_itemsMap[y][x] & 0xff00) != 0)
+ _itemsMap[y][x] = (_itemsMap[y][x] & 0xff00) | id;
+ else
+ _itemsMap[y][x] = (_itemsMap[y][x] & 0x00ff) | (id << 8);
+}
+
+enum {
+ kLeft = (1 << 0),
+ kUp = (1 << 1),
+ kRight = (1 << 2),
+ kDown = (1 << 3)
+};
+
+int16 Map::getDirection(int16 x0, int16 y0, int16 x1, int16 y1) {
+ int16 dir = 0;
+
+ if (x0 == x1 && y0 == y1)
+ return 0;
+
+ if (!(x1 >= 0 && x1 < kMapWidth && y1 >= 0 && y1 < kMapHeight))
+ return 0;
+
+ if (y1 > y0)
+ dir |= kDown;
+ else if (y1 < y0)
+ dir |= kUp;
+
+ if (x1 > x0)
+ dir |= kRight;
+ else if (x1 < x0)
+ dir |= kLeft;
+
+ if (_passMap[y0][x0] == 3 && (dir & kUp)) {
+ if (_passMap[y0 - 1][x0] != 0)
+ return kDirN;
+ }
+
+ if (_passMap[y0][x0] == 3 && (dir & kDown)) {
+ if (_passMap[y0 + 1][x0] != 0)
+ return kDirS;
+ }
+
+ if (_passMap[y0][x0] == 6 && (dir & kUp)) {
+ if (_passMap[y0 - 1][x0] != 0)
+ return kDirN;
+ }
+
+ if (_passMap[y0][x0] == 6 && (dir & kDown)) {
+ if (_passMap[y0 + 1][x0] != 0)
+ return kDirS;
+ }
+
+ if (dir == kLeft) {
+ if (x0 - 1 >= 0 && _passMap[y0][x0 - 1] != 0)
+ return kDirW;
+ return 0;
+ }
+
+ if (dir == kRight) {
+ if (x0 + 1 < kMapWidth && _passMap[y0][x0 + 1] != 0)
+ return kDirE;
+ return 0;
+ }
+
+ if (dir == kUp) {
+ if (y0 - 1 >= 0 && _passMap[y0 - 1][x0] != 0)
+ return kDirN;
+
+ if (y0 - 1 >= 0 && x0 - 1 >= 0
+ && _passMap[y0 - 1][x0 - 1] != 0)
+ return kDirNW;
+
+ if (y0 - 1 >= 0 && x0 + 1 < kMapWidth
+ && _passMap[y0 - 1][x0 + 1] != 0)
+ return kDirNE;
+
+ return 0;
+ }
+
+ if (dir == kDown) {
+ if (y0 + 1 < kMapHeight && _passMap[y0 + 1][x0] != 0)
+ return kDirS;
+
+ if (y0 + 1 < kMapHeight && x0 - 1 >= 0
+ && _passMap[y0 + 1][x0 - 1] != 0)
+ return kDirSW;
+
+ if (y0 + 1 < kMapHeight && x0 + 1 < kMapWidth
+ && _passMap[y0 + 1][x0 + 1] != 0)
+ return kDirSE;
+
+ return 0;
+ }
+
+ if (dir == (kRight | kUp)) {
+ if (y0 - 1 >= 0 && x0 + 1 < kMapWidth
+ && _passMap[y0 - 1][x0 + 1] != 0)
+ return kDirNE;
+
+ if (y0 - 1 >= 0 && _passMap[y0 - 1][x0] != 0)
+ return kDirN;
+
+ if (x0 + 1 < kMapWidth && _passMap[y0][x0 + 1] != 0)
+ return kDirE;
+
+ return 0;
+ }
+
+ if (dir == (kRight | kDown)) {
+ if (x0 + 1 < kMapWidth && y0 + 1 < kMapHeight
+ && _passMap[y0 + 1][x0 + 1] != 0)
+ return kDirSE;
+
+ if (y0 + 1 < kMapHeight && _passMap[y0 + 1][x0] != 0)
+ return kDirS;
+
+ if (x0 + 1 < kMapWidth && _passMap[y0][x0 + 1] != 0)
+ return kDirE;
+
+ return 0;
+ }
+
+ if (dir == (kLeft | kUp)) {
+ if (x0 - 1 >= 0 && y0 - 1 >= 0
+ && _passMap[y0 - 1][x0 - 1] != 0)
+ return kDirNW;
+
+ if (y0 - 1 >= 0 && _passMap[y0 - 1][x0] != 0)
+ return kDirN;
+
+ if (x0 - 1 >= 0 && _passMap[y0][x0 - 1] != 0)
+ return kDirW;
+
+ return 0;
+ }
+
+ if (dir == (kLeft | kDown)) {
+ if (x0 - 1 >= 0 && y0 + 1 < kMapHeight
+ && _passMap[y0 + 1][x0 - 1] != 0)
+ return kDirSW;
+
+ if (y0 + 1 < kMapHeight && _passMap[y0 + 1][x0] != 0)
+ return kDirS;
+
+ if (x0 - 1 >= 0 && _passMap[y0][x0 - 1] != 0)
+ return kDirW;
+
+ return 0;
+ }
+ return -1;
+}
+
+int16 Map::findNearestWayPoint(int16 x, int16 y) {
+ int16 lnearestWayPoint = -1;
+ int16 length;
+ int16 i;
+ int16 tmp;
+
+ length = 30000;
+
+ for (i = 0; i < 40; i++) {
+ if (_wayPoints[i].x < 0 ||
+ _wayPoints[i].x >= kMapWidth ||
+ _wayPoints[i].y < 0 || _wayPoints[i].y >= kMapHeight)
+ return -1;
+
+ tmp = ABS(x - _wayPoints[i].x) + ABS(y - _wayPoints[i].y);
+
+ if (tmp <= length) {
+ lnearestWayPoint = i;
+ length = tmp;
+ }
+ }
+
+ return lnearestWayPoint;
+}
+
+void Map::findNearestToGob(void) {
+ int16 wayPoint = findNearestWayPoint(_curGoblinX, _curGoblinY);
+
+ if (wayPoint != -1)
+ _nearestWayPoint = wayPoint;
+}
+
+void Map::findNearestToDest(void) {
+ int16 wayPoint = findNearestWayPoint(_destX, _destY);
+
+ if (wayPoint != -1)
+ _nearestDest = wayPoint;
+}
+
+int16 Map::checkDirectPath(int16 x0, int16 y0, int16 x1, int16 y1) {
+ uint16 dir;
+
+ while (1) {
+ dir = getDirection(x0, y0, x1, y1);
+
+ if (x0 == x1 && y0 == y1)
+ return 1;
+
+ if (dir == 0)
+ return 3;
+
+ switch (dir) {
+ case kDirNW:
+ x0--;
+ y0--;
+ break;
+
+ case kDirN:
+ y0--;
+ break;
+
+ case kDirNE:
+ x0++;
+ y0--;
+ break;
+
+ case kDirW:
+ x0--;
+ break;
+
+ case kDirE:
+ x0++;
+ break;
+
+ case kDirSW:
+ x0--;
+ y0++;
+ break;
+
+ case kDirS:
+ y0++;
+ break;
+
+ case kDirSE:
+ x0++;
+ y0++;
+ break;
+ }
+ }
+}
+
+int16 Map::checkLongPath(int16 x0, int16 y0, int16 x1, int16 y1, int16 i0, int16 i1) {
+ uint16 dir;
+ int16 curX;
+ int16 curY;
+ int16 nextLink;
+
+ curX = x0;
+ curY = y0;
+ dir = 0;
+
+ nextLink = 1;
+
+ while (1) {
+ if (x0 == curX && y0 == curY)
+ nextLink = 1;
+
+ if (nextLink != 0) {
+ if (checkDirectPath(x0, y0, x1, y1) == 1)
+ return 1;
+
+ nextLink = 0;
+ if (i0 > i1) {
+ curX = _wayPoints[i0].x;
+ curY = _wayPoints[i0].y;
+ i0--;
+ } else if (i0 < i1) {
+ curX = _wayPoints[i0].x;
+ curY = _wayPoints[i0].y;
+ i0++;
+ } else if (i0 == i1) {
+ curX = _wayPoints[i0].x;
+ curY = _wayPoints[i0].y;
+ }
+ }
+ if (i0 == i1 && _wayPoints[i0].x == x0
+ && _wayPoints[i0].y == y0) {
+ if (checkDirectPath(x0, y0, x1, y1) == 1)
+ return 1;
+ return 0;
+ }
+ dir = getDirection(x0, y0, curX, curY);
+ switch (dir) {
+ case 0:
+ return 0;
+
+ case kDirNW:
+ x0--;
+ y0--;
+ break;
+
+ case kDirN:
+ y0--;
+ break;
+
+ case kDirNE:
+ x0++;
+ y0--;
+ break;
+
+ case kDirW:
+ x0--;
+ break;
+
+ case kDirE:
+ x0++;
+ break;
+
+ case kDirSW:
+ x0--;
+ y0++;
+ break;
+
+ case kDirS:
+ y0++;
+ break;
+
+ case kDirSE:
+ x0++;
+ y0++;
+ break;
+ }
+ }
+}
+
+void Map::optimizePoints(void) {
+ int16 i;
+
+ if (_nearestWayPoint < _nearestDest) {
+ for (i = _nearestWayPoint; i <= _nearestDest; i++) {
+ if (checkDirectPath(_curGoblinX, _curGoblinY,
+ _wayPoints[i].x, _wayPoints[i].y) == 1)
+ _nearestWayPoint = i;
+ }
+ } else if (_nearestWayPoint > _nearestDest) {
+ for (i = _nearestWayPoint; i >= _nearestDest; i--) {
+ if (checkDirectPath(_curGoblinX, _curGoblinY,
+ _wayPoints[i].x, _wayPoints[i].y) == 1)
+ _nearestWayPoint = i;
+ }
+ }
+}
+
+void Map::loadDataFromAvo(char *dest, int16 size) {
+ memcpy(dest, _avoDataPtr, size);
+ _avoDataPtr += size;
+}
+
+uint16 Map::loadFromAvo_LE_UINT16() {
+ uint16 tmp = READ_LE_UINT16(_avoDataPtr);
+ _avoDataPtr += 2;
+ return tmp;
+}
+
+void Map::loadItemToObject(void) {
+ int16 flag;
+ int16 count;
+ int16 i;
+
+ flag = loadFromAvo_LE_UINT16();
+ if (flag == 0)
+ return;
+
+ _avoDataPtr += 1456;
+ count = loadFromAvo_LE_UINT16();
+ for (i = 0; i < count; i++) {
+ _avoDataPtr += 20;
+ _vm->_goblin->_itemToObject[i] = loadFromAvo_LE_UINT16();
+ _avoDataPtr += 5;
+ }
+}
+
+void Map::loadMapObjects(char *avjFile) {
+ int16 i;
+ char avoName[128];
+ int16 handle;
+ char item;
+ int16 soundCount;
+ int16 tmp;
+ char *savedPtr;
+ char *savedPtr2;
+ char *savedPtr3;
+ int16 state;
+ int16 col;
+ int32 flag;
+ Goblin::Gob_State *pState;
+ char buf[128];
+ char sndNames[20][14];
+ char *dataBuf;
+ int16 x;
+ int16 y;
+ int16 count2;
+ int16 count3;
+
+ strcpy(avoName, _sourceFile);
+ strcat(avoName, ".avo");
+
+ handle = _vm->_dataio->openData(avoName);
+ if (handle >= 0) {
+ _loadFromAvo = 1;
+ _vm->_dataio->closeData(handle);
+ _avoDataPtr = _vm->_dataio->getData(avoName);
+ dataBuf = _avoDataPtr;
+ loadDataFromAvo((char *)_passMap, kMapHeight * kMapWidth);
+
+ for (y = 0; y < kMapHeight; y++) {
+ for (x = 0; x < kMapWidth; x++) {
+ loadDataFromAvo(&item, 1);
+ _itemsMap[y][x] = item;
+ }
+ }
+
+ for (i = 0; i < 40; i++) {
+ _wayPoints[i].x = loadFromAvo_LE_UINT16();
+ _wayPoints[i].y = loadFromAvo_LE_UINT16();
+ }
+ loadDataFromAvo((char *)_itemPoses, szMap_ItemPos * 20);
+ } else {
+ _loadFromAvo = 0;
+ _avoDataPtr = _vm->_dataio->getData(avjFile);
+ dataBuf = _avoDataPtr;
+ }
+
+ _avoDataPtr += 32;
+ _avoDataPtr += 76;
+ _avoDataPtr += 4;
+ _avoDataPtr += 20;
+
+ for (i = 0; i < 3; i++) {
+ tmp = loadFromAvo_LE_UINT16();
+ _avoDataPtr += tmp * 14;
+ }
+
+ soundCount = loadFromAvo_LE_UINT16();
+ savedPtr = _avoDataPtr;
+
+ _avoDataPtr += 14 * soundCount;
+ _avoDataPtr += 4;
+ _avoDataPtr += 24;
+
+ count2 = loadFromAvo_LE_UINT16();
+ count3 = loadFromAvo_LE_UINT16();
+
+ savedPtr2 = _avoDataPtr;
+ _avoDataPtr += count2 * 8;
+
+ savedPtr3 = _avoDataPtr;
+ _avoDataPtr += count3 * 8;
+
+ _vm->_goblin->_gobsCount = loadFromAvo_LE_UINT16();
+ for (i = 0; i < _vm->_goblin->_gobsCount; i++) {
+ _vm->_goblin->_goblins[i] = new Goblin::Gob_Object;
+
+ _vm->_goblin->_goblins[i]->xPos = READ_LE_UINT16(savedPtr2);
+ savedPtr2 += 2;
+
+ _vm->_goblin->_goblins[i]->yPos = READ_LE_UINT16(savedPtr2);
+ savedPtr2 += 2;
+
+ _vm->_goblin->_goblins[i]->order = READ_LE_UINT16(savedPtr2);
+ savedPtr2 += 2;
+
+ _vm->_goblin->_goblins[i]->state = READ_LE_UINT16(savedPtr2);
+ savedPtr2 += 2;
+
+ if (i == 3)
+ _vm->_goblin->_goblins[i]->stateMach = new Goblin::Gob_StateLine[70];
+ else
+ _vm->_goblin->_goblins[i]->stateMach = new Goblin::Gob_StateLine[40];
+
+ uint32* tempstatedata = new uint32[40*6];
+ for (state = 0; state < 40; ++state) {
+ for (col = 0; col < 6; ++col) {
+ tempstatedata[state*6+col] = READ_LE_UINT32(_avoDataPtr);
+ _avoDataPtr += 4;
+ }
+ }
+ _avoDataPtr += 160;
+ _vm->_goblin->_goblins[i]->multObjIndex = *_avoDataPtr;
+ _avoDataPtr += 2;
+
+ _vm->_goblin->_goblins[i]->realStateMach = _vm->_goblin->_goblins[i]->stateMach;
+ for (state = 0; state < 40; state++) {
+ for (col = 0; col < 6; col++) {
+ if (tempstatedata[state*6+col] == 0) {
+ _vm->_goblin->_goblins[i]->stateMach[state][col] = 0;
+ continue;
+ }
+
+ Goblin::Gob_State *tmpState = new Goblin::Gob_State;
+ _vm->_goblin->_goblins[i]->stateMach[state][col] = tmpState;
+
+ tmpState->animation = loadFromAvo_LE_UINT16();
+ tmpState->layer = loadFromAvo_LE_UINT16();
+ _avoDataPtr += 8;
+ tmpState->unk0 = loadFromAvo_LE_UINT16();
+ tmpState->unk1 = loadFromAvo_LE_UINT16();
+
+ _avoDataPtr += 2;
+ if (READ_LE_UINT32(_avoDataPtr) != 0) {
+ _avoDataPtr += 4;
+ tmpState->sndItem = loadFromAvo_LE_UINT16();
+ } else {
+ _avoDataPtr += 6;
+ tmpState->sndItem = -1;
+ }
+ tmpState->freq = loadFromAvo_LE_UINT16();
+ tmpState->repCount = loadFromAvo_LE_UINT16();
+ tmpState->sndFrame = loadFromAvo_LE_UINT16();
+ }
+ }
+ delete[] tempstatedata;
+ }
+
+ pState = new Goblin::Gob_State;
+ _vm->_goblin->_goblins[0]->stateMach[39][0] = pState;
+ pState->animation = 0;
+ pState->layer = 98;
+ pState->unk0 = 0;
+ pState->unk1 = 0;
+ pState->sndItem = -1;
+
+ pState = new Goblin::Gob_State;
+ _vm->_goblin->_goblins[1]->stateMach[39][0] = pState;
+ pState->animation = 0;
+ pState->layer = 99;
+ pState->unk0 = 0;
+ pState->unk1 = 0;
+ pState->sndItem = -1;
+
+ pState = new Goblin::Gob_State;
+ _vm->_goblin->_goblins[2]->stateMach[39][0] = pState;
+ pState->animation = 0;
+ pState->layer = 100;
+ pState->unk0 = 0;
+ pState->unk1 = 0;
+ pState->sndItem = -1;
+
+ _vm->_goblin->_goblins[2]->stateMach[10][0]->sndFrame = 13;
+ _vm->_goblin->_goblins[2]->stateMach[11][0]->sndFrame = 13;
+ _vm->_goblin->_goblins[2]->stateMach[28][0]->sndFrame = 13;
+ _vm->_goblin->_goblins[2]->stateMach[29][0]->sndFrame = 13;
+
+ _vm->_goblin->_goblins[1]->stateMach[10][0]->sndFrame = 13;
+ _vm->_goblin->_goblins[1]->stateMach[11][0]->sndFrame = 13;
+
+ for (state = 40; state < 70; state++) {
+ pState = new Goblin::Gob_State;
+ _vm->_goblin->_goblins[3]->stateMach[state][0] = pState;
+ _vm->_goblin->_goblins[3]->stateMach[state][1] = 0;
+
+ pState->animation = 9;
+ pState->layer = state - 40;
+ pState->sndItem = -1;
+ pState->sndFrame = 0;
+ }
+
+ _vm->_goblin->_objCount = loadFromAvo_LE_UINT16();
+ for (i = 0; i < _vm->_goblin->_objCount; i++) {
+ _vm->_goblin->_objects[i] = new Goblin::Gob_Object;
+
+ _vm->_goblin->_objects[i]->xPos = READ_LE_UINT16(savedPtr3);
+ savedPtr3 += 2;
+
+ _vm->_goblin->_objects[i]->yPos = READ_LE_UINT16(savedPtr3);
+ savedPtr3 += 2;
+
+ _vm->_goblin->_objects[i]->order = READ_LE_UINT16(savedPtr3);
+ savedPtr3 += 2;
+
+ _vm->_goblin->_objects[i]->state = READ_LE_UINT16(savedPtr3);
+ savedPtr3 += 2;
+
+ _vm->_goblin->_objects[i]->stateMach = new Goblin::Gob_StateLine[40];
+
+ uint32* tempstatedata = new uint32[40*6];
+ for (state = 0; state < 40; ++state) {
+ for (col = 0; col < 6; ++col) {
+ tempstatedata[state*6+col] = READ_LE_UINT32(_avoDataPtr);
+ _avoDataPtr += 4;
+ }
+ }
+ _avoDataPtr += 160;
+ _vm->_goblin->_objects[i]->multObjIndex = *_avoDataPtr;
+ _avoDataPtr += 2;
+
+ _vm->_goblin->_objects[i]->realStateMach = _vm->_goblin->_objects[i]->stateMach;
+ for (state = 0; state < 40; state++) {
+ for (col = 0; col < 6; col++) {
+ if (tempstatedata[state*6+col] == 0) {
+ _vm->_goblin->_objects[i]->stateMach[state][col] = 0;
+ continue;
+ }
+
+ Goblin::Gob_State *tmpState = new Goblin::Gob_State;
+ _vm->_goblin->_objects[i]->stateMach[state][col] = tmpState;
+
+ tmpState->animation = loadFromAvo_LE_UINT16();
+ tmpState->layer = loadFromAvo_LE_UINT16();
+ _avoDataPtr += 8;
+ tmpState->unk0 = loadFromAvo_LE_UINT16();
+ tmpState->unk1 = loadFromAvo_LE_UINT16();
+
+ _avoDataPtr += 2;
+ if (READ_LE_UINT32(_avoDataPtr) != 0) {
+ _avoDataPtr += 4;
+ tmpState->sndItem = loadFromAvo_LE_UINT16();
+ } else {
+ _avoDataPtr += 6;
+ tmpState->sndItem = -1;
+ }
+ tmpState->freq = loadFromAvo_LE_UINT16();
+ tmpState->repCount = loadFromAvo_LE_UINT16();
+ tmpState->sndFrame = loadFromAvo_LE_UINT16();
+ }
+ }
+ delete[] tempstatedata;
+ }
+
+ _vm->_goblin->_objects[10] = new Goblin::Gob_Object;
+ memset(_vm->_goblin->_objects[10], 0, sizeof(Goblin::Gob_Object));
+
+ _vm->_goblin->_objects[10]->stateMach = new Goblin::Gob_StateLine[40];
+ for (state = 0; state < 40; ++state)
+ for (col = 0; col < 6; ++col)
+ _vm->_goblin->_objects[10]->stateMach[state][col] = 0;
+
+ pState = new Goblin::Gob_State;
+ _vm->_goblin->_objects[10]->stateMach[0][0] = pState;
+
+ memset(pState, 0, sizeof(Goblin::Gob_State));
+ pState->animation = 9;
+ pState->layer = 27;
+ pState->unk0 = 0;
+ pState->unk1 = 0;
+ pState->sndItem = -1;
+ pState->sndFrame = 0;
+
+ _vm->_goblin->placeObject(_vm->_goblin->_objects[10], 1);
+
+ _vm->_goblin->_objects[10]->realStateMach = _vm->_goblin->_objects[10]->stateMach;
+ _vm->_goblin->_objects[10]->type = 1;
+ _vm->_goblin->_objects[10]->unk14 = 1;
+
+ state = loadFromAvo_LE_UINT16();
+ for (i = 0; i < state; i++) {
+ _avoDataPtr += 30;
+
+ loadDataFromAvo((char *)&flag, 4);
+ _avoDataPtr += 56;
+
+ if (flag != 0)
+ _avoDataPtr += 30;
+ }
+
+ loadDataFromAvo((char *)&tmp, 2);
+ _avoDataPtr += 48;
+ loadItemToObject();
+ _avoDataPtr = savedPtr;
+
+ for (i = 0; i < soundCount; i++) {
+ loadDataFromAvo(buf, 14);
+ strcat(buf, ".SND");
+ strcpy(sndNames[i], buf);
+ }
+
+ delete[] dataBuf;
+
+ _vm->_goblin->_soundData[14] = _vm->_snd->loadSoundData("diamant1.snd");
+
+ for (i = 0; i < soundCount; i++) {
+ handle = _vm->_dataio->openData(sndNames[i]);
+ if (handle < 0)
+ continue;
+
+ _vm->_dataio->closeData(handle);
+ _vm->_goblin->_soundData[i] = _vm->_snd->loadSoundData(sndNames[i]);
+ }
+}
+
+void Map::loadMapsInitGobs(void) {
+ int16 layer;
+ int16 i;
+
+ if (_loadFromAvo == 0)
+ error("load: Loading .pas/.pos files is not supported!");
+
+ for (i = 0; i < 3; i++) {
+ _vm->_goblin->nextLayer(_vm->_goblin->_goblins[i]);
+ }
+
+ for (i = 0; i < 3; i++) {
+
+ layer =
+ _vm->_goblin->_goblins[i]->stateMach[_vm->_goblin->_goblins[i]->state][0]->layer;
+
+ _vm->_scenery->updateAnim(layer, 0, _vm->_goblin->_goblins[i]->animation, 0,
+ _vm->_goblin->_goblins[i]->xPos, _vm->_goblin->_goblins[i]->yPos, 0);
+
+ _vm->_goblin->_goblins[i]->yPos = (_vm->_goblin->_gobPositions[i].y + 1) * 6 -
+ (_vm->_scenery->_toRedrawBottom - _vm->_scenery->_animTop);
+
+ _vm->_goblin->_goblins[i]->xPos = _vm->_goblin->_gobPositions[i].x * 12 -
+ (_vm->_scenery->_toRedrawLeft - _vm->_scenery->_animLeft);
+
+ _vm->_goblin->_goblins[i]->order = _vm->_scenery->_toRedrawBottom / 24 + 3;
+ }
+
+ _vm->_goblin->_currentGoblin = 0;
+ _vm->_goblin->_pressedMapX = _vm->_goblin->_gobPositions[0].x;
+ _vm->_goblin->_pressedMapY = _vm->_goblin->_gobPositions[0].y;
+ _vm->_goblin->_pathExistence = 0;
+
+ _vm->_goblin->_goblins[0]->doAnim = 0;
+ _vm->_goblin->_goblins[1]->doAnim = 1;
+ _vm->_goblin->_goblins[2]->doAnim = 1;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/map.h b/engines/gob/map.h
new file mode 100644
index 0000000000..59e9a3bdf9
--- /dev/null
+++ b/engines/gob/map.h
@@ -0,0 +1,104 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_MAP_H
+#define GOB_MAP_H
+
+namespace Gob {
+
+// The same numeric values are also used for the arrow keys.
+
+class Map {
+public:
+ enum {
+ kDirNW = 0x4700,
+ kDirN = 0x4800,
+ kDirNE = 0x4900,
+ kDirW = 0x4b00,
+ kDirE = 0x4d00,
+ kDirSW = 0x4f00,
+ kDirS = 0x5000,
+ kDirSE = 0x5100
+ };
+ enum {
+ kMapWidth = 26,
+ kMapHeight = 28
+ };
+
+#pragma START_PACK_STRUCTS
+
+ struct Point {
+ int16 x;
+ int16 y;
+ } GCC_PACK;
+
+#define szMap_ItemPos 3
+
+ struct ItemPos {
+ int8 x;
+ int8 y;
+ int8 orient; // ??
+ } GCC_PACK;
+
+#pragma END_PACK_STRUCTS
+
+ int8 _passMap[kMapHeight][kMapWidth]; // [y][x]
+ int16 _itemsMap[kMapHeight][kMapWidth]; // [y][x]
+ Point _wayPoints[40];
+ int16 _nearestWayPoint;
+ int16 _nearestDest;
+
+ int16 _curGoblinX;
+ int16 _curGoblinY;
+ int16 _destX;
+ int16 _destY;
+ int8 _loadFromAvo;
+
+ ItemPos _itemPoses[40];
+ char _sourceFile[15];
+
+ void placeItem(int16 x, int16 y, int16 id);
+
+ int16 getDirection(int16 x0, int16 y0, int16 x1, int16 y1);
+ void findNearestToGob(void);
+ void findNearestToDest(void);
+ int16 checkDirectPath(int16 x0, int16 y0, int16 x1, int16 y1);
+ int16 checkLongPath(int16 x0, int16 y0, int16 x1, int16 y1, int16 i0, int16 i1);
+ void optimizePoints(void);
+ void loadItemToObject(void);
+ void loadMapObjects(char *avjFile);
+ void loadDataFromAvo(char *dest, int16 size);
+ void loadMapsInitGobs(void);
+
+ Map(GobEngine *vm);
+
+protected:
+ char *_avoDataPtr;
+ GobEngine *_vm;
+
+ int16 findNearestWayPoint(int16 x, int16 y);
+ uint16 loadFromAvo_LE_UINT16();
+};
+
+} // End of namespace Gob
+
+#endif /* __MAP_H */
diff --git a/engines/gob/module.mk b/engines/gob/module.mk
new file mode 100644
index 0000000000..c8188d78b2
--- /dev/null
+++ b/engines/gob/module.mk
@@ -0,0 +1,40 @@
+MODULE := engines/gob
+
+MODULE_OBJS := \
+ engines/gob/anim.o \
+ engines/gob/cdrom.o \
+ engines/gob/dataio.o \
+ engines/gob/draw.o \
+ engines/gob/driver_vga.o \
+ engines/gob/game.o \
+ engines/gob/global.o \
+ engines/gob/gob.o \
+ engines/gob/goblin.o \
+ engines/gob/init.o \
+ engines/gob/inter.o \
+ engines/gob/inter_v1.o \
+ engines/gob/inter_v2.o \
+ engines/gob/map.o \
+ engines/gob/mult.o \
+ engines/gob/music.o \
+ engines/gob/pack.o \
+ engines/gob/palanim.o \
+ engines/gob/parse.o \
+ engines/gob/parse_v1.o \
+ engines/gob/parse_v2.o \
+ engines/gob/scenery.o \
+ engines/gob/sound.o \
+ engines/gob/timer.o \
+ engines/gob/util.o \
+ engines/gob/video.o
+
+MODULE_DIRS += \
+ engines/gob
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/gob/mult.cpp b/engines/gob/mult.cpp
new file mode 100644
index 0000000000..96bffa80d1
--- /dev/null
+++ b/engines/gob/mult.cpp
@@ -0,0 +1,1214 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/video.h"
+#include "gob/anim.h"
+#include "gob/draw.h"
+#include "gob/scenery.h"
+#include "gob/mult.h"
+#include "gob/util.h"
+#include "gob/inter.h"
+#include "gob/parse.h"
+#include "gob/global.h"
+#include "gob/sound.h"
+#include "gob/palanim.h"
+#include "gob/game.h"
+
+namespace Gob {
+
+Mult::Mult(GobEngine *vm) : _vm(vm) {
+ _objects = 0;
+ _renderData = 0;
+ _objCount = 0;
+ _underAnimSurf = 0;
+ _multData = 0;
+ _frame = 0;
+ _doPalSubst = 0;
+ _counter = 0;
+ _frameRate = 0;
+
+ _animArrayX = 0;
+ _animArrayY = 0;
+ _animArrayData = 0;
+
+ _index = 0;
+
+ _staticKeysCount = 0;
+ _staticKeys = 0;
+ int i;
+ for (i = 0; i < 10; i++)
+ _staticIndices[i] = 0;
+
+ for (i = 0; i < 4; i++) {
+ _animKeys[i] = 0;
+ _animKeysCount[i] = 0;
+ }
+ _animLayer = 0;
+ for (i = 0; i < 10; i++)
+ _animIndices[i] = 0;
+
+ _textKeysCount = 0;
+ _textKeys = 0;
+
+ _frameStart = 0;
+
+ _palKeyIndex = 0;
+ _palKeysCount = 0;
+ _palKeys = 0;
+ _oldPalette = 0;
+ _palAnimKey = 0;
+ for (i = 0; i < 256; i++) {
+ _palAnimPalette[i].red = 0;
+ _palAnimPalette[i].green = 0;
+ _palAnimPalette[i].blue = 0;
+ }
+ for (i = 0; i < 4; i++) {
+ _palAnimIndices[i] = 0;
+ _palAnimRed[i] = 0;
+ _palAnimGreen[i] = 0;
+ _palAnimBlue[i] = 0;
+ }
+
+ _palFadeKeys = 0;
+ _palFadeKeysCount = 0;
+ _palFadingRed = 0;
+ _palFadingGreen = 0;
+ _palFadingBlue = 0;
+
+ _animDataAllocated = 0;
+
+ _dataPtr = 0;
+ for (i = 0; i < 10; i++) {
+ _staticLoaded[i] = 0;
+ _animLoaded[i] = 0;
+ }
+ _sndSlotsCount = 0;
+
+ _sndKeysCount = 0;
+ _sndKeys = 0;
+
+ for (i = 0; i < 5; i++)
+ for (int j = 0; j < 16; j++) {
+ _fadePal[i][j].red = 0;
+ _fadePal[i][j].green = 0;
+ _fadePal[i][j].blue = 0;
+ }
+}
+
+void Mult::animate(void) {
+ int16 minOrder;
+ int16 maxOrder;
+ int16 *pCurLefts;
+ int16 *pCurRights;
+ int16 *pCurTops;
+ int16 *pCurBottoms;
+ int16 *pDirtyLefts;
+ int16 *pDirtyRights;
+ int16 *pDirtyTops;
+ int16 *pDirtyBottoms;
+ int16 *pNeedRedraw;
+ Mult_AnimData *pAnimData;
+ int16 i, j;
+ int16 order;
+
+ if (_renderData == 0)
+ return;
+
+ pDirtyLefts = _renderData;
+ pDirtyRights = pDirtyLefts + _objCount;
+ pDirtyTops = pDirtyRights + _objCount;
+ pDirtyBottoms = pDirtyTops + _objCount;
+ pNeedRedraw = pDirtyBottoms + _objCount;
+ pCurLefts = pNeedRedraw + _objCount;
+ pCurRights = pCurLefts + _objCount;
+ pCurTops = pCurRights + _objCount;
+ pCurBottoms = pCurTops + _objCount;
+ minOrder = 100;
+ maxOrder = 0;
+
+ //Find dirty areas
+ for (i = 0; i < _objCount; i++) {
+ pNeedRedraw[i] = 0;
+ pDirtyTops[i] = 1000;
+ pDirtyLefts[i] = 1000;
+ pDirtyBottoms[i] = 1000;
+ pDirtyRights[i] = 1000;
+ pAnimData = _objects[i].pAnimData;
+
+ if (pAnimData->isStatic == 0 && pAnimData->isPaused == 0 &&
+ _objects[i].tick == pAnimData->maxTick) {
+ if (pAnimData->order < minOrder)
+ minOrder = pAnimData->order;
+
+ if (pAnimData->order > maxOrder)
+ maxOrder = pAnimData->order;
+
+ pNeedRedraw[i] = 1;
+ _vm->_scenery->updateAnim(pAnimData->layer, pAnimData->frame,
+ pAnimData->animation, 0,
+ *(_objects[i].pPosX), *(_objects[i].pPosY),
+ 0);
+
+ if (_objects[i].lastLeft != -1) {
+ pDirtyLefts[i] =
+ MIN(_objects[i].lastLeft,
+ _vm->_scenery->_toRedrawLeft);
+ pDirtyTops[i] =
+ MIN(_objects[i].lastTop,
+ _vm->_scenery->_toRedrawTop);
+ pDirtyRights[i] =
+ MAX(_objects[i].lastRight,
+ _vm->_scenery->_toRedrawRight);
+ pDirtyBottoms[i] =
+ MAX(_objects[i].lastBottom,
+ _vm->_scenery->_toRedrawBottom);
+ } else {
+ pDirtyLefts[i] = _vm->_scenery->_toRedrawLeft;
+ pDirtyTops[i] = _vm->_scenery->_toRedrawTop;
+ pDirtyRights[i] = _vm->_scenery->_toRedrawRight;
+ pDirtyBottoms[i] = _vm->_scenery->_toRedrawBottom;
+ }
+ pCurLefts[i] = _vm->_scenery->_toRedrawLeft;
+ pCurRights[i] = _vm->_scenery->_toRedrawRight;
+ pCurTops[i] = _vm->_scenery->_toRedrawTop;
+ pCurBottoms[i] = _vm->_scenery->_toRedrawBottom;
+ } else {
+ if (_objects[i].lastLeft != -1) {
+ if (pAnimData->order < minOrder)
+ minOrder = pAnimData->order;
+
+ if (pAnimData->order > maxOrder)
+ maxOrder = pAnimData->order;
+
+ if (pAnimData->isStatic)
+ *pNeedRedraw = 1;
+
+ pCurLefts[i] = _objects[i].lastLeft;
+ pDirtyLefts[i] = _objects[i].lastLeft;
+
+ pCurTops[i] = _objects[i].lastTop;
+ pDirtyTops[i] = _objects[i].lastTop;
+
+ pCurRights[i] = _objects[i].lastRight;
+ pDirtyRights[i] = _objects[i].lastRight;
+
+ pCurBottoms[i] = _objects[i].lastBottom;
+ pDirtyBottoms[i] = _objects[i].lastBottom;
+ }
+ }
+ }
+
+ // Find intersections
+ for (i = 0; i < _objCount; i++) {
+ pAnimData = _objects[i].pAnimData;
+ pAnimData->intersected = 200;
+
+ if (pAnimData->isStatic)
+ continue;
+
+ for (j = 0; j < _objCount; j++) {
+ if (i == j)
+ continue;
+
+ if (_objects[j].pAnimData->isStatic)
+ continue;
+
+ if (pCurRights[i] < pCurLefts[j])
+ continue;
+
+ if (pCurRights[j] < pCurLefts[i])
+ continue;
+
+ if (pCurBottoms[i] < pCurTops[j])
+ continue;
+
+ if (pCurBottoms[j] < pCurTops[i])
+ continue;
+
+ pAnimData->intersected = j;
+ break;
+ }
+ }
+
+ // Restore dirty areas
+ for (i = 0; i < _objCount; i++) {
+
+ if (pNeedRedraw[i] == 0 || _objects[i].lastLeft == -1)
+ continue;
+
+ _vm->_draw->_sourceSurface = 22;
+ _vm->_draw->_destSurface = 21;
+ _vm->_draw->_spriteLeft = pDirtyLefts[i] - _vm->_anim->_areaLeft;
+ _vm->_draw->_spriteTop = pDirtyTops[i] - _vm->_anim->_areaTop;
+ _vm->_draw->_spriteRight = pDirtyRights[i] - pDirtyLefts[i] + 1;
+ _vm->_draw->_spriteBottom = pDirtyBottoms[i] - pDirtyTops[i] + 1;
+ _vm->_draw->_destSpriteX = pDirtyLefts[i];
+ _vm->_draw->_destSpriteY = pDirtyTops[i];
+ _vm->_draw->_transparency = 0;
+ _vm->_draw->spriteOperation(DRAW_BLITSURF);
+ _objects[i].lastLeft = -1;
+ }
+
+ // Update view
+ for (order = minOrder; order <= maxOrder; order++) {
+ for (i = 0; i < _objCount; i++) {
+ pAnimData = _objects[i].pAnimData;
+ if (pAnimData->order != order)
+ continue;
+
+ if (pNeedRedraw[i]) {
+ if (pAnimData->isStatic == 0) {
+
+ _vm->_scenery->updateAnim(pAnimData->layer,
+ pAnimData->frame,
+ pAnimData->animation, 2,
+ *(_objects[i].pPosX),
+ *(_objects[i].pPosY), 1);
+
+ if (_vm->_scenery->_toRedrawLeft != -12345) {
+ _objects[i].lastLeft =
+ _vm->_scenery->_toRedrawLeft;
+ _objects[i].lastTop =
+ _vm->_scenery->_toRedrawTop;
+ _objects[i].lastRight =
+ _vm->_scenery->_toRedrawRight;
+ _objects[i].lastBottom =
+ _vm->_scenery->_toRedrawBottom;
+ } else {
+ _objects[i].lastLeft = -1;
+ }
+ }
+ _vm->_scenery->updateStatic(order + 1);
+ } else if (pAnimData->isStatic == 0) {
+ for (j = 0; j < _objCount; j++) {
+ if (pNeedRedraw[j] == 0)
+ continue;
+
+ if (pDirtyRights[i] < pDirtyLefts[j])
+ continue;
+
+ if (pDirtyRights[j] < pDirtyLefts[i])
+ continue;
+
+ if (pDirtyBottoms[i] < pDirtyTops[j])
+ continue;
+
+ if (pDirtyBottoms[j] < pDirtyTops[i])
+ continue;
+
+ _vm->_scenery->_toRedrawLeft = pDirtyLefts[j];
+ _vm->_scenery->_toRedrawRight = pDirtyRights[j];
+ _vm->_scenery->_toRedrawTop = pDirtyTops[j];
+ _vm->_scenery->_toRedrawBottom = pDirtyBottoms[j];
+
+ _vm->_scenery->updateAnim(pAnimData->layer,
+ pAnimData->frame,
+ pAnimData->animation, 4,
+ *(_objects[i].pPosX),
+ *(_objects[i].pPosY), 1);
+
+ _vm->_scenery->updateStatic(order + 1);
+ }
+ }
+ }
+ }
+
+ // Advance animations
+ for (i = 0; i < _objCount; i++) {
+ pAnimData = _objects[i].pAnimData;
+ if (pAnimData->isStatic || pAnimData->isPaused)
+ continue;
+
+ if (_objects[i].tick == pAnimData->maxTick) {
+ _objects[i].tick = 0;
+ if (pAnimData->animType == 4) {
+ pAnimData->isPaused = 1;
+ pAnimData->frame = 0;
+ } else {
+ pAnimData->frame++;
+ if (pAnimData->frame >=
+ _vm->_scenery->_animations[(int)pAnimData->animation].layers[pAnimData->layer]->framesCount) {
+ switch (pAnimData->animType) {
+ case 0:
+ pAnimData->frame = 0;
+ break;
+
+ case 1:
+ pAnimData->frame = 0;
+
+ *(_objects[i].pPosX) =
+ *(_objects[i].pPosX) +
+ _vm->_scenery->_animations[(int)pAnimData->animation].layers[pAnimData->layer]->animDeltaX;
+
+ *(_objects[i].pPosY) =
+ *(_objects[i].pPosY) +
+ _vm->_scenery->_animations[(int)pAnimData->animation].layers[pAnimData->layer]->animDeltaY;
+ break;
+
+ case 2:
+ pAnimData->frame = 0;
+ pAnimData->animation =
+ pAnimData->newAnimation;
+ pAnimData->layer =
+ pAnimData->newLayer;
+ break;
+
+ case 3:
+ pAnimData->animType = 4;
+ pAnimData->frame = 0;
+ break;
+
+ case 5:
+ pAnimData->isStatic = 1;
+ pAnimData->frame = 0;
+ break;
+
+ case 6:
+ pAnimData->frame--;
+ pAnimData->isPaused = 1;
+ break;
+ }
+ pAnimData->newCycle = 1;
+ } else {
+ pAnimData->newCycle = 0;
+ }
+ }
+ } else {
+ _objects[i].tick++;
+ }
+ }
+}
+
+void Mult::interGetObjAnimSize(void) {
+ Mult_AnimData *pAnimData;
+ int16 objIndex;
+
+ _vm->_inter->evalExpr(&objIndex);
+ pAnimData = _objects[objIndex].pAnimData;
+ if (pAnimData->isStatic == 0) {
+ _vm->_scenery->updateAnim(pAnimData->layer, pAnimData->frame,
+ pAnimData->animation, 0, *(_objects[objIndex].pPosX),
+ *(_objects[objIndex].pPosY), 0);
+ }
+ WRITE_VAR_OFFSET(_vm->_parse->parseVarIndex(), _vm->_scenery->_toRedrawLeft);
+ WRITE_VAR_OFFSET(_vm->_parse->parseVarIndex(), _vm->_scenery->_toRedrawTop);
+ WRITE_VAR_OFFSET(_vm->_parse->parseVarIndex(), _vm->_scenery->_toRedrawRight);
+ WRITE_VAR_OFFSET(_vm->_parse->parseVarIndex(), _vm->_scenery->_toRedrawBottom);
+}
+
+void Mult::interInitMult(void) {
+ int16 oldAnimHeight;
+ int16 oldAnimWidth;
+ int16 oldObjCount;
+ int16 i;
+ int16 posXVar;
+ int16 posYVar;
+ int16 animDataVar;
+
+ oldAnimWidth = _vm->_anim->_areaWidth;
+ oldAnimHeight = _vm->_anim->_areaHeight;
+ oldObjCount = _objCount;
+
+ _vm->_anim->_areaLeft = _vm->_inter->load16();
+ _vm->_anim->_areaTop = _vm->_inter->load16();
+ _vm->_anim->_areaWidth = _vm->_inter->load16();
+ _vm->_anim->_areaHeight = _vm->_inter->load16();
+ _objCount = _vm->_inter->load16();
+ posXVar = _vm->_parse->parseVarIndex();
+ posYVar = _vm->_parse->parseVarIndex();
+ animDataVar = _vm->_parse->parseVarIndex();
+
+ if (_objects == 0) {
+ _renderData = new int16[_objCount * 9];
+ _objects = new Mult_Object[_objCount];
+
+ for (i = 0; i < _objCount; i++) {
+ _objects[i].pPosX = (int32 *)(_vm->_global->_inter_variables + i * 4 + (posXVar / 4) * 4);
+ _objects[i].pPosY = (int32 *)(_vm->_global->_inter_variables + i * 4 + (posYVar / 4) * 4);
+ _objects[i].pAnimData =
+ (Mult_AnimData *) (_vm->_global->_inter_variables + animDataVar +
+ i * 4 * _vm->_global->_inter_animDataSize);
+
+ _objects[i].pAnimData->isStatic = 1;
+ _objects[i].tick = 0;
+ _objects[i].lastLeft = -1;
+ _objects[i].lastRight = -1;
+ _objects[i].lastTop = -1;
+ _objects[i].lastBottom = -1;
+ }
+ } else if (oldObjCount != _objCount) {
+ error("interInitMult: Object count changed, but storage didn't (old count = %d, new count = %d)",
+ oldObjCount, _objCount);
+ }
+
+ if (_vm->_anim->_animSurf != 0 &&
+ (oldAnimWidth != _vm->_anim->_areaWidth
+ || oldAnimHeight != _vm->_anim->_areaHeight)) {
+ _vm->_video->freeSurfDesc(_vm->_anim->_animSurf);
+ _vm->_anim->_animSurf = 0;
+ }
+
+ if (_vm->_anim->_animSurf == 0) {
+ _vm->_anim->_animSurf = _vm->_video->initSurfDesc(_vm->_global->_videoMode,
+ _vm->_anim->_areaWidth, _vm->_anim->_areaHeight, 0);
+
+ _vm->_draw->_spritesArray[22] = _vm->_anim->_animSurf;
+ }
+
+ _vm->_video->drawSprite(_vm->_draw->_backSurface, _vm->_anim->_animSurf,
+ _vm->_anim->_areaLeft, _vm->_anim->_areaTop,
+ _vm->_anim->_areaLeft + _vm->_anim->_areaWidth - 1,
+ _vm->_anim->_areaTop + _vm->_anim->_areaHeight - 1, 0, 0, 0);
+
+ debug(4, "interInitMult: x = %d, y = %d, w = %d, h = %d",
+ _vm->_anim->_areaLeft, _vm->_anim->_areaTop, _vm->_anim->_areaWidth, _vm->_anim->_areaHeight);
+ debug(4, " _objCount = %d, animation data size = %d", _objCount, _vm->_global->_inter_animDataSize);
+}
+
+void Mult::freeMult(void) {
+ if (_vm->_anim->_animSurf != 0)
+ _vm->_video->freeSurfDesc(_vm->_anim->_animSurf);
+
+ delete[] _objects;
+ delete[] _renderData;
+
+ _objects = 0;
+ _renderData = 0;
+ _vm->_anim->_animSurf = 0;
+}
+
+void Mult::interLoadMult(void) {
+ int16 val;
+ int16 objIndex;
+ int16 i;
+ char *lmultData;
+
+ debug(4, "interLoadMult: Loading...");
+
+ _vm->_inter->evalExpr(&objIndex);
+ _vm->_inter->evalExpr(&val);
+ *_objects[objIndex].pPosX = val;
+ _vm->_inter->evalExpr(&val);
+ *_objects[objIndex].pPosY = val;
+
+ lmultData = (char *)_objects[objIndex].pAnimData;
+ for (i = 0; i < 11; i++) {
+ if ((char)READ_LE_UINT16(_vm->_global->_inter_execPtr) == (char)99) {
+ _vm->_inter->evalExpr(&val);
+ lmultData[i] = val;
+ } else {
+ _vm->_global->_inter_execPtr++;
+ }
+ }
+}
+
+void Mult::freeAll(void) {
+ int16 i;
+
+ freeMult();
+ for (i = 0; i < 10; i++)
+ _vm->_scenery->freeAnim(i);
+
+ for (i = 0; i < 10; i++)
+ _vm->_scenery->freeStatic(i);
+}
+
+void Mult::initAll(void) {
+ int16 i;
+
+ _objects = 0;
+ _vm->_anim->_animSurf = 0;
+ _renderData = 0;
+
+ for (i = 0; i < 10; i++)
+ _vm->_scenery->_animPictCount[i] = 0;
+
+ for (i = 0; i < 20; i++) {
+ _vm->_scenery->_spriteRefs[i] = 0;
+ _vm->_scenery->_spriteResId[i] = -1;
+ }
+
+ for (i = 0; i < 10; i++)
+ _vm->_scenery->_staticPictCount[i] = -1;
+
+ _vm->_scenery->_curStaticLayer = -1;
+ _vm->_scenery->_curStatic = -1;
+}
+
+void Mult::playSound(Snd::SoundDesc * soundDesc, int16 repCount, int16 freq,
+ int16 channel) {
+ _vm->_snd->playSample(soundDesc, repCount, freq);
+}
+
+char Mult::drawStatics(char stop) {
+ if (_staticKeys[_staticKeysCount - 1].frame > _frame)
+ stop = 0;
+
+ for (_counter = 0; _counter < _staticKeysCount;
+ _counter++) {
+ if (_staticKeys[_counter].frame != _frame
+ || _staticKeys[_counter].layer == -1)
+ continue;
+
+ for (_vm->_scenery->_curStatic = 0, _vm->_scenery->_curStaticLayer = _staticKeys[_counter].layer;
+ _vm->_scenery->_curStaticLayer >= _vm->_scenery->_statics[_staticIndices[_vm->_scenery->_curStatic]].layersCount;
+ _vm->_scenery->_curStatic++) {
+ _vm->_scenery->_curStaticLayer -=
+ _vm->_scenery->_statics[_staticIndices[_vm->_scenery->_curStatic]].layersCount;
+ }
+
+ _vm->_scenery->_curStatic = _staticIndices[_vm->_scenery->_curStatic];
+ _vm->_scenery->renderStatic(_vm->_scenery->_curStatic, _vm->_scenery->_curStaticLayer);
+ _vm->_video->drawSprite(_vm->_draw->_backSurface, _vm->_anim->_animSurf,
+ 0, 0, 319, 199, 0, 0, 0);
+ }
+ return stop;
+}
+
+void Mult::drawAnims(void) {
+ Mult_AnimKey *key;
+ Mult_Object *animObj;
+ int16 i;
+ int16 count;
+
+ for (_index = 0; _index < 4; _index++) {
+ for (_counter = 0; _counter < _animKeysCount[_index]; _counter++) {
+ key = &_animKeys[_index][_counter];
+ animObj = &_objects[_index];
+ if (key->frame != _frame)
+ continue;
+
+ if (key->layer != -1) {
+ (*animObj->pPosX) = key->posX;
+ (*animObj->pPosY) = key->posY;
+
+ animObj->pAnimData->frame = 0;
+ animObj->pAnimData->order = key->order;
+ animObj->pAnimData->animType = 1;
+
+ animObj->pAnimData->isPaused = 0;
+ animObj->pAnimData->isStatic = 0;
+ animObj->pAnimData->maxTick = 0;
+ animObj->tick = 0;
+ animObj->pAnimData->layer = key->layer;
+
+ count = _vm->_scenery->_animations[_animIndices[0]].layersCount;
+ i = 0;
+ while (animObj->pAnimData->layer >= count) {
+ animObj->pAnimData->layer -= count;
+ i++;
+
+ count = _vm->_scenery->_animations[_animIndices[i]].layersCount;
+ }
+ animObj->pAnimData->animation = _animIndices[i];
+ } else {
+ animObj->pAnimData->isStatic = 1;
+ }
+ }
+ }
+}
+
+void Mult::drawText(char *pStop, char *pStopNoClear) {
+ char *savedIP;
+
+ int16 cmd;
+ for (_index = 0; _index < _textKeysCount; _index++) {
+ if (_textKeys[_index].frame != _frame)
+ continue;
+
+ cmd = _textKeys[_index].cmd;
+ if (cmd == 0) {
+ *pStop = 0;
+ } else if (cmd == 1) {
+ *pStopNoClear = 1;
+ _frameStart = 0;
+ } else if (cmd == 3) {
+ *pStop = 0;
+ savedIP = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr = (char *)(&_textKeys[_index].index);
+ _vm->_global->_inter_execPtr = savedIP;
+ }
+ }
+}
+
+char Mult::prepPalAnim(char stop) {
+ _palKeyIndex = -1;
+ do {
+ _palKeyIndex++;
+ if (_palKeyIndex >= _palKeysCount)
+ return stop;
+ } while (_palKeys[_palKeyIndex].frame != _frame);
+
+ if (_palKeys[_palKeyIndex].cmd == -1) {
+ stop = 0;
+ _doPalSubst = 0;
+ _vm->_global->_pPaletteDesc->vgaPal = _oldPalette;
+
+ memcpy((char *)_palAnimPalette, (char *)_vm->_global->_pPaletteDesc->vgaPal, 768);
+
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ } else {
+ stop = 0;
+ _doPalSubst = 1;
+ _palAnimKey = _palKeyIndex;
+
+ _palAnimIndices[0] = 0;
+ _palAnimIndices[1] = 0;
+ _palAnimIndices[2] = 0;
+ _palAnimIndices[3] = 0;
+
+ _vm->_global->_pPaletteDesc->vgaPal = _palAnimPalette;
+ }
+ return stop;
+}
+
+void Mult::doPalAnim(void) {
+ int16 off;
+ int16 off2;
+ Video::Color *palPtr;
+ Mult_PalKey *palKey;
+
+ if (_doPalSubst == 0)
+ return;
+
+ for (_index = 0; _index < 4; _index++) {
+ palKey = &_palKeys[_palAnimKey];
+
+ if ((_frame % palKey->rates[_index]) != 0)
+ continue;
+
+ _palAnimRed[_index] =
+ _vm->_global->_pPaletteDesc->vgaPal[palKey->subst[0][_index] - 1].red;
+ _palAnimGreen[_index] =
+ _vm->_global->_pPaletteDesc->vgaPal[palKey->subst[0][_index] - 1].green;
+ _palAnimBlue[_index] =
+ _vm->_global->_pPaletteDesc->vgaPal[palKey->subst[0][_index] - 1].blue;
+
+ while (1) {
+ off = palKey->subst[(_palAnimIndices[_index] + 1) % 16][_index];
+ if (off == 0) {
+ off = palKey->subst[_palAnimIndices[_index]][_index] - 1;
+
+ _vm->_global->_pPaletteDesc->vgaPal[off].red = _palAnimRed[_index];
+ _vm->_global->_pPaletteDesc->vgaPal[off].green = _palAnimGreen[_index];
+ _vm->_global->_pPaletteDesc->vgaPal[off].blue = _palAnimBlue[_index];
+ } else {
+ off = palKey->subst[(_palAnimIndices[_index] + 1) % 16][_index] - 1;
+ off2 = palKey->subst[_palAnimIndices[_index]][_index] - 1;
+
+ _vm->_global->_pPaletteDesc->vgaPal[off2].red = _vm->_global->_pPaletteDesc->vgaPal[off].red;
+ _vm->_global->_pPaletteDesc->vgaPal[off2].green = _vm->_global->_pPaletteDesc->vgaPal[off].green;
+ _vm->_global->_pPaletteDesc->vgaPal[off2].blue = _vm->_global->_pPaletteDesc->vgaPal[off].blue;
+ }
+
+ _palAnimIndices[_index] = (_palAnimIndices[_index] + 1) % 16;
+
+ off = palKey->subst[_palAnimIndices[_index]][_index];
+
+ if (off == 0) {
+ _palAnimIndices[_index] = 0;
+ off = palKey->subst[0][_index] - 1;
+
+ _palAnimRed[_index] = _vm->_global->_pPaletteDesc->vgaPal[off].red;
+ _palAnimGreen[_index] = _vm->_global->_pPaletteDesc->vgaPal[off].green;
+ _palAnimBlue[_index] = _vm->_global->_pPaletteDesc->vgaPal[off].blue;
+ }
+ if (_palAnimIndices[_index] == 0)
+ break;
+ }
+ }
+
+ if (_vm->_global->_colorCount == 256) {
+ _vm->_video->waitRetrace(_vm->_global->_videoMode);
+
+ palPtr = _vm->_global->_pPaletteDesc->vgaPal;
+ for (_counter = 0; _counter < 16; _counter++) {
+ _vm->_video->setPalElem(_counter, palPtr->red, palPtr->green, palPtr->blue, 0, 0x13);
+ palPtr++;
+ }
+
+ palPtr = _vm->_global->_pPaletteDesc->vgaPal;
+ for (_counter = 0; _counter < 16; _counter++) {
+ _vm->_global->_redPalette[_counter] = palPtr->red;
+ _vm->_global->_greenPalette[_counter] = palPtr->green;
+ _vm->_global->_bluePalette[_counter] = palPtr->blue;
+ palPtr++;
+ }
+ } else {
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ }
+}
+
+char Mult::doFadeAnim(char stop) {
+ Mult_PalFadeKey *fadeKey;
+
+ for (_index = 0; _index < _palFadeKeysCount; _index++) {
+ fadeKey = &_palFadeKeys[_index];
+
+ if (fadeKey->frame != _frame)
+ continue;
+
+ stop = 0;
+ if ((fadeKey->flag & 1) == 0) {
+ if (fadeKey->fade == 0) {
+ _vm->_global->_pPaletteDesc->vgaPal = _fadePal[fadeKey->palIndex];
+ _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ } else {
+ _vm->_global->_pPaletteDesc->vgaPal = _fadePal[fadeKey->palIndex];
+ _vm->_palanim->fade(_vm->_global->_pPaletteDesc, fadeKey->fade, 0);
+ }
+ } else {
+ _vm->_global->_pPaletteDesc->vgaPal = _fadePal[fadeKey->palIndex];
+ _vm->_palanim->fade(_vm->_global->_pPaletteDesc, fadeKey->fade, -1);
+
+ _palFadingRed = (fadeKey->flag >> 1) & 1;
+ _palFadingGreen = (fadeKey->flag >> 2) & 1;
+ _palFadingBlue = (fadeKey->flag >> 3) & 1;
+ }
+ }
+
+ if (_palFadingRed) {
+ _palFadingRed = !_vm->_palanim->fadeStep(1);
+ stop = 0;
+ }
+ if (_palFadingGreen) {
+ _palFadingGreen = !_vm->_palanim->fadeStep(2);
+ stop = 0;
+ }
+ if (_palFadingBlue) {
+ _palFadingBlue = !_vm->_palanim->fadeStep(3);
+ stop = 0;
+ }
+ return stop;
+}
+
+char Mult::doSoundAnim(char stop) {
+ Mult_SndKey *sndKey;
+ for (_index = 0; _index < _sndKeysCount; _index++) {
+ sndKey = &_sndKeys[_index];
+ if (sndKey->frame != _frame)
+ continue;
+
+ if (sndKey->cmd != -1) {
+ if (sndKey->cmd == 1) {
+ _vm->_snd->stopSound(0);
+ stop = 0;
+ playSound(_vm->_game->_soundSamples[sndKey->soundIndex], sndKey->repCount,
+ sndKey->freq, sndKey->channel);
+
+ } else if (sndKey->cmd == 4) {
+ _vm->_snd->stopSound(0);
+ stop = 0;
+ playSound(_vm->_game->_soundSamples[sndKey->soundIndex], sndKey->repCount,
+ sndKey->freq, sndKey->channel);
+ }
+ } else {
+ if (_vm->_snd->_playingSound)
+ _vm->_snd->stopSound(sndKey->channel);
+ }
+ }
+ return stop;
+}
+
+void Mult::playMult(int16 startFrame, int16 endFrame, char checkEscape,
+ char handleMouse) {
+ char stopNoClear;
+ char stop;
+ Mult_Object *multObj;
+ Mult_AnimData *animData;
+
+ if (_multData == 0)
+ return;
+
+ stopNoClear = 0;
+ _frame = startFrame;
+ if (endFrame == -1)
+ endFrame = 32767;
+
+ if (_frame == -1) {
+ _doPalSubst = 0;
+ _palFadingRed = 0;
+ _palFadingGreen = 0;
+ _palFadingBlue = 0;
+
+ _oldPalette = _vm->_global->_pPaletteDesc->vgaPal;
+ memcpy((char *)_palAnimPalette, (char *)_vm->_global->_pPaletteDesc->vgaPal, 768);
+
+ if (_vm->_anim->_animSurf == 0) {
+ _vm->_util->setFrameRate(_frameRate);
+ _vm->_anim->_areaTop = 0;
+ _vm->_anim->_areaLeft = 0;
+ _vm->_anim->_areaWidth = 320;
+ _vm->_anim->_areaHeight = 200;
+ _objCount = 4;
+
+ _objects = new Mult_Object[_objCount];
+ _renderData = new int16[9 * _objCount];
+
+ _animArrayX = new int32[_objCount];
+ _animArrayY = new int32[_objCount];
+
+ _animArrayData = new Mult_AnimData[_objCount];
+
+ for (_counter = 0; _counter < _objCount; _counter++) {
+ multObj = &_objects[_counter];
+
+ multObj->pPosX = (int32 *)&_animArrayX[_counter];
+ multObj->pPosY = (int32 *)&_animArrayY[_counter];
+
+ multObj->pAnimData = &_animArrayData[_counter];
+
+ animData = multObj->pAnimData;
+ animData->isStatic = 1;
+
+ multObj->tick = 0;
+ multObj->lastLeft = -1;
+ multObj->lastTop = -1;
+ multObj->lastRight = -1;
+ multObj->lastBottom = -1;
+ }
+
+ _vm->_anim->_animSurf =
+ _vm->_video->initSurfDesc(_vm->_global->_videoMode, 320, 200, 0);
+ _vm->_draw->_spritesArray[22] = _vm->_anim->_animSurf;
+
+ _vm->_video->drawSprite(_vm->_draw->_backSurface, _vm->_anim->_animSurf,
+ 0, 0, 319, 199, 0, 0, 0);
+
+ _animDataAllocated = 1;
+ } else
+ _animDataAllocated = 0;
+ _frame = 0;
+ }
+
+ do {
+ stop = 1;
+
+ if (VAR(58) == 0) {
+ stop = drawStatics(stop);
+ drawAnims();
+ }
+
+ animate();
+ if (handleMouse) {
+ _vm->_draw->animateCursor(-1);
+ } else {
+ _vm->_draw->blitInvalidated();
+ }
+
+ if (VAR(58) == 0) {
+ drawText(&stop, &stopNoClear);
+ }
+
+ stop = prepPalAnim(stop);
+ doPalAnim();
+
+ stop = doFadeAnim(stop);
+ stop = doSoundAnim(stop);
+
+ if (_frame >= endFrame)
+ stopNoClear = 1;
+
+ if (_vm->_snd->_playingSound)
+ stop = 0;
+
+ _vm->_util->processInput();
+ if (checkEscape && _vm->_util->checkKey() == 0x11b) // Esc
+ stop = 1;
+
+ _frame++;
+ _vm->_util->waitEndFrame();
+ } while (stop == 0 && stopNoClear == 0);
+
+ if (stopNoClear == 0) {
+ if (_animDataAllocated) {
+ delete[] _objects;
+ _objects = 0;
+
+ delete[] _renderData;
+ _renderData = 0;
+
+ delete[] _animArrayX;
+ _animArrayX = 0;
+
+ delete[] _animArrayY;
+ _animArrayY = 0;
+
+ delete[] _animArrayData;
+ _animArrayData = 0;
+
+ if (_vm->_anim->_animSurf)
+ _vm->_video->freeSurfDesc(_vm->_anim->_animSurf);
+ _vm->_anim->_animSurf = 0;
+
+ _animDataAllocated = 0;
+ }
+
+ if (_vm->_snd->_playingSound != 0)
+ _vm->_snd->stopSound(10);
+
+ WRITE_VAR(57, (uint32)-1);
+ } else {
+ WRITE_VAR(57, _frame - 1 - _frameStart);
+ }
+}
+
+void Mult::zeroMultData(void) {
+ _multData = 0;
+}
+
+void Mult::loadMult(int16 resId) {
+ char animCount;
+ char staticCount;
+ int16 palIndex;
+ int16 i, j;
+
+ _sndSlotsCount = 0;
+ _frameStart = 0;
+ _multData = _vm->_game->loadExtData(resId, 0, 0);
+ _dataPtr = _multData;
+
+ staticCount = _dataPtr[0];
+ animCount = _dataPtr[1];
+ _dataPtr += 2;
+ staticCount++;
+ animCount++;
+
+ for (i = 0; i < staticCount; i++, _dataPtr += 14) {
+ _staticIndices[i] = _vm->_scenery->loadStatic(1);
+
+ if (_staticIndices[i] >= 100) {
+ _staticIndices[i] -= 100;
+ _staticLoaded[i] = 1;
+ } else {
+ _staticLoaded[i] = 0;
+ }
+ }
+
+ for (i = 0; i < animCount; i++, _dataPtr += 14) {
+ _animIndices[i] = _vm->_scenery->loadAnim(1);
+
+ if (_animIndices[i] >= 100) {
+ _animIndices[i] -= 100;
+ _animLoaded[i] = 1;
+ } else {
+ _animLoaded[i] = 0;
+ }
+ }
+
+ _frameRate = READ_LE_UINT16(_dataPtr);
+ _dataPtr += 2;
+
+ _staticKeysCount = READ_LE_UINT16(_dataPtr);
+ _dataPtr += 2;
+
+ _staticKeys = new Mult_StaticKey[_staticKeysCount];
+ for (i = 0; i < _staticKeysCount; i++, _dataPtr += 4) {
+ _staticKeys[i].frame = (int16)READ_LE_UINT16(_dataPtr);
+ _staticKeys[i].layer = (int16)READ_LE_UINT16(_dataPtr + 2);
+ }
+
+ for (j = 0; j < 4; j++) {
+ _animKeysCount[j] = READ_LE_UINT16(_dataPtr);
+ _dataPtr += 2;
+
+ _animKeys[j] = new Mult_AnimKey[_animKeysCount[j]];
+ for (i = 0; i < _animKeysCount[j]; i++, _dataPtr += 10) {
+ _animKeys[j][i].frame = (int16)READ_LE_UINT16(_dataPtr);
+ _animKeys[j][i].layer = (int16)READ_LE_UINT16(_dataPtr + 2);
+ _animKeys[j][i].posX = (int16)READ_LE_UINT16(_dataPtr + 4);
+ _animKeys[j][i].posY = (int16)READ_LE_UINT16(_dataPtr + 6);
+ _animKeys[j][i].order = (int16)READ_LE_UINT16(_dataPtr + 8);
+ }
+ }
+
+ for (palIndex = 0; palIndex < 5; palIndex++) {
+ for (i = 0; i < 16; i++) {
+ _fadePal[palIndex][i].red = _dataPtr[0];
+ _fadePal[palIndex][i].green = _dataPtr[1];
+ _fadePal[palIndex][i].blue = _dataPtr[2];
+ _dataPtr += 3;
+ }
+ }
+
+ _palFadeKeysCount = READ_LE_UINT16(_dataPtr);
+ _dataPtr += 2;
+ _palFadeKeys = new Mult_PalFadeKey[_palFadeKeysCount];
+
+ for (i = 0; i < _palFadeKeysCount; i++, _dataPtr += 7) {
+ _palFadeKeys[i].frame = (int16)READ_LE_UINT16(_dataPtr);
+ _palFadeKeys[i].fade = (int16)READ_LE_UINT16(_dataPtr + 2);
+ _palFadeKeys[i].palIndex = (int16)READ_LE_UINT16(_dataPtr + 4);
+ _palFadeKeys[i].flag = *(_dataPtr + 6);
+ }
+
+ _palKeysCount = READ_LE_UINT16(_dataPtr);
+ _dataPtr += 2;
+
+ _palKeys = new Mult_PalKey[_palKeysCount];
+ for (i = 0; i < _palKeysCount; i++, _dataPtr += 80) {
+ _palKeys[i].frame = (int16)READ_LE_UINT16(_dataPtr);
+ _palKeys[i].cmd = (int16)READ_LE_UINT16(_dataPtr + 2);
+ _palKeys[i].rates[0] = (int16)READ_LE_UINT16(_dataPtr + 4);
+ _palKeys[i].rates[1] = (int16)READ_LE_UINT16(_dataPtr + 6);
+ _palKeys[i].rates[2] = (int16)READ_LE_UINT16(_dataPtr + 8);
+ _palKeys[i].rates[3] = (int16)READ_LE_UINT16(_dataPtr + 10);
+ _palKeys[i].unknown0 = (int16)READ_LE_UINT16(_dataPtr + 12);
+ _palKeys[i].unknown1 = (int16)READ_LE_UINT16(_dataPtr + 14);
+ memcpy(_palKeys[i].subst, _dataPtr + 16, 64);
+ }
+
+ _textKeysCount = READ_LE_UINT16(_dataPtr);
+ _dataPtr += 2;
+ _textKeys = new Mult_TextKey[_textKeysCount];
+
+ for (i = 0; i < _textKeysCount; i++, _dataPtr += 28) {
+ _textKeys[i].frame = (int16)READ_LE_UINT16(_dataPtr);
+ _textKeys[i].cmd = (int16)READ_LE_UINT16(_dataPtr + 2);
+ for (int k = 0; k < 9; ++k)
+ _textKeys[i].unknown0[k] = (int16)READ_LE_UINT16(_dataPtr + 4 + (k * 2));
+ _textKeys[i].index = (int16)READ_LE_UINT16(_dataPtr + 22);
+ _textKeys[i].unknown1[0] = (int16)READ_LE_UINT16(_dataPtr + 24);
+ _textKeys[i].unknown1[1] = (int16)READ_LE_UINT16(_dataPtr + 26);
+ }
+
+ _sndKeysCount = READ_LE_UINT16(_dataPtr);
+ _dataPtr += 2;
+
+ _sndKeys = new Mult_SndKey[_sndKeysCount];
+ for (i = 0; i < _sndKeysCount; i++) {
+ _sndKeys[i].frame = (int16)READ_LE_UINT16(_dataPtr);
+ _sndKeys[i].cmd = (int16)READ_LE_UINT16(_dataPtr + 2);
+ _sndKeys[i].freq = (int16)READ_LE_UINT16(_dataPtr + 4);
+ _sndKeys[i].channel = (int16)READ_LE_UINT16(_dataPtr + 6);
+ _sndKeys[i].repCount = (int16)READ_LE_UINT16(_dataPtr + 8);
+ _sndKeys[i].resId = (int16)READ_LE_UINT16(_dataPtr + 10);
+ _sndKeys[i].soundIndex = (int16)READ_LE_UINT16(_dataPtr + 12);
+
+ _sndKeys[i].soundIndex = -1;
+ _sndKeys[i].resId = -1;
+ _dataPtr += 36;
+ switch (_sndKeys[i].cmd) {
+ case 1:
+ case 4:
+ _sndKeys[i].resId = READ_LE_UINT16(_vm->_global->_inter_execPtr);
+
+ for (j = 0; j < i; j++) {
+ if (_sndKeys[i].resId ==
+ _sndKeys[j].resId) {
+ _sndKeys[i].soundIndex =
+ _sndKeys[j].soundIndex;
+ _vm->_global->_inter_execPtr += 2;
+ break;
+ }
+ }
+ if (i == j) {
+ _vm->_game->interLoadSound(19 - _sndSlotsCount);
+ _sndKeys[i].soundIndex =
+ 19 - _sndSlotsCount;
+ _sndSlotsCount++;
+ }
+ break;
+
+ case 3:
+ _vm->_global->_inter_execPtr += 6;
+ break;
+
+ case 5:
+ _vm->_global->_inter_execPtr += _sndKeys[i].freq * 2;
+ break;
+ }
+ }
+}
+
+void Mult::freeMultKeys(void) {
+ int i;
+ char animCount;
+ char staticCount;
+
+ _dataPtr = _multData;
+ staticCount = _dataPtr[0];
+ animCount = _dataPtr[1];
+
+ delete[] _dataPtr;
+
+ staticCount++;
+ animCount++;
+ for (i = 0; i < staticCount; i++) {
+
+ if (_staticLoaded[i] != 0)
+ _vm->_scenery->freeStatic(_staticIndices[i]);
+ }
+
+ for (i = 0; i < animCount; i++) {
+ if (_animLoaded[i] != 0)
+ _vm->_scenery->freeAnim(_animIndices[i]);
+ }
+
+ delete[] _staticKeys;
+
+ for (i = 0; i < 4; i++)
+ delete[] _animKeys[i];
+
+ delete[] _palFadeKeys;
+ delete[] _palKeys;
+ delete[] _textKeys;
+
+ for (i = 0; i < _sndSlotsCount; i++) {
+ _vm->_game->freeSoundSlot(19 - i);
+ }
+
+ delete[] _sndKeys;
+
+ _multData = 0;
+
+ if (_animDataAllocated != 0) {
+ delete[] _objects;
+ _objects = 0;
+
+ delete[] _renderData;
+ _renderData = 0;
+
+ delete[] _animArrayX;
+ _animArrayX = 0;
+
+ delete[] _animArrayY;
+ _animArrayY = 0;
+
+ delete[] _animArrayData;
+ _animArrayData = 0;
+
+ if (_vm->_anim->_animSurf)
+ _vm->_video->freeSurfDesc(_vm->_anim->_animSurf);
+ _vm->_anim->_animSurf = 0;
+
+ _animDataAllocated = 0;
+ }
+}
+
+void Mult::checkFreeMult(void) {
+ if (_multData != 0)
+ freeMultKeys();
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/mult.h b/engines/gob/mult.h
new file mode 100644
index 0000000000..992c01cba3
--- /dev/null
+++ b/engines/gob/mult.h
@@ -0,0 +1,208 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_MULT_H
+#define GOB_MULT_H
+
+#include "gob/sound.h"
+
+namespace Gob {
+
+class Mult {
+public:
+#pragma START_PACK_STRUCTS
+ struct Mult_AnimData {
+ int8 animation;
+ int8 layer;
+ int8 frame;
+ int8 animType;
+ int8 order;
+ int8 isPaused;
+ int8 isStatic;
+ int8 maxTick;
+ int8 unknown;
+ int8 newLayer;
+ int8 newAnimation;
+ byte intersected;
+ int8 newCycle;
+ } GCC_PACK;
+
+ struct Mult_Object {
+ int32 *pPosX;
+ int32 *pPosY;
+ Mult_AnimData *pAnimData;
+ int16 tick;
+ int16 lastLeft;
+ int16 lastRight;
+ int16 lastTop;
+ int16 lastBottom;
+ };
+
+ struct Mult_StaticKey {
+ int16 frame;
+ int16 layer;
+ } GCC_PACK;
+
+ struct Mult_AnimKey {
+ int16 frame;
+ int16 layer;
+ int16 posX;
+ int16 posY;
+ int16 order;
+ } GCC_PACK;
+
+ struct Mult_TextKey {
+ int16 frame;
+ int16 cmd;
+ int16 unknown0[9];
+ int16 index;
+ int16 unknown1[2];
+ } GCC_PACK;
+
+ struct Mult_PalKey {
+ int16 frame;
+ int16 cmd;
+ int16 rates[4];
+ int16 unknown0;
+ int16 unknown1;
+ int8 subst[16][4];
+ } GCC_PACK;
+
+ struct Mult_PalFadeKey {
+ int16 frame;
+ int16 fade;
+ int16 palIndex;
+ int8 flag;
+ } GCC_PACK;
+
+ struct Mult_SndKey {
+ int16 frame;
+ int16 cmd;
+ int16 freq;
+ int16 channel;
+ int16 repCount;
+ int16 resId;
+ int16 soundIndex;
+ } GCC_PACK;
+#pragma END_PACK_STRUCTS
+
+ // Globals
+
+ Mult_Object *_objects;
+ int16 *_renderData;
+ int16 _objCount;
+ Video::SurfaceDesc *_underAnimSurf;
+
+ char *_multData;
+ int16 _frame;
+ char _doPalSubst;
+ int16 _counter;
+ int16 _frameRate;
+
+ int32 *_animArrayX;
+ int32 *_animArrayY;
+
+ Mult_AnimData *_animArrayData;
+
+ int16 _index;
+
+ // Static keys
+ int16 _staticKeysCount;
+ Mult_StaticKey *_staticKeys;
+ int16 _staticIndices[10];
+
+ // Anim keys
+ Mult_AnimKey *_animKeys[4];
+ int16 _animKeysCount[4];
+ int16 _animLayer;
+ int16 _animIndices[10];
+
+ // Text keys
+ int16 _textKeysCount;
+ Mult_TextKey *_textKeys;
+
+ int16 _frameStart;
+
+ // Palette keys
+ int16 _palKeyIndex;
+ int16 _palKeysCount;
+ Mult_PalKey *_palKeys;
+ Video::Color *_oldPalette;
+ Video::Color _palAnimPalette[256];
+ int16 _palAnimKey;
+ int16 _palAnimIndices[4];
+ int16 _palAnimRed[4];
+ int16 _palAnimGreen[4];
+ int16 _palAnimBlue[4];
+
+ // Palette fading
+ Mult_PalFadeKey *_palFadeKeys;
+ int16 _palFadeKeysCount;
+ char _palFadingRed;
+ char _palFadingGreen;
+ char _palFadingBlue;
+
+ char _animDataAllocated;
+
+ char *_dataPtr;
+ int16 _staticLoaded[10];
+ int16 _animLoaded[10];
+ int16 _sndSlotsCount;
+
+ // Sound keys
+ int16 _sndKeysCount;
+ Mult_SndKey *_sndKeys;
+
+ void zeroMultData(void);
+ void loadMult(int16 resId);
+ void freeMultKeys(void);
+ void checkFreeMult(void);
+ void playMult(int16 startFrame, int16 endFrame, char checkEscape,
+ char handleMouse);
+ void animate(void);
+ void interGetObjAnimSize(void);
+ void interInitMult(void);
+ void freeMult(void);
+ void interLoadMult(void);
+ void freeAll(void);
+ void initAll(void);
+ void playSound(Snd::SoundDesc * soundDesc, int16 repCount, int16 freq,
+ int16 channel);
+
+ Mult(GobEngine *vm);
+
+protected:
+ Video::Color _fadePal[5][16];
+ GobEngine *_vm;
+
+ char drawStatics(char stop);
+ void drawAnims(void);
+ void drawText(char *pStop, char *pStopNoClear);
+ char prepPalAnim(char stop);
+ void doPalAnim(void);
+ char doFadeAnim(char stop);
+ char doSoundAnim(char stop);
+};
+
+} // End of namespace Gob
+
+#endif /* __MULT_H */
diff --git a/engines/gob/music.cpp b/engines/gob/music.cpp
new file mode 100644
index 0000000000..3fbb37160f
--- /dev/null
+++ b/engines/gob/music.cpp
@@ -0,0 +1,436 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ * Original ADL-Player source Copyright (C) 2004 by Dorian Gray
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "gob/music.h"
+#include "gob/gob.h"
+#include "gob/game.h"
+
+namespace Gob {
+
+const char *Music::_tracks[][2] = {
+ {"avt00.tot", "mine"},
+ {"avt001.tot", "nuit"},
+ {"avt002.tot", "campagne"},
+ {"avt003.tot", "extsor1"},
+ {"avt004.tot", "interieure"},
+ {"avt005.tot", "zombie"},
+ {"avt006.tot", "zombie"},
+ {"avt007.tot", "campagne"},
+ {"avt008.tot", "campagne"},
+ {"avt009.tot", "extsor1"},
+ {"avt010.tot", "extsor1"},
+ {"avt011.tot", "interieure"},
+ {"avt012.tot", "zombie"},
+ {"avt014.tot", "nuit"},
+ {"avt015.tot", "interieure"},
+ {"avt016.tot", "statue"},
+ {"avt017.tot", "zombie"},
+ {"avt018.tot", "statue"},
+ {"avt019.tot", "mine"},
+ {"avt020.tot", "statue"},
+ {"avt021.tot", "mine"},
+ {"avt022.tot", "zombie"}
+};
+
+const char *Music::_tracksToFiles[][2] = {
+ {"campagne", "Musmac2.adl"},
+ {"extsor1", "Musmac3.adl"},
+ {"interieure", "Musmac4.adl"},
+ {"mine", "Musmac5.adl"},
+ {"nuit", "Musmac6.adl"},
+ {"statue", "Musmac2.adl"},
+ {"zombie", "Musmac3.adl"}
+};
+
+const unsigned char Music::_operators[] = {0, 1, 2, 8, 9, 10, 16, 17, 18};
+const unsigned char Music::_volRegNums[] = {
+ 3, 4, 5,
+ 11, 12, 13,
+ 19, 20, 21
+};
+
+Music::Music(GobEngine *vm) : _vm(vm) {
+ _data = 0;
+ _playPos = 0;
+ _dataSize = 0;
+ _rate = _vm->_mixer->getOutputRate();
+ _opl = makeAdlibOPL(_rate);
+ _vm->_mixer->setupPremix(this, Audio::Mixer::kMusicSoundType);
+ _first = true;
+ _ended = false;
+ _playing = false;
+ _looping = true;
+ _samplesTillPoll = 0;
+
+ setFreqs();
+}
+
+Music::~Music(void) {
+ if (_data);
+ delete _data;
+ _vm->_mixer->setupPremix(0);
+}
+
+void Music::premixerCall(int16 *buf, uint len) {
+ if (!_playing) {
+ memset(buf, 0, 2 * len * sizeof(int16));
+ return;
+ }
+ else {
+ if (_first) {
+ memset(buf, 0, 2 * len * sizeof(int16));
+ pollMusic();
+ return;
+ }
+ else {
+ uint32 render;
+ int16 *data = buf;
+ uint datalen = len;
+ while (datalen) {
+ if (_samplesTillPoll) {
+ render = (datalen > _samplesTillPoll) ? (_samplesTillPoll) : (datalen);
+ datalen -= render;
+ _samplesTillPoll -= render;
+ YM3812UpdateOne(_opl, data, render);
+ data += render;
+ } else {
+ pollMusic();
+ if (_ended) {
+ memset(data, 0, datalen * sizeof(int16));
+ datalen = 0;
+ }
+ }
+ }
+ }
+ if (_ended) {
+ _first = true;
+ _ended = false;
+ _playPos = _data + 3 + (_data[1] + 1) * 0x38;
+ _samplesTillPoll = 0;
+ if (_looping) {
+ reset();
+ setVoices();
+ }
+ else
+ _playing = false;
+ }
+ // Convert mono data to stereo
+ for (int i = (len - 1); i >= 0; i--) {
+ buf[2 * i] = buf[2 * i + 1] = buf[i];
+ }
+ }
+}
+
+void Music::writeOPL(byte reg, byte val) {
+ debug(5, "writeOPL(%02X, %02X)", reg, val);
+ OPLWriteReg(_opl, reg, val);
+}
+
+void Music::setFreqs(void) {
+ byte lin;
+ byte col;
+ long val = 0;
+
+ // Run through the 11 channels
+ for (lin = 0; lin < 11; lin ++) {
+ _notes[lin] = 0;
+ _notCol[lin] = 0;
+ _notLin[lin] = 0;
+ _notOn[lin] = false;
+ }
+
+ // Run through the 25 lines
+ for (lin = 0; lin < 25; lin ++) {
+ // Run through the 12 columns
+ for (col = 0; col < 12; col ++) {
+ if (!col)
+ val = (((0x2710L + lin * 0x18) * 0xCB78 / 0x3D090) << 0xE) * 9 / 0x1B503;
+ _freqs[lin][col] = (short)((val + 4) >> 3);
+ val = val * 0x6A / 0x64;
+ // val = val * 392 / 370;
+ }
+ }
+}
+
+void Music::reset() {
+ // Set frequencies and octave to 0; notes off
+ for (int i = 0; i < 9; i++) {
+ writeOPL(0xA0 | i, 0);
+ writeOPL(0xB0 | i, 0);
+ writeOPL(0xE0 | _operators[i] , 0);
+ writeOPL(0xE0 | _operators[i] + 3, 0);
+ }
+
+ // Authorize the control of the waveformes
+ writeOPL(0x01, 0x20);
+}
+
+void Music::setVoices() {
+ // Definitions of the 9 instruments
+ for (int i = 0; i < 9; i++)
+ setVoice(i, i, true);
+}
+
+void Music::setVoice(byte voice, byte instr, bool set) {
+ unsigned short *strct;
+ byte channel;
+
+ // i = 0 : 0 1 2 3 4 5 6 7 8 9 10 11 12 26
+ // i = 1 : 13 14 15 16 17 18 19 20 21 22 23 24 25 27
+ for (int i = 0; i < 2; i++) {
+ strct = (unsigned short*)(_data + 3 + instr * 0x38 + i * 0x1A);
+ channel = _operators[voice] + i * 3;
+ writeOPL(0xBD, 0x00);
+ writeOPL(0x08, 0x00);
+ writeOPL(0x40 | channel, ((strct[0] & 3) << 6) | (strct[8] & 0x3F));
+ if (!i)
+ writeOPL(0xC0 | voice , ((strct[2] & 7) << 1) | (1 - (strct[12] & 1)));
+ writeOPL(0x60 | channel, ((strct[3] & 0xF) << 4) | (strct[6] & 0xF));
+ writeOPL(0x80 | channel, ((strct[4] & 0xF) << 4) | (strct[7] & 0xF));
+ writeOPL(0x20 | channel, ((strct[9] & 1) << 7) |
+ ((strct[10] & 1) << 6) | ((strct[5] & 1) << 5) |
+ ((strct[11] & 1) << 4) | (strct[1] & 0xF));
+ if (!i)
+ writeOPL(0xE0 | channel, (strct[26] & 3));
+ else
+ writeOPL(0xE0 | channel, (strct[14] & 3));
+ if (i && set)
+ writeOPL(0x40 | channel, 0);
+ }
+}
+
+void Music::setKey(byte voice, byte note, bool on, bool spec) {
+ short freq = 0;
+ short octa = 0;
+
+ // Instruction AX
+ if (spec) {
+ // 0x7F donne 0x16B;
+ // 7F
+ // << 7 = 3F80
+ // + E000 = 11F80
+ // & FFFF = 1F80
+ // * 19 = 31380
+ // / 2000 = 18 => Ligne 18h, colonne 0 => freq 16B
+
+ // 0x3A donne 0x2AF;
+ // 3A
+ // << 7 = 1D00
+ // + E000 = FD00 négatif
+ // * 19 = xB500
+ // / 2000 = -2 => Ligne 17h, colonne -1
+
+ // 2E
+ // << 7 = 1700
+ // + E000 = F700 négatif
+ // * 19 = x1F00
+ // / 2000 =
+ short a;
+ short lin;
+ short col;
+
+ a = (note << 7) + 0xE000; // Volontairement tronqué
+ a = (short)((long)a * 25 / 0x2000);
+ if (a < 0) {
+ col = - ((24 - a) / 25);
+ lin = (-a % 25);
+ if (lin)
+ lin = 25 - lin;
+ }
+ else {
+ col = a / 25;
+ lin = a % 25;
+ }
+
+ _notCol[voice] = col;
+ _notLin[voice] = lin;
+ note = _notes[voice];
+ }
+ // Instructions 0X 9X 8X
+ else {
+ note -= 12;
+ _notOn[voice] = on;
+ }
+
+ _notes[voice] = note;
+ note += _notCol[voice];
+ note = MIN(0x5F, (int)note);
+ octa = note / 12;
+ freq = _freqs[_notLin[voice]][note - octa * 12];
+
+ writeOPL(0xA0 + voice, freq & 0xff);
+ writeOPL(0xB0 + voice, (freq >> 8) | (octa << 2) | 0x20 * on);
+
+ if (!freq)
+ warning("Voice %d, note %02X unknown\n", voice, note);
+}
+
+void Music::setVolume(byte voice, byte volume) {
+ volume = 0x3F - (volume * 0x7E + 0x7F) / 0xFE;
+ writeOPL(0x40 + _volRegNums[voice], volume);
+}
+
+void Music::pollMusic(void) {
+ unsigned char instr;
+ byte channel;
+ byte note;
+ byte volume;
+ uint16 tempo;
+
+ if (_playPos > (_data + _dataSize)) {
+ _ended = true;
+ return;
+ }
+
+ // First tempo, we'll ignore it...
+ if (_first) {
+ tempo = *(_playPos++);
+ // Tempo on 2 bytes
+ if (tempo & 0x80)
+ tempo = ((tempo & 3) << 8) | *(_playPos++);
+ }
+ _first = false;
+
+ // Instruction
+ instr = *(_playPos++);
+ channel = instr & 0x0F;
+
+ switch (instr & 0xF0) {
+ // Note on + Volume
+ case 0x00:
+ note = *(_playPos++);
+ _pollNotes[channel] = note;
+ setVolume(channel, *(_playPos++));
+ setKey(channel, note, true, false);
+ break;
+ // Note on
+ case 0x90:
+ note = *(_playPos++);
+ _pollNotes[channel] = note;
+ setKey(channel, note, true, false);
+ break;
+ // Last note off
+ case 0x80:
+ note = _pollNotes[channel];
+ setKey(channel, note, false, false);
+ break;
+ // Frequency on/off
+ case 0xA0:
+ note = *(_playPos++);
+ setKey(channel, note, _notOn[channel], true);
+ break;
+ // Volume
+ case 0xB0:
+ volume = *(_playPos++);
+ setVolume(channel, volume);
+ break;
+ // Program change
+ case 0xC0:
+ setVoice(channel, *(_playPos++), false);
+ break;
+ // Special
+ case 0xF0:
+ break;
+ default:
+ warning("Unknown command in ADL, stopping playback");
+ _looping = false;
+ _ended = true;
+ break;
+ }
+ // End instruction
+ if (instr == 0xFF) {
+ _ended = true;
+ }
+
+ // Temporization
+ tempo = *(_playPos++);
+ // End tempo
+ if (tempo == 0xFF) {
+ _ended = true;
+ return;
+ }
+ // Tempo on 2 bytes
+ if (tempo & 0x80)
+ tempo = ((tempo & 3) << 8) | *(_playPos++);
+ if (!tempo)
+ tempo ++;
+
+ _samplesTillPoll = tempo * (_rate / 1000);
+}
+
+void Music::startPlay(void) {
+ if (!_data)
+ return;
+
+ _playing = true;
+}
+
+void Music::playBgMusic(void) {
+ debug(2, "Music::playBgMusic()");
+ for (int i = 0; i < ARRAYSIZE(_tracks); i++)
+ if (!scumm_stricmp(_vm->_game->_curTotFile, _tracks[i][0])) {
+ playTrack(_tracks[i][1]);
+ break;
+ }
+}
+
+void Music::playTrack(const char *trackname) {
+ if (_playing) return;
+
+ debug(2, "Music::playTrack(%s)", trackname);
+ unloadMusic();
+ for (int i = 0; i < ARRAYSIZE(_tracksToFiles); i++)
+ if (!scumm_stricmp(trackname, _tracksToFiles[i][0])) {
+ loadMusic(_tracksToFiles[i][1]);
+ startPlay();
+ break;
+ }
+}
+
+bool Music::loadMusic(const char *filename) {
+ Common::File song;
+
+ song.open(filename);
+ if (!song.isOpen())
+ return false;
+
+ _dataSize = song.size();
+ _data = new byte[_dataSize];
+ song.read(_data, _dataSize);
+ song.close();
+
+ reset();
+ setVoices();
+ _playPos = _data + 3 + (_data[1] + 1) * 0x38;
+
+ return true;
+}
+
+void Music::unloadMusic(void) {
+ _playing = false;
+
+ if (_data)
+ delete _data;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/music.h b/engines/gob/music.h
new file mode 100644
index 0000000000..e6421ea32c
--- /dev/null
+++ b/engines/gob/music.h
@@ -0,0 +1,96 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ * Original ADL-Player source Copyright (C) 2004 by Dorian Gray
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GOB_MUSIC_H
+#define GOB_MUSIC_H
+
+#include "sound/audiostream.h"
+#include "sound/fmopl.h"
+
+#include "gob/gob.h"
+
+namespace Gob {
+
+class GobEngine;
+
+class Music : public AudioStream {
+public:
+ Music(GobEngine *vm);
+ ~Music();
+
+ bool playing() { return _playing; }
+ bool getLooping() { return _looping; }
+ void setLooping(bool looping) { _looping = looping; }
+ void startPlay(void);
+ void stopPlay(void) { _playing = false; }
+ void playTrack(const char *trackname);
+ void playBgMusic(void);
+ bool loadMusic(const char *filename);
+ void unloadMusic(void);
+
+// AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples) {
+ premixerCall(buffer, numSamples / 2);
+ return numSamples;
+ }
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _rate; }
+
+protected:
+ static const char *_tracks[][2];
+ static const char *_tracksToFiles[][2];
+ static const unsigned char _operators[];
+ static const unsigned char _volRegNums [];
+ FM_OPL *_opl;
+ byte *_data;
+ byte *_playPos;
+ uint32 _dataSize;
+ uint32 _rate;
+ short _freqs[25][12];
+ byte _notes[11];
+ byte _notCol[11];
+ byte _notLin[11];
+ bool _notOn[11];
+ byte _pollNotes[16];
+ uint32 _samplesTillPoll;
+ bool _playing;
+ bool _first;
+ bool _ended;
+ bool _looping;
+ GobEngine *_vm;
+
+ void premixerCall(int16 *buf, uint len);
+ void writeOPL(byte reg, byte val);
+ void setFreqs(void);
+ void reset(void);
+ void setVoices();
+ void setVoice(byte voice, byte instr, bool set);
+ void setKey(byte voice, byte note, bool on, bool spec);
+ void setVolume(byte voice, byte volume);
+ void pollMusic(void);
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/pack.cpp b/engines/gob/pack.cpp
new file mode 100644
index 0000000000..c255a70ace
--- /dev/null
+++ b/engines/gob/pack.cpp
@@ -0,0 +1,108 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/pack.h"
+
+namespace Gob {
+
+int32 Pack::unpackData(char *sourceBuf, char *destBuf) {
+ uint32 realSize;
+ uint32 counter;
+ uint16 cmd;
+ byte *src;
+ byte *dest;
+ byte *tmpBuf;
+ int16 off;
+ byte len;
+ byte i;
+ int16 j;
+ uint16 tmpIndex;
+
+ realSize = READ_LE_UINT32(sourceBuf);
+ counter = READ_LE_UINT32(sourceBuf);
+
+ tmpBuf = new byte[4114];
+
+ /*
+ * Can use assembler unpacker for small blocks - for speed.
+ * Don't need this anymore :)
+ */
+ /*
+ * if (realSize < 65000)
+ * {
+ * asm_unpackData(sourceBuf, destBuf, tmpBuf);
+ * free(tmpBuf);
+ * return realSize;
+ * }
+ */
+
+ if (tmpBuf == 0)
+ return 0;
+
+ for (j = 0; j < 4078; j++)
+ tmpBuf[j] = 0x20;
+ tmpIndex = 4078;
+
+ src = (byte *)(sourceBuf + 4);
+ dest = (byte *)destBuf;
+
+ cmd = 0;
+ while (1) {
+ cmd >>= 1;
+ if ((cmd & 0x0100) == 0) {
+ cmd = *src | 0xff00;
+ src++;
+ }
+ if ((cmd & 1) != 0) { /* copy */
+ *dest++ = *src;
+ tmpBuf[tmpIndex] = *src;
+ src++;
+ tmpIndex++;
+ tmpIndex %= 4096;
+ counter--;
+ if (counter == 0)
+ break;
+ } else { /* copy string */
+
+ off = *src++;
+ off |= (*src & 0xf0) << 4;
+ len = (*src & 0x0f) + 3;
+ src++;
+ for (i = 0; i < len; i++) {
+ *dest++ = tmpBuf[(off + i) % 4096];
+ counter--;
+ if (counter == 0) {
+ delete[] tmpBuf;
+ return realSize;
+ }
+ tmpBuf[tmpIndex] = tmpBuf[(off + i) % 4096];
+ tmpIndex++;
+ tmpIndex %= 4096;
+ }
+ }
+ }
+ delete[] tmpBuf;
+ return realSize;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/pack.h b/engines/gob/pack.h
new file mode 100644
index 0000000000..9df0d97b24
--- /dev/null
+++ b/engines/gob/pack.h
@@ -0,0 +1,36 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_UNPACKER_H
+#define GOB_UNPACKER_H
+
+namespace Gob {
+
+class Pack {
+public:
+ int32 asm_unpackData(char *source, char *dest, char *temp);
+ int32 unpackData(char *source, char *dest);
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/palanim.cpp b/engines/gob/palanim.cpp
new file mode 100644
index 0000000000..7946639c4e
--- /dev/null
+++ b/engines/gob/palanim.cpp
@@ -0,0 +1,227 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/video.h"
+#include "gob/util.h"
+#include "gob/global.h"
+#include "gob/palanim.h"
+
+namespace Gob {
+
+PalAnim::PalAnim(GobEngine *vm) : _vm(vm) {
+ _fadeValue = 1;
+ for (int i = 0; i < 256; i++) {
+ _toFadeRed[i] = 0;
+ _toFadeGreen[i] = 0;
+ _toFadeBlue[i] = 0;
+ }
+}
+
+char PalAnim::fadeColor(char from, char to) {
+ if ((int16)from - _fadeValue > (int16)to)
+ return from - _fadeValue;
+ else if ((int16)from + _fadeValue < (int16)to)
+ return from + _fadeValue;
+ else
+ return to;
+}
+
+char PalAnim::fadeStep(int16 oper) {
+ byte newRed;
+ byte newGreen;
+ byte newBlue;
+ char stop;
+ int16 i;
+
+ if (_vm->_global->_colorCount != 256)
+ error("fadeStep: Only 256 color mode is supported!");
+
+ if (oper == 0) {
+ stop = 1;
+ if (_vm->_global->_setAllPalette) {
+ if (_vm->_global->_inVM != 0)
+ error("fade: _vm->_global->_inVM != 0 not supported.");
+
+ for (i = 0; i < 256; i++) {
+ newRed = fadeColor(_vm->_global->_redPalette[i], _toFadeRed[i]);
+ newGreen = fadeColor(_vm->_global->_greenPalette[i], _toFadeGreen[i]);
+ newBlue = fadeColor(_vm->_global->_bluePalette[i], _toFadeBlue[i]);
+
+ if (_vm->_global->_redPalette[i] != newRed
+ || _vm->_global->_greenPalette[i] != newGreen
+ || _vm->_global->_bluePalette[i] != newBlue) {
+
+ _vm->_video->setPalElem(i, newRed, newGreen, newBlue, 0, 0x13);
+
+ _vm->_global->_redPalette[i] = newRed;
+ _vm->_global->_greenPalette[i] = newGreen;
+ _vm->_global->_bluePalette[i] = newBlue;
+ stop = 0;
+ }
+ }
+ } else {
+ for (i = 0; i < 16; i++) {
+
+ _vm->_video->setPalElem(i,
+ fadeColor(_vm->_global->_redPalette[i],
+ _toFadeRed[i]),
+ fadeColor(_vm->_global->_greenPalette[i],
+ _toFadeGreen[i]),
+ fadeColor(_vm->_global->_bluePalette[i],
+ _toFadeBlue[i]), -1, _vm->_global->_videoMode);
+
+ if (_vm->_global->_redPalette[i] != _toFadeRed[i] ||
+ _vm->_global->_greenPalette[i] != _toFadeGreen[i] ||
+ _vm->_global->_bluePalette[i] != _toFadeBlue[i])
+ stop = 0;
+ }
+ }
+ return stop;
+ } else if (oper == 1) {
+ stop = 1;
+ for (i = 0; i < 16; i++) {
+ _vm->_video->setPalElem(i,
+ fadeColor(_vm->_global->_redPalette[i], _toFadeRed[i]),
+ _vm->_global->_greenPalette[i], _vm->_global->_bluePalette[i], -1, _vm->_global->_videoMode);
+
+ if (_vm->_global->_redPalette[i] != _toFadeRed[i])
+ stop = 0;
+ }
+ return stop;
+ } else if (oper == 2) {
+ stop = 1;
+ for (i = 0; i < 16; i++) {
+ _vm->_video->setPalElem(i,
+ _vm->_global->_redPalette[i],
+ fadeColor(_vm->_global->_greenPalette[i], _toFadeGreen[i]),
+ _vm->_global->_bluePalette[i], -1, _vm->_global->_videoMode);
+
+ if (_vm->_global->_greenPalette[i] != _toFadeGreen[i])
+ stop = 0;
+ }
+ return stop;
+ } else if (oper == 3) {
+ stop = 1;
+ for (i = 0; i < 16; i++) {
+ _vm->_video->setPalElem(i,
+ _vm->_global->_redPalette[i],
+ _vm->_global->_greenPalette[i],
+ fadeColor(_vm->_global->_bluePalette[i], _toFadeBlue[i]),
+ -1, _vm->_global->_videoMode);
+
+ if (_vm->_global->_bluePalette[i] != _toFadeBlue[i])
+ stop = 0;
+ }
+ return stop;
+ }
+ return 1;
+}
+
+void PalAnim::fade(Video::PalDesc *palDesc, int16 fadeV, int16 allColors) {
+ char stop;
+ int16 i;
+
+ if (fadeV < 0)
+ _fadeValue = -fadeV;
+ else
+ _fadeValue = 2;
+
+ if (_vm->_global->_colorCount < 256) {
+ if (palDesc != 0)
+ _vm->_video->setFullPalette(palDesc);
+ return;
+ }
+
+ if (_vm->_global->_setAllPalette == 0) {
+ if (palDesc == 0) {
+ for (i = 0; i < 16; i++) {
+ _toFadeRed[i] = 0;
+ _toFadeGreen[i] = 0;
+ _toFadeBlue[i] = 0;
+ }
+ } else {
+ for (i = 0; i < 16; i++) {
+ _toFadeRed[i] = palDesc->vgaPal[i].red;
+ _toFadeGreen[i] = palDesc->vgaPal[i].green;
+ _toFadeBlue[i] = palDesc->vgaPal[i].blue;
+ }
+ }
+ } else {
+ if (_vm->_global->_inVM != 0)
+ error("fade: _vm->_global->_inVM != 0 is not supported");
+
+ if (palDesc == 0) {
+ for (i = 0; i < 256; i++) {
+ _toFadeRed[i] = 0;
+ _toFadeGreen[i] = 0;
+ _toFadeBlue[i] = 0;
+ }
+ } else {
+ for (i = 0; i < 256; i++) {
+ _toFadeRed[i] = palDesc->vgaPal[i].red;
+ _toFadeGreen[i] = palDesc->vgaPal[i].green;
+ _toFadeBlue[i] = palDesc->vgaPal[i].blue;
+ }
+ }
+ }
+
+ if (allColors == 0) {
+ do {
+ _vm->_video->waitRetrace(_vm->_global->_videoMode);
+
+ stop = fadeStep(0);
+
+ if (fadeV > 0)
+ _vm->_util->delay(fadeV);
+ } while (stop == 0);
+
+ if (palDesc != 0)
+ _vm->_video->setFullPalette(palDesc);
+ else
+ _vm->_util->clearPalette();
+ }
+
+ if (allColors == 1) {
+ do {
+ _vm->_video->waitRetrace(_vm->_global->_videoMode);
+ stop = fadeStep(1);
+ } while (stop == 0);
+
+ do {
+ _vm->_video->waitRetrace(_vm->_global->_videoMode);
+ stop = fadeStep(2);
+ } while (stop == 0);
+
+ do {
+ _vm->_video->waitRetrace(_vm->_global->_videoMode);
+ stop = fadeStep(3);
+ } while (stop == 0);
+
+ if (palDesc != 0)
+ _vm->_video->setFullPalette(palDesc);
+ else
+ _vm->_util->clearPalette();
+ }
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/palanim.h b/engines/gob/palanim.h
new file mode 100644
index 0000000000..8bc661a052
--- /dev/null
+++ b/engines/gob/palanim.h
@@ -0,0 +1,47 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_PALANIM_H
+#define GOB_PALANIM_H
+
+namespace Gob {
+
+class PalAnim {
+public:
+ char fadeColor(char from, char to);
+ char fadeStep(int16 oper); // oper == 0 - fade all colors, 1, 2, 3 - red,green, blue
+ void fade(Video::PalDesc * palDesc, int16 fade, int16 all);
+
+ PalAnim(GobEngine *vm);
+
+protected:
+ int16 _fadeValue;
+
+ byte _toFadeRed[256];
+ byte _toFadeGreen[256];
+ byte _toFadeBlue[256];
+ GobEngine *_vm;
+};
+
+} // End of namespace Gob
+
+#endif /* __PALANIM_H */
diff --git a/engines/gob/parse.cpp b/engines/gob/parse.cpp
new file mode 100644
index 0000000000..45de528adb
--- /dev/null
+++ b/engines/gob/parse.cpp
@@ -0,0 +1,420 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/game.h"
+#include "gob/parse.h"
+#include "gob/util.h"
+#include "gob/inter.h"
+
+namespace Gob {
+
+Parse::Parse(GobEngine *vm) : _vm(vm) {
+}
+
+int32 Parse::encodePtr(char *ptr, int type) {
+ int32 offset;
+
+ switch (type) {
+ case kExecPtr:
+ offset = ptr - _vm->_game->_totFileData;
+ break;
+ case kInterVar:
+ offset = ptr - _vm->_global->_inter_variables;
+ break;
+ case kResStr:
+ offset = ptr - _vm->_global->_inter_resStr;
+ break;
+ default:
+ error("encodePtr: Unknown pointer type");
+ }
+ assert((offset & 0xF0000000) == 0);
+ return (type << 28) | offset;
+}
+
+char *Parse::decodePtr(int32 n) {
+ char *ptr;
+
+ switch (n >> 28) {
+ case kExecPtr:
+ ptr = _vm->_game->_totFileData;
+ break;
+ case kInterVar:
+ ptr = _vm->_global->_inter_variables;
+ break;
+ case kResStr:
+ ptr = _vm->_global->_inter_resStr;
+ break;
+ default:
+ error("decodePtr: Unknown pointer type");
+ }
+ return ptr + (n & 0x0FFFFFFF);
+}
+
+void Parse::skipExpr(char stopToken) {
+ int16 dimCount;
+ char operation;
+ int16 num;
+ int16 dim;
+
+ num = 0;
+ while (1) {
+ operation = *_vm->_global->_inter_execPtr++;
+
+ if (operation >= 19 && operation <= 29) {
+ switch (operation) {
+ case 20:
+ case 23:
+ _vm->_global->_inter_execPtr += 2;
+ break;
+
+ case 19:
+ _vm->_global->_inter_execPtr += 4;
+ break;
+
+ case 22:
+ _vm->_global->_inter_execPtr += strlen(_vm->_global->_inter_execPtr) + 1;
+ break;
+
+ case 25:
+ _vm->_global->_inter_execPtr += 2;
+ if (*_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ skipExpr(12);
+ }
+ break;
+
+ case 26:
+ case 28:
+ dimCount = _vm->_global->_inter_execPtr[2];
+ _vm->_global->_inter_execPtr += 3 + dimCount; // ???
+ for (dim = 0; dim < dimCount; dim++)
+ skipExpr(12);
+
+ if (operation == 28 && *_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ skipExpr(12);
+ }
+ break;
+
+ case 29:
+ _vm->_global->_inter_execPtr++;
+ skipExpr(10);
+ }
+ continue;
+ } // if (operation >= 19 && operation <= 29)
+
+ if (operation == 9) {
+ num++;
+ continue;
+ }
+
+ if (operation == 11 || (operation >= 1 && operation <= 8))
+ continue;
+
+ if (operation >= 30 && operation <= 37)
+ continue;
+
+ if (operation == 10)
+ num--;
+
+ if (operation != stopToken)
+ continue;
+
+ if (stopToken != 10 || num < 0)
+ return;
+ }
+}
+
+void Parse::printExpr(char stopToken) {
+ int16 dimCount;
+ char operation;
+ int16 num;
+ int16 dim;
+ char *arrDesc;
+ char func;
+ char saved = 0;
+ static char *savedPos = 0;
+
+ if (savedPos == 0) {
+ savedPos = _vm->_global->_inter_execPtr;
+ saved = 1;
+ }
+
+ num = 0;
+ while (1) {
+ operation = *_vm->_global->_inter_execPtr++;
+
+ if (operation >= 19 && operation <= 29) {
+ switch (operation) {
+ case 19:
+ debugN(5, "%l", READ_LE_UINT32(_vm->_global->_inter_execPtr));
+ _vm->_global->_inter_execPtr += 4;
+ break;
+
+ case 20:
+ debugN(5, "%d", _vm->_inter->load16());
+ break;
+
+ case 22:
+ debugN(5, "\42%s\42", _vm->_global->_inter_execPtr);
+ _vm->_global->_inter_execPtr += strlen(_vm->_global->_inter_execPtr) + 1;
+ break;
+
+ case 23:
+ {
+ int16 varnum = _vm->_inter->load16();
+ debugN(5, "var_%d (val=%d)", varnum, READ_LE_UINT32(_vm->_global->_inter_variables + varnum * 4) );
+ break;
+ }
+ case 25:
+ debugN(5, "(&var_%d)", _vm->_inter->load16());
+ if (*_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ debugN(5, "{");
+ printExpr(12);
+ debugN(5, "}");
+ }
+ break;
+
+ case 26:
+ case 28:
+ if (operation == 28)
+ debugN(5, "(&");
+
+ debugN(5, "var_%d[", _vm->_inter->load16());
+ dimCount = *_vm->_global->_inter_execPtr++;
+ arrDesc = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += dimCount;
+ for (dim = 0; dim < dimCount; dim++) {
+ printExpr(12);
+ debugN(5, " of %d", (int16)arrDesc[dim]);
+ if (dim != dimCount - 1)
+ debugN(5, ",");
+ }
+ debugN(5, "]");
+ if (operation == 28)
+ debugN(5, ")");
+
+ if (operation == 28 && *_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ debugN(5, "{");
+ printExpr(12);
+// debugN(5, "}");
+ }
+ break;
+
+ case 29:
+ func = *_vm->_global->_inter_execPtr++;
+ if (func == 5)
+ debugN(5, "sqr(");
+ else if (func == 10)
+ debugN(5, "rand(");
+ else if (func == 7)
+ debugN(5, "abs(");
+ else if (func == 0 || func == 1 || func == 6)
+ debugN(5, "sqrt(");
+ else
+ debugN(5, "id(");
+ printExpr(10);
+ break;
+
+ default:
+ debugN(5, "<%d>", (int16)operation);
+ break;
+ }
+ continue;
+ } // if (operation >= 19 && operation <= 29)
+ switch (operation) {
+ case 9:
+ debugN(5, "(");
+ break;
+
+ case 11:
+ debugN(5, "!");
+ break;
+
+ case 10:
+ debugN(5, ")");
+ break;
+
+ case 1:
+ debugN(5, "-");
+ break;
+
+ case 2:
+ debugN(5, "+");
+ break;
+
+ case 3:
+ debugN(5, "-");
+ break;
+
+ case 4:
+ debugN(5, "|");
+ break;
+
+ case 5:
+ debugN(5, "*");
+ break;
+
+ case 6:
+ debugN(5, "/");
+ break;
+
+ case 7:
+ debugN(5, "%");
+ break;
+
+ case 8:
+ debugN(5, "&");
+ break;
+
+ case 30:
+ debugN(5, "||");
+ break;
+
+ case 31:
+ debugN(5, "&&");
+ break;
+
+ case 32:
+ debugN(5, "<");
+ break;
+
+ case 33:
+ debugN(5, "<=");
+ break;
+
+ case 34:
+ debugN(5, ">");
+ break;
+
+ case 35:
+ debugN(5, ">=");
+ break;
+
+ case 36:
+ debugN(5, "==");
+ break;
+
+ case 37:
+ debugN(5, "!=");
+ break;
+
+ case 99:
+ debugN(5, "\n");
+ break;
+
+ case 12:
+ debugN(5, "}");
+ if (stopToken != 12) {
+ debugN(5, "Closing paren without opening?");
+ }
+ break;
+
+ default:
+ debugN(5, "<%d>", (int16)operation);
+ break;
+ }
+
+ if (operation == 9) {
+ num++;
+ continue;
+ }
+
+ if (operation == 11 || (operation >= 1 && operation <= 8))
+ continue;
+
+ if (operation >= 30 && operation <= 37)
+ continue;
+
+ if (operation == 10)
+ num--;
+
+ if (operation == stopToken) {
+ if (stopToken != 10 || num < 0) {
+
+ if (saved != 0) {
+ _vm->_global->_inter_execPtr = savedPos;
+ savedPos = 0;
+ }
+ return;
+ }
+ }
+ }
+}
+
+void Parse::printVarIndex() {
+ char *arrDesc;
+ int16 dim;
+ int16 dimCount;
+ int16 operation;
+ int16 temp;
+
+ char *pos = _vm->_global->_inter_execPtr;
+
+ operation = *_vm->_global->_inter_execPtr++;
+ switch (operation) {
+ case 23:
+ case 25:
+ temp = _vm->_inter->load16() * 4;
+ debugN(5, "&var_%d", temp);
+ if (operation == 25 && *_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ debugN(5, "+");
+ printExpr(12);
+ }
+ break;
+
+ case 26:
+ case 28:
+ debugN(5, "&var_%d[", _vm->_inter->load16());
+ dimCount = *_vm->_global->_inter_execPtr++;
+ arrDesc = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += dimCount;
+ for (dim = 0; dim < dimCount; dim++) {
+ printExpr(12);
+ debugN(5, " of %d", (int16)arrDesc[dim]);
+ if (dim != dimCount - 1)
+ debugN(5, ",");
+ }
+ debugN(5, "]");
+
+ if (operation == 28 && *_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ debugN(5, "+");
+ printExpr(12);
+ }
+ break;
+
+ default:
+ debugN(5, "var_0");
+ break;
+ }
+ debugN(5, "\n");
+ _vm->_global->_inter_execPtr = pos;
+ return;
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/parse.h b/engines/gob/parse.h
new file mode 100644
index 0000000000..e85fbebcfa
--- /dev/null
+++ b/engines/gob/parse.h
@@ -0,0 +1,75 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_PARSE_H
+#define GOB_PARSE_H
+
+namespace Gob {
+
+class Parse {
+public:
+ void skipExpr(char stopToken);
+ void printExpr(char stopToken);
+ void printVarIndex(void);
+ virtual int16 parseVarIndex(void) = 0;
+ virtual int16 parseValExpr(unsigned stopToken=99) = 0;
+ virtual int16 parseExpr(char stopToken, byte *resultPtr) = 0;
+
+ Parse(GobEngine *vm);
+ virtual ~Parse() {};
+
+protected:
+ enum PointerType {
+ kExecPtr = 0,
+ kInterVar = 1,
+ kResStr = 2
+ };
+
+ GobEngine *_vm;
+
+ int32 encodePtr(char *ptr, int type);
+ char *decodePtr(int32 n);
+};
+
+class Parse_v1 : public Parse {
+public:
+ Parse_v1(GobEngine *vm);
+ virtual ~Parse_v1() {};
+
+ virtual int16 parseVarIndex(void);
+ virtual int16 parseValExpr(unsigned stopToken=99);
+ virtual int16 parseExpr(char stopToken, byte *resultPtr);
+};
+
+class Parse_v2 : public Parse_v1 {
+public:
+ Parse_v2(GobEngine *vm);
+ virtual ~Parse_v2() {};
+
+ virtual int16 parseVarIndex(void);
+ virtual int16 parseValExpr(unsigned stopToken=99);
+ virtual int16 parseExpr(char stopToken, byte *resultPtr);
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/parse_v1.cpp b/engines/gob/parse_v1.cpp
new file mode 100644
index 0000000000..fc0dc91d5b
--- /dev/null
+++ b/engines/gob/parse_v1.cpp
@@ -0,0 +1,883 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "gob/gob.h"
+#include "gob/parse.h"
+#include "gob/inter.h"
+#include "gob/global.h"
+
+namespace Gob {
+
+Parse_v1::Parse_v1(GobEngine *vm) : Parse(vm) {
+}
+
+int16 Parse_v1::parseVarIndex() {
+ int16 temp2;
+ char *arrDesc;
+ int16 dim;
+ int16 dimCount;
+ int16 operation;
+ int16 temp;
+ int16 offset;
+ int16 val;
+
+ operation = *_vm->_global->_inter_execPtr++;
+ debug(5, "var parse = %d", operation);
+ switch (operation) {
+ case 23:
+ case 25:
+ temp = _vm->_inter->load16() * 4;
+ debug(5, "oper = %d", (int16)*_vm->_global->_inter_execPtr);
+ if (operation == 25 && *_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ val = parseValExpr(12);
+ temp += val;
+ debug(5, "parse subscript = %d", val);
+ }
+ return temp;
+
+ case 26:
+ case 28:
+ temp = _vm->_inter->load16() * 4;
+ dimCount = *_vm->_global->_inter_execPtr++;
+ arrDesc = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += dimCount;
+ offset = 0;
+ for (dim = 0; dim < dimCount; dim++) {
+ temp2 = parseValExpr(12);
+ offset = arrDesc[dim] * offset + temp2;
+ }
+ offset *= 4;
+ if (operation != 28)
+ return temp + offset;
+
+ if (*_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ temp += parseValExpr(12);
+ }
+ return offset * _vm->_global->_inter_animDataSize + temp;
+
+ default:
+ return 0;
+ }
+}
+
+int16 Parse_v1::parseValExpr(unsigned stopToken) {
+ int16 values[20];
+ byte operStack[20];
+ int16 *valPtr;
+ byte *operPtr;
+ byte *arrDesc;
+ unsigned operation;
+ int16 temp2;
+ int16 dim;
+ int16 dimCount;
+ int16 temp;
+ int16 offset;
+ int16 stkPos;
+ int16 brackPos;
+ static int16 flag = 0;
+ int16 oldflag;
+
+ oldflag = flag;
+ if (flag == 0) {
+ flag = 1;
+ printExpr(stopToken);
+ }
+
+ stkPos = -1;
+ operPtr = operStack - 1;
+ valPtr = values - 1;
+
+ while (1) {
+ stkPos++;
+ operPtr++;
+ valPtr++;
+
+ operation = *_vm->_global->_inter_execPtr++;
+ if (operation >= 19 && operation <= 29) {
+ *operPtr = 20;
+ switch (operation) {
+ case 19:
+ *valPtr = READ_LE_UINT32(_vm->_global->_inter_execPtr);
+ _vm->_global->_inter_execPtr += 4;
+ break;
+
+ case 20:
+ *valPtr = _vm->_inter->load16();
+ break;
+
+ case 23:
+ *valPtr = (uint16)VAR(_vm->_inter->load16());
+ break;
+
+ case 25:
+ temp = _vm->_inter->load16() * 4;
+ _vm->_global->_inter_execPtr++;
+ temp += parseValExpr(12);
+ *valPtr = (uint8)*(_vm->_global->_inter_variables + temp);
+ break;
+
+ case 26:
+ case 28:
+ temp = _vm->_inter->load16();
+ dimCount = *_vm->_global->_inter_execPtr++;
+ arrDesc = (byte*)_vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += dimCount;
+ offset = 0;
+ for (dim = 0; dim < dimCount; dim++) {
+ temp2 = parseValExpr(12);
+ offset = arrDesc[dim] * offset + temp2;
+ }
+ if (operation == 26) {
+ *valPtr = (uint16)VAR(temp + offset);
+ } else {
+ _vm->_global->_inter_execPtr++;
+ temp2 = parseValExpr(12);
+ *valPtr = (uint8)*(_vm->_global->_inter_variables + temp * 4 + offset * 4 * _vm->_global->_inter_animDataSize + temp2);
+ }
+ break;
+
+ case 29:
+ operation = *_vm->_global->_inter_execPtr++;
+ parseExpr(10, 0);
+
+ if (operation == 5) {
+ _vm->_global->_inter_resVal = _vm->_global->_inter_resVal * _vm->_global->_inter_resVal;
+ } else if (operation == 7) {
+ if (_vm->_global->_inter_resVal < 0)
+ _vm->_global->_inter_resVal = -_vm->_global->_inter_resVal;
+ } else if (operation == 10) {
+ _vm->_global->_inter_resVal = _vm->_util->getRandom(_vm->_global->_inter_resVal);
+ }
+ *valPtr = _vm->_global->_inter_resVal;
+ break;
+
+ } // switch
+ if (stkPos > 0 && operPtr[-1] == 1) {
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ operPtr[0] = 20;
+ valPtr[0] = -valPtr[1];
+ }
+
+ if (stkPos > 0 && operPtr[-1] > 4 && operPtr[-1] < 9) {
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+
+ switch (operPtr[1]) {
+ case 5:
+ valPtr[0] *= valPtr[2];
+ break;
+
+ case 6:
+ valPtr[0] /= valPtr[2];
+ break;
+
+ case 7:
+ valPtr[0] %= valPtr[2];
+ break;
+
+ case 8:
+ valPtr[0] &= valPtr[2];
+ break;
+ }
+ } // if (stkPos > 0 && cmdPtr[-1] > 4 && cmdPtr[-1] < 9)
+ continue;
+ }
+
+ if (operation >= 1 && operation <= 9) {
+ *operPtr = operation;
+ continue;
+ }
+
+ while (stkPos >= 2) {
+ if (operPtr[-2] == 9) {
+ stkPos--;
+ operPtr--;
+ valPtr--;
+
+ operPtr[-1] = operPtr[0];
+ valPtr[-1] = valPtr[0];
+ if (stkPos > 1 && operPtr[-2] == 1) {
+ valPtr[-2] = 20;
+ valPtr[-2] = -valPtr[-1];
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+
+ if (stkPos > 2 && operPtr[-2] > 4
+ && operPtr[-2] < 9) {
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ switch (operPtr[0]) {
+ case 5:
+ operPtr[-1] *= operPtr[1];
+ break;
+
+ case 6:
+ operPtr[-1] /= operPtr[1];
+ break;
+
+ case 7:
+ operPtr[-1] %= operPtr[1];
+ break;
+
+ case 8:
+ operPtr[-1] &= operPtr[1];
+ break;
+ }
+ }
+ if (operation == 10)
+ break;
+ } // operPtr[-2] == 9
+
+ for (brackPos = stkPos - 2; brackPos > 0 &&
+ operStack[brackPos] < 30
+ && operStack[brackPos] != 9; brackPos--);
+
+ if (operStack[brackPos] >= 30
+ || operStack[brackPos] == 9)
+ brackPos++;
+
+ if (operPtr[-2] < 2 || operPtr[-2] > 8)
+ break;
+
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ switch (operPtr[0]) {
+ case 2:
+ values[brackPos] += valPtr[1];
+ continue;
+ case 3:
+ values[brackPos] -= valPtr[1];
+ continue;
+ case 4:
+ values[brackPos] |= valPtr[1];
+ continue;
+ case 5:
+ valPtr[-1] *= valPtr[1];
+ continue;
+ case 6:
+ valPtr[-1] /= valPtr[1];
+ continue;
+ case 7:
+ valPtr[-1] %= valPtr[1];
+ continue;
+ case 8:
+ valPtr[-1] &= valPtr[1];
+ continue;
+ }
+ }
+
+ if (operation != 10) {
+ if (operation != stopToken) {
+ debug(5, "stoptoken error: %d != %d", operation, stopToken);
+ }
+ flag = oldflag;
+ return values[0];
+ }
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+}
+
+int16 Parse_v1::parseExpr(char stopToken, byte *arg_2) {
+ int32 values[20];
+ byte operStack[20];
+ int32 prevPrevVal;
+ int32 prevVal;
+ int32 curVal;
+ int32 *valPtr;
+ char *operPtr;
+ byte *arrDescPtr;
+ char var_C;
+ byte operation;
+ int16 dimCount;
+ int16 temp;
+ int16 temp2;
+ uint16 offset;
+ int16 dim;
+ char var_1A;
+ int16 stkPos;
+ int16 brackStart;
+
+ stkPos = -1;
+ operPtr = (char *)(operStack - 1);
+ valPtr = values - 1;
+
+ while (1) {
+ stkPos++;
+ operPtr++;
+ valPtr++;
+ operation = *_vm->_global->_inter_execPtr++;
+ if (operation >= 19 && operation <= 29) {
+ switch (operation) {
+ case 19:
+ *operPtr = 20;
+ *valPtr = READ_LE_UINT32(_vm->_global->_inter_execPtr);
+ _vm->_global->_inter_execPtr += 4;
+ break;
+
+ case 20:
+ *operPtr = 20;
+ *valPtr = _vm->_inter->load16();
+ break;
+
+ case 22:
+ *operPtr = 22;
+ *valPtr = encodePtr(_vm->_global->_inter_execPtr, kExecPtr);
+ _vm->_global->_inter_execPtr += strlen(_vm->_global->_inter_execPtr) + 1;
+ break;
+
+ case 23:
+ *operPtr = 20;
+ *valPtr = VAR(_vm->_inter->load16());
+ break;
+
+ case 25:
+ *operPtr = 22;
+ temp = _vm->_inter->load16() * 4;
+ *valPtr = encodePtr(_vm->_global->_inter_variables + temp, kInterVar);
+ if (*_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ temp += parseValExpr(12);
+ *operPtr = 20;
+ *valPtr = (uint8)*(_vm->_global->_inter_variables + temp);
+ }
+ break;
+
+ case 26:
+ case 28:
+ *operPtr = operation - 6;
+ temp = _vm->_inter->load16();
+ dimCount = *_vm->_global->_inter_execPtr++;
+ arrDescPtr = (byte *)_vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += dimCount;
+ offset = 0;
+ dim = 0;
+ for (dim = 0; dim < dimCount; dim++) {
+ temp2 = parseValExpr(12);
+ offset = offset * arrDescPtr[dim] + temp2;
+ }
+
+ if (operation == 26) {
+ *valPtr = VAR(temp + offset);
+ break;
+ }
+ *valPtr = encodePtr(_vm->_global->_inter_variables + temp * 4 + offset * _vm->_global->_inter_animDataSize * 4, kInterVar);
+ if (*_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ temp2 = parseValExpr(12);
+ *operPtr = 20;
+ *valPtr = (uint8)*(_vm->_global->_inter_variables + temp * 4 + offset * 4 * _vm->_global->_inter_animDataSize + temp2);
+ }
+ break;
+
+ case 29:
+ operation = *_vm->_global->_inter_execPtr++;
+ parseExpr(10, 0);
+
+ switch (operation) {
+ case 5:
+ _vm->_global->_inter_resVal = _vm->_global->_inter_resVal * _vm->_global->_inter_resVal;
+ break;
+
+ case 0:
+ case 1:
+ case 6:
+ curVal = 1;
+ prevVal = 1;
+
+ do {
+ prevPrevVal = prevVal;
+ prevVal = curVal;
+ curVal = (curVal + _vm->_global->_inter_resVal / curVal) / 2;
+ } while (curVal != prevVal && curVal != prevPrevVal);
+ _vm->_global->_inter_resVal = curVal;
+ break;
+
+ case 10:
+ _vm->_global->_inter_resVal = _vm->_util->getRandom(_vm->_global->_inter_resVal);
+ break;
+
+ case 7:
+ if (_vm->_global->_inter_resVal < 0)
+ _vm->_global->_inter_resVal = -_vm->_global->_inter_resVal;
+ break;
+ }
+ *operPtr = 20;
+ *valPtr = _vm->_global->_inter_resVal;
+ }
+
+ if (stkPos > 0 && (operPtr[-1] == 1 || operPtr[-1] == 11)) {
+ stkPos--;
+ operPtr--;
+ valPtr--;
+
+ if (*operPtr == 1) {
+ *operPtr = 20;
+ valPtr[0] = -valPtr[1];
+ } else if (*operPtr == 11) {
+ if (operPtr[1] == 23)
+ *operPtr = 24;
+ else
+ *operPtr = 23;
+ }
+ }
+
+ if (stkPos <= 0)
+ continue;
+
+ switch (operPtr[-1]) {
+ case 2:
+ if (operPtr[-2] == 22) {
+ if (decodePtr(valPtr[-2]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-2]));
+ valPtr[-2] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ strcat(_vm->_global->_inter_resStr, decodePtr(valPtr[0]));
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ }
+ break;
+
+ case 5:
+ valPtr[-2] *= valPtr[0];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 6:
+ valPtr[-2] /= valPtr[0];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 7:
+ valPtr[-2] %= valPtr[0];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 8:
+ valPtr[-2] &= valPtr[0];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+ }
+ continue;
+ } // op>= 19 && op <= 29
+
+ if (operation == stopToken || operation == 30 || operation == 31 || operation == 10) {
+ while (stkPos >= 2) {
+ var_1A = 0;
+ if (operPtr[-2] == 9 && (operation == 10 || operation == stopToken)) {
+ operPtr[-2] = operPtr[-1];
+ if (operPtr[-2] == 20 || operPtr[-2] == 22)
+ valPtr[-2] = valPtr[-1];
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+
+ if (stkPos > 1) {
+ if (operPtr[-2] == 1) {
+ operPtr[-2] = 20;
+ valPtr[-2] = -valPtr[-1];
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ } else if (operPtr[-2] == 11) {
+ if (operPtr[-1] == 23)
+ operPtr[-2] = 24;
+ else
+ operPtr[-2] = 23;
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+ } // stkPos > 1
+
+ if (stkPos > 2) {
+ switch (operPtr[-2]) {
+ case 5:
+ valPtr[-3] *= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+ case 6:
+ valPtr[-3] /= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 7:
+ valPtr[-3] %= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 8:
+ valPtr[-3] &= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+ } // switch
+ } // stkPos > 2
+
+ if (operation != stopToken)
+ break;
+ } // if (operPtr[-2] == 9 && ...)
+
+ for (brackStart = stkPos - 2; brackStart > 0 &&
+ operStack[brackStart] < 30 &&
+ operStack[brackStart] != 9; brackStart--);
+
+ if (operStack[brackStart] >= 30 || operStack[brackStart] == 9)
+ brackStart++;
+
+ switch (operPtr[-2]) {
+ case 2:
+ if (operStack[brackStart] == 20) {
+ values[brackStart] += valPtr[-1];
+ } else if (operStack[brackStart] == 22) {
+ if (decodePtr(values[brackStart]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(values[brackStart]));
+ values[brackStart] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ strcat(_vm->_global->_inter_resStr, decodePtr(valPtr[-1]));
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ continue;
+
+ case 3:
+ values[brackStart] -= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ continue;
+
+ case 4:
+ values[brackStart] |= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ continue;
+
+ case 5:
+ valPtr[-3] *= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 6:
+ valPtr[-3] /= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 7:
+ valPtr[-3] %= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 8:
+ valPtr[-3] &= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 30:
+ if (operPtr[-3] == 23)
+ operPtr[-3] = operPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 31:
+ if (operPtr[-3] == 24)
+ operPtr[-3] = operPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 32:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] < valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) < 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 33:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] <= valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) <= 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 34:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] > valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) > 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 35:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] >= valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) >= 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 36:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] == valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) == 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 37:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] != valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) != 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ default:
+ var_1A = 1;
+ break;
+ } // switch
+
+ if (var_1A != 0)
+ break;
+ } // while (stkPos >= 2)
+
+ if (operation == 30 || operation == 31) {
+ if (operPtr[-1] == 20) {
+ if (valPtr[-1] != 0)
+ operPtr[-1] = 24;
+ else
+ operPtr[-1] = 23;
+ }
+
+ if ((operation == 30 && operPtr[-1] == 24) ||
+ (operation == 31 && operPtr[-1] == 23)) {
+ if (stkPos > 1 && operPtr[-2] == 9) {
+ skipExpr(10);
+ operPtr[-2] = operPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ } else {
+ skipExpr(stopToken);
+ }
+ operation = _vm->_global->_inter_execPtr[-1];
+ if (stkPos > 0 && operPtr[-1] == 11) {
+ if (operPtr[0] == 23)
+ operPtr[-1] = 24;
+ else
+ operPtr[-1] = 23;
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+ } else
+ operPtr[0] = operation;
+ } else {
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+
+ if (operation != stopToken)
+ continue;
+
+ if (arg_2 != 0)
+ *arg_2 = operStack[0];
+
+ switch (operStack[0]) {
+ case 20:
+ _vm->_global->_inter_resVal = values[0];
+ break;
+
+ case 22:
+ if (decodePtr(values[0]) != _vm->_global->_inter_resStr)
+ strcpy(_vm->_global->_inter_resStr, decodePtr(values[0]));
+ break;
+
+ case 11:
+ if (arg_2 != 0)
+ *arg_2 ^= 1;
+ break;
+
+ case 23:
+ case 24:
+ break;
+
+ default:
+ _vm->_global->_inter_resVal = 0;
+ if (arg_2 != 0)
+ *arg_2 = 20;
+ break;
+ }
+ return 0;
+ } // operation == stopToken || operation == 30 || operation == 31 || operation == 10
+
+ if (operation < 1 || operation > 11) {
+ if (operation < 32 || operation > 37)
+ continue;
+
+ if (stkPos > 2) {
+ if (operPtr[-2] == 2) {
+ if (operPtr[-3] == 20) {
+ valPtr[-3] += valPtr[-1];
+ } else if (operPtr[-3] == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ strcat(_vm->_global->_inter_resStr, decodePtr(valPtr[-1]));
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+
+ } else if (operPtr[-2] == 3) {
+ valPtr[-3] -= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ } else if (operPtr[-2] == 4) {
+ valPtr[-3] |= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ }
+ }
+ }
+ *operPtr = operation;
+ }
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/parse_v2.cpp b/engines/gob/parse_v2.cpp
new file mode 100644
index 0000000000..d4dfdc863f
--- /dev/null
+++ b/engines/gob/parse_v2.cpp
@@ -0,0 +1,942 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "gob/gob.h"
+#include "gob/parse.h"
+#include "gob/inter.h"
+#include "gob/global.h"
+
+namespace Gob {
+
+Parse_v2::Parse_v2(GobEngine *vm) : Parse_v1(vm) {
+}
+
+int16 Parse_v2::parseVarIndex() {
+ int16 temp2;
+ char *arrDesc;
+ int16 dim;
+ int16 dimCount;
+ int16 operation;
+ int16 temp;
+ int16 offset;
+ int16 val;
+
+ operation = *_vm->_global->_inter_execPtr++;
+ debug(5, "var parse = %d", operation);
+ switch (operation) {
+ case 16:
+ case 26:
+ case 27:
+ case 28:
+ temp = _vm->_inter->load16();
+ dimCount = *_vm->_global->_inter_execPtr++;
+ arrDesc = _vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += dimCount;
+ offset = 0;
+ for (dim = 0; dim < dimCount; dim++) {
+ temp2 = parseValExpr(12);
+ offset = arrDesc[dim] * offset + temp2;
+ }
+ if (operation == 16)
+ return temp + offset;
+ if (operation == 26)
+ return (temp + offset) * 4;
+ if (operation == 27)
+ return (temp + offset) * 2;
+ temp *= 4;
+ offset *= 4;
+ if (*_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ temp += parseValExpr(12);
+ }
+ return offset * _vm->_global->_inter_animDataSize + temp;
+
+ case 17:
+ return _vm->_inter->load16() * 2;
+
+ case 18:
+ return _vm->_inter->load16();
+
+ case 23:
+ case 24:
+ case 25:
+ temp = _vm->_inter->load16() * 4;
+ debug(5, "oper = %d", (int16)*_vm->_global->_inter_execPtr);
+ if (operation == 25 && *_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ val = parseValExpr(12);
+ temp += val;
+ debug(5, "parse subscript = %d", val);
+ }
+ return temp;
+
+ default:
+ return 0;
+ }
+}
+
+int16 Parse_v2::parseValExpr(unsigned stopToken) {
+ int16 values[20];
+ byte operStack[20];
+ int16 *valPtr;
+ byte *operPtr;
+ byte *arrDesc;
+ unsigned operation;
+ int16 temp2;
+ int16 dim;
+ int16 dimCount;
+ int16 temp;
+ int16 offset;
+ int16 stkPos;
+ int16 brackPos;
+ static int16 flag = 0;
+ int16 oldflag;
+
+ oldflag = flag;
+ if (flag == 0) {
+ flag = 1;
+ printExpr(stopToken);
+ }
+
+ stkPos = -1;
+ operPtr = operStack - 1;
+ valPtr = values - 1;
+
+ while (1) {
+ stkPos++;
+ operPtr++;
+ valPtr++;
+
+ operation = *_vm->_global->_inter_execPtr++;
+ if (operation >= 16 && operation <= 29) {
+ *operPtr = 20;
+ switch (operation) {
+ case 16:
+ case 26:
+ case 27:
+ case 28:
+ temp = _vm->_inter->load16();
+ dimCount = *_vm->_global->_inter_execPtr++;
+ arrDesc = (byte*)_vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += dimCount;
+ offset = 0;
+ for (dim = 0; dim < dimCount; dim++) {
+ temp2 = parseValExpr(12);
+ offset = arrDesc[dim] * offset + temp2;
+ }
+ if (operation == 16)
+ *valPtr = *(_vm->_global->_inter_variables + temp + offset);
+ else if (operation == 26)
+ *valPtr = *(uint16*)(_vm->_global->_inter_variables + temp * 4 + offset * 4);
+ else if (operation == 27)
+ *valPtr = *(uint16*)(_vm->_global->_inter_variables + temp * 2 + offset * 2);
+ else if (operation == 28) {
+ _vm->_global->_inter_execPtr++;
+ temp2 = parseValExpr(12);
+ *valPtr = (uint8)*(_vm->_global->_inter_variables + temp * 4 + offset * 4 * _vm->_global->_inter_animDataSize + temp2);
+ }
+ break;
+
+ case 17:
+ *valPtr = *(uint16*)(_vm->_global->_inter_variables + _vm->_inter->load16() * 2);
+ break;
+
+ case 18:
+ *valPtr = *(_vm->_global->_inter_variables + _vm->_inter->load16());
+ break;
+
+ case 19:
+ *valPtr = _vm->_inter->load16();
+ _vm->_global->_inter_execPtr += 2;
+ break;
+
+ case 20:
+ *valPtr = _vm->_inter->load16();
+ break;
+
+ case 21:
+ *valPtr = *_vm->_global->_inter_execPtr++;
+ break;
+
+ case 23:
+ case 24:
+ *valPtr = *(uint16*)(_vm->_global->_inter_variables + _vm->_inter->load16() * 4);
+ break;
+
+ case 25:
+ temp = _vm->_inter->load16() * 4;
+ _vm->_global->_inter_execPtr++;
+ temp += parseValExpr(12);
+ *valPtr = *(_vm->_global->_inter_variables + temp);
+ break;
+
+ case 29:
+ operation = *_vm->_global->_inter_execPtr++;
+ parseExpr(10, 0);
+
+ if (operation == 5) {
+ _vm->_global->_inter_resVal = _vm->_global->_inter_resVal * _vm->_global->_inter_resVal;
+ } else if (operation == 7) {
+ if (_vm->_global->_inter_resVal < 0)
+ _vm->_global->_inter_resVal = -_vm->_global->_inter_resVal;
+ } else if (operation == 10) {
+ _vm->_global->_inter_resVal = _vm->_util->getRandom(_vm->_global->_inter_resVal);
+ }
+ *valPtr = _vm->_global->_inter_resVal;
+ break;
+
+ } // switch
+ if (stkPos > 0 && operPtr[-1] == 1) {
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ operPtr[0] = 20;
+ valPtr[0] = -valPtr[1];
+ }
+
+ if (stkPos > 0 && operPtr[-1] > 4 && operPtr[-1] < 9) {
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+
+ switch (operPtr[1]) {
+ case 5:
+ valPtr[0] *= valPtr[2];
+ break;
+
+ case 6:
+ valPtr[0] /= valPtr[2];
+ break;
+
+ case 7:
+ valPtr[0] %= valPtr[2];
+ break;
+
+ case 8:
+ valPtr[0] &= valPtr[2];
+ break;
+ }
+ } // if (stkPos > 0 && cmdPtr[-1] > 4 && cmdPtr[-1] < 9)
+ continue;
+ }
+
+ if (operation >= 1 && operation <= 9) {
+ *operPtr = operation;
+ continue;
+ }
+
+ while (stkPos >= 2) {
+ if (operPtr[-2] == 9) {
+ stkPos--;
+ operPtr--;
+ valPtr--;
+
+ operPtr[-1] = operPtr[0];
+ valPtr[-1] = valPtr[0];
+ if (stkPos > 1 && operPtr[-2] == 1) {
+ valPtr[-2] = 20;
+ valPtr[-2] = -valPtr[-1];
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+
+ if (stkPos > 2 && operPtr[-2] > 4
+ && operPtr[-2] < 9) {
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ switch (operPtr[0]) {
+ case 5:
+ operPtr[-1] *= operPtr[1];
+ break;
+
+ case 6:
+ operPtr[-1] /= operPtr[1];
+ break;
+
+ case 7:
+ operPtr[-1] %= operPtr[1];
+ break;
+
+ case 8:
+ operPtr[-1] &= operPtr[1];
+ break;
+ }
+ }
+ if (operation == 10)
+ break;
+ } // operPtr[-2] == 9
+
+ for (brackPos = stkPos - 2; brackPos > 0 &&
+ operStack[brackPos] < 30
+ && operStack[brackPos] != 9; brackPos--);
+
+ if (operStack[brackPos] >= 30
+ || operStack[brackPos] == 9)
+ brackPos++;
+
+ if (operPtr[-2] < 2 || operPtr[-2] > 8)
+ break;
+
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ switch (operPtr[0]) {
+ case 2:
+ values[brackPos] += valPtr[1];
+ continue;
+ case 3:
+ values[brackPos] -= valPtr[1];
+ continue;
+ case 4:
+ values[brackPos] |= valPtr[1];
+ continue;
+ case 5:
+ valPtr[-1] *= valPtr[1];
+ continue;
+ case 6:
+ valPtr[-1] /= valPtr[1];
+ continue;
+ case 7:
+ valPtr[-1] %= valPtr[1];
+ continue;
+ case 8:
+ valPtr[-1] &= valPtr[1];
+ continue;
+ }
+ }
+
+ if (operation != 10) {
+ if (operation != stopToken) {
+ debug(5, "stoptoken error: %d != %d", operation, stopToken);
+ }
+ flag = oldflag;
+ return values[0];
+ }
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+}
+
+int16 Parse_v2::parseExpr(char stopToken, byte *arg_2) {
+ int32 values[20];
+ byte operStack[20];
+ int32 prevPrevVal;
+ int32 prevVal;
+ int32 curVal;
+ int32 *valPtr;
+ char *operPtr;
+ byte *arrDescPtr;
+ char var_C;
+ byte operation;
+ int16 dimCount;
+ int16 temp;
+ int16 temp2;
+ uint16 offset;
+ int16 dim;
+ char var_1A;
+ int16 stkPos;
+ int16 brackStart;
+
+ stkPos = -1;
+ operPtr = (char *)(operStack - 1);
+ valPtr = values - 1;
+
+ while (1) {
+ stkPos++;
+ operPtr++;
+ valPtr++;
+ operation = *_vm->_global->_inter_execPtr++;
+ if (operation >= 19 && operation <= 29) {
+ switch (operation) {
+ case 16:
+ case 26:
+ case 27:
+ case 28:
+ if ((operation == 27) || (operation == 16))
+ *operPtr = 20;
+ else
+ *operPtr = operation - 6;
+ temp = _vm->_inter->load16();
+ dimCount = *_vm->_global->_inter_execPtr++;
+ arrDescPtr = (byte *)_vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += dimCount;
+ offset = 0;
+ for (dim = 0; dim < dimCount; dim++) {
+ temp2 = parseValExpr(12);
+ offset = offset * arrDescPtr[dim] + temp2;
+ }
+ if (operation == 16)
+ *valPtr = *(_vm->_global->_inter_variables + temp + offset);
+ else if (operation == 26)
+ *valPtr = *(uint32*)(_vm->_global->_inter_variables + temp * 4 + offset * 4);
+ else if (operation == 27)
+ *valPtr = *(uint16*)(_vm->_global->_inter_variables + temp * 2 + offset * 2);
+ else if (operation == 28) {
+ *valPtr = encodePtr(_vm->_global->_inter_variables + temp * 4 + offset * _vm->_global->_inter_animDataSize * 4, kInterVar);
+ if (*_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ temp2 = parseValExpr(12);
+ *operPtr = 20;
+ *valPtr = (uint8)*(_vm->_global->_inter_variables + temp * 4 + offset * 4 * _vm->_global->_inter_animDataSize + temp2);
+ }
+ }
+ break;
+
+ case 17:
+ *operPtr = 20;
+ *valPtr = *(uint16*)(_vm->_global->_inter_variables + _vm->_inter->load16() * 2);
+ break;
+
+ case 18:
+ *operPtr = 20;
+ *valPtr = *(_vm->_global->_inter_variables + _vm->_inter->load16());
+ break;
+
+ case 19:
+ *operPtr = 20;
+ *valPtr = READ_LE_UINT32(_vm->_global->_inter_execPtr);
+ _vm->_global->_inter_execPtr += 4;
+ break;
+
+ case 20:
+ *operPtr = 20;
+ *valPtr = _vm->_inter->load16();
+ break;
+
+ case 21:
+ *operPtr = 20;
+ *valPtr = *_vm->_global->_inter_execPtr++;
+ break;
+
+ case 22:
+ *operPtr = 22;
+ *valPtr = encodePtr(_vm->_global->_inter_execPtr, kExecPtr);
+ _vm->_global->_inter_execPtr += strlen(_vm->_global->_inter_execPtr) + 1;
+ break;
+
+ case 23:
+ *operPtr = 20;
+ *valPtr = VAR(_vm->_inter->load16());
+ break;
+
+ case 24:
+ *operPtr = 20;
+ *valPtr = *(uint16*)(_vm->_global->_inter_variables + _vm->_inter->load16() * 4);
+ break;
+
+ case 25:
+ *operPtr = 22;
+ temp = _vm->_inter->load16() * 4;
+ *valPtr = encodePtr(_vm->_global->_inter_variables + temp, kInterVar);
+ if (*_vm->_global->_inter_execPtr == 13) {
+ _vm->_global->_inter_execPtr++;
+ temp += parseValExpr(12);
+ *operPtr = 20;
+ *valPtr = (uint8)*(_vm->_global->_inter_variables + temp);
+ }
+ break;
+
+ case 29:
+ operation = *_vm->_global->_inter_execPtr++;
+ parseExpr(10, 0);
+
+ switch (operation) {
+ case 5:
+ _vm->_global->_inter_resVal = _vm->_global->_inter_resVal * _vm->_global->_inter_resVal;
+ break;
+
+ case 0:
+ case 1:
+ case 6:
+ curVal = 1;
+ prevVal = 1;
+
+ do {
+ prevPrevVal = prevVal;
+ prevVal = curVal;
+ curVal = (curVal + _vm->_global->_inter_resVal / curVal) / 2;
+ } while (curVal != prevVal && curVal != prevPrevVal);
+ _vm->_global->_inter_resVal = curVal;
+ break;
+
+ case 10:
+ _vm->_global->_inter_resVal = _vm->_util->getRandom(_vm->_global->_inter_resVal);
+ break;
+
+ case 7:
+ if (_vm->_global->_inter_resVal < 0)
+ _vm->_global->_inter_resVal = -_vm->_global->_inter_resVal;
+ break;
+ }
+ *operPtr = 20;
+ *valPtr = _vm->_global->_inter_resVal;
+ break;
+ }
+ if (stkPos > 0 && (operPtr[-1] == 1 || operPtr[-1] == 11)) {
+ stkPos--;
+ operPtr--;
+ valPtr--;
+
+ if (*operPtr == 1) {
+ *operPtr = 20;
+ valPtr[0] = -valPtr[1];
+ } else if (*operPtr == 11) {
+ if (operPtr[1] == 23)
+ *operPtr = 24;
+ else
+ *operPtr = 23;
+ }
+ }
+
+ if (stkPos <= 0)
+ continue;
+
+ switch (operPtr[-1]) {
+ case 2:
+ if (operPtr[-2] == 22) {
+ if (decodePtr(valPtr[-2]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-2]));
+ valPtr[-2] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ strcat(_vm->_global->_inter_resStr, decodePtr(valPtr[0]));
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ }
+ break;
+
+ case 5:
+ valPtr[-2] *= valPtr[0];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 6:
+ valPtr[-2] /= valPtr[0];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 7:
+ valPtr[-2] %= valPtr[0];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 8:
+ valPtr[-2] &= valPtr[0];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+ }
+ continue;
+ } // op>= 19 && op <= 29
+
+ if (operation == stopToken || operation == 30 || operation == 31 || operation == 10) {
+ while (stkPos >= 2) {
+ var_1A = 0;
+ if (operPtr[-2] == 9 && (operation == 10 || operation == stopToken)) {
+ operPtr[-2] = operPtr[-1];
+ if (operPtr[-2] == 20 || operPtr[-2] == 22)
+ valPtr[-2] = valPtr[-1];
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+
+ if (stkPos > 1) {
+ if (operPtr[-2] == 1) {
+ operPtr[-2] = 20;
+ valPtr[-2] = -valPtr[-1];
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ } else if (operPtr[-2] == 11) {
+ if (operPtr[-1] == 23)
+ operPtr[-2] = 24;
+ else
+ operPtr[-2] = 23;
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+ } // stkPos > 1
+
+ if (stkPos > 2) {
+ switch (operPtr[-2]) {
+ case 5:
+ valPtr[-3] *= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+ case 6:
+ valPtr[-3] /= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 7:
+ valPtr[-3] %= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 8:
+ valPtr[-3] &= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+ } // switch
+ } // stkPos > 2
+
+ if (operation != stopToken)
+ break;
+ } // if (operPtr[-2] == 9 && ...)
+
+ for (brackStart = stkPos - 2; brackStart > 0 &&
+ operStack[brackStart] < 30 &&
+ operStack[brackStart] != 9; brackStart--);
+
+ if (operStack[brackStart] >= 30 || operStack[brackStart] == 9)
+ brackStart++;
+
+ switch (operPtr[-2]) {
+ case 2:
+ if (operStack[brackStart] == 20) {
+ values[brackStart] += valPtr[-1];
+ } else if (operStack[brackStart] == 22) {
+ if (decodePtr(values[brackStart]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(values[brackStart]));
+ values[brackStart] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ strcat(_vm->_global->_inter_resStr, decodePtr(valPtr[-1]));
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ continue;
+
+ case 3:
+ values[brackStart] -= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ continue;
+
+ case 4:
+ values[brackStart] |= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ continue;
+
+ case 5:
+ valPtr[-3] *= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 6:
+ valPtr[-3] /= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 7:
+ valPtr[-3] %= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 8:
+ valPtr[-3] &= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 30:
+ if (operPtr[-3] == 23)
+ operPtr[-3] = operPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 31:
+ if (operPtr[-3] == 24)
+ operPtr[-3] = operPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 32:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] < valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) < 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 33:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] <= valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) <= 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 34:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] > valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) > 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 35:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] >= valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) >= 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 36:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] == valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) == 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ case 37:
+ var_C = operPtr[-3];
+ operPtr[-3] = 23;
+ if (var_C == 20) {
+ if (valPtr[-3] != valPtr[-1])
+ operPtr[-3] = 24;
+ } else if (var_C == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ if (strcmp(_vm->_global->_inter_resStr, decodePtr(valPtr[-1])) != 0)
+ operPtr[-3] = 24;
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ break;
+
+ default:
+ var_1A = 1;
+ break;
+ } // switch
+
+ if (var_1A != 0)
+ break;
+ } // while (stkPos >= 2)
+
+ if (operation == 30 || operation == 31) {
+ if (operPtr[-1] == 20) {
+ if (valPtr[-1] != 0)
+ operPtr[-1] = 24;
+ else
+ operPtr[-1] = 23;
+ }
+
+ if ((operation == 30 && operPtr[-1] == 24) ||
+ (operation == 31 && operPtr[-1] == 23)) {
+ if (stkPos > 1 && operPtr[-2] == 9) {
+ skipExpr(10);
+ operPtr[-2] = operPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ } else {
+ skipExpr(stopToken);
+ }
+ operation = _vm->_global->_inter_execPtr[-1];
+ if (stkPos > 0 && operPtr[-1] == 11) {
+ if (operPtr[0] == 23)
+ operPtr[-1] = 24;
+ else
+ operPtr[-1] = 23;
+
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+ } else
+ operPtr[0] = operation;
+ } else {
+ stkPos--;
+ operPtr--;
+ valPtr--;
+ }
+
+ if (operation != stopToken)
+ continue;
+
+ if (arg_2 != 0)
+ *arg_2 = operStack[0];
+
+ switch (operStack[0]) {
+ case 20:
+ _vm->_global->_inter_resVal = values[0];
+ break;
+
+ case 22:
+ if (decodePtr(values[0]) != _vm->_global->_inter_resStr)
+ strcpy(_vm->_global->_inter_resStr, decodePtr(values[0]));
+ break;
+
+ case 11:
+ if (arg_2 != 0)
+ *arg_2 ^= 1;
+ break;
+
+ case 23:
+ case 24:
+ break;
+
+ default:
+ _vm->_global->_inter_resVal = 0;
+ if (arg_2 != 0)
+ *arg_2 = 20;
+ break;
+ }
+ return 0;
+ } // operation == stopToken || operation == 30 || operation == 31 || operation == 10
+
+ if (operation < 1 || operation > 11) {
+ if (operation < 32 || operation > 37)
+ continue;
+
+ if (stkPos > 2) {
+ if (operPtr[-2] == 2) {
+ if (operPtr[-3] == 20) {
+ valPtr[-3] += valPtr[-1];
+ } else if (operPtr[-3] == 22) {
+ if (decodePtr(valPtr[-3]) != _vm->_global->_inter_resStr) {
+ strcpy(_vm->_global->_inter_resStr, decodePtr(valPtr[-3]));
+ valPtr[-3] = encodePtr(_vm->_global->_inter_resStr, kResStr);
+ }
+ strcat(_vm->_global->_inter_resStr, decodePtr(valPtr[-1]));
+ }
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+
+ } else if (operPtr[-2] == 3) {
+ valPtr[-3] -= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ } else if (operPtr[-2] == 4) {
+ valPtr[-3] |= valPtr[-1];
+ stkPos -= 2;
+ operPtr -= 2;
+ valPtr -= 2;
+ }
+ }
+ }
+ *operPtr = operation;
+ }
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/scenery.cpp b/engines/gob/scenery.cpp
new file mode 100644
index 0000000000..6061e75272
--- /dev/null
+++ b/engines/gob/scenery.cpp
@@ -0,0 +1,784 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/scenery.h"
+#include "gob/inter.h"
+#include "gob/video.h"
+#include "gob/draw.h"
+#include "gob/game.h"
+#include "gob/global.h"
+#include "gob/util.h"
+#include "gob/anim.h"
+#include "gob/parse.h"
+#include "gob/cdrom.h"
+
+namespace Gob {
+
+Scenery::Scenery(GobEngine *vm) : _vm(vm) {
+ int i;
+
+ for (i = 0; i < 20; i++) {
+ _spriteRefs[i] = 0;
+ _spriteResId[i] = 0;
+ }
+ for (i = 0; i < 70; i++ ) {
+ _staticPictToSprite[i] = 0;
+ _animPictToSprite[i] = 0;
+ }
+ for (i = 0; i < 10; i++) {
+ _staticPictCount[i] = 0;
+ _staticFromExt[i] = 0;
+ _staticResId[i] = 0;
+ _animPictCount[i] = 0;
+ _animFromExt[i] = 0;
+ _animResId[i] = 0;
+ }
+
+ _curStatic = 0;
+ _curStaticLayer = 0;
+
+ _toRedrawLeft = 0;
+ _toRedrawRight = 0;
+ _toRedrawTop = 0;
+ _toRedrawBottom = 0;
+
+ _animTop = 0;
+ _animLeft = 0;
+
+ _pCaptureCounter = 0;
+}
+
+int16 Scenery::loadStatic(char search) {
+ int16 tmp;
+ int16 *backsPtr;
+ int16 picsCount;
+ int16 resId;
+ int16 i;
+ int16 sceneryIndex;
+ char *dataPtr;
+ Static *ptr;
+ int16 offset;
+ int16 pictDescId;
+ int16 width;
+ int16 height;
+ int16 sprResId;
+ int16 sprIndex;
+
+ _vm->_inter->evalExpr(&sceneryIndex);
+ tmp = _vm->_inter->load16();
+ backsPtr = (int16 *)_vm->_global->_inter_execPtr;
+ _vm->_global->_inter_execPtr += tmp * 2;
+ picsCount = _vm->_inter->load16();
+ resId = _vm->_inter->load16();
+ if (search) {
+ for (i = 0; i < 10; i++) {
+ if (_staticPictCount[i] != -1 && _staticResId[i] == resId) {
+ _vm->_global->_inter_execPtr += 8 * _staticPictCount[i];
+ return i;
+ }
+
+ if (_staticPictCount[i] == -1 && i < sceneryIndex)
+ sceneryIndex = i;
+ }
+ }
+
+ _staticPictCount[sceneryIndex] = picsCount;
+ _staticResId[sceneryIndex] = resId;
+
+ if (resId >= 30000) {
+ _staticFromExt[sceneryIndex] = 1;
+ dataPtr = _vm->_game->loadExtData(resId, 0, 0);
+ } else {
+ _staticFromExt[sceneryIndex] = 0;
+ dataPtr = _vm->_game->loadTotResource(resId);
+ }
+
+ ptr = &_statics[sceneryIndex];
+ ptr->dataPtr = dataPtr;
+
+ ptr->layersCount = (int16)READ_LE_UINT16(dataPtr);
+ dataPtr += 2;
+
+ ptr->layers = new StaticLayer*[ptr->layersCount];
+ ptr->pieces = new PieceDesc*[picsCount];
+ ptr->piecesFromExt = new int8[picsCount];
+
+ for (i = 0; i < ptr->layersCount; i++) {
+ offset = (int16)READ_LE_UINT16(&((int16 *)dataPtr)[i]);
+ ptr->layers[i] = (StaticLayer *)(dataPtr + offset - 2);
+
+ ptr->layers[i]->planeCount = (int16)READ_LE_UINT16(&ptr->layers[i]->planeCount);
+
+ for (int j = 0; j < ptr->layers[i]->planeCount; ++j) {
+ ptr->layers[i]->planes[j].destX = (int16)READ_LE_UINT16(&ptr->layers[i]->planes[j].destX);
+ ptr->layers[i]->planes[j].destY = (int16)READ_LE_UINT16(&ptr->layers[i]->planes[j].destY);
+ }
+
+ ptr->layers[i]->backResId = (int16)READ_LE_UINT16(backsPtr);
+ backsPtr++;
+ }
+
+ for (i = 0; i < picsCount; i++) {
+ pictDescId = _vm->_inter->load16();
+ if (pictDescId >= 30000) {
+ ptr->pieces[i] =
+ (PieceDesc *) _vm->_game->loadExtData(pictDescId, 0,
+ 0);
+ ptr->piecesFromExt[i] = 1;
+ } else {
+ ptr->pieces[i] =
+ (PieceDesc *)
+ _vm->_game->loadTotResource(pictDescId);
+ ptr->piecesFromExt[i] = 0;
+ }
+
+ width = _vm->_inter->load16();
+ height = _vm->_inter->load16();
+ sprResId = _vm->_inter->load16();
+ for (sprIndex = 0; sprIndex < 20; sprIndex++) {
+ if (_spriteResId[sprIndex] == sprResId)
+ break;
+ }
+
+ if (sprIndex < 20) {
+ _staticPictToSprite[7 * sceneryIndex + i] =
+ sprIndex;
+ _spriteRefs[sprIndex]++;
+ } else {
+ for (sprIndex = 19; _vm->_draw->_spritesArray[sprIndex] != 0;
+ sprIndex--);
+
+ _staticPictToSprite[7 * sceneryIndex + i] =
+ sprIndex;
+ _spriteRefs[sprIndex] = 1;
+ _spriteResId[sprIndex] = sprResId;
+ _vm->_draw->_spritesArray[sprIndex] =
+ _vm->_video->initSurfDesc(_vm->_global->_videoMode, width, height, 2);
+
+ _vm->_video->clearSurf(_vm->_draw->_spritesArray[sprIndex]);
+ _vm->_draw->_destSurface = sprIndex;
+ _vm->_draw->_spriteLeft = sprResId;
+ _vm->_draw->_transparency = 0;
+ _vm->_draw->_destSpriteX = 0;
+ _vm->_draw->_destSpriteY = 0;
+ _vm->_draw->spriteOperation(DRAW_LOADSPRITE);
+ }
+ }
+ return sceneryIndex + 100;
+}
+
+void Scenery::freeStatic(int16 index) {
+ int16 i;
+ int16 spr;
+
+ if (index == -1)
+ _vm->_inter->evalExpr(&index);
+
+ if (_staticPictCount[index] == -1)
+ return;
+
+ for (i = 0; i < _staticPictCount[index]; i++) {
+ if (_statics[index].piecesFromExt[i] == 1)
+ delete[] _statics[index].pieces[i];
+
+ spr = _staticPictToSprite[index * 7 + i];
+ _spriteRefs[spr]--;
+ if (_spriteRefs[spr] == 0) {
+ _vm->_video->freeSurfDesc(_vm->_draw->_spritesArray[spr]);
+ _vm->_draw->_spritesArray[spr] = 0;
+ _spriteResId[spr] = -1;
+ }
+ }
+
+ delete[] _statics[index].layers;
+ delete[] _statics[index].pieces;
+ delete[] _statics[index].piecesFromExt;
+ if (_staticFromExt[index] == 1)
+ delete[] _statics[index].dataPtr;
+
+ _staticFromExt[index] = 0;
+ _staticPictCount[index] = -1;
+}
+
+void Scenery::renderStatic(int16 scenery, int16 layer) {
+ Static *ptr;
+ StaticLayer *layerPtr;
+ StaticPlane *planePtr;
+ int16 planeCount;
+ int16 order;
+ int16 plane;
+
+ int16 pieceIndex;
+ int16 pictIndex;
+
+ int16 left;
+ int16 right;
+ int16 top;
+ int16 bottom;
+
+ ptr = &_statics[scenery];
+ if (layer >= ptr->layersCount)
+ return;
+
+ layerPtr = ptr->layers[layer];
+
+ _vm->_draw->_spriteLeft = layerPtr->backResId;
+ if (_vm->_draw->_spriteLeft != -1) {
+ _vm->_draw->_destSpriteX = 0;
+ _vm->_draw->_destSpriteY = 0;
+ _vm->_draw->_destSurface = 21;
+ _vm->_draw->_transparency = 0;
+ _vm->_draw->spriteOperation(DRAW_LOADSPRITE);
+ }
+
+ planeCount = layerPtr->planeCount;
+ for (order = 0; order < 10; order++) {
+ for (plane = 0, planePtr = layerPtr->planes;
+ plane < planeCount; plane++, planePtr++) {
+ if (planePtr->drawOrder != order)
+ continue;
+
+ pieceIndex = planePtr->pieceIndex;
+ pictIndex = planePtr->pictIndex - 1;
+
+ _vm->_draw->_destSpriteX = planePtr->destX;
+ _vm->_draw->_destSpriteY = planePtr->destY;
+ left = FROM_LE_16(ptr->pieces[pictIndex][pieceIndex].left);
+ right = FROM_LE_16(ptr->pieces[pictIndex][pieceIndex].right);
+ top = FROM_LE_16(ptr->pieces[pictIndex][pieceIndex].top);
+ bottom = FROM_LE_16(ptr->pieces[pictIndex][pieceIndex].bottom);
+
+ _vm->_draw->_sourceSurface =
+ _staticPictToSprite[scenery * 7 + pictIndex];
+ _vm->_draw->_destSurface = 21;
+ _vm->_draw->_spriteLeft = left;
+ _vm->_draw->_spriteTop = top;
+ _vm->_draw->_spriteRight = right - left + 1;
+ _vm->_draw->_spriteBottom = bottom - top + 1;
+ _vm->_draw->_transparency = planePtr->transp ? 3 : 0;
+ _vm->_draw->spriteOperation(DRAW_BLITSURF);
+ }
+ }
+}
+
+void Scenery::interRenderStatic(void) {
+ int16 layer;
+ int16 index;
+
+ _vm->_inter->evalExpr(&index);
+ _vm->_inter->evalExpr(&layer);
+ renderStatic(index, layer);
+}
+
+void Scenery::interLoadCurLayer(void) {
+ _vm->_inter->evalExpr(&_curStatic);
+ _vm->_inter->evalExpr(&_curStaticLayer);
+}
+
+void Scenery::updateStatic(int16 orderFrom) {
+ StaticLayer *layerPtr;
+ PieceDesc **pictPtr;
+ StaticPlane *planePtr;
+ int16 planeCount;
+ int16 order;
+ int16 plane;
+ int16 pieceIndex;
+ int16 pictIndex;
+
+ int16 left;
+ int16 right;
+ int16 top;
+ int16 bottom;
+
+ if (_curStatic == -1)
+ return;
+
+ if (_curStaticLayer >= _statics[_curStatic].layersCount)
+ return;
+
+ layerPtr = _statics[_curStatic].layers[_curStaticLayer];
+ pictPtr = _statics[_curStatic].pieces;
+
+ planeCount = layerPtr->planeCount;
+
+ for (order = orderFrom; order < 10; order++) {
+ for (planePtr = layerPtr->planes, plane = 0;
+ plane < planeCount; plane++, planePtr++) {
+ if (planePtr->drawOrder != order)
+ continue;
+
+ pieceIndex = planePtr->pieceIndex;
+ pictIndex = planePtr->pictIndex - 1;
+ _vm->_draw->_destSpriteX = planePtr->destX;
+ _vm->_draw->_destSpriteY = planePtr->destY;
+
+ left = FROM_LE_16(pictPtr[pictIndex][pieceIndex].left);
+ right = FROM_LE_16(pictPtr[pictIndex][pieceIndex].right);
+ top = FROM_LE_16(pictPtr[pictIndex][pieceIndex].top);
+ bottom = FROM_LE_16(pictPtr[pictIndex][pieceIndex].bottom);
+
+ if (_vm->_draw->_destSpriteX > _toRedrawRight)
+ continue;
+
+ if (_vm->_draw->_destSpriteY > _toRedrawBottom)
+ continue;
+
+ if (_vm->_draw->_destSpriteX < _toRedrawLeft) {
+ left += _toRedrawLeft - _vm->_draw->_destSpriteX;
+ _vm->_draw->_destSpriteX = _toRedrawLeft;
+ }
+
+ if (_vm->_draw->_destSpriteY < _toRedrawTop) {
+ top += _toRedrawTop - _vm->_draw->_destSpriteY;
+ _vm->_draw->_destSpriteY = _toRedrawTop;
+ }
+
+ _vm->_draw->_spriteLeft = left;
+ _vm->_draw->_spriteTop = top;
+ _vm->_draw->_spriteRight = right - left + 1;
+ _vm->_draw->_spriteBottom = bottom - top + 1;
+
+ if (_vm->_draw->_spriteRight <= 0 || _vm->_draw->_spriteBottom <= 0)
+ continue;
+
+ if (_vm->_draw->_destSpriteX + _vm->_draw->_spriteRight - 1 >
+ _toRedrawRight)
+ _vm->_draw->_spriteRight =
+ _toRedrawRight - _vm->_draw->_destSpriteX + 1;
+
+ if (_vm->_draw->_destSpriteY + _vm->_draw->_spriteBottom - 1 >
+ _toRedrawBottom)
+ _vm->_draw->_spriteBottom =
+ _toRedrawBottom - _vm->_draw->_destSpriteY + 1;
+
+ _vm->_draw->_sourceSurface =
+ _staticPictToSprite[_curStatic * 7 +
+ pictIndex];
+ _vm->_draw->_destSurface = 21;
+ _vm->_draw->_transparency = planePtr->transp ? 3 : 0;
+ _vm->_draw->spriteOperation(DRAW_BLITSURF);
+ }
+ }
+}
+
+int16 Scenery::loadAnim(char search) {
+ int16 picsCount;
+ int16 resId;
+ int16 i;
+ int16 sceneryIndex;
+ char *dataPtr;
+ Animation *ptr;
+ int16 offset;
+ int16 pictDescId;
+ int16 width;
+ int16 height;
+ int16 sprResId;
+ int16 sprIndex;
+
+ if (_vm->_cdrom->_cdPlaying) {
+ while (_vm->_cdrom->getTrackPos() != -1)
+ _vm->_util->longDelay(50);
+
+ _vm->_cdrom->_cdPlaying = false;
+ }
+
+ _vm->_inter->evalExpr(&sceneryIndex);
+ picsCount = _vm->_inter->load16();
+ resId = _vm->_inter->load16();
+
+ if (search) {
+ for (i = 0; i < 10; i++) {
+ if (_animPictCount[i] != 0
+ && _animResId[i] == resId) {
+ _vm->_global->_inter_execPtr += 8 * _animPictCount[i];
+ return i;
+ }
+
+ if (_animPictCount[i] == 0 && i < sceneryIndex)
+ sceneryIndex = i;
+ }
+ }
+
+ _animPictCount[sceneryIndex] = picsCount;
+ _animResId[sceneryIndex] = resId;
+
+ if (resId >= 30000) {
+ _animFromExt[sceneryIndex] = 1;
+ dataPtr = _vm->_game->loadExtData(resId, 0, 0);
+ } else {
+ _animFromExt[sceneryIndex] = 0;
+ dataPtr = _vm->_game->loadTotResource(resId);
+ }
+
+ ptr = &_animations[sceneryIndex];
+ ptr->dataPtr = dataPtr;
+
+ ptr->layersCount = READ_LE_UINT16(dataPtr);
+ dataPtr += 2;
+
+ ptr->layers = new AnimLayer*[ptr->layersCount];
+ ptr->pieces = new PieceDesc*[picsCount];
+ ptr->piecesFromExt = new int8[picsCount];
+
+ for (i = 0; i < ptr->layersCount; i++) {
+ offset = (int16)READ_LE_UINT16(&((int16 *)dataPtr)[i]);
+ ptr->layers[i] = (AnimLayer *) (dataPtr + offset - 2);
+
+ ptr->layers[i]->unknown0 = (int16)READ_LE_UINT16(&ptr->layers[i]->unknown0);
+ ptr->layers[i]->posX = (int16)READ_LE_UINT16(&ptr->layers[i]->posX);
+ ptr->layers[i]->posY = (int16)READ_LE_UINT16(&ptr->layers[i]->posY);
+ ptr->layers[i]->animDeltaX = (int16)READ_LE_UINT16(&ptr->layers[i]->animDeltaX);
+ ptr->layers[i]->animDeltaY = (int16)READ_LE_UINT16(&ptr->layers[i]->animDeltaY);
+ ptr->layers[i]->framesCount = (int16)READ_LE_UINT16(&ptr->layers[i]->framesCount);
+ }
+
+ for (i = 0; i < picsCount; i++) {
+ pictDescId = _vm->_inter->load16();
+ if (pictDescId >= 30000) {
+ ptr->pieces[i] =
+ (PieceDesc *) _vm->_game->loadExtData(pictDescId, 0,
+ 0);
+ ptr->piecesFromExt[i] = 1;
+ } else {
+ ptr->pieces[i] =
+ (PieceDesc *)
+ _vm->_game->loadTotResource(pictDescId);
+ ptr->piecesFromExt[i] = 0;
+ }
+
+ width = _vm->_inter->load16();
+ height = _vm->_inter->load16();
+ sprResId = _vm->_inter->load16();
+ for (sprIndex = 0; sprIndex < 20; sprIndex++) {
+ if (_spriteResId[sprIndex] == sprResId)
+ break;
+ }
+
+ if (sprIndex < 20) {
+ _animPictToSprite[7 * sceneryIndex + i] = sprIndex;
+ _spriteRefs[sprIndex]++;
+ } else {
+ for (sprIndex = 19; _vm->_draw->_spritesArray[sprIndex] != 0;
+ sprIndex--);
+
+ _animPictToSprite[7 * sceneryIndex + i] = sprIndex;
+ _spriteRefs[sprIndex] = 1;
+ _spriteResId[sprIndex] = sprResId;
+ _vm->_draw->_spritesArray[sprIndex] =
+ _vm->_video->initSurfDesc(_vm->_global->_videoMode, width, height, 2);
+
+ _vm->_video->clearSurf(_vm->_draw->_spritesArray[sprIndex]);
+ _vm->_draw->_destSurface = sprIndex;
+ _vm->_draw->_spriteLeft = sprResId;
+ _vm->_draw->_transparency = 0;
+ _vm->_draw->_destSpriteX = 0;
+ _vm->_draw->_destSpriteY = 0;
+ _vm->_draw->spriteOperation(DRAW_LOADSPRITE);
+ }
+ }
+ return sceneryIndex + 100;
+}
+
+// flags & 1 - do capture all area animation is occupying
+// flags & 4 == 0 - calculate animation final size
+// flags & 2 != 0 - don't check with "toRedraw"'s
+// flags & 4 != 0 - checkk view toRedraw
+void Scenery::updateAnim(int16 layer, int16 frame, int16 animation, int16 flags,
+ int16 drawDeltaX, int16 drawDeltaY, char doDraw) {
+ AnimLayer *layerPtr;
+ PieceDesc **pictPtr;
+ AnimFramePiece *framePtr;
+
+ uint16 pieceIndex;
+ uint16 pictIndex;
+
+ int16 left;
+ int16 right;
+ int16 top;
+ int16 bottom;
+
+ byte highX;
+ byte highY;
+
+ int16 i;
+ int16 transp;
+
+ int16 destX;
+ int16 destY;
+
+ if (layer >= _animations[animation].layersCount)
+ return;
+
+ layerPtr = _animations[animation].layers[layer];
+
+ if (frame >= layerPtr->framesCount)
+ return;
+
+ if (flags & 1) // Do capture
+ {
+ updateAnim(layer, frame, animation, 0, drawDeltaX,
+ drawDeltaY, 0);
+
+ if (_toRedrawLeft == -12345) // Some magic number?
+ return;
+
+ _vm->_game->capturePush(_toRedrawLeft, _toRedrawTop,
+ _toRedrawRight - _toRedrawLeft + 1,
+ _toRedrawBottom - _toRedrawTop + 1);
+
+ *_pCaptureCounter = *_pCaptureCounter + 1;
+ }
+ pictPtr = _animations[animation].pieces;
+ framePtr = layerPtr->frames;
+
+ for (i = 0; i < frame; i++, framePtr++) {
+ while (framePtr->notFinal == 1)
+ framePtr++;
+ }
+
+ if ((flags & 4) == 0) {
+ _toRedrawLeft = -12345;
+ } else {
+ _toRedrawLeft =
+ MAX(_toRedrawLeft, _vm->_anim->_areaLeft);
+ _toRedrawTop =
+ MAX(_toRedrawTop, _vm->_anim->_areaTop);
+ _toRedrawRight =
+ MIN(_toRedrawRight,
+ (int16)(_vm->_anim->_areaLeft + _vm->_anim->_areaWidth - 1));
+ _toRedrawBottom =
+ MIN(_toRedrawBottom,
+ (int16)(_vm->_anim->_areaTop + _vm->_anim->_areaHeight - 1));
+ }
+
+ transp = layerPtr->transp ? 3 : 0;
+
+ framePtr--;
+ do {
+ framePtr++;
+
+ pieceIndex = framePtr->pieceIndex;
+ pictIndex = framePtr->pictIndex;
+
+ destX = framePtr->destX;
+ destY = framePtr->destY;
+
+ highX = pictIndex & 0xc0;
+ highY = pictIndex & 0x30;
+ highX >>= 6;
+ highY >>= 4;
+ if (destX >= 0)
+ destX += ((uint16)highX) << 7;
+ else
+ destX -= ((uint16)highX) << 7;
+
+ if (destY >= 0)
+ destY += ((uint16)highY) << 7;
+ else
+ destY -= ((uint16)highY) << 7;
+
+ if (drawDeltaX == 1000)
+ destX += layerPtr->posX;
+ else
+ destX += drawDeltaX;
+
+ if (drawDeltaY == 1000)
+ destY += layerPtr->posY;
+ else
+ destY += drawDeltaY;
+
+ pictIndex = (pictIndex & 15) - 1;
+
+ left = FROM_LE_16(pictPtr[pictIndex][pieceIndex].left);
+ right = FROM_LE_16(pictPtr[pictIndex][pieceIndex].right);
+ top = FROM_LE_16(pictPtr[pictIndex][pieceIndex].top);
+ bottom = FROM_LE_16(pictPtr[pictIndex][pieceIndex].bottom);
+
+ if (flags & 2) {
+ if (destX < _vm->_anim->_areaLeft) {
+ left += _vm->_anim->_areaLeft - destX;
+ destX = _vm->_anim->_areaLeft;
+ }
+
+ if (left <= right
+ && destX + right - left >=
+ _vm->_anim->_areaLeft + _vm->_anim->_areaWidth)
+ right -=
+ (destX + right - left) -
+ (_vm->_anim->_areaLeft + _vm->_anim->_areaWidth) +
+ 1;
+
+ if (destY < _vm->_anim->_areaTop) {
+ top += _vm->_anim->_areaTop - destY;
+ destY = _vm->_anim->_areaTop;
+ }
+
+ if (top <= bottom
+ && destY + bottom - top >=
+ _vm->_anim->_areaTop + _vm->_anim->_areaHeight)
+ bottom -=
+ (destY + bottom - top) -
+ (_vm->_anim->_areaTop + _vm->_anim->_areaHeight) +
+ 1;
+
+ } else if (flags & 4) {
+ if (destX < _toRedrawLeft) {
+ left += _toRedrawLeft - destX;
+ destX = _toRedrawLeft;
+ }
+
+ if (left <= right
+ && destX + right - left > _toRedrawRight)
+ right -=
+ destX + right - left - _toRedrawRight;
+
+ if (destY < _toRedrawTop) {
+ top += _toRedrawTop - destY;
+ destY = _toRedrawTop;
+ }
+
+ if (top <= bottom
+ && destY + bottom - top > _toRedrawBottom)
+ bottom -=
+ destY + bottom - top - _toRedrawBottom;
+ }
+
+ if (left > right || top > bottom)
+ continue;
+
+ if (doDraw) {
+ _vm->_draw->_sourceSurface =
+ _animPictToSprite[animation * 7 + pictIndex];
+ _vm->_draw->_destSurface = 21;
+
+ _vm->_draw->_spriteLeft = left;
+ _vm->_draw->_spriteTop = top;
+ _vm->_draw->_spriteRight = right - left + 1;
+ _vm->_draw->_spriteBottom = bottom - top + 1;
+ _vm->_draw->_destSpriteX = destX;
+ _vm->_draw->_destSpriteY = destY;
+ _vm->_draw->_transparency = transp;
+ _vm->_draw->spriteOperation(DRAW_BLITSURF);
+ }
+
+ if ((flags & 4) == 0) {
+ if (_toRedrawLeft == -12345) {
+ _toRedrawLeft = destX;
+ _animLeft = destX;
+ _toRedrawTop = destY;
+ _animTop = destY;
+ _toRedrawRight = destX + right - left;
+ _toRedrawBottom = destY + bottom - top;
+ } else {
+ _toRedrawLeft =
+ MIN(_toRedrawLeft, destX);
+ _toRedrawTop =
+ MIN(_toRedrawTop, destY);
+ _toRedrawRight =
+ MAX(_toRedrawRight,
+ (int16)(destX + right - left));
+ _toRedrawBottom =
+ MAX(_toRedrawBottom,
+ (int16)(destY + bottom - top));
+ }
+ }
+ } while (framePtr->notFinal == 1);
+}
+
+void Scenery::freeAnim(int16 animation) {
+ int16 i;
+ int16 spr;
+
+ if (animation == -1)
+ _vm->_inter->evalExpr(&animation);
+
+ if (_animPictCount[animation] == 0)
+ return;
+
+ for (i = 0; i < _animPictCount[animation]; i++) {
+ if (_animations[animation].piecesFromExt[i] == 1)
+ delete[] _animations[animation].pieces[i];
+
+ spr = _animPictToSprite[animation * 7 + i];
+ _spriteRefs[spr]--;
+ if (_spriteRefs[spr] == 0) {
+ _vm->_video->freeSurfDesc(_vm->_draw->_spritesArray[spr]);
+
+ _vm->_draw->_spritesArray[spr] = 0;
+ _spriteResId[spr] = -1;
+ }
+ }
+
+ delete[] _animations[animation].layers;
+ delete[] _animations[animation].pieces;
+ delete[] _animations[animation].piecesFromExt;
+ if (_animFromExt[animation] == 1)
+ delete[] _animations[animation].dataPtr;
+
+ _animFromExt[animation] = 0;
+ _animPictCount[animation] = 0;
+}
+
+void Scenery::interUpdateAnim(void) {
+ int16 deltaX;
+ int16 deltaY;
+ int16 flags;
+ int16 frame;
+ int16 layer;
+ int16 animation;
+
+ _vm->_inter->evalExpr(&deltaX);
+ _vm->_inter->evalExpr(&deltaY);
+ _vm->_inter->evalExpr(&animation);
+ _vm->_inter->evalExpr(&layer);
+ _vm->_inter->evalExpr(&frame);
+ flags = _vm->_inter->load16();
+ updateAnim(layer, frame, animation, flags, deltaX, deltaY, 1);
+}
+
+void Scenery::interStoreParams(void) {
+ AnimLayer *layerPtr;
+ int16 animation;
+ int16 layer;
+ int16 var;
+
+ warning("interStoreParams: Storing...");
+
+ _vm->_inter->evalExpr(&animation);
+ _vm->_inter->evalExpr(&layer);
+ layerPtr = _animations[animation].layers[layer];
+
+ var = _vm->_parse->parseVarIndex();
+ WRITE_VAR_OFFSET(var, layerPtr->animDeltaX);
+
+ var = _vm->_parse->parseVarIndex();
+ WRITE_VAR_OFFSET(var, layerPtr->animDeltaY);
+
+ var = _vm->_parse->parseVarIndex();
+ WRITE_VAR_OFFSET(var, layerPtr->unknown0);
+
+ var = _vm->_parse->parseVarIndex();
+ WRITE_VAR_OFFSET(var, layerPtr->framesCount);
+}
+
+} // End of namespace Gob
diff --git a/engines/gob/scenery.h b/engines/gob/scenery.h
new file mode 100644
index 0000000000..119055f7bd
--- /dev/null
+++ b/engines/gob/scenery.h
@@ -0,0 +1,148 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_SCENERY_H
+#define GOB_SCENERY_H
+
+namespace Gob {
+
+class Scenery {
+public:
+#pragma START_PACK_STRUCTS
+ struct PieceDesc {
+ int16 left; //NOTE:
+ int16 right; //These are stored in Little Endian format
+ int16 top; //And should be converted by client code when accessed
+ int16 bottom; //i.e. use FROM_LE_16()
+ } GCC_PACK;
+
+ struct StaticPlane {
+ int8 pictIndex;
+ int8 pieceIndex;
+ int8 drawOrder;
+ int16 destX;
+ int16 destY;
+ int8 transp;
+ } GCC_PACK;
+
+ struct StaticLayer {
+ int16 backResId;
+ int16 planeCount;
+ StaticPlane planes[1];
+ } GCC_PACK;
+
+ // Animations
+
+ struct AnimFramePiece {
+ byte pictIndex;
+ byte pieceIndex;
+ int8 destX;
+ int8 destY;
+ int8 notFinal;
+ } GCC_PACK;
+
+ struct AnimLayer {
+ int16 unknown0;
+ int16 posX;
+ int16 posY;
+ int16 animDeltaX;
+ int16 animDeltaY;
+ int8 transp;
+ int16 framesCount;
+ AnimFramePiece frames[1];
+ } GCC_PACK;
+#pragma END_PACK_STRUCTS
+
+ struct Static {
+ int16 layersCount;
+ StaticLayer **layers;
+ PieceDesc **pieces;
+ int8 *piecesFromExt;
+ char *dataPtr;
+ Static() : layersCount(0), layers(0), pieces(0),
+ piecesFromExt(0), dataPtr(0) {}
+ };
+
+ struct Animation {
+ int16 layersCount;
+ AnimLayer **layers;
+ PieceDesc **pieces;
+ int8 *piecesFromExt;
+ char *dataPtr;
+ Animation() : layersCount(0), layers(0), pieces(0),
+ piecesFromExt(0), dataPtr(0) {}
+ };
+
+ // Global variables
+
+ char _spriteRefs[20];
+ int16 _spriteResId[20];
+
+ char _staticPictToSprite[70];
+ int16 _staticPictCount[10];
+ Static _statics[10];
+ char _staticFromExt[10];
+ int16 _staticResId[10];
+
+ char _animPictToSprite[70];
+ int16 _animPictCount[10];
+ char _animFromExt[10];
+ Animation _animations[10];
+ int16 _animResId[10];
+
+ int16 _curStatic;
+ int16 _curStaticLayer;
+
+ int16 _toRedrawLeft;
+ int16 _toRedrawRight;
+ int16 _toRedrawTop;
+ int16 _toRedrawBottom;
+
+ int16 _animTop;
+ int16 _animLeft;
+
+ int16 *_pCaptureCounter;
+
+ // Functions
+
+ int16 loadStatic(char search);
+ void freeStatic(int16 index);
+ void renderStatic(int16 scenery, int16 layer);
+ void interRenderStatic(void);
+ void interLoadCurLayer(void);
+ void updateStatic(int16 orderFrom);
+ int16 loadAnim(char search);
+ void updateAnim(int16 layer, int16 frame, int16 animation, int16 flags,
+ int16 drawDeltaX, int16 drawDeltaY, char doDraw);
+ void freeAnim(int16 animation);
+ void interUpdateAnim(void);
+ void interStoreParams(void);
+
+ Scenery(GobEngine *vm);
+
+protected:
+ GobEngine *_vm;
+};
+
+} // End of namespace Gob
+
+#endif /* __SCENERY_H */
diff --git a/engines/gob/sound.cpp b/engines/gob/sound.cpp
new file mode 100644
index 0000000000..51c36ef22a
--- /dev/null
+++ b/engines/gob/sound.cpp
@@ -0,0 +1,145 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/sound.h"
+
+namespace Gob {
+
+void Snd::SquareWaveStream::playNote(int freq, int32 ms, uint rate) {
+ _rate = rate;
+ _periodLength = _rate / (2 * freq);
+ _periodSamples = 0;
+ _sampleValue = 6000;
+ if (ms == -1) {
+ _remainingSamples = 1;
+ _beepForever = true;
+ } else {
+ _remainingSamples = (_rate * ms) / 1000;
+ _beepForever = false;
+ }
+}
+
+int Snd::SquareWaveStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samples = 0;
+
+ while (samples < numSamples && _remainingSamples > 0) {
+ *buffer++ = _sampleValue;
+ if (_periodSamples++ > _periodLength) {
+ _periodSamples = 0;
+ _sampleValue = -_sampleValue;
+ }
+ samples++;
+ if (!_beepForever)
+ _remainingSamples--;
+ }
+
+ return samples;
+}
+
+Snd::Snd(GobEngine *vm) : _vm(vm) {
+ //CleanupFuncPtr cleanupFunc;// = &snd_cleanupFuncCallback();
+ _cleanupFunc = 0;
+ for (int i = 0; i < ARRAYSIZE(_loopingSounds); i++)
+ _loopingSounds[i] = NULL;
+ _playingSound = 0;
+}
+
+void Snd::loopSounds(void) {
+ for (int i = 0; i < ARRAYSIZE(_loopingSounds); i++) {
+ SoundDesc *snd = _loopingSounds[i];
+ if (snd && !_vm->_mixer->isSoundHandleActive(snd->handle)) {
+ if (snd->repCount-- > 0) {
+ _vm->_mixer->playRaw(&snd->handle, snd->data, snd->size, snd->frequency, 0);
+ } else {
+ _loopingSounds[i] = NULL;
+ }
+ }
+ }
+}
+
+void Snd::setBlasterPort(int16 port) {return;}
+
+void Snd::speakerOn(int16 frequency, int32 length) {
+ _speakerStream.playNote(frequency, length, _vm->_mixer->getOutputRate());
+ if (!_vm->_mixer->isSoundHandleActive(_speakerHandle)) {
+ _vm->_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_speakerHandle, &_speakerStream, -1, 255, 0, false);
+ }
+}
+
+void Snd::speakerOff(void) {
+ _vm->_mixer->stopHandle(_speakerHandle);
+}
+
+void Snd::playSample(Snd::SoundDesc *sndDesc, int16 repCount, int16 frequency) {
+ assert(frequency > 0);
+
+ if (!_vm->_mixer->isSoundHandleActive(sndDesc->handle)) {
+ _vm->_mixer->playRaw(&sndDesc->handle, sndDesc->data, sndDesc->size, frequency, 0);
+ }
+
+ sndDesc->repCount = repCount - 1;
+ sndDesc->frequency = frequency;
+
+ if (repCount > 1) {
+ for (int i = 0; i < ARRAYSIZE(_loopingSounds); i++) {
+ if (!_loopingSounds[i]) {
+ _loopingSounds[i] = sndDesc;
+ return;
+ }
+ }
+ warning("Looping sounds list is full");
+ }
+}
+
+void Snd::writeAdlib(int16 port, int16 data) {
+ return;
+}
+
+Snd::SoundDesc *Snd::loadSoundData(const char *path) {
+ Snd::SoundDesc *sndDesc;
+
+ sndDesc = new Snd::SoundDesc;
+ sndDesc->size = _vm->_dataio->getDataSize(path);
+ sndDesc->data = _vm->_dataio->getData(path);
+
+ return sndDesc;
+}
+
+void Snd::freeSoundData(Snd::SoundDesc *sndDesc) {
+ _vm->_mixer->stopHandle(sndDesc->handle);
+
+ for (int i = 0; i < ARRAYSIZE(_loopingSounds); i++) {
+ if (_loopingSounds[i] == sndDesc)
+ _loopingSounds[i] = NULL;
+ }
+
+ delete[] sndDesc->data;
+ delete sndDesc;
+}
+
+} // End of namespace Gob
+
+
+
diff --git a/engines/gob/sound.h b/engines/gob/sound.h
new file mode 100644
index 0000000000..169c2e03a4
--- /dev/null
+++ b/engines/gob/sound.h
@@ -0,0 +1,104 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_SOUND_H
+#define GOB_SOUND_H
+
+#include "sound/audiostream.h"
+
+namespace Gob {
+
+class Snd {
+public:
+ struct SoundDesc {
+ Audio::SoundHandle handle;
+ char *data;
+ int32 size;
+ int16 repCount;
+ int16 timerTicks;
+ int16 inClocks;
+ int16 frequency;
+ int16 flag;
+ SoundDesc() : data(0), size(0), repCount(0), timerTicks(0),
+ inClocks(0), frequency(0), flag(0) {}
+ };
+
+ typedef void (*CleanupFuncPtr) (int16);
+
+ SoundDesc *_loopingSounds[5]; // Should be enough
+ char _playingSound;
+ CleanupFuncPtr _cleanupFunc;
+
+ Snd(GobEngine *vm);
+ void speakerOn(int16 frequency, int32 length);
+ void speakerOff(void);
+ SoundDesc *loadSoundData(const char *path);
+ void stopSound(int16 arg){return;}
+ void loopSounds(void);
+ void playSample(SoundDesc *sndDesc, int16 repCount, int16 frequency);
+ void playComposition(Snd::SoundDesc ** samples, int16 *composit, int16 freqVal) {;}
+ void waitEndPlay(void) {;}
+ void freeSoundData(SoundDesc *sndDesc);
+
+protected:
+ // TODO: This is a very primitive square wave generator. The only thing is
+ // has in common with the PC speaker is that it sounds terrible.
+ class SquareWaveStream : public AudioStream {
+ private:
+ uint _rate;
+ bool _beepForever;
+ uint32 _periodLength;
+ uint32 _periodSamples;
+ uint32 _remainingSamples;
+ int16 _sampleValue;
+
+ public:
+ SquareWaveStream() {}
+ ~SquareWaveStream() {}
+
+ void playNote(int freq, int32 ms, uint rate);
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool endOfData() const { return _remainingSamples == 0; }
+ bool isStereo() const { return false; }
+ int getRate() const { return _rate; }
+ };
+
+ SquareWaveStream _speakerStream;
+ Audio::SoundHandle _speakerHandle;
+
+ GobEngine *_vm;
+
+ void cleanupFuncCallback() {;}
+ int16 checkProAudio(void) {return 0;}
+ int16 checkAdlib(void) {return 0;}
+ int16 checkBlaster(void) {return 0;}
+
+ void writeAdlib(int16 port, int16 data);
+ void setBlasterPort(int16 port);
+ void setResetTimerFlag(char flag){return;}
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/timer.cpp b/engines/gob/timer.cpp
new file mode 100644
index 0000000000..0b3bc06588
--- /dev/null
+++ b/engines/gob/timer.cpp
@@ -0,0 +1,37 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/sound.h"
+#include "gob/timer.h"
+
+namespace Gob {
+
+void GTimer::enableTimer() {
+ debug(4, "STUB: GTimer::enableTimer()");
+}
+
+void GTimer::disableTimer() {
+ debug(4, "STUB: GTimer::disableTimer()");
+}
+}
diff --git a/engines/gob/timer.h b/engines/gob/timer.h
new file mode 100644
index 0000000000..d47d20b376
--- /dev/null
+++ b/engines/gob/timer.h
@@ -0,0 +1,43 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_TIMER_H
+#define GOB_TIMER_H
+
+namespace Gob {
+
+class GTimer {
+public:
+ typedef void (* TickHandler) (void);
+
+ void enableTimer(void);
+ void disableTimer(void);
+ void setHandler(void);
+ void restoreHandler(void);
+ void addTicks(int16 ticks);
+ void setTickHandler(TickHandler handler);
+ int32 getTicks(void);
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/util.cpp b/engines/gob/util.cpp
new file mode 100644
index 0000000000..7e6996f00f
--- /dev/null
+++ b/engines/gob/util.cpp
@@ -0,0 +1,486 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/timer.h"
+#include "gob/util.h"
+#include "gob/draw.h"
+#include "gob/game.h"
+
+namespace Gob {
+
+Util::Util(GobEngine *vm) : _vm(vm) {
+ _mouseX = 0;
+ _mouseY = 0;
+ _mouseButtons = 0;
+ for (int i = 0; i < KEYBUFSIZE; i++)
+ _keyBuffer[i] = 0;
+ _keyBufferHead = 0;
+ _keyBufferTail = 0;
+}
+
+void Util::addKeyToBuffer(int16 key) {
+ if ((_keyBufferHead + 1) % KEYBUFSIZE == _keyBufferTail) {
+ warning("key buffer overflow!");
+ return;
+ }
+
+ _keyBuffer[_keyBufferHead] = key;
+ _keyBufferHead = (_keyBufferHead + 1) % KEYBUFSIZE;
+}
+
+bool Util::keyBufferEmpty() {
+ return (_keyBufferHead == _keyBufferTail);
+}
+
+bool Util::getKeyFromBuffer(int16& key) {
+ if (_keyBufferHead == _keyBufferTail) return false;
+
+ key = _keyBuffer[_keyBufferTail];
+ _keyBufferTail = (_keyBufferTail + 1) % KEYBUFSIZE;
+
+ return true;
+}
+
+
+void Util::initInput(void) {
+ _mouseX = _mouseY = _mouseButtons = 0;
+ _keyBufferHead = _keyBufferTail = 0;
+}
+
+void Util::waitKey(void) {
+ // FIXME: wrong function name? This functions clears the keyboard buffer.
+ processInput();
+ _keyBufferHead = _keyBufferTail = 0;
+}
+
+int16 Util::translateKey(int16 key) {
+ struct keyS {
+ int16 from;
+ int16 to;
+ } keys[] = {
+ {8, 0x0e08}, // Backspace
+ {32, 0x3920}, // Space
+ {13, 0x1C0D}, // Enter
+ {27, 0x011b}, // ESC
+ {127, 0x5300}, // Del
+ {273, 0x4800}, // Up arrow
+ {274, 0x5000}, // Down arrow
+ {275, 0x4D00}, // Right arrow
+ {276, 0x4B00}, // Left arrow
+ {282, 0x3b00}, // F1
+ {283, 0x3c00}, // F2
+ {284, 0x3d00}, // F3
+ {285, 0x3E00}, // F4
+ {286, 0x011b}, // F5
+ {287, 0x4000}, // F6
+ {288, 0x4100}, // F7
+ {289, 0x4200}, // F8
+ {290, 0x4300}, // F9
+ {291, 0x4400} // F10
+ };
+ int i;
+
+ for (i = 0; i < ARRAYSIZE(keys); i++)
+ if (key == keys[i].from)
+ return keys[i].to;
+
+ if (key < 32 || key >= 128)
+ return 0;
+
+ return key;
+}
+
+int16 Util::getKey(void) {
+ int16 key;
+
+ while (!getKeyFromBuffer(key)) {
+ processInput();
+
+ if (keyBufferEmpty())
+ g_system->delayMillis(10);
+ }
+ return translateKey(key);
+}
+
+int16 Util::checkKey(void) {
+ int16 key;
+
+ if (!getKeyFromBuffer(key))
+ key = 0;
+
+ return translateKey(key);
+}
+
+int16 Util::getRandom(int16 max) {
+ return _vm->_rnd.getRandomNumber(max - 1);
+}
+
+void Util::processInput() {
+ OSystem::Event event;
+ while (g_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ _mouseButtons |= 1;
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+ _mouseButtons |= 2;
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _mouseButtons &= ~1;
+ break;
+ case OSystem::EVENT_RBUTTONUP:
+ _mouseButtons &= ~2;
+ break;
+ case OSystem::EVENT_KEYDOWN:
+ addKeyToBuffer(event.kbd.keycode);
+ break;
+ case OSystem::EVENT_KEYUP:
+ break;
+ case OSystem::EVENT_QUIT:
+ g_system->quit();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Util::getMouseState(int16 *pX, int16 *pY, int16 *pButtons) {
+ *pX = _mouseX;
+ *pY = _mouseY;
+
+ if (pButtons != 0)
+ *pButtons = _mouseButtons;
+}
+
+void Util::setMousePos(int16 x, int16 y) {
+ g_system->warpMouse(x, y);
+}
+
+void Util::longDelay(uint16 msecs) {
+ uint32 time = g_system->getMillis() + msecs;
+ do {
+ _vm->_video->waitRetrace(_vm->_global->_videoMode);
+ processInput();
+ delay(25);
+ } while (g_system->getMillis() < time);
+}
+
+void Util::delay(uint16 msecs) {
+ g_system->delayMillis(msecs);
+}
+
+void Util::beep(int16 freq) {
+ if (_vm->_global->_soundFlags == 0)
+ return;
+
+ _vm->_snd->speakerOn(freq, 50);
+}
+
+uint32 Util::getTimeKey(void) {
+ return g_system->getMillis();
+}
+
+void Util::waitMouseUp(void) {
+ int16 x;
+ int16 y;
+ int16 buttons;
+
+ do {
+ processInput();
+ getMouseState(&x, &y, &buttons);
+ if (buttons != 0) delay(10);
+ } while (buttons != 0);
+}
+
+void Util::waitMouseDown(void) {
+ int16 x;
+ int16 y;
+ int16 buttons;
+
+ do {
+ processInput();
+ getMouseState(&x, &y, &buttons);
+ if (buttons == 0) delay(10);
+ } while (buttons == 0);
+}
+
+/* NOT IMPLEMENTED */
+int16 Util::calcDelayTime() {
+ return 0;
+}
+
+/* NOT IMPLEMENTED */
+void Util::checkJoystick() {
+ _vm->_global->_useJoystick = 0;
+}
+
+void Util::setFrameRate(int16 rate) {
+ if (rate == 0)
+ rate = 1;
+
+ _vm->_global->_frameWaitTime = 1000 / rate;
+ _vm->_global->_startFrameTime = getTimeKey();
+}
+
+void Util::waitEndFrame() {
+ int32 time;
+
+ _vm->_video->waitRetrace(_vm->_global->_videoMode);
+
+ time = getTimeKey() - _vm->_global->_startFrameTime;
+ if (time > 1000 || time < 0) {
+ _vm->_global->_startFrameTime = getTimeKey();
+ return;
+ }
+ if (_vm->_global->_frameWaitTime - time > 0) {
+ delay(_vm->_global->_frameWaitTime - time);
+ }
+ _vm->_global->_startFrameTime = getTimeKey();
+}
+
+int16 joy_getState() {
+ return 0;
+}
+
+int16 joy_calibrate() {
+ return 0;
+}
+
+Video::FontDesc *Util::loadFont(const char *path) {
+ Video::FontDesc *fontDesc = new Video::FontDesc;
+ char *data;
+
+ if (fontDesc == 0)
+ return 0;
+
+ data = _vm->_dataio->getData(path);
+ if (data == 0) {
+ delete fontDesc;
+ return 0;
+ }
+
+ fontDesc->dataPtr = data + 4;
+ fontDesc->itemWidth = data[0] & 0x7f;
+ fontDesc->itemHeight = data[1];
+ fontDesc->startItem = data[2];
+ fontDesc->endItem = data[3];
+
+ fontDesc->itemSize =
+ ((fontDesc->itemWidth - 1) / 8 + 1) * fontDesc->itemHeight;
+ fontDesc->bitWidth = fontDesc->itemWidth;
+
+ if (data[0] & 0x80)
+ fontDesc->extraData =
+ data + 4 + fontDesc->itemSize * (fontDesc->endItem -
+ fontDesc->startItem + 1);
+ else
+ fontDesc->extraData = 0;
+ return fontDesc;
+}
+
+void Util::freeFont(Video::FontDesc * fontDesc) {
+ delete[] (fontDesc->dataPtr - 4);
+ delete fontDesc;
+}
+
+void Util::clearPalette(void) {
+ int16 i;
+ byte colors[768];
+
+ if (_vm->_global->_videoMode != 0x13)
+ error("clearPalette: Video mode 0x%x is not supported!",
+ _vm->_global->_videoMode);
+
+ if (_vm->_global->_setAllPalette) {
+ for (i = 0; i < 768; i++)
+ colors[i] = 0;
+ g_system->setPalette(colors, 0, 256);
+
+ return;
+ }
+
+ for (i = 0; i < 16; i++)
+ _vm->_video->setPalElem(i, 0, 0, 0, 0, _vm->_global->_videoMode);
+}
+
+void Util::insertStr(const char *str1, char *str2, int16 pos) {
+ int16 len1;
+ int16 i;
+ int16 from;
+
+ i = strlen(str2);
+ len1 = strlen(str1);
+ if (pos < i)
+ from = pos;
+ else
+ from = i;
+
+ for (; i >= from; i--)
+ str2[len1 + i] = str2[i];
+
+ for (i = 0; i < len1; i++)
+ str2[i + from] = str1[i];
+}
+
+void Util::cutFromStr(char *str, int16 from, int16 cutlen) {
+ int16 len;
+ int16 i;
+
+ //log_write("cutFromStr: str = %s, ", str);
+ len = strlen(str);
+ if (from >= len)
+ return;
+ if (from + cutlen > len) {
+ str[from] = 0;
+ //log_write("res = %s\n", str);
+ return;
+ }
+
+ i = from;
+ do {
+ str[i] = str[i + cutlen];
+ i++;
+ } while (str[i] != 0);
+ //log_write("res = %s\n", str);
+}
+
+void Util::listInsertFront(List * list, void *data) {
+ ListNode *node;
+
+ node = new ListNode;
+ if (list->pHead != 0) {
+ node->pData = data;
+ node->pNext = list->pHead;
+ node->pPrev = 0;
+ list->pHead->pPrev = node;
+ list->pHead = node;
+ } else {
+ list->pHead = node;
+ list->pTail = node;
+ node->pData = data;
+ node->pNext = 0;
+ node->pPrev = 0;
+ }
+}
+
+void Util::listInsertBack(List * list, void *data) {
+ ListNode *node;
+
+ if (list->pHead != 0) {
+ if (list->pTail == 0) {
+ list->pTail = list->pHead;
+ warning("listInsertBack: Broken list!");
+ }
+
+ node = new ListNode;
+ node->pData = data;
+ node->pPrev = list->pTail;
+ node->pNext = 0;
+ list->pTail->pNext = node;
+ list->pTail = node;
+ } else {
+ listInsertFront(list, data);
+ }
+}
+
+void Util::listDropFront(List * list) {
+ if (list->pHead->pNext == 0) {
+ delete list->pHead;
+ list->pHead = 0;
+ list->pTail = 0;
+ } else {
+ list->pHead = list->pHead->pNext;
+ delete list->pHead->pPrev;
+ list->pHead->pPrev = 0;
+ }
+}
+
+void Util::deleteList(List * list) {
+ while (list->pHead != 0) {
+ listDropFront(list);
+ }
+
+ delete list;
+}
+
+const char Util::trStr1[] =
+ " ' + - :0123456789: <=> abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz ";
+const char Util::trStr2[] =
+ " ueaaaaceeeiii ooouu aioun ";
+const char Util::trStr3[] = " ";
+
+void Util::prepareStr(char *str) {
+ uint16 i;
+ char *start, *end;
+ char buf[300];
+
+ strcpy(buf, trStr1);
+ strcat(buf, trStr2);
+ strcat(buf, trStr3);
+
+ for (i = 0; i < strlen(str); i++)
+ str[i] = buf[str[i] - 32];
+
+ while (str[0] == ' ')
+ cutFromStr(str, 0, 1);
+
+ while (strlen(str) > 0 && str[strlen(str) - 1] == ' ')
+ cutFromStr(str, strlen(str) - 1, 1);
+
+ start = strchr(str, ' ');
+
+ while (start != 0) {
+ if (*(start+1) == ' ') {
+ cutFromStr(str, start - str, 1);
+ continue;
+ }
+
+ end = strchr(start + 1, ' ');
+ if (end != 0)
+ start = end + 1;
+ else
+ start = 0;
+ }
+}
+
+void Util::waitMouseRelease(char drawMouse) {
+ int16 buttons;
+ int16 mouseX;
+ int16 mouseY;
+
+ do {
+ _vm->_game->checkKeys(&mouseX, &mouseY, &buttons, drawMouse);
+ if (drawMouse != 0)
+ _vm->_draw->animateCursor(2);
+ delay(10);
+ } while (buttons != 0);
+}
+
+void Util::keyboard_release(void) {;}
+} // End of namespace Gob
diff --git a/engines/gob/util.h b/engines/gob/util.h
new file mode 100644
index 0000000000..1e707b42c1
--- /dev/null
+++ b/engines/gob/util.h
@@ -0,0 +1,106 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_UTIL_H
+#define GOB_UTIL_H
+
+#include "gob/video.h"
+
+namespace Gob {
+
+#define KEYBUFSIZE 16
+
+class Util {
+public:
+ struct ListNode;
+ struct ListNode {
+ void *pData;
+ struct ListNode *pNext;
+ struct ListNode *pPrev;
+ ListNode() : pData(0), pNext(0), pPrev(0) {}
+ };
+
+ struct List {
+ ListNode *pHead;
+ ListNode *pTail;
+ List() : pHead(0), pTail(0) {}
+ };
+
+ void initInput(void);
+ void processInput(void);
+ void waitKey(void);
+ int16 getKey(void);
+ int16 checkKey(void);
+ int16 getRandom(int16 max);
+ void getMouseState(int16 *pX, int16 *pY, int16 *pButtons);
+ void setMousePos(int16 x, int16 y);
+ void longDelay(uint16 msecs);
+ void delay(uint16 msecs);
+ void beep(int16 freq);
+ uint32 getTimeKey(void);
+ void waitMouseUp(void);
+ void waitMouseDown(void);
+
+ void keyboard_init(void);
+ void keyboard_release(void);
+
+ void clearPalette(void);
+
+ void asm_setPaletteBlock(char *tmpPalBuffer, int16 start, int16 end);
+
+ void vid_waitRetrace(int16 mode);
+
+ Video::FontDesc *loadFont(const char *path);
+ void freeFont(Video::FontDesc * fontDesc);
+ static void insertStr(const char *str1, char *str2, int16 pos);
+ static void cutFromStr(char *str, int16 from, int16 cutlen);
+ void waitEndFrame();
+ void setFrameRate(int16 rate);
+
+ static void listInsertBack(List * list, void *data);
+ static void listInsertFront(List * list, void *data);
+ static void listDropFront(List * list);
+ static void deleteList(List * list);
+ static void prepareStr(char *str);
+ void waitMouseRelease(char drawMouse);
+
+ static const char trStr1[];
+ static const char trStr2[];
+ static const char trStr3[];
+ Util(GobEngine *vm);
+
+protected:
+ int16 _mouseX, _mouseY, _mouseButtons;
+ int16 _keyBuffer[KEYBUFSIZE], _keyBufferHead, _keyBufferTail;
+ GobEngine *_vm;
+
+ void addKeyToBuffer(int16 key);
+ bool keyBufferEmpty();
+ bool getKeyFromBuffer(int16& key);
+ int16 translateKey(int16 key);
+ int16 calcDelayTime();
+ void checkJoystick();
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/gob/video.cpp b/engines/gob/video.cpp
new file mode 100644
index 0000000000..83259c068d
--- /dev/null
+++ b/engines/gob/video.cpp
@@ -0,0 +1,541 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "gob/gob.h"
+#include "gob/global.h"
+#include "gob/video.h"
+#include "gob/dataio.h"
+
+#include "gob/driver_vga.h"
+
+namespace Gob {
+
+/* NOT IMPLEMENTED */
+
+Video::Video(GobEngine *vm) : _vm(vm) {
+}
+
+//XXX: Use this function to update the screen for now.
+// This should be moved to a better location later on.
+void Video::waitRetrace(int16) {
+ if (_vm->_global->_pPrimarySurfDesc) {
+ g_system->copyRectToScreen(_vm->_global->_pPrimarySurfDesc->vidPtr, 320, 0, 0, 320, 200);
+ g_system->updateScreen();
+ }
+}
+
+char Video::initDriver(int16 vidMode) {
+ warning("STUB: Video::initDriver");
+
+ // FIXME: Finish all this stuff :)
+ _videoDriver = new VGAVideoDriver();
+
+ return 1;
+}
+
+void Video::freeDriver() {
+ delete _videoDriver;
+ warning("STUB: Video::freeDriver");
+}
+
+int32 Video::getRectSize(int16 width, int16 height, int16 flag, int16 mode) {
+ int32 size;
+
+ if ((mode & 0x7f) != 0x13)
+ warning
+ ("Video::getRectSize: Video mode %d is not fully supported!",
+ mode & 0x7f);
+ switch (mode & 0x7f) {
+ case 5:
+ case 7:
+ size = ((int32)((width + 3) / 4)) * height * (flag + 1);
+ break;
+ case 0x13:
+ size = (int32)width *height;
+ break;
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ size = ((int32)((width + 3) / 4)) * height * 4;
+ break;
+ default:
+ size = ((int32)((width + 7) / 8)) * height * (flag + 4);
+ break;
+ }
+ return size;
+}
+
+Video::SurfaceDesc *Video::initSurfDesc(int16 vidMode, int16 width, int16 height, int16 flags) {
+ int8 flagsAnd2;
+ byte *vidMem = 0;
+ int32 sprSize;
+ int16 someFlags = 1;
+ SurfaceDesc *descPtr;
+
+ if (flags != PRIMARY_SURFACE)
+ _vm->_global->_sprAllocated++;
+
+ if (flags & RETURN_PRIMARY)
+ return _vm->_global->_pPrimarySurfDesc;
+
+ if (vidMode != 0x13)
+ error("Video::initSurfDesc: Only VGA 0x13 mode is supported!");
+
+ if ((flags & PRIMARY_SURFACE) == 0)
+ vidMode += 0x80;
+
+ if (flags & 2)
+ flagsAnd2 = 1;
+ else
+ flagsAnd2 = 0;
+
+ if (flags & PRIMARY_SURFACE) {
+ _vm->_global->_primaryWidth = width;
+ _vm->_global->_mouseMaxCol = width;
+ _vm->_global->_primaryHeight = height;
+ _vm->_global->_mouseMaxRow = height;
+ sprSize = 0;
+ } else {
+ sprSize = Video::getRectSize(width, height, flagsAnd2, vidMode);
+ if (flagsAnd2)
+ someFlags += 0x80;
+ }
+ if (flags & PRIMARY_SURFACE) {
+ descPtr = _vm->_global->_pPrimarySurfDesc;
+ delete[] descPtr->vidPtr;
+ assert(descPtr);
+ vidMem = new byte[320 * 200];
+ } else {
+ if (flags & DISABLE_SPR_ALLOC) {
+ descPtr = new SurfaceDesc;
+ // this case causes vidPtr to be set to invalid memory
+ assert(false);
+ } else {
+ descPtr = new SurfaceDesc;
+ descPtr->vidPtr = new byte[sprSize];
+ vidMem = descPtr->vidPtr;
+ }
+ }
+ if (descPtr == 0)
+ return 0;
+
+ descPtr->width = width;
+ descPtr->height = height;
+ descPtr->flag = someFlags;
+ descPtr->vidMode = vidMode;
+ descPtr->vidPtr = vidMem;
+
+ descPtr->reserved1 = 0;
+ descPtr->reserved2 = 0;
+ return descPtr;
+}
+
+void Video::freeSurfDesc(SurfaceDesc * surfDesc) {
+ delete[] surfDesc->vidPtr;
+ if (surfDesc != _vm->_global->_pPrimarySurfDesc) {
+ _vm->_global->_sprAllocated--;
+ delete surfDesc;
+ }
+}
+
+int16 Video::clampValue(int16 val, int16 max) {
+ if (val >= max)
+ val = max - 1;
+
+ if (val < 0)
+ val = 0;
+
+ return val;
+}
+
+void Video::drawSprite(SurfaceDesc *source, SurfaceDesc *dest,
+ int16 left, int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp) {
+ int16 temp;
+ int16 destRight;
+ int16 destBottom;
+
+ if (_vm->_global->_doRangeClamp) {
+ if (left > right) {
+ temp = left;
+ left = right;
+ right = temp;
+ }
+ if (top > bottom) {
+ temp = top;
+ top = bottom;
+ bottom = temp;
+ }
+ if (right < 0)
+ return;
+ if (bottom < 0)
+ return;
+ if (left >= source->width)
+ return;
+ if (top >= source->height)
+ return;
+
+ if (left < 0) {
+ x -= left;
+ left = 0;
+ }
+ if (top < 0) {
+ y -= top;
+ top = 0;
+ }
+ right = Video::clampValue(right, source->width);
+ bottom = Video::clampValue(bottom, source->height);
+ if (right - left >= source->width)
+ right = left + source->width - 1;
+ if (bottom - top >= source->height)
+ bottom = top + source->height - 1;
+
+ if (x < 0) {
+ left -= x;
+ x = 0;
+ }
+ if (y < 0) {
+ top -= y;
+ y = 0;
+ }
+ if (left > right)
+ return;
+ if (top > bottom)
+ return;
+
+ if (x >= dest->width)
+ return;
+
+ if (y >= dest->height)
+ return;
+
+ destRight = x + right - left;
+ destBottom = y + bottom - top;
+ if (destRight >= dest->width)
+ right -= destRight - dest->width + 1;
+
+ if (destBottom >= dest->height)
+ bottom -= destBottom - dest->height + 1;
+ }
+
+ _videoDriver->drawSprite(source, dest, left, top, right, bottom, x, y, transp);
+}
+
+void Video::fillRect(SurfaceDesc *dest, int16 left, int16 top, int16 right, int16 bottom,
+ int16 color) {
+ int16 temp;
+
+ if (_vm->_global->_doRangeClamp) {
+ if (left > right) {
+ temp = left;
+ left = right;
+ right = temp;
+ }
+ if (top > bottom) {
+ temp = top;
+ top = bottom;
+ bottom = temp;
+ }
+ if (right < 0)
+ return;
+ if (bottom < 0)
+ return;
+ if (left >= dest->width)
+ return;
+ if (top >= dest->height)
+ return;
+
+ left = Video::clampValue(left, dest->width);
+ top = Video::clampValue(top, dest->height);
+ right = Video::clampValue(right, dest->width);
+ bottom = Video::clampValue(bottom, dest->height);
+ }
+
+ _videoDriver->fillRect(dest, left, top, right, bottom, color);
+}
+
+void Video::drawLine(SurfaceDesc *dest, int16 x0, int16 y0, int16 x1, int16 y1, int16 color) {
+ if (x0 == x1 || y0 == y1) {
+ Video::fillRect(dest, x0, y0, x1, y1, color);
+ return;
+ }
+
+ _videoDriver->drawLine(dest, x0, y0, x1, y1, color);
+}
+
+void Video::putPixel(int16 x, int16 y, int16 color, SurfaceDesc *dest) {
+ if (x < 0 || y < 0 || x >= dest->width || y >= dest->height)
+ return;
+
+ _videoDriver->putPixel(x, y, color, dest);
+}
+
+void Video::drawLetter(unsigned char item, int16 x, int16 y, FontDesc *fontDesc, int16 color1,
+ int16 color2, int16 transp, SurfaceDesc *dest) {
+
+ _videoDriver->drawLetter(item, x, y, fontDesc, color1, color2, transp, dest);
+}
+
+void Video::clearSurf(SurfaceDesc *dest) {
+ Video::fillRect(dest, 0, 0, dest->width - 1, dest->height - 1, 0);
+}
+
+void Video::drawPackedSprite(byte *sprBuf, int16 width, int16 height, int16 x, int16 y,
+ int16 transp, SurfaceDesc *dest) {
+
+ if (Video::spriteUncompressor(sprBuf, width, height, x, y, transp, dest))
+ return;
+
+ if ((dest->vidMode & 0x7f) != 0x13)
+ error("Video::drawPackedSprite: Video mode 0x%x is not fully supported!",
+ dest->vidMode & 0x7f);
+
+ _videoDriver->drawPackedSprite(sprBuf, width, height, x, y, transp, dest);
+}
+
+void Video::setPalElem(int16 index, char red, char green, char blue, int16 unused,
+ int16 vidMode) {
+ byte pal[4];
+
+ _vm->_global->_redPalette[index] = red;
+ _vm->_global->_greenPalette[index] = green;
+ _vm->_global->_bluePalette[index] = blue;
+
+ if (vidMode != 0x13)
+ error("Video::setPalElem: Video mode 0x%x is not supported!",
+ vidMode);
+
+ pal[0] = (red << 2) | (red >> 4);
+ pal[1] = (green << 2) | (green >> 4);
+ pal[2] = (blue << 2) | (blue >> 4);
+ pal[3] = 0;
+ g_system->setPalette(pal, index, 1);
+}
+
+void Video::setPalette(PalDesc *palDesc) {
+ int16 i;
+ byte pal[1024];
+ int16 numcolors;
+
+ if (_vm->_global->_videoMode != 0x13)
+ error("Video::setPalette: Video mode 0x%x is not supported!",
+ _vm->_global->_videoMode);
+
+ if (_vm->_global->_setAllPalette)
+ numcolors = 256;
+ else
+ numcolors = 16;
+
+ for (i = 0; i < numcolors; i++) {
+ pal[i * 4 + 0] = (palDesc->vgaPal[i].red << 2) | (palDesc->vgaPal[i].red >> 4);
+ pal[i * 4 + 1] = (palDesc->vgaPal[i].green << 2) | (palDesc->vgaPal[i].green >> 4);
+ pal[i * 4 + 2] = (palDesc->vgaPal[i].blue << 2) | (palDesc->vgaPal[i].blue >> 4);
+ pal[i * 4 + 3] = 0;
+ }
+
+ g_system->setPalette(pal, 0, numcolors);
+}
+
+void Video::setFullPalette(PalDesc *palDesc) {
+ Color *colors;
+ int16 i;
+ byte pal[1024];
+
+ if (_vm->_global->_setAllPalette) {
+ colors = palDesc->vgaPal;
+ for (i = 0; i < 256; i++) {
+ _vm->_global->_redPalette[i] = colors[i].red;
+ _vm->_global->_greenPalette[i] = colors[i].green;
+ _vm->_global->_bluePalette[i] = colors[i].blue;
+ }
+
+ for (i = 0; i < 256; i++) {
+ pal[i * 4 + 0] = (colors[i].red << 2) | (colors[i].red >> 4);
+ pal[i * 4 + 1] = (colors[i].green << 2) | (colors[i].green >> 4);
+ pal[i * 4 + 2] = (colors[i].blue << 2) | (colors[i].blue >> 4);
+ pal[i * 4 + 3] = 0;
+ }
+ g_system->setPalette(pal, 0, 256);
+ } else {
+ Video::setPalette(palDesc);
+ }
+}
+
+void Video::initPrimary(int16 mode) {
+ int16 old;
+ if (mode != 0x13 && mode != 3 && mode != -1)
+ error("Video::initPrimary: Video mode 0x%x is not supported!",
+ mode);
+
+ if (_vm->_global->_videoMode != 0x13)
+ error("Video::initPrimary: Video mode 0x%x is not supported!",
+ mode);
+
+ old = _vm->_global->_oldMode;
+ if (mode == -1)
+ mode = 3;
+
+ _vm->_global->_oldMode = mode;
+ if (mode != 3)
+ Video::initDriver(mode);
+
+ if (mode != 3) {
+ Video::initSurfDesc(mode, 320, 200, PRIMARY_SURFACE);
+
+ if (_vm->_global->_dontSetPalette)
+ return;
+
+ Video::setFullPalette(_vm->_global->_pPaletteDesc);
+ }
+}
+
+char Video::spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight,
+ int16 x, int16 y, int16 transp, SurfaceDesc *destDesc) {
+ SurfaceDesc sourceDesc;
+ byte *memBuffer;
+ byte *srcPtr;
+ byte *destPtr;
+ byte *linePtr;
+ byte temp;
+ uint16 sourceLeft;
+ int16 curWidth;
+ int16 curHeight;
+ int16 offset;
+ int16 counter2;
+ uint16 cmdVar;
+ int16 bufPos;
+ int16 strLen;
+
+ if (!destDesc)
+ return 1;
+
+ if ((destDesc->vidMode & 0x7f) != 0x13)
+ error("Video::spriteUncompressor: Video mode 0x%x is not supported!",
+ destDesc->vidMode & 0x7f);
+
+ if (sprBuf[0] != 1)
+ return 0;
+
+ if (sprBuf[1] != 2)
+ return 0;
+
+ if (sprBuf[2] == 2) {
+ sourceDesc.width = srcWidth;
+ sourceDesc.height = srcHeight;
+ sourceDesc.vidMode = 0x93;
+ sourceDesc.vidPtr = sprBuf + 3;
+ Video::drawSprite(&sourceDesc, destDesc, 0, 0, srcWidth - 1,
+ srcHeight - 1, x, y, transp);
+ return 1;
+ } else {
+ memBuffer = new byte[4114];
+ if (memBuffer == 0)
+ return 0;
+
+ srcPtr = sprBuf + 3;
+ sourceLeft = READ_LE_UINT16(srcPtr);
+
+ destPtr = destDesc->vidPtr + destDesc->width * y + x;
+
+ curWidth = 0;
+ curHeight = 0;
+
+ linePtr = destPtr;
+ srcPtr += 4;
+
+ for (offset = 0; offset < 4078; offset++)
+ memBuffer[offset] = 0x20;
+
+ cmdVar = 0;
+ bufPos = 4078;
+ while (1) {
+ cmdVar >>= 1;
+ if ((cmdVar & 0x100) == 0) {
+ cmdVar = *srcPtr | 0xff00;
+ srcPtr++;
+ }
+ if ((cmdVar & 1) != 0) {
+ temp = *srcPtr++;
+ if (temp != 0 || transp == 0)
+ *destPtr = temp;
+ destPtr++;
+ curWidth++;
+ if (curWidth >= srcWidth) {
+ curWidth = 0;
+ linePtr += destDesc->width;
+ destPtr = linePtr;
+ curHeight++;
+ if (curHeight >= srcHeight)
+ break;
+ }
+ sourceLeft--;
+ if (sourceLeft == 0)
+ break;
+
+ memBuffer[bufPos] = temp;
+ bufPos++;
+ bufPos %= 4096;
+ } else {
+ offset = *srcPtr;
+ srcPtr++;
+ offset |= (*srcPtr & 0xf0) << 4;
+ strLen = (*srcPtr & 0x0f) + 3;
+ srcPtr++;
+
+ for (counter2 = 0; counter2 < strLen;
+ counter2++) {
+ temp =
+ memBuffer[(offset +
+ counter2) % 4096];
+ if (temp != 0 || transp == 0)
+ *destPtr = temp;
+ destPtr++;
+
+ curWidth++;
+ if (curWidth >= srcWidth) {
+ curWidth = 0;
+ linePtr += destDesc->width;
+ destPtr = linePtr;
+ curHeight++;
+ if (curHeight >= srcHeight) {
+ delete[] memBuffer;
+ return 1;
+ }
+ }
+ sourceLeft--;
+ if (sourceLeft == 0) {
+ delete[] memBuffer;
+ return 1;
+ }
+ memBuffer[bufPos] = temp;
+ bufPos++;
+ bufPos %= 4096;
+ }
+ }
+ }
+ }
+ delete[] memBuffer;
+ return 1;
+}
+
+void Video::setHandlers() { _vm->_global->_setAllPalette = 1; }
+
+} // End of namespace Gob
diff --git a/engines/gob/video.h b/engines/gob/video.h
new file mode 100644
index 0000000000..58c6bdb3c2
--- /dev/null
+++ b/engines/gob/video.h
@@ -0,0 +1,141 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 Ivan Dubrov
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#ifndef GOB_VIDEO_H
+#define GOB_VIDEO_H
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "gob/gob.h"
+
+namespace Gob {
+
+#define VID_SET_CURSOR(val) { _AH = 1; _CX = (val); geninterrupt(0x10); }
+#define VID_RESTORE_MODE { _AX = 3; geninterrupt(0x10); }
+
+#define TEXT_VID_SEG 0xB800
+#define TEXT_VID_OFF 0
+#define TEXT_COL_COUNT 80
+#define TEXT_ROW_COUNT 25
+
+extern int16 setAllPalette;
+
+class Video {
+public:
+ struct SurfaceDesc {
+ int16 width;
+ int16 height;
+ int8 reserved1;
+ int8 flag;
+ int16 vidMode;
+ byte *vidPtr;
+ int16 reserved2;
+ SurfaceDesc() : width(0), height(0), reserved1(0), flag(0),
+ vidMode(0), vidPtr(0), reserved2(0) {}
+ };
+
+ struct FontDesc {
+ char *dataPtr;
+ int8 itemWidth;
+ int8 itemHeight;
+ int8 startItem;
+ int8 endItem;
+ int8 itemSize;
+ int8 bitWidth;
+ void *extraData;
+ FontDesc() : dataPtr(0), itemWidth(0), itemHeight(0), startItem(0),
+ endItem(0), itemSize(0), bitWidth(0) {}
+ };
+
+#define GDR_VERSION 4
+
+#define PRIMARY_SURFACE 0x80
+#define RETURN_PRIMARY 0x01
+#define DISABLE_SPR_ALLOC 0x20
+
+#pragma START_PACK_STRUCTS
+
+ struct Color {
+ byte red;
+ byte green;
+ byte blue;
+ } GCC_PACK;
+
+#pragma END_PACK_STRUCTS
+
+ struct PalDesc {
+ Color *vgaPal;
+ int16 *unused1;
+ int16 *unused2;
+ PalDesc() : vgaPal(0), unused1(0), unused2(0) {}
+ };
+
+ Video(class GobEngine *vm);
+ int32 getRectSize(int16 width, int16 height, int16 flag, int16 mode);
+ SurfaceDesc *initSurfDesc(int16 vidMode, int16 width, int16 height, int16 flags);
+ void freeSurfDesc(SurfaceDesc * surfDesc);
+ int16 clampValue(int16 val, int16 max);
+ void drawSprite(SurfaceDesc * source, SurfaceDesc * dest, int16 left,
+ int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp);
+ void fillRect(SurfaceDesc * dest, int16 left, int16 top, int16 right, int16 bottom,
+ int16 color);
+ void drawLine(SurfaceDesc * dest, int16 x0, int16 y0, int16 x1, int16 y1,
+ int16 color);
+ void putPixel(int16 x, int16 y, int16 color, SurfaceDesc * dest);
+ void drawLetter(unsigned char item, int16 x, int16 y, FontDesc * fontDesc, int16 color1,
+ int16 color2, int16 transp, SurfaceDesc * dest);
+ void clearSurf(SurfaceDesc * dest);
+ void drawPackedSprite(byte *sprBuf, int16 width, int16 height, int16 x, int16 y,
+ int16 transp, SurfaceDesc * dest);
+ void setPalElem(int16 index, char red, char green, char blue, int16 unused,
+ int16 vidMode);
+ void setPalette(PalDesc * palDesc);
+ void setFullPalette(PalDesc * palDesc);
+ void initPrimary(int16 mode);
+ char spriteUncompressor(byte *sprBuf, int16 srcWidth, int16 srcHeight, int16 x,
+ int16 y, int16 transp, SurfaceDesc * destDesc);
+ void waitRetrace(int16);
+ void freeDriver(void);
+ void setHandlers();
+
+protected:
+ class VideoDriver *_videoDriver;
+ GobEngine *_vm;
+
+ char initDriver(int16 vidMode);
+};
+
+class VideoDriver {
+public:
+ VideoDriver() {}
+ virtual ~VideoDriver() {}
+ virtual void drawSprite(Video::SurfaceDesc *source, Video::SurfaceDesc *dest, int16 left, int16 top, int16 right, int16 bottom, int16 x, int16 y, int16 transp) = 0;
+ virtual void fillRect(Video::SurfaceDesc *dest, int16 left, int16 top, int16 right, int16 bottom, byte color) = 0;
+ virtual void putPixel(int16 x, int16 y, byte color, Video::SurfaceDesc *dest) = 0;
+ virtual void drawLetter(unsigned char item, int16 x, int16 y, Video::FontDesc *fontDesc, byte color1, byte color2, byte transp, Video::SurfaceDesc *dest) = 0;
+ virtual void drawLine(Video::SurfaceDesc *dest, int16 x0, int16 y0, int16 x1, int16 y1, byte color) = 0;
+ virtual void drawPackedSprite(byte *sprBuf, int16 width, int16 height, int16 x, int16 y, byte transp, Video::SurfaceDesc *dest) = 0;
+};
+
+} // End of namespace Gob
+
+#endif
diff --git a/engines/kyra/animator.cpp b/engines/kyra/animator.cpp
new file mode 100644
index 0000000000..e5c5c431c8
--- /dev/null
+++ b/engines/kyra/animator.cpp
@@ -0,0 +1,691 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/animator.h"
+#include "kyra/sprites.h"
+
+#include "common/system.h"
+
+namespace Kyra {
+ScreenAnimator::ScreenAnimator(KyraEngine *vm, OSystem *system) {
+ _vm = vm;
+ _screen = vm->screen();
+ _initOk = false;
+ _updateScreen = false;
+ _system = system;
+ _screenObjects = _actors = _items = _sprites = _objectQueue = 0;
+ _noDrawShapesFlag = 0;
+}
+
+ScreenAnimator::~ScreenAnimator() {
+ close();
+}
+
+void ScreenAnimator::init(int actors_, int items_, int sprites_) {
+ debug(9, "ScreenAnimator::init(%d, %d, %d)", actors_, items_, sprites_);
+ _screenObjects = new AnimObject[actors_ + items_ + sprites_];
+ assert(_screenObjects);
+ memset(_screenObjects, 0, sizeof(AnimObject) * (actors_ + items_ + sprites_));
+ _actors = _screenObjects;
+ _sprites = &_screenObjects[actors_];
+ _items = &_screenObjects[actors_ + items_];
+ _brandonDrawFrame = 113;
+
+ _initOk = true;
+}
+
+void ScreenAnimator::close() {
+ debug(9, "ScreenAnimator::close()");
+ if (_initOk) {
+ _initOk = false;
+ delete [] _screenObjects;
+ _screenObjects = _actors = _items = _sprites = _objectQueue = 0;
+ }
+}
+
+void ScreenAnimator::initAnimStateList() {
+ AnimObject *animStates = _screenObjects;
+ animStates[0].index = 0;
+ animStates[0].active = 1;
+ animStates[0].flags = 0x800;
+ animStates[0].background = _vm->_shapes[2];
+ animStates[0].rectSize = _screen->getRectSize(4, 48);
+ animStates[0].width = 4;
+ animStates[0].height = 48;
+ animStates[0].width2 = 4;
+ animStates[0].height2 = 3;
+
+ for (int i = 1; i <= 4; ++i) {
+ animStates[i].index = i;
+ animStates[i].active = 0;
+ animStates[i].flags = 0x800;
+ animStates[i].background = _vm->_shapes[3];
+ animStates[i].rectSize = _screen->getRectSize(4, 64);
+ animStates[i].width = 4;
+ animStates[i].height = 48;
+ animStates[i].width2 = 4;
+ animStates[i].height2 = 3;
+ }
+
+ for (int i = 5; i < 16; ++i) {
+ animStates[i].index = i;
+ animStates[i].active = 0;
+ animStates[i].flags = 0;
+ }
+
+ for (int i = 16; i < 28; ++i) {
+ animStates[i].index = i;
+ animStates[i].flags = 0;
+ animStates[i].background = _vm->_shapes[349+i];
+ animStates[i].rectSize = _screen->getRectSize(3, 24);
+ animStates[i].width = 3;
+ animStates[i].height = 16;
+ animStates[i].width2 = 0;
+ animStates[i].height2 = 0;
+ }
+}
+
+void ScreenAnimator::preserveAllBackgrounds() {
+ debug(9, "ScreenAnimator::preserveAllBackgrounds()");
+ uint8 curPage = _screen->_curPage;
+ _screen->_curPage = 2;
+
+ AnimObject *curObject = _objectQueue;
+ while (curObject) {
+ if (curObject->active && !curObject->disable) {
+ preserveOrRestoreBackground(curObject, false);
+ curObject->bkgdChangeFlag = 0;
+ }
+ curObject = curObject->nextAnimObject;
+ }
+ _screen->_curPage = curPage;
+}
+
+void ScreenAnimator::flagAllObjectsForBkgdChange() {
+ debug(9, "ScreenAnimator::flagAllObjectsForBkgdChange()");
+ AnimObject *curObject = _objectQueue;
+ while (curObject) {
+ curObject->bkgdChangeFlag = 1;
+ curObject = curObject->nextAnimObject;
+ }
+}
+
+void ScreenAnimator::flagAllObjectsForRefresh() {
+ debug(9, "ScreenAnimator::flagAllObjectsForRefresh()");
+ AnimObject *curObject = _objectQueue;
+ while (curObject) {
+ curObject->refreshFlag = 1;
+ curObject = curObject->nextAnimObject;
+ }
+}
+
+void ScreenAnimator::restoreAllObjectBackgrounds() {
+ debug(9, "ScreenAnimator::restoreAllObjectBackground()");
+ AnimObject *curObject = _objectQueue;
+ _screen->_curPage = 2;
+
+ while (curObject) {
+ if (curObject->active && !curObject->disable) {
+ preserveOrRestoreBackground(curObject, true);
+ curObject->x2 = curObject->x1;
+ curObject->y2 = curObject->y1;
+ }
+ curObject = curObject->nextAnimObject;
+ }
+
+ _screen->_curPage = 0;
+}
+
+void ScreenAnimator::preserveAnyChangedBackgrounds() {
+ debug(9, "ScreenAnimator::preserveAnyChangedBackgrounds()");
+ AnimObject *curObject = _objectQueue;
+ _screen->_curPage = 2;
+
+ while (curObject) {
+ if (curObject->active && !curObject->disable && curObject->bkgdChangeFlag) {
+ preserveOrRestoreBackground(curObject, false);
+ curObject->bkgdChangeFlag = 0;
+ }
+ curObject = curObject->nextAnimObject;
+ }
+
+ _screen->_curPage = 0;
+}
+
+void ScreenAnimator::preserveOrRestoreBackground(AnimObject *obj, bool restore) {
+ debug(9, "ScreenAnimator::preserveOrRestoreBackground(0x%X, restore)", obj, restore);
+ int x = 0, y = 0, width = obj->width << 3, height = obj->height;
+
+ if (restore) {
+ x = obj->x2;
+ y = obj->y2;
+ } else {
+ x = obj->x1;
+ y = obj->y1;
+ }
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+
+ int temp;
+
+ temp = x + width;
+ if (temp >= 319) {
+ x = 319 - width;
+ }
+ temp = y + height;
+ if (temp >= 136) {
+ y = 136 - height;
+ }
+
+ if (restore) {
+ _screen->copyBlockToPage(_screen->_curPage, x, y, width, height, obj->background);
+ } else {
+ _screen->copyRegionToBuffer(_screen->_curPage, x, y, width, height, obj->background);
+ }
+}
+
+void ScreenAnimator::prepDrawAllObjects() {
+ debug(9, "ScreenAnimator::prepDrawAllObjects()");
+ AnimObject *curObject = _objectQueue;
+ int drawPage = 2;
+ int flagUnk1 = 0, flagUnk2 = 0, flagUnk3 = 0;
+ if (_noDrawShapesFlag)
+ return;
+ if (_vm->_brandonStatusBit & 0x20)
+ flagUnk1 = 0x200;
+ if (_vm->_brandonStatusBit & 0x40)
+ flagUnk2 = 0x4000;
+
+ while (curObject) {
+ if (curObject->active) {
+ int xpos = curObject->x1;
+ int ypos = curObject->y1;
+
+ int drawLayer = 0;
+ if (!(curObject->flags & 0x800)) {
+ drawLayer = 7;
+ } else if (curObject->disable) {
+ drawLayer = 0;
+ } else {
+ drawLayer = _vm->_sprites->getDrawLayer(curObject->drawY);
+ }
+
+ // talking head functionallity
+ if (_vm->_talkingCharNum != -1) {
+ const int16 baseAnimFrameTable1[] = { 0x11, 0x35, 0x59, 0x00, 0x00, 0x00 };
+ const int16 baseAnimFrameTable2[] = { 0x15, 0x39, 0x5D, 0x00, 0x00, 0x00 };
+ const int8 xOffsetTable1[] = { 2, 4, 0, 5, 2, 0, 0, 0 };
+ const int8 xOffsetTable2[] = { 6, 4, 8, 3, 6, 0, 0, 0 };
+ const int8 yOffsetTable1[] = { 0, 8, 1, 1, 0, 0, 0, 0 };
+ const int8 yOffsetTable2[] = { 0, 8, 1, 1, 0, 0, 0, 0 };
+ if (curObject->index == 0 || curObject->index <= 4) {
+ int shapesIndex = 0;
+ if (curObject->index == _vm->_charSayUnk3) {
+ shapesIndex = _vm->_currHeadShape + baseAnimFrameTable1[curObject->index];
+ } else {
+ shapesIndex = baseAnimFrameTable2[curObject->index];
+ int temp2 = 0;
+ if (curObject->index == 2) {
+ if (_vm->_characterList[2].sceneId == 77 || _vm->_characterList[2].sceneId == 86) {
+ temp2 = 1;
+ } else {
+ temp2 = 0;
+ }
+ } else {
+ temp2 = 1;
+ }
+
+ if (!temp2) {
+ shapesIndex = -1;
+ }
+ }
+
+ xpos = curObject->x1;
+ ypos = curObject->y1;
+
+ int tempX = 0, tempY = 0;
+ if (curObject->flags & 0x1) {
+ tempX = (xOffsetTable1[curObject->index] * _brandonScaleX) >> 8;
+ tempY = yOffsetTable1[curObject->index];
+ } else {
+ tempX = (xOffsetTable2[curObject->index] * _brandonScaleX) >> 8;
+ tempY = yOffsetTable2[curObject->index];
+ }
+ tempY = (tempY * _brandonScaleY) >> 8;
+ xpos += tempX;
+ ypos += tempY;
+
+ if (_vm->_scaleMode && _brandonScaleX != 256) {
+ ++xpos;
+ }
+
+ if (curObject->index == 0 && shapesIndex != -1) {
+ if (!(_vm->_brandonStatusBit & 2)) {
+ flagUnk3 = 0x100;
+ if ((flagUnk1 & 0x200) || (flagUnk2 & 0x4000)) {
+ flagUnk3 = 0;
+ }
+
+ int tempFlags = 0;
+ if (flagUnk3 & 0x100) {
+ tempFlags = curObject->flags & 1;
+ tempFlags |= 0x800 | flagUnk1 | 0x100;
+ }
+
+ if (!(flagUnk3 & 0x100) && (flagUnk2 & 0x4000)) {
+ tempFlags = curObject->flags & 1;
+ tempFlags |= 0x900 | flagUnk1 | 0x4000;
+ _screen->drawShape(drawPage, _vm->_shapes[4+shapesIndex], xpos, ypos, 2, tempFlags | 4, _vm->_brandonPoisonFlagsGFX, int(1), int(_vm->_brandonInvFlag), drawLayer, _brandonScaleX, _brandonScaleY);
+ } else {
+ if (!(flagUnk2 & 0x4000)) {
+ tempFlags = curObject->flags & 1;
+ tempFlags |= 0x900 | flagUnk1;
+ }
+
+ _screen->drawShape(drawPage, _vm->_shapes[4+shapesIndex], xpos, ypos, 2, tempFlags | 4, _vm->_brandonPoisonFlagsGFX, int(1), drawLayer, _brandonScaleX, _brandonScaleY);
+ }
+ }
+ } else {
+ if (shapesIndex != -1) {
+ int tempFlags = 0;
+ if (curObject->flags & 1) {
+ tempFlags = 1;
+ }
+ _screen->drawShape(drawPage, _vm->_shapes[4+shapesIndex], xpos, ypos, 2, tempFlags | 0x800, drawLayer);
+ }
+ }
+ }
+ }
+
+ xpos = curObject->x1;
+ ypos = curObject->y1;
+
+ curObject->flags |= 0x800;
+ if (curObject->index == 0) {
+ flagUnk3 = 0x100;
+
+ if (flagUnk1 & 0x200 || flagUnk2 & 0x4000) {
+ flagUnk3 = 0;
+ }
+
+ if (_vm->_brandonStatusBit & 2) {
+ curObject->flags &= 0xFFFFFFFE;
+ }
+
+ if (!_vm->_scaleMode) {
+ if (flagUnk3 & 0x100) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x100, (uint8*)_vm->_brandonPoisonFlagsGFX, int(1), drawLayer);
+ } else if (flagUnk3 & 0x4000) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4000, int(_vm->_brandonInvFlag), drawLayer);
+ } else {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1, drawLayer);
+ }
+ } else {
+ if (flagUnk3 & 0x100) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x104, (uint8*)_vm->_brandonPoisonFlagsGFX, int(1), drawLayer, _brandonScaleX, _brandonScaleY);
+ } else if (flagUnk3 & 0x4000) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4004, int(_vm->_brandonInvFlag), drawLayer, _brandonScaleX, _brandonScaleY);
+ } else {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4, drawLayer, _brandonScaleX, _brandonScaleY);
+ }
+ }
+ } else {
+ if (curObject->index >= 16 && curObject->index <= 27) {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | 4, drawLayer, (int)_vm->_scaleTable[curObject->drawY], (int)_vm->_scaleTable[curObject->drawY]);
+ } else {
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags, drawLayer);
+ }
+ }
+ }
+ curObject = curObject->nextAnimObject;
+ }
+}
+
+void ScreenAnimator::copyChangedObjectsForward(int refreshFlag) {
+ debug(9, "ScreenAnimator::copyChangedObjectsForward(%d)", refreshFlag);
+ AnimObject *curObject = _objectQueue;
+
+ while (curObject) {
+ if (curObject->active) {
+ if (curObject->refreshFlag || refreshFlag) {
+ int xpos = 0, ypos = 0, width = 0, height = 0;
+ xpos = curObject->x1 - (curObject->width2+1);
+ ypos = curObject->y1 - curObject->height2;
+ width = (curObject->width + ((curObject->width2)>>3)+1)<<3;
+ height = curObject->height + curObject->height2*2;
+
+ if (xpos < 8) {
+ xpos = 8;
+ } else if (xpos + width > 312) {
+ width = 312 - xpos;
+ }
+
+ if (ypos < 8) {
+ ypos = 8;
+ } else if (ypos + height > 136) {
+ height = 136 - ypos;
+ }
+
+ _screen->copyRegion(xpos, ypos, xpos, ypos, width, height, 2, 0, Screen::CR_CLIPPED);
+ curObject->refreshFlag = 0;
+ _updateScreen = true;
+ }
+ }
+ curObject = curObject->nextAnimObject;
+ }
+ if (_updateScreen) {
+ _screen->updateScreen();
+ _updateScreen = false;
+ }
+}
+
+void ScreenAnimator::updateAllObjectShapes() {
+ debug(9, "ScreenAnimator::updateAllObjectShapes()");
+ restoreAllObjectBackgrounds();
+ preserveAnyChangedBackgrounds();
+ prepDrawAllObjects();
+ copyChangedObjectsForward(0);
+}
+
+void ScreenAnimator::animRemoveGameItem(int index) {
+ debug(9, "ScreenAnimator::animRemoveGameItem(%d)", index);
+ restoreAllObjectBackgrounds();
+
+ AnimObject *animObj = &_items[index];
+ animObj->sceneAnimPtr = 0;
+ animObj->animFrameNumber = -1;
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ updateAllObjectShapes();
+ animObj->active = 0;
+
+ objectRemoveQueue(_objectQueue, animObj);
+}
+
+void ScreenAnimator::animAddGameItem(int index, uint16 sceneId) {
+ debug(9, "ScreenAnimator::animRemoveGameItem(%d, %d)", index, sceneId);
+ restoreAllObjectBackgrounds();
+ assert(sceneId < _vm->_roomTableSize);
+ Room *currentRoom = &_vm->_roomTable[sceneId];
+ AnimObject *animObj = &_items[index];
+ animObj->active = 1;
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ animObj->drawY = currentRoom->itemsYPos[index];
+ animObj->sceneAnimPtr = _vm->_shapes[220+currentRoom->itemsTable[index]];
+ animObj->animFrameNumber = -1;
+ animObj->x1 = currentRoom->itemsXPos[index];
+ animObj->y1 = currentRoom->itemsYPos[index];
+ animObj->x1 -= fetchAnimWidth(animObj->sceneAnimPtr, _vm->_scaleTable[animObj->drawY]) >> 1;
+ animObj->y1 -= fetchAnimHeight(animObj->sceneAnimPtr, _vm->_scaleTable[animObj->drawY]);
+ animObj->x2 = animObj->x1;
+ animObj->y2 = animObj->y1;
+ animObj->width2 = 0;
+ animObj->height2 = 0;
+ _objectQueue = objectQueue(_objectQueue, animObj);
+ preserveAnyChangedBackgrounds();
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+}
+
+void ScreenAnimator::animAddNPC(int character) {
+ debug(9, "ScreenAnimator::animAddNPC(%d)", character);
+ restoreAllObjectBackgrounds();
+ AnimObject *animObj = &_actors[character];
+ const Character *ch = &_vm->_characterList[character];
+
+ animObj->active = 1;
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ animObj->drawY = ch->y1;
+ animObj->sceneAnimPtr = _vm->_shapes[4+ch->currentAnimFrame];
+ animObj->x1 = animObj->x2 = ch->x1 + _vm->_defaultShapeTable[ch->currentAnimFrame-7].xOffset;
+ animObj->y1 = animObj->y2 = ch->y1 + _vm->_defaultShapeTable[ch->currentAnimFrame-7].yOffset;
+ if (ch->facing >= 1 && ch->facing <= 3) {
+ animObj->flags |= 1;
+ } else if (ch->facing >= 5 && ch->facing <= 7) {
+ animObj->flags &= 0xFFFFFFFE;
+ }
+ _objectQueue = objectQueue(_objectQueue, animObj);
+ preserveAnyChangedBackgrounds();
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+}
+
+AnimObject *ScreenAnimator::objectRemoveQueue(AnimObject *queue, AnimObject *rem) {
+ debug(9, "ScreenAnimator::objectRemoveQueue(0x%X, 0x%X)", queue, rem);
+ AnimObject *cur = queue;
+ AnimObject *prev = queue;
+
+ while (cur != rem && cur) {
+ AnimObject *temp = cur->nextAnimObject;
+ if (!temp)
+ break;
+ prev = cur;
+ cur = temp;
+ }
+
+ if (cur == queue) {
+ if (!cur)
+ return 0;
+ return cur->nextAnimObject;
+ }
+
+ if (!cur->nextAnimObject) {
+ if (cur == rem) {
+ if (!prev) {
+ return 0;
+ } else {
+ prev->nextAnimObject = 0;
+ }
+ }
+ } else {
+ if (cur == rem) {
+ prev->nextAnimObject = rem->nextAnimObject;
+ }
+ }
+
+ return queue;
+}
+
+AnimObject *ScreenAnimator::objectAddHead(AnimObject *queue, AnimObject *head) {
+ debug(9, "ScreenAnimator::objectAddHead(0x%X, 0x%X)", queue, head);
+ head->nextAnimObject = queue;
+ return head;
+}
+
+AnimObject *ScreenAnimator::objectQueue(AnimObject *queue, AnimObject *add) {
+ debug(9, "ScreenAnimator::objectQueue(0x%X, 0x%X)", queue, add);
+ if (add->drawY <= queue->drawY || !queue) {
+ add->nextAnimObject = queue;
+ return add;
+ }
+ AnimObject *cur = queue;
+ AnimObject *prev = queue;
+ while (add->drawY > cur->drawY) {
+ AnimObject *temp = cur->nextAnimObject;
+ if (!temp)
+ break;
+ prev = cur;
+ cur = temp;
+ }
+
+ if (add->drawY <= cur->drawY) {
+ prev->nextAnimObject = add;
+ add->nextAnimObject = cur;
+ } else {
+ cur->nextAnimObject = add;
+ add->nextAnimObject = 0;
+ }
+ return queue;
+}
+
+void ScreenAnimator::addObjectToQueue(AnimObject *object) {
+ debug(9, "ScreenAnimator::addObjectToQueue(0x%X)", object);
+ if (!_objectQueue) {
+ _objectQueue = objectAddHead(0, object);
+ } else {
+ _objectQueue = objectQueue(_objectQueue, object);
+ }
+}
+
+void ScreenAnimator::refreshObject(AnimObject *object) {
+ debug(9, "ScreenAnimator::refreshObject(0x%X)", object);
+ _objectQueue = objectRemoveQueue(_objectQueue, object);
+ if (_objectQueue) {
+ _objectQueue = objectQueue(_objectQueue, object);
+ } else {
+ _objectQueue = objectAddHead(0, object);
+ }
+}
+
+void ScreenAnimator::makeBrandonFaceMouse() {
+ debug(9, "ScreenAnimator::makeBrandonFaceMouse()");
+ if (_vm->mouseX() >= _vm->_currentCharacter->x1) {
+ _vm->_currentCharacter->facing = 3;
+ } else {
+ _vm->_currentCharacter->facing = 5;
+ }
+ animRefreshNPC(0);
+ updateAllObjectShapes();
+}
+
+int16 ScreenAnimator::fetchAnimWidth(const uint8 *shape, int16 mult) {
+ debug(9, "ScreenAnimator::fetchAnimWidth(0x%X, %d)", shape, mult);
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+ return (((int16)READ_LE_UINT16((shape+3))) * mult) >> 8;
+}
+
+int16 ScreenAnimator::fetchAnimHeight(const uint8 *shape, int16 mult) {
+ debug(9, "ScreenAnimator::fetchAnimHeight(0x%X, %d)", shape, mult);
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+ return (int16)(((int8)*(shape+2)) * mult) >> 8;
+}
+
+void ScreenAnimator::setBrandonAnimSeqSize(int width, int height) {
+ debug(9, "ScreenAnimator::setBrandonAnimSeqSize(%d, %d)", width, height);
+ restoreAllObjectBackgrounds();
+ _brandonAnimSeqSizeWidth = _actors[0].width;
+ _brandonAnimSeqSizeHeight = _actors[0].height;
+ _actors[0].width = width + 1;
+ _actors[0].height = height;
+ preserveAllBackgrounds();
+}
+
+void ScreenAnimator::resetBrandonAnimSeqSize() {
+ debug(9, "ScreenAnimator::resetBrandonAnimSeqSize()");
+ restoreAllObjectBackgrounds();
+ _actors[0].width = _brandonAnimSeqSizeWidth;
+ _actors[0].height = _brandonAnimSeqSizeHeight;
+ preserveAllBackgrounds();
+}
+
+void ScreenAnimator::animRefreshNPC(int character) {
+ debug(9, "ScreenAnimator::animRefreshNPC(%d)", character);
+ AnimObject *animObj = &_actors[character];
+ Character *ch = &_vm->characterList()[character];
+
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ int facing = ch->facing;
+ if (facing >= 1 && facing <= 3) {
+ animObj->flags |= 1;
+ } else if (facing >= 5 && facing <= 7) {
+ animObj->flags &= 0xFFFFFFFE;
+ }
+
+ animObj->drawY = ch->y1;
+ animObj->sceneAnimPtr = _vm->shapes()[4+ch->currentAnimFrame];
+ animObj->animFrameNumber = ch->currentAnimFrame;
+ if (character == 0) {
+ if (_vm->brandonStatus() & 10) {
+ animObj->animFrameNumber = 88;
+ ch->currentAnimFrame = 88;
+ }
+ if (_vm->brandonStatus() & 2) {
+ animObj->animFrameNumber = _brandonDrawFrame;
+ ch->currentAnimFrame = _brandonDrawFrame;
+ animObj->sceneAnimPtr = _vm->shapes()[4+_brandonDrawFrame];
+ if (_vm->_brandonStatusBit0x02Flag) {
+ ++_brandonDrawFrame;
+ if (_brandonDrawFrame >= 122)
+ _brandonDrawFrame = 113;
+ _vm->_brandonStatusBit0x02Flag = 0;
+ }
+ }
+ }
+
+ int xOffset = _vm->_defaultShapeTable[ch->currentAnimFrame-7].xOffset;
+ int yOffset = _vm->_defaultShapeTable[ch->currentAnimFrame-7].yOffset;
+
+ if (_vm->_scaleMode) {
+ animObj->x1 = ch->x1;
+ animObj->y1 = ch->y1;
+
+ _brandonScaleX = _vm->_scaleTable[ch->y1];
+ _brandonScaleY = _vm->_scaleTable[ch->y1];
+
+ animObj->x1 += (_brandonScaleX * xOffset) >> 8;
+ animObj->y1 += (_brandonScaleY * yOffset) >> 8;
+ } else {
+ animObj->x1 = ch->x1 + xOffset;
+ animObj->y1 = ch->y1 + yOffset;
+ }
+ animObj->width2 = 4;
+ animObj->height2 = 3;
+
+ refreshObject(animObj);
+}
+
+void ScreenAnimator::setCharacterDefaultFrame(int character) {
+ debug(9, "ScreenAnimator::setCharacterDefaultFrame()");
+ static uint16 initFrameTable[] = {
+ 7, 41, 77, 0, 0
+ };
+ assert(character < ARRAYSIZE(initFrameTable));
+ Character *edit = &_vm->characterList()[character];
+ edit->sceneId = 0xFFFF;
+ edit->facing = 0;
+ edit->currentAnimFrame = initFrameTable[character];
+ // edit->unk6 = 1;
+}
+
+void ScreenAnimator::setCharactersHeight() {
+ debug(9, "ScreenAnimator::setCharactersHeight()");
+ static int8 initHeightTable[] = {
+ 48, 40, 48, 47, 56,
+ 44, 42, 47, 38, 35,
+ 40
+ };
+ for (int i = 0; i < 11; ++i) {
+ _vm->characterList()[i].height = initHeightTable[i];
+ }
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/animator.h b/engines/kyra/animator.h
new file mode 100644
index 0000000000..ecc80d1819
--- /dev/null
+++ b/engines/kyra/animator.h
@@ -0,0 +1,126 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef KYRAANIMATOR_H
+#define KYRAANIMATOR_H
+
+namespace Kyra {
+class KyraEngine;
+class Screen;
+
+struct AnimObject {
+ uint8 index;
+ uint32 active;
+ uint32 refreshFlag;
+ uint32 bkgdChangeFlag;
+ bool disable;
+ uint32 flags;
+ int16 drawY;
+ uint8 *sceneAnimPtr;
+ int16 animFrameNumber;
+ uint8 *background;
+ uint16 rectSize;
+ int16 x1, y1;
+ int16 x2, y2;
+ uint16 width;
+ uint16 height;
+ uint16 width2;
+ uint16 height2;
+ AnimObject *nextAnimObject;
+};
+
+class ScreenAnimator {
+public:
+ ScreenAnimator(KyraEngine *vm, OSystem *system);
+ virtual ~ScreenAnimator();
+
+ operator bool() const { return _initOk; }
+
+ void init(int actors, int items, int sprites);
+ void close();
+
+ AnimObject *objects() { return _screenObjects; }
+ AnimObject *actors() { return _actors; }
+ AnimObject *items() { return _items; }
+ AnimObject *sprites() { return _sprites; }
+
+ void initAnimStateList();
+ void preserveAllBackgrounds();
+ void flagAllObjectsForBkgdChange();
+ void flagAllObjectsForRefresh();
+ void restoreAllObjectBackgrounds();
+ void preserveAnyChangedBackgrounds();
+ virtual void prepDrawAllObjects();
+ void copyChangedObjectsForward(int refreshFlag);
+
+ void updateAllObjectShapes();
+ void animRemoveGameItem(int index);
+ void animAddGameItem(int index, uint16 sceneId);
+ void animAddNPC(int character);
+ void animRefreshNPC(int character);
+
+ void clearQueue() { _objectQueue = 0; }
+ void addObjectToQueue(AnimObject *object);
+ void refreshObject(AnimObject *object);
+
+ void makeBrandonFaceMouse();
+ void setBrandonAnimSeqSize(int width, int height);
+ void resetBrandonAnimSeqSize();
+ void setCharacterDefaultFrame(int character);
+ void setCharactersHeight();
+
+ int16 fetchAnimWidth(const uint8 *shape, int16 mult);
+ int16 fetchAnimHeight(const uint8 *shape, int16 mult);
+
+ int _noDrawShapesFlag;
+ bool _updateScreen;
+ uint16 _brandonDrawFrame;
+ int _brandonScaleX;
+ int _brandonScaleY;
+
+protected:
+ KyraEngine *_vm;
+ Screen *_screen;
+ OSystem *_system;
+ bool _initOk;
+
+ AnimObject *_screenObjects;
+
+ AnimObject *_actors;
+ AnimObject *_items;
+ AnimObject *_sprites;
+
+ AnimObject *objectRemoveQueue(AnimObject *queue, AnimObject *rem);
+ AnimObject *objectAddHead(AnimObject *queue, AnimObject *head);
+ AnimObject *objectQueue(AnimObject *queue, AnimObject *add);
+
+ void preserveOrRestoreBackground(AnimObject *obj, bool restore);
+
+ AnimObject *_objectQueue;
+
+ int _brandonAnimSeqSizeWidth;
+ int _brandonAnimSeqSizeHeight;
+
+};
+} // end of namespace Kyra
+
+#endif
diff --git a/engines/kyra/debugger.cpp b/engines/kyra/debugger.cpp
new file mode 100644
index 0000000000..b78056e3ad
--- /dev/null
+++ b/engines/kyra/debugger.cpp
@@ -0,0 +1,206 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/config-manager.h"
+#include "common/debugger.cpp"
+#include "kyra/debugger.h"
+#include "kyra/kyra.h"
+#include "kyra/screen.h"
+
+namespace Kyra {
+
+Debugger::Debugger(KyraEngine *vm)
+ : Common::Debugger<Debugger>() {
+ _vm = vm;
+
+ DCmd_Register("continue", &Debugger::cmd_exit);
+ DCmd_Register("exit", &Debugger::cmd_exit);
+ DCmd_Register("help", &Debugger::cmd_help);
+ DCmd_Register("quit", &Debugger::cmd_exit);
+ DCmd_Register("enter", &Debugger::cmd_enterRoom);
+ DCmd_Register("rooms", &Debugger::cmd_listRooms);
+ DCmd_Register("flags", &Debugger::cmd_listFlags);
+ DCmd_Register("toggleflag", &Debugger::cmd_toggleFlag);
+ DCmd_Register("queryflag", &Debugger::cmd_queryFlag);
+ DCmd_Register("timers", &Debugger::cmd_listTimers);
+ DCmd_Register("settimercountdown", &Debugger::cmd_setTimerCountdown);
+ DCmd_Register("give", &Debugger::cmd_giveItem);
+}
+
+void Debugger::preEnter() {
+ //_vm->midi.pause(1);
+}
+
+void Debugger::postEnter() {
+ //_vm->midi.pause(0);
+}
+
+bool Debugger::cmd_enterRoom(int argc, const char **argv) {
+ uint direction = 0;
+ if (argc > 1) {
+ int room = atoi(argv[1]);
+
+ // game will crash if entering a non-existent room
+ if (room >= _vm->_roomTableSize) {
+ DebugPrintf("room number must be any value between (including) 0 and %d\n", _vm->_roomTableSize-1);
+ return true;
+ }
+
+ if (argc > 2) {
+ direction = atoi(argv[2]);
+ } else {
+ if (_vm->_roomTable[room].northExit != 0xff)
+ direction = 3;
+ else if (_vm->_roomTable[room].eastExit != 0xff)
+ direction = 4;
+ else if (_vm->_roomTable[room].southExit != 0xff)
+ direction = 1;
+ else if (_vm->_roomTable[room].westExit != 0xff)
+ direction = 2;
+ }
+
+ // Dirty way of hiding the debug console while the room entry scripts are running,
+ // otherwise the graphics didn't update.
+ _vm->_system->hideOverlay();
+ _vm->_currentCharacter->facing = direction;
+
+ _vm->enterNewScene(room, _vm->_currentCharacter->facing, 0, 0, 1);
+ _vm->_system->showOverlay();
+ _vm->_screen->_mouseLockCount = 0;
+
+ _detach_now = true;
+ return false;
+ }
+
+ DebugPrintf("Syntax: room <roomnum> <direction>\n");
+ return true;
+}
+
+bool Debugger::cmd_exit(int argc, const char **argv) {
+ _detach_now = true;
+ return false;
+}
+
+bool Debugger::cmd_help(int argc, const char **argv) {
+ // console normally has 39 line width
+ // wrap around nicely
+ int width = 0, size, i;
+
+ DebugPrintf("Commands are:\n");
+ for (i = 0 ; i < _dcmd_count ; i++) {
+ size = strlen(_dcmds[i].name) + 1;
+
+ if ((width + size) >= 39) {
+ DebugPrintf("\n");
+ width = size;
+ } else
+ width += size;
+
+ DebugPrintf("%s ", _dcmds[i].name);
+ }
+ DebugPrintf("\n");
+ return true;
+}
+
+bool Debugger::cmd_listRooms(int argc, const char **argv) {
+ for (int i = 0; i < _vm->_roomTableSize; i++) {
+ DebugPrintf("%-3i: %-10s", i, _vm->_roomFilenameTable[_vm->_roomTable[i].nameIndex]);
+ if (!(i % 8))
+ DebugPrintf("\n");
+ }
+ DebugPrintf("\n");
+ DebugPrintf("Current room: %i\n", _vm->_currentRoom);
+ return true;
+}
+
+bool Debugger::cmd_listFlags(int argc, const char **argv) {
+ for (int i = 0; i < (int)sizeof(_vm->_flagsTable)*8; i++) {
+ DebugPrintf("(%-3i): %-5i", i, _vm->queryGameFlag(i));
+ if (!(i % 10))
+ DebugPrintf("\n");
+ }
+ DebugPrintf("\n");
+ return true;
+}
+
+bool Debugger::cmd_toggleFlag(int argc, const char **argv) {
+ if (argc > 1) {
+ uint flag = atoi(argv[1]);
+ if (_vm->queryGameFlag(flag))
+ _vm->resetGameFlag(flag);
+ else
+ _vm->setGameFlag(flag);
+ DebugPrintf("Flag %i is now %i\n", flag, _vm->queryGameFlag(flag));
+ } else
+ DebugPrintf("Syntax: toggleflag <flag>\n");
+
+ return true;
+}
+
+bool Debugger::cmd_queryFlag(int argc, const char **argv) {
+ if (argc > 1) {
+ uint flag = atoi(argv[1]);
+ DebugPrintf("Flag %i is %i\n", flag, _vm->queryGameFlag(flag));
+ } else
+ DebugPrintf("Syntax: queryflag <flag>\n");
+
+ return true;
+}
+
+bool Debugger::cmd_listTimers(int argc, const char **argv) {
+ for (int i = 0; i < ARRAYSIZE(_vm->_timers); i++)
+ DebugPrintf("Timer %-2i: Active: %-3s Countdown: %-6i\n", i, _vm->_timers[i].active ? "Yes" : "No", _vm->_timers[i].countdown);
+
+ return true;
+}
+
+bool Debugger::cmd_setTimerCountdown(int argc, const char **argv) {
+ if (argc > 2) {
+ uint timer = atoi(argv[1]);
+ uint countdown = atoi(argv[2]);
+ _vm->setTimerCountdown(timer, countdown);
+ DebugPrintf("Timer %i now has countdown %i\n", timer, _vm->_timers[timer].countdown);
+ } else
+ DebugPrintf("Syntax: settimercountdown <timer> <countdown>\n");
+
+ return true;
+}
+
+bool Debugger::cmd_giveItem(int argc, const char **argv) {
+ if (argc == 2) {
+ int item = atoi(argv[1]);
+
+ // Kyrandia 1 has only 108 items (-1 to 106), otherwise it will crash
+ if (item < -1 || item > 106) {
+ DebugPrintf("itemid must be any value between (including) -1 and 106\n");
+ return true;
+ }
+
+ _vm->setMouseItem(item);
+ _vm->_itemInHand = item;
+ } else
+ DebugPrintf("Syntax: give <itemid>\n");
+
+ return true;
+}
+} // End of namespace Kyra
diff --git a/engines/kyra/debugger.h b/engines/kyra/debugger.h
new file mode 100644
index 0000000000..69766f3fc0
--- /dev/null
+++ b/engines/kyra/debugger.h
@@ -0,0 +1,57 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef KYRA_DEBUGGER_H
+#define KYRA_DEBUGGER_H
+
+#include "common/debugger.h"
+
+namespace Kyra {
+
+class KyraEngine;
+
+class Debugger : public Common::Debugger<Debugger> {
+public:
+ Debugger(KyraEngine *vm);
+ virtual ~Debugger() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ
+
+protected:
+ KyraEngine *_vm;
+
+ virtual void preEnter();
+ virtual void postEnter();
+
+ bool cmd_exit(int argc, const char **argv);
+ bool cmd_help(int argc, const char **argv);
+ bool cmd_enterRoom(int argc, const char **argv);
+ bool cmd_listRooms(int argc, const char **argv);
+ bool cmd_listFlags(int argc, const char **argv);
+ bool cmd_toggleFlag(int argc, const char **argv);
+ bool cmd_queryFlag(int argc, const char **argv);
+ bool cmd_listTimers(int argc, const char **argv);
+ bool cmd_setTimerCountdown(int argc, const char **argv);
+ bool cmd_giveItem(int argc, const char **argv);
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/gui.cpp b/engines/kyra/gui.cpp
new file mode 100644
index 0000000000..a1deccdfcd
--- /dev/null
+++ b/engines/kyra/gui.cpp
@@ -0,0 +1,1020 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/script.h"
+#include "kyra/text.h"
+#include "kyra/animator.h"
+
+#include "common/savefile.h"
+#include "common/system.h"
+
+namespace Kyra {
+
+void KyraEngine::initMainButtonList() {
+ _buttonList = &_buttonData[0];
+ for (int i = 0; _buttonDataListPtr[i]; ++i) {
+ _buttonList = initButton(_buttonList, _buttonDataListPtr[i]);
+ }
+}
+
+Button *KyraEngine::initButton(Button *list, Button *newButton) {
+ if (!newButton)
+ return list;
+ if (!list)
+ return newButton;
+ Button *cur = list;
+ while (true) {
+ if (!cur->nextButton) {
+ break;
+ }
+ cur = cur->nextButton;
+ }
+ cur->nextButton = newButton;
+ return list;
+}
+
+int KyraEngine::buttonInventoryCallback(Button *caller) {
+ int itemOffset = caller->specialValue - 2;
+ uint8 inventoryItem = _currentCharacter->inventoryItems[itemOffset];
+ if (_itemInHand == -1) {
+ if (inventoryItem == 0xFF) {
+ snd_playSoundEffect(0x36);
+ return 0;
+ } else {
+ _screen->hideMouse();
+ _screen->fillRect(_itemPosX[itemOffset], _itemPosY[itemOffset], _itemPosX[itemOffset] + 15, _itemPosY[itemOffset] + 15, 12);
+ snd_playSoundEffect(0x35);
+ setMouseItem(inventoryItem);
+ updateSentenceCommand(_itemList[inventoryItem], _takenList[0], 179);
+ _itemInHand = inventoryItem;
+ _screen->showMouse();
+ _currentCharacter->inventoryItems[itemOffset] = 0xFF;
+ }
+ } else {
+ if (inventoryItem != 0xFF) {
+ snd_playSoundEffect(0x35);
+ _screen->hideMouse();
+ _screen->fillRect(_itemPosX[itemOffset], _itemPosY[itemOffset], _itemPosX[itemOffset] + 15, _itemPosY[itemOffset] + 15, 12);
+ _screen->drawShape(0, _shapes[220+_itemInHand], _itemPosX[itemOffset], _itemPosY[itemOffset], 0, 0);
+ setMouseItem(inventoryItem);
+ updateSentenceCommand(_itemList[inventoryItem], _takenList[1], 179);
+ _screen->showMouse();
+ _currentCharacter->inventoryItems[itemOffset] = _itemInHand;
+ _itemInHand = inventoryItem;
+ } else {
+ snd_playSoundEffect(0x32);
+ _screen->hideMouse();
+ _screen->drawShape(0, _shapes[220+_itemInHand], _itemPosX[itemOffset], _itemPosY[itemOffset], 0, 0);
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ updateSentenceCommand(_itemList[_itemInHand], _placedList[0], 179);
+ _screen->showMouse();
+ _currentCharacter->inventoryItems[itemOffset] = _itemInHand;
+ _itemInHand = -1;
+ }
+ }
+ _screen->updateScreen();
+ // XXX clearKyrandiaButtonIO
+ return 0;
+}
+
+int KyraEngine::buttonAmuletCallback(Button *caller) {
+ if (!(_deathHandler & 8))
+ return 1;
+ int jewel = caller->specialValue - 0x14;
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return 1;
+ }
+ if (!queryGameFlag(0x2D))
+ return 1;
+ if (_itemInHand != -1) {
+ assert(_putDownFirst);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2000);
+ }
+ characterSays(_putDownFirst[0], 0, -2);
+ return 1;
+ }
+ if (queryGameFlag(0xF1)) {
+ assert(_waitForAmulet);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2001);
+ }
+ characterSays(_waitForAmulet[0], 0, -2);
+ return 1;
+ }
+ if (!queryGameFlag(0x55+jewel)) {
+ assert(_blackJewel);
+ _animator->makeBrandonFaceMouse();
+ drawJewelPress(jewel, 1);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2002);
+ }
+ characterSays(_blackJewel[0], 0, -2);
+ return 1;
+ }
+ drawJewelPress(jewel, 0);
+ drawJewelsFadeOutStart();
+ drawJewelsFadeOutEnd(jewel);
+
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ _scriptClick->variables[3] = 0;
+ _scriptClick->variables[6] = jewel;
+ _scriptInterpreter->startScript(_scriptClick, 4);
+
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+
+ if (_scriptClick->variables[3])
+ return 1;
+
+ _unkAmuletVar = 1;
+ switch (jewel-1) {
+ case 0:
+ if (_brandonStatusBit & 1) {
+ seq_brandonHealing2();
+ } else if (_brandonStatusBit == 0) {
+ seq_brandonHealing();
+ assert(_healingTip);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2003);
+ }
+ characterSays(_healingTip[0], 0, -2);
+ }
+ break;
+
+ case 1:
+ seq_makeBrandonInv();
+ break;
+
+ case 2:
+ if (_brandonStatusBit & 1) {
+ assert(_wispJewelStrings);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2004);
+ }
+ characterSays(_wispJewelStrings[0], 0, -2);
+ } else {
+ if (_brandonStatusBit & 2) {
+ // XXX
+ seq_makeBrandonNormal2();
+ // XXX
+ } else {
+ // do not check for item in hand again as in the original since some strings are missing
+ // in the cd version
+ if (_currentCharacter->sceneId >= 109 && _currentCharacter->sceneId <= 198) {
+ snd_playWanderScoreViaMap(1, 0);
+ seq_makeBrandonWisp();
+ snd_playWanderScoreViaMap(17, 0);
+ } else {
+ seq_makeBrandonWisp();
+ }
+ setGameFlag(0x9E);
+ }
+ }
+ break;
+
+ case 3:
+ seq_dispelMagicAnimation();
+ assert(_magicJewelString);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2007);
+ }
+ characterSays(_magicJewelString[0], 0, -2);
+ break;
+
+ default:
+ break;
+ }
+ _unkAmuletVar = 0;
+ // XXX clearKyrandiaButtonIO (!used before every return in this function!)
+ return 1;
+}
+
+void KyraEngine::processButtonList(Button *list) {
+ while (list) {
+ if (list->flags & 8) {
+ list = list->nextButton;
+ continue;
+ }
+
+ int x = list->x;
+ int y = list->y;
+ assert(list->dimTableIndex < _screen->_screenDimTableCount);
+ if (x < 0) {
+ x += _screen->_screenDimTable[list->dimTableIndex].w << 3;
+ }
+ x += _screen->_screenDimTable[list->dimTableIndex].sx << 3;
+
+ if (y < 0) {
+ y += _screen->_screenDimTable[list->dimTableIndex].h;
+ }
+ y += _screen->_screenDimTable[list->dimTableIndex].sy;
+
+ if (_mouseX >= x && _mouseY >= y && x + list->width >= _mouseX && y + list->height >= _mouseY) {
+ int processMouseClick = 0;
+ if (list->flags & 0x400) {
+ if (_mousePressFlag) {
+ if (!(list->flags2 & 1)) {
+ list->flags2 |= 1;
+ processButton(list);
+ }
+ } else {
+ if (list->flags2 & 1) {
+ list->flags2 &= 0xFFFE;
+ processButton(list);
+ processMouseClick = 1;
+ }
+ }
+ } else if (_mousePressFlag) {
+ processMouseClick = 1;
+ }
+
+ if (processMouseClick) {
+ if (list->buttonCallback) {
+ if ((this->*(list->buttonCallback))(list)) {
+ break;
+ }
+ }
+ }
+ } else {
+ if (list->flags2 & 1) {
+ list->flags2 &= 0xFFFE;
+ processButton(list);
+ }
+ list = list->nextButton;
+ continue;
+ }
+
+ list = list->nextButton;
+ }
+}
+
+void KyraEngine::processButton(Button *button) {
+ if (!button)
+ return;
+
+ int processType = 0;
+ uint8 *shape = 0;
+ Button::ButtonCallback callback = 0;
+
+ int flags = (button->flags2 & 5);
+ if (flags == 1) {
+ processType = button->process2;
+ if (processType == 1) {
+ shape = button->process2PtrShape;
+ } else if (processType == 4) {
+ callback = button->process2PtrCallback;
+ }
+ } else if (flags == 4 || flags == 5) {
+ processType = button->process1;
+ if (processType == 1) {
+ shape = button->process1PtrShape;
+ } else if (processType == 4) {
+ callback = button->process1PtrCallback;
+ }
+ } else {
+ processType = button->process0;
+ if (processType == 1) {
+ shape = button->process0PtrShape;
+ } else if (processType == 4) {
+ callback = button->process0PtrCallback;
+ }
+ }
+
+ int x = button->x;
+ int y = button->y;
+ assert(button->dimTableIndex < _screen->_screenDimTableCount);
+ if (x < 0) {
+ x += _screen->_screenDimTable[button->dimTableIndex].w << 3;
+ }
+
+ if (y < 0) {
+ y += _screen->_screenDimTable[button->dimTableIndex].h;
+ }
+
+ if (processType == 1 && shape) {
+ _screen->drawShape(_screen->_curPage, shape, x, y, button->dimTableIndex, 0x10);
+ } else if (processType == 4 && callback) {
+ (this->*callback)(button);
+ }
+}
+
+void KyraEngine::processAllMenuButtons() {
+ if (!_menuButtonList)
+ return;
+
+ Button *cur = _menuButtonList;
+ while (true) {
+ if (!cur->nextButton) {
+ break;
+ }
+ processMenuButton(cur);
+ cur = cur->nextButton;
+ }
+ return;
+}
+
+void KyraEngine::processMenuButton(Button *button) {
+ if (!_displayMenu)
+ return;
+
+ //_screen->hideMouse();
+
+ if ( !button || (button->flags & 8))
+ return;
+
+ if (button->flags2 & 1)
+ button->flags2 &= 0xf7;
+ else
+ button->flags2 |= 8;
+
+ button->flags2 &= 0xfc;
+
+ if (button->flags2 & 4)
+ button->flags2 |= 0x10;
+ else
+ button->flags2 &= 0xef;
+
+ button->flags2 &= 0xfb;
+
+ processButton(button);
+
+ //_screen->showMouse();
+}
+
+int KyraEngine::drawBoxCallback(Button *button) {
+ if (!_displayMenu)
+ return 0;
+
+ _screen->hideMouse();
+ _screen->drawBox(button->x + 1, button->y + 1, button->x + button->width - 1, button->y + button->height - 1, 0xf8);
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::drawShadedBoxCallback(Button *button) {
+
+ if (!_displayMenu)
+ return 0;
+
+ _screen->hideMouse();
+ _screen->drawShadedBox(button->x, button->y, button->x + button->width, button->y + button->height, 0xf9, 0xfa);
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::buttonMenuCallback(Button *caller) {
+ _displayMenu = true;
+
+ // XXX setLabels
+ if (_currentCharacter->sceneId == 210) {
+ snd_playSoundEffect(0x36);
+ return 0;
+ }
+ // XXX
+
+ for (int i = 0; i < 6; i++) {
+ _menuButtonData[i].process0 = _menuButtonData[i].process1 = _menuButtonData[i].process2 = 4;
+ _menuButtonData[i].process0PtrCallback = &KyraEngine::drawShadedBoxCallback;
+ _menuButtonData[i].process1PtrCallback = &KyraEngine::drawBoxCallback;
+ _menuButtonData[i].process2PtrCallback = &KyraEngine::drawShadedBoxCallback;
+ }
+
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+ gui_fadePalette();
+
+ calcCoords(_menu[0]);
+ calcCoords(_menu[1]);
+ calcCoords(_menu[2]);
+ calcCoords(_menu[3]);
+
+ _menuRestoreScreen = true;
+
+ if (_menuDirectlyToLoad)
+ gui_loadGameMenu(0);
+ else {
+ initMenu(_menu[0]);
+ processAllMenuButtons();
+ }
+
+ while (_displayMenu) {
+ gui_processHighlights(_menu[0]);
+ processButtonList(_menuButtonList);
+ gui_getInput();
+ }
+
+ if (_menuRestoreScreen) {
+ gui_restorePalette();
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _animator->_updateScreen = true;
+ }
+ else
+ _screen->deletePageFromDisk(0);
+
+ return 0;
+}
+
+void KyraEngine::initMenu(Menu menu) {
+ int menu_x2 = menu.width + menu.x - 1;
+ int menu_y2 = menu.height + menu.y - 1;
+
+ _menuButtonList = 0;
+
+ _screen->hideMouse();
+ _screen->fillRect(menu.x + 2, menu.y + 2, menu_x2 - 2, menu_y2 - 2, menu.bgcolor);
+ _screen->drawShadedBox(menu.x, menu.y, menu_x2, menu_y2, menu.color1, menu.color2);
+
+ int textX;
+ int textY;
+
+ if (menu.field_10 != -1)
+ textX = menu.x;
+ else
+ textX = _text->getCenterStringX(menu.menuName, menu.x, menu_x2);
+
+ textY = menu.y + menu.field_12;
+
+ _text->printText(menu.menuName, textX - 1, textY + 1, 12, 248, 0);
+ _text->printText(menu.menuName, textX, textY, menu.textColor, 0, 0);
+
+ int x1, y1, x2, y2;
+ for (int i = 0; i < menu.nrOfItems; i++) {
+ if (!menu.item[i].enabled)
+ continue;
+
+ x1 = menu.x + menu.item[i].x;
+ y1 = menu.y + menu.item[i].y;
+
+ x2 = x1 + menu.item[i].width - 1;
+ y2 = y1 + menu.item[i].height - 1;
+
+ if (i < 6) {
+ _menuButtonData[i].nextButton = 0;
+ _menuButtonData[i].x = x1;
+ _menuButtonData[i].y = y1;
+ _menuButtonData[i].width = menu.item[i].width - 1;
+ _menuButtonData[i].height = menu.item[i].height - 1;
+ _menuButtonData[i].buttonCallback = menu.item[i].callback;
+ _menuButtonData[i].specialValue = menu.item[i].field_1b;
+ //_menuButtonData[i].field_6 = menu.item[i].field_25;
+ //_menuButtonData[i].field_8 = 0;
+
+ if (!_menuButtonList)
+ _menuButtonList = &_menuButtonData[i];
+ else
+ _menuButtonList = initButton(_menuButtonList, &_menuButtonData[i]);
+ }
+ _screen->fillRect(x1, y1, x2, y2, menu.item[i].bgcolor);
+ _screen->drawShadedBox(x1, y1, x2, y2, menu.item[i].color1, menu.item[i].color2);
+
+ if (menu.item[i].field_12 != -1)
+ textX = x1 + menu.item[i].field_12 + 3;
+ else
+ textX = _text->getCenterStringX(menu.item[i].itemString, x1, x2);
+
+ textY = y1 + 2;
+ _text->printText(menu.item[i].itemString, textX - 1, textY + 1, 12, 0, 0);
+
+ if (i == menu.highlightedItem)
+ _text->printText(menu.item[i].itemString, textX, textY, menu.item[i].highlightColor, 0, 0);
+ else
+ _text->printText(menu.item[i].itemString, textX, textY, menu.item[i].textColor, 0, 0);
+
+ if (menu.item[i].labelString) {
+ _text->printText(menu.item[i].labelString, menu.x + menu.item[i].field_21 - 1, menu.y + menu.item[i].field_23 + 1, 12, 0, 0);
+ _text->printText(menu.item[i].labelString, menu.x + menu.item[i].field_21, menu.y + menu.item[i].field_23, 253, 0, 0);
+ }
+ }
+
+ if (menu.scrollUpBtnX != -1) {
+ _scrollUpButton.x = menu.scrollUpBtnX + menu.x;
+ _scrollUpButton.y = menu.scrollUpBtnY + menu.y;
+ _scrollUpButton.buttonCallback = &KyraEngine::gui_scrollUp;
+ _scrollUpButton.nextButton = 0;
+ _menuButtonList = initButton(_menuButtonList, &_scrollUpButton);
+ processMenuButton(&_scrollUpButton);
+
+ _scrollDownButton.x = menu.scrollDownBtnX + menu.x;
+ _scrollDownButton.y = menu.scrollDownBtnY + menu.y;
+ _scrollDownButton.buttonCallback = &KyraEngine::gui_scrollDown;
+ _scrollDownButton.nextButton = 0;
+ _menuButtonList = initButton(_menuButtonList, &_scrollDownButton);
+ processMenuButton(&_scrollDownButton);
+ }
+
+ _screen->showMouse();
+ _screen->updateScreen();
+}
+
+void KyraEngine::calcCoords(Menu &menu) {
+ if (menu.x == -1)
+ menu.x = (320 - menu.width)/2;
+
+ if (menu.y == -1)
+ menu.y = (200 - menu.height)/2;
+
+ assert(menu.nrOfItems < 7);
+ for (int i = 0; i < menu.nrOfItems; i++)
+ if (menu.item[i].x == -1)
+ menu.item[i].x = (menu.width - menu.item[i].width)/2;
+}
+
+void KyraEngine::gui_getInput() {
+ OSystem::Event event;
+ uint32 now = _system->getMillis();
+
+ _mousePressFlag = false;
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_QUIT:
+ quitGame();
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _mousePressFlag = true;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _system->updateScreen();
+ break;
+ case OSystem::EVENT_KEYDOWN:
+ _keyboardEvent.pending = true;
+ _keyboardEvent.repeat = now + 400;
+ _keyboardEvent.ascii = event.kbd.ascii;
+ break;
+ case OSystem::EVENT_KEYUP:
+ _keyboardEvent.repeat = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!_keyboardEvent.pending && _keyboardEvent.repeat && now >= _keyboardEvent.repeat) {
+ _keyboardEvent.pending = true;
+ _keyboardEvent.repeat = now + 100;
+ }
+ _system->delayMillis(3);
+}
+
+int KyraEngine::gui_resumeGame(Button *button) {
+ debug(9, "KyraEngine::gui_resumeGame()");
+ processMenuButton(button);
+ _displayMenu = false;
+
+ return 0;
+}
+
+const char *KyraEngine::getSavegameFilename(int num) {
+ static char saveLoadSlot[12];
+
+ sprintf(saveLoadSlot, "%s.%.3d", _targetName.c_str(), num);
+ return saveLoadSlot;
+}
+
+int KyraEngine::getNextSavegameSlot() {
+ Common::InSaveFile *in;
+
+ for (int i = 1; i < 1000; i++) {
+ if ((in = _saveFileMan->openForLoading(getSavegameFilename(i)))) {
+ delete in;
+ } else {
+ return i;
+ }
+ }
+ warning("Didn't save: Ran out of savegame filenames!");
+ return 0;
+}
+
+void KyraEngine::setupSavegames(Menu &menu, int num) {
+ Common::InSaveFile *in;
+ static char savenames[5][31];
+ uint8 startSlot;
+ assert(num <= 5);
+
+ if (_savegameOffset == 0) {
+ menu.item[0].itemString = _specialSavegameString;
+ menu.item[0].enabled = 1;
+ menu.item[0].field_1b = 0;
+ startSlot = 1;
+ } else
+ startSlot = 0;
+
+ for (int i = startSlot; i < num; i++) {
+ if ((in = _saveFileMan->openForLoading(getSavegameFilename(i + _savegameOffset)))) {
+ in->skip(8);
+ in->read(savenames[i], 31);
+ menu.item[i].itemString = savenames[i];
+ menu.item[i].enabled = 1;
+ menu.item[i].field_1b = i + _savegameOffset;
+ delete in;
+ } else {
+ menu.item[i].enabled = 0;
+ //menu.item[i].itemString = "";
+ //menu.item[i].field_1b = -1;
+ }
+ }
+}
+
+int KyraEngine::gui_saveGameMenu(Button *button) {
+ debug(9, "KyraEngine::gui_saveGameMenu()");
+ processMenuButton(button);
+ _menu[2].item[5].enabled = true;
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ _menu[2].menuName = "Select a position to save to:";
+ _specialSavegameString = "[ EMPTY SLOT ]";
+ for (int i = 0; i < 5; i++)
+ _menu[2].item[i].callback = &KyraEngine::gui_saveGame;
+
+ _savegameOffset = 0;
+ setupSavegames(_menu[2], 5);
+
+ initMenu(_menu[2]);
+ processAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ while (_displaySubMenu) {
+ gui_getInput();
+ gui_processHighlights(_menu[2]);
+ processButtonList(_menuButtonList);
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ if (_cancelSubMenu) {
+ initMenu(_menu[0]);
+ processAllMenuButtons();
+ } else {
+ _displayMenu = false;
+ }
+ return 0;
+}
+
+int KyraEngine::gui_loadGameMenu(Button *button) {
+ debug(9, "KyraEngine::gui_loadGameMenu()");
+ if (_menuDirectlyToLoad)
+ _menu[2].item[5].enabled = false;
+ else {
+ processMenuButton(button);
+ _menu[2].item[5].enabled = true;
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ _specialSavegameString = "[ START A NEW GAME ]";
+ _menu[2].menuName = "Which game would you like to reload?";
+ for (int i = 0; i < 5; i++)
+ _menu[2].item[i].callback = &KyraEngine::gui_loadGame;
+
+ _savegameOffset = 0;
+ setupSavegames(_menu[2], 5);
+
+ initMenu(_menu[2]);
+ processAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ while (_displaySubMenu) {
+ gui_getInput();
+ gui_processHighlights(_menu[2]);
+ processButtonList(_menuButtonList);
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ if (_cancelSubMenu) {
+ initMenu(_menu[0]);
+ processAllMenuButtons();
+ } else {
+ gui_restorePalette();
+ loadGame(getSavegameFilename(_gameToLoad));
+ _displayMenu = false;
+ _menuRestoreScreen = false;
+ }
+ return 0;
+}
+
+void KyraEngine::gui_redrawTextfield() {
+ _screen->fillRect(38, 91, 287, 102, 250);
+ _text->printText(_savegameName, 38, 92, 253, 0, 0);
+
+ _screen->_charWidth = -2;
+ int width = _screen->getTextWidth(_savegameName);
+ _screen->fillRect(39 + width, 93, 45 + width, 100, 254);
+ _screen->_charWidth = 0;
+
+ _screen->updateScreen();
+}
+
+void KyraEngine::gui_updateSavegameString() {
+ int length;
+
+ if (_keyboardEvent.pending && _keyboardEvent.ascii) {
+ length = strlen(_savegameName);
+
+ if (_keyboardEvent.ascii > 31 && _keyboardEvent.ascii < 127) {
+ if (length < 31) {
+ _savegameName[length] = _keyboardEvent.ascii;
+ _savegameName[length+1] = 0;
+ gui_redrawTextfield();
+ }
+ } else if (_keyboardEvent.ascii == 8 ||_keyboardEvent.ascii == 127) {
+ if (length > 0) {
+ _savegameName[length-1] = 0;
+ gui_redrawTextfield();
+ }
+ } else if (_keyboardEvent.ascii == 13) {
+ _displaySubMenu = false;
+ }
+ }
+
+ _keyboardEvent.pending = false;
+}
+
+int KyraEngine::gui_saveGame(Button *button) {
+ debug(9, "KyraEngine::gui_saveGame()");
+ processMenuButton(button);
+ _gameToLoad = button->specialValue;
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ initMenu(_menu[3]);
+ processAllMenuButtons();
+
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+
+ if (_savegameOffset == 0 && _gameToLoad == 0) {
+ _savegameName[0] = 0;
+ } else {
+ for (int i = 0; i < 5; i++) {
+ if (_menu[2].item[i].field_1b == _gameToLoad) {
+ strncpy(_savegameName, _menu[2].item[i].itemString, 31);
+ break;
+ }
+ }
+ }
+ gui_redrawTextfield();
+
+ _keyboardEvent.pending = 0;
+ _keyboardEvent.repeat = 0;
+ while (_displaySubMenu) {
+ gui_getInput();
+ gui_updateSavegameString();
+ gui_processHighlights(_menu[3]);
+ processButtonList(_menuButtonList);
+ }
+
+ if (_cancelSubMenu) {
+ _displaySubMenu = true;
+ _cancelSubMenu = false;
+ initMenu(_menu[2]);
+ processAllMenuButtons();
+ } else {
+ if (_savegameOffset == 0 && _gameToLoad == 0)
+ _gameToLoad = getNextSavegameSlot();
+ if (_gameToLoad > 0)
+ saveGame(getSavegameFilename(_gameToLoad), _savegameName);
+ }
+
+ return 0;
+}
+
+int KyraEngine::gui_savegameConfirm(Button *button) {
+ debug(9, "KyraEngine::gui_savegameConfirm()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+
+ return 0;
+}
+
+int KyraEngine::gui_loadGame(Button *button) {
+ debug(9, "KyraEngine::gui_loadGame()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+ _gameToLoad = button->specialValue;
+
+ return 0;
+}
+
+int KyraEngine::gui_cancelSubMenu(Button *button) {
+ debug(9, "KyraEngine::gui_cancelLoadGameMenu()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+ _cancelSubMenu = true;
+
+ return 0;
+}
+
+int KyraEngine::gui_quitPlaying(Button *button) {
+ debug(9, "KyraEngine::gui_quitPlaying()");
+ processMenuButton(button);
+
+ if (gui_quitConfirm("Are you sure you want to quit playing?"))
+ quitGame();
+ else {
+ initMenu(_menu[0]);
+ processAllMenuButtons();
+ }
+
+ return 0;
+}
+
+bool KyraEngine::gui_quitConfirm(const char *str) {
+ debug(9, "KyraEngine::gui_quitConfirm()");
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ _menu[1].menuName = str;
+ initMenu(_menu[1]);
+
+ _displaySubMenu = true;
+ _cancelSubMenu = true;
+
+ while (_displaySubMenu) {
+ gui_getInput();
+ gui_processHighlights(_menu[1]);
+ processButtonList(_menuButtonList);
+ }
+
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+
+ return !_cancelSubMenu;
+}
+
+int KyraEngine::gui_quitConfirmYes(Button *button) {
+ debug(9, "KyraEngine::gui_quitConfirmYes()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+ _cancelSubMenu = false;
+
+ return 0;
+}
+
+int KyraEngine::gui_quitConfirmNo(Button *button) {
+ debug(9, "KyraEngine::gui_quitConfirmNo()");
+ processMenuButton(button);
+ _displaySubMenu = false;
+ _cancelSubMenu = true;
+
+ return 0;
+}
+
+int KyraEngine::gui_scrollUp(Button *button) {
+ debug(9, "KyraEngine::gui_scrollUp()");
+ processMenuButton(button);
+
+ if (_savegameOffset > 0) {
+ _savegameOffset--;
+ setupSavegames(_menu[2], 5);
+ initMenu(_menu[2]);
+ }
+ return 0;
+}
+
+int KyraEngine::gui_scrollDown(Button *button) {
+ debug(9, "KyraEngine::gui_scrollDown()");
+ processMenuButton(button);
+
+ _savegameOffset++;
+ setupSavegames(_menu[2], 5);
+ initMenu(_menu[2]);
+
+ return 0;
+}
+
+void KyraEngine::gui_processHighlights(Menu &menu) {
+ int x1, y1, x2, y2;
+
+ for (int i = 0; i < menu.nrOfItems; i++) {
+ if (!menu.item[i].enabled)
+ continue;
+
+ x1 = menu.x + menu.item[i].x;
+ y1 = menu.y + menu.item[i].y;
+
+ x2 = x1 + menu.item[i].width;
+ y2 = y1 + menu.item[i].height;
+
+ if (_mouseX > x1 && _mouseX < x2 &&
+ _mouseY > y1 && _mouseY < y2) {
+
+ if (menu.highlightedItem != i) {
+ if (menu.item[menu.highlightedItem].enabled )
+ gui_redrawText(menu);
+
+ menu.highlightedItem = i;
+ gui_redrawHighlight(menu);
+ _screen->updateScreen();
+ }
+ }
+ }
+}
+
+void KyraEngine::gui_redrawText(Menu menu) {
+ int textX;
+ int i = menu.highlightedItem;
+
+ int x1 = menu.x + menu.item[i].x;
+ int y1 = menu.y + menu.item[i].y;
+
+ int x2 = x1 + menu.item[i].width - 1;
+
+ if (menu.item[i].field_12 != -1)
+ textX = x1 + menu.item[i].field_12 + 3;
+ else
+ textX = _text->getCenterStringX(menu.item[i].itemString, x1, x2);
+
+ int textY = y1 + 2;
+ _text->printText(menu.item[i].itemString, textX - 1, textY + 1, 12, 0, 0);
+ _text->printText(menu.item[i].itemString, textX, textY, menu.item[i].textColor, 0, 0);
+}
+
+void KyraEngine::gui_redrawHighlight(Menu menu) {
+ int textX;
+ int i = menu.highlightedItem;
+
+ int x1 = menu.x + menu.item[i].x;
+ int y1 = menu.y + menu.item[i].y;
+
+ int x2 = x1 + menu.item[i].width - 1;
+
+ if (menu.item[i].field_12 != -1)
+ textX = x1 + menu.item[i].field_12 + 3;
+ else
+ textX = _text->getCenterStringX(menu.item[i].itemString, x1, x2);
+
+ int textY = y1 + 2;
+ _text->printText(menu.item[i].itemString, textX - 1, textY + 1, 12, 0, 0);
+ _text->printText(menu.item[i].itemString, textX, textY, menu.item[i].highlightColor, 0, 0);
+}
+
+void KyraEngine::gui_fadePalette() {
+ static int16 menuPalIndexes[] = {248, 249, 250, 251, 252, 253, 254, -1};
+ int index = 0;
+
+ memcpy(_screen->getPalette(2), _screen->_currentPalette, 768);
+
+ for (int i = 0; i < 768; i++) {
+ _screen->_currentPalette[i] /= 2;
+ }
+
+ while( menuPalIndexes[index] != -1) {
+ memcpy(&_screen->_currentPalette[menuPalIndexes[index]*3], &_screen->getPalette(2)[menuPalIndexes[index]*3], 3);
+ index++;
+ }
+
+ _screen->fadePalette(_screen->_currentPalette, 2);
+}
+
+void KyraEngine::gui_restorePalette() {
+ memcpy(_screen->_currentPalette, _screen->getPalette(2), 768);
+ _screen->fadePalette(_screen->_currentPalette, 2);
+}
+
+
+} // end of namespace Kyra
+
diff --git a/engines/kyra/items.cpp b/engines/kyra/items.cpp
new file mode 100644
index 0000000000..c98e1ee607
--- /dev/null
+++ b/engines/kyra/items.cpp
@@ -0,0 +1,982 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "kyra/kyra.h"
+#include "kyra/seqplayer.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+#include "kyra/sound.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+
+#include "common/system.h"
+#include "common/savefile.h"
+
+namespace Kyra {
+
+int KyraEngine::findDuplicateItemShape(int shape) {
+ static uint8 dupTable[] = {
+ 0x48, 0x46, 0x49, 0x47, 0x4a, 0x46, 0x4b, 0x47,
+ 0x4c, 0x46, 0x4d, 0x47, 0x5b, 0x5a, 0x5c, 0x5a,
+ 0x5d, 0x5a, 0x5e, 0x5a, 0xFF, 0xFF
+ };
+
+ int i = 0;
+
+ while (dupTable[i] != 0xFF) {
+ if (dupTable[i] == shape)
+ return dupTable[i+1];
+ i += 2;
+ }
+ return -1;
+}
+
+void KyraEngine::addToNoDropRects(int x, int y, int w, int h) {
+ debug(9, "KyraEngine::addToNoDropRects(%d, %d, %d, %d)", x, y, w, h);
+ for (int rect = 0; rect < 11; ++rect) {
+ if (_noDropRects[rect].x == -1) {
+ _noDropRects[rect].x = x;
+ _noDropRects[rect].y = y;
+ _noDropRects[rect].x2 = x + w - 1;
+ _noDropRects[rect].y2 = y + h - 1;
+ break;
+ }
+ }
+}
+
+void KyraEngine::clearNoDropRects() {
+ debug(9, "KyraEngine::clearNoDropRects()");
+ memset(_noDropRects, -1, sizeof(_noDropRects));
+}
+
+byte KyraEngine::findFreeItemInScene(int scene) {
+ debug(9, "KyraEngine::findFreeItemInScene(%d)", scene);
+ assert(scene < _roomTableSize);
+ Room *room = &_roomTable[scene];
+ for (int i = 0; i < 12; ++i) {
+ if (room->itemsTable[i] == 0xFF)
+ return i;
+ }
+ return 0xFF;
+}
+
+byte KyraEngine::findItemAtPos(int x, int y) {
+ debug(9, "KyraEngine::findItemAtPos(%d, %d)", x, y);
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ const uint8 *itemsTable = _roomTable[_currentCharacter->sceneId].itemsTable;
+ const uint16 *xposOffset = _roomTable[_currentCharacter->sceneId].itemsXPos;
+ const uint8 *yposOffset = _roomTable[_currentCharacter->sceneId].itemsYPos;
+
+ int highestYPos = -1;
+ byte returnValue = 0xFF;
+
+ for (int i = 0; i < 12; ++i) {
+ if (*itemsTable != 0xFF) {
+ int xpos = *xposOffset - 11;
+ int xpos2 = *xposOffset + 10;
+ if (x > xpos && x < xpos2) {
+ assert(*itemsTable < ARRAYSIZE(_itemTable));
+ int itemHeight = _itemTable[*itemsTable].height;
+ int ypos = *yposOffset + 3;
+ int ypos2 = ypos - itemHeight - 3;
+
+ if (y > ypos2 && ypos > y) {
+ if (highestYPos <= ypos) {
+ returnValue = i;
+ highestYPos = ypos;
+ }
+ }
+ }
+ }
+ ++xposOffset;
+ ++yposOffset;
+ ++itemsTable;
+ }
+
+ return returnValue;
+}
+
+void KyraEngine::placeItemInGenericMapScene(int item, int index) {
+ debug(9, "KyraEngine::placeItemInGenericMapScene(%d, %d)", item, index);
+ static const uint16 itemMapSceneMinTable[] = {
+ 0x0000, 0x0011, 0x006D, 0x0025, 0x00C7, 0x0000
+ };
+ static const uint16 itemMapSceneMaxTable[] = {
+ 0x0010, 0x0024, 0x00C6, 0x006C, 0x00F5, 0x0000
+ };
+
+ int minValue = itemMapSceneMinTable[index];
+ int maxValue = itemMapSceneMaxTable[index];
+
+ while (true) {
+ int room = _rnd.getRandomNumberRng(minValue, maxValue);
+ assert(room < _roomTableSize);
+ int nameIndex = _roomTable[room].nameIndex;
+ bool placeItem = false;
+
+ switch (nameIndex) {
+ case 0: case 1: case 2: case 3:
+ case 4: case 5: case 6: case 11:
+ case 12: case 16: case 17: case 20:
+ case 22: case 23: case 25: case 26:
+ case 27: case 31: case 33: case 34:
+ case 36: case 37: case 58: case 59:
+ case 60: case 61: case 83: case 84:
+ case 85: case 104: case 105: case 106:
+ placeItem = true;
+ break;
+
+ case 51:
+ if (room != 46) {
+ placeItem = true;
+ break;
+ }
+ default:
+ placeItem = false;
+ break;
+ }
+
+ if (placeItem) {
+ Room *roomPtr = &_roomTable[room];
+ if (roomPtr->northExit == 0xFFFF && roomPtr->eastExit == 0xFFFF && roomPtr->southExit == 0xFFFF && roomPtr->westExit == 0xFFFF) {
+ placeItem = false;
+ } else if (_currentCharacter->sceneId == room) {
+ placeItem = false;
+ }
+ }
+
+ if (placeItem) {
+ if (!processItemDrop(room, item, -1, -1, 2, 0))
+ continue;
+ break;
+ }
+ }
+}
+
+void KyraEngine::createMouseItem(int item) {
+ debug(9, "KyraEngine::createMouseItem(%d)", item);
+ _screen->hideMouse();
+ setMouseItem(item);
+ _itemInHand = item;
+ _screen->showMouse();
+}
+
+void KyraEngine::destroyMouseItem() {
+ debug(9, "KyraEngine::destroyMouseItem()");
+ _screen->hideMouse();
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ _itemInHand = -1;
+ _screen->showMouse();
+}
+
+void KyraEngine::setMouseItem(int item) {
+ debug(9, "KyraEngine::setMouseItem(%d)", item);
+ if (item == -1) {
+ _screen->setMouseCursor(1, 1, _shapes[10]);
+ } else {
+ _screen->setMouseCursor(8, 15, _shapes[220+item]);
+ }
+}
+
+void KyraEngine::wipeDownMouseItem(int xpos, int ypos) {
+ debug(9, "KyraEngine::wipeDownMouseItem(%d, %d)", xpos, ypos);
+ if (_itemInHand == -1)
+ return;
+ xpos -= 8;
+ ypos -= 15;
+ _screen->hideMouse();
+ _screen->backUpRect1(xpos, ypos);
+ int y = ypos;
+ int height = 16;
+
+ while (height >= 0) {
+ _screen->restoreRect1(xpos, ypos);
+ _screen->setNewShapeHeight(_shapes[220+_itemInHand], height);
+ uint32 nextTime = _system->getMillis() + 1 * _tickLength;
+ _screen->drawShape(0, _shapes[220+_itemInHand], xpos, y, 0, 0);
+ _screen->updateScreen();
+ y += 2;
+ height -= 2;
+ while (_system->getMillis() < nextTime) {}
+ }
+ _screen->restoreRect1(xpos, ypos);
+ _screen->resetShapeHeight(_shapes[220+_itemInHand]);
+ destroyMouseItem();
+ _screen->showMouse();
+}
+
+void KyraEngine::setupSceneItems() {
+ debug(9, "KyraEngine::setupSceneItems()");
+ uint16 sceneId = _currentCharacter->sceneId;
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+ for (int i = 0; i < 12; ++i) {
+ uint8 item = currentRoom->itemsTable[i];
+ if (item == 0xFF || !currentRoom->needInit[i]) {
+ continue;
+ }
+
+ int xpos = 0;
+ int ypos = 0;
+
+ if (currentRoom->itemsXPos[i] == 0xFFFF) {
+ xpos = currentRoom->itemsXPos[i] = _rnd.getRandomNumberRng(24, 296);
+ ypos = currentRoom->itemsYPos[i] = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 130);
+ } else {
+ xpos = currentRoom->itemsXPos[i];
+ ypos = currentRoom->itemsYPos[i];
+ }
+
+ _lastProcessedItem = i;
+
+ int stop = 0;
+ while (!stop) {
+ stop = processItemDrop(sceneId, item, xpos, ypos, 3, 0);
+ if (!stop) {
+ xpos = currentRoom->itemsXPos[i] = _rnd.getRandomNumberRng(24, 296);
+ ypos = currentRoom->itemsYPos[i] = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 130);
+ if (countItemsInScene(sceneId) >= 12) {
+ break;
+ }
+ } else {
+ currentRoom->needInit[i] = 0;
+ }
+ }
+ }
+}
+
+int KyraEngine::countItemsInScene(uint16 sceneId) {
+ debug(9, "KyraEngine::countItemsInScene(%d)", sceneId);
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+
+ int items = 0;
+
+ for (int i = 0; i < 12; ++i) {
+ if (currentRoom->itemsTable[i] != 0xFF) {
+ ++items;
+ }
+ }
+
+ return items;
+}
+
+int KyraEngine::processItemDrop(uint16 sceneId, uint8 item, int x, int y, int unk1, int unk2) {
+ debug(9, "KyraEngine::processItemDrop(%d, %d, %d, %d, %d, %d)", sceneId, item, x, y, unk1, unk2);
+ int freeItem = -1;
+ uint8 itemIndex = findItemAtPos(x, y);
+ if (unk1) {
+ itemIndex = 0xFF;
+ }
+
+ if (itemIndex != 0xFF) {
+ exchangeItemWithMouseItem(sceneId, itemIndex);
+ return 0;
+ }
+
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+
+ if (unk1 != 3) {
+ for (int i = 0; i < 12; ++i) {
+ if (currentRoom->itemsTable[i] == 0xFF) {
+ freeItem = i;
+ break;
+ }
+ }
+ } else {
+ freeItem = _lastProcessedItem;
+ }
+
+ if (freeItem == -1) {
+ return 0;
+ }
+
+ if (sceneId != _currentCharacter->sceneId) {
+ addItemToRoom(sceneId, item, freeItem, x, y);
+ return 1;
+ }
+
+ int itemHeight = _itemTable[item].height;
+ _lastProcessedItemHeight = itemHeight;
+
+ if (x == -1 && x == -1) {
+ x = _rnd.getRandomNumberRng(16, 304);
+ y = _rnd.getRandomNumberRng(_northExitHeight & 0xFF, 135);
+ }
+
+ int xpos = x;
+ int ypos = y;
+ int destY = -1;
+ int destX = -1;
+ int running = 1;
+
+ while (running) {
+ if ((_northExitHeight & 0xFF) <= ypos) {
+ bool running2 = true;
+
+ if (_screen->getDrawLayer(xpos, ypos) > 1) {
+ if (((_northExitHeight >> 8) & 0xFF) != ypos) {
+ running2 = false;
+ }
+ }
+
+ if (_screen->getDrawLayer2(xpos, ypos, itemHeight) > 1) {
+ if (((_northExitHeight >> 8) & 0xFF) != ypos) {
+ running2 = false;
+ }
+ }
+
+ if (!isDropable(xpos, ypos)) {
+ if (((_northExitHeight >> 8) & 0xFF) != ypos) {
+ running2 = false;
+ }
+ }
+
+ int xpos2 = xpos;
+ int xpos3 = xpos;
+
+ while (running2) {
+ if (isDropable(xpos2, ypos)) {
+ if (_screen->getDrawLayer2(xpos2, ypos, itemHeight) < 7) {
+ if (findItemAtPos(xpos2, ypos) == 0xFF) {
+ destX = xpos2;
+ destY = ypos;
+ running = 0;
+ running2 = false;
+ }
+ }
+ }
+
+ if (isDropable(xpos3, ypos)) {
+ if (_screen->getDrawLayer2(xpos3, ypos, itemHeight) < 7) {
+ if (findItemAtPos(xpos3, ypos) == 0xFF) {
+ destX = xpos3;
+ destY = ypos;
+ running = 0;
+ running2 = false;
+ }
+ }
+ }
+
+ if (!running2)
+ continue;
+
+ xpos2 -= 2;
+ if (xpos2 < 16) {
+ xpos2 = 16;
+ }
+
+ xpos3 += 2;
+ if (xpos3 > 304) {
+ xpos3 = 304;
+ }
+
+ if (xpos2 > 16)
+ continue;
+ if (xpos3 < 304)
+ continue;
+ running2 = false;
+ }
+ }
+
+ if (((_northExitHeight >> 8) & 0xFF) == ypos) {
+ running = 0;
+ destY -= _rnd.getRandomNumberRng(0, 3);
+
+ if ((_northExitHeight & 0xFF) < destY) {
+ continue;
+ }
+
+ destY = (_northExitHeight & 0xFF) + 1;
+ continue;
+ }
+ ypos += 2;
+ if (((_northExitHeight >> 8) & 0xFF) >= ypos) {
+ continue;
+ }
+ ypos = (_northExitHeight >> 8) & 0xFF;
+ }
+
+ if (destX == -1 || destY == -1) {
+ return 0;
+ }
+
+ if (unk1 == 3) {
+ currentRoom->itemsXPos[freeItem] = destX;
+ currentRoom->itemsYPos[freeItem] = destY;
+ return 1;
+ }
+
+ if (unk1 == 2) {
+ itemSpecialFX(x, y, item);
+ }
+
+ if (unk1 == 0) {
+ destroyMouseItem();
+ }
+
+ itemDropDown(x, y, destX, destY, freeItem, item);
+
+ if (unk1 == 0 && unk2 != 0) {
+ assert(_itemList && _droppedList);
+ updateSentenceCommand(_itemList[item], _droppedList[0], 179);
+ }
+
+ return 1;
+}
+
+void KyraEngine::exchangeItemWithMouseItem(uint16 sceneId, int itemIndex) {
+ debug(9, "KyraEngine::exchangeItemWithMouseItem(%d, %d)", sceneId, itemIndex);
+ _screen->hideMouse();
+ _animator->animRemoveGameItem(itemIndex);
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+
+ int item = currentRoom->itemsTable[itemIndex];
+ currentRoom->itemsTable[itemIndex] = _itemInHand;
+ _itemInHand = item;
+ _animator->animAddGameItem(itemIndex, sceneId);
+ snd_playSoundEffect(53);
+
+ setMouseItem(_itemInHand);
+ assert(_itemList && _takenList);
+ updateSentenceCommand(_itemList[_itemInHand], _takenList[1], 179);
+ _screen->showMouse();
+ clickEventHandler2();
+}
+
+void KyraEngine::addItemToRoom(uint16 sceneId, uint8 item, int itemIndex, int x, int y) {
+ debug(9, "KyraEngine::addItemToRoom(%d, %d, %d, %d, %d)", sceneId, item, itemIndex, x, y);
+ assert(sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[sceneId];
+ currentRoom->itemsTable[itemIndex] = item;
+ currentRoom->itemsXPos[itemIndex] = x;
+ currentRoom->itemsYPos[itemIndex] = y;
+ currentRoom->needInit[itemIndex] = 1;
+}
+
+int KyraEngine::checkNoDropRects(int x, int y) {
+ debug(9, "KyraEngine::checkNoDropRects(%d, %d)", x, y);
+ if (_lastProcessedItemHeight < 1 || _lastProcessedItemHeight > 16) {
+ _lastProcessedItemHeight = 16;
+ }
+ if (_noDropRects[0].x == -1) {
+ return 0;
+ }
+
+ for (int i = 0; i < 11; ++i) {
+ if (_noDropRects[i].x == -1) {
+ break;
+ }
+
+ int xpos = _noDropRects[i].x;
+ int ypos = _noDropRects[i].y;
+ int xpos2 = _noDropRects[i].x2;
+ int ypos2 = _noDropRects[i].y2;
+
+ if (xpos > x + 16)
+ continue;
+ if (xpos2 < x)
+ continue;
+ if (y < ypos)
+ continue;
+ if (ypos2 < y - _lastProcessedItemHeight)
+ continue;
+ return 1;
+ }
+
+ return 0;
+}
+
+int KyraEngine::isDropable(int x, int y) {
+ debug(9, "KyraEngine::isDropable(%d, %d)", x, y);
+ x -= 8;
+ y -= 1;
+
+ if (checkNoDropRects(x, y)) {
+ return 0;
+ }
+
+ for (int xpos = x; xpos < x + 16; ++xpos) {
+ if (_screen->getShapeFlag1(xpos, y) == 0) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void KyraEngine::itemDropDown(int x, int y, int destX, int destY, byte freeItem, int item) {
+ debug(9, "KyraEngine::itemDropDown(%d, %d, %d, %d, %d, %d)", x, y, destX, destY, freeItem, item);
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[_currentCharacter->sceneId];
+ if (x == destX && y == destY) {
+ currentRoom->itemsXPos[freeItem] = destX;
+ currentRoom->itemsYPos[freeItem] = destY;
+ currentRoom->itemsTable[freeItem] = item;
+ snd_playSoundEffect(0x32);
+ _animator->animAddGameItem(freeItem, _currentCharacter->sceneId);
+ return;
+ }
+ _screen->hideMouse();
+ if (y <= destY) {
+ int tempY = y;
+ int addY = 2;
+ int drawX = x - 8;
+ int drawY = 0;
+
+ _screen->backUpRect0(drawX, y - 16);
+
+ while (tempY < destY) {
+ _screen->restoreRect0(drawX, tempY - 16);
+ tempY += addY;
+ if (tempY > destY) {
+ tempY = destY;
+ }
+ ++addY;
+ drawY = tempY - 16;
+ _screen->backUpRect0(drawX, drawY);
+ uint32 nextTime = _system->getMillis() + 1 * _tickLength;
+ _screen->drawShape(0, _shapes[220+item], drawX, drawY, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+
+ bool skip = false;
+ if (x == destX) {
+ if (destY - y <= 16) {
+ skip = true;
+ }
+ }
+
+ if (!skip) {
+ snd_playSoundEffect(0x47);
+ if (addY < 6)
+ addY = 6;
+
+ int xDiff = (destX - x) << 4;
+ xDiff /= addY;
+ int startAddY = addY;
+ addY >>= 1;
+ if (destY - y <= 8) {
+ addY >>= 1;
+ }
+ addY = -addY;
+ int unkX = x << 4;
+ while (--startAddY) {
+ drawX = (unkX >> 4) - 8;
+ drawY = tempY - 16;
+ _screen->restoreRect0(drawX, drawY);
+ tempY += addY;
+ unkX += xDiff;
+ if (tempY > destY) {
+ tempY = destY;
+ }
+ ++addY;
+ drawX = (unkX >> 4) - 8;
+ drawY = tempY - 16;
+ _screen->backUpRect0(drawX, drawY);
+ uint32 nextTime = _system->getMillis() + 1 * _tickLength;
+ _screen->drawShape(0, _shapes[220+item], drawX, drawY, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect0(drawX, drawY);
+ } else {
+ _screen->restoreRect0(drawX, tempY - 16);
+ }
+ }
+ currentRoom->itemsXPos[freeItem] = destX;
+ currentRoom->itemsYPos[freeItem] = destY;
+ currentRoom->itemsTable[freeItem] = item;
+ snd_playSoundEffect(0x32);
+ _animator->animAddGameItem(freeItem, _currentCharacter->sceneId);
+ _screen->showMouse();
+}
+
+void KyraEngine::dropItem(int unk1, int item, int x, int y, int unk2) {
+ debug(9, "KyraEngine::dropItem(%d, %d, %d, %d, %d)", unk1, item, x, y, unk2);
+ if (processItemDrop(_currentCharacter->sceneId, item, x, y, unk1, unk2))
+ return;
+ snd_playSoundEffect(54);
+ if (12 == countItemsInScene(_currentCharacter->sceneId)) {
+ assert(_noDropList);
+ drawSentenceCommand(_noDropList[0], 6);
+ } else {
+ assert(_noDropList);
+ drawSentenceCommand(_noDropList[1], 6);
+ }
+}
+
+void KyraEngine::itemSpecialFX(int x, int y, int item) {
+ debug(9, "KyraEngine::itemSpecialFX(%d, %d, %d)", x, y, item);
+ if (item == 41) {
+ itemSpecialFX1(x, y, item);
+ } else {
+ itemSpecialFX2(x, y, item);
+ }
+}
+
+void KyraEngine::itemSpecialFX1(int x, int y, int item) {
+ debug(9, "KyraEngine::itemSpecialFX1(%d, %d, %d)", x, y, item);
+ uint8 *shape = _shapes[220+item];
+ x -= 8;
+ int startY = y;
+ y -= 15;
+ _screen->hideMouse();
+ _screen->backUpRect0(x, y);
+ for (int i = 1; i <= 16; ++i) {
+ _screen->setNewShapeHeight(shape, i);
+ --startY;
+ _screen->restoreRect0(x, y);
+ uint32 nextTime = _system->getMillis() + 1 * _tickLength;
+ _screen->drawShape(0, shape, x, startY, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect0(x, y);
+ _screen->showMouse();
+}
+
+void KyraEngine::itemSpecialFX2(int x, int y, int item) {
+ debug(9, "KyraEngine::itemSpecialFX2(%d, %d, %d)", x, y, item);
+ x -= 8;
+ y -= 15;
+ int yAdd = (int8)(((16 - _itemTable[item].height) >> 1) & 0xFF);
+ _screen->backUpRect0(x, y);
+ if (item >= 80 && item <= 89) {
+ snd_playSoundEffect(55);
+ }
+
+ for (int i = 201; i <= 205; ++i) {
+ _screen->restoreRect0(x, y);
+ uint32 nextTime = _system->getMillis() + 3 * _tickLength;
+ _screen->drawShape(0, _shapes[4+i], x, y + yAdd, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+
+ for (int i = 204; i >= 201; --i) {
+ _screen->restoreRect0(x, y);
+ uint32 nextTime = _system->getMillis() + 3 * _tickLength;
+ _screen->drawShape(0, _shapes[220+item], x, y, 0, 0);
+ _screen->drawShape(0, _shapes[4+i], x, y + yAdd, 0, 0);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if ((nextTime - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect0(x, y);
+}
+
+void KyraEngine::magicOutMouseItem(int animIndex, int itemPos) {
+ debug(9, "KyraEngine::magicOutMouseItem(%d, %d)", animIndex, itemPos);
+ int videoPageBackUp = _screen->_curPage;
+ _screen->_curPage = 0;
+ int x = 0, y = 0;
+ if (itemPos == -1) {
+ x = _mouseX - 12;
+ y = _mouseY - 18;
+ } else {
+ x = _itemPosX[itemPos] - 4;
+ y = _itemPosY[itemPos] - 3;
+ }
+
+ if (_itemInHand == -1 && itemPos == -1) {
+ return;
+ }
+
+ int tableIndex = 0, loopStart = 0, maxLoops = 0;
+ if (animIndex == 0) {
+ tableIndex = _rnd.getRandomNumberRng(0, 5);
+ loopStart = 35;
+ maxLoops = 9;
+ } else if (animIndex == 1) {
+ tableIndex = _rnd.getRandomNumberRng(0, 11);
+ loopStart = 115;
+ maxLoops = 8;
+ } else if (animIndex == 2) {
+ tableIndex = 0;
+ loopStart = 124;
+ maxLoops = 4;
+ } else {
+ tableIndex = -1;
+ }
+
+ if (animIndex == 2) {
+ snd_playSoundEffect(0x5E);
+ } else {
+ snd_playSoundEffect(0x37);
+ }
+ _screen->hideMouse();
+ _screen->backUpRect1(x, y);
+
+ for (int shape = _magicMouseItemStartFrame[animIndex]; shape <= _magicMouseItemEndFrame[animIndex]; ++shape) {
+ _screen->restoreRect1(x, y);
+ uint32 nextTime = _system->getMillis() + 4 * _tickLength;
+ _screen->drawShape(0, _shapes[220+_itemInHand], x + 4, y + 3, 0, 0);
+ if (tableIndex == -1) {
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0);
+ } else {
+ specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ }
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+
+ if (itemPos != -1) {
+ _screen->restoreRect1(x, y);
+ _screen->fillRect(_itemPosX[itemPos], _itemPosY[itemPos], _itemPosX[itemPos] + 15, _itemPosY[itemPos] + 15, 12, 0);
+ _screen->backUpRect1(x, y);
+ }
+
+ for (int shape = _magicMouseItemStartFrame2[animIndex]; shape <= _magicMouseItemEndFrame2[animIndex]; ++shape) {
+ _screen->restoreRect1(x, y);
+ uint32 nextTime = _system->getMillis() + 4 * _tickLength;
+ _screen->drawShape(0, _shapes[220+_itemInHand], x + 4, y + 3, 0, 0);
+ if (tableIndex == -1) {
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0);
+ } else {
+ specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ }
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect1(x, y);
+ if (itemPos == -1) {
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ _itemInHand = -1;
+ } else {
+ _characterList[0].inventoryItems[itemPos] = 0xFF;
+ _screen->hideMouse();
+ _screen->fillRect(_itemPosX[itemPos], _itemPosY[itemPos], _itemPosX[itemPos] + 15, _itemPosY[itemPos] + 15, 12, 0);
+ _screen->showMouse();
+ }
+ _screen->showMouse();
+ _screen->_curPage = videoPageBackUp;
+}
+
+void KyraEngine::magicInMouseItem(int animIndex, int item, int itemPos) {
+ debug(9, "KyraEngine::magicInMouseItem(%d, %d, %d)", animIndex, item, itemPos);
+ int videoPageBackUp = _screen->_curPage;
+ _screen->_curPage = 0;
+ int x = 0, y = 0;
+ if (itemPos == -1) {
+ x = _mouseX - 12;
+ y = _mouseY - 18;
+ } else {
+ x = _itemPosX[itemPos] - 4;
+ y = _itemPosX[itemPos] - 3;
+ }
+ if (item < 0)
+ return;
+
+ int tableIndex = -1, loopStart = 0, maxLoops = 0;
+ if (animIndex == 0) {
+ tableIndex = _rnd.getRandomNumberRng(0, 5);
+ loopStart = 35;
+ maxLoops = 9;
+ } else if (animIndex == 1) {
+ tableIndex = _rnd.getRandomNumberRng(0, 11);
+ loopStart = 115;
+ maxLoops = 8;
+ } else if (animIndex == 2) {
+ tableIndex = 0;
+ loopStart = 124;
+ maxLoops = 4;
+ }
+
+ _screen->hideMouse();
+ _screen->backUpRect1(x, y);
+ if (animIndex == 2) {
+ snd_playSoundEffect(0x5E);
+ } else {
+ snd_playSoundEffect(0x37);
+ }
+
+ for (int shape = _magicMouseItemStartFrame[animIndex]; shape <= _magicMouseItemEndFrame[animIndex]; ++shape) {
+ _screen->restoreRect1(x, y);
+ uint32 nextTime = _system->getMillis() + 4 * _tickLength;
+ if (tableIndex == -1) {
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0);
+ } else {
+ specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ }
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+
+ for (int shape = _magicMouseItemStartFrame2[animIndex]; shape <= _magicMouseItemEndFrame2[animIndex]; ++shape) {
+ _screen->restoreRect1(x, y);
+ uint32 nextTime = _system->getMillis() + 4 * _tickLength;
+ if (tableIndex == -1) {
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0);
+ } else {
+ specialMouseItemFX(shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ }
+ _screen->updateScreen();
+ while (_system->getMillis() < nextTime) {
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+ _screen->restoreRect1(x, y);
+ if (itemPos == -1) {
+ _screen->setMouseCursor(8, 15, _shapes[220+item]);
+ _itemInHand = item;
+ } else {
+ _characterList[0].inventoryItems[itemPos] = item;
+ _screen->hideMouse();
+ _screen->drawShape(0, _shapes[220+item], _itemPosX[itemPos], _itemPosY[itemPos], 0, 0);
+ _screen->showMouse();
+ }
+ _screen->showMouse();
+ _screen->_curPage = videoPageBackUp;
+}
+
+void KyraEngine::specialMouseItemFX(int shape, int x, int y, int animIndex, int tableIndex, int loopStart, int maxLoops) {
+ debug(9, "KyraEngine::specialMouseItemFX(%d, %d, %d, %d, %d, %d, %d)", shape, x, y, animIndex, tableIndex, loopStart, maxLoops);
+ static const uint8 table1[] = {
+ 0x23, 0x45, 0x55, 0x72, 0x84, 0xCF, 0x00, 0x00
+ };
+ static const uint8 table2[] = {
+ 0x73, 0xB5, 0x80, 0x21, 0x13, 0x39, 0x45, 0x55, 0x62, 0xB4, 0xCF, 0xD8
+ };
+ static const uint8 table3[] = {
+ 0x7C, 0xD0, 0x74, 0x84, 0x87, 0x00, 0x00, 0x00
+ };
+ int tableValue = 0;
+ if (animIndex == 0) {
+ tableValue = table1[tableIndex];
+ } else if (animIndex == 1) {
+ tableValue = table2[tableIndex];
+ } else if (animIndex == 2) {
+ tableValue = table3[tableIndex];
+ } else {
+ return;
+ }
+ processSpecialMouseItemFX(shape, x, y, tableValue, loopStart, maxLoops);
+}
+
+void KyraEngine::processSpecialMouseItemFX(int shape, int x, int y, int tableValue, int loopStart, int maxLoops) {
+ debug(9, "KyraEngine::processSpecialMouseItemFX(%d, %d, %d, %d, %d, %d)", shape, x, y, tableValue, loopStart, maxLoops);
+ uint8 shapeColorTable[16];
+ uint8 *shapePtr = _shapes[4+shape] + 10;
+ if (_features & GF_TALKIE)
+ shapePtr += 2;
+ for (int i = 0; i < 16; ++i) {
+ shapeColorTable[i] = shapePtr[i];
+ }
+ for (int i = loopStart; i < loopStart + maxLoops; ++i) {
+ for (int i2 = 0; i2 < 16; ++i2) {
+ if (shapePtr[i2] == i) {
+ shapeColorTable[i2] = (i + tableValue) - loopStart;
+ }
+ }
+ }
+ _screen->drawShape(0, _shapes[4+shape], x, y, 0, 0x8000, shapeColorTable);
+}
+
+void KyraEngine::updatePlayerItemsForScene() {
+ debug(9, "KyraEngine::updatePlayerItemsForScene()");
+ if (_itemInHand >= 29 && _itemInHand < 33) {
+ ++_itemInHand;
+ if (_itemInHand > 33)
+ _itemInHand = 33;
+ _screen->hideMouse();
+ _screen->setMouseCursor(8, 15, _shapes[220+_itemInHand]);
+ _screen->showMouse();
+ }
+
+ bool redraw = false;
+ for (int i = 0; i < 10; ++i) {
+ uint8 item = _currentCharacter->inventoryItems[i];
+ if (item >= 29 && item < 33) {
+ ++item;
+ if (item > 33)
+ item = 33;
+ _currentCharacter->inventoryItems[i] = item;
+ redraw = true;
+ }
+ }
+
+ if (redraw) {
+ _screen->hideMouse();
+ redrawInventory(0);
+ _screen->showMouse();
+ }
+
+ if (_itemInHand == 33) {
+ magicOutMouseItem(2, -1);
+ }
+
+ _screen->hideMouse();
+ for (int i = 0; i < 10; ++i) {
+ uint8 item = _currentCharacter->inventoryItems[i];
+ if (item == 33) {
+ magicOutMouseItem(2, i);
+ }
+ }
+ _screen->showMouse();
+}
+
+void KyraEngine::redrawInventory(int page) {
+ int videoPageBackUp = _screen->_curPage;
+ _screen->_curPage = page;
+ _screen->hideMouse();
+ for (int i = 0; i < 10; ++i) {
+ _screen->fillRect(_itemPosX[i], _itemPosY[i], _itemPosX[i] + 15, _itemPosY[i] + 15, 12, page);
+ if (_currentCharacter->inventoryItems[i] != 0xFF) {
+ uint8 item = _currentCharacter->inventoryItems[i];
+ _screen->drawShape(page, _shapes[220+item], _itemPosX[i], _itemPosY[i], 0, 0);
+ }
+ }
+ _screen->showMouse();
+ _screen->_curPage = videoPageBackUp;
+ _screen->updateScreen();
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/kyra.cpp b/engines/kyra/kyra.cpp
new file mode 100644
index 0000000000..c85ce9b323
--- /dev/null
+++ b/engines/kyra/kyra.cpp
@@ -0,0 +1,1215 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/md5.h"
+#include "common/savefile.h"
+
+#include "sound/mixer.h"
+#include "sound/mididrv.h"
+
+#include "gui/message.h"
+
+#include "kyra/kyra.h"
+#include "kyra/resource.h"
+#include "kyra/screen.h"
+#include "kyra/script.h"
+#include "kyra/seqplayer.h"
+#include "kyra/sound.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+#include "kyra/debugger.h"
+
+using namespace Kyra;
+
+enum {
+ // We only compute MD5 of the first megabyte of our data files.
+ kMD5FileSizeLimit = 1024 * 1024
+};
+
+// Kyra MD5 detection brutally ripped from the Gobliins engine.
+struct KyraGameSettings {
+ const char *gameid;
+ const char *description;
+ byte id;
+ uint32 features;
+ const char *md5sum;
+ const char *checkFile;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { gameid, description, features };
+ return dummy;
+ }
+};
+
+// We could get rid of md5 detection at least for kyra 1 since we can locate all
+// needed files for detecting the right language and version (Floppy, Talkie)
+static const KyraGameSettings kyra_games[] = {
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_ENGLISH | GF_FLOPPY, // english floppy 1.0 from Malice
+ "3c244298395520bb62b5edfe41688879", "GEMCUT.EMC" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_ENGLISH | GF_FLOPPY,
+ "796e44863dd22fa635b042df1bf16673", "GEMCUT.EMC" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_FRENCH | GF_FLOPPY,
+ "abf8eb360e79a6c2a837751fbd4d3d24", "GEMCUT.EMC" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_GERMAN | GF_FLOPPY,
+ "6018e1dfeaca7fe83f8d0b00eb0dd049", "GEMCUT.EMC"},
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_GERMAN | GF_FLOPPY, // from Arne.F
+ "f0b276781f47c130f423ec9679fe9ed9", "GEMCUT.EMC"},
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_SPANISH | GF_FLOPPY, // from VooD
+ "8909b41596913b3f5deaf3c9f1017b01", "GEMCUT.EMC"},
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_SPANISH | GF_FLOPPY, // floppy 1.8 from clemmy
+ "747861d2a9c643c59fdab570df5b9093", "GEMCUT.EMC"},
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_ENGLISH | GF_TALKIE,
+ "fac399fe62f98671e56a005c5e94e39f", "GEMCUT.PAK" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_GERMAN | GF_TALKIE,
+ "230f54e6afc007ab4117159181a1c722", "GEMCUT.PAK" },
+ { "kyra1", "The Legend of Kyrandia", GI_KYRA1, GF_FRENCH | GF_TALKIE,
+ "b037c41768b652a040360ffa3556fd2a", "GEMCUT.PAK" },
+ { "kyra1", "The Legend of Kyrandia Demo", GI_KYRA1, GF_DEMO | GF_ENGLISH,
+ "fb722947d94897512b13b50cc84fd648", "DEMO1.WSA" },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+// Keep list of different supported games
+struct KyraGameList {
+ const char *gameid;
+ const char *description;
+ uint32 features;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { gameid, description, features };
+ return dummy;
+ }
+};
+
+static const KyraGameList kyra_list[] = {
+ { "kyra1", "The Legend of Kyrandia", 0 },
+ { 0, 0, 0 }
+};
+
+struct KyraLanguageTable {
+ const char *file;
+ uint32 language;
+ Common::Language detLanguage;
+};
+
+static const KyraLanguageTable kyra_languages[] = {
+ { "MAIN15.CPS", GF_ENGLISH, Common::EN_USA },
+ { "MAIN_ENG.CPS", GF_ENGLISH, Common::EN_USA },
+ { "MAIN_FRE.CPS", GF_FRENCH, Common::FR_FRA },
+ { "MAIN_GER.CPS", GF_GERMAN, Common::DE_DEU },
+ { "MAIN_SPA.CPS", GF_SPANISH, Common::ES_ESP },
+ { 0, 0, Common::UNK_LANG }
+};
+
+static Common::Language convertKyraLang(uint32 features) {
+ if (features & GF_ENGLISH) {
+ return Common::EN_USA;
+ } else if (features & GF_FRENCH) {
+ return Common::FR_FRA;
+ } else if (features & GF_GERMAN) {
+ return Common::DE_DEU;
+ } else if (features & GF_SPANISH) {
+ return Common::ES_ESP;
+ }
+ return Common::UNK_LANG;
+}
+
+GameList Engine_KYRA_gameList() {
+ GameList games;
+ const KyraGameList *g = kyra_list;
+
+ while (g->gameid) {
+ games.push_back(g->toGameSettings());
+ g++;
+ }
+ return games;
+}
+
+DetectedGameList Engine_KYRA_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+ const KyraGameSettings *g;
+ FSList::const_iterator file;
+
+ // Iterate over all files in the given directory
+ bool isFound = false;
+ for (file = fslist.begin(); file != fslist.end(); file++) {
+ if (file->isDirectory())
+ continue;
+
+ for (g = kyra_games; g->gameid; g++) {
+ if (scumm_stricmp(file->displayName().c_str(), g->checkFile) == 0)
+ isFound = true;
+ }
+ if (isFound)
+ break;
+ }
+
+ if (file == fslist.end())
+ return detectedGames;
+
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+
+ if (Common::md5_file(file->path().c_str(), md5sum, NULL, kMD5FileSizeLimit)) {
+ for (int i = 0; i < 16; i++) {
+ sprintf(md5str + i * 2, "%02x", (int)md5sum[i]);
+ }
+ for (g = kyra_games; g->gameid; g++) {
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ detectedGames.push_back(DetectedGame(g->toGameSettings(), convertKyraLang(g->features), Common::kPlatformUnknown));
+ }
+ }
+ if (detectedGames.isEmpty()) {
+ debug("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team\n", md5str);
+
+ const KyraGameList *g1 = kyra_list;
+ while (g1->gameid) {
+ detectedGames.push_back(g1->toGameSettings());
+ g1++;
+ }
+ }
+ }
+ return detectedGames;
+}
+
+Engine *Engine_KYRA_create(GameDetector *detector, OSystem *system) {
+ return new KyraEngine(detector, system);
+}
+
+REGISTER_PLUGIN(KYRA, "Legend of Kyrandia Engine")
+
+namespace Kyra {
+
+KyraEngine::KyraEngine(GameDetector *detector, OSystem *system)
+ : Engine(system) {
+ _seq_Forest = _seq_KallakWriting = _seq_KyrandiaLogo = _seq_KallakMalcolm =
+ _seq_MalcolmTree = _seq_WestwoodLogo = _seq_Demo1 = _seq_Demo2 = _seq_Demo3 =
+ _seq_Demo4 = 0;
+
+ _seq_WSATable = _seq_CPSTable = _seq_COLTable = _seq_textsTable = 0;
+ _seq_WSATable_Size = _seq_CPSTable_Size = _seq_COLTable_Size = _seq_textsTable_Size = 0;
+
+ _roomFilenameTable = _characterImageTable = 0;
+ _roomFilenameTableSize = _characterImageTableSize = 0;
+ _itemList = _takenList = _placedList = _droppedList = _noDropList = 0;
+ _itemList_Size = _takenList_Size = _placedList_Size = _droppedList_Size = _noDropList_Size = 0;
+ _putDownFirst = _waitForAmulet = _blackJewel = _poisonGone = _healingTip = 0;
+ _putDownFirst_Size = _waitForAmulet_Size = _blackJewel_Size = _poisonGone_Size = _healingTip_Size = 0;
+ _thePoison = _fluteString = _wispJewelStrings = _magicJewelString = _flaskFull = _fullFlask = 0;
+ _thePoison_Size = _fluteString_Size = _wispJewelStrings_Size = 0;
+ _magicJewelString_Size = _flaskFull_Size = _fullFlask_Size = 0;
+
+ _defaultShapeTable = _healingShapeTable = _healingShape2Table = 0;
+ _defaultShapeTableSize = _healingShapeTableSize = _healingShape2TableSize = 0;
+ _posionDeathShapeTable = _fluteAnimShapeTable = 0;
+ _posionDeathShapeTableSize = _fluteAnimShapeTableSize = 0;
+ _winterScrollTable = _winterScroll1Table = _winterScroll2Table = 0;
+ _winterScrollTableSize = _winterScroll1TableSize = _winterScroll2TableSize = 0;
+ _drinkAnimationTable = _brandonToWispTable = _magicAnimationTable = _brandonStoneTable = 0;
+ _drinkAnimationTableSize = _brandonToWispTableSize = _magicAnimationTableSize = _brandonStoneTableSize = 0;
+
+ // Setup mixer
+ if (!_mixer->isReady()) {
+ warning("Sound initialization failed.");
+ }
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
+
+ // Detect game features based on MD5. Again brutally ripped from Gobliins.
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+
+ const KyraGameSettings *g;
+ bool found = false;
+
+ // TODO
+ // Fallback. Maybe we will be able to determine game type from game
+ // data contents
+ _features = 0;
+
+ for (g = kyra_games; g->gameid; g++) {
+ if (!Common::File::exists(g->checkFile))
+ continue;
+
+ if (Common::md5_file(g->checkFile, md5sum, ConfMan.get("path").c_str(), kMD5FileSizeLimit)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+ } else
+ continue;
+
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ _features = g->features;
+ _game = g->id;
+
+ if (g->description)
+ g_system->setWindowCaption(g->description);
+
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ debug("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team", md5str);
+ _features = 0;
+ _game = GI_KYRA1;
+ Common::File test;
+ if (test.open("INTRO.VRM")) {
+ _features |= GF_TALKIE;
+ } else {
+ _features |= GF_FLOPPY;
+ }
+
+ // tries to detect the language
+ const KyraLanguageTable *lang = kyra_languages;
+ for (; lang->file; ++lang) {
+ if (test.open(lang->file)) {
+ _features |= lang->language;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ _features |= GF_LNGUNK;
+ }
+ }
+}
+
+int KyraEngine::init(GameDetector &detector) {
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ //for debug reasons (see Screen::updateScreen)
+ //_system->initSize(640, 200);
+ _system->initSize(320, 200);
+ _system->endGFXTransaction();
+
+ // for now we prefer MIDI-to-Adlib conversion over native midi
+ int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB/* | MDT_PREFER_MIDI*/);
+ bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+
+ MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ if (midiDriver == MD_ADLIB) {
+ // In this case we should play the Adlib tracks, but for now
+ // the automagic MIDI-to-Adlib conversion will do.
+ } else if (native_mt32) {
+ driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+ }
+
+ _sound = new SoundPC(driver, _mixer, this);
+ assert(_sound);
+ static_cast<SoundPC*>(_sound)->hasNativeMT32(native_mt32);
+ _sound->setVolume(255);
+
+ _saveFileMan = _system->getSavefileManager();
+ assert(_saveFileMan);
+ _res = new Resource(this);
+ assert(_res);
+ _screen = new Screen(this, _system);
+ assert(_screen);
+ _sprites = new Sprites(this, _system);
+ assert(_sprites);
+ _seq = new SeqPlayer(this, _system);
+ assert(_seq);
+ _animator = new ScreenAnimator(this, _system);
+ assert(_animator);
+ _animator->init(5, 11, 12);
+ assert(*_animator);
+ _text = new TextDisplayer(_screen);
+ assert(_text);
+
+ _paletteChanged = 1;
+ _currentCharacter = 0;
+ _characterList = new Character[11];
+ assert(_characterList);
+ for (int i = 0; i < 11; ++i) {
+ memset(&_characterList[i], 0, sizeof(Character));
+ memset(_characterList[i].inventoryItems, 0xFF, sizeof(_characterList[i].inventoryItems));
+ }
+ _characterList[0].sceneId = 5;
+ _characterList[0].height = 48;
+ _characterList[0].facing = 3;
+ _characterList[0].currentAnimFrame = 7;
+
+ _scriptInterpreter = new ScriptHelper(this);
+ assert(_scriptInterpreter);
+
+ _npcScriptData = new ScriptData;
+ memset(_npcScriptData, 0, sizeof(ScriptData));
+ assert(_npcScriptData);
+ _npcScript = new ScriptState;
+ assert(_npcScript);
+ memset(_npcScript, 0, sizeof(ScriptState));
+
+ _scriptMain = new ScriptState;
+ assert(_scriptMain);
+ memset(_scriptMain, 0, sizeof(ScriptState));
+
+ _scriptClickData = new ScriptData;
+ assert(_scriptClickData);
+ memset(_scriptClickData, 0, sizeof(ScriptData));
+ _scriptClick = new ScriptState;
+ assert(_scriptClick);
+ memset(_scriptClick, 0, sizeof(ScriptState));
+
+ _debugger = new Debugger(this);
+ assert(_debugger);
+ memset(_shapes, 0, sizeof(_shapes));
+
+ for (int i = 0; i < ARRAYSIZE(_movieObjects); ++i) {
+ _movieObjects[i] = createWSAMovie();
+ }
+
+ memset(_flagsTable, 0, sizeof(_flagsTable));
+
+ _abortWalkFlag = false;
+ _abortWalkFlag2 = false;
+ _talkingCharNum = -1;
+ _charSayUnk3 = -1;
+ _mouseX = _mouseY = -1;
+ memset(_currSentenceColor, 0, 3);
+ _startSentencePalIndex = -1;
+ _fadeText = false;
+
+ _cauldronState = 0;
+ _crystalState[0] = _crystalState[1] = -1;
+
+ _brandonStatusBit = 0;
+ _brandonStatusBit0x02Flag = _brandonStatusBit0x20Flag = 10;
+ _brandonPosX = _brandonPosY = -1;
+ _deathHandler = 0xFF;
+ _poisonDeathCounter = 0;
+
+ memset(_itemTable, 0, sizeof(_itemTable));
+ memset(_exitList, 0xFFFF, sizeof(_exitList));
+ _exitListPtr = 0;
+ _pathfinderFlag = _pathfinderFlag2 = 0;
+ _lastFindWayRet = 0;
+ _sceneChangeState = _loopFlag2 = 0;
+ _timerNextRun = 0;
+
+ _movFacingTable = new int[150];
+ assert(_movFacingTable);
+ _movFacingTable[0] = 8;
+
+ _configTalkspeed = 1;
+
+ _marbleVaseItem = -1;
+ memset(_foyerItemTable, -1, sizeof(_foyerItemTable));
+ _mouseState = _itemInHand = -1;
+ _handleInput = false;
+
+ _currentRoom = 0xFFFF;
+ _scenePhasingFlag = 0;
+ _lastProcessedItem = 0;
+ _lastProcessedItemHeight = 16;
+
+ _unkScreenVar1 = 1;
+ _unkScreenVar2 = 0;
+ _unkScreenVar3 = 0;
+ _unkAmuletVar = 0;
+
+ _endSequenceNeedLoading = 1;
+ _malcolmFlag = 0;
+ _beadStateVar = 0;
+ _endSequenceSkipFlag = 0;
+ _unkEndSeqVar2 = 0;
+ _endSequenceBackUpRect = 0;
+ _unkEndSeqVar4 = 0;
+ _unkEndSeqVar5 = 0;
+ _lastDisplayedPanPage = 0;
+ memset(_panPagesTable, 0, sizeof(_panPagesTable));
+ _finalA = _finalB = _finalC = 0;
+ memset(&_kyragemFadingState, 0, sizeof(_kyragemFadingState));
+ _kyragemFadingState.gOffset = 0x13;
+ _kyragemFadingState.bOffset = 0x13;
+
+ memset(_specialPalettes, 0, sizeof(_specialPalettes));
+ _mousePressFlag = false;
+
+ _targetName = detector._targetName;
+ _menuDirectlyToLoad = false;
+
+ _lastMusicCommand = 0;
+
+ _gameSpeed = 60;
+ _tickLength = (uint8)(1000.0 / _gameSpeed);
+
+ return 0;
+}
+
+KyraEngine::~KyraEngine() {
+ closeFinalWsa();
+ _scriptInterpreter->unloadScript(_npcScriptData);
+ _scriptInterpreter->unloadScript(_scriptClickData);
+
+ delete _debugger;
+ delete _sprites;
+ delete _animator;
+ delete _screen;
+ delete _res;
+ delete _sound;
+ delete _saveFileMan;
+ delete _seq;
+ delete _scriptInterpreter;
+ delete _text;
+
+ delete _npcScriptData;
+ delete _scriptMain;
+
+ delete _scriptClickData;
+ delete _scriptClick;
+
+ delete [] _characterList;
+
+ delete [] _movFacingTable;
+
+ free(_scrollUpButton.process0PtrShape);
+ free(_scrollUpButton.process1PtrShape);
+ free(_scrollUpButton.process2PtrShape);
+ free(_scrollDownButton.process0PtrShape);
+ free(_scrollDownButton.process1PtrShape);
+ free(_scrollDownButton.process2PtrShape);
+
+ for (int i = 0; i < ARRAYSIZE(_shapes); ++i) {
+ if (_shapes[i] != 0) {
+ free(_shapes[i]);
+ _shapes[i] = 0;
+ for (int i2 = 0; i2 < ARRAYSIZE(_shapes); i2++) {
+ if (_shapes[i2] == _shapes[i] && i2 != i) {
+ _shapes[i2] = 0;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < ARRAYSIZE(_sceneAnimTable); ++i) {
+ free(_sceneAnimTable[i]);
+ }
+}
+
+void KyraEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+}
+
+int KyraEngine::go() {
+ _quitFlag = false;
+ uint32 sz;
+
+ res_loadResources();
+ if (_features & GF_FLOPPY) {
+ _screen->loadFont(Screen::FID_6_FNT, _res->fileData("6.FNT", &sz));
+ }
+ _screen->loadFont(Screen::FID_8_FNT, _res->fileData("8FAT.FNT", &sz));
+ _screen->setScreenDim(0);
+
+ _abortIntroFlag = false;
+
+ if (_features & GF_DEMO) {
+ seq_demo();
+ } else {
+ setGameFlag(0xF3);
+ setGameFlag(0xFD);
+ setGameFlag(0xEF);
+ seq_intro();
+ if (_skipIntroFlag &&_abortIntroFlag)
+ resetGameFlag(0xEF);
+ startup();
+ resetGameFlag(0xEF);
+ mainLoop();
+ }
+ quitGame();
+ return 0;
+}
+
+void KyraEngine::startup() {
+ debug(9, "KyraEngine::startup()");
+ static const uint8 colorMap[] = { 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0 };
+ _screen->setTextColorMap(colorMap);
+// _screen->setFont(Screen::FID_6_FNT);
+ _screen->setAnimBlockPtr(3750);
+ memset(_sceneAnimTable, 0, sizeof(_sceneAnimTable));
+ loadMouseShapes();
+ _currentCharacter = &_characterList[0];
+ for (int i = 1; i < 5; ++i)
+ _animator->setCharacterDefaultFrame(i);
+ for (int i = 5; i <= 10; ++i)
+ setCharactersPositions(i);
+ _animator->setCharactersHeight();
+ resetBrandonPoisonFlags();
+ _maskBuffer = _screen->getPagePtr(5);
+ _screen->_curPage = 0;
+ // XXX
+ for (int i = 0; i < 0x0C; ++i) {
+ int size = _screen->getRectSize(3, 24);
+ _shapes[365+i] = (byte*)malloc(size);
+ }
+ _shapes[0] = (uint8*)malloc(_screen->getRectSize(3, 24));
+ memset(_shapes[0], 0, _screen->getRectSize(3, 24));
+ _shapes[1] = (uint8*)malloc(_screen->getRectSize(4, 32));
+ memset(_shapes[1], 0, _screen->getRectSize(4, 32));
+ _shapes[2] = (uint8*)malloc(_screen->getRectSize(8, 69));
+ memset(_shapes[2], 0, _screen->getRectSize(8, 69));
+ _shapes[3] = (uint8*)malloc(_screen->getRectSize(8, 69));
+ memset(_shapes[3], 0, _screen->getRectSize(8, 69));
+ for (int i = 0; i < _roomTableSize; ++i) {
+ for (int item = 0; item < 12; ++item) {
+ _roomTable[i].itemsTable[item] = 0xFF;
+ _roomTable[i].itemsXPos[item] = 0xFFFF;
+ _roomTable[i].itemsYPos[item] = 0xFF;
+ _roomTable[i].needInit[item] = 0;
+ }
+ }
+ loadCharacterShapes();
+ loadSpecialEffectShapes();
+ loadItems();
+ loadButtonShapes();
+ initMainButtonList();
+ loadMainScreen();
+ setupTimers();
+ loadPalette("PALETTE.COL", _screen->_currentPalette);
+
+ // XXX
+ _animator->initAnimStateList();
+ setCharactersInDefaultScene();
+
+ if (!_scriptInterpreter->loadScript("_STARTUP.EMC", _npcScriptData, _opcodeTable, _opcodeTableSize, 0)) {
+ error("Could not load \"_STARTUP.EMC\" script");
+ }
+ _scriptInterpreter->initScript(_scriptMain, _npcScriptData);
+ if (!_scriptInterpreter->startScript(_scriptMain, 0)) {
+ error("Could not start script function 0 of script \"_STARTUP.EMC\"");
+ }
+ while (_scriptInterpreter->validScript(_scriptMain)) {
+ _scriptInterpreter->runScript(_scriptMain);
+ }
+
+ _scriptInterpreter->unloadScript(_npcScriptData);
+ if (!_scriptInterpreter->loadScript("_NPC.EMC", _npcScriptData, _opcodeTable, _opcodeTableSize, 0)) {
+ error("Could not load \"_NPC.EMC\" script");
+ }
+
+ snd_playTheme(1);
+ snd_setSoundEffectFile(1);
+ enterNewScene(_currentCharacter->sceneId, _currentCharacter->facing, 0, 0, 1);
+
+ if (_abortIntroFlag && _skipFlag) {
+ _menuDirectlyToLoad = true;
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ buttonMenuCallback(0);
+ _menuDirectlyToLoad = false;
+ } else
+ saveGame(getSavegameFilename(0), "New game");
+}
+
+void KyraEngine::mainLoop() {
+ debug(9, "KyraEngine::mainLoop()");
+
+ while (!_quitFlag) {
+ int32 frameTime = (int32)_system->getMillis();
+ _skipFlag = false;
+
+ if (_currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ if (seq_playEnd()) {
+ if (_deathHandler != 8)
+ break;
+ }
+ }
+
+ if (_deathHandler != 0xFF) {
+ // this is only used until the original gui is implemented
+ GUI::MessageDialog dialog("Brandon is dead! Game over!", "Quit");
+ dialog.runModal();
+ break;
+ }
+
+ if (_brandonStatusBit & 2) {
+ if (_brandonStatusBit0x02Flag)
+ _animator->animRefreshNPC(0);
+ }
+ if (_brandonStatusBit & 0x20) {
+ if (_brandonStatusBit0x20Flag) {
+ _animator->animRefreshNPC(0);
+ _brandonStatusBit0x20Flag = 0;
+ }
+ }
+
+ _screen->showMouse();
+
+ processButtonList(_buttonList);
+ updateMousePointer();
+ updateGameTimers();
+ updateTextFade();
+
+ _handleInput = true;
+ delay((frameTime + _gameSpeed) - _system->getMillis(), true, true);
+ _handleInput = false;
+ }
+}
+
+void KyraEngine::quitGame() {
+ res_unloadResources(RES_ALL);
+
+ for (int i = 0; i < ARRAYSIZE(_movieObjects); ++i) {
+ _movieObjects[i]->close();
+ delete _movieObjects[i];
+ _movieObjects[i] = 0;
+ }
+
+ _system->quit();
+}
+
+void KyraEngine::delay(uint32 amount, bool update, bool isMainLoop) {
+ OSystem::Event event;
+ char saveLoadSlot[20];
+ char savegameName[14];
+
+ _mousePressFlag = false;
+ uint32 start = _system->getMillis();
+ do {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.keycode >= '1' && event.kbd.keycode <= '9' &&
+ (event.kbd.flags == OSystem::KBD_CTRL || event.kbd.flags == OSystem::KBD_ALT) && isMainLoop) {
+ sprintf(saveLoadSlot, "%s.00%d", _targetName.c_str(), event.kbd.keycode - '0');
+ if (event.kbd.flags == OSystem::KBD_CTRL)
+ loadGame(saveLoadSlot);
+ else {
+ sprintf(savegameName, "Quicksave %d", event.kbd.keycode - '0');
+ saveGame(saveLoadSlot, savegameName);
+ }
+ } else if (event.kbd.flags == OSystem::KBD_CTRL) {
+ if (event.kbd.keycode == 'd')
+ _debugger->attach();
+ else if (event.kbd.keycode == 'q')
+ _quitFlag = true;
+ } else if (event.kbd.keycode == '.')
+ _skipFlag = true;
+ else if (event.kbd.keycode == 13 || event.kbd.keycode == 32 || event.kbd.keycode == 27) {
+ _abortIntroFlag = true;
+ _skipFlag = true;
+ }
+
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _system->updateScreen();
+ break;
+ case OSystem::EVENT_QUIT:
+ quitGame();
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ _mousePressFlag = true;
+ if (_abortWalkFlag2) {
+ _abortWalkFlag = true;
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ }
+ if (_handleInput) {
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ _handleInput = false;
+ processInput(_mouseX, _mouseY);
+ _handleInput = true;
+ } else
+ _skipFlag = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (_debugger->isAttached())
+ _debugger->onFrame();
+
+ if (update)
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+
+ if (_currentCharacter && _currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ }
+
+ if (amount > 0 && !_skipFlag) {
+ _system->delayMillis((amount > 10) ? 10 : amount);
+ }
+ } while (!_skipFlag && _system->getMillis() < start + amount);
+
+}
+
+void KyraEngine::waitForEvent() {
+ bool finished = false;
+ OSystem::Event event;
+ while (!finished) {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ finished = true;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ break;
+ case OSystem::EVENT_QUIT:
+ quitGame();
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ finished = true;
+ _skipFlag = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (_debugger->isAttached())
+ _debugger->onFrame();
+
+ _system->delayMillis(10);
+ }
+}
+
+void KyraEngine::delayWithTicks(int ticks) {
+ uint32 nextTime = _system->getMillis() + ticks * _tickLength;
+ while (_system->getMillis() < nextTime && !_skipFlag) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (_currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ seq_playEnd();
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark - Animation/shape specific code
+#pragma mark -
+
+void KyraEngine::setupShapes123(const Shape *shapeTable, int endShape, int flags) {
+ debug(9, "KyraEngine::setupShapes123(0x%X, startShape, flags)", shapeTable, endShape, flags);
+ for (int i = 123; i <= 172; ++i) {
+ _shapes[4+i] = NULL;
+ }
+ uint8 curImage = 0xFF;
+ int curPageBackUp = _screen->_curPage;
+ _screen->_curPage = 8; // we are using page 8 here in the original page 2 was backuped and then used for this stuff
+ int shapeFlags = 2;
+ if (flags)
+ shapeFlags = 3;
+ for (int i = 123; i < 123+endShape; ++i) {
+ uint8 newImage = shapeTable[i-123].imageIndex;
+ if (newImage != curImage && newImage != 0xFF) {
+ assert(_characterImageTable);
+ loadBitmap(_characterImageTable[newImage], 8, 8, 0);
+ curImage = newImage;
+ }
+ _shapes[4+i] = _screen->encodeShape(shapeTable[i-123].x<<3, shapeTable[i-123].y, shapeTable[i-123].w<<3, shapeTable[i-123].h, shapeFlags);
+ assert(i-7 < _defaultShapeTableSize);
+ _defaultShapeTable[i-7].xOffset = shapeTable[i-123].xOffset;
+ _defaultShapeTable[i-7].yOffset = shapeTable[i-123].yOffset;
+ _defaultShapeTable[i-7].w = shapeTable[i-123].w;
+ _defaultShapeTable[i-7].h = shapeTable[i-123].h;
+ }
+ _screen->_curPage = curPageBackUp;
+}
+
+void KyraEngine::freeShapes123() {
+ debug(9, "KyraEngine::freeShapes123()");
+ for (int i = 123; i <= 172; ++i) {
+ free(_shapes[4+i]);
+ _shapes[4+i] = NULL;
+ }
+}
+
+#pragma mark -
+#pragma mark - Misc stuff
+#pragma mark -
+
+Movie *KyraEngine::createWSAMovie() {
+ // for kyra2 here could be added then WSAMovieV2
+ return new WSAMovieV1(this);
+}
+
+int KyraEngine::setGameFlag(int flag) {
+ _flagsTable[flag >> 3] |= (1 << (flag & 7));
+ return 1;
+}
+
+int KyraEngine::queryGameFlag(int flag) {
+ return ((_flagsTable[flag >> 3] >> (flag & 7)) & 1);
+}
+
+int KyraEngine::resetGameFlag(int flag) {
+ _flagsTable[flag >> 3] &= ~(1 << (flag & 7));
+ return 0;
+}
+
+void KyraEngine::setBrandonPoisonFlags(int reset) {
+ debug(9, "KyraEngine::setBrandonPoisonFlags(%d)", reset);
+ _brandonStatusBit |= 1;
+ if (reset)
+ _poisonDeathCounter = 0;
+ for (int i = 0; i < 0x100; ++i) {
+ _brandonPoisonFlagsGFX[i] = i;
+ }
+ _brandonPoisonFlagsGFX[0x99] = 0x34;
+ _brandonPoisonFlagsGFX[0x9A] = 0x35;
+ _brandonPoisonFlagsGFX[0x9B] = 0x37;
+ _brandonPoisonFlagsGFX[0x9C] = 0x38;
+ _brandonPoisonFlagsGFX[0x9D] = 0x2B;
+}
+
+void KyraEngine::resetBrandonPoisonFlags() {
+ debug(9, "KyraEngine::resetBrandonPoisonFlags()");
+ _brandonStatusBit = 0;
+ for (int i = 0; i < 0x100; ++i) {
+ _brandonPoisonFlagsGFX[i] = i;
+ }
+}
+
+#pragma mark -
+#pragma mark - Input
+#pragma mark -
+
+void KyraEngine::processInput(int xpos, int ypos) {
+ debug(9, "KyraEngine::processInput(%d, %d)", xpos, ypos);
+ _abortWalkFlag2 = false;
+
+ if (processInputHelper(xpos, ypos)) {
+ return;
+ }
+ uint8 item = findItemAtPos(xpos, ypos);
+ if (item == 0xFF) {
+ _changedScene = false;
+ int handled = clickEventHandler(xpos, ypos);
+ if (_changedScene || handled)
+ return;
+ }
+
+ // XXX _deathHandler specific
+ if (ypos <= 158) {
+ uint16 exit = 0xFFFF;
+ if (xpos < 12) {
+ exit = _walkBlockWest;
+ } else if (xpos >= 308) {
+ exit = _walkBlockEast;
+ } else if (ypos >= 136) {
+ exit = _walkBlockSouth;
+ } else if (ypos < 12) {
+ exit = _walkBlockNorth;
+ }
+
+ if (exit != 0xFFFF) {
+ _abortWalkFlag2 = true;
+ handleSceneChange(xpos, ypos, 1, 1);
+ _abortWalkFlag2 = false;
+ return;
+ } else {
+ int script = checkForNPCScriptRun(xpos, ypos);
+ if (script >= 0) {
+ runNpcScript(script);
+ return;
+ }
+ if (_itemInHand != -1) {
+ if (ypos < 155) {
+ if (hasClickedOnExit(xpos, ypos)) {
+ _abortWalkFlag2 = true;
+ handleSceneChange(xpos, ypos, 1, 1);
+ _abortWalkFlag2 = false;
+ return;
+ }
+ dropItem(0, _itemInHand, xpos, ypos, 1);
+ }
+ } else {
+ if (ypos <= 155) {
+ _abortWalkFlag2 = true;
+ handleSceneChange(xpos, ypos, 1, 1);
+ _abortWalkFlag2 = false;
+ }
+ }
+ }
+ }
+}
+
+int KyraEngine::processInputHelper(int xpos, int ypos) {
+ debug(9, "KyraEngine::processInputHelper(%d, %d)", xpos, ypos);
+ uint8 item = findItemAtPos(xpos, ypos);
+ if (item != 0xFF) {
+ if (_itemInHand == -1) {
+ _screen->hideMouse();
+ _animator->animRemoveGameItem(item);
+ snd_playSoundEffect(53);
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ Room *currentRoom = &_roomTable[_currentCharacter->sceneId];
+ int item2 = currentRoom->itemsTable[item];
+ currentRoom->itemsTable[item] = 0xFF;
+ setMouseItem(item2);
+ assert(_itemList && _takenList);
+ updateSentenceCommand(_itemList[item2], _takenList[0], 179);
+ _itemInHand = item2;
+ _screen->showMouse();
+ clickEventHandler2();
+ return 1;
+ } else {
+ exchangeItemWithMouseItem(_currentCharacter->sceneId, item);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int KyraEngine::clickEventHandler(int xpos, int ypos) {
+ debug(9, "KyraEngine::clickEventHandler(%d, %d)", xpos, ypos);
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ _scriptClick->variables[1] = xpos;
+ _scriptClick->variables[2] = ypos;
+ _scriptClick->variables[3] = 0;
+ _scriptClick->variables[4] = _itemInHand;
+ _scriptInterpreter->startScript(_scriptClick, 1);
+
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+
+ return _scriptClick->variables[3];
+}
+
+void KyraEngine::updateMousePointer(bool forceUpdate) {
+ int shape = 0;
+
+ int newMouseState = 0;
+ int newX = 0;
+ int newY = 0;
+ if (_mouseY <= 158) {
+ if (_mouseX >= 12) {
+ if (_mouseX >= 308) {
+ if (_walkBlockEast == 0xFFFF) {
+ newMouseState = -2;
+ } else {
+ newMouseState = -5;
+ shape = 3;
+ newX = 7;
+ newY = 5;
+ }
+ } else if (_mouseY >= 136) {
+ if (_walkBlockSouth == 0xFFFF) {
+ newMouseState = -2;
+ } else {
+ newMouseState = -4;
+ shape = 4;
+ newX = 5;
+ newY = 7;
+ }
+ } else if (_mouseY < 12) {
+ if (_walkBlockNorth == 0xFFFF) {
+ newMouseState = -2;
+ } else {
+ newMouseState = -6;
+ shape = 2;
+ newX = 5;
+ newY = 1;
+ }
+ }
+ } else {
+ if (_walkBlockWest == 0xFFFF) {
+ newMouseState = -2;
+ } else {
+ newMouseState = -3;
+ newX = 1;
+ newY = shape = 5;
+ }
+ }
+ }
+
+ if (_mouseX >= _entranceMouseCursorTracks[0] && _mouseY >= _entranceMouseCursorTracks[1]
+ && _mouseX <= _entranceMouseCursorTracks[2] && _mouseY <= _entranceMouseCursorTracks[3]) {
+ switch (_entranceMouseCursorTracks[4]) {
+ case 0:
+ newMouseState = -6;
+ shape = 2;
+ newX = 5;
+ newY = 1;
+ break;
+
+ case 2:
+ newMouseState = -5;
+ shape = 3;
+ newX = 7;
+ newY = 5;
+ break;
+
+ case 4:
+ newMouseState = -4;
+ shape = 4;
+ newX = 5;
+ newY = 7;
+ break;
+
+ case 6:
+ newMouseState = -3;
+ shape = 5;
+ newX = 1;
+ newY = 5;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (newMouseState == -2) {
+ shape = 6;
+ newX = 4;
+ newY = 4;
+ }
+
+ if ((newMouseState && _mouseState != newMouseState) || (newMouseState && forceUpdate)) {
+ _mouseState = newMouseState;
+ _screen->hideMouse();
+ _screen->setMouseCursor(newX, newY, _shapes[4+shape]);
+ _screen->showMouse();
+ }
+
+ if (!newMouseState) {
+ if (_mouseState != _itemInHand || forceUpdate) {
+ if (_mouseY > 158 || (_mouseX >= 12 && _mouseX < 308 && _mouseY < 136 && _mouseY >= 12) || forceUpdate) {
+ _mouseState = _itemInHand;
+ _screen->hideMouse();
+ if (_itemInHand == -1) {
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ } else {
+ _screen->setMouseCursor(8, 15, _shapes[220+_itemInHand]);
+ }
+ _screen->showMouse();
+ }
+ }
+ }
+}
+
+bool KyraEngine::hasClickedOnExit(int xpos, int ypos) {
+ debug(9, "KyraEngine::hasClickedOnExit(%d, %d)", xpos, ypos);
+ if (xpos < 16 || xpos >= 304) {
+ return true;
+ }
+ if (ypos < 8)
+ return true;
+ if (ypos < 136 || ypos > 155) {
+ return false;
+ }
+ return true;
+}
+
+void KyraEngine::clickEventHandler2() {
+ debug(9, "KyraEngine::clickEventHandler2()");
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ _scriptClick->variables[0] = _currentCharacter->sceneId;
+ _scriptClick->variables[1] = _mouseX;
+ _scriptClick->variables[2] = _mouseY;
+ _scriptClick->variables[4] = _itemInHand;
+ _scriptInterpreter->startScript(_scriptClick, 6);
+
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+}
+
+int KyraEngine::checkForNPCScriptRun(int xpos, int ypos) {
+ debug(9, "KyraEngine::checkForNPCScriptRun(%d, %d)", xpos, ypos);
+ int returnValue = -1;
+ const Character *currentChar = _currentCharacter;
+ int charLeft = 0, charRight = 0, charTop = 0, charBottom = 0;
+
+ int scaleFactor = _scaleTable[currentChar->y1];
+ int addX = (((scaleFactor*8)*3)>>8)>>1;
+ int addY = ((scaleFactor*3)<<4)>>8;
+
+ charLeft = currentChar->x1 - addX;
+ charRight = currentChar->x1 + addX;
+ charTop = currentChar->y1 - addY;
+ charBottom = currentChar->y1;
+
+ if (xpos >= charLeft && charRight >= xpos && charTop <= ypos && charBottom >= ypos) {
+ return 0;
+ }
+
+ if (xpos > 304 || xpos < 16) {
+ return -1;
+ }
+
+ for (int i = 1; i < 5; ++i) {
+ currentChar = &_characterList[i];
+
+ if (currentChar->sceneId != _currentCharacter->sceneId)
+ continue;
+
+ charLeft = currentChar->x1 - 12;
+ charRight = currentChar->x1 + 11;
+ charTop = currentChar->y1 - 48;
+ // if (!i) {
+ // charBottom = currentChar->y2 - 16;
+ // } else {
+ charBottom = currentChar->y1;
+ // }
+
+ if (xpos < charLeft || xpos > charRight || ypos < charTop || charBottom < ypos) {
+ continue;
+ }
+
+ if (returnValue != -1) {
+ if (currentChar->y1 >= _characterList[returnValue].y1) {
+ returnValue = i;
+ }
+ } else {
+ returnValue = i;
+ }
+ }
+
+ return returnValue;
+}
+
+void KyraEngine::runNpcScript(int func) {
+ debug(9, "KyraEngine::runNpcScript(%d)", func);
+ _scriptInterpreter->initScript(_npcScript, _npcScriptData);
+ _scriptInterpreter->startScript(_npcScript, func);
+ _npcScript->variables[0] = _currentCharacter->sceneId;
+ _npcScript->variables[4] = _itemInHand;
+ _npcScript->variables[5] = func;
+
+ while (_scriptInterpreter->validScript(_npcScript)) {
+ _scriptInterpreter->runScript(_npcScript);
+ }
+}
+} // End of namespace Kyra
diff --git a/engines/kyra/kyra.h b/engines/kyra/kyra.h
new file mode 100644
index 0000000000..f1112fa799
--- /dev/null
+++ b/engines/kyra/kyra.h
@@ -0,0 +1,961 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef KYRA_H
+#define KYRA_H
+
+#include "base/engine.h"
+#include "common/rect.h"
+#include "sound/mixer.h"
+#include "common/file.h"
+
+class AudioStream;
+
+namespace Kyra {
+
+class Movie;
+class Sound;
+class SeqPlayer;
+class Resource;
+class PAKFile;
+class Screen;
+class Sprites;
+class ScriptHelper;
+class Debugger;
+class ScreenAnimator;
+class TextDisplayer;
+class KyraEngine;
+
+struct ScriptState;
+struct ScriptData;
+
+enum {
+ GF_FLOPPY = 1 << 0,
+ GF_TALKIE = 1 << 1,
+ GF_AUDIOCD = 1 << 2, // FM-Towns versions seems to use audio CD
+ GF_DEMO = 1 << 3,
+ GF_ENGLISH = 1 << 4,
+ GF_FRENCH = 1 << 5,
+ GF_GERMAN = 1 << 6,
+ GF_SPANISH = 1 << 7,
+ // other languages here
+ GF_LNGUNK = 1 << 16
+};
+
+enum {
+ GI_KYRA1 = 0
+};
+
+struct Character {
+ uint16 sceneId;
+ uint8 height;
+ uint8 facing;
+ uint16 currentAnimFrame;
+ uint8 inventoryItems[10];
+ int16 x1, y1, x2, y2;
+};
+
+struct Shape {
+ uint8 imageIndex;
+ int8 xOffset, yOffset;
+ uint8 x, y, w, h;
+};
+
+struct Room {
+ uint8 nameIndex;
+ uint16 northExit;
+ uint16 eastExit;
+ uint16 southExit;
+ uint16 westExit;
+ uint8 itemsTable[12];
+ uint16 itemsXPos[12];
+ uint8 itemsYPos[12];
+ uint8 needInit[12];
+};
+
+struct Rect {
+ int x, y;
+ int x2, y2;
+};
+
+struct Item {
+ uint8 unk1;
+ uint8 height;
+ uint8 unk2;
+ uint8 unk3;
+};
+
+struct SeqLoop {
+ const uint8 *ptr;
+ uint16 count;
+};
+
+struct SceneExits {
+ uint16 northXPos;
+ uint8 northYPos;
+ uint16 eastXPos;
+ uint8 eastYPos;
+ uint16 southXPos;
+ uint8 southYPos;
+ uint16 westXPos;
+ uint8 westYPos;
+};
+
+struct BeadState {
+ int16 x;
+ int16 y;
+ int16 width;
+ int16 height;
+ int16 dstX;
+ int16 dstY;
+ int16 width2;
+ int16 unk8;
+ int16 unk9;
+ int16 tableIndex;
+};
+
+struct Timer {
+ uint8 active;
+ int32 countdown;
+ uint32 nextRun;
+ void (KyraEngine::*func)(int timerNum);
+};
+
+struct Button {
+ Button *nextButton;
+ uint16 specialValue;
+ // uint8 unk[4];
+ uint8 process0;
+ uint8 process1;
+ uint8 process2;
+ // uint8 unk
+ uint16 flags;
+ typedef int (KyraEngine::*ButtonCallback)(Button*);
+ // using 6 pointers instead of 3 as in the orignal here (safer for use with classes)
+ uint8 *process0PtrShape;
+ uint8 *process1PtrShape;
+ uint8 *process2PtrShape;
+ ButtonCallback process0PtrCallback;
+ ButtonCallback process1PtrCallback;
+ ButtonCallback process2PtrCallback;
+ uint16 dimTableIndex;
+ uint16 x;
+ uint16 y;
+ uint16 width;
+ uint16 height;
+ // uint8 unk[8];
+ uint32 flags2;
+ ButtonCallback buttonCallback;
+ // uint8 unk[8];
+};
+
+struct MenuItem {
+ bool enabled;
+ uint16 field_1;
+ uint8 field_3;
+ const char *itemString;
+ int16 x;
+ int16 field_9;
+ uint16 y;
+ uint16 width;
+ uint16 height;
+ uint8 textColor;
+ uint8 highlightColor;
+ int16 field_12;
+ uint8 field_13;
+ uint8 bgcolor;
+ uint8 color1;
+ uint8 color2;
+ int (KyraEngine::*callback)(Button*);
+ int16 field_1b;
+ const char *labelString;
+ uint16 field_21;
+ uint8 field_23;
+ uint8 field_24;
+ uint32 field_25;
+};
+
+struct Menu {
+ int16 x;
+ int16 y;
+ uint16 width;
+ uint16 height;
+ uint8 bgcolor;
+ uint8 color1;
+ uint8 color2;
+ const char *menuName;
+ uint8 textColor;
+ int16 field_10;
+ uint16 field_12;
+ uint16 highlightedItem;
+ uint8 nrOfItems;
+ int16 scrollUpBtnX;
+ int16 scrollUpBtnY;
+ int16 scrollDownBtnX;
+ int16 scrollDownBtnY;
+ MenuItem item[6];
+};
+
+struct KeyboardEvent {
+ bool pending;
+ uint32 repeat;
+ uint8 ascii;
+};
+
+class KyraEngine : public Engine {
+ friend class MusicPlayer;
+ friend class Debugger;
+ friend class ScreenAnimator;
+public:
+
+ enum {
+ MUSIC_INTRO = 0
+ };
+
+ KyraEngine(GameDetector *detector, OSystem *system);
+ ~KyraEngine();
+
+ void errorString(const char *buf_input, char *buf_output);
+
+ Resource *resource() { return _res; }
+ Screen *screen() { return _screen; }
+ ScreenAnimator *animator() { return _animator; }
+ TextDisplayer *text() { return _text; }
+ Sound *sound() { return _sound; }
+ uint32 tickLength() const { return _tickLength; }
+ Movie *createWSAMovie();
+
+ uint8 game() const { return _game; }
+ uint32 features() const { return _features; }
+
+ uint8 **shapes() { return _shapes; }
+ Character *currentCharacter() { return _currentCharacter; }
+ Character *characterList() { return _characterList; }
+ uint16 brandonStatus() { return _brandonStatusBit; }
+
+ int _paletteChanged;
+ Common::RandomSource _rnd;
+ int16 _northExitHeight;
+
+ typedef void (KyraEngine::*IntroProc)();
+ typedef int (KyraEngine::*OpcodeProc)(ScriptState *script);
+
+ const char **seqWSATable() { return const_cast<const char **>(_seq_WSATable); }
+ const char **seqCPSTable() { return const_cast<const char **>(_seq_CPSTable); }
+ const char **seqCOLTable() { return const_cast<const char **>(_seq_COLTable); }
+ const char **seqTextsTable() { return const_cast<const char **>(_seq_textsTable); }
+
+ const uint8 **palTable1() { return const_cast<const uint8 **>(&_specialPalettes[0]); }
+ const uint8 **palTable2() { return const_cast<const uint8 **>(&_specialPalettes[29]); }
+
+ bool seq_skipSequence() const;
+ void delay(uint32 millis, bool update = false, bool mainLoop = false);
+ void quitGame();
+ void loadBitmap(const char *filename, int tempPage, int dstPage, uint8 *palData);
+
+ void snd_playTheme(int file, int track = 0);
+ void snd_playVoiceFile(int id);
+ void snd_voiceWaitForFinish(bool ingame = true);
+ void snd_playSoundEffect(int track);
+ void snd_playWanderScoreViaMap(int command, int restart);
+
+ void drawSentenceCommand(char *sentence, int unk1);
+ void updateSentenceCommand(char *str1, char *str2, int unk1);
+ void updateTextFade();
+
+ void updateGameTimers();
+ void clearNextEventTickCount();
+ void setTimerCountdown(uint8 timer, int32 countdown);
+ void setTimerDelay(uint8 timer, int32 countdown);
+ int16 getTimerDelay(uint8 timer);
+ void enableTimer(uint8 timer);
+ void disableTimer(uint8 timer);
+
+ void waitTicks(int ticks);
+ void delayWithTicks(int ticks);
+
+ void saveGame(const char *fileName, const char *saveName);
+ void loadGame(const char *fileName);
+
+ int mouseX() { return _mouseX; }
+ int mouseY() { return _mouseY; }
+
+ // all opcode procs (maybe that is somehow useless atm)
+ int cmd_magicInMouseItem(ScriptState *script);
+ int cmd_characterSays(ScriptState *script);
+ int cmd_pauseTicks(ScriptState *script);
+ int cmd_drawSceneAnimShape(ScriptState *script);
+ int cmd_queryGameFlag(ScriptState *script);
+ int cmd_setGameFlag(ScriptState *script);
+ int cmd_resetGameFlag(ScriptState *script);
+ int cmd_runNPCScript(ScriptState *script);
+ int cmd_setSpecialExitList(ScriptState *script);
+ int cmd_blockInWalkableRegion(ScriptState *script);
+ int cmd_blockOutWalkableRegion(ScriptState *script);
+ int cmd_walkPlayerToPoint(ScriptState *script);
+ int cmd_dropItemInScene(ScriptState *script);
+ int cmd_drawAnimShapeIntoScene(ScriptState *script);
+ int cmd_createMouseItem(ScriptState *script);
+ int cmd_savePageToDisk(ScriptState *script);
+ int cmd_sceneAnimOn(ScriptState *script);
+ int cmd_sceneAnimOff(ScriptState *script);
+ int cmd_getElapsedSeconds(ScriptState *script);
+ int cmd_mouseIsPointer(ScriptState *script);
+ int cmd_destroyMouseItem(ScriptState *script);
+ int cmd_runSceneAnimUntilDone(ScriptState *script);
+ int cmd_fadeSpecialPalette(ScriptState *script);
+ int cmd_playAdlibSound(ScriptState *script);
+ int cmd_playAdlibScore(ScriptState *script);
+ int cmd_phaseInSameScene(ScriptState *script);
+ int cmd_setScenePhasingFlag(ScriptState *script);
+ int cmd_resetScenePhasingFlag(ScriptState *script);
+ int cmd_queryScenePhasingFlag(ScriptState *script);
+ int cmd_sceneToDirection(ScriptState *script);
+ int cmd_setBirthstoneGem(ScriptState *script);
+ int cmd_placeItemInGenericMapScene(ScriptState *script);
+ int cmd_setBrandonStatusBit(ScriptState *script);
+ int cmd_pauseSeconds(ScriptState *script);
+ int cmd_getCharactersLocation(ScriptState *script);
+ int cmd_runNPCSubscript(ScriptState *script);
+ int cmd_magicOutMouseItem(ScriptState *script);
+ int cmd_internalAnimOn(ScriptState *script);
+ int cmd_forceBrandonToNormal(ScriptState *script);
+ int cmd_poisonDeathNow(ScriptState *script);
+ int cmd_setScaleMode(ScriptState *script);
+ int cmd_openWSAFile(ScriptState *script);
+ int cmd_closeWSAFile(ScriptState *script);
+ int cmd_runWSAFromBeginningToEnd(ScriptState *script);
+ int cmd_displayWSAFrame(ScriptState *script);
+ int cmd_enterNewScene(ScriptState *script);
+ int cmd_setSpecialEnterXAndY(ScriptState *script);
+ int cmd_runWSAFrames(ScriptState *script);
+ int cmd_popBrandonIntoScene(ScriptState *script);
+ int cmd_restoreAllObjectBackgrounds(ScriptState *script);
+ int cmd_setCustomPaletteRange(ScriptState *script);
+ int cmd_loadPageFromDisk(ScriptState *script);
+ int cmd_customPrintTalkString(ScriptState *script);
+ int cmd_restoreCustomPrintBackground(ScriptState *script);
+ int cmd_hideMouse(ScriptState *script);
+ int cmd_showMouse(ScriptState *script);
+ int cmd_getCharacterX(ScriptState *script);
+ int cmd_getCharacterY(ScriptState *script);
+ int cmd_changeCharactersFacing(ScriptState *script);
+ int cmd_copyWSARegion(ScriptState *script);
+ int cmd_printText(ScriptState *script);
+ int cmd_random(ScriptState *script);
+ int cmd_loadSoundFile(ScriptState *script);
+ int cmd_displayWSAFrameOnHidPage(ScriptState *script);
+ int cmd_displayWSASequentialFrames(ScriptState *script);
+ int cmd_drawCharacterStanding(ScriptState *script);
+ int cmd_internalAnimOff(ScriptState *script);
+ int cmd_changeCharactersXAndY(ScriptState *script);
+ int cmd_clearSceneAnimatorBeacon(ScriptState *script);
+ int cmd_querySceneAnimatorBeacon(ScriptState *script);
+ int cmd_refreshSceneAnimator(ScriptState *script);
+ int cmd_placeItemInOffScene(ScriptState *script);
+ int cmd_wipeDownMouseItem(ScriptState *script);
+ int cmd_placeCharacterInOtherScene(ScriptState *script);
+ int cmd_getKey(ScriptState *script);
+ int cmd_specificItemInInventory(ScriptState *script);
+ int cmd_popMobileNPCIntoScene(ScriptState *script);
+ int cmd_mobileCharacterInScene(ScriptState *script);
+ int cmd_hideMobileCharacter(ScriptState *script);
+ int cmd_unhideMobileCharacter(ScriptState *script);
+ int cmd_setCharactersLocation(ScriptState *script);
+ int cmd_walkCharacterToPoint(ScriptState *script);
+ int cmd_specialEventDisplayBrynnsNote(ScriptState *script);
+ int cmd_specialEventRemoveBrynnsNote(ScriptState *script);
+ int cmd_setLogicPage(ScriptState *script);
+ int cmd_fatPrint(ScriptState *script);
+ int cmd_preserveAllObjectBackgrounds(ScriptState *script);
+ int cmd_updateSceneAnimations(ScriptState *script);
+ int cmd_sceneAnimationActive(ScriptState *script);
+ int cmd_setCharactersMovementDelay(ScriptState *script);
+ int cmd_getCharactersFacing(ScriptState *script);
+ int cmd_bkgdScrollSceneAndMasksRight(ScriptState *script);
+ int cmd_dispelMagicAnimation(ScriptState *script);
+ int cmd_findBrightestFireberry(ScriptState *script);
+ int cmd_setFireberryGlowPalette(ScriptState *script);
+ int cmd_setDeathHandlerFlag(ScriptState *script);
+ int cmd_drinkPotionAnimation(ScriptState *script);
+ int cmd_makeAmuletAppear(ScriptState *script);
+ int cmd_drawItemShapeIntoScene(ScriptState *script);
+ int cmd_setCharactersCurrentFrame(ScriptState *script);
+ int cmd_waitForConfirmationMouseClick(ScriptState *script);
+ int cmd_pageFlip(ScriptState *script);
+ int cmd_setSceneFile(ScriptState *script);
+ int cmd_getItemInMarbleVase(ScriptState *script);
+ int cmd_setItemInMarbleVase(ScriptState *script);
+ int cmd_addItemToInventory(ScriptState *script);
+ int cmd_intPrint(ScriptState *script);
+ int cmd_shakeScreen(ScriptState *script);
+ int cmd_createAmuletJewel(ScriptState *script);
+ int cmd_setSceneAnimCurrXY(ScriptState *script);
+ int cmd_poisonBrandonAndRemaps(ScriptState *script);
+ int cmd_fillFlaskWithWater(ScriptState *script);
+ int cmd_getCharactersMovementDelay(ScriptState *script);
+ int cmd_getBirthstoneGem(ScriptState *script);
+ int cmd_queryBrandonStatusBit(ScriptState *script);
+ int cmd_playFluteAnimation(ScriptState *script);
+ int cmd_playWinterScrollSequence(ScriptState *script);
+ int cmd_getIdolGem(ScriptState *script);
+ int cmd_setIdolGem(ScriptState *script);
+ int cmd_totalItemsInScene(ScriptState *script);
+ int cmd_restoreBrandonsMovementDelay(ScriptState *script);
+ int cmd_setMousePos(ScriptState *script);
+ int cmd_getMouseState(ScriptState *script);
+ int cmd_setEntranceMouseCursorTrack(ScriptState *script);
+ int cmd_itemAppearsOnGround(ScriptState *script);
+ int cmd_setNoDrawShapesFlag(ScriptState *script);
+ int cmd_fadeEntirePalette(ScriptState *script);
+ int cmd_itemOnGroundHere(ScriptState *script);
+ int cmd_queryCauldronState(ScriptState *script);
+ int cmd_setCauldronState(ScriptState *script);
+ int cmd_queryCrystalState(ScriptState *script);
+ int cmd_setCrystalState(ScriptState *script);
+ int cmd_setPaletteRange(ScriptState *script);
+ int cmd_shrinkBrandonDown(ScriptState *script);
+ int cmd_growBrandonUp(ScriptState *script);
+ int cmd_setBrandonScaleXAndY(ScriptState *script);
+ int cmd_resetScaleMode(ScriptState *script);
+ int cmd_getScaleDepthTableValue(ScriptState *script);
+ int cmd_setScaleDepthTableValue(ScriptState *script);
+ int cmd_message(ScriptState *script);
+ int cmd_checkClickOnNPC(ScriptState *script);
+ int cmd_getFoyerItem(ScriptState *script);
+ int cmd_setFoyerItem(ScriptState *script);
+ int cmd_setNoItemDropRegion(ScriptState *script);
+ int cmd_walkMalcolmOn(ScriptState *script);
+ int cmd_passiveProtection(ScriptState *script);
+ int cmd_setPlayingLoop(ScriptState *script);
+ int cmd_brandonToStoneSequence(ScriptState *script);
+ int cmd_brandonHealingSequence(ScriptState *script);
+ int cmd_protectCommandLine(ScriptState *script);
+ int cmd_pauseMusicSeconds(ScriptState *script);
+ int cmd_resetMaskRegion(ScriptState *script);
+ int cmd_setPaletteChangeFlag(ScriptState *script);
+ int cmd_fillRect(ScriptState *script);
+ int cmd_dummy(ScriptState *script);
+ int cmd_vocUnload(ScriptState *script);
+ int cmd_vocLoad(ScriptState *script);
+
+protected:
+
+ int go();
+ int init(GameDetector &detector);
+
+ void startup();
+ void mainLoop();
+ int initCharacterChat(int8 charNum);
+ int8 getChatPartnerNum();
+ void backupChatPartnerAnimFrame(int8 charNum);
+ void restoreChatPartnerAnimFrame(int8 charNum);
+ void endCharacterChat(int8 charNum, int16 arg_4);
+ void waitForChatToFinish(int16 chatDuration, char *str, uint8 charNum);
+ void characterSays(char *chatStr, int8 charNum, int8 chatDuration);
+
+ void setCharactersPositions(int character);
+ int setGameFlag(int flag);
+ int queryGameFlag(int flag);
+ int resetGameFlag(int flag);
+
+ void enterNewScene(int sceneId, int facing, int unk1, int unk2, int brandonAlive);
+ void transcendScenes(int roomIndex, int roomName);
+ void setSceneFile(int roomIndex, int roomName);
+ void moveCharacterToPos(int character, int facing, int xpos, int ypos);
+ void setCharacterPositionWithUpdate(int character);
+ int setCharacterPosition(int character, int *facingTable);
+ void setCharacterPositionHelper(int character, int *facingTable);
+ int getOppositeFacingDirection(int dir);
+ void loadSceneMSC();
+ void startSceneScript(int brandonAlive);
+ void setupSceneItems();
+ void initSceneData(int facing, int unk1, int brandonAlive);
+ void clearNoDropRects();
+ void addToNoDropRects(int x, int y, int w, int h);
+ byte findFreeItemInScene(int scene);
+ byte findItemAtPos(int x, int y);
+ void placeItemInGenericMapScene(int item, int index);
+ void initSceneObjectList(int brandonAlive);
+ void initSceneScreen(int brandonAlive);
+ int findDuplicateItemShape(int shape);
+ int findWay(int x, int y, int toX, int toY, int *moveTable, int moveTableSize);
+ int findSubPath(int x, int y, int toX, int toY, int *moveTable, int start, int end);
+ int getFacingFromPointToPoint(int x, int y, int toX, int toY);
+ void changePosTowardsFacing(int &x, int &y, int facing);
+ bool lineIsPassable(int x, int y);
+ int getMoveTableSize(int *moveTable);
+ int handleSceneChange(int xpos, int ypos, int unk1, int frameReset);
+ int processSceneChange(int *table, int unk1, int frameReset);
+ int changeScene(int facing);
+ void createMouseItem(int item);
+ void destroyMouseItem();
+ void setMouseItem(int item);
+ void wipeDownMouseItem(int xpos, int ypos);
+ void setBrandonPoisonFlags(int reset);
+ void resetBrandonPoisonFlags();
+
+ void processInput(int xpos, int ypos);
+ int processInputHelper(int xpos, int ypos);
+ int clickEventHandler(int xpos, int ypos);
+ void clickEventHandler2();
+ void updateMousePointer(bool forceUpdate = false);
+ bool hasClickedOnExit(int xpos, int ypos);
+ int checkForNPCScriptRun(int xpos, int ypos);
+ void runNpcScript(int func);
+
+ int countItemsInScene(uint16 sceneId);
+ int processItemDrop(uint16 sceneId, uint8 item, int x, int y, int unk1, int unk2);
+ void exchangeItemWithMouseItem(uint16 sceneId, int itemIndex);
+ void addItemToRoom(uint16 sceneId, uint8 item, int itemIndex, int x, int y);
+ int checkNoDropRects(int x, int y);
+ int isDropable(int x, int y);
+ void itemDropDown(int x, int y, int destX, int destY, byte freeItem, int item);
+ void dropItem(int unk1, int item, int x, int y, int unk2);
+ void itemSpecialFX(int x, int y, int item);
+ void itemSpecialFX1(int x, int y, int item);
+ void itemSpecialFX2(int x, int y, int item);
+ void magicOutMouseItem(int animIndex, int itemPos);
+ void magicInMouseItem(int animIndex, int item, int itemPos);
+ void specialMouseItemFX(int shape, int x, int y, int animIndex, int tableIndex, int loopStart, int maxLoops);
+ void processSpecialMouseItemFX(int shape, int x, int y, int tableValue, int loopStart, int maxLoops);
+ void updatePlayerItemsForScene();
+ void redrawInventory(int page);
+
+ void drawJewelPress(int jewel, int drawSpecial);
+ void drawJewelsFadeOutStart();
+ void drawJewelsFadeOutEnd(int jewel);
+ void setupShapes123(const Shape *shapeTable, int endShape, int flags);
+ void freeShapes123();
+
+ void seq_demo();
+ void seq_intro();
+ void seq_introLogos();
+ void seq_introStory();
+ void seq_introMalcolmTree();
+ void seq_introKallakWriting();
+ void seq_introKallakMalcolm();
+ void seq_createAmuletJewel(int jewel, int page, int noSound, int drawOnly);
+ void seq_brandonHealing();
+ void seq_brandonHealing2();
+ void seq_poisonDeathNow(int now);
+ void seq_poisonDeathNowAnim();
+ void seq_playFluteAnimation();
+ void seq_winterScroll1();
+ void seq_winterScroll2();
+ void seq_makeBrandonInv();
+ void seq_makeBrandonNormal();
+ void seq_makeBrandonNormal2();
+ void seq_makeBrandonWisp();
+ void seq_dispelMagicAnimation();
+ void seq_fillFlaskWithWater(int item, int type);
+ void seq_playDrinkPotionAnim(int unk1, int unk2, int flags);
+ int seq_playEnd();
+ void seq_brandonToStone();
+ void seq_playEnding();
+ void seq_playCredits();
+ void updateKyragemFading();
+
+ void snd_setSoundEffectFile(int file);
+
+ static OpcodeProc _opcodeTable[];
+ static const int _opcodeTableSize;
+
+ enum {
+ RES_ALL = 0,
+ RES_INTRO = (1 << 0),
+ RES_INGAME = (1 << 1),
+ RES_OUTRO = (1 << 2)
+ };
+
+ void res_loadResources(int type = RES_ALL);
+ void res_unloadResources(int type = RES_ALL);
+ void res_loadLangTable(const char *filename, PAKFile *res, byte ***loadTo, int *size, bool nativ);
+ void res_loadTable(const byte *src, byte ***loadTo, int *size);
+ void res_loadRoomTable(const byte *src, Room **loadTo, int *size);
+ void res_loadShapeTable(const byte *src, Shape **loadTo, int *size);
+ void res_freeLangTable(char ***sting, int *size);
+
+ void waitForEvent();
+ void loadPalette(const char *filename, uint8 *palData);
+ void loadMouseShapes();
+ void loadCharacterShapes();
+ void loadSpecialEffectShapes();
+ void loadItems();
+ void loadButtonShapes();
+ void initMainButtonList();
+ void loadMainScreen(int page = 3);
+ void setCharactersInDefaultScene();
+ void setupPanPages();
+ void freePanPages();
+ void closeFinalWsa();
+ int handleMalcolmFlag();
+ int handleBeadState();
+ void initBeadState(int x, int y, int x2, int y2, int unk1, BeadState *ptr);
+ int processBead(int x, int y, int &x2, int &y2, BeadState *ptr);
+
+ void setTimer19();
+ void setupTimers();
+ void timerUpdateHeadAnims(int timerNum);
+ void timerSetFlags1(int timerNum);
+ void timerSetFlags2(int timerNum);
+ void timerSetFlags3(int timerNum);
+ void timerCheckAnimFlag1(int timerNum);
+ void timerCheckAnimFlag2(int timerNum);
+ void checkAmuletAnimFlags();
+ void timerRedrawAmulet(int timerNum);
+ void timerFadeText(int timerNum);
+ void updateAnimFlag1(int timerNum);
+ void updateAnimFlag2(int timerNum);
+ void drawAmulet();
+ void setTextFadeTimerCountdown(int16 countdown);
+
+ int buttonInventoryCallback(Button *caller);
+ int buttonAmuletCallback(Button *caller);
+ int buttonMenuCallback(Button *caller);
+ int drawBoxCallback(Button *button);
+ int drawShadedBoxCallback(Button *button);
+ void calcCoords(Menu &menu);
+ void initMenu(Menu menu);
+
+ Button *initButton(Button *list, Button *newButton);
+ void processButtonList(Button *list);
+ void processButton(Button *button);
+ void processMenuButton(Button *button);
+ void processAllMenuButtons();
+
+ const char *getSavegameFilename(int num);
+ void setupSavegames(Menu &menu, int num);
+ int getNextSavegameSlot();
+
+ int gui_resumeGame(Button *button);
+ int gui_loadGameMenu(Button *button);
+ int gui_saveGameMenu(Button *button);
+ int gui_quitPlaying(Button *button);
+ int gui_quitConfirmYes(Button *button);
+ int gui_quitConfirmNo(Button *button);
+ int gui_loadGame(Button *button);
+ int gui_saveGame(Button *button);
+ int gui_savegameConfirm(Button *button);
+ int gui_cancelSubMenu(Button *button);
+ int gui_scrollUp(Button *button);
+ int gui_scrollDown(Button *button);
+
+ bool gui_quitConfirm(const char *str);
+ void gui_getInput();
+ void gui_redrawText(Menu menu);
+ void gui_redrawHighlight(Menu menu);
+ void gui_processHighlights(Menu &menu);
+ void gui_updateSavegameString();
+ void gui_redrawTextfield();
+ void gui_fadePalette();
+ void gui_restorePalette();
+
+ uint8 _game;
+ bool _quitFlag;
+ bool _skipFlag;
+ bool _skipIntroFlag;
+ bool _abortIntroFlag;
+ bool _menuDirectlyToLoad;
+ bool _abortWalkFlag;
+ bool _abortWalkFlag2;
+ bool _mousePressFlag;
+ uint8 _flagsTable[53];
+ uint8 *_shapes[377];
+ uint16 _gameSpeed;
+ uint16 _tickLength;
+ uint32 _features;
+ int _mouseX, _mouseY;
+ int8 _itemInHand;
+ int _mouseState;
+ bool _handleInput;
+ bool _changedScene;
+ int _unkScreenVar1, _unkScreenVar2, _unkScreenVar3;
+ int _beadStateVar;
+ int _unkAmuletVar;
+
+ int _malcolmFlag;
+ int _endSequenceSkipFlag;
+ int _endSequenceNeedLoading;
+ int _unkEndSeqVar2;
+ uint8 *_endSequenceBackUpRect;
+ int _unkEndSeqVar4;
+ int _unkEndSeqVar5;
+ int _lastDisplayedPanPage;
+ uint8 *_panPagesTable[20];
+ Movie *_finalA, *_finalB, *_finalC;
+
+ Movie *_movieObjects[10];
+
+ uint16 _entranceMouseCursorTracks[8];
+ uint16 _walkBlockNorth;
+ uint16 _walkBlockEast;
+ uint16 _walkBlockSouth;
+ uint16 _walkBlockWest;
+
+ int32 _scaleMode;
+ int16 _scaleTable[145];
+
+ Rect _noDropRects[11];
+
+ int8 _birthstoneGemTable[4];
+ int8 _idolGemsTable[3];
+
+ int8 _marbleVaseItem;
+ int8 _foyerItemTable[3];
+
+ int8 _cauldronState;
+ int8 _crystalState[2];
+
+ uint16 _brandonStatusBit;
+ uint8 _brandonStatusBit0x02Flag;
+ uint8 _brandonStatusBit0x20Flag;
+ uint8 _brandonPoisonFlagsGFX[256];
+ uint8 _deathHandler;
+ int16 _brandonInvFlag;
+ uint8 _poisonDeathCounter;
+ int _brandonPosX;
+ int _brandonPosY;
+
+ uint16 _currentChatPartnerBackupFrame;
+ uint16 _currentCharAnimFrame;
+
+ int8 *_sceneAnimTable[50];
+
+ Item _itemTable[145];
+ int _lastProcessedItem;
+ int _lastProcessedItemHeight;
+
+ int16 *_exitListPtr;
+ int16 _exitList[11];
+ SceneExits _sceneExits;
+ uint16 _currentRoom;
+ int _scenePhasingFlag;
+ uint8 *_maskBuffer;
+
+ int _sceneChangeState;
+ int _loopFlag2;
+
+ int _pathfinderFlag;
+ int _pathfinderFlag2;
+ int _lastFindWayRet;
+ int *_movFacingTable;
+
+ int8 _talkingCharNum;
+ int8 _charSayUnk2;
+ int8 _charSayUnk3;
+ int8 _currHeadShape;
+ uint8 _currSentenceColor[3];
+ int8 _startSentencePalIndex;
+ bool _fadeText;
+
+ uint8 _configTalkspeed;
+
+ Common::String _targetName;
+
+ int _curMusicTheme;
+ int _newMusicTheme;
+ int16 _lastMusicCommand;
+
+ Resource *_res;
+ Screen *_screen;
+ ScreenAnimator *_animator;
+ Sound *_sound;
+ SeqPlayer *_seq;
+ Sprites *_sprites;
+ TextDisplayer *_text;
+ ScriptHelper *_scriptInterpreter;
+ Debugger *_debugger;
+ Common::SaveFileManager *_saveFileMan;
+
+ ScriptState *_scriptMain;
+
+ ScriptState *_npcScript;
+ ScriptData *_npcScriptData;
+
+ ScriptState *_scriptClick;
+ ScriptData *_scriptClickData;
+
+ Character *_characterList;
+ Character *_currentCharacter;
+
+ Button *_buttonList;
+ Button *_menuButtonList;
+ bool _displayMenu;
+ bool _menuRestoreScreen;
+ bool _displaySubMenu;
+ bool _cancelSubMenu;
+ int _savegameOffset;
+ int _gameToLoad;
+ char _savegameName[31];
+ const char *_specialSavegameString;
+ KeyboardEvent _keyboardEvent;
+
+ struct KyragemState {
+ uint16 nextOperation;
+ uint16 rOffset;
+ uint16 gOffset;
+ uint16 bOffset;
+ uint32 timerCount;
+ } _kyragemFadingState;
+
+ uint8 *_seq_Forest;
+ uint8 *_seq_KallakWriting;
+ uint8 *_seq_KyrandiaLogo;
+ uint8 *_seq_KallakMalcolm;
+ uint8 *_seq_MalcolmTree;
+ uint8 *_seq_WestwoodLogo;
+ uint8 *_seq_Demo1;
+ uint8 *_seq_Demo2;
+ uint8 *_seq_Demo3;
+ uint8 *_seq_Demo4;
+ uint8 *_seq_Reunion;
+
+ char **_seq_WSATable;
+ char **_seq_CPSTable;
+ char **_seq_COLTable;
+ char **_seq_textsTable;
+
+ int _seq_WSATable_Size;
+ int _seq_CPSTable_Size;
+ int _seq_COLTable_Size;
+ int _seq_textsTable_Size;
+
+ char **_itemList;
+ char **_takenList;
+ char **_placedList;
+ char **_droppedList;
+ char **_noDropList;
+ char **_putDownFirst;
+ char **_waitForAmulet;
+ char **_blackJewel;
+ char **_poisonGone;
+ char **_healingTip;
+ char **_thePoison;
+ char **_fluteString;
+ char **_wispJewelStrings;
+ char **_magicJewelString;
+ char **_flaskFull;
+ char **_fullFlask;
+ char **_veryClever;
+ char **_homeString;
+
+ int _itemList_Size;
+ int _takenList_Size;
+ int _placedList_Size;
+ int _droppedList_Size;
+ int _noDropList_Size;
+ int _putDownFirst_Size;
+ int _waitForAmulet_Size;
+ int _blackJewel_Size;
+ int _poisonGone_Size;
+ int _healingTip_Size;
+ int _thePoison_Size;
+ int _fluteString_Size;
+ int _wispJewelStrings_Size;
+ int _magicJewelString_Size;
+ int _flaskFull_Size;
+ int _fullFlask_Size;
+ int _veryClever_Size;
+ int _homeString_Size;
+
+ char **_characterImageTable;
+ int _characterImageTableSize;
+
+ Shape *_defaultShapeTable;
+ int _defaultShapeTableSize;
+
+ Shape *_healingShapeTable;
+ int _healingShapeTableSize;
+ Shape *_healingShape2Table;
+ int _healingShape2TableSize;
+
+ Shape *_posionDeathShapeTable;
+ int _posionDeathShapeTableSize;
+
+ Shape *_fluteAnimShapeTable;
+ int _fluteAnimShapeTableSize;
+
+ Shape *_winterScrollTable;
+ int _winterScrollTableSize;
+ Shape *_winterScroll1Table;
+ int _winterScroll1TableSize;
+ Shape *_winterScroll2Table;
+ int _winterScroll2TableSize;
+
+ Shape *_drinkAnimationTable;
+ int _drinkAnimationTableSize;
+
+ Shape *_brandonToWispTable;
+ int _brandonToWispTableSize;
+
+ Shape *_magicAnimationTable;
+ int _magicAnimationTableSize;
+
+ Shape *_brandonStoneTable;
+ int _brandonStoneTableSize;
+
+ Room *_roomTable;
+ int _roomTableSize;
+ char **_roomFilenameTable;
+ int _roomFilenameTableSize;
+
+ uint8 *_amuleteAnim;
+
+ uint8 *_specialPalettes[33];
+
+ Timer _timers[34];
+ uint32 _timerNextRun;
+ static const char *_xmidiFiles[];
+ static const int _xmidiFilesCount;
+
+ static const int8 _charXPosTable[];
+ static const int8 _addXPosTable[];
+ static const int8 _charYPosTable[];
+ static const int8 _addYPosTable[];
+
+ // positions of the inventory
+ static const uint16 _itemPosX[];
+ static const uint8 _itemPosY[];
+
+ static Button _buttonData[];
+ static Button *_buttonDataListPtr[];
+ static Button _menuButtonData[];
+ static Button _scrollUpButton;
+ static Button _scrollDownButton;
+
+ static Menu _menu[];
+
+ static const uint8 _magicMouseItemStartFrame[];
+ static const uint8 _magicMouseItemEndFrame[];
+ static const uint8 _magicMouseItemStartFrame2[];
+ static const uint8 _magicMouseItemEndFrame2[];
+
+ static const uint16 _amuletX[];
+ static const uint16 _amuletY[];
+ static const uint16 _amuletX2[];
+ static const uint16 _amuletY2[];
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk
new file mode 100644
index 0000000000..943c628831
--- /dev/null
+++ b/engines/kyra/module.mk
@@ -0,0 +1,33 @@
+MODULE := engines/kyra
+
+MODULE_OBJS := \
+ engines/kyra/kyra.o \
+ engines/kyra/resource.o \
+ engines/kyra/screen.o \
+ engines/kyra/script_v1.o \
+ engines/kyra/script.o \
+ engines/kyra/seqplayer.o \
+ engines/kyra/sound.o \
+ engines/kyra/staticres.o \
+ engines/kyra/sprites.o \
+ engines/kyra/wsamovie.o \
+ engines/kyra/debugger.o \
+ engines/kyra/animator.o \
+ engines/kyra/gui.o \
+ engines/kyra/sequences_v1.o \
+ engines/kyra/items.o \
+ engines/kyra/scene.o \
+ engines/kyra/text.o \
+ engines/kyra/timer.o \
+ engines/kyra/saveload.o
+
+MODULE_DIRS += \
+ engines/kyra
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp
new file mode 100644
index 0000000000..0dd76ff5de
--- /dev/null
+++ b/engines/kyra/resource.cpp
@@ -0,0 +1,366 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "kyra/resource.h"
+#include "kyra/script.h"
+#include "kyra/wsamovie.h"
+#include "kyra/screen.h"
+
+namespace Kyra {
+Resource::Resource(KyraEngine *engine) {
+ _engine = engine;
+
+ // No PAK files in the demo version
+ if (_engine->features() & GF_DEMO)
+ return;
+
+ // prefetches all PAK Files
+
+ // ugly a hardcoded list
+ // TODO: use the FS Backend to get all .PAK Files and load them
+ // or any other thing to get all files
+ static const char *kyra1Filelist[] = {
+ "A_E.PAK", "DAT.PAK", "F_L.PAK", "MAP_5.PAK", "MSC.PAK", "M_S.PAK",
+ "S_Z.PAK", "WSA1.PAK", "WSA2.PAK", "WSA3.PAK", "WSA4.PAK", "WSA5.PAK",
+ "WSA6.PAK", 0
+ };
+
+ static const char *kyra1CDFilelist[] = {
+ "ALTAR.APK", "BELROOM.APK", "BONKBG.APK", "BROKEN.APK", "CASTLE.APK", "CAVE.APK", "CGATE.APK",
+ "DEAD.APK", "DNSTAIR.APK", "DRAGON1.APK", "DRAGON2.APK", "EXTPOT.APK", "FORESTA.APK", "FORESTB.APK",
+ "FOUNTN.APK", "FOYER.APK", "GATECV.APK", "GEM.APK", "GEMCUT.APK", "GENHALL.APK", "GLADE.APK",
+ "GRAVE.APK", "HEALER.APK", "LAGOON.APK", "LANDING.APK", "LAVA.APK", "LEPHOLE.APK", "LIBRARY.APK",
+ "MIX.APK", "MOONCAV.APK", "POTION.APK", "SONG.APK", "SORROW.APK", "SPELL.APK", "STUMP.APK",
+ "TEMPLE.APK", "TRUNK.APK", "WILLOW.APK", "XEDGE.APK",
+
+ "ADL.PAK", "BRINS.PAK", "CLIFF.PAK", "ENTER.PAK", "FORESTA.PAK", "GEM.PAK", "INTRO1.PAK",
+ "LEPHOLE.PAK", "OAKS.PAK", "SPELL.PAK", "WILLOW.PAK", "ALCHEMY.PAK", "BROKEN.PAK", "COL.PAK",
+ "EXTHEAL.PAK", "FORESTB.PAK", "GEMCUT.PAK", "INTRO2.PAK", "LIBRARY.PAK", "PLATEAU.PAK", "SPRING.PAK",
+ "WISE.PAK", "ALGAE.PAK", "BURN.PAK", "DARMS.PAK", "EXTPOT.PAK", "FORESTC.PAK", "GENCAVB.PAK",
+ "INTRO3.PAK", "MISC.PAK", "PLTCAVE.PAK", "SQUARE.PAK", "XEDGE.PAK", "ALTAR.PAK", "CASTLE.PAK",
+ "DEAD.PAK", "EXTSPEL.PAK", "FOUNTN.PAK", "GENHALL.PAK", "INTRO4.PAK", "MIX.PAK", "POTION.PAK",
+ "STARTUP.PAK", "XEDGEB.PAK", "ARCH.PAK", "CATACOM.PAK", "DNSTAIR.PAK", "FALLS.PAK", "FOYER.PAK",
+ "GEN_CAV.PAK", "KITCHEN.PAK", "MOONCAV.PAK", "RUBY.PAK", "STUMP.PAK", "XEDGEC.PAK", "BALCONY.PAK",
+ "CAVE.PAK", "DRAGON.PAK", "FESTSTH.PAK", "FSOUTH.PAK", "GLADE.PAK", "KYRAGEM.PAK", "NCLIFF.PAK",
+ "SICKWIL.PAK", "TEMPLE.PAK", "XMI.PAK", "BELROOM.PAK", "CAVEB.PAK", "EDGE.PAK", "FGOWEST.PAK",
+ "FSOUTHB.PAK", "GRAVE.PAK", "LAGOON.PAK", "NCLIFFB.PAK", "SND.PAK", "TRUNK.PAK", "ZROCK.PAK",
+ "BONKBG.PAK", "CGATE.PAK", "EDGEB.PAK", "FINALE.PAK", "FWSTSTH.PAK", "GRTHALL.PAK", "LANDING.PAK",
+ "NWCLIFB.PAK", "SONG.PAK", "UPSTAIR.PAK", "BRIDGE.PAK", "CHASM.PAK", "EMCAV.PAK", "FNORTH.PAK",
+ "GATECV.PAK", "HEALER.PAK", "LAVA.PAK", "NWCLIFF.PAK", "SORROW.PAK", "WELL.PAK",
+
+ "CHAPTER1.VRM", 0
+ };
+
+ const char **usedFilelist = 0;
+
+ if (_engine->features() & GF_FLOPPY)
+ usedFilelist = kyra1Filelist;
+ else if (_engine->features() & GF_TALKIE)
+ usedFilelist = kyra1CDFilelist;
+ else
+ error("no filelist found for this game");
+
+ for (uint32 tmp = 0; usedFilelist[tmp]; ++tmp) {
+ // prefetch file
+ PAKFile *file = new PAKFile(usedFilelist[tmp]);
+ assert(file);
+
+ PakFileEntry newPak;
+ newPak._file = file;
+ strncpy(newPak._filename, usedFilelist[tmp], 32);
+ if (file->isOpen() && file->isValid())
+ _pakfiles.push_back(newPak);
+ else {
+ delete file;
+ debug(3, "couldn't load file '%s' correctly", usedFilelist[tmp]);
+ }
+ }
+}
+
+Resource::~Resource() {
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+
+ for (;start != _pakfiles.end(); ++start) {
+ delete start->_file;
+ start->_file = 0;
+ }
+}
+
+bool Resource::loadPakFile(const char *filename) {
+ if (isInPakList(filename))
+ return true;
+ PAKFile *file = new PAKFile(filename);
+ if (!file) {
+ error("couldn't load file: '%s'", filename);
+ }
+ PakFileEntry newPak;
+ newPak._file = file;
+ strncpy(newPak._filename, filename, 32);
+ _pakfiles.push_back(newPak);
+ return true;
+}
+
+void Resource::unloadPakFile(const char *filename) {
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+ for (;start != _pakfiles.end(); ++start) {
+ if (scumm_stricmp(start->_filename, filename) == 0) {
+ delete start->_file;
+ _pakfiles.erase(start);
+ break;
+ }
+ }
+ return;
+}
+
+bool Resource::isInPakList(const char *filename) {
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+ for (;start != _pakfiles.end(); ++start) {
+ if (scumm_stricmp(start->_filename, filename) == 0)
+ return true;
+ }
+ return false;
+}
+
+uint8 *Resource::fileData(const char *file, uint32 *size) {
+ uint8 *buffer = 0;
+ Common::File file_;
+
+ // test to open it in the main dir
+ if (file_.open(file)) {
+
+ *size = file_.size();
+ buffer = new uint8[*size];
+ assert(buffer);
+
+ file_.read(buffer, *size);
+
+ file_.close();
+ } else {
+ // opens the file in a PAK File
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+
+ for (;start != _pakfiles.end(); ++start) {
+ *size = start->_file->getFileSize(file);
+
+ if (!(*size))
+ continue;
+
+ buffer = start->_file->getFile(file);
+ break;
+ }
+ }
+
+ if (!buffer || !(*size)) {
+ return 0;
+ }
+
+ return buffer;
+}
+
+bool Resource::fileHandle(const char *file, uint32 *size, Common::File &filehandle) {
+ filehandle.close();
+
+ if (filehandle.open(file))
+ return true;
+
+ Common::List<PakFileEntry>::iterator start = _pakfiles.begin();
+
+ for (;start != _pakfiles.end(); ++start) {
+ *size = start->_file->getFileSize(file);
+
+ if (!(*size))
+ continue;
+
+ if (start->_file->getFileHandle(file, filehandle)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////
+// Pak file manager
+#define PAKFile_Iterate Common::List<PakChunk*>::iterator start=_files.begin();start != _files.end(); ++start
+PAKFile::PAKFile(const Common::String& file) {
+ _filename = 0;
+
+ Common::File pakfile;
+ uint8 *buffer = 0;
+ _open = false;
+
+ if (!pakfile.open(file.c_str())) {
+ debug(3, "couldn't open pakfile '%s'\n", file.c_str());
+ return;
+ }
+
+ uint32 filesize = pakfile.size();
+ buffer = new uint8[filesize];
+ assert(buffer);
+
+ pakfile.read(buffer, filesize);
+ pakfile.close();
+
+ // works with the file
+ uint32 pos = 0, startoffset = 0, endoffset = 0;
+
+ startoffset = READ_LE_UINT32(buffer + pos);
+ pos += 4;
+
+ while (pos < filesize) {
+ PakChunk* chunk = new PakChunk;
+ assert(chunk);
+
+ // saves the name
+ chunk->_name = new char[strlen((const char*)buffer + pos) + 1];
+ assert(chunk->_name);
+ strcpy(chunk->_name, (const char*)buffer + pos);
+ pos += strlen(chunk->_name) + 1;
+ if (!(*chunk->_name))
+ break;
+
+ endoffset = READ_LE_UINT32(buffer + pos);
+ pos += 4;
+
+ if (endoffset == 0) {
+ endoffset = filesize;
+ }
+
+ chunk->_start = startoffset;
+ chunk->_size = endoffset - startoffset;
+
+ _files.push_back(chunk);
+
+ if (endoffset == filesize)
+ break;
+
+ startoffset = endoffset;
+ }
+ _open = true;
+ delete [] buffer;
+
+ _filename = new char[file.size()+1];
+ assert(_filename);
+ strcpy(_filename, file.c_str());
+}
+
+PAKFile::~PAKFile() {
+ delete [] _filename;
+ _filename = 0;
+ _open = false;
+
+ for (PAKFile_Iterate) {
+ delete [] (*start)->_name;
+ (*start)->_name = 0;
+ delete *start;
+ *start = 0;
+ }
+}
+
+uint8 *PAKFile::getFile(const char *file) {
+ for (PAKFile_Iterate) {
+ if (!scumm_stricmp((*start)->_name, file)) {
+ Common::File pakfile;
+ if (!pakfile.open(_filename)) {
+ debug(3, "couldn't open pakfile '%s'\n", _filename);
+ return 0;
+ }
+ pakfile.seek((*start)->_start);
+ uint8 *buffer = new uint8[(*start)->_size];
+ assert(buffer);
+ pakfile.read(buffer, (*start)->_size);
+ return buffer;
+ }
+ }
+ return 0;
+}
+
+bool PAKFile::getFileHandle(const char *file, Common::File &filehandle) {
+ filehandle.close();
+
+ for (PAKFile_Iterate) {
+ if (!scumm_stricmp((*start)->_name, file)) {
+ if (!filehandle.open(_filename)) {
+ debug(3, "couldn't open pakfile '%s'\n", _filename);
+ return 0;
+ }
+ filehandle.seek((*start)->_start);
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32 PAKFile::getFileSize(const char* file) {
+ for (PAKFile_Iterate) {
+ if (!scumm_stricmp((*start)->_name, file))
+ return (*start)->_size;
+ }
+ return 0;
+}
+
+void KyraEngine::loadPalette(const char *filename, uint8 *palData) {
+ debug(9, "KyraEngine::loadPalette('%s' 0x%X)", filename, palData);
+ uint32 fileSize = 0;
+ uint8 *srcData = _res->fileData(filename, &fileSize);
+
+ if (palData && fileSize) {
+ debug(9, "Loading a palette of size %i from '%s'", fileSize, filename);
+ memcpy(palData, srcData, fileSize);
+ }
+ delete [] srcData;
+}
+
+void KyraEngine::loadBitmap(const char *filename, int tempPage, int dstPage, uint8 *palData) {
+ debug(9, "KyraEngine::copyBitmap('%s', %d, %d, 0x%X)", filename, tempPage, dstPage, palData);
+ uint32 fileSize;
+ uint8 *srcData = _res->fileData(filename, &fileSize);
+ uint8 compType = srcData[2];
+ uint32 imgSize = READ_LE_UINT32(srcData + 4);
+ uint16 palSize = READ_LE_UINT16(srcData + 8);
+ if (palData && palSize) {
+ debug(9, "Loading a palette of size %i from %s", palSize, filename);
+ memcpy(palData, srcData + 10, palSize);
+ }
+ uint8 *srcPtr = srcData + 10 + palSize;
+ uint8 *dstData = _screen->getPagePtr(dstPage);
+ switch (compType) {
+ case 0:
+ memcpy(dstData, srcPtr, imgSize);
+ break;
+ case 3:
+ Screen::decodeFrame3(srcPtr, dstData, imgSize);
+ break;
+ case 4:
+ Screen::decodeFrame4(srcPtr, dstData, imgSize);
+ break;
+ default:
+ error("Unhandled bitmap compression %d", compType);
+ break;
+ }
+ delete[] srcData;
+}
+
+} // end of namespace Kyra
+
diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h
new file mode 100644
index 0000000000..1a479c0b73
--- /dev/null
+++ b/engines/kyra/resource.h
@@ -0,0 +1,95 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef RESOURCE_H
+#define RESOURCE_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "common/list.h"
+#include "common/map.h"
+
+#include "kyra/kyra.h"
+
+namespace Kyra {
+
+// standard Package format for Kyrandia games
+class PAKFile {
+ struct PakChunk {
+ char* _name;
+ uint32 _start;
+ uint32 _size;
+ };
+
+public:
+
+ PAKFile(const Common::String &file);
+ ~PAKFile();
+
+ uint8* getFile(const char *file);
+ bool getFileHandle(const char *file, Common::File &filehandle);
+ uint32 getFileSize(const char *file);
+
+ bool isValid(void) const { return (_filename != 0); }
+ bool isOpen(void) const { return _open; }
+
+private:
+
+ bool _open;
+ char *_filename;
+ Common::List<PakChunk*> _files; // the entries
+};
+
+// some resource types
+class Movie;
+class VMContext;
+
+class Resource {
+public:
+
+ Resource(KyraEngine *engine);
+ ~Resource();
+
+ bool loadPakFile(const char *filename);
+ void unloadPakFile(const char *filename);
+ bool isInPakList(const char *filename);
+
+ uint8* fileData(const char *file, uint32 *size);
+ // it gives back a file handle (used for the speech player)
+ // it could be that the needed file is embedded in the returned
+ // handle
+ bool fileHandle(const char *file, uint32 *size, Common::File &filehandle);
+
+protected:
+ struct PakFileEntry {
+ PAKFile *_file;
+ char _filename[32];
+ };
+
+ KyraEngine* _engine;
+ Common::List<PakFileEntry> _pakfiles;
+};
+
+} // end of namespace Kyra
+
+#endif
diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp
new file mode 100644
index 0000000000..fcc6dbfe66
--- /dev/null
+++ b/engines/kyra/saveload.cpp
@@ -0,0 +1,319 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "kyra/kyra.h"
+#include "kyra/animator.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+
+#include "common/savefile.h"
+#include "common/system.h"
+
+#define CURRENT_VERSION 3
+
+namespace Kyra {
+void KyraEngine::loadGame(const char *fileName) {
+ debug(9, "loadGame('%s')", fileName);
+ Common::InSaveFile *in;
+
+ if (!(in = _saveFileMan->openForLoading(fileName))) {
+ warning("Can't open file '%s', game not loaded", fileName);
+ return;
+ }
+
+ uint32 type = in->readUint32BE();
+ if (type != MKID('KYRA')) {
+ warning("No Kyrandia 1 savefile header");
+ delete in;
+ return;
+ }
+ uint32 version = in->readUint32BE();
+ if (version > CURRENT_VERSION) {
+ warning("Savegame is not the right version (%d)", version);
+ delete in;
+ return;
+ }
+
+ char saveName[31];
+ in->read(saveName, 31);
+
+ if (version >= 2) {
+ uint32 gameFlags = in->readUint32BE();
+ if ((gameFlags & GF_FLOPPY) && !(_features & GF_FLOPPY)) {
+ warning("can not load floppy savefile for this (non floppy) gameversion!");
+ delete in;
+ return;
+ } else if ((gameFlags & GF_TALKIE) && !(_features & GF_TALKIE)) {
+ warning("can not load cdrom savefile for this (non cdrom) gameversion!");
+ delete in;
+ return;
+ }
+ } else {
+ warning("Make sure your savefile was from this version! (too old savefile version to detect that)");
+ }
+
+ snd_playSoundEffect(0x0A);
+ snd_playWanderScoreViaMap(0, 1);
+
+ // unload the current voice file should fix some problems with voices
+ if (_currentRoom != 0xFFFF && (_features & GF_TALKIE)) {
+ char file[32];
+ assert(_currentRoom < _roomTableSize);
+ int tableId = _roomTable[_currentRoom].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ strcpy(file, _roomFilenameTable[tableId]);
+ strcat(file, ".VRM");
+ _res->unloadPakFile(file);
+ }
+
+ int brandonX = 0, brandonY = 0;
+ for (int i = 0; i < 11; i++) {
+ _characterList[i].sceneId = in->readUint16BE();
+ _characterList[i].height = in->readByte();
+ _characterList[i].facing = in->readByte();
+ _characterList[i].currentAnimFrame = in->readUint16BE();
+ //_characterList[i].unk6 = in->readUint32BE();
+ in->read(_characterList[i].inventoryItems, 10);
+ _characterList[i].x1 = in->readSint16BE();
+ _characterList[i].y1 = in->readSint16BE();
+ _characterList[i].x2 = in->readSint16BE();
+ _characterList[i].y2 = in->readSint16BE();
+ if (i == 0) {
+ brandonX = _characterList[i].x1;
+ brandonY = _characterList[i].y1;
+ }
+ //_characterList[i].field_20 = in->readUint16BE();
+ //_characterList[i].field_23 = in->readUint16BE();
+ }
+
+ _marbleVaseItem = in->readSint16BE();
+ _itemInHand = in->readByte();
+
+ for (int i = 0; i < 4; ++i) {
+ _birthstoneGemTable[i] = in->readByte();
+ }
+ for (int i = 0; i < 3; ++i) {
+ _idolGemsTable[i] = in->readByte();
+ }
+ for (int i = 0; i < 3; ++i) {
+ _foyerItemTable[i] = in->readByte();
+ }
+ _cauldronState = in->readByte();
+ for (int i = 0; i < 2; ++i) {
+ _crystalState[i] = in->readByte();
+ }
+
+ _brandonStatusBit = in->readUint16BE();
+ _brandonStatusBit0x02Flag = in->readByte();
+ _brandonStatusBit0x20Flag = in->readByte();
+ in->read(_brandonPoisonFlagsGFX, 256);
+ _brandonInvFlag = in->readSint16BE();
+ _poisonDeathCounter = in->readByte();
+ _animator->_brandonDrawFrame = in->readUint16BE();
+
+ for (int i = 0; i < 32; i++) {
+ _timers[i].active = in->readByte();
+ _timers[i].countdown = in->readSint32BE();
+ _timers[i].nextRun = in->readUint32BE();
+ if (_timers[i].nextRun != 0)
+ _timers[i].nextRun += _system->getMillis();
+ }
+ _timerNextRun = 0;
+
+ uint32 flagsSize = in->readUint32BE();
+ assert(flagsSize == sizeof(_flagsTable));
+ in->read(_flagsTable, flagsSize);
+
+ for (int i = 0; i < _roomTableSize; ++i) {
+ for (int item = 0; item < 12; ++item) {
+ _roomTable[i].itemsTable[item] = 0xFF;
+ _roomTable[i].itemsXPos[item] = 0xFFFF;
+ _roomTable[i].itemsYPos[item] = 0xFF;
+ _roomTable[i].needInit[item] = 0;
+ }
+ }
+
+ uint16 sceneId = 0;
+
+ while (true) {
+ sceneId = in->readUint16BE();
+ if (sceneId == 0xFFFF)
+ break;
+ assert(sceneId < _roomTableSize);
+ _roomTable[sceneId].nameIndex = in->readByte();
+
+ for (int i = 0; i < 12; i++) {
+ _roomTable[sceneId].itemsTable[i] = in->readByte();
+ _roomTable[sceneId].itemsXPos[i] = in->readUint16BE();
+ _roomTable[sceneId].itemsYPos[i] = in->readUint16BE();
+ _roomTable[sceneId].needInit[i] = in->readByte();
+ }
+ }
+ if (version >= 3) {
+ _lastMusicCommand = in->readSint16BE();
+ if (_lastMusicCommand != -1)
+ snd_playWanderScoreViaMap(_lastMusicCommand, 1);
+ }
+
+ if (queryGameFlag(0x2D)) {
+ loadMainScreen(8);
+ loadBitmap("AMULET3.CPS", 10, 10, 0);
+ if (!queryGameFlag(0xF1)) {
+ for (int i = 0x55; i <= 0x5A; ++i) {
+ if (queryGameFlag(i)) {
+ seq_createAmuletJewel(i-0x55, 10, 1, 1);
+ }
+ }
+ }
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 10, 8);
+ uint8 *_pageSrc = _screen->getPagePtr(8);
+ uint8 *_pageDst = _screen->getPagePtr(0);
+ memcpy(_pageDst, _pageSrc, 320*200);
+ } else {
+ loadMainScreen(8);
+ }
+
+ createMouseItem(_itemInHand);
+ _animator->setBrandonAnimSeqSize(5, 48);
+ _animator->_noDrawShapesFlag = 1;
+ enterNewScene(_currentCharacter->sceneId, _currentCharacter->facing, 0, 0, 1);
+ _animator->_noDrawShapesFlag = 0;
+
+ _currentCharacter->x1 = brandonX;
+ _currentCharacter->y1 = brandonY;
+ _animator->animRefreshNPC(0);
+ _animator->restoreAllObjectBackgrounds();
+ _animator->preserveAnyChangedBackgrounds();
+ _animator->prepDrawAllObjects();
+ _animator->copyChangedObjectsForward(0);
+ _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);
+ redrawInventory(0);
+
+ _abortWalkFlag = true;
+ _abortWalkFlag2 = false;
+ _mousePressFlag = false;
+ _mouseX = brandonX;
+ _mouseY = brandonY;
+ _system->warpMouse(brandonX, brandonY);
+
+ if (in->ioFailed())
+ error("Load failed.");
+ else
+ debug(1, "Loaded savegame '%s.'", saveName);
+
+ delete in;
+}
+
+void KyraEngine::saveGame(const char *fileName, const char *saveName) {
+ debug(9, "saveGame('%s', '%s')", fileName, saveName);
+ Common::OutSaveFile *out;
+
+ if (!(out = _saveFileMan->openForSaving(fileName))) {
+ warning("Can't create file '%s', game not saved", fileName);
+ return;
+ }
+
+ // Savegame version
+ out->writeUint32BE(MKID('KYRA'));
+ out->writeUint32BE(CURRENT_VERSION);
+ out->write(saveName, 31);
+ out->writeUint32BE(_features);
+
+ for (int i = 0; i < 11; i++) {
+ out->writeUint16BE(_characterList[i].sceneId);
+ out->writeByte(_characterList[i].height);
+ out->writeByte(_characterList[i].facing);
+ out->writeUint16BE(_characterList[i].currentAnimFrame);
+ //out->writeUint32BE(_characterList[i].unk6);
+ out->write(_characterList[i].inventoryItems, 10);
+ out->writeSint16BE(_characterList[i].x1);
+ out->writeSint16BE(_characterList[i].y1);
+ out->writeSint16BE(_characterList[i].x2);
+ out->writeSint16BE(_characterList[i].y2);
+ //out->writeUint16BE(_characterList[i].field_20);
+ //out->writeUint16BE(_characterList[i].field_23);
+ }
+
+ out->writeSint16BE(_marbleVaseItem);
+ out->writeByte(_itemInHand);
+
+ for (int i = 0; i < 4; ++i) {
+ out->writeByte(_birthstoneGemTable[i]);
+ }
+ for (int i = 0; i < 3; ++i) {
+ out->writeByte(_idolGemsTable[i]);
+ }
+ for (int i = 0; i < 3; ++i) {
+ out->writeByte(_foyerItemTable[i]);
+ }
+ out->writeByte(_cauldronState);
+ for (int i = 0; i < 2; ++i) {
+ out->writeByte(_crystalState[i]);
+ }
+
+ out->writeUint16BE(_brandonStatusBit);
+ out->writeByte(_brandonStatusBit0x02Flag);
+ out->writeByte(_brandonStatusBit0x20Flag);
+ out->write(_brandonPoisonFlagsGFX, 256);
+ out->writeSint16BE(_brandonInvFlag);
+ out->writeByte(_poisonDeathCounter);
+ out->writeUint16BE(_animator->_brandonDrawFrame);
+
+ for (int i = 0; i < 32; i++) {
+ out->writeByte(_timers[i].active);
+ out->writeSint32BE(_timers[i].countdown);
+ if (_system->getMillis() >= _timers[i].nextRun) {
+ out->writeUint32BE(0);
+ } else {
+ out->writeUint32BE(_timers[i].nextRun - _system->getMillis());
+ }
+ }
+
+ out->writeUint32BE(sizeof(_flagsTable));
+ out->write(_flagsTable, sizeof(_flagsTable));
+
+ for (uint16 i = 0; i < _roomTableSize; i++) {
+ out->writeUint16BE(i);
+ out->writeByte(_roomTable[i].nameIndex);
+ for (int a = 0; a < 12; a++) {
+ out->writeByte(_roomTable[i].itemsTable[a]);
+ out->writeUint16BE(_roomTable[i].itemsXPos[a]);
+ out->writeUint16BE(_roomTable[i].itemsYPos[a]);
+ out->writeByte(_roomTable[i].needInit[a]);
+ }
+ }
+ // room table terminator
+ out->writeUint16BE(0xFFFF);
+
+ out->writeSint16BE(_lastMusicCommand);
+
+ out->flush();
+
+ // check for errors
+ if (out->ioFailed())
+ warning("Can't write file '%s'. (Disk full?)", fileName);
+ else
+ debug(1, "Saved game '%s.'", saveName);
+
+ delete out;
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/scene.cpp b/engines/kyra/scene.cpp
new file mode 100644
index 0000000000..a94050a296
--- /dev/null
+++ b/engines/kyra/scene.cpp
@@ -0,0 +1,1581 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "kyra/kyra.h"
+#include "kyra/seqplayer.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+#include "kyra/sound.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+#include "kyra/script.h"
+
+#include "common/system.h"
+#include "common/savefile.h"
+
+namespace Kyra {
+
+void KyraEngine::enterNewScene(int sceneId, int facing, int unk1, int unk2, int brandonAlive) {
+ debug(9, "KyraEngine::enterNewScene(%d, %d, %d, %d, %d)", sceneId, facing, unk1, unk2, brandonAlive);
+ int unkVar1 = 1;
+ _screen->hideMouse();
+ _handleInput = false;
+ _abortWalkFlag = false;
+ _abortWalkFlag2 = false;
+ if (_currentCharacter->sceneId == 7 && sceneId == 24) {
+ _newMusicTheme = 3;
+ } else if (_currentCharacter->sceneId == 25 && sceneId == 109) {
+ _newMusicTheme = 4;
+ } else if (_currentCharacter->sceneId == 120 && sceneId == 37) {
+ _newMusicTheme = 5;
+ } else if (_currentCharacter->sceneId == 52 && sceneId == 199) {
+ _newMusicTheme = 6;
+ } else if (_currentCharacter->sceneId == 37 && sceneId == 120) {
+ _newMusicTheme = 4;
+ } else if (_currentCharacter->sceneId == 109 && sceneId == 25) {
+ _newMusicTheme = 3;
+ } else if (_currentCharacter->sceneId == 24 && sceneId == 7) {
+ _newMusicTheme = 2;
+ }
+ if (_newMusicTheme != _curMusicTheme) {
+ snd_playTheme(_newMusicTheme);
+ }
+
+ switch (_currentCharacter->sceneId) {
+ case 1:
+ if (sceneId == 0) {
+ moveCharacterToPos(0, 0, _currentCharacter->x1, 84);
+ unkVar1 = 0;
+ }
+ break;
+
+ case 3:
+ if (sceneId == 2) {
+ moveCharacterToPos(0, 6, 155, _currentCharacter->y1);
+ unkVar1 = 0;
+ }
+ break;
+
+ case 26:
+ if (sceneId == 27) {
+ moveCharacterToPos(0, 6, 155, _currentCharacter->y1);
+ unkVar1 = 0;
+ }
+ break;
+
+ case 44:
+ if (sceneId == 45) {
+ moveCharacterToPos(0, 2, 192, _currentCharacter->y1);
+ unkVar1 = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (unkVar1 && unk1) {
+ int xpos = _currentCharacter->x1;
+ int ypos = _currentCharacter->y1;
+ switch (facing) {
+ case 0:
+ ypos = _currentCharacter->y1 - 6;
+ break;
+
+ case 2:
+ xpos = 336;
+ break;
+
+ case 4:
+ ypos = 143;
+ break;
+
+ case 6:
+ xpos = -16;
+ break;
+
+ default:
+ break;
+ }
+
+ moveCharacterToPos(0, facing, xpos, ypos);
+ }
+
+ for (int i = 0; i < ARRAYSIZE(_movieObjects); ++i) {
+ _movieObjects[i]->close();
+ }
+
+ if (!brandonAlive) {
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ _scriptInterpreter->startScript(_scriptClick, 5);
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+ }
+
+ memset(_entranceMouseCursorTracks, 0xFFFF, sizeof(uint16)*4);
+ _currentCharacter->sceneId = sceneId;
+
+ assert(sceneId < _roomTableSize);
+ assert(_roomTable[sceneId].nameIndex < _roomFilenameTableSize);
+
+ Room *currentRoom = &_roomTable[sceneId];
+
+ if (_currentRoom != 0xFFFF && (_features & GF_TALKIE)) {
+ char file[32];
+ assert(_currentRoom < _roomTableSize);
+ int tableId = _roomTable[_currentRoom].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ strcpy(file, _roomFilenameTable[tableId]);
+ strcat(file, ".VRM");
+ _res->unloadPakFile(file);
+ }
+
+ _currentRoom = sceneId;
+
+ int tableId = _roomTable[_currentCharacter->sceneId].nameIndex;
+ char fileNameBuffer[32];
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".DAT");
+ _sprites->loadDAT(fileNameBuffer, _sceneExits);
+ _sprites->setupSceneAnims();
+ _scriptInterpreter->unloadScript(_scriptClickData);
+ loadSceneMSC();
+
+ if ((_features & GF_TALKIE)) {
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".VRM");
+ _res->loadPakFile(fileNameBuffer);
+ }
+
+ _walkBlockNorth = currentRoom->northExit;
+ _walkBlockEast = currentRoom->eastExit;
+ _walkBlockSouth = currentRoom->southExit;
+ _walkBlockWest = currentRoom->westExit;
+
+ if (_walkBlockNorth == 0xFFFF) {
+ _screen->blockOutRegion(0, 0, 320, (_northExitHeight & 0xFF)+3);
+ }
+ if (_walkBlockEast == 0xFFFF) {
+ _screen->blockOutRegion(312, 0, 8, 139);
+ }
+ if (_walkBlockSouth == 0xFFFF) {
+ _screen->blockOutRegion(0, 135, 320, 8);
+ }
+ if (_walkBlockWest == 0xFFFF) {
+ _screen->blockOutRegion(0, 0, 8, 139);
+ }
+
+ if (!brandonAlive) {
+ updatePlayerItemsForScene();
+ }
+
+ startSceneScript(brandonAlive);
+ setupSceneItems();
+
+ initSceneData(facing, unk2, brandonAlive);
+
+ _loopFlag2 = 0;
+ _screen->showMouse();
+ if (!brandonAlive) {
+ seq_poisonDeathNow(0);
+ }
+ updateMousePointer(true);
+ _changedScene = true;
+}
+
+void KyraEngine::transcendScenes(int roomIndex, int roomName) {
+ debug(9, "KyraEngine::transcendScenes(%d, %d)", roomIndex, roomName);
+ assert(roomIndex < _roomTableSize);
+ if (_features & GF_TALKIE) {
+ char file[32];
+ assert(roomIndex < _roomTableSize);
+ int tableId = _roomTable[roomIndex].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ strcpy(file, _roomFilenameTable[tableId]);
+ strcat(file, ".VRM");
+ _res->unloadPakFile(file);
+ }
+ _roomTable[roomIndex].nameIndex = roomName;
+ _unkScreenVar2 = 1;
+ _unkScreenVar3 = 1;
+ _unkScreenVar1 = 0;
+ _brandonPosX = _currentCharacter->x1;
+ _brandonPosY = _currentCharacter->y1;
+ enterNewScene(roomIndex, _currentCharacter->facing, 0, 0, 0);
+ _unkScreenVar1 = 1;
+ _unkScreenVar2 = 0;
+ _unkScreenVar3 = 0;
+}
+
+void KyraEngine::setSceneFile(int roomIndex, int roomName) {
+ debug(9, "KyraEngine::setSceneFile(%d, %d)", roomIndex, roomName);
+ assert(roomIndex < _roomTableSize);
+ _roomTable[roomIndex].nameIndex = roomName;
+}
+
+void KyraEngine::moveCharacterToPos(int character, int facing, int xpos, int ypos) {
+ debug(9, "KyraEngine::moveCharacterToPos(%d, %d, %d, %d)", character, facing, xpos, ypos);
+ Character *ch = &_characterList[character];
+ ch->facing = facing;
+ _screen->hideMouse();
+ xpos = (int16)(xpos & 0xFFFC);
+ ypos = (int16)(ypos & 0xFFFE);
+ disableTimer(19);
+ disableTimer(14);
+ disableTimer(18);
+ uint32 nextFrame = 0;
+ switch (facing) {
+ case 0:
+ while (ypos < ch->y1) {
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ setCharacterPositionWithUpdate(character);
+ while (_system->getMillis() < nextFrame) { updateGameTimers(); }
+ }
+ break;
+
+ case 2:
+ while (ch->x1 < xpos) {
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ setCharacterPositionWithUpdate(character);
+ while (_system->getMillis() < nextFrame) { updateGameTimers(); }
+ }
+ break;
+
+ case 4:
+ while (ypos > ch->y1) {
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ setCharacterPositionWithUpdate(character);
+ while (_system->getMillis() < nextFrame) { updateGameTimers(); }
+ }
+ break;
+
+ case 6:
+ while (ch->x1 > xpos) {
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ setCharacterPositionWithUpdate(character);
+ while (_system->getMillis() < nextFrame) { updateGameTimers(); }
+ }
+ break;
+
+ default:
+ break;
+ }
+ enableTimer(19);
+ enableTimer(14);
+ enableTimer(18);
+ _screen->showMouse();
+}
+
+void KyraEngine::setCharacterPositionWithUpdate(int character) {
+ debug(9, "KyraEngine::setCharacterPositionWithUpdate(%d)", character);
+ setCharacterPosition(character, 0);
+ _sprites->updateSceneAnims();
+ updateGameTimers();
+ _animator->updateAllObjectShapes();
+ updateTextFade();
+
+ if (_currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ }
+}
+
+int KyraEngine::setCharacterPosition(int character, int *facingTable) {
+ debug(9, "KyraEngine::setCharacterPosition(%d, 0x%X)", character, facingTable);
+ if (character == 0) {
+ _currentCharacter->x1 += _charXPosTable[_currentCharacter->facing];
+ _currentCharacter->y1 += _charYPosTable[_currentCharacter->facing];
+ setCharacterPositionHelper(0, facingTable);
+ return 1;
+ } else {
+ _characterList[character].x1 += _charXPosTable[_characterList[character].facing];
+ _characterList[character].y1 += _charYPosTable[_characterList[character].facing];
+ if (_characterList[character].sceneId == _currentCharacter->sceneId) {
+ setCharacterPositionHelper(character, 0);
+ }
+ }
+ return 0;
+}
+
+void KyraEngine::setCharacterPositionHelper(int character, int *facingTable) {
+ debug(9, "KyraEngine::setCharacterPositionHelper(%d, 0x%X)", character, facingTable);
+ Character *ch = &_characterList[character];
+ ++ch->currentAnimFrame;
+ int facing = ch->facing;
+ if (facingTable) {
+ if (*facingTable != *(facingTable - 1)) {
+ if (*(facingTable - 1) == *(facingTable + 1)) {
+ facing = getOppositeFacingDirection(*(facingTable - 1));
+ *facingTable = *(facingTable - 1);
+ }
+ }
+ }
+
+ static uint8 facingIsZero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static uint8 facingIsFour[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ if (facing == 0) {
+ ++facingIsZero[character];
+ } else {
+ bool resetTables = false;
+ if (facing != 7) {
+ if (facing - 1 != 0) {
+ if (facing != 4) {
+ if (facing == 3 || facing == 5) {
+ if (facingIsFour[character] > 2) {
+ facing = 4;
+ }
+ resetTables = true;
+ }
+ } else {
+ ++facingIsFour[character];
+ }
+ } else {
+ if (facingIsZero[character] > 2) {
+ facing = 0;
+ }
+ resetTables = true;
+ }
+ } else {
+ if (facingIsZero[character] > 2) {
+ facing = 0;
+ }
+ resetTables = true;
+ }
+
+ if (resetTables) {
+ facingIsZero[character] = 0;
+ facingIsFour[character] = 0;
+ }
+ }
+
+ static const uint16 maxAnimationFrame[] = {
+ 0x000F, 0x0031, 0x0055, 0x0000, 0x0000, 0x0000,
+ 0x0008, 0x002A, 0x004E, 0x0000, 0x0000, 0x0000,
+ 0x0022, 0x0046, 0x006A, 0x0000, 0x0000, 0x0000,
+ 0x001D, 0x0041, 0x0065, 0x0000, 0x0000, 0x0000,
+ 0x001F, 0x0043, 0x0067, 0x0000, 0x0000, 0x0000,
+ 0x0028, 0x004C, 0x0070, 0x0000, 0x0000, 0x0000,
+ 0x0023, 0x0047, 0x006B, 0x0000, 0x0000, 0x0000
+ };
+
+ if (facing == 0) {
+ if (maxAnimationFrame[36+character] > ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[36+character];
+ }
+ if (maxAnimationFrame[30+character] < ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[36+character];
+ }
+ } else if (facing == 4) {
+ if (maxAnimationFrame[18+character] > ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[18+character];
+ }
+ if (maxAnimationFrame[12+character] < ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[18+character];
+ }
+ } else {
+ if (maxAnimationFrame[18+character] < ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[30+character];
+ }
+ if (maxAnimationFrame[character] == ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[6+character];
+ }
+ if (maxAnimationFrame[character] < ch->currentAnimFrame) {
+ ch->currentAnimFrame = maxAnimationFrame[6+character]+2;
+ }
+ }
+
+ if (character == 0) {
+ if (_brandonStatusBit & 0x10)
+ ch->currentAnimFrame = 88;
+ }
+
+ _animator->animRefreshNPC(character);
+}
+
+int KyraEngine::getOppositeFacingDirection(int dir) {
+ debug(9, "KyraEngine::getOppositeFacingDirection(%d)", dir);
+ switch (dir) {
+ case 0:
+ return 2;
+ break;
+
+ case 1:
+ return 1;
+ break;
+
+ case 3:
+ return 7;
+ break;
+
+ case 4:
+ return 6;
+ break;
+
+ case 5:
+ return 5;
+ break;
+
+ case 6:
+ return 4;
+ break;
+
+ case 7:
+ return 3;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+void KyraEngine::loadSceneMSC() {
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ int tableId = _roomTable[_currentCharacter->sceneId].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ char fileNameBuffer[32];
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".MSC");
+ _screen->fillRect(0, 0, 319, 199, 0, 5);
+ loadBitmap(fileNameBuffer, 3, 5, 0);
+}
+
+void KyraEngine::startSceneScript(int brandonAlive) {
+ debug(9, "KyraEngine::startSceneScript(%d)", brandonAlive);
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ int tableId = _roomTable[_currentCharacter->sceneId].nameIndex;
+ assert(tableId < _roomFilenameTableSize);
+ char fileNameBuffer[32];
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".CPS");
+ loadBitmap(fileNameBuffer, 3, 3, 0);
+ _sprites->loadSceneShapes();
+ _exitListPtr = 0;
+
+ _screen->setScreenPalette(_screen->_currentPalette);
+
+ _scaleMode = 1;
+ for (int i = 0; i < 145; ++i) {
+ _scaleTable[i] = 256;
+ }
+
+ clearNoDropRects();
+ _scriptInterpreter->initScript(_scriptClick, _scriptClickData);
+ strcpy(fileNameBuffer, _roomFilenameTable[tableId]);
+ strcat(fileNameBuffer, ".EMC");
+ _scriptInterpreter->unloadScript(_scriptClickData);
+ _scriptInterpreter->loadScript(fileNameBuffer, _scriptClickData, _opcodeTable, _opcodeTableSize, 0);
+ _scriptInterpreter->startScript(_scriptClick, 0);
+ _scriptClick->variables[0] = _currentCharacter->sceneId;
+ _scriptClick->variables[7] = brandonAlive;
+
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+}
+
+void KyraEngine::initSceneData(int facing, int unk1, int brandonAlive) {
+ debug(9, "KyraEngine::initSceneData(%d, %d, %d)", facing, unk1, brandonAlive);
+
+ int16 xpos2 = 0;
+ int setFacing = 1;
+
+ int16 xpos = 0, ypos = 0;
+
+ if (_brandonPosX == -1 && _brandonPosY == -1) {
+ switch (facing+1) {
+ case 0:
+ xpos = ypos = -1;
+ break;
+
+ case 1: case 2: case 8:
+ xpos = _sceneExits.southXPos;
+ ypos = _sceneExits.southYPos;
+ break;
+
+ case 3:
+ xpos = _sceneExits.westXPos;
+ ypos = _sceneExits.westYPos;
+ break;
+
+ case 4: case 5: case 6:
+ xpos = _sceneExits.northXPos;
+ ypos = _sceneExits.northYPos;
+ break;
+
+ case 7:
+ xpos = _sceneExits.eastXPos;
+ ypos = _sceneExits.eastYPos;
+ break;
+
+ default:
+ break;
+ }
+
+ if ((uint8)(_northExitHeight & 0xFF) + 2 >= ypos) {
+ ypos = (_northExitHeight & 0xFF) + 4;
+ }
+ if (xpos >= 308) {
+ xpos = 304;
+ }
+ if ((uint8)(_northExitHeight >> 8) - 2 <= ypos) {
+ ypos = (_northExitHeight >> 8) - 4;
+ }
+ if (xpos <= 12) {
+ xpos = 16;
+ }
+ }
+
+ if (_brandonPosX > -1) {
+ xpos = _brandonPosX;
+ }
+ if (_brandonPosY > -1) {
+ ypos = _brandonPosY;
+ }
+
+ int16 ypos2 = 0;
+ if (_brandonPosX > -1 && _brandonPosY > -1) {
+ switch (_currentCharacter->sceneId) {
+ case 1:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 4;
+ xpos2 = 192;
+ ypos2 = 104;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ case 3:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 2;
+ xpos2 = 204;
+ ypos2 = 94;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ case 26:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 2;
+ xpos2 = 192;
+ ypos2 = 128;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ case 44:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 6;
+ xpos2 = 156;
+ ypos2 = 96;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ case 37:
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+ facing = 2;
+ xpos2 = 148;
+ ypos2 = 114;
+ setFacing = 0;
+ unk1 = 1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ _brandonPosX = _brandonPosY = -1;
+
+ if (unk1 && setFacing) {
+ ypos2 = ypos;
+ xpos2 = xpos;
+ switch (facing) {
+ case 0:
+ ypos = 142;
+ break;
+
+ case 2:
+ xpos = -16;
+ break;
+
+ case 4:
+ ypos = (uint8)(_northExitHeight & 0xFF) - 4;
+ break;
+
+ case 6:
+ xpos = 336;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ xpos2 = (int16)(xpos2 & 0xFFFC);
+ ypos2 = (int16)(ypos2 & 0xFFFE);
+ xpos = (int16)(xpos & 0xFFFC);
+ ypos = (int16)(ypos & 0xFFFE);
+ _currentCharacter->facing = facing;
+ _currentCharacter->x1 = xpos;
+ _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = ypos;
+ _currentCharacter->y2 = ypos;
+
+ initSceneObjectList(brandonAlive);
+
+ if (unk1 && brandonAlive == 0) {
+ moveCharacterToPos(0, facing, xpos2, ypos2);
+ }
+
+ _scriptClick->variables[4] = _itemInHand;
+ _scriptClick->variables[7] = brandonAlive;
+ _scriptInterpreter->startScript(_scriptClick, 3);
+ while (_scriptInterpreter->validScript(_scriptClick)) {
+ _scriptInterpreter->runScript(_scriptClick);
+ }
+}
+
+void KyraEngine::initSceneObjectList(int brandonAlive) {
+ debug(9, "KyraEngine::initSceneObjectList(%d)", brandonAlive);
+ for (int i = 0; i < 28; ++i) {
+ _animator->actors()[i].active = 0;
+ }
+
+ int startAnimFrame = 0;
+
+ AnimObject *curAnimState = _animator->actors();
+ curAnimState->active = 1;
+ curAnimState->drawY = _currentCharacter->y1;
+ curAnimState->sceneAnimPtr = _shapes[4+_currentCharacter->currentAnimFrame];
+ curAnimState->animFrameNumber = _currentCharacter->currentAnimFrame;
+ startAnimFrame = _currentCharacter->currentAnimFrame-7;
+ int xOffset = _defaultShapeTable[startAnimFrame].xOffset;
+ int yOffset = _defaultShapeTable[startAnimFrame].yOffset;
+ if (_scaleMode) {
+ curAnimState->x1 = _currentCharacter->x1;
+ curAnimState->y1 = _currentCharacter->y1;
+
+ _animator->_brandonScaleX = _scaleTable[_currentCharacter->y1];
+ _animator->_brandonScaleY = _scaleTable[_currentCharacter->y1];
+
+ curAnimState->x1 += (_animator->_brandonScaleX * xOffset) >> 8;
+ curAnimState->y1 += (_animator->_brandonScaleY * yOffset) >> 8;
+ } else {
+ curAnimState->x1 = _currentCharacter->x1 + xOffset;
+ curAnimState->y1 = _currentCharacter->y1 + yOffset;
+ }
+ curAnimState->x2 = curAnimState->x1;
+ curAnimState->y2 = curAnimState->y1;
+ curAnimState->refreshFlag = 1;
+ curAnimState->bkgdChangeFlag = 1;
+ _animator->clearQueue();
+ _animator->addObjectToQueue(curAnimState);
+
+ int listAdded = 0;
+ int addedObjects = 1;
+
+ for (int i = 1; i < 5; ++i) {
+ Character *ch = &_characterList[i];
+ curAnimState = &_animator->actors()[addedObjects];
+ if (ch->sceneId != _currentCharacter->sceneId) {
+ curAnimState->active = 0;
+ curAnimState->refreshFlag = 0;
+ curAnimState->bkgdChangeFlag = 0;
+ ++addedObjects;
+ continue;
+ }
+
+ curAnimState->drawY = ch->y1;
+ curAnimState->sceneAnimPtr = _shapes[4+ch->currentAnimFrame];
+ curAnimState->animFrameNumber = ch->currentAnimFrame;
+ startAnimFrame = ch->currentAnimFrame-7;
+ xOffset = _defaultShapeTable[startAnimFrame].xOffset;
+ yOffset = _defaultShapeTable[startAnimFrame].yOffset;
+ if (_scaleMode) {
+ curAnimState->x1 = ch->x1;
+ curAnimState->y1 = ch->y1;
+
+ _animator->_brandonScaleX = _scaleTable[ch->y1];
+ _animator->_brandonScaleY = _scaleTable[ch->y1];
+
+ curAnimState->x1 += (_animator->_brandonScaleX * xOffset) >> 8;
+ curAnimState->y1 += (_animator->_brandonScaleY * yOffset) >> 8;
+ } else {
+ curAnimState->x1 = ch->x1 + xOffset;
+ curAnimState->y1 = ch->y1 + yOffset;
+ }
+ curAnimState->x2 = curAnimState->x1;
+ curAnimState->y2 = curAnimState->y1;
+ curAnimState->active = 1;
+ curAnimState->refreshFlag = 1;
+ curAnimState->bkgdChangeFlag = 1;
+
+ if (ch->facing >= 1 && ch->facing <= 3) {
+ curAnimState->flags |= 1;
+ } else if (ch->facing >= 5 && ch->facing <= 7) {
+ curAnimState->flags &= 0xFFFFFFFE;
+ }
+
+ _animator->addObjectToQueue(curAnimState);
+
+ ++addedObjects;
+ ++listAdded;
+ if (listAdded < 2)
+ i = 5;
+ }
+
+ for (int i = 0; i < 11; ++i) {
+ curAnimState = &_animator->sprites()[i];
+
+ if (_sprites->_anims[i].play) {
+ curAnimState->active = 1;
+ curAnimState->refreshFlag = 1;
+ curAnimState->bkgdChangeFlag = 1;
+ } else {
+ curAnimState->active = 0;
+ curAnimState->refreshFlag = 0;
+ curAnimState->bkgdChangeFlag = 0;
+ }
+ curAnimState->height = _sprites->_anims[i].height;
+ curAnimState->height2 = _sprites->_anims[i].height2;
+ curAnimState->width = _sprites->_anims[i].width + 1;
+ curAnimState->width2 = _sprites->_anims[i].width2;
+ curAnimState->drawY = _sprites->_anims[i].drawY;
+ curAnimState->x1 = curAnimState->x2 = _sprites->_anims[i].x;
+ curAnimState->y1 = curAnimState->y2 = _sprites->_anims[i].y;
+ curAnimState->background = _sprites->_anims[i].background;
+ curAnimState->sceneAnimPtr = _sprites->_sceneShapes[_sprites->_anims[i].sprite];
+
+ curAnimState->disable = _sprites->_anims[i].disable;
+
+ if (_sprites->_anims[i].unk2)
+ curAnimState->flags = 0x800;
+ else
+ curAnimState->flags = 0;
+
+ if (_sprites->_anims[i].flipX)
+ curAnimState->flags |= 0x1;
+
+ _animator->addObjectToQueue(curAnimState);
+ }
+
+ for (int i = 0; i < 12; ++i) {
+ curAnimState = &_animator->items()[i];
+ Room *curRoom = &_roomTable[_currentCharacter->sceneId];
+ byte curItem = curRoom->itemsTable[i];
+ if (curItem != 0xFF) {
+ curAnimState->drawY = curRoom->itemsYPos[i];
+ curAnimState->sceneAnimPtr = _shapes[220+curItem];
+ curAnimState->animFrameNumber = (int16)0xFFFF;
+ curAnimState->y1 = curRoom->itemsYPos[i];
+ curAnimState->x1 = curRoom->itemsXPos[i];
+
+ curAnimState->x1 -= (_animator->fetchAnimWidth(curAnimState->sceneAnimPtr, _scaleTable[curAnimState->drawY])) >> 1;
+ curAnimState->y1 -= _animator->fetchAnimHeight(curAnimState->sceneAnimPtr, _scaleTable[curAnimState->drawY]);
+
+ curAnimState->x2 = curAnimState->x1;
+ curAnimState->y2 = curAnimState->y1;
+
+ curAnimState->active = 1;
+ curAnimState->refreshFlag = 1;
+ curAnimState->bkgdChangeFlag = 1;
+
+ _animator->addObjectToQueue(curAnimState);
+ } else {
+ curAnimState->active = 0;
+ curAnimState->refreshFlag = 0;
+ curAnimState->bkgdChangeFlag = 0;
+ }
+ }
+
+ _animator->preserveAnyChangedBackgrounds();
+ curAnimState = _animator->actors();
+ curAnimState->bkgdChangeFlag = 1;
+ curAnimState->refreshFlag = 1;
+ for (int i = 1; i < 28; ++i) {
+ curAnimState = &_animator->objects()[i];
+ if (curAnimState->active) {
+ curAnimState->bkgdChangeFlag = 1;
+ curAnimState->refreshFlag = 1;
+ }
+ }
+ _animator->restoreAllObjectBackgrounds();
+ _animator->preserveAnyChangedBackgrounds();
+ _animator->prepDrawAllObjects();
+ initSceneScreen(brandonAlive);
+ _animator->copyChangedObjectsForward(0);
+}
+
+void KyraEngine::initSceneScreen(int brandonAlive) {
+ // XXX (Pointless?) Palette stuff
+ if (_unkScreenVar2 == 1) {
+ _screen->shuffleScreen(8, 8, 304, 128, 2, 0, _unkScreenVar3, false);
+ } else {
+ _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);
+ }
+ _screen->updateScreen();
+ // XXX More (pointless?) palette stuff
+
+ if (!_scriptInterpreter->startScript(_scriptClick, 2))
+ error("Could not start script function 2 of scene script");
+
+ _scriptClick->variables[7] = brandonAlive;
+
+ while (_scriptInterpreter->validScript(_scriptClick))
+ _scriptInterpreter->runScript(_scriptClick);
+
+ setTextFadeTimerCountdown(-1);
+ if (_currentCharacter->sceneId == 210) {
+ if (_itemInHand != -1)
+ magicOutMouseItem(2, -1);
+
+ _screen->hideMouse();
+ for (int i = 0; i < 10; ++i) {
+ if (_currentCharacter->inventoryItems[i] != 0xFF)
+ magicOutMouseItem(2, i);
+ }
+ _screen->showMouse();
+ }
+}
+
+int KyraEngine::handleSceneChange(int xpos, int ypos, int unk1, int frameReset) {
+ debug(9, "KyraEngine::handleSceneChange(%d, %d, %d, %d)", xpos, ypos, unk1, frameReset);
+ if (queryGameFlag(0xEF)) {
+ unk1 = 0;
+ }
+ int sceneId = _currentCharacter->sceneId;
+ _pathfinderFlag = 0;
+ if (xpos < 12) {
+ if (_roomTable[sceneId].westExit != 0xFFFF) {
+ xpos = 12;
+ ypos = _sceneExits.westYPos;
+ _pathfinderFlag = 7;
+ }
+ } else if(xpos >= 308) {
+ if (_roomTable[sceneId].eastExit != 0xFFFF) {
+ xpos = 307;
+ ypos = _sceneExits.eastYPos;
+ _pathfinderFlag = 13;
+ }
+ }
+
+ if (ypos <= (_northExitHeight&0xFF)+2) {
+ if (_roomTable[sceneId].northExit != 0xFFFF) {
+ xpos = _sceneExits.northXPos;
+ ypos = _northExitHeight & 0xFF;
+ _pathfinderFlag = 14;
+ }
+ } else if (ypos >= 136) {
+ if (_roomTable[sceneId].southExit != 0xFFFF) {
+ xpos = _sceneExits.southXPos;
+ ypos = 136;
+ _pathfinderFlag = 11;
+ }
+ }
+
+ int temp = xpos - _currentCharacter->x1;
+ if (ABS(temp) < 4) {
+ temp = ypos - _currentCharacter->y1;
+ if (ABS(temp) < 2) {
+ return 0;
+ }
+ }
+
+ int x = (int16)(_currentCharacter->x1 & 0xFFFC);
+ int y = (int16)(_currentCharacter->y1 & 0xFFFE);
+ xpos = (int16)(xpos & 0xFFFC);
+ ypos = (int16)(ypos & 0xFFFE);
+ int ret = findWay(x, y, xpos, ypos, _movFacingTable, 150);
+ _pathfinderFlag = 0;
+ if (ret >= _lastFindWayRet) {
+ _lastFindWayRet = ret;
+ }
+ if (ret == 0x7D00 || ret == 0) {
+ return 0;
+ }
+ return processSceneChange(_movFacingTable, unk1, frameReset);
+}
+
+int KyraEngine::processSceneChange(int *table, int unk1, int frameReset) {
+ debug(9, "KyraEngine::processSceneChange(0x%X, %d, %d)", table, unk1, frameReset);
+ if (queryGameFlag(0xEF)) {
+ unk1 = 0;
+ }
+ int *tableStart = table;
+ _sceneChangeState = 0;
+ _loopFlag2 = 0;
+ bool running = true;
+ int returnValue = 0;
+ uint32 nextFrame = 0;
+ _abortWalkFlag = false;
+ _mousePressFlag = false;
+
+ while (running) {
+ if (_abortWalkFlag) {
+ *table = 8;
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ processInput(_mouseX, _mouseY);
+ return 0;
+ }
+ bool forceContinue = false;
+ switch (*table) {
+ case 0: case 1: case 2:
+ case 3: case 4: case 5:
+ case 6: case 7:
+ _currentCharacter->facing = getOppositeFacingDirection(*table);
+ break;
+
+ case 8:
+ forceContinue = true;
+ running = false;
+ break;
+
+ default:
+ ++table;
+ forceContinue = true;
+ break;
+ }
+
+ returnValue = changeScene(_currentCharacter->facing);
+ if (returnValue) {
+ running = false;
+ _abortWalkFlag = false;
+ }
+
+ if (unk1) {
+ if (_mousePressFlag) {
+ running = false;
+ _sceneChangeState = 1;
+ }
+ }
+
+ if (forceContinue || !running) {
+ continue;
+ }
+
+ int temp = 0;
+ if (table == tableStart || table[1] == 8) {
+ temp = setCharacterPosition(0, 0);
+ } else {
+ temp = setCharacterPosition(0, table);
+ }
+ if (temp) {
+ ++table;
+ }
+
+ nextFrame = getTimerDelay(5) * _tickLength + _system->getMillis();
+ while (_system->getMillis() < nextFrame) {
+ _sprites->updateSceneAnims();
+ updateMousePointer();
+ updateGameTimers();
+ _animator->updateAllObjectShapes();
+ updateTextFade();
+ if (_currentCharacter->sceneId == 210) {
+ updateKyragemFading();
+ if (seq_playEnd() || _beadStateVar == 4 || _beadStateVar == 5) {
+ *table = 8;
+ running = false;
+ break;
+ }
+ }
+ if ((nextFrame - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+
+ if (frameReset && !(_brandonStatusBit & 2)) {
+ _currentCharacter->currentAnimFrame = 7;
+ }
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ return returnValue;
+}
+
+int KyraEngine::changeScene(int facing) {
+ debug(9, "KyraEngine::changeScene(%d)", facing);
+ if (queryGameFlag(0xEF)) {
+ if (_currentCharacter->sceneId == 5) {
+ return 0;
+ }
+ }
+
+ int xpos = _charXPosTable[facing] + _currentCharacter->x1;
+ int ypos = _charYPosTable[facing] + _currentCharacter->y1;
+
+ if (xpos >= 12 && xpos <= 308) {
+ if (!lineIsPassable(xpos, ypos))
+ return false;
+ }
+
+ if (_exitListPtr) {
+ int16 *ptr = _exitListPtr;
+ // this loop should be only entered on time, seems to be some hack in the original
+ while (true) {
+ if (*ptr == -1)
+ break;
+
+ if (*ptr > _currentCharacter->x1 || _currentCharacter->y1 < ptr[1] || _currentCharacter->x1 > ptr[2] || _currentCharacter->y1 > ptr[3]) {
+ ptr += 10;
+ break;
+ }
+ _brandonPosX = ptr[6];
+ _brandonPosY = ptr[7];
+ uint16 sceneId = ptr[5];
+ facing = ptr[4];
+ int unk1 = ptr[8];
+ int unk2 = ptr[9];
+ if (sceneId == 0xFFFF) {
+ switch (facing) {
+ case 0:
+ sceneId = _roomTable[_currentCharacter->sceneId].northExit;
+ break;
+
+ case 2:
+ sceneId = _roomTable[_currentCharacter->sceneId].eastExit;
+ break;
+
+ case 4:
+ sceneId = _roomTable[_currentCharacter->sceneId].southExit;
+ break;
+
+ case 6:
+ sceneId = _roomTable[_currentCharacter->sceneId].westExit;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ _currentCharacter->facing = facing;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ enterNewScene(sceneId, facing, unk1, unk2, 0);
+ resetGameFlag(0xEE);
+ return 1;
+ }
+ }
+
+ int returnValue = 0;
+ facing = 0;
+
+ if ((_northExitHeight & 0xFF) + 2 >= ypos || (_northExitHeight & 0xFF) + 2 >= _currentCharacter->y1) {
+ facing = 0;
+ returnValue = 1;
+ }
+
+ if (xpos >= 308 || (_currentCharacter->x1 + 4) >= 308) {
+ facing = 2;
+ returnValue = 1;
+ }
+
+ if (((_northExitHeight >> 8) & 0xFF) - 2 < ypos || ((_northExitHeight >> 8) & 0xFF) - 2 < _currentCharacter->y1) {
+ facing = 4;
+ returnValue = 1;
+ }
+
+ if (xpos <= 12 || _currentCharacter->y1 <= 12) {
+ facing = 6;
+ returnValue = 1;
+ }
+
+ if (!returnValue)
+ return 0;
+
+ uint16 sceneId = 0xFFFF;
+ switch (facing) {
+ case 0:
+ sceneId = _roomTable[_currentCharacter->sceneId].northExit;
+ break;
+
+ case 2:
+ sceneId = _roomTable[_currentCharacter->sceneId].eastExit;
+ break;
+
+ case 4:
+ sceneId = _roomTable[_currentCharacter->sceneId].southExit;
+ break;
+
+ default:
+ sceneId = _roomTable[_currentCharacter->sceneId].westExit;
+ break;
+ }
+
+ if (sceneId == 0xFFFF)
+ return 0;
+
+ enterNewScene(sceneId, facing, 1, 1, 0);
+ return returnValue;
+}
+
+void KyraEngine::setCharactersInDefaultScene() {
+ static const uint32 defaultSceneTable[][4] = {
+ { 0xFFFF, 0x0004, 0x0003, 0xFFFF },
+ { 0xFFFF, 0x0022, 0xFFFF, 0x0000 },
+ { 0xFFFF, 0x001D, 0x0021, 0xFFFF },
+ { 0xFFFF, 0x0000, 0x0000, 0xFFFF }
+ };
+
+ for (int i = 1; i < 5; ++i) {
+ Character *cur = &_characterList[i];
+ //cur->field_20 = 0;
+ const uint32 *curTable = defaultSceneTable[i-1];
+ cur->sceneId = curTable[0];
+ if (cur->sceneId == _currentCharacter->sceneId) {
+ //++cur->field_20;
+ cur->sceneId = curTable[1/*cur->field_20*/];
+ }
+ //cur->field_23 = curTable[cur->field_20+1];
+ }
+}
+
+void KyraEngine::setCharactersPositions(int character) {
+ static uint16 initXPosTable[] = {
+ 0x3200, 0x0024, 0x2230, 0x2F00, 0x0020, 0x002B,
+ 0x00CA, 0x00F0, 0x0082, 0x00A2, 0x0042
+ };
+ static uint8 initYPosTable[] = {
+ 0x00, 0xA2, 0x00, 0x42, 0x00,
+ 0x67, 0x67, 0x60, 0x5A, 0x71,
+ 0x76
+ };
+ assert(character < ARRAYSIZE(initXPosTable));
+ Character *edit = &_characterList[character];
+ edit->x1 = edit->x2 = initXPosTable[character];
+ edit->y1 = edit->y2 = initYPosTable[character];
+}
+
+#pragma mark -
+#pragma mark - Pathfinder
+#pragma mark -
+
+int KyraEngine::findWay(int x, int y, int toX, int toY, int *moveTable, int moveTableSize) {
+ debug(9, "KyraEngine::findWay(%d, %d, %d, %d, 0x%X, %d)", x, y, toX, toY, moveTable, moveTableSize);
+ x &= 0xFFFC; toX &= 0xFFFC;
+ y &= 0xFFFE; toY &= 0xFFFE;
+ x = (int16)x; y = (int16)y; toX = (int16)toX; toY = (int16)toY;
+
+ if (x == toY && y == toY) {
+ moveTable[0] = 8;
+ return 0;
+ }
+
+ int curX = x;
+ int curY = y;
+ int lastUsedEntry = 0;
+ int tempValue = 0;
+ int *pathTable1 = new int[0x7D0];
+ int *pathTable2 = new int[0x7D0];
+ assert(pathTable1 && pathTable2);
+
+ while (true) {
+ int newFacing = getFacingFromPointToPoint(x, y, toX, toY);
+ changePosTowardsFacing(curX, curY, newFacing);
+
+ if (curX == toX && curY == toY) {
+ if (!lineIsPassable(curX, curY))
+ break;
+ moveTable[lastUsedEntry++] = newFacing;
+ break;
+ }
+
+ if (lineIsPassable(curX, curY)) {
+ if (lastUsedEntry == moveTableSize) {
+ delete [] pathTable1;
+ delete [] pathTable2;
+ return 0x7D00;
+ }
+ // debug drawing
+ //if (curX >= 0 && curY >= 0 && curX < 320 && curY < 200) {
+ // _screen->setPagePixel(0, curX, curY, 11);
+ // _screen->updateScreen();
+ // waitTicks(5);
+ //}
+ moveTable[lastUsedEntry++] = newFacing;
+ x = curX;
+ y = curY;
+ continue;
+ }
+
+ int temp = 0;
+ while (true) {
+ newFacing = getFacingFromPointToPoint(curX, curY, toX, toY);
+ changePosTowardsFacing(curX, curY, newFacing);
+ // debug drawing
+ //if (curX >= 0 && curY >= 0 && curX < 320 && curY < 200) {
+ // _screen->setPagePixel(0, curX, curY, 8);
+ // _screen->updateScreen();
+ // waitTicks(5);
+ //}
+
+ if (!lineIsPassable(curX, curY)) {
+ if (curX != toX || curY != toY)
+ continue;
+ }
+
+ if (curX == toX && curY == toY) {
+ if (!lineIsPassable(curX, curY)) {
+ tempValue = 0;
+ temp = 0;
+ break;
+ }
+ }
+
+ temp = findSubPath(x, y, curX, curY, pathTable1, 1, 0x7D0);
+ tempValue = findSubPath(x, y, curX, curY, pathTable2, 0, 0x7D0);
+ if (curX == toX && curY == toY) {
+ if (temp == 0x7D00 && tempValue == 0x7D00) {
+ delete [] pathTable1;
+ delete [] pathTable2;
+ return 0x7D00;
+ }
+ }
+
+ if (temp != 0x7D00 || tempValue != 0x7D00) {
+ break;
+ }
+ }
+
+ if (temp < tempValue) {
+ if (lastUsedEntry + temp > moveTableSize) {
+ delete [] pathTable1;
+ delete [] pathTable2;
+ return 0x7D00;
+ }
+ memcpy(&moveTable[lastUsedEntry], pathTable1, temp*sizeof(int));
+ lastUsedEntry += temp;
+ } else {
+ if (lastUsedEntry + tempValue > moveTableSize) {
+ delete [] pathTable1;
+ delete [] pathTable2;
+ return 0x7D00;
+ }
+ memcpy(&moveTable[lastUsedEntry], pathTable2, tempValue*sizeof(int));
+ lastUsedEntry += tempValue;
+ }
+ x = curX;
+ y = curY;
+ if (curX == toX && curY == toY) {
+ break;
+ }
+ }
+ delete [] pathTable1;
+ delete [] pathTable2;
+ moveTable[lastUsedEntry] = 8;
+ return getMoveTableSize(moveTable);
+}
+
+int KyraEngine::findSubPath(int x, int y, int toX, int toY, int *moveTable, int start, int end) {
+ debug(9, "KyraEngine::findSubPath(%d, %d, %d, %d, 0x%X, %d, %d)", x, y, toX, toY, moveTable, start, end);
+ // only used for debug specific code
+ //static uint16 unkTable[] = { 8, 5 };
+ static const int8 facingTable1[] = { 7, 0, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 7, 0 };
+ static const int8 facingTable2[] = { -1, 0, -1, 2, -1, 4, -1, 6, -1, 2, -1, 4, -1, 6, -1, 0 };
+ static const int8 facingTable3[] = { 2, 4, 4, 6, 6, 0, 0, 2, 6, 6, 0, 0, 2, 2, 4, 4 };
+ static const int8 addPosTableX[] = { -1, 0, -1, 4, -1, 0, -1, -4, -1, -4, -1, 0, -1, 4, -1, 0 };
+ static const int8 addPosTableY[] = { -1, 2, -1, 0, -1, -2, -1, 0, -1, 0, -1, 2, -1, 0, -1, -2 };
+
+ // debug specific
+ //++unkTable[start];
+ //while (_screen->getPalette(0)[unkTable[start]] != 0x0F) {
+ // ++unkTable[start];
+ //}
+
+ int xpos1 = x, xpos2 = x;
+ int ypos1 = y, ypos2 = y;
+ int newFacing = getFacingFromPointToPoint(x, y, toX, toY);
+ int position = 0;
+
+ while (position != end) {
+ int newFacing2 = newFacing;
+ while (true) {
+ changePosTowardsFacing(xpos1, ypos1, facingTable1[start*8 + newFacing2]);
+ if (!lineIsPassable(xpos1, ypos1)) {
+ if (facingTable1[start*8 + newFacing2] == newFacing) {
+ return 0x7D00;
+ }
+ newFacing2 = facingTable1[start*8 + newFacing2];
+ xpos1 = x;
+ ypos1 = y;
+ continue;
+ }
+ newFacing = facingTable1[start*8 + newFacing2];
+ break;
+ }
+ // debug drawing
+ //if (xpos1 >= 0 && ypos1 >= 0 && xpos1 < 320 && ypos1 < 200) {
+ // _screen->setPagePixel(0, xpos1, ypos1, unkTable[start]);
+ // _screen->updateScreen();
+ // waitTicks(5);
+ //}
+ if (newFacing & 1) {
+ int temp = xpos1 + addPosTableX[newFacing + start * 8];
+ if (toX == temp) {
+ temp = ypos1 + addPosTableY[newFacing + start * 8];
+ if (toY == temp) {
+ moveTable[position++] = facingTable2[newFacing + start * 8];
+ return position;
+ }
+ }
+ }
+ moveTable[position++] = newFacing;
+ x = xpos1;
+ y = ypos1;
+ if (x == toX && y == toY) {
+ return position;
+ }
+
+ if (xpos1 == xpos2 && ypos1 == ypos2) {
+ break;
+ }
+
+ newFacing = facingTable3[start*8 + newFacing];
+ }
+ return 0x7D00;
+}
+
+int KyraEngine::getFacingFromPointToPoint(int x, int y, int toX, int toY) {
+ debug(9, "KyraEngine::getFacingFromPointToPoint(%d, %d, %d, %d)", x, y, toX, toY);
+ static const int facingTable[] = {
+ 1, 0, 1, 2, 3, 4, 3, 2, 7, 0, 7, 6, 5, 4, 5, 6
+ };
+
+ int facingEntry = 0;
+ int ydiff = y - toY;
+ if (ydiff < 0) {
+ ++facingEntry;
+ ydiff = -ydiff;
+ }
+ facingEntry <<= 1;
+
+ int xdiff = toX - x;
+ if (xdiff < 0) {
+ ++facingEntry;
+ xdiff = -xdiff;
+ }
+
+ if (xdiff >= ydiff) {
+ int temp = ydiff;
+ ydiff = xdiff;
+ xdiff = temp;
+
+ facingEntry <<= 1;
+ } else {
+ facingEntry <<= 1;
+ facingEntry += 1;
+ }
+ int temp = (ydiff + 1) >> 1;
+
+ if (xdiff < temp) {
+ facingEntry <<= 1;
+ facingEntry += 1;
+ } else {
+ facingEntry <<= 1;
+ }
+ assert(facingEntry < ARRAYSIZE(facingTable));
+ return facingTable[facingEntry];
+}
+
+void KyraEngine::changePosTowardsFacing(int &x, int &y, int facing) {
+ debug(9, "KyraEngine::changePosTowardsFacing(%d, %d, %d)", x, y, facing);
+ x += _addXPosTable[facing];
+ y += _addYPosTable[facing];
+}
+
+bool KyraEngine::lineIsPassable(int x, int y) {
+ debug(9, "KyraEngine::lineIsPassable(%d, %d)", x, y);
+ if (queryGameFlag(0xEF)) {
+ if (_currentCharacter->sceneId == 5)
+ return true;
+ }
+
+ if (_pathfinderFlag & 2) {
+ if (x >= 312)
+ return false;
+ }
+
+ if (_pathfinderFlag & 4) {
+ if (y >= 136)
+ return false;
+ }
+
+ if (_pathfinderFlag & 8) {
+ if (x < 8)
+ return false;
+ }
+
+ if (_pathfinderFlag2) {
+ if (x <= 8 || x >= 312)
+ return true;
+ if (y < (_northExitHeight & 0xFF) || y > 135)
+ return true;
+ }
+
+ if (y > 137) {
+ return false;
+ }
+
+ int ypos = 8;
+ if (_scaleMode) {
+ ypos = (_scaleTable[y] >> 5) + 1;
+ if (8 < ypos)
+ ypos = 8;
+ }
+
+ x -= (ypos >> 1);
+ if (y < 0)
+ y = 0;
+
+ int xpos = x;
+ int xtemp = xpos + ypos - 1;
+ if (x < 0)
+ xpos = 0;
+
+ if (xtemp > 319)
+ xtemp = 319;
+
+ for (; xpos < xtemp; ++xpos) {
+ if (!_screen->getShapeFlag1(xpos, y))
+ return false;
+ }
+ return true;
+}
+
+int KyraEngine::getMoveTableSize(int *moveTable) {
+ debug(9, "KyraEngine::getMoveTableSize(0x%X)", moveTable);
+ int retValue = 0;
+ if (moveTable[0] == 8)
+ return 0;
+
+ static const int facingTable[] = {
+ 4, 5, 6, 7, 0, 1, 2, 3
+ };
+ static const int unkTable[] = {
+ -1, -1, 1, 2, -1, 6, 7, -1,
+ -1, -1, -1, -1, 2, -1, 0, -1,
+ 1, -1, -1, -1, 3, 4, -1, 0,
+ 2, -1, -1, -1, -1, -1, 4, -1,
+ -1, 2, 3, -1, -1, -1, 5, 6,
+ 6, -1, 4, -1, -1, -1, -1, -1,
+ 7, 0, -1, 4, 5, -1, -1, -1,
+ -1, -1, 0, -1, 6, -1, -1, -1
+ };
+
+ int *oldPosition = moveTable;
+ int *tempPosition = moveTable;
+ int *curPosition = moveTable + 1;
+ retValue = 1;
+
+ while (*curPosition != 8) {
+ if (*oldPosition == facingTable[*curPosition]) {
+ retValue -= 2;
+ *oldPosition = 9;
+ *curPosition = 9;
+
+ while (tempPosition != moveTable) {
+ --tempPosition;
+ if (*tempPosition != 9)
+ break;
+ }
+
+ if (tempPosition == moveTable && *tempPosition == 9) {
+ while (*tempPosition != 8 && *tempPosition == 9) {
+ ++tempPosition;
+ }
+ if (*tempPosition == 8) {
+ return 0;
+ }
+ }
+
+ oldPosition = tempPosition;
+ curPosition = oldPosition+1;
+ while (*curPosition != 8 && *curPosition == 9) {
+ ++curPosition;
+ }
+ continue;
+ }
+
+ if (unkTable[*curPosition+((*oldPosition)*8)] != -1) {
+ --retValue;
+ *oldPosition = unkTable[*curPosition+((*oldPosition)*8)];
+ *curPosition = 9;
+
+ if (tempPosition != oldPosition) {
+ curPosition = oldPosition;
+ oldPosition = tempPosition;
+ while (true) {
+ if (tempPosition == moveTable) {
+ break;
+ }
+ --tempPosition;
+ if (*tempPosition != 9) {
+ break;
+ }
+ }
+ } else {
+ while (true) {
+ ++curPosition;
+ if (*curPosition != 9) {
+ break;
+ }
+ }
+ }
+ continue;
+ }
+
+ tempPosition = oldPosition;
+ oldPosition = curPosition;
+ ++retValue;
+ while (true) {
+ ++curPosition;
+ if (*curPosition != 9) {
+ break;
+ }
+ }
+ }
+
+ return retValue;
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp
new file mode 100644
index 0000000000..4bf2d8da75
--- /dev/null
+++ b/engines/kyra/screen.cpp
@@ -0,0 +1,2084 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "kyra/screen.h"
+#include "kyra/kyra.h"
+
+namespace Kyra {
+
+#define BITBLIT_RECTS 10
+
+Screen::Screen(KyraEngine *vm, OSystem *system)
+ : _system(system), _vm(vm) {
+ _curPage = 0;
+ for (int pageNum = 0; pageNum < SCREEN_PAGE_NUM; pageNum += 2) {
+ uint8 *pagePtr = (uint8 *)malloc(SCREEN_PAGE_SIZE);
+ if (pagePtr) {
+ memset(pagePtr, 0, SCREEN_PAGE_SIZE);
+ _pagePtrs[pageNum] = _pagePtrs[pageNum + 1] = pagePtr;
+ }
+ }
+ memset(_shapePages, 0, sizeof(_shapePages));
+ _currentPalette = (uint8 *)malloc(768);
+ if (_currentPalette) {
+ memset(_currentPalette, 0, 768);
+ }
+ _screenPalette = (uint8 *)malloc(768);
+ if (_screenPalette) {
+ memset(_screenPalette, 0, 768);
+ }
+ for (int i = 0; i < 3; ++i) {
+ _palettes[i] = (uint8 *)malloc(768);
+ if (_palettes[i]) {
+ memset(_palettes[i], 0, 768);
+ }
+ }
+ _curDim = &_screenDimTable[0];
+ _charWidth = 0;
+ _charOffset = 0;
+ memset(_fonts, 0, sizeof(_fonts));
+ for (int i = 0; i < ARRAYSIZE(_textColorsMap); ++i) {
+ _textColorsMap[i] = i;
+ }
+ _decodeShapeBuffer = NULL;
+ _decodeShapeBufferSize = 0;
+ _animBlockPtr = NULL;
+ _animBlockSize = 0;
+ _mouseLockCount = 0;
+
+ _bitBlitRects = new Rect[BITBLIT_RECTS];
+ assert(_bitBlitRects);
+ memset(_bitBlitRects, 0, sizeof(Rect)*BITBLIT_RECTS);
+ _bitBlitNum = 0;
+ memset(_saveLoadPage, 0, sizeof(_saveLoadPage));
+
+ _unkPtr1 = (uint8*)malloc(getRectSize(1, 144));
+ memset(_unkPtr1, 0, getRectSize(1, 144));
+ _unkPtr2 = (uint8*)malloc(getRectSize(1, 144));
+ memset(_unkPtr2, 0, getRectSize(1, 144));
+}
+
+Screen::~Screen() {
+ for (int pageNum = 0; pageNum < SCREEN_PAGE_NUM; pageNum += 2) {
+ free(_pagePtrs[pageNum]);
+ _pagePtrs[pageNum] = _pagePtrs[pageNum + 1] = 0;
+ }
+ for (int f = 0; f < ARRAYSIZE(_fonts); ++f) {
+ delete[] _fonts[f].fontData;
+ _fonts[f].fontData = NULL;
+ }
+ free(_currentPalette);
+ free(_screenPalette);
+ free(_decodeShapeBuffer);
+ free(_animBlockPtr);
+ for (int i = 0; i < 3; ++i) {
+ free(_palettes[i]);
+ }
+ delete [] _bitBlitRects;
+ for (int i = 0; i < ARRAYSIZE(_saveLoadPage); ++i) {
+ delete [] _saveLoadPage[i];
+ _saveLoadPage[i] = 0;
+ }
+
+ free(_unkPtr1);
+ free(_unkPtr2);
+}
+
+void Screen::updateScreen() {
+ debug(9, "Screen::updateScreen()");
+ _system->copyRectToScreen(getPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, SCREEN_H);
+ //for debug reasons (needs 640x200 screen)
+ //_system->copyRectToScreen(getPagePtr(2), SCREEN_W, 320, 0, SCREEN_W, SCREEN_H);
+ _system->updateScreen();
+}
+
+uint8 *Screen::getPagePtr(int pageNum) {
+ debug(9, "Screen::getPagePtr(%d)", pageNum);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ return _pagePtrs[pageNum];
+}
+
+void Screen::clearPage(int pageNum) {
+ debug(9, "Screen::clearPage(%d)", pageNum);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ memset(getPagePtr(pageNum), 0, SCREEN_PAGE_SIZE);
+}
+
+int Screen::setCurPage(int pageNum) {
+ debug(9, "Screen::setCurPage(%d)", pageNum);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ int previousPage = _curPage;
+ _curPage = pageNum;
+ return previousPage;
+}
+
+void Screen::clearCurPage() {
+ debug(9, "Screen::clearCurPage()");
+ memset(getPagePtr(_curPage), 0, SCREEN_PAGE_SIZE);
+}
+
+uint8 Screen::getPagePixel(int pageNum, int x, int y) {
+ debug(9, "Screen::getPagePixel(%d, %d, %d)", pageNum, x, y);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+ return _pagePtrs[pageNum][y * SCREEN_W + x];
+}
+
+void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) {
+ debug(9, "Screen::setPagePixel(%d, %d, %d, %d)", pageNum, x, y, color);
+ assert(pageNum < SCREEN_PAGE_NUM);
+ assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+ _pagePtrs[pageNum][y * SCREEN_W + x] = color;
+}
+
+void Screen::fadeFromBlack() {
+ debug(9, "Screen::fadeFromBlack()");
+ fadePalette(_currentPalette, 0x54);
+}
+
+void Screen::fadeToBlack() {
+ debug(9, "Screen::fadeToBlack()");
+ uint8 blackPal[768];
+ memset(blackPal, 0, 768);
+ fadePalette(blackPal, 0x54);
+}
+
+void Screen::fadeSpecialPalette(int palIndex, int startIndex, int size, int fadeTime) {
+ debug(9, "fadeSpecialPalette(%d, %d, %d, %d)", palIndex, startIndex, size, fadeTime);
+ assert(_vm->palTable1()[palIndex]);
+ assert(_currentPalette);
+ uint8 tempPal[768];
+ memcpy(tempPal, _currentPalette, 768);
+ memcpy(&tempPal[startIndex*3], _vm->palTable1()[palIndex], size*3);
+ fadePalette(tempPal, fadeTime*18);
+ memcpy(&_currentPalette[startIndex*3], &tempPal[startIndex*3], size*3);
+ setScreenPalette(_currentPalette);
+ _system->updateScreen();
+}
+
+void Screen::fadePalette(const uint8 *palData, int delay) {
+ debug(9, "Screen::fadePalette(0x%X, %d)", palData, delay);
+ uint8 fadePal[768];
+ memcpy(fadePal, _screenPalette, 768);
+ uint8 diff, maxDiff = 0;
+ for (int i = 0; i < 768; ++i) {
+ diff = ABS(palData[i] - fadePal[i]);
+ if (diff > maxDiff) {
+ maxDiff = diff;
+ }
+ }
+ int16 delayInc = delay << 8;
+ if (maxDiff != 0) {
+ delayInc /= maxDiff;
+ }
+ delay = delayInc;
+ for (diff = 1; diff <= maxDiff; ++diff) {
+ if (delayInc >= 512) {
+ break;
+ }
+ delayInc += delay;
+ }
+ int delayAcc = 0;
+ while (1) {
+ delayAcc += delayInc;
+ bool needRefresh = false;
+ for (int i = 0; i < 768; ++i) {
+ int c1 = palData[i];
+ int c2 = fadePal[i];
+ if (c1 != c2) {
+ needRefresh = true;
+ if (c1 > c2) {
+ c2 += diff;
+ if (c1 < c2) {
+ c2 = c1;
+ }
+ }
+ if (c1 < c2) {
+ c2 -= diff;
+ if (c1 > c2) {
+ c2 = c1;
+ }
+ }
+ fadePal[i] = (uint8)c2;
+ }
+ }
+ if (!needRefresh) {
+ break;
+ }
+ setScreenPalette(fadePal);
+ _system->updateScreen();
+ //_system->delayMillis((delayAcc >> 8) * 1000 / 60);
+ _vm->delay((delayAcc >> 8) * 1000 / 60);
+ delayAcc &= 0xFF;
+ }
+}
+
+void Screen::setScreenPalette(const uint8 *palData) {
+ debug(9, "Screen::setScreenPalette(0x%X)", palData);
+ memcpy(_screenPalette, palData, 768);
+ uint8 screenPal[256 * 4];
+ for (int i = 0; i < 256; ++i) {
+ screenPal[4 * i + 0] = (palData[0] << 2) | (palData[0] & 3);
+ screenPal[4 * i + 1] = (palData[1] << 2) | (palData[1] & 3);
+ screenPal[4 * i + 2] = (palData[2] << 2) | (palData[2] & 3);
+ screenPal[4 * i + 3] = 0;
+ palData += 3;
+ }
+ _system->setPalette(screenPal, 0, 256);
+}
+
+void Screen::copyToPage0(int y, int h, uint8 page, uint8 *seqBuf) {
+ debug(9, "Screen::copyToPage0(%d, %d, %d, 0x%X)", y, h, page, seqBuf);
+ assert(y + h <= SCREEN_H);
+ const uint8 *src = getPagePtr(page) + y * SCREEN_W;
+ uint8 *dstPage = getPagePtr(0) + y * SCREEN_W;
+ for (int i = 0; i < h; ++i) {
+ for (int x = 0; x < SCREEN_W; ++x) {
+ if (seqBuf[x] != src[x]) {
+ seqBuf[x] = src[x];
+ dstPage[x] = src[x];
+ }
+ }
+ src += SCREEN_W;
+ seqBuf += SCREEN_W;
+ dstPage += SCREEN_W;
+ }
+}
+
+void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags) {
+ debug(9, "Screen::copyRegion(%d, %d, %d, %d, %d, %d, %d, %d, %d)", x1, y1, x2, y2, w, h, srcPage, dstPage, flags);
+
+ if (flags & CR_CLIPPED) {
+ if (x2 < 0) {
+ if (x2 <= -w)
+ return;
+ w += x2;
+ x1 -= x2;
+ x2 = 0;
+ } else if (x2 + w >= SCREEN_W) {
+ if (x2 > SCREEN_W)
+ return;
+ w = SCREEN_W - x2;
+ }
+
+ if (y2 < 0) {
+ if (y2 <= -h )
+ return;
+ h += y2;
+ y1 -= y2;
+ y2 = 0;
+ } else if (y2 + h >= SCREEN_H) {
+ if (y2 > SCREEN_H)
+ return;
+ h = SCREEN_H - y2;
+ }
+ }
+
+ assert(x1 + w <= SCREEN_W && y1 + h <= SCREEN_H);
+ const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W + x1;
+ assert(x2 + w <= SCREEN_W && y2 + h <= SCREEN_H);
+ uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W + x2;
+
+ if (flags & CR_X_FLIPPED) {
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ if (src[i]) {
+ dst[w-i] = src[i];
+ }
+ }
+ src += SCREEN_W;
+ dst += SCREEN_W;
+ }
+ } else {
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ if (src[i]) {
+ dst[i] = src[i];
+ }
+ }
+ src += SCREEN_W;
+ dst += SCREEN_W;
+ }
+ }
+}
+
+void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest) {
+ debug(9, "Screen::copyRegionToBuffer(%d, %d, %d, %d, %d)", pageNum, x, y, w, h);
+ assert(x >= 0 && x < Screen::SCREEN_W && y >= 0 && y < Screen::SCREEN_H && dest);
+ uint8 *pagePtr = getPagePtr(pageNum);
+ for (int i = y; i < y + h; i++) {
+ memcpy(dest + (i - y) * w, pagePtr + i * SCREEN_W + x, w);
+ }
+}
+
+void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src) {
+ debug(9, "Screen::copyBlockToPage(%d, %d, %d, %d, %d, 0x%X)", pageNum, x, y, w, h, src);
+ assert(x >= 0 && x < Screen::SCREEN_W && y >= 0 && y < Screen::SCREEN_H);
+ uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W + x;
+ while (h--) {
+ memcpy(dst, src, w);
+ dst += SCREEN_W;
+ src += w;
+ }
+}
+
+void Screen::copyFromCurPageBlock(int x, int y, int w, int h, const uint8 *src) {
+ debug(9, "Screen::copyFromCurPageBlock(%d, %d, %d, %d, 0x%X)", x, y, w, h, src);
+ if (x < 0) {
+ x = 0;
+ } else if (x >= 40) {
+ return;
+ }
+ if (x + w > 40) {
+ w = 40 - x;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y >= 200) {
+ return;
+ }
+ if (y + h > 200) {
+ h = 200 - y;
+ }
+ uint8 *dst = getPagePtr(_curPage) + y * SCREEN_W + x * 8;
+ while (h--) {
+ memcpy(dst, src, w*8);
+ dst += SCREEN_W;
+ src += w*8;
+ }
+}
+
+void Screen::copyCurPageBlock(int x, int y, int w, int h, uint8 *dst) {
+ debug(9, "Screen::copyCurPageBlock(%d, %d, %d, %d, 0x%X)", x, y, w, h, dst);
+ assert(dst);
+ if (x < 0) {
+ x = 0;
+ } else if (x >= 40) {
+ return;
+ }
+ if (x + w > 40) {
+ w = 40 - x;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y >= 200) {
+ return;
+ }
+ if (y + h > 200) {
+ h = 200 - y;
+ }
+ const uint8 *src = getPagePtr(_curPage) + y * SCREEN_W + x * 8;
+ while (h--) {
+ memcpy(dst, src, w*8);
+ dst += w*8;
+ src += SCREEN_W;
+ }
+}
+
+void Screen::shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent) {
+ debug(9, "Screen::shuffleScreen(%d, %d, %d, %d, %d, %d, %d, %d)", sx, sy, w, h, srcPage, dstPage, ticks, transparent);
+ assert(sx >= 0 && w <= SCREEN_W);
+ int x;
+ uint16 x_offs[SCREEN_W];
+ for (x = 0; x < SCREEN_W; ++x) {
+ x_offs[x] = x;
+ }
+ for (x = 0; x < w; ++x) {
+ int i = _vm->_rnd.getRandomNumber(w - 1);
+ SWAP(x_offs[x], x_offs[i]);
+ }
+
+ assert(sy >= 0 && h <= SCREEN_H);
+ int y;
+ uint8 y_offs[SCREEN_H];
+ for (y = 0; y < SCREEN_H; ++y) {
+ y_offs[y] = y;
+ }
+ for (y = 0; y < h; ++y) {
+ int i = _vm->_rnd.getRandomNumber(h - 1);
+ SWAP(y_offs[y], y_offs[i]);
+ }
+
+ int32 start, now;
+ int wait;
+ for (y = 0; y < h; ++y) {
+ start = (int32)_system->getMillis();
+ int y_cur = y;
+ for (x = 0; x < w; ++x) {
+ int i = sx + x_offs[x];
+ int j = sy + y_offs[y_cur];
+ ++y_cur;
+ if (y_cur >= h) {
+ y_cur = 0;
+ }
+ uint8 color = getPagePixel(srcPage, i, j);
+ if (!transparent || color != 0) {
+ setPagePixel(dstPage, i, j, color);
+ }
+ }
+ updateScreen();
+ now = (int32)_system->getMillis();
+ wait = ticks * _vm->tickLength() - (now - start);
+ if (wait > 0) {
+ _vm->delay(wait);
+ }
+ }
+}
+
+void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum) {
+ debug(9, "Screen::fillRect(%d, %d, %d, %d, %d, %d)", x1, y1, x2, y2, color, pageNum);
+ assert(x2 < SCREEN_W && y2 < SCREEN_H);
+ if (pageNum == -1) {
+ pageNum = _curPage;
+ }
+ uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W + x1;
+ for (; y1 <= y2; ++y1) {
+ memset(dst, color, x2 - x1 + 1);
+ dst += SCREEN_W;
+ }
+}
+
+void Screen::drawBox(int x1, int y1, int x2, int y2, int color) {
+ debug(9, "Screen::drawBox(%i, %i, %i, %i, %i)", x1, y1, x2, y2, color);
+
+ drawClippedLine(x1, y1, x2, y1, color);
+ drawClippedLine(x1, y1, x1, y2, color);
+ drawClippedLine(x2, y1, x2, y2, color);
+ drawClippedLine(x1, y2, x2, y2, color);
+}
+
+void Screen::drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2) {
+ debug(9, "Screen::drawShadedBox(%i, %i, %i, %i, %i, %i)", x1, y1, x2, y2, color1, color2);
+ assert(x1 > 0 && y1 > 0);
+ hideMouse();
+
+ fillRect(x1, y1, x2, y1 + 1, color1);
+ fillRect(x2 - 1, y1, x2, y2, color1);
+
+ drawClippedLine(x1, y1, x1, y2, color2);
+ drawClippedLine(x1 + 1, y1 + 1, x1 + 1, y2 - 1, color2);
+ drawClippedLine(x1, y2, x2, y2, color2);
+ drawClippedLine(x1, y2 - 1, x2 - 1, y2 - 1, color2);
+
+ showMouse();
+}
+
+void Screen::drawClippedLine(int x1, int y1, int x2, int y2, int color) {
+ debug(9, "Screen::drawClippedLine(%i, %i, %i, %i, %i)", x1, y1, x2, y2, color);
+
+ if (x1 < 0)
+ x1 = 0;
+ else if (x1 > 319)
+ x1 = 319;
+
+ if (x2 < 0)
+ x2 = 0;
+ else if (x2 > 319)
+ x2 = 319;
+
+ if (y1 < 0)
+ y1 = 0;
+ else if (y1 > 199)
+ y1 = 199;
+
+ if (y2 < 0)
+ y2 = 0;
+ else if (y2 > 199)
+ y2 = 199;
+
+ if (x1 == x2)
+ if (y1 > y2)
+ drawLine(true, x1, y2, y1 - y2 + 1, color);
+ else
+ drawLine(true, x1, y1, y2 - y1 + 1, color);
+ else
+ if (x1 > x2)
+ drawLine(false, x2, y1, x1 - x2 + 1, color);
+ else
+ drawLine(false, x1, y1, x2 - x1 + 1, color);
+}
+
+void Screen::drawLine(bool horizontal, int x, int y, int length, int color) {
+ debug(9, "Screen::drawLine(%i, %i, %i, %i, %i)", horizontal, x, y, length, color);
+
+ uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W + x;
+
+ if (horizontal) {
+ assert((y + length) <= SCREEN_H);
+ int currLine = 0;
+ while (currLine < length) {
+ *ptr = color;
+ ptr += SCREEN_W;
+ currLine++;
+ }
+ } else {
+ assert((x + length) <= SCREEN_W);
+ memset(ptr, color, length);
+ }
+}
+
+void Screen::setAnimBlockPtr(int size) {
+ debug(9, "Screen::setAnimBlockPtr(%d)", size);
+ free(_animBlockPtr);
+ _animBlockPtr = (uint8 *)malloc(size);
+ assert(_animBlockPtr);
+ memset(_animBlockPtr, 0, size);
+ _animBlockSize = size;
+}
+
+void Screen::setTextColorMap(const uint8 *cmap) {
+ debug(9, "Screen::setTextColorMap(0x%X)", cmap);
+ setTextColor(cmap, 0, 11);
+}
+
+void Screen::setTextColor(const uint8 *cmap, int a, int b) {
+ debug(9, "Screen::setTextColor(0x%X, %d, %d)", cmap, a, b);
+ for (int i = a; i <= b; ++i) {
+ _textColorsMap[i] = *cmap++;
+ }
+}
+
+void Screen::loadFont(FontId fontId, uint8 *fontData) {
+ debug(9, "Screen::loadFont(%d, 0x%X)", fontId, fontData);
+ Font *fnt = &_fonts[fontId];
+ assert(fontData && !fnt->fontData);
+ fnt->fontData = fontData;
+ uint16 fontSig = READ_LE_UINT16(fontData + 2);
+ if (fontSig != 0x500) {
+ error("Invalid font data");
+ }
+ fnt->charWidthTable = fontData + READ_LE_UINT16(fontData + 8);
+ fnt->charBoxHeight = READ_LE_UINT16(fontData + 4);
+ fnt->charBitmapOffset = READ_LE_UINT16(fontData + 6);
+ fnt->charWidthTableOffset = READ_LE_UINT16(fontData + 8);
+ fnt->charHeightTableOffset = READ_LE_UINT16(fontData + 0xC);
+}
+
+Screen::FontId Screen::setFont(FontId fontId) {
+ debug(9, "Screen::setFont(%d)", fontId);
+ FontId prev = _currentFont;
+ _currentFont = fontId;
+ return prev;
+}
+
+int Screen::getCharWidth(uint8 c) const {
+ debug(9, "Screen::getCharWidth('%c')", c);
+ return (int)_fonts[_currentFont].charWidthTable[c] + _charWidth;
+}
+
+int Screen::getTextWidth(const char *str) const {
+ debug(9, "Screen::getTextWidth('%s')", str);
+ int curLineLen = 0;
+ int maxLineLen = 0;
+ while (1) {
+ char c = *str++;
+ if (c == 0) {
+ break;
+ } else if (c == '\r') {
+ if (curLineLen > maxLineLen) {
+ maxLineLen = curLineLen;
+ } else {
+ curLineLen = 0;
+ }
+ } else {
+ curLineLen += getCharWidth(c);
+ }
+ }
+ return MAX(curLineLen, maxLineLen);
+}
+
+void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2) {
+ debug(9, "Screen::printText('%s', %d, %d, 0x%X, 0x%X)", str, x, y, color1, color2);
+ uint8 cmap[2];
+ cmap[0] = color2;
+ cmap[1] = color1;
+ setTextColor(cmap, 0, 1);
+
+ Font *fnt = &_fonts[_currentFont];
+ uint8 charHeight = *(fnt->fontData + fnt->charBoxHeight + 4);
+
+ if (x < 0) {
+ x = 0;
+ } else if (x >= SCREEN_W) {
+ return;
+ }
+ int x_start = x;
+ if (y < 0) {
+ y = 0;
+ } else if (y >= SCREEN_H) {
+ return;
+ }
+
+ while (1) {
+ char c = *str++;
+ if (c == 0) {
+ break;
+ } else if (c == '\r') {
+ x = x_start;
+ y += charHeight + _charOffset;
+ } else {
+ int charWidth = getCharWidth(c);
+ if (x + charWidth > SCREEN_W) {
+ x = x_start;
+ y += charHeight + _charOffset;
+ if (y >= SCREEN_H) {
+ break;
+ }
+ }
+ drawChar(c, x, y);
+ x += charWidth;
+ }
+ }
+}
+
+void Screen::drawChar(uint8 c, int x, int y) {
+ debug(9, "Screen::drawChar('%c', %d, %d)", c, x, y);
+ Font *fnt = &_fonts[_currentFont];
+ uint8 *dst = getPagePtr(_curPage) + y * SCREEN_W + x;
+ uint16 bitmapOffset = READ_LE_UINT16(fnt->fontData + fnt->charBitmapOffset + c * 2);
+ if (bitmapOffset == 0) {
+ return;
+ }
+ uint8 charWidth = *(fnt->fontData + fnt->charWidthTableOffset + c);
+ if (charWidth + x > SCREEN_W) {
+ return;
+ }
+ uint8 charH0 = *(fnt->fontData + fnt->charBoxHeight + 4);
+ if (charH0 + y > SCREEN_H) {
+ return;
+ }
+ uint8 charH1 = *(fnt->fontData + fnt->charHeightTableOffset + c * 2);
+ uint8 charH2 = *(fnt->fontData + fnt->charHeightTableOffset + c * 2 + 1);
+ charH0 -= charH1 + charH2;
+
+ const uint8 *src = fnt->fontData + bitmapOffset;
+ const int pitch = SCREEN_W - charWidth;
+
+ while (charH1--) {
+ uint8 col = _textColorsMap[0];
+ for (int i = 0; i < charWidth; ++i) {
+ if (col != 0) {
+ *dst = col;
+ }
+ ++dst;
+ }
+ dst += pitch;
+ }
+
+ while (charH2--) {
+ uint8 b = 0;
+ for (int i = 0; i < charWidth; ++i) {
+ uint8 col;
+ if (i & 1) {
+ col = _textColorsMap[b >> 4];
+ } else {
+ b = *src++;
+ col = _textColorsMap[b & 0xF];
+ }
+ if (col != 0) {
+ *dst = col;
+ }
+ ++dst;
+ }
+ dst += pitch;
+ }
+
+ while (charH0--) {
+ uint8 col = _textColorsMap[0];
+ for (int i = 0; i < charWidth; ++i) {
+ if (col != 0) {
+ *dst = col;
+ }
+ ++dst;
+ }
+ dst += pitch;
+ }
+}
+
+void Screen::setScreenDim(int dim) {
+ debug(9, "setScreenDim(%d)", dim);
+ assert(dim < _screenDimTableCount);
+ _curDim = &_screenDimTable[dim];
+ // XXX
+}
+
+void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) {
+ debug(9, "Screen::drawShape(%d, 0x%X, %d, %d, %d, 0x%.04X, ...)", pageNum, shapeData, x, y, sd, flags);
+ if (!shapeData)
+ return;
+ va_list args;
+ va_start(args, flags);
+
+ static int drawShapeVar1 = 0;
+ static int drawShapeVar2[] = {
+ 1, 3, 2, 5, 4, 3, 2, 1
+ };
+ static int drawShapeVar3 = 1;
+ static int drawShapeVar4 = 0;
+ static int drawShapeVar5 = 0;
+
+ uint8 *table = 0;
+ int tableLoopCount = 0;
+ int drawLayer = 0;
+ uint8 *table2 = 0;
+ uint8 *table3 = 0;
+ uint8 *table4 = 0;
+
+ if (flags & 0x8000) {
+ table2 = va_arg(args, uint8*);
+ }
+ if (flags & 0x100) {
+ table = va_arg(args, uint8*);
+ tableLoopCount = va_arg(args, int);
+ if (!tableLoopCount)
+ flags &= 0xFFFFFEFF;
+ }
+ if (flags & 0x1000) {
+ table3 = va_arg(args, uint8*);
+ table4 = va_arg(args, uint8*);
+ }
+ if (flags & 0x200) {
+ drawShapeVar1 += 1;
+ drawShapeVar1 &= 7;
+ drawShapeVar3 = drawShapeVar2[drawShapeVar1];
+ drawShapeVar4 = 0;
+ drawShapeVar5 = 256;
+ }
+ if (flags & 0x4000) {
+ drawShapeVar5 = va_arg(args, int);
+ }
+ if (flags & 0x800) {
+ drawLayer = va_arg(args, int);
+ }
+ int scale_w, scale_h;
+ if (flags & DSF_SCALE) {
+ scale_w = va_arg(args, int);
+ scale_h = va_arg(args, int);
+ } else {
+ scale_w = 0x100;
+ scale_h = 0x100;
+ }
+
+ int ppc = (flags >> 8) & 0x3F;
+
+ const uint8 *src = shapeData;
+ if (_vm->features() & GF_TALKIE) {
+ src += 2;
+ }
+ uint16 shapeFlags = READ_LE_UINT16(src); src += 2;
+
+ int shapeHeight = *src++;
+ int scaledShapeHeight = (shapeHeight * scale_h) >> 8;
+ if (scaledShapeHeight == 0) {
+ va_end(args);
+ return;
+ }
+
+ int shapeWidth = READ_LE_UINT16(src); src += 2;
+ int scaledShapeWidth = (shapeWidth * scale_w) >> 8;
+ if (scaledShapeWidth == 0) {
+ va_end(args);
+ return;
+ }
+
+ if (flags & DSF_CENTER) {
+ x -= scaledShapeWidth >> 1;
+ y -= scaledShapeHeight >> 1;
+ }
+
+ src += 3;
+
+ uint16 frameSize = READ_LE_UINT16(src); src += 2;
+ if ((shapeFlags & 1) || (flags & 0x400)) {
+ src += 0x10;
+ }
+ if (!(shapeFlags & 2)) {
+ decodeFrame4(src, _animBlockPtr, frameSize);
+ src = _animBlockPtr;
+ }
+
+ int shapeSize = shapeWidth * shapeHeight;
+ if (_decodeShapeBufferSize < shapeSize) {
+ free(_decodeShapeBuffer);
+ _decodeShapeBuffer = (uint8 *)malloc(shapeSize);
+ _decodeShapeBufferSize = shapeSize;
+ }
+ if (!_decodeShapeBuffer) {
+ _decodeShapeBufferSize = 0;
+ va_end(args);
+ return;
+ }
+ memset(_decodeShapeBuffer, 0, _decodeShapeBufferSize);
+ uint8 *decodedShapeFrame = _decodeShapeBuffer;
+
+ // only used if shapeFlag & 1 is NOT zero
+ const uint8 *colorTable = shapeData + 10;
+ if (_vm->features() & GF_TALKIE) {
+ colorTable += 2;
+ }
+
+ for (int j = 0; j < shapeHeight; ++j) {
+ uint8 *dsbNextLine = decodedShapeFrame + shapeWidth;
+ int count = shapeWidth;
+ while (count > 0) {
+ uint8 code = *src++;
+ if (code != 0) {
+ // this is guessed
+ if (shapeFlags & 1) {
+ if (code < 16) {
+ *decodedShapeFrame++ = colorTable[code];
+ }
+ } else {
+ *decodedShapeFrame++ = code;
+ }
+ --count;
+ } else {
+ code = *src++;
+ decodedShapeFrame += code;
+ count -= code;
+ }
+ }
+ decodedShapeFrame = dsbNextLine;
+ }
+
+ uint16 sx1 = _screenDimTable[sd].sx * 8;
+ uint16 sy1 = _screenDimTable[sd].sy;
+ uint16 sx2 = sx1 + _screenDimTable[sd].w * 8;
+ uint16 sy2 = sy1 + _screenDimTable[sd].h;
+ if (flags & DSF_WND_COORDS) {
+ x += sx1;
+ y += sy1;
+ }
+
+ int x1, x2;
+ if (x >= 0) {
+ x1 = 0;
+ if (x + scaledShapeWidth < sx2) {
+ x2 = scaledShapeWidth;
+ } else {
+ x2 = sx2 - x;
+ }
+ } else {
+ x2 = scaledShapeWidth;
+ x1 = -x;
+ x = 0;
+ if (x2 > sx2) {
+ x2 = sx2;
+ }
+ }
+
+ int y1, y2;
+ if (y >= 0) {
+ y1 = 0;
+ if (y + scaledShapeHeight < sy2) {
+ y2 = scaledShapeHeight;
+ } else {
+ y2 = sy2 - y;
+ }
+ } else {
+ y2 = scaledShapeHeight;
+ y1 = -y;
+ y = 0;
+ if (y2 > sy2) {
+ y2 = sy2;
+ }
+ }
+
+ uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W + x;
+ uint8 *dstStart = getPagePtr(pageNum);
+
+ int scaleYTable[SCREEN_H];
+ assert(y1 >= 0 && y2 < SCREEN_H);
+ for (y = y1; y < y2; ++y) {
+ scaleYTable[y] = (y << 8) / scale_h;
+ }
+ int scaleXTable[SCREEN_W];
+ assert(x1 >= 0 && x2 < SCREEN_W);
+ for (x = x1; x < x2; ++x) {
+ scaleXTable[x] = (x << 8) / scale_w;
+ }
+
+ const uint8 *shapeBuffer = _decodeShapeBuffer;
+ if (flags & DSF_Y_FLIPPED) {
+ shapeBuffer += shapeWidth * (shapeHeight - 1);
+ }
+ if (flags & DSF_X_FLIPPED) {
+ shapeBuffer += shapeWidth - 1;
+ }
+
+ for (y = y1; y < y2; ++y) {
+ uint8 *dstNextLine = dst + SCREEN_W;
+ int j = scaleYTable[y];
+ if (flags & DSF_Y_FLIPPED) {
+ j = -j;
+ }
+ for (x = x1; x < x2; ++x) {
+ int xpos = scaleXTable[x];
+ if (flags & DSF_X_FLIPPED) {
+ xpos = -xpos;
+ }
+ uint8 color = shapeBuffer[j * shapeWidth + xpos];
+ if (color != 0) {
+ switch (ppc) {
+ case 0:
+ *dst = color;
+ break;
+
+ case 1:
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ break;
+
+ case 2: {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ drawShapeVar4 = temp & 0xFF;
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ } else {
+ drawShapeVar4 = temp;
+ }
+ } break;
+
+ case 7:
+ case 3:
+ color = *dst;
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ break;
+
+ case 4:
+ color = table2[color];
+ break;
+
+ case 5:
+ color = table2[color];
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ break;
+
+ case 6: {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ drawShapeVar4 = temp & 0xFF;
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ } else {
+ drawShapeVar4 = temp;
+ color = table2[color];
+ }
+ } break;
+
+ case 8: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ }
+ } break;
+
+ case 9: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ } else {
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ }
+ } break;
+
+ case 10: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ drawShapeVar4 = pixel;
+ } else {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ }
+ drawShapeVar4 = temp & 0xFF;
+ }
+ } break;
+
+ case 15:
+ case 11: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ } else {
+ color = *dst;
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ }
+ } break;
+
+ case 12: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ } else {
+ color = table2[color];
+ }
+ } break;
+
+ case 13: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ } else {
+ color = table2[color];
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ }
+ } break;
+
+ case 14: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ drawShapeVar4 = pixel;
+ } else {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ drawShapeVar4 = temp % 0xFF;
+ } else {
+ drawShapeVar4 = temp;
+ color = table2[color];
+ }
+ }
+ } break;
+
+ case 16: {
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 17: {
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 18: {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ drawShapeVar4 = temp & 0xFF;
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } else {
+ drawShapeVar4 = temp;
+ }
+ } break;
+
+ case 23:
+ case 19: {
+ color = *dst;
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 20: {
+ color = table2[color];
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 21: {
+ color = table2[color];
+ for (int i = 0; i < tableLoopCount; ++i) {
+ color = table[color];
+ }
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ case 22: {
+ int temp = drawShapeVar4 + drawShapeVar5;
+ if (temp & 0xFF00) {
+ drawShapeVar4 = temp & 0xFF;
+ dst += drawShapeVar3;
+ color = *dst;
+ dst -= drawShapeVar3;
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } else {
+ drawShapeVar4 = temp;
+ color = table2[color];
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ }
+ } break;
+
+ case 24: {
+ int offset = dst - dstStart;
+ uint8 pixel = *(_shapePages[0] + offset);
+ pixel &= 0x7F;
+ pixel &= 0x87;
+ if (drawLayer < pixel) {
+ color = *(_shapePages[1] + offset);
+ }
+ uint8 newColor = table3[color];
+ if (!(newColor & 0x80)) {
+ color = *dst;
+ color = table4[color + (newColor << 8)];
+ }
+ } break;
+
+ default:
+ warning("unhandled ppc: %d", ppc);
+ break;
+ }
+ *dst = color;
+ }
+ ++dst;
+ }
+ dst = dstNextLine;
+ }
+ va_end(args);
+}
+
+void Screen::decodeFrame3(const uint8 *src, uint8 *dst, uint32 size) {
+ debug(9, "Screen::decodeFrame3(0x%X, 0x%X, %d)", src, dst, size);
+ const uint8 *dstEnd = dst + size;
+ while (dst < dstEnd) {
+ int8 code = *src++;
+ if (code == 0) {
+ uint16 sz = READ_BE_UINT16(src);
+ src += 2;
+ memset(dst, *src++, sz);
+ dst += sz;
+ } else if (code < 0) {
+ memset(dst, *src++, -code);
+ dst -= code;
+ } else {
+ memcpy(dst, src, code);
+ dst += code;
+ src += code;
+ }
+ }
+}
+
+void Screen::decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize) {
+ debug(9, "Screen::decodeFrame4(0x%X, 0x%X, %d)", src, dst, dstSize);
+ uint8 *dstOrig = dst;
+ uint8 *dstEnd = dst + dstSize;
+ while (1) {
+ int count = dstEnd - dst;
+ if (count == 0) {
+ break;
+ }
+ uint8 code = *src++;
+ if (!(code & 0x80)) {
+ int len = MIN(count, (code >> 4) + 3);
+ int offs = ((code & 0xF) << 8) | *src++;
+ const uint8 *dstOffs = dst - offs;
+ while (len--) {
+ *dst++ = *dstOffs++;
+ }
+ } else if (code & 0x40) {
+ int len = (code & 0x3F) + 3;
+ if (code == 0xFE) {
+ len = READ_LE_UINT16(src); src += 2;
+ if (len > count) {
+ len = count;
+ }
+ memset(dst, *src++, len); dst += len;
+ } else {
+ if (code == 0xFF) {
+ len = READ_LE_UINT16(src); src += 2;
+ }
+ int offs = READ_LE_UINT16(src); src += 2;
+ if (len > count) {
+ len = count;
+ }
+ const uint8 *dstOffs = dstOrig + offs;
+ while (len--) {
+ *dst++ = *dstOffs++;
+ }
+ }
+ } else if (code != 0x80) {
+ int len = MIN(count, code & 0x3F);
+ while (len--) {
+ *dst++ = *src++;
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+void Screen::decodeFrameDelta(uint8 *dst, const uint8 *src) {
+ debug(9, "Screen::decodeFrameDelta(0x%X, 0x%X)", dst, src);
+ while (1) {
+ uint8 code = *src++;
+ if (code == 0) {
+ uint8 len = *src++;
+ code = *src++;
+ while (len--) {
+ *dst++ ^= code;
+ }
+ } else if (code & 0x80) {
+ code -= 0x80;
+ if (code != 0) {
+ dst += code;
+ } else {
+ uint16 subcode = READ_LE_UINT16(src); src += 2;
+ if (subcode == 0) {
+ break;
+ } else if (subcode & 0x8000) {
+ subcode -= 0x8000;
+ if (subcode & 0x4000) {
+ uint16 len = subcode - 0x4000;
+ code = *src++;
+ while (len--) {
+ *dst++ ^= code;
+ }
+ } else {
+ while (subcode--) {
+ *dst++ ^= *src++;
+ }
+ }
+ } else {
+ dst += subcode;
+ }
+ }
+ } else {
+ while (code--) {
+ *dst++ ^= *src++;
+ }
+ }
+ }
+}
+
+void Screen::decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch, int noXor) {
+ debug(9, "Screen::decodeFrameDeltaPage(0x%X, 0x%X, %d, %d)", dst, src, pitch, noXor);
+ int count = 0;
+ uint8 *dstNext = dst;
+ while (1) {
+ uint8 code = *src++;
+ if (code == 0) {
+ uint8 len = *src++;
+ code = *src++;
+ while (len--) {
+ if (noXor) {
+ *dst++ = code;
+ } else {
+ *dst++ ^= code;
+ }
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ } else if (code & 0x80) {
+ code -= 0x80;
+ if (code != 0) {
+ dst += code;
+
+ count += code;
+ while (count >= pitch) {
+ count -= pitch;
+ dstNext += SCREEN_W;
+ dst = dstNext + count;
+ }
+ } else {
+ uint16 subcode = READ_LE_UINT16(src); src += 2;
+ if (subcode == 0) {
+ break;
+ } else if (subcode & 0x8000) {
+ subcode -= 0x8000;
+ if (subcode & 0x4000) {
+ uint16 len = subcode - 0x4000;
+ code = *src++;
+ while (len--) {
+ if (noXor) {
+ *dst++ = code;
+ } else {
+ *dst++ ^= code;
+ }
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ } else {
+ while (subcode--) {
+ if (noXor) {
+ *dst++ = *src++;
+ } else {
+ *dst++ ^= *src++;
+ }
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ }
+ } else {
+ dst += subcode;
+
+ count += subcode;
+ while (count >= pitch) {
+ count -= pitch;
+ dstNext += SCREEN_W;
+ dst = dstNext + count;
+ }
+
+ }
+ }
+ } else {
+ while (code--) {
+ if (noXor) {
+ *dst++ = *src++;
+ } else {
+ *dst++ ^= *src++;
+ }
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ }
+ }
+}
+
+uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) {
+ debug(9, "Screen::encodeShape(%d, %d, %d, %d, %d)", x, y, w, h, flags);
+ uint8 *srcPtr = &_pagePtrs[_curPage][y * SCREEN_W + x];
+ int16 shapeSize = 0;
+ uint8 *tmp = srcPtr;
+ int xpos = w;
+
+ for (int i = h; i > 0; --i) {
+ uint8 *start = tmp;
+ shapeSize += w;
+ xpos = w;
+ while (xpos) {
+ uint8 value = *tmp++;
+ --xpos;
+
+ if (!value) {
+ shapeSize += 2;
+ int16 curX = xpos;
+ bool skip = false;
+
+ while (xpos) {
+ value = *tmp++;
+ --xpos;
+
+ if (value) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (!skip)
+ ++curX;
+
+ curX -= xpos;
+ shapeSize -= curX;
+
+ while (curX > 0xFF) {
+ curX -= 0xFF;
+ shapeSize += 2;
+ }
+ }
+ }
+
+ tmp = start + SCREEN_W;
+ }
+
+ int16 shapeSize2 = shapeSize;
+ if (_vm->features() & GF_TALKIE) {
+ shapeSize += 12;
+ } else {
+ shapeSize += 10;
+ }
+ if (flags & 1)
+ shapeSize += 16;
+
+ static uint8 table[274];
+ int tableIndex = 0;
+
+ uint8 *newShape = NULL;
+ newShape = (uint8*)malloc(shapeSize+16);
+ assert(newShape);
+
+ byte *dst = newShape;
+ if (_vm->features() & GF_TALKIE)
+ dst += 2;
+ WRITE_LE_UINT16(dst, (flags & 3)); dst += 2;
+ *dst = h; dst += 1;
+ WRITE_LE_UINT16(dst, w); dst += 2;
+ *dst = h; dst += 1;
+ WRITE_LE_UINT16(dst, shapeSize); dst += 2;
+ WRITE_LE_UINT16(dst, shapeSize2); dst += 2;
+
+ byte *src = srcPtr;
+ if (flags & 1) {
+ dst += 16;
+ memset(table, 0, sizeof(uint8)*274);
+ tableIndex = 1;
+ }
+
+ for (int ypos = h; ypos > 0; --ypos) {
+ uint8 *srcBackUp = src;
+ xpos = w;
+ while (xpos) {
+ uint8 value = *src++;
+ if (value) {
+ if (flags & 1) {
+ if (!table[value]) {
+ if (tableIndex == 16) {
+ value = 1;
+ } else {
+ table[0x100+tableIndex] = value;
+ table[value] = tableIndex;
+ ++tableIndex;
+ value = table[value];
+ }
+ } else {
+ value = table[value];
+ }
+ }
+ --xpos;
+ *dst++ = value;
+ } else {
+ int16 temp = 1;
+ --xpos;
+
+ while (xpos) {
+ if (*src)
+ break;
+ ++src;
+ ++temp;
+ --xpos;
+ }
+
+ while (temp > 0xFF) {
+ *dst++ = 0;
+ *dst++ = 0xFF;
+ temp -= 0xFF;
+ }
+
+ if (temp & 0xFF) {
+ *dst++ = 0;
+ *dst++ = temp & 0xFF;
+ }
+ }
+ }
+ src = srcBackUp + SCREEN_W;
+ }
+
+ if (!(flags & 2)) {
+ if (shapeSize > _animBlockSize) {
+ dst = newShape;
+ if (_vm->features() & GF_TALKIE) {
+ dst += 2;
+ }
+ flags = READ_LE_UINT16(dst);
+ flags |= 2;
+ WRITE_LE_UINT16(dst, flags);
+ } else {
+ src = newShape;
+ if (_vm->features() & GF_TALKIE) {
+ src += 2;
+ }
+ if (flags & 1) {
+ src += 16;
+ }
+ src += 10;
+ uint8 *shapePtrBackUp = src;
+ dst = _animBlockPtr;
+ memcpy(dst, src, shapeSize2);
+
+ int16 size = encodeShapeAndCalculateSize(_animBlockPtr, shapePtrBackUp, shapeSize2);
+ if (size > shapeSize2) {
+ shapeSize -= shapeSize2 - size;
+ newShape = (uint8*)realloc(newShape, shapeSize);
+ assert(newShape);
+ } else {
+ dst = shapePtrBackUp;
+ src = _animBlockPtr;
+ memcpy(dst, src, shapeSize2);
+ dst = newShape;
+ flags = READ_LE_UINT16(dst);
+ flags |= 2;
+ WRITE_LE_UINT16(dst, flags);
+ }
+ }
+ }
+
+ dst = newShape;
+ if (_vm->features() & GF_TALKIE) {
+ dst += 2;
+ }
+ WRITE_LE_UINT16((dst + 6), shapeSize);
+
+ if (flags & 1) {
+ dst = newShape + 10;
+ if (_vm->features() & GF_TALKIE) {
+ dst += 2;
+ }
+ src = &table[0x100];
+ memcpy(dst, src, sizeof(uint8)*16);
+ }
+
+ return newShape;
+}
+
+int16 Screen::encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size_to) {
+ debug(9, "Screen::encodeShapeAndCalculateSize(0x%X, 0x%X, %d)", from, to, size_to);
+ byte *fromPtrEnd = from + size_to;
+ bool skipPixel = true;
+ byte *tempPtr = 0;
+ byte *toPtr = to;
+ byte *fromPtr = from;
+ byte *toPtr2 = to;
+
+ *to++ = 0x81;
+ *to++ = *from++;
+
+ while (from < fromPtrEnd) {
+ byte *curToPtr = to;
+ to = fromPtr;
+ int size = 1;
+
+ while (true) {
+ byte curPixel = *from;
+ if (curPixel == *(from+0x40)) {
+ byte *toBackUp = to;
+ to = from;
+
+ for (int i = 0; i < (fromPtrEnd - from); ++i) {
+ if (*to++ != curPixel)
+ break;
+ }
+ --to;
+ uint16 diffSize = (to - from);
+ if (diffSize >= 0x41) {
+ skipPixel = false;
+ from = to;
+ to = curToPtr;
+ *to++ = 0xFE;
+ WRITE_LE_UINT16(to, diffSize); to += 2;
+ *to++ = (size >> 8) & 0xFF;
+ curToPtr = to;
+ to = toBackUp;
+ continue;
+ } else {
+ to = toBackUp;
+ }
+ }
+
+ bool breakLoop = false;
+ while (true) {
+ if ((from - to) == 0) {
+ breakLoop = true;
+ break;
+ }
+ for (int i = 0; i < (from - to); ++i) {
+ if (*to++ == curPixel)
+ break;
+ }
+ if (*to == curPixel) {
+ if (*(from+size-1) == *(to+size-2))
+ break;
+
+ byte *fromBackUp = from;
+ byte *toBackUp = to;
+ --to;
+ for (int i = 0; i < (fromPtrEnd - from); ++i) {
+ if (*from++ != *to++)
+ break;
+ }
+ if (*(from - 1) == *(to - 1))
+ ++to;
+ from = fromBackUp;
+ int temp = to - toBackUp;
+ to = toBackUp;
+ if (temp >= size) {
+ size = temp;
+ tempPtr = toBackUp - 1;
+ }
+ break;
+ } else {
+ breakLoop = true;
+ break;
+ }
+ }
+
+ if (breakLoop)
+ break;
+ }
+
+ to = curToPtr;
+ if (size > 2) {
+ uint16 word = 0;
+ if (size <= 0x0A) {
+ uint16 diffSize = from - tempPtr;
+ if (size <= 0x0FFF) {
+ byte highByte = ((diffSize & 0xFF00) >> 8) + (((size & 0xFF) - 3) << 4);
+ word = ((diffSize & 0xFF) << 8) | highByte;
+ WRITE_LE_UINT16(to, word); to += 2;
+ from += size;
+ skipPixel = false;
+ continue;
+ }
+ }
+
+ if (size > 0x40) {
+ *to++ = 0xFF;
+ WRITE_LE_UINT16(to, size); to += 2;
+ } else {
+ *to++ = ((size & 0xFF) - 3) | 0xC0;
+ }
+
+ word = tempPtr - fromPtr;
+ WRITE_LE_UINT16(to, word); to += 2;
+ from += size;
+ skipPixel = false;
+ } else {
+ if (!skipPixel) {
+ toPtr2 = to;
+ *to++ = 0x80;
+ }
+
+ if (*toPtr2 == 0xBF) {
+ toPtr2 = to;
+ *to++ = 0x80;
+ }
+
+ ++(*toPtr2);
+ *to++ = *from++;
+ skipPixel = true;
+ }
+ }
+ *to++ = 0x80;
+
+ return (to - toPtr);
+}
+
+int Screen::getRectSize(int x, int y) {
+ if (x < 1) {
+ x = 1;
+ } else if (x > 40) {
+ x = 40;
+ }
+
+ if (y < 1) {
+ y = 1;
+ } else if (y > 200) {
+ y = 200;
+ }
+
+ return ((x*y) << 3);
+}
+
+void Screen::hideMouse() {
+ debug(9, "Screen::hideMouse()");
+ ++_mouseLockCount;
+ _system->showMouse(false);
+}
+
+void Screen::showMouse() {
+ debug(9, "Screen::showMouse()");
+
+ if (_mouseLockCount == 1)
+ _system->showMouse(true);
+
+ if (_mouseLockCount > 0)
+ _mouseLockCount--;
+
+}
+
+void Screen::setShapePages(int page1, int page2) {
+ debug(9, "Screen::setShapePages(%d, %d)", page1, page2);
+ _shapePages[0] = _pagePtrs[page1];
+ _shapePages[1] = _pagePtrs[page2];
+}
+
+void Screen::setMouseCursor(int x, int y, byte *shape) {
+ debug(9, "Screen::setMouseCursor(%d, %d, 0x%X)", x, y, shape);
+ if (!shape)
+ return;
+ // if mouseDisabled
+ // return _mouseShape
+
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+
+ int mouseHeight = *(shape+2);
+ int mouseWidth = (READ_LE_UINT16(shape + 3)) + 2;
+
+ if (_vm->features() & GF_TALKIE)
+ shape -= 2;
+
+ uint8 *cursor = (uint8 *)malloc(mouseHeight * mouseWidth);
+ fillRect(0, 0, mouseWidth, mouseHeight, 0, 8);
+ drawShape(8, shape, 0, 0, 0, 0);
+
+ _system->showMouse(false);
+ copyRegionToBuffer(8, 0, 0, mouseWidth, mouseHeight, cursor);
+ _system->setMouseCursor(cursor, mouseWidth, mouseHeight, x, y, 0);
+ _system->showMouse(true);
+ free(cursor);
+
+ return;
+
+}
+
+void Screen::copyScreenFromRect(int x, int y, int w, int h, uint8 *ptr) {
+ debug(9, "Screen::copyScreenFromRect(%d, %d, %d, %d, 0x%X)", x, y, w, h, ptr);
+ x <<= 3; w <<= 3;
+ uint8 *src = ptr;
+ uint8 *dst = &_pagePtrs[0][y * SCREEN_W + x];
+ for (int i = 0; i < h; ++i) {
+ memcpy(dst, src, w);
+ src += w;
+ dst += SCREEN_W;
+ }
+}
+
+void Screen::copyScreenToRect(int x, int y, int w, int h, uint8 *ptr) {
+ debug(9, "Screen::copyScreenToRect(%d, %d, %d, %d, 0x%X)", x, y, w, h, ptr);
+ x <<= 3; w <<= 3;
+ uint8 *src = &_pagePtrs[0][y * SCREEN_W + x];
+ uint8 *dst = ptr;
+ for (int i = 0; i < h; ++i) {
+ memcpy(dst, src, w);
+ dst += w;
+ src += SCREEN_W;
+ }
+}
+
+uint8 *Screen::getPalette(int num) {
+ debug(9, "Screen::getPalette(%d)", num);
+ assert(num >= 0 && num < 4);
+ if (num == 0) {
+ return _screenPalette;
+ }
+
+ return _palettes[num-1];
+}
+
+byte Screen::getShapeFlag1(int x, int y) {
+ debug(9, "Screen::getShapeFlag1(%d, %d)", x, y);
+ uint8 color = _shapePages[0][y * SCREEN_W + x];
+ color &= 0x80;
+ color ^= 0x80;
+
+ if (color & 0x80) {
+ return 1;
+ }
+ return 0;
+}
+
+byte Screen::getShapeFlag2(int x, int y) {
+ debug(9, "Screen::getShapeFlag2(%d, %d)", x, y);
+ uint8 color = _shapePages[0][y * SCREEN_W + x];
+ color &= 0x7F;
+ color &= 0x87;
+ return color;
+}
+
+int Screen::setNewShapeHeight(uint8 *shape, int height) {
+ debug(9, "Screen::setNewShapeHeight(0x%X, %d)", shape, height);
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+ int oldHeight = shape[2];
+ shape[2] = height;
+ return oldHeight;
+}
+
+int Screen::resetShapeHeight(uint8 *shape) {
+ debug(9, "Screen::setNewShapeHeight(0x%X)", shape);
+ if (_vm->features() & GF_TALKIE)
+ shape += 2;
+ int oldHeight = shape[2];
+ shape[2] = shape[5];
+ return oldHeight;
+}
+
+void Screen::addBitBlitRect(int x, int y, int w, int h) {
+ debug(9, "Screen::addBitBlitRects(%d, %d, %d, %d)", x, y, w, h);
+ if (_bitBlitNum >= BITBLIT_RECTS) {
+ error("too many bit blit rects");
+ }
+ _bitBlitRects[_bitBlitNum].x = x;
+ _bitBlitRects[_bitBlitNum].y = y;
+ _bitBlitRects[_bitBlitNum].x2 = w;
+ _bitBlitRects[_bitBlitNum].y2 = h;
+ ++_bitBlitNum;
+}
+
+void Screen::bitBlitRects() {
+ debug(9, "Screen::bitBlitRects()");
+ Rect *cur = _bitBlitRects;
+ while (_bitBlitNum) {
+ _bitBlitNum--;
+ copyRegion(cur->x, cur->y, cur->x, cur->y, cur->x2, cur->y2, 2, 0);
+ ++cur;
+ }
+}
+
+void Screen::savePageToDisk(const char *file, int page) {
+ debug(9, "Screen::savePageToDisk('%s', %d)", file, page);
+ if (!_saveLoadPage[page/2]) {
+ _saveLoadPage[page/2] = new uint8[SCREEN_W * SCREEN_H];
+ assert(_saveLoadPage[page/2]);
+ }
+ memcpy(_saveLoadPage[page/2], getPagePtr(page), SCREEN_W * SCREEN_H);
+}
+
+void Screen::loadPageFromDisk(const char *file, int page) {
+ debug(9, "Screen::loadPageFromDisk('%s', %d)", file, page);
+ copyBlockToPage(page, 0, 0, SCREEN_W, SCREEN_H, _saveLoadPage[page/2]);
+ delete [] _saveLoadPage[page/2];
+ _saveLoadPage[page/2] = 0;
+}
+
+void Screen::deletePageFromDisk(int page) {
+ debug(9, "Screen::deletePageFromDisk(%d)", page);
+ delete [] _saveLoadPage[page/2];
+ _saveLoadPage[page/2] = 0;
+}
+
+void Screen::blockInRegion(int x, int y, int width, int height) {
+ debug(9, "Screen::blockInRegion(%d, %d, %d, %d)", x, y, width, height);
+ assert(_shapePages[0]);
+ byte *toPtr = _shapePages[0] + (y * 320 + x);
+ for (int i = 0; i < height; ++i) {
+ byte *backUpTo = toPtr;
+ for (int i2 = 0; i2 < width; ++i2) {
+ *toPtr++ &= 0x7F;
+ }
+ toPtr = (backUpTo + 320);
+ }
+}
+
+void Screen::blockOutRegion(int x, int y, int width, int height) {
+ debug(9, "Screen::blockOutRegion(%d, %d, %d, %d)", x, y, width, height);
+ assert(_shapePages[0]);
+ byte *toPtr = _shapePages[0] + (y * 320 + x);
+ for (int i = 0; i < height; ++i) {
+ byte *backUpTo = toPtr;
+ for (int i2 = 0; i2 < width; ++i2) {
+ *toPtr++ |= 0x80;
+ }
+ toPtr = (backUpTo + 320);
+ }
+}
+
+void Screen::rectClip(int &x, int &y, int w, int h) {
+ if (x < 0) {
+ x = 0;
+ } else if (x + w >= 320) {
+ x = 320 - w;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y + h >= 200) {
+ y = 200 - h;
+ }
+}
+
+void Screen::backUpRect0(int xpos, int ypos) {
+ debug(9, "Screen::backUpRect0(%d, %d)", xpos, ypos);
+ rectClip(xpos, ypos, 3<<3, 24);
+ copyRegionToBuffer(_curPage, xpos, ypos, 3<<3, 24, _vm->shapes()[0]);
+}
+
+void Screen::restoreRect0(int xpos, int ypos) {
+ debug(9, "Screen::restoreRect0(%d, %d)", xpos, ypos);
+ rectClip(xpos, ypos, 3<<3, 24);
+ copyBlockToPage(_curPage, xpos, ypos, 3<<3, 24, _vm->shapes()[0]);
+}
+
+void Screen::backUpRect1(int xpos, int ypos) {
+ debug(9, "Screen::backUpRect1(%d, %d)", xpos, ypos);
+ rectClip(xpos, ypos, 4<<3, 32);
+ copyRegionToBuffer(_curPage, xpos, ypos, 4<<3, 32, _vm->shapes()[1]);
+}
+
+void Screen::restoreRect1(int xpos, int ypos) {
+ debug(9, "Screen::restoreRect1(%d, %d)", xpos, ypos);
+ rectClip(xpos, ypos, 4<<3, 32);
+ copyBlockToPage(_curPage, xpos, ypos, 4<<3, 32, _vm->shapes()[1]);
+}
+
+int Screen::getDrawLayer(int x, int y) {
+ debug(9, "Screen::getDrawLayer(%d, %d)", x, y);
+ int xpos = x - 8;
+ int ypos = y - 1;
+ int layer = 1;
+ for (int curX = xpos; curX < xpos + 16; ++curX) {
+ int tempLayer = getShapeFlag2(curX, ypos);
+ if (layer < tempLayer) {
+ layer = tempLayer;
+ }
+ if (layer >= 7) {
+ return 7;
+ }
+ }
+ return layer;
+}
+
+int Screen::getDrawLayer2(int x, int y, int height) {
+ debug(9, "Screen::getDrawLayer2(%d, %d, %d)", x, y, height);
+ int xpos = x - 8;
+ int ypos = y - 1;
+ int layer = 1;
+
+ for (int useX = xpos; useX < xpos + 16; ++useX) {
+ for (int useY = ypos - height; useY < ypos; ++useY) {
+ int tempLayer = getShapeFlag2(useX, useY);
+ if (tempLayer > layer) {
+ layer = tempLayer;
+ }
+
+ if (tempLayer >= 7) {
+ return 7;
+ }
+ }
+ }
+ return layer;
+}
+
+void Screen::copyBackgroundBlock(int x, int page, int flag) {
+ debug(9, "Screen::copyBackgroundBlock(%d, %d, %d)", x, page, flag);
+
+ if (x < 1)
+ return;
+
+ int height = 128;
+ if (flag)
+ height += 8;
+ if (!(x & 1))
+ ++x;
+ if (x == 19)
+ x = 17;
+ uint8 *ptr1 = _unkPtr1;
+ uint8 *ptr2 = _unkPtr2;
+ int oldVideoPage = _curPage;
+ _curPage = page;
+
+ int curX = x;
+ hideMouse();
+ copyRegionToBuffer(_curPage, 8, 8, 8, height, ptr2);
+ for (int i = 0; i < 19; ++i) {
+ int tempX = curX + 1;
+ copyRegionToBuffer(_curPage, tempX<<3, 8, 8, height, ptr1);
+ copyBlockToPage(_curPage, tempX<<3, 8, 8, height, ptr2);
+ int newXPos = curX + x;
+ if (newXPos > 37) {
+ newXPos = newXPos % 38;
+ }
+ tempX = newXPos + 1;
+ copyRegionToBuffer(_curPage, tempX<<3, 8, 8, height, ptr2);
+ copyBlockToPage(_curPage, tempX<<3, 8, 8, height, ptr1);
+ curX += x*2;
+ if (curX > 37) {
+ curX = curX % 38;
+ }
+ }
+ showMouse();
+ _curPage = oldVideoPage;
+}
+
+void Screen::copyBackgroundBlock2(int x) {
+ copyBackgroundBlock(x, 4, 1);
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/screen.h b/engines/kyra/screen.h
new file mode 100644
index 0000000000..0f2c59cbea
--- /dev/null
+++ b/engines/kyra/screen.h
@@ -0,0 +1,206 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef KYRASCREEN_H
+#define KYRASCREEN_H
+
+#include "common/util.h"
+#include <stdarg.h>
+
+class OSystem;
+
+namespace Kyra {
+
+class KyraEngine;
+class Debugger;
+struct Rect;
+
+struct ScreenDim {
+ uint16 sx;
+ uint16 sy;
+ uint16 w;
+ uint16 h;
+ uint16 unk8;
+ uint16 unkA;
+ uint16 unkC;
+ uint16 unkE;
+};
+
+struct Font {
+ uint8 *fontData;
+ uint8 *charWidthTable;
+ uint16 charBoxHeight;
+ uint16 charBitmapOffset;
+ uint16 charWidthTableOffset;
+ uint16 charHeightTableOffset;
+};
+
+class Screen {
+ friend class Debugger;
+public:
+
+ enum {
+ SCREEN_W = 320,
+ SCREEN_H = 200,
+ SCREEN_PAGE_SIZE = 320 * 200 + 1024,
+ SCREEN_PAGE_NUM = 16
+ };
+
+ enum CopyRegionFlags {
+ CR_X_FLIPPED = 0x01,
+ CR_CLIPPED = 0x02
+ };
+
+ enum DrawShapeFlags {
+ DSF_X_FLIPPED = 0x01,
+ DSF_Y_FLIPPED = 0x02,
+ DSF_SCALE = 0x04,
+ DSF_WND_COORDS = 0x10,
+ DSF_CENTER = 0x20
+ };
+
+ enum FontId {
+ FID_6_FNT = 0,
+ FID_8_FNT,
+ FID_CRED6_FNT,
+ FID_CRED8_FNT,
+ FID_NUM
+ };
+
+ Screen(KyraEngine *vm, OSystem *system);
+ ~Screen();
+
+ void updateScreen();
+ uint8 *getPagePtr(int pageNum);
+ void clearPage(int pageNum);
+ int setCurPage(int pageNum);
+ void clearCurPage();
+ uint8 getPagePixel(int pageNum, int x, int y);
+ void setPagePixel(int pageNum, int x, int y, uint8 color);
+ void fadeFromBlack();
+ void fadeToBlack();
+ void fadeSpecialPalette(int palIndex, int startIndex, int size, int fadeTime);
+ void fadePalette(const uint8 *palData, int delay);
+ void setScreenPalette(const uint8 *palData);
+ void copyToPage0(int y, int h, uint8 page, uint8 *seqBuf);
+ void copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags=0);
+ void copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src);
+ void copyFromCurPageBlock(int x, int y, int w, int h, const uint8 *src);
+ void copyCurPageBlock(int x, int y, int w, int h, uint8 *dst);
+ void shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent);
+ void fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum = -1);
+ void drawLine(bool horizontal, int x, int y, int length, int color);
+ void drawClippedLine(int x1, int y1, int x2, int y2, int color);
+ void drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2);
+ void drawBox(int x1, int y1, int x2, int y2, int color);
+ void setAnimBlockPtr(int size);
+ void setTextColorMap(const uint8 *cmap);
+ void setTextColor(const uint8 *cmap, int a, int b);
+ void loadFont(FontId fontId, uint8 *fontData);
+ FontId setFont(FontId fontId);
+ int getCharWidth(uint8 c) const;
+ int getTextWidth(const char *str) const;
+ void printText(const char *str, int x, int y, uint8 color1, uint8 color2);
+ void drawChar(uint8 c, int x, int y);
+ void setScreenDim(int dim);
+ void drawShapePlotPixelCallback1(uint8 *dst, uint8 color);
+ void drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...);
+ static void decodeFrame3(const uint8 *src, uint8 *dst, uint32 size);
+ static void decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize);
+ static void decodeFrameDelta(uint8 *dst, const uint8 *src);
+ static void decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch, int noXor);
+ uint8 *encodeShape(int x, int y, int w, int h, int flags);
+ void copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest);
+
+ int getRectSize(int x, int y);
+ void hideMouse();
+ void showMouse();
+ void setShapePages(int page1, int page2);
+ void setMouseCursor(int x, int y, byte *shape);
+ uint8 *getPalette(int num);
+
+ byte getShapeFlag1(int x, int y);
+ byte getShapeFlag2(int x, int y);
+ int setNewShapeHeight(uint8 *shape, int height);
+ int resetShapeHeight(uint8 *shape);
+
+ void addBitBlitRect(int x, int y, int w, int h);
+ void bitBlitRects();
+
+ void savePageToDisk(const char *file, int page);
+ void loadPageFromDisk(const char *file, int page);
+ void deletePageFromDisk(int page);
+
+ void blockInRegion(int x, int y, int width, int height);
+ void blockOutRegion(int x, int y, int width, int height);
+
+ void backUpRect0(int xpos, int ypos);
+ void restoreRect0(int xpos, int ypos);
+ void backUpRect1(int xpos, int ypos);
+ void restoreRect1(int xpos, int ypos);
+ void copyBackgroundBlock(int x, int page, int flag);
+ void copyBackgroundBlock2(int x);
+ void rectClip(int &x, int &y, int w, int h);
+ int getDrawLayer(int x, int y);
+ int getDrawLayer2(int x, int y, int height);
+
+ int _charWidth;
+ int _charOffset;
+ int _curPage;
+ uint8 *_currentPalette;
+ uint8 *_shapePages[2];
+
+ const ScreenDim *_curDim;
+
+ static const ScreenDim _screenDimTable[];
+ static const int _screenDimTableCount;
+private:
+ int16 encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size);
+ void restoreMouseRect();
+ void copyMouseToScreen();
+ void copyScreenFromRect(int x, int y, int w, int h, uint8 *ptr);
+ void copyScreenToRect(int x, int y, int w, int h, uint8 *ptr);
+
+ uint8 *_pagePtrs[16];
+ uint8 *_saveLoadPage[8];
+ uint8 *_screenPalette;
+ uint8 *_palettes[3];
+ FontId _currentFont;
+ Font _fonts[FID_NUM];
+ uint8 _textColorsMap[16];
+ uint8 *_decodeShapeBuffer;
+ int _decodeShapeBufferSize;
+ uint8 *_animBlockPtr;
+ int _animBlockSize;
+ int _mouseLockCount;
+
+ Rect *_bitBlitRects;
+ int _bitBlitNum;
+ uint8 *_unkPtr1, *_unkPtr2;
+
+ OSystem *_system;
+ KyraEngine *_vm;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/script.cpp b/engines/kyra/script.cpp
new file mode 100644
index 0000000000..b43766dba7
--- /dev/null
+++ b/engines/kyra/script.cpp
@@ -0,0 +1,565 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/system.h"
+#include "kyra/kyra.h"
+#include "kyra/resource.h"
+#include "kyra/script.h"
+
+#define FORM_CHUNK 0x4D524F46
+#define TEXT_CHUNK 0x54584554
+#define DATA_CHUNK 0x41544144
+#define ORDR_CHUNK 0x5244524F
+
+namespace Kyra {
+ScriptHelper::ScriptHelper(KyraEngine *vm) : _vm(vm) {
+#define COMMAND(x) { &ScriptHelper::x, #x }
+ // now we create a list of all Command/Opcode procs and so
+ static CommandEntry commandProcs[] = {
+ // 0x00
+ COMMAND(c1_jmpTo),
+ COMMAND(c1_setRetValue),
+ COMMAND(c1_pushRetOrPos),
+ COMMAND(c1_push),
+ // 0x04
+ COMMAND(c1_push),
+ COMMAND(c1_pushVar),
+ COMMAND(c1_pushBPNeg),
+ COMMAND(c1_pushBPAdd),
+ // 0x08
+ COMMAND(c1_popRetOrPos),
+ COMMAND(c1_popVar),
+ COMMAND(c1_popBPNeg),
+ COMMAND(c1_popBPAdd),
+ // 0x0C
+ COMMAND(c1_addSP),
+ COMMAND(c1_subSP),
+ COMMAND(c1_execOpcode),
+ COMMAND(c1_ifNotJmp),
+ // 0x10
+ COMMAND(c1_negate),
+ COMMAND(c1_eval),
+ COMMAND(c1_setRetAndJmp)
+ };
+ _commands = commandProcs;
+#undef COMMAND
+}
+
+ScriptHelper::~ScriptHelper() {
+}
+
+bool ScriptHelper::loadScript(const char *filename, ScriptData *scriptData, KyraEngine::OpcodeProc *opcodes, int opcodeSize, byte *specialPtr) {
+ uint32 size = 0;
+ uint8 *data = _vm->resource()->fileData(filename, &size);
+ byte *curData = data;
+
+ uint32 formBlockSize = getFORMBlockSize(curData);
+ if (formBlockSize == (uint32)-1) {
+ delete [] data;
+ error("No FORM chunk found in file: '%s'", filename);
+ return false;
+ }
+
+ uint32 chunkSize = getIFFBlockSize(data, curData, size, TEXT_CHUNK);
+ if (chunkSize != (uint32)-1) {
+ if (specialPtr) {
+ scriptData->mustBeFreed = 0;
+ scriptData->text = specialPtr;
+ specialPtr += chunkSize;
+ } else {
+ scriptData->mustBeFreed = 1;
+ scriptData->text = new byte[chunkSize];
+ }
+ if (!loadIFFBlock(data, curData, size, TEXT_CHUNK, scriptData->text, chunkSize)) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("Couldn't load TEXT chunk from file: '%s'", filename);
+ return false;
+ }
+ }
+
+ chunkSize = getIFFBlockSize(data, curData, size, ORDR_CHUNK);
+ if (chunkSize == (uint32)-1) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("No ORDR chunk found in file: '%s'", filename);
+ return false;
+ }
+ if (specialPtr) {
+ scriptData->mustBeFreed = 0;
+ scriptData->ordr = specialPtr;
+ specialPtr += chunkSize;
+ } else {
+ scriptData->mustBeFreed = 1;
+ scriptData->ordr = new byte[chunkSize];
+ }
+ if (!loadIFFBlock(data, curData, size, ORDR_CHUNK, scriptData->ordr, chunkSize)) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("Couldn't load ORDR chunk from file: '%s'", filename);
+ return false;
+ }
+ chunkSize = chunkSize / 2;
+ while (chunkSize--) {
+ ((uint16*)scriptData->ordr)[chunkSize] = READ_BE_UINT16(&((uint16*)scriptData->ordr)[chunkSize]);
+ }
+
+ chunkSize = getIFFBlockSize(data, curData, size, DATA_CHUNK);
+ if (chunkSize == (uint32)-1) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("No DATA chunk found in file: '%s'", filename);
+ return false;
+ }
+ if (specialPtr) {
+ scriptData->mustBeFreed = 0;
+ scriptData->data = specialPtr;
+ specialPtr += chunkSize;
+ } else {
+ scriptData->mustBeFreed = 1;
+ scriptData->data = new byte[chunkSize];
+ }
+ if (!loadIFFBlock(data, curData, size, DATA_CHUNK, scriptData->data, chunkSize)) {
+ delete [] data;
+ unloadScript(scriptData);
+ error("Couldn't load DATA chunk from file: '%s'", filename);
+ return false;
+ }
+ scriptData->dataSize = chunkSize / 2;
+ scriptData->opcodes = opcodes;
+ scriptData->opcodeSize = opcodeSize;
+
+ delete [] data;
+ return true;
+}
+
+void ScriptHelper::unloadScript(ScriptData *data) {
+ if (data->mustBeFreed) {
+ delete [] data->text;
+ delete [] data->ordr;
+ delete [] data->data;
+ }
+
+ data->mustBeFreed = 0;
+ data->text = data->ordr = data->data = 0;
+}
+
+void ScriptHelper::initScript(ScriptState *scriptStat, ScriptData *data) {
+ scriptStat->dataPtr = data;
+ scriptStat->ip = 0;
+ scriptStat->stack[60] = 0;
+ scriptStat->bp = 62;
+ scriptStat->sp = 60;
+}
+
+bool ScriptHelper::startScript(ScriptState *script, int function) {
+ if (!script->dataPtr) {
+ return false;
+ }
+ uint16 functionOffset = ((uint16*)script->dataPtr->ordr)[function];
+ if (functionOffset == (uint16)-1) {
+ return false;
+ }
+ script->ip = &script->dataPtr->data[functionOffset*2];
+ return true;
+}
+
+bool ScriptHelper::validScript(ScriptState *script) {
+ if (!script->ip || !script->dataPtr)
+ return false;
+ return true;
+}
+
+bool ScriptHelper::runScript(ScriptState *script) {
+ _parameter = 0;
+ _continue = true;
+
+ if (!script->ip) {
+ return false;
+ }
+
+ int16 code = READ_BE_UINT16(script->ip); script->ip += 2;
+ int16 opcode = (code >> 8) & 0x1F;
+
+ if (code & 0x8000) {
+ opcode = 0;
+ _parameter = code & 0x7FFF;
+ } else if (code & 0x4000) {
+ _parameter = (int8)(code);
+ } else if (code & 0x2000) {
+ _parameter = READ_BE_UINT16(script->ip); script->ip += 2;
+ } else {
+ _parameter = 0;
+ }
+
+ if (opcode > 18) {
+ error("Script unknown command: %d", opcode);
+ } else {
+ debug(5, "%s(%d)", _commands[opcode].desc, _parameter);
+ (this->*(_commands[opcode].proc))(script);
+ }
+
+ return _continue;
+}
+
+uint32 ScriptHelper::getFORMBlockSize(byte *&data) const {
+ static const uint32 chunkName = FORM_CHUNK;
+ if (READ_LE_UINT32(data) != chunkName) {
+ return (uint32)-1;
+ }
+ data += 4;
+ uint32 retValue = READ_BE_UINT32(data); data += 4;
+ return retValue;
+}
+
+uint32 ScriptHelper::getIFFBlockSize(byte *start, byte *&data, uint32 maxSize, const uint32 chunkName) const {
+ uint32 size = (uint32)-1;
+ bool special = false;
+
+ if (data == (start + maxSize)) {
+ data = start + 0x0C;
+ }
+ while (data < (start + maxSize)) {
+ uint32 chunk = READ_LE_UINT32(data); data += 4;
+ uint32 size_temp = READ_BE_UINT32(data); data += 4;
+ if (chunk != chunkName) {
+ if (special) {
+ data += (size_temp + 1) & 0xFFFFFFFE;
+ } else {
+ data = start + 0x0C;
+ special = true;
+ }
+ } else {
+ // kill our data
+ data = start;
+ size = size_temp;
+ break;
+ }
+ }
+ return size;
+}
+
+bool ScriptHelper::loadIFFBlock(byte *start, byte *&data, uint32 maxSize, const uint32 chunkName, byte *loadTo, uint32 ptrSize) const {
+ bool special = false;
+
+ if (data == (start + maxSize)) {
+ data = start + 0x0C;
+ }
+ while (data < (start + maxSize)) {
+ uint32 chunk = READ_LE_UINT32(data); data += 4;
+ uint32 chunkSize = READ_BE_UINT32(data); data += 4;
+ if (chunk != chunkName) {
+ if (special) {
+ data += (chunkSize + 1) & 0xFFFFFFFE;
+ } else {
+ data = start + 0x0C;
+ special = true;
+ }
+ } else {
+ uint32 loadSize = 0;
+ if (chunkSize < ptrSize)
+ loadSize = chunkSize;
+ else
+ loadSize = ptrSize;
+ memcpy(loadTo, data, loadSize);
+ chunkSize = (chunkSize + 1) & 0xFFFFFFFE;
+ if (chunkSize > loadSize) {
+ data += (chunkSize - loadSize);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+#pragma mark -
+#pragma mark - Command implementations
+#pragma mark -
+
+void ScriptHelper::c1_jmpTo(ScriptState* script) {
+ script->ip = script->dataPtr->data + (_parameter << 1);
+}
+
+void ScriptHelper::c1_setRetValue(ScriptState* script) {
+ script->retValue = _parameter;
+}
+
+void ScriptHelper::c1_pushRetOrPos(ScriptState* script) {
+ switch (_parameter) {
+ case 0:
+ script->stack[--script->sp] = script->retValue;
+ break;
+
+ case 1:
+ script->stack[--script->sp] = (script->ip - script->dataPtr->data) / 2 + 1;
+ script->stack[--script->sp] = script->bp;
+ script->bp = script->sp + 2;
+ break;
+
+ default:
+ _continue = false;
+ script->ip = 0;
+ break;
+ }
+}
+
+void ScriptHelper::c1_push(ScriptState* script) {
+ script->stack[--script->sp] = _parameter;
+}
+
+void ScriptHelper::c1_pushVar(ScriptState* script) {
+ script->stack[--script->sp] = script->variables[_parameter];
+}
+
+void ScriptHelper::c1_pushBPNeg(ScriptState* script) {
+ script->stack[--script->sp] = script->stack[(-(int32)(_parameter + 2)) + script->bp];
+}
+
+void ScriptHelper::c1_pushBPAdd(ScriptState* script) {
+ script->stack[--script->sp] = script->stack[(_parameter - 1) + script->bp];
+}
+
+void ScriptHelper::c1_popRetOrPos(ScriptState* script) {
+ switch (_parameter) {
+ case 0:
+ script->retValue = script->stack[script->sp++];
+ break;
+
+ case 1:
+ if (script->sp >= 60) {
+ _continue = false;
+ script->ip = 0;
+ } else {
+ script->bp = script->stack[script->sp++];
+ script->ip = script->dataPtr->data + (script->stack[script->sp++] << 1);
+ }
+ break;
+
+ default:
+ _continue = false;
+ script->ip = 0;
+ break;
+ }
+}
+
+void ScriptHelper::c1_popVar(ScriptState* script) {
+ script->variables[_parameter] = script->stack[script->sp++];
+}
+
+void ScriptHelper::c1_popBPNeg(ScriptState* script) {
+ script->stack[(-(int32)(_parameter + 2)) + script->bp] = script->stack[script->sp++];
+}
+
+void ScriptHelper::c1_popBPAdd(ScriptState* script) {
+ script->stack[(_parameter - 1) + script->bp] = script->stack[script->sp++];
+}
+
+void ScriptHelper::c1_addSP(ScriptState* script) {
+ script->sp += _parameter;
+}
+
+void ScriptHelper::c1_subSP(ScriptState* script) {
+ script->sp -= _parameter;
+}
+
+void ScriptHelper::c1_execOpcode(ScriptState* script) {
+ assert((uint8)_parameter < script->dataPtr->opcodeSize);
+ if (script->dataPtr->opcodes[(uint8)_parameter] == &KyraEngine::cmd_dummy)
+ debug("calling unimplemented opcode(0x%.02X)", (uint8)_parameter);
+ int val = (_vm->*script->dataPtr->opcodes[(uint8)_parameter])(script);
+ assert(script);
+ script->retValue = val;
+}
+
+void ScriptHelper::c1_ifNotJmp(ScriptState* script) {
+ if (!script->stack[script->sp++]) {
+ _parameter &= 0x7FFF;
+ script->ip = script->dataPtr->data + (_parameter << 1);
+ }
+}
+
+void ScriptHelper::c1_negate(ScriptState* script) {
+ int16 value = script->stack[script->sp];
+ switch (_parameter) {
+ case 0:
+ if (!value) {
+ script->stack[script->sp] = 1;
+ } else {
+ script->stack[script->sp] = 0;
+ }
+ break;
+
+ case 1:
+ script->stack[script->sp] = -value;
+ break;
+
+ case 2:
+ script->stack[script->sp] = ~value;
+ break;
+
+ default:
+ _continue = false;
+ break;
+ }
+}
+
+void ScriptHelper::c1_eval(ScriptState* script) {
+ int16 ret = 0;
+ bool error = false;
+
+ int16 val1 = script->stack[script->sp++];
+ int16 val2 = script->stack[script->sp++];
+
+ switch (_parameter) {
+ case 0:
+ if (!val2 || !val1) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+ break;
+
+ case 1:
+ if (val2 || val1) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 2:
+ if (val1 == val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 3:
+ if (val1 != val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 4:
+ if (val1 > val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 5:
+ if (val1 >= val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 6:
+ if (val1 < val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 7:
+ if (val1 <= val2) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+
+ case 8:
+ ret = val1 + val2;
+ break;
+
+ case 9:
+ ret = val2 - val1;
+ break;
+
+ case 10:
+ ret = val1 * val2;
+ break;
+
+ case 11:
+ ret = val2 / val1;
+ break;
+
+ case 12:
+ ret = val2 >> val1;
+ break;
+
+ case 13:
+ ret = val2 << val1;
+ break;
+
+ case 14:
+ ret = val1 & val2;
+ break;
+
+ case 15:
+ ret = val1 | val2;
+ break;
+
+ case 16:
+ ret = val2 % val1;
+ break;
+
+ case 17:
+ ret = val1 ^ val2;
+ break;
+
+ default:
+ warning("Unknown evaluate func: %d", _parameter);
+ error = true;
+ break;
+ }
+
+ if (error) {
+ script->ip = 0;
+ _continue = false;
+ } else {
+ script->stack[--script->sp] = ret;
+ }
+}
+
+void ScriptHelper::c1_setRetAndJmp(ScriptState* script) {
+ if (script->sp >= 60) {
+ _continue = false;
+ script->ip = 0;
+ } else {
+ script->retValue = script->stack[script->sp++];
+ uint16 temp = script->stack[script->sp++];
+ script->stack[60] = 0;
+ script->ip = &script->dataPtr->data[temp*2];
+ }
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/script.h b/engines/kyra/script.h
new file mode 100644
index 0000000000..28f5170422
--- /dev/null
+++ b/engines/kyra/script.h
@@ -0,0 +1,106 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef KYRASCRIPT_H
+#define KYRASCRIPT_H
+
+#include "kyra/kyra.h"
+
+namespace Kyra {
+struct ScriptData {
+ byte *text;
+ byte *data;
+ byte *ordr;
+ uint16 dataSize;
+ KyraEngine::OpcodeProc *opcodes;
+ int opcodeSize;
+ uint16 mustBeFreed;
+};
+
+struct ScriptState {
+ byte *ip;
+ ScriptData *dataPtr;
+ int16 retValue;
+ uint16 bp;
+ uint16 sp;
+ int16 variables[30];
+ int16 stack[61];
+};
+
+enum {
+ SCRIPT_INIT = 0
+};
+
+class ScriptHelper {
+public:
+ ScriptHelper(KyraEngine *vm);
+ virtual ~ScriptHelper();
+
+ bool loadScript(const char *filename, ScriptData *data, KyraEngine::OpcodeProc *opcodes, int opcodeSize, byte *specialPtr = 0);
+ void unloadScript(ScriptData *data);
+
+ void initScript(ScriptState *scriptState, ScriptData *data);
+ bool startScript(ScriptState *script, int function);
+
+ bool validScript(ScriptState *script);
+
+ bool runScript(ScriptState *script);
+protected:
+ uint32 getFORMBlockSize(byte *&data) const;
+ uint32 getIFFBlockSize(byte *start, byte *&data, uint32 maxSize, const uint32 chunk) const;
+ bool loadIFFBlock(byte *start, byte *&data, uint32 maxSize, const uint32 chunk, byte *loadTo, uint32 ptrSize) const;
+
+ KyraEngine *_vm;
+ int16 _parameter;
+ bool _continue;
+
+ typedef void (ScriptHelper::*CommandProc)(ScriptState*);
+ struct CommandEntry {
+ CommandProc proc;
+ const char* desc;
+ };
+
+ const CommandEntry *_commands;
+private:
+ void c1_jmpTo(ScriptState*);
+ void c1_setRetValue(ScriptState*);
+ void c1_pushRetOrPos(ScriptState*);
+ void c1_push(ScriptState*);
+ //void c1_push(); same as 03
+ void c1_pushVar(ScriptState*);
+ void c1_pushBPNeg(ScriptState*);
+ void c1_pushBPAdd(ScriptState*);
+ void c1_popRetOrPos(ScriptState*);
+ void c1_popVar(ScriptState*);
+ void c1_popBPNeg(ScriptState*);
+ void c1_popBPAdd(ScriptState*);
+ void c1_addSP(ScriptState*);
+ void c1_subSP(ScriptState*);
+ void c1_execOpcode(ScriptState*);
+ void c1_ifNotJmp(ScriptState*);
+ void c1_negate(ScriptState*);
+ void c1_eval(ScriptState*);
+ void c1_setRetAndJmp(ScriptState*);
+};
+} // end of namespace Kyra
+
+#endif
diff --git a/engines/kyra/script_v1.cpp b/engines/kyra/script_v1.cpp
new file mode 100644
index 0000000000..08ff4a6d9f
--- /dev/null
+++ b/engines/kyra/script_v1.cpp
@@ -0,0 +1,1713 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "kyra/kyra.h"
+#include "kyra/script.h"
+#include "kyra/screen.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+#include "common/system.h"
+
+namespace Kyra {
+#define stackPos(x) script->stack[script->sp+x]
+#define stackPosString(x) (char*)&script->dataPtr->text[READ_BE_UINT16(&((uint16 *)script->dataPtr->text)[stackPos(x)])]
+
+int KyraEngine::cmd_magicInMouseItem(ScriptState *script) {
+ debug(3, "cmd_magicInMouseItem(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ magicInMouseItem(stackPos(0), stackPos(1), -1);
+ return 0;
+}
+
+int KyraEngine::cmd_characterSays(ScriptState *script) {
+ _skipFlag = false;
+ if (_features & GF_TALKIE) {
+ debug(3, "cmd_characterSays(0x%X) (%d, '%s', %d, %d)", script, stackPos(0), stackPosString(1), stackPos(2), stackPos(3));
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(stackPos(0));
+ characterSays(stackPosString(1), stackPos(2), stackPos(3));
+ } else {
+ debug(3, "cmd_characterSays(0x%X) ('%s', %d, %d)", script, stackPosString(0), stackPos(1), stackPos(2));
+ characterSays(stackPosString(0), stackPos(1), stackPos(2));
+ }
+
+ return 0;
+}
+
+int KyraEngine::cmd_pauseTicks(ScriptState *script) {
+ debug(3, "cmd_pauseTicks(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ if (stackPos(1)) {
+ warning("STUB: special cmd_pauseTicks");
+ // delete this after correct implementing
+ delayWithTicks(stackPos(0));
+ } else {
+ delayWithTicks(stackPos(0));
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_drawSceneAnimShape(ScriptState *script) {
+ debug(3, "cmd_drawSceneAnimShape(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ _screen->drawShape(stackPos(4), _sprites->_sceneShapes[stackPos(0)], stackPos(1), stackPos(2), 0, (stackPos(3) != 0) ? 1 : 0);
+ return 0;
+}
+
+int KyraEngine::cmd_queryGameFlag(ScriptState *script) {
+ debug(3, "cmd_queryGameFlag(0x%X) (0x%X)", script, stackPos(0));
+ return queryGameFlag(stackPos(0));
+}
+
+int KyraEngine::cmd_setGameFlag(ScriptState *script) {
+ debug(3, "cmd_setGameFlag(0x%X) (0x%X)", script, stackPos(0));
+ return setGameFlag(stackPos(0));
+}
+
+int KyraEngine::cmd_resetGameFlag(ScriptState *script) {
+ debug(3, "cmd_resetGameFlag(0x%X) (0x%X)", script, stackPos(0));
+ return resetGameFlag(stackPos(0));
+}
+
+int KyraEngine::cmd_runNPCScript(ScriptState *script) {
+ warning("STUB: cmd_runNPCScript");
+ return 0;
+}
+
+int KyraEngine::cmd_setSpecialExitList(ScriptState *script) {
+ debug(3, "cmd_setSpecialExitList(0x%X) (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7), stackPos(8), stackPos(9));
+
+ for (int i = 0; i < 10; ++i) {
+ _exitList[i] = stackPos(i);
+ }
+ _exitListPtr = _exitList;
+
+ return 0;
+}
+
+int KyraEngine::cmd_blockInWalkableRegion(ScriptState *script) {
+ debug(3, "cmd_blockInWalkableRegion(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _screen->blockInRegion(stackPos(0), stackPos(1), stackPos(2)-stackPos(0)+1, stackPos(3)-stackPos(1)+1);
+ return 0;
+}
+
+int KyraEngine::cmd_blockOutWalkableRegion(ScriptState *script) {
+ debug(3, "cmd_blockOutWalkableRegion(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _screen->blockOutRegion(stackPos(0), stackPos(1), stackPos(2)-stackPos(0)+1, stackPos(3)-stackPos(1)+1);
+ return 0;
+}
+
+int KyraEngine::cmd_walkPlayerToPoint(ScriptState *script) {
+ debug(3, "cmd_walkPlayerToPoint(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+
+ int normalTimers = stackPos(2);
+ if (!normalTimers) {
+ disableTimer(19);
+ disableTimer(14);
+ disableTimer(18);
+ }
+
+ int reinitScript = handleSceneChange(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+
+ if (!normalTimers) {
+ enableTimer(19);
+ enableTimer(14);
+ enableTimer(18);
+ }
+
+ if (reinitScript) {
+ _scriptInterpreter->initScript(script, script->dataPtr);
+ }
+
+ if (_sceneChangeState) {
+ _sceneChangeState = 0;
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_dropItemInScene(ScriptState *script) {
+ debug(3, "cmd_dropItemInScene(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ int item = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+
+ byte freeItem = findFreeItemInScene(_currentCharacter->sceneId);
+ if (freeItem != 0xFF) {
+ int sceneId = _currentCharacter->sceneId;
+ Room *room = &_roomTable[sceneId];
+ room->itemsXPos[freeItem] = xpos;
+ room->itemsYPos[freeItem] = ypos;
+ room->itemsTable[freeItem] = item;
+
+ _animator->animAddGameItem(freeItem, sceneId);
+ _animator->updateAllObjectShapes();
+ } else {
+ if (item == 43) {
+ placeItemInGenericMapScene(item, 0);
+ } else {
+ placeItemInGenericMapScene(item, 1);
+ }
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_drawAnimShapeIntoScene(ScriptState *script) {
+ debug(3, "cmd_drawAnimShapeIntoScene(0x%X) (%d, %d, %d, %d)", stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _screen->hideMouse();
+ _animator->restoreAllObjectBackgrounds();
+ int shape = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+ int flags = (stackPos(3) != 0) ? 1 : 0;
+ _screen->drawShape(2, _sprites->_sceneShapes[shape], xpos, ypos, 0, flags);
+ _screen->drawShape(0, _sprites->_sceneShapes[shape], xpos, ypos, 0, flags);
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->preserveAnyChangedBackgrounds();
+ _animator->flagAllObjectsForRefresh();
+ _animator->updateAllObjectShapes();
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_createMouseItem(ScriptState *script) {
+ debug(3, "cmd_createMouseItem(0x%X) (%d)", script, stackPos(0));
+ createMouseItem(stackPos(0));
+ return 0;
+}
+
+int KyraEngine::cmd_savePageToDisk(ScriptState *script) {
+ debug(3, "cmd_savePageToDisk(0x%X) ('%s', %d)", script, stackPosString(0), stackPos(1));
+ _screen->savePageToDisk(stackPosString(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_sceneAnimOn(ScriptState *script) {
+ debug(3, "cmd_sceneAnimOn(0x%X) (%d)", script, stackPos(0));
+ _sprites->_anims[stackPos(0)].play = true;
+ return 0;
+}
+
+int KyraEngine::cmd_sceneAnimOff(ScriptState *script) {
+ debug(3, "cmd_sceneAnimOff(0x%X) (%d)", script, stackPos(0));
+ _sprites->_anims[stackPos(0)].play = false;
+ return 0;
+}
+
+int KyraEngine::cmd_getElapsedSeconds(ScriptState *script) {
+ debug(3, "cmd_getElapsedSeconds(0x%X) ()", script);
+ return _system->getMillis() / 1000;
+}
+
+int KyraEngine::cmd_mouseIsPointer(ScriptState *script) {
+ debug(3, "cmd_mouseIsPointer(0x%X) ()", script);
+ if (_itemInHand == -1) {
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_destroyMouseItem(ScriptState *script) {
+ debug(3, "cmd_destroyMouseItem(0x%X) ()", script);
+ destroyMouseItem();
+ return 0;
+}
+
+int KyraEngine::cmd_runSceneAnimUntilDone(ScriptState *script) {
+ debug(3, "cmd_runSceneAnimUntilDone(0x%X) (%d)", script, stackPos(0));
+ _screen->hideMouse();
+ _animator->restoreAllObjectBackgrounds();
+ _sprites->_anims[stackPos(0)].play = true;
+ _animator->sprites()[stackPos(0)].active = 1;
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->preserveAnyChangedBackgrounds();
+ while (_sprites->_anims[stackPos(0)].play) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ delay(10);
+ }
+ _animator->restoreAllObjectBackgrounds();
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_fadeSpecialPalette(ScriptState *script) {
+ debug(3, "cmd_fadeSpecialPalette(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ _screen->fadeSpecialPalette(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ return 0;
+}
+
+int KyraEngine::cmd_playAdlibSound(ScriptState *script) {
+ debug(3, "cmd_playAdlibSound(0x%X) (%d)", script, stackPos(0));
+ snd_playSoundEffect(stackPos(0));
+ return 0;
+}
+
+int KyraEngine::cmd_playAdlibScore(ScriptState *script) {
+ debug(3, "cmd_playAdlibScore(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ snd_playWanderScoreViaMap(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_phaseInSameScene(ScriptState *script) {
+ debug(3, "cmd_phaseInSameScene(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ transcendScenes(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_setScenePhasingFlag(ScriptState *script) {
+ debug(3, "cmd_setScenePhasingFlag(0x%X) ()", script);
+ _scenePhasingFlag = 1;
+ return 1;
+}
+
+int KyraEngine::cmd_resetScenePhasingFlag(ScriptState *script) {
+ debug(3, "cmd_resetScenePhasingFlag(0x%X) ()", script);
+ _scenePhasingFlag = 0;
+ return 0;
+}
+
+int KyraEngine::cmd_queryScenePhasingFlag(ScriptState *script) {
+ debug(3, "cmd_queryScenePhasingFlag(0x%X) ()", script);
+ return _scenePhasingFlag;
+}
+
+int KyraEngine::cmd_sceneToDirection(ScriptState *script) {
+ debug(3, "cmd_sceneToDirection(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ assert(stackPos(0) < _roomTableSize);
+ Room *curRoom = &_roomTable[stackPos(0)];
+ uint16 returnValue = 0xFFFF;
+ switch (stackPos(1)) {
+ case 0:
+ returnValue = curRoom->northExit;
+ break;
+
+ case 2:
+ returnValue = curRoom->eastExit;
+ break;
+
+ case 4:
+ returnValue = curRoom->southExit;
+ break;
+
+ case 6:
+ returnValue = curRoom->westExit;
+ break;
+
+ default:
+ break;
+ }
+ if (returnValue == 0xFFFF)
+ return -1;
+ return returnValue;
+}
+
+int KyraEngine::cmd_setBirthstoneGem(ScriptState *script) {
+ debug(3, "cmd_setBirthstoneGem(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ int index = stackPos(0);
+ if (index < 4 && index >= 0) {
+ _birthstoneGemTable[index] = stackPos(1);
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_placeItemInGenericMapScene(ScriptState *script) {
+ debug(3, "cmd_placeItemInGenericMapScene(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ placeItemInGenericMapScene(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_setBrandonStatusBit(ScriptState *script) {
+ debug(3, "cmd_setBrandonStatusBit(0x%X) (%d)", script, stackPos(0));
+ _brandonStatusBit |= stackPos(0);
+ return 0;
+}
+
+int KyraEngine::cmd_pauseSeconds(ScriptState *script) {
+ debug(3, "cmd_pauseSeconds(0x%X) (%d)", script, stackPos(0));
+ if (stackPos(0) > 0 && !_skipFlag)
+ delay(stackPos(0)*1000, true);
+ _skipFlag = false;
+ return 0;
+}
+
+int KyraEngine::cmd_getCharactersLocation(ScriptState *script) {
+ debug(3, "cmd_getCharactersLocation(0x%X) (%d)", script, stackPos(0));
+ return _characterList[stackPos(0)].sceneId;
+}
+
+int KyraEngine::cmd_runNPCSubscript(ScriptState *script) {
+ warning("STUB: cmd_runNPCSubscript");
+ return 0;
+}
+
+int KyraEngine::cmd_magicOutMouseItem(ScriptState *script) {
+ debug(3, "cmd_magicOutMouseItem(0x%X) (%d)", script, stackPos(0));
+ magicOutMouseItem(stackPos(0), -1);
+ return 0;
+}
+
+int KyraEngine::cmd_internalAnimOn(ScriptState *script) {
+ debug(3, "cmd_internalAnimOn(0x%X) (%d)", script, stackPos(0));
+ _animator->sprites()[stackPos(0)].active = 1;
+ return 0;
+}
+
+int KyraEngine::cmd_forceBrandonToNormal(ScriptState *script) {
+ debug(3, "cmd_forceBrandonToNormal(0x%X) ()", script);
+ checkAmuletAnimFlags();
+ return 0;
+}
+
+int KyraEngine::cmd_poisonDeathNow(ScriptState *script) {
+ debug(3, "cmd_poisonDeathNow(0x%X) ()", script);
+ seq_poisonDeathNow(1);
+ return 0;
+}
+
+int KyraEngine::cmd_setScaleMode(ScriptState *script) {
+ debug(3, "cmd_setScaleMode(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ int len = stackPos(0);
+ int setValue1 = stackPos(1);
+ int start2 = stackPos(2);
+ int setValue2 = stackPos(3);
+ for (int i = 0; i < len; ++i) {
+ _scaleTable[i] = setValue1;
+ }
+ int temp = setValue2 - setValue1;
+ int temp2 = start2 - len;
+ for (int i = len, offset = 0; i < start2; ++i, ++offset) {
+ _scaleTable[i] = (offset * temp) / temp2 + setValue1;
+ }
+ for (int i = start2; i < 145; ++i) {
+ _scaleTable[i] = setValue2;
+ }
+ _scaleMode = 1;
+ return _scaleMode;
+}
+
+int KyraEngine::cmd_openWSAFile(ScriptState *script) {
+ debug(3, "cmd_openWSAFile(0x%X) ('%s', %d, %d)", script, stackPosString(0), stackPos(1), stackPos(2));
+
+ char *filename = stackPosString(0);
+ int wsaIndex = stackPos(1);
+
+ _movieObjects[wsaIndex]->open(filename, (stackPos(2) != 0) ? 1 : 0, 0);
+ assert(_movieObjects[wsaIndex]->opened());
+
+ return 0;
+}
+
+int KyraEngine::cmd_closeWSAFile(ScriptState *script) {
+ debug(3, "cmd_closeWSAFile(0x%X) (%d)", script, stackPos(0));
+
+ int wsaIndex = stackPos(0);
+ if (_movieObjects[wsaIndex]) {
+ _movieObjects[wsaIndex]->close();
+ }
+
+ return 0;
+}
+
+int KyraEngine::cmd_runWSAFromBeginningToEnd(ScriptState *script) {
+ debug(3, "cmd_runWSAFromBeginningToEnd(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+
+ _screen->hideMouse();
+
+ bool running = true;
+
+ int xpos = stackPos(0);
+ int ypos = stackPos(1);
+ int waitTime = stackPos(2);
+ int wsaIndex = stackPos(3);
+ int worldUpdate = stackPos(4);
+ int wsaFrame = 0;
+
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 0;
+ while (running) {
+ _movieObjects[wsaIndex]->displayFrame(wsaFrame++);
+ _animator->_updateScreen = true;
+ if (wsaFrame >= _movieObjects[wsaIndex]->frames())
+ running = false;
+
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ while (_system->getMillis() < continueTime) {
+ if (worldUpdate) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ } else {
+ _screen->updateScreen();
+ }
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::cmd_displayWSAFrame(ScriptState *script) {
+ debug(3, "cmd_displayWSAFrame(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ int frame = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+ int waitTime = stackPos(3);
+ int wsaIndex = stackPos(4);
+ _screen->hideMouse();
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 0;
+ _movieObjects[wsaIndex]->displayFrame(frame);
+ _animator->_updateScreen = true;
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ while (_system->getMillis() < continueTime && !_skipFlag) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_enterNewScene(ScriptState *script) {
+ debug(3, "cmd_enterNewScene(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ enterNewScene(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ return 0;
+}
+
+int KyraEngine::cmd_setSpecialEnterXAndY(ScriptState *script) {
+ debug(3, "cmd_setSpecialEnterXAndY(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _brandonPosX = stackPos(0);
+ _brandonPosY = stackPos(1);
+ if (_brandonPosX + 1 == 0 && _brandonPosY + 1 == 0)
+ _currentCharacter->currentAnimFrame = 88;
+ return 0;
+}
+
+int KyraEngine::cmd_runWSAFrames(ScriptState *script) {
+ debug(3, "cmd_runWSAFrames(0x%X) (%d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ int xpos = stackPos(0);
+ int ypos = stackPos(1);
+ int delayTime = stackPos(2);
+ int startFrame = stackPos(3);
+ int endFrame = stackPos(4);
+ int wsaIndex = stackPos(5);
+ _screen->hideMouse();
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 0;
+ for (; startFrame <= endFrame; ++startFrame) {
+ uint32 nextRun = _system->getMillis() + delayTime * _tickLength;
+ _movieObjects[wsaIndex]->displayFrame(startFrame);
+ _animator->_updateScreen = true;
+ while (_system->getMillis() < nextRun) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (nextRun - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_popBrandonIntoScene(ScriptState *script) {
+ debug(3, "cmd_popBrandonIntoScene(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ int changeScaleMode = stackPos(3);
+ int xpos = (int16)(stackPos(0) & 0xFFFC);
+ int ypos = (int16)(stackPos(1) & 0xFFFE);
+ int facing = stackPos(2);
+ _currentCharacter->x1 = _currentCharacter->x2 = xpos;
+ _currentCharacter->y1 = _currentCharacter->y2 = ypos;
+ _currentCharacter->facing = facing;
+ _currentCharacter->currentAnimFrame = 7;
+ int xOffset = _defaultShapeTable[0].xOffset;
+ int yOffset = _defaultShapeTable[0].yOffset;
+ int width = _defaultShapeTable[0].w << 3;
+ int height = _defaultShapeTable[0].h;
+ AnimObject *curAnim = _animator->actors();
+
+ if (changeScaleMode) {
+ curAnim->x1 = _currentCharacter->x1;
+ curAnim->y1 = _currentCharacter->y1;
+ _animator->_brandonScaleY = _scaleTable[_currentCharacter->y1];
+ _animator->_brandonScaleX = _animator->_brandonScaleY;
+
+ int animWidth = _animator->fetchAnimWidth(curAnim->sceneAnimPtr, _animator->_brandonScaleX) >> 1;
+ int animHeight = _animator->fetchAnimHeight(curAnim->sceneAnimPtr, _animator->_brandonScaleY);
+
+ animWidth = (xOffset * animWidth) / width;
+ animHeight = (yOffset * animHeight) / height;
+
+ curAnim->x2 = curAnim->x1 += animWidth;
+ curAnim->y2 = curAnim->y1 += animHeight;
+ } else {
+ curAnim->x2 = curAnim->x1 = _currentCharacter->x1 + xOffset;
+ curAnim->y2 = curAnim->y1 = _currentCharacter->y1 + yOffset;
+ }
+
+ int scaleModeBackup = _scaleMode;
+ if (changeScaleMode) {
+ _scaleMode = 1;
+ }
+
+ _animator->animRefreshNPC(0);
+ _animator->preserveAllBackgrounds();
+ _animator->prepDrawAllObjects();
+ _animator->copyChangedObjectsForward(0);
+
+ _scaleMode = scaleModeBackup;
+
+ return 0;
+}
+
+int KyraEngine::cmd_restoreAllObjectBackgrounds(ScriptState *script) {
+ debug(3, "cmd_restoreAllObjectBackgrounds(0x%X) ()", script);
+ _animator->restoreAllObjectBackgrounds();
+ return 0;
+}
+
+int KyraEngine::cmd_setCustomPaletteRange(ScriptState *script) {
+ debug(3, "cmd_setCustomPaletteRange(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ uint8 *screenPal = _screen->_currentPalette;
+ memcpy(&screenPal[stackPos(1)*3], _specialPalettes[stackPos(0)], stackPos(2)*3);
+ _screen->setScreenPalette(screenPal);
+ return 0;
+}
+
+int KyraEngine::cmd_loadPageFromDisk(ScriptState *script) {
+ debug(3, "cmd_loadPageFromDisk(0x%X) ('%s', %d)", script, stackPosString(0), stackPos(1));
+ _screen->loadPageFromDisk(stackPosString(0), stackPos(1));
+ _animator->_updateScreen = true;
+ return 0;
+}
+
+int KyraEngine::cmd_customPrintTalkString(ScriptState *script) {
+ if (_features & GF_TALKIE) {
+ debug(3, "cmd_customPrintTalkString(0x%X) (%d, '%s', %d, %d, %d)", script, stackPos(0), stackPosString(1), stackPos(2), stackPos(3), stackPos(4) & 0xFF);
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(stackPos(0));
+ _skipFlag = false;
+ _text->printTalkTextMessage(stackPosString(1), stackPos(2), stackPos(3), stackPos(4) & 0xFF, 0, 2);
+ } else {
+ debug(3, "cmd_customPrintTalkString(0x%X) ('%s', %d, %d, %d)", script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3) & 0xFF);
+ _skipFlag = false;
+ _text->printTalkTextMessage(stackPosString(0), stackPos(1), stackPos(2), stackPos(3) & 0xFF, 0, 2);
+ }
+ _screen->updateScreen();
+ return 0;
+}
+
+int KyraEngine::cmd_restoreCustomPrintBackground(ScriptState *script) {
+ debug(3, "cmd_restoreCustomPrintBackground(0x%X) ()", script);
+ _text->restoreTalkTextMessageBkgd(2, 0);
+ return 0;
+}
+
+int KyraEngine::cmd_hideMouse(ScriptState *script) {
+ debug(3, "cmd_hideMouse(0x%X) ()", script);
+ _screen->hideMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_showMouse(ScriptState *script) {
+ debug(3, "cmd_showMouse(0x%X) ()", script);
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_getCharacterX(ScriptState *script) {
+ debug(3, "cmd_getCharacterX(0x%X) (%d)", script, stackPos(0));
+ return _characterList[stackPos(0)].x1;
+}
+
+int KyraEngine::cmd_getCharacterY(ScriptState *script) {
+ debug(3, "cmd_getCharacterY(0x%X) (%d)", script, stackPos(0));
+ return _characterList[stackPos(0)].y1;
+}
+
+int KyraEngine::cmd_changeCharactersFacing(ScriptState *script) {
+ debug(3, "cmd_changeCharactersFacing(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ int character = stackPos(0);
+ int facing = stackPos(1);
+ int newAnimFrame = stackPos(2);
+
+ _animator->restoreAllObjectBackgrounds();
+ if (newAnimFrame != -1) {
+ _characterList[character].currentAnimFrame = newAnimFrame;
+ }
+ _characterList[character].facing = facing;
+ _animator->animRefreshNPC(character);
+ _animator->preserveAllBackgrounds();
+ _animator->prepDrawAllObjects();
+ _animator->copyChangedObjectsForward(0);
+
+ return 0;
+}
+
+int KyraEngine::cmd_copyWSARegion(ScriptState *script) {
+ debug(3, "cmd_copyWSARegion(0x%X) (%d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ int xpos = stackPos(0);
+ int ypos = stackPos(1);
+ int width = stackPos(2);
+ int height = stackPos(3);
+ int srcPage = stackPos(4);
+ int dstPage = stackPos(5);
+ _screen->copyRegion(xpos, ypos, xpos, ypos, width, height, srcPage, dstPage);
+ _animator->_updateScreen = true;
+ return 0;
+}
+
+int KyraEngine::cmd_printText(ScriptState *script) {
+ debug(3, "cmd_printText(0x%X) ('%s', %d, %d, 0x%X, 0x%X)", script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ _screen->printText(stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ _screen->updateScreen();
+ return 0;
+}
+
+int KyraEngine::cmd_random(ScriptState *script) {
+ debug(3, "cmd_random(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ assert(stackPos(0) < stackPos(1));
+ return _rnd.getRandomNumberRng(stackPos(0), stackPos(1));
+}
+
+int KyraEngine::cmd_loadSoundFile(ScriptState *script) {
+ warning("STUB: cmd_loadSoundFile");
+ return 0;
+}
+
+int KyraEngine::cmd_displayWSAFrameOnHidPage(ScriptState *script) {
+ debug(3, "cmd_displayWSAFrameOnHidPage(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ int frame = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+ int waitTime = stackPos(3);
+ int wsaIndex = stackPos(4);
+
+ _screen->hideMouse();
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 2;
+ _movieObjects[wsaIndex]->displayFrame(frame);
+ _animator->_updateScreen = true;
+ while (_system->getMillis() < continueTime) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::cmd_displayWSASequentialFrames(ScriptState *script) {
+ debug(3, "cmd_displayWSASequentialFrames(0x%X) (%d, %d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6));
+ int startFrame = stackPos(0);
+ int endFrame = stackPos(1);
+ int xpos = stackPos(2);
+ int ypos = stackPos(3);
+ int waitTime = stackPos(4);
+ int wsaIndex = stackPos(5);
+ int maxTime = stackPos(6);
+ if (maxTime - 1 <= 0)
+ maxTime = 1;
+
+ _movieObjects[wsaIndex]->_x = xpos;
+ _movieObjects[wsaIndex]->_y = ypos;
+ _movieObjects[wsaIndex]->_drawPage = 0;
+
+ int curTime = 0;
+ _screen->hideMouse();
+ while (curTime < maxTime) {
+ if (endFrame >= startFrame) {
+ int frame = startFrame;
+ while (endFrame >= frame) {
+ _movieObjects[wsaIndex]->displayFrame(frame);
+ _animator->_updateScreen = true;
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ while (_system->getMillis() < continueTime && !_skipFlag) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ ++frame;
+ }
+ } else {
+ int frame = startFrame;
+ while (endFrame <= frame) {
+ _movieObjects[wsaIndex]->displayFrame(frame);
+ _animator->_updateScreen = true;
+ uint32 continueTime = waitTime * _tickLength + _system->getMillis();
+ while (_system->getMillis() < continueTime && !_skipFlag) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (continueTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ --frame;
+ }
+ }
+ if (!_skipFlag)
+ break;
+ else
+ ++curTime;
+ }
+ _screen->showMouse();
+
+ return 0;
+}
+
+int KyraEngine::cmd_drawCharacterStanding(ScriptState *script) {
+ debug(3, "cmd_drawCharacterStanding(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ int character = stackPos(0);
+ int animFrame = stackPos(1);
+ int newFacing = stackPos(2);
+ int updateShapes = stackPos(3);
+ _characterList[character].currentAnimFrame = animFrame;
+ if (newFacing != -1) {
+ _characterList[character].facing = newFacing;
+ }
+ _animator->animRefreshNPC(character);
+ if (updateShapes) {
+ _animator->updateAllObjectShapes();
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_internalAnimOff(ScriptState *script) {
+ debug(3, "cmd_internalAnimOff(0x%X) (%d)", script, stackPos(0));
+ _animator->sprites()[stackPos(0)].active = 0;
+ return 0;
+}
+
+int KyraEngine::cmd_changeCharactersXAndY(ScriptState *script) {
+ debug(3, "cmd_changeCharactersXAndY(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ Character *ch = &_characterList[stackPos(0)];
+ int16 x = stackPos(1);
+ int16 y = stackPos(2);
+ if (x != -1 && y != -1) {
+ x &= 0xFFFC;
+ y &= 0xFFFE;
+ }
+ _animator->restoreAllObjectBackgrounds();
+ ch->x1 = ch->x2 = x;
+ ch->y1 = ch->y2 = y;
+ _animator->preserveAllBackgrounds();
+ return 0;
+}
+
+int KyraEngine::cmd_clearSceneAnimatorBeacon(ScriptState *script) {
+ debug(3, "cmd_clearSceneAnimatorBeacon(0x%X) ()", script);
+ _sprites->_sceneAnimatorBeaconFlag = 0;
+ return 0;
+}
+
+int KyraEngine::cmd_querySceneAnimatorBeacon(ScriptState *script) {
+ debug(3, "cmd_querySceneAnimatorBeacon(0x%X) ()", script);
+ return _sprites->_sceneAnimatorBeaconFlag;
+}
+
+int KyraEngine::cmd_refreshSceneAnimator(ScriptState *script) {
+ debug(3, "cmd_refreshSceneAnimator(0x%X) ()", script);
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ return 0;
+}
+
+int KyraEngine::cmd_placeItemInOffScene(ScriptState *script) {
+ debug(3, "cmd_placeItemInOffScene(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ int item = stackPos(0);
+ int xpos = stackPos(1);
+ int ypos = stackPos(2);
+ int sceneId = stackPos(3);
+
+ byte freeItem = findFreeItemInScene(sceneId);
+ if (freeItem != 0xFF) {
+ assert(sceneId < _roomTableSize);
+ Room *room = &_roomTable[sceneId];
+
+ room->itemsTable[freeItem] = item;
+ room->itemsXPos[freeItem] = xpos;
+ room->itemsYPos[freeItem] = ypos;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_wipeDownMouseItem(ScriptState *script) {
+ debug(3, "cmd_wipeDownMouseItem(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ _screen->hideMouse();
+ wipeDownMouseItem(stackPos(1), stackPos(2));
+ destroyMouseItem();
+ _screen->showMouse();
+ return 0;
+}
+
+int KyraEngine::cmd_placeCharacterInOtherScene(ScriptState *script) {
+ debug(3, "cmd_placeCharacterInOtherScene(0x%X) (%d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ int id = stackPos(0);
+ int sceneId = stackPos(1);
+ int xpos = (int16)(stackPos(2) & 0xFFFC);
+ int ypos = (int16)(stackPos(3) & 0xFFFE);
+ int facing = stackPos(4);
+ int animFrame = stackPos(5);
+
+ _characterList[id].sceneId = sceneId;
+ _characterList[id].x1 = _characterList[id].x2 = xpos;
+ _characterList[id].y1 = _characterList[id].y2 = ypos;
+ _characterList[id].facing = facing;
+ _characterList[id].currentAnimFrame = animFrame;
+ return 0;
+}
+
+int KyraEngine::cmd_getKey(ScriptState *script) {
+ debug(3, "cmd_getKey(0x%X) ()", script);
+ waitForEvent();
+ return 0;
+}
+
+int KyraEngine::cmd_specificItemInInventory(ScriptState *script) {
+ warning("STUB: cmd_specificItemInInventory");
+ return 0;
+}
+
+int KyraEngine::cmd_popMobileNPCIntoScene(ScriptState *script) {
+ debug(3, "cmd_popMobileNPCIntoScene(0x%X) (%d, %d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), (int16)(stackPos(4) & 0xFFFC), (int8)(stackPos(5) & 0xFE));
+ int character = stackPos(0);
+ int sceneId = stackPos(1);
+ int animFrame = stackPos(2);
+ int facing = stackPos(3);
+ int16 xpos = (int16)(stackPos(4) & 0xFFFC);
+ int8 ypos = (int16)(stackPos(5) & 0xFFFE);
+ Character *curChar = &_characterList[character];
+
+ curChar->sceneId = sceneId;
+ curChar->currentAnimFrame = animFrame;
+ curChar->facing = facing;
+ curChar->x1 = curChar->x2 = xpos;
+ curChar->y1 = curChar->y2 = ypos;
+
+ _animator->animAddNPC(character);
+ _animator->updateAllObjectShapes();
+ return 0;
+}
+
+int KyraEngine::cmd_mobileCharacterInScene(ScriptState *script) {
+ warning("STUB: cmd_mobileCharacterInScene");
+ return 0;
+}
+
+int KyraEngine::cmd_hideMobileCharacter(ScriptState *script) {
+ warning("STUB: cmd_hideMobileCharacter");
+ return 0;
+}
+
+int KyraEngine::cmd_unhideMobileCharacter(ScriptState *script) {
+ warning("STUB: cmd_unhideMobileCharacter");
+ return 0;
+}
+
+int KyraEngine::cmd_setCharactersLocation(ScriptState *script) {
+ debug(3, "cmd_setCharactersLocation(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ Character *ch = &_characterList[stackPos(0)];
+ AnimObject *animObj = &_animator->actors()[stackPos(0)];
+ int newScene = stackPos(1);
+ if (_currentCharacter->sceneId == ch->sceneId) {
+ if (_currentCharacter->sceneId != newScene)
+ animObj->active = 0;
+ } else if (_currentCharacter->sceneId == newScene) {
+ if (_currentCharacter->sceneId != ch->sceneId)
+ animObj->active = 1;
+ }
+
+ ch->sceneId = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_walkCharacterToPoint(ScriptState *script) {
+ debug(3, "cmd_walkCharacterToPoint(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ int character = stackPos(0);
+ int toX = stackPos(1);
+ int toY = stackPos(2);
+ _pathfinderFlag2 = 1;
+ uint32 nextFrame;
+ int findWayReturn = findWay(_characterList[character].x1, _characterList[character].y1, toX, toY, _movFacingTable, 150);
+ _pathfinderFlag2 = 0;
+ if (_lastFindWayRet < findWayReturn) {
+ _lastFindWayRet = findWayReturn;
+ }
+ if (findWayReturn == 0x7D00 || findWayReturn == 0) {
+ return 0;
+ }
+ int *curPos = _movFacingTable;
+ bool running = true;
+ while (running) {
+ bool forceContinue = false;
+ switch (*curPos) {
+ case 0:
+ _characterList[character].facing = 2;
+ break;
+
+ case 1:
+ _characterList[character].facing = 1;
+ break;
+
+ case 2:
+ _characterList[character].facing = 0;
+ break;
+
+ case 3:
+ _characterList[character].facing = 7;
+ break;
+
+ case 4:
+ _characterList[character].facing = 6;
+ break;
+
+ case 5:
+ _characterList[character].facing = 5;
+ break;
+
+ case 6:
+ _characterList[character].facing = 4;
+ break;
+
+ case 7:
+ _characterList[character].facing = 3;
+ break;
+
+ case 8:
+ running = 0;
+ break;
+
+ default:
+ ++curPos;
+ forceContinue = true;
+ break;
+ }
+
+ if (forceContinue || !running) {
+ continue;
+ }
+
+ setCharacterPosition(character, 0);
+ ++curPos;
+
+ nextFrame = getTimerDelay(5 + character) * _tickLength + _system->getMillis();
+ while (_system->getMillis() < nextFrame) {
+ _sprites->updateSceneAnims();
+ updateMousePointer();
+ updateGameTimers();
+ _animator->updateAllObjectShapes();
+ updateTextFade();
+ if ((nextFrame - _system->getMillis()) >= 10)
+ delay(10);
+ }
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_specialEventDisplayBrynnsNote(ScriptState *script) {
+ debug(3, "cmd_specialEventDisplayBrynnsNote(0x%X) ()", script);
+ _screen->hideMouse();
+ _screen->savePageToDisk("HIDPAGE.TMP", 2);
+ _screen->savePageToDisk("SEENPAGE.TMP", 0);
+ if (_features & GF_TALKIE) {
+ if (_features & GF_ENGLISH) {
+ loadBitmap("NOTEENG.CPS", 3, 3, 0);
+ } else if (_features & GF_FRENCH) {
+ loadBitmap("NOTEFRE.CPS", 3, 3, 0);
+ } else if (_features & GF_GERMAN) {
+ loadBitmap("NOTEGER.CPS", 3, 3, 0);
+ }
+ } else {
+ loadBitmap("NOTE.CPS", 3, 3, 0);
+ }
+ _screen->copyRegion(63, 8, 63, 8, 194, 128, 2, 0);
+ _screen->updateScreen();
+ _screen->showMouse();
+ _screen->setFont(Screen::FID_6_FNT);
+ return 0;
+}
+
+int KyraEngine::cmd_specialEventRemoveBrynnsNote(ScriptState *script) {
+ debug(3, "cmd_specialEventRemoveBrynnsNote(0x%X) ()", script);
+ _screen->hideMouse();
+ _screen->loadPageFromDisk("SEENPAGE.TMP", 0);
+ _screen->loadPageFromDisk("HIDPAGE.TMP", 2);
+ _screen->updateScreen();
+ _screen->showMouse();
+ _screen->setFont(Screen::FID_8_FNT);
+ return 0;
+}
+
+int KyraEngine::cmd_setLogicPage(ScriptState *script) {
+ debug(3, "cmd_setLogicPage(0x%X) (%d)", script, stackPos(0));
+ _screen->_curPage = stackPos(0);
+ return stackPos(0);
+}
+
+int KyraEngine::cmd_fatPrint(ScriptState *script) {
+ debug(3, "cmd_fatPrint(0x%X) ('%s', %d, %d, %d, %d, %d)", script, stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ _text->printText(stackPosString(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ return 0;
+}
+
+int KyraEngine::cmd_preserveAllObjectBackgrounds(ScriptState *script) {
+ debug(3, "cmd_preserveAllObjectBackgrounds(0x%X) ()", script);
+ _animator->preserveAllBackgrounds();
+ return 0;
+}
+
+int KyraEngine::cmd_updateSceneAnimations(ScriptState *script) {
+ debug(3, "cmd_updateSceneAnimations(0x%X) (%d)", script, stackPos(0));
+ if (stackPos(0)) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_sceneAnimationActive(ScriptState *script) {
+ debug(3, "cmd_sceneAnimationActive(0x%X) (%d)", script, stackPos(0));
+ return _sprites->_anims[stackPos(0)].play;
+}
+
+int KyraEngine::cmd_setCharactersMovementDelay(ScriptState *script) {
+ debug(3, "cmd_setCharactersMovementDelay(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ setTimerDelay(stackPos(0)+5, stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_getCharactersFacing(ScriptState *script) {
+ debug(3, "cmd_getCharactersFacing(0x%X) (%d)", script, stackPos(0));
+ return _characterList[stackPos(0)].facing;
+}
+
+int KyraEngine::cmd_bkgdScrollSceneAndMasksRight(ScriptState *script) {
+ debug(3, "cmd_bkgdScrollSceneAndMasksRight(0x%X) (%d)", script, stackPos(0));
+ _screen->copyBackgroundBlock(stackPos(0), 2, 0);
+ _screen->copyBackgroundBlock2(stackPos(0));
+ // update the whole screen
+ _screen->copyRegion(7, 7, 7, 7, 305, 129, 3, 0);
+ _screen->updateScreen();
+ return 0;
+}
+
+int KyraEngine::cmd_dispelMagicAnimation(ScriptState *script) {
+ debug(3, "cmd_dispelMagicAnimation(0x%X) ()", script);
+ seq_dispelMagicAnimation();
+ return 0;
+}
+
+int KyraEngine::cmd_findBrightestFireberry(ScriptState *script) {
+ debug(3, "cmd_findBrightestFireberry(0x%X) ()", script);
+ if (_currentCharacter->sceneId >= 187 && _currentCharacter->sceneId <= 198) {
+ return 29;
+ }
+ if (_currentCharacter->sceneId == 133 || _currentCharacter->sceneId == 137 ||
+ _currentCharacter->sceneId == 165 || _currentCharacter->sceneId == 173) {
+ return 29;
+ }
+ if (_itemInHand == 28)
+ return 28;
+ int brightestFireberry = 107;
+ if (_itemInHand >= 29 && _itemInHand <= 33)
+ brightestFireberry = _itemInHand;
+ for (int i = 0; i < 10; ++i) {
+ uint8 item = _currentCharacter->inventoryItems[i];
+ if (item == 0xFF)
+ continue;
+ if (item == 28)
+ return 28;
+ if (item >= 29 && item <= 33) {
+ if (item < brightestFireberry)
+ brightestFireberry = item;
+ }
+ }
+ assert(_currentCharacter->sceneId < _roomTableSize);
+ Room *curRoom = &_roomTable[_currentCharacter->sceneId];
+ for (int i = 0; i < 12; ++i) {
+ uint8 item = curRoom->itemsTable[i];
+ if (item == 0xFF)
+ continue;
+ if (item == 28)
+ return 28;
+ if (item >= 29 && item <= 33) {
+ if (item < brightestFireberry)
+ brightestFireberry = item;
+ }
+ }
+ if (brightestFireberry == 107)
+ return -1;
+ return brightestFireberry;
+}
+
+int KyraEngine::cmd_setFireberryGlowPalette(ScriptState *script) {
+ debug(3, "cmd_setFireberryGlowPalette(0x%X) (%d)", script, stackPos(0));
+ int palIndex = 0;
+ switch (stackPos(0)) {
+ case 0x1E:
+ palIndex = 9;
+ break;
+
+ case 0x1F:
+ palIndex = 10;
+ break;
+
+ case 0x20:
+ palIndex = 11;
+ break;
+
+ case 0x21:
+ case -1:
+ palIndex = 12;
+ break;
+
+ default:
+ palIndex = 8;
+ break;
+ }
+ if (_brandonStatusBit & 2) {
+ if (_currentCharacter->sceneId != 133 && _currentCharacter->sceneId != 137 &&
+ _currentCharacter->sceneId != 165 && _currentCharacter->sceneId != 173 &&
+ (_currentCharacter->sceneId < 187 || _currentCharacter->sceneId > 198)) {
+ palIndex = 14;
+ }
+ }
+ uint8 *palette = _specialPalettes[palIndex];
+ memcpy(&_screen->_currentPalette[684], palette, 44);
+ _screen->setScreenPalette(_screen->_currentPalette);
+ return 0;
+}
+
+int KyraEngine::cmd_setDeathHandlerFlag(ScriptState *script) {
+ debug(3, "cmd_setDeathHandlerFlag(0x%X) (%d)", script, stackPos(0));
+ _deathHandler = stackPos(0);
+ return 0;
+}
+
+int KyraEngine::cmd_drinkPotionAnimation(ScriptState *script) {
+ debug(3, "cmd_drinkPotionAnimation(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ seq_playDrinkPotionAnim(stackPos(0), stackPos(1), stackPos(2));
+ return 0;
+}
+
+int KyraEngine::cmd_makeAmuletAppear(ScriptState *script) {
+ debug(3, "cmd_makeAmuletAppear(0x%X) ()", script);
+ WSAMovieV1 amulet(this);
+ amulet.open("AMULET.WSA", 1, 0);
+ amulet._drawPage = 0;
+ amulet._x = 224;
+ amulet._y = 152;
+ if (amulet.opened()) {
+ assert(_amuleteAnim);
+ _screen->hideMouse();
+ snd_playSoundEffect(0x70);
+ uint32 nextTime = 0;
+ for (int i = 0; _amuleteAnim[i] != 0xFF; ++i) {
+ nextTime = _system->getMillis() + 5 * _tickLength;
+
+ uint8 code = _amuleteAnim[i];
+ if (code == 3 || code == 7) {
+ snd_playSoundEffect(0x71);
+ }
+
+ if (code == 5) {
+ snd_playSoundEffect(0x72);
+ }
+
+ if (code == 14) {
+ snd_playSoundEffect(0x73);
+ }
+
+
+ amulet.displayFrame(code);
+ _animator->_updateScreen = true;
+
+ while (_system->getMillis() < nextTime) {
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ if (nextTime - _system->getMillis() >= 10)
+ delay(10);
+ }
+ }
+ _screen->showMouse();
+ }
+ setGameFlag(0x2D);
+ return 0;
+}
+
+int KyraEngine::cmd_drawItemShapeIntoScene(ScriptState *script) {
+ debug(3, "cmd_drawItemShapeIntoScene(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ int item = stackPos(0);
+ int x = stackPos(1);
+ int y = stackPos(2);
+ int flags = stackPos(3);
+ int onlyHidPage = stackPos(4);
+ if (flags)
+ flags = 1;
+ if (onlyHidPage) {
+ _screen->drawShape(2, _shapes[220+item], x, y, 0, flags);
+ } else {
+ _screen->hideMouse();
+ _animator->restoreAllObjectBackgrounds();
+ _screen->drawShape(2, _shapes[220+item], x, y, 0, flags);
+ _screen->drawShape(0, _shapes[220+item], x, y, 0, flags);
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->flagAllObjectsForRefresh();
+ _animator->updateAllObjectShapes();
+ _screen->showMouse();
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_setCharactersCurrentFrame(ScriptState *script) {
+ debug(3, "cmd_setCharactersCurrentFrame(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _characterList[stackPos(0)].currentAnimFrame = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_waitForConfirmationMouseClick(ScriptState *script) {
+ debug(3, "cmd_waitForConfirmationMouseClick(0x%X) ()", script);
+ // if (mouseEnabled) {
+ while (!_mousePressFlag) {
+ updateMousePointer();
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ delay(10);
+ }
+
+ while (_mousePressFlag) {
+ updateMousePointer();
+ _sprites->updateSceneAnims();
+ _animator->updateAllObjectShapes();
+ delay(10);
+ }
+ // }
+ // XXX processButtonList calls
+ script->variables[1] = _mouseX;
+ script->variables[2] = _mouseY;
+ return 0;
+}
+
+int KyraEngine::cmd_pageFlip(ScriptState *script) {
+ warning("STUB: cmd_pageFlip");
+ return 0;
+}
+
+int KyraEngine::cmd_setSceneFile(ScriptState *script) {
+ debug(3, "cmd_setSceneFile(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ setSceneFile(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_getItemInMarbleVase(ScriptState *script) {
+ debug(3, "cmd_getItemInMarbleVase(0x%X) ()", script);
+ return _marbleVaseItem;
+}
+
+int KyraEngine::cmd_setItemInMarbleVase(ScriptState *script) {
+ debug(3, "cmd_setItemInMarbleVase(0x%X) (%d)", script, stackPos(0));
+ _marbleVaseItem = stackPos(0);
+ return 0;
+}
+
+int KyraEngine::cmd_addItemToInventory(ScriptState *script) {
+ warning("STUB: cmd_addItemToInventory");
+ return 0;
+}
+
+int KyraEngine::cmd_intPrint(ScriptState *script) {
+ warning("STUB: cmd_intPrint");
+ return 0;
+}
+
+int KyraEngine::cmd_shakeScreen(ScriptState *script) {
+ warning("STUB: cmd_shakeScreen");
+ return 0;
+}
+
+int KyraEngine::cmd_createAmuletJewel(ScriptState *script) {
+ debug(3, "cmd_createAmuletJewel(0x%X) (%d)", script, stackPos(0));
+ seq_createAmuletJewel(stackPos(0), 0, 0, 0);
+ return 0;
+}
+
+int KyraEngine::cmd_setSceneAnimCurrXY(ScriptState *script) {
+ debug(3, "cmd_setSceneAnimCurrXY(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ _sprites->_anims[stackPos(0)].x = stackPos(1);
+ _sprites->_anims[stackPos(0)].y = stackPos(2);
+ return 0;
+}
+
+int KyraEngine::cmd_poisonBrandonAndRemaps(ScriptState *script) {
+ debug(3, "cmd_poisonBrandonAndRemaps(0x%X) ()", script);
+ setBrandonPoisonFlags(1);
+ return 0;
+}
+
+int KyraEngine::cmd_fillFlaskWithWater(ScriptState *script) {
+ debug(3, "cmd_fillFlaskWithWater(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ seq_fillFlaskWithWater(stackPos(0), stackPos(1));
+ return 0;
+}
+
+int KyraEngine::cmd_getCharactersMovementDelay(ScriptState *script) {
+ debug(3, "cmd_getCharactersMovementDelay(0x%X) (%d)", script, stackPos(0));
+ return getTimerDelay(stackPos(0)+5);
+}
+
+int KyraEngine::cmd_getBirthstoneGem(ScriptState *script) {
+ debug(3, "cmd_getBirthstoneGem(0x%X) (%d)", script, stackPos(0));
+ if (stackPos(0) < 4) {
+ return _birthstoneGemTable[stackPos(0)];
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_queryBrandonStatusBit(ScriptState *script) {
+ debug(3, "cmd_queryBrandonStatusBit(0x%X) (%d)", script, stackPos(0));
+ if (_brandonStatusBit & stackPos(0)) {
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_playFluteAnimation(ScriptState *script) {
+ debug(3, "cmd_playFluteAnimation(0x%X) ()", script);
+ seq_playFluteAnimation();
+ return 0;
+}
+
+int KyraEngine::cmd_playWinterScrollSequence(ScriptState *script) {
+ debug(3, "cmd_playWinterScrollSequence(0x%X) (%d)", script, stackPos(0));
+ if (!stackPos(0)) {
+ seq_winterScroll2();
+ } else {
+ seq_winterScroll1();
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_getIdolGem(ScriptState *script) {
+ debug(3, "cmd_getIdolGem(0x%X) (%d)", script, stackPos(0));
+ return _idolGemsTable[stackPos(0)];;
+}
+
+int KyraEngine::cmd_setIdolGem(ScriptState *script) {
+ debug(3, "cmd_setIdolGem(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _idolGemsTable[stackPos(0)] = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_totalItemsInScene(ScriptState *script) {
+ debug(3, "cmd_totalItemsInScene(0x%X) (%d)", script, stackPos(0));
+ return countItemsInScene(stackPos(0));
+}
+
+int KyraEngine::cmd_restoreBrandonsMovementDelay(ScriptState *script) {
+ debug(3, "cmd_restoreBrandonsMovementDelay(0x%X) ()", script);
+ //TODO: Use movement set by menu, instead of 5.
+ setTimerDelay(5, 5);
+ return 0;
+}
+
+int KyraEngine::cmd_setMousePos(ScriptState *script) {
+ debug(3, "cmd_setMousePos(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _system->warpMouse(stackPos(0), stackPos(1));
+ _mouseX = stackPos(0);
+ _mouseY = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_getMouseState(ScriptState *script) {
+ debug(3, "cmd_getMouseState(0x%X) ()", script);
+ return _mouseState;
+}
+
+int KyraEngine::cmd_setEntranceMouseCursorTrack(ScriptState *script) {
+ debug(3, "cmd_setEntranceMouseCursorTrack(0x%X) (%d, %d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4));
+ _entranceMouseCursorTracks[0] = stackPos(0);
+ _entranceMouseCursorTracks[1] = stackPos(1);
+ _entranceMouseCursorTracks[2] = stackPos(0) + stackPos(2) - 1;
+ _entranceMouseCursorTracks[3] = stackPos(1) + stackPos(3) - 1;
+ _entranceMouseCursorTracks[4] = stackPos(4);
+ return 0;
+}
+
+int KyraEngine::cmd_itemAppearsOnGround(ScriptState *script) {
+ debug(3, "cmd_itemAppearsOnGround(0x%X) (%d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2));
+ processItemDrop(_currentCharacter->sceneId, stackPos(0), stackPos(1), stackPos(2), 2, 0);
+ return 0;
+}
+
+int KyraEngine::cmd_setNoDrawShapesFlag(ScriptState *script) {
+ debug(3, "cmd_setNoDrawShapesFlag(0x%X) (%d)", script, stackPos(0));
+ _animator->_noDrawShapesFlag = stackPos(0);
+ return 0;
+}
+
+int KyraEngine::cmd_fadeEntirePalette(ScriptState *script) {
+ warning("STUB: cmd_fadeEntirePalette");
+ return 0;
+}
+
+int KyraEngine::cmd_itemOnGroundHere(ScriptState *script) {
+ debug(3, "cmd_itemOnGroundHere(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ assert(stackPos(0) < _roomTableSize);
+ Room *curRoom = &_roomTable[stackPos(0)];
+ for (int i = 0; i < 12; ++i) {
+ if (curRoom->itemsTable[i] == stackPos(1))
+ return 1;
+ }
+ return 0;
+}
+
+int KyraEngine::cmd_queryCauldronState(ScriptState *script) {
+ debug(3, "cmd_queryCauldronState(0x%X) ()", script);
+ return _cauldronState;
+}
+
+int KyraEngine::cmd_setCauldronState(ScriptState *script) {
+ debug(3, "cmd_setCauldronState(0x%X) (%d)", script, stackPos(0));
+ _cauldronState = stackPos(0);
+ return _cauldronState;
+}
+
+int KyraEngine::cmd_queryCrystalState(ScriptState *script) {
+ debug(3, "cmd_queryCrystalState(0x%X) (%d)", script, stackPos(0));
+ if (!stackPos(0)) {
+ return _crystalState[0];
+ } else if (stackPos(0) == 1) {
+ return _crystalState[1];
+ }
+ return -1;
+}
+
+int KyraEngine::cmd_setCrystalState(ScriptState *script) {
+ debug(3, "cmd_setCrystalState(0x%X) (%d)", script, stackPos(0), stackPos(1));
+ if (!stackPos(0)) {
+ _crystalState[0] = stackPos(1);
+ } else if (stackPos(0) == 1) {
+ _crystalState[1] = stackPos(1);
+ }
+ return stackPos(1);
+}
+
+int KyraEngine::cmd_setPaletteRange(ScriptState *script) {
+ warning("STUB: cmd_setPaletteRange");
+ return 0;
+}
+
+int KyraEngine::cmd_shrinkBrandonDown(ScriptState *script) {
+ debug(3, "cmd_shrinkBrandonDown(0x%X) (%d)", script, stackPos(0));
+ int delayTime = stackPos(0);
+ checkAmuletAnimFlags();
+ int scaleValue = _scaleTable[_currentCharacter->y1];
+ int scale = 0;
+ if (_scaleMode) {
+ scale = scaleValue;
+ } else {
+ scale = 256;
+ }
+ int scaleModeBackUp = _scaleMode;
+ _scaleMode = 1;
+ int scaleEnd = scale >> 1;
+ for (; scaleEnd <= scale; --scale) {
+ _scaleTable[_currentCharacter->y1] = scale;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(1);
+ }
+ delayWithTicks(delayTime); // XXX
+ _scaleTable[_currentCharacter->y1] = scaleValue;
+ _scaleMode = scaleModeBackUp;
+ return 0;
+}
+
+int KyraEngine::cmd_growBrandonUp(ScriptState *script) {
+ debug(3, "cmd_growBrandonUp(0x%X) ()", script);
+ int scaleValue = _scaleTable[_currentCharacter->y1];
+ int scale = 0;
+ if (_scaleMode) {
+ scale = scaleValue;
+ } else {
+ scale = 256;
+ }
+ int scaleModeBackUp = _scaleMode;
+ _scaleMode = 1;
+ for (int curScale = scale >> 1; curScale <= scale; ++curScale) {
+ _scaleTable[_currentCharacter->y1] = curScale;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(1);
+ }
+ _scaleTable[_currentCharacter->y1] = scaleValue;
+ _scaleMode = scaleModeBackUp;
+ return 0;
+}
+
+int KyraEngine::cmd_setBrandonScaleXAndY(ScriptState *script) {
+ debug(3, "cmd_setBrandonScaleXAndY(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ _animator->_brandonScaleX = stackPos(0);
+ _animator->_brandonScaleY = stackPos(1);
+ return 0;
+}
+
+int KyraEngine::cmd_resetScaleMode(ScriptState *script) {
+ debug(3, "cmd_resetScaleMode(0x%X) ()", script);
+ _scaleMode = 0;
+ return 0;
+}
+
+int KyraEngine::cmd_getScaleDepthTableValue(ScriptState *script) {
+ debug(3, "cmd_getScaleDepthTableValue(0x%X) (%d)", script, stackPos(0));
+ assert(stackPos(0) < ARRAYSIZE(_scaleTable));
+ return _scaleTable[stackPos(0)];
+}
+
+int KyraEngine::cmd_setScaleDepthTableValue(ScriptState *script) {
+ debug(3, "cmd_setScaleDepthTableValue(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ assert(stackPos(0) < ARRAYSIZE(_scaleTable));
+ _scaleTable[stackPos(0)] = stackPos(1);
+ return stackPos(1);
+}
+
+int KyraEngine::cmd_message(ScriptState *script) {
+ if (_features & GF_TALKIE) {
+ debug(3, "cmd_message(0x%X) (%d, '%s', %d)", script, stackPos(0), stackPosString(1), stackPos(2));
+ drawSentenceCommand(stackPosString(1), stackPos(2));
+ } else {
+ debug(3, "cmd_message(0x%X) ('%s', %d)", script, stackPosString(0), stackPos(1));
+ drawSentenceCommand(stackPosString(0), stackPos(1));
+ }
+
+ return 0;
+}
+
+int KyraEngine::cmd_checkClickOnNPC(ScriptState *script) {
+ debug(3, "cmd_checkClickOnNPC(0x%X) (%d, %d)", script, stackPos(0), stackPos(1));
+ return checkForNPCScriptRun(stackPos(0), stackPos(1));
+}
+
+int KyraEngine::cmd_getFoyerItem(ScriptState *script) {
+ debug(3, "cmd_getFoyerItem(0x%X) (%d)", stackPos(0));
+ assert(stackPos(0) < ARRAYSIZE(_foyerItemTable));
+ return _foyerItemTable[stackPos(0)];
+}
+
+int KyraEngine::cmd_setFoyerItem(ScriptState *script) {
+ debug(3, "cmd_setFoyerItem(0x%X) (%d, %d)", stackPos(0), stackPos(1));
+ assert(stackPos(0) < ARRAYSIZE(_foyerItemTable));
+ _foyerItemTable[stackPos(0)] = stackPos(1);
+ return stackPos(1);
+}
+
+int KyraEngine::cmd_setNoItemDropRegion(ScriptState *script) {
+ debug(3, "cmd_setNoItemDropRegion(0x%X) (%d, %d, %d, %d)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ addToNoDropRects(stackPos(0), stackPos(1), stackPos(2), stackPos(3));
+ return 0;
+}
+
+int KyraEngine::cmd_walkMalcolmOn(ScriptState *script) {
+ debug(3, "cmd_walkMalcolmOn(0x%X) ()", script);
+ if (!_malcolmFlag)
+ _malcolmFlag = 1;
+ return 0;
+}
+
+int KyraEngine::cmd_passiveProtection(ScriptState *script) {
+ debug(3, "cmd_passiveProtection(0x%X) ()", script);
+ return 1;
+}
+
+int KyraEngine::cmd_setPlayingLoop(ScriptState *script) {
+ warning("STUB: cmd_setPlayingLoop");
+ return 0;
+}
+
+int KyraEngine::cmd_brandonToStoneSequence(ScriptState *script) {
+ warning("STUB: cmd_brandonToStoneSequence");
+ return 0;
+}
+
+int KyraEngine::cmd_brandonHealingSequence(ScriptState *script) {
+ debug(3, "cmd_brandonHealingSequence(0x%X) ()", script);
+ seq_brandonHealing();
+ return 0;
+}
+
+int KyraEngine::cmd_protectCommandLine(ScriptState *script) {
+ debug(3, "cmd_protectCommandLine(0x%X) (%d)", script, stackPos(0));
+ return stackPos(0);
+}
+
+int KyraEngine::cmd_pauseMusicSeconds(ScriptState *script) {
+ warning("STUB: cmd_pauseMusicSeconds");
+ return 0;
+}
+
+int KyraEngine::cmd_resetMaskRegion(ScriptState *script) {
+ warning("STUB: cmd_resetMaskRegion");
+ return 0;
+}
+
+int KyraEngine::cmd_setPaletteChangeFlag(ScriptState *script) {
+ debug(3, "cmd_setPaletteChangeFlag(0x%X) (%d)", script, stackPos(0));
+ _paletteChanged = stackPos(0);
+ return _paletteChanged;
+}
+
+int KyraEngine::cmd_fillRect(ScriptState *script) {
+ debug(3, "cmd_fillRect(0x%X) (%d, %d, %d, %d, %d, 0x%X)", script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ int videoPageBackup = _screen->_curPage;
+ _screen->_curPage = stackPos(0);
+ _screen->fillRect(stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5));
+ _screen->_curPage = videoPageBackup;
+ return 0;
+}
+
+int KyraEngine::cmd_vocUnload(ScriptState *script) {
+ debug(3, "cmd_vocUnload(0x%X) ()", script);
+ // this should unload all voc files (not needed)
+ return 0;
+}
+
+int KyraEngine::cmd_vocLoad(ScriptState *script) {
+ debug(3, "cmd_vocLoad(0x%X) (%d)", script, stackPos(0));
+ // this should load the specified voc file (not needed)
+ return 0;
+}
+
+int KyraEngine::cmd_dummy(ScriptState *script) {
+ debug(3, "cmd_dummy(0x%X) ()", script);
+ return 0;
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/seqplayer.cpp b/engines/kyra/seqplayer.cpp
new file mode 100644
index 0000000000..fab2228c8a
--- /dev/null
+++ b/engines/kyra/seqplayer.cpp
@@ -0,0 +1,640 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+
+#include "base/engine.h"
+
+#include "kyra/kyra.h"
+#include "kyra/resource.h"
+#include "kyra/screen.h"
+#include "kyra/sound.h"
+#include "kyra/wsamovie.h"
+#include "kyra/text.h"
+
+#include "kyra/seqplayer.h"
+
+#define SEQOP(n, x) { n, &SeqPlayer::x, #x }
+
+namespace Kyra {
+
+SeqPlayer::SeqPlayer(KyraEngine* vm, OSystem* system) {
+ _vm = vm;
+ _system = system;
+
+ _screen = vm->screen();
+ _sound = vm->sound();
+ _res = vm->resource();
+
+ _copyViewOffs = false;
+ _specialBuffer = 0;
+
+ for (int i = 0; i < ARRAYSIZE(_handShapes); ++i)
+ _handShapes[i] = 0;
+}
+
+SeqPlayer::~SeqPlayer() {
+ freeHandShapes();
+
+ for (int i = 0; i < ARRAYSIZE(_seqMovies); ++i) {
+ _seqMovies[i].movie->close();
+ delete _seqMovies[i].movie;
+ _seqMovies[i].movie = 0;
+ }
+}
+
+uint8 *SeqPlayer::setPanPages(int pageNum, int shape) {
+ debug(9, "SeqPlayer::setPanPages(%d, %d)", pageNum, shape);
+ uint8 *panPage = 0;
+ const uint8 *data = _screen->getPagePtr(pageNum);
+ uint16 numShapes = READ_LE_UINT16(data);
+ if (shape < numShapes) {
+ uint32 offs = 0;
+ if (_vm->features() & GF_TALKIE) {
+ offs = READ_LE_UINT32(data + 2 + shape * 4);
+ } else {
+ offs = READ_LE_UINT16(data + 2 + shape * 2);
+ }
+ if (offs != 0) {
+ data += offs;
+ uint16 sz = READ_LE_UINT16(data + 6);
+ panPage = (uint8 *)malloc(sz);
+ if (panPage) {
+ memcpy(panPage, data, sz);
+ }
+ }
+ }
+ return panPage;
+}
+
+void SeqPlayer::makeHandShapes() {
+ debug(9, "SeqPlayer::makeHandShapes()");
+ _vm->loadBitmap("WRITING.CPS", 3, 3, 0);
+ for (int i = 0; i < ARRAYSIZE(_handShapes); ++i) {
+ if (_handShapes[i])
+ free(_handShapes[i]);
+ _handShapes[i] = setPanPages(3, i);
+ }
+}
+
+void SeqPlayer::freeHandShapes() {
+ debug(9, "SeqPlayer::freeHandShapes()");
+ for (int i = 0; i < ARRAYSIZE(_handShapes); ++i) {
+ free(_handShapes[i]);
+ _handShapes[i] = 0;
+ }
+}
+
+void SeqPlayer::s1_wsaOpen() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ uint8 offscreenDecode = *_seqData++;
+ _seqWsaCurDecodePage = _seqMovies[wsaObj].page = (offscreenDecode == 0) ? 0 : 3;
+ if (!_seqMovies[wsaObj].movie)
+ _seqMovies[wsaObj].movie = _vm->createWSAMovie();
+ _seqMovies[wsaObj].movie->_drawPage = _seqMovies[wsaObj].page;
+ _seqMovies[wsaObj].movie->open(_vm->seqWSATable()[wsaObj], offscreenDecode, 0);
+ _seqMovies[wsaObj].frame = 0;
+ _seqMovies[wsaObj].numFrames = _seqMovies[wsaObj].movie->frames() - 1;
+}
+
+void SeqPlayer::s1_wsaClose() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ if (_seqMovies[wsaObj].movie) {
+ _seqMovies[wsaObj].movie->close();
+ }
+}
+
+void SeqPlayer::s1_wsaPlayFrame() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ int16 frame = (int8)*_seqData++;
+ _seqMovies[wsaObj].pos.x = READ_LE_UINT16(_seqData); _seqData += 2;
+ _seqMovies[wsaObj].pos.y = *_seqData++;
+ assert(_seqMovies[wsaObj].movie);
+ _seqMovies[wsaObj].movie->_x = _seqMovies[wsaObj].pos.x;
+ _seqMovies[wsaObj].movie->_y = _seqMovies[wsaObj].pos.y;
+ _seqMovies[wsaObj].movie->displayFrame(frame);
+ _seqMovies[wsaObj].frame = frame;
+}
+
+void SeqPlayer::s1_wsaPlayNextFrame() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ int16 frame = ++_seqMovies[wsaObj].frame;
+ if (frame > _seqMovies[wsaObj].numFrames) {
+ frame = 0;
+ _seqMovies[wsaObj].frame = 0;
+ }
+ _seqMovies[wsaObj].movie->displayFrame(frame);
+}
+
+void SeqPlayer::s1_wsaPlayPrevFrame() {
+ uint8 wsaObj = *_seqData++;
+ assert(wsaObj < ARRAYSIZE(_seqMovies));
+ int16 frame = --_seqMovies[wsaObj].frame;
+ if (frame < 0) {
+ frame = _seqMovies[wsaObj].numFrames;
+ _seqMovies[wsaObj].frame = frame;
+ } else {
+ _seqMovies[wsaObj].movie->displayFrame(frame);
+ }
+}
+
+void SeqPlayer::s1_drawShape() {
+ uint8 shapeNum = *_seqData++;
+ int x = READ_LE_UINT16(_seqData); _seqData += 2;
+ int y = *_seqData++;
+ _screen->drawShape(2, _handShapes[shapeNum], x, y, 0, 0, 0);
+}
+
+void SeqPlayer::s1_waitTicks() {
+ uint16 ticks = READ_LE_UINT16(_seqData); _seqData += 2;
+ _vm->delay(ticks * _vm->tickLength());
+}
+
+void SeqPlayer::s1_copyWaitTicks() {
+ s1_copyView();
+ s1_waitTicks();
+}
+
+void SeqPlayer::s1_shuffleScreen() {
+ _screen->shuffleScreen(0, 16, 320, 128, 2, 0, 0, false);
+ _screen->_curPage = 2;
+ if (_specialBuffer)
+ _screen->copyCurPageBlock(0, 16, 40, 128, _specialBuffer);
+ _screen->_curPage = 0;
+}
+
+void SeqPlayer::s1_copyView() {
+ int y = 128;
+ if (!_copyViewOffs) {
+ y -= 8;
+ }
+ if (_specialBuffer && !_copyViewOffs) {
+ _screen->copyToPage0(16, y, 3, _specialBuffer);
+ } else {
+ _screen->copyRegion(0, 16, 0, 16, 320, y, 2, 0);
+ }
+}
+
+void SeqPlayer::s1_loopInit() {
+ uint8 seqLoop = *_seqData++;
+ if (seqLoop < ARRAYSIZE(_seqLoopTable)) {
+ _seqLoopTable[seqLoop].ptr = _seqData;
+ } else {
+ _seqQuitFlag = true;
+ }
+}
+
+void SeqPlayer::s1_loopInc() {
+ uint8 seqLoop = *_seqData++;
+ uint16 seqLoopCount = READ_LE_UINT16(_seqData); _seqData += 2;
+ if (_seqLoopTable[seqLoop].count == 0xFFFF) {
+ _seqLoopTable[seqLoop].count = seqLoopCount - 1;
+ _seqData = _seqLoopTable[seqLoop].ptr;
+ } else if (_seqLoopTable[seqLoop].count == 0) {
+ _seqLoopTable[seqLoop].count = 0xFFFF;
+ _seqLoopTable[seqLoop].ptr = 0;
+ } else {
+ --_seqLoopTable[seqLoop].count;
+ _seqData = _seqLoopTable[seqLoop].ptr;
+ }
+}
+
+void SeqPlayer::s1_skip() {
+ uint8 a = *_seqData++;
+ warning("STUB: s1_skip(%d)\n", a);
+}
+
+void SeqPlayer::s1_loadPalette() {
+ uint8 colNum = *_seqData++;
+ uint32 fileSize;
+ uint8 *srcData;
+ srcData = _res->fileData(_vm->seqCOLTable()[colNum], &fileSize);
+ memcpy(_screen->_currentPalette, srcData, fileSize);
+ delete[] srcData;
+}
+
+void SeqPlayer::s1_loadBitmap() {
+ uint8 cpsNum = *_seqData++;
+ _vm->loadBitmap(_vm->seqCPSTable()[cpsNum], 3, 3, 0);
+}
+
+void SeqPlayer::s1_fadeToBlack() {
+ _screen->fadeToBlack();
+}
+
+void SeqPlayer::s1_printText() {
+ static const uint8 colorMap[] = { 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0 };
+ uint8 txt = *_seqData++;
+ _screen->fillRect(0, 180, 319, 195, 12);
+ _screen->setTextColorMap(colorMap);
+ if (!_seqDisplayTextFlag) {
+ const char *str = _vm->seqTextsTable()[txt];
+ int x = (Screen::SCREEN_W - _screen->getTextWidth(str)) / 2;
+ _screen->printText(str, x, 180, 0xF, 0xC);
+ } else {
+ _seqDisplayedTextTimer = _system->getMillis() + 1000 / ((_vm->features() & GF_FRENCH) ? 120 : 60);
+ _seqDisplayedText = txt;
+ _seqDisplayedChar = 0;
+ const char *str = _vm->seqTextsTable()[_seqDisplayedText];
+ _seqDisplayedTextX = (Screen::SCREEN_W - _screen->getTextWidth(str)) / 2;
+ }
+}
+
+void SeqPlayer::s1_printTalkText() {
+ uint8 txt = *_seqData++;
+ int x = READ_LE_UINT16(_seqData); _seqData += 2;
+ int y = *_seqData++;
+ uint8 fillColor = *_seqData++;
+ int b;
+ if (_seqTalkTextPrinted && !_seqTalkTextRestored) {
+ if (_seqWsaCurDecodePage != 0 && !_specialBuffer) {
+ b = 2;
+ } else {
+ b = 0;
+ }
+ _vm->text()->restoreTalkTextMessageBkgd(2, b);
+ }
+ _seqTalkTextPrinted = true;
+ _seqTalkTextRestored = false;
+ if (_seqWsaCurDecodePage != 0 && !_specialBuffer) {
+ b = 2;
+ } else {
+ b = 0;
+ }
+ _vm->text()->printTalkTextMessage(_vm->seqTextsTable()[txt], x, y, fillColor, b, 2);
+}
+
+void SeqPlayer::s1_restoreTalkText() {
+ if (_seqTalkTextPrinted && !_seqTalkTextRestored) {
+ int b;
+ if (_seqWsaCurDecodePage != 0 && !_specialBuffer) {
+ b = 2;
+ } else {
+ b = 0;
+ }
+ _vm->text()->restoreTalkTextMessageBkgd(2, b);
+ _seqTalkTextRestored = true;
+ }
+}
+
+void SeqPlayer::s1_clearCurrentScreen() {
+ _screen->fillRect(10, 180, 319, 196, 0xC);
+}
+
+void SeqPlayer::s1_break() {
+ // Do nothing
+}
+
+void SeqPlayer::s1_fadeFromBlack() {
+ _screen->fadeFromBlack();
+}
+
+void SeqPlayer::s1_copyRegion() {
+ uint8 srcPage = *_seqData++;
+ uint8 dstPage = *_seqData++;
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, srcPage, dstPage);
+}
+
+void SeqPlayer::s1_copyRegionSpecial() {
+ static const uint8 colorMap[] = { 0, 0, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0 };
+ const char *copyStr = 0;
+ if (_vm->features() & GF_FLOPPY || _vm->features() & GF_DEMO) {
+ copyStr = "Copyright (c) 1992 Westwood Studios";
+ } else if (_vm->features() & GF_TALKIE) {
+ copyStr = "Copyright (c) 1992,1993 Westwood Studios";
+ }
+
+ uint8 so = *_seqData++;
+ switch (so) {
+ case 0:
+ _screen->copyRegion(0, 0, 0, 47, 320, 77, 2, 0);
+ break;
+ case 1:
+ _screen->copyRegion(0, 0, 0, 47, 320, 56, 2, 0);
+ break;
+ case 2:
+ _screen->copyRegion(107, 72, 107, 72, 43, 87, 2, 0);
+ _screen->copyRegion(130, 159, 130, 159, 35, 17, 2, 0);
+ _screen->copyRegion(165, 105, 165, 105, 32, 9, 2, 0);
+ _screen->copyRegion(206, 83, 206, 83, 94, 93, 2, 0);
+ break;
+ case 3:
+ _screen->copyRegion(152, 56, 152, 56, 48, 48, 2, 0);
+ break;
+ case 4: {
+ _screen->_charWidth = -2;
+ const int x = (Screen::SCREEN_W - _screen->getTextWidth(copyStr)) / 2;
+ const int y = 179;
+ _screen->setTextColorMap(colorMap);
+ _screen->printText(copyStr, x + 1, y + 1, 0xB, 0xC);
+ _screen->printText(copyStr, x, y, 0xF, 0xC);
+ } break;
+ case 5:
+ _screen->_curPage = 2;
+ break;
+ default:
+ error("Invalid subopcode %d for s1_copyRegionSpecial", so);
+ break;
+ }
+}
+
+void SeqPlayer::s1_fillRect() {
+ int x1 = READ_LE_UINT16(_seqData); _seqData += 2;
+ int y1 = *_seqData++;
+ int x2 = READ_LE_UINT16(_seqData); _seqData += 2;
+ int y2 = *_seqData++;
+ uint8 color = *_seqData++;
+ uint8 page = *_seqData++;
+ _screen->fillRect(x1, y1, x2, y2, color, page);
+}
+
+void SeqPlayer::s1_playEffect() {
+ uint8 track = *_seqData++;
+ _vm->delay(3 * _vm->tickLength());
+ _sound->playSoundEffect(track);
+}
+
+void SeqPlayer::s1_playTrack() {
+ uint8 msg = *_seqData++;
+/*
+ // we do not have audio cd support for now
+ if (_vm->features() & GF_AUDIOCD) {
+ switch (msg) {
+ case 0:
+ // nothing to do here...
+ break;
+ case 1:
+ _sound->beginFadeOut();
+ break;
+ case 56:
+ _vm->snd_playTheme(KyraEngine::MUSIC_INTRO, 3);
+ break;
+ case 57:
+ _vm->snd_playTheme(KyraEngine::MUSIC_INTRO, 4);
+ break;
+ case 58:
+ _vm->snd_playTheme(KyraEngine::MUSIC_INTRO, 5);
+ break;
+ default:
+ warning("Unknown seq. message: %.02d", msg);
+ break;
+ }
+ } else {*/
+ if (msg == 0) {
+ // nothing to do here...
+ } else if (msg == 1) {
+ _sound->beginFadeOut();
+ } else {
+ _sound->playTrack(msg);
+ }
+// }
+}
+
+void SeqPlayer::s1_allocTempBuffer() {
+ if (_vm->features() & GF_DEMO) {
+ _seqQuitFlag = true;
+ } else {
+ if (!_specialBuffer && !_copyViewOffs) {
+ _specialBuffer = new uint8[40960];
+ assert(_specialBuffer);
+ int page = _screen->_curPage;
+ _screen->_curPage = 0;
+ _screen->copyCurPageBlock(0, 0, 320, 128, _specialBuffer);
+ _screen->_curPage = page;
+ }
+ }
+}
+
+void SeqPlayer::s1_textDisplayEnable() {
+ _seqDisplayTextFlag = true;
+}
+
+void SeqPlayer::s1_textDisplayDisable() {
+ _seqDisplayTextFlag = false;
+}
+
+void SeqPlayer::s1_endOfScript() {
+ _seqQuitFlag = true;
+}
+
+void SeqPlayer::s1_loadIntroVRM() {
+ _res->loadPakFile("INTRO.VRM");
+}
+
+void SeqPlayer::s1_playVocFile() {
+ _vm->snd_voiceWaitForFinish(false);
+ uint8 a = *_seqData++;
+ _vm->snd_playVoiceFile(a);
+}
+
+void SeqPlayer::s1_miscUnk3() {
+ warning("STUB: s1_miscUnk3");
+}
+
+void SeqPlayer::s1_prefetchVocFile() {
+ *_seqData++;
+ // we do not have to prefetch the vocfiles on modern systems
+}
+
+bool SeqPlayer::playSequence(const uint8 *seqData, bool skipSeq) {
+ debug(9, "SeqPlayer::seq_playSequence(0x%X, %d)", seqData, skipSeq);
+ assert(seqData);
+
+ static SeqEntry floppySeqProcs[] = {
+ // 0x00
+ SEQOP(3, s1_wsaOpen),
+ SEQOP(2, s1_wsaClose),
+ SEQOP(6, s1_wsaPlayFrame),
+ SEQOP(2, s1_wsaPlayNextFrame),
+ // 0x04
+ SEQOP(2, s1_wsaPlayPrevFrame),
+ SEQOP(5, s1_drawShape),
+ SEQOP(3, s1_waitTicks),
+ SEQOP(3, s1_copyWaitTicks),
+ // 0x08
+ SEQOP(1, s1_shuffleScreen),
+ SEQOP(1, s1_copyView),
+ SEQOP(2, s1_loopInit),
+ SEQOP(4, s1_loopInc),
+ // 0x0C
+ SEQOP(2, s1_loadPalette),
+ SEQOP(2, s1_loadBitmap),
+ SEQOP(1, s1_fadeToBlack),
+ SEQOP(2, s1_printText),
+ // 0x10
+ SEQOP(6, s1_printTalkText),
+ SEQOP(1, s1_restoreTalkText),
+ SEQOP(1, s1_clearCurrentScreen),
+ SEQOP(1, s1_break),
+ // 0x14
+ SEQOP(1, s1_fadeFromBlack),
+ SEQOP(3, s1_copyRegion),
+ SEQOP(2, s1_copyRegionSpecial),
+ SEQOP(9, s1_fillRect),
+ // 0x18
+ SEQOP(2, s1_playEffect),
+ SEQOP(2, s1_playTrack),
+ SEQOP(1, s1_allocTempBuffer),
+ SEQOP(1, s1_textDisplayEnable),
+ // 0x1C
+ SEQOP(1, s1_textDisplayDisable),
+ SEQOP(1, s1_endOfScript)
+ };
+
+ static SeqEntry cdromSeqProcs[] = {
+ // 0x00
+ SEQOP(3, s1_wsaOpen),
+ SEQOP(2, s1_wsaClose),
+ SEQOP(6, s1_wsaPlayFrame),
+ SEQOP(2, s1_wsaPlayNextFrame),
+ // 0x04
+ SEQOP(2, s1_wsaPlayPrevFrame),
+ SEQOP(5, s1_drawShape),
+ SEQOP(3, s1_waitTicks),
+ SEQOP(3, s1_waitTicks),
+ // 0x08
+ SEQOP(3, s1_copyWaitTicks),
+ SEQOP(1, s1_shuffleScreen),
+ SEQOP(1, s1_copyView),
+ SEQOP(2, s1_loopInit),
+ // 0x0C
+ SEQOP(4, s1_loopInc),
+ SEQOP(4, s1_loopInc),
+ SEQOP(2, s1_skip),
+ SEQOP(2, s1_loadPalette),
+ // 0x10
+ SEQOP(2, s1_loadBitmap),
+ SEQOP(1, s1_fadeToBlack),
+ SEQOP(2, s1_printText),
+ SEQOP(6, s1_printTalkText),
+ // 0x14
+ SEQOP(1, s1_restoreTalkText),
+ SEQOP(1, s1_clearCurrentScreen),
+ SEQOP(1, s1_break),
+ SEQOP(1, s1_fadeFromBlack),
+ // 0x18
+ SEQOP(3, s1_copyRegion),
+ SEQOP(2, s1_copyRegionSpecial),
+ SEQOP(9, s1_fillRect),
+ SEQOP(2, s1_playEffect),
+ // 0x1C
+ SEQOP(2, s1_playTrack),
+ SEQOP(1, s1_allocTempBuffer),
+ SEQOP(1, s1_textDisplayEnable),
+ SEQOP(1, s1_textDisplayDisable),
+ // 0x20
+ SEQOP(1, s1_endOfScript),
+ SEQOP(1, s1_loadIntroVRM),
+ SEQOP(2, s1_playVocFile),
+ SEQOP(1, s1_miscUnk3),
+ // 0x24
+ SEQOP(2, s1_prefetchVocFile)
+ };
+
+ const SeqEntry* commands;
+ int numCommands;
+
+ if (_vm->features() & GF_FLOPPY || _vm->features() & GF_DEMO) {
+ commands = floppySeqProcs;
+ numCommands = ARRAYSIZE(floppySeqProcs);
+ } else if (_vm->features() & GF_TALKIE) {
+ commands = cdromSeqProcs;
+ numCommands = ARRAYSIZE(cdromSeqProcs);
+ } else {
+ error("No commandlist found");
+ }
+
+ bool seqSkippedFlag = false;
+
+ _seqData = seqData;
+
+ _seqDisplayedTextTimer = 0xFFFFFFFF;
+ _seqDisplayTextFlag = false;
+ _seqDisplayedTextX = 0;
+ _seqDisplayedText = 0;
+ _seqDisplayedChar = 0;
+ _seqTalkTextRestored = false;
+ _seqTalkTextPrinted = false;
+
+ _seqQuitFlag = false;
+ _seqWsaCurDecodePage = 0;
+
+ for (int i = 0; i < 20; ++i) {
+ _seqLoopTable[i].ptr = 0;
+ _seqLoopTable[i].count = 0xFFFF;
+ }
+
+ memset(_seqMovies, 0, sizeof(_seqMovies));
+
+ _screen->_curPage = 0;
+ while (!_seqQuitFlag) {
+ if (skipSeq && _vm->seq_skipSequence()) {
+ while (1) {
+ uint8 code = *_seqData;
+ if (commands[code].proc == &SeqPlayer::s1_endOfScript || commands[code].proc == &SeqPlayer::s1_break) {
+ break;
+ }
+ _seqData += commands[code].len;
+ }
+ skipSeq = false;
+ seqSkippedFlag = true;
+ }
+ // used in Kallak writing intro
+ if (_seqDisplayTextFlag && _seqDisplayedTextTimer != 0xFFFFFFFF) {
+ if (_seqDisplayedTextTimer < _system->getMillis()) {
+ char charStr[2];
+ charStr[0] = _vm->seqTextsTable()[_seqDisplayedText][_seqDisplayedChar];
+ charStr[1] = '\0';
+ _screen->printText(charStr, _seqDisplayedTextX, 180, 0xF, 0xC);
+ _seqDisplayedTextX += _screen->getCharWidth(charStr[0]);
+ ++_seqDisplayedChar;
+ if (_vm->seqTextsTable()[_seqDisplayedText][_seqDisplayedChar] == '\0') {
+ _seqDisplayedTextTimer = 0xFFFFFFFF;
+ } else {
+ _seqDisplayedTextTimer = _system->getMillis() + 1000 / ((_vm->features() & GF_FRENCH) ? 120 : 60);
+ }
+ }
+ }
+
+ uint8 seqCode = *_seqData++;
+ if (seqCode < numCommands) {
+ SeqProc currentProc = commands[seqCode].proc;
+ debug(5, "seqCode = %d (%s)", seqCode, commands[seqCode].desc);
+ (this->*currentProc)();
+ } else {
+ error("Invalid sequence opcode %d", seqCode);
+ }
+
+ _screen->updateScreen();
+ }
+ delete [] _specialBuffer;
+ _specialBuffer = 0;
+ return seqSkippedFlag;
+}
+
+
+} // End of namespace Kyra
diff --git a/engines/kyra/seqplayer.h b/engines/kyra/seqplayer.h
new file mode 100644
index 0000000000..ad747238c5
--- /dev/null
+++ b/engines/kyra/seqplayer.h
@@ -0,0 +1,123 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef KYRASEQPLAYER_H
+#define KYRASEQPLAYER_H
+
+namespace Kyra {
+
+class SeqPlayer {
+public:
+ SeqPlayer(KyraEngine* vm, OSystem* system);
+ ~SeqPlayer();
+
+ void setCopyViewOffs(bool offs) {
+ _copyViewOffs = offs;
+ }
+
+ void makeHandShapes();
+ void freeHandShapes();
+
+ bool playSequence(const uint8 *seqData, bool skipSeq);
+
+ uint8 *setPanPages(int pageNum, int shape);
+
+protected:
+ KyraEngine *_vm;
+ OSystem *_system;
+ Screen *_screen;
+ Sound *_sound;
+ Resource *_res;
+
+ uint8 *_handShapes[3];
+ bool _copyViewOffs;
+
+ typedef void (SeqPlayer::*SeqProc)();
+ struct SeqEntry {
+ uint8 len;
+ SeqProc proc;
+ const char* desc;
+ };
+
+ // the sequence procs
+ void s1_wsaOpen();
+ void s1_wsaClose();
+ void s1_wsaPlayFrame();
+ void s1_wsaPlayNextFrame();
+ void s1_wsaPlayPrevFrame();
+ void s1_drawShape();
+ void s1_waitTicks();
+ void s1_copyWaitTicks();
+ void s1_shuffleScreen();
+ void s1_copyView();
+ void s1_loopInit();
+ void s1_loopInc();
+ void s1_skip();
+ void s1_loadPalette();
+ void s1_loadBitmap();
+ void s1_fadeToBlack();
+ void s1_printText();
+ void s1_printTalkText();
+ void s1_restoreTalkText();
+ void s1_clearCurrentScreen();
+ void s1_break();
+ void s1_fadeFromBlack();
+ void s1_copyRegion();
+ void s1_copyRegionSpecial();
+ void s1_fillRect();
+ void s1_playEffect();
+ void s1_playTrack();
+ void s1_allocTempBuffer();
+ void s1_textDisplayEnable();
+ void s1_textDisplayDisable();
+ void s1_endOfScript();
+ void s1_loadIntroVRM();
+ void s1_playVocFile();
+ void s1_miscUnk3();
+ void s1_prefetchVocFile();
+
+ struct SeqMovie {
+ Movie *movie;
+ int32 page;
+ int16 frame;
+ int16 numFrames;
+ Common::Point pos;
+ };
+
+ const uint8 *_seqData;
+ uint8 *_specialBuffer;
+ SeqMovie _seqMovies[12];
+ SeqLoop _seqLoopTable[20];
+ uint16 _seqWsaCurDecodePage;
+ uint32 _seqDisplayedTextTimer;
+ bool _seqDisplayTextFlag;
+ uint8 _seqDisplayedText;
+ uint8 _seqDisplayedChar;
+ uint16 _seqDisplayedTextX;
+ bool _seqTalkTextPrinted;
+ bool _seqTalkTextRestored;
+ bool _seqQuitFlag;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sequences_v1.cpp b/engines/kyra/sequences_v1.cpp
new file mode 100644
index 0000000000..8b60196763
--- /dev/null
+++ b/engines/kyra/sequences_v1.cpp
@@ -0,0 +1,1630 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "kyra/kyra.h"
+#include "kyra/seqplayer.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+#include "kyra/sound.h"
+#include "kyra/sprites.h"
+#include "kyra/wsamovie.h"
+#include "kyra/animator.h"
+#include "kyra/text.h"
+
+#include "common/system.h"
+#include "common/savefile.h"
+
+namespace Kyra {
+
+void KyraEngine::seq_demo() {
+ debug(9, "KyraEngine::seq_demo()");
+
+ snd_playTheme(MUSIC_INTRO, 2);
+
+ loadBitmap("START.CPS", 7, 7, _screen->_currentPalette);
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 6, 0);
+ _system->copyRectToScreen(_screen->getPagePtr(0), 320, 0, 0, 320, 200);
+ _screen->fadeFromBlack();
+ delay(60 * _tickLength);
+ _screen->fadeToBlack();
+
+ _screen->clearPage(0);
+ loadBitmap("TOP.CPS", 7, 7, NULL);
+ loadBitmap("BOTTOM.CPS", 5, 5, _screen->_currentPalette);
+ _screen->copyRegion(0, 91, 0, 8, 320, 103, 6, 0);
+ _screen->copyRegion(0, 0, 0, 111, 320, 64, 6, 0);
+ _system->copyRectToScreen(_screen->getPagePtr(0), 320, 0, 0, 320, 200);
+ _screen->fadeFromBlack();
+
+ _seq->playSequence(_seq_WestwoodLogo, true);
+ delay(60 * _tickLength);
+ _seq->playSequence(_seq_KyrandiaLogo, true);
+
+ _screen->fadeToBlack();
+ _screen->clearPage(2);
+ _screen->clearPage(0);
+
+ _seq->playSequence(_seq_Demo1, true);
+
+ _screen->clearPage(0);
+ _seq->playSequence(_seq_Demo2, true);
+
+ _screen->clearPage(0);
+ _seq->playSequence(_seq_Demo3, true);
+
+ _screen->clearPage(0);
+ _seq->playSequence(_seq_Demo4, true);
+
+ _screen->clearPage(0);
+ loadBitmap("FINAL.CPS", 7, 7, _screen->_currentPalette);
+ _screen->_curPage = 0;
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 6, 0);
+ _system->copyRectToScreen(_screen->getPagePtr(0), 320, 0, 0, 320, 200);
+ _screen->fadeFromBlack();
+ delay(60 * _tickLength);
+ _screen->fadeToBlack();
+ _sound->stopMusic();
+}
+
+void KyraEngine::seq_intro() {
+ debug(9, "KyraEngine::seq_intro()");
+
+ if (_features & GF_TALKIE) {
+ _res->loadPakFile("INTRO.VRM");
+ }
+
+ static const IntroProc introProcTable[] = {
+ &KyraEngine::seq_introLogos,
+ &KyraEngine::seq_introStory,
+ &KyraEngine::seq_introMalcolmTree,
+ &KyraEngine::seq_introKallakWriting,
+ &KyraEngine::seq_introKallakMalcolm
+ };
+
+ Common::InSaveFile *in;
+ if ((in = _saveFileMan->openForLoading(getSavegameFilename(0)))) {
+ delete in;
+ _skipIntroFlag = true;
+ } else
+ _skipIntroFlag = false;
+
+ _seq->setCopyViewOffs(true);
+ _screen->setFont(Screen::FID_8_FNT);
+ snd_playTheme(MUSIC_INTRO, 2);
+ snd_setSoundEffectFile(MUSIC_INTRO);
+ _text->setTalkCoords(144);
+ for (int i = 0; i < ARRAYSIZE(introProcTable) && !seq_skipSequence(); ++i) {
+ (this->*introProcTable[i])();
+ }
+ _text->setTalkCoords(136);
+ delay(30 * _tickLength);
+ _seq->setCopyViewOffs(false);
+ _sound->stopMusic();
+ if (_features & GF_TALKIE) {
+ _res->unloadPakFile("INTRO.VRM");
+ }
+ res_unloadResources(RES_INTRO | RES_OUTRO);
+}
+
+void KyraEngine::seq_introLogos() {
+ debug(9, "KyraEngine::seq_introLogos()");
+ _screen->clearPage(0);
+ loadBitmap("TOP.CPS", 7, 7, NULL);
+ loadBitmap("BOTTOM.CPS", 5, 5, _screen->_currentPalette);
+ _screen->_curPage = 0;
+ _screen->copyRegion(0, 91, 0, 8, 320, 103, 6, 0);
+ _screen->copyRegion(0, 0, 0, 111, 320, 64, 6, 0);
+ _system->copyRectToScreen(_screen->getPagePtr(0), 320, 0, 0, 320, 200);
+ _screen->fadeFromBlack();
+
+ if (_seq->playSequence(_seq_WestwoodLogo, _skipFlag)) {
+ _screen->fadeToBlack();
+ _screen->clearPage(0);
+ return;
+ }
+ delay(60 * _tickLength);
+ if (_seq->playSequence(_seq_KyrandiaLogo, _skipFlag) && !seq_skipSequence()) {
+ _screen->fadeToBlack();
+ _screen->clearPage(0);
+ return;
+ }
+ _screen->fillRect(0, 179, 319, 199, 0);
+
+ int y1 = 8;
+ int h1 = 175;
+ int y2 = 176;
+ int h2 = 0;
+ int32 start, now;
+ int wait;
+ _screen->copyRegion(0, 91, 0, 8, 320, 103, 6, 2);
+ _screen->copyRegion(0, 0, 0, 111, 320, 64, 6, 2);
+ do {
+ start = (int32)_system->getMillis();
+ if (h1 > 0) {
+ _screen->copyRegion(0, y1, 0, 8, 320, h1, 2, 0);
+ }
+ ++y1;
+ --h1;
+ if (h2 > 0) {
+ _screen->copyRegion(0, 64, 0, y2, 320, h2, 4, 0);
+ }
+ --y2;
+ ++h2;
+ _screen->updateScreen();
+ now = (int32)_system->getMillis();
+ wait = _tickLength - (now - start);
+ if (wait > 0) {
+ delay(wait);
+ }
+ } while (y2 >= 64);
+
+ _seq->playSequence(_seq_Forest, true);
+}
+
+void KyraEngine::seq_introStory() {
+ debug(9, "KyraEngine::seq_introStory()");
+ _screen->clearPage(3);
+ _screen->clearPage(0);
+ if (_features & GF_TALKIE) {
+ return;
+ } else if (_features & GF_ENGLISH) {
+ loadBitmap("TEXT.CPS", 3, 3, 0);
+ } else if (_features & GF_GERMAN) {
+ loadBitmap("TEXT_GER.CPS", 3, 3, 0);
+ } else if (_features & GF_FRENCH) {
+ loadBitmap("TEXT_FRE.CPS", 3, 3, 0);
+ } else if (_features & GF_SPANISH) {
+ loadBitmap("TEXT_SPA.CPS", 3, 3, 0);
+ } else {
+ warning("no story graphics file found");
+ }
+ _screen->copyRegion(0, 0, 0, 0, 320, 200, 3, 0);
+ _screen->updateScreen();
+ debug("skipFlag %i, %i", _skipFlag, _tickLength);
+ delay(360 * _tickLength);
+}
+
+void KyraEngine::seq_introMalcolmTree() {
+ debug(9, "KyraEngine::seq_introMalcolmTree()");
+ _screen->_curPage = 0;
+ _screen->clearPage(3);
+ _seq->playSequence(_seq_MalcolmTree, true);
+}
+
+void KyraEngine::seq_introKallakWriting() {
+ debug(9, "KyraEngine::seq_introKallakWriting()");
+ _seq->makeHandShapes();
+ _screen->setAnimBlockPtr(5060);
+ _screen->_charWidth = -2;
+ _screen->clearPage(3);
+ _seq->playSequence(_seq_KallakWriting, true);
+}
+
+void KyraEngine::seq_introKallakMalcolm() {
+ debug(9, "KyraEngine::seq_introKallakMalcolm()");
+ _screen->clearPage(3);
+ _seq->playSequence(_seq_KallakMalcolm, true);
+}
+
+void KyraEngine::seq_createAmuletJewel(int jewel, int page, int noSound, int drawOnly) {
+ debug(9, "seq_createAmuletJewel(%d, %d, %d, %d)", jewel, page, noSound, drawOnly);
+ static const uint16 specialJewelTable[] = {
+ 0x167, 0x162, 0x15D, 0x158, 0x153, 0xFFFF
+ };
+ static const uint16 specialJewelTable1[] = {
+ 0x14F, 0x154, 0x159, 0x15E, 0x163, 0xFFFF
+ };
+ static const uint16 specialJewelTable2[] = {
+ 0x150, 0x155, 0x15A, 0x15F, 0x164, 0xFFFF
+ };
+ static const uint16 specialJewelTable3[] = {
+ 0x151, 0x156, 0x15B, 0x160, 0x165, 0xFFFF
+ };
+ static const uint16 specialJewelTable4[] = {
+ 0x152, 0x157, 0x15C, 0x161, 0x166, 0xFFFF
+ };
+ if (!noSound)
+ snd_playSoundEffect(0x5F);
+ _screen->hideMouse();
+ if (!drawOnly) {
+ for (int i = 0; specialJewelTable[i] != 0xFFFF; ++i) {
+ _screen->drawShape(page, _shapes[4+specialJewelTable[i]], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ delayWithTicks(3);
+ }
+
+ const uint16 *opcodes = 0;
+ switch (jewel - 1) {
+ case 0:
+ opcodes = specialJewelTable1;
+ break;
+
+ case 1:
+ opcodes = specialJewelTable2;
+ break;
+
+ case 2:
+ opcodes = specialJewelTable3;
+ break;
+
+ case 3:
+ opcodes = specialJewelTable4;
+ break;
+ }
+
+ if (opcodes) {
+ for (int i = 0; opcodes[i] != 0xFFFF; ++i) {
+ _screen->drawShape(page, _shapes[4+opcodes[i]], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ delayWithTicks(3);
+ }
+ }
+ }
+ _screen->drawShape(page, _shapes[327+jewel], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ _screen->showMouse();
+ setGameFlag(0x55+jewel);
+}
+
+void KyraEngine::seq_brandonHealing() {
+ debug(9, "seq_brandonHealing()");
+ if (!(_deathHandler & 8))
+ return;
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return;
+ }
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_healingShapeTable);
+ setupShapes123(_healingShapeTable, 22, 0);
+ _animator->setBrandonAnimSeqSize(3, 48);
+ snd_playSoundEffect(0x53);
+ for (int i = 123; i <= 144; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ for (int i = 125; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_brandonHealing2() {
+ debug(9, "seq_brandonHealing2()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_healingShape2Table);
+ setupShapes123(_healingShape2Table, 30, 0);
+ resetBrandonPoisonFlags();
+ _animator->setBrandonAnimSeqSize(3, 48);
+ snd_playSoundEffect(0x50);
+ for (int i = 123; i <= 152; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+ assert(_poisonGone);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2010);
+ }
+ characterSays(_poisonGone[0], 0, -2);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(2011);
+ }
+ characterSays(_poisonGone[1], 0, -2);
+}
+
+void KyraEngine::seq_poisonDeathNow(int now) {
+ debug(9, "seq_poisonDeathNow(%d)", now);
+ if (!(_brandonStatusBit & 1))
+ return;
+ ++_poisonDeathCounter;
+ if (now)
+ _poisonDeathCounter = 2;
+ if (_poisonDeathCounter >= 2) {
+ snd_playWanderScoreViaMap(1, 1);
+ assert(_thePoison);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(7000);
+ }
+ characterSays(_thePoison[0], 0, -2);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(7001);
+ }
+ characterSays(_thePoison[1], 0, -2);
+ seq_poisonDeathNowAnim();
+ _deathHandler = 3;
+ } else {
+ assert(_thePoison);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(7002);
+ }
+ characterSays(_thePoison[2], 0, -2);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(7004);
+ }
+ characterSays(_thePoison[3], 0, -2);
+ }
+}
+
+void KyraEngine::seq_poisonDeathNowAnim() {
+ debug(9, "seq_poisonDeathNowAnim()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_posionDeathShapeTable);
+ setupShapes123(_posionDeathShapeTable, 20, 0);
+ _animator->setBrandonAnimSeqSize(8, 48);
+
+ _currentCharacter->currentAnimFrame = 124;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(30);
+
+ _currentCharacter->currentAnimFrame = 123;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(30);
+
+ for (int i = 125; i <= 139; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ delayWithTicks(60);
+
+ for (int i = 140; i <= 142; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ delayWithTicks(60);
+
+ _animator->resetBrandonAnimSeqSize();
+ freeShapes123();
+ _animator->restoreAllObjectBackgrounds();
+ _currentCharacter->x1 = _currentCharacter->x2 = -1;
+ _currentCharacter->y1 = _currentCharacter->y2 = -1;
+ _animator->preserveAllBackgrounds();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_playFluteAnimation() {
+ debug(9, "seq_playFluteAnimation()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ setupShapes123(_fluteAnimShapeTable, 36, 0);
+ _animator->setBrandonAnimSeqSize(3, 75);
+ for (int i = 123; i <= 130; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(2);
+ }
+
+ int delayTime = 0, soundType = 0;
+ if (queryGameFlag(0x85)) {
+ snd_playSoundEffect(0x63);
+ delayTime = 9;
+ soundType = 3;
+ } else if (!queryGameFlag(0x86)) {
+ snd_playSoundEffect(0x61);
+ delayTime = 2;
+ soundType = 1;
+ setGameFlag(0x86);
+ } else {
+ snd_playSoundEffect(0x62);
+ delayTime = 2;
+ soundType = 2;
+ }
+
+ for (int i = 131; i <= 158; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(delayTime);
+ }
+
+ for (int i = 126; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(delayTime);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+
+ if (soundType == 1) {
+ assert(_fluteString);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(1000);
+ }
+ characterSays(_fluteString[0], 0, -2);
+ } else if (soundType == 2) {
+ assert(_fluteString);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(1001);
+ }
+ characterSays(_fluteString[1], 0, -2);
+ }
+}
+
+void KyraEngine::seq_winterScroll1() {
+ debug(9, "seq_winterScroll1()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_winterScrollTable);
+ assert(_winterScroll1Table);
+ assert(_winterScroll2Table);
+ setupShapes123(_winterScrollTable, 7, 0);
+ _animator->setBrandonAnimSeqSize(5, 66);
+
+ for (int i = 123; i <= 129; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ freeShapes123();
+ snd_playSoundEffect(0x20);
+
+ uint8 numFrames, midpoint;
+ if (_features & GF_TALKIE) {
+ numFrames = 18;
+ midpoint = 136;
+ } else {
+ numFrames = 35;
+ midpoint = 147;
+ }
+ setupShapes123(_winterScroll1Table, numFrames, 0);
+ for (int i = 123; i < midpoint; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ if (_currentCharacter->sceneId == 41 && !queryGameFlag(0xA2)) {
+ snd_playSoundEffect(0x20);
+ _sprites->_anims[0].play = false;
+ _animator->sprites()[0].active = 0;
+ _sprites->_anims[1].play = true;
+ _animator->sprites()[1].active = 1;
+ setGameFlag(0xA2);
+ }
+
+ for (int i = midpoint; i < 123 + numFrames; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ if (_currentCharacter->sceneId == 117 && !queryGameFlag(0xB3)) {
+ for (int i = 0; i <= 7; ++i) {
+ _sprites->_anims[i].play = false;
+ _animator->sprites()[i].active = 0;
+ }
+ uint8 tmpPal[768];
+ memcpy(tmpPal, _screen->_currentPalette, 768);
+ memcpy(&tmpPal[684], palTable2()[0], 60);
+ _screen->fadePalette(tmpPal, 72);
+ memcpy(&_screen->_currentPalette[684], palTable2()[0], 60);
+ _screen->setScreenPalette(_screen->_currentPalette);
+ setGameFlag(0xB3);
+ } else {
+ delayWithTicks(120);
+ }
+
+ freeShapes123();
+ setupShapes123(_winterScroll2Table, 4, 0);
+
+ for (int i = 123; i <= 126; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_winterScroll2() {
+ debug(9, "seq_winterScroll2()");
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_winterScrollTable);
+ setupShapes123(_winterScrollTable, 7, 0);
+ _animator->setBrandonAnimSeqSize(5, 66);
+
+ for (int i = 123; i <= 128; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ delayWithTicks(120);
+
+ for (int i = 127; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_makeBrandonInv() {
+ debug(9, "seq_makeBrandonInv()");
+ if (_deathHandler == 8)
+ return;
+
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return;
+ }
+
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ _brandonStatusBit |= 0x20;
+ setTimerCountdown(18, 2700);
+ _brandonStatusBit |= 0x40;
+ snd_playSoundEffect(0x77);
+ _brandonInvFlag = 0;
+ while (_brandonInvFlag <= 0x100) {
+ _animator->animRefreshNPC(0);
+ delayWithTicks(10);
+ _brandonInvFlag += 0x10;
+ }
+ _brandonStatusBit &= 0xFFBF;
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_makeBrandonNormal() {
+ debug(9, "seq_makeBrandonNormal()");
+ _screen->hideMouse();
+ _brandonStatusBit |= 0x40;
+ snd_playSoundEffect(0x77);
+ _brandonInvFlag = 0x100;
+ while (_brandonInvFlag >= 0) {
+ _animator->animRefreshNPC(0);
+ delayWithTicks(10);
+ _brandonInvFlag -= 0x10;
+ }
+ _brandonInvFlag = 0;
+ _brandonStatusBit &= 0xFF9F;
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_makeBrandonNormal2() {
+ debug(9, "seq_makeBrandonNormal2()");
+ _screen->hideMouse();
+ assert(_brandonToWispTable);
+ setupShapes123(_brandonToWispTable, 26, 0);
+ _animator->setBrandonAnimSeqSize(5, 48);
+ _brandonStatusBit &= 0xFFFD;
+ snd_playSoundEffect(0x6C);
+ for (int i = 138; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _animator->setBrandonAnimSeqSize(4, 48);
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ if (_currentCharacter->sceneId >= 229 && _currentCharacter->sceneId <= 245) {
+ _screen->fadeSpecialPalette(31, 234, 13, 4);
+ } else if (_currentCharacter->sceneId >= 118 && _currentCharacter->sceneId <= 186) {
+ _screen->fadeSpecialPalette(14, 228, 15, 4);
+ }
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_makeBrandonWisp() {
+ debug(9, "seq_makeBrandonWisp()");
+ if (_deathHandler == 8)
+ return;
+
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return;
+ }
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ assert(_brandonToWispTable);
+ setupShapes123(_brandonToWispTable, 26, 0);
+ _animator->setBrandonAnimSeqSize(5, 48);
+ snd_playSoundEffect(0x6C);
+ for (int i = 123; i <= 138; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _brandonStatusBit |= 2;
+ if (_currentCharacter->sceneId >= 109 && _currentCharacter->sceneId <= 198) {
+ setTimerCountdown(14, 18000);
+ } else {
+ setTimerCountdown(14, 7200);
+ }
+ _animator->_brandonDrawFrame = 113;
+ _brandonStatusBit0x02Flag = 1;
+ _currentCharacter->currentAnimFrame = 113;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ if (_currentCharacter->sceneId >= 229 && _currentCharacter->sceneId <= 245) {
+ _screen->fadeSpecialPalette(30, 234, 13, 4);
+ } else if (_currentCharacter->sceneId >= 118 && _currentCharacter->sceneId <= 186) {
+ _screen->fadeSpecialPalette(14, 228, 15, 4);
+ }
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_dispelMagicAnimation() {
+ debug(9, "seq_dispelMagicAnimation()");
+ if (_deathHandler == 8)
+ return;
+ if (_currentCharacter->sceneId == 210) {
+ if (_beadStateVar == 4 || _beadStateVar == 6)
+ return;
+ }
+ _screen->hideMouse();
+ if (_currentCharacter->sceneId == 210 && _currentCharacter->sceneId < 160)
+ _currentCharacter->facing = 3;
+ if (_malcolmFlag == 7 && _beadStateVar == 3) {
+ _beadStateVar = 6;
+ _unkEndSeqVar5 = 2;
+ _malcolmFlag = 10;
+ }
+ checkAmuletAnimFlags();
+ setGameFlag(0xEE);
+ assert(_magicAnimationTable);
+ setupShapes123(_magicAnimationTable, 5, 0);
+ _animator->setBrandonAnimSeqSize(8, 49);
+ snd_playSoundEffect(0x15);
+ for (int i = 123; i <= 127; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+
+ delayWithTicks(120);
+
+ for (int i = 127; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(10);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_fillFlaskWithWater(int item, int type) {
+ debug(9, "seq_fillFlaskWithWater(%d, %d)", item, type);
+ int newItem = -1;
+ static const uint8 flaskTable1[] = { 0x46, 0x48, 0x4A, 0x4C };
+ static const uint8 flaskTable2[] = { 0x47, 0x49, 0x4B, 0x4D };
+
+ if (item >= 60 && item <= 77) {
+ assert(_flaskFull);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ snd_playVoiceFile(8006);
+ }
+ characterSays(_flaskFull[0], 0, -2);
+ } else if (item == 78) {
+ assert(type >= 0 && type < ARRAYSIZE(flaskTable1));
+ newItem = flaskTable1[type];
+ } else if (item == 79) {
+ assert(type >= 0 && type < ARRAYSIZE(flaskTable2));
+ newItem = flaskTable2[type];
+ }
+
+ if (newItem == -1)
+ return;
+
+ _screen->hideMouse();
+ setMouseItem(newItem);
+ _screen->showMouse();
+ _itemInHand = newItem;
+ assert(_fullFlask);
+ assert(type < _fullFlask_Size && type >= 0);
+ if (_features & GF_TALKIE) {
+ snd_voiceWaitForFinish();
+ static const uint16 voiceEntries[] = {
+ 0x1F40, 0x1F41, 0x1F42, 0x1F45
+ };
+ assert(type < ARRAYSIZE(voiceEntries));
+ snd_playVoiceFile(voiceEntries[type]);
+ }
+ characterSays(_fullFlask[type], 0, -2);
+}
+
+void KyraEngine::seq_playDrinkPotionAnim(int unk1, int unk2, int flags) {
+ debug(9, "KyraEngine::seq_playDrinkPotionAnim(%d, %d, %d)", unk1, unk2, flags);
+ // XXX
+ _screen->hideMouse();
+ checkAmuletAnimFlags();
+ _currentCharacter->facing = 5;
+ _animator->animRefreshNPC(0);
+ assert(_drinkAnimationTable);
+ setupShapes123(_drinkAnimationTable, 9, flags);
+ _animator->setBrandonAnimSeqSize(5, 54);
+
+ for (int i = 123; i <= 131; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(5);
+ }
+ snd_playSoundEffect(0x34);
+ for (int i = 0; i < 2; ++i) {
+ _currentCharacter->currentAnimFrame = 130;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(7);
+ _currentCharacter->currentAnimFrame = 131;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(7);
+ }
+
+ if (unk2) {
+ // XXX
+ }
+
+ for (int i = 131; i >= 123; --i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(5);
+ }
+
+ _animator->resetBrandonAnimSeqSize();
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ freeShapes123();
+ _screen->showMouse();
+}
+
+int KyraEngine::seq_playEnd() {
+ debug(9, "KyraEngine::seq_playEnd()");
+ if (_endSequenceSkipFlag) {
+ return 0;
+ }
+ if (_deathHandler == 8) {
+ return 0;
+ }
+ _screen->_curPage = 2;
+ if (_endSequenceNeedLoading) {
+ snd_playWanderScoreViaMap(50, 1);
+ setupPanPages();
+ _finalA = new WSAMovieV1(this);
+ assert(_finalA);
+ _finalA->open("finala.wsa", 1, 0);
+ _finalB = new WSAMovieV1(this);
+ assert(_finalB);
+ _finalB->open("finalb.wsa", 1, 0);
+ _finalC = new WSAMovieV1(this);
+ assert(_finalC);
+ _endSequenceNeedLoading = 0;
+ _finalC->open("finalc.wsa", 1, 0);
+ _screen->_curPage = 0;
+ _beadStateVar = 0;
+ _malcolmFlag = 0;
+ // wired stuff with _unkEndSeqVar2 which needs timer handling
+ _screen->copyRegion(312, 0, 312, 0, 8, 136, 0, 2);
+ }
+ if (handleMalcolmFlag()) {
+ _beadStateVar = 0;
+ _malcolmFlag = 12;
+ handleMalcolmFlag();
+ handleBeadState();
+ closeFinalWsa();
+ if (_deathHandler == 8) {
+ _screen->_curPage = 0;
+ checkAmuletAnimFlags();
+ seq_brandonToStone();
+ delay(60 * _tickLength);
+ return 1;
+ } else {
+ _endSequenceSkipFlag = 1;
+ if (_text->printed()) {
+ _text->restoreTalkTextMessageBkgd(2, 0);
+ }
+ _screen->_curPage = 0;
+ _screen->hideMouse();
+ _screen->fadeSpecialPalette(32, 228, 20, 60);
+ delay(60 * _tickLength);
+ loadBitmap("GEMHEAL.CPS", 3, 3, _screen->_currentPalette);
+ _screen->setScreenPalette(_screen->_currentPalette);
+ _screen->shuffleScreen(8, 8, 304, 128, 2, 0, 1, 0);
+ uint32 nextTime = _system->getMillis() + 120 * _tickLength;
+ _finalA = new WSAMovieV1(this);
+ assert(_finalA);
+ _finalA->open("finald.wsa", 1, 0);
+ _finalA->_x = _finalA->_y = 8;
+ _finalA->_drawPage = 0;
+ while (_system->getMillis() < nextTime) {}
+ snd_playSoundEffect(0x40);
+ for (int i = 0; i < 22; ++i) {
+ while (_system->getMillis() < nextTime) {}
+ if (i == 4) {
+ snd_playSoundEffect(0x3E);
+ } else if (i == 20) {
+ snd_playSoundEffect(0x0E);
+ }
+ nextTime = _system->getMillis() + 8 * _tickLength;
+ _finalA->displayFrame(i);
+ _screen->updateScreen();
+ }
+ delete _finalA;
+ _finalA = 0;
+ seq_playEnding();
+ return 1;
+ }
+ } else {
+ handleBeadState();
+ _screen->bitBlitRects();
+ _screen->updateScreen();
+ _screen->_curPage = 0;
+ }
+ return 0;
+}
+
+void KyraEngine::seq_brandonToStone() {
+ debug(9, "KyraEngine::seq_brandonToStone()");
+ _screen->hideMouse();
+ assert(_brandonStoneTable);
+ setupShapes123(_brandonStoneTable, 14, 0);
+ _animator->setBrandonAnimSeqSize(5, 51);
+ for (int i = 123; i <= 136; ++i) {
+ _currentCharacter->currentAnimFrame = i;
+ _animator->animRefreshNPC(0);
+ delayWithTicks(8);
+ }
+ _animator->resetBrandonAnimSeqSize();
+ freeShapes123();
+ _screen->showMouse();
+}
+
+void KyraEngine::seq_playEnding() {
+ debug(9, "KyraEngine::seq_playEnding()");
+ _screen->hideMouse();
+ res_unloadResources(RES_INGAME);
+ res_loadResources(RES_OUTRO);
+ loadBitmap("REUNION.CPS", 3, 3, _screen->_currentPalette);
+ _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);
+ _screen->_curPage = 0;
+ // XXX
+ assert(_homeString);
+ drawSentenceCommand(_homeString[0], 179);
+ _screen->_curPage = 0;
+ _screen->fadeToBlack();
+ _seq->playSequence(_seq_Reunion, false);
+ _screen->fadeToBlack();
+ _screen->showMouse();
+ seq_playCredits();
+}
+
+void KyraEngine::seq_playCredits() {
+ debug(9, "KyraEngine::seq_playCredits()");
+ static const uint8 colorMap[] = { 0, 0, 0xC, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ _screen->hideMouse();
+ uint32 sz = 0;
+ if (_features & GF_FLOPPY) {
+ _screen->loadFont(Screen::FID_CRED6_FNT, _res->fileData("CREDIT6.FNT", &sz));
+ _screen->loadFont(Screen::FID_CRED8_FNT, _res->fileData("CREDIT8.FNT", &sz));
+ }
+ loadBitmap("CHALET.CPS", 2, 2, _screen->_currentPalette);
+ _screen->setScreenPalette(_screen->_currentPalette);
+ _screen->setCurPage(0);
+ _screen->clearCurPage();
+ _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0);
+ _screen->setTextColorMap(colorMap);
+ _screen->_charWidth = -1;
+ snd_playWanderScoreViaMap(53, 1);
+ // delete
+ _screen->updateScreen();
+ // XXX
+ delay(120 * _tickLength); // wait until user presses escape normally
+ _screen->fadeToBlack();
+ _screen->clearCurPage();
+ _screen->showMouse();
+}
+
+bool KyraEngine::seq_skipSequence() const {
+ debug(9, "KyraEngine::seq_skipSequence()");
+ return _quitFlag || _abortIntroFlag;
+}
+
+int KyraEngine::handleMalcolmFlag() {
+ debug(9, "KyraEngine::handleMalcolmFlag()");
+ static uint16 frame = 0;
+ static uint32 timer1 = 0;
+ static uint32 timer2 = 0;
+
+ switch (_malcolmFlag) {
+ case 1:
+ frame = 0;
+ _malcolmFlag = 2;
+ timer2 = 0;
+ case 2:
+ if (_system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 13) {
+ _malcolmFlag = 3;
+ timer1 = _system->getMillis() + 180 * _tickLength;
+ }
+ }
+ break;
+
+ case 3:
+ if (_system->getMillis() < timer1) {
+ if (_system->getMillis() >= timer2) {
+ frame = _rnd.getRandomNumberRng(14, 17);
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ }
+ } else {
+ _malcolmFlag = 4;
+ frame = 18;
+ }
+ break;
+
+ case 4:
+ if (_system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 25) {
+ frame = 26;
+ _malcolmFlag = 5;
+ _beadStateVar = 1;
+ }
+ }
+ break;
+
+ case 5:
+ if (_system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 31) {
+ frame = 32;
+ _malcolmFlag = 6;
+ }
+ }
+ break;
+
+ case 6:
+ if (_unkEndSeqVar4) {
+ if (frame <= 33 && _system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 33) {
+ _malcolmFlag = 7;
+ frame = 32;
+ _unkEndSeqVar5 = 0;
+ }
+ }
+ }
+ break;
+
+ case 7:
+ if (_unkEndSeqVar5 == 1) {
+ _malcolmFlag = 8;
+ frame = 34;
+ } else if (_unkEndSeqVar5 == 2) {
+ _malcolmFlag = 3;
+ timer1 = _system->getMillis() + 180 * _tickLength;
+ }
+ break;
+
+ case 8:
+ if (_system->getMillis() >= timer2) {
+ _finalA->_x = 8;
+ _finalA->_y = 46;
+ _finalA->_drawPage = 0;
+ _finalA->displayFrame(frame);
+ _screen->updateScreen();
+ timer2 = _system->getMillis() + 8 * _tickLength;
+ ++frame;
+ if (frame > 37) {
+ _malcolmFlag = 0;
+ _deathHandler = 8;
+ return 1;
+ }
+ }
+ break;
+
+ case 9:
+ snd_playSoundEffect(12);
+ snd_playSoundEffect(12);
+ _finalC->_x = 16;
+ _finalC->_y = 50;
+ _finalC->_drawPage = 0;
+ for (int i = 0; i < 18; ++i) {
+ timer2 = _system->getMillis() + 4 * _tickLength;
+ _finalC->displayFrame(i);
+ _screen->updateScreen();
+ while (_system->getMillis() < timer2) {}
+ }
+ snd_playWanderScoreViaMap(51, 1);
+ delay(60*_tickLength);
+ _malcolmFlag = 0;
+ return 1;
+ break;
+
+ case 10:
+ if (!_beadStateVar) {
+ handleBeadState();
+ _screen->bitBlitRects();
+ assert(_veryClever);
+ _text->printTalkTextMessage(_veryClever[0], 60, 31, 5, 0, 2);
+ timer2 = _system->getMillis() + 180 * _tickLength;
+ _malcolmFlag = 11;
+ }
+ break;
+
+ case 11:
+ if (_system->getMillis() >= timer2) {
+ _text->restoreTalkTextMessageBkgd(2, 0);
+ _malcolmFlag = 3;
+ timer1 = _system->getMillis() + 180 * _tickLength;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int KyraEngine::handleBeadState() {
+ debug(9, "KyraEngine::handleBeadState()");
+ static uint32 timer1 = 0;
+ static uint32 timer2 = 0;
+ static BeadState beadState1 = { -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static BeadState beadState2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ static const int table1[] = {
+ -1, -2, -4, -5, -6, -7, -6, -5,
+ -4, -2, -1, 0, 1, 2, 4, 5,
+ 6, 7, 6, 5, 4, 2, 1, 0, 0
+ };
+ static const int table2[] = {
+ 0, 0, 1, 1, 2, 2, 3, 3,
+ 4, 4, 5, 5, 5, 5, 4, 4,
+ 3, 3, 2, 2, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ switch (_beadStateVar) {
+ case 0:
+ if (beadState1.x != -1 && _endSequenceBackUpRect) {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ } else {
+ beadState1.x = -1;
+ beadState1.tableIndex = 0;
+ timer1 = 0;
+ timer2 = 0;
+ _lastDisplayedPanPage = 0;
+ return 1;
+ }
+
+ case 1:
+ if (beadState1.x != -1) {
+ if (_endSequenceBackUpRect) {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ }
+ beadState1.x = -1;
+ beadState1.tableIndex = 0;
+ }
+ _beadStateVar = 2;
+ break;
+
+ case 2:
+ if (_system->getMillis() >= timer1) {
+ int x = 0, y = 0;
+ timer1 = _system->getMillis() + 4 * _tickLength;
+ if (beadState1.x == -1) {
+ assert(_panPagesTable);
+ beadState1.width2 = _animator->fetchAnimWidth(_panPagesTable[19], 256);
+ beadState1.width = ((beadState1.width2 + 7) >> 3) + 1;
+ beadState1.height = _animator->fetchAnimHeight(_panPagesTable[19], 256);
+ if (!_endSequenceBackUpRect) {
+ _endSequenceBackUpRect = new uint8[(beadState1.width * beadState1.height) << 3];
+ assert(_endSequenceBackUpRect);
+ memset(_endSequenceBackUpRect, 0, ((beadState1.width * beadState1.height) << 3) * sizeof(uint8));
+ }
+ x = beadState1.x = 60;
+ y = beadState1.y = 40;
+ initBeadState(x, y, x, 25, 8, &beadState2);
+ } else {
+ if (processBead(beadState1.x, beadState1.y, x, y, &beadState2)) {
+ _beadStateVar = 3;
+ timer2 = _system->getMillis() + 240 * _tickLength;
+ _unkEndSeqVar4 = 0;
+ beadState1.dstX = beadState1.x;
+ beadState1.dstY = beadState1.y;
+ return 0;
+ } else {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = x;
+ beadState1.y = y;
+ }
+ }
+ _screen->copyCurPageBlock(x >> 3, y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], x, y, 0, 0);
+ if (_lastDisplayedPanPage > 17)
+ _lastDisplayedPanPage = 0;
+ _screen->addBitBlitRect(x, y, beadState1.width2, beadState1.height);
+ }
+ break;
+
+ case 3:
+ if (_system->getMillis() >= timer1) {
+ timer1 = _system->getMillis() + 4 * _tickLength;
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = beadState1.dstX + table1[beadState1.tableIndex];
+ beadState1.y = beadState1.dstY + table2[beadState1.tableIndex];
+ _screen->copyCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], beadState1.x, beadState1.y, 0, 0);
+ if (_lastDisplayedPanPage >= 17) {
+ _lastDisplayedPanPage = 0;
+ }
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ ++beadState1.tableIndex;
+ if (beadState1.tableIndex > 24) {
+ beadState1.tableIndex = 0;
+ _unkEndSeqVar4 = 1;
+ }
+ if (_system->getMillis() > timer2 && _malcolmFlag == 7 && !_unkAmuletVar && !_text->printed()) {
+ snd_playSoundEffect(0x0B);
+ if (_currentCharacter->x1 > 233 && _currentCharacter->x1 < 305 && _currentCharacter->y1 > 85 && _currentCharacter->y1 < 105 &&
+ (_brandonStatusBit & 0x20)) {
+ beadState1.unk8 = 290;
+ beadState1.unk9 = 40;
+ _beadStateVar = 5;
+ } else {
+ _beadStateVar = 4;
+ beadState1.unk8 = _currentCharacter->x1 - 4;
+ beadState1.unk9 = _currentCharacter->y1 - 30;
+ }
+
+ if (_text->printed()) {
+ _text->restoreTalkTextMessageBkgd(2, 0);
+ }
+ initBeadState(beadState1.x, beadState1.y, beadState1.unk8, beadState1.unk9, 6, &beadState2);
+ _lastDisplayedPanPage = 18;
+ }
+ }
+ break;
+
+ case 4:
+ if (_system->getMillis() >= timer1) {
+ int x = 0, y = 0;
+ timer1 = _system->getMillis();
+ if (processBead(beadState1.x, beadState1.y, x, y, &beadState2)) {
+ if (_brandonStatusBit & 20) {
+ _unkEndSeqVar5 = 2;
+ _beadStateVar = 6;
+ } else {
+ snd_playWanderScoreViaMap(52, 1);
+ snd_playSoundEffect(0x0C);
+ _unkEndSeqVar5 = 1;
+ _beadStateVar = 0;
+ }
+ } else {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = x;
+ beadState1.y = y;
+ _screen->copyCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], x, y, 0, 0);
+ if (_lastDisplayedPanPage > 17) {
+ _lastDisplayedPanPage = 0;
+ }
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ }
+ }
+ break;
+
+ case 5:
+ if (_system->getMillis() >= timer1) {
+ timer1 = _system->getMillis();
+ int x = 0, y = 0;
+ if (processBead(beadState1.x, beadState1.y, x, y, &beadState2)) {
+ if (beadState1.dstX == 290) {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ uint32 nextRun = 0;
+ _finalB->_x = 224;
+ _finalB->_y = 8;
+ _finalB->_drawPage = 0;
+ for (int i = 0; i < 8; ++i) {
+ nextRun = _system->getMillis() + _tickLength;
+ _finalB->displayFrame(i);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextRun) {}
+ }
+ snd_playSoundEffect(0x0D);
+ for (int i = 7; i >= 0; --i) {
+ nextRun = _system->getMillis() + _tickLength;
+ _finalB->displayFrame(i);
+ _screen->updateScreen();
+ while (_system->getMillis() < nextRun) {}
+ }
+ initBeadState(beadState1.x, beadState1.y, 63, 60, 6, &beadState2);
+ } else {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = -1;
+ beadState1.tableIndex = 0;
+ _beadStateVar = 0;
+ _malcolmFlag = 9;
+ }
+ } else {
+ _screen->copyFromCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ beadState1.x = x;
+ beadState1.y = y;
+ _screen->copyCurPageBlock(beadState1.x >> 3, beadState1.y, beadState1.width, beadState1.height, _endSequenceBackUpRect);
+ _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], x, y, 0, 0);
+ if (_lastDisplayedPanPage > 17) {
+ _lastDisplayedPanPage = 0;
+ }
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ }
+ }
+ break;
+
+ case 6:
+ _screen->drawShape(2, _panPagesTable[19], beadState1.x, beadState1.y, 0, 0);
+ _screen->addBitBlitRect(beadState1.x, beadState1.y, beadState1.width2, beadState1.height);
+ _beadStateVar = 0;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+void KyraEngine::initBeadState(int x, int y, int x2, int y2, int unk, BeadState *ptr) {
+ debug(9, "KyraEngine::initBeadState(%d, %d, %d, %d, %d, 0x%X)", x, y, x2, y2, unk, ptr);
+ ptr->unk9 = unk;
+ int xDiff = x2 - x;
+ int yDiff = y2 - y;
+ int unk1 = 0, unk2 = 0;
+ if (xDiff > 0) {
+ unk1 = 1;
+ } else if (xDiff == 0) {
+ unk1 = 0;
+ } else {
+ unk1 = -1;
+ }
+
+ if (yDiff > 0) {
+ unk2 = 1;
+ } else if (yDiff == 0) {
+ unk2 = 0;
+ } else {
+ unk2 = -1;
+ }
+
+ xDiff = abs(xDiff);
+ yDiff = abs(yDiff);
+
+ ptr->y = 0;
+ ptr->x = 0;
+ ptr->width = xDiff;
+ ptr->height = yDiff;
+ ptr->dstX = x2;
+ ptr->dstY = y2;
+ ptr->width2 = unk1;
+ ptr->unk8 = unk2;
+}
+
+int KyraEngine::processBead(int x, int y, int &x2, int &y2, BeadState *ptr) {
+ debug(9, "KyraEngine::processBead(%d, %d, 0x%X, 0x%X, 0x%X)", x, y, &x2, &y2, ptr);
+ if (x == ptr->dstX && y == ptr->dstY) {
+ return 1;
+ }
+
+ int xPos = x, yPos = y;
+ if (ptr->width >= ptr->height) {
+ for (int i = 0; i < ptr->unk9; ++i) {
+ ptr->y += ptr->height;
+ if (ptr->y >= ptr->width) {
+ ptr->y -= ptr->width;
+ yPos += ptr->unk8;
+ }
+ xPos += ptr->width2;
+ }
+ } else {
+ for (int i = 0; i < ptr->unk9; ++i) {
+ ptr->x += ptr->width;
+ if (ptr->x >= ptr->height) {
+ ptr->x -= ptr->height;
+ xPos += ptr->width2;
+ }
+ yPos += ptr->unk8;
+ }
+ }
+
+ int temp = abs(x - ptr->dstX);
+ if (ptr->unk9 > temp) {
+ xPos = ptr->dstX;
+ }
+ temp = abs(y - ptr->dstY);
+ if (ptr->unk9 > temp) {
+ yPos = ptr->dstY;
+ }
+ x2 = xPos;
+ y2 = yPos;
+ return 0;
+}
+
+void KyraEngine::setupPanPages() {
+ debug(9, "KyraEngine::setupPanPages()");
+ loadBitmap("bead.cps", 3, 3, 0);
+ for (int i = 0; i <= 19; ++i) {
+ _panPagesTable[i] = _seq->setPanPages(3, i);
+ }
+}
+
+void KyraEngine::freePanPages() {
+ debug(9, "KyraEngine::freePanPages()");
+ delete _endSequenceBackUpRect;
+ _endSequenceBackUpRect = 0;
+ for (int i = 0; i <= 19; ++i) {
+ free(_panPagesTable[i]);
+ _panPagesTable[i] = NULL;
+ }
+}
+
+void KyraEngine::closeFinalWsa() {
+ debug(9, "KyraEngine::closeFinalWsa()");
+ delete _finalA;
+ _finalA = 0;
+ delete _finalB;
+ _finalB = 0;
+ delete _finalC;
+ _finalC = 0;
+ freePanPages();
+ _endSequenceNeedLoading = 1;
+}
+
+void KyraEngine::updateKyragemFading() {
+ static const uint8 kyraGemPalette[0x28] = {
+ 0x3F, 0x3B, 0x38, 0x34, 0x32, 0x2F, 0x2C, 0x29, 0x25, 0x22,
+ 0x1F, 0x1C, 0x19, 0x16, 0x12, 0x0F, 0x0C, 0x0A, 0x06, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ if (_system->getMillis() < _kyragemFadingState.timerCount)
+ return;
+
+ _kyragemFadingState.timerCount = _system->getMillis() + 4 * _tickLength;
+ int palPos = 684;
+ for (int i = 0; i < 20; ++i) {
+ _screen->_currentPalette[palPos++] = kyraGemPalette[i + _kyragemFadingState.rOffset];
+ _screen->_currentPalette[palPos++] = kyraGemPalette[i + _kyragemFadingState.gOffset];
+ _screen->_currentPalette[palPos++] = kyraGemPalette[i + _kyragemFadingState.bOffset];
+ }
+ _screen->setScreenPalette(_screen->_currentPalette);
+ _animator->_updateScreen = true;
+ switch (_kyragemFadingState.nextOperation) {
+ case 0:
+ --_kyragemFadingState.bOffset;
+ if (_kyragemFadingState.bOffset >= 1)
+ return;
+ _kyragemFadingState.nextOperation = 1;
+ break;
+
+ case 1:
+ ++_kyragemFadingState.rOffset;
+ if (_kyragemFadingState.rOffset < 19)
+ return;
+ _kyragemFadingState.nextOperation = 2;
+ break;
+
+ case 2:
+ --_kyragemFadingState.gOffset;
+ if (_kyragemFadingState.gOffset >= 1)
+ return;
+ _kyragemFadingState.nextOperation = 3;
+ break;
+
+ case 3:
+ ++_kyragemFadingState.bOffset;
+ if (_kyragemFadingState.bOffset < 19)
+ return;
+ _kyragemFadingState.nextOperation = 4;
+ break;
+
+ case 4:
+ --_kyragemFadingState.rOffset;
+ if (_kyragemFadingState.rOffset >= 1)
+ return;
+ _kyragemFadingState.nextOperation = 5;
+ break;
+
+ case 5:
+ ++_kyragemFadingState.gOffset;
+ if (_kyragemFadingState.gOffset < 19)
+ return;
+ _kyragemFadingState.nextOperation = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ _kyragemFadingState.timerCount = _system->getMillis() + 120 * _tickLength;
+}
+
+void KyraEngine::drawJewelPress(int jewel, int drawSpecial) {
+ debug(9, "KyraEngine::drawJewelPress(%d, %d)", jewel, drawSpecial);
+ _screen->hideMouse();
+ int shape = 0;
+ if (drawSpecial) {
+ shape = 0x14E;
+ } else {
+ shape = jewel + 0x149;
+ }
+ snd_playSoundEffect(0x45);
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ delayWithTicks(2);
+ if (drawSpecial) {
+ shape = 0x148;
+ } else {
+ shape = jewel + 0x143;
+ }
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[jewel], _amuletY2[jewel], 0, 0);
+ _screen->updateScreen();
+ _screen->showMouse();
+}
+
+void KyraEngine::drawJewelsFadeOutStart() {
+ debug(9, "KyraEngine::drawJewelsFadeOutStart()");
+ static const uint16 jewelTable1[] = { 0x164, 0x15F, 0x15A, 0x155, 0x150, 0xFFFF };
+ static const uint16 jewelTable2[] = { 0x163, 0x15E, 0x159, 0x154, 0x14F, 0xFFFF };
+ static const uint16 jewelTable3[] = { 0x166, 0x160, 0x15C, 0x157, 0x152, 0xFFFF };
+ static const uint16 jewelTable4[] = { 0x165, 0x161, 0x15B, 0x156, 0x151, 0xFFFF };
+ for (int i = 0; jewelTable1[i] != 0xFFFF; ++i) {
+ if (queryGameFlag(0x57)) {
+ _screen->drawShape(0, _shapes[4+jewelTable1[i]], _amuletX2[2], _amuletY2[2], 0, 0);
+ }
+ if (queryGameFlag(0x59)) {
+ _screen->drawShape(0, _shapes[4+jewelTable3[i]], _amuletX2[4], _amuletY2[4], 0, 0);
+ }
+ if (queryGameFlag(0x56)) {
+ _screen->drawShape(0, _shapes[4+jewelTable2[i]], _amuletX2[1], _amuletY2[1], 0, 0);
+ }
+ if (queryGameFlag(0x58)) {
+ _screen->drawShape(0, _shapes[4+jewelTable4[i]], _amuletX2[3], _amuletY2[3], 0, 0);
+ }
+ _screen->updateScreen();
+ delayWithTicks(3);
+ }
+}
+
+void KyraEngine::drawJewelsFadeOutEnd(int jewel) {
+ debug(9, "KyraEngine::drawJewelsFadeOutEnd(%d)", jewel);
+ static const uint16 jewelTable[] = { 0x153, 0x158, 0x15D, 0x162, 0x148, 0xFFFF };
+ int newDelay = 0;
+ switch (jewel-1) {
+ case 2:
+ if (_currentCharacter->sceneId >= 109 && _currentCharacter->sceneId <= 198) {
+ newDelay = 18900;
+ } else {
+ newDelay = 8100;
+ }
+ break;
+
+ default:
+ newDelay = 3600;
+ break;
+ }
+ setGameFlag(0xF1);
+ setTimerCountdown(19, newDelay);
+ _screen->hideMouse();
+ for (int i = 0; jewelTable[i] != 0xFFFF; ++i) {
+ uint16 shape = jewelTable[i];
+ if (queryGameFlag(0x57)) {
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[2], _amuletY2[2], 0, 0);
+ }
+ if (queryGameFlag(0x59)) {
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[4], _amuletY2[4], 0, 0);
+ }
+ if (queryGameFlag(0x56)) {
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[1], _amuletY2[1], 0, 0);
+ }
+ if (queryGameFlag(0x58)) {
+ _screen->drawShape(0, _shapes[4+shape], _amuletX2[3], _amuletY2[3], 0, 0);
+ }
+ _screen->updateScreen();
+ delayWithTicks(3);
+ }
+ _screen->showMouse();
+}
+
+} // end of namespace Kyra
diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp
new file mode 100644
index 0000000000..48e59a5d15
--- /dev/null
+++ b/engines/kyra/sound.cpp
@@ -0,0 +1,477 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "kyra/resource.h"
+#include "kyra/sound.h"
+
+#include "sound/mixer.h"
+#include "sound/voc.h"
+#include "sound/audiostream.h"
+
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
+#include "sound/flac.h"
+
+namespace Kyra {
+
+SoundPC::SoundPC(MidiDriver *driver, Audio::Mixer *mixer, KyraEngine *engine) : Sound() {
+ _engine = engine;
+ _driver = driver;
+ _passThrough = false;
+ _eventFromMusic = false;
+ _fadeMusicOut = _sfxIsPlaying = false;
+ _fadeStartTime = 0;
+ _isPlaying = _isLooping = _nativeMT32 = false;
+ _soundEffect = _parser = 0;
+ _soundEffectSource = _parserSource = 0;
+
+ memset(_channel, 0, sizeof(MidiChannel*) * 32);
+ memset(_channelVolume, 50, sizeof(uint8) * 16);
+ _channelVolume[10] = 100;
+ for (int i = 0; i < 16; ++i) {
+ _virChannel[i] = i;
+ }
+ _volume = 0;
+
+ int ret = open();
+ if (ret != MERR_ALREADY_OPEN && ret != 0) {
+ error("couldn't open midi driver");
+ }
+
+ _currentVocFile = 0;
+ _mixer = mixer;
+}
+
+SoundPC::~SoundPC() {
+ _driver->setTimerCallback(NULL, NULL);
+ close();
+}
+
+void SoundPC::setVolume(int volume) {
+ if (volume < 0)
+ volume = 0;
+ else if (volume > 255)
+ volume = 255;
+
+ if (_volume == volume)
+ return;
+
+ _volume = volume;
+ for (int i = 0; i < 32; ++i) {
+ if (_channel[i]) {
+ if (i >= 16) {
+ _channel[i]->volume(_channelVolume[i - 16] * _volume / 255);
+ } else {
+ _channel[i]->volume(_channelVolume[i] * _volume / 255);
+ }
+ }
+ }
+}
+
+int SoundPC::open() {
+ // Don't ever call open without first setting the output driver!
+ if (!_driver)
+ return 255;
+
+ int ret = _driver->open();
+ if (ret)
+ return ret;
+
+ _driver->setTimerCallback(this, &onTimer);
+ return 0;
+}
+
+void SoundPC::close() {
+ if (_driver)
+ _driver->close();
+ _driver = 0;
+}
+
+void SoundPC::send(uint32 b) {
+ if (_passThrough) {
+ if ((b & 0xFFF0) == 0x007BB0)
+ return;
+ _driver->send(b);
+ return;
+ }
+
+ uint8 channel = (byte)(b & 0x0F);
+ if (((b & 0xFFF0) == 0x6FB0 || (b & 0xFFF0) == 0x6EB0) && channel != 9) {
+ if (_virChannel[channel] == channel) {
+ _virChannel[channel] = channel + 16;
+ if (!_channel[_virChannel[channel]])
+ _channel[_virChannel[channel]] = _driver->allocateChannel();
+ if (_channel[_virChannel[channel]])
+ _channel[_virChannel[channel]]->volume(_channelVolume[channel] * _volume / 255);
+ }
+ return;
+ }
+
+ if ((b & 0xFFF0) == 0x07B0) {
+ // Adjust volume changes by master volume
+ uint8 volume = (uint8)((b >> 16) & 0x7F);
+ _channelVolume[channel] = volume;
+ volume = volume * _volume / 255;
+ b = (b & 0xFF00FFFF) | (volume << 16);
+ } else if ((b & 0xF0) == 0xC0 && !_nativeMT32) {
+ b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
+ } else if ((b & 0xFFF0) == 0x007BB0) {
+ //Only respond to All Notes Off if this channel
+ //has currently been allocated
+ if (!_channel[channel])
+ return;
+ }
+
+ if (!_channel[_virChannel[channel]]) {
+ _channel[_virChannel[channel]] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+ if (_channel[_virChannel[channel]])
+ _channel[_virChannel[channel]]->volume(_channelVolume[channel] * _volume / 255);
+ }
+ if (_channel[_virChannel[channel]])
+ _channel[_virChannel[channel]]->send(b);
+}
+
+void SoundPC::metaEvent(byte type, byte *data, uint16 length) {
+ switch (type) {
+ case 0x2F: // End of Track
+ if (_eventFromMusic) {
+ if (!_isLooping) {
+ _isPlaying = false;
+ }
+ // remap all channels
+ for (int i = 0; i < 16; ++i) {
+ _virChannel[i] = i;
+ }
+ } else {
+ _sfxIsPlaying = false;
+ }
+ break;
+ default:
+ _driver->metaEvent(type, data, length);
+ break;
+ }
+}
+
+void SoundPC::playMusic(const char *file) {
+ uint32 size;
+ uint8 *data = (_engine->resource())->fileData(file, &size);
+
+ if (!data) {
+ warning("couldn't load '%s'", file);
+ return;
+ }
+
+ playMusic(data, size);
+}
+
+void SoundPC::playMusic(uint8 *data, uint32 size) {
+ stopMusic();
+
+ _parserSource = data;
+ _parser = MidiParser::createParser_XMIDI();
+ assert(_parser);
+
+ if (!_parser->loadMusic(data, size)) {
+ warning("Error reading track!");
+ delete _parser;
+ _parser = 0;
+ return;
+ }
+
+ _parser->setTrack(0);
+ _parser->setMidiDriver(this);
+ _parser->setTimerRate(getBaseTempo());
+ _parser->property(MidiParser::mpAutoLoop, false);
+}
+
+void SoundPC::loadSoundEffectFile(const char *file) {
+ uint32 size;
+ uint8 *data = (_engine->resource())->fileData(file, &size);
+
+ if (!data) {
+ warning("couldn't load '%s'", file);
+ return;
+ }
+
+ loadSoundEffectFile(data, size);
+}
+
+void SoundPC::loadSoundEffectFile(uint8 *data, uint32 size) {
+ stopSoundEffect();
+
+ _soundEffectSource = data;
+ _soundEffect = MidiParser::createParser_XMIDI();
+ assert(_soundEffect);
+
+ if (!_soundEffect->loadMusic(data, size)) {
+ warning("Error reading track!");
+ delete _parser;
+ _parser = 0;
+ return;
+ }
+
+ _soundEffect->setTrack(0);
+ _soundEffect->setMidiDriver(this);
+ _soundEffect->setTimerRate(getBaseTempo());
+ _soundEffect->property(MidiParser::mpAutoLoop, false);
+}
+
+void SoundPC::stopMusic() {
+ _isLooping = false;
+ _isPlaying = false;
+ if (_parser) {
+ _parser->unloadMusic();
+ delete _parser;
+ _parser = 0;
+ delete [] _parserSource;
+ _parserSource = 0;
+
+ _fadeStartTime = 0;
+ _fadeMusicOut = false;
+ setVolume(255);
+ }
+}
+
+void SoundPC::stopSoundEffect() {
+ _sfxIsPlaying = false;
+ if (_soundEffect) {
+ _soundEffect->unloadMusic();
+ delete _soundEffect;
+ _soundEffect = 0;
+ delete [] _soundEffectSource;
+ _soundEffectSource = 0;
+ }
+}
+
+void SoundPC::onTimer(void *refCon) {
+ SoundPC *music = (SoundPC *)refCon;
+
+ // this should be set to the fadeToBlack value
+ static const uint32 musicFadeTime = 2 * 1000;
+
+ if (music->_fadeMusicOut && music->_fadeStartTime + musicFadeTime > music->_engine->_system->getMillis()) {
+ byte volume = (byte)((musicFadeTime - (music->_engine->_system->getMillis() - music->_fadeStartTime)) * 255 / musicFadeTime);
+ music->setVolume(volume);
+ } else if(music->_fadeStartTime) {
+ music->setVolume(255);
+ music->_fadeStartTime = 0;
+ music->_fadeMusicOut = false;
+ music->_isLooping = false;
+ music->_isPlaying = false;
+
+ music->_eventFromMusic = true;
+ // from sound/midiparser.cpp
+ for (int i = 0; i < 128; ++i) {
+ for (int j = 0; j < 16; ++j) {
+ music->send(0x80 | j | i << 8);
+ }
+ }
+ for (int i = 0; i < 16; ++i) {
+ music->send(0x007BB0 | i);
+ }
+ }
+
+ if (music->_isPlaying) {
+ if (music->_parser) {
+ music->_eventFromMusic = true;
+ music->_parser->onTimer();
+ }
+ }
+
+ if (music->_sfxIsPlaying) {
+ if (music->_soundEffect) {
+ music->_eventFromMusic = false;
+ music->_soundEffect->onTimer();
+ }
+ }
+}
+
+void SoundPC::playTrack(uint8 track, bool loop) {
+ if (_parser) {
+ _isPlaying = true;
+ _isLooping = loop;
+ _parser->setTrack(track);
+ _parser->jumpToTick(0);
+ _parser->setTempo(1);
+ _parser->property(MidiParser::mpAutoLoop, loop);
+ }
+}
+
+void SoundPC::playSoundEffect(uint8 track) {
+ if (_soundEffect) {
+ _sfxIsPlaying = true;
+ _soundEffect->setTrack(track);
+ _soundEffect->jumpToTick(0);
+ _soundEffect->property(MidiParser::mpAutoLoop, false);
+ }
+}
+
+void SoundPC::beginFadeOut() {
+ // this should be something like fade out...
+ _fadeMusicOut = true;
+ _fadeStartTime = _engine->_system->getMillis();
+}
+
+void SoundPC::voicePlay(const char *file) {
+ uint32 fileSize = 0;
+ byte *fileData = 0;
+ bool found = false;
+ char filenamebuffer[25];
+
+ for (int i = 0; _supportedCodes[i].fileext; ++i) {
+ strcpy(filenamebuffer, file);
+ strcat(filenamebuffer, _supportedCodes[i].fileext);
+
+ _engine->resource()->fileHandle(filenamebuffer, &fileSize, _compressHandle);
+ if (!_compressHandle.isOpen())
+ continue;
+
+ _currentVocFile = _supportedCodes[i].streamFunc(&_compressHandle, fileSize);
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ strcpy(filenamebuffer, file);
+ strcat(filenamebuffer, ".VOC");
+
+ fileData = _engine->resource()->fileData(filenamebuffer, &fileSize);
+ if (!fileData)
+ return;
+
+ Common::MemoryReadStream vocStream(fileData, fileSize);
+ _mixer->stopHandle(_vocHandle);
+ _currentVocFile = makeVOCStream(vocStream);
+ }
+
+ if (_currentVocFile)
+ _mixer->playInputStream(Audio::Mixer::kSpeechSoundType, &_vocHandle, _currentVocFile);
+ delete [] fileData;
+ fileSize = 0;
+}
+
+bool SoundPC::voiceIsPlaying() {
+ return _mixer->isSoundHandleActive(_vocHandle);
+}
+
+void KyraEngine::snd_playTheme(int file, int track) {
+ debug(9, "KyraEngine::snd_playTheme(%d)", file);
+ assert(file < _xmidiFilesCount);
+ _curMusicTheme = _newMusicTheme = file;
+ _sound->playMusic(_xmidiFiles[file]);
+ _sound->playTrack(track, false);
+}
+
+void KyraEngine::snd_setSoundEffectFile(int file) {
+ debug(9, "KyraEngine::snd_setSoundEffectFile(%d)", file);
+ assert(file < _xmidiFilesCount);
+ _sound->loadSoundEffectFile(_xmidiFiles[file]);
+}
+
+void KyraEngine::snd_playSoundEffect(int track) {
+ debug(9, "KyraEngine::snd_playSoundEffect(%d)", track);
+ if (track == 49) {
+ snd_playWanderScoreViaMap(56, 1);
+ } else {
+ _sound->playSoundEffect(track);
+ }
+}
+
+void KyraEngine::snd_playWanderScoreViaMap(int command, int restart) {
+ debug(9, "KyraEngine::snd_playWanderScoreViaMap(%d, %d)", command, restart);
+ static const int8 soundTable[] = {
+ -1, 0, -1, 1, 0, 3, 0, 2,
+ 0, 4, 1, 2, 1, 3, 1, 4,
+ 1, 0x5C, 1, 6, 1, 7, 2, 2,
+ 2, 3, 2, 4, 2, 5, 2, 6,
+ 2, 7, 3, 3, 3, 4, 1, 8,
+ 1, 9, 4, 2, 4, 3, 4, 4,
+ 4, 5, 4, 6, 4, 7, 4, 8,
+ 1, 0x0B, 1, 0x0C, 1, 0x0E, 1, 0x0D,
+ 4, 9, 5, 0x0C, 6, 2, 6, 6,
+ 6, 7, 6, 8, 6, 9, 6, 3,
+ 6, 4, 6, 5, 7, 2, 7, 3,
+ 7, 4, 7, 5, 7, 6, 7, 7,
+ 7, 8, 7, 9, 8, 2, 8, 3,
+ 8, 4, 8, 5, 6, 0x0B, 5, 0x0B
+ };
+ //if (!_disableSound) {
+ // XXX
+ //}
+ assert(command*2+1 < ARRAYSIZE(soundTable));
+ if (_curMusicTheme != soundTable[command*2]+1) {
+ if (soundTable[command*2] != -1) {
+ snd_playTheme(soundTable[command*2]+1);
+ }
+ }
+
+ if (restart)
+ _lastMusicCommand = -1;
+
+ if (command != 1) {
+ if (_lastMusicCommand != command) {
+ _lastMusicCommand = command;
+ _sound->playTrack(soundTable[command*2+1], true);
+ }
+ } else {
+ _lastMusicCommand = 1;
+ _sound->beginFadeOut();
+ }
+}
+
+void KyraEngine::snd_playVoiceFile(int id) {
+ debug(9, "KyraEngine::snd_playVoiceFile(%d)", id);
+ char vocFile[9];
+ assert(id >= 0 && id < 9999);
+ sprintf(vocFile, "%03d", id);
+ _sound->voicePlay(vocFile);
+}
+
+void KyraEngine::snd_voiceWaitForFinish(bool ingame) {
+ debug(9, "KyraEngine::snd_voiceWaitForFinish(%d)", ingame);
+ while (_sound->voiceIsPlaying() && !_skipFlag) {
+ if (ingame) {
+ delay(10, true);
+ } else {
+ _system->delayMillis(10);
+ }
+ }
+}
+
+// static res
+
+const SoundPC::SpeechCodecs SoundPC::_supportedCodes[] = {
+#ifdef USE_MAD
+ { ".VO3", makeMP3Stream },
+#endif // USE_MAD
+#ifdef USE_VORBIS
+ { ".VOG", makeVorbisStream },
+#endif // USE_VORBIS
+#ifdef USE_FLAC
+ { ".VOF", makeFlacStream },
+#endif // USE_FLAC
+ { 0, 0 }
+};
+
+} // end of namespace Kyra
diff --git a/engines/kyra/sound.h b/engines/kyra/sound.h
new file mode 100644
index 0000000000..7af201cd9f
--- /dev/null
+++ b/engines/kyra/sound.h
@@ -0,0 +1,155 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SOUND_H
+#define SOUND_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "kyra/kyra.h"
+
+class AudioStream;
+
+namespace Audio {
+class Mixer;
+class SoundHandle;
+} // end of namespace Audio
+
+namespace Kyra {
+
+class Sound {
+public:
+ Sound() {}
+ virtual ~Sound() {}
+
+ virtual void setVolume(int volume) = 0;
+ virtual int getVolume() = 0;
+
+ virtual void playMusic(const char *file) = 0;
+ virtual void playMusic(uint8 *data, uint32 size) = 0;
+ virtual void stopMusic() = 0;
+
+ virtual void playTrack(uint8 track, bool looping = true) = 0;
+ virtual void haltTrack() = 0;
+ virtual void startTrack() = 0;
+
+ virtual void loadSoundEffectFile(const char *file) = 0;
+ virtual void loadSoundEffectFile(uint8 *data, uint32 size) = 0;
+ virtual void stopSoundEffect() = 0;
+
+ virtual void playSoundEffect(uint8 track) = 0;
+
+ virtual void beginFadeOut() = 0;
+ virtual bool fadeOut() = 0;
+
+ virtual void voicePlay(const char *file) = 0;
+ virtual void voiceUnload() = 0;
+ virtual bool voiceIsPlaying() = 0;
+};
+
+class SoundPC : public MidiDriver, public Sound {
+public:
+
+ SoundPC(MidiDriver *driver, Audio::Mixer *mixer, KyraEngine *engine);
+ ~SoundPC();
+
+ void setVolume(int volume);
+ int getVolume() { return _volume; }
+
+ void hasNativeMT32(bool nativeMT32) { _nativeMT32 = nativeMT32; }
+ bool isMT32() { return _nativeMT32; }
+
+ void playMusic(const char *file);
+ void playMusic(uint8 *data, uint32 size);
+ void stopMusic();
+
+ void playTrack(uint8 track, bool looping);
+ void haltTrack() { _isPlaying = false; }
+ void startTrack() { _isPlaying = true; }
+ void setPassThrough(bool b) { _passThrough = b; }
+
+ void loadSoundEffectFile(const char *file);
+ void loadSoundEffectFile(uint8 *data, uint32 size);
+ void stopSoundEffect();
+
+ void playSoundEffect(uint8 track);
+
+ void beginFadeOut();
+ bool fadeOut() { return _fadeMusicOut; }
+
+ void voicePlay(const char *file);
+ void voiceUnload() {};
+ bool voiceIsPlaying();
+
+ //MidiDriver interface implementation
+ int open();
+ void close();
+ void send(uint32 b);
+ void metaEvent(byte type, byte *data, uint16 length);
+
+ void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
+ uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; }
+
+ //Channel allocation functions
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+protected:
+
+ static void onTimer(void *data);
+
+ MidiChannel *_channel[32];
+ int _virChannel[16];
+ uint8 _channelVolume[16];
+ MidiDriver *_driver;
+ bool _nativeMT32;
+ bool _passThrough;
+ uint8 _volume;
+ bool _isPlaying;
+ bool _sfxIsPlaying;
+ uint32 _fadeStartTime;
+ bool _fadeMusicOut;
+ bool _isLooping;
+ bool _eventFromMusic;
+ MidiParser *_parser;
+ byte *_parserSource;
+ MidiParser *_soundEffect;
+ byte *_soundEffectSource;
+ KyraEngine *_engine;
+
+ Audio::Mixer *_mixer;
+ AudioStream *_currentVocFile;
+ Audio::SoundHandle _vocHandle;
+ Common::File _compressHandle;
+
+ struct SpeechCodecs {
+ const char *fileext;
+ AudioStream *(*streamFunc)(Common::File*, uint32);
+ };
+
+ static const SpeechCodecs _supportedCodes[];
+};
+} // end of namespace Kyra
+
+#endif
diff --git a/engines/kyra/sprites.cpp b/engines/kyra/sprites.cpp
new file mode 100644
index 0000000000..d1f9d00fd6
--- /dev/null
+++ b/engines/kyra/sprites.cpp
@@ -0,0 +1,569 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/system.h"
+#include "kyra/screen.h"
+#include "kyra/kyra.h"
+#include "kyra/sprites.h"
+#include "kyra/resource.h"
+#include "kyra/animator.h"
+
+namespace Kyra {
+
+Sprites::Sprites(KyraEngine *engine, OSystem *system) {
+ _engine = engine;
+ _res = engine->resource();
+ _screen = engine->screen();
+ _system = system;
+ _dat = 0;
+ memset(_anims, 0, sizeof(_anims));
+ memset(_sceneShapes, 0, sizeof(_sceneShapes));
+ _animDelay = 16;
+ _spriteDefStart = 0;
+ memset(_drawLayerTable, 0, sizeof(_drawLayerTable));
+ _sceneAnimatorBeaconFlag = 0;
+}
+
+Sprites::~Sprites() {
+ delete[] _dat;
+ freeSceneShapes();
+ for (int i = 0; i < MAX_NUM_ANIMS; i++) {
+ if (_anims[i].background)
+ free(_anims[i].background);
+ }
+}
+
+void Sprites::setupSceneAnims() {
+ debug(9, "Sprites::setupSceneAnims()");
+ uint8 *data;
+
+ for (int i = 0; i < MAX_NUM_ANIMS; i++) {
+ if (_anims[i].background) {
+ free(_anims[i].background);
+ _anims[i].background = 0;
+ }
+
+ if (_anims[i].script != 0) {
+ data = _anims[i].script;
+
+ assert( READ_LE_UINT16(data) == 0xFF86 );
+ data += 4;
+
+ _anims[i].disable = READ_LE_UINT16(data) != 0;
+ data += 4;
+ _anims[i].unk2 = READ_LE_UINT16(data);
+ data += 4;
+
+ if (_engine->_northExitHeight > READ_LE_UINT16(data))
+ _anims[i].drawY = _engine->_northExitHeight;
+ else
+ _anims[i].drawY = READ_LE_UINT16(data);
+ data += 4;
+
+ //sceneUnk2[i] = READ_LE_UINT16(data);
+ data += 4;
+
+ _anims[i].x = READ_LE_UINT16(data);
+ data += 4;
+ _anims[i].y = READ_LE_UINT16(data);
+ data += 4;
+ _anims[i].width = *(data) - 1;
+ data += 4;
+ _anims[i].height = *(data);
+ data += 4;
+ _anims[i].sprite = READ_LE_UINT16(data);
+ data += 4;
+ _anims[i].flipX = READ_LE_UINT16(data) != 0;
+ data += 4;
+ _anims[i].width2 = *(data);
+ data += 4;
+ _anims[i].height2 = *(data);
+ data += 4;
+ _anims[i].unk1 = READ_LE_UINT16(data) != 0;
+ data += 4;
+ _anims[i].play = READ_LE_UINT16(data) != 0;
+ data += 2;
+
+ _anims[i].script = data;
+
+ int bkgdWidth = _anims[i].width << 3;
+ int bkgdHeight = _anims[i].height;
+
+ if (_anims[i].width2)
+ bkgdWidth += _anims[i].width2 << 3;
+
+ if (_anims[i].height2)
+ bkgdHeight += _anims[i].height2;
+
+ _anims[i].background = (uint8 *)malloc(_screen->getRectSize(bkgdWidth + 1, bkgdHeight));
+ memset(_anims[i].background, 0, _screen->getRectSize(bkgdWidth + 1, bkgdHeight));
+
+ assert(_anims[i].background);
+ }
+ }
+}
+
+void Sprites::updateSceneAnims() {
+ debug(9, "Sprites::updateSceneAnims()");
+ uint32 currTime = _system->getMillis();
+ uint8 *data;
+ bool endLoop;
+ uint16 rndNr;
+ uint16 anim;
+ uint16 sound;
+
+ for (int i = 0; i < MAX_NUM_ANIMS; i++) {
+ if (_anims[i].script == 0 || !_anims[i].play || _anims[i].nextRun != 0 && _anims[i].nextRun > currTime)
+ continue;
+
+ if (_anims[i].reentry == 0) {
+ data = _anims[i].script;
+ if (READ_LE_UINT16(data) == 0xFF8B)
+ continue;
+ } else {
+ data = _anims[i].reentry;
+ _anims[i].reentry = 0;
+ }
+
+ endLoop = false;
+ while (READ_LE_UINT16(data) != 0xFF87 && !endLoop) {
+ assert((data - _anims[i].script) < _anims[i].length);
+ switch (READ_LE_UINT16(data)) {
+ case 0xFF88:
+ data += 2;
+ debug(6, "func: Set sprite image.");
+ debug(6, "Sprite index %i", READ_LE_UINT16(data));
+ _anims[i].sprite = READ_LE_UINT16(data);
+ data += 2;
+ //debug(6, "Unused %i", READ_LE_UINT16(data));
+ data += 2;
+ debug(6, "X %i", READ_LE_UINT16(data));
+ _anims[i].x = READ_LE_UINT16(data);
+ data += 2;
+ debug(6, "Y %i", READ_LE_UINT16(data));
+ _anims[i].y = READ_LE_UINT16(data);
+ data += 2;
+ _anims[i].flipX = false;
+ refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
+ break;
+ case 0xFF8D:
+ data += 2;
+ debug(6, "func: Set sprite image, flipped.");
+ debug(6, "Sprite index %i", READ_LE_UINT16(data));
+ _anims[i].sprite = READ_LE_UINT16(data);
+ data += 2;
+ //debug(9, "Unused %i", READ_LE_UINT16(data));
+ data += 2;
+ debug(6, "X %i", READ_LE_UINT16(data));
+ _anims[i].x = READ_LE_UINT16(data);
+ data += 2;
+ debug(6, "Y %i", READ_LE_UINT16(data));
+ _anims[i].y = READ_LE_UINT16(data);
+ data += 2;
+ _anims[i].flipX = true;
+ refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
+ break;
+ case 0xFF8A:
+ data += 2;
+ debug(6, "func: Set time to wait");
+ debug(6, "Time %i", READ_LE_UINT16(data));
+ _anims[i].nextRun = _system->getMillis() + READ_LE_UINT16(data) * _animDelay;
+ data += 2;
+ break;
+ case 0xFFB3:
+ data += 2;
+ debug(6, "func: Set time to wait to random value");
+ rndNr = READ_LE_UINT16(data) + _rnd.getRandomNumber( READ_LE_UINT16(data) + 2);
+ debug(6, "Minimum time %i", READ_LE_UINT16(data));
+ data += 2;
+ debug(6, "Maximum time %i", READ_LE_UINT16(data));
+ data += 2;
+ _anims[i].nextRun = _system->getMillis() + rndNr * _animDelay;
+ break;
+ case 0xFF8C:
+ data += 2;
+ debug(6, "func: Wait until wait time has elapsed");
+ _anims[i].reentry = data;
+ endLoop = true;
+ //assert( _anims[i].nextRun > _system->getMillis());
+ break;
+ case 0xFF99:
+ data += 2;
+ debug(1, "func: Set value of unknown animation property to 1");
+ _anims[i].unk1 = 1;
+ break;
+ case 0xFF9A:
+ data += 2;
+ debug(1, "func: Set value of unknown animation property to 0");
+ _anims[i].unk1 = 0;
+ break;
+ case 0xFF97:
+ data += 2;
+ debug(6, "func: Set default X coordinate of sprite");
+ debug(6, "X %i", READ_LE_UINT16(data));
+ _anims[i].x = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF98:
+ data += 2;
+ debug(6, "func: Set default Y coordinate of sprite");
+ debug(6, "Y %i", READ_LE_UINT16(data));
+ _anims[i].y = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF8B:
+ debug(6, "func: Jump to start of script section");
+ //data = scriptStart;
+ _anims[i].nextRun = _system->getMillis();
+ endLoop = true;
+ break;
+ case 0xFF8E:
+ data += 2;
+ debug(6, "func: Begin for () loop");
+ debug(6, "Iterations: %i", READ_LE_UINT16(data));
+ _anims[i].loopsLeft = READ_LE_UINT16(data);
+ data += 2;
+ _anims[i].loopStart = data;
+ break;
+ case 0xFF8F:
+ data += 2;
+ debug(6, "func: End for () loop");
+ if (_anims[i].loopsLeft > 0) {
+ _anims[i].loopsLeft--;
+ data = _anims[i].loopStart;
+ }
+ break;
+ case 0xFF90:
+ data += 2;
+ debug(6, "func: Set sprite image using default X and Y");
+ debug(6, "Sprite index %i", READ_LE_UINT16(data));
+ _anims[i].sprite = READ_LE_UINT16(data);
+ _anims[i].flipX = false;
+ data += 2;
+ refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
+ break;
+ case 0xFF91:
+ data += 2;
+ debug(6, "func: Set sprite image using default X and Y, flipped.");
+ debug(6, "Sprite index %i", READ_LE_UINT16(data));
+ _anims[i].sprite = READ_LE_UINT16(data);
+ _anims[i].flipX = true;
+ data += 2;
+ refreshSceneAnimObject(i, _anims[i].sprite, _anims[i].x, _anims[i].y, _anims[i].flipX, _anims[i].unk1 != 0);
+ break;
+ case 0xFF92:
+ data += 2;
+ debug(6, "func: Increase value of default X-coordinate");
+ debug(6, "Increment %i", READ_LE_UINT16(data));
+ _anims[i].x += READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF93:
+ data += 2;
+ debug(6, "func: Increase value of default Y-coordinate");
+ debug(6, "Increment %i", READ_LE_UINT16(data));
+ _anims[i].y += READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF94:
+ data += 2;
+ debug(6, "func: Decrease value of default X-coordinate");
+ debug(6, "Decrement %i", READ_LE_UINT16(data));
+ _anims[i].x -= READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF95:
+ data += 2;
+ debug(6, "func: Decrease value of default Y-coordinate");
+ debug(6, "Decrement %i", READ_LE_UINT16(data));
+ _anims[i].y -= READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFF96:
+ data += 2;
+ debug(9, "func: Stop animation");
+ debug(9, "Animation index %i", READ_LE_UINT16(data));
+ anim = READ_LE_UINT16(data);
+ data += 2;
+ _anims[anim].play = false;
+ _anims[anim].sprite = -1;
+ break;
+/* case 0xFF97:
+ data += 2;
+ debug(1, "func: Set value of animation property 34h to 0");
+ break;*/
+ case 0xFFAD:
+ data += 2;
+ debug(6, "func: Set Brandon's X coordinate");
+ debug(6, "X %i", READ_LE_UINT16(data));
+ _engine->currentCharacter()->x1 = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFFAE:
+ data += 2;
+ debug(6, "func: Set Brandon's Y coordinate");
+ debug(6, "Y %i", READ_LE_UINT16(data));
+ _engine->currentCharacter()->y1 = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFFAF:
+ data += 2;
+ debug(6, "func: Set Brandon's sprite");
+ debug(6, "Sprite %i", READ_LE_UINT16(data));
+ _engine->currentCharacter()->currentAnimFrame = READ_LE_UINT16(data);
+ data += 2;
+ break;
+ case 0xFFAA:
+ data += 2;
+ debug(1, "TODO func: Reset Brandon's sprite");
+ break;
+ case 0xFFAB:
+ data += 2;
+ debug(6, "func: Update Brandon's sprite");
+ _engine->animator()->animRefreshNPC(0);
+ _engine->animator()->flagAllObjectsForRefresh();
+ _engine->animator()->updateAllObjectShapes();
+ break;
+ case 0xFFB0:
+ data += 2;
+ debug(6, "func: Play sound");
+ debug(6, "Sound index %i", READ_LE_UINT16(data));
+ _engine->snd_playSoundEffect(READ_LE_UINT16(data));
+ data += 2;
+ break;
+ case 0xFFB1:
+ data += 2;
+ _sceneAnimatorBeaconFlag = 1;
+ break;
+ case 0xFFB2:
+ data += 2;
+ _sceneAnimatorBeaconFlag = 0;
+ break;
+ case 0xFFB4:
+ data += 2;
+ debug(6, "func: Play (at random) a certain sound at a certain percentage of time");
+ debug(6, "Sound index %i", READ_LE_UINT16(data));
+ sound = READ_LE_UINT16(data);
+ data += 2;
+ debug(6, "Percentage %i", READ_LE_UINT16(data));
+ rndNr = _rnd.getRandomNumber(100);
+ if (rndNr <= READ_LE_UINT16(data))
+ _engine->snd_playSoundEffect(sound);
+ data += 2;
+ break;
+ case 0xFFA7:
+ data += 2;
+ _anims[READ_LE_UINT16(data)].play = 1;
+ data += 2;
+ break;
+ default:
+ debug(1, "Unsupported anim command %X in script %i", READ_LE_UINT16(data), i);
+ //endLoop = true;
+ data += 1;
+ break;
+ }
+ }
+
+ if (READ_LE_UINT16(data) == 0xFF87)
+ _anims[i].play = false;
+ }
+}
+
+void Sprites::loadDAT(const char *filename, SceneExits &exits) {
+ debug(9, "Sprites::loadDat('%s')", filename);
+ uint32 fileSize;
+
+ delete[] _dat;
+ _spriteDefStart = 0;
+
+ _dat = _res->fileData(filename, &fileSize);
+
+ memset(_anims, 0, sizeof(_anims));
+ uint8 nextAnim = 0;
+
+ assert(fileSize > 0x6D);
+
+ memcpy(_drawLayerTable, (_dat + 0x0D), 8);
+ _engine->_northExitHeight = READ_LE_UINT16(_dat + 0x15);
+ if (_engine->_northExitHeight & 1)
+ _engine->_northExitHeight += 1;
+ // XXX
+ memcpy(_screen->_currentPalette + 744 - 60, _dat + 0x17, 60);
+ uint8 *data = _dat + 0x6B;
+
+ uint16 length = READ_LE_UINT16(data);
+ data += 2;
+
+ if (length > 2) {
+ assert( length < fileSize);
+ uint8 *animstart;
+ uint8 *start = data;
+
+ while (1) {
+ if (((uint16)(data - _dat) >= fileSize) || (data - start) >= length)
+ break;
+
+ if (READ_LE_UINT16(data) == 0xFF83) {
+ //debug(1, "Body section end.");
+ data += 2;
+ break;
+ }
+
+ switch (READ_LE_UINT16(data)) {
+ case 0xFF81:
+ data += 2;
+ //debug(1, "Body section start");
+ break;
+ case 0xFF82:
+ data += 2;
+ //debug(1, "Unknown 0xFF82 section");
+ break;
+ case 0xFF84:
+ data += 2;
+ _spriteDefStart = data;
+ while (READ_LE_UINT16(data) != 0xFF85) {
+ data += 2;
+ }
+ data += 2;
+ break;
+ case 0xFF86:
+ assert(nextAnim < MAX_NUM_ANIMS);
+ _anims[nextAnim].script = data;
+ _anims[nextAnim].sprite = -1;
+ _anims[nextAnim].play = true;
+ animstart = data;
+ data += 2;
+ while (READ_LE_UINT16(data) != 0xFF87) {
+ assert((uint16)(data - _dat) < fileSize);
+ data += 2;
+ }
+ _anims[nextAnim].length = data - animstart;
+ //debug(1, "Found an anim script of length %i!", _anims[nextAnim].length);
+ nextAnim++;
+ data += 2;
+ break;
+ default:
+ debug(1, "Unknown code in DAT file: %x", READ_LE_UINT16(data));
+ data += 2;
+ break;
+ }
+ }
+ } else {
+ data += 2;
+ }
+
+ assert(fileSize - (data - _dat) == 0xC);
+
+ exits.northXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
+ exits.northYPos = *data++ & 0xFFFE;
+ exits.eastXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
+ exits.eastYPos = *data++ & 0xFFFE;
+ exits.southXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
+ exits.southYPos = *data++ & 0xFFFE;
+ exits.westXPos = READ_LE_UINT16(data) & 0xFFFC; data += 2;
+ exits.westYPos = *data++ & 0xFFFE;
+}
+
+void Sprites::freeSceneShapes() {
+ debug(9, "Sprites::freeSceneShapes()");
+ for (int i = 0; i < ARRAYSIZE(_sceneShapes); i++ ) {
+ free(_sceneShapes[i]);
+ _sceneShapes[i] = 0;
+ }
+}
+
+void Sprites::loadSceneShapes() {
+ debug(9, "Sprites::loadSceneShapes()");
+ uint8 *data = _spriteDefStart;
+ int spriteNum, x, y, width, height;
+
+ freeSceneShapes();
+ memset( _sceneShapes, 0, sizeof(_sceneShapes));
+
+ if (_spriteDefStart == 0)
+ return;
+
+ int bakPage = _screen->_curPage;
+ _screen->_curPage = 3;
+
+ while (READ_LE_UINT16(data) != 0xFF85) {
+ spriteNum = READ_LE_UINT16(data);
+ assert(spriteNum < ARRAYSIZE(_sceneShapes));
+ data += 2;
+ x = READ_LE_UINT16(data) * 8;
+ data += 2;
+ y = READ_LE_UINT16(data);
+ data += 2;
+ width = READ_LE_UINT16(data) * 8;
+ data += 2;
+ height = READ_LE_UINT16(data);
+ data += 2;
+ _sceneShapes[spriteNum] = _screen->encodeShape(x, y, width, height, 2);
+ debug(9, "Sprite %i is at (%i, %i), width %i, height %i", spriteNum, x, y, width, height);
+ }
+ _screen->_curPage = bakPage;
+}
+
+void Sprites::refreshSceneAnimObject(uint8 animNum, uint8 shapeNum, uint16 x, uint16 y, bool flipX, bool unkFlag) {
+ debug(9, "Sprites::refreshSceneAnimObject(%i, %i, %i, %i, %i, %i", animNum, shapeNum, x, y, flipX, unkFlag);
+ AnimObject &anim = _engine->animator()->sprites()[animNum];
+ anim.refreshFlag = 1;
+ anim.bkgdChangeFlag = 1;
+
+ if (unkFlag)
+ anim.flags |= 0x0200;
+ else
+ anim.flags &= 0xFD00;
+
+ if (flipX)
+ anim.flags |= 1;
+ else
+ anim.flags &= 0xFE;
+
+ anim.sceneAnimPtr = _sceneShapes[shapeNum];
+ anim.animFrameNumber = -1;
+ anim.x1 = x;
+ anim.y1 = y;
+}
+
+int Sprites::getDrawLayer(int y) {
+ debug(9, "getDrawLayer(%d)", y);
+ uint8 returnValue = 0;
+ for (int i = 0; i < ARRAYSIZE(_drawLayerTable); ++i) {
+ uint8 temp = _drawLayerTable[i];
+ if (temp) {
+ if (temp <= y) {
+ returnValue = i;
+ }
+ }
+ }
+ if (returnValue <= 0) {
+ returnValue = 1;
+ } else if (returnValue >= 7) {
+ returnValue = 6;
+ }
+ return returnValue;
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/sprites.h b/engines/kyra/sprites.h
new file mode 100644
index 0000000000..b1ce9ae8e7
--- /dev/null
+++ b/engines/kyra/sprites.h
@@ -0,0 +1,95 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef KYRASPRITES_H
+#define KYRASPRITES_H
+
+namespace Kyra {
+
+#define MAX_NUM_ANIMS 11
+
+struct Sprite {
+ uint16 x;
+ uint16 y;
+ uint16 width;
+ uint16 height;
+};
+
+struct Anim {
+ uint8 *script;
+ uint16 length;
+ int16 x;
+ int16 y;
+ bool flipX;
+ int8 sprite;
+ uint8 *loopStart;
+ uint16 loopsLeft;
+ uint8 *reentry;
+ uint32 nextRun;
+ bool play;
+ uint16 width;
+ uint16 height;
+ uint16 width2;
+ uint16 height2;
+ uint16 unk1;
+ uint16 drawY;
+ uint16 unk2;
+ uint8 *background;
+ bool disable;
+};
+
+class Sprites {
+public:
+
+ Sprites(KyraEngine *engine, OSystem *system);
+ ~Sprites();
+
+ void updateSceneAnims();
+ void setupSceneAnims();
+ void loadDAT(const char *filename, SceneExits &exits);
+ void loadSceneShapes();
+
+ Anim _anims[MAX_NUM_ANIMS];
+ uint8 *_sceneShapes[50];
+
+ void refreshSceneAnimObject(uint8 animNum, uint8 shapeNum, uint16 x, uint16 y, bool flipX, bool unkFlag);
+
+ int getDrawLayer(int y);
+
+ int _sceneAnimatorBeaconFlag;
+protected:
+ void freeSceneShapes();
+
+ KyraEngine *_engine;
+ Resource *_res;
+ OSystem *_system;
+ Screen *_screen;
+ uint8 *_dat;
+ Common::RandomSource _rnd;
+ uint8 _animDelay;
+ uint8 *_spriteDefStart;
+ uint8 _drawLayerTable[8];
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp
new file mode 100644
index 0000000000..66e10358c3
--- /dev/null
+++ b/engines/kyra/staticres.cpp
@@ -0,0 +1,978 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/resource.h"
+
+namespace Kyra {
+
+#define RESFILE_VERSION 11
+
+#define GAME_FLAGS (GF_FLOPPY | GF_TALKIE | GF_DEMO | GF_AUDIOCD)
+#define LANGUAGE_FLAGS (GF_ENGLISH | GF_FRENCH | GF_GERMAN | GF_SPANISH | GF_LNGUNK)
+
+byte *getFile(PAKFile &res, const char *filename) {
+ uint32 size = 0;
+ size = res.getFileSize(filename);
+ if (!size)
+ return 0;
+ return res.getFile(filename);
+}
+
+struct LanguageTypes {
+ uint32 flags;
+ const char *ext;
+};
+
+static LanguageTypes languages[] = {
+ { GF_ENGLISH, "ENG" }, // this is the default language
+ { GF_FRENCH, "FRE" },
+ { GF_GERMAN, "GER" },
+ { GF_SPANISH, "SPA" },
+ { 0, 0 }
+};
+
+void KyraEngine::res_loadResources(int type) {
+ debug(9, "res_loadResources(%d)", type);
+ PAKFile resFile("KYRA.DAT");
+ if (!resFile.isValid() || !resFile.isOpen()) {
+ error("couldn't open Kyrandia resource file ('KYRA.DAT') make sure you got one file for your version");
+ }
+
+ uint32 version = 0;
+ uint32 gameID = 0;
+ uint32 featuresValue = 0;
+ bool loadNativeLanguage = true;
+
+ byte *temp = 0;
+
+ if (_features & GF_TALKIE) {
+ temp = getFile(resFile, "INDEX.CD");
+ } else if (_features & GF_DEMO) {
+ temp = getFile(resFile, "INDEX.DEM");
+ } else {
+ temp = getFile(resFile, "INDEX");
+ }
+ if (!temp) {
+ error("no matching INDEX file found");
+ }
+
+ version = READ_BE_UINT32(temp);
+ gameID = READ_BE_UINT32((temp+4));
+ featuresValue = READ_BE_UINT32((temp+8));
+
+ delete [] temp;
+ temp = 0;
+
+ if (version < RESFILE_VERSION) {
+ error("invalid KYRA.DAT file version (%d, required %d)", version, RESFILE_VERSION);
+ }
+ if (gameID != _game) {
+ error("invalid game id (%d)", gameID);
+ }
+ if ((featuresValue & GAME_FLAGS) != (_features & GAME_FLAGS)) {
+ error("your data file has a different game flags (0x%.08X has the data and your version has 0x%.08X)", (featuresValue & GAME_FLAGS), (_features & GAME_FLAGS));
+ }
+
+ if (!((featuresValue & LANGUAGE_FLAGS) & (_features & LANGUAGE_FLAGS))) {
+ char buffer[240];
+ sprintf(buffer, "your data file has support for:");
+ if (featuresValue & GF_ENGLISH) {
+ sprintf(buffer + strlen(buffer), " English");
+ }
+ if (featuresValue & GF_FRENCH) {
+ sprintf(buffer + strlen(buffer), " French");
+ }
+ if (featuresValue & GF_GERMAN) {
+ sprintf(buffer + strlen(buffer), " German");
+ }
+ if (featuresValue & GF_SPANISH) {
+ sprintf(buffer + strlen(buffer), " Spanish");
+ }
+ sprintf(buffer + strlen(buffer), " but not your language (");
+ if (_features & GF_ENGLISH) {
+ sprintf(buffer + strlen(buffer), "English");
+ } else if (_features & GF_FRENCH) {
+ sprintf(buffer + strlen(buffer), "French");
+ } else if (_features & GF_GERMAN) {
+ sprintf(buffer + strlen(buffer), "German");
+ } else if (_features & GF_SPANISH) {
+ sprintf(buffer + strlen(buffer), "Spanish");
+ } else {
+ sprintf(buffer + strlen(buffer), "unknown");
+ }
+ sprintf(buffer + strlen(buffer), ")");
+ warning(buffer);
+ loadNativeLanguage = false;
+ }
+
+#define getFileEx(x, y) \
+ if (_features & GF_TALKIE) { \
+ temp = getFile(x, y ".CD"); \
+ } else if (_features & GF_DEMO) { \
+ temp = getFile(x, y ".DEM"); \
+ } else { \
+ temp = getFile(x, y); \
+ }
+#define loadRawFile(x, y, z) \
+ getFileEx(x, y) \
+ if (temp) { \
+ z = temp; \
+ temp = 0; \
+ }
+#define loadTable(x, y, z, a) \
+ getFileEx(x, y) \
+ if (temp) { \
+ res_loadTable(temp, z, a); \
+ delete [] temp; \
+ temp = 0; \
+ }
+#define loadRooms(x, y, z, a) \
+ getFileEx(x, y) \
+ if (temp) { \
+ res_loadRoomTable(temp, z, a); \
+ delete [] temp; \
+ temp = 0; \
+ }
+#define loadShapes(x, y, z, a) \
+ getFileEx(x, y) \
+ if (temp) { \
+ res_loadShapeTable(temp, z, a); \
+ delete [] temp; \
+ temp = 0; \
+ }
+
+
+ if ((type & RES_INTRO) || (type & RES_OUTRO) || type == RES_ALL) {
+ loadRawFile(resFile, "FOREST.SEQ", _seq_Forest);
+ loadRawFile(resFile, "KALLAK-WRITING.SEQ", _seq_KallakWriting);
+ loadRawFile(resFile, "KYRANDIA-LOGO.SEQ", _seq_KyrandiaLogo);
+ loadRawFile(resFile, "KALLAK-MALCOLM.SEQ", _seq_KallakMalcolm);
+ loadRawFile(resFile, "MALCOLM-TREE.SEQ", _seq_MalcolmTree);
+ loadRawFile(resFile, "WESTWOOD-LOGO.SEQ", _seq_WestwoodLogo);
+ loadRawFile(resFile, "DEMO1.SEQ", _seq_Demo1);
+ loadRawFile(resFile, "DEMO2.SEQ", _seq_Demo2);
+ loadRawFile(resFile, "DEMO3.SEQ", _seq_Demo3);
+ loadRawFile(resFile, "DEMO4.SEQ", _seq_Demo4);
+
+ loadTable(resFile, "INTRO-CPS.TXT", (byte***)&_seq_CPSTable, &_seq_CPSTable_Size);
+ loadTable(resFile, "INTRO-COL.TXT", (byte***)&_seq_COLTable, &_seq_COLTable_Size);
+ loadTable(resFile, "INTRO-WSA.TXT", (byte***)&_seq_WSATable, &_seq_WSATable_Size);
+
+ res_loadLangTable("INTRO-STRINGS.", &resFile, (byte***)&_seq_textsTable, &_seq_textsTable_Size, loadNativeLanguage);
+
+ loadRawFile(resFile, "REUNION.SEQ", _seq_Reunion);
+
+ res_loadLangTable("HOME.", &resFile, (byte***)&_homeString, &_homeString_Size, loadNativeLanguage);
+ }
+
+ if ((type & RES_INGAME) || type == RES_ALL) {
+ loadTable(resFile, "ROOM-FILENAMES.TXT", (byte***)&_roomFilenameTable, &_roomFilenameTableSize);
+ loadRooms(resFile, "ROOM-TABLE.ROOM", &_roomTable, &_roomTableSize);
+
+ loadTable(resFile, "CHAR-IMAGE.TXT", (byte***)&_characterImageTable, &_characterImageTableSize);
+
+ loadShapes(resFile, "SHAPES-DEFAULT.SHP", &_defaultShapeTable, &_defaultShapeTableSize);
+
+ res_loadLangTable("ITEMLIST.", &resFile, (byte***)&_itemList, &_itemList_Size, loadNativeLanguage);
+ res_loadLangTable("TAKEN.", &resFile, (byte***)&_takenList, &_takenList_Size, loadNativeLanguage);
+ res_loadLangTable("PLACED.", &resFile, (byte***)&_placedList, &_placedList_Size, loadNativeLanguage);
+ res_loadLangTable("DROPPED.", &resFile, (byte***)&_droppedList, &_droppedList_Size, loadNativeLanguage);
+ res_loadLangTable("NODROP.", &resFile, (byte***)&_noDropList, &_noDropList_Size, loadNativeLanguage);
+
+ loadRawFile(resFile, "AMULETEANIM.SEQ", _amuleteAnim);
+
+ for (int i = 1; i <= 33; ++i) {
+ char buffer[32];
+ sprintf(buffer, "PALTABLE%d.PAL", i);
+ if (_features & GF_TALKIE) {
+ strcat(buffer, ".CD");
+ } else if (_features & GF_DEMO) {
+ strcat(buffer, ".DEM");
+ }
+ temp = getFile(resFile, buffer);
+ if (temp) {
+ _specialPalettes[i-1] = temp;
+ temp = 0;
+ }
+ }
+
+ res_loadLangTable("PUTDOWN.", &resFile, (byte***)&_putDownFirst, &_putDownFirst_Size, loadNativeLanguage);
+ res_loadLangTable("WAITAMUL.", &resFile, (byte***)&_waitForAmulet, &_waitForAmulet_Size, loadNativeLanguage);
+ res_loadLangTable("BLACKJEWEL.", &resFile, (byte***)&_blackJewel, &_blackJewel_Size, loadNativeLanguage);
+ res_loadLangTable("POISONGONE.", &resFile, (byte***)&_poisonGone, &_poisonGone_Size, loadNativeLanguage);
+ res_loadLangTable("HEALINGTIP.", &resFile, (byte***)&_healingTip, &_healingTip_Size, loadNativeLanguage);
+
+ loadShapes(resFile, "HEALING.SHP", &_healingShapeTable, &_healingShapeTableSize);
+ loadShapes(resFile, "HEALING2.SHP", &_healingShape2Table, &_healingShape2TableSize);
+
+ res_loadLangTable("THEPOISON.", &resFile, (byte***)&_thePoison, &_thePoison_Size, loadNativeLanguage);
+ res_loadLangTable("FLUTE.", &resFile, (byte***)&_fluteString, &_fluteString_Size, loadNativeLanguage);
+
+ loadShapes(resFile, "POISONDEATH.SHP", &_posionDeathShapeTable, &_posionDeathShapeTableSize);
+ loadShapes(resFile, "FLUTE.SHP", &_fluteAnimShapeTable, &_fluteAnimShapeTableSize);
+
+ loadShapes(resFile, "WINTER1.SHP", &_winterScrollTable, &_winterScrollTableSize);
+ loadShapes(resFile, "WINTER2.SHP", &_winterScroll1Table, &_winterScroll1TableSize);
+ loadShapes(resFile, "WINTER3.SHP", &_winterScroll2Table, &_winterScroll2TableSize);
+ loadShapes(resFile, "DRINK.SHP", &_drinkAnimationTable, &_drinkAnimationTableSize);
+ loadShapes(resFile, "WISP.SHP", &_brandonToWispTable, &_brandonToWispTableSize);
+ loadShapes(resFile, "MAGICANIM.SHP", &_magicAnimationTable, &_magicAnimationTableSize);
+ loadShapes(resFile, "BRANSTONE.SHP", &_brandonStoneTable, &_brandonStoneTableSize);
+
+ res_loadLangTable("WISPJEWEL.", &resFile, (byte***)&_wispJewelStrings, &_wispJewelStrings_Size, loadNativeLanguage);
+ res_loadLangTable("MAGICJEWEL.", &resFile, (byte***)&_magicJewelString, &_magicJewelString_Size, loadNativeLanguage);
+
+ res_loadLangTable("FLASKFULL.", &resFile, (byte***)&_flaskFull, &_fullFlask_Size, loadNativeLanguage);
+ res_loadLangTable("FULLFLASK.", &resFile, (byte***)&_fullFlask, &_fullFlask_Size, loadNativeLanguage);
+
+ res_loadLangTable("VERYCLEVER.", &resFile, (byte***)&_veryClever, &_veryClever_Size, loadNativeLanguage);
+ }
+
+#undef loadRooms
+#undef loadTable
+#undef loadRawFile
+#undef getFileEx
+}
+
+void KyraEngine::res_unloadResources(int type) {
+ debug(9, "res_unloadResources(%d)", type);
+ if ((type & RES_INTRO) || (type & RES_OUTRO) || type & RES_ALL) {
+ res_freeLangTable(&_seq_WSATable, &_seq_WSATable_Size);
+ res_freeLangTable(&_seq_CPSTable, &_seq_CPSTable_Size);
+ res_freeLangTable(&_seq_COLTable, &_seq_COLTable_Size);
+ res_freeLangTable(&_seq_textsTable, &_seq_textsTable_Size);
+
+ delete [] _seq_Forest; _seq_Forest = 0;
+ delete [] _seq_KallakWriting; _seq_KallakWriting = 0;
+ delete [] _seq_KyrandiaLogo; _seq_KyrandiaLogo = 0;
+ delete [] _seq_KallakMalcolm; _seq_KallakMalcolm = 0;
+ delete [] _seq_MalcolmTree; _seq_MalcolmTree = 0;
+ delete [] _seq_WestwoodLogo; _seq_WestwoodLogo = 0;
+ delete [] _seq_Demo1; _seq_Demo1 = 0;
+ delete [] _seq_Demo2; _seq_Demo2 = 0;
+ delete [] _seq_Demo3; _seq_Demo3 = 0;
+ delete [] _seq_Demo4; _seq_Demo4 = 0;
+
+ delete [] _seq_Reunion; _seq_Reunion = 0;
+ res_freeLangTable(&_homeString, &_homeString_Size);
+ }
+
+ if ((type & RES_INGAME) || type & RES_ALL) {
+ res_freeLangTable(&_roomFilenameTable, &_roomFilenameTableSize);
+
+ delete [] _roomTable; _roomTable = 0;
+ _roomTableSize = 0;
+
+ res_freeLangTable(&_characterImageTable, &_characterImageTableSize);
+
+ delete [] _defaultShapeTable;
+ _defaultShapeTable = 0;
+ _defaultShapeTableSize = 0;
+
+ res_freeLangTable(&_itemList, &_itemList_Size);
+ res_freeLangTable(&_takenList, &_takenList_Size);
+ res_freeLangTable(&_placedList, &_placedList_Size);
+ res_freeLangTable(&_droppedList, &_droppedList_Size);
+ res_freeLangTable(&_noDropList, &_noDropList_Size);
+
+ delete [] _amuleteAnim;
+ _amuleteAnim = 0;
+
+ for (int i = 0; i < 33; ++i) {
+ delete [] _specialPalettes[i];
+ _specialPalettes[i] = 0;
+ }
+
+ res_freeLangTable(&_putDownFirst, &_putDownFirst_Size);
+ res_freeLangTable(&_waitForAmulet, &_waitForAmulet_Size);
+ res_freeLangTable(&_blackJewel, &_blackJewel_Size);
+ res_freeLangTable(&_poisonGone, &_poisonGone_Size);
+ res_freeLangTable(&_healingTip, &_healingTip_Size);
+
+ delete [] _healingShapeTable;
+ _healingShapeTable = 0;
+ _healingShapeTableSize = 0;
+
+ delete [] _healingShape2Table;
+ _healingShape2Table = 0;
+ _healingShape2TableSize = 0;
+
+ res_freeLangTable(&_thePoison, &_thePoison_Size);
+ res_freeLangTable(&_fluteString, &_fluteString_Size);
+
+ delete [] _posionDeathShapeTable;
+ _posionDeathShapeTable = 0;
+ _posionDeathShapeTableSize = 0;
+
+ delete [] _fluteAnimShapeTable;
+ _fluteAnimShapeTable = 0;
+ _fluteAnimShapeTableSize = 0;
+
+ delete [] _winterScrollTable;
+ _winterScrollTable = 0;
+ _winterScrollTableSize = 0;
+
+ delete [] _winterScroll1Table;
+ _winterScroll1Table = 0;
+ _winterScroll1TableSize = 0;
+
+ delete [] _winterScroll2Table;
+ _winterScroll2Table = 0;
+ _winterScroll2TableSize = 0;
+
+ delete [] _drinkAnimationTable;
+ _drinkAnimationTable = 0;
+ _drinkAnimationTableSize = 0;
+
+ delete [] _brandonToWispTable;
+ _brandonToWispTable = 0;
+ _brandonToWispTableSize = 0;
+
+ delete [] _magicAnimationTable;
+ _magicAnimationTable = 0;
+ _magicAnimationTableSize = 0;
+
+ delete [] _brandonStoneTable;
+ _brandonStoneTable = 0;
+ _brandonStoneTableSize = 0;
+
+ res_freeLangTable(&_flaskFull, &_flaskFull_Size);
+ res_freeLangTable(&_fullFlask, &_fullFlask_Size);
+
+ res_freeLangTable(&_veryClever, &_veryClever_Size);
+ }
+}
+
+void KyraEngine::res_loadLangTable(const char *filename, PAKFile *res, byte ***loadTo, int *size, bool nativ) {
+ char file[36];
+ for (int i = 0; languages[i].ext; ++i) {
+ if (languages[i].flags != (_features & LANGUAGE_FLAGS) && nativ) {
+ continue;
+ }
+
+ strcpy(file, filename);
+ strcat(file, languages[i].ext);
+ if (_features & GF_TALKIE) {
+ strcat(file, ".CD");
+ } else if (_features & GF_DEMO) {
+ strcat(file, ".DEM");
+ }
+ byte *temp = getFile(*res, file);
+ if (temp) {
+ res_loadTable(temp, loadTo, size);
+ delete [] temp;
+ temp = 0;
+ } else {
+ if (!nativ)
+ continue;
+ }
+ break;
+ }
+}
+
+void KyraEngine::res_loadTable(const byte *src, byte ***loadTo, int *size) {
+ uint32 count = READ_BE_UINT32(src); src += 4;
+ *size = count;
+ *loadTo = new byte*[count];
+
+ const char *curPos = (const char*)src;
+ for (uint32 i = 0; i < count; ++i) {
+ int strLen = strlen(curPos);
+ (*loadTo)[i] = new byte[strLen+1];
+ memcpy((*loadTo)[i], curPos, strLen+1);
+ curPos += strLen+1;
+ }
+}
+
+void KyraEngine::res_loadRoomTable(const byte *src, Room **loadTo, int *size) {
+ uint32 count = READ_BE_UINT32(src); src += 4;
+ *size = count;
+ *loadTo = new Room[count];
+
+ for (uint32 i = 0; i < count; ++i) {
+ (*loadTo)[i].nameIndex = *src++;
+ (*loadTo)[i].northExit = READ_BE_UINT16(src); src += 2;
+ (*loadTo)[i].eastExit = READ_BE_UINT16(src); src += 2;
+ (*loadTo)[i].southExit = READ_BE_UINT16(src); src += 2;
+ (*loadTo)[i].westExit = READ_BE_UINT16(src); src += 2;
+ memset(&(*loadTo)[i].itemsTable[0], 0xFF, sizeof(byte)*6);
+ memset(&(*loadTo)[i].itemsTable[6], 0, sizeof(byte)*6);
+ memset((*loadTo)[i].itemsXPos, 0, sizeof(uint16)*12);
+ memset((*loadTo)[i].itemsYPos, 0, sizeof(uint8)*12);
+ memset((*loadTo)[i].needInit, 0, sizeof((*loadTo)[i].needInit));
+ }
+}
+
+void KyraEngine::res_loadShapeTable(const byte *src, Shape **loadTo, int *size) {
+ uint32 count = READ_BE_UINT32(src); src += 4;
+ *size = count;
+ *loadTo = new Shape[count];
+
+ for (uint32 i = 0; i < count; ++i) {
+ (*loadTo)[i].imageIndex = *src++;
+ (*loadTo)[i].x = *src++;
+ (*loadTo)[i].y = *src++;
+ (*loadTo)[i].w = *src++;
+ (*loadTo)[i].h = *src++;
+ (*loadTo)[i].xOffset = *src++;
+ (*loadTo)[i].yOffset = *src++;
+ }
+}
+
+void KyraEngine::res_freeLangTable(char ***string, int *size) {
+ if (!string || !size)
+ return;
+ if (!*size || !*string)
+ return;
+ for (int i = 0; i < *size; ++i) {
+ delete [] (*string)[i];
+ }
+ delete [] *string;
+ size = 0;
+ *string = 0;
+}
+
+void KyraEngine::loadMouseShapes() {
+ loadBitmap("MOUSE.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+ _shapes[4] = _screen->encodeShape(0, 0, 8, 10, 0);
+ _shapes[5] = _screen->encodeShape(0, 0x17, 0x20, 7, 0);
+ _shapes[6] = _screen->encodeShape(0x50, 0x12, 0x10, 9, 0);
+ _shapes[7] = _screen->encodeShape(0x60, 0x12, 0x10, 11, 0);
+ _shapes[8] = _screen->encodeShape(0x70, 0x12, 0x10, 9, 0);
+ _shapes[9] = _screen->encodeShape(0x80, 0x12, 0x10, 11, 0);
+ _shapes[10] = _screen->encodeShape(0x90, 0x12, 0x10, 10, 0);
+ _shapes[364] = _screen->encodeShape(0x28, 0, 0x10, 13, 0);
+ _screen->setMouseCursor(1, 1, 0);
+ _screen->setMouseCursor(1, 1, _shapes[4]);
+ _screen->setShapePages(5, 3);
+}
+
+void KyraEngine::loadCharacterShapes() {
+ int curImage = 0xFF;
+ int videoPage = _screen->_curPage;
+ _screen->_curPage = 2;
+ for (int i = 0; i < 115; ++i) {
+ assert(i < _defaultShapeTableSize);
+ Shape *shape = &_defaultShapeTable[i];
+ if (shape->imageIndex == 0xFF) {
+ _shapes[i+7+4] = 0;
+ continue;
+ }
+ if (shape->imageIndex != curImage) {
+ assert(shape->imageIndex < _characterImageTableSize);
+ loadBitmap(_characterImageTable[shape->imageIndex], 3, 3, 0);
+ curImage = shape->imageIndex;
+ }
+ _shapes[i+7+4] = _screen->encodeShape(shape->x<<3, shape->y, shape->w<<3, shape->h, 1);
+ }
+ _screen->_curPage = videoPage;
+}
+
+void KyraEngine::loadSpecialEffectShapes() {
+ loadBitmap("EFFECTS.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+
+ int currShape;
+ for (currShape = 173; currShape < 183; currShape++)
+ _shapes[4 + currShape] = _screen->encodeShape((currShape-173) * 24, 0, 24, 24, 1);
+
+ for (currShape = 183; currShape < 190; currShape++)
+ _shapes[4 + currShape] = _screen->encodeShape((currShape-183) * 24, 24, 24, 24, 1);
+
+ for (currShape = 190; currShape < 201; currShape++)
+ _shapes[4 + currShape] = _screen->encodeShape((currShape-190) * 24, 48, 24, 24, 1);
+
+ for (currShape = 201; currShape < 206; currShape++)
+ _shapes[4 + currShape] = _screen->encodeShape((currShape-201) * 16, 106, 16, 16, 1);
+}
+
+void KyraEngine::loadItems() {
+ int shape;
+
+ loadBitmap("JEWELS3.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+
+ _shapes[327] = 0;
+
+ for (shape = 1; shape < 6; shape++ )
+ _shapes[327 + shape] = _screen->encodeShape((shape - 1) * 32, 0, 32, 17, 0);
+
+ for (shape = 330; shape <= 334; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-330) * 32, 102, 32, 17, 0);
+
+ for (shape = 335; shape <= 339; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-335) * 32, 17, 32, 17, 0);
+
+ for (shape = 340; shape <= 344; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-340) * 32, 34, 32, 17, 0);
+
+ for (shape = 345; shape <= 349; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-345) * 32, 51, 32, 17, 0);
+
+ for (shape = 350; shape <= 354; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-350) * 32, 68, 32, 17, 0);
+
+ for (shape = 355; shape <= 359; shape++)
+ _shapes[4 + shape] = _screen->encodeShape((shape-355) * 32, 85, 32, 17, 0);
+
+
+ loadBitmap("ITEMS.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+
+ for (int i = 0; i < 107; i++) {
+ shape = findDuplicateItemShape(i);
+
+ if (shape != -1)
+ _shapes[220 + i] = _shapes[220 + shape];
+ else
+ _shapes[220 + i] = _screen->encodeShape( (i % 20) * 16, i/20 * 16, 16, 16, 0);
+ }
+
+ uint32 size;
+ uint8 *fileData = _res->fileData("_ITEM_HT.DAT", &size);
+ assert(fileData);
+
+ for (int i = 0; i < 107; i++) {
+ _itemTable[i].height = fileData[i];
+ _itemTable[i].unk1 = _itemTable[i].unk2 = 0;
+ }
+
+ delete[] fileData;
+}
+
+void KyraEngine::loadButtonShapes() {
+ loadBitmap("BUTTONS2.CPS", 3, 3, 0);
+ _screen->_curPage = 2;
+ _scrollUpButton.process0PtrShape = _screen->encodeShape(0, 0, 24, 14, 1);
+ _scrollUpButton.process1PtrShape = _screen->encodeShape(24, 0, 24, 14, 1);
+ _scrollUpButton.process2PtrShape = _screen->encodeShape(48, 0, 24, 14, 1);
+ _scrollDownButton.process0PtrShape = _screen->encodeShape(0, 15, 24, 14, 1);
+ _scrollDownButton.process1PtrShape = _screen->encodeShape(24, 15, 24, 14, 1);
+ _scrollDownButton.process2PtrShape = _screen->encodeShape(48, 15, 24, 14, 1);
+ _screen->_curPage = 0;
+}
+
+void KyraEngine::loadMainScreen(int page) {
+ if ((_features & GF_ENGLISH) && (_features & GF_TALKIE))
+ loadBitmap("MAIN_ENG.CPS", page, page, 0);
+ else if(_features & GF_FRENCH)
+ loadBitmap("MAIN_FRE.CPS", page, page, 0);
+ else if(_features & GF_GERMAN)
+ loadBitmap("MAIN_GER.CPS", page, page, 0);
+ else if ((_features & GF_ENGLISH) && (_features & GF_FLOPPY))
+ loadBitmap("MAIN15.CPS", page, page, 0);
+ else if (_features & GF_SPANISH)
+ loadBitmap("MAIN_SPA.CPS", page, page, 0);
+ else
+ warning("no main graphics file found");
+
+ uint8 *_pageSrc = _screen->getPagePtr(page);
+ uint8 *_pageDst = _screen->getPagePtr(0);
+ memcpy(_pageDst, _pageSrc, 320*200);
+}
+
+const ScreenDim Screen::_screenDimTable[] = {
+ { 0x00, 0x00, 0x28, 0xC8, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x08, 0x48, 0x18, 0x38, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x01, 0x08, 0x26, 0x80, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x00, 0xC2, 0x28, 0x06, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x00, 0x90, 0x28, 0x38, 0x04, 0x0C, 0x00, 0x00 },
+ { 0x01, 0x94, 0x26, 0x30, 0x04, 0x1B, 0x00, 0x00 },
+ { 0x00, 0x90, 0x28, 0x38, 0x0F, 0x0D, 0x00, 0x00 },
+ { 0x01, 0x96, 0x26, 0x32, 0x0F, 0x0D, 0x00, 0x00 },
+ { 0x00, 0x00, 0x28, 0x88, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x01, 0x20, 0x26, 0x80, 0x0F, 0x0C, 0x00, 0x00 },
+ { 0x03, 0x28, 0x22, 0x46, 0x0F, 0x0D, 0x00, 0x00 }
+};
+
+const int Screen::_screenDimTableCount = ARRAYSIZE(_screenDimTable);
+
+// CD Version *could* use an different opcodeTable
+#define Opcode(x) &KyraEngine::x
+KyraEngine::OpcodeProc KyraEngine::_opcodeTable[] = {
+ // 0x00
+ Opcode(cmd_magicInMouseItem),
+ Opcode(cmd_characterSays),
+ Opcode(cmd_pauseTicks),
+ Opcode(cmd_drawSceneAnimShape),
+ // 0x04
+ Opcode(cmd_queryGameFlag),
+ Opcode(cmd_setGameFlag),
+ Opcode(cmd_resetGameFlag),
+ Opcode(cmd_runNPCScript),
+ // 0x08
+ Opcode(cmd_setSpecialExitList),
+ Opcode(cmd_blockInWalkableRegion),
+ Opcode(cmd_blockOutWalkableRegion),
+ Opcode(cmd_walkPlayerToPoint),
+ // 0x0c
+ Opcode(cmd_dropItemInScene),
+ Opcode(cmd_drawAnimShapeIntoScene),
+ Opcode(cmd_createMouseItem),
+ Opcode(cmd_savePageToDisk),
+ // 0x10
+ Opcode(cmd_sceneAnimOn),
+ Opcode(cmd_sceneAnimOff),
+ Opcode(cmd_getElapsedSeconds),
+ Opcode(cmd_mouseIsPointer),
+ // 0x14
+ Opcode(cmd_destroyMouseItem),
+ Opcode(cmd_runSceneAnimUntilDone),
+ Opcode(cmd_fadeSpecialPalette),
+ Opcode(cmd_playAdlibSound),
+ // 0x18
+ Opcode(cmd_playAdlibScore),
+ Opcode(cmd_phaseInSameScene),
+ Opcode(cmd_setScenePhasingFlag),
+ Opcode(cmd_resetScenePhasingFlag),
+ // 0x1c
+ Opcode(cmd_queryScenePhasingFlag),
+ Opcode(cmd_sceneToDirection),
+ Opcode(cmd_setBirthstoneGem),
+ Opcode(cmd_placeItemInGenericMapScene),
+ // 0x20
+ Opcode(cmd_setBrandonStatusBit),
+ Opcode(cmd_pauseSeconds),
+ Opcode(cmd_getCharactersLocation),
+ Opcode(cmd_runNPCSubscript),
+ // 0x24
+ Opcode(cmd_magicOutMouseItem),
+ Opcode(cmd_internalAnimOn),
+ Opcode(cmd_forceBrandonToNormal),
+ Opcode(cmd_poisonDeathNow),
+ // 0x28
+ Opcode(cmd_setScaleMode),
+ Opcode(cmd_openWSAFile),
+ Opcode(cmd_closeWSAFile),
+ Opcode(cmd_runWSAFromBeginningToEnd),
+ // 0x2c
+ Opcode(cmd_displayWSAFrame),
+ Opcode(cmd_enterNewScene),
+ Opcode(cmd_setSpecialEnterXAndY),
+ Opcode(cmd_runWSAFrames),
+ // 0x30
+ Opcode(cmd_popBrandonIntoScene),
+ Opcode(cmd_restoreAllObjectBackgrounds),
+ Opcode(cmd_setCustomPaletteRange),
+ Opcode(cmd_loadPageFromDisk),
+ // 0x34
+ Opcode(cmd_customPrintTalkString),
+ Opcode(cmd_restoreCustomPrintBackground),
+ Opcode(cmd_hideMouse),
+ Opcode(cmd_showMouse),
+ // 0x38
+ Opcode(cmd_getCharacterX),
+ Opcode(cmd_getCharacterY),
+ Opcode(cmd_changeCharactersFacing),
+ Opcode(cmd_copyWSARegion),
+ // 0x3c
+ Opcode(cmd_printText),
+ Opcode(cmd_random),
+ Opcode(cmd_loadSoundFile),
+ Opcode(cmd_displayWSAFrameOnHidPage),
+ // 0x40
+ Opcode(cmd_displayWSASequentialFrames),
+ Opcode(cmd_drawCharacterStanding),
+ Opcode(cmd_internalAnimOff),
+ Opcode(cmd_changeCharactersXAndY),
+ // 0x44
+ Opcode(cmd_clearSceneAnimatorBeacon),
+ Opcode(cmd_querySceneAnimatorBeacon),
+ Opcode(cmd_refreshSceneAnimator),
+ Opcode(cmd_placeItemInOffScene),
+ // 0x48
+ Opcode(cmd_wipeDownMouseItem),
+ Opcode(cmd_placeCharacterInOtherScene),
+ Opcode(cmd_getKey),
+ Opcode(cmd_specificItemInInventory),
+ // 0x4c
+ Opcode(cmd_popMobileNPCIntoScene),
+ Opcode(cmd_mobileCharacterInScene),
+ Opcode(cmd_hideMobileCharacter),
+ Opcode(cmd_unhideMobileCharacter),
+ // 0x50
+ Opcode(cmd_setCharactersLocation),
+ Opcode(cmd_walkCharacterToPoint),
+ Opcode(cmd_specialEventDisplayBrynnsNote),
+ Opcode(cmd_specialEventRemoveBrynnsNote),
+ // 0x54
+ Opcode(cmd_setLogicPage),
+ Opcode(cmd_fatPrint),
+ Opcode(cmd_preserveAllObjectBackgrounds),
+ Opcode(cmd_updateSceneAnimations),
+ // 0x58
+ Opcode(cmd_sceneAnimationActive),
+ Opcode(cmd_setCharactersMovementDelay),
+ Opcode(cmd_getCharactersFacing),
+ Opcode(cmd_bkgdScrollSceneAndMasksRight),
+ // 0x5c
+ Opcode(cmd_dispelMagicAnimation),
+ Opcode(cmd_findBrightestFireberry),
+ Opcode(cmd_setFireberryGlowPalette),
+ Opcode(cmd_setDeathHandlerFlag),
+ // 0x60
+ Opcode(cmd_drinkPotionAnimation),
+ Opcode(cmd_makeAmuletAppear),
+ Opcode(cmd_drawItemShapeIntoScene),
+ Opcode(cmd_setCharactersCurrentFrame),
+ // 0x64
+ Opcode(cmd_waitForConfirmationMouseClick),
+ Opcode(cmd_pageFlip),
+ Opcode(cmd_setSceneFile),
+ Opcode(cmd_getItemInMarbleVase),
+ // 0x68
+ Opcode(cmd_setItemInMarbleVase),
+ Opcode(cmd_addItemToInventory),
+ Opcode(cmd_intPrint),
+ Opcode(cmd_shakeScreen),
+ // 0x6c
+ Opcode(cmd_createAmuletJewel),
+ Opcode(cmd_setSceneAnimCurrXY),
+ Opcode(cmd_poisonBrandonAndRemaps),
+ Opcode(cmd_fillFlaskWithWater),
+ // 0x70
+ Opcode(cmd_getCharactersMovementDelay),
+ Opcode(cmd_getBirthstoneGem),
+ Opcode(cmd_queryBrandonStatusBit),
+ Opcode(cmd_playFluteAnimation),
+ // 0x74
+ Opcode(cmd_playWinterScrollSequence),
+ Opcode(cmd_getIdolGem),
+ Opcode(cmd_setIdolGem),
+ Opcode(cmd_totalItemsInScene),
+ // 0x78
+ Opcode(cmd_restoreBrandonsMovementDelay),
+ Opcode(cmd_setMousePos),
+ Opcode(cmd_getMouseState),
+ Opcode(cmd_setEntranceMouseCursorTrack),
+ // 0x7c
+ Opcode(cmd_itemAppearsOnGround),
+ Opcode(cmd_setNoDrawShapesFlag),
+ Opcode(cmd_fadeEntirePalette),
+ Opcode(cmd_itemOnGroundHere),
+ // 0x80
+ Opcode(cmd_queryCauldronState),
+ Opcode(cmd_setCauldronState),
+ Opcode(cmd_queryCrystalState),
+ Opcode(cmd_setCrystalState),
+ // 0x84
+ Opcode(cmd_setPaletteRange),
+ Opcode(cmd_shrinkBrandonDown),
+ Opcode(cmd_growBrandonUp),
+ Opcode(cmd_setBrandonScaleXAndY),
+ // 0x88
+ Opcode(cmd_resetScaleMode),
+ Opcode(cmd_getScaleDepthTableValue),
+ Opcode(cmd_setScaleDepthTableValue),
+ Opcode(cmd_message),
+ // 0x8c
+ Opcode(cmd_checkClickOnNPC),
+ Opcode(cmd_getFoyerItem),
+ Opcode(cmd_setFoyerItem),
+ Opcode(cmd_setNoItemDropRegion),
+ // 0x90
+ Opcode(cmd_walkMalcolmOn),
+ Opcode(cmd_passiveProtection),
+ Opcode(cmd_setPlayingLoop),
+ Opcode(cmd_brandonToStoneSequence),
+ // 0x94
+ Opcode(cmd_brandonHealingSequence),
+ Opcode(cmd_protectCommandLine),
+ Opcode(cmd_pauseMusicSeconds),
+ Opcode(cmd_resetMaskRegion),
+ // 0x98
+ Opcode(cmd_setPaletteChangeFlag),
+ Opcode(cmd_fillRect),
+ Opcode(cmd_vocUnload),
+ Opcode(cmd_vocLoad),
+ Opcode(cmd_dummy)
+};
+#undef Opcode
+
+const int KyraEngine::_opcodeTableSize = ARRAYSIZE(_opcodeTable);
+
+const char *KyraEngine::_xmidiFiles[] = {
+ "INTRO.XMI",
+ "KYRA1A.XMI",
+ "KYRA1B.XMI",
+ "KYRA2A.XMI",
+ "KYRA3A.XMI",
+ "KYRA4A.XMI",
+ "KYRA4B.XMI",
+ "KYRA5A.XMI",
+ "KYRA5B.XMI",
+ "KYRAMISC.XMI"
+};
+
+const int KyraEngine::_xmidiFilesCount = ARRAYSIZE(_xmidiFiles);
+
+const int8 KyraEngine::_charXPosTable[] = {
+ 0, 4, 4, 4, 0, -4, -4, -4
+};
+
+const int8 KyraEngine::_addXPosTable[] = {
+ 4, 4, 0, -4, -4, -4, 0, 4
+};
+
+const int8 KyraEngine::_charYPosTable[] = {
+ -2, -2, 0, 2, 2, 2, 0, -2
+};
+
+const int8 KyraEngine::_addYPosTable[] = {
+ 0, -2, -2, -2, 0, 2, 2, 2
+};
+
+const uint16 KyraEngine::_itemPosX[] = {
+ 95, 115, 135, 155, 175, 95, 115, 135, 155, 175
+};
+
+const uint8 KyraEngine::_itemPosY[] = {
+ 160, 160, 160, 160, 160, 181, 181, 181, 181, 181
+};
+
+Button KyraEngine::_buttonData[] = {
+ { 0, 0x02, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x05D, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x01, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x009, 0xA4, 0x36, 0x1E, /*XXX,*/ 0, &KyraEngine::buttonMenuCallback/*, XXX*/ },
+ { 0, 0x03, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x071, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x04, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x085, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x05, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x099, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x06, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x0AD, 0x9E, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x07, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x05D, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x08, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x071, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x09, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x085, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x0A, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x099, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x0B, /*XXX,*/0, 0, 0, /*XXX,*/ 0x0400, 0, 0, 0, 0, 0, 0, 0, 0x0AD, 0xB3, 0x13, 0x14, /*XXX,*/ 0, &KyraEngine::buttonInventoryCallback/*, XXX*/ },
+ { 0, 0x15, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x0FD, 0x9C, 0x1A, 0x12, /*XXX,*/ 0, &KyraEngine::buttonAmuletCallback/*, XXX*/ },
+ { 0, 0x16, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x0E7, 0xAA, 0x1A, 0x12, /*XXX,*/ 0, &KyraEngine::buttonAmuletCallback/*, XXX*/ },
+ { 0, 0x17, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x0FD, 0xB5, 0x1A, 0x12, /*XXX,*/ 0, &KyraEngine::buttonAmuletCallback/*, XXX*/ },
+ { 0, 0x18, /*XXX,*/1, 1, 1, /*XXX,*/ 0x0487, 0, 0, 0, 0, 0, 0, 0, 0x113, 0xAA, 0x1A, 0x12, /*XXX,*/ 0, &KyraEngine::buttonAmuletCallback/*, XXX*/ }
+};
+
+Button *KyraEngine::_buttonDataListPtr[] = {
+ &_buttonData[1],
+ &_buttonData[2],
+ &_buttonData[3],
+ &_buttonData[4],
+ &_buttonData[5],
+ &_buttonData[6],
+ &_buttonData[7],
+ &_buttonData[8],
+ &_buttonData[9],
+ &_buttonData[10],
+ &_buttonData[11],
+ &_buttonData[12],
+ &_buttonData[13],
+ &_buttonData[14],
+ 0
+};
+
+Button KyraEngine::_scrollUpButton = {0, 0x12, 1, 1, 1, 0x483, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x18, 0x0f, 0, 0};
+Button KyraEngine::_scrollDownButton = {0, 0x13, 1, 1, 1, 0x483, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x18, 0x0f, 0, 0};
+
+
+
+Button KyraEngine::_menuButtonData[] = {
+ { 0, 0x0c, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x0d, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x0e, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x0f, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x10, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ },
+ { 0, 0x11, /*XXX,*/1, 1, 1, /*XXX,*/ 0x487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /*XXX,*/ 0, 0 /*, XXX*/ }
+};
+
+Menu KyraEngine::_menu[] = {
+ { -1, -1, 208, 136, 248, 249, 250, "The Legend of Kyrandia", 251, -1, 8, 0, 5, -1, -1, -1, -1,
+ {
+ {1, 0, 0, "Load a Game", -1, -1, 30, 148, 15, 252, 253, 24, 0,
+ 248, 249, 250, &KyraEngine::gui_loadGameMenu, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Save this Game", -1, -1, 47, 148, 15, 252, 253, 24, 0,
+ 248, 249, 250, &KyraEngine::gui_saveGameMenu, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Game Controls", -1, -1, 64, 148, 15, 252, 253, 24, 0,
+ 248, 249, 250, /*&menu_gameControls*/ 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Quit playing", -1, -1, 81, 148, 15, 252, 253, 24, 0,
+ 248, 249, 250, &KyraEngine::gui_quitPlaying, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Resume game", 86, 0, 110, 92, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_resumeGame, -1, 0, 0, 0, 0, 0}
+ }
+ },
+ { -1, -1, 288, 56, 248, 249, 250, 0, 254,-1, 8, 0, 2, -1, -1, -1, -1,
+ {
+ {1, 0, 0, "Yes", 24, 0, 30, 72, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_quitConfirmYes, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "No", 192, 0, 30, 72, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_quitConfirmNo, -1, 0, 0, 0, 0, 0}
+ }
+ },
+ { -1, -1, 288, 160, 248, 249, 250, 0, 251, -1, 8, 0, 6, 132, 22, 132, 124,
+ {
+ {1, 0, 0, 0, -1, 255, 39, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, 0, -1, 255, 56, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, 0, -1, 255, 73, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, 0, -1, 255, 90, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, 0, -1, 255, 107, 256, 15, 252, 253, 5, 0,
+ 248, 249, 250, 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Cancel", 184, 0, 134, 88, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_cancelSubMenu, -1, 0, 0, 0, 0, 0},
+ }
+ },
+ { -1, -1, 288, 67, 248, 249, 250, "Enter a description of your saved game:", 251, -1, 8, 0, 3, -1, -1, -1, -1,
+ {
+ {1, 0, 0, "Save", 24, 0, 44, 72, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_savegameConfirm, -1, 0, 0, 0, 0, 0},
+ // {1, 0, 0, "Cancel", 110, 0, 44, 72, 15, 252, 253, -1, 255,
+ // 248, 249, 250, /*&menu_cancelconfirmsave*/ 0, -1, 0, 0, 0, 0, 0},
+ {1, 0, 0, "Cancel", 192, 0, 44, 72, 15, 252, 253, -1, 255,
+ 248, 249, 250, &KyraEngine::gui_cancelSubMenu, -1, 0, 0, 0, 0, 0}
+ }
+ }
+};
+
+const uint8 KyraEngine::_magicMouseItemStartFrame[] = {
+ 0xAD, 0xB7, 0xBE, 0x00
+};
+
+const uint8 KyraEngine::_magicMouseItemEndFrame[] = {
+ 0xB1, 0xB9, 0xC2, 0x00
+};
+
+const uint8 KyraEngine::_magicMouseItemStartFrame2[] = {
+ 0xB2, 0xBA, 0xC3, 0x00
+};
+
+const uint8 KyraEngine::_magicMouseItemEndFrame2[] = {
+ 0xB6, 0xBD, 0xC8, 0x00
+};
+
+const uint16 KyraEngine::_amuletX[] = { 231, 275, 253, 253 };
+const uint16 KyraEngine::_amuletY[] = { 170, 170, 159, 181 };
+
+const uint16 KyraEngine::_amuletX2[] = { 0x000, 0x0FD, 0x0E7, 0x0FD, 0x113, 0x000 };
+const uint16 KyraEngine::_amuletY2[] = { 0x000, 0x09F, 0x0AA, 0x0B5, 0x0AA, 0x000 };
+} // End of namespace Kyra
diff --git a/engines/kyra/text.cpp b/engines/kyra/text.cpp
new file mode 100644
index 0000000000..2877687682
--- /dev/null
+++ b/engines/kyra/text.cpp
@@ -0,0 +1,574 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/text.h"
+#include "kyra/animator.h"
+#include "kyra/sprites.h"
+
+#include "common/system.h"
+
+namespace Kyra {
+
+void KyraEngine::waitForChatToFinish(int16 chatDuration, char *chatStr, uint8 charNum) {
+ debug(9, "KyraEngine::waitForChatToFinish(%i, %s, %i)", chatDuration, chatStr, charNum);
+ bool hasUpdatedNPCs = false;
+ bool runLoop = true;
+ uint8 currPage;
+ OSystem::Event event;
+ int16 delayTime;
+
+ //while( towns_isEscKeyPressed() )
+ //towns_getKey();
+
+ uint32 timeToEnd = strlen(chatStr) * 8 * _tickLength + _system->getMillis();
+
+ if (chatDuration != -1 ) {
+ switch (_configTalkspeed) {
+ case 0: chatDuration *= 2;
+ break;
+ case 2: chatDuration /= 4;
+ break;
+ case 3: chatDuration = -1;
+ }
+ }
+
+ if (chatDuration != -1)
+ chatDuration *= _tickLength;
+
+ disableTimer(14);
+ disableTimer(18);
+ disableTimer(19);
+
+ uint32 timeAtStart = _system->getMillis();
+ uint32 loopStart;
+ while (runLoop) {
+ loopStart = _system->getMillis();
+ if (_currentCharacter->sceneId == 210)
+ if (seq_playEnd())
+ break;
+
+ if (_system->getMillis() > timeToEnd && !hasUpdatedNPCs) {
+ hasUpdatedNPCs = true;
+ disableTimer(15);
+ _currHeadShape = 4;
+ _animator->animRefreshNPC(0);
+ _animator->animRefreshNPC(_talkingCharNum);
+
+ if (_charSayUnk2 != -1) {
+ _animator->sprites()[_charSayUnk2].active = 0;
+ _sprites->_anims[_charSayUnk2].play = false;
+ _charSayUnk2 = -1;
+ }
+ }
+
+ updateGameTimers();
+ _sprites->updateSceneAnims();
+ _animator->restoreAllObjectBackgrounds();
+ _animator->preserveAnyChangedBackgrounds();
+ _animator->prepDrawAllObjects();
+
+ currPage = _screen->_curPage;
+ _screen->_curPage = 2;
+ _text->printCharacterText(chatStr, charNum, _characterList[charNum].x1);
+ _animator->_updateScreen = true;
+ _screen->_curPage = currPage;
+
+ _animator->copyChangedObjectsForward(0);
+ updateTextFade();
+
+ if ((chatDuration < (int16)(_system->getMillis() - timeAtStart)) && chatDuration != -1)
+ break;
+
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.keycode == '.')
+ _skipFlag = true;
+ break;
+ case OSystem::EVENT_QUIT:
+ quitGame();
+ case OSystem::EVENT_LBUTTONDOWN:
+ runLoop = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (_skipFlag)
+ runLoop = false;
+
+ delayTime = (loopStart + _gameSpeed) - _system->getMillis();
+ if (delayTime > 0)
+ _system->delayMillis(delayTime);
+ }
+
+ enableTimer(14);
+ enableTimer(15);
+ enableTimer(18);
+ enableTimer(19);
+ //clearKyrandiaButtonIO();
+}
+
+void KyraEngine::endCharacterChat(int8 charNum, int16 convoInitialized) {
+ _charSayUnk3 = -1;
+
+ if (charNum > 4 && charNum < 11) {
+ //TODO: weird _game_inventory stuff here
+ warning("STUB: endCharacterChat() for high charnums");
+ }
+
+ if (convoInitialized != 0) {
+ _talkingCharNum = -1;
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ }
+}
+
+void KyraEngine::restoreChatPartnerAnimFrame(int8 charNum) {
+ _talkingCharNum = -1;
+
+ if (charNum > 0 && charNum < 5) {
+ _characterList[charNum].currentAnimFrame = _currentChatPartnerBackupFrame;
+ _animator->animRefreshNPC(charNum);
+ }
+
+ _currentCharacter->currentAnimFrame = 7;
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+}
+
+void KyraEngine::backupChatPartnerAnimFrame(int8 charNum) {
+ _talkingCharNum = 0;
+
+ if (charNum < 5 && charNum > 0)
+ _currentChatPartnerBackupFrame = _characterList[charNum].currentAnimFrame;
+
+ if (_scaleMode != 0)
+ _currentCharacter->currentAnimFrame = 7;
+ else
+ _currentCharacter->currentAnimFrame = _currentCharAnimFrame;
+
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+}
+
+int8 KyraEngine::getChatPartnerNum() {
+ uint8 sceneTable[] = {0x2, 0x5, 0x2D, 0x7, 0x1B, 0x8, 0x22, 0x9, 0x30, 0x0A};
+ int pos = 0;
+ int partner = -1;
+
+ for (int i = 1; i < 6; i++) {
+ if (_currentCharacter->sceneId == sceneTable[pos]) {
+ partner = sceneTable[pos+1];
+ break;
+ }
+ pos += 2;
+ }
+
+ for (int i = 1; i < 5; i++) {
+ if (_characterList[i].sceneId == _currentCharacter->sceneId) {
+ partner = i;
+ break;
+ }
+ }
+ return partner;
+}
+
+int KyraEngine::initCharacterChat(int8 charNum) {
+ if (_talkingCharNum == -1) {
+ _talkingCharNum = 0;
+
+ if (_scaleMode != 0)
+ _currentCharacter->currentAnimFrame = 7;
+ else
+ _currentCharacter->currentAnimFrame = 16;
+
+ _animator->animRefreshNPC(0);
+ _animator->updateAllObjectShapes();
+ }
+
+ _charSayUnk2 = -1;
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->restoreAllObjectBackgrounds();
+
+ if (charNum > 4 && charNum < 11) {
+ // TODO: Fill in weird _game_inventory stuff here
+ warning("STUB: initCharacterChat() for high charnums");
+ }
+
+ _animator->flagAllObjectsForRefresh();
+ _animator->flagAllObjectsForBkgdChange();
+ _animator->preserveAnyChangedBackgrounds();
+ _charSayUnk3 = charNum;
+
+ return 1;
+}
+
+void KyraEngine::characterSays(char *chatStr, int8 charNum, int8 chatDuration) {
+ debug(9, "KyraEngine::characterSays('%s', %i, %d)", chatStr, charNum, chatDuration);
+ uint8 startAnimFrames[] = { 0x10, 0x32, 0x56, 0x0, 0x0, 0x0 };
+
+ uint16 chatTicks;
+ int16 convoInitialized;
+ int8 chatPartnerNum;
+
+ if (_currentCharacter->sceneId == 210)
+ return;
+
+ convoInitialized = initCharacterChat(charNum);
+ chatPartnerNum = getChatPartnerNum();
+
+ if (chatPartnerNum != -1 && chatPartnerNum < 5)
+ backupChatPartnerAnimFrame(chatPartnerNum);
+
+ if (charNum < 5) {
+ _characterList[charNum].currentAnimFrame = startAnimFrames[charNum];
+ _charSayUnk3 = charNum;
+ _talkingCharNum = charNum;
+ _animator->animRefreshNPC(charNum);
+ }
+
+ char *processedString = _text->preprocessString(chatStr);
+ int lineNum = _text->buildMessageSubstrings(processedString);
+
+ int16 yPos = _characterList[charNum].y1;
+ yPos -= _scaleTable[charNum] * _characterList[charNum].height;
+ yPos -= 8;
+ yPos -= lineNum * 10;
+
+ if (yPos < 11)
+ yPos = 11;
+
+ if (yPos > 100)
+ yPos = 100;
+
+ _text->_talkMessageY = yPos;
+ _text->_talkMessageH = lineNum * 10;
+ _animator->restoreAllObjectBackgrounds();
+
+ _screen->copyRegion(12, _text->_talkMessageY, 12, 136, 296, _text->_talkMessageH, 2, 2);
+ _screen->hideMouse();
+
+ _text->printCharacterText(processedString, charNum, _characterList[charNum].x1);
+ _screen->showMouse();
+
+ if (chatDuration == -2)
+ chatTicks = strlen(processedString) * 9;
+ else
+ chatTicks = chatDuration;
+
+ waitForChatToFinish(chatTicks, chatStr, charNum);
+
+ _animator->restoreAllObjectBackgrounds();
+
+ _screen->copyRegion(12, 136, 12, _text->_talkMessageY, 296, _text->_talkMessageH, 2, 2);
+ _animator->preserveAllBackgrounds();
+ _animator->prepDrawAllObjects();
+ _screen->hideMouse();
+
+ _screen->copyRegion(12, _text->_talkMessageY, 12, _text->_talkMessageY, 296, _text->_talkMessageH, 2, 0);
+ _screen->showMouse();
+ _animator->flagAllObjectsForRefresh();
+ _animator->copyChangedObjectsForward(0);
+
+ if (chatPartnerNum != -1 && chatPartnerNum < 5)
+ restoreChatPartnerAnimFrame(chatPartnerNum);
+
+ endCharacterChat(charNum, convoInitialized);
+}
+
+void KyraEngine::drawSentenceCommand(char *sentence, int color) {
+ debug(9, "KyraEngine::drawSentenceCommand('%s', %i)", sentence, color);
+ _screen->hideMouse();
+ _screen->fillRect(8, 143, 311, 152, 12);
+
+ if (_startSentencePalIndex != color || _fadeText != false) {
+ _currSentenceColor[0] = _screen->_currentPalette[765] = _screen->_currentPalette[color*3];
+ _currSentenceColor[1] = _screen->_currentPalette[766] = _screen->_currentPalette[color*3+1];
+ _currSentenceColor[2] = _screen->_currentPalette[767] = _screen->_currentPalette[color*3+2];
+
+ _screen->setScreenPalette(_screen->_currentPalette);
+ _startSentencePalIndex = 0;
+ }
+
+ _text->printText(sentence, 8, 143, 0xFF, 12, 0);
+ _screen->showMouse();
+ setTextFadeTimerCountdown(15);
+ _fadeText = false;
+}
+
+void KyraEngine::updateSentenceCommand(char *str1, char *str2, int color) {
+ debug(9, "KyraEngine::updateSentenceCommand('%s', '%s', %i)", str1, str2, color);
+ char sentenceCommand[500];
+ strncpy(sentenceCommand, str1, 500);
+ if (str2)
+ strncat(sentenceCommand, str2, 500 - strlen(sentenceCommand));
+
+ drawSentenceCommand(sentenceCommand, color);
+ _screen->updateScreen();
+}
+
+void KyraEngine::updateTextFade() {
+ debug(9, "KyraEngine::updateTextFade()");
+ if (!_fadeText)
+ return;
+
+ bool finished = false;
+ for (int i = 0; i < 3; i++)
+ if (_currSentenceColor[i] > 4)
+ _currSentenceColor[i] -= 4;
+ else
+ if (_currSentenceColor[i]) {
+ _currSentenceColor[i] = 0;
+ finished = true;
+ }
+
+ _screen->_currentPalette[765] = _currSentenceColor[0];
+ _screen->_currentPalette[766] = _currSentenceColor[1];
+ _screen->_currentPalette[767] = _currSentenceColor[2];
+ _screen->setScreenPalette(_screen->_currentPalette);
+
+ if (finished) {
+ _fadeText = false;
+ _startSentencePalIndex = -1;
+ }
+}
+
+TextDisplayer::TextDisplayer(Screen *screen) {
+ _screen = screen;
+
+ _talkCoords.y = 0x88;
+ _talkCoords.x = 0;
+ _talkCoords.w = 0;
+ _talkMessageY = 0xC;
+ _talkMessageH = 0;
+ _talkMessagePrinted = false;
+}
+
+void TextDisplayer::setTalkCoords(uint16 y) {
+ debug(9, "TextDisplayer::setTalkCoords(%d)", y);
+ _talkCoords.y = y;
+}
+
+int TextDisplayer::getCenterStringX(const char *str, int x1, int x2) {
+ debug(9, "TextDisplayer::getCenterStringX('%s', %d, %d)", str, x1, x2);
+ _screen->_charWidth = -2;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ int strWidth = _screen->getTextWidth(str);
+ _screen->setFont(curFont);
+ _screen->_charWidth = 0;
+ int w = x2 - x1 + 1;
+ return x1 + (w - strWidth) / 2;
+}
+
+int TextDisplayer::getCharLength(const char *str, int len) {
+ debug(9, "TextDisplayer::getCharLength('%s', %d)", str, len);
+ int charsCount = 0;
+ if (*str) {
+ _screen->_charWidth = -2;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ int i = 0;
+ while (i <= len && *str) {
+ i += _screen->getCharWidth(*str++);
+ ++charsCount;
+ }
+ _screen->setFont(curFont);
+ _screen->_charWidth = 0;
+ }
+ return charsCount;
+}
+
+int TextDisplayer::dropCRIntoString(char *str, int offs) {
+ debug(9, "TextDisplayer::dropCRIntoString('%s', %d)", str, offs);
+ int pos = 0;
+ str += offs;
+ while (*str) {
+ if (*str == ' ') {
+ *str = '\r';
+ return pos;
+ }
+ ++str;
+ ++pos;
+ }
+ return 0;
+}
+
+char *TextDisplayer::preprocessString(const char *str) {
+ debug(9, "TextDisplayer::preprocessString('%s')", str);
+ assert(strlen(str) < sizeof(_talkBuffer) - 1);
+ strcpy(_talkBuffer, str);
+ char *p = _talkBuffer;
+ while (*p) {
+ if (*p == '\r') {
+ return _talkBuffer;
+ }
+ ++p;
+ }
+ p = _talkBuffer;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+ int textWidth = _screen->getTextWidth(p);
+ _screen->_charWidth = 0;
+ if (textWidth > 176) {
+ if (textWidth > 352) {
+ int count = getCharLength(p, textWidth / 3);
+ int offs = dropCRIntoString(p, count);
+ p += count + offs;
+ _screen->_charWidth = -2;
+ textWidth = _screen->getTextWidth(p);
+ _screen->_charWidth = 0;
+ count = getCharLength(p, textWidth / 2);
+ dropCRIntoString(p, count);
+ } else {
+ int count = getCharLength(p, textWidth / 2);
+ dropCRIntoString(p, count);
+ }
+ }
+ _screen->setFont(curFont);
+ return _talkBuffer;
+}
+
+int TextDisplayer::buildMessageSubstrings(const char *str) {
+ debug(9, "TextDisplayer::buildMessageSubstrings('%s')", str);
+ int currentLine = 0;
+ int pos = 0;
+ while (*str) {
+ if (*str == '\r') {
+ assert(currentLine < TALK_SUBSTRING_NUM);
+ _talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = '\0';
+ ++currentLine;
+ pos = 0;
+ } else {
+ _talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = *str;
+ ++pos;
+ if (pos > TALK_SUBSTRING_LEN - 2) {
+ pos = TALK_SUBSTRING_LEN - 2;
+ }
+ }
+ ++str;
+ }
+ _talkSubstrings[currentLine * TALK_SUBSTRING_LEN + pos] = '\0';
+ return currentLine + 1;
+}
+
+int TextDisplayer::getWidestLineWidth(int linesCount) {
+ debug(9, "TextDisplayer::getWidestLineWidth(%d)", linesCount);
+ int maxWidth = 0;
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+ for (int l = 0; l < linesCount; ++l) {
+ int w = _screen->getTextWidth(&_talkSubstrings[l * TALK_SUBSTRING_LEN]);
+ if (maxWidth < w) {
+ maxWidth = w;
+ }
+ }
+ _screen->setFont(curFont);
+ _screen->_charWidth = 0;
+ return maxWidth;
+}
+
+void TextDisplayer::calcWidestLineBounds(int &x1, int &x2, int w, int cx) {
+ debug(9, "TextDisplayer::calcWidestLineBounds(%d, %d)", w, cx);
+ x1 = cx - w / 2;
+ if (x1 + w >= Screen::SCREEN_W - 12) {
+ x1 = Screen::SCREEN_W - 12 - w - 1;
+ } else if (x1 < 12) {
+ x1 = 12;
+ }
+ x2 = x1 + w + 1;
+}
+
+void TextDisplayer::restoreTalkTextMessageBkgd(int srcPage, int dstPage) {
+ debug(9, "TextDisplayer::restoreTalkTextMessageBkgd(%d, %d)", srcPage, dstPage);
+ if (_talkMessagePrinted) {
+ _talkMessagePrinted = false;
+ _screen->copyRegion(_talkCoords.x, _talkCoords.y, _talkCoords.x, _talkMessageY, _talkCoords.w, _talkMessageH, srcPage, dstPage);
+ }
+}
+
+void TextDisplayer::printTalkTextMessage(const char *text, int x, int y, uint8 color, int srcPage, int dstPage) {
+ debug(9, "TextDisplayer::printTalkTextMessage('%s', %d, %d, %d, %d, %d)", text, x, y, color, srcPage, dstPage);
+ char *str = preprocessString(text);
+ int lineCount = buildMessageSubstrings(str);
+ int top = y - lineCount * 10;
+ if (top < 0) {
+ top = 0;
+ }
+ _talkMessageY = top;
+ _talkMessageH = lineCount * 10;
+ int w = getWidestLineWidth(lineCount);
+ int x1, x2;
+ calcWidestLineBounds(x1, x2, w, x);
+ _talkCoords.x = x1;
+ _talkCoords.w = w + 2;
+ _screen->copyRegion(_talkCoords.x, _talkMessageY, _talkCoords.x, _talkCoords.y, _talkCoords.w, _talkMessageH, srcPage, dstPage);
+ int curPage = _screen->_curPage;
+ _screen->_curPage = srcPage;
+ for (int i = 0; i < lineCount; ++i) {
+ top = i * 10 + _talkMessageY;
+ char *msg = &_talkSubstrings[i * TALK_SUBSTRING_LEN];
+ int left = getCenterStringX(msg, x1, x2);
+ printText(msg, left, top, color, 0xC, 0);
+ }
+ _screen->_curPage = curPage;
+ _talkMessagePrinted = true;
+}
+
+void TextDisplayer::printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2) {
+ debug(9, "TextDisplayer::printText('%s', %d, %d, %d, %d, %d)", str, x, y, c0, c1, c2);
+ uint8 colorMap[] = { 0, 15, 12, 12 };
+ colorMap[3] = c1;
+ _screen->setTextColor(colorMap, 0, 3);
+ Screen::FontId curFont = _screen->setFont(Screen::FID_8_FNT);
+ _screen->_charWidth = -2;
+ _screen->printText(str, x, y, c0, c2);
+ _screen->_charWidth = 0;
+ _screen->setFont(curFont);
+}
+
+void TextDisplayer::printCharacterText(char *text, int8 charNum, int charX) {
+ debug(9, "TextDisplayer::printCharacterText('%s', %d, %d)", text, charNum, charX);
+ uint8 colorTable[] = {0x0F, 0x9, 0x0C9, 0x80, 0x5, 0x81, 0x0E, 0xD8, 0x55, 0x3A, 0x3a};
+ int top, left, x1, x2, w, x;
+ char *msg;
+
+ uint8 color = colorTable[charNum];
+ text = preprocessString(text);
+ int lineCount = buildMessageSubstrings(text);
+ w = getWidestLineWidth(lineCount);
+ x = charX;
+ calcWidestLineBounds(x1, x2, w, x);
+
+ for (int i = 0; i < lineCount; ++i) {
+ top = i * 10 + _talkMessageY;
+ msg = &_talkSubstrings[i * TALK_SUBSTRING_LEN];
+ left = getCenterStringX(msg, x1, x2);
+ printText(msg, left, top, color, 0xC, 0);
+ }
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/text.h b/engines/kyra/text.h
new file mode 100644
index 0000000000..f8f7c975a6
--- /dev/null
+++ b/engines/kyra/text.h
@@ -0,0 +1,69 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef KYRATEXT_H
+#define KYRATEXT_H
+
+namespace Kyra {
+class Screen;
+
+class TextDisplayer {
+ struct TalkCoords {
+ uint16 y, x, w;
+ };
+
+ enum {
+ TALK_SUBSTRING_LEN = 80,
+ TALK_SUBSTRING_NUM = 3
+ };
+public:
+ TextDisplayer(Screen *screen);
+ ~TextDisplayer() {}
+
+ void setTalkCoords(uint16 y);
+ int getCenterStringX(const char *str, int x1, int x2);
+ int getCharLength(const char *str, int len);
+ int dropCRIntoString(char *str, int offs);
+ char *preprocessString(const char *str);
+ int buildMessageSubstrings(const char *str);
+ int getWidestLineWidth(int linesCount);
+ void calcWidestLineBounds(int &x1, int &x2, int w, int cx);
+ void restoreTalkTextMessageBkgd(int srcPage, int dstPage);
+ void printTalkTextMessage(const char *text, int x, int y, uint8 color, int srcPage, int dstPage);
+ void printText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 c2);
+ void printCharacterText(char *text, int8 charNum, int charX);
+
+ uint16 _talkMessageY;
+ uint16 _talkMessageH;
+ bool printed() const { return _talkMessagePrinted; }
+private:
+ Screen *_screen;
+
+ char _talkBuffer[300];
+ char _talkSubstrings[TALK_SUBSTRING_LEN * TALK_SUBSTRING_NUM];
+ TalkCoords _talkCoords;
+ bool _talkMessagePrinted;
+};
+} // end of namespace Kyra
+
+#endif
+
diff --git a/engines/kyra/timer.cpp b/engines/kyra/timer.cpp
new file mode 100644
index 0000000000..7abfd9fcbc
--- /dev/null
+++ b/engines/kyra/timer.cpp
@@ -0,0 +1,280 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/animator.h"
+
+#include "common/system.h"
+
+namespace Kyra {
+void KyraEngine::setupTimers() {
+ debug(9, "setupTimers()");
+ memset(_timers, 0, sizeof(_timers));
+
+ for (int i = 0; i < 34; i++)
+ _timers[i].active = 1;
+
+ _timers[0].func = _timers[1].func = _timers[2].func = _timers[3].func = _timers[4].func = 0; //Unused.
+ _timers[5].func = _timers[6].func = _timers[7].func = _timers[8].func = _timers[9].func = 0; //_nullsub51;
+ _timers[10].func = _timers[11].func = _timers[12].func = _timers[13].func = 0; //_nullsub50;
+ _timers[14].func = &KyraEngine::timerCheckAnimFlag2; //_nullsub52;
+ _timers[15].func = &KyraEngine::timerUpdateHeadAnims; //_nullsub48;
+ _timers[16].func = &KyraEngine::timerSetFlags1; //_nullsub47;
+ _timers[17].func = 0; //sub_15120;
+ _timers[18].func = &KyraEngine::timerCheckAnimFlag1; //_nullsub53;
+ _timers[19].func = &KyraEngine::timerRedrawAmulet; //_nullsub54;
+ _timers[20].func = 0; //offset _timerDummy1
+ _timers[21].func = 0; //sub_1517C;
+ _timers[22].func = 0; //offset _timerDummy2
+ _timers[23].func = 0; //offset _timerDummy3,
+ _timers[24].func = 0; //_nullsub45;
+ _timers[25].func = 0; //offset _timerDummy4
+ _timers[26].func = 0; //_nullsub46;
+ _timers[27].func = 0; //offset _timerDummy5,
+ _timers[28].func = 0; //offset _timerDummy6
+ _timers[29].func = 0; //offset _timerDummy7,
+ _timers[30].func = 0; //offset _timerDummy8,
+ _timers[31].func = &KyraEngine::timerFadeText; //sub_151F8;
+ _timers[32].func = &KyraEngine::updateAnimFlag1; //_nullsub61;
+ _timers[33].func = &KyraEngine::updateAnimFlag2; //_nullsub62;
+
+ _timers[0].countdown = _timers[1].countdown = _timers[2].countdown = _timers[3].countdown = _timers[4].countdown = -1;
+ _timers[5].countdown = 5;
+ _timers[6].countdown = 7;
+ _timers[7].countdown = 8;
+ _timers[8].countdown = 9;
+ _timers[9].countdown = 7;
+ _timers[10].countdown = _timers[11].countdown = _timers[12].countdown = _timers[13].countdown = 420;
+ _timers[14].countdown = 600;
+ _timers[15].countdown = 11;
+ _timers[16].countdown = _timers[17].countdown = 7200;
+ _timers[18].countdown = _timers[19].countdown = 600;
+ _timers[20].countdown = 7200;
+ _timers[21].countdown = 18000;
+ _timers[22].countdown = 7200;
+ _timers[23].countdown = _timers[24].countdown = _timers[25].countdown = _timers[26].countdown = _timers[27].countdown = 10800;
+ _timers[28].countdown = 21600;
+ _timers[29].countdown = 7200;
+ _timers[30].countdown = 10800;
+ _timers[31].countdown = -1;
+ _timers[32].countdown = 9;
+ _timers[33].countdown = 3;
+}
+
+void KyraEngine::updateGameTimers() {
+ debug(9, "updateGameTimers()");
+
+ if (_system->getMillis() < _timerNextRun)
+ return;
+
+ _timerNextRun += 99999;
+
+ for (int i = 0; i < 34; i++) {
+ if (_timers[i].active && _timers[i].countdown > -1) {
+ if (_timers[i].nextRun <=_system->getMillis()) {
+ if (i > 4 && _timers[i].func)
+ (*this.*_timers[i].func)(i);
+
+ _timers[i].nextRun = _system->getMillis() + _timers[i].countdown * _tickLength;
+ }
+ }
+ if (_timers[i].nextRun < _timerNextRun)
+ _timerNextRun = _timers[i].nextRun;
+ }
+}
+
+void KyraEngine::clearNextEventTickCount() {
+ debug(9, "clearNextEventTickCount()");
+ _timerNextRun = 0;
+}
+
+void KyraEngine::setTimerDelay(uint8 timer, int32 countdown) {
+ debug(9, "setTimerDelay(%i, %d)", timer, countdown);
+ _timers[timer].countdown = countdown;
+}
+
+int16 KyraEngine::getTimerDelay(uint8 timer) {
+ debug(9, "getTimerDelay(%i)", timer);
+ return _timers[timer].countdown;
+}
+
+void KyraEngine::setTimerCountdown(uint8 timer, int32 countdown) {
+ debug(9, "setTimerCountdown(%i, %i)", timer, countdown);
+ _timers[timer].countdown = countdown;
+ _timers[timer].nextRun = _system->getMillis() + countdown * _tickLength;
+
+ uint32 nextRun = _system->getMillis() + countdown * _tickLength;
+ if (nextRun < _timerNextRun)
+ _timerNextRun = nextRun;
+}
+
+void KyraEngine::enableTimer(uint8 timer) {
+ debug(9, "enableTimer(%i)", timer);
+ _timers[timer].active = 1;
+}
+
+void KyraEngine::disableTimer(uint8 timer) {
+ debug(9, "disableTimer(%i)", timer);
+ _timers[timer].active = 0;
+}
+
+void KyraEngine::timerUpdateHeadAnims(int timerNum) {
+ debug(9, "timerUpdateHeadAnims(%i)", timerNum);
+ static int8 currentFrame = 0;
+ static const int8 frameTable[] = {4, 5, 4, 5, 4, 5, 0, 1, 4, 5,
+ 4, 4, 6, 4, 8, 1, 9, 4, -1};
+
+ if (_talkingCharNum < 0)
+ return;
+
+ _currHeadShape = frameTable[currentFrame];
+ currentFrame++;
+
+ if (frameTable[currentFrame] == -1)
+ currentFrame = 0;
+
+ _animator->animRefreshNPC(0);
+ _animator->animRefreshNPC(_talkingCharNum);
+}
+
+void KyraEngine::timerSetFlags1(int timerNum) {
+ debug(9, "timerSetFlags(%i)", timerNum);
+ if (_currentCharacter->sceneId == 0x1C)
+ return;
+
+ int rndNr = _rnd.getRandomNumberRng(0, 3);
+
+ for (int i = 0; i < 4; i++) {
+ if (!queryGameFlag(rndNr + 17)) {
+ setGameFlag(rndNr + 17);
+ break;
+ } else {
+ rndNr++;
+ if (rndNr > 3)
+ rndNr = 0;
+ }
+ }
+}
+
+void KyraEngine::timerFadeText(int timerNum) {
+ debug(9, "timerFadeText(%i)", timerNum);
+ _fadeText = true;
+}
+
+void KyraEngine::updateAnimFlag1(int timerNum) {
+ debug(9, "updateAnimFlag1(%d)", timerNum);
+ if (_brandonStatusBit & 2) {
+ _brandonStatusBit0x02Flag = 1;
+ }
+}
+
+void KyraEngine::updateAnimFlag2(int timerNum) {
+ debug(9, "updateAnimFlag2(%d)", timerNum);
+ if (_brandonStatusBit & 0x20) {
+ _brandonStatusBit0x20Flag = 1;
+ }
+}
+
+void KyraEngine::setTextFadeTimerCountdown(int16 countdown) {
+ debug(9, "setTextFadeTimerCountdown(%i)", countdown);
+ //if (countdown == -1)
+ //countdown = 32000;
+
+ setTimerCountdown(31, countdown*60);
+}
+
+void KyraEngine::timerSetFlags2(int timerNum) {
+ debug(9, "timerSetFlags2(%i)", timerNum);
+ if (!((uint32*)(_flagsTable+0x2D))[timerNum])
+ ((uint32*)(_flagsTable+0x2D))[timerNum] = 1;
+}
+
+void KyraEngine::timerCheckAnimFlag1(int timerNum) {
+ debug(9, "timerCheckAnimFlag1(%i)", timerNum);
+ if (_brandonStatusBit & 0x20) {
+ checkAmuletAnimFlags();
+ setTimerCountdown(18, -1);
+ }
+}
+
+void KyraEngine::timerCheckAnimFlag2(int timerNum) {
+ debug(9, "timerCheckAnimFlag1(%i)", timerNum);
+ if (_brandonStatusBit & 0x2) {
+ checkAmuletAnimFlags();
+ setTimerCountdown(14, -1);
+ }
+}
+
+void KyraEngine::checkAmuletAnimFlags() {
+ debug(9, "checkSpecialAnimFlags()");
+ if (_brandonStatusBit & 2) {
+ seq_makeBrandonNormal2();
+ setTimerCountdown(19, 300);
+ }
+
+ if (_brandonStatusBit & 0x20) {
+ seq_makeBrandonNormal();
+ setTimerCountdown(19, 300);
+ }
+}
+
+void KyraEngine::timerRedrawAmulet(int timerNum) {
+ debug(9, "timerRedrawAmulet(%i)", timerNum);
+ if (queryGameFlag(0xF1)) {
+ drawAmulet();
+ setTimerCountdown(19, -1);
+ }
+}
+
+void KyraEngine::drawAmulet() {
+ debug(9, "drawAmulet()");
+ static const int16 amuletTable1[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x150, 0x155, 0x15A, 0x15F, 0x164, 0x145, -1};
+ static const int16 amuletTable3[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x14F, 0x154, 0x159, 0x15E, 0x163, 0x144, -1};
+ static const int16 amuletTable2[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x152, 0x157, 0x15C, 0x161, 0x166, 0x147, -1};
+ static const int16 amuletTable4[] = {0x167, 0x162, 0x15D, 0x158, 0x153, 0x151, 0x156, 0x15B, 0x160, 0x165, 0x146, -1};
+
+ resetGameFlag(0xF1);
+ _screen->hideMouse();
+
+ int i = 0;
+ while (amuletTable1[i] != -1) {
+ if (queryGameFlag(87))
+ _screen->drawShape(0, _shapes[4+amuletTable1[i]], _amuletX[0], _amuletY[0], 0, 0);
+
+ if (queryGameFlag(89))
+ _screen->drawShape(0, _shapes[4+amuletTable2[i]], _amuletX[1], _amuletY[1], 0, 0);
+
+ if (queryGameFlag(86))
+ _screen->drawShape(0, _shapes[4+amuletTable3[i]], _amuletX[2], _amuletY[2], 0, 0);
+
+ if (queryGameFlag(88))
+ _screen->drawShape(0, _shapes[4+amuletTable4[i]], _amuletX[3], _amuletY[3], 0, 0);
+
+ _screen->updateScreen();
+ delayWithTicks(3);
+ i++;
+ }
+ _screen->showMouse();
+}
+} // end of namespace Kyra
+
diff --git a/engines/kyra/wsamovie.cpp b/engines/kyra/wsamovie.cpp
new file mode 100644
index 0000000000..17fe3f8cb2
--- /dev/null
+++ b/engines/kyra/wsamovie.cpp
@@ -0,0 +1,206 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "kyra/kyra.h"
+#include "kyra/screen.h"
+#include "kyra/wsamovie.h"
+
+
+namespace Kyra {
+WSAMovieV1::WSAMovieV1(KyraEngine *vm) : Movie(vm) {}
+WSAMovieV1::~WSAMovieV1() { close(); }
+
+void WSAMovieV1::open(const char *filename, int offscreenDecode, uint8 *palBuf) {
+ debug(9, "WSAMovieV1::open('%s', %d, 0x%X)", filename, offscreenDecode, palBuf);
+ close();
+
+ uint32 flags = 0;
+ uint32 fileSize;
+ uint8 *p = _vm->resource()->fileData(filename, &fileSize);
+ if (!p)
+ return;
+
+ const uint8 *wsaData = p;
+ _numFrames = READ_LE_UINT16(wsaData); wsaData += 2;
+ _width = READ_LE_UINT16(wsaData); wsaData += 2;
+ _height = READ_LE_UINT16(wsaData); wsaData += 2;
+ _deltaBufferSize = READ_LE_UINT16(wsaData); wsaData += 2;
+ _offscreenBuffer = NULL;
+ _flags = 0;
+ if (_vm->features() & GF_TALKIE) {
+ flags = READ_LE_UINT16(wsaData); wsaData += 2;
+ }
+
+ uint32 offsPal = 0;
+ if (flags & 1) {
+ offsPal = 0x300;
+ _flags |= WF_HAS_PALETTE;
+ if (palBuf) {
+ memcpy(palBuf, wsaData + (_numFrames + 2) * 4, 0x300);
+ }
+ }
+
+ if (offscreenDecode) {
+ _flags |= WF_OFFSCREEN_DECODE;
+ const int offscreenBufferSize = _width * _height;
+ _offscreenBuffer = new uint8[offscreenBufferSize];
+ memset(_offscreenBuffer, 0, offscreenBufferSize);
+ }
+
+ if (_numFrames & 0x8000) {
+ warning("Unhandled wsa flags 0x80");
+ _flags |= 0x80;
+ _numFrames &= 0x7FFF;
+ }
+ _currentFrame = _numFrames;
+
+ _deltaBuffer = new uint8[_deltaBufferSize];
+ memset(_deltaBuffer, 0, _deltaBufferSize);
+
+ // read frame offsets
+ _frameOffsTable = new uint32[_numFrames + 2];
+ _frameOffsTable[0] = 0;
+ uint32 frameDataOffs = READ_LE_UINT32(wsaData); wsaData += 4;
+ bool firstFrame = true;
+ if (frameDataOffs == 0) {
+ firstFrame = false;
+ frameDataOffs = READ_LE_UINT32(wsaData);
+ _flags |= WF_NO_FIRST_FRAME;
+ }
+ for (int i = 1; i < _numFrames + 2; ++i) {
+ _frameOffsTable[i] = READ_LE_UINT32(wsaData) - frameDataOffs;
+ wsaData += 4;
+ }
+
+ // skip palette
+ wsaData += offsPal;
+
+ // read frame data
+ const int frameDataSize = p + fileSize - wsaData;
+ _frameData = new uint8[frameDataSize];
+ memcpy(_frameData, wsaData, frameDataSize);
+
+ // decode first frame
+ if (firstFrame) {
+ Screen::decodeFrame4(_frameData, _deltaBuffer, _deltaBufferSize);
+ }
+
+ delete [] p;
+ _opened = true;
+}
+
+void WSAMovieV1::close() {
+ debug(9, "WSAMovieV1::close()");
+ if (_opened) {
+ delete [] _deltaBuffer;
+ delete [] _offscreenBuffer;
+ delete [] _frameOffsTable;
+ delete [] _frameData;
+ _opened = false;
+ }
+}
+
+void WSAMovieV1::displayFrame(int frameNum) {
+ debug(9, "WSAMovieV1::displayFrame(%d)", frameNum);
+ if (frameNum >= _numFrames || !_opened)
+ return;
+
+ uint8 *dst;
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ dst = _offscreenBuffer;
+ } else {
+ dst = _vm->screen()->getPagePtr(_drawPage) + _y * Screen::SCREEN_W + _x;
+ }
+
+ if (_currentFrame == _numFrames) {
+ if (!(_flags & WF_NO_FIRST_FRAME)) {
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ Screen::decodeFrameDelta(dst, _deltaBuffer);
+ } else {
+ Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, 1);
+ }
+ }
+ _currentFrame = 0;
+ }
+
+ // try to reduce the number of needed frame operations
+ int diffCount = ABS(_currentFrame - frameNum);
+ int frameStep = 1;
+ int frameCount;
+ if (_currentFrame < frameNum) {
+ frameCount = _numFrames - frameNum + _currentFrame;
+ if (diffCount > frameCount) {
+ frameStep = -1;
+ } else {
+ frameCount = diffCount;
+ }
+ } else {
+ frameCount = _numFrames - _currentFrame + frameNum;
+ if (frameCount >= diffCount) {
+ frameStep = -1;
+ frameCount = diffCount;
+ }
+ }
+
+ // process
+ if (frameStep > 0) {
+ uint16 cf = _currentFrame;
+ while (frameCount--) {
+ cf += frameStep;
+ processFrame(cf, dst);
+ if (cf == _numFrames) {
+ cf = 0;
+ }
+ }
+ } else {
+ uint16 cf = _currentFrame;
+ while (frameCount--) {
+ if (cf == 0) {
+ cf = _numFrames;
+ }
+ processFrame(cf, dst);
+ cf += frameStep;
+ }
+ }
+
+ // display
+ _currentFrame = frameNum;
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ _vm->screen()->copyBlockToPage(_drawPage, _x, _y, _width, _height, _offscreenBuffer);
+ }
+}
+
+void WSAMovieV1::processFrame(int frameNum, uint8 *dst) {
+ debug(9, "WSAMovieV1::processFrame(%d, 0x%X)", frameNum, dst);
+ if (!_opened)
+ return;
+ assert(frameNum <= _numFrames);
+ const uint8 *src = _frameData + _frameOffsTable[frameNum];
+ Screen::decodeFrame4(src, _deltaBuffer, _deltaBufferSize);
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ Screen::decodeFrameDelta(dst, _deltaBuffer);
+ } else {
+ Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, 0);
+ }
+}
+} // end of namespace Kyra
diff --git a/engines/kyra/wsamovie.h b/engines/kyra/wsamovie.h
new file mode 100644
index 0000000000..fe3927f175
--- /dev/null
+++ b/engines/kyra/wsamovie.h
@@ -0,0 +1,85 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef WSAMOVIES_H
+#define WSAMOVIES_H
+
+#include "kyra/resource.h"
+
+namespace Kyra {
+class KyraEngine;
+
+class Movie {
+public:
+ Movie(KyraEngine *vm) : _x(-1), _y(-1), _drawPage(-1), _vm(vm), _opened(false) {}
+ virtual ~Movie() {}
+
+ virtual bool opened() { return _opened; }
+
+ virtual void open(const char *filename, int offscreen, uint8 *palette) = 0;
+ virtual void close() = 0;
+
+ virtual int frames() = 0;
+
+ virtual void displayFrame(int frameNum) = 0;
+
+ int _x, _y;
+ int _drawPage;
+protected:
+ KyraEngine *_vm;
+ bool _opened;
+};
+
+class WSAMovieV1 : public Movie {
+public:
+ WSAMovieV1(KyraEngine *vm);
+ virtual ~WSAMovieV1();
+
+ virtual void open(const char *filename, int offscreen, uint8 *palette);
+ virtual void close();
+
+ virtual int frames() { return _opened ? _numFrames : -1; }
+
+ virtual void displayFrame(int frameNum);
+protected:
+ virtual void processFrame(int frameNum, uint8 *dst);
+
+ enum WSAFlags {
+ WF_OFFSCREEN_DECODE = 0x10,
+ WF_NO_FIRST_FRAME = 0x40,
+ WF_HAS_PALETTE = 0x100
+ };
+
+ uint16 _currentFrame;
+ uint16 _numFrames;
+ uint16 _width;
+ uint16 _height;
+ uint16 _flags;
+ uint8 *_deltaBuffer;
+ uint32 _deltaBufferSize;
+ uint8 *_offscreenBuffer;
+ uint32 *_frameOffsTable;
+ uint8 *_frameData;
+};
+} // end of namespace Kyra
+
+#endif
diff --git a/engines/lure/animseq.cpp b/engines/lure/animseq.cpp
new file mode 100644
index 0000000000..c5ca90afd1
--- /dev/null
+++ b/engines/lure/animseq.cpp
@@ -0,0 +1,148 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/animseq.h"
+#include "lure/palette.h"
+#include "lure/decode.h"
+#include "lure/events.h"
+
+namespace Lure {
+
+// delay
+// Delays for a given number of milliseconds. If it returns true, it indicates that
+// Escape has been pressed, and the introduction should be aborted.
+
+AnimAbortType AnimationSequence::delay(uint32 milliseconds) {
+ uint32 delayCtr = _system.getMillis() + milliseconds;
+ Events &events = Events::getReference();
+
+ while (_system.getMillis() < delayCtr) {
+ while (events.pollEvent()) {
+ if (events.type() == OSystem::EVENT_KEYDOWN) {
+ if (events.event().kbd.keycode == 27) return ABORT_END_INTRO;
+ else return ABORT_NEXT_SCENE;
+ } else if (events.type() == OSystem::EVENT_LBUTTONDOWN)
+ return ABORT_NEXT_SCENE;
+ else if (events.type() == OSystem::EVENT_QUIT)
+ return ABORT_END_INTRO;
+ }
+
+ uint32 delayAmount = delayCtr - _system.getMillis();
+ if (delayAmount > 10) delayAmount = 10;
+ _system.delayMillis(delayAmount);
+ }
+ return ABORT_NONE;
+}
+
+// decodeFrame
+// Decodes a single frame of the animation sequence
+
+void AnimationSequence::decodeFrame(byte *&pPixels, byte *&pLines) {
+ byte *screen = _screen.screen_raw();
+ uint16 screenPos = 0;
+ uint16 len;
+
+ while (screenPos < SCREEN_SIZE) {
+ // Get line length
+ len = (uint16) *pLines++;
+ if (len == 0) {
+ len = READ_LE_UINT16(pLines);
+ pLines += 2;
+ }
+
+ // Move the splice over
+ memcpy(screen, pPixels, len);
+ screen += len;
+ screenPos += len;
+ pPixels += len;
+
+ // Get the offset inc amount
+ len = (uint16) *pLines++;
+ if (len == 0) {
+ len = READ_LE_UINT16(pLines);
+ pLines += 2;
+ }
+
+ screen += len;
+ screenPos += len;
+ }
+
+ // Make the decoded frame visible
+ _screen.update();
+}
+
+AnimationSequence::AnimationSequence(Screen &screen, OSystem &system, uint16 screenId, Palette &palette,
+ bool fadeIn): _screen(screen), _system(system), _screenId(screenId), _palette(palette) {
+ PictureDecoder decoder;
+ Disk &d = Disk::getReference();
+ MemoryBlock *data = d.getEntry(_screenId);
+ _decodedData = decoder.decode(data, MAX_ANIM_DECODER_BUFFER_SIZE);
+ delete data;
+
+ _lineRefs = d.getEntry(_screenId + 1);
+
+ // Show the screen that preceeds the start of the animation data
+ _screen.setPaletteEmpty();
+ _screen.screen().data().copyFrom(_decodedData, 0, 0, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH);
+ _screen.update();
+
+ // Set the palette
+ if (fadeIn) _screen.paletteFadeIn(&_palette);
+ else _screen.setPalette(&_palette);
+
+ // Set up frame poitners
+ _pPixels = _decodedData->data() + SCREEN_SIZE;
+ _pLines = _lineRefs->data();
+ _pPixelsEnd = _decodedData->data() + _decodedData->size() - 1;
+ _pLinesEnd = _lineRefs->data() + _lineRefs->size() - 1;
+}
+
+AnimationSequence::~AnimationSequence() {
+ delete _lineRefs;
+ delete _decodedData;
+}
+
+// show
+// Main method for displaying the animation
+
+AnimAbortType AnimationSequence::show() {
+ AnimAbortType result;
+
+ // Loop through displaying the animations
+ while ((_pPixels < _pPixelsEnd) && (_pLines < _pLinesEnd)) {
+ decodeFrame(_pPixels, _pLines);
+
+ result = delay(130);
+ if (result != ABORT_NONE) return result;
+ }
+
+ return ABORT_NONE;
+}
+
+bool AnimationSequence::step() {
+ if ((_pPixels >= _pPixelsEnd) || (_pLines >= _pLinesEnd)) return false;
+ decodeFrame(_pPixels, _pLines);
+ _screen.setPalette(&_palette);
+ return true;
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/animseq.h b/engines/lure/animseq.h
new file mode 100644
index 0000000000..fb6387a85d
--- /dev/null
+++ b/engines/lure/animseq.h
@@ -0,0 +1,56 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_animseq_h__
+#define __lure_animseq_h__
+
+#include "lure/screen.h"
+
+namespace Lure {
+
+enum AnimAbortType {ABORT_NONE, ABORT_END_INTRO, ABORT_NEXT_SCENE};
+
+class AnimationSequence {
+private:
+ Screen &_screen;
+ OSystem &_system;
+ uint16 _screenId;
+ Palette &_palette;
+ MemoryBlock *_decodedData;
+ MemoryBlock *_lineRefs;
+ byte *_pPixels, *_pLines;
+ byte *_pPixelsEnd, *_pLinesEnd;
+
+ AnimAbortType delay(uint32 milliseconds);
+ void decodeFrame(byte *&pPixels, byte *&pLines);
+public:
+ AnimationSequence(Screen &screen, OSystem &system, uint16 screenId, Palette &palette,
+ bool fadeIn);
+ ~AnimationSequence();
+
+ AnimAbortType show();
+ bool step();
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/debug-input.cpp b/engines/lure/debug-input.cpp
new file mode 100644
index 0000000000..9c6d7bf707
--- /dev/null
+++ b/engines/lure/debug-input.cpp
@@ -0,0 +1,133 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/debug-input.h"
+#include "lure/luredefs.h"
+#include "lure/events.h"
+#include "lure/surface.h"
+#include "lure/screen.h"
+
+#ifdef LURE_DEBUG
+
+namespace Lure {
+
+bool get_string(char *buffer, uint32 maxSize, bool isNumeric, uint16 x, uint16 y) {
+ Events &e = Events::getReference();
+ buffer[0] = '\0';
+
+ // Create surface for holding entered text
+ Surface *s = new Surface((maxSize + 1) * FONT_WIDTH, FONT_HEIGHT);
+
+ bool abortFlag = false;
+ bool refreshFlag = true;
+
+ while (!e.quitFlag && !abortFlag) {
+ // Check for refreshing display of text
+ if (refreshFlag) {
+ uint16 strWidth = Surface::textWidth(buffer);
+ s->empty();
+ s->writeString(0, 0, buffer, false, DIALOG_TEXT_COLOUR);
+ s->writeChar(strWidth, 0, '_', false, DIALOG_TEXT_COLOUR);
+ s->copyToScreen(x, y);
+
+ refreshFlag = false;
+ }
+
+ if (e.pollEvent()) {
+ if (e.type() == OSystem::EVENT_KEYDOWN) {
+ char ch = e.event().kbd.ascii;
+ uint16 keycode = e.event().kbd.keycode;
+
+ if ((ch == 13) || (keycode == 0x10f))
+ break;
+ else if (ch == 27)
+ abortFlag = true;
+ else if (ch == 8) {
+ if (*buffer != '\0') {
+ *((char *) buffer + strlen(buffer) - 1) = '\0';
+ refreshFlag = true;
+ }
+ } else if ((ch >= ' ') && (strlen(buffer) < maxSize)) {
+ if (((ch >= '0') && (ch <= '9')) || !isNumeric) {
+ char *p = buffer + strlen(buffer);
+ *p++ = ch;
+ *p++ = '\0';
+ refreshFlag = true;
+ }
+ }
+ }
+ }
+ }
+
+ delete s;
+ if (e.quitFlag) abortFlag = true;
+ return !abortFlag;
+}
+
+bool input_integer(Common::String desc, uint32 &value)
+{
+ const int MAX_SIZE = 5;
+ char buffer[MAX_SIZE + 1];
+
+ uint16 width = DIALOG_EDGE_SIZE + Surface::textWidth(desc.c_str()) + FONT_WIDTH;
+ uint16 totalWidth = width + FONT_WIDTH * (MAX_SIZE + 1) + DIALOG_EDGE_SIZE;
+ uint16 totalHeight = FONT_HEIGHT + DIALOG_EDGE_SIZE * 2;
+
+ Surface *s = new Surface(totalWidth, totalHeight);
+ s->createDialog(true);
+ s->writeString(DIALOG_EDGE_SIZE + 3, DIALOG_EDGE_SIZE, desc, false);
+
+ uint16 xs = (FULL_SCREEN_WIDTH-totalWidth) / 2;
+ uint16 ys = (FULL_SCREEN_HEIGHT-totalHeight) / 2;
+ s->copyToScreen(xs, ys);
+
+ bool result = get_string(&buffer[0], MAX_SIZE, true, xs+width, ys+DIALOG_EDGE_SIZE);
+ Screen::getReference().update();
+ if (!result || (buffer[0] == '\0'))
+ return false;
+
+ value = atoi(buffer);
+ return true;
+}
+
+bool input_string(Common::String desc, char *buffer, uint32 maxSize)
+{
+ uint16 width = Surface::textWidth(desc.c_str());
+ if (width < FONT_WIDTH * maxSize) width = FONT_WIDTH * maxSize;
+
+ Surface *s = new Surface(width + 2 * DIALOG_EDGE_SIZE, 2 * FONT_HEIGHT + 2 * DIALOG_EDGE_SIZE);
+ s->createDialog();
+ s->writeString(DIALOG_EDGE_SIZE, DIALOG_EDGE_SIZE, desc, false, DIALOG_TEXT_COLOUR);
+
+ uint16 xs = (FULL_SCREEN_WIDTH-s->width()) / 2;
+ uint16 ys = (FULL_SCREEN_HEIGHT-s->height()) / 2;
+
+ s->copyToScreen(xs, ys);
+ bool result = get_string(buffer, maxSize, true, xs + width, ys + DIALOG_EDGE_SIZE);
+
+ Screen::getReference().update();
+ return result;
+}
+
+} // end of namespace Lure
+
+#endif
diff --git a/engines/lure/debug-input.h b/engines/lure/debug-input.h
new file mode 100644
index 0000000000..ec820d6745
--- /dev/null
+++ b/engines/lure/debug-input.h
@@ -0,0 +1,42 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifdef LURE_DEBUG
+#ifndef __lure_input_h__
+#define __lure_input_h__
+
+#include "common/stdafx.h"
+#include "common/str.h"
+#include "lure/surface.h"
+
+namespace Lure {
+
+bool get_string(char *buffer, uint32 maxSize, bool isNumeric, uint16 x, uint16 y);
+
+bool input_integer(Common::String desc, uint32 &value);
+
+bool input_string(Common::String desc, char *buffer, uint32 maxSize);
+
+} // End of namespace Lure
+
+#endif
+#endif
diff --git a/engines/lure/debug-methods.cpp b/engines/lure/debug-methods.cpp
new file mode 100644
index 0000000000..2fda7a57dc
--- /dev/null
+++ b/engines/lure/debug-methods.cpp
@@ -0,0 +1,132 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/debug-methods.h"
+#include "lure/luredefs.h"
+
+#include "lure/events.h"
+#include "lure/surface.h"
+#include "lure/screen.h"
+#include "lure/res.h"
+#include "lure/strings.h"
+#include "lure/room.h"
+
+#ifdef LURE_DEBUG
+
+namespace Lure {
+
+void showActiveHotspots() {
+ char buffer[16384];
+ char *lines[100];
+ char *s = buffer;
+ int numLines = 0;
+ lines[0] = s;
+ *s = '\0';
+
+ Resources &resources = Resources::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Events &events = Events::getReference();
+ Screen &screen = Screen::getReference();
+
+ HotspotList::iterator i = resources.activeHotspots().begin();
+ for (; i != resources.activeHotspots().end(); ++i) {
+ Hotspot &h = *i.operator*();
+ lines[numLines++] = s;
+
+ if (numLines == 16) {
+ strcpy(s, "..more..");
+ break;
+ }
+
+ sprintf(s, "%x", h.hotspotId());
+ s += strlen(s);
+
+ sprintf(s, "h pos=(%d,%d,%d) size=(%d,%d) - ",
+ h.resource().roomNumber, h.x(), h.y(), h.width(), h.height());
+ s += strlen(s);
+
+ uint16 nameId = h.resource().nameId;
+ if (nameId != 0) {
+ StringData::getReference().getString(nameId, s, NULL, NULL);
+ s += strlen(s);
+ }
+ ++s;
+ }
+
+ Surface *surface = Surface::newDialog(300, numLines, lines);
+ mouse.cursorOff();
+ surface->copyToScreen(10, 40);
+ events.waitForPress();
+ screen.update();
+ mouse.cursorOn();
+ delete surface;
+}
+
+void showRoomHotspots() {
+ char buffer[16384];
+ char *lines[100];
+ char *s = buffer;
+ int numLines = 0;
+ lines[0] = s;
+ *s = '\0';
+
+ Resources &resources = Resources::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Events &events = Events::getReference();
+ Screen &screen = Screen::getReference();
+ uint16 roomNumber = Room::getReference().roomNumber();
+
+ HotspotDataList::iterator i = resources.hotspotData().begin();
+ for (; i != resources.hotspotData().end(); ++i) {
+ HotspotData &h = *i.operator*();
+ if (h.roomNumber == roomNumber) {
+ lines[numLines++] = s;
+
+ sprintf(s, "%x", h.hotspotId);
+ s += strlen(s);
+
+ sprintf(s, "h pos=(%d,%d) size=(%d,%d) - ",
+ h.startX, h.startY, h.width, h.height);
+ s += strlen(s);
+
+ uint16 nameId = h.nameId;
+ if (nameId != 0) {
+ StringData::getReference().getString(nameId, s, NULL, NULL);
+ s += strlen(s);
+ }
+ ++s;
+ }
+ }
+
+ Surface *surface = Surface::newDialog(300, numLines, lines);
+ mouse.cursorOff();
+ surface->copyToScreen(10, 40);
+ events.waitForPress();
+ screen.update();
+ mouse.cursorOn();
+ delete surface;
+}
+
+
+} // end of namespace Lure
+
+#endif
diff --git a/engines/lure/debug-methods.h b/engines/lure/debug-methods.h
new file mode 100644
index 0000000000..72e31b571b
--- /dev/null
+++ b/engines/lure/debug-methods.h
@@ -0,0 +1,39 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifdef LURE_DEBUG
+#ifndef __lure_debugprocs_h__
+#define __lure_debugprocs_h__
+
+#include "common/stdafx.h"
+#include "lure/surface.h"
+
+namespace Lure {
+
+void showActiveHotspots();
+
+void showRoomHotspots();
+
+} // End of namespace Lure
+
+#endif
+#endif
diff --git a/engines/lure/decode.cpp b/engines/lure/decode.cpp
new file mode 100644
index 0000000000..c04e4dca5b
--- /dev/null
+++ b/engines/lure/decode.cpp
@@ -0,0 +1,360 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/decode.h"
+#include "lure/memory.h"
+#include "lure/luredefs.h"
+
+namespace Lure {
+
+/*--------------------------------------------------------------------------*/
+/* PictureDecoder class */
+/* */
+/* Provides the functionality for decoding screens */
+/*--------------------------------------------------------------------------*/
+
+void PictureDecoder::writeByte(MemoryBlock *dest, byte v) {
+ if (outputOffset == dest->size())
+ error("Decoded data exceeded allocated output buffer size");
+ dest->data()[outputOffset++] = v;
+}
+
+void PictureDecoder::writeBytes(MemoryBlock *dest, byte v, uint16 numBytes) {
+ if (outputOffset + numBytes > dest->size())
+ error("Decoded data exceeded allocated output buffer size");
+ dest->memorySet(v, outputOffset, numBytes);
+ outputOffset += numBytes;
+}
+
+byte PictureDecoder::DSSI(bool incr) {
+ byte result = dataIn[dataPos];
+ if (incr) ++dataPos;
+ return result;
+}
+
+byte PictureDecoder::ESBX(bool incr) {
+ byte result = dataIn[dataPos2];
+ if (incr) ++dataPos2;
+ return result;
+}
+
+void PictureDecoder::decrCtr() {
+ --CL;
+ if (CL == 0) {
+ CH = ESBX();
+ CL = 8;
+ }
+}
+
+bool PictureDecoder::shlCarry() {
+ bool result = (CH & 0x80) != 0;
+ CH <<= 1;
+ return result;
+}
+
+void PictureDecoder::swap(uint16 &v1, uint16 &v2) {
+ uint16 vTemp;
+ vTemp = v1;
+ v1 = v2;
+ v2 = vTemp;
+}
+
+// decode_data
+// Takes care of decoding compressed Lure of the Temptress data
+
+MemoryBlock *PictureDecoder::decode(MemoryBlock *src, uint32 maxOutputSize) {
+ MemoryBlock *dest = Memory::allocate(maxOutputSize);
+
+ // Set up initial states
+ dataIn = src->data();
+ outputOffset = 0;
+ dataPos = READ_LE_UINT32(dataIn + 0x400);
+ dataPos2 = 0x404;
+
+ CH = ESBX();
+ CL = 9;
+
+Loc754:
+ AL = DSSI();
+ writeByte(dest, AL);
+ BP = ((uint16) AL) << 2;
+
+Loc755:
+ decrCtr();
+ if (shlCarry()) goto Loc761;
+ decrCtr();
+ if (shlCarry()) goto Loc759;
+ AL = dataIn[BP];
+
+Loc758:
+ writeByte(dest, AL);
+ BP = ((uint16) AL) << 2;
+ goto Loc755;
+
+Loc759:
+ AL = (byte) (BP >> 2);
+ AH = DSSI();
+ if (AH == 0) goto Loc768;
+
+ writeBytes(dest, AL, AH);
+ goto Loc755;
+
+Loc761:
+ decrCtr();
+ if (shlCarry()) goto Loc765;
+ decrCtr();
+
+ if (shlCarry()) goto Loc764;
+ AL = dataIn[BP+1];
+ goto Loc758;
+
+Loc764:
+ AL = dataIn[BP+2];
+ goto Loc758;
+
+Loc765:
+ decrCtr();
+ if (shlCarry()) goto Loc767;
+ AL = dataIn[BP+3];
+ goto Loc758;
+
+Loc767:
+ goto Loc754;
+
+Loc768:
+ AL = DSSI();
+ if (AL != 0) goto Loc755;
+
+ // Resize the output to be the number of outputed bytes and return it
+ if (outputOffset < dest->size()) dest->reallocate(outputOffset);
+ return dest;
+}
+
+/*--------------------------------------------------------------------------*/
+/* AnimationDecoder class */
+/* */
+/* Provides the functionality for decoding animations */
+/*--------------------------------------------------------------------------*/
+
+// The code below is responsible for decompressing the pixel data
+// for an animation. I'm not currently sure of the of the exact details
+// of the compression format - for now I've simply copied the code
+// from the executable
+
+void AnimationDecoder::rcl(uint16 &value, bool &carry) {
+ bool result = (value & 0x8000) != 0;
+ value = (value << 1) + (carry ? 1 : 0);
+ carry = result;
+}
+
+#define GET_BYTE currData = (currData & 0xff00) | *pSrc++
+#define BX_VAL(x) *((byte *) (dest->data() + tableOffset + x))
+#define SET_HI_BYTE(x,v) x = (x & 0xff) | ((v) << 8);
+#define SET_LO_BYTE(x,v) x = (x & 0xff00) | (v);
+
+void AnimationDecoder::decode_data_2(byte *&pSrc, uint16 &currData, uint16 &bitCtr,
+ uint16 &dx, bool &carry) {
+ SET_HI_BYTE(dx, currData >> 8);
+
+ for (int v = 0; v < 8; ++v) {
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ }
+}
+
+uint32 AnimationDecoder::decode_data(MemoryBlock *src, MemoryBlock *dest, uint32 srcPos) {
+ byte *pSrc = src->data() + srcPos;
+ byte *pDest = dest->data();
+ byte v;
+ bool carry = false;
+ uint16 currData, bitCtr, dx;
+ byte tableOffset;
+ uint16 tempReg1, tempReg2;
+
+ // Handle splitting up 16 bytes into individual nibbles
+ for (int numBytes = 0; numBytes < 16; ++numBytes, ++pDest) {
+ // Split up next byte to pDest and pDest+0x10
+ currData = *pSrc++;
+ *(pDest + 0x10) = currData & 0xf;
+ *pDest = (currData >> 4) & 0xf;
+
+ // Split up next byte to pDest+0x20 and pDest+0x30
+ currData = *pSrc++;
+ *(pDest + 0x30) = currData & 0xf;
+ *(pDest + 0x20) = (currData >> 4) & 0xf;
+ }
+
+ pDest = (byte *) (dest->data() + 0x40);
+ currData = READ_BE_UINT16(pSrc);
+ pSrc += sizeof(uint16);
+
+ bitCtr = 4;
+ *pDest = (currData >> 8) & 0xf0;
+ tableOffset = currData >> 12;
+ currData <<= 4;
+ dx = 1;
+
+ for (;;) {
+ carry = false;
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ if (carry) goto loc_1441;
+ tableOffset = BX_VAL(0);
+
+loc_1439:
+ dx ^= 1;
+ if ((dx & 1) != 0) {
+ SET_HI_BYTE(dx, tableOffset << 4);
+ *pDest = dx >> 8;
+ } else {
+ *pDest++ |= tableOffset;
+ }
+ continue;
+
+loc_1441:
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ if (!carry) {
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+
+ if (!carry) {
+ tableOffset = BX_VAL(0x10);
+ } else {
+ tableOffset = BX_VAL(0x20);
+ }
+ goto loc_1439;
+ }
+
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ if (!carry) {
+ tableOffset = BX_VAL(0x30);
+ goto loc_1439;
+ }
+
+ SET_HI_BYTE(dx, currData >> 12);
+ carry = false;
+ for (int ctr = 0; ctr < 4; ++ctr) {
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ }
+
+ byte dxHigh = dx >> 8;
+ if (dxHigh == BX_VAL(0)) {
+ tempReg1 = bitCtr;
+ tempReg2 = dx;
+ decode_data_2(pSrc, currData, bitCtr, dx, carry);
+
+ SET_LO_BYTE(dx, dx >> 8);
+ decode_data_2(pSrc, currData, bitCtr, dx, carry);
+ SET_HI_BYTE(bitCtr, dx & 0xff);
+ SET_LO_BYTE(bitCtr, dx >> 8);
+ dx = tempReg2;
+
+ if (bitCtr == 0)
+ // Exit out of infinite loop
+ break;
+
+ } else if (dxHigh == BX_VAL(0x10)) {
+ tempReg1 = bitCtr;
+ decode_data_2(pSrc, currData, bitCtr, dx, carry);
+ bitCtr = dx >> 8;
+
+ } else if (dxHigh == BX_VAL(0x20)) {
+ SET_HI_BYTE(dx, currData >> 10);
+
+ for (v = 0; v < 6; ++v) {
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ }
+
+ tempReg1 = bitCtr;
+ bitCtr = dx >> 8;
+
+ } else if (dxHigh == BX_VAL(0x30)) {
+ SET_HI_BYTE(dx, currData >> 11);
+
+ for (v = 0; v < 5; ++v) {
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ }
+
+ tempReg1 = bitCtr;
+ bitCtr = dx >> 8;
+
+ } else {
+ tableOffset = dx >> 8;
+ goto loc_1439;
+ }
+
+ if ((dx & 1) == 1) {
+ *pDest++ |= tableOffset;
+ --bitCtr;
+ dx &= 0xfffe;
+ }
+
+ SET_HI_BYTE(dx, tableOffset << 4);
+ tableOffset |= dx >> 8;
+
+ v = bitCtr >> 1;
+ while (v-- > 0) *pDest++ = tableOffset;
+
+ bitCtr &= 1;
+ if (bitCtr != 0) {
+ *pDest = tableOffset & 0xf0;
+ dx |= 1; //dx.l
+ }
+
+ bitCtr = tempReg1;
+ tableOffset &= 0x0f;
+ }
+
+ // Return number of bytes written
+ return pDest - dest->data();
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/decode.h b/engines/lure/decode.h
new file mode 100644
index 0000000000..4f2994d424
--- /dev/null
+++ b/engines/lure/decode.h
@@ -0,0 +1,62 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_decode_h__
+#define __lure_decode_h__
+
+#include "common/stdafx.h"
+#include "lure/luredefs.h"
+#include "lure/memory.h"
+
+namespace Lure {
+
+class PictureDecoder {
+private:
+ byte *dataIn;
+ uint32 BP;
+ uint32 dataPos, dataPos2;
+ uint32 outputOffset;
+ byte AL, AH;
+ byte CH, CL;
+
+ void writeByte(MemoryBlock *dest, byte v);
+ void writeBytes(MemoryBlock *dest, byte v, uint16 numBytes);
+ byte DSSI(bool incr = true);
+ byte ESBX(bool incr = true);
+ void decrCtr();
+ bool shlCarry();
+ void swap(uint16 &v1, uint16 &v2);
+public:
+ MemoryBlock *decode(MemoryBlock *src, uint32 maxOutputSize = SCREEN_SIZE);
+};
+
+class AnimationDecoder {
+public:
+ static void rcl(uint16 &value, bool &carry);
+ static uint32 decode_data(MemoryBlock *src, MemoryBlock *dest, uint32 srcPos);
+ static void decode_data_2(byte *&pSrc, uint16 &currData, uint16 &bitCtr,
+ uint16 &dx, bool &carry);
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/disk.cpp b/engines/lure/disk.cpp
new file mode 100644
index 0000000000..e996ff6ecc
--- /dev/null
+++ b/engines/lure/disk.cpp
@@ -0,0 +1,174 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/util.h"
+#include "common/scummsys.h"
+
+#include "lure/disk.h"
+#include "lure/luredefs.h"
+
+namespace Lure {
+
+static Disk *int_disk = NULL;
+
+Disk &Disk::getReference() {
+ return *int_disk;
+}
+
+Disk::Disk(const Common::String &gameDataPath) {
+ _gameDataPath = gameDataPath;
+ _fileNum = 0xff;
+ _fileHandle = NULL;
+ int_disk = this;
+}
+
+Disk::~Disk() {
+ if (_fileHandle) delete _fileHandle;
+ int_disk = NULL;
+}
+
+uint8 Disk::indexOf(uint16 id, bool suppressError) {
+ // Make sure the correct file is open - the upper two bits of the Id give the file number. Note
+ // that an extra check is done for the upper byte of the Id being 0x3f, which is the Id range
+ // I use for lure.dat resources, which are resources extracted from the lure.exe executable
+ uint8 entryFileNum = ((id>>8) == 0x3f) ? 0 : ((id >> 14) & 3) + 1;
+ openFile(entryFileNum);
+
+ // Find the correct entry in the list based on the Id
+ for (int entryIndex=0; entryIndex<NUM_ENTRIES_IN_HEADER; ++entryIndex) {
+ if (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID) break;
+ else if (_entries[entryIndex].id == id) return entryIndex;
+ }
+
+ if (suppressError) return 0xff;
+ error("Could not find entry Id #%d in file %sdisk%d.vga", id, _gameDataPath.c_str(), _fileNum);
+}
+
+void Disk::openFile(uint8 fileNum) {
+ // Validate that the file number is correct
+ if (fileNum > 4)
+ error("Invalid file number specified - %d", fileNum);
+
+ // Only load up the new file if the current file number has changed
+ if (fileNum == _fileNum) return;
+
+ // Delete any existing open file handle
+ if (_fileNum != 0xff) delete _fileHandle;
+ _fileNum = fileNum;
+
+ // Open up the the new file
+ _fileHandle = new Common::File();
+
+ char sFilename[10];
+ if (_fileNum == 0)
+ strcpy(sFilename, SUPPORT_FILENAME);
+ else
+ sprintf(sFilename, "disk%d.vga", _fileNum);
+
+ _fileHandle->open(sFilename);
+ if (!_fileHandle->isOpen())
+ error("Could not open %s%s", _gameDataPath.c_str(), sFilename);
+
+ // Validate the header
+ char buffer[7];
+ uint32 bytesRead;
+
+ bytesRead = _fileHandle->read(buffer, 6);
+ buffer[6] = '\0';
+ if (strcmp(buffer, HEADER_IDENT_STRING) != 0)
+ error("The file %s%s was not a valid VGA file", _gameDataPath.c_str(), sFilename);
+
+ uint16 fileFileNum = _fileHandle->readUint16BE();
+ if (fileFileNum != _fileNum)
+ error("The file %s%s was not the correct file number", _gameDataPath.c_str(), sFilename);
+
+ // Read in the header entries
+ uint32 headerSize = sizeof(FileEntry) * NUM_ENTRIES_IN_HEADER;
+ if (_fileHandle->read(_entries, headerSize) != headerSize)
+ error("The file %s%s had a corrupted header", _gameDataPath.c_str(), sFilename);
+
+#ifdef SCUMM_BIG_ENDIAN
+ // Process the read in header list to convert to big endian
+ for (int i = 0; i < NUM_ENTRIES_IN_HEADER; ++i) {
+ _entries[i].id = FROM_LE_16(_entries[i].id);
+ _entries[i].size = FROM_LE_16(_entries[i].size);
+ _entries[i].offset = FROM_LE_16(_entries[i].offset);
+ }
+#endif
+}
+
+uint32 Disk::getEntrySize(uint16 id) {
+ // Get the index of the resource, if necessary opening the correct file
+ uint8 index = indexOf(id);
+
+ // Calculate the offset and size of the entry
+ uint32 size = (uint32) _entries[index].size;
+ if (_entries[index].sizeExtension) size += 0x10000;
+
+ return size;
+}
+
+MemoryBlock *Disk::getEntry(uint16 id)
+{
+ // Get the index of the resource, if necessary opening the correct file
+ uint8 index = indexOf(id);
+
+ // Calculate the offset and size of the entry
+ uint32 size = (uint32) _entries[index].size;
+ if (_entries[index].sizeExtension) size += 0x10000;
+ uint32 offset = (uint32) _entries[index].offset * 0x20;
+
+ MemoryBlock *result = Memory::allocate(size);
+ _fileHandle->seek(offset, SEEK_SET);
+ _fileHandle->read(result->data(), size);
+ return result;
+}
+
+bool Disk::exists(uint16 id) {
+ // Get the index of the resource, if necessary opening the correct file
+ uint8 index = indexOf(id, true);
+ return (index != 0xff);
+}
+
+uint8 Disk::numEntries() {
+ if (_fileNum == 0)
+ error("No file is currently open");
+
+ // Figure out how many entries there are by count until an unused entry is found
+ for (byte entryIndex = 0; entryIndex < NUM_ENTRIES_IN_HEADER; ++entryIndex)
+ if (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID) return entryIndex;
+
+ return NUM_ENTRIES_IN_HEADER;
+}
+
+FileEntry *Disk::getIndex(uint8 entryIndex) {
+ if (_fileNum == 0)
+ error("No file is currently open");
+ if ((entryIndex >= NUM_ENTRIES_IN_HEADER) || (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID))
+ error("There is no entry at the specified index");
+
+ return &_entries[entryIndex];
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/disk.h b/engines/lure/disk.h
new file mode 100644
index 0000000000..9ce1c977ae
--- /dev/null
+++ b/engines/lure/disk.h
@@ -0,0 +1,66 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_disk_h__
+#define __lure_disk_h__
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "lure/memory.h"
+#include "lure/res_struct.h"
+
+namespace Common {
+ class File;
+}
+
+namespace Lure {
+
+#define NUM_ENTRIES_IN_HEADER 0xBF
+#define HEADER_IDENT_STRING "heywow"
+#define HEADER_ENTRY_UNUSED_ID 0xffff
+
+class Disk {
+private:
+ Common::String _gameDataPath;
+ uint8 _fileNum;
+ Common::File *_fileHandle;
+ FileEntry _entries[NUM_ENTRIES_IN_HEADER];
+
+ uint8 indexOf(uint16 id, bool suppressError = false);
+public:
+ Disk(const Common::String &gameDataPath);
+ ~Disk();
+ static Disk &getReference();
+
+ void openFile(uint8 fileNum);
+ uint32 getEntrySize(uint16 id);
+ MemoryBlock *getEntry(uint16 id);
+ bool exists(uint16 id);
+
+ uint8 numEntries();
+ FileEntry *getIndex(uint8 entryIndex);
+};
+
+} // end of namespace Lure
+
+#endif
diff --git a/engines/lure/events.cpp b/engines/lure/events.cpp
new file mode 100644
index 0000000000..1d8ce03b42
--- /dev/null
+++ b/engines/lure/events.cpp
@@ -0,0 +1,161 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/events.h"
+
+namespace Lure {
+
+static Mouse *int_mouse = NULL;
+
+Mouse &Mouse::getReference() {
+ return *int_mouse;
+}
+
+Mouse::Mouse(OSystem &system): _system(system), _cursors(Disk::getReference().getEntry(CURSOR_RESOURCE_ID)) {
+ int_mouse = this;
+
+ _lButton = false;
+ _rButton = false;
+ _cursorNum = 0;
+ setCursorNum(0);
+}
+
+Mouse::~Mouse() {
+ delete _cursors;
+}
+
+void Mouse::handleEvent(OSystem::Event event) {
+ _x = (int16) event.mouse.x;
+ _y = (int16) event.mouse.y;
+
+ switch (event.type) {
+ case OSystem::EVENT_LBUTTONDOWN:
+ _lButton = true;
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _lButton = false;
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+ _rButton = true;
+ break;
+ case OSystem::EVENT_RBUTTONUP:
+ _rButton = false;
+ break;
+ default:
+ break;
+ }
+}
+
+
+void Mouse::cursorOn() {
+ _system.showMouse(true);
+}
+
+void Mouse::cursorOff() {
+ _system.showMouse(false);
+}
+
+void Mouse::setCursorNum(uint8 cursorNum) {
+ int hotspotX = 7, hotspotY = 7;
+ if ((cursorNum == CURSOR_ARROW) || (cursorNum == CURSOR_MENUBAR)) {
+ hotspotX = 0;
+ hotspotY = 0;
+ }
+
+ setCursorNum(cursorNum, hotspotX, hotspotY);
+}
+
+void Mouse::setCursorNum(uint8 cursorNum, int hotspotX, int hotspotY) {
+ _cursorNum = cursorNum;
+ byte *cursorAddr = _cursors->data() + (cursorNum * CURSOR_SIZE);
+ _system.setMouseCursor(cursorAddr, CURSOR_WIDTH, CURSOR_HEIGHT, hotspotX, hotspotY, 0);
+}
+
+void Mouse::setPosition(int newX, int newY) {
+ _system.warpMouse(newX, newY);
+}
+
+void Mouse::waitForRelease() {
+ Events &e = Events::getReference();
+
+ do {
+ e.pollEvent();
+ _system.delayMillis(10);
+ } while (!e.quitFlag && (lButton() || rButton()));
+}
+
+/*--------------------------------------------------------------------------*/
+
+static Events *int_events = NULL;
+
+Events::Events(OSystem &system, Mouse &mouse): _system(system), _mouse(mouse), quitFlag(false) {
+ int_events = this;
+}
+
+Events &Events::getReference() {
+ return *int_events;
+}
+
+
+bool Events::pollEvent() {
+ if (!_system.pollEvent(_event)) return false;
+
+ // Handle keypress
+ switch (_event.type) {
+ case OSystem::EVENT_QUIT:
+ quitFlag = true;
+ break;
+
+ case OSystem::EVENT_LBUTTONDOWN:
+ case OSystem::EVENT_LBUTTONUP:
+ case OSystem::EVENT_RBUTTONDOWN:
+ case OSystem::EVENT_RBUTTONUP:
+ case OSystem::EVENT_MOUSEMOVE:
+ case OSystem::EVENT_WHEELUP:
+ case OSystem::EVENT_WHEELDOWN:
+ _mouse.handleEvent(_event);
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void Events::waitForPress() {
+ bool keyButton = false;
+ while (!keyButton) {
+ if (pollEvent()) {
+ if (_event.type == OSystem::EVENT_QUIT) return;
+ else if (_event.type == OSystem::EVENT_KEYDOWN) keyButton = true;
+ else if ((_event.type == OSystem::EVENT_LBUTTONDOWN) ||
+ (_event.type == OSystem::EVENT_RBUTTONDOWN)) {
+ keyButton = true;
+ _mouse.waitForRelease();
+ }
+ }
+ _system.delayMillis(10);
+ }
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/events.h b/engines/lure/events.h
new file mode 100644
index 0000000000..9e6e9c540c
--- /dev/null
+++ b/engines/lure/events.h
@@ -0,0 +1,78 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_events_h__
+#define __lure_events_h__
+
+#include "common/stdafx.h"
+#include "common/str.h"
+#include "lure/luredefs.h"
+#include "lure/disk.h"
+
+namespace Lure {
+
+class Mouse {
+private:
+ OSystem &_system;
+ MemoryBlock *_cursors;
+ uint8 _cursorNum;
+ int16 _x, _y;
+ bool _lButton, _rButton;
+public:
+ Mouse(OSystem &system);
+ ~Mouse();
+ static Mouse &getReference();
+ void handleEvent(OSystem::Event event);
+
+ void cursorOn();
+ void cursorOff();
+ void setCursorNum(uint8 cursorNum);
+ void setCursorNum(uint8 cursorNum, int hotspotX, int hotspotY);
+ uint8 getCursorNum() { return _cursorNum; }
+ void setPosition(int x, int y);
+ int16 x() { return _x; }
+ int16 y() { return _y; }
+ bool lButton() { return _lButton; }
+ bool rButton() { return _rButton; }
+ void waitForRelease();
+};
+
+class Events {
+private:
+ OSystem &_system;
+ Mouse &_mouse;
+ OSystem::Event _event;
+public:
+ bool quitFlag;
+
+ Events(OSystem &system, Mouse &mouse);
+ static Events &getReference();
+
+ bool pollEvent();
+ void waitForPress();
+ OSystem::Event event() { return _event; }
+ OSystem::EventType type() { return _event.type; }
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/game.cpp b/engines/lure/game.cpp
new file mode 100644
index 0000000000..2cc3e39b42
--- /dev/null
+++ b/engines/lure/game.cpp
@@ -0,0 +1,424 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/game.h"
+#include "lure/strings.h"
+#include "lure/room.h"
+#include "lure/system.h"
+#include "lure/debug-input.h"
+#include "lure/debug-methods.h"
+#include "lure/scripts.h"
+#include "lure/res_struct.h"
+
+namespace Lure {
+
+static Game *int_game = NULL;
+
+Game &Game::getReference() {
+ return *int_game;
+}
+
+Game::Game() {
+ int_game = this;
+ _slowSpeedFlag = true;
+ _soundFlag = true;
+ _remoteView = false;
+}
+
+void Game::nextFrame() {
+ Resources &r = Resources::getReference();
+ HotspotList::iterator i = r.activeHotspots().begin();
+ HotspotList::iterator iTemp;
+
+ // Note the somewhat more complicated loop style as a hotspot tick handler may
+ // unload the hotspot and accompanying record
+ for (; i != r.activeHotspots().end(); i = iTemp) {
+ iTemp = i;
+ ++iTemp;
+ Hotspot &h = *i.operator*();
+ h.tick();
+ }
+}
+
+void Game::execute() {
+ OSystem &system = System::getReference();
+ Room &r = Room::getReference();
+ Resources &res = Resources::getReference();
+ Events &events = Events::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Screen &screen = Screen::getReference();
+ Menu &menu = Menu::getReference();
+ ValueTableData &fields = res.fieldList();
+
+ uint32 timerVal = system.getMillis();
+
+ screen.empty();
+ //_screen.resetPalette();
+ screen.setPaletteEmpty();
+
+ Script::execute(STARTUP_SCRIPT);
+
+ // Load the first room
+ r.setRoomNumber(1);
+
+ // Set the player direction
+ res.getActiveHotspot(PLAYER_ID)->setDirection(UP);
+
+ r.update();
+ mouse.setCursorNum(CURSOR_ARROW);
+ mouse.cursorOn();
+
+ while (!events.quitFlag) {
+ // If time for next frame, allow everything to update
+ if (system.getMillis() > timerVal + GAME_FRAME_DELAY) {
+ timerVal = system.getMillis();
+ nextFrame();
+ }
+ res.delayList().tick();
+ r.update();
+ system.delayMillis(10);
+
+ while (events.pollEvent()) {
+ if (events.type() == OSystem::EVENT_KEYDOWN) {
+ uint16 roomNum = r.roomNumber();
+
+#ifdef LURE_DEBUG
+ if (events.event().kbd.keycode == 282) {
+ doDebugMenu();
+ continue;
+ }
+#endif
+
+ switch (events.event().kbd.ascii) {
+ case 27:
+ events.quitFlag = true;
+ break;
+
+#ifdef LURE_DEBUG
+ case '+':
+ while (++roomNum <= 51)
+ if (res.getRoom(roomNum) != NULL) break;
+ if (roomNum == 52) roomNum = 1;
+
+ r.leaveRoom();
+ r.setRoomNumber(roomNum);
+ break;
+
+ case '-':
+ if (roomNum == 1) roomNum = 55;
+ while (res.getRoom(--roomNum) == NULL) ;
+
+ r.leaveRoom();
+ r.setRoomNumber(roomNum);
+ break;
+
+ case '*':
+ res.getActiveHotspot(PLAYER_ID)->setRoomNumber(
+ r.roomNumber());
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+ if (mouse.y() < MENUBAR_Y_SIZE)
+ {
+ if (mouse.getCursorNum() != CURSOR_MENUBAR) mouse.setCursorNum(CURSOR_MENUBAR);
+ if ((mouse.getCursorNum() == CURSOR_MENUBAR) && mouse.lButton())
+ {
+ uint8 responseId = menu.execute();
+ mouse.setCursorNum((mouse.y() < MENUBAR_Y_SIZE) ? CURSOR_MENUBAR : CURSOR_ARROW);
+ if (responseId != MENUITEM_NONE)
+ handleMenuResponse(responseId);
+ }
+ } else {
+ if (mouse.getCursorNum() == CURSOR_MENUBAR) mouse.setCursorNum(CURSOR_ARROW);
+
+ if (events.type() == OSystem::EVENT_MOUSEMOVE)
+ r.cursorMoved();
+
+ if (mouse.rButton()) handleRightClickMenu();
+ else if (mouse.lButton()) handleLeftClick();
+ }
+ }
+
+ uint16 destRoom = fields.getField(NEW_ROOM_NUMBER);
+ if (_remoteView && (destRoom != 0)) {
+ // Show a remote view of the specified room
+ uint16 currentRoom = r.roomNumber();
+ r.setRoomNumber(destRoom, true);
+
+ // This code eventually needs to be moved into the main loop so that,
+ // amongst other things, the tick handlers controlling animation can work
+ while (!events.quitFlag && !mouse.lButton() && !mouse.rButton()) {
+ while (events.pollEvent()) {
+ if ((events.type() == OSystem::EVENT_KEYDOWN) &&
+ (events.event().kbd.ascii == 27))
+ events.quitFlag = true;
+ if (events.type() == OSystem::EVENT_MOUSEMOVE)
+ r.cursorMoved();
+ }
+
+ if (system.getMillis() > timerVal + GAME_FRAME_DELAY) {
+ timerVal = system.getMillis();
+ nextFrame();
+ }
+ res.delayList().tick();
+ r.update();
+ system.delayMillis(10);
+ }
+
+ fields.setField(NEW_ROOM_NUMBER, 0);
+ Hotspot *player = res.getActiveHotspot(PLAYER_ID);
+ player->setTickProc(0x5e44); // reattach player handler
+ _remoteView = false;
+ r.setRoomNumber(currentRoom);
+ }
+ }
+
+ r.leaveRoom();
+}
+
+#ifdef LURE_DEBUG
+
+#define NUM_DEBUG_ITEMS 4
+const char *debugItems[NUM_DEBUG_ITEMS] =
+ {"Toggle Info", "Set Room", "Show Active HS", "Show Room HS"};
+
+void Game::doDebugMenu() {
+ uint16 index = PopupMenu::Show(NUM_DEBUG_ITEMS, debugItems);
+ Room &r = Room::getReference();
+ Resources &res = Resources::getReference();
+
+ switch (index) {
+ case 0:
+ // Toggle co-ordinates
+ r.setShowInfo(!r.showInfo());
+ break;
+
+ case 1:
+ // Set room number:
+ uint32 roomNumber;
+ if (!input_integer("Enter room number:", roomNumber)) return;
+ if (res.getRoom(roomNumber))
+ r.setRoomNumber(roomNumber);
+ else
+ Dialog::show("The room does not exist");
+ break;
+
+ case 2:
+ // Show active hotspots
+ showActiveHotspots();
+ break;
+
+ case 3:
+ // Show hotspots in room
+ showRoomHotspots();
+ break;
+
+ default:
+ break;
+ }
+}
+
+#endif
+
+void Game::handleMenuResponse(uint8 selection) {
+ switch (selection) {
+ case MENUITEM_CREDITS:
+ doShowCredits();
+ break;
+
+ case MENUITEM_RESTART_GAME:
+ case MENUITEM_SAVE_GAME:
+ case MENUITEM_RESTORE_GAME:
+ break;
+
+ case MENUITEM_QUIT:
+ doQuit();
+ break;
+
+ case MENUITEM_TEXT_SPEED:
+ doTextSpeed();
+ break;
+
+ case MENUITEM_SOUND:
+ doSound();
+ }
+}
+
+void Game::handleRightClickMenu() {
+ Room &r = Room::getReference();
+ Resources &res = Resources::getReference();
+ ValueTableData &fields = Resources::getReference().fieldList();
+ Hotspot *player = res.getActiveHotspot(PLAYER_ID);
+ HotspotData *hotspot;
+ Action action;
+ uint32 actions;
+ uint16 itemId;
+
+ if (r.hotspotId() != 0) {
+ // Get hotspot actions
+ actions = r.hotspotActions();
+ } else {
+ // Standard actions - drink, examine, look, status
+ actions = 0x1184000;
+ }
+
+ // If no inventory items remove entries that require them
+ if (res.numInventoryItems() == 0)
+ actions &= 0xFEF3F9FD;
+
+ action = NONE;
+ hotspot = NULL;
+
+ bool breakFlag = false;
+ while (!breakFlag) {
+ action = PopupMenu::Show(actions);
+
+ switch (action) {
+ case LOOK:
+ case STATUS:
+ breakFlag = true;
+ break;
+
+ case GIVE:
+ case USE:
+ case EXAMINE:
+ case DRINK:
+ if (action != DRINK)
+ hotspot = res.getHotspot(r.hotspotId());
+ itemId = PopupMenu::ShowInventory();
+ breakFlag = (itemId != 0xffff);
+ if (breakFlag)
+ fields.setField(USE_HOTSPOT_ID, itemId);
+ break;
+
+ default:
+ hotspot = res.getHotspot(r.hotspotId());
+ breakFlag = true;
+ break;
+ }
+ }
+
+ // Set fields used by the script interpreter
+ fields.setField(CHARACTER_HOTSPOT_ID, PLAYER_ID);
+ if (hotspot) {
+ fields.setField(ACTIVE_HOTSPOT_ID, hotspot->hotspotId);
+ if ((action != USE) && (action != GIVE)) {
+ fields.setField(USE_HOTSPOT_ID, hotspot->hotspotId);
+ }
+ }
+
+ if (action != NONE)
+ player->doAction(action, hotspot);
+}
+
+void Game::handleLeftClick() {
+ Room &room = Room::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Resources &resources = Resources::getReference();
+
+ if (room.hotspotId()) {
+ // Handle look at hotspot
+ HotspotData *hs = resources.getHotspot(room.hotspotId());
+ Hotspot *player = resources.getActiveHotspot(PLAYER_ID);
+ room.setAction(LOOK_AT);
+ room.update();
+ player->doAction(LOOK_AT, hs);
+ room.setAction(NONE);
+ } else {
+ // Walk to mouse click. TODO: still need to recognise other actions,
+ // such as to room exits or closing an on-screen floating dialog
+ Hotspot *hs = resources.getActiveHotspot(PLAYER_ID);
+ hs->walkTo(mouse.x(), mouse.y(), 0);
+ }
+}
+
+void Game::doShowCredits() {
+ Events &events = Events::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Screen &screen = Screen::getReference();
+
+ mouse.cursorOff();
+ Palette p(CREDITS_RESOURCE_ID - 1);
+ Surface *s = Surface::getScreen(CREDITS_RESOURCE_ID);
+ screen.setPalette(&p);
+ s->copyToScreen(0, 0);
+ delete s;
+
+ events.waitForPress();
+
+ screen.resetPalette();
+ screen.update();
+ mouse.cursorOn();
+}
+
+void Game::doQuit() {
+ OSystem &system = System::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Events &events = Events::getReference();
+ Screen &screen = Screen::getReference();
+
+ mouse.cursorOff();
+ Surface *s = Surface::newDialog(190, "Are you sure (y/n)?");
+ s->centerOnScreen();
+ delete s;
+
+ char key = '\0';
+ do {
+ if (events.pollEvent()) {
+ if (events.event().type == OSystem::EVENT_KEYDOWN) {
+ key = events.event().kbd.ascii;
+ if ((key >= 'A') && (key <= 'Z')) key += 'a' - 'A';
+ }
+ }
+ system.delayMillis(10);
+ } while (((uint8) key != 27) && (key != 'y') && (key != 'n'));
+
+ events.quitFlag = key == 'y';
+ if (!events.quitFlag) {
+ screen.update();
+ mouse.cursorOn();
+ }
+}
+
+void Game::doTextSpeed() {
+ Menu &menu = Menu::getReference();
+
+ _slowSpeedFlag = !_slowSpeedFlag;
+ const char *pSrc = _slowSpeedFlag ? "Slow" : "Fast";
+ char *pDest = menu.getMenu(2).getEntry(1);
+ memcpy(pDest, pSrc, 4);
+}
+
+void Game::doSound() {
+ Menu &menu = Menu::getReference();
+
+ _soundFlag = !_soundFlag;
+ const char *pSrc = _soundFlag ? "on " : "off";
+ char *pDest = menu.getMenu(2).getEntry(2) + 6;
+ memcpy(pDest, pSrc, 3);
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/game.h b/engines/lure/game.h
new file mode 100644
index 0000000000..d1957a6201
--- /dev/null
+++ b/engines/lure/game.h
@@ -0,0 +1,64 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_game_h__
+#define __lure_game_h__
+
+#include "common/stdafx.h"
+#include "base/engine.h"
+#include "lure/luredefs.h"
+#include "lure/menu.h"
+#include "lure/palette.h"
+#include "lure/disk.h"
+#include "lure/memory.h"
+#include "lure/screen.h"
+#include "lure/events.h"
+
+namespace Lure {
+
+class Game {
+private:
+ bool _slowSpeedFlag, _soundFlag;
+ bool _remoteView;
+
+ void handleMenuResponse(uint8 selection);
+ void handleRightClickMenu();
+ void handleLeftClick();
+public:
+ Game();
+ static Game &getReference();
+
+ void nextFrame();
+ void execute();
+ void setRemoteView() { _remoteView = true; }
+
+ // Menu item support methods
+ void doDebugMenu();
+ void doShowCredits();
+ void doQuit();
+ void doTextSpeed();
+ void doSound();
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/hotspots.cpp b/engines/lure/hotspots.cpp
new file mode 100644
index 0000000000..e7df938d47
--- /dev/null
+++ b/engines/lure/hotspots.cpp
@@ -0,0 +1,806 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/hotspots.h"
+#include "lure/decode.h"
+#include "lure/palette.h"
+#include "lure/disk.h"
+#include "lure/res.h"
+#include "lure/scripts.h"
+#include "lure/room.h"
+#include "lure/strings.h"
+#include "lure/res_struct.h"
+#include "lure/events.h"
+
+namespace Lure {
+
+Hotspot::Hotspot(HotspotData *res) {
+ _data = res;
+ _anim = NULL;
+ _frames = NULL;
+ _numFrames = 0;
+ _persistant = false;
+
+ _startX = res->startX;
+ _startY = res->startY;
+ _destX = res->startX;
+ _destY = res->startY;
+ _destHotspotId = 0;
+ _width = res->width;
+ _height = res->height;
+ _tickCtr = res->tickTimeout;
+
+ // Check for a hotspot override
+ HotspotOverrideData *hor = Resources::getReference().getHotspotOverride(res->hotspotId);
+
+ if (hor) {
+ _startX = hor->xs;
+ _startY = hor->ys;
+ if (hor->xe < hor->xs) _width = 0;
+ else _width = hor->xe - hor->xs + 1;
+ if (hor->ye < hor->ys) _height = 0;
+ else _height = hor->ye - hor->ys + 1;
+ }
+
+ if (_data->animRecordId != 0)
+ setAnimation(_data->animRecordId);
+
+ _tickHandler = HotspotTickHandlers::getHandler(_data->tickProcOffset);
+}
+
+Hotspot::~Hotspot() {
+ if (_frames) delete _frames;
+}
+
+void Hotspot::setAnimation(uint16 newAnimId) {
+ Resources &r = Resources::getReference();
+ HotspotAnimData *tempAnim;
+ if (newAnimId == 0) tempAnim = NULL;
+ else tempAnim = r.getAnimation(newAnimId);
+
+ setAnimation(tempAnim);
+}
+
+void Hotspot::setAnimation(HotspotAnimData *newRecord) {
+ Disk &r = Disk::getReference();
+ if (_frames) {
+ delete _frames;
+ _frames = NULL;
+ }
+ _anim = NULL;
+ _numFrames = 0;
+ _frameNumber = 0;
+ if (!newRecord) return;
+ if (!r.exists(newRecord->animId)) return;
+
+ _anim = newRecord;
+ MemoryBlock *src = Disk::getReference().getEntry(_anim->animId);
+
+ uint16 *numEntries = (uint16 *) src->data();
+ uint16 *headerEntry = (uint16 *) (src->data() + 2);
+
+ if ((*numEntries > 99) || (*numEntries == 0)) {
+ // Wobbly, likely something wrong with the resoure
+ _width = 1;
+ _numFrames = 1;
+ _frameNumber = 0;
+ _frames = new Surface(1, 1);
+ _frames->data().memorySet(_data->colourOffset, 0, 1);
+ return;
+ }
+
+ // Calculate total needed size for output and create memory block to hold it
+ uint32 totalSize = 0;
+ for (uint16 ctr = 0; ctr < *numEntries; ++ctr, ++headerEntry) {
+ totalSize += (*headerEntry + 31) / 32;
+ }
+ totalSize = (totalSize + 0x81) << 4;
+ MemoryBlock *dest = Memory::allocate(totalSize);
+
+ uint32 srcStart = (*numEntries + 1) * sizeof(uint16) + 6;
+ AnimationDecoder::decode_data(src, dest, srcStart);
+
+ _numFrames = *numEntries;
+ _frameNumber = 0;
+
+ _frames = new Surface(_data->width * _numFrames, _data->height);
+
+ _frames->data().memorySet(_data->colourOffset, 0, _frames->data().size());
+
+ byte *pSrc = dest->data() + 0x40;
+ byte *pDest;
+ headerEntry = (uint16 *) (src->data() + 2);
+ MemoryBlock &mDest = _frames->data();
+
+ for (uint16 frameCtr = 0; frameCtr < _numFrames; ++frameCtr, ++headerEntry) {
+
+ // Copy over the frame, applying the colour offset to each nibble
+ for (uint16 yPos = 0; yPos < _data->height; ++yPos) {
+ pDest = mDest.data() + (yPos * _numFrames + frameCtr) * _data->width;
+
+ for (uint16 ctr = 0; ctr < _data->width / 2; ++ctr) {
+ *pDest++ = _data->colourOffset + (*pSrc >> 4);
+ *pDest++ = _data->colourOffset + (*pSrc & 0xf);
+ ++pSrc;
+ }
+ }
+ }
+
+ delete src;
+ delete dest;
+}
+
+void Hotspot::copyTo(Surface *dest) {
+/*
+ int16 xPos = x();
+ int16 yPos = y();
+ uint16 hWidth = width();
+ uint16 hHeight = height();
+*/
+ int16 xPos = _data->startX;
+ int16 yPos = _data->startY;
+ uint16 hWidth = _data->width;
+ uint16 hHeight = _data->height;
+
+ Rect r(_frameNumber * hWidth, 0, (_frameNumber + 1) * hWidth - 1,
+ hHeight - 1);
+
+ if (yPos < 0) {
+ if (yPos + hHeight <= 0)
+ // Completely off screen, so don't display
+ return;
+
+ // Reduce the source rectangle to only the on-screen portion
+ r.top = -yPos;
+ yPos = 0;
+ }
+
+ if (xPos < 0) {
+ if (xPos + hWidth <= 0)
+ // Completely off screen, so don't display
+ return;
+
+ // Reduce the source rectangle to only the on-screen portion
+ r.left = -xPos;
+ xPos = 0;
+ }
+
+ if (xPos >= FULL_SCREEN_WIDTH)
+ return;
+ else if (xPos + hWidth > FULL_SCREEN_WIDTH)
+ r.right = (_frameNumber * hWidth) + (FULL_SCREEN_WIDTH - xPos) - 1;
+ if (yPos >= FULL_SCREEN_HEIGHT)
+ return;
+ else if (yPos + hHeight > FULL_SCREEN_HEIGHT)
+ r.bottom = FULL_SCREEN_HEIGHT - yPos - 1;
+
+ _frames->copyTo(dest, r, (uint16) xPos, (uint16) yPos, _data->colourOffset);
+}
+
+void Hotspot::incFrameNumber() {
+ ++_frameNumber;
+ if (_frameNumber >= _numFrames)
+ _frameNumber = 0;
+}
+
+bool Hotspot::isActiveAnimation() {
+ return ((_numFrames != 0) && (_data->layer != 0));
+}
+
+void Hotspot::setPosition(int16 newX, int16 newY) {
+ _startX = newX;
+ _startY = newY;
+ _data->startX = newX;
+ _data->startY = newY;
+}
+
+void Hotspot::setSize(uint16 newWidth, uint16 newHeight) {
+ _width = newWidth;
+ _height = newHeight;
+}
+
+bool Hotspot::executeScript() {
+ if (_data->sequenceOffset == 0)
+ return false;
+ else
+ return HotspotScript::execute(this);
+}
+
+void Hotspot::tick() {
+ _tickHandler(*this);
+}
+
+void Hotspot::setTickProc(uint16 newVal) {
+ _data->tickProcOffset = newVal;
+ _tickHandler = HotspotTickHandlers::getHandler(newVal);
+}
+
+
+void Hotspot::walkTo(int16 endPosX, int16 endPosY, uint16 destHotspot, bool immediate) {
+ _destX = endPosX;
+ _destY = endPosY - _data->height;
+
+ _destHotspotId = destHotspot;
+ if (immediate)
+ setPosition(_destX, _destY);
+}
+
+void Hotspot::setDirection(Direction dir) {
+ switch (dir) {
+ case UP:
+ setFrameNumber(_anim->upFrame);
+ break;
+ case DOWN:
+ setFrameNumber(_anim->downFrame);
+ break;
+ case LEFT:
+ setFrameNumber(_anim->leftFrame);
+ break;
+ case RIGHT:
+ setFrameNumber(_anim->rightFrame);
+ break;
+ default:
+ break;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/* Hotspot action handling */
+/* */
+/*-------------------------------------------------------------------------*/
+
+uint16 validRoomExitHotspots[] = {0x2711, 0x2712, 0x2714, 0x2715, 0x2716, 0x2717,
+ 0x2718, 0x2719, 0x271A, 0x271E, 0x271F, 0x2720, 0x2721, 0x2722, 0x2725, 0x2726,
+ 0x2729, 0x272A, 0x272B, 0x272C, 0x272D, 0x272E, 0x272F, 0};
+
+bool Hotspot::isRoomExit(uint16 id) {
+ for (uint16 *p = &validRoomExitHotspots[0]; *p != 0; ++p)
+ if (*p == id) return true;
+ return false;
+}
+
+void Hotspot::doAction(Action action, HotspotData *hotspot) {
+ switch (action) {
+ case GET:
+ doGet(hotspot);
+ break;
+ case PUSH:
+ case PULL:
+ case OPERATE:
+ doOperate(hotspot, action);
+ break;
+ case OPEN:
+ doOpen(hotspot);
+ break;
+ case CLOSE:
+ doClose(hotspot);
+ break;
+ case LOCK:
+ doSimple(hotspot, LOCK);
+ break;
+ case UNLOCK:
+ doSimple(hotspot, UNLOCK);
+ break;
+ case USE:
+ doUse(hotspot);
+ break;
+ case GIVE:
+ doGive(hotspot);
+ break;
+ case TALK_TO:
+ doTalkTo(hotspot);
+ break;
+ case TELL:
+ doTell(hotspot);
+ break;
+ case LOOK:
+ doLook();
+ break;
+ case LOOK_AT:
+ doLookAt(hotspot);
+ break;
+ case LOOK_THROUGH:
+ doSimple(hotspot, LOOK_THROUGH);
+ break;
+ case ASK:
+ doAsk(hotspot);
+ break;
+ case DRINK:
+ doDrink();
+ break;
+ case STATUS:
+ doStatus();
+ break;
+ case BRIBE:
+ doBribe(hotspot);
+ break;
+ case EXAMINE:
+ doExamine();
+ break;
+ default:
+ doSimple(hotspot, action);
+ break;
+ }
+}
+
+void Hotspot::doGet(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, GET);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ return;
+ }
+
+ if (sequenceOffset != 0) {
+ uint16 result = Script::execute(sequenceOffset);
+
+ if (result == 1) return;
+ else if (result != 0) {
+ Dialog::showMessage(result, hotspotId());
+ return;
+ }
+ }
+
+ // Move hotspot into characters's inventory
+ hotspot->roomNumber = hotspotId();
+
+ if (hotspot->hotspotId < START_NONVISUAL_HOTSPOT_ID) {
+ // Deactive hotspot animation
+ Resources::getReference().deactivateHotspot(hotspot->hotspotId);
+ // Remove any 'on the ground' description for the hotspot
+ hotspot->descId2 = 0;
+ }
+}
+
+void Hotspot::doOperate(HotspotData *hotspot, Action action) {
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, action);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ uint16 result = Script::execute(sequenceOffset);
+ if (result > 1)
+ Dialog::showMessage(result, hotspotId());
+ }
+}
+
+void Hotspot::doOpen(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ RoomExitJoinData *joinRec;
+
+ if (isRoomExit(hotspot->hotspotId)) {
+ joinRec = res.getExitJoin(hotspot->hotspotId);
+ if (!joinRec->blocked) {
+ // Room exit is already open
+ Dialog::showMessage(4, hotspotId());
+ // TODO: jmp loc_1102
+ return;
+ }
+ }
+
+ // TODO: Call to sub_107 and checking the results, then sub_110
+
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, OPEN);
+ if (sequenceOffset >= 0x8000) {
+ // Message to display
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ // Otherwise handle script
+ uint16 result = Script::execute(sequenceOffset);
+
+ if (result == 0) {
+ joinRec = res.getExitJoin(hotspot->hotspotId);
+ if (joinRec->blocked) {
+ joinRec->blocked = 0;
+
+ if (hotspotId() != PLAYER_ID) {
+ // TODO: HS[44h]=3, HS[42h]W = 4
+ }
+ }
+ } else if (result != 1) {
+ // TODO: Figure out: if Hotspot-rec[60h] != 0, then set = 4
+ Dialog::showMessage(result, hotspotId());
+ }
+ }
+}
+
+void Hotspot::doClose(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ RoomExitJoinData *joinRec;
+
+ if (isRoomExit(hotspot->hotspotId)) {
+ joinRec = res.getExitJoin(hotspot->hotspotId);
+ if (joinRec->blocked) {
+ // Room exit is already closed/blocked
+ Dialog::showMessage(3, hotspotId());
+ // TODO: jmp sub_129
+ return;
+ }
+ }
+
+ // TODO: Call to sub_107 and checking the results, then sub_110
+
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, CLOSE);
+
+ if (sequenceOffset >= 0x8000) {
+ // Message to display
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ // Otherwise handle script
+ uint16 result = Script::execute(sequenceOffset);
+
+ if (result != 0) {
+ Dialog::showMessage(result, hotspotId());
+ } else {
+ joinRec = res.getExitJoin(hotspot->hotspotId);
+ if (!joinRec->blocked) {
+ // Close the door
+ // TODO: Decode sub_183 - does check to see if door is 'jammed', but
+ // a cursory inspection seems to indicate that the routine is more
+ // concerned with checking if any character is blocking the door
+// if (!sub183(joinRec->0Dh) || !sub183(joinRec->0Fh)) {
+// Dialog::showMessage(2, hotspotId());
+// } else {
+ joinRec->blocked = 1;
+// }
+ }
+ }
+ }
+}
+
+void Hotspot::doUse(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+// uint16 usedId = res.fieldList().getField(USE_HOTSPOT_ID);
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, USE);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset == 0) {
+ Dialog::showMessage(17, hotspotId());
+ } else {
+ uint16 result = Script::execute(sequenceOffset);
+ if (result != 0)
+ Dialog::showMessage(result, hotspotId());
+ }
+}
+
+void Hotspot::doGive(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ uint16 usedId = res.fieldList().getField(USE_HOTSPOT_ID);
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, GIVE);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else {
+ uint16 result = Script::execute(sequenceOffset);
+ if (result == 0x3E7) {
+ // TODO
+ } else if (result == 0) {
+ // Move item into character's inventory
+ HotspotData *usedItem = res.getHotspot(usedId);
+ usedItem->roomNumber = hotspotId();
+ } else if (result > 1) {
+ // TODO
+ }
+ }
+}
+
+void Hotspot::doTalkTo(HotspotData *hotspot) {
+ // TODO: extra checking at start
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, TALK_TO);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ uint16 result = Script::execute(sequenceOffset);
+
+ if (result == 0) {
+ // Do talking with character
+ // TODO
+ Dialog::show("Still need to figure out talking");
+ }
+ }
+}
+
+void Hotspot::doTell(HotspotData *hotspot) {
+ // TODO
+}
+
+void Hotspot::doLook() {
+ Dialog::show(Room::getReference().descId());
+}
+
+void Hotspot::doLookAt(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, LOOK_AT);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else {
+ if (sequenceOffset != 0)
+ sequenceOffset = Script::execute(sequenceOffset);
+
+ if (sequenceOffset == 0) {
+ uint16 descId = (hotspot->descId2 != 0) ? hotspot->descId2 : hotspot->descId;
+ Dialog::show(descId);
+ }
+ }
+}
+
+void Hotspot::doAsk(HotspotData *hotspot) {
+ // TODO
+}
+
+void Hotspot::doDrink() {
+ Resources &res = Resources::getReference();
+ uint16 usedId = res.fieldList().getField(USE_HOTSPOT_ID);
+ HotspotData *hotspot = res.getHotspot(usedId);
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, DRINK);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset == 0) {
+ Dialog::showMessage(22, hotspotId());
+ } else {
+ uint16 result = Script::execute(sequenceOffset);
+ if (result == 0) {
+ // Item has been drunk, so remove item from game
+ hotspot->roomNumber = 0;
+ } else if (result != 1) {
+ Dialog::showMessage(result, hotspotId());
+ }
+ }
+}
+
+// doStatus
+// Handle the status window
+
+void Hotspot::doStatus() {
+ char buffer[MAX_DESC_SIZE];
+ uint16 numItems = 0;
+ StringData &strings = StringData::getReference();
+ Resources &resources = Resources::getReference();
+ Room &room = Room::getReference();
+
+ strings.getString(room.roomNumber(), buffer, NULL, NULL);
+ strcat(buffer, "\n\nYou are carrying ");
+
+ // Scan through the list and add in any items assigned to the player
+ HotspotDataList &list = resources.hotspotData();
+ HotspotDataList::iterator i;
+ for (i = list.begin(); i != list.end(); ++i) {
+ HotspotData *rec = *i;
+
+ if (rec->roomNumber == PLAYER_ID) {
+ if (numItems++ == 0) strcat(buffer, ": ");
+ else strcat(buffer, ", ");
+ strings.getString(rec->nameId, buffer + strlen(buffer), NULL, NULL);
+ }
+ }
+
+ // If there were no items, add in the word 'nothing'
+ if (numItems == 0) strcat(buffer, "nothing.");
+
+ // If the player has money, add it in
+ // TODO
+
+ // Display the dialog
+ Screen &screen = Screen::getReference();
+ Mouse &mouse = Mouse::getReference();
+ mouse.cursorOff();
+
+ Surface *s = Surface::newDialog(INFO_DIALOG_WIDTH, buffer);
+ s->copyToScreen(INFO_DIALOG_X, (FULL_SCREEN_HEIGHT-s->height())/2);
+
+ Events::getReference().waitForPress();
+ screen.update();
+ mouse.cursorOn();
+}
+
+void Hotspot::doBribe(HotspotData *hotspot) {
+ // TODO
+}
+
+void Hotspot::doExamine() {
+ Resources &res = Resources::getReference();
+ uint16 usedId = res.fieldList().getField(USE_HOTSPOT_ID);
+ HotspotData *hotspot = res.getHotspot(usedId);
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, EXAMINE);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else {
+ if (sequenceOffset != 0)
+ sequenceOffset = Script::execute(sequenceOffset);
+
+ if (sequenceOffset == 0) {
+ Dialog::show(hotspot->descId);
+ }
+ }
+}
+
+void Hotspot::doSimple(HotspotData *hotspot, Action action) {
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, action);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ Script::execute(sequenceOffset);
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+HandlerMethodPtr HotspotTickHandlers::getHandler(uint16 procOffset) {
+ switch (procOffset) {
+ case 0x7F3A:
+ return standardAnimHandler;
+ case 0x7207:
+ return roomExitAnimHandler;
+ case 0x5e44:
+ return playerAnimHandler;
+ case 0x7F69:
+ return droppingTorchAnimHandler;
+ case 0x8009:
+ return fireAnimHandler;
+ case 0x8241:
+ return headAnimationHandler;
+ default:
+ return defaultHandler;
+ }
+}
+
+void HotspotTickHandlers::defaultHandler(Hotspot &h) {
+ // No handling done
+}
+
+void HotspotTickHandlers::standardAnimHandler(Hotspot &h) {
+ if (h.tickCtr() > 0)
+ h.setTickCtr(h.tickCtr() - 1);
+ else
+ h.executeScript();
+}
+
+void HotspotTickHandlers::roomExitAnimHandler(Hotspot &h) {
+ RoomExitJoinData *rec = Resources::getReference().getExitJoin(h.hotspotId());
+ if (!rec) return;
+ byte *currentFrame, *destFrame;
+
+ if (rec->hotspot1Id == h.hotspotId()) {
+ currentFrame = &rec->h1CurrentFrame;
+ destFrame = &rec->h1DestFrame;
+ } else {
+ currentFrame = &rec->h2CurrentFrame;
+ destFrame = &rec->h2DestFrame;
+ }
+
+ if ((rec->blocked != 0) && (*currentFrame != *destFrame)) {
+ // sub_178
+
+ ++*currentFrame;
+ if (*currentFrame != *destFrame) {
+ // cx=1 => sub_184
+ }
+ } else if ((rec->blocked == 0) && (*currentFrame != 0)) {
+ // sub_179
+ if (*currentFrame == *destFrame) {
+ // sub_184 and other stuff TODO
+ }
+ --*currentFrame;
+ }
+
+ h.setFrameNumber(*currentFrame);
+}
+
+void HotspotTickHandlers::playerAnimHandler(Hotspot &h) {
+ int16 xPos = h.x();
+ int16 yPos = h.y();
+ if ((xPos == h.destX()) && (yPos == h.destY())) return;
+ HotspotAnimData &anim = h.anim();
+ int16 xDiff = h.destX() - h.x();
+ int16 yDiff = h.destY() - h.y();
+
+ int16 xChange, yChange;
+ uint16 nextFrame;
+ MovementDataList *moves;
+
+ if ((yDiff < 0) && (xDiff <= 0)) moves = &anim.upFrames;
+ else if (xDiff < 0) moves = &anim.leftFrames;
+ else if (yDiff > 0) moves = &anim.downFrames;
+ else moves = &anim.rightFrames;
+
+ // Get movement amount and next frame number
+ moves->getFrame(h.frameNumber(), xChange, yChange, nextFrame);
+ xPos += xChange; yPos += yChange;
+
+ // Make sure that the move amount doesn't overstep the destination X/Y
+ if ((yDiff < 0) && (yPos < h.destY())) yPos = h.destY();
+ else if ((xDiff < 0) && (xPos < h.destX())) xPos = h.destX();
+ else if ((yDiff > 0) && (yPos > h.destY())) yPos = h.destY();
+ else if ((xDiff > 0) && (xPos > h.destX())) xPos = h.destX();
+
+ // Check to see if player has entered an exit area
+ RoomData *roomData = Resources::getReference().getRoom(h.roomNumber());
+ Room &room = Room::getReference();
+ bool charInRoom = room.roomNumber() == h.roomNumber();
+ RoomExitData *exitRec = roomData->exits.checkExits(xPos, yPos + h.height());
+
+ if (!exitRec) {
+ h.setPosition(xPos, yPos);
+ h.setFrameNumber(nextFrame);
+ } else {
+ h.setRoomNumber(exitRec->roomNumber);
+ h.walkTo(exitRec->x, exitRec->y, 0, true);
+ if (exitRec->direction != NO_DIRECTION)
+ h.setDirection(exitRec->direction);
+ if (charInRoom)
+ room.setRoomNumber(exitRec->roomNumber, false);
+ }
+}
+
+void HotspotTickHandlers::droppingTorchAnimHandler(Hotspot &h) {
+ if (h.tickCtr() > 0)
+ h.setTickCtr(h.tickCtr() - 1);
+ else {
+ bool result = h.executeScript();
+ if (result) {
+ // Changeover to the fire on the straw
+ Resources &res = Resources::getReference();
+ res.deactivateHotspot(h.hotspotId());
+ res.activateHotspot(0x41C);
+
+ // Enable the fire and activate it's animation
+ HotspotData *fire = res.getHotspot(0x418);
+ fire->flags |= 0x80;
+ fire->loadOffset = 0x7172;
+ res.activateHotspot(0x418);
+ }
+ }
+}
+
+void HotspotTickHandlers::fireAnimHandler(Hotspot &h) {
+ standardAnimHandler(h);
+ // TODO: figure out remainder of method
+}
+
+void HotspotTickHandlers::headAnimationHandler(Hotspot &h) {
+ Resources &res = Resources::getReference();
+ Hotspot *character = res.getActiveHotspot(PLAYER_ID);
+ uint16 frameNumber = 0;
+
+ if (character->y() < 79) {
+ //character = res.getActiveHotspot(RATPOUCH_ID);
+ frameNumber = 1;
+ } else {
+ if (character->x() < 72) frameNumber = 0;
+ else if (character->x() < 172) frameNumber = 1;
+ else frameNumber = 2;
+ }
+
+ h.setFrameNumber(frameNumber);
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/hotspots.h b/engines/lure/hotspots.h
new file mode 100644
index 0000000000..3483522c96
--- /dev/null
+++ b/engines/lure/hotspots.h
@@ -0,0 +1,137 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_hotspots_h__
+#define __lure_hotspots_h__
+
+#include "lure/luredefs.h"
+#include "lure/screen.h"
+#include "lure/disk.h"
+#include "lure/res_struct.h"
+
+namespace Lure {
+
+class Hotspot;
+
+typedef void(*HandlerMethodPtr)(Hotspot &h);
+
+class HotspotTickHandlers {
+private:
+ static void defaultHandler(Hotspot &h);
+ static void standardAnimHandler(Hotspot &h);
+ static void roomExitAnimHandler(Hotspot &h);
+ static void playerAnimHandler(Hotspot &h);
+ static void droppingTorchAnimHandler(Hotspot &h);
+ static void fireAnimHandler(Hotspot &h);
+ static void headAnimationHandler(Hotspot &h);
+
+public:
+ static HandlerMethodPtr getHandler(uint16 procOffset);
+};
+
+
+class Hotspot {
+private:
+ HotspotData *_data;
+ HotspotAnimData *_anim;
+ HandlerMethodPtr _tickHandler;
+ Surface *_frames;
+ int16 _startX, _startY;
+ uint16 _height, _width;
+ uint16 _numFrames;
+ uint16 _frameNumber;
+ uint16 _tickCtr;
+ bool _persistant;
+
+ int16 _destX, _destY;
+ uint16 _destHotspotId;
+public:
+ Hotspot(HotspotData *res);
+ ~Hotspot();
+
+ void setAnimation(uint16 newAnimId);
+ void setAnimation(HotspotAnimData *newRecord);
+ uint16 hotspotId() { return _data->hotspotId; }
+ Surface &frames() { return *_frames; }
+ HotspotAnimData &anim() { return *_anim; }
+ HotspotData &resource() { return *_data; }
+ uint16 numFrames() { return _numFrames; }
+ uint16 frameNumber() { return _frameNumber; }
+ void setFrameNumber(uint16 v) { _frameNumber = v; }
+ void incFrameNumber();
+ uint16 frameWidth() { return _width; }
+ int16 x() { return _startX; }
+ int16 y() { return _startY; }
+ int16 destX() { return _destX; }
+ int16 destY() { return _destY; }
+ uint16 destHotspotId() { return _destHotspotId; }
+ uint16 width() { return _width; }
+ uint16 height() { return _height; }
+ uint16 roomNumber() { return _data->roomNumber; }
+ uint16 script() { return _data->sequenceOffset; }
+ uint8 layer() { return _data->layer; }
+ uint16 tickCtr() { return _tickCtr; }
+ void setTickCtr(uint16 newVal) { _tickCtr = newVal; }
+ void setTickProc(uint16 newVal);
+ bool persistant() { return _persistant; }
+ void setPersistant(bool value) { _persistant = value; }
+ void setRoomNumber(uint16 roomNum) { _data->roomNumber = roomNum; }
+ bool isActiveAnimation();
+ void setPosition(int16 newX, int16 newY);
+ void setDestPosition(int16 newX, int16 newY) { _destX = newX; _destY = newY; }
+ void setSize(uint16 newWidth, uint16 newHeight);
+ void setScript(uint16 offset) { _data->sequenceOffset = offset; }
+ void setActions(uint32 newActions) { _data->actions = newActions; }
+
+ void copyTo(Surface *dest);
+ bool executeScript();
+ void tick();
+ void walkTo(int16 endPosX, int16 endPosY, uint16 destHotspot = 0, bool immediate = false);
+ void setDirection(Direction dir);
+
+ // Action set
+ void doAction(Action action, HotspotData *hotspot);
+ bool isRoomExit(uint16 id);
+ void doGet(HotspotData *hotspot);
+ void doOperate(HotspotData *hotspot, Action action);
+ void doOpen(HotspotData *hotspot);
+ void doClose(HotspotData *hotspot);
+ void doLockUnlock(HotspotData *hotspot);
+ void doUse(HotspotData *hotspot);
+ void doGive(HotspotData *hotspot);
+ void doTalkTo(HotspotData *hotspot);
+ void doTell(HotspotData *hotspot);
+ void doLook();
+ void doLookAt(HotspotData *hotspot);
+ void doAsk(HotspotData *hotspot);
+ void doDrink();
+ void doStatus();
+ void doBribe(HotspotData *hotspot);
+ void doExamine();
+ void doSimple(HotspotData *hotspot, Action action);
+};
+
+typedef ManagedList<Hotspot *> HotspotList;
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/intro.cpp b/engines/lure/intro.cpp
new file mode 100644
index 0000000000..58bca80239
--- /dev/null
+++ b/engines/lure/intro.cpp
@@ -0,0 +1,151 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/intro.h"
+#include "lure/animseq.h"
+#include "lure/events.h"
+
+namespace Lure {
+
+struct AnimRecord {
+ uint16 resourceId;
+ uint8 paletteIndex;
+ bool initialPause;
+ bool endingPause;
+};
+
+static const uint16 start_screens[] = {0x18, 0x1A, 0x1E, 0x1C, 0};
+static const AnimRecord anim_screens[] = {{0x40, 0, true, true}, {0x42, 1, false, true},
+ {0x44, 2, false, false}, {0x24, 3, false, true}, {0x46, 3, false, false},
+ {0, 0, false, false}};
+
+// showScreen
+// Shows a screen by loading it from the given resource, and then fading it in
+// with a palette in the following resource. Returns true if the introduction
+// should be aborted
+
+bool Introduction::showScreen(uint16 screenId, uint16 paletteId, uint16 delaySize) {
+ _screen.screen().loadScreen(screenId);
+ _screen.update();
+ Palette p(paletteId);
+ _screen.paletteFadeIn(&p);
+
+ bool result = delay(delaySize);
+ if (Events::getReference().quitFlag) return true;
+
+ _screen.paletteFadeOut();
+ return result;
+}
+
+// delay
+// Delays for a given number of milliseconds. If it returns true, it indicates that
+// Escape has been pressed, and the introduction should be aborted.
+
+bool Introduction::delay(uint32 milliseconds) {
+ Events &events = Events::getReference();
+ uint32 delayCtr = _system.getMillis() + milliseconds;
+
+ while (_system.getMillis() < delayCtr) {
+ if (events.quitFlag) return true;
+
+ if (events.pollEvent()) {
+ if (events.type() == OSystem::EVENT_KEYDOWN)
+ return events.event().kbd.keycode == 27;
+ else if (events.type() == OSystem::EVENT_LBUTTONDOWN)
+ return false;
+ }
+
+ uint32 delayAmount = delayCtr - _system.getMillis();
+ if (delayAmount > 10) delayAmount = 10;
+ _system.delayMillis(delayAmount);
+ }
+ return false;
+}
+
+// show
+// Main method for the introduction sequence
+
+bool Introduction::show() {
+ _screen.setPaletteEmpty();
+
+ // Initial game company and then game screen
+
+ for (int ctr = 0; start_screens[ctr]; ++ctr)
+ if (showScreen(start_screens[ctr], start_screens[ctr] + 1, 5000))
+ return true;
+
+ AnimationSequence *anim;
+ bool result;
+
+ // Animated screens
+
+ PaletteCollection coll(0x32);
+ const AnimRecord *curr_anim = anim_screens;
+ for (; curr_anim->resourceId; ++curr_anim)
+ {
+ bool fadeIn = curr_anim == anim_screens;
+ anim = new AnimationSequence(_screen, _system, curr_anim->resourceId,
+ coll.getPalette(curr_anim->paletteIndex), fadeIn);
+ if (curr_anim->initialPause)
+ if (delay(12000)) return true;
+
+ result = false;
+ switch (anim->show()) {
+ case ABORT_NONE:
+ if (curr_anim->endingPause) {
+ result = delay(12000);
+ }
+ break;
+
+ case ABORT_END_INTRO:
+ result = true;
+ break;
+
+ case ABORT_NEXT_SCENE:
+ break;
+ }
+ delete anim;
+
+ if (result) return true;
+ }
+
+ // Show battle pictures one frame at a time
+
+ result = false;
+ anim = new AnimationSequence(_screen, _system, 0x48, coll.getPalette(4), false);
+ do {
+ result = delay(2000);
+ _screen.paletteFadeOut();
+ if (!result) result = delay(500);
+ if (result) break;
+ } while (anim->step());
+ delete anim;
+ if (result) return true;
+
+ // Show final introduction screen
+
+ showScreen(0x22, 0x21, 10000);
+
+ return false;
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/intro.h b/engines/lure/intro.h
new file mode 100644
index 0000000000..d7934d938a
--- /dev/null
+++ b/engines/lure/intro.h
@@ -0,0 +1,45 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_intro_h__
+#define __lure_intro_h__
+
+#include "lure/screen.h"
+
+namespace Lure {
+
+class Introduction {
+private:
+ Screen &_screen;
+ OSystem &_system;
+
+ bool showScreen(uint16 screenId, uint16 paletteId, uint16 delaySize);
+ bool delay(uint32 milliseconds);
+public:
+ Introduction(Screen &screen, OSystem &system): _screen(screen), _system(system) {};
+
+ bool show();
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/lure.cpp b/engines/lure/lure.cpp
new file mode 100644
index 0000000000..3adc27eea9
--- /dev/null
+++ b/engines/lure/lure.cpp
@@ -0,0 +1,306 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/md5.h"
+
+#include "sound/mixer.h"
+#include "sound/mididrv.h"
+#include "sound/audiostream.h"
+
+#include "lure/luredefs.h"
+#include "lure/surface.h"
+#include "lure/lure.h"
+#include "lure/intro.h"
+#include "lure/game.h"
+#include "lure/system.h"
+
+using namespace Lure;
+
+enum {
+ // We only compute MD5 of the first megabyte of our data files.
+ kMD5FileSizeLimit = 1024 * 1024
+};
+
+struct LureGameSettings {
+ const char *name;
+ const char *description;
+ byte id;
+ uint32 features;
+ const char *md5sum;
+ const char *checkFile;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { name, description, features };
+ return dummy;
+ }
+};
+
+//
+static const LureGameSettings lure_games[] = {
+ { "lure", "Lure of the Temptress (Floppy, English)", GI_LURE, GF_ENGLISH | GF_FLOPPY,
+ "e45ea5d279a268c7d3c6524c2f63a2d2", "disk1.vga" },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+// Keep list of different supported games
+
+struct LureGameList {
+ const char *name;
+ const char *description;
+ uint32 features;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { name, description, features };
+ return dummy;
+ }
+};
+
+static const LureGameList lure_list[] = {
+ { "lure", "Lure of the Temptress", 0 },
+ { 0, 0, 0 }
+};
+
+GameList Engine_LURE_gameList() {
+ GameList games;
+ const LureGameList *g = lure_list;
+
+ while (g->name) {
+ games.push_back(g->toGameSettings());
+ g++;
+ }
+ return games;
+}
+
+DetectedGameList Engine_LURE_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+ const LureGameSettings *g;
+ FSList::const_iterator file;
+
+ // Iterate over all files in the given directory
+ bool isFound = false;
+ for (file = fslist.begin(); file != fslist.end(); file++) {
+ if (file->isDirectory())
+ continue;
+
+ for (g = lure_games; g->name; g++) {
+ if (scumm_stricmp(file->displayName().c_str(), g->checkFile) == 0)
+ isFound = true;
+ }
+ if (isFound)
+ break;
+ }
+
+ if (file == fslist.end())
+ return detectedGames;
+
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+
+ if (Common::md5_file(file->path().c_str(), md5sum, NULL, kMD5FileSizeLimit)) {
+ for (int i = 0; i < 16; i++) {
+ sprintf(md5str + i * 2, "%02x", (int)md5sum[i]);
+ }
+ for (g = lure_games; g->name; g++) {
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ detectedGames.push_back(g->toGameSettings());
+ }
+ }
+ if (detectedGames.isEmpty()) {
+ debug("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team\n", md5str);
+
+ const LureGameList *g1 = lure_list;
+ while (g1->name) {
+ detectedGames.push_back(g1->toGameSettings());
+ g1++;
+ }
+ }
+ }
+ return detectedGames;
+}
+
+Engine *Engine_LURE_create(GameDetector *detector, OSystem *system) {
+ return new LureEngine(detector, system);
+}
+
+REGISTER_PLUGIN(LURE, "Lure of the Temptress Engine")
+
+namespace Lure {
+
+LureEngine::LureEngine(GameDetector *detector, OSystem *system): Engine(system) {
+ // Setup mixer
+/*
+ if (!_mixer->isReady()) {
+ warning("Sound initialization failed.");
+ }
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
+*/
+ _features = 0;
+ _game = 0;
+}
+
+void LureEngine::detectGame() {
+ // Make sure all the needed files are present
+
+ if (!Common::File::exists(SUPPORT_FILENAME))
+ error("Missing %s - this is a custom file containing resources from the\n"
+ "Lure of the Temptress executable. See the documentation for creating it.",
+ SUPPORT_FILENAME);
+
+ for (uint8 fileNum = 1; fileNum <= 4; ++fileNum)
+ {
+ char sFilename[10];
+ sprintf(sFilename, "disk%d.vga", fileNum);
+
+ if (!Common::File::exists(sFilename))
+ error("Missing disk%d.vga", fileNum);
+ }
+
+ // Check the version of the lure.dat file
+ Common::File f;
+ if (!f.open(SUPPORT_FILENAME)) {
+ error("Error opening %s for validation", SUPPORT_FILENAME);
+ } else {
+ f.seek(0xbf * 8);
+ VersionStructure version;
+ f.read(&version, sizeof(VersionStructure));
+ f.close();
+
+ if (READ_LE_UINT16(&version.id) != 0xffff)
+ error("Error validating %s - file is invalid or out of date", SUPPORT_FILENAME);
+ else if ((version.vMajor != LURE_DAT_MAJOR) || (version.vMinor != LURE_DAT_MINOR))
+ error("Incorrect version of %s file - expected %d.%d but got %d.%d",
+ SUPPORT_FILENAME, LURE_DAT_MAJOR, LURE_DAT_MINOR,
+ version.vMajor, version.vMinor);
+ }
+
+ // Do an md5 check
+
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+ const LureGameSettings *g;
+ bool found = false;
+
+ *md5str = 0;
+
+ for (g = lure_games; g->name; g++) {
+ if (!Common::File::exists(g->checkFile))
+ continue;
+
+ if (Common::md5_file(g->checkFile, md5sum, ConfMan.get("path").c_str(), kMD5FileSizeLimit)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j * 2, "%02x", (int)md5sum[j]);
+ }
+ } else
+ continue;
+
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ _features = g->features;
+ _game = g->id;
+
+ if (g->description)
+ g_system->setWindowCaption(g->description);
+
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ debug("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team", md5str);
+ _features = GF_LNGUNK || GF_FLOPPY;
+ _game = GI_LURE;
+ }
+}
+
+int LureEngine::init(GameDetector &detector) {
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ _system->initSize(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+ _system->endGFXTransaction();
+
+ detectGame();
+
+ _sys = new System(_system);
+ _disk = new Disk(_gameDataPath);
+ _resources = new Resources();
+ _strings = new StringData();
+ _screen = new Screen(*_system);
+ _mouse = new Mouse(*_system);
+ _events = new Events(*_system, *_mouse);
+ _menu = new Menu(*_system);
+ Surface::initialise();
+ _room = new Room();
+
+ return 0;
+}
+
+LureEngine::~LureEngine() {
+ Surface::deinitialise();
+ delete _room;
+ delete _menu;
+ delete _events;
+ delete _mouse;
+ delete _screen;
+ delete _strings;
+ delete _resources;
+ delete _disk;
+ delete _sys;
+}
+
+int LureEngine::go() {
+ // Show the introduction
+ Introduction *intro = new Introduction(*_screen, *_system);
+ intro->show();
+ delete intro;
+
+ // Play the game
+ if (!_events->quitFlag) {
+ // Play the game
+ Game *gameInstance = new Game();
+ gameInstance->execute();
+ delete gameInstance;
+ }
+
+ //quitGame();
+ return 0;
+}
+
+void LureEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+}
+
+void LureEngine::quitGame() {
+ _system->quit();
+}
+
+} // End of namespace Lure
diff --git a/engines/lure/lure.h b/engines/lure/lure.h
new file mode 100644
index 0000000000..0a923cb72b
--- /dev/null
+++ b/engines/lure/lure.h
@@ -0,0 +1,73 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __LURE_H__
+#define __LURE_H__
+
+#include "base/engine.h"
+#include "common/rect.h"
+#include "sound/mixer.h"
+#include "common/file.h"
+
+#include "lure/disk.h"
+#include "lure/res.h"
+#include "lure/screen.h"
+#include "lure/events.h"
+#include "lure/menu.h"
+#include "lure/system.h"
+#include "lure/strings.h"
+#include "lure/room.h"
+
+namespace Lure {
+
+class LureEngine : public Engine {
+private:
+ uint32 _features;
+ uint8 _game;
+ Disk *_disk;
+ Resources *_resources;
+ Screen *_screen;
+ Mouse *_mouse;
+ Events *_events;
+ Menu *_menu;
+ System *_sys;
+ StringData *_strings;
+ Room *_room;
+
+ void detectGame();
+public:
+ LureEngine(GameDetector *detector, OSystem *system);
+ ~LureEngine();
+
+ virtual int init(GameDetector &detector);
+ virtual int go();
+ virtual void errorString(const char *buf_input, char *buf_output);
+ void quitGame();
+
+ uint32 features() { return _features; }
+ uint8 game() { return _game; }
+ Disk &disk() { return *_disk; }
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/luredefs.h b/engines/lure/luredefs.h
new file mode 100644
index 0000000000..d4175ce863
--- /dev/null
+++ b/engines/lure/luredefs.h
@@ -0,0 +1,183 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __luredefs_h__
+#define __luredefs_h__
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/list.h"
+
+namespace Lure {
+
+#define LURE_DEBUG 1
+
+#define READ_LE_INT16(x) (int16) READ_LE_UINT16(x)
+#define READ_LE_INT32(x) (int32) READ_LE_UINT32(x)
+
+enum {
+ GF_FLOPPY = 1 << 0,
+ GF_ENGLISH = 1 << 1,
+ GF_LNGUNK = 1 << 15
+};
+
+enum {
+ GI_LURE = 0
+};
+
+enum Action {
+ GET = 1,
+ DROP = 0,
+ PUSH = 3,
+ PULL = 4,
+ OPERATE = 5,
+ OPEN = 6,
+ CLOSE = 7,
+ LOCK = 8,
+ UNLOCK = 9,
+ USE = 10,
+ GIVE = 11,
+ TALK_TO = 12,
+ TELL = 13,
+ BUY = 14,
+ LOOK = 15,
+ LOOK_AT = 16,
+ LOOK_THROUGH = 17,
+ ASK = 18,
+ EAT = 0,
+ DRINK = 20,
+ STATUS = 21,
+ GO_TO = 22,
+ RETURN = 23,
+ BRIBE = 24,
+ EXAMINE = 25,
+ NONE = 0xffff
+};
+
+// Basic game dimensions
+#define FULL_SCREEN_WIDTH 320
+#define FULL_SCREEN_HEIGHT 200
+#define GAME_COLOURS 256
+#define SCREEN_SIZE (FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH)
+
+#define SUPPORT_FILENAME "lure.dat"
+#define LURE_DAT_MAJOR 1
+#define LURE_DAT_MINOR 1
+
+// Some resources include multiple packed palettes of 64 entries each
+#define SUB_PALETTE_SIZE 64
+// Palette resources have 220 palette entries
+#define RES_PALETTE_ENTRIES 220
+// Palette colour increment amouns for palette fade in/outs
+#define PALETTE_FADE_INC_SIZE 4
+
+// Specifies the maximum buffer sized allocated for decoding animation data
+#define MAX_ANIM_DECODER_BUFFER_SIZE 200000
+
+#define MAX_DESC_SIZE 1024
+#define MAX_HOTSPOT_NAME_SIZE 80
+#define MAX_ACTION_NAME_SIZE 15
+
+// Menubar constants
+#define MENUBAR_Y_SIZE 8
+
+// Cursor definitions
+#define CURSOR_WIDTH 16
+#define CURSOR_HEIGHT 16
+#define CURSOR_SIZE 256
+#define CURSOR_RESOURCE_ID 1
+#define CURSOR_ARROW 0
+#define CURSOR_DISK 1
+#define CURSOR_TIME_START 2
+#define CURSOR_TIME_END 9
+#define CURSOR_CROSS 10
+#define CURSOR_MENUBAR 17
+
+// Font details
+#define FONT_RESOURCE_ID 4
+#define NUM_CHARS_IN_FONT 122
+#define FONT_WIDTH 8
+#define FONT_HEIGHT 8
+
+// Menu constants
+#define MENUBAR_SELECTED_COLOUR 0xf7
+#define MENU_UNSELECTED_COLOUR 0xe2
+#define MENU_SELECTED_COLOUR 0xe3
+#define MENUITEM_NONE 0
+#define MENUITEM_CREDITS 1
+#define MENUITEM_RESTART_GAME 2
+#define MENUITEM_SAVE_GAME 3
+#define MENUITEM_RESTORE_GAME 4
+#define MENUITEM_QUIT 5
+#define MENUITEM_TEXT_SPEED 6
+#define MENUITEM_SOUND 7
+
+// Mouse change needed to change an item in a popup menu
+#define POPMENU_CHANGE_SENSITIVITY 5
+
+// Dialog related defines
+#define DIALOG_EDGE_SIZE 9
+#define DIALOG_TEXT_COLOUR 0xe2
+#define DIALOG_WHITE_COLOUR 0xe3
+#define INFO_DIALOG_X 69
+#define INFO_DIALOG_Y 61
+#define INFO_DIALOG_WIDTH 191
+
+// Strings defines
+#define STRINGS_RESOURCE_ID 0x10
+#define STRINGS_2_RESOURCE_ID 0x11
+#define STRINGS_3_RESOURCE_ID 0x12
+#define STRING_ID_RANGE 0x7d0
+#define STRING_ID_UPPER 0xfa0
+
+// Custom resources stored in lure.dat
+#define GAME_PALETTE_RESOURCE_ID 0x3f01
+#define ALT_PALETTE_RESOURCE_ID 0x3f02
+#define DIALOG_RESOURCE_ID 0x3f03
+#define ROOM_DATA_RESOURCE_ID 0x3f04
+#define HOTSPOT_DATA_RESOURCE_ID 0x3f05
+#define HOTSPOT_OVERRIDE_DATA_RESOURCE_ID 0x3f06
+#define ROOM_EXITS_RESOURCE_ID 0x3f07
+#define ROOM_EXIT_JOINS_RESOURCE_ID 0x3f08
+#define ANIM_DATA_RESOURCE_ID 0x3f09
+#define SCRIPT_DATA_RESOURCE_ID 0x3f0a
+#define SCRIPT2_DATA_RESOURCE_ID 0x3f0b
+#define HOTSPOT_SCRIPT_LIST_RESOURCE_ID 0x3f0c
+#define MESSAGES_LIST_RESOURCE_ID 0x3f0d
+#define ACTION_LIST_RESOURCE_ID 0x3f0e
+
+// Script constants
+#define STARTUP_SCRIPT 0x23FC
+
+// Miscellaneous resources
+#define CREDITS_RESOURCE_ID 0x7800
+#define NAMES_RESOURCE_ID 9
+#define PLAYER_ID 0x3E8
+#define RATPOUCH_ID 0x3E9
+#define START_NONVISUAL_HOTSPOT_ID 0x7530
+
+// Milliseconds delay between game frames
+#define GAME_FRAME_DELAY 100
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/memory.cpp b/engines/lure/memory.cpp
new file mode 100644
index 0000000000..2a24101dd3
--- /dev/null
+++ b/engines/lure/memory.cpp
@@ -0,0 +1,107 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/memory.h"
+#include "common/file.h"
+
+namespace Lure {
+
+MemoryBlock *Memory::allocate(uint32 size) {
+ MemoryBlock *block = new MemoryBlock(size);
+ return block;
+}
+
+MemoryBlock *Memory::duplicate(MemoryBlock *src) {
+ MemoryBlock *block = new MemoryBlock(src);
+ return block;
+}
+
+uint8 *Memory::alloc(uint32 size) {
+ return (uint8 *) malloc(size);
+}
+
+void Memory::dealloc(uint8 *block) {
+ free(block);
+}
+
+/*--------------------------------------------------------------------------*/
+
+MemoryBlock::MemoryBlock(uint32 size1) {
+ _data = (uint8 *) malloc(size1);
+ if (!_data) error ("Failed allocating memory block");
+ _size = size1;
+}
+
+MemoryBlock::MemoryBlock(MemoryBlock *src) {
+ _size = src->size();
+ _data = (uint8 *) malloc(_size);
+ if (!_data) error ("Failed allocating memory block");
+ memcpy(_data, src->data(), _size);
+}
+
+MemoryBlock::~MemoryBlock() {
+ free(_data);
+}
+
+void MemoryBlock::empty() {
+ ::memset(_data, 0, _size);
+}
+
+void MemoryBlock::memorySet(int c, size_t startIndex, size_t num) {
+ byte *p = _data + startIndex;
+ ::memset(p, c, num);
+}
+
+void MemoryBlock::copyFrom(MemoryBlock *src) {
+ copyFrom(src, 0, 0, src->size());
+}
+
+void MemoryBlock::copyFrom(MemoryBlock *src, uint32 srcPos, uint32 destPos, uint32 srcLen) {
+ if ((srcPos + srcLen > src->size()) || (destPos + srcLen > size()))
+ error("Memory block overrun in block copy");
+
+ uint8 *pDest = _data + destPos;
+ uint8 *pSrc = src->data() + srcPos;
+ memcpy(pDest, pSrc, srcLen);
+}
+
+void MemoryBlock::copyFrom(const byte *src, uint32 srcPos, uint32 destPos, uint32 srcLen) {
+ byte *pDest = _data + destPos;
+ const byte *pSrc = src + srcPos;
+ memcpy(pDest, pSrc, srcLen);
+}
+
+void MemoryBlock::reallocate(uint32 size1) {
+ _size = size1;
+ _data = (byte *) realloc(_data, size1);
+ if (!_data) error ("Failed reallocating memory block");
+}
+
+void MemoryBlock::saveToFile(const Common::String &filename) {
+ Common::File *f = new Common::File();
+ f->open(filename.c_str(), Common::File::kFileWriteMode);
+ f->write(_data, _size);
+ f->close();
+ delete f;
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/memory.h b/engines/lure/memory.h
new file mode 100644
index 0000000000..c2f735ca2b
--- /dev/null
+++ b/engines/lure/memory.h
@@ -0,0 +1,63 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_memory_h__
+#define __lure_memory_h__
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/str.h"
+
+namespace Lure {
+
+class MemoryBlock {
+private:
+ byte *_data;
+ uint32 _size;
+public:
+ MemoryBlock(uint32 size);
+ MemoryBlock(MemoryBlock *src);
+ ~MemoryBlock();
+
+ byte *data() { return _data; }
+ uint32 size() { return _size; }
+
+ void empty();
+ void memorySet(int c, size_t startIndex, size_t num);
+ void copyFrom(MemoryBlock *src);
+ void copyFrom(MemoryBlock *src, uint32 srcPos, uint32 destPos, uint32 srcLen);
+ void copyFrom(const byte *src, uint32 srcPos, uint32 destPos, uint32 srcLen);
+ void reallocate(uint32 size);
+ void saveToFile(const Common::String &filename);
+};
+
+class Memory {
+public:
+ static MemoryBlock *allocate(uint32 size);
+ static MemoryBlock *duplicate(MemoryBlock *src);
+ static uint8 *alloc(uint32 size);
+ static void dealloc(uint8 *block);
+};
+
+} // end of namspace Lure
+
+#endif
diff --git a/engines/lure/menu.cpp b/engines/lure/menu.cpp
new file mode 100644
index 0000000000..752b11f339
--- /dev/null
+++ b/engines/lure/menu.cpp
@@ -0,0 +1,416 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/menu.h"
+#include "lure/luredefs.h"
+#include "lure/decode.h"
+#include "lure/surface.h"
+#include "lure/system.h"
+#include "lure/res_struct.h"
+#include "lure/res.h"
+#include "lure/strings.h"
+
+namespace Lure {
+
+MenuRecord::MenuRecord(uint16 hsxstartVal, uint16 hsxendVal, uint16 xstartVal,
+ uint16 widthVal, const char *strings) {
+ _xstart = xstartVal; _width = widthVal;
+ _hsxstart = hsxstartVal; _hsxend = hsxendVal;
+
+ // Figure out the number of entries
+ const char *sPtr = strings;
+ _numEntries = 1;
+ while ((sPtr = strchr(sPtr, ',')) != NULL) {
+ ++_numEntries;
+ ++sPtr;
+ }
+
+ // Set up the list of entries
+ char *sCopy = strdup(strings);
+ char *s;
+ _entries = (char **) malloc(sizeof(char *) * _numEntries);
+ uint8 index = 0;
+ s = sCopy;
+ while (s != NULL) {
+ _entries[index++] = s;
+ s = strchr(s, ',');
+ if (s != NULL) *s++ = '\0'; // replace comma with NULL
+ }
+}
+
+MenuRecord::~MenuRecord() {
+ delete _entries[0]; // Delete string data for all the menu items
+ free(_entries); // Free the list
+}
+
+char *MenuRecord::getEntry(uint8 index) {
+ if (index >= _numEntries) error("Invalid menuitem index specified: %d", index);
+ return _entries[index];
+}
+
+/*--------------------------------------------------------------------------*/
+
+static Menu *int_menu = NULL;
+
+Menu::Menu(OSystem &system): _system(system), _screen(Screen::getReference()),
+ _events(Events::getReference()), _mouse(Mouse::getReference()) {
+ int_menu = this;
+
+ MemoryBlock *res = Disk::getReference().getEntry(5);
+ PictureDecoder decoder;
+ _menu = decoder.decode(res, SCREEN_SIZE);
+ delete res;
+
+ _menus[0] = new MenuRecord(40, 87, 20, 80, "Credits");
+ _menus[1] = new MenuRecord(127, 179, 100, 120, "Restart game,Save game,Restore game");
+ _menus[2] = new MenuRecord(224, 281, 210, 105, "Quit,Slow Text\x8b,Sound on ");
+ _selectedMenu = NULL;
+}
+
+Menu::~Menu() {
+ for (int ctr=0; ctr<NUM_MENUS; ++ctr) delete _menus[ctr];
+ delete _menu;
+}
+
+Menu &Menu::getReference() {
+ return *int_menu;
+}
+
+uint8 Menu::execute() {
+ _mouse.setCursorNum(CURSOR_ARROW);
+ _system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
+ _system.updateScreen();
+
+ _selectedMenu = NULL;
+ _surfaceMenu = NULL;
+ _selectedIndex = 0;
+
+ while (_mouse.lButton()) {
+ while (_events.pollEvent()) {
+ // handle events
+ }
+
+ if (_mouse.y() < MENUBAR_Y_SIZE)
+ {
+ MenuRecord *p = getMenuAt(_mouse.x());
+
+ if (_selectedMenu != p) {
+ // If necessary, remove prior menu
+ if (_selectedMenu) {
+ toggleHighlight(_selectedMenu);
+ _screen.updateArea(_selectedMenu->xstart(), MENUBAR_Y_SIZE,
+ _surfaceMenu->width(), _surfaceMenu->height());
+ delete _surfaceMenu;
+ _surfaceMenu = NULL;
+ _selectedIndex = 0;
+ }
+
+ _selectedMenu = p;
+
+ // If a new menu is selected, show it
+ if (_selectedMenu) {
+ toggleHighlight(_selectedMenu);
+ _surfaceMenu = Surface::newDialog(
+ _selectedMenu->width(), _selectedMenu->numEntries(),
+ _selectedMenu->entries(), false, MENU_UNSELECTED_COLOUR);
+ _surfaceMenu->copyToScreen(_selectedMenu->xstart(), MENUBAR_Y_SIZE);
+ }
+
+ _system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
+ _system.updateScreen();
+ }
+ }
+
+ // Check for changing selected index
+ uint8 index = getIndexAt(_mouse.x(), _mouse.y());
+ if (index != _selectedIndex) {
+ if (_selectedIndex != 0) toggleHighlightItem(_selectedIndex);
+ _selectedIndex = index;
+ if (_selectedIndex != 0) toggleHighlightItem(_selectedIndex);
+ }
+ _system.delayMillis(10);
+ }
+
+ if (_surfaceMenu) delete _surfaceMenu;
+
+ // Deselect the currently selected menu header
+ if (_selectedMenu)
+ toggleHighlight(_selectedMenu);
+
+ // Restore the previous screen
+ _screen.update();
+
+ if (_selectedMenu == NULL) return MENUITEM_NONE;
+ else if (_selectedMenu == _menus[0]) return MENUITEM_CREDITS;
+ else if (_selectedMenu == _menus[1]) {
+ switch (_selectedIndex) {
+ case 1: return MENUITEM_RESTART_GAME;
+ case 2: return MENUITEM_SAVE_GAME;
+ case 3: return MENUITEM_RESTORE_GAME;
+ }
+ } else {
+ switch (_selectedIndex) {
+ case 1: return MENUITEM_QUIT;
+ case 2: return MENUITEM_TEXT_SPEED;
+ case 3: return MENUITEM_SOUND;
+ }
+ }
+ return MENUITEM_NONE;
+}
+
+MenuRecord *Menu::getMenuAt(int x) {
+ for (int ctr = 0; ctr < NUM_MENUS; ++ctr)
+ if ((x >= _menus[ctr]->hsxstart()) && (x <= _menus[ctr]->hsxend()))
+ return _menus[ctr];
+
+ return NULL;
+}
+
+uint8 Menu::getIndexAt(uint16 x, uint16 y) {
+ if (!_selectedMenu) return 0;
+
+ int ys = MENUBAR_Y_SIZE + DIALOG_EDGE_SIZE + 3;
+ int ye = MENUBAR_Y_SIZE + _surfaceMenu->height() - DIALOG_EDGE_SIZE - 3;
+ if ((y < ys) || (y > ye)) return 0;
+
+ uint16 yRelative = y - ys;
+ uint8 index = (uint8) (yRelative / 8) + 1;
+ if (index > _selectedMenu->numEntries()) index = _selectedMenu->numEntries();
+ return index;
+}
+
+void Menu::toggleHighlight(MenuRecord *menuRec) {
+ byte *addr = _menu->data();
+
+ for (uint16 y=0; y<MENUBAR_Y_SIZE; ++y) {
+ for (uint16 x=menuRec->hsxstart(); x<=menuRec->hsxend(); ++x) {
+ if (addr[x] == MENUBAR_SELECTED_COLOUR) addr[x] = 0;
+ else if (addr[x] == 0) addr[x] = MENUBAR_SELECTED_COLOUR;
+ }
+ addr += FULL_SCREEN_WIDTH;
+ }
+}
+
+void Menu::toggleHighlightItem(uint8 index) {
+ byte *p = _surfaceMenu->data().data() + (DIALOG_EDGE_SIZE + 3 +
+ ((index - 1) * 8)) * _surfaceMenu->width();
+ uint32 numBytes = 8 * _surfaceMenu->width();
+
+ while (numBytes-- > 0) {
+ if (*p == MENU_UNSELECTED_COLOUR) *p = MENU_SELECTED_COLOUR;
+ else if (*p == MENU_SELECTED_COLOUR) *p = MENU_UNSELECTED_COLOUR;
+ ++p;
+ }
+
+ _surfaceMenu->copyToScreen(_selectedMenu->xstart(), MENUBAR_Y_SIZE);
+}
+
+/*--------------------------------------------------------------------------*/
+
+uint16 PopupMenu::ShowInventory() {
+ Resources &rsc = Resources::getReference();
+ StringData &strings = StringData::getReference();
+
+ uint16 numItems = rsc.numInventoryItems();
+ uint16 itemCtr = 0;
+ char **itemNames = (char **) Memory::alloc(sizeof(char *) * numItems);
+ uint16 *idList = (uint16 *) Memory::alloc(sizeof(uint16) * numItems);
+
+ HotspotDataList::iterator i;
+ for (i = rsc.hotspotData().begin(); i != rsc.hotspotData().end(); ++i) {
+ HotspotData *hotspot = *i;
+ if (hotspot->roomNumber == PLAYER_ID) {
+ idList[itemCtr] = hotspot->hotspotId;
+ char *hotspotName = itemNames[itemCtr++] = (char *) malloc(MAX_HOTSPOT_NAME_SIZE);
+ strings.getString(hotspot->nameId, hotspotName, NULL, NULL);
+ }
+ }
+
+ uint16 result = Show(numItems, (const char **) itemNames);
+ if (result != 0xffff) result = idList[result];
+
+ for (itemCtr = 0; itemCtr < numItems; ++itemCtr)
+ free(itemNames[itemCtr]);
+
+ delete itemNames;
+ delete idList;
+ return result;
+}
+
+Action PopupMenu::Show(uint32 actionMask) {
+ int numEntries = 0;
+ uint32 v = actionMask;
+ int index;
+
+ for (index = 1; index <= EXAMINE; ++index, v >>= 1) {
+ if (v & 1) ++numEntries;
+ }
+
+ const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
+
+ v = actionMask;
+ int strIndex = 0;
+ for (index=1; index<=EXAMINE; ++index, v >>= 1) {
+ if (v & 1)
+ strList[strIndex++] = actionList[index];
+ }
+
+ uint16 result = Show(numEntries, strList);
+
+ if (result == 0xffff) return NONE;
+
+ v = actionMask;
+ for (index = 1; index <= EXAMINE; ++index, v >>= 1) {
+ if (v & 1)
+ if (result-- == 0) return (Action) index;
+ }
+
+ delete strList;
+ return NONE;
+}
+
+Action PopupMenu::Show(int numEntries, Action *actions) {
+ const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
+ Action *actionPtr = actions;
+ for (int index = 0; index < numEntries; ++index)
+ strList[index] = actionList[*actionPtr++];
+ uint16 result = Show(numEntries, strList);
+
+ delete strList;
+ if (result == 0xffff) return NONE;
+ else return actions[result];
+}
+
+uint16 PopupMenu::Show(int numEntries, const char *actions[]) {
+ if (numEntries == 0) return 0xffff;
+ Events &e = Events::getReference();
+ Mouse &mouse = Mouse::getReference();
+ OSystem &system = System::getReference();
+ Screen &screen = Screen::getReference();
+ Rect r;
+
+ mouse.cursorOff();
+ uint16 oldX = mouse.x();
+ uint16 oldY = mouse.y();
+ const uint16 yMiddle = FULL_SCREEN_HEIGHT / 2;
+ mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
+
+ // Round up number of lines in dialog to next odd number
+ uint16 numLines = (numEntries / 2) * 2 + 1;
+ if (numLines > 5) numLines = 5;
+
+ // Figure out the character width
+ uint16 numCols = 0;
+ for (int ctr = 0; ctr < numEntries; ++ctr) {
+ int len = strlen(actions[ctr]);
+ if (len > numCols)
+ numCols = len;
+ }
+
+ // Create the dialog surface
+ Surface *s = new Surface(DIALOG_EDGE_SIZE * 2 + numCols * FONT_WIDTH,
+ DIALOG_EDGE_SIZE * 2 + numLines * FONT_HEIGHT);
+ s->createDialog();
+
+ int selectedIndex = 0;
+ bool refreshFlag = true;
+ r.left = DIALOG_EDGE_SIZE;
+ r.right = s->width() - DIALOG_EDGE_SIZE - 1;
+ r.top = DIALOG_EDGE_SIZE;
+ r.bottom = s->height() - DIALOG_EDGE_SIZE - 1;
+
+ for (;;) {
+ if (refreshFlag) {
+ // Set up the contents of the menu
+ s->fillRect(r, 0);
+
+ for (int index = 0; index < numLines; ++index) {
+ int actionIndex = selectedIndex - (numEntries / 2) + index;
+ if ((actionIndex >= 0) && (actionIndex < numEntries)) {
+ s->writeString(DIALOG_EDGE_SIZE, DIALOG_EDGE_SIZE + index * FONT_HEIGHT,
+ actions[actionIndex], true,
+ (index == (numLines / 2)) ? MENU_SELECTED_COLOUR : MENU_UNSELECTED_COLOUR,
+ false);
+ }
+ }
+
+ s->copyToScreen(0, yMiddle-(s->height() / 2));
+ system.updateScreen();
+ refreshFlag = false;
+ }
+
+ if (e.pollEvent()) {
+ if (e.quitFlag) {
+ selectedIndex = 0xffff;
+ break;
+ }
+
+ if (e.type() == OSystem::EVENT_KEYDOWN) {
+ byte ch = e.event().kbd.ascii;
+ uint16 keycode = e.event().kbd.keycode;
+
+ if (((keycode == 0x108) || (keycode == 0x111)) && (selectedIndex > 0)) {
+ --selectedIndex;
+ refreshFlag = true;
+ } else if (((keycode == 0x102) || (keycode == 0x112)) &&
+ (selectedIndex < numEntries-1)) {
+ ++selectedIndex;
+ refreshFlag = true;
+ } else if ((ch == '\xd') || (keycode == 0x10f)) {
+ break;
+ } else if (ch == '\x1b') {
+ selectedIndex = 0xffff;
+ break;
+ }
+
+ } else if (e.type() == OSystem::EVENT_MOUSEMOVE) {
+ if ((mouse.y() < yMiddle) && (selectedIndex > 0) &&
+ (yMiddle-mouse.y() >= POPMENU_CHANGE_SENSITIVITY)) {
+ --selectedIndex;
+ mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
+ refreshFlag = true;
+ } else if ((mouse.y() > yMiddle) && (selectedIndex < numEntries - 1) &&
+ (mouse.y()-yMiddle >= POPMENU_CHANGE_SENSITIVITY)) {
+ ++selectedIndex;
+ mouse.setPosition(FULL_SCREEN_WIDTH/2, yMiddle);
+ refreshFlag = true;
+ }
+
+ } else if (e.type() == OSystem::EVENT_LBUTTONDOWN) {
+ mouse.waitForRelease();
+ break;
+
+ } else if (e.type() == OSystem::EVENT_RBUTTONDOWN) {
+ mouse.waitForRelease();
+ selectedIndex = 0xffff;
+ break;
+ }
+ }
+ }
+
+ mouse.setPosition(oldX, oldY);
+ mouse.cursorOn();
+ screen.update();
+ return selectedIndex;
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/menu.h b/engines/lure/menu.h
new file mode 100644
index 0000000000..96c6a1b2fd
--- /dev/null
+++ b/engines/lure/menu.h
@@ -0,0 +1,92 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_menu_h__
+#define __lure_menu_h__
+
+#include "common/stdafx.h"
+#include "common/str.h"
+#include "lure/luredefs.h"
+#include "lure/disk.h"
+#include "lure/screen.h"
+#include "lure/surface.h"
+#include "lure/events.h"
+
+#define NUM_MENUS 3
+
+namespace Lure {
+
+class MenuRecord {
+private:
+ uint16 _xstart, _width;
+ uint16 _hsxstart, _hsxend;
+ char **_entries;
+ uint8 _numEntries;
+public:
+ MenuRecord(uint16 hsxstartVal, uint16 hsxendVal, uint16 xstartVal,
+ uint16 widthVal, const char *strings);
+ ~MenuRecord();
+
+ uint16 xstart() { return _xstart; }
+ uint16 width() { return _width; }
+ uint16 hsxstart() { return _hsxstart; }
+ uint16 hsxend() { return _hsxend; }
+ uint8 numEntries() { return _numEntries; }
+ char **entries() { return _entries; }
+ char *getEntry(uint8 index);
+};
+
+class Menu {
+private:
+ OSystem &_system;
+ Screen &_screen;
+ Events &_events;
+ Mouse &_mouse;
+ MemoryBlock *_menu;
+ MenuRecord *_menus[NUM_MENUS];
+ MenuRecord *_selectedMenu;
+ Surface *_surfaceMenu;
+ uint8 _selectedIndex;
+
+ MenuRecord *getMenuAt(int x);
+ uint8 getIndexAt(uint16 x, uint16 y);
+ void toggleHighlight(MenuRecord *menuRec);
+ void toggleHighlightItem(uint8 index);
+public:
+ Menu(OSystem &system);
+ ~Menu();
+ static Menu &getReference();
+ uint8 execute();
+ MenuRecord &getMenu(uint8 index) { return *_menus[index]; }
+};
+
+class PopupMenu {
+public:
+ static Action Show(uint32 actionMask);
+ static Action Show(int numEntries, Action *actions);
+ static uint16 Show(int numEntries, const char *actions[]);
+ static uint16 ShowInventory();
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/module.mk b/engines/lure/module.mk
new file mode 100644
index 0000000000..8b83531bd7
--- /dev/null
+++ b/engines/lure/module.mk
@@ -0,0 +1,36 @@
+MODULE := engines/lure
+
+MODULE_OBJS := \
+ engines/lure/animseq.o \
+ engines/lure/debug-input.o \
+ engines/lure/debug-methods.o \
+ engines/lure/decode.o \
+ engines/lure/disk.o \
+ engines/lure/events.o \
+ engines/lure/game.o \
+ engines/lure/hotspots.o \
+ engines/lure/intro.o \
+ engines/lure/lure.o \
+ engines/lure/memory.o \
+ engines/lure/menu.o \
+ engines/lure/palette.o \
+ engines/lure/res.o \
+ engines/lure/res_struct.o \
+ engines/lure/room.o \
+ engines/lure/screen.o \
+ engines/lure/scripts.o \
+ engines/lure/strings.o \
+ engines/lure/surface.o \
+ engines/lure/system.o
+
+MODULE_DIRS += \
+ engines/lure
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
+
diff --git a/engines/lure/palette.cpp b/engines/lure/palette.cpp
new file mode 100644
index 0000000000..e4d8db0a41
--- /dev/null
+++ b/engines/lure/palette.cpp
@@ -0,0 +1,142 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/palette.h"
+#include "common/util.h"
+
+namespace Lure {
+
+// Constructor
+// Defaults the palette to a full 256 entry palette
+
+Palette::Palette() {
+ _numEntries = GAME_COLOURS;
+ _palette = Memory::allocate(_numEntries * 4);
+ _palette->empty();
+}
+
+// Consructor
+// Sets up a palette with the given number of entries and a copy of the passed data
+
+Palette::Palette(uint8 numEntries1, const byte *data1, PaletteSource paletteSource) {
+ _numEntries = numEntries1;
+ _palette = Memory::allocate(_numEntries * 4);
+
+ if (data1) {
+ if (paletteSource == RGB64)
+ convertPalette(data1, _numEntries);
+ else
+ _palette->copyFrom(data1, 0, 0, _numEntries * 4);
+ } else {
+ // No data provided, set a null palette
+ _palette->empty();
+ }
+}
+
+// Constructor
+// Makes a copy of a passed palette object
+
+Palette::Palette(Palette &src) {
+ _numEntries = src.numEntries();
+ _palette = Memory::duplicate(src._palette);
+}
+
+// Constructor
+// Loads a palette from a resource
+
+Palette::Palette(uint16 resourceId) {
+ Disk &d = Disk::getReference();
+
+ MemoryBlock *srcData = d.getEntry(resourceId);
+ if (((srcData->size() % 3) != 0) || ((srcData->size() / 3) > GAME_COLOURS))
+ error("Specified resource %d is not a palette", resourceId);
+
+ _numEntries = srcData->size() / 3;
+ _palette = Memory::allocate(_numEntries * 4);
+ convertPalette(srcData->data(), _numEntries);
+ delete srcData;
+}
+
+void Palette::convertPalette(const byte *palette1, uint16 numEntries1) {
+ byte *pDest = _palette->data();
+ const byte *pSrc = palette1;
+
+ while (numEntries1-- > 0) {
+ *pDest++ = (pSrc[0] << 2) + (pSrc[0] >> 4);
+ *pDest++ = (pSrc[1] << 2) + (pSrc[1] >> 4);
+ *pDest++ = (pSrc[2] << 2) + (pSrc[2] >> 4);
+ *pDest++ = 0;
+ pSrc += 3;
+ }
+}
+
+void Palette::setEntry(uint8 index, uint32 value) {
+ if (index >= numEntries()) error("Invalid palette index: %d", index);
+ uint32 *entry = (uint32 *) (data() + index * 4);
+ *entry = value;
+}
+
+uint32 Palette::getEntry(uint8 index) {
+ if (index >= numEntries()) error("Invalid palette index: %d", index);
+ uint32 *entry = (uint32 *) (data() + index * 4);
+ return *entry;
+}
+
+void Palette::copyFrom(Palette *src) {
+ _palette->copyFrom(src->palette());
+}
+
+/*--------------------------------------------------------------------------*/
+
+PaletteCollection::PaletteCollection(uint16 resourceId) {
+ Disk &d = Disk::getReference();
+ MemoryBlock *resource = d.getEntry(resourceId);
+ uint32 palSize;
+ uint8 *data = resource->data();
+
+ if (resource->size() % (SUB_PALETTE_SIZE * 3) != 0)
+ error("Resource #%d is not a valid palette set", resourceId);
+
+ palSize = SUB_PALETTE_SIZE * 3;
+ _numPalettes = resource->size() / palSize;
+
+ _palettes = (Palette **) Memory::alloc(_numPalettes * sizeof(Palette *));
+ for (uint8 paletteCtr = 0; paletteCtr < _numPalettes; ++paletteCtr, data += palSize)
+ _palettes[paletteCtr] = new Palette(SUB_PALETTE_SIZE, data, RGB64);
+
+ delete resource;
+}
+
+PaletteCollection::~PaletteCollection() {
+ for (int paletteCtr = 0; paletteCtr < _numPalettes; ++paletteCtr)
+ delete _palettes[paletteCtr];
+ free(_palettes);
+}
+
+
+Palette &PaletteCollection::getPalette(uint8 paletteNum) {
+ if (paletteNum >= _numPalettes)
+ error("Invalid palette index specified");
+ return *_palettes[paletteNum];
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/palette.h b/engines/lure/palette.h
new file mode 100644
index 0000000000..6fa2d9a1b7
--- /dev/null
+++ b/engines/lure/palette.h
@@ -0,0 +1,68 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_palette_h__
+#define __lure_palette_h__
+
+#include "lure/luredefs.h"
+#include "lure/disk.h"
+#include "lure/memory.h"
+
+namespace Lure {
+
+enum PaletteSource {RGB, RGB64};
+
+class Palette {
+private:
+ MemoryBlock *_palette;
+ uint16 _numEntries;
+
+ void convertPalette(const byte *palette, uint16 numEntries);
+public:
+ Palette();
+ Palette(uint8 numEntries, const byte *data, PaletteSource paletteSource);
+ Palette(Palette &src);
+ Palette(uint16 resourceId);
+
+ uint8 *data() { return _palette->data(); }
+ MemoryBlock *palette() { return _palette; }
+ uint16 numEntries() { return _palette->size() / 4; }
+ void setEntry(uint8 index, uint32 value);
+ uint32 getEntry(uint8 index);
+ void copyFrom(Palette *src);
+};
+
+class PaletteCollection {
+private:
+ Palette **_palettes;
+ uint8 _numPalettes;
+public:
+ PaletteCollection(uint16 resourceId);
+ ~PaletteCollection();
+
+ uint8 numPalettes() { return _numPalettes; }
+ Palette &getPalette(uint8 paletteNum);
+};
+
+} // end of namspace Lure
+
+#endif
diff --git a/engines/lure/res.cpp b/engines/lure/res.cpp
new file mode 100644
index 0000000000..929b983f68
--- /dev/null
+++ b/engines/lure/res.cpp
@@ -0,0 +1,399 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/res.h"
+#include "lure/disk.h"
+#include "lure/scripts.h"
+#include "lure/screen.h"
+
+namespace Lure {
+
+static Resources *int_resources = NULL;
+
+Resources &Resources::getReference() {
+ return *int_resources;
+}
+
+Resources::Resources() {
+ int_resources = this;
+ reloadData();
+}
+
+Resources::~Resources() {
+ // Delete any unremoved active hotspots
+ freeData();
+}
+
+void Resources::freeData() {
+ _activeHotspots.clear();
+ _roomData.clear();
+ _hotspotData.clear();
+ _hotspotOverrides.clear();
+ _animData.clear();
+ _exitJoins.clear();
+ _delayList.clear();
+
+ delete _paletteSubset;
+ delete _scriptData;
+ delete _script2Data;
+ delete _hotspotScriptData;
+ delete _messagesData;
+}
+
+struct AnimRecordTemp {
+ uint16 *offset;
+ MovementDataList *list;
+};
+
+void Resources::reloadData() {
+ Disk &d = Disk::getReference();
+ MemoryBlock *mb;
+ uint16 *offset, offsetVal;
+ int ctr;
+
+ // Get the palette subset data
+ _paletteSubset = new Palette(ALT_PALETTE_RESOURCE_ID);
+
+ // Load room data
+ mb = d.getEntry(ROOM_DATA_RESOURCE_ID);
+ offset = (uint16 *) mb->data();
+ for (ctr = 0; READ_LE_UINT16(offset) != 0xffff; ++ctr, ++offset) {
+ offsetVal = READ_LE_UINT16(offset);
+ if (offsetVal != 0) {
+ // Get room resource
+ RoomResource *rec = (RoomResource *) (mb->data() + offsetVal);
+ RoomData *newEntry = new RoomData(rec);
+ _roomData.push_back(newEntry);
+
+ if (rec->numExits > 0) {
+ RoomExitResource *exitRes = (RoomExitResource *)
+ (mb->data() + offsetVal + sizeof(RoomResource));
+
+ for (uint16 exitCtr = 0; exitCtr < rec->numExits; ++exitCtr, ++exitRes) {
+ RoomExitData *exit = new RoomExitData(exitRes);
+ newEntry->exits.push_back(exit);
+ }
+ }
+ }
+ }
+ delete mb;
+
+ // Load room exits
+ mb = d.getEntry(ROOM_EXITS_RESOURCE_ID);
+ ctr = 0;
+ for (;;) {
+ offsetVal = READ_LE_UINT16(mb->data() + (ctr * 2));
+ if (offsetVal == 0xffff) break;
+
+ if (offsetVal != 0) {
+ RoomData *room = getRoom(ctr);
+ if (room) {
+ RoomExitHotspotRecord *re = (RoomExitHotspotRecord *)
+ (mb->data() + offsetVal);
+ while (READ_LE_UINT16(&re->hotspotId) != 0xffff) {
+ RoomExitHotspotData *newEntry = new RoomExitHotspotData(re);
+ room->exitHotspots.push_back(newEntry);
+ ++re;
+ }
+ }
+ }
+ ++ctr;
+ }
+ delete mb;
+
+ // Load room joins
+ mb = d.getEntry(ROOM_EXIT_JOINS_RESOURCE_ID);
+ RoomExitJoinRecord *joinRec = (RoomExitJoinRecord *) mb->data();
+ while (READ_LE_UINT16(&joinRec->hotspot1Id) != 0xffff) {
+ RoomExitJoinData *newEntry = new RoomExitJoinData(joinRec);
+ _exitJoins.push_back(newEntry);
+ ++joinRec;
+ }
+ delete mb;
+
+ // Load the hotspot list
+ mb = d.getEntry(HOTSPOT_DATA_RESOURCE_ID);
+ HotspotResource *hsRec = (HotspotResource *) mb->data();
+ while (READ_LE_UINT16(&hsRec->hotspotId) != 0xffff) {
+ HotspotData *newEntry = new HotspotData(hsRec);
+ _hotspotData.push_back(newEntry);
+ ++hsRec;
+ }
+ delete mb;
+
+ // Load the hotspot overrides
+ mb = d.getEntry(HOTSPOT_OVERRIDE_DATA_RESOURCE_ID);
+ HotspotOverrideResource *hsoRec = (HotspotOverrideResource *) mb->data();
+ while (READ_LE_UINT16(&hsoRec->hotspotId) != 0xffff) {
+ HotspotOverrideData *newEntry = new HotspotOverrideData(hsoRec);
+ _hotspotOverrides.push_back(newEntry);
+ ++hsoRec;
+ }
+ delete mb;
+
+ // Load the animation list
+ mb = d.getEntry(ANIM_DATA_RESOURCE_ID);
+ HotspotAnimResource *animRec = (HotspotAnimResource *) mb->data();
+ while (READ_LE_UINT16(&animRec->animRecordId) != 0xffff) {
+ HotspotAnimData *newEntry = new HotspotAnimData(animRec);
+ _animData.push_back(newEntry);
+
+ // Handle any direction frames
+ AnimRecordTemp dirEntries[4] = {
+ {&animRec->leftOffset, &newEntry->leftFrames},
+ {&animRec->rightOffset, &newEntry->rightFrames},
+ {&animRec->upOffset, &newEntry->upFrames},
+ {&animRec->downOffset, &newEntry->downFrames}};
+ for (int dirCtr = 0; dirCtr < 4; ++dirCtr) {
+ offsetVal = READ_LE_UINT16(dirEntries[dirCtr].offset);
+ if (offsetVal != 0) {
+ MovementResource *moveRec = (MovementResource *)
+ (mb->data() + offsetVal);
+ while (READ_LE_UINT16(&moveRec->frameNumber) != 0xffff) {
+ MovementData *newMove = new MovementData(moveRec);
+ dirEntries[dirCtr].list->push_back(newMove);
+ ++moveRec;
+ }
+ }
+ }
+
+ ++animRec;
+ }
+ delete mb;
+
+ // Hotspot scripts
+ mb = d.getEntry(HOTSPOT_SCRIPT_LIST_RESOURCE_ID);
+ uint16 numEntries = mb->size() / 2;
+ uint16 *srcVal = (uint16 *) mb->data();
+ uint16 *destVal = _hotspotScriptData = (uint16 *)
+ Memory::alloc(numEntries * sizeof(uint16));
+ for (ctr = 0; ctr < numEntries; ++ctr, ++srcVal, ++destVal) {
+ *destVal = READ_LE_UINT16(srcVal);
+ }
+ delete mb;
+
+ // Handle the hotspot action lists
+ mb = d.getEntry(ACTION_LIST_RESOURCE_ID);
+ uint16 *v = (uint16 *) mb->data();
+ uint16 recordId;
+ while ((recordId = READ_LE_UINT16(v)) != 0xffff) {
+ ++v;
+ offsetVal = READ_LE_UINT16(v);
+ ++v;
+
+ HotspotActionList *list = new HotspotActionList(
+ recordId, mb->data() + offsetVal);
+ _actionsList.push_back(list);
+ }
+ delete mb;
+
+ _delayList.clear();
+
+ // Load miscellaneous data
+ _scriptData = d.getEntry(SCRIPT_DATA_RESOURCE_ID);
+ _script2Data = d.getEntry(SCRIPT2_DATA_RESOURCE_ID);
+ _messagesData = d.getEntry(MESSAGES_LIST_RESOURCE_ID);
+}
+
+RoomExitJoinData *Resources::getExitJoin(uint16 hotspotId) {
+ RoomExitJoinList::iterator i;
+
+ for (i = _exitJoins.begin(); i != _exitJoins.end(); ++i) {
+ RoomExitJoinData *rec = *i;
+ if ((rec->hotspot1Id == hotspotId) || (rec->hotspot2Id == hotspotId))
+ return rec;
+ }
+
+ return NULL;
+}
+
+uint16 Resources::getHotspotScript(uint16 index) {
+ return _hotspotScriptData[index];
+}
+
+RoomData *Resources::getRoom(uint16 roomNumber) {
+ RoomDataList::iterator i;
+
+ for (i = _roomData.begin(); i != _roomData.end(); ++i) {
+ RoomData *rec = *i;
+ if (rec->roomNumber == roomNumber) return rec;
+ ++rec;
+ }
+
+ return NULL;
+}
+
+void Resources::insertPaletteSubset(Palette &p) {
+ p.palette()->copyFrom(_paletteSubset->palette(), 0, 129*4, 60*4);
+ p.palette()->copyFrom(_paletteSubset->palette(), 60*4, 220*4, 8*4);
+}
+
+HotspotData *Resources::getHotspot(uint16 hotspotId) {
+ HotspotDataList::iterator i;
+
+ for (i = _hotspotData.begin(); i != _hotspotData.end(); ++i) {
+ HotspotData *rec = *i;
+ if (rec->hotspotId == hotspotId) return rec;
+ }
+
+ return NULL;
+}
+
+Hotspot *Resources::getActiveHotspot(uint16 hotspotId) {
+ HotspotList::iterator i;
+
+ for (i = _activeHotspots.begin(); i != _activeHotspots.end(); ++i) {
+ Hotspot *rec = *i;
+ if (rec->hotspotId() == hotspotId) return rec;
+ }
+
+ return NULL;
+}
+
+
+HotspotOverrideData *Resources::getHotspotOverride(uint16 hotspotId) {
+ HotspotOverrideList::iterator i;
+
+ for (i = _hotspotOverrides.begin(); i != _hotspotOverrides.end(); ++i) {
+ HotspotOverrideData *rec = *i;
+ if (rec->hotspotId == hotspotId) return rec;
+ }
+
+ return NULL;
+}
+
+HotspotAnimData *Resources::getAnimation(uint16 animRecordId) {
+ HotspotAnimList::iterator i;
+
+ for (i = _animData.begin(); i != _animData.end(); ++i) {
+ HotspotAnimData *rec = *i;
+ if (rec->animRecordId == animRecordId) return rec;
+ }
+
+ return NULL;
+}
+
+uint16 Resources::getHotspotAction(uint16 actionsOffset, Action action) {
+ HotspotActionList *list = _actionsList.getActions(actionsOffset);
+ if (!list) return 0;
+ return list->getActionOffset(action);
+}
+
+HotspotActionList *Resources::getHotspotActions(uint16 actionsOffset) {
+ return _actionsList.getActions(actionsOffset);
+}
+
+void Resources::activateHotspot(uint16 hotspotId) {
+ HotspotData *res = getHotspot(hotspotId);
+ if (!res) return;
+ res->roomNumber &= 0x7fff; // clear any suppression bit in room #
+
+ // Make sure that the hotspot isn't already active
+ HotspotList::iterator i = _activeHotspots.begin();
+ bool found = false;
+
+ for (; i != _activeHotspots.end(); ++i) {
+ Hotspot &h = *i.operator*();
+ if (h.hotspotId() == res->hotspotId) {
+ found = true;
+ break;
+ }
+ }
+ if (found) return;
+
+ // Check the script load flag
+ if (res->scriptLoadFlag) {
+ // Execute a script rather than doing a standard load
+ Script::execute(res->loadOffset);
+ } else {
+ // Standard load
+ bool loadFlag = true;
+
+ switch (res->loadOffset) {
+ case 0x3afe:
+ // Copy protection check - since the game is freeware now,
+ // don't bother with it
+ loadFlag = false;
+ break;
+
+ case 0x41BD:
+ // Empty handler used to prevent loading hotspots that
+ // are yet to be active (such as the straw fire)
+ loadFlag = false;
+ break;
+
+ case 0x7172:
+ case 0x7167:
+ // Standard animation load
+ break;
+
+ case 0x88ac:
+ // Torch in room #1
+ loadFlag = _fieldList.getField(TORCH_HIDE) == 0;
+ break;
+
+ default:
+ // All others simply activate the hotspot
+ warning("Hotspot %d uses unknown load offset proc %d",
+ res->hotspotId, res->loadOffset);
+ }
+
+ if (loadFlag) {
+ Hotspot *hotspot = addHotspot(hotspotId);
+// if (res->loadOffset == 0x7167) hotspot->setPersistant(true);
+ // DEBUG - for now only keep certain hotspots active
+ hotspot->setPersistant((res->hotspotId >= 0x3e8) && (res->hotspotId <= 0x3ea));
+ }
+ }
+}
+
+Hotspot *Resources::addHotspot(uint16 hotspotId) {
+ Hotspot *hotspot = new Hotspot(getHotspot(hotspotId));
+ _activeHotspots.push_back(hotspot);
+ return hotspot;
+}
+
+void Resources::deactivateHotspot(uint16 hotspotId) {
+ HotspotList::iterator i = _activeHotspots.begin();
+
+ while (i != _activeHotspots.end()) {
+ Hotspot *h = *i;
+ if (h->hotspotId() == hotspotId)
+ i = _activeHotspots.erase(i);
+ else
+ i++;
+ }
+}
+
+uint16 Resources::numInventoryItems() {
+ uint16 numItems = 0;
+ HotspotDataList &list = _hotspotData;
+ HotspotDataList::iterator i;
+ for (i = list.begin(); i != list.end(); ++i) {
+ HotspotData *rec = *i;
+ if (rec->roomNumber == PLAYER_ID) ++numItems;
+ }
+
+ return numItems;
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/res.h b/engines/lure/res.h
new file mode 100644
index 0000000000..66af55170f
--- /dev/null
+++ b/engines/lure/res.h
@@ -0,0 +1,94 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_res_h__
+#define __lure_res_h__
+
+#include "lure/luredefs.h"
+#include "lure/memory.h"
+#include "common/list.h"
+#include "lure/res_struct.h"
+#include "lure/hotspots.h"
+#include "lure/palette.h"
+
+namespace Lure {
+
+class Resources {
+private:
+ Common::RandomSource _rnd;
+ Palette *_paletteSubset;
+ RoomDataList _roomData;
+ HotspotDataList _hotspotData;
+ HotspotOverrideList _hotspotOverrides;
+ HotspotAnimList _animData;
+ MemoryBlock *_scriptData;
+ MemoryBlock *_script2Data;
+ MemoryBlock *_messagesData;
+ uint16 *_hotspotScriptData;
+ RoomExitJoinList _exitJoins;
+ HotspotList _activeHotspots;
+ ValueTableData _fieldList;
+ HotspotActionSet _actionsList;
+ SequenceDelayList _delayList;
+
+ void freeData();
+public:
+ Resources();
+ ~Resources();
+ static Resources &getReference();
+ void reloadData();
+
+ byte *getResource(uint16 resId);
+ RoomDataList &roomData() { return _roomData; }
+ RoomData *getRoom(uint16 roomNumber);
+ void insertPaletteSubset(Palette &p);
+
+ HotspotDataList &hotspotData() { return _hotspotData; }
+ HotspotOverrideList &hotspotOverrides() { return _hotspotOverrides; }
+ HotspotAnimList &animRecords() { return _animData; }
+ MemoryBlock *scriptData() { return _scriptData; }
+ MemoryBlock *hotspotScriptData() { return _script2Data; }
+ MemoryBlock *messagesData() { return _messagesData; }
+ uint16 getHotspotScript(uint16 index);
+ HotspotList &activeHotspots() { return _activeHotspots; }
+ uint16 random() { return _rnd.getRandomNumber(65536) & 0xffff; }
+ HotspotData *getHotspot(uint16 hotspotId);
+ Hotspot *getActiveHotspot(uint16 hotspotId);
+ HotspotOverrideData *getHotspotOverride(uint16 hotspotId);
+ HotspotAnimData *getAnimation(uint16 animRecordId);
+ RoomExitJoinList &exitJoins() { return _exitJoins; }
+ RoomExitJoinData *getExitJoin(uint16 hotspotId);
+ uint16 getHotspotAction(uint16 actionsOffset, Action action);
+ HotspotActionList *getHotspotActions(uint16 actionsOffset);
+ ValueTableData &fieldList() { return _fieldList; }
+ SequenceDelayList &delayList() { return _delayList; }
+ uint16 numInventoryItems();
+
+ void activateHotspot(uint16 hotspotId);
+ Hotspot *addHotspot(uint16 hotspotId);
+ void deactivateHotspot(uint16 hotspotId);
+
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/res_struct.cpp b/engines/lure/res_struct.cpp
new file mode 100644
index 0000000000..db01a471cd
--- /dev/null
+++ b/engines/lure/res_struct.cpp
@@ -0,0 +1,309 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/res.h"
+#include "lure/disk.h"
+#include "lure/scripts.h"
+#include "lure/system.h"
+
+namespace Lure {
+
+const char *actionList[] = {NULL, "Get", NULL, "Push", "Pull", "Operate", "Open",
+ "Close", "Lock", "Unlock", "Use", "Give", "Talk to", "Tell", "Buy",
+ "Look", "Look at", "Look through", "Ask", NULL, "Drink", "Status",
+ "Go to", "Return", "Bribe", "Examine"};
+
+// Room data holding class
+
+RoomData::RoomData(RoomResource *rec) {
+ roomNumber = READ_LE_UINT16(&rec->roomNumber);
+ descId = READ_LE_UINT16(&rec->descId);
+ sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset);
+ numLayers = READ_LE_UINT16(&rec->numLayers);
+
+ for (int ctr = 0; ctr < 4; ++ctr)
+ layers[ctr] = READ_LE_UINT16(&rec->layers[ctr]);
+}
+
+// Room exit hotspot area holding class
+
+RoomExitHotspotData::RoomExitHotspotData(RoomExitHotspotRecord *rec) {
+ hotspotId = READ_LE_UINT16(&rec->hotspotId);
+ xs = READ_LE_INT16(&rec->xs);
+ ys = READ_LE_INT16(&rec->ys);
+ xe = READ_LE_INT16(&rec->xe);
+ ye = READ_LE_INT16(&rec->ye);
+ cursorNum = rec->cursorNum;
+ destRoomNumber = READ_LE_UINT16(&rec->destRoomNumber);
+}
+
+// Room exit class
+
+RoomExitData::RoomExitData(RoomExitResource *rec) {
+ xs = rec->xs;
+ ys = rec->ys;
+ xe = rec->xe;
+ ye = rec->ye;
+ sequenceOffset = rec->sequenceOffset;
+ roomNumber = rec->newRoom;
+ x = rec->newRoomX;
+ y = rec->newRoomY;
+
+ switch (rec->direction) {
+ case 0x80:
+ direction = UP;
+ break;
+ case 0x40:
+ direction = DOWN;
+ break;
+ case 0x20:
+ direction = LEFT;
+ break;
+ case 0x10:
+ direction = RIGHT;
+ break;
+ default:
+ direction = NO_DIRECTION;
+ break;
+ }
+}
+
+bool RoomExitData::insideRect(int16 xp, int16 yp) {
+ return ((xp >= xs) && (xp <= xe) && (yp >= ys) && (yp <= ye));
+}
+
+RoomExitData *RoomExitList::checkExits(int16 xp, int16 yp) {
+ iterator i;
+ for (i = begin(); i != end(); i++) {
+ RoomExitData *rec = *i;
+ if (rec->insideRect(xp, yp)) return rec;
+ }
+ return NULL;
+}
+
+// Room exit joins class
+
+RoomExitJoinData::RoomExitJoinData(RoomExitJoinRecord *rec) {
+ hotspot1Id = READ_LE_UINT16(&rec->hotspot1Id);
+ h1CurrentFrame = rec->h1CurrentFrame;
+ h1DestFrame = rec->h1DestFrame;
+ h1Unknown = READ_LE_UINT16(&rec->h1Unknown);
+ hotspot2Id = READ_LE_UINT16(&rec->hotspot2Id);
+ h2CurrentFrame = rec->h2CurrentFrame;
+ h2DestFrame = rec->h2DestFrame;
+ h2Unknown = READ_LE_UINT16(&rec->h2Unknown);
+ blocked = rec->blocked;
+ unknown = READ_LE_UINT32(&rec->unknown);
+}
+
+// Hotspot action record
+
+HotspotActionData::HotspotActionData(HotspotActionRecord *rec) {
+ action = (Action) rec->action;
+ sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset);
+}
+
+uint16 HotspotActionList::getActionOffset(Action action) {
+ iterator i;
+ for (i = begin(); i != end(); ++i) {
+ HotspotActionData *rec = *i;
+ if (rec->action == action) return rec->sequenceOffset;
+ }
+
+ return 0;
+}
+
+
+// Hotspot data
+
+HotspotData::HotspotData(HotspotResource *rec) {
+ hotspotId = READ_LE_UINT16(&rec->hotspotId);
+ nameId = READ_LE_UINT16(&rec->nameId);
+ descId = READ_LE_UINT16(&rec->descId);
+ descId2 = READ_LE_UINT16(&rec->descId2);
+ actions = READ_LE_UINT32(&rec->actions);
+ actionsOffset = READ_LE_UINT16(&rec->actionsOffset);
+ flags = (byte) (actions >> 24) & 0xf0;
+ actions &= 0xfffffff;
+
+ roomNumber = READ_LE_UINT16(&rec->roomNumber);
+ layer = rec->layer;
+ scriptLoadFlag = rec->scriptLoadFlag;
+ loadOffset = READ_LE_UINT16(&rec->loadOffset);
+ startX = READ_LE_INT16(&rec->startX);
+ startY = READ_LE_INT16(&rec->startY);
+ width = READ_LE_UINT16(&rec->width);
+ height = READ_LE_UINT16(&rec->height);
+ colourOffset = READ_LE_UINT16(&rec->colourOffset);
+ animRecordId = READ_LE_UINT16(&rec->animRecordId);
+ sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset);
+ tickProcOffset = READ_LE_UINT16(&rec->tickProcOffset);
+ tickTimeout = READ_LE_UINT16(&rec->tickTimeout);
+}
+
+// Hotspot override data
+
+HotspotOverrideData::HotspotOverrideData(HotspotOverrideResource *rec) {
+ hotspotId = READ_LE_UINT16(&rec->hotspotId);
+ xs = READ_LE_INT16(&rec->xs);
+ ys = READ_LE_INT16(&rec->ys);
+ xe = READ_LE_INT16(&rec->xe);
+ ye = READ_LE_INT16(&rec->ye);
+}
+
+// Hotspot animation movement frame
+
+MovementData::MovementData(MovementResource *rec) {
+ frameNumber = READ_LE_UINT16(&rec->frameNumber);
+ xChange = READ_LE_INT16(&rec->xChange);
+ yChange = READ_LE_INT16(&rec->yChange);
+}
+
+// List of movement frames
+
+bool MovementDataList::getFrame(uint16 currentFrame, int16 &xChange,
+ int16 &yChange, uint16 &nextFrame) {
+ if (isEmpty()) return false;
+ bool foundFlag = false;
+ iterator i;
+
+ for (i = begin(); i != end(); ++i) {
+ MovementData *rec = *i;
+ if (foundFlag || (i == begin())) {
+ xChange = rec->xChange;
+ yChange = rec->yChange;
+ nextFrame = rec->frameNumber;
+ if (foundFlag) return true;
+ }
+ if (rec->frameNumber == currentFrame) foundFlag = true;
+ }
+
+ return true;
+}
+
+
+// Hotspot animation data
+
+HotspotAnimData::HotspotAnimData(HotspotAnimResource *rec) {
+ animRecordId = READ_LE_UINT16(&rec->animRecordId);
+ animId = READ_LE_UINT16(&rec->animId);
+ flags = READ_LE_UINT16(&rec->flags);
+
+ upFrame = rec->upFrame;
+ downFrame = rec->downFrame;
+ leftFrame = rec->leftFrame;
+ rightFrame = rec->rightFrame;
+}
+
+// Hotspot action lists
+
+HotspotActionList::HotspotActionList(uint16 id, byte *data) {
+ recordId = id;
+ uint16 numItems = READ_LE_UINT16(data);
+ data += 2;
+
+ HotspotActionRecord *actionRec = (HotspotActionRecord *) data;
+
+ for (int actionCtr = 0; actionCtr < numItems; ++actionCtr, ++actionRec) {
+ HotspotActionData *actionEntry = new HotspotActionData(actionRec);
+ push_back(actionEntry);
+ }
+}
+
+HotspotActionList *HotspotActionSet::getActions(uint16 recordId) {
+ HotspotActionSet::iterator i;
+ for (i = begin(); i != end(); ++i) {
+ HotspotActionList *list = *i;
+ if (list->recordId == recordId) return list;
+ }
+
+ return NULL;
+}
+
+// The following classes hold any sequence offsets that are being delayed
+
+SequenceDelayData::SequenceDelayData(uint16 delay, uint16 seqOffset) {
+ OSystem &system = System::getReference();
+
+ _timeoutCtr = system.getMillis() + delay;
+ _sequenceOffset = seqOffset;
+}
+
+void SequenceDelayList::addSequence(uint16 delay, uint16 seqOffset) {
+ SequenceDelayData *entry = new SequenceDelayData(delay, seqOffset);
+ push_back(entry);
+}
+
+void SequenceDelayList::tick() {
+ uint32 currTime = System::getReference().getMillis();
+ SequenceDelayList::iterator i;
+
+ for (i = begin(); i != end(); i++) {
+ SequenceDelayData *entry = *i;
+ if (entry->_timeoutCtr >= currTime) {
+ uint16 seqOffset = entry->_sequenceOffset;
+ erase(i);
+ Script::execute(seqOffset);
+ return;
+ }
+ }
+}
+
+// Field list and miscellaneous variables
+
+ValueTableData::ValueTableData() {
+ _numGroats = 0;
+
+ for (uint16 index = 0; index < NUM_VALUE_FIELDS; ++index)
+ _fieldList[index] = 0;
+}
+
+bool ValueTableData::isKnownField(uint16 fieldIndex) {
+ return (fieldIndex <= 8) || (fieldIndex == 10) || (fieldIndex == 15) ||
+ (fieldIndex == 18) || (fieldIndex == 20);
+}
+
+uint16 ValueTableData::getField(uint16 fieldIndex) {
+ if (fieldIndex > NUM_VALUE_FIELDS)
+ error("Invalid field index specified %d", fieldIndex);
+ if (!isKnownField(fieldIndex))
+ warning("Unknown field index %d in GET_FIELD opcode", fieldIndex);
+ return _fieldList[fieldIndex];
+}
+
+uint16 ValueTableData::getField(FieldName fieldName) {
+ return getField((uint16) fieldName);
+}
+
+void ValueTableData::setField(uint16 fieldIndex, uint16 value) {
+ if (fieldIndex > NUM_VALUE_FIELDS)
+ error("Invalid field index specified %d", fieldIndex);
+ _fieldList[fieldIndex] = value;
+ if (!isKnownField(fieldIndex))
+ warning("Unknown field index %d in SET_FIELD opcode", fieldIndex);
+}
+
+void ValueTableData::setField(FieldName fieldName, uint16 value) {
+ setField((uint16) fieldName, value);
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/res_struct.h b/engines/lure/res_struct.h
new file mode 100644
index 0000000000..418fb9fbdd
--- /dev/null
+++ b/engines/lure/res_struct.h
@@ -0,0 +1,401 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_resstruct_h__
+#define __lure_resstruct_h__
+
+#include "lure/luredefs.h"
+#include "common/list.h"
+
+namespace Lure {
+
+extern const char *actionList[];
+
+/*-------------------------------------------------------------------------*/
+/* Structure definitions */
+/* */
+/*-------------------------------------------------------------------------*/
+
+#if !defined(__GNUC__)
+#pragma START_PACK_STRUCTS
+#endif
+
+struct HotspotResource {
+ uint16 hotspotId;
+ uint16 nameId;
+ uint16 descId;
+ uint16 descId2;
+ uint32 actions;
+ uint16 actionsOffset;
+ uint16 roomNumber;
+ byte layer;
+ byte scriptLoadFlag;
+ uint16 loadOffset;
+ int16 startX;
+ int16 startY;
+ uint16 width;
+ uint16 height;
+ uint16 colourOffset;
+ uint16 animRecordId;
+ uint16 sequenceOffset;
+ uint16 tickProcOffset;
+ uint16 tickTimeout;
+} GCC_PACK;
+
+struct HotspotAnimResource {
+ uint16 animRecordId;
+ uint16 animId;
+ uint16 flags;
+ uint16 upOffset;
+ uint16 downOffset;
+ uint16 leftOffset;
+ uint16 rightOffset;
+ uint8 upFrame;
+ uint8 downFrame;
+ uint8 leftFrame;
+ uint8 rightFrame;
+} GCC_PACK;
+
+struct MovementResource {
+ uint16 frameNumber;
+ int16 xChange;
+ int16 yChange;
+} GCC_PACK;
+
+
+struct RoomResource {
+ uint16 roomNumber;
+ uint16 descId;
+ uint16 numLayers;
+ uint16 layers[4];
+ uint16 sequenceOffset;
+ uint16 numExits;
+} GCC_PACK;
+
+struct RoomExitResource {
+ int16 xs, xe, ys, ye;
+ uint16 sequenceOffset;
+ uint8 newRoom;
+ uint8 direction;
+ int16 newRoomX, newRoomY;
+} GCC_PACK;
+
+struct HotspotOverrideResource {
+ uint16 hotspotId;
+ int16 xs, xe, ys, ye;
+} GCC_PACK;
+
+struct RoomExitHotspotRecord {
+ uint16 hotspotId;
+ int16 xs, xe;
+ int16 ys, ye;
+ uint16 cursorNum;
+ uint16 destRoomNumber;
+} GCC_PACK;
+
+struct RoomExitJoinRecord {
+ uint16 hotspot1Id;
+ byte h1CurrentFrame;
+ byte h1DestFrame;
+ uint16 h1Unknown;
+ uint16 hotspot2Id;
+ byte h2CurrentFrame;
+ byte h2DestFrame;
+ uint16 h2Unknown;
+ byte blocked;
+ uint32 unknown;
+} GCC_PACK;
+
+struct HotspotActionRecord {
+ byte action;
+ uint16 sequenceOffset;
+} GCC_PACK;
+
+struct FileEntry {
+ uint16 id;
+ byte unused;
+ byte sizeExtension;
+ uint16 size;
+ uint16 offset;
+} GCC_PACK;
+
+struct VersionStructure {
+ uint16 id;
+ byte vMajor;
+ byte vMinor;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+#pragma END_PACK_STRUCTS
+#endif
+
+// Class template for a derived list that destroys the contained
+// object when the record containing it is destroyed. It's not
+// perfect, since the underlying list doesn't have virtual
+// methods, but it's sufficient for my usage
+
+template <class T>
+class ManagedList: public Common::List<T> {
+public:
+ ~ManagedList() {
+ clear();
+ }
+
+ void clear() {
+ typename Common::List<T>::iterator i;
+ for (i = Common::List<T>::begin(); i != Common::List<T>::end(); ++i)
+ delete *i;
+ Common::List<T>::clear();
+ }
+
+ typename Common::List<T>::iterator erase(typename Common::List<T>::iterator pos) {
+ delete *pos;
+ return Common::List<T>::erase(pos);
+ }
+
+ typename Common::List<T>::iterator erase(typename Common::List<T>::iterator first,
+ typename Common::List<T>::iterator last) {
+ typename Common::List<T>::iterator i;
+ for (i = first; i != last; ++i)
+ delete *i;
+ return Common::List<T>::erase(first, last);
+ }
+};
+
+// Enumeration used for direction facings
+
+enum Direction {UP, DOWN, LEFT, RIGHT, NO_DIRECTION};
+
+// Support classes to hold loaded resources
+
+class RoomExitHotspotData {
+public:
+ RoomExitHotspotData(RoomExitHotspotRecord *rec);
+
+ uint16 hotspotId;
+ int16 xs, xe;
+ int16 ys, ye;
+ uint16 cursorNum;
+ uint16 destRoomNumber;
+};
+
+typedef ManagedList<RoomExitHotspotData *> RoomExitHotspotList;
+
+class RoomExitData {
+public:
+ RoomExitData(RoomExitResource *rec);
+ bool insideRect(int16 xp, int16 yp);
+
+ int16 xs, xe, ys, ye;
+ uint16 sequenceOffset;
+ Direction direction;
+ uint8 roomNumber;
+ uint16 x, y;
+};
+
+class RoomExitList: public ManagedList<RoomExitData *> {
+public:
+ RoomExitData *checkExits(int16 xp, int16 yp);
+};
+
+#define MAX_NUM_LAYERS 4
+
+class RoomData {
+public:
+ RoomData(RoomResource *rec);
+
+ uint16 roomNumber;
+ uint16 descId;
+ uint16 numLayers;
+ uint16 layers[MAX_NUM_LAYERS];
+ uint16 sequenceOffset;
+ RoomExitHotspotList exitHotspots;
+ RoomExitList exits;
+};
+
+typedef ManagedList<RoomData *> RoomDataList;
+
+class RoomExitJoinData {
+public:
+ RoomExitJoinData(RoomExitJoinRecord *rec);
+
+ uint16 hotspot1Id;
+ byte h1CurrentFrame;
+ byte h1DestFrame;
+ uint16 h1Unknown;
+ uint16 hotspot2Id;
+ byte h2CurrentFrame;
+ byte h2DestFrame;
+ uint16 h2Unknown;
+ byte blocked;
+ uint32 unknown;
+};
+
+typedef ManagedList<RoomExitJoinData *> RoomExitJoinList;
+
+class HotspotActionData {
+public:
+ HotspotActionData(HotspotActionRecord *rec);
+
+ Action action;
+ uint16 sequenceOffset;
+};
+
+class HotspotActionList: public ManagedList<HotspotActionData *> {
+public:
+ uint16 recordId;
+
+ HotspotActionList(uint16 id, byte *data);
+ uint16 getActionOffset(Action action);
+};
+
+class HotspotActionSet: public ManagedList<HotspotActionList *> {
+public:
+ HotspotActionList *getActions(uint16 recordId);
+};
+
+class HotspotData {
+public:
+ HotspotData(HotspotResource *rec);
+
+ uint16 hotspotId;
+ uint16 nameId;
+ uint16 descId;
+ uint16 descId2;
+ uint32 actions;
+ uint16 actionsOffset;
+ byte flags;
+ uint16 roomNumber;
+ byte layer;
+ byte scriptLoadFlag;
+ uint16 loadOffset;
+ int16 startX;
+ int16 startY;
+ uint16 width;
+ uint16 height;
+ uint16 colourOffset;
+ uint16 animRecordId;
+ uint16 sequenceOffset;
+ uint16 tickProcOffset;
+ uint16 tickTimeout;
+};
+
+typedef ManagedList<HotspotData *> HotspotDataList;
+
+class HotspotOverrideData {
+public:
+ HotspotOverrideData(HotspotOverrideResource *rec);
+
+ uint16 hotspotId;
+ int16 xs, xe, ys, ye;
+};
+
+typedef ManagedList<HotspotOverrideData *> HotspotOverrideList;
+
+class MovementData {
+public:
+ MovementData(MovementResource *);
+
+ uint16 frameNumber;
+ int16 xChange;
+ int16 yChange;
+};
+
+class MovementDataList: public ManagedList<MovementData *> {
+public:
+ bool getFrame(uint16 currentFrame, int16 &xChange, int16 &yChange,
+ uint16 &nextFrame);
+};
+
+class HotspotAnimData {
+public:
+ HotspotAnimData(HotspotAnimResource *rec);
+
+ uint16 animRecordId;
+ uint16 animId;
+ uint16 flags;
+ uint8 upFrame;
+ uint8 downFrame;
+ uint8 leftFrame;
+ uint8 rightFrame;
+
+ MovementDataList leftFrames, rightFrames;
+ MovementDataList upFrames, downFrames;
+};
+
+typedef ManagedList<HotspotAnimData *> HotspotAnimList;
+
+// The following classes hold any sequence offsets that are being delayed
+
+class SequenceDelayData {
+ friend class SequenceDelayList;
+private:
+ uint32 _timeoutCtr;
+ uint16 _sequenceOffset;
+public:
+ SequenceDelayData(uint16 delay, uint16 seqOffset);
+};
+
+class SequenceDelayList: public ManagedList<SequenceDelayData *> {
+public:
+ void addSequence(uint16 delay, uint16 seqOffset);
+ void tick();
+};
+
+// The following class holds the field list used by the script engine as
+// well as miscellaneous fields used by the game.
+
+#define NUM_VALUE_FIELDS 85
+
+enum FieldName {
+ ROOM_NUMBER = 0,
+ CHARACTER_HOTSPOT_ID = 1,
+ USE_HOTSPOT_ID = 2,
+ ACTIVE_HOTSPOT_ID = 3,
+ SEQUENCE_RESULT = 4,
+ GENERAL = 5,
+ NEW_ROOM_NUMBER = 7,
+ GENERAL_STATUS = 8,
+ TORCH_HIDE = 10,
+ PRISONER_DEAD = 15,
+ BOTTLE_FILLED = 18,
+ SACK_CUT = 20
+};
+
+class ValueTableData {
+private:
+ uint16 _numGroats;
+ uint16 _fieldList[NUM_VALUE_FIELDS];
+ bool isKnownField(uint16 fieldIndex);
+public:
+ ValueTableData();
+ uint16 getField(uint16 fieldIndex);
+ uint16 getField(FieldName fieldName);
+
+ void setField(uint16 fieldIndex, uint16 value);
+ void setField(FieldName fieldName, uint16 value);
+ uint16 &numGroats() { return _numGroats; }
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/room.cpp b/engines/lure/room.cpp
new file mode 100644
index 0000000000..748ffccd28
--- /dev/null
+++ b/engines/lure/room.cpp
@@ -0,0 +1,485 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/room.h"
+#include "lure/luredefs.h"
+#include "lure/res.h"
+#include "lure/screen.h"
+#include "lure/events.h"
+#include "lure/strings.h"
+#include "lure/scripts.h"
+
+namespace Lure {
+
+static Room *int_room;
+
+RoomLayer::RoomLayer(uint16 screenId, bool backgroundLayer):
+ Surface(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT) {
+ loadScreen(screenId);
+ byte cellIndex = 0;
+ byte *screenData = data().data();
+
+ memset(_cells, 0xff, FULL_HORIZ_RECTS*FULL_VERT_RECTS);
+
+ // Loop through each cell of the screen
+ for (int cellY = 0; cellY < NUM_VERT_RECTS; ++cellY) {
+ for (int cellX = 0; cellX < NUM_HORIZ_RECTS; ++cellX) {
+ bool hasPixels = false;
+
+ if (backgroundLayer) {
+ hasPixels = true;
+ } else {
+ // Check the cell
+ for (int yP = 0; yP < RECT_SIZE; ++yP) {
+ if (hasPixels) break;
+ byte *linePos = screenData + (cellY * RECT_SIZE + yP + 8)
+ * FULL_SCREEN_WIDTH + (cellX * RECT_SIZE);
+
+ for (int xP = 0; xP < RECT_SIZE; ++xP) {
+ hasPixels = *linePos++ != 0;
+ if (hasPixels) break;
+ }
+ }
+ }
+
+ _cells[(cellY + NUM_EDGE_RECTS) * FULL_HORIZ_RECTS + NUM_EDGE_RECTS +
+ cellX] = !hasPixels ? 0xff : cellIndex++;
+ }
+ }
+}
+
+Room::Room(): _screen(Screen::getReference()) {
+ int_room = this;
+
+ _roomData = NULL;
+ _hotspotName[0] = '\0';
+ for (int ctr = 0; ctr < MAX_NUM_LAYERS; ++ctr) _layers[ctr] = NULL;
+ _numLayers = 0;
+ _showInfo = false;
+ _currentAction = NONE;
+}
+
+Room::~Room() {
+ for (int layerNum = 0; layerNum < _numLayers; ++layerNum)
+ if (_layers[layerNum])
+ delete _layers[layerNum];
+
+ int_room = NULL;
+}
+
+Room &Room::getReference() {
+ return *int_room;
+}
+
+// leaveRoom
+// Handles leaving the current room
+
+void Room::leaveRoom() {
+ Resources &r = Resources::getReference();
+
+ // Deallocate graphical layers from the room
+ for (int layerNum = 0; layerNum < _numLayers; ++layerNum)
+ if (_layers[layerNum]) {
+ delete _layers[layerNum];
+ _layers[layerNum] = NULL;
+ }
+
+ // Scan through the hotspot list and remove any uneeded entries
+//r.activeHotspots().clear();
+ HotspotList &list = r.activeHotspots();
+ HotspotList::iterator i = list.begin();
+ while (i != list.end()) {
+ Hotspot *h = i.operator*();
+ if (!h->persistant()) {
+ i = list.erase(i);
+ } else {
+ ++i;
+ }
+ }
+}
+
+void Room::loadRoomHotspots() {
+ Resources &r = Resources::getReference();
+ HotspotDataList &list = r.hotspotData();
+
+ HotspotDataList::iterator i;
+ for (i = list.begin(); i != list.end(); ++i) {
+ HotspotData *rec = *i;
+
+ if ((rec->hotspotId < 0x7530) && (rec->roomNumber == _roomNumber) &&
+ (rec->layer != 0))
+ r.activateHotspot(rec->hotspotId);
+ }
+}
+
+void Room::checkRoomHotspots() {
+ Mouse &m = Mouse::getReference();
+ Resources &r = Resources::getReference();
+ HotspotDataList &list = r.hotspotData();
+ HotspotData *entry = NULL;
+ int16 currentX = m.x();
+ int16 currentY = m.y();
+
+ HotspotDataList::iterator i;
+ for (i = list.begin(); i != list.end(); ++i) {
+ entry = *i;
+
+ bool skipFlag = (entry->roomNumber != _roomNumber);
+ if (!skipFlag) {
+ skipFlag = (((entry->flags & 0x80) == 0) &&
+ ((entry->flags & 0x40) != 0)) ||
+ ((entry->flags & 0x20) != 0);
+ }
+
+ if ((!skipFlag) && (entry->hotspotId < 0x409))
+ skipFlag = sub_112();
+
+ if (!skipFlag && (entry->hotspotId >= 0x2710) && (entry->hotspotId <= 0x27ff)) {
+ RoomExitJoinData *rec = r.getExitJoin(entry->hotspotId);
+ if ((rec) && (!rec->blocked))
+ // Hotspot is over a room exit, and it's not blocked, so don't
+ // register it as an active hotspot
+ skipFlag = true;
+ }
+
+ if (!skipFlag) {
+ // Check for a hotspot override
+ HotspotOverrideData *hsEntry = r.getHotspotOverride(entry->hotspotId);
+
+ if (hsEntry) {
+ // Check whether cursor is in override hotspot area
+ if ((currentX >= hsEntry->xs) && (currentX <= hsEntry->xe) &&
+ (currentY >= hsEntry->ys) && (currentY <= hsEntry->ye))
+ // Found to be in hotspot entry
+ break;
+ } else {
+ // Check whether cursor is in default hospot area
+ if ((currentX >= entry->startX) && (currentX < entry->startX + entry->width) &&
+ (currentY >= entry->startY) && (currentY < entry->startY + entry->height))
+ // Found hotspot entry
+ break;
+ }
+ }
+ }
+
+ if (i == list.end()) {
+ _hotspotId = 0;
+ _hotspotNameId = 0;
+ _hotspot = NULL;
+ } else {
+ _hotspotNameId = entry->nameId;
+ _hotspot = entry;
+ _hotspotId = entry->hotspotId;
+ }
+}
+
+uint8 Room::checkRoomExits() {
+ Mouse &m = Mouse::getReference();
+ Resources &r = Resources::getReference();
+
+ RoomExitHotspotList &exits = _roomData->exitHotspots;
+ if (exits.isEmpty()) return CURSOR_ARROW;
+ RoomExitJoinData *join;
+ bool skipFlag;
+
+ RoomExitHotspotList::iterator i;
+ for (i = exits.begin(); i != exits.end(); ++i) {
+ RoomExitHotspotData *rec = *i;
+ skipFlag = false;
+
+ if (rec->hotspotId != 0) {
+ join = r.getExitJoin(rec->hotspotId);
+ if ((join) && (join->blocked != 0))
+ skipFlag = true;
+ }
+
+ if (!skipFlag && (m.x() >= rec->xs) && (m.x() <= rec->xe) &&
+ (m.y() >= rec->ys) && (m.y() <= rec->ye)) {
+ // Cursor is within exit area
+ uint8 cursorNum = rec->cursorNum;
+
+ // If it's a hotspotted exit, change arrow to the + arrow
+ if (rec->hotspotId != 0) cursorNum += 7;
+
+ return cursorNum;
+ }
+ }
+
+ // No room exits found
+ return CURSOR_ARROW;
+}
+
+void Room::flagCoveredCells(Hotspot &h) {
+ int16 yStart = (h.y() - MENUBAR_Y_SIZE) / RECT_SIZE;
+ int16 yEnd = (h.y() + h.height() - 1 - MENUBAR_Y_SIZE) / RECT_SIZE;
+ int16 numY = yEnd - yStart + 1;
+ int16 xStart = h.x() / RECT_SIZE;
+ int16 xEnd = (h.x() + h.width() - 1) / RECT_SIZE;
+ int16 numX = xEnd - xStart + 1;
+
+ int index = yStart * NUM_HORIZ_RECTS + xStart;
+
+ for (int16 yP = 0; yP < numY; ++yP) {
+ for (int16 xP = 0; xP < numX; ++xP) {
+ int indexPos = index + xP;
+ if ((indexPos < 0) || (indexPos >= NUM_HORIZ_RECTS*NUM_VERT_RECTS))
+ continue;
+ _cells[index+xP] |= 0x81;
+ _cells2[index+xP] |= 1;
+ }
+ index += NUM_HORIZ_RECTS;
+ }
+}
+
+void Room::addAnimation(Hotspot &h) {
+ Surface &s = _screen.screen();
+ char buffer[10];
+ h.copyTo(&s);
+
+ if (_showInfo) {
+ int16 x = h.x();
+ int16 y = h.y();
+ if ((x >= 0) && (x <= 319) && (y >= 0) && (y <= 200)) {
+ sprintf(buffer, "%x", h.resource().hotspotId);
+ strcat(buffer, "h");
+ s.writeString(h.x(), h.y(), buffer, false);
+ }
+ }
+}
+
+void Room::addLayers(Hotspot &h) {
+ int16 hsX = h.x() + (4 * RECT_SIZE);
+ int16 hsY = h.y() + (4 * RECT_SIZE) - MENUBAR_Y_SIZE;
+
+ int16 xStart = hsX / RECT_SIZE;
+ int16 xEnd = (hsX + h.width()) / RECT_SIZE;
+ int16 numX = xEnd - xStart + 1;
+ int16 yStart = hsY / RECT_SIZE;
+ int16 yEnd = (hsY + h.height() - 1) / RECT_SIZE;
+ int16 numY = yEnd - yStart + 1;
+
+ for (int16 xCtr = 0; xCtr < numX; ++xCtr, ++xStart) {
+ int16 xs = xStart - 4;
+ if (xs < 0) continue;
+
+ // Check foreground layers for an occupied one
+/* DEBUG
+ int layerNum = 1;
+ while ((layerNum < _numLayers) &&
+ !_layers[layerNum]->isOccupied(xStart, yEnd))
+ ++layerNum;
+ if (layerNum == _numLayers) continue;
+*/
+ int layerNum = _numLayers - 1;
+ while ((layerNum > 0) &&
+ !_layers[layerNum]->isOccupied(xStart, yEnd))
+ --layerNum;
+ if (layerNum == 0) continue;
+
+ int16 ye = yEnd - 4;
+ for (int16 yCtr = 0; yCtr < numY; ++yCtr, --ye) {
+ if (ye < 0) break;
+ addCell(xs, ye, layerNum);
+ }
+ }
+}
+
+void Room::addCell(int16 xp, int16 yp, int layerNum) {
+ Surface &s = _screen.screen();
+
+ while ((layerNum > 0) && !_layers[layerNum]->isOccupied(xp+4, yp+4))
+ --layerNum;
+ if (layerNum == 0) return;
+/* DEBUG
+ while ((layerNum < _numLayers) && !_layers[layerNum]->isOccupied(xp+4, yp+4))
+ ++layerNum;
+ if (layerNum == _numLayers) return;
+*/
+ RoomLayer *layer = _layers[layerNum];
+
+ int index = ((yp * RECT_SIZE + 8) * FULL_SCREEN_WIDTH) + (xp * RECT_SIZE);
+ byte *srcPos = layer->data().data() + index;
+ byte *destPos = s.data().data() + index;
+
+ for (int yCtr = 0; yCtr < RECT_SIZE; ++yCtr) {
+ for (int xCtr = 0; xCtr < RECT_SIZE; ++xCtr, ++destPos) {
+ byte pixel = *srcPos++;
+ if (pixel) *destPos = pixel;
+ }
+
+ // Move to start of next cell line
+ srcPos += FULL_SCREEN_WIDTH - RECT_SIZE;
+ destPos += FULL_SCREEN_WIDTH - RECT_SIZE;
+ }
+
+ // Note: old version of screen layers load compresses loaded layers down to
+ // only a set of the non-empty rects. Since modern memory allows me to load
+ // all the layers completely, I'm bypassing the need to use cell index values
+}
+
+void Room::update() {
+ Surface &s = _screen.screen();
+ Resources &r = Resources::getReference();
+ HotspotList &hotspots = r.activeHotspots();
+ HotspotList::iterator i;
+
+ memset(_cells, 0x81, NUM_HORIZ_RECTS*NUM_VERT_RECTS);
+ memset(_cells2, 0x81, NUM_HORIZ_RECTS*NUM_VERT_RECTS);
+
+ _layers[0]->copyTo(&s);
+ for (int ctr = 1; ctr < _numLayers; ++ctr)
+ _layers[ctr]->transparentCopyTo(&s);
+
+ // Handle first layer (layer 3)
+ for (i = hotspots.begin(); i != hotspots.end(); ++i) {
+ Hotspot &h = *i.operator*();
+ if ((h.roomNumber() == _roomNumber) && h.isActiveAnimation() && (h.layer() == 3)) {
+ flagCoveredCells(h);
+ addAnimation(h);
+ addLayers(h);
+ }
+ }
+
+ // Handle second layer (layer 1) - do in order of Y axis
+ List<Hotspot *> tempList;
+ List<Hotspot *>::iterator iTemp;
+ for (i = hotspots.begin(); i != hotspots.end(); ++i) {
+ Hotspot *h = i.operator*();
+ if ((h->roomNumber() != _roomNumber) || !h->isActiveAnimation()
+ || (h->layer() != 1))
+ continue;
+ int16 endY = h->y() + h->height();
+
+ for (iTemp = tempList.begin(); iTemp != tempList.end(); ++iTemp) {
+ Hotspot *hTemp = iTemp.operator*();
+ int16 tempY = hTemp->y() + hTemp->height();
+ if (endY < tempY) {
+ if (iTemp != tempList.begin()) --iTemp;
+ break;
+ }
+ }
+ tempList.insert(iTemp, h);
+ }
+ for (iTemp = tempList.begin(); iTemp != tempList.end(); ++iTemp) {
+ Hotspot &h = *iTemp.operator*();
+ flagCoveredCells(h);
+ addAnimation(h);
+ addLayers(h);
+ }
+
+ // Handle third layer (layer 2)
+ for (i = hotspots.begin(); i != hotspots.end(); ++i) {
+ Hotspot &h = *i.operator*();
+ if ((h.roomNumber() == _roomNumber) && h.isActiveAnimation() && (h.layer() == 2)) {
+ flagCoveredCells(h);
+ addAnimation(h);
+ }
+ }
+
+ // Handle showing name of highlighted hotspot
+ if (_hotspotName[0] != '\0') {
+ if (_currentAction == NONE) {
+ s.writeString(0, 0, _hotspotName, false, DIALOG_TEXT_COLOUR);
+ } else {
+ char buffer[MAX_ACTION_NAME_SIZE + MAX_HOTSPOT_NAME_SIZE];
+ strcpy(buffer, actionList[_currentAction]);
+ strcat(buffer, " ");
+ strcat(buffer, _hotspotName);
+ s.writeString(0, 0, buffer, false, DIALOG_WHITE_COLOUR);
+ }
+ }
+
+ // If show information is turned on, show room and position
+ if (_showInfo) {
+ char buffer[64];
+ Mouse &m = Mouse::getReference();
+ sprintf(buffer, "Room %d Pos (%d,%d)", _roomNumber, m.x(), m.y());
+ s.writeString(FULL_SCREEN_WIDTH / 2, 0, buffer, false, DIALOG_TEXT_COLOUR);
+ }
+
+ _screen.update();
+}
+
+void Room::setRoomNumber(uint16 newRoomNumber, bool showOverlay) {
+ Resources &r = Resources::getReference();
+ _roomData = r.getRoom(newRoomNumber);
+ if (!_roomData)
+ error("Tried to change to non-existant room: %d", newRoomNumber);
+
+ _roomNumber = _roomData->roomNumber;
+ _descId = _roomData->descId;
+
+ _screen.empty();
+ _screen.resetPalette();
+
+ if (_layers[0]) leaveRoom();
+
+ _numLayers = _roomData->numLayers;
+ if (showOverlay) ++_numLayers;
+
+ uint16 paletteId = (_roomData->layers[0] & 0xffe0) - 1;
+
+ for (uint8 layerNum = 0; layerNum < _numLayers; ++layerNum)
+ _layers[layerNum] = new RoomLayer(_roomData->layers[layerNum],
+ layerNum == 0);
+
+ // Load in the palette, add in the two replacements segments, and then
+ // set to the system palette
+ Palette p(228, NULL, RGB64);
+ Palette tempPalette(paletteId);
+ p.copyFrom(&tempPalette);
+ r.insertPaletteSubset(p);
+ _screen.setPalette(&p);
+
+ if (_roomData->sequenceOffset != 0xffff)
+ Script::execute(_roomData->sequenceOffset);
+ loadRoomHotspots();
+ cursorMoved();
+
+ update();
+}
+
+// cursorMoved
+// Called as the cursor moves to handle any changes that must occur
+
+void Room::cursorMoved() {
+ uint16 cursorNew = CURSOR_ARROW;
+ uint16 oldHotspotId = _hotspotId;
+
+ Mouse &m = Mouse::getReference();
+ checkRoomHotspots();
+
+ if (_hotspotId != 0) {
+ cursorNew = CURSOR_CROSS;
+
+ if (oldHotspotId != _hotspotId)
+ StringData::getReference().getString(_hotspotNameId, _hotspotName, NULL, NULL);
+ } else {
+ _hotspotName[0] = '\0';
+ cursorNew = checkRoomExits();
+ }
+
+ if (m.getCursorNum() != cursorNew)
+ m.setCursorNum(cursorNew);
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/room.h b/engines/lure/room.h
new file mode 100644
index 0000000000..8553722cda
--- /dev/null
+++ b/engines/lure/room.h
@@ -0,0 +1,107 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_room_h__
+#define __lure_room_h__
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "lure/disk.h"
+#include "lure/res.h"
+#include "lure/memory.h"
+#include "lure/surface.h"
+#include "lure/screen.h"
+#include "lure/hotspots.h"
+
+namespace Lure {
+
+#define RECT_SIZE 32
+#define NUM_HORIZ_RECTS 10
+#define NUM_VERT_RECTS 6
+#define FULL_HORIZ_RECTS 18
+#define FULL_VERT_RECTS 14
+#define NUM_EDGE_RECTS 4
+
+class RoomLayer: public Surface {
+private:
+ byte _cells[FULL_HORIZ_RECTS*FULL_VERT_RECTS];
+public:
+ RoomLayer(uint16 screenId, bool backgroundLayer);
+ byte cellVal(byte cellX, byte cellY) {
+ return _cells[FULL_HORIZ_RECTS*cellY + cellX];
+ }
+ bool isOccupied(byte cellX, byte cellY) {
+ return cellVal(cellX, cellY) != 0xff;
+ }
+};
+
+class Room {
+private:
+ RoomData *_roomData;
+ Screen &_screen;
+ uint16 _roomNumber;
+ uint16 _descId;
+ uint16 _hotspotId;
+ uint16 _hotspotNameId;
+ Action _currentAction;
+ char _hotspotName[MAX_HOTSPOT_NAME_SIZE + MAX_ACTION_NAME_SIZE];
+ HotspotData *_hotspot;
+ bool _showInfo;
+ uint8 _numLayers;
+ RoomLayer *_layers[MAX_NUM_LAYERS];
+ byte _cells[NUM_HORIZ_RECTS*NUM_VERT_RECTS];
+ byte _cells2[NUM_HORIZ_RECTS*NUM_VERT_RECTS];
+
+ void checkRoomHotspots();
+ uint8 checkRoomExits();
+ void loadRoomHotspots();
+ bool sub_112() { return false; } // not yet implemented
+ void flagCoveredCells(Hotspot &h);
+ void addAnimation(Hotspot &h);
+ void addLayers(Hotspot &h);
+ void addCell(int16 xp, int16 yp, int layerNum);
+public:
+ Room();
+ ~Room();
+ static Room &getReference();
+
+ void update();
+ void nextFrame();
+ void cursorMoved();
+ uint16 roomNumber() { return _roomNumber; }
+ void setRoomNumber(uint16 newRoomNumber, bool showOverlay = false);
+ void leaveRoom();
+ void setAction(Action action) { _currentAction = action; }
+ Action getCurrentAction() { return _currentAction; }
+ uint16 hotspotId() { return _hotspotId; }
+ uint32 hotspotActions() { return _hotspot->actions & 0x10ffffff; }
+ uint8 hotspotFlags() { return (_hotspot->actions >> 24) & 0xfe; }
+ HotspotData &hotspot() { return *_hotspot; }
+ uint16 descId() { return _descId; }
+ bool showInfo() { return _showInfo; }
+ void setShowInfo(bool value) { _showInfo = value; }
+ uint32 xyzzy() { return (uint32) _layers[3]; }
+};
+
+} // end of namespace Lure
+
+#endif
diff --git a/engines/lure/screen.cpp b/engines/lure/screen.cpp
new file mode 100644
index 0000000000..ad84c9e838
--- /dev/null
+++ b/engines/lure/screen.cpp
@@ -0,0 +1,152 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/screen.h"
+#include "lure/luredefs.h"
+#include "lure/memory.h"
+#include "lure/disk.h"
+#include "lure/decode.h"
+
+namespace Lure {
+
+static Screen *int_disk = NULL;
+
+Screen &Screen::getReference() {
+ return *int_disk;
+}
+
+Screen::Screen(OSystem &system): _system(system),
+ _screen(new Surface(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT)),
+ _disk(Disk::getReference()),
+ _palette(new Palette(GAME_PALETTE_RESOURCE_ID)) {
+ int_disk = this;
+ _screen->empty();
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+}
+
+Screen::~Screen() {
+ delete _screen;
+ delete _palette;
+}
+
+// setPaletteEmpty
+// Defaults the palette to an empty set
+
+void Screen::setPaletteEmpty() {
+ delete _palette;
+ _palette = new Palette();
+
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+ _system.updateScreen();
+}
+
+// setPalette
+// Sets the current palette to the passed palette
+
+void Screen::setPalette(Palette *p) {
+ _palette->copyFrom(p);
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+ _system.updateScreen();
+}
+
+// paletteFadeIn
+// Fades in the palette. For proper operation, the palette should have been
+// previously set to empty
+
+void Screen::paletteFadeIn(Palette *p) {
+ bool changed;
+ byte *const pDest = p->data();
+ byte *const pTemp = _palette->data();
+
+ do {
+ changed = false;
+
+ for (int palCtr = 0; palCtr < p->numEntries() * 4; ++palCtr)
+ {
+ if (palCtr % PALETTE_FADE_INC_SIZE == (PALETTE_FADE_INC_SIZE - 1)) continue;
+ bool isDifferent = pTemp[palCtr] < pDest[palCtr];
+ if (isDifferent) {
+ if (pDest[palCtr] - pTemp[palCtr] < PALETTE_FADE_INC_SIZE)
+ pTemp[palCtr] = pDest[palCtr];
+ else pTemp[palCtr] += PALETTE_FADE_INC_SIZE;
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+ _system.updateScreen();
+ _system.delayMillis(20);
+ }
+ } while (changed);
+}
+
+// paletteFadeOut
+// Fades the screen to black by gradually decreasing the palette colours
+
+void Screen::paletteFadeOut() {
+ bool changed;
+
+ do {
+ byte *pTemp = _palette->data();
+ changed = false;
+
+ for (uint32 palCtr = 0; palCtr < _palette->palette()->size(); ++palCtr, ++pTemp) {
+ if (palCtr % PALETTE_FADE_INC_SIZE == (PALETTE_FADE_INC_SIZE - 1))
+ continue;
+ bool isDifferent = *pTemp > 0;
+ if (isDifferent) {
+ if (*pTemp < PALETTE_FADE_INC_SIZE) *pTemp = 0;
+ else *pTemp -= PALETTE_FADE_INC_SIZE;
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+ _system.updateScreen();
+ _system.delayMillis(20);
+ }
+ } while (changed);
+}
+
+void Screen::resetPalette() {
+ Palette p(GAME_PALETTE_RESOURCE_ID);
+ setPalette(&p);
+}
+
+void Screen::empty() {
+ _screen->empty();
+ update();
+}
+
+void Screen::update() {
+ _system.copyRectToScreen(screen_raw(), FULL_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+ _system.updateScreen();
+}
+
+void Screen::updateArea(uint16 x, uint16 y, uint16 w, uint16 h) {
+ _system.copyRectToScreen(screen_raw(), FULL_SCREEN_WIDTH, x, y, w, h);
+ _system.updateScreen();
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/screen.h b/engines/lure/screen.h
new file mode 100644
index 0000000000..064fec0e96
--- /dev/null
+++ b/engines/lure/screen.h
@@ -0,0 +1,64 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_screen_h__
+#define __lure_screen_h__
+
+#include "common/stdafx.h"
+#include "base/engine.h"
+#include "lure/luredefs.h"
+#include "lure/palette.h"
+#include "lure/disk.h"
+#include "lure/memory.h"
+#include "lure/surface.h"
+
+namespace Lure {
+
+class Screen {
+private:
+ OSystem &_system;
+ Disk &_disk;
+ Surface *_screen;
+ Palette *_palette;
+public:
+ Screen(OSystem &system);
+ ~Screen();
+ static Screen &getReference();
+
+ void setPaletteEmpty();
+ void setPalette(Palette *p);
+ Palette &getPalette() { return *_palette; }
+ void paletteFadeIn(Palette *p);
+ void paletteFadeOut();
+ void resetPalette();
+ void empty();
+ void update();
+ void updateArea(uint16 x, uint16 y, uint16 w, uint16 h);
+
+ Surface &screen() { return *_screen; }
+ uint8 *screen_raw() { return _screen->data().data(); }
+ uint8 *pixel_raw(uint16 x, uint16 y) { return screen_raw() + (y * FULL_SCREEN_WIDTH) + x; }
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/scripts.cpp b/engines/lure/scripts.cpp
new file mode 100644
index 0000000000..073753a93f
--- /dev/null
+++ b/engines/lure/scripts.cpp
@@ -0,0 +1,576 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/scripts.h"
+#include "lure/res.h"
+#include "lure/game.h"
+#include "common/stack.h"
+
+namespace Lure {
+
+/*------------------------------------------------------------------------*/
+/*- Script Method List -*/
+/*- -*/
+/*------------------------------------------------------------------------*/
+
+// activateHotspot
+// Activates a hotspot entry for active use
+
+void Script::activateHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources::getReference().activateHotspot(hotspotId);
+}
+
+// setHotspotScript
+// Sets a hotspot's animation script offset from a master table of offsets
+
+void Script::setHotspotScript(uint16 hotspotId, uint16 scriptIndex, uint16 v3) {
+ Resources &r = Resources::getReference();
+ uint16 offset = r.getHotspotScript(scriptIndex);
+ HotspotData *rsc = r.getHotspot(hotspotId);
+ rsc->sequenceOffset = offset;
+}
+
+// Clears the sequence delay list
+
+void Script::clearSequenceDelayList(uint16 v1, uint16 scriptIndex, uint16 v3) {
+ Resources::getReference().delayList().clear();
+}
+
+// deactivates the specified hotspot from active animation
+
+void Script::deactivateHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources &rsc = Resources::getReference();
+ if (hotspotId < START_NONVISUAL_HOTSPOT_ID)
+ rsc.deactivateHotspot(hotspotId);
+ HotspotData *hs = rsc.getHotspot(hotspotId);
+ hs->flags |= 0x20;
+ if (hotspotId < START_NONVISUAL_HOTSPOT_ID)
+ hs->layer = 0xff;
+}
+
+// Sets the offset for the table of action sequence offsets for the given
+// hotspot
+
+void Script::setActionsOffset(uint16 hotspotId, uint16 offset, uint16 v3) {
+ Resources &res = Resources::getReference();
+ HotspotData *hotspot = res.getHotspot(hotspotId);
+
+ if (!res.getHotspotActions(offset))
+ warning("Hotspot %d set to invalid actions offset %d",
+ hotspotId, offset);
+
+ hotspot->actionsOffset = offset;
+}
+
+// Add a sequence to be executed after a specified delay
+
+void Script::addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 v3) {
+ SequenceDelayList &list = Resources::getReference().delayList();
+ list.addSequence(delay, seqOffset);
+}
+
+// Checks whether the given character is in the specified room, and stores
+// the result in the general value field
+
+void Script::characterInRoom(uint16 characterId, uint16 roomNumber, uint16 v3) {
+ Resources &res = Resources::getReference();
+ uint16 result = 0;
+ if (characterId >= PLAYER_ID) {
+ HotspotData *hotspot = res.getHotspot(characterId);
+ if (hotspot->roomNumber == roomNumber)
+ result = 1;
+ }
+
+ res.fieldList().setField(GENERAL, result);
+}
+
+// Changes the given hotspot's name to a new name
+
+void Script::setHotspotName(uint16 hotspotId, uint16 nameId, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->nameId = nameId;
+}
+
+// Displays the given string resource Id in a dialog
+
+void Script::displayDialog(uint16 stringId, uint16 v2, uint16 v3) {
+ Dialog::show(stringId);
+}
+
+// Flags for remotely viewing a room
+
+void Script::remoteRoomViewSetup(uint16 v1, uint16 v2, uint16 v3) {
+ Hotspot *player = Resources::getReference().getActiveHotspot(PLAYER_ID);
+ player->setTickProc(0); // disable player actions
+ Game::getReference().setRemoteView();
+}
+
+// Gets the current blocked state for the given door and stores it in the
+// general value field
+
+void Script::getDoorBlocked(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources &res = Resources::getReference();
+ RoomExitJoinData *joinRec = res.getExitJoin(hotspotId);
+ res.fieldList().setField(GENERAL, joinRec->blocked);
+}
+
+// Decrements the number of inventory itemst he player has
+
+void Script::decrInventoryItems(uint16 v1, uint16 v2, uint16 v3) {
+ // module currently doesn't use a static counter for the number of
+ // inventory items, so don't do anything
+}
+
+// Sets the current frame number for the given hotspot
+
+void Script::setFrameNumber(uint16 hotspotId, uint16 frameNumber, uint16 v3) {
+ Hotspot *hotspot = Resources::getReference().getActiveHotspot(hotspotId);
+ hotspot->setFrameNumber(frameNumber);
+}
+
+// Disables the given hotspot from being highlighted by the cursor
+
+void Script::disableHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->flags |= 0x20;
+}
+
+// Increase the player's number by the specified amount
+
+void Script::increaseNumGroats(uint16 v1, uint16 numGroats, uint16 v3) {
+ ValueTableData &fields = Resources::getReference().fieldList();
+ fields.numGroats() += numGroats;
+}
+
+// Enables the flags for the given hotspot for it to be actively highlighted
+
+void Script::enableHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ // Clear flag 0x20 and add flag 0x80
+ hotspot->flags = (hotspot->flags & 0xdf) | 0x80;
+}
+
+// Marks the door in room 14 for closing
+
+void Script::room14DoorClose(uint16 v1, uint16 v2, uint16 v3) {
+ RoomExitJoinData *joinRec = Resources::getReference().getExitJoin(0x2719);
+ joinRec->blocked = 1;
+}
+
+// Sets the sequence result to 1 if the given secondary description for a
+// hotspot is empty (for inventory items, this gives the description before
+// the item is initially picked up)
+
+void Script::checkDroppedDesc(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources &res = Resources::getReference();
+ HotspotData *hotspot = res.getHotspot(hotspotId);
+ uint16 seqResult = (hotspot->descId2 == 0) ? 1 : 0;
+ res.fieldList().setField(SEQUENCE_RESULT, seqResult);
+}
+
+// Marks the given door hotspot for closing
+
+void Script::doorClose(uint16 hotspotId, uint16 v2, uint16 v3) {
+ RoomExitJoinData *joinRec = Resources::getReference().getExitJoin(hotspotId);
+ if (!joinRec) error("Tried to close a non-door");
+ joinRec->blocked = 1;
+}
+
+// Marks the given door hotspot for opening
+
+void Script::doorOpen(uint16 hotspotId, uint16 v2, uint16 v3) {
+ RoomExitJoinData *joinRec = Resources::getReference().getExitJoin(hotspotId);
+ if (!joinRec) error("Tried to close a non-door");
+ joinRec->blocked = 0;
+}
+
+// Lookup the given message Id for the specified character and display in a dialog
+
+void Script::displayMessage(uint16 messageId, uint16 characterId, uint16 unknownVal) {
+ Dialog::showMessage(messageId, characterId);
+}
+
+// Assign the given hotspot item to the player's inventory
+
+void Script::givePlayerItem(uint16 hotspotId, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->roomNumber = PLAYER_ID;
+ hotspot->flags |= 0x80;
+}
+
+// Decrease the number of graots the player has
+
+void Script::decreaseNumGroats(uint16 characterId, uint16 numGroats, uint16 v3) {
+ ValueTableData &fields = Resources::getReference().fieldList();
+ fields.numGroats() -= numGroats;
+}
+
+// Sets the tick handler for the village Skorl to an alternate handler
+
+void Script::setVillageSkorlTickProc(uint16 v1, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(0x3F1);
+ hotspot->tickProcOffset = 0x7efa;
+}
+
+// Stores the current number of groats in the general field
+
+void Script::getNumGroats(uint16 v1, uint16 v2, uint16 v3) {
+ ValueTableData fields = Resources::getReference().fieldList();
+ fields.setField(GENERAL, fields.numGroats());
+}
+
+// Loads the specified animation, completely bypassing the standard process
+// of checking for a load proc/sequence
+
+void Script::animationLoad(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources::getReference().addHotspot(hotspotId);
+}
+
+// Adds the passed actions to the available actions for the given hotspot
+
+void Script::addActions(uint16 hotspotId, uint16 actions, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->actions |= actions;
+}
+
+// Checks the status of the cell door, and starts music depending on it's state
+
+void Script::checkCellDoor(uint16 v1, uint16 v2, uint16 v3) {
+ // In the original game, this method checks to see if the cell door
+ // is currently open, if it is, starts a music sequence. I'll
+ // implement this method properly when I get around to implementing
+ // the in-game music
+}
+
+typedef void(*SequenceMethodPtr)(uint16, uint16, uint16);
+
+struct SequenceMethodRecord {
+ uint8 methodIndex;
+ SequenceMethodPtr proc;
+};
+
+SequenceMethodRecord scriptMethods[] = {
+ {0, Script::activateHotspot},
+ {1, Script::setHotspotScript},
+ {4, Script::clearSequenceDelayList},
+ {6, Script::deactivateHotspot},
+ {8, Script::addDelayedSequence},
+ {10, Script::characterInRoom},
+ {11, Script::setActionsOffset},
+ {12, Script::setHotspotName},
+ {16, Script::displayDialog},
+ {18, Script::remoteRoomViewSetup},
+ {20, Script::checkCellDoor},
+ {22, Script::getDoorBlocked},
+ {28, Script::decrInventoryItems},
+ {30, Script::setFrameNumber},
+ {32, Script::disableHotspot},
+ {34, Script::increaseNumGroats},
+ {35, Script::enableHotspot},
+ {39, Script::room14DoorClose},
+ {40, Script::checkDroppedDesc},
+ {42, Script::doorClose},
+ {44, Script::doorOpen},
+ {47, Script::displayMessage},
+ {50, Script::givePlayerItem},
+ {51, Script::decreaseNumGroats},
+ {54, Script::setVillageSkorlTickProc},
+ {57, Script::getNumGroats},
+ {62, Script::animationLoad},
+ {63, Script::addActions},
+ {65, Script::checkCellDoor},
+ {0xff, NULL}};
+
+/*------------------------------------------------------------------------*/
+/*- Script Execution -*/
+/*- -*/
+/*------------------------------------------------------------------------*/
+
+uint16 Script::execute(uint16 startOffset) {
+ Resources &r = Resources::getReference();
+ ValueTableData &fields = r.fieldList();
+ MemoryBlock *scriptData = r.scriptData();
+ byte *scripts = scriptData->data();
+ Common::Stack<uint16> stack;
+ Common::Stack<uint16> methodStack;
+ byte opcode;
+ uint16 param, v1, v2;
+ uint16 param1, param2, param3;
+ uint16 fieldNum;
+ uint32 tempVal;
+ SequenceMethodPtr ptr;
+ SequenceMethodRecord *rec;
+
+ uint16 offset = startOffset;
+ bool breakFlag = false;
+ param = 0;
+ fields.setField(SEQUENCE_RESULT, 0);
+
+ while (!breakFlag) {
+ if (offset >= scriptData->size())
+ error("Script failure in script %d - invalid offset %d", startOffset, offset);
+
+ opcode = scripts[offset++];
+ if ((opcode & 1) != 0) {
+ // Flag to read next two bytes as active parameter
+ if (offset >= scriptData->size()-2)
+ error("Script failure in script %d - invalid offset %d", startOffset, offset);
+
+ param = READ_LE_UINT16(scripts + offset);
+ offset += 2;
+ }
+ opcode >>= 1; // Discard param bit from opcode byte
+
+ switch (opcode) {
+ case S_OPCODE_ABORT:
+ case S_OPCODE_ABORT2:
+ case S_OPCODE_ABORT3:
+ methodStack.clear();
+ break;
+
+ case S_OPCODE_ADD:
+ stack.push(stack.pop() + stack.pop());
+ break;
+
+ case S_OPCODE_SUBTRACT:
+ v1 = stack.pop();
+ v2 = stack.pop();
+ stack.push(v2 - v1);
+ break;
+
+ case S_OPCODE_MULTIPLY:
+ tempVal = stack.pop() * stack.pop();
+ stack.push(tempVal & 0xffff);
+ param = (uint16) (tempVal >> 16);
+ break;
+
+ case S_OPCODE_DIVIDE:
+ v1 = stack.pop();
+ v2 = stack.pop();
+ stack.push(v2 / v1);
+ param = v2 % v1; // remainder
+ break;
+
+ case S_OPCODE_NOT_EQUALS:
+ stack.push((stack.pop() != stack.pop()) ? 0 : 1);
+ break;
+
+ case S_OPCODE_EQUALS:
+ stack.push((stack.pop() == stack.pop()) ? 0 : 1);
+ break;
+
+ case S_OPCODE_GT:
+ stack.push((stack.pop() > stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_LT:
+ stack.push((stack.pop() < stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_LT2:
+ stack.push((stack.pop() < stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_GT2:
+ stack.push((stack.pop() > stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_AND:
+ stack.push(stack.pop() & stack.pop());
+ break;
+
+ case S_OPCODE_OR:
+ stack.push(stack.pop() | stack.pop());
+ break;
+
+ case S_OPCODE_LOGICAL_AND:
+ stack.push(((stack.pop() != 0) && (stack.pop() != 0)) ? 1 : 0);
+ break;
+
+ case S_OPCODE_LOGICAL_OR:
+ stack.push(((stack.pop() != 0) || (stack.pop() != 0)) ? 1 : 0);
+ break;
+
+ case S_OPCODE_GET_FIELD:
+ // Opcode not yet fully implemented
+ fieldNum = param >> 1;
+ v1 = fields.getField(fieldNum);
+ stack.push(v1);
+ break;
+
+ case S_OPCODE_SET_FIELD:
+ // Opcode not yet fully implemented
+ fieldNum = param >> 1;
+ v1 = stack.pop();
+ fields.setField(fieldNum, v1);
+ break;
+
+ case S_OPCODE_PUSH:
+ stack.push(param);
+ break;
+
+ case S_OPCODE_SUBROUTINE:
+ methodStack.push(offset);
+ offset = param;
+ break;
+
+ case S_OPCODE_EXEC:
+ param1 = 0; param2 = 0; param3 = 0;
+ if (!stack.empty()) param1 = stack.pop();
+ if (!stack.empty()) param2 = stack.pop();
+ if (!stack.empty()) param3 = stack.pop();
+
+ rec = &scriptMethods[0];
+ while ((rec->methodIndex != 0xff) && (rec->methodIndex != param))
+ ++rec;
+
+ if (rec->methodIndex == 0xff)
+ warning("Undefined script method %d", param);
+ else {
+ ptr = rec->proc;
+ ptr(param1, param2, param3);
+ }
+ break;
+
+ case S_OPCODE_COND_JUMP:
+ v1 = stack.pop();
+ if (v1 == 0) offset += (int16) param;
+ break;
+
+ case S_OPCODE_JUMP:
+ offset += (int16) param;
+ break;
+
+ case S_OPCODE_RANDOM:
+ param = r.random() >> 8; // make number between 0 to 255
+ break;
+
+ case S_OPCODE_END:
+ // Signal to end the execution
+ if (!methodStack.empty())
+ offset = methodStack.pop();
+ else
+ breakFlag = true;
+ break;
+
+ default:
+ error("Unknown script opcode %d", opcode);
+ break;
+ }
+ }
+
+ return fields.getField(SEQUENCE_RESULT);
+}
+
+/*------------------------------------------------------------------------*/
+/*- Hotspot Script Handler -*/
+/*- -*/
+/*------------------------------------------------------------------------*/
+
+int16 HotspotScript::nextVal(MemoryBlock *data, uint16 &offset) {
+ if (offset >= data->size() - 1)
+ error("Script failure - invalid offset");
+ int16 value = READ_LE_UINT16(data->data() + offset);
+ offset += 2;
+ return value;
+}
+
+bool HotspotScript::execute(Hotspot *h)
+{
+ Resources &r = Resources::getReference();
+ MemoryBlock *scriptData = r.hotspotScriptData();
+ uint16 offset = h->script();
+ int16 opcode = 0;
+ int16 param1, param2;
+ bool breakFlag = false;
+
+ while (!breakFlag) {
+ opcode = nextVal(scriptData, offset);
+ switch (opcode) {
+ case S2_OPCODE_TIMEOUT:
+ param1 = nextVal(scriptData, offset);
+ h->setTickCtr(param1);
+ h->setScript(offset);
+ breakFlag = true;
+ break;
+
+ case S2_OPCODE_POSITION:
+ param1 = nextVal(scriptData, offset);
+ param2 = nextVal(scriptData, offset);
+ h->setPosition(param1 - 0x80, param2 - 0x80);
+ break;
+
+ case S2_OPCODE_CHANGE_POS:
+ param1 = nextVal(scriptData, offset);
+ param2 = nextVal(scriptData, offset);
+ h->setPosition(h->x() + param1, h->y() + param2);
+ break;
+
+ case S2_OPCODE_UNLOAD:
+ breakFlag = true;
+ break;
+
+ case S2_OPCODE_DIMENSIONS:
+ param1 = nextVal(scriptData, offset) << 4;
+ param2 = nextVal(scriptData, offset);
+ h->setSize((uint16) param1, (uint16) param2);
+ break;
+
+ case S2_OPCODE_JUMP:
+ offset = (uint16) nextVal(scriptData, offset);
+ break;
+
+ case S2_OPCODE_ANIMATION:
+ param1 = nextVal(scriptData, offset);
+ h->setAnimation(param1);
+ break;
+
+ case S2_OPCODE_UNKNOWN_247:
+ param1 = nextVal(scriptData, offset);
+ param2 = nextVal(scriptData, offset);
+// warning("UNKNOWN_247 stub called");
+ break;
+
+ case S2_OPCODE_UNKNOWN_258:
+ param1 = nextVal(scriptData, offset);
+// warning("UNKNOWN_258 stub called");
+ break;
+
+ case S2_OPCODE_ACTIONS:
+ param1 = nextVal(scriptData, offset) << 4;
+ param2 = nextVal(scriptData, offset);
+ h->setActions((uint32) param1 | ((uint32) param2 << 16));
+ break;
+
+ default:
+ // Set the animation frame number
+ h->setFrameNumber(opcode);
+ h->setScript(offset);
+ breakFlag = true;
+ break;
+ }
+ }
+
+ return (opcode == S2_OPCODE_UNLOAD);
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/scripts.h b/engines/lure/scripts.h
new file mode 100644
index 0000000000..b8ed59b15d
--- /dev/null
+++ b/engines/lure/scripts.h
@@ -0,0 +1,117 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_scripts_h__
+#define __lure_scripts_h__
+
+#include "lure/luredefs.h"
+#include "lure/memory.h"
+#include "lure/hotspots.h"
+
+namespace Lure {
+
+// Opcode list
+#define S_OPCODE_ABORT 0
+#define S_OPCODE_ADD 1
+#define S_OPCODE_SUBTRACT 2
+#define S_OPCODE_MULTIPLY 3
+#define S_OPCODE_DIVIDE 4
+#define S_OPCODE_NOT_EQUALS 5
+#define S_OPCODE_EQUALS 6
+#define S_OPCODE_GT 7
+#define S_OPCODE_LT 8
+#define S_OPCODE_LT2 9
+#define S_OPCODE_GT2 10
+#define S_OPCODE_AND 11
+#define S_OPCODE_OR 12
+#define S_OPCODE_LOGICAL_AND 13
+#define S_OPCODE_LOGICAL_OR 14
+#define S_OPCODE_GET_FIELD 15
+#define S_OPCODE_SET_FIELD 16
+#define S_OPCODE_PUSH 17
+#define S_OPCODE_SUBROUTINE 18
+#define S_OPCODE_EXEC 19
+#define S_OPCODE_END 20
+#define S_OPCODE_COND_JUMP 21
+#define S_OPCODE_JUMP 22
+#define S_OPCODE_ABORT2 23
+#define S_OPCODE_ABORT3 24
+#define S_OPCODE_RANDOM 25
+
+#define S2_OPCODE_TIMEOUT -1
+#define S2_OPCODE_POSITION -2
+#define S2_OPCODE_CHANGE_POS -3
+#define S2_OPCODE_UNLOAD -4
+#define S2_OPCODE_DIMENSIONS -5
+#define S2_OPCODE_JUMP -6
+#define S2_OPCODE_ANIMATION -7
+#define S2_OPCODE_UNKNOWN_247 -8
+#define S2_OPCODE_UNKNOWN_258 -9
+#define S2_OPCODE_ACTIONS -10
+
+
+
+class Script {
+public:
+ static uint16 execute(uint16 startOffset);
+
+ static void activateHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void setHotspotScript(uint16 hotspotId, uint16 scriptIndex, uint16 v3);
+ static void clearSequenceDelayList(uint16 v1, uint16 scriptIndex, uint16 v3);
+ static void method2(uint16 v1, uint16 v2, uint16 v3);
+ static void deactivateHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void setActionsOffset(uint16 hotspotId, uint16 offset, uint16 v3);
+ static void addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 v3);
+ static void characterInRoom(uint16 characterId, uint16 roomNumber, uint16 v3);
+ static void setHotspotName(uint16 hotspotId, uint16 nameId, uint16 v3);
+ static void displayDialog(uint16 stringId, uint16 v2, uint16 v3);
+ static void remoteRoomViewSetup(uint16 v1, uint16 v2, uint16 v3);
+ static void getDoorBlocked(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void decrInventoryItems(uint16 v1, uint16 v2, uint16 v3);
+ static void setFrameNumber(uint16 hotspotId, uint16 offset, uint16 v3);
+ static void disableHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void increaseNumGroats(uint16 characterId, uint16 numGroats, uint16 v3);
+ static void enableHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void room14DoorClose(uint16 v1, uint16 v2, uint16 v3);
+ static void checkDroppedDesc(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void doorClose(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void displayMessage(uint16 messageId, uint16 characterId, uint16 unknownVal);
+ static void doorOpen(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void givePlayerItem(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void decreaseNumGroats(uint16 characterId, uint16 numGroats, uint16 v3);
+ static void setVillageSkorlTickProc(uint16 v1, uint16 v2, uint16 v3);
+ static void getNumGroats(uint16 v1, uint16 v2, uint16 v3);
+ static void animationLoad(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void addActions(uint16 hotspotId, uint16 actions, uint16 v3);
+ static void checkCellDoor(uint16 v1, uint16 v2, uint16 v3);
+};
+
+class HotspotScript {
+private:
+ static int16 nextVal(MemoryBlock *data, uint16 &offset);
+public:
+ static bool execute(Hotspot *h);
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/strings.cpp b/engines/lure/strings.cpp
new file mode 100644
index 0000000000..d509e3842d
--- /dev/null
+++ b/engines/lure/strings.cpp
@@ -0,0 +1,300 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/strings.h"
+#include "lure/disk.h"
+#include "lure/room.h"
+
+namespace Lure {
+
+StringData *int_strings = NULL;
+
+StringData::StringData() {
+ int_strings = this;
+
+ for (uint8 ctr = 0; ctr < MAX_NUM_CHARS; ++ctr) _chars[ctr] = NULL;
+ _numChars = 0;
+ _names = Disk::getReference().getEntry(NAMES_RESOURCE_ID);
+ _strings[0] = Disk::getReference().getEntry(STRINGS_RESOURCE_ID);
+ _strings[1] = Disk::getReference().getEntry(STRINGS_2_RESOURCE_ID);
+ _strings[2] = Disk::getReference().getEntry(STRINGS_3_RESOURCE_ID);
+
+ // Add in the list of bit sequences, and what characters they represent
+ add("00", ' ');
+ add("0100", 'e');
+ add("0101", 'o');
+ add("0110", 't');
+ add("01110", 'a');
+ add("01111", 'n');
+ add("1000", 's');
+ add("1001", 'i');
+ add("1010", 'r');
+ add("10110", 'h');
+ add("101110", 'u');
+ add("1011110", 'l');
+ add("1011111", 'd');
+ add("11000", 'y');
+ add("110010", 'g');
+ add("110011", '\0');
+ add("110100", 'w');
+ add("110101", 'c');
+ add("110110", 'f');
+ add("1101110", '.');
+ add("1101111", 'm');
+ add("111000", 'p');
+ add("111001", 'b');
+ add("1110100", ',');
+ add("1110101", 'k');
+ add("1110110", '\'');
+ add("11101110", 'I');
+ add("11101111", 'v');
+ add("1111000", '!');
+ add("1111001", '\xb4');
+ add("11110100", 'T');
+ add("11110101", '\xb5');
+ add("11110110", '?');
+ add("111101110", '\xb2');
+ add("111101111", '\xb3');
+ add("11111000", 'W');
+ add("111110010", 'H');
+ add("111110011", 'A');
+ add("111110100", '\xb1');
+ add("111110101", 'S');
+ add("111110110", 'Y');
+ add("1111101110", 'G');
+ add("11111011110", 'M');
+ add("11111011111", 'N');
+ add("111111000", 'O');
+ add("1111110010", 'E');
+ add("1111110011", 'L');
+ add("1111110100", '-');
+ add("1111110101", 'R');
+ add("1111110110", 'B');
+ add("11111101110", 'D');
+ add("11111101111", '\xa6');
+ add("1111111000", 'C');
+ add("11111110010", 'x');
+ add("11111110011", 'j');
+ add("1111111010", '\xac');
+ add("11111110110", '\xa3');
+ add("111111101110", 'P');
+ add("111111101111", 'U');
+ add("11111111000", 'q');
+ add("11111111001", '\xad');
+ add("111111110100", 'F');
+ add("111111110101", '1');
+ add("111111110110", '\xaf');
+ add("1111111101110", ';');
+ add("1111111101111", 'z');
+ add("111111111000", '\xa5');
+ add("1111111110010", '2');
+ add("1111111110011", '\xb0');
+ add("111111111010", 'K');
+ add("1111111110110", '%');
+ add("11111111101110", '\xa2');
+ add("11111111101111", '5');
+ add("1111111111000", ':');
+ add("1111111111001", 'J');
+ add("1111111111010", 'V');
+ add("11111111110110", '6');
+ add("11111111110111", '3');
+ add("1111111111100", '\xab');
+ add("11111111111010", '\xae');
+ add("111111111110110", '0');
+ add("111111111110111", '4');
+ add("11111111111100", '7');
+ add("111111111111010", '9');
+ add("111111111111011", '"');
+ add("111111111111100", '8');
+ add("111111111111101", '\xa7');
+ add("1111111111111100", '/');
+ add("1111111111111101", 'Q');
+ add("11111111111111100", '\xa8');
+ add("11111111111111101", '(');
+ add("111111111111111100", ')');
+ add("111111111111111101", '\x99');
+ add("11111111111111111", '\xa9');
+}
+
+StringData::~StringData() {
+ int_strings = NULL;
+
+ for (uint8 ctr = 0; ctr < MAX_NUM_CHARS; ++ctr)
+ if (_chars[ctr]) delete _chars[ctr];
+ else break;
+
+ delete _names;
+ delete _strings[0];
+ delete _strings[1];
+ delete _strings[2];
+}
+
+StringData &StringData::getReference() {
+ return *int_strings;
+}
+
+void StringData::add(const char *sequence, char ascii) {
+ uint32 value = 0;
+
+ for (uint8 index = 0; index < strlen(sequence); ++index) {
+ if (sequence[index] == '1')
+ value |= (1 << index);
+ else if (sequence[index] != '0')
+ error("Invalid character in string bit-stream sequence");
+ }
+
+ if (_numChars == MAX_NUM_CHARS)
+ error("Max characters too lower in string decoder");
+ _chars[_numChars++] = new CharacterEntry(strlen(sequence), value, ascii);
+}
+
+byte StringData::readBit() {
+ byte result = ((*_srcPos & _bitMask) != 0) ? 1 : 0;
+ _bitMask >>= 1;
+ if (_bitMask == 0) {
+ _bitMask = 0x80;
+ ++_srcPos;
+ }
+
+ return result;
+}
+
+void StringData::initPosition(uint16 stringId) {
+ uint16 roomNumber = Room::getReference().roomNumber();
+ byte *stringTable;
+
+ if ((roomNumber >= 0x2A) && (stringId >= STRING_ID_RANGE) && (stringId < STRING_ID_UPPER))
+ stringId = 0x76;
+ if ((roomNumber < 0x2A) && (stringId >= STRING_ID_UPPER))
+ stringId = 0x76;
+
+ if (stringId < STRING_ID_RANGE)
+ stringTable = _strings[0]->data();
+ else if (stringId < STRING_ID_RANGE*2) {
+ stringId -= STRING_ID_RANGE;
+ stringTable = _strings[1]->data();
+ } else {
+ stringId -= STRING_ID_RANGE * 2;
+ stringTable = _strings[2]->data();
+ }
+
+ _srcPos = stringTable + 4;
+
+ uint32 total = 0;
+ int numLoops = stringId >> 5;
+ for (int ctr = 0; ctr < numLoops; ++ctr) {
+ total += READ_LE_UINT16(_srcPos);
+ _srcPos += sizeof(uint16);
+ }
+
+ numLoops = stringId & 0x1f;
+ if (numLoops!= 0) {
+ byte *tempPtr = stringTable + (stringId & 0xffe0) + READ_LE_UINT16(stringTable);
+
+ for (int ctr = 0; ctr < numLoops; ++ctr) {
+ byte v = *tempPtr++;
+ if ((v & 0x80) == 0) {
+ total += v;
+ } else {
+ total += (v & 0x7f) << 3;
+ }
+ }
+ }
+
+ _bitMask = 0x80;
+
+ if ((total & 3) != 0)
+ _bitMask >>= (total & 3) * 2;
+
+ _srcPos = stringTable + (total >> 2) + READ_LE_UINT16(stringTable + 2);
+
+ // Final positioning to start of string
+ for (;;) {
+ if (readBit() == 0) break;
+ _srcPos += 2;
+ }
+ readBit();
+}
+
+// readCharatcer
+// Reads the next character from the input bit stream
+
+char StringData::readCharacter() {
+ uint32 searchValue = 0;
+
+ // Loop through an increasing number of bits
+
+ for (uint8 numBits = 1; numBits <= 18; ++numBits) {
+ searchValue |= readBit() << (numBits - 1);
+
+ // Scan through list for a match
+ for (int index = 0; _chars[index] != NULL; ++index) {
+ if ((_chars[index]->_numBits == numBits) &&
+ (_chars[index]->_sequence == searchValue))
+ return _chars[index]->_ascii;
+ }
+ }
+
+ error("Unknown bit sequence encountered when decoding string");
+}
+
+void StringData::getString(uint16 stringId, char *dest, const char *hotspotName,
+ const char *actionName) {
+ char ch;
+ char *destPos = dest;
+ initPosition(stringId);
+
+ ch = readCharacter();
+ while (ch != '\0') {
+ if (ch == '%') {
+ // Copy over hotspot or action
+ ch = readCharacter();
+ const char *p = (ch == '1') ? hotspotName : actionName;
+ strcpy(destPos, p);
+ destPos += strlen(p);
+ } else if ((uint8) ch >= 0xa0) {
+ const char *p = getName((uint8) ch - 0xa0);
+ strcpy(destPos, p);
+ destPos += strlen(p);
+ } else {
+ *destPos++ = ch;
+ }
+
+ ch = readCharacter();
+ }
+
+ *destPos = '\0';
+}
+
+// getName
+// Returns the name or fragment of word at the specified index in the names resource
+
+char *StringData::getName(uint8 nameIndex) {
+ uint16 numNames = *((uint16 *) _names->data()) / 2;
+ if (nameIndex >= numNames)
+ error("Invalid name index was passed to getCharacterName");
+
+ uint16 nameStart = *((uint16 *) (_names->data() + (nameIndex * 2)));
+ return (char *) (_names->data() + nameStart);
+}
+
+} // namespace Lure
diff --git a/engines/lure/strings.h b/engines/lure/strings.h
new file mode 100644
index 0000000000..03c1da2440
--- /dev/null
+++ b/engines/lure/strings.h
@@ -0,0 +1,67 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_strings_h__
+#define __lure_strings_h__
+
+#include "lure/luredefs.h"
+#include "lure/memory.h"
+
+namespace Lure {
+
+class CharacterEntry {
+public:
+ uint8 _numBits;
+ uint32 _sequence;
+ char _ascii;
+
+ CharacterEntry(uint8 numBits, uint32 sequence, char ascii): _numBits(numBits),
+ _sequence(sequence), _ascii(ascii) {};
+};
+
+#define MAX_NUM_CHARS 218
+
+class StringData {
+private:
+ MemoryBlock *_strings[3];
+ MemoryBlock *_names;
+ CharacterEntry *_chars[MAX_NUM_CHARS];
+ uint8 _numChars;
+ byte *_srcPos;
+ byte _bitMask;
+
+ void add(const char *sequence, char ascii);
+ void initPosition(uint16 stringId);
+ char readCharacter();
+ byte readBit();
+public:
+ StringData();
+ ~StringData();
+ static StringData &getReference();
+
+ void getString(uint16 stringId, char *dest, const char *hotspotName, const char *actionName);
+ char *getName(uint8 nameIndex);
+};
+
+} // namespace Lure
+
+#endif
diff --git a/engines/lure/surface.cpp b/engines/lure/surface.cpp
new file mode 100644
index 0000000000..e254cfe501
--- /dev/null
+++ b/engines/lure/surface.cpp
@@ -0,0 +1,456 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/surface.h"
+#include "lure/decode.h"
+#include "lure/system.h"
+#include "lure/events.h"
+#include "lure/screen.h"
+#include "lure/room.h"
+#include "lure/strings.h"
+
+namespace Lure {
+
+// These variables hold resources commonly used by the Surfaces, and must be initialised and freed
+// by the static Surface methods initialise and deinitailse
+
+static MemoryBlock *int_font = NULL;
+static MemoryBlock *int_dialog_frame = NULL;
+static uint8 fontSize[NUM_CHARS_IN_FONT];
+
+void Surface::initialise() {
+ int_font = Disk::getReference().getEntry(FONT_RESOURCE_ID);
+ int_dialog_frame = Disk::getReference().getEntry(DIALOG_RESOURCE_ID);
+
+ // Calculate the size of each font character
+ for (int ctr = 0; ctr < NUM_CHARS_IN_FONT; ++ctr) {
+ byte *pChar = int_font->data() + (ctr * 8);
+ fontSize[ctr] = 0;
+
+ for (int yp = 0; yp < FONT_HEIGHT; ++yp)
+ {
+ byte v = *pChar++;
+
+ for (int xp = 0; xp < FONT_WIDTH; ++xp) {
+ if ((v & 0x80) && (xp > fontSize[ctr]))
+ fontSize[ctr] = xp;
+ v = (v << 1) & 0xff;
+ }
+ }
+
+ // If character is empty, like for a space, give a default size
+ if (fontSize[ctr] == 0) fontSize[ctr] = 2;
+ }
+}
+
+void Surface::deinitialise() {
+ delete int_font;
+ delete int_dialog_frame;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Surface::Surface(MemoryBlock *src, uint16 wdth, uint16 hght): _data(src),
+ _width(wdth), _height(hght) {
+ if ((uint32) (wdth * hght) != src->size())
+ error("Surface dimensions do not match size of passed data");
+}
+
+Surface::Surface(uint16 wdth, uint16 hght): _data(Memory::allocate(wdth*hght)),
+ _width(wdth), _height(hght) {
+}
+
+Surface::~Surface() {
+ delete _data;
+}
+
+void Surface::loadScreen(uint16 resourceId) {
+ MemoryBlock *rawData = Disk::getReference().getEntry(resourceId);
+ PictureDecoder decoder;
+ MemoryBlock *tmpScreen = decoder.decode(rawData, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH);
+ delete rawData;
+ empty();
+ copyFrom(tmpScreen, MENUBAR_Y_SIZE * FULL_SCREEN_WIDTH);
+
+ delete tmpScreen;
+}
+
+void Surface::writeChar(uint16 x, uint16 y, uint8 ascii, bool transparent, uint8 colour) {
+ byte *const addr = _data->data() + (y * _width) + x;
+
+ if ((ascii < 32) || (ascii >= 32 + NUM_CHARS_IN_FONT))
+ error("Invalid ascii character passed for display '%d'", ascii);
+
+ uint8 v;
+ byte *pFont = int_font->data() + ((ascii - 32) * 8);
+ byte *pDest;
+ uint8 charWidth = 0;
+
+ for (int y1 = 0; y1 < 8; ++y1) {
+ v = *pFont++;
+ pDest = addr + (y1 * _width);
+
+ for (int x1 = 0; x1 < 8; ++x1, ++pDest) {
+ if (v & 0x80) {
+ *pDest = colour;
+ if (x1+1 > charWidth) charWidth = x1 + 1;
+ }
+ else if (!transparent) *pDest = 0;
+ v = (v << 1) & 0xff;
+ }
+ }
+}
+
+void Surface::writeString(uint16 x, uint16 y, Common::String line, bool transparent,
+ uint8 colour, bool varLength) {
+ const char *sPtr = line.c_str();
+
+ while (*sPtr) {
+ writeChar(x, y, (uint8) *sPtr, transparent, colour);
+
+ // Move to after the character in preparation for the next character
+ if (!varLength) x += FONT_WIDTH;
+ else x += fontSize[*sPtr - ' '] + 2;
+
+ ++sPtr; // Move to next character
+ }
+}
+
+void Surface::transparentCopyTo(Surface *dest) {
+ if (dest->width() != _width)
+ error("Incompatible surface sizes for transparent copy");
+
+ byte *pSrc = _data->data();
+ byte *pDest = dest->data().data();
+ uint16 numBytes = MIN(_height,dest->height()) * FULL_SCREEN_WIDTH;
+
+ while (numBytes-- > 0) {
+ if (*pSrc) *pDest = *pSrc;
+
+ ++pSrc;
+ ++pDest;
+ }
+}
+
+void Surface::copyTo(Surface *dest)
+{
+ copyTo(dest, 0, 0);
+}
+
+void Surface::copyTo(Surface *dest, uint16 x, uint16 y)
+{
+ if ((x == 0) && (dest->width() == _width)) {
+ // Use fast data transfer
+ uint32 dataSize = dest->data().size() - (y * _width);
+ if (dataSize > _data->size()) dataSize = _data->size();
+ dest->data().copyFrom(_data, 0, y * _width, dataSize);
+ } else {
+ // Use slower transfer
+ Rect rect;
+ rect.left = 0; rect.top = 0;
+ rect.right = _width-1; rect.bottom = _height-1;
+ copyTo(dest, rect, x, y);
+ }
+}
+
+void Surface::copyTo(Surface *dest, const Rect &srcBounds,
+ uint16 destX, uint16 destY, int transparentColour) {
+ for (uint16 y=0; y<=(srcBounds.bottom-srcBounds.top); ++y) {
+ const uint32 srcPos = (srcBounds.top + y) * _width + srcBounds.left;
+ const uint32 destPos = (destY+y) * dest->width() + destX;
+
+ uint16 numBytes = srcBounds.right-srcBounds.left+1;
+ if (transparentColour == -1) {
+ // No trnnsparent colour, so copy all the bytes of the line
+ dest->data().copyFrom(_data, srcPos, destPos, numBytes);
+ } else {
+ byte *pSrc = _data->data() + srcPos;
+ byte *pDest = dest->data().data() + destPos;
+
+ while (numBytes-- > 0) {
+ if (*pSrc != (uint8) transparentColour)
+ *pDest = *pSrc;
+ ++pSrc;
+ ++pDest;
+ }
+ }
+ }
+}
+
+void Surface::copyFrom(MemoryBlock *src, uint32 destOffset) {
+ uint32 size = _data->size() - destOffset;
+ if (src->size() > size) size = src->size();
+ _data->copyFrom(src, 0, destOffset, size);
+}
+
+// fillRect
+// Fills a rectangular area with a colour
+
+void Surface::fillRect(const Rect &r, uint8 colour) {
+ for (int yp = r.top; yp <= r.bottom; ++yp) {
+ byte *const addr = _data->data() + (yp * _width) + r.left;
+ memset(addr, colour, r.width());
+ }
+}
+
+// createDialog
+// Forms a dialog encompassing the entire surface
+
+void copyLine(byte *pSrc, byte *pDest, uint16 leftSide, uint16 center, uint16 rightSide) {
+ // Left area
+ memcpy(pDest, pSrc, leftSide);
+ pSrc += leftSide; pDest += leftSide;
+ // Center area
+ memset(pDest, *pSrc, center);
+ ++pSrc; pDest += center;
+ // Right side
+ memcpy(pDest, pSrc, rightSide);
+ pSrc += rightSide; pDest += rightSide;
+}
+
+void Surface::createDialog(bool blackFlag) {
+ if ((_width < 20) || (_height < 20)) return;
+
+ byte *pSrc = int_dialog_frame->data();
+ byte *pDest = _data->data();
+ uint16 xCenter = _width - DIALOG_EDGE_SIZE * 2;
+ uint16 yCenter = _height - DIALOG_EDGE_SIZE * 2;
+
+ // Dialog top
+ for (int y = 0; y < 9; ++y) {
+ copyLine(pSrc, pDest, DIALOG_EDGE_SIZE - 2, xCenter + 2, DIALOG_EDGE_SIZE);
+ pSrc += (DIALOG_EDGE_SIZE - 2) + 1 + DIALOG_EDGE_SIZE;
+ pDest += _width;
+ }
+
+ // Dialog sides - note that the same source data gets used for all side lines
+ for (int y = 0; y < yCenter; ++y) {
+ copyLine(pSrc, pDest, DIALOG_EDGE_SIZE, xCenter, DIALOG_EDGE_SIZE);
+ pDest += _width;
+ }
+ pSrc += DIALOG_EDGE_SIZE * 2 + 1;
+
+ // Dialog bottom
+ for (int y = 0; y < 9; ++y) {
+ copyLine(pSrc, pDest, DIALOG_EDGE_SIZE, xCenter + 1, DIALOG_EDGE_SIZE - 1);
+ pSrc += DIALOG_EDGE_SIZE + 1 + (DIALOG_EDGE_SIZE - 1);
+ pDest += _width;
+ }
+
+ // Final processing - if black flag set, clear dialog inside area
+ if (blackFlag) {
+ Rect r = Rect(DIALOG_EDGE_SIZE, DIALOG_EDGE_SIZE,
+ _width - DIALOG_EDGE_SIZE, _height-DIALOG_EDGE_SIZE);
+ fillRect(r, 0);
+ }
+}
+
+void Surface::copyToScreen(uint16 x, uint16 y) {
+ OSystem &system = System::getReference();
+ system.copyRectToScreen(_data->data(), _width, x, y, _width, _height);
+ system.updateScreen();
+}
+
+void Surface::centerOnScreen() {
+ OSystem &system = System::getReference();
+
+ system.copyRectToScreen(_data->data(), _width,
+ (FULL_SCREEN_WIDTH - _width) / 2, (FULL_SCREEN_HEIGHT - _height) / 2,
+ _width, _height);
+ system.updateScreen();
+}
+
+uint16 Surface::textWidth(const char *s, int numChars) {
+ uint16 result = 0;
+ if (numChars == 0) numChars = strlen(s);
+
+ while (numChars-- > 0) result += fontSize[*s++ - ' '] + 2;
+ return result;
+}
+
+Surface *Surface::newDialog(uint16 width, uint8 numLines, char **lines, bool varLength, uint8 colour) {
+ Surface *s = new Surface(width, (DIALOG_EDGE_SIZE + 3) * 2 +
+ numLines * (FONT_HEIGHT - 1));
+ s->createDialog();
+
+ for (uint8 ctr = 0; ctr < numLines; ++ctr)
+ s->writeString(DIALOG_EDGE_SIZE + 3, DIALOG_EDGE_SIZE + 3 +
+ (ctr * (FONT_HEIGHT - 1)), lines[ctr], true, colour, varLength);
+ return s;
+}
+
+Surface *Surface::newDialog(uint16 width, const char *line, uint8 colour) {
+ uint8 numLines = 1;
+ uint16 lineWidth = 0;
+ char *s, *lineCopy;
+ bool newLine;
+
+ s = lineCopy = strdup(line);
+
+ // Scan through the text and insert NULLs to break the line into allowable widths
+
+ while (*s != '\0') {
+ char *wordStart = s;
+ while (*wordStart == ' ') ++wordStart;
+ char *wordEnd = strchr(wordStart, ' ');
+ char *wordEnd2 = strchr(wordStart, '\n');
+ if ((!wordEnd) || ((wordEnd2) && (wordEnd2 < wordEnd))) {
+ wordEnd = wordEnd2;
+ newLine = (wordEnd2 != NULL);
+ } else {
+ newLine = false;
+ }
+
+ if (wordEnd) --wordEnd; // move back one to end of word
+ else wordEnd = strchr(s, '\0') - 1;
+
+ uint16 wordSize = textWidth(s, (int) (wordEnd - s + 1));
+
+ if (lineWidth + wordSize > width - (DIALOG_EDGE_SIZE + 3) * 2) {
+ // Break word onto next line
+ *(wordStart - 1) = '\0';
+ ++numLines;
+ lineWidth = textWidth(wordStart, (int) (wordEnd - wordStart + 1));
+ } else if (newLine) {
+ // Break on newline
+ ++numLines;
+ ++wordEnd;
+ *wordEnd = '\0';
+ lineWidth = 0;
+ } else {
+ // Add word's length to total for line
+ lineWidth += wordSize;
+ }
+
+ s = wordEnd+1;
+ }
+
+ // Set up a list for the start of each line
+ char **lines = (char **) Memory::alloc(sizeof(char *) * numLines);
+ lines[0] = lineCopy;
+ for (int ctr = 1; ctr < numLines; ++ctr)
+ lines[ctr] = strchr(lines[ctr-1], 0) + 1;
+
+ // Create the dialog
+ Surface *result = newDialog(width, numLines, lines, true, colour);
+
+ // Deallocate used resources
+ free(lines);
+ free(lineCopy);
+
+ return result;
+}
+
+Surface *Surface::getScreen(uint16 resourceId) {
+ MemoryBlock *block = Disk::getReference().getEntry(resourceId);
+ PictureDecoder d;
+ MemoryBlock *decodedData = d.decode(block);
+ delete block;
+ return new Surface(decodedData, FULL_SCREEN_WIDTH, decodedData->size() / FULL_SCREEN_WIDTH);
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Dialog::show(const char *text) {
+ Screen &screen = Screen::getReference();
+ Mouse &mouse = Mouse::getReference();
+ mouse.cursorOff();
+
+ Surface *s = Surface::newDialog(INFO_DIALOG_WIDTH, text);
+ s->copyToScreen(INFO_DIALOG_X, INFO_DIALOG_Y);
+
+ // Wait for a keypress or mouse button
+ Events::getReference().waitForPress();
+
+ screen.update();
+ mouse.cursorOn();
+}
+
+void Dialog::show(uint16 stringId) {
+ char buffer[MAX_DESC_SIZE];
+ Room &r = Room::getReference();
+ StringData &sl = StringData::getReference();
+
+ Action action = r.getCurrentAction();
+
+ const char *actionName = (action == NONE) ? NULL : actionList[action];
+ char hotspotName[MAX_HOTSPOT_NAME_SIZE];
+ if (r.hotspotId() == 0)
+ strcpy(hotspotName, "");
+ else
+ sl.getString(r.hotspot().nameId, hotspotName, NULL, NULL);
+
+ sl.getString(stringId, buffer, hotspotName, actionName);
+ show(buffer);
+}
+
+void Dialog::showMessage(uint16 messageId, uint16 characterId) {
+ MemoryBlock *data = Resources::getReference().messagesData();
+ uint16 *v = (uint16 *) data->data();
+ uint16 v2, idVal;
+ messageId &= 0x7fff;
+
+ // Skip through header to find table for given character
+ while (READ_LE_UINT16(v) != characterId) v += 2;
+
+ // Scan through secondary list
+ ++v;
+ v = (uint16 *) (data->data() + READ_LE_UINT16(v));
+ v2 = 0;
+ while ((idVal = READ_LE_UINT16(v)) != 0xffff) {
+ ++v;
+ if (READ_LE_UINT16(v) == messageId) break;
+ ++v;
+ }
+ // default response if a specific response not found
+ if (idVal == 0xffff) idVal = 0x8c4;
+
+ if (idVal == 0x76) {
+ /*
+ call sub_154 ; (64E7)
+ mov ax,word ptr ds:[5813h] ; (273F:5813=1BA3h)
+ mov [bx+ANIM_SEGMENT],ax
+ mov ax,word ptr ds:[5817h] ; (273F:5817=0ED8Eh)
+ mov [bx+ANIM_FRAME],ax
+ retn
+*/
+ } else if (idVal == 0x120) {
+ /*
+ call sub_154 ; (64E7)
+ mov ax,word ptr ds:[5813h] ; (273F:5813=1BA3h)
+ mov [bx+ANIM_SEGMENT],ax
+ mov ax,word ptr ds:[5817h] ; (273F:5817=0ED8Eh)
+ shl ax,1
+ mov [bx+ANIM_FRAME],ax
+*/
+ } else if (idVal >= 0x8000) {
+ // Handle string display
+ idVal &= 0x7fff;
+ Dialog::show(idVal);
+
+ } else if (idVal != 0) {
+ /* still to be decoded */
+
+ }
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/surface.h b/engines/lure/surface.h
new file mode 100644
index 0000000000..9febf542f5
--- /dev/null
+++ b/engines/lure/surface.h
@@ -0,0 +1,83 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_surface_h__
+#define __lure_surface_h__
+
+#include "common/stdafx.h"
+#include "common/str.h"
+#include "lure/disk.h"
+#include "lure/luredefs.h"
+
+using namespace Common;
+
+namespace Lure {
+
+class Surface {
+private:
+ MemoryBlock *_data;
+ uint16 _width, _height;
+public:
+ Surface(MemoryBlock *src, uint16 width, uint16 height);
+ Surface(uint16 width, uint16 height);
+ ~Surface();
+
+ static void initialise();
+ static void deinitialise();
+
+ uint16 width() { return _width; }
+ uint16 height() { return _height; }
+ MemoryBlock &data() { return *_data; }
+
+ void loadScreen(uint16 resourceId);
+ void writeChar(uint16 x, uint16 y, uint8 ascii, bool transparent, uint8 colour);
+ void writeString(uint16 x, uint16 y, Common::String line, bool transparent,
+ uint8 colour = DIALOG_TEXT_COLOUR, bool varLength = true);
+ void transparentCopyTo(Surface *dest);
+ void copyTo(Surface *dest);
+ void copyTo(Surface *dest, uint16 x, uint16 y);
+ void copyTo(Surface *dest, const Rect &srcBounds, uint16 destX, uint16 destY,
+ int transparentColour = -1);
+ void copyFrom(MemoryBlock *src) { _data->copyFrom(src); }
+ void copyFrom(MemoryBlock *src, uint32 destOffset);
+ void empty() { _data->empty(); }
+ void fillRect(const Rect &r, uint8 colour);
+ void createDialog(bool blackFlag = false);
+ void copyToScreen(uint16 x, uint16 y);
+ void centerOnScreen();
+
+ static uint16 textWidth(const char *s, int numChars = 0);
+ static Surface *newDialog(uint16 width, uint8 numLines, char **lines, bool varLength = true, uint8 colour = DIALOG_TEXT_COLOUR);
+ static Surface *newDialog(uint16 width, const char *lines, uint8 colour = DIALOG_TEXT_COLOUR);
+ static Surface *getScreen(uint16 resourceId);
+};
+
+class Dialog {
+public:
+ static void show(const char *text);
+ static void show(uint16 stringId);
+ static void showMessage(uint16 messageId, uint16 characterId);
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/engines/lure/system.cpp b/engines/lure/system.cpp
new file mode 100644
index 0000000000..6b305edc57
--- /dev/null
+++ b/engines/lure/system.cpp
@@ -0,0 +1,41 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/system.h"
+
+namespace Lure {
+
+OSystem *int_system = NULL;
+
+System::System(OSystem *sys) {
+ int_system = sys;
+}
+
+System::~System() {
+ int_system = NULL;
+}
+
+OSystem &System::getReference() {
+ return *int_system;
+}
+
+} // end of namespace Lure
diff --git a/engines/lure/system.h b/engines/lure/system.h
new file mode 100644
index 0000000000..8d7471a296
--- /dev/null
+++ b/engines/lure/system.h
@@ -0,0 +1,40 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_system_h__
+#define __lure_system_h__
+
+#include "common/stdafx.h"
+#include "common/system.h"
+
+namespace Lure {
+
+class System {
+public:
+ System(OSystem *sys);
+ ~System();
+ static OSystem &getReference();
+};
+
+} // end of namspace Lure
+
+#endif
diff --git a/engines/queen/bankman.cpp b/engines/queen/bankman.cpp
new file mode 100644
index 0000000000..18ad276d0f
--- /dev/null
+++ b/engines/queen/bankman.cpp
@@ -0,0 +1,145 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/bankman.h"
+
+#include "queen/resource.h"
+
+namespace Queen {
+
+BankManager::BankManager(Resource *res)
+ : _res(res) {
+ memset(_frames, 0, sizeof(_frames));
+ memset(_banks, 0, sizeof(_banks));
+ memset(_loadedBanks, 0, sizeof(_loadedBanks));
+}
+
+BankManager::~BankManager() {
+ for (uint32 i = 0; i < MAX_BANKS_NUMBER; ++i) {
+ close(i);
+ }
+ eraseFrames(true);
+}
+
+void BankManager::load(const char *bankname, uint32 bankslot) {
+ debug(9, "BankManager::load(%s, %d)", bankname, bankslot);
+ assert(bankslot < MAX_BANKS_NUMBER);
+
+ if (!scumm_stricmp(bankname, _loadedBanks[bankslot])) {
+ debug(9, "BankManager::load() bank '%s' already loaded", bankname);
+ return;
+ }
+
+ close(bankslot);
+ _banks[bankslot].data = _res->loadFile(bankname);
+
+ uint16 entries = READ_LE_UINT16(_banks[bankslot].data);
+ assert(entries < MAX_BANK_SIZE);
+ debug(9, "BankManager::load() entries = %d", entries);
+
+ uint32 offset = 2;
+ const uint8 *p = _banks[bankslot].data;
+ for (uint16 i = 1; i <= entries; ++i) {
+ _banks[bankslot].indexes[i] = offset;
+ uint16 w = READ_LE_UINT16(p + offset + 0);
+ uint16 h = READ_LE_UINT16(p + offset + 2);
+ // jump to next entry, skipping data & header
+ offset += w * h + 8;
+ }
+
+ // mark this bank as loaded
+ strcpy(_loadedBanks[bankslot], bankname);
+}
+
+void BankManager::unpack(uint32 srcframe, uint32 dstframe, uint32 bankslot) {
+ debug(9, "BankManager::unpack(%d, %d, %d)", srcframe, dstframe, bankslot);
+ assert(bankslot < MAX_BANKS_NUMBER);
+ assert(_banks[bankslot].data != NULL);
+
+ BobFrame *pbf = &_frames[dstframe];
+ const uint8 *p = _banks[bankslot].data + _banks[bankslot].indexes[srcframe];
+ pbf->width = READ_LE_UINT16(p + 0);
+ pbf->height = READ_LE_UINT16(p + 2);
+ pbf->xhotspot = READ_LE_UINT16(p + 4);
+ pbf->yhotspot = READ_LE_UINT16(p + 6);
+
+ uint32 size = pbf->width * pbf->height;
+ delete[] pbf->data;
+ pbf->data = new uint8[ size ];
+ memcpy(pbf->data, p + 8, size);
+}
+
+void BankManager::overpack(uint32 srcframe, uint32 dstframe, uint32 bankslot) {
+ debug(9, "BankManager::overpack(%d, %d, %d)", srcframe, dstframe, bankslot);
+ assert(bankslot < MAX_BANKS_NUMBER);
+ assert(_banks[bankslot].data != NULL);
+
+ const uint8 *p = _banks[bankslot].data + _banks[bankslot].indexes[srcframe];
+ uint16 src_w = READ_LE_UINT16(p + 0);
+ uint16 src_h = READ_LE_UINT16(p + 2);
+
+ // unpack if destination frame is smaller than source one
+ if (_frames[dstframe].width < src_w || _frames[dstframe].height < src_h) {
+ unpack(srcframe, dstframe, bankslot);
+ } else {
+ // copy data 'over' destination frame (without changing frame header)
+ memcpy(_frames[dstframe].data, p + 8, src_w * src_h);
+ }
+}
+
+void BankManager::close(uint32 bankslot) {
+ debug(9, "BankManager::close(%d)", bankslot);
+ assert(bankslot < MAX_BANKS_NUMBER);
+ delete[] _banks[bankslot].data;
+ memset(&_banks[bankslot], 0, sizeof(PackedBank));
+ _loadedBanks[bankslot][0] = '\0';
+}
+
+BobFrame *BankManager::fetchFrame(uint32 index) {
+ debug(9, "BankManager::fetchFrame(%d)", index);
+ assert(index < MAX_FRAMES_NUMBER);
+ BobFrame *pbf = &_frames[index];
+ assert(pbf->data != 0);
+ return pbf;
+}
+
+void BankManager::eraseFrame(uint32 index) {
+ debug(9, "BankManager::eraseFrame(%d)", index);
+ assert(index < MAX_FRAMES_NUMBER);
+ BobFrame *pbf = &_frames[index];
+ delete[] pbf->data;
+ memset(pbf, 0, sizeof(BobFrame));
+}
+
+void BankManager::eraseFrames(bool joe) {
+ uint32 i = 0;
+ if (!joe) {
+ i = FRAMES_JOE;
+ }
+ while (i < MAX_FRAMES_NUMBER) {
+ eraseFrame(i);
+ ++i;
+ }
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/bankman.h b/engines/queen/bankman.h
new file mode 100644
index 0000000000..63eb9224ff
--- /dev/null
+++ b/engines/queen/bankman.h
@@ -0,0 +1,87 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENBANKMAN_H
+#define QUEENBANKMAN_H
+
+#include "common/util.h"
+#include "queen/structs.h"
+
+namespace Queen {
+
+class Resource;
+
+class BankManager {
+public:
+
+ BankManager(Resource *res);
+ ~BankManager();
+
+ //! load a bank into the specified slot
+ void load(const char *bankname, uint32 bankslot);
+
+ //! unpack a frame from a loaded bank
+ void unpack(uint32 srcframe, uint32 dstframe, uint32 bankslot);
+
+ //! unpack a frame over an existing one from a loaded bank
+ void overpack(uint32 srcframe, uint32 dstframe, uint32 bankslot);
+
+ //! close a bank
+ void close(uint32 bankslot);
+
+ //! get a reference to unpacked frame
+ BobFrame *fetchFrame(uint32 index);
+
+ //! erase a frame
+ void eraseFrame(uint32 index);
+
+ //! erase all unpacked frames
+ void eraseFrames(bool joe);
+
+ enum {
+ MAX_BANK_SIZE = 110,
+ MAX_FRAMES_NUMBER = 256,
+ MAX_BANKS_NUMBER = 18
+ };
+
+private:
+
+ struct PackedBank {
+ uint32 indexes[MAX_BANK_SIZE];
+ uint8 *data;
+ };
+
+ //! unpacked bob frames
+ BobFrame _frames[MAX_FRAMES_NUMBER];
+
+ //! banked bob frames
+ PackedBank _banks[MAX_BANKS_NUMBER];
+
+ //! loaded banks names
+ char _loadedBanks[MAX_BANKS_NUMBER][20];
+
+ Resource *_res;
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/command.cpp b/engines/queen/command.cpp
new file mode 100644
index 0000000000..a84d859fe6
--- /dev/null
+++ b/engines/queen/command.cpp
@@ -0,0 +1,1255 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/command.h"
+
+#include "queen/display.h"
+#include "queen/input.h"
+#include "queen/graphics.h"
+#include "queen/grid.h"
+#include "queen/logic.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+#include "queen/state.h"
+#include "queen/walk.h"
+
+namespace Queen {
+
+CmdText::CmdText(bool reversed, uint8 y, QueenEngine *vm)
+ : _isReversed(reversed), _y(y), _vm(vm) {
+ clear();
+}
+
+void CmdText::clear() {
+ memset(_command, 0, sizeof(_command));
+}
+
+void CmdText::display(uint8 color) {
+ _vm->display()->textCurrentColor(color);
+ _vm->display()->setTextCentered(_y, _command, false);
+}
+
+void CmdText::displayTemp(uint8 color, Verb v, const char *name, bool outlined) {
+ char temp[MAX_COMMAND_LEN] = "";
+ if (_isReversed) {
+ if (name != NULL)
+ sprintf(temp, "%s ", name);
+ strcat(temp, _vm->logic()->verbName(v));
+ } else {
+ strcpy(temp, _vm->logic()->verbName(v));
+ if (name != NULL) {
+ strcat(temp, " ");
+ strcat(temp, name);
+ }
+ }
+ _vm->display()->textCurrentColor(color);
+ _vm->display()->setTextCentered(_y, temp, outlined);
+}
+
+void CmdText::displayTemp(uint8 color, const char *name, bool outlined) {
+ char temp[MAX_COMMAND_LEN];
+ if (_isReversed)
+ sprintf(temp, "%s %s", name, _command);
+ else
+ sprintf(temp, "%s %s", _command, name);
+ _vm->display()->textCurrentColor(color);
+ _vm->display()->setTextCentered(_y, temp, outlined);
+}
+
+void CmdText::setVerb(Verb v) {
+ strcpy(_command, _vm->logic()->verbName(v));
+}
+
+void CmdText::addLinkWord(Verb v) {
+ if (_isReversed) {
+ char temp[MAX_COMMAND_LEN];
+
+ strcpy(temp, _command);
+ strcpy(_command, _vm->logic()->verbName(v));
+ strcat(_command, " ");
+ strcat(_command, temp);
+ } else {
+ strcat(_command, " ");
+ strcat(_command, _vm->logic()->verbName(v));
+ }
+}
+
+void CmdText::addObject(const char *objName) {
+ if (_isReversed) {
+ char temp[MAX_COMMAND_LEN];
+
+ strcpy(temp, _command);
+ strcpy(_command, objName);
+ strcat(_command, " ");
+ strcat(_command, temp);
+ } else {
+ strcat(_command, " ");
+ strcat(_command, objName);
+ }
+}
+
+bool CmdText::isEmpty() const {
+ return _command[0] == 0;
+}
+
+void CmdState::init() {
+ commandLevel = 1;
+ oldVerb = verb = action = VERB_NONE;
+ oldNoun = noun = subject[0] = subject[1] = 0;
+
+ selAction = VERB_NONE;
+ selNoun = 0;
+}
+
+Command::Command(QueenEngine *vm)
+ : _cmdList(NULL), _cmdArea(NULL), _cmdObject(NULL), _cmdInventory(NULL), _cmdGameState(NULL),
+ _cmdText((vm->resource()->getLanguage() == HEBREW), CmdText::COMMAND_Y_POS, vm), _vm(vm) {
+}
+
+Command::~Command() {
+ delete[] _cmdList;
+ delete[] _cmdArea;
+ delete[] _cmdObject;
+ delete[] _cmdInventory;
+ delete[] _cmdGameState;
+}
+
+void Command::clear(bool clearTexts) {
+ debug(6, "Command::clear(%d)", clearTexts);
+ _cmdText.clear();
+ if (clearTexts) {
+ _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
+ }
+ _parse = false;
+ _state.init();
+}
+
+void Command::executeCurrentAction() {
+ _vm->logic()->entryObj(0);
+
+ if (_mouseKey == Input::MOUSE_RBUTTON && _state.subject[0] > 0) {
+
+ ObjectData *od = _vm->logic()->objectData(_state.subject[0]);
+ if (od == NULL || od->name <= 0) {
+ cleanupCurrentAction();
+ return;
+ }
+
+ _state.verb = State::findDefaultVerb(od->state);
+ _state.selAction = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb;
+ _cmdText.setVerb(_state.selAction);
+ _cmdText.addObject(_vm->logic()->objectName(od->name));
+ }
+
+ // always highlight the current command when actioned
+ _cmdText.display(INK_CMD_SELECT);
+
+ _state.selNoun = _state.noun;
+ _state.commandLevel = 1;
+
+ if (handleWrongAction()) {
+ cleanupCurrentAction();
+ return;
+ }
+
+ // get the commands associated with this object/item
+ uint16 comMax = 0;
+ uint16 matchingCmds[MAX_MATCHING_CMDS];
+ CmdListData *cmdList = &_cmdList[1];
+ uint16 i;
+ for (i = 1; i <= _numCmdList; ++i, ++cmdList) {
+ if (cmdList->match(_state.selAction, _state.subject[0], _state.subject[1])) {
+ assert(comMax < MAX_MATCHING_CMDS);
+ matchingCmds[comMax] = i;
+ ++comMax;
+ }
+ }
+
+ debug(6, "Command::executeCurrentAction() - comMax=%d subj1=%X subj2=%X", comMax, _state.subject[0], _state.subject[1]);
+
+ if (comMax == 0) {
+ sayInvalidAction(_state.selAction, _state.subject[0], _state.subject[1]);
+ clear(true);
+ cleanupCurrentAction();
+ return;
+ }
+
+ // process each associated command for the Object, until all done
+ // or one of the Gamestate tests fails...
+ int16 cond = 0;
+ CmdListData *com = &_cmdList[0];
+ uint16 comId = 0;
+ for (i = 1; i <= comMax; ++i) {
+
+ comId = matchingCmds[i - 1];
+ com = &_cmdList[comId];
+
+ // check the Gamestates and set them if necessary
+ cond = 0;
+ if (com->setConditions) {
+ cond = setConditions(comId, (i == comMax));
+ }
+
+ if (cond == -1 && i == comMax) {
+ // only exit on a condition fail if at last command
+ // Joe hasnt spoken, so do normal LOOK command
+ break;
+ } else if (cond == -2 && i == comMax) {
+ // only exit on a condition fail if at last command
+ // Joe has spoken, so skip LOOK command
+ cleanupCurrentAction();
+ return;
+ } else if (cond >= 0) {
+ // we've had a successful Gamestate check, so we must now exit
+ cond = executeCommand(comId, cond);
+ break;
+ }
+ }
+
+ if (_state.selAction == VERB_USE_JOURNAL) {
+ clear(true);
+ } else {
+ if (cond <= 0 && _state.selAction == VERB_LOOK_AT) {
+ lookAtSelectedObject();
+ } else {
+ // only play song if it's a PLAY AFTER type
+ if (com->song < 0) {
+ _vm->sound()->playSong(-com->song);
+ }
+ clear(true);
+ }
+ cleanupCurrentAction();
+ }
+}
+
+void Command::updatePlayer() {
+ if (_vm->logic()->joeWalk() != JWM_MOVE) {
+ int16 cx = _vm->input()->mousePosX();
+ int16 cy = _vm->input()->mousePosY();
+ lookForCurrentObject(cx, cy);
+ lookForCurrentIcon(cx, cy);
+ }
+
+ if (_vm->input()->keyVerb() != VERB_NONE) {
+ if (_vm->input()->keyVerb() == VERB_USE_JOURNAL) {
+ _vm->logic()->useJournal();
+ } else if (_vm->input()->keyVerb() != VERB_SKIP_TEXT) {
+ _state.verb = _vm->input()->keyVerb();
+ if (isVerbInv(_state.verb)) {
+ _state.noun = _state.selNoun = 0;
+ _state.oldNoun = 0;
+ _state.oldVerb = VERB_NONE;
+ grabSelectedItem();
+ } else {
+ grabSelectedVerb();
+ }
+ }
+ _vm->input()->clearKeyVerb();
+ }
+
+ _mouseKey = _vm->input()->mouseButton();
+ _vm->input()->clearMouseButton();
+ if (_mouseKey > 0) {
+ grabCurrentSelection();
+ }
+}
+
+void Command::readCommandsFrom(byte *&ptr) {
+ uint16 i;
+
+ _numCmdList = READ_BE_UINT16(ptr); ptr += 2;
+ _cmdList = new CmdListData[_numCmdList + 1];
+ if (_numCmdList == 0) {
+ _cmdList[0].readFromBE(ptr);
+ } else {
+ memset(&_cmdList[0], 0, sizeof(CmdListData));
+ for (i = 1; i <= _numCmdList; i++) {
+ _cmdList[i].readFromBE(ptr);
+ }
+ }
+
+ _numCmdArea = READ_BE_UINT16(ptr); ptr += 2;
+ _cmdArea = new CmdArea[_numCmdArea + 1];
+ if (_numCmdArea == 0) {
+ _cmdArea[0].readFromBE(ptr);
+ } else {
+ memset(&_cmdArea[0], 0, sizeof(CmdArea));
+ for (i = 1; i <= _numCmdArea; i++) {
+ _cmdArea[i].readFromBE(ptr);
+ }
+ }
+
+ _numCmdObject = READ_BE_UINT16(ptr); ptr += 2;
+ _cmdObject = new CmdObject[_numCmdObject + 1];
+ if (_numCmdObject == 0) {
+ _cmdObject[0].readFromBE(ptr);
+ } else {
+ memset(&_cmdObject[0], 0, sizeof(CmdObject));
+ for (i = 1; i <= _numCmdObject; i++) {
+ _cmdObject[i].readFromBE(ptr);
+ }
+ }
+
+ _numCmdInventory = READ_BE_UINT16(ptr); ptr += 2;
+ _cmdInventory = new CmdInventory[_numCmdInventory + 1];
+ if (_numCmdInventory == 0) {
+ _cmdInventory[0].readFromBE(ptr);
+ } else {
+ memset(&_cmdInventory[0], 0, sizeof(CmdInventory));
+ for (i = 1; i <= _numCmdInventory; i++) {
+ _cmdInventory[i].readFromBE(ptr);
+ }
+ }
+
+ _numCmdGameState = READ_BE_UINT16(ptr); ptr += 2;
+ _cmdGameState = new CmdGameState[_numCmdGameState + 1];
+ if (_numCmdGameState == 0) {
+ _cmdGameState[0].readFromBE(ptr);
+ } else {
+ memset(&_cmdGameState[0], 0, sizeof(CmdGameState));
+ for (i = 1; i <= _numCmdGameState; i++) {
+ _cmdGameState[i].readFromBE(ptr);
+ }
+ }
+}
+
+ObjectData *Command::findObjectData(uint16 objRoomNum) const {
+ ObjectData *od = NULL;
+ if (objRoomNum != 0) {
+ objRoomNum += _vm->logic()->currentRoomData();
+ od = _vm->logic()->objectData(objRoomNum);
+ }
+ return od;
+}
+
+ItemData *Command::findItemData(Verb invNum) const {
+ ItemData *id = NULL;
+ uint16 itNum = _vm->logic()->findInventoryItem(invNum - VERB_INV_FIRST);
+ if (itNum != 0) {
+ id = _vm->logic()->itemData(itNum);
+ }
+ return id;
+}
+
+int16 Command::executeCommand(uint16 comId, int16 condResult) {
+ // execute.c l.313-452
+ debug(6, "Command::executeCommand() - cond = %X, com = %X", condResult, comId);
+
+ CmdListData *com = &_cmdList[comId];
+
+ if (com->setAreas) {
+ setAreas(comId);
+ }
+
+ // don't try to grab if action is TALK or WALK
+ if (_state.selAction != VERB_TALK_TO && _state.selAction != VERB_WALK_TO) {
+ int i;
+ for (i = 0; i < 2; ++i) {
+ int16 obj = _state.subject[i];
+ if (obj > 0) {
+ _vm->logic()->joeGrab(State::findGrab(_vm->logic()->objectData(obj)->state));
+ }
+ }
+ }
+
+ bool cutDone = false;
+ if (condResult > 0) {
+ // check for cutaway/dialogs before updating Objects
+ const char *desc = _vm->logic()->objectTextualDescription(condResult);
+ if (executeIfCutaway(desc)) {
+ condResult = 0;
+ cutDone = true;
+ } else if (executeIfDialog(desc)) {
+ condResult = 0;
+ }
+ }
+
+ int16 oldImage = 0;
+ if (_state.subject[0] > 0) {
+ // an object (not an item)
+ oldImage = _vm->logic()->objectData(_state.subject[0])->image;
+ }
+
+ if (com->setObjects) {
+ setObjects(comId);
+ }
+
+ if (com->setItems) {
+ setItems(comId);
+ }
+
+ if (com->imageOrder != 0 && _state.subject[0] > 0) {
+ ObjectData *od = _vm->logic()->objectData(_state.subject[0]);
+ // we must update the graphic image of the object
+ if (com->imageOrder < 0) {
+ // instead of setting to -1 or -2, flag as negative
+ if (od->image > 0) {
+ // make sure that object is not already updated
+ od->image = -(od->image + 10);
+ }
+ } else {
+ od->image = com->imageOrder;
+ }
+ _vm->graphics()->refreshObject(_state.subject[0]);
+ } else {
+ // this object is not being updated by command list, see if
+ // it has another image copied to it
+ if (_state.subject[0] > 0) {
+ // an object (not an item)
+ if (_vm->logic()->objectData(_state.subject[0])->image != oldImage) {
+ _vm->graphics()->refreshObject(_state.subject[0]);
+ }
+ }
+ }
+
+ // don't play music on an OPEN/CLOSE command - in case the command fails
+ if (_state.selAction != VERB_NONE &&
+ _state.selAction != VERB_OPEN &&
+ _state.selAction != VERB_CLOSE) {
+ // only play song if it's a PLAY BEFORE type
+ if (com->song > 0) {
+ _vm->sound()->playSong(com->song);
+ }
+ }
+
+ // do a special hardcoded section
+ // l.419-452 execute.c
+ switch (com->specialSection) {
+ case 1:
+ _vm->logic()->useJournal();
+ _state.selAction = VERB_USE_JOURNAL;
+ return condResult;
+ case 2:
+ _vm->logic()->joeUseDress(true);
+ break;
+ case 3:
+ _vm->logic()->joeUseClothes(true);
+ break;
+ case 4:
+ _vm->logic()->joeUseUnderwear();
+ break;
+ }
+
+ if (_state.subject[0] > 0)
+ changeObjectState(_state.selAction, _state.subject[0], com->song, cutDone);
+
+ if (condResult > 0) {
+ _vm->logic()->makeJoeSpeak(condResult, true);
+ }
+ return condResult;
+}
+
+int16 Command::makeJoeWalkTo(int16 x, int16 y, int16 objNum, Verb v, bool mustWalk) {
+ // Check to see if object is actually an exit to another
+ // room. If so, then set up new room
+ ObjectData *objData = _vm->logic()->objectData(objNum);
+ if (objData->x != 0 || objData->y != 0) {
+ x = objData->x;
+ y = objData->y;
+ }
+ if (v == VERB_WALK_TO) {
+ _vm->logic()->entryObj(objData->entryObj);
+ if (objData->entryObj > 0) {
+ _vm->logic()->newRoom(_vm->logic()->objectData(objData->entryObj)->room);
+ // because this is an exit object, see if there is
+ // a walk off point and set (x,y) accordingly
+ WalkOffData *wod = _vm->logic()->walkOffPointForObject(objNum);
+ if (wod != NULL) {
+ x = wod->x;
+ y = wod->y;
+ }
+ }
+ } else {
+ _vm->logic()->entryObj(0);
+ _vm->logic()->newRoom(0);
+ }
+
+ debug(6, "Command::makeJoeWalkTo() - x=%d y=%d newRoom=%d", x, y, _vm->logic()->newRoom());
+
+ int16 p = 0;
+ if (mustWalk) {
+ // determine which way for Joe to face Object
+ uint16 facing = State::findDirection(objData->state);
+ BobSlot *bobJoe = _vm->graphics()->bob(0);
+ if (x == bobJoe->x && y == bobJoe->y) {
+ _vm->logic()->joeFacing(facing);
+ _vm->logic()->joeFace();
+ } else {
+ p = _vm->walk()->moveJoe(facing, x, y, false);
+ if (p != 0) {
+ _vm->logic()->newRoom(0); // cancel makeJoeWalkTo, that should be equivalent to cr10 fix
+ }
+ }
+ }
+ return p;
+}
+
+void Command::grabCurrentSelection() {
+ _selPosX = _vm->input()->mousePosX();
+ _selPosY = _vm->input()->mousePosY();
+
+ uint16 zone = _vm->grid()->findObjectUnderCursor(_selPosX, _selPosY);
+ _state.noun = _vm->grid()->findObjectNumber(zone);
+ _state.verb = _vm->grid()->findVerbUnderCursor(_selPosX, _selPosY);
+
+ _selPosX += _vm->display()->horizontalScroll();
+
+ if (isVerbAction(_state.verb) || isVerbInvScroll(_state.verb)) {
+ grabSelectedVerb();
+ } else if (isVerbInv(_state.verb)) {
+ grabSelectedItem();
+ } else if (_state.noun != 0) {
+ grabSelectedNoun();
+ } else if (_selPosY < ROOM_ZONE_HEIGHT && _state.verb == VERB_NONE) {
+ // select without a command, do a WALK
+ clear(true);
+ _vm->logic()->joeWalk(JWM_EXECUTE);
+ }
+}
+
+void Command::grabSelectedObject(int16 objNum, uint16 objState, uint16 objName) {
+ if (_state.action != VERB_NONE) {
+ _cmdText.addObject(_vm->logic()->objectName(objName));
+ }
+
+ _state.subject[_state.commandLevel - 1] = objNum;
+
+ // if first noun and it's a 2 level command then set up action word
+ if (_state.action == VERB_USE && _state.commandLevel == 1) {
+ if (State::findUse(objState) == STATE_USE_ON) {
+ // object supports 2 levels, command not fully constructed
+ _state.commandLevel = 2;
+ _cmdText.addLinkWord(VERB_PREP_WITH);
+ _cmdText.display(INK_CMD_NORMAL);
+ _parse = false;
+ } else {
+ _parse = true;
+ }
+ } else if (_state.action == VERB_GIVE && _state.commandLevel == 1) {
+ // command not fully constructed
+ _state.commandLevel = 2;
+ _cmdText.addLinkWord(VERB_PREP_TO);
+ _cmdText.display(INK_CMD_NORMAL);
+ _parse = false;
+ } else {
+ _parse = true;
+ }
+
+ if (_parse) {
+ _state.verb = VERB_NONE;
+ _vm->logic()->joeWalk(JWM_EXECUTE);
+ _state.selAction = _state.action;
+ _state.action = VERB_NONE;
+ }
+}
+
+void Command::grabSelectedItem() {
+ ItemData *id = findItemData(_state.verb);
+ if (id == NULL || id->name <= 0) {
+ return;
+ }
+
+ int16 item = _vm->logic()->findInventoryItem(_state.verb - VERB_INV_FIRST);
+
+ // If we've selected via keyboard, and there is no VERB then do
+ // the ITEMs default, otherwise keep constructing!
+
+ if (_mouseKey == Input::MOUSE_LBUTTON ||
+ (_vm->input()->keyVerb() != VERB_NONE && _state.verb != VERB_NONE)) {
+ if (_state.action == VERB_NONE) {
+ if (_vm->input()->keyVerb() != VERB_NONE) {
+ // We've selected via the keyboard, no command is being
+ // constructed, so we shall find the item's default
+ _state.verb = State::findDefaultVerb(id->state);
+ if (_state.verb == VERB_NONE) {
+ // set to Look At
+ _state.verb = VERB_LOOK_AT;
+ _cmdText.setVerb(VERB_LOOK_AT);
+ }
+ _state.action = _state.verb;
+ } else {
+ // Action>0 ONLY if command has been constructed
+ // Left Mouse Button pressed just do Look At
+ _state.action = VERB_LOOK_AT;
+ _cmdText.setVerb(VERB_LOOK_AT);
+ }
+ }
+ _state.verb = VERB_NONE;
+ } else {
+ if (_cmdText.isEmpty()) {
+ _state.verb = VERB_LOOK_AT;
+ _state.action = VERB_LOOK_AT;
+ _cmdText.setVerb(VERB_LOOK_AT);
+ } else {
+ if (_state.commandLevel == 2 && _parse)
+ _state.verb = _state.action;
+ else
+ _state.verb = State::findDefaultVerb(id->state);
+ if (_state.verb == VERB_NONE) {
+ // No match made, so command not yet completed. Redefine as LOOK AT
+ _state.action = VERB_LOOK_AT;
+ _cmdText.setVerb(VERB_LOOK_AT);
+ } else {
+ _state.action = _state.verb;
+ }
+ _state.verb = VERB_NONE;
+ }
+ }
+
+ grabSelectedObject(-item, id->state, id->name);
+}
+
+void Command::grabSelectedNoun() {
+ ObjectData *od = findObjectData(_state.noun);
+ if (od == NULL || od->name <= 0) {
+ // selected a turned off object, so just walk
+ clear(true);
+ _state.noun = 0;
+ _vm->logic()->joeWalk(JWM_EXECUTE);
+ return;
+ }
+
+ if (_state.verb == VERB_NONE) {
+ if (_mouseKey == Input::MOUSE_LBUTTON) {
+ if ((_state.commandLevel != 2 && _state.action == VERB_NONE) ||
+ (_state.commandLevel == 2 && _parse)) {
+ _state.verb = VERB_WALK_TO;
+ _state.action = VERB_WALK_TO;
+ _cmdText.setVerb(VERB_WALK_TO);
+ }
+ } else if (_mouseKey == Input::MOUSE_RBUTTON) {
+ if (_cmdText.isEmpty()) {
+ _state.verb = State::findDefaultVerb(od->state);
+ _state.selAction = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb;
+ _cmdText.setVerb(_state.selAction);
+ _cmdText.addObject(_vm->logic()->objectName(od->name));
+ } else {
+ if ((_state.commandLevel == 2 && !_parse) || _state.action != VERB_NONE) {
+ _state.verb = _state.action;
+ } else {
+ _state.verb = State::findDefaultVerb(od->state);
+ }
+ _state.action = (_state.verb == VERB_NONE) ? VERB_WALK_TO : _state.verb;
+ _state.verb = VERB_NONE;
+ }
+ }
+ }
+
+ _state.selNoun = 0;
+ int16 objNum = _vm->logic()->currentRoomData() + _state.noun;
+ grabSelectedObject(objNum, od->state, od->name);
+}
+
+void Command::grabSelectedVerb() {
+ if (isVerbInvScroll(_state.verb)) {
+ // move through inventory (by four if right mouse button)
+ uint16 scroll = (_mouseKey == Input::MOUSE_RBUTTON) ? 4 : 1;
+ _vm->logic()->inventoryScroll(scroll, _state.verb == VERB_SCROLL_UP);
+ } else {
+ _state.action = _state.verb;
+ _state.subject[0] = 0;
+ _state.subject[1] = 0;
+
+ if (_vm->logic()->joeWalk() == JWM_MOVE && _state.verb != VERB_NONE) {
+ _vm->logic()->joeWalk(JWM_NORMAL);
+ }
+ _state.commandLevel = 1;
+ _state.oldVerb = VERB_NONE;
+ _state.oldNoun = 0;
+ _cmdText.setVerb(_state.verb);
+ _cmdText.display(INK_CMD_NORMAL);
+ }
+}
+
+bool Command::executeIfCutaway(const char *description) {
+ if (strlen(description) > 4 &&
+ scumm_stricmp(description + strlen(description) - 4, ".cut") == 0) {
+
+ _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
+
+ char nextCutaway[20];
+ memset(nextCutaway, 0, sizeof(nextCutaway));
+ _vm->logic()->playCutaway(description, nextCutaway);
+ while (nextCutaway[0] != '\0') {
+ _vm->logic()->playCutaway(nextCutaway, nextCutaway);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool Command::executeIfDialog(const char *description) {
+ if (strlen(description) > 4 &&
+ scumm_stricmp(description + strlen(description) - 4, ".dog") == 0) {
+
+ _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
+
+ char cutaway[20];
+ memset(cutaway, 0, sizeof(cutaway));
+ _vm->logic()->startDialogue(description, _state.selNoun, cutaway);
+
+ while (cutaway[0] != '\0') {
+ char currentCutaway[20];
+ strcpy(currentCutaway, cutaway);
+ _vm->logic()->playCutaway(currentCutaway, cutaway);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool Command::handleWrongAction() {
+ // l.96-141 execute.c
+ uint16 objMax = _vm->grid()->objMax(_vm->logic()->currentRoom());
+ uint16 roomData = _vm->logic()->currentRoomData();
+
+ // select without a command or WALK TO ; do a WALK
+ if ((_state.selAction == VERB_WALK_TO || _state.selAction == VERB_NONE) &&
+ (_state.selNoun > objMax || _state.selNoun == 0)) {
+ if (_state.selAction == VERB_NONE) {
+ _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
+ }
+ _vm->walk()->moveJoe(0, _selPosX, _selPosY, false);
+ return true;
+ }
+
+ // check to see if one of the objects is hidden
+ int i;
+ for (i = 0; i < 2; ++i) {
+ int16 obj = _state.subject[i];
+ if (obj > 0 && _vm->logic()->objectData(obj)->name <= 0) {
+ return true;
+ }
+ }
+
+ // check for USE command on exists
+ if (_state.selAction == VERB_USE &&
+ _state.subject[0] > 0 && _vm->logic()->objectData(_state.subject[0])->entryObj > 0) {
+ _state.selAction = VERB_WALK_TO;
+ }
+
+ if (_state.selNoun > 0 && _state.selNoun <= objMax) {
+ uint16 objNum = roomData + _state.selNoun;
+ if (makeJoeWalkTo(_selPosX, _selPosY, objNum, _state.selAction, true) != 0) {
+ return true;
+ }
+ if (_state.selAction == VERB_WALK_TO && _vm->logic()->objectData(objNum)->entryObj < 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Command::sayInvalidAction(Verb action, int16 subj1, int16 subj2) {
+ // l.158-272 execute.c
+ switch (action) {
+
+ case VERB_LOOK_AT:
+ lookAtSelectedObject();
+ break;
+
+ case VERB_OPEN:
+ // 'it doesn't seem to open'
+ _vm->logic()->makeJoeSpeak(1);
+ break;
+
+ case VERB_USE:
+ if (subj1 < 0) {
+ uint16 k = _vm->logic()->itemData(-subj1)->sfxDescription;
+ if (k > 0) {
+ _vm->logic()->makeJoeSpeak(k, true);
+ } else {
+ _vm->logic()->makeJoeSpeak(2);
+ }
+ } else {
+ _vm->logic()->makeJoeSpeak(2);
+ }
+ break;
+
+ case VERB_TALK_TO:
+ _vm->logic()->makeJoeSpeak(24 + _vm->randomizer.getRandomNumber(2));
+ break;
+
+ case VERB_CLOSE:
+ _vm->logic()->makeJoeSpeak(2);
+ break;
+
+ case VERB_MOVE:
+ // 'I can't move it'
+ if (subj1 > 0) {
+ int16 img = _vm->logic()->objectData(subj1)->image;
+ if (img == -4 || img == -3) {
+ _vm->logic()->makeJoeSpeak(18);
+ } else {
+ _vm->logic()->makeJoeSpeak(3);
+ }
+ } else {
+ _vm->logic()->makeJoeSpeak(3);
+ }
+ break;
+
+ case VERB_GIVE:
+ // 'I can't give the subj1 to subj2'
+ if (subj1 < 0) {
+ if (subj2 > 0) {
+ int16 img = _vm->logic()->objectData(subj2)->image;
+ if (img == -4 || img == -3) {
+ _vm->logic()->makeJoeSpeak(27 + _vm->randomizer.getRandomNumber(2));
+ }
+ } else {
+ _vm->logic()->makeJoeSpeak(11);
+ }
+ } else {
+ _vm->logic()->makeJoeSpeak(12);
+ }
+ break;
+
+ case VERB_PICK_UP:
+ if (subj1 < 0) {
+ _vm->logic()->makeJoeSpeak(14);
+ } else {
+ int16 img = _vm->logic()->objectData(subj1)->image;
+ if (img == -4 || img == -3) {
+ // Trying to get a person
+ _vm->logic()->makeJoeSpeak(20);
+ } else {
+ // 5 : 'I can't pick that up'
+ // 6 : 'I don't think I need that'
+ // 7 : 'I'd rather leave it here'
+ // 8 : 'I don't think I'd have any use for that'
+ _vm->logic()->makeJoeSpeak(5 + _vm->randomizer.getRandomNumber(3));
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void Command::changeObjectState(Verb action, int16 obj, int16 song, bool cutDone) {
+ // l.456-533 execute.c
+ ObjectData *objData = _vm->logic()->objectData(obj);
+
+ if (action == VERB_OPEN && !cutDone) {
+ if (State::findOn(objData->state) == STATE_ON_ON) {
+ State::alterOn(&objData->state, STATE_ON_OFF);
+ State::alterDefaultVerb(&objData->state, VERB_NONE);
+
+ // play music if it exists... (or SFX for open/close door)
+ if (song != 0) {
+ _vm->sound()->playSong(ABS(song));
+ }
+
+ if (objData->entryObj != 0) {
+ // if it's a door, then update door that it links to
+ openOrCloseAssociatedObject(action, ABS(objData->entryObj));
+ objData->entryObj = ABS(objData->entryObj);
+ }
+ } else {
+ // 'it's already open !'
+ _vm->logic()->makeJoeSpeak(9);
+ }
+ } else if (action == VERB_CLOSE && !cutDone) {
+ if (State::findOn(objData->state) == STATE_ON_OFF) {
+ State::alterOn(&objData->state, STATE_ON_ON);
+ State::alterDefaultVerb(&objData->state, VERB_OPEN);
+
+ // play music if it exists... (or SFX for open/close door)
+ if (song != 0) {
+ _vm->sound()->playSong(ABS(song));
+ }
+
+ if (objData->entryObj != 0) {
+ // if it's a door, then update door that it links to
+ openOrCloseAssociatedObject(action, ABS(objData->entryObj));
+ objData->entryObj = -ABS(objData->entryObj);
+ }
+ } else {
+ // 'it's already closed !'
+ _vm->logic()->makeJoeSpeak(10);
+ }
+ } else if (action == VERB_MOVE) {
+ State::alterOn(&objData->state, STATE_ON_OFF);
+ }
+}
+
+void Command::cleanupCurrentAction() {
+ // l.595-597 execute.c
+ _vm->logic()->joeFace();
+ _state.oldNoun = 0;
+ _state.oldVerb = VERB_NONE;
+}
+
+void Command::openOrCloseAssociatedObject(Verb action, int16 otherObj) {
+ CmdListData *cmdList = &_cmdList[1];
+ uint16 com = 0;
+ uint16 i;
+ for (i = 1; i <= _numCmdList && com == 0; ++i, ++cmdList) {
+ if (cmdList->match(action, otherObj, 0)) {
+ if (cmdList->setConditions) {
+ CmdGameState *cmdGs = _cmdGameState;
+ uint16 j;
+ for (j = 1; j <= _numCmdGameState; ++j) {
+ if (cmdGs[j].id == i && cmdGs[j].gameStateSlot > 0) {
+ if (_vm->logic()->gameState(cmdGs[j].gameStateSlot) == cmdGs[j].gameStateValue) {
+ com = i;
+ break;
+ }
+ }
+ }
+ } else {
+ com = i;
+ break;
+ }
+ }
+ }
+
+ if (com != 0) {
+
+ debug(6, "Command::openOrCloseAssociatedObject() com=%X", com);
+
+ cmdList = &_cmdList[com];
+ ObjectData *objData = _vm->logic()->objectData(otherObj);
+
+ if (cmdList->imageOrder != 0) {
+ objData->image = cmdList->imageOrder;
+ }
+
+ if (action == VERB_OPEN) {
+ if (State::findOn(objData->state) == STATE_ON_ON) {
+ State::alterOn(&objData->state, STATE_ON_OFF);
+ State::alterDefaultVerb(&objData->state, VERB_NONE);
+ objData->entryObj = ABS(objData->entryObj);
+ }
+ } else if (action == VERB_CLOSE) {
+ if (State::findOn(objData->state) == STATE_ON_OFF) {
+ State::alterOn(&objData->state, STATE_ON_ON);
+ State::alterDefaultVerb(&objData->state, VERB_OPEN);
+ objData->entryObj = -ABS(objData->entryObj);
+ }
+ }
+ }
+}
+
+int16 Command::setConditions(uint16 command, bool lastCmd) {
+ debug(9, "Command::setConditions(%d, %d)", command, lastCmd);
+
+ int16 ret = 0;
+ uint16 cmdState[21];
+ memset(cmdState, 0, sizeof(cmdState));
+ uint16 cmdStateCount = 0;
+ uint16 i;
+ CmdGameState *cmdGs = &_cmdGameState[1];
+ for (i = 1; i <= _numCmdGameState; ++i, ++cmdGs) {
+ if (cmdGs->id == command) {
+ if (cmdGs->gameStateSlot > 0) {
+ if (_vm->logic()->gameState(cmdGs->gameStateSlot) != cmdGs->gameStateValue) {
+ debug(6, "Command::setConditions() - GS[%d] == %d (should be %d)", cmdGs->gameStateSlot, _vm->logic()->gameState(cmdGs->gameStateSlot), cmdGs->gameStateValue);
+ // failed test
+ ret = i;
+ break;
+ }
+ } else {
+ cmdState[cmdStateCount] = i;
+ ++cmdStateCount;
+ }
+ }
+ }
+
+ if (ret > 0) {
+ // we've failed, so see if we need to make Joe speak
+ cmdGs = &_cmdGameState[ret];
+ if (cmdGs->speakValue > 0 && lastCmd) {
+ // check to see if fail state is in fact a cutaway
+ const char *objDesc = _vm->logic()->objectTextualDescription(cmdGs->speakValue);
+ if (!executeIfCutaway(objDesc) && !executeIfDialog(objDesc)) {
+ _vm->logic()->makeJoeSpeak(cmdGs->speakValue, true);
+ }
+ ret = -2;
+ } else {
+ // return -1 so Joe will be able to speak a normal description
+ ret = -1;
+ }
+ } else {
+ ret = 0;
+ // all tests were okay, now set gamestates
+ for (i = 0; i < cmdStateCount; ++i) {
+ cmdGs = &_cmdGameState[cmdState[i]];
+ _vm->logic()->gameState(ABS(cmdGs->gameStateSlot), cmdGs->gameStateValue);
+ // set return value for Joe to say something
+ ret = cmdGs->speakValue;
+ }
+ }
+ return ret;
+}
+
+void Command::setAreas(uint16 command) {
+ debug(9, "Command::setAreas(%d)", command);
+
+ CmdArea *cmdArea = &_cmdArea[1];
+ for (uint16 i = 1; i <= _numCmdArea; ++i, ++cmdArea) {
+ if (cmdArea->id == command) {
+ uint16 areaNum = ABS(cmdArea->area);
+ Area *area = _vm->grid()->area(cmdArea->room, areaNum);
+ if (cmdArea->area > 0) {
+ // turn on area
+ area->mapNeighbours = ABS(area->mapNeighbours);
+ } else {
+ // turn off area
+ area->mapNeighbours = -ABS(area->mapNeighbours);
+ }
+ }
+ }
+}
+
+void Command::setObjects(uint16 command) {
+ debug(9, "Command::setObjects(%d)", command);
+
+ CmdObject *cmdObj = &_cmdObject[1];
+ for (uint16 i = 1; i <= _numCmdObject; ++i, ++cmdObj) {
+ if (cmdObj->id == command) {
+
+ // found an object
+ uint16 dstObj = ABS(cmdObj->dstObj);
+ ObjectData *objData = _vm->logic()->objectData(dstObj);
+
+ debug(6, "Command::setObjects() - dstObj=%X srcObj=%X _state.subject[0]=%X", cmdObj->dstObj, cmdObj->srcObj, _state.subject[0]);
+
+ if (cmdObj->dstObj > 0) {
+ // show the object
+ objData->name = ABS(objData->name);
+ // test that the object has not already been deleted
+ // by checking if it is not equal to zero
+ if (cmdObj->srcObj == -1 && objData->name != 0) {
+ // delete object by setting its name to 0 and
+ // turning off graphic image
+ objData->name = 0;
+ if (objData->room == _vm->logic()->currentRoom()) {
+ if (dstObj != _state.subject[0]) {
+ // if the new object we have updated is on screen and is not the
+ // current object, then we can update. This is because we turn
+ // current object off ourselves by COM_LIST(com, 8)
+ if (objData->image != -3 && objData->image != -4) {
+ // it is a normal object (not a person)
+ // turn the graphic image off for the object
+ objData->image = -(objData->image + 10);
+ }
+ }
+ // invalidate object area
+ uint16 objZone = dstObj - _vm->logic()->currentRoomData();
+ _vm->grid()->setZone(GS_ROOM, objZone, 0, 0, 1, 1);
+ }
+ }
+
+ if (cmdObj->srcObj > 0) {
+ // copy data from dummy object to object
+ int16 image1 = objData->image;
+ int16 image2 = _vm->logic()->objectData(cmdObj->srcObj)->image;
+ _vm->logic()->objectCopy(cmdObj->srcObj, dstObj);
+ if (image1 != 0 && image2 == 0 && objData->room == _vm->logic()->currentRoom()) {
+ uint16 bobNum = _vm->logic()->findBob(dstObj);
+ if (bobNum != 0) {
+ _vm->graphics()->bob(bobNum)->clear();
+ }
+ }
+ }
+
+ if (dstObj != _state.subject[0]) {
+ // if the new object we have updated is on screen and
+ // is not current object then update it
+ _vm->graphics()->refreshObject(dstObj);
+ }
+ } else {
+ // hide the object
+ if (objData->name > 0) {
+ objData->name = -objData->name;
+ // may need to turn BOBs off for objects to be hidden on current
+ // screen ! if the new object we have updated is on screen and
+ // is not current object then update it
+ _vm->graphics()->refreshObject(dstObj);
+ }
+ }
+ }
+ }
+}
+
+void Command::setItems(uint16 command) {
+ debug(9, "Command::setItems(%d)", command);
+
+ ItemData *items = _vm->logic()->itemData(0);
+ CmdInventory *cmdInv = &_cmdInventory[1];
+ for (uint16 i = 1; i <= _numCmdInventory; ++i, ++cmdInv) {
+ if (cmdInv->id == command) {
+ uint16 dstItem = ABS(cmdInv->dstItem);
+ // found an item
+ if (cmdInv->dstItem > 0) {
+ // add item to inventory
+ if (cmdInv->srcItem > 0) {
+ // copy data from source item to item, then enable it
+ items[dstItem] = items[cmdInv->srcItem];
+ items[dstItem].name = ABS(items[dstItem].name);
+ }
+ _vm->logic()->inventoryInsertItem(cmdInv->dstItem);
+ } else {
+ // delete item
+ if (items[dstItem].name > 0) {
+ _vm->logic()->inventoryDeleteItem(dstItem);
+ }
+ if (cmdInv->srcItem > 0) {
+ // copy data from source item to item, then disable it
+ items[dstItem] = items[cmdInv->srcItem];
+ items[dstItem].name = -ABS(items[dstItem].name);
+ }
+ }
+ }
+ }
+}
+
+uint16 Command::nextObjectDescription(ObjectDescription* objDesc, uint16 firstDesc) {
+ // l.69-103 select.c
+ uint16 i;
+ uint16 diff = objDesc->lastDescription - firstDesc;
+ debug(6, "Command::nextObjectDescription() - diff = %d, type = %d", diff, objDesc->type);
+ switch (objDesc->type) {
+ case 0:
+ // random type, start with first description
+ if (objDesc->lastSeenNumber == 0) {
+ // first time look at called
+ objDesc->lastSeenNumber = firstDesc;
+ break;
+ }
+ // already displayed first, do a random
+ case 1:
+ i = objDesc->lastSeenNumber;
+ while (i == objDesc->lastSeenNumber) {
+ i = firstDesc + _vm->randomizer.getRandomNumber(diff);
+ }
+ objDesc->lastSeenNumber = i;
+ break;
+ case 2:
+ // sequential, but loop
+ ++objDesc->lastSeenNumber;
+ if (objDesc->lastSeenNumber > objDesc->lastDescription) {
+ objDesc->lastSeenNumber = firstDesc;
+ }
+ break;
+ case 3:
+ // sequential without looping
+ if (objDesc->lastSeenNumber != objDesc->lastDescription) {
+ ++objDesc->lastSeenNumber;
+ }
+ break;
+ }
+ return objDesc->lastSeenNumber;
+}
+
+void Command::lookAtSelectedObject() {
+ uint16 desc;
+ if (_state.subject[0] < 0) {
+ desc = _vm->logic()->itemData(-_state.subject[0])->description;
+ } else {
+ ObjectData *objData = _vm->logic()->objectData(_state.subject[0]);
+ if (objData->name <= 0) {
+ return;
+ }
+ desc = objData->description;
+ }
+
+ debug(6, "Command::lookAtSelectedObject() - desc = %X, _state.subject[0] = %X", desc, _state.subject[0]);
+
+ // check to see if the object/item has a series of description
+ ObjectDescription *objDesc = _vm->logic()->objectDescription(1);
+ uint16 i;
+ for (i = 1; i <= _vm->logic()->objectDescriptionCount(); ++i, ++objDesc) {
+ if (objDesc->object == _state.subject[0]) {
+ desc = nextObjectDescription(objDesc, desc);
+ break;
+ }
+ }
+
+ _vm->logic()->makeJoeSpeak(desc, true);
+ _vm->logic()->joeFace();
+}
+
+void Command::lookForCurrentObject(int16 cx, int16 cy) {
+ uint16 obj = _vm->grid()->findObjectUnderCursor(cx, cy);
+ _state.noun = _vm->grid()->findObjectNumber(obj);
+
+ if (_state.oldNoun == _state.noun) {
+ return;
+ }
+
+ ObjectData *od = findObjectData(_state.noun);
+ if (od == NULL || od->name <= 0) {
+ _state.oldNoun = _state.noun;
+ _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
+ if (_state.action != VERB_NONE) {
+ _cmdText.display(INK_CMD_NORMAL);
+ }
+ return;
+ }
+
+ // if no command yet selected, then use DEFAULT command, if any
+ if (_state.action == VERB_NONE) {
+ Verb v = State::findDefaultVerb(od->state);
+ _cmdText.setVerb((v == VERB_NONE) ? VERB_WALK_TO : v);
+ if (_state.noun == 0) {
+ _cmdText.clear();
+ }
+ }
+ const char *name = _vm->logic()->objectName(od->name);
+ _cmdText.displayTemp(INK_CMD_NORMAL, name);
+ _state.oldNoun = _state.noun;
+}
+
+void Command::lookForCurrentIcon(int16 cx, int16 cy) {
+ _state.verb = _vm->grid()->findVerbUnderCursor(cx, cy);
+ if (_state.oldVerb != _state.verb) {
+
+ if (_state.action == VERB_NONE) {
+ _cmdText.clear();
+ }
+ _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
+
+ if (isVerbInv(_state.verb)) {
+ ItemData *id = findItemData(_state.verb);
+ if (id != NULL && id->name > 0) {
+ if (_state.action == VERB_NONE) {
+ Verb v = State::findDefaultVerb(id->state);
+ _cmdText.setVerb((v == VERB_NONE) ? VERB_LOOK_AT : v);
+ }
+ const char *name = _vm->logic()->objectName(id->name);
+ _cmdText.displayTemp(INK_CMD_NORMAL, name);
+ }
+ } else if (isVerbAction(_state.verb)) {
+ _cmdText.displayTemp(INK_CMD_NORMAL, _state.verb);
+ } else if (_state.verb == VERB_NONE) {
+ _cmdText.display(INK_CMD_NORMAL);
+ }
+ _state.oldVerb = _state.verb;
+ }
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/command.h b/engines/queen/command.h
new file mode 100644
index 0000000000..3ef848b24d
--- /dev/null
+++ b/engines/queen/command.h
@@ -0,0 +1,234 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENCOMMAND_H
+#define QUEENCOMMAND_H
+
+#include "common/util.h"
+#include "queen/structs.h"
+
+namespace Queen {
+
+class QueenEngine;
+
+struct CmdText {
+
+ CmdText(bool reversed, uint8 y, QueenEngine *vm);
+
+ //! reset the command sentence
+ void clear();
+
+ //! display the command sentence using the specified color
+ void display(uint8 color);
+
+ //! display a temporary command sentence using the specified parameters
+ void displayTemp(uint8 color, Verb v, const char *name = NULL, bool outlined = false);
+
+ //! display a temporary command sentence using the specified parameters
+ void displayTemp(uint8 color, const char *name, bool outlined = false);
+
+ //! set the verb for the command sentence
+ void setVerb(Verb v);
+
+ //! set the link word (between verb and object) for the command sentence
+ void addLinkWord(Verb v);
+
+ //! add an object name to the command sentence
+ void addObject(const char *objName);
+
+ //! returns true if the command sentence is empty
+ bool isEmpty() const;
+
+ enum {
+ MAX_COMMAND_LEN = 256,
+ COMMAND_Y_POS = 151
+ };
+
+ uint8 _y;
+
+ //! flag indicating if the words in the sentence are reversed (hebrew version)
+ bool _isReversed;
+
+ //! buffer containing the current command sentence
+ char _command[MAX_COMMAND_LEN];
+
+ QueenEngine *_vm;
+};
+
+struct CmdState {
+
+ void init();
+
+ Verb oldVerb, verb;
+ Verb action;
+ int16 oldNoun, noun;
+ int commandLevel;
+ int16 subject[2];
+
+ Verb selAction;
+ int16 selNoun;
+};
+
+class Command {
+public:
+
+ Command(QueenEngine *vm);
+ ~Command();
+
+ //! initialise command construction
+ void clear(bool clearTexts);
+
+ //! execute last constructed command
+ void executeCurrentAction();
+
+ //! get player input and construct command from it
+ void updatePlayer();
+
+ //! read all command arrays from stream
+ void readCommandsFrom(byte *&ptr);
+
+ enum {
+ MAX_MATCHING_CMDS = 50
+ };
+
+private:
+
+ //! get a reference to the ObjectData for the specified room object
+ ObjectData *findObjectData(uint16 objRoomNum) const;
+
+ //! get a reference to the ItemData for the specified inventory object
+ ItemData *findItemData(Verb invNum) const;
+
+ //! execute the current command
+ int16 executeCommand(uint16 comId, int16 condResult);
+
+ //! move Joe to the specified position, handling new room switching
+ int16 makeJoeWalkTo(int16 x, int16 y, int16 objNum, Verb v, bool mustWalk);
+
+ //! update command state with current selected action
+ void grabCurrentSelection();
+
+ //! update command state with current selected object
+ void grabSelectedObject(int16 objNum, uint16 objState, uint16 objName);
+
+ //! update command state with current selected inventory object
+ void grabSelectedItem();
+
+ //! update command state with current selected room object
+ void grabSelectedNoun();
+
+ //! update command state with current selected verb
+ void grabSelectedVerb();
+
+ //! if the description is a cutaway file, execute it
+ bool executeIfCutaway(const char *description);
+
+ //! if the description is a dialog file, execute it
+ bool executeIfDialog(const char *description);
+
+ //! handle a wrong/invalid user action
+ bool handleWrongAction();
+
+ //! make Joe speak something for a wrong/invalid action
+ void sayInvalidAction(Verb action, int16 subj1, int16 subj2);
+
+ //! update an object state
+ void changeObjectState(Verb action, int16 obj, int16 song, bool cutDone);
+
+ //! reset current action
+ void cleanupCurrentAction();
+
+ //! OPEN_CLOSE_OTHER(OBJECT_DATA[S][4])
+ void openOrCloseAssociatedObject(Verb action, int16 obj);
+
+ //! update gamestates - P1_SET_CONDITIONS
+ int16 setConditions(uint16 command, bool lastCmd);
+
+ //! turn on/off areas - P2_SET_AREAS
+ void setAreas(uint16 command);
+
+ //! hide/show objects, redisplay if in the same room as Joe - P3_SET_OBJECTS
+ void setObjects(uint16 command);
+
+ //! inserts/deletes items (inventory) - P4_SET_ITEMS
+ void setItems(uint16 command);
+
+ //! update description for object and returns description number to use
+ uint16 nextObjectDescription(ObjectDescription *objDesc, uint16 firstDesc);
+
+ //! speak description of selected object
+ void lookAtSelectedObject();
+
+ //! get the current object under the cursor
+ void lookForCurrentObject(int16 cx, int16 cy);
+
+ //! get the current icon panel under the cursor (inventory item or verb)
+ void lookForCurrentIcon(int16 cx, int16 cy);
+
+ //! returns true if the verb is an action verb
+ bool isVerbAction(Verb v) const { return (v >= VERB_PANEL_COMMAND_FIRST && v <= VERB_PANEL_COMMAND_LAST) || (v == VERB_WALK_TO); };
+
+ //! return true if the verb is an inventory item
+ bool isVerbInv(Verb v) const { return v >= VERB_INV_FIRST && v <= VERB_INV_LAST; }
+
+ //! returns true if the specified verb is an inventory scroll
+ bool isVerbInvScroll(Verb v) const { return v == VERB_SCROLL_UP || v == VERB_SCROLL_DOWN; }
+
+ //! commands list for each possible action
+ CmdListData *_cmdList;
+ uint16 _numCmdList;
+
+ //! commands list for areas
+ CmdArea *_cmdArea;
+ uint16 _numCmdArea;
+
+ //! commands list for objects
+ CmdObject *_cmdObject;
+ uint16 _numCmdObject;
+
+ //! commands list for inventory
+ CmdInventory *_cmdInventory;
+ uint16 _numCmdInventory;
+
+ //! commands list for gamestate
+ CmdGameState *_cmdGameState;
+ uint16 _numCmdGameState;
+
+ //! textual form of the command (displayed between room and panel areas)
+ CmdText _cmdText;
+
+ //! flag indicating that the current command is fully constructed
+ bool _parse;
+
+ //! state of current constructed command
+ CmdState _state;
+
+ //! last user selection
+ int _mouseKey, _selPosX, _selPosY;
+
+ QueenEngine *_vm;
+};
+
+} // End of namespace Queen
+
+#endif
+
diff --git a/engines/queen/credits.cpp b/engines/queen/credits.cpp
new file mode 100644
index 0000000000..a88c67e587
--- /dev/null
+++ b/engines/queen/credits.cpp
@@ -0,0 +1,145 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/credits.h"
+
+#include "queen/display.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+
+namespace Queen {
+
+Credits::Credits(QueenEngine *vm, const char* filename) :
+ _vm(vm), _running(true), _count(0), _pause(0), _justify(0), _fontSize(0), _color(0), _zone(0) {
+ uint32 size;
+ char *buf = (char *)_vm->resource()->loadFile(filename, 0, &size);
+ _credits = new LineReader(buf, size);
+}
+
+Credits::~Credits() {
+ delete _credits;
+}
+
+void Credits::nextRoom() {
+ if (-1 == _pause) {
+ _pause = 0;
+ _vm->display()->clearTexts(0, 199);
+ }
+}
+
+void Credits::update() {
+ if (!_running)
+ return;
+
+ if (_pause > 0) {
+ _pause--;
+ if (!_pause)
+ _vm->display()->clearTexts(0, 199);
+ return;
+ }
+
+ /* wait until next room */
+ if (-1 == _pause)
+ return;
+
+ for (;;) {
+ const char *line = _credits->nextLine();
+
+ if (0 == memcmp(line, "EN", 2)) {
+ _running = false;
+ return;
+ }
+
+ if ('.' == line[0]) {
+ int i;
+
+ switch (tolower(line[1])) {
+ case 'l' :
+ _justify = 0;
+ break;
+ case 'c' :
+ _justify = 1;
+ break;
+ case 'r' :
+ _justify = 2;
+ break;
+ case 's' :
+ _fontSize = 0;
+ break;
+ case 'b' :
+ _fontSize = 1;
+ break;
+ case 'p' :
+ _pause = atoi(&line[3]);
+ _pause *= 10;
+ /* wait until next room */
+ if (0 == _pause)
+ _pause = -1;
+ for (i = 0; i < _count; i++) {
+ _vm->display()->textCurrentColor(_list[i].color);
+ _vm->display()->setText(_list[i].x, _list[i].y, _list[i].text);
+ }
+ _count = 0;
+ return;
+ case 'i' :
+ _color = atoi(&line[3]);
+ break;
+ case '1' :
+ case '2' :
+ case '3' :
+ case '4' :
+ case '5' :
+ case '6' :
+ case '7' :
+ case '8' :
+ case '9' :
+ _zone = line[1] - '1';
+ break;
+ }
+ } else {
+ assert(_count < ARRAYSIZE(_list));
+ _list[_count].text = line;
+ _list[_count].color = _color;
+ _list[_count].fontSize = _fontSize;
+ switch (_justify) {
+ case 0:
+ _list[_count].x = (_zone % 3) * (320 / 3) + 8;
+ break;
+ case 1:
+ _list[_count].x = (_zone % 3) * (320 / 3) + 54 - _vm->display()->textWidth(line) / 2;
+ if (_list[_count].x < 8)
+ _list[_count].x = 8;
+ break;
+ case 2:
+ _list[_count].x = (_zone % 3) * (320 / 3) + 100 - _vm->display()->textWidth(line);
+ break;
+ }
+ _list[_count].y = (_zone / 3) * (200 / 3) + (_count * 10);
+ _count++;
+ }
+ }
+}
+
+
+} // End of namespace Queen
+
diff --git a/engines/queen/credits.h b/engines/queen/credits.h
new file mode 100644
index 0000000000..4f34772c28
--- /dev/null
+++ b/engines/queen/credits.h
@@ -0,0 +1,88 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef CREDITS_H
+#define CREDITS_H
+
+#include "common/util.h"
+#include "queen/defs.h"
+
+namespace Queen {
+
+class QueenEngine;
+class LineReader;
+
+class Credits {
+public:
+
+ Credits(QueenEngine *vm, const char* filename);
+ ~Credits();
+
+ //! update/display credits for current room
+ void update();
+
+ //! handles room switching
+ void nextRoom();
+
+ //! returns true if the credits are running
+ bool running() const { return _running; }
+
+private:
+
+ struct Line {
+ short x, y, color, fontSize;
+ const char *text;
+ };
+
+ //! contains the formatted lines of texts to display
+ Line _list[19];
+
+ //! true if end of credits description hasn't been reached
+ bool _running;
+
+ //! number of elements in _list array
+ int _count;
+
+ //! pause counts for next room
+ int _pause;
+
+ //! current text justification mode
+ int _justify;
+
+ //! current font size (unused ?)
+ int _fontSize;
+
+ //! current text color
+ int _color;
+
+ //! current text position
+ int _zone;
+
+ //! contains the credits description
+ LineReader *_credits;
+
+ QueenEngine *_vm;
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/cutaway.cpp b/engines/queen/cutaway.cpp
new file mode 100644
index 0000000000..dc24d5b627
--- /dev/null
+++ b/engines/queen/cutaway.cpp
@@ -0,0 +1,1324 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/cutaway.h"
+
+#include "queen/bankman.h"
+#include "queen/display.h"
+#include "queen/graphics.h"
+#include "queen/grid.h"
+#include "queen/input.h"
+#include "queen/logic.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+#include "queen/talk.h"
+#include "queen/walk.h"
+
+namespace Queen {
+
+void Cutaway::run(
+ const char *filename,
+ char *nextFilename,
+ QueenEngine *vm) {
+ Cutaway *cutaway = new Cutaway(filename, vm);
+ cutaway->run(nextFilename);
+ delete cutaway;
+}
+
+Cutaway::Cutaway(
+ const char *filename,
+ QueenEngine *vm)
+ : _vm(vm), _personDataCount(0), _personFaceCount(0), _lastSong(0), _songBeforeComic(0) {
+ memset(&_bankNames, 0, sizeof(_bankNames));
+ _vm->input()->cutawayQuitReset();
+ load(filename);
+}
+
+Cutaway::~Cutaway() {
+ delete[] _fileData;
+}
+
+void Cutaway::load(const char *filename) {
+ byte *ptr;
+
+ debug(6, "----- Cutaway::load(\"%s\") -----", filename);
+
+ ptr = _fileData = _vm->resource()->loadFile(filename, 20);
+
+ if (0 == scumm_stricmp(filename, "comic.cut"))
+ _songBeforeComic = _vm->sound()->lastOverride();
+
+ strcpy(_basename, filename);
+ _basename[strlen(_basename)-4] = '\0';
+
+ _comPanel = READ_BE_UINT16(ptr);
+ ptr += 2;
+ debug(6, "_comPanel = %i", _comPanel);
+ _cutawayObjectCount = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ debug(6, "_cutawayObjectCount = %i", _cutawayObjectCount);
+
+ if (_cutawayObjectCount < 0) {
+ _cutawayObjectCount = -_cutawayObjectCount;
+ _vm->input()->canQuit(false);
+ } else
+ _vm->input()->canQuit(true);
+
+ int16 flags1 = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+ debug(6, "flags1 = %i", flags1);
+
+ if (flags1 < 0) {
+ _vm->logic()->entryObj(0);
+ _finalRoom = -flags1;
+ } else
+ _finalRoom = PREVIOUS_ROOM;
+
+ _anotherCutaway = (flags1 == 1);
+
+ debug(6, "[Cutaway::load] _finalRoom = %i", _finalRoom);
+ debug(6, "[Cutaway::load] _anotherCutaway = %i", _anotherCutaway);
+
+ /*
+ Pointers to other places in the cutaway data
+ */
+
+ _gameStatePtr = _fileData + READ_BE_UINT16(ptr);
+ ptr += 2;
+
+ _nextSentenceOff = READ_BE_UINT16(ptr);
+ ptr += 2;
+
+ uint16 bankNamesOff = READ_BE_UINT16(ptr);
+ ptr += 2;
+
+ _objectData = ptr;
+
+ loadStrings(bankNamesOff);
+
+ if (_bankNames[0][0]) {
+ debug(6, "Loading bank '%s'", _bankNames[0]);
+ _vm->bankMan()->load(_bankNames[0], CUTAWAY_BANK);
+ }
+
+ char entryString[MAX_STRING_SIZE];
+ Talk::getString(_fileData, _nextSentenceOff, entryString, MAX_STRING_LENGTH);
+ debug(6, "Entry string = '%s'", entryString);
+
+ _vm->logic()->joeCutFacing(_vm->logic()->joeFacing());
+ _vm->logic()->joeFace();
+
+ if (entryString[0] == '*' &&
+ entryString[1] == 'F' &&
+ entryString[3] == '\0') {
+ switch (entryString[2]) {
+ case 'L':
+ _vm->logic()->joeCutFacing(DIR_LEFT);
+ break;
+ case 'R':
+ _vm->logic()->joeCutFacing(DIR_RIGHT);
+ break;
+ case 'F':
+ _vm->logic()->joeCutFacing(DIR_FRONT);
+ break;
+ case 'B':
+ _vm->logic()->joeCutFacing(DIR_BACK);
+ break;
+ }
+ }
+
+}
+
+void Cutaway::loadStrings(uint16 offset) {
+ int bankNameCount = READ_BE_UINT16(_fileData + offset);
+ offset += 2;
+
+ debug(6, "Bank name count = %i", bankNameCount);
+
+ /*
+ The _bankNames zero-based array is the one-based BANK_NAMEstr array in
+ the original source code.
+ */
+
+ for (int i = 0, j = 0; i < bankNameCount; i++) {
+ Talk::getString(_fileData, offset, _bankNames[j], MAX_FILENAME_LENGTH);
+ if (_bankNames[j][0]) {
+ debug(6, "Bank name %i = '%s'", j, _bankNames[j]);
+ j++;
+ }
+ }
+
+ debug(6, "Getting talk file");
+ Talk::getString(_fileData, offset, _talkFile, MAX_FILENAME_LENGTH);
+ debug(6, "Talk file = '%s'", _talkFile);
+
+ _talkTo = (int16)READ_BE_INT16(_fileData + offset);
+ debug(6, "_talkTo = %i", _talkTo);
+}
+
+const byte *Cutaway::getCutawayObject(const byte *ptr, CutawayObject &object)
+{
+ const byte *oldPtr = ptr;
+
+ object.objectNumber = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.moveToX = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.moveToY = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.bank = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.animList = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.execute = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.limitBobX1 = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.limitBobY1 = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.limitBobX2 = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.limitBobY2 = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.specialMove = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.animType = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.fromObject = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.bobStartX = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.bobStartY = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.room = (int16)READ_BE_INT16(ptr); ptr += 2;
+ object.scale = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ if ((ptr - oldPtr) != 17*sizeof(int16))
+ error("Wrong number of values read");
+
+ // Make ugly reuse of data less ugly
+ if (object.limitBobX1 < 0) {
+ object.song = -object.limitBobX1;
+ object.limitBobX1 = 0;
+ } else
+ object.song = 0;
+
+ return ptr;
+}
+
+void Cutaway::dumpCutawayObject(int index, CutawayObject &object)
+{
+ debug(6, "----- CutawayObject[%i] -----", index);
+
+ const char *objectNumberStr;
+
+ switch (object.objectNumber) {
+ case -1:
+ objectNumberStr = "MESSAGE";
+ break;
+ case 0:
+ objectNumberStr = "Joe";
+ break;
+ default:
+ if (object.objectNumber > 0)
+ objectNumberStr = _vm->logic()->objectName(ABS(_vm->logic()->objectData(object.objectNumber)->name));
+ else
+ objectNumberStr = "Unknown!";
+ break;
+ }
+
+ debug(6, "objectNumber = %i (%s)", object.objectNumber, objectNumberStr);
+
+ if (object.moveToX) debug(6, "moveToX = %i", object.moveToX);
+ if (object.moveToY) debug(6, "moveToY = %i", object.moveToY);
+ if (object.bank) debug(6, "bank = %i", object.bank);
+ if (object.animList) debug(6, "animList = %i", object.animList);
+ if (object.execute) debug(6, "execute = %i", object.execute);
+ if (object.limitBobX1) debug(6, "limitBobX1 = %i", object.limitBobX1);
+ if (object.limitBobY1) debug(6, "limitBobY1 = %i", object.limitBobY1);
+ if (object.limitBobX2) debug(6, "limitBobX2 = %i", object.limitBobX2);
+ if (object.limitBobY2) debug(6, "limitBobY2 = %i", object.limitBobY2);
+ if (object.specialMove) debug(6, "specialMove = %i", object.specialMove);
+ if (object.animType) debug(6, "animType = %i", object.animType);
+ if (object.fromObject) debug(6, "fromObject = %i", object.fromObject);
+ if (object.bobStartX) debug(6, "bobStartX = %i", object.bobStartX);
+ if (object.bobStartY) debug(6, "bobStartY = %i", object.bobStartY);
+ if (object.room) debug(6, "room = %i", object.room);
+ if (object.scale) debug(6, "scale = %i", object.scale);
+
+}
+
+
+const byte *Cutaway::turnOnPeople(const byte *ptr, CutawayObject &object) {
+ // Lines 1248-1259 in cutaway.c
+ object.personCount = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ if (object.personCount > MAX_PERSON_COUNT)
+ error("[Cutaway::turnOnPeople] object.personCount > MAX_PERSON_COUNT");
+
+ for (int i = 0; i < object.personCount; i++) {
+ object.person[i] = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+ debug(7, "[%i] Turn on person %i", i, object.person[i]);
+ }
+
+ return ptr;
+}
+
+void Cutaway::limitBob(CutawayObject &object) {
+ if (object.limitBobX1) {
+
+ if (object.objectNumber < 0) {
+ warning("QueenCutaway::limitBob called with objectNumber = %i", object.objectNumber);
+ return;
+ }
+
+ BobSlot *bob =
+ _vm->graphics()->bob( _vm->logic()->findBob(object.objectNumber) );
+
+ if (!bob) {
+ warning("Failed to find bob");
+ return;
+ }
+
+ bob->box.x1 = object.limitBobX1;
+ bob->box.y1 = object.limitBobY1;
+ bob->box.x2 = object.limitBobX2;
+ bob->box.y2 = object.limitBobY2;
+ }
+}
+
+void Cutaway::restorePersonData() {
+ for (int i = 0; i < _personDataCount; i++) {
+ int index = _personData[i].index;
+ ObjectData *objectData = _vm->logic()->objectData(index);
+ objectData->name = _personData[i].name;
+ objectData->image = _personData[i].image;
+ }
+}
+
+void Cutaway::changeRooms(CutawayObject &object) {
+ // Lines 1291-1385 in cutaway.c
+
+ debug(6, "Changing from room %i to room %i",
+ _temporaryRoom,
+ object.room);
+
+ restorePersonData();
+ _personDataCount = 0;
+
+ if (_finalRoom != object.room) {
+ int firstObjectInRoom = _vm->logic()->roomData(object.room) + 1;
+ int lastObjectInRoom = _vm->logic()->roomData(object.room) + _vm->grid()->objMax(object.room);
+
+ for (int i = firstObjectInRoom; i <= lastObjectInRoom; i++) {
+ ObjectData *objectData = _vm->logic()->objectData(i);
+
+ if (objectData->image == -3 || objectData->image == -4) {
+
+ assert(_personDataCount < MAX_PERSON_COUNT);
+ // The object is a person! So record the details...
+ _personData[_personDataCount].index = i;
+ _personData[_personDataCount].name = objectData->name;
+ _personData[_personDataCount].image = objectData->image;
+ _personDataCount++;
+
+ // Now, check to see if we need to keep the person on
+ bool on = false;
+ for (int j = 0; j < object.personCount; j++) {
+ if (object.person[j] == i) {
+ on = true;
+ break;
+ }
+ }
+
+ if (on) {
+ // It is needed, so ensure it's ON
+ objectData->name = ABS(objectData->name);
+ } else {
+ // Not needed, so switch off!
+ objectData->name = -ABS(objectData->name);
+ }
+
+ }
+ } // for ()
+ }
+
+ // set coordinates for Joe if he is on screen
+
+ _vm->logic()->joePos(0, 0);
+
+ for (int i = 0; i < object.personCount; i++) {
+ if (PERSON_JOE == object.person[i]) {
+ _vm->logic()->joePos(object.bobStartX, object.bobStartY);
+ }
+ }
+
+ _vm->logic()->oldRoom(_initialRoom);
+
+ // FIXME - the first cutaway is played at the end of the command 0x178. This
+ // command setups some persons and associates bob slots to them. They should be
+ // hidden as their y coordinate is > 150, but they aren't ! A (temporary)
+ // workaround is to display the room with the panel area enabled. Same problem
+ // for cutaway c62c.
+ int16 comPanel = _comPanel;
+ if ((strcmp(_basename, "c41f") == 0 && _temporaryRoom == 106 && object.room == 41) ||
+ (strcmp(_basename, "c62c") == 0 && _temporaryRoom == 105 && object.room == 41)) {
+ comPanel = 1;
+ }
+
+ // FIXME - in the original engine, panel is hidden once the 'head room' is displayed, we
+ // do it before (ie before palette fading)
+ if (object.room == FAYE_HEAD || object.room == AZURA_HEAD || object.room == FRANK_HEAD) {
+ comPanel = 2;
+ }
+
+ RoomDisplayMode mode;
+
+ if (!_vm->logic()->joeX() && !_vm->logic()->joeY()) {
+ mode = RDM_FADE_NOJOE;
+ } else {
+ // We need to display Joe on screen
+ if (_roomFade)
+ mode = RDM_NOFADE_JOE;
+ else
+ mode = RDM_FADE_JOE_XY;
+ }
+
+ _vm->logic()->displayRoom(_vm->logic()->currentRoom(), mode, object.scale, comPanel, true);
+
+ _currentImage = _vm->graphics()->numFrames();
+
+ _temporaryRoom = _vm->logic()->currentRoom();
+
+ restorePersonData();
+}
+
+Cutaway::ObjectType Cutaway::getObjectType(CutawayObject &object) {
+ // Lines 1387-1449 in cutaway.c
+
+ ObjectType objectType = OBJECT_TYPE_ANIMATION;
+
+ if (object.objectNumber > 0) {
+ if (!object.animList) {
+ // No anim frames, so treat as a PERSON, ie. allow to speak/walk
+ ObjectData *objectData = _vm->logic()->objectData(object.objectNumber);
+ if (objectData->image == -3 || objectData->image == -4)
+ objectType = OBJECT_TYPE_PERSON;
+ }
+ } else if (object.objectNumber == OBJECT_JOE) {
+ // It's Joe. See if he's to be treated as a person.
+ if (!object.animList) {
+ // There's no animation list, so Joe must be talking.
+ objectType = OBJECT_TYPE_PERSON;
+ }
+ }
+
+ if (object.fromObject > 0) {
+ /* Copy FROM_OBJECT into OBJECT */
+
+ if (object.objectNumber != object.fromObject) {
+ _vm->logic()->objectCopy(object.fromObject, object.objectNumber);
+ } else {
+ // Same object, so just turn it on!
+ ObjectData *objectData = _vm->logic()->objectData(object.objectNumber);
+ objectData->name = ABS(objectData->name);
+ }
+
+ _vm->graphics()->refreshObject(object.objectNumber);
+
+ // Skip doing any anim stuff
+ objectType = OBJECT_TYPE_NO_ANIMATION;
+ }
+
+ switch (object.objectNumber) {
+ case -2:
+ // Text to be spoken
+ objectType = OBJECT_TYPE_TEXT_SPEAK;
+ break;
+ case -3:
+ // Text to be displayed AND spoken
+ objectType = OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK;
+ break;
+ case -4:
+ // Text to be displayed only (not spoken)
+ objectType = OBJECT_TYPE_TEXT_DISPLAY;
+ break;
+ }
+
+ if (OBJECT_TYPE_ANIMATION == objectType && !object.execute) {
+ // Execute is not on, and it's an object, so ignore any Anims
+ objectType = OBJECT_TYPE_NO_ANIMATION;
+ }
+
+ return objectType;
+}
+
+const byte *Cutaway::getCutawayAnim(const byte *ptr, int header, CutawayAnim &anim) {
+ // lines 1531-1607 in cutaway.c
+
+ //debug(6, "[Cutaway::getCutawayAnim] header=%i", header);
+
+ anim.currentFrame = 0;
+ anim.originalFrame = 0;
+
+ if (-1 == header)
+ header = 0;
+
+ if (0 == header) {
+ anim.object = 0;
+ anim.originalFrame = 31;
+ } else {
+ anim.object = _vm->logic()->findBob(header);
+ anim.originalFrame = _vm->logic()->findFrame(header);
+ }
+
+ anim.unpackFrame = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.speed = ((int16)READ_BE_INT16(ptr)) / 3 + 1;
+ ptr += 2;
+
+ anim.bank = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ if (anim.bank == 0) {
+ anim.bank = 15;
+ } else {
+ if (anim.bank != 13) {
+ _vm->bankMan()->load(_bankNames[anim.bank-1], CUTAWAY_BANK);
+ anim.bank = 8;
+ } else {
+ // Make sure we ref correct JOE bank (7)
+ anim.bank = 7;
+ }
+ }
+
+ anim.mx = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.my = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.cx = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.cy = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ anim.scale = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ if (_vm->resource()->isDemo()) {
+ anim.song = 0;
+ } else {
+ anim.song = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+ }
+
+ // Extract information that depend on the signedness of values
+ if (anim.unpackFrame < 0) {
+ anim.flip = true;
+ anim.unpackFrame = -anim.unpackFrame;
+ } else
+ anim.flip = false;
+
+ return ptr;
+}
+
+void Cutaway::dumpCutawayAnim(CutawayAnim &anim) {
+ debug(6, "----- CutawayAnim -----");
+ if (anim.object) debug(6, "object = %i", anim.object);
+ if (anim.unpackFrame) debug(6, "unpackFrame = %i", anim.unpackFrame);
+ if (anim.speed) debug(6, "speed = %i", anim.speed);
+ if (anim.bank) debug(6, "bank = %i", anim.bank);
+ if (anim.mx) debug(6, "mx = %i", anim.mx);
+ if (anim.my) debug(6, "my = %i", anim.my);
+ if (anim.cx) debug(6, "cx = %i", anim.cx);
+ if (anim.cy) debug(6, "cy = %i", anim.cy);
+ if (anim.scale) debug(6, "scale = %i", anim.scale);
+ if (anim.currentFrame) debug(6, "currentFrame = %i", anim.currentFrame);
+ if (anim.originalFrame) debug(6, "originalFrame = %i", anim.originalFrame);
+ if (anim.song) debug(6, "song = %i", anim.song);
+}
+
+const byte *Cutaway::handleAnimation(const byte *ptr, CutawayObject &object) {
+ // lines 1517-1770 in cutaway.c
+ int frameCount = 0;
+ int header = 0;
+ int i;
+
+ CutawayAnim objAnim[56];
+
+ // Read animation frames
+ for (;;) {
+
+ header = (int16)READ_BE_INT16(ptr);
+ ptr += 2;
+
+ if (-2 == header)
+ break;
+
+ //debug(6, "Animation frame %i, header = %i", frameCount, header);
+
+ if (header > 1000)
+ error("Header too large");
+
+ ptr = getCutawayAnim(ptr, header, objAnim[frameCount]);
+ //dumpCutawayAnim(objAnim[frameCount]);
+
+ frameCount++;
+
+ if (_vm->input()->cutawayQuit())
+ return NULL;
+ }
+
+ if (object.animType == 1) {
+ // lines 1615-1636 in cutaway.c
+
+ debug(6, "----- Complex cutaway animation (animType = %i) -----", object.animType);
+
+ if ((_vm->logic()->currentRoom() == 47 || _vm->logic()->currentRoom() == 63) &&
+ objAnim[0].object == 1) {
+ //CR 2 - 3/3/95, Special harcoded section to make Oracle work...
+ makeComplexAnimation(_vm->graphics()->personFrames(1) - 1, objAnim, frameCount);
+ } else {
+ _currentImage = makeComplexAnimation(_currentImage, objAnim, frameCount);
+ }
+
+ if (object.bobStartX || object.bobStartY) {
+ BobSlot *bob = _vm->graphics()->bob(objAnim[0].object);
+ bob->x = object.bobStartX;
+ bob->y = object.bobStartY;
+ }
+ }
+
+ // Setup the SYNCHRO bob channels
+
+ for (i = 0; i < frameCount; i++) {
+ if (objAnim[i].mx || objAnim[i].my) {
+ BobSlot *bob = _vm->graphics()->bob(objAnim[i].object);
+ bob->frameNum = objAnim[i].originalFrame;
+ bob->move(objAnim[i].mx, objAnim[i].my, (object.specialMove > 0) ? object.specialMove : 4);
+ // Boat room hard coded
+ if (_vm->logic()->currentRoom() == ROOM_TEMPLE_OUTSIDE) {
+ BobSlot *bobJoe = _vm->graphics()->bob(0);
+ if (bobJoe->x < 320) {
+ bobJoe->move(bobJoe->x + 346, bobJoe->y, 4);
+ }
+ }
+ }
+ }
+
+ // Normal cutaway
+
+ if (object.animType != 1) {
+ // lines 1657-1761 in cutaway.c
+
+ debug(6, "----- Normal cutaway animation (animType = %i) -----", object.animType);
+
+ for (i = 0; i < frameCount; i++) {
+ //debug(6, "===== Animating frame %i =====", i);
+ //dumpCutawayAnim(objAnim[i]);
+
+ BobSlot *bob = _vm->graphics()->bob(objAnim[i].object);
+ bob->active = true;
+ if (bob->animating) {
+ bob->animating = false;
+ bob->frameNum = objAnim[i].originalFrame;
+ }
+
+ if (objAnim[i].object < 4)
+ bob->frameNum = 31 + objAnim[i].object;
+
+ if (objAnim[i].unpackFrame == 0) {
+ // Turn off the bob
+ bob->active = false;
+ } else {
+ if (object.animType == 2 || object.animType == 0) {
+ // Unpack animation, but do not unpack moving people
+
+ if (!((objAnim[i].mx > 0 || objAnim[i].my > 0) && inRange(objAnim[i].object, 1, 3))) {
+ _vm->bankMan()->unpack(
+ objAnim[i].unpackFrame,
+ objAnim[i].originalFrame,
+ objAnim[i].bank);
+ }
+
+ if (0 == objAnim[i].object) {
+ // Scale Joe
+ bob->scale = scale(object);
+ }
+ }
+
+ if (objAnim[i].cx || objAnim[i].cy) {
+ bob->x = objAnim[i].cx;
+ bob->y = objAnim[i].cy;
+ }
+
+ // Only flip if we are not moving or it is not a person object
+ if (!(objAnim[i].object > 0 && objAnim[i].object < 4) ||
+ !(objAnim[i].mx || objAnim[i].my) )
+ bob->xflip = objAnim[i].flip;
+
+ // Add frame alteration
+ if (!(objAnim[i].object > 0 && objAnim[i].object < 4)) {
+ bob->frameNum = objAnim[i].originalFrame;
+ }
+
+ int j;
+ for (j = 0; j < objAnim[i].speed; j++)
+ _vm->update();
+ }
+
+ if (_vm->input()->cutawayQuit())
+ return NULL;
+
+ if (objAnim[i].song > 0)
+ _vm->sound()->playSong(objAnim[i].song);
+
+ } // for ()
+ }
+
+ bool moving = true;
+
+ while (moving) {
+ moving = false;
+ _vm->update();
+
+ for (i = 0; i < frameCount; i++) {
+ BobSlot *bob = _vm->graphics()->bob(objAnim[i].object);
+ if (bob->moving) {
+ moving = true;
+ break;
+ }
+ }
+
+ if (_vm->input()->cutawayQuit())
+ return NULL;
+ }
+
+ return ptr;
+}
+
+static void findCdCut(const char *basename, int index, char *result) {
+ strcpy(result, basename);
+ for (int i = strlen(basename); i < 5; i++)
+ result[i] = '_';
+ snprintf(result + 5, 3, "%02i", index);
+}
+
+void Cutaway::handlePersonRecord(
+ int index,
+ CutawayObject &object,
+ const char *sentence) {
+ // Lines 1455-1516 in cutaway.c
+
+ Person p;
+
+ if (object.objectNumber == OBJECT_JOE) {
+ if (object.moveToX || object.moveToY) {
+ _vm->walk()->moveJoe(0, object.moveToX, object.moveToY, true);
+ }
+ } else {
+ _vm->logic()->initPerson(
+ object.objectNumber - _vm->logic()->currentRoomData(),
+ "", true, &p);
+
+ if (object.bobStartX || object.bobStartY) {
+ BobSlot *bob = _vm->graphics()->bob(p.actor->bobNum);
+ bob->scale = scale(object);
+ bob->x = object.bobStartX;
+ bob->y = object.bobStartY;
+ }
+
+ if (object.moveToX || object.moveToY)
+ _vm->walk()->movePerson(
+ &p,
+ object.moveToX, object.moveToY,
+ _currentImage + 1,
+ _vm->logic()->objectData(object.objectNumber)->image
+ );
+ }
+
+ if (_vm->input()->cutawayQuit())
+ return;
+
+ if (0 != strcmp(sentence, "*")) {
+ if (sentence[0] == '#') {
+ debug(4, "Starting credits '%s'", sentence + 1);
+ _vm->logic()->startCredits(sentence + 1);
+ } else {
+ if (object.objectNumber > 0) {
+ bool foundPerson = false;
+
+ for (int i = 1; i <= _personFaceCount; i++) {
+ if (_personFace[i].index == object.objectNumber) {
+ foundPerson = true;
+ break;
+ }
+ }
+
+ if (!foundPerson) {
+ _personFaceCount++;
+ assert(_personFaceCount < MAX_PERSON_FACE_COUNT);
+ _personFace[_personFaceCount].index = object.objectNumber;
+ _personFace[_personFaceCount].image = _vm->logic()->objectData(object.objectNumber)->image;
+ }
+ }
+
+ char voiceFilePrefix[MAX_STRING_SIZE];
+ findCdCut(_basename, index, voiceFilePrefix);
+ _vm->logic()->makePersonSpeak(sentence, (object.objectNumber == OBJECT_JOE) ? NULL : &p, voiceFilePrefix);
+ }
+
+ }
+
+ if (_vm->input()->cutawayQuit())
+ return;
+}
+
+void Cutaway::run(char *nextFilename) {
+ int i;
+ nextFilename[0] = '\0';
+
+ _currentImage = _vm->graphics()->numFrames();
+
+ BobSlot *joeBob = _vm->graphics()->bob(0);
+ int initialJoeX = joeBob->x;
+ int initialJoeY = joeBob->y;
+ debug(6, "[Cutaway::run] Joe started at (%i, %i)", initialJoeX, initialJoeY);
+
+ _vm->input()->cutawayRunning(true);
+
+ _initialRoom = _temporaryRoom = _vm->logic()->currentRoom();
+
+ _vm->display()->screenMode(_comPanel, true);
+
+ if (_comPanel == 0 || _comPanel == 2) {
+ _vm->logic()->sceneStart();
+ }
+
+ memset(_personFace, 0, sizeof(_personFace));
+ _personFaceCount = 0;
+
+ const byte *ptr = _objectData;
+
+ for (i = 0; i < _cutawayObjectCount; i++) {
+ CutawayObject object;
+ ptr = getCutawayObject(ptr, object);
+ //dumpCutawayObject(i, object);
+
+ if (!object.moveToX &&
+ !object.moveToY &&
+ object.specialMove > 0 &&
+ object.objectNumber >= 0) {
+ _vm->logic()->executeSpecialMove(object.specialMove);
+ object.specialMove = 0;
+ }
+
+ if (CURRENT_ROOM == object.room) {
+ // Get current room
+ object.room = _vm->logic()->currentRoom();
+ } else {
+ // Change current room
+ _vm->logic()->currentRoom(object.room);
+ }
+
+ ptr = turnOnPeople(ptr, object);
+
+ limitBob(object);
+
+ char sentence[MAX_STRING_SIZE];
+ Talk::getString(_fileData, _nextSentenceOff, sentence, MAX_STRING_LENGTH);
+
+ if (OBJECT_ROOMFADE == object.objectNumber) {
+ _roomFade = true;
+ object.objectNumber = OBJECT_JOE;
+ } else {
+ _roomFade = false;
+ }
+
+ if (object.room != _temporaryRoom)
+ changeRooms(object);
+
+ ObjectType objectType = getObjectType(object);
+
+ if (object.song)
+ _vm->sound()->playSong(object.song);
+
+ switch (objectType) {
+ case OBJECT_TYPE_ANIMATION:
+ ptr = handleAnimation(ptr, object);
+ break;
+ case OBJECT_TYPE_PERSON:
+ handlePersonRecord(i + 1, object, sentence);
+ break;
+ case OBJECT_TYPE_NO_ANIMATION:
+ // Do nothing?
+ break;
+ case OBJECT_TYPE_TEXT_SPEAK:
+ case OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK:
+ case OBJECT_TYPE_TEXT_DISPLAY:
+ handleText(i + 1, objectType, object, sentence);
+ break;
+ default:
+ warning("Unhandled object type: %i", objectType);
+ break;
+ }
+
+ if (_vm->input()->cutawayQuit())
+ break;
+
+ if (_roomFade) {
+ _vm->update();
+ BobSlot *j = _vm->graphics()->bob(0);
+ _vm->display()->palFadeIn(_vm->logic()->currentRoom(), j->active, j->x, j->y);
+ _roomFade = false;
+ }
+
+ } // for ()
+
+ _vm->display()->clearTexts(0, 198);
+ // XXX lines 1887-1895 in cutaway.c
+
+ stop();
+
+ updateGameState();
+
+ _vm->bankMan()->close(CUTAWAY_BANK);
+
+ talk(nextFilename);
+
+ if (_comPanel == 0 || (_comPanel == 2 && !_anotherCutaway)) {
+ _vm->logic()->sceneStop();
+ _comPanel = 0;
+ }
+
+ if (nextFilename[0] == '\0' && !_anotherCutaway && _vm->logic()->currentRoom() != ROOM_ENDING_CREDITS) {
+ _vm->display()->fullscreen(false);
+
+ // Lines 2138-2182 in cutaway.c
+ if (_finalRoom) {
+ _vm->logic()->newRoom(0);
+ _vm->logic()->entryObj(0);
+ } else {
+ /// No need to stay in current room, so return to previous room
+ // if one exists. Reset Joe's X,Y coords to those when first entered
+
+ restorePersonData();
+
+ debug(6, "_vm->logic()->entryObj() = %i", _vm->logic()->entryObj());
+ if (_vm->logic()->entryObj() > 0) {
+ _initialRoom = _vm->logic()->objectData(_vm->logic()->entryObj())->room;
+ } else {
+ // We're not returning to new room, so return to old Joe X,Y coords
+ debug(6, "[Cutaway::run] Moving joe to (%i, %i)", initialJoeX, initialJoeY);
+ _vm->logic()->joePos(initialJoeX, initialJoeY);
+ }
+
+ if (_vm->logic()->currentRoom() != _initialRoom) {
+ _vm->logic()->currentRoom(_initialRoom);
+ _vm->logic()->changeRoom();
+ if (_vm->logic()->currentRoom() == _vm->logic()->newRoom()) {
+ _vm->logic()->newRoom(0);
+ }
+ }
+ _vm->logic()->joePos(0, 0);
+ }
+
+ _vm->logic()->joeCutFacing(0);
+ _comPanel = 0;
+
+ int k = 0;
+ for (i = _vm->logic()->roomData(_vm->logic()->currentRoom());
+ i <= _vm->logic()->roomData(_vm->logic()->currentRoom() + 1); i++) {
+
+ ObjectData *object = _vm->logic()->objectData(i);
+ if (object->image == -3 || object->image == -4) {
+ k++;
+ if (object->name > 0) {
+ _vm->graphics()->resetPersonAnim(k);
+ }
+ }
+ }
+
+ _vm->logic()->removeHotelItemsFromInventory();
+ }
+
+ joeBob->animating = 0;
+ joeBob->moving = 0;
+
+ // if the cutaway has been cancelled, we must stop the speech and the sfx as well
+ if (_vm->input()->cutawayQuit()) {
+ if (_vm->sound()->isSpeechActive())
+ _vm->sound()->stopSpeech();
+ _vm->sound()->stopSfx();
+ }
+
+ _vm->input()->cutawayRunning(false);
+ _vm->input()->cutawayQuitReset();
+ _vm->input()->quickSaveReset();
+ _vm->input()->quickLoadReset();
+
+ if (_songBeforeComic > 0)
+ _vm->sound()->playSong(_songBeforeComic);
+ else if (_lastSong > 0)
+ _vm->sound()->playSong(_lastSong);
+}
+
+void Cutaway::stop() {
+ // Lines 1901-2032 in cutaway.c
+ byte *ptr = _gameStatePtr;
+
+ // Skipping GAMESTATE data
+ int gameStateCount = (int16)READ_BE_INT16(ptr); ptr += 2;
+ if (gameStateCount > 0)
+ ptr += (gameStateCount * 12);
+
+ // Get the final room and Joe's final position
+
+ int16 joeRoom = READ_BE_UINT16(ptr); ptr += 2;
+ int16 joeX = READ_BE_UINT16(ptr); ptr += 2;
+ int16 joeY = READ_BE_UINT16(ptr); ptr += 2;
+
+ debug(6, "[Cutaway::stop] Final position is room %i and coordinates (%i, %i)",
+ joeRoom, joeX, joeY);
+
+ if ((!_vm->input()->cutawayQuit() || (!_anotherCutaway && joeRoom == _finalRoom)) &&
+ joeRoom != _temporaryRoom &&
+ joeRoom != 0) {
+
+ debug(6, "[Cutaway::stop] Changing rooms and moving Joe");
+
+ _vm->logic()->joePos(joeX, joeY);
+ _vm->logic()->currentRoom(joeRoom);
+ _vm->logic()->oldRoom(_initialRoom);
+ _vm->logic()->displayRoom(_vm->logic()->currentRoom(), RDM_FADE_JOE_XY, 0, _comPanel, true);
+ }
+
+ if (_vm->input()->cutawayQuit()) {
+ // Lines 1927-2032 in cutaway.c
+ int i;
+
+ // Stop the credits from running
+ _vm->logic()->stopCredits();
+
+ _vm->graphics()->stopBobs();
+
+ for (i = 1; i <= _personFaceCount; i++) {
+ int index = _personFace[i].index;
+ if (index > 0) {
+ _vm->logic()->objectData(_personFace[i].index)->image = _personFace[i].image;
+
+ _vm->graphics()->bob(_vm->logic()->findBob(index))->xflip =
+ (_personFace[i].image != -4);
+ }
+ }
+
+ int quitObjectCount = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ for (i = 0; i < quitObjectCount; i++) {
+ int16 objectIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 fromIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 x = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 y = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 room = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 frame = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 bank = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ int bobIndex = _vm->logic()->findBob(objectIndex);
+ ObjectData *object = _vm->logic()->objectData(objectIndex);
+
+ if (fromIndex > 0) {
+ if (fromIndex == objectIndex) {
+ // Enable object
+ object->name = ABS(object->name);
+ } else {
+ _vm->logic()->objectCopy(fromIndex, objectIndex);
+
+ ObjectData *from = _vm->logic()->objectData(fromIndex);
+ if (object->image && !from->image && bobIndex && _vm->logic()->currentRoom() == object->room)
+ _vm->graphics()->bob(bobIndex)->clear();
+ }
+
+ if (_vm->logic()->currentRoom() == room)
+ _vm->graphics()->refreshObject(objectIndex);
+ }
+
+ if (_vm->logic()->currentRoom() == object->room) {
+ BobSlot *pbs = _vm->graphics()->bob(bobIndex);
+
+ if (x || y) {
+ pbs->x = x;
+ pbs->y = y;
+ if (inRange(object->image, -4, -3))
+ pbs->scale = _vm->grid()->findScale(x, y);
+ }
+
+ if (frame) {
+ if (0 == bank)
+ bank = 15;
+ else if (bank != 13) {
+ _vm->bankMan()->load(_bankNames[bank-1], CUTAWAY_BANK);
+ bank = 8;
+ }
+
+ int objectFrame = _vm->logic()->findFrame(objectIndex);
+
+ if (objectFrame == 1000) {
+ _vm->graphics()->bob(bobIndex)->clear();
+ } else if (objectFrame) {
+ _vm->bankMan()->unpack(ABS(frame), objectFrame, bank);
+ pbs->frameNum = objectFrame;
+ if (frame < 0)
+ pbs->xflip = true;
+
+ }
+ }
+ }
+ } // for ()
+
+ int16 specialMove = (int16)READ_BE_INT16(ptr); ptr += 2;
+ if (specialMove > 0)
+ _vm->logic()->executeSpecialMove(specialMove);
+
+ _lastSong = (int16)READ_BE_INT16(ptr); ptr += 2;
+ }
+
+ if (joeRoom == _temporaryRoom &&
+ joeRoom != 37 && joeRoom != 105 && joeRoom != 106 &&
+ (joeX || joeY)) {
+ BobSlot *joeBob = _vm->graphics()->bob(0);
+
+ debug(6, "[Cutaway::stop] Moving Joe");
+
+ joeBob->x = joeX;
+ joeBob->y = joeY;
+ _vm->logic()->joeScale(_vm->grid()->findScale(joeX, joeY));
+ _vm->logic()->joeFace();
+ }
+}
+
+void Cutaway::updateGameState() {
+ // Lines 2047-2115 in cutaway.c
+ byte *ptr = _gameStatePtr;
+
+ int gameStateCount = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ for (int i = 0; i < gameStateCount; i++) {
+ int16 stateIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 stateValue = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 objectIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 areaIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 areaSubIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ int16 fromObject = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ bool update = false;
+
+ if (stateIndex > 0) {
+ if (_vm->logic()->gameState(stateIndex) == stateValue)
+ update = true;
+ } else {
+ _vm->logic()->gameState(ABS(stateIndex), stateValue);
+ update = true;
+ }
+
+ if (update) {
+
+ if (objectIndex > 0) { // Show the object
+ ObjectData *objectData = _vm->logic()->objectData(objectIndex);
+ objectData->name = ABS(objectData->name);
+ if (fromObject > 0)
+ _vm->logic()->objectCopy(fromObject, objectIndex);
+ _vm->graphics()->refreshObject(objectIndex);
+ } else if (objectIndex < 0) { // Hide the object
+ objectIndex = -objectIndex;
+ ObjectData *objectData = _vm->logic()->objectData(objectIndex);
+ objectData->name = -ABS(objectData->name);
+ _vm->graphics()->refreshObject(objectIndex);
+ }
+
+ if (areaIndex > 0) {
+
+ // Turn area on or off
+
+ if (areaSubIndex > 0) {
+ Area *area = _vm->grid()->area(areaIndex, areaSubIndex);
+ area->mapNeighbours = ABS(area->mapNeighbours);
+ } else {
+ Area *area = _vm->grid()->area(areaIndex, ABS(areaSubIndex));
+ area->mapNeighbours = -ABS(area->mapNeighbours);
+ }
+ }
+
+ }
+ } // for ()
+}
+
+void Cutaway::talk(char *nextFilename) {
+ const char *p = strrchr(_talkFile, '.');
+ if (p && 0 == scumm_stricmp(p, ".dog")) {
+ nextFilename[0] = '\0';
+ assert(_talkTo > 0);
+ int personInRoom = _talkTo - _vm->logic()->roomData(_vm->logic()->currentRoom());
+ _vm->logic()->startDialogue(_talkFile, personInRoom, nextFilename);
+ }
+}
+
+int Cutaway::makeComplexAnimation(int16 currentImage, Cutaway::CutawayAnim *objAnim, int frameCount) {
+ int frameIndex[256];
+ int i;
+ assert(frameCount < 30);
+ AnimFrame cutAnim[30];
+
+ memset(frameIndex, 0, sizeof(frameIndex));
+ debug(6, "[Cutaway::makeComplexAnimation] currentImage = %i", currentImage);
+
+ for (i = 0; i < frameCount; i++) {
+ cutAnim[i].frame = objAnim[i].unpackFrame;
+ cutAnim[i].speed = objAnim[i].speed;
+ frameIndex[objAnim[i].unpackFrame] = 1;
+ }
+
+ cutAnim[frameCount].frame = 0;
+ cutAnim[frameCount].speed = 0;
+
+ int nextFrameIndex = 1;
+
+ for (i = 1; i < 256; i++)
+ if (frameIndex[i])
+ frameIndex[i] = nextFrameIndex++;
+
+ for (i = 0; i < frameCount; i++) {
+ cutAnim[i].frame = currentImage + frameIndex[objAnim[i].unpackFrame];
+ }
+
+ for (i = 1; i < 256; i++) {
+ if (frameIndex[i]) {
+ currentImage++;
+ _vm->bankMan()->unpack(i, currentImage, objAnim[0].bank);
+ }
+ }
+
+ _vm->graphics()->setBobCutawayAnim(objAnim[0].object, objAnim[0].flip, cutAnim, frameCount + 1);
+ return currentImage;
+}
+
+void Cutaway::handleText(
+ int index,
+ ObjectType type,
+ CutawayObject &object,
+ const char *sentence) {
+ // lines 1776-1863 in cutaway.c
+
+ int spaces = countSpaces(type, sentence);
+
+ int x;
+ int flags;
+
+ if (OBJECT_TYPE_TEXT_DISPLAY == type) {
+ x = _vm->display()->textCenterX(sentence);
+ flags = 2;
+ } else {
+ x = object.bobStartX;
+ flags = 1;
+ }
+
+ BobSlot *bob =
+ _vm->graphics()->bob( _vm->logic()->findBob(ABS(object.objectNumber)) );
+
+ _vm->graphics()->setBobText(bob, sentence, x, object.bobStartY, object.specialMove, flags);
+
+ if (OBJECT_TYPE_TEXT_SPEAK == type || OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK == type) {
+ if (_vm->sound()->speechOn()) {
+ char voiceFileName[MAX_STRING_SIZE];
+ findCdCut(_basename, index, voiceFileName);
+ strcat(voiceFileName, "1");
+ _vm->sound()->playSfx(voiceFileName, true);
+ }
+
+ if (OBJECT_TYPE_TEXT_SPEAK == type && _vm->sound()->speechOn() && !_vm->subtitles())
+ _vm->display()->clearTexts(0, 150);
+ }
+
+ while (1) {
+ _vm->update();
+
+ if (_vm->input()->cutawayQuit())
+ return;
+
+ if (_vm->input()->keyVerb() == VERB_SKIP_TEXT) {
+ _vm->input()->clearKeyVerb();
+ break;
+ }
+
+ if ((OBJECT_TYPE_TEXT_SPEAK == type || OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK == type) && _vm->sound()->speechOn() && _vm->sound()->speechSfxExists()) {
+ if (!_vm->sound()->isSpeechActive()) {
+ break;
+ }
+ } else {
+ --spaces;
+ if (spaces <= 0) {
+ break;
+ }
+ }
+ }
+
+ _vm->display()->clearTexts(0, 198);
+ _vm->update();
+}
+
+int Cutaway::countSpaces(ObjectType type, const char *segment) {
+ int tmp = 0;
+
+ while (*segment++)
+ tmp++;
+
+ if (tmp < 50)
+ tmp = 50;
+
+ if (OBJECT_TYPE_TEXT_DISPLAY == type)
+ tmp *= 3;
+
+ return (tmp * 2) / (_vm->talkSpeed() / 3);
+
+}
+
+int Cutaway::scale(CutawayObject &object) {
+ int scaling = 100;
+
+ if (object.scale > 0)
+ scaling = object.scale;
+ else if (!object.objectNumber) {
+ // Only scale Joe
+ int x, y;
+
+ if (object.bobStartX > 0 || object.bobStartY > 0) {
+ x = object.bobStartX;
+ y = object.bobStartY;
+ } else {
+ BobSlot *bob = _vm->graphics()->bob(0);
+ x = bob->x;
+ y = bob->y;
+ }
+
+ int zone = _vm->grid()->findAreaForPos(GS_ROOM, x, y);
+ if (zone > 0) {
+ Area *area = _vm->grid()->area(_vm->logic()->currentRoom(), zone);
+ scaling = area->calcScale(y);
+ }
+ }
+
+ return scaling;
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/cutaway.h b/engines/queen/cutaway.h
new file mode 100644
index 0000000000..bbf5ec0652
--- /dev/null
+++ b/engines/queen/cutaway.h
@@ -0,0 +1,265 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENCUTAWAY_H
+#define QUEENCUTAWAY_H
+
+#include "common/util.h"
+#include "queen/structs.h"
+
+namespace Queen {
+
+class QueenEngine;
+
+class Cutaway {
+public:
+
+ //! Public interface to run a cutaway from a file
+ static void run(const char *filename, char *nextFilename, QueenEngine *vm);
+
+ //! Collection of constants used by QueenCutaway
+ enum {
+ PREVIOUS_ROOM = 0,
+ CURRENT_ROOM = 0,
+ OBJECT_ROOMFADE = -1,
+ PERSON_JOE = -1,
+ OBJECT_JOE = 0,
+ MAX_PERSON_COUNT = 6,
+ CUTAWAY_BANK = 8,
+ MAX_BANK_NAME_COUNT = 5,
+ MAX_FILENAME_LENGTH = 12,
+ MAX_FILENAME_SIZE = (MAX_FILENAME_LENGTH + 1),
+ MAX_PERSON_FACE_COUNT = 13,
+ MAX_STRING_LENGTH = 255,
+ MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1),
+ LEFT = 1,
+ RIGHT = 2,
+ FRONT = 3,
+ BACK = 4
+ };
+
+ //! Different kinds of cutaway objects
+ enum ObjectType {
+ OBJECT_TYPE_ANIMATION = 0,
+ OBJECT_TYPE_PERSON = 1,
+ OBJECT_TYPE_NO_ANIMATION = 2,
+ OBJECT_TYPE_TEXT_SPEAK = 3,
+ OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK = 4,
+ OBJECT_TYPE_TEXT_DISPLAY = 5
+ };
+
+private:
+ //! Data for a cutaway object
+ struct CutawayObject {
+ int16 objectNumber; // 0 = JOE, -1 = MESSAGE
+ int16 moveToX;
+ int16 moveToY;
+ int16 bank; // 0 = PBOB, 13 = Joe Bank, else BANK NAMEstr()
+ int16 animList;
+ int16 execute; // 1 Yes, 0 No
+ int16 limitBobX1;
+ int16 limitBobY1;
+ int16 limitBobX2;
+ int16 limitBobY2;
+ int16 specialMove;
+ int16 animType; // 0 - Packet, 1 - Amal, 2 - Unpack
+ int16 fromObject;
+ int16 bobStartX;
+ int16 bobStartY;
+ int16 room;
+ int16 scale;
+ // Variables derived from the variables above
+ int song;
+
+ //! People to turn on
+ int person[MAX_PERSON_COUNT];
+
+ //! Number of elements used in _person array
+ int personCount;
+ };
+
+ struct CutawayAnim {
+ int16 object;
+ int16 unpackFrame; // Frame to unpack
+ int16 speed;
+ int16 bank;
+ int16 mx;
+ int16 my;
+ int16 cx;
+ int16 cy;
+ int16 scale;
+ int16 currentFrame; // Index to Current Frame
+ int16 originalFrame; // Index to Original Object Frame
+ int16 song;
+ bool flip; // set this if unpackFrame is negative
+ };
+
+ struct ObjectDataBackup {
+ int index;
+ int16 name;
+ int16 image;
+ };
+
+ struct PersonFace {
+ int16 index;
+ int16 image;
+ };
+
+ QueenEngine *_vm;
+
+ //! Raw .cut file data (without 20 byte header)
+ byte *_fileData;
+
+ //! COMPANEL
+ int16 _comPanel;
+
+ //! Game state data inside of _fileDat
+ byte *_gameStatePtr;
+
+ //! Actual cutaway data inside of _fileData
+ byte *_objectData;
+
+ //! Pointer to next sentence string in _fileData
+ uint16 _nextSentenceOff;
+
+ //! ???
+ bool _roomFade;
+
+ //! Number of cutaway objects at _cutawayData
+ int16 _cutawayObjectCount;
+
+ //! This cutaway is followed by another
+ bool _anotherCutaway;
+
+ //! Room before cutaway
+ int _initialRoom;
+
+ //! Temporary room for cutaway
+ int _temporaryRoom;
+
+ //! Room to stay in
+ int _finalRoom;
+
+ //! Bank names
+ char _bankNames[MAX_BANK_NAME_COUNT][MAX_FILENAME_SIZE];
+
+ //! Filename without ".cut"
+ char _basename[MAX_FILENAME_SIZE];
+
+ //! Name of .dog file
+ char _talkFile[MAX_FILENAME_SIZE];
+
+ //! Person to talk to
+ int16 _talkTo;
+
+ //! Used by changeRooms
+ ObjectDataBackup _personData[MAX_PERSON_COUNT];
+
+ //! Number of elements used in _personData array
+ int _personDataCount;
+
+ //! Used by handlePersonRecord()
+ PersonFace _personFace[MAX_PERSON_FACE_COUNT];
+
+ //! Number of entries in _personFace array
+ int _personFaceCount;
+
+ //! Play this song when leaving cutaway
+ int16 _lastSong;
+
+ //! Song played before running comic.cut
+ int16 _songBeforeComic;
+
+ int16 _currentImage;
+
+ Cutaway(const char *filename, QueenEngine *vm);
+ ~Cutaway();
+
+ //! Run this cutaway object
+ void run(char *nextFilename);
+
+ //! Load cutaway data from file
+ void load(const char *filename);
+
+ //! Used by load to read string data
+ void loadStrings(uint16 offset);
+
+ //! Get persons
+ const byte *turnOnPeople(const byte *ptr, CutawayObject &object);
+
+ //! Limit the BOB
+ void limitBob(CutawayObject &object);
+
+ //! This cutaway object occurs in another room
+ void changeRooms(CutawayObject &object);
+
+ //! Get the object type for this CutawayObject
+ ObjectType getObjectType(CutawayObject &object);
+
+ //! Perform actions for an animation
+ const byte *handleAnimation(const byte *ptr, CutawayObject &object);
+
+ //! Perform actions for a person record
+ void handlePersonRecord(int index, CutawayObject &object, const char *sentence);
+
+ //! Perform text actions
+ void handleText(int index, ObjectType type, CutawayObject &object, const char *sentence);
+
+ //! Restore Logic::_objectData from _personData
+ void restorePersonData();
+
+ //! Stop the cutaway
+ void stop();
+
+ //! Update game state after cutaway
+ void updateGameState();
+
+ //! Prepare for talk after cutaway
+ void talk(char *nextFilename);
+
+ //! Get CutawayAnim data from ptr and return new ptr
+ const byte *getCutawayAnim(const byte *ptr, int header, CutawayAnim &anim);
+
+ //! Special animation
+ int makeComplexAnimation(int16 currentImage, CutawayAnim *objAnim, int frameCount);
+
+ //! Read a CutawayObject from ptr and return new ptr
+ static const byte *getCutawayObject(const byte *ptr, CutawayObject &object);
+
+ //! Dump a CutawayObject with debug()
+ void dumpCutawayObject(int index, CutawayObject &object);
+
+ //! Used by handleText()
+ int countSpaces(ObjectType type, const char *segment);
+
+ //! Scale Joe
+ int scale(CutawayObject &object);
+
+ //! Dump CutawayAnum data with debug()
+ static void dumpCutawayAnim(CutawayAnim &anim);
+
+ bool inRange(int16 x, int16 l, int16 h) const { return (x <= h && x >= l); }
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/debug.cpp b/engines/queen/debug.cpp
new file mode 100644
index 0000000000..63fc40f444
--- /dev/null
+++ b/engines/queen/debug.cpp
@@ -0,0 +1,219 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/debug.h"
+
+#include "queen/graphics.h"
+#include "queen/logic.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+
+#include "common/debugger.cpp"
+
+namespace Queen {
+
+Debugger::Debugger(QueenEngine *vm)
+ : _vm(vm), _flags(0) {
+
+ DCmd_Register("exit", &Debugger::Cmd_Exit);
+ DCmd_Register("help", &Debugger::Cmd_Help);
+ DCmd_Register("areas", &Debugger::Cmd_Areas);
+ DCmd_Register("asm", &Debugger::Cmd_Asm);
+ DCmd_Register("bob", &Debugger::Cmd_Bob);
+ DCmd_Register("bobs", &Debugger::Cmd_PrintBobs);
+ DCmd_Register("gs", &Debugger::Cmd_GameState);
+ DCmd_Register("info", &Debugger::Cmd_Info);
+ DCmd_Register("items", &Debugger::Cmd_Items);
+ DCmd_Register("room", &Debugger::Cmd_Room);
+ DCmd_Register("song", &Debugger::Cmd_Song);
+}
+
+Debugger::~Debugger() {} // we need this here for __SYMBIAN32__
+void Debugger::preEnter() {
+}
+
+void Debugger::postEnter() {
+ _vm->graphics()->setupMouseCursor();
+}
+
+bool Debugger::Cmd_Exit(int argc, const char **argv) {
+ _detach_now = true;
+ return false;
+}
+
+bool Debugger::Cmd_Help(int argc, const char **argv) {
+ // console normally has 39 line width
+ // wrap around nicely
+ int width = 0, size, i;
+
+ DebugPrintf("Commands are:\n");
+ for (i = 0 ; i < _dcmd_count ; i++) {
+ size = strlen(_dcmds[i].name) + 1;
+
+ if ((width + size) >= 39) {
+ DebugPrintf("\n");
+ width = size;
+ } else
+ width += size;
+
+ DebugPrintf("%s ", _dcmds[i].name);
+ }
+ DebugPrintf("\n");
+ return true;
+}
+
+bool Debugger::Cmd_Asm(int argc, const char **argv) {
+ if (argc == 2) {
+ uint16 sm = atoi(argv[1]);
+ _vm->logic()->executeSpecialMove(sm);
+ return false;
+ } else {
+ DebugPrintf("Usage: %s smnum\n", argv[0]);
+ }
+ return true;
+}
+
+bool Debugger::Cmd_Areas(int argc, const char **argv) {
+ _flags ^= DF_DRAW_AREAS;
+ DebugPrintf("Room areas display %s\n", (_flags & DF_DRAW_AREAS) != 0 ? "on" : "off");
+ return true;
+}
+
+bool Debugger::Cmd_Bob(int argc, const char **argv) {
+ if (argc >= 3) {
+ int bobNum = atoi(argv[1]);
+ if (bobNum >= Graphics::MAX_BOBS_NUMBER) {
+ DebugPrintf("Bob %d is out of range (range: 0 - %d)\n", bobNum, Graphics::MAX_BOBS_NUMBER);
+ } else {
+ int param = (argc > 3) ? atoi(argv[3]) : 0;
+ BobSlot *bob = _vm->graphics()->bob(bobNum);
+ if (!strcmp(argv[2], "toggle")) {
+ bob->active = !bob->active;
+ DebugPrintf("bob[%d].active = %d\n", bobNum, bob->active);
+ } else if (!strcmp(argv[2], "x")) {
+ bob->x = param;
+ DebugPrintf("bob[%d].x = %d\n", bobNum, bob->x);
+ } else if (!strcmp(argv[2], "y")) {
+ bob->y = param;
+ DebugPrintf("bob[%d].y = %d\n", bobNum, bob->y);
+ } else if (!strcmp(argv[2], "frame")) {
+ bob->frameNum = param;
+ DebugPrintf("bob[%d].frameNum = %d\n", bobNum, bob->frameNum);
+ } else if (!strcmp(argv[2], "speed")) {
+ bob->speed = param;
+ DebugPrintf("bob[%d].speed = %d\n", bobNum, bob->speed);
+ } else {
+ DebugPrintf("Unknown bob command '%s'\n", argv[2]);
+ }
+ }
+ } else {
+ DebugPrintf("Usage: %s bobnum command parameter\n", argv[0]);
+ }
+ return true;
+}
+
+bool Debugger::Cmd_GameState(int argc, const char **argv) {
+ uint16 slot;
+ switch (argc) {
+ case 2:
+ slot = atoi(argv[1]);
+ DebugPrintf("GAMESTATE[%d] ", slot);
+ DebugPrintf("is %d\n", _vm->logic()->gameState(slot));
+ break;
+ case 3:
+ slot = atoi(argv[1]);
+ DebugPrintf("GAMESTATE[%d] ", slot);
+ DebugPrintf("was %d ", _vm->logic()->gameState(slot));
+ _vm->logic()->gameState(slot, atoi(argv[2]));
+ DebugPrintf("now %d\n", _vm->logic()->gameState(slot));
+ break;
+ default:
+ DebugPrintf("Usage: %s slotnum value\n", argv[0]);
+ break;
+ }
+ return true;
+}
+
+bool Debugger::Cmd_Info(int argc, const char **argv) {
+ DebugPrintf("Version: %s\n", _vm->resource()->JASVersion());
+ DebugPrintf("Audio compression: %d\n", _vm->resource()->compression());
+ return true;
+}
+
+bool Debugger::Cmd_Items(int argc, const char **argv) {
+ int n = _vm->logic()->itemDataCount();
+ ItemData *item = _vm->logic()->itemData(1);
+ while (n--) {
+ item->name = ABS(item->name);
+ ++item;
+ }
+ DebugPrintf("Enabled all inventory items\n");
+ return true;
+}
+
+bool Debugger::Cmd_PrintBobs(int argc, const char**argv) {
+ int i;
+ BobSlot *bob = _vm->graphics()->bob(0);
+ DebugPrintf("+--------------------------------+\n");
+ DebugPrintf("|# | x| y|f|scl|frm|a|m| ex| ey|\n");
+ DebugPrintf("+--+---+---+-+---+---+-+-+---+---+\n");
+ for (i = 0; i < Graphics::MAX_BOBS_NUMBER; ++i, ++bob) {
+ if (bob->active) {
+ DebugPrintf("|%2d|%3d|%3d|%1d|%3d|%3d|%1d|%1d|%3d|%3d|\n",
+ i, bob->x, bob->y, bob->xflip, bob->scale, bob->frameNum,
+ bob->animating, bob->moving, bob->speed, bob->endx, bob->endy);
+ }
+ }
+ DebugPrintf("+--------------------------------+\n");
+ return true;
+}
+
+bool Debugger::Cmd_Room(int argc, const char **argv) {
+ if (argc == 2) {
+ uint16 roomNum = atoi(argv[1]);
+ _vm->logic()->joePos(0, 0);
+ _vm->logic()->newRoom(roomNum);
+ _vm->logic()->entryObj(_vm->logic()->roomData(roomNum) + 1);
+ return false;
+ } else {
+ DebugPrintf("Current room: %d (%s), use '%s <roomnum>' to switch\n",
+ _vm->logic()->currentRoom(),
+ _vm->logic()->roomName(_vm->logic()->currentRoom()),
+ argv[0]);
+ }
+ return true;
+}
+
+bool Debugger::Cmd_Song(int argc, const char **argv) {
+ if (argc == 2) {
+ int16 songNum = atoi(argv[1]);
+ _vm->sound()->playSong(songNum);
+ DebugPrintf("Playing song %d\n", songNum);
+ } else {
+ DebugPrintf("Usage: %s songnum\n", argv[0]);
+ }
+ return true;
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/debug.h b/engines/queen/debug.h
new file mode 100644
index 0000000000..40cb8bb6ff
--- /dev/null
+++ b/engines/queen/debug.h
@@ -0,0 +1,69 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENDEBUG_H
+#define QUEENDEBUG_H
+
+#include "common/debugger.h"
+
+namespace Queen {
+
+class QueenEngine;
+
+class Debugger : public Common::Debugger<Debugger> {
+public:
+
+ Debugger(QueenEngine *vm);
+ virtual ~Debugger(); // we need this here for __SYMBIAN32__ archaic gcc/UIQ
+
+ int flags() const { return _flags; }
+
+ enum {
+ DF_DRAW_AREAS = 1 << 0
+ };
+
+protected:
+
+ virtual void preEnter();
+ virtual void postEnter();
+
+ bool Cmd_Exit(int argc, const char **argv);
+ bool Cmd_Help(int argc, const char **argv);
+ bool Cmd_Areas(int argc, const char **argv);
+ bool Cmd_Asm(int argc, const char **argv);
+ bool Cmd_Bob(int argc, const char **argv);
+ bool Cmd_GameState(int argc, const char **argv);
+ bool Cmd_Info(int argc, const char **argv);
+ bool Cmd_Items(int argc, const char **argv);
+ bool Cmd_PrintBobs(int argc, const char **argv);
+ bool Cmd_Room(int argc, const char **argv);
+ bool Cmd_Song(int argc, const char **argv);
+
+private:
+
+ QueenEngine *_vm;
+ int _flags;
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/defs.h b/engines/queen/defs.h
new file mode 100644
index 0000000000..223e539894
--- /dev/null
+++ b/engines/queen/defs.h
@@ -0,0 +1,322 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENDEFS_H
+#define QUEENDEFS_H
+
+namespace Queen {
+
+#define SAVEGAME_SIZE 24622
+
+enum {
+ COMPRESSION_NONE = 0,
+ COMPRESSION_MP3 = 1,
+ COMPRESSION_OGG = 2,
+ COMPRESSION_FLAC = 3
+};
+
+enum {
+ GAME_SCREEN_WIDTH = 320,
+ GAME_SCREEN_HEIGHT = 200,
+ ROOM_ZONE_HEIGHT = 150,
+ PANEL_ZONE_HEIGHT = 50
+};
+
+
+enum {
+ FRAMES_JOE = 38,
+ FRAMES_JOURNAL = 40
+};
+
+
+enum Direction {
+ DIR_LEFT = 1,
+ DIR_RIGHT = 2,
+ DIR_FRONT = 3,
+ DIR_BACK = 4
+};
+
+
+enum {
+ INK_BG_PANEL = 226,
+ INK_JOURNAL = 248,
+ INK_PINNACLE_ROOM = 243,
+ INK_CMD_SELECT = 255,
+ INK_CMD_NORMAL = 225,
+ INK_CMD_LOCK = 234,
+ INK_TALK_NORMAL = 7,
+ INK_JOE = 14,
+ INK_OUTLINED_TEXT = 16
+};
+
+
+enum {
+ ITEM_NONE = 0,
+ ITEM_BAT,
+ ITEM_JOURNAL,
+ ITEM_KNIFE,
+ ITEM_COCONUT_HALVES,
+ ITEM_BEEF_JERKY,
+ ITEM_PROPELLER,
+ ITEM_BANANA,
+ ITEM_VINE,
+ ITEM_SLOTH_HAIR,
+ ITEM_COMIC_BOOK,
+ ITEM_FLOWER,
+ ITEM_BEETLE,
+ ITEM_ORCHID,
+ ITEM_DICTIONARY,
+ ITEM_DEATH_MASH,
+ ITEM_PERFUME,
+ ITEM_TYRANNO_HORN,
+ ITEM_LOTION,
+ ITEM_RECORD,
+ ITEM_VACUUM_CLEANER,
+ ITEM_NET,
+ ITEM_ALCOHOL,
+ ITEM_ROCKET_PACK,
+ ITEM_SOME_MONEY,
+ ITEM_CHEESE_BITZ,
+ ITEM_DOG_FOOD,
+ ITEM_CAN_OPENER,
+ ITEM_LETTER,
+ ITEM_SQUEAKY_TOY,
+ ITEM_KEY,
+ ITEM_BOOK,
+ ITEM_PIECE_OF_PAPER,
+ ITEM_ROCKET_PLAN,
+ ITEM_PADLOCK_KEY,
+ ITEM_RIB_CAGE,
+ ITEM_SKULL,
+ ITEM_LEG_BONE,
+ ITEM_BAT2,
+ ITEM_MAKESHIFT_TOCH,
+ ITEM_LIGHTER,
+ ITEM_GREEN_JEWEL,
+ ITEM_PICK,
+ ITEM_STONE_KEY,
+ ITEM_BLUE_JEWEL,
+ ITEM_CRYSTAL_SKULL,
+ ITEM_TREE_SAP,
+ ITEM_DINO_RAY_GUN,
+ ITEM_BRANCHES,
+ ITEM_WIG,
+ ITEM_TOWEL,
+ ITEM_OTHER_SHEET,
+ ITEM_SHEET,
+ ITEM_SHEET_ROPE,
+ ITEM_CROWBAR,
+ ITEM_COMEDY_BREASTS,
+ ITEM_DRESS,
+ ITEM_KEY2,
+ ITEM_CLOTHES,
+ ITEM_HAY,
+ ITEM_OIL,
+ ITEM_CHICKEN,
+ ITEM_LIT_TORCH,
+ ITEM_OPENED_DOG_FOOD,
+ ITEM_SOME_MONEY2,
+ ITEM_SOME_MORE_MONEY,
+ ITEM_PEELED_BANANA,
+ ITEM_STONE_DISC,
+ ITEM_GNARLED_VINE,
+ ITEM_FLINT,
+ ITEM_LIGHTER2,
+ ITEM_REST_OF_BEEF_JERKY,
+ ITEM_LOTS_OF_MONEY,
+ ITEM_HEAPS_OF_MONEY,
+ ITEM_OPEN_BOOK,
+ ITEM_REST_OF_THE_CHEESE_BITZ,
+ ITEM_SCISSORS,
+ ITEM_PENCIL,
+ ITEM_SUPER_WEENIE_SERUM,
+ ITEM_MUMMY_WRAPPINGS,
+ ITEM_COCONUT,
+ ITEM_ID_CARD,
+ ITEM_BIT_OF_STONE,
+ ITEM_CHUNK_OF_ROCK,
+ ITEM_BIG_STICK,
+ ITEM_STICKY_BIT_OF_STONE,
+ ITEM_STICKY_CHUNK_OF_ROCK,
+ ITEM_DEATH_MASK2,
+ ITEM_CHEFS_SURPRISE,
+ ITEM_STICKY_BAT,
+ ITEM_REST_OF_WRAPPINGS,
+ ITEM_BANANA2,
+ ITEM_MUG,
+ ITEM_FILE,
+ ITEM_POCKET_ROCKET_BLUEPRINTS,
+ ITEM_HAND_PUPPET,
+ ITEM_ARM_BONE,
+ ITEM_CROWN,
+ ITEM_COMIC_COUPON,
+ ITEM_TORN_PAGE
+};
+
+enum {
+ ROOM_JUNGLE_INSIDE_PLANE = 1,
+ ROOM_JUNGLE_OUTSIDE_PLANE = 2,
+ ROOM_JUNGLE_BRIDGE = 4,
+ ROOM_JUNGLE_GORILLA_1 = 6,
+ ROOM_JUNGLE_PINNACLE = 7,
+ ROOM_JUNGLE_SLOTH = 8,
+ ROOM_JUNGLE_BUD_SKIP = 9,
+ ROOM_JUNGLE_BEETLE = 11,
+ ROOM_JUNGLE_MISSIONARY = 13,
+ ROOM_JUNGLE_GORILLA_2 = 14,
+
+ ROOM_AMAZON_ENTRANCE = 16,
+ ROOM_AMAZON_HIDEOUT = 17,
+ ROOM_AMAZON_THRONE = 18,
+ ROOM_AMAZON_JAIL = 19,
+
+ ROOM_VILLAGE = 20,
+ ROOM_TRADER_BOBS = 21,
+
+ ROOM_FLODA_OUTSIDE = 22,
+ ROOM_FLODA_KITCHEN = 26,
+ ROOM_FLODA_LOCKERROOM = 27,
+ ROOM_FLODA_KLUNK = 30,
+ ROOM_FLODA_HENRY = 32,
+ ROOM_FLODA_OFFICE = 35,
+ ROOM_FLODA_JAIL = 41,
+ ROOM_FLODA_FRONTDESK = 103,
+
+ ROOM_TEMPLE_OUTSIDE = 43,
+ ROOM_TEMPLE_MUMMIES = 46,
+ ROOM_TEMPLE_ZOMBIES = 50,
+ ROOM_TEMPLE_TREE = 51,
+ ROOM_TEMPLE_SNAKE = 53,
+ ROOM_TEMPLE_LIZARD_LASER = 55,
+ ROOM_TEMPLE_MAZE = 58,
+ ROOM_TEMPLE_MAZE_2 = 59,
+ ROOM_TEMPLE_MAZE_3 = 60,
+ ROOM_TEMPLE_MAZE_4 = 61,
+ ROOM_TEMPLE_MAZE_5 = 100,
+ ROOM_TEMPLE_MAZE_6 = 101,
+
+ ROOM_VALLEY_CARCASS = 67,
+
+ ROOM_HOTEL_UPSTAIRS = 70,
+ ROOM_HOTEL_DOWNSTAIRS = 71,
+ ROOM_HOTEL_LOLA = 72,
+ ROOM_HOTEL_LOBBY = 73,
+
+ ROOM_CAR_CHASE = 74,
+
+ ROOM_FINAL_FIGHT = 69,
+
+ ROOM_INTRO_RITA_JOE_HEADS = 116,
+ ROOM_INTRO_EXPLOSION = 123,
+
+ //special
+ SPARKY_OUTSIDE_HOTEL = 77,
+ DEATH_MASK = 79,
+ IBI_LOGO = 82,
+ COMIC_1 = 87,
+ COMIC_2 = 88,
+ COMIC_3 = 89,
+ ROOM_UNUSED_INTRO_1 = 90,
+ ROOM_UNUSED_INTRO_2 = 91,
+ ROOM_UNUSED_INTRO_3 = 92,
+ ROOM_UNUSED_INTRO_4 = 93,
+ ROOM_UNUSED_INTRO_5 = 94,
+ FOTAQ_LOGO = 95,
+ WARNER_LOGO = 126,
+
+ FAYE_HEAD = 37,
+ AZURA_HEAD = 106,
+ FRANK_HEAD = 107,
+
+ ROOM_ENDING_CREDITS = 110,
+
+ ROOM_JOURNAL = 200 // dummy value to keep Display methods happy
+};
+
+
+//! GameState vars
+enum {
+ VAR_HOTEL_ITEMS_REMOVED = 3,
+ VAR_JOE_DRESSING_MODE = 19,
+ VAR_BYPASS_ZOMBIES = 21,
+ VAR_BYPASS_FLODA_RECEPTIONIST = 35,
+ VAR_GUARDS_TURNED_ON = 85,
+ VAR_HOTEL_ESCAPE_STATE = 93,
+ VAR_INTRO_PLAYED = 117,
+ VAR_AZURA_IN_LOVE = 167
+};
+
+
+enum Language {
+ ENGLISH = 'E',
+ FRENCH = 'F',
+ GERMAN = 'G',
+ HEBREW = 'H',
+ ITALIAN = 'I',
+ SPANISH = 'S'
+};
+
+
+enum Verb {
+ VERB_NONE = 0,
+
+ VERB_PANEL_COMMAND_FIRST = 1,
+ VERB_OPEN = 1,
+ VERB_CLOSE = 2,
+ VERB_MOVE = 3,
+ // no verb 4
+ VERB_GIVE = 5,
+ VERB_USE = 6,
+ VERB_PICK_UP = 7,
+ VERB_LOOK_AT = 9,
+ VERB_TALK_TO = 8,
+ VERB_PANEL_COMMAND_LAST = 9,
+
+ VERB_WALK_TO = 10,
+ VERB_SCROLL_UP = 11,
+ VERB_SCROLL_DOWN = 12,
+
+ VERB_DIGIT_FIRST = 13,
+ VERB_DIGIT_1 = 13,
+ VERB_DIGIT_2 = 14,
+ VERB_DIGIT_3 = 15,
+ VERB_DIGIT_4 = 16,
+ VERB_DIGIT_LAST = 16,
+
+ VERB_INV_FIRST = VERB_DIGIT_FIRST,
+ VERB_INV_1 = VERB_DIGIT_1,
+ VERB_INV_2 = VERB_DIGIT_2,
+ VERB_INV_3 = VERB_DIGIT_3,
+ VERB_INV_4 = VERB_DIGIT_4,
+ VERB_INV_LAST = VERB_DIGIT_LAST,
+
+ VERB_USE_JOURNAL = 20,
+ VERB_SKIP_TEXT = 101,
+
+ VERB_PREP_WITH = 11,
+ VERB_PREP_TO = 12
+};
+
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/display.cpp b/engines/queen/display.cpp
new file mode 100644
index 0000000000..d56e472e30
--- /dev/null
+++ b/engines/queen/display.cpp
@@ -0,0 +1,1307 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "queen/display.h"
+
+#include "queen/input.h"
+#include "queen/logic.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+
+namespace Queen {
+
+#ifdef PALMOS_68K
+static const uint8 *_fontRegular;
+static const uint8 *_fontHebrew;
+static const uint8 *_palJoeClothes;
+static const uint8 *_palJoeDress;
+#endif
+
+Display::Display(QueenEngine *vm, OSystem *system)
+ : _fullscreen(true), _horizontalScroll(0), _bdWidth(0), _bdHeight(0),
+ _system(system), _vm(vm) {
+
+ if (vm->resource()->getLanguage() == HEBREW)
+ _font = _fontHebrew;
+ else
+ _font = _fontRegular;
+
+ initFont();
+
+ _screenBuf = new uint8[SCREEN_W * SCREEN_H];
+ _panelBuf = new uint8[PANEL_W * PANEL_H];
+ _backdropBuf = new uint8[BACKDROP_W * BACKDROP_H];
+ memset(_screenBuf, 0, SCREEN_W * SCREEN_H);
+ memset(_panelBuf, 0, PANEL_W * PANEL_H);
+ memset(_backdropBuf, 0, BACKDROP_W * BACKDROP_H);
+
+ _fullRefresh = 1;
+ _dirtyBlocksWidth = SCREEN_W / D_BLOCK_W;
+ _dirtyBlocksHeight = SCREEN_H / D_BLOCK_H;
+ _dirtyBlocks = new uint8[_dirtyBlocksWidth * _dirtyBlocksHeight];
+ memset(_dirtyBlocks, 0, _dirtyBlocksWidth * _dirtyBlocksHeight);
+
+ _pal.room = new uint8[ 256 * 3 ];
+ _pal.screen = new uint8[ 256 * 3 ];
+ _pal.panel = new uint8[ 112 * 3 ];
+ memset(_pal.room, 0, 256 * 3);
+ memset(_pal.screen, 0, 256 * 3);
+ memset(_pal.panel, 0, 112 * 3);
+ _pal.dirtyMin = 0;
+ _pal.dirtyMax = 255;
+ _pal.scrollable = true;
+
+ memset(&_dynalum, 0, sizeof(_dynalum));
+}
+
+Display::~Display() {
+ delete[] _backdropBuf;
+ delete[] _panelBuf;
+ delete[] _screenBuf;
+
+ delete[] _dirtyBlocks;
+
+ delete[] _pal.room;
+ delete[] _pal.screen;
+ delete[] _pal.panel;
+
+ delete[] _dynalum.mskBuf;
+ delete[] _dynalum.lumBuf;
+}
+
+void Display::dynalumInit(const char *roomName, uint16 roomNum) {
+ debug(9, "Display::dynalumInit(%s, %d)", roomName, roomNum);
+
+ _dynalum.valid = false;
+ delete[] _dynalum.mskBuf;
+ _dynalum.mskBuf = NULL;
+ delete[] _dynalum.lumBuf;
+ _dynalum.lumBuf = NULL;
+
+ if (!isPalFadingDisabled(roomNum)) {
+ char filename[20];
+ sprintf(filename, "%s.msk", roomName);
+ if (_vm->resource()->fileExists(filename)) {
+ _dynalum.mskBuf = (uint8 *)_vm->resource()->loadFile(filename, 0, &_dynalum.mskSize);
+ sprintf(filename, "%s.lum", roomName);
+ if (_vm->resource()->fileExists(filename)) {
+ _dynalum.lumBuf = (int8 *)_vm->resource()->loadFile(filename, 0, &_dynalum.lumSize);
+ _dynalum.valid = true;
+ _dynalum.prevColMask = 0xFF;
+ }
+ }
+ }
+}
+
+void Display::dynalumUpdate(int16 x, int16 y) {
+ if (!_dynalum.valid)
+ return;
+
+ if (x < 0) {
+ x = 0;
+ } else if (x > _bdWidth) {
+ x = _bdWidth;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y > ROOM_ZONE_HEIGHT - 1) {
+ y = ROOM_ZONE_HEIGHT - 1;
+ }
+
+ uint32 offset = (y / 4) * 160 + (x / 4);
+ assert(offset < _dynalum.mskSize);
+
+ uint8 colMask = _dynalum.mskBuf[offset];
+ debug(9, "Display::dynalumUpdate(%d, %d) - colMask = %d", x, y, colMask);
+ if (colMask != _dynalum.prevColMask) {
+ for (int i = 144; i < 160; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ int16 c = (int16)(_pal.room[i * 3 + j] + _dynalum.lumBuf[colMask * 3 + j] * 4);
+ if (c < 0) {
+ c = 0;
+ } else if (c > 255) {
+ c = 255;
+ }
+ _pal.screen[i * 3 + j] = (uint8)c;
+ }
+ }
+ _pal.dirtyMin = MIN(_pal.dirtyMin, 144);
+ _pal.dirtyMax = MAX(_pal.dirtyMax, 159);
+ _dynalum.prevColMask = colMask;
+ }
+}
+
+void Display::palSet(const uint8 *pal, int start, int end, bool updateScreen) {
+ debug(9, "Display::palSet(%d, %d)", start, end);
+ const int numColors = end - start + 1;
+ uint8 tempPal[256 * 4];
+ for (int i = 0; i < numColors; i++) {
+ tempPal[4 * i + 0] = *pal++;
+ tempPal[4 * i + 1] = *pal++;
+ tempPal[4 * i + 2] = *pal++;
+ tempPal[4 * i + 3] = 0;
+ }
+ _system->setPalette(tempPal, start, numColors);
+ if (updateScreen) {
+ _system->updateScreen();
+ _vm->input()->delay(20);
+ }
+}
+
+void Display::palSetJoeDress() {
+ memcpy(_pal.room + 144 * 3, _palJoeDress, 16 * 3);
+ memcpy(_pal.screen + 144 * 3, _palJoeDress, 16 * 3);
+ palSet(_pal.screen, 144, 159, true);
+}
+
+void Display::palSetJoeNormal() {
+ memcpy(_pal.room + 144 * 3, _palJoeClothes, 16 * 3);
+ memcpy(_pal.screen + 144 * 3, _palJoeClothes, 16 * 3);
+ palSet(_pal.screen, 144, 159, true);
+}
+
+void Display::palSetPanel() {
+ memcpy(_pal.room + 144 * 3, _pal.panel, (256 - 144) * 3);
+ memcpy(_pal.screen + 144 * 3, _pal.panel, (256 - 144) * 3);
+}
+
+void Display::palFadeIn(uint16 roomNum, bool dynalum, int16 dynaX, int16 dynaY) {
+ debug(9, "Display::palFadeIn(%d)", roomNum);
+ int n = getNumColorsForRoom(roomNum);
+ memcpy(_pal.screen, _pal.room, n * 3);
+ if (!isPalFadingDisabled(roomNum)) {
+ if (dynalum) {
+ dynalumUpdate(dynaX, dynaY);
+ }
+ uint8 tempPal[256 * 3];
+ for (int i = 0; i <= FADE_SPEED; ++i) {
+ for (int j = 0; j < n * 3; ++j) {
+ tempPal[j] = _pal.screen[j] * i / FADE_SPEED;
+ }
+ palSet(tempPal, 0, n - 1, true);
+ }
+ }
+ _pal.dirtyMin = 0;
+ _pal.dirtyMax = n - 1;
+ _pal.scrollable = true;
+}
+
+void Display::palFadeOut(uint16 roomNum) {
+ debug(9, "Display::palFadeOut(%d)", roomNum);
+ _pal.scrollable = false;
+ int n = getNumColorsForRoom(roomNum);
+ if (isPalFadingDisabled(roomNum)) {
+ memset(_pal.screen, 0, n * 3);
+ palSet(_pal.screen, 0, n - 1, true);
+ } else {
+ uint8 tempPal[256 * 3];
+ memcpy(tempPal, _pal.screen, n * 3);
+ for (int i = FADE_SPEED; i >= 0; --i) {
+ for (int j = 0; j < n * 3; ++j) {
+ _pal.screen[j] = tempPal[j] * i / FADE_SPEED;
+ }
+ palSet(_pal.screen, 0, n - 1, true);
+ }
+ }
+}
+
+void Display::palGreyPanel() {
+ debug(9, "Display::palGreyPanel()");
+ uint8 tempPal[256 * 3];
+ for (int i = 224 * 3; i < 256 * 3; i += 3) {
+ tempPal[i] = tempPal[i + 1] = tempPal[i + 2] = _pal.screen[i + 1] * 2 / 3;
+ }
+ palSet(tempPal, 224, 255, true);
+}
+
+void Display::palScroll(int start, int end) {
+ debug(9, "Display::palScroll(%d, %d)", start, end);
+
+ uint8 *palEnd = _pal.screen + end * 3;
+ uint8 *palStart = _pal.screen + start * 3;
+
+ uint8 r = *palEnd++;
+ uint8 g = *palEnd++;
+ uint8 b = *palEnd;
+
+ int n = (end - start) * 3;
+ while (n--) {
+ *palEnd = *(palEnd - 3);
+ --palEnd;
+ }
+
+ *palStart++ = r;
+ *palStart++ = g;
+ *palStart = b;
+}
+
+void Display::palCustomColors(uint16 roomNum) {
+ debug(9, "Display::palCustomColors(%d)", roomNum);
+ int i;
+ switch (roomNum) {
+ case 31:
+ for (i = 72; i < 84; i++) {
+ _pal.room[i * 3 + 1] = _pal.room[i * 3 + 1] * 90 / 100;
+ _pal.room[i * 3 + 2] = _pal.room[i * 3 + 2] * 70 / 100;
+ }
+ break;
+ case 29:
+ for (i = 72; i < 84; i++) {
+ _pal.room[i * 3 + 1] = _pal.room[i * 3 + 1] * 60 / 100;
+ _pal.room[i * 3 + 2] = _pal.room[i * 3 + 2] * 60 / 100;
+ }
+ break;
+ case 30:
+ for (i = 72; i < 84; i++) {
+ _pal.room[i * 3 + 0] = _pal.room[i * 3 + 0] * 60 / 100;
+ _pal.room[i * 3 + 1] = _pal.room[i * 3 + 1] * 80 / 100;
+ }
+ break;
+ case 28:
+ for (i = 72; i < 84; i++) {
+ _pal.room[i * 3 + 0] = _pal.room[i * 3 + 0] * 80 / 100;
+ _pal.room[i * 3 + 2] = _pal.room[i * 3 + 1] * 60 / 100;
+ }
+ break;
+ }
+}
+
+void Display::palCustomScroll(uint16 roomNum) {
+ debug(9, "Display::palCustomScroll(%d)", roomNum);
+ static int16 scrollx = 0;
+
+ if (!_pal.scrollable) {
+ return;
+ }
+
+ int hiPal = 0;
+ int loPal = 255;
+ int i;
+
+ ++scrollx;
+ switch (roomNum) {
+ case 123: {
+ static int16 j = 0, jdir = 2;
+ for (i = 96; i < 111; ++i) {
+ _pal.screen[i * 3 + 0] = MIN(255, _pal.room[i * 3 + 0] + j * 8);
+ _pal.screen[i * 3 + 1] = MIN(255, _pal.room[i * 3 + 1] + j * 4);
+ }
+ j += jdir;
+ if (j <= 0 || j >= 18) {
+ jdir = -jdir;
+ }
+ loPal = 96;
+ hiPal = 111;
+ }
+ break;
+ case 124: {
+ static int16 j = 0,jdir = 2;
+ for (i = 80; i < 144; ++i) {
+ _pal.screen[i * 3 + 0] = MIN(255, _pal.room[i * 3 + 0] + j * 8);
+ _pal.screen[i * 3 + 1] = MIN(255, _pal.room[i * 3 + 1] + j * 4);
+ }
+ j += jdir;
+ if (j <= 0 || j >= 14) {
+ jdir = -jdir;
+ if (_rnd.getRandomNumber(1)) {
+ if (ABS(jdir) == 1) {
+ jdir *= 2;
+ } else {
+ jdir /= 2;
+ }
+ }
+ }
+ loPal = 80;
+ hiPal = 143;
+ }
+ break;
+ case 125:
+ palScroll(32, 63);
+ palScroll(64, 95);
+ loPal = 32;
+ hiPal = 95;
+ break;
+ case 100:
+ if (scrollx & 1) {
+ palScroll(128, 132);
+ palScroll(133, 137);
+ palScroll(138, 143);
+ loPal = 128;
+ hiPal = 143;
+ }
+ break;
+ case 102:
+ if (scrollx & 1) {
+ palScroll(112, 127);
+ loPal = 112;
+ hiPal = 127;
+ }
+ break;
+ case 62:
+ if (scrollx & 1) {
+ palScroll(108, 119);
+ loPal = 108;
+ hiPal = 119;
+ }
+ break;
+ case 25:
+ palScroll(116, 123);
+ loPal = 116;
+ hiPal = 123;
+ break;
+ case 59:
+ if (scrollx & 1) {
+ palScroll(56, 63);
+ loPal = 56;
+ hiPal = 63;
+ }
+ break;
+ case 39:
+ palScroll(112, 143);
+ loPal = 112;
+ hiPal = 143;
+ break;
+ case 74:
+ palScroll(28, 31);
+ palScroll(88, 91);
+ palScroll(92, 95);
+ palScroll(128, 135);
+ if (scrollx & 1) {
+ palScroll(136, 143);
+ }
+ loPal = 28;
+ hiPal = 143;
+ break;
+ case 40:
+ if (scrollx & 1) {
+ palScroll(96, 103);
+ }
+ if (scrollx & 3) {
+ palScroll(104, 107);
+ }
+ loPal = 96;
+ hiPal = 107;
+ break;
+ case 97:
+ if (scrollx & 1) {
+ palScroll(96, 107);
+ palScroll(108, 122);
+ loPal = 96;
+ hiPal = 122;
+ }
+ break;
+ case 55:
+ palScroll(128, 143);
+ loPal = 128;
+ hiPal = 143;
+ break;
+ case 57:
+ palScroll(128, 143);
+ if (scrollx & 1) {
+ palScroll(96, 103);
+ }
+ loPal = 96;
+ hiPal = 143;
+ break;
+ case 76:
+ palScroll(88, 95);
+ loPal = 88;
+ hiPal = 95;
+ break;
+ case 2:
+ if (scrollx & 1) {
+ palScroll(120, 127);
+ loPal = 120;
+ hiPal = 127;
+ }
+ break;
+ case 3:
+ case 5:
+ if (scrollx & 1) {
+ palScroll(128, 135);
+ palScroll(136, 143);
+ loPal = 128;
+ hiPal = 143;
+ }
+ break;
+ case 7:
+ if (scrollx & 1) {
+ palScroll(119, 127);
+ loPal = 119;
+ hiPal = 127;
+ }
+ break;
+ case 42:
+ if (scrollx & 1) {
+ palScroll(118, 127);
+ palScroll(136, 143);
+ loPal = 118;
+ hiPal = 143;
+ }
+ break;
+ case 4:
+ if (scrollx & 1) {
+ palScroll(32,47);
+ }
+ palScroll(64, 70);
+ palScroll(71, 79);
+ loPal = 32;
+ hiPal = 79;
+ break;
+ case 8:
+ if (scrollx & 1) {
+ palScroll(120, 127);
+ }
+ loPal = 120;
+ hiPal = 127;
+ break;
+ case 12:
+ case 64:
+ if (scrollx & 1) {
+ palScroll(112, 119);
+ palScroll(120, 127);
+ loPal = 112;
+ hiPal = 127;
+ }
+ break;
+ case 49:
+ palScroll(101, 127);
+ loPal = 101;
+ hiPal = 127;
+ break;
+ }
+ _pal.dirtyMin = MIN(_pal.dirtyMin, loPal);
+ _pal.dirtyMax = MAX(_pal.dirtyMax, hiPal);
+}
+
+void Display::palCustomFlash() {
+ uint8 tempPal[256 * 3];
+ memset(tempPal, 255, 17 * 3);
+ memset(tempPal + 17 * 3, 0, 67 * 3);
+ memset(tempPal + 67 * 3, 255, 172 * 3);
+ // set flash palette
+ palSet(tempPal, 0, 255, true);
+ // restore original palette
+ palSet(_pal.screen, 0, 255, true);
+}
+
+void Display::palCustomLightsOff(uint16 roomNum) {
+ int end = 223;
+ int start = (roomNum == ROOM_FLODA_FRONTDESK) ? 32 : 16;
+ int n = end - start + 1;
+
+ memset(_pal.screen + start * 3, 0, n * 3);
+ palSet(_pal.screen, start, end, true);
+
+ _pal.scrollable = false;
+}
+
+void Display::palCustomLightsOn(uint16 roomNum) {
+ int end = 223;
+ int start = (roomNum == ROOM_FLODA_FRONTDESK) ? 32 : 0;
+ int n = end - start + 1;
+
+ memcpy(_pal.screen + start * 3, _pal.room + start * 3, n * 3);
+ palSet(_pal.screen, start, end, true);
+
+ _pal.dirtyMin = 0;
+ _pal.dirtyMax = 223;
+ _pal.scrollable = true;
+}
+
+int Display::getNumColorsForRoom(uint16 room) const {
+ int n = 224;
+ if (room >= 114 && room <= 125) {
+ n = 256;
+ }
+ return n;
+}
+
+bool Display::isPalFadingDisabled(uint16 room) const {
+ // introduction rooms don't fade palette
+ return (room >= 90 && room <= 94) || (room >= 115 && room <= 125);
+}
+
+void Display::screenMode(int comPanel, bool inCutaway) {
+ debug(6, "Display::screenMode(%d, %d)", comPanel, inCutaway);
+
+ if (comPanel == 2 && inCutaway) {
+ fullscreen((_bdHeight == GAME_SCREEN_HEIGHT));
+ } else if (comPanel == 1) {
+ fullscreen(false);
+ }
+}
+
+void Display::prepareUpdate() {
+ int h = GAME_SCREEN_HEIGHT;
+ if (!_fullscreen) {
+ h = ROOM_ZONE_HEIGHT;
+ memcpy(_screenBuf + SCREEN_W * ROOM_ZONE_HEIGHT, _panelBuf, PANEL_W * PANEL_H);
+ }
+ uint8 *dst = _screenBuf;
+ const uint8 *src = _backdropBuf + _horizontalScroll;
+
+ while (h--) {
+ memcpy(dst, src, SCREEN_W);
+ dst += SCREEN_W;
+ src += BACKDROP_W;
+ }
+}
+
+void Display::update(bool dynalum, int16 dynaX, int16 dynaY) {
+ drawTexts();
+ if (_pal.scrollable && dynalum) {
+ dynalumUpdate(dynaX, dynaY);
+ }
+ if (_pal.dirtyMin != 144 || _pal.dirtyMax != 144) {
+ palSet(_pal.screen, _pal.dirtyMin, _pal.dirtyMax);
+ _pal.dirtyMin = 144;
+ _pal.dirtyMax = 144;
+ }
+ // uncomment this line to disable the dirty blocks rendering
+// _fullRefresh = 1;
+ if (_fullRefresh) {
+ _system->copyRectToScreen(_screenBuf, SCREEN_W, 0, 0, SCREEN_W, SCREEN_H);
+ _system->updateScreen();
+ --_fullRefresh;
+ if (_fullRefresh) {
+ memset(_dirtyBlocks, 0, _dirtyBlocksWidth * _dirtyBlocksHeight);
+ }
+ debug(7, "Display::update() - Full blit (%d)", _fullRefresh);
+ } else {
+ uint16 count = 0;
+ uint8 *scrBuf = _screenBuf;
+ uint8 *dbBuf = _dirtyBlocks;
+ for (int j = 0; j < _dirtyBlocksHeight; ++j) {
+ uint16 accW = 0;
+ for (int i = 0; i < _dirtyBlocksWidth; ++i) {
+ if (dbBuf[i] != 0) {
+ --dbBuf[i];
+ ++accW;
+ } else if (accW != 0) {
+ int x = (i - accW) * D_BLOCK_W;
+ _system->copyRectToScreen(scrBuf + x, SCREEN_W, x, j * D_BLOCK_H, accW * D_BLOCK_W, D_BLOCK_H);
+ accW = 0;
+ ++count;
+ }
+ }
+ if (accW != 0) {
+ int x = (_dirtyBlocksWidth - accW) * D_BLOCK_W;
+ _system->copyRectToScreen(scrBuf + x, SCREEN_W, x, j * D_BLOCK_H, accW * D_BLOCK_W, D_BLOCK_H);
+ ++count;
+ }
+ dbBuf += _dirtyBlocksWidth;
+ scrBuf += SCREEN_W * D_BLOCK_H;
+ }
+ if (count != 0) {
+ _system->updateScreen();
+ }
+ debug(7, "Display::update() - Dirtyblocks blit (%d)", count);
+ }
+}
+
+void Display::setupPanel() {
+ uint32 size;
+ uint8 *pcxBuf = _vm->resource()->loadFile("panel.pcx", 0, &size);
+ uint8 *dst = _panelBuf + PANEL_W * 10;
+ readPCX(dst, PANEL_W, pcxBuf + 128, PANEL_W, PANEL_H - 10);
+ const uint8 *pal = pcxBuf + size - 768 + 144 * 3;
+ memcpy(_pal.panel, pal, (256 - 144) * 3);
+ delete[] pcxBuf;
+
+ palSetPanel();
+}
+
+void Display::setupNewRoom(const char *name, uint16 room) {
+ dynalumInit(name, room);
+ uint32 size;
+ char filename[20];
+ sprintf(filename, "%s.PCX", name);
+ uint8 *pcxBuf = _vm->resource()->loadFile(filename, 0, &size);
+ _bdWidth = READ_LE_UINT16(pcxBuf + 12);
+ _bdHeight = READ_LE_UINT16(pcxBuf + 14);
+ readPCX(_backdropBuf, BACKDROP_W, pcxBuf + 128, _bdWidth, _bdHeight);
+ int n = getNumColorsForRoom(room);
+ if (n != 256) {
+ n = 144;
+ }
+ memcpy(_pal.room, pcxBuf + size - 768, n * 3);
+ delete[] pcxBuf;
+
+ palCustomColors(room);
+
+ forceFullRefresh();
+}
+
+void Display::drawBobSprite(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h, uint16 pitch, bool xflip) {
+ blit(_screenBuf, SCREEN_W, x, y, data, pitch, w, h, xflip, true);
+ setDirtyBlock(xflip ? (x - w + 1) : x, y, w, h);
+}
+
+void Display::drawBobPasteDown(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h) {
+ blit(_backdropBuf, BACKDROP_W, x, y, data, w, w, h, false, true);
+}
+
+void Display::drawInventoryItem(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h) {
+ if (data != NULL) {
+ blit(_panelBuf, PANEL_W, x, y, data, w, w, h, false, false);
+ } else {
+ fill(_panelBuf, PANEL_W, x, y, w, h, INK_BG_PANEL);
+ }
+ setDirtyBlock(x, 150 + y, w, h);
+}
+
+void Display::blit(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, const uint8 *srcBuf, uint16 srcPitch, uint16 w, uint16 h, bool xflip, bool masked) {
+ assert(w <= dstPitch);
+ dstBuf += dstPitch * y + x;
+
+ if (!masked) { // Unmasked always unflipped
+ while (h--) {
+ memcpy(dstBuf, srcBuf, w);
+ srcBuf += srcPitch;
+ dstBuf += dstPitch;
+ }
+ } else if (!xflip) { // Masked bitmap unflipped
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ uint8 b = *(srcBuf + i);
+ if (b != 0) {
+ *(dstBuf + i) = b;
+ }
+ }
+ srcBuf += srcPitch;
+ dstBuf += dstPitch;
+ }
+ } else { // Masked bitmap flipped
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ uint8 b = *(srcBuf + i);
+ if (b != 0) {
+ *(dstBuf - i) = b;
+ }
+ }
+ srcBuf += srcPitch;
+ dstBuf += dstPitch;
+ }
+ }
+}
+
+void Display::fill(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, uint16 w, uint16 h, uint8 color) {
+ assert(w <= dstPitch);
+ dstBuf += dstPitch * y + x;
+ while (h--) {
+ memset(dstBuf, color, w);
+ dstBuf += dstPitch;
+ }
+}
+
+void Display::readPCX(uint8 *dst, uint16 dstPitch, const uint8 *src, uint16 w, uint16 h) {
+ while (h--) {
+ uint8 *p = dst;
+ while (p < dst + w) {
+ uint8 col = *src++;
+ if ((col & 0xC0) == 0xC0) {
+ uint8 len = col & 0x3F;
+ memset(p, *src++, len);
+ p += len;
+ } else {
+ *p++ = col;
+ }
+ }
+ dst += dstPitch;
+ }
+}
+
+void Display::horizontalScrollUpdate(int16 xCamera) {
+ debug(9, "Display::horizontalScrollUpdate(%d)", xCamera);
+ if (_bdWidth <= 320) {
+ horizontalScroll(0);
+ } else {
+ if (xCamera > 160 && xCamera < 480) {
+ horizontalScroll(xCamera - 160);
+ } else if (xCamera >= 480) {
+ horizontalScroll(320);
+ } else {
+ horizontalScroll(0);
+ }
+ }
+}
+
+void Display::horizontalScroll(int16 scroll) {
+ if (_horizontalScroll != scroll) {
+ _fullRefresh = 2;
+ _horizontalScroll = scroll;
+ }
+}
+
+void Display::setDirtyBlock(uint16 x, uint16 y, uint16 w, uint16 h) {
+ if (_fullRefresh < 2) {
+ uint16 ex = (x + w - 1) / D_BLOCK_W;
+ uint16 ey = (y + h - 1) / D_BLOCK_H;
+ x /= D_BLOCK_W;
+ y /= D_BLOCK_H;
+ uint16 cy = ey - y + 1;
+ uint16 cx = ex - x + 1;
+ if (cy >= _dirtyBlocksHeight) cy = _dirtyBlocksHeight - 1;
+ if (cx >= _dirtyBlocksWidth) cx = _dirtyBlocksWidth - 1;
+ uint8 *p = _dirtyBlocks + _dirtyBlocksWidth * y + x;
+ while (cy--) {
+ for (uint16 i = 0; i < cx; ++i) {
+ p[i] = 2;
+ }
+ p += _dirtyBlocksWidth;
+ }
+ }
+}
+
+void Display::setMouseCursor(uint8 *buf, uint16 w, uint16 h) {
+ _system->setMouseCursor(buf, w, h, 1, 1, 0);
+}
+
+void Display::showMouseCursor(bool show) {
+ _system->showMouse(show);
+}
+
+void Display::initFont() {
+ // calculate font justification sizes
+ for (int i = 0; i < 256; ++i) {
+ _charWidth[i] = 0;
+ for (int y = 0; y < 8; ++y) {
+ uint8 c = _font[i * 8 + y];
+ for (int x = 0; x < 8; ++x) {
+ if ((c & (0x80 >> x)) && (x > _charWidth[i])) {
+ _charWidth[i] = x;
+ }
+ }
+ }
+ _charWidth[i] += 2;
+ }
+ _charWidth[0x20] = 4;
+ --_charWidth[0x5E];
+}
+
+void Display::setText(uint16 x, uint16 y, const char *text, bool outlined) {
+ if (y < GAME_SCREEN_HEIGHT) {
+ if (x == 0) x = 1;
+ if (y == 0) y = 1;
+ TextSlot *pts = &_texts[y];
+ pts->x = x;
+ pts->color = _curTextColor;
+ pts->outlined = outlined;
+ pts->text = text;
+ }
+}
+
+void Display::setTextCentered(uint16 y, const char *text, bool outlined) {
+ int len = strlen(text);
+ int16 x;
+ while ((x = (GAME_SCREEN_WIDTH - textWidth(text, len)) / 2) <= 0) {
+ ++text;
+ len -= 2;
+ }
+ assert(y < GAME_SCREEN_HEIGHT);
+ TextSlot *pts = &_texts[y];
+ pts->x = x;
+ pts->color = _curTextColor;
+ pts->outlined = outlined;
+ pts->text = Common::String(text, len);
+}
+
+void Display::drawTexts() {
+ for (int y = GAME_SCREEN_HEIGHT - 1; y > 0; --y) {
+ const TextSlot *pts = &_texts[y];
+ if (!pts->text.isEmpty()) {
+ drawText(pts->x, y, pts->color, pts->text.c_str(), pts->outlined);
+ }
+ }
+}
+
+void Display::clearTexts(uint16 y1, uint16 y2) {
+ assert(y1 <= y2 && y2 < GAME_SCREEN_HEIGHT);
+ while (y1 <= y2) {
+ _texts[y1].text.clear();
+ ++y1;
+ }
+}
+
+int Display::textCenterX(const char *text) const {
+ return (GAME_SCREEN_WIDTH - textWidth(text)) / 2;
+}
+
+uint16 Display::textWidth(const char *text) const {
+ return textWidth(text, strlen(text));
+}
+
+uint16 Display::textWidth(const char *text, uint16 len) const {
+ assert(len <= strlen(text));
+ uint16 width = 0;
+ for (uint16 i = 0; i < len; ++i) {
+ width += _charWidth[ (uint8)text[i] ];
+ }
+ return width;
+}
+
+void Display::drawChar(uint16 x, uint16 y, uint8 color, const uint8 *chr) {
+ uint8 *dstBuf = _screenBuf + SCREEN_W * y + x;
+ for (int j = 0; j < 8; ++j) {
+ uint8 *p = dstBuf;
+ uint8 c = *chr++;
+ if (c != 0) {
+ for (int i = 0; i < 8; ++i) {
+ if (c & 0x80) {
+ *p = color;
+ }
+ ++p;
+ c <<= 1;
+ }
+ }
+ dstBuf += SCREEN_W;
+ }
+}
+
+void Display::drawText(uint16 x, uint16 y, uint8 color, const char *text, bool outlined) {
+ static const int dx[] = { -1, 0, 1, 1, 1, 0, -1, -1 };
+ static const int dy[] = { -1, -1, -1, 0, 1, 1, 1, 0 };
+ const uint8 *str = (const uint8 *)text;
+ uint16 xs = x;
+ while (*str && x < SCREEN_W) {
+ const uint8 ch = *str++;
+ const uint8 *ftch = _font + ch * 8;
+ if (outlined) {
+ for (int i = 0; i < 8; ++i) {
+ drawChar(x + dx[i], y + dy[i], INK_OUTLINED_TEXT, ftch);
+ }
+ }
+ drawChar(x, y, color, ftch);
+ x += _charWidth[ch];
+ }
+ setDirtyBlock(xs - 1, y - 1, x - xs + 2, 8 + 2);
+}
+
+void Display::drawBox(int16 x1, int16 y1, int16 x2, int16 y2, uint8 col) {
+ int i;
+ for (i = y1; i <= y2; ++i) {
+ _screenBuf[i * SCREEN_W + x1] = _screenBuf[i * SCREEN_W + x2] = col;
+ }
+ setDirtyBlock(x1, y1, 1, y2 - y1);
+ setDirtyBlock(x2, y1, 1, y2 - y1);
+ for (i = x1; i <= x2; ++i) {
+ _screenBuf[y1 * SCREEN_W + i] = _screenBuf[y2 * SCREEN_W + i] = col;
+ }
+ setDirtyBlock(x1, y1, x2 - x1, 1);
+ setDirtyBlock(x1, y2, x2 - x1, 1);
+}
+
+void Display::shake(bool reset) {
+ _system->setShakePos(reset ? 0 : 3);
+}
+
+void Display::blankScreen() {
+ static int current = 0;
+ typedef void (Display::*BlankerEffect)();
+ static const BlankerEffect effects[] = {
+ &Display::blankScreenEffect1,
+ &Display::blankScreenEffect2,
+ &Display::blankScreenEffect3
+ };
+ (this->*effects[current])();
+ current = (current + 1) % ARRAYSIZE(effects);
+ forceFullRefresh();
+}
+
+void Display::blankScreenEffect1() {
+ uint8 buf[32 * 32];
+ while (_vm->input()->idleTime() >= Input::DELAY_SCREEN_BLANKER) {
+ for (int i = 0; i < 2; ++i) {
+ int x = _rnd.getRandomNumber(SCREEN_W - 32 - 2) + 1;
+ int y = _rnd.getRandomNumber(SCREEN_H - 32 - 2) + 1;
+ const uint8 *p = _screenBuf + SCREEN_W * y + x;
+ for (int j = 0; j < 32; ++j) {
+ memcpy(buf + j * 32, p, 32);
+ p += SCREEN_W;
+ }
+ if (_rnd.getRandomNumber(1)) {
+ ++x;
+ } else {
+ --x;
+ }
+ if (_rnd.getRandomNumber(1)) {
+ ++y;
+ } else {
+ --y;
+ }
+ _system->copyRectToScreen(buf, 32, x, y, 32, 32);
+ _system->updateScreen();
+ _vm->input()->delay(10);
+ }
+ }
+}
+
+void Display::blankScreenEffect2() {
+ while (_vm->input()->idleTime() >= Input::DELAY_SCREEN_BLANKER) {
+ int x = _rnd.getRandomNumber(SCREEN_W - 2);
+ int y = _rnd.getRandomNumber(SCREEN_H - 2);
+ uint8 *p = _screenBuf + y * SCREEN_W + x;
+ uint8 c = 0;
+ switch (_rnd.getRandomNumber(3)) {
+ case 0:
+ c = *p;
+ break;
+ case 1:
+ c = *(p + 1);
+ break;
+ case 2:
+ c = *(p + SCREEN_W);
+ break;
+ case 3:
+ c = *(p + SCREEN_W + 1);
+ break;
+ }
+ memset(p, c, 2);
+ memset(p + SCREEN_W, c, 2);
+ _system->copyRectToScreen(p, SCREEN_W, x, y, 2, 2);
+ _system->updateScreen();
+ _vm->input()->delay(10);
+ }
+}
+
+void Display::blankScreenEffect3() {
+ uint32 i = 0;
+ while (_vm->input()->idleTime() >= Input::DELAY_SCREEN_BLANKER) {
+ if (i > 4000000) {
+ memset(_screenBuf, 0, SCREEN_W * SCREEN_H);
+ _system->copyRectToScreen(_screenBuf, SCREEN_W, 0, 0, SCREEN_W, SCREEN_H);
+ } else {
+ int x = _rnd.getRandomNumber(SCREEN_W - 2);
+ int y = _rnd.getRandomNumber(SCREEN_H - 2);
+ uint8 *p = _screenBuf + SCREEN_W * y + x;
+ int sum = *p + *(p + 1) + *(p + SCREEN_W) + *(p + SCREEN_W + 1);
+ uint8 c = (uint8)(sum / 4);
+ memset(p, c, 2);
+ memset(p + SCREEN_W, c, 2);
+ ++i;
+ _system->copyRectToScreen(p, SCREEN_W, x, y, 2, 2);
+ }
+ _system->updateScreen();
+ _vm->input()->delay(10);
+ }
+}
+
+#ifndef PALMOS_68K
+const uint8 Display::_fontRegular[] = {
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0x00,
+ 0xD8, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00,
+ 0x30, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x30, 0x00, 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00,
+ 0x38, 0x6C, 0x68, 0x36, 0xDC, 0xCC, 0x76, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x60, 0xC0, 0xC0, 0xC0, 0x60, 0x30, 0x00, 0xC0, 0x60, 0x30, 0x30, 0x30, 0x60, 0xC0, 0x00,
+ 0x00, 0x6C, 0x38, 0xFE, 0x38, 0x6C, 0x00, 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00,
+ 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x30, 0x70, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xCC, 0x0C, 0x78, 0xC0, 0xC0, 0xFC, 0x00, 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00,
+ 0x1C, 0x3C, 0x6C, 0xCC, 0xFC, 0x0C, 0x0C, 0x00, 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00,
+ 0x78, 0xCC, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00,
+ 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0xC0,
+ 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0x00, 0x00,
+ 0xC0, 0x60, 0x30, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x38, 0x7C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
+ 0xF8, 0xCC, 0xCC, 0xF8, 0xCC, 0xCC, 0xF8, 0x00, 0x78, 0xCC, 0xC0, 0xC0, 0xC0, 0xCC, 0x78, 0x00,
+ 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xF8, 0x00, 0xFC, 0xC0, 0xC0, 0xF0, 0xC0, 0xC0, 0xFC, 0x00,
+ 0xFC, 0xC0, 0xC0, 0xF0, 0xC0, 0xC0, 0xC0, 0x00, 0x78, 0xCC, 0xC0, 0xDC, 0xCC, 0xCC, 0x7C, 0x00,
+ 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, 0xC6, 0xCC, 0xD8, 0xF8, 0xD8, 0xCC, 0xC6, 0x00,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFC, 0x00, 0x82, 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
+ 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00,
+ 0xF8, 0xCC, 0xCC, 0xF8, 0xC0, 0xC0, 0xC0, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x0C,
+ 0xF8, 0xCC, 0xCC, 0xF8, 0xD8, 0xCC, 0xCC, 0x00, 0x78, 0xCC, 0xC0, 0x78, 0x0C, 0xCC, 0x78, 0x00,
+ 0xFC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x00,
+ 0xC6, 0xC6, 0x6C, 0x6C, 0x38, 0x38, 0x10, 0x00, 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00,
+ 0xC6, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0xC6, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x30, 0x00,
+ 0xFC, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xFC, 0x00, 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xF0, 0x00,
+ 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x00, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0xF0, 0x00,
+ 0xE8, 0x4D, 0x4A, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0xC0, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x7C, 0x00,
+ 0xC0, 0xC0, 0xF8, 0xCC, 0xCC, 0xCC, 0xF8, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00,
+ 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x7C, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00,
+ 0x38, 0x6C, 0x60, 0xF8, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x78,
+ 0xC0, 0xC0, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00,
+ 0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0x78, 0xC0, 0xC0, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0x00,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xCC, 0xEE, 0xD6, 0xC6, 0xC6, 0x00,
+ 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00,
+ 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xF8, 0xC0, 0xC0, 0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C,
+ 0x00, 0x00, 0xF8, 0xCC, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0x78, 0x00,
+ 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x00,
+ 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, 0x00, 0x00, 0xC6, 0xD6, 0xD6, 0x6C, 0x6C, 0x00,
+ 0x00, 0x00, 0xCC, 0x78, 0x30, 0x78, 0xCC, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0xE0,
+ 0x00, 0x00, 0xFC, 0x18, 0x30, 0x60, 0xFC, 0x00, 0x38, 0x60, 0x60, 0xC0, 0x60, 0x60, 0x38, 0x00,
+ 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0xE0, 0x30, 0x30, 0x18, 0x30, 0x30, 0xE0, 0x00,
+ 0x38, 0x44, 0xBA, 0xAA, 0xBA, 0x44, 0x38, 0x00, 0x00, 0x98, 0x30, 0x60, 0xC8, 0x98, 0x30, 0x00,
+ 0x1E, 0x30, 0x60, 0x60, 0x30, 0x1E, 0x0C, 0x18, 0x00, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x0C, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x18, 0x66, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x30, 0x60, 0x60, 0x30, 0x1E, 0x0C, 0x18,
+ 0x18, 0x66, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x66, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00,
+ 0x30, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x00, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x30, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00,
+ 0x00, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00,
+ 0x18, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x7C, 0x00, 0x0C, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x30, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x18, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x00,
+ 0x71, 0x8E, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x00, 0x71, 0xCE, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0x3C, 0x60, 0x3C, 0x66, 0x3C, 0x06, 0x3C, 0x00,
+ 0x18, 0x00, 0x18, 0x30, 0x60, 0x66, 0x3C, 0x00, 0x3F, 0x40, 0x4E, 0x58, 0x4E, 0x40, 0x3F, 0x00,
+ 0x1C, 0xA4, 0xC4, 0xBC, 0x80, 0xFE, 0x00, 0x00, 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00,
+ 0x3E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x00,
+ 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0x81, 0x7E, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x78, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0xFC, 0x00,
+ 0xF0, 0x18, 0x30, 0x60, 0xF8, 0x00, 0x00, 0x00, 0xF0, 0x18, 0x30, 0x18, 0xF0, 0x00, 0x00, 0x00,
+ 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xFE, 0xC0,
+ 0x3E, 0x7A, 0x7A, 0x3A, 0x0A, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x60, 0xE0, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00,
+ 0x38, 0x44, 0x44, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00,
+ 0x40, 0xC6, 0x4C, 0x58, 0x32, 0x66, 0xCF, 0x02, 0x40, 0xC6, 0x4C, 0x58, 0x3E, 0x62, 0xC4, 0x0E,
+ 0xC0, 0x23, 0x66, 0x2C, 0xD9, 0x33, 0x67, 0x01, 0x18, 0x00, 0x18, 0x30, 0x60, 0x66, 0x3C, 0x00,
+ 0x60, 0x30, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x0C, 0x18, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
+ 0x38, 0xC6, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x71, 0x8E, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
+ 0x6C, 0x00, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x38, 0x44, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
+ 0x1F, 0x3C, 0x3C, 0x6F, 0x7C, 0xCC, 0xCF, 0x00, 0x1E, 0x30, 0x60, 0x60, 0x30, 0x1E, 0x0C, 0x18,
+ 0x60, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0x18, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00,
+ 0x30, 0xCC, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0xCC, 0x00, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00,
+ 0x60, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, 0x18, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x30, 0xCC, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, 0xCC, 0x00, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x78, 0x6C, 0x66, 0xF6, 0x66, 0x6C, 0x78, 0x00, 0x71, 0xCE, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x00,
+ 0x30, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x0C, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0x18, 0x66, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0xC3, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, 0x00,
+ 0x3F, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0xFC, 0x00, 0x30, 0x18, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x0C, 0x18, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x18, 0x24, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x06, 0x08, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x00,
+ 0x60, 0x60, 0x7E, 0x63, 0x7E, 0x60, 0x60, 0x00, 0x3C, 0x66, 0x66, 0x6C, 0x66, 0x66, 0x6C, 0x60,
+ 0x30, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x0C, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
+ 0x18, 0x66, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x71, 0x8E, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
+ 0x66, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x18, 0x24, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
+ 0x00, 0x00, 0x7E, 0x1B, 0x7F, 0xD8, 0x77, 0x00, 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x18,
+ 0x30, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x0C, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00,
+ 0x18, 0x66, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x66, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00,
+ 0x30, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x0C, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x60, 0xFC, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x00,
+ 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00,
+ 0x18, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00,
+ 0x00, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x02, 0x7C, 0xCE, 0xD6, 0xE6, 0x7C, 0x80, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x0C, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x18, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x00, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x0C, 0x18, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x30,
+ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00, 0x66, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x30
+};
+
+const uint8 Display::_fontHebrew[] = {
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00, 0xF8, 0xB0, 0xB0, 0x80, 0xB0, 0xB0, 0xC0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0x00,
+ 0xD8, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00,
+ 0x30, 0x7C, 0xC0, 0x78, 0x0C, 0xF8, 0x30, 0x00, 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00,
+ 0x38, 0x6C, 0x68, 0x36, 0xDC, 0xCC, 0x76, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x60, 0xC0, 0xC0, 0xC0, 0x60, 0x30, 0x00, 0xC0, 0x60, 0x30, 0x30, 0x30, 0x60, 0xC0, 0x00,
+ 0x00, 0x6C, 0x38, 0xFE, 0x38, 0x6C, 0x00, 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x00,
+ 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x30, 0x70, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xCC, 0x0C, 0x78, 0xC0, 0xC0, 0xFC, 0x00, 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00,
+ 0x1C, 0x3C, 0x6C, 0xCC, 0xFC, 0x0C, 0x0C, 0x00, 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00,
+ 0x78, 0xCC, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00,
+ 0x00, 0xC0, 0xC0, 0x00, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0xC0,
+ 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0x00, 0x00,
+ 0xC0, 0x60, 0x30, 0x18, 0x30, 0x60, 0xC0, 0x00, 0x78, 0xCC, 0x0C, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x38, 0x7C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
+ 0xF8, 0xCC, 0xCC, 0xF8, 0xCC, 0xCC, 0xF8, 0x00, 0x78, 0xCC, 0xC0, 0xC0, 0xC0, 0xCC, 0x78, 0x00,
+ 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xF8, 0x00, 0xFC, 0xC0, 0xC0, 0xF0, 0xC0, 0xC0, 0xFC, 0x00,
+ 0xFC, 0xC0, 0xC0, 0xF0, 0xC0, 0xC0, 0xC0, 0x00, 0x78, 0xCC, 0xC0, 0xDC, 0xCC, 0xCC, 0x7C, 0x00,
+ 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, 0xC6, 0xCC, 0xD8, 0xF8, 0xD8, 0xCC, 0xC6, 0x00,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFC, 0x00, 0x82, 0xC6, 0xEE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00,
+ 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00,
+ 0xF8, 0xCC, 0xCC, 0xF8, 0xC0, 0xC0, 0xC0, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xDC, 0x78, 0x0C,
+ 0xF8, 0xCC, 0xCC, 0xF8, 0xD8, 0xCC, 0xCC, 0x00, 0x78, 0xCC, 0xC0, 0x78, 0x0C, 0xCC, 0x78, 0x00,
+ 0xFC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x00,
+ 0xC6, 0xC6, 0x6C, 0x6C, 0x38, 0x38, 0x10, 0x00, 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00,
+ 0xC6, 0x6C, 0x38, 0x10, 0x38, 0x6C, 0xC6, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x30, 0x00,
+ 0xFC, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xFC, 0x00, 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xF0, 0x00,
+ 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x00, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0xF0, 0x00,
+ 0xE8, 0x4D, 0x4A, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0xC0, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x7C, 0x00,
+ 0xC0, 0xC0, 0xF8, 0xCC, 0xCC, 0xCC, 0xF8, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00,
+ 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x7C, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00,
+ 0x38, 0x6C, 0x60, 0xF8, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x78,
+ 0xC0, 0xC0, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00,
+ 0x0C, 0x00, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0x78, 0xC0, 0xC0, 0xCC, 0xD8, 0xF0, 0xD8, 0xCC, 0x00,
+ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xCC, 0xEE, 0xD6, 0xC6, 0xC6, 0x00,
+ 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00,
+ 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xF8, 0xC0, 0xC0, 0x00, 0x00, 0x7C, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C,
+ 0x00, 0x00, 0xF8, 0xCC, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x7C, 0xC0, 0x78, 0x0C, 0x78, 0x00,
+ 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x00,
+ 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, 0x00, 0x00, 0xC6, 0xD6, 0xD6, 0x6C, 0x6C, 0x00,
+ 0x00, 0x00, 0xCC, 0x78, 0x30, 0x78, 0xCC, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0xE0,
+ 0x00, 0x00, 0xFC, 0x18, 0x30, 0x60, 0xFC, 0x00, 0x38, 0x60, 0x60, 0xC0, 0x60, 0x60, 0x38, 0x00,
+ 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0xE0, 0x30, 0x30, 0x18, 0x30, 0x30, 0xE0, 0x00,
+ 0x38, 0x44, 0xBA, 0xAA, 0xBA, 0x44, 0x38, 0x00, 0x00, 0x98, 0x30, 0x60, 0xC8, 0x98, 0x30, 0x00,
+ 0xCC, 0x66, 0x76, 0xBC, 0x98, 0x8C, 0xE6, 0x00, 0xFC, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xFE, 0x00,
+ 0x78, 0x18, 0x18, 0x18, 0x38, 0x78, 0xD8, 0x00, 0xFE, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0xFE, 0x06, 0x06, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x7C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0xFE, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0xDC, 0x66, 0xE6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0xF0, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xFE, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xF8, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xF8, 0x00,
+ 0xC0, 0xFE, 0x06, 0x06, 0x0C, 0x18, 0x18, 0x00, 0xFE, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7E, 0x00,
+ 0xFC, 0x76, 0x66, 0x66, 0x66, 0x66, 0x6E, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, 0xFE, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0xEE, 0x66, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0xFE, 0xC6, 0xC6, 0xF6, 0x06, 0x06, 0x06, 0x06,
+ 0xFE, 0xC6, 0xC6, 0xFE, 0x06, 0x06, 0xFE, 0x00, 0xFE, 0x66, 0x6C, 0x78, 0x60, 0x60, 0x60, 0x60,
+ 0xEE, 0x66, 0x3C, 0x18, 0x0C, 0x06, 0xFE, 0x00, 0xFE, 0x06, 0x0E, 0xD8, 0xF0, 0xF0, 0xC0, 0xC0,
+ 0xFC, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0xEE, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0x7C, 0x00,
+ 0xFF, 0x67, 0x67, 0x67, 0x67, 0x67, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x0C, 0x3E, 0x6C, 0x3E, 0x0C, 0x00, 0x00, 0x38, 0x6C, 0x60, 0xF0, 0x60, 0x60, 0xFC, 0x00,
+ 0x42, 0x3C, 0x66, 0x3C, 0x42, 0x00, 0x00, 0x00, 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0x3C, 0x60, 0x3C, 0x66, 0x3C, 0x06, 0x3C, 0x00,
+ 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x4E, 0x58, 0x4E, 0x40, 0x3F, 0x00,
+ 0x1C, 0xA4, 0xC4, 0xBC, 0x80, 0xFE, 0x00, 0x00, 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00,
+ 0x3E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0xB9, 0xA5, 0xB9, 0xA5, 0x81, 0x7E, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x78, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x00, 0xFC, 0x00,
+ 0xF0, 0x18, 0x30, 0x60, 0xF8, 0x00, 0x00, 0x00, 0xF0, 0x18, 0x30, 0x18, 0xF0, 0x00, 0x00, 0x00,
+ 0x30, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xFE, 0xC0,
+ 0x3E, 0x7A, 0x7A, 0x3A, 0x0A, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0x60, 0xE0, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00,
+ 0x38, 0x44, 0x44, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00,
+ 0x40, 0xC6, 0x4C, 0x58, 0x32, 0x66, 0xCF, 0x02, 0x40, 0xC6, 0x4C, 0x58, 0x3E, 0x62, 0xC4, 0x0E,
+ 0xC0, 0x23, 0x66, 0x2C, 0xD9, 0x33, 0x67, 0x01, 0x18, 0x00, 0x18, 0x30, 0x60, 0x66, 0x3C, 0x00,
+ 0x60, 0x30, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x0C, 0x18, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
+ 0x38, 0xC6, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x71, 0x8E, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
+ 0x6C, 0x00, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, 0x38, 0x44, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00,
+ 0x1F, 0x3C, 0x3C, 0x6F, 0x7C, 0xCC, 0xCF, 0x00, 0x1E, 0x30, 0x60, 0x60, 0x30, 0x1E, 0x0C, 0x18,
+ 0x60, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0x18, 0x30, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00,
+ 0x30, 0xCC, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00, 0xCC, 0x00, 0xFC, 0xC0, 0xF0, 0xC0, 0xFC, 0x00,
+ 0x60, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, 0x18, 0x30, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x30, 0xCC, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, 0xCC, 0x00, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x78, 0x6C, 0x66, 0xF6, 0x66, 0x6C, 0x78, 0x00, 0x71, 0xCE, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0x00,
+ 0x30, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x0C, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0x18, 0x66, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0xC3, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, 0x00,
+ 0x3F, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0xFC, 0x00, 0x30, 0x18, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x0C, 0x18, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x18, 0x24, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x06, 0x08, 0xC3, 0x66, 0x3C, 0x18, 0x18, 0x00,
+ 0x60, 0x60, 0x7E, 0x63, 0x7E, 0x60, 0x60, 0x00, 0x3C, 0x66, 0x66, 0x6C, 0x66, 0x66, 0x6C, 0x60,
+ 0x30, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x0C, 0x18, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
+ 0x18, 0x66, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x71, 0x8E, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
+ 0x66, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, 0x18, 0x24, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
+ 0x00, 0x00, 0x7E, 0x1B, 0x7F, 0xD8, 0x77, 0x00, 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x18,
+ 0x30, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x0C, 0x18, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00,
+ 0x18, 0x66, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, 0x66, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00,
+ 0x30, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x0C, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x66, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x60, 0xFC, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x00,
+ 0x30, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x0C, 0x18, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00,
+ 0x18, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x71, 0x8E, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00,
+ 0x00, 0x66, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x02, 0x7C, 0xCE, 0xD6, 0xE6, 0x7C, 0x80, 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x0C, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x18, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x00, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x0C, 0x18, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x30,
+ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00, 0x66, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x30
+};
+
+const uint8 Display::_palJoeClothes[] = {
+ 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x87, 0x87, 0x87, 0xB0, 0xB0, 0xB0, 0xDA, 0xDA, 0xDA, 0x43,
+ 0x34, 0x20, 0x77, 0x33, 0x1F, 0xA3, 0x43, 0x27, 0x80, 0x45, 0x45, 0x9E, 0x5D, 0x5B, 0xB9, 0x78,
+ 0x75, 0xDF, 0x97, 0x91, 0x17, 0x27, 0x63, 0x1F, 0x3F, 0x83, 0x27, 0x5B, 0xA7, 0x98, 0xD4, 0xFF
+};
+
+const uint8 Display::_palJoeDress[] = {
+ 0x00, 0x00, 0x00, 0x50, 0x50, 0x50, 0x70, 0x70, 0x70, 0x90, 0x90, 0x90, 0xC6, 0xC6, 0xC6, 0xFF,
+ 0xFF, 0xFF, 0x30, 0x30, 0x90, 0x47, 0x49, 0xD0, 0x40, 0x24, 0x00, 0x79, 0x34, 0x0B, 0xB2, 0x3D,
+ 0x22, 0xED, 0x42, 0x42, 0x80, 0x45, 0x45, 0xA3, 0x5F, 0x5F, 0xC8, 0x7C, 0x7C, 0xEC, 0x9C, 0x9C
+};
+
+#endif
+
+} // End of namespace Queen
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Queen_Display)
+_GSETPTR(Queen::_fontRegular, GBVARS_DISPLAYFONTREGULAR_INDEX, uint8, GBVARS_QUEEN)
+_GSETPTR(Queen::_fontHebrew, GBVARS_DISPLAYFONTHEBREW_INDEX, uint8, GBVARS_QUEEN)
+_GSETPTR(Queen::_palJoeClothes, GBVARS_DISPLAYPALJOECLOTHES_INDEX, uint8, GBVARS_QUEEN)
+_GSETPTR(Queen::_palJoeDress, GBVARS_DISPLAYPALJOEDRESS_INDEX, uint8, GBVARS_QUEEN)
+_GEND
+
+_GRELEASE(Queen_Display)
+_GRELEASEPTR(GBVARS_DISPLAYFONTREGULAR_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_DISPLAYFONTHEBREW_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_DISPLAYPALJOECLOTHES_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_DISPLAYPALJOEDRESS_INDEX, GBVARS_QUEEN)
+_GEND
+
+#endif
diff --git a/engines/queen/display.h b/engines/queen/display.h
new file mode 100644
index 0000000000..87608a57e3
--- /dev/null
+++ b/engines/queen/display.h
@@ -0,0 +1,246 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENDISPLAY_H
+#define QUEENDISPLAY_H
+
+#include "common/str.h"
+#include "common/util.h"
+#include "queen/defs.h"
+
+class OSystem;
+
+namespace Queen {
+
+class QueenEngine;
+
+class Display {
+public:
+
+ Display(QueenEngine *vm, OSystem *system);
+ ~Display();
+
+ //! initialise dynalum for the specified room
+ void dynalumInit(const char *roomName, uint16 roomNum);
+
+ //! update dynalum for the current room
+ void dynalumUpdate(int16 x, int16 y);
+
+ //! update the palette
+ void palSet(const uint8 *pal, int start, int end, bool updateScreen = false);
+
+ //! setup palette for Joe's dress
+ void palSetJoeDress();
+
+ //! setup palette for Joe's normal clothes
+ void palSetJoeNormal();
+
+ //! setup palette for panel and inventory objects
+ void palSetPanel();
+
+ //! fade the current palette in
+ void palFadeIn(uint16 roomNum, bool dynalum = false, int16 dynaX = 0, int16 dynaY = 0);
+
+ //! fade the current palette out
+ void palFadeOut(uint16 roomNum);
+
+ //! grey the panel area (used when panel is disabled)
+ void palGreyPanel();
+
+ //! scroll some palette colors
+ void palScroll(int start, int end);
+
+ //! custom palette effect for the specified room
+ void palCustomColors(uint16 roomNum);
+
+ //! custom palette scroll for the specified room
+ void palCustomScroll(uint16 roomNum);
+
+ //! process a 'palette flash' effect
+ void palCustomFlash();
+
+ void palCustomLightsOff(uint16 roomNum);
+ void palCustomLightsOn(uint16 roomNum);
+
+ //! mark all palette entries as dirty
+ void palSetAllDirty() { _pal.dirtyMin = 0; _pal.dirtyMax = 255; }
+
+ //! returns the number of colors used by the room
+ int getNumColorsForRoom(uint16 room) const;
+
+ //! returns true if we shouldn't fade the palette in the specified room
+ bool isPalFadingDisabled(uint16 room) const;
+
+ //! change fullscreen/panel mode
+ void screenMode(int comPanel, bool inCutaway);
+
+ void prepareUpdate();
+ void update(bool dynalum = false, int16 dynaX = 0, int16 dynaY = 0);
+
+ void setupPanel();
+ void setupNewRoom(const char *name, uint16 room);
+
+ void drawBobSprite(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h, uint16 pitch, bool xflip);
+ void drawBobPasteDown(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h);
+ void drawInventoryItem(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h);
+
+ void blit(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, const uint8 *srcBuf, uint16 srcPitch, uint16 w, uint16 h, bool xflip, bool masked);
+ void fill(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, uint16 w, uint16 h, uint8 color);
+
+ //! decode a PCX stream
+ void readPCX(uint8 *dst, uint16 dstPitch, const uint8 *src, uint16 w, uint16 h);
+
+ void horizontalScrollUpdate(int16 xCamera);
+ void horizontalScroll(int16 scroll);
+ int16 horizontalScroll() const { return _horizontalScroll; }
+
+ void fullscreen(bool fs) { _fullRefresh = 2; _fullscreen = fs; }
+ bool fullscreen() const { return _fullscreen; }
+
+ //! mark the specified block as dirty
+ void setDirtyBlock(uint16 x, uint16 y, uint16 w, uint16 h);
+
+ //! force a full refresh (bypassing the dirtyblocks rendering), on next screen update
+ void forceFullRefresh() { _fullRefresh = 2; }
+
+ //! change mouse cursor bitmap
+ void setMouseCursor(uint8 *buf, uint16 w, uint16 h);
+
+ //! show/hide mouse cursor
+ void showMouseCursor(bool show);
+
+ //! initialise font, compute justification sizes
+ void initFont();
+
+ //! add the specified text to the texts list
+ void setText(uint16 x, uint16 y, const char *text, bool outlined = true);
+
+ //! add the specified text to the texts list
+ void setTextCentered(uint16 y, const char *text, bool outlined = true);
+
+ //! draw the text lists
+ void drawTexts();
+
+ //! remove entries from the texts list
+ void clearTexts(uint16 y1, uint16 y2);
+
+ //! change the current text color
+ void textCurrentColor(uint8 color) { _curTextColor = color; }
+
+ //! change the text color for the specified texts list entry
+ void textColor(uint16 y, uint8 color) { _texts[y].color = color; }
+
+ int textCenterX(const char *text) const;
+ uint16 textWidth(const char *text) const;
+ uint16 textWidth(const char *text, uint16 len) const;
+ void drawChar(uint16 x, uint16 y, uint8 color, const uint8 *chr);
+ void drawText(uint16 x, uint16 y, uint8 color, const char *text, bool outlined = true);
+ void drawBox(int16 x1, int16 y1, int16 x2, int16 y2, uint8 col);
+
+ void shake(bool reset);
+
+ void blankScreen();
+ void blankScreenEffect1();
+ void blankScreenEffect2();
+ void blankScreenEffect3();
+
+private:
+
+ enum {
+ FADE_SPEED = 16,
+ D_BLOCK_W = 8,
+ D_BLOCK_H = 8
+ };
+
+ enum BufferDimension {
+ BACKDROP_W = 640,
+ BACKDROP_H = 200,
+ SCREEN_W = 320,
+ SCREEN_H = 200,
+ PANEL_W = 320,
+ PANEL_H = 50
+ };
+
+ struct {
+ uint8 *room;
+ uint8 *screen;
+ uint8 *panel;
+ int dirtyMin, dirtyMax;
+ bool scrollable;
+ } _pal;
+
+ struct Dynalum {
+ bool valid;
+ uint8 *mskBuf;
+ uint32 mskSize;
+ int8 *lumBuf;
+ uint32 lumSize;
+ uint8 prevColMask;
+ };
+
+ struct TextSlot {
+ uint16 x;
+ uint8 color;
+ Common::String text;
+ bool outlined;
+ };
+
+ uint8 *_screenBuf;
+ uint8 *_panelBuf;
+ uint8 *_backdropBuf;
+
+ uint8 _fullRefresh;
+ uint8 *_dirtyBlocks;
+ uint16 _dirtyBlocksWidth, _dirtyBlocksHeight;
+
+ bool _fullscreen;
+
+ uint16 _horizontalScroll;
+ uint16 _bdWidth, _bdHeight;
+
+ //! texts list
+ TextSlot _texts[GAME_SCREEN_HEIGHT];
+
+ //! current text color to use for display
+ uint8 _curTextColor;
+
+ //! font justification sizes
+ uint8 _charWidth[256];
+
+ Common::RandomSource _rnd;
+ Dynalum _dynalum;
+ OSystem *_system;
+ QueenEngine *_vm;
+
+ const uint8 *_font;
+#ifndef PALMOS_68K
+ static const uint8 _fontRegular[];
+ static const uint8 _fontHebrew[];
+ static const uint8 _palJoeClothes[];
+ static const uint8 _palJoeDress[];
+#endif
+};
+
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/graphics.cpp b/engines/queen/graphics.cpp
new file mode 100644
index 0000000000..be2f8d6017
--- /dev/null
+++ b/engines/queen/graphics.cpp
@@ -0,0 +1,1537 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/graphics.h"
+
+#include "queen/bankman.h"
+#include "queen/display.h"
+#include "queen/grid.h"
+#include "queen/logic.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+
+namespace Queen {
+
+#ifdef PALMOS_68K
+static const BamScene::BamDataBlock *_carData;
+static const BamScene::BamDataBlock *_fight1Data;
+static const BamScene::BamDataBlock *_fight2Data;
+static const BamScene::BamDataBlock *_fight3Data;
+#endif
+
+const Box BobSlot::_defaultBox(-1, -1, -1, -1);
+
+void BobSlot::curPos(int16 xx, int16 yy) {
+ active = true;
+ x = xx;
+ y = yy;
+}
+
+void BobSlot::move(int16 dstx, int16 dsty, int16 spd) {
+ active = true;
+ moving = true;
+
+ endx = dstx;
+ endy = dsty;
+
+ speed = (spd < 1) ? 1 : spd;
+
+ int16 deltax = endx - x;
+ if (deltax < 0) {
+ dx = -deltax;
+ xdir = -1;
+ } else {
+ dx = deltax;
+ xdir = 1;
+ }
+ int16 deltay = endy - y;
+ if (deltay < 0) {
+ dy = -deltay;
+ ydir = -1;
+ } else {
+ dy = deltay;
+ ydir = 1;
+ }
+
+ if (dx > dy) {
+ total = dy / 2;
+ xmajor = true;
+ } else {
+ total = dx / 2;
+ xmajor = false;
+ }
+
+ // move one step along line to avoid glitching
+ moveOneStep();
+}
+
+void BobSlot::moveOneStep() {
+ if (xmajor) {
+ if (x == endx) {
+ y = endy;
+ moving = false;
+ } else {
+ x += xdir;
+ total += dy;
+ if (total > dx) {
+ y += ydir;
+ total -= dx;
+ }
+ }
+ } else {
+ if (y == endy) {
+ x = endx;
+ moving = false;
+ } else {
+ y += ydir;
+ total += dx;
+ if (total > dy) {
+ x += xdir;
+ total -= dy;
+ }
+ }
+ }
+}
+
+void BobSlot::animOneStep() {
+ if (anim.string.buffer != NULL) {
+ --anim.speed;
+ if (anim.speed <= 0) {
+ // jump to next entry
+ ++anim.string.curPos;
+ uint16 nextFrame = anim.string.curPos->frame;
+ if (nextFrame == 0) {
+ anim.string.curPos = anim.string.buffer;
+ frameNum = anim.string.curPos->frame;
+ } else {
+ frameNum = nextFrame;
+ }
+ anim.speed = anim.string.curPos->speed / 4;
+ }
+ } else {
+ // normal looping animation
+ --anim.speed;
+ if (anim.speed == 0) {
+ anim.speed = anim.speedBak;
+
+ int16 nextFrame = frameNum + frameDir;
+ if (nextFrame > anim.normal.lastFrame || nextFrame < anim.normal.firstFrame) {
+ if (anim.normal.rebound) {
+ frameDir *= -1;
+ } else {
+ frameNum = anim.normal.firstFrame - 1;
+ }
+ }
+ frameNum += frameDir;
+ }
+ }
+}
+
+void BobSlot::animString(const AnimFrame *animBuf) {
+ active = true;
+ animating = true;
+ anim.string.buffer = animBuf;
+ anim.string.curPos = animBuf;
+ frameNum = animBuf->frame;
+ anim.speed = animBuf->speed / 4;
+}
+
+void BobSlot::animNormal(uint16 firstFrame, uint16 lastFrame, uint16 spd, bool rebound, bool flip) {
+ active = true;
+ animating = true;
+ frameNum = firstFrame;
+ anim.speed = spd;
+ anim.speedBak = spd;
+ anim.string.buffer = NULL;
+ anim.normal.firstFrame = firstFrame;
+ anim.normal.lastFrame = lastFrame;
+ anim.normal.rebound = rebound;
+ frameDir = 1;
+ xflip = flip;
+}
+
+void BobSlot::scaleWalkSpeed(uint16 ms) {
+ if (!xmajor) {
+ ms /= 2;
+ }
+ speed = scale * ms / 100;
+ if (speed == 0) {
+ speed = 1;
+ }
+}
+
+void BobSlot::clear() {
+ active = false;
+ xflip = false;
+ animating = false;
+ anim.string.buffer = NULL;
+ moving = false;
+ scale = 100;
+
+#ifdef PALMOS_ARM
+ Box *tmp = (Box *)&_defaultBox;
+ tmp->x1 = -1;
+ tmp->y1 = -1;
+ tmp->x2 = -1;
+ tmp->y2 = -1;
+#endif
+ box = _defaultBox;
+}
+
+static int compareBobDrawOrder(const void *a, const void *b) {
+ const BobSlot *bob1 = *(const BobSlot * const *)a;
+ const BobSlot *bob2 = *(const BobSlot * const *)b;
+ int d = bob1->y - bob2->y;
+ // As the qsort() function may reorder "equal" elements,
+ // we use the bob slot number when needed. This is required
+ // during the introduction, to hide a crate behind the clock.
+ if (d == 0) {
+ d = bob1 - bob2;
+ }
+ return d;
+}
+
+const Box Graphics::_gameScreenBox(0, 0, GAME_SCREEN_WIDTH - 1, ROOM_ZONE_HEIGHT - 1);
+const Box Graphics::_fullScreenBox(0, 0, GAME_SCREEN_WIDTH - 1, GAME_SCREEN_HEIGHT - 1);
+
+Graphics::Graphics(QueenEngine *vm)
+ : _cameraBob(0), _vm(vm) {
+ memset(_bobs, 0, sizeof(_bobs));
+ memset(_sortedBobs, 0, sizeof(_sortedBobs));
+ _sortedBobsCount = 0;
+ _shrinkBuffer.data = new uint8[ BOB_SHRINK_BUF_SIZE ];
+
+#ifdef PALMOS_ARM
+ Box *tmp1 = (Box *)&BobSlot::_defaultBox;
+ tmp1->x1 = -1;
+ tmp1->y1 = -1;
+ tmp1->x2 = -1;
+ tmp1->y2 = -1;
+ Box *tmp2 = (Box *)&_gameScreenBox;
+ tmp2->x1 = 0;
+ tmp2->y1 = 0;
+ tmp2->x2 = GAME_SCREEN_WIDTH - 1;
+ tmp2->y2 = ROOM_ZONE_HEIGHT - 1;
+ Box *tmp3 = (Box *)&_fullScreenBox;
+ tmp3->x1 = 0;
+ tmp3->y1 = 0;
+ tmp3->x2 = GAME_SCREEN_WIDTH - 1;
+ tmp3->y2 = GAME_SCREEN_HEIGHT - 1;
+#endif
+}
+
+Graphics::~Graphics() {
+ delete[] _shrinkBuffer.data;
+}
+
+void Graphics::unpackControlBank() {
+ _vm->bankMan()->load("control.BBK",17);
+ _vm->bankMan()->unpack(1, 1, 17); // Mouse pointer
+ // unpack arrows frames and change hotspot to be always on top
+ for (int i = 3; i <= 4; ++i) {
+ _vm->bankMan()->unpack(i, i, 17);
+ BobFrame *bf = _vm->bankMan()->fetchFrame(i);
+ bf->yhotspot += 200;
+ }
+ _vm->bankMan()->close(17);
+}
+
+void Graphics::setupArrows() {
+ int scrollX = _vm->display()->horizontalScroll();
+ BobSlot *arrow;
+ arrow = bob(ARROW_BOB_UP);
+ arrow->curPos(303 + 8 + scrollX, 150 + 1 + 200);
+ arrow->frameNum = 3;
+ arrow = bob(ARROW_BOB_DOWN);
+ arrow->curPos(303 + scrollX, 175 + 200);
+ arrow->frameNum = 4;
+}
+
+void Graphics::setupMouseCursor() {
+ BobFrame *bf = _vm->bankMan()->fetchFrame(1);
+ _vm->display()->setMouseCursor(bf->data, bf->width, bf->height);
+}
+
+void Graphics::drawBob(const BobSlot *bs, const BobFrame *bf, const Box *bbox, int16 x, int16 y) {
+ debug(9, "Graphics::drawBob(%d, %d, %d)", bs->frameNum, x, y);
+
+ uint16 w, h;
+ if (bs->scale < 100) {
+ shrinkFrame(bf, bs->scale);
+ bf = &_shrinkBuffer;
+ }
+ w = bf->width;
+ h = bf->height;
+
+ const Box *box = (bs->box == BobSlot::_defaultBox) ? bbox : &bs->box;
+
+ if (w != 0 && h != 0 && box->intersects(x, y, w, h)) {
+ uint8 *src = bf->data;
+ uint16 x_skip = 0;
+ uint16 y_skip = 0;
+ uint16 w_new = w;
+ uint16 h_new = h;
+
+ // compute bounding box intersection with frame
+ if (x < box->x1) {
+ x_skip = box->x1 - x;
+ w_new -= x_skip;
+ x = box->x1;
+ }
+
+ if (y < box->y1) {
+ y_skip = box->y1 - y;
+ h_new -= y_skip;
+ y = box->y1;
+ }
+
+ if (x + w_new > box->x2 + 1) {
+ w_new = box->x2 - x + 1;
+ }
+
+ if (y + h_new > box->y2 + 1) {
+ h_new = box->y2 - y + 1;
+ }
+
+ src += w * y_skip;
+ if (!bs->xflip) {
+ src += x_skip;
+ } else {
+ src += w - w_new - x_skip;
+ x += w_new - 1;
+ }
+ _vm->display()->drawBobSprite(src, x, y, w_new, h_new, w, bs->xflip);
+ }
+}
+
+void Graphics::drawInventoryItem(uint32 frameNum, uint16 x, uint16 y) {
+ if (frameNum != 0) {
+ BobFrame *bf = _vm->bankMan()->fetchFrame(frameNum);
+ _vm->display()->drawInventoryItem(bf->data, x, y, bf->width, bf->height);
+ } else {
+ _vm->display()->drawInventoryItem(NULL, x, y, 32, 32);
+ }
+}
+
+void Graphics::pasteBob(uint16 objNum, uint16 image) {
+ GraphicData *pgd = _vm->logic()->graphicData(objNum);
+ _vm->bankMan()->unpack(pgd->firstFrame, image, 15);
+ BobFrame *bf = _vm->bankMan()->fetchFrame(image);
+ _vm->display()->drawBobPasteDown(bf->data, pgd->x, pgd->y, bf->width, bf->height);
+ _vm->bankMan()->eraseFrame(image);
+}
+
+void Graphics::shrinkFrame(const BobFrame *bf, uint16 percentage) {
+ // computing new size, rounding to upper value
+ uint16 new_w = (bf->width * percentage + 50) / 100;
+ uint16 new_h = (bf->height * percentage + 50) / 100;
+ assert(new_w * new_h < BOB_SHRINK_BUF_SIZE);
+
+ if (new_w != 0 && new_h != 0) {
+ _shrinkBuffer.width = new_w;
+ _shrinkBuffer.height = new_h;
+
+ uint16 x, y;
+ uint16 sh[GAME_SCREEN_WIDTH];
+ for (x = 0; x < MAX(new_h, new_w); ++x) {
+ sh[x] = x * 100 / percentage;
+ }
+ uint8* dst = _shrinkBuffer.data;
+ for (y = 0; y < new_h; ++y) {
+ uint8 *p = bf->data + sh[y] * bf->width;
+ for (x = 0; x < new_w; ++x) {
+ *dst++ = *(p + sh[x]);
+ }
+ }
+ }
+}
+
+void Graphics::sortBobs() {
+ _sortedBobsCount = 0;
+
+ // animate/move the bobs
+ for (int32 i = 0; i < ARRAYSIZE(_bobs); ++i) {
+ BobSlot *pbs = &_bobs[i];
+ if (pbs->active) {
+ _sortedBobs[_sortedBobsCount] = pbs;
+ ++_sortedBobsCount;
+
+ if (pbs->animating) {
+ pbs->animOneStep();
+ if (pbs->frameNum > 500) { // SFX frame
+ _vm->sound()->playSfx(_vm->logic()->currentRoomSfx(), false);
+ pbs->frameNum -= 500;
+ }
+ }
+ if (pbs->moving) {
+ int16 j;
+ for (j = 0; pbs->moving && j < pbs->speed; ++j) {
+ pbs->moveOneStep();
+ }
+ }
+ }
+ }
+ qsort(_sortedBobs, _sortedBobsCount, sizeof(BobSlot *), compareBobDrawOrder);
+}
+
+void Graphics::drawBobs() {
+ const Box *bobBox = _vm->display()->fullscreen() ? &_fullScreenBox : &_gameScreenBox;
+ for (int i = 0; i < _sortedBobsCount; ++i) {
+ BobSlot *pbs = _sortedBobs[i];
+ if (pbs->active) {
+
+ BobFrame *pbf = _vm->bankMan()->fetchFrame(pbs->frameNum);
+ uint16 xh, yh, x, y;
+
+ xh = pbf->xhotspot;
+ yh = pbf->yhotspot;
+
+ if (pbs->xflip) {
+ xh = pbf->width - xh;
+ }
+
+ // adjusts hot spots when object is scaled
+ if (pbs->scale != 100) {
+ xh = (xh * pbs->scale) / 100;
+ yh = (yh * pbs->scale) / 100;
+ }
+
+ // adjusts position to hot-spot and screen scroll
+ x = pbs->x - xh - _vm->display()->horizontalScroll();
+ y = pbs->y - yh;
+
+ drawBob(pbs, pbf, bobBox, x, y);
+ }
+ }
+}
+
+void Graphics::clearBobs() {
+ for (int32 i = 0; i < ARRAYSIZE(_bobs); ++i) {
+ _bobs[i].clear();
+ }
+}
+
+void Graphics::stopBobs() {
+ for (int32 i = 0; i < ARRAYSIZE(_bobs); ++i) {
+ _bobs[i].moving = false;
+ }
+}
+
+BobSlot *Graphics::bob(int index) {
+ assert(index < MAX_BOBS_NUMBER);
+ return &_bobs[index];
+}
+
+void Graphics::setBobText(const BobSlot *pbs, const char *text, int textX, int textY, int color, int flags) {
+
+ if (text[0] == '\0')
+ return;
+
+ // Duplicate string and append zero if needed
+
+ char textCopy[MAX_STRING_SIZE];
+
+ int length = strlen(text);
+ memcpy(textCopy, text, length);
+
+ if (textCopy[length - 1] >= 'A')
+ textCopy[length++] = '.';
+
+ textCopy[length] = '\0';
+
+ // Split text into lines
+
+ char lines[8][MAX_STRING_SIZE];
+ int lineCount = 0;
+ int lineLength = 0;
+ int i;
+
+ // Hebrew strings are written from right to left and should be cut
+ // to lines in reverse
+ if (_vm->resource()->getLanguage() == HEBREW) {
+ for (i = length - 1; i >= 0; i--) {
+ lineLength++;
+
+ if ((lineLength > 20 && textCopy[i] == ' ') || i == 0) {
+ memcpy(lines[lineCount], textCopy + i, lineLength);
+ lines[lineCount][lineLength] = '\0';
+ lineCount++;
+ lineLength = 0;
+ }
+ }
+ } else {
+ for (i = 0; i < length; i++) {
+ lineLength++;
+
+ if ((lineLength > 20 && textCopy[i] == ' ') || i == (length-1)) {
+ memcpy(lines[lineCount], textCopy + i + 1 - lineLength, lineLength);
+ lines[lineCount][lineLength] = '\0';
+ lineCount++;
+ lineLength = 0;
+ }
+ }
+ }
+
+ // Find width of widest line
+
+ int maxLineWidth = 0;
+
+ for (i = 0; i < lineCount; i++) {
+ int width = _vm->display()->textWidth(lines[i]);
+ if (maxLineWidth < width)
+ maxLineWidth = width;
+ }
+
+ // Calc text position
+
+ short x, y, width, height;
+
+ if (flags) {
+ if (flags == 2)
+ x = 160 - maxLineWidth / 2;
+ else
+ x = textX;
+
+ y = textY;
+
+ width = 0;
+ } else {
+ x = pbs->x;
+ y = pbs->y;
+
+ BobFrame *pbf = _vm->bankMan()->fetchFrame(pbs->frameNum);
+
+ width = (pbf->width * pbs->scale) / 100;
+ height = (pbf->height * pbs->scale) / 100;
+
+ y = y - height - 16 - lineCount * 9;
+ }
+
+ x -= _vm->display()->horizontalScroll();
+
+ if (y < 0) {
+ y = 0;
+
+ if (x < 160)
+ x += width / 2;
+ else
+ x -= width / 2 + maxLineWidth;
+ } else if (!flags)
+ x -= maxLineWidth / 2;
+
+ if (x < 0)
+ x = 4;
+ else if ((x + maxLineWidth) > 320)
+ x = 320 - maxLineWidth - 4;
+
+ _vm->display()->textCurrentColor(color);
+
+ for (i = 0; i < lineCount; i++) {
+ int lineX = x + (maxLineWidth - _vm->display()->textWidth(lines[i])) / 2;
+
+ debug(7, "Setting text '%s' at (%i, %i)", lines[i], lineX, y + 9 * i);
+ _vm->display()->setText(lineX, y + 9 * i, lines[i]);
+ }
+}
+
+void Graphics::handleParallax(uint16 roomNum) {
+ uint16 screenScroll = _vm->display()->horizontalScroll();
+ switch (roomNum) {
+ case ROOM_AMAZON_HIDEOUT:
+ _bobs[8].x = 250 - screenScroll / 2;
+ break;
+ case ROOM_TEMPLE_MAZE_5:
+ _bobs[5].x = 410 - screenScroll / 2;
+ _bobs[6].x = 790 - screenScroll / 2;
+ break;
+ case ROOM_TEMPLE_OUTSIDE:
+ _bobs[5].x = 320 - screenScroll / 2;
+ break;
+ case ROOM_TEMPLE_TREE:
+ _bobs[5].x = 280 - screenScroll / 2;
+ break;
+ case ROOM_VALLEY_CARCASS:
+ _bobs[5].x = 600 - screenScroll / 2;
+ break;
+ case ROOM_UNUSED_INTRO_1:
+ _bobs[5].x = 340 - screenScroll / 2;
+ _bobs[6].x = 50 - screenScroll / 2;
+ _bobs[7].x = 79 - screenScroll / 2;
+ break;
+ case ROOM_CAR_CHASE:
+ _vm->bam()->updateCarAnimation();
+ break;
+ case ROOM_FINAL_FIGHT:
+ _vm->bam()->updateFightAnimation();
+ break;
+ case ROOM_INTRO_RITA_JOE_HEADS:
+ _cameraBob = -1;
+ if (screenScroll < 80) {
+ _vm->display()->horizontalScroll(screenScroll + 4);
+ // Joe's body and head
+ _bobs[ 1].x += 4;
+ _bobs[20].x += 4;
+ // Rita's body and head
+ _bobs[ 2].x -= 2;
+ _bobs[21].x -= 2;
+ }
+ break;
+ case ROOM_INTRO_EXPLOSION:
+ _bobs[21].x += 2;
+ _bobs[21].y += 2;
+ break;
+ }
+}
+
+void Graphics::setupNewRoom(const char *room, uint16 roomNum, int16 *furniture, uint16 furnitureCount) {
+ // reset sprites table
+ clearBobs();
+
+ // load/setup objects associated to this room
+ char filename[20];
+ sprintf(filename, "%s.BBK", room);
+ _vm->bankMan()->load(filename, 15);
+
+ _numFrames = FRAMES_JOE + 1;
+ setupRoomFurniture(furniture, furnitureCount);
+ setupRoomObjects();
+
+ if (roomNum >= 90) {
+ putCameraOnBob(0);
+ }
+}
+
+void Graphics::setBobCutawayAnim(uint16 bobNum, bool xflip, const AnimFrame *af, uint8 frameCount) {
+ assert(bobNum < 21 && frameCount < 30);
+ memcpy(_cutAnim[bobNum], af, sizeof(AnimFrame) * frameCount);
+ _bobs[bobNum].xflip = xflip;
+ _bobs[bobNum].animString(_cutAnim[bobNum]);
+}
+
+void Graphics::fillAnimBuffer(const char *anim, AnimFrame *af) {
+ for (;;) {
+ // anim frame format is "%3hu,%3hu," (frame number, frame speed)
+ af->frame = atoi(anim);
+ anim += 4;
+ af->speed = atoi(anim);
+ anim += 4;
+ if (af->frame == 0)
+ break;
+ ++af;
+ }
+}
+
+uint16 Graphics::countAnimFrames(const char *anim) {
+ AnimFrame afbuf[30];
+ fillAnimBuffer(anim, afbuf);
+
+ bool frames[256];
+ memset(frames, 0, sizeof(frames));
+ uint16 count = 0;
+ AnimFrame *af = afbuf;
+ for ( ; af->frame != 0; ++af) {
+ uint16 frameNum = af->frame;
+ if (frameNum > 500) {
+ frameNum -= 500;
+ }
+ if (!frames[frameNum]) {
+ frames[frameNum] = true;
+ ++count;
+ }
+ }
+ return count;
+}
+
+void Graphics::setupObjectAnim(const GraphicData *gd, uint16 firstImage, uint16 bobNum, bool visible) {
+ int16 tempFrames[20];
+ memset(tempFrames, 0, sizeof(tempFrames));
+ uint16 numTempFrames = 0;
+ uint16 i, j;
+ for (i = 1; i <= _vm->logic()->graphicAnimCount(); ++i) {
+ const GraphicAnim *pga = _vm->logic()->graphicAnim(i);
+ if (pga->keyFrame == gd->firstFrame) {
+ int16 frame = pga->frame;
+ if (frame > 500) { // SFX
+ frame -= 500;
+ }
+ bool foundMatchingFrame = false;
+ for (j = 0; j < numTempFrames; ++j) {
+ if (tempFrames[j] == frame) {
+ foundMatchingFrame = true;
+ break;
+ }
+ }
+ if (!foundMatchingFrame) {
+ assert(numTempFrames < 20);
+ tempFrames[numTempFrames] = frame;
+ ++numTempFrames;
+ }
+ }
+ }
+
+ // sort found frames ascending
+ bool swap = true;
+ while (swap) {
+ swap = false;
+ for (i = 0; i < numTempFrames - 1; ++i) {
+ if (tempFrames[i] > tempFrames[i + 1]) {
+ SWAP(tempFrames[i], tempFrames[i + 1]);
+ swap = true;
+ }
+ }
+ }
+
+ // queen.c l.962-980 / l.1269-1294
+ for (i = 0; i < gd->lastFrame; ++i) {
+ _vm->bankMan()->unpack(ABS(tempFrames[i]), firstImage + i, 15);
+ }
+ BobSlot *pbs = bob(bobNum);
+ pbs->animating = false;
+ if (visible) {
+ pbs->curPos(gd->x, gd->y);
+ if (tempFrames[0] < 0) {
+ pbs->xflip = true;
+ }
+ AnimFrame *paf = _newAnim[bobNum];
+ for (i = 1; i <= _vm->logic()->graphicAnimCount(); ++i) {
+ const GraphicAnim *pga = _vm->logic()->graphicAnim(i);
+ if (pga->keyFrame == gd->firstFrame) {
+ uint16 frameNr = 0;
+ for (j = 1; j <= gd->lastFrame; ++j) {
+ if (pga->frame > 500) {
+ if (pga->frame - 500 == tempFrames[j - 1]) {
+ frameNr = j + firstImage - 1 + 500;
+ }
+ } else if (pga->frame == tempFrames[j - 1]) {
+ frameNr = j + firstImage - 1;
+ }
+ }
+ paf->frame = frameNr;
+ paf->speed = pga->speed;
+ ++paf;
+ }
+ }
+ paf->frame = 0;
+ paf->speed = 0;
+ pbs->animString(_newAnim[bobNum]);
+ }
+}
+
+uint16 Graphics::setupPersonAnim(const ActorData *ad, const char *anim, uint16 curImage) {
+ debug(9, "Graphics::setupPersonAnim(%s, %d)", anim, curImage);
+ _personFrames[ad->bobNum] = curImage + 1;
+
+ AnimFrame *animFrames = _newAnim[ad->bobNum];
+ fillAnimBuffer(anim, animFrames);
+ uint16 frameCount[256];
+ memset(frameCount, 0, sizeof(frameCount));
+ AnimFrame *af = animFrames;
+ for ( ; af->frame != 0; ++af) {
+ uint16 frameNum = af->frame;
+ if (frameNum > 500) {
+ frameNum -= 500;
+ }
+ if (!frameCount[frameNum]) {
+ frameCount[frameNum] = 1;
+ }
+ }
+ uint16 i, n = 1;
+ for (i = 1; i < 256; ++i) {
+ if (frameCount[i]) {
+ frameCount[i] = n;
+ ++n;
+ }
+ }
+ af = animFrames;
+ for ( ; af->frame != 0; ++af) {
+ if (af->frame > 500) {
+ af->frame = curImage + frameCount[af->frame - 500] + 500;
+ } else {
+ af->frame = curImage + frameCount[af->frame];
+ }
+ }
+
+ // unpack necessary frames
+ for (i = 1; i < 256; ++i) {
+ if (frameCount[i]) {
+ ++curImage;
+ _vm->bankMan()->unpack(i, curImage, ad->bankNum);
+ }
+ }
+
+ // start animation
+ bob(ad->bobNum)->animString(animFrames);
+ return curImage;
+}
+
+void Graphics::resetPersonAnim(uint16 bobNum) {
+ if (_newAnim[bobNum][0].frame != 0) {
+ bob(bobNum)->animString(_newAnim[bobNum]);
+ }
+}
+
+void Graphics::erasePersonAnim(uint16 bobNum) {
+ _newAnim[bobNum][0].frame = 0;
+ BobSlot *pbs = bob(bobNum);
+ pbs->animating = false;
+ pbs->anim.string.buffer = NULL;
+}
+
+void Graphics::eraseAllAnims() {
+ for (int i = 1; i <= 16; ++i) {
+ _newAnim[i][0].frame = 0;
+ }
+}
+
+uint16 Graphics::refreshObject(uint16 obj) {
+ debug(6, "Graphics::refreshObject(%X)", obj);
+ uint16 curImage = _numFrames;
+
+ ObjectData *pod = _vm->logic()->objectData(obj);
+ if (pod->image == 0) {
+ return curImage;
+ }
+
+ // check the object is in the current room
+ if (pod->room != _vm->logic()->currentRoom()) {
+ return curImage;
+ }
+
+ // find bob for the object
+ uint16 curBob = _vm->logic()->findBob(obj);
+ BobSlot *pbs = bob(curBob);
+
+ if (pod->image == -3 || pod->image == -4) {
+ // a person object
+ if (pod->name <= 0) {
+ pbs->clear();
+ } else {
+ // find person number
+ uint16 pNum = _vm->logic()->findPersonNumber(obj, _vm->logic()->currentRoom());
+ curImage = _personFrames[pNum] - 1;
+ if (_personFrames[pNum] == 0) {
+ _personFrames[pNum] = curImage = _numFrames;
+ }
+ curImage = setupPerson(obj - _vm->logic()->currentRoomData(), curImage);
+ }
+ return curImage;
+ }
+
+ // find frame used for object
+ curImage = _vm->logic()->findFrame(obj);
+
+ if (pod->name < 0 || pod->image < 0) {
+ // object is hidden or disabled
+ pbs->clear();
+ return curImage;
+ }
+
+ int image = pod->image;
+ if (image > 5000) {
+ image -= 5000;
+ }
+
+ GraphicData *pgd = _vm->logic()->graphicData(image);
+ bool rebound = false;
+ int16 lastFrame = pgd->lastFrame;
+ if (lastFrame < 0) {
+ lastFrame = -lastFrame;
+ rebound = true;
+ }
+ if (pgd->firstFrame < 0) {
+ setupObjectAnim(pgd, curImage, curBob, pod->name != 0);
+ curImage += pgd->lastFrame - 1;
+ } else if (lastFrame != 0) {
+ // turn on an animated bob
+ pbs->animating = false;
+ uint16 firstImage = curImage;
+ --curImage;
+ uint16 j;
+ for (j = pgd->firstFrame; j <= lastFrame; ++j) {
+ ++curImage;
+ _vm->bankMan()->unpack(j, curImage, 15);
+ }
+ pbs->curPos(pgd->x, pgd->y);
+ pbs->frameNum = firstImage;
+ if (pgd->speed > 0) {
+ pbs->animNormal(firstImage, curImage, pgd->speed / 4, rebound, false);
+ }
+ } else {
+ _vm->bankMan()->unpack(pgd->firstFrame, curImage, 15);
+ pbs->curPos(pgd->x, pgd->y);
+ pbs->frameNum = curImage;
+ }
+
+ return curImage;
+}
+
+void Graphics::setupRoomFurniture(int16 *furniture, uint16 furnitureCount) {
+ uint16 i;
+ uint16 curImage = FRAMES_JOE;
+
+ // unpack the static bobs
+ _numFurnitureStatic = 0;
+ for (i = 1; i <= furnitureCount; ++i) {
+ int16 obj = furniture[i];
+ if (obj > 0 && obj <= 5000) {
+ GraphicData *pgd = _vm->logic()->graphicData(obj);
+ if (pgd->lastFrame == 0) {
+ ++_numFurnitureStatic;
+ ++curImage;
+ _vm->bankMan()->unpack(pgd->firstFrame, curImage, 15);
+ ++_numFrames;
+ BobSlot *pbs = bob(19 + _numFurnitureStatic);
+ pbs->curPos(pgd->x, pgd->y);
+ pbs->frameNum = curImage;
+ }
+ }
+ }
+
+ // unpack the animated bobs
+ _numFurnitureAnimated = 0;
+ _numFurnitureAnimatedLen = 0;
+ uint16 curBob = 0;
+ for (i = 1; i <= furnitureCount; ++i) {
+ int16 obj = furniture[i];
+ if (obj > 0 && obj <= 5000) {
+ GraphicData *pgd = _vm->logic()->graphicData(obj);
+
+ bool rebound = false;
+ int16 lastFrame = pgd->lastFrame;
+ if (lastFrame < 0) {
+ rebound = true;
+ lastFrame = -lastFrame;
+ }
+
+ if (lastFrame > 0) {
+ _numFurnitureAnimatedLen += lastFrame - pgd->firstFrame + 1;
+ ++_numFurnitureAnimated;
+ uint16 image = curImage + 1;
+ int k;
+ for (k = pgd->firstFrame; k <= lastFrame; ++k) {
+ ++curImage;
+ _vm->bankMan()->unpack(k, curImage, 15);
+ ++_numFrames;
+ }
+ BobSlot *pbs = bob(5 + curBob);
+ pbs->animNormal(image, curImage, pgd->speed / 4, rebound, false);
+ pbs->curPos(pgd->x, pgd->y);
+ ++curBob;
+ }
+ }
+ }
+
+ // unpack the paste downs
+ for (i = 1; i <= furnitureCount; ++i) {
+ if (furniture[i] > 5000) {
+ pasteBob(furniture[i] - 5000, curImage + 1);
+ }
+ }
+}
+
+void Graphics::setupRoomObjects() {
+ uint16 i;
+ // furniture frames are reserved in ::setupRoomFurniture(), we append objects
+ // frames after the furniture ones.
+ uint16 curImage = FRAMES_JOE + _numFurnitureStatic + _numFurnitureAnimatedLen;
+ uint16 firstRoomObj = _vm->logic()->currentRoomData() + 1;
+ uint16 lastRoomObj = _vm->logic()->roomData(_vm->logic()->currentRoom() + 1);
+ uint16 numObjectStatic = 0;
+ uint16 numObjectAnimated = 0;
+ uint16 curBob;
+
+ // invalidates all Bobs for persons (except Joe's one)
+ for (i = 1; i <= 3; ++i) {
+ _bobs[i].active = false;
+ }
+
+ // static/animated Bobs
+ for (i = firstRoomObj; i <= lastRoomObj; ++i) {
+ ObjectData *pod = _vm->logic()->objectData(i);
+ // setup blanks bobs for turned off objects (in case
+ // you turn them on again)
+ if (pod->image == -1) {
+ // static OFF Bob
+ curBob = 20 + _numFurnitureStatic + numObjectStatic;
+ ++numObjectStatic;
+ // create a blank frame for the OFF object
+ ++_numFrames;
+ ++curImage;
+ } else if (pod->image == -2) {
+ // animated OFF Bob
+ curBob = 5 + _numFurnitureAnimated + numObjectAnimated;
+ ++numObjectAnimated;
+ } else if (pod->image > 0 && pod->image < 5000) {
+ GraphicData *pgd = _vm->logic()->graphicData(pod->image);
+ int16 lastFrame = pgd->lastFrame;
+ bool rebound = false;
+ if (lastFrame < 0) {
+ lastFrame = -lastFrame;
+ rebound = true;
+ }
+ if (pgd->firstFrame < 0) {
+ curBob = 5 + _numFurnitureAnimated;
+ setupObjectAnim(pgd, curImage + 1, curBob + numObjectAnimated, pod->name > 0);
+ curImage += pgd->lastFrame;
+ ++numObjectAnimated;
+ } else if (lastFrame != 0) {
+ // animated objects
+ uint16 j;
+ uint16 firstFrame = curImage + 1;
+ for (j = pgd->firstFrame; j <= lastFrame; ++j) {
+ ++curImage;
+ _vm->bankMan()->unpack(j, curImage, 15);
+ ++_numFrames;
+ }
+ curBob = 5 + _numFurnitureAnimated + numObjectAnimated;
+ if (pod->name > 0) {
+ BobSlot *pbs = bob(curBob);
+ pbs->curPos(pgd->x, pgd->y);
+ pbs->frameNum = firstFrame;
+ if (pgd->speed > 0) {
+ pbs->animNormal(firstFrame, curImage, pgd->speed / 4, rebound, false);
+ }
+ }
+ ++numObjectAnimated;
+ } else {
+ // static objects
+ curBob = 20 + _numFurnitureStatic + numObjectStatic;
+ ++curImage;
+ bob(curBob)->clear();
+
+ _vm->bankMan()->unpack(pgd->firstFrame, curImage, 15);
+ ++_numFrames;
+ if (pod->name > 0) {
+ BobSlot *pbs = bob(curBob);
+ pbs->curPos(pgd->x, pgd->y);
+ pbs->frameNum = curImage;
+ }
+ ++numObjectStatic;
+ }
+ }
+ }
+
+ // persons Bobs
+ for (i = firstRoomObj; i <= lastRoomObj; ++i) {
+ ObjectData *pod = _vm->logic()->objectData(i);
+ if (pod->image == -3 || pod->image == -4) {
+ debug(6, "Graphics::setupRoomObjects() - Setting up person %X, name=%X", i, pod->name);
+ uint16 noun = i - _vm->logic()->currentRoomData();
+ if (pod->name > 0) {
+ curImage = setupPerson(noun, curImage);
+ } else {
+ curImage = allocPerson(noun, curImage);
+ }
+ }
+ }
+
+ // paste downs list
+ ++curImage;
+ _numFrames = curImage;
+ for (i = firstRoomObj; i <= lastRoomObj; ++i) {
+ ObjectData *pod = _vm->logic()->objectData(i);
+ if (pod->name > 0 && pod->image > 5000) {
+ pasteBob(pod->image - 5000, curImage);
+ }
+ }
+}
+
+uint16 Graphics::setupPerson(uint16 noun, uint16 curImage) {
+ if (noun == 0) {
+ warning("Trying to setup person 0");
+ return curImage;
+ }
+
+ Person p;
+ if (!_vm->logic()->initPerson(noun, "", true, &p)) {
+ return curImage;
+ }
+
+ const ActorData *pad = p.actor;
+ uint16 scale = 100;
+ uint16 a = _vm->grid()->findAreaForPos(GS_ROOM, pad->x, pad->y);
+ if (a != 0) {
+ // person is not standing in the area box, scale it accordingly
+ scale = _vm->grid()->area(_vm->logic()->currentRoom(), a)->calcScale(pad->y);
+ }
+
+ _vm->bankMan()->unpack(pad->bobFrameStanding, p.bobFrame, p.actor->bankNum);
+ uint16 obj = _vm->logic()->currentRoomData() + noun;
+ BobSlot *pbs = bob(pad->bobNum);
+ pbs->curPos(pad->x, pad->y);
+ pbs->scale = scale;
+ pbs->frameNum = p.bobFrame;
+ pbs->xflip = (_vm->logic()->objectData(obj)->image == -3); // person is facing left
+
+ debug(6, "Graphics::setupPerson(%d, %d) - bob = %d name = %s", noun, curImage, pad->bobNum, p.name);
+
+ if (p.anim != NULL) {
+ curImage = setupPersonAnim(pad, p.anim, curImage);
+ } else {
+ erasePersonAnim(pad->bobNum);
+ }
+ return curImage;
+}
+
+uint16 Graphics::allocPerson(uint16 noun, uint16 curImage) {
+ Person p;
+ if (_vm->logic()->initPerson(noun, "", false, &p) && p.anim != NULL) {
+ curImage += countAnimFrames(p.anim);
+ _personFrames[p.actor->bobNum] = curImage + 1;
+ }
+ return curImage;
+}
+
+void Graphics::update(uint16 room) {
+ sortBobs();
+ if (_cameraBob >= 0) {
+ _vm->display()->horizontalScrollUpdate(_bobs[_cameraBob].x);
+ }
+ handleParallax(room);
+ _vm->display()->prepareUpdate();
+ drawBobs();
+}
+
+
+BamScene::BamScene(QueenEngine *vm)
+ : _flag(F_STOP), _screenShaked(false), _fightData(_fight1Data), _vm(vm) {
+}
+
+void BamScene::playSfx() {
+ // FIXME - we don't play all sfx here. This is only necessary for
+ // the fight bam, where the number of 'sfx bam frames' is too much
+ // important / too much closer. The original game does not have
+ // this problem since their playSfx() function returns immediately
+ // if a sound is already being played.
+ if (_lastSoundIndex == 0 || _index - _lastSoundIndex >= SFX_SKIP) {
+ _vm->sound()->playSfx(_vm->logic()->currentRoomSfx(), false);
+ _lastSoundIndex = _index;
+ }
+}
+
+void BamScene::prepareAnimation() {
+ _obj1 = _vm->graphics()->bob(BOB_OBJ1);
+ _obj1->clear();
+ _obj1->active = true;
+
+ _obj2 = _vm->graphics()->bob(BOB_OBJ2);
+ _obj2->clear();
+ _obj2->active = true;
+
+ _objfx = _vm->graphics()->bob(BOB_FX);
+ _objfx->clear();
+ _objfx->active = true;
+
+ _index = 0;
+ _lastSoundIndex = 0;
+}
+
+void BamScene::updateCarAnimation() {
+ if (_flag != F_STOP) {
+ const BamDataBlock *bdb = &_carData[_index];
+
+ // Truck
+ _obj1->curPos(bdb->obj1.x, bdb->obj1.y);
+ _obj1->frameNum = 40 + bdb->obj1.frame;
+
+ // Rico
+ _obj2->curPos(bdb->obj2.x, bdb->obj2.y);
+ _obj2->frameNum = 30 + bdb->obj2.frame;
+
+ // FX
+ _objfx->curPos(bdb->fx.x, bdb->fx.y);
+ _objfx->frameNum = 41 + bdb->fx.frame;
+
+ if (bdb->sfx < 0) {
+ _vm->sound()->playSong(-bdb->sfx);
+ }
+
+ if (bdb->sfx == 99) {
+ _lastSoundIndex = _index = 0;
+ } else {
+ ++_index;
+ }
+
+ if (bdb->sfx == 2) {
+ playSfx();
+ }
+ }
+}
+
+void BamScene::updateFightAnimation() {
+ static const BamDataBlock *fightDataBlocks[] = {
+ _fight1Data,
+ _fight2Data,
+ _fight3Data
+ };
+ if (_flag != F_STOP) {
+ const BamDataBlock *bdb = &_fightData[_index];
+
+ // Frank
+ _obj1->curPos(bdb->obj1.x, bdb->obj1.y);
+ _obj1->frameNum = 40 + ABS(bdb->obj1.frame);
+ _obj1->xflip = (bdb->obj1.frame < 0);
+
+ // Robot
+ _obj2->curPos(bdb->obj2.x, bdb->obj2.y);
+ _obj2->frameNum = 40 + ABS(bdb->obj2.frame);
+ _obj2->xflip = (bdb->obj2.frame < 0);
+
+ // FX
+ _objfx->curPos(bdb->fx.x, bdb->fx.y);
+ _objfx->frameNum = 40 + ABS(bdb->fx.frame);
+ _objfx->xflip = (bdb->fx.frame < 0);
+
+ if (bdb->sfx < 0) {
+ _vm->sound()->playSong(-bdb->sfx);
+ }
+
+ ++_index;
+ switch (bdb->sfx) {
+ case 0: // nothing, so reset shaked screen if necessary
+ if (_screenShaked) {
+ _vm->display()->shake(true);
+ _screenShaked = false;
+ }
+ break;
+ case 1: // shake screen
+ _vm->display()->shake(false);
+ _screenShaked = true;
+ break;
+ case 2: // play background sfx
+ playSfx();
+ break;
+ case 3: // play background sfx and shake screen
+ playSfx();
+ _vm->display()->shake(false);
+ _screenShaked = true;
+ break;
+ case 99: // end of BAM data
+ _lastSoundIndex = _index = 0;
+ _fightData = fightDataBlocks[_vm->randomizer.getRandomNumber(2)];
+ if (_flag == F_REQ_STOP) {
+ _flag = F_STOP;
+ }
+ break;
+ }
+ }
+}
+
+void BamScene::saveState(byte *&ptr) {
+ WRITE_BE_UINT16(ptr, _flag); ptr += 2;
+}
+
+void BamScene::loadState(uint32 ver, byte *&ptr) {
+ _flag = READ_BE_UINT16(ptr); ptr += 2;
+}
+
+#ifndef PALMOS_68K
+const BamScene::BamDataBlock BamScene::_carData[] = {
+ { { 310, 105, 1 }, { 314, 106, 17 }, { 366, 101, 1 }, 0 },
+ { { 303, 105, 1 }, { 307, 106, 17 }, { 214, 0, 10 }, 0 },
+ { { 297, 104, 1 }, { 301, 105, 17 }, { 214, 0, 10 }, 0 },
+ { { 294, 103, 1 }, { 298, 104, 17 }, { 214, 0, 10 }, 0 },
+ { { 291, 102, 1 }, { 295, 103, 18 }, { 214, 0, 10 }, 0 },
+ { { 287, 101, 1 }, { 291, 102, 18 }, { 266, 51, 10 }, 2 },
+ { { 283, 100, 1 }, { 287, 101, 19 }, { 279, 47, 11 }, 0 },
+ { { 279, 99, 1 }, { 283, 100, 20 }, { 294, 46, 12 }, 0 },
+ { { 274, 98, 1 }, { 278, 99, 20 }, { 305, 44, 13 }, 0 },
+ { { 269, 98, 1 }, { 273, 99, 20 }, { 320, 42, 14 }, 0 },
+ { { 264, 98, 1 }, { 268, 99, 17 }, { 214, 0, 10 }, 0 },
+ { { 261, 98, 1 }, { 265, 99, 17 }, { 214, 0, 10 }, 0 },
+ { { 259, 98, 1 }, { 263, 99, 17 }, { 214, 0, 10 }, 0 },
+ { { 258, 98, 1 }, { 262, 99, 17 }, { 214, 0, 10 }, 0 },
+ { { 257, 98, 2 }, { 260, 99, 17 }, { 214, 0, 10 }, 0 },
+ { { 255, 99, 3 }, { 258, 100, 17 }, { 214, 0, 10 }, 0 },
+ { { 258, 99, 4 }, { 257, 100, 17 }, { 214, 0, 10 }, 0 },
+ { { 264, 102, 4 }, { 263, 103, 17 }, { 214, 0, 10 }, 0 },
+ { { 272, 105, 5 }, { 274, 106, 17 }, { 214, 0, 10 }, 0 },
+ { { 276, 107, 5 }, { 277, 108, 17 }, { 214, 0, 10 }, 0 },
+ { { 283, 108, 5 }, { 284, 109, 17 }, { 214, 0, 10 }, 0 },
+ { { 288, 109, 5 }, { 288, 110, 17 }, { 214, 0, 10 }, 0 },
+ { { 293, 110, 5 }, { 293, 111, 18 }, { 266, 59, 10 }, 2 },
+ { { 298, 110, 5 }, { 299, 111, 18 }, { 277, 56, 11 }, 0 },
+ { { 303, 110, 5 }, { 304, 111, 19 }, { 285, 55, 12 }, 0 },
+ { { 308, 110, 4 }, { 307, 111, 20 }, { 296, 54, 13 }, 0 },
+ { { 309, 110, 3 }, { 312, 111, 20 }, { 304, 53, 14 }, 0 },
+ { { 310, 110, 3 }, { 313, 111, 20 }, { 214, 0, 10 }, 0 },
+ { { 311, 110, 3 }, { 314, 111, 17 }, { 214, 0, 10 }, 0 },
+ { { 309, 110, 2 }, { 312, 111, 17 }, { 214, 0, 10 }, 0 },
+ { { 304, 111, 2 }, { 307, 112, 17 }, { 214, 0, 10 }, 0 },
+ { { 300, 110, 2 }, { 303, 111, 17 }, { 214, 0, 10 }, 0 },
+ { { 296, 109, 2 }, { 299, 110, 17 }, { 214, 0, 10 }, 0 },
+ { { 292, 108, 1 }, { 296, 109, 17 }, { 214, 0, 10 }, 0 },
+ { { 285, 107, 2 }, { 289, 108, 17 }, { 214, 0, 10 }, 0 },
+ { { 282, 107, 3 }, { 285, 108, 17 }, { 214, 0, 10 }, 0 },
+ { { 278, 107, 4 }, { 277, 108, 18 }, { 214, 0, 10 }, 0 },
+ { { 279, 108, 4 }, { 278, 109, 18 }, { 252, 57, 10 }, 2 },
+ { { 281, 108, 5 }, { 283, 109, 18 }, { 265, 55, 11 }, 0 },
+ { { 284, 109, 5 }, { 285, 110, 19 }, { 277, 55, 12 }, 0 },
+ { { 287, 110, 5 }, { 288, 111, 20 }, { 288, 54, 13 }, 0 },
+ { { 289, 111, 5 }, { 290, 112, 20 }, { 299, 54, 14 }, 0 },
+ { { 291, 112, 4 }, { 290, 113, 20 }, { 214, 0, 10 }, 0 },
+ { { 293, 113, 3 }, { 295, 114, 17 }, { 214, 0, 10 }, 0 },
+ { { 296, 114, 2 }, { 299, 115, 17 }, { 214, 0, 10 }, 0 },
+ { { 295, 115, 2 }, { 298, 116, 17 }, { 214, 0, 10 }, 0 },
+ { { 293, 116, 1 }, { 297, 117, 17 }, { 214, 0, 10 }, 0 },
+ { { 289, 116, 1 }, { 292, 117, 17 }, { 214, 0, 10 }, 0 },
+ { { 285, 115, 1 }, { 289, 116, 17 }, { 214, 0, 10 }, 0 },
+ { { 281, 114, 1 }, { 284, 115, 17 }, { 214, 0, 10 }, 0 },
+ { { 277, 113, 1 }, { 280, 114, 17 }, { 214, 0, 10 }, 0 },
+ { { 274, 112, 1 }, { 277, 113, 17 }, { 214, 0, 10 }, 0 },
+ { { 271, 111, 1 }, { 274, 112, 17 }, { 214, 0, 10 }, 0 },
+ { { 267, 110, 1 }, { 270, 111, 17 }, { 214, 0, 10 }, 0 },
+ { { 263, 109, 1 }, { 266, 110, 17 }, { 214, 0, 10 }, 0 },
+ { { 260, 108, 1 }, { 263, 109, 17 }, { 214, 0, 10 }, 0 },
+ { { 254, 108, 2 }, { 256, 109, 17 }, { 214, 0, 10 }, 0 },
+ { { 252, 107, 3 }, { 254, 108, 17 }, { 214, 0, 10 }, 0 },
+ { { 253, 108, 3 }, { 255, 109, 17 }, { 214, 0, 10 }, 0 },
+ { { 255, 109, 3 }, { 257, 110, 18 }, { 231, 59, 10 }, 2 },
+ { { 258, 111, 3 }, { 260, 112, 18 }, { 242, 57, 11 }, 0 },
+ { { 263, 112, 4 }, { 262, 113, 19 }, { 256, 57, 12 }, 0 },
+ { { 270, 111, 4 }, { 269, 112, 20 }, { 267, 57, 13 }, 0 },
+ { { 274, 112, 5 }, { 276, 113, 20 }, { 281, 56, 14 }, 0 },
+ { { 280, 111, 6 }, { 282, 112, 19 }, { 214, 0, 10 }, 0 },
+ { { 284, 109, 6 }, { 285, 110, 17 }, { 214, 0, 10 }, 0 },
+ { { 289, 108, 6 }, { 291, 109, 17 }, { 214, 0, 10 }, 0 },
+ { { 294, 107, 6 }, { 296, 108, 17 }, { 214, 0, 10 }, 0 },
+ { { 294, 107, 5 }, { 296, 108, 18 }, { 272, 57, 10 }, 2 },
+ { { 295, 107, 5 }, { 297, 108, 18 }, { 282, 57, 11 }, 0 },
+ { { 296, 108, 5 }, { 298, 109, 19 }, { 295, 57, 12 }, 0 },
+ { { 300, 108, 4 }, { 299, 109, 20 }, { 303, 57, 13 }, 0 },
+ { { 303, 108, 3 }, { 306, 109, 20 }, { 313, 57, 14 }, 0 },
+ { { 307, 109, 2 }, { 311, 110, 17 }, { 214, 0, 10 }, 0 },
+ { { 310, 110, 1 }, { 314, 111, 17 }, { 214, 0, 10 }, 99 }
+};
+
+const BamScene::BamDataBlock BamScene::_fight1Data[] = {
+ { { 75, 96, 1 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 },
+ { { 75, 96, 2 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 },
+ { { 75, 96, 3 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 },
+ { { 75, 96, 4 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 },
+ { { 75, 96, 1 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 },
+ { { 75, 96, 2 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 },
+ { { 75, 96, 3 }, { 187, 96, -23 }, { 58, 37, 46 }, 0 },
+ { { 75, 96, 4 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 },
+ { { 79, 96, 1 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 },
+ { { 85, 96, 2 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 },
+ { { 94, 96, 3 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 },
+ { { 100, 96, 4 }, { 187, 96, -24 }, { 58, 37, 46 }, 0 },
+ { { 113, 96, 1 }, { 187, 96, -25 }, { 58, 37, 46 }, 0 },
+ { { 121, 96, 1 }, { 187, 96, -25 }, { 58, 37, 46 }, 0 },
+ { { 136, 96, 16 }, { 187, 96, -26 }, { 58, 37, 46 }, 0 },
+ { { 151, 93, 6 }, { 187, 96, -27 }, { 58, 37, 46 }, 0 },
+ { { 159, 83, 16 }, { 187, 96, -28 }, { 58, 37, 46 }, 0 },
+ { { 170, 73, 16 }, { 187, 96, -29 }, { 182, 96, 48 }, 3 },
+ { { 176, 69, 13 }, { 187, 96, -31 }, { 182, 94, 49 }, 1 },
+ { { 168, 66, 13 }, { 187, 98, -32 }, { 182, 92, 50 }, 0 },
+ { { 155, 75, 13 }, { 187, 96, -32 }, { 182, 88, 51 }, 3 },
+ { { 145, 86, 13 }, { 187, 98, -32 }, { 182, 85, 52 }, 0 },
+ { { 127, 104, 13 }, { 187, 98, -32 }, { 182, 25, 52 }, 1 },
+ { { 122, 108, 13 }, { 187, 98, -32 }, { 182, 25, 52 }, 1 },
+ { { 120, 104, 14 }, { 187, 96, -34 }, { 107, 145, 42 }, 2 },
+ { { 111, 103, 13 }, { 187, 96, -23 }, { 107, 144, 43 }, 0 },
+ { { 102, 105, 13 }, { 187, 96, -23 }, { 107, 142, 43 }, 0 },
+ { { 97, 107, 13 }, { 187, 96, -23 }, { 107, 139, 44 }, 0 },
+ { { 92, 101, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 3 },
+ { { 90, 105, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 },
+ { { 88, 104, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 },
+ { { 87, 105, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 },
+ { { 86, 105, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 },
+ { { 86, 105, 14 }, { 187, 96, -23 }, { 107, 34, 47 }, 0 },
+ { { 86, 105, 15 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 85, 98, 16 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 92, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 92, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 89, 96, 4 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 86, 96, 3 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 83, 96, 2 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 81, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 78, 96, 4 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 75, 96, 3 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 75, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 0 },
+ { { 75, 96, 1 }, { 187, 96, -23 }, { 0, 0, 0 }, 99 }
+};
+
+const BamScene::BamDataBlock BamScene::_fight2Data[] = {
+ { { 75, 96, 1 }, { 187, 96, -23 }, { 150, 45, 35 }, 0 },
+ { { 78, 96, 2 }, { 187, 96, -23 }, { 150, 45, 35 }, 0 },
+ { { 81, 96, 3 }, { 189, 96, -18 }, { 150, 45, 35 }, 0 },
+ { { 84, 96, 4 }, { 183, 96, -19 }, { 150, 45, 35 }, 0 },
+ { { 87, 96, 1 }, { 181, 96, -20 }, { 150, 45, 35 }, 0 },
+ { { 90, 96, 2 }, { 177, 96, -21 }, { 150, 45, 35 }, 0 },
+ { { 93, 96, 3 }, { 171, 96, -22 }, { 150, 45, 35 }, 0 },
+ { { 96, 96, 4 }, { 169, 96, -17 }, { 150, 45, 35 }, 0 },
+ { { 99, 96, 1 }, { 165, 96, -18 }, { 150, 45, 35 }, 0 },
+ { { 102, 96, 2 }, { 159, 96, -19 }, { 150, 45, 35 }, 0 },
+ { { 105, 96, 3 }, { 157, 96, -20 }, { 150, 45, 35 }, 0 },
+ { { 108, 96, 4 }, { 153, 96, -21 }, { 150, 45, 35 }, 0 },
+ { { 111, 96, 1 }, { 147, 96, -22 }, { 150, 45, 35 }, 0 },
+ { { 114, 96, 2 }, { 147, 96, -23 }, { 150, 45, 35 }, 0 },
+ { { 117, 96, 3 }, { 147, 96, -23 }, { 150, 45, 35 }, 0 },
+ { { 120, 96, 4 }, { 147, 96, -24 }, { 150, 45, 35 }, 0 },
+ { { 123, 96, 1 }, { 147, 96, -25 }, { 150, 45, 35 }, 0 },
+ { { 125, 96, 2 }, { 147, 96, -25 }, { 150, 45, 35 }, 0 },
+ { { 127, 96, 12 }, { 147, 96, -69 }, { 122, 94, 36 }, 3 },
+ { { 127, 95, 11 }, { 147, 96, -70 }, { 122, 94, 41 }, 0 },
+ { { 127, 96, 12 }, { 147, 96, -71 }, { 122, 100, 36 }, 3 },
+ { { 127, 97, 11 }, { 147, 96, -69 }, { 122, 100, 41 }, 0 },
+ { { 127, 96, 12 }, { 147, 96, -70 }, { 127, 103, 36 }, 3 },
+ { { 127, 95, 11 }, { 147, 96, -71 }, { 127, 103, 41 }, 0 },
+ { { 127, 94, 12 }, { 147, 96, -69 }, { 123, 94, 36 }, 3 },
+ { { 127, 95, 11 }, { 147, 96, -70 }, { 123, 94, 41 }, 0 },
+ { { 127, 96, 12 }, { 147, 96, -71 }, { 120, 99, 36 }, 3 },
+ { { 127, 96, 12 }, { 147, 96, -71 }, { 115, 98, 41 }, 0 },
+ { { 117, 93, 11 }, { 147, 96, -25 }, { 115, 134, 42 }, 0 },
+ { { 110, 92, 11 }, { 147, 96, -25 }, { 114, 133, 42 }, 0 },
+ { { 102, 93, 11 }, { 147, 96, -25 }, { 114, 131, 43 }, 0 },
+ { { 92, 93, 11 }, { 147, 96, -25 }, { 114, 130, 43 }, 0 },
+ { { 82, 94, 11 }, { 147, 96, -25 }, { 114, 128, 44 }, 0 },
+ { { 76, 95, 11 }, { 147, 96, -25 }, { 114, 127, 44 }, 0 },
+ { { 70, 96, 11 }, { 147, 96, -25 }, { 114, 126, 45 }, 0 },
+ { { 75, 96, 5 }, { 147, 96, -25 }, { 114, 125, 46 }, 1 },
+ { { 75, 96, 6 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 },
+ { { 75, 96, 6 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 },
+ { { 75, 96, 5 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 },
+ { { 75, 96, 7 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 },
+ { { 75, 96, 68 }, { 147, 96, -25 }, { 114, 43, 46 }, 0 },
+ { { 75, 96, 68 }, { 147, 96, -25 }, { 89, 104, 36 }, 2 },
+ { { 75, 96, 68 }, { 147, 96, -25 }, { 94, 103, 62 }, 0 },
+ { { 75, 96, 68 }, { 147, 96, -25 }, { 122, 103, 63 }, 0 },
+ { { 75, 96, 68 }, { 147, 96, -25 }, { 141, 103, 64 }, 0 },
+ { { 75, 96, 68 }, { 147, 96, -30 }, { 150, 103, 65 }, 3 },
+ { { 75, 96, 68 }, { 156, 96, -30 }, { 160, 103, 66 }, 0 },
+ { { 75, 96, 7 }, { 164, 96, -30 }, { 169, 103, 67 }, 0 },
+ { { 75, 96, 5 }, { 169, 96, -30 }, { 177, 103, 48 }, 3 },
+ { { 75, 96, 5 }, { 173, 96, -30 }, { 185, 103, 49 }, 0 },
+ { { 75, 96, 6 }, { 178, 96, -30 }, { 198, 103, 50 }, 0 },
+ { { 75, 96, 6 }, { 181, 96, -30 }, { 207, 103, 51 }, 1 },
+ { { 75, 96, 5 }, { 184, 96, -30 }, { 221, 103, 52 }, 0 },
+ { { 75, 96, 5 }, { 186, 96, -30 }, { 224, 53, 53 }, 0 },
+ { { 75, 96, 5 }, { 187, 96, -23 }, { 224, 53, 53 }, 99 }
+};
+
+const BamScene::BamDataBlock BamScene::_fight3Data[] = {
+ { { 75, 96, 1 }, { 187, 96, -23 }, { 150, 45, 35 }, 0 },
+ { { 77, 96, 2 }, { 187, 96, -22 }, { 150, 45, 35 }, 0 },
+ { { 80, 96, 3 }, { 185, 96, -17 }, { 150, 45, 35 }, 0 },
+ { { 83, 96, 4 }, { 181, 96, -18 }, { 150, 45, 35 }, 0 },
+ { { 86, 96, 1 }, { 175, 96, -19 }, { 150, 45, 35 }, 0 },
+ { { 88, 96, 2 }, { 173, 96, -20 }, { 150, 45, 35 }, 0 },
+ { { 91, 96, 3 }, { 169, 96, -21 }, { 150, 45, 35 }, 0 },
+ { { 94, 96, 4 }, { 163, 96, -22 }, { 150, 45, 35 }, 0 },
+ { { 97, 96, 1 }, { 161, 96, -17 }, { 150, 45, 35 }, 0 },
+ { { 99, 96, 2 }, { 157, 96, -18 }, { 150, 45, 35 }, 0 },
+ { { 102, 96, 3 }, { 151, 96, -19 }, { 150, 45, 35 }, 0 },
+ { { 105, 96, 4 }, { 149, 96, -20 }, { 150, 45, 35 }, 0 },
+ { { 108, 96, 1 }, { 145, 96, -21 }, { 150, 45, 35 }, 0 },
+ { { 110, 96, 2 }, { 145, 96, -25 }, { 150, 45, 35 }, 0 },
+ { { 113, 96, 3 }, { 145, 96, -26 }, { 132, 96, 36 }, 2 },
+ { { 117, 96, 7 }, { 145, 96, -27 }, { 122, 97, 36 }, 0 },
+ { { 117, 96, 7 }, { 145, 96, -28 }, { 117, 97, 37 }, 0 },
+ { { 116, 96, 12 }, { 145, 96, -24 }, { 110, 96, 38 }, 3 },
+ { { 109, 96, 12 }, { 145, 96, -24 }, { 103, 95, 39 }, 0 },
+ { { 105, 96, 12 }, { 145, 96, -24 }, { 95, 90, 40 }, 1 },
+ { { 96, 96, 11 }, { 145, 96, -24 }, { 86, 80, 41 }, 0 },
+ { { 92, 96, 11 }, { 145, 96, -24 }, { 86, 80, 41 }, 0 },
+ { { 93, 96, 5 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 },
+ { { 91, 96, 5 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 },
+ { { 89, 96, 5 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 },
+ { { 88, 96, 5 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 6 }, { 145, 96, -24 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 5 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 5 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 5 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 5 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 6 }, { 145, 96, -23 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 6 }, { 145, 96, -26 }, { 86, 38, 41 }, 0 },
+ { { 87, 96, 6 }, { 145, 96, -27 }, { 132, 97, 36 }, 2 },
+ { { 87, 96, 5 }, { 145, 96, -28 }, { 118, 97, 36 }, 0 },
+ { { 87, 96, 7 }, { 145, 96, -24 }, { 107, 97, 36 }, 0 },
+ { { 87, 96, 8 }, { 145, 96, -24 }, { 101, 97, 36 }, 0 },
+ { { 87, 96, 9 }, { 145, 96, -23 }, { 102, 97, 66 }, 3 },
+ { { 87, 96, 10 }, { 145, 96, -23 }, { 120, 97, 67 }, 0 },
+ { { 87, 96, 10 }, { 145, 96, -30 }, { 139, 97, 67 }, 1 },
+ { { 87, 96, 7 }, { 146, 96, -30 }, { 144, 97, 62 }, 2 },
+ { { 86, 96, 4 }, { 160, 96, -30 }, { 144, 97, 48 }, 1 },
+ { { 83, 96, 3 }, { 170, 96, -31 }, { 154, 93, 49 }, 0 },
+ { { 80, 96, 2 }, { 174, 96, -31 }, { 161, 89, 50 }, 0 },
+ { { 78, 96, 1 }, { 178, 99, -31 }, { 169, 85, 51 }, 0 },
+ { { 75, 96, 4 }, { 183, 104, -31 }, { 175, 79, 52 }, 0 },
+ { { 75, 96, 1 }, { 185, 99, -32 }, { 180, 144, 42 }, 3 },
+ { { 75, 96, 1 }, { 185, 106, -31 }, { 181, 141, 42 }, 0 },
+ { { 75, 96, 5 }, { 185, 104, -31 }, { 181, 138, 43 }, 0 },
+ { { 75, 96, 5 }, { 188, 106, -31 }, { 182, 135, 43 }, 0 },
+ { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 131, 44 }, 3 },
+ { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 127, 45 }, 0 },
+ { { 75, 96, 5 }, { 191, 99, -32 }, { 184, 121, 46 }, 0 },
+ { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 115, 46 }, 0 },
+ { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 5 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 6 }, { 191, 99, -32 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 5 }, { 195, 98, -33 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 6 }, { 191, 96, -34 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 6 }, { 193, 96, -25 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 5 }, { 193, 96, -24 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 5 }, { 193, 96, -24 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 5 }, { 193, 96, -24 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 6 }, { 191, 96, -18 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 6 }, { 190, 96, -19 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 6 }, { 187, 96, -20 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 1 }, { 187, 96, -23 }, { 183, 41, 47 }, 0 },
+ { { 75, 96, 1 }, { 187, 96, -23 }, { 183, 41, 47 }, 99 }
+};
+#endif
+
+} // End of namespace Queen
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Queen_Graphics)
+_GSETPTR(Queen::_carData, GBVARS_GRAPHICSCARDATA_INDEX, Queen::BamScene::BamDataBlock, GBVARS_QUEEN)
+_GSETPTR(Queen::_fight1Data, GBVARS_GRAPHICSFIGHT1DATA_INDEX, Queen::BamScene::BamDataBlock, GBVARS_QUEEN)
+_GSETPTR(Queen::_fight2Data, GBVARS_GRAPHICSFIGHT2DATA_INDEX, Queen::BamScene::BamDataBlock, GBVARS_QUEEN)
+_GSETPTR(Queen::_fight3Data, GBVARS_GRAPHICSFIGHT3DATA_INDEX, Queen::BamScene::BamDataBlock, GBVARS_QUEEN)
+_GEND
+
+_GRELEASE(Queen_Graphics)
+_GRELEASEPTR(GBVARS_GRAPHICSCARDATA_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_GRAPHICSFIGHT1DATA_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_GRAPHICSFIGHT2DATA_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_GRAPHICSFIGHT3DATA_INDEX, GBVARS_QUEEN)
+_GEND
+
+#endif
diff --git a/engines/queen/graphics.h b/engines/queen/graphics.h
new file mode 100644
index 0000000000..b2899a5036
--- /dev/null
+++ b/engines/queen/graphics.h
@@ -0,0 +1,293 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENGRAPHICS_H
+#define QUEENGRAPHICS_H
+
+#include "common/util.h"
+#include "queen/structs.h"
+
+namespace Queen {
+
+struct BobSlot {
+ bool active;
+ //! current position
+ int16 x, y;
+ //! bounding box
+ Box box;
+ bool xflip;
+ //! shrinking percentage
+ uint16 scale;
+ //! associated BobFrame
+ uint16 frameNum;
+ //! 'direction' for the next frame (-1, 1)
+ int frameDir;
+
+ //! animation stuff
+ bool animating;
+ struct {
+ int16 speed, speedBak;
+
+ //! string based animation
+ struct {
+ const AnimFrame *buffer;
+ const AnimFrame *curPos;
+ } string;
+
+ //! normal moving animation
+ struct {
+ bool rebound;
+ uint16 firstFrame, lastFrame;
+ } normal;
+
+ } anim;
+
+ bool moving;
+ //! moving speed
+ int16 speed;
+ //! move along x axis instead of y
+ bool xmajor;
+ //! moving direction
+ int8 xdir, ydir;
+ //! destination point
+ int16 endx, endy;
+ uint16 dx, dy;
+ uint16 total;
+
+ void curPos(int16 xx, int16 yy);
+ void move(int16 dstx, int16 dsty, int16 spd);
+ void moveOneStep();
+ void animOneStep();
+
+ void animString(const AnimFrame *animBuf);
+ void animNormal(uint16 firstFrame, uint16 lastFrame, uint16 speed, bool rebound, bool xflip);
+
+ void scaleWalkSpeed(uint16 ms);
+
+ void clear();
+
+ static const Box _defaultBox;
+};
+
+class QueenEngine;
+
+class Graphics {
+public:
+
+ Graphics(QueenEngine *vm);
+ ~Graphics();
+
+ //! unpacks control frames (ie. arrows)
+ void unpackControlBank();
+
+ //! setup dialog arrows
+ void setupArrows();
+
+ //! setup mouse cursor
+ void setupMouseCursor();
+
+ //! draw a bob
+ void drawBob(const BobSlot *bs, const BobFrame *bf, const Box *box, int16 x, int16 y);
+
+ //! draw an inventory item
+ void drawInventoryItem(uint32 frameNum, uint16 x, uint16 y);
+
+ //! draw a bob directly on the backdrop bitmap
+ void pasteBob(uint16 objNum, uint16 image);
+
+ //! resize a bobframe
+ void shrinkFrame(const BobFrame *bf, uint16 percentage);
+
+ //! animate/move bobs and sort them
+ void sortBobs();
+
+ //! draw all the sorted bobs
+ void drawBobs();
+
+ //! clear all setup bobs
+ void clearBobs();
+
+ //! stop all animating/movings bobs
+ void stopBobs();
+
+ //! returns a reference to the specified bob
+ BobSlot *bob(int index);
+
+ //! display a text 'near' the specified bob
+ void setBobText(const BobSlot *bob, const char *text, int textX, int textY, int color, int flags);
+
+ //! handles parallax scrolling for the specified room
+ void handleParallax(uint16 roomNum);
+
+ void setupNewRoom(const char *room, uint16 roomNum, int16 *furniture, uint16 furnitureCount);
+
+ void setBobCutawayAnim(uint16 bobNum, bool xflip, const AnimFrame *af, uint8 frameCount);
+ void fillAnimBuffer(const char *anim, AnimFrame *af);
+ uint16 countAnimFrames(const char *anim);
+ void setupObjectAnim(const GraphicData *gd, uint16 firstImage, uint16 bobNum, bool visible);
+ uint16 setupPersonAnim(const ActorData *ad, const char *anim, uint16 curImage);
+ void resetPersonAnim(uint16 bobNum);
+ void erasePersonAnim(uint16 bobNum);
+ void eraseAllAnims();
+
+ uint16 refreshObject(uint16 obj);
+
+ void setupRoomFurniture(int16 *furniture, uint16 furnitureCount);
+ void setupRoomObjects();
+
+ uint16 setupPerson(uint16 noun, uint16 curImage);
+ uint16 allocPerson(uint16 noun, uint16 curImage);
+
+ uint16 personFrames(uint16 bobNum) const { return _personFrames[bobNum]; }
+ void clearPersonFrames() { memset(_personFrames, 0, sizeof(_personFrames)); }
+ uint16 numFrames() const { return _numFrames; }
+ uint16 numStaticFurniture() const { return _numFurnitureStatic; }
+ uint16 numAnimatedFurniture() const { return _numFurnitureAnimated; }
+ uint16 numFurnitureFrames() const { return _numFurnitureStatic + _numFurnitureAnimatedLen; }
+
+ void putCameraOnBob(int bobNum) { _cameraBob = bobNum; }
+
+ void update(uint16 room);
+
+ enum {
+ ARROW_BOB_UP = 62,
+ ARROW_BOB_DOWN = 63,
+ MAX_BOBS_NUMBER = 64,
+ MAX_STRING_LENGTH = 255,
+ MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1),
+ BOB_SHRINK_BUF_SIZE = 60000
+ };
+
+
+private:
+
+ BobSlot _bobs[MAX_BOBS_NUMBER];
+
+ //! bobs to display
+ BobSlot *_sortedBobs[MAX_BOBS_NUMBER];
+
+ //! number of bobs to display
+ uint16 _sortedBobsCount;
+
+ //! used to scale a BobFrame
+ BobFrame _shrinkBuffer;
+
+ //! in-game objects/persons animations
+ AnimFrame _newAnim[17][30];
+
+ //! cutaway objects/persons animations
+ AnimFrame _cutAnim[21][30];
+
+ uint16 _personFrames[4];
+
+ //! number of animated furniture in current room
+ uint16 _numFurnitureAnimated;
+
+ //! number of static furniture in current room
+ uint16 _numFurnitureStatic;
+
+ //! total number of frames for the animated furniture
+ uint16 _numFurnitureAnimatedLen;
+
+ //! current number of frames unpacked
+ uint16 _numFrames;
+
+ //! bob number followed by camera
+ int _cameraBob;
+
+ QueenEngine *_vm;
+
+ static const Box _gameScreenBox;
+ static const Box _fullScreenBox;
+};
+
+class BamScene {
+public:
+
+ BamScene(QueenEngine *vm);
+
+ void playSfx();
+ void prepareAnimation();
+ void updateCarAnimation();
+ void updateFightAnimation();
+
+ void saveState(byte *&ptr);
+ void loadState(uint32 ver, byte *&ptr);
+
+ enum {
+ BOB_OBJ1 = 5,
+ BOB_OBJ2 = 6,
+ BOB_FX = 7
+ };
+
+ enum {
+ F_STOP = 0,
+ F_PLAY = 1,
+ F_REQ_STOP = 2
+ };
+
+ enum {
+ SFX_SKIP = 8
+ };
+
+ uint16 _flag, _index;
+
+private:
+
+ struct BamDataObj {
+ int16 x, y;
+ int16 frame;
+ };
+
+#ifdef PALMOS_68K
+public:
+#endif
+ struct BamDataBlock {
+ BamDataObj obj1; // truck / Frank
+ BamDataObj obj2; // Rico / robot
+ BamDataObj fx;
+ int16 sfx;
+ };
+#ifdef PALMOS_68K
+private:
+#endif
+
+ BobSlot *_obj1;
+ BobSlot *_obj2;
+ BobSlot *_objfx;
+ bool _screenShaked;
+ const BamDataBlock *_fightData;
+ uint16 _lastSoundIndex;
+
+ QueenEngine *_vm;
+
+#ifndef PALMOS_68K
+ static const BamDataBlock _carData[];
+ static const BamDataBlock _fight1Data[];
+ static const BamDataBlock _fight2Data[];
+ static const BamDataBlock _fight3Data[];
+#endif
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/grid.cpp b/engines/queen/grid.cpp
new file mode 100644
index 0000000000..cb8ed4696b
--- /dev/null
+++ b/engines/queen/grid.cpp
@@ -0,0 +1,255 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/grid.h"
+
+#include "queen/display.h"
+#include "queen/logic.h"
+#include "queen/queen.h"
+
+namespace Queen {
+
+Grid::Grid(QueenEngine *vm)
+ : _vm(vm) {
+ memset(_zones, 0, sizeof(_zones));
+}
+
+Grid::~Grid() {
+ delete[] _objMax;
+ delete[] _areaMax;
+ delete[] _area;
+ delete[] _objectBox;
+}
+
+void Grid::readDataFrom(uint16 numObjects, uint16 numRooms, byte *&ptr) {
+ uint16 i, j;
+
+ _numRoomAreas = numRooms;
+
+ _objMax = new int16[_numRoomAreas + 1];
+ _areaMax = new int16[_numRoomAreas + 1];
+ _area = new Area[_numRoomAreas + 1][MAX_AREAS_NUMBER];
+
+ _objMax[0] = 0;
+ _areaMax[0] = 0;
+ memset(&_area[0], 0, sizeof(Area) * MAX_AREAS_NUMBER);
+ for (i = 1; i <= _numRoomAreas; i++) {
+ _objMax[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
+ _areaMax[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
+ memset(&_area[i][0], 0, sizeof(Area));
+ for (j = 1; j <= _areaMax[i]; j++) {
+ assert(j < MAX_AREAS_NUMBER);
+ _area[i][j].readFromBE(ptr);
+ }
+ }
+
+ _objectBox = new Box[numObjects + 1];
+ memset(&_objectBox[0], 0, sizeof(Box));
+ for (i = 1; i <= numObjects; i++) {
+ _objectBox[i].readFromBE(ptr);
+ }
+}
+
+void Grid::setZone(GridScreen screen, uint16 zoneNum, uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
+ debug(9, "Grid::setZone(%d, %d, (%d,%d), (%d,%d))", screen, zoneNum, x1, y1, x2, y2);
+ ZoneSlot *pzs = &_zones[screen][zoneNum];
+ pzs->valid = true;
+ pzs->box.x1 = x1;
+ pzs->box.y1 = y1;
+ pzs->box.x2 = x2;
+ pzs->box.y2 = y2;
+}
+
+void Grid::setZone(GridScreen screen, uint16 zoneNum, const Box &box) {
+ debug(9, "Grid::setZone(%d, %d, (%d,%d), (%d,%d))", screen, zoneNum, box.x1, box.y1, box.x2, box.y2);
+ ZoneSlot *pzs = &_zones[screen][zoneNum];
+ pzs->valid = true;
+ pzs->box = box;
+}
+
+uint16 Grid::findZoneForPos(GridScreen screen, uint16 x, uint16 y) const {
+ debug(9, "Logic::findZoneForPos(%d, (%d,%d))", screen, x, y);
+ int i;
+ if (screen == GS_PANEL) {
+ y -= ROOM_ZONE_HEIGHT;
+ }
+ for (i = 1; i < MAX_ZONES_NUMBER; ++i) {
+ const ZoneSlot *pzs = &_zones[screen][i];
+ if (pzs->valid && pzs->box.contains(x, y)) {
+ return i;
+ }
+ }
+ return 0;
+}
+
+uint16 Grid::findAreaForPos(GridScreen screen, uint16 x, uint16 y) const {
+ uint16 room = _vm->logic()->currentRoom();
+ uint16 zoneNum = findZoneForPos(screen, x, y);
+ if (zoneNum <= _objMax[room]) {
+ zoneNum = 0;
+ } else {
+ zoneNum -= _objMax[room];
+ }
+ return zoneNum;
+}
+
+void Grid::clear(GridScreen screen) {
+ debug(9, "Grid::clear(%d)", screen);
+ for (int i = 1; i < MAX_ZONES_NUMBER; ++i) {
+ _zones[screen][i].valid = false;
+ }
+}
+
+void Grid::setupNewRoom(uint16 room, uint16 firstRoomObjNum) {
+ debug(9, "Grid::setupNewRoom()");
+ clear(GS_ROOM);
+
+ uint16 i;
+ uint16 zoneNum;
+
+ // setup objects zones
+ uint16 maxObjRoom = _objMax[room];
+ zoneNum = 1;
+ for (i = firstRoomObjNum + 1; i <= firstRoomObjNum + maxObjRoom; ++i) {
+ if (_vm->logic()->objectData(i)->name != 0) {
+ setZone(GS_ROOM, zoneNum, _objectBox[i]);
+ }
+ ++zoneNum;
+ }
+
+ // setup room zones (areas)
+ uint16 maxAreaRoom = _areaMax[room];
+ for (zoneNum = 1; zoneNum <= maxAreaRoom; ++zoneNum) {
+ setZone(GS_ROOM, maxObjRoom + zoneNum, _area[room][zoneNum].box);
+ }
+}
+
+void Grid::setupPanel() {
+ for (int i = 0; i <= 7; ++i) {
+ uint16 x = i * 20;
+ setZone(GS_PANEL, i + 1, x, 10, x + 19, 49);
+ }
+
+ // inventory scrolls
+ setZone(GS_PANEL, 9, 160, 10, 179, 29);
+ setZone(GS_PANEL, 10, 160, 30, 179, 49);
+
+ // inventory items
+ setZone(GS_PANEL, 11, 180, 10, 213, 49);
+ setZone(GS_PANEL, 12, 214, 10, 249, 49);
+ setZone(GS_PANEL, 13, 250, 10, 284, 49);
+ setZone(GS_PANEL, 14, 285, 10, 320, 49);
+}
+
+void Grid::drawZones() {
+ for (int i = 1; i < MAX_ZONES_NUMBER; ++i) {
+ const ZoneSlot *pzs = &_zones[GS_ROOM][i];
+ if (pzs->valid) {
+ const Box *b = &pzs->box;
+ _vm->display()->drawBox(b->x1, b->y1, b->x2, b->y2, 3);
+ }
+ }
+}
+
+const Box *Grid::zone(GridScreen screen, uint16 index) const {
+ const ZoneSlot *zs = &_zones[screen][index];
+ assert(zs->valid);
+ return &zs->box;
+}
+
+Verb Grid::findVerbUnderCursor(int16 cursorx, int16 cursory) const {
+ static const Verb pv[] = {
+ VERB_NONE,
+ VERB_OPEN,
+ VERB_CLOSE,
+ VERB_MOVE,
+ VERB_GIVE,
+ VERB_LOOK_AT,
+ VERB_PICK_UP,
+ VERB_TALK_TO,
+ VERB_USE,
+ VERB_SCROLL_UP,
+ VERB_SCROLL_DOWN,
+ VERB_INV_1,
+ VERB_INV_2,
+ VERB_INV_3,
+ VERB_INV_4,
+ };
+ return pv[findZoneForPos(GS_PANEL, cursorx, cursory)];
+}
+
+uint16 Grid::findObjectUnderCursor(int16 cursorx, int16 cursory) const {
+ uint16 roomObj = 0;
+ if (cursory < ROOM_ZONE_HEIGHT) {
+ int16 x = cursorx + _vm->display()->horizontalScroll();
+ roomObj = findZoneForPos(GS_ROOM, x, cursory);
+ }
+ return roomObj;
+}
+
+uint16 Grid::findObjectNumber(uint16 zoneNum) const {
+ // l.316-327 select.c
+ uint16 room = _vm->logic()->currentRoom();
+ uint16 obj = zoneNum;
+ uint16 objectMax = _objMax[room];
+ debug(9, "Grid::findObjectNumber(%X, %X)", zoneNum, objectMax);
+ if (zoneNum > objectMax) {
+ // this is an area box, check for associated object
+ obj = _area[room][zoneNum - objectMax].object;
+ if (obj != 0) {
+ // there is an object, get its number
+ obj -= _vm->logic()->currentRoomData();
+ }
+ }
+ return obj;
+}
+
+uint16 Grid::findScale(uint16 x, uint16 y) const {
+ uint16 room = _vm->logic()->currentRoom();
+ uint16 scale = 100;
+ uint16 areaNum = findAreaForPos(GS_ROOM, x, y);
+ if (areaNum != 0) {
+ scale = _area[room][areaNum].calcScale(y);
+ }
+ return scale;
+}
+
+void Grid::saveState(byte *&ptr) {
+ uint16 i, j;
+ for (i = 1; i <= _numRoomAreas; ++i) {
+ for (j = 1; j <= _areaMax[i]; ++j) {
+ _area[i][j].writeToBE(ptr);
+ }
+ }
+}
+
+void Grid::loadState(uint32 ver, byte *&ptr) {
+ uint16 i, j;
+ for (i = 1; i <= _numRoomAreas; ++i) {
+ for (j = 1; j <= _areaMax[i]; ++j) {
+ _area[i][j].readFromBE(ptr);
+ }
+ }
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/grid.h b/engines/queen/grid.h
new file mode 100644
index 0000000000..d6e148eec2
--- /dev/null
+++ b/engines/queen/grid.h
@@ -0,0 +1,136 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENGRID_H
+#define QUEENGRID_H
+
+#include "common/util.h"
+#include "queen/structs.h"
+
+namespace Queen {
+
+enum GridScreen {
+ GS_ROOM = 0,
+ GS_PANEL = 1,
+ GS_COUNT = 2
+};
+
+class QueenEngine;
+
+class Grid {
+public:
+
+ Grid(QueenEngine *vm);
+ ~Grid();
+
+ //! read areas data from specified stream
+ void readDataFrom(uint16 numObjects, uint16 numRooms, byte *&ptr);
+
+ //! defines a new zone
+ void setZone(GridScreen screen, uint16 zoneNum, uint16 x1, uint16 y1, uint16 x2, uint16 y2);
+
+ //! defines a new zone
+ void setZone(GridScreen screen, uint16 zoneNum, const Box &box);
+
+ //! find the zone number containing the specified point
+ uint16 findZoneForPos(GridScreen screen, uint16 x, uint16 y) const;
+
+ //! find the area number containing the specified point
+ uint16 findAreaForPos(GridScreen screen, uint16 x, uint16 y) const;
+
+ //! clear the zones for current room
+ void clear(GridScreen screen);
+
+ //! setup objects zones for the specified room
+ void setupNewRoom(uint16 room, uint16 firstRoomObjNum);
+
+ //! setup panel zones
+ void setupPanel();
+
+ //! draw the zones for current room (debug only)
+ void drawZones();
+
+ //! retuns a reference to the specified zone
+ const Box *zone(GridScreen screen, uint16 index) const;
+
+ //! get the verb for the specified cursor position
+ Verb findVerbUnderCursor(int16 cursorx, int16 cursory) const;
+
+ //! get the object for the specified cursor position
+ uint16 findObjectUnderCursor(int16 cursorx, int16 cursory) const;
+
+ //! get the object for the specified zone number
+ uint16 findObjectNumber(uint16 zoneNum) const;
+
+ //! get scale for the specified position
+ uint16 findScale(uint16 x, uint16 y) const;
+
+ //! returns a reference to the specfied room area
+ Area *area(int room, int num) const { return &_area[room][num]; }
+
+ //! returns the number of areas in this room
+ uint16 areaMax(int room) const { return _areaMax[room]; }
+
+ //! returns the number of objects in this room
+ uint16 objMax(int room) const { return _objMax[room]; }
+
+ void saveState(byte *&ptr);
+ void loadState(uint32 ver, byte *&ptr);
+
+ enum {
+ MAX_ZONES_NUMBER = 32,
+ MAX_AREAS_NUMBER = 11
+ };
+
+
+private:
+
+ struct ZoneSlot {
+ bool valid;
+ Box box;
+ };
+
+ //! current room zones
+ ZoneSlot _zones[GS_COUNT][MAX_ZONES_NUMBER];
+
+ //! number of objects for each room
+ int16 *_objMax;
+
+ //! number of areas for each room
+ int16 *_areaMax;
+
+ //! areas for each room
+ Area (*_area)[MAX_AREAS_NUMBER];
+
+ //! total number of room areas
+ uint16 _numRoomAreas;
+
+ //! box/zone for each objects
+ Box *_objectBox;
+
+ QueenEngine *_vm;
+};
+
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/input.cpp b/engines/queen/input.cpp
new file mode 100644
index 0000000000..7ae2e2abce
--- /dev/null
+++ b/engines/queen/input.cpp
@@ -0,0 +1,213 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+
+#include "queen/input.h"
+
+namespace Queen {
+
+const char *Input::_commandKeys[LANGUAGE_COUNT] = {
+ "ocmglptu", // English
+ "osbgpnre", // German
+ "ofdnepau", // French
+ "acsdgpqu", // Italian
+ "ocmglptu", // Hebrew
+ "acodmthu" // Spanish
+};
+
+const Verb Input::_verbKeys[8] = {
+ VERB_OPEN,
+ VERB_CLOSE,
+ VERB_MOVE,
+ VERB_GIVE,
+ VERB_LOOK_AT,
+ VERB_PICK_UP,
+ VERB_TALK_TO,
+ VERB_USE
+};
+
+Input::Input(Language language, OSystem *system) :
+ _system(system), _fastMode(false), _keyVerb(VERB_NONE),
+ _cutawayRunning(false), _canQuit(false), _cutawayQuit(false),
+ _dialogueRunning(false), _talkQuit(false), _quickSave(false),
+ _quickLoad(false), _debugger(false), _inKey(0), _mouse_x(0),
+ _mouse_y(0), _mouseButton(0), _idleTime(0) {
+
+ switch (language) {
+ case ENGLISH:
+ _currentCommandKeys = _commandKeys[0];
+ break;
+ case GERMAN:
+ _currentCommandKeys = _commandKeys[1];
+ break;
+ case FRENCH:
+ _currentCommandKeys = _commandKeys[2];
+ break;
+ case ITALIAN:
+ _currentCommandKeys = _commandKeys[3];
+ break;
+ case HEBREW:
+ _currentCommandKeys = _commandKeys[4];
+ break;
+ case SPANISH:
+ _currentCommandKeys = _commandKeys[5];
+ break;
+ default:
+ error("Unknown language");
+ break;
+ }
+}
+
+void Input::delay() {
+ delay(_fastMode ? DELAY_SHORT : DELAY_NORMAL);
+}
+
+void Input::delay(uint amount) {
+ if (_idleTime < DELAY_SCREEN_BLANKER) {
+ _idleTime += amount;
+ }
+ uint32 end = _system->getMillis() + amount;
+ do {
+ OSystem::Event event;
+ while (_system->pollEvent(event)) {
+ _idleTime = 0;
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.flags == OSystem::KBD_CTRL) {
+ if (event.kbd.keycode == 'd') {
+ _debugger = true;
+ } else if (event.kbd.keycode == 'f') {
+ _fastMode = !_fastMode;
+ }
+ } else {
+ _inKey = event.kbd.keycode;
+ }
+ break;
+
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouse_x = event.mouse.x;
+ _mouse_y = event.mouse.y;
+ break;
+
+ case OSystem::EVENT_LBUTTONDOWN:
+ _mouseButton |= MOUSE_LBUTTON;
+ _mouse_x = event.mouse.x;
+ _mouse_y = event.mouse.y;
+ break;
+
+ case OSystem::EVENT_RBUTTONDOWN:
+ _mouseButton |= MOUSE_RBUTTON;
+ _mouse_x = event.mouse.x;
+ _mouse_y = event.mouse.y;
+ break;
+
+ case OSystem::EVENT_QUIT:
+ _system->quit();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (amount == 0)
+ break;
+
+ _system->delayMillis((amount > 20) ? 20 : amount);
+ } while (_system->getMillis() < end);
+}
+
+int Input::checkKeys() {
+
+ if (_inKey)
+ debug(6, "[Input::checkKeys] _inKey = %i", _inKey);
+
+ switch (_inKey) {
+ case KEY_SPACE:
+ _keyVerb = VERB_SKIP_TEXT;
+ break;
+ case KEY_COMMA:
+ _keyVerb = VERB_SCROLL_UP;
+ break;
+ case KEY_DOT:
+ _keyVerb = VERB_SCROLL_DOWN;
+ break;
+ case KEY_DIGIT_1:
+ _keyVerb = VERB_DIGIT_1;
+ break;
+ case KEY_DIGIT_2:
+ _keyVerb = VERB_DIGIT_2;
+ break;
+ case KEY_DIGIT_3:
+ _keyVerb = VERB_DIGIT_3;
+ break;
+ case KEY_DIGIT_4:
+ _keyVerb = VERB_DIGIT_4;
+ break;
+ case KEY_ESCAPE: // slip cutaway / dialogue
+ if (_canQuit) {
+ if (_cutawayRunning) {
+ debug(6, "[Input::checkKeys] Setting _cutawayQuit to true!");
+ _cutawayQuit = true;
+ }
+ if (_dialogueRunning)
+ _talkQuit = true;
+ }
+ break;
+ case KEY_F1: // use Journal
+ case KEY_F5:
+ if (_cutawayRunning) {
+ if (_canQuit) {
+ _keyVerb = VERB_USE_JOURNAL;
+ _cutawayQuit = _talkQuit = true;
+ }
+ } else {
+ _keyVerb = VERB_USE_JOURNAL;
+ if (_canQuit)
+ _talkQuit = true;
+ }
+ break;
+ case KEY_F11: // quicksave
+ _quickSave = true;
+ break;
+ case KEY_F12: // quickload
+ _quickLoad = true;
+ break;
+ default:
+ for (int i = 0; i < ARRAYSIZE(_verbKeys); ++i) {
+ if (_inKey == _currentCommandKeys[i]) {
+ _keyVerb = _verbKeys[i];
+ break;
+ }
+ }
+ break;
+ }
+
+ int inKey = _inKey;
+ _inKey = 0; // reset
+ return inKey;
+}
+
+
+} // End of namespace Queen
diff --git a/engines/queen/input.h b/engines/queen/input.h
new file mode 100644
index 0000000000..d1e86016c9
--- /dev/null
+++ b/engines/queen/input.h
@@ -0,0 +1,178 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef INPUT_H
+#define INPUT_H
+
+#include "common/util.h"
+#include "queen/defs.h"
+
+class OSystem;
+
+namespace Queen {
+
+class Input {
+public:
+
+ //! Adjust here to change delays!
+ enum {
+ DELAY_SHORT = 10,
+ DELAY_NORMAL = 100,
+ DELAY_SCREEN_BLANKER = 5 * 60 * 1000
+ };
+ enum {
+ MOUSE_LBUTTON = 1,
+ MOUSE_RBUTTON = 2
+ };
+
+ Input(Language language, OSystem *system);
+
+ //! calls the other delay() with a value adjusted depending on _fastMode
+ void delay();
+
+ //! moved QueenEngine::delay() here
+ void delay(uint amount);
+
+ //! convert input to verb
+ int checkKeys();
+
+ //! use instead of KEYVERB=0
+ void clearKeyVerb() { _keyVerb = VERB_NONE; }
+
+ void canQuit(bool cq) { _canQuit = cq; }
+
+ bool cutawayRunning() const { return _cutawayRunning; }
+ void cutawayRunning(bool running) { _cutawayRunning = running; }
+
+ bool cutawayQuit() const { return _cutawayQuit; }
+ void cutawayQuitReset() { _cutawayQuit = false; }
+
+ void dialogueRunning(bool running) { _dialogueRunning = running; }
+
+ bool talkQuit() const { return _talkQuit; }
+ void talkQuitReset() { _talkQuit = false; }
+
+ bool quickSave() const { return _quickSave; }
+ void quickSaveReset() { _quickSave = false; }
+ bool quickLoad() const { return _quickLoad; }
+ void quickLoadReset() { _quickLoad = false; }
+ bool debugger() const { return _debugger; }
+ void debuggerReset() { _debugger = false; }
+
+ bool fastMode() const { return _fastMode; }
+ void fastMode(bool fm) { _fastMode = fm; }
+
+ Verb keyVerb() const { return _keyVerb; }
+
+ int mousePosX() const { return _mouse_x; }
+ int mousePosY() const { return _mouse_y; }
+
+ int mouseButton() const { return _mouseButton; }
+ void clearMouseButton() { _mouseButton = 0; }
+
+ //! returns user idle time (used by Display, to trigger the screensaver)
+ uint32 idleTime() const { return _idleTime; }
+
+private:
+
+ enum KeyCode {
+ KEY_SPACE = ' ',
+ KEY_COMMA = ',',
+ KEY_DOT = '.',
+
+ KEY_DIGIT_1 = '1',
+ KEY_DIGIT_2 = '2',
+ KEY_DIGIT_3 = '3',
+ KEY_DIGIT_4 = '4',
+
+ KEY_ESCAPE = 27,
+ KEY_RETURN = 13,
+ KEY_BACKSPACE = 8,
+
+ KEY_F1 = 282,
+ KEY_F11 = KEY_F1 + 10,
+ KEY_F5 = KEY_F1 + 4,
+ KEY_F12
+ };
+
+ enum {
+ LANGUAGE_COUNT = 6
+ };
+
+ //! used to get keyboard and mouse events
+ OSystem *_system;
+
+ //! some cutaways require update() run faster
+ bool _fastMode;
+
+ //! the current verb received from keyboard
+ Verb _keyVerb;
+
+ //! set if a cutaway is running
+ bool _cutawayRunning;
+
+ //! set this if we can quit
+ bool _canQuit;
+
+ //! moved Cutaway::_quit here
+ bool _cutawayQuit;
+
+ //! set if a dialogue is running
+ bool _dialogueRunning;
+
+ //! moved Talk::_quit here
+ bool _talkQuit;
+
+ //! set if quicksave requested
+ bool _quickSave;
+
+ //! set if quickload requested
+ bool _quickLoad;
+
+ //! set if debugger requested
+ bool _debugger;
+
+ //! set by delay();
+ int _inKey;
+
+ //! set by delay();
+ int _mouse_x, _mouse_y;
+
+ //! set by delay();
+ int _mouseButton;
+
+ //! user idle time
+ uint32 _idleTime;
+
+ //! command keys for current language
+ const char *_currentCommandKeys;
+
+ //! command keys for all languages
+ static const char *_commandKeys[LANGUAGE_COUNT];
+
+ //! verbs matching the command keys
+ static const Verb _verbKeys[8];
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/journal.cpp b/engines/queen/journal.cpp
new file mode 100644
index 0000000000..0aeb71e083
--- /dev/null
+++ b/engines/queen/journal.cpp
@@ -0,0 +1,587 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "queen/journal.h"
+
+#include "queen/bankman.h"
+#include "queen/display.h"
+#include "queen/graphics.h"
+#include "queen/grid.h"
+#include "queen/logic.h"
+#include "queen/music.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+
+namespace Queen {
+
+Journal::Journal(QueenEngine *vm)
+ : _vm(vm) {
+ _currentSavePage = 0;
+ _currentSaveSlot = 0;
+}
+
+void Journal::use() {
+ BobSlot *joe = _vm->graphics()->bob(0);
+ _prevJoeX = joe->x;
+ _prevJoeY = joe->y;
+
+ _panelMode = PM_NORMAL;
+ _system = &OSystem::instance();
+
+ _panelTextCount = 0;
+ memset(_panelTextY, 0, sizeof(_panelTextY));
+ memset(&_textField, 0, sizeof(_textField));
+
+ memset(_saveDescriptions, 0, sizeof(_saveDescriptions));
+ _vm->findGameStateDescriptions(_saveDescriptions);
+
+ setup();
+ redraw();
+ update();
+ _vm->display()->palFadeIn(ROOM_JOURNAL);
+
+ _quitMode = QM_LOOP;
+ while (_quitMode == QM_LOOP) {
+ OSystem::Event event;
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ handleKeyDown(event.kbd.ascii, event.kbd.keycode);
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ handleMouseDown(event.mouse.x, event.mouse.y);
+ break;
+ case OSystem::EVENT_WHEELUP:
+ handleMouseWheel(-1);
+ break;
+ case OSystem::EVENT_WHEELDOWN:
+ handleMouseWheel(1);
+ break;
+ case OSystem::EVENT_QUIT:
+ _system->quit();
+ break;
+ default:
+ break;
+ }
+ }
+ _system->delayMillis(20);
+ _system->updateScreen();
+ }
+
+ _vm->writeOptionSettings();
+
+ _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
+ _vm->graphics()->putCameraOnBob(0);
+ if (_quitMode == QM_CONTINUE) {
+ continueGame();
+ }
+}
+
+void Journal::continueGame() {
+ _vm->display()->fullscreen(false);
+ _vm->display()->forceFullRefresh();
+
+ _vm->logic()->joePos(_prevJoeX, _prevJoeY);
+ _vm->logic()->joeCutFacing(_vm->logic()->joeFacing());
+
+ _vm->logic()->oldRoom(_vm->logic()->currentRoom());
+ _vm->logic()->displayRoom(_vm->logic()->currentRoom(), RDM_FADE_JOE, 0, 0, false);
+}
+
+void Journal::setup() {
+ _vm->display()->palFadeOut(_vm->logic()->currentRoom());
+ _vm->display()->horizontalScroll(0);
+ _vm->display()->fullscreen(true);
+ _vm->graphics()->clearBobs();
+ _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
+ _vm->bankMan()->eraseFrames(false);
+ _vm->display()->textCurrentColor(INK_JOURNAL);
+
+ _vm->grid()->clear(GS_ROOM);
+ for (int i = 0; i < MAX_ZONES; ++i) {
+ const Zone *zn = &_zones[i];
+ _vm->grid()->setZone(GS_ROOM, zn->num, zn->x1, zn->y1, zn->x2, zn->y2);
+ }
+
+ _vm->display()->setupNewRoom("journal", ROOM_JOURNAL);
+ _vm->bankMan()->load("journal.BBK", JOURNAL_BANK);
+ for (int f = 1; f <= 20; ++f) {
+ int frameNum = JOURNAL_FRAMES + f;
+ _vm->bankMan()->unpack(f, frameNum, JOURNAL_BANK);
+ BobFrame *bf = _vm->bankMan()->fetchFrame(frameNum);
+ bf->xhotspot = 0;
+ bf->yhotspot = 0;
+ if (f == FRAME_INFO_BOX) { // adjust info box hot spot to put it always on top
+ bf->yhotspot = 200;
+ }
+ }
+ _vm->bankMan()->close(JOURNAL_BANK);
+
+ _textField.x = 136;
+ _textField.y = 9;
+ _textField.w = 146;
+ _textField.h = 13;
+}
+
+void Journal::redraw() {
+ drawNormalPanel();
+ drawConfigPanel();
+ drawSaveDescriptions();
+ drawSaveSlot();
+}
+
+void Journal::update() {
+ _vm->graphics()->sortBobs();
+ _vm->display()->prepareUpdate();
+ _vm->graphics()->drawBobs();
+ if (_textField.enabled) {
+ int16 x = _textField.x + _textField.posCursor;
+ int16 y = _textField.y + _currentSaveSlot * _textField.h + 8;
+ _vm->display()->drawBox(x, y, x + 6, y, INK_JOURNAL);
+ }
+ _vm->display()->forceFullRefresh();
+ _vm->display()->update();
+ _system->updateScreen();
+}
+
+void Journal::showBob(int bobNum, int16 x, int16 y, int frameNum) {
+ BobSlot *bob = _vm->graphics()->bob(bobNum);
+ bob->curPos(x, y);
+ bob->frameNum = JOURNAL_FRAMES + frameNum;
+}
+
+void Journal::hideBob(int bobNum) {
+ _vm->graphics()->bob(bobNum)->active = false;
+}
+
+void Journal::drawSaveDescriptions() {
+ for (int i = 0; i < NUM_SAVES_PER_PAGE; ++i) {
+ int n = _currentSavePage * 10 + i;
+ char nb[4];
+ sprintf(nb, "%d", n + 1);
+ int y = _textField.y + i * _textField.h;
+ _vm->display()->setText(_textField.x, y, _saveDescriptions[n], false);
+ _vm->display()->setText(_textField.x - 27, y + 1, nb, false);
+ }
+ // highlight current page
+ showBob(BOB_SAVE_PAGE, 300, 3 + _currentSavePage * 15, 6 + _currentSavePage);
+}
+
+void Journal::drawSaveSlot() {
+ showBob(BOB_SAVE_DESC, 130, 6 + _currentSaveSlot * 13, 17);
+}
+
+void Journal::enterYesNoPanelMode(int16 prevZoneNum, int titleNum) {
+ _panelMode = PM_YES_NO;
+ _prevZoneNum = prevZoneNum;
+ drawYesNoPanel(titleNum);
+}
+
+void Journal::exitYesNoPanelMode() {
+ _panelMode = PM_NORMAL;
+ if (_prevZoneNum == ZN_MAKE_ENTRY) {
+ closeTextField();
+ }
+ redraw();
+}
+
+void Journal::enterInfoPanelMode() {
+ _panelMode = PM_INFO_BOX;
+ _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
+ drawInfoPanel();
+}
+
+void Journal::exitInfoPanelMode() {
+ _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
+ hideBob(BOB_INFO_BOX);
+ redraw();
+ _panelMode = PM_NORMAL;
+}
+
+void Journal::handleKeyDown(uint16 ascii, int keycode) {
+ switch (_panelMode) {
+ case PM_INFO_BOX:
+ break;
+ case PM_YES_NO:
+ if (keycode == 27) {
+ exitYesNoPanelMode();
+ } else if (_textField.enabled) {
+ updateTextField(ascii, keycode);
+ }
+ break;
+ case PM_NORMAL:
+ if (keycode == 27) {
+ _quitMode = QM_CONTINUE;
+ }
+ break;
+ }
+}
+
+void Journal::handleMouseWheel(int inc) {
+ if (_panelMode == PM_NORMAL) {
+ int curSave = _currentSavePage * NUM_SAVES_PER_PAGE + _currentSaveSlot + inc;
+ if (curSave >= 0 && curSave < NUM_SAVES_PER_PAGE * 10) {
+ _currentSavePage = curSave / NUM_SAVES_PER_PAGE;
+ _currentSaveSlot = curSave % NUM_SAVES_PER_PAGE;
+ drawSaveDescriptions();
+ drawSaveSlot();
+ update();
+ }
+ }
+}
+
+void Journal::handleMouseDown(int x, int y) {
+ int val;
+ int16 zoneNum = _vm->grid()->findZoneForPos(GS_ROOM, x, y);
+ switch (_panelMode) {
+ case PM_INFO_BOX:
+ exitInfoPanelMode();
+ break;
+ case PM_YES_NO:
+ if (zoneNum == ZN_YES) {
+ _panelMode = PM_NORMAL;
+ int currentSlot = _currentSavePage * 10 + _currentSaveSlot;
+ switch (_prevZoneNum) {
+ case ZN_REVIEW_ENTRY:
+ if (_saveDescriptions[currentSlot][0]) {
+ _vm->graphics()->clearBobs();
+ _vm->display()->palFadeOut(ROOM_JOURNAL);
+ _vm->music()->stopSong();
+ _vm->loadGameState(currentSlot);
+ _vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
+ _quitMode = QM_RESTORE;
+ } else {
+ exitYesNoPanelMode();
+ }
+ break;
+ case ZN_MAKE_ENTRY:
+ if (_textField.text[0]) {
+ closeTextField();
+ _vm->saveGameState(currentSlot, _textField.text);
+ _quitMode = QM_CONTINUE;
+ } else {
+ exitYesNoPanelMode();
+ }
+ break;
+ case ZN_GIVEUP:
+ _quitMode = QM_CONTINUE;
+ _vm->quitGame();
+ break;
+ }
+ } else if (zoneNum == ZN_NO) {
+ exitYesNoPanelMode();
+ }
+ break;
+ case PM_NORMAL:
+ switch (zoneNum) {
+ case ZN_REVIEW_ENTRY:
+ enterYesNoPanelMode(zoneNum, TXT_REVIEW_ENTRY);
+ break;
+ case ZN_MAKE_ENTRY:
+ initTextField(_saveDescriptions[_currentSavePage * 10 + _currentSaveSlot]);
+ enterYesNoPanelMode(zoneNum, TXT_MAKE_ENTRY);
+ break;
+ case ZN_CLOSE:
+ _quitMode = QM_CONTINUE;
+ break;
+ case ZN_GIVEUP:
+ enterYesNoPanelMode(zoneNum, TXT_GIVE_UP);
+ break;
+ case ZN_TEXT_SPEED:
+ val = (x - 136) * QueenEngine::MAX_TEXT_SPEED / (266 - 136);
+ _vm->talkSpeed(val);
+ drawConfigPanel();
+ break;
+ case ZN_SFX_TOGGLE:
+ _vm->sound()->toggleSfx();
+ drawConfigPanel();
+ break;
+ case ZN_MUSIC_VOLUME:
+ val = (x - 136) * QueenEngine::MAX_MUSIC_VOLUME / (266 - 136);
+ _vm->music()->setVolume(val);
+ drawConfigPanel();
+ break;
+ case ZN_DESC_1:
+ case ZN_DESC_2:
+ case ZN_DESC_3:
+ case ZN_DESC_4:
+ case ZN_DESC_5:
+ case ZN_DESC_6:
+ case ZN_DESC_7:
+ case ZN_DESC_8:
+ case ZN_DESC_9:
+ case ZN_DESC_10:
+ _currentSaveSlot = zoneNum - ZN_DESC_1;
+ drawSaveSlot();
+ break;
+ case ZN_PAGE_A:
+ case ZN_PAGE_B:
+ case ZN_PAGE_C:
+ case ZN_PAGE_D:
+ case ZN_PAGE_E:
+ case ZN_PAGE_F:
+ case ZN_PAGE_G:
+ case ZN_PAGE_H:
+ case ZN_PAGE_I:
+ case ZN_PAGE_J:
+ _currentSavePage = zoneNum - ZN_PAGE_A;
+ drawSaveDescriptions();
+ break;
+ case ZN_INFO_BOX:
+ enterInfoPanelMode();
+ break;
+ case ZN_MUSIC_TOGGLE:
+ _vm->sound()->toggleMusic();
+ if (_vm->sound()->musicOn()) {
+ _vm->sound()->playLastSong();
+ } else {
+ _vm->music()->stopSong();
+ }
+ drawConfigPanel();
+ break;
+ case ZN_VOICE_TOGGLE:
+ _vm->sound()->toggleSpeech();
+ drawConfigPanel();
+ break;
+ case ZN_TEXT_TOGGLE:
+ _vm->subtitles(!_vm->subtitles());
+ drawConfigPanel();
+ break;
+ }
+ break;
+ }
+ update();
+}
+
+void Journal::drawPanelText(int y, const char *text) {
+ debug(7, "Journal::drawPanelText(%d, '%s')", y, text);
+ char s[80];
+ strcpy(s, text);
+ char *p = strchr(s, ' ');
+ if (p == NULL) {
+ int x = (128 - _vm->display()->textWidth(s)) / 2;
+ _vm->display()->setText(x, y, s, false);
+ assert(_panelTextCount < MAX_PANEL_TEXTS);
+ _panelTextY[_panelTextCount++] = y;
+ } else {
+ *p++ = '\0';
+ if (_vm->resource()->getLanguage() == HEBREW) {
+ drawPanelText(y - 5, p);
+ drawPanelText(y + 5, s);
+ } else {
+ drawPanelText(y - 5, s);
+ drawPanelText(y + 5, p);
+ }
+ }
+}
+
+void Journal::drawCheckBox(bool active, int bobNum, int16 x, int16 y, int frameNum) {
+ if (active) {
+ showBob(bobNum, x, y, frameNum);
+ } else {
+ hideBob(bobNum);
+ }
+}
+
+void Journal::drawSlideBar(int value, int maxValue, int bobNum, int16 y, int frameNum) {
+ showBob(bobNum, 136 + value * (266 - 136) / maxValue, y, frameNum);
+}
+
+void Journal::drawPanel(const int *frames, const int *titles, int n) {
+ for (int i = 0; i < _panelTextCount; ++i) {
+ _vm->display()->clearTexts(_panelTextY[i], _panelTextY[i]);
+ }
+ _panelTextCount = 0;
+ int bobNum = 1;
+ int y = 8;
+ while (n--) {
+ showBob(bobNum++, 32, y, *frames++);
+ drawPanelText(y + 12, _vm->logic()->joeResponse(*titles++));
+ y += 48;
+ }
+}
+
+void Journal::drawNormalPanel() {
+ static const int frames[] = { FRAME_BLUE_1, FRAME_BLUE_2, FRAME_BLUE_1, FRAME_ORANGE };
+ static const int titles[] = { TXT_REVIEW_ENTRY, TXT_MAKE_ENTRY, TXT_CLOSE, TXT_GIVE_UP };
+ drawPanel(frames, titles, 4);
+}
+
+void Journal::drawYesNoPanel(int titleNum) {
+ static const int frames[] = { FRAME_GREY, FRAME_BLUE_1, FRAME_BLUE_2 };
+ const int titles[] = { titleNum, TXT_YES, TXT_NO };
+ drawPanel(frames, titles, 3);
+
+ hideBob(BOB_LEFT_RECT_4);
+ hideBob(BOB_TALK_SPEED);
+ hideBob(BOB_SFX_TOGGLE);
+ hideBob(BOB_MUSIC_VOLUME);
+ hideBob(BOB_SPEECH_TOGGLE);
+ hideBob(BOB_TEXT_TOGGLE);
+ hideBob(BOB_MUSIC_TOGGLE);
+}
+
+void Journal::drawConfigPanel() {
+ _vm->checkOptionSettings();
+
+ drawSlideBar(_vm->talkSpeed(), QueenEngine::MAX_TEXT_SPEED, BOB_TALK_SPEED, 164, FRAME_BLUE_PIN);
+ drawSlideBar(_vm->music()->volume(), QueenEngine::MAX_MUSIC_VOLUME, BOB_MUSIC_VOLUME, 177, FRAME_GREEN_PIN);
+
+ drawCheckBox(_vm->sound()->sfxOn(), BOB_SFX_TOGGLE, 221, 155, FRAME_CHECK_BOX);
+ drawCheckBox(_vm->sound()->speechOn(), BOB_SPEECH_TOGGLE, 158, 155, FRAME_CHECK_BOX);
+ drawCheckBox(_vm->subtitles(), BOB_TEXT_TOGGLE, 125, 167, FRAME_CHECK_BOX);
+ drawCheckBox(_vm->sound()->musicOn(), BOB_MUSIC_TOGGLE, 125, 181, FRAME_CHECK_BOX);
+}
+
+void Journal::drawInfoPanel() {
+ showBob(BOB_INFO_BOX, 72, 221, FRAME_INFO_BOX);
+ const char *ver = _vm->resource()->JASVersion();
+ switch (ver[0]) {
+ case 'P':
+ _vm->display()->setTextCentered(132, "PC Hard Drive", false);
+ break;
+ case 'C':
+ _vm->display()->setTextCentered(132, "PC CD-ROM", false);
+ break;
+ case 'a':
+ _vm->display()->setTextCentered(132, "Amiga A500/600", false);
+ break;
+ case 'A':
+ _vm->display()->setTextCentered(132, "Amiga A1200", false);
+ break;
+ case 'c':
+ _vm->display()->setTextCentered(132, "Amiga CD-32", false);
+ break;
+ }
+ switch (ver[1]) {
+ case 'E':
+ _vm->display()->setTextCentered(144, "English", false);
+ break;
+ case 'F' :
+ _vm->display()->setTextCentered(144, "Fran\x87""ais", false);
+ break;
+ case 'G':
+ _vm->display()->setTextCentered(144, "Deutsch", false);
+ break;
+ case 'H':
+ _vm->display()->setTextCentered(144, "Hebrew", false);
+ break;
+ case 'I':
+ _vm->display()->setTextCentered(144, "Italiano", false);
+ break;
+ case 'S':
+ _vm->display()->setTextCentered(144, "Espa\xA4""ol", false);
+ break;
+ }
+ char versionId[13];
+ sprintf(versionId, "Version %c.%c%c", ver[2], ver[3], ver[4]);
+ _vm->display()->setTextCentered(156, versionId, false);
+}
+
+void Journal::initTextField(const char *desc) {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+ _textField.enabled = true;
+ _textField.posCursor = _vm->display()->textWidth(desc);
+ _textField.textCharsCount = strlen(desc);
+ memset(_textField.text, 0, sizeof(_textField.text));
+ strcpy(_textField.text, desc);
+}
+
+void Journal::updateTextField(uint16 ascii, int keycode) {
+ bool dirty = false;
+ switch (keycode) {
+ case 8: // backspace
+ if (_textField.textCharsCount > 0) {
+ --_textField.textCharsCount;
+ _textField.text[_textField.textCharsCount] = '\0';
+ dirty = true;
+ }
+ break;
+ case '\n':
+ case '\r':
+ if (_textField.text[0]) {
+ closeTextField();
+ int currentSlot = _currentSavePage * 10 + _currentSaveSlot;
+ _vm->saveGameState(currentSlot, _textField.text);
+ _quitMode = QM_CONTINUE;
+ }
+ break;
+ default:
+ if (isprint((char)ascii) &&
+ _textField.textCharsCount < (sizeof(_textField.text) - 1) &&
+ _vm->display()->textWidth(_textField.text) < _textField.w) {
+ _textField.text[_textField.textCharsCount] = (char)ascii;
+ ++_textField.textCharsCount;
+ dirty = true;
+ }
+ break;
+ }
+ if (dirty) {
+ _vm->display()->setText(_textField.x, _textField.y + _currentSaveSlot * _textField.h, _textField.text, false);
+ _textField.posCursor = _vm->display()->textWidth(_textField.text);
+ update();
+ }
+}
+
+void Journal::closeTextField() {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ _textField.enabled = false;
+}
+
+const Journal::Zone Journal::_zones[] = {
+ { ZN_REVIEW_ENTRY, 32, 8, 96, 40 },
+ { ZN_MAKE_ENTRY, 32, 56, 96, 88 }, // == ZN_YES
+ { ZN_CLOSE, 32, 104, 96, 136 }, // == ZN_NO
+ { ZN_GIVEUP, 32, 152, 96, 184 },
+ { ZN_TEXT_SPEED, 136, 169, 265, 176 },
+ { ZN_SFX_TOGGLE, 197, 155, 231, 164 },
+ { ZN_MUSIC_VOLUME, 136, 182, 265, 189 },
+ { ZN_DESC_1, 131, 7, 290, 18 },
+ { ZN_DESC_2, 131, 20, 290, 31 },
+ { ZN_DESC_3, 131, 33, 290, 44 },
+ { ZN_DESC_4, 131, 46, 290, 57 },
+ { ZN_DESC_5, 131, 59, 290, 70 },
+ { ZN_DESC_6, 131, 72, 290, 83 },
+ { ZN_DESC_7, 131, 85, 290, 96 },
+ { ZN_DESC_8, 131, 98, 290, 109 },
+ { ZN_DESC_9, 131, 111, 290, 122 },
+ { ZN_DESC_10, 131, 124, 290, 135 },
+ { ZN_PAGE_A, 300, 4, 319, 17 },
+ { ZN_PAGE_B, 300, 19, 319, 32 },
+ { ZN_PAGE_C, 300, 34, 319, 47 },
+ { ZN_PAGE_D, 300, 49, 319, 62 },
+ { ZN_PAGE_E, 300, 64, 319, 77 },
+ { ZN_PAGE_F, 300, 79, 319, 92 },
+ { ZN_PAGE_G, 300, 94, 319, 107 },
+ { ZN_PAGE_H, 300, 109, 319, 122 },
+ { ZN_PAGE_I, 300, 124, 319, 137 },
+ { ZN_PAGE_J, 300, 139, 319, 152 },
+ { ZN_INFO_BOX, 273, 146, 295, 189 },
+ { ZN_MUSIC_TOGGLE, 109, 181, 135, 190 },
+ { ZN_VOICE_TOGGLE, 134, 155, 168, 164 },
+ { ZN_TEXT_TOGGLE, 109, 168, 135, 177 }
+};
+
+} // End of namespace Queen
diff --git a/engines/queen/journal.h b/engines/queen/journal.h
new file mode 100644
index 0000000000..ab3e2a0ecd
--- /dev/null
+++ b/engines/queen/journal.h
@@ -0,0 +1,208 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENJOURNAL_H
+#define QUEENJOURNAL_H
+
+#include "common/util.h"
+
+class OSystem;
+
+namespace Queen {
+
+class QueenEngine;
+
+class Journal {
+public:
+
+ Journal(QueenEngine *vm);
+ void use();
+
+ enum {
+ JOURNAL_BANK = 8,
+ JOURNAL_FRAMES = 40
+ };
+
+ enum {
+ ZN_REVIEW_ENTRY = 1,
+ ZN_MAKE_ENTRY = 2,
+ ZN_YES = ZN_MAKE_ENTRY,
+ ZN_CLOSE = 3,
+ ZN_NO = ZN_CLOSE,
+ ZN_GIVEUP = 4,
+ ZN_TEXT_SPEED = 5,
+ ZN_SFX_TOGGLE = 6,
+ ZN_MUSIC_VOLUME = 7,
+ ZN_DESC_1 = 8,
+ ZN_DESC_2 = 9,
+ ZN_DESC_3 = 10,
+ ZN_DESC_4 = 11,
+ ZN_DESC_5 = 12,
+ ZN_DESC_6 = 13,
+ ZN_DESC_7 = 14,
+ ZN_DESC_8 = 15,
+ ZN_DESC_9 = 16,
+ ZN_DESC_10 = 17,
+ ZN_PAGE_A = 18,
+ ZN_PAGE_B = 19,
+ ZN_PAGE_C = 20,
+ ZN_PAGE_D = 21,
+ ZN_PAGE_E = 22,
+ ZN_PAGE_F = 23,
+ ZN_PAGE_G = 24,
+ ZN_PAGE_H = 25,
+ ZN_PAGE_I = 26,
+ ZN_PAGE_J = 27,
+ ZN_INFO_BOX = 28,
+ ZN_MUSIC_TOGGLE = 29,
+ ZN_VOICE_TOGGLE = 30,
+ ZN_TEXT_TOGGLE = 31
+ };
+
+ enum {
+ BOB_LEFT_RECT_1 = 1,
+ BOB_LEFT_RECT_2 = 2,
+ BOB_LEFT_RECT_3 = 3,
+ BOB_LEFT_RECT_4 = 4,
+ BOB_TALK_SPEED = 5,
+ BOB_SFX_TOGGLE = 6,
+ BOB_MUSIC_VOLUME = 7,
+ BOB_SAVE_DESC = 8,
+ BOB_SAVE_PAGE = 9,
+ BOB_SPEECH_TOGGLE = 10,
+ BOB_TEXT_TOGGLE = 11,
+ BOB_MUSIC_TOGGLE = 12,
+ BOB_INFO_BOX = 13
+ };
+
+ enum {
+ FRAME_BLUE_1 = 1,
+ FRAME_BLUE_2 = 2,
+ FRAME_ORANGE = 3,
+ FRAME_GREY = 5,
+ FRAME_CHECK_BOX = 16,
+ FRAME_BLUE_PIN = 18,
+ FRAME_GREEN_PIN = 19,
+ FRAME_INFO_BOX = 20
+ };
+
+ enum {
+ TXT_CLOSE = 30,
+ TXT_GIVE_UP = 31,
+ TXT_MAKE_ENTRY = 32,
+ TXT_REVIEW_ENTRY = 33,
+ TXT_YES = 34,
+ TXT_NO = 35
+ };
+
+ enum {
+ NUM_SAVES_PER_PAGE = 10,
+ MAX_PANEL_TEXTS = 8,
+ MAX_ZONES = 31
+ };
+
+ enum PanelMode {
+ PM_NORMAL,
+ PM_INFO_BOX,
+ PM_YES_NO
+ };
+
+ enum QuitMode {
+ QM_LOOP,
+ QM_RESTORE,
+ QM_CONTINUE
+ };
+
+
+private:
+
+ void continueGame();
+
+ void setup();
+ void redraw();
+ void update();
+
+ void showBob(int bobNum, int16 x, int16 y, int frameNum);
+ void hideBob(int bobNum);
+
+ void drawSaveDescriptions();
+ void drawSaveSlot();
+
+ void enterYesNoPanelMode(int16 prevZoneNum, int titleNum);
+ void exitYesNoPanelMode();
+ void enterInfoPanelMode();
+ void exitInfoPanelMode();
+
+ void handleMouseWheel(int inc);
+ void handleMouseDown(int x, int y);
+ void handleKeyDown(uint16 ascii, int keycode);
+
+ void drawPanelText(int y, const char *text);
+ void drawCheckBox(bool active, int bobNum, int16 x, int16 y, int frameNum);
+ void drawSlideBar(int value, int maxValue, int bobNum, int16 y, int frameNum);
+ void drawPanel(const int *frames, const int *titles, int n);
+ void drawNormalPanel();
+ void drawYesNoPanel(int titleNum);
+ void drawConfigPanel();
+ void drawInfoPanel();
+
+ void initTextField(const char *desc);
+ void updateTextField(uint16 ascii, int keycode);
+ void closeTextField();
+
+ struct TextField {
+ bool enabled;
+ int posCursor;
+ uint textCharsCount;
+ char text[32];
+ int x, y;
+ int w, h;
+ };
+
+ struct Zone {
+ int num;
+ int16 x1, y1, x2, y2;
+ };
+
+ PanelMode _panelMode;
+ QuitMode _quitMode;
+
+ int _currentSavePage;
+ int _currentSaveSlot;
+
+ int _prevJoeX, _prevJoeY;
+
+ int _panelTextCount;
+ int _panelTextY[MAX_PANEL_TEXTS];
+ TextField _textField;
+ uint16 _prevZoneNum;
+ char _saveDescriptions[100][32];
+
+ OSystem *_system;
+ QueenEngine *_vm;
+
+ static const Zone _zones[MAX_ZONES];
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/logic.cpp b/engines/queen/logic.cpp
new file mode 100644
index 0000000000..a535907dd9
--- /dev/null
+++ b/engines/queen/logic.cpp
@@ -0,0 +1,2221 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/logic.h"
+
+#include "common/config-manager.h"
+#include "queen/bankman.h"
+#include "queen/command.h"
+#include "queen/credits.h"
+#include "queen/cutaway.h"
+#include "queen/debug.h"
+#include "queen/defs.h"
+#include "queen/display.h"
+#include "queen/graphics.h"
+#include "queen/grid.h"
+#include "queen/input.h"
+#include "queen/journal.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+#include "queen/state.h"
+#include "queen/talk.h"
+#include "queen/walk.h"
+
+namespace Queen {
+
+static Common::String trim(const Common::String &s) {
+ const char *p;
+
+ p = s.c_str();
+ while (*p == ' ') ++p;
+ int start = p - s.c_str();
+
+ p = s.c_str() + s.size() - 1;
+ while (p != s.c_str() && *p == ' ') --p;
+ int end = p - s.c_str();
+
+ return Common::String(s.c_str() + start, end - start + 1);
+}
+
+Logic::Logic(QueenEngine *vm)
+ : _credits(NULL), _objectData(NULL), _roomData(NULL), _sfxName(NULL),
+ _itemData(NULL), _graphicData(NULL), _walkOffData(NULL), _objectDescription(NULL),
+ _furnitureData(NULL), _actorData(NULL), _graphicAnim(NULL), _vm(vm) {
+ _joe.x = _joe.y = 0;
+ _joe.scale = 100;
+ _joe.walk = JWM_NORMAL;
+ memset(_gameState, 0, sizeof(_gameState));
+ memset(_talkSelected, 0, sizeof(_talkSelected));
+ _puzzleAttemptCount = 0;
+ _journal = new Journal(vm);
+ _scene = 0;
+ initialise();
+}
+
+Logic::~Logic() {
+ delete _journal;
+ delete _credits;
+ delete[] _objectData;
+ delete[] _roomData;
+ delete[] _sfxName;
+ delete[] _itemData;
+ delete[] _graphicData;
+ delete[] _walkOffData;
+ delete[] _objectDescription;
+ delete[] _furnitureData;
+ delete[] _actorData;
+ delete[] _graphicAnim;
+}
+
+void Logic::initialise() {
+ int16 i;
+
+ uint8 *jas = _vm->resource()->loadFile("QUEEN.JAS", 20);
+ uint8 *ptr = jas;
+
+ _numRooms = READ_BE_UINT16(ptr); ptr += 2;
+ _numNames = READ_BE_UINT16(ptr); ptr += 2;
+ _numObjects = READ_BE_UINT16(ptr); ptr += 2;
+ _numDescriptions = READ_BE_UINT16(ptr); ptr += 2;
+
+ _objectData = new ObjectData[_numObjects + 1];
+ memset(&_objectData[0], 0, sizeof(ObjectData));
+ for (i = 1; i <= _numObjects; i++) {
+ _objectData[i].readFromBE(ptr);
+ }
+
+ _roomData = new uint16[_numRooms + 2];
+ _roomData[0] = 0;
+ for (i = 1; i <= (_numRooms + 1); i++) {
+ _roomData[i] = READ_BE_UINT16(ptr); ptr += 2;
+ }
+ _roomData[_numRooms + 1] = _numObjects;
+
+ if (_vm->resource()->isDemo()) {
+ _sfxName = NULL;
+ } else {
+ _sfxName = new uint16[_numRooms + 1];
+ _sfxName[0] = 0;
+ for (i = 1; i <= _numRooms; i++) {
+ _sfxName[i] = READ_BE_UINT16(ptr); ptr += 2;
+ }
+ }
+
+ _numItems = READ_BE_UINT16(ptr); ptr += 2;
+ _itemData = new ItemData[_numItems + 1];
+ memset(&_itemData[0], 0, sizeof(ItemData));
+ for (i = 1; i <= _numItems; i++) {
+ _itemData[i].readFromBE(ptr);
+ }
+
+ _numGraphics = READ_BE_UINT16(ptr); ptr += 2;
+ _graphicData = new GraphicData[_numGraphics + 1];
+ memset(&_graphicData[0], 0, sizeof(GraphicData));
+ for (i = 1; i <= _numGraphics; i++) {
+ _graphicData[i].readFromBE(ptr);
+ }
+
+ _vm->grid()->readDataFrom(_numObjects, _numRooms, ptr);
+
+ _numWalkOffs = READ_BE_UINT16(ptr); ptr += 2;
+ _walkOffData = new WalkOffData[_numWalkOffs + 1];
+ memset(&_walkOffData[0], 0, sizeof(WalkOffData));
+ for (i = 1; i <= _numWalkOffs; i++) {
+ _walkOffData[i].readFromBE(ptr);
+ }
+
+ _numObjDesc = READ_BE_UINT16(ptr); ptr += 2;
+ _objectDescription = new ObjectDescription[_numObjDesc + 1];
+ memset(&_objectDescription[0], 0, sizeof(ObjectDescription));
+ for (i = 1; i <= _numObjDesc; i++) {
+ _objectDescription[i].readFromBE(ptr);
+ }
+
+ _vm->command()->readCommandsFrom(ptr);
+
+ _entryObj = READ_BE_UINT16(ptr); ptr += 2;
+
+ _numFurniture = READ_BE_UINT16(ptr); ptr += 2;
+ _furnitureData = new FurnitureData[_numFurniture + 1];
+ memset(&_furnitureData[0], 0, sizeof(FurnitureData));
+ for (i = 1; i <= _numFurniture; i++) {
+ _furnitureData[i].readFromBE(ptr);
+ }
+
+ // Actors
+ _numActors = READ_BE_UINT16(ptr); ptr += 2;
+ _numAAnim = READ_BE_UINT16(ptr); ptr += 2;
+ _numAName = READ_BE_UINT16(ptr); ptr += 2;
+ _numAFile = READ_BE_UINT16(ptr); ptr += 2;
+
+ _actorData = new ActorData[_numActors + 1];
+ memset(&_actorData[0], 0, sizeof(ActorData));
+ for (i = 1; i <= _numActors; i++) {
+ _actorData[i].readFromBE(ptr);
+ }
+
+ _numGraphicAnim = READ_BE_UINT16(ptr); ptr += 2;
+
+ _graphicAnim = new GraphicAnim[_numGraphicAnim + 1];
+ if (_numGraphicAnim == 0) {
+ _graphicAnim[0].readFromBE(ptr);
+ } else {
+ memset(&_graphicAnim[0], 0, sizeof(GraphicAnim));
+ for (i = 1; i <= _numGraphicAnim; i++) {
+ _graphicAnim[i].readFromBE(ptr);
+ }
+ }
+
+ _currentRoom = _objectData[_entryObj].room;
+ _entryObj = 0;
+
+ if (memcmp(ptr, _vm->resource()->JASVersion(), 5) != 0) {
+ warning("Unexpected queen.jas file format");
+ }
+
+ delete[] jas;
+
+ uint32 size;
+ char *buf = (char *)_vm->resource()->loadFile("QUEEN2.JAS", 0, &size);
+ LineReader queen2jas(buf, size);
+
+ _objDescription.push_back("");
+ for (i = 1; i <= _numDescriptions; i++) {
+ _objDescription.push_back(queen2jas.nextLine());
+ }
+
+ // Patch for German text bug
+ if (_vm->resource()->getLanguage() == GERMAN) {
+ _objDescription[296] = "Es bringt nicht viel, das festzubinden.";
+ }
+
+ _objName.push_back("");
+ for (i = 1; i <= _numNames; i++) {
+ _objName.push_back(queen2jas.nextLine());
+ }
+
+ _roomName.push_back("");
+ for (i = 1; i <= _numRooms; i++) {
+ _roomName.push_back(queen2jas.nextLine());
+ }
+
+ _verbName.push_back("");
+ for (i = 1; i <= 12; i++) {
+ _verbName.push_back(queen2jas.nextLine());
+ }
+
+ _joeResponse.push_back("");
+ for (i = 1; i <= JOE_RESPONSE_MAX; i++) {
+ _joeResponse.push_back(queen2jas.nextLine());
+ }
+
+ // FIXME - the spanish version adds some space characters (0x20) at the
+ // beginning and the end of the journal button captions. As we don't need
+ // that 'trick' to center horizontally the texts, we simply trim them.
+ if (_vm->resource()->getLanguage() == SPANISH) {
+ for (i = 30; i <= 35; i++) {
+ _joeResponse[i] = trim(_joeResponse[i]);
+ }
+ }
+
+ _aAnim.push_back("");
+ for (i = 1; i <= _numAAnim; i++) {
+ _aAnim.push_back(queen2jas.nextLine());
+ }
+
+ _aName.push_back("");
+ for (i = 1; i <= _numAName; i++) {
+ _aName.push_back(queen2jas.nextLine());
+ }
+
+ _aFile.push_back("");
+ for (i = 1; i <= _numAFile; i++) {
+ _aFile.push_back(queen2jas.nextLine());
+ }
+}
+
+void Logic::start() {
+ _vm->command()->clear(false);
+ _vm->display()->setupPanel();
+ _vm->graphics()->unpackControlBank();
+ _vm->graphics()->setupMouseCursor();
+ setupJoe();
+ _vm->grid()->setupPanel();
+ inventorySetup();
+
+ _oldRoom = 0;
+ _newRoom = _currentRoom;
+}
+
+ObjectData* Logic::objectData(int index) const {
+ assert(index >= 0 && index <= _numObjects);
+ return &_objectData[index];
+}
+
+uint16 Logic::findBob(uint16 obj) const {
+ assert(obj <= _numObjects);
+
+ uint16 room = _objectData[obj].room;
+ assert(room <= _numRooms);
+
+ uint16 bobnum = 0;
+ int16 img = _objectData[obj].image;
+ if (img != 0) {
+ if (img == -3 || img == -4) {
+ // a person object
+ bobnum = findPersonNumber(obj, room);
+ } else {
+ uint16 bobtype = 0; // 1 for animated, 0 for static
+
+ if (img <= -10) {
+ // object has been turned off, but the image order hasn't been updated
+ if (_graphicData[-(img + 10)].lastFrame != 0) {
+ bobtype = 1;
+ }
+ } else if (img == -2) {
+ // -1 static, -2 animated
+ bobtype = 1;
+ } else if (img > 0) {
+ if (_graphicData[img].lastFrame != 0) {
+ bobtype = 1;
+ }
+ }
+
+ uint16 idxAnimated = 0;
+ uint16 idxStatic = 0;
+ for (uint16 i = _roomData[room] + 1; i <= obj; ++i) {
+ img = _objectData[i].image;
+ if (img <= -10) {
+ if (_graphicData[-(img + 10)].lastFrame != 0) {
+ ++idxAnimated;
+ } else {
+ ++idxStatic;
+ }
+ } else if (img > 0) {
+ if (img > 5000) {
+ img -= 5000;
+ }
+
+ assert (img <= _numGraphics);
+
+ if (_graphicData[img].lastFrame != 0) {
+ ++idxAnimated;
+ } else {
+ ++idxStatic;
+ }
+ } else if (img == -1) {
+ ++idxStatic;
+ } else if (img == -2) {
+ ++idxAnimated;
+ }
+ }
+ if (bobtype == 0) {
+ // static bob
+ if (idxStatic > 0) {
+ bobnum = 19 + _vm->graphics()->numStaticFurniture() + idxStatic;
+ }
+ } else {
+ // animated bob
+ if (idxAnimated > 0) {
+ bobnum = 4 + _vm->graphics()->numAnimatedFurniture() + idxAnimated;
+ }
+ }
+ }
+ }
+ return bobnum;
+}
+
+uint16 Logic::findFrame(uint16 obj) const {
+ uint16 framenum = 0;
+ uint16 room = _objectData[obj].room;
+ int16 img = _objectData[obj].image;
+ if (img == -3 || img == -4) {
+ uint16 bobnum = findPersonNumber(obj, room);
+ if (bobnum <= 3) {
+ framenum = 31 + bobnum;
+ }
+ } else {
+ uint16 idx = 0;
+ for (uint16 i = _roomData[room] + 1; i < obj; ++i) {
+ img = _objectData[i].image;
+ if (img <= -10) {
+ const GraphicData* pgd = &_graphicData[-(img + 10)];
+ if (pgd->lastFrame != 0) {
+ // skip all the frames of the animation
+ idx += ABS(pgd->lastFrame) - pgd->firstFrame + 1;
+ } else {
+ // static bob, skip one frame
+ ++idx;
+ }
+ } else if (img == -1) {
+ ++idx;
+ } else if (img > 0) {
+ if (img > 5000) {
+ img -= 5000;
+ }
+ const GraphicData* pgd = &_graphicData[img];
+ uint16 lastFrame = ABS(pgd->lastFrame);
+ if (pgd->firstFrame < 0) {
+ idx += lastFrame;
+ } else if (lastFrame != 0) {
+ idx += (lastFrame - pgd->firstFrame) + 1;
+ } else {
+ ++idx;
+ }
+ }
+ }
+
+ img = _objectData[obj].image;
+ if (img <= -10) {
+ const GraphicData* pgd = &_graphicData[-(img + 10)];
+ if (pgd->lastFrame != 0) {
+ idx += ABS(pgd->lastFrame) - pgd->firstFrame + 1;
+ } else {
+ ++idx;
+ }
+ } else if (img == -1 || img > 0) {
+ ++idx;
+ }
+
+ // calculate only if there are person frames
+ if (idx > 0) {
+ framenum = FRAMES_JOE + _vm->graphics()->numFurnitureFrames() + idx;
+ }
+ }
+ return framenum;
+}
+
+uint16 Logic::objectForPerson(uint16 bobNum) const {
+ uint16 bobcur = 0;
+ // first object number in the room
+ uint16 cur = currentRoomData() + 1;
+ // last object number in the room
+ uint16 last = _roomData[_currentRoom + 1];
+ for (; cur <= last; ++cur) {
+ int16 image = _objectData[cur].image;
+ if (image == -3 || image == -4) {
+ // the object is a bob
+ ++bobcur;
+ }
+ if (bobcur == bobNum) {
+ return cur;
+ }
+ }
+ return 0;
+}
+
+WalkOffData *Logic::walkOffPointForObject(int16 obj) const {
+ for (uint16 i = 1; i <= _numWalkOffs; ++i) {
+ if (_walkOffData[i].entryObj == obj) {
+ return &_walkOffData[i];
+ }
+ }
+ return NULL;
+}
+
+void Logic::joeWalk(JoeWalkMode walking) {
+ _joe.walk = walking;
+ // Do this so that Input doesn't need to know the walk value
+ _vm->input()->dialogueRunning(JWM_SPEAK == walking);
+}
+
+int16 Logic::gameState(int index) const {
+ assert(index >= 0 && index < GAME_STATE_COUNT);
+ return _gameState[index];
+}
+
+void Logic::gameState(int index, int16 newValue) {
+ assert(index >= 0 && index < GAME_STATE_COUNT);
+ debug(8, "Logic::gameState() [%d] = %d", index, newValue);
+ _gameState[index] = newValue;
+}
+
+const char *Logic::roomName(uint16 roomNum) const {
+ assert(roomNum >= 1 && roomNum <= _numRooms);
+ return _roomName[roomNum].c_str();
+}
+
+const char *Logic::objectName(uint16 objNum) const {
+ assert(objNum >= 1 && objNum <= _numNames);
+ return _objName[objNum].c_str();
+}
+
+const char *Logic::objectTextualDescription(uint16 objNum) const {
+ assert(objNum >= 1 && objNum <= _numDescriptions);
+ return _objDescription[objNum].c_str();
+}
+
+const char *Logic::joeResponse(int i) const {
+ assert(i >= 1 && i <= JOE_RESPONSE_MAX);
+ return _joeResponse[i].c_str();
+}
+
+const char *Logic::verbName(Verb v) const {
+ assert(v >= 0 && v <= 12);
+ return _verbName[v].c_str();
+}
+
+void Logic::eraseRoom() {
+ _vm->bankMan()->eraseFrames(false);
+ _vm->bankMan()->close(15);
+ _vm->bankMan()->close(11);
+ _vm->bankMan()->close(10);
+ _vm->bankMan()->close(12);
+
+ _vm->display()->palFadeOut(_currentRoom);
+
+ // invalidates all persons animations
+ _vm->graphics()->clearPersonFrames();
+ _vm->graphics()->eraseAllAnims();
+
+ uint16 cur = _roomData[_oldRoom] + 1;
+ uint16 last = _roomData[_oldRoom + 1];
+ for (; cur <= last; ++cur) {
+ ObjectData *pod = &_objectData[cur];
+ if (pod->name == 0) {
+ // object has been deleted, invalidate image
+ pod->image = 0;
+ } else if (pod->image > -4000 && pod->image <= -10) {
+ if (_graphicData[ABS(pod->image + 10)].lastFrame == 0) {
+ // static Bob
+ pod->image = -1;
+ } else {
+ // animated Bob
+ pod->image = -2;
+ }
+ }
+ }
+}
+
+void Logic::setupRoom(const char *room, int comPanel, bool inCutaway) {
+ // load backdrop image, init dynalum, setup colors
+ _vm->display()->setupNewRoom(room, _currentRoom);
+
+ // setup graphics to enter fullscreen/panel mode
+ _vm->display()->screenMode(comPanel, inCutaway);
+
+ _vm->grid()->setupNewRoom(_currentRoom, _roomData[_currentRoom]);
+
+ int16 furn[9];
+ uint16 furnTot = 0;
+ for (uint16 i = 1; i <= _numFurniture; ++i) {
+ if (_furnitureData[i].room == _currentRoom) {
+ ++furnTot;
+ furn[furnTot] = _furnitureData[i].objNum;
+ }
+ }
+ _vm->graphics()->setupNewRoom(room, _currentRoom, furn, furnTot);
+
+ _vm->display()->forceFullRefresh();
+}
+
+void Logic::displayRoom(uint16 room, RoomDisplayMode mode, uint16 scale, int comPanel, bool inCutaway) {
+ debug(6, "Logic::displayRoom(%d, %d, %d, %d, %d)", room, mode, scale, comPanel, inCutaway);
+
+ eraseRoom();
+
+ if (_credits)
+ _credits->nextRoom();
+
+ setupRoom(roomName(room), comPanel, inCutaway);
+ if (mode != RDM_FADE_NOJOE) {
+ setupJoeInRoom(mode != RDM_FADE_JOE_XY, scale);
+ }
+ if (mode != RDM_NOFADE_JOE) {
+ _vm->update();
+ BobSlot *joe = _vm->graphics()->bob(0);
+ _vm->display()->palFadeIn(_currentRoom, joe->active, joe->x, joe->y);
+ }
+ if (mode != RDM_FADE_NOJOE && joeX() != 0 && joeY() != 0) {
+ int16 jx = joeX();
+ int16 jy = joeY();
+ joePos(0, 0);
+ _vm->walk()->moveJoe(0, jx, jy, inCutaway);
+ }
+}
+
+ActorData *Logic::findActor(uint16 noun, const char *name) const {
+ uint16 obj = currentRoomData() + noun;
+ int16 img = objectData(obj)->image;
+ if (img != -3 && img != -4) {
+ warning("Logic::findActor() - Object %d is not a person", obj);
+ return NULL;
+ }
+
+ // search Bob number for the person
+ uint16 bobNum = findPersonNumber(obj, _currentRoom);
+
+ // search for a matching actor
+ if (bobNum > 0) {
+ for (uint16 i = 1; i <= _numActors; ++i) {
+ ActorData *pad = &_actorData[i];
+ if (pad->room == _currentRoom && gameState(pad->gsSlot) == pad->gsValue) {
+ if (bobNum == pad->bobNum || (name && _aName[pad->name] == name)) {
+ return pad;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+bool Logic::initPerson(uint16 noun, const char *actorName, bool loadBank, Person *pp) {
+ const ActorData *pad = findActor(noun, actorName);
+ if (pad != NULL) {
+ pp->actor = pad;
+ pp->name = _aName[pad->name].c_str();
+ if (pad->anim != 0) {
+ pp->anim = _aAnim[pad->anim].c_str();
+ } else {
+ pp->anim = NULL;
+ }
+ if (loadBank && pad->file != 0) {
+ _vm->bankMan()->load(_aFile[pad->file].c_str(), pad->bankNum);
+ // if there is no valid actor file (ie pad->file is 0), the person
+ // data is already loaded as it is included in objects room bank (.bbk)
+ }
+ pp->bobFrame = 31 + pp->actor->bobNum;
+ }
+ return pad != NULL;
+}
+
+uint16 Logic::findPersonNumber(uint16 obj, uint16 room) const {
+ uint16 num = 0;
+ for (uint16 i = _roomData[room] + 1; i <= obj; ++i) {
+ int16 img = _objectData[i].image;
+ if (img == -3 || img == -4) {
+ ++num;
+ }
+ }
+ return num;
+}
+
+void Logic::loadJoeBanks(const char *animBank, const char *standBank) {
+ _vm->bankMan()->load(animBank, 13);
+ for (int i = 11; i < 31; ++i) {
+ _vm->bankMan()->unpack(i - 10, i, 13);
+ }
+ _vm->bankMan()->close(13);
+
+ _vm->bankMan()->load(standBank, 7);
+ _vm->bankMan()->unpack(1, 35, 7);
+ _vm->bankMan()->unpack(3, 36, 7);
+ _vm->bankMan()->unpack(5, 37, 7);
+}
+
+void Logic::setupJoe() {
+ loadJoeBanks("joe_a.BBK", "joe_b.BBK");
+ joePrevFacing(DIR_FRONT);
+ joeFacing(DIR_FRONT);
+}
+
+void Logic::setupJoeInRoom(bool autoPosition, uint16 scale) {
+ debug(9, "Logic::setupJoeInRoom(%d, %d) joe.x=%d joe.y=%d", autoPosition, scale, _joe.x, _joe.y);
+
+ int16 oldx, oldy;
+ if (!autoPosition || joeX() != 0 || joeY() != 0) {
+ oldx = joeX();
+ oldy = joeY();
+ joePos(0, 0);
+ } else {
+ const ObjectData *pod = objectData(_entryObj);
+ // find the walk off point for the entry object and make
+ // Joe walking to that point
+ const WalkOffData *pwo = walkOffPointForObject(_entryObj);
+ if (pwo != NULL) {
+ oldx = pwo->x;
+ oldy = pwo->y;
+ // entryObj has a walk off point, then walk from there to object x,y
+ joePos(pod->x, pod->y);
+ } else {
+ // no walk off point, use object position
+ oldx = pod->x;
+ oldy = pod->y;
+ joePos(0, 0);
+ }
+ }
+
+ debug(6, "Logic::setupJoeInRoom() - oldx=%d, oldy=%d scale=%d", oldx, oldy, scale);
+
+ if (scale > 0 && scale < 100) {
+ joeScale(scale);
+ } else {
+ uint16 a = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy);
+ if (a > 0) {
+ joeScale(_vm->grid()->area(_currentRoom, a)->calcScale(oldy));
+ } else {
+ joeScale(100);
+ }
+ }
+
+ if (joeCutFacing() > 0) {
+ joeFacing(joeCutFacing());
+ joeCutFacing(0);
+ } else {
+ // check to see which way Joe entered room
+ const ObjectData *pod = objectData(_entryObj);
+ switch (State::findDirection(pod->state)) {
+ case DIR_BACK:
+ joeFacing(DIR_FRONT);
+ break;
+ case DIR_FRONT:
+ joeFacing(DIR_BACK);
+ break;
+ case DIR_LEFT:
+ joeFacing(DIR_RIGHT);
+ break;
+ case DIR_RIGHT:
+ joeFacing(DIR_LEFT);
+ break;
+ }
+ }
+ joePrevFacing(joeFacing());
+
+ BobSlot *pbs = _vm->graphics()->bob(0);
+ pbs->scale = joeScale();
+
+ if (_currentRoom == 108) {
+ _vm->graphics()->putCameraOnBob(-1);
+ _vm->bankMan()->load("joe_e.act", 7);
+ _vm->bankMan()->unpack(2, 31, 7);
+
+ _vm->display()->horizontalScroll(320);
+
+ joeFacing(DIR_RIGHT);
+ joeCutFacing(DIR_RIGHT);
+ joePrevFacing(DIR_RIGHT);
+ }
+
+ joeFace();
+ pbs->curPos(oldx, oldy);
+ pbs->frameNum = 31;
+}
+
+uint16 Logic::joeFace() {
+ debug(9, "Logic::joeFace() - curFace = %d, prevFace = %d", _joe.facing, _joe.prevFacing);
+ BobSlot *pbs = _vm->graphics()->bob(0);
+ uint16 frame;
+ if (_currentRoom == 108) {
+ frame = 1;
+ } else {
+ frame = 35;
+ if (joeFacing() == DIR_FRONT) {
+ if (joePrevFacing() == DIR_BACK) {
+ pbs->frameNum = 35;
+ _vm->update();
+ }
+ frame = 36;
+ } else if (joeFacing() == DIR_BACK) {
+ if (joePrevFacing() == DIR_FRONT) {
+ pbs->frameNum = 35;
+ _vm->update();
+ }
+ frame = 37;
+ } else if ((joeFacing() == DIR_LEFT && joePrevFacing() == DIR_RIGHT)
+ || (joeFacing() == DIR_RIGHT && joePrevFacing() == DIR_LEFT)) {
+ pbs->frameNum = 36;
+ _vm->update();
+ }
+ pbs->frameNum = frame;
+ pbs->scale = joeScale();
+ pbs->xflip = (joeFacing() == DIR_LEFT);
+ _vm->update();
+ joePrevFacing(joeFacing());
+ switch (frame) {
+ case 35:
+ frame = 1;
+ break;
+ case 36:
+ frame = 3;
+ break;
+ case 37:
+ frame = 5;
+ break;
+ }
+ }
+ pbs->frameNum = 31;
+ _vm->bankMan()->unpack(frame, pbs->frameNum, 7);
+ return frame;
+}
+
+void Logic::joeGrab(int16 grabState) {
+ uint16 frame = 0;
+ BobSlot *bobJoe = _vm->graphics()->bob(0);
+
+ switch (grabState) {
+ case STATE_GRAB_NONE:
+ break;
+ case STATE_GRAB_MID:
+ if (joeFacing() == DIR_BACK) {
+ frame = 6;
+ } else if (joeFacing() == DIR_FRONT) {
+ frame = 4;
+ } else {
+ frame = 2;
+ }
+ break;
+ case STATE_GRAB_DOWN:
+ if (joeFacing() == DIR_BACK) {
+ frame = 9;
+ } else {
+ frame = 8;
+ }
+ break;
+ case STATE_GRAB_UP:
+ // turn back
+ _vm->bankMan()->unpack(5, 31, 7);
+ bobJoe->xflip = (joeFacing() == DIR_LEFT);
+ bobJoe->scale = joeScale();
+ _vm->update();
+ // grab up
+ _vm->bankMan()->unpack(7, 31, 7);
+ bobJoe->xflip = (joeFacing() == DIR_LEFT);
+ bobJoe->scale = joeScale();
+ _vm->update();
+ // turn back
+ frame = 7;
+ break;
+ }
+
+ if (frame != 0) {
+ _vm->bankMan()->unpack(frame, 31, 7);
+ bobJoe->xflip = (joeFacing() == DIR_LEFT);
+ bobJoe->scale = joeScale();
+ _vm->update();
+
+ // extra delay for grab down
+ if (grabState == STATE_GRAB_DOWN) {
+ _vm->update();
+ _vm->update();
+ }
+ }
+}
+
+void Logic::joeUseDress(bool showCut) {
+ if (showCut) {
+ joeFacing(DIR_FRONT);
+ joeFace();
+ if (gameState(VAR_JOE_DRESSING_MODE) == 0) {
+ playCutaway("cdres.CUT");
+ inventoryInsertItem(ITEM_CLOTHES);
+ } else {
+ playCutaway("cudrs.CUT");
+ }
+ }
+ _vm->display()->palSetJoeDress();
+ loadJoeBanks("JoeD_A.BBK", "JoeD_B.BBK");
+ inventoryDeleteItem(ITEM_DRESS);
+ gameState(VAR_JOE_DRESSING_MODE, 2);
+}
+
+void Logic::joeUseClothes(bool showCut) {
+ if (showCut) {
+ joeFacing(DIR_FRONT);
+ joeFace();
+ playCutaway("cdclo.CUT");
+ inventoryInsertItem(ITEM_DRESS);
+ }
+ _vm->display()->palSetJoeNormal();
+ loadJoeBanks("Joe_A.BBK", "Joe_B.BBK");
+ inventoryDeleteItem(ITEM_CLOTHES);
+ gameState(VAR_JOE_DRESSING_MODE, 0);
+}
+
+void Logic::joeUseUnderwear() {
+ _vm->display()->palSetJoeNormal();
+ loadJoeBanks("JoeU_A.BBK", "JoeU_B.BBK");
+ gameState(VAR_JOE_DRESSING_MODE, 1);
+}
+
+void Logic::makePersonSpeak(const char *sentence, Person *person, const char *voiceFilePrefix) {
+ _vm->command()->clear(false);
+ Talk::speak(sentence, person, voiceFilePrefix, _vm);
+}
+
+void Logic::startDialogue(const char *dlgFile, int personInRoom, char *cutaway) {
+ ObjectData *data = objectData(_roomData[_currentRoom] + personInRoom);
+ if (data->name > 0 && data->entryObj <= 0) {
+ if (State::findTalk(data->state) == STATE_TALK_MUTE) {
+ // 'I can't talk to that'
+ makeJoeSpeak(24 + _vm->randomizer.getRandomNumber(2));
+ } else {
+ char cutawayFile[20];
+ if (cutaway == NULL) {
+ cutaway = cutawayFile;
+ }
+ _vm->display()->fullscreen(true);
+ Talk::talk(dlgFile, personInRoom, cutaway, _vm);
+ if (!cutaway[0]) {
+ _vm->display()->fullscreen(false);
+ }
+ }
+ }
+}
+
+void Logic::playCutaway(const char *cutFile, char *next) {
+ char nextFile[20];
+ if (next == NULL) {
+ next = nextFile;
+ }
+ _vm->display()->clearTexts(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
+ Cutaway::run(cutFile, next, _vm);
+}
+
+void Logic::makeJoeSpeak(uint16 descNum, bool objectType) {
+ const char *text = objectType ? _objDescription[descNum].c_str() : _joeResponse[descNum].c_str();
+ if (objectType) {
+ descNum += JOE_RESPONSE_MAX;
+ }
+ char descFilePrefix[10];
+ sprintf(descFilePrefix, "JOE%04i", descNum);
+ makePersonSpeak(text, NULL, descFilePrefix);
+}
+
+uint16 Logic::findInventoryItem(int invSlot) const {
+ // queen.c l.3894-3898
+ if (invSlot >= 0 && invSlot < 4) {
+ return _inventoryItem[invSlot];
+ }
+ return 0;
+}
+
+void Logic::inventorySetup() {
+ _vm->bankMan()->load("objects.BBK", 14);
+ if (_vm->resource()->isInterview()) {
+ _inventoryItem[0] = 1;
+ _inventoryItem[1] = 2;
+ _inventoryItem[2] = 3;
+ _inventoryItem[3] = 4;
+ } else {
+ _inventoryItem[0] = ITEM_BAT;
+ _inventoryItem[1] = ITEM_JOURNAL;
+ _inventoryItem[2] = ITEM_NONE;
+ _inventoryItem[3] = ITEM_NONE;
+ }
+}
+
+void Logic::inventoryRefresh() {
+ uint16 x = 182;
+ for (int i = 0; i < 4; ++i) {
+ uint16 itemNum = _inventoryItem[i];
+ if (itemNum != 0) {
+ // 1st object in inventory uses frame 9,
+ // whereas 2nd, 3rd and 4th uses frame 8
+ uint16 dstFrame = (itemNum != 0) ? 8 : 9;
+ // unpack frame for object and draw it
+ _vm->bankMan()->unpack(_itemData[itemNum].frame, dstFrame, 14);
+ _vm->graphics()->drawInventoryItem(dstFrame, x, 14);
+ } else {
+ // no object, clear the panel
+ _vm->graphics()->drawInventoryItem(0, x, 14);
+ }
+ x += 35;
+ }
+}
+
+int16 Logic::previousInventoryItem(int16 first) const {
+ int i;
+ for (i = first - 1; i >= 1; i--)
+ if (_itemData[i].name > 0)
+ return i;
+ for (i = _numItems; i > first; i--)
+ if (_itemData[i].name > 0)
+ return i;
+
+ return 0; //nothing found
+}
+
+int16 Logic::nextInventoryItem(int16 first) const {
+ int i;
+ for (i = first + 1; i < _numItems; i++)
+ if (_itemData[i].name > 0)
+ return i;
+ for (i = 1; i < first; i++)
+ if (_itemData[i].name > 0)
+ return i;
+
+ return 0; //nothing found
+}
+
+void Logic::removeDuplicateItems() {
+ for (int i = 0; i < 4; i++)
+ for (int j = i + 1; j < 4; j++)
+ if (_inventoryItem[i] == _inventoryItem[j])
+ _inventoryItem[j] = ITEM_NONE;
+}
+
+uint16 Logic::numItemsInventory() const {
+ uint16 count = 0;
+ for (int i = 1; i < _numItems; i++)
+ if (_itemData[i].name > 0)
+ count++;
+
+ return count;
+}
+
+void Logic::inventoryInsertItem(uint16 itemNum, bool refresh) {
+ int16 item = _inventoryItem[0] = (int16)itemNum;
+ _itemData[itemNum].name = ABS(_itemData[itemNum].name); //set visible
+ for (int i = 1; i < 4; i++) {
+ item = nextInventoryItem(item);
+ _inventoryItem[i] = item;
+ removeDuplicateItems();
+ }
+
+ if (refresh)
+ inventoryRefresh();
+}
+
+void Logic::inventoryDeleteItem(uint16 itemNum, bool refresh) {
+ int16 item = (int16)itemNum;
+ _itemData[itemNum].name = -ABS(_itemData[itemNum].name); //set invisible
+ for (int i = 0; i < 4; i++) {
+ item = nextInventoryItem(item);
+ _inventoryItem[i] = item;
+ removeDuplicateItems();
+ }
+
+ if (refresh)
+ inventoryRefresh();
+}
+
+void Logic::inventoryScroll(uint16 count, bool up) {
+ if (!(numItemsInventory() > 4))
+ return;
+ while (count--) {
+ if (up) {
+ for (int i = 3; i > 0; i--)
+ _inventoryItem[i] = _inventoryItem[i - 1];
+ _inventoryItem[0] = previousInventoryItem(_inventoryItem[0]);
+ } else {
+ for (int i = 0; i < 3; i++)
+ _inventoryItem[i] = _inventoryItem[i + 1];
+ _inventoryItem[3] = nextInventoryItem(_inventoryItem[3]);
+ }
+ }
+
+ inventoryRefresh();
+}
+
+void Logic::removeHotelItemsFromInventory() {
+ if (currentRoom() == 1 && gameState(VAR_HOTEL_ITEMS_REMOVED) == 0) {
+ inventoryDeleteItem(ITEM_CROWBAR, false);
+ inventoryDeleteItem(ITEM_DRESS, false);
+ inventoryDeleteItem(ITEM_CLOTHES, false);
+ inventoryDeleteItem(ITEM_HAY, false);
+ inventoryDeleteItem(ITEM_OIL, false);
+ inventoryDeleteItem(ITEM_CHICKEN, false);
+ gameState(VAR_HOTEL_ITEMS_REMOVED, 1);
+ inventoryRefresh();
+ }
+}
+
+void Logic::objectCopy(int dummyObjectIndex, int realObjectIndex) {
+ // copy data from dummy object to real object, if COPY_FROM object
+ // images are greater than COPY_TO Object images then swap the objects around.
+
+ ObjectData *dummyObject = objectData(dummyObjectIndex);
+ ObjectData *realObject = objectData(realObjectIndex);
+
+ int fromState = (dummyObject->name < 0) ? -1 : 0;
+
+ int frameCountReal = 1;
+ int frameCountDummy = 1;
+
+ int graphic = realObject->image;
+ if (graphic > 0) {
+ if (graphic > 5000)
+ graphic -= 5000;
+
+ GraphicData *data = graphicData(graphic);
+
+ if (data->lastFrame > 0)
+ frameCountReal = data->lastFrame - data->firstFrame + 1;
+
+ graphic = dummyObject->image;
+ if (graphic > 0) {
+ if (graphic > 5000)
+ graphic -= 5000;
+
+ data = graphicData(graphic);
+
+ if (data->lastFrame > 0)
+ frameCountDummy = data->lastFrame - data->firstFrame + 1;
+ }
+ }
+
+ ObjectData temp = *realObject;
+ *realObject = *dummyObject;
+
+ if (frameCountDummy > frameCountReal)
+ *dummyObject = temp;
+
+ realObject->name = ABS(realObject->name);
+
+ if (fromState == -1)
+ dummyObject->name = -ABS(dummyObject->name);
+
+ for (int i = 1; i <= _numWalkOffs; i++) {
+ WalkOffData *walkOff = &_walkOffData[i];
+ if (walkOff->entryObj == (int16)dummyObjectIndex) {
+ walkOff->entryObj = (int16)realObjectIndex;
+ break;
+ }
+ }
+}
+
+void Logic::handleSpecialArea(Direction facing, uint16 areaNum, uint16 walkDataNum) {
+ // queen.c l.2838-2911
+ debug(9, "handleSpecialArea(%d, %d, %d)\n", facing, areaNum, walkDataNum);
+
+ // Stop animating Joe
+ _vm->graphics()->bob(0)->animating = false;
+
+ // Make Joe face the right direction
+ joeFacing(facing);
+ joeFace();
+
+ _newRoom = 0;
+ _entryObj = 0;
+
+ char nextCut[20];
+ memset(nextCut, 0, sizeof(nextCut));
+
+ switch (_currentRoom) {
+ case ROOM_JUNGLE_BRIDGE:
+ makeJoeSpeak(16);
+ break;
+ case ROOM_JUNGLE_GORILLA_1:
+ playCutaway("c6c.CUT", nextCut);
+ break;
+ case ROOM_JUNGLE_GORILLA_2:
+ playCutaway("c14b.CUT", nextCut);
+ break;
+ case ROOM_AMAZON_ENTRANCE:
+ if (areaNum == 3) {
+ playCutaway("c16a.CUT", nextCut);
+ }
+ break;
+ case ROOM_AMAZON_HIDEOUT:
+ if (walkDataNum == 4) {
+ playCutaway("c17a.CUT", nextCut);
+ } else if (walkDataNum == 2) {
+ playCutaway("c17b.CUT", nextCut);
+ }
+ break;
+ case ROOM_FLODA_OUTSIDE:
+ playCutaway("c22a.CUT", nextCut);
+ break;
+ case ROOM_FLODA_KITCHEN:
+ playCutaway("c26b.CUT", nextCut);
+ break;
+ case ROOM_FLODA_KLUNK:
+ playCutaway("c30a.CUT", nextCut);
+ break;
+ case ROOM_FLODA_HENRY:
+ playCutaway("c32c.CUT", nextCut);
+ break;
+ case ROOM_TEMPLE_ZOMBIES:
+ if (areaNum == 6) {
+ switch (gameState(VAR_BYPASS_ZOMBIES)) {
+ case 0:
+ playCutaway("c50d.CUT", nextCut);
+ while (nextCut[0] != '\0') {
+ playCutaway(nextCut, nextCut);
+ }
+ gameState(VAR_BYPASS_ZOMBIES, 1);
+ break;
+ case 1:
+ playCutaway("c50h.CUT", nextCut);
+ break;
+ }
+ }
+ break;
+ case ROOM_TEMPLE_SNAKE:
+ playCutaway("c53b.CUT", nextCut);
+ break;
+ case ROOM_TEMPLE_LIZARD_LASER:
+ makeJoeSpeak(19);
+ break;
+ case ROOM_HOTEL_DOWNSTAIRS:
+ makeJoeSpeak(21);
+ break;
+ case ROOM_HOTEL_LOBBY:
+ switch (gameState(VAR_HOTEL_ESCAPE_STATE)) {
+ case 0:
+ playCutaway("c73a.CUT");
+ joeUseUnderwear();
+ joeFace();
+ gameState(VAR_HOTEL_ESCAPE_STATE, 1);
+ break;
+ case 1:
+ playCutaway("c73b.CUT");
+ gameState(VAR_HOTEL_ESCAPE_STATE, 2);
+ break;
+ case 2:
+ playCutaway("c73c.CUT");
+ break;
+ }
+ break;
+ case ROOM_TEMPLE_MAZE_5:
+ if (areaNum == 7) {
+ makeJoeSpeak(17);
+ }
+ break;
+ case ROOM_TEMPLE_MAZE_6:
+ if (areaNum == 5 && gameState(187) == 0) {
+ playCutaway("c101b.CUT", nextCut);
+ }
+ break;
+ case ROOM_FLODA_FRONTDESK:
+ if (areaNum == 3) {
+ switch (gameState(VAR_BYPASS_FLODA_RECEPTIONIST)) {
+ case 0:
+ playCutaway("c103b.CUT", nextCut);
+ gameState(VAR_BYPASS_FLODA_RECEPTIONIST, 1);
+ break;
+ case 1:
+ playCutaway("c103e.CUT", nextCut);
+ break;
+ }
+ }
+ break;
+ }
+
+ while (strlen(nextCut) > 4 &&
+ scumm_stricmp(nextCut + strlen(nextCut) - 4, ".cut") == 0) {
+ playCutaway(nextCut, nextCut);
+ }
+}
+
+void Logic::handlePinnacleRoom() {
+ // camera does not follow Joe anymore
+ _vm->graphics()->putCameraOnBob(-1);
+ displayRoom(ROOM_JUNGLE_PINNACLE, RDM_NOFADE_JOE, 100, 2, true);
+
+ BobSlot *joe = _vm->graphics()->bob(6);
+ BobSlot *piton = _vm->graphics()->bob(7);
+
+ // set scrolling value to mouse position to avoid glitch
+ _vm->display()->horizontalScroll(_vm->input()->mousePosX());
+
+ joe->x = piton->x = 3 * _vm->input()->mousePosX() / 4 + 200;
+ joe->frameNum = _vm->input()->mousePosX() / 36 + 45;
+
+ // bobs have been unpacked from animating objects, we don't need them
+ // to animate anymore ; so turn animation off
+ joe->animating = piton->animating = false;
+
+ _vm->update();
+ _vm->display()->palFadeIn(ROOM_JUNGLE_PINNACLE, joe->active, joe->x, joe->y);
+
+ _entryObj = 0;
+ uint16 prevObj = 0;
+ CmdText cmdText((_vm->resource()->getLanguage() == HEBREW), 5, _vm);
+ cmdText.setVerb(VERB_WALK_TO);
+ while (_vm->input()->mouseButton() == 0 || _entryObj == 0) {
+
+ _vm->update();
+ int mx = _vm->input()->mousePosX();
+ int my = _vm->input()->mousePosY();
+
+ // update screen scrolling
+ _vm->display()->horizontalScroll(mx);
+
+ // update bobs position / frame
+ joe->x = piton->x = 3 * mx / 4 + 200;
+ joe->frameNum = mx / 36 + 45;
+
+ _vm->display()->clearTexts(5, 5);
+
+ uint16 curObj = _vm->grid()->findObjectUnderCursor(mx, my);
+ if (curObj != 0 && curObj != prevObj) {
+ _entryObj = 0;
+ curObj += currentRoomData(); // global object number
+ ObjectData *objData = objectData(curObj);
+ if (objData->name > 0) {
+ _entryObj = objData->entryObj;
+ cmdText.displayTemp(INK_PINNACLE_ROOM, objectName(objData->name), true);
+ }
+ prevObj = curObj;
+ }
+ }
+ _vm->input()->clearMouseButton();
+
+ _newRoom = objectData(_entryObj)->room;
+
+ // FIXME - only a few commands can be triggered from this room :
+ // piton -> crash : 0x216 (obj1=0x2a, song=3)
+ // piton -> floda : 0x217 (obj1=0x29, song=16)
+ // piton -> bob : 0x219 (obj1=0x2f, song=6)
+ // piton -> embark : 0x218 (obj1=0x2c, song=7)
+ // piton -> jungle : 0x20B (obj1=0x2b, song=3)
+ // piton -> amazon : 0x21A (obj1=0x30, song=3)
+ //
+ // Because none of these update objects/areas/gamestate, the EXECUTE_ACTION()
+ // call, as the original does, is useless. All we have to do is the playsong
+ // call (all songs have the PLAY_BEFORE type). This way we could get rid of
+ // the hack described in execute.c l.334-339.
+ struct {
+ uint16 obj;
+ int16 song;
+ } cmds[] = {
+ { 0x2A, 3 },
+ { 0x29, 16 },
+ { 0x2F, 6 },
+ { 0x2C, 7 },
+ { 0x2B, 3 },
+ { 0x30, 3 }
+ };
+ for (int i = 0; i < ARRAYSIZE(cmds); ++i) {
+ if (cmds[i].obj == prevObj) {
+ _vm->sound()->playSong(cmds[i].song);
+ break;
+ }
+ }
+
+ joe->active = piton->active = false;
+ _vm->display()->clearTexts(5, 5);
+
+ // camera follows Joe again
+ _vm->graphics()->putCameraOnBob(0);
+
+ _vm->display()->palFadeOut(ROOM_JUNGLE_PINNACLE);
+}
+
+void Logic::update() {
+ if (_credits)
+ _credits->update();
+
+ if (_vm->debugger()->flags() & Debugger::DF_DRAW_AREAS) {
+ _vm->grid()->drawZones();
+ }
+}
+
+void Logic::saveState(byte *&ptr) {
+ uint16 i;
+ for (i = 0; i < 4; i++) {
+ WRITE_BE_UINT16(ptr, _inventoryItem[i]); ptr += 2;
+ }
+
+ WRITE_BE_UINT16(ptr, _vm->graphics()->bob(0)->x); ptr += 2;
+ WRITE_BE_UINT16(ptr, _vm->graphics()->bob(0)->y); ptr += 2;
+
+ WRITE_BE_UINT16(ptr, _currentRoom); ptr += 2;
+
+ for (i = 1; i <= _numObjects; i++)
+ _objectData[i].writeToBE(ptr);
+
+ for (i = 1; i <= _numItems; i++)
+ _itemData[i].writeToBE(ptr);
+
+ for (i = 0; i < GAME_STATE_COUNT; i++) {
+ WRITE_BE_UINT16(ptr, _gameState[i]); ptr += 2;
+ }
+
+ for (i = 0; i < TALK_SELECTED_COUNT; i++)
+ _talkSelected[i].writeToBE(ptr);
+
+ for (i = 1; i <= _numWalkOffs; i++)
+ _walkOffData[i].writeToBE(ptr);
+
+ WRITE_BE_UINT16(ptr, _joe.facing); ptr += 2;
+
+ // V1
+ WRITE_BE_UINT16(ptr, _puzzleAttemptCount); ptr += 2;
+ for (i = 1; i <= _numObjDesc; i++)
+ _objectDescription[i].writeToBE(ptr);
+}
+
+void Logic::loadState(uint32 ver, byte *&ptr) {
+ uint16 i;
+ for (i = 0; i < 4; i++) {
+ _inventoryItem[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
+ }
+
+ _joe.x = (int16)READ_BE_INT16(ptr); ptr += 2;
+ _joe.y = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ _currentRoom = READ_BE_UINT16(ptr); ptr += 2;
+
+ for (i = 1; i <= _numObjects; i++)
+ _objectData[i].readFromBE(ptr);
+
+ for (i = 1; i <= _numItems; i++)
+ _itemData[i].readFromBE(ptr);
+
+ for (i = 0; i < GAME_STATE_COUNT; i++) {
+ _gameState[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
+ }
+
+ for (i = 0; i < TALK_SELECTED_COUNT; i++)
+ _talkSelected[i].readFromBE(ptr);
+
+ for (i = 1; i <= _numWalkOffs; i++)
+ _walkOffData[i].readFromBE(ptr);
+
+ _joe.facing = READ_BE_UINT16(ptr); ptr += 2;
+
+ if (ver >= 1) {
+ _puzzleAttemptCount = READ_BE_UINT16(ptr); ptr += 2;
+
+ for (i = 1; i <= _numObjDesc; i++)
+ _objectDescription[i].readFromBE(ptr);
+ }
+}
+
+void Logic::setupRestoredGame() {
+ _vm->sound()->playLastSong();
+
+ switch (gameState(VAR_JOE_DRESSING_MODE)) {
+ case 0:
+ _vm->display()->palSetJoeNormal();
+ loadJoeBanks("Joe_A.BBK", "Joe_B.BBK");
+ break;
+ case 1:
+ _vm->display()->palSetJoeNormal();
+ loadJoeBanks("JoeU_A.BBK", "JoeU_B.BBK");
+ break;
+ case 2:
+ _vm->display()->palSetJoeDress();
+ loadJoeBanks("JoeD_A.BBK", "JoeD_B.BBK");
+ break;
+ }
+
+ BobSlot *pbs = _vm->graphics()->bob(0);
+ pbs->xflip = (joeFacing() == DIR_LEFT);
+ joePrevFacing(joeFacing());
+ joeCutFacing(joeFacing());
+ switch (joeFacing()) {
+ case DIR_FRONT:
+ pbs->frameNum = 36;
+ _vm->bankMan()->unpack(3, 31, 7);
+ break;
+ case DIR_BACK:
+ pbs->frameNum = 37;
+ _vm->bankMan()->unpack(5, 31, 7);
+ break;
+ default:
+ pbs->frameNum = 35;
+ _vm->bankMan()->unpack(1, 31, 7);
+ break;
+ }
+
+ _oldRoom = 0;
+ _newRoom = _currentRoom;
+ _entryObj = 0;
+
+ if (_vm->bam()->_flag != BamScene::F_STOP) {
+ _vm->bam()->prepareAnimation();
+ }
+
+ inventoryRefresh();
+}
+
+void Logic::sceneStart() {
+ debug(6, "[Logic::sceneStart] _scene = %i", _scene);
+ _scene++;
+
+ _vm->display()->showMouseCursor(false);
+
+ if (1 == _scene) {
+ _vm->display()->palGreyPanel();
+ }
+
+ _vm->update();
+}
+
+void Logic::sceneStop() {
+ debug(6, "[Logic::sceneStop] _scene = %i", _scene);
+ _scene--;
+
+ if (_scene > 0)
+ return;
+
+ _vm->display()->palSetAllDirty();
+ _vm->display()->showMouseCursor(true);
+ _vm->grid()->setupPanel();
+}
+
+void Logic::changeRoom() {
+ if (!preChangeRoom())
+ displayRoom(currentRoom(), RDM_FADE_JOE, 100, 1, false);
+ _vm->display()->showMouseCursor(true);
+}
+
+void Logic::executeSpecialMove(uint16 sm) {
+ debug(6, "Special move: %d", sm);
+ if (!handleSpecialMove(sm))
+ warning("unhandled / invalid special move : %d", sm);
+}
+
+void Logic::asmMakeJoeUseDress() {
+ joeUseDress(false);
+}
+
+void Logic::asmMakeJoeUseNormalClothes() {
+ joeUseClothes(false);
+}
+
+void Logic::asmMakeJoeUseUnderwear() {
+ joeUseUnderwear();
+}
+
+void Logic::asmSwitchToDressPalette() {
+ _vm->display()->palSetJoeDress();
+}
+
+void Logic::asmSwitchToNormalPalette() {
+ _vm->display()->palSetJoeNormal();
+}
+
+void Logic::asmStartCarAnimation() {
+ _vm->bam()->_flag = BamScene::F_PLAY;
+ _vm->bam()->prepareAnimation();
+}
+
+void Logic::asmStopCarAnimation() {
+ _vm->bam()->_flag = BamScene::F_STOP;
+ _vm->graphics()->bob(findBob(594))->active = false; // oil object
+ _vm->graphics()->bob(7)->active = false; // gun shots
+}
+
+void Logic::asmStartFightAnimation() {
+ _vm->bam()->_flag = BamScene::F_PLAY;
+ _vm->bam()->prepareAnimation();
+ gameState(148, 1);
+}
+
+void Logic::asmWaitForFrankPosition() {
+ _vm->bam()->_flag = BamScene::F_REQ_STOP;
+ while (_vm->bam()->_flag != BamScene::F_STOP) {
+ _vm->update();
+ }
+}
+
+void Logic::asmMakeFrankGrowing() {
+ _vm->bankMan()->unpack(1, 38, 15);
+ BobSlot *bobFrank = _vm->graphics()->bob(5);
+ bobFrank->frameNum = 38;
+ bobFrank->curPos(160, 200);
+
+ int i;
+ for (i = 10; i <= 100; i += 4) {
+ bobFrank->scale = i;
+ _vm->update();
+ }
+ for (i = 0; i <= 20; ++i) {
+ _vm->update();
+ }
+
+ objectData(521)->name = ABS(objectData(521)->name); // Dinoray
+ objectData(526)->name = ABS(objectData(526)->name); // Frank obj
+ objectData(522)->name = -ABS(objectData(522)->name); // TMPD object off
+ objectData(525)->name = -ABS(objectData(525)->name); // Floda guards off
+ objectData(523)->name = -ABS(objectData(523)->name); // Sparky object off
+ gameState(157, 1); // No more Ironstein
+}
+
+void Logic::asmMakeRobotGrowing() {
+ _vm->bankMan()->unpack(1, 38, 15);
+ BobSlot *bobRobot = _vm->graphics()->bob(5);
+ bobRobot->frameNum = 38;
+ bobRobot->curPos(160, 200);
+
+ int i;
+ for (i = 10; i <= 100; i += 4) {
+ bobRobot->scale = i;
+ _vm->update();
+ }
+ for (i = 0; i <= 20; ++i) {
+ _vm->update();
+ }
+
+ objectData(524)->name = -ABS(objectData(524)->name); // Azura object off
+ objectData(526)->name = -ABS(objectData(526)->name); // Frank object off
+}
+
+void Logic::asmShrinkRobot() {
+ int i;
+ BobSlot *robot = _vm->graphics()->bob(6);
+ for (i = 100; i >= 35; i -= 5) {
+ robot->scale = i;
+ _vm->update();
+ }
+}
+
+void Logic::asmEndGame() {
+ int n = 40;
+ while (n--) {
+ _vm->update();
+ }
+ debug(0, "Game completed.");
+ _vm->quitGame();
+}
+
+void Logic::asmPutCameraOnDino() {
+ _vm->graphics()->putCameraOnBob(-1);
+ int16 scrollx = _vm->display()->horizontalScroll();
+ while (scrollx < 320) {
+ scrollx += 16;
+ if (scrollx > 320) {
+ scrollx = 320;
+ }
+ _vm->display()->horizontalScroll(scrollx);
+ _vm->update();
+ }
+ _vm->graphics()->putCameraOnBob(1);
+}
+
+void Logic::asmPutCameraOnJoe() {
+ _vm->graphics()->putCameraOnBob(0);
+}
+
+void Logic::asmAltIntroPanRight() {
+ _vm->graphics()->putCameraOnBob(-1);
+ _vm->input()->fastMode(true);
+ _vm->update();
+ int16 scrollx = _vm->display()->horizontalScroll();
+ while (scrollx < 285 && !_vm->input()->cutawayQuit()) {
+ ++scrollx;
+ if (scrollx > 285) {
+ scrollx = 285;
+ }
+ _vm->display()->horizontalScroll(scrollx);
+ _vm->update();
+ }
+ _vm->input()->fastMode(false);
+}
+
+void Logic::asmAltIntroPanLeft() {
+ _vm->graphics()->putCameraOnBob(-1);
+ _vm->input()->fastMode(true);
+ int16 scrollx = _vm->display()->horizontalScroll();
+ while (scrollx > 0 && !_vm->input()->cutawayQuit()) {
+ scrollx -= 4;
+ if (scrollx < 0) {
+ scrollx = 0;
+ }
+ _vm->display()->horizontalScroll(scrollx);
+ _vm->update();
+ }
+ _vm->input()->fastMode(false);
+}
+
+void Logic::asmSetAzuraInLove() {
+ gameState(VAR_AZURA_IN_LOVE, 1);
+}
+
+void Logic::asmPanRightFromJoe() {
+ _vm->graphics()->putCameraOnBob(-1);
+ int16 scrollx = _vm->display()->horizontalScroll();
+ while (scrollx < 320) {
+ scrollx += 16;
+ if (scrollx > 320) {
+ scrollx = 320;
+ }
+ _vm->display()->horizontalScroll(scrollx);
+ _vm->update();
+ }
+}
+
+void Logic::asmSetLightsOff() {
+ _vm->display()->palCustomLightsOff(currentRoom());
+}
+
+void Logic::asmSetLightsOn() {
+ _vm->display()->palCustomLightsOn(currentRoom());
+}
+
+void Logic::asmSetManequinAreaOn() {
+ Area *a = _vm->grid()->area(ROOM_FLODA_FRONTDESK, 7);
+ a->mapNeighbours = ABS(a->mapNeighbours);
+}
+
+void Logic::asmPanToJoe() {
+ int i = _vm->graphics()->bob(0)->x - 160;
+ if (i < 0) {
+ i = 0;
+ } else if (i > 320) {
+ i = 320;
+ }
+ _vm->graphics()->putCameraOnBob(-1);
+ int16 scrollx = _vm->display()->horizontalScroll();
+ if (i < scrollx) {
+ while (scrollx > i) {
+ scrollx -= 16;
+ if (scrollx < i) {
+ scrollx = i;
+ }
+ _vm->display()->horizontalScroll(scrollx);
+ _vm->update();
+ }
+ } else {
+ while (scrollx < i) {
+ scrollx += 16;
+ if (scrollx > i) {
+ scrollx = i;
+ }
+ _vm->display()->horizontalScroll(scrollx);
+ _vm->update();
+ }
+ _vm->update();
+ }
+ _vm->graphics()->putCameraOnBob(0);
+}
+
+void Logic::asmTurnGuardOn() {
+ gameState(VAR_GUARDS_TURNED_ON, 1);
+}
+
+void Logic::asmPanLeft320To144() {
+ _vm->graphics()->putCameraOnBob(-1);
+ int16 scrollx = _vm->display()->horizontalScroll();
+ while (scrollx > 144) {
+ scrollx -= 8;
+ if (scrollx < 144) {
+ scrollx = 144;
+ }
+ _vm->display()->horizontalScroll(scrollx);
+ _vm->update();
+ }
+}
+
+void Logic::asmSmooch() {
+ _vm->graphics()->putCameraOnBob(-1);
+ BobSlot *bobAzura = _vm->graphics()->bob(5);
+ BobSlot *bobJoe = _vm->graphics()->bob(6);
+ int16 scrollx = _vm->display()->horizontalScroll();
+ while (scrollx < 320) {
+ scrollx += 8;
+ _vm->display()->horizontalScroll(scrollx);
+ if (bobJoe->x - bobAzura->x > 128) {
+ bobAzura->x += 10;
+ bobJoe->x += 6;
+ } else {
+ bobAzura->x += 8;
+ bobJoe->x += 8;
+ }
+ _vm->update();
+ }
+}
+
+void Logic::asmMakeLightningHitPlane() {
+ _vm->graphics()->putCameraOnBob(-1);
+ short iy = 0, x, ydir = -1, j, k;
+
+ BobSlot *planeBob = _vm->graphics()->bob(5);
+ BobSlot *lightningBob = _vm->graphics()->bob(20);
+
+ planeBob->y = 135;
+
+ planeBob->scale = 20;
+
+ for (x = 660; x > 163; x -= 6) {
+ planeBob->x = x;
+ planeBob->y = 135 + iy;
+
+ iy -= ydir;
+ if (iy < -9 || iy > 9)
+ ydir = -ydir;
+
+ planeBob->scale++;
+ if (planeBob->scale > 100)
+ planeBob->scale = 100;
+
+ int scrollX = x - 163;
+ if (scrollX > 320)
+ scrollX = 320;
+ _vm->display()->horizontalScroll(scrollX);
+ _vm->update();
+ }
+
+ planeBob->scale = 100;
+ _vm->display()->horizontalScroll(0);
+
+ planeBob->x += 8;
+ planeBob->y += 6;
+
+ lightningBob->x = 160;
+ lightningBob->y = 0;
+
+ _vm->sound()->playSfx(currentRoomSfx(), false);
+
+ _vm->bankMan()->unpack(18, lightningBob->frameNum, 15);
+ _vm->bankMan()->unpack(4, planeBob ->frameNum, 15);
+
+ // Plane plunges into the jungle!
+ BobSlot *fireBob = _vm->graphics()->bob(6);
+
+ fireBob->animating = true;
+ fireBob->x = planeBob->x;
+ fireBob->y = planeBob->y + 10;
+
+ _vm->bankMan()->unpack(19, fireBob->frameNum, 15);
+ _vm->update();
+
+ k = 20;
+ j = 1;
+
+ for (x = 163; x > -30; x -= 10) {
+ planeBob->y += 4;
+ fireBob->y += 4;
+ planeBob->x = fireBob->x = x;
+
+ if (k < 40) {
+ _vm->bankMan()->unpack(j, planeBob->frameNum, 15);
+ _vm->bankMan()->unpack(k, fireBob ->frameNum, 15);
+ k++;
+ j++;
+
+ if (j == 4)
+ j = 1;
+ }
+
+ _vm->update();
+ }
+
+ _vm->graphics()->putCameraOnBob(0);
+}
+
+void Logic::asmScaleBlimp() {
+ int16 z = 256;
+ BobSlot *bob = _vm->graphics()->bob(7);
+ int16 x = bob->x;
+ int16 y = bob->y;
+ while (bob->x > 150) {
+ bob->x = x * 256 / z + 150;
+ bob->y = y * 256 / z + 112;
+ bob->scale = 100 * 256 / z;
+
+ ++z;
+ if (z % 6 == 0) {
+ --x;
+ }
+
+ _vm->update();
+ }
+}
+
+void Logic::asmScaleEnding() {
+ _vm->graphics()->bob(7)->active = false; // Turn off blimp
+ BobSlot *b = _vm->graphics()->bob(20);
+ b->x = 160;
+ b->y = 100;
+ int i;
+ for (i = 5; i <= 100; i += 5) {
+ b->scale = i;
+ _vm->update();
+ }
+ for (i = 0; i < 50; ++i) {
+ _vm->update();
+ }
+ _vm->display()->palFadeOut(_currentRoom);
+}
+
+void Logic::asmWaitForCarPosition() {
+ // Wait for car to reach correct position before pouring oil
+ while (_vm->bam()->_index != 60) {
+ _vm->update();
+ }
+}
+
+void Logic::asmShakeScreen() {
+ _vm->display()->shake(false);
+ _vm->update();
+ _vm->display()->shake(true);
+ _vm->update();
+}
+
+void Logic::asmAttemptPuzzle() {
+ ++_puzzleAttemptCount;
+ if (_puzzleAttemptCount == 4) {
+ makeJoeSpeak(226, true);
+ _puzzleAttemptCount = 0;
+ }
+}
+
+void Logic::asmScaleTitle() {
+ BobSlot *bob = _vm->graphics()->bob(5);
+ bob->animating = false;
+ bob->x = 161;
+ bob->y = 200;
+ bob->scale = 100;
+
+ int i;
+ for (i = 5; i <= 100; i +=5) {
+ bob->scale = i;
+ bob->y -= 4;
+ _vm->update();
+ }
+}
+
+void Logic::asmPanRightToHugh() {
+ BobSlot *bob_thugA1 = _vm->graphics()->bob(20);
+ BobSlot *bob_thugA2 = _vm->graphics()->bob(21);
+ BobSlot *bob_thugA3 = _vm->graphics()->bob(22);
+ BobSlot *bob_hugh1 = _vm->graphics()->bob(1);
+ BobSlot *bob_hugh2 = _vm->graphics()->bob(23);
+ BobSlot *bob_hugh3 = _vm->graphics()->bob(24);
+ BobSlot *bob_thugB1 = _vm->graphics()->bob(25);
+ BobSlot *bob_thugB2 = _vm->graphics()->bob(26);
+
+ _vm->graphics()->putCameraOnBob(-1);
+ _vm->input()->fastMode(true);
+ _vm->update();
+
+ // Adjust thug1 gun so it matches rest of body
+ bob_thugA1->x += 160 - 45;
+ bob_thugA2->x += 160;
+ bob_thugA3->x += 160;
+
+ bob_hugh1->x += 160 * 2;
+ bob_hugh2->x += 160 * 2;
+ bob_hugh3->x += 160 * 2;
+
+ bob_thugB1->x += 160 * 3;
+ bob_thugB2->x += 160 * 3;
+
+ int horizontalScroll = 0;
+ while (horizontalScroll < 160 && !_vm->input()->cutawayQuit()) {
+
+ horizontalScroll += 8;
+ if (horizontalScroll > 160)
+ horizontalScroll = 160;
+
+ _vm->display()->horizontalScroll(horizontalScroll);
+
+ bob_thugA1->x -= 16;
+ bob_thugA2->x -= 16;
+ bob_thugA3->x -= 16;
+
+ bob_hugh1->x -= 24;
+ bob_hugh2->x -= 24;
+ bob_hugh3->x -= 24;
+
+ bob_thugB1->x -= 32;
+ bob_thugB2->x -= 32;
+
+ _vm->update();
+ }
+
+ _vm->input()->fastMode(false);
+}
+
+void Logic::asmMakeWhiteFlash() {
+ _vm->display()->palCustomFlash();
+}
+
+void Logic::asmPanRightToJoeAndRita() { // cdint.cut
+ BobSlot *bob_box = _vm->graphics()->bob(20);
+ BobSlot *bob_beam = _vm->graphics()->bob(21);
+ BobSlot *bob_crate = _vm->graphics()->bob(22);
+ BobSlot *bob_clock = _vm->graphics()->bob(23);
+ BobSlot *bob_hands = _vm->graphics()->bob(24);
+
+ _vm->graphics()->putCameraOnBob(-1);
+ _vm->input()->fastMode(true);
+
+ _vm->update();
+
+ bob_box ->x += 280 * 2;
+ bob_beam ->x += 30;
+ bob_crate->x += 180 * 3;
+
+ int horizontalScroll = _vm->display()->horizontalScroll();
+
+ while (horizontalScroll < 290 && !_vm->input()->cutawayQuit()) {
+
+ ++horizontalScroll;
+ if (horizontalScroll > 290)
+ horizontalScroll = 290;
+
+ _vm->display()->horizontalScroll(horizontalScroll);
+
+ bob_box ->x -= 2;
+ bob_beam ->x -= 1;
+ bob_crate->x -= 3;
+ bob_clock->x -= 2;
+ bob_hands->x -= 2;
+
+ _vm->update();
+ }
+ _vm->input()->fastMode(false);
+}
+
+void Logic::asmPanLeftToBomb() {
+ BobSlot *bob21 = _vm->graphics()->bob(21);
+ BobSlot *bob22 = _vm->graphics()->bob(22);
+
+ _vm->graphics()->putCameraOnBob(-1);
+ _vm->input()->fastMode(true);
+
+ int horizontalScroll = _vm->display()->horizontalScroll();
+
+ while ((horizontalScroll > 0 || bob21->x < 136) && !_vm->input()->cutawayQuit()) {
+
+ horizontalScroll -= 5;
+ if (horizontalScroll < 0)
+ horizontalScroll = 0;
+
+ _vm->display()->horizontalScroll(horizontalScroll);
+
+ if (horizontalScroll < 272 && bob21->x < 136)
+ bob21->x += 2;
+
+ bob22->x += 5;
+
+ _vm->update();
+ }
+
+ _vm->input()->fastMode(false);
+}
+
+void Logic::asmEndDemo() {
+ debug(0, "Flight of the Amazon Queen, released January 95.");
+ _vm->quitGame();
+}
+
+void Logic::asmInterviewIntro() {
+ // put camera on airship
+ _vm->graphics()->putCameraOnBob(5);
+ BobSlot *bas = _vm->graphics()->bob(5);
+
+ bas->curPos(-30, 40);
+
+ bas->move(700, 10, 3);
+ int scale = 450;
+ while (bas->moving && !_vm->input()->cutawayQuit()) {
+ bas->scale = 256 * 100 / scale;
+ --scale;
+ if (scale < 256) {
+ scale = 256;
+ }
+ _vm->update();
+ }
+
+ bas->scale = 90;
+ bas->xflip = true;
+
+ bas->move(560, 25, 4);
+ while (bas->moving && !_vm->input()->cutawayQuit()) {
+ _vm->update();
+ }
+
+ bas->move(545, 65, 2);
+ while (bas->moving && !_vm->input()->cutawayQuit()) {
+ _vm->update();
+ }
+
+ bas->move(540, 75, 2);
+ while (bas->moving && !_vm->input()->cutawayQuit()) {
+ _vm->update();
+ }
+
+ // put camera on Joe
+ _vm->graphics()->putCameraOnBob(0);
+}
+
+void Logic::asmEndInterview() {
+ debug(0, "Interactive Interview copyright (c) 1995, IBI.");
+ _vm->quitGame();
+}
+
+void Logic::startCredits(const char *filename) {
+ stopCredits();
+ _credits = new Credits(_vm, filename);
+}
+
+void Logic::stopCredits() {
+ if (_credits) {
+ _vm->display()->clearTexts(0, 199);
+ delete _credits;
+ _credits = NULL;
+ }
+}
+
+void LogicDemo::useJournal() {
+ makePersonSpeak("This is a demo, so I can't load or save games*14", NULL, "");
+}
+
+bool LogicDemo::preChangeRoom() {
+ if (currentRoom() == FOTAQ_LOGO && gameState(VAR_INTRO_PLAYED) == 0) {
+ currentRoom(79);
+ displayRoom(currentRoom(), RDM_FADE_NOJOE, 100, 2, true);
+ playCutaway("clogo.cut");
+ sceneReset();
+ currentRoom(ROOM_HOTEL_LOBBY);
+ entryObj(584);
+ displayRoom(currentRoom(), RDM_FADE_JOE, 100, 2, true);
+ playCutaway("c70d.cut");
+ gameState(VAR_INTRO_PLAYED, 1);
+ inventoryRefresh();
+ return true;
+ }
+ return false;
+}
+
+bool LogicDemo::handleSpecialMove(uint16 sm) {
+ switch (sm) {
+ case 4:
+ asmMakeJoeUseUnderwear();
+ break;
+ case 5:
+ asmSwitchToDressPalette();
+ break;
+ case 14:
+ asmEndDemo();
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void LogicInterview::useJournal() {
+ // no-op
+}
+
+bool LogicInterview::preChangeRoom() {
+ if (currentRoom() == 2 && gameState(2) == 0) {
+ currentRoom(6);
+ displayRoom(currentRoom(), RDM_FADE_NOJOE, 100, 2, true);
+ playCutaway("start.cut");
+ gameState(2, 1);
+ inventoryRefresh();
+ return true;
+ }
+ return false;
+}
+
+bool LogicInterview::handleSpecialMove(uint16 sm) {
+ switch (sm) {
+ case 1:
+ asmInterviewIntro();
+ break;
+ case 2:
+ asmEndInterview();
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void LogicGame::useJournal() {
+ _vm->command()->clear(false);
+ _journal->use();
+ _vm->walk()->stopJoe();
+}
+
+bool LogicGame::preChangeRoom() {
+ if (currentRoom() == ROOM_JUNGLE_PINNACLE) {
+ handlePinnacleRoom();
+ return true;
+ } else if (currentRoom() == FOTAQ_LOGO && gameState(VAR_INTRO_PLAYED) == 0) {
+ displayRoom(currentRoom(), RDM_FADE_NOJOE, 100, 2, true);
+ playCutaway("copy.cut");
+ playCutaway("clogo.cut");
+
+ if (ConfMan.getBool("alt_intro") && _vm->resource()->isCD()) {
+ playCutaway("cintr.cut");
+ } else {
+ playCutaway("cdint.cut");
+ }
+
+ playCutaway("cred.cut");
+ _vm->display()->palSetPanel();
+ sceneReset();
+ currentRoom(ROOM_HOTEL_LOBBY);
+ entryObj(584);
+ displayRoom(currentRoom(), RDM_FADE_JOE, 100, 2, true);
+ playCutaway("c70d.cut");
+ gameState(VAR_INTRO_PLAYED, 1);
+ inventoryRefresh();
+ return true;
+ }
+ return false;
+}
+
+bool LogicGame::handleSpecialMove(uint16 sm) {
+ typedef void (LogicGame::*SpecialMoveProc)();
+ static const SpecialMoveProc asmTable[] = {
+ /* 00 */
+ 0,
+ 0,
+ &LogicGame::asmMakeJoeUseDress,
+ &LogicGame::asmMakeJoeUseNormalClothes,
+ /* 04 */
+ &LogicGame::asmMakeJoeUseUnderwear,
+ &LogicGame::asmSwitchToDressPalette,
+ &LogicGame::asmSwitchToNormalPalette,
+ &LogicGame::asmStartCarAnimation, // room 74
+ /* 08 */
+ &LogicGame::asmStopCarAnimation, // room 74
+ &LogicGame::asmStartFightAnimation, // room 69
+ &LogicGame::asmWaitForFrankPosition, // c69e.cut
+ &LogicGame::asmMakeFrankGrowing, // c69z.cut
+ /* 12 */
+ &LogicGame::asmMakeRobotGrowing, // c69z.cut
+ &LogicGame::asmShrinkRobot,
+ &LogicGame::asmEndGame,
+ &LogicGame::asmPutCameraOnDino,
+ /* 16 */
+ &LogicGame::asmPutCameraOnJoe,
+ &LogicGame::asmAltIntroPanRight, // cintr.cut
+ &LogicGame::asmAltIntroPanLeft, // cintr.cut
+ &LogicGame::asmSetAzuraInLove,
+ /* 20 */
+ &LogicGame::asmPanRightFromJoe,
+ &LogicGame::asmSetLightsOff,
+ &LogicGame::asmSetLightsOn,
+ &LogicGame::asmSetManequinAreaOn,
+ /* 24 */
+ &LogicGame::asmPanToJoe,
+ &LogicGame::asmTurnGuardOn,
+ &LogicGame::asmPanLeft320To144,
+ &LogicGame::asmSmooch,
+ /* 28 */
+ &LogicGame::asmMakeLightningHitPlane,
+ &LogicGame::asmScaleBlimp,
+ &LogicGame::asmScaleEnding,
+ &LogicGame::asmWaitForCarPosition,
+ /* 32 */
+ &LogicGame::asmShakeScreen,
+ &LogicGame::asmAttemptPuzzle,
+ &LogicGame::asmScaleTitle,
+ 0,
+ /* 36 */
+ &LogicGame::asmPanRightToHugh,
+ &LogicGame::asmMakeWhiteFlash,
+ &LogicGame::asmPanRightToJoeAndRita,
+ &LogicGame::asmPanLeftToBomb // cdint.cut
+ };
+ if (sm >= ARRAYSIZE(asmTable) || asmTable[sm] == 0)
+ return false;
+ (this->*asmTable[sm])();
+ return true;
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/logic.h b/engines/queen/logic.h
new file mode 100644
index 0000000000..f1f93de17e
--- /dev/null
+++ b/engines/queen/logic.h
@@ -0,0 +1,410 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENLOGIC_H
+#define QUEENLOGIC_H
+
+#include "common/str.h"
+#include "common/util.h"
+#include "queen/structs.h"
+
+namespace Queen {
+
+enum RoomDisplayMode {
+ RDM_FADE_NOJOE = 0, // fade in, hide Joe
+ RDM_FADE_JOE = 1, // fade in, display Joe
+ RDM_NOFADE_JOE = 2, // screen does not dissolve into view
+ RDM_FADE_JOE_XY = 3 // display Joe at the current X, Y coords
+};
+
+enum JoeWalkMode {
+ JWM_NORMAL = 0,
+ JWM_MOVE = 1,
+ JWM_EXECUTE = 2,
+ JWM_SPEAK = 3
+};
+
+class Credits;
+class Journal;
+class LineReader;
+class QueenEngine;
+
+class Logic {
+
+public:
+ Logic(QueenEngine *vm);
+ virtual ~Logic();
+
+ uint16 currentRoom() const { return _currentRoom; }
+ void currentRoom(uint16 room) {
+ assert(room >= 1 && room <= _numRooms);
+ _currentRoom = room;
+ }
+
+ uint16 oldRoom() const { return _oldRoom; }
+ void oldRoom(uint16 room) {
+ assert(room <= _numRooms);
+ _oldRoom = room;
+ }
+
+ uint16 newRoom() const { return _newRoom; }
+ void newRoom(uint16 room) {
+ assert(room <= _numRooms);
+ _newRoom = room;
+ }
+
+ ObjectData *objectData(int index) const;
+ uint16 roomData(int room) const { return _roomData[room]; }
+ GraphicData *graphicData(int index) const { return &_graphicData[index]; }
+ ItemData *itemData(int index) const { return &_itemData[index]; }
+ uint16 itemDataCount() const { return _numItems; }
+
+ uint16 findBob(uint16 obj) const;
+ uint16 findFrame(uint16 obj) const;
+ uint16 objectForPerson(uint16 bobnum) const;
+ WalkOffData *walkOffPointForObject(int16 obj) const;
+
+ uint16 walkOffCount() const { return _numWalkOffs; }
+ WalkOffData *walkOffData(int index) const { return &_walkOffData[index]; }
+ uint16 currentRoomData() const { return _roomData[_currentRoom]; }
+ GraphicAnim *graphicAnim(int index) const { return &_graphicAnim[index]; }
+ uint16 graphicAnimCount() const { return _numGraphicAnim; }
+ ObjectDescription *objectDescription(uint16 objNum) const { return &_objectDescription[objNum]; }
+ uint16 objectDescriptionCount() const { return _numObjDesc; }
+ uint16 currentRoomSfx() const { return _sfxName[_currentRoom]; }
+
+ uint16 joeFacing() const { return _joe.facing; }
+ uint16 joeX() const { return _joe.x; }
+ uint16 joeY() const { return _joe.y; }
+ JoeWalkMode joeWalk() const { return _joe.walk; }
+ uint16 joeScale() const { return _joe.scale; }
+ uint16 joeCutFacing() const { return _joe.cutFacing; }
+ uint16 joePrevFacing() const { return _joe.prevFacing; }
+
+ void joeFacing(uint16 dir) { _joe.facing = dir; }
+ void joePos(uint16 x, uint16 y) { _joe.x = x; _joe.y = y; }
+ void joeWalk(JoeWalkMode walking);
+ void joeScale(uint16 scale) { _joe.scale = scale; }
+ void joeCutFacing(uint16 dir) { _joe.cutFacing = dir; }
+ void joePrevFacing(uint16 dir) { _joe.prevFacing = dir; }
+
+ int16 gameState(int index) const;
+ void gameState(int index, int16 newValue);
+
+ TalkSelected *talkSelected(int index) { return &_talkSelected[index]; }
+
+ const char *roomName(uint16 roomNum) const;
+ const char *objectName(uint16 objNum) const;
+ const char *objectTextualDescription(uint16 objNum) const;
+ const char *joeResponse(int i) const;
+ const char *verbName(Verb v) const;
+
+ void eraseRoom();
+ void setupRoom(const char *room, int comPanel, bool inCutaway);
+ void displayRoom(uint16 room, RoomDisplayMode mode, uint16 joeScale, int comPanel, bool inCutaway);
+
+ int16 entryObj() const { return _entryObj; }
+ void entryObj(int16 obj) { _entryObj = obj; }
+
+ ActorData *findActor(uint16 noun, const char *name = NULL) const;
+ bool initPerson(uint16 noun, const char *actorName, bool loadBank, Person *pp);
+ uint16 findPersonNumber(uint16 obj, uint16 room) const;
+
+ //! load banks used for Joe animation
+ void loadJoeBanks(const char *animBank, const char *standBank);
+
+ //! load the various bobs needed to animate Joe
+ void setupJoe();
+
+ //! setup Joe at the right place when entering a room
+ void setupJoeInRoom(bool autoPosition, uint16 scale);
+
+ uint16 joeFace();
+ void joeGrab(int16 grabState);
+
+ //! change Joe clothes to dress
+ void joeUseDress(bool showCut);
+
+ //! restore Joe clothes
+ void joeUseClothes(bool showCut);
+
+ //! change Joe clothes to underwear
+ void joeUseUnderwear();
+
+ void makeJoeSpeak(uint16 descNum, bool objectType = false);
+ void makePersonSpeak(const char *sentence, Person *person, const char *voiceFilePrefix);
+
+ //! start the specified dialogue
+ void startDialogue(const char *dlgFile, int personInRoom, char *cutaway);
+
+ //! play the specified cutaway
+ void playCutaway(const char *cutFile, char *next = NULL);
+
+ //! initialize the inventory
+ void inventorySetup();
+
+ //! get the inventory item for the specified inventory slot
+ uint16 findInventoryItem(int invSlot) const;
+
+ //! refresh inventory contents
+ void inventoryRefresh();
+ int16 previousInventoryItem(int16 first) const;
+ int16 nextInventoryItem(int16 first) const;
+ void removeDuplicateItems();
+ uint16 numItemsInventory() const;
+ void inventoryInsertItem(uint16 itemNum, bool refresh = true);
+ void inventoryDeleteItem(uint16 itemNum, bool refresh = true);
+ void inventoryScroll(uint16 count, bool up);
+ void removeHotelItemsFromInventory();
+
+ //! copy data from dummy object to object
+ void objectCopy(int dummyObjectIndex, int objectIndex);
+
+ //! handle a particular event when Joe walks on this area
+ void handleSpecialArea(Direction facing, uint16 areaNum, uint16 walkDataNum);
+
+ //! handle the pinnacle room (== room chooser in the jungle)
+ void handlePinnacleRoom();
+
+ void update();
+
+ void saveState(byte *&ptr);
+ void loadState(uint32 ver, byte *&ptr);
+
+ //! called after a save state has been loaded
+ void setupRestoredGame();
+
+ //! ugly hack from original code
+ void sceneReset() { _scene = 0; }
+
+ //! make a scene
+ void sceneStart();
+
+ //! stop making a scene
+ void sceneStop();
+
+ void changeRoom();
+
+ //! enter the Journal (save/load, configuration)
+ virtual void useJournal() = 0;
+
+ //! execute a special move
+ void executeSpecialMove(uint16 sm);
+
+ void startCredits(const char *filename);
+ void stopCredits();
+
+ void start();
+
+ enum {
+ JOE_RESPONSE_MAX = 40,
+ DEFAULT_TALK_SPEED = 7 * 3,
+ GAME_STATE_COUNT = 211,
+ TALK_SELECTED_COUNT = 86
+ };
+
+protected:
+
+ void initialise();
+
+ void asmMakeJoeUseDress();
+ void asmMakeJoeUseNormalClothes();
+ void asmMakeJoeUseUnderwear();
+ void asmSwitchToDressPalette();
+ void asmSwitchToNormalPalette();
+ void asmStartCarAnimation();
+ void asmStopCarAnimation();
+ void asmStartFightAnimation();
+ void asmWaitForFrankPosition();
+ void asmMakeFrankGrowing();
+ void asmMakeRobotGrowing();
+ void asmShrinkRobot();
+ void asmEndGame();
+ void asmPutCameraOnDino();
+ void asmPutCameraOnJoe();
+ void asmAltIntroPanRight();
+ void asmAltIntroPanLeft();
+ void asmSetAzuraInLove();
+ void asmPanRightFromJoe();
+ void asmSetLightsOff();
+ void asmSetLightsOn();
+ void asmSetManequinAreaOn();
+ void asmPanToJoe();
+ void asmTurnGuardOn();
+ void asmPanLeft320To144();
+ void asmSmooch();
+ void asmMakeLightningHitPlane();
+ void asmScaleBlimp();
+ void asmScaleEnding();
+ void asmWaitForCarPosition();
+ void asmShakeScreen();
+ void asmAttemptPuzzle();
+ void asmScaleTitle();
+ void asmPanRightToHugh();
+ void asmMakeWhiteFlash();
+ void asmPanRightToJoeAndRita();
+ void asmPanLeftToBomb();
+ void asmEndDemo();
+ void asmInterviewIntro();
+ void asmEndInterview();
+
+ virtual bool preChangeRoom() = 0;
+ virtual bool handleSpecialMove(uint16 sm) = 0;
+
+
+ uint16 _currentRoom;
+ uint16 _oldRoom;
+ uint16 _newRoom;
+
+ //! total number of room in game
+ uint16 _numRooms;
+
+ //! first object number in room
+ uint16 *_roomData;
+
+ //! background music to play in room
+ uint16 *_sfxName;
+
+ //! bounding box of object
+ Box *_objectBox;
+
+ //! inventory items
+ ItemData *_itemData;
+ uint16 _numItems;
+
+ GraphicData *_graphicData;
+ uint16 _numGraphics;
+
+ ObjectData *_objectData;
+ uint16 _numObjects;
+
+ ObjectDescription *_objectDescription;
+ uint16 _numObjDesc;
+
+ ActorData *_actorData;
+ uint16 _numActors;
+
+ //! walk off point for an object
+ WalkOffData *_walkOffData;
+ uint16 _numWalkOffs;
+
+ FurnitureData *_furnitureData;
+ uint16 _numFurniture;
+
+ GraphicAnim *_graphicAnim;
+ uint16 _numGraphicAnim;
+
+ //! actor initial position in room is _walkOffData[_entryObj]
+ int16 _entryObj;
+
+ //! object description (Look At)
+ Common::StringList _objDescription;
+ uint16 _numDescriptions;
+
+ Common::StringList _objName;
+ uint16 _numNames;
+
+ //! room name, prefix for data files (PCX, LUM...)
+ Common::StringList _roomName;
+
+ Common::StringList _verbName;
+
+ Common::StringList _joeResponse;
+
+ //! actor animation strings
+ Common::StringList _aAnim;
+ uint16 _numAAnim;
+
+ //! actor names
+ Common::StringList _aName;
+ uint16 _numAName;
+
+ //! actor filenames
+ Common::StringList _aFile;
+ uint16 _numAFile;
+
+ struct {
+ uint16 x, y;
+ uint16 facing, cutFacing, prevFacing;
+ JoeWalkMode walk;
+ uint16 scale;
+ } _joe;
+
+ int16 _gameState[GAME_STATE_COUNT];
+
+ TalkSelected _talkSelected[TALK_SELECTED_COUNT];
+
+ //! inventory items
+ int16 _inventoryItem[4];
+
+ //! puzzle counter for room T7
+ uint8 _puzzleAttemptCount;
+
+ //! cutscene counter
+ int _scene;
+
+ Credits *_credits;
+ Journal *_journal;
+
+ QueenEngine *_vm;
+};
+
+class LogicDemo : public Logic {
+public:
+
+ LogicDemo(QueenEngine *vm) : Logic(vm) {}
+ void useJournal();
+
+protected:
+
+ bool preChangeRoom();
+ bool handleSpecialMove(uint16 sm);
+};
+
+class LogicInterview : public Logic {
+public:
+
+ LogicInterview(QueenEngine *vm) : Logic(vm) {}
+ void useJournal();
+
+protected:
+
+ bool preChangeRoom();
+ bool handleSpecialMove(uint16 sm);
+};
+
+class LogicGame : public Logic {
+public:
+
+ LogicGame(QueenEngine *vm) : Logic(vm) {}
+ void useJournal();
+
+protected:
+
+ bool preChangeRoom();
+ bool handleSpecialMove(uint16 sm);
+};
+
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/module.mk b/engines/queen/module.mk
new file mode 100644
index 0000000000..be3c5707e6
--- /dev/null
+++ b/engines/queen/module.mk
@@ -0,0 +1,34 @@
+MODULE := engines/queen
+
+MODULE_OBJS := \
+ engines/queen/bankman.o \
+ engines/queen/command.o \
+ engines/queen/credits.o \
+ engines/queen/cutaway.o \
+ engines/queen/debug.o \
+ engines/queen/display.o \
+ engines/queen/graphics.o \
+ engines/queen/grid.o \
+ engines/queen/input.o \
+ engines/queen/journal.o \
+ engines/queen/logic.o \
+ engines/queen/music.o \
+ engines/queen/musicdata.o \
+ engines/queen/queen.o \
+ engines/queen/resource.o \
+ engines/queen/restables.o \
+ engines/queen/sound.o \
+ engines/queen/state.o \
+ engines/queen/talk.o \
+ engines/queen/walk.o
+
+MODULE_DIRS += \
+ engines/queen
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp
new file mode 100644
index 0000000000..c4a7a24503
--- /dev/null
+++ b/engines/queen/music.cpp
@@ -0,0 +1,344 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/music.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+
+#include "sound/midiparser.h"
+
+namespace Queen {
+
+MusicPlayer::MusicPlayer(MidiDriver *driver, byte *data, uint32 size) : _driver(driver), _isPlaying(false), _looping(false), _randomLoop(false), _masterVolume(192), _queuePos(0), _musicData(data), _musicDataSize(size), _passThrough(false), _buf(0) {
+ memset(_channel, 0, sizeof(_channel));
+ queueClear();
+ _lastSong = _currentSong = 0;
+ _parser = MidiParser::createParser_SMF();
+ _parser->setMidiDriver(this);
+ _parser->setTimerRate(_driver->getBaseTempo());
+
+ _numSongs = READ_LE_UINT16(_musicData);
+ this->open();
+}
+
+MusicPlayer::~MusicPlayer() {
+ _parser->unloadMusic();
+ delete _parser;
+ this->close();
+ delete[] _buf;
+}
+
+void MusicPlayer::setVolume(int volume) {
+ if (volume < 0)
+ volume = 0;
+ else if (volume > 255)
+ volume = 255;
+
+ if (_masterVolume == volume)
+ return;
+
+ _masterVolume = volume;
+
+ for (int i = 0; i < 16; ++i) {
+ if (_channel[i])
+ _channel[i]->volume(_channelVolume[i] * _masterVolume / 255);
+ }
+}
+
+bool MusicPlayer::queueSong(uint16 songNum) {
+ if (songNum >= _numSongs && songNum < 1000) {
+ // this happens at the end of the car chase, where we try to play song 176,
+ // see Sound::_tune[], entry 39
+ debug(3, "Trying to queue an invalid song number %d, max %d", songNum, _numSongs);
+ return false;
+ }
+ uint8 emptySlots = 0;
+ for (int i = 0; i < MUSIC_QUEUE_SIZE; i++)
+ if (!_songQueue[i])
+ emptySlots++;
+
+ if (!emptySlots)
+ return false;
+
+ // Work around bug in Roland music, note that these numbers are 'one-off'
+ // from the original code
+ if (/*isRoland && */ songNum == 88 || songNum == 89)
+ songNum = 62;
+
+ _songQueue[MUSIC_QUEUE_SIZE - emptySlots] = songNum;
+ return true;
+}
+
+void MusicPlayer::queueClear() {
+ _lastSong = _songQueue[0];
+ _queuePos = 0;
+ _looping = _randomLoop = false;
+ memset(_songQueue, 0, sizeof(_songQueue));
+}
+
+int MusicPlayer::open() {
+ // Don't ever call open without first setting the output driver!
+ if (!_driver)
+ return 255;
+
+ int ret = _driver->open();
+ if (ret)
+ return ret;
+ _driver->setTimerCallback(this, &onTimer);
+ return 0;
+}
+
+void MusicPlayer::close() {
+ _driver->setTimerCallback(NULL, NULL);
+ if (_driver)
+ _driver->close();
+ _driver = 0;
+}
+
+void MusicPlayer::send(uint32 b) {
+ if (_passThrough) {
+ _driver->send(b);
+ return;
+ }
+
+ byte channel = (byte)(b & 0x0F);
+ if ((b & 0xFFF0) == 0x07B0) {
+ // Adjust volume changes by master volume
+ byte volume = (byte)((b >> 16) & 0x7F);
+ _channelVolume[channel] = volume;
+ volume = volume * _masterVolume / 255;
+ b = (b & 0xFF00FFFF) | (volume << 16);
+ } else if ((b & 0xF0) == 0xC0 && !_nativeMT32) {
+ b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
+ }
+ else if ((b & 0xFFF0) == 0x007BB0) {
+ //Only respond to All Notes Off if this channel
+ //has currently been allocated
+ if (_channel[b & 0x0F])
+ return;
+ }
+
+ //Work around annoying loud notes in certain Roland Floda tunes
+ if (channel == 3 && _currentSong == 90)
+ return;
+ if (channel == 4 && _currentSong == 27)
+ return;
+ if (channel == 5 && _currentSong == 38)
+ return;
+
+ if (!_channel[channel])
+ _channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+
+ if (_channel[channel])
+ _channel[channel]->send(b);
+}
+
+void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
+ //Only thing we care about is End of Track.
+ if (type != 0x2F)
+ return;
+
+ if (_looping || _songQueue[1])
+ playMusic();
+ else
+ stopMusic();
+}
+
+void MusicPlayer::onTimer(void *refCon) {
+ MusicPlayer *music = (MusicPlayer *)refCon;
+ if (music->_isPlaying)
+ music->_parser->onTimer();
+}
+
+void MusicPlayer::queueTuneList(int16 tuneList) {
+ queueClear();
+
+ //Jungle is the only part of the game that uses multiple tunelists.
+ //For the sake of code simplification we just hardcode the extended list ourselves
+ if ((tuneList + 1) == 3) {
+ _randomLoop = true;
+ int i = 0;
+ while (Sound::_jungleList[i])
+ queueSong(Sound::_jungleList[i++] - 1);
+ return;
+ }
+
+ int mode = (_numSongs == 40) ? Sound::_tuneDemo[tuneList].mode : Sound::_tune[tuneList].mode;
+ switch (mode) {
+ case 0: // random loop
+ _randomLoop = true;
+ setLoop(false);
+ break;
+ case 1: // sequential loop
+ setLoop(_songQueue[1] == 0);
+ break;
+ case 2: // play once
+ default:
+ setLoop(false);
+ break;
+ }
+
+ int i = 0;
+ if (_numSongs == 40) {
+ while (Sound::_tuneDemo[tuneList].tuneNum[i])
+ queueSong(Sound::_tuneDemo[tuneList].tuneNum[i++] - 1);
+ } else {
+ while (Sound::_tune[tuneList].tuneNum[i])
+ queueSong(Sound::_tune[tuneList].tuneNum[i++] - 1);
+ }
+
+ if (_randomLoop)
+ _queuePos = randomQueuePos();
+}
+
+void MusicPlayer::playMusic() {
+ if (!_songQueue[0]) {
+ debug(5, "MusicPlayer::playMusic - Music queue is empty!");
+ return;
+ }
+
+ uint16 songNum = _songQueue[_queuePos];
+
+ //Special type
+ // > 1000 && < 2000 -> queue different tunelist
+ // 2000 -> repeat music from previous queue
+ if (songNum > 999) {
+ if ((songNum + 1) == 2000) {
+ songNum = _lastSong;
+ queueClear();
+ queueSong(songNum);
+ } else {
+ queueTuneList(songNum - 1000);
+ _queuePos = _randomLoop ? randomQueuePos() : 0;
+ songNum = _songQueue[_queuePos];
+ }
+ }
+
+ byte *prevSong = _musicData + songOffset(_currentSong);
+ if (*prevSong == 0x43 || *prevSong == 0x63) {
+ if (_buf) {
+ delete[] _buf;
+ _buf = 0;
+ }
+ }
+
+ _currentSong = songNum;
+ if (!songNum) {
+ stopMusic();
+ return;
+ }
+
+ byte *musicPtr = _musicData + songOffset(songNum);
+ uint32 size = songLength(songNum);
+ if (*musicPtr == 0x43 || *musicPtr == 0x63) {
+ uint32 packedSize = songLength(songNum) - 0x200;
+ _buf = new uint16[packedSize];
+
+ uint16 *data = (uint16 *)(musicPtr + 1);
+ byte *idx = ((byte *)data) + 0x200;
+
+ for (uint i = 0; i < packedSize; i++)
+#if defined(SCUMM_NEED_ALIGNMENT)
+ memcpy(&_buf[i], (byte*)((byte*)data + *(idx + i) * sizeof(uint16)), sizeof(uint16));
+#else
+ _buf[i] = data[*(idx + i)];
+#endif
+
+ musicPtr = ((byte *)_buf) + ((*musicPtr == 0x63) ? 1 : 0);
+ size = packedSize * 2;
+ }
+
+ _parser->loadMusic(musicPtr, size);
+ _parser->setTrack(0);
+ debug(8, "Playing song %d [queue position: %d]", songNum, _queuePos);
+ _isPlaying = true;
+ queueUpdatePos();
+}
+
+void MusicPlayer::queueUpdatePos() {
+ if (_randomLoop) {
+ _queuePos = randomQueuePos();
+ } else {
+ if (_queuePos < (MUSIC_QUEUE_SIZE - 1) && _songQueue[_queuePos + 1])
+ _queuePos++;
+ else if (_looping)
+ _queuePos = 0;
+ }
+}
+
+uint8 MusicPlayer::randomQueuePos() {
+ int queueSize = 0;
+ for (int i = 0; i < MUSIC_QUEUE_SIZE; i++)
+ if (_songQueue[i])
+ queueSize++;
+
+ if (!queueSize)
+ return 0;
+
+ return (uint8) _rnd.getRandomNumber(queueSize - 1) & 0xFF;
+}
+
+void MusicPlayer::stopMusic() {
+ _isPlaying = false;
+ _parser->unloadMusic();
+}
+
+uint32 MusicPlayer::songOffset(uint16 songNum) const {
+ uint16 offsLo = READ_LE_UINT16(_musicData + (songNum * 4) + 2);
+ uint16 offsHi = READ_LE_UINT16(_musicData + (songNum * 4) + 4);
+ return (offsHi << 4) | offsLo;
+}
+
+uint32 MusicPlayer::songLength(uint16 songNum) const {
+ if (songNum < _numSongs)
+ return (songOffset(songNum + 1) - songOffset(songNum));
+ return (_musicDataSize - songOffset(songNum));
+}
+
+Music::Music(MidiDriver *driver, QueenEngine *vm) : _vToggle(false) {
+ if (vm->resource()->isDemo()) {
+ _musicData = vm->resource()->loadFile("AQ8.RL", 0, &_musicDataSize);
+ } else {
+ _musicData = vm->resource()->loadFile("AQ.RL", 0, &_musicDataSize);
+ }
+ _player = new MusicPlayer(driver, _musicData, _musicDataSize);
+}
+
+Music::~Music() {
+ delete _player;
+ delete[] _musicData;
+}
+
+void Music::playSong(uint16 songNum) {
+ _player->queueClear();
+ _player->queueSong(songNum);
+ _player->playMusic();
+}
+
+void Music::toggleVChange() {
+ setVolume(_vToggle ? (volume() * 2) : (volume() / 2));
+ _vToggle ^= true;
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/music.h b/engines/queen/music.h
new file mode 100644
index 0000000000..1051b8b988
--- /dev/null
+++ b/engines/queen/music.h
@@ -0,0 +1,125 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENMUSIC_H
+#define QUEENMUSIC_H
+
+#include "common/util.h"
+#include "sound/mididrv.h"
+
+class MidiParser;
+
+namespace Queen {
+
+class QueenEngine;
+
+class MusicPlayer : public MidiDriver {
+public:
+ MusicPlayer(MidiDriver *driver, byte *data, uint32 size);
+ ~MusicPlayer();
+ void setVolume(int volume);
+ int getVolume() { return _masterVolume; }
+
+ void hasNativeMT32(bool b) { _nativeMT32 = b; }
+ void playMusic();
+ void stopMusic();
+ void setLoop(bool loop) { _looping = loop; }
+ void queueTuneList(int16 tuneList);
+ bool queueSong(uint16 songNum);
+ void queueClear();
+ void setPassThrough(bool b) { _passThrough = b; }
+
+ //MidiDriver interface implementation
+ int open();
+ void close();
+ void send(uint32 b);
+
+ void metaEvent(byte type, byte *data, uint16 length);
+
+ void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
+ uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; }
+
+ //Channel allocation functions
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+protected:
+
+ enum {
+ MUSIC_QUEUE_SIZE = 14
+ };
+
+ void queueUpdatePos();
+ uint8 randomQueuePos();
+ static void onTimer(void *data);
+ uint32 songOffset(uint16 songNum) const;
+ uint32 songLength(uint16 songNum) const;
+
+ MidiDriver *_driver;
+ MidiParser *_parser;
+ MidiChannel *_channel[16];
+ byte _channelVolume[16];
+ bool _nativeMT32;
+ bool _passThrough;
+
+ Common::RandomSource _rnd;
+
+ bool _isPlaying;
+ bool _looping;
+ bool _randomLoop;
+ byte _masterVolume;
+ uint8 _queuePos;
+ int16 _currentSong;
+ int16 _lastSong; //first song from previous queue
+ int16 _songQueue[MUSIC_QUEUE_SIZE];
+
+ uint16 _numSongs;
+ byte *_musicData;
+ uint16 *_buf;
+ uint32 _musicDataSize;
+};
+
+class Music {
+public:
+ Music(MidiDriver *_driver, QueenEngine *vm);
+ ~Music();
+ void hasNativeMT32(bool b) { _player->hasNativeMT32(b); }
+ void playSong(uint16 songNum);
+ void queueTuneList(int16 tuneList) { _player->queueTuneList(tuneList); }
+ void playMusic() { _player->playMusic(); }
+ void stopSong() { _player->stopMusic(); }
+ void setPassThrough(bool b) { _player->setPassThrough(b); }
+
+ void toggleVChange();
+ void setVolume(int vol) { _player->setVolume(vol); }
+ int volume() { return _player->getVolume(); }
+
+protected:
+ bool _vToggle;
+ byte *_musicData;
+ uint32 _musicDataSize;
+ MusicPlayer *_player;
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/musicdata.cpp b/engines/queen/musicdata.cpp
new file mode 100644
index 0000000000..39acdf8ab4
--- /dev/null
+++ b/engines/queen/musicdata.cpp
@@ -0,0 +1,1944 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/sound.h"
+
+namespace Queen {
+
+#ifdef PALMOS_68K
+
+const songData *Sound::_songDemo;
+const songData *Sound::_song;
+const tuneData *Sound::_tuneDemo;
+const tuneData *Sound::_tune;
+const char *Sound::_sfxName;
+const int16 *Sound::_jungleList;
+
+#else
+const songData Sound::_songDemo[] = {
+ /* 1 - Hotel Gangsters */
+ { { 1, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 2 - Hotel General */
+ { { 2, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 3 - Jungle */
+ { { 3, 4, 5, 6, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 4 - Waterfall On */
+ { { 7, 0 }, 128, 128, 128, 0, 0 },
+
+ /* 5 - Vnormal */
+ { { 8, 0 }, 128, 128, 128, 2, 0 },
+
+ /* 6 - Trader Bob */
+ { { 9, 0 }, 120, 128, 128, 1, 0 },
+
+ /* 7 - Jetty Music */
+ { { 10, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 8 - Ferry Music */
+ { { 11, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 9 - Temple Upstairs */
+ { { 12, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 10 - Temple Downstairs */
+ { { 13, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 11 - Temple Maze */
+ { { 14, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 12 - Temple Skull */
+ { { 15, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 13 - Johns Theme (Love Story) */
+ { { 16, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 14 - Vmedium */
+ { { 17, 0 }, 128, 128, 0, 2, 0 },
+
+ /* 15 - Vsoft */
+ { { 18, 0 }, 128, 128, 0, 2, 0 },
+
+ /* 16 - Floda Upstairs */
+ { { 19, 0 }, 128, 128, 0, 1, 0 },
+
+ /* 17 - Floda General */
+ { { 20, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 18 - Klunks Room */
+ { { 21, 0 }, 110, 128, 128, 1, 0 },
+
+ /* 19 - Hotel Lola */
+ { { 22, 0 }, 120, 18128, 128, 1, 0 },
+
+ /* 20 - Hotel Escape 1 */
+ { { 23, 0 }, 128, 18128, 128, 1, 0 },
+
+ /* 21 - Amazon Fortress */
+ { { 24, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 22 - Waterfall Off */
+ { { 25, 0 }, 128, 128, 128, 0, 0 },
+
+ /* 23 - Wave Torch */
+ { { 26, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 24 - Zombies Rez Out */
+ { { 27, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 25 - Open Door (standard) */
+ { { 28, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 26 - Close Door (standard) */
+ { { 29, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 27 - Cloth Unrolls */
+ { { 30, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 28 - Snake Slithers Off */
+ { { 31, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 29 - Hotel Fanfare 2 */
+ { { 32, 0 }, 128, 128, 128, 1, 1 },
+
+ /* 30 - Floda Secret */
+ { { 33, 0 }, 120, 128, 128, 1, 0 },
+
+ /* 31 - Temple Fanfare 1 */
+ { { 34, 0 }, 128, 128, 128, 1, 1 },
+};
+
+const songData Sound::_song[] = {
+ /* 1 - Hotel Gangsters */
+ { { 1, 0 }, 128, 180, 0, 1, 0 },
+
+ /* 2 - Hotel General */
+ { { 2, 0 }, 128, 180, 0, 1, 0 },
+
+ /* 3 - Jungle */
+ { { 3, 4, 5, 6, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 4 - Waterfall On */
+ { { 7, 0 }, 128, 0, 0, 0, 0 },
+
+ /* 5 - Vnormal */
+ { { 8, 0 }, 128, 0, 0, 2, 0 },
+
+ /* 6 - Trader Bob */
+ { { 9, 0 }, 120, 0, 0, 1, 0 },
+
+ /* 7 - Jetty Music */
+ { { 10, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 8 - Ferry Music */
+ { { 11, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 9 - Temple Upstairs */
+ { { 12, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 10 - Temple Downstairs */
+ { { 13, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 11 - Temple Maze */
+ { { 14, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 12 - Temple Skull */
+ { { 15, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 13 - Johns Theme (Love Story) */
+ { { 16, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 14 - Vmedium */
+ { { 17, 0 }, 120, 0, 0, 2, 0 },
+
+ /* 15 - Vsoft */
+ { { 18, 0 }, 110, 0, 0, 2, 0 },
+
+ /* 16 - Floda Upstairs */
+ { { 19, 0 }, 110, 0, 0, 1, 0 },
+
+ /* 17 - Floda General */
+ { { 20, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 18 - Klunks Room */
+ { { 21, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 19 - Hotel Lola */
+ { { 22, 0 }, 120, 180, 0, 1, 0 },
+
+ /* 20 - Hotel Escape 1 */
+ { { 23, 0 }, 128, 180, 0, 1, 0 },
+
+ /* 21 - Amazon Fortress */
+ { { 24, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 22 - Waterfall Off */
+ { { 25, 0 }, 128, 0, 0, 0, 0 },
+
+ /* 23 - Wave Torch */
+ { { 26, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 24 - Zombies Rez Out */
+ { { 27, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 25 - Open Door (standard) */
+ { { 28, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 26 - Close Door (standard) */
+ { { 29, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 27 - Cloth Unrolls */
+ { { 30, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 28 - Snake Slithers Off */
+ { { 31, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 29 - Hotel Fanfare 2 */
+ { { 32, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 30 - Floda Secret */
+ { { 33, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 31 - Temple Fanfare 1 */
+ { { 34, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 32 - Commander Rocket 1 */
+ { { 35, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 33 - Hotel Escape 2 */
+ { { 36, 0 }, 128, 180, 0, 1, 0 },
+
+ /* 34 - Back of Truck */
+ { { 37, 0 }, 128, 180, 0, 1, 0 },
+
+ /* 35 - Hotel Fanfare 1 */
+ { { 38, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 36 - Truck Fanfare */
+ { { 39, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 37 - Airport */
+ { { 40, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 38 - Plane Leaves */
+ { { 41, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 39 - Arrive Hotel */
+ { { 42, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 40 - Jungle Fanfare */
+ { { 43, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 41 - General Fanfare */
+ { { 44, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 42 - Johns Room */
+ { { 45, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 43 - Floda Lab */
+ { { 46, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 44 - Azura's Theme */
+ { { 47, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 45 - Use Record */
+ { { 48, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 46 - Franks Theme */
+ { { 49, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 47 - Anderson Doubts */
+ { { 50, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 48 - Bud and Lou Theme */
+ { { 51, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 49 - Gorilla Theme */
+ { { 52, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 50 - Missionaries Theme */
+ { { 53, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 51 - Sloth Theme */
+ { { 54, 0 }, 128, 0, 0, 1, 1 },
+
+ /* 52 - Amazon Dungeon */
+ { { 55, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 53 - Throne Room */
+ { { 56, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 54 - Temple Puzzle */
+ { { 57, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 55 - Temple Fountain Room */
+ { { 58, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 56 - Light Switch */
+ { { 59, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 57 - Hydraulic Open */
+ { { 60, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 58 - Hydraulic Close */
+ { { 61, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 59 - Close Door (metal) */
+ { { 62, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 60 - Small Hatch Close */
+ { { 63, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 61 - Scissors Snip */
+ { { 64, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 62 - Pick up Sticky */
+ { { 65, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 63 - Oracle Rezzes In */
+ { { 66, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 64 - Sparkle SFX */
+ { { 67, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 65 - Splorch! */
+ { { 68, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 66 - Pour Liquid */
+ { { 69, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 67 - End Credit Medley */
+ { { 70, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 68 - Dino Ray */
+ { { 71, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 69 - Squish! */
+ { { 72, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 70 - Robot Laser */
+ { { 73, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 71 - Thud wood light */
+ { { 74, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 72 - Thud wood deep */
+ { { 75, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 73 - Thud metallic */
+ { { 76, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 74 - Cut Coconut */
+ { { 77, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 75 - Thud Stone */
+ { { 78, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 76 - Cloth Slide 1 */
+ { { 79, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 77 - Open Chest */
+ { { 80, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 78 - Close Chest */
+ { { 81, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 79 - Open Drawer */
+ { { 82, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 80 - Truck door closes */
+ { { 83, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 81 - Truck Starts */
+ { { 84, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 82 - Truck Drives Off */
+ { { 85, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 83 - Fish Splash */
+ { { 86, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 84 - Close Drawer/Push Ladder */
+ { { 87, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 85 - Agression Enhancer */
+ { { 88, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 86 - Stone Door Grind 1 */
+ { { 89, 0 }, 128, 0, 0, 0, 1 },
+
+ /* 87 - Prequel 1 */
+ { { 90, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 88 - Intro Credits */
+ { { 91, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 89 - Valley 1 */
+ { { 92, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 90 - Valley 3 */
+ { { 93, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 91 - Fight Music */
+ { { 94, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 92 - Confrontation 1 */
+ { { 95, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 93 - Confrontation 2 */
+ { { 96, 0 }, 128, 0, 0, 1, 0 },
+
+ /* 94 - Plane Hatch Open */
+ { { 97, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 95 - Plane Hatch Close */
+ { { 98, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 96 - Tie Vines */
+ { { 99, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 97 - Pterodactyl */
+ { { 100, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 98 - Beef Jerky Splash */
+ { { 101, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 99 - Piranha Burp */
+ { { 102, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 100 - Falling Vine */
+ { { 103, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 101 - Stone Door Grind 2 */
+ { { 104, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 102 - Stone Grind (light) */
+ { { 105, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 103 - Ape Takes Off Mask */
+ { { 106, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 104 - Bark Breaks */
+ { { 107, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 105 - Stone Click */
+ { { 108, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 106 - Sproing! */
+ { { 109, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 107 - Cash Register */
+ { { 110, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 108 - Squeaky Toy */
+ { { 111, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 109 - Falling Chains */
+ { { 112, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 110 - Open Locker Door */
+ { { 113, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 111 - Close Locker Door */
+ { { 114, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 112 - Rub Pencil */
+ { { 115, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 113 - Open Safe */
+ { { 116, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 114 - Close Safe */
+ { { 117, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 115 - Push Chair */
+ { { 118, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 116 - Snake Hiss */
+ { { 119, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 117 - Oracle Rezzes Out */
+ { { 120, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 118 - Wall Crumbles */
+ { { 121, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 119 - Crypt Crumbles */
+ { { 122, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 120 - Joe Sucked Up */
+ { { 123, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 121 - Rocket Pack Zoom */
+ { { 124, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 122 - Piranha Splash */
+ { { 125, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 123 - Snap Branch */
+ { { 126, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 124 - Dino Horn */
+ { { 127, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 125 - Tyre Screech */
+ { { 128, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 126 - Oil Splat */
+ { { 129, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 127 - Punch */
+ { { 130, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 128 - Body Hits Ground */
+ { { 131, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 129 - Chicken */
+ { { 132, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 130 - Open Sarcophagus */
+ { { 133, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 131 - Close Sarcophagus */
+ { { 134, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 132 - Creaking Stick */
+ { { 135, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 133 - Pick Hits Stone */
+ { { 136, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 134 - Stalactite Crumbles */
+ { { 137, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 135 - Tic-Toc */
+ { { 138, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 136 - Stone Grind (heavy) */
+ { { 139, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 137 - Explosion */
+ { { 140, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 138 - Cloth Slide 2 */
+ { { 141, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 139 - Temple Laser */
+ { { 142, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 140 - Dino Transformation */
+ { { 143, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 141 - Experimental Laser */
+ { { 144, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 142 - Stone Grind (medium) */
+ { { 145, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 143 - Weeping God Grind */
+ { { 146, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 144 - Alien Hum */
+ { { 147, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 145 - Alien Puzzle */
+ { { 148, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 146 - Vacuum On */
+ { { 149, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 147 - Vacuum Off */
+ { { 150, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 148 - Elevator Starts */
+ { { 151, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 149 - Mummy Crumbles */
+ { { 152, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 150 - Temple Green Circle */
+ { { 153, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 151 - Rattle Bars */
+ { { 154, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 152 - Door Dissolves */
+ { { 155, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 153 - Altar Slides */
+ { { 156, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 154 - Light Torch */
+ { { 157, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 155 - Stamp Sound */
+ { { 158, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 156 - Plaster Loud */
+ { { 159, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 157 - Sparky Bathtub */
+ { { 160, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 158 - Ape Rezzes Out */
+ { { 161, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 159 - Song 159 */
+ { { 162, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 160 - Song 160 */
+ { { 163, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 161 - Song 161 */
+ { { 164, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 162 - Piranhas Swim */
+ { { 165, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 163 - Prison/Dungeon Door */
+ { { 166, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 164 - Fight Explosion */
+ { { 167, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 165 - Press Button */
+ { { 168, 0 }, 128, 128, 128, 2, 1 },
+
+ /* 166 - Pull Lever */
+ { { 169, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 167 - Wrong Code */
+ { { 170, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 168 - Correct Code */
+ { { 171, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 169 - Sizzle */
+ { { 172, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 170 - Money In Slot */
+ { { 173, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 171 - Lightning Crack */
+ { { 174, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 172 - Machine Gun Fire */
+ { { 175, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 173 - Cage Descends */
+ { { 176, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 174 - Chair Activates */
+ { { 177, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 175 - Robot Powers On */
+ { { 178, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 176 - Grow Big */
+ { { 179, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 177 - Eat Food */
+ { { 180, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 178 - Head Shrink */
+ { { 181, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 179 - Grinding Gears */
+ { { 182, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 180 - Chair Splash */
+ { { 183, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 181 - Deflect Laser */
+ { { 184, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 182 - Zap Frank */
+ { { 185, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 183 - Frank Transforms */
+ { { 186, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 184 - Alarm Clock */
+ { { 187, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 185 - Slide Chute */
+ { { 188, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 186 - Puff */
+ { { 189, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 187 - Bite */
+ { { 190, 0 }, 128, 128, 128, 0, 0 },
+
+ /* 188 - Stone Door Grind 2 */
+ { { 191, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 189 - Prequel 2 */
+ { { 192, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 190 - Prequel 3 */
+ { { 193, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 191 - Prequel 4 */
+ { { 194, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 192 - Stop Music */
+ { { 195, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 193 - Plane Flyby */
+ { { 196, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 194 - Commander Rocket 2 */
+ { { 197, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 195 - Commander Rocket 3 */
+ { { 198, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 196 - Rescue */
+ { { 199, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 197 - Slow Fanfare */
+ { { 200, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 198 - Plane Crash */
+ { { 201, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 199 - Plane Engine 1 */
+ { { 202, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 200 - Plane Engine 2 */
+ { { 203, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 201 - Boat In */
+ { { 204, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 202 - Boat Out */
+ { { 205, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 203 - Final Fanfare! */
+ { { 206, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 204 - Frank Destroyed */
+ { { 207, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 205 - Jaspar Eats */
+ { { 208, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 206 - Compy Scream 1 */
+ { { 209, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 207 - Compy Scream 2 */
+ { { 210, 0 }, 128, 128, 128, 0, 1 },
+
+ /* 208 - Punch Klunk Fanfare */
+ { { 211, 0 }, 128, 128, 128, 1, 0 },
+
+ /* 209 - Talk Frank */
+ { { 212, 0 }, 128, 128, 128, 1, 0 }
+};
+
+const tuneData Sound::_tuneDemo[] = {
+ /* 1 - Hotel Gangsters */
+ { { 32, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 2 - Hotel General */
+ { { 26, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 3 - Jungle */
+ { { 15, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 4 - Jungle */
+ { { 17, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 5 - Jungle */
+ { { 18, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 6 - Jungle */
+ { { 7, 8, 9, 10, 11, 12, 13, 14, 0 }, { 0, 0 }, 0, 0 },
+
+ /* 7 - Waterfall On */
+ { { 3, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 8 - Vnormal */
+ { { 1, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 9 - Trader Bob */
+ { { 1, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 10 - Jetty Music */
+ { { 37, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 11 - Ferry Music */
+ { { 38, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 12 - Temple Upstairs */
+ { { 30, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 13 - Temple Downstairs */
+ { { 34, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 14 - Temple Maze */
+ { { 35, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 15 - Temple Skull */
+ { { 36, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 16 - Johns Theme (Love Story) */
+ { { 43, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 17 - Vmedium */
+ { { 28, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 18 - Vsoft */
+ { { 28, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 19 - Floda Upstairs */
+ { { 28, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 20 - Floda General */
+ { { 29, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 21 - Klunks Room */
+ { { 39, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 22 - Hotel Lola */
+ { { 31, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 23 - Hotel Escape 1 */
+ { { 33, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 24 - Amazon Fortress */
+ { { 40, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 25 - Waterfall Off */
+ { { -3, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 26 - Wave Torch */
+ { { 22, 0 }, { 121, 0 }, 2, 0 },
+
+ /* 27 - Zombies Rez Out */
+ { { 25, 0 }, { 20, 0 }, 2, 0 },
+
+ /* 28 - Open Door (standard) */
+ { { 20, 0 }, { 1, 0 }, 2, 0 },
+
+ /* 29 - Close Door (standard) */
+ { { 21, 0 }, { 2, 0 }, 2, 0 },
+
+ /* 30 - Cloth Unrolls */
+ { { 23, 0 }, { 51, 0 }, 2, 0 },
+
+ /* 31 - Snake Slithers Off */
+ { { 24, 0 }, { 122, 0 }, 2, 0 },
+
+ /* 32 - Hotel Fanfare 2 */
+ { { 69, 1003, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 33 - Floda Secret */
+ { { 28, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 34 - Temple Fanfare 1 */
+ { { 60, 162, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 35 - Commander Rocket 1 */
+ { { 46, 0 }, { 0, 0 }, 1, 0 },
+};
+
+const tuneData Sound::_tune[] = {
+ /* 1 - Hotel Gangsters */
+ { { 32, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 2 - Hotel General */
+ { { 41, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 3 - Jungle */
+ { { 15, 16, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 4 - Jungle */
+ { { 17, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 5 - Jungle */
+ { { 18, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 6 - Jungle */
+ { { 7, 8, 9, 10, 11, 12, 13, 14, 0 }, { 0, 0 }, 0, -10 },
+
+ /* 7 - Waterfall On */
+ { { 3, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 8 - Vnormal */
+ { { 23, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 9 - Trader Bob */
+ { { 23, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 10 - Jetty Music */
+ { { 37, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 11 - Ferry Music */
+ { { 38, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 12 - Temple Upstairs */
+ { { 30, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 13 - Temple Downstairs */
+ { { 34, 36, 56, 0 }, { 0, 0 }, 0, 0 },
+
+ /* 14 - Temple Maze */
+ { { 87, 35, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 15 - Temple Skull */
+ { { 76, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 16 - Johns Theme (Love Story) */
+ { { 44, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 17 - Vmedium */
+ { { 28, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 18 - Vsoft */
+ { { 28, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 19 - Floda Upstairs */
+ { { 28, 39, 0 }, { 0, 0 }, 0, 0 },
+
+ /* 20 - Floda General */
+ { { 89, 63, 64, 65, 0 }, { 0, 0 }, 0, 0 },
+
+ /* 21 - Klunks Room */
+ { { 43, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 22 - Hotel Lola */
+ { { 31, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 23 - Hotel Escape 1 */
+ { { 52, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 24 - Amazon Fortress */
+ { { 40, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 25 - Waterfall Off */
+ { {-3, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 26 - Wave Torch */
+ { { 0, 0 }, { 121, 0 }, 2, 0 },
+
+ /* 27 - Zombies Rez Out */
+ { { 0, 0 }, { 20, 0 }, 2, 0 },
+
+ /* 28 - Open Door (standard) */
+ { { 0, 0 }, { 1, 0 }, 2, 0 },
+
+ /* 29 - Close Door (standard) */
+ { { 0, 0 }, { 2, 0 }, 2, 0 },
+
+ /* 30 - Cloth Unrolls */
+ { { 0, 0 }, { 51, 0 }, 2, 0 },
+
+ /* 31 - Snake Slithers Off */
+ { { 0, 0 }, { 122, 0 }, 2, 0 },
+
+ /* 32 - Hotel Fanfare 2 */
+ { { 69, 2000, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 33 - Floda Secret */
+ { { 29, 42, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 34 - Temple Fanfare 1 */
+ { { 70, 2000, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 35 - Commander Rocket 1 */
+ { { 45, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 36 - Hotel Escape 2 */
+ { { 52, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 37 - Back of Truck */
+ { { 51, 48, 33, 54, 52, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 38 - Hotel Fanfare 1 */
+ { { 67, 2000, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 39 - Truck Fanfare */
+ { { 67, 177, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 40 - Airport */
+ { { 81, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 41 - Plane Leaves */
+ { { 68, 1198, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 42 - Arrive Hotel */
+ { { 26, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 43 - Jungle Fanfare */
+ { { 68, 2000, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 44 - General Fanfare */
+ { { 57, 2000, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 45 - Johns Room */
+ { { 90, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 46 - Floda Lab */
+ { { 92, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 47 - Azura's Theme */
+ { { 80, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 48 - Use Record */
+ { { 91, 2000, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 49 - Franks Theme */
+ { { 77, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 50 - Anderson Doubts */
+ { { 75, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 51 - Bud and Lou Theme */
+ { { 94, 1003, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 52 - Gorilla Theme */
+ { { 97, 1003, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 53 - Missionaries Theme */
+ { { 98, 1003, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 54 - Sloth Theme */
+ { { 100, 1003, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 55 - Amazon Dungeon */
+ { { 96, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 56 - Throne Room */
+ { { 78, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 57 - Temple Puzzle */
+ { { 88, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 58 - Temple Fountain Room */
+ { { 55, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 59 - Light Switch */
+ { { 0, 0 }, { 4, 0 }, 2, 0 },
+
+ /* 60 - Hydraulic Open */
+ { { 0, 0 }, { 7, 0 }, 2, 0 },
+
+ /* 61 - Hydraulic Close */
+ { { 0, 0 }, { 8, 0 }, 2, 0 },
+
+ /* 62 - Close Door (metal) */
+ { { 0, 0 }, { 9, 0 }, 2, 0 },
+
+ /* 63 - Small Hatch Close */
+ { { 0, 0 }, { 10, 0 }, 2, 0 },
+
+ /* 64 - Scissors Snip */
+ { { 0, 0 }, { 5, 0 }, 2, 0 },
+
+ /* 65 - Pick up Sticky */
+ { { 0, 0 }, { 6, 0 }, 2, 0 },
+
+ /* 66 - Oracle Rezzes In */
+ { { 0, 0 }, { 11, 0 }, 2, 0 },
+
+ /* 67 - Sparkle SFX */
+ { { 0, 0 }, { 12, 0 }, 2, 0 },
+
+ /* 68 - Splorch! */
+ { { 0, 0 }, { 13, 0 }, 2, 0 },
+
+ /* 69 - Pour Liquid */
+ { { 0, 0 }, { 3, 0 }, 2, 0 },
+
+ /* 70 - End Credit Medley */
+ { { 95, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 71 - Dino Ray */
+ { { 0, 0 }, { 14, 0 }, 2, 0 },
+
+ /* 72 - Squish! */
+ { { 0, 0 }, { 15, 0 }, 2, 0 },
+
+ /* 73 - Robot Laser */
+ { { 0, 0 }, { 16, 0 }, 2, 0 },
+
+ /* 74 - Thud wood light */
+ { { 0, 0 }, { 17, 0 }, 2, 0 },
+
+ /* 75 - Thud wood deep */
+ { { 0, 0 }, { 18, 0 }, 2, 0 },
+
+ /* 76 - Thud metallic */
+ { { 0, 0 }, { 19, 0 }, 2, 0 },
+
+ /* 77 - Cut Coconut */
+ { { 0, 0 }, { 22, 0 }, 2, 0 },
+
+ /* 78 - Thud Stone */
+ { { 0, 0 }, { 23, 0 }, 2, 0 },
+
+ /* 79 - Cloth Slide 1 */
+ { { 0, 0 }, { 24, 0 }, 2, 0 },
+
+ /* 80 - Open Chest */
+ { { 0, 0 }, { 25, 0 }, 2, 0 },
+
+ /* 81 - Close Chest */
+ { { 0, 0 }, { 26, 0 }, 2, 0 },
+
+ /* 82 - Open Drawer */
+ { { 0, 0 }, { 27, 0 }, 2, 0 },
+
+ /* 83 - Truck door closes */
+ { { 0, 0 }, { 28, 0 }, 2, 0 },
+
+ /* 84 - Truck Starts */
+ { { 0, 0 }, { 29, 0 }, 2, 0 },
+
+ /* 85 - Truck Drives Off */
+ { { 0, 0 }, { 30, 0 }, 2, 0 },
+
+ /* 86 - Fish Splash */
+ { { 0, 0 }, { 31, 0 }, 2, 0 },
+
+ /* 87 - Close Drawer/Push Ladder */
+ { { 0, 0 }, { 33, 0 }, 2, 0 },
+
+ /* 88 - Agression Enhancer */
+ { { 0, 0 }, { 32, 0 }, 2, 0 },
+
+ /* 89 - Stone Door Grind 1 */
+ { { 0, 0 }, { 78, 0 }, 2, 0 },
+
+ /* 90 - Prequel 1 */
+ { { 20, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 91 - Intro Credits */
+ { { 21, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 92 - Valley 1 */
+ { { 71, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 93 - Valley 3 */
+ { { 73, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 94 - Fight Music */
+ { { 72, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 95 - Confrontation 1 */
+ { { 93, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 96 - Confrontation 2 */
+ { { 74, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 97 - Plane Hatch Open */
+ { { 0, 0 }, { 35, 0 }, 2, 0 },
+
+ /* 98 - Plane Hatch Close */
+ { { 0, 0 }, { 36, 0 }, 2, 0 },
+
+ /* 99 - Tie Vines */
+ { { 0, 0 }, { 37, 0 }, 2, 0 },
+
+ /* 100 - Pterodactyl */
+ { { 0, 0 }, { 38, 0 }, 2, 0 },
+
+ /* 101 - Beef Jerky Splash */
+ { { 0, 0 }, { 39, 0 }, 2, 0 },
+
+ /* 102 - Piranha Burp */
+ { { 0, 0 }, { 40, 0 }, 2, 0 },
+
+ /* 103 - Falling Vine */
+ { { 0, 0 }, { 41, 0 }, 2, 0 },
+
+ /* 104 - Stone Door Grind 2 */
+ { { 0, 0 }, { 79, 0 }, 2, 0 },
+
+ /* 105 - Stone Grind (light) */
+ { { 0, 0 }, { 82, 0 }, 2, 0 },
+
+ /* 106 - Ape Takes Off Mask */
+ { { 0, 0 }, { 44, 0 }, 2, 0 },
+
+ /* 107 - Bark Breaks */
+ { { 0, 0 }, { 45, 0 }, 2, 0 },
+
+ /* 108 - Stone Click */
+ { { 0, 0 }, { 46, 0 }, 2, 0 },
+
+ /* 109 - Sproing! */
+ { { 0, 0 }, { 42, 0 }, 2, 0 },
+
+ /* 110 - Cash Register */
+ { { 0, 0 }, { 48, 0 }, 2, 0 },
+
+ /* 111 - Squeaky Toy */
+ { { 0, 0 }, { 49, 0 }, 2, 0 },
+
+ /* 112 - Falling Chains */
+ { { 0, 0 }, { 50, 0 }, 2, 0 },
+
+ /* 113 - Open Locker Door */
+ { { 0, 0 }, { 52, 0 }, 2, 0 },
+
+ /* 114 - Close Locker Door */
+ { { 0, 0 }, { 53, 0 }, 2, 0 },
+
+ /* 115 - Rub Pencil */
+ { { 0, 0 }, { 54, 0 }, 2, 0 },
+
+ /* 116 - Open Safe */
+ { { 0, 0 }, { 55, 0 }, 2, 0 },
+
+ /* 117 - Close Safe */
+ { { 0, 0 }, { 56, 0 }, 2, 0 },
+
+ /* 118 - Push Chair */
+ { { 0, 0 }, { 57, 0 }, 2, 0 },
+
+ /* 119 - Snake Hiss */
+ { { 0, 0 }, { 58, 0 }, 2, 0 },
+
+ /* 120 - Oracle Rezzes Out */
+ { { 0, 0 }, { 59, 0 }, 2, 0 },
+
+ /* 121 - Wall Crumbles */
+ { { 0, 0 }, { 60, 0 }, 2, 0 },
+
+ /* 122 - Crypt Crumbles */
+ { { 0, 0 }, { 61, 0 }, 2, 0 },
+
+ /* 123 - Joe Sucked Up */
+ { { 0, 0 }, { 63, 0 }, 2, 0 },
+
+ /* 124 - Rocket Pack Zoom */
+ { { 0, 0 }, { 47, 0 }, 2, 0 },
+
+ /* 125 - Piranha Splash */
+ { { 0, 0 }, { 83, 0 }, 2, 0 },
+
+ /* 126 - Snap Branch */
+ { { 0, 0 }, { 66, 0 }, 2, 0 },
+
+ /* 127 - Dino Horn */
+ { { 0, 0 }, { 67, 0 }, 2, 0 },
+
+ /* 128 - Tyre Screech */
+ { { 0, 0 }, { 68, 0 }, 2, 0 },
+
+ /* 129 - Oil Splat */
+ { { 0, 0 }, { 70, 0 }, 2, 0 },
+
+ /* 130 - Punch */
+ { { 0, 0 }, { 71, 0 }, 2, 0 },
+
+ /* 131 - Body Hits Ground */
+ { { 0, 0 }, { 72, 0 }, 2, 0 },
+
+ /* 132 - Chicken */
+ { { 0, 0 }, { 69, 0 }, 2, 0 },
+
+ /* 133 - Open Sarcophagus */
+ { { 0, 0 }, { 21, 0 }, 2, 0 },
+
+ /* 134 - Close Sarcophagus */
+ { { 0, 0 }, { 21, 0 }, 2, 0 },
+
+ /* 135 - Creaking Stick */
+ { { 0, 0 }, { 62, 0 }, 2, 0 },
+
+ /* 136 - Pick Hits Stone */
+ { { 0, 0 }, { 73, 0 }, 2, 0 },
+
+ /* 137 - Stalactite Crumbles */
+ { { 0, 0 }, { 74, 0 }, 2, 0 },
+
+ /* 138 - Tic-Toc */
+ { { 0, 0 }, { 76, 0 }, 2, 0 },
+
+ /* 139 - Stone Grind (heavy) */
+ { { 0, 0 }, { 81, 0 }, 2, 0 },
+
+ /* 140 - Explosion */
+ { { 0, 0 }, { 77, 0 }, 2, 0 },
+
+ /* 141 - Cloth Slide 2 */
+ { { 0, 0 }, { 84, 0 }, 2, 0 },
+
+ /* 142 - Temple Laser */
+ { { 0, 0 }, { 85, 0 }, 2, 0 },
+
+ /* 143 - Dino Transformation */
+ { { 0, 0 }, { 86, 0 }, 2, 0 },
+
+ /* 144 - Experimental Laser */
+ { { 0, 0 }, { 87, 0 }, 2, 0 },
+
+ /* 145 - Stone Grind (medium) */
+ { { 0, 0 }, { 88, 0 }, 2, 0 },
+
+ /* 146 - Weeping God Grind */
+ { { 0, 0 }, { 89, 0 }, 2, 0 },
+
+ /* 147 - Alien Hum */
+ { { 0, 0 }, { 90, 0 }, 2, 0 },
+
+ /* 148 - Alien Puzzle */
+ { { 0, 0 }, { 91, 0 }, 2, 0 },
+
+ /* 149 - Vacuum On */
+ { { 0, 0 }, { 92, 0 }, 2, 0 },
+
+ /* 150 - Vacuum Off */
+ { { 0, 0 }, { 93, 0 }, 2, 0 },
+
+ /* 151 - Elevator Starts */
+ { { 0, 0 }, { 94, 0 }, 2, 0 },
+
+ /* 152 - Mummy Crumbles */
+ { { 0, 0 }, { 95, 0 }, 2, 0 },
+
+ /* 153 - Temple Green Circle */
+ { { 0, 0 }, { 96, 0 }, 2, 0 },
+
+ /* 154 - Rattle Bars */
+ { { 0, 0 }, { 97, 0 }, 2, 0 },
+
+ /* 155 - Door Dissolves */
+ { { 0, 0 }, { 98, 0 }, 2, 0 },
+
+ /* 156 - Altar Slides */
+ { { 0, 0 }, { 99, 0 }, 2, 0 },
+
+ /* 157 - Light Torch */
+ { { 0, 0 }, { 100, 0 }, 2, 0 },
+
+ /* 158 - Stamp Sound */
+ { { 0, 0 }, { 34, 0 }, 2, 0 },
+
+ /* 159 - Plaster Loud */
+ { { 0, 0 }, { 102, 0 }, 2, 0 },
+
+ /* 160 - Sparky Bathtub */
+ { { 0, 0 }, { 103, 0 }, 2, 0 },
+
+ /* 161 - Ape Rezzes Out */
+ { { 0, 0 }, { 104, 0 }, 2, 0 },
+
+ /* 162 - Song 159 */
+ { { 0, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 163 - Song 160 */
+ { { 0, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 164 - Song 161 */
+ { { 0, 0 }, { 0, 0 }, 2, 0 },
+
+ /* 165 - Piranhas Swim */
+ { { 0, 0 }, { 105, 0 }, 2, 0 },
+
+ /* 166 - Prison/Dungeon Door */
+ { { 0, 0 }, { 43, 0 }, 2, 0 },
+
+ /* 167 - Fight Explosion */
+ { { 0, 0 }, { 80, 0 }, 2, 0 },
+
+ /* 168 - Press Button */
+ { { 0, 0 }, { 65, 0 }, 2, 0 },
+
+ /* 169 - Pull Lever */
+ { { 0, 0 }, { 107, 0 }, 2, 0 },
+
+ /* 170 - Wrong Code */
+ { { 0, 0 }, { 108, 0 }, 2, 0 },
+
+ /* 171 - Correct Code */
+ { { 0, 0 }, { 109, 0 }, 2, 0 },
+
+ /* 172 - Sizzle */
+ { { 0, 0 }, { 110, 0 }, 2, 0 },
+
+ /* 173 - Money In Slot */
+ { { 0, 0 }, { 111, 0 }, 2, 0 },
+
+ /* 174 - Lightning Crack */
+ { { 0, 0 }, { 112, 0 }, 2, 0 },
+
+ /* 175 - Machine Gun Fire */
+ { { 0, 0 }, { 113, 0 }, 2, 0 },
+
+ /* 176 - Cage Descends */
+ { { 0, 0 }, { 114, 0 }, 2, 0 },
+
+ /* 177 - Chair Activates */
+ { { 0, 0 }, { 115, 0 }, 2, 0 },
+
+ /* 178 - Robot Powers On */
+ { { 0, 0 }, { 116, 0 }, 2, 0 },
+
+ /* 179 - Grow Big */
+ { { 0, 0 }, { 117, 0 }, 2, 0 },
+
+ /* 180 - Eat Food */
+ { { 0, 0 }, { 118, 0 }, 2, 0 },
+
+ /* 181 - Head Shrink */
+ { { 0, 0 }, { 119, 0 }, 2, 0 },
+
+ /* 182 - Grinding Gears */
+ { { 0, 0 }, { 120, 0 }, 2, 0 },
+
+ /* 183 - Chair Splash */
+ { { 0, 0 }, { 123, 0 }, 2, 0 },
+
+ /* 184 - Deflect Laser */
+ { { 0, 0 }, { 124, 0 }, 2, 0 },
+
+ /* 185 - Zap Frank */
+ { { 0, 0 }, { 125, 0 }, 2, 0 },
+
+ /* 186 - Frank Transforms */
+ { { 0, 0 }, { 126, 0 }, 2, 0 },
+
+ /* 187 - Alarm Clock */
+ { { 0, 0 }, { 127, 0 }, 2, 0 },
+
+ /* 188 - Slide Chute */
+ { { 0, 0 }, { 64, 0 }, 2, 0 },
+
+ /* 189 - Puff */
+ { { 0, 0 }, { 128, 0 }, 2, 0 },
+
+ /* 190 - Bite */
+ { { 0, 0 }, { 129, 0 }, 2, 0 },
+
+ /* 191 - Stone Door Grind 2 */
+ { { 0, 0 }, { 79, 0 }, 2, 0 },
+
+ /* 192 - Prequel 2 */
+ { { 22, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 193 - Prequel 3 */
+ { { 24, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 194 - Prequel 4 */
+ { { 25, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 195 - Stop Music */
+ { { 1, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 196 - Plane Flyby */
+ { { 0, 0 }, { 101, 0 }, 2, 0 },
+
+ /* 197 - Commander Rocket 2 */
+ { { 46, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 198 - Commander Rocket 3 */
+ { { 47, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 199 - Rescue */
+ { { 99, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 200 - Slow Fanfare */
+ { { 0, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 201 - Plane Crash */
+ { { 93, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 202 - Plane Engine 1 */
+ { { 0, 0 }, { 130, 0 }, 2, 0 },
+
+ /* 203 - Plane Engine 2 */
+ { { 0, 0 }, { 131, 0 }, 2, 0 },
+
+ /* 204 - Boat In */
+ { { 0, 0 }, { 132, 0 }, 2, 0 },
+
+ /* 205 - Boat Out */
+ { { 0, 0 }, { 133, 0 }, 2, 0 },
+
+ /* 206 - Final Fanfare! */
+ { { 21, 95, 21, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 207 - Frank Destroyed */
+ { { 25, 1044, 0 }, { 0, 0 }, 1, 0 },
+
+
+ /* 208 - Jaspar Eats */
+ { { 0, 0 }, { 134, 0 }, 2, 0 },
+
+ /* 209 - Compy Scream 1 */
+ { { 0, 0 }, { 135, 0 }, 2, 0 },
+
+ /* 210 - Compy Scream 2 */
+ { { 0, 0 }, { 136, 0 }, 2, 0 },
+
+ /* 211 - Punch Klunk Fanfare */
+ { { 69, 1017, 0 }, { 0, 0 }, 1, 0 },
+
+ /* 212 - Talk Frank */
+ { { 77, 1017, 0 }, { 0, 0 }, 1, 0 }
+};
+
+const char *Sound::_sfxName[] = {
+ /* 1 - Door Open (standard) */
+ "116Bssss",
+
+ /* 2 - Door Close (standard) */
+ "105assss",
+
+ /* 3 - Pour Liquid */
+ "133sssss",
+
+ /* 4 - Light Switch */
+ "27ssssss",
+
+ /* 5 - Scissor Snip */
+ "15ssssss",
+
+ /* 6 - Pick up Sticky */
+ "79ssssss",
+
+ /* 7 - Hydraulic Doors Open */
+ "96ssssss",
+
+ /* 8 - Hydraulic Doors Close */
+ "97ssssss",
+
+ /* 9 - Metallic Door Slams */
+ "105sssss",
+
+ /* 10 - Small Hatch Close */
+ "106sssss",
+
+ /* 11 - Oracle Rezzes In */
+ "132sssss",
+
+ /* 12 - Polish Sparkle */
+ "132Cssss",
+
+ /* 13 - Splorch! */
+ "137Bssss",
+
+ /* 14 - Dino Ray Gun */
+ "138sssss",
+
+ /* 15 - Squish! */
+ "137Assss",
+
+ /* 16 - Robot Laser */
+ "61ssssss",
+
+ /* 17 - Thud wood light */
+ "109sssss",
+
+ /* 18 - Thud wood deep */
+ "110sssss",
+
+ /* 19 - Thud metallic */
+ "111sssss",
+
+ /* 20 - Zombies Rez Out */
+ "77ssssss",
+
+ /* 21 - Sarc Door Closes */
+ "58ssssss",
+
+ /* 22 - Thud breadboard fruit/Coconut */
+ "131Assss",
+
+ /* 23 - Thud stone */
+ "75ssssss",
+
+ /* 24 - Cloth Slide 1 */
+ "135sssss",
+
+ /* 25 - Open Chest */
+ "112sssss",
+
+ /* 26 - Close Chest */
+ "121sssss",
+
+ /* 27 - Open Drawer */
+ "120sssss",
+
+ /* 28 - Truck door closes */
+ "122sssss",
+
+ /* 29 - Truck Starts */
+ "123Assss",
+
+ /* 30 - Truck Drives Off */
+ "123Bssss",
+
+ /* 31 - Fish Splash */
+ "18ssssss",
+
+ /* 32 - Agression Enhancer */
+ "138Bssss",
+
+ /* 33 - Close Drawer/Push Ladder */
+ "113sssss",
+
+ /* 34 - *Stamp Sound */
+ "40ssssss",
+
+ /* 35 - plane hatch open */
+ "3sssssss",
+
+ /* 36 - plane hatch close */
+ "4sssssss",
+
+ /* 37 - tie vines */
+ "11ssssss",
+
+ /* 38 - Pterodactyl */
+ "10ssssss",
+
+ /* 39 - Beef Jerky Splash */
+ "6sssssss",
+
+ /* 40 - Piranha Burp */
+ "7sssssss",
+
+ /* 41 - Falling Vine */
+ "13ssssss",
+
+ /* 42 - Sproing! */
+ "29ssssss",
+
+ /* 43 - Prison/Dungeon Door */
+ "33ssssss",
+
+ /* 44 - Ape takes off mask */
+ "24ssssss",
+
+ /* 45 - Bark breaks */
+ "25ssssss",
+
+ /* 46 - Stone Click */
+ "136sssss",
+
+ /* 47 - Rocket Pack Zoom */
+ "1006ssss",
+
+ /* 48 - Cash Register */
+ "36ssssss",
+
+ /* 49 - Squeaky Toy */
+ "37ssssss",
+
+ /* 50 - Falling Chains */
+ "38ssssss",
+
+ /* 51 - Cloth Unravels */
+ "64ssssss",
+
+ /* 52 - Open Locker Door */
+ "48ssssss",
+
+ /* 53 - Close Locker Door */
+ "49ssssss",
+
+ /* 54 - Rub Pencil on Pad */
+ "50ssssss",
+
+ /* 55 - Open Safe */
+ "51ssssss",
+
+ /* 56 - Close Safe */
+ "52ssssss",
+
+ /* 57 - Push Chair */
+ "59ssssss",
+
+ /* 58 - Snake Hiss */
+ "83ssssss",
+
+ /* 59 - Oracle Rezzes Out */
+ "70ssssss",
+
+ /* 60 - Wall Crumbles */
+ "73Asssss",
+
+ /* 61 - Crypt Crumbles */
+ "76ssssss",
+
+ /* 62 - Creaking Stick */
+ "74Asssss",
+
+ /* 63 - Joe Sucked Up */
+ "80ssssss",
+
+ /* 64 - Slide Chute */
+ "114assss",
+
+ /* 65 - Press Button */
+ "1007ssss",
+
+ /* 66 - Snap Branch */
+ "101sssss",
+
+ /* 67 - Dino Horn */
+ "103sssss",
+
+ /* 68 - Tyre Screech */
+ "125sssss",
+
+ /* 69 - Chicken */
+ "126sssss",
+
+ /* 70 - Oil Splat */
+ "127sssss",
+
+ /* 71 - Punch */
+ "128sssss",
+
+ /* 72 - Body Hits Ground */
+ "129sssss",
+
+ /* 73 - Pick Hits Stone */
+ "71ssssss",
+
+ /* 74 - Stalactite Crumbles */
+ "119sssss",
+
+ /* 75 - *Drip */
+ "93ssssss",
+
+ /* 76 - Tic-Toc */
+ "42Bsssss",
+
+ /* 77 - Explosion */
+ "88ssssss",
+
+ /* 78 - Stone Door Grind 1 */
+ "1001ssss",
+
+ /* 79 - Stone Door Grind 2 */
+ "1002ssss",
+
+ /* 80 - *Fight Explosion */
+ "1000ssss",
+
+ /* 81 - Stone Grind (heavy) */
+ "1003ssss",
+
+ /* 82 - Stone Grind (light) */
+ "89ssssss",
+
+ /* 83 - Piranha Splash */
+ "5sssssss",
+
+ /* 84 - Cloth Slide 2 */
+ "1005ssss",
+
+ /* 85 - Temple Laser */
+ "87ssssss",
+
+ /* 86 - Dino Transformation */
+ "55Bsssss",
+
+ /* 87 - Experimental Laser */
+ "55ssssss",
+
+ /* 88 - Stone Grind (medium) */
+ "134sssss",
+
+ /* 89 - Weeping God Grind */
+ "94ssssss",
+
+ /* 90 - Alien Hum */
+ "95ssssss",
+
+ /* 91 - Alien Puzzle */
+ "103Assss",
+
+ /* 92 - Vacuum On */
+ "21ssssss",
+
+ /* 93 - Vacuum Off */
+ "21Csssss",
+
+ /* 94 - Elevator Starts */
+ "44ssssss",
+
+ /* 95 - Mummy Crumbles */
+ "68ssssss",
+
+ /* 96 - Temple Green Circle */
+ "60Bsssss",
+
+ /* 97 - Rattle Bars */
+ "115sssss",
+
+ /* 98 - Door Dissolves */
+ "56ssssss",
+
+ /* 99 - Altar Slides */
+ "85ssssss",
+
+ /* 100 - Light Torch */
+ "81ssssss",
+
+ /* 101 - Plane Flyby */
+ "1027ssss",
+
+ /* 102 - Plaster Loud */
+ "41Bsssss",
+
+ /* 103 - Sparky Bathtub */
+ "73ssssss",
+
+ /* 104 - Ape Rezzes Out */
+ "14ssssss",
+
+ /* 105 - Piranhas Swim */
+ "17ssssss",
+
+ /* 106 - *Gun Shot */
+ "1004ssss",
+
+ /* 107 - Pull Lever */
+ "1008ssss",
+
+ /* 108 - Wrong Code */
+ "1009ssss",
+
+ /* 109 - Correct Code */
+ "1010ssss",
+
+ /* 110 - Sizzle */
+ "1011ssss",
+
+ /* 111 - Money In Slot */
+ "1012ssss",
+
+ /* 112 - Lightning */
+ "1013ssss",
+
+ /* 113 - Machine Gun Fire */
+ "1014ssss",
+
+ /* 114 - Cage Descends */
+ "1015ssss",
+
+ /* 115 - Temple Chair Activates */
+ "1016ssss",
+
+ /* 116 - Robot Powers On */
+ "1017ssss",
+
+ /* 117 - Grow Big */
+ "1018ssss",
+
+ /* 118 - Eat Food */
+ "1019ssss",
+
+ /* 119 - Head Shrink */
+ "1020ssss",
+
+ /* 120 - Grinding Gears */
+ "84ssssss",
+
+ /* 121 - Wave Torch */
+ "1021ssss",
+
+ /* 122 - Snake Slithers Off */
+ "1022ssss",
+
+ /* 123 - Chair Splash */
+ "26ssssss",
+
+ /* 124 - Deflect Laser */
+ "60ssssss",
+
+ /* 125 - Zap Frank */
+ "1023ssss",
+
+ /* 126 - Frank Transforms */
+ "1024ssss",
+
+ /* 127 - Alarm Clock */
+ "1025ssss",
+
+ /* 128 - Puff */
+ "35ssssss",
+
+ /* 129 - Bite */
+ "1026ssss",
+
+ /* 130 - Plane Engine 1 */
+ "1028ssss",
+
+ /* 131 - Plane Engine 2 */
+ "1029ssss",
+
+ /* 132 - Boat In */
+ "1030ssss",
+
+ /* 133 - Boat Out */
+ "1031ssss",
+
+ /* 134 - Jaspar Eats */
+ "1032ssss",
+
+ /* 135 - Compy Scream 1 */
+ "1033ssss",
+
+ /* 136 - Compy Scream 2 */
+ "1034ssss"
+};
+
+const int16 Sound::_jungleList[] = { 15, 16, 17, 18, 7, 8, 9, 10, 11, 12, 13, 14, 0 };
+#endif
+
+} // End of namespace Queen
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Queen_Musicdata)
+_GSETPTR(Queen::Sound::_songDemo, GBVARS_MUSICDATASONGDEMO_INDEX, Queen::songData, GBVARS_QUEEN)
+_GSETPTR(Queen::Sound::_song, GBVARS_MUSICDATASONG_INDEX, Queen::songData, GBVARS_QUEEN)
+_GSETPTR(Queen::Sound::_tuneDemo, GBVARS_MUSICDATATUNEDEMO_INDEX, Queen::tuneData, GBVARS_QUEEN)
+_GSETPTR(Queen::Sound::_tune, GBVARS_MUSICDATATUNE_INDEX, Queen::tuneData, GBVARS_QUEEN)
+_GSETPTR(Queen::Sound::_sfxName, GBVARS_MUSICDATASFXNAME_INDEX, char, GBVARS_QUEEN)
+_GSETPTR(Queen::Sound::_jungleList, GBVARS_MUSICDATAJUNGLELIST_INDEX, int16, GBVARS_QUEEN)
+_GEND
+
+_GRELEASE(Queen_Musicdata)
+_GRELEASEPTR(GBVARS_MUSICDATASONGDEMO_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_MUSICDATASONG_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_MUSICDATATUNEDEMO_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_MUSICDATATUNE_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_MUSICDATASFXNAME_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_MUSICDATAJUNGLELIST_INDEX, GBVARS_QUEEN)
+_GEND
+
+#endif
diff --git a/engines/queen/queen.cpp b/engines/queen/queen.cpp
new file mode 100644
index 0000000000..e6c6f39269
--- /dev/null
+++ b/engines/queen/queen.cpp
@@ -0,0 +1,454 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/system.h"
+
+#include "queen/queen.h"
+#include "queen/bankman.h"
+#include "queen/command.h"
+#include "queen/cutaway.h"
+#include "queen/debug.h"
+#include "queen/display.h"
+#include "queen/graphics.h"
+#include "queen/grid.h"
+#include "queen/input.h"
+#include "queen/logic.h"
+#include "queen/music.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+#include "queen/talk.h"
+#include "queen/walk.h"
+
+#include "sound/mididrv.h"
+
+#ifdef _WIN32_WCE
+bool isSmartphone();
+#endif
+
+/* Flight of the Amazon Queen */
+static const GameSettings queen_setting[] = {
+ { "queen", "Flight of the Amazon Queen", 0 },
+ { "queen", "Flight of the Amazon Queen (Demo)", 0 },
+ { "queen", "Flight of the Amazon Queen (Interview)", 0 },
+ { 0, 0, 0 }
+};
+
+GameList Engine_QUEEN_gameList() {
+ GameList games;
+ const GameSettings *g = queen_setting;
+
+ while (g->gameid) {
+ games.push_back(*g);
+ g++;
+ }
+ return games;
+}
+
+GameSettings determineTarget(uint32 size) {
+ switch (size) {
+ case 3724538: //regular demo
+ case 3732177:
+ return queen_setting[1];
+ break;
+ case 1915913: //interview demo
+ return queen_setting[2];
+ break;
+ default: //non-demo
+ return queen_setting[0];
+ break;
+ }
+ return queen_setting[0];
+}
+
+DetectedGameList Engine_QUEEN_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+
+ // Iterate over all files in the given directory
+ for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (!file->isDirectory()) {
+ const char *gameName = file->displayName().c_str();
+
+ if (0 == scumm_stricmp("queen.1", gameName) || 0 == scumm_stricmp("queen.1c", gameName)) {
+ Common::File dataFile;
+ dataFile.open(file->path().c_str());
+ assert(dataFile.isOpen());
+
+ if (0 == scumm_stricmp("queen.1", gameName)) { //an unmodified file
+ detectedGames.push_back(determineTarget(dataFile.size()));
+ } else if (0 == scumm_stricmp("queen.1c", gameName)) { //oh joy, it's a rebuilt file
+ char header[9];
+ dataFile.read(header, 9);
+ if (0 == scumm_strnicmp("QTBL", header, 4)) { //check validity
+ uint8 version = 0; //default to full/normal version
+
+ if (0 == scumm_strnicmp("PE100", header + 4, 5)) //One of the 2 regular demos
+ version = 1;
+ if (0 == scumm_strnicmp("PEint", header + 4, 5)) //Interview demo
+ version = 2;
+
+ detectedGames.push_back(queen_setting[version]);
+ }
+ }
+
+ dataFile.close();
+ break;
+ }
+ }
+ }
+ return detectedGames;
+}
+
+Engine *Engine_QUEEN_create(GameDetector *detector, OSystem *syst) {
+ return new Queen::QueenEngine(detector, syst);
+}
+
+REGISTER_PLUGIN(QUEEN, "Flight of the Amazon Queen")
+
+namespace Queen {
+
+QueenEngine::QueenEngine(GameDetector *detector, OSystem *syst)
+ : Engine(syst), _debugger(0) {
+}
+
+QueenEngine::~QueenEngine() {
+ delete _bam;
+ delete _resource;
+ delete _bankMan;
+ delete _command;
+ delete _debugger;
+ delete _display;
+ delete _graphics;
+ delete _grid;
+ delete _input;
+ delete _logic;
+ delete _music;
+ delete _sound;
+ delete _walk;
+}
+
+void QueenEngine::registerDefaultSettings() {
+ ConfMan.registerDefault("music_mute", false);
+ ConfMan.registerDefault("sfx_mute", false);
+ ConfMan.registerDefault("talkspeed", Logic::DEFAULT_TALK_SPEED);
+ ConfMan.registerDefault("speech_mute", _resource->isDemo() || _resource->isInterview());
+ ConfMan.registerDefault("subtitles", true);
+}
+
+void QueenEngine::checkOptionSettings() {
+ // check talkspeed value
+ if (_talkSpeed < MIN_TEXT_SPEED) {
+ _talkSpeed = MIN_TEXT_SPEED;
+ } else if (_talkSpeed > MAX_TEXT_SPEED) {
+ _talkSpeed = MAX_TEXT_SPEED;
+ }
+
+ // ensure text is always on when voice is off
+ if (!_sound->speechOn()) {
+ _subtitles = true;
+ }
+
+ // demo and interview versions don't have speech at all
+ if (_sound->speechOn() && (_resource->isDemo() || _resource->isInterview())) {
+ _sound->speechToggle(false);
+ }
+}
+
+void QueenEngine::readOptionSettings() {
+ _music->setVolume(ConfMan.getInt("music_volume"));
+ _sound->musicToggle(!ConfMan.getBool("music_mute"));
+ _sound->sfxToggle(!ConfMan.getBool("sfx_mute"));
+ _talkSpeed = ConfMan.getInt("talkspeed");
+ _sound->speechToggle(!ConfMan.getBool("speech_mute"));
+ _subtitles = ConfMan.getBool("subtitles");
+ checkOptionSettings();
+}
+
+void QueenEngine::writeOptionSettings() {
+ ConfMan.set("music_volume", _music->volume());
+ ConfMan.set("music_mute", !_sound->musicOn());
+ ConfMan.set("sfx_mute", !_sound->sfxOn());
+ ConfMan.set("talkspeed", _talkSpeed);
+ ConfMan.set("speech_mute", !_sound->speechOn());
+ ConfMan.set("subtitles", _subtitles);
+ ConfMan.flushToDisk();
+}
+
+void QueenEngine::update(bool checkPlayerInput) {
+ if (_debugger->isAttached()) {
+ _debugger->onFrame();
+ }
+
+ _graphics->update(_logic->currentRoom());
+ _logic->update();
+
+ _input->delay();
+
+ if (!_resource->isInterview()) {
+ _display->palCustomScroll(_logic->currentRoom());
+ }
+ BobSlot *joe = _graphics->bob(0);
+ _display->update(joe->active, joe->x, joe->y);
+
+ _input->checkKeys();
+ if (_input->debugger()) {
+ _input->debuggerReset();
+ _debugger->attach();
+ }
+ if (canLoadOrSave()) {
+ if (_input->quickSave()) {
+ _input->quickSaveReset();
+ saveGameState(0, "Quicksave");
+ }
+ if (_input->quickLoad()) {
+ _input->quickLoadReset();
+ loadGameState(0);
+ }
+ if (shouldPerformAutoSave(_lastSaveTime)) {
+ saveGameState(AUTOSAVE_SLOT, "Autosave");
+ _lastSaveTime = _system->getMillis();
+ }
+ }
+ if (!_input->cutawayRunning()) {
+ if (checkPlayerInput) {
+ _command->updatePlayer();
+ }
+ if (_input->idleTime() >= Input::DELAY_SCREEN_BLANKER) {
+ _display->blankScreen();
+ }
+ }
+}
+
+bool QueenEngine::canLoadOrSave() const {
+ return !_input->cutawayRunning() && !(_resource->isDemo() || _resource->isInterview());
+}
+
+void QueenEngine::saveGameState(uint16 slot, const char *desc) {
+ debug(3, "Saving game to slot %d", slot);
+ char name[20];
+ makeGameStateName(slot, name);
+ Common::OutSaveFile *file = _saveFileMan->openForSaving(name);
+ if (file) {
+ // save data
+ byte *saveData = new byte[SAVESTATE_MAX_SIZE];
+ byte *p = saveData;
+ _bam->saveState(p);
+ _grid->saveState(p);
+ _logic->saveState(p);
+ _sound->saveState(p);
+ uint32 dataSize = p - saveData;
+ assert(dataSize < SAVESTATE_MAX_SIZE);
+
+ // write header
+ GameStateHeader header;
+ memset(&header, 0, sizeof(header));
+ file->writeUint32BE('SCVM');
+ header.version = TO_BE_32(SAVESTATE_CUR_VER);
+ header.flags = TO_BE_32(0);
+ header.dataSize = TO_BE_32(dataSize);
+ strncpy(header.description, desc, sizeof(header.description) - 1);
+ file->write(&header, sizeof(header));
+
+ // write save data
+ file->write(saveData, dataSize);
+ file->flush();
+
+ // check for errors
+ if (file->ioFailed()) {
+ warning("Can't write file '%s'. (Disk full?)", name);
+ }
+ delete[] saveData;
+ delete file;
+ } else {
+ warning("Can't create file '%s', game not saved", name);
+ }
+}
+
+void QueenEngine::loadGameState(uint16 slot) {
+ debug(3, "Loading game from slot %d", slot);
+ GameStateHeader header;
+ Common::InSaveFile *file = readGameStateHeader(slot, &header);
+ if (file && header.dataSize != 0) {
+ byte *saveData = new byte[header.dataSize];
+ byte *p = saveData;
+ if (file->read(saveData, header.dataSize) != header.dataSize) {
+ warning("Error reading savegame file");
+ } else {
+ _bam->loadState(header.version, p);
+ _grid->loadState(header.version, p);
+ _logic->loadState(header.version, p);
+ _sound->loadState(header.version, p);
+ if (header.dataSize != (uint32)(p - saveData)) {
+ warning("Corrupted savegame file");
+ } else {
+ _logic->setupRestoredGame();
+ }
+ }
+ delete[] saveData;
+ delete file;
+ }
+}
+
+Common::InSaveFile *QueenEngine::readGameStateHeader(uint16 slot, GameStateHeader *gsh) {
+ char name[20];
+ makeGameStateName(slot, name);
+ Common::InSaveFile *file = _saveFileMan->openForLoading(name);
+ if (file && file->readUint32BE() == 'SCVM') {
+ gsh->version = file->readUint32BE();
+ gsh->flags = file->readUint32BE();
+ gsh->dataSize = file->readUint32BE();
+ file->read(gsh->description, sizeof(gsh->description));
+ } else {
+ memset(gsh, 0, sizeof(GameStateHeader));
+ }
+ return file;
+}
+
+void QueenEngine::makeGameStateName(uint16 slot, char *buf) {
+ if (slot == AUTOSAVE_SLOT) {
+ strcpy(buf, "queen.asd");
+ } else {
+ sprintf(buf, "queen.s%02d", slot);
+ }
+}
+
+void QueenEngine::findGameStateDescriptions(char descriptions[100][32]) {
+ char filename[20];
+ makeGameStateName(0, filename);
+ filename[strlen(filename) - 2] = 0;
+ bool marks[SAVESTATE_MAX_NUM];
+ _saveFileMan->listSavefiles(filename, marks, SAVESTATE_MAX_NUM);
+ for (int i = 0; i < SAVESTATE_MAX_NUM; ++i) {
+ if (marks[i]) {
+ GameStateHeader header;
+ Common::InSaveFile *f = readGameStateHeader(i, &header);
+ strcpy(descriptions[i], header.description);
+ delete f;
+ }
+ }
+}
+
+void QueenEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+
+#ifdef _WIN32_WCE
+ if (isSmartphone())
+ return;
+#endif
+
+ // Unless an error -originated- within the debugger, spawn the
+ // debugger. Otherwise exit out normally.
+ if (_debugger && !_debugger->isAttached()) {
+ // (Print it again in case debugger segfaults)
+ printf("%s\n", buf2);
+ _debugger->attach(buf2);
+ _debugger->onFrame();
+ }
+}
+
+int QueenEngine::go() {
+ _logic->start();
+ if (ConfMan.hasKey("save_slot") && canLoadOrSave()) {
+ loadGameState(ConfMan.getInt("save_slot"));
+ }
+ _lastSaveTime = _system->getMillis();
+ _quit = false;
+ while (!_quit) {
+ if (_logic->newRoom() > 0) {
+ _logic->update();
+ _logic->oldRoom(_logic->currentRoom());
+ _logic->currentRoom(_logic->newRoom());
+ _logic->changeRoom();
+ _display->fullscreen(false);
+ if (_logic->currentRoom() == _logic->newRoom()) {
+ _logic->newRoom(0);
+ }
+ } else if (_logic->joeWalk() == JWM_EXECUTE) {
+ _logic->joeWalk(JWM_NORMAL);
+ _command->executeCurrentAction();
+ } else {
+ _logic->joeWalk(JWM_NORMAL);
+ update(true);
+ }
+ }
+ return 0;
+}
+
+int QueenEngine::init(GameDetector &detector) {
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ _system->initSize(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
+ _system->endGFXTransaction();
+
+ _bam = new BamScene(this);
+ _resource = new Resource();
+ _bankMan = new BankManager(_resource);
+ _command = new Command(this);
+ _debugger = new Debugger(this);
+ _display = new Display(this, _system);
+ _graphics = new Graphics(this);
+ _grid = new Grid(this);
+ _input = new Input(_resource->getLanguage(), _system);
+
+ if (_resource->isDemo()) {
+ _logic = new LogicDemo(this);
+ } else if (_resource->isInterview()) {
+ _logic = new LogicInterview(this);
+ } else {
+ _logic = new LogicGame(this);
+ }
+
+ if (!_mixer->isReady())
+ warning("Sound initialisation failed");
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ // Set mixer music volume to maximum, since music volume is regulated by MusicPlayer's MIDI messages
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, Audio::Mixer::kMaxMixerVolume);
+
+ int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
+ bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+
+ MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ if (native_mt32)
+ driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+
+ _music = new Music(driver, this);
+ _music->hasNativeMT32(native_mt32);
+
+ _sound = Sound::giveSound(_mixer, this, _resource->compression());
+ _walk = new Walk(this);
+
+ registerDefaultSettings();
+ readOptionSettings();
+
+ return 0;
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/queen.h b/engines/queen/queen.h
new file mode 100644
index 0000000000..91c657cf14
--- /dev/null
+++ b/engines/queen/queen.h
@@ -0,0 +1,164 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEEN_H
+#define QUEEN_H
+
+#include "base/engine.h"
+
+class GameDetector;
+namespace Common {
+ class InSaveFile;
+}
+
+#if defined(_WIN32_WCE) && (_WIN32_WCE <= 300)
+
+FORCEINLINE int16 READ_BE_INT16(const void *ptr) {
+ uint16 result;
+ char dummy[2];
+ result = READ_BE_UINT16(ptr);
+ strcpy(dummy, "x"); // Hello, I'm a drunk optimizer. Thanks for helping me.
+ return result;
+}
+
+#else
+
+#define READ_BE_INT16 READ_BE_UINT16
+
+#endif
+
+namespace Queen {
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct GameStateHeader {
+ uint32 version;
+ uint32 flags;
+ uint32 dataSize;
+ char description[32];
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+class BamScene;
+class BankManager;
+class Command;
+class Debugger;
+class Display;
+class Graphics;
+class Grid;
+class Input;
+class Logic;
+class Music;
+class Resource;
+class Sound;
+class Walk;
+
+class QueenEngine : public Engine {
+public:
+
+ QueenEngine(GameDetector *detector, OSystem *syst);
+ virtual ~QueenEngine();
+
+ BamScene *bam() const { return _bam; }
+ BankManager *bankMan() const { return _bankMan; }
+ Command *command() const { return _command; }
+ Debugger *debugger() const { return _debugger; }
+ Display *display() const { return _display; }
+ Graphics *graphics() const { return _graphics; }
+ Grid *grid() const { return _grid; }
+ Input *input() const { return _input; }
+ Logic *logic() const { return _logic; }
+ Music *music() const { return _music; }
+ Resource *resource() const { return _resource; }
+ Sound *sound() const { return _sound; }
+ Walk *walk() const { return _walk; }
+
+ Common::RandomSource randomizer;
+
+ void registerDefaultSettings();
+ void checkOptionSettings();
+ void readOptionSettings();
+ void writeOptionSettings();
+
+ int talkSpeed() const { return _talkSpeed; }
+ void talkSpeed(int speed) { _talkSpeed = speed; }
+ bool subtitles() const { return _subtitles; }
+ void subtitles(bool enable) { _subtitles = enable; }
+ void quitGame() { _quit = true; }
+
+ void update(bool checkPlayerInput = false);
+
+ bool canLoadOrSave() const;
+ void saveGameState(uint16 slot, const char *desc);
+ void loadGameState(uint16 slot);
+ void makeGameStateName(uint16 slot, char *buf);
+ void findGameStateDescriptions(char descriptions[100][32]);
+ Common::InSaveFile *readGameStateHeader(uint16 slot, GameStateHeader *gsh);
+
+ enum {
+ SAVESTATE_CUR_VER = 1,
+ SAVESTATE_MAX_NUM = 100,
+ SAVESTATE_MAX_SIZE = 30000,
+
+ AUTOSAVE_SLOT = 0xFF,
+
+ MIN_TEXT_SPEED = 4,
+ MAX_TEXT_SPEED = 100,
+ MAX_MUSIC_VOLUME = 255
+ };
+
+protected:
+
+ void errorString(const char *buf_input, char *buf_output);
+
+ int go();
+ int init(GameDetector &detector);
+
+
+ int _talkSpeed;
+ bool _subtitles;
+ bool _quit;
+ uint32 _lastSaveTime;
+
+ BamScene *_bam;
+ BankManager *_bankMan;
+ Command *_command;
+ Debugger *_debugger;
+ Display *_display;
+ Graphics *_graphics;
+ Grid *_grid;
+ Input *_input;
+ Logic *_logic;
+ Music *_music;
+ Resource *_resource;
+ Sound *_sound;
+ Walk *_walk;
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/resource.cpp b/engines/queen/resource.cpp
new file mode 100644
index 0000000000..51fa47b687
--- /dev/null
+++ b/engines/queen/resource.cpp
@@ -0,0 +1,287 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/resource.h"
+
+namespace Queen {
+
+#ifdef PALMOS_68K
+static ResourceEntry *_resourceTablePEM10;
+#endif
+
+const char *Resource::_tableFilename = "queen.tbl";
+
+const GameVersion Resource::_gameVersions[] = {
+ { "PEM10", 0x00000008, 22677657 },
+ { "CEM10", 0x0000584E, 190787021 },
+ { "PFM10", 0x0002CD93, 22157304 },
+ { "CFM10", 0x00032585, 186689095 },
+ { "PGM10", 0x00059ACA, 22240013 },
+ { "CGM10", 0x0005F2A7, 217648975 },
+ { "PIM10", 0x000866B1, 22461366 },
+ { "CIM10", 0x0008BEE2, 190795582 },
+ { "CSM10", 0x000B343C, 190730602 },
+ { "CHM10", 0x000DA981, 190705558 },
+ { "PE100", 0x00101EC6, 3724538 },
+ { "PE100", 0x00102B7F, 3732177 },
+ { "PEint", 0x00103838, 1915913 }
+};
+
+static int compareResourceEntry(const void *a, const void *b) {
+ const char *filename = (const char *)a;
+ const ResourceEntry *entry = (const ResourceEntry *)b;
+ return strcmp(filename, entry->filename);
+}
+
+Resource::Resource()
+ : _resourceEntries(0), _resourceTable(NULL) {
+ _resourceFile = new Common::File();
+ if (!findCompressedVersion() && !findNormalVersion())
+ error("Could not open resource file '%s'", "queen.1");
+ checkJASVersion();
+ debug(5, "Detected game version: %s, which has %d resource entries", _versionString, _resourceEntries);
+}
+
+Resource::~Resource() {
+ _resourceFile->close();
+ delete _resourceFile;
+
+ if (_resourceTable != _resourceTablePEM10)
+ delete[] _resourceTable;
+}
+
+ResourceEntry *Resource::resourceEntry(const char *filename) const {
+ assert(filename[0] && strlen(filename) < 14);
+
+ char entryName[14];
+ char *ptr = entryName;
+
+ strcpy(entryName, filename);
+ do
+ *ptr = toupper(*ptr);
+ while (*ptr++);
+
+ ResourceEntry *re = NULL;
+#ifndef PALMOS_MODE
+ re = (ResourceEntry *)bsearch(entryName, _resourceTable, _resourceEntries, sizeof(ResourceEntry), compareResourceEntry);
+#else
+ // PALMOS FIXME (?) : still doesn't work for me (????) use this instead
+ uint32 cur = 0;
+ do {
+ if (!strcmp(entryName, _resourceTable[cur].filename)) {
+ re = &_resourceTable[cur];
+ break;
+ }
+ } while (cur++ < _resourceEntries);
+#endif
+ return re;
+}
+
+uint8 *Resource::loadFile(const char *filename, uint32 skipBytes, uint32 *size, bool useMalloc) {
+ ResourceEntry *re = resourceEntry(filename);
+ assert(re != NULL);
+ uint32 sz = re->size - skipBytes;
+ if (size != NULL) {
+ *size = sz;
+ }
+
+ byte *dstBuf;
+ if (useMalloc) {
+ dstBuf = (byte *)malloc(sz);
+ } else {
+ dstBuf = new byte[sz];
+ }
+
+ _resourceFile->seek(re->offset + skipBytes);
+ _resourceFile->read(dstBuf, sz);
+ return dstBuf;
+}
+
+bool Resource::findNormalVersion() {
+ _resourceFile->open("queen.1");
+ if (!_resourceFile->isOpen()) {
+ return false;
+ }
+
+ _compression = COMPRESSION_NONE;
+
+ // detect game version based on resource file size ; we try to
+ // verify that it is indeed the version we think it is later on
+ const GameVersion *gameVersion = detectGameVersion(_resourceFile->size());
+ if (gameVersion == NULL)
+ error("Unknown/unsupported FOTAQ version");
+
+ strcpy(_versionString, gameVersion->versionString);
+ if (!readTableFile(gameVersion)) {
+ // check if it is the english floppy version, for which we have a hardcoded version of the table
+ if (!strcmp(gameVersion->versionString, _gameVersions[VER_ENG_FLOPPY].versionString)) {
+ _resourceEntries = 1076;
+ _resourceTable = _resourceTablePEM10;
+ } else {
+ error("Could not find tablefile '%s'", _tableFilename);
+ }
+ }
+ return true;
+}
+
+bool Resource::findCompressedVersion() {
+ _resourceFile->open("queen.1c");
+ if (!_resourceFile->isOpen()) {
+ return false;
+ }
+ readTableCompResource();
+ return true;
+}
+
+void Resource::checkJASVersion() {
+ ResourceEntry *re = resourceEntry("QUEEN.JAS");
+ assert(re != NULL);
+ uint32 offset = re->offset;
+ if (isDemo())
+ offset += JAS_VERSION_OFFSET_DEMO;
+ else if (isInterview())
+ offset += JAS_VERSION_OFFSET_INTV;
+ else
+ offset += JAS_VERSION_OFFSET_PC;
+ _resourceFile->seek(offset);
+
+ char versionStr[6];
+ _resourceFile->read(versionStr, 6);
+ if (strcmp(_versionString, versionStr))
+ error("Verifying game version failed! (expected: '%s', found: '%s')", _versionString, versionStr);
+}
+
+Language Resource::getLanguage() const {
+ switch (_versionString[1]) {
+ case 'E':
+ return ENGLISH;
+ case 'G':
+ return GERMAN;
+ case 'F':
+ return FRENCH;
+ case 'I':
+ return ITALIAN;
+ case 'S':
+ return SPANISH;
+ case 'H':
+ return HEBREW;
+ default:
+ return ENGLISH;
+ }
+}
+
+bool Resource::readTableFile(const GameVersion *gameVersion) {
+ Common::File tableFile;
+ tableFile.open(_tableFilename);
+ if (tableFile.isOpen() && tableFile.readUint32BE() == 'QTBL') {
+ if (tableFile.readUint32BE() != CURRENT_TBL_VERSION)
+ warning("Incorrect version of queen.tbl, please update it");
+ tableFile.seek(gameVersion->tableOffset);
+ readTableEntries(&tableFile);
+ return true;
+ }
+ return false;
+}
+
+void Resource::readTableCompResource() {
+ if (_resourceFile->readUint32BE() != 'QTBL')
+ error("Invalid table header");
+
+ _resourceFile->read(_versionString, 6);
+ _resourceFile->readByte(); // obsolete
+ _resourceFile->readByte(); // obsolete
+ _compression = _resourceFile->readByte();
+
+ readTableEntries(_resourceFile);
+}
+
+void Resource::readTableEntries(Common::File *file) {
+ _resourceEntries = file->readUint16BE();
+ _resourceTable = new ResourceEntry[_resourceEntries];
+ for (uint16 i = 0; i < _resourceEntries; ++i) {
+ ResourceEntry *re = &_resourceTable[i];
+ file->read(re->filename, 12);
+ re->filename[12] = '\0';
+ re->bundle = file->readByte();
+ re->offset = file->readUint32BE();
+ re->size = file->readUint32BE();
+ }
+}
+
+const GameVersion *Resource::detectGameVersion(uint32 size) const {
+ const GameVersion *pgv = _gameVersions;
+ for (int i = 0; i < VER_COUNT; ++i, ++pgv) {
+ if (pgv->dataFileSize == size) {
+ return pgv;
+ }
+ }
+ return NULL;
+}
+
+Common::File *Resource::giveCompressedSound(const char *filename, uint32 *size) {
+ assert(strstr(filename, ".SB"));
+ Common::File *f = NULL;
+ ResourceEntry *re = resourceEntry(filename);
+ if (re) {
+ if (size != NULL) {
+ *size = re->size;
+ }
+ _resourceFile->seek(re->offset);
+ f = _resourceFile;
+ }
+ return f;
+}
+
+LineReader::LineReader(char *buffer, uint32 bufsize) : _buffer(buffer), _bufSize(bufsize), _current(0) {
+}
+
+LineReader::~LineReader() {
+ delete[] _buffer;
+}
+
+char *LineReader::nextLine() {
+ char *startOfLine = _buffer + _current;
+ char *curPos = startOfLine;
+ while (curPos < _buffer + _bufSize && *curPos++ != 0xd) ;
+ *(curPos - 1) = '\0'; // '\r'
+ if (curPos < _buffer + _bufSize) {
+ *curPos = '\0'; // '\n'
+ _current = (curPos - _buffer) + 1;
+ }
+ return startOfLine;
+}
+
+} // End of namespace Queen
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Queen_Restables)
+_GSETPTR(Queen::_resourceTablePEM10, GBVARS_RESOURCETABLEPM10_INDEX, Queen::ResourceEntry, GBVARS_QUEEN)
+_GEND
+
+_GRELEASE(Queen_Restables)
+_GRELEASEPTR(GBVARS_RESOURCETABLEPM10_INDEX, GBVARS_QUEEN)
+_GEND
+
+#endif
diff --git a/engines/queen/resource.h b/engines/queen/resource.h
new file mode 100644
index 0000000000..e205490eca
--- /dev/null
+++ b/engines/queen/resource.h
@@ -0,0 +1,169 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENRESOURCE_H
+#define QUEENRESOURCE_H
+
+#include "common/file.h"
+#include "common/util.h"
+#include "queen/defs.h"
+
+namespace Queen {
+
+struct ResourceEntry {
+ char filename[13];
+ uint8 bundle;
+ uint32 offset;
+ uint32 size;
+};
+
+struct GameVersion {
+ char versionString[6];
+ uint32 tableOffset;
+ uint32 dataFileSize;
+};
+
+class LineReader {
+public:
+
+ LineReader(char *buffer, uint32 bufsize);
+ ~LineReader();
+ char *nextLine();
+
+private:
+
+ char *_buffer;
+ uint32 _bufSize;
+ int _current;
+};
+
+class Resource {
+public:
+
+ Resource();
+ ~Resource();
+
+ //! loads the specified from the resource file
+ uint8 *loadFile(const char *filename, uint32 skipBytes = 0, uint32 *size = NULL, bool useMalloc = false);
+
+ //! returns true if the file is present in the resource
+ bool fileExists(const char *filename) const { return resourceEntry(filename) != NULL; }
+
+ //! returns a reference to a sound file
+ Common::File *giveCompressedSound(const char *filename, uint32 *size);
+
+ bool isDemo() const { return !strcmp(_versionString, "PE100"); }
+ bool isInterview() const { return !strcmp(_versionString, "PEint"); }
+ bool isFloppy() const { return _versionString[0] == 'P'; }
+ bool isCD() const { return _versionString[0] == 'C'; }
+
+ //! returns compression type for audio files
+ uint8 compression() const { return _compression; }
+
+ //! returns JAS version string (contains language, platform and version information)
+ const char *JASVersion() const { return _versionString; }
+
+ //! returns language of the game
+ Language getLanguage() const;
+
+ enum Version {
+ VER_ENG_FLOPPY = 0,
+ VER_ENG_TALKIE = 1,
+ VER_FRE_FLOPPY = 2,
+ VER_FRE_TALKIE = 3,
+ VER_GER_FLOPPY = 4,
+ VER_GER_TALKIE = 5,
+ VER_ITA_FLOPPY = 6,
+ VER_ITA_TALKIE = 7,
+ VER_SPA_TALKIE = 8,
+ VER_HEB_TALKIE = 9,
+ VER_DEMO_PCGAMES = 10,
+ VER_DEMO = 11,
+ VER_INTERVIEW = 12,
+
+ VER_COUNT = 13
+ };
+
+ enum {
+ CURRENT_TBL_VERSION = 1
+ };
+
+ enum {
+ JAS_VERSION_OFFSET_DEMO = 0x119A8,
+ JAS_VERSION_OFFSET_INTV = 0xCF8,
+ JAS_VERSION_OFFSET_PC = 0x12484
+ };
+
+protected:
+
+ Common::File *_resourceFile;
+
+ //! compression type for audio files
+ uint8 _compression;
+
+ //! JAS version string of the game
+ char _versionString[6];
+
+ //! number of entries in resource table
+ uint32 _resourceEntries;
+
+ ResourceEntry *_resourceTable;
+
+ //! look for a normal queen version (ie. queen.1)
+ bool findNormalVersion();
+
+ //! look for a compressed/rebuilt queen version (ie. queen.1c)
+ bool findCompressedVersion();
+
+ //! verify the version of the selected game
+ void checkJASVersion();
+
+ //! returns a reference to the ReseourceEntry for the specified filename
+ ResourceEntry *resourceEntry(const char *filename) const;
+
+ //! extarct the resource table for the specified game version
+ bool readTableFile(const GameVersion *gameVersion);
+
+ //! reads the resource table from a rebuilt datafile (ie. queen.1c)
+ void readTableCompResource();
+
+ //! read the resource table from the specified file
+ void readTableEntries(Common::File *file);
+
+ //! detect game version based on queen.1 datafile size
+ const GameVersion *detectGameVersion(uint32 size) const;
+
+ //! resource table filename (queen.tbl)
+ static const char *_tableFilename;
+
+ //! known FOTAQ versions
+ static const GameVersion _gameVersions[];
+
+#ifndef PALMOS_68K
+ //! resource table for english floppy version
+ static ResourceEntry _resourceTablePEM10[];
+#endif
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/restables.cpp b/engines/queen/restables.cpp
new file mode 100644
index 0000000000..8cebaccf49
--- /dev/null
+++ b/engines/queen/restables.cpp
@@ -0,0 +1,1108 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "queen/resource.h"
+
+namespace Queen {
+
+#ifndef PALMOS_68K
+//English Floppy Version
+ResourceEntry Resource::_resourceTablePEM10[] = {
+ { "1000SSSS.SB", 1, 0x00000000, 0x000027fe },
+ { "1001SSSS.SB", 1, 0x000027fe, 0x00007af8 },
+ { "1002SSSS.SB", 1, 0x0000a2f6, 0x000049e2 },
+ { "1003SSSS.SB", 1, 0x0000ecd8, 0x00001d42 },
+ { "1004SSSS.SB", 1, 0x00010a1a, 0x00001a1c },
+ { "1005SSSS.SB", 1, 0x00012436, 0x00001a8a },
+ { "1006SSSS.SB", 1, 0x00013ec0, 0x00009d41 },
+ { "1007SSSS.SB", 1, 0x0001dc01, 0x00001372 },
+ { "1008SSSS.SB", 1, 0x0001ef73, 0x00002e4a },
+ { "1009SSSS.SB", 1, 0x00021dbd, 0x0000220c },
+ { "1010SSSS.SB", 1, 0x00023fc9, 0x00003b1e },
+ { "1011SSSS.SB", 1, 0x00027ae7, 0x00000dd8 },
+ { "1012SSSS.SB", 1, 0x000288bf, 0x0000444c },
+ { "1013SSSS.SB", 1, 0x0002cd0b, 0x00007e66 },
+ { "1014SSSS.SB", 1, 0x00034b71, 0x00006e14 },
+ { "1015SSSS.SB", 1, 0x0003b985, 0x0000760c },
+ { "1016SSSS.SB", 1, 0x00042f91, 0x00004f8b },
+ { "1017SSSS.SB", 1, 0x00047f1c, 0x00004848 },
+ { "1018SSSS.SB", 1, 0x0004c764, 0x00007e94 },
+ { "1019SSSS.SB", 1, 0x000545f8, 0x00003a70 },
+ { "101SSSSS.SB", 1, 0x00058068, 0x0000191a },
+ { "1020SSSS.SB", 1, 0x00059982, 0x00004d90 },
+ { "1021SSSS.SB", 1, 0x0005e712, 0x00003dcc },
+ { "1023SSSS.SB", 1, 0x000624de, 0x00003d7f },
+ { "1024SSSS.SB", 1, 0x0006625d, 0x00004f8a },
+ { "1025SSSS.SB", 1, 0x0006b1e7, 0x00006488 },
+ { "1026SSSS.SB", 1, 0x0007166f, 0x00002022 },
+ { "1027SSSS.SB", 1, 0x00073691, 0x00009e76 },
+ { "1028SSSS.SB", 1, 0x0007d507, 0x0001b37c },
+ { "1029SSSS.SB", 1, 0x00098883, 0x0000ce2c },
+ { "1030SSSS.SB", 1, 0x000a56af, 0x0001e6e4 },
+ { "1031SSSS.SB", 1, 0x000c3d93, 0x00011532 },
+ { "1032SSSS.SB", 1, 0x000d52c5, 0x000034d4 },
+ { "1033SSSS.SB", 1, 0x000d8799, 0x00002de6 },
+ { "1034SSSS.SB", 1, 0x000db57f, 0x000099a1 },
+ { "103ASSSS.SB", 1, 0x000e4f20, 0x00005040 },
+ { "103SSSSS.SB", 1, 0x000e9f60, 0x00006a94 },
+ { "105ASSSS.SB", 1, 0x000f09f4, 0x00001d04 },
+ { "105SSSSS.SB", 1, 0x000f26f8, 0x000024de },
+ { "106SSSSS.SB", 1, 0x000f4bd6, 0x00000ac4 },
+ { "109SSSSS.SB", 1, 0x000f569a, 0x00000d96 },
+ { "10SSSSSS.SB", 1, 0x000f6430, 0x000013f6 },
+ { "110SSSSS.SB", 1, 0x000f7826, 0x00000c96 },
+ { "111SSSSS.SB", 1, 0x000f84bc, 0x00000f72 },
+ { "112SSSSS.SB", 1, 0x000f942e, 0x00000f1a },
+ { "113SSSSS.SB", 1, 0x000fa348, 0x00003f5e },
+ { "114ASSSS.SB", 1, 0x000fe2a6, 0x00001e54 },
+ { "115SSSSS.SB", 1, 0x001000fa, 0x00002580 },
+ { "116BSSSS.SB", 1, 0x0010267a, 0x00002350 },
+ { "119SSSSS.SB", 1, 0x001049ca, 0x00003808 },
+ { "11SSSSSS.SB", 1, 0x001081d2, 0x0000157e },
+ { "120SSSSS.SB", 1, 0x00109750, 0x00002be4 },
+ { "121SSSSS.SB", 1, 0x0010c334, 0x00001340 },
+ { "122SSSSS.SB", 1, 0x0010d674, 0x0000173e },
+ { "123ASSSS.SB", 1, 0x0010edb2, 0x00009e9d },
+ { "123BSSSS.SB", 1, 0x00118c4f, 0x0000f613 },
+ { "125SSSSS.SB", 1, 0x00128262, 0x0000e628 },
+ { "126SSSSS.SB", 1, 0x0013688a, 0x0000372e },
+ { "127SSSSS.SB", 1, 0x00139fb8, 0x0000ff1e },
+ { "128SSSSS.SB", 1, 0x00149ed6, 0x00000e34 },
+ { "129SSSSS.SB", 1, 0x0014ad0a, 0x00000b02 },
+ { "130SSSSS.SB", 1, 0x0014b80c, 0x00000a10 },
+ { "131ASSSS.SB", 1, 0x0014c21c, 0x000022d4 },
+ { "132CSSSS.SB", 1, 0x0014e4f0, 0x000094c3 },
+ { "132SSSSS.SB", 1, 0x001579b3, 0x00006183 },
+ { "133SSSSS.SB", 1, 0x0015db36, 0x00006a13 },
+ { "134SSSSS.SB", 1, 0x00164549, 0x00004ea3 },
+ { "135SSSSS.SB", 1, 0x001693ec, 0x000017c8 },
+ { "136SSSSS.SB", 1, 0x0016abb4, 0x000004a0 },
+ { "137ASSSS.SB", 1, 0x0016b054, 0x00001826 },
+ { "137BSSSS.SB", 1, 0x0016c87a, 0x00001d46 },
+ { "138BSSSS.SB", 1, 0x0016e5c0, 0x000065f9 },
+ { "138SSSSS.SB", 1, 0x00174bb9, 0x000053c3 },
+ { "13SSSSSS.SB", 1, 0x00179f7c, 0x00001f32 },
+ { "14SSSSSS.SB", 1, 0x0017beae, 0x00004921 },
+ { "15SSSSSS.SB", 1, 0x001807cf, 0x000007aa },
+ { "17SSSSSS.SB", 1, 0x00180f79, 0x00005080 },
+ { "18SSSSSS.SB", 1, 0x00185ff9, 0x000015e2 },
+ { "21CSSSSS.SB", 1, 0x001875db, 0x000048ec },
+ { "21SSSSSS.SB", 1, 0x0018bec7, 0x0000bb40 },
+ { "24SSSSSS.SB", 1, 0x00197a07, 0x000023fc },
+ { "25SSSSSS.SB", 1, 0x00199e03, 0x000016d8 },
+ { "26SSSSSS.SB", 1, 0x0019b4db, 0x000049dd },
+ { "27SSSSSS.SB", 1, 0x0019feb8, 0x000010a0 },
+ { "29SSSSSS.SB", 1, 0x001a0f58, 0x00004e50 },
+ { "33SSSSSS.SB", 1, 0x001a5da8, 0x00005218 },
+ { "35SSSSSS.SB", 1, 0x001aafc0, 0x00002c6c },
+ { "36SSSSSS.SB", 1, 0x001adc2c, 0x000031d2 },
+ { "37SSSSSS.SB", 1, 0x001b0dfe, 0x0000197a },
+ { "38SSSSSS.SB", 1, 0x001b2778, 0x00002162 },
+ { "3SSSSSSS.SB", 1, 0x001b48da, 0x00000d04 },
+ { "40SSSSSS.SB", 1, 0x001b55de, 0x00000268 },
+ { "41BSSSSS.SB", 1, 0x001b5846, 0x000005fe },
+ { "42BSSSSS.SB", 1, 0x001b5e44, 0x00000308 },
+ { "44SSSSSS.SB", 1, 0x001b614c, 0x000052c0 },
+ { "48SSSSSS.SB", 1, 0x001bb40c, 0x000010a8 },
+ { "49SSSSSS.SB", 1, 0x001bc4b4, 0x00001dd4 },
+ { "4SSSSSSS.SB", 1, 0x001be288, 0x000009d2 },
+ { "50SSSSSS.SB", 1, 0x001bec5a, 0x00003f8a },
+ { "51SSSSSS.SB", 1, 0x001c2be4, 0x000015da },
+ { "52SSSSSS.SB", 1, 0x001c41be, 0x0000125a },
+ { "55BSSSSS.SB", 1, 0x001c5418, 0x00003d7f },
+ { "55SSSSSS.SB", 1, 0x001c9197, 0x00004508 },
+ { "56SSSSSS.SB", 1, 0x001cd69f, 0x0000520b },
+ { "58SSSSSS.SB", 1, 0x001d28aa, 0x00003399 },
+ { "59SSSSSS.SB", 1, 0x001d5c43, 0x000032b8 },
+ { "5SSSSSSS.SB", 1, 0x001d8efb, 0x00004ae9 },
+ { "60BSSSSS.SB", 1, 0x001dd9e4, 0x000050f2 },
+ { "60SSSSSS.SB", 1, 0x001e2ad6, 0x00003c57 },
+ { "61SSSSSS.SB", 1, 0x001e672d, 0x0000513d },
+ { "64SSSSSS.SB", 1, 0x001eb86a, 0x00005a98 },
+ { "68SSSSSS.SB", 1, 0x001f1302, 0x00003e85 },
+ { "6SSSSSSS.SB", 1, 0x001f5187, 0x00001650 },
+ { "70SSSSSS.SB", 1, 0x001f67d7, 0x00009645 },
+ { "71SSSSSS.SB", 1, 0x001ffe1c, 0x00000fa3 },
+ { "73ASSSSS.SB", 1, 0x00200dbf, 0x00008812 },
+ { "73SSSSSS.SB", 1, 0x002095d1, 0x00006706 },
+ { "74ASSSSS.SB", 1, 0x0020fcd7, 0x000029b2 },
+ { "75SSSSSS.SB", 1, 0x00212689, 0x00000920 },
+ { "76SSSSSS.SB", 1, 0x00212fa9, 0x00004ef8 },
+ { "77SSSSSS.SB", 1, 0x00217ea1, 0x00003358 },
+ { "79SSSSSS.SB", 1, 0x0021b1f9, 0x000016a8 },
+ { "7SSSSSSS.SB", 1, 0x0021c8a1, 0x000010d4 },
+ { "80SSSSSS.SB", 1, 0x0021d975, 0x0000441e },
+ { "81SSSSSS.SB", 1, 0x00221d93, 0x0000517f },
+ { "83SSSSSS.SB", 1, 0x00226f12, 0x000043e3 },
+ { "84SSSSSS.SB", 1, 0x0022b2f5, 0x00003baa },
+ { "85SSSSSS.SB", 1, 0x0022ee9f, 0x00004470 },
+ { "87SSSSSS.SB", 1, 0x0023330f, 0x00005113 },
+ { "88SSSSSS.SB", 1, 0x00238422, 0x00005275 },
+ { "89SSSSSS.SB", 1, 0x0023d697, 0x00001a69 },
+ { "93SSSSSS.SB", 1, 0x0023f100, 0x00000470 },
+ { "94SSSSSS.SB", 1, 0x0023f570, 0x000051e4 },
+ { "95SSSSSS.SB", 1, 0x00244754, 0x0000343b },
+ { "96SSSSSS.SB", 1, 0x00247b8f, 0x00005110 },
+ { "97SSSSSS.SB", 1, 0x0024cc9f, 0x00004972 },
+ { "AMAZON.ACT", 1, 0x00251611, 0x0000a97a },
+ { "AND1.DOG", 1, 0x0025bf8b, 0x000009ca },
+ { "ANDERSON.ACT", 1, 0x0025c955, 0x00007c0a },
+ { "ANDSON2.ACT", 1, 0x0026455f, 0x00007c0a },
+ { "ANDSON_E.ACT", 1, 0x0026c169, 0x00003c42 },
+ { "APE.ACT", 1, 0x0026fdab, 0x000084cd },
+ { "APE.DOG", 1, 0x00278278, 0x000011d4 },
+ { "APE2.DOG", 1, 0x0027944c, 0x000008da },
+ { "APE3.DOG", 1, 0x00279d26, 0x00000788 },
+ { "AQ.RL", 1, 0x0027a4ae, 0x00063f3a },
+ { "AQ8.RL", 1, 0x002de3e8, 0x000167f8 },
+ { "AQB2.MUS", 1, 0x002f4be0, 0x00039972 },
+ { "AQBANK.MUS", 1, 0x0032e552, 0x0004fe89 },
+ { "AQBANK.RL", 1, 0x0037e3db, 0x000059e9 },
+ { "AZURA_E.ACT", 1, 0x00383dc4, 0x0000425a },
+ { "AZURA_H.ACT", 1, 0x0038801e, 0x00008a7a },
+ { "AZURA_H.BBK", 1, 0x00390a98, 0x0001a712 },
+ { "AZURA_H.PCX", 1, 0x003ab1aa, 0x0000accb },
+ { "B1.BBK", 1, 0x003b5e75, 0x000051fb },
+ { "B1.LUM", 1, 0x003bb070, 0x00000018 },
+ { "B1.MSK", 1, 0x003bb088, 0x00001f40 },
+ { "B1.PCX", 1, 0x003bcfc8, 0x000162a8 },
+ { "B2.BBK", 1, 0x003d3270, 0x000034b1 },
+ { "B2.LUM", 1, 0x003d6721, 0x00000018 },
+ { "B2.MSK", 1, 0x003d6739, 0x00001f40 },
+ { "B2.PCX", 1, 0x003d8679, 0x0000a521 },
+ { "BAT.SAM", 1, 0x003e2b9a, 0x00009d9a },
+ { "BEETLE.ACT", 1, 0x003ec934, 0x00001e42 },
+ { "BIGAM.ACT", 1, 0x003ee776, 0x00002d12 },
+ { "BLANK000.SB", 1, 0x003f1488, 0x00000076 },
+ { "BLUEP.CUT", 1, 0x003f14fe, 0x00000148 },
+ { "BOB1.DOG", 1, 0x003f1646, 0x000010bc },
+ { "BOB2.DOG", 1, 0x003f2702, 0x00000e5a },
+ { "BOB3.DOG", 1, 0x003f355c, 0x00000df8 },
+ { "BOB4.DOG", 1, 0x003f4354, 0x00000dea },
+ { "BOB5.DOG", 1, 0x003f513e, 0x00000bf0 },
+ { "BUD.ACT", 1, 0x003f5d2e, 0x00006582 },
+ { "BUD1.DOG", 1, 0x003fc2b0, 0x0000129c },
+ { "BUD2.DOG", 1, 0x003fd54c, 0x00000c0e },
+ { "C1.BBK", 1, 0x003fe15a, 0x000026fd },
+ { "C1.LUM", 1, 0x00400857, 0x00000018 },
+ { "C1.MSK", 1, 0x0040086f, 0x00001f40 },
+ { "C1.PCX", 1, 0x004027af, 0x00004888 },
+ { "C10.BBK", 1, 0x00407037, 0x0003949f },
+ { "C10.PCX", 1, 0x004404d6, 0x0000e6fd },
+ { "C100A.CUT", 1, 0x0044ebd3, 0x000003be },
+ { "C100B.CUT", 1, 0x0044ef91, 0x00000346 },
+ { "C100C.CUT", 1, 0x0044f2d7, 0x0000019a },
+ { "C100D.CUT", 1, 0x0044f471, 0x00000774 },
+ { "C101A.CUT", 1, 0x0044fbe5, 0x0000021a },
+ { "C101B.CUT", 1, 0x0044fdff, 0x000000e6 },
+ { "C101C.CUT", 1, 0x0044fee5, 0x00000200 },
+ { "C101D.CUT", 1, 0x004500e5, 0x00000144 },
+ { "C101E.CUT", 1, 0x00450229, 0x00000144 },
+ { "C102A.CUT", 1, 0x0045036d, 0x0000040e },
+ { "C102B.CUT", 1, 0x0045077b, 0x0000040e },
+ { "C102C.CUT", 1, 0x00450b89, 0x0000047e },
+ { "C102D.CUT", 1, 0x00451007, 0x000003b8 },
+ { "C102E.CUT", 1, 0x004513bf, 0x0000037e },
+ { "C103A.CUT", 1, 0x0045173d, 0x000000b4 },
+ { "C103B.CUT", 1, 0x004517f1, 0x00000104 },
+ { "C103C.CUT", 1, 0x004518f5, 0x00000090 },
+ { "C103D.CUT", 1, 0x00451985, 0x00000070 },
+ { "C103E.CUT", 1, 0x004519f5, 0x000000da },
+ { "C103F.CUT", 1, 0x00451acf, 0x0000084a },
+ { "C103G.CUT", 1, 0x00452319, 0x0000068e },
+ { "C103H.CUT", 1, 0x004529a7, 0x000003be },
+ { "C103I.CUT", 1, 0x00452d65, 0x000001f2 },
+ { "C103J.CUT", 1, 0x00452f57, 0x0000041c },
+ { "C103K.CUT", 1, 0x00453373, 0x0000016a },
+ { "C103L.CUT", 1, 0x004534dd, 0x00000458 },
+ { "C11.BBK", 1, 0x00453935, 0x0000bf94 },
+ { "C11.PCX", 1, 0x0045f8c9, 0x0000c01a },
+ { "C11A.CUT", 1, 0x0046b8e3, 0x00000346 },
+ { "C12A.CUT", 1, 0x0046bc29, 0x000004d0 },
+ { "C13A.CUT", 1, 0x0046c0f9, 0x00000164 },
+ { "C13B.CUT", 1, 0x0046c25d, 0x0000016c },
+ { "C13C.CUT", 1, 0x0046c3c9, 0x000001fa },
+ { "C13D.CUT", 1, 0x0046c5c3, 0x00000174 },
+ { "C13E.CUT", 1, 0x0046c737, 0x000001d0 },
+ { "C13F.CUT", 1, 0x0046c907, 0x000002d6 },
+ { "C13G.CUT", 1, 0x0046cbdd, 0x00000152 },
+ { "C13H.CUT", 1, 0x0046cd2f, 0x00000116 },
+ { "C13I.CUT", 1, 0x0046ce45, 0x00000080 },
+ { "C13J.CUT", 1, 0x0046cec5, 0x00000272 },
+ { "C13K.CUT", 1, 0x0046d137, 0x00000192 },
+ { "C13L.CUT", 1, 0x0046d2c9, 0x000001dc },
+ { "C13M.CUT", 1, 0x0046d4a5, 0x00000468 },
+ { "C13N.CUT", 1, 0x0046d90d, 0x0000015e },
+ { "C14A.CUT", 1, 0x0046da6b, 0x000002fe },
+ { "C14B.CUT", 1, 0x0046dd69, 0x00000126 },
+ { "C14C.CUT", 1, 0x0046de8f, 0x00000150 },
+ { "C14D.CUT", 1, 0x0046dfdf, 0x00000108 },
+ { "C15A.CUT", 1, 0x0046e0e7, 0x00000264 },
+ { "C15B.CUT", 1, 0x0046e34b, 0x00000136 },
+ { "C15C.CUT", 1, 0x0046e481, 0x00000380 },
+ { "C15D.CUT", 1, 0x0046e801, 0x000002d2 },
+ { "C15E.CUT", 1, 0x0046ead3, 0x000000f4 },
+ { "C16A.CUT", 1, 0x0046ebc7, 0x000005d6 },
+ { "C16B.CUT", 1, 0x0046f19d, 0x0000057e },
+ { "C17A.CUT", 1, 0x0046f71b, 0x00000156 },
+ { "C18A.CUT", 1, 0x0046f871, 0x00000a60 },
+ { "C18B.CUT", 1, 0x004702d1, 0x000012b8 },
+ { "C18C.CUT", 1, 0x00471589, 0x00001202 },
+ { "C18D.CUT", 1, 0x0047278b, 0x000009fa },
+ { "C18E.CUT", 1, 0x00473185, 0x000009e4 },
+ { "C19A.CUT", 1, 0x00473b69, 0x00001076 },
+ { "C19B.CUT", 1, 0x00474bdf, 0x0000015c },
+ { "C1A.CUT", 1, 0x00474d3b, 0x0000034e },
+ { "C2.BBK", 1, 0x00475089, 0x00005908 },
+ { "C2.LUM", 1, 0x0047a991, 0x00000018 },
+ { "C2.MSK", 1, 0x0047a9a9, 0x00001f40 },
+ { "C2.PCX", 1, 0x0047c8e9, 0x00007f3a },
+ { "C2.SAM", 1, 0x00484823, 0x000147ac },
+ { "C20A.CUT", 1, 0x00498fcf, 0x00000500 },
+ { "C20B.CUT", 1, 0x004994cf, 0x00000168 },
+ { "C20C.CUT", 1, 0x00499637, 0x00000170 },
+ { "C20D.CUT", 1, 0x004997a7, 0x00000388 },
+ { "C20E.CUT", 1, 0x00499b2f, 0x00000394 },
+ { "C20F.CUT", 1, 0x00499ec3, 0x0000073a },
+ { "C20G.CUT", 1, 0x0049a5fd, 0x0000029c },
+ { "C21A.CUT", 1, 0x0049a899, 0x000000da },
+ { "C21B.CUT", 1, 0x0049a973, 0x000000ee },
+ { "C21C.CUT", 1, 0x0049aa61, 0x0000025e },
+ { "C21D.CUT", 1, 0x0049acbf, 0x000002ec },
+ { "C21E.CUT", 1, 0x0049afab, 0x00000602 },
+ { "C21F.CUT", 1, 0x0049b5ad, 0x000003e2 },
+ { "C21G.CUT", 1, 0x0049b98f, 0x00000136 },
+ { "C21H.CUT", 1, 0x0049bac5, 0x00000172 },
+ { "C21I.CUT", 1, 0x0049bc37, 0x0000024c },
+ { "C21J.CUT", 1, 0x0049be83, 0x00000122 },
+ { "C21K.CUT", 1, 0x0049bfa5, 0x00000584 },
+ { "C21L.CUT", 1, 0x0049c529, 0x00000522 },
+ { "C21M.CUT", 1, 0x0049ca4b, 0x000001ea },
+ { "C21N.CUT", 1, 0x0049cc35, 0x00000610 },
+ { "C21O.CUT", 1, 0x0049d245, 0x000000f4 },
+ { "C21P.CUT", 1, 0x0049d339, 0x00000150 },
+ { "C21Q.CUT", 1, 0x0049d489, 0x000002ec },
+ { "C21R.CUT", 1, 0x0049d775, 0x00000106 },
+ { "C21S.CUT", 1, 0x0049d87b, 0x0000012a },
+ { "C21T.CUT", 1, 0x0049d9a5, 0x0000015e },
+ { "C21U.CUT", 1, 0x0049db03, 0x000000c6 },
+ { "C22A.CUT", 1, 0x0049dbc9, 0x00000156 },
+ { "C22B.CUT", 1, 0x0049dd1f, 0x000000fc },
+ { "C22C.CUT", 1, 0x0049de1b, 0x00000166 },
+ { "C24A.CUT", 1, 0x0049df81, 0x000000ec },
+ { "C24B.CUT", 1, 0x0049e06d, 0x000005ca },
+ { "C25A.CUT", 1, 0x0049e637, 0x000002d0 },
+ { "C25B.CUT", 1, 0x0049e907, 0x000002e4 },
+ { "C25C.CUT", 1, 0x0049ebeb, 0x0000023e },
+ { "C25D.CUT", 1, 0x0049ee29, 0x00000182 },
+ { "C25E.CUT", 1, 0x0049efab, 0x00000126 },
+ { "C25F.CUT", 1, 0x0049f0d1, 0x0000017c },
+ { "C25G.CUT", 1, 0x0049f24d, 0x000001e2 },
+ { "C25H.CUT", 1, 0x0049f42f, 0x00000218 },
+ { "C25I.CUT", 1, 0x0049f647, 0x000001da },
+ { "C26A.CUT", 1, 0x0049f821, 0x0000030e },
+ { "C26B.CUT", 1, 0x0049fb2f, 0x000000be },
+ { "C26C.CUT", 1, 0x0049fbed, 0x000001a4 },
+ { "C2A.CUT", 1, 0x0049fd91, 0x000008d0 },
+ { "C2B.CUT", 1, 0x004a0661, 0x000004b8 },
+ { "C2C.CUT", 1, 0x004a0b19, 0x0000023e },
+ { "C3.BBK", 1, 0x004a0d57, 0x00006875 },
+ { "C3.LUM", 1, 0x004a75cc, 0x00000018 },
+ { "C3.MSK", 1, 0x004a75e4, 0x00001f40 },
+ { "C3.PCX", 1, 0x004a9524, 0x0000c40c },
+ { "C3.SAM", 1, 0x004b5930, 0x00011c72 },
+ { "C30A.CUT", 1, 0x004c75a2, 0x00000254 },
+ { "C30B.CUT", 1, 0x004c77f6, 0x000003c4 },
+ { "C30C.CUT", 1, 0x004c7bba, 0x00000722 },
+ { "C30D.CUT", 1, 0x004c82dc, 0x00000206 },
+ { "C30E.CUT", 1, 0x004c84e2, 0x00000976 },
+ { "C30F.CUT", 1, 0x004c8e58, 0x0000010e },
+ { "C30G.CUT", 1, 0x004c8f66, 0x000001e6 },
+ { "C30H.CUT", 1, 0x004c914c, 0x0000014c },
+ { "C31A.CUT", 1, 0x004c9298, 0x0000020c },
+ { "C31B.CUT", 1, 0x004c94a4, 0x00000596 },
+ { "C31C.CUT", 1, 0x004c9a3a, 0x000000e2 },
+ { "C31D.CUT", 1, 0x004c9b1c, 0x000004e4 },
+ { "C31E.CUT", 1, 0x004ca000, 0x000000ee },
+ { "C31F.CUT", 1, 0x004ca0ee, 0x0000010e },
+ { "C32A.CUT", 1, 0x004ca1fc, 0x000000ac },
+ { "C32B.CUT", 1, 0x004ca2a8, 0x0000010c },
+ { "C32C.CUT", 1, 0x004ca3b4, 0x000000e8 },
+ { "C35A.CUT", 1, 0x004ca49c, 0x0000042a },
+ { "C35B.CUT", 1, 0x004ca8c6, 0x000001c0 },
+ { "C36A.CUT", 1, 0x004caa86, 0x0000078c },
+ { "C39A.CUT", 1, 0x004cb212, 0x00000ed4 },
+ { "C3A.CUT", 1, 0x004cc0e6, 0x00000610 },
+ { "C3B.CUT", 1, 0x004cc6f6, 0x000000a0 },
+ { "C3C.CUT", 1, 0x004cc796, 0x000005d8 },
+ { "C3D.CUT", 1, 0x004ccd6e, 0x00000344 },
+ { "C3E.CUT", 1, 0x004cd0b2, 0x0000013a },
+ { "C3F.CUT", 1, 0x004cd1ec, 0x000000e0 },
+ { "C3G.CUT", 1, 0x004cd2cc, 0x00000102 },
+ { "C3H.CUT", 1, 0x004cd3ce, 0x00000120 },
+ { "C4.BBK", 1, 0x004cd4ee, 0x00009420 },
+ { "C4.LUM", 1, 0x004d690e, 0x00000018 },
+ { "C4.MSK", 1, 0x004d6926, 0x00001f40 },
+ { "C4.PCX", 1, 0x004d8866, 0x00009487 },
+ { "C4.SAM", 1, 0x004e1ced, 0x00002754 },
+ { "C40A.CUT", 1, 0x004e4441, 0x0000094a },
+ { "C40B.CUT", 1, 0x004e4d8b, 0x000002c2 },
+ { "C41A.CUT", 1, 0x004e504d, 0x00000b84 },
+ { "C41B.CUT", 1, 0x004e5bd1, 0x00001ba0 },
+ { "C41C.CUT", 1, 0x004e7771, 0x00000170 },
+ { "C41D.CUT", 1, 0x004e78e1, 0x0000025c },
+ { "C41E.CUT", 1, 0x004e7b3d, 0x000001b8 },
+ { "C41F.CUT", 1, 0x004e7cf5, 0x0000009c },
+ { "C41G.CUT", 1, 0x004e7d91, 0x000000fe },
+ { "C41H.CUT", 1, 0x004e7e8f, 0x000000ba },
+ { "C42A.CUT", 1, 0x004e7f49, 0x000003ce },
+ { "C42B.CUT", 1, 0x004e8317, 0x00000802 },
+ { "C42C.CUT", 1, 0x004e8b19, 0x000001ae },
+ { "C42D.CUT", 1, 0x004e8cc7, 0x0000010e },
+ { "C42E.CUT", 1, 0x004e8dd5, 0x00000106 },
+ { "C42F.CUT", 1, 0x004e8edb, 0x00000166 },
+ { "C44A.CUT", 1, 0x004e9041, 0x000004f8 },
+ { "C44B.CUT", 1, 0x004e9539, 0x0000079e },
+ { "C44C.CUT", 1, 0x004e9cd7, 0x0000030a },
+ { "C45A.CUT", 1, 0x004e9fe1, 0x000000f8 },
+ { "C45B.CUT", 1, 0x004ea0d9, 0x000000f8 },
+ { "C45C.CUT", 1, 0x004ea1d1, 0x000000f8 },
+ { "C45D.CUT", 1, 0x004ea2c9, 0x000000f8 },
+ { "C45E.CUT", 1, 0x004ea3c1, 0x00000186 },
+ { "C46A.CUT", 1, 0x004ea547, 0x000000f8 },
+ { "C46B.CUT", 1, 0x004ea63f, 0x000000f8 },
+ { "C46C.CUT", 1, 0x004ea737, 0x000000f8 },
+ { "C46D.CUT", 1, 0x004ea82f, 0x000000f8 },
+ { "C47A.CUT", 1, 0x004ea927, 0x00000640 },
+ { "C48A.CUT", 1, 0x004eaf67, 0x00000656 },
+ { "C48C.CUT", 1, 0x004eb5bd, 0x00000516 },
+ { "C49B.CUT", 1, 0x004ebad3, 0x00000398 },
+ { "C49C.CUT", 1, 0x004ebe6b, 0x00000132 },
+ { "C49D.CUT", 1, 0x004ebf9d, 0x000004e4 },
+ { "C4A.CUT", 1, 0x004ec481, 0x00000094 },
+ { "C4B.CUT", 1, 0x004ec515, 0x000003f4 },
+ { "C5.BBK", 1, 0x004ec909, 0x00004171 },
+ { "C5.LUM", 1, 0x004f0a7a, 0x00000018 },
+ { "C5.MSK", 1, 0x004f0a92, 0x00001f40 },
+ { "C5.PCX", 1, 0x004f29d2, 0x000099f2 },
+ { "C5.SAM", 1, 0x004fc3c4, 0x00003016 },
+ { "C50A.CUT", 1, 0x004ff3da, 0x000004f2 },
+ { "C50B.CUT", 1, 0x004ff8cc, 0x000001f8 },
+ { "C50C.CUT", 1, 0x004ffac4, 0x0000086e },
+ { "C50D.CUT", 1, 0x00500332, 0x000000e2 },
+ { "C50E.CUT", 1, 0x00500414, 0x00000618 },
+ { "C50F.CUT", 1, 0x00500a2c, 0x000003b8 },
+ { "C50G.CUT", 1, 0x00500de4, 0x000008da },
+ { "C50H.CUT", 1, 0x005016be, 0x000000ba },
+ { "C50I.CUT", 1, 0x00501778, 0x000009d4 },
+ { "C50J.CUT", 1, 0x0050214c, 0x000000f2 },
+ { "C51A.CUT", 1, 0x0050223e, 0x0000080e },
+ { "C51B.CUT", 1, 0x00502a4c, 0x000001d8 },
+ { "C51C.CUT", 1, 0x00502c24, 0x000001d8 },
+ { "C51D.CUT", 1, 0x00502dfc, 0x000000ec },
+ { "C53A.CUT", 1, 0x00502ee8, 0x00000566 },
+ { "C53B.CUT", 1, 0x0050344e, 0x0000018a },
+ { "C54A.CUT", 1, 0x005035d8, 0x0000028c },
+ { "C55A.CUT", 1, 0x00503864, 0x000006c8 },
+ { "C56A.CUT", 1, 0x00503f2c, 0x000003ba },
+ { "C56B.CUT", 1, 0x005042e6, 0x00000678 },
+ { "C58A.CUT", 1, 0x0050495e, 0x000001cc },
+ { "C59A.CUT", 1, 0x00504b2a, 0x0000014a },
+ { "C5A.CUT", 1, 0x00504c74, 0x0000040c },
+ { "C5C.CUT", 1, 0x00505080, 0x0000050c },
+ { "C6.BBK", 1, 0x0050558c, 0x0000711b },
+ { "C6.LUM", 1, 0x0050c6a7, 0x00000018 },
+ { "C6.MSK", 1, 0x0050c6bf, 0x00001f40 },
+ { "C6.PCX", 1, 0x0050e5ff, 0x0000c6c0 },
+ { "C6.SAM", 1, 0x0051acbf, 0x00004a87 },
+ { "C60A.CUT", 1, 0x0051f746, 0x00000170 },
+ { "C61A.CUT", 1, 0x0051f8b6, 0x00000170 },
+ { "C62A.CUT", 1, 0x0051fa26, 0x00000dde },
+ { "C62B.CUT", 1, 0x00520804, 0x00000268 },
+ { "C62C.CUT", 1, 0x00520a6c, 0x00000164 },
+ { "C62D.CUT", 1, 0x00520bd0, 0x000002a2 },
+ { "C63A.CUT", 1, 0x00520e72, 0x000002d0 },
+ { "C63B.CUT", 1, 0x00521142, 0x0000098e },
+ { "C63C.CUT", 1, 0x00521ad0, 0x000005d8 },
+ { "C63D.CUT", 1, 0x005220a8, 0x00000194 },
+ { "C63E.CUT", 1, 0x0052223c, 0x00000336 },
+ { "C63F.CUT", 1, 0x00522572, 0x0000041a },
+ { "C63G.CUT", 1, 0x0052298c, 0x00000170 },
+ { "C63H.CUT", 1, 0x00522afc, 0x0000039a },
+ { "C63I.CUT", 1, 0x00522e96, 0x000003ac },
+ { "C63J.CUT", 1, 0x00523242, 0x000002f0 },
+ { "C63K.CUT", 1, 0x00523532, 0x00000634 },
+ { "C64A.CUT", 1, 0x00523b66, 0x00000128 },
+ { "C66A.CUT", 1, 0x00523c8e, 0x000002e8 },
+ { "C67A.CUT", 1, 0x00523f76, 0x000005b6 },
+ { "C67B.CUT", 1, 0x0052452c, 0x0000054c },
+ { "C69A.CUT", 1, 0x00524a78, 0x00000700 },
+ { "C69B.CUT", 1, 0x00525178, 0x00000670 },
+ { "C69C.CUT", 1, 0x005257e8, 0x00000688 },
+ { "C69D.CUT", 1, 0x00525e70, 0x000006ac },
+ { "C69E.CUT", 1, 0x0052651c, 0x000009cc },
+ { "C69F.CUT", 1, 0x00526ee8, 0x00000aa2 },
+ { "C69G.CUT", 1, 0x0052798a, 0x000019ac },
+ { "C69H.CUT", 1, 0x00529336, 0x0000075a },
+ { "C69I.CUT", 1, 0x00529a90, 0x000003f0 },
+ { "C69J.CUT", 1, 0x00529e80, 0x0000008a },
+ { "C69K.CUT", 1, 0x00529f0a, 0x000005c8 },
+ { "C69L.CUT", 1, 0x0052a4d2, 0x0000062a },
+ { "C69M.CUT", 1, 0x0052aafc, 0x000005ba },
+ { "C69N.CUT", 1, 0x0052b0b6, 0x0000012c },
+ { "C69O.CUT", 1, 0x0052b1e2, 0x000001e4 },
+ { "C69Z.CUT", 1, 0x0052b3c6, 0x000017a4 },
+ { "C6A.CUT", 1, 0x0052cb6a, 0x00000220 },
+ { "C6B.CUT", 1, 0x0052cd8a, 0x000000da },
+ { "C6C.CUT", 1, 0x0052ce64, 0x00000138 },
+ { "C7.BBK", 1, 0x0052cf9c, 0x00013c13 },
+ { "C7.PCX", 1, 0x00540baf, 0x0000a75d },
+ { "C70A.CUT", 1, 0x0054b30c, 0x000002b8 },
+ { "C70B.CUT", 1, 0x0054b5c4, 0x00000384 },
+ { "C70B.SAM", 1, 0x0054b948, 0x00009a4e },
+ { "C70C.CUT", 1, 0x00555396, 0x00000292 },
+ { "C70D.CUT", 1, 0x00555628, 0x00000952 },
+ { "C70E.CUT", 1, 0x00555f7a, 0x0000038c },
+ { "C70F.CUT", 1, 0x00556306, 0x0000034c },
+ { "C70F.SAM", 1, 0x00556652, 0x000076bf },
+ { "C70G.CUT", 1, 0x0055dd11, 0x00000348 },
+ { "C70G.SAM", 1, 0x0055e059, 0x00008d7d },
+ { "C70H.CUT", 1, 0x00566dd6, 0x00000322 },
+ { "C70I.CUT", 1, 0x005670f8, 0x000003d8 },
+ { "C70J.CUT", 1, 0x005674d0, 0x00000184 },
+ { "C70K.CUT", 1, 0x00567654, 0x000002c0 },
+ { "C70L.CUT", 1, 0x00567914, 0x000002c0 },
+ { "C70M.CUT", 1, 0x00567bd4, 0x000002ba },
+ { "C70N.CUT", 1, 0x00567e8e, 0x000002ba },
+ { "C71A.CUT", 1, 0x00568148, 0x00000094 },
+ { "C71B.CUT", 1, 0x005681dc, 0x00000094 },
+ { "C71C.CUT", 1, 0x00568270, 0x000000b2 },
+ { "C72A.CUT", 1, 0x00568322, 0x00000730 },
+ { "C72B.CUT", 1, 0x00568a52, 0x00000632 },
+ { "C72C.CUT", 1, 0x00569084, 0x0000007c },
+ { "C73A.CUT", 1, 0x00569100, 0x0000038a },
+ { "C73B.CUT", 1, 0x0056948a, 0x000002b8 },
+ { "C73C.CUT", 1, 0x00569742, 0x00000192 },
+ { "C73D.CUT", 1, 0x005698d4, 0x000000c8 },
+ { "C73E.CUT", 1, 0x0056999c, 0x00000330 },
+ { "C73F.CUT", 1, 0x00569ccc, 0x00000344 },
+ { "C73G.CUT", 1, 0x0056a010, 0x00000210 },
+ { "C74A.CUT", 1, 0x0056a220, 0x0000075e },
+ { "C74B.CUT", 1, 0x0056a97e, 0x0000018c },
+ { "C74C.CUT", 1, 0x0056ab0a, 0x000001dc },
+ { "C74D.CUT", 1, 0x0056ace6, 0x0000018c },
+ { "C74F.CUT", 1, 0x0056ae72, 0x000000e0 },
+ { "C75B.CUT", 1, 0x0056af52, 0x00000d00 },
+ { "C76A.CUT", 1, 0x0056bc52, 0x00000072 },
+ { "C76B.CUT", 1, 0x0056bcc4, 0x00000f28 },
+ { "C8.BBK", 1, 0x0056cbec, 0x0000b5ce },
+ { "C8.PCX", 1, 0x005781ba, 0x0000c31f },
+ { "C8A.CUT", 1, 0x005844d9, 0x000007e4 },
+ { "C8B.CUT", 1, 0x00584cbd, 0x000002a8 },
+ { "C9.BBK", 1, 0x00584f65, 0x0000f68b },
+ { "C9.PCX", 1, 0x005945f0, 0x0000a787 },
+ { "C97A.CUT", 1, 0x0059ed77, 0x0000017e },
+ { "C97B.CUT", 1, 0x0059eef5, 0x0000013c },
+ { "C99D.CUT", 1, 0x0059f031, 0x00000094 },
+ { "C99E.CUT", 1, 0x0059f0c5, 0x000002e6 },
+ { "C99F.CUT", 1, 0x0059f3ab, 0x000002e6 },
+ { "C9A.CUT", 1, 0x0059f691, 0x000001fa },
+ { "C9B.CUT", 1, 0x0059f88b, 0x000000f8 },
+ { "C9C.CUT", 1, 0x0059f983, 0x00000136 },
+ { "C9D.CUT", 1, 0x0059fab9, 0x000000a0 },
+ { "CDCLO.CUT", 1, 0x0059fb59, 0x000001f6 },
+ { "CDINT.CUT", 1, 0x0059fd4f, 0x000017fe },
+ { "CDINT061.SB", 1, 0x005a154d, 0x0000334c },
+ { "CDINT063.SB", 1, 0x005a4899, 0x0000798b },
+ { "CDINT072.SB", 1, 0x005ac224, 0x00005f03 },
+ { "CDINT081.SB", 1, 0x005b2127, 0x000062b3 },
+ { "CDINT091.SB", 1, 0x005b83da, 0x00008a0e },
+ { "CDINT102.SB", 1, 0x005c0de8, 0x0000673a },
+ { "CDINT111.SB", 1, 0x005c7522, 0x00008453 },
+ { "CDINT141.SB", 1, 0x005cf975, 0x000031be },
+ { "CDINT151.SB", 1, 0x005d2b33, 0x00002a7c },
+ { "CDINT191.SB", 1, 0x005d55af, 0x00001fc8 },
+ { "CDINT201.SB", 1, 0x005d7577, 0x00002cd8 },
+ { "CDINT212.SB", 1, 0x005da24f, 0x00004b03 },
+ { "CDINT231.SB", 1, 0x005ded52, 0x00008dc0 },
+ { "CDINT241.SB", 1, 0x005e7b12, 0x000032cf },
+ { "CDINT281.SB", 1, 0x005eade1, 0x000053a6 },
+ { "CDINT291.SB", 1, 0x005f0187, 0x00002138 },
+ { "CDINT301.SB", 1, 0x005f22bf, 0x00003dae },
+ { "CDINT321.SB", 1, 0x005f606d, 0x00007e92 },
+ { "CDINT351.SB", 1, 0x005fdeff, 0x00002000 },
+ { "CDINT361.SB", 1, 0x005ffeff, 0x00005a8b },
+ { "CDINT381.SB", 1, 0x0060598a, 0x00003882 },
+ { "CDINT442.SB", 1, 0x0060920c, 0x0000471b },
+ { "CDINT451.SB", 1, 0x0060d927, 0x00005929 },
+ { "CDINT452.SB", 1, 0x00613250, 0x00007915 },
+ { "CDINT461.SB", 1, 0x0061ab65, 0x00001c6a },
+ { "CDINT463.SB", 1, 0x0061c7cf, 0x000030ae },
+ { "CDINT464.SB", 1, 0x0061f87d, 0x0000c980 },
+ { "CDINT471.SB", 1, 0x0062c1fd, 0x00003593 },
+ { "CDINT481.SB", 1, 0x0062f790, 0x00004e60 },
+ { "CDINT492.SB", 1, 0x006345f0, 0x00004594 },
+ { "CDRES.CUT", 1, 0x00638b84, 0x000001f6 },
+ { "CHANGE.SAM", 1, 0x00638d7a, 0x00005ef2 },
+ { "CHEF.ACT", 1, 0x0063ec6c, 0x00005362 },
+ { "CHEF.DOG", 1, 0x00643fce, 0x00000af0 },
+ { "CHIEF1.DOG", 1, 0x00644abe, 0x000009ca },
+ { "CHIEF2.DOG", 1, 0x00645488, 0x0000121e },
+ { "CHIEF3.DOG", 1, 0x006466a6, 0x00000be4 },
+ { "CHIEF4.DOG", 1, 0x0064728a, 0x000002c4 },
+ { "CHORN.CUT", 1, 0x0064754e, 0x0000034a },
+ { "CINTR.CUT", 1, 0x00647898, 0x00001768 },
+ { "CLOGO.CUT", 1, 0x00649000, 0x000000aa },
+ { "CMASK.CUT", 1, 0x006490aa, 0x00000386 },
+ { "COCON.CUT", 1, 0x00649430, 0x000003d8 },
+ { "COCONUT.SAM", 1, 0x00649808, 0x0000a047 },
+ { "COMIC.CUT", 1, 0x0065384f, 0x0000094e },
+ { "COMPY.ACT", 1, 0x0065419d, 0x00005533 },
+ { "CONTROL.BBK", 1, 0x006596d0, 0x00000e0e },
+ { "COPY.BBK", 1, 0x0065a4de, 0x00000002 },
+ { "COPY.CUT", 1, 0x0065a4e0, 0x00000086 },
+ { "COPY.LBM", 1, 0x0065a566, 0x000053ca },
+ { "COPY.PCX", 1, 0x0065f930, 0x0000646f },
+ { "CRAP.PCX", 1, 0x00665d9f, 0x0000724c },
+ { "CRED.CUT", 1, 0x0066cfeb, 0x000003ae },
+ { "CREDIT1.CRD", 1, 0x0066d399, 0x00000604 },
+ { "CREDIT2.CRD", 1, 0x0066d99d, 0x000010d3 },
+ { "CROWBAR.SAM", 1, 0x0066ea70, 0x00009aca },
+ { "CUDRS.CUT", 1, 0x0067853a, 0x000001f6 },
+ { "D1.BBK", 1, 0x00678730, 0x000061b7 },
+ { "D1.LUM", 1, 0x0067e8e7, 0x00000018 },
+ { "D1.MSK", 1, 0x0067e8ff, 0x00001f40 },
+ { "D1.PCX", 1, 0x0068083f, 0x0000a331 },
+ { "D1C.SAM", 1, 0x0068ab70, 0x0000ddd7 },
+ { "D2.BBK", 1, 0x00698947, 0x00004630 },
+ { "D2.LUM", 1, 0x0069cf77, 0x00000018 },
+ { "D2.MSK", 1, 0x0069cf8f, 0x00001f40 },
+ { "D2.PCX", 1, 0x0069eecf, 0x00008dc3 },
+ { "D3.BBK", 1, 0x006a7c92, 0x00004b34 },
+ { "D3.LUM", 1, 0x006ac7c6, 0x00000018 },
+ { "D3.MSK", 1, 0x006ac7de, 0x00001f40 },
+ { "D3.PCX", 1, 0x006ae71e, 0x0000a662 },
+ { "D3.SAM", 1, 0x006b8d80, 0x0000c55a },
+ { "D4.BBK", 1, 0x006c52da, 0x00013e0d },
+ { "D4.LUM", 1, 0x006d90e7, 0x00000018 },
+ { "D4.MSK", 1, 0x006d90ff, 0x00001f40 },
+ { "D4.PCX", 1, 0x006db03f, 0x0000a69b },
+ { "D5.BBK", 1, 0x006e56da, 0x000088a1 },
+ { "D5.PCX", 1, 0x006edf7b, 0x00007bc5 },
+ { "D5.SAM", 1, 0x006f5b40, 0x00002ee7 },
+ { "D5B.SAM", 1, 0x006f8a27, 0x0001128e },
+ { "D6.BBK", 1, 0x00709cb5, 0x0000a480 },
+ { "D6.LUM", 1, 0x00714135, 0x00000018 },
+ { "D6.MSK", 1, 0x0071414d, 0x00001f40 },
+ { "D6.PCX", 1, 0x0071608d, 0x000099a6 },
+ { "D9.BBK", 1, 0x0071fa33, 0x00008461 },
+ { "D9.PCX", 1, 0x00727e94, 0x0000b551 },
+ { "DATA", 1, 0x007333e5, 0x00001434 },
+ { "DEATH.ACT", 1, 0x00734819, 0x0000d83a },
+ { "DEATH1.DOG", 1, 0x00742053, 0x000017ca },
+ { "DEATH2.DOG", 1, 0x0074381d, 0x0000081e },
+ { "DEINO.ACT", 1, 0x0074403b, 0x0000e199 },
+ { "DFRANK.ACT", 1, 0x007521d4, 0x000052e2 },
+ { "DISK1.SAM", 1, 0x007574b6, 0x000033da },
+ { "DOG.ACT", 1, 0x0075a890, 0x00002a62 },
+ { "DOG.DOG", 1, 0x0075d2f2, 0x0000023a },
+ { "E1.BBK", 1, 0x0075d52c, 0x0001765b },
+ { "E1.PCX", 1, 0x00774b87, 0x0000dcc7 },
+ { "E2.BBK", 1, 0x0078284e, 0x0000cefc },
+ { "E2.PCX", 1, 0x0078f74a, 0x0000f5f2 },
+ { "E3.BBK", 1, 0x0079ed3c, 0x00011042 },
+ { "E3.PCX", 1, 0x007afd7e, 0x0000a4d0 },
+ { "EQDATA.LHA", 1, 0x007ba24e, 0x00031a7a },
+ { "F1.BBK", 1, 0x007ebcc8, 0x00004716 },
+ { "F1.LUM", 1, 0x007f03de, 0x00000018 },
+ { "F1.MSK", 1, 0x007f03f6, 0x00001f40 },
+ { "F1.PCX", 1, 0x007f2336, 0x0000991d },
+ { "F1.SAM", 1, 0x007fbc53, 0x0000fd04 },
+ { "F1B.PCX", 1, 0x0080b957, 0x00005739 },
+ { "F2.BBK", 1, 0x00811090, 0x00006a18 },
+ { "F2.LUM", 1, 0x00817aa8, 0x00000018 },
+ { "F2.MSK", 1, 0x00817ac0, 0x00001f40 },
+ { "F2.PCX", 1, 0x00819a00, 0x00009f28 },
+ { "F2B.PCX", 1, 0x00823928, 0x00004abb },
+ { "F3.BBK", 1, 0x008283e3, 0x00002c59 },
+ { "F3.LUM", 1, 0x0082b03c, 0x00000018 },
+ { "F3.MSK", 1, 0x0082b054, 0x00001f40 },
+ { "F3.PCX", 1, 0x0082cf94, 0x0000aa0c },
+ { "F3.SAM", 1, 0x008379a0, 0x0000a940 },
+ { "F4.BBK", 1, 0x008422e0, 0x0000b561 },
+ { "F4.LUM", 1, 0x0084d841, 0x00000018 },
+ { "F4.MSK", 1, 0x0084d859, 0x00001f40 },
+ { "F4.PCX", 1, 0x0084f799, 0x00014df0 },
+ { "F4B.PCX", 1, 0x00864589, 0x000117d8 },
+ { "F4S.PCX", 1, 0x00875d61, 0x0000833e },
+ { "FALL.SAM", 1, 0x0087e09f, 0x0000cbda },
+ { "FAYE.ACT", 1, 0x0088ac79, 0x00013fda },
+ { "FAYE2.DOG", 1, 0x0089ec53, 0x0000098e },
+ { "FAYE3.DOG", 1, 0x0089f5e1, 0x000005d8 },
+ { "FAYE4.DOG", 1, 0x0089fbb9, 0x00000c40 },
+ { "FAYE5.CUT", 1, 0x008a07f9, 0x0000007a },
+ { "FAYE5.DOG", 1, 0x008a0873, 0x000009ce },
+ { "FAYE6.DOG", 1, 0x008a1241, 0x00000466 },
+ { "FAYE_E.ACT", 1, 0x008a16a7, 0x00002d4a },
+ { "FAYE_H.ACT", 1, 0x008a43f1, 0x0000807a },
+ { "FAYE_H.BBK", 1, 0x008ac46b, 0x0000e9fb },
+ { "FAYE_H.PCX", 1, 0x008bae66, 0x000096b5 },
+ { "FRANK.ACT", 1, 0x008c451b, 0x00008342 },
+ { "FRANK.DOG", 1, 0x008cc85d, 0x00000e48 },
+ { "FRANK_H.ACT", 1, 0x008cd6a5, 0x0000ad60 },
+ { "FRANK_H.BBK", 1, 0x008d8405, 0x00016369 },
+ { "FRANK_H.PCX", 1, 0x008ee76e, 0x00008dd6 },
+ { "GET_GEM.SAM", 1, 0x008f7544, 0x000099d2 },
+ { "GET_HORN.BBK", 1, 0x00900f16, 0x000045d8 },
+ { "GET_HORN.SAM", 1, 0x009054ee, 0x00005dde },
+ { "GM.CUT", 1, 0x0090b2cc, 0x0000009c },
+ { "GOONS.ACT", 1, 0x0090b368, 0x0000728a },
+ { "GUARDS.ACT", 1, 0x009125f2, 0x0000504a },
+ { "HENRY.ACT", 1, 0x0091763c, 0x0000b102 },
+ { "HENRY.DOG", 1, 0x0092273e, 0x00001280 },
+ { "HENRY2.DOG", 1, 0x009239be, 0x00000b24 },
+ { "HORN.SAM", 1, 0x009244e2, 0x000067b2 },
+ { "HUGH.ACT", 1, 0x0092ac94, 0x0000e8da },
+ { "I1.SAM", 1, 0x0093956e, 0x0000cbf2 },
+ { "IAN.ACT", 1, 0x00946160, 0x0000a542 },
+ { "IAN1.DOG", 1, 0x009506a2, 0x00000518 },
+ { "IAN2.DOG", 1, 0x00950bba, 0x00000a9a },
+ { "J1.BBK", 1, 0x00951654, 0x000025fc },
+ { "J1.LUM", 1, 0x00953c50, 0x00000018 },
+ { "J1.MSK", 1, 0x00953c68, 0x00001f40 },
+ { "J1.PCX", 1, 0x00955ba8, 0x00008d84 },
+ { "J1.SAM", 1, 0x0095e92c, 0x00008442 },
+ { "J2.BBK", 1, 0x00966d6e, 0x00000e97 },
+ { "J2.LUM", 1, 0x00967c05, 0x00000018 },
+ { "J2.MSK", 1, 0x00967c1d, 0x00001f40 },
+ { "J2.PCX", 1, 0x00969b5d, 0x0000c33c },
+ { "J2.SAM", 1, 0x00975e99, 0x0000a1e3 },
+ { "J3.BBK", 1, 0x0098007c, 0x00003289 },
+ { "J3.LUM", 1, 0x00983305, 0x00000018 },
+ { "J3.MSK", 1, 0x0098331d, 0x00001f40 },
+ { "J3.PCX", 1, 0x0098525d, 0x000094b4 },
+ { "J4.BBK", 1, 0x0098e711, 0x00006e28 },
+ { "J4.LUM", 1, 0x00995539, 0x00000018 },
+ { "J4.MSK", 1, 0x00995551, 0x00001f40 },
+ { "J4.PCX", 1, 0x00997491, 0x00008e90 },
+ { "J5.BBK", 1, 0x009a0321, 0x00004521 },
+ { "J5.LUM", 1, 0x009a4842, 0x00000018 },
+ { "J5.MSK", 1, 0x009a485a, 0x00001f40 },
+ { "J5.PCX", 1, 0x009a679a, 0x00009b37 },
+ { "J5.SAM", 1, 0x009b02d1, 0x000042f2 },
+ { "J6.BBK", 1, 0x009b45c3, 0x000043d7 },
+ { "J6.LUM", 1, 0x009b899a, 0x00000018 },
+ { "J6.MSK", 1, 0x009b89b2, 0x00001f40 },
+ { "J6.PCX", 1, 0x009ba8f2, 0x0000a5c5 },
+ { "J7.BBK", 1, 0x009c4eb7, 0x00003234 },
+ { "J7.LUM", 1, 0x009c80eb, 0x00000018 },
+ { "J7.MSK", 1, 0x009c8103, 0x00001f40 },
+ { "J7.PCX", 1, 0x009ca043, 0x0000cd63 },
+ { "J7.SAM", 1, 0x009d6da6, 0x000083aa },
+ { "J8.BBK", 1, 0x009df150, 0x0000320d },
+ { "J8.LUM", 1, 0x009e235d, 0x00000018 },
+ { "J8.MSK", 1, 0x009e2375, 0x00001f40 },
+ { "J8.PCX", 1, 0x009e42b5, 0x0000c0e2 },
+ { "J8.SAM", 1, 0x009f0397, 0x0000024e },
+ { "JASPAR.ACT", 1, 0x009f05e5, 0x00004fb2 },
+ { "JIM1.DOG", 1, 0x009f5597, 0x00001276 },
+ { "JIM2.DOG", 1, 0x009f680d, 0x00001282 },
+ { "JIM3.DOG", 1, 0x009f7a8f, 0x00000df6 },
+ { "JIMTAM.ACT", 1, 0x009f8885, 0x0000a08a },
+ { "JOE.BBK", 1, 0x00a0290f, 0x00014b8a },
+ { "JOE1.BBK", 1, 0x00a17499, 0x00012a5a },
+ { "JOED_A.BBK", 1, 0x00a29ef3, 0x00009b8a },
+ { "JOED_B.BBK", 1, 0x00a33a7d, 0x0000a50a },
+ { "JOEU_A.BBK", 1, 0x00a3df87, 0x00009b8a },
+ { "JOEU_B.BBK", 1, 0x00a47b11, 0x0000a50a },
+ { "JOE_A.BBK", 1, 0x00a5201b, 0x00009b8a },
+ { "JOE_B.BBK", 1, 0x00a5bba5, 0x0000dc5a },
+ { "JOE_E.ACT", 1, 0x00a697ff, 0x0000388a },
+ { "JOE_H.ACT", 1, 0x00a6d089, 0x0000dace },
+ { "JOHN.ACT", 1, 0x00a7ab57, 0x00006312 },
+ { "JOHN1.DOG", 1, 0x00a80e69, 0x0000067c },
+ { "JOURNAL.BBK", 1, 0x00a814e5, 0x0000a318 },
+ { "JOURNAL.PCX", 1, 0x00a8b7fd, 0x00009c70 },
+ { "KISS1.SAM", 1, 0x00a9546d, 0x0000f722 },
+ { "KLUNK.ACT", 1, 0x00aa4b8f, 0x00007c30 },
+ { "KLUNK1.DOG", 1, 0x00aac7bf, 0x00000e6a },
+ { "KLUNK2.DOG", 1, 0x00aad629, 0x00000ed0 },
+ { "KLUNK2.SAM", 1, 0x00aae4f9, 0x0001737c },
+ { "L1.BBK", 1, 0x00ac5875, 0x00000002 },
+ { "L1.PCX", 1, 0x00ac5877, 0x000043f7 },
+ { "L2.BBK", 1, 0x00ac9c6e, 0x00000002 },
+ { "L2.PCX", 1, 0x00ac9c70, 0x0000ba60 },
+ { "LARIS.ACT", 1, 0x00ad56d0, 0x0000355a },
+ { "LARIS.DOG", 1, 0x00ad8c2a, 0x00000f32 },
+ { "LARIS3.DOG", 1, 0x00ad9b5c, 0x0000079a },
+ { "LITTLEP.ACT", 1, 0x00ada2f6, 0x000002e2 },
+ { "LOLA.ACT", 1, 0x00ada5d8, 0x0001342d },
+ { "LOLA1.DOG", 1, 0x00aeda05, 0x00000ad6 },
+ { "LOU.ACT", 1, 0x00aee4db, 0x00005552 },
+ { "LOU1.DOG", 1, 0x00af3a2d, 0x000010ac },
+ { "LOU2.DOG", 1, 0x00af4ad9, 0x00000bae },
+ { "M1.BBK", 1, 0x00af5687, 0x00006d5c },
+ { "M1.PCX", 1, 0x00afc3e3, 0x000193ce },
+ { "M2.BBK", 1, 0x00b157b1, 0x00001a4a },
+ { "M2.PCX", 1, 0x00b171fb, 0x0000a9d3 },
+ { "M2.SAM", 1, 0x00b21bce, 0x0001433c },
+ { "MAN1.DOG", 1, 0x00b35f0a, 0x0000101e },
+ { "MAN2.DOG", 1, 0x00b36f28, 0x000007a4 },
+ { "MASK.SAM", 1, 0x00b376cc, 0x000081ea },
+ { "N1.BBK", 1, 0x00b3f8b6, 0x00002d74 },
+ { "N1.LUM", 1, 0x00b4262a, 0x00000018 },
+ { "N1.MSK", 1, 0x00b42642, 0x00001f40 },
+ { "N1.PCX", 1, 0x00b44582, 0x0000cbe7 },
+ { "N10.BBK", 1, 0x00b51169, 0x00000002 },
+ { "N10.PCX", 1, 0x00b5116b, 0x0000a28d },
+ { "N10.SAM", 1, 0x00b5b3f8, 0x000147f8 },
+ { "N11.BBK", 1, 0x00b6fbf0, 0x00000d48 },
+ { "N11.PCX", 1, 0x00b70938, 0x00008e85 },
+ { "N12.BBK", 1, 0x00b797bd, 0x000049aa },
+ { "N12.LUM", 1, 0x00b7e167, 0x00000018 },
+ { "N12.MSK", 1, 0x00b7e17f, 0x00001f40 },
+ { "N12.PCX", 1, 0x00b800bf, 0x000074ea },
+ { "N13.BBK", 1, 0x00b875a9, 0x000046b7 },
+ { "N13.LUM", 1, 0x00b8bc60, 0x00000018 },
+ { "N13.MSK", 1, 0x00b8bc78, 0x00001f40 },
+ { "N13.PCX", 1, 0x00b8dbb8, 0x00008640 },
+ { "N13.SAM", 1, 0x00b961f8, 0x00025a26 },
+ { "N13B.SAM", 1, 0x00bbbc1e, 0x0000250a },
+ { "N14.BBK", 1, 0x00bbe128, 0x000073ec },
+ { "N14.PCX", 1, 0x00bc5514, 0x0000b3f3 },
+ { "N14.SAM", 1, 0x00bd0907, 0x0002550a },
+ { "N14B.PCX", 1, 0x00bf5e11, 0x0000b933 },
+ { "N15.BBK", 1, 0x00c01744, 0x00000002 },
+ { "N15.PCX", 1, 0x00c01746, 0x0000ae43 },
+ { "N16.BBK", 1, 0x00c0c589, 0x000014c0 },
+ { "N16.LUM", 1, 0x00c0da49, 0x00000018 },
+ { "N16.MSK", 1, 0x00c0da61, 0x00001f40 },
+ { "N16.PCX", 1, 0x00c0f9a1, 0x0000e038 },
+ { "N2.BBK", 1, 0x00c1d9d9, 0x00007377 },
+ { "N2.LUM", 1, 0x00c24d50, 0x00000018 },
+ { "N2.MSK", 1, 0x00c24d68, 0x00001f40 },
+ { "N2.PCX", 1, 0x00c26ca8, 0x00007d65 },
+ { "N3.BBK", 1, 0x00c2ea0d, 0x00004fe2 },
+ { "N3.LUM", 1, 0x00c339ef, 0x00000018 },
+ { "N3.MSK", 1, 0x00c33a07, 0x00001f40 },
+ { "N3.PCX", 1, 0x00c35947, 0x0000a6de },
+ { "N4.BBK", 1, 0x00c40025, 0x0000acc0 },
+ { "N4.LUM", 1, 0x00c4ace5, 0x00000018 },
+ { "N4.MSK", 1, 0x00c4acfd, 0x00001f40 },
+ { "N4.PCX", 1, 0x00c4cc3d, 0x00006e0b },
+ { "N5.BBK", 1, 0x00c53a48, 0x0000202a },
+ { "N5.LUM", 1, 0x00c55a72, 0x00000018 },
+ { "N5.MSK", 1, 0x00c55a8a, 0x00001f40 },
+ { "N5.PCX", 1, 0x00c579ca, 0x000092f0 },
+ { "N6.BBK", 1, 0x00c60cba, 0x00001d97 },
+ { "N6.LUM", 1, 0x00c62a51, 0x00000018 },
+ { "N6.MSK", 1, 0x00c62a69, 0x00001f40 },
+ { "N6.PCX", 1, 0x00c649a9, 0x000086e2 },
+ { "N7.BBK", 1, 0x00c6d08b, 0x000025f8 },
+ { "N7.LUM", 1, 0x00c6f683, 0x00000018 },
+ { "N7.MSK", 1, 0x00c6f69b, 0x00001f40 },
+ { "N7.PCX", 1, 0x00c715db, 0x00008e79 },
+ { "N8.BBK", 1, 0x00c7a454, 0x00007bb7 },
+ { "N8.LUM", 1, 0x00c8200b, 0x00000018 },
+ { "N8.MSK", 1, 0x00c82023, 0x00001f40 },
+ { "N8.PCX", 1, 0x00c83f63, 0x0000be5f },
+ { "N9.BBK", 1, 0x00c8fdc2, 0x0000c9bf },
+ { "N9.LUM", 1, 0x00c9c781, 0x00000018 },
+ { "N9.MSK", 1, 0x00c9c799, 0x00001f40 },
+ { "N9.PCX", 1, 0x00c9e6d9, 0x0000aa4b },
+ { "N9.SAM", 1, 0x00ca9124, 0x0000e902 },
+ { "NAOMI.DOG", 1, 0x00cb7a26, 0x000013b6 },
+ { "NAOMI2.DOG", 1, 0x00cb8ddc, 0x00000a40 },
+ { "OBJECTS.BBK", 1, 0x00cb981c, 0x00019322 },
+ { "ORACLE.ACT", 1, 0x00cd2b3e, 0x00004042 },
+ { "ORACLE1.DOG", 1, 0x00cd6b80, 0x00001088 },
+ { "ORACLE2.DOG", 1, 0x00cd7c08, 0x000003c0 },
+ { "ORACLE3.DOG", 1, 0x00cd7fc8, 0x00000806 },
+ { "PANEL.PCX", 1, 0x00cd87ce, 0x00002279 },
+ { "PRIN1.CUT", 1, 0x00cdaa47, 0x0000007a },
+ { "PRIN1.DOG", 1, 0x00cdaac1, 0x00000aea },
+ { "PRIN2.DOG", 1, 0x00cdb5ab, 0x0000055a },
+ { "PRIN4.DOG", 1, 0x00cdbb05, 0x00000720 },
+ { "PRINCESS.ACT", 1, 0x00cdc225, 0x0000d732 },
+ { "PRISON.ACT", 1, 0x00ce9957, 0x00006f22 },
+ { "PUNCH.SAM", 1, 0x00cf0879, 0x00007e9a },
+ { "PUSH.SAM", 1, 0x00cf8713, 0x00009308 },
+ { "PYGMY.ACT", 1, 0x00d01a1b, 0x00010a63 },
+ { "QUEEN.JAS", 1, 0x00d1247e, 0x0001371a },
+ { "QUEEN2.JAS", 1, 0x00d25b98, 0x00008c00 },
+ { "R1.BBK", 1, 0x00d2e798, 0x00001a4a },
+ { "R1.PCX", 1, 0x00d301e2, 0x000065c0 },
+ { "R2.BBK", 1, 0x00d367a2, 0x00001a4a },
+ { "R2.PCX", 1, 0x00d381ec, 0x00005f32 },
+ { "R3.BBK", 1, 0x00d3e11e, 0x00001a4a },
+ { "R3.PCX", 1, 0x00d3fb68, 0x000061a1 },
+ { "R4.BBK", 1, 0x00d45d09, 0x00001a4a },
+ { "R4.PCX", 1, 0x00d47753, 0x00009ec3 },
+ { "RASH.SAM", 1, 0x00d51616, 0x0000431a },
+ { "RENEGADE.BBK", 1, 0x00d55930, 0x0000117a },
+ { "RENEGADE.PCX", 1, 0x00d56aaa, 0x0000296d },
+ { "RITA.ACT", 1, 0x00d59417, 0x000020ea },
+ { "RITA_H.ACT", 1, 0x00d5b501, 0x00015cdb },
+ { "ROCKET.SAM", 1, 0x00d711dc, 0x00027e20 },
+ { "SEC.ACT", 1, 0x00d98ffc, 0x00006e6a },
+ { "SEC1.DOG", 1, 0x00d9fe66, 0x00001672 },
+ { "SEC2.DOG", 1, 0x00da14d8, 0x00000944 },
+ { "SHEET.SAM", 1, 0x00da1e1c, 0x0000ad00 },
+ { "SHIELD.SAM", 1, 0x00dacb1c, 0x00002afb },
+ { "SHOWER.ACT", 1, 0x00daf617, 0x0000762a },
+ { "SHOWERAM.DOG", 1, 0x00db6c41, 0x000005fc },
+ { "SKULL.ACT", 1, 0x00db723d, 0x00001973 },
+ { "SPARKY.ACT", 1, 0x00db8bb0, 0x0000f912 },
+ { "SPARKY1.DOG", 1, 0x00dc84c2, 0x00000986 },
+ { "SPARKY2.DOG", 1, 0x00dc8e48, 0x00000402 },
+ { "SPARKY3.DOG", 1, 0x00dc924a, 0x0000126e },
+ { "SPARKY4.DOG", 1, 0x00dca4b8, 0x0000043a },
+ { "SPARKY5.DOG", 1, 0x00dca8f2, 0x0000091c },
+ { "SPARKY6.DOG", 1, 0x00dcb20e, 0x000007b6 },
+ { "SPARKY7.DOG", 1, 0x00dcb9c4, 0x0000095e },
+ { "SPARKY8.DOG", 1, 0x00dcc322, 0x0000072a },
+ { "SPARKY_H.ACT", 1, 0x00dcca4c, 0x0000280a },
+ { "T1.BBK", 1, 0x00dcf256, 0x0000f492 },
+ { "T1.LUM", 1, 0x00dde6e8, 0x00000018 },
+ { "T1.MSK", 1, 0x00dde700, 0x00001f40 },
+ { "T1.PCX", 1, 0x00de0640, 0x0000aa3d },
+ { "T1.SAM", 1, 0x00deb07d, 0x0000711a },
+ { "T10.BBK", 1, 0x00df2197, 0x0000a957 },
+ { "T10.LUM", 1, 0x00dfcaee, 0x00000018 },
+ { "T10.MSK", 1, 0x00dfcb06, 0x00001f40 },
+ { "T10.PCX", 1, 0x00dfea46, 0x0001198a },
+ { "T10.SAM", 1, 0x00e103d0, 0x0000bc49 },
+ { "T11.BBK", 1, 0x00e1c019, 0x00001e84 },
+ { "T11.LUM", 1, 0x00e1de9d, 0x00000018 },
+ { "T11.MSK", 1, 0x00e1deb5, 0x00001f40 },
+ { "T11.PCX", 1, 0x00e1fdf5, 0x00008e7e },
+ { "T12.BBK", 1, 0x00e28c73, 0x000059b5 },
+ { "T12.LUM", 1, 0x00e2e628, 0x00000018 },
+ { "T12.MSK", 1, 0x00e2e640, 0x00001f40 },
+ { "T12.PCX", 1, 0x00e30580, 0x00007b3c },
+ { "T12.SAM", 1, 0x00e380bc, 0x0000fe4b },
+ { "T13.BBK", 1, 0x00e47f07, 0x000043b4 },
+ { "T13.LUM", 1, 0x00e4c2bb, 0x00000018 },
+ { "T13.MSK", 1, 0x00e4c2d3, 0x00001f40 },
+ { "T13.PCX", 1, 0x00e4e213, 0x00007591 },
+ { "T14.BBK", 1, 0x00e557a4, 0x0000468b },
+ { "T14.LUM", 1, 0x00e59e2f, 0x00000018 },
+ { "T14.MSK", 1, 0x00e59e47, 0x00001f40 },
+ { "T14.PCX", 1, 0x00e5bd87, 0x00007df5 },
+ { "T14.SAM", 1, 0x00e63b7c, 0x00016db3 },
+ { "T15.BBK", 1, 0x00e7a92f, 0x000096e6 },
+ { "T15.LUM", 1, 0x00e84015, 0x00000018 },
+ { "T15.MSK", 1, 0x00e8402d, 0x00001f40 },
+ { "T15.PCX", 1, 0x00e85f6d, 0x00008b28 },
+ { "T15.SAM", 1, 0x00e8ea95, 0x0000a7d0 },
+ { "T15B.SAM", 1, 0x00e99265, 0x000164d7 },
+ { "T16.BBK", 1, 0x00eaf73c, 0x000027ff },
+ { "T16.LUM", 1, 0x00eb1f3b, 0x00000018 },
+ { "T16.MSK", 1, 0x00eb1f53, 0x00001f40 },
+ { "T16.PCX", 1, 0x00eb3e93, 0x00009110 },
+ { "T17.BBK", 1, 0x00ebcfa3, 0x000056df },
+ { "T17.LUM", 1, 0x00ec2682, 0x00000018 },
+ { "T17.MSK", 1, 0x00ec269a, 0x00001f40 },
+ { "T17.PCX", 1, 0x00ec45da, 0x00007db6 },
+ { "T18.BBK", 1, 0x00ecc390, 0x00006174 },
+ { "T18.LUM", 1, 0x00ed2504, 0x00000018 },
+ { "T18.MSK", 1, 0x00ed251c, 0x00001f40 },
+ { "T18.PCX", 1, 0x00ed445c, 0x000087d6 },
+ { "T19.BBK", 1, 0x00edcc32, 0x0000aec8 },
+ { "T19.LUM", 1, 0x00ee7afa, 0x00000018 },
+ { "T19.MSK", 1, 0x00ee7b12, 0x00001f40 },
+ { "T19.PCX", 1, 0x00ee9a52, 0x0000768e },
+ { "T19.SAM", 1, 0x00ef10e0, 0x000167ea },
+ { "T1B.SAM", 1, 0x00f078ca, 0x0002fa9f },
+ { "T2.BBK", 1, 0x00f37369, 0x00004dea },
+ { "T2.LUM", 1, 0x00f3c153, 0x00000018 },
+ { "T2.MSK", 1, 0x00f3c16b, 0x00001f40 },
+ { "T2.PCX", 1, 0x00f3e0ab, 0x00011404 },
+ { "T2.SAM", 1, 0x00f4f4af, 0x0000aa32 },
+ { "T20.BBK", 1, 0x00f59ee1, 0x0000611c },
+ { "T20.LUM", 1, 0x00f5fffd, 0x00000018 },
+ { "T20.MSK", 1, 0x00f60015, 0x00001f40 },
+ { "T20.PCX", 1, 0x00f61f55, 0x0000852a },
+ { "T20.SAM", 1, 0x00f6a47f, 0x00007f9c },
+ { "T20B.SAM", 1, 0x00f7241b, 0x0000a2bc },
+ { "T21.BBK", 1, 0x00f7c6d7, 0x00000002 },
+ { "T21.PCX", 1, 0x00f7c6d9, 0x0000b7b0 },
+ { "T22.BBK", 1, 0x00f87e89, 0x00000002 },
+ { "T22.PCX", 1, 0x00f87e8b, 0x0000a982 },
+ { "T23.BBK", 1, 0x00f9280d, 0x00005bca },
+ { "T23.LUM", 1, 0x00f983d7, 0x00000018 },
+ { "T23.MSK", 1, 0x00f983ef, 0x00001f40 },
+ { "T23.PCX", 1, 0x00f9a32f, 0x00008200 },
+ { "T24.BBK", 1, 0x00fa252f, 0x0000aaf1 },
+ { "T24.LUM", 1, 0x00fad020, 0x00000018 },
+ { "T24.MSK", 1, 0x00fad038, 0x00001f40 },
+ { "T24.PCX", 1, 0x00faef78, 0x00006f7e },
+ { "T25.BBK", 1, 0x00fb5ef6, 0x0000a631 },
+ { "T25.LUM", 1, 0x00fc0527, 0x00000018 },
+ { "T25.MSK", 1, 0x00fc053f, 0x00001f40 },
+ { "T25.PCX", 1, 0x00fc247f, 0x00008881 },
+ { "T25.SAM", 1, 0x00fcad00, 0x000091ac },
+ { "T26.BBK", 1, 0x00fd3eac, 0x00014578 },
+ { "T26.LUM", 1, 0x00fe8424, 0x00000018 },
+ { "T26.MSK", 1, 0x00fe843c, 0x00001f40 },
+ { "T26.PCX", 1, 0x00fea37c, 0x00012570 },
+ { "T26A.SAM", 1, 0x00ffc8ec, 0x000126a6 },
+ { "T27.BBK", 1, 0x0100ef92, 0x0000a73e },
+ { "T27.LUM", 1, 0x010196d0, 0x00000018 },
+ { "T27.MSK", 1, 0x010196e8, 0x00001f40 },
+ { "T27.PCX", 1, 0x0101b628, 0x000085fa },
+ { "T28.BBK", 1, 0x01023c22, 0x00000002 },
+ { "T28.PCX", 1, 0x01023c24, 0x000017d2 },
+ { "T2B.SAM", 1, 0x010253f6, 0x00021df6 },
+ { "T3.BBK", 1, 0x010471ec, 0x00004b24 },
+ { "T3.LUM", 1, 0x0104bd10, 0x00000018 },
+ { "T3.MSK", 1, 0x0104bd28, 0x00001f40 },
+ { "T3.PCX", 1, 0x0104dc68, 0x0000724c },
+ { "T3.SAM", 1, 0x01054eb4, 0x00006042 },
+ { "T4.BBK", 1, 0x0105aef6, 0x00002dca },
+ { "T4.MSK", 1, 0x0105dcc0, 0x00001f40 },
+ { "T4.PCX", 1, 0x0105fc00, 0x00007566 },
+ { "T5.BBK", 1, 0x01067166, 0x00001ac0 },
+ { "T5.LUM", 1, 0x01068c26, 0x00000018 },
+ { "T5.MSK", 1, 0x01068c3e, 0x00001f40 },
+ { "T5.PCX", 1, 0x0106ab7e, 0x00009509 },
+ { "T5.SAM", 1, 0x01074087, 0x000049aa },
+ { "T5B.SAM", 1, 0x01078a31, 0x00022018 },
+ { "T5C.SAM", 1, 0x0109aa49, 0x00011612 },
+ { "T6.BBK", 1, 0x010ac05b, 0x00007db0 },
+ { "T6.LUM", 1, 0x010b3e0b, 0x00000018 },
+ { "T6.MSK", 1, 0x010b3e23, 0x00001f40 },
+ { "T6.PCX", 1, 0x010b5d63, 0x000096b4 },
+ { "T6.SAM", 1, 0x010bf417, 0x0000f04d },
+ { "T6A.SAM", 1, 0x010ce464, 0x000199ee },
+ { "T6B.PCX", 1, 0x010e7e52, 0x0000ad10 },
+ { "T6B.SAM", 1, 0x010f2b62, 0x00010cba },
+ { "T6C.SAM", 1, 0x0110381c, 0x00015041 },
+ { "T7.BBK", 1, 0x0111885d, 0x0000c781 },
+ { "T7.PCX", 1, 0x01124fde, 0x00006da0 },
+ { "T7.SAM", 1, 0x0112bd7e, 0x000172ea },
+ { "T8.BBK", 1, 0x01143068, 0x00002762 },
+ { "T8.LUM", 1, 0x011457ca, 0x00000018 },
+ { "T8.MSK", 1, 0x011457e2, 0x00001f40 },
+ { "T8.PCX", 1, 0x01147722, 0x0000831b },
+ { "T8.SAM", 1, 0x0114fa3d, 0x00012c01 },
+ { "T9.BBK", 1, 0x0116263e, 0x000029f3 },
+ { "T9.LUM", 1, 0x01165031, 0x00000018 },
+ { "T9.MSK", 1, 0x01165049, 0x00001f40 },
+ { "T9.PCX", 1, 0x01166f89, 0x0000735b },
+ { "T9.SAM", 1, 0x0116e2e4, 0x0000d9e6 },
+ { "TABLET.BBK", 1, 0x0117bcca, 0x00013902 },
+ { "TABLET.PCX", 1, 0x0118f5cc, 0x0000af16 },
+ { "TALLPYG.DOG", 1, 0x0119a4e2, 0x0000034a },
+ { "TAM1.DOG", 1, 0x0119a82c, 0x00001e8a },
+ { "TAM2.DOG", 1, 0x0119c6b6, 0x0000076c },
+ { "TAM3.DOG", 1, 0x0119ce22, 0x000007c2 },
+ { "TAM4.DOG", 1, 0x0119d5e4, 0x0000083c },
+ { "TEMPLE.ACT", 1, 0x0119de20, 0x00005052 },
+ { "TMPD.ACT", 1, 0x011a2e72, 0x0000b00c },
+ { "TRADER.ACT", 1, 0x011ade7e, 0x0001424a },
+ { "V1.BBK", 1, 0x011c20c8, 0x00006724 },
+ { "V1.PCX", 1, 0x011c87ec, 0x000091ea },
+ { "V1.SAM", 1, 0x011d19d6, 0x000061e5 },
+ { "V10.BBK", 1, 0x011d7bbb, 0x000094e8 },
+ { "V10.PCX", 1, 0x011e10a3, 0x0000946c },
+ { "V11.BBK", 1, 0x011ea50f, 0x0000e122 },
+ { "V11.PCX", 1, 0x011f8631, 0x0000946c },
+ { "V2.BBK", 1, 0x01201a9d, 0x00007dfb },
+ { "V2.LUM", 1, 0x01209898, 0x00000018 },
+ { "V2.MSK", 1, 0x012098b0, 0x00001f40 },
+ { "V2.PCX", 1, 0x0120b7f0, 0x0000876c },
+ { "V3.BBK", 1, 0x01213f5c, 0x0000d716 },
+ { "V3.LUM", 1, 0x01221672, 0x00000018 },
+ { "V3.MSK", 1, 0x0122168a, 0x00001f40 },
+ { "V3.PCX", 1, 0x012235ca, 0x00005efa },
+ { "V4.BBK", 1, 0x012294c4, 0x0000571a },
+ { "V4.PCX", 1, 0x0122ebde, 0x00010cd4 },
+ { "V5.BBK", 1, 0x0123f8b2, 0x0001c43f },
+ { "V5.MSK", 1, 0x0125bcf1, 0x00001f40 },
+ { "V5.PCX", 1, 0x0125dc31, 0x00009148 },
+ { "V5.SAM", 1, 0x01266d79, 0x0003953d },
+ { "V5B.SAM", 1, 0x012a02b6, 0x0000ce6f },
+ { "V5C.SAM", 1, 0x012ad125, 0x0000f142 },
+ { "V5D.SAM", 1, 0x012bc267, 0x00000f50 },
+ { "V5E.SAM", 1, 0x012bd1b7, 0x00009352 },
+ { "V5X.SAM", 1, 0x012c6509, 0x0001d7c2 },
+ { "V6.BBK", 1, 0x012e3ccb, 0x0000d716 },
+ { "V6.LUM", 1, 0x012f13e1, 0x00000018 },
+ { "V6.MSK", 1, 0x012f13f9, 0x00001f40 },
+ { "V6.PCX", 1, 0x012f3339, 0x000074ce },
+ { "V7.BBK", 1, 0x012fa807, 0x000177cd },
+ { "V7.PCX", 1, 0x01311fd4, 0x0000a3b4 },
+ { "V8.BBK", 1, 0x0131c388, 0x00006724 },
+ { "V8.PCX", 1, 0x01322aac, 0x0000a8d0 },
+ { "VACUUM.SAM", 1, 0x0132d37c, 0x00009516 },
+ { "WATER.ACT", 1, 0x01336892, 0x00001c02 },
+ { "WEDGE.ACT", 1, 0x01338494, 0x0000390e },
+ { "WEDGE.DOG", 1, 0x0133bda2, 0x000002c4 },
+ { "WEENIE.SAM", 1, 0x0133c066, 0x0000b4d2 },
+ { "WITCH1.DOG", 1, 0x01347538, 0x000012e4 },
+ { "WITCH2.DOG", 1, 0x0134881c, 0x0000088e },
+ { "WITCH3.DOG", 1, 0x013490aa, 0x00000df8 },
+ { "WITCH4.DOG", 1, 0x01349ea2, 0x000002b2 },
+ { "X1.BBK", 1, 0x0134a154, 0x00010e5d },
+ { "X1.PCX", 1, 0x0135afb1, 0x0000cc4b },
+ { "X10.BBK", 1, 0x01367bfc, 0x00009907 },
+ { "X10.PCX", 1, 0x01371503, 0x0000a1b3 },
+ { "X10_JOE.ACT", 1, 0x0137b6b6, 0x0000943a },
+ { "X10_RITA.ACT", 1, 0x01384af0, 0x000076d9 },
+ { "X11.BBK", 1, 0x0138c1c9, 0x00016966 },
+ { "X11.PCX", 1, 0x013a2b2f, 0x0000c160 },
+ { "X11_JOE.ACT", 1, 0x013aec8f, 0x0000872e },
+ { "X11_RITA.ACT", 1, 0x013b73bd, 0x0000a6f2 },
+ { "X2.BBK", 1, 0x013c1aaf, 0x0000df2b },
+ { "X2.PCX", 1, 0x013cf9da, 0x00013ed5 },
+ { "X2_JOE.ACT", 1, 0x013e38af, 0x00008042 },
+ { "X2_RITA.ACT", 1, 0x013eb8f1, 0x0000df02 },
+ { "X3.BBK", 1, 0x013f97f3, 0x00000002 },
+ { "X3.PCX", 1, 0x013f97f5, 0x0000d165 },
+ { "X3_RITA.ACT", 1, 0x0140695a, 0x0000a0fa },
+ { "X4.BBK", 1, 0x01410a54, 0x00004b53 },
+ { "X4.PCX", 1, 0x014155a7, 0x0000b51b },
+ { "X4A.SAM", 1, 0x01420ac2, 0x0001b456 },
+ { "X4B.SAM", 1, 0x0143bf18, 0x0002a1b4 },
+ { "X4_JOE.ACT", 1, 0x014660cc, 0x000088a5 },
+ { "X4_RITA.ACT", 1, 0x0146e971, 0x0000398a },
+ { "X5.BBK", 1, 0x014722fb, 0x000075a0 },
+ { "X5.PCX", 1, 0x0147989b, 0x0000adeb },
+ { "X5_SPARK.ACT", 1, 0x01484686, 0x00006e5a },
+ { "X6.BBK", 1, 0x0148b4e0, 0x0001889e },
+ { "X6.PCX", 1, 0x014a3d7e, 0x0000be75 },
+ { "X6_HUGH.ACT", 1, 0x014afbf3, 0x0000c25a },
+ { "X7.BBK", 1, 0x014bbe4d, 0x00002ada },
+ { "X7.PCX", 1, 0x014be927, 0x00009456 },
+ { "X7A.SAM", 1, 0x014c7d7d, 0x0001b7cb },
+ { "X7B.SAM", 1, 0x014e3548, 0x0003b107 },
+ { "X8.BBK", 1, 0x0151e64f, 0x00032a14 },
+ { "X8.PCX", 1, 0x01551063, 0x00013d4f },
+ { "X9.BBK", 1, 0x01564db2, 0x00028337 },
+ { "X9.PCX", 1, 0x0158d0e9, 0x0000a31c },
+ { "ZOMBIE.ACT", 1, 0x01597405, 0x000078ea },
+ { "ZOMBIE1.DOG", 1, 0x0159ecef, 0x00000f6a },
+ { "ZOMBIE2.DOG", 1, 0x0159fc59, 0x00000c40 }
+};
+#endif
+} // End of namespace Queen
diff --git a/engines/queen/sound.cpp b/engines/queen/sound.cpp
new file mode 100644
index 0000000000..a5c756e366
--- /dev/null
+++ b/engines/queen/sound.cpp
@@ -0,0 +1,239 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/sound.h"
+
+#include "queen/input.h"
+#include "queen/music.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+
+#include "sound/flac.h"
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
+
+#define SB_HEADER_SIZE 110
+#define STOP_MUSIC -1
+
+namespace Queen {
+
+Sound::Sound(Audio::Mixer *mixer, QueenEngine *vm) :
+ _mixer(mixer), _vm(vm), _sfxToggle(true), _speechToggle(true), _musicToggle(true), _lastOverride(0) {
+}
+
+Sound::~Sound() {
+}
+
+Sound *Sound::giveSound(Audio::Mixer *mixer, QueenEngine *vm, uint8 compression) {
+ if (!mixer->isReady())
+ return new SilentSound(mixer, vm);
+
+ switch (compression) {
+ case COMPRESSION_NONE:
+ return new SBSound(mixer, vm);
+ break;
+ case COMPRESSION_MP3:
+#ifndef USE_MAD
+ warning("Using MP3 compressed datafile, but MP3 support not compiled in");
+ return new SilentSound(mixer, vm);
+#else
+ return new MP3Sound(mixer, vm);
+#endif
+ break;
+ case COMPRESSION_OGG:
+#ifndef USE_VORBIS
+ warning("Using OGG compressed datafile, but OGG support not compiled in");
+ return new SilentSound(mixer, vm);
+#else
+ return new OGGSound(mixer, vm);
+#endif
+ break;
+ case COMPRESSION_FLAC:
+#ifndef USE_FLAC
+ warning("Using FLAC compressed datafile, but FLAC support not compiled in");
+ return new SilentSound(mixer, vm);
+#else
+ return new FLACSound(mixer, vm);
+#endif
+ break;
+ default:
+ warning("Unknown compression type");
+ return new SilentSound(mixer, vm);
+ }
+}
+
+void Sound::waitFinished(bool isSpeech) {
+ if (isSpeech)
+ while (_mixer->isSoundHandleActive(_speechHandle))
+ _vm->input()->delay(10);
+ else
+ while (_mixer->isSoundHandleActive(_sfxHandle))
+ _vm->input()->delay(10);
+}
+
+void Sound::playSfx(uint16 sfx, bool isSpeech) {
+ if (isSpeech && !speechOn()) return;
+ else if (!sfxOn()) return;
+
+ if (sfx != 0) {
+ char name[13];
+#ifndef PALMOS_68K
+ strcpy(name, _sfxName[sfx - 1]);
+#else
+ strncpy(name, _sfxName + 10 * (sfx - 1), 10); // saved as 8char + /0/0
+#endif
+ strcat(name, ".SB");
+ waitFinished(isSpeech);
+ if (sfxPlay(name, isSpeech ? &_speechHandle : &_sfxHandle)) {
+ _speechSfxExists = isSpeech;
+ } else {
+ _speechSfxExists = false;
+ }
+ }
+}
+
+void Sound::playSfx(const char *base, bool isSpeech) {
+ if (isSpeech && !speechOn()) return;
+ else if (!sfxOn()) return;
+
+ char name[13];
+ strcpy(name, base);
+ // alter filename to add zeros and append ".SB"
+ for (int i = 0; i < 8; i++) {
+ if (name[i] == ' ')
+ name[i] = '0';
+ }
+ strcat(name, ".SB");
+ waitFinished(isSpeech);
+ if (sfxPlay(name, isSpeech ? &_speechHandle : &_sfxHandle)) {
+ _speechSfxExists = isSpeech;
+ } else {
+ _speechSfxExists = false;
+ }
+}
+
+void Sound::playSong(int16 songNum) {
+ if (songNum <= 0) {
+ _vm->music()->stopSong();
+ return;
+ }
+
+ int16 newTune;
+ if (_vm->resource()->isDemo()) {
+ if (songNum == 17) {
+ _vm->music()->stopSong();
+ return;
+ }
+ newTune = _songDemo[songNum - 1].tuneList[0] - 1;
+ } else {
+ newTune = _song[songNum - 1].tuneList[0] - 1;
+ }
+
+ if (_tune[newTune].sfx[0]) {
+ if (sfxOn())
+ playSfx(_tune[newTune].sfx[0], false);
+ return;
+ }
+
+ if (!musicOn())
+ return;
+
+ int override = (_vm->resource()->isDemo()) ? _songDemo[songNum - 1].override : _song[songNum - 1].override;
+ switch (override) {
+ // Override all songs
+ case 1:
+ break;
+ // Alter song settings (such as volume) and exit
+ case 2:
+ _vm->music()->toggleVChange();
+ default:
+ return;
+ break;
+ }
+
+ _lastOverride = songNum;
+
+ _vm->music()->queueTuneList(newTune);
+ _vm->music()->playMusic();
+}
+
+void Sound::saveState(byte *&ptr) {
+ WRITE_BE_UINT16(ptr, _lastOverride); ptr += 2;
+}
+
+void Sound::loadState(uint32 ver, byte *&ptr) {
+ _lastOverride = (int16)READ_BE_INT16(ptr); ptr += 2;
+}
+
+bool SilentSound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) {
+ return false;
+}
+
+bool SBSound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) {
+ if (_vm->resource()->fileExists(name)) {
+ uint32 size;
+ uint8 *sound = _vm->resource()->loadFile(name, SB_HEADER_SIZE, &size, true);
+ byte flags = Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE;
+ _mixer->playRaw(soundHandle, sound, size, 11025, flags);
+ return true;
+ }
+ return false;
+}
+
+#ifdef USE_MAD
+bool MP3Sound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) {
+ uint32 size;
+ Common::File *f = _vm->resource()->giveCompressedSound(name, &size);
+ if (f) {
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, soundHandle, makeMP3Stream(f, size));
+ return true;
+ }
+ return false;
+}
+#endif
+
+#ifdef USE_VORBIS
+bool OGGSound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) {
+ uint32 size;
+ Common::File *f = _vm->resource()->giveCompressedSound(name, &size);
+ if (f) {
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, soundHandle, makeVorbisStream(f, size));
+ return true;
+ }
+ return false;
+}
+#endif
+
+#ifdef USE_FLAC
+bool FLACSound::sfxPlay(const char *name, Audio::SoundHandle *soundHandle) {
+ uint32 size;
+ Common::File *f = _vm->resource()->giveCompressedSound(name, &size);
+ if (f) {
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, soundHandle, makeFlacStream(f, size));
+ return true;
+ }
+ return false;
+}
+#endif
+
+} //End of namespace Queen
diff --git a/engines/queen/sound.h b/engines/queen/sound.h
new file mode 100644
index 0000000000..d12032e994
--- /dev/null
+++ b/engines/queen/sound.h
@@ -0,0 +1,158 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENSOUND_H
+#define QUEENSOUND_H
+
+#include "common/util.h"
+#include "sound/mixer.h"
+#include "queen/defs.h"
+
+namespace Queen {
+
+class Input;
+class Resource;
+
+struct songData {
+ int16 tuneList[5];
+ int16 volume;
+ int16 tempo;
+ int16 reverb;
+ int16 override;
+ int16 ignore;
+};
+
+struct tuneData {
+ int16 tuneNum[9];
+ int16 sfx[2];
+ int16 mode;
+ int16 delay;
+};
+
+class QueenEngine;
+
+class Sound {
+public:
+ Sound(Audio::Mixer *mixer, QueenEngine *vm);
+ virtual ~Sound();
+ virtual bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle) = 0;
+ static Sound *giveSound(Audio::Mixer *mixer, QueenEngine *vm, uint8 compression);
+ void playSfx(uint16 sfx, bool isSpeech);
+ void playSfx(const char *base, bool isSpeech);
+ void playSong(int16 songNum);
+ void playLastSong() { playSong(_lastOverride); }
+ void stopSpeech() { _mixer->stopHandle(_speechHandle); }
+ void stopSfx() { _mixer->stopHandle(_sfxHandle); }
+
+ bool sfxOn() const { return _sfxToggle; }
+ void sfxToggle(bool val) { _sfxToggle = val; }
+ void toggleSfx() { _sfxToggle ^= true; }
+
+ bool speechOn() const { return _speechToggle; }
+ void speechToggle(bool val) { _speechToggle = val; }
+ void toggleSpeech() { _speechToggle ^= true; }
+
+ bool musicOn() const { return _musicToggle; }
+ void musicToggle(bool val) { _musicToggle = val; }
+ void toggleMusic() { _musicToggle ^= true; }
+
+ bool isSpeechActive() const { return _mixer->isSoundHandleActive(_speechHandle); }
+ bool isSfxActive() const { return _mixer->isSoundHandleActive(_sfxHandle); }
+
+ bool speechSfxExists() const { return _speechSfxExists; }
+
+ int16 lastOverride() const { return _lastOverride; }
+
+ void saveState(byte *&ptr);
+ void loadState(uint32 ver, byte *&ptr);
+
+#ifndef PALMOS_68K
+ static const songData _songDemo[];
+ static const songData _song[];
+ static const tuneData _tuneDemo[];
+ static const tuneData _tune[];
+ static const char *_sfxName[];
+ static const int16 _jungleList[];
+#else
+ static const songData *_songDemo;
+ static const songData *_song;
+ static const tuneData *_tuneDemo;
+ static const tuneData *_tune;
+ static const char *_sfxName;
+ static const int16 *_jungleList;
+#endif
+
+protected:
+ void waitFinished(bool isSpeech);
+
+ Audio::Mixer *_mixer;
+ QueenEngine *_vm;
+
+ bool _sfxToggle;
+ bool _speechToggle;
+ bool _musicToggle;
+ bool _speechSfxExists;
+
+ int16 _lastOverride;
+ Audio::SoundHandle _sfxHandle;
+ Audio::SoundHandle _speechHandle;
+};
+
+class SilentSound : public Sound {
+public:
+ SilentSound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {};
+ bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle);
+};
+
+class SBSound : public Sound {
+public:
+ SBSound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {};
+ bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle);
+};
+
+#ifdef USE_MAD
+class MP3Sound : public Sound {
+public:
+ MP3Sound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {};
+ bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle);
+};
+#endif
+
+#ifdef USE_VORBIS
+class OGGSound : public Sound {
+public:
+ OGGSound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {};
+ bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle);
+};
+#endif
+
+#ifdef USE_FLAC
+class FLACSound : public Sound {
+public:
+ FLACSound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) {};
+ bool sfxPlay(const char *name, Audio::SoundHandle *soundHandle);
+};
+#endif // #ifdef USE_FLAC
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/state.cpp b/engines/queen/state.cpp
new file mode 100644
index 0000000000..5709bb07c6
--- /dev/null
+++ b/engines/queen/state.cpp
@@ -0,0 +1,130 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/state.h"
+
+namespace Queen {
+
+Direction State::findDirection(uint16 state) {
+ static const Direction sd[] = {
+ DIR_BACK,
+ DIR_RIGHT,
+ DIR_LEFT,
+ DIR_FRONT
+ };
+ return sd[(state >> 2) & 3];
+}
+
+StateTalk State::findTalk(uint16 state) {
+ return (state & (1 << 9)) ? STATE_TALK_TALK : STATE_TALK_MUTE;
+}
+
+StateGrab State::findGrab(uint16 state) {
+ static const StateGrab sg[] = {
+ STATE_GRAB_NONE,
+ STATE_GRAB_DOWN,
+ STATE_GRAB_UP,
+ STATE_GRAB_MID
+ };
+ return sg[state & 3];
+}
+
+StateOn State::findOn(uint16 state) {
+ return (state & (1 << 8)) ? STATE_ON_ON : STATE_ON_OFF;
+}
+
+Verb State::findDefaultVerb(uint16 state) {
+ static const Verb sdv[] = {
+ VERB_NONE,
+ VERB_OPEN,
+ VERB_NONE,
+ VERB_CLOSE,
+
+ VERB_NONE,
+ VERB_NONE,
+ VERB_LOOK_AT,
+ VERB_MOVE,
+
+ VERB_GIVE,
+ VERB_TALK_TO,
+ VERB_NONE,
+ VERB_NONE,
+
+ VERB_USE,
+ VERB_NONE,
+ VERB_PICK_UP,
+ VERB_NONE
+ };
+ return sdv[(state >> 4) & 0xF];
+}
+
+StateUse State::findUse(uint16 state) {
+ return (state & (1 << 10)) ? STATE_USE : STATE_USE_ON;
+}
+
+void State::alterOn(uint16 *objState, StateOn state) {
+ switch (state) {
+ case STATE_ON_ON:
+ *objState |= (1 << 8);
+ break;
+ case STATE_ON_OFF:
+ *objState &= ~(1 << 8);
+ break;
+ }
+}
+
+void State::alterDefaultVerb(uint16 *objState, Verb v) {
+ uint16 val;
+ switch (v) {
+ case VERB_OPEN:
+ val = 1;
+ break;
+ case VERB_CLOSE:
+ val = 3;
+ break;
+ case VERB_MOVE:
+ val = 7;
+ break;
+ case VERB_GIVE:
+ val = 8;
+ break;
+ case VERB_USE:
+ val = 12;
+ break;
+ case VERB_PICK_UP:
+ val = 14;
+ break;
+ case VERB_TALK_TO:
+ val = 9;
+ break;
+ case VERB_LOOK_AT:
+ val = 6;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ *objState = (*objState & ~0xF0) | (val << 4);
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/state.h b/engines/queen/state.h
new file mode 100644
index 0000000000..d7be47fbc8
--- /dev/null
+++ b/engines/queen/state.h
@@ -0,0 +1,113 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENSTATE_H
+#define QUEENSTATE_H
+
+#include "common/util.h"
+#include "queen/defs.h"
+
+namespace Queen {
+
+
+enum StateTalk {
+ STATE_TALK_TALK,
+ STATE_TALK_MUTE
+};
+
+enum StateGrab {
+ STATE_GRAB_NONE,
+ STATE_GRAB_DOWN,
+ STATE_GRAB_UP,
+ STATE_GRAB_MID
+};
+
+enum StateOn {
+ STATE_ON_ON,
+ STATE_ON_OFF
+};
+
+enum StateUse {
+ STATE_USE,
+ STATE_USE_ON
+};
+
+
+/*!
+ Each object/item in game has a state field.
+ (refer to ObjectData and ItemData).
+
+ <table>
+ <tr>
+ <td>Name</td>
+ <td>Bits</td>
+ <td>Description</td>
+ </tr>
+ <tr>
+ <td>USE</td>
+ <td>10</td>
+ <td>Use</td>
+ </tr>
+ <tr>
+ <td>TALK</td>
+ <td>9</td>
+ <td>Talk</td>
+ </tr>
+ <tr>
+ <td>ON</td>
+ <td>8</td>
+ <td>On/Off</td>
+ </tr>
+ <tr>
+ <td>DEF</td>
+ <td>7,6,5,4</td>
+ <td>Default verb command</td>
+ </tr>
+ <tr>
+ <td>DIR</td>
+ <td>3,2</td>
+ <td>Direction to face for the object</td>
+ </tr>
+ <tr>
+ <td>GRAB</td>
+ <td>1,0</td>
+ <td>Grab Direction</td>
+ </tr>
+ </table>
+*/
+struct State {
+
+ static Direction findDirection(uint16 state);
+ static StateTalk findTalk(uint16 state);
+ static StateGrab findGrab(uint16 state);
+ static StateOn findOn(uint16 state);
+ static Verb findDefaultVerb(uint16 state);
+ static StateUse findUse(uint16 state);
+
+ static void alterOn(uint16 *objState, StateOn state);
+ static void alterDefaultVerb(uint16 *objState, Verb v);
+};
+
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/structs.h b/engines/queen/structs.h
new file mode 100644
index 0000000000..4f43950fa4
--- /dev/null
+++ b/engines/queen/structs.h
@@ -0,0 +1,580 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENSTRUCTS_H
+#define QUEENSTRUCTS_H
+
+#include "queen/defs.h"
+
+namespace Queen {
+
+struct Box {
+ int16 x1, y1, x2, y2;
+
+ Box()
+ : x1(0), y1(0), x2(0), y2(0) {
+ }
+
+ Box(int16 xx1, int16 yy1, int16 xx2, int16 yy2)
+ : x1(xx1), y1(yy1), x2(xx2), y2(yy2) {
+ }
+
+ void readFromBE(byte *&ptr) {
+ x1 = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ y1 = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ x2 = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ y2 = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ }
+
+ void writeToBE(byte *&ptr) {
+ WRITE_BE_UINT16(ptr, x1); ptr += 2;
+ WRITE_BE_UINT16(ptr, y1); ptr += 2;
+ WRITE_BE_UINT16(ptr, x2); ptr += 2;
+ WRITE_BE_UINT16(ptr, y2); ptr += 2;
+ }
+
+ int16 xDiff() const {
+ return x2 - x1;
+ }
+
+ int16 yDiff() const {
+ return y2 - y1;
+ }
+
+ bool intersects(int16 x, int16 y, uint16 w, uint16 h) const {
+ return (x + w > x1) && (y + h > y1) && (x <= x2) && (y <= y2);
+ }
+
+ bool contains(int16 x, int16 y) const {
+ return (x >= x1) && (x <= x2) && (y >= y1) && (y <= y2);
+ }
+
+ bool operator==(const Box &b) const {
+ return (x1 == b.x1) && (x2 == b.x2) && (y1 == b.y1) && (y2 == b.y2);
+ }
+};
+
+
+struct Area {
+ //! bitmask of connected areas
+ int16 mapNeighbours;
+ //! coordinates defining area limits
+ Box box;
+ //! scaling factors for bobs actors
+ uint16 bottomScaleFactor, topScaleFactor;
+ //! entry in ObjectData, object lying in this area
+ uint16 object;
+
+ void readFromBE(byte *&ptr) {
+ mapNeighbours = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ box.readFromBE(ptr);
+ bottomScaleFactor = READ_BE_UINT16(ptr); ptr += 2;
+ topScaleFactor = READ_BE_UINT16(ptr); ptr += 2;
+ object = READ_BE_UINT16(ptr); ptr += 2;
+ }
+
+ void writeToBE(byte *&ptr) {
+ WRITE_BE_UINT16(ptr, mapNeighbours); ptr += 2;
+ box.writeToBE(ptr);
+ WRITE_BE_UINT16(ptr, bottomScaleFactor); ptr += 2;
+ WRITE_BE_UINT16(ptr, topScaleFactor); ptr += 2;
+ WRITE_BE_UINT16(ptr, object); ptr += 2;
+ }
+
+ uint16 calcScale(int16 y) const {
+ uint16 dy = box.yDiff();
+ int16 ds = scaleDiff();
+ uint16 scale = 0;
+
+ if (dy) // Prevent division-by-zero
+ scale = ((((y - box.y1) * 100) / dy) * ds) / 100 + bottomScaleFactor;
+
+ if (scale == 0)
+ scale = 100;
+
+ return scale;
+ }
+
+ int16 scaleDiff() const {
+ return (int16)(topScaleFactor - bottomScaleFactor);
+ }
+};
+
+
+struct WalkOffData {
+ //! entry in ObjectData
+ int16 entryObj;
+ //! coordinates to reach
+ uint16 x, y;
+
+ void readFromBE(byte *&ptr) {
+ entryObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ x = READ_BE_UINT16(ptr); ptr += 2;
+ y = READ_BE_UINT16(ptr); ptr += 2;
+ }
+
+ void writeToBE(byte *&ptr) {
+ WRITE_BE_UINT16(ptr, entryObj); ptr += 2;
+ WRITE_BE_UINT16(ptr, x); ptr += 2;
+ WRITE_BE_UINT16(ptr, y); ptr += 2;
+ }
+};
+
+
+struct GraphicData {
+ //! coordinates of object
+ uint16 x, y;
+ //! bank bobframes
+ /*!
+ <table>
+ <tr>
+ <td>lastFrame == 0</td>
+ <td>non-animated bob (one frame)</td>
+ </tr>
+ <tr>
+ <td>lastFrame < 0</td>
+ <td>rebound animation</td>
+ </tr>
+ <tr>
+ <td>firstFrame < 0</td>
+ <td>BobSlot::animString (animation is described by a string)</td>
+ </tr>
+ <tr>
+ <td>firstFrame > 0</td>
+ <td>BobSlot::animNormal (animation is a sequence of frames)</td>
+ </tr>
+ </table>
+ */
+ int16 firstFrame, lastFrame;
+ //! moving speed of object
+ uint16 speed;
+
+ void readFromBE(byte *&ptr) {
+ x = READ_BE_UINT16(ptr); ptr += 2;
+ y = READ_BE_UINT16(ptr); ptr += 2;
+ firstFrame = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ lastFrame = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ speed = READ_BE_UINT16(ptr); ptr += 2;
+ }
+};
+
+
+struct ObjectData {
+ //! entry in OBJECT_NAME (<0: object is hidden, 0: object has been deleted)
+ int16 name;
+ //! coordinates of object
+ uint16 x, y;
+ //! entry in OBJECT_DESCR
+ uint16 description;
+ //! associated object
+ int16 entryObj;
+ //! room in which this object is available
+ uint16 room;
+ //! state of the object (grab direction, on/off, default command...)
+ uint16 state;
+ //! entry in GraphicData
+ /*!
+ <table>
+ <tr>
+ <td>value</td>
+ <td>description</td>
+ </tr>
+ <tr>
+ <td>]-4000..-10]</td>
+ <td>graphic image turned off</td>
+ </tr>
+ <tr>
+ <td>-4</td>
+ <td>person object (right facing)</td>
+ </tr>
+ <tr>
+ <td>-3</td>
+ <td>person object (left facing)</td>
+ </tr>
+ <tr>
+ <td>-2</td>
+ <td>animated bob (off)</td>
+ </tr>
+ <tr>
+ <td>-1</td>
+ <td>static bob (off)</td>
+ </tr>
+ <tr>
+ <td>0</td>
+ <td>object deleted</td>
+ </tr>
+ <tr>
+ <td>]0..5000]</td>
+ <td>static or animated bob (on)</td>
+ </tr>
+ <tr>
+ <td>]5000.. [</td>
+ <td>'paste down' bob</td>
+ </tr>
+ </table>
+ */
+ int16 image;
+
+ void readFromBE(byte *&ptr) {
+ name = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ x = READ_BE_UINT16(ptr); ptr += 2;
+ y = READ_BE_UINT16(ptr); ptr += 2;
+ description = READ_BE_UINT16(ptr); ptr += 2;
+ entryObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ room = READ_BE_UINT16(ptr); ptr += 2;
+ state = READ_BE_UINT16(ptr); ptr += 2;
+ image = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ }
+
+ void writeToBE(byte *&ptr) {
+ WRITE_BE_UINT16(ptr, name); ptr += 2;
+ WRITE_BE_UINT16(ptr, x); ptr += 2;
+ WRITE_BE_UINT16(ptr, y); ptr += 2;
+ WRITE_BE_UINT16(ptr, description); ptr += 2;
+ WRITE_BE_UINT16(ptr, entryObj); ptr += 2;
+ WRITE_BE_UINT16(ptr, room); ptr += 2;
+ WRITE_BE_UINT16(ptr, state); ptr += 2;
+ WRITE_BE_UINT16(ptr, image); ptr += 2;
+ }
+};
+
+
+struct ObjectDescription {
+ //! entry in ObjectData or ItemData
+ uint16 object;
+ //! type of the description
+ /*!
+ refer to select.c l.75-101
+ <table>
+ <tr>
+ <td>value</td>
+ <td>description</td>
+ </tr>
+ <tr>
+ <td>0</td>
+ <td>random but starts at first description</td>
+ <tr>
+ <td>1</td>
+ <td>random</td>
+ </tr>
+ <tr>
+ <td>2</td>
+ <td>sequential with loop</td>
+ </tr>
+ <tr>
+ <td>3</td>
+ <td>sequential and set description to last</td>
+ </tr>
+ </table>
+ */
+ uint16 type;
+ //! last entry possible in OBJECT_DESCR for this object
+ uint16 lastDescription;
+ //! last description number used (in order to avoid re-using it)
+ uint16 lastSeenNumber;
+
+ void readFromBE(byte *&ptr) {
+ object = READ_BE_UINT16(ptr); ptr += 2;
+ type = READ_BE_UINT16(ptr); ptr += 2;
+ lastDescription = READ_BE_UINT16(ptr); ptr += 2;
+ lastSeenNumber = READ_BE_UINT16(ptr); ptr += 2;
+ }
+
+ void writeToBE(byte *&ptr) {
+ WRITE_BE_UINT16(ptr, object); ptr += 2;
+ WRITE_BE_UINT16(ptr, type); ptr += 2;
+ WRITE_BE_UINT16(ptr, lastDescription); ptr += 2;
+ WRITE_BE_UINT16(ptr, lastSeenNumber); ptr += 2;
+ }
+};
+
+
+struct ItemData {
+ //! entry in OBJECT_NAME
+ int16 name;
+ //! entry in OBJECT_DESCR
+ uint16 description;
+ //! state of the object
+ uint16 state;
+ //! bank bobframe
+ uint16 frame;
+ //! entry in OBJECT_DESCR (>0 if available)
+ int16 sfxDescription;
+
+ void readFromBE(byte *&ptr) {
+ name = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ description = READ_BE_UINT16(ptr); ptr += 2;
+ state = READ_BE_UINT16(ptr); ptr += 2;
+ frame = READ_BE_UINT16(ptr); ptr += 2;
+ sfxDescription = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ }
+
+ void writeToBE(byte *&ptr) {
+ WRITE_BE_UINT16(ptr, name); ptr += 2;
+ WRITE_BE_UINT16(ptr, description); ptr += 2;
+ WRITE_BE_UINT16(ptr, state); ptr += 2;
+ WRITE_BE_UINT16(ptr, frame); ptr += 2;
+ WRITE_BE_UINT16(ptr, sfxDescription); ptr += 2;
+ }
+};
+
+
+struct ActorData {
+ //! room in which the actor is
+ int16 room;
+ //! bob number associated to this actor
+ int16 bobNum;
+ //! entry in ACTOR_NAME
+ uint16 name;
+ //! gamestate entry/value, actor is valid if GAMESTATE[slot] == value
+ int16 gsSlot, gsValue;
+ //! spoken text color
+ uint16 color;
+ //! bank bobframe for standing position of the actor
+ uint16 bobFrameStanding;
+ //! initial coordinates in the room
+ uint16 x, y;
+ //! entry in ACTOR_ANIM
+ uint16 anim;
+ //! bank to use to load the actor file
+ uint16 bankNum;
+ //! entry in ACTOR_FILE
+ uint16 file;
+
+ void readFromBE(byte *&ptr) {
+ room = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ bobNum = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ name = READ_BE_UINT16(ptr); ptr += 2;
+ gsSlot = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ gsValue = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ color = READ_BE_UINT16(ptr); ptr += 2;
+ bobFrameStanding = READ_BE_UINT16(ptr); ptr += 2;
+ x = READ_BE_UINT16(ptr); ptr += 2;
+ y = READ_BE_UINT16(ptr); ptr += 2;
+ anim = READ_BE_UINT16(ptr); ptr += 2;
+ bankNum = READ_BE_UINT16(ptr); ptr += 2;
+ file = READ_BE_UINT16(ptr); ptr += 2;
+ // Fix the actor data (see queen.c - l.1518-1519). When there is no
+ // valid actor file, we must load the data from the objects room bank.
+ // This bank has number 15 (not 10 as in the data files).
+ if (file == 0) {
+ bankNum = 15;
+ }
+ }
+};
+
+
+struct CmdListData {
+ //! action to perform
+ Verb verb;
+ //! first object used in the action
+ int16 nounObj1;
+ //! second object used in the action
+ int16 nounObj2;
+ //! song to play (>0: playbefore, <0: playafter)
+ int16 song;
+ //! if set, P2_SET_AREAS must be called (using CmdArea)
+ bool setAreas;
+ //! if set, P3_SET_OBJECTS must be called (using CmdObject)
+ bool setObjects;
+ //! if set, P4_SET_ITEMS must be called (using CmdInventory)
+ bool setItems;
+ //! if set, P1_SET_CONDITIONS must be called (using CmdGameState)
+ bool setConditions;
+ //! graphic image order
+ int16 imageOrder;
+ //! special section to execute (refer to execute.c l.423-451)
+ int16 specialSection;
+
+ void readFromBE(byte *&ptr) {
+ verb = (Verb)READ_BE_UINT16(ptr); ptr += 2;
+ nounObj1 = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ nounObj2 = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ song = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ setAreas = READ_BE_UINT16(ptr) != 0; ptr += 2;
+ setObjects = READ_BE_UINT16(ptr) != 0; ptr += 2;
+ setItems = READ_BE_UINT16(ptr) != 0; ptr += 2;
+ setConditions = READ_BE_UINT16(ptr) != 0; ptr += 2;
+ imageOrder = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ specialSection = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ }
+
+ bool match(const Verb& v, int16 obj1, int16 obj2) const {
+ return verb == v && nounObj1 == obj1 && nounObj2 == obj2;
+ }
+};
+
+
+struct CmdArea {
+ //! CmdListData number
+ int16 id;
+ //! area to turn off/on (<0: off, >0: on)
+ int16 area;
+ //! room in which the area must be changed
+ uint16 room;
+
+ void readFromBE(byte *&ptr) {
+ id = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ area = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ room = READ_BE_UINT16(ptr); ptr += 2;
+ }
+};
+
+
+struct CmdObject {
+ //! CmdListData number
+ int16 id;
+ //! >0: show, <0: hide
+ int16 dstObj;
+ //! >0: copy from srcObj, 0: nothing, -1: delete dstObj
+ int16 srcObj;
+
+ void readFromBE(byte *&ptr) {
+ id = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ dstObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ srcObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ }
+};
+
+
+struct CmdInventory {
+ //! CmdListData number
+ int16 id;
+ //! <0: delete, >0: add
+ int16 dstItem;
+ //! >0: valid
+ int16 srcItem;
+
+ void readFromBE(byte *&ptr) {
+ id = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ dstItem = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ srcItem = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ }
+};
+
+
+struct CmdGameState {
+ //! CmdListData number
+ int16 id;
+ int16 gameStateSlot;
+ int16 gameStateValue;
+ uint16 speakValue;
+
+ void readFromBE(byte *&ptr) {
+ id = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ gameStateSlot = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ gameStateValue = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ speakValue = READ_BE_UINT16(ptr); ptr += 2;
+ }
+};
+
+
+struct FurnitureData {
+ //! room in which the furniture are
+ int16 room;
+ //! furniture object number
+ /*!
+ <table>
+ <tr>
+ <td>range</td>
+ <td>type</td>
+ </tr>
+ <tr>
+ <td>]0..5000]</td>
+ <td>static or animated</td>
+ </tr>
+ <tr>
+ <td>]5000..[</td>
+ <td>paste down</td>
+ </tr>
+ </table>
+ */
+ int16 objNum;
+
+ void readFromBE(byte *&ptr) {
+ room = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ objNum = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ }
+};
+
+
+struct GraphicAnim {
+ int16 keyFrame;
+ int16 frame;
+ uint16 speed;
+
+ void readFromBE(byte *&ptr) {
+ keyFrame = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ frame = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ speed = READ_BE_UINT16(ptr); ptr += 2;
+ }
+};
+
+
+struct AnimFrame {
+ uint16 frame;
+ uint16 speed;
+};
+
+
+struct Person {
+ //! actor settings to use
+ const ActorData *actor;
+ //! actor name
+ const char *name;
+ //! animation string
+ const char *anim;
+ //! current frame
+ uint16 bobFrame;
+};
+
+
+struct TalkSelected {
+ bool hasTalkedTo;
+ int16 values[4];
+
+ void readFromBE(byte *&ptr) {
+ hasTalkedTo = READ_BE_UINT16(ptr) != 0; ptr += 2;
+ for (int i = 0; i < 4; i++) {
+ values[i] = (int16)READ_BE_UINT16(ptr); ptr += 2;
+ }
+ }
+
+ void writeToBE(byte *&ptr) {
+ WRITE_BE_UINT16(ptr, (uint16)hasTalkedTo); ptr += 2;
+ for (int i = 0; i < 4; i++) {
+ WRITE_BE_UINT16(ptr, values[i]); ptr += 2;
+ }
+ }
+};
+
+
+struct BobFrame {
+ uint16 width, height;
+ uint16 xhotspot, yhotspot;
+ uint8 *data;
+};
+
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/talk.cpp b/engines/queen/talk.cpp
new file mode 100644
index 0000000000..27399ab0cf
--- /dev/null
+++ b/engines/queen/talk.cpp
@@ -0,0 +1,1812 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/talk.h"
+
+#include "queen/bankman.h"
+#include "queen/display.h"
+#include "queen/graphics.h"
+#include "queen/grid.h"
+#include "queen/input.h"
+#include "queen/logic.h"
+#include "queen/queen.h"
+#include "queen/resource.h"
+#include "queen/sound.h"
+#include "queen/state.h"
+#include "queen/walk.h"
+
+#include "common/file.h"
+
+namespace Queen {
+
+#ifdef PALMOS_68K
+static const Talk::SpeechParameters *_speechParameters;
+#endif
+
+void Talk::talk(
+ const char *filename,
+ int personInRoom,
+ char *cutawayFilename,
+ QueenEngine *vm) {
+ Talk *talk = new Talk(vm);
+ talk->talk(filename, personInRoom, cutawayFilename);
+ delete talk;
+}
+
+bool Talk::speak(
+ const char *sentence,
+ Person *person,
+ const char *voiceFilePrefix,
+ QueenEngine *vm) {
+ Talk *talk = new Talk(vm);
+ bool result;
+ if (sentence)
+ result = talk->speak(sentence, person, voiceFilePrefix);
+ else
+ result = false;
+ delete talk;
+ return result;
+}
+
+Talk::Talk(QueenEngine *vm)
+ : _vm(vm), _fileData(NULL) {
+ _vm->input()->talkQuitReset();
+}
+
+Talk::~Talk() {
+ delete[] _fileData;
+}
+
+void Talk::talk(const char *filename, int personInRoom, char *cutawayFilename) {
+ int i;
+ _oldSelectedSentenceIndex = 0;
+ _oldSelectedSentenceValue = 0;
+
+ debug(6, "----- talk(\"%s\") -----", filename);
+
+ cutawayFilename[0] = '\0';
+
+ load(filename);
+
+ Person person;
+ memset(&person, 0, sizeof(Person));
+ _vm->logic()->initPerson(personInRoom, "", false, &person);
+
+ if (NULL == person.name) {
+ error("Invalid person object");
+ }
+
+ int16 oldLevel = 0;
+ bool personWalking = false;
+
+ // Lines 828-846 in talk.c
+ for (i = 1; i <= 4; i++) {
+ if (selectedValue(i) > 0) {
+ // This option has been redefined so display new dialogue option
+ _dialogueTree[1][i].head = selectedValue(i);
+ } else if (selectedValue(i) == -1) {
+ // Already selected so don't redisplay
+ if (_dialogueTree[1][i].gameStateIndex >= 0) {
+ _dialogueTree[1][i].head = -1;
+ _dialogueTree[1][i].dialogueNodeValue1 = -1;
+ _dialogueTree[1][i].gameStateIndex = -1;
+ _dialogueTree[1][i].gameStateValue = -1;
+ }
+ }
+ }
+
+ initialTalk();
+
+ // Lines 906-? in talk.c
+ _vm->display()->showMouseCursor(true);
+
+ int16 level=1, retval=0;
+ int16 head = _dialogueTree[level][0].head;
+
+ // TODO: split this loop in several functions
+ while (retval != -1) {
+ char otherVoiceFilePrefix[MAX_STRING_SIZE];
+
+ _talkString[0][0] = '\0';
+
+ if (hasTalkedTo() && head == 1)
+ strcpy(_talkString[0], _person2String);
+ else
+ findDialogueString(_person1PtrOff, head, _pMax, _talkString[0]);
+
+ if (hasTalkedTo() && head == 1)
+ sprintf(otherVoiceFilePrefix, "%2dXXXXP", _talkKey);
+ else
+ sprintf(otherVoiceFilePrefix, "%2d%4xP", _talkKey, head);
+
+ if (_talkString[0][0] == '\0' && retval > 1) {
+ findDialogueString(_person1PtrOff, retval, _pMax, _talkString[0]);
+ sprintf(otherVoiceFilePrefix,"%2d%4xP", _talkKey, retval);
+ }
+
+ // Joe dialogue
+
+ for (i = 1; i <= 4; i++) {
+ findDialogueString(_joePtrOff, _dialogueTree[level][i].head, _jMax, _talkString[i]);
+
+ int16 index = _dialogueTree[level][i].gameStateIndex;
+
+ if (index < 0 && _vm->logic()->gameState(ABS(index)) != _dialogueTree[level][i].gameStateValue)
+ _talkString[i][0] = '\0';
+
+ sprintf(_joeVoiceFilePrefix[i], "%2d%4xJ", _talkKey, _dialogueTree[level][i].head);
+ }
+
+ // Check to see if (all the dialogue options have been selected.
+ // if this is the case, and the last one left is the exit option,
+ // then automatically set S to that and exit.
+
+ int choicesLeft = 0;
+ int selectedSentence = 0;
+
+ for (i = 1; i <= 4; i++) {
+ if (_talkString[i][0] != '\0') {
+ choicesLeft++;
+ selectedSentence = i;
+ }
+ }
+
+ debug(6, "choicesLeft = %i", choicesLeft);
+
+ if (1 == choicesLeft) {
+ // Automatically run the final dialogue option
+ if (speak(_talkString[0], &person, otherVoiceFilePrefix))
+ personWalking = true;
+
+ if (_vm->input()->talkQuit())
+ break;
+
+ speak(_talkString[selectedSentence], NULL, _joeVoiceFilePrefix[selectedSentence]);
+ } else {
+ if (person.actor->bobNum > 0) {
+ speak(_talkString[0], &person, otherVoiceFilePrefix);
+ selectedSentence = selectSentence();
+ } else {
+ warning("bobBum is %i", person.actor->bobNum);
+ selectedSentence = 0;
+ }
+ }
+
+ if (_vm->input()->talkQuit())
+ break;
+
+ retval = _dialogueTree[level][selectedSentence].dialogueNodeValue1;
+ head = _dialogueTree[level][selectedSentence].head;
+ oldLevel = level;
+ level = 0;
+
+ // Set LEVEL to the selected child in dialogue tree
+
+ for (i = 1; i <= _levelMax; i++)
+ if (_dialogueTree[i][0].head == head)
+ level = i;
+
+ if (0 == level) {
+ // No new level has been selected, so lets set LEVEL to the
+ // tree path pointed to by the RETVAL
+
+ for (i = 1; i <= _levelMax; i++)
+ for (int j = 0; j <= 5; j++)
+ if (_dialogueTree[i][j].head == retval)
+ level = i;
+
+ disableSentence(oldLevel, selectedSentence);
+ } else { // 0 != level
+ // Check to see if Person Return value is positive, if it is, then
+ // change the selected dialogue option to the Return value
+
+ if (_dialogueTree[level][0].dialogueNodeValue1 > 0) {
+ if (1 == oldLevel) {
+ _oldSelectedSentenceIndex = selectedSentence;
+ _oldSelectedSentenceValue = selectedValue(selectedSentence);
+ selectedValue(selectedSentence, _dialogueTree[level][0].dialogueNodeValue1);
+ }
+
+ _dialogueTree[oldLevel][selectedSentence].head = _dialogueTree[level][0].dialogueNodeValue1;
+ _dialogueTree[level][0].dialogueNodeValue1 = -1;
+ } else {
+ disableSentence(oldLevel, selectedSentence);
+ }
+ }
+
+ // Check selected person to see if any Gamestates need setting
+
+ int16 index = _dialogueTree[level][0].gameStateIndex;
+ if (index > 0)
+ _vm->logic()->gameState(index, _dialogueTree[level][0].gameStateValue);
+
+ // if the selected dialogue line has a POSITIVE game state value
+ // then set gamestate to Value = TALK(OLDLEVEL,S,3)
+
+ index = _dialogueTree[oldLevel][selectedSentence].gameStateIndex;
+ if (index > 0)
+ _vm->logic()->gameState(index, _dialogueTree[oldLevel][selectedSentence].gameStateValue);
+
+ // check to see if person has something final to say
+ if (-1 == retval) {
+ findDialogueString(_person1PtrOff, head, _pMax, _talkString[0]);
+ if (_talkString[0][0] != '\0') {
+ sprintf(otherVoiceFilePrefix, "%2d%4xP", _talkKey, head);
+ if (speak(_talkString[0], &person, otherVoiceFilePrefix))
+ personWalking = true;
+ }
+ }
+ }
+
+ cutawayFilename[0] = '\0';
+
+ for (i = 0; i < 2; i++) {
+ if (_gameState[i] > 0) {
+ if (_vm->logic()->gameState(_gameState[i]) == _testValue[i]) {
+ if (_itemNumber[i] > 0)
+ _vm->logic()->inventoryInsertItem(_itemNumber[i]);
+ else
+ _vm->logic()->inventoryDeleteItem(ABS(_itemNumber[i]));
+ }
+ }
+ }
+
+ _vm->grid()->setupPanel();
+
+ uint16 offset = _cutawayPtrOff;
+
+ int16 cutawayGameState = (int16)READ_BE_INT16(_fileData + offset); offset += 2;
+ int16 cutawayTestValue = (int16)READ_BE_INT16(_fileData + offset); offset += 2;
+
+ if (_vm->logic()->gameState(cutawayGameState) == cutawayTestValue) {
+ getString(_fileData, offset, cutawayFilename, 20);
+ if (cutawayFilename[0]) {
+ //CR 2 - 7/3/95, If we're executing a cutaway scene, then make sure
+ // Joe can talk, so set TALKQUIT to 0 just in case we exit on the
+ // line that set's the cutaway game states.
+ _vm->input()->talkQuitReset();
+ }
+ }
+ if (_vm->input()->talkQuit()) {
+ if (_oldSelectedSentenceIndex > 0)
+ selectedValue(_oldSelectedSentenceIndex, _oldSelectedSentenceValue);
+ _vm->input()->talkQuitReset();
+ _vm->display()->clearTexts(0, 198);
+ _vm->logic()->makeJoeSpeak(15, false);
+ } else {
+ setHasTalkedTo();
+ }
+
+ _vm->logic()->joeFace();
+
+ if (cutawayFilename[0] == '\0') {
+ BobSlot *pbs = _vm->graphics()->bob(person.actor->bobNum);
+
+ pbs->x = person.actor->x;
+ pbs->y = person.actor->y;
+
+ // Better kick start the persons anim sequence
+ _vm->graphics()->resetPersonAnim(person.actor->bobNum);
+ }
+
+ _vm->logic()->joeWalk(JWM_NORMAL);
+}
+
+void Talk::disableSentence(int oldLevel, int selectedSentence) {
+ // Mark off selected option
+
+ if (1 == oldLevel) {
+ if (_dialogueTree[oldLevel][selectedSentence].dialogueNodeValue1 != -1) {
+ // Make sure choice is not exit option
+ _oldSelectedSentenceIndex = selectedSentence;
+ _oldSelectedSentenceValue = selectedValue(selectedSentence);
+ selectedValue(selectedSentence, -1);
+ }
+ }
+
+ // Cancel selected dialogue line, so that its no longer displayed
+ _dialogueTree[oldLevel][selectedSentence].head = -1;
+ _dialogueTree[oldLevel][selectedSentence].dialogueNodeValue1 = -1;
+}
+
+void Talk::findDialogueString(uint16 offset, int16 id, int16 max, char *str) {
+ str[0] = '\0';
+ for (int i = 1; i <= max; i++) {
+ offset += 2;
+ int16 currentId = (int16)READ_BE_INT16(_fileData + offset);
+ offset += 2;
+ if (id == currentId) {
+ getString(_fileData, offset, str, MAX_STRING_LENGTH, 4);
+ break;
+ } else {
+ getString(_fileData, offset, NULL, MAX_STRING_LENGTH, 4);
+ }
+ }
+}
+
+byte *Talk::loadDialogFile(const char *filename) {
+ static const struct {
+ const char *filename;
+ Language lang;
+ } dogFiles[] = {
+ { "chief1.dog", FRENCH },
+ { "chief2.dog", FRENCH },
+ { "bud1.dog", ITALIAN }
+ };
+ for (int i = 0; i < ARRAYSIZE(dogFiles); ++i) {
+ if (!scumm_stricmp(filename, dogFiles[i].filename) &&
+ _vm->resource()->getLanguage() == dogFiles[i].lang) {
+ Common::File fdog;
+ fdog.open(filename);
+ if (fdog.isOpen()) {
+ debug(6, "Loading dog file '%s' from game data path", filename);
+ uint32 size = fdog.size() - DOG_HEADER_SIZE;
+ byte *buf = new byte[size];
+ fdog.seek(DOG_HEADER_SIZE);
+ fdog.read(buf, size);
+ return buf;
+ }
+ }
+ }
+ return _vm->resource()->loadFile(filename, DOG_HEADER_SIZE);
+}
+
+void Talk::load(const char *filename) {
+ int i;
+ byte *ptr = _fileData = loadDialogFile(filename);
+ bool canQuit;
+
+ // Load talk header
+
+ _levelMax = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ if (_levelMax < 0) {
+ _levelMax = -_levelMax;
+ canQuit = false;
+ } else {
+ canQuit = true;
+ }
+
+ _uniqueKey = (int16)READ_BE_INT16(ptr); ptr += 2;
+ _talkKey = (int16)READ_BE_INT16(ptr); ptr += 2;
+ _jMax = (int16)READ_BE_INT16(ptr); ptr += 2;
+ _pMax = (int16)READ_BE_INT16(ptr); ptr += 2;
+
+ for (i = 0; i < 2; i++) {
+ _gameState [i] = (int16)READ_BE_INT16(ptr); ptr += 2;
+ _testValue [i] = (int16)READ_BE_INT16(ptr); ptr += 2;
+ _itemNumber[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
+ }
+
+ _person1PtrOff = READ_BE_UINT16(ptr); ptr += 2;
+ _cutawayPtrOff = READ_BE_UINT16(ptr); ptr += 2;
+ _person2PtrOff = READ_BE_UINT16(ptr); ptr += 2;
+ _joePtrOff = 32 + _levelMax * 96;
+
+ // Load dialogue tree
+ ptr = _fileData + 32;
+ memset(&_dialogueTree[0], 0, sizeof(_dialogueTree[0]));
+ for (i = 1; i <= _levelMax; i++)
+ for (int j = 0; j <= 5; j++) {
+ ptr += 2;
+ _dialogueTree[i][j].head = (int16)READ_BE_INT16(ptr); ptr += 2;
+ ptr += 2;
+ _dialogueTree[i][j].dialogueNodeValue1 = (int16)READ_BE_INT16(ptr); ptr += 2;
+ ptr += 2;
+ _dialogueTree[i][j].gameStateIndex = (int16)READ_BE_INT16(ptr); ptr += 2;
+ ptr += 2;
+ _dialogueTree[i][j].gameStateValue = (int16)READ_BE_INT16(ptr); ptr += 2;
+ }
+}
+
+void Talk::initialTalk() {
+ // Lines 848-903 in talk.c
+
+ uint16 offset = _joePtrOff + 2;
+ uint16 hasNotString = READ_BE_UINT16(_fileData + offset); offset += 2;
+
+ char joeString[MAX_STRING_SIZE];
+ if (!hasNotString) {
+ getString(_fileData, offset, joeString, MAX_STRING_LENGTH);
+ } else {
+ joeString[0] = '\0';
+ }
+
+ offset = _person2PtrOff;
+ char joe2String[MAX_STRING_SIZE];
+ getString(_fileData, offset, _person2String, MAX_STRING_LENGTH);
+ getString(_fileData, offset, joe2String, MAX_STRING_LENGTH);
+
+ if (!hasTalkedTo()) {
+ // Not yet talked to this person
+ if (joeString[0] != '0') {
+ char voiceFilePrefix[MAX_STRING_SIZE];
+ sprintf(voiceFilePrefix, "%2dSSSSJ", _talkKey);
+ speak(joeString, NULL, voiceFilePrefix);
+ }
+ } else {
+ // Already spoken to them, choose second response
+ if (joe2String[0] != '0') {
+ char voiceFilePrefix[MAX_STRING_SIZE];
+ sprintf(voiceFilePrefix, "%2dXXXXJ", _talkKey);
+ speak(joe2String, NULL, voiceFilePrefix);
+ }
+ }
+}
+
+int Talk::getSpeakCommand(const Person *person, const char *sentence, unsigned &index) {
+ // Lines 1299-1362 in talk.c
+ int commandCode = SPEAK_DEFAULT;
+ uint16 id = (sentence[index] << 8) | sentence[index + 1];
+ switch (id) {
+ case 'AO':
+ commandCode = SPEAK_AMAL_ON;
+ break;
+ case 'FL':
+ commandCode = SPEAK_FACE_LEFT;
+ break;
+ case 'FF':
+ commandCode = SPEAK_FACE_FRONT;
+ break;
+ case 'FB':
+ commandCode = SPEAK_FACE_BACK;
+ break;
+ case 'FR':
+ commandCode = SPEAK_FACE_RIGHT;
+ break;
+ case 'GD':
+ _vm->logic()->joeGrab(STATE_GRAB_DOWN);
+ commandCode = SPEAK_NONE;
+ break;
+ case 'GM':
+ _vm->logic()->joeGrab(STATE_GRAB_MID);
+ commandCode = SPEAK_NONE;
+ break;
+ case 'WT':
+ commandCode = SPEAK_PAUSE;
+ break;
+ case 'XY':
+ // For example *XY00(237,112)
+ {
+ commandCode = atoi(sentence + index + 2);
+ int x = atoi(sentence + index + 5);
+ int y = atoi(sentence + index + 9);
+ if (0 == strcmp(person->name, "JOE"))
+ _vm->walk()->moveJoe(0, x, y, _vm->input()->cutawayRunning());
+ else
+ _vm->walk()->movePerson(person, x, y, _vm->graphics()->numFrames(), 0);
+ index += 11;
+ // if (JOEWALK==3) CUTQUIT=0;
+ // XXX personWalking = true;
+ }
+ break;
+ default:
+ if (sentence[index + 0] >= '0' && sentence[index + 0] <= '9' &&
+ sentence[index + 1] >= '0' && sentence[index + 1] <= '9') {
+ commandCode = (sentence[index] - '0') * 10 + (sentence[index + 1] - '0');
+ } else
+ warning("Unknown command string: '%2s'", sentence + index);
+ }
+
+ index += 2;
+
+ return commandCode;
+}
+
+
+bool Talk::speak(const char *sentence, Person *person, const char *voiceFilePrefix) {
+ // Function SPEAK, lines 1266-1384 in talk.c
+ bool personWalking = false;
+ unsigned segmentIndex = 0;
+ unsigned segmentStart = 0;
+ unsigned i;
+
+ Person joe_person;
+ ActorData joe_actor;
+
+ _vm->logic()->joeWalk(JWM_SPEAK);
+
+ if (!person) {
+ // Fill in values for use by speakSegment() etc.
+ memset(&joe_person, 0, sizeof(Person));
+ memset(&joe_actor, 0, sizeof(ActorData));
+
+ joe_actor.bobNum = 0;
+ joe_actor.color = 14;
+ joe_actor.bankNum = 7;
+
+ joe_person.actor = &joe_actor;
+ joe_person.name = "JOE";
+
+ person = &joe_person;
+ }
+
+ debug(6, "Sentence '%s' is said by person '%s' and voice files with prefix '%s' played",
+ sentence, person->name, voiceFilePrefix);
+
+ if (sentence[0] == '\0') {
+ return personWalking;
+ }
+
+ if (0 == strcmp(person->name, "FAYE-H" ) ||
+ 0 == strcmp(person->name, "FRANK-H") ||
+ 0 == strcmp(person->name, "AZURA-H") ||
+ 0 == strcmp(person->name, "X3_RITA") ||
+ (0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == FAYE_HEAD ) ||
+ (0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == AZURA_HEAD) ||
+ (0 == strcmp(person->name, "JOE") && _vm->logic()->currentRoom() == FRANK_HEAD))
+ _talkHead = true;
+ else
+ _talkHead = false;
+
+ for (i = 0; i < strlen(sentence); ) {
+ if (sentence[i] == '*') {
+ int segmentLength = i - segmentStart;
+
+ i++;
+ int command = getSpeakCommand(person, sentence, i);
+
+ if (SPEAK_NONE != command) {
+ speakSegment(
+ sentence + segmentStart,
+ segmentLength,
+ person,
+ command,
+ voiceFilePrefix,
+ segmentIndex);
+ // XXX if (JOEWALK == 2) break
+ }
+
+ segmentIndex++;
+ segmentStart = i;
+ } else
+ i++;
+
+ if (_vm->input()->cutawayQuit() || _vm->input()->talkQuit())
+ return personWalking;
+ }
+
+ if (segmentStart != i) {
+ speakSegment(
+ sentence + segmentStart,
+ i - segmentStart,
+ person,
+ 0,
+ voiceFilePrefix,
+ segmentIndex);
+ }
+
+ return personWalking;
+}
+
+int Talk::countSpaces(const char *segment) {
+ int tmp = 0;
+
+ while (*segment++)
+ tmp++;
+
+ if (tmp < 10)
+ tmp = 10;
+
+ return (tmp * 2) / (_vm->talkSpeed() / 3);
+}
+
+void Talk::headStringAnimation(const SpeechParameters *parameters, int bobNum, int bankNum) {
+ // talk.c lines 1612-1635
+ BobSlot *bob2 = _vm->graphics()->bob(2);
+
+ if (parameters->animation[0] == 'E') {
+ int offset = 1;
+
+ BobSlot *bob = _vm->graphics()->bob(bobNum);
+ int16 x = bob->x;
+ int16 y = bob->y;
+
+ for (;;) {
+ uint16 frame;
+
+ frame = atoi(parameters->animation + offset);
+ if (!frame)
+ break;
+
+ offset += 4;
+
+ _vm->bankMan()->unpack(frame, _vm->graphics()->numFrames(), bankNum);
+
+ bob2->frameNum = _vm->graphics()->numFrames();
+ bob2->scale = 100;
+ bob2->active = true;
+ bob2->x = x;
+ bob2->y = y;
+
+ _vm->update();
+ }
+ } else
+ bob2->active = false;
+}
+
+void Talk::stringAnimation(const SpeechParameters *parameters, int startFrame, int bankNum) {
+ // lines 1639-1690 in talk.c
+
+ int offset = 0;
+ bool torso;
+
+ if (parameters->animation[0] == 'T') {
+ // Torso animation
+ torso = true;
+ _vm->bankMan()->overpack(parameters->body, startFrame, bankNum);
+ offset++;
+ } else if (parameters->animation[0] == 'E') {
+ // Talking head animation
+ return;
+ } else if (!isdigit(parameters->animation[0])) {
+ debug(6, "Error in speak string animation: '%s'", parameters->animation);
+ return;
+ } else
+ torso = false;
+
+ for (;;) {
+ uint16 frame;
+
+ frame = atoi(parameters->animation + offset);
+ if (!frame)
+ break;
+
+ offset += 4;
+
+ if (frame > 500) {
+ frame -= 500;
+ _vm->sound()->playSfx(_vm->logic()->currentRoomSfx(), false);
+ }
+
+ if (torso) {
+ _vm->bankMan()->overpack(frame, startFrame, bankNum);
+ } else {
+ _vm->bankMan()->unpack(frame, startFrame, bankNum);
+ // XXX bobs[BNUM].scale=SF;
+ }
+
+ _vm->update();
+ }
+}
+
+void Talk::defaultAnimation(
+ const char *segment,
+ bool isJoe,
+ const SpeechParameters *parameters,
+ int startFrame,
+ int bankNum) {
+ // lines 1730-1823 in talk.c
+
+ if (segment[0] != 0) {
+
+ // Why on earth would someone name a variable qzx?
+ short qzx = 0;
+
+ int len = countSpaces(segment);
+ while (1) {
+ if (parameters != NULL) {
+
+ int bf;
+ if (segment[0] == ' ')
+ bf = 0;
+ else
+ bf = parameters->bf;
+
+ int head;
+ if (parameters->rf > 0)
+ head = bf + _vm->randomizer.getRandomNumber(parameters->rf);
+ else
+ head = bf;
+
+ if (bf > 0) {
+ // Make the head move
+ qzx ^= 1;
+ if (parameters->af && qzx)
+ _vm->bankMan()->overpack(parameters->af + head, startFrame, bankNum);
+ else {
+ _vm->bankMan()->overpack(head, startFrame, bankNum);
+ }
+ } else {
+ debug(6, "[Talk::defaultAnimation] Body action!");
+ // Just do a body action
+ _vm->bankMan()->overpack(parameters->body, startFrame, bankNum);
+ }
+
+ if (!_talkHead)
+ _vm->update();
+ } else { // (_talkHead && isJoe)
+ _vm->update();
+ }
+
+ if (_vm->input()->talkQuit())
+ break;
+
+ if (_vm->logic()->joeWalk() == JWM_SPEAK) {
+ _vm->update();
+ } else {
+ _vm->update(true);
+ if (_vm->logic()->joeWalk() == JWM_EXECUTE)
+ // Selected a command, so exit
+ break;
+ }
+
+ // Skip through text more quickly
+ if (_vm->input()->keyVerb() == VERB_SKIP_TEXT) {
+ _vm->input()->clearKeyVerb();
+ _vm->sound()->stopSpeech();
+ break;
+ }
+
+ if (_vm->sound()->speechOn() && _vm->sound()->speechSfxExists()) {
+ // sfx is finished, stop the speak animation
+ if (!_vm->sound()->isSpeechActive()) {
+ break;
+ }
+ } else {
+ // no sfx, stop the animation when speak segment 'length' is 0
+ --len;
+ if (len <= 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Make sure that Person closes their mouth
+ if (!isJoe && parameters->ff > 0)
+ _vm->bankMan()->overpack(parameters->ff, startFrame, bankNum);
+}
+
+
+void Talk::speakSegment(
+ const char *segmentStart,
+ int length,
+ Person *person,
+ int command,
+ const char *voiceFilePrefix,
+ int index)
+{
+ int i;
+ char segment[MAX_STRING_SIZE];
+ memcpy(segment, segmentStart, length);
+ segment[length] = '\0';
+
+ char voiceFileName[MAX_STRING_SIZE];
+ sprintf(voiceFileName, "%s%1x", voiceFilePrefix, index + 1);
+
+ // FIXME - it seems the french talkie version has a useless voice file ;
+ // the c30e_102 file is very similar to c30e_101, so there is no need to
+ // play it. This voice was used in room 30 (N8) when talking to Klunk.
+ if (!(_vm->resource()->getLanguage() == FRENCH && !strcmp(voiceFileName, "c30e_102"))
+ && _vm->sound()->speechOn())
+ _vm->sound()->playSfx(voiceFileName, true);
+
+ int faceDirectionCommand = 0;
+
+ switch (command) {
+ case SPEAK_PAUSE:
+ for (i = 0; i < 10 && !_vm->input()->talkQuit(); i++) {
+ _vm->update();
+ }
+ return;
+
+ case SPEAK_FACE_LEFT:
+ case SPEAK_FACE_RIGHT:
+ case SPEAK_FACE_FRONT:
+ case SPEAK_FACE_BACK:
+ faceDirectionCommand = command;
+ command = 0;
+ break;
+ }
+
+ bool isJoe = (0 == person->actor->bobNum);
+
+ int16 bobNum = person->actor->bobNum;
+ uint16 color = person->actor->color;
+ uint16 bankNum = person->actor->bankNum;
+
+ BobSlot *bob = _vm->graphics()->bob(bobNum);
+
+ bool oracle = false;
+ int textX = 0;
+ int textY = 0;
+
+ if (!isJoe) {
+ if (SPEAK_AMAL_ON == command) {
+ // It's the oracle!
+ // Don't turn AMAL animation off, and don't manually anim person
+ command = SPEAK_ORACLE;
+ oracle = true;
+ uint16 frameNum = _vm->graphics()->personFrames(bobNum);
+ for (i = 5; i <= 8; ++i) {
+ _vm->bankMan()->unpack(i, frameNum, bankNum);
+ ++frameNum;
+ }
+ } else {
+ bob->animating = false;
+ bob->frameNum = 31 + bobNum;
+ }
+ }
+
+ if (_talkHead) {
+ // talk.c lines 1491-1533
+ switch (_vm->logic()->currentRoom()) {
+ case FAYE_HEAD:
+ case AZURA_HEAD:
+ textX = 15;
+ break;
+ default: // FRANK_HEAD
+ textX = 150;
+ break;
+ }
+ textY = isJoe ? 30 : 60;
+ } else {
+ textX = bob->x;
+ textY = bob->y;
+ }
+
+ //int SF = _vm->grid()->findScale(textX, textY);
+
+ const SpeechParameters *parameters = NULL;
+ int startFrame = 0;
+
+ if (_talkHead && isJoe) {
+ if (_vm->subtitles())
+ _vm->graphics()->setBobText(bob, segment, textX, textY, color, true);
+ defaultAnimation(segment, isJoe, parameters, startFrame, bankNum);
+ } else {
+ if (SPEAK_UNKNOWN_6 == command)
+ return;
+
+ if (isJoe) {
+ if (_vm->logic()->currentRoom() == 108)
+ parameters = findSpeechParameters("JOE-E", command, 0);
+ else
+ parameters = findSpeechParameters("JOE", command, _vm->logic()->joeFacing());
+ }
+ else
+ parameters = findSpeechParameters(person->name, command, 0);
+
+ startFrame = 31 + bobNum;
+ int faceDirection = 0;
+
+ if (isJoe && _vm->logic()->joeFacing() == DIR_LEFT)
+ faceDirection = DIR_LEFT;
+ else if (!isJoe) {
+ ObjectData *data = _vm->logic()->objectData(_vm->logic()->objectForPerson(bobNum));
+
+ if (data->image == -3)
+ faceDirection = DIR_LEFT;
+
+ if (faceDirectionCommand == SPEAK_FACE_LEFT)
+ data->image = -3;
+ else if (faceDirectionCommand == SPEAK_FACE_RIGHT)
+ data->image = -4;
+ }
+
+ if (faceDirectionCommand) {
+ switch (faceDirectionCommand) {
+ case SPEAK_FACE_LEFT:
+ faceDirection = DIR_LEFT;
+ break;
+ case SPEAK_FACE_RIGHT:
+ faceDirection = DIR_RIGHT;
+ break;
+ case SPEAK_FACE_FRONT:
+ faceDirection = DIR_FRONT;
+ break;
+ case SPEAK_FACE_BACK:
+ faceDirection = DIR_BACK;
+ break;
+ }
+ if (isJoe)
+ _vm->logic()->joeFacing(faceDirection);
+ }
+
+ if (!isJoe) {
+ bob->xflip = (faceDirection == DIR_LEFT);
+ }
+
+ // Run animated sequence if SANIMstr is primed
+
+ if (_talkHead) {
+ // talk.c lines 1612-1635
+ headStringAnimation(parameters, bobNum, bankNum);
+ }
+
+ if (_vm->subtitles())
+ _vm->graphics()->setBobText(bob, segment, textX, textY, color, _talkHead);
+
+ if (parameters->animation[0] != '\0' && parameters->animation[0] != 'E') {
+ stringAnimation(parameters, startFrame, bankNum);
+ } else {
+ _vm->bankMan()->unpack(parameters->body, startFrame, bankNum);
+
+ if (length == 0 && !isJoe && parameters->bf > 0) {
+ _vm->bankMan()->overpack(parameters->bf, startFrame, bankNum);
+ _vm->update();
+ }
+
+ if (-1 == parameters->rf) {
+ // Setup the Torso frames
+ _vm->bankMan()->overpack(parameters->bf, startFrame, bankNum);
+ if (isJoe)
+ parameters = findSpeechParameters(person->name, 0, _vm->logic()->joeFacing());
+ else
+ parameters = findSpeechParameters(person->name, 0, 0);
+ }
+
+ if (-2 == parameters->rf) {
+ // Setup the Torso frames
+ _vm->bankMan()->overpack(parameters->bf, startFrame, bankNum);
+ if (isJoe)
+ parameters = findSpeechParameters(person->name, 14, _vm->logic()->joeFacing());
+ else
+ parameters = findSpeechParameters(person->name, 14, 0);
+ }
+
+ defaultAnimation(segment, isJoe, parameters, startFrame, bankNum);
+ }
+ }
+
+ // Moved here so that Text is cleared when a Torso command done!
+ _vm->display()->clearTexts(0,198);
+
+ if (oracle) {
+ uint16 frameNum = _vm->graphics()->personFrames(bobNum);
+ for (i = 1; i <= 4; ++i) {
+ _vm->bankMan()->unpack(i, frameNum, bankNum);
+ ++frameNum;
+ }
+ }
+
+ // Ensure that the correct buffer frame is selected
+
+ if (isJoe && !_talkHead) {
+ if (_vm->logic()->joeFacing() == DIR_FRONT ||
+ _vm->logic()->joeFacing() == DIR_BACK) {
+ // Joe is facing either Front or Back!
+ // - Don't FACE_JOE in room 69, because Joe is probably
+ // holding the Dino Ray gun.
+ if (_vm->logic()->currentRoom() != 69)
+ _vm->logic()->joeFace();
+ } else {
+ if (command == SPEAK_DEFAULT ||
+ command == 6 ||
+ command == 7) {
+ _vm->logic()->joeFace();
+ } else if (command != 5) {
+ // 7/11/94, Ensure that correct mouth closed frame is used!
+ if (parameters->rf != -1)
+ // XXX should really be just "bf", but it is not always calculated... :-(
+ _vm->bankMan()->overpack(parameters->bf, startFrame, bankNum);
+
+ if (parameters->ff == 0)
+ _vm->bankMan()->overpack(10, startFrame, bankNum);
+ else
+ _vm->bankMan()->overpack(parameters->ff, startFrame, bankNum);
+ }
+ }
+ }
+
+ _vm->update();
+}
+
+const Talk::SpeechParameters *Talk::findSpeechParameters(
+ const char *name,
+ int state,
+ int faceDirection) {
+ const SpeechParameters *iterator = _speechParameters;
+ if (faceDirection == DIR_RIGHT)
+ faceDirection = DIR_LEFT;
+
+ while (iterator->name[0] != '*') {
+ if (0 == scumm_stricmp(iterator->name, name) &&
+ iterator->state == state &&
+ iterator->faceDirection == faceDirection)
+ break;
+ iterator++;
+ }
+
+ return iterator;
+}
+
+void Talk::getString(const byte *ptr, uint16 &offset, char *str, int maxLength, int align) {
+ assert((align & 1) == 0);
+ int length = *(ptr + offset);
+ ++offset;
+
+ if (length > maxLength) {
+ error("String too long. Length = %i, maxLength = %i", length, maxLength);
+ } else if (length) {
+ if (str) {
+ memcpy(str, ptr + offset, length);
+ str[length] = '\0';
+ }
+ offset = (offset + length + (align - 1)) & ~(align - 1);
+ } else {
+ if (str) {
+ str[0] = '\0';
+ }
+ }
+}
+
+TalkSelected *Talk::talkSelected() {
+ return _vm->logic()->talkSelected(_uniqueKey);
+}
+
+int Talk::splitOption(const char *str, char optionText[5][MAX_STRING_SIZE]) {
+ char option[MAX_STRING_SIZE];
+ strcpy(option, str);
+ // option text ends at '*' char
+ char *p = strchr(option, '*');
+ if (p) {
+ *p = '\0';
+ }
+ int lines;
+ memset(optionText, 0, 5 * MAX_STRING_SIZE);
+ if (_vm->resource()->getLanguage() == ENGLISH || _vm->display()->textWidth(option) <= MAX_TEXT_WIDTH) {
+ strcpy(optionText[0], option);
+ lines = 1;
+ } else if (_vm->resource()->getLanguage() == HEBREW) {
+ lines = splitOptionHebrew(option, optionText);
+ } else {
+ lines = splitOptionDefault(option, optionText);
+ }
+ return lines;
+}
+
+int Talk::splitOptionHebrew(const char *str, char optionText[5][MAX_STRING_SIZE]) {
+ char tmpString[MAX_STRING_SIZE] = "";
+ uint16 len = 0;
+ uint16 spaceCharWidth = _vm->display()->textWidth(" ");
+ uint16 width = 0;
+ uint16 optionLines = 0;
+ uint16 maxTextLen = MAX_TEXT_WIDTH;
+ const char *p = strchr(str, '\0');
+ while (p != str - 1) {
+ while (*p != ' ' && p != str - 1) {
+ --p;
+ ++len;
+ }
+ if (p != str - 1) {
+ uint16 wordWidth = _vm->display()->textWidth(p, len);
+ width += wordWidth;
+ if (width > maxTextLen) {
+ ++optionLines;
+ strncpy(optionText[optionLines], p, len);
+ optionText[optionLines][len] = '\0';
+ width = wordWidth;
+ maxTextLen = MAX_TEXT_WIDTH - OPTION_TEXT_MARGIN;
+ } else {
+ strcpy(tmpString, optionText[optionLines]);
+ strncpy(optionText[optionLines], p, len);
+ optionText[optionLines][len] = '\0';
+ strcat(optionText[optionLines], tmpString);
+ }
+ --p;
+ len = 1;
+ width += spaceCharWidth;
+ } else {
+ if (len > 1) {
+ if (width + _vm->display()->textWidth(p + 1, len) > maxTextLen) {
+ ++optionLines;
+ }
+ strcpy(tmpString, optionText[optionLines]);
+ strncpy(optionText[optionLines], p + 1, len);
+ optionText[optionLines][len] = '\0';
+ strcat(optionText[optionLines], tmpString);
+ }
+ ++optionLines;
+ }
+ }
+ return optionLines;
+}
+
+int Talk::splitOptionDefault(const char *str, char optionText[5][MAX_STRING_SIZE]) {
+ // Split up multiple line option at closest space character
+ uint16 spaceCharWidth = _vm->display()->textWidth(" ");
+ uint16 width = 0;
+ uint16 optionLines = 0;
+ uint16 maxTextLen = MAX_TEXT_WIDTH;
+ const char *p = str;
+ while (p) {
+ p = strchr(str, ' ');
+ if (p) {
+ uint16 len = p - str;
+ uint16 wordWidth = _vm->display()->textWidth(str, len);
+ width += wordWidth;
+ if (width > maxTextLen) {
+ ++optionLines;
+ strncpy(optionText[optionLines], str, len + 1);
+ width = wordWidth;
+ maxTextLen = MAX_TEXT_WIDTH - OPTION_TEXT_MARGIN;
+ } else {
+ strncat(optionText[optionLines], str, len + 1);
+ }
+ width += spaceCharWidth;
+ str = p + 1;
+ } else {
+ if (str[0]) {
+ if (width + _vm->display()->textWidth(str) > maxTextLen) {
+ ++optionLines;
+ }
+ strcat(optionText[optionLines], str);
+ }
+ ++optionLines;
+ }
+ }
+ return optionLines;
+}
+
+int16 Talk::selectSentence() {
+ int selectedSentence = 0;
+
+ int startOption = 1;
+ int optionLines = 0;
+ char optionText[5][MAX_STRING_SIZE];
+ int talkZone[5];
+ int i;
+
+ _vm->display()->textCurrentColor(INK_TALK_NORMAL);
+
+ _vm->graphics()->setupArrows();
+ BobSlot *arrowBobUp = _vm->graphics()->bob(Graphics::ARROW_BOB_UP);
+ arrowBobUp->active = false;
+ BobSlot *arrowBobDown = _vm->graphics()->bob(Graphics::ARROW_BOB_DOWN);
+ arrowBobDown->active = false;
+
+ bool rezone = true;
+
+ while (rezone) {
+ rezone = false;
+
+ // Set zones for UP/DOWN text arrows when not English version
+
+ _vm->grid()->clear(GS_PANEL);
+
+ if (_vm->resource()->getLanguage() != ENGLISH) {
+ _vm->grid()->setZone(GS_PANEL, ARROW_ZONE_UP, MAX_TEXT_WIDTH + 1, 0, 319, 24);
+ _vm->grid()->setZone(GS_PANEL, ARROW_ZONE_DOWN, MAX_TEXT_WIDTH + 1, 25, 319, 49);
+ }
+
+ _vm->display()->clearTexts(151, 199);
+
+ int sentenceCount = 0;
+ int yOffset = 1;
+
+ for (i = startOption; i <= 4; i++) {
+ talkZone[i] = 0;
+
+ if (_talkString[i][0] != '\0') {
+ sentenceCount++;
+ optionLines = splitOption(_talkString[i], optionText);
+
+ if (yOffset < 5) {
+ _vm->grid()->setZone(
+ GS_PANEL,
+ i,
+ 0,
+ yOffset * LINE_HEIGHT - PUSHUP,
+ (_vm->resource()->getLanguage() == ENGLISH) ? 319 : MAX_TEXT_WIDTH,
+ (yOffset + optionLines) * LINE_HEIGHT - PUSHUP);
+ }
+
+ int j;
+ for (j = 0; j < optionLines; j++) {
+ if (yOffset < 5) {
+ _vm->display()->setText(
+ (j == 0) ? 0 : OPTION_TEXT_MARGIN,
+ 150 - PUSHUP + yOffset * LINE_HEIGHT,
+ optionText[j]);
+ }
+ yOffset++;
+ }
+
+ talkZone[i] = sentenceCount;
+ }
+ }
+
+ yOffset--;
+
+ // Up and down dialogue arrows
+
+ if (_vm->resource()->getLanguage() != ENGLISH) {
+ arrowBobUp->active = (startOption > 1);
+ arrowBobDown->active = (yOffset > 4);
+ }
+
+ _vm->input()->clearKeyVerb();
+
+ if (sentenceCount > 0) {
+ int zone = 0;
+ int oldZone = 0;
+
+ while (0 == selectedSentence) {
+
+ if (_vm->input()->talkQuit())
+ break;
+
+ _vm->update();
+
+ zone = _vm->grid()->findZoneForPos(GS_PANEL, _vm->input()->mousePosX(), _vm->input()->mousePosY());
+
+ int mouseButton = _vm->input()->mouseButton();
+ _vm->input()->clearMouseButton();
+
+ if (ARROW_ZONE_UP == zone || ARROW_ZONE_DOWN == zone) {
+ if (oldZone > 0) {
+ int16 y;
+ const Box *b = _vm->grid()->zone(GS_PANEL, oldZone);
+ for (y = b->y1; y < b->y2; y += 10)
+ _vm->display()->textColor(150 + y, INK_TALK_NORMAL);
+ oldZone = 0;
+ }
+ if (mouseButton != 0) {
+ if (zone == ARROW_ZONE_UP && arrowBobUp->active) {
+ startOption--;
+ } else if (zone == ARROW_ZONE_DOWN && arrowBobDown->active) {
+ startOption++;
+ }
+ }
+ rezone = true;
+ break;
+ } else {
+ if (oldZone != zone) {
+ // Changed zone, change text colors
+ int y;
+
+ debug(6, "Changed zone. oldZone = %i, zone = %i",
+ oldZone, zone);
+
+ if (zone > 0) {
+ const Box *b = _vm->grid()->zone(GS_PANEL, zone);
+ for (y = b->y1; y < b->y2; y += 10)
+ _vm->display()->textColor(150 + y, INK_JOE);
+ }
+
+ if (oldZone > 0) {
+ const Box *b = _vm->grid()->zone(GS_PANEL, oldZone);
+ for (y = b->y1; y < b->y2; y += 10)
+ _vm->display()->textColor(150 + y, INK_TALK_NORMAL);
+ }
+
+ oldZone = zone;
+ }
+
+ }
+
+ Verb v = _vm->input()->keyVerb();
+ if (v >= VERB_DIGIT_FIRST && v <= VERB_DIGIT_LAST) {
+ int n = v - VERB_DIGIT_FIRST + 1;
+ for (i = 1; i <= 4; i++) {
+ if (talkZone[i] == n) {
+ selectedSentence = i;
+ break;
+ }
+ }
+
+ _vm->input()->clearKeyVerb();
+ }
+ else if (mouseButton) {
+ selectedSentence = zone;
+ }
+
+ } // while ()
+ }
+ }
+
+ debug(6, "Selected sentence %i", selectedSentence);
+
+ arrowBobUp->active = false;
+ arrowBobDown->active = false;
+
+ if (selectedSentence > 0) {
+ _vm->display()->clearTexts(0, 198);
+
+ speak(_talkString[selectedSentence], NULL, _joeVoiceFilePrefix[selectedSentence]);
+ }
+
+ _vm->display()->clearTexts(151, 151);
+
+ return selectedSentence;
+}
+
+#ifndef PALMOS_68K
+const Talk::SpeechParameters Talk::_speechParameters[] = {
+ { "JOE", 0, 1, 1, 10, 2, 3, "", 0 },
+ { "JOE", 0, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 0, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 1, 1, 1, 45, -1, 0, "", 0 },
+ { "JOE", 1, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 1, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 2, 1, 1, 46, -1, 0, "", 0 },
+ { "JOE", 2, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 2, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 3, 1, 1, 47, -1, 0, "", 0 },
+ { "JOE", 3, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 3, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 4, 1, 1, 50, -1, 0, "", 0 },
+ { "JOE", 4, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 4, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 5, 1, 2, 0, 0, 0, "", 0 },
+ { "JOE", 5, 3, 4, 0, 0, 0, "", 0 },
+ { "JOE", 5, 4, 6, 0, 0, 0, "", 0 },
+
+ { "JOE", 6, 1, 1, 48, 0, 1, "", 0 },
+ { "JOE", 6, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 6, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 7, 1, 1, 51, 0, 1, "", 0 },
+ { "JOE", 7, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 7, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 8, 1, 1, 26, 0, 0, "", 0 },
+ { "JOE", 8, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 8, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 9, 1, 1, 29, 0, 0, "", 0 },
+ { "JOE", 9, 3, 3, 28, 0, 0, "", 0 },
+ { "JOE", 9, 4, 5, 38, 0, 0, "", 0 },
+
+ { "JOE", 10, 1, 1, 12, 0, 0, "T046,010,010,010,012,012,012,012,012,012,012,012,012,012,012,012,012,012,010,000", 0 },
+ { "JOE", 10, 3, 3, 18, 0, 0, "", 0 },
+ { "JOE", 10, 4, 5, 44, 0, 0, "", 0 },
+
+ { "JOE", 11, 1, 1, 53, -1, 0, "", 0 },
+ { "JOE", 11, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 11, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 12, 1, 1, 10, 2, 3, "", 0 },
+ { "JOE", 12, 3, 3, 28, 2, 0, "", 0 },
+ { "JOE", 12, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 13, 1, 1, 10, 2, 3, "T012,013,019,019,019,019,019,019,019,019,019,019,013,010,000", 0 },
+ { "JOE", 13, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 13, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 14, 1, 1, 16, 2, 3, "", 16 },
+ { "JOE", 14, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 14, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 15, 1, 1, 58, -1, 0, "", 0 },
+ { "JOE", 15, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 15, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 16, 1, 1, 59, -1, 0, "", 0 },
+ { "JOE", 16, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 16, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 17, 1, 1, 56, -1, 0, "", 0 },
+ { "JOE", 17, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 17, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 18, 1, 56, 16, 2, 3, "T056,057,057,057,056,056,000", 0 },
+ { "JOE", 18, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 18, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 19, 1, 54, 16, 2, 3, "T054,055,057,056,000", 0 },
+ { "JOE", 19, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 19, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 20, 1, 56, 16, 2, 3, "T056,057,055,054,001,000", 0 },
+ { "JOE", 20, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 20, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 21, 1, 1, 60, -1, 0, "", 0 },
+ { "JOE", 21, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 21, 4, 61, 38, 1, 0, "", 0 },
+
+ { "JOE", 22, 1, 1, 16, 2, 3, "T063,060,000", 0 },
+ { "JOE", 22, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 22, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 23, 1, 1, 16, 2, 3, "T060,063,001,000", 0 },
+ { "JOE", 23, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 23, 4, 5, 38, 1, 0, "", 0 },
+
+ { "JOE", 24, 1, 1, 47, -2, 0, "", 0 },
+ { "JOE", 24, 3, 3, 28, 2, 3, "", 0 },
+ { "JOE", 24, 4, 5, 38, 1, 0, "", 0 },
+
+ { "RICO", 0, 0, 1, 7, 1, 3, "", 7 },
+ { "RICO", 1, 0, 1, 5, -1, 0, "", 7 },
+ { "RICO", 2, 0, 1, 9, 0, 3, "", 7 },
+ { "RICO", 3, 0, 1, 4, -1, 0, "", 7 },
+
+ { "EDDY", 0, 0, 14, 18, 1, 3, "", 18 },
+ { "EDDY", 1, 0, 14, 0, 0, 0, "T016,017,017,016,016,017,017,016,016,017,017,000", 18 },
+
+ { "MIKE", 0, 0, 1, 2, 2, 3, "", 2 },
+
+ { "LOLA", 0, 0, 30, 33, 2, 3, "", 33 },
+ { "LOLA", 1, 0, 9, 10, 2, 3, "", 33 },
+ { "LOLA", 2, 0, 30, 33, 2, 3, "", 33 },
+ { "LOLA", 3, 0, 32, 33, 2, 3, "", 33 },
+ { "LOLA", 4, 0, 8, 0, 0, 0, "", 33 },
+ { "LOLA", 5, 0, 31, 0, 0, 0, "", 0 },
+ { "LOLA", 6, 0, 31, 0, 0, 0, "047,048,049,050,000", 33 },
+
+ { "LOLA_SHOWER", 0, 0, 7, 10, 2, 3, "", 10 },
+ { "LOLA_SHOWER", 1, 0, 9, 10, 2, 3, "", 10 },
+ { "LOLA_SHOWER", 2, 0, 30, 33, 2, 3, "", 10 },
+ { "LOLA_SHOWER", 3, 0, 32, 33, 2, 3, "", 10 },
+ { "LOLA_SHOWER", 4, 0, 8, 0, 0, 0, "", 0 },
+ { "LOLA_SHOWER", 5, 0, 61, 0, 0, 0, "", 0 },
+ { "LOLA_SHOWER", 6, 0, 64, 10, 2, 3, "", 0 },
+ { "LOLA_SHOWER", 7, 0, 31, 0, 0, 0, "062,063,064,000", 0 },
+
+ { "SECRETARY", 0, 0, 1, 12, 2, 3, "", 12 },
+ { "SECRETARY", 1, 0, 1, 12, 2, 0, "", 12 },
+ { "SECRETARY", 2, 0, 1, 12, 2, 0, "", 12 },
+
+ { "SPARKY", 0, 0, 21, 23, 2, 3, "", 23 },
+ { "SPARKY", 1, 0, 21, 22, 0, 0, "", 0 },
+ { "SPARKY", 2, 0, 21, 22, 0, 0, "021,042,043,000", 0 },
+ { "SPARKY", 3, 0, 21, 22, 0, 0, "043,042,021,000", 0 },
+ { "SPARKY", 4, 0, 43, 43, 1, 0, "", 0 },
+ { "SPARKY", 14, 0, 21, 29, 5, 0, "", 29 },
+
+ { "SPARKY-F", 0, 0, 45, 23, 5, 0, "", 23 },
+ { "SPARKY-F", 1, 0, 45, 47, 0, 0, "", 0 },
+ { "SPARKY-F", 2, 0, 45, 23, 5, 0, "045,046,046,045,000", 23 },
+ { "SPARKY-F", 14, 0, 45, 29, 5, 0, "", 29 },
+
+ { "FAYE", 0, 0, 19, 35, 2, 3, "", 35 },
+ { "FAYE", 1, 0, 19, 41, 2, 3, "", 35 },
+ { "FAYE", 2, 0, 19, 28, -1, 0, "", 35 },
+ { "FAYE", 3, 0, 19, 20, 0, 0, "", 0 },
+ { "FAYE", 4, 0, 19, 27, -1, 0, "", 35 },
+ { "FAYE", 5, 0, 19, 29, -1, 0, "", 35 },
+ { "FAYE", 6, 0, 59, 35, 2, 3, "", 35 },
+ { "FAYE", 7, 0, 19, 30, -1, 0, "", 35 },
+ { "FAYE", 8, 0, 19, 31, -1, 0, "", 35 },
+
+ { "BOB", 0, 0, 27, 34, 2, 3, "", 34 },
+ { "BOB", 1, 0, 27, 28, -1, 0, "", 34 },
+ { "BOB", 2, 0, 30, 0, 0, 0, "", 0 },
+ { "BOB", 3, 0, 31, 0, 0, 0, "", 0 },
+ { "BOB", 4, 0, 27, 61, -1, 0, "", 34 },
+ { "BOB", 5, 0, 27, 42, 1, 0, "", 42 },
+
+ { "PYGMY", 0, 0, 20, 21, 2, 6, "", 0 },
+ { "PYGMY", 1, 0, 20, 21, 2, 6, "020,068,068,068,068,068,068,068,068,020,000", 0 },
+ { "PYGMY", 2, 0, 20, 21, 2, 6, "T028,029,030,031,031,031,031,030,029,028,035,000", 0 },
+ { "PYGMY", 3, 0, 20, 21, 2, 6, "T035,036,037,038,037,038,037,038,036,035,000", 0 },
+ { "PYGMY", 4, 0, 20, 21, 2, 6, "T032,033,032,033,032,033,035,000", 0 },
+ { "PYGMY", 5, 0, 20, 21, 2, 6, "T023,022,021,022,023,024,025,026,027,026,025,024,023,000", 0 },
+ { "PYGMY", 6, 0, 20, 21, 2, 6, "T034,034,034,035,000", 0 },
+
+ { "WITCH", 0, 0, 39, 40, 2, 6, "", 40 },
+ { "WITCH", 1, 0, 39, 40, 2, 6, "073,074,000", 40 },
+ { "WITCH", 2, 0, 39, 40, 2, 6, "074,073,000", 40 },
+ { "WITCH", 3, 0, 39, 40, 2, 6, "T047,048,049,050,051,000", 40 },
+ { "WITCH", 4, 0, 39, 40, 2, 6, "T052,053,054,055,056,057,058,057,056,056,056,055,054,053,052,000", 40 },
+ { "WITCH", 5, 0, 39, 40, 2, 6, "069,070,071,072,073,074,000", 40 },
+ { "WITCH", 6, 0, 39, 40, 2, 6, "074,073,072,071,070,069,070,000", 40 },
+ { "WITCH", 7, 0, 39, 51, -1, 0, "", 40 },
+ { "WITCH", 8, 0, 39, 40, 2, 6, "T051,050,049,048,047,000", 40 },
+
+ { "CHIEF", 0, 0, 1, 7, 1, 7, "", 3 },
+ { "CHIEF", 1, 0, 1, 2, 2, 6, "062,063,064,065,066,000", 0 },
+ { "CHIEF", 2, 0, 1, 2, 2, 6, "066,065,064,063,062,000", 0 },
+ { "CHIEF", 3, 0, 1, 17, -1, 0, "", 3 },
+ { "CHIEF", 4, 0, 1, 18, -1, 0, "", 3 },
+ { "CHIEF", 5, 0, 1, 19, -1, 0, "", 3 },
+
+ { "NAOMI", 0, 0, 1, 2, 2, 3, "", 2 },
+ { "NAOMI", 1, 0, 1, 2, 2, 6, "048,049,050,051,052,053,054,055,000", 0 },
+ { "NAOMI", 2, 0, 1, 2, 2, 6, "055,054,053,052,051,050,049,048,000", 0 },
+ { "NAOMI", 3, 0, 1, 13, -1, 0, "", 2 },
+ { "NAOMI", 4, 0, 1, 14, -1, 0, "", 2 },
+ { "NAOMI", 5, 0, 1, 10, -1, 0, "", 2 },
+ { "NAOMI", 6, 0, 1, 12, -1, 0, "", 2 },
+ { "NAOMI", 7, 0, 1, 12, -1, 0, "T008,008,008,002,000", 2 },
+
+ { "WEDGEWOOD", 0, 0, 8, 1, 2, 0, "", 8 },
+ { "WEDGEWOOD", 1, 0, 1, 1, 3, 0, "", 1 },
+
+ { "BUD", 0, 0, 1, 2, 3, 2, "", 2 },
+ { "BUD", 1, 0, 1, 2, 4, 2, "T017,018,000", 2 },
+ { "BUD", 2, 0, 1, 21, -1, 0, "", 2 },
+ { "BUD", 3, 0, 1, 14, -1, 0, "", 2 },
+ { "BUD", 4, 0, 1, 15, -1, 0, "", 2 },
+ { "BUD", 5, 0, 1, 20, -1, 0, "", 2 },
+ { "BUD", 6, 0, 1, 16, -1, 0, "", 2 },
+ { "BUD", 7, 0, 1, 19, -1, 0, "", 2 },
+ { "BUD", 8, 0, 1, 17, -1, 0, "", 2 },
+ { "BUD", 9, 0, 1, 14, -1, 0, "T014,008,008,003,003,008,008,003,003,010,010,012,012,000", 2 },
+
+ { "LOU", 0, 0, 1, 2, 2, 3, "", 2 },
+ { "LOU", 1, 0, 1, 2, 4, 2, "013,014,015,016,017,018,000", 2 },
+ { "LOU", 2, 0, 1, 2, 4, 2, "018,017,016,015,014,013,000", 2 },
+
+ { "JIMMY", 0, 0, 16, 17, 2, 3, "", 17 },
+ { "JIMMY", 1, 0, 16, 25, -1, 0, "", 17 },
+ { "JIMMY", 2, 0, 16, 26, -1, 0, "", 17 },
+ { "JIMMY", 3, 0, 16, 27, -1, 0, "", 17 },
+ { "JIMMY", 4, 0, 16, 28, -1, 0, "", 17 },
+ { "JIMMY", 5, 0, 16, 29, -1, 0, "", 17 },
+
+ { "TAMMY", 0, 0, 1, 2, 2, 3, "", 2 },
+ { "TAMMY", 1, 0, 1, 2, 2, 3, "T008,008,009,009,008,008,009,009,008,008,009,009,002,000", 2 },
+ { "TAMMY", 2, 0, 1, 2, 2, 3, "T002,010,010,010,002,000", 2 },
+ { "TAMMY", 3, 0, 1, 2, 2, 3, "T011,011,011,011,011,002,000", 2 },
+ { "TAMMY", 4, 0, 1, 2, 2, 3, "T013,014,015,013,014,015,013,009,001,000", 2 },
+
+ { "SKULL", 0, 0, 9, 9, 4, 0, "", 0 },
+ { "SKULL", 1, 0, 1, 9, 4, 0, "001,002,003,004,005,006,007,008,009,000", 0 },
+ { "SKULL", 2, 0, 1, 9, 4, 0, "009,008,007,006,005,004,003,002,001,000", 0 },
+
+ { "APE", 0, 0, 1, 6, 7, 0, "", 6 },
+ { "APE", 1, 0, 1, 6, 7, 0, "002,001,000", 6 },
+ { "APE", 2, 0, 1, 6, 7, 0, "002,003,001,000", 6 },
+ { "APE", 3, 0, 1, 6, 7, 0, "004,005,004,005,004,001,000", 6 },
+ { "APE", 4, 0, 1, 6, 7, 0, "001,003,005,004,005,004,001,000", 6 },
+
+ { "APE1", 0, 0, 15, 16, 7, 0, "", 16 },
+ { "APE2", 0, 0, 14, 6, 7, 0, "", 6 },
+
+ { "SHOWERAM", 0, 0, 1, 2, 3, 0, "", 2 },
+ { "SHOWERAM", 1, 0, 1, 2, 3, 0, "026,027,028,029,001,000", 2 },
+ { "SHOWERAM", 2, 0, 1, 2, 3, 0, "001,029,028,027,026,000", 2 },
+
+ { "PRINCESS1", 0, 0, 19, 23, 2, 3, "", 23 },
+ { "PRINCESS1", 1, 0, 19, 41, -1, 0, "", 23 },
+ { "PRINCESS1", 2, 0, 19, 42, -1, 0, "", 23 },
+ { "PRINCESS1", 3, 0, 19, 45, -1, 0, "", 23 },
+ { "PRINCESS1", 4, 0, 19, 40, -1, 0, "", 23 },
+ { "PRINCESS1", 5, 0, 19, 45, 2, 3, "T40,043,044,045,000", 45 },
+ { "PRINCESS1", 6, 0, 19, 45, -1, 0, "T041,038,000", 38 },
+ { "PRINCESS1", 7, 0, 22, 0, 0, 0, "", 0 },
+ { "PRINCESS1", 8, 0, 19, 45, 2, 3, "T045,044,043,040,039,000", 39 },
+
+ { "PRINCESS2", 0, 0, 46, 23, 2, 3, "", 23 },
+ { "PRINCESS2", 1, 0, 46, 29, 2, 3, "", 29 },
+ { "PRINCESS2", 2, 0, 46, 29, 2, 3, "T029,036,035,000", 35 },
+
+ { "GUARDS", 0, 0, 7, 7, 0, 0, "", 7 },
+
+ { "AMGUARD", 0, 0, 19, 22, 2, 3, "", 22 },
+
+ { "MAN1", 0, 0, 2, 3, 2, 3, "", 3 },
+ { "MAN2", 0, 0, 9, 10, 1, 2, "", 10 },
+
+ { "DOG", 0, 0, 6, 6, 1, 0, "", 0 },
+ { "DOG", 1, 0, 6, 6, 1, 0, "010,009,008,000", 0 },
+ { "DOG", 2, 0, 6, 6, 1, 0, "008,009,010,000", 0 },
+
+ { "CHEF", 0, 0, 5, 6, 2, 3, "", 6 },
+
+ { "HENRY", 0, 0, 7, 9, 2, 3, "", 9 },
+ { "HENRY", 1, 0, 7, 21, -1, 0, "", 9 },
+ { "HENRY", 2, 0, 7, 19, -1, 0, "", 9 },
+ { "HENRY", 3, 0, 7, 20, -1, 0, "", 9 },
+ { "HENRY", 4, 0, 8, 9, 2, 3, "", 9 },
+ { "HENRY", 5, 0, 23, 9, -1, 0, "", 9 },
+ { "HENRY", 6, 0, 7, 9, 2, 3, "T019,015,017,017,017,017,017,017,017,015,009,000", 9 },
+ { "HENRY", 7, 0, 7, 9, 2, 3, "T018,010,000", 10 },
+ { "HENRY", 8, 0, 7, 9, 2, 3, "T018,016,000", 16 },
+ { "HENRY", 9, 0, 7, 9, 2, 3, "T018,011,000", 11 },
+ { "HENRY", 10, 0, 29, 33, 1, 1, "", 33 },
+ { "HENRY", 11, 0, 7, 30, 2, 0, "", 9 },
+ { "HENRY", 12, 0, 7, 9, 2, 3, "025,026,000", 26 },
+ { "HENRY", 13, 0, 7, 9, 2, 3, "027,028,027,028,000", 28 },
+ { "HENRY", 14, 0, 7, 9, 2, 3, "026,025,007,000", 9 },
+
+ { "JOHAN", 0, 0, 1, 15, 2, 3, "", 15 },
+ { "JOHAN", 1, 0, 1, 0, 0, 0, "T006,007,008,000", 15 },
+ { "JOHAN", 2, 0, 1, 15, 2, 3, "T002,003,004,005,004,005,004,005,004,005,004,005,004,003,002,000", 15 },
+ { "JOHAN", 3, 0, 1, 8, -1, 0, "", 15 },
+ { "JOHAN", 4, 0, 1, 0, 0, 0, "T008,007,006,001,000", 15 },
+
+ { "KLUNK", 0, 0, 1, 2, 2, 3, "", 2 },
+ { "KLUNK", 1, 0, 1, 2, 2, 3, "019,020,021,022,001,000", 2 },
+ { "KLUNK", 2, 0, 1, 2, 2, 3, "001,022,021,020,019,016,517,000", 2 },
+ { "KLUNK", 3, 0, 1, 2, 2, 3, "T010,011,010,011,010,011,009,000", 2 },
+
+ { "FRANK", 0, 0, 13, 14, 2, 3, "", 14 },
+ { "FRANK", 1, 0, 13, 20, 0, 1, "", 14 },
+ { "FRANK", 2, 0, 13, 14, 2, 3, "025,026,027,027,027,026,026,026,027,027,026,026,027,025,013,000", 14 },
+ { "FRANK", 3, 0, 28, 14, 2, 3, "", 14 },
+
+ { "DEATH", 0, 0, 1, 2, 2, 3, "", 2 },
+ { "DEATH", 1, 0, 1, 2, 2, 3, "013,014,015,016,017,001,000", 0 },
+ { "DEATH", 2, 0, 1, 2, 2, 3, "001,017,016,015,014,013,000", 0 },
+ { "DEATH", 3, 0, 1, 2, 2, 3, "T018,019,020,021,021,022,022,020,021,022,020,021,022,023,024,524,000", 2 },
+ { "DEATH", 4, 0, 1, 2, 2, 3, "T025,026,027,028,028,028,028,028,028,028,028,028,029,035,000", 2 },
+ { "DEATH", 5, 0, 1, 2, 2, 3, "T030,031,032,033,033,033,033,033,033,033,033,033,034,035,000", 2 },
+ { "DEATH", 6, 0, 1, 2, 2, 3, "T023,022,020,019,018,001,000", 2 },
+
+ { "JASPAR", 0, 0, 1, 1, 22, 0, "026,027,028,029,028,029,028,029,030,023,000", 0 },
+ { "JASPAR", 1, 0, 1, 1, 22, 0, "023,026,000", 0 },
+
+ { "ORACLE", 0, 0, 1, 5, 3, 0, "", 0 },
+
+ { "ZOMBIE", 0, 0, 1, 5, 2, 3, "", 5 },
+ { "ZOMBIE", 1, 0, 1, 12, -1, 0, "", 5 },
+ { "ZOMBIE", 2, 0, 1, 13, -1, 0, "", 5 },
+ { "ZOMBIE", 3, 0, 1, 1, 5, 5, "", 5 },
+
+ { "ZOMBIE2", 0, 0, 14, 14, 0, 0, "", 0 },
+ { "ZOMBIE3", 0, 0, 18, 18, 0, 0, "", 0 },
+
+ { "ANDERSON", 0, 0, 7, 8, 2, 3, "", 8 },
+ { "ANDERSON", 1, 0, 7, 8, 1, 0, "", 8 },
+ { "ANDERSON", 2, 0, 7, 16, -1, 0, "", 8 },
+ { "ANDERSON", 3, 0, 7, 18, -1, 0, "", 8 },
+ { "ANDERSON", 4, 0, 7, 19, -1, 0, "", 8 },
+ { "ANDERSON", 5, 0, 7, 20, -1, 0, "", 8 },
+ { "ANDERSON", 6, 0, 7, 21, 1, 0, "", 8 },
+
+ { "COMPY", 0, 0, 12, 12, -1, 0, "", 0 },
+ { "COMPY", 1, 0, 10, 10, 10, 0, "010,011,012,012,013,014,014,000", 0 },
+ { "COMPY", 2, 0, 10, 10, 10, 0, "014,013,012,000", 0 },
+
+ { "DEINO", 0, 0, 13, 13, -1, 0, "", 0 },
+ { "DEINO", 1, 0, 9, 9, 9, 0, "009,010,000", 0 },
+
+ { "TMPD", 0, 0, 19, 22, 2, 3, "", 22 },
+
+ { "IAN", 0, 0, 7, 9, 2, 3, "", 9 },
+ { "IAN", 1, 0, 8, 25, 3, 0, "", 25 },
+ { "IAN", 2, 0, 7, 21, -1, 0, "", 9 },
+ { "IAN", 3, 0, 7, 22, 1, 0, "", 9 },
+ { "IAN", 4, 0, 7, 22, -1, 0, "", 9 },
+ { "IAN", 5, 0, 7, 24, -1, 0, "", 9 },
+ { "IAN", 6, 0, 7, 9, 2, 3, "034,034,034,035,035,036,036,035,035,036,035,036,035,000", 9 },
+ { "IAN", 7, 0, 7, 31, -1, 0, "", 9 },
+
+ { "FAYE-H", 0, 0, 1, 1, 4, 1, "", 1 },
+ { "FAYE-H", 1, 0, 1, 1, 4, 1, "007,000", 7 },
+ { "FAYE-H", 2, 0, 1, 1, 4, 1, "009,010,011,009,001,000", 1 },
+ { "FAYE-H", 3, 0, 1, 1, 4, 1, "E012,013,000", 1 },
+ { "FAYE-H", 4, 0, 1, 1, 4, 1, "E015,000", 1 },
+ { "FAYE-H", 5, 0, 1, 1, 4, 1, "E014,000", 1 },
+
+ { "AZURA-H", 0, 0, 1, 1, 4, 1, "", 1 },
+ { "AZURA-H", 1, 0, 1, 1, 4, 1, "007,000", 7 },
+ { "AZURA-H", 2, 0, 1, 1, 4, 1, "009,010,011,009,001,000", 1 },
+ { "AZURA-H", 3, 0, 1, 1, 4, 1, "E012,013, 000", 1 },
+ { "AZURA-H", 4, 0, 1, 1, 4, 1, "E015,000", 1 },
+ { "AZURA-H", 5, 0, 1, 1, 4, 1, "E014,000", 1 },
+
+ { "FRANK-H", 0, 0, 1, 1, 4, 1, "", 1 },
+ { "FRANK-H", 1, 0, 1, 1, 4, 1, "E009,000", 1 },
+ { "FRANK-H", 2, 0, 1, 1, 4, 1, "E007,000", 1 },
+ { "FRANK-H", 3, 0, 1, 1, 4, 1, "010,011,012,013,014,015,010,000", 1 },
+
+ { "JOE-E", 0, 0, 1, 2, 4, 1, "", 2 },
+ { "JOE-E", 6, 0, 1, 2, 4, 1, "008,009,008,002,000", 2 },
+
+ { "AZURA-E", 0, 0, 1, 1, 5, 1, "", 1 },
+ { "AZURA-E", 1, 0, 1, 1, 5, 1, "009,010,009,008,000", 1 },
+
+ { "FAYE-E", 0, 0, 1, 4, 4, 1, "", 1 },
+ { "FAYE-E", 1, 0, 1, 4, 4, 1, "002,003,002,001,000", 1 },
+
+ { "ANDSON-E", 0, 0, 1, 3, 4, 1, "", 1 },
+ { "ANDSON-E", 1, 0, 1, 3, 4, 1, "002,001,000", 1 },
+
+ { "JOE-H", 0, 0, 1, 1, 4, 4, "", 1 },
+ { "JOE-H", 1, 0, 1, 1, 2, 3, "012,013,014,000", 14 },
+ { "JOE-H", 2, 0, 1, 1, 2, 3, "010,011,000", 11 },
+ { "JOE-H", 3, 0, 1, 1, 2, 3, "014,013,012,001,000", 1 },
+ { "JOE-H", 4, 0, 1, 13, 1, 0, "", 13 },
+
+ { "RITA-H", 0, 0, 7, 1, 2, 3, "", 1 },
+ { "RITA-H", 1, 0, 7, 0, 0, 0, "009,010,011,012,013,000", 13 },
+ { "RITA-H", 2, 0, 7, 0, 0, 0, "014,015,016,000", 16 },
+ { "RITA-H", 3, 0, 7, 0, 0, 0, "013,012,011,010,000", 10 },
+ { "RITA-H", 4, 0, 7, 0, 0, 0, "009,007,008,007,009,000", 9 },
+ { "RITA-H", 5, 0, 7, 0, 0, 0, "016,015,014,000", 14 },
+
+ { "RITA", 0, 0, 1, 4, 2, 3, "", 4 },
+ { "RITA", 1, 0, 2, 4, 2, 3, "", 4 },
+
+ { "SPARKY-H", 0, 0, 1, 1, 2, 3, "", 1 },
+
+ { "HUGH", 0, 0, 1, 1, 2, 3, "", 1 },
+ { "HUGH", 1, 0, 7, 7, 2, 3, "", 7 },
+
+ { "X2_JOE", 0, 0, 1, 1, 2, 3, "", 1 },
+ { "X2_JOE", 1, 0, 1, 1, 2, 3, "001,007,008,008,007,001,000", 1 },
+
+ { "X2_RITA", 0, 0, 1, 1, 2, 3, "", 1 },
+ { "X2_RITA", 1, 0, 1, 1, 2, 3, "001,007,008,008,007,001,000", 1 },
+
+ { "X3_RITA", 0, 0, 1, 1, 4, 1, "", 1 },
+ { "X3_RITA", 1, 0, 1, 1, 4, 1, "007,000", 7 },
+ { "X3_RITA", 2, 0, 1, 1, 4, 1, "009,010,011,009,001,000", 1 },
+ { "X3_RITA", 3, 0, 1, 1, 4, 1, "E012,013,000", 1 },
+ { "X3_RITA", 4, 0, 1, 1, 4, 1, "E015,000", 1 },
+ { "X3_RITA", 5, 0, 1, 1, 4, 1, "E014,000", 1 },
+
+ { "X4_JOE", 0, 0, 1, 1, 3, 4, "", 1 },
+ { "X4_JOE", 1, 0, 1, 13, 2, 3, "", 13 },
+ { "X4_JOE", 2, 0, 1, 1, 3, 4, "009, 010, 011, 012, 013, 000", 13 },
+ { "X4_JOE", 3, 0, 1, 1, 3, 4, "012, 011, 010, 009, 000", 9 },
+ { "X4_JOE", 4, 0, 1, 1, 3, 4, "001, 019, 000", 19 },
+
+ { "X4_RITA", 0, 0, 1, 1, 0, 1, "", 1 },
+ { "X4_RITA", 1, 0, 1, 7, 0, 1, "", 7 },
+ { "X4_RITA", 2, 0, 1, 1, 3, 4, "004,005,006,006,006,006,007,000", 7 },
+ { "X4_RITA", 3, 0, 1, 1, 3, 4, "005,004,001,000", 1 },
+ { "X4_RITA", 4, 0, 1, 1, 3, 4, "001,003,000", 3 },
+
+ { "X5_SPARKY", 0, 0, 1, 1, 2, 3, "", 1 },
+ { "X5_SPARKY", 1, 0, 1, 1, 2, 3, "001,010,011,011,001,000", 1 },
+ { "X5_SPARKY", 2, 0, 1, 1, 2, 3, "001,007,008,009,000", 9 },
+
+ { "X6_HUGH", 0, 0, 1, 1, 2, 3, "", 1 },
+ { "X6_HUGH", 1, 0, 1, 1, 2, 3, "007,007,007,007,,001,000", 1 },
+ { "X6_HUGH", 2, 0, 1, 1, 2, 3, "008,008,008,008,008,009,009,008,008,008,009,008,000", 8 },
+
+ { "X10_JOE", 0, 0, 1, 2, 2, 3, "", 2 },
+ { "X10_JOE", 1, 0, 1, 8, 2, 3, "", 8 },
+ { "X10_JOE", 2, 0, 1, 2, 2, 3, "014,014,014,015,015,014,014,015,015,000", 2 },
+
+ { "X10_RITA", 0, 0, 1, 2, 2, 3, "", 2 },
+
+ { "X11_JOE", 0, 0, 1, 2, 0, 1, "", 2 },
+
+ { "X11_RITA", 0, 0, 1, 2, 0, 1, "", 2 },
+ { "X11_RITA", 1, 0, 1, 2, 1, 0, "003,004,000", 4 },
+
+ { "JOHN", 0, 0, 1, 2, 2, 3, "", 1 },
+ { "JOHN", 1, 0, 1, 15, -1, 0, "", 1 },
+ { "JOHN", 2, 0, 1, 16, -1, 0, "", 1 },
+ { "JOHN", 3, 0, 1, 17, -1, 0, "", 1 },
+
+ { "STEVE", 0, 0, 8, 2, 2, 3, "", 2 },
+ { "STEVE", 1, 0, 8, 16, -1, 0, "", 2 },
+ { "STEVE", 2, 0, 9, 18, -1, 0, "T016,017,017,016,008,000", 2 },
+ { "STEVE", 3, 0, 8, 18, -1, 0, "", 2 },
+
+ { "TONY", 0, 0, 1, 2, 2, 3, "", 1 },
+ { "TONY", 1, 0, 1, 12, -1, 0, "", 1 },
+
+ { "*", 0, 0, 0, 0, 0, 0, "", 0 }
+};
+#endif
+
+} // End of namespace Queen
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Queen_Talk)
+_GSETPTR(Queen::_speechParameters, GBVARS_SPEECHPARAMETERS_INDEX, Queen::Talk::SpeechParameters, GBVARS_QUEEN)
+_GEND
+
+_GRELEASE(Queen_Talk)
+_GRELEASEPTR(GBVARS_SPEECHPARAMETERS_INDEX, GBVARS_QUEEN)
+_GEND
+
+#endif
diff --git a/engines/queen/talk.h b/engines/queen/talk.h
new file mode 100644
index 0000000000..d1469e2fe8
--- /dev/null
+++ b/engines/queen/talk.h
@@ -0,0 +1,244 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEEN_TALK_H
+#define QUEEN_TALK_H
+
+#include "common/util.h"
+#include "queen/structs.h"
+
+namespace Queen {
+
+class QueenEngine;
+
+class Talk {
+public:
+
+ //! Public interface to run a talk from a file
+ static void talk(const char *filename, int personInRoom, char *cutawayFilename, QueenEngine *vm);
+
+ //! Public interface to speak a sentence
+ static bool speak(const char *sentence, Person *person, const char *voiceFilePrefix, QueenEngine *vm);
+
+ //! Read a string from ptr and update offset
+ static void getString(const byte *ptr, uint16 &offset, char *str, int maxLength, int align = 2);
+
+private:
+
+ //! Collection of constants used by Talk
+ enum {
+ LINE_HEIGHT = 10,
+ MAX_STRING_LENGTH = 255,
+ MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1),
+ MAX_TEXT_WIDTH = (320-18),
+ PUSHUP = 4,
+ ARROW_ZONE_UP = 5,
+ ARROW_ZONE_DOWN = 6,
+ DOG_HEADER_SIZE = 20,
+ OPTION_TEXT_MARGIN = 24
+ };
+
+ //! Special commands for speech
+ enum {
+ SPEAK_DEFAULT = 0,
+ SPEAK_FACE_LEFT = -1,
+ SPEAK_FACE_RIGHT = -2,
+ SPEAK_FACE_FRONT = -3,
+ SPEAK_FACE_BACK = -4,
+ SPEAK_ORACLE = -5,
+ SPEAK_UNKNOWN_6 = -6,
+ SPEAK_AMAL_ON = -7,
+ SPEAK_PAUSE = -8,
+ SPEAK_NONE = -9
+ };
+
+ struct DialogueNode {
+ int16 head;
+ int16 dialogueNodeValue1;
+ int16 gameStateIndex;
+ int16 gameStateValue;
+ };
+
+#ifndef PALMOS_68K
+ struct SpeechParameters {
+ const char *name;
+ signed char state,faceDirection;
+ signed char body,bf,rf,af;
+ const char *animation;
+ signed char ff;
+ };
+#else
+public:
+ struct SpeechParameters {
+ const char name[11];
+ signed char state,faceDirection;
+ signed char body,bf,rf,af;
+ const char animation[80];
+ signed char ff;
+ };
+private:
+#endif
+
+ QueenEngine *_vm;
+
+ bool _wasFullscren;
+
+ //! Raw .dog file data (without 20 byte header)
+ byte *_fileData;
+
+ //! Number of dialogue levels
+ int16 _levelMax;
+
+ //! Unique key for this dialogue
+ int16 _uniqueKey;
+
+ //! Used to select voice files
+ int16 _talkKey;
+
+ int16 _jMax;
+
+ //! Used by findDialogueString
+ int16 _pMax;
+
+ // Update game state efter dialogue
+ int16 _gameState[2];
+ int16 _testValue[2];
+ int16 _itemNumber[2];
+
+ //! String data
+ uint16 _person1PtrOff;
+
+ //! Cutaway data
+ uint16 _cutawayPtrOff;
+
+ //! Data used if we have talked to the person before
+ uint16 _person2PtrOff;
+
+ //! Data used if we haven't talked to the person before
+ uint16 _joePtrOff;
+
+ //! Is a talking head
+ bool _talkHead;
+
+ //! IDs for sentences
+ DialogueNode _dialogueTree[18][6];
+
+ //! Greeting from person Joe has talked to before
+ char _person2String[MAX_STRING_SIZE];
+
+ int _oldSelectedSentenceIndex;
+ int _oldSelectedSentenceValue;
+
+ char _talkString[5][MAX_STRING_SIZE];
+ char _joeVoiceFilePrefix[5][MAX_STRING_SIZE];
+
+#ifndef PALMOS_68K
+ static const SpeechParameters _speechParameters[];
+#endif
+
+ Talk(QueenEngine *vm);
+ ~Talk();
+
+ //! Perform talk in file and return a cutaway filename
+ void talk(const char *filename, int personInRoom, char *cutawayFilename);
+
+ byte *loadDialogFile(const char *filename);
+
+ //! Load talk data from .dog file
+ void load(const char *filename);
+
+ //! First things spoken
+ void initialTalk();
+
+ //! Find a string in the dialogue tree
+ void findDialogueString(uint16 offset, int16 id, int16 max, char *str);
+
+ //! Get TalkSelected struct for this talk
+ TalkSelected *talkSelected();
+
+ //! Interface to the TalkSelected struct
+ bool hasTalkedTo() { return talkSelected()->hasTalkedTo; }
+
+ //! Interface to the TalkSelected struct
+ void setHasTalkedTo() { talkSelected()->hasTalkedTo = true; }
+
+ //! Get a selected value
+ int16 selectedValue(int index) {
+ return talkSelected()->values[index-1];
+ }
+
+ //! Set a selected value
+ void selectedValue(int index, int16 value) {
+ talkSelected()->values[index-1] = value;
+ }
+
+ //! The sentence will not be displayed again
+ void disableSentence(int oldLevel, int selectedSentence);
+
+ //! Select what to say
+ int16 selectSentence();
+
+ //! Speak sentence
+ bool speak(const char *sentence, Person *person, const char *voiceFilePrefix);
+
+ //! Convert command in sentence to command code
+ int getSpeakCommand(const Person *person, const char *sentence, unsigned &index);
+
+ //! Speak a part of a sentence
+ void speakSegment(
+ const char *segmentStart,
+ int length,
+ Person *person,
+ int command,
+ const char *voiceFilePrefix,
+ int index);
+
+ void headStringAnimation(const SpeechParameters *parameters, int bobNum, int bankNum);
+
+ void stringAnimation(const SpeechParameters *parameters, int startFrame, int bankNum);
+
+ void defaultAnimation(
+ const char *segment,
+ bool isJoe,
+ const SpeechParameters *parameters,
+ int startFrame,
+ int bankNum);
+
+ int countSpaces(const char *segment);
+
+ //! Get special parameters for speech
+ const SpeechParameters *findSpeechParameters(
+ const char *name,
+ int state,
+ int faceDirection);
+
+ int splitOption(const char *str, char optionText[5][MAX_STRING_SIZE]);
+
+ int splitOptionHebrew(const char *str, char optionText[5][MAX_STRING_SIZE]);
+
+ int splitOptionDefault(const char *str, char optionText[5][MAX_STRING_SIZE]);
+
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/walk.cpp b/engines/queen/walk.cpp
new file mode 100644
index 0000000000..69854b373a
--- /dev/null
+++ b/engines/queen/walk.cpp
@@ -0,0 +1,564 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "queen/walk.h"
+
+#include "queen/bankman.h"
+#include "queen/input.h"
+#include "queen/logic.h"
+#include "queen/graphics.h"
+#include "queen/grid.h"
+#include "queen/queen.h"
+
+namespace Queen {
+
+const MovePersonData Walk::_moveData[] = {
+ { "COMPY", -1, -6, 1, 6, 0, 0, 0, 0, 12, 12, 1, 14 },
+ { "DEINO", -1, -8, 1, 8, 0, 0, 0, 0, 11, 11, 1, 10 },
+ { "FAYE", -1, -6, 1, 6, 13, 18, 7, 12, 19, 22, 2, 5 },
+ { "GUARDS", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 5 },
+ { "PRINCESS1", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 },
+ { "PRINCESS2", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 },
+ { "AMGUARD", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 },
+ { "SPARKY", -1, -6, 1, 6, 13, 18, 7, 12, 21, 20, 2, 5 },
+ { "LOLA_SHOWER", -1, -6, 55, 60, 0, 0, 0, 0, 7, 7, 2, 5 },
+ { "LOLA", -24, -29, 24, 29, 0, 0, 0, 0, 30, 30, 2, 5 },
+ { "BOB", -15, -20, 15, 20, 21, 26, 0, 0, 27, 29, 2, 5 },
+ { "CHEF", -1, -4, 1, 4, 0, 0, 0, 0, 1, 5, 2, 4 },
+ { "HENRY", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 6 },
+ { "ANDERSON", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 5 },
+ { "JASPAR", -4, -9, 4, 9, 16, 21, 10, 15, 1, 3, 1, 10 },
+ { "PYGMY", -7, -12, 7, 12, 0, 0, 0, 0, 27, 27, 2, 5 },
+ { "FRANK", 7, 12, 1, 6, 0, 0, 0, 0, 13, 13, 2, 4 },
+ { "WEDGEWOOD", -20, -25, 20, 25, 0, 0, 0, 0, 1, 1, 1, 5 },
+ { "TMPD", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 },
+ { "IAN", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 6 },
+ { "*", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+Walk::Walk(QueenEngine *vm)
+ : _vm(vm) {
+}
+
+void Walk::animateJoePrepare() {
+ // queen.c l.2748-2788
+ uint16 i;
+ for (i = 1; i <= _walkDataCount; ++i) {
+
+ WalkData *pwd = &_walkData[i];
+
+ if (pwd->dx < 0) {
+ pwd->anim.set(11, 18, DIR_LEFT);
+ } else {
+ pwd->anim.set(11, 18, DIR_RIGHT);
+ }
+
+ int16 k = ABS(pwd->dy);
+ int16 ds = pwd->area->scaleDiff();
+ if (ds > 0) {
+ k *= ((k * ds) / pwd->area->box.yDiff()) / 2;
+ }
+
+ if (ABS(pwd->dx) < k) {
+ if (pwd->dy < 0) {
+ if (ds < 0) {
+ pwd->anim.set(19, 24, DIR_FRONT);
+ } else {
+ pwd->anim.set(25, 30, DIR_BACK);
+ }
+ } else if (pwd->dy > 0) {
+ if (ds < 0) {
+ pwd->anim.set(25, 30, DIR_BACK);
+ } else {
+ pwd->anim.set(19, 24, DIR_FRONT);
+ }
+ }
+ }
+ }
+}
+
+void Walk::animateJoe() {
+ // queen.c l.2789-2835
+ uint16 lastDirection = 0;
+ uint16 i;
+ BobSlot *pbs = _vm->graphics()->bob(0);
+ _vm->logic()->joeFacing(_walkData[1].anim.facing);
+ _vm->logic()->joeScale(_walkData[1].area->calcScale(pbs->y));
+ _vm->logic()->joeFace();
+ for (i = 1; i <= _walkDataCount && !_joeInterrupted; ++i) {
+
+ WalkData *pwd = &_walkData[i];
+
+ // area has been turned off, see if we should execute a cutaway
+ if (pwd->area->mapNeighbours < 0) {
+ // queen.c l.2838-2911
+ _vm->logic()->handleSpecialArea(pwd->anim.facing, pwd->areaNum, i);
+ _joeMoveBlock = true;
+ return;
+ }
+ if (lastDirection != pwd->anim.facing) {
+ pbs->animNormal(pwd->anim.firstFrame, pwd->anim.lastFrame, 1, false, false);
+ }
+
+ uint16 moveSpeed = _vm->grid()->findScale(pbs->x, pbs->y) * 6 / 100;
+ pbs->move(pbs->x + pwd->dx, pbs->y + pwd->dy, moveSpeed);
+ pbs->xflip = (pbs->xdir < 0);
+ while (pbs->moving) {
+ // adjust Joe's movespeed according to scale
+ pbs->scale = pwd->area->calcScale(pbs->y);
+ _vm->logic()->joeScale(pbs->scale);
+ pbs->scaleWalkSpeed(6);
+ _vm->update(true);
+ if (_vm->input()->cutawayQuit() || _vm->logic()->joeWalk() == JWM_EXECUTE) {
+ stopJoe();
+ break;
+ }
+ }
+ lastDirection = pwd->anim.facing;
+ }
+ _vm->logic()->joeFacing(lastDirection);
+}
+
+void Walk::animatePersonPrepare(const MovePersonData *mpd, int direction) {
+ // queen.c l.2469-2572
+ int i;
+ for (i = 1; i <= _walkDataCount; ++i) {
+
+ WalkData *pwd = &_walkData[i];
+
+ if (pwd->dx < 0) {
+ pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_LEFT);
+ } else if (pwd->dx > 0) {
+ pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT);
+ } else {
+ if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
+ pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT);
+ } else {
+ // we have specific moves for this actor, see what direction they were last facing
+ if (direction == -3) {
+ // previously facing right
+ pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_LEFT);
+ } else {
+ // previously facing left
+ pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT);
+ }
+ }
+ }
+
+ int16 k = ABS(pwd->dy);
+ int16 ds = pwd->area->scaleDiff();
+ if (ds > 0) {
+ k *= ((k * ds) / pwd->area->box.yDiff()) / 2;
+ }
+
+ if (ABS(pwd->dx) < k) {
+ if (pwd->dy < 0) {
+ if (mpd->walkBack1 > 0) {
+ pwd->anim.set(mpd->walkBack1, mpd->walkBack2, DIR_BACK);
+ } else if (pwd->dx < 0) {
+ pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_BACK);
+ } else {
+ pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_BACK);
+ }
+ } else if (pwd->dy > 0) {
+ if (mpd->walkFront1 > 0) {
+ pwd->anim.set(mpd->walkFront1, mpd->walkFront2, DIR_FRONT);
+ } else if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
+ if (pwd->dx < 0) {
+ pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_FRONT);
+ } else {
+ pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_FRONT);
+ }
+ } else {
+ // we have a special move for left/right, so select that instead!
+ if (direction == -3) {
+ // previously facing right
+ pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_FRONT);
+ } else {
+ // previously facing left
+ pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_FRONT);
+ }
+ }
+ }
+ }
+ }
+}
+
+void Walk::animatePerson(const MovePersonData *mpd, uint16 image, uint16 bobNum, uint16 bankNum, int direction) {
+ // queen.c l.2572-2651
+ BobSlot *pbs = _vm->graphics()->bob(bobNum);
+
+ // check to see which way person should be facing
+ if (mpd->walkLeft1 == mpd->walkRight1) {
+ pbs->xflip = (direction == -3);
+ } else {
+ // they have special walk for left and right, so don't flip
+ pbs->xflip = false;
+ }
+
+ uint16 i;
+ for (i = 1; i <= _walkDataCount; ++i) {
+ WalkData *pwd = &_walkData[i];
+
+ // unpack necessary frames for bob animation
+ uint16 dstFrame = image;
+ uint16 srcFrame = ABS(pwd->anim.firstFrame);
+ while (srcFrame <= ABS(pwd->anim.lastFrame)) {
+ _vm->bankMan()->unpack(srcFrame, dstFrame, bankNum);
+ ++dstFrame;
+ ++srcFrame;
+ }
+ // pass across bobs direction ONLY if walk is a mirror flip!
+ if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
+ pbs->animNormal(image, dstFrame - 1, mpd->animSpeed, false, pbs->xflip);
+ } else {
+ pbs->animNormal(image, dstFrame - 1, mpd->animSpeed, false, false);
+ }
+
+ // move other actors at correct speed relative to scale
+ uint16 moveSpeed = _vm->grid()->findScale(pbs->x, pbs->y) * mpd->moveSpeed / 100;
+ pbs->move(pbs->x + pwd->dx, pbs->y + pwd->dy, moveSpeed);
+
+ // flip if one set of frames for actor
+ if (mpd->walkLeft1 < 0 || ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
+ pbs->xflip = pwd->dx < 0;
+ }
+
+ while (pbs->moving) {
+ _vm->update();
+ pbs->scale = pwd->area->calcScale(pbs->y);
+ pbs->scaleWalkSpeed(mpd->moveSpeed);
+ if (_vm->input()->cutawayQuit()) {
+ stopPerson(bobNum);
+ break;
+ }
+ }
+ }
+}
+
+int16 Walk::moveJoe(int direction, int16 endx, int16 endy, bool inCutaway) {
+ _joeInterrupted = false;
+ _joeMoveBlock = false;
+ int16 can = 0;
+ initWalkData();
+
+ uint16 oldx = _vm->graphics()->bob(0)->x;
+ uint16 oldy = _vm->graphics()->bob(0)->y;
+
+ _vm->logic()->joeWalk(JWM_MOVE);
+
+ uint16 oldPos = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy);
+ uint16 newPos = _vm->grid()->findAreaForPos(GS_ROOM, endx, endy);
+
+ debug(9, "Walk::moveJoe(%d, %d, %d, %d, %d) - old = %d, new = %d", direction, oldx, oldy, endx, endy, oldPos, newPos);
+
+ // if in cutaway, allow Joe to walk anywhere
+ if (newPos == 0 && inCutaway) {
+ incWalkData(oldx, oldy, endx, endy, oldPos);
+ } else {
+ if (calc(oldPos, newPos, oldx, oldy, endx, endy)) {
+ if (_walkDataCount > 0) {
+ animateJoePrepare();
+ animateJoe();
+ if (_joeInterrupted) {
+ can = -1;
+ }
+ }
+ } else {
+ // path has been blocked, make Joe say so
+ _vm->logic()->makeJoeSpeak(4);
+ can = -1;
+ }
+ }
+
+ _vm->graphics()->bob(0)->animating = false;
+ if (_joeMoveBlock) {
+ can = -2;
+ _joeMoveBlock = false;
+ } else if (direction > 0) {
+ _vm->logic()->joeFacing(direction);
+ }
+ _vm->logic()->joePrevFacing(_vm->logic()->joeFacing());
+ _vm->logic()->joeFace();
+ return can;
+}
+
+int16 Walk::movePerson(const Person *pp, int16 endx, int16 endy, uint16 curImage, int direction) {
+ if (endx == 0 && endy == 0) {
+ warning("Walk::movePerson() - endx == 0 && endy == 0");
+ return 0;
+ }
+
+ int16 can = 0;
+ initWalkData();
+
+ uint16 bobNum = pp->actor->bobNum;
+ uint16 bankNum = pp->actor->bankNum;
+
+ uint16 oldx = _vm->graphics()->bob(bobNum)->x;
+ uint16 oldy = _vm->graphics()->bob(bobNum)->y;
+
+ uint16 oldPos = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy);
+ uint16 newPos = _vm->grid()->findAreaForPos(GS_ROOM, endx, endy);
+
+ debug(9, "Walk::movePerson(%d, %d, %d, %d, %d) - old = %d, new = %d", direction, oldx, oldy, endx, endy, oldPos, newPos);
+
+ // find MovePersonData associated to Person
+ const MovePersonData *mpd = _moveData;
+ while (mpd->name[0] != '*') {
+ if (scumm_stricmp(mpd->name, pp->name) == 0) {
+ break;
+ }
+ ++mpd;
+ }
+
+ if (calc(oldPos, newPos, oldx, oldy, endx, endy)) {
+ if (_walkDataCount > 0) {
+ animatePersonPrepare(mpd, direction);
+ animatePerson(mpd, curImage, bobNum, bankNum, direction);
+ }
+ } else {
+ can = -1;
+ }
+
+ uint16 standingFrame = 31 + bobNum;
+
+ // make other person face the right direction
+ BobSlot *pbs = _vm->graphics()->bob(bobNum);
+ pbs->endx = endx;
+ pbs->endy = endy;
+ pbs->animating = false;
+ pbs->scale = _walkData[_walkDataCount].area->calcScale(endy);
+ if (_walkData[_walkDataCount].anim.facing == DIR_BACK) {
+ _vm->bankMan()->unpack(mpd->backStandingFrame, standingFrame, bankNum);
+ } else {
+ _vm->bankMan()->unpack(mpd->frontStandingFrame, standingFrame, bankNum);
+ }
+ uint16 obj = _vm->logic()->objectForPerson(bobNum);
+ if (_walkData[_walkDataCount].dx < 0) {
+ _vm->logic()->objectData(obj)->image = -3;
+ pbs->xflip = true;
+ } else {
+ _vm->logic()->objectData(obj)->image = -4;
+ pbs->xflip = false;
+ }
+ pbs->frameNum = standingFrame;
+ return can;
+}
+
+void Walk::stopJoe() {
+ BobSlot *pbs = _vm->graphics()->bob(0);
+ pbs->moving = false;
+ _joeInterrupted = true;
+}
+
+void Walk::stopPerson(uint16 bobNum) {
+ BobSlot *pbs = _vm->graphics()->bob(bobNum);
+ pbs->x = pbs->endx;
+ pbs->y = pbs->endy;
+ pbs->moving = false;
+}
+
+bool Walk::calc(uint16 oldPos, uint16 newPos, int16 oldx, int16 oldy, int16 x, int16 y) {
+ // if newPos is outside of an AREA then traverse Y axis until an AREA is found
+ if (newPos == 0) {
+ newPos = findAreaPosition(&x, &y, true);
+ }
+
+ // do the same for oldPos in case Joe somehow sits on the border of an AREA
+ // and does not register
+ if (oldPos == 0) {
+ oldPos = findAreaPosition(&oldx, &oldy, false);
+ }
+
+ if (oldPos == newPos) {
+ incWalkData(oldx, oldy, x, y, newPos);
+ return true;
+ } else if (calcPath(oldPos, newPos)) {
+ uint16 i;
+ int16 px = oldx;
+ int16 py = oldy;
+ for (i = 2; i <= _areaListCount; ++i) {
+ uint16 a1 = _areaList[i - 1];
+ uint16 a2 = _areaList[i];
+ const Area *pa1 = &_roomArea[a1];
+ const Area *pa2 = &_roomArea[a2];
+ uint16 x1 = calcC(pa1->box.x1, pa1->box.x2, pa2->box.x1, pa2->box.x2, px);
+ uint16 y1 = calcC(pa1->box.y1, pa1->box.y2, pa2->box.y1, pa2->box.y2, py);
+ incWalkData(px, py, x1, y1, a1);
+ px = x1;
+ py = y1;
+ }
+ incWalkData(px, py, x, y, newPos);
+ return true;
+ }
+ return false;
+}
+
+int16 Walk::calcC(int16 c1, int16 c2, int16 c3, int16 c4, int16 lastc) {
+ int16 s1 = MAX(c1, c3);
+ int16 s2 = MIN(c2, c4);
+ int16 c;
+ if ((lastc >= s1 && lastc <= s2) || (lastc >= s2 && lastc <= s1)) {
+ c = lastc;
+ } else {
+ c = (s1 + s2) / 2;
+ }
+ return c;
+}
+
+int16 Walk::findAreaPosition(int16 *x, int16 *y, bool recalibrate) {
+ // FIXME - in order to locate the nearest available area, the original
+ // algorithm computes the X (or Y) closest face distance for each available
+ // area. We simply added the case where the pointer is neither lying in the
+ // X range nor in the Y one.
+ // To get an example of this in action, in the room D1, make Joe walking
+ // to the wall at the right of the window (just above the radiator). On the
+ // original game, Joe will go to the left door...
+ uint16 i;
+ uint16 pos = 1;
+ uint32 minDist = (uint32)~0;
+ const Box *b = &_roomArea[1].box;
+ for (i = 1; i <= _roomAreaCount; ++i) {
+
+ b = &_roomArea[i].box;
+
+ uint16 dx1 = ABS(b->x1 - *x);
+ uint16 dx2 = ABS(b->x2 - *x);
+ uint16 dy1 = ABS(b->y1 - *y);
+ uint16 dy2 = ABS(b->y2 - *y);
+ uint16 csx = MIN(dx1, dx2);
+ uint16 csy = MIN(dy1, dy2);
+
+ bool inX = (*x >= b->x1) && (*x <= b->x2);
+ bool inY = (*y >= b->y1) && (*y <= b->y2);
+
+ uint32 dist = minDist;
+ if (!inX && !inY) {
+ dist = csx * csx + csy * csy;
+ } else if (inX) {
+ dist = csy * csy;
+ } else if (inY) {
+ dist = csx * csx;
+ }
+
+ if (dist < minDist) {
+ minDist = dist;
+ pos = i;
+ }
+ }
+ // we now have the closest area near X,Y, so we can recalibrate
+ // the X,Y coord to be in this area
+ if (recalibrate) {
+ b = &_roomArea[pos].box;
+ if (*x < b->x1) *x = b->x1;
+ if (*x > b->x2) *x = b->x2;
+ if (*y < b->y1) *y = b->y1;
+ if (*y > b->y2) *y = b->y2;
+ }
+ return pos;
+}
+
+uint16 Walk::findFreeArea(uint16 area) const {
+ uint16 testArea;
+ uint16 freeArea = 0;
+ uint16 map = ABS(_roomArea[area].mapNeighbours);
+ for (testArea = 1; testArea <= _roomAreaCount; ++testArea) {
+ int b = _roomAreaCount - testArea;
+ if (map & (1 << b)) {
+ // connecting area, check if it's been struck off
+ if (!isAreaStruck(testArea)) {
+ // legitimate connecting area, keep it
+ freeArea = testArea;
+ break;
+ }
+ }
+ }
+ return freeArea;
+}
+
+bool Walk::isAreaStruck(uint16 area) const {
+ uint16 i;
+ bool found = false;
+ for (i = 1; i <= _areaStrikeCount; ++i) {
+ if (_areaStrike[i] == area) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+
+bool Walk::calcPath(uint16 oldArea, uint16 newArea) {
+ debug(9, "Walk::calcPath(%d, %d)", oldArea, newArea);
+ _areaList[1] = _areaStrike[1] = oldArea;
+ _areaListCount = _areaStrikeCount = 1;
+ uint16 area = oldArea;
+ while (_areaListCount > 0 && area != newArea) {
+ area = findFreeArea(area);
+ if (!area) {
+ // wrong path, rolling back
+ _areaList[_areaListCount] = 0;
+ --_areaListCount;
+ area = _areaList[_areaListCount];
+ } else {
+ ++_areaListCount;
+ assert(_areaListCount < MAX_WALK_DATA);
+ _areaList[_areaListCount] = area;
+ if (!isAreaStruck(area)) {
+ ++_areaStrikeCount;
+ assert(_areaStrikeCount < MAX_WALK_DATA);
+ _areaStrike[_areaStrikeCount] = area;
+ }
+ }
+ }
+ return _areaList[1] != 0;
+}
+
+void Walk::initWalkData() {
+ uint16 curRoom = _vm->logic()->currentRoom();
+ _roomArea = _vm->grid()->area(curRoom, 0);
+ _roomAreaCount = _vm->grid()->areaMax(curRoom);
+
+ _walkDataCount = 0;
+ memset(_walkData, 0, sizeof(_walkData));
+ _areaStrikeCount = 0;
+ memset(_areaStrike, 0, sizeof(_areaStrike));
+ _areaListCount = 0;
+ memset(_areaList, 0, sizeof(_areaList));
+}
+
+void Walk::incWalkData(int16 px, int16 py, int16 x, int16 y, uint16 areaNum) {
+ debug(9, "Walk::incWalkData(%d, %d, %d)", (x - px), (y - py), areaNum);
+ if (px != x || py != y) {
+ ++_walkDataCount;
+ assert(_walkDataCount < MAX_WALK_DATA);
+ WalkData *pwd = &_walkData[_walkDataCount];
+ pwd->dx = x - px;
+ pwd->dy = y - py;
+ pwd->area = &_roomArea[areaNum];
+ pwd->areaNum = areaNum;
+ }
+}
+
+} // End of namespace Queen
diff --git a/engines/queen/walk.h b/engines/queen/walk.h
new file mode 100644
index 0000000000..02c8264db9
--- /dev/null
+++ b/engines/queen/walk.h
@@ -0,0 +1,144 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef QUEENWALK_H
+#define QUEENWALK_H
+
+#include "common/util.h"
+#include "queen/structs.h"
+
+namespace Queen {
+
+struct MovePersonAnim {
+ int16 firstFrame;
+ int16 lastFrame;
+ Direction facing;
+
+ void set(int16 ff, int16 lf, Direction dir) {
+ firstFrame = ff;
+ lastFrame = lf;
+ facing = dir;
+ }
+};
+
+struct WalkData {
+ int16 dx, dy;
+ const Area *area;
+ uint16 areaNum;
+ MovePersonAnim anim;
+};
+
+struct MovePersonData {
+ const char *name;
+ int16 walkLeft1, walkLeft2;
+ int16 walkRight1, walkRight2;
+ int16 walkBack1, walkBack2;
+ int16 walkFront1, walkFront2;
+ uint16 frontStandingFrame;
+ uint16 backStandingFrame;
+ uint16 animSpeed;
+ uint16 moveSpeed;
+};
+
+class QueenEngine;
+
+class Walk {
+public:
+
+ Walk(QueenEngine *vm);
+
+ int16 moveJoe(int direction, int16 endx, int16 endy, bool inCutaway);
+ int16 movePerson(const Person *pp, int16 endx, int16 endy, uint16 curImage, int direction);
+
+ void stopJoe();
+ void stopPerson(uint16 bobNum);
+
+ enum {
+ MAX_WALK_DATA = 16
+ };
+
+private:
+
+ void animateJoePrepare();
+ void animateJoe();
+
+ void animatePersonPrepare(const MovePersonData *mpd, int direction);
+ void animatePerson(const MovePersonData *mpd, uint16 image, uint16 bobNum, uint16 bankNum, int direction);
+
+ //! compute transition coordinate
+ static int16 calcC(int16 c1, int16 c2, int16 c3, int16 c4, int16 lastc);
+
+ //! find area for position
+ int16 findAreaPosition(int16 *x, int16 *y, bool recalibrate);
+
+ //! find an area not already struck
+ uint16 findFreeArea(uint16 area) const;
+
+ //! return true if the area is already on the walking path
+ bool isAreaStruck(uint16 area) const;
+
+ //! calculates the path list from oldArea to newArea
+ bool calcPath(uint16 oldArea, uint16 newArea);
+
+ //! resets path computed in calcPath()
+ void initWalkData();
+
+ //! add an area to the path
+ void incWalkData(int16 px, int16 py, int16 x, int16 y, uint16 area);
+
+ //! compute path (and populates _walkData) from current position to the new one
+ bool calc(uint16 oldPos, uint16 newPos, int16 oldx, int16 oldy, int16 x, int16 y);
+
+
+ //! areas for current room
+ const Area *_roomArea;
+
+ //! number of areas for current room
+ uint16 _roomAreaCount;
+
+ //! walking steps
+ WalkData _walkData[MAX_WALK_DATA];
+
+ //! number of walking steps
+ uint16 _walkDataCount;
+
+ uint16 _areaStrike[MAX_WALK_DATA];
+ uint16 _areaStrikeCount;
+
+ uint16 _areaList[MAX_WALK_DATA];
+ uint16 _areaListCount;
+
+ //! set if stopJoe() is called
+ bool _joeInterrupted;
+
+ //! set if handleSpecialArea() is called
+ bool _joeMoveBlock;
+
+ QueenEngine *_vm;
+
+ //! persons walking animation data
+ static const MovePersonData _moveData[];
+};
+
+} // End of namespace Queen
+
+#endif
diff --git a/engines/queen/xref.txt b/engines/queen/xref.txt
new file mode 100644
index 0000000000..55067aa922
--- /dev/null
+++ b/engines/queen/xref.txt
@@ -0,0 +1,496 @@
+$Id$
+
+Cross-reference for functions and variables for the original source code and
+the ScummVM implementation.
+
+
+BANKS
+=====
+erase() BankManager::close
+freeallframes() BankManager::eraseFrames(true)
+freeframes() BankManager::eraseFrames(false)
+loadbank() BankManager::load
+overpack() BankManager::overpack
+unpack() BankManager::unpack
+
+
+COMMAND
+=======
+ALTER_DEFAULT() *not needed* (use State::alterDefaultVerb)
+CLEAR_COMMAND() Command::clear
+EXECUTE_ACTION() Command::executeCurrentAction
+FIND_DEFAULT() *not needed* (use State::findDefaultVerb)
+LOOK() Command::lookAtSelectedObject
+LOOK_ICON(),LOOK_ITEM() Command::lookForCurrentIcon
+LOOK_ROOM() Command::lookForCurrentObject
+OPEN_CLOSE_OTHER() Command::openOrCloseAssociatedObject
+P1_SET_CONDITIONS() Command::setConditions
+P2_SET_AREAS() Command::setAreas
+P3_SET_OBJECTS() Command::setObjects
+P4_SET_ITEMS() Command::setItems
+SELECT() Command::grabCurrentSelection
+SELECT_ITEM() Command::grabSelectedItem
+SELECT_NOUN() Command::grabSelectedNoun
+SELECT_VERB() Command::grabSelectedVerb
+WALK() Command::makeJoeWalkTo
+-
+ACTION Command::_state.action
+ACTION2 Command::_state.selAction
+CLEVEL Command::_state.commandLevel
+COM_A Command::_cmdArea
+COM_A_MAX Command::_numCmdArea
+COM_O Command::_cmdObject
+COM_O_MAX Command::_numCmdObject
+COM_G Command::_cmdGameState
+COM_G_MAX Command::_numCmdGameState
+COM_I Command::_cmdInventory
+COM_I_MAX Command::_numCmdInventory
+COM_LIST Command::_cmdList
+COM_LIST_MAX Command::_numCmdList
+COMMANDstr Command::_cmdText
+DEFCOMM Command::_state.defaultVerb
+MKEY Command::_mouseKey
+OLDVERB,VERB Command::_state.*verb
+OLDNOUN,NOUN Command::_state.*noun
+NOUN2 Command::_state.selNoun
+PARSE Command::_parse
+SUBJ1,SUBJ2 Command::_state.subject*
+
+
+CREDIT SCRIPTING SYSTEM
+=======================
+Cinit() Credits::Credits()
+Ctext() *not needed* (included in Credits::update)
+Cupdate() Credits::update
+-
+Ccol Credits::_color
+Ccount Credits::_count
+Cfp
+Cflag Credits::_running
+Cfontsize Credits::_fontSize
+Cjustify Credits::_justify
+Cpausecount Credits::_pause
+Czone Credits::_zone
+
+
+CUTAWAY
+=======
+action_special_move() Cutaway::actionSpecialMove
+CUTAWAY() Cutaway::run
+MAKE_COMPLEX_ANIM() Cutaway::makeComplexAnimation
+SCENE_START() Logic::sceneStart
+SCENE_END() Logic::sceneStop
+-
+CUTON Input::_cutawayRunning
+CUTQUIT Input::_cutawayQuit
+FINAL_ROOM Cutaway::_finalRoom
+IROOM Cutaway::_initialRoom
+OBJ_CUT
+OBJ_ANIM
+OLDBANK
+PERSON_DATA
+SCENE Logic::_scene
+TROOM Cutaway::_temporaryRoom
+
+
+DEBUG
+=====
+cd_sample_check()
+debuginfo() Debugger::Cmd_Info
+select_new_room() Debugger::Cmd_Room
+-
+AREAVAR (boolean, if true display objects/areas boxes)
+
+
+GAME SETTINGS
+=============
+game_load() Logic::gameLoad
+game_save() Logic::gameSave
+-
+config_request
+MUSICTOGGLE Sound::_musicToggle / ConfMan.("music_mute")
+SFXTOGGLE Sound::_sfxToggle / ConfMan.("sfx_mute")
+TALKSPD QueenEngine::_talkSpeed / ConfMan.("talkspeed")
+TEXTTOGGLE QueenEngine::_subtitles / ConfMan.("subtitles")
+VersionStr GameVersion::versionString
+VOICETOGGLE Sound::_speechToggle / ConfMan.("speech_mute")
+VOLUME ? / ConfMan.("master_volume")
+
+
+GRAPHICS
+========
+bob() Graphics::drawBob
+CHECK_PARALLAX() Graphics::handleParallax
+clearallbobs() Graphics::clearBobs
+clearbob() BobSlot::clear
+DISP_OBJECTS() Graphics::setupRoomObjects
+drawbobs() Graphics::drawBobs
+invbob() Graphics::drawInventoryItem
+loadbackdrop() *not needed* (included in Display::setupNewRoom)
+loadpanel() Display::setupPanel
+MAKE_SPEAK_BOB() Graphics::setBobText
+makeanim() BobSlot::animNormal
+movebob() BobSlot::move
+pastebob() Graphics::pasteBob
+REDISP_OBJECT() Graphics::refreshObject
+requestor()
+shrinkbob() Graphics::shrinkFrame
+sortbobs() Graphics::sortBobs
+stringanim() BobSlot::animString
+-
+bobs Graphics::_bobs
+cambob Graphics::_cameraBob
+sortedbobs Graphics::_sortedBobs
+
+
+INPUT
+=====
+check_keys() Input::checkKeys()
+get_key() *not needed*
+-
+drawmouseflag *not needed* (equivalent to _display->showMouseCursor(bool))
+key_commands Input::_currentCommandKeys
+key_language Input::_commandKeys
+KEYVERB Input::_keyVerb
+MouseButton Input::_mouseButton
+mouseflag *not needed*
+no_check_keys Input::_noCheckKeys
+
+
+INVENTORY
+=========
+DEL_ITEM_NUM() Logic::inventoryDeleteItem
+INS_ITEM_NUM() Logic::inventoryInsertItem
+INVDWN() Logic::inventoryScroll
+INVENTORY() Logic::inventoryRefresh
+INVUP() Logic::inventoryScroll
+SETUP_ITEMS() Logic::inventorySetup
+-
+INV1,INV2,INV3,INV4 Logic::_inventoryItem
+
+
+JOE
+===
+FACE_JOE() Logic::joeFace
+GRAB_DIR(),GRAB_JOE() Logic::joeGrab
+SETUP_HERO() Logic::setupJoeInRoom
+SETUP_JOE() Logic::setupJoe
+USE_UNDERWEAR() Logic::joeUseUnderwear
+USE_CLOTHES() Logic::joeUseClothes
+USE_DRESS() Logic::joeUseDress
+-
+CUTJOEF Logic::_joe.cutFacing
+JOE_RESPstr Logic::_joeResponse
+JOEF,JX,JY,JDIR Logic::_joe.*
+JOEWALK Logic::_joe.walk
+
+
+JOURNAL
+=======
+clearlefttext() Journal::clearPanelTexts
+drawnames() Journal::drawSaveDescriptions
+findsaves() Journal::findSaveDescriptions
+menutext() Journal::drawPanelText
+predrawbobs() Journal::drawConfigPanel / Journal::drawNormalPanel
+prompt_do() *not needed*
+USE_JOURNAL() Logic::useJournal
+waitmousezone() *not needed*
+-
+choice Journal::_currentSaveSlot
+decbase Journal::_currentSavePage
+in_journal *not needed* (the hack in puttext() seems useless and CHECK_PARALLAX() is never called)
+save_descriptions Journal::_saveDescriptions
+walkgameload *not needed ?*
+
+
+LOGIC
+=====
+CHECK_PLAYER() QueenEngine::update
+CUTAWAY_SPECIAL() Logic::removeHotelItemsFromInventory
+DISP_ROOM() Logic::displayRoom
+FIND_BOB() Logic::findBob
+FIND_FRAME() Logic::findFrame
+FIND_GRAPHIC() Logic::graphicData
+P3_COPY_FROM() Logic::objectCopy
+R_MAP() Logic::handlePinnacleRoom
+restart_game()
+SETUP_BOBS() Graphics::unpackControlBank / Graphics::setupMouseCursor
+SETUP_FURNITURE() Graphics::setupRoomFurniture
+SETUP_ROOM() Logic::changeRoom
+SETUP_SCREENS() *not needed* (only calls Display::setupPanel)
+SETUP_VARS() *not needed* (equivalent to Command::clear(), SCENE=0, clear(gamestate))
+update() QueenEngine::update
+-
+A_ANIMstr Logic::_aAnim
+A_ANIM_MAX Logic::_numAAnim
+A_NAMEstr Logic::_aName
+A_NAME_MAX Logic::_numAName
+A_FILEstr Logic::_aFile
+A_FILE_MAX Logic::_numAFile
+ACTOR_DATA_MAX Logic::_numActors
+bamflag BamScene::_flag
+bamindex BamScene::_index
+DESCTOT Logic::_numDescriptions
+ENTRY_OBJ Logic::_entryObj
+FMAX Logic::_numFurnitureStatic
+FMAXA Logic::_numFurnitureAnimated
+FMAXLEN Logic::_numFurnitureAnimatedLen
+FRAMES Logic::_numFrames
+FURN_DATA_MAX Logic::_numFurniture
+GAMESTATE Logic::_gameState
+GRAPHIC_ANIM_MAX Logic::_numGraphicAnim
+GRAPHIC_DATA Logic::_graphicData
+GRAPHIC_MAX Logic::_numGraphics
+ITEMTOT Logic::_numItems
+ITEM_DATA Logic::_itemData
+NAMETOT Logic::_numNames
+OBJ_DESC_DATA Logic::_objectDescription
+OBJ_DESC_MAX Logic::_numObjDesc
+OBJECT_DATA Logic::_objectData
+OBJECT_DESCRstr Logic::_objDescription
+OBJECT_NAMEstr Logic::_objName
+OBJTOT Logic::_numObjects
+OLDROOM,ROOM,NEW_ROOM Logic::_*oom
+ROOMTOT Logic::_numRooms
+ROOM_DATA Logic::_roomData
+ROOM_NAMEstr Logic::_roomName
+SFACTOR Logic::_joe.scale
+VERB_NAMEstr Logic::_verbName
+WALK_OFF_DATA Logic::_walkOffData
+WALK_OFF_MAX Logic::_numWalkOffs
+
+
+PERSONS
+=======
+ALLOCATE_PERSON() Logic::allocatePersonFrames
+CREATE_ANIM() Graphics::setupPersonAnim
+SET_PERSON_DATA() Logic::initPerson
+SETUP_PERSON() Logic::setupPersonInRoom
+OBJ_PERSON() Logic::objectForPerson
+-
+NEW_ANIM Graphics::_newAnim
+PERSON_FACE
+PERSON_FACE_MAX
+PERSON_FRAMES Logic::_personFrames
+P_ANIMstr Person.anim
+P_NAMEstr Person.name
+P_STAND,P_BNUM,P_ROOM Person.actor->*
+P_BANK,P_X,P_Y,P_COLOR Person.actor->*
+P_VALUE,P_GAMES Person.actor->*
+SFRAME Person.bobFrame
+
+
+RESOURCE
+========
+tflen() Resource::fileSize
+topen() Resource::loadFile
+tseek() *not needed*
+
+
+SCREEN
+======
+Box() Display::drawBox
+calc_screen_scroll() Display::horizontalScrollUpdate
+changejoepal() Display::palSetJoe*
+check_colors() Display::palCustomColors
+check_pal_scroll Display::palCustomScroll
+clearpanel() Display::prepareUpdate
+drawbackdrop() Display::prepareUpdate
+drawpanel() Display::prepareUpdate
+drawscreen() Display::update
+dynalum() Display::dynalumUpdate
+fade_panel() Display::palGreyPanel
+fadein() Display::palFadeIn
+fadeout() Display::palFadeOut
+flashspecial() Display::palCustomFlash
+loadfont() Display::initFont
+palscroll() Display::palScroll
+putcharacter() Display::drawChar
+setpal() Display::palSet
+-
+BDxres Display::_bdWidth
+BDyres Display::_bdHeight
+clothespal Display::_palJoeClothes
+COMPANEL *not needed* (argument)
+dresspal Display::_palJoeDress
+font Display::_font
+font_sizes Display::_charWidth
+FULLSCREEN Display::_fullscreen
+nopalscroll Display::_pal.scrollable
+palette Display::_pal.room
+panelflag *not needed* (redundant with fullscreen)
+scrollx Display::_horizontalScroll
+tpal Display::_pal.screen
+
+
+SOUND
+=====
+alter_current_volume()
+playsong() Sound::playSong()
+sfxbusy() Sound::waitSfxFinished()
+sfxplay() Sound::playSfx()
+-
+song[] Sound::_song[]
+tunelist[] Sound::_tune[]
+CURRSONG Music::_currentSong
+SFXNAME Sound::_sfxName
+VOLUME
+
+
+STATE
+=====
+ALTER_STATE() State::alterState*
+FIND_STATE() State::findState*
+-
+
+
+TALK
+====
+FIND_SACTION() Talk::findSpeechParameters
+MOVE_SPEAK() *not needed* (included in Talk::getSpeakCommand)
+SPEAK() Talk::speak
+SPEAK_SUB() Talk::speakSegment
+talk() Talk::talk
+TALK_PROC() Talk::talk
+-
+A1,A12
+actiondata Talk::_speechParameters
+HEAD
+JMAX
+JOEKEYstr
+LEVEL
+LEVELMAX
+OLDLEVEL
+OLDS
+OWALK
+PERstr
+PKEYstr
+TALKHEAD
+TALKQUIT Input::_talkQuit
+TALKstr
+TALK_SELECTED Logic::_talkSelected
+
+
+TEXTS
+=====
+blanktexts() Display::clearTexts
+drawtext() Display::drawTexts
+Ink() Display::textCurrentColor
+MIDDLE() Display::textCenterX / Display::textSetCentered
+text() Display::setText
+textlen() Display::textWidth
+-
+textcol Display::_curTextColor
+texts Display::_texts
+
+
+WALK
+====
+CALC_PATH() Walk::calcPath
+CALC_WALK() Walk::incWalkData
+CALC_X() Walk::calcC
+CALC_Y() Walk::calcC
+CALCSCALE() Area::calcScale
+FIND_FREE_AREA Walk::findFreeArea
+FIND_NEWP() Walk::findAreaPosition
+FIND_OLDP() Walk::findAreaPosition
+MOVE_JOE() Walk::moveJoe
+MOVE_OTHER() Walk::movePerson
+-
+AREALIST Walk::_areaList
+AREASTRIKE Walk::_areaStrike
+movdata Walk::_moveData
+WALK_DATA Walk::_walkData
+WALKI Walk::_walkDataCount
+
+
+ZONES
+=====
+ClearZones() Grid::clear
+FIND_SCALE() Grid::findScale
+FIND_VERB() Grid::findVerbUnderCursor
+SETUP_PANEL_ZONES() Grid::setupPanel
+SETUP_ZONES() Grid::setupNewRoom
+SetZone() Grid::setZone
+zone() Grid::findZoneForPos / Logic::findAreaForPos
+-
+AREA Grid::_area
+AREAMAX Grid::_areaMax
+OBJECT_BOX Grid::_objectBox
+OBJMAX Grid::_objMax
+zones Grid::_zones
+
+
+(UNSORTED)
+==========
+in() Cutaway::inRange
+find_cd_cut() findCdCut
+find_cd_desc() *not needed* (included in Logic::joeSpeak)
+-
+Kstr
+bank9
+NEWDEF,
+M,A,
+FRAME,
+AM,
+WX,WY,
+PX,PY,
+LD,FD
+DESC2,DESC
+PERSON_OBJ
+FS,FE,FACE,
+TY,
+DY,
+I2,
+N,V,
+ds,bs,
+bx,by,
+dx,dy,
+SFAC,FDIR,
+OBJ,E,T,
+CH,
+OLDG,S2,S1,ITEM,TYPE,C,
+NAME,TL,TI,TS,WC,IMAGE,
+D,P,LI,R
+CANTQUIT !Input::_canQuit
+
+
+(NO NEED TO BE GLOBAL)
+======================
+Nstr,F1,F2,F3,F4,F5,F6,F7,F8,SF,BF,AS,MS // MOVE_OTHER (struct movdata *)
+Nstr,S,F,BODY,BF,RF,AF,SANIMstr,FF // FIND_SACTION (struct action *)
+CURRBOB // SETUP_FURNITURE, REDISP_OBJECT, DISP_OBJECTS
+PSY,PSX,CSX,DX1,DX2,DY1,DY2,PCF,CCF,CSY // FIND_NEWP, FIND_OLDP
+tx,ty,SFRAME,EFRAME,SPEED // FIND_GRAPHIC
+AREAMAXv
+CURRY
+OMAX,OMAXA
+TEMPA
+BANK,BNUM
+DIFF // LOOK local var
+RET // P1_SET_CONDITIONS local var
+BS,DS // CALC_SCALE
+SX,SY,
+NEWA // FIND_FREE_AREA local
+IX,IY // Cutaway locals
+COM // EXECUTE_ACTION local
+COMMAX // EXECUTE_ACTION local
+COND // EXECUTE_ACTION local
+CURRCOM // EXECUTE_ACTION local
+GSET // P1_SET_CONDITIONS local
+A2 // EXECUTE_ACTION local
+TEMPI // P1_SET_CONDITIONS local
+MAPC // findFreeArea local var
+NEWP,OLDP // locals in joeMove && personMove
+OLDX,X,OLDY,Y // passed as arguments
+X2,X1,XD,YD // incWalkData && findFreeArea locals
+Gstr // not needed, grab state
+Pstr // not needed, FIND_STATE result
+OUTLINE // not needed, textSet() Graphics::parameter
+FTOT // queen.c/SETUP_FURNITURE local var
+OBJMAXv // == Logic::_objMax[Logic::_currentRoom]
+TEMPstr
+WORDstr
+JOE2str,PERSON2str // locals in Talk::initialTalk
+SUBJECT
+tmpbamflag
diff --git a/engines/saga/actor.cpp b/engines/saga/actor.cpp
new file mode 100644
index 0000000000..cc89f0ef2a
--- /dev/null
+++ b/engines/saga/actor.cpp
@@ -0,0 +1,3092 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "saga/saga.h"
+
+#include "saga/actor.h"
+#include "saga/animation.h"
+#include "saga/console.h"
+#include "saga/events.h"
+#include "saga/gfx.h"
+#include "saga/interface.h"
+#include "saga/isomap.h"
+#include "saga/itedata.h"
+#include "saga/objectmap.h"
+#include "saga/resnames.h"
+#include "saga/rscfile.h"
+#include "saga/script.h"
+#include "saga/sndres.h"
+#include "saga/sprite.h"
+#include "saga/stream.h"
+#include "saga/font.h"
+#include "saga/sound.h"
+#include "saga/scene.h"
+
+#include "common/config-manager.h"
+
+namespace Saga {
+
+enum ActorFrameIds {
+//ITE
+ kFrameITEStand = 0,
+ kFrameITEWalk = 1,
+ kFrameITESpeak = 2,
+ kFrameITEGive = 3,
+ kFrameITEGesture = 4,
+ kFrameITEWait = 5,
+ kFrameITEPickUp = 6,
+ kFrameITELook = 7,
+//IHNM
+ kFrameIHNMStand = 0,
+ kFrameIHNMSpeak = 1,
+ kFrameIHNMWait = 2,
+ kFrameIHNMGesture = 3,
+ kFrameIHNMWalk = 4
+};
+
+static int commonObjectCompare(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) {
+ int p1 = obj1->_location.y - obj1->_location.z;
+ int p2 = obj2->_location.y - obj2->_location.z;
+ if (p1 == p2)
+ return 0;
+ if (p1 < p2)
+ return -1;
+ return 1;
+}
+
+static int tileCommonObjectCompare(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) {
+ int p1 = -obj1->_location.u() - obj1->_location.v() - obj1->_location.z;
+ int p2 = -obj2->_location.u() - obj2->_location.v() - obj2->_location.z;
+ //TODO: for kObjNotFlat obj Height*3 of sprite should be added to p1 and p2
+ //if (validObjId(obj1->id)) {
+
+ if (p1 == p2)
+ return 0;
+ if (p1 < p2)
+ return -1;
+ return 1;
+}
+
+// Lookup table to convert 8 cardinal directions to 4
+static const int actorDirectectionsLUT[8] = {
+ ACTOR_DIRECTION_BACK, // kDirUp
+ ACTOR_DIRECTION_RIGHT, // kDirUpRight
+ ACTOR_DIRECTION_RIGHT, // kDirRight
+ ACTOR_DIRECTION_RIGHT, // kDirDownRight
+ ACTOR_DIRECTION_FORWARD,// kDirDown
+ ACTOR_DIRECTION_LEFT, // kDirDownLeft
+ ACTOR_DIRECTION_LEFT, // kDirLeft
+ ACTOR_DIRECTION_LEFT, // kDirUpLeft
+};
+
+static const PathDirectionData pathDirectionLUT[8][3] = {
+ { { 0, 0, -1 }, { 7, -1, -1 }, { 4, 1, -1 } },
+ { { 1, 1, 0 }, { 4, 1, -1 }, { 5, 1, 1 } },
+ { { 2, 0, 1 }, { 5, 1, 1 }, { 6, -1, 1 } },
+ { { 3, -1, 0 }, { 6, -1, 1 }, { 7, -1, -1 } },
+ { { 0, 0, -1 }, { 1, 1, 0 }, { 4, 1, -1 } },
+ { { 1, 1, 0 }, { 2, 0, 1 }, { 5, 1, 1 } },
+ { { 2, 0, 1 }, { 3, -1, 0 }, { 6, -1, 1 } },
+ { { 3, -1, 0 }, { 0, 0, -1 }, { 7, -1, -1 } }
+};
+
+static const int pathDirectionLUT2[8][2] = {
+ { 0, -1 },
+ { 1, 0 },
+ { 0, 1 },
+ { -1, 0 },
+ { 1, -1 },
+ { 1, 1 },
+ { -1, 1 },
+ { -1, -1 }
+};
+
+static const int angleLUT[16][2] = {
+ { 0, -256 },
+ { 98, -237 },
+ { 181, -181 },
+ { 237, -98 },
+ { 256, 0 },
+ { 237, 98 },
+ { 181, 181 },
+ { 98, 237 },
+ { 0, 256 },
+ { -98, 237 },
+ { -181, 181 },
+ { -237, 98 },
+ { -256, 0 },
+ { -237, -98 },
+ { -181, -181 },
+ { -98, -237 }
+};
+
+static const int directionLUT[8][2] = {
+ { 0 * 2, -2 * 2 },
+ { 2 * 2, -1 * 2 },
+ { 3 * 2, 0 * 2 },
+ { 2 * 2, 1 * 2 },
+ { 0 * 2, 2 * 2 },
+ { -2 * 2, 1 * 2 },
+ { -4 * 2, 0 * 2 },
+ { -2 * 2, -1 * 2 }
+};
+
+static const int tileDirectionLUT[8][2] = {
+ { 1, 1 },
+ { 2, 0 },
+ { 1, -1 },
+ { 0, -2 },
+ { -1, -1 },
+ { -2, 0 },
+ { -1, 1 },
+ { 0, 2 }
+};
+
+struct DragonMove {
+ uint16 baseFrame;
+ int16 offset[4][2];
+};
+
+static const DragonMove dragonMoveTable[12] = {
+ { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } },
+ { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } },
+ { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } },
+ { 0, { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } },
+ { 28, { { -0, 0 }, { -1, 6 }, { -5, 11 }, { -10, 15 } } },
+ { 56, { { 0, 0 }, { 1, 6 }, { 5, 11 }, { 10, 15 } } },
+ { 40, { { 0, 0 }, { 6, 1 }, { 11, 5 }, { 15, 10 } } },
+ { 44, { { 0, 0 }, { 6, -1 }, { 11, -5 }, { 15, -10 } } },
+ { 32, { { -0, -0 }, { -6, -1 }, { -11, -5 }, { -15, -10 } } },
+ { 52, { { -0, 0 }, { -6, 1 }, { -11, 5 }, { -15, 10 } } },
+ { 36, { { 0, -0 }, { 1, -6 }, { 5, -11 }, { 10, -15 } } },
+ { 48, { { -0, -0 }, { -1, -6 }, { -5, -11 }, { -10, -15 } } }
+};
+
+Actor::Actor(SagaEngine *vm) : _vm(vm) {
+ int i;
+ byte *stringsPointer;
+ size_t stringsLength;
+ ActorData *actor;
+ ObjectData *obj;
+ debug(9, "Actor::Actor()");
+ _handleActionDiv = 15;
+
+ _actors = NULL;
+ _actorsCount = 0;
+
+ _objs = NULL;
+ _objsCount = 0;
+
+#ifdef ACTOR_DEBUG
+ _debugPoints = NULL;
+ _debugPointsAlloced = _debugPointsCount = 0;
+#endif
+
+ _protagStates = 0;
+ _protagStatesCount = 0;
+
+ _pathNodeList = _newPathNodeList = NULL;
+ _pathList = NULL;
+ _pathDirectionList = NULL;
+ _pathListAlloced = _pathNodeListAlloced = _newPathNodeListAlloced = 0;
+ _pathListIndex = _pathNodeListIndex = _newPathNodeListIndex = -1;
+ _pathDirectionListCount = 0;
+ _pathDirectionListAlloced = 0;
+
+ _centerActor = _protagonist = NULL;
+ _protagState = 0;
+ _lastTickMsec = 0;
+
+ _yCellCount = _vm->_scene->getHeight();
+ _xCellCount = _vm->getDisplayWidth();
+
+ _pathCell = (int8 *)malloc(_yCellCount * _xCellCount * sizeof(*_pathCell));
+
+ _pathRect.left = 0;
+ _pathRect.right = _vm->getDisplayWidth();
+ _pathRect.top = _vm->getDisplayInfo().pathStartY;
+ _pathRect.bottom = _vm->_scene->getHeight();
+
+ // Get actor resource file context
+ _actorContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ if (_actorContext == NULL) {
+ error("Actor::Actor() resource context not found");
+ }
+
+ // Load ITE actor strings. (IHNM actor strings are loaded by
+ // loadGlobalResources() instead.)
+
+ if (_vm->getGameType() == GType_ITE) {
+
+ _vm->_resource->loadResource(_actorContext, _vm->getResourceDescription()->actorsStringsResourceId, stringsPointer, stringsLength);
+
+ _vm->loadStrings(_actorsStrings, stringsPointer, stringsLength);
+ free(stringsPointer);
+ }
+
+ if (_vm->getGameType() == GType_ITE) {
+ _actorsCount = ITE_ACTORCOUNT;
+ _actors = (ActorData **)malloc(_actorsCount * sizeof(*_actors));
+ for (i = 0; i < _actorsCount; i++) {
+ actor = _actors[i] = new ActorData();
+ actor->_id = actorIndexToId(i);
+ actor->_index = i;
+ debug(9, "init actor id=%d index=%d", actor->_id, actor->_index);
+ actor->_nameIndex = ITE_ActorTable[i].nameIndex;
+ actor->_scriptEntrypointNumber = ITE_ActorTable[i].scriptEntrypointNumber;
+ actor->_spriteListResourceId = ITE_ActorTable[i].spriteListResourceId;
+ actor->_frameListResourceId = ITE_ActorTable[i].frameListResourceId;
+ actor->_speechColor = ITE_ActorTable[i].speechColor;
+ actor->_sceneNumber = ITE_ActorTable[i].sceneIndex;
+ actor->_flags = ITE_ActorTable[i].flags;
+ actor->_currentAction = ITE_ActorTable[i].currentAction;
+ actor->_facingDirection = ITE_ActorTable[i].facingDirection;
+ actor->_actionDirection = ITE_ActorTable[i].actionDirection;
+
+ actor->_location.x = ITE_ActorTable[i].x;
+ actor->_location.y = ITE_ActorTable[i].y;
+ actor->_location.z = ITE_ActorTable[i].z;
+
+ actor->_disabled = !loadActorResources(actor);
+ if (actor->_disabled) {
+ warning("Disabling actor Id=%d index=%d", actor->_id, actor->_index);
+ }
+ }
+ _objsCount = ITE_OBJECTCOUNT;
+ _objs = (ObjectData **)malloc(_objsCount * sizeof(*_objs));
+ for (i = 0; i < _objsCount; i++) {
+ obj = _objs[i] = new ObjectData();
+ obj->_id = objIndexToId(i);
+ obj->_index = i;
+ debug(9, "init obj id=%d index=%d", obj->_id, obj->_index);
+ obj->_nameIndex = ITE_ObjectTable[i].nameIndex;
+ obj->_scriptEntrypointNumber = ITE_ObjectTable[i].scriptEntrypointNumber;
+ obj->_spriteListResourceId = ITE_ObjectTable[i].spriteListResourceId;
+ obj->_sceneNumber = ITE_ObjectTable[i].sceneIndex;
+ obj->_interactBits = ITE_ObjectTable[i].interactBits;
+
+ obj->_location.x = ITE_ObjectTable[i].x;
+ obj->_location.y = ITE_ObjectTable[i].y;
+ obj->_location.z = ITE_ObjectTable[i].z;
+ }
+ } else {
+ // TODO. This is causing problems for SYMBIAN os as it doesn't like a static class here
+ ActorData dummyActor;
+
+ dummyActor._frames = NULL;
+ dummyActor._walkStepsPoints = NULL;
+
+ _protagonist = &dummyActor;
+ }
+
+ _dragonHunt = true;
+}
+
+Actor::~Actor() {
+ debug(9, "Actor::~Actor()");
+
+#ifdef ACTOR_DEBUG
+ free(_debugPoints);
+#endif
+ free(_pathDirectionList);
+ free(_pathNodeList);
+ free(_newPathNodeList);
+ free(_pathList);
+ free(_pathCell);
+ _actorsStrings.freeMem();
+ //release resources
+ freeActorList();
+ freeObjList();
+}
+
+bool Actor::loadActorResources(ActorData *actor) {
+ byte *resourcePointer;
+ size_t resourceLength;
+ int framesCount;
+ ActorFrameSequence *framesPointer;
+ bool gotSomething = false;
+
+ if (actor->_frameListResourceId) {
+ debug(9, "Loading frame resource id %d", actor->_frameListResourceId);
+ _vm->_resource->loadResource(_actorContext, actor->_frameListResourceId, resourcePointer, resourceLength);
+
+ framesCount = resourceLength / 16;
+ debug(9, "Frame resource contains %d frames (res length is %d)", framesCount, resourceLength);
+
+ framesPointer = (ActorFrameSequence *)malloc(sizeof(ActorFrameSequence) * framesCount);
+ if (framesPointer == NULL && framesCount != 0) {
+ memoryError("Actor::loadActorResources");
+ }
+
+ MemoryReadStreamEndian readS(resourcePointer, resourceLength, _actorContext->isBigEndian);
+
+ for (int i = 0; i < framesCount; i++) {
+ debug(9, "frameType %d", i);
+ for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
+ // Load all four orientations
+ framesPointer[i].directions[orient].frameIndex = readS.readUint16();
+ if (_vm->getGameType() == GType_ITE) {
+ framesPointer[i].directions[orient].frameCount = readS.readSint16();
+ } else {
+ framesPointer[i].directions[orient].frameCount = readS.readByte();
+ readS.readByte();
+ }
+ if (framesPointer[i].directions[orient].frameCount < 0)
+ warning("frameCount < 0 (%d)", framesPointer[i].directions[orient].frameCount);
+ debug(9, "frameIndex %d frameCount %d", framesPointer[i].directions[orient].frameIndex, framesPointer[i].directions[orient].frameCount);
+ }
+ }
+
+ free(resourcePointer);
+
+ actor->_frames = framesPointer;
+ actor->_framesCount = framesCount;
+
+ gotSomething = true;
+ } else {
+ warning("Frame List ID = 0 for actor index %d", actor->_index);
+
+ //if (_vm->getGameType() == GType_ITE)
+ return true;
+ }
+
+ if (actor->_spriteListResourceId) {
+ gotSomething = true;
+ } else {
+ warning("Sprite List ID = 0 for actor index %d", actor->_index);
+ }
+
+ return gotSomething;
+}
+
+void Actor::freeActorList() {
+ int i;
+ ActorData *actor;
+ for (i = 0; i < _actorsCount; i++) {
+ actor = _actors[i];
+ delete actor;
+ }
+ free(_actors);
+ _actors = NULL;
+ _actorsCount = 0;
+}
+
+void Actor::loadActorSpriteList(ActorData *actor) {
+ int lastFrame = 0;
+ int resourceId = actor->_spriteListResourceId;
+
+ for (int i = 0; i < actor->_framesCount; i++) {
+ for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
+ if (actor->_frames[i].directions[orient].frameIndex > lastFrame) {
+ lastFrame = actor->_frames[i].directions[orient].frameIndex;
+ }
+ }
+ }
+
+ debug(9, "Loading actor sprite resource id %d", resourceId);
+
+ _vm->_sprite->loadList(resourceId, actor->_spriteList);
+
+ if (_vm->getGameType() == GType_ITE) {
+ if (actor->_flags & kExtended) {
+ while ((lastFrame >= actor->_spriteList.spriteCount)) {
+ resourceId++;
+ debug(9, "Appending to actor sprite list %d", resourceId);
+ _vm->_sprite->loadList(resourceId, actor->_spriteList);
+ }
+ }
+ }
+}
+
+void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResourceID, int protagStatesCount, int protagStatesResourceID) {
+ int i, j;
+ ActorData *actor;
+ byte* actorListData;
+ size_t actorListLength;
+ byte walk[128];
+ byte acv[6];
+ int movementSpeed;
+ int walkStepIndex;
+ int walkStepCount;
+
+ freeActorList();
+
+ _vm->_resource->loadResource(_actorContext, actorsResourceID, actorListData, actorListLength);
+
+ _actorsCount = actorCount;
+
+ if (actorListLength != (uint)_actorsCount * ACTOR_INHM_SIZE) {
+ error("Actor::loadActorList wrong actorlist length");
+ }
+
+ MemoryReadStream actorS(actorListData, actorListLength);
+
+ _actors = (ActorData **)malloc(_actorsCount * sizeof(*_actors));
+ for (i = 0; i < _actorsCount; i++) {
+ actor = _actors[i] = new ActorData();
+ actor->_id = objectIndexToId(kGameObjectActor, i); //actorIndexToId(i);
+ actor->_index = i;
+ debug(4, "init actor id=0x%x index=%d", actor->_id, actor->_index);
+ actorS.readUint32LE(); //next displayed
+ actorS.readByte(); //type
+ actor->_flags = actorS.readByte();
+ actor->_nameIndex = actorS.readUint16LE();
+ actor->_sceneNumber = actorS.readUint32LE();
+ actor->_location.fromStream(actorS);
+ actor->_screenPosition.x = actorS.readUint16LE();
+ actor->_screenPosition.y = actorS.readUint16LE();
+ actor->_screenScale = actorS.readUint16LE();
+ actor->_screenDepth = actorS.readUint16LE();
+ actor->_spriteListResourceId = actorS.readUint32LE();
+ actor->_frameListResourceId = actorS.readUint32LE();
+ debug(4, "%d: %d, %d [%d]", i, actor->_spriteListResourceId, actor->_frameListResourceId, actor->_nameIndex);
+ actor->_scriptEntrypointNumber = actorS.readUint32LE();
+ actorS.readUint32LE(); // xSprite *dSpr;
+ actorS.readUint16LE(); //LEFT
+ actorS.readUint16LE(); //RIGHT
+ actorS.readUint16LE(); //TOP
+ actorS.readUint16LE(); //BOTTOM
+ actor->_speechColor = actorS.readByte();
+ actor->_currentAction = actorS.readByte();
+ actor->_facingDirection = actorS.readByte();
+ actor->_actionDirection = actorS.readByte();
+ actor->_actionCycle = actorS.readUint16LE();
+ actor->_frameNumber = actorS.readUint16LE();
+ actor->_finalTarget.fromStream(actorS);
+ actor->_partialTarget.fromStream(actorS);
+ movementSpeed = actorS.readUint16LE(); //movement speed
+ if (movementSpeed) {
+ error("Actor::loadActorList movementSpeed != 0");
+ }
+ actorS.read(walk, 128);
+ for (j = 0; j < 128; j++) {
+ if (walk[j]) {
+ error("Actor::loadActorList walk[128] != 0");
+ }
+ }
+ //actorS.seek(128, SEEK_CUR);
+ walkStepCount = actorS.readByte();//walkStepCount
+ if (walkStepCount) {
+ error("Actor::loadActorList walkStepCount != 0");
+ }
+ walkStepIndex = actorS.readByte();//walkStepIndex
+ if (walkStepIndex) {
+ error("Actor::loadActorList walkStepIndex != 0");
+ }
+ //no need to check pointers
+ actorS.readUint32LE(); //sprites
+ actorS.readUint32LE(); //frames
+ actorS.readUint32LE(); //last zone
+ actor->_targetObject = actorS.readUint16LE();
+ actor->_actorFlags = actorS.readUint16LE();
+ //no need to check pointers
+ actorS.readUint32LE(); //next in scene
+ actorS.read(acv, 6);
+ for (j = 0; j < 6; j++) {
+ if (acv[j]) {
+ error("Actor::loadActorList acv[%d] != 0", j);
+ }
+ }
+// actorS.seek(6, SEEK_CUR); //action vars
+ }
+ free(actorListData);
+
+ _actors[protagonistIdx]->_flags |= kProtagonist | kExtended;
+
+ for (i = 0; i < _actorsCount; i++) {
+ actor = _actors[i];
+ //if (actor->_flags & kProtagonist) {
+ loadActorResources(actor);
+ //break;
+ //}
+ }
+
+ _centerActor = _protagonist = _actors[protagonistIdx];
+ _protagState = 0;
+
+ if (protagStatesResourceID) {
+ free(_protagStates);
+
+ _protagStates = (ActorFrameSequence *)malloc(sizeof(ActorFrameSequence) * protagStatesCount);
+
+ byte *resourcePointer;
+ size_t resourceLength;
+
+ _vm->_resource->loadResource(_actorContext, protagStatesResourceID,
+ resourcePointer, resourceLength);
+
+
+ MemoryReadStream statesS(resourcePointer, resourceLength);
+
+ for (i = 0; i < protagStatesCount; i++) {
+ for (j = 0; j < ACTOR_DIRECTIONS_COUNT; j++) {
+ _protagStates[i].directions[j].frameIndex = statesS.readUint16LE();
+ _protagStates[i].directions[j].frameCount = statesS.readUint16LE();
+ }
+ }
+ free(resourcePointer);
+
+ _protagonist->_frames = &_protagStates[_protagState];
+ }
+
+ _protagStatesCount = protagStatesCount;
+}
+
+void Actor::freeObjList() {
+ int i;
+ ObjectData *object;
+ for (i = 0; i < _objsCount; i++) {
+ object = _objs[i];
+ delete object;
+ }
+ free(_objs);
+ _objs = NULL;
+ _objsCount = 0;
+}
+
+void Actor::loadObjList(int objectCount, int objectsResourceID) {
+ int i;
+ int frameListResourceId;
+ ObjectData *object;
+ byte* objectListData;
+ size_t objectListLength;
+ freeObjList();
+
+ _vm->_resource->loadResource(_actorContext, objectsResourceID, objectListData, objectListLength);
+
+ _objsCount = objectCount;
+
+ MemoryReadStream objectS(objectListData, objectListLength);
+
+ _objs = (ObjectData **)malloc(_objsCount * sizeof(*_objs));
+ for (i = 0; i < _objsCount; i++) {
+ object = _objs[i] = new ObjectData();
+ object->_id = objectIndexToId(kGameObjectObject, i);
+ object->_index = i;
+ debug(9, "init object id=%d index=%d", object->_id, object->_index);
+ objectS.readUint32LE(); //next displayed
+ objectS.readByte(); //type
+ object->_flags = objectS.readByte();
+ object->_nameIndex = objectS.readUint16LE();
+ object->_sceneNumber = objectS.readUint32LE();
+ object->_location.fromStream(objectS);
+ object->_screenPosition.x = objectS.readUint16LE();
+ object->_screenPosition.y = objectS.readUint16LE();
+ object->_screenScale = objectS.readUint16LE();
+ object->_screenDepth = objectS.readUint16LE();
+ object->_spriteListResourceId = objectS.readUint32LE();
+ frameListResourceId = objectS.readUint32LE(); // object->_frameListResourceId
+ if (frameListResourceId) {
+ error("Actor::loadObjList frameListResourceId != 0");
+ }
+ object->_scriptEntrypointNumber = objectS.readUint32LE();
+ objectS.readUint32LE(); // xSprite *dSpr;
+ objectS.readUint16LE(); //LEFT
+ objectS.readUint16LE(); //RIGHT
+ objectS.readUint16LE(); //TOP
+ objectS.readUint16LE(); //BOTTOM
+ object->_interactBits = objectS.readUint16LE();
+ }
+ free(objectListData);
+}
+
+void Actor::takeExit(uint16 actorId, const HitZone *hitZone) {
+ ActorData *actor;
+ actor = getActor(actorId);
+ actor->_lastZone = NULL;
+
+ _vm->_scene->changeScene(hitZone->getSceneNumber(), hitZone->getActorsEntrance(), kTransitionNoFade);
+ if (_vm->_interface->getMode() != kPanelSceneSubstitute) {
+ _vm->_script->setNoPendingVerb();
+ }
+}
+
+void Actor::stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit, bool stopped) {
+ Event event;
+
+ if (actor != _protagonist) {
+ return;
+ }
+ if (((hitZone->getFlags() & kHitZoneTerminus) && !stopped) || (!(hitZone->getFlags() & kHitZoneTerminus) && stopped)) {
+ return;
+ }
+
+ if (!exit) {
+ if (hitZone->getFlags() & kHitZoneAutoWalk) {
+ actor->_currentAction = kActionWalkDir;
+ actor->_actionDirection = actor->_facingDirection = hitZone->getDirection();
+ actor->_walkFrameSequence = getFrameType(kFrameWalk);
+ return;
+ }
+ } else if (!(hitZone->getFlags() & kHitZoneAutoWalk)) {
+ return;
+ }
+ if (hitZone->getFlags() & kHitZoneExit) {
+ takeExit(actor->_id, hitZone);
+ } else if (hitZone->getScriptNumber() > 0) {
+ event.type = kEvTOneshot;
+ event.code = kScriptEvent;
+ event.op = kEventExecNonBlocking;
+ event.time = 0;
+ event.param = _vm->_scene->getScriptModuleNumber(); // module number
+ event.param2 = hitZone->getScriptNumber(); // script entry point number
+ event.param3 = _vm->_script->getVerbType(kVerbEnter); // Action
+ event.param4 = ID_NOTHING; // Object
+ event.param5 = ID_NOTHING; // With Object
+ event.param6 = ID_PROTAG; // Actor
+
+ _vm->_events->queue(&event);
+ }
+}
+
+void Actor::realLocation(Location &location, uint16 objectId, uint16 walkFlags) {
+ int angle;
+ int distance;
+ ActorData *actor;
+ ObjectData *obj;
+ debug (8, "Actor::realLocation objectId=%i", objectId);
+ if (walkFlags & kWalkUseAngle) {
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ angle = (location.x + 2) & 15;
+ distance = location.y;
+
+ location.u() = (angleLUT[angle][0] * distance) >> 8;
+ location.v() = -(angleLUT[angle][1] * distance) >> 8;
+ } else {
+ angle = location.x & 15;
+ distance = location.y;
+
+ location.x = (angleLUT[angle][0] * distance) >> 6;
+ location.y = (angleLUT[angle][1] * distance) >> 6;
+ }
+ }
+
+ if (objectId != ID_NOTHING) {
+ if (validActorId(objectId)) {
+ actor = getActor(objectId);
+ location.addXY(actor->_location);
+ } else if (validObjId(objectId)) {
+ obj = getObj(objectId);
+ location.addXY(obj->_location);
+ }
+ }
+}
+
+void Actor::actorFaceTowardsPoint(uint16 actorId, const Location &toLocation) {
+ ActorData *actor;
+ Location delta;
+ //debug (8, "Actor::actorFaceTowardsPoint actorId=%i", actorId);
+ actor = getActor(actorId);
+
+ toLocation.delta(actor->_location, delta);
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ if (delta.u() > 0) {
+ actor->_facingDirection = (delta.v() > 0) ? kDirUp : kDirRight;
+ } else {
+ actor->_facingDirection = (delta.v() > 0) ? kDirLeft : kDirDown;
+ }
+ } else {
+ if (ABS(delta.y) > ABS(delta.x * 2)) {
+ actor->_facingDirection = (delta.y > 0) ? kDirDown : kDirUp;
+ } else {
+ actor->_facingDirection = (delta.x > 0) ? kDirRight : kDirLeft;
+ }
+ }
+}
+
+void Actor::actorFaceTowardsObject(uint16 actorId, uint16 objectId) {
+ ActorData *actor;
+ ObjectData *obj;
+
+ if (validActorId(objectId)) {
+ actor = getActor(objectId);
+ actorFaceTowardsPoint(actorId, actor->_location);
+ } else if (validObjId(objectId)) {
+ obj = getObj(objectId);
+ actorFaceTowardsPoint(actorId, obj->_location);
+ }
+}
+
+
+ObjectData *Actor::getObj(uint16 objId) {
+ ObjectData *obj;
+
+ if (!validObjId(objId))
+ error("Actor::getObj Wrong objId 0x%X", objId);
+
+ obj = _objs[objIdToIndex(objId)];
+
+ if (obj->_disabled)
+ error("Actor::getObj disabled objId 0x%X", objId);
+
+ return obj;
+}
+
+ActorData *Actor::getActor(uint16 actorId) {
+ ActorData *actor;
+
+ if (!validActorId(actorId)) {
+ warning("Actor::getActor Wrong actorId 0x%X", actorId);
+ assert(0);
+ }
+
+ if (actorId == ID_PROTAG) {
+ if (_protagonist == NULL) {
+ error("_protagonist == NULL");
+ }
+ return _protagonist;
+ }
+
+ actor = _actors[actorIdToIndex(actorId)];
+
+ if (actor->_disabled)
+ error("Actor::getActor disabled actorId 0x%X", actorId);
+
+ return actor;
+}
+
+bool Actor::validFollowerLocation(const Location &location) {
+ Point point;
+ location.toScreenPointXY(point);
+
+ if ((point.x < 5) || (point.x >= _vm->getDisplayWidth() - 5) ||
+ (point.y < 0) || (point.y > _vm->_scene->getHeight())) {
+ return false;
+ }
+
+ return (_vm->_scene->canWalk(point));
+}
+
+void Actor::setProtagState(int state) {
+ _protagState = state;
+
+ if (_vm->getGameType() == GType_IHNM)
+ _protagonist->_frames = &_protagStates[state];
+}
+
+void Actor::updateActorsScene(int actorsEntrance) {
+ int i, j;
+ int followerDirection;
+ ActorData *actor;
+ Location tempLocation;
+ Location possibleLocation;
+ Point delta;
+ const SceneEntry *sceneEntry;
+
+ if (_vm->_scene->currentSceneNumber() == 0) {
+ error("Actor::updateActorsScene _vm->_scene->currentSceneNumber() == 0");
+ }
+
+ _vm->_sound->stopVoice();
+ _activeSpeech.stringsCount = 0;
+ _activeSpeech.playing = false;
+ _protagonist = NULL;
+
+ for (i = 0; i < _actorsCount; i++) {
+ actor = _actors[i];
+ actor->_inScene = false;
+ actor->_spriteList.freeMem();
+ if (actor->_disabled) {
+ continue;
+ }
+ if ((actor->_flags & (kProtagonist | kFollower)) || (i == 0)) {
+ if (actor->_flags & kProtagonist) {
+ actor->_finalTarget = actor->_location;
+ _centerActor = _protagonist = actor;
+ } else if (_vm->getGameType() == GType_ITE &&
+ _vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) {
+ continue;
+ }
+
+ actor->_sceneNumber = _vm->_scene->currentSceneNumber();
+ }
+ if (actor->_sceneNumber == _vm->_scene->currentSceneNumber()) {
+ actor->_inScene = true;
+ actor->_actionCycle = (_vm->_rnd.getRandomNumber(7) & 0x7) * 4; // 1/8th chance
+ }
+ }
+
+ assert(_protagonist);
+
+ if ((actorsEntrance >= 0) && (_vm->_scene->_entryList.entryListCount > 0)) {
+ if (_vm->_scene->_entryList.entryListCount <= actorsEntrance) {
+ actorsEntrance = 0; //OCEAN bug
+ }
+
+ sceneEntry = _vm->_scene->_entryList.getEntry(actorsEntrance);
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _protagonist->_location = sceneEntry->location;
+ } else {
+ _protagonist->_location.x = sceneEntry->location.x * ACTOR_LMULT;
+ _protagonist->_location.y = sceneEntry->location.y * ACTOR_LMULT;
+ _protagonist->_location.z = sceneEntry->location.z * ACTOR_LMULT;
+ }
+ // Workaround for bug #1328045:
+ // "When entering any of the houses at the start of the
+ // game if you click on anything inside the building you
+ // start walking through the door, turn around and leave."
+ //
+ // After steping of action zone - Rif trying to exit.
+ // This piece of code shift Rif's entry position to non action zone area.
+ if (_vm->getGameType() == GType_ITE) {
+ if ((_vm->_scene->currentSceneNumber() >= 53) && (_vm->_scene->currentSceneNumber() <= 66))
+ _protagonist->_location.y += 10;
+ }
+
+ _protagonist->_facingDirection = _protagonist->_actionDirection = sceneEntry->facing;
+ }
+
+ _protagonist->_currentAction = kActionWait;
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ //nothing?
+ } else {
+ _vm->_scene->initDoorsState(); //TODO: move to _scene
+ }
+
+ followerDirection = _protagonist->_facingDirection + 3;
+ calcScreenPosition(_protagonist);
+
+ for (i = 0; i < _actorsCount; i++) {
+ actor = _actors[i];
+ if (actor->_flags & (kFollower)) {
+ actor->_facingDirection = actor->_actionDirection = _protagonist->_facingDirection;
+ actor->_currentAction = kActionWait;
+ actor->_walkStepsCount = actor->_walkStepIndex = 0;
+ actor->_location.z = _protagonist->_location.z;
+
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _vm->_isoMap->placeOnTileMap(_protagonist->_location, actor->_location, 3, followerDirection & 0x07);
+ } else {
+ followerDirection &= 0x07;
+
+ possibleLocation = _protagonist->_location;
+
+
+ delta.x = directionLUT[followerDirection][0];
+ delta.y = directionLUT[followerDirection][1];
+
+
+ for (j = 0; j < 30; j++) {
+ tempLocation = possibleLocation;
+ tempLocation.x += delta.x;
+ tempLocation.y += delta.y;
+
+ if (validFollowerLocation(tempLocation)) {
+ possibleLocation = tempLocation;
+ } else {
+ tempLocation = possibleLocation;
+ tempLocation.x += delta.x;
+ if (validFollowerLocation(tempLocation)) {
+ possibleLocation = tempLocation;
+ } else {
+ tempLocation = possibleLocation;
+ tempLocation.y += delta.y;
+ if (validFollowerLocation(tempLocation)) {
+ possibleLocation = tempLocation;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ actor->_location = possibleLocation;
+ }
+ followerDirection += 2;
+ }
+
+ }
+
+ handleActions(0, true);
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _vm->_isoMap->adjustScroll(true);
+ }
+}
+
+int Actor::getFrameType(ActorFrameTypes frameType) {
+
+ if (_vm->getGameType() == GType_ITE) {
+ switch (frameType) {
+ case kFrameStand:
+ return kFrameITEStand;
+ case kFrameWalk:
+ return kFrameITEWalk;
+ case kFrameSpeak:
+ return kFrameITESpeak;
+ case kFrameGive:
+ return kFrameITEGive;
+ case kFrameGesture:
+ return kFrameITEGesture;
+ case kFrameWait:
+ return kFrameITEWait;
+ case kFramePickUp:
+ return kFrameITEPickUp;
+ case kFrameLook:
+ return kFrameITELook;
+ }
+ }
+ else {
+ switch (frameType) {
+ case kFrameStand:
+ return kFrameIHNMStand;
+ case kFrameWalk:
+ return kFrameIHNMWalk;
+ case kFrameSpeak:
+ return kFrameIHNMSpeak;
+ case kFrameGesture:
+ return kFrameIHNMGesture;
+ case kFrameWait:
+ return kFrameIHNMWait;
+ case kFrameGive:
+ case kFramePickUp:
+ case kFrameLook:
+ error("Actor::getFrameType() unknown frame type %d", frameType);
+ return kFrameIHNMStand;
+ }
+ }
+ error("Actor::getFrameType() unknown frame type %d", frameType);
+}
+
+ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) {
+ ActorData *actor;
+ int fourDirection;
+ static ActorFrameRange def = {0, 0};
+
+ actor = getActor(actorId);
+ if (actor->_disabled)
+ error("Actor::getActorFrameRange Wrong actorId 0x%X", actorId);
+
+ if ((actor->_facingDirection < kDirUp) || (actor->_facingDirection > kDirUpLeft))
+ error("Actor::getActorFrameRange Wrong direction 0x%X actorId 0x%X", actor->_facingDirection, actorId);
+
+ //if (_vm->getGameType() == GType_ITE) {
+ if (frameType >= actor->_framesCount) {
+ warning("Actor::getActorFrameRange Wrong frameType 0x%X (%d) actorId 0x%X", frameType, actor->_framesCount, actorId);
+ return &def;
+ }
+
+
+ fourDirection = actorDirectectionsLUT[actor->_facingDirection];
+ return &actor->_frames[frameType].directions[fourDirection];
+/*
+ } else {
+ if (0 == actor->_framesCount) {
+ return &def;
+ }
+
+ //TEST
+ if (actor->_id == 0x2000) {
+ if (actor->_framesCount <= _currentFrameIndex) {
+ _currentFrameIndex = 0;
+ }
+ fr = actor->_frames[_currentFrameIndex].directions;
+ return fr;
+ }
+ //TEST
+ if (frameType >= actor->_framesCount) {
+ frameType = actor->_framesCount - 1;
+ }
+ if (frameType < 0) {
+ frameType = 0;
+ }
+
+ if (frameType == kFrameIHNMWalk ) {
+ switch (actor->_facingDirection) {
+ case kDirUpRight:
+ if (frameType > 0)
+ fr = &actor->_frames[frameType - 1].directions[ACTOR_DIRECTION_RIGHT];
+ else
+ fr = &def;
+ if (!fr->frameCount)
+ fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_RIGHT];
+ break;
+ case kDirDownRight:
+ if (frameType > 0)
+ fr = &actor->_frames[frameType - 1].directions[ACTOR_DIRECTION_FORWARD];
+ else
+ fr = &def;
+ if (!fr->frameCount)
+ fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_RIGHT];
+ break;
+ case kDirUpLeft:
+ if (frameType > 0)
+ fr = &actor->_frames[frameType - 1].directions[ACTOR_DIRECTION_LEFT];
+ else
+ fr = &def;
+ if (!fr->frameCount)
+ fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_LEFT];
+ break;
+ case kDirDownLeft:
+ if (frameType > 0)
+ fr = &actor->_frames[frameType - 1].directions[ACTOR_DIRECTION_BACK];
+ else
+ fr = &def;
+ if (!fr->frameCount)
+ fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_LEFT];
+ break;
+ case kDirRight:
+ fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_RIGHT];
+ break;
+ case kDirLeft:
+ fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_LEFT];
+ break;
+ case kDirUp:
+ fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_BACK];
+ break;
+ case kDirDown:
+ fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_FORWARD];
+ break;
+ }
+ return fr;
+ }
+ else {
+ if (frameType >= actor->_framesCount) {
+ error("Actor::getActorFrameRange Wrong frameType 0x%X (%d) actorId 0x%X", frameType, actor->_framesCount, actorId);
+ }
+ fourDirection = actorDirectectionsLUT[actor->_facingDirection];
+ return &actor->_frames[frameType].directions[fourDirection];
+ }
+ }*/
+}
+
+void Actor::handleSpeech(int msec) {
+ int stringLength;
+ int sampleLength;
+ bool removeFirst;
+ int i;
+ ActorData *actor;
+ int width, height, height2;
+ Point posPoint;
+
+ if (_activeSpeech.playing) {
+ _activeSpeech.playingTime -= msec;
+ stringLength = strlen(_activeSpeech.strings[0]);
+
+ removeFirst = false;
+ if (_activeSpeech.playingTime <= 0) {
+ if (_activeSpeech.speechFlags & kSpeakSlow) {
+ _activeSpeech.slowModeCharIndex++;
+ if (_activeSpeech.slowModeCharIndex >= stringLength)
+ removeFirst = true;
+ } else {
+ removeFirst = true;
+ }
+ _activeSpeech.playing = false;
+ if (_activeSpeech.actorIds[0] != 0) {
+ actor = getActor(_activeSpeech.actorIds[0]);
+ if (!(_activeSpeech.speechFlags & kSpeakNoAnimate)) {
+ actor->_currentAction = kActionWait;
+ }
+ }
+ }
+
+ if (removeFirst) {
+ for (i = 1; i < _activeSpeech.stringsCount; i++) {
+ _activeSpeech.strings[i - 1] = _activeSpeech.strings[i];
+ }
+ _activeSpeech.stringsCount--;
+ }
+
+ if (_vm->_script->_skipSpeeches) {
+ _activeSpeech.stringsCount = 0;
+ _vm->_script->wakeUpThreads(kWaitTypeSpeech);
+ return;
+ }
+
+ if (_activeSpeech.stringsCount == 0) {
+ _vm->_script->wakeUpThreadsDelayed(kWaitTypeSpeech, ticksToMSec(kScriptTimeTicksPerSecond / 3));
+ }
+
+ return;
+ }
+
+ if (_vm->_script->_skipSpeeches) {
+ _activeSpeech.stringsCount = 0;
+ _vm->_script->wakeUpThreads(kWaitTypeSpeech);
+ }
+
+ if (_activeSpeech.stringsCount == 0) {
+ return;
+ }
+
+ stringLength = strlen(_activeSpeech.strings[0]);
+
+ if (_activeSpeech.speechFlags & kSpeakSlow) {
+ if (_activeSpeech.slowModeCharIndex >= stringLength)
+ error("Wrong string index");
+
+ warning("Slow string encountered!");
+ _activeSpeech.playingTime = stringLength * 1000 / 4;
+
+ } else {
+ sampleLength = _vm->_sndRes->getVoiceLength(_activeSpeech.sampleResourceId);
+
+ if (sampleLength < 0) {
+ _activeSpeech.playingTime = stringLength * 1000 / 22;
+ switch (_vm->_readingSpeed) {
+ case 1:
+ _activeSpeech.playingTime *= 2;
+ break;
+ case 2:
+ _activeSpeech.playingTime *= 4;
+ break;
+ case 3:
+ _activeSpeech.playingTime = 0x7fffff;
+ break;
+ }
+ } else {
+ _activeSpeech.playingTime = sampleLength;
+ }
+ }
+
+ if (_activeSpeech.sampleResourceId != -1) {
+ _vm->_sndRes->playVoice(_activeSpeech.sampleResourceId);
+ _activeSpeech.sampleResourceId++;
+ }
+
+ if (_activeSpeech.actorIds[0] != 0) {
+ actor = getActor(_activeSpeech.actorIds[0]);
+ if (!(_activeSpeech.speechFlags & kSpeakNoAnimate)) {
+ actor->_currentAction = kActionSpeak;
+ actor->_actionCycle = _vm->_rnd.getRandomNumber(63);
+ }
+ }
+
+ if (_activeSpeech.actorsCount == 1) {
+ if (_speechBoxScript.width() > 0) {
+ _activeSpeech.drawRect.left = _speechBoxScript.left;
+ _activeSpeech.drawRect.right = _speechBoxScript.right;
+ _activeSpeech.drawRect.top = _speechBoxScript.top;
+ _activeSpeech.drawRect.bottom = _speechBoxScript.bottom;
+ } else {
+ width = _activeSpeech.speechBox.width();
+ height = _vm->_font->getHeight(kKnownFontScript, _activeSpeech.strings[0], width - 2, _activeSpeech.getFontFlags(0)) + 1;
+
+ if (height > 40 && width < _vm->getDisplayWidth() - 100) {
+ width = _vm->getDisplayWidth() - 100;
+ height = _vm->_font->getHeight(kKnownFontScript, _activeSpeech.strings[0], width - 2, _activeSpeech.getFontFlags(0)) + 1;
+ }
+
+ _activeSpeech.speechBox.setWidth(width);
+
+ if (_activeSpeech.actorIds[0] != 0) {
+ actor = getActor(_activeSpeech.actorIds[0]);
+ _activeSpeech.speechBox.setHeight(height);
+
+ if (_activeSpeech.speechBox.right > _vm->getDisplayWidth() - 10) {
+ _activeSpeech.drawRect.left = _vm->getDisplayWidth() - 10 - width;
+ } else {
+ _activeSpeech.drawRect.left = _activeSpeech.speechBox.left;
+ }
+
+ height2 = actor->_screenPosition.y - 50;
+ _activeSpeech.speechBox.top = _activeSpeech.drawRect.top = MAX(10, (height2 - height) / 2);
+ } else {
+ _activeSpeech.drawRect.left = _activeSpeech.speechBox.left;
+ _activeSpeech.drawRect.top = _activeSpeech.speechBox.top + (_activeSpeech.speechBox.height() - height) / 2;
+ }
+ _activeSpeech.drawRect.setWidth(width);
+ _activeSpeech.drawRect.setHeight(height);
+ }
+ }
+
+ _activeSpeech.playing = true;
+}
+
+void Actor::handleActions(int msec, bool setup) {
+ int i;
+ ActorData *actor;
+ ActorFrameRange *frameRange;
+ int state;
+ int speed;
+ int32 framesLeft;
+ Location delta;
+ Location addDelta;
+ int hitZoneIndex;
+ const HitZone *hitZone;
+ Point hitPoint;
+ Location pickLocation;
+
+ for (i = 0; i < _actorsCount; i++) {
+ actor = _actors[i];
+ if (!actor->_inScene)
+ continue;
+
+ if ((_vm->getGameType() == GType_ITE) && (i == ACTOR_DRAGON_INDEX)) {
+ moveDragon(actor);
+ continue;
+ }
+
+ switch (actor->_currentAction) {
+ case kActionWait:
+ if (!setup && (actor->_flags & kFollower)) {
+ followProtagonist(actor);
+ if (actor->_currentAction != kActionWait)
+ break;
+ }
+
+ if (actor->_targetObject != ID_NOTHING) {
+ actorFaceTowardsObject(actor->_id, actor->_targetObject);
+ }
+
+ if (actor->_flags & kCycle) {
+ frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand));
+ if (frameRange->frameCount > 0) {
+ actor->_actionCycle++;
+ actor->_actionCycle = (actor->_actionCycle) % frameRange->frameCount;
+ } else {
+ actor->_actionCycle = 0;
+ }
+ actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
+ break;
+ }
+
+ if ((actor->_actionCycle & 3) == 0) {
+ actor->cycleWrap(100);
+
+ frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameWait));
+ if ((frameRange->frameCount < 1 || actor->_actionCycle > 33))
+ frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand));
+
+ if (frameRange->frameCount) {
+ actor->_frameNumber = frameRange->frameIndex + (uint16)_vm->_rnd.getRandomNumber(frameRange->frameCount - 1);
+ } else {
+ actor->_frameNumber = frameRange->frameIndex;
+ }
+ }
+ actor->_actionCycle++;
+ break;
+
+ case kActionWalkToPoint:
+ case kActionWalkToLink:
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ actor->_partialTarget.delta(actor->_location, delta);
+
+ while ((delta.u() == 0) && (delta.v() == 0)) {
+
+ if ((actor == _protagonist) && (_vm->mouseButtonPressed())) {
+ _vm->_isoMap->screenPointToTileCoords(_vm->mousePos(), pickLocation);
+
+ if (!actorWalkTo(_protagonist->_id, pickLocation)) {
+ break;
+ }
+ } else if (!_vm->_isoMap->nextTileTarget(actor) && !actorEndWalk(actor->_id, true)) {
+ break;
+ }
+
+ actor->_partialTarget.delta(actor->_location, delta);
+ actor->_partialTarget.z = 0;
+ }
+
+ if (actor->_flags & kFastest) {
+ speed = 8;
+ } else if (actor->_flags & kFaster) {
+ speed = 6;
+ } else {
+ speed = 4;
+ }
+
+ if (_vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) {
+ speed = 2;
+ }
+
+ if ((actor->_actionDirection == 2) || (actor->_actionDirection == 6)) {
+ speed = speed / 2;
+ }
+
+ if (ABS(delta.v()) > ABS(delta.u())) {
+ addDelta.v() = clamp(-speed, delta.v(), speed);
+ if (addDelta.v() == delta.v()) {
+ addDelta.u() = delta.u();
+ } else {
+ addDelta.u() = delta.u() * addDelta.v();
+ addDelta.u() += (addDelta.u() > 0) ? (delta.v() / 2) : (-delta.v() / 2);
+ addDelta.u() /= delta.v();
+ }
+ } else {
+ addDelta.u() = clamp(-speed, delta.u(), speed);
+ if (addDelta.u() == delta.u()) {
+ addDelta.v() = delta.v();
+ } else {
+ addDelta.v() = delta.v() * addDelta.u();
+ addDelta.v() += (addDelta.v() > 0) ? (delta.u() / 2) : (-delta.u() / 2);
+ addDelta.v() /= delta.u();
+ }
+ }
+
+ actor->_location.add(addDelta);
+ } else {
+ actor->_partialTarget.delta(actor->_location, delta);
+
+ while ((delta.x == 0) && (delta.y == 0)) {
+
+ if (actor->_walkStepIndex >= actor->_walkStepsCount) {
+ actorEndWalk(actor->_id, true);
+ break;
+ }
+
+ actor->_partialTarget.fromScreenPoint(actor->_walkStepsPoints[actor->_walkStepIndex++]);
+ if (_vm->getGameType() == GType_ITE) {
+ if (actor->_partialTarget.x > 224 * 2 * ACTOR_LMULT) {
+ actor->_partialTarget.x -= 256 * 2 * ACTOR_LMULT;
+ }
+ } else {
+ if (actor->_partialTarget.x > 224 * 4 * ACTOR_LMULT) {
+ actor->_partialTarget.x -= 256 * 4 * ACTOR_LMULT;
+ }
+ }
+
+ actor->_partialTarget.delta(actor->_location, delta);
+
+ if (ABS(delta.y) > ABS(delta.x)) {
+ actor->_actionDirection = delta.y > 0 ? kDirDown : kDirUp;
+ } else {
+ actor->_actionDirection = delta.x > 0 ? kDirRight : kDirLeft;
+ }
+ }
+
+ speed = (ACTOR_LMULT * 2 * actor->_screenScale + 63) / 256;
+ if (speed < 1) {
+ speed = 1;
+ }
+
+ if ((actor->_actionDirection == kDirUp) || (actor->_actionDirection == kDirDown)) {
+ addDelta.y = clamp(-speed, delta.y, speed);
+ if (addDelta.y == delta.y) {
+ addDelta.x = delta.x;
+ } else {
+ addDelta.x = delta.x * addDelta.y;
+ addDelta.x += (addDelta.x > 0) ? (delta.y / 2) : (-delta.y / 2);
+ addDelta.x /= delta.y;
+ actor->_facingDirection = actor->_actionDirection;
+ }
+ } else {
+ addDelta.x = clamp(-2 * speed, delta.x, 2 * speed);
+ if (addDelta.x == delta.x) {
+ addDelta.y = delta.y;
+ } else {
+ addDelta.y = delta.y * addDelta.x;
+ addDelta.y += (addDelta.y > 0) ? (delta.x / 2) : (-delta.x / 2);
+ addDelta.y /= delta.x;
+ actor->_facingDirection = actor->_actionDirection;
+ }
+ }
+
+ actor->_location.add(addDelta);
+ }
+
+ if (actor->_actorFlags & kActorBackwards) {
+ actor->_facingDirection = (actor->_actionDirection + 4) & 7;
+ actor->_actionCycle--;
+ } else {
+ actor->_actionCycle++;
+ }
+
+ frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence);
+
+ if (actor->_actionCycle < 0) {
+ actor->_actionCycle = frameRange->frameCount - 1;
+ } else if (actor->_actionCycle >= frameRange->frameCount) {
+ actor->_actionCycle = 0;
+ }
+
+ actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
+ break;
+
+ case kActionWalkDir:
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ actor->_location.u() += tileDirectionLUT[actor->_actionDirection][0];
+ actor->_location.v() += tileDirectionLUT[actor->_actionDirection][1];
+
+ frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence);
+
+ actor->_actionCycle++;
+ actor->cycleWrap(frameRange->frameCount);
+ actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
+ } else {
+ actor->_location.x += directionLUT[actor->_actionDirection][0] * 2;
+ actor->_location.y += directionLUT[actor->_actionDirection][1] * 2;
+
+ frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence);
+ actor->_actionCycle++;
+ actor->cycleWrap(frameRange->frameCount);
+ actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
+ }
+ break;
+
+ case kActionSpeak:
+ actor->_actionCycle++;
+ actor->cycleWrap(64);
+
+ frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameGesture));
+ if (actor->_actionCycle >= frameRange->frameCount) {
+ if (actor->_actionCycle & 1)
+ break;
+ frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameSpeak));
+
+ state = (uint16)_vm->_rnd.getRandomNumber(frameRange->frameCount);
+
+ if (state == 0) {
+ frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand));
+ } else {
+ state--;
+ }
+ } else {
+ state = actor->_actionCycle;
+ }
+
+ actor->_frameNumber = frameRange->frameIndex + state;
+ break;
+
+ case kActionAccept:
+ case kActionStoop:
+ break;
+
+ case kActionCycleFrames:
+ case kActionPongFrames:
+ if (actor->_cycleTimeCount > 0) {
+ actor->_cycleTimeCount--;
+ break;
+ }
+
+ actor->_cycleTimeCount = actor->_cycleDelay;
+ actor->_actionCycle++;
+
+ frameRange = getActorFrameRange(actor->_id, actor->_cycleFrameSequence);
+
+ if (actor->_currentAction == kActionPongFrames) {
+ if (actor->_actionCycle >= frameRange->frameCount * 2 - 2) {
+ if (actor->_actorFlags & kActorContinuous) {
+ actor->_actionCycle = 0;
+ } else {
+ actor->_currentAction = kActionFreeze;
+ break;
+ }
+ }
+
+ state = actor->_actionCycle;
+ if (state >= frameRange->frameCount) {
+ state = frameRange->frameCount * 2 - 2 - state;
+ }
+ } else {
+ if (actor->_actionCycle >= frameRange->frameCount) {
+ if (actor->_actorFlags & kActorContinuous) {
+ actor->_actionCycle = 0;
+ } else {
+ actor->_currentAction = kActionFreeze;
+ break;
+ }
+ }
+ state = actor->_actionCycle;
+ }
+
+ if (frameRange->frameCount && (actor->_actorFlags & kActorRandom)) {
+ state = _vm->_rnd.getRandomNumber(frameRange->frameCount - 1);
+ }
+
+ if (actor->_actorFlags & kActorBackwards) {
+ actor->_frameNumber = frameRange->frameIndex + frameRange->frameCount - 1 - state;
+ } else {
+ actor->_frameNumber = frameRange->frameIndex + state;
+ }
+ break;
+
+ case kActionFall:
+ if (actor->_actionCycle > 0) {
+ framesLeft = actor->_actionCycle--;
+ actor->_finalTarget.delta(actor->_location, delta);
+ delta.x /= framesLeft;
+ delta.y /= framesLeft;
+ actor->_location.addXY(delta);
+ actor->_fallVelocity += actor->_fallAcceleration;
+ actor->_fallPosition += actor->_fallVelocity;
+ actor->_location.z = actor->_fallPosition >> 4;
+ } else {
+ actor->_location = actor->_finalTarget;
+ actor->_currentAction = kActionFreeze;
+ _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor);
+ }
+ break;
+
+ case kActionClimb:
+ actor->_cycleDelay++;
+ if (actor->_cycleDelay & 3) {
+ break;
+ }
+
+ if (actor->_location.z >= actor->_finalTarget.z + ACTOR_CLIMB_SPEED) {
+ actor->_location.z -= ACTOR_CLIMB_SPEED;
+ actor->_actionCycle--;
+ } else if (actor->_location.z <= actor->_finalTarget.z - ACTOR_CLIMB_SPEED) {
+ actor->_location.z += ACTOR_CLIMB_SPEED;
+ actor->_actionCycle++;
+ } else {
+ actor->_location.z = actor->_finalTarget.z;
+ actor->_currentAction = kActionFreeze;
+ _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor);
+ }
+
+ frameRange = getActorFrameRange(actor->_id, actor->_cycleFrameSequence);
+
+ if (actor->_actionCycle < 0) {
+ actor->_actionCycle = frameRange->frameCount - 1;
+ }
+ actor->cycleWrap(frameRange->frameCount);
+ actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
+ break;
+ }
+
+ if ((actor->_currentAction >= kActionWalkToPoint) && (actor->_currentAction <= kActionWalkDir)) {
+ hitZone = NULL;
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ actor->_location.toScreenPointUV(hitPoint);
+ } else {
+ actor->_location.toScreenPointXY(hitPoint);
+ }
+ hitZoneIndex = _vm->_scene->_actionMap->hitTest(hitPoint);
+ if (hitZoneIndex != -1) {
+ hitZone = _vm->_scene->_actionMap->getHitZone(hitZoneIndex);
+ }
+
+ if (hitZone != actor->_lastZone) {
+ if (actor->_lastZone)
+ stepZoneAction(actor, actor->_lastZone, true, false);
+ actor->_lastZone = hitZone;
+ if (hitZone)
+ stepZoneAction(actor, hitZone, false, false);
+ }
+ }
+ }
+}
+
+void Actor::direct(int msec) {
+
+ if (_vm->_scene->_entryList.entryListCount == 0) {
+ return;
+ }
+
+ if (_vm->_interface->_statusTextInput) {
+ return;
+ }
+
+ // FIXME: HACK. This should be turned into cycle event.
+ _lastTickMsec += msec;
+
+ if (_lastTickMsec > 1000 / _handleActionDiv) {
+ _lastTickMsec = 0;
+ //process actions
+ handleActions(msec, false);
+ }
+
+//process speech
+ handleSpeech(msec);
+}
+
+
+bool Actor::calcScreenPosition(CommonObjectData *commonObjectData) {
+ int beginSlope, endSlope, middle;
+ bool result;
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _vm->_isoMap->tileCoordsToScreenPoint(commonObjectData->_location, commonObjectData->_screenPosition);
+ commonObjectData->_screenScale = 256;
+ } else {
+ middle = _vm->_scene->getHeight() - commonObjectData->_location.y / ACTOR_LMULT;
+
+ _vm->_scene->getSlopes(beginSlope, endSlope);
+
+ commonObjectData->_screenDepth = (14 * middle) / endSlope + 1;
+
+ if (middle <= beginSlope) {
+ commonObjectData->_screenScale = 256;
+ } else if (middle >= endSlope) {
+ commonObjectData->_screenScale = 1;
+ } else {
+ middle -= beginSlope;
+ endSlope -= beginSlope;
+ commonObjectData->_screenScale = 256 - (middle * 256) / endSlope;
+ }
+
+ commonObjectData->_location.toScreenPointXYZ(commonObjectData->_screenPosition);
+ }
+
+ result = commonObjectData->_screenPosition.x > -64 &&
+ commonObjectData->_screenPosition.x < _vm->getDisplayWidth() + 64 &&
+ commonObjectData->_screenPosition.y > -64 &&
+ commonObjectData->_screenPosition.y < _vm->_scene->getHeight() + 64;
+
+ return result;
+}
+
+uint16 Actor::hitTest(const Point &testPoint, bool skipProtagonist) {
+ // We can only interact with objects or actors that are inside the
+ // scene area. While this is usually the entire upper part of the
+ // screen, it could also be an inset. Note that other kinds of hit
+ // areas may be outside the inset, and that those are still perfectly
+ // fine to interact with. For example, the door entrance at the glass
+ // makers's house in ITE's ferret village.
+
+ if (!_vm->_scene->getSceneClip().contains(testPoint))
+ return ID_NOTHING;
+
+ CommonObjectOrderList::iterator drawOrderIterator;
+ CommonObjectDataPointer drawObject;
+ int frameNumber;
+ SpriteList *spriteList;
+
+ createDrawOrderList();
+
+ for (drawOrderIterator = _drawOrderList.begin(); drawOrderIterator != _drawOrderList.end(); ++drawOrderIterator) {
+ drawObject = drawOrderIterator.operator*();
+ if (skipProtagonist && (drawObject == _protagonist)) {
+ continue;
+ }
+ if (!getSpriteParams(drawObject, frameNumber, spriteList)) {
+ continue;
+ }
+ if (_vm->_sprite->hitTest(*spriteList, frameNumber, drawObject->_screenPosition, drawObject->_screenScale, testPoint)) {
+ return drawObject->_id;
+ }
+ }
+ return ID_NOTHING;
+}
+
+void Actor::createDrawOrderList() {
+ int i;
+ ActorData *actor;
+ ObjectData *obj;
+ CommonObjectOrderList::CompareFunction compareFunction;
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ compareFunction = &tileCommonObjectCompare;
+ } else {
+ compareFunction = &commonObjectCompare;
+ }
+
+ _drawOrderList.clear();
+ for (i = 0; i < _actorsCount; i++) {
+ actor = _actors[i];
+
+ if (!actor->_inScene)
+ continue;
+
+ if (calcScreenPosition(actor)) {
+ _drawOrderList.pushBack(actor, compareFunction);
+ }
+ }
+
+ for (i = 0; i < _objsCount; i++) {
+ obj = _objs[i];
+ if (obj->_disabled)
+ continue;
+
+ if (obj->_sceneNumber != _vm->_scene->currentSceneNumber())
+ continue;
+
+ if (calcScreenPosition(obj)) {
+ _drawOrderList.pushBack(obj, compareFunction);
+ }
+ }
+}
+
+bool Actor::getSpriteParams(CommonObjectData *commonObjectData, int &frameNumber, SpriteList *&spriteList) {
+ if (_vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) {
+ if (!(commonObjectData->_flags & kProtagonist)){
+// warning("not protagonist");
+ return false;
+ }
+ frameNumber = 8;
+ spriteList = &_vm->_sprite->_mainSprites;
+ } else if (validActorId(commonObjectData->_id)) {
+ ActorData *actor = (ActorData *)commonObjectData;
+ spriteList = &(actor->_spriteList);
+ frameNumber = actor->_frameNumber;
+ if (spriteList->infoList == NULL)
+ loadActorSpriteList(actor);
+
+ } else if (validObjId(commonObjectData->_id)) {
+ spriteList = &_vm->_sprite->_mainSprites;
+ frameNumber = commonObjectData->_spriteListResourceId;
+ }
+
+ if ((frameNumber < 0) || (spriteList->spriteCount <= frameNumber)) {
+ debug(1, "Actor::getSpriteParams frameNumber invalid for %s id 0x%X (%d)",
+ validObjId(commonObjectData->_id) ? "object" : "actor",
+ commonObjectData->_id, frameNumber);
+ return false;
+ }
+ return true;
+}
+
+void Actor::drawActors() {
+ if (_vm->_anim->hasCutaway()) {
+ drawSpeech();
+ return;
+ }
+
+ if (_vm->_scene->currentSceneNumber() <= 0) {
+ return;
+ }
+
+ if (_vm->_scene->_entryList.entryListCount == 0) {
+ return;
+ }
+
+ CommonObjectOrderList::iterator drawOrderIterator;
+ CommonObjectDataPointer drawObject;
+ int frameNumber;
+ SpriteList *spriteList;
+
+ Surface *backBuffer;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ createDrawOrderList();
+
+ for (drawOrderIterator = _drawOrderList.begin(); drawOrderIterator != _drawOrderList.end(); ++drawOrderIterator) {
+ drawObject = drawOrderIterator.operator*();
+
+ if (!getSpriteParams(drawObject, frameNumber, spriteList)) {
+ continue;
+ }
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _vm->_isoMap->drawSprite(backBuffer, *spriteList, frameNumber, drawObject->_location, drawObject->_screenPosition, drawObject->_screenScale);
+ } else {
+ _vm->_sprite->drawOccluded(backBuffer, _vm->_scene->getSceneClip(),*spriteList, frameNumber, drawObject->_screenPosition, drawObject->_screenScale, drawObject->_screenDepth);
+ }
+ }
+
+ drawSpeech();
+}
+
+void Actor::drawSpeech(void) {
+ if (!isSpeaking() || !_activeSpeech.playing || _vm->_script->_skipSpeeches
+ || (!_vm->_subtitlesEnabled && (_vm->getFeatures() & GF_CD_FX)))
+ return;
+
+ int i;
+ Point textPoint;
+ ActorData *actor;
+ int width, height;
+ char oneChar[2];
+ oneChar[1] = 0;
+ const char *outputString;
+ Surface *backBuffer;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ if (_activeSpeech.speechFlags & kSpeakSlow) {
+ outputString = oneChar;
+ oneChar[0] = _activeSpeech.strings[0][_activeSpeech.slowModeCharIndex];
+ } else {
+ outputString = _activeSpeech.strings[0];
+ }
+
+ if (_activeSpeech.actorsCount > 1) {
+ height = _vm->_font->getHeight(kKnownFontScript);
+ width = _vm->_font->getStringWidth(kKnownFontScript, _activeSpeech.strings[0], 0, kFontNormal);
+
+ for (i = 0; i < _activeSpeech.actorsCount; i++) {
+ actor = getActor(_activeSpeech.actorIds[i]);
+ calcScreenPosition(actor);
+
+ textPoint.x = clamp(10, actor->_screenPosition.x - width / 2, _vm->getDisplayWidth() - 10 - width);
+ textPoint.y = clamp(10, actor->_screenPosition.y - 58, _vm->_scene->getHeight() - 10 - height);
+
+ _vm->_font->textDraw(kKnownFontScript, backBuffer, _activeSpeech.strings[0], textPoint,
+ _activeSpeech.speechColor[i], _activeSpeech.outlineColor[i], _activeSpeech.getFontFlags(i));
+ }
+ } else {
+ _vm->_font->textDrawRect(kKnownFontScript, backBuffer, _activeSpeech.strings[0], _activeSpeech.drawRect, _activeSpeech.speechColor[0],
+ _activeSpeech.outlineColor[0], _activeSpeech.getFontFlags(0));
+ }
+}
+
+bool Actor::followProtagonist(ActorData *actor) {
+ Location protagonistLocation;
+ Location newLocation;
+ Location delta;
+ int protagonistBGMaskType;
+ Point prefer1;
+ Point prefer2;
+ Point prefer3;
+ int16 prefU;
+ int16 prefV;
+ int16 newU;
+ int16 newV;
+
+ assert(_protagonist);
+
+ actor->_flags &= ~(kFaster | kFastest);
+ protagonistLocation = _protagonist->_location;
+ calcScreenPosition(_protagonist);
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ prefU = 60;
+ prefV = 60;
+
+
+ actor->_location.delta(protagonistLocation, delta);
+
+ if (actor->_id == actorIndexToId(2)) {
+ prefU = prefV = 48;
+ }
+
+ if ((delta.u() > prefU) || (delta.u() < -prefU) || (delta.v() > prefV) || (delta.v() < -prefV)) {
+
+ if ((delta.u() > prefU * 2) || (delta.u() < -prefU * 2) || (delta.v() > prefV * 2) || (delta.v() < -prefV * 2)) {
+ actor->_flags |= kFaster;
+
+ if ((delta.u() > prefU * 3) || (delta.u() < -prefU*3) || (delta.v() > prefV * 3) || (delta.v() < -prefV * 3)) {
+ actor->_flags |= kFastest;
+ }
+ }
+
+ prefU /= 2;
+ prefV /= 2;
+
+ newU = clamp(-prefU, delta.u(), prefU) + protagonistLocation.u();
+ newV = clamp(-prefV, delta.v(), prefV) + protagonistLocation.v();
+
+ newLocation.u() = newU + _vm->_rnd.getRandomNumber(prefU - 1) - prefU / 2;
+ newLocation.v() = newV + _vm->_rnd.getRandomNumber(prefV - 1) - prefV / 2;
+ newLocation.z = 0;
+
+ return actorWalkTo(actor->_id, newLocation);
+ }
+
+ } else {
+ prefer1.x = (100 * _protagonist->_screenScale) >> 8;
+ prefer1.y = (50 * _protagonist->_screenScale) >> 8;
+
+ if (_protagonist->_currentAction == kActionWalkDir) {
+ prefer1.x /= 2;
+ }
+
+ if (prefer1.x < 8) {
+ prefer1.x = 8;
+ }
+
+ if (prefer1.y < 8) {
+ prefer1.y = 8;
+ }
+
+ prefer2.x = prefer1.x * 2;
+ prefer2.y = prefer1.y * 2;
+ prefer3.x = prefer1.x + prefer1.x / 2;
+ prefer3.y = prefer1.y + prefer1.y / 2;
+
+ actor->_location.delta(protagonistLocation, delta);
+
+ protagonistBGMaskType = 0;
+ if (_vm->_scene->isBGMaskPresent() && _vm->_scene->validBGMaskPoint(_protagonist->_screenPosition)) {
+ protagonistBGMaskType = _vm->_scene->getBGMaskType(_protagonist->_screenPosition);
+ }
+
+ if ((_vm->_rnd.getRandomNumber(7) & 0x7) == 0) // 1/8th chance
+ actor->_actorFlags &= ~kActorNoFollow;
+
+ if (actor->_actorFlags & kActorNoFollow) {
+ return false;
+ }
+
+ if ((delta.x > prefer2.x) || (delta.x < -prefer2.x) ||
+ (delta.y > prefer2.y) || (delta.y < -prefer2.y) ||
+ ((_protagonist->_currentAction == kActionWait) &&
+ (delta.x * 2 < prefer1.x) && (delta.x * 2 > -prefer1.x) &&
+ (delta.y < prefer1.y) && (delta.y > -prefer1.y))) {
+
+ if (ABS(delta.x) > ABS(delta.y)) {
+
+ delta.x = (delta.x > 0) ? prefer3.x : -prefer3.x;
+
+ newLocation.x = delta.x + protagonistLocation.x;
+ newLocation.y = clamp(-prefer2.y, delta.y, prefer2.y) + protagonistLocation.y;
+ } else {
+ delta.y = (delta.y > 0) ? prefer3.y : -prefer3.y;
+
+ newLocation.x = clamp(-prefer2.x, delta.x, prefer2.x) + protagonistLocation.x;
+ newLocation.y = delta.y + protagonistLocation.y;
+ }
+ newLocation.z = 0;
+
+ if (protagonistBGMaskType != 3) {
+ newLocation.x += _vm->_rnd.getRandomNumber(prefer1.x - 1) - prefer1.x / 2;
+ newLocation.y += _vm->_rnd.getRandomNumber(prefer1.y - 1) - prefer1.y / 2;
+ }
+
+ newLocation.x = clamp(-31*4, newLocation.x, (_vm->getDisplayWidth() + 31) * 4); //fixme
+
+ return actorWalkTo(actor->_id, newLocation);
+ }
+ }
+ return false;
+}
+
+bool Actor::actorEndWalk(uint16 actorId, bool recurse) {
+ bool walkMore = false;
+ ActorData *actor;
+ const HitZone *hitZone;
+ int hitZoneIndex;
+ Point testPoint;
+
+ actor = getActor(actorId);
+ actor->_actorFlags &= ~kActorBackwards;
+
+ if (actor->_location.distance(actor->_finalTarget) > 8 && (actor->_flags & kProtagonist) && recurse && !(actor->_actorFlags & kActorNoCollide)) {
+ actor->_actorFlags |= kActorNoCollide;
+ return actorWalkTo(actorId, actor->_finalTarget);
+ }
+
+ actor->_currentAction = kActionWait;
+ if (actor->_actorFlags & kActorFinalFace) {
+ actor->_facingDirection = actor->_actionDirection = (actor->_actorFlags >> 6) & 0x07; //?
+ }
+
+ actor->_actorFlags &= ~(kActorNoCollide | kActorCollided | kActorFinalFace | kActorFacingMask);
+ actor->_flags &= ~(kFaster | kFastest);
+
+ if (actor == _protagonist) {
+ _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor);
+ if (_vm->_script->_pendingVerb == _vm->_script->getVerbType(kVerbWalkTo)) {
+ if (_vm->getGameType() == GType_ITE)
+ actor->_location.toScreenPointUV(testPoint); // it's wrong calculation, but it is used in ITE
+ else
+ actor->_location.toScreenPointXY(testPoint);
+
+ hitZoneIndex = _vm->_scene->_actionMap->hitTest(testPoint);
+ if (hitZoneIndex != -1) {
+ hitZone = _vm->_scene->_actionMap->getHitZone(hitZoneIndex);
+ stepZoneAction(actor, hitZone, false, true);
+ } else {
+ _vm->_script->setNoPendingVerb();
+ }
+ } else if (_vm->_script->_pendingVerb != _vm->_script->getVerbType(kVerbNone)) {
+ _vm->_script->doVerb();
+ }
+ } else {
+ if (recurse && (actor->_flags & kFollower))
+ walkMore = followProtagonist(actor);
+
+ _vm->_script->wakeUpActorThread(kWaitTypeWalk, actor);
+ }
+ return walkMore;
+}
+
+bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
+ ActorData *actor;
+ ActorData *anotherActor;
+ int i;
+
+ Rect testBox;
+ Rect testBox2;
+ Point anotherActorScreenPosition;
+ Point collision;
+ Point pointFrom, pointTo, pointBest, pointAdd;
+ Point delta, bestDelta;
+ Point tempPoint;
+ bool extraStartNode;
+ bool extraEndNode;
+
+ actor = getActor(actorId);
+
+ if (actor == _protagonist) {
+ _vm->_scene->setDoorState(2, 0xff);
+ _vm->_scene->setDoorState(3, 0);
+ } else {
+ _vm->_scene->setDoorState(2, 0);
+ _vm->_scene->setDoorState(3, 0xff);
+ }
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+
+ if ((_vm->getGameType() == GType_ITE) && (actor->_index == ACTOR_DRAGON_INDEX)) {
+ return false;
+ }
+
+ actor->_finalTarget = toLocation;
+ actor->_walkStepsCount = 0;
+ _vm->_isoMap->findTilePath(actor, actor->_location, toLocation);
+
+
+ if ((actor->_walkStepsCount == 0) && (actor->_flags & kProtagonist)) {
+ actor->_actorFlags |= kActorNoCollide;
+ _vm->_isoMap->findTilePath(actor, actor->_location, toLocation);
+ }
+
+ actor->_walkStepIndex = 0;
+ if (_vm->_isoMap->nextTileTarget(actor)) {
+ actor->_currentAction = kActionWalkToPoint;
+ actor->_walkFrameSequence = getFrameType(kFrameWalk);
+ } else {
+ actorEndWalk(actorId, false);
+ return false;
+ }
+ } else {
+
+ actor->_location.toScreenPointXY(pointFrom);
+ pointFrom.x &= ~1;
+
+ extraStartNode = _vm->_scene->offscreenPath(pointFrom);
+
+ toLocation.toScreenPointXY(pointTo);
+ pointTo.x &= ~1;
+
+ extraEndNode = _vm->_scene->offscreenPath(pointTo);
+
+ if (_vm->_scene->isBGMaskPresent()) {
+
+ if ((((actor->_currentAction >= kActionWalkToPoint) &&
+ (actor->_currentAction <= kActionWalkDir)) || (actor == _protagonist)) &&
+ !_vm->_scene->canWalk(pointFrom)) {
+ for (i = 1; i < 8; i++) {
+ pointAdd = pointFrom;
+ pointAdd.y += i;
+ if (_vm->_scene->canWalk(pointAdd)) {
+ pointFrom = pointAdd;
+ break;
+ }
+ pointAdd = pointFrom;
+ pointAdd.y -= i;
+ if (_vm->_scene->canWalk(pointAdd)) {
+ pointFrom = pointAdd;
+ break;
+ }
+ pointAdd = pointFrom;
+ pointAdd.x += i;
+ if (_vm->_scene->canWalk(pointAdd)) {
+ pointFrom = pointAdd;
+ break;
+ }
+ pointAdd = pointFrom;
+ pointAdd.x -= i;
+ if (_vm->_scene->canWalk(pointAdd)) {
+ pointFrom = pointAdd;
+ break;
+ }
+ }
+ }
+
+ _barrierCount = 0;
+ if (!(actor->_actorFlags & kActorNoCollide)) {
+ collision.x = ACTOR_COLLISION_WIDTH * actor->_screenScale / (256 * 2);
+ collision.y = ACTOR_COLLISION_HEIGHT * actor->_screenScale / (256 * 2);
+
+
+ for (i = 0; (i < _actorsCount) && (_barrierCount < ACTOR_BARRIERS_MAX); i++) {
+ anotherActor = _actors[i];
+ if (!anotherActor->_inScene)
+ continue;
+ if (anotherActor == actor)
+ continue;
+
+ anotherActorScreenPosition = anotherActor->_screenPosition;
+ testBox.left = (anotherActorScreenPosition.x - collision.x) & ~1;
+ testBox.right = (anotherActorScreenPosition.x + collision.x) & ~1 + 1;
+ testBox.top = anotherActorScreenPosition.y - collision.y;
+ testBox.bottom = anotherActorScreenPosition.y + collision.y + 1;
+ testBox2 = testBox;
+ testBox2.right += 2;
+ testBox2.left -= 2;
+ testBox2.top -= 1;
+ testBox2.bottom += 1;
+
+ if (testBox2.contains(pointFrom)) {
+ if (pointFrom.x > anotherActorScreenPosition.x + 4) {
+ testBox.right = pointFrom.x - 1;
+ } else if (pointFrom.x < anotherActorScreenPosition.x - 4) {
+ testBox.left = pointFrom.x + 2;
+ } else if (pointFrom.y > anotherActorScreenPosition.y) {
+ testBox.bottom = pointFrom.y;
+ } else {
+ testBox.top = pointFrom.y + 1 ;
+ }
+ }
+
+ if ((testBox.width() > 0) && (testBox.height() > 0)) {
+ _barrierList[_barrierCount++] = testBox;
+ }
+ }
+ }
+
+
+ pointBest = pointTo;
+ actor->_walkStepsCount = 0;
+ findActorPath(actor, pointFrom, pointTo);
+
+ if (actor->_walkStepsCount == 0) {
+ error("actor->_walkStepsCount == 0");
+ }
+
+ if (extraStartNode) {
+ actor->_walkStepIndex = 0;
+ } else {
+ actor->_walkStepIndex = 1;
+ }
+
+ if (extraEndNode) {
+ toLocation.toScreenPointXY(tempPoint);
+ actor->_walkStepsCount--;
+ actor->addWalkStepPoint(tempPoint);
+ }
+
+
+ pointBest = actor->_walkStepsPoints[actor->_walkStepsCount - 1];
+
+ pointBest.x &= ~1;
+ delta.x = ABS(pointFrom.x - pointTo.x);
+ delta.y = ABS(pointFrom.y - pointTo.y);
+
+ bestDelta.x = ABS(pointBest.x - pointTo.x);
+ bestDelta.y = ABS(pointBest.y - pointTo.y);
+
+ if ((delta.x + delta.y <= bestDelta.x + bestDelta.y) && (actor->_flags & kFollower)) {
+ actor->_actorFlags |= kActorNoFollow;
+ }
+
+ if (pointBest == pointFrom) {
+ actor->_walkStepsCount = 0;
+ }
+ } else {
+ actor->_walkStepsCount = 0;
+ actor->addWalkStepPoint(pointTo);
+ actor->_walkStepIndex = 0;
+ }
+
+ actor->_partialTarget = actor->_location;
+ actor->_finalTarget = toLocation;
+ if (actor->_walkStepsCount == 0) {
+ actorEndWalk(actorId, false);
+ return false;
+ } else {
+ if (actor->_flags & kProtagonist) {
+ _actors[1]->_actorFlags &= ~kActorNoFollow; // TODO: mark all actors with kFollower flag, not only 1 and 2
+ _actors[2]->_actorFlags &= ~kActorNoFollow;
+ }
+ actor->_currentAction = (actor->_walkStepsCount >= ACTOR_MAX_STEPS_COUNT) ? kActionWalkToLink : kActionWalkToPoint;
+ actor->_walkFrameSequence = getFrameType(kFrameWalk);
+ }
+ }
+ return true;
+}
+
+void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount, int sampleResourceId, int speechFlags) {
+ ActorData *actor;
+ int i;
+ int16 dist;
+
+ actor = getActor(actorId);
+ calcScreenPosition(actor);
+ for (i = 0; i < stringsCount; i++) {
+ _activeSpeech.strings[i] = strings[i];
+ }
+
+ _activeSpeech.stringsCount = stringsCount;
+ _activeSpeech.speechFlags = speechFlags;
+ _activeSpeech.actorsCount = 1;
+ _activeSpeech.actorIds[0] = actorId;
+ _activeSpeech.speechColor[0] = actor->_speechColor;
+ _activeSpeech.outlineColor[0] = (_vm->getGameType() == GType_ITE ? kITEColorBlack : kIHNMColorBlack);
+ _activeSpeech.sampleResourceId = sampleResourceId;
+ _activeSpeech.playing = false;
+ _activeSpeech.slowModeCharIndex = 0;
+
+ dist = MIN(actor->_screenPosition.x - 10, _vm->getDisplayWidth() - 10 - actor->_screenPosition.x);
+ dist = clamp(60, dist, 150);
+
+ _activeSpeech.speechBox.left = actor->_screenPosition.x - dist;
+ _activeSpeech.speechBox.right = actor->_screenPosition.x + dist;
+
+ if (_activeSpeech.speechBox.left < 10) {
+ _activeSpeech.speechBox.right += 10 - _activeSpeech.speechBox.left;
+ _activeSpeech.speechBox.left = 10;
+ }
+ if (_activeSpeech.speechBox.right > _vm->getDisplayWidth() - 10) {
+ _activeSpeech.speechBox.left -= _activeSpeech.speechBox.right - _vm->getDisplayWidth() - 10;
+ _activeSpeech.speechBox.right = _vm->getDisplayWidth() - 10;
+ }
+}
+
+void Actor::nonActorSpeech(const Common::Rect &box, const char **strings, int stringsCount, int sampleResourceId, int speechFlags) {
+ int i;
+
+ _vm->_script->wakeUpThreads(kWaitTypeSpeech);
+
+ for (i = 0; i < stringsCount; i++) {
+ _activeSpeech.strings[i] = strings[i];
+ }
+ _activeSpeech.stringsCount = stringsCount;
+ _activeSpeech.speechFlags = speechFlags;
+ _activeSpeech.actorsCount = 1;
+ _activeSpeech.actorIds[0] = 0;
+ if (!(_vm->getFeatures() & GF_CD_FX))
+ _activeSpeech.sampleResourceId = -1;
+ else
+ _activeSpeech.sampleResourceId = sampleResourceId;
+ _activeSpeech.playing = false;
+ _activeSpeech.slowModeCharIndex = 0;
+ _activeSpeech.speechBox = box;
+}
+
+void Actor::simulSpeech(const char *string, uint16 *actorIds, int actorIdsCount, int speechFlags, int sampleResourceId) {
+ int i;
+
+ for (i = 0; i < actorIdsCount; i++) {
+ ActorData *actor;
+
+ actor = getActor(actorIds[i]);
+ _activeSpeech.actorIds[i] = actorIds[i];
+ _activeSpeech.speechColor[i] = actor->_speechColor;
+ _activeSpeech.outlineColor[i] = 0; // disable outline
+ }
+ _activeSpeech.actorsCount = actorIdsCount;
+ _activeSpeech.strings[0] = string;
+ _activeSpeech.stringsCount = 1;
+ _activeSpeech.speechFlags = speechFlags;
+ _activeSpeech.sampleResourceId = sampleResourceId;
+ _activeSpeech.playing = false;
+ _activeSpeech.slowModeCharIndex = 0;
+
+ // caller should call thread->wait(kWaitTypeSpeech) by itself
+}
+
+void Actor::abortAllSpeeches() {
+ abortSpeech();
+
+ if (_vm->_script->_abortEnabled)
+ _vm->_script->_skipSpeeches = true;
+
+ for (int i = 0; i < 10; i++)
+ _vm->_script->executeThreads(0);
+}
+
+void Actor::abortSpeech() {
+ _vm->_sound->stopVoice();
+ _activeSpeech.playingTime = 0;
+}
+
+void Actor::moveDragon(ActorData *actor) {
+ int16 dir0, dir1, dir2, dir3;
+ int16 moveType;
+ Event event;
+ const DragonMove *dragonMove;
+
+ if ((actor->_actionCycle < 0) ||
+ ((actor->_actionCycle == 0) && (actor->_dragonMoveType >= ACTOR_DRAGON_TURN_MOVES))) {
+
+ moveType = kDragonMoveInvalid;
+ if (actor->_location.distance(_protagonist->_location) < 24) {
+ if (_dragonHunt && (_protagonist->_currentAction != kActionFall)) {
+ event.type = kEvTOneshot;
+ event.code = kScriptEvent;
+ event.op = kEventExecNonBlocking;
+ event.time = 0;
+ event.param = _vm->_scene->getScriptModuleNumber(); // module number
+ event.param2 = ACTOR_EXP_KNOCK_RIF; // script entry point number
+ event.param3 = -1; // Action
+ event.param4 = -1; // Object
+ event.param5 = -1; // With Object
+ event.param6 = -1; // Actor
+
+ _vm->_events->queue(&event);
+ _dragonHunt = false;
+ }
+ } else {
+ _dragonHunt = true;
+ }
+
+ if (actor->_walkStepIndex + 2 > actor->_walkStepsCount) {
+
+ _vm->_isoMap->findDragonTilePath(actor, actor->_location, _protagonist->_location, actor->_actionDirection);
+
+ if (actor->_walkStepsCount == 0) {
+ _vm->_isoMap->findDragonTilePath(actor, actor->_location, _protagonist->_location, 0);
+ }
+
+ if (actor->_walkStepsCount < 2) {
+ return;
+ }
+
+ actor->_partialTarget = actor->_location;
+ actor->_finalTarget = _protagonist->_location;
+ actor->_walkStepIndex = 0;
+ }
+
+ dir0 = actor->_actionDirection;
+ dir1 = actor->_tileDirections[actor->_walkStepIndex++];
+ dir2 = actor->_tileDirections[actor->_walkStepIndex];
+ dir3 = actor->_tileDirections[actor->_walkStepIndex + 1];
+
+ if (dir0 != dir1){
+ actor->_actionDirection = dir0 = dir1;
+ }
+
+ actor->_location = actor->_partialTarget;
+
+ if ((dir1 != dir2) && (dir1 == dir3)) {
+ switch (dir1) {
+ case kDirUpLeft:
+ actor->_partialTarget.v() += 16;
+ moveType = kDragonMoveUpLeft;
+ break;
+ case kDirDownLeft:
+ actor->_partialTarget.u() -= 16;
+ moveType = kDragonMoveDownLeft;
+ break;
+ case kDirDownRight:
+ actor->_partialTarget.v() -= 16;
+ moveType = kDragonMoveDownRight;
+ break;
+ case kDirUpRight:
+ actor->_partialTarget.u() += 16;
+ moveType = kDragonMoveUpRight;
+ break;
+ }
+
+ switch (dir2) {
+ case kDirUpLeft:
+ actor->_partialTarget.v() += 16;
+ break;
+ case kDirDownLeft:
+ actor->_partialTarget.u() -= 16;
+ break;
+ case kDirDownRight:
+ actor->_partialTarget.v() -= 16;
+ break;
+ case kDirUpRight:
+ actor->_partialTarget.u() += 16;
+ break;
+ }
+
+ actor->_walkStepIndex++;
+ } else {
+ switch (dir1) {
+ case kDirUpLeft:
+ actor->_partialTarget.v() += 16;
+ switch (dir2) {
+ case kDirDownLeft:
+ moveType = kDragonMoveUpLeft_Left;
+ actor->_partialTarget.u() -= 16;
+ break;
+ case kDirUpLeft:
+ moveType = kDragonMoveUpLeft;
+ break;
+ case kDirUpRight:
+ actor->_partialTarget.u() += 16;
+ moveType = kDragonMoveUpLeft_Right;
+ break;
+ default:
+ actor->_actionDirection = dir1;
+ actor->_walkStepsCount = 0;
+ break;
+ }
+ break;
+ case kDirDownLeft:
+ actor->_partialTarget.u() -= 16;
+ switch (dir2) {
+ case kDirDownRight:
+ moveType = kDragonMoveDownLeft_Left;
+ actor->_partialTarget.v() -= 16;
+ break;
+ case kDirDownLeft:
+ moveType = kDragonMoveDownLeft;
+ break;
+ case kDirUpLeft:
+ moveType = kDragonMoveDownLeft_Right;
+ actor->_partialTarget.v() += 16;
+ break;
+ default:
+ actor->_actionDirection = dir1;
+ actor->_walkStepsCount = 0;
+ break;
+ }
+ break;
+ case kDirDownRight:
+ actor->_partialTarget.v() -= 16;
+ switch (dir2) {
+ case kDirUpRight:
+ moveType = kDragonMoveDownRight_Left;
+ actor->_partialTarget.u() += 16;
+ break;
+ case kDirDownRight:
+ moveType = kDragonMoveDownRight;
+ break;
+ case kDirDownLeft:
+ moveType = kDragonMoveDownRight_Right;
+ actor->_partialTarget.u() -= 16;
+ break;
+ default:
+ actor->_actionDirection = dir1;
+ actor->_walkStepsCount = 0;
+ break;
+ }
+ break;
+ case kDirUpRight:
+ actor->_partialTarget.u() += 16;
+ switch (dir2) {
+ case kDirUpLeft:
+ moveType = kDragonMoveUpRight_Left;
+ actor->_partialTarget.v() += 16;
+ break;
+ case kDirUpRight:
+ moveType = kDragonMoveUpRight;
+ break;
+ case kDirDownRight:
+ moveType = kDragonMoveUpRight_Right;
+ actor->_partialTarget.v() -= 16;
+ break;
+ default:
+ actor->_actionDirection = dir1;
+ actor->_walkStepsCount = 0;
+ break;
+ }
+ break;
+
+ default:
+ actor->_actionDirection = dir1;
+ actor->_walkStepsCount = 0;
+ break;
+ }
+ }
+
+ actor->_dragonMoveType = moveType;
+
+ if (moveType >= ACTOR_DRAGON_TURN_MOVES) {
+ actor->_dragonStepCycle = 0;
+ actor->_actionCycle = 4;
+ actor->_walkStepIndex++;
+ } else {
+ actor->_actionCycle = 4;
+ }
+ }
+
+ actor->_actionCycle--;
+
+ if ((actor->_walkStepsCount < 1) || (actor->_actionCycle < 0)) {
+ return;
+ }
+
+ if (actor->_dragonMoveType < ACTOR_DRAGON_TURN_MOVES) {
+
+ actor->_dragonStepCycle++;
+ if (actor->_dragonStepCycle >= 7) {
+ actor->_dragonStepCycle = 0;
+ }
+
+ actor->_dragonBaseFrame = actor->_dragonMoveType * 7;
+
+ if (actor->_location.u() > actor->_partialTarget.u() + 3) {
+ actor->_location.u() -= 4;
+ } else if (actor->_location.u() < actor->_partialTarget.u() - 3) {
+ actor->_location.u() += 4;
+ } else {
+ actor->_location.u() = actor->_partialTarget.u();
+ }
+
+ if (actor->_location.v() > actor->_partialTarget.v() + 3) {
+ actor->_location.v() -= 4;
+ } else if (actor->_location.v() < actor->_partialTarget.v() - 3) {
+ actor->_location.v() += 4;
+ } else {
+ actor->_location.v() = actor->_partialTarget.v();
+ }
+ } else {
+ dragonMove = &dragonMoveTable[actor->_dragonMoveType];
+ actor->_dragonBaseFrame = dragonMove->baseFrame;
+
+
+ actor->_location.u() = actor->_partialTarget.u() - dragonMove->offset[actor->_actionCycle][0];
+ actor->_location.v() = actor->_partialTarget.v() - dragonMove->offset[actor->_actionCycle][1];
+
+ actor->_dragonStepCycle++;
+ if (actor->_dragonStepCycle >= 3) {
+ actor->_dragonStepCycle = 3;
+ }
+ }
+
+ actor->_frameNumber = actor->_dragonBaseFrame + actor->_dragonStepCycle;
+}
+
+void Actor::findActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint) {
+ Point iteratorPoint;
+ Point bestPoint;
+ int maskType;
+ int i;
+ Rect intersect;
+
+#ifdef ACTOR_DEBUG
+ _debugPointsCount = 0;
+#endif
+
+ actor->_walkStepsCount = 0;
+ if (fromPoint == toPoint) {
+ actor->addWalkStepPoint(toPoint);
+ return;
+ }
+
+ for (iteratorPoint.y = 0; iteratorPoint.y < _yCellCount; iteratorPoint.y++) {
+ for (iteratorPoint.x = 0; iteratorPoint.x < _xCellCount; iteratorPoint.x++) {
+ if (_vm->_scene->validBGMaskPoint(iteratorPoint)) {
+ maskType = _vm->_scene->getBGMaskType(iteratorPoint);
+ setPathCell(iteratorPoint, _vm->_scene->getDoorState(maskType) ? kPathCellBarrier : kPathCellEmpty);
+ } else {
+ setPathCell(iteratorPoint, kPathCellBarrier);
+ }
+ }
+ }
+
+ for (i = 0; i < _barrierCount; i++) {
+ intersect.left = MAX(_pathRect.left, _barrierList[i].left);
+ intersect.top = MAX(_pathRect.top, _barrierList[i].top);
+ intersect.right = MIN(_pathRect.right, _barrierList[i].right);
+ intersect.bottom = MIN(_pathRect.bottom, _barrierList[i].bottom);
+
+ for (iteratorPoint.y = intersect.top; iteratorPoint.y < intersect.bottom; iteratorPoint.y++) {
+ for (iteratorPoint.x = intersect.left; iteratorPoint.x < intersect.right; iteratorPoint.x++) {
+ setPathCell(iteratorPoint, kPathCellBarrier);
+ }
+ }
+ }
+
+#ifdef ACTOR_DEBUG
+ for (iteratorPoint.y = 0; iteratorPoint.y < _yCellCount; iteratorPoint.y++) {
+ for (iteratorPoint.x = 0; iteratorPoint.x < _xCellCount; iteratorPoint.x++) {
+ if (getPathCell(iteratorPoint) == kPathCellBarrier) {
+ addDebugPoint(iteratorPoint, 24);
+ }
+ }
+ }
+#endif
+
+ if (scanPathLine(fromPoint, toPoint)) {
+ actor->addWalkStepPoint(fromPoint);
+ actor->addWalkStepPoint(toPoint);
+ return;
+ }
+
+ i = fillPathArray(fromPoint, toPoint, bestPoint);
+
+ if (fromPoint == bestPoint) {
+ actor->addWalkStepPoint(bestPoint);
+ return;
+ }
+
+ if (i == 0) {
+ error("fillPathArray returns zero");
+ }
+
+ setActorPath(actor, fromPoint, bestPoint);
+}
+
+bool Actor::scanPathLine(const Point &point1, const Point &point2) {
+ Point point;
+ Point delta;
+ bool interchange = false;
+ Point fDelta;
+ int errterm;
+ int s1;
+ int s2;
+ int i;
+
+ point = point1;
+ delta.x = ABS(point1.x - point2.x);
+ delta.y = ABS(point1.y - point2.y);
+ s1 = integerCompare(point2.x, point1.x);
+ s2 = integerCompare(point2.y, point1.y);
+
+ if (delta.y > delta.x) {
+ SWAP(delta.y, delta.x);
+ interchange = true;
+ }
+
+ fDelta.x = delta.x * 2;
+ fDelta.y = delta.y * 2;
+
+ errterm = fDelta.y - delta.x;
+
+ for (i = 0; i < delta.x; i++) {
+ while (errterm >= 0) {
+ if (interchange) {
+ point.x += s1;
+ } else {
+ point.y += s2;
+ }
+ errterm -= fDelta.x;
+ }
+
+ if (interchange)
+ point.y += s2;
+ else
+ point.x += s1;
+
+ errterm += fDelta.y;
+
+ if (!validPathCellPoint(point)) {
+ return false;
+ }
+ if (getPathCell(point) == kPathCellBarrier) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int Actor::fillPathArray(const Point &fromPoint, const Point &toPoint, Point &bestPoint) {
+ int bestRating;
+ int currentRating;
+ int i;
+ Point bestPath;
+ int pointCounter;
+ int startDirection;
+ PathDirectionData *pathDirection;
+ PathDirectionData *newPathDirection;
+ const PathDirectionData *samplePathDirection;
+ Point nextPoint;
+ int directionCount;
+
+ _pathDirectionListCount = 0;
+ pointCounter = 0;
+ bestRating = quickDistance(fromPoint, toPoint);
+ bestPath = fromPoint;
+
+ for (startDirection = 0; startDirection < 4; startDirection++) {
+ newPathDirection = addPathDirectionListData();
+ newPathDirection->x = fromPoint.x;
+ newPathDirection->y = fromPoint.y;
+ newPathDirection->direction = startDirection;
+ }
+
+ if (validPathCellPoint(fromPoint)) {
+ setPathCell(fromPoint, kDirUp);
+
+#ifdef ACTOR_DEBUG
+ addDebugPoint(fromPoint, 24+36);
+#endif
+ }
+
+ i = 0;
+
+ do {
+ pathDirection = &_pathDirectionList[i];
+ for (directionCount = 0; directionCount < 3; directionCount++) {
+ samplePathDirection = &pathDirectionLUT[pathDirection->direction][directionCount];
+ nextPoint.x = samplePathDirection->x + pathDirection->x;
+ nextPoint.y = samplePathDirection->y + pathDirection->y;
+
+ if (!validPathCellPoint(nextPoint)) {
+ continue;
+ }
+
+ if (getPathCell(nextPoint) != kPathCellEmpty) {
+ continue;
+ }
+
+ setPathCell(nextPoint, samplePathDirection->direction);
+
+#ifdef ACTOR_DEBUG
+ addDebugPoint(nextPoint, samplePathDirection->direction + 96);
+#endif
+ newPathDirection = addPathDirectionListData();
+ newPathDirection->x = nextPoint.x;
+ newPathDirection->y = nextPoint.y;
+ newPathDirection->direction = samplePathDirection->direction;
+ ++pointCounter;
+ if (nextPoint == toPoint) {
+ bestPoint = toPoint;
+ return pointCounter;
+ }
+ currentRating = quickDistance(nextPoint, toPoint);
+ if (currentRating < bestRating) {
+ bestRating = currentRating;
+ bestPath = nextPoint;
+ }
+ pathDirection = &_pathDirectionList[i];
+ }
+ ++i;
+ } while (i < _pathDirectionListCount);
+
+ bestPoint = bestPath;
+ return pointCounter;
+}
+
+void Actor::setActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint) {
+ Point nextPoint;
+ int8 direction;
+ int i;
+
+ _pathListIndex = -1;
+ addPathListPoint(toPoint);
+ nextPoint = toPoint;
+
+ while (!(nextPoint == fromPoint)) {
+ direction = getPathCell(nextPoint);
+ if ((direction < 0) || (direction >= 8)) {
+ error("Actor::setActorPath error direction 0x%X", direction);
+ }
+ nextPoint.x -= pathDirectionLUT2[direction][0];
+ nextPoint.y -= pathDirectionLUT2[direction][1];
+ addPathListPoint(nextPoint);
+
+#ifdef ACTOR_DEBUG
+ addDebugPoint(nextPoint, 0x8a);
+#endif
+ }
+
+ pathToNode();
+ removeNodes();
+ nodeToPath();
+ removePathPoints();
+
+ for (i = 0; i <= _pathNodeListIndex; i++) {
+ actor->addWalkStepPoint(_pathNodeList[i].point);
+ }
+}
+
+void Actor::pathToNode() {
+ Point point1, point2, delta;
+ int direction;
+ int i;
+ Point *point;
+
+ point= &_pathList[_pathListIndex];
+ direction = 0;
+
+ _pathNodeListIndex = -1;
+ addPathNodeListPoint(*point);
+
+ for (i = _pathListIndex; i > 0; i--) {
+ point1 = *point;
+ --point;
+ point2 = *point;
+ if (direction == 0) {
+ delta.x = integerCompare(point2.x, point1.x);
+ delta.y = integerCompare(point2.y, point1.y);
+ direction++;
+ }
+ if ((point1.x + delta.x != point2.x) || (point1.y + delta.y != point2.y)) {
+ addPathNodeListPoint(point1);
+ direction--;
+ i++;
+ point++;
+ }
+ }
+ addPathNodeListPoint(*_pathList);
+}
+
+int pathLine(Point *pointList, const Point &point1, const Point &point2) {
+ Point point;
+ Point delta;
+ Point tempPoint;
+ int s1;
+ int s2;
+ bool interchange = false;
+ int errterm;
+ int i;
+
+ delta.x = abs(point2.x - point1.x);
+ delta.y = abs(point2.y - point1.y);
+ point = point1;
+ s1 = integerCompare(point2.x, point1.x);
+ s2 = integerCompare(point2.y, point1.y);
+
+ if (delta.y > delta.x) {
+ SWAP(delta.y, delta.x);
+ interchange = true;
+ }
+
+ tempPoint.x = delta.x * 2;
+ tempPoint.y = delta.y * 2;
+
+ errterm = tempPoint.y - delta.x;
+
+ for (i = 0; i < delta.x; i++) {
+ while (errterm >= 0) {
+ if (interchange) {
+ point.x += s1;
+ } else {
+ point.y += s2;
+ }
+ errterm -= tempPoint.x;
+ }
+ if (interchange) {
+ point.y += s2;
+ } else {
+ point.x += s1;
+ }
+ errterm += tempPoint.y;
+
+ pointList[i] = point;
+ }
+ return delta.x;
+}
+
+void Actor::nodeToPath() {
+ int i;
+ Point point1, point2;
+ PathNode *node;
+ Point *point;
+
+ for (i = 0, point = _pathList; i < _pathListAlloced; i++, point++) {
+ point->x = point->y = PATH_NODE_EMPTY;
+ }
+
+ _pathListIndex = 1;
+ _pathList[0] = _pathNodeList[0].point;
+ _pathNodeList[0].link = 0;
+ for (i = 0, node = _pathNodeList; i < _pathNodeListIndex; i++) {
+ point1 = node->point;
+ node++;
+ point2 = node->point;
+ _pathListIndex += pathLine(&_pathList[_pathListIndex], point1, point2);
+ node->link = _pathListIndex - 1;
+ }
+ _pathListIndex--;
+ _pathNodeList[_pathNodeListIndex].link = _pathListIndex;
+
+}
+
+void Actor::removeNodes() {
+ int i, j, k;
+ PathNode *iNode, *jNode, *kNode, *fNode;
+ fNode = &_pathNodeList[_pathNodeListIndex];
+
+ if (scanPathLine(_pathNodeList[0].point, fNode->point)) {
+ _pathNodeList[1] = *fNode;
+ _pathNodeListIndex = 1;
+ }
+
+ if (_pathNodeListIndex < 4) {
+ return;
+ }
+
+ for (i = _pathNodeListIndex - 1, iNode = fNode-1; i > 1 ; i--, iNode--) {
+ if (iNode->point.x == PATH_NODE_EMPTY) {
+ continue;
+ }
+
+ if (scanPathLine(_pathNodeList[0].point, iNode->point)) {
+ for (j = 1, jNode = _pathNodeList + 1; j < i; j++, jNode++) {
+ jNode->point.x = PATH_NODE_EMPTY;
+ }
+ }
+ }
+
+ for (i = 1, iNode = _pathNodeList + 1; i < _pathNodeListIndex - 1; i++, iNode++) {
+ if (iNode->point.x == PATH_NODE_EMPTY) {
+ continue;
+ }
+
+ if (scanPathLine(fNode->point, iNode->point)) {
+ for (j = i + 1, jNode = iNode + 1; j < _pathNodeListIndex; j++, jNode++) {
+ jNode->point.x = PATH_NODE_EMPTY;
+ }
+ }
+ }
+ condenseNodeList();
+
+ for (i = 1, iNode = _pathNodeList + 1; i < _pathNodeListIndex - 1; i++, iNode++) {
+ if (iNode->point.x == PATH_NODE_EMPTY) {
+ continue;
+ }
+ for (j = i + 2, jNode = iNode + 2; j < _pathNodeListIndex; j++, jNode++) {
+ if (jNode->point.x == PATH_NODE_EMPTY) {
+ continue;
+ }
+
+ if (scanPathLine(iNode->point, jNode->point)) {
+ for (k = i + 1,kNode = iNode + 1; k < j; k++, kNode++) {
+ kNode->point.x = PATH_NODE_EMPTY;
+ }
+ }
+ }
+ }
+ condenseNodeList();
+}
+
+void Actor::condenseNodeList() {
+ int i, j, count;
+ PathNode *iNode, *jNode;
+
+ count = _pathNodeListIndex;
+
+ for (i = 1, iNode = _pathNodeList + 1; i < _pathNodeListIndex; i++, iNode++) {
+ if (iNode->point.x == PATH_NODE_EMPTY) {
+ j = i + 1;
+ jNode = iNode + 1;
+ while (jNode->point.x == PATH_NODE_EMPTY) {
+ j++;
+ jNode++;
+ }
+ *iNode = *jNode;
+ count = i;
+ jNode->point.x = PATH_NODE_EMPTY;
+ if (j == _pathNodeListIndex) {
+ break;
+ }
+ }
+ }
+ _pathNodeListIndex = count;
+}
+
+void Actor::removePathPoints() {
+ int i, j, k, l;
+ PathNode *node;
+ int start;
+ int end;
+ Point point1, point2;
+
+ if (_pathNodeListIndex < 2)
+ return;
+
+ _newPathNodeListIndex = -1;
+ addNewPathNodeListPoint(_pathNodeList[0]);
+
+ for (i = 1, node = _pathNodeList + 1; i < _pathNodeListIndex; i++, node++) {
+ addNewPathNodeListPoint(*node);
+
+ for (j = 5; j > 0; j--) {
+ start = node->link - j;
+ end = node->link + j;
+
+ if (start < 0 || end > _pathListIndex) {
+ continue;
+ }
+
+ point1 = _pathList[start];
+ point2 = _pathList[end];
+ if ((point1.x == PATH_NODE_EMPTY) || (point2.x == PATH_NODE_EMPTY)) {
+ continue;
+ }
+
+ if (scanPathLine(point1, point2)) {
+ for (l = 1; l <= _newPathNodeListIndex; l++) {
+ if (start <= _newPathNodeList[l].link) {
+ _newPathNodeListIndex = l;
+ _newPathNodeList[_newPathNodeListIndex].point = point1;
+ _newPathNodeList[_newPathNodeListIndex].link = start;
+ incrementNewPathNodeListIndex();
+ break;
+ }
+ }
+ _newPathNodeList[_newPathNodeListIndex].point = point2;
+ _newPathNodeList[_newPathNodeListIndex].link = end;
+
+ for (k = start + 1; k < end; k++) {
+ _pathList[k].x = PATH_NODE_EMPTY;
+ }
+ break;
+ }
+ }
+ }
+
+ addNewPathNodeListPoint(_pathNodeList[_pathNodeListIndex]);
+
+ for (i = 0, j = 0; i <= _newPathNodeListIndex; i++) {
+ if (_newPathNodeListIndex == i || (_newPathNodeList[i].point != _newPathNodeList[i+1].point)) {
+ _pathNodeList[j++] = _newPathNodeList[i];
+ }
+ }
+ _pathNodeListIndex = j - 1;
+}
+
+void Actor::drawPathTest() {
+#ifdef ACTOR_DEBUG
+ int i;
+ Surface *surface;
+ surface = _vm->_gfx->getBackBuffer();
+ if (_debugPoints == NULL) {
+ return;
+ }
+
+ for (i = 0; i < _debugPointsCount; i++) {
+ *((byte *)surface->pixels + (_debugPoints[i].point.y * surface->pitch) + _debugPoints[i].point.x) = _debugPoints[i].color;
+ }
+#endif
+}
+
+void Actor::saveState(Common::OutSaveFile *out) {
+ uint16 i;
+
+ out->writeSint16LE(getProtagState());
+
+ for (i = 0; i < _actorsCount; i++) {
+ ActorData *a = _actors[i];
+ a->saveState(out);
+ }
+
+ for (i = 0; i < _objsCount; i++) {
+ ObjectData *o = _objs[i];
+ o->saveState(out);
+ }
+}
+
+void Actor::loadState(Common::InSaveFile *in) {
+ int32 i;
+
+ setProtagState(in->readSint16LE());
+
+ for (i = 0; i < _actorsCount; i++) {
+ ActorData *a = _actors[i];
+ a->loadState(_vm->getCurrentLoadVersion(), in);
+
+ // Fix bug #1258633 "ITE: Second Rif appears in wall of dog castle prison"
+ // For some reason in some cases actor position is all wrong, so Rif
+ // crawls to his original poition
+ if (i == 122 && _vm->getGameType() == GType_ITE) {
+ a->_location.x = 130;
+ a->_location.y = 55;
+ }
+ }
+
+ for (i = 0; i < _objsCount; i++) {
+ ObjectData *o = _objs[i];
+ o->loadState(in);
+ }
+}
+
+// Console wrappers - must be safe to run
+
+void Actor::cmdActorWalkTo(int argc, const char **argv) {
+ uint16 actorId = (uint16) atoi(argv[1]);
+ Location location;
+ Point movePoint;
+
+ movePoint.x = atoi(argv[2]);
+ movePoint.y = atoi(argv[3]);
+
+ location.fromScreenPoint(movePoint);
+
+ if (!validActorId(actorId)) {
+ _vm->_console->DebugPrintf("Actor::cmActorWalkTo Invalid actorId 0x%X.\n", actorId);
+ return;
+ }
+
+ actorWalkTo(actorId, location);
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/actor.h b/engines/saga/actor.h
new file mode 100644
index 0000000000..74f1abd689
--- /dev/null
+++ b/engines/saga/actor.h
@@ -0,0 +1,778 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Actor management module header file
+
+#ifndef SAGA_ACTOR_H__
+#define SAGA_ACTOR_H__
+
+#include "common/savefile.h"
+
+#include "saga/sprite.h"
+#include "saga/itedata.h"
+#include "saga/list.h"
+#include "saga/saga.h"
+#include "saga/font.h"
+
+namespace Saga {
+
+class HitZone;
+
+
+//#define ACTOR_DEBUG //only for actor pathfinding debug!
+
+#define ACTOR_BARRIERS_MAX 16
+
+#define ACTOR_MAX_STEPS_COUNT 32
+
+#define ACTOR_DIALOGUE_HEIGHT 100
+
+#define ACTOR_LMULT 4
+
+#define ACTOR_CLIMB_SPEED 8
+
+#define ACTOR_COLLISION_WIDTH 32
+#define ACTOR_COLLISION_HEIGHT 8
+
+#define ACTOR_DIRECTIONS_COUNT 4 // for ActorFrameSequence
+#define ACTOR_DIRECTION_RIGHT 0
+#define ACTOR_DIRECTION_LEFT 1
+#define ACTOR_DIRECTION_BACK 2
+#define ACTOR_DIRECTION_FORWARD 3
+
+#define ACTOR_SPEECH_STRING_MAX 16 // speech const
+#define ACTOR_SPEECH_ACTORS_MAX 8
+
+#define ACTOR_DRAGON_TURN_MOVES 4
+#define ACTOR_DRAGON_INDEX 133
+
+#define ACTOR_NO_ENTRANCE -1
+
+#define ACTOR_EXP_KNOCK_RIF 24
+
+#define PATH_NODE_EMPTY -1
+
+#define ACTOR_INHM_SIZE 228
+
+enum ActorActions {
+ kActionWait = 0,
+ kActionWalkToPoint = 1,
+ kActionWalkToLink = 2,
+ kActionWalkDir = 3,
+ kActionSpeak = 4,
+ kActionAccept = 5,
+ kActionStoop = 6,
+ kActionLook = 7,
+ kActionCycleFrames = 8,
+ kActionPongFrames = 9,
+ kActionFreeze = 10,
+ kActionFall = 11,
+ kActionClimb = 12
+};
+
+enum SpeechFlags {
+ kSpeakNoAnimate = 1,
+ kSpeakAsync = 2,
+ kSpeakSlow = 4
+};
+
+enum ActorFrameTypes {
+ kFrameStand,
+ kFrameWalk,
+ kFrameSpeak,
+ kFrameGive,
+ kFrameGesture,
+ kFrameWait,
+ kFramePickUp,
+ kFrameLook
+};
+
+enum ActorFlagsEx {
+ kActorNoCollide = (1 << 0),
+ kActorNoFollow = (1 << 1),
+ kActorCollided = (1 << 2),
+ kActorBackwards = (1 << 3),
+ kActorContinuous = (1 << 4),
+ kActorFinalFace = (1 << 5),
+ kActorFinishLeft = ((1 << 5) | (kDirLeft << 6)),
+ kActorFinishRight = ((1 << 5) | (kDirRight << 6)),
+ kActorFinishUp = ((1 << 5) | (kDirUp << 6)),
+ kActorFinishDown = ((1 << 5) | (kDirDown << 6)),
+ kActorFacingMask = (0xf << 5),
+ kActorRandom = (1 << 10)
+};
+
+enum PathCellType {
+ kPathCellEmpty = -1,
+ //kDirUp = 0 .... kDirUpLeft = 7
+ kPathCellBarrier = 0x57
+};
+
+enum DragonMoveTypes {
+ kDragonMoveUpLeft = 0,
+ kDragonMoveUpRight = 1,
+ kDragonMoveDownLeft = 2,
+ kDragonMoveDownRight = 3,
+ kDragonMoveUpLeft_Left = 4,
+ kDragonMoveUpLeft_Right = 5,
+ kDragonMoveUpRight_Left = 6,
+ kDragonMoveUpRight_Right = 7,
+ kDragonMoveDownLeft_Left = 8,
+ kDragonMoveDownLeft_Right = 9,
+ kDragonMoveDownRight_Left = 10,
+ kDragonMoveDownRight_Right = 11,
+ kDragonMoveInvalid = 12
+};
+
+struct PathDirectionData {
+ int8 direction;
+ int16 x;
+ int16 y;
+};
+
+struct ActorFrameRange {
+ int frameIndex;
+ int frameCount;
+};
+
+struct ActorFrameSequence {
+ ActorFrameRange directions[ACTOR_DIRECTIONS_COUNT];
+};
+
+int pathLine(Point *pointList, const Point &point1, const Point &point2);
+
+struct Location {
+ int32 x; // logical coordinates
+ int32 y; //
+ int32 z; //
+ Location() {
+ x = y = z = 0;
+ }
+ void saveState(Common::OutSaveFile *out) {
+ out->writeSint32LE(x);
+ out->writeSint32LE(y);
+ out->writeSint32LE(z);
+ }
+ void loadState(Common::InSaveFile *in) {
+ x = in->readSint32LE();
+ y = in->readSint32LE();
+ z = in->readSint32LE();
+ }
+
+ int distance(const Location &location) const {
+ return MAX(ABS(x - location.x), ABS(y - location.y));
+ }
+ int32 &u() {
+ return x;
+ }
+ int32 &v() {
+ return y;
+ }
+ int32 u() const {
+ return x;
+ }
+ int32 v() const {
+ return y;
+ }
+ int32 uv() const {
+ return u() + v();
+ }
+ void delta(const Location &location, Location &result) const {
+ result.x = x - location.x;
+ result.y = y - location.y;
+ result.z = z - location.z;
+ }
+ void addXY(const Location &location) {
+ x += location.x;
+ y += location.y;
+ }
+ void add(const Location &location) {
+ x += location.x;
+ y += location.y;
+ z += location.z;
+ }
+ void fromScreenPoint(const Point &screenPoint) {
+ x = (screenPoint.x * ACTOR_LMULT);
+ y = (screenPoint.y * ACTOR_LMULT);
+ z = 0;
+ }
+ void toScreenPointXY(Point &screenPoint) const {
+ screenPoint.x = x / ACTOR_LMULT;
+ screenPoint.y = y / ACTOR_LMULT;
+ }
+ void toScreenPointUV(Point &screenPoint) const {
+ screenPoint.x = u();
+ screenPoint.y = v();
+ }
+ void toScreenPointXYZ(Point &screenPoint) const {
+ screenPoint.x = x / ACTOR_LMULT;
+ screenPoint.y = y / ACTOR_LMULT - z;
+ }
+ void fromStream(Common::MemoryReadStream &stream) {
+ x = stream.readUint16LE();
+ y = stream.readUint16LE();
+ z = stream.readUint16LE();
+ }
+
+ void debugPrint(int debuglevel = 0, const char *loc = "Loc:") const {
+ debug(debuglevel, "%s %d, %d, %d", loc, x, y, z);
+ }
+};
+
+class CommonObjectData {
+public:
+//constant
+ bool _disabled; // disabled in init section
+ int32 _index; // index in local array
+ uint16 _id; // object id
+ int32 _scriptEntrypointNumber; // script entrypoint number
+
+//variables
+ uint16 _flags; // initial flags
+ int32 _nameIndex; // index in name string list
+ int32 _sceneNumber; // scene
+ int32 _spriteListResourceId; // sprite list resource id
+
+ Location _location; // logical coordinates
+ Point _screenPosition; // screen coordinates
+ int32 _screenDepth; //
+ int32 _screenScale; //
+
+ void saveState(Common::OutSaveFile *out) {
+ out->writeUint16LE(_flags);
+ out->writeSint32LE(_nameIndex);
+ out->writeSint32LE(_sceneNumber);
+ out->writeSint32LE(_spriteListResourceId);
+ _location.saveState(out);
+ out->writeSint16LE(_screenPosition.x);
+ out->writeSint16LE(_screenPosition.y);
+ out->writeSint32LE(_screenDepth);
+ out->writeSint32LE(_screenScale);
+ }
+ void loadState(Common::InSaveFile *in) {
+ _flags = in->readUint16LE();
+ _nameIndex = in->readSint32LE();
+ _sceneNumber = in->readSint32LE();
+ _spriteListResourceId = in->readSint32LE();
+ _location.loadState(in);
+ _screenPosition.x = in->readSint16LE();
+ _screenPosition.y = in->readSint16LE();
+ _screenDepth = in->readSint32LE();
+ _screenScale = in->readSint32LE();
+ }
+};
+
+typedef CommonObjectData *CommonObjectDataPointer;
+
+typedef SortedList<CommonObjectDataPointer> CommonObjectOrderList;
+
+class ObjectData: public CommonObjectData {
+public:
+ //constant
+ uint16 _interactBits;
+ ObjectData() {
+ memset(this, 0, sizeof(*this));
+ }
+};
+
+class ActorData: public CommonObjectData {
+public:
+ //constant
+ SpriteList _spriteList; // sprite list data
+
+ ActorFrameSequence *_frames; // Actor's frames
+ int _framesCount; // Actor's frames count
+ int _frameListResourceId; // Actor's frame list resource id
+
+ byte _speechColor; // Actor dialogue color
+ //
+ bool _inScene;
+
+ //variables
+ uint16 _actorFlags; // dynamic flags
+ int32 _currentAction; // ActorActions type
+ int32 _facingDirection; // orientation
+ int32 _actionDirection;
+ int32 _actionCycle;
+ uint16 _targetObject;
+ const HitZone *_lastZone;
+
+ int32 _cycleFrameSequence;
+ uint8 _cycleDelay;
+ uint8 _cycleTimeCount;
+ uint8 _cycleFlags;
+
+ int16 _fallVelocity;
+ int16 _fallAcceleration;
+ int16 _fallPosition;
+
+ uint8 _dragonBaseFrame;
+ uint8 _dragonStepCycle;
+ uint8 _dragonMoveType;
+
+ int32 _frameNumber; // current frame number
+
+ int32 _tileDirectionsAlloced;
+ byte *_tileDirections;
+
+ int32 _walkStepsAlloced;
+ Point *_walkStepsPoints;
+
+ int32 _walkStepsCount;
+ int32 _walkStepIndex;
+
+ Location _finalTarget;
+ Location _partialTarget;
+ int32 _walkFrameSequence;
+
+public:
+ void saveState(Common::OutSaveFile *out) {
+ int i = 0;
+ CommonObjectData::saveState(out);
+ out->writeUint16LE(_actorFlags);
+ out->writeSint32LE(_currentAction);
+ out->writeSint32LE(_facingDirection);
+ out->writeSint32LE(_actionDirection);
+ out->writeSint32LE(_actionCycle);
+ out->writeUint16LE(_targetObject);
+
+ out->writeSint32LE(_cycleFrameSequence);
+ out->writeByte(_cycleDelay);
+ out->writeByte(_cycleTimeCount);
+ out->writeByte(_cycleFlags);
+ out->writeSint16LE(_fallVelocity);
+ out->writeSint16LE(_fallAcceleration);
+ out->writeSint16LE(_fallPosition);
+ out->writeByte(_dragonBaseFrame);
+ out->writeByte(_dragonStepCycle);
+ out->writeByte(_dragonMoveType);
+ out->writeSint32LE(_frameNumber);
+
+ out->writeSint32LE(_tileDirectionsAlloced);
+ for (i = 0; i < _tileDirectionsAlloced; i++) {
+ out->writeByte(_tileDirections[i]);
+ }
+
+ out->writeSint32LE(_walkStepsAlloced);
+ for (i = 0; i < _walkStepsAlloced; i++) {
+ out->writeSint16LE(_walkStepsPoints[i].x);
+ out->writeSint16LE(_walkStepsPoints[i].y);
+ }
+
+ out->writeSint32LE(_walkStepsCount);
+ out->writeSint32LE(_walkStepIndex);
+ _finalTarget.saveState(out);
+ _partialTarget.saveState(out);
+ out->writeSint32LE(_walkFrameSequence);
+ }
+
+ void loadState(uint32 version, Common::InSaveFile *in) {
+ int i = 0;
+ CommonObjectData::loadState(in);
+ _actorFlags = in->readUint16LE();
+ _currentAction = in->readSint32LE();
+ _facingDirection = in->readSint32LE();
+ _actionDirection = in->readSint32LE();
+ _actionCycle = in->readSint32LE();
+ _targetObject = in->readUint16LE();
+
+ _lastZone = NULL;
+ _cycleFrameSequence = in->readSint32LE();
+ _cycleDelay = in->readByte();
+ _cycleTimeCount = in->readByte();
+ _cycleFlags = in->readByte();
+ if (version > 1) {
+ _fallVelocity = in->readSint16LE();
+ _fallAcceleration = in->readSint16LE();
+ _fallPosition = in->readSint16LE();
+ } else {
+ _fallVelocity = _fallAcceleration = _fallPosition = 0;
+ }
+ if (version > 2) {
+ _dragonBaseFrame = in->readByte();
+ _dragonStepCycle = in->readByte();
+ _dragonMoveType = in->readByte();
+ } else {
+ _dragonBaseFrame = _dragonStepCycle = _dragonMoveType = 0;
+ }
+
+ _frameNumber = in->readSint32LE();
+
+
+ setTileDirectionsSize(in->readSint32LE(), true);
+ for (i = 0; i < _tileDirectionsAlloced; i++) {
+ _tileDirections[i] = in->readByte();
+ }
+
+ setWalkStepsPointsSize(in->readSint32LE(), true);
+ for (i = 0; i < _walkStepsAlloced; i++) {
+ _walkStepsPoints[i].x = in->readSint16LE();
+ _walkStepsPoints[i].y = in->readSint16LE();
+ }
+
+ _walkStepsCount = in->readSint32LE();
+ _walkStepIndex = in->readSint32LE();
+ _finalTarget.loadState(in);
+ _partialTarget.loadState(in);
+ _walkFrameSequence = in->readSint32LE();
+ }
+
+ void setTileDirectionsSize(int size, bool forceRealloc) {
+ if ((size <= _tileDirectionsAlloced) && !forceRealloc) {
+ return;
+ }
+ _tileDirectionsAlloced = size;
+ _tileDirections = (byte*)realloc(_tileDirections, _tileDirectionsAlloced * sizeof(*_tileDirections));
+ }
+
+ void cycleWrap(int cycleLimit) {
+ if (_actionCycle >= cycleLimit)
+ _actionCycle = 0;
+ }
+
+ void setWalkStepsPointsSize(int size, bool forceRealloc) {
+ if ((size <= _walkStepsAlloced) && !forceRealloc) {
+ return;
+ }
+ _walkStepsAlloced = size;
+ _walkStepsPoints = (Point*)realloc(_walkStepsPoints, _walkStepsAlloced * sizeof(*_walkStepsPoints));
+ }
+
+ void addWalkStepPoint(const Point &point) {
+ setWalkStepsPointsSize(_walkStepsCount + 1, false);
+ _walkStepsPoints[_walkStepsCount++] = point;
+ }
+
+ void freeSpriteList() {
+ _spriteList.freeMem();
+ }
+
+ ActorData() {
+ memset(this, 0, sizeof(*this));
+ }
+ ~ActorData() {
+ free(_frames);
+ free(_tileDirections);
+ free(_walkStepsPoints);
+ freeSpriteList();
+ }
+};
+
+
+
+struct SpeechData {
+ int speechColor[ACTOR_SPEECH_ACTORS_MAX];
+ int outlineColor[ACTOR_SPEECH_ACTORS_MAX];
+ int speechFlags;
+ const char *strings[ACTOR_SPEECH_STRING_MAX];
+ Rect speechBox;
+ Rect drawRect;
+ int stringsCount;
+ int slowModeCharIndex;
+ uint16 actorIds[ACTOR_SPEECH_ACTORS_MAX];
+ int actorsCount;
+ int sampleResourceId;
+ bool playing;
+ int playingTime;
+
+ SpeechData() {
+ memset(this, 0, sizeof(*this));
+ }
+
+ FontEffectFlags getFontFlags(int i) {
+ if (outlineColor[i] != 0) {
+ return kFontOutline;
+ } else {
+ return kFontNormal;
+ }
+ }
+};
+
+
+
+class Actor {
+ friend class IsoMap;
+ friend class SagaEngine;
+ friend class Puzzle;
+public:
+
+ Actor(SagaEngine *vm);
+ ~Actor();
+
+ void cmdActorWalkTo(int argc, const char **argv);
+
+ bool validActorId(uint16 id) { return (id == ID_PROTAG) || ((id >= objectIndexToId(kGameObjectActor, 0)) && (id < objectIndexToId(kGameObjectActor, _actorsCount))); }
+ int actorIdToIndex(uint16 id) { return (id == ID_PROTAG ) ? 0 : objectIdToIndex(id); }
+ uint16 actorIndexToId(int index) { return (index == 0 ) ? ID_PROTAG : objectIndexToId(kGameObjectActor, index); }
+ ActorData *getActor(uint16 actorId);
+
+// clarification: Obj - means game object, such Hat, Spoon etc, Object - means Actor,Obj,HitZone,StepZone
+
+ bool validObjId(uint16 id) { return (id >= objectIndexToId(kGameObjectObject, 0)) && (id < objectIndexToId(kGameObjectObject, _objsCount)); }
+ int objIdToIndex(uint16 id) { return objectIdToIndex(id); }
+ uint16 objIndexToId(int index) { return objectIndexToId(kGameObjectObject, index); }
+ ObjectData *getObj(uint16 objId);
+
+ int getObjectScriptEntrypointNumber(uint16 id) {
+ int objectType;
+ objectType = objectTypeId(id);
+ if (!(objectType & (kGameObjectObject | kGameObjectActor))) {
+ error("Actor::getObjectScriptEntrypointNumber wrong id 0x%X", id);
+ }
+ return (objectType == kGameObjectObject) ? getObj(id)->_scriptEntrypointNumber : getActor(id)->_scriptEntrypointNumber;
+ }
+ int getObjectFlags(uint16 id) {
+ int objectType;
+ objectType = objectTypeId(id);
+ if (!(objectType & (kGameObjectObject | kGameObjectActor))) {
+ error("Actor::getObjectFlags wrong id 0x%X", id);
+ }
+ return (objectType == kGameObjectObject) ? getObj(id)->_flags : getActor(id)->_flags;
+ }
+
+ void direct(int msec);
+ void drawActors();
+ void updateActorsScene(int actorsEntrance); // calls from scene loading to update Actors info
+
+ void drawSpeech();
+
+ void drawPathTest();
+
+ uint16 hitTest(const Point &testPoint, bool skipProtagonist);
+ void takeExit(uint16 actorId, const HitZone *hitZone);
+ bool actorEndWalk(uint16 actorId, bool recurse);
+ bool actorWalkTo(uint16 actorId, const Location &toLocation);
+ int getFrameType(ActorFrameTypes frameType);
+ ActorFrameRange *getActorFrameRange(uint16 actorId, int frameType);
+ void actorFaceTowardsPoint(uint16 actorId, const Location &toLocation);
+ void actorFaceTowardsObject(uint16 actorId, uint16 objectId);
+
+ void realLocation(Location &location, uint16 objectId, uint16 walkFlags);
+
+// speech
+ void actorSpeech(uint16 actorId, const char **strings, int stringsCount, int sampleResourceId, int speechFlags);
+ void nonActorSpeech(const Common::Rect &box, const char **strings, int stringsCount, int sampleResourceId, int speechFlags);
+ void simulSpeech(const char *string, uint16 *actorIds, int actorIdsCount, int speechFlags, int sampleResourceId);
+ void setSpeechColor(int speechColor, int outlineColor) {
+ _activeSpeech.speechColor[0] = speechColor;
+ _activeSpeech.outlineColor[0] = outlineColor;
+ }
+ void abortAllSpeeches();
+ void abortSpeech();
+ bool isSpeaking() {
+ return _activeSpeech.stringsCount > 0;
+ }
+
+ void saveState(Common::OutSaveFile *out);
+ void loadState(Common::InSaveFile *in);
+
+ void setProtagState(int state);
+ int getProtagState() { return _protagState; }
+
+ void freeActorList();
+ void loadActorList(int protagonistIdx, int actorCount, int actorsResourceID,
+ int protagStatesCount, int protagStatesResourceID);
+ void freeObjList();
+ void loadObjList(int objectCount, int objectsResourceID);
+
+ /*
+ uint16 _currentFrameIndex;
+ void frameTest() {
+ _currentFrameIndex++;
+ }*/
+protected:
+ friend class Script;
+ bool loadActorResources(ActorData *actor);
+
+private:
+ void stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit, bool stopped);
+ void loadActorSpriteList(ActorData *actor);
+
+ void createDrawOrderList();
+ bool calcScreenPosition(CommonObjectData *commonObjectData);
+ bool getSpriteParams(CommonObjectData *commonObjectData, int &frameNumber, SpriteList *&spriteList);
+
+ bool followProtagonist(ActorData *actor);
+ void findActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint);
+ void handleSpeech(int msec);
+ void handleActions(int msec, bool setup);
+ bool validPathCellPoint(const Point &testPoint) {
+ return !((testPoint.x < 0) || (testPoint.x >= _xCellCount) ||
+ (testPoint.y < 0) || (testPoint.y >= _yCellCount));
+ }
+ void setPathCell(const Point &testPoint, int8 value) {
+ if (!validPathCellPoint(testPoint)) {
+ error("Actor::setPathCell wrong point");
+ }
+ _pathCell[testPoint.x + testPoint.y * _xCellCount] = value;
+ }
+ int8 getPathCell(const Point &testPoint) {
+ if (!validPathCellPoint(testPoint)) {
+ error("Actor::getPathCell wrong point");
+ }
+ return _pathCell[testPoint.x + testPoint.y * _xCellCount];
+ }
+ bool scanPathLine(const Point &point1, const Point &point2);
+ int fillPathArray(const Point &fromPoint, const Point &toPoint, Point &bestPoint);
+ void setActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint);
+ void pathToNode();
+ void condenseNodeList();
+ void removeNodes();
+ void nodeToPath();
+ void removePathPoints();
+ bool validFollowerLocation(const Location &location);
+ void moveDragon(ActorData *actor);
+
+
+protected:
+//constants
+ int _actorsCount;
+ ActorData **_actors;
+
+ int _objsCount;
+ ObjectData **_objs;
+
+ SagaEngine *_vm;
+ ResourceContext *_actorContext;
+
+ int _lastTickMsec;
+ CommonObjectOrderList _drawOrderList;
+
+//variables
+public:
+ ActorData *_centerActor;
+ ActorData *_protagonist;
+ int _handleActionDiv;
+
+ Rect _speechBoxScript;
+
+ StringsTable _objectsStrings;
+ StringsTable _actorsStrings;
+
+protected:
+ SpeechData _activeSpeech;
+ int _protagState;
+ bool _dragonHunt;
+
+private:
+ ActorFrameSequence *_protagStates;
+ int _protagStatesCount;
+
+//path stuff
+ struct PathNode {
+ Point point;
+ int link;
+ };
+
+ Rect _barrierList[ACTOR_BARRIERS_MAX];
+ int _barrierCount;
+ int8 *_pathCell;
+
+ int _xCellCount;
+ int _yCellCount;
+ Rect _pathRect;
+
+ PathDirectionData *_pathDirectionList;
+ int _pathDirectionListCount;
+ int _pathDirectionListAlloced;
+ PathDirectionData * addPathDirectionListData() {
+ if (_pathDirectionListCount + 1 >= _pathDirectionListAlloced) {
+ _pathDirectionListAlloced += 100;
+ _pathDirectionList = (PathDirectionData*) realloc(_pathDirectionList, _pathDirectionListAlloced * sizeof(*_pathDirectionList));
+ }
+ return &_pathDirectionList[_pathDirectionListCount++];
+ }
+
+ Point *_pathList;
+ int _pathListIndex;
+ int _pathListAlloced;
+ void addPathListPoint(const Point &point) {
+ ++_pathListIndex;
+ if (_pathListIndex >= _pathListAlloced) {
+ _pathListAlloced += 100;
+ _pathList = (Point*) realloc(_pathList, _pathListAlloced * sizeof(*_pathList));
+
+ }
+ _pathList[_pathListIndex] = point;
+ }
+
+ int _pathNodeListIndex;
+ int _pathNodeListAlloced;
+ PathNode *_pathNodeList;
+ void addPathNodeListPoint(const Point &point) {
+ ++_pathNodeListIndex;
+ if (_pathNodeListIndex >= _pathNodeListAlloced) {
+ _pathNodeListAlloced += 100;
+ _pathNodeList = (PathNode*) realloc(_pathNodeList, _pathNodeListAlloced * sizeof(*_pathNodeList));
+
+ }
+ _pathNodeList[_pathNodeListIndex].point = point;
+ }
+
+ int _newPathNodeListIndex;
+ int _newPathNodeListAlloced;
+ PathNode *_newPathNodeList;
+ void incrementNewPathNodeListIndex() {
+ ++_newPathNodeListIndex;
+ if (_newPathNodeListIndex >= _newPathNodeListAlloced) {
+ _newPathNodeListAlloced += 100;
+ _newPathNodeList = (PathNode*) realloc(_newPathNodeList, _newPathNodeListAlloced * sizeof(*_newPathNodeList));
+
+ }
+ }
+ void addNewPathNodeListPoint(const PathNode &pathNode) {
+ incrementNewPathNodeListIndex();
+ _newPathNodeList[_newPathNodeListIndex] = pathNode;
+ }
+
+public:
+#ifdef ACTOR_DEBUG
+//path debug - use with care
+ struct DebugPoint {
+ Point point;
+ byte color;
+ };
+ DebugPoint *_debugPoints;
+ int _debugPointsCount;
+ int _debugPointsAlloced;
+ void addDebugPoint(const Point &point, byte color) {
+ if (_debugPointsCount + 1 > _debugPointsAlloced) {
+ _debugPointsAlloced += 1000;
+ _debugPoints = (DebugPoint*) realloc(_debugPoints, _debugPointsAlloced * sizeof(*_debugPoints));
+ }
+ _debugPoints[_debugPointsCount].color = color;
+ _debugPoints[_debugPointsCount++].point = point;
+ }
+#endif
+};
+
+inline int16 quickDistance(const Point &point1, const Point &point2) {
+ Point delta;
+ delta.x = ABS(point1.x - point2.x) / 2;
+ delta.y = ABS(point1.y - point2.y);
+ return ((delta.x < delta.y) ? (delta.y + delta.x / 2) : (delta.x + delta.y / 2));
+}
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/animation.cpp b/engines/saga/animation.cpp
new file mode 100644
index 0000000000..016070dc50
--- /dev/null
+++ b/engines/saga/animation.cpp
@@ -0,0 +1,720 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Background animation management module
+#include "saga/saga.h"
+#include "saga/gfx.h"
+
+#include "saga/console.h"
+#include "saga/events.h"
+#include "saga/interface.h"
+#include "saga/render.h"
+#include "saga/rscfile.h"
+#include "saga/scene.h"
+
+#include "saga/animation.h"
+
+namespace Saga {
+
+Anim::Anim(SagaEngine *vm) : _vm(vm) {
+ uint16 i;
+
+ _cutawayList = NULL;
+ _cutawayListLength = 0;
+ _cutawayActive = false;
+
+ for (i = 0; i < MAX_ANIMATIONS; i++)
+ _animations[i] = NULL;
+
+ for (i = 0; i < ARRAYSIZE(_cutawayAnimations); i++)
+ _cutawayAnimations[i] = NULL;
+}
+
+Anim::~Anim(void) {
+ reset();
+}
+
+void Anim::loadCutawayList(const byte *resourcePointer, size_t resourceLength) {
+ free(_cutawayList);
+ _cutawayListLength = resourceLength / 8;
+ _cutawayList = (Cutaway *)malloc(_cutawayListLength * sizeof(Cutaway));
+
+ MemoryReadStream cutawayS(resourcePointer, resourceLength);
+
+ for (int i = 0; i < _cutawayListLength; i++) {
+ _cutawayList[i].backgroundResourceId = cutawayS.readUint16LE();
+ _cutawayList[i].animResourceId = cutawayS.readUint16LE();
+ _cutawayList[i].cycles = cutawayS.readSint16LE();
+ _cutawayList[i].frameRate = cutawayS.readSint16LE();
+ }
+}
+
+void Anim::freeCutawayList(void) {
+ free(_cutawayList);
+ _cutawayList = NULL;
+ _cutawayListLength = 0;
+}
+
+void Anim::playCutaway(int cut, bool fade) {
+ debug(0, "playCutaway(%d, %d)", cut, fade);
+
+ if (fade) {
+ // TODO: Fade down. Is this blocking or non-blocking?
+ }
+
+ if (!_cutawayActive) {
+ _vm->_gfx->showCursor(false);
+ _vm->_interface->setStatusText("");
+ _vm->_interface->setSaveReminderState(0);
+ _vm->_interface->rememberMode();
+ _vm->_interface->setMode(kPanelCutaway);
+ _cutawayActive = true;
+ }
+
+ // Set the initial background and palette for the cutaway
+
+ ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
+
+ byte *resourceData;
+ size_t resourceDataLength;
+
+ _vm->_resource->loadResource(context, _cutawayList[cut].backgroundResourceId, resourceData, resourceDataLength);
+
+ byte *buf;
+ size_t buflen;
+ int width;
+ int height;
+
+ _vm->decodeBGImage(resourceData, resourceDataLength, &buf, &buflen, &width, &height);
+
+ const PalEntry *palette = (const PalEntry *)_vm->getImagePal(resourceData, resourceDataLength);
+
+ Surface *bgSurface = _vm->_render->getBackGroundSurface();
+ const Rect rect(width, height);
+
+ bgSurface->blit(rect, buf);
+ _vm->_gfx->setPalette(palette);
+
+ free(buf);
+ free(resourceData);
+
+ // Play the animation
+
+ int cutawaySlot = -1;
+
+ for (int i = 0; i < ARRAYSIZE(_cutawayAnimations); i++) {
+ if (!_cutawayAnimations[i]) {
+ cutawaySlot = i;
+ } else if (_cutawayAnimations[i]->state == ANIM_PAUSE) {
+ delete _cutawayAnimations[i];
+ _cutawayAnimations[i] = NULL;
+ cutawaySlot = i;
+ } else if (_cutawayAnimations[i]->state == ANIM_PLAYING) {
+ _cutawayAnimations[i]->state = ANIM_PAUSE;
+ }
+ }
+
+ if (cutawaySlot == -1) {
+ warning("Could not allocate cutaway animation slot");
+ return;
+ }
+
+ _vm->_resource->loadResource(context, _cutawayList[cut].animResourceId, resourceData, resourceDataLength);
+
+ load(MAX_ANIMATIONS + cutawaySlot, resourceData, resourceDataLength);
+
+ free(resourceData);
+
+ setCycles(MAX_ANIMATIONS + cutawaySlot, _cutawayList[cut].cycles);
+ setFrameTime(MAX_ANIMATIONS + cutawaySlot, 1000 / _cutawayList[cut].frameRate);
+ play(MAX_ANIMATIONS + cutawaySlot, 0);
+}
+
+void Anim::endCutaway(void) {
+ // I believe this is called by scripts after running one cutaway. At
+ // this time, nothing needs to be done here.
+
+ debug(0, "endCutaway()");
+}
+
+void Anim::returnFromCutaway(void) {
+ // I believe this is called by scripts after running a cutaway to
+ // ensure that we return to the scene as if nothing had happened. It's
+ // not called by the IHNM intro, presumably because there is no old
+ // scene to return to.
+
+ debug(0, "returnFromCutaway()");
+
+ if (_cutawayActive) {
+ // Note that clearCutaway() sets _cutawayActive to false.
+ clearCutaway();
+
+ // TODO: Handle fade up, if we previously faded down
+
+ // TODO: Restore the scene
+
+ // TODO: Restore the animations
+
+ for (int i = 0; i < MAX_ANIMATIONS; i++) {
+ if (_animations[i] && _animations[i]->state == ANIM_PLAYING) {
+ resume(i, 0);
+ }
+ }
+ }
+}
+
+void Anim::clearCutaway(void) {
+ debug(0, "clearCutaway()");
+
+ if (_cutawayActive) {
+ _cutawayActive = false;
+
+ for (int i = 0; i < ARRAYSIZE(_cutawayAnimations); i++) {
+ delete _cutawayAnimations[i];
+ _cutawayAnimations[i] = NULL;
+ }
+
+ _vm->_interface->restoreMode();
+ _vm->_gfx->showCursor(true);
+ }
+}
+
+void Anim::load(uint16 animId, const byte *animResourceData, size_t animResourceLength) {
+ AnimationData *anim;
+ uint16 temp;
+
+ if (animId >= MAX_ANIMATIONS) {
+ if (animId >= MAX_ANIMATIONS + ARRAYSIZE(_cutawayAnimations))
+ error("Anim::load could not find unused animation slot");
+ anim = _cutawayAnimations[animId - MAX_ANIMATIONS] = new AnimationData(animResourceData, animResourceLength);
+ } else
+ anim = _animations[animId] = new AnimationData(animResourceData, animResourceLength);
+
+ MemoryReadStreamEndian headerReadS(anim->resourceData, anim->resourceLength, _vm->isBigEndian());
+ anim->magic = headerReadS.readUint16LE(); // cause ALWAYS LE
+ anim->screenWidth = headerReadS.readUint16();
+ anim->screenHeight = headerReadS.readUint16();
+
+ anim->unknown06 = headerReadS.readByte();
+ anim->unknown07 = headerReadS.readByte();
+ anim->maxFrame = headerReadS.readByte() - 1;
+ anim->loopFrame = headerReadS.readByte() - 1;
+ temp = headerReadS.readUint16BE();
+ anim->start = headerReadS.pos();
+ if (temp == (uint16)(-1)) {
+ temp = 0;
+ }
+ anim->start += temp;
+
+ // Cache frame offsets
+ anim->frameOffsets = (size_t *)malloc((anim->maxFrame + 1) * sizeof(*anim->frameOffsets));
+ if (anim->frameOffsets == NULL) {
+ memoryError("Anim::load");
+ }
+
+ fillFrameOffsets(anim);
+
+ /* char s[200];
+ sprintf(s, "d:\\anim%i",animId);
+ long flen=anim->resourceLength;
+ char *buf=(char*)anim->resourceData;
+ FILE*f;
+ f=fopen(s,"wb");
+ for(long i=0;i<flen;i++)
+ fputc(buf[i],f);
+ fclose(f);*/
+
+ // Set animation data
+ anim->currentFrame = 0;
+ anim->completed = 0;
+ anim->cycles = anim->maxFrame;
+
+ anim->frameTime = DEFAULT_FRAME_TIME;
+ anim->flags = ANIM_FLAG_NONE;
+ anim->linkId = -1;
+ anim->state = ANIM_PAUSE;
+}
+
+void Anim::link(int16 animId1, int16 animId2) {
+ AnimationData *anim1;
+ AnimationData *anim2;
+
+ anim1 = getAnimation(animId1);
+
+ anim1->linkId = animId2;
+
+ if (animId2 == -1) {
+ return;
+ }
+
+ anim2 = getAnimation(animId2);
+ anim2->frameTime = anim1->frameTime;
+}
+
+void Anim::setCycles(uint16 animId, int cycles) {
+ AnimationData *anim;
+
+ anim = getAnimation(animId);
+
+ anim->cycles = cycles;
+}
+
+void Anim::play(uint16 animId, int vectorTime, bool playing) {
+ Event event;
+ Surface *backGroundSurface;
+
+ byte *displayBuffer;
+
+ uint16 frame;
+ int frameTime;
+
+ AnimationData *anim;
+ AnimationData *linkAnim;
+
+ if (animId > MAX_ANIMATIONS && !_cutawayActive)
+ return;
+
+ if (animId < MAX_ANIMATIONS && _cutawayActive)
+ return;
+
+ anim = getAnimation(animId);
+
+ backGroundSurface = _vm->_render->getBackGroundSurface();
+ displayBuffer = (byte*)backGroundSurface->pixels;
+
+ if (playing) {
+ anim->state = ANIM_PLAYING;
+ }
+
+ if (anim->state == ANIM_PAUSE) {
+ return;
+ }
+
+ if (anim->completed < anim->cycles) {
+ frame = anim->currentFrame;
+ // FIXME: if start > 0, then this works incorrectly
+ decodeFrame(anim, anim->frameOffsets[frame], displayBuffer, _vm->getDisplayWidth() * _vm->getDisplayHeight());
+
+ anim->currentFrame++;
+ if (anim->completed != 65535) {
+ anim->completed++;
+ }
+
+ if (anim->currentFrame > anim->maxFrame) {
+ anim->currentFrame = anim->loopFrame;
+
+ if (anim->state == ANIM_STOPPING || anim->currentFrame == -1) {
+ anim->state = ANIM_PAUSE;
+ }
+ }
+ } else {
+ // Animation done playing
+ anim->state = ANIM_PAUSE;
+ if (anim->linkId == -1) {
+ if (anim->flags & ANIM_FLAG_ENDSCENE) {
+ // This animation ends the scene
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = anim->frameTime + vectorTime;
+ _vm->_events->queue(&event);
+ }
+ return;
+ } else {
+ anim->currentFrame = 0;
+ anim->completed = 0;
+ }
+ }
+
+ if (anim->state == ANIM_PAUSE && anim->linkId != -1) {
+ // If this animation has a link, follow it
+ linkAnim = getAnimation(anim->linkId);
+
+ debug(5, "Animation ended going to %d", anim->linkId);
+ linkAnim->state = ANIM_PLAYING;
+ animId = anim->linkId;
+ frameTime = 0;
+ } else {
+ frameTime = anim->frameTime + vectorTime;
+ }
+
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventFrame;
+ event.param = animId;
+ event.time = frameTime;
+
+ _vm->_events->queue(&event);
+}
+
+void Anim::stop(uint16 animId) {
+ AnimationData *anim;
+
+ anim = getAnimation(animId);
+
+ anim->state = ANIM_PAUSE;
+}
+
+void Anim::finish(uint16 animId) {
+ AnimationData *anim;
+
+ anim = getAnimation(animId);
+
+ anim->state = ANIM_STOPPING;
+}
+
+void Anim::resume(uint16 animId, int cycles) {
+ AnimationData *anim;
+
+ anim = getAnimation(animId);
+
+ anim->cycles += cycles;
+ play(animId, 0, true);
+}
+
+void Anim::reset() {
+ uint16 i;
+
+ for (i = 0; i < MAX_ANIMATIONS; i++) {
+ if (_animations[i] != NULL) {
+ delete _animations[i];
+ _animations[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < ARRAYSIZE(_cutawayAnimations); i++) {
+ if (_cutawayAnimations[i] != NULL) {
+ delete _cutawayAnimations[i];
+ _cutawayAnimations[i] = NULL;
+ }
+ }
+}
+
+void Anim::setFlag(uint16 animId, uint16 flag) {
+ AnimationData *anim;
+
+ anim = getAnimation(animId);
+
+ anim->flags |= flag;
+}
+
+void Anim::clearFlag(uint16 animId, uint16 flag) {
+ AnimationData *anim;
+
+ anim = getAnimation(animId);
+
+ anim->flags &= ~flag;
+}
+
+void Anim::setFrameTime(uint16 animId, int time) {
+ AnimationData *anim;
+
+ anim = getAnimation(animId);
+
+ anim->frameTime = time;
+}
+
+int16 Anim::getCurrentFrame(uint16 animId) {
+ AnimationData *anim;
+
+ anim = getAnimation(animId);
+
+ return anim->currentFrame;
+}
+
+void Anim::decodeFrame(AnimationData *anim, size_t frameOffset, byte *buf, size_t bufLength) {
+ byte *writePointer = NULL;
+
+ uint16 xStart = 0;
+ uint16 yStart = 0;
+ uint32 screenWidth;
+ uint32 screenHeight;
+
+ int markByte;
+ byte dataByte;
+ int newRow;
+
+ uint16 controlChar;
+ uint16 paramChar;
+
+ uint16 runcount;
+ int xVector;
+
+ uint16 i;
+ bool longData = isLongData();
+
+ screenWidth = anim->screenWidth;
+ screenHeight = anim->screenHeight;
+
+ if ((screenWidth * screenHeight) > bufLength) {
+ // Buffer argument is too small to hold decoded frame, abort.
+ error("decodeFrame() Buffer size inadequate");
+ }
+
+ MemoryReadStream readS(anim->resourceData + frameOffset, anim->resourceLength - frameOffset);
+
+
+#if 1
+#define VALIDATE_WRITE_POINTER \
+ if ((writePointer < buf) || (writePointer >= (buf + screenWidth * screenHeight))) { \
+ error("VALIDATE_WRITE_POINTER: writePointer=%x buf=%x", writePointer, buf); \
+ }
+#else
+#define VALIDATE_WRITE_POINTER
+#endif
+
+
+ // Begin RLE decompression to output buffer
+ do {
+ markByte = readS.readByte();
+ switch (markByte) {
+ case SAGA_FRAME_START:
+ xStart = readS.readUint16BE();
+ if (longData)
+ yStart = readS.readUint16BE();
+ else
+ yStart = readS.readByte();
+ readS.readByte(); /* Skip pad byte */
+ /*xPos = */readS.readUint16BE();
+ /*yPos = */readS.readUint16BE();
+ /*width = */readS.readUint16BE();
+ /*height = */readS.readUint16BE();
+
+ // Setup write pointer to the draw origin
+ writePointer = (buf + (yStart * screenWidth) + xStart);
+ VALIDATE_WRITE_POINTER;
+ continue;
+ break;
+ case SAGA_FRAME_NOOP: // Does nothing
+ readS.readByte();
+ readS.readByte();
+ readS.readByte();
+ continue;
+ break;
+ case SAGA_FRAME_LONG_UNCOMPRESSED_RUN: // Long Unencoded Run
+ runcount = readS.readSint16BE();
+ for (i = 0; i < runcount; i++) {
+ dataByte = readS.readByte();
+ if (dataByte != 0) {
+ *writePointer = dataByte;
+ }
+ writePointer++;
+ VALIDATE_WRITE_POINTER;
+ }
+ continue;
+ break;
+ case SAGA_FRAME_LONG_COMPRESSED_RUN: // Long encoded run
+ runcount = readS.readSint16BE();
+ dataByte = readS.readByte();
+ for (i = 0; i < runcount; i++) {
+ *writePointer++ = dataByte;
+ VALIDATE_WRITE_POINTER;
+ }
+ continue;
+ break;
+ case SAGA_FRAME_ROW_END: // End of row
+ xVector = readS.readSint16BE();
+
+ if (longData)
+ newRow = readS.readSint16BE();
+ else
+ newRow = readS.readByte();
+
+ // Set write pointer to the new draw origin
+ writePointer = buf + ((yStart + newRow) * screenWidth) + xStart + xVector;
+ VALIDATE_WRITE_POINTER;
+ continue;
+ break;
+ case SAGA_FRAME_REPOSITION: // Reposition command
+ xVector = readS.readSint16BE();
+ writePointer += xVector;
+ VALIDATE_WRITE_POINTER;
+ continue;
+ break;
+ case SAGA_FRAME_END: // End of frame marker
+ return;
+ break;
+ default:
+ break;
+ }
+
+ // Mask all but two high order control bits
+ controlChar = markByte & 0xC0U;
+ paramChar = markByte & 0x3FU;
+ switch (controlChar) {
+ case SAGA_FRAME_EMPTY_RUN: // 1100 0000
+ // Run of empty pixels
+ runcount = paramChar + 1;
+ writePointer += runcount;
+ VALIDATE_WRITE_POINTER;
+ continue;
+ break;
+ case SAGA_FRAME_COMPRESSED_RUN: // 1000 0000
+ // Run of compressed data
+ runcount = paramChar + 1;
+ dataByte = readS.readByte();
+ for (i = 0; i < runcount; i++) {
+ *writePointer++ = dataByte;
+ VALIDATE_WRITE_POINTER;
+ }
+ continue;
+ break;
+ case SAGA_FRAME_UNCOMPRESSED_RUN: // 0100 0000
+ // Uncompressed run
+ runcount = paramChar + 1;
+ for (i = 0; i < runcount; i++) {
+ dataByte = readS.readByte();
+ if (dataByte != 0) {
+ *writePointer = dataByte;
+ }
+ writePointer++;
+ VALIDATE_WRITE_POINTER;
+ }
+ continue;
+ break;
+ default:
+ // Unknown marker found - abort
+ error("decodeFrame() Invalid RLE marker encountered");
+ break;
+ }
+ } while (1);
+}
+
+void Anim::fillFrameOffsets(AnimationData *anim) {
+ uint16 currentFrame;
+ byte markByte;
+ uint16 control;
+ uint16 runcount;
+ int i;
+ bool longData = isLongData();
+
+ MemoryReadStreamEndian readS(anim->resourceData, anim->resourceLength, _vm->isBigEndian());
+
+ readS.seek(12);
+
+ readS._bigEndian = !_vm->isBigEndian(); // RLE has inversion BE<>LE
+
+ for (currentFrame = 0; currentFrame <= anim->maxFrame; currentFrame++) {
+ anim->frameOffsets[currentFrame] = readS.pos();
+
+ // For some strange reason, the animation header is in little
+ // endian format, but the actual RLE encoded frame data,
+ // including the frame header, is in big endian format. */
+ do {
+ markByte = readS.readByte();
+// debug(7, "_pos=%x currentFrame=%i markByte=%x", readS.pos(), currentFrame, markByte);
+
+ switch (markByte) {
+ case SAGA_FRAME_START: // Start of frame
+ // skip header
+ if (longData) {
+ readS.seek(13, SEEK_CUR);
+ } else {
+ readS.seek(12, SEEK_CUR);
+ }
+ continue;
+ break;
+
+ case SAGA_FRAME_END: // End of frame marker
+ continue;
+ break;
+ case SAGA_FRAME_REPOSITION: // Reposition command
+ readS.readSint16BE();
+ continue;
+ break;
+ case SAGA_FRAME_ROW_END: // End of row marker
+ readS.readSint16BE();
+ if (longData)
+ readS.readSint16BE();
+ else
+ readS.readByte();
+ continue;
+ break;
+ case SAGA_FRAME_LONG_COMPRESSED_RUN: // Long compressed run marker
+ readS.readSint16BE();
+ readS.readByte();
+ continue;
+ break;
+ case SAGA_FRAME_LONG_UNCOMPRESSED_RUN: // (16) 0001 0000
+ // Long Uncompressed Run
+ runcount = readS.readSint16BE();
+ for (i = 0; i < runcount; i++)
+ readS.readByte();
+ continue;
+ break;
+ case SAGA_FRAME_NOOP: // Does nothing
+ readS.readByte();
+ readS.readByte();
+ readS.readByte();
+ continue;
+ break;
+ default:
+ break;
+ }
+
+ // Mask all but two high order (control) bits
+ control = markByte & 0xC0;
+ switch (control) {
+ case SAGA_FRAME_EMPTY_RUN:
+ // Run of empty pixels
+ continue;
+ break;
+ case SAGA_FRAME_COMPRESSED_RUN:
+ // Run of compressed data
+ readS.readByte(); // Skip data byte
+ continue;
+ break;
+ case SAGA_FRAME_UNCOMPRESSED_RUN:
+ // Uncompressed run
+ runcount = (markByte & 0x3f) + 1;
+ for (i = 0; i < runcount; i++)
+ readS.readByte();
+ continue;
+ break;
+ default:
+ error("Encountered unknown RLE marker %i", markByte);
+ break;
+ }
+ } while (markByte != SAGA_FRAME_END);
+ }
+}
+
+void Anim::animInfo() {
+ uint16 animCount;
+ uint16 i;
+
+ animCount = getAnimationCount();
+
+ _vm->_console->DebugPrintf("There are %d animations loaded:\n", animCount);
+
+ for (i = 0; i < MAX_ANIMATIONS; i++) {
+ if (_animations[i] == NULL) {
+ continue;
+ }
+
+ _vm->_console->DebugPrintf("%02d: Frames: %u Flags: %u\n", i, _animations[i]->maxFrame, _animations[i]->flags);
+ }
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/animation.h b/engines/saga/animation.h
new file mode 100644
index 0000000000..191166732a
--- /dev/null
+++ b/engines/saga/animation.h
@@ -0,0 +1,198 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Background animation management module private header
+
+#ifndef SAGA_ANIMATION_H_
+#define SAGA_ANIMATION_H_
+
+#include "saga/stream.h"
+
+namespace Saga {
+
+#define MAX_ANIMATIONS 10
+#define DEFAULT_FRAME_TIME 140
+
+#define SAGA_FRAME_START 0xF
+#define SAGA_FRAME_END 0x3F
+#define SAGA_FRAME_NOOP 0x1F
+#define SAGA_FRAME_REPOSITION 0x30
+#define SAGA_FRAME_ROW_END 0x2F
+#define SAGA_FRAME_LONG_COMPRESSED_RUN 0x20
+#define SAGA_FRAME_LONG_UNCOMPRESSED_RUN 0x10
+#define SAGA_FRAME_COMPRESSED_RUN 0x80
+#define SAGA_FRAME_UNCOMPRESSED_RUN 0x40
+#define SAGA_FRAME_EMPTY_RUN 0xC0
+
+enum AnimationState {
+ ANIM_PLAYING = 0x01,
+ ANIM_PAUSE = 0x02,
+ ANIM_STOPPING = 0x03
+};
+
+enum AnimationFlags {
+ ANIM_FLAG_NONE = 0x00,
+ ANIM_FLAG_ENDSCENE = 0x01 // When animation ends, dispatch scene end event
+};
+
+// Cutaway info array member. Cutaways are basically animations with a really
+// bad attitude.
+struct Cutaway {
+ uint16 backgroundResourceId;
+ uint16 animResourceId;
+ int16 cycles;
+ int16 frameRate;
+};
+
+// Animation info array member
+struct AnimationData {
+ byte *resourceData;
+ size_t resourceLength;
+
+ uint16 magic;
+
+ uint16 screenWidth;
+ uint16 screenHeight;
+
+ byte unknown06;
+ byte unknown07;
+
+ int16 maxFrame;
+ int16 loopFrame;
+
+ int16 start;
+
+ int16 currentFrame;
+ size_t *frameOffsets;
+
+ uint16 completed;
+ uint16 cycles;
+
+ int frameTime;
+
+ AnimationState state;
+ int16 linkId;
+ uint16 flags;
+
+ AnimationData(const byte *animResourceData, size_t animResourceLength) {
+ memset(this, 0, sizeof(*this));
+ resourceLength = animResourceLength;
+ resourceData = (byte*)malloc(animResourceLength);
+ memcpy(resourceData, animResourceData, animResourceLength);
+ }
+ ~AnimationData() {
+ free(frameOffsets);
+ free(resourceData);
+ }
+};
+
+class Anim {
+public:
+ Anim(SagaEngine *vm);
+ ~Anim(void);
+
+ void loadCutawayList(const byte *resourcePointer, size_t resourceLength);
+ void freeCutawayList(void);
+ void playCutaway(int cut, bool fade);
+ void endCutaway(void);
+ void returnFromCutaway(void);
+ void clearCutaway(void);
+
+ void load(uint16 animId, const byte *animResourceData, size_t animResourceLength);
+ void freeId(uint16 animId);
+ void play(uint16 animId, int vectorTime, bool playing = true);
+ void link(int16 animId1, int16 animId2);
+ void setFlag(uint16 animId, uint16 flag);
+ void clearFlag(uint16 animId, uint16 flag);
+ void setFrameTime(uint16 animId, int time);
+ void reset(void);
+ void animInfo(void);
+ void setCycles(uint16 animId, int cycles);
+ void stop(uint16 animId);
+ void finish(uint16 animId);
+ void resume(uint16 animId, int cycles);
+ int16 getCurrentFrame(uint16 animId);
+ bool hasCutaway(void) {
+ return _cutawayActive;
+ }
+ bool hasAnimation(uint16 animId) {
+ if (animId >= MAX_ANIMATIONS) {
+ if (animId < MAX_ANIMATIONS + ARRAYSIZE(_cutawayAnimations))
+ return (_cutawayAnimations[animId - MAX_ANIMATIONS] != NULL);
+ return false;
+ }
+ return (_animations[animId] != NULL);
+ }
+private:
+ void decodeFrame(AnimationData *anim, size_t frameOffset, byte *buf, size_t bufLength);
+ void fillFrameOffsets(AnimationData *anim);
+
+ void validateAnimationId(uint16 animId) {
+ if (animId >= MAX_ANIMATIONS) {
+ if (animId >= MAX_ANIMATIONS + ARRAYSIZE(_cutawayAnimations))
+ error("validateAnimationId: animId out of range");
+ if (_cutawayAnimations[animId - MAX_ANIMATIONS] == NULL) {
+ error("validateAnimationId: animId=%i unassigned", animId);
+ }
+ }
+ if (_animations[animId] == NULL) {
+ error("validateAnimationId: animId=%i unassigned", animId);
+ }
+ }
+
+ bool isLongData() const {
+ if ((_vm->getGameType() == GType_ITE) && (_vm->getPlatform() != Common::kPlatformMacintosh)) {
+ return false;
+ }
+ return true;
+ }
+
+ AnimationData* getAnimation(uint16 animId) {
+ validateAnimationId(animId);
+ if (animId > MAX_ANIMATIONS)
+ return _cutawayAnimations[animId - MAX_ANIMATIONS];
+ return _animations[animId];
+ }
+
+ uint16 getAnimationCount() const {
+ uint16 i = 0;
+ for (; i < MAX_ANIMATIONS; i++) {
+ if (_animations[i] == NULL) {
+ break;
+ }
+ }
+ return i;
+ }
+
+ SagaEngine *_vm;
+ AnimationData *_animations[MAX_ANIMATIONS];
+ AnimationData *_cutawayAnimations[2];
+ Cutaway *_cutawayList;
+ int _cutawayListLength;
+ bool _cutawayActive;
+};
+
+} // End of namespace Saga
+
+#endif /* ANIMATION_H_ */
diff --git a/engines/saga/console.cpp b/engines/saga/console.cpp
new file mode 100644
index 0000000000..3455c8c1c3
--- /dev/null
+++ b/engines/saga/console.cpp
@@ -0,0 +1,158 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Console module
+
+#include "saga/saga.h"
+#include "saga/actor.h"
+#include "saga/animation.h"
+#include "saga/scene.h"
+#include "saga/script.h"
+
+#include "saga/console.h"
+
+#include "common/debugger.cpp"
+
+namespace Saga {
+
+Console::Console(SagaEngine *vm) : Common::Debugger<Console>() {
+ _vm = vm;
+
+ DCmd_Register("continue", &Console::Cmd_Exit);
+ DCmd_Register("exit", &Console::Cmd_Exit);
+ DCmd_Register("quit", &Console::Cmd_Exit);
+ DCmd_Register("help", &Console::Cmd_Help);
+
+ // CVAR_Register_I(&_soundEnabled, "sound", NULL, CVAR_CFG, 0, 1);
+ // CVAR_Register_I(&_musicEnabled, "music", NULL, CVAR_CFG, 0, 1);
+
+ // Actor commands
+ DCmd_Register("actor_walk_to", &Console::cmdActorWalkTo);
+
+ // Animation commands
+ DCmd_Register("anim_info", &Console::Cmd_AnimInfo);
+
+ // Game stuff
+
+#if 0
+ // Register "g_language" cfg cvar
+ strncpy(GameModule.game_language, "us", MAXPATH);
+
+ CVAR_Register_S(GameModule.game_language, "g_language", NULL, CVAR_CFG, GAME_LANGSTR_LIMIT);
+
+ // Register "g_skipintro" cfg cvar
+ CVAR_Register_I(&GameModule.g_skipintro, "g_skipintro", NULL, CVAR_CFG, 0, 1);
+#endif
+
+ // Scene commands
+ DCmd_Register("scene_change", &Console::cmdSceneChange);
+ DCmd_Register("action_map_info", &Console::cmdActionMapInfo);
+ DCmd_Register("object_map_info", &Console::cmdObjectMapInfo);
+}
+
+Console::~Console() {
+}
+
+void Console::preEnter() {
+}
+
+void Console::postEnter() {
+}
+
+bool Console::Cmd_Exit(int argc, const char **argv) {
+ _detach_now = true;
+ return false;
+}
+
+bool Console::Cmd_Help(int argc, const char **argv) {
+ // console normally has 39 line width
+ // wrap around nicely
+ int width = 0, size, i;
+
+ DebugPrintf("Commands are:\n");
+ for (i = 0 ; i < _dcmd_count ; i++) {
+ size = strlen(_dcmds[i].name) + 1;
+
+ if ((width + size) >= 39) {
+ DebugPrintf("\n");
+ width = size;
+ } else
+ width += size;
+
+ DebugPrintf("%s ", _dcmds[i].name);
+ }
+
+ width = 0;
+
+ DebugPrintf("\n\nVariables are:\n");
+ for (i = 0 ; i < _dvar_count ; i++) {
+ size = strlen(_dvars[i].name) + 1;
+
+ if ((width + size) >= 39) {
+ DebugPrintf("\n");
+ width = size;
+ } else
+ width += size;
+
+ DebugPrintf("%s ", _dvars[i].name);
+ }
+
+ DebugPrintf("\n");
+
+ return true;
+}
+
+bool Console::cmdActorWalkTo(int argc, const char **argv) {
+ if (argc != 4)
+ DebugPrintf("Usage: %s <Actor id> <lx> <ly>\n", argv[0]);
+ else
+ _vm->_actor->cmdActorWalkTo(argc, argv);
+ return true;
+}
+
+
+bool Console::Cmd_AnimInfo(int argc, const char **argv) {
+ _vm->_anim->animInfo();
+ return true;
+}
+
+bool Console::cmdSceneChange(int argc, const char **argv) {
+ if (argc != 2)
+ DebugPrintf("Usage: %s <Scene number>\n", argv[0]);
+ else
+ _vm->_scene->cmdSceneChange(argc, argv);
+ return true;
+}
+
+bool Console::cmdActionMapInfo(int argc, const char **argv) {
+ _vm->_scene->cmdActionMapInfo();
+ return true;
+}
+
+bool Console::cmdObjectMapInfo(int argc, const char **argv) {
+ _vm->_scene->cmdObjectMapInfo();
+ return true;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/console.h b/engines/saga/console.h
new file mode 100644
index 0000000000..c93373960f
--- /dev/null
+++ b/engines/saga/console.h
@@ -0,0 +1,62 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+ // Console module header file
+
+#ifndef SAGA_CONSOLE_H_
+#define SAGA_CONSOLE_H_
+
+#include "common/debugger.h"
+
+namespace Saga {
+
+class Console : public Common::Debugger<Console> {
+public:
+ Console(SagaEngine *vm);
+ virtual ~Console(void);
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+private:
+ bool Cmd_Exit(int argc, const char **argv);
+ bool Cmd_Help(int argc, const char **argv);
+
+ bool cmdActorWalkTo(int argc, const char **argv);
+
+ bool Cmd_AnimInfo(int argc, const char **argv);
+
+ bool cmdSceneChange(int argc, const char **argv);
+ bool cmdActionMapInfo(int argc, const char **argv);
+ bool cmdObjectMapInfo(int argc, const char **argv);
+
+
+private:
+ SagaEngine *_vm;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/events.cpp b/engines/saga/events.cpp
new file mode 100644
index 0000000000..0d1da65efb
--- /dev/null
+++ b/engines/saga/events.cpp
@@ -0,0 +1,590 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Event management module
+
+#include "saga/saga.h"
+#include "saga/gfx.h"
+
+#include "saga/animation.h"
+#include "saga/console.h"
+#include "saga/scene.h"
+#include "saga/interface.h"
+#include "saga/palanim.h"
+#include "saga/render.h"
+#include "saga/sndres.h"
+#include "saga/music.h"
+#include "saga/actor.h"
+
+#include "saga/events.h"
+
+namespace Saga {
+
+Events::Events(SagaEngine *vm) : _vm(vm), _initialized(false) {
+ debug(8, "Initializing event subsystem...");
+ _initialized = true;
+}
+
+Events::~Events(void) {
+ debug(8, "Shutting down event subsystem...");
+ freeList();
+}
+
+// Function to process event list once per frame.
+// First advances event times, then processes each event with the appropriate
+// handler depending on the type of event.
+int Events::handleEvents(long msec) {
+ Event *event_p;
+
+ long delta_time;
+ int result;
+
+ // Advance event times
+ processEventTime(msec);
+
+ // Process each event in list
+ for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
+ event_p = (Event *)eventi.operator->();
+
+ // Call the appropriate event handler for the specific event type
+ switch (event_p->type) {
+
+ case kEvTOneshot:
+ result = handleOneShot(event_p);
+ break;
+
+ case kEvTContinuous:
+ result = handleContinuous(event_p);
+ break;
+
+ case kEvTInterval:
+ result = handleInterval(event_p);
+ break;
+
+ case kEvTImmediate:
+ result = handleImmediate(event_p);
+ break;
+
+ default:
+ result = kEvStInvalidCode;
+ warning("Invalid event code encountered");
+ break;
+ }
+
+ // Process the event appropriately based on result code from
+ // handler
+ if ((result == kEvStDelete) || (result == kEvStInvalidCode)) {
+ // If there is no event chain, delete the base event.
+ if (event_p->chain == NULL) {
+ eventi = _eventList.eraseAndPrev(eventi);
+ } else {
+ // If there is an event chain present, move the next event
+ // in the chain up, adjust it by the previous delta time,
+ // and reprocess the event
+ delta_time = event_p->time;
+ Event *from_chain = event_p->chain;
+ memcpy(event_p, from_chain, sizeof(*event_p));
+ free(from_chain);
+
+ event_p->time += delta_time;
+ --eventi;
+ }
+ } else if (result == kEvStBreak) {
+ break;
+ }
+ }
+
+ return SUCCESS;
+}
+
+int Events::handleContinuous(Event *event) {
+ double event_pc = 0.0; // Event completion percentage
+ int event_done = 0;
+
+ Surface *backGroundSurface;
+ BGInfo bgInfo;
+ Rect rect;
+ if(event->duration != 0) {
+ event_pc = ((double)event->duration - event->time) / event->duration;
+ } else {
+ event_pc = 1.0;
+ }
+
+ if (event_pc >= 1.0) {
+ // Cap percentage to 100
+ event_pc = 1.0;
+ event_done = 1;
+ }
+
+ if (event_pc < 0.0) {
+ // Event not signaled, skip it
+ return kEvStContinue;
+ } else if (!(event->code & kEvFSignaled)) {
+ // Signal event
+ event->code |= kEvFSignaled;
+ event_pc = 0.0;
+ }
+
+ switch (event->code & EVENT_MASK) {
+ case kPalEvent:
+ switch (event->op) {
+ case kEventBlackToPal:
+ _vm->_gfx->blackToPal((PalEntry *)event->data, event_pc);
+ break;
+
+ case kEventPalToBlack:
+ _vm->_gfx->palToBlack((PalEntry *)event->data, event_pc);
+ break;
+ default:
+ break;
+ }
+ break;
+ case kTransitionEvent:
+ switch (event->op) {
+ case kEventDissolve:
+ backGroundSurface = _vm->_render->getBackGroundSurface();
+ _vm->_scene->getBGInfo(bgInfo);
+ rect.left = rect.top = 0;
+ rect.right = bgInfo.bounds.width();
+ rect.bottom = bgInfo.bounds.height();
+ backGroundSurface->transitionDissolve(bgInfo.buffer, rect, 0, event_pc);
+ break;
+ case kEventDissolveBGMask:
+ // we dissolve it centered.
+ // set flag of Dissolve to 1. It is a hack to simulate zero masking.
+ int w, h;
+ byte *maskBuffer;
+ size_t len;
+
+ backGroundSurface = _vm->_render->getBackGroundSurface();
+ _vm->_scene->getBGMaskInfo(w, h, maskBuffer, len);
+ rect.left = (_vm->getDisplayWidth() - w) / 2;
+ rect.top = (_vm->getDisplayHeight() - h) / 2;
+ rect.setWidth(w);
+ rect.setHeight(h);
+
+ backGroundSurface->transitionDissolve( maskBuffer, rect, 1, event_pc);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+
+ }
+
+ if (event_done) {
+ return kEvStDelete;
+ }
+
+ return kEvStContinue;
+}
+
+int Events::handleImmediate(Event *event) {
+ double event_pc = 0.0; // Event completion percentage
+ bool event_done = false;
+
+ // Duration might be 0 so dont do division then
+ if(event->duration != 0) {
+ event_pc = ((double)event->duration - event->time) / event->duration;
+ } else {
+ // Just make sure that event_pc is 1.0 so event_done is true
+ event_pc = 1.0;
+ }
+
+ if (event_pc >= 1.0) {
+ // Cap percentage to 100
+ event_pc = 1.0;
+ event_done = true;
+ }
+
+ if (event_pc < 0.0) {
+ // Event not signaled, skip it
+ return kEvStBreak;
+ } else if (!(event->code & kEvFSignaled)) {
+ // Signal event
+ event->code |= kEvFSignaled;
+ event_pc = 0.0;
+ }
+
+ switch (event->code & EVENT_MASK) {
+ case kPalEvent:
+ switch (event->op) {
+ case kEventBlackToPal:
+ _vm->_gfx->blackToPal((PalEntry *)event->data, event_pc);
+ break;
+
+ case kEventPalToBlack:
+ _vm->_gfx->palToBlack((PalEntry *)event->data, event_pc);
+ break;
+ default:
+ break;
+ }
+ break;
+ case kScriptEvent:
+ case kBgEvent:
+ case kInterfaceEvent:
+ handleOneShot(event);
+ event_done = true;
+ break;
+ default:
+ break;
+
+ }
+
+ if (event_done) {
+ return kEvStDelete;
+ }
+
+ return kEvStBreak;
+}
+
+int Events::handleOneShot(Event *event) {
+ Surface *backBuffer;
+ ScriptThread *sthread;
+ Rect rect;
+
+
+ if (event->time > 0) {
+ return kEvStContinue;
+ }
+
+ // Event has been signaled
+
+ switch (event->code & EVENT_MASK) {
+ case kTextEvent:
+ switch (event->op) {
+ case kEventDisplay:
+ ((TextListEntry *)event->data)->display = true;
+ break;
+ case kEventRemove:
+ _vm->_scene->_textList.remove((TextListEntry *)event->data);
+ break;
+ default:
+ break;
+ }
+
+ break;
+ case kSoundEvent:
+ _vm->_sound->stopSound();
+ if (event->op == kEventPlay)
+ _vm->_sndRes->playSound(event->param, event->param2, event->param3 != 0);
+ break;
+ case kVoiceEvent:
+ _vm->_sndRes->playVoice(event->param);
+ break;
+ case kMusicEvent:
+ _vm->_music->stop();
+ if (event->op == kEventPlay)
+ _vm->_music->play(event->param, (MusicFlags)event->param2);
+ break;
+ case kBgEvent:
+ {
+ Surface *backGroundSurface;
+ BGInfo bgInfo;
+
+ if (!(_vm->_scene->getFlags() & kSceneFlagISO)) {
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+ backGroundSurface = _vm->_render->getBackGroundSurface();
+ _vm->_scene->getBGInfo(bgInfo);
+
+ backGroundSurface->blit(bgInfo.bounds, bgInfo.buffer);
+
+ // If it is inset scene then draw black border
+ if (bgInfo.bounds.width() < _vm->getDisplayWidth() || bgInfo.bounds.height() < _vm->_scene->getHeight()) {
+ Common::Rect rect1(2, bgInfo.bounds.height() + 4);
+ Common::Rect rect2(bgInfo.bounds.width() + 4, 2);
+ Common::Rect rect3(2, bgInfo.bounds.height() + 4);
+ Common::Rect rect4(bgInfo.bounds.width() + 4, 2);
+ rect1.moveTo(bgInfo.bounds.left - 2, bgInfo.bounds.top - 2);
+ rect2.moveTo(bgInfo.bounds.left - 2, bgInfo.bounds.top - 2);
+ rect3.moveTo(bgInfo.bounds.right, bgInfo.bounds.top - 2);
+ rect4.moveTo(bgInfo.bounds.left - 2, bgInfo.bounds.bottom);
+
+ backGroundSurface->drawRect(rect1, kITEColorBlack);
+ backGroundSurface->drawRect(rect2, kITEColorBlack);
+ backGroundSurface->drawRect(rect3, kITEColorBlack);
+ backGroundSurface->drawRect(rect4, kITEColorBlack);
+ }
+
+ if (event->param == kEvPSetPalette) {
+ PalEntry *palPointer;
+ _vm->_scene->getBGPal(palPointer);
+ _vm->_gfx->setPalette(palPointer);
+ }
+ }
+ }
+ break;
+ case kAnimEvent:
+ switch (event->op) {
+ case kEventPlay:
+ _vm->_anim->play(event->param, event->time, true);
+ break;
+ case kEventStop:
+ _vm->_anim->stop(event->param);
+ break;
+ case kEventFrame:
+ _vm->_anim->play(event->param, event->time, false);
+ break;
+ case kEventSetFlag:
+ _vm->_anim->setFlag(event->param, event->param2);
+ break;
+ case kEventClearFlag:
+ _vm->_anim->clearFlag(event->param, event->param2);
+ break;
+ default:
+ break;
+ }
+ break;
+ case kSceneEvent:
+ switch (event->op) {
+ case kEventEnd:
+ _vm->_scene->nextScene();
+ return kEvStBreak;
+ break;
+ default:
+ break;
+ }
+ break;
+ case kPalAnimEvent:
+ switch (event->op) {
+ case kEventCycleStart:
+ _vm->_palanim->cycleStart();
+ break;
+ case kEventCycleStep:
+ _vm->_palanim->cycleStep(event->time);
+ break;
+ default:
+ break;
+ }
+ break;
+ case kInterfaceEvent:
+ switch (event->op) {
+ case kEventActivate:
+ _vm->_interface->activate();
+ break;
+ case kEventDeactivate:
+ _vm->_interface->deactivate();
+ break;
+ case kEventSetStatus:
+ _vm->_interface->setStatusText((const char*)event->data);
+ _vm->_interface->drawStatusBar();
+ break;
+ case kEventClearStatus:
+ _vm->_interface->setStatusText("");
+ _vm->_interface->drawStatusBar();
+ break;
+ case kEventSetFadeMode:
+ _vm->_interface->setFadeMode(event->param);
+ break;
+ default:
+ break;
+ }
+ break;
+ case kScriptEvent:
+ switch (event->op) {
+ case kEventExecBlocking:
+ case kEventExecNonBlocking:
+ debug(6, "Exec module number %d script entry number %d", event->param, event->param2);
+
+ sthread = _vm->_script->createThread(event->param, event->param2);
+ if (sthread == NULL) {
+ _vm->_console->DebugPrintf("Thread creation failed.\n");
+ break;
+ }
+
+ sthread->_threadVars[kThreadVarAction] = event->param3;
+ sthread->_threadVars[kThreadVarObject] = event->param4;
+ sthread->_threadVars[kThreadVarWithObject] = event->param5;
+ sthread->_threadVars[kThreadVarActor] = event->param6;
+
+ if (event->op == kEventExecBlocking)
+ _vm->_script->completeThread();
+
+ break;
+ case kEventThreadWake:
+ _vm->_script->wakeUpThreads(event->param);
+ break;
+ }
+ break;
+ case kCursorEvent:
+ switch (event->op) {
+ case kEventShow:
+ _vm->_gfx->showCursor(true);
+ break;
+ case kEventHide:
+ _vm->_gfx->showCursor(false);
+ break;
+ default:
+ break;
+ }
+ break;
+ case kGraphicsEvent:
+ switch (event->op) {
+ case kEventFillRect:
+ rect.top = event->param2;
+ rect.bottom = event->param3;
+ rect.left = event->param4;
+ rect.right = event->param5;
+ ((Surface *)event->data)->drawRect(rect, event->param);
+ break;
+ case kEventSetFlag:
+ _vm->_render->setFlag(event->param);
+ break;
+ case kEventClearFlag:
+ _vm->_render->clearFlag(event->param);
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ return kEvStDelete;
+}
+
+int Events::handleInterval(Event *event) {
+ return kEvStDelete;
+}
+
+// Schedules an event in the event list; returns a pointer to the scheduled
+// event suitable for chaining if desired.
+Event *Events::queue(Event *event) {
+ Event *queuedEvent;
+
+ queuedEvent = _eventList.pushBack(*event).operator->();
+ initializeEvent(queuedEvent);
+
+ return queuedEvent;
+}
+
+// Places a 'add_event' on the end of an event chain given by 'head_event'
+// (head_event may be in any position in the event chain)
+Event *Events::chain(Event *headEvent, Event *addEvent) {
+ if (headEvent == NULL) {
+ return queue(addEvent);
+ }
+
+ Event *walkEvent;
+ for (walkEvent = headEvent; walkEvent->chain != NULL; walkEvent = walkEvent->chain) {
+ continue;
+ }
+
+ walkEvent->chain = (Event *)malloc(sizeof(*walkEvent->chain));
+ *walkEvent->chain = *addEvent;
+ initializeEvent(walkEvent->chain);
+
+ return walkEvent->chain;
+}
+
+int Events::initializeEvent(Event *event) {
+ event->chain = NULL;
+ switch (event->type) {
+ case kEvTOneshot:
+ break;
+ case kEvTContinuous:
+ case kEvTImmediate:
+ event->time += event->duration;
+ break;
+ case kEvTInterval:
+ break;
+ default:
+ return FAILURE;
+ break;
+ }
+
+ return SUCCESS;
+}
+
+int Events::clearList() {
+ Event *chain_walk;
+ Event *next_chain;
+ Event *event_p;
+
+ // Walk down event list
+ for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
+ event_p = (Event *)eventi.operator->();
+
+ // Only remove events not marked kEvFNoDestory (engine events)
+ if (!(event_p->code & kEvFNoDestory)) {
+ // Remove any events chained off this one */
+ for (chain_walk = event_p->chain; chain_walk != NULL; chain_walk = next_chain) {
+ next_chain = chain_walk->chain;
+ free(chain_walk);
+ }
+ eventi = _eventList.eraseAndPrev(eventi);
+ }
+ }
+
+ return SUCCESS;
+}
+
+// Removes all events from the list (even kEvFNoDestory)
+int Events::freeList() {
+ Event *chain_walk;
+ Event *next_chain;
+ Event *event_p;
+
+ // Walk down event list
+ EventList::iterator eventi = _eventList.begin();
+ while (eventi != _eventList.end()) {
+ event_p = (Event *)eventi.operator->();
+
+ // Remove any events chained off this one */
+ for (chain_walk = event_p->chain; chain_walk != NULL; chain_walk = next_chain) {
+ next_chain = chain_walk->chain;
+ free(chain_walk);
+ }
+ eventi=_eventList.erase(eventi);
+ }
+
+ return SUCCESS;
+}
+
+// Walks down the event list, updating event times by 'msec'.
+int Events::processEventTime(long msec) {
+ Event *event_p;
+ uint16 event_count = 0;
+
+ for (EventList::iterator eventi = _eventList.begin(); eventi != _eventList.end(); ++eventi) {
+ event_p = (Event *)eventi.operator->();
+
+ event_p->time -= msec;
+ event_count++;
+
+ if (event_p->type == kEvTImmediate)
+ break;
+
+ if (event_count > EVENT_WARNINGCOUNT) {
+ warning("Event list exceeds %u", EVENT_WARNINGCOUNT);
+ }
+ }
+
+ return SUCCESS;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/events.h b/engines/saga/events.h
new file mode 100644
index 0000000000..d89b3d89f5
--- /dev/null
+++ b/engines/saga/events.h
@@ -0,0 +1,179 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Event management module header file
+
+#ifndef SAGA_EVENT_H
+#define SAGA_EVENT_H
+
+#include "saga/list.h"
+
+namespace Saga {
+
+enum EventTypes {
+ kEvTOneshot, // Event takes no time
+ kEvTContinuous, // Event takes time; next event starts immediately
+ kEvTInterval, // Not yet implemented
+ kEvTImmediate // Event takes time; next event starts when event is done
+};
+
+enum EventFlags {
+ kEvFSignaled = 0x8000,
+ kEvFNoDestory = 0x4000
+};
+
+enum EventCodes {
+ kBgEvent = 1,
+ kAnimEvent,
+ kMusicEvent,
+ kVoiceEvent,
+ kSoundEvent,
+ kSceneEvent,
+ kTextEvent,
+ kPalEvent,
+ kPalAnimEvent,
+ kTransitionEvent,
+ kInterfaceEvent,
+ kActorEvent,
+ kScriptEvent,
+ kCursorEvent,
+ kGraphicsEvent
+};
+
+enum EventOps {
+ // INSTANTANEOUS events
+ // BG events
+ kEventDisplay = 1,
+ // ANIM events
+ // kEventPlay = 1, // reused
+ // kEventStop = 2, // reused
+ kEventFrame = 3,
+ kEventSetFlag = 4,
+ kEventClearFlag = 5,
+ // MUISC & SOUND events
+ kEventPlay = 1,
+ kEventStop = 2,
+ // SCENE events
+ kEventEnd = 2,
+ // TEXT events
+ kEventHide = 2,
+ kEventRemove = 3,
+ // PALANIM events
+ kEventCycleStart = 1,
+ kEventCycleStep = 2,
+ // INTERFACE events
+ kEventActivate = 1,
+ kEventDeactivate = 2,
+ kEventSetStatus = 3,
+ kEventClearStatus = 4,
+ kEventSetFadeMode = 5,
+ // ACTOR events
+ kEventMove = 1,
+ // SCRIPT events
+ kEventExecBlocking = 1,
+ kEventExecNonBlocking = 2,
+ kEventThreadWake = 3,
+ // CURSOR events
+ kEventShow = 1,
+ // kEventHide = 2, // reused
+ // GRAPHICS events
+ kEventFillRect = 1,
+ // kEventSetFlag = 4, // reused
+ // kEventClearFlag = 5, // reused
+
+ // CONTINUOUS events
+ // PALETTE events
+ kEventPalToBlack = 1,
+ kEventBlackToPal = 2,
+ // TRANSITION events
+ kEventDissolve = 1,
+ kEventDissolveBGMask = 2
+};
+
+enum EventParams {
+ kEvPNoSetPalette,
+ kEvPSetPalette
+};
+
+struct Event {
+ unsigned int type;
+ unsigned int code; // Event operation category & flags
+ int op; // Event operation
+ long param; // Optional event parameter
+ long param2;
+ long param3;
+ long param4;
+ long param5;
+ long param6;
+ void *data; // Optional event data
+ long time; // Elapsed time until event
+ long duration; // Duration of event
+ long d_reserved;
+
+ Event *chain; // Event chain (For consecutive events)
+ Event() {
+ memset(this, 0, sizeof(*this));
+ }
+};
+
+typedef SortedList<Event> EventList;
+
+#define EVENT_WARNINGCOUNT 1000
+#define EVENT_MASK 0x00FF
+
+enum EventStatusCode {
+ kEvStInvalidCode = 0,
+ kEvStDelete,
+ kEvStContinue,
+ kEvStBreak
+};
+
+class Events {
+ public:
+ Events(SagaEngine *vm);
+ ~Events(void);
+ int handleEvents(long msec);
+ int clearList();
+ int freeList();
+ Event *queue(Event *event);
+ Event *chain(Event *headEvent, Event *addEvent);
+
+ private:
+ int handleContinuous(Event *event);
+ int handleOneShot(Event *event);
+ int handleInterval(Event *event);
+ int handleImmediate(Event *event);
+ int processEventTime(long msec);
+ int initializeEvent(Event *event);
+
+ private:
+ SagaEngine *_vm;
+ bool _initialized;
+
+ EventList _eventList;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/font.cpp b/engines/saga/font.cpp
new file mode 100644
index 0000000000..9a91bf4872
--- /dev/null
+++ b/engines/saga/font.cpp
@@ -0,0 +1,681 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Font management and font drawing module
+#include "saga/saga.h"
+#include "saga/gfx.h"
+#include "saga/rscfile.h"
+
+#include "saga/font.h"
+#include "saga/stream.h"
+
+namespace Saga {
+
+Font::Font(SagaEngine *vm) : _vm(vm), _initialized(false) {
+ int i;
+
+ // Load font module resource context
+
+ assert(_vm->getFontsCount() > 0);
+
+ _fonts = (FontData **)calloc(_vm->getFontsCount(), sizeof(*_fonts));
+ _loadedFonts = 0;
+
+ for (i = 0; i < _vm->getFontsCount(); i++) {
+ loadFont(_vm->getFontDescription(i)->fontResourceId);
+ }
+
+ _initialized = true;
+}
+
+Font::~Font(void) {
+ debug(8, "Font::~Font(): Freeing fonts.");
+ int i;
+
+ for (i = 0 ; i < _loadedFonts ; i++) {
+ if (_fonts[i] != NULL) {
+ free(_fonts[i]->normal.font);
+ free(_fonts[i]->outline.font);
+ }
+
+ free(_fonts[i]);
+ }
+}
+
+
+void Font::loadFont(uint32 fontResourceId) {
+ FontData *font;
+ byte *fontResourcePointer;
+ size_t fontResourceLength;
+ int numBits;
+ int c;
+ ResourceContext *fontContext;
+
+ debug(1, "Font::loadFont(): Reading fontResourceId %d...", fontResourceId);
+
+ fontContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ if (fontContext == NULL) {
+ error("Font::Font() resource context not found");
+ }
+
+ // Load font resource
+ _vm->_resource->loadResource(fontContext, fontResourceId, fontResourcePointer, fontResourceLength);
+
+ if (fontResourceLength < FONT_DESCSIZE) {
+ error("Font::loadFont() Invalid font length (%i < %i)", fontResourceLength, FONT_DESCSIZE);
+ }
+
+ MemoryReadStreamEndian readS(fontResourcePointer, fontResourceLength, fontContext->isBigEndian);
+
+ // Create new font structure
+ font = (FontData *)malloc(sizeof(*font));
+
+ // Read font header
+ font->normal.header.charHeight = readS.readUint16();
+ font->normal.header.charWidth = readS.readUint16();
+ font->normal.header.rowLength = readS.readUint16();
+
+
+ debug(2, "Character width: %d", font->normal.header.charWidth);
+ debug(2, "Character height: %d", font->normal.header.charHeight);
+ debug(2, "Row padding: %d", font->normal.header.rowLength);
+
+ for (c = 0; c < FONT_CHARCOUNT; c++) {
+ font->normal.fontCharEntry[c].index = readS.readUint16();
+ }
+
+ for (c = 0; c < FONT_CHARCOUNT; c++) {
+ numBits = font->normal.fontCharEntry[c].width = readS.readByte();
+ font->normal.fontCharEntry[c].byteWidth = getByteLen(numBits);
+ }
+
+ for (c = 0; c < FONT_CHARCOUNT; c++) {
+ font->normal.fontCharEntry[c].flag = readS.readByte();
+ }
+
+ for (c = 0; c < FONT_CHARCOUNT; c++) {
+ font->normal.fontCharEntry[c].tracking = readS.readByte();
+ }
+
+ if (readS.pos() != FONT_DESCSIZE) {
+ error("Invalid font resource size.");
+ }
+
+ font->normal.font = (byte*)malloc(fontResourceLength - FONT_DESCSIZE);
+ memcpy(font->normal.font, fontResourcePointer + FONT_DESCSIZE, fontResourceLength - FONT_DESCSIZE);
+
+ free(fontResourcePointer);
+
+
+ // Create outline font style
+ createOutline(font);
+
+ // Set font data
+ _fonts[_loadedFonts++] = font;
+}
+
+void Font::createOutline(FontData *font) {
+ int i;
+ int row;
+ int newByteWidth;
+ int oldByteWidth;
+ int newRowLength = 0;
+ size_t indexOffset = 0;
+ int index;
+ int currentByte;
+ unsigned char *basePointer;
+ unsigned char *srcPointer;
+ unsigned char *destPointer1;
+ unsigned char *destPointer2;
+ unsigned char *destPointer3;
+ unsigned char charRep;
+
+
+ // Populate new font style character data
+ for (i = 0; i < FONT_CHARCOUNT; i++) {
+ newByteWidth = 0;
+ oldByteWidth = 0;
+ index = font->normal.fontCharEntry[i].index;
+ if ((index > 0) || (i == FONT_FIRSTCHAR)) {
+ index += indexOffset;
+ }
+
+ font->outline.fontCharEntry[i].index = index;
+ font->outline.fontCharEntry[i].tracking = font->normal.fontCharEntry[i].tracking;
+ font->outline.fontCharEntry[i].flag = font->normal.fontCharEntry[i].flag;
+
+ if (font->normal.fontCharEntry[i].width != 0) {
+ newByteWidth = getByteLen(font->normal.fontCharEntry[i].width + 2);
+ oldByteWidth = getByteLen(font->normal.fontCharEntry[i].width);
+
+ if (newByteWidth > oldByteWidth) {
+ indexOffset++;
+ }
+ }
+
+ font->outline.fontCharEntry[i].width = font->normal.fontCharEntry[i].width + 2;
+ font->outline.fontCharEntry[i].byteWidth = newByteWidth;
+ newRowLength += newByteWidth;
+ }
+
+ debug(2, "New row length: %d", newRowLength);
+
+ font->outline.header = font->normal.header;
+ font->outline.header.charWidth += 2;
+ font->outline.header.charHeight += 2;
+ font->outline.header.rowLength = newRowLength;
+
+ // Allocate new font representation storage
+ font->outline.font = (unsigned char *)calloc(newRowLength, font->outline.header.charHeight);
+
+
+ // Generate outline font representation
+ for (i = 0; i < FONT_CHARCOUNT; i++) {
+ for (row = 0; row < font->normal.header.charHeight; row++) {
+ for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
+ basePointer = font->outline.font + font->outline.fontCharEntry[i].index + currentByte;
+ destPointer1 = basePointer + newRowLength * row;
+ destPointer2 = basePointer + newRowLength * (row + 1);
+ destPointer3 = basePointer + newRowLength * (row + 2);
+ if (currentByte > 0) {
+ // Get last two columns from previous byte
+ srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1);
+ charRep = *srcPointer;
+ *destPointer1 |= ((charRep << 6) | (charRep << 7));
+ *destPointer2 |= ((charRep << 6) | (charRep << 7));
+ *destPointer3 |= ((charRep << 6) | (charRep << 7));
+ }
+
+ if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
+ srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte;
+ charRep = *srcPointer;
+ *destPointer1 |= charRep | (charRep >> 1) | (charRep >> 2);
+ *destPointer2 |= charRep | (charRep >> 1) | (charRep >> 2);
+ *destPointer3 |= charRep | (charRep >> 1) | (charRep >> 2);
+ }
+ }
+ }
+
+ // "Hollow out" character to prevent overdraw
+ for (row = 0; row < font->normal.header.charHeight; row++) {
+ for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
+ destPointer2 = font->outline.font + font->outline.header.rowLength * (row + 1) + font->outline.fontCharEntry[i].index + currentByte;
+ if (currentByte > 0) {
+ // Get last two columns from previous byte
+ srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1);
+ *destPointer2 &= ((*srcPointer << 7) ^ 0xFFU);
+ }
+
+ if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
+ srcPointer = font->normal.font + font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte;
+ *destPointer2 &= ((*srcPointer >> 1) ^ 0xFFU);
+ }
+ }
+ }
+ }
+}
+
+// Returns the horizontal length in pixels of the graphical representation
+// of at most 'count' characters of the string 'text', taking
+// into account any formatting options specified by 'flags'.
+// If 'count' is 0, all characters of 'test' are counted.
+int Font::getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) {
+ FontData *font;
+ size_t ct;
+ int width = 0;
+ int ch;
+ const byte *txt;
+
+
+ font = getFont(fontId);
+
+ txt = (const byte *) text;
+
+ for (ct = count; *txt && (!count || ct > 0); txt++, ct--) {
+ ch = *txt & 0xFFU;
+ // Translate character
+ ch = _charMap[ch];
+ assert(ch < FONT_CHARCOUNT);
+ width += font->normal.fontCharEntry[ch].tracking;
+ }
+
+ if ((flags & kFontBold) || (flags & kFontOutline)) {
+ width += 1;
+ }
+
+ return width;
+}
+
+
+void Font::draw(FontId fontId, Surface *ds, const char *text, size_t count, const Common::Point &point,
+ int color, int effectColor, FontEffectFlags flags) {
+ FontData *font;
+ Point offsetPoint(point);
+
+ font = getFont(fontId);
+
+ if (flags & kFontOutline) {
+ offsetPoint.x--;
+ offsetPoint.y--;
+ outFont(font->outline, ds, text, count, offsetPoint, effectColor, flags);
+ outFont(font->normal, ds, text, count, point, color, flags);
+ } else if (flags & kFontShadow) {
+ offsetPoint.x--;
+ offsetPoint.y++;
+ outFont(font->normal, ds, text, count, offsetPoint, effectColor, flags);
+ outFont(font->normal, ds, text, count, point, color, flags);
+ } else { // FONT_NORMAL
+ outFont(font->normal, ds, text, count, point, color, flags);
+ }
+}
+
+void Font::outFont(const FontStyle &drawFont, Surface *ds, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags) {
+ const byte *textPointer;
+ byte *c_dataPointer;
+ int c_code;
+ int charRow;
+ Point textPoint(point);
+
+ byte *outputPointer;
+ byte *outputPointer_min;
+ byte *outputPointer_max;
+
+ int row;
+ int rowLimit;
+
+ int c_byte_len;
+ int c_byte;
+ int c_bit;
+ int ct;
+
+ if ((point.x > ds->w) || (point.y > ds->h)) {
+ // Output string can't be visible
+ return;
+ }
+
+ textPointer = (const byte *)text;
+ ct = count;
+
+ // Draw string one character at a time, maximum of 'draw_str'_ct
+ // characters, or no limit if 'draw_str_ct' is 0
+ for (; *textPointer && (!count || ct); textPointer++, ct--) {
+ c_code = *textPointer & 0xFFU;
+
+ // Translate character
+ if (!(flags & kFontDontmap))
+ c_code = _charMap[c_code];
+ assert(c_code < FONT_CHARCOUNT);
+
+ // Check if character is defined
+ if ((drawFont.fontCharEntry[c_code].index == 0) && (c_code != FONT_FIRSTCHAR)) {
+#if FONT_SHOWUNDEFINED
+ if (c_code == FONT_CH_SPACE) {
+ textPoint.x += drawFont.fontCharEntry[c_code].tracking;
+ continue;
+ }
+ c_code = FONT_CH_QMARK;
+#else
+ // Character code is not defined, but advance tracking
+ // ( Not defined if offset is 0, except for 33 ('!') which
+ // is defined )
+ textPoint.x += drawFont.fontCharEntry[c_code].tracking;
+ continue;
+#endif
+ }
+
+ // Get length of character in bytes
+ c_byte_len = ((drawFont.fontCharEntry[c_code].width - 1) / 8) + 1;
+ rowLimit = (ds->h < (textPoint.y + drawFont.header.charHeight)) ? ds->h : textPoint.y + drawFont.header.charHeight;
+ charRow = 0;
+
+ for (row = textPoint.y; row < rowLimit; row++, charRow++) {
+ // Clip negative rows */
+ if (row < 0) {
+ continue;
+ }
+
+ outputPointer = (byte *)ds->pixels + (ds->pitch * row) + textPoint.x;
+ outputPointer_min = (byte *)ds->pixels + (ds->pitch * row) + (textPoint.x > 0 ? textPoint.x : 0);
+ outputPointer_max = outputPointer + (ds->pitch - textPoint.x);
+
+ // If character starts off the screen, jump to next character
+ if (outputPointer < outputPointer_min) {
+ break;
+ }
+
+ c_dataPointer = drawFont.font + charRow * drawFont.header.rowLength + drawFont.fontCharEntry[c_code].index;
+
+ for (c_byte = 0; c_byte < c_byte_len; c_byte++, c_dataPointer++) {
+ // Check each bit, draw pixel if bit is set
+ for (c_bit = 7; c_bit >= 0 && (outputPointer < outputPointer_max); c_bit--) {
+ if ((*c_dataPointer >> c_bit) & 0x01) {
+ *outputPointer = (byte)color;
+ }
+ outputPointer++;
+ } // end per-bit processing
+ } // end per-byte processing
+ } // end per-row processing
+
+ // Advance tracking position
+ textPoint.x += drawFont.fontCharEntry[c_code].tracking;
+ } // end per-character processing
+}
+
+
+void Font::textDraw(FontId fontId, Surface *ds, const char *text, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) {
+ int textWidth;
+ int textLength;
+ int fitWidth;
+ Common::Point textPoint(point);
+
+ textLength = strlen(text);
+
+ if (!(flags & kFontCentered)) {
+ // Text is not centered; No formatting required
+ draw(fontId, ds, text, textLength, point, color, effectColor, flags);
+ return;
+ }
+
+ // Text is centered... format output
+ // Enforce minimum and maximum center points for centered text
+ if (textPoint.x < TEXT_CENTERLIMIT) {
+ textPoint.x = TEXT_CENTERLIMIT;
+ }
+
+ if (textPoint.x > ds->w - TEXT_CENTERLIMIT) {
+ textPoint.x = ds->w - TEXT_CENTERLIMIT;
+ }
+
+ if (textPoint.x < (TEXT_MARGIN * 2)) {
+ // Text can't be centered if it's too close to the margin
+ return;
+ }
+
+ textWidth = getStringWidth(fontId, text, textLength, flags);
+
+ if (textPoint.x < (ds->w / 2)) {
+ // Fit to right side
+ fitWidth = (textPoint.x - TEXT_MARGIN) * 2;
+ } else {
+ // Fit to left side
+ fitWidth = ((ds->w - TEXT_MARGIN) - textPoint.x) * 2;
+ }
+
+ if (fitWidth < textWidth) {
+ warning("text too long to be displayed in one line");
+ textWidth = fitWidth;
+ }
+ // Entire string fits, draw it
+ textPoint.x = textPoint.x - (textWidth / 2);
+ draw(fontId, ds, text, textLength, textPoint, color, effectColor, flags);
+}
+
+int Font::getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags) {
+ int textWidth;
+ int textLength;
+ int fitWidth;
+ const char *startPointer;
+ const char *searchPointer;
+ const char *measurePointer;
+ const char *foundPointer;
+ int len;
+ int w;
+ const char *endPointer;
+ int h;
+ int wc;
+ int w_total;
+ int len_total;
+ Common::Point textPoint;
+ Common::Point textPoint2;
+
+ textLength = strlen(text);
+
+ textWidth = getStringWidth(fontId, text, textLength, flags);
+ h = getHeight(fontId);
+ fitWidth = width;
+
+ textPoint.x = (fitWidth / 2);
+ textPoint.y = 0;
+
+ if (fitWidth >= textWidth) {
+ return h;
+ }
+
+ // String won't fit on one line
+ w_total = 0;
+ len_total = 0;
+ wc = 0;
+
+ startPointer = text;
+ measurePointer = text;
+ searchPointer = text;
+ endPointer = text + textLength;
+
+ for (;;) {
+ foundPointer = strchr(searchPointer, ' ');
+ if (foundPointer == NULL) {
+ // Ran to the end of the buffer
+ len = endPointer - measurePointer;
+ } else {
+ len = foundPointer - measurePointer;
+ }
+
+ w = getStringWidth(fontId, measurePointer, len, flags);
+ measurePointer = foundPointer;
+
+ if ((w_total + w) > fitWidth) {
+ // This word won't fit
+ if (wc == 0) {
+ // The first word in the line didn't fit. Still print it
+ searchPointer = measurePointer + 1;
+ }
+ // Wrap what we've got and restart
+ textPoint.y += h + TEXT_LINESPACING;
+ if (foundPointer == NULL) {
+ // Since word hit NULL but fit, we are done
+ return textPoint.y + h;
+ }
+ w_total = 0;
+ len_total = 0;
+ wc = 0;
+ measurePointer = searchPointer;
+ startPointer = searchPointer;
+ } else {
+ // Word will fit ok
+ w_total += w;
+ len_total += len;
+ wc++;
+ if (foundPointer == NULL) {
+ // Since word hit NULL but fit, we are done
+ return textPoint.y + h;
+ }
+ searchPointer = measurePointer + 1;
+ }
+ }
+}
+
+void Font::textDrawRect(FontId fontId, Surface *ds, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) {
+ int textWidth;
+ int textLength;
+ int fitWidth;
+ const char *startPointer;
+ const char *searchPointer;
+ const char *measurePointer;
+ const char *foundPointer;
+ int len;
+ int w;
+ const char *endPointer;
+ int h;
+ int wc;
+ int w_total;
+ int len_total;
+ Common::Point textPoint;
+ Common::Point textPoint2;
+
+ textLength = strlen(text);
+
+ textWidth = getStringWidth(fontId, text, textLength, flags);
+ fitWidth = rect.width();
+
+ textPoint.x = rect.left + (fitWidth / 2);
+ textPoint.y = rect.top;
+
+ if (fitWidth >= textWidth) {
+ // Entire string fits, draw it
+ textPoint.x -= (textWidth / 2);
+ draw(fontId, ds, text, textLength, textPoint, color, effectColor, flags);
+ return;
+ }
+
+ // String won't fit on one line
+ h = getHeight(fontId);
+ w_total = 0;
+ len_total = 0;
+ wc = 0;
+
+ startPointer = text;
+ measurePointer = text;
+ searchPointer = text;
+ endPointer = text + textLength;
+
+ for (;;) {
+ foundPointer = strchr(searchPointer, ' ');
+ if (foundPointer == NULL) {
+ // Ran to the end of the buffer
+ len = endPointer - measurePointer;
+ } else {
+ len = foundPointer - measurePointer;
+ }
+
+ w = getStringWidth(fontId, measurePointer, len, flags);
+ measurePointer = foundPointer;
+
+ if ((w_total + w) > fitWidth) {
+ // This word won't fit
+ if (wc == 0) {
+ w_total = fitWidth;
+ len_total = len;
+ }
+
+ // Wrap what we've got and restart
+ textPoint2.x = textPoint.x - (w_total / 2);
+ textPoint2.y = textPoint.y;
+ draw(fontId, ds, startPointer, len_total, textPoint2, color, effectColor, flags);
+ textPoint.y += h + TEXT_LINESPACING;
+ if (textPoint.y >= rect.bottom) {
+ return;
+ }
+ w_total = 0;
+ len_total = 0;
+ if (wc == 0) {
+ searchPointer = measurePointer + 1;
+ }
+ wc = 0;
+
+ // Advance the search pointer to the next non-space.
+ // Otherwise, the first "word" to be measured will be
+ // an empty string. Measuring or drawing a string of
+ // length 0 is interpreted as measure/draw the entire
+ // buffer, which certainly is not what we want here.
+ //
+ // This happes because a string may contain several
+ // spaces in a row, e.g. after a period.
+
+ while (*searchPointer == ' ')
+ searchPointer++;
+
+ measurePointer = searchPointer;
+ startPointer = searchPointer;
+ } else {
+ // Word will fit ok
+ w_total += w;
+ len_total += len;
+ wc++;
+ if (foundPointer == NULL) {
+ // Since word hit NULL but fit, we are done
+ textPoint2.x = textPoint.x - (w_total / 2);
+ textPoint2.y = textPoint.y;
+ draw(fontId, ds, startPointer, len_total, textPoint2, color,
+ effectColor, flags);
+ return;
+ }
+ searchPointer = measurePointer + 1;
+ }
+ }
+}
+
+Font::FontId Font::knownFont2FontIdx(KnownFont font) {
+ FontId fontId = kSmallFont;
+
+ if (_vm->getGameType() == GType_ITE) {
+ switch (font)
+ {
+ case (kKnownFontSmall):
+ fontId = kSmallFont;
+ break;
+ case (kKnownFontMedium):
+ fontId = kMediumFont;
+ break;
+ case (kKnownFontBig):
+ fontId = kBigFont;
+ break;
+
+ case (kKnownFontVerb):
+ fontId = kSmallFont;
+ break;
+ case (kKnownFontScript):
+ fontId = kMediumFont;
+ break;
+ case (kKnownFontPause):
+ fontId = _vm->_font->valid(kBigFont) ? kBigFont : kMediumFont;
+ break;
+ }
+ } else if (_vm->getGameType() == GType_IHNM) {
+ switch (font)
+ {
+ case (kKnownFontSmall):
+ fontId = kSmallFont;
+ break;
+ case (kKnownFontMedium):
+ fontId = kMediumFont;
+ break;
+ case (kKnownFontBig):
+ fontId = kBigFont;
+ break;
+
+ case (kKnownFontVerb):
+ fontId = kIHNMFont8;
+ break;
+ case (kKnownFontScript):
+ fontId = kIHNMMainFont;
+ break;
+ case (kKnownFontPause):
+ fontId = kMediumFont; // unchecked
+ break;
+ }
+ }
+ return fontId;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/font.h b/engines/saga/font.h
new file mode 100644
index 0000000000..f7f2113808
--- /dev/null
+++ b/engines/saga/font.h
@@ -0,0 +1,205 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Font management and font drawing header file
+
+#ifndef SAGA_FONT_H__
+#define SAGA_FONT_H__
+
+#include "saga/list.h"
+#include "saga/gfx.h"
+
+namespace Saga {
+
+#define FONT_SHOWUNDEFINED 1 // Define to draw undefined characters * as ?'s
+
+// The first defined character (!) is the only one that may
+// have a valid offset of '0'
+#define FONT_FIRSTCHAR 33
+
+#define FONT_CH_SPACE 32
+#define FONT_CH_QMARK 63
+
+// Minimum font header size without font data
+// (6 + 512 + 256 + 256 + 256 )
+#define FONT_DESCSIZE 1286
+
+#define FONT_CHARCOUNT 256
+#define FONT_CHARMASK 0xFFU
+
+#define SAGA_FONT_HEADER_LEN 6
+
+#define TEXT_CENTERLIMIT 50
+#define TEXT_MARGIN 10
+#define TEXT_LINESPACING 2
+
+enum FontEffectFlags {
+ kFontNormal = 0,
+ kFontOutline = 1 << 0,
+ kFontShadow = 1 << 1,
+ kFontBold = 1 << 2,
+ kFontCentered = 1 << 3,
+ kFontDontmap = 1 << 4
+};
+
+enum KnownFont {
+ kKnownFontSmall,
+ kKnownFontMedium,
+ kKnownFontBig,
+
+ kKnownFontPause,
+ kKnownFontScript,
+ kKnownFontVerb
+};
+
+struct TextListEntry {
+ bool display;
+ bool useRect;
+ Common::Point point;
+ Common::Rect rect;
+ KnownColor knownColor;
+ KnownColor effectKnownColor;
+ FontEffectFlags flags;
+ KnownFont font;
+ const char *text;
+ TextListEntry() {
+ memset(this, 0, sizeof(*this));
+ }
+};
+
+class TextList: public SortedList<TextListEntry> {
+public:
+
+ TextListEntry *addEntry(const TextListEntry &entry) {
+ return pushBack(entry).operator->();
+ }
+};
+
+struct FontHeader {
+ int charHeight;
+ int charWidth;
+ int rowLength;
+};
+
+struct FontCharEntry {
+ int index;
+ int byteWidth;
+ int width;
+ int flag;
+ int tracking;
+};
+
+struct FontStyle {
+ FontHeader header;
+ FontCharEntry fontCharEntry[256];
+ byte *font;
+};
+
+struct FontData {
+ FontStyle normal;
+ FontStyle outline;
+};
+
+class Font {
+ public:
+ Font(SagaEngine *vm);
+ ~Font(void);
+ int getStringWidth(KnownFont font, const char *text, size_t count, FontEffectFlags flags) {
+ return getStringWidth(knownFont2FontIdx(font), text, count, flags);
+ }
+ int getHeight(KnownFont font) {
+ return getHeight(knownFont2FontIdx(font));
+ }
+ int getHeight(KnownFont font, const char *text, int width, FontEffectFlags flags) {
+ return getHeight(knownFont2FontIdx(font), text, width, flags);
+ }
+ void textDraw(KnownFont font, Surface *ds, const char *string, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) {
+ textDraw(knownFont2FontIdx(font), ds, string, point, color, effectColor, flags);
+ }
+ void textDrawRect(KnownFont font, Surface *ds, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) {
+ textDrawRect(knownFont2FontIdx(font), ds, text, rect, color, effectColor, flags);
+ }
+
+ private:
+ enum FontId {
+ kSmallFont,
+ kMediumFont,
+ kBigFont,
+ kIHNMUnknown,
+ kIHNMFont8,
+ kIHNMUnknown2,
+ kIHNMMainFont
+ };
+
+ Font::FontId knownFont2FontIdx(KnownFont font);
+
+ int getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags);
+ int getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags);
+ void textDrawRect(FontId fontId, Surface *ds, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags);
+ void textDraw(FontId fontId, Surface *ds, const char *string, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
+
+ void loadFont(uint32 fontResourceId);
+ void createOutline(FontData *font);
+ void draw(FontId fontId, Surface *ds, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
+ void outFont(const FontStyle &drawFont, Surface *ds, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags);
+
+ FontData *getFont(FontId fontId) {
+ validate(fontId);
+ return _fonts[fontId];
+ }
+
+ int getHeight(FontId fontId) {
+ return getFont(fontId)->normal.header.charHeight;
+ }
+
+ void validate(FontId fontId) {
+ if (!valid(fontId)) {
+ error("Font::validate: Invalid font id.");
+ }
+ }
+ bool valid(FontId fontId) {
+ return ((fontId >= 0) && (fontId < _loadedFonts));
+ }
+ int getByteLen(int numBits) const {
+ int byteLength = numBits / 8;
+
+ if (numBits % 8) {
+ byteLength++;
+ }
+
+ return byteLength;
+ }
+
+ static const int _charMap[256];
+ SagaEngine *_vm;
+
+ bool _initialized;
+
+ int _loadedFonts;
+ FontData **_fonts;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/font_map.cpp b/engines/saga/font_map.cpp
new file mode 100644
index 0000000000..04e3400b2e
--- /dev/null
+++ b/engines/saga/font_map.cpp
@@ -0,0 +1,293 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Font module character mapping table ( MS CP-850 to ISO 8859-1 )
+
+// Translation table derived from http://www.kostis.net/charsets/
+
+#include "saga/saga.h"
+#include "saga/font.h"
+
+namespace Saga {
+
+const int Font::_charMap[256] = {
+ 0, // 0
+ 1, // 1
+ 2, // 2
+ 3, // 3
+ 4, // 4
+ 5, // 5
+ 6, // 6
+ 7, // 7
+ 8, // 8
+ 9, // 9
+ 10, // 10
+ 11, // 11
+ 12, // 12
+ 13, // 13
+ 14, // 14
+ 15, // 15
+ 16, // 16
+ 17, // 17
+ 18, // 18
+ 19, // 19
+ 20, // 20
+ 21, // 21
+ 22, // 22
+ 23, // 23
+ 24, // 24
+ 25, // 25
+ 26, // 26
+ 27, // 27
+ 28, // 28
+ 29, // 29
+ 30, // 30
+ 31, // 31
+ 32, // 32
+ 33, // 33
+ 34, // 34
+ 35, // 35
+ 36, // 36
+ 37, // 37
+ 38, // 38
+ 39, // 39
+ 40, // 40
+ 41, // 41
+ 42, // 42
+ 43, // 43
+ 44, // 44
+ 45, // 45
+ 46, // 46
+ 47, // 47
+ 48, // 48
+ 49, // 49
+ 50, // 50
+ 51, // 51
+ 52, // 52
+ 53, // 53
+ 54, // 54
+ 55, // 55
+ 56, // 56
+ 57, // 57
+ 58, // 58
+ 59, // 59
+ 60, // 60
+ 61, // 61
+ 62, // 62
+ 63, // 63
+ 64, // 64
+ 65, // 65
+ 66, // 66
+ 67, // 67
+ 68, // 68
+ 69, // 69
+ 70, // 70
+ 71, // 71
+ 72, // 72
+ 73, // 73
+ 74, // 74
+ 75, // 75
+ 76, // 76
+ 77, // 77
+ 78, // 78
+ 79, // 79
+ 80, // 80
+ 81, // 81
+ 82, // 82
+ 83, // 83
+ 84, // 84
+ 85, // 85
+ 86, // 86
+ 87, // 87
+ 88, // 88
+ 89, // 89
+ 90, // 90
+ 91, // 91
+ 92, // 92
+ 93, // 93
+ 94, // 94
+ 95, // 95
+ 96, // 96
+ 97, // 97
+ 98, // 98
+ 99, // 99
+ 100, // 100
+ 101, // 101
+ 102, // 102
+ 103, // 103
+ 104, // 104
+ 105, // 105
+ 106, // 106
+ 107, // 107
+ 108, // 108
+ 109, // 109
+ 110, // 110
+ 111, // 111
+ 112, // 112
+ 113, // 113
+ 114, // 114
+ 115, // 115
+ 116, // 116
+ 117, // 117
+ 118, // 118
+ 119, // 119
+ 120, // 120
+ 121, // 121
+ 122, // 122
+ 123, // 123
+ 124, // 124
+ 125, // 125
+ 126, // 126
+ 127, // 127
+ 199, // 128 LATIN CAPITAL LETTER C WITH CEDILLA
+ 252, // 129 LATIN SMALL LETTER U WITH DIAERESIS
+ 233, // 130 LATIN SMALL LETTER E WITH ACUTE
+ 226, // 131 LATIN SMALL LETTER A WITH CIRCUMFLEX
+ 228, // 132 LATIN SMALL LETTER A WITH DIAERESIS
+ 224, // 133 LATIN SMALL LETTER A WITH GRAVE
+ 229, // 134 LATIN SMALL LETTER A WITH RING ABOVE
+ 231, // 135 LATIN SMALL LETTER C WITH CEDILLA
+ 234, // 136 LATIN SMALL LETTER E WITH CIRCUMFLEX
+ 235, // 137 LATIN SMALL LETTER E WITH DIAERESIS
+ 232, // 138 LATIN SMALL LETTER E WITH GRAVE
+ 239, // 139 LATIN SMALL LETTER I WITH DIAERESIS
+ 238, // 140 LATIN SMALL LETTER I WITH CIRCUMFLEX
+ 236, // 141 LATIN SMALL LETTER I WITH GRAVE
+ 196, // 142 LATIN CAPITAL LETTER A WITH DIAERESIS
+ 197, // 143 LATIN CAPITAL LETTER A WITH RING ABOVE
+ 201, // 144 LATIN CAPITAL LETTER E WITH ACUTE
+ 230, // 145 LATIN SMALL LETTER AE
+ 198, // 146 LATIN CAPITAL LETTER AE
+ 244, // 147 LATIN SMALL LETTER O WITH CIRCUMFLEX
+ 246, // 148 LATIN SMALL LETTER O WITH DIAERESIS
+ 242, // 149 LATIN SMALL LETTER O WITH GRAVE
+ 251, // 150 LATIN SMALL LETTER U WITH CIRCUMFLEX
+ 249, // 151 LATIN SMALL LETTER U WITH GRAVE
+ 255, // 152 LATIN SMALL LETTER Y WITH DIAERESIS
+ 214, // 153 LATIN CAPITAL LETTER O WITH DIAERESIS
+ 220, // 154 LATIN CAPITAL LETTER U WITH DIAERESIS
+ 248, // 155 LATIN SMALL LETTER O WITH STROKE
+ 163, // 156 POUND SIGN
+ 216, // 157 LATIN CAPITAL LETTER O WITH STROKE
+ 215, // 158 MULTIPLICATION SIGN
+ 0, // 159 LATIN SMALL LETTER F WITH HOOK
+ 225, // 160 LATIN SMALL LETTER A WITH ACUTE
+ 237, // 161 LATIN SMALL LETTER I WITH ACUTE
+ 243, // 162 LATIN SMALL LETTER O WITH ACUTE
+ 250, // 163 LATIN SMALL LETTER U WITH ACUTE
+ 241, // 164 LATIN SMALL LETTER N WITH TILDE
+ 209, // 165 LATIN CAPITAL LETTER N WITH TILDE
+ 170, // 166 FEMININE ORDINAL INDICATOR
+ 186, // 167 MASCULINE ORDINAL INDICATOR
+ 191, // 168 INVERTED QUESTION MARK
+ 174, // 169 REGISTERED SIGN
+ 172, // 170 NOT SIGN
+ 189, // 171 VULGAR FRACTION ONE HALF
+ 188, // 172 VULGAR FRACTION ONE QUARTER
+ 161, // 173 INVERTED EXCLAMATION MARK
+ 171, // 174 LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ 187, // 175 RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ 0, // 176 LIGHT SHADE
+ 0, // 177 MEDIUM SHADE
+ 0, // 178 DARK SHADE
+ 0, // 179 BOX DRAWINGS LIGHT VERTICAL
+ 0, // 180 BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ 193, // 181 LATIN CAPITAL LETTER A WITH ACUTE
+ 194, // 182 LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+ 192, // 183 LATIN CAPITAL LETTER A WITH GRAVE
+ 169, // 184 COPYRIGHT SIGN
+ 0, // 185 BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+ 0, // 186 BOX DRAWINGS DOUBLE VERTICAL
+ 0, // 187 BOX DRAWINGS DOUBLE DOWN AND LEFT
+ 0, // 188 BOX DRAWINGS DOUBLE UP AND LEFT
+ 162, // 189 CENT SIGN
+ 165, // 190 YEN SIGN
+ 0, // 191 BOX DRAWINGS LIGHT DOWN AND LEFT
+ 0, // 192 BOX DRAWINGS LIGHT UP AND RIGHT
+ 0, // 193 BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ 0, // 194 BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ 0, // 195 BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ 0, // 196 BOX DRAWINGS LIGHT HORIZONTAL
+ 0, // 197 BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ 227, // 198 LATIN SMALL LETTER A WITH TILDE
+ 195, // 199 LATIN CAPITAL LETTER A WITH TILDE
+ 0, // 200 BOX DRAWINGS DOUBLE UP AND RIGHT
+ 0, // 201 BOX DRAWINGS DOUBLE DOWN AND RIGHT
+ 0, // 202 BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+ 0, // 203 BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+ 0, // 204 BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+ 0, // 205 BOX DRAWINGS DOUBLE HORIZONTAL
+ 0, // 206 BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+ 164, // 207 CURRENCY SIGN
+ 240, // 208 LATIN SMALL LETTER ETH
+ 208, // 209 LATIN CAPITAL LETTER ETH
+ 202, // 210 LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+ 203, // 211 LATIN CAPITAL LETTER E WITH DIAERESIS
+ 200, // 212 LATIN CAPITAL LETTER E WITH GRAVE
+ 305, // 213 LATIN SMALL LETTER DOTLESS I
+ 205, // 214 LATIN CAPITAL LETTER I WITH ACUTE
+ 206, // 215 LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+ 207, // 216 LATIN CAPITAL LETTER I WITH DIAERESIS
+ 0, // 217 BOX DRAWINGS LIGHT UP AND LEFT
+ 0, // 218 BOX DRAWINGS LIGHT DOWN AND RIGHT
+ 0, // 219 FULL BLOCK
+ 0, // 220 LOWER HALF BLOCK
+ 166, // 221 BROKEN BAR
+ 204, // 222 LATIN CAPITAL LETTER I WITH GRAVE
+ 0, // 223 UPPER HALF BLOCK
+ 211, // 224 LATIN CAPITAL LETTER O WITH ACUTE
+ 223, // 225 LATIN SMALL LETTER SHARP S
+ 212, // 226 LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+ 210, // 227 LATIN CAPITAL LETTER O WITH GRAVE
+ 245, // 228 LATIN SMALL LETTER O WITH TILDE
+ 213, // 229 LATIN CAPITAL LETTER O WITH TILDE
+ 181, // 230 MICRO SIGN
+ 254, // 231 LATIN SMALL LETTER THORN
+ 222, // 232 LATIN CAPITAL LETTER THORN
+ 218, // 233 LATIN CAPITAL LETTER U WITH ACUTE
+ 219, // 234 LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+ 217, // 235 LATIN CAPITAL LETTER U WITH GRAVE
+ 253, // 236 LATIN SMALL LETTER Y WITH ACUTE
+ 221, // 237 LATIN CAPITAL LETTER Y WITH ACUTE
+ 175, // 238 MACRON
+ 180, // 239 ACUTE ACCENT
+ 173, // 240 SOFT HYPHEN
+ 177, // 241 PLUS-MINUS SIGN
+ 0, // 242 DOUBLE LOW LINE
+ 190, // 243 VULGAR FRACTION THREE QUARTERS
+ 182, // 244 PILCROW SIGN
+ 167, // 245 SECTION SIGN
+ 247, // 246 DIVISION SIGN
+ 184, // 247 CEDILLA
+ 176, // 248 DEGREE SIGN
+ 168, // 249 DIAERESIS
+ 183, // 250 MIDDLE DOT
+ 185, // 251 SUPERSCRIPT ONE
+ 179, // 252 SUPERSCRIPT THREE
+ 178, // 253 SUPERSCRIPT TWO
+ 0, // 254 BLACK SQUARE
+ 160 // 255 NO-BREAK SPACE
+};
+
+} // End of namespace Saga
diff --git a/engines/saga/game.cpp b/engines/saga/game.cpp
new file mode 100644
index 0000000000..86526fe267
--- /dev/null
+++ b/engines/saga/game.cpp
@@ -0,0 +1,1790 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Game detection, general game parameters
+
+#include "saga/saga.h"
+
+#include "common/file.h"
+#include "common/md5.h"
+#include "common/map.h"
+#include "common/config-manager.h"
+#include "base/plugins.h"
+#include "base/gameDetector.h"
+#include "backends/fs/fs.h"
+
+#include "saga/rscfile.h"
+#include "saga/interface.h"
+#include "saga/scene.h"
+#include "saga/resnames.h"
+
+#define ITE_CONVERSE_MAX_TEXT_WIDTH (256 - 60)
+#define ITE_CONVERSE_TEXT_HEIGHT 10
+#define ITE_CONVERSE_TEXT_LINES 4
+
+//TODO: ihnm
+#define IHNM_CONVERSE_MAX_TEXT_WIDTH (256 - 60)
+#define IHNM_CONVERSE_TEXT_HEIGHT 10
+#define IHNM_CONVERSE_TEXT_LINES 10
+
+namespace Saga {
+
+static int detectGame(const FSList &fslist, bool mode = false, int start = -1);
+
+// ITE section
+static PanelButton ITE_MainPanelButtons[] = {
+ {kPanelButtonVerb, 52,4, 57,10, kVerbITEWalkTo,'w',0, 0,1,0},
+ {kPanelButtonVerb, 52,15, 57,10, kVerbITELookAt,'l',0, 2,3,0},
+ {kPanelButtonVerb, 52,26, 57,10, kVerbITEPickUp,'p',0, 4,5,0},
+ {kPanelButtonVerb, 52,37, 57,10, kVerbITETalkTo,'t',0, 0,1,0},
+ {kPanelButtonVerb, 110,4, 56,10, kVerbITEOpen,'o',0, 6,7,0},
+ {kPanelButtonVerb, 110,15, 56,10, kVerbITEClose,'c',0, 8,9,0},
+ {kPanelButtonVerb, 110,26, 56,10, kVerbITEUse,'u',0, 10,11,0},
+ {kPanelButtonVerb, 110,37, 56,10, kVerbITEGive,'g',0, 12,13,0},
+ {kPanelButtonArrow, 306,6, 8,5, -1,'U',0, 0,4,2},
+ {kPanelButtonArrow, 306,41, 8,5, 1,'D',0, 1,5,3},
+
+ {kPanelButtonInventory, 181 + 32*0,6, 27,18, 0,'-',0, 0,0,0},
+ {kPanelButtonInventory, 181 + 32*1,6, 27,18, 1,'-',0, 0,0,0},
+ {kPanelButtonInventory, 181 + 32*2,6, 27,18, 2,'-',0, 0,0,0},
+ {kPanelButtonInventory, 181 + 32*3,6, 27,18, 3,'-',0, 0,0,0},
+
+ {kPanelButtonInventory, 181 + 32*0,27, 27,18, 4,'-',0, 0,0,0},
+ {kPanelButtonInventory, 181 + 32*1,27, 27,18, 5,'-',0, 0,0,0},
+ {kPanelButtonInventory, 181 + 32*2,27, 27,18, 6,'-',0, 0,0,0},
+ {kPanelButtonInventory, 181 + 32*3,27, 27,18, 7,'-',0, 0,0,0}
+};
+
+static PanelButton ITE_ConversePanelButtons[] = {
+ {kPanelButtonConverseText, 52,6 + ITE_CONVERSE_TEXT_HEIGHT * 0, ITE_CONVERSE_MAX_TEXT_WIDTH,ITE_CONVERSE_TEXT_HEIGHT, 0,'1',0, 0,0,0},
+ {kPanelButtonConverseText, 52,6 + ITE_CONVERSE_TEXT_HEIGHT * 1, ITE_CONVERSE_MAX_TEXT_WIDTH,ITE_CONVERSE_TEXT_HEIGHT, 1,'2',0, 0,0,0},
+ {kPanelButtonConverseText, 52,6 + ITE_CONVERSE_TEXT_HEIGHT * 2, ITE_CONVERSE_MAX_TEXT_WIDTH,ITE_CONVERSE_TEXT_HEIGHT, 2,'3',0, 0,0,0},
+ {kPanelButtonConverseText, 52,6 + ITE_CONVERSE_TEXT_HEIGHT * 3, ITE_CONVERSE_MAX_TEXT_WIDTH,ITE_CONVERSE_TEXT_HEIGHT, 3,'4',0, 0,0,0},
+ {kPanelButtonArrow, 257,6, 9,6, -1,'u',0, 0,4,2},
+ {kPanelButtonArrow, 257,41, 9,6, 1,'d',0, 1,5,3},
+};
+
+static PanelButton ITE_OptionPanelButtons[] = {
+ {kPanelButtonOptionSlider, 284,19, 13,75, 0,'-',0, 0,0,0}, //slider-scroller
+ {kPanelButtonOption, 113,18, 45,17, kTextReadingSpeed,'r',0, 0,0,0}, //read speed
+ {kPanelButtonOption, 113,37, 45,17, kTextMusic,'m',0, 0,0,0}, //music
+ {kPanelButtonOption, 113,56, 45,17, kTextSound,'n',0, 0,0,0}, //sound-noise
+ {kPanelButtonOption, 13,79, 135,17, kTextQuitGame,'q',0, 0,0,0}, //quit
+ {kPanelButtonOption, 13,98, 135,17, kTextContinuePlaying,'c',0, 0,0,0}, //continue
+ {kPanelButtonOption, 164,98, 57,17, kTextLoad,'l',0, 0,0,0}, //load
+ {kPanelButtonOption, 241,98, 57,17, kTextSave,'s',0, 0,0,0}, //save
+ {kPanelButtonOptionSaveFiles, 166,20, 112,74, 0,'-',0, 0,0,0}, //savefiles
+
+ {kPanelButtonOptionText,106,4, 0,0, kTextGameOptions,'-',0, 0,0,0}, // text: game options
+ {kPanelButtonOptionText,11,22, 0,0, kTextReadingSpeed,'-',0, 0,0,0}, // text: read speed
+ {kPanelButtonOptionText,28,22, 0,0, kTextShowDialog,'-',0, 0,0,0}, // text: read speed
+ {kPanelButtonOptionText,73,41, 0,0, kTextMusic,'-',0, 0,0,0}, // text: music
+ {kPanelButtonOptionText,69,60, 0,0, kTextSound,'-',0, 0,0,0}, // text: noise
+};
+
+static PanelButton ITE_QuitPanelButtons[] = {
+ {kPanelButtonQuit, 11,17, 60,16, kTextQuit,'q',0, 0,0,0},
+ {kPanelButtonQuit, 121,17, 60,16, kTextCancel,'c',0, 0,0,0},
+ {kPanelButtonQuitText, -1,5, 0,0, kTextQuitTheGameQuestion,'-',0, 0,0,0},
+};
+
+static PanelButton ITE_LoadPanelButtons[] = {
+ {kPanelButtonLoad, 101,19, 60,16, kTextOK,'o',0, 0,0,0},
+ {kPanelButtonLoadText, -1,5, 0,0, kTextLoadSuccessful,'-',0, 0,0,0},
+};
+
+static PanelButton ITE_SavePanelButtons[] = {
+ {kPanelButtonSave, 11,37, 60,16, kTextSave,'s',0, 0,0,0},
+ {kPanelButtonSave, 101,37, 60,16, kTextCancel,'c',0, 0,0,0},
+ {kPanelButtonSaveEdit, 26,17, 119,17, 0,'-',0, 0,0,0},
+ {kPanelButtonSaveText, -1,5, 0,0, kTextEnterSaveGameName,'-',0, 0,0,0},
+};
+
+static PanelButton ITE_ProtectPanelButtons[] = {
+ {kPanelButtonProtectEdit, 26,17, 119,17, 0,'-',0, 0,0,0},
+ {kPanelButtonProtectText, -1,5, 0,0, kTextEnterProtectAnswer,'-',0, 0,0,0},
+};
+
+/*
+static PanelButton ITE_ProtectionPanelButtons[] = {
+ {kPanelButtonArrow, 0,0, 0,0, 0,'-',0, 0,0,0}, //TODO
+};*/
+
+static GameDisplayInfo ITE_DisplayInfo = {
+ 320, 200, // logical width&height
+
+ 35, // scene path y offset
+ 137, // scene height
+
+ 0, // status x offset
+ 137, // status y offset
+ 320, // status width
+ 11, // status height
+ 2, // status text y offset
+ 186, // status text color
+ 15, // status BG color
+ 308,137, // save reminder pos
+ 12,12, // save reminder w & h
+ 6,7, // save reminder sprite numbers
+
+ 5, 4, // left portrait x, y offset
+ 274, 4, // right portrait x, y offset
+
+ 8, 9, // inventory Up & Down button indexies
+ 2, 4, // inventory rows, columns
+
+ 0, 148, // main panel offsets
+ ARRAYSIZE(ITE_MainPanelButtons),
+ ITE_MainPanelButtons,
+
+ ITE_CONVERSE_MAX_TEXT_WIDTH,
+ ITE_CONVERSE_TEXT_HEIGHT,
+ ITE_CONVERSE_TEXT_LINES,
+ 4, 5, // converse Up & Down button indexies
+ 0, 148, // converse panel offsets
+ ARRAYSIZE(ITE_ConversePanelButtons),
+ ITE_ConversePanelButtons,
+
+ 8, 0, // save file index
+ 8, // optionSaveFileVisible
+ 8, 8, // option panel offsets
+ ARRAYSIZE(ITE_OptionPanelButtons),
+ ITE_OptionPanelButtons,
+
+ 64,54, // quit panel offsets
+ 192,38, // quit panel width & height
+ ARRAYSIZE(ITE_QuitPanelButtons),
+ ITE_QuitPanelButtons,
+
+ 74, 53, // load panel offsets
+ 172, 40, // load panel width & height
+ ARRAYSIZE(ITE_LoadPanelButtons),
+ ITE_LoadPanelButtons,
+
+ 2, // save edit index
+ 74, 44, // save panel offsets
+ 172, 58, // save panel width & height
+ ARRAYSIZE(ITE_SavePanelButtons),
+ ITE_SavePanelButtons,
+
+ 0, // protect edit index
+ 74, 44, // protect panel offsets
+ 172, 58, // protect panel width & height
+ ARRAYSIZE(ITE_ProtectPanelButtons),
+ ITE_ProtectPanelButtons
+};
+
+static GameResourceDescription ITE_Resources = {
+ RID_ITE_SCENE_LUT, // Scene lookup table RN
+ RID_ITE_SCRIPT_LUT, // Script lookup table RN
+ RID_ITE_MAIN_PANEL,
+ RID_ITE_CONVERSE_PANEL,
+ RID_ITE_OPTION_PANEL,
+ RID_ITE_MAIN_SPRITES,
+ RID_ITE_MAIN_PANEL_SPRITES,
+ RID_ITE_DEFAULT_PORTRAITS,
+ RID_ITE_MAIN_STRINGS,
+ RID_ITE_ACTOR_NAMES
+};
+
+static GameResourceDescription ITEDemo_Resources = {
+ RID_ITEDEMO_SCENE_LUT, // Scene lookup table RN
+ RID_ITEDEMO_SCRIPT_LUT, // Script lookup table RN
+ RID_ITEDEMO_MAIN_PANEL,
+ RID_ITEDEMO_CONVERSE_PANEL,
+ RID_ITEDEMO_OPTION_PANEL,
+ RID_ITEDEMO_MAIN_SPRITES,
+ RID_ITEDEMO_MAIN_PANEL_SPRITES,
+ RID_ITEDEMO_DEFAULT_PORTRAITS,
+ RID_ITEDEMO_MAIN_STRINGS,
+ RID_ITEDEMO_ACTOR_NAMES
+};
+
+// Inherit the Earth - DOS Demo version
+static GameFileDescription ITEDEMO_GameFiles[] = {
+ {"ite.rsc", GAME_RESOURCEFILE},
+ //{"ite.dmo", GAME_DEMOFILE},
+ {"scripts.rsc", GAME_SCRIPTFILE},
+ {"voices.rsc", GAME_SOUNDFILE | GAME_VOICEFILE}
+};
+
+static GameFontDescription ITEDEMO_GameFonts[] = {
+ {0},
+ {1}
+};
+
+static GameSoundInfo ITEDEMO_GameSound = {
+ kSoundVOC,
+ -1,
+ -1,
+ false,
+ false,
+ true
+};
+
+// Inherit the Earth - Wyrmkeep Win32 Demo version
+static GameFileDescription ITEWINDEMO_GameFiles[] = {
+ {"ited.rsc", GAME_RESOURCEFILE},
+ {"scriptsd.rsc", GAME_SCRIPTFILE},
+ {"soundsd.rsc", GAME_SOUNDFILE},
+ {"voicesd.rsc", GAME_VOICEFILE}
+};
+
+static GameFontDescription ITEWINDEMO_GameFonts[] = {
+ {2},
+ {0}
+};
+
+static GameSoundInfo ITEWINDEMO1_GameSound = {
+ kSoundPCM,
+ 22050,
+ 8,
+ false,
+ false,
+ false
+};
+
+static GameSoundInfo ITEWINDEMO2_GameVoice = {
+ kSoundVOX,
+ 22050,
+ 16,
+ false,
+ false,
+ true
+};
+
+static GameSoundInfo ITEWINDEMO2_GameSound = {
+ kSoundPCM,
+ 22050,
+ 16,
+ false,
+ false,
+ true
+};
+
+// Inherit the Earth - Wyrmkeep Mac Demo version
+static GameFileDescription ITEMACDEMO_GameFiles[] = {
+ {"ited.rsc", GAME_RESOURCEFILE},
+ {"scriptsd.rsc", GAME_SCRIPTFILE},
+ {"soundsd.rsc", GAME_SOUNDFILE},
+ {"voicesd.rsc", GAME_VOICEFILE},
+ {"musicd.rsc", GAME_MUSICFILE}
+};
+
+static GameSoundInfo ITEMACDEMO_GameVoice = {
+ kSoundVOX,
+ 22050,
+ 16,
+ false,
+ false,
+ true
+};
+
+static GameSoundInfo ITEMACDEMO_GameSound = {
+ kSoundPCM,
+ 22050,
+ 16,
+ false,
+ true,
+ true
+};
+
+static GameSoundInfo ITEMACDEMO_GameMusic = {
+ kSoundPCM,
+ 11025,
+ 16,
+ false,
+ false,
+ true
+};
+
+// Inherit the Earth - Wyrmkeep Linux Demo version
+static GameFileDescription ITELINDEMO_GameFiles[] = {
+ {"ited.rsc", GAME_RESOURCEFILE},
+ {"scriptsd.rsc", GAME_SCRIPTFILE},
+ {"soundsd.rsc", GAME_SOUNDFILE},
+ {"voicesd.rsc", GAME_VOICEFILE},
+ {"musicd.rsc", GAME_MUSICFILE}
+};
+
+static GameSoundInfo ITELINDEMO_GameMusic = {
+ kSoundPCM,
+ 11025,
+ 16,
+ true,
+ false,
+ true
+};
+
+// Inherit the Earth - Wyrmkeep Linux version
+static GameFileDescription ITELINCD_GameFiles[] = {
+ {"ite.rsc", GAME_RESOURCEFILE},
+ {"scripts.rsc", GAME_SCRIPTFILE},
+ {"sounds.rsc", GAME_SOUNDFILE},
+ {"voices.rsc", GAME_VOICEFILE},
+ {"music.rsc", GAME_MUSICFILE}
+};
+
+// Inherit the Earth - Wyrmkeep combined Windows/Mac/Linux version. This
+// version is different from the other Wyrmkeep re-releases in that it does
+// not have any substitute files. Presumably the ite.rsc file has been
+// modified to include the Wyrmkeep changes. The resource files are little-
+// endian, except for the voice file which is big-endian.
+
+static GameFileDescription ITEMULTICD_GameFiles[] = {
+ {"ite.rsc", GAME_RESOURCEFILE},
+ {"scripts.rsc", GAME_SCRIPTFILE},
+ {"sounds.rsc", GAME_SOUNDFILE},
+ {"Inherit the Earth Voices", GAME_VOICEFILE | GAME_SWAPENDIAN},
+ {"music.rsc", GAME_MUSICFILE}
+};
+
+static GameFileDescription ITEMACCD_G_GameFiles[] = {
+ {"ITE Resources.bin", GAME_RESOURCEFILE | GAME_MACBINARY},
+ {"ITE Scripts.bin", GAME_SCRIPTFILE | GAME_MACBINARY},
+ {"ITE Sounds.bin", GAME_SOUNDFILE | GAME_MACBINARY},
+ {"ITE Music.bin", GAME_MUSICFILE_GM | GAME_MACBINARY},
+ {"ITE Voices.bin", GAME_VOICEFILE | GAME_MACBINARY}
+};
+
+static GameSoundInfo ITEMACCD_G_GameSound = {
+ kSoundMacPCM,
+ 22050,
+ 8,
+ false,
+ false,
+ false
+};
+
+// Inherit the Earth - Mac Wyrmkeep version
+static GameFileDescription ITEMACCD_GameFiles[] = {
+ {"ite.rsc", GAME_RESOURCEFILE},
+ {"scripts.rsc", GAME_SCRIPTFILE},
+ {"sounds.rsc", GAME_SOUNDFILE},
+ {"Inherit the Earth Voices", GAME_VOICEFILE},
+ {"music.rsc", GAME_MUSICFILE}
+};
+
+static GameSoundInfo ITEMACCD_GameSound = {
+ kSoundPCM,
+ 22050,
+ 16,
+ false,
+ true,
+ true
+};
+
+static GameSoundInfo ITEMACCD_GameMusic = {
+ kSoundPCM,
+ 11025,
+ 16,
+ true,
+ false,
+ true
+};
+
+// Inherit the Earth - Diskette version
+static GameFileDescription ITEDISK_GameFiles[] = {
+ {"ite.rsc", GAME_RESOURCEFILE},
+ {"scripts.rsc", GAME_SCRIPTFILE},
+ {"voices.rsc", GAME_SOUNDFILE | GAME_VOICEFILE}
+};
+
+static GameFileDescription ITEDISK2_GameFiles[] = {
+ {"ite.rsc", GAME_RESOURCEFILE},
+ {"scripts.rsc", GAME_SCRIPTFILE},
+ {"voices.rsc", GAME_SOUNDFILE | GAME_VOICEFILE},
+ {"music.rsc", GAME_MUSICFILE}
+};
+
+static GameFontDescription ITEDISK_GameFonts[] = {
+ {2},
+ {0},
+ {1}
+};
+
+static GameSoundInfo ITEDISK_GameSound = {
+ kSoundVOC,
+ -1,
+ -1,
+ false,
+ false,
+ true
+};
+
+// Inherit the Earth - CD Enhanced version
+static GameFileDescription ITECD_GameFiles[] = {
+ {"ite.rsc", GAME_RESOURCEFILE},
+ {"scripts.rsc", GAME_SCRIPTFILE},
+ {"sounds.rsc", GAME_SOUNDFILE},
+ {"voices.rsc", GAME_VOICEFILE}
+};
+
+static GameFileDescription ITECD2_GameFiles[] = {
+ {"ite.rsc", GAME_RESOURCEFILE},
+ {"scripts.rsc", GAME_SCRIPTFILE},
+ {"sounds.rsc", GAME_SOUNDFILE},
+ {"voices.rsc", GAME_VOICEFILE},
+ {"music.rsc", GAME_MUSICFILE}
+};
+
+static GameFontDescription ITECD_GameFonts[] = {
+ {2},
+ {0},
+ {1}
+};
+
+static GameSoundInfo ITECD_GameSound = {
+ kSoundPCM,
+ 22050,
+ 16,
+ false,
+ false,
+ true
+};
+
+static GamePatchDescription ITEWinPatch1_Files[] = {
+ { "cave.mid", GAME_RESOURCEFILE, 9, NULL},
+ { "intro.mid", GAME_RESOURCEFILE, 10, NULL},
+ { "fvillage.mid", GAME_RESOURCEFILE, 11, NULL},
+ { "elkhall.mid", GAME_RESOURCEFILE, 12, NULL},
+ { "mouse.mid", GAME_RESOURCEFILE, 13, NULL},
+ { "darkclaw.mid", GAME_RESOURCEFILE, 14, NULL},
+ { "birdchrp.mid", GAME_RESOURCEFILE, 15, NULL},
+ { "orbtempl.mid", GAME_RESOURCEFILE, 16, NULL},
+ { "spooky.mid", GAME_RESOURCEFILE, 17, NULL},
+ { "catfest.mid", GAME_RESOURCEFILE, 18, NULL},
+ { "elkfanfare.mid", GAME_RESOURCEFILE, 19, NULL},
+ { "bcexpl.mid", GAME_RESOURCEFILE, 20, NULL},
+ { "boargtnt.mid", GAME_RESOURCEFILE, 21, NULL},
+ { "boarking.mid", GAME_RESOURCEFILE, 22, NULL},
+ { "explorea.mid", GAME_RESOURCEFILE, 23, NULL},
+ { "exploreb.mid", GAME_RESOURCEFILE, 24, NULL},
+ { "explorec.mid", GAME_RESOURCEFILE, 25, NULL},
+ { "sunstatm.mid", GAME_RESOURCEFILE, 26, NULL},
+ { "nitstrlm.mid", GAME_RESOURCEFILE, 27, NULL},
+ { "humruinm.mid", GAME_RESOURCEFILE, 28, NULL},
+ { "damexplm.mid", GAME_RESOURCEFILE, 29, NULL},
+ { "tychom.mid", GAME_RESOURCEFILE, 30, NULL},
+ { "kitten.mid", GAME_RESOURCEFILE, 31, NULL},
+ { "sweet.mid", GAME_RESOURCEFILE, 32, NULL},
+ { "brutalmt.mid", GAME_RESOURCEFILE, 33, NULL},
+ { "shiala.mid", GAME_RESOURCEFILE, 34, NULL},
+
+ { "wyrm.pak", GAME_RESOURCEFILE, 1529, NULL},
+ { "wyrm1.dlt", GAME_RESOURCEFILE, 1530, NULL},
+ { "wyrm2.dlt", GAME_RESOURCEFILE, 1531, NULL},
+ { "wyrm3.dlt", GAME_RESOURCEFILE, 1532, NULL},
+ { "wyrm4.dlt", GAME_RESOURCEFILE, 1533, NULL},
+ { "credit3n.dlt", GAME_RESOURCEFILE, 1796, NULL},
+ { "credit4n.dlt", GAME_RESOURCEFILE, 1797, NULL},
+ { "p2_a.voc", GAME_VOICEFILE, 4, NULL}
+};
+
+static GamePatchDescription ITEWinPatch2_Files[] = {
+ { "cave.mid", GAME_RESOURCEFILE, 9, NULL},
+ { "intro.mid", GAME_RESOURCEFILE, 10, NULL},
+ { "fvillage.mid", GAME_RESOURCEFILE, 11, NULL},
+ { "elkfanfare.mid", GAME_RESOURCEFILE, 19, NULL},
+ { "bcexpl.mid", GAME_RESOURCEFILE, 20, NULL},
+ { "boargtnt.mid", GAME_RESOURCEFILE, 21, NULL},
+ { "explorea.mid", GAME_RESOURCEFILE, 23, NULL},
+ { "sweet.mid", GAME_RESOURCEFILE, 32, NULL},
+
+ { "wyrm.pak", GAME_RESOURCEFILE, 1529, NULL},
+ { "wyrm1.dlt", GAME_RESOURCEFILE, 1530, NULL},
+ { "wyrm2.dlt", GAME_RESOURCEFILE, 1531, NULL},
+ { "wyrm3.dlt", GAME_RESOURCEFILE, 1532, NULL},
+ { "p2_a.iaf", GAME_VOICEFILE, 4, &ITECD_GameSound}
+/* boarhall.bbm
+ elkenter.bbm
+ ferrets.bbm
+ ratdoor.bbm
+ sanctuar.bbm
+ tycho.bbm*/
+};
+
+static GamePatchDescription ITEMacPatch_Files[] = {
+ { "wyrm.pak", GAME_RESOURCEFILE, 1529, NULL},
+ { "wyrm1.dlt", GAME_RESOURCEFILE, 1530, NULL},
+ { "wyrm2.dlt", GAME_RESOURCEFILE, 1531, NULL},
+ { "wyrm3.dlt", GAME_RESOURCEFILE, 1532, NULL},
+ { "wyrm4.dlt", GAME_RESOURCEFILE, 1533, NULL},
+ { "credit3m.dlt", GAME_RESOURCEFILE, 1796, NULL},
+ { "credit4m.dlt", GAME_RESOURCEFILE, 1797, NULL},
+ { "p2_a.iaf", GAME_VOICEFILE, 4, &ITEMACCD_GameSound}
+};
+
+static GamePatchDescription ITELinPatch_Files[] = {
+ { "wyrm.pak", GAME_RESOURCEFILE, 1529, NULL},
+ { "wyrm1.dlt", GAME_RESOURCEFILE, 1530, NULL},
+ { "wyrm2.dlt", GAME_RESOURCEFILE, 1531, NULL},
+ { "wyrm3.dlt", GAME_RESOURCEFILE, 1532, NULL},
+ { "credit3n.dlt", GAME_RESOURCEFILE, 1796, NULL},
+ { "credit4n.dlt", GAME_RESOURCEFILE, 1797, NULL},
+ { "P2_A.iaf", GAME_VOICEFILE, 4, &ITECD_GameSound}
+};
+
+// IHNM section
+
+static PanelButton IHNM_MainPanelButtons[] = {
+ {kPanelButtonVerb, 106,12, 114,30, kVerbIHNMWalk,'w',0, 0,1,0},
+ {kPanelButtonVerb, 106,44, 114,30, kVerbIHNMLookAt,'l',0, 2,3,0},
+ {kPanelButtonVerb, 106,76, 114,30, kVerbIHNMTake,'k',0, 4,5,0},
+ {kPanelButtonVerb, 106,108, 114,30, kVerbIHNMUse,'u',0, 6,7,0},
+ {kPanelButtonVerb, 223,12, 114,30, kVerbIHNMTalkTo,'t',0, 8,9,0},
+ {kPanelButtonVerb, 223,44, 114,30, kVerbIHNMSwallow,'s',0, 10,11,0},
+ {kPanelButtonVerb, 223,76, 114,30, kVerbIHNMGive,'g',0, 12,13,0},
+ {kPanelButtonVerb, 223,108, 114,30, kVerbIHNMPush,'p',0, 14,15,0},
+ {kPanelButtonArrow, 606,22, 20,25, -1,'[',0, 0,0,0}, //TODO: arrow Sprite Numbers
+ {kPanelButtonArrow, 606,108, 20,25, 1,']',0, 0,0,0},
+
+ {kPanelButtonInventory, 357 + 64*0,18, 54,54, 0,'-',0, 0,0,0},
+ {kPanelButtonInventory, 357 + 64*1,18, 54,54, 1,'-',0, 0,0,0},
+ {kPanelButtonInventory, 357 + 64*2,18, 54,54, 2,'-',0, 0,0,0},
+ {kPanelButtonInventory, 357 + 64*3,18, 54,54, 3,'-',0, 0,0,0},
+
+ {kPanelButtonInventory, 357 + 64*0,80, 54,54, 4,'-',0, 0,0,0},
+ {kPanelButtonInventory, 357 + 64*1,80, 54,54, 5,'-',0, 0,0,0},
+ {kPanelButtonInventory, 357 + 64*2,80, 54,54, 6,'-',0, 0,0,0},
+ {kPanelButtonInventory, 357 + 64*3,80, 54,54, 7,'-',0, 0,0,0}
+};
+
+static PanelButton IHNM_ConversePanelButtons[] = {
+ {kPanelButtonConverseText, 117,18 + IHNM_CONVERSE_TEXT_HEIGHT * 0, IHNM_CONVERSE_MAX_TEXT_WIDTH,IHNM_CONVERSE_TEXT_HEIGHT, 0,'1',0, 0,0,0},
+ {kPanelButtonConverseText, 52,18 + IHNM_CONVERSE_TEXT_HEIGHT * 1, IHNM_CONVERSE_MAX_TEXT_WIDTH,IHNM_CONVERSE_TEXT_HEIGHT, 1,'2',0, 0,0,0},
+ {kPanelButtonConverseText, 52,18 + IHNM_CONVERSE_TEXT_HEIGHT * 2, IHNM_CONVERSE_MAX_TEXT_WIDTH,IHNM_CONVERSE_TEXT_HEIGHT, 2,'3',0, 0,0,0},
+ {kPanelButtonConverseText, 52,18 + IHNM_CONVERSE_TEXT_HEIGHT * 3, IHNM_CONVERSE_MAX_TEXT_WIDTH,IHNM_CONVERSE_TEXT_HEIGHT, 3,'4',0, 0,0,0},
+ //.....
+ {kPanelButtonArrow, 606,22, 20,25, -1,'[',0, 0,0,0}, //TODO: arrow Sprite Numbers
+ {kPanelButtonArrow, 606,108, 20,25, 1,']',0, 0,0,0}
+};
+
+static PanelButton IHNM_OptionPanelButtons[] = {
+ {kPanelButtonArrow, 0,0, 0,0, 0,'-',0, 0,0,0}, //TODO
+};
+
+static PanelButton IHNM_QuitPanelButtons[] = {
+ {kPanelButtonArrow, 0,0, 0,0, 0,'-',0, 0,0,0}, //TODO
+};
+
+static PanelButton IHNM_LoadPanelButtons[] = {
+ {kPanelButtonArrow, 0,0, 0,0, 0,'-',0, 0,0,0}, //TODO
+};
+
+static PanelButton IHNM_SavePanelButtons[] = {
+ {kPanelButtonArrow, 0,0, 0,0, 0,'-',0, 0,0,0}, //TODO
+};
+
+
+static GameDisplayInfo IHNM_DisplayInfo = { //TODO: fill it all
+ 640, 480, // logical width&height
+
+ 0, // scene path y offset
+ 304, // scene height
+
+ 0, // status x offset
+ 304, // status y offset
+ 616, // status width
+ 24, // status height
+ 8, // status text y offset
+ 253, // status text color
+ 250, // status BG color
+ 616, 303, // save reminder pos
+ 24, 24, // save reminder w&h
+ 0,1, // save reminder sprite numbers
+
+ 11, 12, // left portrait x, y offset
+ -1, -1, // right portrait x, y offset
+
+ -1, -1, // inventory Up & Down button indexies
+ 2, 4, // inventory rows, columns
+
+ 0, 328, // main panel offsets
+ ARRAYSIZE(IHNM_MainPanelButtons),
+ IHNM_MainPanelButtons,
+
+ -1, -1, // converse Up & Down button indexies
+
+ IHNM_CONVERSE_MAX_TEXT_WIDTH,
+ IHNM_CONVERSE_TEXT_HEIGHT,
+ IHNM_CONVERSE_TEXT_LINES,
+ 0, 328, // converse panel offsets
+ ARRAYSIZE(IHNM_ConversePanelButtons),
+ IHNM_ConversePanelButtons,
+
+ -1, -1, // save file index
+ 0, // optionSaveFileVisible
+ 0, 0, // option panel offsets
+ ARRAYSIZE(IHNM_OptionPanelButtons),
+ IHNM_OptionPanelButtons,
+
+ 0,0, // quit panel offsets
+ 0,0, // quit panel width & height
+ ARRAYSIZE(IHNM_QuitPanelButtons),
+ IHNM_QuitPanelButtons,
+
+ 0, 0, // load panel offsets
+ 0, 0, // load panel width & height
+ ARRAYSIZE(IHNM_LoadPanelButtons),
+ IHNM_LoadPanelButtons,
+
+ -1, // save edit index
+ 0, 0, // save panel offsets
+ 0, 0, // save panel width & height
+ ARRAYSIZE(IHNM_SavePanelButtons),
+ IHNM_SavePanelButtons,
+
+ // No protection panel in IHNM
+ -1, // protect edit index
+ 0, 0, // protect panel offsets
+ 0, 0, // protect panel width & height
+ ARRAYSIZE(IHNM_SavePanelButtons),
+ IHNM_SavePanelButtons
+};
+
+static GameResourceDescription IHNM_Resources = {
+ RID_IHNM_SCENE_LUT, // Scene lookup table RN
+ RID_IHNM_SCRIPT_LUT, // Script lookup table RN
+ RID_IHNM_MAIN_PANEL,
+ RID_IHNM_CONVERSE_PANEL,
+ RID_IHNM_OPTION_PANEL,
+ RID_IHNM_MAIN_SPRITES,
+ RID_IHNM_MAIN_PANEL_SPRITES,
+ 0,
+ RID_IHNM_MAIN_STRINGS,
+ 0
+};
+
+// I Have No Mouth and I Must Scream - Demo version
+static GameFileDescription IHNMDEMO_GameFiles[] = {
+ {"scream.res", GAME_RESOURCEFILE},
+ {"scripts.res", GAME_SCRIPTFILE},
+ {"sfx.res", GAME_SOUNDFILE},
+ {"voicesd.res", GAME_VOICEFILE}
+};
+
+// I Have No Mouth and I Must Scream - Retail CD version
+static GameFileDescription IHNMCD_GameFiles[] = {
+ {"musicfm.res", GAME_MUSICFILE_FM},
+ {"musicgm.res", GAME_MUSICFILE_GM},
+ {"scream.res", GAME_RESOURCEFILE},
+ {"patch.re_", GAME_PATCHFILE | GAME_RESOURCEFILE},
+ {"scripts.res", GAME_SCRIPTFILE},
+ {"sfx.res", GAME_SOUNDFILE},
+ {"voicess.res", GAME_VOICEFILE}, //order of voice bank file is important
+ {"voices1.res", GAME_VOICEFILE},
+ {"voices2.res", GAME_VOICEFILE},
+ {"voices3.res", GAME_VOICEFILE},
+ {"voices4.res", GAME_VOICEFILE},
+ {"voices5.res", GAME_VOICEFILE},
+ {"voices6.res", GAME_VOICEFILE}
+};
+
+// I Have No Mouth and I Must Scream - Censored CD version (without Nimdok)
+static GameFileDescription IHNMCD_Censored_GameFiles[] = {
+ {"musicfm.res", GAME_MUSICFILE_FM},
+ {"musicgm.res", GAME_MUSICFILE_GM},
+ {"scream.res", GAME_RESOURCEFILE},
+ {"scripts.res", GAME_SCRIPTFILE},
+ {"patch.re_", GAME_PATCHFILE | GAME_RESOURCEFILE},
+ {"sfx.res", GAME_SOUNDFILE},
+ {"voicess.res", GAME_VOICEFILE}, //order of voice bank file is important
+ {"voices1.res", GAME_VOICEFILE},
+ {"voices2.res", GAME_VOICEFILE},
+ {"voices3.res", GAME_VOICEFILE},
+ {"voices5.res", GAME_VOICEFILE},
+ {"voices6.res", GAME_VOICEFILE}
+};
+
+static GameFontDescription IHNMDEMO_GameFonts[] = {
+ {2},
+ {3},
+ {4}
+};
+
+static GameFontDescription IHNMCD_GameFonts[] = {
+ {2},
+ {3},
+ {4},
+ {5},
+ {6}, // kIHNMFont8
+ {7},
+ {8} // kIHNMMainFont
+};
+
+static GameSoundInfo IHNM_GameSound = {
+ kSoundWAV,
+ -1,
+ -1,
+ false,
+ false,
+ true
+};
+
+struct GameMD5 {
+ GameIds id;
+ const char *md5;
+ const char *filename;
+ bool caseSensitive;
+};
+
+#define FILE_MD5_BYTES 5000
+
+static GameMD5 gameMD5[] = {
+ { GID_ITE_DISK_G, "8f4315a9bb10ec839253108a032c8b54", "ite.rsc", false },
+ { GID_ITE_DISK_G, "516f7330f8410057b834424ea719d1ef", "scripts.rsc", false },
+ { GID_ITE_DISK_G, "c46e4392fcd2e89bc91e5567db33b62d", "voices.rsc", false },
+
+ { GID_ITE_DISK_G2, "8f4315a9bb10ec839253108a032c8b54", "ite.rsc", false },
+ { GID_ITE_DISK_G2, "516f7330f8410057b834424ea719d1ef", "scripts.rsc", false },
+ { GID_ITE_DISK_G2, "c46e4392fcd2e89bc91e5567db33b62d", "voices.rsc", false },
+ { GID_ITE_DISK_G2, "d6454756517f042f01210458abe8edd4", "music.rsc", false },
+
+ { GID_ITE_CD_G, "8f4315a9bb10ec839253108a032c8b54", "ite.rsc", false },
+ { GID_ITE_CD_G, "50a0d2d7003c926a3832d503c8534e90", "scripts.rsc", false },
+ { GID_ITE_CD_G, "e2ccb61c325d6d1ead3be0e731fe29fe", "sounds.rsc", false },
+ { GID_ITE_CD_G, "41bb6b95d792dde5196bdb78740895a6", "voices.rsc", false },
+
+ { GID_ITE_CD_G2, "8f4315a9bb10ec839253108a032c8b54", "ite.rsc", false },
+ { GID_ITE_CD_G2, "50a0d2d7003c926a3832d503c8534e90", "scripts.rsc", false },
+ { GID_ITE_CD_G2, "e2ccb61c325d6d1ead3be0e731fe29fe", "sounds.rsc", false },
+ { GID_ITE_CD_G2, "41bb6b95d792dde5196bdb78740895a6", "voices.rsc", false },
+ { GID_ITE_CD_G2, "d6454756517f042f01210458abe8edd4", "music.rsc", false },
+
+ { GID_ITE_CD, "8f4315a9bb10ec839253108a032c8b54", "ite.rsc", false },
+ { GID_ITE_CD, "a891405405edefc69c9d6c420c868b84", "scripts.rsc", false },
+ { GID_ITE_CD, "e2ccb61c325d6d1ead3be0e731fe29fe", "sounds.rsc", false },
+ { GID_ITE_CD, "41bb6b95d792dde5196bdb78740895a6", "voices.rsc", false },
+
+ // reported by mld. Bestsellergamers cover disk
+ { GID_ITE_CD_DE, "869fc23c8f38f575979ec67152914fee", "ite.rsc", false },
+ { GID_ITE_CD_DE, "a891405405edefc69c9d6c420c868b84", "scripts.rsc", false },
+ { GID_ITE_CD_DE, "e2ccb61c325d6d1ead3be0e731fe29fe", "sounds.rsc", false },
+ { GID_ITE_CD_DE, "2fbad5d10b9b60a3415dc4aebbb11718", "voices.rsc", false },
+
+ { GID_ITE_CD_DE2, "869fc23c8f38f575979ec67152914fee", "ite.rsc", false },
+ { GID_ITE_CD_DE2, "a891405405edefc69c9d6c420c868b84", "scripts.rsc", false },
+ { GID_ITE_CD_DE2, "e2ccb61c325d6d1ead3be0e731fe29fe", "sounds.rsc", false },
+ { GID_ITE_CD_DE2, "2fbad5d10b9b60a3415dc4aebbb11718", "voices.rsc", false },
+ { GID_ITE_CD_DE2, "d6454756517f042f01210458abe8edd4", "music.rsc", false },
+
+ { GID_ITE_DEMO_G, "986c79c4d2939dbe555576529fd37932", "ite.rsc", false },
+ { GID_ITE_DEMO_G, "d5697dd3240a3ceaddaa986c47e1a2d7", "scripts.rsc", false },
+ { GID_ITE_DEMO_G, "c58e67c506af4ffa03fd0aac2079deb0", "voices.rsc", false },
+ { GID_ITE_DEMO_G, "0b9a70eb4e120b6f00579b46c8cae29e", "ite.dmo", false },
+
+ { GID_ITE_WINCD, "8f4315a9bb10ec839253108a032c8b54", "ite.rsc", false },
+ { GID_ITE_WINCD, "a891405405edefc69c9d6c420c868b84", "scripts.rsc", false },
+ { GID_ITE_WINCD, "e2ccb61c325d6d1ead3be0e731fe29fe", "sounds.rsc", false },
+ { GID_ITE_WINCD, "41bb6b95d792dde5196bdb78740895a6", "voices.rsc", false },
+
+ { GID_ITE_MACCD, "4f7fa11c5175980ed593392838523060", "ite.rsc", false },
+ { GID_ITE_MACCD, "adf1f46c1d0589083996a7060c798ad0", "scripts.rsc", false },
+ { GID_ITE_MACCD, "1a91cd60169f367ecb6c6e058d899b2f", "music.rsc", false },
+ { GID_ITE_MACCD, "95863b89a0916941f6c5e1789843ba14", "sounds.rsc", false },
+ { GID_ITE_MACCD, "c14c4c995e7a0d3828e3812a494301b7", "Inherit the Earth Voices", true },
+
+ { GID_ITE_MACCD_G, "0bd506aa887bfc7965f695c6bd28237d", "ITE Resources.bin", true },
+ { GID_ITE_MACCD_G, "af0d7a2588e09ad3ecbc5b474ea238bf", "ITE Scripts.bin", true },
+ { GID_ITE_MACCD_G, "c1d20324b7cdf1650e67061b8a93251c", "ITE Music.bin", true },
+ { GID_ITE_MACCD_G, "441426c6bb2a517f65c7e49b57f7a345", "ITE Sounds.bin", true },
+ { GID_ITE_MACCD_G, "dba92ae7d57e942250fe135609708369", "ITE Voices.bin", true },
+
+ { GID_ITE_LINCD, "8f4315a9bb10ec839253108a032c8b54", "ite.rsc", false },
+ { GID_ITE_LINCD, "a891405405edefc69c9d6c420c868b84", "scripts.rsc", false },
+ { GID_ITE_LINCD, "e2ccb61c325d6d1ead3be0e731fe29fe", "sounds.rsc", false },
+ { GID_ITE_LINCD, "41bb6b95d792dde5196bdb78740895a6", "voices.rsc", false },
+ { GID_ITE_LINCD, "d6454756517f042f01210458abe8edd4", "music.rsc", false },
+
+ { GID_ITE_MULTICD, "a6433e34b97b15e64fe8214651012db9", "ite.rsc", false },
+ { GID_ITE_MULTICD, "a891405405edefc69c9d6c420c868b84", "scripts.rsc", false },
+ { GID_ITE_MULTICD, "e2ccb61c325d6d1ead3be0e731fe29fe", "sounds.rsc", false },
+ { GID_ITE_MULTICD, "c14c4c995e7a0d3828e3812a494301b7", "Inherit the Earth Voices", true },
+ { GID_ITE_MULTICD, "d6454756517f042f01210458abe8edd4", "music.rsc", false },
+
+ { GID_ITE_DISK_DE, "869fc23c8f38f575979ec67152914fee", "ite.rsc", false },
+ { GID_ITE_DISK_DE, "516f7330f8410057b834424ea719d1ef", "scripts.rsc", false },
+ { GID_ITE_DISK_DE, "0c9113e630f97ef0996b8c3114badb08", "voices.rsc", false },
+
+ { GID_ITE_DISK_DE2, "869fc23c8f38f575979ec67152914fee", "ite.rsc", false },
+ { GID_ITE_DISK_DE2, "516f7330f8410057b834424ea719d1ef", "scripts.rsc", false },
+ { GID_ITE_DISK_DE2, "0c9113e630f97ef0996b8c3114badb08", "voices.rsc", false },
+ { GID_ITE_DISK_DE2, "d6454756517f042f01210458abe8edd4", "music.rsc", false },
+
+ { GID_ITE_WINDEMO2, "3a450852cbf3c80773984d565647e6ac", "ited.rsc", false },
+ { GID_ITE_WINDEMO2, "3f12b67fa93e56e1a6be39d2921d80bb", "scriptsd.rsc", false },
+ { GID_ITE_WINDEMO2, "95a6c148e22e99a8c243f2978223583c", "soundsd.rsc", false },
+ { GID_ITE_WINDEMO2, "e139d86bab2ee8ba3157337f894a92d4", "voicesd.rsc", false },
+
+ { GID_ITE_LINDEMO, "3a450852cbf3c80773984d565647e6ac", "ited.rsc", false },
+ { GID_ITE_LINDEMO, "3f12b67fa93e56e1a6be39d2921d80bb", "scriptsd.rsc", false },
+ { GID_ITE_LINDEMO, "d6454756517f042f01210458abe8edd4", "musicd.rsc", false },
+ { GID_ITE_LINDEMO, "95a6c148e22e99a8c243f2978223583c", "soundsd.rsc", false },
+ { GID_ITE_LINDEMO, "e139d86bab2ee8ba3157337f894a92d4", "voicesd.rsc", false },
+
+ { GID_ITE_MACDEMO2, "addfc9d82bc2fa1f4cab23743c652c08", "ited.rsc", false },
+ { GID_ITE_MACDEMO2, "fded5c59b8b7c5976229f960d21e6b0b", "scriptsd.rsc", false },
+ { GID_ITE_MACDEMO2, "495bdde51fd9f4bea2b9c911091b1ab2", "musicd.rsc", false },
+ { GID_ITE_MACDEMO2, "b3a831fbed337d1f1300fee1dd474f6c", "soundsd.rsc", false },
+ { GID_ITE_MACDEMO2, "e139d86bab2ee8ba3157337f894a92d4", "voicesd.rsc", false },
+
+ { GID_ITE_WINDEMO1, "3a450852cbf3c80773984d565647e6ac", "ited.rsc", false },
+ { GID_ITE_WINDEMO1, "3f12b67fa93e56e1a6be39d2921d80bb", "scriptsd.rsc", false },
+ { GID_ITE_WINDEMO1, "a741139dd7365a13f463cd896ff9969a", "soundsd.rsc", false },
+ { GID_ITE_WINDEMO1, "0759eaf5b64ae19fd429920a70151ad3", "voicesd.rsc", false },
+
+ { GID_ITE_MACDEMO1, "addfc9d82bc2fa1f4cab23743c652c08", "ited.rsc", false },
+ { GID_ITE_MACDEMO1, "fded5c59b8b7c5976229f960d21e6b0b", "scriptsd.rsc", false },
+ { GID_ITE_MACDEMO1, "1a91cd60169f367ecb6c6e058d899b2f", "musicd.rsc", false },
+ { GID_ITE_MACDEMO1, "b3a831fbed337d1f1300fee1dd474f6c", "soundsd.rsc", false },
+ { GID_ITE_MACDEMO1, "e139d86bab2ee8ba3157337f894a92d4", "voicesd.rsc", false },
+
+ { GID_IHNM_CD, "0439083e3dfdc51b486071d45872ae52", "musicfm.res", false },
+ { GID_IHNM_CD, "80f875a1fb384160d1f4b27166eef583", "musicgm.res", false },
+ { GID_IHNM_CD, "46bbdc65d164ba7e89836a0935eec8e6", "scream.res", false },
+ { GID_IHNM_CD, "be38bbc5a26be809dbf39f13befebd01", "scripts.res", false },
+ { GID_IHNM_CD, "58b79e61594779513c7f2d35509fa89e", "patch.re_", false },
+ { GID_IHNM_CD, "1c610d543f32ec8b525e3f652536f269", "sfx.res", false },
+ { GID_IHNM_CD, "fc6440b38025f4b2cc3ff55c3da5c3eb", "voices1.res", false },
+ { GID_IHNM_CD, "b37f10fd1696ade7d58704ccaaebceeb", "voices2.res", false },
+ { GID_IHNM_CD, "3bbc16a8f741dbb511da506c660a0b54", "voices3.res", false },
+ { GID_IHNM_CD, "ebfa160122d2247a676ca39920e5d481", "voices4.res", false },
+ { GID_IHNM_CD, "1f501ce4b72392bdd1d9ec38f6eec6da", "voices5.res", false },
+ { GID_IHNM_CD, "f580ed7568c7d6ef34e934ba20adf834", "voices6.res", false },
+ { GID_IHNM_CD, "54b1f2013a075338ceb0e258d97808bd", "voicess.res", false },
+
+ // Reported by mld. German Retail
+ { GID_IHNM_CD_DE, "0439083e3dfdc51b486071d45872ae52", "musicfm.res", false },
+ { GID_IHNM_CD_DE, "80f875a1fb384160d1f4b27166eef583", "musicgm.res", false },
+ { GID_IHNM_CD_DE, "c92370d400e6f2a3fc411c3729d09224", "scream.res", false },
+ { GID_IHNM_CD_DE, "32aa01a89937520fe0ea513950117292", "scripts.res", false },
+ { GID_IHNM_CD_DE, "58b79e61594779513c7f2d35509fa89e", "patch.re_", false },
+ { GID_IHNM_CD_DE, "1c610d543f32ec8b525e3f652536f269", "sfx.res", false },
+ { GID_IHNM_CD_DE, "424971e1e2373187c3f5734fe36071a2", "voices1.res", false },
+ { GID_IHNM_CD_DE, "c270e0980782af43641a86e4a14e2a32", "voices2.res", false },
+ { GID_IHNM_CD_DE, "49e42befea883fd101ec3d0f5d0647b9", "voices3.res", false },
+ { GID_IHNM_CD_DE, "c477443c52a0aa56e686ebd8d051e4ab", "voices5.res", false },
+ { GID_IHNM_CD_DE, "2b9aea838f74b4eecfb29a8f205a2bd4", "voices6.res", false },
+ { GID_IHNM_CD_DE, "8b09a196a52627cacb4eab13bfe0b2c3", "voicess.res", false },
+
+ { GID_IHNM_CD_ES, "0439083e3dfdc51b486071d45872ae52", "musicfm.res", false },
+ { GID_IHNM_CD_ES, "80f875a1fb384160d1f4b27166eef583", "musicgm.res", false },
+ { GID_IHNM_CD_ES, "58b79e61594779513c7f2d35509fa89e", "patch.re_", false },
+ { GID_IHNM_CD_ES, "c92370d400e6f2a3fc411c3729d09224", "scream.res", false },
+ { GID_IHNM_CD_ES, "be38bbc5a26be809dbf39f13befebd01", "scripts.res", false },
+ { GID_IHNM_CD_ES, "1c610d543f32ec8b525e3f652536f269", "sfx.res", false },
+ { GID_IHNM_CD_ES, "dc6a34e3d1668730ea46815a92c7847f", "voices1.res", false },
+ { GID_IHNM_CD_ES, "dc6a5fa7a4cdc2ca5a6fd924e969986c", "voices2.res", false },
+ { GID_IHNM_CD_ES, "dc6a5fa7a4cdc2ca5a6fd924e969986c", "voices3.res", false },
+ { GID_IHNM_CD_ES, "0f87400b804232a58dd22e404420cc45", "voices4.res", false },
+ { GID_IHNM_CD_ES, "172668cfc5d8c305cb5b1a9b4d995fc0", "voices5.res", false },
+ { GID_IHNM_CD_ES, "96c9bda9a5f41d6bc232ed7bf6d371d9", "voices6.res", false },
+ { GID_IHNM_CD_ES, "d869de9883c8faea7f687217a9ec7057", "voicess.res", false },
+
+ { GID_IHNM_CD_RU, "0439083e3dfdc51b486071d45872ae52", "musicfm.res", false },
+ { GID_IHNM_CD_RU, "80f875a1fb384160d1f4b27166eef583", "musicgm.res", false },
+ { GID_IHNM_CD_RU, "46bbdc65d164ba7e89836a0935eec8e6", "scream.res", false },
+ { GID_IHNM_CD_RU, "be38bbc5a26be809dbf39f13befebd01", "scripts.res", false },
+ { GID_IHNM_CD_RU, "58b79e61594779513c7f2d35509fa89e", "patch.re_", false },
+ { GID_IHNM_CD_RU, "1c610d543f32ec8b525e3f652536f269", "sfx.res", false },
+ { GID_IHNM_CD_RU, "d6100d2dc3b2b9f2e1ad247f613dce9b", "voices1.res", false },
+ { GID_IHNM_CD_RU, "84f6f48ecc2832841ea6417a9a379430", "voices2.res", false },
+ { GID_IHNM_CD_RU, "ebb9501283047f27a0f54e27b3c8ba1e", "voices3.res", false },
+ { GID_IHNM_CD_RU, "4c145da5fa6d1306162a7ca8ce5a4f2e", "voices4.res", false },
+ { GID_IHNM_CD_RU, "871a559644281917677eca4af1b05620", "voices5.res", false },
+ { GID_IHNM_CD_RU, "211be5c24f066d69a2f6cfa953acfba6", "voices6.res", false },
+ { GID_IHNM_CD_RU, "9df7cd3b18ddaa16b5291b3432567036", "voicess.res", false },
+
+ { GID_IHNM_CD_FR, "0439083e3dfdc51b486071d45872ae52", "musicfm.res", false },
+ { GID_IHNM_CD_FR, "80f875a1fb384160d1f4b27166eef583", "musicgm.res", false },
+ { GID_IHNM_CD_FR, "58b79e61594779513c7f2d35509fa89e", "patch.re_", false },
+ { GID_IHNM_CD_FR, "c92370d400e6f2a3fc411c3729d09224", "scream.res", false },
+ { GID_IHNM_CD_FR, "32aa01a89937520fe0ea513950117292", "scripts.res", false },
+ { GID_IHNM_CD_FR, "1c610d543f32ec8b525e3f652536f269", "sfx.res", false },
+ { GID_IHNM_CD_FR, "424971e1e2373187c3f5734fe36071a2", "voices1.res", false },
+ { GID_IHNM_CD_FR, "c2d93a35d2c2def9c3d6d242576c794b", "voices2.res", false },
+ { GID_IHNM_CD_FR, "49e42befea883fd101ec3d0f5d0647b9", "voices3.res", false },
+ { GID_IHNM_CD_FR, "f4c415de7c03de86b73f9a12b8bd632f", "voices5.res", false },
+ { GID_IHNM_CD_FR, "3fc5358a5d8eee43bdfab2740276572e", "voices6.res", false },
+ { GID_IHNM_CD_FR, "b8642e943bbebf89cef2f48b31cb4305", "voicess.res", false },
+
+ { GID_IHNM_DEMO, "46bbdc65d164ba7e89836a0935eec8e6", "scream.res", false },
+ { GID_IHNM_DEMO, "9626bda8978094ff9b29198bc1ed5f9a", "scripts.res", false },
+ { GID_IHNM_DEMO, "1c610d543f32ec8b525e3f652536f269", "sfx.res", false },
+ { GID_IHNM_DEMO, "3bbc16a8f741dbb511da506c660a0b54", "voicesd.res", false },
+};
+
+static GameDescription gameDescriptions[] = {
+ // Inherit the earth - DOS Demo version
+ // sound unchecked
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_DEMO_G, // Game id
+ "Inherit the Earth: Quest for the Orb (DOS Demo)", // Game title
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE, // Starting scene number
+ &ITEDemo_Resources,
+ ARRAYSIZE(ITEDEMO_GameFiles), // Game datafiles
+ ITEDEMO_GameFiles,
+ ARRAYSIZE(ITEDEMO_GameFonts),
+ ITEDEMO_GameFonts,
+ &ITEDEMO_GameSound,
+ &ITEDEMO_GameSound,
+ NULL,
+ 0,
+ NULL,
+ 0, // features
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Inherit the earth - MAC Demo version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_MACDEMO2,
+ "Inherit the Earth: Quest for the Orb (MAC Demo)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEMACDEMO_GameFiles),
+ ITEMACDEMO_GameFiles,
+ ARRAYSIZE(ITEWINDEMO_GameFonts),
+ ITEWINDEMO_GameFonts,
+ &ITEMACDEMO_GameVoice,
+ &ITEMACDEMO_GameSound,
+ &ITEMACDEMO_GameMusic,
+ ARRAYSIZE(ITEMacPatch_Files),
+ ITEMacPatch_Files,
+ GF_BIG_ENDIAN_DATA | GF_WYRMKEEP | GF_CD_FX | GF_SCENE_SUBSTITUTES,
+ Common::EN_USA,
+ Common::kPlatformMacintosh,
+ },
+
+ // Inherit the earth - early MAC Demo version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_MACDEMO1,
+ "Inherit the Earth: Quest for the Orb (early MAC Demo)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEMACDEMO_GameFiles),
+ ITEMACDEMO_GameFiles,
+ ARRAYSIZE(ITEWINDEMO_GameFonts),
+ ITEWINDEMO_GameFonts,
+ &ITEMACDEMO_GameVoice,
+ &ITEMACDEMO_GameSound,
+ &ITEMACCD_GameMusic,
+ ARRAYSIZE(ITEMacPatch_Files),
+ ITEMacPatch_Files,
+ GF_BIG_ENDIAN_DATA | GF_WYRMKEEP | GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformMacintosh,
+ },
+
+ // Inherit the earth - MAC CD Guild version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_MACCD_G,
+ "Inherit the Earth: Quest for the Orb (MAC CD)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEMACCD_G_GameFiles),
+ ITEMACCD_G_GameFiles,
+ ARRAYSIZE(ITEWINDEMO_GameFonts),
+ ITEWINDEMO_GameFonts,
+ &ITEMACCD_G_GameSound,
+ &ITEMACCD_G_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_BIG_ENDIAN_DATA | GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformMacintosh,
+ },
+
+ // Inherit the earth - MAC CD Wyrmkeep version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_MACCD,
+ "Inherit the Earth: Quest for the Orb (Wyrmkeep MAC CD)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEMACCD_GameFiles),
+ ITEMACCD_GameFiles,
+ ARRAYSIZE(ITEWINDEMO_GameFonts),
+ ITEWINDEMO_GameFonts,
+ &ITEMACCD_GameSound,
+ &ITEMACCD_GameSound,
+ &ITEMACCD_GameMusic,
+ ARRAYSIZE(ITEMacPatch_Files),
+ ITEMacPatch_Files,
+ GF_BIG_ENDIAN_DATA | GF_WYRMKEEP | GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformMacintosh,
+ },
+
+ // Inherit the earth - Linux Demo version
+ // Note: it should be before GID_ITE_WINDEMO2 version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_LINDEMO,
+ "Inherit the Earth: Quest for the Orb (Linux Demo)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITELINDEMO_GameFiles),
+ ITELINDEMO_GameFiles,
+ ARRAYSIZE(ITEWINDEMO_GameFonts),
+ ITEWINDEMO_GameFonts,
+ &ITEWINDEMO2_GameVoice,
+ &ITEWINDEMO2_GameSound,
+ &ITELINDEMO_GameMusic,
+ ARRAYSIZE(ITELinPatch_Files),
+ ITELinPatch_Files,
+ GF_WYRMKEEP | GF_CD_FX | GF_SCENE_SUBSTITUTES,
+ Common::EN_USA,
+ Common::kPlatformLinux,
+ },
+
+ // Inherit the earth - Win32 Demo version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_WINDEMO2,
+ "Inherit the Earth: Quest for the Orb (Win32 Demo)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEWINDEMO_GameFiles),
+ ITEWINDEMO_GameFiles,
+ ARRAYSIZE(ITEWINDEMO_GameFonts),
+ ITEWINDEMO_GameFonts,
+ &ITEWINDEMO2_GameVoice,
+ &ITEWINDEMO2_GameSound,
+ NULL,
+ ARRAYSIZE(ITEWinPatch2_Files),
+ ITEWinPatch2_Files,
+ GF_WYRMKEEP | GF_CD_FX | GF_SCENE_SUBSTITUTES,
+ Common::EN_USA,
+ Common::kPlatformWindows,
+ },
+
+ // Inherit the earth - early Win32 Demo version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_WINDEMO1,
+ "Inherit the Earth: Quest for the Orb (early Win32 Demo)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEWINDEMO_GameFiles),
+ ITEWINDEMO_GameFiles,
+ ARRAYSIZE(ITEWINDEMO_GameFonts),
+ ITEWINDEMO_GameFonts,
+ &ITEWINDEMO1_GameSound,
+ &ITEWINDEMO1_GameSound,
+ NULL,
+ ARRAYSIZE(ITEWinPatch1_Files),
+ ITEWinPatch1_Files,
+ GF_WYRMKEEP | GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformWindows,
+ },
+
+ // Inherit the earth - Wyrmkeep combined Windows/Mac/Linux CD
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_MULTICD,
+ "Inherit the Earth: Quest for the Orb (Multi-OS CD Version)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEMULTICD_GameFiles),
+ ITEMULTICD_GameFiles,
+ ARRAYSIZE(ITECD_GameFonts),
+ ITECD_GameFonts,
+ &ITEMACCD_GameSound,
+ &ITECD_GameSound,
+ &ITEMACCD_GameMusic,
+ 0,
+ NULL,
+ GF_WYRMKEEP | GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformUnknown,
+ },
+
+ // Inherit the earth - Wyrmkeep Linux CD version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_LINCD,
+ "Inherit the Earth: Quest for the Orb (Linux CD Version)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITELINCD_GameFiles),
+ ITELINCD_GameFiles,
+ ARRAYSIZE(ITECD_GameFonts),
+ ITECD_GameFonts,
+ &ITECD_GameSound,
+ &ITECD_GameSound,
+ &ITEMACCD_GameMusic,
+ ARRAYSIZE(ITELinPatch_Files),
+ ITELinPatch_Files,
+ GF_WYRMKEEP | GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformLinux,
+ },
+
+ // Inherit the earth - Wyrmkeep Windows CD version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_WINCD,
+ "Inherit the Earth: Quest for the Orb (Win32 CD Version)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITECD_GameFiles),
+ ITECD_GameFiles,
+ ARRAYSIZE(ITECD_GameFonts),
+ ITECD_GameFonts,
+ &ITECD_GameSound,
+ &ITECD_GameSound,
+ NULL,
+ ARRAYSIZE(ITEWinPatch1_Files),
+ ITEWinPatch1_Files,
+ GF_WYRMKEEP | GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformWindows,
+ },
+
+ // Inherit the earth - DOS CD version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_CD_G,
+ "Inherit the Earth: Quest for the Orb (DOS CD Version)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITECD_GameFiles),
+ ITECD_GameFiles,
+ ARRAYSIZE(ITECD_GameFonts),
+ ITECD_GameFonts,
+ &ITECD_GameSound,
+ &ITECD_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Inherit the earth - DOS CD version with digital music
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_CD_G2,
+ "Inherit the Earth: Quest for the Orb (DOS CD Version)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITECD2_GameFiles),
+ ITECD2_GameFiles,
+ ARRAYSIZE(ITECD_GameFonts),
+ ITECD_GameFonts,
+ &ITECD_GameSound,
+ &ITECD_GameSound,
+ &ITEMACCD_GameMusic,
+ 0,
+ NULL,
+ GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Inherit the earth - DOS CD German version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_CD_DE,
+ "Inherit the Earth: Quest for the Orb (De DOS CD Version)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITECD_GameFiles),
+ ITECD_GameFiles,
+ ARRAYSIZE(ITECD_GameFonts),
+ ITECD_GameFonts,
+ &ITECD_GameSound,
+ &ITECD_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_CD_FX,
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ },
+
+ // Inherit the earth - DOS CD German version with digital music
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_CD_DE2,
+ "Inherit the Earth: Quest for the Orb (De DOS CD Version)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITECD2_GameFiles),
+ ITECD2_GameFiles,
+ ARRAYSIZE(ITECD_GameFonts),
+ ITECD_GameFonts,
+ &ITECD_GameSound,
+ &ITECD_GameSound,
+ &ITEMACCD_GameMusic,
+ 0,
+ NULL,
+ GF_CD_FX,
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ },
+
+ // Inherit the earth - CD version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_CD,
+ "Inherit the Earth: Quest for the Orb (DOS CD Version)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITECD_GameFiles),
+ ITECD_GameFiles,
+ ARRAYSIZE(ITECD_GameFonts),
+ ITECD_GameFonts,
+ &ITECD_GameSound,
+ &ITECD_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_CD_FX,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Inherit the earth - German Floppy version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_DISK_DE,
+ "Inherit the Earth: Quest for the Orb (De DOS Floppy)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEDISK_GameFiles),
+ ITEDISK_GameFiles,
+ ARRAYSIZE(ITEDISK_GameFonts),
+ ITEDISK_GameFonts,
+ &ITEDISK_GameSound,
+ &ITEDISK_GameSound,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ },
+
+ // Inherit the earth - German Floppy version with digital music
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_DISK_DE2,
+ "Inherit the Earth: Quest for the Orb (De DOS Floppy)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEDISK2_GameFiles),
+ ITEDISK2_GameFiles,
+ ARRAYSIZE(ITEDISK_GameFonts),
+ ITEDISK_GameFonts,
+ &ITEDISK_GameSound,
+ &ITEDISK_GameSound,
+ &ITEMACCD_GameMusic,
+ 0,
+ NULL,
+ 0,
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ },
+
+ // Inherit the earth - Disk version
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_DISK_G,
+ "Inherit the Earth: Quest for the Orb (DOS Floppy)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEDISK_GameFiles),
+ ITEDISK_GameFiles,
+ ARRAYSIZE(ITEDISK_GameFonts),
+ ITEDISK_GameFonts,
+ &ITEDISK_GameSound,
+ &ITEDISK_GameSound,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Inherit the earth - Disk version with digital music
+ {
+ "ite",
+ GType_ITE,
+ GID_ITE_DISK_G2,
+ "Inherit the Earth: Quest for the Orb (DOS Floppy)",
+ &ITE_DisplayInfo,
+ ITE_DEFAULT_SCENE,
+ &ITE_Resources,
+ ARRAYSIZE(ITEDISK2_GameFiles),
+ ITEDISK2_GameFiles,
+ ARRAYSIZE(ITEDISK_GameFonts),
+ ITEDISK_GameFonts,
+ &ITEDISK_GameSound,
+ &ITEDISK_GameSound,
+ &ITEMACCD_GameMusic,
+ 0,
+ NULL,
+ 0,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // I Have No Mouth And I Must Scream - Demo version
+ {
+ "ihnm",
+ GType_IHNM,
+ GID_IHNM_DEMO,
+ "I Have No Mouth and I Must Scream (DOS Demo)",
+ &IHNM_DisplayInfo,
+ 0,
+ &IHNM_Resources,
+ ARRAYSIZE(IHNMDEMO_GameFiles),
+ IHNMDEMO_GameFiles,
+ ARRAYSIZE(IHNMDEMO_GameFonts),
+ IHNMDEMO_GameFonts,
+ &IHNM_GameSound,
+ &IHNM_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_DEFAULT_TO_1X_SCALER,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // I Have No Mouth And I Must Scream - CD version
+ {
+ "ihnm",
+ GType_IHNM,
+ GID_IHNM_CD,
+ "I Have No Mouth and I Must Scream (DOS)",
+ &IHNM_DisplayInfo,
+ IHNM_DEFAULT_SCENE,
+ &IHNM_Resources,
+ ARRAYSIZE(IHNMCD_GameFiles),
+ IHNMCD_GameFiles,
+ ARRAYSIZE(IHNMCD_GameFonts),
+ IHNMCD_GameFonts,
+ &IHNM_GameSound,
+ &IHNM_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_DEFAULT_TO_1X_SCALER,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // I Have No Mouth And I Must Scream - De CD version
+ {
+ "ihnm",
+ GType_IHNM,
+ GID_IHNM_CD_DE,
+ "I Have No Mouth and I Must Scream (DE DOS)",
+ &IHNM_DisplayInfo,
+ IHNM_DEFAULT_SCENE,
+ &IHNM_Resources,
+ ARRAYSIZE(IHNMCD_Censored_GameFiles),
+ IHNMCD_Censored_GameFiles,
+ ARRAYSIZE(IHNMCD_GameFonts),
+ IHNMCD_GameFonts,
+ &IHNM_GameSound,
+ &IHNM_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_DEFAULT_TO_1X_SCALER,
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ },
+ // I Have No Mouth And I Must Scream - Sp CD version
+ {
+ "ihnm",
+ GType_IHNM,
+ GID_IHNM_CD_ES,
+ "I Have No Mouth and I Must Scream (Sp DOS)",
+ &IHNM_DisplayInfo,
+ IHNM_DEFAULT_SCENE,
+ &IHNM_Resources,
+ ARRAYSIZE(IHNMCD_GameFiles),
+ IHNMCD_GameFiles,
+ ARRAYSIZE(IHNMCD_GameFonts),
+ IHNMCD_GameFonts,
+ &IHNM_GameSound,
+ &IHNM_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_DEFAULT_TO_1X_SCALER,
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ },
+ // I Have No Mouth And I Must Scream - Ru CD version
+ {
+ "ihnm",
+ GType_IHNM,
+ GID_IHNM_CD_RU,
+ "I Have No Mouth and I Must Scream (Ru DOS)",
+ &IHNM_DisplayInfo,
+ IHNM_DEFAULT_SCENE,
+ &IHNM_Resources,
+ ARRAYSIZE(IHNMCD_GameFiles),
+ IHNMCD_GameFiles,
+ ARRAYSIZE(IHNMCD_GameFonts),
+ IHNMCD_GameFonts,
+ &IHNM_GameSound,
+ &IHNM_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_DEFAULT_TO_1X_SCALER,
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ },
+ // I Have No Mouth And I Must Scream - Fr CD version
+ {
+ "ihnm",
+ GType_IHNM,
+ GID_IHNM_CD_FR,
+ "I Have No Mouth and I Must Scream (Fr DOS)",
+ &IHNM_DisplayInfo,
+ IHNM_DEFAULT_SCENE,
+ &IHNM_Resources,
+ ARRAYSIZE(IHNMCD_Censored_GameFiles),
+ IHNMCD_Censored_GameFiles,
+ ARRAYSIZE(IHNMCD_GameFonts),
+ IHNMCD_GameFonts,
+ &IHNM_GameSound,
+ &IHNM_GameSound,
+ NULL,
+ 0,
+ NULL,
+ GF_DEFAULT_TO_1X_SCALER,
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ },
+};
+
+bool SagaEngine::initGame() {
+ uint16 gameCount = ARRAYSIZE(gameDescriptions);
+ int gameNumber = -1;
+ FSList dummy;
+ DetectedGameList detectedGames;
+ int *matches;
+ Common::Language language = Common::UNK_LANG;
+ Common::Platform platform = Common::kPlatformUnknown;
+
+ if (ConfMan.hasKey("language"))
+ language = Common::parseLanguage(ConfMan.get("language"));
+ if (ConfMan.hasKey("platform"))
+ platform = Common::parsePlatform(ConfMan.get("platform"));
+
+
+ detectedGames = GAME_ProbeGame(dummy, &matches);
+
+ if (detectedGames.size() == 0) {
+ warning("No valid games were found in the specified directory.");
+ return false;
+ }
+
+ // If we have more than one match then try to match by platform and
+ // language
+ int count = 0;
+ if (detectedGames.size() > 1) {
+ for (int i = 0; i < ARRAYSIZE(gameDescriptions); i++)
+ if (matches[i] != -1) {
+ if ((gameDescriptions[matches[i]].language != language &&
+ language != Common::UNK_LANG) ||
+ (gameDescriptions[matches[i]].platform != platform &&
+ platform != Common::kPlatformUnknown)) {
+ debug(2, "Purged (pass 2) %s", gameDescriptions[matches[i]].title);
+ matches[i] = -1;
+ }
+ else
+ count++;
+ }
+ } else
+ count = 1;
+
+ if (count != 1)
+ warning("Conflicting targets detected (%d)", count);
+
+ for (int i = 0; i < ARRAYSIZE(gameDescriptions); i++)
+ if (matches[i] != -1) {
+ gameNumber = matches[i];
+ break;
+ }
+
+ free(matches);
+
+ if (gameNumber >= gameCount || gameNumber == -1) {
+ error("SagaEngine::loadGame wrong gameNumber");
+ }
+
+ debug(2, "Running %s", gameDescriptions[gameNumber].title);
+
+ _gameNumber = gameNumber;
+ _gameDescription = &gameDescriptions[gameNumber];
+ _gameDisplayInfo = *_gameDescription->gameDisplayInfo;
+ _displayClip.right = _gameDisplayInfo.logicalWidth;
+ _displayClip.bottom = _gameDisplayInfo.logicalHeight;
+
+ if (!_resource->createContexts()) {
+ return false;
+ }
+ return true;
+}
+
+DetectedGameList GAME_ProbeGame(const FSList &fslist, int **retmatches) {
+ DetectedGameList detectedGames;
+ int game_n;
+ int index = 0, i, j;
+ int matches[ARRAYSIZE(gameDescriptions)];
+ bool mode = retmatches ? false : true;
+
+ game_n = -1;
+ for (i = 0; i < ARRAYSIZE(gameDescriptions); i++)
+ matches[i] = -1;
+
+ while (1) {
+ game_n = detectGame(fslist, mode, game_n);
+ if (game_n == -1)
+ break;
+ matches[index++] = game_n;
+ }
+
+ // We have some resource sets which are superpositions of other
+ // Particularly it is ite-demo-linux vs ite-demo-win
+ // Now remove lesser set if bigger matches too
+
+ if (index > 1) {
+ // Search max number
+ int maxcount = 0;
+ for (i = 0; i < index; i++) {
+ int count = 0;
+ for (j = 0; j < ARRAYSIZE(gameMD5); j++)
+ if (gameMD5[j].id == gameDescriptions[matches[i]].gameId)
+ count++;
+ maxcount = MAX(maxcount, count);
+ }
+
+ // Now purge targets with number of files lesser than max
+ for (i = 0; i < index; i++) {
+ int count = 0;
+ for (j = 0; j < ARRAYSIZE(gameMD5); j++)
+ if (gameMD5[j].id == gameDescriptions[matches[i]].gameId)
+ count++;
+ if (count < maxcount) {
+ debug(2, "Purged: %s", gameDescriptions[matches[i]].title);
+ matches[i] = -1;
+ }
+ }
+
+ }
+
+ // and now push them into list of detected games
+ for (i = 0; i < index; i++)
+ if (matches[i] != -1)
+ detectedGames.push_back(DetectedGame(gameDescriptions[matches[i]].toGameSettings(),
+ gameDescriptions[matches[i]].language,
+ gameDescriptions[matches[i]].platform));
+
+ if (retmatches) {
+ *retmatches = (int *)calloc(ARRAYSIZE(gameDescriptions), sizeof(int));
+ for (i = 0; i < ARRAYSIZE(gameDescriptions); i++)
+ (*retmatches)[i] = matches[i];
+ }
+
+ return detectedGames;
+}
+
+int detectGame(const FSList &fslist, bool mode, int start) {
+ int game_count = ARRAYSIZE(gameDescriptions);
+ int game_n = -1;
+ typedef Common::Map<Common::String, Common::String> StringMap;
+ StringMap filesMD5;
+
+ typedef Common::Map<Common::String, bool> StringSet;
+ StringSet filesList;
+
+ uint16 file_count;
+ uint16 file_n;
+ Common::File test_file;
+ bool file_missing;
+
+ Common::String tstr, tstr1;
+ char md5str[32+1];
+ uint8 md5sum[16];
+
+ // First we compose list of files which we need MD5s for
+ for (int i = 0; i < ARRAYSIZE(gameMD5); i++) {
+ tstr = Common::String(gameMD5[i].filename);
+ tstr.toLowercase();
+
+ if (gameMD5[i].caseSensitive && !mode)
+ filesList[Common::String(gameMD5[i].filename)] = true;
+ else
+ filesList[tstr] = true;
+ }
+
+ if (mode) {
+ // Now count MD5s for required files
+ for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (!file->isDirectory()) {
+ tstr = file->displayName();
+ // FIXME: there is a bug in String class. tstr1 = tstr; tstr.toLowercase()
+ // makes tstr1 lowercase as well
+ tstr1 = Common::String(file->displayName().c_str());
+ tstr.toLowercase();
+
+ if (filesList.contains(tstr) || filesList.contains(tstr1)) {
+ if (Common::md5_file(file->path().c_str(), md5sum, NULL, FILE_MD5_BYTES)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+ filesMD5[tstr] = Common::String(md5str);
+ filesMD5[tstr1] = Common::String(md5str);
+ }
+ }
+ }
+ }
+ } else {
+ Common::File testFile;
+
+ for (StringSet::const_iterator file = filesList.begin(); file != filesList.end(); ++file) {
+ if (testFile.open(file->_key.c_str())) {
+ testFile.close();
+ if (Common::md5_file(file->_key.c_str(), md5sum, NULL, FILE_MD5_BYTES)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+ filesMD5[file->_key] = Common::String(md5str);
+ }
+ }
+ }
+ }
+
+ for (game_n = start + 1; game_n < game_count; game_n++) {
+ file_count = gameDescriptions[game_n].filesCount;
+ file_missing = false;
+
+ // Try to open all files for this game
+ for (file_n = 0; file_n < file_count; file_n++) {
+ tstr = gameDescriptions[game_n].filesDescriptions[file_n].fileName;
+
+ if (!filesMD5.contains(tstr)) {
+ file_missing = true;
+ break;
+ }
+ }
+
+ // Try the next game, couldn't find all files for the current
+ // game
+ if (file_missing) {
+ continue;
+ } else {
+ bool match = true;
+
+ debug(2, "Probing game: %s", gameDescriptions[game_n].title);
+
+ for (int i = 0; i < ARRAYSIZE(gameMD5); i++) {
+ if (gameMD5[i].id == gameDescriptions[game_n].gameId) {
+ tstr = gameMD5[i].filename;
+
+ if (strcmp(gameMD5[i].md5, filesMD5[tstr].c_str())) {
+ match = false;
+ break;
+ }
+ }
+ }
+ if (!match)
+ continue;
+
+ debug(2, "Found game: %s", gameDescriptions[game_n].title);
+
+ return game_n;
+ }
+ }
+
+ if (!filesMD5.isEmpty() && start == -1) {
+ printf("MD5s of your game version are unknown. Please, report following data to\n");
+ printf("ScummVM team along with your game name and version:\n");
+
+ for (StringMap::const_iterator file = filesMD5.begin(); file != filesMD5.end(); ++file)
+ printf("%s: %s\n", file->_key.c_str(), file->_value.c_str());
+ }
+
+ return -1;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/gfx.cpp b/engines/saga/gfx.cpp
new file mode 100644
index 0000000000..4de8c52de2
--- /dev/null
+++ b/engines/saga/gfx.cpp
@@ -0,0 +1,485 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Misc. graphics routines
+
+#include "saga/saga.h"
+#include "saga/gfx.h"
+#include "saga/interface.h"
+#include "saga/resnames.h"
+#include "saga/rscfile.h"
+#include "saga/scene.h"
+#include "saga/stream.h"
+
+#include "common/system.h"
+
+namespace Saga {
+
+Gfx::Gfx(SagaEngine *vm, OSystem *system, int width, int height, GameDetector &detector) : _vm(vm), _system(system) {
+ _system->beginGFXTransaction();
+ _vm->initCommonGFX(detector);
+ _system->initSize(width, height);
+ _system->endGFXTransaction();
+
+ debug(5, "Init screen %dx%d", width, height);
+ // Convert surface data to R surface data
+ _backBuffer.create(width, height, 1);
+
+ // Set module data
+ _init = 1;
+
+ // Start with the cursor shown. It will be hidden before the intro, if
+ // there is an intro. (With boot params, there may not be.)
+ setCursor(kCursorNormal);
+ showCursor(true);
+}
+
+Gfx::~Gfx() {
+ _backBuffer.free();
+}
+
+void Surface::drawPalette() {
+ int x;
+ int y;
+ int color = 0;
+ Rect palRect;
+
+ for (y = 0; y < 16; y++) {
+ palRect.top = (y * 8) + 4;
+ palRect.bottom = palRect.top + 8;
+
+ for (x = 0; x < 16; x++) {
+ palRect.left = (x * 8) + 4;
+ palRect.right = palRect.left + 8;
+
+ drawRect(palRect, color);
+ color++;
+ }
+ }
+}
+
+// * Copies a rectangle from a raw 8 bit pixel buffer to the specified surface.
+// - The surface must match the logical dimensions of the buffer exactly.
+void Surface::blit(const Common::Rect &destRect, const byte *sourceBuffer) {
+ const byte *readPointer;
+ byte *writePointer;
+ int row;
+ ClipData clipData;
+
+ clipData.sourceRect.left = 0;
+ clipData.sourceRect.top = 0;
+ clipData.sourceRect.right = destRect.width();
+ clipData.sourceRect.bottom = destRect.height();
+
+ clipData.destPoint.x = destRect.left;
+ clipData.destPoint.y = destRect.top;
+ clipData.destRect.left = 0;
+ clipData.destRect.right = w;
+ clipData.destRect.top = 0;
+ clipData.destRect.bottom = h;
+
+ if (!clipData.calcClip()) {
+ return;
+ }
+
+ // Transfer buffer data to surface
+ readPointer = (sourceBuffer + clipData.drawSource.x) +
+ (clipData.sourceRect.right * clipData.drawSource.y);
+
+ writePointer = ((byte *)pixels + clipData.drawDest.x) + (pitch * clipData.drawDest.y);
+
+ for (row = 0; row < clipData.drawHeight; row++) {
+ memcpy(writePointer, readPointer, clipData.drawWidth);
+
+ writePointer += pitch;
+ readPointer += clipData.sourceRect.right;
+ }
+}
+
+void Surface::drawPolyLine(const Point *points, int count, int color) {
+ int i;
+ if (count >= 3) {
+ for (i = 1; i < count; i++) {
+ drawLine(points[i].x, points[i].y, points[i - 1].x, points[i - 1].y, color);
+ }
+
+ drawLine(points[count - 1].x, points[count - 1].y, points->x, points->y, color);
+ }
+}
+
+/**
+* Dissolve one image with another.
+* If flags if set to 1, do zero masking.
+*/
+void Surface::transitionDissolve(const byte *sourceBuffer, const Common::Rect &sourceRect, int flags, double percent) {
+#define XOR_MASK 0xB400;
+ int pixelcount = w * h;
+ int seqlimit = (int)(65535 * percent);
+ int seq = 1;
+ int i, x1, y1;
+ byte color;
+
+ for (i = 0; i < seqlimit; i++) {
+ if (seq & 1) {
+ seq = (seq >> 1) ^ XOR_MASK;
+ } else {
+ seq = seq >> 1;
+ }
+
+ if (seq == 1) {
+ return;
+ }
+
+ if (seq >= pixelcount) {
+ continue;
+ } else {
+ x1 = seq % w;
+ y1 = seq / w;
+
+ if (sourceRect.contains(x1, y1)) {
+ color = sourceBuffer[(x1-sourceRect.left) + sourceRect.width()*(y1-sourceRect.top)];
+ if (flags == 0 || color)
+ ((byte*)pixels)[seq] = color;
+ }
+ }
+ }
+}
+
+void Gfx::initPalette() {
+ if(_vm->getGameType() != GType_IHNM)
+ return;
+
+ ResourceContext *resourceContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ if (resourceContext == NULL) {
+ error("Resource::loadGlobalResources() resource context not found");
+ }
+
+ byte *resourcePointer;
+ size_t resourceLength;
+
+ _vm->_resource->loadResource(resourceContext, RID_IHNM_DEFAULT_PALETTE,
+ resourcePointer, resourceLength);
+
+ MemoryReadStream metaS(resourcePointer, resourceLength);
+
+ for(int i = 0; i < 256; i++) {
+ _globalPalette[i].red = metaS.readByte();
+ _globalPalette[i].green = metaS.readByte();
+ _globalPalette[i].blue = metaS.readByte();
+ }
+
+ free(resourcePointer);
+
+ setPalette(_globalPalette, true);
+}
+
+void Gfx::setPalette(const PalEntry *pal, bool full) {
+ int i;
+ byte *ppal;
+ int from, numcolors;
+
+ if (_vm->getGameType() != GType_IHNM || full) {
+ from = 0;
+ numcolors = PAL_ENTRIES;
+ } else {
+ from = 0;
+ numcolors = 248;
+ }
+
+ for (i = 0, ppal = &_currentPal[from * 4]; i < numcolors; i++, ppal += 4) {
+ ppal[0] = _globalPalette[i].red = pal[i].red;
+ ppal[1] = _globalPalette[i].green = pal[i].green;
+ ppal[2] = _globalPalette[i].blue = pal[i].blue;
+ ppal[3] = 0;
+ }
+
+ // Make 256th color black. See bug #1256368
+ if ((_vm->getPlatform() == Common::kPlatformMacintosh) && !_vm->_scene->isInIntro())
+ memset(&_currentPal[255 * 4], 0, 4);
+
+ _system->setPalette(_currentPal, 0, PAL_ENTRIES);
+}
+
+void Gfx::setPaletteColor(int n, int r, int g, int b) {
+ bool update = false;
+
+ // This function may get called a lot. To avoid forcing full-screen
+ // updates, only update the palette if the color actually changes.
+
+ if (_currentPal[4 * n + 0] != r) {
+ _currentPal[4 * n + 0] = _globalPalette[n].red = r;
+ update = true;
+ }
+ if (_currentPal[4 * n + 1] != g) {
+ _currentPal[4 * n + 1] = _globalPalette[n].green = g;
+ update = true;
+ }
+ if (_currentPal[4 * n + 2] != b) {
+ _currentPal[4 * n + 2] = _globalPalette[n].blue = b;
+ update = true;
+ }
+ if (_currentPal[4 * n + 3] != 0) {
+ _currentPal[4 * n + 3] = 0;
+ update = true;
+ }
+
+ if (update)
+ _system->setPalette(_currentPal, n, 1);
+}
+
+void Gfx::getCurrentPal(PalEntry *src_pal) {
+ int i;
+ byte *ppal;
+
+ for (i = 0, ppal = _currentPal; i < PAL_ENTRIES; i++, ppal += 4) {
+ src_pal[i].red = ppal[0];
+ src_pal[i].green = ppal[1];
+ src_pal[i].blue = ppal[2];
+ }
+}
+
+void Gfx::palToBlack(PalEntry *srcPal, double percent) {
+ int i;
+ //int fade_max = 255;
+ int new_entry;
+ byte *ppal;
+ PalEntry *palE;
+ int from, numcolors;
+
+ double fpercent;
+
+ if (_vm->getGameType() != GType_IHNM) {
+ from = 0;
+ numcolors = PAL_ENTRIES;
+ } else {
+ from = 0;
+ numcolors = 248;
+ }
+
+ if (percent > 1.0) {
+ percent = 1.0;
+ }
+
+ // Exponential fade
+ fpercent = percent * percent;
+
+ fpercent = 1.0 - fpercent;
+
+ // Use the correct percentage change per frame for each palette entry
+ for (i = 0, ppal = _currentPal; i < PAL_ENTRIES; i++, ppal += 4) {
+ if (i < from || i >= from + numcolors)
+ palE = &_globalPalette[i];
+ else
+ palE = &srcPal[i];
+
+ new_entry = (int)(palE->red * fpercent);
+
+ if (new_entry < 0) {
+ ppal[0] = 0;
+ } else {
+ ppal[0] = (byte) new_entry;
+ }
+
+ new_entry = (int)(palE->green * fpercent);
+
+ if (new_entry < 0) {
+ ppal[1] = 0;
+ } else {
+ ppal[1] = (byte) new_entry;
+ }
+
+ new_entry = (int)(palE->blue * fpercent);
+
+ if (new_entry < 0) {
+ ppal[2] = 0;
+ } else {
+ ppal[2] = (byte) new_entry;
+ }
+ ppal[3] = 0;
+ }
+
+ // Make 256th color black. See bug #1256368
+ if ((_vm->getPlatform() == Common::kPlatformMacintosh) && !_vm->_scene->isInIntro())
+ memset(&_currentPal[255 * 4], 0, 4);
+
+ _system->setPalette(_currentPal, 0, PAL_ENTRIES);
+}
+
+void Gfx::blackToPal(PalEntry *srcPal, double percent) {
+ int new_entry;
+ double fpercent;
+ byte *ppal;
+ int i;
+ PalEntry *palE;
+ int from, numcolors;
+
+ if (_vm->getGameType() != GType_IHNM) {
+ from = 0;
+ numcolors = PAL_ENTRIES;
+ } else {
+ from = 0;
+ numcolors = 248;
+ }
+
+ if (percent > 1.0) {
+ percent = 1.0;
+ }
+
+ // Exponential fade
+ fpercent = percent * percent;
+
+ fpercent = 1.0 - fpercent;
+
+ // Use the correct percentage change per frame for each palette entry
+ for (i = 0, ppal = _currentPal; i < PAL_ENTRIES; i++, ppal += 4) {
+ if (i < from || i >= from + numcolors)
+ palE = &_globalPalette[i];
+ else
+ palE = &srcPal[i];
+
+ new_entry = (int)(palE->red - palE->red * fpercent);
+
+ if (new_entry < 0) {
+ ppal[0] = 0;
+ } else {
+ ppal[0] = (byte)new_entry;
+ }
+
+ new_entry = (int)(palE->green - palE->green * fpercent);
+
+ if (new_entry < 0) {
+ ppal[1] = 0;
+ } else {
+ ppal[1] = (byte) new_entry;
+ }
+
+ new_entry = (int)(palE->blue - palE->blue * fpercent);
+
+ if (new_entry < 0) {
+ ppal[2] = 0;
+ } else {
+ ppal[2] = (byte) new_entry;
+ }
+ ppal[3] = 0;
+ }
+
+ // Make 256th color black. See bug #1256368
+ if ((_vm->getPlatform() == Common::kPlatformMacintosh) && !_vm->_scene->isInIntro())
+ memset(&_currentPal[255 * 4], 0, 4);
+
+ _system->setPalette(_currentPal, 0, PAL_ENTRIES);
+}
+
+void Gfx::showCursor(bool state) {
+ g_system->showMouse(state);
+}
+
+void Gfx::setCursor(CursorType cursorType) {
+ if (_vm->getGameType() == GType_ITE) {
+ // Set up the mouse cursor
+ const byte A = kITEColorLightGrey;
+ const byte B = kITEColorWhite;
+
+ const byte cursor_img[CURSOR_W * CURSOR_H] = {
+ 0, 0, 0, A, 0, 0, 0,
+ 0, 0, 0, A, 0, 0, 0,
+ 0, 0, 0, A, 0, 0, 0,
+ A, A, A, B, A, A, A,
+ 0, 0, 0, A, 0, 0, 0,
+ 0, 0, 0, A, 0, 0, 0,
+ 0, 0, 0, A, 0, 0, 0,
+ };
+
+ _system->setMouseCursor(cursor_img, CURSOR_W, CURSOR_H, 3, 3, 0);
+ } else {
+ uint32 resourceId;
+
+ switch (cursorType) {
+ case kCursorBusy:
+ resourceId = RID_IHNM_HOURGLASS_CURSOR;
+ break;
+ default:
+ resourceId = (uint32)-1;
+ break;
+ }
+
+ byte *resource;
+ size_t resourceLength;
+ byte *image;
+ size_t imageLength;
+ int width, height;
+
+ if (resourceId != (uint32)-1) {
+ ResourceContext *context = _vm->_resource->getContext(GAME_RESOURCEFILE);
+
+ _vm->_resource->loadResource(context, resourceId, resource, resourceLength);
+
+ _vm->decodeBGImage(resource, resourceLength, &image, &imageLength, &width, &height);
+ } else {
+ resource = NULL;
+ width = height = 31;
+ image = (byte *)calloc(width, height);
+
+ for (int i = 0; i < 14; i++) {
+ image[15 * 31 + i] = 1;
+ image[15 * 31 + 30 - i] = 1;
+ image[i * 31 + 15] = 1;
+ image[(30 - i) * 31 + 15] = 1;
+ }
+ }
+
+ // Note: Hard-coded hotspot
+ _system->setMouseCursor(image, width, height, 15, 15, 0);
+
+ free(image);
+ free(resource);
+ }
+}
+
+bool hitTestPoly(const Point *points, unsigned int npoints, const Point& test_point) {
+ int yflag0;
+ int yflag1;
+ bool inside_flag = false;
+ unsigned int pt;
+
+ const Point *vtx0 = &points[npoints - 1];
+ const Point *vtx1 = &points[0];
+
+ yflag0 = (vtx0->y >= test_point.y);
+ for (pt = 0; pt < npoints; pt++, vtx1++) {
+ yflag1 = (vtx1->y >= test_point.y);
+ if (yflag0 != yflag1) {
+ if (((vtx1->y - test_point.y) * (vtx0->x - vtx1->x) >=
+ (vtx1->x - test_point.x) * (vtx0->y - vtx1->y)) == yflag1) {
+ inside_flag = !inside_flag;
+ }
+ }
+ yflag0 = yflag1;
+ vtx0 = vtx1;
+ }
+
+ return inside_flag;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/gfx.h b/engines/saga/gfx.h
new file mode 100644
index 0000000000..ff96cbf081
--- /dev/null
+++ b/engines/saga/gfx.h
@@ -0,0 +1,164 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Graphics maniuplation routines - private header file
+
+#ifndef SAGA_GFX_H_
+#define SAGA_GFX_H_
+
+#include "graphics/surface.h"
+
+namespace Saga {
+
+using Common::Point;
+using Common::Rect;
+
+enum CursorType {
+ kCursorNormal,
+ kCursorBusy
+};
+
+struct ClipData {
+ // input members
+ Rect sourceRect;
+ Rect destRect;
+ Point destPoint;
+
+ // output members
+ Point drawSource;
+ Point drawDest;
+ int drawWidth;
+ int drawHeight;
+
+ bool calcClip() {
+ Common::Rect s;
+
+ // Adjust the rect to draw to its screen coordinates
+ s = sourceRect;
+ s.left += destPoint.x;
+ s.right += destPoint.x;
+ s.top += destPoint.y;
+ s.bottom += destPoint.y;
+
+ s.clip(destRect);
+
+ if ((s.width() <= 0) || (s.height() <= 0)) {
+ return false;
+ }
+
+ drawSource.x = s.left - sourceRect.left - destPoint.x;
+ drawSource.y = s.top - sourceRect.top - destPoint.y;
+ drawDest.x = s.left;
+ drawDest.y = s.top;
+ drawWidth = s.width();
+ drawHeight = s.height();
+
+ return true;
+ }
+};
+
+#pragma START_PACK_STRUCTS
+struct PalEntry {
+ byte red;
+ byte green;
+ byte blue;
+} GCC_PACK;
+
+#pragma END_PACK_STRUCTS
+
+struct Color {
+ int red;
+ int green;
+ int blue;
+ int alpha;
+};
+
+struct Surface : Graphics::Surface {
+
+ void transitionDissolve(const byte *sourceBuffer, const Common::Rect &sourceRect, int flags, double percent);
+ void drawPalette();
+ void drawPolyLine(const Point *points, int count, int color);
+ void blit(const Common::Rect &destRect, const byte *sourceBuffer);
+
+ void getRect(Common::Rect &rect) {
+ rect.left = rect.top = 0;
+ rect.right = w;
+ rect.bottom = h;
+ }
+ void drawFrame(const Common::Point &p1, const Common::Point &p2, int color) {
+ Common::Rect rect(MIN(p1.x, p2.x), MIN(p1.y, p2.y), MAX(p1.x, p2.x) + 1, MAX(p1.y, p2.y) + 1);
+ frameRect(rect, color);
+ }
+ void drawRect(const Common::Rect &destRect, int color) {
+ Common::Rect rect(w , h);
+ rect.clip(destRect);
+
+ if (rect.isValidRect()) {
+ fillRect(rect, color);
+ }
+ }
+};
+
+#define PAL_ENTRIES 256
+
+#define CURSOR_W 7
+#define CURSOR_H 7
+
+#define CURSOR_ORIGIN_X 4
+#define CURSOR_ORIGIN_Y 4
+
+bool hitTestPoly(const Point *points, unsigned int npoints, const Point& test_point);
+class SagaEngine;
+
+class Gfx {
+public:
+
+ Gfx(SagaEngine *vm, OSystem *system, int width, int height, GameDetector &detector);
+ ~Gfx();
+ Surface *getBackBuffer() {
+ return &_backBuffer;
+ }
+
+ void initPalette();
+ void setPalette(const PalEntry *pal, bool full = false);
+ void setPaletteColor(int n, int r, int g, int b);
+ void getCurrentPal(PalEntry *src_pal);
+ void palToBlack(PalEntry *src_pal, double percent);
+ void blackToPal(PalEntry *src_pal, double percent);
+ void showCursor(bool state);
+
+private:
+ void setCursor(CursorType cursorType = kCursorNormal);
+ int _init;
+ Surface _backBuffer;
+ byte _currentPal[PAL_ENTRIES * 4];
+ OSystem *_system;
+ SagaEngine *_vm;
+
+ PalEntry _globalPalette[PAL_ENTRIES];
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/ihnm_introproc.cpp b/engines/saga/ihnm_introproc.cpp
new file mode 100644
index 0000000000..881625c170
--- /dev/null
+++ b/engines/saga/ihnm_introproc.cpp
@@ -0,0 +1,331 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// "I Have No Mouth" Intro sequence scene procedures
+
+#include "saga/saga.h"
+#include "saga/gfx.h"
+
+#include "saga/animation.h"
+#include "saga/events.h"
+#include "saga/interface.h"
+#include "saga/sndres.h"
+#include "saga/music.h"
+
+#include "saga/scene.h"
+
+namespace Saga {
+
+SceneResourceData IHNM_IntroMovie1RL[] = {
+ {30, 2, 0, 0, false} ,
+ {31, 14, 0, 0, false}
+};
+
+SceneDescription IHNM_IntroMovie1Desc = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ IHNM_IntroMovie1RL,
+ ARRAYSIZE(IHNM_IntroMovie1RL)
+};
+
+SceneResourceData IHNM_IntroMovie2RL[] = {
+ {32, 2, 0, 0, false} ,
+ {33, 14, 0, 0, false}
+};
+
+SceneDescription IHNM_IntroMovie2Desc = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ IHNM_IntroMovie2RL,
+ ARRAYSIZE(IHNM_IntroMovie2RL)
+};
+
+SceneResourceData IHNM_IntroMovie3RL[] = {
+ {34, 2, 0, 0, false},
+ {35, 14, 0, 0, false}
+};
+
+SceneDescription IHNM_IntroMovie3Desc = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ IHNM_IntroMovie3RL,
+ ARRAYSIZE(IHNM_IntroMovie3RL)
+};
+
+SceneResourceData IHNM_IntroMovie4RL[] = {
+ {1227, 2, 0, 0, false},
+ {1226, 14, 0, 0, false}
+};
+
+SceneDescription IHNM_IntroMovie4Desc = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ IHNM_IntroMovie4RL,
+ ARRAYSIZE(IHNM_IntroMovie4RL)
+};
+
+LoadSceneParams IHNM_IntroList[] = {
+ {0, kLoadByDescription, &IHNM_IntroMovie1Desc, Scene::SC_IHNMIntroMovieProc1, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {0, kLoadByDescription, &IHNM_IntroMovie2Desc, Scene::SC_IHNMIntroMovieProc2, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {0, kLoadByDescription, &IHNM_IntroMovie3Desc, Scene::SC_IHNMIntroMovieProc3, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+};
+
+int Scene::IHNMStartProc() {
+ size_t n_introscenes;
+ size_t i;
+
+ LoadSceneParams firstScene;
+
+ // The original used the "play video" mechanism for the first part of
+ // the intro. We just use that panel mode.
+
+ _vm->_interface->setMode(kPanelVideo);
+
+ n_introscenes = ARRAYSIZE(IHNM_IntroList);
+
+ for (i = 0; i < n_introscenes; i++) {
+ _vm->_scene->queueScene(&IHNM_IntroList[i]);
+ }
+
+ firstScene.loadFlag = kLoadBySceneNumber;
+ firstScene.sceneDescriptor = -1;
+ firstScene.sceneDescription = NULL;
+ firstScene.sceneSkipTarget = true;
+ firstScene.sceneProc = NULL;
+ firstScene.transitionType = kTransitionFade;
+ firstScene.actorsEntrance = 0;
+ firstScene.chapter = -1;
+
+ _vm->_scene->queueScene(&firstScene);
+
+ return SUCCESS;
+}
+
+int Scene::SC_IHNMIntroMovieProc1(int param, void *refCon) {
+ return ((Scene *)refCon)->IHNMIntroMovieProc1(param);
+}
+
+int Scene::IHNMIntroMovieProc1(int param) {
+ Event event;
+ Event *q_event;
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Background for intro scene is the first frame of the
+ // intro animation; display it and set the palette
+ event.type = kEvTOneshot;
+ event.code = kBgEvent;
+ event.op = kEventDisplay;
+ event.param = kEvPSetPalette;
+ event.time = 0;
+
+ q_event = _vm->_events->queue(&event);
+
+ _vm->_anim->setFrameTime(0, IHNM_INTRO_FRAMETIME);
+ _vm->_anim->setFlag(0, ANIM_FLAG_ENDSCENE);
+
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventPlay;
+ event.param = 0;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_IHNMIntroMovieProc2(int param, void *refCon) {
+ return ((Scene *)refCon)->IHNMIntroMovieProc2(param);
+}
+
+int Scene::IHNMIntroMovieProc2(int param) {
+ Event event;
+ Event *q_event;
+ PalEntry *pal;
+
+ static PalEntry current_pal[PAL_ENTRIES];
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Fade to black out of the intro CyberDreams logo anim
+ _vm->_gfx->getCurrentPal(current_pal);
+
+ event.type = kEvTContinuous;
+ event.code = kPalEvent;
+ event.op = kEventPalToBlack;
+ event.time = 0;
+ event.duration = IHNM_PALFADE_TIME;
+ event.data = current_pal;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Background for intro scene is the first frame of the
+ // intro animation; display it but don't set palette
+ event.type = kEvTOneshot;
+ event.code = kBgEvent;
+ event.op = kEventDisplay;
+ event.param = kEvPNoSetPalette;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ _vm->_anim->setCycles(0, -1);
+
+ // Unlike the original, we keep the logo spinning during the
+ // palette fades. We don't have to, but I think it looks better
+ // that way.
+
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventPlay;
+ event.param = 0;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Fade in from black to the scene background palette
+ _vm->_scene->getBGPal(pal);
+
+ event.type = kEvTContinuous;
+ event.code = kPalEvent;
+ event.op = kEventBlackToPal;
+ event.time = 0;
+ event.duration = IHNM_PALFADE_TIME;
+ event.data = pal;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Fade to black after looping animation for a while
+ event.type = kEvTContinuous;
+ event.code = kPalEvent;
+ event.op = kEventPalToBlack;
+ event.time = IHNM_DGLOGO_TIME;
+ event.duration = IHNM_PALFADE_TIME;
+ event.data = pal;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Queue end of scene
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_IHNMIntroMovieProc3(int param, void *refCon) {
+ return ((Scene *)refCon)->IHNMIntroMovieProc3(param);
+}
+
+int Scene::IHNMIntroMovieProc3(int param) {
+ Event event;
+ Event *q_event;
+ PalEntry *pal;
+ static PalEntry current_pal[PAL_ENTRIES];
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Fade to black out of the intro DG logo anim
+ _vm->_gfx->getCurrentPal(current_pal);
+
+ event.type = kEvTContinuous;
+ event.code = kPalEvent;
+ event.op = kEventPalToBlack;
+ event.time = 0;
+ event.duration = IHNM_PALFADE_TIME;
+ event.data = current_pal;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Music, maestro
+
+ // In the GM file, this music also appears as tracks 7, 13, 19,
+ // 25 and 31, but only track 1 sounds right with the FM music.
+
+ event.type = kEvTOneshot;
+ event.code = kMusicEvent;
+ event.param = 1;
+ event.param2 = MUSIC_NORMAL;
+ event.op = kEventPlay;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Background for intro scene is the first frame of the intro
+ // animation; display it but don't set palette
+ event.type = kEvTOneshot;
+ event.code = kBgEvent;
+ event.op = kEventDisplay;
+ event.param = kEvPNoSetPalette;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Fade in from black to the scene background palette
+ _vm->_scene->getBGPal(pal);
+
+ event.type = kEvTContinuous;
+ event.code = kPalEvent;
+ event.op = kEventBlackToPal;
+ event.time = 0;
+ event.duration = IHNM_PALFADE_TIME;
+ event.data = pal;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventPlay;
+ event.param = 0;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Queue end of scene after a while
+ // TODO: I've increased the delay so the speech won't start
+ // until the music has ended. Could someone verify if that's
+ // the correct behaviour?
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = _vm->_music->hasAdlib() ? IHNM_TITLE_TIME_FM : IHNM_TITLE_TIME_GM;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/image.cpp b/engines/saga/image.cpp
new file mode 100644
index 0000000000..b7ac53f179
--- /dev/null
+++ b/engines/saga/image.cpp
@@ -0,0 +1,439 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// SAGA Image resource management routines
+#include "saga/saga.h"
+
+#include "saga/stream.h"
+
+namespace Saga {
+
+static int granulate(int value, int granularity) {
+ int remainder;
+
+ if (value == 0)
+ return 0;
+
+ if (granularity == 0)
+ return 0;
+
+ remainder = value % granularity;
+
+ if (remainder == 0) {
+ return value;
+ } else {
+ return (granularity - remainder + value);
+ }
+}
+
+int SagaEngine::decodeBGImage(const byte *image_data, size_t image_size,
+ byte **output_buf, size_t *output_buf_len, int *w, int *h, bool flip) {
+ ImageHeader hdr;
+ int modex_height;
+ const byte *RLE_data_ptr;
+ size_t RLE_data_len;
+ byte *decode_buf;
+ size_t decode_buf_len;
+ byte *out_buf;
+ size_t out_buf_len;
+
+ if (image_size <= SAGA_IMAGE_DATA_OFFSET) {
+ error("decodeBGImage() Image size is way too small (%d)", image_size);
+ }
+
+ MemoryReadStreamEndian readS(image_data, image_size, isBigEndian());
+
+ hdr.width = readS.readUint16();
+ hdr.height = readS.readUint16();
+ // The next four bytes of the image header aren't used.
+ readS.readUint16();
+ readS.readUint16();
+
+ RLE_data_ptr = image_data + SAGA_IMAGE_DATA_OFFSET;
+ RLE_data_len = image_size - SAGA_IMAGE_DATA_OFFSET;
+
+ modex_height = granulate(hdr.height, 4);
+
+ decode_buf_len = hdr.width * modex_height;
+ decode_buf = (byte *)malloc(decode_buf_len);
+
+ out_buf_len = hdr.width * hdr.height;
+ out_buf = (byte *)malloc(out_buf_len);
+
+ if (decodeBGImageRLE(RLE_data_ptr,
+ RLE_data_len, decode_buf, decode_buf_len) != SUCCESS) {
+ free(decode_buf);
+ free(out_buf);
+ return FAILURE;
+ }
+
+ unbankBGImage(out_buf, decode_buf, hdr.width, hdr.height);
+
+ // For some reason bg images in IHNM are upside down
+ if (getGameType() == GType_IHNM && !flip) {
+ flipImage(out_buf, hdr.width, hdr.height);
+ }
+
+ free(decode_buf);
+
+ *output_buf_len = out_buf_len;
+ *output_buf = out_buf;
+
+ *w = hdr.width;
+ *h = hdr.height;
+
+ return SUCCESS;
+}
+
+int SagaEngine::decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outbuf, size_t outbuf_len) {
+ const byte *inbuf_ptr;
+ byte *outbuf_ptr;
+ uint32 inbuf_remain;
+
+ const byte *inbuf_end;
+ byte *outbuf_end;
+ uint32 outbuf_remain;
+
+ byte mark_byte;
+ int test_byte;
+
+ uint32 runcount;
+
+ byte bitfield;
+ byte bitfield_byte1;
+ byte bitfield_byte2;
+
+ byte *backtrack_ptr;
+ int backtrack_amount;
+
+ uint16 c, b;
+
+ int decode_err = 0;
+
+ inbuf_ptr = inbuf;
+ inbuf_remain = inbuf_len;
+
+ outbuf_ptr = outbuf;
+ outbuf_remain = outbuf_len;
+
+ inbuf_end = (inbuf + inbuf_len) - 1;
+ outbuf_end = (outbuf + outbuf_len) - 1;
+
+ memset(outbuf, 0, outbuf_len);
+
+ while ((inbuf_remain > 1) && (outbuf_remain > 0) && !decode_err) {
+
+ if ((inbuf_ptr > inbuf_end) || (outbuf_ptr > outbuf_end)) {
+ return FAILURE;
+ }
+
+ mark_byte = *inbuf_ptr++;
+ inbuf_remain--;
+
+ test_byte = mark_byte & 0xC0; // Mask all but two high order bits
+
+ switch (test_byte) {
+ case 0xC0: // 1100 0000
+ // Uncompressed run follows: Max runlength 63
+ runcount = mark_byte & 0x3f;
+ if ((inbuf_remain < runcount) || (outbuf_remain < runcount)) {
+ return FAILURE;
+ }
+
+ for (c = 0; c < runcount; c++) {
+ *outbuf_ptr++ = *inbuf_ptr++;
+ }
+
+ inbuf_remain -= runcount;
+ outbuf_remain -= runcount;
+ continue;
+ break;
+ case 0x80: // 1000 0000
+ // Compressed run follows: Max runlength 63
+ runcount = (mark_byte & 0x3f) + 3;
+ if (!inbuf_remain || (outbuf_remain < runcount)) {
+ return FAILURE;
+ }
+
+ for (c = 0; c < runcount; c++) {
+ *outbuf_ptr++ = *inbuf_ptr;
+ }
+
+ inbuf_ptr++;
+ inbuf_remain--;
+ outbuf_remain -= runcount;
+ continue;
+
+ break;
+
+ case 0x40: // 0100 0000
+ // Repeat decoded sequence from output stream:
+ // Max runlength 10
+
+ runcount = ((mark_byte >> 3) & 0x07U) + 3;
+ backtrack_amount = *inbuf_ptr;
+
+ if (!inbuf_remain || (backtrack_amount > (outbuf_ptr - outbuf)) || (runcount > outbuf_remain)) {
+ return FAILURE;
+ }
+
+ inbuf_ptr++;
+ inbuf_remain--;
+
+ backtrack_ptr = outbuf_ptr - backtrack_amount;
+
+ for (c = 0; c < runcount; c++) {
+ *outbuf_ptr++ = *backtrack_ptr++;
+ }
+
+ outbuf_remain -= runcount;
+ continue;
+ break;
+ default: // 0000 0000
+ break;
+ }
+
+ // Mask all but the third and fourth highest order bits
+ test_byte = mark_byte & 0x30;
+
+ switch (test_byte) {
+
+ case 0x30: // 0011 0000
+ // Bitfield compression
+ runcount = (mark_byte & 0x0F) + 1;
+
+ if ((inbuf_remain < (runcount + 2)) || (outbuf_remain < (runcount * 8))) {
+ return FAILURE;
+ }
+
+ bitfield_byte1 = *inbuf_ptr++;
+ bitfield_byte2 = *inbuf_ptr++;
+
+ for (c = 0; c < runcount; c++) {
+ bitfield = *inbuf_ptr;
+ for (b = 0; b < 8; b++) {
+ if (bitfield & 0x80) {
+ *outbuf_ptr = bitfield_byte2;
+ } else {
+ *outbuf_ptr = bitfield_byte1;
+ }
+ bitfield <<= 1;
+ outbuf_ptr++;
+ }
+ inbuf_ptr++;
+ }
+
+ inbuf_remain -= (runcount + 2);
+ outbuf_remain -= (runcount * 8);
+ continue;
+ break;
+ case 0x20: // 0010 0000
+ // Uncompressed run follows
+ runcount = ((mark_byte & 0x0F) << 8) + *inbuf_ptr;
+ if ((inbuf_remain < (runcount + 1)) || (outbuf_remain < runcount)) {
+ return FAILURE;
+ }
+
+ inbuf_ptr++;
+
+ for (c = 0; c < runcount; c++) {
+ *outbuf_ptr++ = *inbuf_ptr++;
+ }
+
+ inbuf_remain -= (runcount + 1);
+ outbuf_remain -= runcount;
+ continue;
+
+ break;
+
+ case 0x10: // 0001 0000
+ // Repeat decoded sequence from output stream
+ backtrack_amount = ((mark_byte & 0x0F) << 8) + *inbuf_ptr;
+ if (inbuf_remain < 2) {
+ return FAILURE;
+ }
+
+ inbuf_ptr++;
+ runcount = *inbuf_ptr++;
+
+ if ((backtrack_amount > (outbuf_ptr - outbuf)) || (outbuf_remain < runcount)) {
+ return FAILURE;
+ }
+
+ backtrack_ptr = outbuf_ptr - backtrack_amount;
+
+ for (c = 0; c < runcount; c++) {
+ *outbuf_ptr++ = *backtrack_ptr++;
+ }
+
+ inbuf_remain -= 2;
+ outbuf_remain -= runcount;
+ continue;
+ break;
+ default:
+ return FAILURE;
+ break;
+ }
+ }
+
+ return SUCCESS;
+}
+
+int SagaEngine::flipImage(byte *img_buf, int columns, int scanlines) {
+ int line;
+ byte *tmp_scan;
+
+ byte *flip_p1;
+ byte *flip_p2;
+
+ int flipcount = scanlines / 2;
+
+ tmp_scan = (byte *)malloc(columns);
+ if (tmp_scan == NULL) {
+ return FAILURE;
+ }
+
+ flip_p1 = img_buf;
+ flip_p2 = img_buf + (columns * (scanlines - 1));
+
+ for (line = 0; line < flipcount; line++) {
+ memcpy(tmp_scan, flip_p1, columns);
+ memcpy(flip_p1, flip_p2, columns);
+ memcpy(flip_p2, tmp_scan, columns);
+ flip_p1 += columns;
+ flip_p2 -= columns;
+ }
+
+ free(tmp_scan);
+
+ return SUCCESS;
+}
+
+int SagaEngine::unbankBGImage(byte *dst_buf, const byte *src_buf, int columns, int scanlines) {
+ int x, y;
+ int temp;
+ int quadruple_rows;
+ int remain_rows;
+ int rowjump_src;
+ int rowjump_dest;
+ const byte *src_p;
+ byte *dst_p;
+ const byte *srcptr1, *srcptr2, *srcptr3, *srcptr4;
+ byte *dstptr1, *dstptr2, *dstptr3, *dstptr4;
+
+ quadruple_rows = scanlines - (scanlines % 4);
+ remain_rows = scanlines - quadruple_rows;
+
+ assert(scanlines > 0);
+
+ src_p = src_buf;
+ dst_p = dst_buf + columns;
+
+ srcptr1 = src_p;
+ srcptr2 = src_p + 1;
+ srcptr3 = src_p + 2;
+ srcptr4 = src_p + 3;
+
+ dstptr1 = dst_buf;
+ dstptr2 = dst_buf + columns;
+ dstptr3 = dst_buf + columns * 2;
+ dstptr4 = dst_buf + columns * 3;
+
+ rowjump_src = columns * 4;
+ rowjump_dest = columns * 4;
+
+ // Unbank groups of 4 first
+ for (y = 0; y < quadruple_rows; y += 4) {
+ for (x = 0; x < columns; x++) {
+ temp = x * 4;
+ dstptr1[x] = srcptr1[temp];
+ dstptr2[x] = srcptr2[temp];
+ dstptr3[x] = srcptr3[temp];
+ dstptr4[x] = srcptr4[temp];
+ }
+
+ // This is to avoid generating invalid pointers -
+ // usually innocuous, but undefined
+ if (y < quadruple_rows - 4) {
+ dstptr1 += rowjump_dest;
+ dstptr2 += rowjump_dest;
+ dstptr3 += rowjump_dest;
+ dstptr4 += rowjump_dest;
+ srcptr1 += rowjump_src;
+ srcptr2 += rowjump_src;
+ srcptr3 += rowjump_src;
+ srcptr4 += rowjump_src;
+ }
+ }
+
+ // Unbank rows remaining
+ switch (remain_rows) {
+ case 1:
+ dstptr1 += rowjump_dest;
+ srcptr1 += rowjump_src;
+ for (x = 0; x < columns; x++) {
+ temp = x * 4;
+ dstptr1[x] = srcptr1[temp];
+ }
+ break;
+ case 2:
+ dstptr1 += rowjump_dest;
+ dstptr2 += rowjump_dest;
+ srcptr1 += rowjump_src;
+ srcptr2 += rowjump_src;
+ for (x = 0; x < columns; x++) {
+ temp = x * 4;
+ dstptr1[x] = srcptr1[temp];
+ dstptr2[x] = srcptr2[temp];
+ }
+ break;
+ case 3:
+ dstptr1 += rowjump_dest;
+ dstptr2 += rowjump_dest;
+ dstptr3 += rowjump_dest;
+ srcptr1 += rowjump_src;
+ srcptr2 += rowjump_src;
+ srcptr3 += rowjump_src;
+ for (x = 0; x < columns; x++) {
+ temp = x * 4;
+ dstptr1[x] = srcptr1[temp];
+ dstptr2[x] = srcptr2[temp];
+ dstptr3[x] = srcptr3[temp];
+ }
+ break;
+ default:
+ break;
+ }
+ return SUCCESS;
+}
+
+const byte *SagaEngine::getImagePal(const byte *image_data, size_t image_size) {
+ if (image_size <= SAGA_IMAGE_HEADER_LEN) {
+ return NULL;
+ }
+
+ return image_data + SAGA_IMAGE_HEADER_LEN;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/input.cpp b/engines/saga/input.cpp
new file mode 100644
index 0000000000..0abce03a31
--- /dev/null
+++ b/engines/saga/input.cpp
@@ -0,0 +1,163 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "saga/saga.h"
+
+#include "saga/gfx.h"
+#include "saga/actor.h"
+#include "saga/console.h"
+#include "saga/interface.h"
+#include "saga/render.h"
+#include "saga/scene.h"
+#include "saga/script.h"
+#include "saga/isomap.h"
+
+#include "common/system.h"
+
+namespace Saga {
+
+int SagaEngine::processInput() {
+ OSystem::Event event;
+
+// Point imousePt;
+
+ while (g_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.flags == OSystem::KBD_CTRL) {
+ if (event.kbd.keycode == 'd')
+ _console->attach();
+ }
+ if (_interface->_textInput || _interface->_statusTextInput) {
+ _interface->processAscii(event.kbd.ascii);
+ return SUCCESS;
+ }
+
+ switch (event.kbd.keycode) {
+ case '#':
+ case '`':
+ case '~':
+ _console->attach();
+ break;
+ case 'r':
+ _interface->draw();
+ break;
+
+#if 0
+ case 269:
+ case 270:
+ case 273:
+ case 274:
+ case 275:
+ case 276:
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _vm->_isoMap->_viewDiff += (event.kbd.keycode == 270) - (event.kbd.keycode == 269);
+ _vm->_isoMap->_viewScroll.y += (_vm->_isoMap->_viewDiff * (event.kbd.keycode == 274) - _vm->_isoMap->_viewDiff * (event.kbd.keycode == 273));
+ _vm->_isoMap->_viewScroll.x += (_vm->_isoMap->_viewDiff * (event.kbd.keycode == 275) - _vm->_isoMap->_viewDiff * (event.kbd.keycode == 276));
+ }
+ break;
+#endif
+ case 282: // F1
+ _render->toggleFlag(RF_SHOW_FPS);
+ _actor->_handleActionDiv = (_actor->_handleActionDiv == 15) ? 50 : 15;
+ break;
+ case 283: // F2
+ _render->toggleFlag(RF_PALETTE_TEST);
+ break;
+ case 284: // F3
+ _render->toggleFlag(RF_TEXT_TEST);
+ break;
+ case 285: // F4
+ _render->toggleFlag(RF_OBJECTMAP_TEST);
+ break;
+ case 286: // F5
+ if (_interface->getSaveReminderState() > 0)
+ _interface->setMode(kPanelOption);
+ break;
+ case 287: // F6
+ _render->toggleFlag(RF_ACTOR_PATH_TEST);
+ break;
+ case 288: // F7
+ //_actor->frameTest();
+ break;
+ case 289: // F8
+ break;
+ case 290: // F9
+ _interface->keyBoss();
+ break;
+
+ // Actual game keys
+ case 32: // space
+ _actor->abortSpeech();
+ break;
+ case 19: // pause
+ case 'z':
+ _render->toggleFlag(RF_RENDERPAUSE);
+ break;
+ default:
+ _interface->processAscii(event.kbd.ascii);
+ break;
+ }
+ break;
+ case OSystem::EVENT_KEYUP:
+ _interface->processKeyUp(event.kbd.ascii);
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _leftMouseButtonPressed = false;
+ break;
+ case OSystem::EVENT_RBUTTONUP:
+ _rightMouseButtonPressed = false;
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ _leftMouseButtonPressed = true;
+ _mousePos = event.mouse;
+ _interface->update(_mousePos, UPDATE_LEFTBUTTONCLICK);
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+ _rightMouseButtonPressed = true;
+ _mousePos = event.mouse;
+ _interface->update(_mousePos, UPDATE_RIGHTBUTTONCLICK);
+ break;
+ case OSystem::EVENT_WHEELUP:
+ _interface->update(_mousePos, UPDATE_WHEELUP);
+ break;
+ case OSystem::EVENT_WHEELDOWN:
+ _interface->update(_mousePos, UPDATE_WHEELDOWN);
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mousePos = event.mouse;
+ break;
+ case OSystem::EVENT_QUIT:
+ shutDown();
+ break;
+ default:
+ break;
+ }
+ }
+
+ return SUCCESS;
+}
+
+
+} // End of namespace Saga
+
diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
new file mode 100644
index 0000000000..1d3b110068
--- /dev/null
+++ b/engines/saga/interface.cpp
@@ -0,0 +1,2453 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Game interface module
+#include "saga/saga.h"
+
+#include "saga/gfx.h"
+#include "saga/actor.h"
+#include "saga/console.h"
+#include "saga/events.h"
+#include "saga/font.h"
+#include "saga/objectmap.h"
+#include "saga/isomap.h"
+#include "saga/itedata.h"
+#include "saga/music.h"
+#include "saga/puzzle.h"
+#include "saga/render.h"
+#include "saga/scene.h"
+#include "saga/script.h"
+#include "saga/sound.h"
+#include "saga/sprite.h"
+#include "saga/rscfile.h"
+#include "saga/resnames.h"
+
+#include "saga/interface.h"
+
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "common/timer.h"
+
+namespace Saga {
+
+static int verbTypeToTextStringsIdLUT[2][kVerbTypeIdsMax] = {
+ {-1,
+ kTextPickUp,
+ kTextLookAt,
+ kTextWalkTo,
+ kTextTalkTo,
+ kTextOpen,
+ kTextClose,
+ kTextGive,
+ kTextUse,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1},
+ {-1,
+ 3, //TODO:check
+ 2,
+ 1,
+ 5,
+ 6, //TODO:check
+ 8, //TODO:check
+ 7,
+ 4}
+};
+
+Interface::Interface(SagaEngine *vm) : _vm(vm) {
+ byte *resource;
+ size_t resourceLength;
+ int i;
+
+ // Load interface module resource file context
+ _interfaceContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ if (_interfaceContext == NULL) {
+ error("Interface::Interface() resource context not found");
+ }
+
+ _mainPanel.buttons = _vm->getDisplayInfo().mainPanelButtons;
+ _mainPanel.buttonsCount = _vm->getDisplayInfo().mainPanelButtonsCount;
+
+ for (i = 0; i < kVerbTypeIdsMax; i++) {
+ _verbTypeToPanelButton[i] = NULL;
+ }
+
+ for (i = 0; i < _mainPanel.buttonsCount; i++) {
+ if (_mainPanel.buttons[i].type == kPanelButtonVerb) {
+ _verbTypeToPanelButton[_mainPanel.buttons[i].id] = &_mainPanel.buttons[i];
+ }
+ }
+
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->mainPanelResourceId, resource, resourceLength);
+ _vm->decodeBGImage(resource, resourceLength, &_mainPanel.image,
+ &_mainPanel.imageLength, &_mainPanel.imageWidth, &_mainPanel.imageHeight);
+
+ free(resource);
+
+ _conversePanel.buttons = _vm->getDisplayInfo().conversePanelButtons;
+ _conversePanel.buttonsCount = _vm->getDisplayInfo().conversePanelButtonsCount;
+
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->conversePanelResourceId, resource, resourceLength);
+ _vm->decodeBGImage(resource, resourceLength, &_conversePanel.image,
+ &_conversePanel.imageLength, &_conversePanel.imageWidth, &_conversePanel.imageHeight);
+ free(resource);
+
+ _optionPanel.buttons = _vm->getDisplayInfo().optionPanelButtons;
+ _optionPanel.buttonsCount = _vm->getDisplayInfo().optionPanelButtonsCount;
+
+ _vm->_resource->loadResource(_interfaceContext, _vm->getResourceDescription()->optionPanelResourceId, resource, resourceLength);
+ _vm->decodeBGImage(resource, resourceLength, &_optionPanel.image,
+ &_optionPanel.imageLength, &_optionPanel.imageWidth, &_optionPanel.imageHeight);
+ free(resource);
+
+ _vm->_sprite->loadList(_vm->getResourceDescription()->mainPanelSpritesResourceId, _mainPanel.sprites);
+
+ if (_vm->getGameType() == GType_ITE) {
+ _vm->_sprite->loadList(_vm->getResourceDescription()->defaultPortraitsResourceId, _defPortraits);
+ }
+
+ setPortraitBgColor(0, 0, 0);
+
+ _mainPanel.x = _vm->getDisplayInfo().mainPanelXOffset;
+ _mainPanel.y = _vm->getDisplayInfo().mainPanelYOffset;
+ _mainPanel.currentButton = NULL;
+ _inventoryUpButton = _mainPanel.getButton(_vm->getDisplayInfo().inventoryUpButtonIndex);
+ _inventoryDownButton = _mainPanel.getButton(_vm->getDisplayInfo().inventoryDownButtonIndex);
+
+ _conversePanel.x = _vm->getDisplayInfo().conversePanelXOffset;
+ _conversePanel.y = _vm->getDisplayInfo().conversePanelYOffset;
+ _conversePanel.currentButton = NULL;
+ _converseUpButton = _conversePanel.getButton(_vm->getDisplayInfo().converseUpButtonIndex);
+ _converseDownButton = _conversePanel.getButton(_vm->getDisplayInfo().converseDownButtonIndex);
+
+ _leftPortrait = 0;
+ _rightPortrait = 0;
+
+ _optionPanel.x = _vm->getDisplayInfo().optionPanelXOffset;
+ _optionPanel.y = _vm->getDisplayInfo().optionPanelYOffset;
+ _optionPanel.currentButton = NULL;
+ _optionSaveFileSlider = _optionPanel.getButton(_vm->getDisplayInfo().optionSaveFileSliderIndex);
+ _optionSaveFilePanel = _optionPanel.getButton(_vm->getDisplayInfo().optionSaveFilePanelIndex);
+
+ _quitPanel.x = _vm->getDisplayInfo().quitPanelXOffset;
+ _quitPanel.y = _vm->getDisplayInfo().quitPanelYOffset;
+ _quitPanel.imageWidth = _vm->getDisplayInfo().quitPanelWidth;
+ _quitPanel.imageHeight = _vm->getDisplayInfo().quitPanelHeight;
+ _quitPanel.buttons = _vm->getDisplayInfo().quitPanelButtons;
+ _quitPanel.buttonsCount = _vm->getDisplayInfo().quitPanelButtonsCount;
+ _quitPanel.currentButton = NULL;
+
+ _loadPanel.x = _vm->getDisplayInfo().loadPanelXOffset;
+ _loadPanel.y = _vm->getDisplayInfo().loadPanelYOffset;
+ _loadPanel.imageWidth = _vm->getDisplayInfo().loadPanelWidth;
+ _loadPanel.imageHeight = _vm->getDisplayInfo().loadPanelHeight;
+ _loadPanel.buttons = _vm->getDisplayInfo().loadPanelButtons;
+ _loadPanel.buttonsCount = _vm->getDisplayInfo().loadPanelButtonsCount;
+ _loadPanel.currentButton = NULL;
+
+ _savePanel.x = _vm->getDisplayInfo().savePanelXOffset;
+ _savePanel.y = _vm->getDisplayInfo().savePanelYOffset;
+ _savePanel.imageWidth = _vm->getDisplayInfo().savePanelWidth;
+ _savePanel.imageHeight = _vm->getDisplayInfo().savePanelHeight;
+ _savePanel.buttons = _vm->getDisplayInfo().savePanelButtons;
+ _savePanel.buttonsCount = _vm->getDisplayInfo().savePanelButtonsCount;
+ _saveEdit = _savePanel.getButton(_vm->getDisplayInfo().saveEditIndex);
+ _savePanel.currentButton = NULL;
+
+ _protectPanel.x = _vm->getDisplayInfo().protectPanelXOffset;
+ _protectPanel.y = _vm->getDisplayInfo().protectPanelYOffset;
+ _protectPanel.imageWidth = _vm->getDisplayInfo().protectPanelWidth;
+ _protectPanel.imageHeight = _vm->getDisplayInfo().protectPanelHeight;
+ _protectPanel.buttons = _vm->getDisplayInfo().protectPanelButtons;
+ _protectPanel.buttonsCount = _vm->getDisplayInfo().protectPanelButtonsCount;
+ _protectEdit = _protectPanel.getButton(_vm->getDisplayInfo().protectEditIndex);
+ _protectPanel.currentButton = NULL;
+
+ _active = true;
+ _panelMode = _lockedMode = kPanelNull;
+ _savedMode = -1;
+ _bossMode = -1;
+ _fadeMode = kNoFade;
+ _inMainMode = false;
+ *_statusText = 0;
+ _statusOnceColor = -1;
+
+ _inventoryCount = 0;
+ _inventoryPos = 0;
+ _inventoryStart = 0;
+ _inventoryEnd = 0;
+ _inventoryBox = 0;
+ _inventorySize = ITE_INVENTORY_SIZE;
+ _saveReminderState = 0;
+
+ _optionSaveFileTop = 0;
+ _optionSaveFileTitleNumber = 0;
+
+ _inventory = (uint16 *)calloc(_inventorySize, sizeof(uint16));
+ if (_inventory == NULL) {
+ error("Interface::Interface(): not enough memory");
+ }
+
+ _textInputRepeatPhase = 0;
+ _textInput = false;
+ _statusTextInput = false;
+ _statusTextInputState = kStatusTextInputFirstRun;
+
+ _disableAbortSpeeches = false;
+}
+
+Interface::~Interface(void) {
+ free(_inventory);
+
+ _mainPanel.sprites.freeMem();
+ _defPortraits.freeMem();
+ _scenePortraits.freeMem();
+}
+
+int Interface::activate() {
+ if (!_active) {
+ _active = true;
+ _vm->_script->_skipSpeeches = false;
+ _vm->_actor->_protagonist->_targetObject = ID_NOTHING;
+ unlockMode();
+ if (_panelMode == kPanelMain){
+ _saveReminderState = 1;
+ }
+ draw();
+ }
+ _vm->_gfx->showCursor(true);
+
+ return SUCCESS;
+}
+
+int Interface::deactivate() {
+ if (_active) {
+ _active = false;
+ lockMode();
+ setMode(kPanelNull);
+ }
+ _vm->_gfx->showCursor(false);
+
+ return SUCCESS;
+}
+
+void Interface::rememberMode() {
+ assert (_savedMode == -1);
+
+ _savedMode = _panelMode;
+}
+
+void Interface::restoreMode() {
+ assert (_savedMode != -1);
+
+ _panelMode = _savedMode;
+ _savedMode = -1;
+
+ draw();
+}
+
+void Interface::setMode(int mode) {
+ debug(1, "Interface::setMode %i", mode);
+
+ if (mode == kPanelMain) {
+ _inMainMode = true;
+ _saveReminderState = 1; //TODO: blinking timeout
+ } else {
+ if (mode == kPanelConverse) {
+ _inMainMode = false;
+ }
+ _saveReminderState = 0;
+ }
+
+ _panelMode = mode;
+
+ switch (_panelMode) {
+ case kPanelMain:
+ if (_vm->getGameType() == GType_IHNM)
+ warning("FIXME: Implement IHNM differences from ExecuteInventoryPanel");
+
+ _mainPanel.currentButton = NULL;
+ break;
+ case kPanelConverse:
+ _conversePanel.currentButton = NULL;
+ converseDisplayText();
+ break;
+ case kPanelOption:
+ _optionPanel.currentButton = NULL;
+ _vm->fillSaveList();
+ calcOptionSaveSlider();
+ if (_optionSaveFileTitleNumber >= _vm->getDisplayInfo().optionSaveFileVisible) {
+ _optionSaveFileTitleNumber = _vm->getDisplayInfo().optionSaveFileVisible - 1;
+ }
+ break;
+ case kPanelLoad:
+ _loadPanel.currentButton = NULL;
+ break;
+ case kPanelQuit:
+ _quitPanel.currentButton = NULL;
+ break;
+ case kPanelSave:
+ _savePanel.currentButton = NULL;
+ _textInputMaxWidth = _saveEdit->width - 10;
+ _textInput = true;
+ _textInputStringLength = strlen(_textInputString);
+ _textInputPos = _textInputStringLength + 1;
+ _textInputRepeatPhase = 0;
+ break;
+ case kPanelMap:
+ mapPanelShow();
+ break;
+ case kPanelSceneSubstitute:
+ _vm->_render->setFlag(RF_DEMO_SUBST);
+ _vm->_gfx->getCurrentPal(_mapSavedPal);
+ break;
+ case kPanelChapterSelection:
+ break;
+ case kPanelBoss:
+ _vm->_render->setFlag(RF_DEMO_SUBST);
+ break;
+ case kPanelProtect:
+ _protectPanel.currentButton = NULL;
+ _textInputMaxWidth = _protectEdit->width - 10;
+ _textInput = true;
+ _textInputString[0] = 0;
+ _textInputStringLength = 0;
+ _textInputPos = _textInputStringLength + 1;
+ _textInputRepeatPhase = 0;
+ break;
+ }
+
+ draw();
+}
+
+bool Interface::processAscii(uint16 ascii, bool synthetic) {
+ // TODO: Checking for Esc and Enter below is a bit hackish, and
+ // and probably only works with the English version. Maybe we should
+ // add a flag to the button so it can indicate if it's the default or
+ // cancel button?
+
+ int i;
+ PanelButton *panelButton;
+ if (!synthetic)
+ _textInputRepeatPhase = 0;
+ if (_statusTextInput) {
+ processStatusTextInput(ascii);
+ return true;
+ }
+
+ switch (_panelMode) {
+ case kPanelNull:
+ if (ascii == 27) { // Esc
+ if (_vm->_scene->isInIntro()) {
+ _vm->_scene->skipScene();
+ } else {
+ if (!_disableAbortSpeeches)
+ _vm->_actor->abortAllSpeeches();
+ }
+ return true;
+ }
+ break;
+ case kPanelCutaway:
+ if (ascii == 27) { // Esc
+ if (!_disableAbortSpeeches)
+ _vm->_actor->abortAllSpeeches();
+ _vm->_scene->cutawaySkip();
+ return true;
+ }
+ break;
+ case kPanelVideo:
+ if (ascii == 27) { // Esc
+ if (_vm->_scene->isInIntro()) {
+ _vm->_scene->skipScene();
+ } else {
+ if (!_disableAbortSpeeches)
+ _vm->_actor->abortAllSpeeches();
+ }
+ _vm->_scene->cutawaySkip();
+ }
+ break;
+ case kPanelOption:
+ // TODO: check input dialog keys
+ if (ascii == 27 || ascii == 13) { // Esc or Enter
+ ascii = 'c'; //continue
+ }
+
+ for (i = 0; i < _optionPanel.buttonsCount; i++) {
+ panelButton = &_optionPanel.buttons[i];
+ if (panelButton->type == kPanelButtonOption) {
+ if (panelButton->ascii == ascii) {
+ setOption(panelButton);
+ return true;
+ }
+ }
+ }
+ break;
+ case kPanelSave:
+ if (_textInput && processTextInput(ascii)) {
+ return true;
+ }
+
+ if (ascii == 27) { // Esc
+ ascii = 'c'; // cancel
+ } else if (ascii == 13) { // Enter
+ ascii = 's'; // save
+ }
+
+ for (i = 0; i < _savePanel.buttonsCount; i++) {
+ panelButton = &_savePanel.buttons[i];
+ if (panelButton->type == kPanelButtonSave) {
+ if (panelButton->ascii == ascii) {
+ setSave(panelButton);
+ return true;
+ }
+ }
+ }
+ break;
+ case kPanelQuit:
+ if (ascii == 27) { // Esc
+ ascii = 'c'; // cancel
+ } else if (ascii == 13) { // Enter
+ ascii = 'q'; // quit
+ }
+
+ for (i = 0; i < _quitPanel.buttonsCount; i++) {
+ panelButton = &_quitPanel.buttons[i];
+ if (panelButton->type == kPanelButtonQuit) {
+ if (panelButton->ascii == ascii) {
+ setQuit(panelButton);
+ return true;
+ }
+ }
+ }
+ break;
+ case kPanelLoad:
+ for (i = 0; i < _loadPanel.buttonsCount; i++) {
+ panelButton = &_loadPanel.buttons[i];
+ if (panelButton->type == kPanelButtonLoad) {
+ if (panelButton->ascii == ascii) {
+ setLoad(panelButton);
+ return true;
+ }
+ }
+ }
+ break;
+ case kPanelMain:
+ for (i = 0; i < _mainPanel.buttonsCount; i++) {
+ panelButton = &_mainPanel.buttons[i];
+ if (panelButton->ascii == ascii) {
+ if (panelButton->type == kPanelButtonVerb) {
+ _vm->_script->setVerb(panelButton->id);
+ }
+ if (panelButton->type == kPanelButtonArrow) {
+ inventoryChangePos(panelButton->id);
+ }
+ return true;
+ }
+ }
+ if (ascii == 15) // ctrl-o
+ {
+ if (_saveReminderState > 0) {
+ setMode(kPanelOption);
+ return true;
+ }
+ }
+ break;
+ case kPanelConverse:
+ switch (ascii) {
+ case 'x':
+ setMode(kPanelMain);
+ if (_vm->_puzzle->isActive())
+ _vm->_puzzle->exitPuzzle();
+ break;
+
+ case 'u':
+ converseChangePos(-1);
+ break;
+
+ case 'd':
+ converseChangePos(1);
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ converseSetPos(ascii);
+ break;
+ }
+ break;
+ case kPanelMap:
+ mapPanelClean();
+ break;
+ case kPanelSceneSubstitute:
+ if (ascii == 13) {
+ _vm->_render->clearFlag(RF_DEMO_SUBST);
+ _vm->_gfx->setPalette(_mapSavedPal);
+ setMode(kPanelMain);
+ _vm->_script->setNoPendingVerb();
+ } else if (ascii == 'q' || ascii == 'Q') {
+ _vm->shutDown();
+ }
+ break;
+ case kPanelBoss:
+ _vm->_render->clearFlag(RF_DEMO_SUBST);
+ keyBossExit();
+ break;
+ case kPanelProtect:
+ if (_textInput && processTextInput(ascii)) {
+ return true;
+ }
+
+ if (ascii == 27 || ascii == 13) { // Esc or Enter
+ _vm->_script->wakeUpThreads(kWaitTypeRequest);
+ _vm->_interface->setMode(kPanelMain);
+
+ _protectHash = 0;
+
+ for (char *p = _textInputString; *p; p++)
+ _protectHash = (_protectHash << 1) + toupper(*p);
+ }
+ break;
+ }
+ return false;
+}
+
+#define KEYBOARD_REPEAT_DELAY1 300000L
+#define KEYBOARD_REPEAT_DELAY2 50000L
+
+void Interface::textInputRepeatCallback(void *refCon) {
+ ((Interface *)refCon)->textInputRepeat();
+}
+
+void Interface::textInputStartRepeat(uint16 ascii) {
+ if (!_textInputRepeatPhase) {
+ _textInputRepeatPhase = 1;
+ Common::g_timer->removeTimerProc(&textInputRepeatCallback);
+ Common::g_timer->installTimerProc(&textInputRepeatCallback, KEYBOARD_REPEAT_DELAY1, this);
+ }
+
+ _textInputRepeatChar = ascii;
+}
+
+void Interface::textInputRepeat() {
+ if (_textInputRepeatPhase == 1) {
+ _textInputRepeatPhase = 2;
+ Common::g_timer->removeTimerProc(&textInputRepeatCallback);
+ Common::g_timer->installTimerProc(&textInputRepeatCallback, KEYBOARD_REPEAT_DELAY2, this);
+ } else if (_textInputRepeatPhase == 2) {
+ processAscii(_textInputRepeatChar, true);
+ }
+}
+
+void Interface::processKeyUp(uint16 ascii) {
+ if (_textInputRepeatPhase) {
+ Common::g_timer->removeTimerProc(&textInputRepeatCallback);
+ _textInputRepeatPhase = 0;
+ }
+}
+
+void Interface::setStatusText(const char *text, int statusColor) {
+ assert(text != NULL);
+ assert(strlen(text) < STATUS_TEXT_LEN);
+
+ if (_vm->_render->getFlags() & (RF_PLACARD | RF_MAP))
+ return;
+
+ strncpy(_statusText, text, STATUS_TEXT_LEN);
+ _statusOnceColor = statusColor;
+ drawStatusBar();
+}
+
+void Interface::loadScenePortraits(int resourceId) {
+ _scenePortraits.freeMem();
+
+ _vm->_sprite->loadList(resourceId, _scenePortraits);
+}
+
+void Interface::drawVerbPanel(Surface *backBuffer, PanelButton* panelButton) {
+ PanelButton * rightButtonVerbPanelButton;
+ PanelButton * currentVerbPanelButton;
+ KnownColor textColor;
+ int spriteNumber;
+ Point point;
+
+ rightButtonVerbPanelButton = getPanelButtonByVerbType(_vm->_script->getRightButtonVerb());
+ currentVerbPanelButton = getPanelButtonByVerbType(_vm->_script->getCurrentVerb());
+
+ if (panelButton->state) {
+ textColor = kKnownColorVerbTextActive;
+ } else if (panelButton == rightButtonVerbPanelButton) {
+ textColor = kKnownColorVerbTextActive;
+ } else {
+ textColor = kKnownColorVerbText;
+ }
+
+ if (panelButton == currentVerbPanelButton) {
+ spriteNumber = panelButton->downSpriteNumber;
+ } else {
+ spriteNumber = panelButton->upSpriteNumber;
+ }
+ point.x = _mainPanel.x + panelButton->xOffset;
+ point.y = _mainPanel.y + panelButton->yOffset;
+
+ _vm->_sprite->draw(backBuffer, _vm->getDisplayClip(), _mainPanel.sprites, spriteNumber, point, 256);
+
+ drawVerbPanelText(backBuffer, panelButton, textColor, kKnownColorVerbTextShadow);
+}
+
+void Interface::draw() {
+ Surface *backBuffer;
+ int i;
+
+ Point leftPortraitPoint;
+ Point rightPortraitPoint;
+ Rect rect;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ if (_vm->_scene->isInIntro() || _fadeMode == kFadeOut)
+ return;
+
+ drawStatusBar();
+
+ if (_panelMode == kPanelMain || _panelMode == kPanelMap) {
+ _mainPanel.getRect(rect);
+ backBuffer->blit(rect, _mainPanel.image);
+
+ for (i = 0; i < kVerbTypeIdsMax; i++) {
+ if (_verbTypeToPanelButton[i] != NULL) {
+ drawVerbPanel(backBuffer, _verbTypeToPanelButton[i]);
+ }
+ }
+ } else if (_panelMode == kPanelConverse) {
+ _conversePanel.getRect(rect);
+ backBuffer->blit(rect, _conversePanel.image);
+ converseDisplayTextLines(backBuffer);
+ }
+
+ if (_vm->getGameType() == GType_IHNM) {
+ if (_vm->_spiritualBarometer > 255)
+ _vm->_gfx->setPaletteColor(kIHNMColorPortrait, 0xff, 0xff, 0xff);
+ else
+ _vm->_gfx->setPaletteColor(kIHNMColorPortrait,
+ _vm->_spiritualBarometer * _portraitBgColor.red / 256,
+ _vm->_spiritualBarometer * _portraitBgColor.green / 256,
+ _vm->_spiritualBarometer * _portraitBgColor.blue / 256);
+ }
+
+ if (_panelMode == kPanelMain || _panelMode == kPanelConverse ||
+ _lockedMode == kPanelMain || _lockedMode == kPanelConverse) {
+ leftPortraitPoint.x = _mainPanel.x + _vm->getDisplayInfo().leftPortraitXOffset;
+ leftPortraitPoint.y = _mainPanel.y + _vm->getDisplayInfo().leftPortraitYOffset;
+ _vm->_sprite->draw(backBuffer, _vm->getDisplayClip(), _defPortraits, _leftPortrait, leftPortraitPoint, 256);
+ }
+
+ if (!_inMainMode && _vm->getDisplayInfo().rightPortraitXOffset >= 0) { //FIXME: should we change !_inMainMode to _panelMode == kPanelConverse ?
+ rightPortraitPoint.x = _mainPanel.x + _vm->getDisplayInfo().rightPortraitXOffset;
+ rightPortraitPoint.y = _mainPanel.y + _vm->getDisplayInfo().rightPortraitYOffset;
+
+ // This looks like hack - particularly since it's only done for
+ // the right-side portrait - and perhaps it is! But as far as I
+ // can tell this is what the original engine does. And it keeps
+ // ITE from crashing when entering the Elk King's court.
+
+ if (_rightPortrait >= _scenePortraits.spriteCount)
+ _rightPortrait = 0;
+
+ _vm->_sprite->draw(backBuffer, _vm->getDisplayClip(), _scenePortraits, _rightPortrait, rightPortraitPoint, 256);
+ }
+
+ drawInventory(backBuffer);
+}
+
+void Interface::calcOptionSaveSlider() {
+ int totalFiles = _vm->getSaveFilesCount();
+ int visibleFiles = _vm->getDisplayInfo().optionSaveFileVisible;
+ if (_optionSaveFileSlider == NULL) return; //TODO:REMOVE
+ int height = _optionSaveFileSlider->height;
+ int sliderHeight;
+ int pos;
+
+ if (totalFiles < visibleFiles) {
+ totalFiles = visibleFiles;
+ }
+
+ sliderHeight = visibleFiles * height / totalFiles;
+ if (sliderHeight < 7) {
+ sliderHeight = 7;
+ }
+
+ if (totalFiles - visibleFiles <= 0) {
+ pos = 0;
+ } else {
+ pos = _optionSaveFileTop * (height - sliderHeight) / (totalFiles - visibleFiles);
+ }
+ _optionPanel.calcPanelButtonRect(_optionSaveFileSlider, _optionSaveRectTop);
+ _optionSaveRectBottom = _optionSaveRectSlider = _optionSaveRectTop;
+
+ _optionSaveRectTop.bottom = _optionSaveRectTop.top + pos;
+ _optionSaveRectTop.top++;
+ _optionSaveRectTop.right--;
+
+ _optionSaveRectSlider.top = _optionSaveRectTop.bottom;
+ _optionSaveRectSlider.bottom = _optionSaveRectSlider.top + sliderHeight;
+
+ _optionSaveRectBottom.top = _optionSaveRectSlider.bottom;
+ _optionSaveRectBottom.right--;
+}
+
+void Interface::drawPanelText(Surface *ds, InterfacePanel *panel, PanelButton *panelButton) {
+ const char *text;
+ int textWidth;
+ Rect rect;
+ Point textPoint;
+
+ // Button differs for CD version
+ if (panelButton->id == kTextReadingSpeed && _vm->getFeatures() & GF_CD_FX)
+ return;
+ if (panelButton->id == kTextShowDialog && !(_vm->getFeatures() & GF_CD_FX))
+ return;
+
+ text = _vm->getTextString(panelButton->id);
+ panel->calcPanelButtonRect(panelButton, rect);
+ if (panelButton->xOffset < 0) {
+ textWidth = _vm->_font->getStringWidth(kKnownFontMedium, text, 0, kFontNormal);
+ rect.left += 2 + (panel->imageWidth - 1 - textWidth) / 2;
+ }
+
+ textPoint.x = rect.left;
+ textPoint.y = rect.top + 1;
+
+ _vm->_font->textDraw(kKnownFontMedium, ds, text, textPoint, _vm->KnownColor2ColorId(kKnownColorVerbText), _vm->KnownColor2ColorId(kKnownColorVerbTextShadow), kFontShadow);
+}
+
+void Interface::drawOption() {
+ const char *text;
+ Surface *backBuffer;
+ int i;
+ int fontHeight;
+ uint j, idx;
+ int fgColor;
+ int bgColor;
+ Rect rect;
+ Rect rect2;
+ PanelButton *panelButton;
+ Point textPoint;
+ if (_optionSaveFileSlider == NULL) return;//TODO:REMOVE
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ _optionPanel.getRect(rect);
+ backBuffer->blit(rect, _optionPanel.image);
+
+ for (i = 0; i < _optionPanel.buttonsCount; i++) {
+ panelButton = &_optionPanel.buttons[i];
+ if (panelButton->type == kPanelButtonOption) {
+ drawPanelButtonText(backBuffer, &_optionPanel, panelButton);
+ }
+ if (panelButton->type == kPanelButtonOptionText) {
+ drawPanelText(backBuffer, &_optionPanel, panelButton);
+ }
+ }
+
+ if (_optionSaveRectTop.height() > 0) {
+ backBuffer->drawRect(_optionSaveRectTop, kITEColorDarkGrey);
+ }
+
+ drawButtonBox(backBuffer, _optionSaveRectSlider, kSlider, _optionSaveFileSlider->state > 0);
+
+ if (_optionSaveRectBottom.height() > 0) {
+ backBuffer->drawRect(_optionSaveRectBottom, kITEColorDarkGrey);
+ }
+
+ _optionPanel.calcPanelButtonRect(_optionSaveFilePanel, rect);
+ rect.top++;
+ rect2 = rect;
+ fontHeight = _vm->_font->getHeight(kKnownFontSmall);
+ for (j = 0; j < _vm->getDisplayInfo().optionSaveFileVisible; j++) {
+ bgColor = kITEColorDarkGrey0C;
+ fgColor = kITEColorBrightWhite;
+
+ idx = j + _optionSaveFileTop;
+ if (idx == _optionSaveFileTitleNumber) {
+ SWAP(bgColor, fgColor);
+ }
+ if (idx < _vm->getSaveFilesCount()) {
+ rect2.top = rect.top + j * (fontHeight + 1);
+ rect2.bottom = rect2.top + fontHeight;
+ backBuffer->fillRect(rect2, bgColor);
+ text = _vm->getSaveFile(idx)->name;
+ textPoint.x = rect.left + 1;
+ textPoint.y = rect2.top;
+ _vm->_font->textDraw(kKnownFontSmall, backBuffer, text, textPoint, fgColor, 0, kFontNormal);
+ }
+ }
+
+}
+
+void Interface::drawQuit() {
+ Surface *backBuffer;
+ Rect rect;
+ int i;
+ PanelButton *panelButton;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ _quitPanel.getRect(rect);
+ drawButtonBox(backBuffer, rect, kButton, false);
+ for (i = 0; i < _quitPanel.buttonsCount; i++) {
+ panelButton = &_quitPanel.buttons[i];
+ if (panelButton->type == kPanelButtonQuit) {
+ drawPanelButtonText(backBuffer, &_quitPanel, panelButton);
+ }
+ if (panelButton->type == kPanelButtonQuitText) {
+ drawPanelText(backBuffer, &_quitPanel, panelButton);
+ }
+ }
+}
+
+void Interface::handleQuitUpdate(const Point& mousePoint) {
+ bool releasedButton;
+
+ _quitPanel.currentButton = quitHitTest(mousePoint);
+ releasedButton = (_quitPanel.currentButton != NULL) && (_quitPanel.currentButton->state > 0) && (!_vm->mouseButtonPressed());
+
+ if (!_vm->mouseButtonPressed()) {
+ _quitPanel.zeroAllButtonState();
+ }
+
+ if (releasedButton) {
+ setQuit(_quitPanel.currentButton);
+ }
+}
+
+void Interface::handleQuitClick(const Point& mousePoint) {
+ _quitPanel.currentButton = quitHitTest(mousePoint);
+
+ _quitPanel.zeroAllButtonState();
+
+ if (_quitPanel.currentButton == NULL) {
+ return;
+ }
+
+ _quitPanel.currentButton->state = 1;
+}
+
+void Interface::setQuit(PanelButton *panelButton) {
+ _quitPanel.currentButton = NULL;
+ switch (panelButton->id) {
+ case kTextCancel:
+ setMode(kPanelOption);
+ break;
+ case kTextQuit:
+ _vm->shutDown();
+ break;
+ }
+}
+
+void Interface::drawLoad() {
+ Surface *backBuffer;
+ Rect rect;
+ int i;
+ PanelButton *panelButton;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ _loadPanel.getRect(rect);
+ drawButtonBox(backBuffer, rect, kButton, false);
+ for (i = 0; i < _loadPanel.buttonsCount; i++) {
+ panelButton = &_loadPanel.buttons[i];
+ if (panelButton->type == kPanelButtonLoad) {
+ drawPanelButtonText(backBuffer, &_loadPanel, panelButton);
+ }
+ if (panelButton->type == kPanelButtonLoadText) {
+ drawPanelText(backBuffer, &_loadPanel, panelButton);
+ }
+ }
+}
+
+void Interface::handleLoadUpdate(const Point& mousePoint) {
+ bool releasedButton;
+
+ _loadPanel.currentButton = loadHitTest(mousePoint);
+ releasedButton = (_loadPanel.currentButton != NULL) && (_loadPanel.currentButton->state > 0) && (!_vm->mouseButtonPressed());
+
+ if (!_vm->mouseButtonPressed()) {
+ _loadPanel.zeroAllButtonState();
+ }
+
+ if (releasedButton) {
+ setLoad(_loadPanel.currentButton);
+ }
+}
+
+void Interface::handleLoadClick(const Point& mousePoint) {
+ _loadPanel.currentButton = loadHitTest(mousePoint);
+
+ _loadPanel.zeroAllButtonState();
+
+ if (_loadPanel.currentButton == NULL) {
+ return;
+ }
+
+ _loadPanel.currentButton->state = 1;
+}
+
+void Interface::setLoad(PanelButton *panelButton) {
+ _loadPanel.currentButton = NULL;
+ switch (panelButton->id) {
+ case kTextOK:
+ setMode(kPanelMain);
+ break;
+ }
+}
+
+void Interface::processStatusTextInput(uint16 ascii) {
+
+ textInputStartRepeat(ascii);
+ switch (ascii) {
+ case 27: // esc
+ _statusTextInputState = kStatusTextInputAborted;
+ _statusTextInput = false;
+ _vm->_script->wakeUpThreads(kWaitTypeStatusTextInput);
+ break;
+ case 13: // return
+ _statusTextInputState = kStatusTextInputEntered;
+ _statusTextInput = false;
+ _vm->_script->wakeUpThreads(kWaitTypeStatusTextInput);
+ break;
+ case 8: // backspace
+ if (_statusTextInputPos == 0) {
+ break;
+ }
+ _statusTextInputPos--;
+ _statusTextInputString[_statusTextInputPos] = 0;
+ default:
+ if (_statusTextInputPos >= STATUS_TEXT_INPUT_MAX) {
+ break;
+ }
+ if (((ascii >= 'a') && (ascii <='z')) ||
+ ((ascii >= '0') && (ascii <='9')) ||
+ ((ascii >= 'A') && (ascii <='Z')) ||
+ (ascii == ' ')) {
+ _statusTextInputString[_statusTextInputPos++] = ascii;
+ _statusTextInputString[_statusTextInputPos] = 0;
+ }
+ }
+ setStatusText(_statusTextInputString);
+}
+
+bool Interface::processTextInput(uint16 ascii) {
+ char ch[2];
+ char tempString[SAVE_TITLE_SIZE];
+ uint tempWidth;
+ memset(tempString, 0, SAVE_TITLE_SIZE);
+ ch[1] = 0;
+
+ textInputStartRepeat(ascii);
+
+ switch (ascii) {
+ case 13:
+ return false;
+ case 27: // esc
+ _textInput = false;
+ break;
+ case 8: // backspace
+ if (_textInputPos <= 1) {
+ break;
+ }
+ _textInputPos--;
+ case 127: // del
+ if (_textInputPos <= _textInputStringLength) {
+ if (_textInputPos != 1) {
+ strncpy(tempString, _textInputString, _textInputPos - 1);
+ }
+ if (_textInputPos != _textInputStringLength) {
+ strncat(tempString, &_textInputString[_textInputPos], _textInputStringLength - _textInputPos);
+ }
+ strcpy(_textInputString, tempString);
+ _textInputStringLength = strlen(_textInputString);
+ }
+ break;
+ case 276: // left
+ if (_textInputPos > 1) {
+ _textInputPos--;
+ }
+ break;
+ case 275: // right
+ if (_textInputPos <= _textInputStringLength) {
+ _textInputPos++;
+ }
+ break;
+ default:
+ if (((ascii >= 'a') && (ascii <='z')) ||
+ ((ascii >= '0') && (ascii <='9')) ||
+ ((ascii >= 'A') && (ascii <='Z')) ||
+ (ascii == ' ')) {
+ if (_textInputStringLength < SAVE_TITLE_SIZE - 1) {
+ ch[0] = ascii;
+ tempWidth = _vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal);
+ tempWidth += _vm->_font->getStringWidth(kKnownFontSmall, _textInputString, 0, kFontNormal);
+ if (tempWidth > _textInputMaxWidth) {
+ break;
+ }
+ if (_textInputPos != 1) {
+ strncpy(tempString, _textInputString, _textInputPos - 1);
+ strcat(tempString, ch);
+ }
+ if ((_textInputStringLength == 0) || (_textInputPos == 1)) {
+ strcpy(tempString, ch);
+ }
+ if ((_textInputStringLength != 0) && (_textInputPos != _textInputStringLength)) {
+ strncat(tempString, &_textInputString[_textInputPos - 1], _textInputStringLength - _textInputPos + 1);
+ }
+
+ strcpy(_textInputString, tempString);
+ _textInputStringLength = strlen(_textInputString);
+ _textInputPos++;
+ }
+ }
+ break;
+ }
+ return true;
+}
+
+void Interface::drawTextInput(Surface *ds, InterfacePanel *panel, PanelButton *panelButton) {
+ Point textPoint;
+ Rect rect;
+ char ch[2];
+ int fgColor;
+ uint i;
+
+ ch[1] = 0;
+ panel->calcPanelButtonRect(panelButton, rect);
+ drawButtonBox(ds, rect, kEdit, _textInput);
+ rect.left += 4;
+ rect.top += 4;
+ rect.setHeight(_vm->_font->getHeight(kKnownFontSmall));
+
+ i = 0;
+ while ((ch[0] = _textInputString[i++]) != 0) {
+ rect.setWidth(_vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal));
+ if ((i == _textInputPos) && _textInput) {
+ fgColor = kITEColorBlack;
+ ds->fillRect(rect, kITEColorWhite);
+ } else {
+ fgColor = kITEColorWhite;
+ }
+ textPoint.x = rect.left;
+ textPoint.y = rect.top + 1;
+
+ _vm->_font->textDraw(kKnownFontSmall, ds, ch, textPoint, fgColor, 0, kFontNormal);
+ rect.left += rect.width();
+ }
+ if (_textInput && (_textInputPos >= i)) {
+ ch[0] = ' ';
+ rect.setWidth(_vm->_font->getStringWidth(kKnownFontSmall, ch, 0, kFontNormal));
+ ds->fillRect(rect, kITEColorWhite);
+ }
+}
+
+void Interface::drawSave() {
+ Surface *backBuffer;
+ Rect rect;
+ int i;
+ PanelButton *panelButton;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ _savePanel.getRect(rect);
+ drawButtonBox(backBuffer, rect, kButton, false);
+ for (i = 0; i < _savePanel.buttonsCount; i++) {
+ panelButton = &_savePanel.buttons[i];
+ if (panelButton->type == kPanelButtonSave) {
+ drawPanelButtonText(backBuffer, &_savePanel, panelButton);
+ }
+ if (panelButton->type == kPanelButtonSaveText) {
+ drawPanelText(backBuffer, &_savePanel, panelButton);
+ }
+ }
+
+ drawTextInput(backBuffer, &_savePanel, _saveEdit);
+}
+
+void Interface::drawProtect() {
+ Surface *backBuffer;
+ Rect rect;
+ int i;
+ PanelButton *panelButton;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ _protectPanel.getRect(rect);
+ drawButtonBox(backBuffer, rect, kButton, false);
+
+ for (i = 0; i < _protectPanel.buttonsCount; i++) {
+ panelButton = &_protectPanel.buttons[i];
+ if (panelButton->type == kPanelButtonProtectText) {
+ drawPanelText(backBuffer, &_protectPanel, panelButton);
+ }
+ }
+ drawTextInput(backBuffer, &_protectPanel, _protectEdit);
+}
+
+void Interface::handleSaveUpdate(const Point& mousePoint) {
+ bool releasedButton;
+
+ _savePanel.currentButton = saveHitTest(mousePoint);
+
+ validateSaveButtons();
+
+ releasedButton = (_savePanel.currentButton != NULL) &&
+ (_savePanel.currentButton->state > 0) && (!_vm->mouseButtonPressed());
+
+ if (!_vm->mouseButtonPressed()) {
+ _savePanel.zeroAllButtonState();
+ }
+
+ if (releasedButton) {
+ setSave(_savePanel.currentButton);
+ }
+}
+
+void Interface::handleSaveClick(const Point& mousePoint) {
+ _savePanel.currentButton = saveHitTest(mousePoint);
+
+ validateSaveButtons();
+
+ _savePanel.zeroAllButtonState();
+
+ if (_savePanel.currentButton == NULL) {
+ _textInput = false;
+ return;
+ }
+
+ _savePanel.currentButton->state = 1;
+ if (_savePanel.currentButton == _saveEdit) {
+ _textInput = true;
+ }
+}
+
+void Interface::setSave(PanelButton *panelButton) {
+ _savePanel.currentButton = NULL;
+ uint titleNumber;
+ char *fileName;
+ switch (panelButton->id) {
+ case kTextSave:
+ if (_textInputStringLength == 0 ) {
+ break;
+ }
+ if (!_vm->isSaveListFull() && (_optionSaveFileTitleNumber == 0)) {
+ if (_vm->locateSaveFile(_textInputString, titleNumber)) {
+ fileName = _vm->calcSaveFileName(_vm->getSaveFile(titleNumber)->slotNumber);
+ _vm->save(fileName, _textInputString);
+ _optionSaveFileTitleNumber = titleNumber;
+ } else {
+ fileName = _vm->calcSaveFileName(_vm->getNewSaveSlotNumber());
+ _vm->save(fileName, _textInputString);
+ _vm->fillSaveList();
+ calcOptionSaveSlider();
+ }
+ } else {
+ fileName = _vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber);
+ _vm->save(fileName, _textInputString);
+ }
+ _textInput = false;
+ setMode(kPanelOption);
+ break;
+ case kTextCancel:
+ _textInput = false;
+ setMode(kPanelOption);
+ break;
+ }
+}
+
+void Interface::handleOptionUpdate(const Point& mousePoint) {
+ int16 mouseY;
+ Rect rect;
+ int totalFiles = _vm->getSaveFilesCount();
+ int visibleFiles = _vm->getDisplayInfo().optionSaveFileVisible;
+ bool releasedButton;
+
+ if (_vm->mouseButtonPressed()) {
+ if (_optionSaveFileSlider != NULL) //TODO:REMOVE
+ if (_optionSaveFileSlider->state > 0) {
+ _optionPanel.calcPanelButtonRect(_optionSaveFileSlider, rect);
+
+ mouseY = mousePoint.y - rect.top -_optionSaveFileMouseOff;
+
+ if (totalFiles - visibleFiles <= 0) {
+ _optionSaveFileTop = 0;
+ } else {
+ _optionSaveFileTop = mouseY * (totalFiles - visibleFiles) /
+ (_optionSaveFileSlider->height - _optionSaveRectSlider.height());
+ }
+
+ _optionSaveFileTop = clamp(0, _optionSaveFileTop, _vm->getSaveFilesCount() - _vm->getDisplayInfo().optionSaveFileVisible);
+ calcOptionSaveSlider();
+ }
+ }
+
+ _optionPanel.currentButton = optionHitTest(mousePoint);
+
+ validateOptionButtons();
+
+ releasedButton = (_optionPanel.currentButton != NULL) && (_optionPanel.currentButton->state > 0) && (!_vm->mouseButtonPressed());
+
+ if (!_vm->mouseButtonPressed()) {
+ _optionPanel.zeroAllButtonState();
+ }
+
+ if (releasedButton) {
+ setOption(_optionPanel.currentButton);
+ }
+}
+
+
+void Interface::handleOptionClick(const Point& mousePoint) {
+ Rect rect;
+ _optionPanel.currentButton = optionHitTest(mousePoint);
+
+ validateOptionButtons();
+
+ _optionPanel.zeroAllButtonState();
+
+ if (_optionPanel.currentButton == NULL) {
+ return;
+ }
+
+ if (_optionPanel.currentButton == _optionSaveFileSlider) {
+ if ((_optionSaveRectTop.height() > 0) && (mousePoint.y < _optionSaveRectTop.bottom)) {
+ _optionSaveFileTop -= _vm->getDisplayInfo().optionSaveFileVisible;
+ } else {
+ if ((_optionSaveRectBottom.height() > 0) && (mousePoint.y >= _optionSaveRectBottom.top)) {
+ _optionSaveFileTop += _vm->getDisplayInfo().optionSaveFileVisible;
+ } else {
+ if (_vm->getDisplayInfo().optionSaveFileVisible < _vm->getSaveFilesCount()) {
+ _optionSaveFileMouseOff = mousePoint.y - _optionSaveRectSlider.top;
+ _optionPanel.currentButton->state = 1;
+ }
+ }
+ }
+
+ _optionSaveFileTop = clamp(0, _optionSaveFileTop, _vm->getSaveFilesCount() - _vm->getDisplayInfo().optionSaveFileVisible);
+ calcOptionSaveSlider();
+ } else {
+ if (_optionPanel.currentButton == _optionSaveFilePanel) {
+ _optionPanel.calcPanelButtonRect(_optionSaveFilePanel, rect);
+ _optionSaveFileTitleNumber = (mousePoint.y - rect.top) / (_vm->_font->getHeight(kKnownFontSmall) + 1);
+
+ if (_optionSaveFileTitleNumber >= _vm->getDisplayInfo().optionSaveFileVisible) {
+ _optionSaveFileTitleNumber = _vm->getDisplayInfo().optionSaveFileVisible - 1;
+ }
+ _optionSaveFileTitleNumber += _optionSaveFileTop;
+ if (_optionSaveFileTitleNumber >= _vm->getSaveFilesCount()) {
+ _optionSaveFileTitleNumber = _vm->getSaveFilesCount() - 1;
+ }
+ } else {
+ _optionPanel.currentButton->state = 1;
+ }
+ }
+}
+
+void Interface::handleChapterSelectionUpdate(const Point& mousePoint) {
+ uint16 objectId;
+
+ // FIXME: Original handled more object types here.
+
+ objectId = _vm->_actor->hitTest(mousePoint, true);
+
+ if (objectId != _vm->_script->_pointerObject) {
+ _vm->_script->_pointerObject = objectId;
+ }
+}
+
+void Interface::handleChapterSelectionClick(const Point& mousePoint) {
+ int obj = _vm->_script->_pointerObject;
+
+ _vm->_actor->abortSpeech();
+
+ if (obj) {
+ int script = 0;
+ HitZone *hitZone;
+ ActorData *a;
+ ObjectData *o;
+ Event event;
+
+ switch (objectTypeId(obj)) {
+ case kGameObjectHitZone:
+ hitZone = _vm->_scene->_actionMap->getHitZone(objectIdToIndex(obj));
+ if (hitZone->getFlags() & kHitZoneExit)
+ script = hitZone->getScriptNumber();
+ break;
+
+ case kGameObjectActor:
+ a = _vm->_actor->getActor(obj);
+ script = a->_scriptEntrypointNumber;
+ break;
+
+ case kGameObjectObject:
+ o = _vm->_actor->getObj(obj);
+ script = o->_scriptEntrypointNumber;
+ break;
+ }
+
+ if (script > 0) {
+ event.type = kEvTOneshot;
+ event.code = kScriptEvent;
+ event.op = kEventExecNonBlocking;
+ event.time = 0;
+ event.param = _vm->_scene->getScriptModuleNumber();
+ event.param2 = script;
+ event.param3 = _vm->_script->getVerbType(kVerbUse); // Action
+ event.param4 = obj; // Object
+ event.param5 = 0; // With Object
+ event.param6 = obj; // Actor
+
+ _vm->_events->queue(&event);
+ }
+ }
+}
+
+void Interface::setOption(PanelButton *panelButton) {
+ char * fileName;
+ _optionPanel.currentButton = NULL;
+ switch (panelButton->id) {
+ case kTextContinuePlaying:
+ ConfMan.flushToDisk();
+ setMode(kPanelMain);
+ break;
+ case kTextQuitGame:
+ setMode(kPanelQuit);
+ break;
+ case kTextLoad:
+ if (_vm->getSaveFilesCount() > 0) {
+ if (_vm->isSaveListFull() || (_optionSaveFileTitleNumber > 0)) {
+ fileName = _vm->calcSaveFileName(_vm->getSaveFile(_optionSaveFileTitleNumber)->slotNumber);
+ setMode(kPanelMain);
+ _vm->load(fileName);
+ }
+ }
+ break;
+ case kTextSave:
+ if (!_vm->isSaveListFull() && (_optionSaveFileTitleNumber == 0)) {
+ _textInputString[0] = 0;
+ } else {
+ strcpy(_textInputString, _vm->getSaveFile(_optionSaveFileTitleNumber)->name);
+ }
+ setMode(kPanelSave);
+ break;
+ case kTextReadingSpeed:
+ if (_vm->getFeatures() & GF_CD_FX) {
+ _vm->_subtitlesEnabled = !_vm->_subtitlesEnabled;
+ ConfMan.set("subtitles", _vm->_subtitlesEnabled);
+ } else {
+ _vm->_readingSpeed = (_vm->_readingSpeed + 1) % 4;
+ ConfMan.set("talkspeed", _vm->_readingSpeed);
+ }
+ break;
+ case kTextMusic:
+ _vm->_musicVolume = (_vm->_musicVolume + 1) % 11;
+ _vm->_music->setVolume(_vm->_musicVolume == 10 ? -1 : _vm->_musicVolume * 25, 1);
+ ConfMan.set("music_volume", _vm->_musicVolume * 25);
+ break;
+ case kTextSound:
+ _vm->_soundVolume = (_vm->_soundVolume + 1) % 11;
+ _vm->_sound->setVolume(_vm->_soundVolume == 10 ? 255 : _vm->_soundVolume * 25);
+ ConfMan.set("sfx_volume", _vm->_soundVolume * 25);
+ break;
+ }
+}
+
+void Interface::update(const Point& mousePoint, int updateFlag) {
+
+ if (!_active && _panelMode == kPanelNull && (updateFlag & UPDATE_MOUSECLICK))
+ _vm->_actor->abortSpeech();
+
+ if (_vm->_scene->isInIntro() || _fadeMode == kFadeOut || !_active) {
+ return;
+ }
+
+ if (_statusTextInput) {
+ return;
+ }
+
+ switch (_panelMode) {
+ case kPanelMain:
+ if (updateFlag & UPDATE_MOUSEMOVE) {
+ bool lastWasPlayfield = _lastMousePoint.y < _vm->_scene->getHeight();
+ if (mousePoint.y < _vm->_scene->getHeight()) {
+ if (!lastWasPlayfield) {
+ handleMainUpdate(mousePoint);
+ }
+ _vm->_script->whichObject(mousePoint);
+ } else {
+ if (lastWasPlayfield) {
+ _vm->_script->setNonPlayfieldVerb();
+ }
+ handleMainUpdate(mousePoint);
+ }
+
+ } else {
+
+ if (updateFlag & UPDATE_MOUSECLICK) {
+ if (mousePoint.y < _vm->_scene->getHeight()) {
+ _vm->_script->playfieldClick(mousePoint, (updateFlag & UPDATE_LEFTBUTTONCLICK) != 0);
+ } else {
+ handleMainClick(mousePoint);
+ }
+ }
+ }
+ break;
+
+ case kPanelConverse:
+ if (updateFlag & UPDATE_MOUSEMOVE) {
+ handleConverseUpdate(mousePoint);
+ } else {
+ if (updateFlag & UPDATE_MOUSECLICK) {
+ handleConverseClick(mousePoint);
+ }
+ if (updateFlag & UPDATE_WHEELUP) {
+ converseChangePos(-1);
+ }
+ if (updateFlag & UPDATE_WHEELDOWN) {
+ converseChangePos(1);
+ }
+
+ if (_vm->_puzzle->isActive()) {
+ _vm->_puzzle->handleClick(mousePoint);
+ }
+ }
+ break;
+
+ case kPanelOption:
+ if (updateFlag & UPDATE_MOUSEMOVE) {
+ handleOptionUpdate(mousePoint);
+ } else {
+ if (updateFlag & UPDATE_MOUSECLICK) {
+ handleOptionClick(mousePoint);
+ }
+ if (updateFlag & UPDATE_WHEELUP) {
+ if (_optionSaveFileTop)
+ _optionSaveFileTop--;
+ calcOptionSaveSlider();
+ }
+ if (updateFlag & UPDATE_WHEELDOWN) {
+ if (_optionSaveFileTop < _vm->getSaveFilesCount() - _vm->getDisplayInfo().optionSaveFileVisible)
+ _optionSaveFileTop++;
+ calcOptionSaveSlider();
+ }
+ }
+ break;
+
+ case kPanelQuit:
+ if (updateFlag & UPDATE_MOUSEMOVE) {
+ handleQuitUpdate(mousePoint);
+ } else {
+ if (updateFlag & UPDATE_MOUSECLICK) {
+ handleQuitClick(mousePoint);
+ }
+ }
+ break;
+
+ case kPanelLoad:
+ if (updateFlag & UPDATE_MOUSEMOVE) {
+
+ handleLoadUpdate(mousePoint);
+
+ } else {
+ if (updateFlag & UPDATE_MOUSECLICK) {
+ handleLoadClick(mousePoint);
+ }
+ }
+ break;
+
+ case kPanelSave:
+ if (updateFlag & UPDATE_MOUSEMOVE) {
+
+ handleSaveUpdate(mousePoint);
+
+ } else {
+ if (updateFlag & UPDATE_MOUSECLICK) {
+ handleSaveClick(mousePoint);
+ }
+ }
+ break;
+
+ case kPanelMap:
+ if (updateFlag & UPDATE_MOUSECLICK)
+ mapPanelClean();
+ break;
+
+ case kPanelSceneSubstitute:
+ if (updateFlag & UPDATE_MOUSECLICK) {
+ _vm->_render->clearFlag(RF_DEMO_SUBST);
+ _vm->_gfx->setPalette(_mapSavedPal);
+ setMode(kPanelMain);
+ _vm->_script->setNoPendingVerb();
+ }
+ break;
+
+ case kPanelChapterSelection:
+ // TODO: panel has silent button
+ if (updateFlag & UPDATE_MOUSEMOVE) {
+ handleChapterSelectionUpdate(mousePoint);
+ } else {
+ if (updateFlag & UPDATE_MOUSECLICK)
+ handleChapterSelectionClick(mousePoint);
+ }
+ break;
+
+ case kPanelProtect:
+ // No mouse interaction
+ break;
+
+ }
+
+ _lastMousePoint = mousePoint;
+}
+
+void Interface::drawStatusBar() {
+ Surface *backBuffer;
+ Rect rect;
+ Point textPoint;
+ int stringWidth;
+ int color;
+
+ if (_panelMode == kPanelChapterSelection)
+ return;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ // Disable this for IHNM for now, since that game uses the full screen
+ // in some cases.
+
+ // Erase background of status bar
+ rect.left = _vm->getDisplayInfo().statusXOffset;
+ rect.top = _vm->getDisplayInfo().statusYOffset;
+ rect.right = rect.left + _vm->getDisplayWidth();
+ rect.bottom = rect.top + _vm->getDisplayInfo().statusHeight;
+
+ backBuffer->drawRect(rect, _vm->getDisplayInfo().statusBGColor);
+
+ stringWidth = _vm->_font->getStringWidth(kKnownFontSmall, _statusText, 0, kFontNormal);
+
+ if (_statusOnceColor == -1)
+ color = _vm->getDisplayInfo().statusTextColor;
+ else
+ color = _statusOnceColor;
+
+ textPoint.x = _vm->getDisplayInfo().statusXOffset + (_vm->getDisplayInfo().statusWidth - stringWidth) / 2;
+ textPoint.y = _vm->getDisplayInfo().statusYOffset + _vm->getDisplayInfo().statusTextY;
+ _vm->_font->textDraw(kKnownFontSmall, backBuffer, _statusText, textPoint, color, 0, kFontNormal);
+
+ if (_saveReminderState > 0) {
+ rect.left = _vm->getDisplayInfo().saveReminderXOffset;
+ rect.top = _vm->getDisplayInfo().saveReminderYOffset;
+
+ rect.right = rect.left + _vm->getDisplayInfo().saveReminderWidth;
+ rect.bottom = rect.top + _vm->getDisplayInfo().saveReminderHeight;
+ _vm->_sprite->draw(backBuffer, _vm->getDisplayClip(), _vm->_sprite->_saveReminderSprites,
+ _saveReminderState == 1 ? _vm->getDisplayInfo().saveReminderFirstSpriteNumber : _vm->getDisplayInfo().saveReminderSecondSpriteNumber,
+ rect, 256);
+
+ }
+}
+
+void Interface::handleMainClick(const Point& mousePoint) {
+
+ PanelButton *panelButton;
+
+ panelButton = verbHitTest(mousePoint);
+ if (panelButton) {
+ _vm->_script->setVerb(panelButton->id);
+ return;
+ }
+
+ panelButton = _mainPanel.hitTest(mousePoint, kPanelAllButtons);
+
+ if (panelButton != NULL) {
+ if (panelButton->type == kPanelButtonArrow) {
+ panelButton->state = 1;
+ converseChangePos(panelButton->id);
+ }
+
+ if (panelButton->type == kPanelButtonInventory) {
+ if (_vm->_script->_pointerObject != ID_NOTHING) {
+ _vm->_script->hitObject(_vm->leftMouseButtonPressed());
+ }
+ if (_vm->_script->_pendingVerb) {
+ _vm->_actor->_protagonist->_currentAction = kActionWait;
+ _vm->_script->doVerb();
+ }
+ }
+ } else {
+ if (_saveReminderState > 0) {
+ Rect rect;
+ rect.left = _vm->getDisplayInfo().saveReminderXOffset;
+ rect.top = _vm->getDisplayInfo().saveReminderYOffset;
+
+ rect.right = rect.left + _vm->getDisplayInfo().saveReminderWidth;
+ rect.bottom = rect.top + _vm->getDisplayInfo().saveReminderHeight;
+ if (rect.contains(mousePoint)) {
+ setMode(kPanelOption);
+ }
+ }
+ }
+}
+
+void Interface::handleMainUpdate(const Point& mousePoint) {
+ PanelButton *panelButton;
+
+ panelButton = verbHitTest(mousePoint);
+ if (_mainPanel.currentButton != panelButton) {
+ if (_mainPanel.currentButton) {
+ if (_mainPanel.currentButton->type == kPanelButtonVerb) {
+ setVerbState(_mainPanel.currentButton->id, 0);
+ }
+ }
+ if (panelButton) {
+ setVerbState(panelButton->id, 1);
+ }
+ }
+
+ if (panelButton) {
+ _mainPanel.currentButton = panelButton;
+ return;
+ }
+
+
+ if (!_vm->mouseButtonPressed()) { // remove pressed flag
+ if (_inventoryUpButton) {
+ _inventoryUpButton->state = 0;
+ _inventoryDownButton->state = 0;
+ }
+ }
+
+ panelButton = _mainPanel.hitTest(mousePoint, kPanelAllButtons);
+
+ bool changed = false;
+
+ if ((panelButton != NULL) && (panelButton->type == kPanelButtonArrow)) {
+ if (panelButton->state == 1) {
+ //TODO: insert timeout catchup
+ inventoryChangePos(panelButton->id);
+ }
+ changed = true;
+ } else {
+ _vm->_script->whichObject(mousePoint);
+ }
+
+ changed = changed || (panelButton != _mainPanel.currentButton);
+ _mainPanel.currentButton = panelButton;
+ if (changed) {
+ draw();
+ }
+}
+
+//inventory stuff
+void Interface::inventoryChangePos(int chg) {
+ if ((chg < 0 && _inventoryStart + chg >= 0) ||
+ (chg > 0 && _inventoryStart < _inventoryEnd)) {
+ _inventoryStart += chg;
+ draw();
+ }
+}
+
+void Interface::inventorySetPos(int key) {
+ _inventoryBox = key - '1';
+ _inventoryPos = _inventoryStart + _inventoryBox;
+ if (_inventoryPos >= _inventoryCount)
+ _inventoryPos = -1;
+}
+
+void Interface::updateInventory(int pos) {
+ int cols = _vm->getDisplayInfo().inventoryColumns;
+ if (pos >= _inventoryCount) {
+ pos = _inventoryCount - 1;
+ }
+ if (pos < 0) {
+ pos = 0;
+ }
+ _inventoryStart = (pos - cols) / cols * cols;
+ if (_inventoryStart < 0) {
+ _inventoryStart = 0;
+ }
+
+ _inventoryEnd = (_inventoryCount - 1 - cols) / cols * cols;
+ if (_inventoryEnd < 0) {
+ _inventoryEnd = 0;
+ }
+}
+
+void Interface::addToInventory(int objectId) {
+ if (_inventoryCount >= _inventorySize) {
+ return;
+ }
+
+ for (int i = _inventoryCount; i > 0; i--) {
+ _inventory[i] = _inventory[i - 1];
+ }
+
+ _inventory[0] = objectId;
+ _inventoryCount++;
+
+ _inventoryPos = 0;
+ updateInventory(0);
+ draw();
+}
+
+void Interface::removeFromInventory(int objectId) {
+ int j = inventoryItemPosition(objectId);
+ if (j == -1) {
+ return;
+ }
+
+ int i;
+
+ for (i = j; i < _inventoryCount - 1; i++) {
+ _inventory[i] = _inventory[i + 1];
+ }
+
+ --_inventoryCount;
+ _inventory[_inventoryCount] = 0;
+ updateInventory(j);
+ draw();
+}
+
+void Interface::clearInventory() {
+ for (int i = 0; i < _inventoryCount; i++)
+ _inventory[i] = 0;
+
+ _inventoryCount = 0;
+ updateInventory(0);
+}
+
+int Interface::inventoryItemPosition(int objectId) {
+ for (int i = 0; i < _inventoryCount; i++)
+ if (_inventory[i] == objectId)
+ return i;
+
+ return -1;
+}
+
+void Interface::drawInventory(Surface *backBuffer) {
+ if (!isInMainMode())
+ return;
+
+ int i;
+ Rect rect;
+ int ci;
+ ObjectData *obj;
+ ci = _inventoryStart;
+ if (_inventoryStart != 0) {
+ drawPanelButtonArrow(backBuffer, &_mainPanel, _inventoryUpButton);
+ }
+ if (_inventoryStart != _inventoryEnd) {
+ drawPanelButtonArrow(backBuffer, &_mainPanel, _inventoryDownButton);
+ }
+
+ for (i = 0; i < _mainPanel.buttonsCount; i++) {
+ if (_mainPanel.buttons[i].type != kPanelButtonInventory) {
+ continue;
+ }
+ _mainPanel.calcPanelButtonRect(&_mainPanel.buttons[i], rect);
+
+ // TODO: Different colour for IHNM, probably.
+ backBuffer->drawRect(rect, kITEColorDarkGrey);
+
+ if (ci < _inventoryCount) {
+ obj = _vm->_actor->getObj(_inventory[ci]);
+ _vm->_sprite->draw(backBuffer, _vm->getDisplayClip(), _vm->_sprite->_inventorySprites, obj->_spriteListResourceId, rect, 256);
+ }
+
+ ci++;
+ }
+}
+
+void Interface::setVerbState(int verb, int state) {
+ PanelButton * panelButton = getPanelButtonByVerbType(verb);
+ if (panelButton == NULL) return;
+ if (state == 2) {
+ state = (_mainPanel.currentButton == panelButton) ? 1 : 0;
+ }
+ panelButton->state = state;
+ draw();
+}
+
+void Interface::drawButtonBox(Surface *ds, const Rect& rect, ButtonKind kind, bool down) {
+ byte cornerColor;
+ byte frameColor;
+ byte fillColor;
+ byte solidColor;
+ byte odl, our, idl, iur;
+
+ switch (kind ) {
+ case kSlider:
+ cornerColor = 0x8b;
+ frameColor = kITEColorBlack;
+ fillColor = kITEColorLightBlue96;
+ odl = kITEColorDarkBlue8a;
+ our = kITEColorLightBlue92;
+ idl = 0x89;
+ iur = 0x94;
+ solidColor = down ? kITEColorLightBlue94 : kITEColorLightBlue96;
+ break;
+ case kEdit:
+ cornerColor = kITEColorLightBlue96;
+ frameColor = kITEColorLightBlue96;
+ fillColor = kITEColorLightBlue96;
+ our = kITEColorDarkBlue8a;
+ odl = kITEColorLightBlue94;
+ iur = 0x97;
+ idl = 0x95;
+ if (down) {
+ solidColor = kITEColorBlue;
+ } else {
+ solidColor = kITEColorDarkGrey0C;
+ }
+ break;
+ default:
+ cornerColor = 0x8b;
+ frameColor = kITEColorBlack;
+ solidColor = fillColor = kITEColorLightBlue96;
+ odl = kITEColorDarkBlue8a;
+ our = kITEColorLightBlue94;
+ idl = 0x97;
+ iur = 0x95;
+ if (down) {
+ SWAP(odl, our);
+ SWAP(idl, iur);
+ }
+ break;
+ }
+
+ int x = rect.left;
+ int y = rect.top;
+ int w = rect.width();
+ int h = rect.height();
+ int xe = rect.right - 1;
+ int ye = rect.bottom - 1;
+
+ ((byte *)ds->getBasePtr(x, y))[0] = cornerColor;
+ ((byte *)ds->getBasePtr(x, ye))[0] = cornerColor;
+ ((byte *)ds->getBasePtr(xe, y))[0] = cornerColor;
+ ((byte *)ds->getBasePtr(xe, ye))[0] = cornerColor;
+ ds->hLine(x + 1, y, x + 1 + w - 2, frameColor);
+ ds->hLine(x + 1, ye, x + 1 + w - 2, frameColor);
+ ds->vLine(x, y + 1, y + 1 + h - 2, frameColor);
+ ds->vLine(xe, y + 1, y + 1 + h - 2, frameColor);
+
+ x++;
+ y++;
+ xe--;
+ ye--;
+ w -= 2;
+ h -= 2;
+ ds->vLine(x, y, y + h - 1, odl);
+ ds->hLine(x, ye, x + w - 1, odl);
+ ds->vLine(xe, y, y + h - 1, our);
+ ds->hLine(x + 1, y, x + 1 + w - 2, our);
+
+ x++;
+ y++;
+ xe--;
+ ye--;
+ w -= 2;
+ h -= 2;
+ ((byte *)ds->getBasePtr(x, y))[0] = fillColor;
+ ((byte *)ds->getBasePtr(xe, ye))[0] = fillColor;
+ ds->vLine(x, y + 1, y + 1 + h - 2, idl);
+ ds->hLine(x + 1, ye, x + 1 + w - 2, idl);
+ ds->vLine(xe, y, y + h - 1, iur);
+ ds->hLine(x + 1, y, x + 1 + w - 2, iur);
+
+ x++; y++;
+ w -= 2; h -= 2;
+
+ Common::Rect fill(x, y, x + w, y + h);
+ ds->fillRect(fill, solidColor);
+}
+
+static const int readingSpeeds[] = { kTextFast, kTextMid, kTextSlow, kTextClick };
+
+void Interface::drawPanelButtonText(Surface *ds, InterfacePanel *panel, PanelButton *panelButton) {
+ const char *text;
+ int textId;
+ int textWidth;
+ int textHeight;
+ Point point;
+ KnownColor textColor;
+ Rect rect;
+
+ textId = panelButton->id;
+ switch(panelButton->id) {
+ case kTextReadingSpeed:
+ if (_vm->getFeatures() & GF_CD_FX) {
+ if (_vm->_subtitlesEnabled)
+ textId = kTextOn;
+ else
+ textId = kTextOff;
+ } else {
+ textId = readingSpeeds[_vm->_readingSpeed];
+ }
+ break;
+ case kTextMusic:
+ if (_vm->_musicVolume)
+ textId = kText10Percent + _vm->_musicVolume - 1;
+ else
+ textId = kTextOff;
+ break;
+ case kTextSound:
+ if (_vm->_soundVolume)
+ textId = kText10Percent + _vm->_soundVolume - 1;
+ else
+ textId = kTextOff;
+ break;
+ }
+ text = _vm->getTextString(textId);
+
+ textWidth = _vm->_font->getStringWidth(kKnownFontMedium, text, 0, kFontNormal);
+ textHeight = _vm->_font->getHeight(kKnownFontMedium);
+
+ point.x = panel->x + panelButton->xOffset + (panelButton->width / 2) - (textWidth / 2);
+ point.y = panel->y + panelButton->yOffset + (panelButton->height / 2) - (textHeight / 2);
+
+ if (panelButton == panel->currentButton) {
+ textColor = kKnownColorVerbTextActive;
+ } else {
+ textColor = kKnownColorVerbText;
+ }
+
+ panel->calcPanelButtonRect(panelButton, rect);
+ drawButtonBox(ds, rect, kButton, panelButton->state > 0);
+
+ _vm->_font->textDraw(kKnownFontMedium, ds, text, point,
+ _vm->KnownColor2ColorId(textColor), _vm->KnownColor2ColorId(kKnownColorVerbTextShadow), kFontShadow);
+}
+
+void Interface::drawPanelButtonArrow(Surface *ds, InterfacePanel *panel, PanelButton *panelButton) {
+ Point point;
+ int spriteNumber;
+
+ if (panel->currentButton == panelButton) {
+ if (panelButton->state != 0) {
+ spriteNumber = panelButton->downSpriteNumber;
+ } else {
+ spriteNumber = panelButton->overSpriteNumber;
+ }
+ } else {
+ spriteNumber = panelButton->upSpriteNumber;
+ }
+
+ point.x = panel->x + panelButton->xOffset;
+ point.y = panel->y + panelButton->yOffset;
+
+ _vm->_sprite->draw(ds, _vm->getDisplayClip(), _vm->_sprite->_mainSprites, spriteNumber, point, 256);
+}
+
+void Interface::drawVerbPanelText(Surface *ds, PanelButton *panelButton, KnownColor textKnownColor, KnownColor textShadowKnownColor) {
+ const char *text;
+ int textWidth;
+ Point point;
+ int textId;
+
+ if (_vm->getGameType() == GType_ITE) {
+ textId = verbTypeToTextStringsIdLUT[0][panelButton->id];
+ text = _vm->getTextString(textId);
+ } else {
+ textId = verbTypeToTextStringsIdLUT[1][panelButton->id];
+ text = _vm->_script->_mainStrings.getString(textId + 1);
+ textShadowKnownColor = kKnownColorTransparent;
+ }
+
+ textWidth = _vm->_font->getStringWidth(kKnownFontVerb, text, 0, kFontNormal);
+
+ if (_vm->getGameType() == GType_ITE) {
+ point.x = _mainPanel.x + panelButton->xOffset + 1 + (panelButton->width - 1 - textWidth) / 2;
+ point.y = _mainPanel.y + panelButton->yOffset + 1;
+ } else {
+ point.x = _mainPanel.x + panelButton->xOffset + 1 + (panelButton->width - textWidth) / 2;
+ point.y = _mainPanel.y + panelButton->yOffset + 12;
+ }
+
+ _vm->_font->textDraw(kKnownFontVerb, ds, text, point, _vm->KnownColor2ColorId(textKnownColor),_vm->KnownColor2ColorId(textShadowKnownColor), (textShadowKnownColor != kKnownColorTransparent) ? kFontShadow : kFontNormal);
+}
+
+
+// Converse stuff
+void Interface::converseInit(void) {
+ for (int i = 0; i < CONVERSE_MAX_TEXTS; i++)
+ _converseText[i].text = NULL;
+ converseClear();
+}
+
+void Interface::converseClear(void) {
+ for (int i = 0; i < CONVERSE_MAX_TEXTS; i++) {
+ if (_converseText[i].text != NULL) {
+ free(_converseText[i].text);
+ _converseText[i].text = NULL;
+ }
+ _converseText[i].stringNum = -1;
+ _converseText[i].replyId = 0;
+ _converseText[i].replyFlags = 0;
+ _converseText[i].replyBit = 0;
+ }
+
+ _converseTextCount = 0;
+ _converseStrCount = 0;
+ _converseStartPos = 0;
+ _converseEndPos = 0;
+ _conversePos = -1;
+}
+
+bool Interface::converseAddText(const char *text, int replyId, byte replyFlags, int replyBit) {
+ int count = 0; // count how many pieces of text per string
+ int i;
+ int len;
+ byte c;
+
+ assert(strlen(text) < CONVERSE_MAX_WORK_STRING);
+
+ strncpy(_converseWorkString, text, CONVERSE_MAX_WORK_STRING);
+
+ while (1) {
+ len = strlen(_converseWorkString);
+
+ for (i = len; i >= 0; i--) {
+ c = _converseWorkString[i];
+
+ if ((c == ' ' || c == '\0') && (_vm->_font->getStringWidth(kKnownFontSmall, _converseWorkString, i, kFontNormal) <= _vm->getDisplayInfo().converseMaxTextWidth)) {
+ break;
+ }
+ }
+ if (i < 0) {
+ return true;
+ }
+
+ if (_converseTextCount == CONVERSE_MAX_TEXTS) {
+ return true;
+ }
+
+ _converseText[_converseTextCount].text = (char *)malloc(i + 1);
+ strncpy(_converseText[_converseTextCount].text, _converseWorkString, i);
+
+ _converseText[_converseTextCount].text[i] = 0;
+ _converseText[_converseTextCount].textNum = count;
+ _converseText[_converseTextCount].stringNum = _converseStrCount;
+ _converseText[_converseTextCount].replyId = replyId;
+ _converseText[_converseTextCount].replyFlags = replyFlags;
+ _converseText[_converseTextCount].replyBit = replyBit;
+
+ _converseTextCount++;
+ count++;
+
+ if (len == i)
+ break;
+
+ strncpy(_converseWorkString, &_converseWorkString[i + 1], len - i);
+ }
+
+ _converseStrCount++;
+
+ return false;
+}
+
+void Interface::converseDisplayText() {
+ int end;
+
+ _converseStartPos = 0;
+
+ end = _converseTextCount - _vm->getDisplayInfo().converseTextLines;
+
+ if (end < 0)
+ end = 0;
+
+ _converseEndPos = end;
+ draw();
+}
+
+
+void Interface::converseSetTextLines(int row) {
+ int pos = row + _converseStartPos;
+ if (pos >= _converseTextCount)
+ pos = -1;
+ if (pos != _conversePos) {
+ _conversePos = pos;
+ draw();
+ }
+}
+
+void Interface::converseDisplayTextLines(Surface *ds) {
+ int relPos;
+ byte foregnd;
+ byte backgnd;
+ byte bulletForegnd;
+ byte bulletBackgnd;
+ const char *str;
+ char bullet[2] = {
+ (char)0xb7, 0
+ };
+ Rect rect(8, _vm->getDisplayInfo().converseTextLines * _vm->getDisplayInfo().converseTextHeight);
+ Point textPoint;
+
+ assert(_conversePanel.buttonsCount >= 6);
+
+ bulletForegnd = kITEColorGreen;
+ bulletBackgnd = kITEColorBlack;
+
+ rect.moveTo(_conversePanel.x + _conversePanel.buttons[0].xOffset,
+ _conversePanel.y + _conversePanel.buttons[0].yOffset);
+
+ ds->drawRect(rect, kITEColorDarkGrey); //fill bullet place
+
+ for (int i = 0; i < _vm->getDisplayInfo().converseTextLines; i++) {
+ relPos = _converseStartPos + i;
+
+ if (_converseTextCount <= relPos) {
+ break;
+ }
+
+ if (_conversePos >= 0 && _converseText[_conversePos].stringNum == _converseText[relPos].stringNum) {
+ foregnd = kITEColorBrightWhite;
+ backgnd = (!_vm->leftMouseButtonPressed()) ? kITEColorDarkGrey : kITEColorGrey;
+ } else {
+ foregnd = kITEColorBlue;
+ backgnd = kITEColorDarkGrey;
+ }
+
+ _conversePanel.calcPanelButtonRect(&_conversePanel.buttons[i], rect);
+ rect.left += 8;
+ ds->drawRect(rect, backgnd);
+
+ str = _converseText[relPos].text;
+
+ if (_converseText[relPos].textNum == 0) { // first entry
+ textPoint.x = rect.left - 6;
+ textPoint.y = rect.top;
+
+ _vm->_font->textDraw(kKnownFontSmall, ds, bullet, textPoint, bulletForegnd, bulletBackgnd, (FontEffectFlags)(kFontShadow | kFontDontmap));
+ }
+ textPoint.x = rect.left + 1;
+ textPoint.y = rect.top;
+ _vm->_font->textDraw(kKnownFontSmall, ds, str, textPoint, foregnd, kITEColorBlack, kFontShadow);
+ }
+
+ if (_converseStartPos != 0) {
+ drawPanelButtonArrow(ds, &_conversePanel, _converseUpButton);
+ }
+
+ if (_converseStartPos != _converseEndPos) {
+ drawPanelButtonArrow(ds, &_conversePanel, _converseDownButton);
+ }
+}
+
+void Interface::converseChangePos(int chg) {
+ if ((chg < 0 && _converseStartPos + chg >= 0) ||
+ (chg > 0 && _converseStartPos < _converseEndPos)) {
+ _converseStartPos += chg;
+ draw();
+ }
+}
+
+void Interface::converseSetPos(int key) {
+ Converse *ct;
+ int selection = key - '1';
+
+ if (selection >= _converseTextCount)
+ return;
+
+ converseSetTextLines(selection);
+
+ ct = &_converseText[_conversePos];
+
+ _vm->_script->finishDialog(ct->replyId, ct->replyFlags, ct->replyBit);
+
+ if (_vm->_puzzle->isActive())
+ _vm->_puzzle->handleReply(ct->replyId);
+
+ _conversePos = -1;
+}
+
+
+void Interface::handleConverseUpdate(const Point& mousePoint) {
+ bool changed;
+
+ PanelButton *last = _conversePanel.currentButton;
+
+ if (!_vm->mouseButtonPressed()) { // remove pressed flag
+ if (_converseUpButton) {
+ _converseUpButton->state = 0;
+ _converseDownButton->state = 0;
+ }
+ }
+
+ _conversePanel.currentButton = converseHitTest(mousePoint);
+ changed = last != _conversePanel.currentButton;
+
+
+ if (_conversePanel.currentButton == NULL) {
+ _conversePos = -1;
+ if (changed) {
+ draw();
+ }
+ return;
+ }
+
+ if (_conversePanel.currentButton->type == kPanelButtonConverseText) {
+ converseSetTextLines(_conversePanel.currentButton->id);
+ }
+
+ if (_conversePanel.currentButton->type == kPanelButtonArrow) {
+ if (_conversePanel.currentButton->state == 1) {
+ //TODO: insert timeout catchup
+ converseChangePos(_conversePanel.currentButton->id);
+ }
+ draw();
+ }
+}
+
+
+void Interface::handleConverseClick(const Point& mousePoint) {
+ _conversePanel.currentButton = converseHitTest(mousePoint);
+
+ if (_conversePanel.currentButton == NULL) {
+ return;
+ }
+
+ if (_conversePanel.currentButton->type == kPanelButtonConverseText) {
+ converseSetPos(_conversePanel.currentButton->ascii);
+ }
+
+ if (_conversePanel.currentButton->type == kPanelButtonArrow) {
+ _conversePanel.currentButton->state = 1;
+ converseChangePos(_conversePanel.currentButton->id);
+ }
+
+}
+
+void Interface::saveState(Common::OutSaveFile *out) {
+ out->writeUint16LE(_inventoryCount);
+
+ for (int i = 0; i < _inventoryCount; i++) {
+ out->writeUint16LE(_inventory[i]);
+ }
+}
+
+void Interface::loadState(Common::InSaveFile *in) {
+ _inventoryCount = in->readUint16LE();
+
+ for (int i = 0; i < _inventoryCount; i++) {
+ _inventory[i] = in->readUint16LE();
+ }
+
+ updateInventory(0);
+}
+
+void Interface::mapPanelShow() {
+ int i;
+ byte *resource;
+ size_t resourceLength, imageLength;
+ Surface *backBuffer;
+ Rect rect;
+ byte *image;
+ int imageWidth, imageHeight;
+ const byte *pal;
+ PalEntry cPal[PAL_ENTRIES];
+
+ _vm->_gfx->showCursor(false);
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ rect.left = rect.top = 0;
+
+ _vm->_resource->loadResource(_interfaceContext,
+ _vm->_resource->convertResourceId(RID_ITE_TYCHO_MAP), resource, resourceLength);
+ if (resourceLength == 0) {
+ error("Interface::mapPanelShow() unable to load Tycho map resource");
+ }
+
+ _vm->_gfx->getCurrentPal(_mapSavedPal);
+
+ for (i = 0; i < 6 ; i++) {
+ _vm->_gfx->palToBlack(_mapSavedPal, 0.2 * i);
+ _vm->_render->drawScene();
+ _vm->_system->delayMillis(5);
+ }
+
+ _vm->_render->setFlag(RF_MAP);
+
+ _vm->decodeBGImage(resource, resourceLength, &image, &imageLength, &imageWidth, &imageHeight);
+ pal = _vm->getImagePal(resource, resourceLength);
+
+ for (i = 0; i < PAL_ENTRIES; i++) {
+ cPal[i].red = *pal++;
+ cPal[i].green = *pal++;
+ cPal[i].blue = *pal++;
+ }
+
+ rect.setWidth(imageWidth);
+ rect.setHeight(imageHeight);
+
+ backBuffer->blit(rect, image);
+
+ // Evil Evil
+ for (i = 0; i < 6 ; i++) {
+ _vm->_gfx->blackToPal(cPal, 0.2 * i);
+ _vm->_render->drawScene();
+ _vm->_system->delayMillis(5);
+ }
+
+ free(resource);
+ free(image);
+
+ setSaveReminderState(false);
+
+ _mapPanelCrossHairState = true;
+}
+
+void Interface::mapPanelClean() {
+ PalEntry pal[PAL_ENTRIES];
+ int i;
+
+ _vm->_gfx->getCurrentPal(pal);
+
+ for (i = 0; i < 6 ; i++) {
+ _vm->_gfx->palToBlack(pal, 0.2 * i);
+ _vm->_render->drawScene();
+ _vm->_system->delayMillis(5);
+ }
+
+ _vm->_render->clearFlag(RF_MAP);
+ setMode(kPanelMain);
+
+ _vm->_gfx->showCursor(true);
+ _vm->_render->drawScene();
+
+ for (i = 0; i < 6 ; i++) {
+ _vm->_gfx->blackToPal(_mapSavedPal, 0.2 * i);
+ _vm->_render->drawScene();
+ _vm->_system->delayMillis(5);
+ }
+}
+
+void Interface::mapPanelDrawCrossHair() {
+ Surface *backBuffer;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+ _mapPanelCrossHairState = !_mapPanelCrossHairState;
+
+ Point mapPosition = _vm->_isoMap->getMapPosition();
+ Rect screen(_vm->getDisplayWidth(), _vm->_scene->getHeight());
+
+ if (screen.contains(mapPosition)) {
+ _vm->_sprite->draw(backBuffer, _vm->getDisplayClip(), _vm->_sprite->_mainSprites,
+ _mapPanelCrossHairState? RID_ITE_SPR_XHAIR1 : RID_ITE_SPR_XHAIR2,
+ mapPosition, 256);
+ }
+}
+
+void Interface::keyBoss() {
+ if (_vm->getGameType() != GType_IHNM)
+ return;
+
+ if (_bossMode != -1 || _fadeMode != kNoFade)
+ return;
+
+ _vm->_sound->pauseVoice();
+ _vm->_sound->pauseSound();
+ _vm->_music->pause();
+
+ int i;
+ byte *resource;
+ size_t resourceLength, imageLength;
+ Surface *backBuffer;
+ Rect rect;
+ byte *image;
+ int imageWidth, imageHeight;
+ const byte *pal;
+ PalEntry cPal[PAL_ENTRIES];
+
+ _vm->_gfx->showCursor(false);
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ rect.left = rect.top = 0;
+
+ _vm->_resource->loadResource(_interfaceContext, RID_IHNM_BOSS_SCREEN, resource, resourceLength);
+ if (resourceLength == 0) {
+ error("Interface::bossKey() unable to load Boss image resource");
+ }
+
+ _bossMode = _panelMode;
+ setMode(kPanelBoss);
+
+ _vm->decodeBGImage(resource, resourceLength, &image, &imageLength, &imageWidth, &imageHeight);
+ rect.setWidth(imageWidth);
+ rect.setHeight(imageHeight);
+
+ _vm->_gfx->getCurrentPal(_mapSavedPal);
+ pal = _vm->getImagePal(resource, resourceLength);
+
+ for (i = 0; i < PAL_ENTRIES; i++) {
+ cPal[i].red = *pal++;
+ cPal[i].green = *pal++;
+ cPal[i].blue = *pal++;
+ }
+
+ backBuffer->blit(rect, image);
+
+ _vm->_gfx->setPalette(cPal);
+
+ free(resource);
+ free(image);
+}
+
+
+void Interface::keyBossExit() {
+ PalEntry pal[PAL_ENTRIES];
+
+ _vm->_sound->resumeVoice();
+ _vm->_sound->resumeSound();
+ _vm->_music->resume();
+
+ _vm->_gfx->getCurrentPal(pal);
+
+ _vm->_gfx->palToBlack(pal, 1);
+ setMode(_bossMode);
+
+ _vm->_render->drawScene();
+
+ _vm->_gfx->blackToPal(_mapSavedPal, 1);
+
+ _vm->_gfx->showCursor(true);
+
+ _bossMode = -1;
+}
+
+
+} // End of namespace Saga
diff --git a/engines/saga/interface.h b/engines/saga/interface.h
new file mode 100644
index 0000000000..bee3fd4a2a
--- /dev/null
+++ b/engines/saga/interface.h
@@ -0,0 +1,466 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Game interface module private header file
+
+#ifndef SAGA_INTERFACE_H__
+#define SAGA_INTERFACE_H__
+
+#include "common/savefile.h"
+
+#include "saga/sprite.h"
+#include "saga/script.h"
+
+namespace Saga {
+
+enum InterfaceUpdateFlags {
+ UPDATE_MOUSEMOVE = 1,
+ UPDATE_LEFTBUTTONCLICK = 2,
+ UPDATE_RIGHTBUTTONCLICK = 4,
+ UPDATE_MOUSECLICK = UPDATE_LEFTBUTTONCLICK | UPDATE_RIGHTBUTTONCLICK,
+ UPDATE_WHEELUP = 8,
+ UPDATE_WHEELDOWN = 16
+};
+
+#define CONVERSE_MAX_TEXTS 64
+#define CONVERSE_MAX_WORK_STRING 128
+
+#define ITE_INVENTORY_SIZE 24
+
+#define VERB_STRLIMIT 32
+
+#define STATUS_TEXT_LEN 128
+#define STATUS_TEXT_INPUT_MAX 256
+
+// Converse-specific stuff
+
+enum PanelModes {
+ kPanelNull,
+ kPanelMain,
+ kPanelOption,
+ kPanelSave, //ex- kPanelTextBox,
+ kPanelQuit,
+ kPanelError,
+ kPanelLoad,
+ kPanelConverse,
+ kPanelProtect,
+ kPanelPlacard,
+ kPanelMap,
+ kPanelSceneSubstitute,
+ kPanelChapterSelection,
+ kPanelCutaway,
+ kPanelVideo,
+ kPanelBoss
+// kPanelInventory
+};
+
+enum FadeModes {
+ kNoFade = 0,
+ kFadeIn,
+ kFadeOut
+};
+
+struct InterfacePanel {
+ int x;
+ int y;
+ byte *image;
+ size_t imageLength;
+ int imageWidth;
+ int imageHeight;
+
+ PanelButton *currentButton;
+ int buttonsCount;
+ PanelButton *buttons;
+ SpriteList sprites;
+
+ InterfacePanel() {
+ x = y = 0;
+ image = NULL;
+ imageLength = 0;
+ imageWidth = imageHeight = 0;
+ currentButton = NULL;
+ buttonsCount = 0;
+ buttons = NULL;
+ }
+
+ PanelButton *getButton(int index) {
+ if ((index >= 0) && (index < buttonsCount)) {
+ return &buttons[index];
+ }
+ return NULL;
+ }
+
+ void getRect(Rect &rect) {
+ rect.left = x;
+ rect.top = y;
+ rect.setWidth(imageWidth);
+ rect.setHeight(imageHeight);
+ }
+
+ void calcPanelButtonRect(const PanelButton* panelButton, Rect &rect) {
+ rect.left = x + panelButton->xOffset;
+ rect.right = rect.left + panelButton->width;
+ rect.top = y + panelButton->yOffset;
+ rect.bottom = rect.top + panelButton->height;
+ }
+
+ PanelButton *hitTest(const Point& mousePoint, int buttonType) {
+ PanelButton *panelButton;
+ Rect rect;
+ int i;
+ for (i = 0; i < buttonsCount; i++) {
+ panelButton = &buttons[i];
+ if (panelButton != NULL) {
+ if ((panelButton->type & buttonType) > 0) {
+ calcPanelButtonRect(panelButton, rect);
+ if (rect.contains(mousePoint)) {
+ return panelButton;
+ }
+ }
+ }
+ }
+ return NULL;
+ }
+
+ void zeroAllButtonState() {
+ int i;
+ for (i = 0; i < buttonsCount; i++) {
+ buttons[i].state = 0;
+ }
+ }
+
+
+};
+
+struct Converse {
+ char *text;
+ int stringNum;
+ int textNum;
+ int replyId;
+ int replyFlags;
+ int replyBit;
+};
+
+
+enum StatusTextInputState {
+ kStatusTextInputFirstRun,
+ kStatusTextInputEntered,
+ kStatusTextInputAborted
+};
+
+class Interface {
+public:
+ Interface(SagaEngine *vm);
+ ~Interface(void);
+
+ int activate();
+ int deactivate();
+ void setSaveReminderState(int state) {
+ _saveReminderState = state;
+ draw();
+ }
+ int getSaveReminderState() {
+ return _saveReminderState;
+ }
+ bool isActive() { return _active; }
+ void setMode(int mode);
+ int getMode(void) const { return _panelMode; }
+ void setFadeMode(int fadeMode) {
+ _fadeMode = fadeMode;
+ draw();
+ }
+ int getFadeMode() const {
+ return _fadeMode;
+ }
+ void rememberMode();
+ void restoreMode();
+ bool isInMainMode() { return _inMainMode; }
+ void setStatusText(const char *text, int statusColor = -1);
+ void loadScenePortraits(int resourceId);
+ void setLeftPortrait(int portrait) {
+ _leftPortrait = portrait;
+ draw();
+ }
+ void setRightPortrait(int portrait) {
+ _rightPortrait = portrait;
+ draw();
+ }
+ void setPortraitBgColor(int red, int green, int blue) {
+ _portraitBgColor.red = red;
+ _portraitBgColor.green = green;
+ _portraitBgColor.blue = blue;
+ }
+
+ void draw();
+ void drawOption();
+ void drawQuit();
+ void drawLoad();
+ void drawSave();
+ void drawProtect();
+ void update(const Point& mousePoint, int updateFlag);
+ void drawStatusBar();
+ void setVerbState(int verb, int state);
+
+ bool processAscii(uint16 ascii, bool synthetic = false);
+ void processKeyUp(uint16 ascii);
+
+ void keyBoss();
+ void keyBossExit();
+
+ void disableAbortSpeeches(bool d) { _disableAbortSpeeches = d; }
+
+ bool _textInput;
+
+ bool _statusTextInput;
+ StatusTextInputState _statusTextInputState;
+ char _statusTextInputString[STATUS_TEXT_INPUT_MAX];
+ void enterStatusString() {
+ _statusTextInput = true;
+ _statusTextInputPos = 0;
+ _statusTextInputString[0] = 0;
+ setStatusText(_statusTextInputString);
+ }
+
+private:
+ static void textInputRepeatCallback(void *refCon);
+
+ void drawInventory(Surface *backBuffer);
+ void updateInventory(int pos);
+ void inventoryChangePos(int chg);
+ void inventorySetPos(int key);
+
+public:
+ void refreshInventory() {
+ updateInventory(_inventoryCount);
+ draw();
+ }
+ void addToInventory(int objectId);
+ void removeFromInventory(int objectId);
+ void clearInventory();
+ int inventoryItemPosition(int objectId);
+ int getInventoryContentByPanelButton(PanelButton * panelButton) {
+ int cell = _inventoryStart + panelButton->id;
+ if (cell >= _inventoryCount) {
+ return 0;
+ }
+ return _inventory[cell];
+ }
+
+ PanelButton *inventoryHitTest(const Point& mousePoint) {
+ return _mainPanel.hitTest(mousePoint, kPanelButtonInventory);
+ }
+ PanelButton *verbHitTest(const Point& mousePoint){
+ return _mainPanel.hitTest(mousePoint, kPanelButtonVerb);
+ }
+ void saveState(Common::OutSaveFile *out);
+ void loadState(Common::InSaveFile *in);
+
+ void mapPanelDrawCrossHair();
+
+ int32 getProtectHash() { return _protectHash; }
+
+private:
+ void handleMainUpdate(const Point& mousePoint); // main panel update
+ void handleMainClick(const Point& mousePoint); // main panel click
+
+ PanelButton *converseHitTest(const Point& mousePoint) {
+ return _conversePanel.hitTest(mousePoint, kPanelAllButtons);
+ }
+ void handleConverseUpdate(const Point& mousePoint); // converse panel update
+ void handleConverseClick(const Point& mousePoint); // converse panel click
+
+ PanelButton *optionHitTest(const Point& mousePoint) {
+ return _optionPanel.hitTest(mousePoint, kPanelButtonOptionSaveFiles | kPanelButtonOption | kPanelButtonOptionSlider);
+ }
+ void handleOptionUpdate(const Point& mousePoint); // option panel update
+ void handleOptionClick(const Point& mousePoint); // option panel click
+
+ PanelButton *quitHitTest(const Point& mousePoint) {
+ return _quitPanel.hitTest(mousePoint, kPanelAllButtons);
+ }
+ void handleQuitUpdate(const Point& mousePoint); // quit panel update
+ void handleQuitClick(const Point& mousePoint); // quit panel click
+
+ PanelButton *loadHitTest(const Point& mousePoint) {
+ return _loadPanel.hitTest(mousePoint, kPanelAllButtons);
+ }
+ void handleLoadUpdate(const Point& mousePoint); // load panel update
+ void handleLoadClick(const Point& mousePoint); // load panel click
+
+ PanelButton *saveHitTest(const Point& mousePoint) {
+ return _savePanel.hitTest(mousePoint, kPanelAllButtons);
+ }
+ void handleSaveUpdate(const Point& mousePoint); // save panel update
+ void handleSaveClick(const Point& mousePoint); // save panel click
+
+ void handleChapterSelectionUpdate(const Point& mousePoint);
+ void handleChapterSelectionClick(const Point& mousePoint);
+
+ void mapPanelShow();
+ void mapPanelClean();
+
+ void lockMode() { _lockedMode = _panelMode; }
+ void unlockMode() { _panelMode = _lockedMode; }
+
+ void setOption(PanelButton *panelButton);
+ void setQuit(PanelButton *panelButton);
+ void setLoad(PanelButton *panelButton);
+ void setSave(PanelButton *panelButton);
+
+ void drawTextInput(Surface *ds, InterfacePanel *panel, PanelButton *panelButton);
+ void drawPanelText(Surface *ds, InterfacePanel *panel, PanelButton *panelButton);
+ void drawPanelButtonText(Surface *ds, InterfacePanel *panel, PanelButton *panelButton);
+ enum ButtonKind {
+ kButton,
+ kSlider,
+ kEdit
+ };
+ void drawButtonBox(Surface *ds, const Rect &rect, ButtonKind kind, bool down);
+ void drawPanelButtonArrow(Surface *ds, InterfacePanel *panel, PanelButton *panelButton);
+ void drawVerbPanelText(Surface *ds, PanelButton *panelButton, KnownColor textKnownColor, KnownColor textShadowKnownColor);
+ void drawVerbPanel(Surface *backBuffer, PanelButton* panelButton);
+ void calcOptionSaveSlider();
+ bool processTextInput(uint16 ascii);
+ void processStatusTextInput(uint16 ascii);
+ void textInputStartRepeat(uint16 ascii);
+ void textInputRepeat(void);
+
+public:
+ void converseInit(void);
+ void converseClear(void);
+ bool converseAddText(const char *text, int replyId, byte replyFlags, int replyBit);
+ void converseDisplayText();
+ void converseSetTextLines(int row);
+ void converseChangePos(int chg);
+ void converseSetPos(int key);
+
+private:
+ void converseDisplayTextLines(Surface *ds);
+ PanelButton *getPanelButtonByVerbType(int verb) {
+ if ((verb < 0) || (verb >= kVerbTypeIdsMax)) {
+ error("Interface::getPanelButtonByVerbType wrong verb");
+ }
+ return _verbTypeToPanelButton[verb];
+ }
+
+ void validateOptionButtons() {
+ if (!_vm->isSaveListFull() && (_optionSaveFileTitleNumber == 0) && (_optionPanel.currentButton != NULL)) {
+ if (_optionPanel.currentButton->id == kTextLoad) {
+ _optionPanel.currentButton = NULL;
+ }
+ }
+ }
+ void validateSaveButtons() {
+ if ((_textInputStringLength == 0) && (_savePanel.currentButton != NULL)) {
+ if (_savePanel.currentButton->id == kTextSave) {
+ _savePanel.currentButton = NULL;
+ }
+ }
+ }
+
+public:
+ SpriteList _defPortraits;
+
+private:
+ SagaEngine *_vm;
+
+ ResourceContext *_interfaceContext;
+ InterfacePanel _mainPanel;
+ PanelButton *_inventoryUpButton;
+ PanelButton *_inventoryDownButton;
+ InterfacePanel _conversePanel;
+ PanelButton *_converseUpButton;
+ PanelButton *_converseDownButton;
+ SpriteList _scenePortraits;
+ PanelButton *_verbTypeToPanelButton[kVerbTypeIdsMax];
+ InterfacePanel _optionPanel;
+ PanelButton * _optionSaveFileSlider;
+ PanelButton * _optionSaveFilePanel;
+ InterfacePanel _quitPanel;
+ InterfacePanel _loadPanel;
+ InterfacePanel _savePanel;
+ PanelButton * _saveEdit;
+ InterfacePanel _protectPanel;
+ PanelButton * _protectEdit;
+
+ bool _disableAbortSpeeches;
+
+ int _saveReminderState;
+ bool _active;
+ int _fadeMode;
+ int _panelMode;
+ int _savedMode;
+ int _lockedMode;
+ int _bossMode;
+ bool _inMainMode;
+ char _statusText[STATUS_TEXT_LEN];
+ int _statusOnceColor;
+ int _leftPortrait;
+ int _rightPortrait;
+ PalEntry _portraitBgColor;
+
+ Point _lastMousePoint;
+
+ uint16 *_inventory;
+ int _inventorySize;
+ int _inventoryStart;
+ int _inventoryEnd;
+ int _inventoryPos;
+ int _inventoryBox;
+ int _inventoryCount;
+
+ char _converseWorkString[CONVERSE_MAX_WORK_STRING];
+ Converse _converseText[CONVERSE_MAX_TEXTS];
+ int _converseTextCount;
+ int _converseStrCount;
+ int _converseStartPos;
+ int _converseEndPos;
+ int _conversePos;
+
+ uint _optionSaveFileTop;
+ uint _optionSaveFileTitleNumber;
+ int16 _optionSaveFileMouseOff;
+ Rect _optionSaveRectTop;
+ Rect _optionSaveRectSlider;
+ Rect _optionSaveRectBottom;
+
+ char _textInputString[SAVE_TITLE_SIZE];
+ uint _textInputStringLength;
+ uint _textInputPos;
+ uint _textInputMaxWidth;
+
+ uint _statusTextInputPos;
+
+ int _textInputRepeatPhase;
+ uint16 _textInputRepeatChar;
+
+ PalEntry _mapSavedPal[PAL_ENTRIES];
+ bool _mapPanelCrossHairState;
+
+ int32 _protectHash;
+};
+
+} // End of namespace Saga
+
+#endif /* INTERFACE_H__ */
diff --git a/engines/saga/isomap.cpp b/engines/saga/isomap.cpp
new file mode 100644
index 0000000000..6acbf8ba70
--- /dev/null
+++ b/engines/saga/isomap.cpp
@@ -0,0 +1,1694 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Isometric level module
+#include "saga/saga.h"
+
+#include "saga/gfx.h"
+
+#include "saga/resnames.h"
+#include "saga/scene.h"
+#include "saga/isomap.h"
+#include "saga/stream.h"
+
+namespace Saga {
+
+enum MaskRules {
+ kMaskRuleNever = 0,
+ kMaskRuleAlways,
+ kMaskRuleUMIN,
+ kMaskRuleUMID,
+ kMaskRuleUMAX,
+ kMaskRuleVMIN,
+ kMaskRuleVMID,
+ kMaskRuleVMAX,
+ kMaskRuleYMIN,
+ kMaskRuleYMID,
+ kMaskRuleYMAX,
+ kMaskRuleUVMAX,
+ kMaskRuleUVMIN,
+ kMaskRuleUorV,
+ kMaskRuleUandV
+};
+
+
+static const IsoMap::TilePoint normalDirTable[8] = {
+ { 1, 1, 0, SAGA_DIAG_NORMAL_COST},
+ { 1, 0, 0, SAGA_STRAIGHT_NORMAL_COST},
+ { 1,-1, 0, SAGA_DIAG_NORMAL_COST},
+ { 0,-1, 0, SAGA_STRAIGHT_NORMAL_COST},
+ {-1,-1, 0, SAGA_DIAG_NORMAL_COST},
+ {-1, 0, 0, SAGA_STRAIGHT_NORMAL_COST},
+ {-1, 1, 0, SAGA_DIAG_NORMAL_COST},
+ { 0, 1, 0, SAGA_STRAIGHT_NORMAL_COST},
+};
+
+static const IsoMap::TilePoint easyDirTable[8] = {
+ { 1, 1, 0, SAGA_DIAG_EASY_COST},
+ { 1, 0, 0, SAGA_STRAIGHT_EASY_COST},
+ { 1,-1, 0, SAGA_DIAG_EASY_COST},
+ { 0,-1, 0, SAGA_STRAIGHT_EASY_COST},
+ {-1,-1, 0, SAGA_DIAG_EASY_COST},
+ {-1, 0, 0, SAGA_STRAIGHT_EASY_COST},
+ {-1, 1, 0, SAGA_DIAG_EASY_COST},
+ { 0, 1, 0, SAGA_STRAIGHT_EASY_COST},
+};
+
+static const IsoMap::TilePoint hardDirTable[8] = {
+ { 1, 1, 0, SAGA_DIAG_HARD_COST},
+ { 1, 0, 0, SAGA_STRAIGHT_HARD_COST},
+ { 1,-1, 0, SAGA_DIAG_HARD_COST},
+ { 0,-1, 0, SAGA_STRAIGHT_HARD_COST},
+ {-1,-1, 0, SAGA_DIAG_HARD_COST},
+ {-1, 0, 0, SAGA_STRAIGHT_HARD_COST},
+ {-1, 1, 0, SAGA_DIAG_HARD_COST},
+ { 0, 1, 0, SAGA_STRAIGHT_HARD_COST},
+};
+
+IsoMap::IsoMap(SagaEngine *vm) : _vm(vm) {
+ _tileData = NULL;
+ _tilesCount = 0;
+ _tilePlatformList = NULL;
+ _tilePlatformsCount = 0;
+ _metaTileList = NULL;
+ _metaTilesCount = 0;
+ _multiTable = NULL;
+ _multiCount = 0;
+ _multiTableData = NULL;
+ _multiDataCount = 0;
+ _viewScroll.x = (128 - 8) * 16;
+ _viewScroll.x = (128 - 8) * 16 - 64;
+ _viewDiff = 1;
+
+}
+
+void IsoMap::loadImages(const byte *resourcePointer, size_t resourceLength) {
+ IsoTileData *tileData;
+ uint16 i;
+
+ if (resourceLength == 0) {
+ error("IsoMap::loadImages wrong resourceLength");
+ }
+
+ _tileData = (byte*)malloc(resourceLength);
+ _tileDataLength = resourceLength;
+ memcpy(_tileData, resourcePointer, resourceLength);
+
+ MemoryReadStreamEndian readS(_tileData, _tileDataLength, _vm->isBigEndian());
+ readS.readUint16(); // skip
+ _tilesCount = readS.readUint16();
+ _tilesCount = _tilesCount / SAGA_ISOTILEDATA_LEN;
+
+ readS.seek(0);
+
+ _tilesTable = (IsoTileData *)malloc(_tilesCount * sizeof(*_tilesTable));
+ if (_tilesTable == NULL) {
+ memoryError("IsoMap::loadImages");
+ }
+
+ for (i = 0; i < _tilesCount; i++) {
+ tileData = &_tilesTable[i];
+ tileData->height = readS.readByte();
+ tileData->attributes = readS.readSByte();
+ tileData->offset = readS.readUint16();
+ tileData->terrainMask = readS.readUint16();
+ tileData->FGDBGDAttr = readS.readByte();
+ readS.readByte(); //skip
+ }
+
+}
+
+void IsoMap::loadPlatforms(const byte * resourcePointer, size_t resourceLength) {
+ TilePlatformData *tilePlatformData;
+ uint16 i, x, y;
+
+ if (resourceLength == 0) {
+ error("IsoMap::loadPlatforms wrong resourceLength");
+ }
+
+ MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+
+ _tilePlatformsCount = resourceLength / SAGA_TILEPLATFORMDATA_LEN;
+ _tilePlatformList = (TilePlatformData *)malloc(_tilePlatformsCount * sizeof(*_tilePlatformList));
+ if (_tilePlatformList == NULL) {
+ memoryError("IsoMap::loadPlatforms");
+ }
+
+ for (i = 0; i < _tilePlatformsCount; i++) {
+ tilePlatformData = &_tilePlatformList[i];
+ tilePlatformData->metaTile = readS.readSint16();
+ tilePlatformData->height = readS.readSint16();
+ tilePlatformData->highestPixel = readS.readSint16();
+ tilePlatformData->vBits = readS.readByte();
+ tilePlatformData->uBits = readS.readByte();
+ for (x = 0; x < SAGA_PLATFORM_W; x++) {
+ for (y = 0; y < SAGA_PLATFORM_W; y++) {
+ tilePlatformData->tiles[x][y] = readS.readSint16();
+ }
+ }
+ }
+
+}
+
+void IsoMap::loadMap(const byte * resourcePointer, size_t resourceLength) {
+ uint16 x, y;
+
+ if (resourceLength != SAGA_TILEMAP_LEN) {
+ error("IsoMap::loadMap wrong resourceLength");
+ }
+
+ MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ _tileMap.edgeType = readS.readByte();
+ readS.readByte(); //skip
+
+ for (x = 0; x < SAGA_TILEMAP_W; x++) {
+ for (y = 0; y < SAGA_TILEMAP_H; y++) {
+ _tileMap.tilePlatforms[x][y] = readS.readSint16();
+ }
+ }
+
+}
+
+void IsoMap::loadMetaTiles(const byte * resourcePointer, size_t resourceLength) {
+ MetaTileData *metaTileData;
+ uint16 i, j;
+
+ if (resourceLength == 0) {
+ error("IsoMap::loadMetaTiles wrong resourceLength");
+ }
+
+ MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ _metaTilesCount = resourceLength / SAGA_METATILEDATA_LEN;
+
+ _metaTileList = (MetaTileData *)malloc(_metaTilesCount * sizeof(*_metaTileList));
+ if (_metaTileList == NULL) {
+ memoryError("IsoMap::loadMetaTiles");
+ }
+
+ for (i = 0; i < _metaTilesCount; i++) {
+ metaTileData = &_metaTileList[i];
+ metaTileData->highestPlatform = readS.readUint16();
+ metaTileData->highestPixel = readS.readUint16();
+ for (j = 0; j < SAGA_MAX_PLATFORM_H; j++) {
+ metaTileData->stack[j] = readS.readSint16();
+ }
+ }
+}
+
+void IsoMap::loadMulti(const byte * resourcePointer, size_t resourceLength) {
+ MultiTileEntryData *multiTileEntryData;
+ uint16 i;
+ int16 offsetDiff;
+
+ if (resourceLength < 2) {
+ error("IsoMap::loadMetaTiles wrong resourceLength");
+ }
+
+ MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+ _multiCount = readS.readUint16();
+ _multiTable = (MultiTileEntryData *)malloc(_multiCount * sizeof(*_multiTable));
+ if (_multiTable == NULL) {
+ memoryError("IsoMap::loadMulti");
+ }
+
+ for (i = 0; i < _multiCount; i++) {
+ multiTileEntryData = &_multiTable[i];
+ readS.readUint32();//skip
+ multiTileEntryData->offset = readS.readSint16();
+ multiTileEntryData->u = readS.readByte();
+ multiTileEntryData->v = readS.readByte();
+ multiTileEntryData->h = readS.readByte();
+ multiTileEntryData->uSize = readS.readByte();
+ multiTileEntryData->vSize = readS.readByte();
+ multiTileEntryData->numStates = readS.readByte();
+ multiTileEntryData->currentState = readS.readByte();
+ readS.readByte();//skip
+ }
+
+ offsetDiff = (readS.pos() - 2);
+
+ for (i = 0; i < _multiCount; i++) {
+ _multiTable[i].offset -= offsetDiff;
+ }
+
+ _multiDataCount = (readS.size() - readS.pos()) / 2;
+
+ _multiTableData = (int16 *)malloc(_multiDataCount * sizeof(*_multiTableData));
+ for (i = 0; i < _multiDataCount; i++) {
+ _multiTableData[i] = readS.readSint16();
+ }
+}
+
+void IsoMap::freeMem() {
+ free(_tileData);
+ _tileData = NULL;
+ _tilesCount = 0;
+ free(_tilePlatformList);
+ _tilePlatformList = NULL;
+ _tilePlatformsCount = 0;
+ free(_metaTileList);
+ _metaTileList = NULL;
+ _metaTilesCount = 0;
+ free(_multiTable);
+ _multiTable = NULL;
+ _multiCount = 0;
+ free(_multiTableData);
+ _multiTableData = NULL;
+ _multiDataCount = 0;
+}
+
+void IsoMap::adjustScroll(bool jump) {
+ Point playerPoint;
+ Point minScrollPos;
+ Point maxScrollPos;
+
+
+ tileCoordsToScreenPoint(_vm->_actor->_centerActor->_location, playerPoint);
+
+ if (_vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) {
+ _mapPosition.x = (playerPoint.x + _viewScroll.x) * 30 / 100 - (381);
+ _mapPosition.y = (playerPoint.y + _viewScroll.y) * 30 / 100 - (342);
+ }
+
+ if (_vm->_actor->_centerActor != _vm->_actor->_protagonist) {
+ playerPoint.y -= 24;
+ }
+ playerPoint.y -= 28;
+
+ playerPoint.x += _viewScroll.x - _vm->getDisplayWidth()/2;
+ playerPoint.y += _viewScroll.y - _vm->_scene->getHeight()/2;
+
+ minScrollPos.x = playerPoint.x - SAGA_SCROLL_LIMIT_X1;
+ minScrollPos.y = playerPoint.y - SAGA_SCROLL_LIMIT_Y1;
+
+ maxScrollPos.x = playerPoint.x + SAGA_SCROLL_LIMIT_X1;
+ maxScrollPos.y = playerPoint.y + SAGA_SCROLL_LIMIT_Y2;
+
+ if (jump) {
+ if (_viewScroll.y < minScrollPos.y) {
+ _viewScroll.y = minScrollPos.y;
+ }
+ if (_viewScroll.y > maxScrollPos.y) {
+ _viewScroll.y = maxScrollPos.y;
+ }
+ if (_viewScroll.x < minScrollPos.x) {
+ _viewScroll.x = minScrollPos.x;
+ }
+ if (_viewScroll.x > maxScrollPos.x) {
+ _viewScroll.x = maxScrollPos.x;
+ }
+ } else {
+ _viewScroll.y = smoothSlide( _viewScroll.y, minScrollPos.y, maxScrollPos.y );
+ _viewScroll.x = smoothSlide( _viewScroll.x, minScrollPos.x, maxScrollPos.x );
+ }
+
+ if (_vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) {
+ ObjectData *obj;
+ uint16 objectId;
+ objectId = _vm->_actor->objIndexToId(ITE_OBJ_MAP);
+ obj = _vm->_actor->getObj(objectId);
+ if (obj->_sceneNumber != ITE_SCENE_INV) {
+ _viewScroll.x = 1552 + 8;
+ _viewScroll.y = 1456 + 8;
+ }
+ }
+}
+
+int16 IsoMap::findMulti(int16 tileIndex, int16 absU, int16 absV, int16 absH) {
+ MultiTileEntryData *multiTileEntryData;
+ int16 ru;
+ int16 rv;
+ int16 mu;
+ int16 mv;
+ int16 state;
+ uint16 i, offset;
+ int16 *tiles;
+
+ ru = (tileIndex >> 13) & 0x03;
+ rv = (tileIndex >> 11) & 0x03;
+ mu = absU - ru;
+ mv = absV - rv;
+
+ tileIndex = 0;
+ for (i = 0; i < _multiCount; i++) {
+ multiTileEntryData = &_multiTable[i];
+
+ if ((multiTileEntryData->u == mu) &&
+ (multiTileEntryData->v == mv) &&
+ (multiTileEntryData->h == absH)) {
+ state = multiTileEntryData->currentState;
+
+ offset = (ru + state * multiTileEntryData->uSize) * multiTileEntryData->vSize + rv;
+ offset *= sizeof(*_multiTableData);
+ offset += multiTileEntryData->offset;
+ if (offset + sizeof(*_multiTableData) - 1 >= _multiDataCount * sizeof(*_multiTableData)) {
+ error("wrong multiTileEntryData->offset");
+ }
+ tiles = (int16*)((byte*)_multiTableData + offset);
+ tileIndex = *tiles;
+ if (tileIndex >= 256) {
+ warning("something terrible happened");
+ return 1;
+ }
+ return tileIndex;
+ }
+ }
+
+ return 1;
+}
+
+void IsoMap::draw(Surface *ds) {
+
+ _tileClip = _vm->_scene->getSceneClip();
+ ds->drawRect(_tileClip, 0);
+ drawTiles(ds, NULL);
+}
+
+void IsoMap::setMapPosition(int x, int y) {
+ _mapPosition.x = x;
+ _mapPosition.y = y;
+}
+
+void IsoMap::drawSprite(Surface *ds, SpriteList &spriteList, int spriteNumber, const Location &location, const Point &screenPosition, int scale) {
+ int width;
+ int height;
+ int xAlign;
+ int yAlign;
+ const byte *spriteBuffer;
+ Point spritePointer;
+ Rect clip(_vm->_scene->getSceneClip());
+
+ _vm->_sprite->getScaledSpriteBuffer(spriteList, spriteNumber, scale, width, height, xAlign, yAlign, spriteBuffer);
+
+ spritePointer.x = screenPosition.x + xAlign;
+ spritePointer.y = screenPosition.y + yAlign;
+
+ _tileClip.left = spritePointer.x;
+ _tileClip.top = spritePointer.y;
+ _tileClip.right = spritePointer.x + width;
+ _tileClip.bottom = spritePointer.y + height;
+
+ if (_tileClip.left < 0) {
+ _tileClip.left = 0;
+ }
+ if (_tileClip.right > _vm->getDisplayWidth()) {
+ _tileClip.right = _vm->getDisplayWidth();
+ }
+ if (_tileClip.top < 0) {
+ _tileClip.top = 0;
+ }
+ if (_tileClip.bottom > _vm->_scene->getHeight()) {
+ _tileClip.bottom = _vm->_scene->getHeight();
+ }
+
+ _vm->_sprite->drawClip(ds, clip, spritePointer, width, height, spriteBuffer);
+ drawTiles(ds, &location);
+}
+
+
+void IsoMap::drawTiles(Surface *ds, const Location *location) {
+ Point view1;
+ Point fineScroll;
+ Point tileScroll;
+ Point metaTileY;
+ Point metaTileX;
+ int16 u0, v0,
+ u1, v1,
+ u2, v2,
+ uc, vc;
+ uint16 metaTileIndex;
+ Location rLocation;
+ int16 workAreaWidth;
+ int16 workAreaHeight;
+
+ tileScroll.x = _viewScroll.x >> 4;
+ tileScroll.y = _viewScroll.y >> 4;
+
+ fineScroll.x = _viewScroll.x & 0xf;
+ fineScroll.y = _viewScroll.y & 0xf;
+
+ view1.x = tileScroll.x - (8 * SAGA_TILEMAP_W);
+ view1.y = (8 * SAGA_TILEMAP_W) - tileScroll.y;
+
+ u0 = ((view1.y + 64) * 2 + view1.x) >> 4;
+ v0 = ((view1.y + 64) * 2 - view1.x) >> 4;
+
+ metaTileY.x = (u0 - v0) * 128 - (view1.x * 16 + fineScroll.x);
+ metaTileY.y = (view1.y * 16 - fineScroll.y) - (u0 + v0) * 64;
+
+ workAreaWidth = _vm->getDisplayWidth() + 128;
+ workAreaHeight = _vm->_scene->getHeight() + 128 + 80;
+
+ for (u1 = u0, v1 = v0; metaTileY.y < workAreaHeight; u1--, v1-- ) {
+ metaTileX = metaTileY;
+
+ for (u2 = u1, v2 = v1; metaTileX.x < workAreaWidth; u2++, v2--, metaTileX.x += 256) {
+
+ uc = u2 & (SAGA_TILEMAP_W - 1);
+ vc = v2 & (SAGA_TILEMAP_W - 1);
+
+ if (uc != u2 || vc != v2) {
+ metaTileIndex = 0;
+ switch ( _tileMap.edgeType) {
+ case kEdgeTypeBlack:
+ continue;
+ case kEdgeTypeFill0:
+ break;
+ case kEdgeTypeFill1:
+ metaTileIndex = 1;
+ break;
+ case kEdgeTypeRpt:
+ uc = clamp( 0, u2, SAGA_TILEMAP_W - 1);
+ vc = clamp( 0, v2, SAGA_TILEMAP_W - 1);
+ metaTileIndex = _tileMap.tilePlatforms[uc][vc];
+ break;
+ case kEdgeTypeWrap:
+ metaTileIndex = _tileMap.tilePlatforms[uc][vc];
+ break;
+ }
+ } else {
+ metaTileIndex = _tileMap.tilePlatforms[uc][vc];
+ }
+
+ if (location != NULL) {
+ rLocation.u() = location->u() - (u2 << 7);
+ rLocation.v() = location->v() - (v2 << 7);
+ rLocation.z = location->z;
+ drawSpriteMetaTile(ds, metaTileIndex, metaTileX, rLocation, u2 << 3, v2 << 3);
+ } else {
+ drawMetaTile(ds, metaTileIndex, metaTileX, u2 << 3, v2 << 3);
+ }
+ }
+
+ metaTileY.y += 64;
+
+ metaTileX = metaTileY;
+
+ metaTileX.x -= 128;
+
+ for (u2 = u1 - 1, v2 = v1; metaTileX.x < workAreaWidth; u2++, v2--, metaTileX.x += 256) {
+
+ uc = u2 & (SAGA_TILEMAP_W - 1);
+ vc = v2 & (SAGA_TILEMAP_W - 1);
+
+ if (uc != u2 || vc != v2) {
+ metaTileIndex = 0;
+ switch ( _tileMap.edgeType) {
+ case kEdgeTypeBlack:
+ continue;
+ case kEdgeTypeFill0:
+ break;
+ case kEdgeTypeFill1:
+ metaTileIndex = 1;
+ break;
+ case kEdgeTypeRpt:
+ uc = clamp( 0, u2, SAGA_TILEMAP_W - 1);
+ vc = clamp( 0, v2, SAGA_TILEMAP_W - 1);
+ metaTileIndex = _tileMap.tilePlatforms[uc][vc];
+ break;
+ case kEdgeTypeWrap:
+ metaTileIndex = _tileMap.tilePlatforms[uc][vc];
+ break;
+ }
+ } else {
+ metaTileIndex = _tileMap.tilePlatforms[uc][vc];
+ }
+
+ if (location != NULL) {
+ rLocation.u() = location->u() - (u2 << 7);
+ rLocation.v() = location->v() - (v2 << 7);
+ rLocation.z = location->z;
+ drawSpriteMetaTile(ds, metaTileIndex, metaTileX, rLocation, u2 << 3, v2 << 3);
+ } else {
+ drawMetaTile(ds, metaTileIndex, metaTileX, u2 << 3, v2 << 3);
+ }
+ }
+ metaTileY.y += 64;
+ }
+
+}
+
+void IsoMap::drawSpriteMetaTile(Surface *ds, uint16 metaTileIndex, const Point &point, Location &location, int16 absU, int16 absV) {
+ MetaTileData * metaTile;
+ uint16 high;
+ int16 platformIndex;
+ Point platformPoint;
+ platformPoint = point;
+
+ if (_metaTilesCount <= metaTileIndex) {
+ error("IsoMap::drawMetaTile wrong metaTileIndex");
+ }
+
+ metaTile = &_metaTileList[metaTileIndex];
+
+ if (metaTile->highestPlatform > 18) {
+ metaTile->highestPlatform = 0;
+ }
+
+ for (high = 0; high <= metaTile->highestPlatform; high++, platformPoint.y -= 8, location.z -= 8) {
+ assert(SAGA_MAX_PLATFORM_H > high);
+ platformIndex = metaTile->stack[high];
+
+ if (platformIndex >= 0) {
+ drawSpritePlatform( ds, platformIndex, platformPoint, location, absU, absV, high );
+ }
+ }
+}
+
+void IsoMap::drawMetaTile(Surface *ds, uint16 metaTileIndex, const Point &point, int16 absU, int16 absV) {
+ MetaTileData * metaTile;
+ uint16 high;
+ int16 platformIndex;
+ Point platformPoint;
+ platformPoint = point;
+
+ if (_metaTilesCount <= metaTileIndex) {
+ error("IsoMap::drawMetaTile wrong metaTileIndex");
+ }
+
+ metaTile = &_metaTileList[metaTileIndex];
+
+ if (metaTile->highestPlatform > 18) {
+ metaTile->highestPlatform = 0;
+ }
+
+ for (high = 0; high <= metaTile->highestPlatform; high++, platformPoint.y -= 8) {
+ assert(SAGA_MAX_PLATFORM_H > high);
+ platformIndex = metaTile->stack[high];
+
+ if (platformIndex >= 0) {
+ drawPlatform( ds, platformIndex, platformPoint, absU, absV, high );
+ }
+ }
+}
+
+void IsoMap::drawSpritePlatform(Surface *ds, uint16 platformIndex, const Point &point, const Location &location, int16 absU, int16 absV, int16 absH) {
+ TilePlatformData *tilePlatform;
+ int16 u, v;
+ Point s;
+ Point s0;
+ uint16 tileIndex;
+ Location copyLocation(location);
+
+ if (_tilePlatformsCount <= platformIndex) {
+ error("IsoMap::drawPlatform wrong platformIndex");
+ }
+
+ tilePlatform = &_tilePlatformList[platformIndex];
+
+ if ((point.y <= _tileClip.top) || (point.y - SAGA_MAX_TILE_H - SAGA_PLATFORM_W * SAGA_TILE_NOMINAL_H >= _tileClip.bottom)) {
+ return;
+ }
+
+ s0 = point;
+ s0.y -= (((SAGA_PLATFORM_W - 1) + (SAGA_PLATFORM_W - 1)) * 8);
+
+ for (v = SAGA_PLATFORM_W - 1,
+ copyLocation.v() = location.v() - ((SAGA_PLATFORM_W - 1) << 4);
+ v >= 0 && s0.y - SAGA_MAX_TILE_H < _tileClip.bottom && s0.x - 128 < _tileClip.right;
+ v--, copyLocation.v() += 16, s0.x += 16, s0.y += 8) {
+
+ if ((tilePlatform->vBits & (1 << v)) == 0) {
+ continue;
+ }
+
+ if (s0.x + 128 + 32 < _tileClip.left) {
+ continue;
+ }
+
+ s = s0;
+
+ for (u = SAGA_PLATFORM_W - 1,
+ copyLocation.u() = location.u() - ((SAGA_PLATFORM_W - 1) << 4);
+ u >= 0 && s.x + 32 > _tileClip.left && s.y - SAGA_MAX_TILE_H < _tileClip.bottom;
+ u--, copyLocation.u() += 16, s.x -= 16, s.y += 8 ) {
+ if (s.x < _tileClip.right && s.y > _tileClip.top) {
+
+ tileIndex = tilePlatform->tiles[u][v];
+ if (tileIndex != 0) {
+ if (tileIndex & SAGA_MULTI_TILE) {
+ tileIndex = findMulti(tileIndex, absU + u, absV + v, absH);
+ }
+
+ drawTile(ds, tileIndex, s, &copyLocation);
+ }
+ }
+ }
+ }
+}
+
+void IsoMap::drawPlatform(Surface *ds, uint16 platformIndex, const Point &point, int16 absU, int16 absV, int16 absH) {
+ TilePlatformData *tilePlatform;
+ int16 u, v;
+ Point s;
+ Point s0;
+ uint16 tileIndex;
+
+ if (_tilePlatformsCount <= platformIndex) {
+ error("IsoMap::drawPlatform wrong platformIndex");
+ }
+
+ tilePlatform = &_tilePlatformList[platformIndex];
+
+ if ((point.y <= _tileClip.top) || (point.y - SAGA_MAX_TILE_H - SAGA_PLATFORM_W * SAGA_TILE_NOMINAL_H >= _tileClip.bottom)) {
+ return;
+ }
+
+ s0 = point;
+ s0.y -= (((SAGA_PLATFORM_W - 1) + (SAGA_PLATFORM_W - 1)) * 8);
+
+ for (v = SAGA_PLATFORM_W - 1;
+ v >= 0 && s0.y - SAGA_MAX_TILE_H < _tileClip.bottom && s0.x - 128 < _tileClip.right;
+ v--, s0.x += 16, s0.y += 8) {
+
+ if ((tilePlatform->vBits & (1 << v)) == 0) {
+ continue;
+ }
+
+ if (s0.x + 128 + 32 < _tileClip.left) {
+ continue;
+ }
+
+ s = s0;
+
+ for (u = SAGA_PLATFORM_W - 1;
+ u >= 0 && s.x + 32 > _tileClip.left && s.y - SAGA_MAX_TILE_H < _tileClip.bottom;
+ u--, s.x -= 16, s.y += 8 ) {
+ if (s.x < _tileClip.right && s.y > _tileClip.top) {
+
+ tileIndex = tilePlatform->tiles[u][v];
+ if (tileIndex > 1) {
+ if (tileIndex & SAGA_MULTI_TILE) {
+ tileIndex = findMulti(tileIndex, absU + u, absV + v, absH);
+ }
+
+ drawTile(ds, tileIndex, s, NULL);
+ }
+ }
+ }
+ }
+}
+
+#define THRESH0 0
+#define THRESH8 8
+#define THRESH16 16
+
+void IsoMap::drawTile(Surface *ds, uint16 tileIndex, const Point &point, const Location *location) {
+ const byte *tilePointer;
+ const byte *readPointer;
+ byte *drawPointer;
+ Point drawPoint;
+ int height;
+ int widthCount = 0;
+ int row, col, count, lowBound;
+ int bgRunCount;
+ int fgRunCount;
+
+
+ if (tileIndex >= _tilesCount) {
+ error("IsoMap::drawTile wrong tileIndex");
+ }
+
+
+ if (point.x + SAGA_ISOTILE_WIDTH < _tileClip.left) {
+ return;
+ }
+
+ if (point.x - SAGA_ISOTILE_WIDTH >= _tileClip.right) {
+ return;
+ }
+
+ tilePointer = _tileData + _tilesTable[tileIndex].offset;
+ height = _tilesTable[tileIndex].height;
+
+ if ((height <= 8) || (height > 64)) {
+ return;
+ }
+
+ drawPoint = point;
+ drawPoint.y -= height;
+
+ if (drawPoint.y >= _tileClip.bottom) {
+ return;
+ }
+
+ if (location != NULL) {
+ if (location->z <= -16) {
+ if (location->z <= -48) {
+ if (location->u() < -THRESH8 || location->v() < -THRESH8) {
+ return;
+ }
+ } else {
+ if (location->u() < THRESH0 || location->v() < THRESH0) {
+ return;
+ }
+ }
+ } else {
+ if (location->z >= 16) {
+ return;
+ } else {
+ switch (_tilesTable[tileIndex].GetMaskRule()) {
+ case kMaskRuleNever:
+ return;
+ case kMaskRuleAlways:
+ break;
+ case kMaskRuleUMIN:
+ if (location->u() < THRESH0) {
+ return;
+ }
+ break;
+ case kMaskRuleUMID:
+ if (location->u() < THRESH8) {
+ return;
+ }
+ break;
+ case kMaskRuleUMAX:
+ if (location->u() < THRESH16) {
+ return;
+ }
+ break;
+ case kMaskRuleVMIN:
+ if (location->v() < THRESH0) {
+ return;
+ }
+ break;
+ case kMaskRuleVMID:
+ if (location->v() < THRESH8) {
+ return;
+ }
+ break;
+ case kMaskRuleVMAX:
+ if (location->v() < THRESH16) {
+ return;
+ }
+ break;
+ case kMaskRuleYMIN:
+ if (location->uv() < THRESH0 * 2) {
+ return;
+ }
+ break;
+ case kMaskRuleYMID:
+ if (location->uv() < THRESH8 * 2) {
+ return;
+ }
+ break;
+ case kMaskRuleYMAX:
+ if (location->uv() < THRESH16 * 2) {
+ return;
+ }
+ break;
+ case kMaskRuleUVMAX:
+ if (location->u() < THRESH16 && location->v() < THRESH16) {
+ return;
+ }
+ break;
+ case kMaskRuleUVMIN:
+ if (location->u() < THRESH0 || location->v() < THRESH0) {
+ return;
+ }
+ break;
+ case kMaskRuleUorV:
+ if (location->u() < THRESH8 && location->v() < THRESH8) {
+ return;
+ }
+ break;
+ case kMaskRuleUandV:
+ if (location->u() < THRESH8 || location->v() < THRESH8) {
+ return;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ readPointer = tilePointer;
+ lowBound = MIN((int)(drawPoint.y + height), (int)_tileClip.bottom);
+ for (row = drawPoint.y; row < lowBound; row++) {
+ widthCount = 0;
+ if (row >= _tileClip.top) {
+ drawPointer = (byte *)ds->pixels + drawPoint.x + (row * ds->pitch);
+ col = drawPoint.x;
+ for (;;) {
+ bgRunCount = *readPointer++;
+ widthCount += bgRunCount;
+ if (widthCount >= SAGA_ISOTILE_WIDTH) {
+ break;
+ }
+
+ drawPointer += bgRunCount;
+ col += bgRunCount;
+ fgRunCount = *readPointer++;
+ widthCount += fgRunCount;
+
+ count = 0;
+ while ((col < _tileClip.left) && (count < fgRunCount)) {
+ count++;
+ col++;
+ }
+ while ((col < _tileClip.right) && (count < fgRunCount)) {
+ assert((byte *)ds->pixels <= (byte *)(drawPointer + count));
+ assert((byte *)((byte *)ds->pixels + (_vm->getDisplayWidth() *
+ _vm->getDisplayHeight())) > (byte *)(drawPointer + count));
+ drawPointer[count] = readPointer[count];
+ count++;
+ col++;
+ }
+ readPointer += fgRunCount;
+ drawPointer += fgRunCount;
+ }
+ } else {
+ for (;;) {
+ bgRunCount = *readPointer++;
+ widthCount += bgRunCount;
+ if (widthCount >= SAGA_ISOTILE_WIDTH) {
+ break;
+ }
+
+ fgRunCount = *readPointer++;
+ widthCount += fgRunCount;
+
+ readPointer += fgRunCount;
+ }
+ }
+ }
+
+}
+
+bool IsoMap::checkDragonPoint(int16 u, int16 v, uint16 direction) {
+ DragonPathCell *pathCell;
+
+ if ((u < 1) || (u >= SAGA_DRAGON_SEARCH_DIAMETER - 1) || (v < 1) || (v >= SAGA_DRAGON_SEARCH_DIAMETER - 1)) {
+ return false;
+ }
+
+ pathCell = _dragonSearchArray.getPathCell(u, v);
+
+ if (pathCell->visited) {
+ return false;
+ }
+
+ pathCell->visited = 1;
+ pathCell->direction = direction;
+ return true;
+}
+
+void IsoMap::pushDragonPoint(int16 u, int16 v, uint16 direction) {
+ DragonTilePoint *tilePoint;
+ DragonPathCell *pathCell;
+
+ if ((u < 1) || (u >= SAGA_DRAGON_SEARCH_DIAMETER - 1) || (v < 1) || (v >= SAGA_DRAGON_SEARCH_DIAMETER - 1)) {
+ return;
+ }
+
+ pathCell = _dragonSearchArray.getPathCell(u, v);
+
+ if (pathCell->visited) {
+ return;
+ }
+
+ tilePoint = _dragonSearchArray.getQueue(_queueCount);
+ _queueCount++;
+ if (_queueCount >= SAGA_SEARCH_QUEUE_SIZE) {
+ _queueCount = 0;
+ }
+
+ tilePoint->u = u;
+ tilePoint->v = v;
+ tilePoint->direction = direction;
+
+ pathCell->visited = 1;
+ pathCell->direction = direction;
+}
+
+void IsoMap::pushPoint(int16 u, int16 v, uint16 cost, uint16 direction) {
+ int16 upper;
+ int16 lower;
+ int16 mid;
+ TilePoint *tilePoint;
+ PathCell *pathCell;
+
+ upper = _queueCount;
+ lower = 0;
+
+ if ((u < 1) || (u >= SAGA_SEARCH_DIAMETER - 1) || (v < 1) || (v >= SAGA_SEARCH_DIAMETER - 1)) {
+ return;
+ }
+
+ pathCell = _searchArray.getPathCell(u, v);
+
+ if ((pathCell->visited) && (pathCell->cost <= cost)) {
+ return;
+ }
+
+ if (_queueCount >= SAGA_SEARCH_QUEUE_SIZE) {
+ return;
+ }
+
+ while (1) {
+ mid = (upper + lower) / 2;
+ tilePoint = _searchArray.getQueue(mid);
+
+ if (upper <= lower) {
+ break;
+ }
+
+ if (cost < tilePoint->cost) {
+ lower = mid + 1;
+ } else {
+ upper = mid;
+ }
+ }
+
+ if (mid < _queueCount ) {
+ memmove(tilePoint + 1, tilePoint, (_queueCount - mid) * sizeof (*tilePoint));
+ }
+ _queueCount++;
+
+ tilePoint->u = u;
+ tilePoint->v = v;
+ tilePoint->cost = cost;
+ tilePoint->direction = direction;
+
+ pathCell->visited = 1;
+ pathCell->direction = direction;
+ pathCell->cost = cost;
+}
+
+int16 IsoMap::getTileIndex(int16 u, int16 v, int16 z) {
+ int16 mtileU;
+ int16 mtileV;
+ int16 uc;
+ int16 vc;
+ int16 u0;
+ int16 v0;
+ int16 platformIndex;
+ int16 metaTileIndex;
+
+ mtileU = u >> 3;
+ mtileV = v >> 3;
+ uc = mtileU & (SAGA_TILEMAP_W - 1);
+ vc = mtileV & (SAGA_TILEMAP_W - 1);
+ u0 = u & (SAGA_PLATFORM_W - 1);
+ v0 = v & (SAGA_PLATFORM_W - 1);
+
+ if ((uc != mtileU) || (vc != mtileV)) {
+ metaTileIndex = 0;
+ switch ( _tileMap.edgeType) {
+ case kEdgeTypeBlack:
+ return 0;
+ case kEdgeTypeFill0:
+ break;
+ case kEdgeTypeFill1:
+ metaTileIndex = 1;
+ break;
+ case kEdgeTypeRpt:
+ uc = clamp( 0, mtileU, SAGA_TILEMAP_W - 1);
+ vc = clamp( 0, mtileV, SAGA_TILEMAP_W - 1);
+ metaTileIndex = _tileMap.tilePlatforms[uc][vc];
+ break;
+ case kEdgeTypeWrap:
+ metaTileIndex = _tileMap.tilePlatforms[uc][vc];
+ break;
+ }
+ } else {
+ metaTileIndex = _tileMap.tilePlatforms[uc][vc];
+ }
+
+ if (_metaTilesCount <= metaTileIndex) {
+ error("IsoMap::getTile wrong metaTileIndex");
+ }
+
+ platformIndex = _metaTileList[metaTileIndex].stack[z];
+ if (platformIndex < 0) {
+ return 0;
+ }
+
+ if (_tilePlatformsCount <= platformIndex) {
+ error("IsoMap::getTile wrong platformIndex");
+ }
+
+ return _tilePlatformList[platformIndex].tiles[u0][v0];
+}
+
+IsoTileData *IsoMap::getTile(int16 u, int16 v, int16 z) {
+ int16 tileIndex;
+
+ tileIndex = getTileIndex(u, v, z);
+
+ if (tileIndex == 0) {
+ return NULL;
+ }
+
+ if (tileIndex & SAGA_MULTI_TILE) {
+ tileIndex = findMulti(tileIndex, u, v, z);
+ }
+
+ return &_tilesTable[tileIndex];
+}
+
+void IsoMap::testPossibleDirections(int16 u, int16 v, uint16 terraComp[8], int skipCenter) {
+ IsoTileData *tile;
+ uint16 fgdMask;
+ uint16 bgdMask;
+ uint16 mask;
+
+
+ memset(terraComp, 0, 8 * sizeof(uint16));
+
+#define FILL_MASK(index, testMask) \
+ if ( mask & testMask) { \
+ terraComp[index] |= fgdMask; \
+ } \
+ if (~mask & testMask) { \
+ terraComp[index] |= bgdMask; \
+ }
+
+#define TEST_TILE_PROLOG(offsetU, offsetV) \
+ tile = getTile(u + offsetU, v + offsetV , _platformHeight); \
+ if (tile != NULL) { \
+ fgdMask = tile->GetFGDMask(); \
+ bgdMask = tile->GetBGDMask(); \
+ mask = tile->terrainMask;
+
+#define TEST_TILE_EPILOG(index) \
+ } else { \
+ if (_vm->_actor->_protagonist->_location.z > 0) { \
+ terraComp[index] = SAGA_IMPASSABLE; \
+ } \
+ }
+
+#define TEST_TILE_END }
+
+ TEST_TILE_PROLOG(0, 0)
+ if (skipCenter) {
+ if ((mask & 0x0660) && (fgdMask & SAGA_IMPASSABLE)) {
+ fgdMask = 0;
+ }
+ if ((~mask & 0x0660) && (bgdMask & SAGA_IMPASSABLE)) {
+ bgdMask = 0;
+ }
+ }
+
+ FILL_MASK(0, 0xcc00)
+ FILL_MASK(1, 0x6600)
+ FILL_MASK(2, 0x3300)
+ FILL_MASK(3, 0x0330)
+ FILL_MASK(4, 0x0033)
+ FILL_MASK(5, 0x0066)
+ FILL_MASK(6, 0x00cc)
+ FILL_MASK(7, 0x0cc0)
+ TEST_TILE_END
+
+ TEST_TILE_PROLOG(1, 1)
+ FILL_MASK(0, 0x0673)
+ TEST_TILE_EPILOG(0)
+
+
+ TEST_TILE_PROLOG(1, 0)
+ FILL_MASK(0, 0x0008)
+ FILL_MASK(1, 0x0666)
+ FILL_MASK(2, 0x0001)
+ TEST_TILE_EPILOG(1)
+
+
+ TEST_TILE_PROLOG(1, -1)
+ FILL_MASK(2, 0x06ec)
+ TEST_TILE_EPILOG(2)
+
+ TEST_TILE_PROLOG(0, 1)
+ FILL_MASK(0, 0x1000)
+ FILL_MASK(7, 0x0770)
+ FILL_MASK(6, 0x0001)
+ TEST_TILE_EPILOG(7)
+
+
+ TEST_TILE_PROLOG(0, -1)
+ FILL_MASK(2, 0x8000)
+ FILL_MASK(3, 0x0ee0)
+ FILL_MASK(4, 0x0008)
+ TEST_TILE_EPILOG(3)
+
+
+ TEST_TILE_PROLOG(-1, 1)
+ FILL_MASK(6, 0x3670)
+ TEST_TILE_EPILOG(6)
+
+
+ TEST_TILE_PROLOG(-1, 0)
+ FILL_MASK(6, 0x8000)
+ FILL_MASK(5, 0x6660)
+ FILL_MASK(4, 0x1000)
+ TEST_TILE_EPILOG(5)
+
+ TEST_TILE_PROLOG(-1, -1)
+ FILL_MASK(4, 0xce60)
+ TEST_TILE_EPILOG(4)
+}
+
+void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 distance, uint16 direction) {
+ int16 bestDistance;
+ int16 bestU;
+ int16 bestV;
+ int16 uBase;
+ int16 vBase;
+ int16 u;
+ int16 v;
+ int i;
+ ActorData *actor;
+ TilePoint tilePoint;
+ uint16 dir;
+ int16 dist;
+ uint16 terraComp[8];
+ const TilePoint *tdir;
+ uint16 terrainMask;
+
+ bestDistance = 0;
+
+
+ uBase = (start.u() >> 4) - SAGA_SEARCH_CENTER;
+ vBase = (start.v() >> 4) - SAGA_SEARCH_CENTER;
+
+ bestU = SAGA_SEARCH_CENTER;
+ bestV = SAGA_SEARCH_CENTER;
+
+ _platformHeight = _vm->_actor->_protagonist->_location.z / 8;
+
+ memset( &_searchArray, 0, sizeof(_searchArray));
+
+ for (i = 0; i < _vm->_actor->_actorsCount; i++) {
+ actor = _vm->_actor->_actors[i];
+ if (!actor->_inScene) continue;
+
+ u = (actor->_location.u() >> 4) - uBase;
+ v = (actor->_location.v() >> 4) - vBase;
+ if ((u >= 0) && (u < SAGA_SEARCH_DIAMETER) &&
+ (v >= 0) && (v < SAGA_SEARCH_DIAMETER) &&
+ ((u != SAGA_SEARCH_CENTER) || (v != SAGA_SEARCH_CENTER))) {
+ _searchArray.getPathCell(u, v)->visited = 1;
+ }
+ }
+
+ _queueCount = 0;
+ pushPoint(SAGA_SEARCH_CENTER, SAGA_SEARCH_CENTER, 0, 0);
+
+ while (_queueCount > 0) {
+
+ _queueCount--;
+ tilePoint = *_searchArray.getQueue(_queueCount);
+
+
+ dist = ABS(tilePoint.u - SAGA_SEARCH_CENTER) + ABS(tilePoint.v - SAGA_SEARCH_CENTER);
+
+ if (dist > bestDistance) {
+ bestU = tilePoint.u;
+ bestV = tilePoint.v;
+ bestDistance = dist;
+
+ if (dist >= distance) {
+ break;
+ }
+ }
+
+ testPossibleDirections(uBase + tilePoint.u, vBase + tilePoint.v, terraComp, 0);
+
+
+ for (dir = 0; dir < 8; dir++) {
+ terrainMask = terraComp[dir];
+
+ if (terrainMask & SAGA_IMPASSABLE ) {
+ continue;
+ }
+
+ if (dir == direction) {
+ tdir = &easyDirTable[ dir ];
+ } else {
+ if (dir + 1 == direction || dir - 1 == direction) {
+ tdir = &normalDirTable[ dir ];
+ } else {
+ tdir = &hardDirTable[ dir ];
+ }
+ }
+
+ pushPoint(tilePoint.u + tdir->u,tilePoint.v + tdir->v, tilePoint.cost + tdir->cost, dir);
+ }
+ }
+
+ result.u() = ((uBase + bestU) << 4) + 8;
+ result.v() = ((vBase + bestV) << 4) + 8;
+}
+
+bool IsoMap::findNearestChasm(int16 &u0, int16 &v0, uint16 &direction) {
+ int16 u, v;
+ uint16 i;
+ u = u0;
+ v = v0;
+
+ for (i = 1; i < 5; i++) {
+ if (getTile( u - i, v, 6) == NULL) {
+ u0 = u - i - 1;
+ v0 = v;
+ direction = kDirDownLeft;
+ return true;
+ }
+
+ if (getTile( u, v - i, 6) == NULL) {
+ u0 = u;
+ v0 = v - i - 1;
+ direction = kDirDownRight;
+ return true;
+ }
+
+ if (getTile( u - i, v - i, 6) == NULL) {
+ u0 = u - i - 1;
+ v0 = v - i - 1;
+ direction = kDirDown;
+ return true;
+ }
+
+ if (getTile( u + i, v - i, 6) == NULL) {
+ u0 = u + i + 1;
+ v0 = v - i - 1;
+ direction = kDirDownRight;
+ return true;
+ }
+
+ if (getTile( u - i, v + i, 6) == NULL) {
+ u0 = u + i + 1;
+ v0 = v - i - 1;
+ direction = kDirLeft;
+ return true;
+ }
+ }
+
+ for (i = 1; i < 5; i++) {
+ if (getTile( u + i, v, 6) == NULL) {
+ u0 = u + i + 1;
+ v0 = v;
+ direction = kDirUpRight;
+ return true;
+ }
+
+ if (getTile( u, v + i, 6) == NULL) {
+ u0 = u;
+ v0 = v + i + 1;
+ direction = kDirUpLeft;
+ return true;
+ }
+
+ if (getTile( u + i, v + i, 6) == NULL) {
+ u0 = u + i + 1;
+ v0 = v + i + 1;
+ direction = kDirUp;
+ return true;
+ }
+ }
+ return false;
+}
+
+void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Location &end, uint16 initialDirection) {
+ byte *res;
+ int i;
+ int16 u;
+ int16 v;
+ int16 u1;
+ int16 v1;
+ uint16 dir;
+
+ int16 bestDistance;
+ int16 bestU;
+ int16 bestV;
+
+ int16 uBase;
+ int16 vBase;
+ int16 uFinish;
+ int16 vFinish;
+ DragonPathCell *pcell;
+ IsoTileData *tile;
+ uint16 mask;
+ DragonTilePoint *tilePoint;
+
+ int16 dist;
+ bool first;
+
+ bestDistance = SAGA_DRAGON_SEARCH_DIAMETER;
+ bestU = SAGA_DRAGON_SEARCH_CENTER,
+ bestV = SAGA_DRAGON_SEARCH_CENTER;
+
+ uBase = (start.u() >> 4) - SAGA_DRAGON_SEARCH_CENTER;
+ vBase = (start.v() >> 4) - SAGA_DRAGON_SEARCH_CENTER;
+ uFinish = (end.u() >> 4) - uBase;
+ vFinish = (end.v() >> 4) - vBase;
+
+ _platformHeight = _vm->_actor->_protagonist->_location.z / 8;
+
+ memset( &_dragonSearchArray, 0, sizeof(_dragonSearchArray));
+
+ for (u = 0; u < SAGA_DRAGON_SEARCH_DIAMETER; u++) {
+ for (v = 0; v < SAGA_DRAGON_SEARCH_DIAMETER; v++) {
+
+ pcell = _dragonSearchArray.getPathCell(u, v);
+
+ u1 = uBase + u;
+ v1 = vBase + v;
+
+ if ((u1 > 127) || (u1 < 48) || (v1 > 127) || (v1 < 0)) {
+ pcell->visited = 1;
+ continue;
+ }
+
+ tile = getTile(u1, v1, _platformHeight );
+ if (tile != NULL) {
+ mask = tile->terrainMask;
+ if ( ((mask != 0) && (tile->GetFGDAttr() >= kTerrBlock)) ||
+ ((mask != 0xFFFF) && (tile->GetBGDAttr() >= kTerrBlock)) ) {
+ pcell->visited = 1;
+ }
+ } else {
+ pcell->visited = 1;
+ }
+ }
+ }
+
+ first = true;
+ _queueCount = _readCount = 0;
+ pushDragonPoint( SAGA_DRAGON_SEARCH_CENTER, SAGA_DRAGON_SEARCH_CENTER, initialDirection);
+
+ while (_queueCount != _readCount) {
+
+ tilePoint = _dragonSearchArray.getQueue(_readCount++);
+ if (_readCount >= SAGA_SEARCH_QUEUE_SIZE) {
+ _readCount = 0;
+ }
+
+
+ dist = ABS(tilePoint->u - uFinish) + ABS(tilePoint->v - vFinish);
+
+ if (dist < bestDistance) {
+
+ bestU = tilePoint->u;
+ bestV = tilePoint->v;
+ bestDistance = dist;
+ if (dist == 0) {
+ break;
+ }
+ }
+
+ switch (tilePoint->direction) {
+ case kDirUpRight:
+ if (checkDragonPoint( tilePoint->u + 1, tilePoint->v + 0, kDirUpRight)) {
+ pushDragonPoint( tilePoint->u + 2, tilePoint->v + 0, kDirUpRight);
+ pushDragonPoint( tilePoint->u + 1, tilePoint->v + 1, kDirUpLeft);
+ pushDragonPoint( tilePoint->u + 1, tilePoint->v - 1, kDirDownRight);
+ }
+ break;
+ case kDirDownRight:
+ if (checkDragonPoint( tilePoint->u + 0, tilePoint->v - 1, kDirDownRight)) {
+ pushDragonPoint( tilePoint->u + 0, tilePoint->v - 2, kDirDownRight);
+ pushDragonPoint( tilePoint->u + 1, tilePoint->v - 1, kDirUpRight);
+ pushDragonPoint( tilePoint->u - 1, tilePoint->v - 1, kDirDownLeft);
+ }
+ break;
+ case kDirDownLeft:
+ if (checkDragonPoint( tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft)) {
+ pushDragonPoint( tilePoint->u - 2, tilePoint->v + 0, kDirDownLeft);
+ pushDragonPoint( tilePoint->u - 1, tilePoint->v - 1, kDirDownRight);
+ pushDragonPoint( tilePoint->u - 1, tilePoint->v + 1, kDirUpLeft);
+ }
+ break;
+ case kDirUpLeft:
+ if (checkDragonPoint( tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft)) {
+ pushDragonPoint( tilePoint->u + 0, tilePoint->v + 2, kDirUpLeft);
+ pushDragonPoint( tilePoint->u - 1, tilePoint->v + 1, kDirDownLeft);
+ pushDragonPoint( tilePoint->u + 1, tilePoint->v + 1, kDirUpRight);
+ }
+ break;
+ }
+
+ if (first && (_queueCount == _readCount)) {
+ pushDragonPoint( tilePoint->u + 1, tilePoint->v + 0, kDirUpRight);
+ pushDragonPoint( tilePoint->u + 0, tilePoint->v - 1, kDirDownRight);
+ pushDragonPoint( tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft);
+ pushDragonPoint( tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft);
+ }
+ first = false;
+ }
+
+ res = &_pathDirections[SAGA_MAX_PATH_DIRECTIONS];
+ i = 0;
+ while ((bestU != SAGA_DRAGON_SEARCH_CENTER) || (bestV != SAGA_DRAGON_SEARCH_CENTER)) {
+ pcell = _dragonSearchArray.getPathCell(bestU, bestV);
+
+ *--res = pcell->direction;
+ i++;
+ if (i >= SAGA_MAX_PATH_DIRECTIONS) {
+ break;
+ }
+
+ dir = (pcell->direction + 4) & 0x07;
+
+ bestU += normalDirTable[dir].u;
+ bestV += normalDirTable[dir].v;
+ }
+
+/* if (i > 64) {
+ i = 64;
+ }*/
+
+ actor->_walkStepsCount = i;
+ if (i) {
+ actor->setTileDirectionsSize(i, false);
+ memcpy(actor->_tileDirections, res, i );
+ }
+
+}
+
+void IsoMap::findTilePath(ActorData* actor, const Location &start, const Location &end) {
+ ActorData *other;
+ int i;
+ int16 u;
+ int16 v;
+ int16 bestDistance;
+ int16 bestU;
+ int16 bestV;
+
+ int16 uBase;
+ int16 vBase;
+ int16 uFinish;
+ int16 vFinish;
+
+ TilePoint tilePoint;
+ uint16 dir;
+ int16 dist;
+ uint16 terraComp[8];
+ const TilePoint *tdir;
+ uint16 terrainMask;
+ const PathCell *pcell;
+ byte *res;
+
+
+ bestDistance = SAGA_SEARCH_DIAMETER;
+ bestU = SAGA_SEARCH_CENTER,
+ bestV = SAGA_SEARCH_CENTER;
+
+ uBase = (start.u() >> 4) - SAGA_SEARCH_CENTER;
+ vBase = (start.v() >> 4) - SAGA_SEARCH_CENTER;
+ uFinish = (end.u() >> 4) - uBase;
+ vFinish = (end.v() >> 4) - vBase;
+
+ _platformHeight = _vm->_actor->_protagonist->_location.z / 8;
+
+
+
+ memset( &_searchArray, 0, sizeof(_searchArray));
+
+ if (!(actor->_actorFlags & kActorNoCollide) &&
+ (_vm->_scene->currentSceneResourceId() != RID_ITE_OVERMAP_SCENE)) {
+ for (i = 0; i < _vm->_actor->_actorsCount; i++) {
+ other = _vm->_actor->_actors[i];
+ if (!other->_inScene) continue;
+ if (other == actor) continue;
+
+ u = (other->_location.u() >> 4) - uBase;
+ v = (other->_location.v() >> 4) - vBase;
+ if ((u >= 1) && (u < SAGA_SEARCH_DIAMETER) &&
+ (v >= 1) && (v < SAGA_SEARCH_DIAMETER) &&
+ ((u != SAGA_SEARCH_CENTER) || (v != SAGA_SEARCH_CENTER))) {
+ _searchArray.getPathCell(u, v)->visited = 1;
+ }
+ }
+ }
+
+ _queueCount = 0;
+ pushPoint(SAGA_SEARCH_CENTER, SAGA_SEARCH_CENTER, 0, 0);
+
+
+ while (_queueCount > 0) {
+
+ _queueCount--;
+ tilePoint = *_searchArray.getQueue(_queueCount);
+
+ if (tilePoint.cost > 100 && actor == _vm->_actor->_protagonist) continue;
+
+ dist = ABS(tilePoint.u - uFinish) + ABS(tilePoint.v - vFinish);
+
+ if (dist < bestDistance) {
+ bestU = tilePoint.u;
+ bestV = tilePoint.v;
+ bestDistance = dist;
+
+ if (dist == 0) {
+ break;
+ }
+ }
+
+ testPossibleDirections(uBase + tilePoint.u, vBase + tilePoint.v, terraComp,
+ (tilePoint.u == SAGA_SEARCH_CENTER && tilePoint.v == SAGA_SEARCH_CENTER));
+
+ for (dir = 0; dir < 8; dir++) {
+ terrainMask = terraComp[dir];
+
+ if (terrainMask & SAGA_IMPASSABLE) {
+ continue;
+ } else {
+ if (terrainMask & (1 << kTerrRough)) {
+ tdir = &hardDirTable[ dir ];
+ } else {
+ if (terrainMask & (1 << kTerrNone)) {
+ tdir = &normalDirTable[ dir ];
+ } else {
+ tdir = &easyDirTable[ dir ];
+ }
+ }
+ }
+
+
+ pushPoint(tilePoint.u + tdir->u, tilePoint.v + tdir->v, tilePoint.cost + tdir->cost, dir);
+ }
+ }
+
+ res = &_pathDirections[SAGA_MAX_PATH_DIRECTIONS];
+ i = 0;
+ while ((bestU != SAGA_SEARCH_CENTER) || (bestV != SAGA_SEARCH_CENTER)) {
+ pcell = _searchArray.getPathCell(bestU, bestV);
+
+ *--res = pcell->direction;
+ i++;
+ if (i >= SAGA_MAX_PATH_DIRECTIONS) {
+ break;
+ }
+
+ dir = (pcell->direction + 4) & 0x07;
+
+ bestU += normalDirTable[dir].u;
+ bestV += normalDirTable[dir].v;
+ }
+
+/* if (i > 64) {
+ i = 64;
+ }*/
+ actor->_walkStepsCount = i;
+ if (i) {
+ actor->setTileDirectionsSize(i, false);
+ memcpy(actor->_tileDirections, res, i );
+ }
+}
+
+void IsoMap::setTileDoorState(int doorNumber, int doorState) {
+ MultiTileEntryData *multiTileEntryData;
+
+ if ((doorNumber < 0) || (doorNumber >= _multiCount)) {
+ error("setTileDoorState: doorNumber >= _multiCount");
+ }
+
+ multiTileEntryData = &_multiTable[doorNumber];
+ multiTileEntryData->currentState = doorState;
+}
+
+static const int16 directions[8][2] = {
+ { 16, 16},
+ { 16, 0},
+ { 16, -16},
+ { 0, -16},
+ { -16, -16},
+ { -16, 0},
+ { -16, 16},
+ { 0, 16}
+};
+
+
+
+bool IsoMap::nextTileTarget(ActorData* actor) {
+ uint16 dir;
+
+ if (actor->_walkStepIndex >= actor->_walkStepsCount) {
+ return false;
+ }
+
+
+ actor->_actionDirection = dir = actor->_tileDirections[actor->_walkStepIndex++];
+
+ actor->_partialTarget.u() =
+ (actor->_location.u() & ~0x0f) + 8 + directions[dir][0];
+
+ actor->_partialTarget.v() =
+ (actor->_location.v() & ~0x0f) + 8 + directions[dir][1];
+
+
+ if (dir == 0) {
+ actor->_facingDirection = kDirUp;
+ } else {
+ if (dir == 4) {
+ actor->_facingDirection = kDirDown;
+ } else {
+ if (dir < 4) {
+ actor->_facingDirection = kDirRight;
+ } else {
+ actor->_facingDirection = kDirLeft;
+ }
+ }
+ }
+
+ return true;
+}
+
+void IsoMap::screenPointToTileCoords(const Point &position, Location &location) {
+ Point mPos(position);
+ int x,y;
+
+ if (_vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE){
+ if (mPos.y < 16) {
+ mPos.y = 16;
+ }
+ }
+
+ x = mPos.x + _viewScroll.x - (128 * SAGA_TILEMAP_W) - 16;
+ y = mPos.y + _viewScroll.y - (128 * SAGA_TILEMAP_W) + _vm->_actor->_protagonist->_location.z;
+
+ location.u() = (x - y * 2) >> 1;
+ location.v() = - (x + y * 2) >> 1;
+ location.z = _vm->_actor->_protagonist->_location.z;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/isomap.h b/engines/saga/isomap.h
new file mode 100644
index 0000000000..9264c20fbe
--- /dev/null
+++ b/engines/saga/isomap.h
@@ -0,0 +1,294 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Isometric level module - private header
+
+#ifndef SAGA_ISOMAP_H_
+#define SAGA_ISOMAP_H_
+
+#include "saga/actor.h"
+
+namespace Saga {
+
+#define SAGA_ISOTILEDATA_LEN 8
+#define SAGA_ISOTILE_WIDTH 32
+#define SAGA_ISOTILE_BASEHEIGHT 15
+#define SAGA_TILE_NOMINAL_H 16
+#define SAGA_MAX_TILE_H 64
+
+#define SAGA_TILEPLATFORMDATA_LEN 136
+#define SAGA_PLATFORM_W 8
+#define SAGA_MAX_PLATFORM_H 16
+
+#define SAGA_TILEMAP_LEN 514
+#define SAGA_TILEMAP_W 16
+#define SAGA_TILEMAP_H 16
+
+#define SAGA_METATILEDATA_LEN 36
+
+#define SAGA_MULTI_TILE (1 << 15)
+
+#define SAGA_SCROLL_LIMIT_X1 32
+#define SAGA_SCROLL_LIMIT_X2 64
+#define SAGA_SCROLL_LIMIT_Y1 8
+#define SAGA_SCROLL_LIMIT_Y2 32
+
+#define SAGA_DRAGON_SEARCH_CENTER 24
+#define SAGA_DRAGON_SEARCH_DIAMETER (SAGA_DRAGON_SEARCH_CENTER * 2)
+
+#define SAGA_SEARCH_CENTER 15
+#define SAGA_SEARCH_DIAMETER (SAGA_SEARCH_CENTER * 2)
+#define SAGA_SEARCH_QUEUE_SIZE 128
+#define SAGA_IMPASSABLE ((1 << kTerrBlock) | (1 << kTerrWater))
+
+#define SAGA_STRAIGHT_NORMAL_COST 4
+#define SAGA_DIAG_NORMAL_COST 6
+
+#define SAGA_STRAIGHT_EASY_COST 2
+#define SAGA_DIAG_EASY_COST 3
+
+#define SAGA_STRAIGHT_HARD_COST 9
+#define SAGA_DIAG_HARD_COST 10
+#define SAGA_MAX_PATH_DIRECTIONS 256
+
+enum TerrainTypes {
+ kTerrNone = 0,
+ kTerrPath = 1,
+ kTerrRough = 2,
+ kTerrBlock = 3,
+ kTerrWater = 4,
+ kTerrLast = 5
+};
+
+enum TileMapEdgeType {
+ kEdgeTypeBlack = 0,
+ kEdgeTypeFill0 = 1,
+ kEdgeTypeFill1 = 2,
+ kEdgeTypeRpt = 3,
+ kEdgeTypeWrap = 4
+};
+
+struct IsoTileData {
+ byte height;
+ int8 attributes;
+ size_t offset;
+ uint16 terrainMask;
+ byte FGDBGDAttr;
+ int8 GetMaskRule() const {
+ return attributes & 0x0F;
+ }
+ byte GetFGDAttr() const {
+ return FGDBGDAttr >> 4;
+ }
+ byte GetBGDAttr() const {
+ return FGDBGDAttr & 0x0F;
+ }
+ uint16 GetFGDMask() const {
+ return 1 << GetFGDAttr();
+ }
+ uint16 GetBGDMask() const {
+ return 1 << GetBGDAttr();
+ }
+};
+
+struct TilePlatformData {
+ int16 metaTile;
+ int16 height;
+ int16 highestPixel;
+ byte vBits;
+ byte uBits;
+ int16 tiles[SAGA_PLATFORM_W][SAGA_PLATFORM_W];
+};
+
+struct TileMapData {
+ byte edgeType;
+ int16 tilePlatforms[SAGA_TILEMAP_W][SAGA_TILEMAP_H];
+};
+
+struct MetaTileData {
+ uint16 highestPlatform;
+ uint16 highestPixel;
+ int16 stack[SAGA_MAX_PLATFORM_H];
+};
+
+struct MultiTileEntryData {
+ int16 offset;
+ byte u;
+ byte v;
+ byte h;
+ byte uSize;
+ byte vSize;
+ byte numStates;
+ byte currentState;
+};
+
+
+
+
+
+class IsoMap {
+public:
+ IsoMap(SagaEngine *vm);
+ ~IsoMap() {
+ freeMem();
+ }
+ void loadImages(const byte * resourcePointer, size_t resourceLength);
+ void loadMap(const byte * resourcePointer, size_t resourceLength);
+ void loadPlatforms(const byte * resourcePointer, size_t resourceLength);
+ void loadMetaTiles(const byte * resourcePointer, size_t resourceLength);
+ void loadMulti(const byte * resourcePointer, size_t resourceLength);
+ void freeMem();
+ void draw(Surface *ds);
+ void drawSprite(Surface *ds, SpriteList &spriteList, int spriteNumber, const Location &location, const Point &screenPosition, int scale);
+ void adjustScroll(bool jump);
+ void tileCoordsToScreenPoint(const Location &location, Point &position) {
+ position.x = location.u() - location.v() + (128 * SAGA_TILEMAP_W) - _viewScroll.x + 16;
+ position.y = -(location.uv() >> 1) + (128 * SAGA_TILEMAP_W) - _viewScroll.y - location.z;
+ }
+ void screenPointToTileCoords(const Point &position, Location &location);
+ void placeOnTileMap(const Location &start, Location &result, int16 distance, uint16 direction);
+ void findDragonTilePath(ActorData* actor, const Location &start, const Location &end, uint16 initialDirection);
+ bool findNearestChasm(int16 &u0, int16 &v0, uint16 &direction);
+ void findTilePath(ActorData* actor, const Location &start, const Location &end);
+ bool nextTileTarget(ActorData* actor);
+ void setTileDoorState(int doorNumber, int doorState);
+ Point getMapPosition() { return _mapPosition; }
+ void setMapPosition(int x, int y);
+ int16 getTileIndex(int16 u, int16 v, int16 z);
+
+private:
+ void drawTiles(Surface *ds, const Location *location);
+ void drawMetaTile(Surface *ds, uint16 metaTileIndex, const Point &point, int16 absU, int16 absV);
+ void drawSpriteMetaTile(Surface *ds, uint16 metaTileIndex, const Point &point, Location &location, int16 absU, int16 absV);
+ void drawPlatform(Surface *ds, uint16 platformIndex, const Point &point, int16 absU, int16 absV, int16 absH);
+ void drawSpritePlatform(Surface *ds, uint16 platformIndex, const Point &point, const Location &location, int16 absU, int16 absV, int16 absH);
+ void drawTile(Surface *ds, uint16 tileIndex, const Point &point, const Location *location);
+ int16 smoothSlide(int16 value, int16 min, int16 max) {
+ if (value < min) {
+ if (value < min - 100 || value > min - 4) {
+ value = min;
+ } else {
+ value += 4;
+ }
+ } else {
+ if (value > max) {
+ if (value > max + 100 || value < max + 4) {
+ value = max;
+ } else {
+ value -= 4;
+ }
+ }
+ }
+ return value;
+ }
+ int16 findMulti(int16 tileIndex, int16 absU, int16 absV, int16 absH);
+ void pushPoint(int16 u, int16 v, uint16 cost, uint16 direction);
+ void pushDragonPoint(int16 u, int16 v, uint16 direction);
+ bool checkDragonPoint(int16 u, int16 v, uint16 direction);
+ void testPossibleDirections(int16 u, int16 v, uint16 terraComp[8], int skipCenter);
+ IsoTileData *getTile(int16 u, int16 v, int16 z);
+
+
+ byte *_tileData;
+ size_t _tileDataLength;
+ uint16 _tilesCount;
+ IsoTileData *_tilesTable;
+
+ uint16 _tilePlatformsCount;
+ TilePlatformData *_tilePlatformList;
+ uint16 _metaTilesCount;
+ MetaTileData *_metaTileList;
+
+ uint16 _multiCount;
+ MultiTileEntryData *_multiTable;
+ uint16 _multiDataCount;
+ int16 *_multiTableData;
+
+ TileMapData _tileMap;
+
+ Point _mapPosition;
+
+// path finding stuff
+ uint16 _platformHeight;
+
+ struct DragonPathCell {
+ uint8 visited:1,direction:3;
+ };
+ struct DragonTilePoint {
+ int8 u, v;
+ uint8 direction:4;
+ };
+ struct PathCell {
+ uint16 visited:1,direction:3,cost:12;
+ };
+
+public:
+ struct TilePoint {
+ int8 u, v;
+ uint16 direction:4,cost:12;
+ };
+
+private:
+ struct DragonSearchArray {
+ DragonPathCell cell[SAGA_DRAGON_SEARCH_DIAMETER][SAGA_DRAGON_SEARCH_DIAMETER];
+ DragonTilePoint queue[SAGA_SEARCH_QUEUE_SIZE];
+ DragonTilePoint *getQueue(uint16 i) {
+ assert(i < SAGA_SEARCH_QUEUE_SIZE);
+ return &queue[i];
+ }
+ DragonPathCell *getPathCell(uint16 u, uint16 v) {
+ assert((u < SAGA_DRAGON_SEARCH_DIAMETER) && (v < SAGA_DRAGON_SEARCH_DIAMETER));
+ return &cell[u][v];
+ }
+ };
+ struct SearchArray {
+ PathCell cell[SAGA_SEARCH_DIAMETER][SAGA_SEARCH_DIAMETER];
+ TilePoint queue[SAGA_SEARCH_QUEUE_SIZE];
+ TilePoint *getQueue(uint16 i) {
+ assert(i < SAGA_SEARCH_QUEUE_SIZE);
+ return &queue[i];
+ }
+ PathCell *getPathCell(uint16 u, uint16 v) {
+ assert((u < SAGA_SEARCH_DIAMETER) && (v < SAGA_SEARCH_DIAMETER));
+ return &cell[u][v];
+ }
+ };
+
+ int16 _queueCount;
+ int16 _readCount;
+ SearchArray _searchArray;
+ DragonSearchArray _dragonSearchArray;
+ byte _pathDirections[SAGA_MAX_PATH_DIRECTIONS];
+
+
+ int _viewDiff;
+ Point _viewScroll;
+ Rect _tileClip;
+
+ SagaEngine *_vm;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/ite_introproc.cpp b/engines/saga/ite_introproc.cpp
new file mode 100644
index 0000000000..abe2deb4a5
--- /dev/null
+++ b/engines/saga/ite_introproc.cpp
@@ -0,0 +1,1028 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+
+// Intro sequence scene procedures
+
+#include "saga/saga.h"
+#include "saga/gfx.h"
+
+#include "saga/animation.h"
+#include "saga/events.h"
+#include "saga/font.h"
+#include "saga/sndres.h"
+#include "saga/palanim.h"
+#include "saga/music.h"
+
+#include "saga/scene.h"
+#include "saga/resnames.h"
+#include "saga/rscfile.h"
+
+namespace Saga {
+
+using Common::UNK_LANG;
+using Common::EN_USA;
+using Common::DE_DEU;
+
+LoadSceneParams ITE_IntroList[] = {
+ {RID_ITE_INTRO_ANIM_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroAnimProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_1, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave1Proc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_2, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave2Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_3, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave3Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_CAVE_SCENE_4, kLoadByResourceId, NULL, Scene::SC_ITEIntroCave4Proc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_VALLEY_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroValleyProc, false, kTransitionFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_TREEHOUSE_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroTreeHouseProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_FAIREPATH_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroFairePathProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE},
+ {RID_ITE_FAIRETENT_SCENE, kLoadByResourceId, NULL, Scene::SC_ITEIntroFaireTentProc, false, kTransitionNoFade, 0, NO_CHAPTER_CHANGE}
+};
+
+int Scene::ITEStartProc() {
+ size_t scenesCount;
+ size_t i;
+
+ LoadSceneParams firstScene;
+ LoadSceneParams tempScene;
+
+ scenesCount = ARRAYSIZE(ITE_IntroList);
+
+ for (i = 0; i < scenesCount; i++) {
+ tempScene = ITE_IntroList[i];
+ tempScene.sceneDescriptor = _vm->_resource->convertResourceId(tempScene.sceneDescriptor);
+ _vm->_scene->queueScene(&tempScene);
+ }
+
+
+ firstScene.loadFlag = kLoadBySceneNumber;
+ firstScene.sceneDescriptor = _vm->getStartSceneNumber();
+ firstScene.sceneDescription = NULL;
+ firstScene.sceneSkipTarget = true;
+ firstScene.sceneProc = NULL;
+ firstScene.transitionType = kTransitionFade;
+ firstScene.actorsEntrance = 0;
+ firstScene.chapter = -1;
+
+ _vm->_scene->queueScene(&firstScene);
+
+ return SUCCESS;
+}
+
+Event *Scene::ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialogue dialogue[]) {
+ TextListEntry textEntry;
+ TextListEntry *entry;
+ Event event;
+ int voice_len;
+ int i;
+
+ // Queue narrator dialogue list
+ textEntry.knownColor = kKnownColorSubtitleTextColor;
+ textEntry.effectKnownColor = kKnownColorTransparent;
+ textEntry.useRect = true;
+ textEntry.rect.left = 0;
+ textEntry.rect.right = _vm->getDisplayWidth();
+ textEntry.rect.top = (_vm->getLanguage() == Common::DE_DEU) ? INTRO_DE_CAPTION_Y : INTRO_CAPTION_Y;
+ textEntry.rect.bottom = _vm->getDisplayHeight();
+ textEntry.font = kKnownFontMedium;
+ textEntry.flags = (FontEffectFlags)(kFontOutline | kFontCentered);
+
+ for (i = 0; i < n_dialogues; i++) {
+ textEntry.text = dialogue[i].i_str;
+ entry = _vm->_scene->_textList.addEntry(textEntry);
+
+ // Display text
+ event.type = kEvTOneshot;
+ event.code = kTextEvent;
+ event.op = kEventDisplay;
+ event.data = entry;
+ event.time = (i == 0) ? 0 : VOICE_PAD;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Play voice
+ event.type = kEvTOneshot;
+ event.code = kVoiceEvent;
+ event.op = kEventPlay;
+ event.param = dialogue[i].i_voice_rn;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ voice_len = _vm->_sndRes->getVoiceLength(dialogue[i].i_voice_rn);
+ if (voice_len < 0) {
+ voice_len = strlen(dialogue[i].i_str) * VOICE_LETTERLEN;
+ }
+
+ // Remove text
+ event.type = kEvTOneshot;
+ event.code = kTextEvent;
+ event.op = kEventRemove;
+ event.data = entry;
+ event.time = voice_len;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ }
+
+ return q_event;
+}
+
+enum {
+ kCHeader,
+ kCText
+};
+
+enum {
+ kITEPC = (1 << 0),
+ kITEPCCD = (1 << 1),
+ kITEMac = (1 << 2),
+ kITEWyrmKeep = (1 << 3),
+ kITEAny = 0xffff,
+ kITENotWyrmKeep = kITEAny & ~kITEWyrmKeep
+};
+
+// Queue a page of credits text. The original interpreter did word-wrapping
+// automatically. We currently don't.
+
+Event *Scene::ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]) {
+ int game;
+ Common::Language lang;
+
+ // The assumption here is that all WyrmKeep versions have the same
+ // credits, regardless of which operating system they're for.
+
+ lang = _vm->getLanguage();
+
+ if (_vm->getFeatures() & GF_WYRMKEEP) {
+ game = kITEWyrmKeep;
+ } else if (_vm->getPlatform() == Common::kPlatformMacintosh) {
+ game = kITEMac;
+ } else if (_vm->getGameId() == GID_ITE_CD_G) {
+ game = kITEPCCD;
+ } else {
+ game = kITEPC;
+ }
+
+ int line_spacing = 0;
+ int paragraph_spacing;
+ KnownFont font = kKnownFontSmall;
+ int i;
+
+ int n_paragraphs = 0;
+ int credits_height = 0;
+
+ for (i = 0; i < n_credits; i++) {
+ if (credits[i].lang != lang && credits[i].lang != UNK_LANG) {
+ continue;
+ }
+
+ if (!(credits[i].game & game)) {
+ continue;
+ }
+
+ switch (credits[i].type) {
+ case kCHeader:
+ font = kKnownFontSmall;
+ line_spacing = 4;
+ n_paragraphs++;
+ break;
+ case kCText:
+ font = kKnownFontMedium;
+ line_spacing = 2;
+ break;
+ default:
+ error("Unknown credit type");
+ }
+
+ credits_height += (_vm->_font->getHeight(font) + line_spacing);
+ }
+
+ paragraph_spacing = (200 - credits_height) / (n_paragraphs + 3);
+ credits_height += (n_paragraphs * paragraph_spacing);
+
+ int y = paragraph_spacing;
+
+ TextListEntry textEntry;
+ TextListEntry *entry;
+ Event event;
+ Event *q_event = NULL;
+
+ textEntry.knownColor = kKnownColorSubtitleTextColor;
+ textEntry.effectKnownColor = kKnownColorTransparent;
+ textEntry.flags = (FontEffectFlags)(kFontOutline | kFontCentered);
+ textEntry.point.x = 160;
+
+ for (i = 0; i < n_credits; i++) {
+ if (credits[i].lang != lang && credits[i].lang != UNK_LANG) {
+ continue;
+ }
+
+ if (!(credits[i].game & game)) {
+ continue;
+ }
+
+ switch (credits[i].type) {
+ case kCHeader:
+ font = kKnownFontSmall;
+ line_spacing = 4;
+ y += paragraph_spacing;
+ break;
+ case kCText:
+ font = kKnownFontMedium;
+ line_spacing = 2;
+ break;
+ default:
+ break;
+ }
+
+ textEntry.text = credits[i].string;
+ textEntry.font = font;
+ textEntry.point.y = y;
+
+ entry = _vm->_scene->_textList.addEntry(textEntry);
+
+ // Display text
+ event.type = kEvTOneshot;
+ event.code = kTextEvent;
+ event.op = kEventDisplay;
+ event.data = entry;
+ event.time = delta_time;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Remove text
+ event.type = kEvTOneshot;
+ event.code = kTextEvent;
+ event.op = kEventRemove;
+ event.data = entry;
+ event.time = duration;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ y += (_vm->_font->getHeight(font) + line_spacing);
+ }
+
+ return q_event;
+}
+
+int Scene::SC_ITEIntroAnimProc(int param, void *refCon) {
+ return ((Scene *)refCon)->ITEIntroAnimProc(param);
+}
+
+// Handles the introductory Dreamer's Guild / NWC logo animation scene.
+int Scene::ITEIntroAnimProc(int param) {
+ Event event;
+ Event *q_event;
+
+ switch (param) {
+ case SCENE_BEGIN:{
+ // Background for intro scene is the first frame of the
+ // intro animation; display it and set the palette
+ event.type = kEvTOneshot;
+ event.code = kBgEvent;
+ event.op = kEventDisplay;
+ event.param = kEvPSetPalette;
+ event.time = 0;
+
+ q_event = _vm->_events->queue(&event);
+
+ debug(3, "Intro animation procedure started.");
+ debug(3, "Linking animation resources...");
+
+ _vm->_anim->setFrameTime(0, ITE_INTRO_FRAMETIME);
+
+ // Link this scene's animation resources for continuous
+ // playback
+ int lastAnim;
+
+ if (_vm->getFeatures() & GF_WYRMKEEP) {
+ if (_vm->getPlatform() == Common::kPlatformMacintosh) {
+ lastAnim = 3;
+ } else {
+ lastAnim = 2;
+ }
+ } else {
+ if (_vm->getPlatform() == Common::kPlatformMacintosh) {
+ lastAnim = 4;
+ } else {
+ lastAnim = 5;
+ }
+ }
+
+ for (int i = 0; i < lastAnim; i++)
+ _vm->_anim->link(i, i+1);
+
+ _vm->_anim->setFlag(lastAnim, ANIM_FLAG_ENDSCENE);
+
+ debug(3, "Beginning animation playback.");
+
+ // Begin the animation
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventPlay;
+ event.param = 0;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Queue intro music playback
+ event.type = kEvTOneshot;
+ event.code = kMusicEvent;
+ event.param = MUSIC_1;
+ event.param2 = MUSIC_LOOP;
+ event.op = kEventPlay;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ }
+ break;
+ case SCENE_END:
+ break;
+ default:
+ warning("Illegal scene procedure parameter");
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_ITEIntroCave1Proc(int param, void *refCon) {
+ return ((Scene *)refCon)->ITEIntroCave1Proc(param);
+}
+
+// Handles first introductory cave painting scene
+int Scene::ITEIntroCave1Proc(int param) {
+ Event event;
+ Event *q_event;
+ int lang = (_vm->getLanguage() == Common::DE_DEU) ? 1 : 0;
+
+ static const IntroDialogue dialogue[][4] = {
+ { { // English
+ RID_CAVE_VOICE_0,
+ "We see the sky, we see the land, we see the water, "
+ "and we wonder: Are we the only ones?"
+ },
+ {
+ RID_CAVE_VOICE_1,
+ "Long before we came to exist, the humans ruled the "
+ "Earth."
+ },
+ {
+ RID_CAVE_VOICE_2,
+ "They made marvelous things, and moved whole "
+ "mountains."
+ },
+ {
+ RID_CAVE_VOICE_3,
+ "They knew the Secret of Flight, the Secret of "
+ "Happiness, and other secrets beyond our imagining."
+ } },
+ { { // German
+ RID_CAVE_VOICE_0,
+ "Um uns sind der Himmel, das Land und die Seen; und "
+ "wir fragen uns - sind wir die einzigen?"
+ },
+ {
+ RID_CAVE_VOICE_1,
+ "Lange vor unserer Zeit herrschten die Menschen "
+ "\201ber die Erde."
+ },
+ {
+ RID_CAVE_VOICE_2,
+ "Sie taten wundersame Dinge und versetzten ganze "
+ "Berge."
+ },
+ {
+ RID_CAVE_VOICE_3,
+ "Sie kannten das Geheimnis des Fluges, das Geheimnis "
+ "der Fr\224hlichkeit und andere Geheimnisse, die "
+ "unsere Vorstellungskraft \201bersteigen."
+ } }
+ };
+
+ int n_dialogues = ARRAYSIZE(dialogue[lang]);
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Begin palette cycling animation for candles
+ event.type = kEvTOneshot;
+ event.code = kPalAnimEvent;
+ event.op = kEventCycleStart;
+ event.time = 0;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Queue narrator dialogue list
+ q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+
+ // End scene after last dialogue over
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = VOICE_PAD;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ case SCENE_END:
+ break;
+
+ default:
+ warning("Illegal scene procedure paramater");
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_ITEIntroCave2Proc(int param, void *refCon) {
+ return ((Scene *)refCon)->ITEIntroCave2Proc(param);
+}
+
+// Handles second introductory cave painting scene
+int Scene::ITEIntroCave2Proc(int param) {
+ Event event;
+ Event *q_event;
+ int lang = (_vm->getLanguage() == Common::DE_DEU) ? 1 : 0;
+
+ static const IntroDialogue dialogue[][3] = {
+ { { // English
+ RID_CAVE_VOICE_4,
+ "The humans also knew the Secret of Life, and they "
+ "used it to give us the Four Great Gifts:"
+ },
+ {
+ RID_CAVE_VOICE_5,
+ "Thinking minds, feeling hearts, speaking mouths, and "
+ "reaching hands."
+ },
+ {
+ RID_CAVE_VOICE_6,
+ "We are their children."
+ } },
+ { { // German
+ RID_CAVE_VOICE_4,
+ "Au$erdem kannten die Menschen das Geheimnis des "
+ "Lebens. Und sie nutzten es, um uns die vier gro$en "
+ "Geschenke zu geben -"
+ },
+ {
+ RID_CAVE_VOICE_5,
+ "den denkenden Geist, das f\201hlende Herz, den "
+ "sprechenden Mund und die greifende Hand."
+ },
+ {
+ RID_CAVE_VOICE_6,
+ "Wir sind ihre Kinder."
+ } }
+ };
+
+ int n_dialogues = ARRAYSIZE(dialogue[lang]);
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Start 'dissolve' transition to new scene background
+ event.type = kEvTContinuous;
+ event.code = kTransitionEvent;
+ event.op = kEventDissolve;
+ event.time = 0;
+ event.duration = DISSOLVE_DURATION;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Begin palette cycling animation for candles
+ event.type = kEvTOneshot;
+ event.code = kPalAnimEvent;
+ event.op = kEventCycleStart;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Queue narrator dialogue list
+ q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+
+ // End scene after last dialogue over
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = VOICE_PAD;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ case SCENE_END:
+ break;
+ default:
+ warning("Illegal scene procedure paramater");
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_ITEIntroCave3Proc(int param, void *refCon) {
+ return ((Scene *)refCon)->ITEIntroCave3Proc(param);
+}
+
+// Handles third introductory cave painting scene
+int Scene::ITEIntroCave3Proc(int param) {
+ Event event;
+ Event *q_event;
+ int lang = (_vm->getLanguage() == Common::DE_DEU) ? 1 : 0;
+
+ static const IntroDialogue dialogue[][3] = {
+ { { // English
+ RID_CAVE_VOICE_7,
+ "They taught us how to use our hands, and how to "
+ "speak."
+ },
+ {
+ RID_CAVE_VOICE_8,
+ "They showed us the joy of using our minds."
+ },
+ {
+ RID_CAVE_VOICE_9,
+ "They loved us, and when we were ready, they surely "
+ "would have given us the Secret of Happiness."
+ } },
+ { { // German
+ RID_CAVE_VOICE_7,
+ "Sie lehrten uns zu sprechen und unsere H\204nde zu "
+ "benutzen."
+ },
+ {
+ RID_CAVE_VOICE_8,
+ "Sie zeigten uns die Freude am Denken."
+ },
+ {
+ RID_CAVE_VOICE_9,
+ "Sie liebten uns, und w\204ren wir bereit gewesen, "
+ "h\204tten sie uns sicherlich das Geheimnis der "
+ "Fr\224hlichkeit offenbart."
+ } }
+ };
+
+ int n_dialogues = ARRAYSIZE(dialogue[lang]);
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Start 'dissolve' transition to new scene background
+ event.type = kEvTContinuous;
+ event.code = kTransitionEvent;
+ event.op = kEventDissolve;
+ event.time = 0;
+ event.duration = DISSOLVE_DURATION;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Begin palette cycling animation for candles
+ event.type = kEvTOneshot;
+ event.code = kPalAnimEvent;
+ event.op = kEventCycleStart;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Queue narrator dialogue list
+ q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+
+ // End scene after last dialogue over
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = VOICE_PAD;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ case SCENE_END:
+ break;
+ default:
+ warning("Illegal scene procedure paramater");
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_ITEIntroCave4Proc(int param, void *refCon) {
+ return ((Scene *)refCon)->ITEIntroCave4Proc(param);
+}
+
+// Handles fourth introductory cave painting scene
+int Scene::ITEIntroCave4Proc(int param) {
+ Event event;
+ Event *q_event;
+ int lang = (_vm->getLanguage() == Common::DE_DEU) ? 1 : 0;
+
+ static const IntroDialogue dialogue[][4] = {
+ { { // English
+ RID_CAVE_VOICE_10,
+ "And now we see the sky, the land, and the water that "
+ "we are heirs to, and we wonder: why did they leave?"
+ },
+ {
+ RID_CAVE_VOICE_11,
+ "Do they live still, in the stars? In the oceans "
+ "depths? In the wind?"
+ },
+ {
+ RID_CAVE_VOICE_12,
+ "We wonder, was their fate good or evil?"
+ },
+ {
+ RID_CAVE_VOICE_13,
+ "And will we also share the same fate one day?"
+ } },
+ { { // German
+ RID_CAVE_VOICE_10,
+ "Und nun sehen wir den Himmel, das Land und die "
+ "Seen - unser Erbe. Und wir fragen uns - warum "
+ "verschwanden sie?"
+ },
+ {
+ RID_CAVE_VOICE_11,
+ "Leben sie noch in den Sternen? In den Tiefen des "
+ "Ozeans? Im Wind?"
+ },
+ {
+ RID_CAVE_VOICE_12,
+ "Wir fragen uns - war ihr Schicksal gut oder b\224se?"
+ },
+ {
+ RID_CAVE_VOICE_13,
+ "Und wird uns eines Tages das gleiche Schicksal "
+ "ereilen?"
+ } }
+ };
+
+ int n_dialogues = ARRAYSIZE(dialogue[lang]);
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Start 'dissolve' transition to new scene background
+ event.type = kEvTContinuous;
+ event.code = kTransitionEvent;
+ event.op = kEventDissolve;
+ event.time = 0;
+ event.duration = DISSOLVE_DURATION;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Begin palette cycling animation for candles
+ event.type = kEvTOneshot;
+ event.code = kPalAnimEvent;
+ event.op = kEventCycleStart;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Queue narrator dialogue list
+ q_event = ITEQueueDialogue(q_event, n_dialogues, dialogue[lang]);
+
+ // End scene after last dialogue over
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = VOICE_PAD;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ case SCENE_END:
+ break;
+ default:
+ warning("Illegal scene procedure paramater");
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_ITEIntroValleyProc(int param, void *refCon) {
+ return ((Scene *)refCon)->ITEIntroValleyProc(param);
+}
+
+// Handles intro title scene (valley overlook)
+int Scene::ITEIntroValleyProc(int param) {
+ Event event;
+ Event *q_event;
+
+ static const IntroCredit credits[] = {
+ {EN_USA, kITEAny, kCHeader, "Producer"},
+ {DE_DEU, kITEAny, kCHeader, "Produzent"},
+ {UNK_LANG, kITEAny, kCText, "Walter Hochbrueckner"},
+ {EN_USA, kITEAny, kCHeader, "Executive Producer"},
+ {DE_DEU, kITEAny, kCHeader, "Ausf\201hrender Produzent"},
+ {UNK_LANG, kITEAny, kCText, "Robert McNally"},
+ {UNK_LANG, kITEWyrmKeep, kCHeader, "2nd Executive Producer"},
+ {EN_USA, kITENotWyrmKeep, kCHeader, "Publisher"},
+ {DE_DEU, kITENotWyrmKeep, kCHeader, "Herausgeber"},
+ {UNK_LANG, kITEAny, kCText, "Jon Van Caneghem"}
+ };
+
+ int n_credits = ARRAYSIZE(credits);
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Begin title screen background animation
+ _vm->_anim->setCycles(0, -1);
+
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventPlay;
+ event.param = 0;
+ event.time = 0;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Begin ITE title theme music
+ _vm->_music->stop();
+
+ event.type = kEvTOneshot;
+ event.code = kMusicEvent;
+ event.param = MUSIC_2;
+ event.param2 = MUSIC_NORMAL;
+ event.op = kEventPlay;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Pause animation before logo
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventStop;
+ event.param = 0;
+ event.time = 3000;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Display logo
+ event.type = kEvTContinuous;
+ event.code = kTransitionEvent;
+ event.op = kEventDissolveBGMask;
+ event.time = 0;
+ event.duration = LOGO_DISSOLVE_DURATION;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Remove logo
+ event.type = kEvTContinuous;
+ event.code = kTransitionEvent;
+ event.op = kEventDissolve;
+ event.time = 3000;
+ event.duration = LOGO_DISSOLVE_DURATION;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Unpause animation before logo
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventPlay;
+ event.time = 0;
+ event.param = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Queue game credits list
+ q_event = ITEQueueCredits(9000, CREDIT_DURATION1, n_credits, credits);
+
+ // End scene after credit display
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = 1000;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ case SCENE_END:
+ break;
+ default:
+ warning("Illegal scene procedure parameter");
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_ITEIntroTreeHouseProc(int param, void *refCon) {
+ return ((Scene *)refCon)->ITEIntroTreeHouseProc(param);
+}
+
+// Handles second intro credit screen (treehouse view)
+int Scene::ITEIntroTreeHouseProc(int param) {
+ Event event;
+ Event *q_event;
+
+ static const IntroCredit credits1[] = {
+ {EN_USA, kITEAny, kCHeader, "Game Design"},
+ {DE_DEU, kITEAny, kCHeader, "Spielentwurf"},
+ {UNK_LANG, kITEAny, kCText, "Talin, Joe Pearce, Robert McNally"},
+ {EN_USA, kITEAny, kCText, "and Carolly Hauksdottir"},
+ {DE_DEU, kITEAny, kCText, "und Carolly Hauksdottir"},
+ {EN_USA, kITEAny, kCHeader, "Screenplay and Dialog"},
+ {EN_USA, kITEAny, kCText, "Robert Leh, Len Wein, and Bill Rotsler"},
+ {DE_DEU, kITEAny, kCHeader, "Geschichte und Dialoge"},
+ {DE_DEU, kITEAny, kCText, "Robert Leh, Len Wein und Bill Rotsler"}
+ };
+
+ int n_credits1 = ARRAYSIZE(credits1);
+
+ static const IntroCredit credits2[] = {
+ {UNK_LANG, kITEWyrmKeep, kCHeader, "Art Direction"},
+ {UNK_LANG, kITEWyrmKeep, kCText, "Allison Hershey"},
+ {EN_USA, kITEAny, kCHeader, "Art"},
+ {DE_DEU, kITEAny, kCHeader, "Grafiken"},
+ {UNK_LANG, kITEWyrmKeep, kCText, "Ed Lacabanne, Glenn Price, April Lee,"},
+ {UNK_LANG, kITENotWyrmKeep, kCText, "Edward Lacabanne, Glenn Price, April Lee,"},
+ {UNK_LANG, kITEWyrmKeep, kCText, "Lisa Sample, Brian Dowrick, Reed Waller,"},
+ {EN_USA, kITEWyrmKeep, kCText, "Allison Hershey and Talin"},
+ {DE_DEU, kITEWyrmKeep, kCText, "Allison Hershey und Talin"},
+ {EN_USA, kITENotWyrmKeep, kCText, "Lisa Iennaco, Brian Dowrick, Reed"},
+ {EN_USA, kITENotWyrmKeep, kCText, "Waller, Allison Hershey and Talin"},
+ {DE_DEU, kITEAny, kCText, "Waller, Allison Hershey und Talin"},
+ {EN_USA, kITENotWyrmKeep, kCHeader, "Art Direction"},
+ {DE_DEU, kITENotWyrmKeep, kCHeader, "Grafische Leitung"},
+ {UNK_LANG, kITENotWyrmKeep, kCText, "Allison Hershey"}
+ };
+
+ int n_credits2 = ARRAYSIZE(credits2);
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Start 'dissolve' transition to new scene background
+ event.type = kEvTContinuous;
+ event.code = kTransitionEvent;
+ event.op = kEventDissolve;
+ event.time = 0;
+ event.duration = DISSOLVE_DURATION;
+
+ q_event = _vm->_events->queue(&event);
+
+ if (_vm->_anim->hasAnimation(0)) {
+ // Begin title screen background animation
+ _vm->_anim->setFrameTime(0, 100);
+
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventPlay;
+ event.param = 0;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ }
+
+ // Queue game credits list
+ q_event = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
+ q_event = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
+
+ // End scene after credit display
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = 1000;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ case SCENE_END:
+ break;
+ default:
+ warning("Illegal scene procedure parameter");
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_ITEIntroFairePathProc(int param, void *refCon) {
+ return ((Scene *)refCon)->ITEIntroFairePathProc(param);
+}
+
+// Handles third intro credit screen (path to puzzle tent)
+int Scene::ITEIntroFairePathProc(int param) {
+ Event event;
+ Event *q_event;
+
+ static const IntroCredit credits1[] = {
+ {EN_USA, kITEAny, kCHeader, "Programming"},
+ {DE_DEU, kITEAny, kCHeader, "Programmiert von"},
+ {UNK_LANG, kITEAny, kCText, "Talin, Walter Hochbrueckner,"},
+ {EN_USA, kITEAny, kCText, "Joe Burks and Robert Wiggins"},
+ {DE_DEU, kITEAny, kCText, "Joe Burks und Robert Wiggins"},
+ {EN_USA, kITEPCCD | kITEWyrmKeep, kCHeader, "Additional Programming"},
+ {EN_USA, kITEPCCD | kITEWyrmKeep, kCText, "John Bolton"},
+ {UNK_LANG, kITEMac, kCHeader, "Macintosh Version"},
+ {UNK_LANG, kITEMac, kCText, "Michael McNally and Robert McNally"},
+ {EN_USA, kITEAny, kCHeader, "Music and Sound"},
+ {DE_DEU, kITEAny, kCHeader, "Musik und Sound"},
+ {UNK_LANG, kITEAny, kCText, "Matt Nathan"}
+ };
+
+ int n_credits1 = ARRAYSIZE(credits1);
+
+ static const IntroCredit credits2[] = {
+ {EN_USA, kITEAny, kCHeader, "Directed by"},
+ {DE_DEU, kITEAny, kCHeader, "Regie"},
+ {UNK_LANG, kITEAny, kCText, "Talin"}
+ };
+
+ int n_credits2 = ARRAYSIZE(credits2);
+
+ switch (param) {
+ case SCENE_BEGIN:
+ // Start 'dissolve' transition to new scene background
+ event.type = kEvTContinuous;
+ event.code = kTransitionEvent;
+ event.op = kEventDissolve;
+ event.time = 0;
+ event.duration = DISSOLVE_DURATION;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Begin title screen background animation
+ _vm->_anim->setCycles(0, -1);
+
+ event.type = kEvTOneshot;
+ event.code = kAnimEvent;
+ event.op = kEventPlay;
+ event.param = 0;
+ event.time = 0;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Queue game credits list
+ q_event = ITEQueueCredits(DISSOLVE_DURATION + 2000, CREDIT_DURATION1, n_credits1, credits1);
+ q_event = ITEQueueCredits(DISSOLVE_DURATION + 7000, CREDIT_DURATION1, n_credits2, credits2);
+
+ // End scene after credit display
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = 1000;
+
+ q_event = _vm->_events->chain(q_event, &event);
+ break;
+ case SCENE_END:
+ break;
+ default:
+ warning("Illegal scene procedure parameter");
+ break;
+ }
+
+ return 0;
+}
+
+int Scene::SC_ITEIntroFaireTentProc(int param, void *refCon) {
+ return ((Scene *)refCon)->ITEIntroFaireTentProc(param);
+}
+
+// Handles fourth intro credit screen (treehouse view)
+int Scene::ITEIntroFaireTentProc(int param) {
+ Event event;
+ Event *q_event;
+ Event *q_event_start;
+
+ switch (param) {
+ case SCENE_BEGIN:
+
+ // Start 'dissolve' transition to new scene background
+ event.type = kEvTContinuous;
+ event.code = kTransitionEvent;
+ event.op = kEventDissolve;
+ event.time = 0;
+ event.duration = DISSOLVE_DURATION;
+
+ q_event_start = _vm->_events->queue(&event);
+
+ // End scene after momentary pause
+ event.type = kEvTOneshot;
+ event.code = kSceneEvent;
+ event.op = kEventEnd;
+ event.time = 5000;
+ q_event = _vm->_events->chain(q_event_start, &event);
+ break;
+ case SCENE_END:
+ break;
+ default:
+ warning("Illegal scene procedure parameter");
+ break;
+ }
+
+ return 0;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/itedata.cpp b/engines/saga/itedata.cpp
new file mode 100644
index 0000000000..3329ab1bbd
--- /dev/null
+++ b/engines/saga/itedata.cpp
@@ -0,0 +1,484 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Actor and Object data tables
+#include "saga/saga.h"
+#include "saga/itedata.h"
+#include "saga/resnames.h"
+#include "saga/sndres.h"
+
+namespace Saga {
+
+ActorTableData ITE_ActorTable[ITE_ACTORCOUNT] = {
+ // Original used so called permanent actors for first three and that was designed by
+ // EXTENDED object flag. They contained frames in more than one resource. We use
+ // different technique here see "Appending to sprite list" in loadActorResources()
+
+// flags name scene x y z spr frm scp col
+// ------------ ---- ---- ---- ----- ---- ---- ---- --- ---- -- -- --
+ { kProtagonist | kExtended,
+ 0, 1, 0, 0, 0, 37, 135, 0, 1, 0, 0, 0}, // map party
+ // spr and frm numbers taken from permanent actors list
+ { kFollower | kExtended,
+ 1, 0, 0, 0, 0, 45, 177, 1, 132, 0, 0, 0}, // Okk
+ { kFollower | kExtended,
+ 2, 0, 0, 0, 0, 48, 143, 2, 161, 0, 0, 0}, // Eeah
+ { 0, 3, 0, 240, 480, 0, 115, 206, 0, 25, 0, 0, 0}, // albino ferret
+ { 0, 4, 17, 368, 400, 0, 115, 206, 4, 49, 0, 0, 0}, // moneychanger
+ { 0, 5, 11, 552, 412, 0, 54, 152, 1, 171, 0, 0, 0}, // Sist
+ { 0, 17, 2, 1192, 888, 0, 57, 153, 17, 49, 0, 0, 0}, // worker ferret 1
+ { 0, 17, 2, 816, 1052, 0, 57, 153, 18, 49, 0, 0, 0}, // worker ferret 2
+ { 0, 17, 2, 928, 932, 0, 58, 153, 19, 49, 0, 0, 0}, // worker ferret 3
+ { 0, 17, 2, 1416, 1160, 0, 58, 153, 20, 49, 0, 0, 0}, // worker ferret 4
+ { 0, 19, 49, 1592, 1336, 0, 92, 175, 15, 162, 0, 0, 0}, // faire merchant 1 (bear)
+ { 0, 20, 49, 744, 824, 0, 63, 156, 19, 112, 0, 4, 4}, // faire merchant 2 (ferret)
+ { 0, 19, 0, 1592, 1336, 0, 92, 175, 0, 171, 0, 0, 0}, // faire merchant 3
+ { 0, 19, 0, 1592, 1336, 0, 92, 175, 0, 171, 0, 0, 0}, // faire merchant 4
+ { 0, 9, 49, 1560, 1624, 0, 94, 147, 18, 132, 0, 4, 4}, // faire goer 1a (rat)
+ { 0, 56, 49, 1384, 792, 0, 95, 193, 20, 72, 0, 0, 0}, // faire goer 1b (otter)
+ { 0, 19, 0, 1592, 1336, 0, 92, 175, 0, 171, 0, 0, 0}, // faire goer 2a
+ { 0, 19, 0, 1592, 1336, 0, 92, 175, 0, 171, 0, 0, 0}, // faire goer 2b
+ { 0, 19, 0, 1592, 1336, 0, 92, 175, 0, 171, 0, 0, 0}, // faire goer 3a
+ { 0, 19, 0, 1592, 1336, 0, 92, 175, 0, 171, 0, 0, 0}, // faire goer 3b
+ { 0, 19, 0, 1592, 1336, 0, 92, 175, 0, 171, 0, 0, 0}, // faire goer 4a
+ { 0, 19, 0, 1592, 1336, 0, 92, 175, 0, 171, 0, 0, 0}, // faire goer 4b
+ { 0, 18, 32, 764, 448, 0, 55, 150, 0, 48, 10, 4, 4}, // Scorry
+ { 0, 35, 32, 0, 0, 0, 56, 151, 0, 112, 0, 0, 0}, // grand puzzler
+ { 0, 36, 32, 0, 0, 0, 105, 142, 0, 155, 0, 0, 0}, // Rhene
+ { 0, 32, 32, 0, 0, 0, 91, 190, 0, 98, 0, 0, 0}, // elk captain
+ { 0, 31, 32, 0, 0, 0, 90, 189, 0, 171, 0, 0, 0}, // elk guard 1
+ { 0, 31, 32, 0, 0, 0, 90, 189, 0, 171, 0, 0, 0}, // elk guard 2
+ { 0, 31, 32, 0, 0, 0, 90, 189, 0, 171, 0, 0, 0}, // elk guard 3
+ { 0, 31, 32, 0, 0, 0, 79, 172, 0, 18, 0, 0, 0}, // boar sergeant
+ { 0, 21, 50, 664, 400, 0, 76, 171, 2, 74, 0, 4, 4}, // boar sentry 1
+ { 0, 21, 50, 892, 428, 0, 76, 171, 2, 74, 0, 4, 4}, // boar sentry 2
+ { 0, 9, 51, 904, 936, 0, 51, 145, 35, 5, 0, 0, 0}, // hall rat 1
+ { 0, 9, 51, 872, 840, 0, 51, 145, 36, 5, 0, 0, 0}, // hall rat 2
+ { 0, 9, 51, 1432, 344, 0, 51, 145, 37, 5, 0, 0, 0}, // hall rat 3
+ { 0, 9, 51, 664, 472, 0, 51, 145, 38, 5, 0, 0, 0}, // hall rat 4
+ { 0, 10, 51, 1368, 1464, 0, 80, 146, 39, 147, 0, 0, 0}, // book rat 1
+ { 0, 10, 51, 1416, 1624, 0, 80, 146, 40, 147, 0, 0, 0}, // book rat 2
+ { 0, 10, 51, 1752, 120, 0, 80, 146, 41, 147, 0, 0, 0}, // book rat 3
+ { 0, 10, 51, 984, 408, 0, 80, 146, 42, 147, 0, 0, 0}, // book rat 4
+ { 0, 14, 52, 856, 376, 0, 82, 174, 8, 73, 0, 0, 0}, // grounds servant 1
+ { 0, 14, 52, 808, 664, 0, 82, 174, 9, 73, 0, 0, 0}, // grounds servant 2
+ { 0, 14, 52, 440, 568, 0, 82, 174, 10, 73, 0, 0, 0}, // grounds servant 3
+ { 0, 14, 52, 392, 776, 0, 82, 174, 11, 73, 0, 0, 0}, // grounds servant 4
+ { 0, 21, 4, 240, 384, 0, 79, 172, 0, 18, 0, 2, 2}, // boar sentry 3 (by doorway)
+ { 0, 23, 4, 636, 268, 0, 77, 173, 0, 74, 0, 4, 4}, // boar courtier
+ { 0, 22, 4, 900, 320, 0, 78, 179, 0, 60, 0, 4, 4}, // boar king
+ { 0, 14, 4, 788, 264, 0, 75, 170, 0, 171, 0, 2, 2}, // boar servant 1
+ { 0, 14, 4, 1088, 264, 0, 75, 170, 0, 171, 0, 6, 6}, // boar servant 2
+ { 0, 24, 19, 728, 396, 0, 65, 181, 47, 146, 0, 6, 6}, // glass master
+ { 0, 24, 21, -20, -20, 0, 66, 182, 0, 146, 0, 4, 4}, // glass master (with orb)
+ { kCycle, 25, 19, 372, 464, 0, 67, 183, 73, 146, 0, 2, 2}, // glass worker
+ { 0, 26, 5, 564, 476, 27, 53, 149, 1, 5, 0, 4, 4}, // door rat
+ { kCycle, 27, 31, 868, 344, 0, 81, 180, 0, 171, 0, 4, 4}, // bees
+ { 0, 28, 73, 568, 380, 0, 83, 176, 30, 120, 0, 4, 4}, // fortune teller
+ { 0, 14, 7, 808, 480, 0, 82, 174, 9, 73, 0, 0, 0}, // orb messenger
+ { 0, 29, 10, 508, 432, 0, 84, 186, 6, 112, 0, 4, 4}, // elk king
+ { 0, 33, 10, 676, 420, 0, 86, 184, 6, 171, 0, 4, 4}, // elk chancellor
+ { 0, 30, 10, 388, 452, 0, 88, 185, 6, 171, 0, 4, 4}, // elk courtier 1
+ { 0, 30, 10, 608, 444, 0, 89, 185, 6, 171, 0, 4, 4}, // elk courtier 2
+ { 0, 31, 10, 192, 468, 0, 90, 189, 6, 171, 0, 4, 4}, // elk throne guard 1
+ { 0, 31, 10, 772, 432, 0, 90, 189, 6, 171, 0, 4, 4}, // elk throne guard 2
+ { 0, 14, 10, 1340, 444, 0, 87, 188, 6, 171, 0, 4, 4}, // elk servant
+ { 0, 20, 18, 808, 360, 7, 60, 154, 64, 88, 0, 4, 4}, // hardware ferret
+ { 0, 34, 49, 1128, 1256, 0, 96, 191, 16, 35, 0, 4, 4}, // porcupine
+ { 0, 34, 49, 1384, 792, 0, 93, 192, 17, 66, 0, 4, 4}, // faire ram
+ { 0, 24, 21, 0, -40, 0, 65, 181, 50, 146, 0, 6, 6}, // glass master 2
+ { 0, 3, 21, 0, -40, 0, 64, 158, 49, 112, 0, 0, 0}, // Sakka
+ { 0, 17, 21, 0, -40, 0, 62, 157, 74, 48, 0, 0, 0}, // lodge ferret 1
+ { 0, 17, 21, 0, -40, 0, 62, 157, 74, 49, 0, 0, 0}, // lodge ferret 2
+ { 0, 17, 21, 0, -40, 0, 62, 157, 74, 50, 0, 0, 0}, // lodge ferret 3
+ { 0, 12, 244, 1056, 504, 0, 107, 167, 21, 124, 0, 6, 6}, // Elara
+ { 0, 8, 33, 248, 440, 0, 68, 169, 14, 112, 0, 0, 0}, // Tycho
+ { 0, 11, 23, 308, 424, 0, 106, 166, 6, 48, 0, 2, 2}, // Alamma
+ { 0, 17, 2, 1864, 1336, 0, 58, 153, 21, 49, 0, 0, 0}, // worker ferret 5
+ { 0, 17, 2, 760, 216, 0, 58, 153, 22, 49, 0, 0, 0}, // worker ferret 6
+ { 0, 44, 29, 0, 0, 0, 72, 159, 0, 112, 0, 0, 0}, // Prince
+ { 0, 45, 29, 0, 0, 0, 71, 163, 0, 146, 0, 6, 6}, // harem girl 1
+ { 0, 45, 29, 0, 0, 0, 71, 163, 0, 124, 0, 2, 2}, // harem girl 2
+ { 0, 45, 29, 0, 0, 0, 71, 163, 0, 169, 0, 0, 0}, // harem girl 3
+ { 0, 7, 29, 0, 0, 0, 69, 164, 0, 4, 0, 0, 0}, // dog sergeant
+ { 0, 7, 29, 0, 0, 0, 70, 165, 0, 4, 0, 0, 0}, // throne dog guard 1
+ { 0, 7, 257, 552, 408, 0, 70, 165, 0, 4, 0, 2, 2}, // throne dog guard 2
+ { 0, 7, 29, 0, 0, 0, 70, 165, 0, 4, 0, 0, 0}, // throne dog guard 3
+ { 0, 7, 29, 0, 0, 0, 70, 165, 0, 4, 0, 0, 0}, // throne dog guard 4
+ { 0, 7, 257, 712, 380, 0, 69, 164, 0, 4, 0, 4, 4}, // throne dog guard 5
+ { 0, 7, 29, 0, 0, 0, 69, 164, 0, 4, 0, 0, 0}, // throne dog guard 6
+ { 0, 7, 29, 0, 0, 0, 69, 164, 0, 4, 0, 0, 0}, // throne dog guard 7
+ { 0, 7, 29, 0, 0, 0, 69, 164, 0, 4, 0, 0, 0}, // throne dog guard 8
+ { 0, 7, 29, 0, 0, 0, 69, 164, 0, 4, 0, 0, 0}, // throne dog guard 9
+ { 0, 7, 0, 0, 0, 0, 69, 164, 0, 4, 0, 0, 0}, // throne dog guard 10
+ { 0, 7, 29, 0, 0, 0, 70, 165, 0, 4, 0, 0, 0}, // throne dog guard 11
+ { 0, 47, 30, 0, 0, 0, 102, 199, 1, 186, 0, 0, 0}, // old wolf ferryman
+ { 0, 48, 69, 0, 0, 0, 109, 202, 35, 26, 0, 0, 0}, // cat village wildcat
+ { 0, 49, 69, 0, 0, 0, 109, 202, 35, 26, 0, 0, 0}, // cat village attendant
+ { 0, 50, 69, 0, 0, 0, 111, 203, 16, 67, 0, 0, 0}, // cat village Prowwa
+ { 0, 51, 20, 0, 0, 0, 112, 204, 15, 26, 0, 0, 0}, // Prowwa hut Mirrhp
+ { 0, 50, 20, 0, 0, 0, 111, 203, 14, 67, 0, 0, 0}, // Prowwa hut Prowwa
+ { 0, 49, 20, 0, 0, 0, 109, 202, 35, 26, 0, 0, 0}, // Prowwa hut attendant
+ { 0, 48, 256, 0, 0, 0, 109, 202, 35, 26, 0, 0, 0}, // wildcat sentry
+ { 0, 21, 32, 0, 0, 0, 76, 171, 0, 171, 0, 0, 0}, // boar warrior 1
+ { 0, 21, 32, 0, 0, 0, 76, 171, 0, 171, 0, 0, 0}, // boar warrior 2
+ { 0, 21, 32, 0, 0, 0, 76, 171, 0, 171, 0, 0, 0}, // boar warrior 3
+ { 0, 52, 15, 152, 400, 0, 108, 168, 19, 48, 10, 2, 2}, // Alamma's voice
+ { 0, 47, 251, 640, 360, 0, 113, 205, 5, 186, 10, 2, 2}, // ferry on ocean
+ { 0, 41, 75, 152, 400, 0, 100, 197, 5, 81, 0, 0, 0}, // Shiala
+ { 0, 44, 9, 0, 0, 0, 73, 160, 54, 112, 0, 0, 0}, // Prince (asleep)
+ { 0, 0, 22, -20, -20, 0, 118, 209, 0, 171, 0, 0, 0}, // Rif and Eeah (at rockslide)
+ { 0, 1, 22, 0, 0, 0, 119, 210, 0, 171, 0, 0, 0}, // Okk (at rockslide)
+ { 0, 0, 22, -20, -20, 0, 118, 209, 0, 171, 0, 0, 0}, // Rif and Eeah (at rockslide w. rope)
+ { 0, 1, 22, 0, 0, 0, 119, 210, 0, 171, 0, 0, 0}, // Okk (at rockslide w. rope)
+ { 0, 53, 42, 640, 400, 0, 104, 201, 8, 141, 0, 0, 0}, // Kylas Honeyfoot
+ { 0, 54, 21, -20, -20, 0, 120, 211, 48, 238, 0, 0, 0}, // Orb of Hands
+ { 0, 0, 4, -20, -20, 0, 42, 140, 0, 1, 0, 0, 0}, // Rif (muddy)
+ { 0, 26, 5, -20, -20, 27, 52, 148, 1, 5, 0, 4, 4}, // door rat (standing)
+ { 0, 36, 4, -20, -20, 0, 116, 207, 0, 155, 0, 0, 0}, // boar with Rhene 1
+ { 0, 36, 0, -20, -20, 0, 117, 208, 0, 155, 0, 0, 0}, // boar with Rhene 2
+ { 0, 46, 252, -20, -20, 0, 74, 162, 29, 34, 0, 0, 0}, // dog jailer
+ { 0, 0, 32, -20, -20, 0, 41, 137, 0, 1, 0, 0, 0}, // Rif (tourney)
+ { 0, 0, 259, -20, -20, 0, 44, 138, 0, 1, 0, 0, 0}, // cliff rat
+ { 0, 0, 5, -20, -20, 0, 43, 139, 0, 1, 0, 0, 0}, // Rif (cloaked)
+ { 0, 0, 31, -20, -20, 0, 39, 136, 0, 1, 0, 0, 0}, // Rif (oak tree scene)
+ { 0, 0, 252, -20, -20, 0, 39, 136, 0, 1, 0, 0, 0}, // Rif (jail cell scene)
+ { 0, 0, 15, -20, -20, 0, 39, 136, 0, 1, 0, 0, 0}, // Rif (outside Alamma's)
+ { 0, 0, 20, -20, -20, 0, 39, 136, 0, 1, 0, 0, 0}, // Rif (sick tent)
+ { 0, 0, 25, -20, -20, 0, 39, 136, 0, 1, 0, 0, 0}, // Rif (gem room)
+ { 0, 0, 272, -20, -20, 0, 40, 141, 0, 1, 0, 0, 0}, // Rif (dragon maze)
+ { 0, 0, 50, -20, -20, 0, 39, 136, 0, 1, 0, 0, 0}, // Rif (boar entry gate)
+ { 0, 50, 71, -20, -20, 0, 111, 203, 0, 67, 0, 0, 0}, // Prowwa (dog castle back)
+ { 0, 50, 274, -20, -20, 0, 111, 203, 0, 67, 0, 0, 0}, // Prowwa (cat festival)
+ { 0, 50, 274, -20, -20, 0, 110, 212, 0, 171, 0, 0, 0}, // cat festival dancer 1
+ { 0, 50, 274, -20, -20, 0, 110, 212, 0, 171, 0, 0, 0}, // cat festival dancer 2
+ { 0, 50, 274, -20, -20, 0, 110, 212, 0, 171, 0, 0, 0}, // cat festival dancer 3
+ { 0, 57, 272, 909, 909, 48, 121, 213, 0, 171, 0, 0, 0}, // komodo dragon
+ { 0, 58, 15, -20, -20, 0, 122, 214, 0, 171, 0, 0, 0}, // letter from Elara
+ { 0, 37, 246, -20, -20, 0, 97, 194, 0, 141, 0, 0, 0}, // Gar (wolves' cage)
+ { 0, 38, 246, -20, -20, 0, 98, 195, 0, 27, 0, 0, 0}, // Wrah (wolves' cage)
+ { 0, 59, 246, -20, -20, 0, 103, 200, 0, 26, 0, 0, 0}, // Chota (wolves' cage)
+ { 0, 41, 245, -20, -20, 0, 100, 197, 0, 81, 0, 0, 0}, // Shiala (wolves' cage)
+ { 0, 47, 250, 640, 360, 0, 114, 205, 0, 186, 10, 2, 2}, // ferry on ocean
+ { 0, 0, 278, -20, -20, 0, 40, 141, 0, 1, 0, 0, 0}, // Rif (falling in tunnel trap door)
+ { 0, 0, 272, -20, -20, 0, 40, 141, 0, 1, 0, 0, 0}, // Rif (falling in dragon maze)
+ { 0, 41, 77, -20, -20, 0, 100, 197, 24, 81, 0, 0, 0}, // Shiala (grotto)
+ { 0, 37, 261, -20, -20, 0, 97, 194, 0, 141, 0, 0, 0}, // Gar (ambush)
+ { 0, 38, 261, -20, -20, 0, 98, 195, 0, 27, 0, 0, 0}, // Wrah (ambush)
+ { 0, 39, 261, -20, -20, 0, 99, 196, 0, 5, 0, 0, 0}, // dark claw wolf (ambush)
+ { 0, 39, 261, -20, -20, 0, 99, 196, 0, 5, 0, 0, 0}, // dark claw wolf (ambush)
+ { 0, 39, 261, -20, -20, 0, 99, 196, 0, 5, 0, 0, 0}, // dark claw wolf (ambush)
+ { 0, 39, 261, -20, -20, 0, 99, 196, 0, 5, 0, 0, 0}, // dark claw wolf (ambush)
+ { 0, 59, 279, -20, -20, 0, 103, 200, 0, 26, 0, 0, 0}, // Chota (top of dam)
+ { 0, 38, 279, -20, -20, 0, 98, 195, 0, 27, 0, 0, 0}, // Wrah (top of dam)
+ { 0, 42, 77, -20, -20, 0, 101, 198, 25, 171, 0, 0, 0}, // Shiala's spear
+ { 0, 59, 281, -20, -20, 0, 103, 200, 26, 26, 0, 0, 0}, // Chota (lab)
+ { 0, 59, 279, -20, -20, 0, 123, 215, 0, 1, 0, 0, 0}, // Rif (finale)
+ { 0, 59, 279, -20, -20, 0, 123, 215, 0, 132, 0, 0, 0}, // Okk (finale)
+ { 0, 59, 279, -20, -20, 0, 123, 215, 0, 161, 0, 0, 0}, // Eeah (finale)
+ { 0, 54, 279, -20, -20, 0, 120, 211, 0, 133, 0, 6, 6}, // Orb of Storms (top of dam)
+ { 0, 44, 9, -20, -20, 0, 124, 161, 0, 171, 0, 6, 6}, // Prince's snores
+ { 0, 7, 255, 588, 252, 0, 70, 165, 0, 3, 0, 2, 2}, // hall dog guard 1
+ { 0, 7, 255, 696, 252, 0, 70, 165, 0, 5, 0, 6, 6}, // hall dog guard 2
+ { 0, 36, 4, 0, 0, 0, 105, 142, 0, 155, 0, 0, 0}, // Rhene
+ { 0, 44, 272, 1124, 1124, 120, 72, 159, 0, 112, 0, 0, 0}, // Prince (dragon maze)
+ { 0, 7, 272, 1124, 1108, 120, 70, 165, 0, 4, 0, 0, 0}, // dog heckler 1 (dragon maze)
+ { 0, 7, 272, 1108, 1124, 120, 70, 165, 0, 4, 0, 0, 0}, // dog heckler 2 (dragon maze)
+ { 0, 29, 288, 508, 432, 0, 85, 187, 0, 112, 0, 4, 4}, // elk king (finale)
+ { 0, 29, 0, 508, 432, 0, 84, 186, 0, 99, 0, 4, 4}, // crowd voice 1 (finale)
+ { 0, 29, 0, 508, 432, 0, 84, 186, 0, 98, 0, 4, 4}, // crowd voice 2 (finale)
+ { 0, 29, 0, 508, 432, 0, 84, 186, 0, 104, 0, 4, 4}, // crowd voice 3 (finale)
+ { 0, 29, 0, 508, 432, 0, 84, 186, 0, 99, 0, 4, 4}, // crowd voice 4 (finale)
+ { 0, 36, 288, 0, 0, 0, 105, 142, 0, 155, 0, 0, 0}, // Rhene (finale)
+ { 0, 1, 27, -20, -20, 0, 47, 178, 0, 132, 0, 0, 0}, // Okk (temple gate)
+ { 0, 1, 252, -20, -20, 0, 47, 178, 0, 132, 0, 0, 0}, // Okk (jail cell)
+ { 0, 1, 25, -20, -20, 0, 47, 178, 0, 132, 0, 0, 0}, // Okk (gem room)
+ { 0, 1, 259, -20, -20, 0, 47, 178, 0, 132, 0, 0, 0}, // Okk (cliff)
+ { 0, 1, 279, -20, -20, 0, 47, 178, 0, 132, 0, 0, 0}, // Okk (dam top)
+ { 0, 1, 273, -20, -20, 0, 47, 178, 0, 132, 0, 0, 0}, // Okk (human ruins)
+ { 0, 1, 26, -20, -20, 0, 8, 178, 0, 171, 0, 0, 0}, // puzzle pieces
+ { 0, 1, 0, -20, -20, 0, 0, 0, 0, 50, 0, 0, 0}, // poker dog 1
+ { 0, 1, 0, -20, -20, 0, 0, 0, 0, 82, 0, 0, 0}, // poker dog 2
+ { 0, 1, 0, -20, -20, 0, 0, 0, 0, 35, 0, 0, 0}, // poker dog 3
+ { 0, 9, 74, -20, -20, 0, 51, 145, 0, 5, 0, 0, 0} // sundial rat
+};
+
+
+ObjectTableData ITE_ObjectTable[ITE_OBJECTCOUNT] = {
+ { 8, 49, 1256, 760, 0, 9, 5, kObjNotFlat }, // Magic Hat
+ { 9, 52, 1080, 1864, 0, 68, 4, kObjUseWith }, // Berries
+ { 10, 259, 744, 524, 0, 79, 42, kObjUseWith }, // Card Key
+ { 11, 0, 480, 480, 0, 69, 6, 0 }, // Foot Print
+ { 12, 0, 480, 480, 0, 13, 38, kObjUseWith }, // Power Cell
+ { 13, 28, 640, 412, 40, 14, 15, kObjUseWith }, // Digital Clock
+ { 14, 0, 480, 480, 0, 15, 41, kObjUseWith }, // Oil Lamp
+ { 15, 24, 868, 456, 35, 46, 13, kObjUseWith }, // Magnetic Key
+ { 16, 0, 480, 480, 0, 17, 7, kObjUseWith }, // Plaster
+ { 17, 249, 320, 476, 45, 18, 44, 0 }, // Trophy
+ { 18, 0, 480, 480, 0, 19, 20, 0 }, // Coins
+ { 19, 19, 600, 480, 0, 20, 8, 0 }, // Lens Fragments
+ { 20, 0, 1012, 568, 80, 44, 10, kObjUseWith }, // Key to jail cell
+ { 21, 0, 480, 480, 0, 22, 9, 0 }, // Remade lens
+ { 22, 0, 480, 480, 0, 23, 21, 0 }, // Tycho's Map
+ { 23, 0, 480, 480, 0, 24, 23, 0 }, // Silver Medallion
+ { 24, 0, 480, 480, 0, 25, 24, 0 }, // Mud in Fur
+ { 25, 0, 480, 480, 0, 26, 25, 0 }, // Gold Ring
+ { 27, 13, 1036, 572, 40, 47, 14, kObjUseWith }, // Screwdriver
+ { 28, 0, 480, 480, 0, 29, 26, 0 }, // Apple Token
+ { 29, 0, 480, 480, 0, 30, 22, kObjUseWith }, // Letter from Elara
+ { 30, 0, 164, 440, 0, 31, 16, kObjUseWith }, // Spoon
+ { 32, 0, 480, 480, 0, 33, 43, 0 }, // Catnip
+ { 33, 31, 580, 392, 0, 45, 11, 0 }, // Twigs
+ { 35, 0, 468, 480, 0, 36, 12, kObjUseWith }, // Empty Bowl (also bowl of honey)
+ { 37, 0, 480, 480, 0, 38, 45, kObjUseWith }, // Needle and Thread
+ { 38, 25, 332, 328, 0, 48, 19, 0 }, // Rock Crystal
+ { 39, 0, 480, 480, 0, 40, 0, kObjUseWith }, // Salve
+ { 40, 269, 644, 416, 0, 41, 39, kObjNotFlat }, // Electrical Cable
+ { 41, 12, 280, 516, 0, 43, 17, kObjUseWith }, // Piece of flint
+ { 42, 5, 876, 332, 32, 65, 18, 0 }, // Rat Cloak
+ { 43, 52, 556, 1612, 0, 49, 28, kObjUseWith | kObjNotFlat }, // Bucket
+ { 48, 52, 732, 948, 0, 50, 27, kObjUseWith }, // Cup
+ { 49, 52, 520, 1872, 0, 53, 29, 0 }, // Fertilizer
+ { 50, 52, 1012, 1268, 0, 52, 30, 0 }, // Feeder
+ { 51, 252, -20, -20, 0, 71, 32, kObjUseWith | kObjNotFlat }, // Bowl in jail cell
+ { 53, 252, 1148, 388, 0, 70, 33, 0 }, // Loose stone block in jail cell
+ { 26, 12, 496, 368, 0, 76, 31, 0 }, // Coil of Rope from Quarry
+ { 54, 281, 620, 352, 0, 80, 46, 0 } // Orb of Storms in Dam Lab
+};
+
+FxTable ITE_SfxTable[ITE_SFXCOUNT] = {
+ { FX_DOOR_OPEN, 127 },
+ { FX_DOOR_CLOSE, 127 },
+ { FX_RUSH_WATER, 63 }, // Floppy volume: 127
+ { FX_RUSH_WATER, 26 }, // Floppy volume: 40
+ { FX_CRICKET, 64 },
+ { FX_PORTICULLIS, 84 }, // Floppy volume: 127
+ { FX_CLOCK_1, 64 },
+ { FX_CLOCK_2, 64 },
+ { FX_DAM_MACHINE, 64 },
+ { FX_DAM_MACHINE, 40 },
+ { FX_HUM1, 64 },
+ { FX_HUM2, 64 },
+ { FX_HUM3, 64 },
+ { FX_HUM4, 64 },
+ { FX_WATER_LOOP_S, 32 }, // Floppy volume: 64
+ { FX_SURF, 42 }, // Floppy volume: 127
+ { FX_SURF, 32 }, // Floppy volume: 64
+ { FX_FIRELOOP, 64 }, // Floppy volume: 96
+ { FX_SCRAPING, 84 }, // Floppy volume: 127
+ { FX_BEE_SWARM, 64 }, // Floppy volume: 96
+ { FX_BEE_SWARM, 26 }, // Floppy volume: 40
+ { FX_SQUEAKBOARD, 64 },
+ { FX_KNOCK, 127 },
+ { FX_COINS, 32 }, // Floppy volume: 48
+ { FX_STORM, 84 }, // Floppy volume: 127
+ { FX_DOOR_CLOSE_2, 84 }, // Floppy volume: 127
+ { FX_ARCWELD, 84 }, // Floppy volume: 127
+ { FX_RETRACT_ORB, 127 },
+ { FX_DRAGON, 127 },
+ { FX_SNORES, 127 },
+ { FX_SPLASH, 127 },
+ { FX_LOBBY_DOOR, 127 },
+ { FX_CHIRP_LOOP, 26 }, // Floppy volume: 40
+ { FX_DOOR_CREAK, 96 },
+ { FX_SPOON_DIG, 64 },
+ { FX_CROW, 96 },
+ { FX_COLDWIND, 42 }, // Floppy volume: 64
+ { FX_TOOL_SND_1, 96 },
+ { FX_TOOL_SND_2, 127 },
+ { FX_TOOL_SND_3, 64 },
+ { FX_DOOR_METAL, 96 },
+ { FX_WATER_LOOP_S, 32 },
+ { FX_WATER_LOOP_L, 32 }, // Floppy volume: 64
+ { FX_DOOR_OPEN_2, 127 },
+ { FX_JAIL_DOOR, 64 },
+ { FX_KILN_FIRE, 53 }, // Floppy volume: 80
+
+ // Only in the CD version
+ { FX_CROWD_01, 64 },
+ { FX_CROWD_02, 64 },
+ { FX_CROWD_03, 64 },
+ { FX_CROWD_04, 64 },
+ { FX_CROWD_05, 64 },
+ { FX_CROWD_06, 64 },
+ { FX_CROWD_07, 64 },
+ { FX_CROWD_08, 64 },
+ { FX_CROWD_09, 64 },
+ { FX_CROWD_10, 64 },
+ { FX_CROWD_11, 64 },
+ { FX_CROWD_12, 64 },
+ { FX_CROWD_13, 64 },
+ { FX_CROWD_14, 64 },
+ { FX_CROWD_15, 64 },
+ { FX_CROWD_16, 64 },
+ { FX_CROWD_17, 64 }
+};
+
+const char *ITEinterfaceTextStrings[][52] = {
+ {
+ "Walk to", "Look At", "Pick Up", "Talk to", "Open",
+ "Close", "Use", "Give", "Options", "Test",
+ "Demo", "Help", "Quit Game", "Fast", "Slow",
+ "On", "Off", "Continue Playing", "Load", "Save",
+ "Game Options", "Reading Speed", "Music", "Sound", "Cancel",
+ "Quit", "OK", "Mid", "Click", "10%",
+ "20%", "30%", "40%", "50%", "60%",
+ "70%", "80%", "90%", "Max", "Quit the Game?",
+ "Load Successful!", "Enter Save Game Name", "Give %s to %s", "Use %s with %s",
+ "[New Save Game]",
+ "I can't pick that up.",
+ "I see nothing special about it.",
+ "There's no place to open it.",
+ "There's no opening to close.",
+ "I don't know how to do that.",
+ "Show Dialog",
+ "What is Rif's reply?"
+ },
+ // German
+ {
+ "Gehe zu", "Schau an", "Nimm", "Rede mit", "\231ffne",
+ "Schlie$e", "Benutze", "Gib", "Optionen", "Test",
+ "Demo", "Hilfe", "Spiel beenden", "S", "L",
+ "An", "Aus", "Weiterspielen", "Laden", "Sichern",
+ "Spieleoptionen", "Lesegeschw.", "Musik", "Sound", "Abbr.",
+ "Beenden", NULL, "M", "Klick", "10%",
+ "20%", "30%", "40%", "50%", "60%",
+ "70%", "80%", "90%", "Max", "Spiel beenden?",
+ "Spielstand geladen!", "Bitte Namen eingeben", "Gib %s zu %s", "Benutze %s mit %s",
+ "[Neuer Spielstand]",
+ "Das kann ich nicht aufnehmen.",
+ "Ich sehe nichts besonderes.",
+ "Das kann man nicht \224ffnen.",
+ "Hier ist keine \231ffnung zum Schlie$en.",
+ "Ich wei$ nicht, wie ich das machen soll.",
+ "Text zeigen",
+ "Wie lautet die Antwort?"
+ }
+};
+
+Point pieceOrigins[PUZZLE_PIECES] = {
+ Point(268, 18),
+ Point(270, 51),
+ Point( 19, 51),
+ Point( 73, 0),
+ Point( 0, 34),
+ Point(215, 0),
+ Point(159, 0),
+ Point( 9, 69),
+ Point(288, 18),
+ Point(112, 0),
+ Point( 27, 88),
+ Point( 43, 0),
+ Point( 0, 0),
+ Point(262, 0),
+ Point(271, 103)
+};
+
+const char *pieceNames[][PUZZLE_PIECES] = {
+ { "screwdriver", "pliers", "c-clamp", "wood clamp", "level",
+ "twine", "wood plane", "claw hammer", "tape measure", "hatchet",
+ "shears", "ruler", "saw", "mallet", "paint brush"
+ },
+ { "Schraubendreher", "Zange", "Schraubzwinge", "Holzzwinge", "Wasserwaage",
+ "Bindfaden", "Hobel", "Schusterhammer", "Bandma$", "Beil",
+ "Schere", "Winkel", "S\204ge", "Hammer", "Pinsel"
+ }
+};
+
+const char *hintStr[][4] = {
+ { "Check which pieces could fit in each corner first.",
+ "Check which corner has the least number of pieces that can fit and start from there.",
+ "Check each new corner and any new side for pieces that fit.",
+ "I don't see anything out of place."
+ },
+ { "\232berpr\201fe zun\204chst, welche die Eckteile sein k\224nnten.",
+ "Schau, in welche Ecke die wenigsten Teile passen, und fang dort an.",
+ "Untersuche jede Ecke und jede Seite auf Teile, die dort passen k\224nnen.",
+ "Ich sehe nichts an der falschen Stelle."
+ }
+};
+
+const char *solicitStr[][NUM_SOLICIT_REPLIES] = {
+ { "Hey, Fox! Would you like a hint?",
+ "Would you like some help?",
+ "Umm...Umm...",
+ "Psst! want a hint?",
+ "I would have done this differently, you know."
+ },
+ { "Hey, Fuchs! Brauchst Du \047nen Tip?",
+ "M\224chtest Du etwas Hilfe?"
+ "\231hm...\216hm..."
+ "Psst! \047n Tip vielleicht?"
+ "Ja, wei$t Du... ich h\204tte das anders gemacht."
+ }
+};
+
+const char portraitList[] = {
+ RID_ITE_JFERRET_SERIOUS,
+ RID_ITE_JFERRET_GOOFY,
+ RID_ITE_JFERRET_SERIOUS,
+ RID_ITE_JFERRET_GOOFY,
+ RID_ITE_JFERRET_ALOOF
+};
+
+const char *sakkaStr[][NUM_SAKKA] = {
+ { "Hey, you're not supposed to help the applicants!",
+ "Guys! This is supposed to be a test!",
+ "C'mon fellows, that's not in the rules!"
+ },
+ { "Hey, Du darfst dem Pr\201fling nicht helfen!",
+ "Hallo?! Dies soll eine Pr\201fung sein!",
+ "Also, Jungs. Schummeln steht nicht in den Regeln!"
+ }
+};
+
+const char *whineStr[][NUM_WHINES] = {
+ { "Aww, c'mon Sakka!",
+ "One hint won't hurt, will it?",
+ "Sigh...",
+ "I think that clipboard has gone to your head, Sakka!",
+ "Well, I don't recall any specific rule against hinting."
+ },
+ { "Och, sei nicht so, Sakka!"
+ "EIN Tip wird schon nicht schaden, oder?",
+ "Seufz..."
+ "Ich glaube, Du hast ein Brett vor dem Kopf, Sakka!",
+ "Hm, ich kann mich an keine Regel erinnern, die Tips verbietet."
+ }
+};
+
+const char *optionsStr[][4] = {
+ { "\"I'll do this puzzle later.\"",
+ "\"Yes, I'd like a hint please.\"",
+ "\"No, thank you, I'd like to try and solve it myself.\"",
+ "I think the %s is in the wrong place."
+ },
+ { "\"Ich l\224se das Puzzle sp\204ter.\"",
+ "\"Ja, ich m\224chte einen Tip, bitte.\"",
+ "\"Nein danke, ich m\224chte das alleine l\224sen.\"",
+ "Pssst... %s... falsche Stelle..."
+ }
+};
+
+} // End of namespace Saga
diff --git a/engines/saga/itedata.h b/engines/saga/itedata.h
new file mode 100644
index 0000000000..45d045356d
--- /dev/null
+++ b/engines/saga/itedata.h
@@ -0,0 +1,113 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Actor data table header file
+
+#ifndef SAGA_ITEDATA_H
+#define SAGA_ITEDATA_H
+
+namespace Saga {
+
+enum ActorFlags {
+ kProtagonist = 0x01, // Actor is protagonist
+ kFollower = 0x02, // Actor is follower
+ kCycle = 0x04, // Actor stand has a cycle
+ kFaster = 0x08, // Actor is fast
+ kFastest = 0x10, // Actor is faster
+ kExtended = 0x20 // Actor uses extended sprites
+};
+
+// TODO: This doesn't quite correspond to the original Actor struct, so I'm not
+// sure if I got it right.
+
+struct ActorTableData {
+ byte flags;
+ byte nameIndex;
+ int32 sceneIndex;
+ int16 x;
+ int16 y;
+ int16 z;
+ int32 spriteListResourceId;
+ int32 frameListResourceId;
+ byte scriptEntrypointNumber;
+ byte speechColor;
+ byte currentAction;
+ byte facingDirection;
+ byte actionDirection;
+};
+
+#define ITE_ACTORCOUNT 181
+
+extern ActorTableData ITE_ActorTable[ITE_ACTORCOUNT];
+
+enum {
+ kObjUseWith = 0x01,
+ kObjNotFlat = 0x02
+};
+
+struct ObjectTableData {
+ byte nameIndex;
+ int32 sceneIndex;
+ int16 x;
+ int16 y;
+ int16 z;
+ int32 spriteListResourceId;
+ byte scriptEntrypointNumber;
+ uint16 interactBits;
+};
+
+struct FxTable {
+ int res;
+ int vol;
+};
+
+#define ITE_OBJECTCOUNT 39
+#define ITE_SFXCOUNT 63
+
+extern ObjectTableData ITE_ObjectTable[ITE_OBJECTCOUNT];
+extern FxTable ITE_SfxTable[ITE_SFXCOUNT];
+
+extern const char *ITEinterfaceTextStrings[][52];
+
+#define PUZZLE_PIECES 15
+
+extern Point pieceOrigins[PUZZLE_PIECES];
+extern const char *pieceNames[][PUZZLE_PIECES];
+
+#define NUM_SOLICIT_REPLIES 5
+extern const char *solicitStr[][NUM_SOLICIT_REPLIES];
+
+#define NUM_SAKKA 3
+extern const char *sakkaStr[][NUM_SAKKA];
+
+#define NUM_WHINES 5
+extern const char *whineStr[][NUM_WHINES];
+
+extern const char *hintStr[][4];
+extern const char portraitList[];
+extern const char *optionsStr[][4];
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/list.h b/engines/saga/list.h
new file mode 100644
index 0000000000..d002f405de
--- /dev/null
+++ b/engines/saga/list.h
@@ -0,0 +1,156 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SAGA_LIST_H__
+#define SAGA_LIST_H__
+
+#include "common/list.h"
+
+namespace Saga {
+
+template <class T>
+class SortedList : public Common::List<T> {
+public:
+ typedef int (*CompareFunction) (const T& a, const T& b);
+
+ typedef typename Common::List<T>::iterator iterator;
+ typedef typename Common::List<T>::const_iterator const_iterator;
+ typedef typename Common::List<T> Common_List;
+
+public:
+
+ iterator pushFront(const T& element) {
+ return insert(Common::List<T>::begin(), element);
+ }
+
+ iterator pushBack(const T& element) {
+ return insert(Common_List::end(), element);
+ }
+
+ iterator insert(iterator pos, const T& element) {
+ Common_List::insert(pos, element);
+ return --pos;
+ }
+
+ iterator pushFront() {
+ return insert(Common_List::begin());
+ }
+
+ iterator pushBack() {
+ return insert(Common_List::end());
+ }
+
+ iterator insert(iterator pos) {
+ T init;
+ return insert(pos, init);
+ }
+
+ iterator pushFront(const T& element, CompareFunction compareFunction) {
+ return insert(Common::List<T>::begin(), element, compareFunction);
+ }
+
+ iterator pushBack(const T& element, CompareFunction compareFunction) {
+ return insert(Common_List::end(), element, compareFunction);
+ }
+
+ iterator insert(iterator pos, const T& element, CompareFunction compareFunction) {
+ int res;
+
+ for (iterator i = Common_List::begin(); i != Common_List::end(); ++i) {
+ res = compareFunction(element, i.operator*());
+ if (res < 0) {
+ return insert(i, element);
+ }
+ }
+ return pushBack(element);
+ }
+
+ iterator reorderUp(iterator pos, CompareFunction compareFunction) {
+ iterator i(pos);
+ int res;
+
+ --i;
+ while (i != Common::List<T>::end()) {
+ res = compareFunction(i.operator*(), pos.operator*());
+ if (res <= 0) {
+
+ T temp(*pos);
+ erase(pos);
+ ++i;
+ return insert(i, temp);
+ }
+ --i;
+ }
+ return pos;
+ }
+
+ iterator reorderDown(iterator pos, CompareFunction compareFunction) {
+ iterator i(pos);
+ int res;
+
+ ++i;
+ while (i != Common::List<T>::end()) {
+ res = compareFunction(i.operator*(), pos.operator*());
+ if (res >= 0) {
+
+ T temp(*pos);
+ erase(pos);
+ return insert(i, temp);
+ }
+ ++i;
+ }
+ return pos;
+ }
+
+ iterator eraseAndPrev(iterator pos) {
+ assert(pos != Common_List::end());
+ iterator res(pos);
+
+ --res;
+ erase(pos);
+ return res;
+ }
+
+ void remove(const T* val) {
+ for (iterator i = Common_List::begin(); i != Common_List::end(); ++i)
+ if (val == i.operator->()) {
+ erase(i);
+ return;
+ }
+ }
+
+ bool locate(const T* val, iterator& foundedIterator) {
+
+ for (iterator i = Common::List<T>::begin(); i != Common::List<T>::end(); ++i)
+ if (val == i.operator->())
+ {
+ foundedIterator = i;
+ return true;
+ }
+
+ return false;
+ }
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/module.mk b/engines/saga/module.mk
new file mode 100644
index 0000000000..3d39108444
--- /dev/null
+++ b/engines/saga/module.mk
@@ -0,0 +1,44 @@
+MODULE := engines/saga
+
+MODULE_OBJS := \
+ engines/saga/actor.o \
+ engines/saga/animation.o \
+ engines/saga/console.o \
+ engines/saga/events.o \
+ engines/saga/font.o \
+ engines/saga/font_map.o \
+ engines/saga/game.o \
+ engines/saga/gfx.o \
+ engines/saga/ihnm_introproc.o \
+ engines/saga/image.o \
+ engines/saga/interface.o \
+ engines/saga/isomap.o \
+ engines/saga/ite_introproc.o \
+ engines/saga/itedata.o \
+ engines/saga/objectmap.o \
+ engines/saga/puzzle.o \
+ engines/saga/palanim.o \
+ engines/saga/render.o \
+ engines/saga/rscfile.o \
+ engines/saga/saga.o \
+ engines/saga/saveload.o \
+ engines/saga/scene.o \
+ engines/saga/script.o \
+ engines/saga/sfuncs.o \
+ engines/saga/sndres.o \
+ engines/saga/sprite.o \
+ engines/saga/sthread.o \
+ engines/saga/input.o \
+ engines/saga/music.o \
+ engines/saga/sound.o
+
+MODULE_DIRS += \
+ engines/saga
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
new file mode 100644
index 0000000000..e352818ee6
--- /dev/null
+++ b/engines/saga/music.cpp
@@ -0,0 +1,524 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "saga/saga.h"
+
+#include "saga/rscfile.h"
+#include "saga/music.h"
+#include "saga/stream.h"
+#include "sound/audiostream.h"
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
+#include "sound/flac.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+
+namespace Saga {
+
+#define BUFFER_SIZE 4096
+
+struct TrackFormat {
+ DigitalTrackInfo* (*openTrackFunction)(int);
+};
+
+static const TrackFormat TRACK_FORMATS[] = {
+#ifdef USE_FLAC
+ { getFlacTrack },
+#endif
+#ifdef USE_VORBIS
+ { getVorbisTrack },
+#endif
+#ifdef USE_MAD
+ { getMP3Track },
+#endif
+
+ { NULL } // Terminator
+};
+
+// I haven't decided yet if it's a good idea to make looping part of the audio
+// stream class, or if I should use a "wrapper" class, like I did for Broken
+// Sword 2, to make it easier to add support for compressed music... but I'll
+// worry about that later.
+
+class RAWInputStream : public AudioStream {
+private:
+ ResourceContext *_context;
+ Common::File *_file;
+ uint32 _filePos;
+ uint32 _startPos;
+ uint32 _endPos;
+ bool _finished;
+ bool _looping;
+ int16 _buf[BUFFER_SIZE];
+ const int16 *_bufferEnd;
+ const int16 *_pos;
+ const GameSoundInfo *_musicInfo;
+
+ void refill();
+ bool eosIntern() const {
+ return _pos >= _bufferEnd;
+ }
+
+public:
+ RAWInputStream(SagaEngine *vm, ResourceContext *context, uint32 resourceId, bool looping);
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool endOfData() const { return eosIntern(); }
+ bool isStereo() const { return _musicInfo->stereo; }
+ int getRate() const { return _musicInfo->frequency; }
+};
+
+RAWInputStream::RAWInputStream(SagaEngine *vm, ResourceContext *context, uint32 resourceId, bool looping)
+ : _context(context), _finished(false), _looping(looping), _bufferEnd(_buf + BUFFER_SIZE) {
+
+ ResourceData * resourceData;
+
+ resourceData = vm->_resource->getResourceData(context, resourceId);
+ _file = context->getFile(resourceData);
+ _musicInfo = vm->getMusicInfo();
+
+ if (_musicInfo == NULL) {
+ error("RAWInputStream() wrong musicInfo");
+ }
+
+ // Determine the end position
+ _startPos = resourceData->offset;
+ _endPos = _startPos + resourceData->size;
+ _filePos = _startPos;
+
+ // Read in initial data
+ refill();
+}
+
+int RAWInputStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samples = 0;
+ while (samples < numSamples && !eosIntern()) {
+ const int len = MIN(numSamples - samples, (int) (_bufferEnd - _pos));
+ memcpy(buffer, _pos, len * 2);
+ buffer += len;
+ _pos += len;
+ samples += len;
+ if (_pos >= _bufferEnd) {
+ refill();
+ }
+ }
+ return samples;
+}
+
+void RAWInputStream::refill() {
+ if (_finished)
+ return;
+
+ uint32 lengthLeft;
+ byte *ptr = (byte *) _buf;
+
+
+ _file->seek(_filePos, SEEK_SET);
+
+ if (_looping)
+ lengthLeft = 2 * BUFFER_SIZE;
+ else
+ lengthLeft = MIN((uint32) (2 * BUFFER_SIZE), _endPos - _filePos);
+
+ while (lengthLeft > 0) {
+ uint32 len = _file->read(ptr, MIN(lengthLeft, _endPos - _file->pos()));
+
+ if (len & 1)
+ len--;
+
+#ifdef SCUMM_BIG_ENDIAN
+ if (!_context->isBigEndian) {
+#else
+ if (_context->isBigEndian) {
+#endif
+ uint16 *ptr16 = (uint16 *)ptr;
+ for (uint32 i = 0; i < (len / 2); i++)
+ ptr16[i] = SWAP_BYTES_16(ptr16[i]);
+ }
+
+ lengthLeft -= len;
+ ptr += len;
+
+ if (lengthLeft > 0)
+ _file->seek(_startPos);
+ }
+
+ _filePos = _file->pos();
+ _pos = _buf;
+ _bufferEnd = (int16 *)ptr;
+
+ if (!_looping && _filePos >= _endPos) {
+ _finished = true;
+ }
+}
+
+
+MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false), _passThrough(false), _isGM(false) {
+ memset(_channel, 0, sizeof(_channel));
+ _masterVolume = 0;
+ this->open();
+}
+
+MusicPlayer::~MusicPlayer() {
+ _driver->setTimerCallback(NULL, NULL);
+ stopMusic();
+ this->close();
+}
+
+void MusicPlayer::setVolume(int volume) {
+ if (volume < 0)
+ volume = 0;
+ else if (volume > 255)
+ volume = 255;
+
+ if (_masterVolume == volume)
+ return;
+
+ _masterVolume = volume;
+
+ for (int i = 0; i < 16; ++i) {
+ if (_channel[i]) {
+ _channel[i]->volume(_channelVolume[i] * _masterVolume / 255);
+ }
+ }
+}
+
+int MusicPlayer::open() {
+ // Don't ever call open without first setting the output driver!
+ if (!_driver)
+ return 255;
+
+ int ret = _driver->open();
+ if (ret)
+ return ret;
+
+ _driver->setTimerCallback(this, &onTimer);
+ return 0;
+}
+
+void MusicPlayer::close() {
+ stopMusic();
+ if (_driver)
+ _driver->close();
+ _driver = 0;
+}
+
+void MusicPlayer::send(uint32 b) {
+ if (_passThrough) {
+ _driver->send(b);
+ return;
+ }
+
+ byte channel = (byte)(b & 0x0F);
+ if ((b & 0xFFF0) == 0x07B0) {
+ // Adjust volume changes by master volume
+ byte volume = (byte)((b >> 16) & 0x7F);
+ _channelVolume[channel] = volume;
+ volume = volume * _masterVolume / 255;
+ b = (b & 0xFF00FFFF) | (volume << 16);
+ } else if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
+ b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
+ }
+ else if ((b & 0xFFF0) == 0x007BB0) {
+ //Only respond to All Notes Off if this channel
+ //has currently been allocated
+ if (_channel[b & 0x0F])
+ return;
+ }
+
+ if (!_channel[channel])
+ _channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+
+ if (_channel[channel])
+ _channel[channel]->send(b);
+}
+
+void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
+ // FIXME: The "elkfanfare" is played much too quickly. There are some
+ // meta events that we don't handle. Perhaps there is a
+ // connection...?
+
+ switch (type) {
+ case 0x2F: // End of Track
+ if (_looping)
+ _parser->jumpToTick(0);
+ else
+ stopMusic();
+ break;
+ default:
+ //warning("Unhandled meta event: %02x", type);
+ break;
+ }
+}
+
+void MusicPlayer::onTimer(void *refCon) {
+ MusicPlayer *music = (MusicPlayer *)refCon;
+ if (music->_isPlaying)
+ music->_parser->onTimer();
+}
+
+void MusicPlayer::playMusic() {
+ _isPlaying = true;
+}
+
+void MusicPlayer::stopMusic() {
+ _isPlaying = false;
+ if (_parser) {
+ _parser->unloadMusic();
+ _parser = NULL;
+ }
+}
+
+Music::Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver, int enabled) : _vm(vm), _mixer(mixer), _enabled(enabled), _adlib(false) {
+ _player = new MusicPlayer(driver);
+ _currentVolume = 0;
+
+ xmidiParser = MidiParser::createParser_XMIDI();
+ smfParser = MidiParser::createParser_SMF();
+
+ _musicContext = _vm->_resource->getContext(GAME_MUSICFILE);
+
+ _songTableLen = 0;
+ _songTable = 0;
+
+ _track = NULL;
+}
+
+Music::~Music() {
+ _mixer->stopHandle(_musicHandle);
+ delete _player;
+ xmidiParser->setMidiDriver(NULL);
+ smfParser->setMidiDriver(NULL);
+ delete xmidiParser;
+ delete smfParser;
+
+ free(_songTable);
+}
+
+void Music::musicVolumeGaugeCallback(void *refCon) {
+ ((Music *)refCon)->musicVolumeGauge();
+}
+
+void Music::musicVolumeGauge() {
+ int volume;
+
+ _currentVolumePercent += 10;
+
+ if (_currentVolume - _targetVolume > 0) { // Volume decrease
+ volume = _targetVolume + (_currentVolume - _targetVolume) * (100 - _currentVolumePercent) / 100;
+ } else {
+ volume = _currentVolume + (_targetVolume - _currentVolume) * _currentVolumePercent / 100;
+ }
+
+ if (volume < 0)
+ volume = 1;
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
+ _player->setVolume(volume);
+
+ if (_currentVolumePercent == 100) {
+ Common::g_timer->removeTimerProc(&musicVolumeGaugeCallback);
+ _currentVolume = _targetVolume;
+ }
+}
+
+void Music::setVolume(int volume, int time) {
+ _targetVolume = volume * 2; // ScummVM has different volume scale
+ _currentVolumePercent = 0;
+
+ if (volume == -1) // Set Full volume
+ volume = 255;
+
+ if (time == 1) {
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
+ _player->setVolume(volume);
+ Common::g_timer->removeTimerProc(&musicVolumeGaugeCallback);
+ _currentVolume = volume;
+ return;
+ }
+
+ Common::g_timer->installTimerProc(&musicVolumeGaugeCallback, time * 100L, this);
+}
+
+bool Music::isPlaying() {
+ return _mixer->isSoundHandleActive(_musicHandle) || _player->isPlaying();
+}
+
+void Music::play(uint32 resourceId, MusicFlags flags) {
+ AudioStream *audioStream = NULL;
+ MidiParser *parser;
+ ResourceContext *context;
+ byte *resourceData;
+ size_t resourceSize;
+
+ debug(2, "Music::play %d, %d", resourceId, flags);
+
+ if (!_enabled) {
+ return;
+ }
+
+ if (isPlaying() && _trackNumber == resourceId) {
+ return;
+ }
+
+ _trackNumber = resourceId;
+ _player->stopMusic();
+ _mixer->stopHandle(_musicHandle);
+
+ int realTrackNumber;
+
+ if (_vm->getGameType() == GType_ITE) {
+ if (flags == MUSIC_DEFAULT) {
+ if (resourceId == 13 || resourceId == 19) {
+ flags = MUSIC_NORMAL;
+ } else {
+ flags = MUSIC_LOOP;
+ }
+ }
+ realTrackNumber = resourceId - 8;
+ } else {
+ realTrackNumber = resourceId + 1;
+ }
+
+ // Try to open standalone digital track
+ for (int i = 0; i < ARRAYSIZE(TRACK_FORMATS) - 1; ++i)
+ if ((_track = TRACK_FORMATS[i].openTrackFunction(realTrackNumber))) {
+ break;
+ }
+ if (_track) {
+ _track->play(_mixer, &_musicHandle, (flags == MUSIC_LOOP) ? -1 : 1, 10000);
+ return;
+ }
+
+ if (_vm->getGameType() == GType_ITE) {
+ if (resourceId >= 9 && resourceId <= 34) {
+ if (_musicContext != NULL) {
+ //TODO: check resource size
+ audioStream = new RAWInputStream(_vm, _musicContext, resourceId - 9, flags == MUSIC_LOOP);
+ }
+ }
+ }
+
+ if (audioStream) {
+ debug(2, "Playing digitized music");
+ _mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_musicHandle, audioStream);
+ return;
+ }
+
+ if (flags == MUSIC_DEFAULT) {
+ flags = MUSIC_NORMAL;
+ }
+
+ // FIXME: Is resource_data ever freed?
+ // Load MIDI/XMI resource data
+
+ if (_vm->getGameType() == GType_ITE) {
+ context = _vm->_resource->getContext(GAME_MUSICFILE_GM);
+ if (context == NULL) {
+ context = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ }
+ } else {
+ // I've listened to music from both the FM and the GM
+ // file, and I've tentatively reached the conclusion
+ // that they are both General MIDI. My guess is that
+ // the FM file has been reorchestrated to sound better
+ // on Adlib and other FM synths.
+ //
+ // Sev says the Adlib music does not sound like in the
+ // original, but I still think assuming General MIDI is
+ // the right thing to do. Some music, like the End
+ // Title (song 0) sound absolutely atrocious when piped
+ // through our MT-32 to GM mapping.
+ //
+ // It is, however, quite possible that the original
+ // used a different GM to FM mapping. If the original
+ // sounded markedly better, perhaps we should add some
+ // way of replacing our stock mapping in adlib.cpp?
+ //
+ // For the composer's own recording of the End Title,
+ // see http://www.johnottman.com/
+
+ // Oddly enough, the intro music (song 1) is very
+ // different in the two files. I have no idea why.
+
+ if (hasAdlib()) {
+ context = _vm->_resource->getContext(GAME_MUSICFILE_FM);
+ } else {
+ context = _vm->_resource->getContext(GAME_MUSICFILE_GM);
+ }
+ }
+
+ _player->setGM(true);
+
+ _vm->_resource->loadResource(context, resourceId, resourceData, resourceSize);
+
+ if (resourceSize < 4) {
+ error("Music::play() wrong music resource size");
+ }
+
+ if (xmidiParser->loadMusic(resourceData, resourceSize)) {
+ if (_vm->getGameType() == GType_ITE)
+ _player->setGM(false);
+
+ parser = xmidiParser;
+ } else {
+ if (smfParser->loadMusic(resourceData, resourceSize)) {
+ parser = smfParser;
+ } else {
+ error("Music::play() wrong music resource");
+ }
+ }
+
+ parser->setTrack(0);
+ parser->setMidiDriver(_player);
+ parser->setTimerRate(_player->getBaseTempo());
+ parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+
+ _player->_parser = parser;
+ _player->setVolume(_vm->_musicVolume == 10 ? 255 : _vm->_musicVolume * 25);
+
+ if (flags & MUSIC_LOOP)
+ _player->setLoop(true);
+ else
+ _player->setLoop(false);
+
+ _player->playMusic();
+}
+
+void Music::pause(void) {
+ //TODO: do it
+}
+
+void Music::resume(void) {
+ //TODO: do it}
+}
+
+void Music::stop(void) {
+ //TODO: do it
+}
+
+} // End of namespace Saga
+
diff --git a/engines/saga/music.h b/engines/saga/music.h
new file mode 100644
index 0000000000..377a5bf1d8
--- /dev/null
+++ b/engines/saga/music.h
@@ -0,0 +1,148 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Music class
+
+#ifndef SAGA_MUSIC_H_
+#define SAGA_MUSIC_H_
+
+#include "sound/audiocd.h"
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+
+namespace Saga {
+
+enum MusicFlags {
+ MUSIC_NORMAL = 0,
+ MUSIC_LOOP = 0x0001,
+ MUSIC_DEFAULT = 0xffff
+};
+
+class MusicPlayer : public MidiDriver {
+public:
+ MusicPlayer(MidiDriver *driver);
+ ~MusicPlayer();
+
+ bool isPlaying() { return _isPlaying; }
+
+ void setVolume(int volume);
+ int getVolume() { return _masterVolume; }
+
+ void setNativeMT32(bool b) { _nativeMT32 = b; }
+ bool hasNativeMT32() { return _nativeMT32; }
+ void playMusic();
+ void stopMusic();
+ void setLoop(bool loop) { _looping = loop; }
+ void setPassThrough(bool b) { _passThrough = b; }
+
+ void setGM(bool isGM) { _isGM = isGM; }
+
+ //MidiDriver interface implementation
+ int open();
+ void close();
+ void send(uint32 b);
+
+ void metaEvent(byte type, byte *data, uint16 length);
+
+ void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
+ uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; }
+
+ //Channel allocation functions
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+ MidiParser *_parser;
+
+protected:
+
+ static void onTimer(void *data);
+
+ MidiChannel *_channel[16];
+ MidiDriver *_driver;
+ byte _channelVolume[16];
+ bool _nativeMT32;
+ bool _isGM;
+ bool _passThrough;
+
+ bool _isPlaying;
+ bool _looping;
+ bool _randomLoop;
+ byte _masterVolume;
+
+ byte *_musicData;
+ uint16 *_buf;
+ uint32 _musicDataSize;
+};
+
+class Music {
+public:
+
+ Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver, int enabled);
+ ~Music(void);
+ void setNativeMT32(bool b) { _player->setNativeMT32(b); }
+ bool hasNativeMT32() { return _player->hasNativeMT32(); }
+ void setAdlib(bool b) { _adlib = b; }
+ bool hasAdlib() { return _adlib; }
+ void setPassThrough(bool b) { _player->setPassThrough(b); }
+ bool isPlaying(void);
+
+ void play(uint32 resourceId, MusicFlags flags = MUSIC_DEFAULT);
+ void pause(void);
+ void resume(void);
+ void stop(void);
+
+ void setVolume(int volume, int time = 1);
+ int getVolume() { return _currentVolume; }
+
+ int32 *_songTable;
+ int _songTableLen;
+
+private:
+ SagaEngine *_vm;
+ Audio::Mixer *_mixer;
+
+ MusicPlayer *_player;
+ Audio::SoundHandle _musicHandle;
+ uint32 _trackNumber;
+
+ int _enabled;
+ bool _adlib;
+
+ int _targetVolume;
+ int _currentVolume;
+ int _currentVolumePercent;
+
+ ResourceContext *_musicContext;
+ MidiParser *xmidiParser;
+ MidiParser *smfParser;
+
+ DigitalTrackInfo *_track;
+
+ static void musicVolumeGaugeCallback(void *refCon);
+ void musicVolumeGauge(void);
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/objectmap.cpp b/engines/saga/objectmap.cpp
new file mode 100644
index 0000000000..d0a302b0d7
--- /dev/null
+++ b/engines/saga/objectmap.cpp
@@ -0,0 +1,267 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Object map / Object click-area module
+
+// Polygon Hit Test code ( HitTestPoly() ) adapted from code (C) Eric Haines
+// appearing in Graphics Gems IV, "Point in Polygon Strategies."
+// p. 24-46, code: p. 34-45
+#include "saga/saga.h"
+
+#include "saga/gfx.h"
+#include "saga/console.h"
+#include "saga/font.h"
+#include "saga/interface.h"
+#include "saga/objectmap.h"
+#include "saga/stream.h"
+#include "saga/actor.h"
+#include "saga/scene.h"
+#include "saga/isomap.h"
+
+namespace Saga {
+
+HitZone::HitZone(MemoryReadStreamEndian *readStream, int index): _index(index) {
+ int i, j;
+ HitZone::ClickArea *clickArea;
+ Point *point;
+
+ _flags = readStream->readByte();
+ _clickAreasCount = readStream->readByte();
+ _rightButtonVerb = readStream->readByte();
+ readStream->readByte(); // pad
+ _nameIndex = readStream->readUint16();
+ _scriptNumber = readStream->readUint16();
+
+ _clickAreas = (HitZone::ClickArea *)malloc(_clickAreasCount * sizeof(*_clickAreas));
+
+ if (_clickAreas == NULL) {
+ memoryError("HitZone::HitZone");
+ }
+
+ for (i = 0; i < _clickAreasCount; i++) {
+ clickArea = &_clickAreas[i];
+ clickArea->pointsCount = readStream->readUint16LE();
+
+ assert(clickArea->pointsCount);
+
+ clickArea->points = (Point *)malloc(clickArea->pointsCount * sizeof(*(clickArea->points)));
+ if (clickArea->points == NULL) {
+ memoryError("HitZone::HitZone");
+ }
+
+ for (j = 0; j < clickArea->pointsCount; j++) {
+ point = &clickArea->points[j];
+ point->x = readStream->readSint16();
+ point->y = readStream->readSint16();
+ }
+ }
+}
+
+HitZone::~HitZone() {
+ for (int i = 0; i < _clickAreasCount; i++) {
+ free(_clickAreas[i].points);
+ }
+ free(_clickAreas);
+}
+
+bool HitZone::getSpecialPoint(Point &specialPoint) const {
+ int i, pointsCount;
+ HitZone::ClickArea *clickArea;
+ Point *points;
+
+ for (i = 0; i < _clickAreasCount; i++) {
+ clickArea = &_clickAreas[i];
+ pointsCount = clickArea->pointsCount;
+ points = clickArea->points;
+ if (pointsCount == 1) {
+ specialPoint = points[0];
+ return true;
+ }
+ }
+ return false;
+}
+bool HitZone::hitTest(const Point &testPoint) {
+ int i, pointsCount;
+ HitZone::ClickArea *clickArea;
+ Point *points;
+
+ if (_flags & kHitZoneEnabled) {
+ for (i = 0; i < _clickAreasCount; i++) {
+ clickArea = &_clickAreas[i];
+ pointsCount = clickArea->pointsCount;
+ points = clickArea->points;
+
+ if (pointsCount == 2) {
+ // Hit-test a box region
+ if ((testPoint.x >= points[0].x) &&
+ (testPoint.x <= points[1].x) &&
+ (testPoint.y >= points[0].y) &&
+ (testPoint.y <= points[1].y)) {
+ return true;
+ }
+ } else {
+ if (pointsCount > 2) {
+ // Hit-test a polygon
+ if (hitTestPoly(points, pointsCount, testPoint)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void HitZone::draw(SagaEngine *vm, Surface *ds, int color) {
+ int i, pointsCount, j;
+ Location location;
+ HitZone::ClickArea *clickArea;
+ Point *points;
+ for (i = 0; i < _clickAreasCount; i++) {
+ clickArea = &_clickAreas[i];
+ pointsCount = clickArea->pointsCount;
+ if (vm->_scene->getFlags() & kSceneFlagISO) {
+ points = (Point*)malloc(sizeof(Point) * pointsCount);
+ for (j = 0; j < pointsCount; j++) {
+ location.u() = clickArea->points[j].x;
+ location.v() = clickArea->points[j].y;
+ location.z = 0;
+ vm->_isoMap->tileCoordsToScreenPoint(location, points[j]);
+ }
+ } else {
+ points = clickArea->points;
+ }
+
+ if (pointsCount == 2) {
+ // 2 points represent a box
+ ds->drawFrame(points[0], points[1], color);
+ } else {
+ if (pointsCount > 2) {
+ // Otherwise draw a polyline
+ ds->drawPolyLine(points, pointsCount, color);
+ }
+ }
+ if (vm->_scene->getFlags() & kSceneFlagISO) {
+ free(points);
+ }
+
+ }
+}
+
+
+// Loads an object map resource ( objects ( clickareas ( points ) ) )
+void ObjectMap::load(const byte *resourcePointer, size_t resourceLength) {
+ int i;
+
+ if (resourceLength == 0) {
+ return;
+ }
+
+ if (resourceLength < 4) {
+ error("ObjectMap::load wrong resourceLength");
+ }
+
+ MemoryReadStreamEndian readS(resourcePointer, resourceLength, _vm->isBigEndian());
+
+ _hitZoneListCount = readS.readSint16();
+ if (_hitZoneListCount < 0) {
+ error("ObjectMap::load _hitZoneListCount < 0");
+ }
+
+ if (_hitZoneList)
+ error("ObjectMap::load _hitZoneList != NULL");
+
+ _hitZoneList = (HitZone **) malloc(_hitZoneListCount * sizeof(HitZone *));
+ if (_hitZoneList == NULL) {
+ memoryError("ObjectMap::load");
+ }
+
+ for (i = 0; i < _hitZoneListCount; i++) {
+ _hitZoneList[i] = new HitZone(&readS, i);
+ }
+}
+
+void ObjectMap::freeMem() {
+ int i;
+
+ if (_hitZoneList) {
+ for (i = 0; i < _hitZoneListCount; i++) {
+ delete _hitZoneList[i];
+ }
+
+ free(_hitZoneList);
+ _hitZoneList = NULL;
+ }
+ _hitZoneListCount = 0;
+}
+
+
+
+void ObjectMap::draw(Surface *ds, const Point& testPoint, int color, int color2) {
+ int i;
+ int hitZoneIndex;
+ char txtBuf[32];
+ Point pickPoint;
+ Point textPoint;
+ Location pickLocation;
+ pickPoint = testPoint;
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ assert(_vm->_actor->_protagonist);
+ pickPoint.y -= _vm->_actor->_protagonist->_location.z;
+ _vm->_isoMap->screenPointToTileCoords(pickPoint, pickLocation);
+ pickLocation.toScreenPointUV(pickPoint);
+ }
+
+ hitZoneIndex = hitTest(pickPoint);
+
+ for (i = 0; i < _hitZoneListCount; i++) {
+ _hitZoneList[i]->draw(_vm, ds, (hitZoneIndex == i) ? color2 : color);
+ }
+
+ if (hitZoneIndex != -1) {
+ snprintf(txtBuf, sizeof(txtBuf), "hitZone %d", hitZoneIndex);
+ textPoint.x = 2;
+ textPoint.y = 2;
+ _vm->_font->textDraw(kKnownFontSmall, ds, txtBuf, textPoint, kITEColorBrightWhite, kITEColorBlack, kFontOutline);
+ }
+}
+
+int ObjectMap::hitTest(const Point& testPoint) {
+ int i;
+
+ // Loop through all scene objects
+ for (i = 0; i < _hitZoneListCount; i++) {
+ if (_hitZoneList[i]->hitTest(testPoint)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void ObjectMap::cmdInfo(void) {
+ _vm->_console->DebugPrintf("%d zone(s) loaded.\n\n", _hitZoneListCount);
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/objectmap.h b/engines/saga/objectmap.h
new file mode 100644
index 0000000000..43356d3579
--- /dev/null
+++ b/engines/saga/objectmap.h
@@ -0,0 +1,128 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Object map / Object click-area module header file
+
+#ifndef SAGA_OBJECTMAP_H_
+#define SAGA_OBJECTMAP_H_
+
+#include "saga/stream.h"
+
+namespace Saga {
+
+
+class HitZone {
+private:
+ struct ClickArea {
+ int pointsCount;
+ Point *points;
+ };
+
+public:
+ HitZone(MemoryReadStreamEndian *readStream, int index);
+ ~HitZone();
+
+ int getNameIndex() const {
+ return _nameIndex;
+ }
+ int getSceneNumber() const {
+ return _nameIndex;
+ }
+ int getActorsEntrance() const {
+ return _scriptNumber;
+ }
+ int getScriptNumber() const {
+ return _scriptNumber;
+ }
+ int getRightButtonVerb() const {
+ return _rightButtonVerb;
+ }
+ int getFlags() const {
+ return _flags;
+ }
+ void setFlag(HitZoneFlags flag) {
+ _flags |= flag;
+ }
+ void clearFlag(HitZoneFlags flag) {
+ _flags &= ~flag;
+ }
+ int getDirection() const {
+ return ((_flags >> 4) & 0xF);
+ }
+ uint16 getHitZoneId() const {
+ return objectIndexToId(kGameObjectHitZone, _index);
+ }
+ uint16 getStepZoneId() const {
+ return objectIndexToId(kGameObjectStepZone, _index);
+ }
+ bool getSpecialPoint(Point &specialPoint) const;
+ void draw(SagaEngine *vm, Surface *ds, int color);
+ bool hitTest(const Point &testPoint);
+
+private:
+ int _flags; // Saga::HitZoneFlags
+ int _clickAreasCount;
+ int _rightButtonVerb;
+ int _nameIndex;
+ int _scriptNumber;
+ int _index;
+
+ ClickArea *_clickAreas;
+};
+
+
+class ObjectMap {
+public:
+ ObjectMap(SagaEngine *vm) : _vm(vm) {
+ _hitZoneList = NULL;
+ _hitZoneListCount = 0;
+
+ }
+ ~ObjectMap(void) {
+ freeMem();
+ }
+ void load(const byte *resourcePointer, size_t resourceLength);
+ void freeMem(void);
+
+ void draw(Surface *drawSurface, const Point& testPoint, int color, int color2);
+ int hitTest(const Point& testPoint);
+ HitZone *getHitZone(int16 index) {
+ if ((index < 0) || (index >= _hitZoneListCount)) {
+ error("ObjectMap::getHitZone wrong index 0x%X", index);
+ }
+ return _hitZoneList[index];
+ }
+
+ void cmdInfo(void);
+
+private:
+ SagaEngine *_vm;
+
+ int _hitZoneListCount;
+ HitZone **_hitZoneList;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/palanim.cpp b/engines/saga/palanim.cpp
new file mode 100644
index 0000000000..4cfaf5e0fd
--- /dev/null
+++ b/engines/saga/palanim.cpp
@@ -0,0 +1,209 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Palette animation module
+#include "saga/saga.h"
+#include "saga/gfx.h"
+
+#include "saga/events.h"
+
+#include "saga/palanim.h"
+#include "saga/stream.h"
+
+namespace Saga {
+
+PalAnim::PalAnim(SagaEngine *vm) : _vm(vm) {
+ _loaded = false;
+ _entryCount = 0;
+ _entries = NULL;
+}
+
+PalAnim::~PalAnim(void) {
+}
+
+int PalAnim::loadPalAnim(const byte *resdata, size_t resdata_len) {
+ void *test_p;
+
+ uint16 i;
+
+ if (_loaded) {
+ freePalAnim();
+ }
+
+ if (resdata == NULL) {
+ return FAILURE;
+ }
+
+ MemoryReadStreamEndian readS(resdata, resdata_len, _vm->isBigEndian());
+
+ if (_vm->getGameType() == GType_IHNM) {
+ return SUCCESS;
+ }
+
+ _entryCount = readS.readUint16();
+
+ debug(3, "PalAnim::loadPalAnim(): Loading %d PALANIM entries.", _entryCount);
+
+ test_p = calloc(_entryCount, sizeof(PalanimEntry));
+ if (test_p == NULL) {
+ warning("PalAnim::loadPalAnim(): Allocation failure");
+ return MEM;
+ }
+
+ _entries = (PalanimEntry *)test_p;
+
+ for (i = 0; i < _entryCount; i++) {
+ int color_count;
+ int pal_count;
+ int p, c;
+
+ color_count = readS.readUint16();
+ pal_count = readS.readUint16();
+
+ _entries[i].pal_count = pal_count;
+ _entries[i].color_count = color_count;
+
+ debug(2, "PalAnim::loadPalAnim(): Entry %d: Loading %d palette indices.\n", i, pal_count);
+
+ test_p = calloc(1, sizeof(char) * pal_count);
+ if (test_p == NULL) {
+ warning("PalAnim::loadPalAnim(): Allocation failure");
+ return MEM;
+ }
+
+ _entries[i].pal_index = (byte *)test_p;
+
+ debug(2, "PalAnim::loadPalAnim(): Entry %d: Loading %d SAGA_COLOR structures.", i, color_count);
+
+ test_p = calloc(1, sizeof(Color) * color_count);
+ if (test_p == NULL) {
+ warning("PalAnim::loadPalAnim(): Allocation failure");
+ return MEM;
+ }
+
+ _entries[i].colors = (Color *)test_p;
+
+ for (p = 0; p < pal_count; p++) {
+ _entries[i].pal_index[p] = readS.readByte();
+ }
+
+ for (c = 0; c < color_count; c++) {
+ _entries[i].colors[c].red = readS.readByte();
+ _entries[i].colors[c].green = readS.readByte();
+ _entries[i].colors[c].blue = readS.readByte();
+ }
+ }
+
+ _loaded = true;
+ return SUCCESS;
+}
+
+int PalAnim::cycleStart() {
+ Event event;
+
+ if (!_loaded) {
+ return FAILURE;
+ }
+
+ event.type = kEvTOneshot;
+ event.code = kPalAnimEvent;
+ event.op = kEventCycleStep;
+ event.time = PALANIM_CYCLETIME;
+
+ _vm->_events->queue(&event);
+
+ return SUCCESS;
+}
+
+int PalAnim::cycleStep(int vectortime) {
+ static PalEntry pal[256];
+ uint16 pal_index;
+ uint16 col_index;
+
+ uint16 i, j;
+ uint16 cycle;
+ uint16 cycle_limit;
+
+ Event event;
+
+ if (!_loaded) {
+ return FAILURE;
+ }
+
+ _vm->_gfx->getCurrentPal(pal);
+
+ for (i = 0; i < _entryCount; i++) {
+ cycle = _entries[i].cycle;
+ cycle_limit = _entries[i].color_count;
+ for (j = 0; j < _entries[i].pal_count; j++) {
+ pal_index = (unsigned char)_entries[i].pal_index[j];
+ col_index = (cycle + j) % cycle_limit;
+ pal[pal_index].red = (byte) _entries[i].colors[col_index].red;
+ pal[pal_index].green = (byte) _entries[i].colors[col_index].green;
+ pal[pal_index].blue = (byte) _entries[i].colors[col_index].blue;
+ }
+
+ _entries[i].cycle++;
+
+ if (_entries[i].cycle == cycle_limit) {
+ _entries[i].cycle = 0;
+ }
+ }
+
+ _vm->_gfx->setPalette(pal);
+
+ event.type = kEvTOneshot;
+ event.code = kPalAnimEvent;
+ event.op = kEventCycleStep;
+ event.time = vectortime + PALANIM_CYCLETIME;
+
+ _vm->_events->queue(&event);
+
+ return SUCCESS;
+}
+
+int PalAnim::freePalAnim() {
+ uint16 i;
+
+ if (!_loaded) {
+ return FAILURE;
+ }
+
+ for (i = 0; i < _entryCount; i++) {
+ debug(2, "PalAnim::freePalAnim(): Entry %d: Freeing colors.", i);
+ free(_entries[i].colors);
+ debug(2, "PalAnim::freePalAnim(): Entry %d: Freeing indices.", i);
+ free(_entries[i].pal_index);
+ }
+
+ debug(3, "PalAnim::freePalAnim(): Freeing entries.");
+
+ free(_entries);
+
+ _loaded = false;
+
+ return SUCCESS;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/palanim.h b/engines/saga/palanim.h
new file mode 100644
index 0000000000..e2ca695eb6
--- /dev/null
+++ b/engines/saga/palanim.h
@@ -0,0 +1,63 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Palette animation module header file
+
+#ifndef SAGA_PALANIM_H
+#define SAGA_PALANIM_H
+
+namespace Saga {
+
+#define PALANIM_CYCLETIME 100
+
+struct PalanimEntry {
+ uint16 pal_count;
+ uint16 color_count;
+ uint16 cycle;
+ byte *pal_index;
+ Color *colors;
+};
+
+class PalAnim {
+ public:
+ PalAnim(SagaEngine *vm);
+ ~PalAnim(void);
+
+ int loadPalAnim(const byte *, size_t);
+ int cycleStart();
+ int cycleStep(int vectortime);
+ int freePalAnim();
+
+ private:
+ SagaEngine *_vm;
+
+ bool _loaded;
+ uint16 _entryCount;
+ PalanimEntry *_entries;
+};
+
+} // End of namespace Saga
+
+#endif
+
diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp
new file mode 100644
index 0000000000..c09a9c0462
--- /dev/null
+++ b/engines/saga/puzzle.cpp
@@ -0,0 +1,560 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "saga/saga.h"
+
+#include "saga/actor.h"
+#include "saga/interface.h"
+#include "saga/scene.h"
+#include "saga/sprite.h"
+#include "saga/puzzle.h"
+#include "saga/render.h"
+#include "saga/resnames.h"
+
+#include "common/system.h"
+#include "common/timer.h"
+
+namespace Saga {
+
+#define PUZZLE_X_OFFSET 72
+#define PUZZLE_Y_OFFSET 46
+
+#define PUZZLE_FIT 0x01 // 1 when in correct position
+#define PUZZLE_MOVED 0x04 // 1 when somewhere in the box
+#define PUZZLE_ALL_SET PUZZLE_FIT | PUZZLE_MOVED
+
+
+enum rifOptions {
+ kROLater = 0,
+ kROAccept = 1,
+ kRODecline = 2,
+ kROHint = 3
+};
+
+Puzzle::Puzzle(SagaEngine *vm) : _vm(vm), _solved(false), _active(false) {
+ _lang = (_vm->getLanguage() == Common::DE_DEU) ? 1 : 0;
+
+ _hintRqState = kRQNoHint;
+ _hintOffer = 0;
+ _hintCount = 0;
+ _helpCount = 0;
+ _puzzlePiece = -1;
+ _newPuzzle = true;
+ _sliding = false;
+ _hintBox.left = 70;
+ _hintBox.top = 105;
+ _hintBox.setWidth(240);
+ _hintBox.setHeight(30);
+
+ initPieceInfo( 0, 268, 18, 0, 0, 0 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 3,
+ Point(0, 1), Point(0, 62), Point(15, 31), Point(0, 0), Point(0, 0), Point(0,0));
+ initPieceInfo( 1, 270, 52, 0, 0, 0 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 4,
+ Point(0, 31), Point(0, 47), Point(39, 47), Point(15, 1), Point(0, 0), Point(0, 0));
+ initPieceInfo( 2, 19, 51, 0, 0, 0 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4,
+ Point(0, 0), Point(23, 46), Point(39, 15), Point(31, 0), Point(0, 0), Point(0, 0));
+ initPieceInfo( 3, 73, 0, 0, 0, 32 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 6,
+ Point(0, 0), Point(8, 16), Point(0, 31), Point(31, 31), Point(39, 15), Point(31, 0));
+ initPieceInfo( 4, 0, 35, 0, 0, 64 + PUZZLE_X_OFFSET, 16 + PUZZLE_Y_OFFSET, 0, 4,
+ Point(0, 15), Point(15, 46), Point(23, 32), Point(7, 1), Point(0, 0), Point(0, 0));
+ initPieceInfo( 5, 215, 0, 0, 0, 24 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 6,
+ Point(0, 15), Point(8, 31), Point(39, 31), Point(47, 16), Point(39, 0), Point(8, 0));
+ initPieceInfo( 6, 159, 0, 0, 0, 32 + PUZZLE_X_OFFSET, 48 + PUZZLE_Y_OFFSET, 0, 5,
+ Point(0, 16), Point(8, 31), Point(55, 31), Point(39, 1), Point(32, 15), Point(0, 0));
+ initPieceInfo( 7, 9, 70, 0, 0, 80 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 5,
+ Point(0, 31), Point(8, 47), Point(23, 47), Point(31, 31), Point(15, 1), Point(0, 0));
+ initPieceInfo( 8, 288, 18, 0, 0, 96 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4,
+ Point(0, 31), Point(15, 62), Point(31, 32), Point(15, 1), Point(0, 0), Point(0, 0));
+ initPieceInfo( 9, 112, 0, 0, 0, 112 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4,
+ Point(0, 0), Point(16, 31), Point(47, 31), Point(31, 0), Point(0, 0), Point(0, 0));
+ initPieceInfo(10, 27, 89, 0, 0, 104 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 4,
+ Point(0, 47), Point(31, 47), Point(31, 0), Point(24, 0), Point(0, 0), Point(0, 0));
+ initPieceInfo(11, 43, 0, 0, 0, 136 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 6,
+ Point(0, 0), Point(0, 47), Point(15, 47), Point(15, 15), Point(31, 15), Point(23, 0));
+ initPieceInfo(12, 0, 0, 0, 0, 144 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 4,
+ Point(0, 0), Point(24, 47), Point(39, 47), Point(39, 0), Point(0, 0), Point(0, 0));
+ initPieceInfo(13, 262, 0, 0, 0, 64 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 3,
+ Point(0, 0), Point(23, 46), Point(47, 0), Point(0, 0), Point(0, 0), Point(0, 0));
+ initPieceInfo(14, 271, 103, 0, 0, 152 + PUZZLE_X_OFFSET, 48 + PUZZLE_Y_OFFSET, 0, 4,
+ Point(0, 0), Point(0, 31), Point(31, 31), Point(31, 0), Point(0, 0), Point(0, 0));
+}
+
+void Puzzle::initPieceInfo(int i, int16 curX, int16 curY, byte offX, byte offY, int16 trgX,
+ int16 trgY, uint8 flag, uint8 count, Point point0, Point point1,
+ Point point2, Point point3, Point point4, Point point5) {
+ _pieceInfo[i].curX = curX;
+ _pieceInfo[i].curY = curY;
+ _pieceInfo[i].offX = offX;
+ _pieceInfo[i].offY = offY;
+ _pieceInfo[i].trgX = trgX;
+ _pieceInfo[i].trgY = trgY;
+ _pieceInfo[i].flag = flag;
+ _pieceInfo[i].count = count;
+ _pieceInfo[i].point[0] = point0;
+ _pieceInfo[i].point[1] = point1;
+ _pieceInfo[i].point[2] = point2;
+ _pieceInfo[i].point[3] = point3;
+ _pieceInfo[i].point[4] = point4;
+ _pieceInfo[i].point[5] = point5;
+}
+
+
+void Puzzle::execute(void) {
+ _active = true;
+ Common::g_timer->installTimerProc(&hintTimerCallback, kPuzzleHintTime, this);
+
+ initPieces();
+
+ showPieces();
+
+ _vm->_interface->setMode(kPanelConverse);
+ clearHint();
+ //_solved = true; // Cheat
+ //exitPuzzle();
+}
+
+void Puzzle::exitPuzzle(void) {
+ _active = false;
+
+ Common::g_timer->removeTimerProc(&hintTimerCallback);
+
+ _vm->_scene->changeScene(ITE_SCENE_LODGE, 0, kTransitionNoFade);
+ _vm->_interface->setMode(kPanelMain);
+}
+
+void Puzzle::initPieces(void) {
+ SpriteInfo *spI;
+ ActorData *puzzle = _vm->_actor->getActor(_vm->_actor->actorIndexToId(ITE_ACTOR_PUZZLE));
+ int frameNumber;
+ SpriteList *spriteList;
+ _vm->_actor->getSpriteParams(puzzle, frameNumber, spriteList);
+
+ for (int i = 0; i < PUZZLE_PIECES; i++) {
+ spI = &(spriteList->infoList[i]);
+ _pieceInfo[i].offX = (byte)(spI->width >> 1);
+ _pieceInfo[i].offY = (byte)(spI->height >> 1);
+
+ if (_newPuzzle) {
+ _pieceInfo[i].curX = pieceOrigins[i].x;
+ _pieceInfo[i].curY = pieceOrigins[i].y;
+ }
+ _piecePriority[i] = i;
+ }
+ _newPuzzle = false;
+}
+
+void Puzzle::showPieces(void) {
+ ActorData *puzzle = _vm->_actor->getActor(_vm->_actor->actorIndexToId(ITE_ACTOR_PUZZLE));
+ int frameNumber;
+ SpriteList *spriteList;
+ Surface *backBuffer = _vm->_gfx->getBackBuffer();
+ _vm->_actor->getSpriteParams(puzzle, frameNumber, spriteList);
+
+ for (int j = PUZZLE_PIECES - 1 ; j >= 0; j--) {
+ int num = _piecePriority[j];
+
+ if (_puzzlePiece != num) {
+ _vm->_sprite->draw(backBuffer, _vm->getDisplayClip(), *spriteList, num, Point(_pieceInfo[num].curX, _pieceInfo[num].curY), 256);
+ }
+ }
+}
+
+void Puzzle::drawCurrentPiece() {
+ ActorData *puzzle = _vm->_actor->getActor(_vm->_actor->actorIndexToId(ITE_ACTOR_PUZZLE));
+ Surface *backBuffer = _vm->_gfx->getBackBuffer();
+ int frameNumber;
+ SpriteList *spriteList;
+ _vm->_actor->getSpriteParams(puzzle, frameNumber, spriteList);
+
+ _vm->_sprite->draw(backBuffer, _vm->_scene->getSceneClip(), *spriteList, _puzzlePiece,
+ Point(_pieceInfo[_puzzlePiece].curX, _pieceInfo[_puzzlePiece].curY), 256);
+}
+
+void Puzzle::movePiece(Point mousePt) {
+ int newx, newy;
+
+ showPieces();
+
+ if (_puzzlePiece == -1)
+ return;
+
+ if (_sliding) {
+ newx = _slidePointX;
+ newy = _slidePointY;
+ } else {
+ if (mousePt.y >= 137)
+ return;
+
+ newx = mousePt.x;
+ newy = mousePt.y;
+ }
+
+ newx -= _pieceInfo[_puzzlePiece].offX;
+ newy -= _pieceInfo[_puzzlePiece].offY;
+
+ _pieceInfo[_puzzlePiece].curX = newx;
+ _pieceInfo[_puzzlePiece].curY = newy;
+
+ drawCurrentPiece();
+}
+
+void Puzzle::handleClick(Point mousePt) {
+ if (_puzzlePiece != -1) {
+ dropPiece(mousePt);
+
+ if (!_active)
+ return; // we won
+
+ drawCurrentPiece();
+ _puzzlePiece = -1;
+
+ return;
+ }
+
+ for (int j = 0; j < PUZZLE_PIECES; j++) {
+ int i = _piecePriority[j];
+ int adjX = mousePt.x - _pieceInfo[i].curX;
+ int adjY = mousePt.y - _pieceInfo[i].curY;
+
+ if (hitTestPoly(&_pieceInfo[i].point[0], _pieceInfo[i].count, Point(adjX, adjY))) {
+ _puzzlePiece = i;
+ break;
+ }
+ }
+
+ if (_puzzlePiece == -1)
+ return;
+
+ alterPiecePriority();
+
+ // Display scene background
+ _vm->_scene->draw();
+ showPieces();
+
+ int newx = mousePt.x - _pieceInfo[_puzzlePiece].offX;
+ int newy = mousePt.y - _pieceInfo[_puzzlePiece].offY;
+
+ if (newx != _pieceInfo[_puzzlePiece].curX
+ || newy != _pieceInfo[_puzzlePiece].curY) {
+ _pieceInfo[_puzzlePiece].curX = newx;
+ _pieceInfo[_puzzlePiece].curY = newy;
+ }
+ _vm->_interface->setStatusText(pieceNames[_lang][_puzzlePiece]);
+}
+
+void Puzzle::alterPiecePriority(void) {
+ for (int i = 1; i < PUZZLE_PIECES; i++) {
+ if (_puzzlePiece == _piecePriority[i]) {
+ for (int j = i - 1; j >= 0; j--)
+ _piecePriority[j+1] = _piecePriority[j];
+ _piecePriority[0] = _puzzlePiece;
+ break;
+ }
+ }
+}
+
+void Puzzle::slidePiece(int x1, int y1, int x2, int y2) {
+ int count;
+ Point slidePoints[320];
+
+ x1 += _pieceInfo[_puzzlePiece].offX;
+ y1 += _pieceInfo[_puzzlePiece].offY;
+
+ count = pathLine(&slidePoints[0], Point(x1, y1),
+ Point(x2 + _pieceInfo[_puzzlePiece].offX, y2 + _pieceInfo[_puzzlePiece].offY));
+
+ if (count > 1) {
+ int factor = count / 4;
+ _sliding = true;
+
+ if (!factor)
+ factor++;
+
+ for (int i = 1; i < count; i += factor) {
+ _slidePointX = slidePoints[i].x;
+ _slidePointY = slidePoints[i].y;
+ _vm->_render->drawScene();
+ _vm->_system->delayMillis(10);
+ }
+ _sliding = false;
+ }
+
+ _pieceInfo[_puzzlePiece].curX = x2;
+ _pieceInfo[_puzzlePiece].curY = y2;
+}
+
+void Puzzle::dropPiece(Point mousePt) {
+ int boxx = PUZZLE_X_OFFSET;
+ int boxy = PUZZLE_Y_OFFSET;
+ int boxw = boxx + 184;
+ int boxh = boxy + 80;
+
+ // if the center is within the box quantize within
+ // else move it back to its original start point
+ if (mousePt.x >= boxx && mousePt.x < boxw && mousePt.y >= boxy && mousePt.y <= boxh) {
+ ActorData *puzzle = _vm->_actor->getActor(_vm->_actor->actorIndexToId(ITE_ACTOR_PUZZLE));
+ SpriteInfo *spI;
+ int frameNumber;
+ SpriteList *spriteList;
+ _vm->_actor->getSpriteParams(puzzle, frameNumber, spriteList);
+
+ int newx = mousePt.x - _pieceInfo[_puzzlePiece].offX;
+ int newy = mousePt.y - _pieceInfo[_puzzlePiece].offY;
+
+ if (newx < boxx)
+ newx = PUZZLE_X_OFFSET;
+ if (newy < boxy)
+ newy = PUZZLE_Y_OFFSET;
+
+ spI = &(spriteList->infoList[_puzzlePiece]);
+
+ if (newx + spI->width > boxw)
+ newx = boxw - spI->width ;
+ if (newy + spI->height > boxh)
+ newy = boxh - spI->height ;
+
+ int x1 = ((newx - PUZZLE_X_OFFSET) & ~7) + PUZZLE_X_OFFSET;
+ int y1 = ((newy - PUZZLE_Y_OFFSET) & ~7) + PUZZLE_Y_OFFSET;
+ int x2 = x1 + 8;
+ int y2 = y1 + 8;
+ newx = (x2 - newx < newx - x1) ? x2 : x1;
+ newy = (y2 - newy < newy - y1) ? y2 : y1;
+
+ // if any part of the puzzle piece falls outside the box
+ // force it back in
+
+ // is the piece at the target location
+ if (newx == _pieceInfo[_puzzlePiece].trgX
+ && newy == _pieceInfo[_puzzlePiece].trgY) {
+ _pieceInfo[_puzzlePiece].flag |= (PUZZLE_MOVED | PUZZLE_FIT);
+ } else {
+ _pieceInfo[_puzzlePiece].flag &= ~PUZZLE_FIT;
+ _pieceInfo[_puzzlePiece].flag |= PUZZLE_MOVED;
+ }
+ _pieceInfo[_puzzlePiece].curX = newx;
+ _pieceInfo[_puzzlePiece].curY = newy;
+ } else {
+ int newx = pieceOrigins[_puzzlePiece].x;
+ int newy = pieceOrigins[_puzzlePiece].y;
+ _pieceInfo[_puzzlePiece].flag &= ~(PUZZLE_FIT | PUZZLE_MOVED);
+
+ // slide piece from current position to new position
+ slidePiece(_pieceInfo[_puzzlePiece].curX, _pieceInfo[_puzzlePiece].curY,
+ newx, newy);
+ }
+
+ // is the puzzle completed?
+
+ _solved = true;
+ for (int i = 0; i < PUZZLE_PIECES; i++)
+ if ((_pieceInfo[i].flag & PUZZLE_FIT) == 0) {
+ _solved = false;
+ break;
+ }
+
+ if (_solved)
+ exitPuzzle();
+}
+
+void Puzzle::hintTimerCallback(void *refCon) {
+ ((Puzzle *)refCon)->solicitHint();
+}
+
+void Puzzle::solicitHint(void) {
+ int i;
+
+ _vm->_actor->setSpeechColor(1, kITEColorBlack);
+
+ Common::g_timer->removeTimerProc(&hintTimerCallback);
+
+ switch (_hintRqState) {
+ case kRQSpeaking:
+ if (_vm->_actor->isSpeaking()) {
+ Common::g_timer->installTimerProc(&hintTimerCallback, 50000, this);
+ break;
+ }
+
+ _hintRqState = _hintNextRqState;
+ Common::g_timer->installTimerProc(&hintTimerCallback, 333333, this);
+ break;
+
+ case kRQNoHint:
+ // Pick a random hint request.
+ i = _hintOffer++;
+ if (_hintOffer >= NUM_SOLICIT_REPLIES)
+ _hintOffer = 0;
+
+ // Determine which of the journeymen will offer then
+ // hint, and then show that character's portrait.
+ _hintGiver = portraitList[i];
+ _hintSpeaker = _hintGiver - RID_ITE_JFERRET_SERIOUS;
+ _vm->_interface->setRightPortrait(_hintGiver);
+
+ _vm->_actor->nonActorSpeech(_hintBox, &solicitStr[_lang][i], 1, PUZZLE_SOLICIT_SOUNDS + i * 3 + _hintSpeaker, 0);
+
+ // Add Rif's reply to the list.
+ clearHint();
+
+ // Roll to see if Sakka scolds
+ if (_vm->_rnd.getRandomNumber(1)) {
+ _hintRqState = kRQSakkaDenies;
+ Common::g_timer->installTimerProc(&hintTimerCallback, 200000, this);
+ } else {
+ _hintRqState = kRQSpeaking;
+ _hintNextRqState = kRQHintRequested;
+ Common::g_timer->installTimerProc(&hintTimerCallback, 50000, this);
+ }
+
+ break;
+
+ case kRQHintRequested:
+ i = _vm->_rnd.getRandomNumber(NUM_SAKKA - 1);
+ _vm->_actor->nonActorSpeech(_hintBox, &sakkaStr[_lang][i], 1, PUZZLE_SAKKA_SOUNDS + i, 0);
+
+ _vm->_interface->setRightPortrait(RID_ITE_SAKKA_APPRAISING);
+
+ _hintRqState = kRQSpeaking;
+ _hintNextRqState = kRQHintRequestedStage2;
+ Common::g_timer->installTimerProc(&hintTimerCallback, 50000, this);
+
+ _vm->_interface->converseClear();
+ _vm->_interface->converseAddText(optionsStr[_lang][kROAccept], 1, 0, 0 );
+ _vm->_interface->converseAddText(optionsStr[_lang][kRODecline], 2, 0, 0 );
+ _vm->_interface->converseAddText(optionsStr[_lang][kROLater], 0, 0, 0 );
+ _vm->_interface->converseDisplayText();
+ break;
+
+ case kRQHintRequestedStage2:
+ if (_vm->_rnd.getRandomNumber(1)) { // Skip Reply part
+ i = _vm->_rnd.getRandomNumber(NUM_WHINES - 1);
+ _vm->_actor->nonActorSpeech(_hintBox, &whineStr[_lang][i], 1, PUZZLE_WHINE_SOUNDS + i * 3 + _hintSpeaker, 0);
+ }
+
+ _vm->_interface->setRightPortrait(_hintGiver);
+
+ _hintRqState = kRQSakkaDenies;
+ break;
+
+ case kRQSakkaDenies:
+ _vm->_interface->converseClear();
+ _vm->_interface->converseAddText(optionsStr[_lang][kROAccept], 1, 0, 0);
+ _vm->_interface->converseAddText(optionsStr[_lang][kRODecline], 2, 0, 0);
+ _vm->_interface->converseAddText(optionsStr[_lang][kROLater], 0, 0, 0);
+ _vm->_interface->converseDisplayText();
+
+ Common::g_timer->installTimerProc(&hintTimerCallback, kPuzzleHintTime, this);
+
+ _hintRqState = kRQSkipEverything;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void Puzzle::handleReply(int reply) {
+ switch(reply) {
+ case 0: // Quit the puzzle
+ exitPuzzle();
+ break;
+
+ case 1: // Accept the hint
+ giveHint();
+ break;
+
+ case 2: // Decline the hint
+ _vm->_actor->abortSpeech();
+ _hintRqState = kRQNoHint;
+ Common::g_timer->removeTimerProc(&hintTimerCallback);
+ Common::g_timer->installTimerProc(&hintTimerCallback, kPuzzleHintTime * 2, this);
+ clearHint();
+ break;
+ }
+}
+
+void Puzzle::giveHint(void) {
+ int i, total = 0;
+
+ _vm->_interface->converseClear();
+
+ _vm->_actor->abortSpeech();
+ _vm->_interface->setRightPortrait(_hintGiver);
+
+ for (i = 0; i < PUZZLE_PIECES; i++)
+ total += _pieceInfo[i].flag & PUZZLE_FIT;
+
+ if (_hintCount == 0 && (_pieceInfo[1].flag & PUZZLE_FIT
+ || _pieceInfo[12].flag & PUZZLE_FIT))
+ _hintCount++;
+ if (_hintCount == 1 && _pieceInfo[14].flag & PUZZLE_FIT)
+ _hintCount++;
+
+ if (_hintCount == 2 && total > 3)
+ _hintCount++;
+
+ _vm->_actor->setSpeechColor(1, kITEColorBlack);
+
+ if (_hintCount < 3) {
+ _vm->_actor->nonActorSpeech(_hintBox, &hintStr[_lang][_hintCount], 1, PUZZLE_HINT_SOUNDS + _hintCount * 3 + _hintSpeaker, 0);
+ } else {
+ int piece = 0;
+
+ for (i = PUZZLE_PIECES - 1; i >= 0; i--) {
+ piece = _piecePriority[i];
+ if (_pieceInfo[piece].flag & PUZZLE_MOVED
+ && !(_pieceInfo[piece].flag & PUZZLE_FIT)) {
+ if (_helpCount < 12)
+ _helpCount++;
+ break;
+ }
+ }
+
+ if (i >= 0) {
+ static char hintBuf[64];
+ static const char *hintPtr = hintBuf;
+ sprintf(hintBuf, optionsStr[_lang][kROHint], pieceNames[_lang][piece]);
+
+ _vm->_actor->nonActorSpeech(_hintBox, &hintPtr, 1, PUZZLE_TOOL_SOUNDS + _hintSpeaker + piece * 3, 0);
+ }
+ else {
+ // If no pieces are in the wrong place
+ _vm->_actor->nonActorSpeech(_hintBox, &hintStr[_lang][3], 1, PUZZLE_HINT_SOUNDS + 3 * 3 + _hintSpeaker, 0);
+ }
+ }
+ _hintCount++;
+
+ _hintRqState = kRQNoHint;
+
+ _vm->_interface->converseAddText(optionsStr[_lang][kROLater], 0, 0, 0);
+ _vm->_interface->converseDisplayText();
+
+ Common::g_timer->removeTimerProc(&hintTimerCallback);
+ Common::g_timer->installTimerProc(&hintTimerCallback, kPuzzleHintTime, this);
+}
+
+void Puzzle::clearHint(void) {
+ _vm->_interface->converseClear();
+ _vm->_interface->converseAddText(optionsStr[_lang][kROLater], 0, 0, 0);
+ _vm->_interface->converseDisplayText();
+ _vm->_interface->setStatusText(" ");
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/puzzle.h b/engines/saga/puzzle.h
new file mode 100644
index 0000000000..b5c9b7c15c
--- /dev/null
+++ b/engines/saga/puzzle.h
@@ -0,0 +1,120 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SAGA_PUZZLE_H_
+#define SAGA_PUZZLE_H_
+
+namespace Saga {
+
+
+#define PUZZLE_SOUNDS 3622
+#define PUZZLE_TOOL_SOUNDS (PUZZLE_SOUNDS + 0)
+#define PUZZLE_HINT_SOUNDS (PUZZLE_SOUNDS + 45)
+#define PUZZLE_SOLICIT_SOUNDS (PUZZLE_SOUNDS + 57)
+#define PUZZLE_WHINE_SOUNDS (PUZZLE_SOUNDS + 72)
+#define PUZZLE_SAKKA_SOUNDS (PUZZLE_SOUNDS + 87)
+
+class Puzzle {
+private:
+ enum kRQStates {
+ kRQNoHint = 0,
+ kRQHintRequested = 1,
+ kRQHintRequestedStage2 = 2,
+ kRQSakkaDenies = 3,
+ kRQSkipEverything = 4,
+ kRQSpeaking = 5
+ };
+
+ SagaEngine *_vm;
+
+ bool _solved;
+ bool _active;
+ bool _newPuzzle;
+ bool _sliding;
+
+ kRQStates _hintRqState;
+ kRQStates _hintNextRqState;
+ int _hintGiver;
+ int _hintSpeaker;
+ int _hintOffer;
+ int _hintCount;
+ int _helpCount;
+
+ int _puzzlePiece;
+ int _piecePriority[PUZZLE_PIECES];
+
+ int _lang;
+
+public:
+ Puzzle(SagaEngine *vm);
+
+ void execute(void);
+ void exitPuzzle(void);
+
+ bool isSolved(void) { return _solved; }
+ bool isActive(void) { return _active; }
+
+ void handleReply(int reply);
+ void handleClick(Point mousePt);
+
+ void movePiece(Point mousePt);
+
+private:
+ void initPieceInfo(int i, int16 curX, int16 curY, byte offX, byte offY, int16 trgX,
+ int16 trgY, uint8 flag, uint8 count, Point point0, Point point1,
+ Point point2, Point point3, Point point4, Point point5);
+
+ static void hintTimerCallback(void *refCon);
+
+ void solicitHint(void);
+
+ void initPieces(void);
+ void showPieces(void);
+ void slidePiece(int x1, int y1, int x2, int y2);
+ void dropPiece(Point mousePt);
+ void alterPiecePriority(void);
+ void drawCurrentPiece(void);
+
+ void giveHint(void);
+ void clearHint(void);
+
+private:
+ struct PieceInfo {
+ int16 curX;
+ int16 curY;
+ byte offX;
+ byte offY;
+ int16 trgX;
+ int16 trgY;
+ uint8 flag;
+ uint8 count;
+ Point point[6];
+ };
+
+ PieceInfo _pieceInfo[PUZZLE_PIECES];
+ int _slidePointX, _slidePointY;
+ Rect _hintBox;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/render.cpp b/engines/saga/render.cpp
new file mode 100644
index 0000000000..7d312fb920
--- /dev/null
+++ b/engines/saga/render.cpp
@@ -0,0 +1,189 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Main rendering loop
+#include "saga/saga.h"
+
+#include "saga/actor.h"
+#include "saga/font.h"
+#include "saga/gfx.h"
+#include "saga/interface.h"
+#include "saga/objectmap.h"
+#include "saga/puzzle.h"
+#include "saga/render.h"
+#include "saga/scene.h"
+
+#include "common/timer.h"
+#include "common/system.h"
+
+namespace Saga {
+
+const char *test_txt = "The quick brown fox jumped over the lazy dog. She sells sea shells down by the sea shore.";
+const char *pauseString = "PAWS GAME";
+
+Render::Render(SagaEngine *vm, OSystem *system) {
+ _vm = vm;
+ _system = system;
+ _initialized = false;
+
+ // Initialize FPS timer callback
+ Common::g_timer->installTimerProc(&fpsTimerCallback, 1000000, this);
+
+ _backGroundSurface.create(_vm->getDisplayWidth(), _vm->getDisplayHeight(), 1);
+
+ _flags = 0;
+
+ _initialized = true;
+}
+
+Render::~Render(void) {
+ Common::g_timer->removeTimerProc(&fpsTimerCallback);
+ _backGroundSurface.free();
+
+ _initialized = false;
+}
+
+bool Render::initialized() {
+ return _initialized;
+}
+
+void Render::drawScene() {
+ Surface *backBufferSurface;
+ char txtBuffer[20];
+ Point mousePoint;
+ Point textPoint;
+
+ assert(_initialized);
+
+ _frameCount++;
+
+ backBufferSurface = _vm->_gfx->getBackBuffer();
+
+ // Get mouse coordinates
+ mousePoint = _vm->mousePos();
+
+ if (!(_flags & (RF_DEMO_SUBST | RF_PLACARD | RF_MAP))) {
+ // Display scene background
+ _vm->_scene->draw();
+
+ if (_vm->_interface->getFadeMode() != kFadeOut) {
+ if (_vm->_puzzle->isActive()) {
+ _vm->_puzzle->movePiece(mousePoint);
+ _vm->_actor->drawSpeech();
+ } else {
+ // Draw queued actors
+ if (!(_flags & RF_DISABLE_ACTORS))
+ _vm->_actor->drawActors();
+ }
+
+ if (getFlags() & RF_OBJECTMAP_TEST) {
+ if (_vm->_scene->_objectMap)
+ _vm->_scene->_objectMap->draw(backBufferSurface, mousePoint, kITEColorBrightWhite, kITEColorBlack);
+ if (_vm->_scene->_actionMap)
+ _vm->_scene->_actionMap->draw(backBufferSurface, mousePoint, kITEColorRed, kITEColorBlack);
+ }
+ if (getFlags() & RF_ACTOR_PATH_TEST) {
+ _vm->_actor->drawPathTest();
+ }
+ }
+ }
+
+ if (_flags & RF_MAP)
+ _vm->_interface->mapPanelDrawCrossHair();
+
+ if ((_vm->_interface->getMode() == kPanelOption) ||
+ (_vm->_interface->getMode() == kPanelQuit) ||
+ (_vm->_interface->getMode() == kPanelLoad) ||
+ (_vm->_interface->getMode() == kPanelSave)) {
+ _vm->_interface->drawOption();
+
+ if (_vm->_interface->getMode() == kPanelQuit) {
+ _vm->_interface->drawQuit();
+ }
+ if (_vm->_interface->getMode() == kPanelLoad) {
+ _vm->_interface->drawLoad();
+ }
+ if (_vm->_interface->getMode() == kPanelSave) {
+ _vm->_interface->drawSave();
+ }
+ }
+
+ if (_vm->_interface->getMode() == kPanelProtect) {
+ _vm->_interface->drawProtect();
+ }
+
+ // Draw queued text strings
+ _vm->_scene->drawTextList(backBufferSurface);
+
+ // Handle user input
+ _vm->processInput();
+
+ // Display rendering information
+ if (_flags & RF_SHOW_FPS) {
+ sprintf(txtBuffer, "%d", _fps);
+ textPoint.x = backBufferSurface->w - _vm->_font->getStringWidth(kKnownFontSmall, txtBuffer, 0, kFontOutline);
+ textPoint.y = 2;
+
+ _vm->_font->textDraw(kKnownFontSmall, backBufferSurface, txtBuffer, textPoint, kITEColorBrightWhite, kITEColorBlack, kFontOutline);
+ }
+
+ // Display "paused game" message, if applicable
+ if (_flags & RF_RENDERPAUSE) {
+ textPoint.x = (backBufferSurface->w - _vm->_font->getStringWidth(kKnownFontPause, pauseString, 0, kFontOutline)) / 2;
+ textPoint.y = 90;
+
+ _vm->_font->textDraw(kKnownFontPause, backBufferSurface, pauseString, textPoint, kITEColorBrightWhite, kITEColorBlack, kFontOutline);
+ }
+
+ // Update user interface
+ _vm->_interface->update(mousePoint, UPDATE_MOUSEMOVE);
+
+ // Display text formatting test, if applicable
+ if (_flags & RF_TEXT_TEST) {
+ Rect rect(mousePoint.x, mousePoint.y, mousePoint.x + 100, mousePoint.y + 50);
+ _vm->_font->textDrawRect(kKnownFontMedium, backBufferSurface, test_txt, rect,
+ kITEColorBrightWhite, kITEColorBlack, (FontEffectFlags)(kFontOutline | kFontCentered));
+ }
+
+ // Display palette test, if applicable
+ if (_flags & RF_PALETTE_TEST) {
+ backBufferSurface->drawPalette();
+ }
+
+ _system->copyRectToScreen((byte *)backBufferSurface->pixels, backBufferSurface->w, 0, 0,
+ backBufferSurface->w, backBufferSurface->h);
+
+ _system->updateScreen();
+}
+
+void Render::fpsTimerCallback(void *refCon) {
+ ((Render *)refCon)->fpsTimer();
+}
+
+void Render::fpsTimer(void) {
+ _fps = _frameCount;
+ _frameCount = 0;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/render.h b/engines/saga/render.h
new file mode 100644
index 0000000000..f6187393d2
--- /dev/null
+++ b/engines/saga/render.h
@@ -0,0 +1,93 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Main rendering loop - private header
+
+#ifndef SAGA_RENDER_H_
+#define SAGA_RENDER_H_
+
+#include "saga/sprite.h"
+
+namespace Saga {
+
+enum RENDER_FLAGS {
+ RF_SHOW_FPS = (1 << 0),
+ RF_PALETTE_TEST = (1 << 1),
+ RF_TEXT_TEST = (1 << 2),
+ RF_OBJECTMAP_TEST = (1 << 3),
+ RF_RENDERPAUSE = (1 << 4),
+ RF_GAMEPAUSE = (1 << 5),
+ RF_PLACARD = (1 << 6),
+ RF_ACTOR_PATH_TEST = (1 << 7),
+ RF_MAP = (1 << 8),
+ RF_DISABLE_ACTORS = (1 << 9),
+ RF_DEMO_SUBST = (1 << 10)
+};
+
+class Render {
+public:
+ Render(SagaEngine *vm, OSystem *system);
+ ~Render(void);
+ bool initialized();
+ void drawScene(void);
+
+ unsigned int getFlags() const {
+ return _flags;
+ }
+
+ void setFlag(unsigned int flag) {
+ _flags |= flag;
+ }
+
+ void clearFlag(unsigned int flag) {
+ _flags &= ~flag;
+ }
+
+ void toggleFlag(unsigned int flag) {
+ _flags ^= flag;
+ }
+
+ Surface *getBackGroundSurface() {
+ return &_backGroundSurface;
+ }
+
+private:
+ static void fpsTimerCallback(void *refCon);
+ void fpsTimer(void);
+
+ SagaEngine *_vm;
+ OSystem *_system;
+ bool _initialized;
+
+ // Module data
+ Surface _backGroundSurface;
+
+ unsigned int _fps;
+ unsigned int _frameCount;
+ uint32 _flags;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/resnames.h b/engines/saga/resnames.h
new file mode 100644
index 0000000000..01522a9ca7
--- /dev/null
+++ b/engines/saga/resnames.h
@@ -0,0 +1,253 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Descriptive names for game resource numbers
+
+#ifndef SAGA_RESOURCENAMES_H_
+#define SAGA_RESOURCENAMES_H_
+
+namespace Saga {
+
+// Prefix RID_ means Resource Id
+
+// Lookup tables
+#define RID_ITE_SCENE_LUT 1806
+#define RID_ITE_SCRIPT_LUT 216
+
+#define RID_ITEDEMO_SCENE_LUT 318
+#define RID_ITEDEMO_SCRIPT_LUT 146
+
+#define RID_IHNM_SCENE_LUT 1272
+#define RID_IHNM_SCRIPT_LUT 29
+#define RID_IHNM_SFX_LUT 265
+
+#define RID_IHNMDEMO_SCENE_LUT 286
+#define RID_IHNMDEMO_SCRIPT_LUT 18
+
+//obj names
+#define ITE_OBJ_MAP 14
+#define ITE_OBJ_MAGIC_HAT 0
+
+#define IHNM_OBJ_PROFILE 0x4000
+
+#define RID_IHNM_DEFAULT_PALETTE 1
+
+//actor names
+#define ITE_ACTOR_PUZZLE 176
+
+// SCENES
+#define ITE_SCENE_INV -1
+#define ITE_SCENE_PUZZLE 26
+#define ITE_SCENE_LODGE 21
+#define ITE_SCENE_ENDCREDIT1 295
+
+#define ITE_DEFAULT_SCENE 32
+#define IHNM_DEFAULT_SCENE 151
+
+#define ITEDEMO_DEFAULT_SCENE 68
+
+// FONTS
+#define RID_MEDIUM_FONT 0
+#define RID_BIG_FONT 1
+#define RID_SMALL_FONT 2
+
+// INTERFACE IMAGES
+#define RID_ITE_MAIN_PANEL 3
+#define RID_ITE_CONVERSE_PANEL 4
+#define RID_ITE_OPTION_PANEL 5
+#define RID_ITE_MAIN_SPRITES 6
+#define RID_ITE_MAIN_PANEL_SPRITES 7
+#define RID_ITE_MAIN_STRINGS 35 //main strings
+#define RID_ITE_ACTOR_NAMES 36 //actors names
+#define RID_ITE_DEFAULT_PORTRAITS 125
+
+#define RID_ITEDEMO_MAIN_PANEL 2
+#define RID_ITEDEMO_CONVERSE_PANEL 3
+#define RID_ITEDEMO_OPTION_PANEL 3 // FIXME: should be 4 but it is an empty resource.
+#define RID_ITEDEMO_MAIN_SPRITES 5 // Proper fix would be not load options panel when demo is running
+#define RID_ITEDEMO_MAIN_PANEL_SPRITES 6
+#define RID_ITEDEMO_MAIN_STRINGS 8 //main strings
+#define RID_ITEDEMO_ACTOR_NAMES 9 //actors names
+#define RID_ITEDEMO_DEFAULT_PORTRAITS 80
+
+#define RID_ITE_TYCHO_MAP 1686
+#define RID_ITE_SPR_XHAIR1 (73 + 9)
+#define RID_ITE_SPR_XHAIR2 (74 + 9)
+
+#define RID_IHNM_MAIN_PANEL 9
+#define RID_IHNM_CONVERSE_PANEL 10
+#define RID_IHNM_HOURGLASS_CURSOR 11
+#define RID_IHNM_MAIN_SPRITES 12 // TODO: verify this
+#define RID_IHNM_MAIN_PANEL_SPRITES 12
+#define RID_IHNM_ARROW_SPRITES 13
+#define RID_IHNM_SAVEREMINDER_SPRITES 14
+#define RID_IHNM_OPTION_PANEL 15
+#define RID_IHNM_WARNING_PANEL 17
+#define RID_IHNM_BOSS_SCREEN 19
+#define RID_IHNM_PROFILE_BG 20
+#define RID_IHNM_MAIN_STRINGS 21
+
+// Puzzle portraits
+#define RID_ITE_SAKKA_APPRAISING 6
+#define RID_ITE_SAKKA_DENIAL 7
+#define RID_ITE_SAKKA_EXCITED 8
+#define RID_ITE_JFERRET_SERIOUS 9
+#define RID_ITE_JFERRET_GOOFY 10
+#define RID_ITE_JFERRET_ALOOF 11
+
+// ITE Scene resource numbers
+#define RID_ITE_OVERMAP_SCENE 226
+#define RID_ITE_INTRO_ANIM_SCENE 1538
+#define RID_ITE_CAVE_SCENE_1 1542
+#define RID_ITE_CAVE_SCENE_2 1545
+#define RID_ITE_CAVE_SCENE_3 1548
+#define RID_ITE_CAVE_SCENE_4 1551
+
+#define RID_ITE_VALLEY_SCENE 1556
+#define RID_ITE_TREEHOUSE_SCENE 1560
+#define RID_ITE_FAIREPATH_SCENE 1564
+#define RID_ITE_FAIRETENT_SCENE 1567
+
+#define RID_ITE_INTRO_ANIM_STARTFRAME 1529
+
+#define RID_ITE_INTRO_ANIM_1 1530
+#define RID_ITE_INTRO_ANIM_2 1531
+#define RID_ITE_INTRO_ANIM_3 1532
+#define RID_ITE_INTRO_ANIM_4 1533
+#define RID_ITE_INTRO_ANIM_5 1534
+#define RID_ITE_INTRO_ANIM_6 1535
+#define RID_ITE_INTRO_ANIM_7 1536
+
+#define RID_ITE_CAVE_IMG_1 1540
+#define RID_ITE_CAVE_IMG_2 1543
+#define RID_ITE_CAVE_IMG_3 1546
+#define RID_ITE_CAVE_IMG_4 1549
+
+#define RID_ITE_INTRO_IMG_1 1552
+#define RID_ITE_INTRO_IMG_2 1557
+#define RID_ITE_INTRO_IMG_3 1561
+#define RID_ITE_INTRO_IMG_4 1565
+
+// ITE_VOICES
+#define RID_CAVE_VOICE_0 0
+#define RID_CAVE_VOICE_1 1
+#define RID_CAVE_VOICE_2 2
+#define RID_CAVE_VOICE_3 3
+#define RID_CAVE_VOICE_4 4
+#define RID_CAVE_VOICE_5 5
+#define RID_CAVE_VOICE_6 6
+#define RID_CAVE_VOICE_7 7
+#define RID_CAVE_VOICE_8 8
+#define RID_CAVE_VOICE_9 9
+#define RID_CAVE_VOICE_10 10
+#define RID_CAVE_VOICE_11 11
+#define RID_CAVE_VOICE_12 12
+#define RID_CAVE_VOICE_13 13
+
+#define RID_SCENE1_VOICE_009 57
+//TODO: fill it
+#define RID_SCENE1_VOICE_138 186
+
+#define RID_BOAR_VOICE_000 239
+#define RID_BOAR_VOICE_002 241
+#define RID_BOAR_VOICE_005 244
+#define RID_BOAR_VOICE_006 245
+#define RID_BOAR_VOICE_007 246
+
+// MUSIC
+#define MUSIC_1 9
+#define MUSIC_2 10
+
+// TODO: If the sound effects are numbered sequentially, we don't really need
+// these constants. But for now they might be useful for debugging.
+
+// SOUND EFFECTS
+
+#define FX_DOOR_OPEN 14
+#define FX_DOOR_CLOSE 15
+#define FX_RUSH_WATER 16
+#define FX_CRICKET 17
+#define FX_PORTICULLIS 18
+#define FX_CLOCK_1 19
+#define FX_CLOCK_2 20
+#define FX_DAM_MACHINE 21
+#define FX_HUM1 22
+#define FX_HUM2 23
+#define FX_HUM3 24
+#define FX_HUM4 25
+#define FX_STREAM 26
+#define FX_SURF 27
+#define FX_FIRELOOP 28
+#define FX_SCRAPING 29
+#define FX_BEE_SWARM 30
+#define FX_SQUEAKBOARD 31
+#define FX_KNOCK 32
+#define FX_COINS 33
+#define FX_STORM 34
+#define FX_DOOR_CLOSE_2 35
+#define FX_ARCWELD 36
+#define FX_RETRACT_ORB 37
+#define FX_DRAGON 38
+#define FX_SNORES 39
+#define FX_SPLASH 40
+#define FX_LOBBY_DOOR 41
+#define FX_CHIRP_LOOP 42
+#define FX_DOOR_CREAK 43
+#define FX_SPOON_DIG 44
+#define FX_CROW 45
+#define FX_COLDWIND 46
+#define FX_TOOL_SND_1 47
+#define FX_TOOL_SND_2 48
+#define FX_TOOL_SND_3 49
+#define FX_DOOR_METAL 50
+#define FX_WATER_LOOP_S 51
+#define FX_WATER_LOOP_L 52
+#define FX_DOOR_OPEN_2 53
+#define FX_JAIL_DOOR 54
+#define FX_KILN_FIRE 55
+#define FX_DUMMY 56
+
+// These are only in the CD version
+
+#define FX_CROWD_01 57
+#define FX_CROWD_02 58
+#define FX_CROWD_03 59
+#define FX_CROWD_04 60
+#define FX_CROWD_05 61
+#define FX_CROWD_06 62
+#define FX_CROWD_07 63
+#define FX_CROWD_08 64
+#define FX_CROWD_09 65
+#define FX_CROWD_10 66
+#define FX_CROWD_11 67
+#define FX_CROWD_12 68
+#define FX_CROWD_13 69
+#define FX_CROWD_14 70
+#define FX_CROWD_15 71
+#define FX_CROWD_16 72
+#define FX_CROWD_17 73
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/rscfile.cpp b/engines/saga/rscfile.cpp
new file mode 100644
index 0000000000..24d1262d56
--- /dev/null
+++ b/engines/saga/rscfile.cpp
@@ -0,0 +1,615 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// RSC Resource file management module
+#include "saga/saga.h"
+
+#include "saga/actor.h"
+#include "saga/animation.h"
+#include "saga/interface.h"
+#include "saga/music.h"
+#include "saga/rscfile.h"
+#include "saga/scene.h"
+#include "saga/sndres.h"
+#include "saga/stream.h"
+
+namespace Saga {
+
+struct MacResMap {
+ int16 resAttr;
+ int16 typeOffset;
+ int16 nameOffset;
+ int16 numTypes;
+};
+
+struct MacResource {
+ int16 id;
+ int16 nameOffset;
+ byte attr;
+ int32 dataOffset;
+ byte name[255];
+};
+
+struct MacResType {
+ uint32 id;
+ int16 items;
+ int16 maxItemId;
+ int16 offset;
+ MacResource *resources;
+};
+
+
+#define ID_MIDI MKID_BE('Midi')
+
+Resource::Resource(SagaEngine *vm): _vm(vm) {
+ _contexts = NULL;
+ _contextsCount = 0;
+}
+
+Resource::~Resource() {
+ clearContexts();
+}
+
+bool Resource::loadSagaContext(ResourceContext *context, uint32 contextOffset, uint32 contextSize) {
+ size_t i;
+ bool result;
+ byte tableInfo[RSC_TABLEINFO_SIZE];
+ byte *tableBuffer;
+ size_t tableSize;
+ uint32 resourceTableOffset;
+ ResourceData *resourceData;
+
+ if (contextSize < RSC_MIN_FILESIZE) {
+ return false;
+ }
+
+ context->file->seek(contextOffset + contextSize - RSC_TABLEINFO_SIZE);
+
+ if (context->file->read(tableInfo, RSC_TABLEINFO_SIZE) != RSC_TABLEINFO_SIZE) {
+ return false;
+ }
+
+ MemoryReadStreamEndian readS(tableInfo, RSC_TABLEINFO_SIZE, context->isBigEndian);
+
+ resourceTableOffset = readS.readUint32();
+ context->count = readS.readUint32();
+
+ // Check for sane table offset
+ if (resourceTableOffset != contextSize - RSC_TABLEINFO_SIZE - RSC_TABLEENTRY_SIZE * context->count) {
+ return false;
+ }
+
+ // Load resource table
+ tableSize = RSC_TABLEENTRY_SIZE * context->count;
+
+ tableBuffer = (byte *)malloc(tableSize);
+
+ context->file->seek(resourceTableOffset + contextOffset, SEEK_SET);
+
+ result = (context->file->read(tableBuffer, tableSize) == tableSize);
+ if (result) {
+ context->table = (ResourceData *)calloc(context->count, sizeof(*context->table));
+
+ MemoryReadStreamEndian readS1(tableBuffer, tableSize, context->isBigEndian);
+
+ for (i = 0; i < context->count; i++) {
+ resourceData = &context->table[i];
+ resourceData->offset = contextOffset + readS1.readUint32();
+ resourceData->size = readS1.readUint32();
+ //sanity check
+ if ((resourceData->offset > context->file->size()) || (resourceData->size > contextSize)) {
+ result = false;
+ break;
+ }
+ }
+ }
+
+ free(tableBuffer);
+ return result;
+}
+
+bool Resource::loadMacContext(ResourceContext *context) {
+ int32 macDataSize, macDataSizePad;
+ int32 macResSize, macResSizePad;
+ int32 macResOffset;
+
+ uint32 macMapLength;
+ uint32 macDataLength;
+ uint32 macMapOffset;
+ uint32 macDataOffset;
+
+ MacResMap macResMap;
+ MacResType *macResTypes;
+
+ MacResType *macResType;
+ MacResource *macResource;
+ int i, j;
+ byte macNameLen;
+ bool notSagaContext = false;
+
+ if (context->file->size() < RSC_MIN_FILESIZE + MAC_BINARY_HEADER_SIZE) {
+ return false;
+ }
+
+ if (context->file->readByte() != 0) {
+ return false;
+ }
+ context->file->readByte(); //MAX Name Len
+ context->file->seek(74);
+ if (context->file->readByte() != 0) {
+ return false;
+ }
+ context->file->seek(82);
+ if (context->file->readByte() != 0) {
+ return false;
+ }
+
+ macDataSize = context->file->readSint32BE();
+ macResSize = context->file->readSint32BE();
+ macDataSizePad = (((macDataSize + 127) >> 7) << 7);
+ macResSizePad = (((macResSize + 127) >> 7) << 7);
+
+ macResOffset = MAC_BINARY_HEADER_SIZE + macDataSizePad;
+ context->file->seek(macResOffset);
+
+ macDataOffset = context->file->readUint32BE() + macResOffset;
+ macMapOffset = context->file->readUint32BE() + macResOffset;
+ macDataLength = context->file->readUint32BE();
+ macMapLength = context->file->readUint32BE();
+
+ if (macDataOffset >= context->file->size() || macMapOffset >= context->file->size() ||
+ macDataLength + macMapLength > context->file->size()) {
+ return false;
+ }
+
+ context->file->seek(macMapOffset + 22);
+
+ macResMap.resAttr = context->file->readUint16BE();
+ macResMap.typeOffset = context->file->readUint16BE();
+ macResMap.nameOffset = context->file->readUint16BE();
+ macResMap.numTypes = context->file->readUint16BE();
+ macResMap.numTypes++;
+
+ context->file->seek(macMapOffset + macResMap.typeOffset + 2);
+
+ macResTypes = (MacResType *)calloc(macResMap.numTypes, sizeof(*macResTypes));
+
+ for (i = macResMap.numTypes, macResType = macResTypes; i > 0; i--, macResType++) {
+ macResType->id = context->file->readUint32BE();
+ macResType->items = context->file->readUint16BE();
+ macResType->offset = context->file->readUint16BE();
+ macResType->items++;
+ macResType->resources = (MacResource*)calloc(macResType->items, sizeof(*macResType->resources));
+ }
+
+ for (i = macResMap.numTypes, macResType = macResTypes; i > 0; i--, macResType++) {
+ context->file->seek(macResType->offset + macMapOffset + macResMap.typeOffset);
+
+ for (j = macResType->items, macResource = macResType->resources; j > 0; j--, macResource++) {
+ macResource->id = context->file->readUint16BE();
+ macResource->nameOffset = context->file->readUint16BE();
+ macResource->dataOffset = context->file->readUint32BE();
+ macResSize = context->file->readUint32BE();
+
+ macResource->attr = macResource->dataOffset >> 24;
+ macResource->dataOffset &= 0xFFFFFF;
+ if (macResource->id > macResType->maxItemId) {
+ macResType->maxItemId = macResource->id;
+ }
+ }
+
+ for (j = macResType->items, macResource = macResType->resources; j > 0; j--, macResource++) {
+ if (macResource->nameOffset != -1) {
+ context->file->seek(macResource->nameOffset + macMapOffset + macResMap.nameOffset);
+ macNameLen = context->file->readByte();
+ context->file->read(macResource->name, macNameLen);
+ }
+ }
+ }
+
+//
+ for (i = macResMap.numTypes, macResType = macResTypes; i > 0; i--, macResType++) {
+ //getting offsets & sizes of midi
+ if (((context->fileType & GAME_MUSICFILE_GM) > 0) && (macResType->id == ID_MIDI)) {
+
+ context->count = macResType->maxItemId + 1;
+ context->table = (ResourceData *)calloc(context->count, sizeof(*context->table));
+ for (j = macResType->items, macResource = macResType->resources; j > 0; j--, macResource++) {
+ context->file->seek(macDataOffset + macResource->dataOffset);
+ context->table[macResource->id].size = context->file->readUint32BE();
+ context->table[macResource->id].offset = context->file->pos();
+ }
+ notSagaContext = true;
+ break;
+ }
+ }
+
+//free
+ for (i = 0; i < macResMap.numTypes; i++) {
+ free(macResTypes[i].resources);
+ }
+ free(macResTypes);
+
+ if ((!notSagaContext) && (!loadSagaContext(context, MAC_BINARY_HEADER_SIZE, macDataSize))) {
+ return false;
+ }
+
+ return true;
+}
+
+bool Resource::loadContext(ResourceContext *context) {
+ size_t i;
+ int j;
+ GamePatchDescription *patchDescription;
+ ResourceData *resourceData;
+ uint16 subjectResourceType;
+ ResourceContext *subjectContext;
+ uint32 subjectResourceId;
+ uint32 patchResourceId;
+ ResourceData *subjectResourceData;
+ byte *tableBuffer;
+ size_t tableSize;
+ bool isMacBinary;
+
+ if (!context->file->open(context->fileName)) {
+ return false;
+ }
+
+ context->isBigEndian = _vm->isBigEndian();
+
+ if (context->fileType & GAME_SWAPENDIAN)
+ context->isBigEndian = !context->isBigEndian;
+
+ isMacBinary = (context->fileType & GAME_MACBINARY) > 0;
+ context->fileType &= ~GAME_MACBINARY;
+
+ if (isMacBinary) {
+ if (!loadMacContext(context)) {
+ return false;
+ }
+ } else {
+ if (!loadSagaContext(context, 0, context->file->size())) {
+ return false;
+ }
+ }
+
+ //process internal patch files
+ if (GAME_PATCHFILE & context->fileType) {
+ subjectResourceType = ~GAME_PATCHFILE & context->fileType;
+ subjectContext = getContext((GameFileTypes)subjectResourceType);
+ if (subjectContext == NULL) {
+ error("Resource::loadContext() Subject context not found");
+ }
+ loadResource(context, context->count - 1, tableBuffer, tableSize);
+
+ MemoryReadStreamEndian readS2(tableBuffer, tableSize, context->isBigEndian);
+ for (i = 0; i < tableSize / 8; i++) {
+ subjectResourceId = readS2.readUint32();
+ patchResourceId = readS2.readUint32();
+ subjectResourceData = getResourceData(subjectContext, subjectResourceId);
+ resourceData = getResourceData(context, patchResourceId);
+ subjectResourceData->patchData = new PatchData(context->file);
+ subjectResourceData->offset = resourceData->offset;
+ subjectResourceData->size = resourceData->size;
+ }
+
+ }
+
+ //process external patch files
+ for (j = 0; j < _vm->getGameDescription()->patchesCount; j++) {
+ patchDescription = &_vm->getGameDescription()->patchDescriptions[j];
+ if ((patchDescription->fileType & context->fileType) != 0) {
+ if (patchDescription->resourceId < context->count) {
+ resourceData = &context->table[patchDescription->resourceId];
+ resourceData->patchData = new PatchData(patchDescription);
+ if (resourceData->patchData->_patchFile->open(patchDescription->fileName)) {
+ resourceData->offset = 0;
+ resourceData->size = resourceData->patchData->_patchFile->size();
+ } else {
+ delete resourceData->patchData;
+ resourceData->patchData = NULL;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool Resource::createContexts() {
+ int i;
+ ResourceContext *context;
+ _contextsCount = _vm->getGameDescription()->filesCount;
+ _contexts = (ResourceContext*)calloc(_contextsCount, sizeof(*_contexts));
+
+ for (i = 0; i < _contextsCount; i++) {
+ context = &_contexts[i];
+ context->file = new Common::File();
+ context->fileName = _vm->getGameDescription()->filesDescriptions[i].fileName;
+ context->fileType = _vm->getGameDescription()->filesDescriptions[i].fileType;
+ context->serial = 0;
+
+ // IHNM has serveral different voice files, so we need to allow
+ // multiple resource contexts of the same type. We tell them
+ // apart by assigning each of the duplicates an unique serial
+ // number. The default behaviour when requesting a context will
+ // be to look for serial number 0.
+
+ for (int j = i - 1; j >= 0; j--) {
+ if (_contexts[j].fileType & context->fileType) {
+ context->serial = _contexts[j].serial + 1;
+ break;
+ }
+ }
+
+ if (!loadContext(context)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void Resource::clearContexts() {
+ int i;
+ size_t j;
+ ResourceContext *context;
+ if (_contexts == NULL) {
+ return;
+ }
+ for(i = 0; i < _contextsCount; i++) {
+ context = &_contexts[i];
+ delete context->file;
+ if (context->table != NULL) {
+ for(j = 0; j < context->count; j++) {
+ delete context->table[j].patchData;
+ }
+ }
+ free(context->table);
+ }
+ free(_contexts);
+ _contexts = NULL;
+}
+
+uint32 Resource::convertResourceId(uint32 resourceId) {
+
+ if (_vm->getGameType() == GType_ITE && _vm->isMacResources()) {
+ if (resourceId > 1537) {
+ return resourceId - 2;
+ } else {
+ if (resourceId == 1535 || resourceId == 1536) {
+ error ("Wrong resource number %d for Mac ITE", resourceId);
+ }
+ }
+ }
+
+ return resourceId;
+}
+
+void Resource::loadResource(ResourceContext *context, uint32 resourceId, byte*&resourceBuffer, size_t &resourceSize) {
+ Common::File *file;
+ uint32 resourceOffset;
+ ResourceData *resourceData;
+
+ debug(8, "loadResource %d", resourceId);
+
+ resourceData = getResourceData(context, resourceId);
+
+ file = context->getFile(resourceData);
+
+ resourceOffset = resourceData->offset;
+ resourceSize = resourceData->size;
+
+ resourceBuffer = (byte*)malloc(resourceSize);
+
+ file->seek((long)resourceOffset, SEEK_SET);
+
+ if (file->read(resourceBuffer, resourceSize) != resourceSize) {
+ error("Resource::loadResource() failed to read");
+ }
+}
+
+static int metaResourceTable[] = { 0, 326, 517, 677, 805, 968, 1165, 0, 1271 };
+
+void Resource::loadGlobalResources(int chapter, int actorsEntrance) {
+ if (chapter < 0)
+ chapter = 8;
+
+ // TODO
+ //if (module.voiceLUT)
+ // free module.voiceLUT;
+
+ // TODO: close chapter context, or rather reassign it in our case
+
+ ResourceContext *resourceContext;
+ ResourceContext *soundContext;
+ int i;
+
+ resourceContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ if (resourceContext == NULL) {
+ error("Resource::loadGlobalResources() resource context not found");
+ }
+
+ soundContext = _vm->_resource->getContext(GAME_SOUNDFILE);
+ if (soundContext == NULL) {
+ error("Resource::loadGlobalResources() sound context not found");
+ }
+
+ byte *resourcePointer;
+ size_t resourceLength;
+
+ _vm->_resource->loadResource(resourceContext, metaResourceTable[chapter],
+ resourcePointer, resourceLength);
+
+ if (resourceLength == 0) {
+ error("Resource::loadGlobalResources wrong metaResource");
+ }
+
+ MemoryReadStream metaS(resourcePointer, resourceLength);
+
+ _metaResource.sceneIndex = metaS.readSint16LE();
+ _metaResource.objectCount = metaS.readSint16LE();
+ _metaResource.objectsStringsResourceID = metaS.readSint32LE();
+ _metaResource.inventorySpritesID = metaS.readSint32LE();
+ _metaResource.mainSpritesID = metaS.readSint32LE();
+ _metaResource.objectsResourceID = metaS.readSint32LE();
+ _metaResource.actorCount = metaS.readSint16LE();
+ _metaResource.actorsStringsResourceID = metaS.readSint32LE();
+ _metaResource.actorsResourceID = metaS.readSint32LE();
+ _metaResource.protagFaceSpritesID = metaS.readSint32LE();
+ _metaResource.field_22 = metaS.readSint32LE();
+ _metaResource.field_26 = metaS.readSint16LE();
+ _metaResource.protagStatesCount = metaS.readSint16LE();
+ _metaResource.protagStatesResourceID = metaS.readSint32LE();
+ _metaResource.cutawayListResourceID = metaS.readSint32LE();
+ _metaResource.songTableID = metaS.readSint32LE();
+
+ free(resourcePointer);
+
+ _vm->_actor->loadActorList(actorsEntrance, _metaResource.actorCount,
+ _metaResource.actorsResourceID, _metaResource.protagStatesCount,
+ _metaResource.protagStatesResourceID);
+
+ _vm->_actor->_protagonist->_sceneNumber = _metaResource.sceneIndex;
+
+ _vm->_actor->_objectsStrings.freeMem();
+
+ _vm->_resource->loadResource(resourceContext, _metaResource.objectsStringsResourceID, resourcePointer, resourceLength);
+ _vm->loadStrings(_vm->_actor->_objectsStrings, resourcePointer, resourceLength);
+ free(resourcePointer);
+
+ if (chapter >= _vm->_sndRes->_fxTableIDsLen) {
+ error("Chapter ID exceeds fxTableIDs length");
+ }
+
+ debug(0, "Going to read %d of %d", chapter, _vm->_sndRes->_fxTableIDs[chapter]);
+ _vm->_resource->loadResource(soundContext, _vm->_sndRes->_fxTableIDs[chapter],
+ resourcePointer, resourceLength);
+
+ if (resourceLength == 0) {
+ error("Resource::loadGlobalResources Can't load sound effects for current track");
+ }
+
+ free(_vm->_sndRes->_fxTable);
+
+ _vm->_sndRes->_fxTableLen = resourceLength / 4;
+ _vm->_sndRes->_fxTable = (FxTable *)malloc(sizeof(FxTable) * _vm->_sndRes->_fxTableLen);
+
+ MemoryReadStream fxS(resourcePointer, resourceLength);
+
+ for (i = 0; i < _vm->_sndRes->_fxTableLen; i++) {
+ _vm->_sndRes->_fxTable[i].res = fxS.readSint16LE();
+ _vm->_sndRes->_fxTable[i].vol = fxS.readSint16LE();
+ }
+ free(resourcePointer);
+
+ _vm->_interface->_defPortraits.freeMem();
+ _vm->_sprite->loadList(_metaResource.protagFaceSpritesID, _vm->_interface->_defPortraits);
+
+ _vm->_actor->_actorsStrings.freeMem();
+
+ _vm->_resource->loadResource(resourceContext, _metaResource.actorsStringsResourceID, resourcePointer, resourceLength);
+ _vm->loadStrings(_vm->_actor->_actorsStrings, resourcePointer, resourceLength);
+ free(resourcePointer);
+
+ _vm->_sprite->_inventorySprites.freeMem();
+ _vm->_sprite->loadList(_metaResource.inventorySpritesID, _vm->_sprite->_inventorySprites);
+
+ _vm->_sprite->_mainSprites.freeMem();
+ _vm->_sprite->loadList(_metaResource.mainSpritesID, _vm->_sprite->_mainSprites);
+
+ _vm->_actor->loadObjList(_metaResource.objectCount, _metaResource.objectsResourceID);
+
+ _vm->_resource->loadResource(resourceContext, _metaResource.cutawayListResourceID, resourcePointer, resourceLength);
+
+ if (resourceLength == 0) {
+ error("Resource::loadGlobalResources Can't load cutaway list");
+ }
+
+ _vm->_anim->loadCutawayList(resourcePointer, resourceLength);
+
+ _vm->_resource->loadResource(resourceContext, _metaResource.songTableID, resourcePointer, resourceLength);
+
+ if (resourceLength == 0) {
+ error("Resource::loadGlobalResources Can't load songs list for current track");
+ }
+
+ free(_vm->_music->_songTable);
+
+ _vm->_music->_songTableLen = resourceLength / 4;
+ _vm->_music->_songTable = (int32 *)malloc(sizeof(int32) * _vm->_music->_songTableLen);
+
+ MemoryReadStream songS(resourcePointer, resourceLength);
+
+ for (i = 0; i < _vm->_music->_songTableLen; i++)
+ _vm->_music->_songTable[i] = songS.readSint32LE();
+ free(resourcePointer);
+
+ int voiceLUTResourceID = 0;
+
+ _vm->_script->_globalVoiceLUT.freeMem();
+
+ switch (chapter) {
+ case 1:
+ _vm->_sndRes->setVoiceBank(1);
+ voiceLUTResourceID = 23;
+ break;
+ case 2:
+ _vm->_sndRes->setVoiceBank(2);
+ voiceLUTResourceID = 24;
+ break;
+ case 3:
+ _vm->_sndRes->setVoiceBank(3);
+ voiceLUTResourceID = 25;
+ break;
+ case 4:
+ _vm->_sndRes->setVoiceBank(4);
+ voiceLUTResourceID = 26;
+ break;
+ case 5:
+ _vm->_sndRes->setVoiceBank(5);
+ voiceLUTResourceID = 27;
+ break;
+ case 6:
+ _vm->_sndRes->setVoiceBank(6);
+ voiceLUTResourceID = 28;
+ break;
+ case 7:
+ break;
+ case 8:
+ _vm->_sndRes->setVoiceBank(0);
+ voiceLUTResourceID = 22;
+ break;
+ }
+
+ if (voiceLUTResourceID) {
+ _vm->_resource->loadResource(resourceContext, voiceLUTResourceID, resourcePointer, resourceLength);
+ _vm->_script->loadVoiceLUT(_vm->_script->_globalVoiceLUT, resourcePointer, resourceLength);
+ free(resourcePointer);
+ }
+
+ _vm->_spiritualBarometer = 0;
+ _vm->_scene->setChapterNumber(chapter);
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/rscfile.h b/engines/saga/rscfile.h
new file mode 100644
index 0000000000..434a6d9a96
--- /dev/null
+++ b/engines/saga/rscfile.h
@@ -0,0 +1,173 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// RSC Resource file management header file
+
+#ifndef SAGA_RSCFILE_H__
+#define SAGA_RSCFILE_H__
+
+#include "common/file.h"
+
+namespace Saga {
+
+#define MAC_BINARY_HEADER_SIZE 128
+#define RSC_TABLEINFO_SIZE 8
+#define RSC_TABLEENTRY_SIZE 8
+
+#define RSC_MIN_FILESIZE (RSC_TABLEINFO_SIZE + RSC_TABLEENTRY_SIZE + 1)
+
+struct PatchData {
+ bool _deletePatchFile;
+ Common::File *_patchFile;
+ GamePatchDescription *_patchDescription;
+
+ PatchData(GamePatchDescription *patchDescription): _patchDescription(patchDescription), _deletePatchFile(true) {
+ _patchFile = new Common::File();
+ }
+ PatchData(Common::File *patchFile): _patchDescription(NULL), _patchFile(patchFile), _deletePatchFile(false) {
+ }
+
+ ~PatchData() {
+ if (_deletePatchFile) {
+ delete _patchFile;
+ }
+ }
+};
+
+struct ResourceData {
+ size_t offset;
+ size_t size;
+ PatchData *patchData;
+ void fillSoundPatch(const GameSoundInfo *&soundInfo) {
+ if (patchData != NULL) {
+ if (patchData->_patchDescription != NULL) {
+ if (patchData->_patchDescription->soundInfo != NULL) {
+ soundInfo = patchData->_patchDescription->soundInfo;
+ }
+ }
+ }
+ }
+};
+
+struct ResourceContext {
+ const char *fileName;
+ uint16 fileType;
+ Common::File *file;
+ int serial;
+
+ bool isBigEndian;
+ ResourceData *table;
+ size_t count;
+
+ Common::File *getFile(ResourceData *resourceData) const {
+ if (resourceData->patchData != NULL) {
+ return resourceData->patchData->_patchFile;
+ } else {
+ return file;
+ }
+ }
+};
+
+struct MetaResource {
+ int16 sceneIndex;
+ int16 objectCount;
+ int32 objectsStringsResourceID;
+ int32 inventorySpritesID;
+ int32 mainSpritesID;
+ int32 objectsResourceID;
+ int16 actorCount;
+ int32 actorsStringsResourceID;
+ int32 actorsResourceID;
+ int32 protagFaceSpritesID;
+ int32 field_22;
+ int16 field_26;
+ int16 protagStatesCount;
+ int32 protagStatesResourceID;
+ int32 cutawayListResourceID;
+ int32 songTableID;
+
+ MetaResource() {
+ memset(this, 0, sizeof(*this));
+ }
+};
+
+class Resource {
+public:
+ Resource(SagaEngine *vm);
+ ~Resource();
+ bool createContexts();
+ void clearContexts();
+ void loadResource(ResourceContext *context, uint32 resourceId, byte*&resourceBuffer, size_t &resourceSize);
+ size_t getResourceSize(ResourceContext *context, uint32 resourceId);
+ uint32 convertResourceId(uint32 resourceId);
+
+ void loadGlobalResources(int chapter, int actorsEntrance);
+
+ ResourceContext *getContext(uint16 fileType, int serial = 0) {
+ int i;
+ for (i = 0; i < _contextsCount; i++) {
+ if ((_contexts[i].fileType & fileType) && _contexts[i].serial == serial) {
+ return &_contexts[i];
+ }
+ }
+ return NULL;
+ }
+
+ bool validResourceId(ResourceContext *context, uint32 resourceId) const {
+ return (resourceId < context->count);
+ }
+
+ size_t getResourceSize(ResourceContext *context, uint32 resourceId) const {
+ return getResourceData(context, resourceId)->size;
+ }
+
+ size_t getResourceOffset(ResourceContext *context, uint32 resourceId) const {
+ return getResourceData(context, resourceId)->offset;
+ }
+
+ ResourceData *getResourceData(ResourceContext *context, uint32 resourceId) const {
+ if (!validResourceId(context, resourceId)) {
+ warning("Resource::getResourceData() wrong resourceId %d", resourceId);
+ assert(0);
+ }
+ return &context->table[resourceId];
+ }
+
+private:
+ SagaEngine *_vm;
+ ResourceContext *_contexts;
+ int _contextsCount;
+
+ bool loadContext(ResourceContext *context);
+ bool loadMacContext(ResourceContext *context);
+ bool loadSagaContext(ResourceContext *context, uint32 contextOffset, uint32 contextSize);
+
+
+public:
+ MetaResource _metaResource;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
new file mode 100644
index 0000000000..4adda480c6
--- /dev/null
+++ b/engines/saga/saga.cpp
@@ -0,0 +1,505 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "common/stdafx.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+#include "backends/fs/fs.h"
+
+#include "common/file.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+
+#include "sound/mixer.h"
+
+#include "saga/saga.h"
+
+#include "saga/rscfile.h"
+#include "saga/gfx.h"
+#include "saga/render.h"
+#include "saga/actor.h"
+#include "saga/animation.h"
+#include "saga/console.h"
+#include "saga/events.h"
+#include "saga/font.h"
+#include "saga/interface.h"
+#include "saga/isomap.h"
+#include "saga/puzzle.h"
+#include "saga/script.h"
+#include "saga/scene.h"
+#include "saga/sndres.h"
+#include "saga/sprite.h"
+#include "saga/sound.h"
+#include "saga/music.h"
+#include "saga/palanim.h"
+#include "saga/objectmap.h"
+#include "saga/resnames.h"
+
+static const GameSettings saga_games[] = {
+ {"ite", "Inherit the Earth", 0},
+ {"ihnm", "I Have No Mouth and I Must Scream", GF_DEFAULT_TO_1X_SCALER },
+ {0, 0, 0}
+};
+
+GameList Engine_SAGA_gameList() {
+ GameList games;
+ const GameSettings *g = saga_games;
+
+ while (g->gameid) {
+ games.push_back(*g);
+ g++;
+ }
+
+ return games;
+}
+
+DetectedGameList Engine_SAGA_detectGames(const FSList &fslist) {
+ return Saga::GAME_ProbeGame(fslist);
+}
+
+Engine *Engine_SAGA_create(GameDetector *detector, OSystem *syst) {
+ return new Saga::SagaEngine(detector, syst);
+}
+
+REGISTER_PLUGIN(SAGA, "SAGA Engine")
+
+namespace Saga {
+
+#define MAX_TIME_DELTA 100
+
+SagaEngine::SagaEngine(GameDetector *detector, OSystem *syst)
+ : Engine(syst),
+ _targetName(detector->_targetName) {
+
+ _leftMouseButtonPressed = _rightMouseButtonPressed = false;
+
+ _console = NULL;
+ _quit = false;
+
+ _resource = NULL;
+ _sndRes = NULL;
+ _events = NULL;
+ _font = NULL;
+ _sprite = NULL;
+ _anim = NULL;
+ _script = NULL;
+ _interface = NULL;
+ _actor = NULL;
+ _palanim = NULL;
+ _scene = NULL;
+ _isoMap = NULL;
+ _gfx = NULL;
+ _console = NULL;
+ _render = NULL;
+ _music = NULL;
+ _sound = NULL;
+ _puzzle = NULL;
+
+ _frameCount = 0;
+ _globalFlags = 0;
+ memset(_ethicsPoints, 0, sizeof(_ethicsPoints));
+
+ // The Linux version of Inherit the Earth puts all data files in an
+ // 'itedata' sub-directory, except for voices.rsc
+ Common::File::addDefaultDirectory(_gameDataPath + "itedata/");
+
+ // The Windows version of Inherit the Earth puts various data files in
+ // other subdirectories.
+ Common::File::addDefaultDirectory(_gameDataPath + "graphics/");
+ Common::File::addDefaultDirectory(_gameDataPath + "music/");
+ Common::File::addDefaultDirectory(_gameDataPath + "sound/");
+
+ // The Multi-OS version puts the voices file in the root directory of
+ // the CD. The rest of the data files are in game/itedata
+ Common::File::addDefaultDirectory(_gameDataPath + "game/itedata/");
+
+ // Mac CD Wyrmkeep
+ Common::File::addDefaultDirectory(_gameDataPath + "patch/");
+
+ // Setup mixer
+ if (!_mixer->isReady()) {
+ warning("Sound initialization failed.");
+ }
+
+ _displayClip.left = _displayClip.top = 0;
+}
+
+SagaEngine::~SagaEngine() {
+ if (_scene != NULL) {
+ if (_scene->isSceneLoaded()) {
+ _scene->endScene();
+ }
+ }
+
+ delete _puzzle;
+ delete _sndRes;
+ delete _events;
+ delete _font;
+ delete _sprite;
+ delete _anim;
+ delete _script;
+ delete _interface;
+ delete _actor;
+ delete _palanim;
+ delete _scene;
+ delete _isoMap;
+ delete _render;
+ delete _music;
+ delete _sound;
+ delete _gfx;
+ delete _console;
+
+ delete _resource;
+}
+
+void SagaEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+}
+
+int SagaEngine::init(GameDetector &detector) {
+ _soundVolume = ConfMan.getInt("sfx_volume") / 25;
+ _musicVolume = ConfMan.getInt("music_volume") / 25;
+ _subtitlesEnabled = ConfMan.getBool("subtitles");
+ _readingSpeed = ConfMan.getInt("talkspeed");
+ _copyProtection = ConfMan.getBool("copy_protection");
+
+ if (_readingSpeed > 3)
+ _readingSpeed = 0;
+
+ _resource = new Resource(this);
+
+ // Add some default directories
+ // Win32 demo & full game
+ Common::File::addDefaultDirectory("graphics");
+ Common::File::addDefaultDirectory("music");
+ Common::File::addDefaultDirectory("sound");
+
+ // Linux demo
+ Common::File::addDefaultDirectory("itedata");
+
+ // Mac demos & full game
+ Common::File::addDefaultDirectory("patch");
+
+ // Process command line
+
+ // Detect game and open resource files
+ if (!initGame()) {
+ return FAILURE;
+ }
+
+ // Initialize engine modules
+ _sndRes = new SndRes(this);
+ _events = new Events(this);
+ _font = new Font(this);
+ _sprite = new Sprite(this);
+ _anim = new Anim(this);
+ _script = new Script(this);
+ _interface = new Interface(this); // requires script module
+ _scene = new Scene(this);
+ _actor = new Actor(this);
+ _palanim = new PalAnim(this);
+ _isoMap = new IsoMap(this);
+ _puzzle = new Puzzle(this);
+
+ // System initialization
+
+ _previousTicks = _system->getMillis();
+
+ // Initialize graphics
+ _gfx = new Gfx(this, _system, getDisplayWidth(), getDisplayHeight(), detector);
+
+ // Graphics driver should be initialized before console
+ _console = new Console(this);
+
+ // Graphics should be initialized before music
+ int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
+ bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ bool adlib = (midiDriver == MD_ADLIB);
+
+ MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ if (native_mt32)
+ driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+
+ _music = new Music(this, _mixer, driver, _musicVolume);
+ _music->setNativeMT32(native_mt32);
+ _music->setAdlib(adlib);
+
+ if (!_musicVolume) {
+ debug(1, "Music disabled.");
+ }
+
+ _render = new Render(this, _system);
+ if (!_render->initialized()) {
+ return FAILURE;
+ }
+
+ // Initialize system specific sound
+ _sound = new Sound(this, _mixer, _soundVolume);
+ if (!_soundVolume) {
+ debug(1, "Sound disabled.");
+ }
+
+ _interface->converseInit();
+ _script->setVerb(_script->getVerbType(kVerbWalkTo));
+
+ _music->setVolume(-1, 1);
+
+ _gfx->initPalette();
+
+ // FIXME: This is the ugly way of reducing redraw overhead. It works
+ // well for 320x200 but it's unclear how well it will work for
+ // 640x480.
+
+ if (getGameType() == GType_ITE)
+ _system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, true);
+
+ return SUCCESS;
+}
+
+int SagaEngine::go() {
+ int msec = 0;
+
+ _previousTicks = _system->getMillis();
+
+ if (ConfMan.hasKey("start_scene")) {
+ _scene->changeScene(ConfMan.getInt("start_scene"), 0, kTransitionNoFade);
+ } else if (ConfMan.hasKey("boot_param")) {
+ if (getGameType() == GType_ITE)
+ _interface->addToInventory(_actor->objIndexToId(ITE_OBJ_MAGIC_HAT));
+ _scene->changeScene(ConfMan.getInt("boot_param"), 0, kTransitionNoFade);
+ } else if (ConfMan.hasKey("save_slot")) {
+ // First scene sets up palette
+ _scene->changeScene(getStartSceneNumber(), 0, kTransitionNoFade);
+ _events->handleEvents(0); // Process immediate events
+
+ char *fileName;
+ fileName = calcSaveFileName(ConfMan.getInt("save_slot"));
+ load(fileName);
+ _interface->setMode(kPanelMain);
+ } else {
+ _framesEsc = 0;
+ _scene->startScene();
+ }
+
+ uint32 currentTicks;
+
+ while (!_quit) {
+ if (_console->isAttached())
+ _console->onFrame();
+
+ if (_render->getFlags() & RF_RENDERPAUSE) {
+ // Freeze time while paused
+ _previousTicks = _system->getMillis();
+ } else {
+ currentTicks = _system->getMillis();
+ // Timer has rolled over after 49 days
+ if (currentTicks < _previousTicks)
+ msec = 0;
+ else {
+ msec = currentTicks - _previousTicks;
+ _previousTicks = currentTicks;
+ }
+ if (msec > MAX_TIME_DELTA) {
+ msec = MAX_TIME_DELTA;
+ }
+
+ // Since Puzzle is actorless, we do it here
+ if (_puzzle->isActive()) {
+ _actor->handleSpeech(msec);
+ } else if (!_scene->isInIntro()) {
+ if (_interface->getMode() == kPanelMain ||
+ _interface->getMode() == kPanelConverse ||
+ _interface->getMode() == kPanelCutaway ||
+ _interface->getMode() == kPanelNull ||
+ _interface->getMode() == kPanelChapterSelection)
+ _actor->direct(msec);
+ }
+
+ _events->handleEvents(msec);
+ _script->executeThreads(msec);
+ }
+ // Per frame processing
+ _render->drawScene();
+ _system->delayMillis(10);
+ }
+
+ return 0;
+}
+
+void SagaEngine::loadStrings(StringsTable &stringsTable, const byte *stringsPointer, size_t stringsLength) {
+ uint16 stringsCount;
+ size_t offset;
+ int i;
+
+ if (stringsLength == 0) {
+ error("SagaEngine::loadStrings() Error loading strings list resource");
+ }
+
+ stringsTable.stringsPointer = (byte*)malloc(stringsLength);
+ memcpy(stringsTable.stringsPointer, stringsPointer, stringsLength);
+
+
+ MemoryReadStreamEndian scriptS(stringsTable.stringsPointer, stringsLength, isBigEndian()); //TODO: get endianess from context
+
+ offset = scriptS.readUint16();
+ stringsCount = offset / 2;
+ stringsTable.strings = (const char **)malloc(stringsCount * sizeof(*stringsTable.strings));
+ i = 0;
+ scriptS.seek(0);
+ while (i < stringsCount) {
+ offset = scriptS.readUint16();
+ if (offset == stringsLength) {
+ stringsCount = i;
+ stringsTable.strings = (const char **)realloc(stringsTable.strings, stringsCount * sizeof(*stringsTable.strings));
+ break;
+ }
+ if (offset > stringsLength) {
+ error("SagaEngine::loadStrings wrong strings table");
+ }
+ stringsTable.strings[i] = (const char *)stringsTable.stringsPointer + offset;
+ debug(9, "string[%i]=%s", i, stringsTable.strings[i]);
+ i++;
+ }
+ stringsTable.stringsCount = stringsCount;
+}
+
+const char *SagaEngine::getObjectName(uint16 objectId) {
+ ActorData *actor;
+ ObjectData *obj;
+ const HitZone *hitZone;
+ switch (objectTypeId(objectId)) {
+ case kGameObjectObject:
+ obj = _actor->getObj(objectId);
+ if (getGameType() == GType_ITE)
+ return _script->_mainStrings.getString(obj->_nameIndex);
+ return _actor->_objectsStrings.getString(obj->_nameIndex);
+ case kGameObjectActor:
+ actor = _actor->getActor(objectId);
+ return _actor->_actorsStrings.getString(actor->_nameIndex);
+ case kGameObjectHitZone:
+ hitZone = _scene->_objectMap->getHitZone(objectIdToIndex(objectId));
+ return _scene->_sceneStrings.getString(hitZone->getNameIndex());
+ }
+ warning("SagaEngine::getObjectName name not found for 0x%X", objectId);
+ return NULL;
+}
+
+const char *SagaEngine::getTextString(int textStringId) {
+ const char *string;
+ int lang = (getLanguage() == Common::DE_DEU) ? 1 : 0;
+
+ string = ITEinterfaceTextStrings[lang][textStringId];
+ if (!string)
+ string = ITEinterfaceTextStrings[0][textStringId];
+
+ return string;
+}
+
+void SagaEngine::getExcuseInfo(int verb, const char *&textString, int &soundResourceId) {
+ textString = NULL;
+
+ if (verb == _script->getVerbType(kVerbPickUp)) {
+ textString = getTextString(kTextICantPickup);
+ soundResourceId = RID_BOAR_VOICE_007;
+ } else
+ if (verb == _script->getVerbType(kVerbLookAt)) {
+ textString = getTextString(kTextNothingSpecial);
+ soundResourceId = RID_BOAR_VOICE_006;
+ }
+ if (verb == _script->getVerbType(kVerbOpen)) {
+ textString = getTextString(kTextNoPlaceToOpen);
+ soundResourceId = RID_BOAR_VOICE_000;
+ }
+ if (verb == _script->getVerbType(kVerbClose)) {
+ textString = getTextString(kTextNoOpening);
+ soundResourceId = RID_BOAR_VOICE_002;
+ }
+ if (verb == _script->getVerbType(kVerbUse)) {
+ textString = getTextString(kTextDontKnow);
+ soundResourceId = RID_BOAR_VOICE_005;
+ }
+}
+
+ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) {
+ ColorId colorId = kITEColorTransBlack;
+
+ if (getGameType() == GType_ITE) {
+ switch (knownColor) {
+ case(kKnownColorTransparent):
+ colorId = kITEColorTransBlack;
+ break;
+
+ case (kKnownColorBrightWhite):
+ colorId = kITEColorBrightWhite;
+ break;
+ case (kKnownColorBlack):
+ colorId = kITEColorBlack;
+ break;
+
+ case (kKnownColorSubtitleTextColor):
+ colorId = (ColorId)255;
+ break;
+ case (kKnownColorVerbText):
+ colorId = kITEColorBlue;
+ break;
+ case (kKnownColorVerbTextShadow):
+ colorId = kITEColorBlack;
+ break;
+ case (kKnownColorVerbTextActive):
+ colorId = (ColorId)96;
+ break;
+
+ default:
+ error("SagaEngine::KnownColor2ColorId unknown color %i", knownColor);
+ }
+ } else if (getGameType() == GType_IHNM) {
+ switch (knownColor)
+ {
+ case(kKnownColorTransparent):
+ colorId = kITEColorTransBlack;
+ break;
+
+ case (kKnownColorBlack):
+ colorId = kIHNMColorBlack;
+ break;
+
+ case (kKnownColorVerbText):
+ colorId = (ColorId)253;
+ break;
+ case (kKnownColorVerbTextShadow):
+ colorId = (ColorId)15;
+ break;
+ case (kKnownColorVerbTextActive):
+ colorId = (ColorId)252;
+ break;
+
+ default:
+ error("SagaEngine::KnownColor2ColorId unknown color %i", knownColor);
+ }
+ }
+ return colorId;
+}
+
+
+} // End of namespace Saga
diff --git a/engines/saga/saga.h b/engines/saga/saga.h
new file mode 100644
index 0000000000..f2111265bd
--- /dev/null
+++ b/engines/saga/saga.h
@@ -0,0 +1,741 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SAGA_H
+#define SAGA_H
+
+#include "base/engine.h"
+#include "base/plugins.h"
+
+#include "common/stream.h"
+
+#include "saga/gfx.h"
+#include "saga/list.h"
+
+namespace Saga {
+
+class SndRes;
+class Sound;
+class Music;
+class Anim;
+class Render;
+class IsoMap;
+class Gfx;
+class Script;
+class Actor;
+class Font;
+class Sprite;
+class Scene;
+class Interface;
+class Console;
+class Events;
+class PalAnim;
+class Puzzle;
+class Resource;
+
+struct ResourceContext;
+struct StringList;
+
+//#define MIN_IMG_RLECODE 3
+//#define MODEX_SCANLINE_LIMIT 200 //TODO: remove
+
+#define SAGA_IMAGE_DATA_OFFSET 776
+#define SAGA_IMAGE_HEADER_LEN 8
+
+#define MAXPATH 512 //TODO: remove
+
+#define SAVE_TITLE_SIZE 28
+#define MAX_SAVES 96
+#define MAX_FILE_NAME 256
+
+#define ID_NOTHING 0
+#define ID_PROTAG 1
+#define OBJECT_TYPE_SHIFT 13
+#define OBJECT_TYPE_MASK ((1 << OBJECT_TYPE_SHIFT) - 1)
+
+#define OBJ_SPRITE_BASE 9
+
+#define memoryError(Place) error("%s Memory allocation error.", Place)
+
+enum ERRORCODE {
+ MEM = -2,//todo: remove
+ FAILURE = -1,
+ SUCCESS = 0
+};
+
+enum SAGAGameType {
+ GType_ITE,
+ GType_IHNM
+};
+
+enum GameObjectTypes {
+ kGameObjectNone = 0,
+ kGameObjectActor = 1,
+ kGameObjectObject = 2,
+ kGameObjectHitZone = 3,
+ kGameObjectStepZone = 4
+};
+
+enum ScriptTimings {
+ kScriptTimeTicksPerSecond = (728L/10L),
+ kRepeatSpeedTicks = (728L/10L)/3,
+ kNormalFadeDuration = 320, // 64 steps, 5 msec each
+ kQuickFadeDuration = 64, // 64 steps, 1 msec each
+ kPuzzleHintTime = 30000000L // 30 secs. used in timer
+};
+
+enum Directions {
+ kDirUp = 0,
+ kDirUpRight = 1,
+ kDirRight = 2,
+ kDirDownRight = 3,
+ kDirDown = 4,
+ kDirDownLeft = 5,
+ kDirLeft = 6,
+ kDirUpLeft = 7
+};
+
+enum HitZoneFlags {
+ kHitZoneEnabled = (1 << 0), // Zone is enabled
+ kHitZoneExit = (1 << 1), // Causes char to exit
+
+ // The following flag causes the zone to act differently.
+ // When the actor hits the zone, it will immediately begin walking
+ // in the specified direction, and the actual specified effect of
+ // the zone will be delayed until the actor leaves the zone.
+ kHitZoneAutoWalk = (1 << 2),
+
+ // When set on a hit zone, this causes the character not to walk
+ // to the object (but they will look at it).
+ kHitZoneNoWalk = (1 << 2),
+
+ // zone activates only when character stops walking
+ kHitZoneTerminus = (1 << 3),
+
+ // Hit zones only - when the zone is clicked on it projects the
+ // click point downwards from the middle of the zone until it
+ // reaches the lowest point in the zone.
+ kHitZoneProject = (1 << 3)
+};
+
+
+enum PanelButtonType {
+ kPanelButtonVerb = 1 << 0,
+ kPanelButtonArrow = 1 << 1,
+ kPanelButtonConverseText = 1 << 2,
+ kPanelButtonInventory = 1 << 3,
+
+ kPanelButtonOption = 1 << 4,
+ kPanelButtonOptionSlider = 1 << 5,
+ kPanelButtonOptionSaveFiles = 1 << 6,
+ kPanelButtonOptionText = 1 << 7,
+
+ kPanelButtonQuit = 1 << 8,
+ kPanelButtonQuitText = 1 << 9,
+
+ kPanelButtonLoad = 1 << 10,
+ kPanelButtonLoadText = 1 << 11,
+
+ kPanelButtonSave = 1 << 12,
+ kPanelButtonSaveText = 1 << 13,
+ kPanelButtonSaveEdit = 1 << 14,
+
+ kPanelButtonProtectText = 1 << 15,
+ kPanelButtonProtectEdit = 1 << 16,
+
+ kPanelAllButtons = 0xFFFFF
+};
+
+enum TextStringIds {
+ kTextWalkTo,
+ kTextLookAt,
+ kTextPickUp,
+ kTextTalkTo,
+ kTextOpen,
+ kTextClose,
+ kTextUse,
+ kTextGive,
+ kTextOptions,
+ kTextTest,
+ kTextDemo,
+ kTextHelp,
+ kTextQuitGame,
+ kTextFast,
+ kTextSlow,
+ kTextOn,
+ kTextOff,
+ kTextContinuePlaying,
+ kTextLoad,
+ kTextSave,
+ kTextGameOptions,
+ kTextReadingSpeed,
+ kTextMusic,
+ kTextSound,
+ kTextCancel,
+ kTextQuit,
+ kTextOK,
+ kTextMid,
+ kTextClick,
+ kText10Percent,
+ kText20Percent,
+ kText30Percent,
+ kText40Percent,
+ kText50Percent,
+ kText60Percent,
+ kText70Percent,
+ kText80Percent,
+ kText90Percent,
+ kTextMax,
+ kTextQuitTheGameQuestion,
+ kTextLoadSuccessful,
+ kTextEnterSaveGameName,
+ kTextGiveTo,
+ kTextUseWidth,
+ kTextNewSave,
+ kTextICantPickup,
+ kTextNothingSpecial,
+ kTextNoPlaceToOpen,
+ kTextNoOpening,
+ kTextDontKnow,
+ kTextShowDialog,
+ kTextEnterProtectAnswer
+};
+
+struct ImageHeader {
+ int width;
+ int height;
+};
+
+struct StringsTable {
+ byte *stringsPointer;
+ int stringsCount;
+ const char **strings;
+
+ const char *getString(int index) const {
+ if ((stringsCount <= index) || (index < 0)) {
+ error("StringList::getString wrong index 0x%X (%d)", index, stringsCount);
+ }
+ return strings[index];
+ }
+
+ void freeMem() {
+ free(strings);
+ free(stringsPointer);
+ memset(this, 0, sizeof(*this));
+ }
+
+ StringsTable() {
+ memset(this, 0, sizeof(*this));
+ }
+ ~StringsTable() {
+ freeMem();
+ }
+};
+
+enum GameIds {
+ // Dreamers Guild
+ GID_ITE_DEMO_G = 0,
+ GID_ITE_DISK_G,
+ GID_ITE_DISK_G2,
+ GID_ITE_CD_G,
+ GID_ITE_CD_G2,
+ GID_ITE_MACCD_G,
+
+ // Wyrmkeep
+ GID_ITE_CD, // data for Win rerelease is same as in old DOS
+ GID_ITE_WINCD, // but it has a bunch of patch files
+ GID_ITE_MACCD,
+ GID_ITE_LINCD,
+ GID_ITE_MULTICD, // Wyrmkeep combined Windows/Mac/Linux version
+ GID_ITE_WINDEMO1, // older Wyrmkeep windows demo
+ GID_ITE_MACDEMO1, // older Wyrmkeep mac demo
+ GID_ITE_LINDEMO,
+ GID_ITE_WINDEMO2,
+ GID_ITE_MACDEMO2,
+
+ // German
+ GID_ITE_DISK_DE,
+ GID_ITE_DISK_DE2,
+ GID_ITE_AMIGACD_DE, // TODO
+ GID_ITE_OLDMAC_DE, // TODO
+ GID_ITE_AMIGA_FL_DE,// TODO
+ GID_ITE_CD_DE, // reported by mld. Bestsellergamers cover disk
+ GID_ITE_CD_DE2,
+ GID_ITE_AMIGA_AGA_DEMO, // TODO
+ GID_ITE_AMIGA_ECS_DEMO, // TODO
+
+ GID_IHNM_DEMO,
+ GID_IHNM_CD,
+ GID_IHNM_CD_DE, // reported by mld. German retail
+ GID_IHNM_CD_ES,
+ GID_IHNM_CD_RU,
+ GID_IHNM_CD_FR
+};
+
+enum GameFileTypes {
+ GAME_RESOURCEFILE = 1 << 0,
+ GAME_SCRIPTFILE = 1 << 1,
+ GAME_SOUNDFILE = 1 << 2,
+ GAME_VOICEFILE = 1 << 3,
+ GAME_DEMOFILE = 1 << 4,
+ GAME_MUSICFILE = 1 << 5,
+ GAME_MUSICFILE_GM = 1 << 6,
+ GAME_MUSICFILE_FM = 1 << 7,
+ GAME_PATCHFILE = 1 << 8,
+ GAME_MACBINARY = 1 << 9,
+ GAME_SWAPENDIAN = 1 << 10
+};
+
+enum GameSoundTypes {
+ kSoundPCM = 0,
+ kSoundVOX = 1,
+ kSoundVOC = 2,
+ kSoundWAV = 3,
+ kSoundMacPCM = 4
+};
+
+enum GameFeatures {
+ GF_BIG_ENDIAN_DATA = 1 << 0,
+ GF_WYRMKEEP = 1 << 1,
+ GF_CD_FX = 1 << 2,
+ GF_SCENE_SUBSTITUTES = 1 << 3
+};
+
+enum ColorId {
+ kITEColorTransBlack = 0x00,
+ kITEColorBrightWhite = 0x01,
+ kITEColorWhite = 0x02,
+ kITEColorLightGrey = 0x04,
+ kITEColorGrey = 0x0a,
+ kITEColorDarkGrey = 0x0b,
+ kITEColorDarkGrey0C = 0x0C,
+ kITEColorBlack = 0x0f,
+ kITEColorRed = 0x65,
+ kITEColorDarkBlue8a = 0x8a,
+ kITEColorBlue89 = 0x89,
+ kITEColorLightBlue92 = 0x92,
+ kITEColorBlue = 0x93,
+ kITEColorLightBlue94 = 0x94,
+ kITEColorLightBlue96 = 0x96,
+ kITEColorGreen = 0xba,
+
+ kIHNMColorBlack = 0xfa,
+ kIHNMColorPortrait = 0xfe
+};
+
+enum KnownColor {
+ kKnownColorTransparent,
+ kKnownColorBrightWhite,
+ kKnownColorBlack,
+
+ kKnownColorSubtitleTextColor,
+ kKnownColorVerbText,
+ kKnownColorVerbTextShadow,
+ kKnownColorVerbTextActive
+};
+
+struct GameSoundInfo {
+ GameSoundTypes resourceType;
+ long frequency;
+ int sampleBits;
+ bool stereo;
+ bool isBigEndian;
+ bool isSigned;
+};
+
+struct GameFontDescription {
+ uint32 fontResourceId;
+};
+
+struct GameResourceDescription {
+ uint32 sceneLUTResourceId;
+ uint32 moduleLUTResourceId;
+ uint32 mainPanelResourceId;
+ uint32 conversePanelResourceId;
+ uint32 optionPanelResourceId;
+ uint32 mainSpritesResourceId;
+ uint32 mainPanelSpritesResourceId;
+ uint32 defaultPortraitsResourceId;
+ uint32 mainStringsResourceId;
+ uint32 actorsStringsResourceId;
+};
+
+struct GameFileDescription {
+ const char *fileName;
+ uint16 fileType;
+};
+
+struct GamePatchDescription {
+ const char *fileName;
+ uint16 fileType;
+ uint32 resourceId;
+ GameSoundInfo *soundInfo;
+};
+
+struct PanelButton {
+ PanelButtonType type;
+ int xOffset;
+ int yOffset;
+ int width;
+ int height;
+ int id;
+ uint16 ascii;
+ int state;
+ int upSpriteNumber;
+ int downSpriteNumber;
+ int overSpriteNumber;
+};
+
+struct GameDisplayInfo {
+ int logicalWidth;
+ int logicalHeight;
+
+ int pathStartY;
+ int sceneHeight;
+
+ int statusXOffset;
+ int statusYOffset;
+ int statusWidth;
+ int statusHeight;
+ int statusTextY;
+ int statusTextColor;
+ int statusBGColor;
+
+ int saveReminderXOffset;
+ int saveReminderYOffset;
+ int saveReminderWidth;
+ int saveReminderHeight;
+ int saveReminderFirstSpriteNumber;
+ int saveReminderSecondSpriteNumber;
+
+ int leftPortraitXOffset;
+ int leftPortraitYOffset;
+ int rightPortraitXOffset;
+ int rightPortraitYOffset;
+
+ int inventoryUpButtonIndex;
+ int inventoryDownButtonIndex;
+ int inventoryRows;
+ int inventoryColumns;
+
+ int mainPanelXOffset;
+ int mainPanelYOffset;
+ int mainPanelButtonsCount;
+ PanelButton *mainPanelButtons;
+
+ int converseMaxTextWidth;
+ int converseTextHeight;
+ int converseTextLines;
+ int converseUpButtonIndex;
+ int converseDownButtonIndex;
+
+ int conversePanelXOffset;
+ int conversePanelYOffset;
+ int conversePanelButtonsCount;
+ PanelButton *conversePanelButtons;
+
+ int optionSaveFilePanelIndex;
+ int optionSaveFileSliderIndex;
+ uint optionSaveFileVisible;
+
+ int optionPanelXOffset;
+ int optionPanelYOffset;
+ int optionPanelButtonsCount;
+ PanelButton *optionPanelButtons;
+
+ int quitPanelXOffset;
+ int quitPanelYOffset;
+ int quitPanelWidth;
+ int quitPanelHeight;
+ int quitPanelButtonsCount;
+ PanelButton *quitPanelButtons;
+
+ int loadPanelXOffset;
+ int loadPanelYOffset;
+ int loadPanelWidth;
+ int loadPanelHeight;
+ int loadPanelButtonsCount;
+ PanelButton *loadPanelButtons;
+
+ int saveEditIndex;
+ int savePanelXOffset;
+ int savePanelYOffset;
+ int savePanelWidth;
+ int savePanelHeight;
+ int savePanelButtonsCount;
+ PanelButton *savePanelButtons;
+
+ int protectEditIndex;
+ int protectPanelXOffset;
+ int protectPanelYOffset;
+ int protectPanelWidth;
+ int protectPanelHeight;
+ int protectPanelButtonsCount;
+ PanelButton *protectPanelButtons;
+};
+
+
+struct GameDescription {
+ const char *name;
+ SAGAGameType gameType;
+ GameIds gameId;
+ const char *title;
+ GameDisplayInfo *gameDisplayInfo;
+ int startSceneNumber;
+ GameResourceDescription *resourceDescription;
+ int filesCount;
+ GameFileDescription *filesDescriptions;
+ int fontsCount;
+ GameFontDescription *fontDescriptions;
+ GameSoundInfo *voiceInfo;
+ GameSoundInfo *sfxInfo;
+ GameSoundInfo *musicInfo;
+ int patchesCount;
+ GamePatchDescription *patchDescriptions;
+ uint32 features;
+ Common::Language language;
+ Common::Platform platform;
+
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { name, title, features };
+ return dummy;
+ }
+};
+
+struct SaveFileData {
+ char name[SAVE_TITLE_SIZE];
+ uint slotNumber;
+};
+
+struct SaveGameHeader {
+ uint32 type;
+ uint32 size;
+ uint32 version;
+ char name[SAVE_TITLE_SIZE];
+};
+
+inline int ticksToMSec(int tick) {
+ return tick * 1000 / kScriptTimeTicksPerSecond;
+}
+
+inline int clamp(int minValue, int value, int maxValue) {
+ if (value <= minValue) {
+ return minValue;
+ } else {
+ if (value >= maxValue) {
+ return maxValue;
+ } else {
+ return value;
+ }
+ }
+}
+
+inline int integerCompare(int i1, int i2) {
+ return ((i1) > (i2) ? 1 : ((i1) < (i2) ? -1 : 0));
+}
+
+inline int objectTypeId(uint16 objectId) {
+ return objectId >> OBJECT_TYPE_SHIFT;
+}
+
+inline int objectIdToIndex(uint16 objectId) {
+ return OBJECT_TYPE_MASK & objectId;
+}
+
+inline uint16 objectIndexToId(int type, int index) {
+ return (type << OBJECT_TYPE_SHIFT) | (OBJECT_TYPE_MASK & index);
+}
+
+
+DetectedGameList GAME_ProbeGame(const FSList &fslist, int **matches = NULL);
+
+class SagaEngine : public Engine {
+ friend class Scene;
+
+ void errorString(const char *buf_input, char *buf_output);
+
+protected:
+ int go();
+ int init(GameDetector &detector);
+public:
+ SagaEngine(GameDetector * detector, OSystem * syst);
+ virtual ~SagaEngine();
+ void shutDown() { _quit = true; }
+
+ void save(const char *fileName, const char *saveName);
+ void load(const char *fileName);
+ uint32 getCurrentLoadVersion() {
+ return _saveHeader.version;
+ }
+ void fillSaveList();
+ char *calcSaveFileName(uint slotNumber);
+
+ SaveFileData *getSaveFile(uint idx);
+ uint getSaveSlotNumber(uint idx);
+ uint getNewSaveSlotNumber();
+ bool locateSaveFile(char *saveName, uint &titleNumber);
+ bool isSaveListFull() const {
+ return _saveFilesMaxCount == _saveFilesCount;
+ }
+ uint getSaveFilesCount() const {
+ return isSaveListFull() ? _saveFilesCount : _saveFilesCount + 1;
+ }
+
+ int16 _framesEsc;
+
+ uint32 _globalFlags;
+ int16 _ethicsPoints[8];
+ int _spiritualBarometer;
+
+ int _soundVolume;
+ int _musicVolume;
+ bool _subtitlesEnabled;
+ int _readingSpeed;
+
+ bool _copyProtection;
+
+ SndRes *_sndRes;
+ Sound *_sound;
+ Music *_music;
+ Anim *_anim;
+ Render *_render;
+ IsoMap *_isoMap;
+ Gfx *_gfx;
+ Script *_script;
+ Actor *_actor;
+ Font *_font;
+ Sprite *_sprite;
+ Scene *_scene;
+ Interface *_interface;
+ Console *_console;
+ Events *_events;
+ PalAnim *_palanim;
+ Puzzle *_puzzle;
+ Resource *_resource;
+
+
+ /** Random number generator */
+ Common::RandomSource _rnd;
+
+private:
+ int decodeBGImageRLE(const byte *inbuf, size_t inbuf_len, byte *outbuf, size_t outbuf_len);
+ int flipImage(byte *img_buf, int columns, int scanlines);
+ int unbankBGImage(byte *dest_buf, const byte *src_buf, int columns, int scanlines);
+ uint32 _previousTicks;
+
+public:
+ int decodeBGImage(const byte *image_data, size_t image_size,
+ byte **output_buf, size_t *output_buf_len, int *w, int *h, bool flip = false);
+ const byte *getImagePal(const byte *image_data, size_t image_size);
+ void loadStrings(StringsTable &stringsTable, const byte *stringsPointer, size_t stringsLength);
+
+ const char *getObjectName(uint16 objectId);
+public:
+ int processInput(void);
+ const Point &mousePos() const {
+ return _mousePos;
+ }
+
+ const bool leftMouseButtonPressed() const {
+ return _leftMouseButtonPressed;
+ }
+
+ const bool rightMouseButtonPressed() const {
+ return _rightMouseButtonPressed;
+ }
+
+ const bool mouseButtonPressed() const {
+ return _leftMouseButtonPressed || _rightMouseButtonPressed;
+ }
+
+ private:
+ Common::String _targetName;
+
+ uint _saveFilesMaxCount;
+ uint _saveFilesCount;
+ SaveFileData _saveFiles[MAX_SAVES];
+ bool _saveMarks[MAX_SAVES];
+ SaveGameHeader _saveHeader;
+
+ Point _mousePos;
+ bool _leftMouseButtonPressed;
+ bool _rightMouseButtonPressed;
+
+ bool _quit;
+
+//current game description
+ int _gameNumber;
+ GameDescription *_gameDescription;
+ Common::Rect _displayClip;
+
+protected:
+ GameDisplayInfo _gameDisplayInfo;
+
+public:
+ int32 _frameCount;
+
+public:
+ bool initGame(void);
+public:
+ const GameDescription *getGameDescription() const { return _gameDescription; }
+ const bool isBigEndian() const { return (_gameDescription->features & GF_BIG_ENDIAN_DATA) != 0; }
+ const bool isMacResources() const { return (getPlatform() == Common::kPlatformMacintosh); }
+ const GameResourceDescription *getResourceDescription() { return _gameDescription->resourceDescription; }
+ const GameSoundInfo *getVoiceInfo() const { return _gameDescription->voiceInfo; }
+ const GameSoundInfo *getSfxInfo() const { return _gameDescription->sfxInfo; }
+ const GameSoundInfo *getMusicInfo() const { return _gameDescription->musicInfo; }
+
+ const GameFontDescription *getFontDescription(int index) {
+ assert(index < _gameDescription->fontsCount);
+ return &_gameDescription->fontDescriptions[index];
+ }
+ int getFontsCount() const { return _gameDescription->fontsCount; }
+
+ int getGameId() const { return _gameDescription->gameId; }
+ int getGameType() const { return _gameDescription->gameType; }
+ uint32 getFeatures() const { return _gameDescription->features; }
+ Common::Language getLanguage() const { return _gameDescription->language; }
+ Common::Platform getPlatform() const { return _gameDescription->platform; }
+ int getGameNumber() const { return _gameNumber; }
+ int getStartSceneNumber() const { return _gameDescription->startSceneNumber; }
+
+
+ const Common::Rect &getDisplayClip() const { return _displayClip;}
+ int getDisplayWidth() const { return _gameDisplayInfo.logicalWidth; }
+ int getDisplayHeight() const { return _gameDisplayInfo.logicalHeight;}
+ const GameDisplayInfo & getDisplayInfo() { return _gameDisplayInfo; }
+
+ const char *getTextString(int textStringId);
+ void getExcuseInfo(int verb, const char *&textString, int &soundResourceId);
+
+private:
+
+public:
+ ColorId KnownColor2ColorId(KnownColor knownColor);
+};
+
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/saveload.cpp b/engines/saga/saveload.cpp
new file mode 100644
index 0000000000..761ae49521
--- /dev/null
+++ b/engines/saga/saveload.cpp
@@ -0,0 +1,299 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "common/file.h"
+
+#include "saga/saga.h"
+#include "saga/actor.h"
+#include "saga/events.h"
+#include "saga/interface.h"
+#include "saga/isomap.h"
+#include "saga/music.h"
+#include "saga/render.h"
+#include "saga/resnames.h"
+#include "saga/scene.h"
+#include "saga/script.h"
+
+#define CURRENT_SAGA_VER 5
+
+namespace Saga {
+
+static SaveFileData emptySlot = {
+ "", 0
+};
+
+//TODO:
+// - delete savegame
+
+char* SagaEngine::calcSaveFileName(uint slotNumber) {
+ static char name[MAX_FILE_NAME];
+ sprintf(name, "%s.s%02d", _targetName.c_str(), slotNumber);
+ return name;
+}
+
+SaveFileData *SagaEngine::getSaveFile(uint idx) {
+ if (idx >= _saveFilesMaxCount) {
+ error("getSaveFileName wrong idx");
+ }
+ if (isSaveListFull()) {
+ return &_saveFiles[_saveFilesCount - idx - 1];
+ } else {
+ if (!emptySlot.name[0])
+ strcpy(emptySlot.name, getTextString(kTextNewSave));
+
+ return (idx == 0) ? &emptySlot : &_saveFiles[_saveFilesCount - idx];
+ }
+}
+
+bool SagaEngine::locateSaveFile(char *saveName, uint &titleNumber) {
+ uint i;
+ for (i = 0; i < _saveFilesCount; i++) {
+ if (strcmp(saveName, _saveFiles[i].name) == 0) {
+ if (isSaveListFull()) {
+ titleNumber = _saveFilesCount - i - 1;
+ } else {
+ titleNumber = _saveFilesCount - i;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+uint SagaEngine::getNewSaveSlotNumber() {
+ uint i, j;
+ bool found;
+ if (isSaveListFull()) {
+ error("getNewSaveSlotNumber save list is full");
+ }
+ for (i = 0; i < MAX_SAVES; i++) {
+ if (_saveMarks[i]) {
+ found = false;
+ for (j = 0; j < _saveFilesCount; j++) {
+ if (_saveFiles[j].slotNumber == i) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return i;
+ }
+ }
+ }
+
+ error("getNewSaveSlotNumber save list is full");
+}
+
+void SagaEngine::fillSaveList() {
+ int i;
+ Common::InSaveFile *in;
+ char *name;
+
+ name = calcSaveFileName(MAX_SAVES);
+ name[strlen(name) - 2] = 0;
+ _saveFileMan->listSavefiles(name, _saveMarks, MAX_SAVES);
+
+ _saveFilesMaxCount = 0;
+ for (i = 0; i < MAX_SAVES; i++) {
+ if (_saveMarks[i]) {
+ _saveFilesMaxCount++;
+ }
+ _saveFiles[i].name[0] = 0;
+ _saveFiles[i].slotNumber = (uint)-1;
+ }
+
+ _saveFilesCount = 0;
+
+ i = 0;
+ while (i < MAX_SAVES) {
+ if (_saveMarks[i]) {
+ name = calcSaveFileName(i);
+ if ((in = _saveFileMan->openForLoading(name)) != NULL) {
+ in->read(&_saveHeader, sizeof(_saveHeader));
+
+ if (_saveHeader.type != MKID('SAGA')) {
+ error("SagaEngine::load wrong format");
+ }
+ strcpy(_saveFiles[_saveFilesCount].name, _saveHeader.name);
+ _saveFiles[_saveFilesCount].slotNumber = i;
+ delete in;
+ _saveFilesCount++;
+ }
+ }
+ i++;
+ }
+/* 4debug
+ for (i = 0; i < 14; i++) {
+ sprintf(_saveFiles[i].name,"test%i", i);
+ _saveFiles[i].slotNumber = i;
+ }
+ _saveFilesCount = 14;
+ _saveFilesMaxCount = 14;
+ */
+}
+
+
+#define TITLESIZE 80
+void SagaEngine::save(const char *fileName, const char *saveName) {
+ Common::OutSaveFile *out;
+ char title[TITLESIZE];
+
+ if (!(out = _saveFileMan->openForSaving(fileName))) {
+ return;
+ }
+
+ _saveHeader.type = MKID('SAGA');
+ _saveHeader.size = 0;
+ _saveHeader.version = TO_LE_32(CURRENT_SAGA_VER);
+ strncpy(_saveHeader.name, saveName, SAVE_TITLE_SIZE);
+
+ out->write(&_saveHeader, sizeof(_saveHeader));
+
+ // Original game title
+ memset(title, 0, TITLESIZE);
+ strncpy(title, getGameDescription()->title, TITLESIZE);
+ out->write(title, TITLESIZE);
+
+ // Surrounding scene
+ out->writeSint32LE(_scene->getOutsetSceneNumber());
+
+ // Inset scene
+ out->writeSint32LE(_scene->currentSceneNumber());
+
+ if (getGameType() != GType_ITE) {
+ out->writeUint32LE(_globalFlags);
+ for (int i = 0; i < ARRAYSIZE(_ethicsPoints); i++)
+ out->writeSint16LE(_ethicsPoints[i]);
+ }
+
+ _interface->saveState(out);
+
+ _actor->saveState(out);
+
+ out->writeSint16LE(_script->_commonBufferSize);
+
+ out->write(_script->_commonBuffer, _script->_commonBufferSize);
+
+ out->writeSint16LE(_isoMap->getMapPosition().x);
+ out->writeSint16LE(_isoMap->getMapPosition().y);
+
+ out->flush();
+
+ // TODO: Check out->ioFailed()
+
+ delete out;
+}
+
+void SagaEngine::load(const char *fileName) {
+ Common::InSaveFile *in;
+ int commonBufferSize;
+ int sceneNumber, insetSceneNumber;
+ int mapx, mapy;
+ char title[TITLESIZE];
+
+ if (!(in = _saveFileMan->openForLoading(fileName))) {
+ return;
+ }
+
+ in->read(&_saveHeader, sizeof(_saveHeader));
+
+ _saveHeader.size = FROM_LE_32(_saveHeader.size);
+ _saveHeader.version = FROM_LE_32(_saveHeader.version);
+
+ // This save was written in native endianness (fix that, so warning will show up)
+ if (_saveHeader.version > CURRENT_SAGA_VER) {
+#ifdef SCUMM_LITTLE_ENDIAN
+ _saveHeader.version = TO_BE_32(_saveHeader.version);
+#else
+ _saveHeader.version = TO_LE_32(_saveHeader.version);
+#endif
+ }
+
+ debug(2, "Save version: %x", _saveHeader.version);
+
+ if (_saveHeader.version < 4)
+ warning("This savegame is not endian-safe. There may be problems");
+
+ if (_saveHeader.type != MKID('SAGA')) {
+ error("SagaEngine::load wrong format");
+ }
+
+ if (_saveHeader.version > 4) {
+ in->read(title, TITLESIZE);
+ debug(0, "Save is for: %s", title);
+ }
+
+ // Surrounding scene
+ sceneNumber = in->readSint32LE();
+
+ // Inset scene
+ insetSceneNumber = in->readSint32LE();
+
+ if (getGameType() != GType_ITE) {
+ _globalFlags = in->readUint32LE();
+ for (int i = 0; i < ARRAYSIZE(_ethicsPoints); i++)
+ _ethicsPoints[i] = in->readSint16LE();
+ }
+
+ _interface->loadState(in);
+
+ _actor->loadState(in);
+
+ commonBufferSize = in->readSint16LE();
+ in->read(_script->_commonBuffer, commonBufferSize);
+
+ mapx = in->readSint16LE();
+ mapy = in->readSint16LE();
+
+ delete in;
+
+ // Mute volume to prevent outScene music play
+ int volume = _music->getVolume();
+ _music->setVolume(0);
+
+ _isoMap->setMapPosition(mapx, mapy);
+
+ _scene->clearSceneQueue();
+ _scene->changeScene(sceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade);
+
+ _events->handleEvents(0); //dissolve backgrounds
+
+ if (insetSceneNumber != sceneNumber) {
+ _render->setFlag(RF_DISABLE_ACTORS);
+ _render->drawScene();
+ _render->clearFlag(RF_DISABLE_ACTORS);
+ _scene->changeScene(insetSceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade);
+ }
+
+ _music->setVolume(volume);
+
+ _interface->draw();
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp
new file mode 100644
index 0000000000..708800dc41
--- /dev/null
+++ b/engines/saga/scene.cpp
@@ -0,0 +1,1283 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Scene management module
+#include "saga/saga.h"
+
+#include "saga/gfx.h"
+#include "saga/animation.h"
+#include "saga/console.h"
+#include "saga/interface.h"
+#include "saga/events.h"
+#include "saga/isomap.h"
+#include "saga/objectmap.h"
+#include "saga/palanim.h"
+#include "saga/puzzle.h"
+#include "saga/render.h"
+#include "saga/script.h"
+#include "saga/sound.h"
+#include "saga/music.h"
+
+#include "saga/scene.h"
+#include "saga/stream.h"
+#include "saga/actor.h"
+#include "saga/rscfile.h"
+#include "saga/resnames.h"
+
+#include "graphics/ilbm.h"
+#include "common/util.h"
+
+namespace Saga {
+
+static int initSceneDoors[SCENE_DOORS_MAX] = {
+ 0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static SAGAResourceTypes ITESceneResourceTypes[26] = {
+ SAGA_ACTOR,
+ SAGA_OBJECT,
+ SAGA_BG_IMAGE,
+ SAGA_BG_MASK,
+SAGA_UNKNOWN,
+ SAGA_STRINGS,
+ SAGA_OBJECT_MAP,
+ SAGA_ACTION_MAP,
+ SAGA_ISO_IMAGES,
+ SAGA_ISO_MAP,
+ SAGA_ISO_PLATFORMS,
+ SAGA_ISO_METATILES,
+ SAGA_ENTRY,
+SAGA_UNKNOWN,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ISO_MULTI,
+ SAGA_PAL_ANIM,
+ SAGA_FACES,
+ SAGA_PALETTE
+};
+
+static SAGAResourceTypes IHNMSceneResourceTypes[28] = {
+ SAGA_ACTOR,
+SAGA_UNKNOWN,
+ SAGA_BG_IMAGE,
+ SAGA_BG_MASK,
+SAGA_UNKNOWN,
+ SAGA_STRINGS,
+ SAGA_OBJECT_MAP,
+ SAGA_ACTION_MAP,
+ SAGA_ISO_IMAGES,
+ SAGA_ISO_MAP,
+ SAGA_ISO_PLATFORMS,
+ SAGA_ISO_METATILES,
+ SAGA_ENTRY,
+SAGA_UNKNOWN,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ANIM,
+ SAGA_ISO_MULTI,
+ SAGA_PAL_ANIM,
+ SAGA_FACES,
+ SAGA_PALETTE
+};
+
+const char *SAGAResourceTypesString[] = {
+ "SAGA_UNKNOWN",
+ "SAGA_ACTOR",
+ "SAGA_OBJECT",
+ "SAGA_BG_IMAGE",
+ "SAGA_BG_MASK",
+ "SAGA_STRINGS",
+ "SAGA_OBJECT_MAP",
+ "SAGA_ACTION_MAP",
+ "SAGA_ISO_IMAGES",
+ "SAGA_ISO_MAP",
+ "SAGA_ISO_PLATFORMS",
+ "SAGA_ISO_METATILES",
+ "SAGA_ENTRY",
+ "SAGA_ANIM",
+ "SAGA_ISO_MULTI",
+ "SAGA_PAL_ANIM",
+ "SAGA_FACES",
+ "SAGA_PALETTE"
+};
+
+Scene::Scene(SagaEngine *vm) : _vm(vm) {
+ byte *sceneLUTPointer;
+ size_t sceneLUTLength;
+ uint32 resourceId;
+ int i;
+
+ // Load scene module resource context
+ _sceneContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ if (_sceneContext == NULL) {
+ error("Scene::Scene() scene context not found");
+ }
+
+ // Load scene lookup table
+ resourceId = _vm->_resource->convertResourceId(_vm->getResourceDescription()->sceneLUTResourceId);
+ debug(3, "Loading scene LUT from resource %i", resourceId);
+ _vm->_resource->loadResource(_sceneContext, resourceId, sceneLUTPointer, sceneLUTLength);
+ if (sceneLUTLength == 0) {
+ error("Scene::Scene() sceneLUTLength == 0");
+ }
+ _sceneCount = sceneLUTLength / 2;
+ _sceneLUT = (int *)malloc(_sceneCount * sizeof(*_sceneLUT));
+ if (_sceneLUT == NULL) {
+ memoryError("Scene::Scene()");
+ }
+
+ MemoryReadStreamEndian readS(sceneLUTPointer, sceneLUTLength, _sceneContext->isBigEndian);
+
+ for (i = 0; i < _sceneCount; i++) {
+ _sceneLUT[i] = readS.readUint16();
+ debug(8, "sceneNumber %i has resourceId %i", i, _sceneLUT[i]);
+ }
+
+ free(sceneLUTPointer);
+
+#define DUMP_SCENES_LEVEL 10
+
+ if (DUMP_SCENES_LEVEL <= gDebugLevel) {
+ uint j;
+ int backUpDebugLevel = gDebugLevel;
+ SAGAResourceTypes *types;
+ int typesCount;
+ SAGAResourceTypes resType;
+
+ getResourceTypes(types, typesCount);
+
+ for (i = 0; i < _sceneCount; i++) {
+ gDebugLevel = -1;
+ loadSceneDescriptor(_sceneLUT[i]);
+ loadSceneResourceList(_sceneDescription.resourceListResourceId);
+ gDebugLevel = backUpDebugLevel;
+ debug(DUMP_SCENES_LEVEL, "Dump Scene: number %i, descriptor resourceId %i, resourceList resourceId %i", i, _sceneLUT[i], _sceneDescription.resourceListResourceId);
+ debug(DUMP_SCENES_LEVEL, "\tresourceListCount %i", _resourceListCount);
+ for (j = 0; j < _resourceListCount; j++) {
+ if (_resourceList[j].resourceType >= typesCount) {
+ error("wrong resource type %i", _resourceList[j].resourceType);
+ }
+ resType = types[_resourceList[j].resourceType];
+
+ debug(DUMP_SCENES_LEVEL, "\t%s resourceId %i", SAGAResourceTypesString[resType], _resourceList[j].resourceId);
+ }
+ free(_resourceList);
+ }
+ }
+
+
+ debug(3, "LUT has %d entries.", _sceneCount);
+
+ _sceneLoaded = false;
+ _sceneNumber = 0;
+ _sceneResourceId = 0;
+ _inGame = false;
+ _loadDescription = false;
+ memset(&_sceneDescription, 0, sizeof(_sceneDescription));
+ _resourceListCount = 0;
+ _resourceList = NULL;
+ _sceneProc = NULL;
+ _objectMap = new ObjectMap(_vm);
+ _actionMap = new ObjectMap(_vm);
+ memset(&_bg, 0, sizeof(_bg));
+ memset(&_bgMask, 0, sizeof(_bgMask));
+}
+
+Scene::~Scene() {
+ delete _actionMap;
+ delete _objectMap;
+ free(_sceneLUT);
+}
+
+void Scene::getResourceTypes(SAGAResourceTypes *&types, int &typesCount) {
+ if (_vm->getGameType() == GType_IHNM) {
+ typesCount = ARRAYSIZE(IHNMSceneResourceTypes);
+ types = IHNMSceneResourceTypes;
+ } else {
+ typesCount = ARRAYSIZE(ITESceneResourceTypes);
+ types = ITESceneResourceTypes;
+ }
+}
+
+void Scene::drawTextList(Surface *ds) {
+ TextListEntry *entry;
+
+ for (TextList::iterator textIterator = _textList.begin(); textIterator != _textList.end(); ++textIterator) {
+ entry = (TextListEntry *)textIterator.operator->();
+ if (entry->display) {
+
+ if (entry->useRect) {
+ _vm->_font->textDrawRect(entry->font, ds, entry->text, entry->rect, _vm->KnownColor2ColorId(entry->knownColor), _vm->KnownColor2ColorId(entry->effectKnownColor), entry->flags);
+ } else {
+ _vm->_font->textDraw(entry->font, ds, entry->text, entry->point, _vm->KnownColor2ColorId(entry->knownColor), _vm->KnownColor2ColorId(entry->effectKnownColor), entry->flags);
+ }
+ }
+ }
+}
+
+void Scene::startScene() {
+ SceneQueueList::iterator queueIterator;
+ LoadSceneParams *sceneQueue;
+ Event event;
+
+ if (_sceneLoaded) {
+ error("Scene::start(): Error: Can't start game...scene already loaded");
+ }
+
+ if (_inGame) {
+ error("Scene::start(): Error: Can't start game...game already started");
+ }
+
+ // Hide cursor during intro
+ event.type = kEvTOneshot;
+ event.code = kCursorEvent;
+ event.op = kEventHide;
+ _vm->_events->queue(&event);
+
+ switch (_vm->getGameType()) {
+ case GType_ITE:
+ ITEStartProc();
+ break;
+ case GType_IHNM:
+ IHNMStartProc();
+ break;
+ default:
+ error("Scene::start(): Error: Can't start game... gametype not supported");
+ break;
+ }
+
+ // Load the head in scene queue
+ queueIterator = _sceneQueue.begin();
+ if (queueIterator == _sceneQueue.end()) {
+ return;
+ }
+
+ sceneQueue = queueIterator.operator->();
+
+ loadScene(sceneQueue);
+}
+
+void Scene::nextScene() {
+ SceneQueueList::iterator queueIterator;
+ LoadSceneParams *sceneQueue;
+
+ if (!_sceneLoaded) {
+ error("Scene::next(): Error: Can't advance scene...no scene loaded");
+ }
+
+ if (_inGame) {
+ error("Scene::next(): Error: Can't advance scene...game already started");
+ }
+
+ endScene();
+
+ // Delete the current head in scene queue
+ queueIterator = _sceneQueue.begin();
+ if (queueIterator == _sceneQueue.end()) {
+ return;
+ }
+
+ queueIterator = _sceneQueue.erase(queueIterator);
+
+ if (queueIterator == _sceneQueue.end()) {
+ return;
+ }
+
+ // Load the head in scene queue
+ sceneQueue = queueIterator.operator->();
+
+ loadScene(sceneQueue);
+}
+
+void Scene::skipScene() {
+ SceneQueueList::iterator queueIterator;
+
+ LoadSceneParams *sceneQueue = NULL;
+ LoadSceneParams *skipQueue = NULL;
+
+ if (!_sceneLoaded) {
+ error("Scene::skip(): Error: Can't skip scene...no scene loaded");
+ }
+
+ if (_inGame) {
+ error("Scene::skip(): Error: Can't skip scene...game already started");
+ }
+
+ // Walk down scene queue and try to find a skip target
+ queueIterator = _sceneQueue.begin();
+ if (queueIterator == _sceneQueue.end()) {
+ error("Scene::skip(): Error: Can't skip scene...no scenes in queue");
+ }
+
+ ++queueIterator;
+ while (queueIterator != _sceneQueue.end()) {
+ sceneQueue = queueIterator.operator->();
+ assert(sceneQueue != NULL);
+
+ if (sceneQueue->sceneSkipTarget) {
+ skipQueue = sceneQueue;
+ break;
+ }
+ ++queueIterator;
+ }
+
+ // If skip target found, remove preceding scenes and load
+ if (skipQueue != NULL) {
+ _sceneQueue.erase(_sceneQueue.begin(), queueIterator);
+
+ endScene();
+ loadScene(skipQueue);
+ }
+}
+
+static struct SceneSubstitutes {
+ int sceneId;
+ const char *message;
+ const char *title;
+ const char *image;
+} sceneSubstitutes[] = {
+ {
+ 7,
+ "Tycho says he knows much about the northern lands. Can Rif convince "
+ "the Dog to share this knowledge?",
+ "The Home of Tycho Northpaw",
+ "tycho.bbm"
+ },
+
+ {
+ 27,
+ "The scene of the crime may hold many clues, but will the servants of "
+ "the Sanctuary trust Rif?",
+ "The Sanctuary of the Orb",
+ "sanctuar.bbm"
+ },
+
+ {
+ 5,
+ "The Rats hold many secrets that could guide Rif on his quest -- assuming "
+ "he can get past the doorkeeper.",
+ "The Rat Complex",
+ "ratdoor.bbm"
+ },
+
+ {
+ 2,
+ "The Ferrets enjoy making things and have the materials to do so. How can "
+ "that help Rif?",
+ "The Ferret Village",
+ "ferrets.bbm"
+ },
+
+ {
+ 67,
+ "What aid can the noble King of the Elks provide to Rif and his companions?",
+ "The Realm of the Forest King",
+ "elkenter.bbm"
+ },
+
+ {
+ 3,
+ "The King holds Rif's sweetheart hostage. Will the Boar provide any "
+ "assistance to Rif?",
+ "The Great Hall of the Boar King",
+ "boarhall.bbm"
+ }
+};
+
+void Scene::changeScene(int16 sceneNumber, int actorsEntrance, SceneTransitionType transitionType, int chapter) {
+
+ debug(5, "Scene::changeScene(%d, %d, %d, %d)", sceneNumber, actorsEntrance, transitionType, chapter);
+
+ // This is used for latter ITE demos where all places on world map except
+ // Tent Faire are substituted with LBM picture and short description
+ if (_vm->getFeatures() & GF_SCENE_SUBSTITUTES) {
+ for (int i = 0; i < ARRAYSIZE(sceneSubstitutes); i++) {
+ if (sceneSubstitutes[i].sceneId == sceneNumber) {
+ Surface *backBuffer = _vm->_gfx->getBackBuffer();
+ Surface bbmBuffer;
+ byte *pal, *colors;
+ Common::File file;
+ Rect rect;
+ PalEntry cPal[PAL_ENTRIES];
+
+ _vm->_interface->setMode(kPanelSceneSubstitute);
+
+ if (file.open(sceneSubstitutes[i].image)) {
+ Graphics::decodeILBM(file, bbmBuffer, pal);
+ colors = pal;
+ rect.setWidth(bbmBuffer.w);
+ rect.setHeight(bbmBuffer.h);
+ backBuffer->blit(rect, (const byte*)bbmBuffer.pixels);
+ for (int j = 0; j < PAL_ENTRIES; j++) {
+ cPal[j].red = *pal++;
+ cPal[j].green = *pal++;
+ cPal[j].blue = *pal++;
+ }
+ free(colors);
+ _vm->_gfx->setPalette(cPal);
+
+ }
+
+ _vm->_interface->setStatusText("Click or Press Return to continue. Press Q to quit.", 96);
+ _vm->_font->textDrawRect(kKnownFontMedium, backBuffer, sceneSubstitutes[i].title,
+ Common::Rect(0, 7, _vm->getDisplayWidth(), 27), _vm->KnownColor2ColorId(kKnownColorBrightWhite), _vm->KnownColor2ColorId(kKnownColorBlack), kFontOutline);
+ _vm->_font->textDrawRect(kKnownFontMedium, backBuffer, sceneSubstitutes[i].message,
+ Common::Rect(24, getHeight() - 33, _vm->getDisplayWidth() - 11,
+ getHeight()), _vm->KnownColor2ColorId(kKnownColorBrightWhite), _vm->KnownColor2ColorId(kKnownColorBlack), kFontOutline);
+ return;
+ }
+ }
+ }
+
+ LoadSceneParams sceneParams;
+
+ sceneParams.actorsEntrance = actorsEntrance;
+ sceneParams.loadFlag = kLoadBySceneNumber;
+ sceneParams.sceneDescriptor = sceneNumber;
+ sceneParams.transitionType = transitionType;
+ sceneParams.sceneProc = NULL;
+ sceneParams.sceneSkipTarget = false;
+ sceneParams.chapter = chapter;
+
+ if (sceneNumber != -2) {
+ endScene();
+ }
+ loadScene(&sceneParams);
+}
+
+void Scene::getSlopes(int &beginSlope, int &endSlope) {
+ beginSlope = getHeight() - _sceneDescription.beginSlope;
+ endSlope = getHeight() - _sceneDescription.endSlope;
+}
+
+void Scene::getBGInfo(BGInfo &bgInfo) {
+ bgInfo.buffer = _bg.buf;
+ bgInfo.bufferLength = _bg.buf_len;
+ bgInfo.bounds.left = 0;
+ bgInfo.bounds.top = 0;
+
+ if (_bg.w < _vm->getDisplayWidth()) {
+ bgInfo.bounds.left = (_vm->getDisplayWidth() - _bg.w) / 2;
+ }
+
+ if (_bg.h < getHeight()) {
+ bgInfo.bounds.top = (getHeight() - _bg.h) / 2;
+ }
+
+ bgInfo.bounds.setWidth(_bg.w);
+ bgInfo.bounds.setHeight(_bg.h);
+}
+
+int Scene::getBGMaskType(const Point &testPoint) {
+ uint offset;
+ if (!_bgMask.loaded) {
+ return 0;
+ }
+ offset = testPoint.x + testPoint.y * _bgMask.w;
+ if (offset >= _bgMask.buf_len) {
+ error("Scene::getBGMaskType offset 0x%X exceed bufferLength 0x%X", offset, _bgMask.buf_len);
+ }
+
+ return (_bgMask.buf[offset] >> 4) & 0x0f;
+}
+
+bool Scene::validBGMaskPoint(const Point &testPoint) {
+ if (!_bgMask.loaded) {
+ error("Scene::validBGMaskPoint _bgMask not loaded");
+ }
+
+ return !((testPoint.x < 0) || (testPoint.x >= _bgMask.w) ||
+ (testPoint.y < 0) || (testPoint.y >= _bgMask.h));
+}
+
+bool Scene::canWalk(const Point &testPoint) {
+ int maskType;
+
+ if (!_bgMask.loaded) {
+ return true;
+ }
+ if (!validBGMaskPoint(testPoint)) {
+ return true;
+ }
+
+ maskType = getBGMaskType(testPoint);
+ return getDoorState(maskType) == 0;
+}
+
+bool Scene::offscreenPath(Point &testPoint) {
+ Point point;
+
+ if (!_bgMask.loaded) {
+ return false;
+ }
+
+ point.x = clamp( 0, testPoint.x, _bgMask.w - 1 );
+ point.y = clamp( 0, testPoint.y, _bgMask.h - 1 );
+ if (point == testPoint) {
+ return false;
+ }
+
+ if (point.y >= _bgMask.h - 1) {
+ point.y = _bgMask.h - 2;
+ }
+ testPoint = point;
+
+ return true;
+}
+
+
+void Scene::getBGMaskInfo(int &width, int &height, byte *&buffer, size_t &bufferLength) {
+ if (!_bgMask.loaded) {
+ error("Scene::getBGMaskInfo _bgMask not loaded");
+ }
+
+ width = _bgMask.w;
+ height = _bgMask.h;
+ buffer = _bgMask.buf;
+ bufferLength = _bgMask.buf_len;
+}
+
+void Scene::setDoorState(int doorNumber, int doorState) {
+ if ((doorNumber < 0) || (doorNumber >= SCENE_DOORS_MAX))
+ error("Scene::setDoorState wrong doorNumber");
+
+ _sceneDoors[doorNumber] = doorState;
+}
+
+int Scene::getDoorState(int doorNumber) {
+ if ((doorNumber < 0) || (doorNumber >= SCENE_DOORS_MAX))
+ error("Scene::getDoorState wrong doorNumber");
+
+ return _sceneDoors[doorNumber];
+}
+
+void Scene::initDoorsState() {
+ memcpy(_sceneDoors, initSceneDoors, sizeof (_sceneDoors) );
+}
+
+void Scene::loadScene(LoadSceneParams *loadSceneParams) {
+ size_t i;
+ Event event;
+ Event *q_event;
+ static PalEntry current_pal[PAL_ENTRIES];
+
+ if ((_vm->getGameType() == GType_IHNM) && (loadSceneParams->chapter != NO_CHAPTER_CHANGE)) {
+ if (loadSceneParams->loadFlag != kLoadBySceneNumber) {
+ error("loadScene wrong usage");
+ }
+
+ if (loadSceneParams->chapter == 6)
+ _vm->_interface->setLeftPortrait(0);
+
+ _vm->_anim->freeCutawayList();
+ _vm->_script->freeModules();
+ // deleteAllScenes();
+
+ // installSomeAlarm()
+
+ _vm->_interface->clearInventory();
+ _vm->_resource->loadGlobalResources(loadSceneParams->chapter, loadSceneParams->actorsEntrance);
+ _vm->_interface->addToInventory(IHNM_OBJ_PROFILE);
+ _vm->_interface->activate();
+
+ if (loadSceneParams->chapter == 8 || loadSceneParams->chapter == -1)
+ _vm->_interface->setMode(kPanelChapterSelection);
+ else
+ _vm->_interface->setMode(kPanelMain);
+
+ _inGame = true;
+
+ _vm->_script->setVerb(_vm->_script->getVerbType(kVerbWalkTo));
+
+ if (loadSceneParams->sceneDescriptor == -2) {
+ return;
+ }
+ }
+
+ if (_sceneLoaded) {
+ error("Scene::loadScene(): Error, a scene is already loaded");
+ }
+
+ _loadDescription = true;
+
+ if (_vm->getGameType() == GType_IHNM) {
+ if (loadSceneParams->loadFlag == kLoadBySceneNumber) // When will we get rid of it?
+ if (loadSceneParams->sceneDescriptor <= 0)
+ loadSceneParams->sceneDescriptor = _vm->_resource->_metaResource.sceneIndex;
+ }
+
+ switch (loadSceneParams->loadFlag) {
+ case kLoadByResourceId:
+ _sceneNumber = 0; // original assign zero for loaded by resource id
+ _sceneResourceId = loadSceneParams->sceneDescriptor;
+ break;
+ case kLoadBySceneNumber:
+ _sceneNumber = loadSceneParams->sceneDescriptor;
+ _sceneResourceId = getSceneResourceId(_sceneNumber);
+ break;
+ case kLoadByDescription:
+ _sceneNumber = -1;
+ _sceneResourceId = -1;
+ assert(loadSceneParams->sceneDescription != NULL);
+ assert(loadSceneParams->sceneDescription->resourceList != NULL);
+ _loadDescription = false;
+ _sceneDescription = *loadSceneParams->sceneDescription;
+ _resourceList = loadSceneParams->sceneDescription->resourceList;
+ _resourceListCount = loadSceneParams->sceneDescription->resourceListCount;
+ break;
+ }
+
+ debug(3, "Loading scene number %d:", _sceneNumber);
+
+ // Load scene descriptor and resource list resources
+ if (_loadDescription) {
+ debug(3, "Loading scene resource %i", _sceneResourceId);
+
+ loadSceneDescriptor(_sceneResourceId);
+
+ loadSceneResourceList(_sceneDescription.resourceListResourceId);
+ } else {
+ debug(3, "Loading memory scene resource");
+ }
+
+ // Load resources from scene resource list
+ for (i = 0; i < _resourceListCount; i++) {
+ if (!_resourceList[i].invalid) {
+ _vm->_resource->loadResource(_sceneContext, _resourceList[i].resourceId,
+ _resourceList[i].buffer, _resourceList[i].size);
+
+
+ if (_resourceList[i].size >= 6) {
+ if (!memcmp(_resourceList[i].buffer, "DUMMY!", 6)) {
+ _resourceList[i].invalid = true;
+ warning("DUMMY resource %i", _resourceList[i].resourceId);
+ }
+ }
+ }
+ }
+
+ // Process resources from scene resource list
+ processSceneResources();
+
+ if (_sceneDescription.flags & kSceneFlagISO) {
+ _outsetSceneNumber = _sceneNumber;
+
+ _sceneClip.left = 0;
+ _sceneClip.top = 0;
+ _sceneClip.right = _vm->getDisplayWidth();
+ _sceneClip.bottom = getHeight();
+ } else {
+ BGInfo backGroundInfo;
+ getBGInfo(backGroundInfo);
+ _sceneClip = backGroundInfo.bounds;
+ if (!(_bg.w < _vm->getDisplayWidth() || _bg.h < getHeight()))
+ _outsetSceneNumber = _sceneNumber;
+ }
+
+ _sceneLoaded = true;
+
+ q_event = NULL;
+
+ //fix placard bug
+ //i guess we should remove RF_PLACARD flag - and use _interface->getMode()
+ event.type = kEvTOneshot;
+ event.code = kGraphicsEvent;
+ event.op = kEventClearFlag;
+ event.param = RF_PLACARD;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ if (loadSceneParams->transitionType == kTransitionFade) {
+
+ _vm->_interface->setFadeMode(kFadeOut);
+
+ // Fade to black out
+ _vm->_gfx->getCurrentPal(current_pal);
+ event.type = kEvTImmediate;
+ event.code = kPalEvent;
+ event.op = kEventPalToBlack;
+ event.time = 0;
+ event.duration = kNormalFadeDuration;
+ event.data = current_pal;
+ q_event = _vm->_events->queue(&event);
+
+ // set fade mode
+ event.type = kEvTImmediate;
+ event.code = kInterfaceEvent;
+ event.op = kEventSetFadeMode;
+ event.param = kNoFade;
+ event.time = 0;
+ event.duration = 0;
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Display scene background, but stay with black palette
+ event.type = kEvTImmediate;
+ event.code = kBgEvent;
+ event.op = kEventDisplay;
+ event.param = kEvPNoSetPalette;
+ event.time = 0;
+ event.duration = 0;
+ q_event = _vm->_events->chain(q_event, &event);
+
+ }
+
+ // Start the scene pre script, but stay with black palette
+ if (_sceneDescription.startScriptEntrypointNumber > 0) {
+ event.type = kEvTOneshot;
+ event.code = kScriptEvent;
+ event.op = kEventExecBlocking;
+ event.time = 0;
+ event.param = _sceneDescription.scriptModuleNumber;
+ event.param2 = _sceneDescription.startScriptEntrypointNumber;
+ event.param3 = 0; // Action
+ event.param4 = _sceneNumber; // Object
+ event.param5 = loadSceneParams->actorsEntrance; // With Object
+ event.param6 = 0; // Actor
+
+ q_event = _vm->_events->chain(q_event, &event);
+ }
+
+ if (loadSceneParams->transitionType == kTransitionFade) {
+
+ // set fade mode
+ event.type = kEvTImmediate;
+ event.code = kInterfaceEvent;
+ event.op = kEventSetFadeMode;
+ event.param = kFadeIn;
+ event.time = 0;
+ event.duration = 0;
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Fade in from black to the scene background palette
+ event.type = kEvTImmediate;
+ event.code = kPalEvent;
+ event.op = kEventBlackToPal;
+ event.time = 0;
+ event.duration = kNormalFadeDuration;
+ event.data = _bg.pal;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // set fade mode
+ event.type = kEvTImmediate;
+ event.code = kInterfaceEvent;
+ event.op = kEventSetFadeMode;
+ event.param = kNoFade;
+ event.time = 0;
+ event.duration = 0;
+ q_event = _vm->_events->chain(q_event, &event);
+ }
+
+ if (loadSceneParams->sceneProc == NULL) {
+ if (!_inGame && _vm->getGameType() == GType_ITE) {
+ _inGame = true;
+ _vm->_interface->setMode(kPanelMain);
+ }
+
+ _vm->_sound->stopAll();
+
+ // FIXME: Does IHNM use scene background music, or is all the
+ // music scripted? At the very least, it shouldn't try
+ // to start song 0 at the beginning of the game, since
+ // it's the end credits music.
+
+ if (_vm->getGameType() == GType_ITE) {
+ if (_sceneDescription.musicResourceId >= 0) {
+ event.type = kEvTOneshot;
+ event.code = kMusicEvent;
+ event.param = _sceneDescription.musicResourceId;
+ event.param2 = MUSIC_DEFAULT;
+ event.op = kEventPlay;
+ event.time = 0;
+
+ _vm->_events->queue(&event);
+ } else {
+ event.type = kEvTOneshot;
+ event.code = kMusicEvent;
+ event.op = kEventStop;
+ event.time = 0;
+
+ _vm->_events->queue(&event);
+ }
+ }
+
+ // Set scene background
+ event.type = kEvTOneshot;
+ event.code = kBgEvent;
+ event.op = kEventDisplay;
+ event.param = kEvPSetPalette;
+ event.time = 0;
+
+ _vm->_events->queue(&event);
+
+ // Begin palette cycle animation if present
+ event.type = kEvTOneshot;
+ event.code = kPalAnimEvent;
+ event.op = kEventCycleStart;
+ event.time = 0;
+
+ q_event = _vm->_events->queue(&event);
+
+ // Start the scene main script
+ if (_sceneDescription.sceneScriptEntrypointNumber > 0) {
+ event.type = kEvTOneshot;
+ event.code = kScriptEvent;
+ event.op = kEventExecNonBlocking;
+ event.time = 0;
+ event.param = _sceneDescription.scriptModuleNumber;
+ event.param2 = _sceneDescription.sceneScriptEntrypointNumber;
+ event.param3 = _vm->_script->getVerbType(kVerbEnter); // Action
+ event.param4 = _sceneNumber; // Object
+ event.param5 = loadSceneParams->actorsEntrance; // With Object
+ event.param6 = 0; // Actor
+
+ _vm->_events->queue(&event);
+ }
+
+ debug(3, "Scene started");
+
+ } else {
+ loadSceneParams->sceneProc(SCENE_BEGIN, this);
+ }
+
+
+
+ // We probably don't want "followers" to go into scene -1 , 0. At the very
+ // least we don't want garbage to be drawn that early in the ITE intro.
+ if (_sceneNumber > 0 && _sceneNumber != ITE_SCENE_PUZZLE)
+ _vm->_actor->updateActorsScene(loadSceneParams->actorsEntrance);
+
+ if (_sceneNumber == ITE_SCENE_PUZZLE)
+ _vm->_puzzle->execute();
+
+ if (getFlags() & kSceneFlagShowCursor) {
+ // Activate user interface
+ event.type = kEvTOneshot;
+ event.code = kInterfaceEvent;
+ event.op = kEventActivate;
+ event.time = 0;
+ _vm->_events->queue(&event);
+ }
+}
+
+void Scene::loadSceneDescriptor(uint32 resourceId) {
+ byte *sceneDescriptorData;
+ size_t sceneDescriptorDataLength;
+
+ memset(&_sceneDescription, 0, sizeof(_sceneDescription));
+
+ if (resourceId == 0) {
+ return;
+ }
+
+ _vm->_resource->loadResource(_sceneContext, resourceId, sceneDescriptorData, sceneDescriptorDataLength);
+
+ if (sceneDescriptorDataLength == 16) {
+ MemoryReadStreamEndian readS(sceneDescriptorData, sceneDescriptorDataLength, _sceneContext->isBigEndian);
+
+ _sceneDescription.flags = readS.readSint16();
+ _sceneDescription.resourceListResourceId = readS.readSint16();
+ _sceneDescription.endSlope = readS.readSint16();
+ _sceneDescription.beginSlope = readS.readSint16();
+ _sceneDescription.scriptModuleNumber = readS.readUint16();
+ _sceneDescription.sceneScriptEntrypointNumber = readS.readUint16();
+ _sceneDescription.startScriptEntrypointNumber = readS.readUint16();
+ _sceneDescription.musicResourceId = readS.readSint16();
+ }
+
+ free(sceneDescriptorData);
+}
+
+void Scene::loadSceneResourceList(uint32 resourceId) {
+ byte *resourceListData;
+ size_t resourceListDataLength;
+ size_t i;
+
+ _resourceListCount = 0;
+ _resourceList = NULL;
+
+ if (resourceId == 0) {
+ return;
+ }
+
+ // Load the scene resource table
+ _vm->_resource->loadResource(_sceneContext, resourceId, resourceListData, resourceListDataLength);
+
+ if ((resourceListDataLength % SAGA_RESLIST_ENTRY_LEN) == 0) {
+ MemoryReadStreamEndian readS(resourceListData, resourceListDataLength, _sceneContext->isBigEndian);
+
+ // Allocate memory for scene resource list
+ _resourceListCount = resourceListDataLength / SAGA_RESLIST_ENTRY_LEN;
+ debug(3, "Scene resource list contains %i entries", _resourceListCount);
+ _resourceList = (SceneResourceData *)calloc(_resourceListCount, sizeof(*_resourceList));
+
+ // Load scene resource list from raw scene
+ // resource table
+ debug(3, "Loading scene resource list");
+
+ for (i = 0; i < _resourceListCount; i++) {
+ _resourceList[i].resourceId = readS.readUint16();
+ _resourceList[i].resourceType = readS.readUint16();
+ // demo version may contain invalid resourceId
+ _resourceList[i].invalid = !_vm->_resource->validResourceId(_sceneContext, _resourceList[i].resourceId);
+ }
+
+ }
+ free(resourceListData);
+}
+
+void Scene::processSceneResources() {
+ byte *resourceData;
+ size_t resourceDataLength;
+ const byte *palPointer;
+ size_t i;
+ SAGAResourceTypes *types;
+ int typesCount;
+ SAGAResourceTypes resType;
+
+ getResourceTypes(types, typesCount);
+
+ // Process the scene resource list
+ for (i = 0; i < _resourceListCount; i++) {
+ if (_resourceList[i].invalid) {
+ continue;
+ }
+ resourceData = _resourceList[i].buffer;
+ resourceDataLength = _resourceList[i].size;
+
+ if (_resourceList[i].resourceType >= typesCount) {
+ error("Scene::processSceneResources() wrong resource type %i", _resourceList[i].resourceType);
+ }
+
+ resType = types[_resourceList[i].resourceType];
+
+ switch (resType) {
+ case SAGA_UNKNOWN:
+ warning("UNKNOWN resourceType %i", _resourceList[i].resourceType);
+ break;
+ case SAGA_ACTOR:
+ //for (a = actorsInScene; a; a = a->nextInScene)
+ // if (a->obj.figID == glist->file_id)
+ // if (_vm->getGameType() == GType_ITE ||
+ // ((a->obj.flags & ACTORF_FINAL_FACE) & 0xff))
+ // a->sprites = (xSpriteSet *)glist->offset;
+ warning("STUB: unimplemeted handler of SAGA_ACTOR resource");
+ break;
+ case SAGA_OBJECT:
+ break;
+ case SAGA_BG_IMAGE: // Scene background resource
+ if (_bg.loaded) {
+ error("Scene::processSceneResources() Multiple background resources encountered");
+ }
+
+ debug(3, "Loading background resource.");
+ _bg.res_buf = resourceData;
+ _bg.res_len = resourceDataLength;
+ _bg.loaded = 1;
+
+ if (_vm->decodeBGImage(_bg.res_buf,
+ _bg.res_len,
+ &_bg.buf,
+ &_bg.buf_len,
+ &_bg.w,
+ &_bg.h) != SUCCESS) {
+ error("Scene::processSceneResources() Error loading background resource %i", _resourceList[i].resourceId);
+ }
+
+ palPointer = _vm->getImagePal(_bg.res_buf, _bg.res_len);
+ memcpy(_bg.pal, palPointer, sizeof(_bg.pal));
+ break;
+ case SAGA_BG_MASK: // Scene background mask resource
+ if (_bgMask.loaded) {
+ error("Scene::ProcessSceneResources(): Duplicate background mask resource encountered");
+ }
+ debug(3, "Loading BACKGROUND MASK resource.");
+ _bgMask.res_buf = resourceData;
+ _bgMask.res_len = resourceDataLength;
+ _bgMask.loaded = 1;
+ _vm->decodeBGImage(_bgMask.res_buf, _bgMask.res_len, &_bgMask.buf,
+ &_bgMask.buf_len, &_bgMask.w, &_bgMask.h, true);
+
+ // At least in ITE the mask needs to be clipped.
+
+ _bgMask.w = MIN(_bgMask.w, _vm->getDisplayWidth());
+ _bgMask.h = MIN(_bgMask.h, getHeight());
+
+ debug(4, "BACKGROUND MASK width=%d height=%d length=%d", _bgMask.w, _bgMask.h, _bgMask.buf_len);
+ break;
+ case SAGA_STRINGS:
+ debug(3, "Loading scene strings resource...");
+ _vm->loadStrings(_sceneStrings, resourceData, resourceDataLength);
+ break;
+ case SAGA_OBJECT_MAP:
+ debug(3, "Loading object map resource...");
+ _objectMap->load(resourceData, resourceDataLength);
+ break;
+ case SAGA_ACTION_MAP:
+ debug(3, "Loading action map resource...");
+ _actionMap->load(resourceData, resourceDataLength);
+ break;
+ case SAGA_ISO_IMAGES:
+ if (!(_sceneDescription.flags & kSceneFlagISO)) {
+ error("Scene::ProcessSceneResources(): not Iso mode");
+ }
+
+ debug(3, "Loading isometric images resource.");
+
+ _vm->_isoMap->loadImages(resourceData, resourceDataLength);
+ break;
+ case SAGA_ISO_MAP:
+ if (!(_sceneDescription.flags & kSceneFlagISO)) {
+ error("Scene::ProcessSceneResources(): not Iso mode");
+ }
+
+ debug(3, "Loading isometric map resource.");
+
+ _vm->_isoMap->loadMap(resourceData, resourceDataLength);
+ break;
+ case SAGA_ISO_PLATFORMS:
+ if (!(_sceneDescription.flags & kSceneFlagISO)) {
+ error("Scene::ProcessSceneResources(): not Iso mode");
+ }
+
+ debug(3, "Loading isometric platforms resource.");
+
+ _vm->_isoMap->loadPlatforms(resourceData, resourceDataLength);
+ break;
+ case SAGA_ISO_METATILES:
+ if (!(_sceneDescription.flags & kSceneFlagISO)) {
+ error("Scene::ProcessSceneResources(): not Iso mode");
+ }
+
+ debug(3, "Loading isometric metatiles resource.");
+
+ _vm->_isoMap->loadMetaTiles(resourceData, resourceDataLength);
+ break;
+ case SAGA_ANIM:
+ {
+ uint16 animId = _resourceList[i].resourceType - 14;
+
+ debug(3, "Loading animation resource animId=%i", animId);
+
+ _vm->_anim->load(animId, resourceData, resourceDataLength);
+ }
+ break;
+ case SAGA_ENTRY:
+ debug(3, "Loading entry list resource...");
+ loadSceneEntryList(resourceData, resourceDataLength);
+ break;
+ case SAGA_ISO_MULTI:
+ if (!(_sceneDescription.flags & kSceneFlagISO)) {
+ error("Scene::ProcessSceneResources(): not Iso mode");
+ }
+
+ debug(3, "Loading isometric multi resource.");
+
+ _vm->_isoMap->loadMulti(resourceData, resourceDataLength);
+ break;
+ case SAGA_PAL_ANIM:
+ debug(3, "Loading palette animation resource.");
+ _vm->_palanim->loadPalAnim(resourceData, resourceDataLength);
+ break;
+ case SAGA_FACES:
+ if (_vm->getGameType() == GType_ITE)
+ _vm->_interface->loadScenePortraits(_resourceList[i].resourceId);
+ break;
+ case SAGA_PALETTE:
+ {
+ PalEntry pal[PAL_ENTRIES];
+ byte *palPtr = resourceData;
+
+ if (resourceDataLength < 3 * PAL_ENTRIES)
+ error("Too small scene palette %i", resourceDataLength);
+
+ for (uint16 c = 0; c < PAL_ENTRIES; c++) {
+ pal[c].red = *palPtr++;
+ pal[c].green = *palPtr++;
+ pal[c].blue = *palPtr++;
+ }
+ _vm->_gfx->setPalette(pal);
+ }
+ break;
+ default:
+ error("Scene::ProcessSceneResources() Encountered unknown resource type %i", _resourceList[i].resourceType);
+ break;
+ }
+ }
+}
+
+void Scene::draw() {
+ Surface *backBuffer;
+ Surface *backGroundSurface;
+ Rect rect;
+
+ backBuffer = _vm->_gfx->getBackBuffer();
+
+ backGroundSurface = _vm->_render->getBackGroundSurface();
+
+ if (_sceneDescription.flags & kSceneFlagISO) {
+ _vm->_isoMap->adjustScroll(false);
+ _vm->_isoMap->draw(backBuffer);
+ } else {
+ backGroundSurface->getRect(rect);
+ if (_sceneClip.bottom < rect.bottom) {
+ rect.bottom = getHeight();
+ }
+ backBuffer->blit(rect, (const byte *)backGroundSurface->pixels);
+ }
+}
+
+void Scene::endScene() {
+ Surface *backBuffer;
+ Surface *backGroundSurface;
+ Rect rect;
+ size_t i;
+
+ if (!_sceneLoaded)
+ return;
+
+ debug(3, "Ending scene...");
+
+ if (_sceneProc != NULL) {
+ _sceneProc(SCENE_END, this);
+ }
+
+ //
+ _vm->_script->abortAllThreads();
+ _vm->_script->_skipSpeeches = false;
+
+ // Copy current screen to render buffer so inset rooms will get proper background
+ backGroundSurface = _vm->_render->getBackGroundSurface();
+ if (!(_sceneDescription.flags & kSceneFlagISO) && !_vm->_scene->isInIntro()) {
+ BGInfo bgInfo;
+
+ _vm->_scene->getBGInfo(bgInfo);
+ backGroundSurface->blit(bgInfo.bounds, bgInfo.buffer);
+ } else {
+ backBuffer = _vm->_gfx->getBackBuffer();
+ backBuffer->getRect(rect);
+ backGroundSurface->blit(rect, (const byte *)backBuffer->pixels);
+ }
+
+ // Free scene background
+ if (_bg.loaded) {
+ free(_bg.buf);
+ _bg.loaded = 0;
+ }
+
+ // Free scene background mask
+ if (_bgMask.loaded) {
+ free(_bgMask.buf);
+ _bgMask.loaded = 0;
+ }
+
+ // Free scene resource list
+ for (i = 0; i < _resourceListCount; i++) {
+ free(_resourceList[i].buffer);
+ }
+
+ if (_loadDescription) {
+ free(_resourceList);
+ }
+
+ // Free animation info list
+ _vm->_anim->reset();
+
+ _vm->_palanim->freePalAnim();
+
+ _objectMap->freeMem();
+ _actionMap->freeMem();
+ _entryList.freeMem();
+ _sceneStrings.freeMem();
+ _vm->_isoMap->freeMem();
+
+ _vm->_events->clearList();
+ _textList.clear();
+
+ _sceneLoaded = false;
+
+}
+
+void Scene::cmdSceneChange(int argc, const char **argv) {
+ int scene_num = 0;
+
+ scene_num = atoi(argv[1]);
+
+ if ((scene_num < 1) || (scene_num >= _sceneCount)) {
+ _vm->_console->DebugPrintf("Invalid scene number.\n");
+ return;
+ }
+
+ clearSceneQueue();
+
+ changeScene(scene_num, 0, kTransitionNoFade);
+}
+
+void Scene::cmdActionMapInfo() {
+ _actionMap->cmdInfo();
+}
+
+void Scene::cmdObjectMapInfo() {
+ _objectMap->cmdInfo();
+}
+
+
+void Scene::loadSceneEntryList(const byte* resourcePointer, size_t resourceLength) {
+ int i;
+
+ _entryList.entryListCount = resourceLength / 8;
+
+ MemoryReadStreamEndian readS(resourcePointer, resourceLength, _sceneContext->isBigEndian);
+
+
+ if (_entryList.entryList)
+ error("Scene::loadSceneEntryList entryList != NULL");
+
+ _entryList.entryList = (SceneEntry *) malloc(_entryList.entryListCount * sizeof(*_entryList.entryList));
+ if (_entryList.entryList == NULL) {
+ memoryError("Scene::loadSceneEntryList");
+ }
+
+ for (i = 0; i < _entryList.entryListCount; i++) {
+ _entryList.entryList[i].location.x = readS.readSint16();
+ _entryList.entryList[i].location.y = readS.readSint16();
+ _entryList.entryList[i].location.z = readS.readSint16();
+ _entryList.entryList[i].facing = readS.readUint16();
+ }
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/scene.h b/engines/saga/scene.h
new file mode 100644
index 0000000000..7f99140d10
--- /dev/null
+++ b/engines/saga/scene.h
@@ -0,0 +1,370 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Scene management module private header file
+
+#ifndef SAGA_SCENE_H
+#define SAGA_SCENE_H
+
+#include "saga/font.h"
+#include "saga/list.h"
+#include "saga/actor.h"
+#include "saga/interface.h"
+
+namespace Saga {
+
+#define SCENE_DOORS_MAX 16
+
+#define NO_CHAPTER_CHANGE -2
+
+class ObjectMap;
+
+struct Event;
+
+enum SceneFlags {
+ kSceneFlagISO = 1,
+ kSceneFlagShowCursor = 2
+};
+
+struct BGInfo {
+ Rect bounds;
+ byte *buffer;
+ size_t bufferLength;
+};
+
+typedef int (SceneProc) (int, void *);
+
+
+enum SCENE_PROC_PARAMS {
+ SCENE_BEGIN = 0,
+ SCENE_END
+};
+
+// Resource type numbers
+enum SAGAResourceTypes {
+ SAGA_UNKNOWN,
+ SAGA_ACTOR,
+ SAGA_OBJECT,
+ SAGA_BG_IMAGE,
+ SAGA_BG_MASK,
+ SAGA_STRINGS,
+ SAGA_OBJECT_MAP,
+ SAGA_ACTION_MAP,
+ SAGA_ISO_IMAGES,
+ SAGA_ISO_MAP,
+ SAGA_ISO_PLATFORMS,
+ SAGA_ISO_METATILES,
+ SAGA_ENTRY,
+ SAGA_ANIM,
+ SAGA_ISO_MULTI,
+ SAGA_PAL_ANIM,
+ SAGA_FACES,
+ SAGA_PALETTE
+};
+
+#define SAGA_RESLIST_ENTRY_LEN 4
+
+struct SceneResourceData {
+ uint32 resourceId;
+ int resourceType;
+ byte *buffer;
+ size_t size;
+ bool invalid;
+};
+
+#define SAGA_SCENE_DESC_LEN 16
+
+struct SceneDescription {
+ int16 flags;
+ int16 resourceListResourceId;
+ int16 endSlope;
+ int16 beginSlope;
+ uint16 scriptModuleNumber;
+ uint16 sceneScriptEntrypointNumber;
+ uint16 startScriptEntrypointNumber;
+ int16 musicResourceId;
+ SceneResourceData *resourceList;
+ size_t resourceListCount;
+};
+
+struct SceneEntry {
+ Location location;
+ int facing;
+};
+
+struct SceneEntryList {
+ SceneEntry *entryList;
+ int entryListCount;
+
+ const SceneEntry * getEntry(int index) {
+ if ((index < 0) || (index >= entryListCount)) {
+ error("SceneEntryList::getEntry wrong index (%d)", index);
+ }
+ return &entryList[index];
+ }
+ void freeMem() {
+ free(entryList);
+ memset(this, 0, sizeof(*this));
+ }
+ SceneEntryList() {
+ memset(this, 0, sizeof(*this));
+ }
+ ~SceneEntryList() {
+ freeMem();
+ }
+};
+
+struct SceneImage {
+ int loaded;
+ int w;
+ int h;
+ int p;
+ byte *buf;
+ size_t buf_len;
+ byte *res_buf;
+ size_t res_len;
+ PalEntry pal[256];
+};
+
+
+enum SceneTransitionType {
+ kTransitionNoFade,
+ kTransitionFade
+};
+
+enum SceneLoadFlags {
+ kLoadByResourceId,
+ kLoadBySceneNumber,
+ kLoadByDescription
+};
+
+struct LoadSceneParams {
+ int32 sceneDescriptor;
+ SceneLoadFlags loadFlag;
+ SceneDescription* sceneDescription;
+ SceneProc *sceneProc;
+ bool sceneSkipTarget;
+ SceneTransitionType transitionType;
+ int actorsEntrance;
+ int chapter;
+};
+
+typedef Common::List<LoadSceneParams> SceneQueueList;
+
+///// IHNM-specific stuff
+#define IHNM_PALFADE_TIME 1000
+#define IHNM_INTRO_FRAMETIME 80
+#define IHNM_DGLOGO_TIME 8000
+#define IHNM_TITLE_TIME_GM 28750
+#define IHNM_TITLE_TIME_FM 19500
+
+///// ITE-specific stuff
+#define ITE_INTRO_FRAMETIME 90
+
+#define INTRO_CAPTION_Y 170
+#define INTRO_DE_CAPTION_Y 160
+#define VOICE_PAD 50
+#define VOICE_LETTERLEN 90
+
+#define PALETTE_FADE_DURATION 1000
+#define DISSOLVE_DURATION 3000
+#define LOGO_DISSOLVE_DURATION 1000
+
+#define CREDIT_DURATION1 4000
+
+struct IntroDialogue {
+ uint32 i_voice_rn;
+ const char *i_str;
+};
+
+struct IntroCredit {
+ Common::Language lang;
+ int game;
+ int type;
+ const char *string;
+};
+
+
+class Scene {
+ public:
+ Scene(SagaEngine *vm);
+ ~Scene();
+
+// Console functions
+ void cmdActionMapInfo();
+ void cmdObjectMapInfo();
+
+ void cmdSceneChange(int argc, const char **argv);
+
+ void startScene();
+ void nextScene();
+ void skipScene();
+ void endScene();
+ void queueScene(LoadSceneParams *sceneQueue) {
+ _sceneQueue.push_back(*sceneQueue);
+ }
+
+ void draw();
+ int getFlags() const { return _sceneDescription.flags; }
+ int getScriptModuleNumber() const { return _sceneDescription.scriptModuleNumber; }
+ bool isInIntro() { return !_inGame; }
+ const Rect& getSceneClip() const { return _sceneClip; }
+
+ void getBGMaskInfo(int &width, int &height, byte *&buffer, size_t &bufferLength);
+ int isBGMaskPresent() { return _bgMask.loaded; }
+ int getBGMaskType(const Point &testPoint);
+ bool validBGMaskPoint(const Point &testPoint);
+ bool canWalk(const Point &testPoint);
+ bool offscreenPath(Point &testPoint);
+
+ void setDoorState(int doorNumber, int doorState);
+ int getDoorState(int doorNumber);
+ void initDoorsState();
+
+ void getBGInfo(BGInfo &bgInfo);
+ void getBGPal(PalEntry *&pal) {
+ pal = (PalEntry *)_bg.pal;
+ }
+
+ void getSlopes(int &beginSlope, int &endSlope);
+
+ void clearSceneQueue(void) {
+ _sceneQueue.clear();
+ }
+ void changeScene(int16 sceneNumber, int actorsEntrance, SceneTransitionType transitionType, int chapter = NO_CHAPTER_CHANGE);
+
+ bool isSceneLoaded() const { return _sceneLoaded; }
+
+ int getSceneResourceId(int sceneNumber) {
+ if ((sceneNumber < 0) || (sceneNumber >= _sceneCount)) {
+ error("getSceneResourceId: wrong sceneNumber %i", sceneNumber);
+ }
+ return _sceneLUT[sceneNumber];
+ }
+ int currentSceneNumber() const { return _sceneNumber; }
+ int currentChapterNumber() const { return _chapterNumber; }
+ void setChapterNumber(int ch) { _chapterNumber = ch; }
+ int getOutsetSceneNumber() const { return _outsetSceneNumber; }
+ int currentSceneResourceId() const { return _sceneResourceId; }
+ void cutawaySkip() {
+ if (_vm->_scene->isInIntro())
+ _vm->_framesEsc = 2;
+ else
+ _vm->_framesEsc = 1;
+ }
+
+ void drawTextList(Surface *ds);
+
+ int getHeight() const {
+ if (_vm->_interface->getMode() == kPanelChapterSelection)
+ return _vm->_gameDisplayInfo.logicalHeight;
+ else
+ return _vm->_gameDisplayInfo.sceneHeight;
+ }
+
+ private:
+ void loadScene(LoadSceneParams *loadSceneParams);
+ void loadSceneDescriptor(uint32 resourceId);
+ void loadSceneResourceList(uint32 resourceId);
+ void loadSceneEntryList(const byte* resourcePointer, size_t resourceLength);
+ void processSceneResources();
+ void getResourceTypes(SAGAResourceTypes *&types, int &typesCount);
+
+
+ SagaEngine *_vm;
+
+ ResourceContext *_sceneContext;
+ int *_sceneLUT;
+ int _sceneCount;
+ SceneQueueList _sceneQueue;
+ bool _sceneLoaded;
+ int _sceneNumber;
+ int _chapterNumber;
+ int _outsetSceneNumber;
+ int _sceneResourceId;
+ bool _inGame;
+ bool _loadDescription;
+ SceneDescription _sceneDescription;
+ size_t _resourceListCount;
+ SceneResourceData *_resourceList;
+ SceneProc *_sceneProc;
+ SceneImage _bg;
+ SceneImage _bgMask;
+ Common::Rect _sceneClip;
+
+ int _sceneDoors[SCENE_DOORS_MAX];
+
+
+ public:
+ ObjectMap *_actionMap;
+ ObjectMap *_objectMap;
+ SceneEntryList _entryList;
+ StringsTable _sceneStrings;
+ TextList _textList;
+
+ private:
+ int IHNMStartProc();
+ int ITEStartProc();
+
+ public:
+ static int SC_IHNMIntroMovieProc1(int param, void *refCon);
+ static int SC_IHNMIntroMovieProc2(int param, void *refCon);
+ static int SC_IHNMIntroMovieProc3(int param, void *refCon);
+ static int SC_IHNMHateProc(int param, void *refCon);
+
+ private:
+ int IHNMIntroMovieProc1(int param);
+ int IHNMIntroMovieProc2(int param);
+ int IHNMIntroMovieProc3(int param);
+ int IHNMHateProc(int param);
+
+ public:
+ static int SC_ITEIntroAnimProc(int param, void *refCon);
+ static int SC_ITEIntroCave1Proc(int param, void *refCon);
+ static int SC_ITEIntroCave2Proc(int param, void *refCon);
+ static int SC_ITEIntroCave3Proc(int param, void *refCon);
+ static int SC_ITEIntroCave4Proc(int param, void *refCon);
+ static int SC_ITEIntroValleyProc(int param, void *refCon);
+ static int SC_ITEIntroTreeHouseProc(int param, void *refCon);
+ static int SC_ITEIntroFairePathProc(int param, void *refCon);
+ static int SC_ITEIntroFaireTentProc(int param, void *refCon);
+
+ private:
+ Event *ITEQueueDialogue(Event *q_event, int n_dialogues, const IntroDialogue dialogue[]);
+ Event *ITEQueueCredits(int delta_time, int duration, int n_credits, const IntroCredit credits[]);
+ int ITEIntroAnimProc(int param);
+ int ITEIntroCave1Proc(int param);
+ int ITEIntroCave2Proc(int param);
+ int ITEIntroCave3Proc(int param);
+ int ITEIntroCave4Proc(int param);
+ int ITEIntroValleyProc(int param);
+ int ITEIntroTreeHouseProc(int param);
+ int ITEIntroFairePathProc(int param);
+ int ITEIntroFaireTentProc(int param);
+
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/script.cpp b/engines/saga/script.cpp
new file mode 100644
index 0000000000..3d3a626e9f
--- /dev/null
+++ b/engines/saga/script.cpp
@@ -0,0 +1,806 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Scripting module: Script resource handling functions
+#include "saga/saga.h"
+
+#include "saga/gfx.h"
+#include "saga/console.h"
+
+#include "saga/script.h"
+#include "saga/stream.h"
+#include "saga/interface.h"
+#include "saga/itedata.h"
+#include "saga/scene.h"
+#include "saga/events.h"
+#include "saga/actor.h"
+#include "saga/objectmap.h"
+#include "saga/isomap.h"
+#include "saga/rscfile.h"
+
+namespace Saga {
+
+
+
+// Initializes the scripting module.
+// Loads script resource look-up table, initializes script data system
+Script::Script(SagaEngine *vm) : _vm(vm) {
+ ResourceContext *resourceContext;
+ byte *resourcePointer;
+ size_t resourceLength;
+ int prevTell;
+ int i, j;
+ byte *stringsPointer;
+ size_t stringsLength;
+
+ //initialize member variables
+ _abortEnabled = true;
+ _skipSpeeches = false;
+ _conversingThread = NULL;
+
+ _firstObjectSet = false;
+ _secondObjectNeeded = false;
+ _pendingVerb = getVerbType(kVerbNone);
+ _currentVerb = getVerbType(kVerbNone);
+ _stickyVerb = getVerbType(kVerbWalkTo);
+ _leftButtonVerb = getVerbType(kVerbNone);
+ _rightButtonVerb = getVerbType(kVerbNone);
+ _pointerObject = ID_NOTHING;
+
+ _staticSize = 0;
+ _commonBufferSize = COMMON_BUFFER_SIZE;
+ _commonBuffer = (byte*)malloc(_commonBufferSize);
+ memset(_commonBuffer, 0, _commonBufferSize);
+
+ debug(8, "Initializing scripting subsystem");
+ // Load script resource file context
+ _scriptContext = _vm->_resource->getContext(GAME_SCRIPTFILE);
+ if (_scriptContext == NULL) {
+ error("Script::Script() script context not found");
+ }
+
+ resourceContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ if (resourceContext == NULL) {
+ error("Script::Script() resource context not found");
+ }
+
+ debug(3, "Loading module LUT from resource %i", _vm->getResourceDescription()->moduleLUTResourceId);
+ _vm->_resource->loadResource(resourceContext, _vm->getResourceDescription()->moduleLUTResourceId, resourcePointer, resourceLength);
+
+
+ // Create logical script LUT from resource
+ if (resourceLength % S_LUT_ENTRYLEN_ITECD == 0) {
+ _modulesLUTEntryLen = S_LUT_ENTRYLEN_ITECD;
+ } else if (resourceLength % S_LUT_ENTRYLEN_ITEDISK == 0) {
+ _modulesLUTEntryLen = S_LUT_ENTRYLEN_ITEDISK;
+ } else {
+ error("Script::Script() Invalid script lookup table length (%i)", resourceLength);
+ }
+
+ // Calculate number of entries
+ _modulesCount = resourceLength / _modulesLUTEntryLen;
+
+ debug(3, "LUT has %i entries", _modulesCount);
+
+ // Allocate space for logical LUT
+ _modules = (ModuleData *)malloc(_modulesCount * sizeof(*_modules));
+ if (_modules == NULL) {
+ memoryError("Script::Script()");
+ }
+
+ // Convert LUT resource to logical LUT
+ MemoryReadStreamEndian scriptS(resourcePointer, resourceLength, resourceContext->isBigEndian);
+ for (i = 0; i < _modulesCount; i++) {
+ memset(&_modules[i], 0, sizeof(ModuleData));
+
+ prevTell = scriptS.pos();
+ _modules[i].scriptResourceId = scriptS.readUint16();
+ _modules[i].stringsResourceId = scriptS.readUint16();
+ _modules[i].voicesResourceId = scriptS.readUint16();
+
+ // Skip the unused portion of the structure
+ for (j = scriptS.pos(); j < prevTell + _modulesLUTEntryLen; j++) {
+ if (scriptS.readByte() != 0)
+ warning("Unused scriptLUT part isn't really unused for LUT %d (pos: %d)", i, j);
+ }
+ }
+
+ free(resourcePointer);
+
+ // TODO
+ //
+ // In ITE, the "main strings" resource contains both the verb strings
+ // and the object names.
+ //
+ // In IHNM, the "main strings" contains the verb strings, but not the
+ // object names. At least, I think that's the case.
+
+ _vm->_resource->loadResource(resourceContext, _vm->getResourceDescription()->mainStringsResourceId, stringsPointer, stringsLength);
+
+ _vm->loadStrings(_mainStrings, stringsPointer, stringsLength);
+ free(stringsPointer);
+
+ setupScriptFuncList();
+}
+
+// Shut down script module gracefully; free all allocated module resources
+Script::~Script() {
+
+ debug(8, "Shutting down scripting subsystem.");
+
+ _mainStrings.freeMem();
+
+ freeModules();
+ free(_modules);
+
+ free(_commonBuffer);
+}
+
+void Script::loadModule(int scriptModuleNumber) {
+ byte *resourcePointer;
+ size_t resourceLength;
+
+ // Validate script number
+ if ((scriptModuleNumber < 0) || (scriptModuleNumber >= _modulesCount)) {
+ error("Script::loadScript() Invalid script module number");
+ }
+
+ if (_modules[scriptModuleNumber].loaded) {
+ return;
+ }
+
+ // Initialize script data structure
+ debug(3, "Loading script module #%d", scriptModuleNumber);
+
+ _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].scriptResourceId, resourcePointer, resourceLength);
+
+ loadModuleBase(_modules[scriptModuleNumber], resourcePointer, resourceLength);
+ free(resourcePointer);
+
+ _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].stringsResourceId, resourcePointer, resourceLength);
+
+ _vm->loadStrings(_modules[scriptModuleNumber].strings, resourcePointer, resourceLength);
+ free(resourcePointer);
+
+ if (_modules[scriptModuleNumber].voicesResourceId > 0) {
+ _vm->_resource->loadResource(_scriptContext, _modules[scriptModuleNumber].voicesResourceId, resourcePointer, resourceLength);
+
+ loadVoiceLUT(_modules[scriptModuleNumber].voiceLUT, resourcePointer, resourceLength);
+ free(resourcePointer);
+ }
+
+ _modules[scriptModuleNumber].staticOffset = _staticSize;
+ _staticSize += _modules[scriptModuleNumber].staticSize;
+ if (_staticSize > _commonBufferSize) {
+ error("Script::loadModule() _staticSize > _commonBufferSize");
+ }
+ _modules[scriptModuleNumber].loaded = true;
+}
+
+void Script::freeModules() {
+ int i;
+ for (i = 0; i < _modulesCount; i++) {
+ if (_modules[i].loaded) {
+ _modules[i].freeMem();
+ }
+ }
+ _staticSize = 0;
+}
+
+void Script::loadModuleBase(ModuleData &module, const byte *resourcePointer, size_t resourceLength) {
+ int i;
+
+ debug(3, "Loading module base...");
+
+ module.moduleBase = (byte*)malloc(resourceLength);
+ module.moduleBaseSize = resourceLength;
+
+ memcpy(module.moduleBase, resourcePointer, resourceLength);
+
+ MemoryReadStreamEndian scriptS(module.moduleBase, module.moduleBaseSize, _scriptContext->isBigEndian);
+
+ module.entryPointsCount = scriptS.readUint16();
+ scriptS.readUint16(); //skip
+ module.entryPointsTableOffset = scriptS.readUint16();
+ scriptS.readUint16(); //skip
+
+ if ((module.moduleBaseSize - module.entryPointsTableOffset) < (module.entryPointsCount * SCRIPT_TBLENTRY_LEN)) {
+ error("Script::loadModuleBase() Invalid table offset");
+ }
+
+ if (module.entryPointsCount > SCRIPT_MAX) {
+ error("Script::loadModuleBase()Script limit exceeded");
+ }
+
+ module.entryPoints = (EntryPoint *)malloc(module.entryPointsCount * sizeof(*module.entryPoints));
+ if (module.entryPoints == NULL) {
+ memoryError("Script::loadModuleBase");
+ }
+
+ // Read in the entrypoint table
+
+ module.staticSize = scriptS.readUint16();
+ while (scriptS.pos() < module.entryPointsTableOffset)
+ scriptS.readByte();
+
+ for (i = 0; i < module.entryPointsCount; i++) {
+ // First uint16 is the offset of the entrypoint name from the start
+ // of the bytecode resource, second uint16 is the offset of the
+ // bytecode itself for said entrypoint
+ module.entryPoints[i].nameOffset = scriptS.readUint16();
+ module.entryPoints[i].offset = scriptS.readUint16();
+
+ // Perform a simple range check on offset values
+ if ((module.entryPoints[i].nameOffset >= module.moduleBaseSize) || (module.entryPoints[i].offset >= module.moduleBaseSize)) {
+ error("Script::loadModuleBase() Invalid offset encountered in script entrypoint table");
+ }
+ }
+}
+
+void Script::loadVoiceLUT(VoiceLUT &voiceLUT, const byte *resourcePointer, size_t resourceLength) {
+ uint16 i;
+
+ voiceLUT.voicesCount = resourceLength / 2;
+
+ voiceLUT.voices = (uint16 *)malloc(voiceLUT.voicesCount * sizeof(*voiceLUT.voices));
+ if (voiceLUT.voices == NULL) {
+ error("Script::loadVoiceLUT() not enough memory");
+ }
+
+ MemoryReadStreamEndian scriptS(resourcePointer, resourceLength, _scriptContext->isBigEndian);
+
+ for (i = 0; i < voiceLUT.voicesCount; i++) {
+ voiceLUT.voices[i] = scriptS.readUint16();
+ }
+}
+
+// verb
+void Script::showVerb(int statusColor) {
+ const char *verbName;
+ const char *object1Name;
+ const char *object2Name;
+ char statusString[STATUS_TEXT_LEN];
+
+ if (_leftButtonVerb == getVerbType(kVerbNone)) {
+ _vm->_interface->setStatusText("");
+ return;
+ }
+
+ verbName = _mainStrings.getString(_leftButtonVerb - 1);
+
+ if (objectTypeId(_currentObject[0]) == kGameObjectNone) {
+ _vm->_interface->setStatusText(verbName, statusColor);
+ return;
+ }
+
+ object1Name = _vm->getObjectName(_currentObject[0]);
+
+ if (!_secondObjectNeeded) {
+ snprintf(statusString, STATUS_TEXT_LEN, "%s %s", verbName, object1Name);
+ _vm->_interface->setStatusText(statusString, statusColor);
+ return;
+ }
+
+
+ if (objectTypeId(_currentObject[1]) != kGameObjectNone) {
+ object2Name = _vm->getObjectName(_currentObject[1]);
+ } else {
+ object2Name = "";
+ }
+
+ if (_leftButtonVerb == getVerbType(kVerbGive)) {
+ snprintf(statusString, STATUS_TEXT_LEN, _vm->getTextString(kTextGiveTo), object1Name, object2Name);
+ _vm->_interface->setStatusText(statusString, statusColor);
+ } else {
+ if (_leftButtonVerb == getVerbType(kVerbUse)) {
+ snprintf(statusString, STATUS_TEXT_LEN, _vm->getTextString(kTextUseWidth), object1Name, object2Name);
+ _vm->_interface->setStatusText(statusString, statusColor);
+ } else {
+ snprintf(statusString, STATUS_TEXT_LEN, "%s %s", verbName, object1Name);
+ _vm->_interface->setStatusText(statusString, statusColor);
+ }
+ }
+}
+
+int Script::getVerbType(VerbTypes verbType) {
+ if (_vm->getGameType() == GType_ITE) {
+ switch (verbType) {
+ case kVerbNone:
+ return kVerbITENone;
+ case kVerbWalkTo:
+ return kVerbITEWalkTo;
+ case kVerbGive:
+ return kVerbITEGive;
+ case kVerbUse:
+ return kVerbITEUse;
+ case kVerbEnter:
+ return kVerbITEEnter;
+ case kVerbLookAt:
+ return kVerbITELookAt;
+ case kVerbPickUp:
+ return kVerbITEPickUp;
+ case kVerbOpen:
+ return kVerbITEOpen;
+ case kVerbClose:
+ return kVerbITEClose;
+ case kVerbTalkTo:
+ return kVerbITETalkTo;
+ case kVerbWalkOnly:
+ return kVerbITEWalkOnly;
+ case kVerbLookOnly:
+ return kVerbITELookOnly;
+ case kVerbOptions:
+ return kVerbITEOptions;
+ }
+ }
+ else {
+ switch (verbType) {
+ case kVerbNone:
+ return kVerbIHNMNone;
+ case kVerbWalkTo:
+ return kVerbIHNMWalk;
+ case kVerbGive:
+ return kVerbIHNMGive;
+ case kVerbUse:
+ return kVerbIHNMUse;
+ case kVerbEnter:
+ return kVerbIHNMEnter;
+ case kVerbLookAt:
+ return kVerbIHNMLookAt;
+ case kVerbPickUp:
+ return kVerbIHNMTake;
+ case kVerbOpen:
+ return -2;
+ case kVerbClose:
+ return -2;
+ case kVerbTalkTo:
+ return kVerbIHNMTalkTo;
+ case kVerbWalkOnly:
+ return kVerbIHNMWalkOnly;
+ case kVerbLookOnly:
+ return kVerbIHNMLookOnly;
+ case kVerbOptions:
+ return kVerbIHNMOptions;
+ }
+ }
+ error("Script::getVerbType() unknown verb type %d", verbType);
+}
+
+void Script::setVerb(int verb) {
+ _pendingObject[0] = ID_NOTHING;
+ _currentObject[0] = ID_NOTHING;
+ _pendingObject[1] = ID_NOTHING;
+ _currentObject[1] = ID_NOTHING;
+ _firstObjectSet = false;
+ _secondObjectNeeded = false;
+
+ // The pointer object will be updated again immediately. This way the
+ // new verb will be applied to it. It's not exactly how the original
+ // engine did it, but it appears to work.
+ _pointerObject = ID_NOTHING;
+
+ setLeftButtonVerb( verb );
+ showVerb();
+}
+
+void Script::setLeftButtonVerb(int verb) {
+ int oldVerb = _currentVerb;
+
+ _currentVerb = _leftButtonVerb = verb;
+
+ if ((_currentVerb != oldVerb) && (_vm->_interface->getMode() == kPanelMain)){
+ if (oldVerb > getVerbType(kVerbNone))
+ _vm->_interface->setVerbState(oldVerb, 2);
+
+ if (_currentVerb > getVerbType(kVerbNone))
+ _vm->_interface->setVerbState(_currentVerb, 2);
+ }
+}
+
+void Script::setRightButtonVerb(int verb) {
+ int oldVerb = _rightButtonVerb;
+
+ _rightButtonVerb = verb;
+
+ if ((_rightButtonVerb != oldVerb) && (_vm->_interface->getMode() == kPanelMain)){
+ if (oldVerb > getVerbType(kVerbNone))
+ _vm->_interface->setVerbState(oldVerb, 2);
+
+ if (_rightButtonVerb > getVerbType(kVerbNone))
+ _vm->_interface->setVerbState(_rightButtonVerb, 2);
+ }
+}
+
+void Script::doVerb() {
+ int scriptEntrypointNumber = 0;
+ int scriptModuleNumber = 0;
+ int objectType;
+ Event event;
+ const char *excuseText;
+ int excuseSampleResourceId;
+ const HitZone *hitZone;
+
+ objectType = objectTypeId(_pendingObject[0]);
+
+ if (_pendingVerb == getVerbType(kVerbGive)) {
+ scriptEntrypointNumber = _vm->_actor->getObjectScriptEntrypointNumber(_pendingObject[1]);
+ if (_vm->_actor->getObjectFlags(_pendingObject[1]) & (kFollower|kProtagonist|kExtended)) {
+ scriptModuleNumber = 0;
+ } else {
+ scriptModuleNumber = _vm->_scene->getScriptModuleNumber();
+ }
+ } else {
+ if (_pendingVerb == getVerbType(kVerbUse)) {
+ if ((objectTypeId(_pendingObject[1]) > kGameObjectNone) && (objectType < objectTypeId(_pendingObject[1]))) {
+ SWAP(_pendingObject[0], _pendingObject[1]);
+ objectType = objectTypeId(_pendingObject[0]);
+ }
+ }
+
+ if (objectType == kGameObjectHitZone) {
+ scriptModuleNumber = _vm->_scene->getScriptModuleNumber();
+ hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(_pendingObject[0]));
+ if ((hitZone->getFlags() & kHitZoneExit) == 0) {
+ scriptEntrypointNumber = hitZone->getScriptNumber();
+ }
+ } else {
+ if (objectType & (kGameObjectActor | kGameObjectObject)) {
+ scriptEntrypointNumber = _vm->_actor->getObjectScriptEntrypointNumber(_pendingObject[0]);
+
+ if ((objectType == kGameObjectActor) && !(_vm->_actor->getObjectFlags(_pendingObject[0]) & (kFollower|kProtagonist|kExtended))) {
+ scriptModuleNumber = _vm->_scene->getScriptModuleNumber();
+ } else {
+ scriptModuleNumber = 0;
+ }
+ }
+ }
+ }
+
+ if (scriptEntrypointNumber > 0) {
+
+ event.type = kEvTOneshot;
+ event.code = kScriptEvent;
+ event.op = kEventExecNonBlocking;
+ event.time = 0;
+ event.param = scriptModuleNumber;
+ event.param2 = scriptEntrypointNumber;
+ event.param3 = _pendingVerb; // Action
+ event.param4 = _pendingObject[0]; // Object
+ event.param5 = _pendingObject[1]; // With Object
+ event.param6 = (objectType == kGameObjectActor) ? _pendingObject[0] : ID_PROTAG; // Actor
+
+ _vm->_events->queue(&event);
+
+ } else {
+ _vm->getExcuseInfo(_pendingVerb, excuseText, excuseSampleResourceId);
+ if (excuseText) {
+ // In Floppy versions we don't have excuse texts
+ if (!(_vm->getFeatures() & GF_CD_FX))
+ excuseSampleResourceId = -1;
+
+ _vm->_actor->actorSpeech(ID_PROTAG, &excuseText, 1, excuseSampleResourceId, 0);
+ }
+ }
+
+ if ((_currentVerb == getVerbType(kVerbWalkTo)) || (_currentVerb == getVerbType(kVerbLookAt))) {
+ _stickyVerb = _currentVerb;
+ }
+
+ _pendingVerb = getVerbType(kVerbNone);
+ _currentObject[0] = _currentObject[1] = ID_NOTHING;
+ setLeftButtonVerb(_stickyVerb);
+
+ setPointerVerb();
+}
+
+void Script::setPointerVerb() {
+ if (_vm->_interface->isActive()) {
+ _pointerObject = ID_PROTAG;
+ whichObject(_vm->mousePos());
+ }
+}
+
+void Script::hitObject(bool leftButton) {
+ int verb;
+ verb = leftButton ? _leftButtonVerb : _rightButtonVerb;
+
+ if (verb > getVerbType(kVerbNone)) {
+ if (_firstObjectSet) {
+ if (_secondObjectNeeded) {
+ _pendingObject[0] = _currentObject[0];
+ _pendingObject[1] = _currentObject[1];
+ _pendingVerb = verb;
+
+ _leftButtonVerb = verb;
+ if (_pendingVerb > getVerbType(kVerbNone))
+ showVerb(kITEColorBrightWhite);
+ else
+ showVerb();
+
+ _secondObjectNeeded = false;
+ _firstObjectSet = false;
+ return;
+ }
+ } else {
+ if (verb == getVerbType(kVerbGive)) {
+ _secondObjectNeeded = true;
+ } else {
+ if (verb == getVerbType(kVerbUse)) {
+
+ if (_currentObjectFlags[0] & kObjUseWith) {
+ _secondObjectNeeded = true;
+ }
+ }
+ }
+
+ if (!_secondObjectNeeded) {
+ _pendingObject[0] = _currentObject[0];
+ _pendingObject[1] = ID_NOTHING;
+ _pendingVerb = verb;
+
+ _secondObjectNeeded = false;
+ _firstObjectSet = false;
+ } else {
+ _firstObjectSet = true;
+ }
+ }
+
+ _leftButtonVerb = verb;
+ if (_pendingVerb > getVerbType(kVerbNone))
+ showVerb(kITEColorBrightWhite);
+ else
+ showVerb();
+ }
+
+}
+
+void Script::playfieldClick(const Point& mousePoint, bool leftButton) {
+ Location pickLocation;
+ const HitZone *hitZone;
+ Point specialPoint;
+
+ _vm->_actor->abortSpeech();
+
+ if ((_vm->_actor->_protagonist->_currentAction != kActionWait) &&
+ (_vm->_actor->_protagonist->_currentAction != kActionFreeze) &&
+ (_vm->_actor->_protagonist->_currentAction != kActionWalkToLink) &&
+ (_vm->_actor->_protagonist->_currentAction != kActionWalkToPoint)) {
+ return;
+ }
+ if (_pendingVerb > getVerbType(kVerbNone)) {
+ setLeftButtonVerb(getVerbType(kVerbWalkTo));
+ }
+
+ if (_pointerObject != ID_NOTHING) {
+ hitObject( leftButton );
+ } else {
+ _pendingObject[0] = ID_NOTHING;
+ _pendingObject[1] = ID_NOTHING;
+ _pendingVerb = getVerbType(kVerbWalkTo);
+ }
+
+
+ // tiled stuff
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _vm->_isoMap->screenPointToTileCoords(mousePoint, pickLocation);
+ } else {
+ pickLocation.fromScreenPoint(mousePoint);
+ }
+
+
+ hitZone = NULL;
+
+ if (objectTypeId(_pendingObject[0]) == kGameObjectHitZone) {
+ hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(_pendingObject[0]));
+ } else {
+ if ((_pendingVerb == getVerbType(kVerbUse)) && (objectTypeId(_pendingObject[1]) == kGameObjectHitZone)) {
+ hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(_pendingObject[1]));
+ }
+ }
+
+ if (hitZone != NULL) {
+ if (hitZone->getFlags() & kHitZoneNoWalk) {
+ _vm->_actor->actorFaceTowardsPoint(ID_PROTAG, pickLocation);
+ doVerb();
+ return;
+ }
+
+ if (hitZone->getFlags() & kHitZoneProject) {
+ if (!hitZone->getSpecialPoint(specialPoint)) {
+ // Original behaved this way and this prevents from crash
+ // at ruins. See bug #1257459
+ specialPoint.x = specialPoint.y = 0;
+ }
+
+ // tiled stuff
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ pickLocation.u() = specialPoint.x;
+ pickLocation.v() = specialPoint.y;
+ pickLocation.z = _vm->_actor->_protagonist->_location.z;
+ } else {
+ pickLocation.fromScreenPoint(specialPoint);
+ }
+ }
+ }
+
+ if ((_pendingVerb == getVerbType(kVerbWalkTo)) ||
+ (_pendingVerb == getVerbType(kVerbPickUp)) ||
+ (_pendingVerb == getVerbType(kVerbOpen)) ||
+ (_pendingVerb == getVerbType(kVerbClose)) ||
+ (_pendingVerb == getVerbType(kVerbUse))) {
+ _vm->_actor->actorWalkTo(ID_PROTAG, pickLocation);
+ } else {
+ if (_pendingVerb == getVerbType(kVerbLookAt)) {
+ if (objectTypeId(_pendingObject[0]) != kGameObjectActor ) {
+ _vm->_actor->actorWalkTo(ID_PROTAG, pickLocation);
+ } else {
+ doVerb();
+ }
+ } else {
+ if ((_pendingVerb == getVerbType(kVerbTalkTo)) ||
+ (_pendingVerb == getVerbType(kVerbGive))) {
+ doVerb();
+ }
+ }
+ }
+}
+
+void Script::whichObject(const Point& mousePoint) {
+ uint16 objectId;
+ int16 objectFlags;
+ int newRightButtonVerb;
+ uint16 newObjectId;
+ ActorData *actor;
+ ObjectData *obj;
+ Point pickPoint;
+ Location pickLocation;
+ int hitZoneIndex;
+ const HitZone * hitZone;
+ PanelButton * panelButton;
+
+ objectId = ID_NOTHING;
+ objectFlags = 0;
+ _leftButtonVerb = _currentVerb;
+ newRightButtonVerb = getVerbType(kVerbNone);
+
+ if (_vm->_actor->_protagonist->_currentAction != kActionWalkDir) {
+ if (_vm->_scene->getHeight() >= mousePoint.y) {
+ newObjectId = _vm->_actor->hitTest(mousePoint, true);
+
+ if (newObjectId != ID_NOTHING) {
+ if (objectTypeId(newObjectId) == kGameObjectObject) {
+ objectId = newObjectId;
+ objectFlags = 0;
+ newRightButtonVerb = getVerbType(kVerbLookAt);
+
+ if ((_currentVerb == getVerbType(kVerbTalkTo)) || ((_currentVerb == getVerbType(kVerbGive)) && _firstObjectSet)) {
+ objectId = ID_NOTHING;
+ newObjectId = ID_NOTHING;
+ }
+ } else {
+ actor = _vm->_actor->getActor(newObjectId);
+ objectId = newObjectId;
+ objectFlags = kObjUseWith;
+ newRightButtonVerb = getVerbType(kVerbTalkTo);
+
+ if ((_currentVerb == getVerbType(kVerbPickUp)) ||
+ (_currentVerb == getVerbType(kVerbOpen)) ||
+ (_currentVerb == getVerbType(kVerbClose)) ||
+ ((_currentVerb == getVerbType(kVerbGive)) && !_firstObjectSet) ||
+ ((_currentVerb == getVerbType(kVerbUse)) && !(actor->_flags & kFollower))) {
+ objectId = ID_NOTHING;
+ newObjectId = ID_NOTHING;
+ }
+ }
+ }
+
+ if (newObjectId == ID_NOTHING) {
+
+ pickPoint = mousePoint;
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ pickPoint.y -= _vm->_actor->_protagonist->_location.z;
+ _vm->_isoMap->screenPointToTileCoords(pickPoint, pickLocation);
+ pickLocation.toScreenPointUV(pickPoint);
+ }
+
+ hitZoneIndex = _vm->_scene->_objectMap->hitTest(pickPoint);
+
+ if ((hitZoneIndex != -1)) {
+ hitZone = _vm->_scene->_objectMap->getHitZone(hitZoneIndex);
+ objectId = hitZone->getHitZoneId();
+ objectFlags = 0;
+ newRightButtonVerb = hitZone->getRightButtonVerb() & 0x7f;
+
+ if (_vm->getGameType() == GType_ITE) {
+
+ if (newRightButtonVerb == getVerbType(kVerbWalkOnly)) {
+ if (_firstObjectSet) {
+ objectId = ID_NOTHING;
+ } else {
+ newRightButtonVerb = _leftButtonVerb = getVerbType(kVerbWalkTo);
+ }
+ } else {
+ if (newRightButtonVerb == getVerbType(kVerbLookOnly)) {
+ if (_firstObjectSet) {
+ objectId = ID_NOTHING;
+ } else {
+ newRightButtonVerb = _leftButtonVerb = getVerbType(kVerbLookAt);
+ }
+ }
+ }
+
+ if (newRightButtonVerb >= getVerbType(kVerbOptions)) {
+ newRightButtonVerb = getVerbType(kVerbNone);
+ }
+ }
+
+ if ((_currentVerb == getVerbType(kVerbTalkTo)) || ((_currentVerb == getVerbType(kVerbGive)) && _firstObjectSet)) {
+ objectId = ID_NOTHING;
+ newObjectId = ID_NOTHING;
+ }
+
+ if ((_leftButtonVerb == getVerbType(kVerbUse)) && (hitZone->getRightButtonVerb() & 0x80)) {
+ objectFlags = kObjUseWith;
+ }
+ }
+ }
+ } else {
+ if ((_currentVerb == getVerbType(kVerbTalkTo)) || ((_currentVerb == getVerbType(kVerbGive)) && _firstObjectSet)) {
+ // no way
+ } else {
+ panelButton = _vm->_interface->inventoryHitTest(mousePoint);
+ if (panelButton) {
+ objectId = _vm->_interface->getInventoryContentByPanelButton(panelButton);
+ if (objectId != 0) {
+ obj = _vm->_actor->getObj(objectId);
+ newRightButtonVerb = getVerbType(kVerbLookAt);
+ if (obj->_interactBits & kObjUseWith) {
+ objectFlags = kObjUseWith;
+ }
+ }
+ }
+ }
+
+ if ((_currentVerb == getVerbType(kVerbPickUp)) || (_currentVerb == getVerbType(kVerbTalkTo)) || (_currentVerb == getVerbType(kVerbWalkTo))) {
+ _leftButtonVerb = getVerbType(kVerbLookAt);
+ }
+ }
+ }
+
+ if (objectId != _pointerObject) {
+ _pointerObject = objectId;
+ _currentObject[_firstObjectSet ? 1 : 0] = objectId;
+ _currentObjectFlags[_firstObjectSet ? 1 : 0] = objectFlags;
+ if (_pendingVerb == getVerbType(kVerbNone)) {
+ showVerb();
+ }
+ }
+
+ if (newRightButtonVerb != _rightButtonVerb) {
+ setRightButtonVerb(newRightButtonVerb);
+ }
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/script.h b/engines/saga/script.h
new file mode 100644
index 0000000000..bc11013b3f
--- /dev/null
+++ b/engines/saga/script.h
@@ -0,0 +1,637 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Scripting module private header
+
+#ifndef SAGA_SCRIPT_H
+#define SAGA_SCRIPT_H
+
+#include "saga/font.h"
+#include "saga/list.h"
+
+namespace Saga {
+
+#define COMMON_BUFFER_SIZE 1024 // Why 1024?
+
+#define S_LUT_ENTRYLEN_ITECD 22
+#define S_LUT_ENTRYLEN_ITEDISK 16
+
+#define SCRIPT_TBLENTRY_LEN 4
+
+#define SCRIPT_MAX 5000
+
+#define ITE_SCRIPT_FUNCTION_MAX 78
+#define IHNM_SCRIPT_FUNCTION_MAX 105
+#define DEFAULT_THREAD_STACK_SIZE 256
+
+enum AddressTypes {
+ kAddressCommon = 0, // offset from global variables
+ kAddressStatic = 1, // offset from global variables
+ kAddressModule = 2, // offset from start of module
+ kAddressStack = 3, // offset from stack
+ kAddressThread = 4 // offset from thread structure
+/* kAddressId = 5, // offset from const id object
+ kAddressIdIndirect = 6, // offset from stack id object
+ kAddressIndex = 7 // index from id*/
+};
+
+enum VerbTypeIds {
+ kVerbITENone = 0,
+ kVerbITEPickUp = 1,
+ kVerbITELookAt = 2,
+ kVerbITEWalkTo = 3,
+ kVerbITETalkTo = 4,
+ kVerbITEOpen = 5,
+ kVerbITEClose = 6,
+ kVerbITEGive = 7,
+ kVerbITEUse = 8,
+ kVerbITEOptions = 9,
+ kVerbITEEnter = 10,
+ kVerbITELeave = 11,
+ kVerbITEBegin = 12,
+ kVerbITEWalkOnly = 13,
+ kVerbITELookOnly = 14,
+
+
+ kVerbIHNMNone = 0,
+ kVerbIHNMWalk = 1,
+ kVerbIHNMLookAt = 2,
+ kVerbIHNMTake = 3,
+ kVerbIHNMUse = 4,
+ kVerbIHNMTalkTo = 5,
+ kVerbIHNMSwallow = 6,
+ kVerbIHNMGive = 7,
+ kVerbIHNMPush = 8,
+ kVerbIHNMOptions = 9,
+ kVerbIHNMEnter = 10,
+ kVerbIHNMLeave = 11,
+ kVerbIHNMBegin = 12,
+ kVerbIHNMWalkOnly = 13,
+ kVerbIHNMLookOnly = 14,
+
+ kVerbTypeIdsMax = kVerbITELookOnly + 1
+};
+
+enum VerbTypes {
+ kVerbNone,
+ kVerbWalkTo,
+ kVerbGive,
+ kVerbUse,
+ kVerbEnter,
+ kVerbLookAt,
+ kVerbPickUp,
+ kVerbOpen,
+ kVerbClose,
+ kVerbTalkTo,
+ kVerbWalkOnly,
+ kVerbLookOnly,
+ kVerbOptions
+};
+
+#define STHREAD_TIMESLICE 8
+
+enum ThreadVarTypes {
+ kThreadVarObject = 0,
+ kThreadVarWithObject = 1,
+ kThreadVarAction = 2,
+ kThreadVarActor = 3,
+
+ kThreadVarMax = kThreadVarActor + 1
+};
+
+enum ThreadFlags {
+ kTFlagNone = 0,
+ kTFlagWaiting = 1, // wait for even denoted in waitType
+ kTFlagFinished = 2,
+ kTFlagAborted = 4,
+ kTFlagAsleep = kTFlagWaiting | kTFlagFinished | kTFlagAborted // Combination of all flags which can halt a thread
+};
+
+enum ThreadWaitTypes {
+ kWaitTypeNone = 0, // waiting for nothing
+ kWaitTypeDelay = 1, // waiting for a timer
+ kWaitTypeSpeech = 2, // waiting for speech to finish
+ kWaitTypeDialogEnd = 3, // waiting for my dialog to finish
+ kWaitTypeDialogBegin = 4, // waiting for other dialog to finish
+ kWaitTypeWalk = 5, // waiting to finish walking
+ kWaitTypeRequest = 6, // a request is up
+ kWaitTypePause = 7,
+ kWaitTypePlacard = 8,
+ kWaitTypeStatusTextInput = 9,
+ kWaitTypeWaitFrames = 10 // IHNM. waiting for a frame count
+};
+
+enum OpCodes {
+ opNextBlock = 0x01,
+ opDup = 0x02,
+ opDrop = 0x03,
+ opZero = 0x04,
+ opOne = 0x05,
+ opConstint = 0x06,
+//...
+ opStrlit = 0x08,
+//...
+ opGetFlag = 0x0B,
+ opGetInt = 0x0C,
+//...
+ opPutFlag = 0x0F,
+ opPutInt = 0x10,
+ //...
+ opPutFlagV = 0x13,
+ opPutIntV = 0x14,
+//...
+ opCall = 0x17,
+ opCcall = 0x18,
+ opCcallV = 0x19,
+ opEnter = 0x1A,
+ opReturn = 0x1B,
+ opReturnV = 0x1C,
+ opJmp = 0x1D,
+ opJmpTrueV = 0x1E,
+ opJmpFalseV = 0x1F,
+ opJmpTrue = 0x20,
+ opJmpFalse = 0x21,
+ opJmpSwitch = 0x22,
+//...
+ opJmpRandom = 0x24,
+ opNegate = 0x25,
+ opNot = 0x26,
+ opCompl = 0x27,
+ opIncV = 0x28,
+ opDecV = 0x29,
+ opPostInc = 0x2A,
+ opPostDec = 0x2B,
+ opAdd = 0x2C,
+ opSub = 0x2D,
+ opMul = 0x2E,
+ opDiv = 0x2F,
+ opMod = 0x30,
+//...
+ opEq = 0x33,
+ opNe = 0x34,
+ opGt = 0x35,
+ opLt = 0x36,
+ opGe = 0x37,
+ opLe = 0x38,
+//...
+ opRsh = 0x3F,
+ opLsh = 0x40,
+ opAnd = 0x41,
+ opOr = 0x42,
+ opXor = 0x43,
+ opLAnd = 0x44,
+ opLOr = 0x45,
+ opLXor = 0x46,
+//...
+ opSpeak = 0x53,
+ opDialogBegin = 0x54,
+ opDialogEnd = 0x55,
+ opReply = 0x56,
+ opAnimate = 0x57
+};
+
+enum CycleFlags {
+ kCyclePong = 1 << 0,
+ kCycleOnce = 1 << 1,
+ kCycleRandom = 1 << 2,
+ kCycleReverse = 1 << 3
+};
+
+enum WalkFlags {
+ kWalkBackPedal = 1 << 0,
+ kWalkAsync = 1 << 1,
+ kWalkUseAngle = 1 << 2,
+ kWalkFace = 1 << 5
+};
+
+enum ReplyFlags {
+ kReplyOnce = 1 << 0,
+ kReplySummary = 1 << 1,
+ kReplyCondition = 1 << 2
+};
+
+struct EntryPoint {
+ uint16 nameOffset;
+ uint16 offset;
+};
+
+struct VoiceLUT {
+ uint16 voicesCount;
+ uint16 *voices;
+ void freeMem() {
+ voicesCount = 0;
+ free(voices);
+ }
+ VoiceLUT() {
+ memset(this, 0, sizeof(*this));
+ }
+};
+
+struct ModuleData {
+ bool loaded; // is it loaded or not?
+ int scriptResourceId;
+ int stringsResourceId;
+ int voicesResourceId;
+
+ byte *moduleBase; // all base module
+ uint16 moduleBaseSize; // base module size
+ uint16 staticSize; // size of static data
+ uint staticOffset; // offset of static data begining in _commonBuffer
+
+ uint16 entryPointsTableOffset; // offset of entrypoint table in moduleBase
+ uint16 entryPointsCount;
+ EntryPoint *entryPoints;
+
+ StringsTable strings;
+ VoiceLUT voiceLUT;
+ void freeMem() {
+ strings.freeMem();
+ voiceLUT.freeMem();
+ free(moduleBase);
+ free(entryPoints);
+ memset(this, 0x0, sizeof(*this));
+ }
+};
+
+class ScriptThread {
+public:
+ uint16 *_stackBuf;
+ uint16 _stackSize; // stack size in uint16
+
+ uint16 _stackTopIndex;
+ uint16 _frameIndex;
+
+ uint16 _threadVars[kThreadVarMax];
+
+ byte *_moduleBase; //
+ uint16 _moduleBaseSize;
+
+ byte *_commonBase; //
+ byte *_staticBase; //
+ VoiceLUT *_voiceLUT; //
+ StringsTable *_strings; //
+
+ int _flags; // ThreadFlags
+ int _waitType; // ThreadWaitTypes
+ uint _sleepTime;
+ void *_threadObj; // which object we're handling
+
+ int16 _returnValue;
+
+ uint16 _instructionOffset; // Instruction offset
+
+ int32 _frameWait;
+
+public:
+ byte *baseAddress(byte addrMode) {
+ switch (addrMode) {
+ case kAddressCommon:
+ return _commonBase;
+ case kAddressStatic:
+ return _staticBase;
+ case kAddressModule:
+ return _moduleBase;
+ case kAddressStack:
+ return (byte*)&_stackBuf[_frameIndex];
+ case kAddressThread:
+ return (byte*)_threadVars;
+ default:
+ return _commonBase;
+ }
+ }
+
+ int16 stackTop() {
+ return (int16)_stackBuf[_stackTopIndex];
+ }
+
+ uint pushedSize() {
+ return _stackSize - _stackTopIndex - 2;
+ }
+
+ void push(int16 value) {
+ if (_stackTopIndex <= 0) {
+ error("ScriptThread::push() stack overflow");
+ }
+ _stackBuf[--_stackTopIndex] = (uint16)value;
+ }
+
+ int16 pop() {
+ if (_stackTopIndex >= _stackSize) {
+ error("ScriptThread::pop() stack underflow");
+ }
+ return (int16)_stackBuf[_stackTopIndex++];
+ }
+
+
+// wait stuff
+ void wait(int waitType) {
+ _waitType = waitType;
+ _flags |= kTFlagWaiting;
+ }
+
+ void waitWalk(void *threadObj) {
+ wait(kWaitTypeWalk);
+ _threadObj = threadObj;
+ }
+
+ void waitDelay(int sleepTime) {
+ wait(kWaitTypeDelay);
+ _sleepTime = sleepTime;
+ }
+
+ ScriptThread() {
+ memset(this, 0xFE, sizeof(*this));
+ _stackBuf = NULL;
+ }
+ ~ScriptThread() {
+ free(_stackBuf);
+ }
+};
+
+typedef SortedList<ScriptThread> ScriptThreadList;
+
+
+#define SCRIPTFUNC_PARAMS ScriptThread *thread, int nArgs, bool &disContinue
+
+class Script {
+public:
+ StringsTable _mainStrings;
+
+ Script(SagaEngine *vm);
+ ~Script();
+
+ void loadModule(int scriptModuleNumber);
+ void freeModules();
+
+ void doVerb();
+ void showVerb(int statusColor = -1);
+ void setVerb(int verb);
+ int getCurrentVerb() const { return _currentVerb; }
+ void setPointerVerb();
+ void whichObject(const Point& mousePoint);
+ void hitObject(bool leftButton);
+ void playfieldClick(const Point& mousePoint, bool leftButton);
+
+ void setLeftButtonVerb(int verb);
+ int getLeftButtonVerb() const { return _leftButtonVerb; }
+ void setRightButtonVerb(int verb);
+ int getRightButtonVerb() const { return _rightButtonVerb; }
+ void setNonPlayfieldVerb() {
+ setRightButtonVerb(getVerbType(kVerbNone));
+ _pointerObject = ID_NOTHING;
+ _currentObject[_firstObjectSet ? 1 : 0] = ID_NOTHING;
+ }
+ void setNoPendingVerb() {
+ _pendingVerb = getVerbType(kVerbNone);
+ _currentObject[0] = _currentObject[0] = ID_NOTHING;
+ setPointerVerb();
+ }
+ int getVerbType(VerbTypes verbType);
+
+private:
+ // When reading or writing data to the common buffer, we have to use a
+ // well-defined byte order since it's stored in savegames. Otherwise,
+ // we use native byte ordering since that data may be accessed in other
+ // ways than through these functions.
+ //
+ // The "module" area is a special case, which possibly never ever
+ // happens. But if it does, we need well-defined byte ordering.
+
+ uint16 readUint16(byte *addr, byte addrMode) {
+ switch (addrMode) {
+ case kAddressCommon:
+ case kAddressStatic:
+ case kAddressModule:
+ return READ_LE_UINT16(addr);
+ default:
+ return READ_UINT16(addr);
+ }
+ }
+
+ void writeUint16(byte *addr, uint16 value, byte addrMode) {
+ switch (addrMode) {
+ case kAddressCommon:
+ case kAddressStatic:
+ case kAddressModule:
+ WRITE_LE_UINT16(addr, value);
+ break;
+ default:
+ WRITE_UINT16(addr, value);
+ break;
+ }
+ }
+
+ SagaEngine *_vm;
+ ResourceContext *_scriptContext;
+
+ uint16 _modulesLUTEntryLen;
+ ModuleData *_modules;
+ int _modulesCount;
+ TextListEntry *_placardTextEntry;
+
+protected:
+ friend class SagaEngine;
+ byte *_commonBuffer;
+ uint _commonBufferSize;
+
+private:
+ uint _staticSize;
+
+ ScriptThreadList _threadList;
+
+ ScriptThread *_conversingThread;
+
+//verb
+ bool _firstObjectSet;
+ bool _secondObjectNeeded;
+ uint16 _currentObject[2];
+ int16 _currentObjectFlags[2];
+ int _currentVerb;
+ int _stickyVerb;
+ int _leftButtonVerb;
+ int _rightButtonVerb;
+
+public:
+ uint16 _pendingObject[2];
+ int _pendingVerb;
+ uint16 _pointerObject;
+
+ bool _skipSpeeches;
+ bool _abortEnabled;
+
+ VoiceLUT _globalVoiceLUT;
+
+public:
+ ScriptThread *createThread(uint16 scriptModuleNumber, uint16 scriptEntryPointNumber);
+ int executeThread(ScriptThread *thread, int entrypointNumber);
+ void executeThreads(uint msec);
+ void completeThread(void);
+ void abortAllThreads(void);
+
+ void wakeUpActorThread(int waitType, void *threadObj);
+ void wakeUpThreads(int waitType);
+ void wakeUpThreadsDelayed(int waitType, int sleepTime);
+
+ void loadVoiceLUT(VoiceLUT &voiceLUT, const byte *resourcePointer, size_t resourceLength);
+
+private:
+ void loadModuleBase(ModuleData &module, const byte *resourcePointer, size_t resourceLength);
+
+ // runThread returns true if we should break running of other threads
+ bool runThread(ScriptThread *thread, uint instructionLimit);
+ void setThreadEntrypoint(ScriptThread *thread, int entrypointNumber);
+
+public:
+ void finishDialog(int replyID, int flags, int bitOffset);
+
+private:
+
+ typedef void (Script::*ScriptFunctionType)(SCRIPTFUNC_PARAMS);
+
+ struct ScriptFunctionDescription {
+ ScriptFunctionType scriptFunction;
+ const char *scriptFunctionName;
+ };
+ const ScriptFunctionDescription *_scriptFunctionsList;
+
+ void setupScriptFuncList(void);
+
+ void sfPutString(SCRIPTFUNC_PARAMS);
+ void sfWait(SCRIPTFUNC_PARAMS);
+ void sfTakeObject(SCRIPTFUNC_PARAMS);
+ void sfIsCarried(SCRIPTFUNC_PARAMS);
+ void sfStatusBar(SCRIPTFUNC_PARAMS);
+ void sfMainMode(SCRIPTFUNC_PARAMS);
+ void sfScriptWalkTo(SCRIPTFUNC_PARAMS);
+ void sfScriptDoAction(SCRIPTFUNC_PARAMS);
+ void sfSetActorFacing(SCRIPTFUNC_PARAMS);
+ void sfStartBgdAnim(SCRIPTFUNC_PARAMS);
+ void sfStopBgdAnim(SCRIPTFUNC_PARAMS);
+ void sfLockUser(SCRIPTFUNC_PARAMS);
+ void sfPreDialog(SCRIPTFUNC_PARAMS);
+ void sfKillActorThreads(SCRIPTFUNC_PARAMS);
+ void sfFaceTowards(SCRIPTFUNC_PARAMS);
+ void sfSetFollower(SCRIPTFUNC_PARAMS);
+ void sfScriptGotoScene(SCRIPTFUNC_PARAMS);
+
+ void sfSetObjImage(SCRIPTFUNC_PARAMS);
+ void sfSetObjName(SCRIPTFUNC_PARAMS);
+ void sfGetObjImage(SCRIPTFUNC_PARAMS);
+ void sfGetNumber(SCRIPTFUNC_PARAMS);
+ void sfScriptOpenDoor(SCRIPTFUNC_PARAMS);
+ void sfScriptCloseDoor(SCRIPTFUNC_PARAMS);
+ void sfSetBgdAnimSpeed(SCRIPTFUNC_PARAMS);
+ void SF_cycleColors(SCRIPTFUNC_PARAMS);
+ void sfDoCenterActor(SCRIPTFUNC_PARAMS);
+ void sfStartBgdAnimSpeed(SCRIPTFUNC_PARAMS);
+ void sfScriptWalkToAsync(SCRIPTFUNC_PARAMS);
+ void sfEnableZone(SCRIPTFUNC_PARAMS);
+ void sfSetActorState(SCRIPTFUNC_PARAMS);
+ void sfScriptMoveTo(SCRIPTFUNC_PARAMS);
+ void sfSceneEq(SCRIPTFUNC_PARAMS);
+ void sfDropObject(SCRIPTFUNC_PARAMS);
+ void sfFinishBgdAnim(SCRIPTFUNC_PARAMS);
+ void sfSwapActors(SCRIPTFUNC_PARAMS);
+ void sfSimulSpeech(SCRIPTFUNC_PARAMS);
+ void sfScriptWalk(SCRIPTFUNC_PARAMS);
+ void sfCycleFrames(SCRIPTFUNC_PARAMS);
+ void sfSetFrame(SCRIPTFUNC_PARAMS);
+ void sfSetPortrait(SCRIPTFUNC_PARAMS);
+ void sfSetProtagPortrait(SCRIPTFUNC_PARAMS);
+ void sfChainBgdAnim(SCRIPTFUNC_PARAMS);
+ void sfScriptSpecialWalk(SCRIPTFUNC_PARAMS);
+ void sfPlaceActor(SCRIPTFUNC_PARAMS);
+ void sfCheckUserInterrupt(SCRIPTFUNC_PARAMS);
+ void sfScriptWalkRelative(SCRIPTFUNC_PARAMS);
+ void sfScriptMoveRelative(SCRIPTFUNC_PARAMS);
+ void sfSimulSpeech2(SCRIPTFUNC_PARAMS);
+ void sfPlacard(SCRIPTFUNC_PARAMS);
+ void sfPlacardOff(SCRIPTFUNC_PARAMS);
+ void sfSetProtagState(SCRIPTFUNC_PARAMS);
+ void sfResumeBgdAnim(SCRIPTFUNC_PARAMS);
+ void sfThrowActor(SCRIPTFUNC_PARAMS);
+ void sfWaitWalk(SCRIPTFUNC_PARAMS);
+ void sfScriptSceneID(SCRIPTFUNC_PARAMS);
+ void sfChangeActorScene(SCRIPTFUNC_PARAMS);
+ void sfScriptClimb(SCRIPTFUNC_PARAMS);
+ void sfSetDoorState(SCRIPTFUNC_PARAMS);
+ void sfSetActorZ(SCRIPTFUNC_PARAMS);
+ void sfScriptText(SCRIPTFUNC_PARAMS);
+ void sfGetActorX(SCRIPTFUNC_PARAMS);
+ void sfGetActorY(SCRIPTFUNC_PARAMS);
+ void sfEraseDelta(SCRIPTFUNC_PARAMS);
+ void sfPlayMusic(SCRIPTFUNC_PARAMS);
+ void sfPickClimbOutPos(SCRIPTFUNC_PARAMS);
+ void sfTossRif(SCRIPTFUNC_PARAMS);
+ void sfShowControls(SCRIPTFUNC_PARAMS);
+ void sfShowMap(SCRIPTFUNC_PARAMS);
+ void sfPuzzleWon(SCRIPTFUNC_PARAMS);
+ void sfEnableEscape(SCRIPTFUNC_PARAMS);
+ void sfPlaySound(SCRIPTFUNC_PARAMS);
+ void sfPlayLoopedSound(SCRIPTFUNC_PARAMS);
+ void sfGetDeltaFrame(SCRIPTFUNC_PARAMS);
+ void sfShowProtect(SCRIPTFUNC_PARAMS);
+ void sfProtectResult(SCRIPTFUNC_PARAMS);
+ void sfRand(SCRIPTFUNC_PARAMS);
+ void sfFadeMusic(SCRIPTFUNC_PARAMS);
+ void sfScriptStartCutAway(SCRIPTFUNC_PARAMS);
+ void sfReturnFromCutAway(SCRIPTFUNC_PARAMS);
+ void sfEndCutAway(SCRIPTFUNC_PARAMS);
+ void sfGetMouseClicks(SCRIPTFUNC_PARAMS);
+ void sfResetMouseClicks(SCRIPTFUNC_PARAMS);
+ void sfWaitFrames(SCRIPTFUNC_PARAMS);
+ void sfScriptFade(SCRIPTFUNC_PARAMS);
+ void sfPlayVoice(SCRIPTFUNC_PARAMS);
+ void sfVstopFX(SCRIPTFUNC_PARAMS);
+ void sfVstopLoopedFX(SCRIPTFUNC_PARAMS);
+ void sfDemoIsInteractive(SCRIPTFUNC_PARAMS);
+ void sfVsetTrack(SCRIPTFUNC_PARAMS);
+ void sfDebugShowData(SCRIPTFUNC_PARAMS);
+ void sfNull(SCRIPTFUNC_PARAMS);
+ void sfWaitFramesEsc(SCRIPTFUNC_PARAMS);
+ void sfPsychicProfile(SCRIPTFUNC_PARAMS);
+ void sfPsychicProfileOff(SCRIPTFUNC_PARAMS);
+ void sfSetSpeechBox(SCRIPTFUNC_PARAMS);
+ void sfSetChapterPoints(SCRIPTFUNC_PARAMS);
+ void sfSetPortraitBgColor(SCRIPTFUNC_PARAMS);
+ void sfScriptStartVideo(SCRIPTFUNC_PARAMS);
+ void sfScriptReturnFromVideo(SCRIPTFUNC_PARAMS);
+ void sfScriptEndVideo(SCRIPTFUNC_PARAMS);
+ void sf87(SCRIPTFUNC_PARAMS);
+ void sf88(SCRIPTFUNC_PARAMS);
+ void sf89(SCRIPTFUNC_PARAMS);
+ void sfGetPoints(SCRIPTFUNC_PARAMS);
+ void sfSetGlobalFlag(SCRIPTFUNC_PARAMS);
+ void sfClearGlobalFlag(SCRIPTFUNC_PARAMS);
+ void sfTestGlobalFlag(SCRIPTFUNC_PARAMS);
+ void sfSetPoints(SCRIPTFUNC_PARAMS);
+ void sf103(SCRIPTFUNC_PARAMS);
+ void sfDisableAbortSpeeches(SCRIPTFUNC_PARAMS);
+
+ void SF_stub(const char *name, ScriptThread *thread, int nArgs);
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/sfuncs.cpp b/engines/saga/sfuncs.cpp
new file mode 100644
index 0000000000..9caa15150b
--- /dev/null
+++ b/engines/saga/sfuncs.cpp
@@ -0,0 +1,2038 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Scripting module script function component
+
+#include "saga/saga.h"
+
+#include "saga/gfx.h"
+#include "saga/actor.h"
+#include "saga/animation.h"
+#include "saga/console.h"
+#include "saga/events.h"
+#include "saga/font.h"
+#include "saga/interface.h"
+#include "saga/music.h"
+#include "saga/itedata.h"
+#include "saga/puzzle.h"
+#include "saga/render.h"
+#include "saga/sound.h"
+#include "saga/sndres.h"
+
+#include "saga/script.h"
+#include "saga/objectmap.h"
+
+#include "saga/scene.h"
+#include "saga/isomap.h"
+#include "saga/resnames.h"
+
+#include "common/config-manager.h"
+
+namespace Saga {
+
+#define OPCODE(x) {&Script::x, #x}
+
+void Script::setupScriptFuncList(void) {
+ static const ScriptFunctionDescription ITEscriptFunctionsList[ITE_SCRIPT_FUNCTION_MAX] = {
+ OPCODE(sfPutString),
+ OPCODE(sfWait),
+ OPCODE(sfTakeObject),
+ OPCODE(sfIsCarried),
+ OPCODE(sfStatusBar),
+ OPCODE(sfMainMode),
+ OPCODE(sfScriptWalkTo),
+ OPCODE(sfScriptDoAction),
+ OPCODE(sfSetActorFacing),
+ OPCODE(sfStartBgdAnim),
+ OPCODE(sfStopBgdAnim),
+ OPCODE(sfLockUser),
+ OPCODE(sfPreDialog),
+ OPCODE(sfKillActorThreads),
+ OPCODE(sfFaceTowards),
+ OPCODE(sfSetFollower),
+ OPCODE(sfScriptGotoScene),
+ OPCODE(sfSetObjImage),
+ OPCODE(sfSetObjName),
+ OPCODE(sfGetObjImage),
+ OPCODE(sfGetNumber),
+ OPCODE(sfScriptOpenDoor),
+ OPCODE(sfScriptCloseDoor),
+ OPCODE(sfSetBgdAnimSpeed),
+ OPCODE(SF_cycleColors),
+ OPCODE(sfDoCenterActor),
+ OPCODE(sfStartBgdAnimSpeed),
+ OPCODE(sfScriptWalkToAsync),
+ OPCODE(sfEnableZone),
+ OPCODE(sfSetActorState),
+ OPCODE(sfScriptMoveTo),
+ OPCODE(sfSceneEq),
+ OPCODE(sfDropObject),
+ OPCODE(sfFinishBgdAnim),
+ OPCODE(sfSwapActors),
+ OPCODE(sfSimulSpeech),
+ OPCODE(sfScriptWalk),
+ OPCODE(sfCycleFrames),
+ OPCODE(sfSetFrame),
+ OPCODE(sfSetPortrait),
+ OPCODE(sfSetProtagPortrait),
+ OPCODE(sfChainBgdAnim),
+ OPCODE(sfScriptSpecialWalk),
+ OPCODE(sfPlaceActor),
+ OPCODE(sfCheckUserInterrupt),
+ OPCODE(sfScriptWalkRelative),
+ OPCODE(sfScriptMoveRelative),
+ OPCODE(sfSimulSpeech2),
+ OPCODE(sfPlacard),
+ OPCODE(sfPlacardOff),
+ OPCODE(sfSetProtagState),
+ OPCODE(sfResumeBgdAnim),
+ OPCODE(sfThrowActor),
+ OPCODE(sfWaitWalk),
+ OPCODE(sfScriptSceneID),
+ OPCODE(sfChangeActorScene),
+ OPCODE(sfScriptClimb),
+ OPCODE(sfSetDoorState),
+ OPCODE(sfSetActorZ),
+ OPCODE(sfScriptText),
+ OPCODE(sfGetActorX),
+ OPCODE(sfGetActorY),
+ OPCODE(sfEraseDelta),
+ OPCODE(sfPlayMusic),
+ OPCODE(sfPickClimbOutPos),
+ OPCODE(sfTossRif),
+ OPCODE(sfShowControls),
+ OPCODE(sfShowMap),
+ OPCODE(sfPuzzleWon),
+ OPCODE(sfEnableEscape),
+ OPCODE(sfPlaySound),
+ OPCODE(sfPlayLoopedSound),
+ OPCODE(sfGetDeltaFrame),
+ OPCODE(sfShowProtect),
+ OPCODE(sfProtectResult),
+ OPCODE(sfRand),
+ OPCODE(sfFadeMusic),
+ OPCODE(sfPlayVoice)
+ };
+
+static const ScriptFunctionDescription IHNMscriptFunctionsList[IHNM_SCRIPT_FUNCTION_MAX] = {
+ OPCODE(sfNull),
+ OPCODE(sfWait),
+ OPCODE(sfTakeObject),
+ OPCODE(sfIsCarried),
+ OPCODE(sfStatusBar),
+ OPCODE(sfMainMode),
+ OPCODE(sfScriptWalkTo),
+ OPCODE(sfScriptDoAction),
+ OPCODE(sfSetActorFacing),
+ OPCODE(sfStartBgdAnim),
+ OPCODE(sfStopBgdAnim),
+ OPCODE(sfNull),
+ OPCODE(sfPreDialog),
+ OPCODE(sfKillActorThreads),
+ OPCODE(sfFaceTowards),
+ OPCODE(sfSetFollower),
+ OPCODE(sfScriptGotoScene),
+ OPCODE(sfSetObjImage),
+ OPCODE(sfSetObjName),
+ OPCODE(sfGetObjImage),
+ OPCODE(sfGetNumber),
+ OPCODE(sfScriptOpenDoor),
+ OPCODE(sfScriptCloseDoor),
+ OPCODE(sfSetBgdAnimSpeed),
+ OPCODE(SF_cycleColors),
+ OPCODE(sfDoCenterActor),
+ OPCODE(sfStartBgdAnimSpeed),
+ OPCODE(sfScriptWalkToAsync),
+ OPCODE(sfEnableZone),
+ OPCODE(sfSetActorState),
+ OPCODE(sfScriptMoveTo),
+ OPCODE(sfSceneEq),
+ OPCODE(sfDropObject),
+ OPCODE(sfFinishBgdAnim),
+ OPCODE(sfSwapActors),
+ OPCODE(sfSimulSpeech),
+ OPCODE(sfScriptWalk),
+ OPCODE(sfCycleFrames),
+ OPCODE(sfSetFrame),
+ OPCODE(sfSetPortrait),
+ OPCODE(sfSetProtagPortrait),
+ OPCODE(sfChainBgdAnim),
+ OPCODE(sfScriptSpecialWalk),
+ OPCODE(sfPlaceActor),
+ OPCODE(sfCheckUserInterrupt),
+ OPCODE(sfScriptWalkRelative),
+ OPCODE(sfScriptMoveRelative),
+ OPCODE(sfSimulSpeech2),
+ OPCODE(sfPsychicProfile),
+ OPCODE(sfPsychicProfileOff),
+ OPCODE(sfSetProtagState),
+ OPCODE(sfResumeBgdAnim),
+ OPCODE(sfThrowActor),
+ OPCODE(sfWaitWalk),
+ OPCODE(sfScriptSceneID),
+ OPCODE(sfChangeActorScene),
+ OPCODE(sfScriptClimb),
+ OPCODE(sfSetDoorState),
+ OPCODE(sfSetActorZ),
+ OPCODE(sfScriptText),
+ OPCODE(sfGetActorX),
+ OPCODE(sfGetActorY),
+ OPCODE(sfEraseDelta),
+ OPCODE(sfPlayMusic),
+ OPCODE(sfNull),
+ OPCODE(sfEnableEscape),
+ OPCODE(sfPlaySound),
+ OPCODE(sfPlayLoopedSound),
+ OPCODE(sfGetDeltaFrame),
+ OPCODE(sfNull),
+ OPCODE(sfNull),
+ OPCODE(sfRand),
+ OPCODE(sfFadeMusic),
+ OPCODE(sfNull),
+ OPCODE(sfSetChapterPoints),
+ OPCODE(sfSetPortraitBgColor),
+ OPCODE(sfScriptStartCutAway),
+ OPCODE(sfReturnFromCutAway),
+ OPCODE(sfEndCutAway),
+ OPCODE(sfGetMouseClicks),
+ OPCODE(sfResetMouseClicks),
+ OPCODE(sfWaitFrames),
+ OPCODE(sfScriptFade),
+ OPCODE(sfScriptStartVideo),
+ OPCODE(sfScriptReturnFromVideo),
+ OPCODE(sfScriptEndVideo),
+ OPCODE(sfSetActorZ),
+ OPCODE(sf87),
+ OPCODE(sf88),
+ OPCODE(sf89),
+ OPCODE(sfVstopFX),
+ OPCODE(sfVstopLoopedFX),
+ OPCODE(sfNull),
+ OPCODE(sfDemoIsInteractive),
+ OPCODE(sfVsetTrack),
+ OPCODE(sfGetPoints),
+ OPCODE(sfSetGlobalFlag),
+ OPCODE(sfClearGlobalFlag),
+ OPCODE(sfTestGlobalFlag),
+ OPCODE(sfSetPoints),
+ OPCODE(sfSetSpeechBox),
+ OPCODE(sfDebugShowData),
+ OPCODE(sfWaitFramesEsc),
+ OPCODE(sf103),
+ OPCODE(sfDisableAbortSpeeches)
+ };
+ if (_vm->getGameType() == GType_IHNM)
+ _scriptFunctionsList = IHNMscriptFunctionsList;
+ else
+ _scriptFunctionsList = ITEscriptFunctionsList;
+}
+
+// Script function #0 (0x00)
+// Print a debugging message
+void Script::sfPutString(SCRIPTFUNC_PARAMS) {
+ const char *str;
+ str = thread->_strings->getString(thread->pop());
+
+ _vm->_console->DebugPrintf("sfPutString: %s\n",str);
+ debug(0, "sfPutString: %s", str);
+}
+
+// Script function #1 (0x01) blocking
+// Param1: time in ticks
+void Script::sfWait(SCRIPTFUNC_PARAMS) {
+ int16 time;
+ time = thread->pop();
+
+ if (!_skipSpeeches) {
+ thread->waitDelay(ticksToMSec(time)); // put thread to sleep
+ }
+}
+
+// Script function #2 (0x02)
+void Script::sfTakeObject(SCRIPTFUNC_PARAMS) {
+ uint16 objectId = thread->pop();
+ ObjectData *obj;
+ obj = _vm->_actor->getObj(objectId);
+ if (obj->_sceneNumber != ITE_SCENE_INV) {
+ obj->_sceneNumber = ITE_SCENE_INV;
+ //4debug for (int j=0;j<17;j++)
+ _vm->_interface->addToInventory(objectId);
+ }
+}
+
+// Script function #3 (0x03)
+// Check if an object is carried.
+void Script::sfIsCarried(SCRIPTFUNC_PARAMS) {
+ uint16 objectId = thread->pop();
+ CommonObjectData *object;
+ if (_vm->_actor->validObjId(objectId)) {
+ object = _vm->_actor->getObj(objectId);
+ thread->_returnValue = (object->_sceneNumber == ITE_SCENE_INV) ? 1 : 0;
+ } else {
+ thread->_returnValue = 0;
+ }
+
+
+}
+
+// Script function #4 (0x04) nonblocking
+// Set the command display to the specified text string
+// Param1: dialogue index of string
+void Script::sfStatusBar(SCRIPTFUNC_PARAMS) {
+ int16 stringIndex = thread->pop();
+
+ _vm->_interface->setStatusText(thread->_strings->getString(stringIndex));
+}
+
+// Script function #5 (0x05)
+void Script::sfMainMode(SCRIPTFUNC_PARAMS) {
+ _vm->_actor->_centerActor = _vm->_actor->_protagonist;
+
+ showVerb();
+ _vm->_interface->activate();
+ _vm->_interface->setMode(kPanelMain);
+
+ if (_vm->getGameType() == GType_ITE)
+ setPointerVerb();
+}
+
+// Script function #6 (0x06) blocking
+// Param1: actor id
+// Param2: actor x
+// Param3: actor y
+void Script::sfScriptWalkTo(SCRIPTFUNC_PARAMS) {
+ uint16 actorId;
+ Location actorLocation;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ actorLocation.x = thread->pop();
+ actorLocation.y = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ actorLocation.z = actor->_location.z;
+
+ actor->_flags &= ~kFollower;
+
+ if (_vm->_actor->actorWalkTo(actorId, actorLocation)) {
+ thread->waitWalk(actor);
+ }
+}
+
+// Script function #7 (0x07)
+// Param1: actor id
+// Param2: action
+// Param3: theObject
+// Param4: withObject
+void Script::sfScriptDoAction(SCRIPTFUNC_PARAMS) {
+ uint16 objectId;
+ uint16 action;
+ uint16 theObject;
+ uint16 withObject;
+ int16 scriptEntryPointNumber;
+ int16 moduleNumber;
+ ActorData *actor;
+ ObjectData *obj;
+ const HitZone *hitZone;
+ Event event;
+
+ objectId = thread->pop();
+ action = thread->pop();
+ theObject = thread->pop();
+ withObject = thread->pop();
+
+ switch (objectTypeId(objectId)) {
+ case kGameObjectObject:
+ obj = _vm->_actor->getObj(objectId);
+ scriptEntryPointNumber = obj->_scriptEntrypointNumber;
+ if (scriptEntryPointNumber <= 0) {
+ return;
+ }
+ moduleNumber = 0;
+ break;
+ case kGameObjectActor:
+ actor = _vm->_actor->getActor(objectId);
+ scriptEntryPointNumber = actor->_scriptEntrypointNumber;
+ if (scriptEntryPointNumber <= 0) {
+ return;
+ }
+ if (actor->_flags & (kProtagonist | kFollower)) {
+ moduleNumber = 0;
+ } else {
+ moduleNumber = _vm->_scene->getScriptModuleNumber();
+ }
+ break;
+ case kGameObjectHitZone:
+ case kGameObjectStepZone:
+ if (objectTypeId(objectId) == kGameObjectHitZone) {
+ hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(objectId));
+ } else {
+ hitZone = _vm->_scene->_actionMap->getHitZone(objectIdToIndex(objectId));
+ }
+ scriptEntryPointNumber = hitZone->getScriptNumber();
+ moduleNumber = _vm->_scene->getScriptModuleNumber();
+ break;
+ default:
+ error("Script::sfScriptDoAction wrong object type 0x%X", objectId);
+ }
+
+ event.type = kEvTOneshot;
+ event.code = kScriptEvent;
+ event.op = kEventExecNonBlocking;
+ event.time = 0;
+ event.param = moduleNumber;
+ event.param2 = scriptEntryPointNumber;
+ event.param3 = action; // Action
+ event.param4 = theObject; // Object
+ event.param5 = withObject; // With Object
+ event.param6 = objectId;
+
+ _vm->_events->queue(&event);
+}
+
+// Script function #8 (0x08) nonblocking
+// Param1: actor id
+// Param2: actor orientation
+void Script::sfSetActorFacing(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int actorDirection;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ actorDirection = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ actor->_facingDirection = actor->_actionDirection = actorDirection;
+ actor->_targetObject = ID_NOTHING;
+}
+
+// Script function #9 (0x09)
+void Script::sfStartBgdAnim(SCRIPTFUNC_PARAMS) {
+ int16 animId = thread->pop();
+ int16 cycles = thread->pop();
+
+ _vm->_anim->setCycles(animId, cycles);
+ _vm->_anim->setFrameTime(animId, ticksToMSec(kRepeatSpeedTicks));
+ _vm->_anim->play(animId, 0);
+
+ debug(1, "sfStartBgdAnim(%d, %d)", animId, cycles);
+}
+
+// Script function #10 (0x0A)
+void Script::sfStopBgdAnim(SCRIPTFUNC_PARAMS) {
+ int16 animId = thread->pop();
+
+ _vm->_anim->stop(animId);
+
+ debug(1, "sfStopBgdAnim(%d)", animId);
+}
+
+// Script function #11 (0x0B) nonblocking
+// If the parameter is true, the user interface is disabled while script
+// continues to run. If the parameter is false, the user interface is
+// reenabled.
+// Param1: boolean
+void Script::sfLockUser(SCRIPTFUNC_PARAMS) {
+ int16 lock;
+
+ lock = thread->pop();
+
+ if (lock) {
+ _vm->_interface->deactivate();
+ } else {
+ _vm->_interface->activate();
+ }
+
+}
+
+// Script function #12 (0x0C)
+// Disables mouse input, etc.
+void Script::sfPreDialog(SCRIPTFUNC_PARAMS) {
+ _vm->_interface->deactivate();
+ _vm->_interface->converseClear();
+ if (_vm->_interface->isInMainMode())
+ _vm->_interface->setMode(kPanelConverse);
+ else
+ _vm->_interface->converseDisplayText();
+
+ _vm->_interface->setMode(kPanelNull);
+}
+
+// Script function #13 (0x0D)
+void Script::sfKillActorThreads(SCRIPTFUNC_PARAMS) {
+ ScriptThread *anotherThread;
+ ScriptThreadList::iterator threadIterator;
+ int16 actorId;
+
+ actorId = thread->pop();
+
+
+ for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
+ anotherThread = threadIterator.operator->();
+ if ((anotherThread != thread) && (anotherThread->_threadVars[kThreadVarActor] == actorId)) {
+ anotherThread->_flags &= ~kTFlagWaiting;
+ anotherThread->_flags |= kTFlagAborted;
+ }
+ }
+}
+
+// Script function #14 (0x0E)
+// Param1: actor id
+// Param2: object id
+void Script::sfFaceTowards(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int16 targetObject;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ targetObject = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ actor->_targetObject = targetObject;
+}
+
+// Script function #15 (0x0F)
+// Param1: actor id
+// Param2: target object
+void Script::sfSetFollower(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int16 targetObject;
+
+ ActorData *actor;
+
+ actorId = thread->pop();
+ targetObject = thread->pop();
+
+ debug(1, "sfSetFollower(%d, %d) [%d]", actorId, targetObject, _vm->_actor->actorIdToIndex(actorId));
+
+ actor = _vm->_actor->getActor(actorId);
+ actor->_targetObject = targetObject;
+ if (targetObject != ID_NOTHING) {
+ actor->_flags |= kFollower;
+ actor->_actorFlags &= ~kActorNoFollow;
+ } else {
+ actor->_flags &= ~kFollower;
+ }
+}
+
+// Script function #16 (0x10)
+void Script::sfScriptGotoScene(SCRIPTFUNC_PARAMS) {
+ int16 sceneNumber;
+ int16 entrance;
+
+ sceneNumber = thread->pop();
+ entrance = thread->pop();
+ if (sceneNumber < 0) {
+ _vm->shutDown();
+ return;
+ }
+
+ if (_vm->getGameType() == GType_IHNM) {
+ warning("FIXME: implement sfScriptGotoScene differences for IHNM");
+
+ // Since it doesn't look like the IHNM scripts remove the
+ // cutaway after the intro, this is probably the best place to
+ // to it.
+ _vm->_anim->clearCutaway();
+ }
+
+ // It is possible to leave scene when converse panel is on,
+ // particulalrly it may happen at Moneychanger tent. This
+ // prevent this from happening.
+ if (_vm->_interface->getMode() == kPanelConverse) {
+ _vm->_interface->setMode(kPanelMain);
+ }
+
+ _vm->_scene->changeScene(sceneNumber, entrance, (sceneNumber == ITE_SCENE_ENDCREDIT1) ? kTransitionFade : kTransitionNoFade);
+
+ //TODO: placard stuff
+ _pendingVerb = _vm->_script->getVerbType(kVerbNone);
+ _currentObject[0] = _currentObject[1] = ID_NOTHING;
+ showVerb();
+}
+
+// Script function #17 (0x11)
+// Param1: object id
+// Param2: sprite index
+void Script::sfSetObjImage(SCRIPTFUNC_PARAMS) {
+ uint16 objectId;
+ uint16 spriteId;
+ ObjectData *obj;
+
+ objectId = thread->pop();
+ spriteId = thread->pop();
+
+ obj = _vm->_actor->getObj(objectId);
+ obj->_spriteListResourceId = OBJ_SPRITE_BASE + spriteId;
+ _vm->_interface->refreshInventory();
+}
+
+// Script function #18 (0x12)
+// Param1: object id
+// Param2: name index
+void Script::sfSetObjName(SCRIPTFUNC_PARAMS) {
+ uint16 objectId;
+ uint16 nameIdx;
+ ObjectData *obj;
+
+ objectId = thread->pop();
+ nameIdx = thread->pop();
+
+ obj = _vm->_actor->getObj(objectId);
+ obj->_nameIndex = nameIdx;
+}
+
+// Script function #19 (0x13)
+// Param1: object id
+void Script::sfGetObjImage(SCRIPTFUNC_PARAMS) {
+ uint16 objectId;
+ ObjectData *obj;
+
+ objectId = thread->pop();
+
+ obj = _vm->_actor->getObj(objectId);
+
+ if (_vm->getGameType() == GType_IHNM)
+ thread->_returnValue = obj->_spriteListResourceId;
+ else
+ thread->_returnValue = obj->_spriteListResourceId - OBJ_SPRITE_BASE;
+}
+
+// Script function #20 (0x14)
+void Script::sfGetNumber(SCRIPTFUNC_PARAMS) {
+ if (_vm->_interface->_statusTextInputState == kStatusTextInputFirstRun) {
+ _vm->_interface->enterStatusString();
+ thread->wait(kWaitTypeStatusTextInput);
+ disContinue = true;
+ } else {
+ if (_vm->_interface->_statusTextInputState == kStatusTextInputAborted) {
+ thread->_returnValue = -1;
+ } else {
+ thread->_returnValue = atoi(_vm->_interface->_statusTextInputString);
+ }
+
+ _vm->_interface->_statusTextInputState = kStatusTextInputFirstRun;
+ }
+}
+
+// Script function #21 (0x15)
+// Param1: door #
+void Script::sfScriptOpenDoor(SCRIPTFUNC_PARAMS) {
+ int16 doorNumber;
+ doorNumber = thread->pop();
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _vm->_isoMap->setTileDoorState(doorNumber, 1);
+ } else {
+ _vm->_scene->setDoorState(doorNumber, 0);
+ }
+}
+
+// Script function #22 (0x16)
+// Param1: door #
+void Script::sfScriptCloseDoor(SCRIPTFUNC_PARAMS) {
+ int16 doorNumber;
+ doorNumber = thread->pop();
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _vm->_isoMap->setTileDoorState(doorNumber, 0);
+ } else {
+ _vm->_scene->setDoorState(doorNumber, 0xff);
+ }
+}
+
+// Script function #23 (0x17)
+void Script::sfSetBgdAnimSpeed(SCRIPTFUNC_PARAMS) {
+ int16 animId = thread->pop();
+ int16 speed = thread->pop();
+
+ _vm->_anim->setFrameTime(animId, ticksToMSec(speed));
+ debug(1, "sfSetBgdAnimSpeed(%d, %d)", animId, speed);
+}
+
+// Script function #24 (0x18)
+void Script::SF_cycleColors(SCRIPTFUNC_PARAMS) {
+ SF_stub("SF_cycleColors", thread, nArgs);
+
+ error("Please, report this to sev");
+}
+
+// Script function #25 (0x19)
+// Param1: actor id
+void Script::sfDoCenterActor(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ actorId = thread->pop();
+
+ _vm->_actor->_centerActor = _vm->_actor->getActor(actorId);
+}
+
+// Script function #26 (0x1A) nonblocking
+// Starts the specified animation
+void Script::sfStartBgdAnimSpeed(SCRIPTFUNC_PARAMS) {
+ int16 animId = thread->pop();
+ int16 cycles = thread->pop();
+ int16 speed = thread->pop();
+
+ _vm->_anim->setCycles(animId, cycles);
+ _vm->_anim->setFrameTime(animId, ticksToMSec(speed));
+ _vm->_anim->play(animId, 0);
+
+ debug(1, "sfStartBgdAnimSpeed(%d, %d, %d)", animId, cycles, speed);
+}
+
+// Script function #27 (0x1B) nonblocking
+// Param1: actor id
+// Param2: actor x
+// Param3: actor y
+void Script::sfScriptWalkToAsync(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ Location actorLocation;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ actorLocation.x = thread->pop();
+ actorLocation.y = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ actorLocation.z = actor->_location.z;
+
+ actor->_flags &= ~kFollower;
+
+ _vm->_actor->actorWalkTo(actorId, actorLocation);
+}
+
+// Script function #28 (0x1C)
+void Script::sfEnableZone(SCRIPTFUNC_PARAMS) {
+ uint16 objectId = thread->pop();
+ int16 flag = thread->pop();
+ HitZone *hitZone;
+
+ if (objectTypeId(objectId) == kGameObjectHitZone) {
+ hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(objectId));
+ } else {
+ hitZone = _vm->_scene->_actionMap->getHitZone(objectIdToIndex(objectId));
+ }
+
+ if (flag) {
+ hitZone->setFlag(kHitZoneEnabled);
+ } else {
+ hitZone->clearFlag(kHitZoneEnabled);
+ _vm->_actor->_protagonist->_lastZone = NULL;
+ }
+}
+
+// Script function #29 (0x1D)
+// Param1: actor id
+// Param2: current action
+void Script::sfSetActorState(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int currentAction;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ currentAction = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+
+ if ((currentAction >= kActionWalkToPoint) && (currentAction <= kActionWalkToPoint)) {
+ wakeUpActorThread(kWaitTypeWalk, actor);
+ }
+ actor->_currentAction = currentAction;
+ actor->_actorFlags &= ~kActorBackwards;
+}
+
+// Script function #30 (0x1E) nonblocking
+// Param1: actor id
+// Param2: actor pos x
+// Param3: actor pos y
+void Script::sfScriptMoveTo(SCRIPTFUNC_PARAMS) {
+ int16 objectId;
+ Location location;
+ ActorData *actor;
+ ObjectData *obj;
+
+ objectId = thread->pop();
+ location.x = thread->pop();
+ location.y = thread->pop();
+
+ if (_vm->_actor->validActorId(objectId)) {
+ actor = _vm->_actor->getActor(objectId);
+
+ actor->_location.x = location.x;
+ actor->_location.y = location.y;
+ } else {
+ if (_vm->_actor->validObjId(objectId)) {
+ obj = _vm->_actor->getObj(objectId);
+ obj->_location.x = location.x;
+ obj->_location.y = location.y;
+ }
+ }
+}
+
+// Script function #31 (0x21)
+// Param1: sceneNumber
+void Script::sfSceneEq(SCRIPTFUNC_PARAMS) {
+ int16 sceneNumber = thread->pop();
+
+ if (_vm->_scene->getSceneResourceId(sceneNumber) == _vm->_scene->currentSceneResourceId())
+ thread->_returnValue = 1;
+ else
+ thread->_returnValue = 0;
+}
+
+// Script function #32 (0x20)
+void Script::sfDropObject(SCRIPTFUNC_PARAMS) {
+ uint16 objectId;
+ uint16 spriteId;
+ int16 x;
+ int16 y;
+ ObjectData *obj;
+
+ objectId = thread->pop();
+ spriteId = thread->pop();
+ x = thread->pop();
+ y = thread->pop();
+
+ obj = _vm->_actor->getObj(objectId);
+
+ if (obj->_sceneNumber == ITE_SCENE_INV) {
+ _vm->_interface->removeFromInventory(objectId);
+ }
+
+ obj->_sceneNumber = _vm->_scene->currentSceneNumber();
+
+ if (_vm->getGameType() == GType_IHNM)
+ obj->_spriteListResourceId = spriteId;
+ else
+ obj->_spriteListResourceId = OBJ_SPRITE_BASE + spriteId;
+
+ obj->_location.x = x;
+ obj->_location.y = y;
+}
+
+// Script function #33 (0x21)
+void Script::sfFinishBgdAnim(SCRIPTFUNC_PARAMS) {
+ int16 animId = thread->pop();
+
+ _vm->_anim->finish(animId);
+
+ debug(1, "sfFinishBgdAnim(%d)", animId);
+}
+
+// Script function #34 (0x22)
+// Param1: actor id 1
+// Param2: actor id 2
+void Script::sfSwapActors(SCRIPTFUNC_PARAMS) {
+ int16 actorId1;
+ int16 actorId2;
+ ActorData *actor1;
+ ActorData *actor2;
+
+ actorId1 = thread->pop();
+ actorId2 = thread->pop();
+
+ actor1 = _vm->_actor->getActor(actorId1);
+ actor2 = _vm->_actor->getActor(actorId2);
+
+ SWAP(actor1->_location, actor2->_location);
+
+ if (actor1->_flags & kProtagonist) {
+ actor1->_flags &= ~kProtagonist;
+ actor2->_flags |= kProtagonist;
+ _vm->_actor->_protagonist = _vm->_actor->_centerActor = actor2;
+ } else if (actor2->_flags & kProtagonist) {
+ actor2->_flags &= ~kProtagonist;
+ actor1->_flags |= kProtagonist;
+ _vm->_actor->_protagonist = _vm->_actor->_centerActor = actor1;
+ }
+
+ // Here non-protagonist ID gets saved in variable
+ if (_vm->getGameType() == GType_IHNM)
+ warning("sfSwapActors: incomplete implementation");
+}
+
+// Script function #35 (0x23)
+// Param1: string rid
+// Param2: actorscount
+// Param3: actor id1
+///....
+// Param3: actor idN
+void Script::sfSimulSpeech(SCRIPTFUNC_PARAMS) {
+ int16 stringId;
+ int16 actorsCount;
+ int i;
+ uint16 actorsIds[ACTOR_SPEECH_ACTORS_MAX];
+ const char *string;
+ int16 sampleResourceId = -1;
+
+ stringId = thread->pop();
+ actorsCount = thread->pop();
+
+ if (actorsCount > ACTOR_SPEECH_ACTORS_MAX)
+ error("sfSimulSpeech actorsCount=0x%X exceed ACTOR_SPEECH_ACTORS_MAX", actorsCount);
+
+ for (i = 0; i < actorsCount; i++)
+ actorsIds[i] = thread->pop();
+
+ string = thread->_strings->getString(stringId);
+
+ if (thread->_voiceLUT->voices) {
+ if (_vm->getGameType() == GType_IHNM && stringId >= 338) {
+ sampleResourceId = -1;
+ } else {
+ sampleResourceId = thread->_voiceLUT->voices[stringId];
+ if (sampleResourceId <= 0 || sampleResourceId > 4000)
+ sampleResourceId = -1;
+ }
+ }
+
+ _vm->_actor->simulSpeech(string, actorsIds, actorsCount, 0, sampleResourceId);
+ thread->wait(kWaitTypeSpeech);
+}
+
+// Script function #36 (0x24) ?
+// Param1: actor id
+// Param2: actor x
+// Param3: actor y
+// Param4: actor walk flag
+void Script::sfScriptWalk(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ Location actorLocation;
+ ActorData *actor;
+ uint16 walkFlags;
+
+ actorId = thread->pop();
+ actorLocation.x = thread->pop();
+ actorLocation.y = thread->pop();
+ walkFlags = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ actorLocation.z = actor->_location.z;
+
+ _vm->_actor->realLocation(actorLocation, ID_NOTHING, walkFlags);
+
+ actor->_flags &= ~kFollower;
+
+ if (_vm->_actor->actorWalkTo(actorId, actorLocation) && !(walkFlags & kWalkAsync)) {
+ thread->waitWalk(actor);
+ }
+
+ if (walkFlags & kWalkBackPedal) {
+ actor->_actorFlags |= kActorBackwards;
+ }
+
+ actor->_actorFlags = (actor->_actorFlags & ~kActorFacingMask) | (walkFlags & kActorFacingMask);
+}
+
+// Script function #37 (0x25) nonblocking
+// Param1: actor id
+// Param2: flags telling how to cycle the frames
+// Param3: cycle frame number
+// Param4: cycle delay
+void Script::sfCycleFrames(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int16 flags;
+ int cycleFrameSequence;
+ int cycleDelay;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ flags = thread->pop();
+ cycleFrameSequence = thread->pop();
+ cycleDelay = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+
+ if (flags & kCyclePong) {
+ actor->_currentAction = kActionPongFrames;
+ } else {
+ actor->_currentAction = kActionCycleFrames;
+ }
+
+ actor->_actorFlags &= ~(kActorContinuous | kActorRandom | kActorBackwards);
+
+ if (!(flags & kCycleOnce)) {
+ actor->_actorFlags |= kActorContinuous;
+ }
+ if (flags & kCycleRandom) {
+ actor->_actorFlags |= kActorRandom;
+ }
+ if (flags & kCycleReverse) {
+ actor->_actorFlags |= kActorBackwards;
+ }
+
+ actor->_cycleFrameSequence = cycleFrameSequence;
+ actor->_cycleTimeCount = 0;
+ actor->_cycleDelay = cycleDelay;
+ actor->_actionCycle = 0;
+}
+
+// Script function #38 (0x26) nonblocking
+// Param1: actor id
+// Param2: frame type
+// Param3: frame offset
+void Script::sfSetFrame(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int frameType;
+ int frameOffset;
+ ActorData *actor;
+ ActorFrameRange *frameRange;
+
+ actorId = thread->pop();
+ frameType = thread->pop();
+ frameOffset = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+
+ frameRange = _vm->_actor->getActorFrameRange(actorId, frameType);
+
+ actor->_frameNumber = frameRange->frameIndex + frameOffset;
+
+ if (actor->_currentAction != kActionFall) {
+ actor->_currentAction = kActionFreeze;
+ }
+}
+
+// Script function #39 (0x27)
+// Sets the right-hand portrait
+void Script::sfSetPortrait(SCRIPTFUNC_PARAMS) {
+ int16 param = thread->pop();
+
+ _vm->_interface->setRightPortrait(param);
+}
+
+// Script function #40 (0x28)
+// Sets the left-hand portrait
+void Script::sfSetProtagPortrait(SCRIPTFUNC_PARAMS) {
+ int16 param = thread->pop();
+
+ _vm->_interface->setLeftPortrait(param);
+}
+
+// Script function #41 (0x29) nonblocking
+// Links the specified animations for playback
+
+// Param1: ?
+// Param2: total linked frame count
+// Param3: animation id link target
+// Param4: animation id link source
+void Script::sfChainBgdAnim(SCRIPTFUNC_PARAMS) {
+ int16 animId1 = thread->pop();
+ int16 animId = thread->pop();
+ int16 cycles = thread->pop();
+ int16 speed = thread->pop();
+
+ if (speed >= 0) {
+ _vm->_anim->setCycles(animId, cycles);
+ _vm->_anim->stop(animId);
+ _vm->_anim->setFrameTime(animId, ticksToMSec(speed));
+ }
+
+ _vm->_anim->link(animId1, animId);
+ debug(1, "sfChainBgdAnim(%d, %d, %d, %d)", animId1, animId, cycles, speed);
+}
+
+// Script function #42 (0x2A)
+// Param1: actor id
+// Param2: actor x
+// Param3: actor y
+// Param4: frame seq
+void Script::sfScriptSpecialWalk(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int16 walkFrameSequence;
+ Location actorLocation;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ actorLocation.x = thread->pop();
+ actorLocation.y = thread->pop();
+ walkFrameSequence = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ actorLocation.z = actor->_location.z;
+
+ _vm->_actor->actorWalkTo(actorId, actorLocation);
+
+ actor->_walkFrameSequence = walkFrameSequence;
+}
+
+// Script function #43 (0x2B) nonblocking
+// Param1: actor id
+// Param2: actor x
+// Param3: actor y
+// Param4: actor direction
+// Param5: actor action
+// Param6: actor frame number
+void Script::sfPlaceActor(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ Location actorLocation;
+ int actorDirection;
+ int frameType;
+ int frameOffset;
+ ActorData *actor;
+ ActorFrameRange *frameRange;
+
+ actorId = thread->pop();
+ actorLocation.x = thread->pop();
+ actorLocation.y = thread->pop();
+ actorDirection = thread->pop();
+ frameType = thread->pop();
+ frameOffset = thread->pop();
+
+ debug(1, "sfPlaceActor(id = 0x%x, x=%d, y=%d, dir=%d, frameType=%d, frameOffset=%d)", actorId, actorLocation.x,
+ actorLocation.y, actorDirection, frameType, frameOffset);
+
+ actor = _vm->_actor->getActor(actorId);
+ actor->_location.x = actorLocation.x;
+ actor->_location.y = actorLocation.y;
+ actor->_facingDirection = actor->_actionDirection = actorDirection;
+
+ if (!actor->_frames)
+ _vm->_actor->loadActorResources(actor); //? is not it already loaded ?
+
+ if (frameType >= 0) {
+ frameRange = _vm->_actor->getActorFrameRange(actorId, frameType);
+
+ if (frameRange->frameCount <= frameOffset) {
+ error("Wrong frameOffset 0x%X", frameOffset);
+ }
+
+ actor->_frameNumber = frameRange->frameIndex + frameOffset;
+ actor->_currentAction = kActionFreeze;
+ } else {
+ actor->_currentAction = kActionWait;
+ }
+
+ actor->_targetObject = ID_NOTHING;
+}
+
+// Script function #44 (0x2C) nonblocking
+// Checks to see if the user has interrupted a currently playing
+// game cinematic. Pushes a zero or positive value if the game
+// has not been interrupted.
+void Script::sfCheckUserInterrupt(SCRIPTFUNC_PARAMS) {
+ thread->_returnValue = (_skipSpeeches == true);
+}
+
+// Script function #45 (0x2D)
+// Param1: actor id
+// Param2: object id
+// Param3: actor x
+// Param4: actor y
+// Param5: actor walk flag
+void Script::sfScriptWalkRelative(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int16 objectId;
+ uint16 walkFlags;
+ Location actorLocation;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ objectId = thread->pop();
+ actorLocation.x = thread->pop();
+ actorLocation.y = thread->pop();
+ walkFlags = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ actorLocation.z = actor->_location.z;
+
+ _vm->_actor->realLocation(actorLocation, objectId, walkFlags);
+
+ actor->_flags &= ~kFollower;
+
+ if (_vm->_actor->actorWalkTo(actorId, actorLocation) && !(walkFlags & kWalkAsync)) {
+ thread->waitWalk(actor);
+ }
+
+ if (walkFlags & kWalkBackPedal) {
+ actor->_actorFlags |= kActorBackwards;
+ }
+
+ actor->_actorFlags = (actor->_actorFlags & ~kActorFacingMask) | (walkFlags & kActorFacingMask);
+}
+
+// Script function #46 (0x2E)
+// Param1: actor id
+// Param2: object id
+// Param3: actor x
+// Param4: actor y
+// Param5: actor walk flag
+void Script::sfScriptMoveRelative(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int16 objectId;
+ uint16 walkFlags;
+ Location actorLocation;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ objectId = thread->pop();
+ actorLocation.x = thread->pop();
+ actorLocation.y = thread->pop();
+ walkFlags = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ actorLocation.z = actor->_location.z;
+
+ _vm->_actor->realLocation(actorLocation, objectId, walkFlags);
+
+
+ actor->_location = actorLocation;
+ actor->_actorFlags = (actor->_actorFlags & ~kActorFacingMask) | (walkFlags & kActorFacingMask);
+}
+
+// Script function #47 (0x2F)
+void Script::sfSimulSpeech2(SCRIPTFUNC_PARAMS) {
+ int16 stringId;
+ int16 actorsCount;
+ int16 speechFlags;
+ int i;
+ uint16 actorsIds[ACTOR_SPEECH_ACTORS_MAX];
+ const char *string;
+ int16 sampleResourceId = -1;
+
+ stringId = thread->pop();
+ actorsCount = thread->pop();
+ speechFlags = thread->pop();
+
+ if (actorsCount > ACTOR_SPEECH_ACTORS_MAX)
+ error("sfSimulSpeech2 actorsCount=0x%X exceed ACTOR_SPEECH_ACTORS_MAX", actorsCount);
+
+ for (i = 0; i < actorsCount; i++)
+ actorsIds[i] = thread->pop();
+
+ string = thread->_strings->getString(stringId);
+
+ if (thread->_voiceLUT->voices) {
+ sampleResourceId = thread->_voiceLUT->voices[stringId];
+ if (sampleResourceId <= 0 || sampleResourceId > 4000)
+ sampleResourceId = -1;
+ }
+
+ _vm->_actor->simulSpeech(string, actorsIds, actorsCount, speechFlags, sampleResourceId);
+ thread->wait(kWaitTypeSpeech);
+}
+
+
+// Script function #48 (0x30)
+// Param1: string rid
+void Script::sfPlacard(SCRIPTFUNC_PARAMS) {
+ int stringId;
+ Surface *backBuffer = _vm->_gfx->getBackBuffer();
+ static PalEntry cur_pal[PAL_ENTRIES];
+ PalEntry *pal;
+ Event event;
+ Event *q_event;
+
+ if (_vm->getGameType() == GType_IHNM) {
+ warning("Psychic profile is not implemented");
+ return;
+ }
+
+ thread->wait(kWaitTypePlacard);
+
+ _vm->_interface->rememberMode();
+ _vm->_interface->setMode(kPanelPlacard);
+
+ stringId = thread->pop();
+
+ event.type = kEvTOneshot;
+ event.code = kCursorEvent;
+ event.op = kEventHide;
+
+ q_event = _vm->_events->queue(&event);
+
+ _vm->_gfx->getCurrentPal(cur_pal);
+
+ event.type = kEvTImmediate;
+ event.code = kPalEvent;
+ event.op = kEventPalToBlack;
+ event.time = 0;
+ event.duration = kNormalFadeDuration;
+ event.data = cur_pal;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ event.type = kEvTOneshot;
+ event.code = kInterfaceEvent;
+ event.op = kEventClearStatus;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ event.type = kEvTOneshot;
+ event.code = kGraphicsEvent;
+ event.op = kEventSetFlag;
+ event.param = RF_PLACARD;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ event.type = kEvTOneshot;
+ event.code = kGraphicsEvent;
+ event.op = kEventFillRect;
+ event.data = backBuffer;
+ event.param = 138;
+ event.param2 = 0;
+ event.param3 = _vm->_scene->getHeight();
+ event.param4 = 0;
+ event.param5 = _vm->getDisplayWidth();
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ // Put the text in the center of the viewport, assuming it will fit on
+ // one line. If we cannot make that assumption we'll need to extend
+ // the text drawing function so that it can center text around a point.
+ // It doesn't end up in exactly the same spot as the original did it,
+ // but it's close enough for now at least.
+
+ TextListEntry textEntry;
+
+ textEntry.knownColor = kKnownColorBrightWhite;
+ textEntry.effectKnownColor = kKnownColorBlack;
+ textEntry.point.x = _vm->getDisplayWidth() / 2;
+ textEntry.point.y = (_vm->_scene->getHeight() - _vm->_font->getHeight(kKnownFontMedium)) / 2;
+ textEntry.font = kKnownFontMedium;
+ textEntry.flags = (FontEffectFlags)(kFontOutline | kFontCentered);
+ textEntry.text = thread->_strings->getString(stringId);
+
+ _placardTextEntry = _vm->_scene->_textList.addEntry(textEntry);
+
+ event.type = kEvTOneshot;
+ event.code = kTextEvent;
+ event.op = kEventDisplay;
+ event.data = _placardTextEntry;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ _vm->_scene->getBGPal(pal);
+
+ event.type = kEvTImmediate;
+ event.code = kPalEvent;
+ event.op = kEventBlackToPal;
+ event.time = 0;
+ event.duration = kNormalFadeDuration;
+ event.data = pal;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ event.type = kEvTOneshot;
+ event.code = kScriptEvent;
+ event.op = kEventThreadWake;
+ event.param = kWaitTypePlacard;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+}
+
+// Script function #49 (0x31)
+void Script::sfPlacardOff(SCRIPTFUNC_PARAMS) {
+ static PalEntry cur_pal[PAL_ENTRIES];
+ PalEntry *pal;
+ Event event;
+ Event *q_event;
+
+ thread->wait(kWaitTypePlacard);
+
+ _vm->_interface->restoreMode();
+
+ _vm->_gfx->getCurrentPal(cur_pal);
+
+ event.type = kEvTImmediate;
+ event.code = kPalEvent;
+ event.op = kEventPalToBlack;
+ event.time = 0;
+ event.duration = kNormalFadeDuration;
+ event.data = cur_pal;
+
+ q_event = _vm->_events->queue(&event);
+
+ event.type = kEvTOneshot;
+ event.code = kGraphicsEvent;
+ event.op = kEventClearFlag;
+ event.param = RF_PLACARD;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ event.type = kEvTOneshot;
+ event.code = kTextEvent;
+ event.op = kEventRemove;
+ event.data = _placardTextEntry;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ _vm->_scene->getBGPal(pal);
+
+ event.type = kEvTImmediate;
+ event.code = kPalEvent;
+ event.op = kEventBlackToPal;
+ event.time = 0;
+ event.duration = kNormalFadeDuration;
+ event.data = pal;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ event.type = kEvTOneshot;
+ event.code = kCursorEvent;
+ event.op = kEventShow;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+ event.type = kEvTOneshot;
+ event.code = kScriptEvent;
+ event.op = kEventThreadWake;
+ event.param = kWaitTypePlacard;
+
+ q_event = _vm->_events->chain(q_event, &event);
+
+}
+
+void Script::sfPsychicProfile(SCRIPTFUNC_PARAMS) {
+ SF_stub("sfPsychicProfile", thread, nArgs);
+}
+
+void Script::sfPsychicProfileOff(SCRIPTFUNC_PARAMS) {
+ SF_stub("sfPsychicProfileOff", thread, nArgs);
+}
+
+// Script function #50 (0x32)
+void Script::sfSetProtagState(SCRIPTFUNC_PARAMS) {
+ int protagState = thread->pop();
+
+ _vm->_actor->setProtagState(protagState);
+}
+
+// Script function #51 (0x33)
+void Script::sfResumeBgdAnim(SCRIPTFUNC_PARAMS) {
+ int16 animId = thread->pop();
+ int16 cycles = thread->pop();
+
+ _vm->_anim->resume(animId, cycles);
+ debug(1, "sfResumeBgdAnimSpeed(%d, %d)", animId, cycles);
+
+}
+
+// Script function #52 (0x34)
+// Param1: actor id
+// Param2: x
+// Param3: y
+// Param4: unknown
+// Param5: actionCycle
+// Param6: flags
+void Script::sfThrowActor(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ ActorData *actor;
+ int16 flags;
+ int32 actionCycle;
+ Location location;
+
+ actorId = thread->pop();
+ location.x = thread->pop();
+ location.y = thread->pop();
+ thread->pop();
+ actionCycle = thread->pop();
+ flags = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ location.z = actor->_location.z;
+ actor->_currentAction = kActionFall;
+ actor->_actionCycle = actionCycle;
+ actor->_fallAcceleration = -20;
+ actor->_fallVelocity = - (actor->_fallAcceleration * actor->_actionCycle) / 2;
+ actor->_fallPosition = actor->_location.z << 4;
+
+ actor->_finalTarget = location;
+ actor->_actionCycle--;
+ if (!(flags & kWalkAsync)) {
+ thread->waitWalk(actor);
+ }
+}
+
+// Script function #53 (0x35)
+// Param1: actor id
+// Param2: target object
+void Script::sfWaitWalk(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ actor = _vm->_actor->getActor(actorId);
+
+ if ((actor->_currentAction == kActionWalkToPoint) ||
+ (actor->_currentAction == kActionWalkToLink) ||
+ (actor->_currentAction == kActionFall)) {
+ thread->waitWalk(actor);
+ }
+}
+
+// Script function #54 (0x36)
+void Script::sfScriptSceneID(SCRIPTFUNC_PARAMS) {
+ thread->_returnValue = _vm->_scene->currentSceneNumber();
+}
+
+// Script function #55 (0x37)
+// Param1: actor id
+// Param2: scene number
+void Script::sfChangeActorScene(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int32 sceneNumber;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ sceneNumber = thread->pop();
+ actor = _vm->_actor->getActor(actorId);
+ actor->_sceneNumber = sceneNumber;
+}
+
+// Script function #56 (0x38)
+// Param1: actor id
+// Param2: z
+// Param3: frame seq
+// Param4: flags
+void Script::sfScriptClimb(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ int16 z;
+ ActorData *actor;
+ uint16 flags;
+ int cycleFrameSequence;
+
+ actorId = thread->pop();
+ z = thread->pop();
+ cycleFrameSequence = thread->pop();
+ flags = thread->pop();
+
+ actor = _vm->_actor->getActor(actorId);
+ actor->_finalTarget.z = z;
+ actor->_flags &= ~kFollower;
+ actor->_actionCycle = 1;
+ actor->_cycleFrameSequence = cycleFrameSequence;
+ actor->_currentAction = kActionClimb;
+ if (!(flags & kWalkAsync)) {
+ thread->waitWalk(actor);
+ }
+}
+
+// Script function #57 (0x39)
+// Param1: door #
+// Param2: door state
+void Script::sfSetDoorState(SCRIPTFUNC_PARAMS) {
+ int16 doorNumber;
+ int16 doorState;
+ doorNumber = thread->pop();
+ doorState = thread->pop();
+
+ if (_vm->_scene->getFlags() & kSceneFlagISO) {
+ _vm->_isoMap->setTileDoorState(doorNumber, doorState);
+ } else {
+ _vm->_scene->setDoorState(doorNumber, doorState);
+ }
+}
+
+// Script function #58 (0x3A)
+// Param1: actor id
+// Param2: z
+void Script::sfSetActorZ(SCRIPTFUNC_PARAMS) {
+ int16 objectId;
+ ActorData *actor;
+ ObjectData *obj;
+ int16 z;
+
+ objectId = thread->pop();
+ z = thread->pop();
+
+
+ if (_vm->_actor->validActorId(objectId)) {
+ actor = _vm->_actor->getActor(objectId);
+ actor->_location.z = z;
+ } else {
+ if (_vm->_actor->validObjId(objectId)) {
+ obj = _vm->_actor->getObj(objectId);
+ obj->_location.z = z;
+ }
+ }
+}
+
+// Script function #59 (0x3B)
+// Param1: stringId
+// Param2: flags
+// Param3: color
+// Param4: x
+// Param5: y
+void Script::sfScriptText(SCRIPTFUNC_PARAMS) {
+ int16 stringId;
+ int16 flags;
+ Rect rect;
+ int color;
+ Point point;
+ int width;
+ const char*text;
+ stringId = thread->pop();
+ flags = thread->pop();
+ color = thread->pop();
+ point.x = thread->pop();
+ point.y = thread->pop();
+
+ text = thread->_strings->getString(stringId);
+
+ width = _vm->_font->getStringWidth(kKnownFontScript, text, 0, kFontOutline);
+ rect.top = point.y - 6;
+ rect.setHeight(12);
+ rect.left = point.x - width / 2;
+ rect.setWidth(width);
+
+ _vm->_actor->setSpeechColor(color, _vm->KnownColor2ColorId(kKnownColorBlack));
+ _vm->_actor->nonActorSpeech(rect, &text, 1, -1, flags);
+}
+
+// Script function #60 (0x3C)
+// Param1: actor id
+void Script::sfGetActorX(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ actor = _vm->_actor->getActor(actorId);
+
+ thread->_returnValue = actor->_location.x >> 2;
+}
+
+// Script function #61 (0x3D)
+// Param1: actor id
+void Script::sfGetActorY(SCRIPTFUNC_PARAMS) {
+ int16 actorId;
+ ActorData *actor;
+
+ actorId = thread->pop();
+ actor = _vm->_actor->getActor(actorId);
+
+ thread->_returnValue = actor->_location.y >> 2;
+}
+
+// Script function #62 (0x3E)
+void Script::sfEraseDelta(SCRIPTFUNC_PARAMS) {
+ Surface *backGroundSurface;
+ BGInfo backGroundInfo;
+
+ backGroundSurface = _vm->_render->getBackGroundSurface();
+ _vm->_scene->getBGInfo(backGroundInfo);
+
+ backGroundSurface->blit(backGroundInfo.bounds, backGroundInfo.buffer);
+}
+
+// Script function #63 (0x3F)
+void Script::sfPlayMusic(SCRIPTFUNC_PARAMS) {
+ if (_vm->getGameType() == GType_ITE) {
+ int16 param = thread->pop() + 9;
+
+ if (param >= 9 && param <= 34) {
+ _vm->_music->setVolume(-1, 1);
+ _vm->_music->play(param);
+ } else {
+ _vm->_music->stop();
+ }
+ } else {
+ int16 param1 = thread->pop();
+ int16 param2 = thread->pop();
+
+ if (param1 < 0) {
+ _vm->_music->stop();
+ return;
+ }
+
+ if (param1 >= _vm->_music->_songTableLen) {
+ warning("sfPlayMusic: Wrong song number (%d > %d)", param1, _vm->_music->_songTableLen - 1);
+ } else {
+ _vm->_music->setVolume(-1, 1);
+ _vm->_music->play(_vm->_music->_songTable[param1], param2 ? MUSIC_LOOP : MUSIC_NORMAL);
+ }
+ }
+}
+
+// Script function #64 (0x40)
+void Script::sfPickClimbOutPos(SCRIPTFUNC_PARAMS) {
+ int16 u, v, t;
+ ActorData *protagonist = _vm->_actor->_protagonist;
+ while (true) {
+
+ u = (_vm->_rnd.getRandomNumber(63) & 63) + 40;
+ v = (_vm->_rnd.getRandomNumber(63) & 63) + 40;
+ t = _vm->_isoMap->getTileIndex(u, v, 6);
+ if (t == 65) {
+ protagonist->_location.u() = (u << 4) + 4;
+ protagonist->_location.v() = (v << 4) + 4;
+ protagonist->_location.z = 48;
+ break;
+ }
+
+ }
+}
+
+// Script function #65 (0x41)
+void Script::sfTossRif(SCRIPTFUNC_PARAMS) {
+ int16 uc , vc;
+ uint16 direction;
+ ActorData *protagonist = _vm->_actor->_protagonist;
+
+ uc = protagonist->_location.u() >> 4;
+ vc = protagonist->_location.v() >> 4;
+ if (_vm->_isoMap->findNearestChasm(uc, vc, direction)) {
+ uc <<= 4;
+ vc <<= 4;
+ protagonist->_facingDirection = direction;
+
+ protagonist->_finalTarget.u() = uc;
+ protagonist->_finalTarget.v() = vc;
+ protagonist->_finalTarget.z = -40;
+ protagonist->_currentAction = kActionFall;
+ protagonist->_actionCycle = 24;
+ protagonist->_fallAcceleration = - 20;
+ protagonist->_fallVelocity = - (protagonist->_fallAcceleration * 16) / 2 - (44 / 12);
+ protagonist->_fallPosition = protagonist->_location.z << 4;
+ protagonist->_actionCycle--;
+ }
+}
+
+// Script function #66 (0x42)
+void Script::sfShowControls(SCRIPTFUNC_PARAMS) {
+ // It has zero implementation in Win rerelase, and in DOS
+ // release it deals with video ports.
+}
+
+// Script function #67 (0x43)
+void Script::sfShowMap(SCRIPTFUNC_PARAMS) {
+ _vm->_interface->setMode(kPanelMap);
+}
+
+// Script function #68 (0x44)
+void Script::sfPuzzleWon(SCRIPTFUNC_PARAMS) {
+ thread->_returnValue = _vm->_puzzle->isSolved();
+}
+
+// Script function #69 (0x45)
+void Script::sfEnableEscape(SCRIPTFUNC_PARAMS) {
+ if (thread->pop())
+ _abortEnabled = true;
+ else {
+ _skipSpeeches = false;
+ _abortEnabled = false;
+ }
+}
+
+// Script function #70 (0x46)
+void Script::sfPlaySound(SCRIPTFUNC_PARAMS) {
+ int16 param = thread->pop();
+ int res;
+
+ if (param >= 0 && param < _vm->_sndRes->_fxTableLen) {
+ res = _vm->_sndRes->_fxTable[param].res;
+ if (_vm->getFeatures() & GF_CD_FX)
+ res -= 14;
+ _vm->_sndRes->playSound(res, _vm->_sndRes->_fxTable[param].vol, false);
+ } else {
+ _vm->_sound->stopSound();
+ }
+}
+
+// Script function #71 (0x47)
+void Script::sfPlayLoopedSound(SCRIPTFUNC_PARAMS) {
+ int16 param = thread->pop();
+ int res;
+
+ if (param >= 0 && param < _vm->_sndRes->_fxTableLen) {
+ res = _vm->_sndRes->_fxTable[param].res;
+ if (_vm->getFeatures() & GF_CD_FX)
+ res -= 14;
+
+ _vm->_sndRes->playSound(res, _vm->_sndRes->_fxTable[param].vol, true);
+ } else {
+ _vm->_sound->stopSound();
+ }
+}
+
+// Script function #72 (0x48)
+void Script::sfGetDeltaFrame(SCRIPTFUNC_PARAMS) {
+ uint16 animId = (uint16)thread->pop();
+
+ thread->_returnValue = _vm->_anim->getCurrentFrame(animId);
+}
+
+// Script function #73 (0x49)
+void Script::sfShowProtect(SCRIPTFUNC_PARAMS) {
+ if (_vm->_copyProtection) {
+ thread->wait(kWaitTypeRequest);
+
+ _vm->_interface->setMode(kPanelProtect);
+ }
+}
+
+// Script function #74 (0x4A)
+void Script::sfProtectResult(SCRIPTFUNC_PARAMS) {
+ if (_vm->_copyProtection) {
+ thread->_returnValue = _vm->_interface->getProtectHash();
+ } else {
+ int protectHash;
+
+ //cheating
+ protectHash = thread->pop();
+ thread->push(protectHash);
+ thread->_returnValue = protectHash;
+ }
+}
+
+// Script function #75 (0x4b)
+void Script::sfRand(SCRIPTFUNC_PARAMS) {
+ int16 param;
+
+ param = thread->pop();
+ thread->_returnValue = _vm->_rnd.getRandomNumber(param - 1);
+}
+
+// Script function #76 (0x4c)
+void Script::sfFadeMusic(SCRIPTFUNC_PARAMS) {
+ _vm->_music->setVolume(0, 1000);
+}
+
+// Script function #77 (0x4d)
+void Script::sfPlayVoice(SCRIPTFUNC_PARAMS) {
+ int16 param = thread->pop();
+
+ warning("sfPlayVoice(%d)", param);
+ if (param > 0) {
+ _vm->_sndRes->playVoice(param + 3712);
+ } else {
+ _vm->_sound->stopSound();
+ }
+}
+
+void Script::finishDialog(int replyID, int flags, int bitOffset) {
+ byte *addr;
+
+ if (_conversingThread) {
+ _vm->_interface->setMode(kPanelNull);
+
+ _conversingThread->_flags &= ~kTFlagWaiting;
+
+ _conversingThread->push(replyID);
+
+ if (flags & kReplyOnce) {
+ addr = _conversingThread->_staticBase + (bitOffset >> 3);
+ *addr |= (1 << (bitOffset & 7));
+ }
+ }
+
+ _conversingThread = NULL;
+ wakeUpThreads(kWaitTypeDialogBegin);
+}
+
+void Script::sfSetChapterPoints(SCRIPTFUNC_PARAMS) {
+ int16 ethics = thread->pop();
+ int16 barometer = thread->pop();
+ int chapter = _vm->_scene->currentChapterNumber();
+
+ _vm->_ethicsPoints[chapter] = ethics;
+ _vm->_spiritualBarometer = ethics * 256 / barometer;
+}
+
+void Script::sfSetPortraitBgColor(SCRIPTFUNC_PARAMS) {
+ int16 red = thread->pop();
+ int16 green = thread->pop();
+ int16 blue = thread->pop();
+
+ _vm->_interface->setPortraitBgColor(red, green, blue);
+}
+
+void Script::sfScriptStartCutAway(SCRIPTFUNC_PARAMS) {
+ int16 cut;
+ int16 fade;
+
+ cut = thread->pop();
+ thread->pop(); // Not used
+ fade = thread->pop();
+
+ _vm->_anim->playCutaway(cut, fade != 0);
+}
+
+void Script::sfReturnFromCutAway(SCRIPTFUNC_PARAMS) {
+ _vm->_anim->returnFromCutaway();
+}
+
+void Script::sfEndCutAway(SCRIPTFUNC_PARAMS) {
+ _vm->_anim->endCutaway();
+}
+
+void Script::sfGetMouseClicks(SCRIPTFUNC_PARAMS) {
+ SF_stub("sfGetMouseClicks", thread, nArgs);
+}
+
+void Script::sfResetMouseClicks(SCRIPTFUNC_PARAMS) {
+ SF_stub("sfResetMouseClicks", thread, nArgs);
+}
+
+void Script::sfWaitFrames(SCRIPTFUNC_PARAMS) {
+ SF_stub("sfWaitFrames", thread, nArgs);
+}
+
+void Script::sfScriptFade(SCRIPTFUNC_PARAMS) {
+ SF_stub("sfScriptFade", thread, nArgs);
+}
+
+void Script::sfScriptStartVideo(SCRIPTFUNC_PARAMS) {
+ SF_stub("sfScriptStartVideo", thread, nArgs);
+}
+
+void Script::sfScriptReturnFromVideo(SCRIPTFUNC_PARAMS) {
+ SF_stub("sfScriptReturnFromVideo", thread, nArgs);
+}
+
+void Script::sfScriptEndVideo(SCRIPTFUNC_PARAMS) {
+ SF_stub("sfScriptEndVideo", thread, nArgs);
+}
+
+void Script::sf87(SCRIPTFUNC_PARAMS) {
+ SF_stub("sf87", thread, nArgs);
+}
+
+void Script::sf88(SCRIPTFUNC_PARAMS) {
+ SF_stub("sf88", thread, nArgs);
+}
+
+void Script::sf89(SCRIPTFUNC_PARAMS) {
+ SF_stub("sf89", thread, nArgs);
+}
+
+void Script::sfVstopFX(SCRIPTFUNC_PARAMS) {
+ _vm->_sound->stopSound();
+}
+
+void Script::sfVstopLoopedFX(SCRIPTFUNC_PARAMS) {
+ _vm->_sound->stopSound();
+}
+
+void Script::sfDemoIsInteractive(SCRIPTFUNC_PARAMS) {
+ thread->_returnValue = 0;
+}
+
+void Script::sfVsetTrack(SCRIPTFUNC_PARAMS) {
+ int16 chapter = thread->pop();
+ int16 sceneNumber = thread->pop();
+ int16 actorsEntrance = thread->pop();
+
+ debug(2, "sfVsetTrrack(%d, %d, %d)", chapter, sceneNumber, actorsEntrance);
+
+ _vm->_scene->changeScene(sceneNumber, actorsEntrance, kTransitionFade, chapter);
+}
+
+void Script::sfGetPoints(SCRIPTFUNC_PARAMS) {
+ int16 index = thread->pop();
+
+ if (index >= 0 && index < ARRAYSIZE(_vm->_ethicsPoints))
+ thread->_returnValue = _vm->_ethicsPoints[index];
+ else
+ thread->_returnValue = 0;
+}
+
+void Script::sfSetGlobalFlag(SCRIPTFUNC_PARAMS) {
+ int16 flag = thread->pop();
+
+ if (flag >= 0 && flag < 32)
+ _vm->_globalFlags |= (1 << flag);
+}
+
+void Script::sfClearGlobalFlag(SCRIPTFUNC_PARAMS) {
+ int16 flag = thread->pop();
+
+ if (flag >= 0 && flag < 32)
+ _vm->_globalFlags &= ~(1 << flag);
+}
+
+void Script::sfTestGlobalFlag(SCRIPTFUNC_PARAMS) {
+ int16 flag = thread->pop();
+
+ if (flag >= 0 && flag < 32 && _vm->_globalFlags & (1 << flag))
+ thread->_returnValue = 1;
+ else
+ thread->_returnValue = 0;
+}
+
+void Script::sfSetPoints(SCRIPTFUNC_PARAMS) {
+ int16 index = thread->pop();
+ int16 points = thread->pop();
+
+ if (index >= 0 && index < ARRAYSIZE(_vm->_ethicsPoints))
+ _vm->_ethicsPoints[index] = points;
+}
+
+void Script::sfSetSpeechBox(SCRIPTFUNC_PARAMS) {
+ int16 param1 = thread->pop();
+ int16 param2 = thread->pop();
+ int16 param3 = thread->pop();
+ int16 param4 = thread->pop();
+
+ _vm->_actor->_speechBoxScript.left = param1;
+ _vm->_actor->_speechBoxScript.top = param2;
+ _vm->_actor->_speechBoxScript.setWidth(param3);
+ _vm->_actor->_speechBoxScript.setHeight(param4);
+}
+
+void Script::sfDebugShowData(SCRIPTFUNC_PARAMS) {
+ int16 param = thread->pop();
+ char buf[50];
+
+ snprintf(buf, 50, "Reached breakpoint %d", param);
+
+ _vm->_interface->setStatusText(buf);
+}
+
+void Script::sfWaitFramesEsc(SCRIPTFUNC_PARAMS) {
+ thread->_returnValue = _vm->_framesEsc;
+}
+
+void Script::sf103(SCRIPTFUNC_PARAMS) {
+ SF_stub("sf103", thread, nArgs);
+}
+
+void Script::sfDisableAbortSpeeches(SCRIPTFUNC_PARAMS) {
+ int value = thread->pop();
+
+ _vm->_interface->disableAbortSpeeches(value != 0);
+}
+
+void Script::sfNull(SCRIPTFUNC_PARAMS) {
+ for (int i = 0; i < nArgs; i++)
+ thread->pop();
+}
+
+void Script::SF_stub(const char *name, ScriptThread *thread, int nArgs) {
+ char buf[256], buf1[100];
+
+ snprintf(buf, 256, "STUB: %s(", name);
+
+ for (int i = 0; i < nArgs; i++) {
+ snprintf(buf1, 100, "%d", thread->pop());
+ strncat(buf, buf1, 256);
+ if (i + 1 < nArgs)
+ strncat(buf, ", ", 256);
+ }
+
+ debug(0, "%s)", buf);
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp
new file mode 100644
index 0000000000..d3679599fd
--- /dev/null
+++ b/engines/saga/sndres.cpp
@@ -0,0 +1,297 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Sound resource management class
+
+#include "saga/saga.h"
+
+#include "saga/itedata.h"
+#include "saga/resnames.h"
+#include "saga/rscfile.h"
+#include "saga/sndres.h"
+#include "saga/sound.h"
+#include "saga/stream.h"
+
+#include "common/file.h"
+
+#include "sound/voc.h"
+#include "sound/wave.h"
+#include "sound/adpcm.h"
+#include "sound/audiostream.h"
+
+namespace Saga {
+
+SndRes::SndRes(SagaEngine *vm) : _vm(vm) {
+ /* Load sound module resource file contexts */
+ _sfxContext = _vm->_resource->getContext(GAME_SOUNDFILE);
+ if (_sfxContext == NULL) {
+ error("SndRes::SndRes resource context not found");
+ }
+
+ _voiceSerial = -1;
+
+ setVoiceBank(0);
+
+ if (_vm->getGameType() == GType_ITE) {
+ _fxTable = ITE_SfxTable;
+ _fxTableLen = ITE_SFXCOUNT;
+ } else {
+ ResourceContext *resourceContext;
+
+ resourceContext = _vm->_resource->getContext(GAME_SOUNDFILE);
+ if (resourceContext == NULL) {
+ error("Resource::loadGlobalResources() resource context not found");
+ }
+
+ byte *resourcePointer;
+ size_t resourceLength;
+
+ _vm->_resource->loadResource(resourceContext, RID_IHNM_SFX_LUT,
+ resourcePointer, resourceLength);
+
+ if (resourceLength == 0) {
+ error("Sndres::SndRes can't read SfxIDs table");
+ }
+
+ _fxTableIDsLen = resourceLength / 2;
+ _fxTableIDs = (int16 *)malloc(_fxTableIDsLen * sizeof(int16));
+
+ MemoryReadStream metaS(resourcePointer, resourceLength);
+ for (int i = 0; i < _fxTableIDsLen; i++)
+ _fxTableIDs[i] = metaS.readSint16LE();
+
+ free(resourcePointer);
+
+ _fxTable = 0;
+ _fxTableLen = 0;
+ }
+}
+
+SndRes::~SndRes() {
+ if (_vm->getGameType() == GType_IHNM) {
+ free(_fxTable);
+ free(_fxTableIDs);
+ }
+}
+
+void SndRes::setVoiceBank(int serial)
+{
+ if (_voiceSerial == serial) return;
+
+ _voiceSerial = serial;
+
+ _voiceContext = _vm->_resource->getContext(GAME_VOICEFILE, _voiceSerial);
+ if (_voiceContext == NULL) {
+ error("SndRes::SndRes resource context not found");
+ }
+
+
+}
+
+void SndRes::playSound(uint32 resourceId, int volume, bool loop) {
+ SoundBuffer buffer;
+
+ debug(4, "SndRes::playSound %i", resourceId);
+
+ if (!load(_sfxContext, resourceId, buffer, false)) {
+ warning("Failed to load sound");
+ return;
+ }
+
+ _vm->_sound->playSound(buffer, volume, loop);
+}
+
+void SndRes::playVoice(uint32 resourceId) {
+ SoundBuffer buffer;
+
+ debug(4, "SndRes::playVoice %i", resourceId);
+
+ if (!load(_voiceContext, resourceId, buffer, false)) {
+ warning("Failed to load voice");
+ return;
+ }
+
+ _vm->_sound->playVoice(buffer);
+}
+
+bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buffer, bool onlyHeader) {
+ byte *soundResource;
+ AudioStream *voxStream;
+ size_t soundResourceLength;
+ bool result = false;
+ GameSoundTypes resourceType;
+ byte *data;
+ int rate;
+ int size;
+ byte flags;
+ size_t voxSize;
+ const GameSoundInfo *soundInfo;
+
+ if (resourceId == (uint32)-1) {
+ return false;
+ }
+
+
+ _vm->_resource->loadResource(context, resourceId, soundResource, soundResourceLength);
+
+ if ((context->fileType & GAME_VOICEFILE) != 0) {
+ soundInfo = _vm->getVoiceInfo();
+ } else {
+ soundInfo = _vm->getSfxInfo();
+ }
+
+ context->table[resourceId].fillSoundPatch(soundInfo);
+
+ MemoryReadStream readS(soundResource, soundResourceLength);
+
+ resourceType = soundInfo->resourceType;
+ buffer.isBigEndian = soundInfo->isBigEndian;
+
+ if (soundResourceLength >= 8) {
+ if (!memcmp(soundResource, "Creative", 8)) {
+ resourceType = kSoundVOC;
+ } else if (!memcmp(soundResource, "RIFF", 4) != 0) {
+ resourceType = kSoundWAV;
+ }
+ }
+
+
+ switch (resourceType) {
+ case kSoundPCM:
+ buffer.frequency = soundInfo->frequency;
+ buffer.isSigned = soundInfo->isSigned;
+ buffer.sampleBits = soundInfo->sampleBits;
+ buffer.size = soundResourceLength;
+ buffer.stereo = soundInfo->stereo;
+ if (onlyHeader) {
+ buffer.buffer = NULL;
+ free(soundResource);
+ } else {
+ buffer.buffer = soundResource;
+ }
+ result = true;
+ break;
+ case kSoundMacPCM:
+ buffer.frequency = soundInfo->frequency;
+ buffer.isSigned = soundInfo->isSigned;
+ buffer.sampleBits = soundInfo->sampleBits;
+ buffer.size = soundResourceLength - 36;
+ buffer.stereo = soundInfo->stereo;
+ if (onlyHeader) {
+ buffer.buffer = NULL;
+ } else {
+ buffer.buffer = (byte *)malloc(buffer.size);
+ memcpy(buffer.buffer, soundResource + 36, buffer.size);
+ }
+ free(soundResource);
+ result = true;
+ break;
+ case kSoundVOX:
+ buffer.frequency = soundInfo->frequency;
+ buffer.isSigned = soundInfo->isSigned;
+ buffer.sampleBits = soundInfo->sampleBits;
+ buffer.stereo = soundInfo->stereo;
+ buffer.size = soundResourceLength * 4;
+ if (onlyHeader) {
+ buffer.buffer = NULL;
+ free(soundResource);
+ } else {
+ voxStream = new ADPCMInputStream(&readS, soundResourceLength, kADPCMOki);
+ buffer.buffer = (byte *)malloc(buffer.size);
+ voxSize = voxStream->readBuffer((int16*)buffer.buffer, soundResourceLength * 2);
+ if (voxSize != soundResourceLength * 2) {
+ error("SndRes::load() wrong VOX output size");
+ }
+ delete voxStream;
+ }
+ result = true;
+ break;
+ case kSoundVOC:
+ data = loadVOCFromStream(readS, size, rate);
+ if (data) {
+ buffer.frequency = rate;
+ buffer.sampleBits = 8;
+ buffer.stereo = false;
+ buffer.isSigned = false;
+ buffer.size = size;
+ if (onlyHeader) {
+ buffer.buffer = NULL;
+ free(data);
+ } else {
+ buffer.buffer = data;
+ }
+ result = true;
+ }
+ free(soundResource);
+ break;
+ case kSoundWAV:
+ if (loadWAVFromStream(readS, size, rate, flags)) {
+ buffer.frequency = rate;
+ buffer.sampleBits = 16;
+ buffer.stereo = ((flags & Audio::Mixer::FLAG_STEREO) != 0);
+ buffer.isSigned = true;
+ buffer.size = size;
+ if (onlyHeader) {
+ buffer.buffer = NULL;
+ } else {
+ buffer.buffer = (byte *)malloc(size);
+ readS.read(buffer.buffer, size);
+ }
+ result = true;
+ }
+ free(soundResource);
+ break;
+ default:
+ error("SndRes::load Unknown sound type");
+ }
+
+ // In ITE CD De some voices are absent and contain just 5 bytes header
+ // Round it to even number so soundmanager will not crash.
+ // See bug #1256701
+ buffer.size &= ~(0x1);
+
+ return result;
+}
+
+int SndRes::getVoiceLength(uint32 resourceId) {
+ double msDouble;
+ SoundBuffer buffer;
+
+ if (!load(_voiceContext, resourceId, buffer, true)) {
+ return -1;
+ }
+
+ msDouble = (double)buffer.size;
+ if (buffer.sampleBits == 16) {
+ msDouble /= 2.0;
+ }
+ if (buffer.stereo) {
+ msDouble /= 2.0;
+ }
+
+ msDouble = msDouble / buffer.frequency * 1000.0;
+ return (int)msDouble;
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/sndres.h b/engines/saga/sndres.h
new file mode 100644
index 0000000000..638fb80849
--- /dev/null
+++ b/engines/saga/sndres.h
@@ -0,0 +1,68 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Sound resource class header
+
+#ifndef SAGA_SNDRES_H_
+#define SAGA_SNDRES_H_
+
+#include "saga/itedata.h"
+#include "saga/sound.h"
+
+namespace Saga {
+
+class SndRes {
+public:
+
+ SndRes(SagaEngine *vm);
+ ~SndRes();
+
+ int loadSound(uint32 resourceId);
+ void playSound(uint32 resourceId, int volume, bool loop);
+ void playVoice(uint32 resourceId);
+ int getVoiceLength(uint32 resourceId);
+ void setVoiceBank(int serial);
+
+ FxTable *_fxTable;
+ int _fxTableLen;
+
+ int16 *_fxTableIDs;
+ int _fxTableIDsLen;
+
+ private:
+ bool load(ResourceContext *context, uint32 resourceId, SoundBuffer &buffer, bool onlyHeader);
+ bool loadVocSound(byte *soundResource, size_t soundResourceLength, SoundBuffer &buffer);
+ bool loadWavSound(byte *soundResource, size_t soundResourceLength, SoundBuffer &buffer);
+
+ ResourceContext *_sfxContext;
+ ResourceContext *_voiceContext;
+
+ int _voiceSerial; // voice bank number
+
+ SagaEngine *_vm;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/sound.cpp b/engines/saga/sound.cpp
new file mode 100644
index 0000000000..37f9cf174f
--- /dev/null
+++ b/engines/saga/sound.cpp
@@ -0,0 +1,149 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "saga/saga.h"
+
+#include "saga/sound.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+#include "sound/adpcm.h"
+
+namespace Saga {
+
+Sound::Sound(SagaEngine *vm, Audio::Mixer *mixer, int volume) :
+ _vm(vm), _mixer(mixer), _voxStream(0) {
+
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ _handles[i].type = kFreeHandle;
+
+ setVolume(volume == 10 ? 255 : volume * 25);
+}
+
+Sound::~Sound() {
+ delete _voxStream;
+}
+
+SndHandle *Sound::getHandle() {
+ for (int i = 0; i < SOUND_HANDLES; i++) {
+ if (_handles[i].type == kFreeHandle)
+ return &_handles[i];
+
+ if (!_mixer->isSoundHandleActive(_handles[i].handle)) {
+ _handles[i].type = kFreeHandle;
+ return &_handles[i];
+ }
+ }
+
+ error("Sound::getHandle(): Too many sound handles");
+
+ return NULL;
+}
+
+void Sound::playSoundBuffer(Audio::SoundHandle *handle, SoundBuffer &buffer, int volume, bool loop) {
+ byte flags;
+
+ flags = Audio::Mixer::FLAG_AUTOFREE;
+
+ if (loop)
+ flags |= Audio::Mixer::FLAG_LOOP;
+
+ if (buffer.sampleBits == 16) {
+ flags |= Audio::Mixer::FLAG_16BITS;
+
+ if (!buffer.isBigEndian)
+ flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+ }
+ if (buffer.stereo)
+ flags |= Audio::Mixer::FLAG_STEREO;
+ if (!buffer.isSigned)
+ flags |= Audio::Mixer::FLAG_UNSIGNED;
+
+ _mixer->playRaw(handle, buffer.buffer, buffer.size, buffer.frequency, flags, -1, volume);
+}
+
+void Sound::playSound(SoundBuffer &buffer, int volume, bool loop) {
+ SndHandle *handle = getHandle();
+
+ handle->type = kEffectHandle;
+ playSoundBuffer(&handle->handle, buffer, 2 * volume, loop);
+}
+
+void Sound::pauseSound() {
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ if (_handles[i].type == kEffectHandle)
+ _mixer->pauseHandle(_handles[i].handle, true);
+}
+
+void Sound::resumeSound() {
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ if (_handles[i].type == kEffectHandle)
+ _mixer->pauseHandle(_handles[i].handle, false);
+}
+
+void Sound::stopSound() {
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ if (_handles[i].type == kEffectHandle) {
+ _mixer->stopHandle(_handles[i].handle);
+ _handles[i].type = kFreeHandle;
+ }
+}
+
+void Sound::playVoice(SoundBuffer &buffer) {
+ SndHandle *handle = getHandle();
+
+ handle->type = kVoiceHandle;
+ playSoundBuffer(&handle->handle, buffer, 255, false);
+}
+
+void Sound::pauseVoice() {
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ if (_handles[i].type == kVoiceHandle)
+ _mixer->pauseHandle(_handles[i].handle, true);
+}
+
+void Sound::resumeVoice() {
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ if (_handles[i].type == kVoiceHandle)
+ _mixer->pauseHandle(_handles[i].handle, false);
+}
+
+void Sound::stopVoice() {
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ if (_handles[i].type == kVoiceHandle) {
+ _mixer->stopHandle(_handles[i].handle);
+ _handles[i].type = kFreeHandle;
+ }
+}
+
+void Sound::stopAll() {
+ stopVoice();
+ stopSound();
+}
+
+void Sound::setVolume(int volume) {
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume);
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume);
+}
+
+} // End of namespace Saga
diff --git a/engines/saga/sound.h b/engines/saga/sound.h
new file mode 100644
index 0000000000..85c3eda748
--- /dev/null
+++ b/engines/saga/sound.h
@@ -0,0 +1,97 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Sound class
+
+#ifndef SAGA_SOUND_H_
+#define SAGA_SOUND_H_
+
+#include "sound/mixer.h"
+
+namespace Saga {
+
+#define SOUND_HANDLES 10
+
+enum SOUND_FLAGS {
+ SOUND_LOOP = 1
+};
+
+struct SoundBuffer {
+ uint16 frequency;
+ int sampleBits;
+ bool stereo;
+ bool isSigned;
+
+ byte *buffer;
+ size_t size;
+ bool isBigEndian;
+};
+
+enum sndHandleType {
+ kFreeHandle,
+ kEffectHandle,
+ kVoiceHandle
+};
+
+struct SndHandle {
+ Audio::SoundHandle handle;
+ sndHandleType type;
+};
+
+class Sound {
+public:
+
+ Sound(SagaEngine *vm, Audio::Mixer *mixer, int volume);
+ ~Sound();
+
+ void playSound(SoundBuffer &buffer, int volume, bool loop);
+ void pauseSound();
+ void resumeSound();
+ void stopSound();
+
+ void playVoice(SoundBuffer &buffer);
+ void pauseVoice();
+ void resumeVoice();
+ void stopVoice();
+
+ void stopAll();
+
+ void setVolume(int volume);
+
+ private:
+
+ void playSoundBuffer(Audio::SoundHandle *handle, SoundBuffer &buffer, int volume, bool loop);
+
+ SndHandle *getHandle();
+
+ SagaEngine *_vm;
+ Audio::Mixer *_mixer;
+ Common::MemoryReadStream *_voxStream;
+
+ SndHandle _handles[SOUND_HANDLES];
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/sprite.cpp b/engines/saga/sprite.cpp
new file mode 100644
index 0000000000..5309b1f109
--- /dev/null
+++ b/engines/saga/sprite.cpp
@@ -0,0 +1,444 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Sprite management module
+#include "saga/saga.h"
+
+#include "saga/gfx.h"
+#include "saga/scene.h"
+#include "saga/resnames.h"
+#include "saga/rscfile.h"
+#include "saga/font.h"
+
+#include "saga/sprite.h"
+#include "saga/stream.h"
+
+namespace Saga {
+
+Sprite::Sprite(SagaEngine *vm) : _vm(vm) {
+ debug(8, "Initializing sprite subsystem...");
+
+ // Load sprite module resource context
+ _spriteContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ if (_spriteContext == NULL) {
+ error("Sprite::Sprite resource context not found");
+ }
+
+ _decodeBufLen = DECODE_BUF_LEN;
+
+ _decodeBuf = (byte *)malloc(_decodeBufLen);
+ if (_decodeBuf == NULL) {
+ memoryError("Sprite::Sprite");
+ }
+
+ if (_vm->getGameType() == GType_ITE) {
+ loadList(_vm->getResourceDescription()->mainSpritesResourceId, _mainSprites);
+ _arrowSprites = _saveReminderSprites = _inventorySprites = _mainSprites;
+ } else {
+ loadList(RID_IHNM_ARROW_SPRITES, _arrowSprites);
+ loadList(RID_IHNM_SAVEREMINDER_SPRITES, _saveReminderSprites);
+ }
+}
+
+Sprite::~Sprite(void) {
+ debug(8, "Shutting down sprite subsystem...");
+ _mainSprites.freeMem();
+ free(_decodeBuf);
+}
+
+void Sprite::loadList(int resourceId, SpriteList &spriteList) {
+ SpriteInfo *spriteInfo;
+ byte *spriteListData;
+ size_t spriteListLength;
+ uint16 oldSpriteCount;
+ uint16 newSpriteCount;
+ uint16 spriteCount;
+ int i;
+ int outputLength, inputLength;
+ uint32 offset;
+ const byte *spritePointer;
+ const byte *spriteDataPointer;
+
+ _vm->_resource->loadResource(_spriteContext, resourceId, spriteListData, spriteListLength);
+
+ if (spriteListLength == 0) {
+ return;
+ }
+
+ MemoryReadStreamEndian readS(spriteListData, spriteListLength, _spriteContext->isBigEndian);
+
+ spriteCount = readS.readUint16();
+
+ debug(9, "Sprites: %d", spriteCount);
+
+ oldSpriteCount = spriteList.spriteCount;
+ newSpriteCount = spriteList.spriteCount + spriteCount;
+
+ spriteList.infoList = (SpriteInfo *)realloc(spriteList.infoList, newSpriteCount * sizeof(*spriteList.infoList));
+ if (spriteList.infoList == NULL) {
+ memoryError("Sprite::loadList");
+ }
+
+ spriteList.spriteCount = newSpriteCount;
+
+ bool bigHeader = _vm->getGameType() != GType_ITE || _vm->isMacResources();
+
+ for (i = oldSpriteCount; i < spriteList.spriteCount; i++) {
+ spriteInfo = &spriteList.infoList[i];
+ if (bigHeader)
+ offset = readS.readUint32();
+ else
+ offset = readS.readUint16();
+
+ if (offset >= spriteListLength) {
+ error("Sprite::loadList offset exceed");
+ }
+
+ spritePointer = spriteListData;
+ spritePointer += offset;
+
+ if (bigHeader) {
+ MemoryReadStreamEndian readS2(spritePointer, 8, _spriteContext->isBigEndian);
+
+ spriteInfo->xAlign = readS2.readSint16();
+ spriteInfo->yAlign = readS2.readSint16();
+
+ spriteInfo->width = readS2.readUint16();
+ spriteInfo->height = readS2.readUint16();
+
+ spriteDataPointer = spritePointer + readS2.pos();
+ } else {
+ MemoryReadStreamEndian readS2(spritePointer, 4);
+
+ spriteInfo->xAlign = readS2.readSByte();
+ spriteInfo->yAlign = readS2.readSByte();
+
+ spriteInfo->width = readS2.readByte();
+ spriteInfo->height = readS2.readByte();
+ spriteDataPointer = spritePointer + readS2.pos();
+ }
+
+ outputLength = spriteInfo->width * spriteInfo->height;
+ inputLength = spriteListLength - (spriteDataPointer - spriteListData);
+ decodeRLEBuffer(spriteDataPointer, inputLength, outputLength);
+ spriteInfo->decodedBuffer = (byte *) malloc(outputLength);
+ if (spriteInfo->decodedBuffer == NULL) {
+ memoryError("Sprite::loadList");
+ }
+
+ // IHNM sprites are upside-down, for reasons which i can only
+ // assume are perverse. To simplify things, flip them now. Not
+ // at drawing time.
+
+ if (_vm->getGameType() == GType_IHNM) {
+ byte *src = _decodeBuf + spriteInfo->width * (spriteInfo->height - 1);
+ byte *dst = spriteInfo->decodedBuffer;
+
+ for (int j = 0; j < spriteInfo->height; j++) {
+ memcpy(dst, src, spriteInfo->width);
+ src -= spriteInfo->width;
+ dst += spriteInfo->width;
+ }
+ } else
+ memcpy(spriteInfo->decodedBuffer, _decodeBuf, outputLength);
+ }
+
+ free(spriteListData);
+}
+
+void Sprite::getScaledSpriteBuffer(SpriteList &spriteList, int spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer) {
+ SpriteInfo *spriteInfo;
+ assert(spriteList.spriteCount>spriteNumber);
+ spriteInfo = &spriteList.infoList[spriteNumber];
+
+ if (scale < 256) {
+ xAlign = (spriteInfo->xAlign * scale) >> 8;
+ yAlign = (spriteInfo->yAlign * scale) >> 8;
+ height = (spriteInfo->height * scale + 0x7f) >> 8;
+ width = (spriteInfo->width * scale + 0x7f) >> 8;
+ scaleBuffer(spriteInfo->decodedBuffer, spriteInfo->width, spriteInfo->height, scale);
+ buffer = _decodeBuf;
+ } else {
+ xAlign = spriteInfo->xAlign;
+ yAlign = spriteInfo->yAlign;
+ height = spriteInfo->height;
+ width = spriteInfo->width;
+ buffer = spriteInfo->decodedBuffer;
+ }
+}
+
+void Sprite::drawClip(Surface *ds, const Rect &clipRect, const Point &spritePointer, int width, int height, const byte *spriteBuffer) {
+ int clipWidth;
+ int clipHeight;
+
+ int i, j, jo, io;
+ byte *bufRowPointer;
+ const byte *srcRowPointer;
+
+ bufRowPointer = (byte *)ds->pixels + ds->pitch * spritePointer.y;
+ srcRowPointer = spriteBuffer;
+
+ clipWidth = width;
+ if (width > (clipRect.right - spritePointer.x)) {
+ clipWidth = (clipRect.right - spritePointer.x);
+ }
+
+ clipHeight = height;
+ if (height > (clipRect.bottom - spritePointer.y)) {
+ clipHeight = (clipRect.bottom - spritePointer.y);
+ }
+
+ jo = 0;
+ io = 0;
+ if (spritePointer.x < clipRect.left) {
+ jo = clipRect.left - spritePointer.x;
+ }
+ if (spritePointer.y < clipRect.top) {
+ io = clipRect.top - spritePointer.y;
+ bufRowPointer += ds->pitch * io;
+ srcRowPointer += width * io;
+ }
+
+ for (i = io; i < clipHeight; i++) {
+ for (j = jo; j < clipWidth; j++) {
+ assert((byte *)ds->pixels <= (byte *)(bufRowPointer + j + spritePointer.x));
+ assert(((byte *)ds->pixels + (_vm->getDisplayWidth() *
+ _vm->getDisplayHeight())) > (byte *)(bufRowPointer + j + spritePointer.x));
+ assert((const byte *)spriteBuffer <= (const byte *)(srcRowPointer + j));
+ assert(((const byte *)spriteBuffer + (width * height)) > (const byte *)(srcRowPointer + j));
+
+ if (*(srcRowPointer + j) != 0) {
+ *(bufRowPointer + j + spritePointer.x) = *(srcRowPointer + j);
+ }
+ }
+ bufRowPointer += ds->pitch;
+ srcRowPointer += width;
+ }
+}
+
+void Sprite::draw(Surface *ds, const Rect &clipRect, SpriteList &spriteList, int32 spriteNumber, const Point &screenCoord, int scale) {
+ const byte *spriteBuffer;
+ int width;
+ int height;
+ int xAlign;
+ int yAlign;
+ Point spritePointer;
+
+ getScaledSpriteBuffer(spriteList, spriteNumber, scale, width, height, xAlign, yAlign, spriteBuffer);
+
+ spritePointer.x = screenCoord.x + xAlign;
+ spritePointer.y = screenCoord.y + yAlign;
+
+ drawClip(ds, clipRect, spritePointer, width, height, spriteBuffer);
+}
+
+void Sprite::draw(Surface *ds, const Rect &clipRect, SpriteList &spriteList, int32 spriteNumber, const Rect &screenRect, int scale) {
+ const byte *spriteBuffer;
+ int width;
+ int height;
+ int xAlign, spw;
+ int yAlign, sph;
+ Point spritePointer;
+
+ getScaledSpriteBuffer(spriteList, spriteNumber, scale, width, height, xAlign, yAlign, spriteBuffer);
+ spw = (screenRect.width() - width) / 2;
+ sph = (screenRect.height() - height) / 2;
+ if (spw < 0) {
+ spw = 0;
+ }
+ if (sph < 0) {
+ sph = 0;
+ }
+ spritePointer.x = screenRect.left + xAlign + spw;
+ spritePointer.y = screenRect.top + yAlign + sph;
+ drawClip(ds, clipRect, spritePointer, width, height, spriteBuffer);
+}
+
+bool Sprite::hitTest(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, const Point &testPoint) {
+ const byte *spriteBuffer;
+ int i, j;
+ const byte *srcRowPointer;
+ int width;
+ int height;
+ int xAlign;
+ int yAlign;
+ Point spritePointer;
+
+ getScaledSpriteBuffer(spriteList, spriteNumber, scale, width, height, xAlign, yAlign, spriteBuffer);
+
+ spritePointer.x = screenCoord.x + xAlign;
+ spritePointer.y = screenCoord.y + yAlign;
+
+ if ((testPoint.y < spritePointer.y) || (testPoint.y >= spritePointer.y + height)) {
+ return false;
+ }
+ if ((testPoint.x < spritePointer.x) || (testPoint.x >= spritePointer.x + width)) {
+ return false;
+ }
+ i = testPoint.y - spritePointer.y;
+ j = testPoint.x - spritePointer.x;
+ srcRowPointer = spriteBuffer + j + i * width;
+ return *srcRowPointer != 0;
+}
+
+void Sprite::drawOccluded(Surface *ds, const Rect &clipRect, SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, int depth) {
+ const byte *spriteBuffer;
+ int x, y;
+ byte *destRowPointer;
+ const byte *sourceRowPointer;
+ const byte *sourcePointer;
+ byte *destPointer;
+ byte *maskPointer;
+ int width;
+ int height;
+ int xAlign;
+ int yAlign;
+
+ ClipData clipData;
+
+ // BG mask variables
+ int maskWidth;
+ int maskHeight;
+ byte *maskBuffer;
+ size_t maskBufferLength;
+ byte *maskRowPointer;
+ int maskZ;
+
+ if (!_vm->_scene->isBGMaskPresent()) {
+ draw(ds, clipRect, spriteList, spriteNumber, screenCoord, scale);
+ return;
+ }
+
+ _vm->_scene->getBGMaskInfo(maskWidth, maskHeight, maskBuffer, maskBufferLength);
+
+ getScaledSpriteBuffer(spriteList, spriteNumber, scale, width, height, xAlign, yAlign, spriteBuffer);
+
+ clipData.destPoint.x = screenCoord.x + xAlign;
+ clipData.destPoint.y = screenCoord.y + yAlign;
+
+ clipData.sourceRect.left = 0;
+ clipData.sourceRect.top = 0;
+ clipData.sourceRect.right = width;
+ clipData.sourceRect.bottom = height;
+
+ clipData.destRect = clipRect;
+
+ if (!clipData.calcClip()) {
+ return;
+ }
+
+ // Finally, draw the occluded sprite
+
+ sourceRowPointer = spriteBuffer + clipData.drawSource.x + (clipData.drawSource.y * width);
+ destRowPointer = (byte *)ds->pixels + clipData.drawDest.x + (clipData.drawDest.y * ds->pitch);
+ maskRowPointer = maskBuffer + clipData.drawDest.x + (clipData.drawDest.y * maskWidth);
+
+ for (y = 0; y < clipData.drawHeight; y++) {
+ sourcePointer = sourceRowPointer;
+ destPointer = destRowPointer;
+ maskPointer = maskRowPointer;
+ for (x = 0; x < clipData.drawWidth; x++) {
+ if (*sourcePointer != 0) {
+ maskZ = *maskPointer & SPRITE_ZMASK;
+ if (maskZ > depth) {
+ *destPointer = *sourcePointer;
+ }
+ }
+ sourcePointer++;
+ destPointer++;
+ maskPointer++;
+ }
+ destRowPointer += ds->pitch;
+ maskRowPointer += maskWidth;
+ sourceRowPointer += width;
+ }
+}
+
+void Sprite::decodeRLEBuffer(const byte *inputBuffer, size_t inLength, size_t outLength) {
+ int bg_runcount;
+ int fg_runcount;
+ byte *outPointer;
+ byte *outPointerEnd;
+ int c;
+
+ if (outLength > _decodeBufLen) { // TODO: may we should make dynamic growing?
+ error("Sprite::decodeRLEBuffer outLength > _decodeBufLen");
+ }
+
+ outPointer = _decodeBuf;
+ outPointerEnd = _decodeBuf + outLength;
+ outPointerEnd--;
+
+ memset(outPointer, 0, outLength);
+
+ MemoryReadStream readS(inputBuffer, inLength);
+
+ while (!readS.eos() && (outPointer < outPointerEnd)) {
+ bg_runcount = readS.readByte();
+ fg_runcount = readS.readByte();
+
+ for (c = 0; c < bg_runcount && !readS.eos(); c++) {
+ *outPointer = (byte) 0;
+ if (outPointer < outPointerEnd)
+ outPointer++;
+ else
+ return;
+ }
+
+ for (c = 0; c < fg_runcount && !readS.eos(); c++) {
+ *outPointer = readS.readByte();
+ if (outPointer < outPointerEnd)
+ outPointer++;
+ else
+ return;
+ }
+ }
+}
+
+void Sprite::scaleBuffer(const byte *src, int width, int height, int scale) {
+ byte skip = 256 - scale; // skip factor
+ byte vskip = 0x80, hskip;
+ byte *dst = _decodeBuf;
+
+ for (int i = 0; i < height; i++) {
+ vskip += skip;
+
+ if (vskip < skip) { // We had an overflow
+ src += width;
+ } else {
+ hskip = 0x80;
+
+ for (int j = 0; j < width; j++) {
+ *dst++ = *src++;
+
+ hskip += skip;
+ if (hskip < skip) // overflow
+ dst--;
+ }
+ }
+ }
+}
+
+
+} // End of namespace Saga
diff --git a/engines/saga/sprite.h b/engines/saga/sprite.h
new file mode 100644
index 0000000000..03bde8e050
--- /dev/null
+++ b/engines/saga/sprite.h
@@ -0,0 +1,102 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Sprite management module private header file
+
+#ifndef SAGA_SPRITE_H__
+#define SAGA_SPRITE_H__
+
+namespace Saga {
+
+#define SPRITE_ZMAX 16
+#define SPRITE_ZMASK 0x0F
+
+#define DECODE_BUF_LEN 64000
+
+struct SpriteInfo {
+ byte *decodedBuffer;
+ int width;
+ int height;
+ int xAlign;
+ int yAlign;
+};
+
+struct SpriteList {
+ int spriteListResourceId;
+ int spriteCount;
+ SpriteInfo *infoList;
+
+ void freeMem() {
+ int i;
+ for (i = 0; i < spriteCount; i++) {
+ free(infoList[i].decodedBuffer);
+ }
+ free(infoList);
+ memset(this, 0, sizeof(*this));
+ }
+
+ SpriteList() {
+ memset(this, 0, sizeof(*this));
+ }
+};
+
+
+class Sprite {
+public:
+ SpriteList _mainSprites;
+ SpriteList _saveReminderSprites;
+ SpriteList _arrowSprites;
+ SpriteList _inventorySprites;
+
+ Sprite(SagaEngine *vm);
+ ~Sprite(void);
+
+ // draw scaled sprite using background scene mask
+ void drawOccluded(Surface *ds, const Rect &clipRect, SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, int depth);
+
+ // draw scaled sprite using background scene mask
+ void draw(Surface *ds, const Rect &clipRect, SpriteList &spriteList, int32 spriteNumber, const Point &screenCoord, int scale);
+
+ // main function
+ void drawClip(Surface *ds, const Rect &clipRect, const Point &spritePointer, int width, int height, const byte *spriteBuffer);
+
+ void draw(Surface *ds, const Rect &clipRect, SpriteList &spriteList, int32 spriteNumber, const Rect &screenRect, int scale);
+
+ void loadList(int resourceId, SpriteList &spriteList); // load or append spriteList
+ bool hitTest(SpriteList &spriteList, int spriteNumber, const Point &screenCoord, int scale, const Point &testPoint);
+ void getScaledSpriteBuffer(SpriteList &spriteList, int spriteNumber, int scale, int &width, int &height, int &xAlign, int &yAlign, const byte *&buffer);
+
+private:
+ void decodeRLEBuffer(const byte *inputBuffer, size_t inLength, size_t outLength);
+ void scaleBuffer(const byte *src, int width, int height, int scale);
+
+ SagaEngine *_vm;
+ ResourceContext *_spriteContext;
+ byte *_decodeBuf;
+ size_t _decodeBufLen;
+};
+
+} // End of namespace Saga
+
+#endif
diff --git a/engines/saga/sthread.cpp b/engines/saga/sthread.cpp
new file mode 100644
index 0000000000..a6724b588c
--- /dev/null
+++ b/engines/saga/sthread.cpp
@@ -0,0 +1,746 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Scripting module thread management component
+#include "saga/saga.h"
+
+#include "saga/gfx.h"
+#include "saga/actor.h"
+#include "saga/console.h"
+#include "saga/interface.h"
+
+#include "saga/script.h"
+
+#include "saga/stream.h"
+#include "saga/scene.h"
+#include "saga/resnames.h"
+
+namespace Saga {
+
+ScriptThread *Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntryPointNumber) {
+ ScriptThread *newThread;
+
+ loadModule(scriptModuleNumber);
+ if (_modules[scriptModuleNumber].entryPointsCount <= scriptEntryPointNumber) {
+ error("Script::createThread wrong scriptEntryPointNumber");
+ }
+
+ newThread = _threadList.pushFront().operator->();
+ newThread->_flags = kTFlagNone;
+ newThread->_stackSize = DEFAULT_THREAD_STACK_SIZE;
+ newThread->_stackBuf = (uint16 *)malloc(newThread->_stackSize * sizeof(*newThread->_stackBuf));
+ newThread->_stackTopIndex = newThread->_stackSize - 2;
+ newThread->_instructionOffset = _modules[scriptModuleNumber].entryPoints[scriptEntryPointNumber].offset;
+ newThread->_commonBase = _commonBuffer;
+ newThread->_staticBase = _commonBuffer + _modules[scriptModuleNumber].staticOffset;
+ newThread->_moduleBase = _modules[scriptModuleNumber].moduleBase;
+ newThread->_moduleBaseSize = _modules[scriptModuleNumber].moduleBaseSize;
+
+ newThread->_strings = &_modules[scriptModuleNumber].strings;
+
+ if (_vm->getGameType() == GType_IHNM)
+ newThread->_voiceLUT = &_globalVoiceLUT;
+ else
+ newThread->_voiceLUT = &_modules[scriptModuleNumber].voiceLUT;
+
+ return newThread;
+}
+
+void Script::wakeUpActorThread(int waitType, void *threadObj) {
+ ScriptThread *thread;
+ ScriptThreadList::iterator threadIterator;
+
+ for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
+ thread = threadIterator.operator->();
+ if ((thread->_flags & kTFlagWaiting) && (thread->_waitType == waitType) && (thread->_threadObj == threadObj)) {
+ thread->_flags &= ~kTFlagWaiting;
+ }
+ }
+}
+
+void Script::wakeUpThreads(int waitType) {
+ ScriptThread *thread;
+ ScriptThreadList::iterator threadIterator;
+
+ for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
+ thread = threadIterator.operator->();
+ if ((thread->_flags & kTFlagWaiting) && (thread->_waitType == waitType)) {
+ thread->_flags &= ~kTFlagWaiting;
+ }
+ }
+}
+
+void Script::wakeUpThreadsDelayed(int waitType, int sleepTime) {
+ ScriptThread *thread;
+ ScriptThreadList::iterator threadIterator;
+
+ for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
+ thread = threadIterator.operator->();
+ if ((thread->_flags & kTFlagWaiting) && (thread->_waitType == waitType)) {
+ thread->_waitType = kWaitTypeDelay;
+ thread->_sleepTime = sleepTime;
+ }
+ }
+}
+
+void Script::executeThreads(uint msec) {
+ ScriptThread *thread;
+ ScriptThreadList::iterator threadIterator;
+
+ if (_vm->_interface->_statusTextInput) {
+ return;
+ }
+
+ threadIterator = _threadList.begin();
+
+ while (threadIterator != _threadList.end()) {
+ thread = threadIterator.operator->();
+
+ if (thread->_flags & (kTFlagFinished | kTFlagAborted)) {
+ if (thread->_flags & kTFlagFinished)
+ setPointerVerb();
+
+ if (_vm->getGameType() == GType_IHNM) {
+ thread->_flags &= ~kTFlagFinished;
+ thread->_flags |= kTFlagAborted;
+ ++threadIterator;
+ } else {
+ threadIterator = _threadList.erase(threadIterator);
+ }
+ continue;
+ }
+
+ if (thread->_flags & kTFlagWaiting) {
+
+ switch (thread->_waitType) {
+ case kWaitTypeDelay:
+ if (thread->_sleepTime < msec) {
+ thread->_sleepTime = 0;
+ } else {
+ thread->_sleepTime -= msec;
+ }
+
+ if (thread->_sleepTime == 0)
+ thread->_flags &= ~kTFlagWaiting;
+ break;
+
+ case kWaitTypeWalk:
+ {
+ ActorData *actor;
+ actor = (ActorData *)thread->_threadObj;
+ if (actor->_currentAction == kActionWait) {
+ thread->_flags &= ~kTFlagWaiting;
+ }
+ }
+ break;
+
+ case kWaitTypeWaitFrames: // IHNM
+ if (thread->_frameWait < _vm->_frameCount)
+ thread->_flags &= ~kTFlagWaiting;
+ break;
+ }
+ }
+
+ if (!(thread->_flags & kTFlagWaiting)) {
+ if (runThread(thread, STHREAD_TIMESLICE)) {
+ break;
+ }
+ }
+
+ ++threadIterator;
+ }
+
+}
+
+void Script::abortAllThreads(void) {
+ ScriptThread *thread;
+ ScriptThreadList::iterator threadIterator;
+
+ threadIterator = _threadList.begin();
+
+ while (threadIterator != _threadList.end()) {
+ thread = threadIterator.operator->();
+ thread->_flags |= kTFlagAborted;
+ ++threadIterator;
+ }
+ executeThreads(0);
+}
+
+void Script::completeThread(void) {
+ int limit = (_vm->getGameType() == GType_IHNM) ? 100 : 40;
+
+ for (int i = 0; i < limit && !_threadList.isEmpty(); i++)
+ executeThreads(0);
+}
+
+bool Script::runThread(ScriptThread *thread, uint instructionLimit) {
+ const char*operandName;
+ uint instructionCount;
+ uint16 savedInstructionOffset;
+
+ byte *addr;
+ byte mode;
+ uint16 jmpOffset1;
+ int16 iparam1;
+ int16 iparam2;
+ int16 iparam3;
+
+ bool disContinue;
+ byte argumentsCount;
+ uint16 functionNumber;
+ uint16 checkStackTopIndex;
+ ScriptFunctionType scriptFunction;
+
+ int operandChar;
+ int i;
+
+ MemoryReadStream scriptS(thread->_moduleBase, thread->_moduleBaseSize);
+
+ scriptS.seek(thread->_instructionOffset);
+
+ for (instructionCount = 0; instructionCount < instructionLimit; instructionCount++) {
+ if (thread->_flags & (kTFlagAsleep))
+ break;
+
+ savedInstructionOffset = thread->_instructionOffset;
+ operandChar = scriptS.readByte();
+
+
+#define CASEOP(opName) case opName: \
+ if (operandChar == opName) { \
+ operandName = #opName; \
+ debug(2, operandName); \
+ _vm->_console->DebugPrintf("%s\n", operandName); \
+ }
+
+ debug(8, "Executing thread offset: %lu (%x) stack: %d", thread->_instructionOffset, operandChar, thread->pushedSize());
+ operandName="";
+ switch (operandChar) {
+ CASEOP(opNextBlock)
+ // Some sort of "jump to the start of the next memory
+ // page" instruction, I think.
+ thread->_instructionOffset = (((thread->_instructionOffset) >> 10) + 1) << 10;
+ break;
+
+// STACK INSTRUCTIONS
+ CASEOP(opDup)
+ thread->push(thread->stackTop());
+ break;
+ CASEOP(opDrop)
+ thread->pop();
+ break;
+ CASEOP(opZero)
+ thread->push(0);
+ break;
+ CASEOP(opOne)
+ thread->push(1);
+ break;
+ CASEOP(opConstint)
+ CASEOP(opStrlit)
+ iparam1 = scriptS.readSint16LE();
+ thread->push(iparam1);
+ debug(8, "0x%X", iparam1);
+ break;
+
+// DATA INSTRUCTIONS
+ CASEOP(opGetFlag)
+ addr = thread->baseAddress(scriptS.readByte());
+ iparam1 = scriptS.readSint16LE();
+ addr += (iparam1 >> 3);
+ iparam1 = (1 << (iparam1 & 7));
+ thread->push((*addr) & iparam1 ? 1 : 0);
+ break;
+ CASEOP(opGetInt)
+ mode = scriptS.readByte();
+ addr = thread->baseAddress(mode);
+ iparam1 = scriptS.readSint16LE();
+ addr += iparam1;
+ thread->push(readUint16(addr, mode));
+ debug(8, "0x%X", readUint16(addr, mode));
+ break;
+ CASEOP(opPutFlag)
+ addr = thread->baseAddress(scriptS.readByte());
+ iparam1 = scriptS.readSint16LE();
+ addr += (iparam1 >> 3);
+ iparam1 = (1 << (iparam1 & 7));
+ if (thread->stackTop()) {
+ *addr |= iparam1;
+ } else {
+ *addr &= ~iparam1;
+ }
+ break;
+ CASEOP(opPutInt)
+ mode = scriptS.readByte();
+ addr = thread->baseAddress(mode);
+ iparam1 = scriptS.readSint16LE();
+ addr += iparam1;
+ writeUint16(addr, thread->stackTop(), mode);
+ break;
+ CASEOP(opPutFlagV)
+ addr = thread->baseAddress(scriptS.readByte());
+ iparam1 = scriptS.readSint16LE();
+ addr += (iparam1 >> 3);
+ iparam1 = (1 << (iparam1 & 7));
+ if (thread->pop()) {
+ *addr |= iparam1;
+ } else {
+ *addr &= ~iparam1;
+ }
+ break;
+ CASEOP(opPutIntV)
+ mode = scriptS.readByte();
+ addr = thread->baseAddress(mode);
+ iparam1 = scriptS.readSint16LE();
+ addr += iparam1;
+ writeUint16(addr, thread->pop(), mode);
+ break;
+
+// FUNCTION CALL INSTRUCTIONS
+ CASEOP(opCall)
+ argumentsCount = scriptS.readByte();
+ iparam1 = scriptS.readByte();
+ if (iparam1 != kAddressModule) {
+ error("Script::runThread iparam1 != kAddressModule");
+ }
+ addr = thread->baseAddress(iparam1);
+ iparam1 = scriptS.readSint16LE();
+ addr += iparam1;
+ thread->push(argumentsCount);
+
+ jmpOffset1 = scriptS.pos();
+ // NOTE: The original pushes the program
+ // counter as a pointer here. But I don't think
+ // we will have to do that.
+ thread->push(jmpOffset1);
+ // NOTE2: program counter is 32bit - so we should "emulate" it size - because kAddressStack relies on it
+ thread->push(0);
+ thread->_instructionOffset = iparam1;
+
+ break;
+ CASEOP(opCcall)
+ CASEOP(opCcallV)
+ argumentsCount = scriptS.readByte();
+ functionNumber = scriptS.readUint16LE();
+ if (functionNumber >= ((_vm->getGameType() == GType_IHNM) ?
+ IHNM_SCRIPT_FUNCTION_MAX : ITE_SCRIPT_FUNCTION_MAX)) {
+ error("Script::runThread() Invalid script function number (%d)", functionNumber);
+ }
+
+ debug(2, "Calling #%d %s argCount=%i", functionNumber, _scriptFunctionsList[functionNumber].scriptFunctionName, argumentsCount);
+ scriptFunction = _scriptFunctionsList[functionNumber].scriptFunction;
+ checkStackTopIndex = thread->_stackTopIndex + argumentsCount;
+ disContinue = false;
+ (this->*scriptFunction)(thread, argumentsCount, disContinue);
+ if (disContinue) {
+ return true;
+ }
+ if (scriptFunction == &Saga::Script::sfScriptGotoScene ||
+ scriptFunction == &Saga::Script::sfVsetTrack) {
+ return true; // cause abortAllThreads called and _this_ thread destroyed
+ }
+
+ thread->_stackTopIndex = checkStackTopIndex;
+
+ if (operandChar == opCcall) {// CALL function
+ thread->push(thread->_returnValue);
+ }
+
+ if (thread->_flags & kTFlagAsleep)
+ instructionCount = instructionLimit; // break out of loop!
+ break;
+ CASEOP(opEnter)
+ thread->push(thread->_frameIndex);
+ thread->_frameIndex = thread->_stackTopIndex;
+ thread->_stackTopIndex -= (scriptS.readSint16LE() / 2);
+ break;
+ CASEOP(opReturn)
+ thread->_returnValue = thread->pop();
+ CASEOP(opReturnV)
+ thread->_stackTopIndex = thread->_frameIndex;
+ thread->_frameIndex = thread->pop();
+ if (thread->pushedSize() == 0) {
+ thread->_flags |= kTFlagFinished;
+ return true;
+ } else {
+ thread->pop(); //cause it 0
+ thread->_instructionOffset = thread->pop();
+
+ // Pop all the call parameters off the stack
+ iparam1 = thread->pop();
+ while (iparam1--) {
+ thread->pop();
+ }
+
+ if (operandChar == opReturn) {
+ thread->push(thread->_returnValue);
+ }
+ }
+ break;
+
+// BRANCH INSTRUCTIONS
+ CASEOP(opJmp)
+ jmpOffset1 = scriptS.readUint16LE();
+ thread->_instructionOffset = jmpOffset1;
+ break;
+ CASEOP(opJmpTrueV)
+ jmpOffset1 = scriptS.readUint16LE();
+ if (thread->pop()) {
+ thread->_instructionOffset = jmpOffset1;
+ }
+ break;
+ CASEOP(opJmpFalseV)
+ jmpOffset1 = scriptS.readUint16LE();
+ if (!thread->pop()) {
+ thread->_instructionOffset = jmpOffset1;
+ }
+ break;
+ CASEOP(opJmpTrue)
+ jmpOffset1 = scriptS.readUint16LE();
+ if (thread->stackTop()) {
+ thread->_instructionOffset = jmpOffset1;
+ }
+ break;
+ CASEOP(opJmpFalse)
+ jmpOffset1 = scriptS.readUint16LE();
+ if (!thread->stackTop()) {
+ thread->_instructionOffset = jmpOffset1;
+ }
+ break;
+ CASEOP(opJmpSwitch)
+ iparam1 = scriptS.readSint16LE();
+ iparam2 = thread->pop();
+ while (iparam1--) {
+ iparam3 = scriptS.readUint16LE();
+ thread->_instructionOffset = scriptS.readUint16LE();
+ if (iparam3 == iparam2) {
+ break;
+ }
+ }
+ if (iparam1 < 0) {
+ thread->_instructionOffset = scriptS.readUint16LE();
+ }
+ break;
+ CASEOP(opJmpRandom)
+ // Supposedly the number of possible branches.
+ // The original interpreter ignores it.
+ scriptS.readUint16LE();
+ iparam1 = scriptS.readSint16LE();
+ iparam1 = _vm->_rnd.getRandomNumber(iparam1 - 1);
+ while (1) {
+ iparam2 = scriptS.readSint16LE();
+ thread->_instructionOffset = scriptS.readUint16LE();
+
+ iparam1 -= iparam2;
+ if (iparam1 < 0) {
+ break;
+ }
+ }
+ break;
+
+// UNARY INSTRUCTIONS
+ CASEOP(opNegate)
+ thread->push(-thread->pop());
+ break;
+ CASEOP(opNot)
+ thread->push(!thread->pop());
+ break;
+ CASEOP(opCompl)
+ thread->push(~thread->pop());
+ break;
+
+ CASEOP(opIncV)
+ mode = scriptS.readByte();
+ addr = thread->baseAddress(mode);
+ iparam1 = scriptS.readSint16LE();
+ addr += iparam1;
+ iparam1 = readUint16(addr, mode);
+ writeUint16(addr, iparam1 + 1, mode);
+ break;
+ CASEOP(opDecV)
+ mode = scriptS.readByte();
+ addr = thread->baseAddress(mode);
+ iparam1 = scriptS.readSint16LE();
+ addr += iparam1;
+ iparam1 = readUint16(addr, mode);
+ writeUint16(addr, iparam1 - 1, mode);
+ break;
+ CASEOP(opPostInc)
+ mode = scriptS.readByte();
+ addr = thread->baseAddress(mode);
+ iparam1 = scriptS.readSint16LE();
+ addr += iparam1;
+ iparam1 = readUint16(addr, mode);
+ thread->push(iparam1);
+ writeUint16(addr, iparam1 + 1, mode);
+ break;
+ CASEOP(opPostDec)
+ mode = scriptS.readByte();
+ addr = thread->baseAddress(mode);
+ iparam1 = scriptS.readSint16LE();
+ addr += iparam1;
+ iparam1 = readUint16(addr, mode);
+ thread->push(iparam1);
+ writeUint16(addr, iparam1 - 1, mode);
+ break;
+
+// ARITHMETIC INSTRUCTIONS
+ CASEOP(opAdd)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 += iparam2;
+ thread->push(iparam1);
+ break;
+ CASEOP(opSub)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 -= iparam2;
+ thread->push(iparam1);
+ break;
+ CASEOP(opMul)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 *= iparam2;
+ thread->push(iparam1);
+ break;
+ CASEOP(opDiv)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 /= iparam2;
+ thread->push(iparam1);
+ break;
+ CASEOP(opMod)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 %= iparam2;
+ thread->push(iparam1);
+ break;
+
+// COMPARISION INSTRUCTIONS
+ CASEOP(opEq)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ thread->push((iparam1 == iparam2) ? 1 : 0);
+ debug(8, "0x%X 0x%X", iparam1, iparam2);
+ break;
+ CASEOP(opNe)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ thread->push((iparam1 != iparam2) ? 1 : 0);
+ break;
+ CASEOP(opGt)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ thread->push((iparam1 > iparam2) ? 1 : 0);
+ break;
+ CASEOP(opLt)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ thread->push((iparam1 < iparam2) ? 1 : 0);
+ break;
+ CASEOP(opGe)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ thread->push((iparam1 >= iparam2) ? 1 : 0);
+ break;
+ CASEOP(opLe)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ thread->push((iparam1 <= iparam2) ? 1 : 0);
+ break;
+
+// SHIFT INSTRUCTIONS
+ CASEOP(opRsh)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 >>= iparam2;
+ thread->push(iparam1);
+ break;
+ CASEOP(opLsh)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 <<= iparam2;
+ thread->push(iparam1);
+ break;
+
+// BITWISE INSTRUCTIONS
+ CASEOP(opAnd)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 &= iparam2;
+ thread->push(iparam1);
+ break;
+ CASEOP(opOr)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 |= iparam2;
+ thread->push(iparam1);
+ break;
+ CASEOP(opXor)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ iparam1 ^= iparam2;
+ thread->push(iparam1);
+ break;
+
+// LOGICAL INSTRUCTIONS
+ CASEOP(opLAnd)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ thread->push((iparam1 && iparam2) ? 1 : 0);
+ break;
+ CASEOP(opLOr)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ thread->push((iparam1 || iparam2) ? 1 : 0);
+ break;
+ CASEOP(opLXor)
+ iparam2 = thread->pop();
+ iparam1 = thread->pop();
+ thread->push(((iparam1 && !iparam2) || (!iparam1 && iparam2)) ? 1 : 0);
+ break;
+
+// GAME INSTRUCTIONS
+ CASEOP(opSpeak) {
+ int stringsCount;
+ uint16 actorId;
+ uint16 speechFlags;
+ int sampleResourceId = -1;
+ int16 first;
+ const char *strings[ACTOR_SPEECH_STRING_MAX];
+
+ if (_vm->_actor->isSpeaking()) {
+ thread->wait(kWaitTypeSpeech);
+ return false;
+ }
+
+ stringsCount = scriptS.readByte();
+ actorId = scriptS.readUint16LE();
+ speechFlags = scriptS.readByte();
+ scriptS.readUint16LE(); // x,y skip
+
+ if (stringsCount == 0)
+ error("opSpeak stringsCount == 0");
+
+ if (stringsCount > ACTOR_SPEECH_STRING_MAX)
+ error("opSpeak stringsCount=0x%X exceed ACTOR_SPEECH_STRING_MAX", stringsCount);
+
+ iparam1 = first = thread->stackTop();
+ for (i = 0; i < stringsCount; i++) {
+ iparam1 = thread->pop();
+ strings[i] = thread->_strings->getString(iparam1);
+ }
+ // now data contains last string index
+
+ if (_vm->getGameId() == GID_ITE_DISK_G) { // special ITE dos
+ if ((_vm->_scene->currentSceneNumber() == ITE_DEFAULT_SCENE) &&
+ (iparam1 >= 288) && (iparam1 <= (RID_SCENE1_VOICE_138 - RID_SCENE1_VOICE_009 + 288))) {
+ sampleResourceId = RID_SCENE1_VOICE_009 + iparam1 - 288;
+ }
+ } else {
+ if (thread->_voiceLUT->voicesCount > first) {
+ sampleResourceId = thread->_voiceLUT->voices[first];
+ }
+ }
+
+ if (sampleResourceId < 0 || sampleResourceId > 4000)
+ sampleResourceId = -1;
+
+ if (_vm->getGameType() == GType_ITE && !sampleResourceId)
+ sampleResourceId = -1;
+
+ _vm->_actor->actorSpeech(actorId, strings, stringsCount, sampleResourceId, speechFlags);
+
+ if (!(speechFlags & kSpeakAsync)) {
+ thread->wait(kWaitTypeSpeech);
+ }
+ }
+ break;
+ CASEOP(opDialogBegin)
+ if (_conversingThread) {
+ thread->wait(kWaitTypeDialogBegin);
+ return false;
+ }
+ _conversingThread = thread;
+ _vm->_interface->converseClear();
+ break;
+ CASEOP(opDialogEnd)
+ if (thread == _conversingThread) {
+ _vm->_interface->activate();
+ _vm->_interface->setMode(kPanelConverse);
+ thread->wait(kWaitTypeDialogEnd);
+ return false;
+ }
+ break;
+ CASEOP(opReply) {
+ const char *str;
+ byte replyNum;
+ byte flags;
+ replyNum = scriptS.readByte();
+ flags = scriptS.readByte();
+ iparam1 = 0;
+
+ if (flags & kReplyOnce) {
+ iparam1 = scriptS.readSint16LE();
+ addr = thread->_staticBase + (iparam1 >> 3);
+ if (*addr & (1 << (iparam1 & 7))) {
+ break;
+ }
+ }
+
+ str = thread->_strings->getString(thread->pop());
+ if (_vm->_interface->converseAddText(str, replyNum, flags, iparam1))
+ warning("Error adding ConverseText (%s, %d, %d, %d)", str, replyNum, flags, iparam1);
+ }
+ break;
+ CASEOP(opAnimate)
+ scriptS.readUint16LE();
+ scriptS.readUint16LE();
+ jmpOffset1 = scriptS.readByte();
+ thread->_instructionOffset += jmpOffset1;
+ break;
+
+ default:
+ error("Script::runThread() Invalid opcode encountered 0x%X", operandChar);
+ }
+
+ if (thread->_flags & (kTFlagFinished | kTFlagAborted)) {
+ error("Wrong flags %d in thread", thread->_flags);
+ }
+
+ // Set instruction offset only if a previous instruction didn't branch
+ if (savedInstructionOffset == thread->_instructionOffset) {
+ thread->_instructionOffset = scriptS.pos();
+ } else {
+ if (thread->_instructionOffset >= scriptS.size()) {
+ error("Script::runThread() Out of range script execution");
+ }
+
+ scriptS.seek(thread->_instructionOffset);
+ }
+ }
+ return false;
+}
+
+} // End of namespace Saga
+
diff --git a/engines/saga/stream.h b/engines/saga/stream.h
new file mode 100644
index 0000000000..4a407216b2
--- /dev/null
+++ b/engines/saga/stream.h
@@ -0,0 +1,57 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SAGA_STREAM_H__
+#define SAGA_STREAM_H__
+
+#include "common/stream.h"
+
+namespace Saga {
+
+using Common::MemoryReadStream;
+
+class MemoryReadStreamEndian : public Common::MemoryReadStream {
+private:
+public:
+ bool _bigEndian;
+ MemoryReadStreamEndian(const byte *buf, uint32 len, bool bigEndian = false) : MemoryReadStream(buf, len), _bigEndian(bigEndian) {}
+
+ uint16 readUint16() {
+ return (_bigEndian) ? readUint16BE(): readUint16LE();
+ }
+
+ uint32 readUint32() {
+ return (_bigEndian) ? readUint32BE(): readUint32LE();
+ }
+
+ inline int16 readSint16() {
+ return (int16)readUint16();
+ }
+
+
+ inline int32 readSint32() {
+ return (int32)readUint32();
+ }
+};
+
+} // End of namespace Saga
+#endif
diff --git a/engines/saga/xref.txt b/engines/saga/xref.txt
new file mode 100644
index 0000000000..1d3aff0824
--- /dev/null
+++ b/engines/saga/xref.txt
@@ -0,0 +1,139 @@
+$Id$
+
+Cross-reference for functions and variables for the original source code and
+the ScummVM implementation.
+
+Watcom C++ arguments order:
+
+ eax, edx, ebx, ecx, stack
+
+Sceneres.h
+==========
+ LOADREQ_FIGURE
+ LOADREQ_OBJECT
+ LOADREQ_BACKGROUND SAGA_BG_IMAGE
+ LOADREQ_ZBUF SAGA_BG_MASK
+ LOADREQ_SCENE_SCRIPT
+ LOADREQ_STRINGS SAGA_OBJECT_NAME_LIST
+ LOADREQ_HITZONES SAGA_OBJECT_MAP
+ LOADREQ_STEPZONES SAGA_ACTION_MAP
+ LOADREQ_TILE_IMAGES SAGA_ISO_TILESET
+ LOADREQ_TILE_MAP SAGA_ISO_METAMAP
+ LOADREQ_TILE_PLATFORMS SAGA_ISO_METATILESET
+ LOADREQ_TILE_METATILES
+ LOADREQ_ENTRY SAGA_ENTRY
+ LOADREQ_FRAMELIST
+
+ LOADREQ_ANIM_0 SAGA_ANIM_1
+ LOADREQ_ANIM_1 SAGA_ANIM_2
+ LOADREQ_ANIM_2 SAGA_ANIM_3
+ LOADREQ_ANIM_3 SAGA_ANIM_4
+ LOADREQ_ANIM_4 SAGA_ANIM_5
+ LOADREQ_ANIM_5 SAGA_ANIM_6
+ LOADREQ_ANIM_6 SAGA_ANIM_7
+ LOADREQ_ANIM_7
+
+ LOADREQ_TILE_MULTI
+ LOADREQ_CYCLES SAGA_PAL_ANIM
+ LOADREQ_FACES SAGA_FACES
+ LOADREQ_PALETTE
+
+ hitZone _objectMap
+ stepZone _actionMap
+
+ HZONEF_EXIT OBJECT_EXIT (in Verb.c), ACTION_EXIT (in Actor.c)
+ HZONEF_ENABLED OBJECT_ENABLED (in Verb.c), ACTION_ENABLED (in Actor.c)
+ HZONEF_NOWALK OBJECT_NOWALK
+ HZONEF_PROJECT OBJECT_PROJECT
+ HZONEF_AUTOWALK ACTION_AUTOWALK
+ HZONEF_TERMINUS ACTION_TERMINUS
+
+ FrameRange.startFrame ACTORACTIONITEM.frame_index
+ FrameRange.frameCount ACTORACTIONITEM.frame_count
+
+ FrameSequence.right ACTORACTION.dir[0]
+ FrameSequence.left ACTORACTION.dir[1]
+ FrameSequence.back ACTORACTION.dir[2]
+ FrameSequence.forward ACTORACTION.dir[3]
+
+Scene.c
+=======
+ ResToImage() _vm->decodeBGImage()
+ resInfo->sceneFlags _desc.flags
+ resInfo->loadList _desc.resListRN
+ resInfo->horizon _desc.endSlope
+ resInfo->nearFigureLimit _desc.beginSlope
+ resInfo->scriptModule _desc.scriptModuleNumber
+ resInfo->entryScript _desc.sceneScriptEntrypointNumber
+ resInfo->preScript _desc.startScriptEntrypointNumber
+ resInfo->backgroundMusic _desc.musicRN
+ thisScene->ID currentSceneNumber()
+
+Interp.c
+========
+ dispatchThreads() executeThreads()
+ runThread() SThreadCompleteThread()
+ moduleList _scriptLUT
+ ModuleEntry->codeID _scriptLUT->script_rn
+ ModuleEntry->strID _scriptLUT->diag_list_rn
+ ModuleEntry->vtableID _scriptLUT->voice_lut_rn
+ threadBase.theAction threadVars[kVarAction]
+ threadBase.theObject threadVars[kVarObject]
+ threadBase.withObject threadVars[kVarWithObject]
+ threadBase.theActor threadVars[kVarActor]
+
+Actor.h
+=======
+ GOF_PROTAGONIST kProtagonist
+ GOF_FOLLOWER kFollower
+ GOF_CYCLE kCycle
+ GOF_FASTER kFaster
+ GOF_FASTEST kFastest
+ GOF_EXTENDED kExtended
+
+Actor.c
+=======
+ abortAllSpeeches() abortAllSpeeches()
+
+Main.c
+======
+ sceneIndexTable _scene->getSceneLUT()
+
+Main.h
+======
+BRIGHT_WHITE kITEColorBrightWhite
+WHITE_02 kITEColorWhite
+GREY_0A kITEColorGrey
+DK_GREY_0B kITEColorDarkGrey
+PITCH_BLACK kITEColorBlack
+RED_65 kITEColorRed
+BLUE_93 kITEColorBlue
+GREEB_BA kITEColorGreen
+
+Note that ScummVM's kITEColorLightGrey does not have any corresponding
+constant in the original SAGA engine. We use it for the ITE mouse cursor. See
+PtrData[] in Main.c and setCursor() in gfx.cpp
+
+Tile.h
+======
+ isoTile.height ISOTILE_ENTRY.tile_h
+ isoTile.attributes ISOTILE_ENTRY.mask_rule
+ isoTile.offset ISOTILE_ENTRY.tile_offset
+ isoTile.terrain_mask ISOTILE_ENTRY.terrain_mask
+ isoTile.fgd_bgd_attr ISOTILE_ENTRY.mask
+
+ tilePlatform.metaTile ISO_METATILE_ENTRY.mtile_n
+ tilePlatform.height ISO_METATILE_ENTRY.height
+ tilePlatform.highestPixel ISO_METATILE_ENTRY.highest_pixel
+ tilePlatform.vBits ISO_METATILE_ENTRY.v_bits
+ tilePlatform.uBits ISO_METATILE_ENTRY.u_bits
+
+Resource.h
+==========
+ PicHeader.width IMAGE_HEADER.width
+ PicHeader.height IMAGE_HEADER.height
+
+
+Process.c
+=========
+ mainPanelMode Interface::_inMainMode
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
new file mode 100644
index 0000000000..7e6696680f
--- /dev/null
+++ b/engines/scumm/actor.cpp
@@ -0,0 +1,2253 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/actor.h"
+#include "scumm/akos.h"
+#include "scumm/boxes.h"
+#include "scumm/charset.h"
+#include "scumm/costume.h"
+#include "scumm/intern.h"
+#include "scumm/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/saveload.h"
+#include "scumm/sound.h"
+#include "scumm/sprite_he.h"
+#include "scumm/usage_bits.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+byte Actor::kInvalidBox = 0;
+ScummEngine *Actor::_vm = 0;
+
+void Actor::initActorClass(ScummEngine *scumm) {
+ _vm = scumm;
+ if (_vm->_features & GF_SMALL_HEADER) {
+ kInvalidBox = 255;
+ }
+}
+
+Actor::Actor() {
+ assert(_vm != 0);
+ _number = 0;
+
+ initActor(-1);
+}
+
+void Actor::initActor(int mode) {
+ if (mode == -1) {
+ _offsX = _offsY = 0;
+ _top = _bottom = 0;
+ _needRedraw = _needBgReset = _costumeNeedsInit = _visible = false;
+ _flip = false;
+ _speedx = 8;
+ _speedy = 2;
+ _frame = 0;
+ _walkbox = 0;
+ _animProgress = 0;
+ _heSkipLimbs = false;
+ _drawToBackBuf = false;
+ memset(_animVariable, 0, sizeof(_animVariable));
+ memset(_palette, 0, sizeof(_palette));
+ memset(_sound, 0, sizeof(_sound));
+ memset(&_cost, 0, sizeof(CostumeData));
+ memset(&_walkdata, 0, sizeof(ActorWalkData));
+ _walkdata.point3.x = 32000;
+ _walkScript = 0;
+ memset(_heTalkQueue, 0, sizeof(_heTalkQueue));
+
+ mode = 1;
+ }
+
+ if (mode == 1) {
+ _costume = 0;
+ _room = 0;
+ _pos.x = 0;
+ _pos.y = 0;
+ _facing = 180;
+ _heCondMask = 1;
+ _heNoTalkAnimation = 0;
+ if (_vm->_version >= 7)
+ _visible = false;
+ _heSkipLimbs = false;
+ } else if (mode == 2) {
+ _facing = 180;
+ _heCondMask = 1;
+ _heSkipLimbs = false;
+ }
+ _elevation = 0;
+ _width = 24;
+ _talkColor = 15;
+ _talkPosX = 0;
+ _talkPosY = -80;
+ _boxscale = _scaley = _scalex = 0xFF;
+ _charset = 0;
+ memset(_sound, 0, sizeof(_sound));
+ _targetFacing = _facing;
+
+ stopActorMoving();
+
+ _heXmapNum = 0;
+ _shadowMode = 0;
+ _layer = 0;
+
+ setActorWalkSpeed(8, 2);
+ _animSpeed = 0;
+ if (_vm->_version >= 6)
+ _animProgress = 0;
+
+ _ignoreBoxes = false;
+ _forceClip = (_vm->_version >= 7) ? 100 : 0;
+ _ignoreTurns = false;
+
+ if (_vm->_heversion >= 61)
+ _flip = 0;
+
+ _talkFrequency = 256;
+ _talkPan = 64;
+ _talkVolume = 127;
+
+ if (_vm->_version <= 2) {
+ _initFrame = 2;
+ _walkFrame = 0;
+ _standFrame = 1;
+ _talkStartFrame = 5;
+ _talkStopFrame = 4;
+ } else {
+ _initFrame = 1;
+ _walkFrame = 2;
+ _standFrame = 3;
+ _talkStartFrame = 4;
+ _talkStopFrame = 5;
+ }
+
+ _heTalking = false;
+ _walkScript = 0;
+ _talkScript = 0;
+
+ _clipOverride = _vm->_actorClipOverride;
+
+ _auxBlock.reset();
+ _hePaletteNum = 0;
+
+ _vm->_classData[_number] = (_vm->_version >= 7) ? _vm->_classData[0] : 0;
+}
+
+void Actor::stopActorMoving() {
+ if (_walkScript)
+ _vm->stopScript(_walkScript);
+ _moving = 0;
+}
+
+void Actor::setActorWalkSpeed(uint newSpeedX, uint newSpeedY) {
+ if (newSpeedX == _speedx && newSpeedY == _speedy)
+ return;
+
+ _speedx = newSpeedX;
+ _speedy = newSpeedY;
+
+ if (_moving) {
+ calcMovementFactor(_walkdata.next);
+ }
+}
+
+int ScummEngine::getAngleFromPos(int x, int y) const {
+ if (_gameId == GID_DIG || _gameId == GID_CMI) {
+ double temp = atan2((double)x, (double)-y);
+ return normalizeAngle((int)(temp * 180 / PI));
+ } else {
+ if (ABS(y) * 2 < ABS(x)) {
+ if (x > 0)
+ return 90;
+ return 270;
+ } else {
+ if (y > 0)
+ return 180;
+ return 0;
+ }
+ }
+}
+
+int Actor::calcMovementFactor(const Common::Point& next) {
+ Common::Point _actorPos(_pos);
+ int diffX, diffY;
+ int32 deltaXFactor, deltaYFactor;
+
+ if (_actorPos == next)
+ return 0;
+
+ diffX = next.x - _actorPos.x;
+ diffY = next.y - _actorPos.y;
+ deltaYFactor = _speedy << 16;
+
+ if (diffY < 0)
+ deltaYFactor = -deltaYFactor;
+
+ deltaXFactor = deltaYFactor * diffX;
+ if (diffY != 0) {
+ deltaXFactor /= diffY;
+ } else {
+ deltaYFactor = 0;
+ }
+
+ if ((uint) ABS((int)(deltaXFactor >> 16)) > _speedx) {
+ deltaXFactor = _speedx << 16;
+ if (diffX < 0)
+ deltaXFactor = -deltaXFactor;
+
+ deltaYFactor = deltaXFactor * diffY;
+ if (diffX != 0) {
+ deltaYFactor /= diffX;
+ } else {
+ deltaXFactor = 0;
+ }
+ }
+
+ _walkdata.cur = _actorPos;
+ _walkdata.next = next;
+ _walkdata.deltaXFactor = deltaXFactor;
+ _walkdata.deltaYFactor = deltaYFactor;
+ _walkdata.xfrac = 0;
+ _walkdata.yfrac = 0;
+
+ _targetFacing = _vm->getAngleFromPos(deltaXFactor, deltaYFactor);
+
+ return actorWalkStep();
+}
+
+int Actor::remapDirection(int dir, bool is_walking) {
+ int specdir;
+ byte flags;
+ bool flipX;
+ bool flipY;
+
+ // FIXME - It seems that at least in The Dig the original code does
+ // check _ignoreBoxes here. However, it breaks some animations in Loom,
+ // causing Bobbin to face towards the camera instead of away from it
+ // in some places: After the tree has been destroyed by lightning, and
+ // when entering the dark tunnels beyond the dragon's lair at the very
+ // least. Possibly other places as well.
+ //
+ // The Dig also checks if the actor is in the current room, but that's
+ // not necessary here because we never call the function unless the
+ // actor is in the current room anyway.
+
+ if (!_ignoreBoxes || _vm->_gameId == GID_LOOM) {
+ specdir = _vm->_extraBoxFlags[_walkbox];
+ if (specdir) {
+ if (specdir & 0x8000) {
+ dir = specdir & 0x3FFF;
+ } else {
+ specdir = specdir & 0x3FFF;
+ if (specdir - 90 < dir && dir < specdir + 90)
+ dir = specdir;
+ else
+ dir = specdir + 180;
+ }
+ }
+
+ flags = _vm->getBoxFlags(_walkbox);
+
+ flipX = (_walkdata.deltaXFactor > 0);
+ flipY = (_walkdata.deltaYFactor > 0);
+
+ // Check for X-Flip
+ if ((flags & kBoxXFlip) || isInClass(kObjectClassXFlip)) {
+ dir = 360 - dir;
+ flipX = !flipX;
+ }
+ // Check for Y-Flip
+ if ((flags & kBoxYFlip) || isInClass(kObjectClassYFlip)) {
+ dir = 180 - dir;
+ flipY = !flipY;
+ }
+
+ switch (flags & 7) {
+ case 1:
+ if (_vm->_version >= 7) {
+ if (dir < 180)
+ return 90;
+ else
+ return 270;
+ } else {
+ if (is_walking) // Actor is walking
+ return flipX ? 90 : 270;
+ else // Actor is standing/turning
+ return (dir == 90) ? 90 : 270;
+ }
+ case 2:
+ if (_vm->_version >= 7) {
+ if (dir > 90 && dir < 270)
+ return 180;
+ else
+ return 0;
+ } else {
+ if (is_walking) // Actor is walking
+ return flipY ? 180 : 0;
+ else // Actor is standing/turning
+ return (dir == 0) ? 0 : 180;
+ }
+ case 3:
+ return 270;
+ case 4:
+ return 90;
+ case 5:
+ return 0;
+ case 6:
+ return 180;
+ }
+ }
+ // OR 1024 in to signal direction interpolation should be done
+ return normalizeAngle(dir) | 1024;
+}
+
+int Actor::updateActorDirection(bool is_walking) {
+ int from;
+ bool dirType = false;
+ int dir;
+ bool shouldInterpolate;
+
+ if ((_vm->_version == 6) && _ignoreTurns)
+ return _facing;
+
+ dirType = (_vm->_version >= 7) ? _vm->_costumeLoader->hasManyDirections(_costume) : false;
+
+ from = toSimpleDir(dirType, _facing);
+ dir = remapDirection(_targetFacing, is_walking);
+
+ if (_vm->_version >= 7)
+ // Direction interpolation interfers with walk scripts in Dig; they perform
+ // (much better) interpolation themselves.
+ shouldInterpolate = false;
+ else
+ shouldInterpolate = (dir & 1024) ? true : false;
+ dir &= 1023;
+
+ if (shouldInterpolate) {
+ int to = toSimpleDir(dirType, dir);
+ int num = dirType ? 8 : 4;
+
+ // Turn left or right, depending on which is shorter.
+ int diff = to - from;
+ if (ABS(diff) > (num >> 1))
+ diff = -diff;
+
+ if (diff > 0) {
+ to = from + 1;
+ } else if (diff < 0){
+ to = from - 1;
+ }
+
+ dir = fromSimpleDir(dirType, (to + num) % num);
+ }
+
+ return dir;
+}
+
+void Actor::setBox(int box) {
+ _walkbox = box;
+ setupActorScale();
+}
+
+int Actor::actorWalkStep() {
+ int tmpX, tmpY;
+ Common::Point _actorPos;
+ int distX, distY;
+ int nextFacing;
+
+ _needRedraw = true;
+
+ nextFacing = updateActorDirection(true);
+ if (!(_moving & MF_IN_LEG) || _facing != nextFacing) {
+ if (_walkFrame != _frame || _facing != nextFacing) {
+ startWalkAnim(1, nextFacing);
+ }
+ _moving |= MF_IN_LEG;
+ }
+
+ _actorPos = _pos;
+
+ if (_walkbox != _walkdata.curbox && _vm->checkXYInBoxBounds(_walkdata.curbox, _actorPos.x, _actorPos.y)) {
+ setBox(_walkdata.curbox);
+ }
+
+ distX = ABS(_walkdata.next.x - _walkdata.cur.x);
+ distY = ABS(_walkdata.next.y - _walkdata.cur.y);
+
+ if (ABS(_actorPos.x - _walkdata.cur.x) >= distX && ABS(_actorPos.y - _walkdata.cur.y) >= distY) {
+ _moving &= ~MF_IN_LEG;
+ return 0;
+ }
+
+ tmpX = (_actorPos.x << 16) + _walkdata.xfrac + (_walkdata.deltaXFactor >> 8) * _scalex;
+ _walkdata.xfrac = (uint16)tmpX;
+ _actorPos.x = (tmpX >> 16);
+
+ tmpY = (_actorPos.y << 16) + _walkdata.yfrac + (_walkdata.deltaYFactor >> 8) * _scaley;
+ _walkdata.yfrac = (uint16)tmpY;
+ _actorPos.y = (tmpY >> 16);
+
+ if (ABS(_actorPos.x - _walkdata.cur.x) > distX) {
+ _actorPos.x = _walkdata.next.x;
+ }
+
+ if (ABS(_actorPos.y - _walkdata.cur.y) > distY) {
+ _actorPos.y = _walkdata.next.y;
+ }
+
+ _pos = _actorPos;
+ return 1;
+}
+
+
+void Actor::setupActorScale() {
+
+ if (_vm->_features & GF_NO_SCALING) {
+ _scalex = 0xFF;
+ _scaley = 0xFF;
+ return;
+ }
+
+ if (_ignoreBoxes)
+ return;
+
+ // For some boxes, we ignore the scaling and use whatever values the
+ // scripts set. This is used e.g. in the Mystery Vortex in Sam&Max.
+ // Older games used the flag 0x20 differently, though.
+ if (_vm->_gameId == GID_SAMNMAX && (_vm->getBoxFlags(_walkbox) & kBoxIgnoreScale))
+ return;
+
+ _boxscale = _vm->getBoxScale(_walkbox);
+
+ uint16 scale = _vm->getScale(_walkbox, _pos.x, _pos.y);
+ assert(scale <= 0xFF);
+
+ _scalex = _scaley = (byte)scale;
+}
+
+void Actor::startAnimActor(int f) {
+ if (_vm->_version >= 7 && !((_vm->_gameId == GID_FT) && (_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))) {
+ switch (f) {
+ case 1001:
+ f = _initFrame;
+ break;
+ case 1002:
+ f = _walkFrame;
+ break;
+ case 1003:
+ f = _standFrame;
+ break;
+ case 1004:
+ f = _talkStartFrame;
+ break;
+ case 1005:
+ f = _talkStopFrame;
+ break;
+ }
+
+ if (_costume != 0) {
+ _animProgress = 0;
+ _needRedraw = true;
+ if (f == _initFrame)
+ _cost.reset();
+ _vm->_costumeLoader->costumeDecodeData(this, f, (uint) - 1);
+ _frame = f;
+ }
+ } else {
+ switch (f) {
+ case 0x38:
+ f = _initFrame;
+ break;
+ case 0x39:
+ f = _walkFrame;
+ break;
+ case 0x3A:
+ f = _standFrame;
+ break;
+ case 0x3B:
+ f = _talkStartFrame;
+ break;
+ case 0x3C:
+ f = _talkStopFrame;
+ break;
+ }
+
+ assert(f != 0x3E);
+
+ if (isInCurrentRoom() && _costume != 0) {
+ _animProgress = 0;
+ _cost.animCounter = 0;
+ _needRedraw = true;
+ // V1 - V2 games don't seem to need a _cost.reset() at this point.
+ // Causes Zak to lose his body in several scenes, see bug #771508
+ if (_vm->_version >= 3 && f == _initFrame) {
+ _cost.reset();
+ _auxBlock.reset();
+ }
+ _vm->_costumeLoader->costumeDecodeData(this, f, (uint) - 1);
+ _frame = f;
+ }
+ }
+}
+
+void Actor::animateActor(int anim) {
+ int cmd, dir;
+
+ if (_vm->_version >= 7 && !((_vm->_gameId == GID_FT) && (_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))) {
+
+ if (anim == 0xFF)
+ anim = 2000;
+
+ cmd = anim / 1000;
+ dir = anim % 1000;
+
+ } else {
+
+ cmd = anim / 4;
+ dir = oldDirToNewDir(anim % 4);
+
+ // Convert into old cmd code
+ cmd = 0x3F - cmd + 2;
+
+ }
+
+ switch (cmd) {
+ case 2: // stop walking
+ startAnimActor(_standFrame);
+ stopActorMoving();
+ break;
+ case 3: // change direction immediatly
+ _moving &= ~MF_TURN;
+ setDirection(dir);
+ break;
+ case 4: // turn to new direction
+ turnToDirection(dir);
+ break;
+ default:
+ if (_vm->_version <= 2)
+ startAnimActor(anim / 4);
+ else
+ startAnimActor(anim);
+ }
+}
+
+void Actor::setDirection(int direction) {
+ uint aMask;
+ int i;
+ uint16 vald;
+
+ // Do nothing if actor is already facing in the given direction
+ if (_facing == direction)
+ return;
+
+ // Normalize the angle
+ _facing = normalizeAngle(direction);
+
+ // If there is no costume set for this actor, we are finished
+ if (_costume == 0)
+ return;
+
+ // Update the costume for the new direction (and mark the actor for redraw)
+ aMask = 0x8000;
+ for (i = 0; i < 16; i++, aMask >>= 1) {
+ vald = _cost.frame[i];
+ if (vald == 0xFFFF)
+ continue;
+ _vm->_costumeLoader->costumeDecodeData(this, vald, (_vm->_version <= 2) ? 0xFFFF : aMask);
+ }
+
+ _needRedraw = true;
+}
+
+void Actor::drawActorToBackBuf(int x, int y) {
+ int curTop = _top;
+ int curBottom = _bottom;
+
+ _pos.x = x;
+ _pos.y = y;
+
+ _drawToBackBuf = true;
+ _needRedraw = true;
+ drawActorCostume();
+
+ _drawToBackBuf = false;
+ _needRedraw = true;
+ drawActorCostume();
+ _needRedraw = false;
+
+ if (_top > curTop)
+ _top = curTop;
+ if (_bottom < curBottom)
+ _bottom = curBottom;
+}
+
+
+void Actor::putActor(int dstX, int dstY, byte newRoom) {
+ if (_visible && _vm->_currentRoom != newRoom && _vm->getTalkingActor() == _number) {
+ _vm->stopTalk();
+ }
+
+ // WORKAROUND: The green transparency of the tank in the Hall of Oddities is
+ // is positioned one pixel too far to the left. This appears to be a
+ // bug in the original game as well.
+ if (_vm->_gameId == GID_SAMNMAX && newRoom == 16 && _number == 5 && dstX == 235 && dstY == 236)
+ dstX++;
+
+ _pos.x = dstX;
+ _pos.y = dstY;
+ _room = newRoom;
+ _needRedraw = true;
+
+ if (_vm->VAR(_vm->VAR_EGO) == _number) {
+ _vm->_egoPositioned = true;
+ }
+
+ if (_visible) {
+ if (isInCurrentRoom()) {
+ if (_moving) {
+ stopActorMoving();
+ startAnimActor(_standFrame);
+ }
+ adjustActorPos();
+ } else {
+#ifndef DISABLE_HE
+ if (_vm->_heversion >= 71)
+ ((ScummEngine_v71he *)_vm)->queueAuxBlock(this);
+#endif
+ hideActor();
+ }
+ } else {
+ if (isInCurrentRoom())
+ showActor();
+ }
+}
+
+int Actor::getActorXYPos(int &xPos, int &yPos) const {
+ if (!isInCurrentRoom())
+ return -1;
+
+ xPos = _pos.x;
+ yPos = _pos.y;
+ return 0;
+}
+
+AdjustBoxResult Actor::adjustXYToBeInBox(int dstX, int dstY) {
+ const uint thresholdTable[] = { 30, 80, 0 };
+ AdjustBoxResult abr;
+ int16 tmpX, tmpY;
+ int tmpDist, bestDist, threshold, numBoxes;
+ byte flags, bestBox;
+ int box;
+ const int firstValidBox = (_vm->_features & GF_SMALL_HEADER) ? 0 : 1;
+
+ abr.x = dstX;
+ abr.y = dstY;
+ abr.box = kInvalidBox;
+
+ if (_ignoreBoxes)
+ return abr;
+
+ for (int tIdx = 0; tIdx < ARRAYSIZE(thresholdTable); tIdx++) {
+ threshold = thresholdTable[tIdx];
+
+ numBoxes = _vm->getNumBoxes() - 1;
+ if (numBoxes < firstValidBox)
+ return abr;
+
+ bestDist = (_vm->_version >= 7) ? 0x7FFFFFFF : 0xFFFF;
+ if (_vm->_version <= 2)
+ bestDist *= 8*2; // Adjust for the fact that we multiply x by 8 and y by 2
+ bestBox = kInvalidBox;
+
+ // We iterate (backwards) over all boxes, searching the one closest
+ // to the desired coordinates.
+ for (box = numBoxes; box >= firstValidBox; box--) {
+ flags = _vm->getBoxFlags(box);
+
+ // Skip over invisible boxes
+ if (flags & kBoxInvisible && !(flags & kBoxPlayerOnly && !isPlayer()))
+ continue;
+
+ // For increased performance, we perform a quick test if
+ // the coordinates can even be within a distance of 'threshold'
+ // pixels of the box.
+ if (threshold > 0 && _vm->inBoxQuickReject(box, dstX, dstY, threshold))
+ continue;
+
+ // Check if the point is contained in the box. If it is,
+ // we don't have to search anymore.
+ if (_vm->checkXYInBoxBounds(box, dstX, dstY)) {
+ abr.x = dstX;
+ abr.y = dstY;
+ abr.box = box;
+ return abr;
+ }
+
+ // Find the point in the box which is closest to our point.
+ tmpDist = _vm->getClosestPtOnBox(box, dstX, dstY, tmpX, tmpY);
+
+ // Check if the box is closer than the previous boxes.
+ if (tmpDist < bestDist) {
+ abr.x = tmpX;
+ abr.y = tmpY;
+
+ if (tmpDist == 0) {
+ abr.box = box;
+ return abr;
+ }
+ bestDist = tmpDist;
+ bestBox = box;
+ }
+ }
+
+ // If the closest ('best') box we found is within the threshold, or if
+ // we are on the last run (i.e. threshold == 0), return that box.
+ if (threshold == 0 || threshold * threshold >= bestDist) {
+ abr.box = bestBox;
+ return abr;
+ }
+ }
+
+ return abr;
+}
+
+void Actor::adjustActorPos() {
+ AdjustBoxResult abr;
+
+ abr = adjustXYToBeInBox(_pos.x, _pos.y);
+
+ _pos.x = abr.x;
+ _pos.y = abr.y;
+ _walkdata.destbox = abr.box;
+
+ setBox(abr.box);
+
+ _walkdata.dest.x = -1;
+
+ stopActorMoving();
+ _cost.soundCounter = 0;
+
+ if (_walkbox != kInvalidBox) {
+ byte flags = _vm->getBoxFlags(_walkbox);
+ if (flags & 7) {
+ turnToDirection(_facing);
+ }
+ }
+}
+
+void Actor::faceToObject(int obj) {
+ int x2, y2, dir;
+
+ if (!isInCurrentRoom())
+ return;
+
+ if (_vm->getObjectOrActorXY(obj, x2, y2) == -1)
+ return;
+
+ dir = (x2 > _pos.x) ? 90 : 270;
+ turnToDirection(dir);
+}
+
+void Actor::turnToDirection(int newdir) {
+ if (newdir == -1 || _ignoreTurns)
+ return;
+
+ _moving &= ~MF_TURN;
+
+ if (newdir != _facing) {
+ if (_vm->_version <= 3)
+ _moving = MF_TURN;
+ else
+ _moving |= MF_TURN;
+ _targetFacing = newdir;
+ }
+}
+
+void Actor::hideActor() {
+ if (!_visible)
+ return;
+
+ if (_moving) {
+ stopActorMoving();
+ startAnimActor(_standFrame);
+ }
+ _visible = false;
+ _cost.soundCounter = 0;
+ _needRedraw = false;
+ _needBgReset = true;
+ _auxBlock.reset();
+}
+
+void Actor::showActor() {
+ if (_vm->_currentRoom == 0 || _visible)
+ return;
+
+ adjustActorPos();
+
+ _vm->ensureResourceLoaded(rtCostume, _costume);
+
+ if (_costumeNeedsInit) {
+ startAnimActor(_initFrame);
+ if (_vm->_version <= 2) {
+ startAnimActor(_standFrame);
+ startAnimActor(_talkStopFrame);
+ }
+ _costumeNeedsInit = false;
+ }
+
+ // FIXME: Evil hack to work around bug #770717
+ if (!_moving && _vm->_version <= 2)
+ startAnimActor(_standFrame);
+
+ stopActorMoving();
+ _visible = true;
+ _needRedraw = true;
+}
+
+// V1 Maniac doesn't have a ScummVar for VAR_TALK_ACTOR, and just uses
+// an internal variable. Emulate this to prevent overwriting script vars...
+// Maniac NES (V1), however, DOES have a ScummVar for VAR_TALK_ACTOR
+int ScummEngine::getTalkingActor() {
+ if (_gameId == GID_MANIAC && _version == 1 && !(_platform == Common::kPlatformNES))
+ return _V1TalkingActor;
+ else
+ return VAR(VAR_TALK_ACTOR);
+}
+
+void ScummEngine::setTalkingActor(int value) {
+ if (_gameId == GID_MANIAC && _version == 1 && !(_platform == Common::kPlatformNES))
+ _V1TalkingActor = value;
+ else
+ VAR(VAR_TALK_ACTOR) = value;
+}
+
+void ScummEngine::putActors() {
+ Actor *a;
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ a = &_actors[i];
+ if (a && a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ }
+}
+
+static const int c64MMActorTalkColor[25] = {
+ 1, 7, 2, 14, 8, 15, 3, 7, 7, 15, 1, 13, 1, 4, 5, 5, 4, 3, 1, 5, 1, 1, 1, 1, 7
+};
+static const int v1MMActorTalkColor[25] = {
+ 1, 7, 2, 14, 8, 1, 3, 7, 7, 12, 1, 13, 1, 4, 5, 5, 4, 3, 1, 5, 1, 1, 1, 7, 7
+};
+
+void ScummEngine::setupV1ActorTalkColor() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_platform == Common::kPlatformC64) {
+ _actors[i]._talkColor = c64MMActorTalkColor[i];
+ } else {
+ _actors[i]._talkColor = v1MMActorTalkColor[i];
+ }
+ }
+}
+
+void ScummEngine::showActors() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_actors[i].isInCurrentRoom())
+ _actors[i].showActor();
+ }
+}
+
+void ScummEngine::walkActors() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_actors[i].isInCurrentRoom())
+ if (_version <= 3)
+ _actors[i].walkActorOld();
+ else
+ _actors[i].walkActor();
+ }
+}
+
+/* Used in Scumm v5 only. Play sounds associated with actors */
+void ScummEngine::playActorSounds() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_actors[i]._cost.soundCounter && _actors[i].isInCurrentRoom() && _actors[i]._sound) {
+ _currentScript = 0xFF;
+ _sound->addSoundToQueue(_actors[i]._sound[0]);
+ for (i = 1; i < _numActors; i++) {
+ _actors[i]._cost.soundCounter = 0;
+ }
+ return;
+ }
+ }
+}
+
+bool ScummEngine::isValidActor(int id) const {
+ return id >= 0 && id < _numActors && _actors[id]._number == id;
+}
+
+Actor *ScummEngine::derefActor(int id, const char *errmsg) const {
+ if (id == 0)
+ debugC(DEBUG_ACTORS, "derefActor(0, \"%s\") in script %d, opcode 0x%x",
+ errmsg, vm.slot[_currentScript].number, _opcode);
+
+ if (!isValidActor(id)) {
+ if (errmsg)
+ error("Invalid actor %d in %s", id, errmsg);
+ else
+ error("Invalid actor %d", id);
+ }
+ return &_actors[id];
+}
+
+Actor *ScummEngine::derefActorSafe(int id, const char *errmsg) const {
+ if (id == 0)
+ debugC(DEBUG_ACTORS, "derefActorSafe(0, \"%s\") in script %d, opcode 0x%x",
+ errmsg, vm.slot[_currentScript].number, _opcode);
+
+ if (!isValidActor(id)) {
+ debugC(DEBUG_ACTORS, "Invalid actor %d in %s (script %d, opcode 0x%x)",
+ id, errmsg, vm.slot[_currentScript].number, _opcode);
+ return NULL;
+ }
+ return &_actors[id];
+}
+
+void ScummEngine::processActors() {
+ int numactors = 0;
+
+ // Make a list of all actors in this room
+ for (int i = 1; i < _numActors; i++) {
+ if (_version == 8 && _actors[i]._layer < 0)
+ continue;
+ if (_actors[i].isInCurrentRoom()) {
+ _sortedActors[numactors++] = &_actors[i];
+ }
+ }
+ if (!numactors) {
+ return;
+ }
+
+ // Sort actors by position before drawing them (to ensure that actors
+ // in front are drawn after those "behind" them).
+ //
+ // Note: This algorithm works exactly the way the original engine did.
+ // Please resist any urge to 'optimize' this. Many of the games rely on
+ // the quirks of this particular sorting algorithm, and since we are
+ // dealing with far less than 100 objects being sorted here, any
+ // 'optimization' wouldn't yield a useful gain anyway.
+ //
+ // In particular, changing this loop caused a number of bugs in the
+ // past, including bugs #758167, #775097, and #1093867.
+ //
+ // Note that Sam & Max uses a stable sorting method. Older games don't
+ // and, according to cyx, neither do newer ones. At least not FT and
+ // COMI. See bug #1220168 for more details.
+
+ if (_gameId == GID_SAMNMAX) {
+ for (int j = 0; j < numactors; ++j) {
+ for (int i = 0; i < numactors; ++i) {
+ int sc_actor1 = _sortedActors[j]->_pos.y;
+ int sc_actor2 = _sortedActors[i]->_pos.y;
+ if (sc_actor1 == sc_actor2) {
+ sc_actor1 += _sortedActors[j]->_number;
+ sc_actor2 += _sortedActors[i]->_number;
+ }
+ if (sc_actor1 < sc_actor2) {
+ SWAP(_sortedActors[i], _sortedActors[j]);
+ }
+ }
+ }
+ } else {
+ for (int j = 0; j < numactors; ++j) {
+ for (int i = 0; i < numactors; ++i) {
+ int sc_actor1 = _sortedActors[j]->_pos.y - _sortedActors[j]->_layer * 2000;
+ int sc_actor2 = _sortedActors[i]->_pos.y - _sortedActors[i]->_layer * 2000;
+ if (sc_actor1 < sc_actor2) {
+ SWAP(_sortedActors[i], _sortedActors[j]);
+ }
+ }
+ }
+ }
+
+ // Finally draw the now sorted actors
+ Actor** end = _sortedActors + numactors;
+ for (Actor** ac = _sortedActors; ac != end; ++ac) {
+ Actor* a = *ac;
+ // Draw and animate the actors, except those w/o a costume.
+ // Note: We could 'optimize' this a little bit by only putting
+ // actors with a costume into the _sortedActors array in the
+ // first place. However, that would mess up the sorting, and
+ // would hence cause regressions. See also the other big
+ // comment further up in this method for some details.
+ if (a->_costume) {
+ a->drawActorCostume();
+ a->animateCostume();
+ }
+ }
+
+ if (_features & GF_NEW_COSTUMES)
+ akos_processQueue();
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v71he::processActors() {
+ preProcessAuxQueue();
+
+ if (!_skipProcessActors)
+ ScummEngine::processActors();
+
+ _fullRedraw = false;
+
+ postProcessAuxQueue();
+}
+
+void ScummEngine_v90he::processActors() {
+ preProcessAuxQueue();
+
+ _sprite->setRedrawFlags(false);
+ _sprite->processImages(true);
+
+ if (!_skipProcessActors)
+ ScummEngine::processActors();
+
+ _fullRedraw = false;
+
+ postProcessAuxQueue();
+
+ _sprite->setRedrawFlags(true);
+ _sprite->processImages(false);
+}
+#endif
+
+// Used in Scumm v8, to allow the verb coin to be drawn over the inventory
+// chest. I'm assuming that draw order won't matter here.
+void ScummEngine::processUpperActors() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_actors[i].isInCurrentRoom() && _actors[i]._costume && _actors[i]._layer < 0) {
+ CHECK_HEAP
+ _actors[i].drawActorCostume();
+ CHECK_HEAP
+ _actors[i].animateCostume();
+ }
+ }
+}
+
+void Actor::drawActorCostume(bool hitTestMode) {
+ if (_costume == 0)
+ return;
+
+ if (!hitTestMode) {
+ if (!_needRedraw)
+ return;
+
+ _needRedraw = false;
+ }
+
+ setupActorScale();
+
+ BaseCostumeRenderer* bcr = _vm->_costumeRenderer;
+
+ bcr->_actorID = _number;
+
+ bcr->_actorX = _pos.x + _offsX - _vm->virtscr[0].xstart;
+ bcr->_actorY = _pos.y + _offsY - _elevation;
+
+ if (_vm->_platform == Common::kPlatformNES) {
+ // In the NES version, when the actor is facing right,
+ // we need to shift it 8 pixels to the left
+ if (_facing == 90)
+ bcr->_actorX -= 8;
+ } else if (_vm->_version <= 2) {
+ // HACK: We have to adjust the x position by one strip (8 pixels) in
+ // V2 games. However, it is not quite clear to me why. And to fully
+ // match the original, it seems we have to offset by 2 strips if the
+ // actor is facing left (270 degree).
+ // V1 games are once again slightly different, here we only have
+ // to adjust the 270 degree case...
+ if (_facing == 270)
+ bcr->_actorX += 16;
+ else if (_vm->_version == 2)
+ bcr->_actorX += 8;
+ }
+
+ bcr->_clipOverride = _clipOverride;
+
+ if (_vm->_version == 4 && _boxscale & 0x8000) {
+ bcr->_scaleX = bcr->_scaleY = _vm->getScaleFromSlot((_boxscale & 0x7fff) + 1, _pos.x, _pos.y);
+ } else {
+ bcr->_scaleX = _scalex;
+ bcr->_scaleY = _scaley;
+ }
+
+ bcr->_shadow_mode = _shadowMode;
+ if (_vm->_version >= 5 && _vm->_heversion == 0) {
+ bcr->_shadow_table = _vm->_shadowPalette;
+ } else if (_vm->_heversion == 70) {
+ bcr->_shadow_table = _vm->_HEV7ActorPalette;
+ }
+
+ bcr->setCostume(_costume, _heXmapNum);
+ bcr->setPalette(_palette);
+ bcr->setFacing(this);
+
+ if (_vm->_version >= 7) {
+
+ bcr->_zbuf = _forceClip;
+ if (bcr->_zbuf == 100) {
+ bcr->_zbuf = _vm->getMaskFromBox(_walkbox);
+ if (bcr->_zbuf > _vm->gdi._numZBuffer-1)
+ bcr->_zbuf = _vm->gdi._numZBuffer-1;
+ }
+
+ } else {
+ if (_forceClip)
+ bcr->_zbuf = _forceClip;
+ else if (isInClass(kObjectClassNeverClip))
+ bcr->_zbuf = 0;
+ else {
+ bcr->_zbuf = _vm->getMaskFromBox(_walkbox);
+ if (bcr->_zbuf > _vm->gdi._numZBuffer-1)
+ bcr->_zbuf = _vm->gdi._numZBuffer-1;
+ }
+
+ }
+
+ bcr->_draw_top = 0x7fffffff;
+ bcr->_draw_bottom = 0;
+
+ bcr->_skipLimbs = (_heSkipLimbs != 0);
+ bcr->_paletteNum = _hePaletteNum;
+
+ if (_vm->_heversion >= 80 && _heNoTalkAnimation == 0 && _animProgress == 0) {
+ if (_vm->getTalkingActor() == _number && !_vm->_string[0].no_talk_anim) {
+ int talkState = 0;
+
+ if (_vm->_sound->isSoundCodeUsed(1))
+ talkState = _vm->_sound->getSoundVar(1, 19);
+ if (talkState == 0)
+ talkState = _vm->_rnd.getRandomNumberRng(1, 10);
+
+ checkRange(13, 1, talkState, "Talk state %d out of range");
+ setTalkCondition(talkState);
+ } else {
+ setTalkCondition(1);
+ }
+ }
+ _heNoTalkAnimation = 0;
+
+ // If the actor is partially hidden, redraw it next frame.
+ // Only done for pre-AKOS, though.
+ if (bcr->drawCostume(_vm->virtscr[0], _vm->gdi._numStrips, this, _drawToBackBuf) & 1) {
+ _needRedraw = (_vm->_version <= 6);
+ }
+
+ if (!hitTestMode) {
+ // Record the vertical extent of the drawn actor
+ _top = bcr->_draw_top;
+ _bottom = bcr->_draw_bottom;
+ }
+}
+
+#ifndef DISABLE_SCUMM_7_8
+bool Actor::actorHitTest(int x, int y) {
+ AkosRenderer *ar = (AkosRenderer *)_vm->_costumeRenderer;
+
+ ar->_actorHitX = x;
+ ar->_actorHitY = y;
+ ar->_actorHitMode = true;
+ ar->_actorHitResult = false;
+
+ drawActorCostume(true);
+
+ ar->_actorHitMode = false;
+
+ return ar->_actorHitResult;
+}
+#endif
+
+void Actor::animateCostume() {
+ if (_costume == 0)
+ return;
+
+ _animProgress++;
+ if (_animProgress >= _animSpeed) {
+ _animProgress = 0;
+
+ _vm->_costumeLoader->loadCostume(_costume);
+ if (_vm->_costumeLoader->increaseAnims(this)) {
+ _needRedraw = true;
+ }
+ }
+}
+
+#ifndef DISABLE_SCUMM_7_8
+void Actor::animateLimb(int limb, int f) {
+ // This methods is very similiar to animateCostume().
+ // However, instead of animating *all* the limbs, it only animates
+ // the specified limb to be at the frame specified by "f".
+
+ if (!f)
+ return;
+
+ _animProgress++;
+ if (_animProgress >= _animSpeed) {
+ _animProgress = 0;
+
+ if (_costume == 0)
+ return;
+
+ const byte *aksq, *akfo;
+ uint size;
+ byte *akos = _vm->getResourceAddress(rtCostume, _costume);
+ assert(akos);
+
+ aksq = _vm->findResourceData(MKID('AKSQ'), akos);
+ akfo = _vm->findResourceData(MKID('AKFO'), akos);
+
+ size = _vm->getResourceDataSize(akfo) / 2;
+
+ while (f--) {
+ if (_cost.active[limb] != 0)
+ _vm->akos_increaseAnim(this, limb, aksq, (const uint16 *)akfo, size);
+ }
+
+// _needRedraw = true;
+// _needBgReset = true;
+ }
+}
+#endif
+
+void ScummEngine::redrawAllActors() {
+ int j;
+
+ for (j = 1; j < _numActors; j++) {
+ _actors[j]._needRedraw = true;
+ _actors[j]._needBgReset = true;
+ }
+}
+
+void ScummEngine::setActorRedrawFlags() {
+ int i, j;
+
+ // Redraw all actors if a full redraw was requested.
+ // Also redraw all actors in COMI (see bug #1066329 for details).
+ if (_fullRedraw || _version == 8 || (VAR_REDRAW_ALL_ACTORS != 0xFF && VAR(VAR_REDRAW_ALL_ACTORS) != 0)) {
+ for (j = 1; j < _numActors; j++) {
+ _actors[j]._needRedraw = true;
+ }
+ } else {
+ for (i = 0; i < gdi._numStrips; i++) {
+ int strip = _screenStartStrip + i;
+ if (testGfxAnyUsageBits(strip)) {
+ for (j = 1; j < _numActors; j++) {
+ if (testGfxUsageBit(strip, j) && testGfxOtherUsageBits(strip, j)) {
+ _actors[j]._needRedraw = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+void ScummEngine::resetActorBgs() {
+ int i, j;
+
+ for (i = 0; i < gdi._numStrips; i++) {
+ int strip = _screenStartStrip + i;
+ clearGfxUsageBit(strip, USAGE_BIT_DIRTY);
+ clearGfxUsageBit(strip, USAGE_BIT_RESTORED);
+ for (j = 1; j < _numActors; j++) {
+ if (testGfxUsageBit(strip, j) &&
+ ((_actors[j]._top != 0x7fffffff && _actors[j]._needRedraw) || _actors[j]._needBgReset)) {
+ clearGfxUsageBit(strip, j);
+ if ((_actors[j]._bottom - _actors[j]._top) >= 0)
+ gdi.resetBackground(_actors[j]._top, _actors[j]._bottom, i);
+ }
+ }
+ }
+
+ for (i = 1; i < _numActors; i++) {
+ _actors[i]._needBgReset = false;
+ }
+}
+
+int ScummEngine::getActorFromPos(int x, int y) {
+ int i;
+
+ if (!testGfxAnyUsageBits(x / 8))
+ return 0;
+
+ for (i = 1; i < _numActors; i++) {
+ if (testGfxUsageBit(x / 8, i) && !getClass(i, kObjectClassUntouchable)
+ && y >= _actors[i]._top && y <= _actors[i]._bottom) {
+ if (_version > 2 || i != VAR(VAR_EGO))
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+#ifndef DISABLE_HE
+int ScummEngine_v70he::getActorFromPos(int x, int y) {
+ int curActor, i;
+
+ if (!testGfxAnyUsageBits(x / 8))
+ return 0;
+
+ curActor = 0;
+ for (i = 1; i < _numActors; i++) {
+ if (testGfxUsageBit(x / 8, i) && !getClass(i, kObjectClassUntouchable)
+ && y >= _actors[i]._top && y <= _actors[i]._bottom
+ && (_actors[i]._pos.y > _actors[curActor]._pos.y || curActor == 0))
+ curActor = i;
+ }
+
+ return curActor;
+}
+#endif
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::actorTalk(const byte *msg) {
+ Actor *a;
+
+ convertMessageToString(msg, _charsetBuffer, sizeof(_charsetBuffer));
+
+ // Play associated speech, if any
+ playSpeech((byte *)_lastStringTag);
+
+ if ((_version == 7 && !_keepText) || (_version == 8 && VAR(VAR_HAVE_MSG))) {
+ stopTalk();
+ }
+ if (_actorToPrintStrFor == 0xFF) {
+ setTalkingActor(0xFF);
+ } else {
+ a = derefActor(_actorToPrintStrFor, "actorTalk");
+ setTalkingActor(a->_number);
+ if (!_string[0].no_talk_anim) {
+ a->runActorTalkScript(a->_talkStartFrame);
+ _useTalkAnims = true;
+ }
+ }
+
+ if (getTalkingActor() > 0x7F) {
+ _charsetColor = (byte)_string[0].color;
+ } else {
+ a = derefActor(getTalkingActor(), "actorTalk(2)");
+ _charsetColor = a->_talkColor;
+ }
+ _charsetBufPos = 0;
+ _talkDelay = 0;
+ _haveMsg = 1;
+ if (_version == 7)
+ VAR(VAR_HAVE_MSG) = 0xFF;
+ _haveActorSpeechMsg = true;
+ CHARSET_1();
+ if (_version == 8)
+ VAR(VAR_HAVE_MSG) = (_string[0].no_talk_anim) ? 2 : 1;
+}
+#endif
+
+void ScummEngine::actorTalk(const byte *msg) {
+ Actor *a;
+
+ convertMessageToString(msg, _charsetBuffer, sizeof(_charsetBuffer));
+
+ // FIXME: Workaround for bugs #770039 and #770049
+ if (_gameId == GID_LOOM) {
+ if (!*_charsetBuffer)
+ return;
+ }
+
+ if (_actorToPrintStrFor == 0xFF) {
+ if (!_keepText) {
+ stopTalk();
+ }
+ setTalkingActor(0xFF);
+ } else {
+ int oldact;
+
+ // WORKAROUND bug #770724
+ if (_gameId == GID_LOOM && _roomResource == 23 &&
+ vm.slot[_currentScript].number == 232 && _actorToPrintStrFor == 0) {
+ _actorToPrintStrFor = 2; // Could be anything from 2 to 5. Maybe compare to original?
+ }
+
+ a = derefActor(_actorToPrintStrFor, "actorTalk");
+ if (!a->isInCurrentRoom()) {
+ oldact = 0xFF;
+ } else {
+ if (!_keepText) {
+ stopTalk();
+ }
+ setTalkingActor(a->_number);
+ a->_heTalking = true;
+ if (!_string[0].no_talk_anim) {
+ a->runActorTalkScript(a->_talkStartFrame);
+ _useTalkAnims = true;
+ }
+ oldact = getTalkingActor();
+ }
+ if (oldact >= 0x80)
+ return;
+ }
+
+ if (_heversion >= 72 || getTalkingActor() > 0x7F) {
+ _charsetColor = (byte)_string[0].color;
+ } else if (_platform == Common::kPlatformNES) {
+ if (_NES_lastTalkingActor != getTalkingActor())
+ _NES_talkColor ^= 1;
+ _NES_lastTalkingActor = getTalkingActor();
+ _charsetColor = _NES_talkColor;
+ } else {
+ a = derefActor(getTalkingActor(), "actorTalk(2)");
+ _charsetColor = a->_talkColor;
+ }
+ _charsetBufPos = 0;
+ _talkDelay = 0;
+ _haveMsg = 0xFF;
+ VAR(VAR_HAVE_MSG) = 0xFF;
+ if (VAR_CHARCOUNT != 0xFF)
+ VAR(VAR_CHARCOUNT) = 0;
+ _haveActorSpeechMsg = true;
+ CHARSET_1();
+}
+
+void Actor::runActorTalkScript(int f) {
+ if (_vm->_version == 8 && _vm->VAR(_vm->VAR_HAVE_MSG) == 2)
+ return;
+
+ if (_talkScript) {
+ int script = _talkScript;
+ int args[16];
+ memset(args, 0, sizeof(args));
+ args[1] = f;
+ args[0] = _number;
+
+ _vm->runScript(script, 1, 0, args);
+ } else {
+ if (_frame != f)
+ startAnimActor(f);
+ }
+}
+
+void ScummEngine::stopTalk() {
+ int act;
+
+ _sound->stopTalkSound();
+
+ _haveMsg = 0;
+ _talkDelay = 0;
+
+ act = getTalkingActor();
+ if (act && act < 0x80) {
+ Actor *a = derefActor(act, "stopTalk");
+ if ((_version >= 7 && !_string[0].no_talk_anim) ||
+ (_version <= 6 && a->isInCurrentRoom() && _useTalkAnims)) {
+ a->runActorTalkScript(a->_talkStopFrame);
+ _useTalkAnims = false;
+ }
+ if (_version <= 7 && _heversion == 0)
+ setTalkingActor(0xFF);
+ a->_heTalking = false;
+ }
+ if (_version == 8 || _heversion >= 60)
+ setTalkingActor(0);
+ if (_version == 8)
+ VAR(VAR_HAVE_MSG) = 0;
+ _keepText = false;
+ if (_version >= 7) {
+#ifndef DISABLE_SCUMM_7_8
+ ((ScummEngine_v7 *)this)->clearSubtitleQueue();
+#endif
+ } else {
+ _charset->restoreCharsetBg();
+ }
+}
+
+void Actor::setActorCostume(int c) {
+ int i;
+
+ if (_vm->_heversion >= 61 && (c == -1 || c == -2)) {
+ _heSkipLimbs = (c == -1);
+ _needRedraw = true;
+ return;
+ }
+
+ // Based on disassembly. It seems that high byte is not used at all, though
+ // it is attached to all horizontally flipped object, like left eye.
+ if (_vm->_heversion == 61)
+ c &= 0xff;
+
+ _costumeNeedsInit = true;
+
+ if (_vm->_features & GF_NEW_COSTUMES) {
+ memset(_animVariable, 0, sizeof(_animVariable));
+
+#ifndef DISABLE_HE
+ if (_vm->_heversion >= 71)
+ ((ScummEngine_v71he *)_vm)->queueAuxBlock(this);
+#endif
+
+ _costume = c;
+ _cost.reset();
+ _auxBlock.reset();
+
+ if (_visible) {
+ if (_costume) {
+ _vm->ensureResourceLoaded(rtCostume, _costume);
+ }
+ startAnimActor(_initFrame);
+ }
+ } else {
+ if (_visible) {
+ hideActor();
+ _cost.reset();
+ _costume = c;
+ showActor();
+ } else {
+ _costume = c;
+ _cost.reset();
+ }
+ }
+
+
+ // V1 zak uses palette[] as a dynamic costume color array.
+ if (_vm->_version == 1)
+ return;
+
+ if (_vm->_features & GF_NEW_COSTUMES) {
+ for (i = 0; i < 256; i++)
+ _palette[i] = 0xFF;
+ } else if (_vm->_features & GF_OLD_BUNDLE) {
+ for (i = 0; i < 16; i++)
+ _palette[i] = i;
+
+ // Make stuff more visible on CGA. Based on disassembly
+ if (_vm->_renderMode == Common::kRenderCGA && _vm->_version > 2) {
+ _palette[6] = 5;
+ _palette[7] = 15;
+ }
+ } else {
+ for (i = 0; i < 32; i++)
+ _palette[i] = 0xFF;
+ }
+
+ if (_vm->_heversion >= 71 && _vm->getTalkingActor() == _number) {
+ if (_vm->_heversion <= 95 || (_vm->_heversion >= 98 && _vm->VAR(_vm->VAR_SKIP_RESET_TALK_ACTOR) == 0)) {
+ //_vm->setTalkingActor(0);
+ }
+ }
+}
+
+void Actor::startWalkActor(int destX, int destY, int dir) {
+ AdjustBoxResult abr;
+
+ if (_vm->_version <= 3) {
+ abr.x = destX;
+ abr.y = destY;
+ } else {
+ abr = adjustXYToBeInBox(destX, destY);
+ }
+
+ if (!isInCurrentRoom()) {
+ _pos.x = abr.x;
+ _pos.y = abr.y;
+ if (!(_vm->_version == 6 && _ignoreTurns) && dir != -1)
+ setDirection(dir);
+ return;
+ }
+
+ if (_ignoreBoxes) {
+ abr.box = kInvalidBox;
+ _walkbox = kInvalidBox;
+ } else {
+ if (_vm->checkXYInBoxBounds(_walkdata.destbox, abr.x, abr.y)) {
+ abr.box = _walkdata.destbox;
+ } else {
+ abr = adjustXYToBeInBox(abr.x, abr.y);
+ }
+ if (_moving && _walkdata.destdir == dir && _walkdata.dest.x == abr.x && _walkdata.dest.y == abr.y)
+ return;
+ }
+
+ if (_pos.x == abr.x && _pos.y == abr.y) {
+ turnToDirection(dir);
+ return;
+ }
+
+ _walkdata.dest.x = abr.x;
+ _walkdata.dest.y = abr.y;
+ _walkdata.destbox = abr.box;
+ _walkdata.destdir = dir;
+ _moving = (_moving & MF_IN_LEG) | MF_NEW_LEG;
+ _walkdata.point3.x = 32000;
+
+ _walkdata.curbox = _walkbox;
+}
+
+void Actor::startWalkAnim(int cmd, int angle) {
+ if (angle == -1)
+ angle = _facing;
+
+ /* Note: walk scripts aren't required to make the Dig
+ * work as usual
+ */
+ if (_walkScript) {
+ int args[16];
+ memset(args, 0, sizeof(args));
+ args[0] = _number;
+ args[1] = cmd;
+ args[2] = angle;
+ _vm->runScript(_walkScript, 1, 0, args);
+ } else {
+ switch (cmd) {
+ case 1: /* start walk */
+ setDirection(angle);
+ startAnimActor(_walkFrame);
+ break;
+ case 2: /* change dir only */
+ setDirection(angle);
+ break;
+ case 3: /* stop walk */
+ turnToDirection(angle);
+ startAnimActor(_standFrame);
+ break;
+ }
+ }
+}
+
+void Actor::walkActor() {
+ int new_dir, next_box;
+ Common::Point foundPath;
+
+ if (_vm->_version >= 7) {
+ if (_moving & MF_FROZEN) {
+ if (_moving & MF_TURN) {
+ new_dir = updateActorDirection(false);
+ if (_facing != new_dir)
+ setDirection(new_dir);
+ else
+ _moving &= ~MF_TURN;
+ }
+ return;
+ }
+ }
+
+ if (!_moving)
+ return;
+
+ if (!(_moving & MF_NEW_LEG)) {
+ if (_moving & MF_IN_LEG && actorWalkStep())
+ return;
+
+ if (_moving & MF_LAST_LEG) {
+ _moving = 0;
+ setBox(_walkdata.destbox);
+ startWalkAnim(3, _walkdata.destdir);
+ return;
+ }
+
+ if (_moving & MF_TURN) {
+ new_dir = updateActorDirection(false);
+ if (_facing != new_dir)
+ setDirection(new_dir);
+ else
+ _moving = 0;
+ return;
+ }
+
+ setBox(_walkdata.curbox);
+ _moving &= MF_IN_LEG;
+ }
+
+ _moving &= ~MF_NEW_LEG;
+ do {
+
+ if (_walkbox == kInvalidBox) {
+ setBox(_walkdata.destbox);
+ _walkdata.curbox = _walkdata.destbox;
+ break;
+ }
+
+ if (_walkbox == _walkdata.destbox)
+ break;
+
+ next_box = _vm->getPathToDestBox(_walkbox, _walkdata.destbox);
+ if (next_box < 0) {
+ _walkdata.destbox = _walkbox;
+ _moving |= MF_LAST_LEG;
+ return;
+ }
+
+ _walkdata.curbox = next_box;
+
+ if (findPathTowards(_walkbox, next_box, _walkdata.destbox, foundPath))
+ break;
+
+ if (calcMovementFactor(foundPath))
+ return;
+
+ setBox(_walkdata.curbox);
+ } while (1);
+
+ _moving |= MF_LAST_LEG;
+ calcMovementFactor(_walkdata.dest);
+}
+
+/*
+void Actor::walkActorV12() {
+ Common::Point foundPath, tmp;
+ int new_dir, next_box;
+
+ if (_moving & MF_TURN) {
+ new_dir = updateActorDirection(false);
+ if (_facing != new_dir)
+ setDirection(new_dir);
+ else
+ _moving = 0;
+ return;
+ }
+
+ if (!_moving)
+ return;
+
+ if (_moving & MF_IN_LEG) {
+ actorWalkStep();
+ } else {
+ if (_moving & MF_LAST_LEG) {
+ _moving = 0;
+ startWalkAnim(3, _walkdata.destdir);
+ } else {
+ setBox(_walkdata.curbox);
+ if (_walkbox == _walkdata.destbox) {
+ foundPath = _walkdata.dest;
+ _moving |= MF_LAST_LEG;
+ } else {
+ next_box = _vm->getPathToDestBox(_walkbox, _walkdata.destbox);
+ if (next_box < 0) {
+ _moving |= MF_LAST_LEG;
+ return;
+ }
+
+ // Can't walk through locked boxes
+ int flags = _vm->getBoxFlags(next_box);
+ if (flags & kBoxLocked && !(flags & kBoxPlayerOnly && !isPlayer())) {
+ _moving |= MF_LAST_LEG;
+ }
+
+ _walkdata.curbox = next_box;
+
+ _vm->getClosestPtOnBox(_walkdata.curbox, x, y, tmp.x, tmp.y);
+ _vm->getClosestPtOnBox(_walkbox, tmp.x, tmp.y, foundPath.x, foundPath.y);
+ }
+ calcMovementFactor(foundPath);
+ }
+ }
+}
+*/
+
+void Actor::walkActorOld() {
+ Common::Point p2, p3; // Gate locations
+ int new_dir, next_box;
+
+ if (!_moving)
+ return;
+
+ if (!(_moving & MF_NEW_LEG)) {
+ if (_moving & MF_IN_LEG && actorWalkStep())
+ return;
+
+ if (_moving & MF_LAST_LEG) {
+ _moving = 0;
+ startWalkAnim(3, _walkdata.destdir);
+ return;
+ }
+
+ if (_moving & MF_TURN) {
+ new_dir = updateActorDirection(false);
+ if (_facing != new_dir)
+ setDirection(new_dir);
+ else
+ _moving = 0;
+ return;
+ }
+
+ if (_walkdata.point3.x != 32000) {
+ if (calcMovementFactor(_walkdata.point3)) {
+ _walkdata.point3.x = 32000;
+ return;
+ }
+ _walkdata.point3.x = 32000;
+ }
+
+ setBox(_walkdata.curbox);
+ _moving &= MF_IN_LEG;
+ }
+
+ _moving &= ~MF_NEW_LEG;
+ do {
+ if (_walkbox == kInvalidBox) {
+ setBox(_walkdata.destbox);
+ _walkdata.curbox = _walkdata.destbox;
+ break;
+ }
+
+ if (_walkbox == _walkdata.destbox)
+ break;
+
+ next_box = _vm->getPathToDestBox(_walkbox, _walkdata.destbox);
+
+ // WORKAROUND: To fully fix bug #774783, we add a special case
+ // here, resulting in a different next_box value for Hitler.
+ if ((_vm->_gameId == GID_INDY3) && _vm->_roomResource == 46 && _walkbox == 1 && _walkdata.destbox == 0 && _number == 9)
+ next_box = 1;
+
+ if (next_box < 0) {
+ _moving |= MF_LAST_LEG;
+ return;
+ }
+
+ // Can't walk through locked boxes
+ int flags = _vm->getBoxFlags(next_box);
+ if (flags & kBoxLocked && !(flags & kBoxPlayerOnly && !isPlayer())) {
+ _moving |= MF_LAST_LEG;
+// FIXME: Work in progress
+// _walkdata.destdir = _facing;
+ return;
+ }
+
+ _walkdata.curbox = next_box;
+
+ if (_vm->_version <= 2) {
+ _vm->getClosestPtOnBox(_walkdata.curbox, _pos.x, _pos.y, p2.x, p2.y);
+ _vm->getClosestPtOnBox(_walkbox, p2.x, p2.y, p3.x, p3.y);
+// FIXME: Work in progress
+// calcMovementFactor(p3);
+// return;
+ } else {
+ findPathTowardsOld(_walkbox, next_box, _walkdata.destbox, p2, p3);
+ if (p2.x == 32000 && p3.x == 32000) {
+ break;
+ }
+
+ if (p2.x != 32000) {
+ if (calcMovementFactor(p2)) {
+ _walkdata.point3 = p3;
+ return;
+ }
+ }
+ }
+ if (calcMovementFactor(p3))
+ return;
+
+ setBox(_walkdata.destbox);
+ } while (1);
+
+ _moving |= MF_LAST_LEG;
+ calcMovementFactor(_walkdata.dest);
+}
+
+byte *Actor::getActorName() {
+ byte *ptr = _vm->getResourceAddress(rtActorName, _number);
+ if (ptr == NULL) {
+ debug(0, "Failed to find name of actor %d", _number);
+ }
+ return ptr;
+}
+
+int Actor::getAnimVar(byte var) const {
+ checkRange(26, 0, var, "getAnimVar %d out of range(r)");
+ return _animVariable[var];
+}
+
+void Actor::setAnimVar(byte var, int value) {
+ checkRange(26, 0, var, "setAnimVar %d out of range(r)");
+ _animVariable[var] = value;
+}
+
+void Actor::remapActorPaletteColor(int color, int new_color) {
+ const byte *akos, *akpl;
+ int akpl_size, i;
+ byte akpl_color;
+
+ akos = _vm->getResourceAddress(rtCostume, _costume);
+ if (!akos) {
+ debug(0, "Can't remap actor %d, costume %d not found", _number, _costume);
+ return;
+ }
+
+ akpl = _vm->findResourceData(MKID('AKPL'), akos);
+ if (!akpl) {
+ debug(0, "Can't remap actor %d, costume %d doesn't contain an AKPL block", _number, _costume);
+ return;
+ }
+
+ // Get the number palette entries
+ akpl_size = _vm->getResourceDataSize(akpl);
+
+ for (i = 0; i < akpl_size; i++) {
+ akpl_color = *akpl++;
+ if (akpl_color == color) {
+ _palette[i] = new_color;
+ return;
+ }
+ }
+}
+
+void Actor::remapActorPalette(int r_fact, int g_fact, int b_fact, int threshold) {
+ const byte *akos, *rgbs, *akpl;
+ int akpl_size, i;
+ int r, g, b;
+ byte akpl_color;
+
+ if (!isInCurrentRoom()) {
+ debugC(DEBUG_ACTORS, "Remap actor %d not in current room", _number);
+ return;
+ } else if (_costume < 1 || _costume >= _vm->_numCostumes - 1) {
+ debugC(DEBUG_ACTORS, "Remap actor %d invalid costume %d", _number, _costume);
+ return;
+ }
+
+ akos = _vm->getResourceAddress(rtCostume, _costume);
+ if (!akos) {
+ debug(0, "Can't remap actor %d, costume %d not found", _number, _costume);
+ return;
+ }
+
+ akpl = _vm->findResourceData(MKID('AKPL'), akos);
+ if (!akpl) {
+ debug(0, "Can't remap actor %d, costume %d doesn't contain an AKPL block", _number, _costume);
+ return;
+ }
+
+ // Get the number palette entries
+ akpl_size = _vm->getResourceDataSize(akpl);
+
+ rgbs = _vm->findResourceData(MKID('RGBS'), akos);
+
+ if (!rgbs) {
+ debugC(DEBUG_ACTORS, "Can't remap actor %d costume %d doesn't contain an RGB block", _number, _costume);
+ return;
+ }
+
+ for (i = 0; i < akpl_size; i++) {
+ r = *rgbs++;
+ g = *rgbs++;
+ b = *rgbs++;
+
+ akpl_color = *akpl++;
+
+ // allow remap of generic palette entry?
+ if (!_shadowMode || akpl_color >= 16) {
+ r = (r * r_fact) >> 8;
+ g = (g * g_fact) >> 8;
+ b = (b * b_fact) >> 8;
+ _palette[i] = _vm->remapPaletteColor(r, g, b, threshold);
+ }
+ }
+}
+
+void Actor::classChanged(int cls, bool value) {
+ if (cls == kObjectClassAlwaysClip)
+ _forceClip = value;
+ if (cls == kObjectClassIgnoreBoxes)
+ _ignoreBoxes = value;
+}
+
+bool Actor::isInClass(int cls) {
+ return _vm->getClass(_number, cls);
+}
+
+bool Actor::isPlayer() {
+ if (_vm->_version <= 2)
+ return _vm->VAR(42) <= _number && _number <= _vm->VAR(43);
+ else
+ return isInClass(kObjectClassPlayer);
+}
+
+void Actor::setUserCondition(int slot, int set) {
+ const int condMaskCode = (_vm->_heversion >= 90) ? 0x1FFF : 0x3FF;
+ checkRange(32, 1, slot, "Condition %d out of range");
+ if (set == 0) {
+ _heCondMask &= ~(1 << (slot + 0xF));
+ } else {
+ _heCondMask |= 1 << (slot + 0xF);
+ }
+ if (_heCondMask & condMaskCode) {
+ _heCondMask &= ~1;
+ } else {
+ _heCondMask |= 1;
+ }
+}
+
+bool Actor::isUserConditionSet(int slot) const {
+ checkRange(32, 1, slot, "Condition %d out of range");
+ return (_heCondMask & (1 << (slot + 0xF))) != 0;
+}
+
+void Actor::setTalkCondition(int slot) {
+ const int condMaskCode = (_vm->_heversion >= 90) ? 0x1FFF : 0x3FF;
+ checkRange(32, 1, slot, "Condition %d out of range");
+ _heCondMask = (_heCondMask & ~condMaskCode) | 1;
+ if (slot != 1) {
+ _heCondMask |= 1 << (slot - 1);
+ if (_heCondMask & condMaskCode) {
+ _heCondMask &= ~1;
+ } else {
+ _heCondMask |= 1;
+ }
+ }
+}
+
+bool Actor::isTalkConditionSet(int slot) const {
+ checkRange(32, 1, slot, "Condition %d out of range");
+ return (_heCondMask & (1 << (slot - 1))) != 0;
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v71he::preProcessAuxQueue() {
+ if (!_skipProcessActors) {
+ for (int i = 0; i < _auxBlocksNum; ++i) {
+ AuxBlock *ab = &_auxBlocks[i];
+ if (ab->r.top <= ab->r.bottom) {
+ gdi.copyVirtScreenBuffers(ab->r);
+ }
+ }
+ }
+ _auxBlocksNum = 0;
+}
+
+void ScummEngine_v71he::postProcessAuxQueue() {
+ if (!_skipProcessActors) {
+ for (int i = 0; i < _auxEntriesNum; ++i) {
+ AuxEntry *ae = &_auxEntries[i];
+ if (ae->actorNum != -1) {
+ Actor *a = derefActor(ae->actorNum, "postProcessAuxQueue");
+ const uint8 *cost = getResourceAddress(rtCostume, a->_costume);
+ int dy = a->_offsY + a->_pos.y - a->getElevation();
+ int dx = a->_offsX + a->_pos.x;
+
+ const uint8 *akax = findResource(MKID('AKAX'), cost);
+ assert(akax);
+ const uint8 *auxd = findPalInPals(akax, ae->subIndex) - _resourceHeaderSize;
+ assert(auxd);
+ const uint8 *frel = findResourceData(MKID('FREL'), auxd);
+ if (frel) {
+ error("unhandled FREL block");
+ }
+ const uint8 *disp = findResourceData(MKID('DISP'), auxd);
+ if (disp) {
+ error("unhandled DISP block");
+ }
+ const uint8 *axfd = findResourceData(MKID('AXFD'), auxd);
+ assert(axfd);
+
+ uint16 comp = READ_LE_UINT16(axfd);
+ if (comp != 0) {
+ int x = (int16)READ_LE_UINT16(axfd + 2) + dx;
+ int y = (int16)READ_LE_UINT16(axfd + 4) + dy;
+ int w = (int16)READ_LE_UINT16(axfd + 6);
+ int h = (int16)READ_LE_UINT16(axfd + 8);
+ VirtScreen *pvs = &virtscr[kMainVirtScreen];
+ uint8 *dst1 = pvs->getPixels(0, pvs->topline);
+ uint8 *dst2 = pvs->getBackPixels(0, pvs->topline);
+ switch (comp) {
+ case 1:
+ Wiz::copyAuxImage(dst1, dst2, axfd + 10, pvs->w, pvs->h, x, y, w, h);
+ break;
+ default:
+ error("unimplemented compression type %d", comp);
+ }
+ }
+ const uint8 *axur = findResourceData(MKID('AXUR'), auxd);
+ if (axur) {
+ uint16 n = READ_LE_UINT16(axur); axur += 2;
+ while (n--) {
+ int x1 = (int16)READ_LE_UINT16(axur + 0) + dx;
+ int y1 = (int16)READ_LE_UINT16(axur + 2) + dy;
+ int x2 = (int16)READ_LE_UINT16(axur + 4) + dx;
+ int y2 = (int16)READ_LE_UINT16(axur + 6) + dy;
+ markRectAsDirty(kMainVirtScreen, x1, x2, y1, y2 + 1);
+ axur += 8;
+ }
+ }
+ const uint8 *axer = findResourceData(MKID('AXER'), auxd);
+ if (axer) {
+ a->_auxBlock.visible = true;
+ a->_auxBlock.r.left = (int16)READ_LE_UINT16(axer + 0) + dx;
+ a->_auxBlock.r.top = (int16)READ_LE_UINT16(axer + 2) + dy;
+ a->_auxBlock.r.right = (int16)READ_LE_UINT16(axer + 4) + dx;
+ a->_auxBlock.r.bottom = (int16)READ_LE_UINT16(axer + 6) + dy;
+ }
+ }
+ }
+ }
+ _auxEntriesNum = 0;
+}
+
+void ScummEngine_v71he::queueAuxBlock(Actor *a) {
+ if (!a->_auxBlock.visible)
+ return;
+
+ assert(_auxBlocksNum < ARRAYSIZE(_auxBlocks));
+ _auxBlocks[_auxBlocksNum] = a->_auxBlock;
+ ++_auxBlocksNum;
+}
+
+void ScummEngine_v71he::queueAuxEntry(int actorNum, int subIndex) {
+ assert(_auxEntriesNum < ARRAYSIZE(_auxEntries));
+ AuxEntry *ae = &_auxEntries[_auxEntriesNum];
+ ae->actorNum = actorNum;
+ ae->subIndex = subIndex;
+ ++_auxEntriesNum;
+}
+#endif
+
+
+void Actor::saveLoadWithSerializer(Serializer *ser) {
+ static const SaveLoadEntry actorEntries[] = {
+ MKLINE(Actor, _pos.x, sleInt16, VER(8)),
+ MKLINE(Actor, _pos.y, sleInt16, VER(8)),
+ MKLINE(Actor, _offsX, sleInt16, VER(32)),
+ MKLINE(Actor, _offsY, sleInt16, VER(32)),
+ MKLINE(Actor, _top, sleInt16, VER(8)),
+ MKLINE(Actor, _bottom, sleInt16, VER(8)),
+ MKLINE(Actor, _elevation, sleInt16, VER(8)),
+ MKLINE(Actor, _width, sleUint16, VER(8)),
+ MKLINE(Actor, _facing, sleUint16, VER(8)),
+ MKLINE(Actor, _costume, sleUint16, VER(8)),
+ MKLINE(Actor, _room, sleByte, VER(8)),
+ MKLINE(Actor, _talkColor, sleByte, VER(8)),
+ MKLINE(Actor, _talkFrequency, sleInt16, VER(16)),
+ MKLINE(Actor, _talkPan, sleInt16, VER(24)),
+ MKLINE(Actor, _talkVolume, sleInt16, VER(29)),
+ MKLINE(Actor, _boxscale, sleUint16, VER(34)),
+ MKLINE(Actor, _scalex, sleByte, VER(8)),
+ MKLINE(Actor, _scaley, sleByte, VER(8)),
+ MKLINE(Actor, _charset, sleByte, VER(8)),
+
+ // Actor sound grew from 8 to 32 bytes and switched to uint16 in HE games
+ MKARRAY_OLD(Actor, _sound[0], sleByte, 8, VER(8), VER(36)),
+ MKARRAY_OLD(Actor, _sound[0], sleByte, 32, VER(37), VER(61)),
+ MKARRAY(Actor, _sound[0], sleUint16, 32, VER(62)),
+
+ // Actor animVariable grew from 8 to 27
+ MKARRAY_OLD(Actor, _animVariable[0], sleUint16, 8, VER(8), VER(40)),
+ MKARRAY(Actor, _animVariable[0], sleUint16, 27, VER(41)),
+
+ MKLINE(Actor, _targetFacing, sleUint16, VER(8)),
+ MKLINE(Actor, _moving, sleByte, VER(8)),
+ MKLINE(Actor, _ignoreBoxes, sleByte, VER(8)),
+ MKLINE(Actor, _forceClip, sleByte, VER(8)),
+ MKLINE(Actor, _initFrame, sleByte, VER(8)),
+ MKLINE(Actor, _walkFrame, sleByte, VER(8)),
+ MKLINE(Actor, _standFrame, sleByte, VER(8)),
+ MKLINE(Actor, _talkStartFrame, sleByte, VER(8)),
+ MKLINE(Actor, _talkStopFrame, sleByte, VER(8)),
+ MKLINE(Actor, _speedx, sleUint16, VER(8)),
+ MKLINE(Actor, _speedy, sleUint16, VER(8)),
+ MKLINE(Actor, _cost.animCounter, sleUint16, VER(8)),
+ MKLINE(Actor, _cost.soundCounter, sleByte, VER(8)),
+ MKLINE(Actor, _drawToBackBuf, sleByte, VER(32)),
+ MKLINE(Actor, _flip, sleByte, VER(32)),
+ MKLINE(Actor, _heSkipLimbs, sleByte, VER(32)),
+
+ // Actor palette grew from 64 to 256 bytes
+ MKARRAY_OLD(Actor, _palette[0], sleByte, 64, VER(8), VER(9)),
+ MKARRAY(Actor, _palette[0], sleByte, 256, VER(10)),
+
+ MK_OBSOLETE(Actor, _mask, sleByte, VER(8), VER(9)),
+ MKLINE(Actor, _shadowMode, sleByte, VER(8)),
+ MKLINE(Actor, _visible, sleByte, VER(8)),
+ MKLINE(Actor, _frame, sleByte, VER(8)),
+ MKLINE(Actor, _animSpeed, sleByte, VER(8)),
+ MKLINE(Actor, _animProgress, sleByte, VER(8)),
+ MKLINE(Actor, _walkbox, sleByte, VER(8)),
+ MKLINE(Actor, _needRedraw, sleByte, VER(8)),
+ MKLINE(Actor, _needBgReset, sleByte, VER(8)),
+ MKLINE(Actor, _costumeNeedsInit, sleByte, VER(8)),
+ MKLINE(Actor, _heCondMask, sleUint32, VER(38)),
+ MKLINE(Actor, _hePaletteNum, sleUint32, VER(59)),
+ MKLINE(Actor, _heXmapNum, sleUint32, VER(59)),
+
+ MKLINE(Actor, _talkPosY, sleInt16, VER(8)),
+ MKLINE(Actor, _talkPosX, sleInt16, VER(8)),
+ MKLINE(Actor, _ignoreTurns, sleByte, VER(8)),
+
+ // Actor layer switched to int32 in HE games
+ MKLINE_OLD(Actor, _layer, sleByte, VER(8), VER(57)),
+ MKLINE(Actor, _layer, sleInt32, VER(58)),
+
+ MKLINE(Actor, _talkScript, sleUint16, VER(8)),
+ MKLINE(Actor, _walkScript, sleUint16, VER(8)),
+
+ MKLINE(Actor, _walkdata.dest.x, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.dest.y, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.destbox, sleByte, VER(8)),
+ MKLINE(Actor, _walkdata.destdir, sleUint16, VER(8)),
+ MKLINE(Actor, _walkdata.curbox, sleByte, VER(8)),
+ MKLINE(Actor, _walkdata.cur.x, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.cur.y, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.next.x, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.next.y, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.deltaXFactor, sleInt32, VER(8)),
+ MKLINE(Actor, _walkdata.deltaYFactor, sleInt32, VER(8)),
+ MKLINE(Actor, _walkdata.xfrac, sleUint16, VER(8)),
+ MKLINE(Actor, _walkdata.yfrac, sleUint16, VER(8)),
+
+ MKLINE(Actor, _walkdata.point3.x, sleUint16, VER(42)),
+ MKLINE(Actor, _walkdata.point3.y, sleUint16, VER(42)),
+
+ MKARRAY(Actor, _cost.active[0], sleByte, 16, VER(8)),
+ MKLINE(Actor, _cost.stopped, sleUint16, VER(8)),
+ MKARRAY(Actor, _cost.curpos[0], sleUint16, 16, VER(8)),
+ MKARRAY(Actor, _cost.start[0], sleUint16, 16, VER(8)),
+ MKARRAY(Actor, _cost.end[0], sleUint16, 16, VER(8)),
+ MKARRAY(Actor, _cost.frame[0], sleUint16, 16, VER(8)),
+ MKEND()
+ };
+
+ if (ser->isLoading()) {
+ // Not all actor data is saved; so when loading, we first reset
+ // the actor, to ensure completely reproducible behaviour (else,
+ // some not saved value in the actor class can cause odd things)
+ initActor(-1);
+ }
+
+ ser->saveLoadEntries(this, actorEntries);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h
new file mode 100644
index 0000000000..2a6e49ed15
--- /dev/null
+++ b/engines/scumm/actor.h
@@ -0,0 +1,294 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+
+#ifndef ACTOR_H
+#define ACTOR_H
+
+#include "common/scummsys.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+
+
+namespace Scumm {
+
+enum MoveFlags {
+ MF_NEW_LEG = 1,
+ MF_IN_LEG = 2,
+ MF_TURN = 4,
+ MF_LAST_LEG = 8,
+ MF_FROZEN = 0x80
+};
+
+struct ActorWalkData {
+ Common::Point dest; // Final destination point
+ byte destbox; // Final destination box
+ int16 destdir; // Final destination, direction to face at
+
+ Common::Point cur; // Last position
+ byte curbox; // Last box
+
+ Common::Point next; // Next position on our way to the destination, i.e. our intermediate destination
+
+ Common::Point point3;
+ int32 deltaXFactor, deltaYFactor;
+ uint16 xfrac, yfrac;
+};
+
+struct CostumeData {
+ byte active[16];
+ uint16 animCounter;
+ byte soundCounter;
+ uint16 stopped;
+ uint16 curpos[16];
+ uint16 start[16];
+ uint16 end[16];
+ uint16 frame[16];
+
+ uint16 heJumpOffsetTable[16];
+ uint16 heJumpCountTable[16];
+ uint32 heCondMaskTable[16];
+
+ void reset() {
+ stopped = 0;
+ for (int i = 0; i < 16; i++) {
+ active[i] = 0;
+ curpos[i] = start[i] = end[i] = frame[i] = 0xFFFF;
+ }
+ }
+};
+
+struct AdjustBoxResult { /* Result type of AdjustBox functions */
+ int16 x, y;
+ byte box;
+};
+
+class Actor : public Serializable {
+
+public:
+ static byte kInvalidBox;
+
+ static void initActorClass(ScummEngine *scumm);
+
+public:
+ /** The position of the actor inside the virtual screen. */
+ Common::Point _pos;
+
+ /** HE specific: This rect is used to clip actor drawing. */
+ Common::Rect _clipOverride;
+
+ int _offsX, _offsY;
+ int _top, _bottom;
+ uint _width;
+ byte _number;
+ uint16 _costume;
+ byte _room;
+ byte _talkColor;
+ int _talkFrequency;
+ byte _talkPan;
+ byte _talkVolume;
+ uint16 _boxscale;
+ byte _scalex, _scaley;
+ byte _charset;
+ byte _moving;
+ bool _ignoreBoxes;
+ byte _forceClip;
+
+ byte _initFrame;
+ byte _walkFrame;
+ byte _standFrame;
+ byte _talkStartFrame;
+ byte _talkStopFrame;
+
+ bool _needRedraw, _needBgReset, _visible;
+ byte _shadowMode;
+ bool _flip;
+ byte _frame;
+ byte _walkbox;
+ int16 _talkPosX, _talkPosY;
+ uint16 _talkScript, _walkScript;
+ bool _ignoreTurns;
+ bool _drawToBackBuf;
+ int32 _layer;
+ uint16 _sound[32];
+ CostumeData _cost;
+
+ /* HE specific */
+ bool _heNoTalkAnimation;
+ bool _heSkipLimbs;
+ bool _heTalking;
+ uint32 _heCondMask;
+ uint32 _hePaletteNum;
+ uint32 _heXmapNum;
+
+ AuxBlock _auxBlock;
+
+ struct {
+ int16 posX;
+ int16 posY;
+ int16 color;
+ byte sentence[128];
+ } _heTalkQueue[16];
+
+protected:
+ byte _palette[256];
+ int _elevation;
+ uint16 _facing;
+ uint16 _targetFacing;
+ uint _speedx, _speedy;
+ byte _animProgress, _animSpeed;
+ bool _costumeNeedsInit;
+ ActorWalkData _walkdata;
+ int16 _animVariable[27];
+
+ static ScummEngine *_vm;
+
+public:
+
+ Actor();
+
+//protected:
+ void hideActor();
+ void showActor();
+
+ void initActor(int mode);
+ void putActor(int x, int y, byte room);
+ void setActorWalkSpeed(uint newSpeedX, uint newSpeedY);
+protected:
+ int calcMovementFactor(const Common::Point& next);
+ int actorWalkStep();
+ int remapDirection(int dir, bool is_walking);
+ void setupActorScale();
+
+ void setBox(int box);
+ int updateActorDirection(bool is_walking);
+
+public:
+ void adjustActorPos();
+ AdjustBoxResult adjustXYToBeInBox(int dstX, int dstY);
+
+ void setDirection(int direction);
+ void faceToObject(int obj);
+ void turnToDirection(int newdir);
+ void walkActor();
+ void walkActorOld();
+ void drawActorToBackBuf(int x, int y);
+ void drawActorCostume(bool hitTestMode = false);
+ void animateCostume();
+ void setActorCostume(int c);
+
+ void animateLimb(int limb, int f);
+
+ bool actorHitTest(int x, int y);
+
+ byte *getActorName();
+ void startWalkActor(int x, int y, int dir);
+ void stopActorMoving();
+protected:
+ void startWalkAnim(int cmd, int angle);
+public:
+ void runActorTalkScript(int f);
+ void startAnimActor(int frame);
+
+ void remapActorPalette(int r_fact, int g_fact, int b_fact, int threshold);
+ void remapActorPaletteColor(int slot, int color);
+
+ void animateActor(int anim);
+
+ bool isInCurrentRoom() const {
+ return _room == _vm->_currentRoom;
+ }
+
+ int getActorXYPos(int &x, int &y) const;
+
+ int getRoom() const {
+ return _room;
+ }
+
+ int getFacing() const {
+ return _facing;
+ }
+
+ int getAnimVar(byte var) const;
+ void setAnimVar(byte var, int value);
+
+ void setAnimSpeed(byte newAnimSpeed) {
+ _animSpeed = newAnimSpeed;
+ _animProgress = 0;
+ }
+
+ int getAnimSpeed() const {
+ return _animSpeed;
+ }
+
+ int getAnimProgress() const {
+ return _animProgress;
+ }
+
+ int getElevation() const {
+ return _elevation;
+ }
+
+ void setElevation(int newElevation) {
+ if (_elevation != newElevation) {
+ _elevation = newElevation;
+ _needRedraw = true;
+ }
+ }
+
+ void setPalette(int idx, int val) {
+ _palette[idx] = val;
+ _needRedraw = true;
+ }
+
+ void setScale(int sx, int sy) {
+ if (sx != -1)
+ _scalex = sx;
+ if (sy != -1)
+ _scaley = sy;
+ _needRedraw = true;
+ }
+
+ void classChanged(int cls, bool value);
+
+ void setUserCondition(int slot, int set);
+ bool isUserConditionSet(int slot) const;
+
+ void setTalkCondition(int slot);
+ bool isTalkConditionSet(int slot) const;
+
+ // Used by the save/load system:
+ void saveLoadWithSerializer(Serializer *ser);
+
+protected:
+ bool isInClass(int cls);
+
+ bool isPlayer();
+
+ bool findPathTowards(byte box, byte box2, byte box3, Common::Point &foundPath);
+ void findPathTowardsOld(byte box, byte box2, byte box3, Common::Point &p2, Common::Point &p3);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/akos.cpp b/engines/scumm/akos.cpp
new file mode 100644
index 0000000000..d8f484f1d3
--- /dev/null
+++ b/engines/scumm/akos.cpp
@@ -0,0 +1,1869 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/actor.h"
+#include "scumm/akos.h"
+#include "scumm/bomp.h"
+#include "scumm/imuse.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/intern.h"
+#include "scumm/intern_he.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "scumm/wiz_he.h"
+
+namespace Scumm {
+
+#if !defined(__GNUC__)
+#pragma START_PACK_STRUCTS
+#endif
+
+struct AkosHeader {
+ byte unk_1[2];
+ byte flags;
+ byte unk_2;
+ uint16 num_anims;
+ uint16 unk_3;
+ uint16 codec;
+} GCC_PACK;
+
+struct AkosOffset {
+ uint32 akcd;
+ uint16 akci;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+#pragma END_PACK_STRUCTS
+#endif
+
+
+enum AkosOpcodes {
+ AKC_Return = 0xC001,
+ AKC_SetVar = 0xC010,
+ AKC_CmdQue3 = 0xC015,
+ AKC_C016 = 0xC016,
+ AKC_C017 = 0xC017,
+ AKC_C018 = 0xC018,
+ AKC_C019 = 0xC019,
+ AKC_ComplexChan = 0xC020,
+ AKC_C021 = 0xC021,
+ AKC_C022 = 0xC022,
+ AKC_ComplexChan2 = 0xC025,
+ AKC_Jump = 0xC030,
+ AKC_JumpIfSet = 0xC031,
+ AKC_AddVar = 0xC040,
+ AKC_C042 = 0xC042,
+ AKC_C044 = 0xC044,
+ AKC_C045 = 0xC045,
+ AKC_C046 = 0xC046,
+ AKC_C047 = 0xC047,
+ AKC_C048 = 0xC048,
+ AKC_Ignore = 0xC050,
+ AKC_IncVar = 0xC060,
+ AKC_CmdQue3Quick = 0xC061,
+ AKC_JumpStart = 0xC070,
+ AKC_JumpE = 0xC070,
+ AKC_JumpNE = 0xC071,
+ AKC_JumpL = 0xC072,
+ AKC_JumpLE = 0xC073,
+ AKC_JumpG = 0xC074,
+ AKC_JumpGE = 0xC075,
+ AKC_StartAnim = 0xC080,
+ AKC_StartVarAnim = 0xC081,
+ AKC_Random = 0xC082,
+ AKC_SetActorClip = 0xC083,
+ AKC_StartAnimInActor = 0xC084,
+ AKC_SetVarInActor = 0xC085,
+ AKC_HideActor = 0xC086,
+ AKC_SetDrawOffs = 0xC087,
+ AKC_JumpTable = 0xC088,
+ AKC_SoundStuff = 0xC089,
+ AKC_Flip = 0xC08A,
+ AKC_Cmd3 = 0xC08B,
+ AKC_Ignore3 = 0xC08C,
+ AKC_Ignore2 = 0xC08D,
+ AKC_C08E = 0xC08E,
+ AKC_SkipStart = 0xC090,
+ AKC_SkipE = 0xC090,
+ AKC_SkipNE = 0xC091,
+ AKC_SkipL = 0xC092,
+ AKC_SkipLE = 0xC093,
+ AKC_SkipG = 0xC094,
+ AKC_SkipGE = 0xC095,
+ AKC_ClearFlag = 0xC09F,
+ AKC_C0A0 = 0xC0A0,
+ AKC_C0A1 = 0xC0A1,
+ AKC_C0A2 = 0xC0A2,
+ AKC_C0A3 = 0xC0A3,
+ AKC_C0A4 = 0xC0A4,
+ AKC_C0A5 = 0xC0A5,
+ AKC_C0A6 = 0xC0A6,
+ AKC_C0A7 = 0xC0A7,
+ AKC_EndSeq = 0xC0FF
+};
+
+static bool akos_compare(int a, int b, byte cmd) {
+ switch (cmd) {
+ case 0:
+ return a == b;
+ case 1:
+ return a != b;
+ case 2:
+ return a < b;
+ case 3:
+ return a <= b;
+ case 4:
+ return a > b;
+ default:
+ return a >= b;
+ }
+}
+
+void AkosCostumeLoader::loadCostume(int id) {
+ _akos = _vm->getResourceAddress(rtCostume, id);
+ assert(_akos);
+}
+
+bool AkosCostumeLoader::hasManyDirections() {
+ const AkosHeader *akhd;
+
+ akhd = (const AkosHeader *)_vm->findResourceData(MKID('AKHD'), _akos);
+ return (akhd->flags & 2) != 0;
+}
+
+void AkosCostumeLoader::costumeDecodeData(Actor *a, int frame, uint usemask) {
+ uint anim;
+ const byte *r;
+ const AkosHeader *akhd;
+ uint offs;
+ int i;
+ byte code;
+ uint16 start, len;
+ uint16 mask;
+
+ if (a->_costume == 0)
+ return;
+
+ loadCostume(a->_costume);
+
+ if (_vm->_version >= 7 && hasManyDirections())
+ anim = toSimpleDir(1, a->getFacing()) + frame * 8;
+ else
+ anim = newDirToOldDir(a->getFacing()) + frame * 4;
+
+ akhd = (const AkosHeader *)_vm->findResourceData(MKID('AKHD'), _akos);
+
+ if (anim >= READ_LE_UINT16(&akhd->num_anims))
+ return;
+
+ r = _vm->findResourceData(MKID('AKCH'), _akos);
+ assert(r);
+
+ offs = READ_LE_UINT16(r + anim * sizeof(uint16));
+ if (offs == 0)
+ return;
+ r += offs;
+
+ const uint8 *akstPtr = _vm->findResourceData(MKID('AKST'), _akos);
+ const uint8 *aksfPtr = _vm->findResourceData(MKID('AKSF'), _akos);
+
+ i = 0;
+ mask = READ_LE_UINT16(r); r += 2;
+ do {
+ if (mask & 0x8000) {
+ const uint8 *akst = akstPtr;
+ const uint8 *aksf = aksfPtr;
+
+ code = *r++;
+ if (usemask & 0x8000) {
+ switch (code) {
+ case 1:
+ a->_cost.active[i] = 0;
+ a->_cost.frame[i] = frame;
+ a->_cost.end[i] = 0;
+ a->_cost.start[i] = 0;
+ a->_cost.curpos[i] = 0;
+ a->_cost.heCondMaskTable[i] = 0;
+
+ if (akst) {
+ int size = _vm->getResourceDataSize(akst) / 8;
+ if (size > 0) {
+ bool found = false;
+ while (size--) {
+ if (READ_LE_UINT32(akst) == 0) {
+ a->_cost.heCondMaskTable[i] = READ_LE_UINT32(akst + 4);
+ found = true;
+ break;
+ }
+ akst += 8;
+ }
+ if (!found) {
+ error("Sequence not found in actor 0x%X costume %d", a, a->_costume);
+ }
+ }
+ }
+ break;
+ case 4:
+ a->_cost.stopped |= 1 << i;
+ break;
+ case 5:
+ a->_cost.stopped &= ~(1 << i);
+ break;
+ default:
+ start = READ_LE_UINT16(r); r += 2;
+ len = READ_LE_UINT16(r); r += 2;
+
+ a->_cost.heJumpOffsetTable[i] = 0;
+ a->_cost.heJumpCountTable[i] = 0;
+ if (aksf) {
+ int size = _vm->getResourceDataSize(aksf) / 6;
+ if (size > 0) {
+ bool found = false;
+ while (size--) {
+ if (READ_LE_UINT16(aksf) == start) {
+ a->_cost.heJumpOffsetTable[i] = READ_LE_UINT16(aksf + 2);
+ a->_cost.heJumpCountTable[i] = READ_LE_UINT16(aksf + 4);
+ found = true;
+ break;
+ }
+ aksf += 6;
+ }
+ if (!found) {
+ error("Sequence not found in actor 0x%X costume %d", a, a->_costume);
+ }
+ }
+ }
+
+ a->_cost.active[i] = code;
+ a->_cost.frame[i] = frame;
+ a->_cost.end[i] = start + len;
+ a->_cost.start[i] = start;
+ a->_cost.curpos[i] = start;
+ a->_cost.heCondMaskTable[i] = 0;
+ if (akst) {
+ int size = _vm->getResourceDataSize(akst) / 8;
+ if (size > 0) {
+ bool found = false;
+ while (size--) {
+ if (READ_LE_UINT32(akst) == start) {
+ a->_cost.heCondMaskTable[i] = READ_LE_UINT32(akst + 4);
+ found = true;
+ break;
+ }
+ akst += 8;
+ }
+ if (!found) {
+ error("Sequence not found in actor 0x%X costume %d", a, a->_costume);
+ }
+ }
+ }
+ break;
+ }
+ } else {
+ if (code != 1 && code != 4 && code != 5)
+ r += sizeof(uint16) * 2;
+ }
+ }
+ i++;
+ mask <<= 1;
+ usemask <<= 1;
+ } while ((uint16)mask);
+}
+
+void AkosRenderer::setPalette(byte *new_palette) {
+ uint size, i;
+
+ size = _vm->getResourceDataSize(akpl);
+ if (size == 0)
+ return;
+
+ if (size > 256)
+ error("akos_setPalette: %d is too many colors", size);
+
+ if (_vm->_heversion >= 99 && _paletteNum) {
+ for (i = 0; i < size; i++)
+ palette[i] = (byte)_vm->_hePalettes[_paletteNum * 1024 + 768 + akpl[i]];
+ } else {
+ for (i = 0; i < size; i++) {
+ palette[i] = new_palette[i] != 0xFF ? new_palette[i] : akpl[i];
+ }
+ }
+
+ if (_vm->_heversion == 70) {
+ for (i = 0; i < size; i++)
+ palette[i] = _vm->_HEV7ActorPalette[palette[i]];
+ }
+
+ if (size == 256) {
+ byte color = new_palette[0];
+ if (color == 255) {
+ palette[0] = color;
+ } else {
+ _vm->_bompActorPalettePtr = palette;
+ }
+ }
+}
+
+void AkosRenderer::setCostume(int costume, int shadow) {
+ akos = _vm->getResourceAddress(rtCostume, costume);
+ assert(akos);
+
+ akhd = (const AkosHeader *) _vm->findResourceData(MKID('AKHD'), akos);
+ akof = (const AkosOffset *) _vm->findResourceData(MKID('AKOF'), akos);
+ akci = _vm->findResourceData(MKID('AKCI'), akos);
+ aksq = _vm->findResourceData(MKID('AKSQ'), akos);
+ akcd = _vm->findResourceData(MKID('AKCD'), akos);
+ akpl = _vm->findResourceData(MKID('AKPL'), akos);
+ codec = READ_LE_UINT16(&akhd->codec);
+ akct = _vm->findResourceData(MKID('AKCT'), akos);
+
+ xmap = 0;
+ if (shadow) {
+ const uint8 *xmapPtr = _vm->getResourceAddress(rtImage, shadow);
+ assert(xmapPtr);
+ xmap = _vm->findResourceData(MKID('XMAP'), xmapPtr);
+ assert(xmap);
+ }
+}
+
+void AkosRenderer::setFacing(const Actor *a) {
+ _mirror = (newDirToOldDir(a->getFacing()) != 0 || akhd->flags & 1);
+ if (a->_flip)
+ _mirror = !_mirror;
+}
+
+byte AkosRenderer::drawLimb(const Actor *a, int limb) {
+ uint code;
+ const byte *p;
+ const AkosOffset *off;
+ const CostumeData &cost = a->_cost;
+ const CostumeInfo *costumeInfo;
+ uint i, extra;
+ byte result = 0;
+ int xmoveCur, ymoveCur;
+ uint32 heCondMaskIndex[32];
+ bool useCondMask;
+ int lastDx, lastDy;
+
+ lastDx = lastDy = 0;
+ for (i = 0; i < 32; ++i) {
+ heCondMaskIndex[i] = i;
+ }
+
+ if (_skipLimbs)
+ return 0;
+
+ if (_vm->_heversion >= 70 && cost.active[limb] == 8)
+ return 0;
+
+ if (!cost.active[limb] || cost.stopped & (1 << limb))
+ return 0;
+
+ useCondMask = false;
+ p = aksq + cost.curpos[limb];
+
+ code = p[0];
+ if (code & 0x80)
+ code = (code << 8) | p[1];
+
+ if (code == AKC_C021 || code == AKC_C022) {
+ uint16 s = cost.curpos[limb] + 4;
+ uint j = 0;
+ extra = p[3];
+ uint8 n = extra;
+ assert(n < ARRAYSIZE(heCondMaskIndex));
+ while (n--) {
+ heCondMaskIndex[j++] = aksq[s++];
+ }
+ useCondMask = true;
+ p += extra + 2;
+ code = (code == AKC_C021) ? AKC_ComplexChan : AKC_ComplexChan2;
+ }
+
+ if (code == AKC_Return || code == AKC_EndSeq)
+ return 0;
+
+ if (code != AKC_ComplexChan && code != AKC_ComplexChan2) {
+ off = akof + (code & 0xFFF);
+
+ assert((code & 0xFFF) * 6 < READ_BE_UINT32((const byte *)akof - 4) - 8);
+ assert((code & 0x7000) == 0);
+
+ _srcptr = akcd + READ_LE_UINT32(&off->akcd);
+ costumeInfo = (const CostumeInfo *) (akci + READ_LE_UINT16(&off->akci));
+
+ _width = READ_LE_UINT16(&costumeInfo->width);
+ _height = READ_LE_UINT16(&costumeInfo->height);
+ xmoveCur = _xmove + (int16)READ_LE_UINT16(&costumeInfo->rel_x);
+ ymoveCur = _ymove + (int16)READ_LE_UINT16(&costumeInfo->rel_y);
+ _xmove += (int16)READ_LE_UINT16(&costumeInfo->move_x);
+ _ymove -= (int16)READ_LE_UINT16(&costumeInfo->move_y);
+
+ switch (codec) {
+ case 1:
+ result |= codec1(xmoveCur, ymoveCur);
+ break;
+ case 5:
+ result |= codec5(xmoveCur, ymoveCur);
+ break;
+ case 16:
+ result |= codec16(xmoveCur, ymoveCur);
+ break;
+ default:
+ error("akos_drawLimb: invalid codec %d", codec);
+ }
+ } else {
+ if (code == AKC_ComplexChan2) {
+ lastDx = (int16)READ_LE_UINT16(p + 2);
+ lastDy = (int16)READ_LE_UINT16(p + 4);
+ p += 4;
+ }
+
+ extra = p[2];
+ p += 3;
+ uint32 decflag = heCondMaskIndex[0];
+
+ for (i = 0; i != extra; i++) {
+ code = p[4];
+ if (code & 0x80)
+ code = ((code & 0xF) << 8) | (p[5] & 0xFFF);
+ off = akof + code;
+
+ _srcptr = akcd + READ_LE_UINT32(&off->akcd);
+ costumeInfo = (const CostumeInfo *) (akci + READ_LE_UINT16(&off->akci));
+
+ _width = READ_LE_UINT16(&costumeInfo->width);
+ _height = READ_LE_UINT16(&costumeInfo->height);
+
+ xmoveCur = _xmove + (int16)READ_LE_UINT16(p + 0);
+ ymoveCur = _ymove + (int16)READ_LE_UINT16(p + 2);
+
+ if (i == extra - 1) {
+ _xmove += lastDx;
+ _ymove -= lastDy;
+ }
+
+ uint16 shadowMask = 0;
+
+ if (!useCondMask || !akct) {
+ decflag = 1;
+ } else {
+ uint32 cond = READ_LE_UINT32(akct + cost.heCondMaskTable[limb] + heCondMaskIndex[i] * 4);
+ if (cond == 0) {
+ decflag = 1;
+ } else {
+ uint32 type = cond & ~0x3FFFFFFF;
+ cond &= 0x3FFFFFFF;
+ if (_vm->_heversion >= 90) {
+ shadowMask = cond & 0xE000;
+ cond &= ~0xE000;
+ }
+ if (_vm->_heversion >= 90 && cond == 0) {
+ decflag = 1;
+ } else if (type == 0x40000000) { // restored_bit
+ decflag = (a->_heCondMask & cond) ? 1 : 0;
+ } else if (type == 0x80000000) { // dirty_bit
+ decflag = (a->_heCondMask & cond) ? 0 : 1;
+ } else {
+ decflag = (a->_heCondMask & cond) ? 1 : 0;
+ }
+ }
+ }
+
+ p += (p[4] & 0x80) ? 6 : 5;
+
+ if (decflag == 0)
+ continue;
+
+ if (_vm->_heversion >= 90) {
+ _shadow_mode = ((shadowMask & 0x8000) && xmap) ? 3 : 0;
+ }
+
+ switch (codec) {
+ case 1:
+ result |= codec1(xmoveCur, ymoveCur);
+ break;
+ case 5:
+ result |= codec5(xmoveCur, ymoveCur);
+ break;
+ case 16:
+ result |= codec16(xmoveCur, ymoveCur);
+ break;
+ case 32:
+ result |= codec32(xmoveCur, ymoveCur);
+ break;
+ default:
+ error("akos_drawLimb: invalid codec %d", codec);
+ }
+ }
+ }
+
+ return result;
+}
+
+void AkosRenderer::codec1_genericDecode(Codec1 &v1) {
+ const byte *mask, *src;
+ byte *dst;
+ byte len, maskbit;
+ int y;
+ uint color, height, pcolor;
+ const byte *scaleytab;
+ bool masked;
+ bool skip_column = false;
+
+ y = v1.y;
+ src = _srcptr;
+ dst = v1.destptr;
+ len = v1.replen;
+ color = v1.repcolor;
+ height = _height;
+
+ scaleytab = &v1.scaletable[v1.scaleYindex];
+ maskbit = revBitMask(v1.x & 7);
+ mask = _vm->getMaskBuffer(v1.x - (_vm->virtscr[0].xstart & 7), v1.y, _zbuf);
+
+ if (len)
+ goto StartPos;
+
+ do {
+ len = *src++;
+ color = len >> v1.shr;
+ len &= v1.mask;
+ if (!len)
+ len = *src++;
+
+ do {
+ if (*scaleytab++ < _scaleY) {
+ if (_actorHitMode) {
+ if (color && y == _actorHitY && v1.x == _actorHitX) {
+ _actorHitResult = true;
+ return;
+ }
+ } else {
+ masked = (y < 0 || y >= _out.h) || (*mask & maskbit);
+
+ if (color && !masked && !skip_column) {
+ pcolor = palette[color];
+ if (_shadow_mode == 1) {
+ if (pcolor == 13)
+ pcolor = _shadow_table[*dst];
+ } else if (_shadow_mode == 2) {
+ error("codec1_spec2"); // TODO
+ } else if (_shadow_mode == 3) {
+ if (_vm->_heversion >= 90) {
+ pcolor = (pcolor << 8) + *dst;
+ pcolor = xmap[pcolor];
+ } else if (pcolor < 8) {
+ pcolor = (pcolor << 8) + *dst;
+ pcolor = _shadow_table[pcolor];
+ }
+ }
+ *dst = pcolor;
+ }
+ }
+ dst += _out.pitch;
+ mask += _numStrips;
+ y++;
+ }
+ if (!--height) {
+ if (!--v1.skip_width)
+ return;
+ height = _height;
+ y = v1.y;
+
+ scaleytab = &v1.scaletable[v1.scaleYindex];
+
+ if (_scaleX == 255 || v1.scaletable[v1.scaleXindex] < _scaleX) {
+ v1.x += v1.scaleXstep;
+ if (v1.x < 0 || v1.x >= _out.w)
+ return;
+ maskbit = revBitMask(v1.x & 7);
+ v1.destptr += v1.scaleXstep;
+ skip_column = false;
+ } else
+ skip_column = true;
+ v1.scaleXindex += v1.scaleXstep;
+ dst = v1.destptr;
+ mask = _vm->getMaskBuffer(v1.x - (_vm->virtscr[0].xstart & 7), v1.y, _zbuf);
+ }
+ StartPos:;
+ } while (--len);
+ } while (1);
+}
+
+#ifdef PALMOS_68K
+const byte *bigCostumeScaleTable;
+const byte *smallCostumeScaleTableAKOS;
+#else
+// This is exact duplicate of smallCostumeScaleTable[] in costume.cpp
+// See FIXME below for explanation
+const byte smallCostumeScaleTableAKOS[256] = {
+ 0xFF, 0xFD, 0x7D, 0xBD, 0x3D, 0xDD, 0x5D, 0x9D,
+ 0x1D, 0xED, 0x6D, 0xAD, 0x2D, 0xCD, 0x4D, 0x8D,
+ 0x0D, 0xF5, 0x75, 0xB5, 0x35, 0xD5, 0x55, 0x95,
+ 0x15, 0xE5, 0x65, 0xA5, 0x25, 0xC5, 0x45, 0x85,
+ 0x05, 0xF9, 0x79, 0xB9, 0x39, 0xD9, 0x59, 0x99,
+ 0x19, 0xE9, 0x69, 0xA9, 0x29, 0xC9, 0x49, 0x89,
+ 0x09, 0xF1, 0x71, 0xB1, 0x31, 0xD1, 0x51, 0x91,
+ 0x11, 0xE1, 0x61, 0xA1, 0x21, 0xC1, 0x41, 0x81,
+ 0x01, 0xFB, 0x7B, 0xBB, 0x3B, 0xDB, 0x5B, 0x9B,
+ 0x1B, 0xEB, 0x6B, 0xAB, 0x2B, 0xCB, 0x4B, 0x8B,
+ 0x0B, 0xF3, 0x73, 0xB3, 0x33, 0xD3, 0x53, 0x93,
+ 0x13, 0xE3, 0x63, 0xA3, 0x23, 0xC3, 0x43, 0x83,
+ 0x03, 0xF7, 0x77, 0xB7, 0x37, 0xD7, 0x57, 0x97,
+ 0x17, 0xE7, 0x67, 0xA7, 0x27, 0xC7, 0x47, 0x87,
+ 0x07, 0xEF, 0x6F, 0xAF, 0x2F, 0xCF, 0x4F, 0x8F,
+ 0x0F, 0xDF, 0x5F, 0x9F, 0x1F, 0xBF, 0x3F, 0x7F,
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE
+};
+const byte bigCostumeScaleTable[768] = {
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFE,
+
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFE,
+
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
+};
+#endif
+
+byte AkosRenderer::codec1(int xmoveCur, int ymoveCur) {
+ int num_colors;
+ bool use_scaling;
+ int i, j;
+ int skip = 0, startScaleIndexX, startScaleIndexY;
+ Common::Rect rect;
+ int step;
+ byte drawFlag = 1;
+ Codec1 v1;
+
+ const int scaletableSize = (_vm->_heversion >= 61) ? 128 : 384;
+
+ /* implement custom scale table */
+
+ // FIXME. HACK
+ // For some illogical reason gcc 3.4.x produces wrong code if
+ // smallCostumeScaleTable from costume.cpp is used here
+ // So I had to put copy of it back here as it was before 1.227 revision
+ // of this file.
+ v1.scaletable = (_vm->_heversion >= 61) ? smallCostumeScaleTableAKOS : bigCostumeScaleTable;
+ if (_vm->VAR_CUSTOMSCALETABLE != 0xFF && _vm->res.isResourceLoaded(rtString, _vm->VAR(_vm->VAR_CUSTOMSCALETABLE))) {
+ v1.scaletable = _vm->getStringAddressVar(_vm->VAR_CUSTOMSCALETABLE);
+ }
+
+ // Setup color decoding variables
+ num_colors = _vm->getResourceDataSize(akpl);
+ if (num_colors == 32) {
+ v1.mask = 7;
+ v1.shr = 3;
+ } else if (num_colors == 64) {
+ v1.mask = 3;
+ v1.shr = 2;
+ } else {
+ v1.mask = 15;
+ v1.shr = 4;
+ }
+
+ use_scaling = (_scaleX != 0xFF) || (_scaleY != 0xFF);
+
+ v1.x = _actorX;
+ v1.y = _actorY;
+
+ if (use_scaling) {
+
+ /* Scale direction */
+ v1.scaleXstep = -1;
+ if (xmoveCur < 0) {
+ xmoveCur = -xmoveCur;
+ v1.scaleXstep = 1;
+ }
+
+ if (_mirror) {
+ /* Adjust X position */
+ startScaleIndexX = j = scaletableSize - xmoveCur;
+ for (i = 0; i < xmoveCur; i++) {
+ if (v1.scaletable[j++] < _scaleX)
+ v1.x -= v1.scaleXstep;
+ }
+
+ rect.left = rect.right = v1.x;
+
+ j = startScaleIndexX;
+ for (i = 0, skip = 0; i < _width; i++) {
+ if (rect.right < 0) {
+ skip++;
+ startScaleIndexX = j;
+ }
+ if (v1.scaletable[j++] < _scaleX)
+ rect.right++;
+ }
+ } else {
+ /* No mirror */
+ /* Adjust X position */
+ startScaleIndexX = j = scaletableSize + xmoveCur;
+ for (i = 0; i < xmoveCur; i++) {
+ if (v1.scaletable[j--] < _scaleX)
+ v1.x += v1.scaleXstep;
+ }
+
+ rect.left = rect.right = v1.x;
+
+ j = startScaleIndexX;
+ for (i = 0; i < _width; i++) {
+ if (rect.left >= _out.w) {
+ startScaleIndexX = j;
+ skip++;
+ }
+ if (v1.scaletable[j--] < _scaleX)
+ rect.left--;
+ }
+ }
+
+ if (skip)
+ skip--;
+
+ step = -1;
+ if (ymoveCur < 0) {
+ ymoveCur = -ymoveCur;
+ step = -step;
+ }
+
+ startScaleIndexY = scaletableSize - ymoveCur;
+ for (i = 0; i < ymoveCur; i++) {
+ if (v1.scaletable[startScaleIndexY++] < _scaleY)
+ v1.y -= step;
+ }
+
+ rect.top = rect.bottom = v1.y;
+ startScaleIndexY = scaletableSize - ymoveCur;
+ for (i = 0; i < _height; i++) {
+ if (v1.scaletable[startScaleIndexY++] < _scaleY)
+ rect.bottom++;
+ }
+
+ startScaleIndexY = scaletableSize - ymoveCur;
+ } else {
+ if (!_mirror)
+ xmoveCur = -xmoveCur;
+
+ v1.x += xmoveCur;
+ v1.y += ymoveCur;
+
+ if (_mirror) {
+ rect.left = v1.x;
+ rect.right = v1.x + _width;
+ } else {
+ rect.left = v1.x - _width;
+ rect.right = v1.x;
+ }
+
+ rect.top = v1.y;
+ rect.bottom = rect.top + _height;
+
+ startScaleIndexX = scaletableSize;
+ startScaleIndexY = scaletableSize;
+ }
+
+ v1.scaleXindex = startScaleIndexX;
+ v1.scaleYindex = startScaleIndexY;
+ v1.skip_width = _width;
+ v1.scaleXstep = _mirror ? 1 : -1;
+
+ if (_vm->_heversion >= 71) {
+ if (_clipOverride.right > _clipOverride.left && _clipOverride.bottom > _clipOverride.top) {
+ if (rect.left < _clipOverride.left)
+ rect.left = _clipOverride.left;
+
+ if (rect.right > _clipOverride.right)
+ rect.right = _clipOverride.right;
+
+ if (rect.top < _clipOverride.top)
+ rect.top = _clipOverride.top;
+
+ if (rect.bottom > _clipOverride.bottom)
+ rect.bottom = _clipOverride.bottom;
+ }
+
+ if (rect.isValidRect() == false)
+ return 1;
+ }
+
+ if (_actorHitMode) {
+ if (_actorHitX < rect.left || _actorHitX >= rect.right || _actorHitY < rect.top || _actorHitY >= rect.bottom)
+ return 0;
+ } else
+ markRectAsDirty(rect);
+
+ if (rect.top >= _out.h || rect.bottom <= 0)
+ return 0;
+
+ if (rect.left >= _out.w || rect.right <= 0)
+ return 0;
+
+ v1.replen = 0;
+
+ if (_mirror) {
+ if (!use_scaling)
+ skip = -v1.x;
+ if (skip > 0) {
+ v1.skip_width -= skip;
+ codec1_ignorePakCols(v1, skip);
+ v1.x = 0;
+ } else {
+ skip = rect.right - _out.w;
+ if (skip <= 0) {
+ drawFlag = 2;
+ } else {
+ v1.skip_width -= skip;
+ }
+ }
+ } else {
+ if (!use_scaling)
+ skip = rect.right - _out.w + 1;
+ if (skip > 0) {
+ v1.skip_width -= skip;
+ codec1_ignorePakCols(v1, skip) ;
+ v1.x = _out.w - 1;
+ } else {
+ skip = -1 - rect.left;
+ if (skip <= 0)
+ drawFlag = 2;
+ else
+ v1.skip_width -= skip;
+ }
+ }
+
+ if (v1.skip_width <= 0 || _height <= 0)
+ return 0;
+
+ if (rect.left < 0)
+ rect.left = 0;
+
+ if (rect.top < 0)
+ rect.top = 0;
+
+ if (rect.top > _out.h)
+ rect.top = _out.h;
+
+ if (rect.bottom > _out.h)
+ rect.bottom = _out.h;
+
+ if (_draw_top > rect.top)
+ _draw_top = rect.top;
+ if (_draw_bottom < rect.bottom)
+ _draw_bottom = rect.bottom;
+
+ v1.destptr = (byte *)_out.pixels + v1.y * _out.pitch + v1.x;
+
+ codec1_genericDecode(v1);
+
+ return drawFlag;
+}
+
+void AkosRenderer::markRectAsDirty(Common::Rect rect) {
+ rect.left -= _vm->virtscr[0].xstart & 7;
+ rect.right -= _vm->virtscr[0].xstart & 7;
+ _vm->markRectAsDirty(kMainVirtScreen, rect, _actorID);
+}
+
+byte AkosRenderer::codec5(int xmoveCur, int ymoveCur) {
+ Common::Rect clip;
+ int32 maxw, maxh;
+
+ if (_actorHitMode) {
+ error("codec5: _actorHitMode not yet implemented");
+ return 0;
+ }
+
+ if (!_mirror) {
+ clip.left = (_actorX - xmoveCur - _width) + 1;
+ } else {
+ clip.left = _actorX + xmoveCur - 1;
+ }
+
+ clip.top = _actorY + ymoveCur;
+ clip.right = clip.left + _width;
+ clip.bottom = clip.top + _height;
+ maxw = _out.w;
+ maxh = _out.h;
+
+ markRectAsDirty(clip);
+
+ clip.clip(maxw, maxh);
+
+ if ((clip.left >= clip.right) || (clip.top >= clip.bottom))
+ return 0;
+
+ if (_draw_top > clip.top)
+ _draw_top = clip.top;
+ if (_draw_bottom < clip.bottom)
+ _draw_bottom = clip.bottom;
+
+ BompDrawData bdd;
+
+ bdd.srcwidth = _width;
+ bdd.srcheight = _height;
+ bdd.dst = _out;
+ bdd.dataptr = _srcptr;
+ bdd.scale_x = 255;
+ bdd.scale_y = 255;
+ bdd.shadowMode = _shadow_mode;
+
+ if (!_mirror) {
+ bdd.x = (_actorX - xmoveCur - _width) + 1;
+ } else {
+ bdd.x = _actorX + xmoveCur;
+ }
+ bdd.y = _actorY + ymoveCur;
+
+ bdd.maskPtr = _vm->getMaskBuffer(0, 0, _zbuf);
+ _vm->drawBomp(bdd, !_mirror);
+
+ _vm->_bompActorPalettePtr = NULL;
+
+ return 0;
+}
+
+void AkosRenderer::akos16SetupBitReader(const byte *src) {
+ akos16.unk5 = 0;
+ akos16.numbits = 16;
+ akos16.mask = (1 << *src) - 1;
+ akos16.shift = *(src);
+ akos16.color = *(src + 1);
+ akos16.bits = (*(src + 2) | *(src + 3) << 8);
+ akos16.dataptr = src + 4;
+}
+
+#define AKOS16_FILL_BITS() \
+ if (akos16.numbits <= 8) { \
+ akos16.bits |= (*akos16.dataptr++) << akos16.numbits; \
+ akos16.numbits += 8; \
+ }
+
+#define AKOS16_EAT_BITS(n) \
+ akos16.numbits -= (n); \
+ akos16.bits >>= (n);
+
+
+void AkosRenderer::akos16SkipData(int32 numbytes) {
+ akos16DecodeLine(0, numbytes, 0);
+}
+
+void AkosRenderer::akos16DecodeLine(byte *buf, int32 numbytes, int32 dir) {
+ uint16 bits, tmp_bits;
+
+ while (numbytes != 0) {
+ if (buf) {
+ *buf = akos16.color;
+ buf += dir;
+ }
+
+ if (akos16.unk5 == 0) {
+ AKOS16_FILL_BITS()
+ bits = akos16.bits & 3;
+ if (bits & 1) {
+ AKOS16_EAT_BITS(2)
+ if (bits & 2) {
+ tmp_bits = akos16.bits & 7;
+ AKOS16_EAT_BITS(3)
+ if (tmp_bits != 4) {
+ akos16.color += (tmp_bits - 4);
+ } else {
+ akos16.unk5 = 1;
+ AKOS16_FILL_BITS()
+ akos16.unk6 = (akos16.bits & 0xff) - 1;
+ AKOS16_EAT_BITS(8)
+ AKOS16_FILL_BITS()
+ }
+ } else {
+ AKOS16_FILL_BITS()
+ akos16.color = ((byte)akos16.bits) & akos16.mask;
+ AKOS16_EAT_BITS(akos16.shift)
+ AKOS16_FILL_BITS()
+ }
+ } else {
+ AKOS16_EAT_BITS(1);
+ }
+ } else {
+ if (--akos16.unk6 == 0) {
+ akos16.unk5 = 0;
+ }
+ }
+ numbytes--;
+ }
+}
+
+void AkosRenderer::akos16Decompress(byte *dest, int32 pitch, const byte *src, int32 t_width, int32 t_height, int32 dir,
+ int32 numskip_before, int32 numskip_after, byte transparency, int maskLeft, int maskTop, int zBuf) {
+ byte *tmp_buf = akos16.buffer;
+ int maskpitch;
+ byte *maskptr;
+ const byte maskbit = revBitMask(maskLeft & 7);
+
+ if (dir < 0) {
+ dest -= (t_width - 1);
+ tmp_buf += (t_width - 1);
+ }
+
+ akos16SetupBitReader(src);
+
+ if (numskip_before != 0) {
+ akos16SkipData(numskip_before);
+ }
+
+ maskpitch = _numStrips;
+
+ maskptr = _vm->getMaskBuffer(maskLeft, maskTop, zBuf);
+
+ assert(t_height > 0);
+ assert(t_width > 0);
+ while (t_height--) {
+ akos16DecodeLine(tmp_buf, t_width, dir);
+ bompApplyMask(akos16.buffer, maskptr, maskbit, t_width, transparency);
+ bool HE7Check = (_vm->_heversion == 70);
+ bompApplyShadow(_shadow_mode, _shadow_table, akos16.buffer, dest, t_width, transparency, HE7Check);
+
+ if (numskip_after != 0) {
+ akos16SkipData(numskip_after);
+ }
+ dest += pitch;
+ maskptr += maskpitch;
+ }
+}
+
+byte AkosRenderer::codec16(int xmoveCur, int ymoveCur) {
+ Common::Rect clip;
+ int32 minx, miny, maxw, maxh;
+ int32 skip_x, skip_y, cur_x, cur_y;
+ byte transparency = (_vm->_heversion >= 61) ? palette[0] : 255;
+
+ if (_actorHitMode) {
+ error("codec16: _actorHitMode not yet implemented");
+ return 0;
+ }
+
+ if (!_mirror) {
+ clip.left = (_actorX - xmoveCur - _width) + 1;
+ } else {
+ clip.left = _actorX + xmoveCur;
+ }
+
+ clip.top = _actorY + ymoveCur;
+ clip.right = clip.left + _width;
+ clip.bottom = clip.top + _height;
+
+ minx = miny = 0;
+ maxw = _out.w;
+ maxh = _out.h;
+
+ if (_vm->_heversion >= 71) {
+ if (_clipOverride.right > _clipOverride.left && _clipOverride.bottom > _clipOverride.top) {
+ minx = _clipOverride.left;
+ miny = _clipOverride.top;
+ maxw = _clipOverride.right;
+ maxh = _clipOverride.bottom;
+ }
+ }
+
+ markRectAsDirty(clip);
+
+ skip_x = 0;
+ skip_y = 0;
+ cur_x = _width - 1;
+ cur_y = _height - 1;
+
+ if (clip.left < minx) {
+ skip_x = -clip.left;
+ clip.left = 0;
+ }
+
+ if (clip.right > maxw) {
+ cur_x -= clip.right - maxw;
+ clip.right = maxw;
+ }
+
+ if (clip.top < miny) {
+ skip_y -= clip.top;
+ clip.top = 0;
+ }
+
+ if (clip.bottom > maxh) {
+ cur_y -= clip.bottom - maxh;
+ clip.bottom = maxh;
+ }
+
+ if ((clip.left >= clip.right) || (clip.top >= clip.bottom))
+ return 0;
+
+ if (_draw_top > clip.top)
+ _draw_top = clip.top;
+ if (_draw_bottom < clip.bottom)
+ _draw_bottom = clip.bottom;
+
+ int32 width_unk, height_unk;
+
+ height_unk = clip.top;
+ int32 dir;
+
+ if (!_mirror) {
+ dir = -1;
+
+ int tmp_skip_x = skip_x;
+ skip_x = _width - 1 - cur_x;
+ cur_x = _width - 1 - tmp_skip_x;
+ width_unk = clip.right - 1;
+ } else {
+ dir = 1;
+ width_unk = clip.left;
+ }
+
+ int32 out_height;
+
+ out_height = cur_y - skip_y;
+ if (out_height < 0) {
+ out_height = -out_height;
+ }
+ out_height++;
+
+ cur_x -= skip_x;
+ if (cur_x < 0) {
+ cur_x = -cur_x;
+ }
+ cur_x++;
+
+ int32 numskip_before = skip_x + (skip_y * _width);
+ int32 numskip_after = _width - cur_x;
+
+ byte *dst = (byte *)_out.pixels + width_unk + height_unk * _out.pitch;
+
+ akos16Decompress(dst, _out.pitch, _srcptr, cur_x, out_height, dir, numskip_before, numskip_after, transparency, clip.left, clip.top, _zbuf);
+ return 0;
+}
+
+byte AkosRenderer::codec32(int xmoveCur, int ymoveCur) {
+#ifndef DISABLE_HE
+ Common::Rect src, dst;
+
+ if (!_mirror) {
+ dst.left = (_actorX - xmoveCur - _width) + 1;
+ } else {
+ dst.left = _actorX + xmoveCur;
+ }
+
+ src.top = src.left = 0;
+ src.right = _width;
+ src.bottom = _height;
+
+ dst.top = _actorY + ymoveCur;
+ dst.right = dst.left + _width;
+ dst.bottom = dst.top + _height;
+
+ int diff;
+ diff = dst.left - _clipOverride.left;
+ if (diff < 0) {
+ src.left -= diff;
+ dst.left -= diff;
+ }
+ diff = dst.right - _clipOverride.right;
+ if (diff > 0) {
+ src.right -= diff;
+ dst.right -= diff;
+ }
+ diff = dst.top - _clipOverride.top;
+ if (diff < 0) {
+ src.top -= diff;
+ dst.top -= diff;
+ }
+ diff = dst.bottom - _clipOverride.bottom;
+ if (diff > 0) {
+ src.bottom -= diff;
+ dst.bottom -= diff;
+ }
+
+ if (dst.isValidRect() == false)
+ return 0;
+
+ markRectAsDirty(dst);
+
+ if (_draw_top > dst.top)
+ _draw_top = dst.top;
+ if (_draw_bottom < dst.bottom)
+ _draw_bottom = dst.bottom;
+
+ const uint8 *palPtr = NULL;
+ if (_vm->_heversion >= 99) {
+ palPtr = _vm->_hePalettes + 1792;
+ }
+
+ byte *dstPtr = (byte *)_out.pixels + dst.left + dst.top * _out.pitch;
+ if (_shadow_mode == 3) {
+ Wiz::decompressWizImage(dstPtr, _out.pitch, dst, _srcptr, src, 0, palPtr, xmap);
+ } else {
+ Wiz::decompressWizImage(dstPtr, _out.pitch, dst, _srcptr, src, 0, palPtr);
+ }
+#endif
+ return 0;
+}
+
+byte AkosCostumeLoader::increaseAnims(Actor *a) {
+ return _vm->akos_increaseAnims(_akos, a);
+}
+
+bool ScummEngine::akos_increaseAnims(const byte *akos, Actor *a) {
+ const byte *aksq, *akfo;
+ int i;
+ uint size;
+ bool result;
+
+ aksq = findResourceData(MKID('AKSQ'), akos);
+ akfo = findResourceData(MKID('AKFO'), akos);
+
+ size = getResourceDataSize(akfo) / 2;
+
+ result = false;
+ for (i = 0; i < 16; i++) {
+ if (a->_cost.active[i] != 0)
+ result |= akos_increaseAnim(a, i, aksq, (const uint16 *)akfo, size);
+ }
+ return result;
+}
+
+#define GW(o) ((int16)READ_LE_UINT16(aksq+curpos+(o)))
+#define GUW(o) READ_LE_UINT16(aksq+curpos+(o))
+#define GB(o) aksq[curpos+(o)]
+
+bool ScummEngine::akos_increaseAnim(Actor *a, int chan, const byte *aksq, const uint16 *akfo, int numakfo) {
+ byte active;
+ uint old_curpos, curpos, end;
+ uint code;
+ bool flag_value, needRedraw;
+ int tmp, tmp2;
+
+ active = a->_cost.active[chan];
+ end = a->_cost.end[chan];
+ old_curpos = curpos = a->_cost.curpos[chan];
+ flag_value = false;
+ needRedraw = false;
+
+ do {
+
+ code = aksq[curpos];
+ if (code & 0x80)
+ code = (code << 8) | aksq[curpos + 1];
+
+ switch (active) {
+ case 6:
+ case 8:
+ switch (code) {
+ case AKC_JumpIfSet:
+ case AKC_AddVar:
+ case AKC_SetVar:
+ case AKC_SkipGE:
+ case AKC_SkipG:
+ case AKC_SkipLE:
+ case AKC_SkipL:
+
+ case AKC_SkipNE:
+ case AKC_SkipE:
+ case AKC_C016:
+ case AKC_C017:
+ case AKC_C018:
+ case AKC_C019:
+ curpos += 5;
+ break;
+ case AKC_JumpTable:
+ case AKC_SetActorClip:
+ case AKC_Ignore3:
+ case AKC_Ignore2:
+ case AKC_Ignore:
+ case AKC_StartAnim:
+ case AKC_StartVarAnim:
+ case AKC_CmdQue3:
+ case AKC_C042:
+ case AKC_C044:
+ case AKC_C0A3:
+ curpos += 3;
+ break;
+ case AKC_SoundStuff:
+ if (_heversion >= 61)
+ curpos += 6;
+ else
+ curpos += 8;
+ break;
+ case AKC_Cmd3:
+ case AKC_SetVarInActor:
+ case AKC_SetDrawOffs:
+ curpos += 6;
+ break;
+ case AKC_ClearFlag:
+ case AKC_HideActor:
+ case AKC_IncVar:
+ case AKC_CmdQue3Quick:
+ case AKC_Return:
+ case AKC_EndSeq:
+ curpos += 2;
+ break;
+ case AKC_JumpGE:
+ case AKC_JumpG:
+ case AKC_JumpLE:
+ case AKC_JumpL:
+ case AKC_JumpNE:
+ case AKC_JumpE:
+ case AKC_Random:
+ curpos += 7;
+ break;
+ case AKC_Flip:
+ case AKC_Jump:
+ case AKC_StartAnimInActor:
+ case AKC_C0A0:
+ case AKC_C0A1:
+ case AKC_C0A2:
+ curpos += 4;
+ break;
+ case AKC_ComplexChan2:
+ curpos += 4;
+ // Fall through
+ case AKC_ComplexChan:
+ curpos += 3;
+ tmp = aksq[curpos - 1];
+ while (--tmp >= 0) {
+ curpos += 4;
+ curpos += (aksq[curpos] & 0x80) ? 2 : 1;
+ }
+ break;
+ case AKC_C021:
+ case AKC_C022:
+ case AKC_C045:
+ case AKC_C046:
+ case AKC_C047:
+ case AKC_C048:
+ needRedraw = 1;
+ curpos += aksq[curpos + 2];
+ break;
+ case AKC_C08E:
+ akos_queCommand(7, a, GW(2), 0);
+ curpos += 4;
+ break;
+ default:
+ if ((code & 0xC000) == 0xC000)
+ error("akos_increaseAnim: invalid code %x", code);
+ curpos += (code & 0x8000) ? 2 : 1;
+ break;
+ }
+ break;
+ case 2:
+ curpos += (code & 0x8000) ? 2 : 1;
+ if (curpos > end)
+ curpos = a->_cost.start[chan];
+ break;
+ case 3:
+ if (curpos != end)
+ curpos += (code & 0x8000) ? 2 : 1;
+ break;
+ }
+
+ code = aksq[curpos];
+ if (code & 0x80)
+ code = (code << 8) | aksq[curpos + 1];
+
+ if (flag_value && code != AKC_ClearFlag)
+ continue;
+
+ switch (code) {
+ case AKC_StartAnimInActor:
+ akos_queCommand(4, derefActor(a->getAnimVar(GB(2)), "akos_increaseAnim:29"), a->getAnimVar(GB(3)), 0);
+ continue;
+
+ case AKC_Random:
+ a->setAnimVar(GB(6), _rnd.getRandomNumberRng(GW(2), GW(4)));
+ continue;
+ case AKC_JumpGE:
+ case AKC_JumpG:
+ case AKC_JumpLE:
+ case AKC_JumpL:
+ case AKC_JumpNE:
+ case AKC_JumpE:
+ if (akos_compare(a->getAnimVar(GB(4)), GW(5), code - AKC_JumpStart) != 0) {
+ curpos = GUW(2);
+ break;
+ }
+ continue;
+ case AKC_IncVar:
+ a->setAnimVar(0, a->getAnimVar(0) + 1);
+ continue;
+ case AKC_SetVar:
+ a->setAnimVar(GB(4), GW(2));
+ continue;
+ case AKC_AddVar:
+ a->setAnimVar(GB(4), a->getAnimVar(GB(4)) + GW(2));
+ continue;
+ case AKC_Flip:
+ a->_flip = GW(2) != 0;
+ continue;
+ case AKC_CmdQue3:
+ if (_heversion >= 61)
+ tmp = GB(2);
+ else
+ tmp = GB(2) - 1;
+ if ((uint) tmp < 24)
+ akos_queCommand(3, a, a->_sound[tmp], 0);
+ continue;
+ case AKC_CmdQue3Quick:
+ akos_queCommand(3, a, a->_sound[0], 0);
+ continue;
+ case AKC_StartAnim:
+ akos_queCommand(4, a, GB(2), 0);
+ continue;
+ case AKC_StartVarAnim:
+ akos_queCommand(4, a, a->getAnimVar(GB(2)), 0);
+ continue;
+ case AKC_SetVarInActor:
+ derefActor(a->getAnimVar(GB(2)), "akos_increaseAnim:9")->setAnimVar(GB(3), GW(4));
+ continue;
+ case AKC_HideActor:
+ akos_queCommand(1, a, 0, 0);
+ continue;
+ case AKC_SetActorClip:
+ akos_queCommand(5, a, GB(2), 0);
+ continue;
+ case AKC_SoundStuff:
+ if (_heversion >= 61)
+ continue;
+ tmp = GB(2) - 1;
+ if (tmp >= 8)
+ continue;
+ tmp2 = GB(4);
+ if (tmp2 < 1 || tmp2 > 3)
+ error("akos_increaseAnim:8 invalid code %d", tmp2);
+ akos_queCommand(tmp2 + 6, a, a->_sound[tmp], GB(6));
+ continue;
+ case AKC_SetDrawOffs:
+ akos_queCommand(6, a, GW(2), GW(4));
+ continue;
+ case AKC_JumpTable:
+ if (akfo == NULL)
+ error("akos_increaseAnim: no AKFO table");
+ tmp = a->getAnimVar(GB(2)) - 1;
+ if (_heversion >= 80) {
+ if (tmp < 0 || tmp > a->_cost.heJumpCountTable[chan] - 1)
+ error("akos_increaseAnim: invalid jump value %d", tmp);
+ curpos = READ_LE_UINT16(akfo + a->_cost.heJumpOffsetTable[chan] + tmp * 2);
+ } else {
+ if (tmp < 0 || tmp > numakfo - 1)
+ error("akos_increaseAnim: invalid jump value %d", tmp);
+ curpos = READ_LE_UINT16(&akfo[tmp]);
+ }
+ break;
+ case AKC_JumpIfSet:
+ if (!a->getAnimVar(GB(4)))
+ continue;
+ a->setAnimVar(GB(4), 0);
+ curpos = GUW(2);
+ break;
+
+ case AKC_ClearFlag:
+ flag_value = false;
+ continue;
+
+ case AKC_Jump:
+ curpos = GUW(2);
+ break;
+
+ case AKC_Return:
+ case AKC_EndSeq:
+ case AKC_ComplexChan:
+ case AKC_C08E:
+ case AKC_ComplexChan2:
+ break;
+
+ case AKC_C021:
+ case AKC_C022:
+ needRedraw = 1;
+ break;
+
+ case AKC_Cmd3:
+ case AKC_Ignore:
+ case AKC_Ignore3:
+ continue;
+
+ case AKC_Ignore2:
+ if (_heversion >= 71)
+ akos_queCommand(3, a, a->_sound[a->getAnimVar(GB(2))], 0);
+ continue;
+
+ case AKC_SkipE:
+ case AKC_SkipNE:
+ case AKC_SkipL:
+ case AKC_SkipLE:
+ case AKC_SkipG:
+ case AKC_SkipGE:
+ if (akos_compare(a->getAnimVar(GB(4)), GW(2), code - AKC_SkipStart) == 0)
+ flag_value = true;
+ continue;
+ case AKC_C016:
+ if (_sound->isSoundRunning( a->_sound[a->getAnimVar(GB(4))])) {
+ curpos = GUW(2);
+ break;
+ }
+ continue;
+ case AKC_C017:
+ if (!_sound->isSoundRunning(a->_sound[a->getAnimVar(GB(4))])) {
+ curpos = GUW(2);
+ break;
+ }
+ continue;
+ case AKC_C018:
+ if (_sound->isSoundRunning(a->_sound[GB(4)])) {
+ curpos = GUW(2);
+ break;
+ }
+ continue;
+ case AKC_C019:
+ if (!_sound->isSoundRunning(a->_sound[GB(4)])) {
+ curpos = GUW(2);
+ break;
+ }
+ continue;
+ case AKC_C042:
+ akos_queCommand(9, a, a->_sound[GB(2)], 0);
+ continue;
+ case AKC_C044:
+ akos_queCommand(9, a, a->_sound[a->getAnimVar(GB(2))], 0);
+ continue;
+ case AKC_C045:
+ a->setUserCondition(GB(3), a->getAnimVar(GB(4)));
+ continue;
+ case AKC_C046:
+ a->setAnimVar(GB(4), a->isUserConditionSet(GB(3)));
+ continue;
+ case AKC_C047:
+ a->setTalkCondition(GB(3));
+ continue;
+ case AKC_C048:
+ a->setAnimVar(GB(4), a->isTalkConditionSet(GB(3)));
+ continue;
+ case AKC_C0A0:
+ akos_queCommand(8, a, GB(2), 0);
+ continue;
+ case AKC_C0A1:
+ if (a->_heTalking != 0) {
+ curpos = GUW(2);
+ break;
+ }
+ continue;
+ case AKC_C0A2:
+ if (a->_heTalking == 0) {
+ curpos = GUW(2);
+ break;
+ }
+ continue;
+ case AKC_C0A3:
+ akos_queCommand(8, a, a->getAnimVar(GB(2)), 0);
+ continue;
+ case AKC_C0A4:
+ if (VAR(VAR_TALK_ACTOR) != 0) {
+ curpos = GUW(2);
+ break;
+ }
+ continue;
+ case AKC_C0A5:
+ if (VAR(VAR_TALK_ACTOR) == 0) {
+ curpos = GUW(2);
+ break;
+ }
+ continue;
+ default:
+ if ((code & 0xC000) == 0xC000)
+ error("Undefined uSweat token %X", code);
+ }
+ break;
+ } while (1);
+
+ int code2 = aksq[curpos];
+ if (code2 & 0x80)
+ code2 = (code2 << 8) | aksq[curpos + 1];
+
+ assert((code2 & 0xC000) != 0xC000 || code2 == AKC_ComplexChan || code2 == AKC_Return || code2 == AKC_EndSeq || code2 == AKC_C08E || code2 == AKC_ComplexChan2 || code2 == AKC_C021 || code2 == AKC_C022);
+
+ a->_cost.curpos[chan] = curpos;
+
+ if (needRedraw)
+ return 1;
+ else
+ return curpos != old_curpos;
+}
+
+void ScummEngine::akos_queCommand(byte cmd, Actor *a, int param_1, int param_2) {
+ _akosQueuePos++;
+ checkRange(31, 0, _akosQueuePos, "akos_queCommand overflow");
+
+ _akosQueue[_akosQueuePos].cmd = cmd;
+ _akosQueue[_akosQueuePos].actor = a->_number;
+ _akosQueue[_akosQueuePos].param1 = param_1;
+ _akosQueue[_akosQueuePos].param2 = param_2;
+}
+
+void ScummEngine::akos_processQueue() {
+ byte cmd;
+ int actor, param_1, param_2;
+
+ while (_akosQueuePos) {
+ cmd = _akosQueue[_akosQueuePos].cmd;
+ actor = _akosQueue[_akosQueuePos].actor;
+ param_1 = _akosQueue[_akosQueuePos].param1;
+ param_2 = _akosQueue[_akosQueuePos].param2;
+ _akosQueuePos--;
+
+ Actor *a = derefActor(actor, "akos_processQueue");
+
+ switch (cmd) {
+ case 1:
+ a->putActor(0, 0, 0);
+ break;
+ case 3:
+ _sound->addSoundToQueue(param_1, 0, -1, 0);
+ break;
+ case 4:
+ a->startAnimActor(param_1);
+ break;
+ case 5:
+ a->_forceClip = param_1;
+ break;
+ case 6:
+ a->_offsX = param_1;
+ a->_offsY = param_2;
+ break;
+ case 7:
+#ifndef DISABLE_HE
+ assert(_heversion >= 71);
+ ((ScummEngine_v71he *)this)->queueAuxEntry(a->_number, param_1);
+#endif
+ break;
+ case 8:
+ _actorToPrintStrFor = a->_number;
+
+ a->_talkPosX = a->_heTalkQueue[param_1].posX;
+ a->_talkPosY = a->_heTalkQueue[param_1].posY;
+ a->_talkColor = a->_heTalkQueue[param_1].color;
+
+ _string[0].loadDefault();
+ _string[0].color = a->_talkColor;
+ actorTalk(a->_heTalkQueue[param_1].sentence);
+
+ break;
+ case 9:
+ _sound->addSoundToQueue(param_1, 0, -1, 4);
+ break;
+ default:
+ error("akos_queCommand(%d,%d,%d,%d)", cmd, a->_number, param_1, param_2);
+ }
+ }
+}
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::akos_processQueue() {
+ byte cmd;
+ int actor, param_1, param_2;
+
+ while (_akosQueuePos) {
+ cmd = _akosQueue[_akosQueuePos].cmd;
+ actor = _akosQueue[_akosQueuePos].actor;
+ param_1 = _akosQueue[_akosQueuePos].param1;
+ param_2 = _akosQueue[_akosQueuePos].param2;
+ _akosQueuePos--;
+
+ Actor *a = derefActor(actor, "akos_processQueue");
+
+ switch (cmd) {
+ case 1:
+ a->putActor(0, 0, 0);
+ break;
+ case 3:
+ if (param_1 != 0) {
+ if (_imuseDigital) {
+ _imuseDigital->startSfx(param_1, 63);
+ }
+ }
+ break;
+ case 4:
+ a->startAnimActor(param_1);
+ break;
+ case 5:
+ a->_forceClip = param_1;
+ break;
+ case 6:
+ a->_offsX = param_1;
+ a->_offsY = param_2;
+ break;
+ case 7:
+ if (param_1 != 0) {
+ if (_imuseDigital) {
+ _imuseDigital->setVolume(param_1, param_2);
+ }
+ }
+ break;
+ case 8:
+ if (param_1 != 0) {
+ if (_imuseDigital) {
+ _imuseDigital->setPan(param_1, param_2);
+ }
+ }
+ break;
+ case 9:
+ if (param_1 != 0) {
+ if (_imuseDigital) {
+ _imuseDigital->setPriority(param_1, param_2);
+ }
+ }
+ break;
+ default:
+ error("akos_queCommand(%d,%d,%d,%d)", cmd, a->_number, param_1, param_2);
+ }
+ }
+}
+#endif
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Akos)
+_GSETPTR(Scumm::bigCostumeScaleTable, GBVARS_BIGSCALETABLE_INDEX, byte, GBVARS_SCUMM)
+//_GSETPTR(Scumm::smallCostumeScaleTableAKOS, GBVARS_SMALLSCALETABLEAKOS_INDEX, byte, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(Akos)
+_GRELEASEPTR(GBVARS_BIGSCALETABLE_INDEX, GBVARS_SCUMM)
+//_GRELEASEPTR(GBVARS_SMALLSCALETABLEAKOS_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/akos.h b/engines/scumm/akos.h
new file mode 100644
index 0000000000..1a849903d8
--- /dev/null
+++ b/engines/scumm/akos.h
@@ -0,0 +1,125 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef AKOS_H
+#define AKOS_H
+
+#include "scumm/base-costume.h"
+
+namespace Scumm {
+
+struct CostumeData;
+struct AkosHeader;
+struct AkosOffset;
+
+class AkosCostumeLoader : public BaseCostumeLoader {
+protected:
+ const byte *_akos;
+
+public:
+ AkosCostumeLoader(ScummEngine *vm) : BaseCostumeLoader(vm) {}
+
+ void loadCostume(int id);
+ byte increaseAnims(Actor *a);
+ void costumeDecodeData(Actor *a, int frame, uint usemask);
+
+ //void animateLimb(int limb, int f);
+ bool hasManyDirections(int id) {
+ loadCostume(id);
+ return hasManyDirections();
+ }
+
+protected:
+ bool hasManyDirections();
+};
+
+class AkosRenderer : public BaseCostumeRenderer {
+protected:
+ uint16 codec;
+
+ // actor palette
+ byte palette[256];
+
+ // pointer to various parts of the costume resource
+ const byte *akos;
+ const AkosHeader *akhd;
+
+ const byte *akpl, *akci, *aksq;
+ const AkosOffset *akof;
+ const byte *akcd;
+ const byte *akct;
+ const uint8 *xmap;
+
+ struct {
+ byte unk5;
+ int unk6;
+ byte mask;
+ byte color;
+ byte shift;
+ uint16 bits;
+ byte numbits;
+ const byte *dataptr;
+ byte buffer[336];
+ } akos16;
+
+public:
+ AkosRenderer(ScummEngine *scumm) : BaseCostumeRenderer(scumm) {
+ akos = 0;
+ akhd = 0;
+ akpl = 0;
+ akci = 0;
+ aksq = 0;
+ akof = 0;
+ akcd = 0;
+ akct = 0;
+ xmap = 0;
+ _actorHitMode = false;
+ }
+
+ bool _actorHitMode;
+ int16 _actorHitX, _actorHitY;
+ bool _actorHitResult;
+
+ void setPalette(byte *palette);
+ void setFacing(const Actor *a);
+ void setCostume(int costume, int shadow);
+
+protected:
+ byte drawLimb(const Actor *a, int limb);
+
+ byte codec1(int xmoveCur, int ymoveCur);
+ void codec1_genericDecode(Codec1 &v1);
+ byte codec5(int xmoveCur, int ymoveCur);
+ byte codec16(int xmoveCur, int ymoveCur);
+ byte codec32(int xmoveCur, int ymoveCur);
+ void akos16SetupBitReader(const byte *src);
+ void akos16SkipData(int32 numskip);
+ void akos16DecodeLine(byte *buf, int32 numbytes, int32 dir);
+ void akos16Decompress(byte *dest, int32 pitch, const byte *src, int32 t_width, int32 t_height, int32 dir, int32 numskip_before, int32 numskip_after, byte transparency, int maskLeft, int maskTop, int zBuf);
+
+ void markRectAsDirty(Common::Rect rect);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/base-costume.cpp b/engines/scumm/base-costume.cpp
new file mode 100644
index 0000000000..e93b5b80cd
--- /dev/null
+++ b/engines/scumm/base-costume.cpp
@@ -0,0 +1,92 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/base-costume.h"
+#include "scumm/costume.h"
+
+namespace Scumm {
+
+byte BaseCostumeRenderer::drawCostume(const VirtScreen &vs, int numStrips, const Actor *a, bool drawToBackBuf) {
+ int i;
+ byte result = 0;
+
+ _out = vs;
+ if (drawToBackBuf)
+ _out.pixels = vs.getBackPixels(0, 0);
+ else
+ _out.pixels = vs.getPixels(0, 0);
+
+ _actorX += _vm->virtscr[0].xstart & 7;
+ _out.w = _out.pitch;
+ _out.pixels = (byte *)_out.pixels - (_vm->virtscr[0].xstart & 7);
+
+ _numStrips = numStrips;
+
+ if (_vm->_version == 1) {
+ _xmove = 0;
+ _ymove = 0;
+ } else if (_vm->_features & GF_OLD_BUNDLE) {
+ _xmove = -72;
+ _ymove = -100;
+ } else {
+ _xmove = _ymove = 0;
+ }
+ for (i = 0; i < 16; i++)
+ result |= drawLimb(a, i);
+ return result;
+}
+
+void BaseCostumeRenderer::codec1_ignorePakCols(Codec1 &v1, int num) {
+ num *= _height;
+
+ do {
+ v1.replen = *_srcptr++;
+ v1.repcolor = v1.replen >> v1.shr;
+ v1.replen &= v1.mask;
+
+ if (!v1.replen)
+ v1.replen = *_srcptr++;
+
+ do {
+ if (!--num)
+ return;
+ } while (--v1.replen);
+ } while (1);
+}
+
+bool ScummEngine::isCostumeInUse(int cost) const {
+ int i;
+ Actor *a;
+
+ if (_roomResource != 0)
+ for (i = 1; i < _numActors; i++) {
+ a = derefActor(i);
+ if (a->isInCurrentRoom() && a->_costume == cost)
+ return true;
+ }
+
+ return false;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/base-costume.h b/engines/scumm/base-costume.h
new file mode 100644
index 0000000000..e1d096dea9
--- /dev/null
+++ b/engines/scumm/base-costume.h
@@ -0,0 +1,168 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BASE_COSTUME_H
+#define BASE_COSTUME_H
+
+#include "common/scummsys.h"
+#include "scumm/actor.h" // for CostumeData
+
+namespace Scumm {
+
+#if !defined(__GNUC__)
+#pragma START_PACK_STRUCTS
+#endif
+
+struct CostumeInfo {
+ uint16 width, height;
+ int16 rel_x, rel_y;
+ int16 move_x, move_y;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+#pragma END_PACK_STRUCTS
+#endif
+
+
+
+#ifdef PALMOS_68K
+extern const byte *smallCostumeScaleTable;
+extern const byte *bigCostumeScaleTable;
+#else
+extern const byte smallCostumeScaleTable[256];
+extern const byte bigCostumeScaleTable[768];
+#endif
+
+
+
+class Actor;
+class ScummEngine;
+struct VirtScreen;
+
+class BaseCostumeLoader {
+protected:
+ ScummEngine *_vm;
+
+public:
+ BaseCostumeLoader(ScummEngine *vm) : _vm(vm) {}
+ virtual ~BaseCostumeLoader() {}
+
+ virtual void loadCostume(int id) = 0;
+ virtual byte increaseAnims(Actor *a) = 0;
+ virtual void costumeDecodeData(Actor *a, int frame, uint usemask) = 0;
+
+ bool hasManyDirections(int id) { return false; }
+};
+
+
+/**
+ * Base class for both ClassicCostumeRenderer and AkosRenderer.
+ */
+class BaseCostumeRenderer {
+public:
+ Common::Rect _clipOverride;
+ byte _actorID;
+
+ byte _shadow_mode;
+ byte *_shadow_table;
+
+ int _actorX, _actorY;
+ byte _zbuf;
+ byte _scaleX, _scaleY;
+
+ int _draw_top, _draw_bottom;
+ byte _paletteNum;
+ bool _skipLimbs;
+ bool _actorDrawVirScr;
+
+
+protected:
+ ScummEngine *_vm;
+
+ // Destination
+ Graphics::Surface _out;
+ int32 _numStrips;
+
+ // Source pointer
+ const byte *_srcptr;
+
+ // current move offset
+ int _xmove, _ymove;
+
+ // whether to draw the actor mirrored
+ bool _mirror;
+
+ // width and height of cel to decode
+ int _width, _height;
+
+ struct Codec1 {
+ // Parameters for the original ("V1") costume codec.
+ const byte *scaletable;
+ byte mask, shr;
+ byte repcolor;
+ byte replen;
+ int scaleXstep;
+ int x, y;
+ int scaleXindex, scaleYindex;
+ int skip_width;
+ byte *destptr;
+ const byte *mask_ptr;
+ };
+
+public:
+ BaseCostumeRenderer(ScummEngine *scumm) {
+ _actorID = 0;
+ _shadow_mode = 0;
+ _shadow_table = 0;
+ _actorX = _actorY = 0;
+ _zbuf = 0;
+ _scaleX = _scaleY = 0;
+ _draw_top = _draw_bottom = 0;
+
+ _vm = scumm;
+ _numStrips = -1;
+ _srcptr = 0;
+ _xmove = _ymove = 0;
+ _mirror = false;
+ _width = _height = 0;
+ _skipLimbs = 0;
+ _paletteNum = 0;
+ }
+ virtual ~BaseCostumeRenderer() {}
+
+ virtual void setPalette(byte *palette) = 0;
+ virtual void setFacing(const Actor *a) = 0;
+ virtual void setCostume(int costume, int shadow) = 0;
+
+
+ byte drawCostume(const VirtScreen &vs, int numStrips, const Actor *a, bool drawToBackBuf);
+
+protected:
+ virtual byte drawLimb(const Actor *a, int limb) = 0;
+
+ void codec1_ignorePakCols(Codec1 &v1, int num);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/bomp.cpp b/engines/scumm/bomp.cpp
new file mode 100644
index 0000000000..f3f78e4731
--- /dev/null
+++ b/engines/scumm/bomp.cpp
@@ -0,0 +1,393 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/akos.h"
+#include "scumm/bomp.h"
+#include "scumm/util.h"
+
+
+namespace Scumm {
+
+static int32 setupBompScale(byte *scaling, int32 size, byte scale);
+
+static void bompScaleFuncX(byte *line_buffer, byte *scaling_x_ptr, byte skip, int32 size);
+
+static void bompApplyShadow0(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency, byte HE7Check);
+static void bompApplyShadow1(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency);
+static void bompApplyShadow3(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency);
+static void bompApplyActorPalette(byte *actorPalette, byte *line_buffer, int32 size);
+
+
+
+void decompressBomp(byte *dst, const byte *src, int w, int h) {
+ assert(w > 0);
+ assert(h > 0);
+
+ do {
+ bompDecodeLine(dst, src + 2, w);
+ src += READ_LE_UINT16(src) + 2;
+ dst += w;
+ } while (--h);
+}
+
+void bompDecodeLine(byte *dst, const byte *src, int len) {
+ assert(len > 0);
+
+ int num;
+ byte code, color;
+
+ while (len > 0) {
+ code = *src++;
+ num = (code >> 1) + 1;
+ if (num > len)
+ num = len;
+ len -= num;
+ if (code & 1) {
+ color = *src++;
+ memset(dst, color, num);
+ } else {
+ memcpy(dst, src, num);
+ src += num;
+ }
+ dst += num;
+ }
+}
+
+void bompDecodeLineReverse(byte *dst, const byte *src, int len) {
+ assert(len > 0);
+
+ dst += len;
+
+ int num;
+ byte code, color;
+
+ while (len > 0) {
+ code = *src++;
+ num = (code >> 1) + 1;
+ if (num > len)
+ num = len;
+ len -= num;
+ dst -= num;
+ if (code & 1) {
+ color = *src++;
+ memset(dst, color, num);
+ } else {
+ memcpy(dst, src, num);
+ src += num;
+ }
+ }
+}
+
+void bompApplyMask(byte *line_buffer, byte *mask, byte maskbit, int32 size, byte transparency) {
+ while (1) {
+ do {
+ if (size-- == 0)
+ return;
+ if (*mask & maskbit) {
+ *line_buffer = transparency;
+ }
+ line_buffer++;
+ maskbit >>= 1;
+ } while (maskbit);
+ mask++;
+ maskbit = 128;
+ }
+}
+
+void bompApplyShadow(int shadowMode, const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency, byte HE7Check) {
+ assert(size > 0);
+ switch (shadowMode) {
+ case 0:
+ bompApplyShadow0(shadowPalette, line_buffer, dst, size, transparency, HE7Check);
+ break;
+ case 1:
+ bompApplyShadow1(shadowPalette, line_buffer, dst, size, transparency);
+ break;
+ case 3:
+ bompApplyShadow3(shadowPalette, line_buffer, dst, size, transparency);
+ break;
+ default:
+ error("Unknown shadow mode %d", shadowMode);
+ }
+}
+void bompApplyShadow0(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency, byte HE7Check = false) {
+ while (size-- > 0) {
+ byte tmp = *line_buffer++;
+ if (tmp != transparency) {
+ if (HE7Check)
+ *dst = shadowPalette[tmp];
+ else
+ *dst = tmp;
+ }
+ dst++;
+ }
+}
+
+void bompApplyShadow1(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency) {
+ while (size-- > 0) {
+ byte tmp = *line_buffer++;
+ if (tmp != transparency) {
+ if (tmp == 13) {
+ tmp = shadowPalette[*dst];
+ }
+ *dst = tmp;
+ }
+ dst++;
+ }
+}
+
+void bompApplyShadow3(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency) {
+ while (size-- > 0) {
+ byte tmp = *line_buffer++;
+ if (tmp != transparency) {
+ if (tmp < 8) {
+ tmp = shadowPalette[*dst + (tmp << 8)];
+ }
+ *dst = tmp;
+ }
+ dst++;
+ }
+}
+
+void bompApplyActorPalette(byte *actorPalette, byte *line_buffer, int32 size) {
+ if (actorPalette != 0) {
+ actorPalette[255] = 255;
+ while (size-- > 0) {
+ *line_buffer = actorPalette[*line_buffer];
+ line_buffer++;
+ }
+ }
+}
+
+void bompScaleFuncX(byte *line_buffer, byte *scaling_x_ptr, byte skip, int32 size) {
+ byte *line_ptr1 = line_buffer;
+ byte *line_ptr2 = line_buffer;
+
+ byte tmp = *scaling_x_ptr++;
+
+ while (size--) {
+ if ((skip & tmp) == 0) {
+ *line_ptr1++ = *line_ptr2;
+ }
+ line_ptr2++;
+ skip >>= 1;
+ if (skip == 0) {
+ skip = 128;
+ tmp = *scaling_x_ptr++;
+ }
+ }
+}
+
+void ScummEngine::drawBomp(const BompDrawData &bd, bool mirror) {
+ const byte *src;
+ byte *dst;
+ byte *mask = 0;
+ Common::Rect clip;
+ byte *scalingYPtr = 0;
+ byte skip_y_bits = 0x80;
+ byte skip_y_new = 0;
+ byte tmp;
+ byte bomp_scaling_x[64];
+ byte bomp_scaling_y[64];
+
+
+ if (bd.x < 0) {
+ clip.left = -bd.x;
+ } else {
+ clip.left = 0;
+ }
+
+ if (bd.y < 0) {
+ clip.top = -bd.y;
+ } else {
+ clip.top = 0;
+ }
+
+ clip.right = bd.srcwidth;
+ if (clip.right > bd.dst.w - bd.x) {
+ clip.right = bd.dst.w - bd.x;
+ }
+
+ clip.bottom = bd.srcheight;
+ if (clip.bottom > bd.dst.h - bd.y) {
+ clip.bottom = bd.dst.h - bd.y;
+ }
+
+ src = bd.dataptr;
+ dst = (byte *)bd.dst.pixels + bd.y * bd.dst.pitch + (bd.x + clip.left);
+
+ const byte maskbit = revBitMask((bd.x + clip.left) & 7);
+
+ // Mask against any additionally imposed mask
+ if (bd.maskPtr) {
+ mask = bd.maskPtr + (bd.y * gdi._numStrips) + ((bd.x + clip.left) / 8);
+ }
+
+ // Setup vertical scaling
+ if (bd.scale_y != 255) {
+ int scaleBottom = setupBompScale(bomp_scaling_y, bd.srcheight, bd.scale_y);
+ scalingYPtr = bomp_scaling_y;
+
+ skip_y_new = *scalingYPtr++;
+ skip_y_bits = 0x80;
+
+ if (clip.bottom > scaleBottom) {
+ clip.bottom = scaleBottom;
+ }
+ }
+
+ // Setup horizontal scaling
+ if (bd.scale_x != 255) {
+ int scaleRight = setupBompScale(bomp_scaling_x, bd.srcwidth, bd.scale_x);
+
+ if (clip.right > scaleRight) {
+ clip.right = scaleRight;
+ }
+ }
+
+ const int width = clip.right - clip.left;
+
+ if (width <= 0)
+ return;
+
+ int pos_y = 0;
+ byte line_buffer[1024];
+
+ byte *line_ptr = line_buffer + clip.left;
+
+ // Loop over all lines
+ while (pos_y < clip.bottom) {
+ // Decode a single (bomp encoded) line, reversed if we are in mirror mode
+ if (mirror)
+ bompDecodeLineReverse(line_buffer, src + 2, bd.srcwidth);
+ else
+ bompDecodeLine(line_buffer, src + 2, bd.srcwidth);
+ src += READ_LE_UINT16(src) + 2;
+
+ // If vertical scaling is enabled, do it
+ if (bd.scale_y != 255) {
+ // A bit set means we should skip this line...
+ tmp = skip_y_new & skip_y_bits;
+
+ // Advance the scale-skip bit mask, if it's 0, get the next scale-skip byte
+ skip_y_bits /= 2;
+ if (skip_y_bits == 0) {
+ skip_y_bits = 0x80;
+ skip_y_new = *scalingYPtr++;
+ }
+
+ // Skip the current line if the above check tells us to
+ if (tmp != 0)
+ continue;
+ }
+
+ // Perform horizontal scaling
+ if (bd.scale_x != 255) {
+ bompScaleFuncX(line_buffer, bomp_scaling_x, 0x80, bd.srcwidth);
+ }
+
+ // The first clip.top lines are to be clipped, i.e. not drawn
+ if (clip.top > 0) {
+ clip.top--;
+ } else {
+ // Replace the parts of the line which are masked with the transparency color
+ if (bd.maskPtr)
+ bompApplyMask(line_ptr, mask, maskbit, width, 255);
+
+ // Apply custom color map, if available
+ if (_bompActorPalettePtr)
+ bompApplyActorPalette(_bompActorPalettePtr, line_ptr, width);
+
+ // Finally, draw the decoded, scaled, masked and recolored line onto
+ // the target surface, using the specified shadow mode
+ bompApplyShadow(bd.shadowMode, _shadowPalette, line_ptr, dst, width, 255);
+ }
+
+ // Advance to the next line
+ pos_y++;
+ mask += gdi._numStrips;
+ dst += bd.dst.pitch;
+ }
+}
+
+static const byte bitCount[] = {
+ 8, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4,
+ 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
+ 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
+ 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
+ 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
+ 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
+ 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
+ 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
+ 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
+ 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
+ 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
+ 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
+ 6, 5, 5, 4, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 3, 2,
+ 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
+ 5, 4, 4, 3, 4, 3, 3, 2, 4, 3, 3, 2, 3, 2, 2, 1,
+ 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0,
+};
+
+int32 setupBompScale(byte *scaling, int32 size, byte scale) {
+ byte tmp;
+ int32 count;
+ const byte *tmp_ptr;
+ byte *tmp_scaling = scaling;
+ byte a = 0;
+ byte ret_value = 0;
+ const int offsets[8] = { 3, 2, 1, 0, 7, 6, 5, 4 };
+
+ count = (256 - size / 2);
+ assert(0 <= count && count < 768);
+ tmp_ptr = bigCostumeScaleTable + count;
+
+ count = (size + 7) / 8;
+ while (count--) {
+ a = 0;
+ for (int i = 0; i < 8; i++) {
+ tmp = *(tmp_ptr + offsets[i]);
+ a <<= 1;
+ if (scale < tmp) {
+ a |= 1;
+ }
+ }
+ tmp_ptr += 8;
+
+ *tmp_scaling++ = a;
+ }
+ if ((size & 7) != 0) {
+ *(tmp_scaling - 1) |= revBitMask(size & 7);
+ }
+
+ count = (size + 7) / 8;
+ while (count--) {
+ tmp = *scaling++;
+ ret_value += bitCount[tmp];
+ }
+
+ return ret_value;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/bomp.h b/engines/scumm/bomp.h
new file mode 100644
index 0000000000..74d367172d
--- /dev/null
+++ b/engines/scumm/bomp.h
@@ -0,0 +1,39 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BOMP_H
+#define BOMP_H
+
+#include "common/scummsys.h"
+
+namespace Scumm {
+
+void bompApplyMask(byte *line_buffer, byte *mask, byte maskbit, int32 size, byte transparency);
+void bompApplyShadow(int shadowMode, const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency, byte HE7Check = false);
+
+void decompressBomp(byte *dst, const byte *src, int w, int h);
+void bompDecodeLine(byte *dst, const byte *src, int size);
+void bompDecodeLineReverse(byte *dst, const byte *src, int size);
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp
new file mode 100644
index 0000000000..4460519480
--- /dev/null
+++ b/engines/scumm/boxes.cpp
@@ -0,0 +1,1271 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/actor.h"
+#include "scumm/boxes.h"
+#include "scumm/util.h"
+
+#include "common/util.h"
+
+namespace Scumm {
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct Box { /* Internal walkbox file format */
+ union {
+ struct {
+ byte uy;
+ byte ly;
+ byte ulx;
+ byte urx;
+ byte llx;
+ byte lrx;
+ byte mask;
+ byte flags;
+ } GCC_PACK v2;
+
+ struct {
+ int16 ulx, uly;
+ int16 urx, ury;
+ int16 lrx, lry;
+ int16 llx, lly;
+ byte mask;
+ byte flags;
+ uint16 scale;
+ } GCC_PACK old;
+
+ struct {
+ int32 ulx, uly;
+ int32 urx, ury;
+ int32 lrx, lry;
+ int32 llx, lly;
+ uint32 mask;
+ uint32 flags;
+ uint32 scaleSlot;
+ uint32 scale;
+ uint32 unk2;
+ uint32 unk3;
+ } GCC_PACK v8;
+ } GCC_PACK;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+#define BOX_MATRIX_SIZE 2000
+#define BOX_DEBUG 0
+
+
+static bool compareSlope(int X1, int Y1, int X2, int Y2, int X3, int Y3);
+static Common::Point closestPtOnLine(const Common::Point &start, const Common::Point &end, int x, int y);
+
+
+byte ScummEngine::getMaskFromBox(int box) {
+ // Fix for bug #740244 and #755863. This appears to have been a
+ // long standing bug in the original engine?
+ if (_version <= 3 && box == 255)
+ return 1;
+
+ Box *ptr = getBoxBaseAddr(box);
+ if (!ptr)
+ return 0;
+
+ // WORKAROUND for bug #847827: This is a bug in the data files, as it also
+ // occurs with the original engine. We work around it here anyway.
+ if (_gameId == GID_INDY4 && _currentRoom == 225 && _roomResource == 94 && box == 8)
+ return 0;
+
+ if (_version == 8)
+ return (byte) FROM_LE_32(ptr->v8.mask);
+ else if (_version <= 2)
+ return ptr->v2.mask;
+ else
+ return ptr->old.mask;
+}
+
+void ScummEngine::setBoxFlags(int box, int val) {
+ debug(2, "setBoxFlags(%d, 0x%02x)", box, val);
+
+ /* SCUMM7+ stuff */
+ if (val & 0xC000) {
+ assert(box >= 0 && box < 65);
+ _extraBoxFlags[box] = val;
+ } else {
+ Box *ptr = getBoxBaseAddr(box);
+ if (!ptr)
+ return;
+ if (_version == 8)
+ ptr->v8.flags = TO_LE_32(val);
+ else if (_version <= 2)
+ ptr->v2.flags = val;
+ else
+ ptr->old.flags = val;
+ }
+}
+
+byte ScummEngine::getBoxFlags(int box) {
+ Box *ptr = getBoxBaseAddr(box);
+ if (!ptr)
+ return 0;
+ if (_version == 8)
+ return (byte) FROM_LE_32(ptr->v8.flags);
+ else if (_version <= 2)
+ return ptr->v2.flags;
+ else
+ return ptr->old.flags;
+}
+
+void ScummEngine::setBoxScale(int box, int scale) {
+ Box *ptr = getBoxBaseAddr(box);
+ assert(ptr);
+ if (_version == 8)
+ ptr->v8.scale = TO_LE_32(scale);
+ else if (_version <= 2)
+ error("This should not ever be called!");
+ else
+ ptr->old.scale = TO_LE_16(scale);
+}
+
+void ScummEngine::setBoxScaleSlot(int box, int slot) {
+ Box *ptr = getBoxBaseAddr(box);
+ assert(ptr);
+ ptr->v8.scaleSlot = TO_LE_32(slot);
+}
+
+int ScummEngine::getScale(int box, int x, int y) {
+ if (_features & GF_NO_SCALING)
+ return 255;
+
+ Box *ptr = getBoxBaseAddr(box);
+ if (!ptr)
+ return 255;
+
+ int slot , scale;
+
+ if (_version == 8) {
+ // COMI has a separate field for the scale slot...
+ slot = FROM_LE_32(ptr->v8.scaleSlot);
+ scale = FROM_LE_32(ptr->v8.scale);
+ } else {
+ scale = READ_LE_UINT16(&ptr->old.scale);
+ if (scale & 0x8000)
+ slot = (scale & 0x7FFF) + 1;
+ else
+ slot = 0;
+ }
+
+ // Was a scale slot specified? If so, we compute the effective scale
+ // from it, ignoring the box scale.
+ if (slot)
+ scale = getScaleFromSlot(slot, x, y);
+
+ return scale;
+}
+
+
+int ScummEngine::getScaleFromSlot(int slot, int x, int y) {
+ assert(1 <= slot && slot <= ARRAYSIZE(_scaleSlots));
+ int scale;
+ int scaleX = 0, scaleY = 0;
+ ScaleSlot &s = _scaleSlots[slot-1];
+
+ if (s.y1 == s.y2 && s.x1 == s.x2)
+ error("Invalid scale slot %d", slot);
+
+ if (s.y1 != s.y2) {
+ if (y < 0)
+ y = 0;
+
+ scaleY = (s.scale2 - s.scale1) * (y - s.y1) / (s.y2 - s.y1) + s.scale1;
+ }
+ if (s.x1 == s.x2) {
+ scale = scaleY;
+ } else {
+ scaleX = (s.scale2 - s.scale1) * (x - s.x1) / (s.x2 - s.x1) + s.scale1;
+
+ if (s.y1 == s.y2) {
+ scale = scaleX;
+ } else {
+ scale = (scaleX + scaleY) / 2;
+ }
+ }
+
+ // Clip the scale to range 1-255
+ if (scale < 1)
+ scale = 1;
+ else if (scale > 255)
+ scale = 255;
+
+ return scale;
+}
+
+int ScummEngine::getBoxScale(int box) {
+ if (_features & GF_NO_SCALING)
+ return 255;
+ Box *ptr = getBoxBaseAddr(box);
+ if (!ptr)
+ return 255;
+ if (_version == 8)
+ return FROM_LE_32(ptr->v8.scale);
+ else
+ return READ_LE_UINT16(&ptr->old.scale);
+}
+
+/**
+ * Convert a rtScaleTable resource to a corresponding scale slot entry.
+ *
+ * At some point, we discovered that the old scale items (stored in rtScaleTable
+ * resources) are in fact the same as (or rather, a predecessor of) the
+ * scale slots used in COMI. While not being precomputed (and thus slightly
+ * slower), scale slots are more flexible, and most importantly, can cope with
+ * rooms higher than 200 pixels. That's an essential feature for DIG and FT
+ * and in fact the lack of it caused various bugs in the past.
+ *
+ * Hence, we decided to switch all games to use the more powerful scale slots.
+ * To accomodate old savegames, we attempt here to convert rtScaleTable
+ * resources to scale slots.
+ */
+void ScummEngine::convertScaleTableToScaleSlot(int slot) {
+ assert(1 <= slot && slot <= ARRAYSIZE(_scaleSlots));
+
+ byte *resptr = getResourceAddress(rtScaleTable, slot);
+ int lowerIdx, upperIdx;
+ float m, oldM;
+
+ // Do nothing if the given scale table doesn't exist
+ if (resptr == 0)
+ return;
+
+ if (resptr[0] == resptr[199]) {
+ // The scale is constant. This usually means we encountered one of the
+ // "broken" cases. We set pseudo scale item values which lead to a
+ // constant scale of 255.
+ setScaleSlot(slot, 0, 0, 255, 0, 199, 255);
+ return;
+ }
+
+ /*
+ * Essentially, what we are doing here is some kind of "line fitting"
+ * algorithm. The data in the scale table represents a linear graph. What
+ * we want to find is the slope and (vertical) offset of this line. Things
+ * are complicated by the fact that the line is cut of vertically at 1 and
+ * 255. We have to be careful in handling this and some border cases.
+ *
+ * Some typical graphs look like these:
+ * --- --- ---
+ * / --- \
+ * ___/ --- \___
+ *
+ * The method used here is to compute the slope of secants fixed at the
+ * left and right end. For most cases this detects the cut-over points
+ * quite accurately.
+ */
+
+ // Search for the bend on the left side
+ m = (resptr[199] - resptr[0]) / 199.0;
+ for (lowerIdx = 0; lowerIdx < 199 && (resptr[lowerIdx] == 1 || resptr[lowerIdx] == 255); lowerIdx++) {
+ oldM = m;
+ m = (resptr[199] - resptr[lowerIdx+1]) / (float)(199 - (lowerIdx+1));
+ if (m > 0) {
+ if (m <= oldM)
+ break;
+ } else {
+ if (m >= oldM)
+ break;
+ }
+ }
+
+ // Search for the bend on the right side
+ m = (resptr[199] - resptr[0]) / 199.0;
+ for (upperIdx = 199; upperIdx > 1 && (resptr[upperIdx] == 1 || resptr[upperIdx] == 255); upperIdx--) {
+ oldM = m;
+ m = (resptr[upperIdx-1] - resptr[0]) / (float)(upperIdx-1);
+ if (m > 0) {
+ if (m <= oldM)
+ break;
+ } else {
+ if (m >= oldM)
+ break;
+ }
+ }
+
+ // If lowerIdx and upperIdx are equal, we assume that there
+ // was no bend at all, and go for the maximum range.
+ if (lowerIdx == upperIdx) {
+ lowerIdx = 0;
+ upperIdx = 199;
+ }
+
+ // The values of y1 and y2, as well as the scale, can now easily be computed
+ setScaleSlot(slot, 0, lowerIdx, resptr[lowerIdx], 0, upperIdx, resptr[upperIdx]);
+
+ // Compute the variance, for debugging. It shouldn't exceed 1
+ ScaleSlot &s = _scaleSlots[slot-1];
+ int y;
+ int sum = 0;
+ int scale;
+ float variance;
+ for (y = 0; y < 200; y++) {
+ scale = (s.scale2 - s.scale1) * (y - s.y1) / (s.y2 - s.y1) + s.scale1;
+ if (scale < 1)
+ scale = 1;
+ else if (scale > 255)
+ scale = 255;
+
+ sum += (resptr[y] - scale) * (resptr[y] - scale);
+ }
+ variance = sum / (200.0 - 1.0);
+ if (variance > 1)
+ debug(1, "scale item %d, variance %f exceeds 1 (room %d)", slot, variance, _currentRoom);
+}
+
+void ScummEngine::setScaleSlot(int slot, int x1, int y1, int scale1, int x2, int y2, int scale2) {
+ assert(1 <= slot && slot <= ARRAYSIZE(_scaleSlots));
+ ScaleSlot &s = _scaleSlots[slot-1];
+ s.x2 = x2;
+ s.y2 = y2;
+ s.scale2 = scale2;
+ s.x1 = x1;
+ s.y1 = y1;
+ s.scale1 = scale1;
+}
+
+byte ScummEngine::getNumBoxes() {
+ byte *ptr = getResourceAddress(rtMatrix, 2);
+ if (!ptr)
+ return 0;
+ if (_version == 8)
+ return (byte) READ_LE_UINT32(ptr);
+ else
+ return ptr[0];
+}
+
+Box *ScummEngine::getBoxBaseAddr(int box) {
+ byte *ptr = getResourceAddress(rtMatrix, 2);
+ if (!ptr || box == 255)
+ return NULL;
+
+ // The NES version of Maniac Mansion attempts to set flags for boxes 2-4
+ // when there are only three boxes (0-2) when walking out to the garage.
+ if ((_gameId == GID_MANIAC) && (_platform == Common::kPlatformNES) && (box >= ptr[0]))
+ return NULL;
+
+ // FIXME: In "pass to adventure", the loom demo, when bobbin enters
+ // the tent to the elders, box = 2, but ptr[0] = 2 -> errors out.
+ // Hence we disable the check for now. Maybe in PASS (and other old games)
+ // we shouldn't subtract 1 from ptr[0] when performing the check?
+ // this also seems to be incorrect for atari st demo of zak
+ // and assumingly other v2 games
+ // The same happens in Indy3EGA (see bug #770351)
+ // Also happens in ZakEGA (see bug #771803).
+ //
+ // This *might* mean that we have a bug in our box implementation
+ // OTOH, the original engine, unlike ScummVM, performed no bound
+ // checking at all. All the problems so far have been cases where
+ // the value was exactly one more than what we consider the maximum.
+ // So it's very well possible that all of these are script errors.
+ if (_version <= 4 && ptr[0] == box)
+ box--;
+
+ checkRange(ptr[0] - 1, 0, box, "Illegal box %d");
+ if (_version <= 2)
+ return (Box *)(ptr + box * SIZEOF_BOX_V2 + 1);
+ else if (_version == 3)
+ return (Box *)(ptr + box * SIZEOF_BOX_V3 + 1);
+ else if (_features & GF_SMALL_HEADER)
+ return (Box *)(ptr + box * SIZEOF_BOX + 1);
+ else if (_version == 8)
+ return (Box *)(ptr + box * SIZEOF_BOX_V8 + 4);
+ else
+ return (Box *)(ptr + box * SIZEOF_BOX + 2);
+}
+
+int ScummEngine::getSpecialBox(int x, int y) {
+ int i;
+ int numOfBoxes;
+ byte flag;
+
+ numOfBoxes = getNumBoxes() - 1;
+
+ for (i = numOfBoxes; i >= 0; i--) {
+ flag = getBoxFlags(i);
+
+ if (!(flag & kBoxInvisible) && (flag & kBoxPlayerOnly))
+ return (-1);
+
+ if (checkXYInBoxBounds(i, x, y))
+ return (i);
+ }
+
+ return (-1);
+}
+
+bool ScummEngine::checkXYInBoxBounds(int b, int x, int y) {
+ BoxCoords box;
+
+ if (b < 0 || b == Actor::kInvalidBox)
+ return false;
+
+ getBoxCoordinates(b, &box);
+
+ if (x < box.ul.x && x < box.ur.x && x < box.lr.x && x < box.ll.x)
+ return false;
+
+ if (x > box.ul.x && x > box.ur.x && x > box.lr.x && x > box.ll.x)
+ return false;
+
+ if (y < box.ul.y && y < box.ur.y && y < box.lr.y && y < box.ll.y)
+ return false;
+
+ if (y > box.ul.y && y > box.ur.y && y > box.lr.y && y > box.ll.y)
+ return false;
+
+ if (box.ul.x == box.ur.x && box.ul.y == box.ur.y && box.lr.x == box.ll.x && box.lr.y == box.ll.y ||
+ box.ul.x == box.ll.x && box.ul.y == box.ll.y && box.ur.x == box.lr.x && box.ur.y == box.lr.y) {
+
+ Common::Point pt;
+ pt = closestPtOnLine(box.ul, box.lr, x, y);
+ if (distanceFromPt(x, y, pt.x, pt.y) <= 4)
+ return true;
+ }
+
+ if (!compareSlope(box.ul.x, box.ul.y, box.ur.x, box.ur.y, x, y))
+ return false;
+
+ if (!compareSlope(box.ur.x, box.ur.y, box.lr.x, box.lr.y, x, y))
+ return false;
+
+ if (!compareSlope(box.ll.x, box.ll.y, x, y, box.lr.x, box.lr.y))
+ return false;
+
+ if (!compareSlope(box.ul.x, box.ul.y, x, y, box.ll.x, box.ll.y))
+ return false;
+
+ return true;
+}
+
+void ScummEngine::getBoxCoordinates(int boxnum, BoxCoords *box) {
+ Box *bp = getBoxBaseAddr(boxnum);
+ assert(bp);
+
+ if (_version == 8) {
+ box->ul.x = (short)FROM_LE_32(bp->v8.ulx);
+ box->ul.y = (short)FROM_LE_32(bp->v8.uly);
+ box->ur.x = (short)FROM_LE_32(bp->v8.urx);
+ box->ur.y = (short)FROM_LE_32(bp->v8.ury);
+
+ box->ll.x = (short)FROM_LE_32(bp->v8.llx);
+ box->ll.y = (short)FROM_LE_32(bp->v8.lly);
+ box->lr.x = (short)FROM_LE_32(bp->v8.lrx);
+ box->lr.y = (short)FROM_LE_32(bp->v8.lry);
+
+ // FIXME: Some walkboxes in CMI appear to have been flipped,
+ // in the sense that for instance the lower boundary is above
+ // the upper one. Can that really be the case, or is there
+ // some more sinister problem afoot?
+ //
+ // Is this fix sufficient, or will we need something more
+ // elaborate?
+
+ if (box->ul.y > box->ll.y && box->ur.y > box->lr.y) {
+ SWAP(box->ul, box->ll);
+ SWAP(box->ur, box->lr);
+ }
+
+ if (box->ul.x > box->ur.x && box->ll.x > box->lr.x) {
+ SWAP(box->ul, box->ur);
+ SWAP(box->ll, box->lr);
+ }
+ } else if (_version <= 2) {
+ box->ul.x = bp->v2.ulx * 8;
+ box->ul.y = bp->v2.uy * 2;
+ box->ur.x = bp->v2.urx * 8;
+ box->ur.y = bp->v2.uy * 2;
+
+ box->ll.x = bp->v2.llx * 8;
+ box->ll.y = bp->v2.ly * 2;
+ box->lr.x = bp->v2.lrx * 8;
+ box->lr.y = bp->v2.ly * 2;
+ } else {
+ box->ul.x = (int16)READ_LE_UINT16(&bp->old.ulx);
+ box->ul.y = (int16)READ_LE_UINT16(&bp->old.uly);
+ box->ur.x = (int16)READ_LE_UINT16(&bp->old.urx);
+ box->ur.y = (int16)READ_LE_UINT16(&bp->old.ury);
+
+ box->ll.x = (int16)READ_LE_UINT16(&bp->old.llx);
+ box->ll.y = (int16)READ_LE_UINT16(&bp->old.lly);
+ box->lr.x = (int16)READ_LE_UINT16(&bp->old.lrx);
+ box->lr.y = (int16)READ_LE_UINT16(&bp->old.lry);
+ }
+}
+
+uint ScummEngine::distanceFromPt(int x, int y, int ptx, int pty) {
+ int diffx, diffy;
+
+ diffx = ABS(ptx - x);
+
+ if (diffx >= 0x1000)
+ return 0xFFFFFF;
+
+ diffy = ABS(pty - y);
+
+ if (diffy >= 0x1000)
+ return 0xFFFFFF;
+ diffx *= diffx;
+ diffy *= diffy;
+ return diffx + diffy;
+}
+
+
+bool compareSlope(int X1, int Y1, int X2, int Y2, int X3, int Y3) {
+ return (Y2 - Y1) * (X3 - X1) <= (Y3 - Y1) * (X2 - X1);
+}
+
+/**
+ * Find the point on a line segment which is closest to a given point.
+ *
+ * @param start the start of the line segment
+ * @param end the end of the line segment
+ * @param x the x coordinate of the point which we want to 'project' on the line segment
+ * @param y the y coordinate of the point which we want to 'project' on the line segment
+ * @return the point on the line segmen closes to the given point
+ */
+Common::Point closestPtOnLine(const Common::Point &start, const Common::Point &end, int x, int y) {
+ Common::Point pt;
+
+ const int lxdiff = end.x - start.x;
+ const int lydiff = end.y - start.y;
+
+ if (end.x == start.x) { // Vertical line?
+ pt.x = start.x;
+ pt.y = y;
+ } else if (end.y == start.y) { // Horizontal line?
+ pt.x = x;
+ pt.y = start.y;
+ } else {
+ const int dist = lxdiff * lxdiff + lydiff * lydiff;
+ int a, b, c;
+ if (ABS(lxdiff) > ABS(lydiff)) {
+ a = start.x * lydiff / lxdiff;
+ b = x * lxdiff / lydiff;
+
+ c = (a + b - start.y + y) * lydiff * lxdiff / dist;
+
+ pt.x = c;
+ pt.y = c * lydiff / lxdiff - a + start.y;
+ } else {
+ a = start.y * lxdiff / lydiff;
+ b = y * lydiff / lxdiff;
+
+ c = (a + b - start.x + x) * lydiff * lxdiff / dist;
+
+ pt.y = c;
+ pt.x = c * lxdiff / lydiff - a + start.x;
+ }
+ }
+
+ if (ABS(lydiff) < ABS(lxdiff)) {
+ if (lxdiff > 0) {
+ if (pt.x < start.x)
+ pt = start;
+ else if (pt.x > end.x)
+ pt = end;
+ } else {
+ if (pt.x > start.x)
+ pt = start;
+ else if (pt.x < end.x)
+ pt = end;
+ }
+ } else {
+ if (lydiff > 0) {
+ if (pt.y < start.y)
+ pt = start;
+ else if (pt.y > end.y)
+ pt = end;
+ } else {
+ if (pt.y > start.y)
+ pt = start;
+ else if (pt.y < end.y)
+ pt = end;
+ }
+ }
+
+ return pt;
+}
+
+bool ScummEngine::inBoxQuickReject(int b, int x, int y, int threshold) {
+ int t;
+ BoxCoords box;
+
+ getBoxCoordinates(b, &box);
+
+ t = x - threshold;
+ if (t > box.ul.x && t > box.ur.x && t > box.lr.x && t > box.ll.x)
+ return true;
+
+ t = x + threshold;
+ if (t < box.ul.x && t < box.ur.x && t < box.lr.x && t < box.ll.x)
+ return true;
+
+ t = y - threshold;
+ if (t > box.ul.y && t > box.ur.y && t > box.lr.y && t > box.ll.y)
+ return true;
+
+ t = y + threshold;
+ if (t < box.ul.y && t < box.ur.y && t < box.lr.y && t < box.ll.y)
+ return true;
+
+ return false;
+}
+
+int ScummEngine::getClosestPtOnBox(int b, int x, int y, int16& outX, int16& outY) {
+ Common::Point pt;
+ uint dist;
+ uint bestdist = 0xFFFFFF;
+ BoxCoords box;
+
+ getBoxCoordinates(b, &box);
+
+ pt = closestPtOnLine(box.ul, box.ur, x, y);
+ dist = distanceFromPt(x, y, pt.x, pt.y);
+ if (dist < bestdist) {
+ bestdist = dist;
+ outX = pt.x;
+ outY = pt.y;
+ }
+
+ pt = closestPtOnLine(box.ur, box.lr, x, y);
+ dist = distanceFromPt(x, y, pt.x, pt.y);
+ if (dist < bestdist) {
+ bestdist = dist;
+ outX = pt.x;
+ outY = pt.y;
+ }
+
+ pt = closestPtOnLine(box.lr, box.ll, x, y);
+ dist = distanceFromPt(x, y, pt.x, pt.y);
+ if (dist < bestdist) {
+ bestdist = dist;
+ outX = pt.x;
+ outY = pt.y;
+ }
+
+ pt = closestPtOnLine(box.ll, box.ul, x, y);
+ dist = distanceFromPt(x, y, pt.x, pt.y);
+ if (dist < bestdist) {
+ bestdist = dist;
+ outX = pt.x;
+ outY = pt.y;
+ }
+
+ return bestdist;
+}
+
+byte *ScummEngine::getBoxMatrixBaseAddr() {
+ byte *ptr = getResourceAddress(rtMatrix, 1);
+ assert(ptr);
+ if (*ptr == 0xFF)
+ ptr++;
+ return ptr;
+}
+
+/*
+ * Compute if there is a way that connects box 'from' with box 'to'.
+ * Returns the number of a box adjactant to 'from' that is the next on the
+ * way to 'to' (this can be 'to' itself or a third box).
+ * If there is no connection -1 is return.
+ */
+int ScummEngine::getPathToDestBox(byte from, byte to) {
+ const byte *boxm;
+ byte i;
+ const int numOfBoxes = getNumBoxes();
+ int dest = -1;
+
+ if (from == to)
+ return to;
+
+ if (to == Actor::kInvalidBox)
+ return -1;
+
+ if (from == Actor::kInvalidBox)
+ return to;
+
+ assert(from < numOfBoxes);
+ assert(to < numOfBoxes);
+
+ boxm = getBoxMatrixBaseAddr();
+
+ if (_version <= 2) {
+ // The v2 box matrix is a real matrix with numOfBoxes rows and columns.
+ // The first numOfBoxes bytes contain indices to the start of the corresponding
+ // row (although that seems unnecessary to me - the value is easily computable.
+ boxm += numOfBoxes + boxm[from];
+ return (int8)boxm[to];
+ }
+
+ // WORKAROUND #1: It seems that in some cases, the box matrix is corrupt
+ // (more precisely, is too short) in the datafiles already. In
+ // particular this seems to be the case in room 46 of Indy3 EGA (see
+ // also bug #770690). This didn't cause problems in the original
+ // engine, because there, the memory layout is different. After the
+ // walkbox would follow the rest of the room file, thus the program
+ // always behaved the same (and by chance, correct). Not so for us,
+ // since random data may follow after the resource in ScummVM.
+ //
+ // As a workaround, we add a check for the end of the box matrix
+ // resource, and abort the search once we reach the end.
+ const byte *end = boxm + getResourceSize(rtMatrix, 1);
+
+ // WORKAROUND #2: In addition to the above, we have to add this special
+ // case to fix the scene in Indy3 where Indy meets Hitler in Berlin.
+ // See bug #770690 and also bug #774783.
+ if ((_gameId == GID_INDY3) && _roomResource == 46 && from == 1 && to == 0)
+ return 0;
+
+ // Skip up to the matrix data for box 'from'
+ for (i = 0; i < from && boxm < end; i++) {
+ while (boxm < end && *boxm != 0xFF)
+ boxm += 3;
+ boxm++;
+ }
+
+ // Now search for the entry for box 'to'
+ while (boxm < end && boxm[0] != 0xFF) {
+ if (boxm[0] <= to && to <= boxm[1])
+ dest = (int8)boxm[2];
+ boxm += 3;
+ }
+
+ if (boxm >= end)
+ debug(0, "The box matrix apparently is truncated (room %d)", _roomResource);
+
+ return dest;
+}
+
+/*
+ * Computes the next point actor a has to walk towards in a straight
+ * line in order to get from box1 to box3 via box2.
+ */
+bool Actor::findPathTowards(byte box1nr, byte box2nr, byte box3nr, Common::Point &foundPath) {
+ BoxCoords box1;
+ BoxCoords box2;
+ Common::Point tmp;
+ int i, j;
+ int flag;
+ int q, pos;
+
+ _vm->getBoxCoordinates(box1nr, &box1);
+ _vm->getBoxCoordinates(box2nr, &box2);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ if (box1.ul.x == box1.ur.x && box1.ul.x == box2.ul.x && box1.ul.x == box2.ur.x) {
+ flag = 0;
+ if (box1.ul.y > box1.ur.y) {
+ SWAP(box1.ul.y, box1.ur.y);
+ flag |= 1;
+ }
+
+ if (box2.ul.y > box2.ur.y) {
+ SWAP(box2.ul.y, box2.ur.y);
+ flag |= 2;
+ }
+
+ if (box1.ul.y > box2.ur.y || box2.ul.y > box1.ur.y ||
+ (box1.ur.y == box2.ul.y || box2.ur.y == box1.ul.y) &&
+ box1.ul.y != box1.ur.y && box2.ul.y != box2.ur.y) {
+ if (flag & 1)
+ SWAP(box1.ul.y, box1.ur.y);
+ if (flag & 2)
+ SWAP(box2.ul.y, box2.ur.y);
+ } else {
+ pos = _pos.y;
+ if (box2nr == box3nr) {
+ int diffX = _walkdata.dest.x - _pos.x;
+ int diffY = _walkdata.dest.y - _pos.y;
+ int boxDiffX = box1.ul.x - _pos.x;
+
+ if (diffX != 0) {
+ int t;
+
+ diffY *= boxDiffX;
+ t = diffY / diffX;
+ if (t == 0 && (diffY <= 0 || diffX <= 0)
+ && (diffY >= 0 || diffX >= 0))
+ t = -1;
+ pos = _pos.y + t;
+ }
+ }
+
+ q = pos;
+ if (q < box2.ul.y)
+ q = box2.ul.y;
+ if (q > box2.ur.y)
+ q = box2.ur.y;
+ if (q < box1.ul.y)
+ q = box1.ul.y;
+ if (q > box1.ur.y)
+ q = box1.ur.y;
+ if (q == pos && box2nr == box3nr)
+ return true;
+ foundPath.y = q;
+ foundPath.x = box1.ul.x;
+ return false;
+ }
+ }
+
+ if (box1.ul.y == box1.ur.y && box1.ul.y == box2.ul.y && box1.ul.y == box2.ur.y) {
+ flag = 0;
+ if (box1.ul.x > box1.ur.x) {
+ SWAP(box1.ul.x, box1.ur.x);
+ flag |= 1;
+ }
+
+ if (box2.ul.x > box2.ur.x) {
+ SWAP(box2.ul.x, box2.ur.x);
+ flag |= 2;
+ }
+
+ if (box1.ul.x > box2.ur.x || box2.ul.x > box1.ur.x ||
+ (box1.ur.x == box2.ul.x || box2.ur.x == box1.ul.x) &&
+ box1.ul.x != box1.ur.x && box2.ul.x != box2.ur.x) {
+ if (flag & 1)
+ SWAP(box1.ul.x, box1.ur.x);
+ if (flag & 2)
+ SWAP(box2.ul.x, box2.ur.x);
+ } else {
+
+ if (box2nr == box3nr) {
+ int diffX = _walkdata.dest.x - _pos.x;
+ int diffY = _walkdata.dest.y - _pos.y;
+ int boxDiffY = box1.ul.y - _pos.y;
+
+ pos = _pos.x;
+ if (diffY != 0) {
+ pos += diffX * boxDiffY / diffY;
+ }
+ } else {
+ pos = _pos.x;
+ }
+
+ q = pos;
+ if (q < box2.ul.x)
+ q = box2.ul.x;
+ if (q > box2.ur.x)
+ q = box2.ur.x;
+ if (q < box1.ul.x)
+ q = box1.ul.x;
+ if (q > box1.ur.x)
+ q = box1.ur.x;
+ if (q == pos && box2nr == box3nr)
+ return true;
+ foundPath.x = q;
+ foundPath.y = box1.ul.y;
+ return false;
+ }
+ }
+ tmp = box1.ul;
+ box1.ul = box1.ur;
+ box1.ur = box1.lr;
+ box1.lr = box1.ll;
+ box1.ll = tmp;
+ }
+ tmp = box2.ul;
+ box2.ul = box2.ur;
+ box2.ur = box2.lr;
+ box2.lr = box2.ll;
+ box2.ll = tmp;
+ }
+ return false;
+}
+
+#if BOX_DEBUG
+static void printMatrix(byte *boxm, int num) {
+ int i;
+ for (i = 0; i < num; i++) {
+ printf("%d: ", i);
+ while (*boxm != 0xFF) {
+ printf("%d, ", *boxm);
+ boxm++;
+ }
+ boxm++;
+ printf("\n");
+ }
+}
+
+static void printMatrix2(byte *matrix, int num) {
+ int i, j;
+ printf(" ");
+ for (i = 0; i < num; i++)
+ printf("%2d ", i);
+ printf("\n");
+ for (i = 0; i < num; i++) {
+ printf("%2d: ", i);
+ for (j = 0; j < num; j++) {
+ int val = matrix[i * 64 + j];
+ if (val == Actor::kInvalidBox)
+ printf(" ? ");
+ else
+ printf("%2d ", val);
+ }
+ printf("\n");
+ }
+}
+#endif
+
+void ScummEngine::createBoxMatrix() {
+ int num, i, j, k;
+ byte *adjacentMatrix, *itineraryMatrix;
+
+ // The total number of boxes
+ num = getNumBoxes();
+ assert(num <= 64);
+
+ // Allocate the adjacent & itinerary matrices
+ adjacentMatrix = (byte *)malloc(64 * 64);
+ itineraryMatrix = (byte *)malloc(64 * 64);
+
+ // Initialise the adjacent matrix: each box has distance 0 to itself,
+ // and distance 1 to its direct neighbors. Initially, it has distance
+ // 255 (= infinity) to all other boxes.
+ for (i = 0; i < num; i++) {
+ for (j = 0; j < num; j++) {
+ if (i == j) {
+ adjacentMatrix[i * 64 + j] = 0;
+ itineraryMatrix[i * 64 + j] = j;
+ } else if (areBoxesNeighbours(i, j)) {
+ adjacentMatrix[i * 64 + j] = 1;
+ itineraryMatrix[i * 64 + j] = j;
+ } else {
+ adjacentMatrix[i * 64 + j] = 255;
+ itineraryMatrix[i * 64 + j] = Actor::kInvalidBox;
+ }
+ }
+ }
+
+ // Compute the shortest routes between boxes via Kleene's algorithm.
+ // The original code used some kind of mangled Dijkstra's algorithm;
+ // while that might in theory be slightly faster, it was
+ // a) extremly obfuscated
+ // b) incorrect: it didn't always find the shortest paths
+ // c) not any faster in reality for our sparse & small adjacent matrices
+ for (k = 0; k < num; k++) {
+ for (i = 0; i < num; i++) {
+ for (j = 0; j < num; j++) {
+ if (i == j)
+ continue;
+ byte distIK = adjacentMatrix[64 * i + k];
+ byte distKJ = adjacentMatrix[64 * k + j];
+ if (adjacentMatrix[64 * i + j] > distIK + distKJ) {
+ adjacentMatrix[64 * i + j] = distIK + distKJ;
+ itineraryMatrix[64 * i + j] = itineraryMatrix[64 * i + k];
+ }
+ }
+ }
+
+ }
+
+ // "Compress" the distance matrix into the box matrix format used
+ // by the engine. The format is like this:
+ // For each box (from 0 to num) there is first a byte with value 0xFF,
+ // followed by an arbitrary number of byte triples; the end is marked
+ // again by the lead 0xFF for the next "row". The meaning of the
+ // byte triples is as follows: the first two bytes define a range
+ // of box numbers (e.g. 7-11), while the third byte defines an
+ // itineray box. Assuming we are in the 5th "row" and encounter
+ // the triplet 7,11,15: this means to get from box 5 to any of
+ // the boxes 7,8,9,10,11 the shortest way is to go via box 15.
+ // See also getPathToDestBox.
+
+ byte *matrixStart = res.createResource(rtMatrix, 1, BOX_MATRIX_SIZE);
+ const byte *matrixEnd = matrixStart + BOX_MATRIX_SIZE;
+
+ #define addToMatrix(b) do { *matrixStart++ = (b); assert(matrixStart < matrixEnd); } while (0)
+
+ for (i = 0; i < num; i++) {
+ addToMatrix(0xFF);
+ for (j = 0; j < num; j++) {
+ byte itinerary = itineraryMatrix[64 * i + j];
+ if (itinerary != Actor::kInvalidBox) {
+ addToMatrix(j);
+ while (j < num - 1 && itinerary == itineraryMatrix[64 * i + (j + 1)])
+ j++;
+ addToMatrix(j);
+ addToMatrix(itinerary);
+ }
+ }
+ }
+ addToMatrix(0xFF);
+
+
+#if BOX_DEBUG
+ printf("Itinerary matrix:\n");
+ printMatrix2(itineraryMatrix, num);
+ printf("compressed matrix:\n");
+ printMatrix(getBoxMatrixBaseAddr(), num);
+#endif
+
+ free(adjacentMatrix);
+ free(itineraryMatrix);
+}
+
+/** Check if two boxes are neighbours. */
+bool ScummEngine::areBoxesNeighbours(int box1nr, int box2nr) {
+ Common::Point tmp;
+ BoxCoords box;
+ BoxCoords box2;
+
+ if (getBoxFlags(box1nr) & kBoxInvisible || getBoxFlags(box2nr) & kBoxInvisible)
+ return false;
+
+ getBoxCoordinates(box1nr, &box2);
+ getBoxCoordinates(box2nr, &box);
+
+ // Roughly, the idea of this algorithm is to check if we can find
+ // two sides of the two given boxes which touch.
+ // In order to keep te code simple, we only match the upper sides;
+ // then, we "rotate" the box coordinates four times each, for a total
+ // of 16 comparisions (four sides compared with four sides each).
+ for (int j = 0; j < 4; j++) {
+ for (int k = 0; k < 4; k++) {
+ // Are the "upper" sides of the boxes on a single vertical line
+ // (i.e. all share one x value) ?
+ if (box2.ur.x == box2.ul.x && box.ul.x == box2.ul.x && box.ur.x == box2.ul.x) {
+ bool swappedBox2 = false, swappedBox1 = false;
+ if (box2.ur.y < box2.ul.y) {
+ swappedBox2 = 1;
+ SWAP(box2.ur.y, box2.ul.y);
+ }
+ if (box.ur.y < box.ul.y) {
+ swappedBox1 = 1;
+ SWAP(box.ur.y, box.ul.y);
+ }
+ if (box.ur.y < box2.ul.y ||
+ box.ul.y > box2.ur.y ||
+ (box.ul.y == box2.ur.y ||
+ box.ur.y == box2.ul.y) && box2.ur.y != box2.ul.y && box.ul.y != box.ur.y) {
+ } else {
+ return true;
+ }
+
+ // Swap back if necessary
+ if (swappedBox2) {
+ SWAP(box2.ur.y, box2.ul.y);
+ }
+ if (swappedBox1) {
+ SWAP(box.ur.y, box.ul.y);
+ }
+ }
+
+ // Are the "upper" sides of the boxes on a single horizontal line
+ // (i.e. all share one y value) ?
+ if (box2.ur.y == box2.ul.y && box.ul.y == box2.ul.y && box.ur.y == box2.ul.y) {
+ bool swappedBox2 = false, swappedBox1 = false;
+ if (box2.ur.x < box2.ul.x) {
+ swappedBox2 = 1;
+ SWAP(box2.ur.x, box2.ul.x);
+ }
+ if (box.ur.x < box.ul.x) {
+ swappedBox1 = 1;
+ SWAP(box.ur.x, box.ul.x);
+ }
+ if (box.ur.x < box2.ul.x ||
+ box.ul.x > box2.ur.x ||
+ (box.ul.x == box2.ur.x ||
+ box.ur.x == box2.ul.x) && box2.ur.x != box2.ul.x && box.ul.x != box.ur.x) {
+
+ } else {
+ return true;
+ }
+
+ // Swap back if necessary
+ if (swappedBox2) {
+ SWAP(box2.ur.x, box2.ul.x);
+ }
+ if (swappedBox1) {
+ SWAP(box.ur.x, box.ul.x);
+ }
+ }
+
+ // "Rotate" the box coordinates
+ tmp = box2.ul;
+ box2.ul = box2.ur;
+ box2.ur = box2.lr;
+ box2.lr = box2.ll;
+ box2.ll = tmp;
+ }
+
+ // "Rotate" the box coordinates
+ tmp = box.ul;
+ box.ul = box.ur;
+ box.ur = box.lr;
+ box.lr = box.ll;
+ box.ll = tmp;
+ }
+
+ return false;
+}
+
+void Actor::findPathTowardsOld(byte box1, byte box2, byte finalBox, Common::Point &p2, Common::Point &p3) {
+ Common::Point pt;
+ Common::Point gateA[2];
+ Common::Point gateB[2];
+
+ _vm->getGates(box1, box2, gateA, gateB);
+
+ p2.x = 32000;
+ p3.x = 32000;
+
+ // next box (box2) = final box?
+ if (box2 == finalBox) {
+ // In Indy3, the masks (= z-level) have to match, too -- needed for the
+ // 'maze' in the zeppeling (see bug #1032964).
+ if (_vm->_gameId != GID_INDY3 || _vm->getMaskFromBox(box1) == _vm->getMaskFromBox(box2)) {
+ // Is the actor (x,y) between both gates?
+ if (compareSlope(_pos.x, _pos.y, _walkdata.dest.x, _walkdata.dest.y, gateA[0].x, gateA[0].y) !=
+ compareSlope(_pos.x, _pos.y, _walkdata.dest.x, _walkdata.dest.y, gateB[0].x, gateB[0].y) &&
+ compareSlope(_pos.x, _pos.y, _walkdata.dest.x, _walkdata.dest.y, gateA[1].x, gateA[1].y) !=
+ compareSlope(_pos.x, _pos.y, _walkdata.dest.x, _walkdata.dest.y, gateB[1].x, gateB[1].y)) {
+ return;
+ }
+ }
+ }
+
+ p3 = pt = closestPtOnLine(gateA[1], gateB[1], _pos.x, _pos.y);
+
+ if (compareSlope(_pos.x, _pos.y, p3.x, p3.y, gateA[0].x, gateA[0].y) ==
+ compareSlope(_pos.x, _pos.y, p3.x, p3.y, gateB[0].x, gateB[0].y)) {
+ closestPtOnLine(gateA[0], gateB[0], _pos.x, _pos.y);
+ p2 = pt; // if point 2 between gates, ignore!
+ }
+}
+
+/**
+ * Compute the "gate" between two boxes. The gate is a pair of two lines which
+ * both start on box 'box1' and end on 'box2'. For both lines, one of its
+ * end points is the corner point of one of the two boxes. The other end point
+ * is a point on the other point closest to first end point.
+ * This way the lines bound a 'corridor' between the two boxes, through which
+ * the actor has to walk to get from box1 to box2.
+ */
+void ScummEngine::getGates(int box1, int box2, Common::Point gateA[2], Common::Point gateB[2]) {
+ int i, j;
+ int dist[8];
+ int minDist[3];
+ int closest[3];
+ int box[3];
+ BoxCoords coords;
+ Common::Point closestPoint[8];
+ Common::Point boxCorner[8];
+ int line1, line2;
+
+ // For all corner coordinates of the first box, compute the point closest
+ // to them on the second box (and also compute the distance of these points).
+ getBoxCoordinates(box1, &coords);
+ boxCorner[0] = coords.ul;
+ boxCorner[1] = coords.ur;
+ boxCorner[2] = coords.lr;
+ boxCorner[3] = coords.ll;
+ for (i = 0; i < 4; i++) {
+ dist[i] = getClosestPtOnBox(box2, boxCorner[i].x, boxCorner[i].y, closestPoint[i].x, closestPoint[i].y);
+ }
+
+ // Now do the same but with the roles of the first and second box swapped.
+ getBoxCoordinates(box2, &coords);
+ boxCorner[4] = coords.ul;
+ boxCorner[5] = coords.ur;
+ boxCorner[6] = coords.lr;
+ boxCorner[7] = coords.ll;
+ for (i = 4; i < 8; i++) {
+ dist[i] = getClosestPtOnBox(box1, boxCorner[i].x, boxCorner[i].y, closestPoint[i].x, closestPoint[i].y);
+ }
+
+ // Find the three closest "close" points between the two boxes.
+ for (j = 0; j < 3; j++) {
+ minDist[j] = 0xFFFF;
+ for (i = 0; i < 8; i++) {
+ if (dist[i] < minDist[j]) {
+ minDist[j] = dist[i];
+ closest[j] = i;
+ }
+ }
+ dist[closest[j]] = 0xFFFF;
+ minDist[j] = (int)sqrt((double)minDist[j]);
+ box[j] = (closest[j] > 3); // Is the point on the first or on the second box?
+ }
+
+
+ // Finally, compute the actual "gate".
+
+ if (box[0] == box[1] && ABS(minDist[0] - minDist[1]) < 4) {
+ line1 = closest[0];
+ line2 = closest[1];
+
+ } else if (box[0] == box[1] && minDist[0] == minDist[1]) { // parallel
+ line1 = closest[0];
+ line2 = closest[1];
+ } else if (box[0] == box[2] && minDist[0] == minDist[2]) { // parallel
+ line1 = closest[0];
+ line2 = closest[2];
+ } else if (box[1] == box[2] && minDist[1] == minDist[2]) { // parallel
+ line1 = closest[1];
+ line2 = closest[2];
+
+ } else if (box[0] == box[2] && ABS(minDist[0] - minDist[2]) < 4) {
+ line1 = closest[0];
+ line2 = closest[2];
+ } else if (ABS(minDist[0] - minDist[2]) < 4) {
+ line1 = closest[1];
+ line2 = closest[2];
+ } else if (ABS(minDist[0] - minDist[1]) < 4) {
+ line1 = closest[0];
+ line2 = closest[1];
+ } else {
+ line1 = closest[0];
+ line2 = closest[0];
+ }
+
+ // Set the gate
+ if (line1 < 4) {
+ gateA[0] = boxCorner[line1];
+ gateA[1] = closestPoint[line1];
+ } else {
+ gateA[1] = boxCorner[line1];
+ gateA[0] = closestPoint[line1];
+ }
+
+ if (line2 < 4) {
+ gateB[0] = boxCorner[line2];
+ gateB[1] = closestPoint[line2];
+ } else {
+ gateB[1] = boxCorner[line2];
+ gateB[0] = closestPoint[line2];
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/boxes.h b/engines/scumm/boxes.h
new file mode 100644
index 0000000000..f5fb7160fd
--- /dev/null
+++ b/engines/scumm/boxes.h
@@ -0,0 +1,54 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BOXES_H
+#define BOXES_H
+
+#include "common/rect.h"
+
+namespace Scumm {
+
+#define SIZEOF_BOX_V2 8
+#define SIZEOF_BOX_V3 18
+#define SIZEOF_BOX 20
+#define SIZEOF_BOX_V8 52
+
+typedef enum {
+ kBoxXFlip = 0x08,
+ kBoxYFlip = 0x10,
+ kBoxIgnoreScale = 0x20,
+ kBoxPlayerOnly = 0x20,
+ kBoxLocked = 0x40,
+ kBoxInvisible = 0x80
+} BoxFlags;
+
+struct BoxCoords { /* Box coordinates */
+ Common::Point ul;
+ Common::Point ur;
+ Common::Point ll;
+ Common::Point lr;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/camera.cpp b/engines/scumm/camera.cpp
new file mode 100644
index 0000000000..cc6d9bfaed
--- /dev/null
+++ b/engines/scumm/camera.cpp
@@ -0,0 +1,367 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/intern.h"
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+
+namespace Scumm {
+
+void ScummEngine::setCameraAtEx(int at) {
+ if (!(_features & GF_NEW_CAMERA)) {
+ camera._mode = kNormalCameraMode;
+ camera._cur.x = at;
+ setCameraAt(at, 0);
+ camera._movingToActor = false;
+ }
+}
+
+void ScummEngine::setCameraAt(int pos_x, int pos_y) {
+ if (camera._mode != kFollowActorCameraMode || ABS(pos_x - camera._cur.x) > (_screenWidth / 2)) {
+ camera._cur.x = pos_x;
+ }
+ camera._dest.x = pos_x;
+
+ if (camera._cur.x < VAR(VAR_CAMERA_MIN_X))
+ camera._cur.x = (short) VAR(VAR_CAMERA_MIN_X);
+
+ if (camera._cur.x > VAR(VAR_CAMERA_MAX_X))
+ camera._cur.x = (short) VAR(VAR_CAMERA_MAX_X);
+
+ if (VAR_SCROLL_SCRIPT != 0xFF && VAR(VAR_SCROLL_SCRIPT)) {
+ VAR(VAR_CAMERA_POS_X) = camera._cur.x;
+ runScript(VAR(VAR_SCROLL_SCRIPT), 0, 0, 0);
+ }
+
+ // If the camera moved and text is visible, remove it
+ if (camera._cur.x != camera._last.x && _charset->_hasMask && _version > 3)
+ stopTalk();
+}
+
+void ScummEngine::setCameraFollows(Actor *a) {
+
+ int t, i;
+
+ camera._mode = kFollowActorCameraMode;
+ camera._follows = a->_number;
+
+ if (!a->isInCurrentRoom()) {
+ startScene(a->getRoom(), 0, 0);
+ camera._mode = kFollowActorCameraMode;
+ camera._cur.x = a->_pos.x;
+ setCameraAt(camera._cur.x, 0);
+ }
+
+ t = a->_pos.x / 8 - _screenStartStrip;
+
+ if (t < camera._leftTrigger || t > camera._rightTrigger)
+ setCameraAt(a->_pos.x, 0);
+
+ for (i = 1; i < _numActors; i++) {
+ if (_actors[i].isInCurrentRoom())
+ _actors[i]._needRedraw = true;
+ }
+ runInventoryScript(0);
+}
+
+void ScummEngine::clampCameraPos(Common::Point *pt) {
+ if (pt->x < VAR(VAR_CAMERA_MIN_X))
+ pt->x = (short) VAR(VAR_CAMERA_MIN_X);
+
+ if (pt->x > VAR(VAR_CAMERA_MAX_X))
+ pt->x = (short) VAR(VAR_CAMERA_MAX_X);
+
+ if (pt->y < VAR(VAR_CAMERA_MIN_Y))
+ pt->y = (short) VAR(VAR_CAMERA_MIN_Y);
+
+ if (pt->y > VAR(VAR_CAMERA_MAX_Y))
+ pt->y = (short) VAR(VAR_CAMERA_MAX_Y);
+}
+
+void ScummEngine::moveCamera() {
+ int pos = camera._cur.x;
+ int actorx, t;
+ Actor *a = NULL;
+
+ camera._cur.x &= 0xFFF8;
+
+ if (camera._cur.x < VAR(VAR_CAMERA_MIN_X)) {
+ if (VAR_CAMERA_FAST_X != 0xFF && VAR(VAR_CAMERA_FAST_X))
+ camera._cur.x = (short) VAR(VAR_CAMERA_MIN_X);
+ else
+ camera._cur.x += 8;
+ cameraMoved();
+ return;
+ }
+
+ if (camera._cur.x > VAR(VAR_CAMERA_MAX_X)) {
+ if (VAR_CAMERA_FAST_X != 0xFF && VAR(VAR_CAMERA_FAST_X))
+ camera._cur.x = (short) VAR(VAR_CAMERA_MAX_X);
+ else
+ camera._cur.x -= 8;
+ cameraMoved();
+ return;
+ }
+
+ if (camera._mode == kFollowActorCameraMode) {
+ a = derefActor(camera._follows, "moveCamera");
+
+ actorx = a->_pos.x;
+ t = actorx / 8 - _screenStartStrip;
+
+ if (t < camera._leftTrigger || t > camera._rightTrigger) {
+ if (VAR_CAMERA_FAST_X != 0xFF && VAR(VAR_CAMERA_FAST_X)) {
+ if (t > 35)
+ camera._dest.x = actorx + 80;
+ if (t < 5)
+ camera._dest.x = actorx - 80;
+ } else
+ camera._movingToActor = true;
+ }
+ }
+
+ if (camera._movingToActor) {
+ a = derefActor(camera._follows, "moveCamera(2)");
+ camera._dest.x = a->_pos.x;
+ }
+
+ if (camera._dest.x < VAR(VAR_CAMERA_MIN_X))
+ camera._dest.x = (short) VAR(VAR_CAMERA_MIN_X);
+
+ if (camera._dest.x > VAR(VAR_CAMERA_MAX_X))
+ camera._dest.x = (short) VAR(VAR_CAMERA_MAX_X);
+
+ if (VAR_CAMERA_FAST_X != 0xFF && VAR(VAR_CAMERA_FAST_X)) {
+ camera._cur.x = camera._dest.x;
+ } else {
+ if (camera._cur.x < camera._dest.x)
+ camera._cur.x += 8;
+ if (camera._cur.x > camera._dest.x)
+ camera._cur.x -= 8;
+ }
+
+ /* a is set a bit above */
+ if (camera._movingToActor && (camera._cur.x / 8) == (a->_pos.x / 8)) {
+ camera._movingToActor = false;
+ }
+
+ cameraMoved();
+
+ if (VAR_SCROLL_SCRIPT != 0xFF && VAR(VAR_SCROLL_SCRIPT) && pos != camera._cur.x) {
+ VAR(VAR_CAMERA_POS_X) = camera._cur.x;
+ runScript(VAR(VAR_SCROLL_SCRIPT), 0, 0, 0);
+ }
+}
+
+void ScummEngine::cameraMoved() {
+ int screenLeft;
+ if (_features & GF_NEW_CAMERA) {
+ assert(camera._cur.x >= (_screenWidth / 2) && camera._cur.y >= (_screenHeight / 2));
+ } else {
+ if (camera._cur.x < (_screenWidth / 2)) {
+ camera._cur.x = (_screenWidth / 2);
+ } else if (camera._cur.x > _roomWidth - (_screenWidth / 2)) {
+ camera._cur.x = _roomWidth - (_screenWidth / 2);
+ }
+ }
+
+ _screenStartStrip = camera._cur.x / 8 - gdi._numStrips / 2;
+ _screenEndStrip = _screenStartStrip + gdi._numStrips - 1;
+
+ _screenTop = camera._cur.y - (_screenHeight / 2);
+ if (_features & GF_NEW_CAMERA) {
+ screenLeft = camera._cur.x - (_screenWidth / 2);
+ } else {
+ screenLeft = _screenStartStrip * 8;
+ }
+
+ virtscr[0].xstart = screenLeft;
+}
+
+void ScummEngine::panCameraTo(int x, int y) {
+ camera._dest.x = x;
+ camera._mode = kPanningCameraMode;
+ camera._movingToActor = false;
+}
+
+void ScummEngine::actorFollowCamera(int act) {
+ if (!(_features & GF_NEW_CAMERA)) {
+ int old;
+
+ old = camera._follows;
+ setCameraFollows(derefActor(act, "actorFollowCamera"));
+ if (camera._follows != old)
+ runInventoryScript(0);
+
+ camera._movingToActor = false;
+ }
+}
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::setCameraAt(int pos_x, int pos_y) {
+ Common::Point old;
+
+ old = camera._cur;
+
+ camera._cur.x = pos_x;
+ camera._cur.y = pos_y;
+
+ clampCameraPos(&camera._cur);
+
+ camera._dest = camera._cur;
+ VAR(VAR_CAMERA_DEST_X) = camera._dest.x;
+ VAR(VAR_CAMERA_DEST_Y) = camera._dest.y;
+
+ assert(camera._cur.x >= (_screenWidth / 2) && camera._cur.y >= (_screenHeight / 2));
+
+ if (camera._cur.x != old.x || camera._cur.y != old.y) {
+ if (VAR(VAR_SCROLL_SCRIPT)) {
+ VAR(VAR_CAMERA_POS_X) = camera._cur.x;
+ VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
+ runScript(VAR(VAR_SCROLL_SCRIPT), 0, 0, 0);
+ }
+
+ // Even though cameraMoved() is called automatically, we may
+ // need to know at once that the camera has moved, or text may
+ // be printed at the wrong coordinates. See bugs #795938 and
+ // #929242
+ cameraMoved();
+ }
+}
+
+void ScummEngine_v7::setCameraFollows(Actor *a) {
+
+ byte oldfollow = camera._follows;
+ int ax, ay;
+
+ camera._follows = a->_number;
+ VAR(VAR_CAMERA_FOLLOWED_ACTOR) = a->_number;
+
+ if (!a->isInCurrentRoom()) {
+ startScene(a->getRoom(), 0, 0);
+ }
+
+ ax = ABS(a->_pos.x - camera._cur.x);
+ ay = ABS(a->_pos.y - camera._cur.y);
+
+ if (ax > VAR(VAR_CAMERA_THRESHOLD_X) || ay > VAR(VAR_CAMERA_THRESHOLD_Y) || ax > (_screenWidth / 2) || ay > (_screenHeight / 2)) {
+ setCameraAt(a->_pos.x, a->_pos.y);
+ }
+
+ if (a->_number != oldfollow)
+ runInventoryScript(0);
+}
+
+void ScummEngine_v7::moveCamera() {
+ Common::Point old = camera._cur;
+ Actor *a = NULL;
+
+ if (camera._follows) {
+ a = derefActor(camera._follows, "moveCamera");
+ if (ABS(camera._cur.x - a->_pos.x) > VAR(VAR_CAMERA_THRESHOLD_X) ||
+ ABS(camera._cur.y - a->_pos.y) > VAR(VAR_CAMERA_THRESHOLD_Y)) {
+ camera._movingToActor = true;
+ if (VAR(VAR_CAMERA_THRESHOLD_X) == 0)
+ camera._cur.x = a->_pos.x;
+ if (VAR(VAR_CAMERA_THRESHOLD_Y) == 0)
+ camera._cur.y = a->_pos.y;
+ clampCameraPos(&camera._cur);
+ }
+ } else {
+ camera._movingToActor = false;
+ }
+
+ if (camera._movingToActor) {
+ VAR(VAR_CAMERA_DEST_X) = camera._dest.x = a->_pos.x;
+ VAR(VAR_CAMERA_DEST_Y) = camera._dest.y = a->_pos.y;
+ }
+
+ assert(camera._cur.x >= (_screenWidth / 2) && camera._cur.y >= (_screenHeight / 2));
+
+ clampCameraPos(&camera._dest);
+
+ if (camera._cur.x < camera._dest.x) {
+ camera._cur.x += (short) VAR(VAR_CAMERA_SPEED_X);
+ if (camera._cur.x > camera._dest.x)
+ camera._cur.x = camera._dest.x;
+ }
+
+ if (camera._cur.x > camera._dest.x) {
+ camera._cur.x -= (short) VAR(VAR_CAMERA_SPEED_X);
+ if (camera._cur.x < camera._dest.x)
+ camera._cur.x = camera._dest.x;
+ }
+
+ if (camera._cur.y < camera._dest.y) {
+ camera._cur.y += (short) VAR(VAR_CAMERA_SPEED_Y);
+ if (camera._cur.y > camera._dest.y)
+ camera._cur.y = camera._dest.y;
+ }
+
+ if (camera._cur.y > camera._dest.y) {
+ camera._cur.y -= (short) VAR(VAR_CAMERA_SPEED_Y);
+ if (camera._cur.y < camera._dest.y)
+ camera._cur.y = camera._dest.y;
+ }
+
+ if (camera._cur.x == camera._dest.x && camera._cur.y == camera._dest.y) {
+
+ camera._movingToActor = false;
+ camera._accel.x = camera._accel.y = 0;
+ VAR(VAR_CAMERA_SPEED_X) = VAR(VAR_CAMERA_SPEED_Y) = 0;
+ } else {
+
+ camera._accel.x += (short) VAR(VAR_CAMERA_ACCEL_X);
+ camera._accel.y += (short) VAR(VAR_CAMERA_ACCEL_Y);
+
+ VAR(VAR_CAMERA_SPEED_X) += camera._accel.x / 100;
+ VAR(VAR_CAMERA_SPEED_Y) += camera._accel.y / 100;
+
+ if (VAR(VAR_CAMERA_SPEED_X) < 8)
+ VAR(VAR_CAMERA_SPEED_X) = 8;
+
+ if (VAR(VAR_CAMERA_SPEED_Y) < 8)
+ VAR(VAR_CAMERA_SPEED_Y) = 8;
+
+ }
+
+ cameraMoved();
+
+ if (camera._cur.x != old.x || camera._cur.y != old.y) {
+ VAR(VAR_CAMERA_POS_X) = camera._cur.x;
+ VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
+
+ if (VAR(VAR_SCROLL_SCRIPT))
+ runScript(VAR(VAR_SCROLL_SCRIPT), 0, 0, 0);
+ }
+}
+
+void ScummEngine_v7::panCameraTo(int x, int y) {
+ VAR(VAR_CAMERA_FOLLOWED_ACTOR) = camera._follows = 0;
+ VAR(VAR_CAMERA_DEST_X) = camera._dest.x = x;
+ VAR(VAR_CAMERA_DEST_Y) = camera._dest.y = y;
+}
+#endif
+
+} // End of namespace Scumm
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
new file mode 100644
index 0000000000..e8086ed30a
--- /dev/null
+++ b/engines/scumm/charset.cpp
@@ -0,0 +1,1827 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "scumm/charset.h"
+#include "scumm/scumm.h"
+#include "scumm/nut_renderer.h"
+#include "scumm/util.h"
+#include "scumm/wiz_he.h"
+
+namespace Scumm {
+
+void ScummEngine::loadCJKFont() {
+ Common::File fp;
+ _useCJKMode = false;
+ if (_language == Common::JA_JPN && _version <= 5) { // FM-TOWNS v3 / v5 Kanji
+ int numChar = 256 * 32;
+ _2byteWidth = 16;
+ _2byteHeight = 16;
+ // use FM-TOWNS font rom, since game files don't have kanji font resources
+ if (fp.open("fmt_fnt.rom", Common::File::kFileReadMode)) {
+ _useCJKMode = true;
+ debug(2, "Loading FM-TOWNS Kanji rom");
+ _2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar];
+ fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar);
+ fp.close();
+ }
+ } else if (_language == Common::KO_KOR || _language == Common::JA_JPN || _language == Common::ZH_TWN) {
+ int numChar = 0;
+ const char *fontFile = NULL;
+
+ switch (_language) {
+ case Common::KO_KOR:
+ fontFile = "korean.fnt";
+ numChar = 2350;
+ break;
+ case Common::JA_JPN:
+ fontFile = (_gameId == GID_DIG) ? "kanji16.fnt" : "japanese.fnt";
+ numChar = 1024; //FIXME
+ break;
+ case Common::ZH_TWN:
+ if (_gameId == GID_CMI) {
+ fontFile = "chinese.fnt";
+ numChar = 1; //FIXME
+ }
+ break;
+ default:
+ break;
+ }
+ if (fontFile && fp.open(fontFile)) {
+ debug(2, "Loading CJK Font");
+ _useCJKMode = true;
+ fp.seek(2, SEEK_CUR);
+ _2byteWidth = fp.readByte();
+ _2byteHeight = fp.readByte();
+
+ _2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar];
+ fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar);
+ fp.close();
+ } else {
+ error("Couldn't load any font");
+ }
+ }
+}
+
+static int SJIStoFMTChunk(int f, int s) { //converts sjis code to fmt font offset
+ enum {
+ KANA = 0,
+ KANJI = 1,
+ EKANJI = 2
+ };
+ int base = s - ((s + 1) % 32);
+ int c = 0, p = 0, chunk_f = 0, chunk = 0, cr = 0, kanjiType = KANA;
+
+ if (f >= 0x81 && f <= 0x84) kanjiType = KANA;
+ if (f >= 0x88 && f <= 0x9f) kanjiType = KANJI;
+ if (f >= 0xe0 && f <= 0xea) kanjiType = EKANJI;
+
+ if ((f > 0xe8 || (f == 0xe8 && base >= 0x9f)) || (f > 0x90 || (f == 0x90 && base >= 0x9f))) {
+ c = 48; //correction
+ p = -8; //correction
+ }
+
+ if (kanjiType == KANA) {//Kana
+ chunk_f = (f - 0x81) * 2;
+ } else if (kanjiType == KANJI) {//Standard Kanji
+ p += f - 0x88;
+ chunk_f = c + 2 * p;
+ } else if (kanjiType == EKANJI) {//Enhanced Kanji
+ p += f - 0xe0;
+ chunk_f = c + 2 * p;
+ }
+
+ // Base corrections
+ if (base == 0x7f && s == 0x7f)
+ base -= 0x20;
+ if (base == 0x9f && s == 0xbe)
+ base += 0x20;
+ if (base == 0xbf && s == 0xde)
+ base += 0x20;
+ //if (base == 0x7f && s == 0x9e)
+ // base += 0x20;
+
+ switch (base) {
+ case 0x3f:
+ cr = 0; //3f
+ if (kanjiType == KANA) chunk = 1;
+ else if (kanjiType == KANJI) chunk = 31;
+ else if (kanjiType == EKANJI) chunk = 111;
+ break;
+ case 0x5f:
+ cr = 0; //5f
+ if (kanjiType == KANA) chunk = 17;
+ else if (kanjiType == KANJI) chunk = 47;
+ else if (kanjiType == EKANJI) chunk = 127;
+ break;
+ case 0x7f:
+ cr = -1; //80
+ if (kanjiType == KANA) chunk = 9;
+ else if (kanjiType == KANJI) chunk = 63;
+ else if (kanjiType == EKANJI) chunk = 143;
+ break;
+ case 0x9f:
+ cr = 1; //9e
+ if (kanjiType == KANA) chunk = 2;
+ else if (kanjiType == KANJI) chunk = 32;
+ else if (kanjiType == EKANJI) chunk = 112;
+ break;
+ case 0xbf:
+ cr = 1; //be
+ if (kanjiType == KANA) chunk = 18;
+ else if (kanjiType == KANJI) chunk = 48;
+ else if (kanjiType == EKANJI) chunk = 128;
+ break;
+ case 0xdf:
+ cr = 1; //de
+ if (kanjiType == KANA) chunk = 10;
+ else if (kanjiType == KANJI) chunk = 64;
+ else if (kanjiType == EKANJI) chunk = 144;
+ break;
+ default:
+ debug(4, "Invalid Char! f %x s %x base %x c %d p %d", f, s, base, c, p);
+ return 0;
+ }
+
+ debug(6, "Kanji: %c%c f 0x%x s 0x%x base 0x%x c %d p %d chunk %d cr %d index %d", f, s, f, s, base, c, p, chunk, cr, ((chunk_f + chunk) * 32 + (s - base)) + cr);
+ return ((chunk_f + chunk) * 32 + (s - base)) + cr;
+}
+
+byte *ScummEngine::get2byteCharPtr(int idx) {
+ switch (_language) {
+ case Common::KO_KOR:
+ idx = ((idx % 256) - 0xb0) * 94 + (idx / 256) - 0xa1;
+ break;
+ case Common::JA_JPN:
+ idx = SJIStoFMTChunk((idx % 256), (idx / 256));
+ break;
+ case Common::ZH_TWN:
+ default:
+ idx = 0;
+ }
+ return _2byteFontPtr + ((_2byteWidth + 7) / 8) * _2byteHeight * idx;
+}
+
+
+#pragma mark -
+
+
+CharsetRenderer::CharsetRenderer(ScummEngine *vm) {
+
+ _nextLeft = 0;
+ _nextTop = 0;
+
+ _top = 0;
+ _left = 0;
+ _startLeft = 0;
+ _right = 0;
+
+ _color = 0;
+
+ _center = false;
+ _hasMask = false;
+ _textScreenID = kMainVirtScreen;
+ _blitAlso = false;
+ _firstChar = false;
+ _disableOffsX = false;
+
+ _vm = vm;
+ _curId = 0;
+
+ const int size = _vm->_screenWidth * _vm->_screenHeight;
+ _textSurface.pixels = malloc(size);
+ _textSurface.w = _vm->_screenWidth;
+ _textSurface.h = _vm->_screenHeight;
+ _textSurface.pitch = _vm->_screenWidth;
+ _textSurface.bytesPerPixel = 1;
+ clearTextSurface();
+}
+
+CharsetRenderer::~CharsetRenderer() {
+ free(_textSurface.pixels);
+}
+
+CharsetRendererCommon::CharsetRendererCommon(ScummEngine *vm)
+ : CharsetRenderer(vm), _bitDepth(0), _fontHeight(0), _numChars(0) {
+ _shadowMode = kNoShadowMode;
+ _shadowColor = 0;
+}
+
+void CharsetRendererCommon::setCurID(byte id) {
+ checkRange(_vm->_numCharsets - 1, 0, id, "Printing with bad charset %d");
+
+ _curId = id;
+
+ _fontPtr = _vm->getResourceAddress(rtCharset, id);
+ if (_fontPtr == 0)
+ error("CharsetRendererCommon::setCurID: charset %d not found!", id);
+
+ if (_vm->_version == 4)
+ _fontPtr += 17;
+ else
+ _fontPtr += 29;
+
+ _bitDepth = _fontPtr[0];
+ _fontHeight = _fontPtr[1];
+ _numChars = READ_LE_UINT16(_fontPtr + 2);
+}
+
+void CharsetRendererV3::setCurID(byte id) {
+ checkRange(_vm->_numCharsets - 1, 0, id, "Printing with bad charset %d");
+
+ _curId = id;
+
+ _fontPtr = _vm->getResourceAddress(rtCharset, id);
+ if (_fontPtr == 0)
+ error("CharsetRendererCommon::setCurID: charset %d not found!", id);
+
+ _bitDepth = 1;
+ _numChars = _fontPtr[4];
+ _fontHeight = _fontPtr[5];
+
+ _fontPtr += 6;
+ _widthTable = _fontPtr;
+ _fontPtr += _numChars;
+}
+
+int CharsetRendererCommon::getFontHeight() {
+ if (_vm->_useCJKMode)
+ return MAX(_vm->_2byteHeight + 1, _fontHeight);
+ else
+ return _fontHeight;
+}
+
+int CharsetRendererV3::getFontHeight() {
+ if (_vm->_useCJKMode)
+ return MAX(_vm->_2byteHeight + 1, _fontHeight);
+ else
+ return _fontHeight;
+}
+
+// do spacing for variable width old-style font
+int CharsetRendererClassic::getCharWidth(byte chr) {
+ if (chr >= 0x80 && _vm->_useCJKMode)
+ return _vm->_2byteWidth / 2;
+ int spacing = 0;
+
+ int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
+ if (offs) {
+ spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2];
+ }
+
+ return spacing;
+}
+
+int CharsetRenderer::getStringWidth(int arg, const byte *text) {
+ int pos = 0;
+ int width = 1;
+ byte chr;
+ int oldID = getCurID();
+ int code = (_vm->_heversion >= 80) ? 127 : 64;
+
+ while ((chr = text[pos++]) != 0) {
+ if (chr == '\n' || chr == '\r')
+ break;
+ if (_vm->_heversion >= 72) {
+ if (chr == code) {
+ chr = text[pos++];
+ if (chr == 84 || chr == 116) { // Strings of speech offset/size
+ while (chr != code)
+ chr = text[pos++];
+ continue;
+ }
+ if (chr == 119) // 'Wait'
+ break;
+ if (chr == 104|| chr == 110) // 'Newline'
+ break;
+ }
+ } else {
+ if (chr == '@')
+ continue;
+ if (chr == 255 || (_vm->_version <= 6 && chr == 254)) {
+ chr = text[pos++];
+ if (chr == 3) // 'WAIT'
+ break;
+ if (chr == 8) { // 'Verb on next line'
+ if (arg == 1)
+ break;
+ while (text[pos++] == ' ')
+ ;
+ continue;
+ }
+ if (chr == 10 || chr == 21 || chr == 12 || chr == 13) {
+ pos += 2;
+ continue;
+ }
+ if (chr == 9 || chr == 1 || chr == 2) // 'Newline'
+ break;
+ if (chr == 14) {
+ int set = text[pos] | (text[pos + 1] << 8);
+ pos += 2;
+ setCurID(set);
+ continue;
+ }
+ }
+ }
+ if ((chr & 0x80) && _vm->_useCJKMode) {
+ pos++;
+ width += _vm->_2byteWidth;
+ } else {
+ width += getCharWidth(chr);
+ }
+ }
+
+ setCurID(oldID);
+
+ return width;
+}
+
+void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
+ int lastspace = -1;
+ int curw = 1;
+ byte chr;
+ int oldID = getCurID();
+ int code = (_vm->_heversion >= 80) ? 127 : 64;
+
+ while ((chr = str[pos++]) != 0) {
+ if (_vm->_heversion >= 72) {
+ if (chr == code) {
+ chr = str[pos++];
+ if (chr == 84 || chr == 116) { // Strings of speech offset/size
+ while (chr != code)
+ chr = str[pos++];
+ continue;
+ }
+ if (chr == 119) // 'Wait'
+ break;
+ if (chr == 110) { // 'Newline'
+ curw = 1;
+ continue;
+ }
+ if (chr == 104) // 'Don't terminate with \n'
+ break;
+ }
+ } else {
+ if (chr == '@')
+ continue;
+ if (chr == 255 || (_vm->_version <= 6 && chr == 254)) {
+ chr = str[pos++];
+ if (chr == 3) // 'Wait'
+ break;
+ if (chr == 8) { // 'Verb on next line'
+ if (a == 1) {
+ curw = 1;
+ } else {
+ while (str[pos] == ' ')
+ str[pos++] = '@';
+ }
+ continue;
+ }
+ if (chr == 10 || chr == 21 || chr == 12 || chr == 13) {
+ pos += 2;
+ continue;
+ }
+ if (chr == 1) { // 'Newline'
+ curw = 1;
+ continue;
+ }
+ if (chr == 2) // 'Don't terminate with \n'
+ break;
+ if (chr == 14) {
+ int set = str[pos] | (str[pos + 1] << 8);
+ pos += 2;
+ setCurID(set);
+ continue;
+ }
+ }
+ }
+ if (chr == ' ')
+ lastspace = pos - 1;
+
+ if ((chr & 0x80) && _vm->_useCJKMode) {
+ pos++;
+ curw += _vm->_2byteWidth;
+ } else {
+ curw += getCharWidth(chr);
+ }
+ if (lastspace == -1)
+ continue;
+ if (curw > maxwidth) {
+ str[lastspace] = 0xD;
+ curw = 1;
+ pos = lastspace + 1;
+ lastspace = -1;
+ }
+ }
+
+ setCurID(oldID);
+}
+
+#ifdef PALMOS_68K
+static byte *englishCharsetDataV2;
+static byte *germanCharsetDataV2;
+static byte *frenchCharsetDataV2;
+static byte *italianCharsetDataV2;
+static byte *spanishCharsetDataV2;
+#else
+// English Zak font
+static byte englishCharsetDataV2[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x06, 0x0C, 0x18, 0x3E, 0x03, 0x00,
+ 0x80, 0xC0, 0x60, 0x30, 0x18, 0x7C, 0xC0, 0x00,
+ 0x00, 0x03, 0x3E, 0x18, 0x0C, 0x06, 0x03, 0x01,
+ 0x00, 0xC0, 0x7C, 0x18, 0x30, 0x60, 0xC0, 0x80,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x07, 0x07, 0x0F, 0x1F, 0x7F,
+ 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0,
+ 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+ 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0C, 0x18,
+ 0x00, 0x00, 0x00, 0x80, 0xC0, 0x60, 0x30, 0x18,
+ 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00,
+ 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x07, 0x0C, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x30, 0x18, 0x18,
+ 0x18, 0x18, 0x30, 0xE0, 0xC0, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x0C, 0x07, 0x03, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xF8, 0xF8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+ 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0x66, 0x3C, 0x18,
+ 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x18,
+ 0x18, 0x66, 0xC3, 0xDB, 0xDB, 0xC3, 0x66, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, 0x00,
+ 0x18, 0x3E, 0x58, 0x3C, 0x1A, 0x7C, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x49, 0x00,
+ 0x3C, 0x66, 0x3C, 0x38, 0x67, 0x66, 0x3F, 0x00,
+ 0x06, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00,
+ 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00,
+ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00,
+ 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E, 0x00,
+ 0x3C, 0x66, 0x06, 0x0C, 0x30, 0x60, 0x7E, 0x00,
+ 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00,
+ 0x06, 0x0E, 0x1E, 0x66, 0x7F, 0x06, 0x06, 0x00,
+ 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00,
+ 0x3C, 0x66, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00,
+ 0x7E, 0x66, 0x0C, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00,
+ 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x0E, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0E, 0x00,
+ 0x7C, 0x82, 0xBA, 0xA2, 0xBA, 0x82, 0x7C, 0x00,
+ 0x70, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x70, 0x00,
+ 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x00, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+ 0x18, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00,
+ 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00,
+ 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00,
+ 0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00,
+ 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7E, 0x00,
+ 0x7E, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00,
+ 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00,
+ 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00,
+ 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
+ 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00,
+ 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00,
+ 0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x00,
+ 0x66, 0x76, 0x7E, 0x7E, 0x6E, 0x66, 0x66, 0x00,
+ 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00,
+ 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x0E, 0x00,
+ 0x7C, 0x66, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x00,
+ 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00,
+ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00,
+ 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00,
+ 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00,
+ 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00,
+ 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00,
+ 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00,
+ 0x0C, 0x12, 0x30, 0x7C, 0x30, 0x62, 0xFC, 0x00,
+ 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xDB, 0x00,
+ 0x00, 0x10, 0x30, 0x7F, 0x7F, 0x30, 0x10, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00,
+ 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00,
+ 0x00, 0x00, 0x3C, 0x60, 0x60, 0x60, 0x3C, 0x00,
+ 0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00,
+ 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00,
+ 0x00, 0x0E, 0x18, 0x3E, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x7C,
+ 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3C, 0x00,
+ 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3C,
+ 0x00, 0x60, 0x60, 0x6C, 0x78, 0x6C, 0x66, 0x00,
+ 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00,
+ 0x00, 0x00, 0x66, 0x7F, 0x7F, 0x6B, 0x63, 0x00,
+ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00,
+ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60,
+ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06,
+ 0x00, 0x00, 0x7C, 0x66, 0x60, 0x60, 0x60, 0x00,
+ 0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00,
+ 0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x0E, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00,
+ 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x3E, 0x36, 0x00,
+ 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x0C, 0x78,
+ 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00,
+ 0x01, 0x03, 0x06, 0x6C, 0x78, 0x70, 0x60, 0x00,
+ 0x18, 0x3C, 0x7E, 0xFF, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xFF, 0x7E, 0x3C, 0x18,
+ 0x10, 0x30, 0x70, 0xFF, 0xFF, 0x70, 0x30, 0x10,
+ 0x08, 0x0C, 0x0E, 0xFF, 0xFF, 0x0E, 0x0C, 0x08,
+};
+
+// German Zak font
+static byte germanCharsetDataV2[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x06, 0x0c, 0x18, 0x3e, 0x03, 0x00,
+ 0x80, 0xc0, 0x60, 0x30, 0x18, 0x7c, 0xc0, 0x00,
+ 0x00, 0x03, 0x3e, 0x18, 0x0c, 0x06, 0x03, 0x01,
+ 0x00, 0xc0, 0x7c, 0x18, 0x30, 0x60, 0xc0, 0x80,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x1f, 0x7f,
+ 0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0,
+ 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+ 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0c, 0x18,
+ 0x00, 0x00, 0x00, 0x80, 0xc0, 0x60, 0x30, 0x18,
+ 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00,
+ 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x07, 0x0c, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xc0, 0xe0, 0x30, 0x18, 0x18,
+ 0x18, 0x18, 0x30, 0xe0, 0xc0, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x0c, 0x07, 0x03, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x66, 0x3c, 0x18,
+ 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18,
+ 0x18, 0x66, 0xc3, 0xdb, 0xdb, 0xc3, 0x66, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x66, 0xff, 0x66, 0xff, 0x66, 0x66, 0x00,
+ 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00,
+ 0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00,
+ 0x3c, 0x66, 0x3c, 0x38, 0x67, 0x66, 0x3f, 0x00,
+ 0x30, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00,
+ 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00,
+ 0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00,
+ 0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00,
+ 0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00,
+ 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00,
+ 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00,
+ 0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00,
+ 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x0e, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0e, 0x00,
+ 0x7c, 0x82, 0xba, 0xa2, 0xa2, 0xba, 0x82, 0x7c,
+ 0x70, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x70, 0x00,
+ 0x3c, 0x66, 0x06, 0x0c, 0x18, 0x00, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00,
+ 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00,
+ 0x78, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00,
+ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00,
+ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00,
+ 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
+ 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00,
+ 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00,
+ 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x00,
+ 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0e, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x78, 0x6c, 0x66, 0x00,
+ 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00,
+ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00,
+ 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00,
+ 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00,
+ 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00,
+ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x66, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0xdb, 0x00,
+ 0x00, 0x10, 0x30, 0x7f, 0x7f, 0x30, 0x10, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00,
+ 0x00, 0x00, 0x3c, 0x60, 0x60, 0x60, 0x3c, 0x00,
+ 0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x00, 0x0e, 0x18, 0x3e, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x7c,
+ 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
+ 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3c,
+ 0x00, 0x60, 0x60, 0x6c, 0x78, 0x6c, 0x66, 0x00,
+ 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0x00, 0x00, 0x66, 0x7f, 0x7f, 0x6b, 0x63, 0x00,
+ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60,
+ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06,
+ 0x00, 0x00, 0x7c, 0x66, 0x60, 0x60, 0x60, 0x00,
+ 0x00, 0x00, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x00,
+ 0x00, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x0e, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0x00, 0x00, 0x63, 0x6b, 0x7f, 0x3e, 0x36, 0x00,
+ 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x0c, 0x78,
+ 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00,
+ 0x66, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x18, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x00,
+ 0x42, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x1c, 0x36, 0x36, 0x7c, 0x66, 0x66, 0x7c, 0x40,
+ 0x08, 0x0c, 0x0e, 0xff, 0xff, 0x0e, 0x0c, 0x08,
+};
+
+// French Zak font.
+static byte frenchCharsetDataV2[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x06, 0x0c, 0x18, 0x3e, 0x03, 0x00,
+ 0x80, 0xc0, 0x60, 0x30, 0x18, 0x7c, 0xc0, 0x00,
+ 0x00, 0x03, 0x3e, 0x18, 0x0c, 0x06, 0x03, 0x01,
+ 0x00, 0xc0, 0x7c, 0x18, 0x30, 0x60, 0xc0, 0x80,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x1f, 0x7f,
+ 0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0,
+ 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+ 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0c, 0x18,
+ 0x00, 0x00, 0x00, 0x80, 0xc0, 0x60, 0x30, 0x18,
+ 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00,
+ 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x07, 0x0c, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xc0, 0xe0, 0x30, 0x18, 0x18,
+ 0x18, 0x18, 0x30, 0xe0, 0xc0, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x0c, 0x07, 0x03, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x66, 0x3c, 0x18,
+ 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18,
+ 0x18, 0x66, 0xc3, 0xdb, 0xdb, 0xc3, 0x66, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x66, 0xff, 0x66, 0xff, 0x66, 0x66, 0x00,
+ 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00,
+ 0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00,
+ 0x3c, 0x66, 0x3c, 0x38, 0x67, 0x66, 0x3f, 0x00,
+ 0x30, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00,
+ 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00,
+ 0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00,
+ 0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00,
+ 0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00,
+ 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00,
+ 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00,
+ 0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00,
+ 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x10, 0x08, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x18, 0x24, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x00, 0x00, 0x3c, 0x60, 0x60, 0x3c, 0x18, 0x38,
+ 0x3c, 0x66, 0x06, 0x0c, 0x18, 0x00, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00,
+ 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00,
+ 0x78, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00,
+ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00,
+ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00,
+ 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
+ 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00,
+ 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00,
+ 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x00,
+ 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0e, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x78, 0x6c, 0x66, 0x00,
+ 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00,
+ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00,
+ 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00,
+ 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00,
+ 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00,
+ 0x08, 0x10, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x10, 0x08, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x18, 0x24, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0xdb, 0x00,
+ 0x00, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00,
+ 0x00, 0x00, 0x3c, 0x60, 0x60, 0x60, 0x3c, 0x00,
+ 0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x00, 0x0e, 0x18, 0x3e, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x7c,
+ 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
+ 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3c,
+ 0x00, 0x60, 0x60, 0x6c, 0x78, 0x6c, 0x66, 0x00,
+ 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0x00, 0x00, 0x66, 0x7f, 0x7f, 0x6b, 0x63, 0x00,
+ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60,
+ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06,
+ 0x00, 0x00, 0x7c, 0x66, 0x60, 0x60, 0x60, 0x00,
+ 0x00, 0x00, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x00,
+ 0x00, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x0e, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0x00, 0x00, 0x63, 0x6b, 0x7f, 0x3e, 0x36, 0x00,
+ 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x0c, 0x78,
+ 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00,
+ 0x18, 0x24, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
+ 0x18, 0x24, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
+ 0x10, 0x08, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x18, 0x24, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x08, 0x0c, 0x0e, 0xff, 0xff, 0x0e, 0x0c, 0x08,
+};
+
+// Italian Zak font.
+static byte italianCharsetDataV2[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x06, 0x0c, 0x18, 0x3e, 0x03, 0x00,
+ 0x80, 0xc0, 0x60, 0x30, 0x18, 0x7c, 0xc0, 0x00,
+ 0x00, 0x03, 0x3e, 0x18, 0x0c, 0x06, 0x03, 0x01,
+ 0x00, 0xc0, 0x7c, 0x18, 0x30, 0x60, 0xc0, 0x80,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x1f, 0x7f,
+ 0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0,
+ 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+ 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0c, 0x18,
+ 0x00, 0x00, 0x00, 0x80, 0xc0, 0x60, 0x30, 0x18,
+ 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00,
+ 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x07, 0x0c, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xc0, 0xe0, 0x30, 0x18, 0x18,
+ 0x18, 0x18, 0x30, 0xe0, 0xc0, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x0c, 0x07, 0x03, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x66, 0x3c, 0x18,
+ 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18,
+ 0x18, 0x66, 0xc3, 0xdb, 0xdb, 0xc3, 0x66, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x66, 0xff, 0x66, 0xff, 0x66, 0x66, 0x00,
+ 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00,
+ 0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00,
+ 0x3c, 0x66, 0x3c, 0x38, 0x67, 0x66, 0x3f, 0x00,
+ 0x30, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00,
+ 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00,
+ 0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00,
+ 0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00,
+ 0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00,
+ 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00,
+ 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00,
+ 0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00,
+ 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x10, 0x08, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x08, 0x10, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x10, 0x08, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x3c, 0x66, 0x06, 0x0c, 0x18, 0x00, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00,
+ 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00,
+ 0x78, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00,
+ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00,
+ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00,
+ 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
+ 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00,
+ 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00,
+ 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x00,
+ 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0e, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x78, 0x6c, 0x66, 0x00,
+ 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00,
+ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00,
+ 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00,
+ 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00,
+ 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00,
+ 0x08, 0x10, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x10, 0x08, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x18, 0x24, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0xdb, 0x00,
+ 0x00, 0x6c, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00,
+ 0x00, 0x00, 0x3c, 0x60, 0x60, 0x60, 0x3c, 0x00,
+ 0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x00, 0x0e, 0x18, 0x3e, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x7c,
+ 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
+ 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3c,
+ 0x00, 0x60, 0x60, 0x6c, 0x78, 0x6c, 0x66, 0x00,
+ 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0x00, 0x00, 0x66, 0x7f, 0x7f, 0x6b, 0x63, 0x00,
+ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60,
+ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06,
+ 0x00, 0x00, 0x7c, 0x66, 0x60, 0x60, 0x60, 0x00,
+ 0x00, 0x00, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x00,
+ 0x00, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x0e, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0x00, 0x00, 0x63, 0x6b, 0x7f, 0x3e, 0x36, 0x00,
+ 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x0c, 0x78,
+ 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00,
+ 0x10, 0x08, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
+ 0x10, 0x08, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
+ 0x10, 0x08, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x18, 0x24, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x08, 0x0c, 0x0e, 0xff, 0xff, 0x0e, 0x0c, 0x08,
+};
+
+// Spanish Zak font.
+// FIXME: This is identical to germanCharsetDataV2 it seems?!
+static byte spanishCharsetDataV2[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x03, 0x06, 0x0c, 0x18, 0x3e, 0x03, 0x00,
+ 0x80, 0xc0, 0x60, 0x30, 0x18, 0x7c, 0xc0, 0x00,
+ 0x00, 0x03, 0x3e, 0x18, 0x0c, 0x06, 0x03, 0x01,
+ 0x00, 0xc0, 0x7c, 0x18, 0x30, 0x60, 0xc0, 0x80,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x1f, 0x7f,
+ 0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0,
+ 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+ 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0c, 0x18,
+ 0x00, 0x00, 0x00, 0x80, 0xc0, 0x60, 0x30, 0x18,
+ 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00,
+ 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x07, 0x0c, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xc0, 0xe0, 0x30, 0x18, 0x18,
+ 0x18, 0x18, 0x30, 0xe0, 0xc0, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x0c, 0x07, 0x03, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x1f, 0x1f, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xf8, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x66, 0x3c, 0x18,
+ 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18,
+ 0x18, 0x66, 0xc3, 0xdb, 0xdb, 0xc3, 0x66, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x66, 0xff, 0x66, 0xff, 0x66, 0x66, 0x00,
+ 0x18, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x18, 0x00,
+ 0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00,
+ 0x3c, 0x66, 0x3c, 0x38, 0x67, 0x66, 0x3f, 0x00,
+ 0x30, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00,
+ 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00,
+ 0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00,
+ 0x3c, 0x66, 0x06, 0x1c, 0x06, 0x66, 0x3c, 0x00,
+ 0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00,
+ 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00,
+ 0x3c, 0x66, 0x60, 0x7c, 0x66, 0x66, 0x3c, 0x00,
+ 0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00,
+ 0x3c, 0x66, 0x66, 0x3e, 0x06, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30,
+ 0x0e, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0e, 0x00,
+ 0x7c, 0x82, 0xba, 0xa2, 0xa2, 0xba, 0x82, 0x7c,
+ 0x70, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x70, 0x00,
+ 0x3c, 0x66, 0x06, 0x0c, 0x18, 0x00, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x7c, 0x00,
+ 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00,
+ 0x78, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00,
+ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x7e, 0x00,
+ 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00,
+ 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00,
+ 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00,
+ 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x00,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00,
+ 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x00,
+ 0x66, 0x76, 0x7e, 0x7e, 0x6e, 0x66, 0x66, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00,
+ 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0e, 0x00,
+ 0x7c, 0x66, 0x66, 0x7c, 0x78, 0x6c, 0x66, 0x00,
+ 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00,
+ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00,
+ 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00,
+ 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00,
+ 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00,
+ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x66, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0xdb, 0x00,
+ 0x00, 0x10, 0x30, 0x7f, 0x7f, 0x30, 0x10, 0x00,
+ 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
+ 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00,
+ 0x00, 0x00, 0x3c, 0x60, 0x60, 0x60, 0x3c, 0x00,
+ 0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0x00, 0x0e, 0x18, 0x3e, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x7c,
+ 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
+ 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3c,
+ 0x00, 0x60, 0x60, 0x6c, 0x78, 0x6c, 0x66, 0x00,
+ 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0x00, 0x00, 0x66, 0x7f, 0x7f, 0x6b, 0x63, 0x00,
+ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60,
+ 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06,
+ 0x00, 0x00, 0x7c, 0x66, 0x60, 0x60, 0x60, 0x00,
+ 0x00, 0x00, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x00,
+ 0x00, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x0e, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0x00, 0x00, 0x63, 0x6b, 0x7f, 0x3e, 0x36, 0x00,
+ 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x0c, 0x78,
+ 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00,
+ 0x66, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x66, 0x18, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x00,
+ 0x42, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
+ 0x1c, 0x36, 0x36, 0x7c, 0x66, 0x66, 0x7c, 0x40,
+ 0x08, 0x0c, 0x0e, 0xff, 0xff, 0x0e, 0x0c, 0x08,
+};
+#endif
+
+CharsetRendererV2::CharsetRendererV2(ScummEngine *vm, Common::Language language)
+ : CharsetRendererV3(vm) {
+
+ _fontHeight = 8;
+
+ switch (language) {
+ case Common::DE_DEU:
+ _fontPtr = germanCharsetDataV2;
+ break;
+ case Common::FR_FRA:
+ _fontPtr = frenchCharsetDataV2;
+ break;
+ case Common::IT_ITA:
+ _fontPtr = italianCharsetDataV2;
+ break;
+ case Common::ES_ESP:
+ _fontPtr = spanishCharsetDataV2;
+ break;
+ default:
+ _fontPtr = englishCharsetDataV2;
+ break;
+ }
+
+#if 0
+ // Decompress weird encoding in which the Zak executable contains the font.
+ // I leave the code around in case we need to use it again (e.g. we might
+ // have to include different fonts for french/spanish/russian/... version
+ // of MM / Zak
+ //
+ int count = 0, len;
+ byte b;
+ const byte *data = spanishCharsetDataV2;
+ const int size = sizeof(spanishCharsetDataV2);
+ for (int offset = 0; offset < size; offset++) {
+ if (data[offset+1] == 0x00 && data[offset+2] == 0xB2 &&
+ data[offset+5] == 0x00 && data[offset+6] == 0xB0) {
+ b = data[offset+3];
+ len = data[offset+4];
+ while (len--) {
+ printf("0x%02x, ", b);
+ count++;
+ if (count % 8 == 0)
+ printf("\n");
+ }
+ offset += 6;
+ } else {
+ printf("0x%02x, ", data[offset]);
+ count++;
+ if (count % 8 == 0)
+ printf("\n");
+ }
+ }
+ printf("\n");
+ _vm->_system->quit();
+#endif
+}
+
+int CharsetRendererV3::getCharWidth(byte chr) {
+ if (chr & 0x80 && _vm->_useCJKMode)
+ return _vm->_2byteWidth / 2;
+ int spacing = 0;
+
+ spacing = *(_widthTable + chr);
+
+ return spacing;
+}
+
+void CharsetRendererV3::setColor(byte color)
+{
+ bool useShadow = false;
+ _color = color;
+
+ // FM-TOWNS version of Loom uses old color method as well
+ if ((_vm->_version >= 2) && (_vm->_features & GF_16COLOR || (_vm->_gameId == GID_LOOM && _vm->_version == 3))) {
+ useShadow = ((_color & 0xF0) != 0);
+ _color &= 0x0f;
+ } else if (_vm->_features & GF_OLD256) {
+ useShadow = ((_color & 0x80) != 0);
+ _color &= 0x7f;
+ } else
+ useShadow = false;
+
+ enableShadow(useShadow);
+
+ translateColor();
+}
+
+void CharsetRendererCommon::enableShadow(bool enable) {
+ if (enable) {
+ if (_vm->_platform == Common::kPlatformFMTowns) {
+ _shadowColor = 8;
+ _shadowMode = kFMTOWNSShadowMode;
+ } else {
+ _shadowColor = 0;
+ _shadowMode = kNormalShadowMode;
+ }
+ } else {
+ _shadowMode = kNoShadowMode;
+ }
+}
+
+
+void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {
+ // Indy3 / Zak256 / Loom
+ int width, height, origWidth, origHeight;
+ VirtScreen *vs;
+ byte *charPtr, *dst;
+ int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
+
+ checkRange(_vm->_numCharsets - 1, 0, _curId, "Printing with bad charset %d");
+
+ if ((vs = _vm->findVirtScreen(_top)) == NULL)
+ return;
+
+ if (chr == '@')
+ return;
+
+ if (is2byte) {
+ charPtr = _vm->get2byteCharPtr(chr);
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ } else {
+ charPtr = _fontPtr + chr * 8;
+// width = height = 8;
+ width = getCharWidth(chr);
+ height = 8;
+ }
+
+ origWidth = width;
+ origHeight = height;
+
+ if (_shadowMode != kNoShadowMode) {
+ width++;
+ height++;
+ }
+
+ if (_firstChar) {
+ _str.left = _left;
+ _str.top = _top;
+ _str.right = _left;
+ _str.bottom = _top;
+ _firstChar = false;
+ }
+
+ int drawTop = _top - vs->topline;
+
+ _vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height);
+
+ if (!ignoreCharsetMask) {
+ _hasMask = true;
+ _textScreenID = vs->number;
+ }
+ if (ignoreCharsetMask || !vs->hasTwoBuffers) {
+ dst = vs->getPixels(_left, drawTop);
+ drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight);
+ } else {
+ dst = (byte *)_textSurface.pixels + _top * _textSurface.pitch + _left;
+ drawBits1(_textSurface, dst, charPtr, drawTop, origWidth, origHeight);
+ }
+
+ if (_str.left > _left)
+ _str.left = _left;
+
+ _left += origWidth;
+
+ if (_str.right < _left) {
+ _str.right = _left;
+ if (_shadowMode != kNoShadowMode)
+ _str.right++;
+ }
+
+ if (_str.bottom < _top + height)
+ _str.bottom = _top + height;
+}
+
+void CharsetRendererV3::drawChar(int chr, const Graphics::Surface &s, int x, int y) {
+ byte *charPtr, *dst;
+ int width, height;
+ int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
+ if (is2byte) {
+ charPtr = _vm->get2byteCharPtr(chr);
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ } else {
+ charPtr = _fontPtr + chr * 8;
+// width = height = 8;
+ width = getCharWidth(chr);
+ height = 8;
+ }
+ dst = (byte *)s.pixels + y * s.pitch + x;
+ drawBits1(s, dst, charPtr, y, width, height);
+}
+
+void CharsetRenderer::translateColor() {
+ // Based on disassembly
+ if (_vm->_renderMode == Common::kRenderCGA) {
+ static byte CGAtextColorMap[16] = {0, 3, 3, 3, 5, 5, 5, 15,
+ 15, 3, 3, 3, 5, 5, 15, 15};
+ _color = CGAtextColorMap[_color & 0x0f];
+ }
+
+ if (_vm->_renderMode == Common::kRenderHercA || _vm->_renderMode == Common::kRenderHercG) {
+ static byte HercTextColorMap[16] = {0, 15, 2, 15, 15, 5, 15, 15,
+ 8, 15, 15, 15, 15, 15, 15, 15};
+ _color = HercTextColorMap[_color & 0x0f];
+ }
+}
+
+
+void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
+ int width, height, origWidth, origHeight;
+ int offsX, offsY;
+ VirtScreen *vs;
+ const byte *charPtr;
+ int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
+
+ checkRange(_vm->_numCharsets - 1, 1, _curId, "Printing with bad charset %d");
+
+ if ((vs = _vm->findVirtScreen(_top)) == NULL && (vs = _vm->findVirtScreen(_top + getFontHeight())) == NULL)
+ return;
+
+ if (chr == '@')
+ return;
+
+ translateColor();
+
+ _vm->_charsetColorMap[1] = _color;
+
+ if (is2byte) {
+ enableShadow(true);
+ charPtr = _vm->get2byteCharPtr(chr);
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ offsX = offsY = 0;
+ } else {
+ uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
+ assert(charOffs < 0x10000);
+ if (!charOffs)
+ return;
+ charPtr = _fontPtr + charOffs;
+
+ width = charPtr[0];
+ height = charPtr[1];
+
+ if (_disableOffsX) {
+ offsX = 0;
+ } else {
+ offsX = (signed char)charPtr[2];
+ }
+
+ offsY = (signed char)charPtr[3];
+
+ charPtr += 4; // Skip over char header
+ }
+ origWidth = width;
+ origHeight = height;
+
+ if (_shadowMode != kNoShadowMode) {
+ width++;
+ height++;
+ }
+ if (_firstChar) {
+ _str.left = 0;
+ _str.top = 0;
+ _str.right = 0;
+ _str.bottom = 0;
+ }
+
+ _top += offsY;
+ _left += offsX;
+
+ if (_left + origWidth > _right + 1 || _left < 0) {
+ _left += origWidth;
+ _top -= offsY;
+ return;
+ }
+
+ _disableOffsX = false;
+
+ if (_firstChar) {
+ _str.left = _left;
+ _str.top = _top;
+ _str.right = _left;
+ _str.bottom = _top;
+ _firstChar = false;
+ }
+
+ if (_left < _str.left)
+ _str.left = _left;
+
+ if (_top < _str.top)
+ _str.top = _top;
+
+ int drawTop = _top - vs->topline;
+
+ _vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height);
+
+ byte *dstPtr;
+ byte *back = NULL;
+
+ if (!ignoreCharsetMask) {
+ _hasMask = true;
+ _textScreenID = vs->number;
+ }
+
+ if ((_vm->_heversion >= 71 && _bitDepth >= 8) || (_vm->_heversion >= 90 && _bitDepth == 0)) {
+#ifndef DISABLE_HE
+ if (ignoreCharsetMask || !vs->hasTwoBuffers) {
+ dstPtr = vs->getPixels(0, 0);
+ } else {
+ dstPtr = (byte *)_textSurface.pixels;
+ }
+
+ if (_blitAlso && vs->hasTwoBuffers) {
+ dstPtr = vs->getBackPixels(0, 0);
+ }
+
+ Common::Rect rScreen(vs->w, vs->h);
+ if (_bitDepth >= 8) {
+ byte imagePalette[256];
+ memset(imagePalette, 0, sizeof(imagePalette));
+ memcpy(imagePalette, _vm->_charsetColorMap, 4);
+ Wiz::copyWizImage(dstPtr, charPtr, vs->w, vs->h, _left, _top, origWidth, origHeight, &rScreen, 0, imagePalette);
+ } else {
+ Wiz::copyWizImage(dstPtr, charPtr, vs->w, vs->h, _left, _top, origWidth, origHeight, &rScreen);
+ }
+
+ if (_blitAlso && vs->hasTwoBuffers) {
+ Common::Rect dst(_left, _top, _left + origWidth, _top + origHeight);
+ _vm->gdi.copyVirtScreenBuffers(dst);
+ }
+#endif
+ } else {
+ Graphics::Surface dstSurface;
+ Graphics::Surface backSurface;
+ if (ignoreCharsetMask || !vs->hasTwoBuffers) {
+ dstSurface = *vs;
+ dstPtr = vs->getPixels(_left, drawTop);
+ } else {
+ dstSurface = _textSurface;
+ dstPtr = (byte *)_textSurface.pixels + (_top - _vm->_screenTop) * _textSurface.pitch + _left;
+ }
+
+ if (_blitAlso && vs->hasTwoBuffers) {
+ backSurface = dstSurface;
+ back = dstPtr;
+ dstSurface = *vs;
+ dstPtr = vs->getBackPixels(_left, drawTop);
+ }
+
+ if (!ignoreCharsetMask && vs->hasTwoBuffers) {
+ drawTop = _top - _vm->_screenTop;
+ }
+
+ if (is2byte) {
+ drawBits1(dstSurface, dstPtr, charPtr, drawTop, origWidth, origHeight);
+ } else {
+ drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight);
+ }
+
+ if (_blitAlso && vs->hasTwoBuffers) {
+ // FIXME: Revisiting this code, I think the _blitAlso mode is likely broken
+ // right now -- we are copying stuff from "dstPtr" to "back", but "dstPtr" really
+ // only conatains charset data...
+ // One way to fix this: don't copy etc.; rather simply render the char twice,
+ // once to each of the two buffers. That should hypothetically yield
+ // identical results, though I didn't try it and right now I don't know
+ // any spots where I can test this...
+ if (!ignoreCharsetMask)
+ warning("This might be broken -- please report where you encountered this to Fingolfin");
+
+ // Perform some clipping
+ int w = MIN(width, dstSurface.w - _left);
+ int h = MIN(height, dstSurface.h - drawTop);
+ if (_left < 0) {
+ w += _left;
+ back -= _left;
+ dstPtr -= _left;
+ }
+ if (drawTop < 0) {
+ h += drawTop;
+ back -= drawTop * backSurface.pitch;
+ dstPtr -= drawTop * dstSurface.pitch;
+ }
+
+ // Blit the image data
+ if (w > 0) {
+ while (h-- > 0) {
+ memcpy(back, dstPtr, w);
+ back += backSurface.pitch;
+ dstPtr += dstSurface.pitch;
+ }
+ }
+ }
+ }
+
+ _left += origWidth;
+
+ if (_str.right < _left) {
+ _str.right = _left;
+ if (_shadowMode != kNoShadowMode)
+ _str.right++;
+ }
+
+ if (_str.bottom < _top + height)
+ _str.bottom = _top + height;
+
+ _top -= offsY;
+}
+
+void CharsetRendererClassic::drawChar(int chr, const Graphics::Surface &s, int x, int y) {
+ const byte *charPtr;
+ byte *dst;
+ int width, height;
+ int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0;
+
+ if (is2byte) {
+ enableShadow(true);
+ charPtr = _vm->get2byteCharPtr(chr);
+ width = _vm->_2byteWidth;
+ height = _vm->_2byteHeight;
+ } else {
+ uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4);
+ assert(charOffs < 0x10000);
+ if (!charOffs)
+ return;
+ charPtr = _fontPtr + charOffs;
+
+ width = charPtr[0];
+ height = charPtr[1];
+
+ charPtr += 4; // Skip over char header
+ }
+
+ dst = (byte *)s.pixels + y * s.pitch + x;
+
+ if (is2byte) {
+ drawBits1(s, dst, charPtr, y, width, height);
+ } else {
+ drawBitsN(s, dst, charPtr, *_fontPtr, y, width, height);
+ }
+}
+
+void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) {
+ int y, x;
+ int color;
+ byte numbits, bits;
+
+ assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
+ bits = *src++;
+ numbits = 8;
+
+ for (y = 0; y < height && y + drawTop < s.h; y++) {
+ for (x = 0; x < width; x++) {
+ color = (bits >> (8 - bpp)) & 0xFF;
+
+ if (color && y + drawTop >= 0) {
+ *dst = _vm->_charsetColorMap[color];
+ }
+ dst++;
+ bits <<= bpp;
+ numbits -= bpp;
+ if (numbits == 0) {
+ bits = *src++;
+ numbits = 8;
+ }
+ }
+ dst += s.pitch - width;
+ }
+}
+
+void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height) {
+ int y, x;
+ byte bits = 0;
+
+ for (y = 0; y < height && y + drawTop < s.h; y++) {
+ for (x = 0; x < width; x++) {
+ if ((x % 8) == 0)
+ bits = *src++;
+ if ((bits & revBitMask(x % 8)) && y + drawTop >= 0) {
+ if (_shadowMode != kNoShadowMode) {
+ *(dst + 1) = _shadowColor;
+ *(dst + s.pitch) = _shadowColor;
+ if (_shadowMode != kFMTOWNSShadowMode)
+ *(dst + s.pitch + 1) = _shadowColor;
+ }
+ *dst = _color;
+ }
+ dst++;
+ }
+
+ dst += s.pitch - width;
+ }
+}
+
+#ifndef DISABLE_SCUMM_7_8
+CharsetRendererNut::CharsetRendererNut(ScummEngine *vm)
+ : CharsetRenderer(vm) {
+ _current = 0;
+
+ for (int i = 0; i < 5; i++) {
+ char fontname[256];
+ if ((_vm->_gameId == GID_CMI) && (_vm->_features & GF_DEMO) && (i == 4))
+ break;
+ sprintf(fontname, "font%d.nut", i);
+ _fr[i] = new NutRenderer(_vm);
+ if (!(_fr[i]->loadFont(fontname))) {
+ delete _fr[i];
+ _fr[i] = NULL;
+ }
+ }
+}
+
+CharsetRendererNut::~CharsetRendererNut() {
+ for (int i = 0; i < 5; i++) {
+ if ((_vm->_gameId == GID_CMI) && (_vm->_features & GF_DEMO) && (i == 4))
+ break;
+ delete _fr[i];
+ }
+}
+
+void CharsetRendererNut::setCurID(byte id) {
+ assert(id < 5);
+ _curId = id;
+ _current = _fr[id];
+ assert(_current);
+}
+
+int CharsetRendererNut::getCharHeight(byte chr) {
+ assert(_current);
+ return _current->getCharHeight(chr);
+}
+
+int CharsetRendererNut::getCharWidth(byte chr) {
+ assert(_current);
+ return _current->getCharWidth(chr);
+}
+
+int CharsetRendererNut::getFontHeight() {
+ // FIXME / TODO: how to implement this properly???
+ assert(_current);
+ return _current->getCharHeight('|');
+}
+
+void CharsetRendererNut::printChar(int chr, bool ignoreCharsetMask) {
+ Common::Rect shadow;
+
+ assert(_current);
+ if (chr == '@')
+ return;
+
+ shadow.left = _left - 1;
+ shadow.top = _top - 1;
+
+ // Note that the character is drawn with a shadow, so it is slightly
+ // larger than the advertised dimensions. See drawShadowChar() for
+ // details.
+
+ if (_firstChar) {
+ _str.left = (shadow.left >= 0) ? shadow.left : 0;
+ _str.top = (shadow.top >= 0) ? shadow.top : 0;
+ _str.right = _str.left;
+ _str.bottom = _str.top;
+ _firstChar = false;
+ }
+
+ int width = _current->getCharWidth(chr);
+ int height = _current->getCharHeight(chr);
+
+ if (chr >= 256 && _vm->_useCJKMode)
+ width = _vm->_2byteWidth;
+
+ shadow.right = _left + width + 2;
+ shadow.bottom = _top + height + 2;
+
+ Graphics::Surface s;
+ if (!ignoreCharsetMask) {
+ _hasMask = true;
+ _textScreenID = kMainVirtScreen;
+ }
+
+ int drawTop = _top;
+ if (ignoreCharsetMask) {
+ VirtScreen *vs = &_vm->virtscr[kMainVirtScreen];
+ s = *vs;
+ s.pixels = vs->getPixels(0, 0);
+ } else {
+ s = _textSurface;
+ drawTop -= _vm->_screenTop;
+ }
+
+ _current->drawShadowChar(s, chr, _left, drawTop, _color, _curId != 3);
+ _vm->markRectAsDirty(kMainVirtScreen, shadow);
+
+ if (_str.left > _left)
+ _str.left = _left;
+
+ _left += width;
+
+ if (_str.right < shadow.right)
+ _str.right = shadow.right;
+
+ if (_str.bottom < shadow.bottom)
+ _str.bottom = shadow.bottom;
+}
+#endif
+
+void CharsetRendererNES::printChar(int chr, bool ignoreCharsetMask) {
+ int width, height, origWidth, origHeight;
+ VirtScreen *vs;
+ byte *charPtr, *dst;
+
+ // Init it here each time since it is cheap and fixes bug with
+ // charset after game load
+ _trTable = _vm->getResourceAddress(rtCostume, 77) + 2;
+
+ // HACK: how to set it properly?
+ if (_top == 0)
+ _top = 16;
+
+ if ((vs = _vm->findVirtScreen(_top)) == NULL)
+ return;
+
+ if (chr == '@')
+ return;
+
+ charPtr = _vm->_NESPatTable[1] + _trTable[chr - 32] * 16;
+ width = getCharWidth(chr);
+ height = 8;
+
+ origWidth = width;
+ origHeight = height;
+
+ if (_firstChar) {
+ _str.left = _left;
+ _str.top = _top;
+ _str.right = _left;
+ _str.bottom = _top;
+ _firstChar = false;
+ }
+
+ int drawTop = _top - vs->topline;
+
+ _vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height);
+
+ if (!ignoreCharsetMask) {
+ _hasMask = true;
+ _textScreenID = vs->number;
+ }
+
+ if (ignoreCharsetMask || !vs->hasTwoBuffers) {
+ dst = vs->getPixels(_left, drawTop);
+ drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight);
+ } else {
+ dst = (byte *)_textSurface.pixels + _top * _textSurface.pitch + _left;
+ drawBits1(_textSurface, dst, charPtr, drawTop, origWidth, origHeight);
+ }
+
+ if (_str.left > _left)
+ _str.left = _left;
+
+ _left += origWidth;
+
+ if (_str.right < _left) {
+ _str.right = _left;
+ if (_shadowMode != kNoShadowMode)
+ _str.right++;
+ }
+
+ if (_str.bottom < _top + height)
+ _str.bottom = _top + height;
+}
+
+void CharsetRendererNES::drawChar(int chr, const Graphics::Surface &s, int x, int y) {
+ byte *charPtr, *dst;
+ int width, height;
+
+ if (!_trTable)
+ _trTable = _vm->getResourceAddress(rtCostume, 77) + 2;
+
+ charPtr = _vm->_NESPatTable[1] + _trTable[chr - 32] * 16;
+ width = getCharWidth(chr);
+ height = 8;
+
+ dst = (byte *)s.pixels + y * s.pitch + x;
+ drawBits1(s, dst, charPtr, y, width, height);
+}
+
+void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height) {
+ for (int i = 0; i < 8; i++) {
+ byte c0 = src[i];
+ byte c1 = src[i + 8];
+ for (int j = 0; j < 8; j++)
+ dst[j] = _vm->_NESPalette[0][((c0 >> (7 - j)) & 1) | (((c1 >> (7 - j)) & 1) << 1) |
+ (_color ? 12 : 8)];
+ dst += s.pitch;
+ }
+}
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Charset)
+_GSETPTR(Scumm::germanCharsetDataV2, GBVARS_GERMANCHARSETDATAV2_INDEX, byte, GBVARS_SCUMM)
+_GSETPTR(Scumm::frenchCharsetDataV2, GBVARS_FRENCHCHARSETDATAV2_INDEX, byte, GBVARS_SCUMM)
+_GSETPTR(Scumm::englishCharsetDataV2, GBVARS_ENGLISHCHARSETDATAV2_INDEX, byte, GBVARS_SCUMM)
+_GSETPTR(Scumm::italianCharsetDataV2, GBVARS_ITALIANCHARSETDATAV2_INDEX, byte, GBVARS_SCUMM)
+_GSETPTR(Scumm::spanishCharsetDataV2, GBVARS_SPANISHCHARSETDATAV2_INDEX, byte, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(Charset)
+_GRELEASEPTR(GBVARS_GERMANCHARSETDATAV2_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FRENCHCHARSETDATAV2_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_ENGLISHCHARSETDATAV2_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_ITALIANCHARSETDATAV2_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_SPANISHCHARSETDATAV2_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
new file mode 100644
index 0000000000..3746f3be4e
--- /dev/null
+++ b/engines/scumm/charset.h
@@ -0,0 +1,202 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef CHARSET_H
+#define CHARSET_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+#include "scumm/gfx.h"
+
+namespace Scumm {
+
+class ScummEngine;
+class NutRenderer;
+struct VirtScreen;
+
+static inline bool checkSJISCode(byte c) {
+ if ((c > 0x84 && c < 0x88) || (c > 0x9f && c < 0xe0) || (c > 0xea /* && c <= 0xff */))
+ return false;
+ return true;
+}
+
+
+class CharsetRenderer {
+public:
+
+ Common::Rect _str;
+ int _nextLeft, _nextTop;
+
+ int _top;
+ int _left, _startLeft;
+ int _right;
+
+protected:
+ byte _color;
+
+public:
+ bool _center;
+
+ bool _hasMask; // True if "removable" text is visible somewhere (should be called _hasText or so)
+ VirtScreenNumber _textScreenID; // ID of the virtual screen on which the text is visible.
+
+ bool _blitAlso;
+ bool _firstChar;
+ bool _disableOffsX;
+
+ /**
+ * All text is normally rendered into this overlay surface. Then later
+ * drawStripToScreen() composits it over the game graphics.
+ */
+ Graphics::Surface _textSurface;
+
+protected:
+ ScummEngine *_vm;
+ byte _curId;
+
+public:
+ CharsetRenderer(ScummEngine *vm);
+ virtual ~CharsetRenderer();
+
+ void restoreCharsetBg();
+ void clearCharsetMask();
+ void clearTextSurface();
+
+ virtual void printChar(int chr, bool ignoreCharsetMask) = 0;
+ virtual void drawChar(int chr, const Graphics::Surface &s, int x, int y) {}
+
+ int getStringWidth(int a, const byte *str);
+ void addLinebreaks(int a, byte *str, int pos, int maxwidth);
+ void translateColor();
+
+ virtual void setCurID(byte id) = 0;
+ int getCurID() { return _curId; }
+
+ virtual int getFontHeight() = 0;
+ virtual int getCharHeight(byte chr) { return getFontHeight(); }
+ virtual int getCharWidth(byte chr) = 0;
+
+ virtual void setColor(byte color) { _color = color; translateColor(); }
+};
+
+class CharsetRendererCommon : public CharsetRenderer {
+protected:
+ byte *_fontPtr;
+ int _bitDepth;
+ int _fontHeight;
+ int _numChars;
+
+ enum ShadowMode {
+ kNoShadowMode,
+ kFMTOWNSShadowMode,
+ kNormalShadowMode
+ };
+ byte _shadowColor;
+ ShadowMode _shadowMode;
+
+ void enableShadow(bool enable);
+ void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height);
+
+public:
+ CharsetRendererCommon(ScummEngine *vm);
+
+ void setCurID(byte id);
+
+ int getFontHeight();
+};
+
+class CharsetRendererClassic : public CharsetRendererCommon {
+protected:
+ void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height);
+
+public:
+ CharsetRendererClassic(ScummEngine *vm) : CharsetRendererCommon(vm) {}
+
+ void printChar(int chr, bool ignoreCharsetMask);
+ void drawChar(int chr, const Graphics::Surface &s, int x, int y);
+
+ int getCharWidth(byte chr);
+};
+
+class CharsetRendererNES : public CharsetRendererCommon {
+protected:
+ byte *_trTable;
+
+ void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height);
+
+public:
+ CharsetRendererNES(ScummEngine *vm) : CharsetRendererCommon(vm) {}
+
+ void setCurID(byte id) {}
+ void printChar(int chr, bool ignoreCharsetMask);
+ void drawChar(int chr, const Graphics::Surface &s, int x, int y);
+
+ int getFontHeight() { return 8; }
+ int getCharWidth(byte chr) { return 8; }
+};
+
+class CharsetRendererV3 : public CharsetRendererCommon {
+protected:
+ byte *_widthTable;
+
+public:
+ CharsetRendererV3(ScummEngine *vm) : CharsetRendererCommon(vm) {}
+
+ void printChar(int chr, bool ignoreCharsetMask);
+ void drawChar(int chr, const Graphics::Surface &s, int x, int y);
+ void setCurID(byte id);
+ void setColor(byte color);
+ int getFontHeight();
+ int getCharWidth(byte chr);
+};
+
+class CharsetRendererV2 : public CharsetRendererV3 {
+public:
+ CharsetRendererV2(ScummEngine *vm, Common::Language language);
+
+ void setCurID(byte id) {}
+ int getCharWidth(byte chr) { return 8; }
+};
+
+#ifndef DISABLE_SCUMM_7_8
+class CharsetRendererNut : public CharsetRenderer {
+protected:
+ NutRenderer *_fr[5];
+ NutRenderer *_current;
+
+public:
+ CharsetRendererNut(ScummEngine *vm);
+ ~CharsetRendererNut();
+
+ void printChar(int chr, bool ignoreCharsetMask);
+
+ void setCurID(byte id);
+
+ int getFontHeight();
+ int getCharHeight(byte chr);
+ int getCharWidth(byte chr);
+};
+#endif
+
+} // End of namespace Scumm
+
+
+#endif
diff --git a/engines/scumm/costume.cpp b/engines/scumm/costume.cpp
new file mode 100644
index 0000000000..48f81111c6
--- /dev/null
+++ b/engines/scumm/costume.cpp
@@ -0,0 +1,1160 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/actor.h"
+#include "scumm/costume.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#ifdef PALMOS_68K
+const byte *smallCostumeScaleTable;
+#else
+const byte smallCostumeScaleTable[256] = {
+ 0xFF, 0xFD, 0x7D, 0xBD, 0x3D, 0xDD, 0x5D, 0x9D,
+ 0x1D, 0xED, 0x6D, 0xAD, 0x2D, 0xCD, 0x4D, 0x8D,
+ 0x0D, 0xF5, 0x75, 0xB5, 0x35, 0xD5, 0x55, 0x95,
+ 0x15, 0xE5, 0x65, 0xA5, 0x25, 0xC5, 0x45, 0x85,
+ 0x05, 0xF9, 0x79, 0xB9, 0x39, 0xD9, 0x59, 0x99,
+ 0x19, 0xE9, 0x69, 0xA9, 0x29, 0xC9, 0x49, 0x89,
+ 0x09, 0xF1, 0x71, 0xB1, 0x31, 0xD1, 0x51, 0x91,
+ 0x11, 0xE1, 0x61, 0xA1, 0x21, 0xC1, 0x41, 0x81,
+ 0x01, 0xFB, 0x7B, 0xBB, 0x3B, 0xDB, 0x5B, 0x9B,
+ 0x1B, 0xEB, 0x6B, 0xAB, 0x2B, 0xCB, 0x4B, 0x8B,
+ 0x0B, 0xF3, 0x73, 0xB3, 0x33, 0xD3, 0x53, 0x93,
+ 0x13, 0xE3, 0x63, 0xA3, 0x23, 0xC3, 0x43, 0x83,
+ 0x03, 0xF7, 0x77, 0xB7, 0x37, 0xD7, 0x57, 0x97,
+ 0x17, 0xE7, 0x67, 0xA7, 0x27, 0xC7, 0x47, 0x87,
+ 0x07, 0xEF, 0x6F, 0xAF, 0x2F, 0xCF, 0x4F, 0x8F,
+ 0x0F, 0xDF, 0x5F, 0x9F, 0x1F, 0xBF, 0x3F, 0x7F,
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE
+};
+#endif
+
+static const int v1MMNESLookup[25] = {
+ 0x00, 0x03, 0x01, 0x06, 0x08,
+ 0x02, 0x00, 0x07, 0x0C, 0x04,
+ 0x09, 0x0A, 0x12, 0x0B, 0x14,
+ 0x0D, 0x11, 0x0F, 0x0E, 0x10,
+ 0x17, 0x00, 0x01, 0x05, 0x16
+};
+
+
+byte ClassicCostumeRenderer::mainRoutine(int xmoveCur, int ymoveCur) {
+ int i, skip = 0;
+ byte drawFlag = 1;
+ bool use_scaling;
+ byte startScaleIndexX;
+ int ex1, ex2;
+ Common::Rect rect;
+ int step;
+ Codec1 v1;
+
+
+ const int scaletableSize = 128;
+ const bool newAmiCost = (_vm->_version == 5) && (_vm->_platform == Common::kPlatformAmiga);
+
+ CHECK_HEAP
+
+ v1.scaletable = smallCostumeScaleTable;
+
+ if (_loaded._numColors == 32) {
+ v1.mask = 7;
+ v1.shr = 3;
+ } else {
+ v1.mask = 15;
+ v1.shr = 4;
+ }
+
+ switch (_loaded._format) {
+ case 0x60:
+ case 0x61:
+ // This format is used e.g. in the Sam&Max intro
+ ex1 = _srcptr[0];
+ ex2 = _srcptr[1];
+ _srcptr += 2;
+ if (ex1 != 0xFF || ex2 != 0xFF) {
+ ex1 = READ_LE_UINT16(_loaded._frameOffsets + ex1 * 2);
+ _srcptr = _loaded._baseptr + READ_LE_UINT16(_loaded._baseptr + ex1 + ex2 * 2) + 14;
+ }
+ }
+
+ use_scaling = (_scaleX != 0xFF) || (_scaleY != 0xFF);
+
+ v1.x = _actorX;
+ v1.y = _actorY;
+
+ if (use_scaling) {
+
+ /* Scale direction */
+ v1.scaleXstep = -1;
+ if (xmoveCur < 0) {
+ xmoveCur = -xmoveCur;
+ v1.scaleXstep = 1;
+ }
+
+ if (_mirror) {
+ /* Adjust X position */
+ startScaleIndexX = _scaleIndexX = scaletableSize - xmoveCur;
+ for (i = 0; i < xmoveCur; i++) {
+ if (v1.scaletable[_scaleIndexX++] < _scaleX)
+ v1.x -= v1.scaleXstep;
+ }
+
+ rect.left = rect.right = v1.x;
+
+ _scaleIndexX = startScaleIndexX;
+ for (i = 0; i < _width; i++) {
+ if (rect.right < 0) {
+ skip++;
+ startScaleIndexX = _scaleIndexX;
+ }
+ if (v1.scaletable[_scaleIndexX++] < _scaleX)
+ rect.right++;
+ }
+ } else {
+ /* No mirror */
+ /* Adjust X position */
+ startScaleIndexX = _scaleIndexX = xmoveCur + scaletableSize;
+ for (i = 0; i < xmoveCur; i++) {
+ if (v1.scaletable[_scaleIndexX--] < _scaleX)
+ v1.x += v1.scaleXstep;
+ }
+
+ rect.left = rect.right = v1.x;
+
+ _scaleIndexX = startScaleIndexX;
+ for (i = 0; i < _width; i++) {
+ if (rect.left >= _out.w) {
+ startScaleIndexX = _scaleIndexX;
+ skip++;
+ }
+ if (v1.scaletable[_scaleIndexX--] < _scaleX)
+ rect.left--;
+ }
+ }
+ _scaleIndexX = startScaleIndexX;
+
+ if (skip)
+ skip--;
+
+ step = -1;
+ if (ymoveCur < 0) {
+ ymoveCur = -ymoveCur;
+ step = 1;
+ }
+
+ _scaleIndexY = scaletableSize - ymoveCur;
+ for (i = 0; i < ymoveCur; i++) {
+ if (v1.scaletable[_scaleIndexY++] < _scaleY)
+ v1.y -= step;
+ }
+
+ rect.top = rect.bottom = v1.y;
+ _scaleIndexY = scaletableSize - ymoveCur;
+ for (i = 0; i < _height; i++) {
+ if (v1.scaletable[_scaleIndexY++] < _scaleY)
+ rect.bottom++;
+ }
+
+ _scaleIndexY = scaletableSize - ymoveCur;
+ } else {
+ if (!_mirror)
+ xmoveCur = -xmoveCur;
+
+ v1.x += xmoveCur;
+ v1.y += ymoveCur;
+
+ if (_mirror) {
+ rect.left = v1.x;
+ rect.right = v1.x + _width;
+ } else {
+ rect.left = v1.x - _width;
+ rect.right = v1.x;
+ }
+
+ rect.top = v1.y;
+ rect.bottom = rect.top + _height;
+
+ }
+
+ v1.skip_width = _width;
+ v1.scaleXstep = _mirror ? 1 : -1;
+
+ if (_vm->_version == 1)
+ // V1 games uses 8 x 8 pixels for actors
+ _vm->markRectAsDirty(kMainVirtScreen, rect.left, rect.right + 8, rect.top, rect.bottom, _actorID);
+ else
+ _vm->markRectAsDirty(kMainVirtScreen, rect.left, rect.right + 1, rect.top, rect.bottom, _actorID);
+
+ if (rect.top >= _out.h || rect.bottom <= 0)
+ return 0;
+
+ if (rect.left >= _out.w || rect.right <= 0)
+ return 0;
+
+ v1.replen = 0;
+
+ if (_mirror) {
+ if (!use_scaling)
+ skip = -v1.x;
+ if (skip > 0) {
+ if (!newAmiCost && _loaded._format != 0x57) {
+ v1.skip_width -= skip;
+ codec1_ignorePakCols(v1, skip);
+ v1.x = 0;
+ }
+ } else {
+ skip = rect.right - _out.w;
+ if (skip <= 0) {
+ drawFlag = 2;
+ } else {
+ v1.skip_width -= skip;
+ }
+ }
+ } else {
+ if (!use_scaling)
+ skip = rect.right - _out.w;
+ if (skip > 0) {
+ if (!newAmiCost && _loaded._format != 0x57) {
+ v1.skip_width -= skip;
+ codec1_ignorePakCols(v1, skip);
+ v1.x = _out.w - 1;
+ }
+ } else {
+ // V1 games uses 8 x 8 pixels for actors
+ if (_loaded._format == 0x57)
+ skip = -8 - rect.left;
+ else
+ skip = -1 - rect.left;
+ if (skip <= 0)
+ drawFlag = 2;
+ else
+ v1.skip_width -= skip;
+ }
+ }
+
+ if (v1.skip_width <= 0)
+ return 0;
+
+ if (rect.left < 0)
+ rect.left = 0;
+
+ if (rect.top < 0)
+ rect.top = 0;
+
+ if (rect.top > _out.h)
+ rect.top = _out.h;
+
+ if (rect.bottom > _out.h)
+ rect.bottom = _out.h;
+
+ if (_draw_top > rect.top)
+ _draw_top = rect.top;
+ if (_draw_bottom < rect.bottom)
+ _draw_bottom = rect.bottom;
+
+ if (_height + rect.top >= 256) {
+ CHECK_HEAP
+ return 2;
+ }
+
+ v1.destptr = (byte *)_out.pixels + v1.y * _out.pitch + v1.x;
+
+ v1.mask_ptr = _vm->getMaskBuffer(0, v1.y, _zbuf);
+
+ CHECK_HEAP
+
+ if (_loaded._format == 0x57) {
+ // The v1 costume renderer needs the actor number, which is
+ // the same thing as the costume renderer's _actorID.
+ procC64(v1, _actorID);
+ } else if (newAmiCost)
+ proc3_ami(v1);
+ else
+ proc3(v1);
+
+ CHECK_HEAP
+ return drawFlag;
+}
+
+static const int v1MMActorPalatte1[25] = {
+ 8, 8, 8, 8, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
+};
+static const int v1MMActorPalatte2[25] = {
+ 0, 7, 2, 6, 9, 1, 3, 7, 7, 1, 1, 9, 1, 4, 5, 5, 4, 1, 0, 5, 4, 2, 2, 7, 7
+};
+
+#define MASK_AT(xoff) \
+ (mask && (mask[((v1.x + xoff) / 8)] & revBitMask((v1.x + xoff) & 7)))
+#define LINE(c,p) \
+ pcolor = (color >> c) & 3; \
+ if (pcolor) { \
+ if (!MASK_AT(p)) \
+ dst[p] = palette[pcolor]; \
+ if (!MASK_AT(p + 1)) \
+ dst[p + 1] = palette[pcolor]; \
+ }
+
+void ClassicCostumeRenderer::procC64(Codec1 &v1, int actor) {
+ const byte *mask, *src;
+ byte *dst;
+ byte len;
+ int y;
+ uint height;
+ byte color, pcolor;
+ bool rep;
+
+ y = v1.y;
+ src = _srcptr;
+ dst = v1.destptr;
+ len = v1.replen;
+ color = v1.repcolor;
+ height = _height;
+
+ v1.skip_width /= 8;
+
+ // Set up the palette data
+ byte palette[4] = { 0, 0, 0, 0 };
+ if (!(_vm->VAR(_vm->VAR_CURRENT_LIGHTS) & LIGHTMODE_actor_color)) {
+ palette[2] = 11;
+ palette[3] = 11;
+ } else if (_vm->_gameId == GID_MANIAC) {
+ palette[1] = v1MMActorPalatte1[actor];
+ palette[2] = v1MMActorPalatte2[actor];
+ } else {
+ palette[1] = (_vm->_platform == Common::kPlatformC64) ? 10 : 8;
+ palette[2] = _palette[actor];
+ }
+ mask = v1.mask_ptr;
+
+ if (len)
+ goto StartPos;
+
+ do {
+ len = *src++;
+ if (len & 0x80)
+ color = *src++;
+ StartPos:;
+ rep = (len & 0x80) != 0;
+ len &= 0x7f;
+ while (len--) {
+ if (!rep)
+ color = *src++;
+
+ if (0 <= y && y < _out.h && 0 <= v1.x && v1.x < _out.w) {
+ if (!_mirror) {
+ LINE(0, 0); LINE(2, 2); LINE(4, 4); LINE(6, 6);
+ } else {
+ LINE(6, 0); LINE(4, 2); LINE(2, 4); LINE(0, 6);
+ }
+ }
+ dst += _out.pitch;
+ y++;
+ mask += _numStrips;
+ if (!--height) {
+ if (!--v1.skip_width)
+ return;
+ height = _height;
+ y = v1.y;
+ v1.x += 8 * v1.scaleXstep;
+ if (v1.x < 0 || v1.x >= _out.w)
+ return;
+ mask = v1.mask_ptr;
+ v1.destptr += 8 * v1.scaleXstep;
+ dst = v1.destptr;
+ }
+ }
+ } while (1);
+}
+
+#undef LINE
+#undef MASK_AT
+
+void ClassicCostumeRenderer::proc3(Codec1 &v1) {
+ const byte *mask, *src;
+ byte *dst;
+ byte len, maskbit;
+ int y;
+ uint color, height, pcolor;
+ const byte *scaleytab;
+ bool masked;
+
+ y = v1.y;
+ src = _srcptr;
+ dst = v1.destptr;
+ len = v1.replen;
+ color = v1.repcolor;
+ height = _height;
+
+ scaleytab = &v1.scaletable[_scaleIndexY];
+ maskbit = revBitMask(v1.x & 7);
+ mask = v1.mask_ptr + v1.x / 8;
+
+ if (len)
+ goto StartPos;
+
+ do {
+ len = *src++;
+ color = len >> v1.shr;
+ len &= v1.mask;
+ if (!len)
+ len = *src++;
+
+ do {
+ if (_scaleY == 255 || *scaleytab++ < _scaleY) {
+ masked = (y < 0 || y >= _out.h) || (v1.mask_ptr && (mask[0] & maskbit));
+
+ if (color && !masked) {
+ if (_shadow_mode & 0x20) {
+ pcolor = _shadow_table[*dst];
+ } else {
+ pcolor = _palette[color];
+ if (pcolor == 13 && _shadow_table)
+ pcolor = _shadow_table[*dst];
+ }
+ *dst = pcolor;
+ }
+ dst += _out.pitch;
+ mask += _numStrips;
+ y++;
+ }
+ if (!--height) {
+ if (!--v1.skip_width)
+ return;
+ height = _height;
+ y = v1.y;
+
+ scaleytab = &v1.scaletable[_scaleIndexY];
+
+ if (_scaleX == 255 || v1.scaletable[_scaleIndexX] < _scaleX) {
+ v1.x += v1.scaleXstep;
+ if (v1.x < 0 || v1.x >= _out.w)
+ return;
+ maskbit = revBitMask(v1.x & 7);
+ v1.destptr += v1.scaleXstep;
+ }
+ _scaleIndexX += v1.scaleXstep;
+ dst = v1.destptr;
+ mask = v1.mask_ptr + v1.x / 8;
+ }
+ StartPos:;
+ } while (--len);
+ } while (1);
+}
+
+void ClassicCostumeRenderer::proc3_ami(Codec1 &v1) {
+ const byte *mask, *src;
+ byte *dst;
+ byte maskbit, len, height, width;
+ int color;
+ int y;
+ bool masked;
+ int oldXpos, oldScaleIndexX;
+
+ mask = v1.mask_ptr + v1.x / 8;
+ dst = v1.destptr;
+ height = _height;
+ width = _width;
+ src = _srcptr;
+ maskbit = revBitMask(v1.x & 7);
+ y = v1.y;
+ oldXpos = v1.x;
+ oldScaleIndexX = _scaleIndexX;
+
+ do {
+ len = *src++;
+ color = len >> v1.shr;
+ len &= v1.mask;
+ if (!len)
+ len = *src++;
+ do {
+ if (_scaleY == 255 || v1.scaletable[_scaleIndexY] < _scaleY) {
+ masked = (y < 0 || y >= _out.h) || (v1.mask_ptr && (mask[0] & maskbit));
+
+ if (color && v1.x >= 0 && v1.x < _out.w && !masked) {
+ *dst = _palette[color];
+ }
+
+ if (_scaleX == 255 || v1.scaletable[_scaleIndexX] < _scaleX) {
+ v1.x += v1.scaleXstep;
+ dst += v1.scaleXstep;
+ maskbit = revBitMask(v1.x & 7);
+ }
+ _scaleIndexX += v1.scaleXstep;
+ mask = v1.mask_ptr + v1.x / 8;
+ }
+ if (!--width) {
+ if (!--height)
+ return;
+
+ if (y >= _out.h)
+ return;
+
+ if (v1.x != oldXpos) {
+ dst += _out.pitch - (v1.x - oldXpos);
+ v1.mask_ptr += _numStrips;
+ mask = v1.mask_ptr + oldXpos / 8;
+ maskbit = revBitMask(oldXpos & 7);
+ y++;
+ }
+ width = _width;
+ v1.x = oldXpos;
+ _scaleIndexX = oldScaleIndexX;
+ _scaleIndexY++;
+ }
+ } while (--len);
+ } while (1);
+}
+
+void ClassicCostumeLoader::loadCostume(int id) {
+ _id = id;
+ byte *ptr = _vm->getResourceAddress(rtCostume, id);
+
+ if (_vm->_version >= 6)
+ ptr += 8;
+ else if (_vm->_features & GF_OLD_BUNDLE)
+ ptr += -2;
+ else if (_vm->_features & GF_SMALL_HEADER)
+ ptr += 0;
+ else
+ ptr += 2;
+
+ _baseptr = ptr;
+
+ _numAnim = ptr[6];
+ _format = ptr[7] & 0x7F;
+ _mirror = (ptr[7] & 0x80) != 0;
+ _palette = ptr + 8;
+ switch (_format) {
+ case 0x57: // Only used in V1 games
+ _numColors = 0;
+ break;
+ case 0x58:
+ _numColors = 16;
+ break;
+ case 0x59:
+ _numColors = 32;
+ break;
+ case 0x60: // New since version 6
+ _numColors = 16;
+ break;
+ case 0x61: // New since version 6
+ _numColors = 32;
+ break;
+ default:
+ error("Costume %d with format 0x%X is invalid", id, _format);
+ }
+
+
+ // In GF_OLD_BUNDLE games, there is no actual palette, just a single color byte.
+ // Don't forget, these games were designed around a fixed 16 color HW palette :-)
+ // In addition, all offsets are shifted by 2; we accomodate that via a separate
+ // _baseptr value (instead of adding tons of if's throughout the code).
+ if (_vm->_features & GF_OLD_BUNDLE) {
+ _numColors = (_format == 0x57) ? 0 : 1;
+ _baseptr += 2;
+ }
+ ptr += 8 + _numColors;
+ _frameOffsets = ptr + 2;
+ if (_format == 0x57) {
+ _dataOffsets = ptr + 18;
+ _baseptr += 4;
+ } else {
+ _dataOffsets = ptr + 34;
+ }
+ _animCmds = _baseptr + READ_LE_UINT16(ptr);
+}
+
+byte NESCostumeRenderer::drawLimb(const Actor *a, int limb) {
+ const byte darkpalette[16] = {0x00,0x00,0x2D,0x3D,0x00,0x00,0x2D,0x3D,0x00,0x00,0x2D,0x3D,0x00,0x00,0x2D,0x3D};
+ const CostumeData &cost = a->_cost;
+ const byte *palette, *src, *sprdata;
+ int anim, frameNum, frame, offset, numSprites;
+
+ // If the specified limb is stopped or not existing, do nothing.
+ if (cost.curpos[limb] == 0xFFFF)
+ return 0;
+
+ if (_vm->VAR(_vm->VAR_CURRENT_LIGHTS) & LIGHTMODE_actor_base)
+ palette = _vm->_NESPalette[1];
+ else
+ palette = darkpalette;
+
+ src = _loaded._dataOffsets;
+ anim = 4 * cost.frame[limb] + newDirToOldDir(a->getFacing());
+ frameNum = cost.curpos[limb];
+ frame = src[src[2 * anim] + frameNum];
+
+ offset = READ_LE_UINT16(_vm->_NEScostdesc + v1MMNESLookup[_loaded._id] * 2);
+ numSprites = _vm->_NEScostlens[offset + frame] + 1;
+ sprdata = _vm->_NEScostdata + READ_LE_UINT16(_vm->_NEScostoffs + 2 * (offset + frame)) + numSprites * 3;
+
+ bool flipped = (newDirToOldDir(a->getFacing()) == 1);
+ int left = 239, right = 0, top = 239, bottom = 0;
+ byte *maskBuf = _vm->getMaskBuffer(0, 0, 1);
+
+ for (int spr = 0; spr < numSprites; spr++) {
+ byte mask, tile, sprpal;
+ int8 y, x;
+
+ sprdata -= 3;
+
+ mask = (sprdata[0] & 0x80) ? 0x01 : 0x80;
+ y = sprdata[0] << 1;
+ y >>= 1;
+
+ tile = sprdata[1];
+
+ sprpal = (sprdata[2] & 0x03) << 2;
+ x = sprdata[2];
+ x >>= 2;
+
+ if (flipped) {
+ mask = (mask == 0x80) ? 0x01 : 0x80;
+ x = -x;
+ }
+
+ if ((_actorX + x < 0) || (_actorX + x + 8 >= _out.w))
+ continue;
+ if ((_actorY + y < 0) || (_actorY + y + 8 >= _out.h))
+ continue;
+
+ for (int ty = 0; ty < 8; ty++) {
+ byte c1 = _vm->_NESPatTable[0][tile * 16 + ty];
+ byte c2 = _vm->_NESPatTable[0][tile * 16 + ty + 8];
+
+ for (int tx = 0; tx < 8; tx++) {
+ unsigned char c = ((c1 & mask) ? 1 : 0) | ((c2 & mask) ? 2 : 0) | sprpal;
+ if (mask == 0x01) {
+ c1 >>= 1;
+ c2 >>= 1;
+ } else {
+ c1 <<= 1;
+ c2 <<= 1;
+ }
+ if (!(c & 3))
+ continue;
+ int my = _actorY + y + ty;
+ int mx = _actorX + x + tx;
+ if (!(_zbuf && (maskBuf[my * _numStrips + mx / 8] & revBitMask(mx & 7))))
+ *((byte *)_out.pixels + my * _out.pitch + mx) = palette[c];
+ }
+ }
+ left = MIN(left, _actorX + x);
+ right = MAX(right, _actorX + x + 8);
+ top = MIN(top, _actorY + y);
+ bottom = MAX(bottom, _actorY + y + 8);
+ }
+
+ _draw_top = top;
+ _draw_bottom = bottom;
+
+ _vm->markRectAsDirty(kMainVirtScreen, left, right, top, bottom, _actorID);
+
+ return 0;
+}
+
+byte ClassicCostumeRenderer::drawLimb(const Actor *a, int limb) {
+ int i;
+ int code;
+ const byte *frameptr;
+ const CostumeData &cost = a->_cost;
+
+ // If the specified limb is stopped or not existing, do nothing.
+ if (cost.curpos[limb] == 0xFFFF || cost.stopped & (1 << limb))
+ return 0;
+
+ // Determine the position the limb is at
+ i = cost.curpos[limb] & 0x7FFF;
+
+ // Get the frame pointer for that limb
+ frameptr = _loaded._baseptr + READ_LE_UINT16(_loaded._frameOffsets + limb * 2);
+
+ // Determine the offset to the costume data for the limb at position i
+ code = _loaded._animCmds[i] & 0x7F;
+
+ // Code 0x7B indicates a limb for which there is nothing to draw
+ if (code != 0x7B) {
+ _srcptr = _loaded._baseptr + READ_LE_UINT16(frameptr + code * 2);
+
+ if (!(_vm->_features & GF_OLD256) || code < 0x79) {
+ const CostumeInfo *costumeInfo;
+ int xmoveCur, ymoveCur;
+
+ if (_loaded._format == 0x57) {
+ _width = _srcptr[0] * 8;
+ _height = _srcptr[1];
+ xmoveCur = _xmove + (int8)_srcptr[2] * 8;
+ ymoveCur = _ymove - (int8)_srcptr[3];
+ _xmove += (int8)_srcptr[4] * 8;
+ _ymove -= (int8)_srcptr[5];
+ _srcptr += 6;
+ } else {
+ costumeInfo = (const CostumeInfo *)_srcptr;
+ _width = READ_LE_UINT16(&costumeInfo->width);
+ _height = READ_LE_UINT16(&costumeInfo->height);
+ xmoveCur = _xmove + (int16)READ_LE_UINT16(&costumeInfo->rel_x);
+ ymoveCur = _ymove + (int16)READ_LE_UINT16(&costumeInfo->rel_y);
+ _xmove += (int16)READ_LE_UINT16(&costumeInfo->move_x);
+ _ymove -= (int16)READ_LE_UINT16(&costumeInfo->move_y);
+ _srcptr += 12;
+ }
+
+ return mainRoutine(xmoveCur, ymoveCur);
+ }
+ }
+
+ return 0;
+
+}
+
+void NESCostumeRenderer::setPalette(byte *palette) {
+ // TODO
+}
+
+void NESCostumeRenderer::setFacing(const Actor *a) {
+ // TODO
+ //_mirror = newDirToOldDir(a->getFacing()) != 0 || _loaded._mirror;
+}
+
+void NESCostumeRenderer::setCostume(int costume, int shadow) {
+ _loaded.loadCostume(costume);
+}
+
+void ClassicCostumeLoader::costumeDecodeData(Actor *a, int frame, uint usemask) {
+ const byte *r;
+ uint mask, j;
+ int i;
+ byte extra, cmd;
+ int anim;
+
+ loadCostume(a->_costume);
+
+ anim = newDirToOldDir(a->getFacing()) + frame * 4;
+
+ if (anim > _numAnim) {
+ return;
+ }
+
+ r = _baseptr + READ_LE_UINT16(_dataOffsets + anim * 2);
+
+ if (r == _baseptr) {
+ return;
+ }
+
+ if (_vm->_version == 1) {
+ mask = *r++ << 8;
+ } else {
+ mask = READ_LE_UINT16(r);
+ r += 2;
+ }
+ i = 0;
+ do {
+ if (mask & 0x8000) {
+ if (_vm->_version <= 3) {
+ j = *r++;
+
+ if (j == 0xFF)
+ j = 0xFFFF;
+ } else {
+ j = READ_LE_UINT16(r);
+ r += 2;
+ }
+ if (usemask & 0x8000) {
+ if (j == 0xFFFF) {
+ a->_cost.curpos[i] = 0xFFFF;
+ a->_cost.start[i] = 0;
+ a->_cost.frame[i] = frame;
+ } else {
+ extra = *r++;
+ cmd = _animCmds[j];
+ if (cmd == 0x7A) {
+ a->_cost.stopped &= ~(1 << i);
+ } else if (cmd == 0x79) {
+ a->_cost.stopped |= (1 << i);
+ } else {
+ a->_cost.curpos[i] = a->_cost.start[i] = j;
+ a->_cost.end[i] = j + (extra & 0x7F);
+ if (extra & 0x80)
+ a->_cost.curpos[i] |= 0x8000;
+ a->_cost.frame[i] = frame;
+ }
+ }
+ } else {
+ if (j != 0xFFFF)
+ r++;
+ }
+ }
+ i++;
+ usemask <<= 1;
+ mask <<= 1;
+ } while (mask&0xFFFF);
+}
+
+void ClassicCostumeRenderer::setPalette(byte *palette) {
+ int i;
+ byte color;
+
+ if (_loaded._format == 0x57) {
+ memcpy(_palette, palette, 13);
+ } else if (_vm->_features & GF_OLD_BUNDLE) {
+ if ((_vm->VAR(_vm->VAR_CURRENT_LIGHTS) & LIGHTMODE_actor_color)) {
+ memcpy(_palette, palette, 16);
+ } else {
+ memset(_palette, 8, 16);
+ _palette[12] = 0;
+ }
+ _palette[_loaded._palette[0]] = _palette[0];
+ } else {
+ if ((_vm->VAR_CURRENT_LIGHTS == 0xFF) || (_vm->VAR(_vm->VAR_CURRENT_LIGHTS) & LIGHTMODE_actor_color)) {
+ for (i = 0; i < _loaded._numColors; i++) {
+ color = palette[i];
+ if (color == 255)
+ color = _loaded._palette[i];
+ _palette[i] = color;
+ }
+ } else {
+ memset(_palette, 8, _loaded._numColors);
+ _palette[12] = 0;
+ }
+ }
+}
+
+void ClassicCostumeRenderer::setFacing(const Actor *a) {
+ _mirror = newDirToOldDir(a->getFacing()) != 0 || _loaded._mirror;
+}
+
+void ClassicCostumeRenderer::setCostume(int costume, int shadow) {
+ _loaded.loadCostume(costume);
+}
+
+byte ClassicCostumeLoader::increaseAnims(Actor *a) {
+ int i;
+ byte r = 0;
+
+ for (i = 0; i != 16; i++) {
+ if (a->_cost.curpos[i] != 0xFFFF)
+ r += increaseAnim(a, i);
+ }
+ return r;
+}
+
+byte ClassicCostumeLoader::increaseAnim(Actor *a, int slot) {
+ int highflag;
+ int i, end;
+ byte code, nc;
+
+ if (a->_cost.curpos[slot] == 0xFFFF)
+ return 0;
+
+ highflag = a->_cost.curpos[slot] & 0x8000;
+ i = a->_cost.curpos[slot] & 0x7FFF;
+ end = a->_cost.end[slot];
+ code = _animCmds[i] & 0x7F;
+
+ if (_vm->_version <= 3) {
+ if (_animCmds[i] & 0x80)
+ a->_cost.soundCounter++;
+ }
+
+ do {
+ if (!highflag) {
+ if (i++ >= end)
+ i = a->_cost.start[slot];
+ } else {
+ if (i != end)
+ i++;
+ }
+ nc = _animCmds[i];
+
+ if (nc == 0x7C) {
+ a->_cost.animCounter++;
+ if (a->_cost.start[slot] != end)
+ continue;
+ } else {
+ if (_vm->_version >= 6) {
+ if (nc >= 0x71 && nc <= 0x78) {
+ uint sound = (_vm->_heversion == 60) ? 0x78 - nc : nc - 0x71;
+ _vm->_sound->addSoundToQueue2(a->_sound[sound]);
+ if (a->_cost.start[slot] != end)
+ continue;
+ }
+ } else {
+ if (nc == 0x78) {
+ a->_cost.soundCounter++;
+ if (a->_cost.start[slot] != end)
+ continue;
+ }
+ }
+ }
+
+ a->_cost.curpos[slot] = i | highflag;
+ return (_animCmds[i] & 0x7F) != code;
+ } while (1);
+}
+
+/**
+ * costume ID -> v1MMNESLookup[] -> desc -> lens & offs -> data -> Gfx & pal
+ */
+void NESCostumeLoader::loadCostume(int id) {
+ _id = id;
+ _baseptr = _vm->getResourceAddress(rtCostume, id);
+ _dataOffsets = _baseptr + 2;
+ _numAnim = 0x17;
+}
+
+void NESCostumeLoader::costumeDecodeData(Actor *a, int frame, uint usemask) {
+ int anim;
+
+ loadCostume(a->_costume);
+
+ anim = 4 * frame + newDirToOldDir(a->getFacing());
+
+ if (anim > _numAnim) {
+ return;
+ }
+
+ a->_cost.curpos[0] = 0;
+ a->_cost.start[0] = 0;
+ a->_cost.end[0] = _dataOffsets[2 * anim + 1];
+ a->_cost.frame[0] = frame;
+}
+
+byte NESCostumeLoader::increaseAnims(Actor *a) {
+ int i;
+ byte r = 0;
+
+ for (i = 0; i != 16; i++) {
+ if (a->_cost.curpos[i] != 0xFFFF)
+ r += increaseAnim(a, i);
+ }
+ return r;
+}
+
+byte NESCostumeLoader::increaseAnim(Actor *a, int slot) {
+ int oldframe = a->_cost.curpos[slot]++;
+ if (a->_cost.curpos[slot] >= a->_cost.end[slot])
+ a->_cost.curpos[slot] = a->_cost.start[slot];
+ return (a->_cost.curpos[slot] != oldframe);
+}
+
+static const byte actorColorsMMC64[] = {
+ 0, 7, 2, 6, 9, 1, 3, 7, 7, 1, 1, 9, 1, 4, 5, 5,
+ 4, 1, 0, 5, 4, 2, 2, 7, 7, 0, 6, 6, 6, 6, 6, 6
+};
+
+byte C64CostumeRenderer::drawLimb(const Actor *a, int limb) {
+ if (limb >= 8)
+ return 0;
+
+ if (limb == 0) {
+ _draw_top = 200;
+ _draw_bottom = 0;
+ }
+
+ // TODO:
+ // get out how animations are handled
+ byte state = a->_moving != 0 ? 0 : 1;
+ byte unk1 = (_loaded._animCmds + (state*32) + newDirToOldDir(a->getFacing()) * 8)[limb];
+ byte unk2 = _loaded._frameOffsets[_loaded._frameOffsets[limb] + (unk1 & 0x7f)];
+ bool flipped = (unk1 & 0x80) != 0;
+
+ byte p1 = _loaded._frameOffsets[unk2];
+ byte temp1 = _loaded._baseptr[p1];
+ byte temp2 = temp1 + _loaded._dataOffsets[4];
+ int offL = _loaded._baseptr[temp1 + 2];
+ int offH = _loaded._baseptr[temp2];
+ int off = (offH << 8) + offL;
+
+ const byte *data = _loaded._baseptr + off;
+ const byte actorColors[] = {
+ 0, 10, actorColorsMMC64[_actorID], 0
+ };
+
+ int width = *data++;
+ int height = *data++;
+ int offsetX = *data++;
+ int offsetY = *data++;
+ // these two fields seems to be most times zero
+ // byte6 was one time 255 in one costume I tried
+// int byte5 = *data++;
+// int byte6 = *data++;
+// debug(3, "byte5: %d", byte5);
+// debug(3, "byte6: %d", byte6);
+ data += 2;
+
+ if (!width || !height)
+ return 0;
+
+ int xpos = 0;
+ int ypos = _loaded._maxHeight - offsetY;
+
+ if (flipped) {
+ if (offsetX)
+ xpos += (offsetX-1) * 8;
+ } else {
+ xpos += offsetX * 8;
+ }
+
+ // + 4 could be commented, because maybe the _actorX position is
+ // wrong, I looked at the scumm-c64 interpreter by lloyd
+ // and there Bernhard is directly on the right in the intro
+ // but here in ScummVM he is 4 pixel left of the other position.
+ xpos += _actorX - (a->_width / 2) + 4;
+ ypos += _actorY - _loaded._maxHeight;
+
+ if (flipped) {
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ byte c = data[y*width+x];
+ byte b, d;
+ int realX = 0;
+ if (offsetX == 0||offsetX == 1) {
+ realX = width-(x+1);
+ } else if (offsetX == 2) {
+ realX = width-(x+2);
+ }
+ byte *dest = &(((byte*)_out.pixels)[((y + ypos) * _out.pitch) + ((realX * 8) + xpos)]);
+
+ for (int i = 0; i <= 6; i += 2) {
+ if ((d = (c >> i) & 0x03)) {
+ b = actorColors[d];
+ *dest++ = b;
+ *dest++ = b;
+ continue;
+ }
+ dest += 2;
+ }
+ }
+ }
+ } else {
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ byte c = data[y*width+x];
+ byte b, d;
+ byte *dest = &(((byte*)_out.pixels)[((y + ypos) * _out.pitch) + ((x * 8) + xpos)]);
+
+ for (int i = 6; i >= 0; i -= 2) {
+ if ((d = (c >> i) & 0x03)) {
+ b = actorColors[d];
+ *dest++ = b;
+ *dest++ = b;
+ continue;
+ }
+ dest += 2;
+ }
+ }
+ }
+ }
+
+ _draw_top = MIN(_draw_top, ypos);
+ _draw_bottom = MAX(_draw_bottom, ypos+height);
+ // if +4 above is NOT commented, here "+(flipped ? 4 : 0)" can be commented out
+ // and other way round
+ _vm->markRectAsDirty(kMainVirtScreen, xpos, xpos+(width*8)/*+(flipped ? 4 : 0)*/, ypos, ypos+height, _actorID);
+
+ return 0;
+}
+
+void C64CostumeRenderer::setCostume(int costume, int shadow) {
+ _loaded.loadCostume(costume);
+}
+
+void C64CostumeLoader::loadCostume(int id) {
+ const byte *ptr = _vm->getResourceAddress(rtCostume, id);
+ _id = id;
+ _baseptr = ptr + 9;
+
+ _format = 0x57;
+ _numColors = 0;
+ _numAnim = 0;
+ _mirror = 0;
+ _palette = &actorColorsMMC64[id];
+
+ _frameOffsets = _baseptr + READ_LE_UINT16(ptr + 5);
+ _dataOffsets = ptr;
+ _animCmds = _baseptr + READ_LE_UINT16(ptr + 7);
+
+ _maxHeight = 0;
+ for (int i = 0; i < 8; ++i) {
+ int pid = _frameOffsets[_frameOffsets[i]];
+ byte p1 = _frameOffsets[pid];
+ byte b = _baseptr[p1];
+ byte c = b + _dataOffsets[4];
+ int offL = _baseptr[b + 2];
+ int offH = _baseptr[c];
+ int off = (offH << 8) + offL;
+ const byte *data = _baseptr + off;
+
+ if (data[3] > _maxHeight) {
+ _maxHeight = data[3]; // data[3] is libs's Y offset
+ }
+ }
+ ++_maxHeight;
+}
+
+void C64CostumeLoader::costumeDecodeData(Actor *a, int frame, uint usemask) {
+}
+
+byte C64CostumeLoader::increaseAnims(Actor *a) {
+ return 0;
+}
+
+byte C64CostumeLoader::increaseAnim(Actor *a, int slot) {
+ return 0;
+}
+
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Costume)
+_GSETPTR(Scumm::smallCostumeScaleTable, GBVARS_SMALLSCALETABLE_INDEX, byte, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(Costume)
+_GRELEASEPTR(GBVARS_SMALLSCALETABLE_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/costume.h b/engines/scumm/costume.h
new file mode 100644
index 0000000000..56c97e0a8c
--- /dev/null
+++ b/engines/scumm/costume.h
@@ -0,0 +1,141 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef COSTUME_H
+#define COSTUME_H
+
+#include "scumm/base-costume.h"
+
+namespace Scumm {
+
+class ClassicCostumeLoader : public BaseCostumeLoader {
+public:
+ int _id;
+ const byte *_baseptr;
+ const byte *_animCmds;
+ const byte *_dataOffsets;
+ const byte *_palette;
+ const byte *_frameOffsets;
+ byte _numColors;
+ byte _numAnim;
+ byte _format;
+ bool _mirror;
+
+ ClassicCostumeLoader(ScummEngine *vm) :
+ BaseCostumeLoader(vm),
+ _id(-1), _baseptr(0), _animCmds(0), _dataOffsets(0), _palette(0),
+ _frameOffsets(0), _numColors(0), _numAnim(0), _format(0), _mirror(false) {}
+
+ void loadCostume(int id);
+ void costumeDecodeData(Actor *a, int frame, uint usemask);
+ byte increaseAnims(Actor *a);
+
+protected:
+ byte increaseAnim(Actor *a, int slot);
+};
+
+class NESCostumeLoader : public BaseCostumeLoader {
+public:
+ int _id;
+ const byte *_baseptr;
+ const byte *_dataOffsets;
+ byte _numAnim;
+
+ NESCostumeLoader(ScummEngine *vm) : BaseCostumeLoader(vm) {}
+ void loadCostume(int id);
+ void costumeDecodeData(Actor *a, int frame, uint usemask);
+ byte increaseAnims(Actor *a);
+
+protected:
+ byte increaseAnim(Actor *a, int slot);
+};
+
+class C64CostumeLoader : public ClassicCostumeLoader {
+public:
+ C64CostumeLoader(ScummEngine *vm) : ClassicCostumeLoader(vm) {}
+ void loadCostume(int id);
+ void costumeDecodeData(Actor *a, int frame, uint usemask);
+ byte increaseAnims(Actor *a);
+
+ int _maxHeight;
+protected:
+ byte increaseAnim(Actor *a, int slot);
+};
+
+class ClassicCostumeRenderer : public BaseCostumeRenderer {
+protected:
+ ClassicCostumeLoader _loaded;
+
+ byte _scaleIndexX; /* must wrap at 256 */
+ byte _scaleIndexY;
+ byte _palette[32];
+
+public:
+ ClassicCostumeRenderer(ScummEngine *vm) : BaseCostumeRenderer(vm), _loaded(vm) {}
+
+ void setPalette(byte *palette);
+ void setFacing(const Actor *a);
+ void setCostume(int costume, int shadow);
+
+protected:
+ byte drawLimb(const Actor *a, int limb);
+
+ void proc3(Codec1 &v1);
+ void proc3_ami(Codec1 &v1);
+
+ void procC64(Codec1 &v1, int actor);
+
+ byte mainRoutine(int xmoveCur, int ymoveCur);
+};
+
+class NESCostumeRenderer : public BaseCostumeRenderer {
+protected:
+ NESCostumeLoader _loaded;
+
+public:
+ NESCostumeRenderer(ScummEngine *vm) : BaseCostumeRenderer(vm), _loaded(vm) {}
+
+ void setPalette(byte *palette);
+ void setFacing(const Actor *a);
+ void setCostume(int costume, int shadow);
+
+protected:
+ byte drawLimb(const Actor *a, int limb);
+};
+
+class C64CostumeRenderer : public BaseCostumeRenderer {
+protected:
+ C64CostumeLoader _loaded;
+
+public:
+ C64CostumeRenderer(ScummEngine *vm) : BaseCostumeRenderer(vm), _loaded(vm) {}
+
+ void setPalette(byte *palette) {}
+ void setFacing(const Actor *a) {}
+ void setCostume(int costume, int shadow);
+
+protected:
+ byte drawLimb(const Actor *a, int limb);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp
new file mode 100644
index 0000000000..6fee33e79b
--- /dev/null
+++ b/engines/scumm/cursor.cpp
@@ -0,0 +1,520 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/util.h"
+#include "scumm/bomp.h"
+#include "scumm/charset.h"
+#include "scumm/intern.h"
+#include "scumm/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource_v7he.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+/*
+ * Mouse cursor cycle colors (for the default crosshair).
+ */
+static const byte default_v1_cursor_colors[4] = {
+ 1, 1, 12, 11
+};
+
+static const byte default_cursor_colors[4] = {
+ 15, 15, 7, 8
+};
+
+
+static const uint16 default_cursor_images[4][16] = {
+ /* cross-hair */
+ { 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0000, 0x7e3f,
+ 0x0000, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0000 },
+ /* hourglass */
+ { 0x0000, 0x7ffe, 0x6006, 0x300c, 0x1818, 0x0c30, 0x0660, 0x03c0,
+ 0x0660, 0x0c30, 0x1998, 0x33cc, 0x67e6, 0x7ffe, 0x0000, 0x0000 },
+ /* arrow */
+ { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x7f00,
+ 0x7f80, 0x78c0, 0x7c00, 0x4600, 0x0600, 0x0300, 0x0300, 0x0180 },
+ /* hand */
+ { 0x1e00, 0x1200, 0x1200, 0x1200, 0x1200, 0x13ff, 0x1249, 0x1249,
+ 0xf249, 0x9001, 0x9001, 0x9001, 0x8001, 0x8001, 0x8001, 0xffff },
+};
+
+static const byte default_cursor_hotspots[10] = {
+ 8, 7, 8, 7, 1, 1, 5, 0,
+ 8, 7, //zak256
+};
+
+static const uint16 default_he_cursor[64] = {
+ 0x0000, 0x0000, 0x3800, 0x0000, 0x7e00, 0x0000, 0x5f80, 0x0000,
+ 0x5fe0, 0x0000, 0x2ff8, 0x0000, 0x27fe, 0x0000, 0x17ff, 0x8000,
+ 0x13ff, 0xe000, 0x09ff, 0xf000, 0x09ff, 0xf800, 0x04ff, 0xf800,
+ 0x047f, 0xf000, 0x027f, 0xe000, 0x023f, 0xf000, 0x011f, 0xf800,
+ 0x0111, 0xfc00, 0x0080, 0xfc00, 0x0084, 0x0c00, 0x004a, 0x0800,
+ 0x0031, 0x1000, 0x0000, 0xe000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static const byte default_v6_cursor[] = {
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x0F,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
+ 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF,
+ 0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x0F,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+};
+
+ScummEngine_v5::ScummEngine_v5(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine(detector, syst, gs, md5sum, substResFileNameIndex) {
+
+ for (int i = 0; i < 4; i++) {
+ memcpy(_cursorImages[i], default_cursor_images[i], 32);
+ }
+ memcpy(_cursorHotspots, default_cursor_hotspots, 8);
+}
+
+
+void ScummEngine::setupCursor() {
+ _cursor.animate = 1;
+}
+
+void ScummEngine_v5::animateCursor() {
+ if (_cursor.animate) {
+ if (!(_cursor.animateIndex & 0x1)) {
+ setBuiltinCursor((_cursor.animateIndex >> 1) & 3);
+ }
+ _cursor.animateIndex++;
+ }
+}
+
+void ScummEngine_v6::setCursorHotspot(int x, int y) {
+ _cursor.hotspotX = x;
+ _cursor.hotspotY = y;
+}
+
+void ScummEngine_v6::setCursorTransparency(int a) {
+ int i, size;
+
+ size = _cursor.width * _cursor.height;
+
+ for (i = 0; i < size; i++)
+ if (_grabbedCursor[i] == (byte)a)
+ _grabbedCursor[i] = 0xFF;
+
+ updateCursor();
+}
+
+void ScummEngine::updateCursor() {
+ const int transColor = (_heversion >= 80) ? 5 : 255;
+ _system->setMouseCursor(_grabbedCursor, _cursor.width, _cursor.height,
+ _cursor.hotspotX, _cursor.hotspotY,
+ (_platform == Common::kPlatformNES ? _grabbedCursor[63] : transColor),
+ (_heversion == 70 ? 2 : 1));
+}
+
+void ScummEngine_v6::grabCursor(int x, int y, int w, int h) {
+ VirtScreen *vs = findVirtScreen(y);
+
+ if (vs == NULL) {
+ debug(0, "grabCursor: invalid Y %d", y);
+ return;
+ }
+
+ setCursorFromBuffer((byte *)vs->pixels + (y - vs->topline) * vs->pitch + x, w, h, vs->pitch);
+}
+
+void ScummEngine_v6::setDefaultCursor() {
+ setCursorHotspot(7, 6);
+ setCursorFromBuffer(default_v6_cursor, 16, 13, 16);
+}
+
+void ScummEngine::setCursorFromBuffer(const byte *ptr, int width, int height, int pitch) {
+ uint size;
+ byte *dst;
+
+ size = width * height;
+ if (size > sizeof(_grabbedCursor))
+ error("grabCursor: grabbed cursor too big");
+
+ _cursor.width = width;
+ _cursor.height = height;
+ _cursor.animate = 0;
+
+ dst = _grabbedCursor;
+ for (; height; height--) {
+ memcpy(dst, ptr, width);
+ dst += width;
+ ptr += pitch;
+ }
+
+ updateCursor();
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v70he::setCursorFromImg(uint img, uint room, uint imgindex) {
+ _resExtractor->setCursor(img);
+}
+
+void ScummEngine_v90he::setDefaultCursor() {
+ const uint16 *src;
+ int i, j;
+ static byte palette[] = { 0xff, 0xff, 0xff, 0,
+ 0, 0, 0, 0};
+
+ memset(_grabbedCursor, 5, sizeof(_grabbedCursor));
+
+ _cursor.hotspotX = _cursor.hotspotY = 2;
+ src = default_he_cursor;
+
+ _cursor.width = 32;
+ _cursor.height = 32;
+
+ for (i = 0; i < 32; i++) {
+ for (j = 0; j < 32; j++) {
+ if (*src & (1 << (15 - (j % 16))))
+ _grabbedCursor[32 * i + j] = 0xfe;
+ if (j == 15)
+ src++;
+ }
+ src++;
+ }
+
+ // Since white color position is not guaranteed
+ // we setup our own palette if supported by backend
+ if (_system->hasFeature(OSystem::kFeatureCursorHasPalette))
+ _system->setCursorPalette(palette, 0xfe, 2);
+
+ updateCursor();
+}
+#endif
+
+void ScummEngine_v6::setCursorFromImg(uint img, uint room, uint imgindex) {
+ int w, h;
+ const byte *dataptr, *bomp;
+ uint32 size;
+ FindObjectInRoom foir;
+ const ImageHeader *imhd;
+
+ if (room == (uint) - 1)
+ room = getObjectRoom(img);
+
+ findObjectInRoom(&foir, foCodeHeader | foImageHeader | foCheckAlreadyLoaded, img, room);
+ imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), foir.obim);
+
+ if (_version == 8) {
+ setCursorHotspot(READ_LE_UINT32(&imhd->v8.hotspot[0].x),
+ READ_LE_UINT32(&imhd->v8.hotspot[0].y));
+ w = READ_LE_UINT32(&imhd->v8.width) / 8;
+ h = READ_LE_UINT32(&imhd->v8.height) / 8;
+ } else if (_version == 7) {
+ setCursorHotspot(READ_LE_UINT16(&imhd->v7.hotspot[0].x),
+ READ_LE_UINT16(&imhd->v7.hotspot[0].y));
+ w = READ_LE_UINT16(&imhd->v7.width) / 8;
+ h = READ_LE_UINT16(&imhd->v7.height) / 8;
+ } else {
+ if (_heversion == 0) {
+ setCursorHotspot(READ_LE_UINT16(&imhd->old.hotspot[0].x),
+ READ_LE_UINT16(&imhd->old.hotspot[0].y));
+ }
+ w = READ_LE_UINT16(&foir.cdhd->v6.w) / 8;
+ h = READ_LE_UINT16(&foir.cdhd->v6.h) / 8;
+ }
+
+ dataptr = getObjectImage(foir.obim, imgindex);
+ assert(dataptr);
+ if (_version == 8) {
+ bomp = dataptr;
+ } else {
+ size = READ_BE_UINT32(dataptr + 4);
+ if (size > sizeof(_grabbedCursor))
+ error("setCursorFromImg: Cursor image too large");
+
+ bomp = findResource(MKID('BOMP'), dataptr);
+ }
+
+ if (bomp != NULL)
+ useBompCursor(bomp, w, h);
+ else
+ useIm01Cursor(dataptr, w, h);
+}
+
+void ScummEngine_v6::useIm01Cursor(const byte *im, int w, int h) {
+ VirtScreen *vs = &virtscr[0];
+ byte *buf, *dst;
+ const byte *src;
+ int i;
+
+ w *= 8;
+ h *= 8;
+
+ // Backup the screen content
+ dst = buf = (byte *)malloc(w * h);
+ src = vs->getPixels(0, 0);
+
+ for (i = 0; i < h; i++) {
+ memcpy(dst, src, w);
+ dst += w;
+ src += vs->pitch;
+ }
+
+ // Do some drawing
+ drawBox(0, 0, w - 1, h - 1, 0xFF);
+
+ vs->hasTwoBuffers = false;
+ gdi.disableZBuffer();
+ gdi.drawBitmap(im, vs, _screenStartStrip, 0, w, h, 0, w / 8, 0);
+ vs->hasTwoBuffers = true;
+ gdi.enableZBuffer();
+
+ // Grab the data we just drew and setup the cursor with it
+ setCursorFromBuffer(vs->getPixels(0, 0), w, h, vs->pitch);
+
+ // Restore the screen content
+ src = buf;
+ dst = vs->getPixels(0, 0);
+
+ for (i = 0; i < h; i++) {
+ memcpy(dst, src, w);
+ dst += vs->pitch;
+ src += w;
+ }
+
+ free(buf);
+}
+
+void ScummEngine_v6::useBompCursor(const byte *im, int width, int height) {
+ uint size;
+
+ width *= 8;
+ height *= 8;
+
+ size = width * height;
+ if (size > sizeof(_grabbedCursor))
+ error("useBompCursor: cursor too big (%d)", size);
+
+ _cursor.width = width;
+ _cursor.height = height;
+ _cursor.animate = 0;
+
+ // Skip the header
+ if (_version == 8) {
+ im += 16;
+ } else {
+ im += 18;
+ }
+ decompressBomp(_grabbedCursor, im, width, height);
+
+ updateCursor();
+}
+
+void ScummEngine_v5::redefineBuiltinCursorFromChar(int index, int chr) {
+ // Cursor image in both Looms are based on images from charset.
+ if (_gameId != GID_LOOM) {
+ // FIXME: Actually: is this opcode ever called by a non-Loom game?
+ // Which V3-V5 game besides Loom makes use of custom cursors, ever?
+ error("V3--V5 SO_CURSOR_IMAGE(%d,%d) called - tell Fingolfin where you saw this!", index, chr);
+ }
+
+ assert(index >= 0 && index < 4);
+
+// const int oldID = _charset->getCurID();
+
+ if (_version == 3) {
+ _charset->setCurID(0);
+ } else if (_version >= 4) {
+ _charset->setCurID(1);
+ }
+
+ Graphics::Surface s;
+ byte buf[16*17];
+ memset(buf, 123, 16*17);
+ s.pixels = buf;
+ s.w = _charset->getCharWidth(chr);
+ s.h = _charset->getFontHeight();
+ s.pitch = s.w;
+ // s.h = 17 for FM-TOWNS Loom Japanese. Fixes bug #1166917
+ assert(s.w <= 16 && s.h <= 17);
+ s.bytesPerPixel = 1;
+
+ _charset->drawChar(chr, s, 0, 0);
+
+ uint16 *ptr = _cursorImages[index];
+ memset(ptr, 0, 17 * sizeof(uint16));
+ for (int h = 0; h < s.h; h++) {
+ for (int w = 0; w < s.w; w++) {
+ if (buf[s.pitch * h + w] != 123)
+ *ptr |= 1 << (15 - w);
+ }
+ ptr++;
+ }
+
+// _charset->setCurID(oldID);
+}
+
+void ScummEngine_v5::redefineBuiltinCursorHotspot(int index, int x, int y) {
+ // Cursor image in both Looms are based on images from charset.
+ if (_gameId != GID_LOOM) {
+ // FIXME: Actually: is this opcode ever called by a non-Loom game?
+ // Which V3-V5 game besides Loom makes use of custom cursors, ever?
+ error("V3--V5 SO_CURSOR_HOTSPOT(%d,%d,%d) called - tell Fingolfin where you saw this!", index, x, y);
+ }
+
+ assert(index >= 0 && index < 4);
+
+ _cursorHotspots[index * 2] = x;
+ _cursorHotspots[index * 2 + 1] = y;
+}
+
+void ScummEngine_v5::setBuiltinCursor(int idx) {
+ int i, j;
+ byte color;
+
+ memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor));
+
+ if (_version == 1)
+ color = default_v1_cursor_colors[idx];
+ else
+ color = default_cursor_colors[idx];
+
+ if (_platform == Common::kPlatformNES) {
+ _cursor.width = 8;
+ _cursor.height = 8;
+ _cursor.hotspotX = 0;
+ _cursor.hotspotY = 0;
+
+ byte *dst = _grabbedCursor;
+ byte *src = &_NESPatTable[0][0xfa * 16];
+ byte *palette = _NESPalette[1];
+
+ for (i = 0; i < 8; i++) {
+ byte c0 = src[i];
+ byte c1 = src[i + 8];
+ for (j = 0; j < 8; j++)
+ *dst++ = palette[((c0 >> (7 - j)) & 1) | (((c1 >> (7 - j)) & 1) << 1) | ((idx == 3) ? 4 : 0)];
+ }
+
+ } else if (_version <= 2 && _platform == Common::kPlatformAmiga) {
+ _cursor.width = 15;
+ _cursor.height = 15;
+ _cursor.hotspotX = 7;
+ _cursor.hotspotY = 7;
+
+ byte *hotspot = _grabbedCursor + _cursor.hotspotY * _cursor.width + _cursor.hotspotX;
+
+ // Crosshair, symmetric
+ // TODO: Instead of setting this up via code, we should simply extend
+ // default_cursor_images to contain this shape.
+ for (i = 0; i < 5; i++) {
+ *(hotspot - 3 - i) = color;
+ *(hotspot + 3 + i) = color;
+ *(hotspot - _cursor.width * (3 + i)) = color;
+ *(hotspot + _cursor.width * (3 + i)) = color;
+ }
+
+ // Arrow heads, diagonal lines
+ for (i = 1; i <= 2; i++) {
+ *(hotspot - _cursor.width * i - (3 + i)) = color;
+ *(hotspot + _cursor.width * i - (3 + i)) = color;
+ *(hotspot - _cursor.width * i + (3 + i)) = color;
+ *(hotspot + _cursor.width * i + (3 + i)) = color;
+ *(hotspot - _cursor.width * (3 + i) - i) = color;
+ *(hotspot + _cursor.width * (3 + i) - i) = color;
+ *(hotspot - _cursor.width * (3 + i) + i) = color;
+ *(hotspot + _cursor.width * (3 + i) + i) = color;
+ }
+ } else if (_version <= 2) {
+ _cursor.width = 23;
+ _cursor.height = 21;
+ _cursor.hotspotX = 11;
+ _cursor.hotspotY = 10;
+
+ byte *hotspot = _grabbedCursor + _cursor.hotspotY * _cursor.width + _cursor.hotspotX;
+
+ // Crosshair, slightly assymetric
+ // TODO: Instead of setting this up via code, we should simply extend
+ // default_cursor_images to contain this shape.
+
+ for (i = 0; i < 7; i++) {
+ *(hotspot - 5 - i) = color;
+ *(hotspot + 5 + i) = color;
+ }
+
+ for (i = 0; i < 8; i++) {
+ *(hotspot - _cursor.width * (3 + i)) = color;
+ *(hotspot + _cursor.width * (3 + i)) = color;
+ }
+
+ // Arrow heads, diagonal lines
+
+ for (i = 1; i <= 3; i++) {
+ *(hotspot - _cursor.width * i - 5 - i) = color;
+ *(hotspot + _cursor.width * i - 5 - i) = color;
+ *(hotspot - _cursor.width * i + 5 + i) = color;
+ *(hotspot + _cursor.width * i + 5 + i) = color;
+ *(hotspot - _cursor.width * (i + 3) - i) = color;
+ *(hotspot - _cursor.width * (i + 3) + i) = color;
+ *(hotspot + _cursor.width * (i + 3) - i) = color;
+ *(hotspot + _cursor.width * (i + 3) + i) = color;
+ }
+
+ // Final touches
+
+ *(hotspot - _cursor.width - 7) = color;
+ *(hotspot - _cursor.width + 7) = color;
+ *(hotspot + _cursor.width - 7) = color;
+ *(hotspot + _cursor.width + 7) = color;
+ *(hotspot - (_cursor.width * 5) - 1) = color;
+ *(hotspot - (_cursor.width * 5) + 1) = color;
+ *(hotspot + (_cursor.width * 5) - 1) = color;
+ *(hotspot + (_cursor.width * 5) + 1) = color;
+ } else {
+ const uint16 *src;
+
+ _cursor.hotspotX = _cursorHotspots[2 * _currentCursor];
+ _cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1];
+ src = _cursorImages[_currentCursor];
+
+ _cursor.width = 16;
+ _cursor.height = 16;
+
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < 16; j++) {
+ if (src[i] & (1 << j))
+ _grabbedCursor[16 * i + 15 - j] = color;
+ }
+ }
+ }
+
+ updateCursor();
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp
new file mode 100644
index 0000000000..e14e9a36f0
--- /dev/null
+++ b/engines/scumm/debugger.cpp
@@ -0,0 +1,915 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/str.h"
+#include "common/system.h"
+#include "common/util.h"
+
+#include "scumm/actor.h"
+#include "scumm/boxes.h"
+#include "scumm/debugger.h"
+#include "scumm/imuse.h"
+#include "scumm/object.h"
+#include "scumm/player_v2.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+
+#include "common/debugger.cpp"
+
+namespace Scumm {
+
+// Debug channel lookup table for Debugger console
+static const dbgChannelDesc debugChannels[] = {
+ {"SCRIPTS", "Track script execution", DEBUG_SCRIPTS},
+ {"OPCODES", "Track opcode execution", DEBUG_OPCODES},
+ {"IMUSE", "Track iMUSE events", DEBUG_IMUSE},
+ {"RESOURCE", "Track resource loading/management", DEBUG_RESOURCE},
+ {"VARS", "Track variable changes", DEBUG_VARS},
+ {"ACTORS", "Actor-related debug", DEBUG_ACTORS},
+ {"SOUND", "Sound related debug", DEBUG_SOUND},
+ {"INSANE", "Track INSANE", DEBUG_INSANE},
+ {"SMUSH", "Track SMUSH", DEBUG_SMUSH}
+};
+
+
+void CDECL debugC(int channel, const char *s, ...) {
+ char buf[STRINGBUFLEN];
+ va_list va;
+
+ // FIXME: Still spew all debug at -d9, for crashes in startup etc.
+ // Add setting from commandline ( / abstract channel interface)
+ if (!(g_scumm->_debugFlags & channel) && (gDebugLevel < 9))
+ return;
+
+ va_start(va, s);
+ vsnprintf(buf, STRINGBUFLEN, s, va);
+ va_end(va);
+
+ debug(buf);
+}
+
+ScummDebugger::ScummDebugger(ScummEngine *s)
+ : Common::Debugger<ScummDebugger>() {
+ _vm = s;
+
+ // Register variables
+ DVar_Register("debug_countdown", &_frame_countdown, DVAR_INT, 0);
+
+ DVar_Register("scumm_speed", &_vm->_fastMode, DVAR_BYTE, 0);
+ DVar_Register("scumm_room", &_vm->_currentRoom, DVAR_BYTE, 0);
+ DVar_Register("scumm_roomresource", &_vm->_roomResource, DVAR_INT, 0);
+ DVar_Register("scumm_vars", &_vm->_scummVars, DVAR_INTARRAY, _vm->_numVariables);
+
+ DVar_Register("scumm_gamename", &_vm->_targetName, DVAR_STRING, 0);
+ DVar_Register("scumm_exename", &_vm->_baseName, DVAR_STRING, 0);
+ DVar_Register("scumm_gameid", &_vm->_gameId, DVAR_BYTE, 0);
+
+ // Register commands
+ DCmd_Register("continue", &ScummDebugger::Cmd_Exit);
+ DCmd_Register("exit", &ScummDebugger::Cmd_Exit);
+ DCmd_Register("quit", &ScummDebugger::Cmd_Exit);
+ DCmd_Register("restart", &ScummDebugger::Cmd_Restart);
+
+ DCmd_Register("actor", &ScummDebugger::Cmd_Actor);
+ DCmd_Register("actors", &ScummDebugger::Cmd_PrintActor);
+ DCmd_Register("box", &ScummDebugger::Cmd_PrintBox);
+ DCmd_Register("matrix", &ScummDebugger::Cmd_PrintBoxMatrix);
+ DCmd_Register("camera", &ScummDebugger::Cmd_Camera);
+ DCmd_Register("room", &ScummDebugger::Cmd_Room);
+ DCmd_Register("objects", &ScummDebugger::Cmd_PrintObjects);
+ DCmd_Register("object", &ScummDebugger::Cmd_Object);
+ DCmd_Register("script", &ScummDebugger::Cmd_Script);
+ DCmd_Register("scr", &ScummDebugger::Cmd_Script);
+ DCmd_Register("scripts", &ScummDebugger::Cmd_PrintScript);
+ DCmd_Register("importres", &ScummDebugger::Cmd_ImportRes);
+
+ if (_vm->_gameId == GID_LOOM)
+ DCmd_Register("drafts", &ScummDebugger::Cmd_PrintDraft);
+
+ DCmd_Register("loadgame", &ScummDebugger::Cmd_LoadGame);
+ DCmd_Register("savegame", &ScummDebugger::Cmd_SaveGame);
+
+ DCmd_Register("level", &ScummDebugger::Cmd_DebugLevel);
+ DCmd_Register("debug", &ScummDebugger::Cmd_Debug);
+ DCmd_Register("help", &ScummDebugger::Cmd_Help);
+
+ DCmd_Register("show", &ScummDebugger::Cmd_Show);
+ DCmd_Register("hide", &ScummDebugger::Cmd_Hide);
+
+ DCmd_Register("imuse", &ScummDebugger::Cmd_IMuse);
+}
+
+ScummDebugger::~ScummDebugger() {} // we need this here for __SYMBIAN32__
+
+void ScummDebugger::preEnter() {
+ // Pause sound output
+ _old_soundsPaused = _vm->_sound->_soundsPaused;
+ _vm->_sound->pauseSounds(true);
+}
+
+void ScummDebugger::postEnter() {
+ // Resume previous sound state
+ _vm->_sound->pauseSounds(_old_soundsPaused);
+}
+
+///////////////////////////////////////////////////
+// Now the fun stuff:
+
+// Commands
+bool ScummDebugger::Cmd_Exit(int argc, const char **argv) {
+ _detach_now = true;
+ return false;
+}
+
+bool ScummDebugger::Cmd_Restart(int argc, const char **argv) {
+ _vm->restart();
+
+ _detach_now = true;
+ return false;
+}
+
+bool ScummDebugger::Cmd_IMuse(int argc, const char **argv) {
+ if (!_vm->_imuse && !_vm->_musicEngine) {
+ DebugPrintf("No iMuse engine is active.\n");
+ return true;
+ }
+
+ if (argc > 1) {
+ if (!strcmp(argv[1], "panic")) {
+ _vm->_musicEngine->stopAllSounds();
+ DebugPrintf("AAAIIIEEEEEE!\n");
+ DebugPrintf("Shutting down all music tracks\n");
+ return true;
+ } else if (!strcmp(argv[1], "play")) {
+ if (argc > 2 && (!strcmp(argv[2], "random") || atoi(argv[2]) != 0)) {
+ int sound = atoi(argv[2]);
+ if (!strcmp(argv[2], "random")) {
+ DebugPrintf("Selecting from %d songs...\n", _vm->_numSounds);
+ sound = _vm->_rnd.getRandomNumber(_vm->_numSounds);
+ }
+ _vm->ensureResourceLoaded(rtSound, sound);
+ _vm->_musicEngine->startSound(sound);
+
+ DebugPrintf("Attempted to start music %d.\n", sound);
+ } else {
+ DebugPrintf("Specify a music resource # from 1-255.\n");
+ }
+ return true;
+ } else if (!strcmp(argv[1], "stop")) {
+ if (argc > 2 && (!strcmp(argv[2], "all") || atoi(argv[2]) != 0)) {
+ if (!strcmp(argv[2], "all")) {
+ _vm->_musicEngine->stopAllSounds();
+ DebugPrintf("Shutting down all music tracks.\n");
+ } else {
+ _vm->_musicEngine->stopSound(atoi(argv[2]));
+ DebugPrintf("Attempted to stop music %d.\n", atoi(argv[2]));
+ }
+ } else {
+ DebugPrintf("Specify a music resource # or \"all\".\n");
+ }
+ return true;
+ }
+ }
+
+ DebugPrintf("Available iMuse commands:\n");
+ DebugPrintf(" panic - Stop all music tracks\n");
+ DebugPrintf(" play # - Play a music resource\n");
+ DebugPrintf(" stop # - Stop a music resource\n");
+ return true;
+}
+
+bool ScummDebugger::Cmd_Room(int argc, const char **argv) {
+ if (argc > 1) {
+ int room = atoi(argv[1]);
+ _vm->_actors[_vm->VAR(_vm->VAR_EGO)]._room = room;
+ _vm->_sound->stopAllSounds();
+ _vm->startScene(room, 0, 0);
+ _vm->_fullRedraw = true;
+ return false;
+ } else {
+ DebugPrintf("Current room: %d [%d] - use 'room <roomnum>' to switch\n", _vm->_currentRoom, _vm->_roomResource);
+ return true;
+ }
+}
+
+bool ScummDebugger::Cmd_LoadGame(int argc, const char **argv) {
+ if (argc > 1) {
+ int slot = atoi(argv[1]);
+
+ _vm->requestLoad(slot);
+
+ _detach_now = true;
+ return false;
+ }
+
+ DebugPrintf("Syntax: loadgame <slotnum>\n");
+ return true;
+}
+
+bool ScummDebugger::Cmd_SaveGame(int argc, const char **argv) {
+ if (argc > 2) {
+ int slot = atoi(argv[1]);
+
+ _vm->requestSave(slot, argv[2]);
+ } else
+ DebugPrintf("Syntax: savegame <slotnum> <name>\n");
+
+ return true;
+}
+
+bool ScummDebugger::Cmd_Show(int argc, const char **argv) {
+
+ if (argc != 2) {
+ DebugPrintf("Syntax: show <parameter>\n");
+ return true;
+ }
+
+ if (!strcmp(argv[1], "hex")) {
+ _vm->_hexdumpScripts = true;
+ DebugPrintf("Script hex dumping on\n");
+ } else if (!strncmp(argv[1], "sta", 3)) {
+ _vm->_showStack = 1;
+ DebugPrintf("Stack tracing on\n");
+ } else {
+ DebugPrintf("Unknown show parameter '%s'\nParameters are 'hex' for hex dumping and 'sta' for stack tracing\n", argv[1]);
+ }
+ return true;
+}
+
+bool ScummDebugger::Cmd_Hide(int argc, const char **argv) {
+
+ if (argc != 2) {
+ DebugPrintf("Syntax: hide <parameter>\n");
+ return true;
+ }
+
+ if (!strcmp(argv[1], "hex")) {
+ _vm->_hexdumpScripts = false;
+ DebugPrintf("Script hex dumping off\n");
+ } else if (!strncmp(argv[1], "sta", 3)) {
+ _vm->_showStack = 0;
+ DebugPrintf("Stack tracing off\n");
+ } else {
+ DebugPrintf("Unknown hide parameter '%s'\nParameters are 'hex' to turn off hex dumping and 'sta' to turn off stack tracing\n", argv[1]);
+ }
+ return true;
+}
+
+bool ScummDebugger::Cmd_Script(int argc, const char** argv) {
+ int scriptnum;
+
+ if (argc < 2) {
+ DebugPrintf("Syntax: script <scriptnum> <command>\n");
+ return true;
+ }
+
+ scriptnum = atoi(argv[1]);
+
+ // FIXME: what is the max range on these?
+ // if (scriptnum >= _vm->_numScripts) {
+ // DebugPrintf("Script number %d is out of range (range: 1 - %d)\n", scriptnum, _vm->_numScripts);
+ // return true;
+ //}
+
+ if ((!strcmp(argv[2], "kill")) || (!strcmp(argv[2], "stop"))) {
+ _vm->stopScript(scriptnum);
+ } else if ((!strcmp(argv[2], "run")) || (!strcmp(argv[2], "start"))) {
+ _vm->runScript(scriptnum, 0, 0, 0);
+ return false;
+ } else {
+ DebugPrintf("Unknown script command '%s'\nUse <kill/stop | run/start> as command\n", argv[2]);
+ }
+
+ return true;
+}
+
+bool ScummDebugger::Cmd_ImportRes(int argc, const char** argv) {
+ Common::File file;
+ uint32 size;
+ int resnum;
+
+ if (argc != 4) {
+ DebugPrintf("Syntax: importres <restype> <filename> <resnum>\n");
+ return true;
+ }
+
+ resnum = atoi(argv[3]);
+ // FIXME add bounds check
+
+ if (!strncmp(argv[1], "scr", 3)) {
+ file.open(argv[2], Common::File::kFileReadMode);
+ if (file.isOpen() == false) {
+ DebugPrintf("Could not open file %s\n", argv[2]);
+ return true;
+ }
+ if (_vm->_features & GF_SMALL_HEADER) {
+ size = file.readUint16LE();
+ file.seek(-2, SEEK_CUR);
+ } else if (_vm->_features & GF_SMALL_HEADER) {
+ if (_vm->_version == 4)
+ file.seek(8, SEEK_CUR);
+ size = file.readUint32LE();
+ file.readUint16LE();
+ file.seek(-6, SEEK_CUR);
+ } else {
+ file.readUint32BE();
+ size = file.readUint32BE();
+ file.seek(-8, SEEK_CUR);
+ }
+
+ file.read(_vm->res.createResource(rtScript, resnum, size), size);
+
+ } else
+ DebugPrintf("Unknown importres type '%s'\n", argv[1]);
+ return true;
+}
+
+bool ScummDebugger::Cmd_PrintScript(int argc, const char **argv) {
+ int i;
+ ScriptSlot *ss = _vm->vm.slot;
+ DebugPrintf("+-----------------------------------+\n");
+ DebugPrintf("|# | num|offst|sta|typ|fr|rec|fc|cut|\n");
+ DebugPrintf("+--+----+-----+---+---+--+---+--+---+\n");
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++) {
+ if (ss->number) {
+ DebugPrintf("|%2d|%4d|%05x|%3d|%3d|%2d|%3d|%2d|%3d|\n",
+ i, ss->number, ss->offs, ss->status, ss->where,
+ ss->freezeResistant, ss->recursive,
+ ss->freezeCount, ss->cutsceneOverride);
+ }
+ }
+ DebugPrintf("+-----------------------------------+\n");
+
+ return true;
+}
+
+bool ScummDebugger::Cmd_Actor(int argc, const char **argv) {
+ Actor *a;
+ int actnum;
+ int value = 0, value2 = 0;
+
+ if (argc < 3) {
+ DebugPrintf("Syntax: actor <actornum> <command> <parameter>\n");
+ return true;
+ }
+
+ actnum = atoi(argv[1]);
+ if (actnum >= _vm->_numActors) {
+ DebugPrintf("Actor %d is out of range (range: 1 - %d)\n", actnum, _vm->_numActors);
+ return true;
+ }
+
+ a = &_vm->_actors[actnum];
+ if (argc > 3)
+ value = atoi(argv[3]);
+ if (argc > 4)
+ value2 = atoi(argv[4]);
+
+ if (!strcmp(argv[2], "animvar")) {
+ a->setAnimVar(value, value2);
+ DebugPrintf("Actor[%d].animVar[%d] = %d\n", actnum, value, a->getAnimVar(value));
+ } else if (!strcmp(argv[2], "anim")) {
+ a->animateActor(value);
+ DebugPrintf("Actor[%d].animateActor(%d)\n", actnum, value);
+ } else if (!strcmp(argv[2], "ignoreboxes")) {
+ a->_ignoreBoxes = (value > 0);
+ DebugPrintf("Actor[%d].ignoreBoxes = %d\n", actnum, a->_ignoreBoxes);
+ } else if (!strcmp(argv[2], "x")) {
+ a->putActor(value, a->_pos.y, a->_room);
+ DebugPrintf("Actor[%d].x = %d\n", actnum, a->_pos.x);
+ _vm->_fullRedraw = true;
+ } else if (!strcmp(argv[2], "y")) {
+ a->putActor(a->_pos.x, value, a->_room);
+ DebugPrintf("Actor[%d].y = %d\n", actnum, a->_pos.y);
+ _vm->_fullRedraw = true;
+ } else if (!strcmp(argv[2], "_elevation")) {
+ a->setElevation(value);
+ DebugPrintf("Actor[%d]._elevation = %d\n", actnum, a->getElevation());
+ _vm->_fullRedraw = true;
+ } else if (!strcmp(argv[2], "costume")) {
+ if (value >= _vm->res.num[rtCostume])
+ DebugPrintf("Costume not changed as %d exceeds max of %d\n", value, _vm->res.num[rtCostume]);
+ else {
+ a->setActorCostume(value);
+ _vm->_fullRedraw = true;
+ DebugPrintf("Actor[%d].costume = %d\n", actnum, a->_costume);
+ }
+ } else if (!strcmp(argv[2], "name")) {
+ DebugPrintf("Name of actor %d: %s\n", actnum, _vm->getObjOrActorName(actnum));
+ } else {
+ DebugPrintf("Unknown actor command '%s'\nUse <ignoreboxes |costume> as command\n", argv[2]);
+ }
+
+ return true;
+
+}
+bool ScummDebugger::Cmd_PrintActor(int argc, const char **argv) {
+ int i;
+ Actor *a;
+
+ DebugPrintf("+-----------------------------------------------------------+\n");
+ DebugPrintf("|# | x | y | w |elev|cos|box|mov| zp|frm|scl|dir| cls |\n");
+ DebugPrintf("+--+----+----+---+----+---+---+---+---+---+---+---+---------+\n");
+ for (i = 1; i < _vm->_numActors; i++) {
+ a = &_vm->_actors[i];
+ if (a->_visible)
+ DebugPrintf("|%2d|%4d|%4d|%3d|%4d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|$%08x|\n",
+ a->_number, a->_pos.x, a->_pos.y, a->_width, a->getElevation(),
+ a->_costume, a->_walkbox, a->_moving, a->_forceClip, a->_frame,
+ a->_scalex, a->getFacing(), _vm->_classData[a->_number]);
+ }
+ DebugPrintf("\n");
+ return true;
+}
+
+bool ScummDebugger::Cmd_PrintObjects(int argc, const char **argv) {
+ int i;
+ ObjectData *o;
+ DebugPrintf("Objects in current room\n");
+ DebugPrintf("+---------------------------------+------------+\n");
+ DebugPrintf("|num | x | y |width|height|state|fl| cls |\n");
+ DebugPrintf("+----+----+----+-----+------+-----+--+---------+\n");
+
+ for (i = 1; i < _vm->_numLocalObjects ; i++) {
+ o = &(_vm->_objs[i]);
+ if (o->obj_nr == 0)
+ continue;
+ DebugPrintf("|%4d|%4d|%4d|%5d|%6d|%5d|%2d|$%08x|\n",
+ o->obj_nr, o->x_pos, o->y_pos, o->width, o->height, o->state,
+ o->fl_object_index, _vm->_classData[o->obj_nr]);
+ }
+ DebugPrintf("\n");
+
+ return true;
+}
+
+bool ScummDebugger::Cmd_Object(int argc, const char **argv) {
+ int i;
+ int obj;
+
+ if (argc < 3) {
+ DebugPrintf("Syntax: object <objectnum> <command> <parameter>\n");
+ return true;
+ }
+
+ obj = atoi(argv[1]);
+ if (obj >= _vm->_numGlobalObjects) {
+ DebugPrintf("Object %d is out of range (range: 1 - %d)\n", obj, _vm->_numGlobalObjects);
+ return true;
+ }
+
+ if (!strcmp(argv[2], "pickup")) {
+ for (i = 0; i < _vm->_numInventory; i++) {
+ if (_vm->_inventory[i] == (uint16)obj) {
+ _vm->putOwner(obj, _vm->VAR(_vm->VAR_EGO));
+ _vm->runInventoryScript(obj);
+ return true;
+ }
+ }
+
+ if (argc == 3)
+ _vm->addObjectToInventory(obj, _vm->_currentRoom);
+ else
+ _vm->addObjectToInventory(obj, atoi(argv[3]));
+
+ _vm->putOwner(obj, _vm->VAR(_vm->VAR_EGO));
+ _vm->putClass(obj, kObjectClassUntouchable, 1);
+ _vm->putState(obj, 1);
+ _vm->markObjectRectAsDirty(obj);
+ _vm->clearDrawObjectQueue();
+ _vm->runInventoryScript(obj);
+ } else if (!strcmp(argv[2], "state")) {
+ _vm->putState(obj, atoi(argv[3]));
+ //is BgNeedsRedraw enough?
+ _vm->_bgNeedsRedraw = true;
+ } else if (!strcmp(argv[2], "name")) {
+ DebugPrintf("Name of object %d: %s\n", obj, _vm->getObjOrActorName(obj));
+ } else {
+ DebugPrintf("Unknown object command '%s'\nUse <pickup | state> as command\n", argv[2]);
+ }
+
+ return true;
+}
+
+bool ScummDebugger::Cmd_Help(int argc, const char **argv) {
+ // console normally has 39 line width
+ // wrap around nicely
+ int width = 0, size, i;
+
+ DebugPrintf("Commands are:\n");
+ for (i = 0 ; i < _dcmd_count ; i++) {
+ size = strlen(_dcmds[i].name) + 1;
+
+ if ((width + size) >= 39) {
+ DebugPrintf("\n");
+ width = size;
+ } else
+ width += size;
+
+ DebugPrintf("%s ", _dcmds[i].name);
+ }
+
+ width = 0;
+
+ DebugPrintf("\n\nVariables are:\n");
+ for (i = 0 ; i < _dvar_count ; i++) {
+ size = strlen(_dvars[i].name) + 1;
+
+ if ((width + size) >= 39) {
+ DebugPrintf("\n");
+ width = size;
+ } else
+ width += size;
+
+ DebugPrintf("%s ", _dvars[i].name);
+ }
+
+ DebugPrintf("\n");
+
+ return true;
+}
+
+bool ScummDebugger::Cmd_Debug(int argc, const char **argv) {
+ int numChannels = sizeof(debugChannels) / sizeof(dbgChannelDesc);
+
+ bool setFlag = false; // Remove or add debug channel?
+
+ if ((argc == 1) && (_vm->_debugFlags == 0)) {
+ DebugPrintf("No debug flags are enabled\n");
+ DebugPrintf("Available Channels: ");
+ for (int i = 0; i < numChannels; i++) {
+ DebugPrintf("%s, ", debugChannels[i].channel);
+ }
+ DebugPrintf("\n");
+ return true;
+ }
+
+ if ((argc == 1) && (_vm->_debugFlags > 0)) {
+ for (int i = 0; i < numChannels; i++) {
+ if (_vm->_debugFlags & debugChannels[i].flag)
+ DebugPrintf("%s - %s\n", debugChannels[i].channel,
+ debugChannels[i].desc);
+ }
+ return true;
+ }
+
+ // Enable or disable channel?
+ if (argv[1][0] == '+') {
+ setFlag = true;
+ } else if (argv[1][0] == '-') {
+ setFlag = false;
+ } else {
+ DebugPrintf("Syntax: Debug +CHANNEL, or Debug -CHANNEL\n");
+ DebugPrintf("Available Channels: ");
+ for (int i = 0; i < numChannels; i++) {
+ DebugPrintf("%s, ", debugChannels[i].channel);
+ DebugPrintf("\n");
+ }
+ }
+
+ // Identify flag
+ const char *realFlag = argv[1] + 1;
+ for (int i = 0; i < numChannels; i++) {
+ if ((scumm_stricmp(debugChannels[i].channel, realFlag)) == 0) {
+ if (setFlag) {
+ _vm->_debugFlags |= debugChannels[i].flag;
+ DebugPrintf("Enable ");
+ } else {
+ _vm->_debugFlags &= ~debugChannels[i].flag;
+ DebugPrintf("Disable ");
+ }
+
+ DebugPrintf("%s\n", debugChannels[i].desc);
+ return true;
+ }
+ }
+
+ DebugPrintf("Unknown flag. Type 'Debug ?' for syntax\n");
+ return true;
+}
+
+bool ScummDebugger::Cmd_DebugLevel(int argc, const char **argv) {
+ if (argc == 1) {
+ if (_vm->_debugMode == false)
+ DebugPrintf("Debugging is not enabled at this time\n");
+ else
+ DebugPrintf("Debugging is currently set at level %d\n", gDebugLevel);
+ } else { // set level
+ gDebugLevel = atoi(argv[1]);
+ if (gDebugLevel >= 0) {
+ _vm->_debugMode = true;
+ DebugPrintf("Debug level set to level %d\n", gDebugLevel);
+ } else if (gDebugLevel < 0) {
+ _vm->_debugMode = false;
+ DebugPrintf("Debugging is now disabled\n");
+ } else
+ DebugPrintf("Not a valid debug level\n");
+ }
+
+ return true;
+}
+
+bool ScummDebugger::Cmd_Camera(int argc, const char **argv) {
+ DebugPrintf("Camera: cur (%d,%d) - dest (%d,%d) - accel (%d,%d) -- last (%d,%d)\n",
+ _vm->camera._cur.x, _vm->camera._cur.y, _vm->camera._dest.x, _vm->camera._dest.y,
+ _vm->camera._accel.x, _vm->camera._accel.y, _vm->camera._last.x, _vm->camera._last.y);
+
+ return true;
+}
+
+bool ScummDebugger::Cmd_PrintBox(int argc, const char **argv) {
+ int num, i = 0;
+
+ if (argc > 1) {
+ for (i = 1; i < argc; i++)
+ printBox(atoi(argv[i]));
+ } else {
+ num = _vm->getNumBoxes();
+ DebugPrintf("\nWalk boxes:\n");
+ for (i = 0; i < num; i++)
+ printBox(i);
+ }
+ return true;
+}
+
+bool ScummDebugger::Cmd_PrintBoxMatrix(int argc, const char **argv) {
+ byte *boxm = _vm->getBoxMatrixBaseAddr();
+ int num = _vm->getNumBoxes();
+ int i, j;
+
+ DebugPrintf("Walk matrix:\n");
+ if (_vm->_version <= 2)
+ boxm += num;
+ for (i = 0; i < num; i++) {
+ DebugPrintf("%d: ", i);
+ if (_vm->_version <= 2) {
+ for (j = 0; j < num; j++)
+ DebugPrintf("[%d] ", *boxm++);
+ } else {
+ while (*boxm != 0xFF) {
+ DebugPrintf("[%d-%d=>%d] ", boxm[0], boxm[1], boxm[2]);
+ boxm += 3;
+ }
+ boxm++;
+ }
+ DebugPrintf("\n");
+ }
+ return true;
+}
+
+void ScummDebugger::printBox(int box) {
+ if (box < 0 || box >= _vm->getNumBoxes()) {
+ DebugPrintf("%d is not a valid box!\n", box);
+ return;
+ }
+ BoxCoords coords;
+ int flags = _vm->getBoxFlags(box);
+ int mask = _vm->getMaskFromBox(box);
+ int scale = _vm->getBoxScale(box);
+
+ _vm->getBoxCoordinates(box, &coords);
+
+ // Print out coords, flags, zbuffer mask
+ DebugPrintf("%d: [%d x %d] [%d x %d] [%d x %d] [%d x %d], flags=0x%02x, mask=%d, scale=%d\n",
+ box,
+ coords.ul.x, coords.ul.y, coords.ll.x, coords.ll.y,
+ coords.ur.x, coords.ur.y, coords.lr.x, coords.lr.y,
+ flags, mask, scale);
+
+ // Draw the box
+ drawBox(box);
+}
+
+/************ ENDER: Temporary debug code for boxen **************/
+
+static int gfxPrimitivesCompareInt(const void *a, const void *b);
+
+
+static void hlineColor(ScummEngine *scumm, int x1, int x2, int y, byte color) {
+ VirtScreen *vs = &scumm->virtscr[0];
+ byte *ptr;
+
+ // Clip y
+ y += scumm->_screenTop;
+ if (y < 0 || y >= scumm->_screenHeight)
+ return;
+
+ if (x2 < x1)
+ SWAP(x2, x1);
+
+ // Clip x1 / x2
+ const int left = scumm->_screenStartStrip * 8;
+ const int right = scumm->_screenEndStrip * 8;
+ if (x1 < left)
+ x1 = left;
+ if (x2 >= right)
+ x2 = right - 1;
+
+
+ ptr = (byte *)vs->pixels + x1 + y * vs->pitch;
+
+ while (x1++ <= x2) {
+ *ptr++ = color;
+ }
+}
+
+static int gfxPrimitivesCompareInt(const void *a, const void *b) {
+ return (*(const int *)a) - (*(const int *)b);
+}
+
+static void fillQuad(ScummEngine *scumm, Common::Point v[4], int color) {
+ const int N = 4;
+ int i;
+ int y;
+ int miny, maxy;
+ Common::Point pt1, pt2;
+
+ int polyInts[N];
+
+
+ // Determine Y maxima
+ miny = maxy = v[0].y;
+ for (i = 1; i < N; i++) {
+ if (v[i].y < miny) {
+ miny = v[i].y;
+ } else if (v[i].y > maxy) {
+ maxy = v[i].y;
+ }
+ }
+
+ // Draw, scanning y
+ for (y = miny; y <= maxy; y++) {
+ int ints = 0;
+ for (i = 0; i < N; i++) {
+ int ind1 = i;
+ int ind2 = (i + 1) % N;
+ pt1 = v[ind1];
+ pt2 = v[ind2];
+ if (pt1.y > pt2.y) {
+ SWAP(pt1, pt2);
+ }
+
+ if (pt1.y <= y && y <= pt2.y) {
+ if (y == pt1.y && y == pt2.y) {
+ hlineColor(scumm, pt1.x, pt2.x, y, color);
+ } else if ((y >= pt1.y) && (y < pt2.y)) {
+ polyInts[ints++] = (y - pt1.y) * (pt2.x - pt1.x) / (pt2.y - pt1.y) + pt1.x;
+ } else if ((y == maxy) && (y > pt1.y) && (y <= pt2.y)) {
+ polyInts[ints++] = (y - pt1.y) * (pt2.x - pt1.x) / (pt2.y - pt1.y) + pt1.x;
+ }
+ }
+ }
+ qsort(polyInts, ints, sizeof(int), gfxPrimitivesCompareInt);
+
+ for (i = 0; i < ints; i += 2) {
+ hlineColor(scumm, polyInts[i], polyInts[i + 1], y, color);
+ }
+ }
+
+ return;
+}
+
+void ScummDebugger::drawBox(int box) {
+ BoxCoords coords;
+ Common::Point r[4];
+
+ _vm->getBoxCoordinates(box, &coords);
+
+ r[0] = coords.ul;
+ r[1] = coords.ur;
+ r[2] = coords.lr;
+ r[3] = coords.ll;
+
+ // TODO - maybe use different colors for each box, and/or print the box number inside it?
+ fillQuad(_vm, r, 13);
+
+ VirtScreen *vs = _vm->findVirtScreen(coords.ul.y);
+ if (vs != NULL)
+ _vm->markRectAsDirty(vs->number, 0, vs->w, 0, vs->h);
+ _vm->drawDirtyScreenParts();
+ _vm->_system->updateScreen();
+}
+
+bool ScummDebugger::Cmd_PrintDraft(int argc, const char **argv) {
+ const char *names[] = {
+ "Opening", "Straw to Gold", "Dyeing",
+ "Night Vision", "Twisting", "Sleep",
+ "Emptying", "Invisibility", "Terror",
+ "Sharpening", "Reflection", "Healing",
+ "Silence", "Shaping", "Unmaking",
+ "Transcendence"
+ };
+ int odds[] = {
+ 15162, 15676, 16190, 64, 16961, 17475, 17989, 18503,
+ 73, 19274, 76, 77, 20302, 20816, 21330, 84
+ };
+
+ const char *notes = "cdefgabC";
+ int i, base, draft;
+
+ if (_vm->_gameId != GID_LOOM) {
+ DebugPrintf("Command only works with Loom/LoomCD\n");
+ return true;
+ }
+
+ // There are 16 drafts, stored from variable 50 or 100 and upwards.
+ // Each draft occupies two variables. Even-numbered variables contain
+ // the notes for each draft, and a number of flags:
+ //
+ // +---+---+---+---+-----+-----+-----+-----+
+ // | A | B | C | D | 444 | 333 | 222 | 111 |
+ // +---+---+---+---+-----+-----+-----+-----+
+ //
+ // A Unknown
+ // B The player has used the draft successfully at least once
+ // C The player has knowledge of the draft
+ // D Unknown
+ // 444 The fourth note
+ // 333 The third note
+ // 222 The second note
+ // 111 The first note
+ //
+ // I don't yet know what the odd-numbered variables are used for.
+ // Possibly they store information on where and/or how the draft can
+ // be used. They appear to remain constant throughout the game.
+
+ base = (_vm->_version == 3) ? 50 : 100;
+
+ if (argc == 2) {
+ // We had to debug a problem at the end of the game that only
+ // happened if you interrupted the intro at a specific point.
+ // That made it useful with a command to learn all the drafts
+ // and notes.
+
+ if (strcmp(argv[1], "learn") == 0) {
+ for (i = 0; i < 16; i++)
+ _vm->_scummVars[base + 2 * i] |= 0x2000;
+ _vm->_scummVars[base + 72] = 8;
+
+ // In theory, we could run script 18 here to redraw
+ // the distaff, but I don't know if that's a safe
+ // thing to do.
+
+ DebugPrintf("Learned all drafts and notes.\n");
+ return true;
+ }
+
+ // During the testing of EGA Loom we had some trouble with the
+ // drafts data structure being overwritten. I don't expect
+ // this command is particularly useful any more, but it will
+ // attempt to repair the (probably) static part of it.
+
+ if (strcmp(argv[1], "fix") == 0) {
+ for (i = 0; i < 16; i++)
+ _vm->_scummVars[base + 2 * i + 1] = odds[i];
+ DebugPrintf(
+ "An attempt has been made to repair\n"
+ "the internal drafts data structure.\n"
+ "Continue on your own risk.\n");
+ return true;
+ }
+ }
+
+ // Probably the most useful command for ordinary use: list the drafts.
+
+ for (i = 0; i < 16; i++) {
+ draft = _vm->_scummVars[base + i * 2];
+ DebugPrintf("%d %-13s %c%c%c%c %c%c %5d %c\n",
+ base + 2 * i,
+ names[i],
+ notes[draft & 0x0007],
+ notes[(draft & 0x0038) >> 3],
+ notes[(draft & 0x01c0) >> 6],
+ notes[(draft & 0x0e00) >> 9],
+ (draft & 0x2000) ? 'K' : ' ',
+ (draft & 0x4000) ? 'U' : ' ',
+ _vm->_scummVars[base + 2 * i + 1],
+ (_vm->_scummVars[base + 2 * i + 1] != odds[i]) ? '!' : ' ');
+ }
+
+ return true;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/debugger.h b/engines/scumm/debugger.h
new file mode 100644
index 0000000000..664c28923d
--- /dev/null
+++ b/engines/scumm/debugger.h
@@ -0,0 +1,78 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef DEBUGGER_H
+#define DEBUGGER_H
+
+#include "common/debugger.h"
+
+namespace Scumm {
+
+class ScummEngine;
+
+class ScummDebugger : public Common::Debugger<ScummDebugger> {
+public:
+ ScummDebugger(ScummEngine *s);
+ virtual ~ScummDebugger(); // we need this here for __SYMBIAN32__
+
+protected:
+ ScummEngine *_vm;
+ bool _old_soundsPaused;
+
+ virtual void preEnter();
+ virtual void postEnter();
+
+ // Commands
+ bool Cmd_Exit(int argc, const char **argv);
+ bool Cmd_Room(int argc, const char **argv);
+ bool Cmd_LoadGame(int argc, const char **argv);
+ bool Cmd_SaveGame(int argc, const char **argv);
+ bool Cmd_Restart(int argc, const char **argv);
+
+ bool Cmd_PrintActor(int argc, const char **argv);
+ bool Cmd_PrintBox(int argc, const char **argv);
+ bool Cmd_PrintBoxMatrix(int argc, const char **argv);
+ bool Cmd_PrintObjects(int argc, const char **argv);
+ bool Cmd_Actor(int argc, const char **argv);
+ bool Cmd_Camera(int argc, const char **argv);
+ bool Cmd_Object(int argc, const char **argv);
+ bool Cmd_Script(int argc, const char **argv);
+ bool Cmd_PrintScript(int argc, const char **argv);
+ bool Cmd_ImportRes(int argc, const char **argv);
+
+ bool Cmd_PrintDraft(int argc, const char **argv);
+
+ bool Cmd_Debug(int argc, const char **argv);
+ bool Cmd_DebugLevel(int argc, const char **argv);
+ bool Cmd_Help(int argc, const char **argv);
+
+ bool Cmd_Show(int argc, const char **argv);
+ bool Cmd_Hide(int argc, const char **argv);
+
+ bool Cmd_IMuse (int argc, const char **argv);
+
+ void printBox(int box);
+ void drawBox(int box);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
new file mode 100644
index 0000000000..eafd956251
--- /dev/null
+++ b/engines/scumm/dialogs.cpp
@@ -0,0 +1,986 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "common/scaler.h"
+
+#include "gui/about.h"
+#include "gui/chooser.h"
+#include "gui/newgui.h"
+#include "gui/ListWidget.h"
+
+#include "scumm/dialogs.h"
+#include "scumm/sound.h"
+#include "scumm/scumm.h"
+#include "scumm/imuse.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/player_v2.h"
+#include "scumm/verbs.h"
+#include "sound/mididrv.h"
+#include "sound/mixer.h"
+
+#ifndef DISABLE_HELP
+#include "scumm/help.h"
+#endif
+
+#ifdef SMALL_SCREEN_DEVICE
+#include "KeysDialog.h"
+#endif
+
+using GUI::CommandSender;
+using GUI::StaticTextWidget;
+using GUI::kButtonWidth;
+using GUI::kButtonHeight;
+using GUI::kBigButtonWidth;
+using GUI::kBigButtonHeight;
+using GUI::kCloseCmd;
+using GUI::kTextAlignCenter;
+using GUI::kTextAlignLeft;
+using GUI::WIDGET_ENABLED;
+
+typedef GUI::OptionsDialog GUI_OptionsDialog;
+typedef GUI::ChooserDialog GUI_ChooserDialog;
+typedef GUI::Dialog GUI_Dialog;
+
+namespace Scumm {
+
+struct ResString {
+ int num;
+ char string[80];
+};
+
+#ifdef PALMOS_68K
+static ResString *string_map_table_v7;
+static ResString *string_map_table_v6;
+static ResString *string_map_table_v5;
+#else
+static ResString string_map_table_v7[] = {
+ {96, "game name and version"}, //that's how it's supposed to be
+ {77, "Select a game to LOAD"},
+ {76, "Name your SAVE game"},
+ {70, "save"}, //boot8
+ {71, "load"}, //boot9
+ {72, "play"}, //boot10
+ {73, "cancel"}, //boot11
+ {74, "quit"}, //boot12
+ {75, "ok"}, //boot13
+ {85, "game paused"}, // boot3
+ {96, "the dig v1.0"},
+
+ /* this is the almost complete string map for v7
+ {63, "how may I serve you?"},
+ {64, "the dig v1.0"}, //(game name/version)
+ {67, "text display only"},
+ {68, "c:\\dig"}, //boot007 (save path ?)
+ {69, "the dig"}, //boot21 (game name)
+ {70, "save"}, //boot8
+ {71, "load"}, //boot9
+ {72, "play"}, //boot10
+ {73, "cancel"}, //boot11
+ {74, "quit"}, //boot12
+ {75, "ok"}, //boot13
+ {76, "name your save game"}, //boot19
+ {77, "select a game to load"}, //boot20
+ {78, "you must enter a name"},//boot14
+ {79, "saving '%s'"}, //boot17
+ {80, "loading '%s'"}, //boot18
+ {81, "the game was NOT saved"}, //boot15
+ {82, "the game was NOT loaded"}, //boot16
+ {83, "how may I serve you?"},
+ {84, "how may I serve you?"},
+ {85, "game paused"}, // boot3
+ {86, "Are you sure you want to restart"},
+ {87, "Are you sure you want to quit?"}, //boot05
+ {89, "how may I serve you?"},
+ {90, "music"}, //boot22
+ {91, "voice"}, //boot23
+ {92, "sfx"}, //boot24
+ {93, "disabled"}, //boot25
+ {94, "text speed"}, //boot26
+ {95, "text display"}, //boot27
+ {96, "the dig v1.0"},*/
+
+};
+
+static ResString string_map_table_v6[] = {
+ {117, "How may I serve you?"},
+ {109, "Select a game to LOAD"},
+ {108, "Name your SAVE game"},
+ {96, "Save"},
+ {97, "Load"},
+ {98, "Play"},
+ {99, "Cancel"},
+ {100, "Quit"},
+ {101, "OK"},
+ {93, "Game paused"},
+ {210, "Game version"}
+};
+
+static ResString string_map_table_v5[] = {
+ {28, "How may I serve you?"},
+ {20, "Select a game to LOAD"},
+ {19, "Name your SAVE game"},
+ {7, "Save"},
+ {8, "Load"},
+ {9, "Play"},
+ {10, "Cancel"},
+ {11, "Quit"},
+ {12, "OK"},
+ {4, "Game paused"}
+};
+#endif
+
+#pragma mark -
+
+ScummDialog::ScummDialog(ScummEngine *scumm, int x, int y, int w, int h)
+ : GUI::Dialog(x, y, w, h), _vm(scumm) {
+_drawingHints |= GUI::THEME_HINT_SPECIAL_COLOR;
+}
+
+const Common::String ScummDialog::queryResString(int stringno) {
+ byte buf[256];
+ byte *result;
+
+ if (stringno == 0)
+ return String();
+
+ if (_vm->_version >= 7)
+ result = _vm->getStringAddressVar(string_map_table_v7[stringno - 1].num);
+ else if (_vm->_version == 6)
+ result = _vm->getStringAddressVar(string_map_table_v6[stringno - 1].num);
+ else if (_vm->_version == 5)
+ result = _vm->getStringAddress(string_map_table_v5[stringno - 1].num);
+ else
+ // TODO: For V8 games, maybe grab the strings from the language file?
+ return string_map_table_v5[stringno - 1].string;
+
+ if (result && *result == '/') {
+ _vm->translateText(result, buf);
+ result = buf;
+ }
+
+ if (!result || *result == '\0') { // Gracelessly degrade to english :)
+ return string_map_table_v5[stringno - 1].string;
+ }
+
+ // Convert to a proper string (take care of FF codes)
+ byte chr;
+ String tmp;
+ while ((chr = *result++)) {
+ if (chr == 0xFF) {
+ result += 3;
+ } else if (chr != '@') {
+ tmp += chr;
+ }
+ }
+ return tmp;
+}
+
+#pragma mark -
+
+Common::StringList generateSavegameList(ScummEngine *scumm, bool saveMode);
+
+enum {
+ kSaveCmd = 'SAVE',
+ kLoadCmd = 'LOAD',
+ kPlayCmd = 'PLAY',
+ kOptionsCmd = 'OPTN',
+ kHelpCmd = 'HELP',
+ kAboutCmd = 'ABOU',
+ kQuitCmd = 'QUIT'
+};
+
+class SaveLoadChooser : public GUI::ChooserDialog, public BaseSaveLoadChooser {
+ typedef Common::String String;
+ typedef Common::StringList StringList;
+protected:
+ bool _saveMode;
+
+public:
+ SaveLoadChooser(const String &title, const String &buttonLabel, bool saveMode);
+
+ virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+ const String &getResultString() const;
+ void setList(const StringList& list) { GUI_ChooserDialog::setList(list); }
+ int runModal() { return GUI_ChooserDialog::runModal(); }
+};
+
+SaveLoadChooser::SaveLoadChooser(const String &title, const String &buttonLabel, bool saveMode)
+ : GUI::ChooserDialog(title, buttonLabel, 182), _saveMode(saveMode) {
+
+ _list->setEditable(saveMode);
+ _list->setNumberingMode(saveMode ? GUI::kListNumberingOne : GUI::kListNumberingZero);
+}
+
+const Common::String &SaveLoadChooser::getResultString() const {
+ return _list->getSelectedString();
+}
+
+void SaveLoadChooser::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+ int selItem = _list->getSelected();
+ switch (cmd) {
+ case GUI::kListItemActivatedCmd:
+ case GUI::kListItemDoubleClickedCmd:
+ if (selItem >= 0) {
+ if (_saveMode || !getResultString().isEmpty()) {
+ setResult(selItem);
+ close();
+ }
+ }
+ break;
+ case GUI::kListSelectionChangedCmd:
+ if (_saveMode) {
+ _list->startEditMode();
+ }
+ // Disable button if nothing is selected, or (in load mode) if an empty
+ // list item is selected. We allow choosing an empty item in save mode
+ // because we then just assign a default name.
+ _chooseButton->setEnabled(selItem >= 0 && (_saveMode || !getResultString().isEmpty()));
+ _chooseButton->draw();
+ break;
+ default:
+ GUI_ChooserDialog::handleCommand(sender, cmd, data);
+ }
+}
+
+#pragma mark -
+
+enum {
+ kChooseCmd = 'Chos'
+};
+
+// only for use with >= 640x400 resolutions
+class SaveLoadChooserEx : public GUI::Dialog, public BaseSaveLoadChooser {
+ typedef Common::String String;
+ typedef Common::StringList StringList;
+protected:
+ bool _saveMode;
+ GUI::ListWidget *_list;
+ GUI::ButtonWidget *_chooseButton;
+ GUI::GraphicsWidget *_gfxWidget;
+ GUI::StaticTextWidget *_date;
+ GUI::StaticTextWidget *_time;
+ GUI::StaticTextWidget *_playtime;
+ ScummEngine *_scumm;
+
+public:
+ SaveLoadChooserEx(const String &title, const String &buttonLabel, bool saveMode, ScummEngine *engine);
+
+ virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
+ const String &getResultString() const;
+ void setList(const StringList& list);
+ int runModal();
+};
+
+SaveLoadChooserEx::SaveLoadChooserEx(const String &title, const String &buttonLabel, bool saveMode, ScummEngine *engine)
+ : Dialog(8, 8, engine->_system->getOverlayWidth() - 2 * 8, engine->_system->getOverlayHeight() - 16), _saveMode(saveMode), _list(0), _chooseButton(0), _gfxWidget(0), _scumm(engine) {
+
+ new StaticTextWidget(this, 10, 6, _w - 2 * 10, kLineHeight, title, kTextAlignCenter);
+
+ // Add choice list
+ _list = new GUI::ListWidget(this, 10, 18, _w - 2 * 10 - 180, _h - 14 - kBigButtonHeight - 18, GUI::kBigWidgetSize);
+ _list->setEditable(saveMode);
+ _list->setNumberingMode(saveMode ? GUI::kListNumberingOne : GUI::kListNumberingZero);
+
+ // Add the thumbnail display
+ _gfxWidget = new GUI::GraphicsWidget(this,
+ _w - (kThumbnailWidth + 22),
+ 18,
+ kThumbnailWidth + 8,
+ ((_scumm->_system->getHeight() % 200 && _scumm->_system->getHeight() != 350) ? kThumbnailHeight2 : kThumbnailHeight1) + 8);
+ _gfxWidget->setFlags(GUI::WIDGET_BORDER);
+
+ int height = 18 + ((_scumm->_system->getHeight() % 200 && _scumm->_system->getHeight() != 350) ? kThumbnailHeight2 : kThumbnailHeight1) + 8;
+
+ _date = new StaticTextWidget(this,
+ _w - (kThumbnailWidth + 22),
+ height,
+ kThumbnailWidth + 8,
+ kLineHeight,
+ "No date saved",
+ kTextAlignCenter);
+ _date->setFlags(GUI::WIDGET_CLEARBG);
+
+ height += kLineHeight;
+
+ _time = new StaticTextWidget(this,
+ _w - (kThumbnailWidth + 22),
+ height,
+ kThumbnailWidth + 8,
+ kLineHeight,
+ "No time saved",
+ kTextAlignCenter);
+ _time->setFlags(GUI::WIDGET_CLEARBG);
+
+ height += kLineHeight;
+
+ _playtime = new StaticTextWidget(this,
+ _w - (kThumbnailWidth + 22),
+ height,
+ kThumbnailWidth + 8,
+ kLineHeight,
+ "No playtime saved",
+ kTextAlignCenter);
+ _playtime->setFlags(GUI::WIDGET_CLEARBG);
+
+ // Buttons
+ addButton(this, _w - 2 * (kBigButtonWidth + 10), _h - kBigButtonHeight - 8, "Cancel", kCloseCmd, 0, GUI::kBigWidgetSize);
+ _chooseButton = addButton(this, _w - (kBigButtonWidth + 10), _h - kBigButtonHeight - 8, buttonLabel, kChooseCmd, 0, GUI::kBigWidgetSize);
+ _chooseButton->setEnabled(false);
+}
+
+const Common::String &SaveLoadChooserEx::getResultString() const {
+ return _list->getSelectedString();
+}
+
+void SaveLoadChooserEx::setList(const StringList& list) {
+ _list->setList(list);
+}
+
+int SaveLoadChooserEx::runModal() {
+ _gfxWidget->setGfx(0);
+ int ret = Dialog::runModal();
+ return ret;
+}
+
+void SaveLoadChooserEx::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+ int selItem = _list->getSelected();
+ switch (cmd) {
+ case GUI::kListItemActivatedCmd:
+ case GUI::kListItemDoubleClickedCmd:
+ if (selItem >= 0) {
+ if (_saveMode || !getResultString().isEmpty()) {
+ _list->endEditMode();
+ setResult(selItem);
+ close();
+ }
+ }
+ break;
+ case kChooseCmd:
+ _list->endEditMode();
+ setResult(selItem);
+ close();
+ break;
+ case GUI::kListSelectionChangedCmd: {
+ Graphics::Surface *thumb;
+ thumb = _scumm->loadThumbnailFromSlot(_saveMode ? selItem + 1 : selItem);
+ _gfxWidget->setGfx(thumb);
+ if (thumb)
+ thumb->free();
+ delete thumb;
+ _gfxWidget->draw();
+
+ InfoStuff infos;
+ memset(&infos, 0, sizeof(InfoStuff));
+ char buffer[32];
+ if (_scumm->loadInfosFromSlot(_saveMode ? selItem + 1 : selItem, &infos)) {
+ snprintf(buffer, 32, "Date: %.2d.%.2d.%.4d",
+ (infos.date >> 24) & 0xFF, (infos.date >> 16) & 0xFF,
+ infos.date & 0xFFFF);
+ _date->setLabel(buffer);
+ _date->draw();
+
+ snprintf(buffer, 32, "Time: %.2d:%.2d",
+ (infos.time >> 8) & 0xFF, infos.time & 0xFF);
+ _time->setLabel(buffer);
+ _time->draw();
+
+ int minutes = infos.playtime / 60;
+ int hours = minutes / 60;
+ minutes %= 60;
+
+ snprintf(buffer, 32, "Playtime: %.2d:%.2d",
+ hours & 0xFF, minutes & 0xFF);
+ _playtime->setLabel(buffer);
+ _playtime->draw();
+ } else {
+ snprintf(buffer, 32, "No date saved");
+ _date->setLabel(buffer);
+ _date->draw();
+
+ snprintf(buffer, 32, "No time saved");
+ _time->setLabel(buffer);
+ _time->draw();
+
+ snprintf(buffer, 32, "No playtime saved");
+ _playtime->setLabel(buffer);
+ _playtime->draw();
+ }
+
+ if (_saveMode) {
+ _list->startEditMode();
+ }
+ // Disable button if nothing is selected, or (in load mode) if an empty
+ // list item is selected. We allow choosing an empty item in save mode
+ // because we then just assign a default name.
+ _chooseButton->setEnabled(selItem >= 0 && (_saveMode || !getResultString().isEmpty()));
+ _chooseButton->draw();
+ } break;
+ case kCloseCmd:
+ setResult(-1);
+ default:
+ Dialog::handleCommand(sender, cmd, data);
+ }
+}
+
+#pragma mark -
+
+Common::StringList generateSavegameList(ScummEngine *scumm, bool saveMode) {
+ // Get savegame names
+ Common::StringList l;
+ char name[32];
+ uint i = saveMode ? 1 : 0;
+ bool avail_saves[81];
+
+ scumm->listSavegames(avail_saves, ARRAYSIZE(avail_saves));
+ for (; i < ARRAYSIZE(avail_saves); i++) {
+ if (avail_saves[i])
+ scumm->getSavegameName(i, name);
+ else
+ name[0] = 0;
+ l.push_back(name);
+ }
+
+ return l;
+}
+
+#define addBigButton(label, cmd, hotkey) \
+ new GUI::ButtonWidget(this, hOffset, y, buttonWidth, buttonHeight, label, cmd, hotkey, ws); \
+ y += (buttonHeight + vAddOff)
+
+MainMenuDialog::MainMenuDialog(ScummEngine *scumm)
+ : ScummDialog(scumm, 0, 0, 0, 0) {
+
+ const int screenW = g_system->getOverlayWidth();
+ const int screenH = g_system->getOverlayHeight();
+
+ int hOffset;
+ int vSpace;
+ int vAddOff;
+
+ GUI::WidgetSize ws;
+ int buttonWidth;
+ int buttonHeight;
+
+ if (screenW >= 400 && screenH >= 300) {
+ buttonWidth = 160;
+ buttonHeight = 28;
+ ws = GUI::kBigWidgetSize;
+ hOffset = 12;
+ vSpace = 7;
+ vAddOff = 3;
+ } else {
+ buttonWidth = 90;
+ buttonHeight = 16;
+ ws = GUI::kNormalWidgetSize;
+ hOffset = 8;
+ vSpace = 5;
+ vAddOff = 2;
+ }
+
+ int y = vSpace + vAddOff;
+
+
+ addBigButton("Resume", kPlayCmd, 'P');
+ y += vSpace;
+
+ addBigButton("Load", kLoadCmd, 'L');
+ addBigButton("Save", kSaveCmd, 'S');
+ y += vSpace;
+
+ addBigButton("Options", kOptionsCmd, 'O');
+#ifndef DISABLE_HELP
+ addBigButton("Help", kHelpCmd, 'H');
+#endif
+ addBigButton("About", kAboutCmd, 'A');
+ y += vSpace;
+
+ addBigButton("Quit", kQuitCmd, 'Q');
+
+
+ _w = buttonWidth + 2 * hOffset;
+ _h = y + vSpace;
+
+ _x = (screenW - _w) / 2;
+ _y = (screenH - _h) / 2;
+
+
+ //
+ // Create the sub dialog(s)
+ //
+ _aboutDialog = new GUI::AboutDialog();
+ _optionsDialog = new ConfigDialog(scumm);
+#ifndef DISABLE_HELP
+ _helpDialog = new HelpDialog(scumm);
+#endif
+ if (scumm->_system->getOverlayWidth() <= 320) {
+ _saveDialog = new SaveLoadChooser("Save game:", "Save", true);
+ _loadDialog = new SaveLoadChooser("Load game:", "Load", false);
+ } else {
+ _saveDialog = new SaveLoadChooserEx("Save game:", "Save", true, scumm);
+ _loadDialog = new SaveLoadChooserEx("Load game:", "Load", false, scumm);
+ }
+}
+
+MainMenuDialog::~MainMenuDialog() {
+ delete _aboutDialog;
+ delete _optionsDialog;
+#ifndef DISABLE_HELP
+ delete _helpDialog;
+#endif
+ delete _saveDialog;
+ delete _loadDialog;
+}
+
+void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+ switch (cmd) {
+ case kSaveCmd:
+ save();
+ break;
+ case kLoadCmd:
+ load();
+ break;
+ case kPlayCmd:
+ close();
+ break;
+ case kOptionsCmd:
+ _optionsDialog->runModal();
+ break;
+ case kAboutCmd:
+ _aboutDialog->runModal();
+ break;
+#ifndef DISABLE_HELP
+ case kHelpCmd:
+ _helpDialog->runModal();
+ break;
+#endif
+ case kQuitCmd:
+ _vm->_quit = true;
+ close();
+ break;
+ default:
+ ScummDialog::handleCommand(sender, cmd, data);
+ }
+}
+
+void MainMenuDialog::save() {
+ int idx;
+ _saveDialog->setList(generateSavegameList(_vm, true));
+ idx = _saveDialog->runModal();
+ if (idx >= 0) {
+ const String &result = _saveDialog->getResultString();
+ char buffer[20];
+ const char *str;
+ if (result.isEmpty()) {
+ // If the user was lazy and entered no save name, come up with a default name.
+ sprintf(buffer, "Save %d", idx + 1);
+ str = buffer;
+ } else
+ str = result.c_str();
+ _vm->requestSave(idx + 1, str);
+ close();
+ }
+}
+
+void MainMenuDialog::load() {
+ int idx;
+ _loadDialog->setList(generateSavegameList(_vm, false));
+ idx = _loadDialog->runModal();
+ if (idx >= 0) {
+ _vm->requestLoad(idx);
+ close();
+ }
+}
+
+#pragma mark -
+
+enum {
+ kOKCmd = 'ok '
+};
+
+enum {
+ kKeysCmd = 'KEYS'
+};
+
+ConfigDialog::ConfigDialog(ScummEngine *scumm)
+ : GUI::OptionsDialog("", 40, 30, 240, 124), _vm(scumm) {
+
+ const int screenW = g_system->getOverlayWidth();
+ const int screenH = g_system->getOverlayHeight();
+
+ _w = screenW - 2 * 40;
+
+ GUI::WidgetSize ws;
+ int buttonWidth;
+ int buttonHeight;
+
+ if (screenW >= 400 && screenH >= 300) {
+ ws = GUI::kBigWidgetSize;
+ buttonWidth = kBigButtonWidth;
+ buttonHeight = kBigButtonHeight;
+ } else {
+ ws = GUI::kNormalWidgetSize;
+ buttonWidth = kButtonWidth;
+ buttonHeight = kButtonHeight;
+ }
+
+ int yoffset = 8;
+
+ //
+ // Sound controllers
+ //
+
+ yoffset = addVolumeControls(this, yoffset, ws) + 4;
+
+ //
+ // Some misc options
+ //
+
+ _subtitlesCheckbox = addCheckbox(this, 15, yoffset, "Show subtitles", 0, 'S', ws);
+ yoffset += _subtitlesCheckbox->getHeight();
+
+ _speechCheckbox = addCheckbox(this, 15, yoffset, "Enable speech", 0, 'E', ws);
+ yoffset += _speechCheckbox->getHeight() + 4;
+
+ //
+ // Add the buttons
+ //
+
+ _w = 8 + 3 * (buttonWidth + 4); // FIXME/TODO
+
+ addButton(this, _w - (buttonWidth + 4) - 4, yoffset, "OK", GUI::OptionsDialog::kOKCmd, 'O', ws);
+ addButton(this, _w - 2 * (buttonWidth + 4) - 4, yoffset, "Cancel", kCloseCmd, 'C', ws);
+#ifdef SMALL_SCREEN_DEVICE
+ addButton(this, _w - 3 * (buttonWidth + 4) - 4, yoffset, "Keys", kKeysCmd, 'K', ws);
+#endif
+
+ yoffset += buttonHeight;
+
+ _h = yoffset + 8;
+
+ _x = (screenW - _w) / 2;
+ _y = (screenH - _h) / 2;
+
+#ifdef SMALL_SCREEN_DEVICE
+ //
+ // Create the sub dialog(s)
+ //
+
+ _keysDialog = new GUI::KeysDialog();
+#endif
+}
+
+ConfigDialog::~ConfigDialog() {
+#ifdef SMALL_SCREEN_DEVICE
+ delete _keysDialog;
+#endif
+}
+
+void ConfigDialog::open() {
+ GUI_OptionsDialog::open();
+
+ // update checkboxes, too
+ _subtitlesCheckbox->setState(ConfMan.getBool("subtitles"));
+ _speechCheckbox->setState(!ConfMan.getBool("speech_mute"));
+}
+
+void ConfigDialog::close() {
+ if (getResult()) {
+ // Subtitles
+ ConfMan.set("subtitles", _subtitlesCheckbox->getState(), _domain);
+ ConfMan.set("speech_mute", !_speechCheckbox->getState(), _domain);
+ // Sync with current setting
+ if (ConfMan.getBool("speech_mute"))
+ _vm->_voiceMode = 2;
+ else
+ _vm->_voiceMode = ConfMan.getBool("subtitles");
+
+ if (_vm->_version >= 7)
+ _vm->VAR(_vm->VAR_VOICE_MODE) = _vm->_voiceMode;
+ }
+
+ GUI_OptionsDialog::close();
+
+ _vm->setupVolumes();
+}
+
+void ConfigDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+ switch (cmd) {
+ case kKeysCmd:
+#ifdef SMALL_SCREEN_DEVICE
+ _keysDialog->runModal();
+#endif
+ break;
+ default:
+ GUI_OptionsDialog::handleCommand (sender, cmd, data);
+ }
+}
+
+#ifndef DISABLE_HELP
+
+#pragma mark -
+
+enum {
+ kNextCmd = 'NEXT',
+ kPrevCmd = 'PREV'
+};
+
+HelpDialog::HelpDialog(ScummEngine *scumm)
+ : ScummDialog(scumm, 5, 5, 310, 190) {
+ _drawingHints &= ~GUI::THEME_HINT_SPECIAL_COLOR;
+
+ const int screenW = g_system->getOverlayWidth();
+ const int screenH = g_system->getOverlayHeight();
+
+ GUI::WidgetSize ws;
+ int buttonHeight;
+ int buttonWidth;
+
+ if (screenW >= 400 && screenH >= 300) {
+ ws = GUI::kBigWidgetSize;
+ buttonHeight = kBigButtonHeight;
+ buttonWidth = kBigButtonWidth;
+ _w = 370;
+ _x = (screenW - _w) / 2;
+ } else {
+ ws = GUI::kNormalWidgetSize;
+ buttonHeight = kButtonHeight;
+ buttonWidth = kButtonWidth;
+ _x = 5;
+ _w = screenW - 2 * 5;
+ }
+
+ int lineHeight = g_gui.getFontHeight();
+
+ _h = 5 + (2 + HELP_NUM_LINES) * lineHeight + buttonHeight + 7;
+ _y = (screenH - _h) / 2;
+
+ _title = new StaticTextWidget(this, 10, 5, _w, lineHeight, "", kTextAlignCenter, ws);
+
+ for (int i = 0; i < HELP_NUM_LINES; i++) {
+ _key[i] = new StaticTextWidget(this, 10, 5 + lineHeight * (i + 2), 80, lineHeight, "", kTextAlignLeft, ws);
+ _dsc[i] = new StaticTextWidget(this, 90, 5 + lineHeight * (i + 2), _w - 10 - 90, lineHeight, "", kTextAlignLeft, ws);
+ }
+
+ _page = 1;
+ _numPages = ScummHelp::numPages(scumm->_gameId);
+
+ int y = 5 + lineHeight * (HELP_NUM_LINES + 2) + 2;
+
+ _prevButton = addButton(this, 10, y, "Previous", kPrevCmd, 'P', ws);
+ _nextButton = addButton(this, 10 + buttonWidth + 8, y, "Next", kNextCmd, 'N', ws);
+ addButton(this, _w - 8 - buttonWidth, y, "Close", kCloseCmd, 'C', ws);
+ _prevButton->clearFlags(WIDGET_ENABLED);
+
+ displayKeyBindings();
+}
+
+void HelpDialog::displayKeyBindings() {
+
+ String titleStr, *keyStr, *dscStr;
+
+ ScummHelp::updateStrings(_vm->_gameId, _vm->_version, _vm->_platform, _page, titleStr, keyStr, dscStr);
+
+ _title->setLabel(titleStr);
+ for (int i = 0; i < HELP_NUM_LINES; i++) {
+ _key[i]->setLabel(keyStr[i]);
+ _dsc[i]->setLabel(dscStr[i]);
+ }
+
+ delete [] keyStr;
+ delete [] dscStr;
+}
+
+void HelpDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+
+ switch (cmd) {
+ case kNextCmd:
+ _page++;
+ if (_page >= _numPages) {
+ _nextButton->clearFlags(WIDGET_ENABLED);
+ }
+ if (_page >= 2) {
+ _prevButton->setFlags(WIDGET_ENABLED);
+ }
+ displayKeyBindings();
+ draw();
+ break;
+ case kPrevCmd:
+ _page--;
+ if (_page <= _numPages) {
+ _nextButton->setFlags(WIDGET_ENABLED);
+ }
+ if (_page <= 1) {
+ _prevButton->clearFlags(WIDGET_ENABLED);
+ }
+ displayKeyBindings();
+ draw();
+ break;
+ default:
+ ScummDialog::handleCommand(sender, cmd, data);
+ }
+}
+
+#endif
+
+#pragma mark -
+
+InfoDialog::InfoDialog(ScummEngine *scumm, int res)
+: ScummDialog(scumm, 0, 80, 0, 16) { // dummy x and w
+ setInfoText(queryResString (res));
+}
+
+InfoDialog::InfoDialog(ScummEngine *scumm, const String& message)
+: ScummDialog(scumm, 0, 80, 0, 16) { // dummy x and w
+ setInfoText(message);
+}
+
+void InfoDialog::setInfoText(const String& message) {
+ const int screenW = g_system->getOverlayWidth();
+ const int screenH = g_system->getOverlayHeight();
+ GUI::WidgetSize ws;
+
+ if (screenW >= 400 && screenH >= 300) {
+ ws = GUI::kBigWidgetSize;
+ } else {
+ ws = GUI::kNormalWidgetSize;
+ }
+
+ int width = g_gui.getStringWidth(message) + 16;
+ int height = g_gui.getFontHeight() + 8;
+
+ _w = width;
+ _h = height;
+ _x = (screenW - width) / 2;
+ _y = (screenH - height) / 2;
+
+ new StaticTextWidget(this, 4, 4, _w - 8, _h, message, kTextAlignCenter, ws);
+}
+
+#pragma mark -
+
+PauseDialog::PauseDialog(ScummEngine *scumm, int res)
+ : InfoDialog(scumm, res) {
+}
+
+void PauseDialog::handleKeyDown(uint16 ascii, int keycode, int modifiers) {
+ if (ascii == ' ') // Close pause dialog if space key is pressed
+ close();
+ else
+ ScummDialog::handleKeyDown(ascii, keycode, modifiers);
+}
+
+ConfirmDialog::ConfirmDialog(ScummEngine *scumm, const String& message)
+ : InfoDialog(scumm, message) {
+}
+
+void ConfirmDialog::handleKeyDown(uint16 ascii, int keycode, int modifiers) {
+ if (tolower(ascii) == 'n') {
+ setResult(0);
+ close();
+ } else if (tolower(ascii) == 'y') {
+ setResult(1);
+ close();
+ } else
+ ScummDialog::handleKeyDown(ascii, keycode, modifiers);
+}
+
+#pragma mark -
+
+ValueDisplayDialog::ValueDisplayDialog(const Common::String& label, int minVal, int maxVal, int val, uint16 incKey, uint16 decKey)
+ : GUI::Dialog(0, 80, 0, 16), _label(label), _min(minVal), _max(maxVal), _value(val), _incKey(incKey), _decKey(decKey) {
+ assert(_min <= _value && _value <= _max);
+
+ const int screenW = g_system->getOverlayWidth();
+ const int screenH = g_system->getOverlayHeight();
+
+ if (screenW >= 400 && screenH >= 300) {
+ _percentBarWidth = kBigPercentBarWidth;
+ } else {
+ _percentBarWidth = kPercentBarWidth;
+ }
+
+ int width = g_gui.getStringWidth(label) + 16 + _percentBarWidth;
+ int height = g_gui.getFontHeight() + 4 * 2;
+
+ _x = (screenW - width) / 2;
+ _y = (screenH - height) / 2;
+ _w = width;
+ _h = height;
+}
+
+void ValueDisplayDialog::drawDialog() {
+ const int labelWidth = _w - 8 - _percentBarWidth;
+ g_gui.theme()->drawDialogBackground(Common::Rect(_x, _y, _x+_w, _y+_h), GUI::THEME_HINT_SAVE_BACKGROUND | GUI::THEME_HINT_FIRST_DRAW);
+ g_gui.theme()->drawText(Common::Rect(_x+4, _y+4, _x+labelWidth+4, _y+g_gui.theme()->getFontHeight()+4), _label);
+ g_gui.theme()->drawSlider(Common::Rect(_x+4+labelWidth, _y+4, _x+_w-4, _y+_h-4), _percentBarWidth * (_value - _min) / (_max - _min));
+}
+
+void ValueDisplayDialog::handleTickle() {
+ if (getMillis() > _timer)
+ close();
+}
+
+void ValueDisplayDialog::handleKeyDown(uint16 ascii, int keycode, int modifiers) {
+ if (ascii == _incKey || ascii == _decKey) {
+ if (ascii == _incKey && _value < _max)
+ _value++;
+ else if (ascii == _decKey && _value > _min)
+ _value--;
+
+ setResult(_value);
+ _timer = getMillis() + kDisplayDelay;
+ draw();
+ } else {
+ close();
+ }
+}
+
+void ValueDisplayDialog::open() {
+ GUI_Dialog::open();
+ setResult(_value);
+ _timer = getMillis() + kDisplayDelay;
+}
+
+
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Dialogs)
+_GSETPTR(Scumm::string_map_table_v7, GBVARS_STRINGMAPTABLEV7_INDEX, Scumm::ResString, GBVARS_SCUMM)
+_GSETPTR(Scumm::string_map_table_v6, GBVARS_STRINGMAPTABLEV6_INDEX, Scumm::ResString, GBVARS_SCUMM)
+_GSETPTR(Scumm::string_map_table_v5, GBVARS_STRINGMAPTABLEV5_INDEX, Scumm::ResString, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(Dialogs)
+_GRELEASEPTR(GBVARS_STRINGMAPTABLEV7_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_STRINGMAPTABLEV6_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_STRINGMAPTABLEV5_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h
new file mode 100644
index 0000000000..60d796956e
--- /dev/null
+++ b/engines/scumm/dialogs.h
@@ -0,0 +1,211 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef SCUMM_DIALOGS_H
+#define SCUMM_DIALOGS_H
+
+#include "common/str.h"
+#include "gui/dialog.h"
+#include "gui/options.h"
+#include "gui/widget.h"
+
+#ifndef DISABLE_HELP
+#include "scumm/help.h"
+#endif
+
+namespace GUI {
+ class ListWidget;
+}
+
+
+namespace Scumm {
+
+class ScummEngine;
+
+class ScummDialog : public GUI::Dialog {
+public:
+ ScummDialog(ScummEngine *scumm, int x, int y, int w, int h);
+
+protected:
+ typedef Common::String String;
+
+ ScummEngine *_vm;
+
+ // Query a string from the resources
+ const String queryResString(int stringno);
+};
+
+// to have a base for all different Save/Load Choosers
+// currently only for SaveLoadChooser (320x200)
+// and for SaveLoadChooserEx (640x400/640x480)
+class BaseSaveLoadChooser
+{
+public:
+ virtual ~BaseSaveLoadChooser() {};
+
+ virtual const Common::String &getResultString() const = 0;
+ virtual void setList(const Common::StringList& list) = 0;
+ virtual int runModal() = 0;
+};
+
+class MainMenuDialog : public ScummDialog {
+public:
+ MainMenuDialog(ScummEngine *scumm);
+ ~MainMenuDialog();
+ virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+
+protected:
+ GUI::Dialog *_aboutDialog;
+ GUI::Dialog *_optionsDialog;
+#ifndef DISABLE_HELP
+ GUI::Dialog *_helpDialog;
+#endif
+ BaseSaveLoadChooser *_saveDialog;
+ BaseSaveLoadChooser *_loadDialog;
+
+ void save();
+ void load();
+};
+
+#ifndef DISABLE_HELP
+
+class HelpDialog : public ScummDialog {
+public:
+ HelpDialog(ScummEngine *scumm);
+ virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+
+protected:
+ typedef Common::String String;
+
+ GUI::ButtonWidget *_nextButton;
+ GUI::ButtonWidget *_prevButton;
+
+ GUI::StaticTextWidget *_title;
+ GUI::StaticTextWidget *_key[HELP_NUM_LINES];
+ GUI::StaticTextWidget *_dsc[HELP_NUM_LINES];
+
+ int _page;
+ int _numPages;
+
+ void displayKeyBindings();
+};
+
+#endif
+
+class ConfigDialog : public GUI::OptionsDialog {
+protected:
+ ScummEngine *_vm;
+#ifdef SMALL_SCREEN_DEVICE
+ GUI::Dialog *_keysDialog;
+#endif
+
+public:
+ ConfigDialog(ScummEngine *scumm);
+ ~ConfigDialog();
+
+ virtual void open();
+ virtual void close();
+ virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
+
+protected:
+ GUI::CheckboxWidget *_subtitlesCheckbox;
+ GUI::CheckboxWidget *_speechCheckbox;
+};
+
+/**
+ * A dialog which displays an arbitrary message to the user and returns
+ * ther users reply as its result value. More specifically, it returns
+ * the ASCII code of the key used to close the dialog (0 if a mouse
+ * click closed the dialog).
+ */
+class InfoDialog : public ScummDialog {
+public:
+ // arbitrary message
+ InfoDialog(ScummEngine *scumm, const String& message);
+ // from resources
+ InfoDialog(ScummEngine *scumm, int res);
+
+ virtual void handleMouseDown(int x, int y, int button, int clickCount) {
+ setResult(0);
+ close();
+ }
+ virtual void handleKeyDown(uint16 ascii, int keycode, int modifiers) {
+ setResult(ascii);
+ close();
+ }
+
+protected:
+ void setInfoText (const String& message);
+};
+
+/**
+ * The pause dialog, visible whenever the user activates pause mode. Goes
+ * away uon any key or mouse button press.
+ */
+class PauseDialog : public InfoDialog {
+public:
+ PauseDialog(ScummEngine *scumm, int res);
+ virtual void handleKeyDown(uint16 ascii, int keycode, int modifiers);
+};
+
+/**
+ * A simple yes/no dialog, used to ask the user whether to really
+ * quit/restart ScummVM.
+ */
+class ConfirmDialog : public InfoDialog {
+public:
+ ConfirmDialog(ScummEngine *scumm, const String& message);
+ virtual void handleKeyDown(uint16 ascii, int keycode, int modifiers);
+};
+
+/**
+ * A dialog used to display the music volume / text speed.
+ * Automatically closes after a brief time passed.
+ */
+class ValueDisplayDialog : public GUI::Dialog {
+public:
+ ValueDisplayDialog(const Common::String& label, int minVal, int maxVal, int val, uint16 incKey, uint16 decKey);
+
+ virtual void open();
+ virtual void drawDialog();
+ virtual void handleTickle();
+ virtual void handleMouseDown(int x, int y, int button, int clickCount) {
+ close();
+ }
+ virtual void handleKeyDown(uint16 ascii, int keycode, int modifiers);
+
+protected:
+ enum {
+ kPercentBarWidth = 50,
+ kBigPercentBarWidth = 75,
+ kDisplayDelay = 1500
+ };
+ Common::String _label;
+ const int _min, _max;
+ const uint16 _incKey, _decKey;
+ int _percentBarWidth;
+ int _value;
+ uint32 _timer;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/floodfill_he.cpp b/engines/scumm/floodfill_he.cpp
new file mode 100644
index 0000000000..c7df765606
--- /dev/null
+++ b/engines/scumm/floodfill_he.cpp
@@ -0,0 +1,293 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/floodfill_he.h"
+#include "scumm/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+static bool floodFillPixelCheck(int x, int y, const FloodFillState *ffs) {
+ int diffColor = ffs->color1 - ffs->color2;
+ if (x >= 0 && x < ffs->dst_w && y >= 0 && y < ffs->dst_h) {
+ uint8 color = *(ffs->dst + y * ffs->dst_w + x);
+ diffColor = color - ffs->color1;
+ }
+ return diffColor == 0;
+}
+
+static void floodFillProcessRect(FloodFillState *ffs, const Common::Rect *r) {
+ Common::Rect *dr = &ffs->dstBox;
+ if (dr->right >= dr->left && dr->top <= dr->bottom) {
+ int rw = r->right - r->left + 1;
+ int rh = r->bottom - r->top + 1;
+ assert(r->top + rh <= ffs->dst_h);
+ assert(r->left + rw <= ffs->dst_w);
+ uint8 *dst = ffs->dst + r->top * ffs->dst_w + r->left;
+ if (rw <= 1) {
+ --rh;
+ while (rh >= 0) {
+ *dst = ffs->color2;
+ dst += ffs->dst_w;
+ --rh;
+ }
+ } else {
+ --rh;
+ while (rh >= 0) {
+ memset(dst, ffs->color2, rw);
+ dst += ffs->dst_w;
+ --rh;
+ }
+ }
+ dr->extend(*r);
+ } else {
+ *dr = *r;
+ }
+}
+
+static void floodFillAddLine(FloodFillLine **ffl, int y, int x1, int x2, int dy) {
+ (*ffl)->y = y;
+ (*ffl)->x1 = x1;
+ (*ffl)->x2 = x2;
+ (*ffl)->inc = dy;
+ (*ffl)++;
+}
+
+static void floodFillProcess(int x, int y, FloodFillState *ffs, FloodFillPixelCheckCallback pixelCheckCallback) {
+ ffs->dstBox.left = ffs->dstBox.top = 12345;
+ ffs->dstBox.right = ffs->dstBox.bottom = -12345;
+
+ FloodFillLine **fillLineCur = &ffs->fillLineTableCur;
+ FloodFillLine **fillLineEnd = &ffs->fillLineTableEnd;
+
+ assert(*fillLineCur < *fillLineEnd);
+ if (ffs->srcBox.top <= y + 1 && ffs->srcBox.bottom >= y + 1) {
+ (*fillLineCur)->y = y;
+ (*fillLineCur)->x1 = x;
+ (*fillLineCur)->x2 = x;
+ (*fillLineCur)->inc = 1;
+ (*fillLineCur)++;
+ }
+
+ assert(*fillLineCur < *fillLineEnd);
+ if (ffs->srcBox.top <= y && ffs->srcBox.bottom >= y) {
+ (*fillLineCur)->y = y + 1;
+ (*fillLineCur)->x1 = x;
+ (*fillLineCur)->x2 = x;
+ (*fillLineCur)->inc = -1;
+ (*fillLineCur)++;
+ }
+
+ assert(ffs->fillLineTable <= *fillLineCur);
+ FloodFillLine **fillLineStart = fillLineCur;
+
+ while (ffs->fillLineTable < *fillLineStart) {
+ Common::Rect r;
+ int x_start;
+ FloodFillLine *fflCur = --(*fillLineCur);
+ int dy = fflCur->inc;
+ int x_end = fflCur->x2;
+ int x1 = fflCur->x1;
+ int x2 = fflCur->x1 + 1;
+ r.bottom = r.top = y = fflCur->y + fflCur->inc;
+ r.left = x2;
+ r.right = x1;
+ x = x1;
+ while (ffs->srcBox.left <= x) {
+ if (!(*pixelCheckCallback)(x, y, ffs)) {
+ break;
+ }
+ r.left = x;
+ --x;
+ }
+ if (r.right >= r.left && r.top <= r.bottom) {
+ floodFillProcessRect(ffs, &r);
+ }
+ if (x >= x1) goto skip;
+ x_start = x + 1;
+ if (x1 > x_start) {
+ assert(*fillLineEnd > *fillLineCur);
+ if (ffs->srcBox.top <= y - dy && ffs->srcBox.bottom >= y - dy) {
+ --x1;
+ floodFillAddLine(fillLineCur, y, x_start, x1, -dy);
+ }
+ }
+ x = x2;
+ while (x_start <= x_end) {
+ r.left = x;
+ r.top = y;
+ r.right = x - 1;
+ r.bottom = y;
+ while (ffs->srcBox.right >= x) {
+ if (!(*pixelCheckCallback)(x, y, ffs)) {
+ break;
+ }
+ r.right = x;
+ ++x;
+ }
+ if (r.right >= r.left && r.top <= r.bottom) {
+ floodFillProcessRect(ffs, &r);
+ }
+ assert(ffs->fillLineTableCur < ffs->fillLineTableEnd);
+ if (ffs->srcBox.top <= y + dy && ffs->srcBox.bottom >= y + dy) {
+ floodFillAddLine(&ffs->fillLineTableCur, y, x_start, x - 1, dy);
+ }
+ x_start = x_end + 1;
+ if (x > x_start) {
+ assert(ffs->fillLineTableCur < ffs->fillLineTableEnd);
+ if (ffs->srcBox.top <= y - dy && ffs->srcBox.bottom >= y - dy) {
+ floodFillAddLine(&ffs->fillLineTableCur, y, x_start, x - 1, -dy);
+ }
+ }
+skip:
+ ++x;
+ while (x <= x_end) {
+ if ((*pixelCheckCallback)(x, y, ffs)) {
+ break;
+ }
+ ++x;
+ }
+ x_start = x;
+ }
+ }
+}
+
+void floodFill(FloodFillParameters *ffp, ScummEngine_v90he *vm) {
+ uint8 *dst;
+ VirtScreen *vs = &vm->virtscr[kMainVirtScreen];
+ if (ffp->flags & 0x8000) {
+ dst = vs->getBackPixels(0, vs->topline);
+ } else {
+ dst = vs->getPixels(0, vs->topline);
+ }
+ uint8 color = ffp->flags & 0xFF;
+
+ Common::Rect r;
+ r.left = r.top = 12345;
+ r.right = r.bottom = -12345;
+
+ FloodFillState *ffs = new FloodFillState;
+ ffs->fillLineTableCount = vs->h * 2;
+ ffs->fillLineTable = new FloodFillLine[ffs->fillLineTableCount];
+ ffs->color2 = color;
+ ffs->dst = dst;
+ ffs->dst_w = vs->w;
+ ffs->dst_h = vs->h;
+ ffs->srcBox = ffp->box;
+ ffs->fillLineTableCur = &ffs->fillLineTable[0];
+ ffs->fillLineTableEnd = &ffs->fillLineTable[ffs->fillLineTableCount];
+
+ if (ffp->x < 0 || ffp->y < 0 || ffp->x >= vs->w || ffp->y >= vs->h) {
+ ffs->color1 = color;
+ } else {
+ ffs->color1 = *(dst + ffp->y * vs->w + ffp->x);
+ }
+
+ debug(5, "floodFill() x=%d y=%d color1=%d ffp->flags=0x%X", ffp->x, ffp->y, ffs->color1, ffp->flags);
+ if (ffs->color1 != color) {
+ floodFillProcess(ffp->x, ffp->y, ffs, floodFillPixelCheck);
+ r = ffs->dstBox;
+ }
+ r.debugPrint(5, "floodFill() dirty_rect");
+
+ delete[] ffs->fillLineTable;
+ delete ffs;
+
+ vm->VAR(119) = 1;
+
+ if (r.left <= r.right && r.top <= r.bottom) {
+ if (ffp->flags & 0x8000) {
+ vm->gdi.copyVirtScreenBuffers(r);
+ } else {
+ ++r.bottom;
+ vm->markRectAsDirty(kMainVirtScreen, r);
+ }
+ }
+}
+
+void Wiz::fillWizFlood(const WizParameters *params) {
+ if (params->processFlags & kWPFClipBox2) {
+ int px = params->box2.left;
+ int py = params->box2.top;
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, params->img.resNum);
+ if (dataPtr) {
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ assert(c == 0);
+ Common::Rect imageRect(w, h);
+ if (params->processFlags & kWPFClipBox) {
+ if (!imageRect.intersects(params->box)) {
+ return;
+ }
+ imageRect.clip(params->box);
+ }
+ uint8 color = _vm->VAR(93);
+ if (params->processFlags & kWPFFillColor) {
+ color = params->fillColor;
+ }
+ if (imageRect.contains(px, py)) {
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+
+ FloodFillState *ffs = new FloodFillState;
+ ffs->fillLineTableCount = h * 2;
+ ffs->fillLineTable = new FloodFillLine[ffs->fillLineTableCount];
+ ffs->color2 = color;
+ ffs->dst = wizd;
+ ffs->dst_w = w;
+ ffs->dst_h = h;
+ ffs->srcBox = imageRect;
+ ffs->fillLineTableCur = &ffs->fillLineTable[0];
+ ffs->fillLineTableEnd = &ffs->fillLineTable[ffs->fillLineTableCount];
+
+ if (px < 0 || py < 0 || px >= w || py >= h) {
+ ffs->color1 = color;
+ } else {
+ ffs->color1 = *(wizd + py * w + px);
+ }
+
+ debug(0, "floodFill() x=%d y=%d color1=%d", px, py, ffs->color1);
+
+ if (ffs->color1 != color) {
+ floodFillProcess(px, py, ffs, floodFillPixelCheck);
+ }
+
+ delete[] ffs->fillLineTable;
+ delete ffs;
+ }
+ }
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/floodfill_he.h b/engines/scumm/floodfill_he.h
new file mode 100644
index 0000000000..7d0fa7073f
--- /dev/null
+++ b/engines/scumm/floodfill_he.h
@@ -0,0 +1,67 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(FLOODFILL_HE_H) && !defined(DISABLE_HE)
+#define FLOODFILL_HE_H
+
+#include "common/rect.h"
+
+namespace Scumm {
+
+struct FloodFillParameters {
+ Common::Rect box;
+ int32 x;
+ int32 y;
+ int32 flags;
+};
+
+struct FloodFillLine {
+ int y;
+ int x1;
+ int x2;
+ int inc;
+};
+
+struct FloodFillState {
+ FloodFillLine *fillLineTable;
+ FloodFillLine *fillLineTableEnd;
+ FloodFillLine *fillLineTableCur;
+ Common::Rect dstBox;
+ Common::Rect srcBox;
+ uint8 *dst;
+ int dst_w;
+ int dst_h;
+ int color1;
+ int color2;
+ int fillLineTableCount;
+};
+
+class ScummEngine_v90he;
+
+typedef bool (*FloodFillPixelCheckCallback)(int x, int y, const FloodFillState *ffs);
+
+void floodFill(FloodFillParameters *ffp, ScummEngine_v90he *vm);
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
new file mode 100644
index 0000000000..dc476a8f0b
--- /dev/null
+++ b/engines/scumm/gfx.cpp
@@ -0,0 +1,3307 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "scumm/scumm.h"
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern.h"
+#ifndef DISABLE_HE
+#include "scumm/intern_he.h"
+#endif
+#include "scumm/resource.h"
+#include "scumm/usage_bits.h"
+#include "scumm/wiz_he.h"
+
+namespace Scumm {
+
+static void blit(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h);
+static void fill(byte *dst, int dstPitch, byte color, int w, int h);
+
+static void copy8Col(byte *dst, int dstPitch, const byte *src, int height);
+static void clear8Col(byte *dst, int dstPitch, int height);
+
+
+struct StripTable {
+ int offsets[160];
+ int run[160];
+ int color[160];
+ int zoffsets[120]; // FIXME: Why only 120 here?
+ int zrun[120]; // FIXME: Why only 120 here?
+};
+
+enum {
+ kScrolltime = 500, // ms scrolling is supposed to take
+ kPictureDelay = 20
+};
+
+#define NUM_SHAKE_POSITIONS 8
+static const int8 shake_positions[NUM_SHAKE_POSITIONS] = {
+ 0, 1 * 2, 2 * 2, 1 * 2, 0 * 2, 2 * 2, 3 * 2, 1 * 2
+};
+
+/**
+ * The following structs define four basic fades/transitions used by
+ * transitionEffect(), each looking differently to the user.
+ * Note that the stripTables contain strip numbers, and they assume
+ * that the screen has 40 vertical strips (i.e. 320 pixel), and 25 horizontal
+ * strips (i.e. 200 pixel). There is a hack in transitionEffect that
+ * makes it work correctly in games which have a different screen height
+ * (for example, 240 pixel), but nothing is done regarding the width, so this
+ * code won't work correctly in COMI. Also, the number of iteration depends
+ * on min(vertStrips, horizStrips}. So the 13 is derived from 25/2, rounded up.
+ * And the 25 = min(25,40). Hence for Zak256 instead of 13 and 25, the values
+ * 15 and 30 should be used, and for COMI probably 30 and 60.
+ */
+struct TransitionEffect {
+ byte numOfIterations;
+ int8 deltaTable[16]; // four times l / t / r / b
+ byte stripTable[16]; // ditto
+};
+
+#ifdef PALMOS_68K
+static const TransitionEffect *transitionEffects;
+#else
+static const TransitionEffect transitionEffects[6] = {
+ // Iris effect (looks like an opening/closing camera iris)
+ {
+ 13, // Number of iterations
+ {
+ 1, 1, -1, 1,
+ -1, 1, -1, -1,
+ 1, -1, -1, -1,
+ 1, 1, 1, -1
+ },
+ {
+ 0, 0, 39, 0,
+ 39, 0, 39, 24,
+ 0, 24, 39, 24,
+ 0, 0, 0, 24
+ }
+ },
+
+ // Box wipe (a box expands from the upper-left corner to the lower-right corner)
+ {
+ 25, // Number of iterations
+ {
+ 0, 1, 2, 1,
+ 2, 0, 2, 1,
+ 2, 0, 2, 1,
+ 0, 0, 0, 0
+ },
+ {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 1, 0, 1, 0,
+ 255, 0, 0, 0
+ }
+ },
+
+ // Box wipe (a box expands from the lower-right corner to the upper-left corner)
+ {
+ 25, // Number of iterations
+ {
+ -2, -1, 0, -1,
+ -2, -1, -2, 0,
+ -2, -1, -2, 0,
+ 0, 0, 0, 0
+ },
+ {
+ 39, 24, 39, 24,
+ 39, 24, 39, 24,
+ 38, 24, 38, 24,
+ 255, 0, 0, 0
+ }
+ },
+
+ // Inverse box wipe
+ {
+ 25, // Number of iterations
+ {
+ 0, -1, -2, -1,
+ -2, 0, -2, -1,
+ -2, 0, -2, -1,
+ 0, 0, 0, 0
+ },
+ {
+ 0, 24, 39, 24,
+ 39, 0, 39, 24,
+ 38, 0, 38, 24,
+ 255, 0, 0, 0
+ }
+ },
+
+ // Inverse iris effect, specially tailored for V1/V2 games
+ {
+ 9, // Number of iterations
+ {
+ -1, -1, 1, -1,
+ -1, 1, 1, 1,
+ -1, -1, -1, 1,
+ 1, -1, 1, 1
+ },
+ {
+ 7, 7, 32, 7,
+ 7, 8, 32, 8,
+ 7, 8, 7, 8,
+ 32, 7, 32, 8
+ }
+ },
+
+ // Horizontal wipe (a box expands from left to right side). For MM NES
+ {
+ 16, // Number of iterations
+ {
+ 2, 0, 2, 0,
+ 2, 0, 2, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+ },
+ {
+ 0, 0, 0, 15,
+ 1, 0, 1, 15,
+ 255, 0, 0, 0,
+ 255, 0, 0, 0
+ }
+ }
+
+};
+#endif
+
+
+Gdi::Gdi(ScummEngine *vm) {
+ memset(this, 0, sizeof(*this));
+ _vm = vm;
+ _paletteMod = 0;
+ _roomPalette = vm->_roomPalette;
+ _roomStrips = 0;
+}
+
+Gdi::~Gdi() {
+ free(_roomStrips);
+}
+
+void Gdi::init() {
+ _numStrips = _vm->_screenWidth / 8;
+
+ // Increase the number of screen strips by one; needed for smooth scrolling
+ if (_vm->_version >= 7) {
+ // We now have mostly working smooth scrolling code in place for V7+ games
+ // (i.e. The Dig, Full Throttle and COMI). It seems to work very well so far.
+ // One area which still may need some work are the AKOS codecs (except for
+ // codec 1, which I already updated): their masking code may need adjustments,
+ // similar to the treatment codec 1 received.
+ //
+ // To understand how we achieve smooth scrolling, first note that with it, the
+ // virtual screen strips don't match the display screen strips anymore. To
+ // overcome that problem, we simply use a screen pitch that is 8 pixel wider
+ // than the actual screen width, and always draw one strip more than needed to
+ // the backbuf (of course we have to treat the right border seperately). This
+ _numStrips += 1;
+ }
+}
+
+void Gdi::roomChanged(byte *roomptr, uint32 IM00_offs, byte transparentColor) {
+ if (_vm->_version == 1) {
+ if (_vm->_platform == Common::kPlatformNES) {
+ decodeNESGfx(roomptr);
+ } else {
+ for (int i = 0; i < 4; i++){
+ _C64.colors[i] = roomptr[6 + i];
+ }
+ decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 10), _C64.charMap, 2048);
+ decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 12), _C64.picMap, roomptr[4] * roomptr[5]);
+ decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 14), _C64.colorMap, roomptr[4] * roomptr[5]);
+ decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 16), _C64.maskMap, roomptr[4] * roomptr[5]);
+ decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 18) + 2, _C64.maskChar, READ_LE_UINT16(roomptr + READ_LE_UINT16(roomptr + 18)));
+ _objectMode = true;
+ }
+ } else if (_vm->_version == 2) {
+ _roomStrips = generateStripTable(roomptr + IM00_offs, _vm->_roomWidth, _vm->_roomHeight, _roomStrips);
+ }
+
+ _transparentColor = transparentColor;
+}
+
+
+#pragma mark -
+#pragma mark --- Virtual Screens ---
+#pragma mark -
+
+
+void ScummEngine::initScreens(int b, int h) {
+ int i;
+ int adj = 0;
+
+ for (i = 0; i < 3; i++) {
+ res.nukeResource(rtBuffer, i + 1);
+ res.nukeResource(rtBuffer, i + 5);
+ }
+
+ if (!getResourceAddress(rtBuffer, 4)) {
+ // Since the size of screen 3 is fixed, there is no need to reallocate
+ // it if its size changed.
+ // Not sure what it is good for, though. I think it may have been used
+ // in pre-V7 for the games messages (like 'Pause', Yes/No dialogs,
+ // version display, etc.). I don't know about V7, maybe the same is the
+ // case there. If so, we could probably just remove it completely.
+ if (_version >= 7) {
+ initVirtScreen(kUnkVirtScreen, (_screenHeight / 2) - 10, _screenWidth, 13, false, false);
+ } else {
+ initVirtScreen(kUnkVirtScreen, 80, _screenWidth, 13, false, false);
+ }
+ }
+
+ if ((_platform == Common::kPlatformNES) && (h != _screenHeight)) {
+ // This is a hack to shift the whole screen downwards to match the original.
+ // Otherwise we would have to do lots of coordinate adjustments all over
+ // the code.
+ adj = 16;
+ initVirtScreen(kUnkVirtScreen, 0, _screenWidth, adj, false, false);
+ }
+
+ initVirtScreen(kMainVirtScreen, b + adj, _screenWidth, h - b, true, true);
+ initVirtScreen(kTextVirtScreen, adj, _screenWidth, b, false, false);
+ initVirtScreen(kVerbVirtScreen, h + adj, _screenWidth, _screenHeight - h - adj, false, false);
+ _screenB = b;
+ _screenH = h;
+
+ gdi.init();
+}
+
+void ScummEngine::initVirtScreen(VirtScreenNumber slot, int top, int width, int height, bool twobufs,
+ bool scrollable) {
+ VirtScreen *vs = &virtscr[slot];
+ int size;
+
+ assert(height >= 0);
+ assert(slot >= 0 && slot < 4);
+
+ if (_version >= 7) {
+ if (slot == kMainVirtScreen && (_roomHeight != 0))
+ height = _roomHeight;
+ }
+
+ vs->number = slot;
+ vs->w = width;
+ vs->topline = top;
+ vs->h = height;
+ vs->hasTwoBuffers = twobufs;
+ vs->xstart = 0;
+ vs->backBuf = NULL;
+ vs->bytesPerPixel = 1;
+ vs->pitch = width;
+
+ if (_version >= 7) {
+ // Increase the pitch by one; needed to accomodate the extra
+ // screen strip which we use to implement smooth scrolling.
+ // See Gdi::init()
+ vs->pitch += 8;
+ }
+
+ size = vs->pitch * vs->h;
+ if (scrollable) {
+ // Allow enough spaces so that rooms can be up to 4 resp. 8 screens
+ // wide. To achieve (horizontal!) scrolling, we use a neat trick:
+ // only the offset into the screen buffer (xstart) is changed. That way
+ // very little of the screen has to be redrawn, and we have a very low
+ // memory overhead (namely for every pixel we want to scroll, we need
+ // one additional byte in the buffer).
+ if (_version >= 7) {
+ size += vs->pitch * 8;
+ } else {
+ size += vs->pitch * 4;
+ }
+ }
+
+ res.createResource(rtBuffer, slot + 1, size);
+ vs->pixels = getResourceAddress(rtBuffer, slot + 1);
+ memset(vs->pixels, 0, size); // reset background
+
+ if (twobufs) {
+ vs->backBuf = res.createResource(rtBuffer, slot + 5, size);
+ }
+
+ if (slot != 3) {
+ vs->setDirtyRange(0, height);
+ }
+}
+
+VirtScreen *ScummEngine::findVirtScreen(int y) {
+ VirtScreen *vs = virtscr;
+ int i;
+
+ for (i = 0; i < 3; i++, vs++) {
+ if (y >= vs->topline && y < vs->topline + vs->h) {
+ return vs;
+ }
+ }
+ return NULL;
+}
+
+void ScummEngine::markRectAsDirty(VirtScreenNumber virt, int left, int right, int top, int bottom, int dirtybit) {
+ VirtScreen *vs = &virtscr[virt];
+ int lp, rp;
+
+ if (left > right || top > bottom)
+ return;
+ if (top > vs->h || bottom < 0)
+ return;
+
+ if (top < 0)
+ top = 0;
+ if (bottom > vs->h)
+ bottom = vs->h;
+
+ if (virt == kMainVirtScreen && dirtybit) {
+
+ lp = left / 8 + _screenStartStrip;
+ if (lp < 0)
+ lp = 0;
+
+ rp = (right + vs->xstart) / 8;
+ if (_version >= 7) {
+ if (rp > 409)
+ rp = 409;
+ } else {
+ if (rp >= 200)
+ rp = 200;
+ }
+ for (; lp <= rp; lp++)
+ setGfxUsageBit(lp, dirtybit);
+ }
+
+ // The following code used to be in the separate method setVirtscreenDirty
+ lp = left / 8;
+ rp = right / 8;
+
+ if ((lp >= gdi._numStrips) || (rp < 0))
+ return;
+ if (lp < 0)
+ lp = 0;
+ if (rp >= gdi._numStrips)
+ rp = gdi._numStrips - 1;
+
+ while (lp <= rp) {
+ if (top < vs->tdirty[lp])
+ vs->tdirty[lp] = top;
+ if (bottom > vs->bdirty[lp])
+ vs->bdirty[lp] = bottom;
+ lp++;
+ }
+}
+
+/**
+ * Update all dirty screen areas. This method blits all of the internal engine
+ * graphics to the actual display, as needed. In addition, the 'shaking'
+ * code in the backend is controlled from here.
+ */
+void ScummEngine::drawDirtyScreenParts() {
+ // Update verbs
+ updateDirtyScreen(kVerbVirtScreen);
+
+ // Update the conversation area (at the top of the screen)
+ updateDirtyScreen(kTextVirtScreen);
+
+ // Update game area ("stage")
+ if (camera._last.x != camera._cur.x || (_features & GF_NEW_CAMERA && (camera._cur.y != camera._last.y))) {
+ // Camera moved: redraw everything
+ VirtScreen *vs = &virtscr[kMainVirtScreen];
+ drawStripToScreen(vs, 0, vs->w, 0, vs->h);
+ vs->setDirtyRange(vs->h, 0);
+ } else {
+ updateDirtyScreen(kMainVirtScreen);
+ }
+
+ // Handle shaking
+ if (_shakeEnabled) {
+ _shakeFrame = (_shakeFrame + 1) % NUM_SHAKE_POSITIONS;
+ _system->setShakePos(shake_positions[_shakeFrame]);
+ } else if (!_shakeEnabled &&_shakeFrame != 0) {
+ _shakeFrame = 0;
+ _system->setShakePos(0);
+ }
+}
+
+void ScummEngine_v6::drawDirtyScreenParts() {
+ // For the Full Throttle credits to work properly, the blast
+ // texts have to be drawn before the blast objects. Unless
+ // someone can think of a better way to achieve this effect.
+
+ if (_version >= 7 && VAR(VAR_BLAST_ABOVE_TEXT) == 1) {
+ drawBlastTexts();
+ drawBlastObjects();
+ } else {
+ drawBlastObjects();
+ drawBlastTexts();
+ }
+ if (_version == 8)
+ processUpperActors();
+
+ // Call the original method.
+ ScummEngine::drawDirtyScreenParts();
+
+ // Remove all blasted objects/text again.
+ removeBlastTexts();
+ removeBlastObjects();
+}
+
+/**
+ * Blit the dirty data from the given VirtScreen to the display. If the camera moved,
+ * a full blit is done, otherwise only the visible dirty areas are updated.
+ */
+void ScummEngine::updateDirtyScreen(VirtScreenNumber slot) {
+ VirtScreen *vs = &virtscr[slot];
+
+ // Do nothing for unused virtual screens
+ if (vs->h == 0)
+ return;
+
+ int i;
+ int w = 8;
+ int start = 0;
+
+ for (i = 0; i < gdi._numStrips; i++) {
+ if (vs->bdirty[i]) {
+ const int top = vs->tdirty[i];
+ const int bottom = vs->bdirty[i];
+ vs->tdirty[i] = vs->h;
+ vs->bdirty[i] = 0;
+ if (i != (gdi._numStrips - 1) && vs->bdirty[i + 1] == bottom && vs->tdirty[i + 1] == top) {
+ // Simple optimizations: if two or more neighbouring strips
+ // form one bigger rectangle, coalesce them.
+ w += 8;
+ continue;
+ }
+ drawStripToScreen(vs, start * 8, w, top, bottom);
+ w = 8;
+ }
+ start = i + 1;
+ }
+}
+
+/**
+ * Blit the specified rectangle from the given virtual screen to the display.
+ * Note: t and b are in *virtual screen* coordinates, while x is relative to
+ * the *real screen*. This is due to the way tdirty/vdirty work: they are
+ * arrays which map 'strips' (sections of the real screen) to dirty areas as
+ * specified by top/bottom coordinate in the virtual screen.
+ */
+void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, int bottom) {
+
+ if (bottom <= top)
+ return;
+
+ if (top >= vs->h)
+ return;
+
+ assert(top >= 0 && bottom <= vs->h); // Paranoia checks
+ assert(x >= 0 && width <= vs->pitch);
+ assert(_charset->_textSurface.pixels);
+ assert(_compositeBuf);
+
+ if (width > vs->w - x)
+ width = vs->w - x;
+
+ // Clip to the visible part of the scene
+ if (top < _screenTop)
+ top = _screenTop;
+ if (bottom > _screenTop + _screenHeight)
+ bottom = _screenTop + _screenHeight;
+
+ // Convert the vertical coordinates to real screen coords
+ int y = vs->topline + top - _screenTop;
+ int height = bottom - top;
+
+ if (height <= 0 || width <= 0)
+ return;
+
+ // Compute screen etc. buffer pointers
+ const byte *src = vs->getPixels(x, top);
+ byte *dst = _compositeBuf + x + y * _screenWidth;
+
+ if (_version < 7) {
+ // Handle the text mask in older games; newer (V7/V8) games do not use it anymore.
+ const byte *text = (byte *)_charset->_textSurface.pixels + x + y * _charset->_textSurface.pitch;
+
+ // Compose the text over the game graphics
+ for (int h = 0; h < height; ++h) {
+ for (int w = 0; w < width; ++w) {
+ if (text[w] == CHARSET_MASK_TRANSPARENCY)
+ dst[w] = src[w];
+ else
+ dst[w] = text[w];
+ }
+ src += vs->pitch;
+ dst += _screenWidth;
+ text += _charset->_textSurface.pitch;
+ }
+ } else {
+ // Just do a simple blit in V7/V8 games.
+ blit(dst, _screenWidth, src, vs->pitch, width, height);
+ }
+
+ if (_renderMode == Common::kRenderCGA)
+ ditherCGA(_compositeBuf + x + y * _screenWidth, _screenWidth, x, y, width, height);
+
+ if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
+ ditherHerc(_compositeBuf + x + y * _screenWidth, _herculesBuf, _screenWidth, &x, &y, &width, &height);
+ // center image on the screen
+ _system->copyRectToScreen(_herculesBuf + x + y * Common::kHercW,
+ Common::kHercW, x + (Common::kHercW - _screenWidth * 2) / 2, y, width, height);
+ } else {
+ // Finally blit the whole thing to the screen
+ int x1 = x;
+
+ // HACK: This is dirty hack which renders narrow NES rooms centered
+ // NES can address negative number strips and that poses problem for
+ // our code. So instead adding zillions of fixes and potentially break
+ // other games we shift it right on rendering stage
+ if ((_platform == Common::kPlatformNES) && (((_NESStartStrip > 0) && (vs->number == kMainVirtScreen)) || (vs->number == kTextVirtScreen))) {
+ x += 16;
+ while (x + width >= _screenWidth)
+ width -= 16;
+ if (width < 0)
+ return;
+ }
+
+ _system->copyRectToScreen(_compositeBuf + x1 + y * _screenWidth, _screenWidth, x, y, width, height);
+ }
+}
+
+// CGA
+// indy3 loom maniac monkey1 zak
+//
+// Herc (720x350)
+// maniac monkey1 zak
+//
+// EGA
+// monkey2 loom maniac monkey1 atlantis indy3 zak loomcd
+
+// CGA dithers 4x4 square with direct substitutes
+// Odd lines have colors swapped, so there will be checkered patterns.
+// But apparently there is a mistake for 10th color.
+void ScummEngine::ditherCGA(byte *dst, int dstPitch, int x, int y, int width, int height) const {
+ byte *ptr;
+ int idx1, idx2;
+ static const byte cgaDither[2][2][16] = {
+ {{0, 1, 0, 1, 2, 2, 0, 0, 3, 1, 3, 1, 3, 2, 1, 3},
+ {0, 0, 1, 1, 0, 2, 2, 3, 0, 3, 1, 1, 3, 3, 1, 3}},
+ {{0, 0, 1, 1, 0, 2, 2, 3, 0, 3, 1, 1, 3, 3, 1, 3},
+ {0, 1, 0, 1, 2, 2, 0, 0, 3, 1, 1, 1, 3, 2, 1, 3}}};
+
+ for (int y1 = 0; y1 < height; y1++) {
+ ptr = dst + y1 * dstPitch;
+
+ idx1 = (y + y1) % 2;
+
+ if (_version == 2)
+ idx1 = 0;
+
+ for (int x1 = 0; x1 < width; x1++) {
+ idx2 = (x + x1) % 2;
+ *ptr = cgaDither[idx1][idx2][*ptr & 0xF];
+ ptr++;
+ }
+ }
+}
+
+// Hercules dithering. It uses same dithering tables but output is 1bpp and
+// it stretches in this way:
+// aaaa0
+// aa aaaa1
+// bb bbbb0 Here 0 and 1 mean dithering table row number
+// cc --> bbbb1
+// dd cccc0
+// cccc1
+// dddd0
+void ScummEngine::ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *width, int *height) const {
+ byte *srcptr, *dstptr;
+ int xo = *x, yo = *y, widtho = *width, heighto = *height;
+ int idx1, idx2, dsty = 0, y1;
+ static const byte cgaDither[2][2][16] = {
+ {{0, 1, 0, 1, 2, 2, 0, 0, 3, 1, 3, 1, 3, 2, 1, 3},
+ {0, 0, 1, 1, 0, 2, 2, 3, 0, 3, 1, 1, 3, 3, 1, 3}},
+ {{0, 0, 1, 1, 0, 2, 2, 3, 0, 3, 1, 1, 3, 3, 1, 3},
+ {0, 1, 0, 1, 2, 2, 0, 0, 3, 1, 1, 1, 3, 2, 1, 3}}};
+
+ // calculate dsty
+ for (y1 = 0; y1 < yo; y1++) {
+ dsty += 2;
+ if (y1 % 4 == 3)
+ dsty--;
+ }
+ *y = dsty;
+ *x *= 2;
+ *width *= 2;
+ *height = 0;
+
+ for (y1 = 0; y1 < heighto;) {
+ srcptr = src + y1 * srcPitch;
+ dstptr = hercbuf + dsty * Common::kHercW + xo * 2;
+
+ assert(dstptr < hercbuf + Common::kHercW * Common::kHercH + widtho * 2);
+
+ idx1 = (dsty % 7) % 2;
+ for (int x1 = 0; x1 < widtho; x1++) {
+ idx2 = (xo + x1) % 2;
+ *dstptr++ = cgaDither[idx1][idx2][*srcptr & 0xF] >> 1;
+ *dstptr++ = cgaDither[idx1][idx2][*srcptr & 0xF] & 0x1;
+ srcptr++;
+ }
+ if (idx1 || dsty % 7 == 6)
+ y1++;
+ dsty++;
+ (*height)++;
+ }
+}
+
+
+#pragma mark -
+#pragma mark --- Background buffers & charset mask ---
+#pragma mark -
+
+
+void ScummEngine::initBGBuffers(int height) {
+ const byte *ptr;
+ int size, itemsize, i;
+ byte *room;
+
+ if (_version >= 7) {
+ // Resize main virtual screen in V7 games. This is necessary
+ // because in V7, rooms may be higher than one screen, so we have
+ // to accomodate for that.
+ initVirtScreen(kMainVirtScreen, virtscr[0].topline, _screenWidth, height, true, true);
+ }
+
+ if (_heversion >= 70)
+ room = getResourceAddress(rtRoomImage, _roomResource);
+ else
+ room = getResourceAddress(rtRoom, _roomResource);
+
+ if (_version <= 3) {
+ gdi._numZBuffer = 2;
+ } else if (_features & GF_SMALL_HEADER) {
+ int off;
+ ptr = findResourceData(MKID('SMAP'), room);
+ gdi._numZBuffer = 0;
+
+ if (_features & GF_16COLOR)
+ off = READ_LE_UINT16(ptr);
+ else
+ off = READ_LE_UINT32(ptr);
+
+ while (off && gdi._numZBuffer < 4) {
+ gdi._numZBuffer++;
+ ptr += off;
+ off = READ_LE_UINT16(ptr);
+ }
+ } else if (_version == 8) {
+ // in V8 there is no RMIH and num z buffers is in RMHD
+ ptr = findResource(MKID('RMHD'), room);
+ gdi._numZBuffer = READ_LE_UINT32(ptr + 24) + 1;
+ } else if (_heversion >= 70) {
+ ptr = findResource(MKID('RMIH'), room);
+ gdi._numZBuffer = READ_LE_UINT16(ptr + 8) + 1;
+ } else {
+ ptr = findResource(MKID('RMIH'), findResource(MKID('RMIM'), room));
+ gdi._numZBuffer = READ_LE_UINT16(ptr + 8) + 1;
+ }
+ assert(gdi._numZBuffer >= 1 && gdi._numZBuffer <= 8);
+
+ if (_version >= 7)
+ itemsize = (_roomHeight + 10) * gdi._numStrips;
+ else
+ itemsize = (_roomHeight + 4) * gdi._numStrips;
+
+
+ size = itemsize * gdi._numZBuffer;
+ memset(res.createResource(rtBuffer, 9, size), 0, size);
+
+ for (i = 0; i < (int)ARRAYSIZE(gdi._imgBufOffs); i++) {
+ if (i < gdi._numZBuffer)
+ gdi._imgBufOffs[i] = i * itemsize;
+ else
+ gdi._imgBufOffs[i] = (gdi._numZBuffer - 1) * itemsize;
+ }
+}
+
+/**
+ * Redraw background as needed, i.e. the left/right sides if scrolling took place etc.
+ * Note that this only updated the virtual screen, not the actual display.
+ */
+void ScummEngine::redrawBGAreas() {
+ int i;
+ int diff;
+ int val = 0;
+
+ if (!(_features & GF_NEW_CAMERA))
+ if (camera._cur.x != camera._last.x && _charset->_hasMask && (_version > 3 && _gameId != GID_PASS))
+ stopTalk();
+
+ // Redraw parts of the background which are marked as dirty.
+ if (!_fullRedraw && _bgNeedsRedraw) {
+ for (i = 0; i != gdi._numStrips; i++) {
+ if (testGfxUsageBit(_screenStartStrip + i, USAGE_BIT_DIRTY)) {
+ redrawBGStrip(i, 1);
+ }
+ }
+ }
+
+ if (_features & GF_NEW_CAMERA) {
+ diff = camera._cur.x / 8 - camera._last.x / 8;
+ if (_fullRedraw || ABS(diff) >= gdi._numStrips) {
+ _bgNeedsRedraw = false;
+ redrawBGStrip(0, gdi._numStrips);
+ } else if (diff > 0) {
+ val = -diff;
+ redrawBGStrip(gdi._numStrips - diff, diff);
+ } else if (diff < 0) {
+ val = -diff;
+ redrawBGStrip(0, -diff);
+ }
+ } else {
+ diff = camera._cur.x - camera._last.x;
+ if (!_fullRedraw && diff == 8) {
+ val = -1;
+ redrawBGStrip(gdi._numStrips - 1, 1);
+ } else if (!_fullRedraw && diff == -8) {
+ val = +1;
+ redrawBGStrip(0, 1);
+ } else if (_fullRedraw || diff != 0) {
+ _bgNeedsRedraw = false;
+ _flashlight.isDrawn = false;
+ redrawBGStrip(0, gdi._numStrips);
+ }
+ }
+
+ drawRoomObjects(val);
+ _bgNeedsRedraw = false;
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v71he::redrawBGAreas() {
+ if (camera._cur.x != camera._last.x && _charset->_hasMask)
+ stopTalk();
+
+ byte *room = getResourceAddress(rtRoomImage, _roomResource) + _IM00_offs;
+ if (_fullRedraw) {
+ _bgNeedsRedraw = false;
+ gdi.drawBMAPBg(room, &virtscr[0]);
+ }
+
+ drawRoomObjects(0);
+ _bgNeedsRedraw = false;
+}
+
+void ScummEngine_v72he::redrawBGAreas() {
+ ScummEngine_v71he::redrawBGAreas();
+ _wiz->flushWizBuffer();
+}
+#endif
+
+void ScummEngine::redrawBGStrip(int start, int num) {
+ byte *room;
+
+ int s = _screenStartStrip + start;
+
+ for (int i = 0; i < num; i++)
+ setGfxUsageBit(s + i, USAGE_BIT_DIRTY);
+
+ if (_heversion >= 70)
+ room = getResourceAddress(rtRoomImage, _roomResource);
+ else
+ room = getResourceAddress(rtRoom, _roomResource);
+
+ gdi.drawBitmap(room + _IM00_offs, &virtscr[0], s, 0, _roomWidth, virtscr[0].h, s, num, 0);
+}
+
+void ScummEngine::restoreBG(Common::Rect rect, byte backColor) {
+ VirtScreen *vs;
+ byte *screenBuf;
+
+ if (rect.top < 0)
+ rect.top = 0;
+ if (rect.left >= rect.right || rect.top >= rect.bottom)
+ return;
+
+ if ((vs = findVirtScreen(rect.top)) == NULL)
+ return;
+
+ if (rect.left > vs->w)
+ return;
+
+ // Convert 'rect' to local (virtual screen) coordinates
+ rect.top -= vs->topline;
+ rect.bottom -= vs->topline;
+
+ rect.clip(vs->w, vs->h);
+
+ markRectAsDirty(vs->number, rect, USAGE_BIT_RESTORED);
+
+ screenBuf = vs->getPixels(rect.left, rect.top);
+
+ const int height = rect.height();
+ const int width = rect.width();
+
+ if (!height)
+ return;
+
+ if (vs->hasTwoBuffers && _currentRoom != 0 && isLightOn()) {
+ blit(screenBuf, vs->pitch, vs->getBackPixels(rect.left, rect.top), vs->pitch, width, height);
+ if (vs->number == kMainVirtScreen && _charset->_hasMask) {
+ byte *mask = (byte *)_charset->_textSurface.pixels + _charset->_textSurface.pitch * (rect.top - _screenTop) + rect.left;
+ fill(mask, _charset->_textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width, height);
+ }
+ } else {
+ fill(screenBuf, vs->pitch, backColor, width, height);
+ }
+}
+
+void CharsetRenderer::restoreCharsetBg() {
+ _nextLeft = _vm->_string[0].xpos;
+ _nextTop = _vm->_string[0].ypos + _vm->_screenTop;
+
+ if (_hasMask) {
+ _hasMask = false;
+ _str.left = -1;
+ _left = -1;
+
+ // Restore background on the whole text area. This code is based on
+ // restoreBG(), but was changed to only restore those parts which are
+ // currently covered by the charset mask.
+
+ VirtScreen *vs = &_vm->virtscr[_textScreenID];
+ if (!vs->h)
+ return;
+
+ _vm->markRectAsDirty(vs->number, Common::Rect(vs->w, vs->h), USAGE_BIT_RESTORED);
+
+ byte *screenBuf = vs->getPixels(0, 0);
+
+ if (vs->hasTwoBuffers && _vm->_currentRoom != 0 && _vm->isLightOn()) {
+ if (vs->number != kMainVirtScreen) {
+ // Restore from back buffer
+ const byte *backBuf = vs->getBackPixels(0, 0);
+ blit(screenBuf, vs->pitch, backBuf, vs->pitch, vs->w, vs->h);
+ }
+ } else {
+ // Clear area
+ memset(screenBuf, 0, vs->h * vs->pitch);
+ }
+
+ if (vs->hasTwoBuffers) {
+ // Clean out the charset mask
+ clearTextSurface();
+ }
+ }
+}
+
+void CharsetRenderer::clearCharsetMask() {
+ memset(_vm->getResourceAddress(rtBuffer, 9), 0, _vm->gdi._imgBufOffs[1]);
+}
+
+void CharsetRenderer::clearTextSurface() {
+ memset(_textSurface.pixels, CHARSET_MASK_TRANSPARENCY, _textSurface.pitch * _textSurface.h);
+}
+
+byte *ScummEngine::getMaskBuffer(int x, int y, int z) {
+ return gdi.getMaskBuffer((x + virtscr[0].xstart) / 8, y, z);
+}
+
+byte *Gdi::getMaskBuffer(int x, int y, int z) {
+ return _vm->getResourceAddress(rtBuffer, 9)
+ + x + y * _numStrips + _imgBufOffs[z];
+}
+
+
+#pragma mark -
+#pragma mark --- Misc ---
+#pragma mark -
+
+static void blit(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h) {
+ assert(w > 0);
+ assert(h > 0);
+ assert(src != NULL);
+ assert(dst != NULL);
+
+ if (w == srcPitch && w == dstPitch) {
+ memcpy(dst, src, w*h);
+ } else {
+ do {
+ memcpy(dst, src, w);
+ dst += dstPitch;
+ src += srcPitch;
+ } while (--h);
+ }
+}
+
+static void fill(byte *dst, int dstPitch, byte color, int w, int h) {
+ assert(h > 0);
+ assert(dst != NULL);
+
+ if (w == dstPitch) {
+ memset(dst, color, w*h);
+ } else {
+ do {
+ memset(dst, color, w);
+ dst += dstPitch;
+ } while (--h);
+ }
+}
+
+static void copy8Col(byte *dst, int dstPitch, const byte *src, int height) {
+ do {
+#if defined(SCUMM_NEED_ALIGNMENT)
+ memcpy(dst, src, 8);
+#else
+ ((uint32 *)dst)[0] = ((const uint32 *)src)[0];
+ ((uint32 *)dst)[1] = ((const uint32 *)src)[1];
+#endif
+ dst += dstPitch;
+ src += dstPitch;
+ } while (--height);
+}
+
+static void clear8Col(byte *dst, int dstPitch, int height) {
+ do {
+#if defined(SCUMM_NEED_ALIGNMENT)
+ memset(dst, 0, 8);
+#else
+ ((uint32 *)dst)[0] = 0;
+ ((uint32 *)dst)[1] = 0;
+#endif
+ dst += dstPitch;
+ } while (--height);
+}
+
+void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {
+ int width, height;
+ VirtScreen *vs;
+ byte *backbuff, *bgbuff;
+
+ if ((vs = findVirtScreen(y)) == NULL)
+ return;
+
+ if (x > x2)
+ SWAP(x, x2);
+
+ if (y > y2)
+ SWAP(y, y2);
+
+ x2++;
+ y2++;
+
+ // Adjust for the topline of the VirtScreen
+ y -= vs->topline;
+ y2 -= vs->topline;
+
+ // Clip the coordinates
+ if (x < 0)
+ x = 0;
+ else if (x >= vs->w)
+ return;
+
+ if (x2 < 0)
+ return;
+ else if (x2 > vs->w)
+ x2 = vs->w;
+
+ if (y < 0)
+ y = 0;
+ else if (y > vs->h)
+ return;
+
+ if (y2 < 0)
+ return;
+ else if (y2 > vs->h)
+ y2 = vs->h;
+
+ width = x2 - x;
+ height = y2 - y;
+
+ // This will happen in the Sam & Max intro - see bug #1039162 - where
+ // it would trigger an assertion in blit().
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ markRectAsDirty(vs->number, x, x2, y, y2);
+
+ backbuff = vs->getPixels(x, y);
+ bgbuff = vs->getBackPixels(x, y);
+
+ if (color == -1) {
+ if (vs->number != kMainVirtScreen)
+ error("can only copy bg to main window");
+ blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height);
+ if (_charset->_hasMask) {
+ byte *mask = (byte *)_charset->_textSurface.pixels + _charset->_textSurface.pitch * (y - _screenTop) + x;
+ fill(mask, _charset->_textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width, height);
+ }
+ } else if (_heversion == 100) {
+ // Flags are used for different methods in HE games
+ int32 flags = color;
+ if (flags & 0x4000000) {
+ blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height);
+ } else if (flags & 0x2000000) {
+ blit(bgbuff, vs->pitch, backbuff, vs->pitch, width, height);
+ } else if (flags & 0x1000000) {
+ flags &= 0xFFFFFF;
+ fill(backbuff, vs->pitch, flags, width, height);
+ fill(bgbuff, vs->pitch, flags, width, height);
+ } else {
+ fill(backbuff, vs->pitch, flags, width, height);
+ }
+ } else {
+ // Flags are used for different methods in HE games
+ int16 flags = color;
+ if (flags & 0x2000) {
+ blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height);
+ } else if (flags & 0x4000) {
+ blit(bgbuff, vs->pitch, backbuff, vs->pitch, width, height);
+ } else if (flags & 0x8000) {
+ flags &= 0x7FFF;
+ fill(backbuff, vs->pitch, flags, width, height);
+ fill(bgbuff, vs->pitch, flags, width, height);
+ } else {
+ fill(backbuff, vs->pitch, flags, width, height);
+ }
+ }
+}
+
+void ScummEngine::drawFlashlight() {
+ int i, j, x, y;
+ VirtScreen *vs = &virtscr[kMainVirtScreen];
+
+ // Remove the flash light first if it was previously drawn
+ if (_flashlight.isDrawn) {
+ markRectAsDirty(kMainVirtScreen, _flashlight.x, _flashlight.x + _flashlight.w,
+ _flashlight.y, _flashlight.y + _flashlight.h, USAGE_BIT_DIRTY);
+
+ if (_flashlight.buffer) {
+ fill(_flashlight.buffer, vs->pitch, 0, _flashlight.w, _flashlight.h);
+ }
+ _flashlight.isDrawn = false;
+ }
+
+ if (_flashlight.xStrips == 0 || _flashlight.yStrips == 0)
+ return;
+
+ // Calculate the area of the flashlight
+ if (_gameId == GID_ZAK || _gameId == GID_MANIAC) {
+ x = _mouse.x + vs->xstart;
+ y = _mouse.y - vs->topline;
+ } else {
+ Actor *a = derefActor(VAR(VAR_EGO), "drawFlashlight");
+ x = a->_pos.x;
+ y = a->_pos.y;
+ }
+ _flashlight.w = _flashlight.xStrips * 8;
+ _flashlight.h = _flashlight.yStrips * 8;
+ _flashlight.x = x - _flashlight.w / 2 - _screenStartStrip * 8;
+ _flashlight.y = y - _flashlight.h / 2;
+
+ if (_gameId == GID_LOOM)
+ _flashlight.y -= 12;
+
+ // Clip the flashlight at the borders
+ if (_flashlight.x < 0)
+ _flashlight.x = 0;
+ else if (_flashlight.x + _flashlight.w > gdi._numStrips * 8)
+ _flashlight.x = gdi._numStrips * 8 - _flashlight.w;
+ if (_flashlight.y < 0)
+ _flashlight.y = 0;
+ else if (_flashlight.y + _flashlight.h> vs->h)
+ _flashlight.y = vs->h - _flashlight.h;
+
+ // Redraw any actors "under" the flashlight
+ for (i = _flashlight.x / 8; i < (_flashlight.x + _flashlight.w) / 8; i++) {
+ assert(0 <= i && i < gdi._numStrips);
+ setGfxUsageBit(_screenStartStrip + i, USAGE_BIT_DIRTY);
+ vs->tdirty[i] = 0;
+ vs->bdirty[i] = vs->h;
+ }
+
+ byte *bgbak;
+ _flashlight.buffer = vs->getPixels(_flashlight.x, _flashlight.y);
+ bgbak = vs->getBackPixels(_flashlight.x, _flashlight.y);
+
+ blit(_flashlight.buffer, vs->pitch, bgbak, vs->pitch, _flashlight.w, _flashlight.h);
+
+ // Round the corners. To do so, we simply hard-code a set of nicely
+ // rounded corners.
+ static const int corner_data[] = { 8, 6, 4, 3, 2, 2, 1, 1 };
+ int minrow = 0;
+ int maxcol = _flashlight.w - 1;
+ int maxrow = (_flashlight.h - 1) * vs->pitch;
+
+ for (i = 0; i < 8; i++, minrow += vs->pitch, maxrow -= vs->pitch) {
+ int d = corner_data[i];
+
+ for (j = 0; j < d; j++) {
+ _flashlight.buffer[minrow + j] = 0;
+ _flashlight.buffer[minrow + maxcol - j] = 0;
+ _flashlight.buffer[maxrow + j] = 0;
+ _flashlight.buffer[maxrow + maxcol - j] = 0;
+ }
+ }
+
+ _flashlight.isDrawn = true;
+}
+
+bool ScummEngine::isLightOn() const {
+ return (VAR_CURRENT_LIGHTS == 0xFF) || (VAR(VAR_CURRENT_LIGHTS) & LIGHTMODE_screen);
+}
+
+void ScummEngine::setShake(int mode) {
+ if (_shakeEnabled != (mode != 0))
+ _fullRedraw = true;
+
+ _shakeEnabled = mode != 0;
+ _shakeFrame = 0;
+ _system->setShakePos(0);
+}
+
+#pragma mark -
+#pragma mark --- Image drawing ---
+#pragma mark -
+
+
+void Gdi::drawBitmapV2Helper(const byte *ptr, VirtScreen *vs, int x, int y, const int width, const int height, int stripnr, int numstrip) {
+ StripTable *table = (_objectMode ? 0 : _roomStrips);
+ const int left = (stripnr * 8);
+ const int right = left + (numstrip * 8);
+ byte *dst;
+ byte *mask_ptr;
+ const byte *src;
+ byte color, data = 0;
+ int run;
+ bool dither = false;
+ byte dither_table[128];
+ byte *ptr_dither_table;
+ int theX, theY, maxX;
+
+ memset(dither_table, 0, sizeof(dither_table));
+
+ if (vs->hasTwoBuffers)
+ dst = vs->backBuf + y * vs->pitch + x * 8;
+ else
+ dst = (byte *)vs->pixels + y * vs->pitch + x * 8;
+
+ mask_ptr = getMaskBuffer(x, y, 1);
+
+
+ if (table) {
+ run = table->run[stripnr];
+ color = table->color[stripnr];
+ src = ptr + table->offsets[stripnr];
+ theX = left;
+ maxX = right;
+ } else {
+ run = 1;
+ color = 0;
+ src = ptr;
+ theX = 0;
+ maxX = width;
+ }
+
+ // Decode and draw the image data.
+ assert(height <= 128);
+ for (; theX < maxX; theX++) {
+ ptr_dither_table = dither_table;
+ for (theY = 0; theY < height; theY++) {
+ if (--run == 0) {
+ data = *src++;
+ if (data & 0x80) {
+ run = data & 0x7f;
+ dither = true;
+ } else {
+ run = data >> 4;
+ dither = false;
+ }
+ color = _roomPalette[data & 0x0f];
+ if (run == 0) {
+ run = *src++;
+ }
+ }
+ if (!dither) {
+ *ptr_dither_table = color;
+ }
+ if (left <= theX && theX < right) {
+ *dst = *ptr_dither_table++;
+ dst += vs->pitch;
+ }
+ }
+ if (left <= theX && theX < right) {
+ dst -= _vertStripNextInc;
+ }
+ }
+
+
+ // Draw mask (zplane) data
+ theY = 0;
+
+ if (table) {
+ src = ptr + table->zoffsets[stripnr];
+ run = table->zrun[stripnr];
+ theX = left;
+ } else {
+ run = *src++;
+ theX = 0;
+ }
+ while (theX < right) {
+ const byte runFlag = run & 0x80;
+ if (runFlag) {
+ run &= 0x7f;
+ data = *src++;
+ }
+ do {
+ if (!runFlag)
+ data = *src++;
+
+ if (left <= theX) {
+ *mask_ptr = data;
+ mask_ptr += _numStrips;
+ }
+ theY++;
+ if (theY >= height) {
+ if (left <= theX) {
+ mask_ptr -= _numStrips * height - 1;
+ }
+ theY = 0;
+ theX += 8;
+ if (theX >= right)
+ break;
+ }
+ } while (--run);
+ run = *src++;
+ }
+}
+
+int Gdi::getZPlanes(const byte *ptr, const byte *zplane_list[9], bool bmapImage) const {
+ int numzbuf;
+ int i;
+
+ if ((_vm->_features & GF_SMALL_HEADER) || _vm->_version == 8)
+ zplane_list[0] = ptr;
+ else if (bmapImage)
+ zplane_list[0] = _vm->findResource(MKID('BMAP'), ptr);
+ else
+ zplane_list[0] = _vm->findResource(MKID('SMAP'), ptr);
+
+ if (_zbufferDisabled)
+ numzbuf = 0;
+ else if (_numZBuffer <= 1 || (_vm->_version <= 2))
+ numzbuf = _numZBuffer;
+ else {
+ numzbuf = _numZBuffer;
+ assert(numzbuf <= 9);
+
+ if (_vm->_features & GF_SMALL_HEADER) {
+ if (_vm->_features & GF_16COLOR)
+ zplane_list[1] = ptr + READ_LE_UINT16(ptr);
+ else {
+ zplane_list[1] = ptr + READ_LE_UINT32(ptr);
+ if (_vm->_features & GF_OLD256) {
+ if (0 == READ_LE_UINT32(zplane_list[1]))
+ zplane_list[1] = 0;
+ }
+ }
+ for (i = 2; i < numzbuf; i++) {
+ zplane_list[i] = zplane_list[i-1] + READ_LE_UINT16(zplane_list[i-1]);
+ }
+ } else if (_vm->_version == 8) {
+ // Find the OFFS chunk of the ZPLN chunk
+ const byte *zplnOffsChunkStart = ptr + 24 + READ_BE_UINT32(ptr + 12);
+
+ // Each ZPLN contains a WRAP chunk, which has (as always) an OFFS subchunk pointing
+ // at ZSTR chunks. These once more contain a WRAP chunk which contains nothing but
+ // an OFFS chunk. The content of this OFFS chunk contains the offsets to the
+ // Z-planes.
+ // We do not directly make use of this, but rather hard code offsets (like we do
+ // for all other Scumm-versions, too). Clearly this is a bit hackish, but works
+ // well enough, and there is no reason to assume that there are any cases where it
+ // might fail. Still, doing this properly would have the advantage of catching
+ // invalid/damaged data files, and allow us to exit gracefully instead of segfaulting.
+ for (i = 1; i < numzbuf; i++) {
+ zplane_list[i] = zplnOffsChunkStart + READ_LE_UINT32(zplnOffsChunkStart + 4 + i*4) + 16;
+ }
+ } else {
+ const uint32 zplane_tags[] = {
+ MKID('ZP00'),
+ MKID('ZP01'),
+ MKID('ZP02'),
+ MKID('ZP03'),
+ MKID('ZP04')
+ };
+
+ for (i = 1; i < numzbuf; i++) {
+ zplane_list[i] = _vm->findResource(zplane_tags[i], ptr);
+ }
+ }
+ }
+
+ return numzbuf;
+}
+
+/**
+ * Draw a bitmap onto a virtual screen. This is main drawing method for room backgrounds
+ * and objects, used throughout all SCUMM versions.
+ */
+void Gdi::drawBitmap(const byte *ptr, VirtScreen *vs, int x, int y, const int width, const int height,
+ int stripnr, int numstrip, byte flag) {
+ assert(ptr);
+ assert(height > 0);
+ byte *dstPtr;
+ const byte *smap_ptr;
+ const byte *z_plane_ptr;
+ byte *mask_ptr;
+
+ int i;
+ const byte *zplane_list[9];
+
+ int bottom;
+ int numzbuf;
+ int sx;
+ bool transpStrip = false;
+
+ // Check whether lights are turned on or not
+ const bool lightsOn = _vm->isLightOn();
+
+ _objectMode = (flag & dbObjectMode) == dbObjectMode;
+
+ if (_objectMode && _vm->_version == 1) {
+ if (_vm->_platform == Common::kPlatformNES) {
+ decodeNESObject(ptr, x, y, width, height);
+ } else {
+ decodeC64Gfx(ptr, _C64.objectMap, (width / 8) * (height / 8) * 3);
+ }
+ }
+
+ CHECK_HEAP;
+ if (_vm->_features & GF_SMALL_HEADER) {
+ smap_ptr = ptr;
+ } else if (_vm->_version == 8) {
+ // Skip to the BSTR->WRAP->OFFS chunk
+ smap_ptr = ptr + 24;
+ } else
+ smap_ptr = _vm->findResource(MKID('SMAP'), ptr);
+
+ assert(smap_ptr);
+
+ numzbuf = getZPlanes(ptr, zplane_list, false);
+
+ const byte *tmsk_ptr = NULL;
+ if (_vm->_heversion >= 72) {
+ tmsk_ptr = _vm->findResource(MKID('TMSK'), ptr);
+ }
+
+ bottom = y + height;
+ if (bottom > vs->h) {
+ warning("Gdi::drawBitmap, strip drawn to %d below window bottom %d", bottom, vs->h);
+ }
+
+ _vertStripNextInc = height * vs->pitch - 1;
+
+ //
+ // Since V3, all graphics data was encoded in strips, which is very efficient
+ // for redrawing only parts of the screen. However, V2 is different: here
+ // the whole graphics are encoded as one big chunk. That makes it rather
+ // dificult to draw only parts of a room/object. We handle the V2 graphics
+ // differently from all other (newer) graphic formats for this reason.
+ //
+ if (_vm->_version == 2)
+ drawBitmapV2Helper(ptr, vs, x, y, width, height, stripnr, numstrip);
+
+ sx = x - vs->xstart / 8;
+ if (sx < 0) {
+ numstrip -= -sx;
+ x += -sx;
+ stripnr += -sx;
+ sx = 0;
+ }
+
+ //if (_vm->_NESStartStrip > 0)
+ // stripnr -= _vm->_NESStartStrip;
+
+ // Compute the number of strips we have to iterate over.
+ // TODO/FIXME: The computation of its initial value looks very fishy.
+ // It was added as a kind of hack to fix some corner cases, but it compares
+ // the room width to the virtual screen width; but the former should always
+ // be bigger than the latter (except for MM NES, maybe)... strange
+ int limit = MAX(_vm->_roomWidth, (int) vs->w) / 8 - x;
+ if (limit > numstrip)
+ limit = numstrip;
+ if (limit > _numStrips - sx)
+ limit = _numStrips - sx;
+ for (int k = 0; k < limit; ++k, ++stripnr) {
+ CHECK_HEAP;
+
+ if (y < vs->tdirty[sx + k])
+ vs->tdirty[sx + k] = y;
+
+ if (bottom > vs->bdirty[sx + k])
+ vs->bdirty[sx + k] = bottom;
+
+ // In the case of a double buffered virtual screen, we draw to
+ // the backbuffer, otherwise to the primary surface memory.
+ if (vs->hasTwoBuffers)
+ dstPtr = vs->backBuf + y * vs->pitch + (x + k) * 8;
+ else
+ dstPtr = (byte *)vs->pixels + y * vs->pitch + (x + k) * 8;
+
+ if (_vm->_version == 1) {
+ if (_vm->_platform == Common::kPlatformNES) {
+ mask_ptr = getMaskBuffer(x + k, y, 1);
+ drawStripNES(dstPtr, mask_ptr, vs->pitch, stripnr, y, height);
+ }
+ else if (_objectMode)
+ drawStripC64Object(dstPtr, vs->pitch, stripnr, width, height);
+ else
+ drawStripC64Background(dstPtr, vs->pitch, stripnr, height);
+ } else if (_vm->_version == 2) {
+ // Do nothing here for V2 games - drawing was already handled.
+ } else {
+ // Do some input verification and make sure the strip/strip offset
+ // are actually valid. Normally, this should never be a problem,
+ // but if e.g. a savegame gets corrupted, we can easily get into
+ // trouble here. See also bug #795214.
+ int offset = -1, smapLen;
+ if (_vm->_features & GF_16COLOR) {
+ smapLen = READ_LE_UINT16(smap_ptr);
+ if (stripnr * 2 + 2 < smapLen)
+ offset = READ_LE_UINT16(smap_ptr + stripnr * 2 + 2);
+ } else if (_vm->_features & GF_SMALL_HEADER) {
+ smapLen = READ_LE_UINT32(smap_ptr);
+ if (stripnr * 4 + 4 < smapLen)
+ offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 4);
+ } else {
+ smapLen = READ_BE_UINT32(smap_ptr);
+ if (stripnr * 4 + 8 < smapLen)
+ offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 8);
+ }
+ if (offset < 0 || offset >= smapLen) {
+ error("drawBitmap: Trying to draw a non-existant strip");
+ return;
+ }
+ transpStrip = decompressBitmap(dstPtr, vs->pitch, smap_ptr + offset, height);
+ }
+
+ CHECK_HEAP;
+ if (vs->hasTwoBuffers) {
+ byte *frontBuf = (byte *)vs->pixels + y * vs->pitch + (x + k) * 8;
+ if (lightsOn)
+ copy8Col(frontBuf, vs->pitch, dstPtr, height);
+ else
+ clear8Col(frontBuf, vs->pitch, height);
+ }
+ CHECK_HEAP;
+
+ // COMI and HE games only uses flag value
+ if (_vm->_version == 8 || _vm->_heversion >= 60)
+ transpStrip = true;
+
+ if (_vm->_version == 1) {
+ mask_ptr = getMaskBuffer(x + k, y, 1);
+ if (_vm->_platform == Common::kPlatformNES) {
+ drawStripNESMask(mask_ptr, stripnr, y, height);
+ } else {
+ drawStripC64Mask(mask_ptr, stripnr, width, height);
+ }
+ } else if (_vm->_version == 2) {
+ // Do nothing here for V2 games - zplane was already handled.
+ } else if (flag & dbDrawMaskOnAll) {
+ // Sam & Max uses dbDrawMaskOnAll for things like the inventory
+ // box and the speech icons. While these objects only have one
+ // mask, it should be applied to all the Z-planes in the room,
+ // i.e. they should mask every actor.
+ //
+ // This flag used to be called dbDrawMaskOnBoth, and all it
+ // would do was to mask Z-plane 0. (Z-plane 1 would also be
+ // masked, because what is now the else-clause used to be run
+ // always.) While this seems to be the only way there is to
+ // mask Z-plane 0, this wasn't good enough since actors in
+ // Z-planes >= 2 would not be masked.
+ //
+ // The flag is also used by The Dig and Full Throttle, but I
+ // don't know what for. At the time of writing, these games
+ // are still too unstable for me to investigate.
+
+ if (_vm->_version == 8)
+ z_plane_ptr = zplane_list[1] + READ_LE_UINT32(zplane_list[1] + stripnr * 4 + 8);
+ else
+ z_plane_ptr = zplane_list[1] + READ_LE_UINT16(zplane_list[1] + stripnr * 2 + 8);
+ for (i = 0; i < numzbuf; i++) {
+ mask_ptr = getMaskBuffer(x + k, y, i);
+ if (transpStrip && (flag & dbAllowMaskOr))
+ decompressMaskImgOr(mask_ptr, z_plane_ptr, height);
+ else
+ decompressMaskImg(mask_ptr, z_plane_ptr, height);
+ }
+ } else {
+ for (i = 1; i < numzbuf; i++) {
+ uint32 offs;
+
+ if (!zplane_list[i])
+ continue;
+
+ if (_vm->_features & GF_OLD_BUNDLE)
+ offs = READ_LE_UINT16(zplane_list[i] + stripnr * 2);
+ else if (_vm->_features & GF_OLD256)
+ offs = READ_LE_UINT16(zplane_list[i] + stripnr * 2 + 4);
+ else if (_vm->_features & GF_SMALL_HEADER)
+ offs = READ_LE_UINT16(zplane_list[i] + stripnr * 2 + 2);
+ else if (_vm->_version == 8)
+ offs = READ_LE_UINT32(zplane_list[i] + stripnr * 4 + 8);
+ else
+ offs = READ_LE_UINT16(zplane_list[i] + stripnr * 2 + 8);
+
+ mask_ptr = getMaskBuffer(x + k, y, i);
+
+ if (offs) {
+ z_plane_ptr = zplane_list[i] + offs;
+
+ if (tmsk_ptr) {
+ const byte *tmsk = tmsk_ptr + READ_LE_UINT16(tmsk_ptr + 8);
+ decompressTMSK(mask_ptr, tmsk, z_plane_ptr, height);
+ } else if (transpStrip && (flag & dbAllowMaskOr)) {
+ decompressMaskImgOr(mask_ptr, z_plane_ptr, height);
+ } else {
+ decompressMaskImg(mask_ptr, z_plane_ptr, height);
+ }
+
+ } else {
+ if (!(transpStrip && (flag & dbAllowMaskOr)))
+ for (int h = 0; h < height; h++)
+ mask_ptr[h * _numStrips] = 0;
+ // FIXME: needs better abstraction
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Draw a bitmap onto a virtual screen. This is main drawing method for room backgrounds
+ * used throughout in 7.2+ HE versions.
+ *
+ * @note This function essentially is a stripped down & special cased version of
+ * the generic Gdi::drawBitmap() method.
+ */
+void Gdi::drawBMAPBg(const byte *ptr, VirtScreen *vs) {
+ const byte *z_plane_ptr;
+ byte *mask_ptr;
+ const byte *zplane_list[9];
+
+ const byte *bmap_ptr = _vm->findResourceData(MKID('BMAP'), ptr);
+ assert(bmap_ptr);
+
+ byte code = *bmap_ptr++;
+ int scrX = _vm->_screenStartStrip * 8;
+ byte *dst = (byte *)_vm->virtscr[0].backBuf + scrX;
+
+ // The following few lines more or less duplicate decompressBitmap(), only
+ // for an area spanning multiple strips. In particular, the codecs 13 & 14
+ // in decompressBitmap call drawStripHE()
+ _decomp_shr = code % 10;
+ _decomp_mask = 0xFF >> (8 - _decomp_shr);
+
+ switch (code) {
+ case 134:
+ case 135:
+ case 136:
+ case 137:
+ case 138:
+ drawStripHE(dst, vs->pitch, bmap_ptr, vs->w, vs->h, false);
+ break;
+ case 144:
+ case 145:
+ case 146:
+ case 147:
+ case 148:
+ drawStripHE(dst, vs->pitch, bmap_ptr, vs->w, vs->h, true);
+ break;
+ case 150:
+ fill(dst, vs->pitch, *bmap_ptr, vs->w, vs->h);
+ break;
+ default:
+ // Alternative russian freddi3 uses badly formatted bitmaps
+ debug(0, "Gdi::drawBMAPBg: default case %d", code);
+ }
+
+ copyVirtScreenBuffers(Common::Rect(vs->w, vs->h));
+
+ int numzbuf = getZPlanes(ptr, zplane_list, true);
+ if (numzbuf <= 1)
+ return;
+
+ uint32 offs;
+ for (int stripnr = 0; stripnr < _numStrips; stripnr++)
+ for (int i = 1; i < numzbuf; i++) {
+ if (!zplane_list[i])
+ continue;
+
+ offs = READ_LE_UINT16(zplane_list[i] + stripnr * 2 + 8);
+ mask_ptr = getMaskBuffer(stripnr, 0, i);
+
+ if (offs) {
+ z_plane_ptr = zplane_list[i] + offs;
+ decompressMaskImg(mask_ptr, z_plane_ptr, vs->h);
+ }
+ }
+}
+
+void Gdi::drawBMAPObject(const byte *ptr, VirtScreen *vs, int obj, int x, int y, int w, int h) {
+#ifndef DISABLE_HE
+ const byte *bmap_ptr = _vm->findResourceData(MKID('BMAP'), ptr);
+ assert(bmap_ptr);
+
+ byte code = *bmap_ptr++;
+ int scrX = _vm->_screenStartStrip * 8;
+
+ if (code == 8 || code == 9) {
+ Common::Rect rScreen(0, 0, vs->w, vs->h);
+ byte *dst = (byte *)_vm->virtscr[0].backBuf + scrX;
+ Wiz::copyWizImage(dst, bmap_ptr, vs->w, vs->h, x - scrX, y, w, h, &rScreen);
+ }
+
+ Common::Rect rect1(x, y, x + w, y + h);
+ Common::Rect rect2(scrX, 0, vs->w + scrX, vs->h);
+
+ if (rect1.intersects(rect2)) {
+ rect1.clip(rect2);
+ rect1.left -= rect2.left;
+ rect1.right -= rect2.left;
+ rect1.top -= rect2.top;
+ rect1.bottom -= rect2.top;
+
+ copyVirtScreenBuffers(rect1);
+ }
+#endif
+}
+
+void Gdi::copyVirtScreenBuffers(Common::Rect rect, int dirtybit) {
+ byte *src, *dst;
+ VirtScreen *vs = &_vm->virtscr[0];
+
+ debug(1,"copyVirtScreenBuffers: Left %d Right %d Top %d Bottom %d", rect.left, rect.right, rect.top, rect.bottom);
+
+ if (rect.top > vs->h || rect.bottom < 0)
+ return;
+
+ if (rect.left > vs->w || rect.right < 0)
+ return;
+
+ rect.left = MAX(0, (int)rect.left);
+ rect.left = MIN((int)rect.left, (int)vs->w - 1);
+
+ rect.right = MAX(0, (int)rect.right);
+ rect.right = MIN((int)rect.right, (int)vs->w);
+
+ rect.top = MAX(0, (int)rect.top);
+ rect.top = MIN((int)rect.top, (int)vs->h - 1);
+
+ rect.bottom = MAX(0, (int)rect.bottom);
+ rect.bottom = MIN((int)rect.bottom, (int)vs->h);
+
+ const int rw = rect.width();
+ const int rh = rect.height();
+
+ if (rw == 0 || rh == 0)
+ return;
+
+ src = _vm->virtscr[0].getBackPixels(rect.left, rect.top);
+ dst = _vm->virtscr[0].getPixels(rect.left, rect.top);
+
+ assert(rw <= _vm->_screenWidth && rw > 0);
+ assert(rh <= _vm->_screenHeight && rh > 0);
+ blit(dst, _vm->virtscr[0].pitch, src, _vm->virtscr[0].pitch, rw, rh);
+ _vm->markRectAsDirty(kMainVirtScreen, rect, dirtybit);
+}
+
+/**
+ * Reset the background behind an actor or blast object.
+ */
+void Gdi::resetBackground(int top, int bottom, int strip) {
+ VirtScreen *vs = &_vm->virtscr[0];
+ byte *backbuff_ptr, *bgbak_ptr;
+ int numLinesToProcess;
+
+ if (top < 0)
+ top = 0;
+
+ if (bottom > vs->h)
+ bottom = vs->h;
+
+ if (top >= bottom)
+ return;
+
+ assert(0 <= strip && strip < _numStrips);
+
+ if (top < vs->tdirty[strip])
+ vs->tdirty[strip] = top;
+
+ if (bottom > vs->bdirty[strip])
+ vs->bdirty[strip] = bottom;
+
+ bgbak_ptr = (byte *)vs->backBuf + top * vs->pitch + (strip + vs->xstart/8) * 8;
+ backbuff_ptr = (byte *)vs->pixels + top * vs->pitch + (strip + vs->xstart/8) * 8;
+
+ numLinesToProcess = bottom - top;
+ if (numLinesToProcess) {
+ if (_vm->isLightOn()) {
+ copy8Col(backbuff_ptr, vs->pitch, bgbak_ptr, numLinesToProcess);
+ } else {
+ clear8Col(backbuff_ptr, vs->pitch, numLinesToProcess);
+ }
+ }
+}
+
+bool Gdi::decompressBitmap(byte *dst, int dstPitch, const byte *src, int numLinesToProcess) {
+ assert(numLinesToProcess);
+
+ if (_vm->_features & GF_16COLOR) {
+ drawStripEGA(dst, dstPitch, src, numLinesToProcess);
+ return false;
+ }
+
+ if ((_vm->_platform == Common::kPlatformAmiga) && (_vm->_version >= 4))
+ _paletteMod = 16;
+ else
+ _paletteMod = 0;
+
+ byte code = *src++;
+ bool transpStrip = false;
+ _decomp_shr = code % 10;
+ _decomp_mask = 0xFF >> (8 - _decomp_shr);
+
+ switch (code) {
+ case 1:
+ drawStripRaw(dst, dstPitch, src, numLinesToProcess, false);
+ break;
+
+ case 2:
+ unkDecode8(dst, dstPitch, src, numLinesToProcess); /* Ender - Zak256/Indy256 */
+ break;
+
+ case 3:
+ unkDecode9(dst, dstPitch, src, numLinesToProcess); /* Ender - Zak256/Indy256 */
+ break;
+
+ case 4:
+ unkDecode10(dst, dstPitch, src, numLinesToProcess); /* Ender - Zak256/Indy256 */
+ break;
+
+ case 7:
+ unkDecode11(dst, dstPitch, src, numLinesToProcess); /* Ender - Zak256/Indy256 */
+ break;
+
+ case 8:
+ // Used in 3DO versions of HE games
+ transpStrip = true;
+ drawStrip3DO(dst, dstPitch, src, numLinesToProcess, true);
+ break;
+
+ case 9:
+ drawStrip3DO(dst, dstPitch, src, numLinesToProcess, false);
+ break;
+
+ case 10:
+ // Used in Amiga version of Monkey Island 1
+ drawStripEGA(dst, dstPitch, src, numLinesToProcess);
+ break;
+
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ drawStripBasicV(dst, dstPitch, src, numLinesToProcess, false);
+ break;
+
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ drawStripBasicH(dst, dstPitch, src, numLinesToProcess, false);
+ break;
+
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ transpStrip = true;
+ drawStripBasicV(dst, dstPitch, src, numLinesToProcess, true);
+ break;
+
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ case 48:
+ transpStrip = true;
+ drawStripBasicH(dst, dstPitch, src, numLinesToProcess, true);
+ break;
+
+ case 64:
+ case 65:
+ case 66:
+ case 67:
+ case 68:
+ case 104:
+ case 105:
+ case 106:
+ case 107:
+ case 108:
+ drawStripComplex(dst, dstPitch, src, numLinesToProcess, false);
+ break;
+
+ case 84:
+ case 85:
+ case 86:
+ case 87:
+ case 88:
+ case 124:
+ case 125:
+ case 126:
+ case 127:
+ case 128:
+ transpStrip = true;
+ drawStripComplex(dst, dstPitch, src, numLinesToProcess, true);
+ break;
+
+ case 134:
+ case 135:
+ case 136:
+ case 137:
+ case 138:
+ drawStripHE(dst, dstPitch, src, 8, numLinesToProcess, false);
+ break;
+
+ case 143: // Triggered by Russian water
+ case 144:
+ case 145:
+ case 146:
+ case 147:
+ case 148:
+ transpStrip = true;
+ drawStripHE(dst, dstPitch, src, 8, numLinesToProcess, true);
+ break;
+
+ case 149:
+ drawStripRaw(dst, dstPitch, src, numLinesToProcess, true);
+ break;
+
+ default:
+ error("Gdi::decompressBitmap: default case %d", code);
+ }
+
+ return transpStrip;
+}
+
+void Gdi::decompressMaskImg(byte *dst, const byte *src, int height) const {
+ byte b, c;
+
+ while (height) {
+ b = *src++;
+
+ if (b & 0x80) {
+ b &= 0x7F;
+ c = *src++;
+
+ do {
+ *dst = c;
+ dst += _numStrips;
+ --height;
+ } while (--b && height);
+ } else {
+ do {
+ *dst = *src++;
+ dst += _numStrips;
+ --height;
+ } while (--b && height);
+ }
+ }
+}
+
+void Gdi::decompressTMSK(byte *dst, const byte *tmsk, const byte *src, int height) const {
+ byte srcbits = 0;
+ byte srcFlag = 0;
+ byte maskFlag = 0;
+
+ byte srcCount = 0;
+ byte maskCount = 0;
+ byte maskbits = 0;
+
+ while (height) {
+ if (srcCount == 0) {
+ srcCount = *src++;
+ srcFlag = srcCount & 0x80;
+ if (srcFlag) {
+ srcCount &= 0x7F;
+ srcbits = *src++;
+ }
+ }
+
+ if (srcFlag == 0) {
+ srcbits = *src++;
+ }
+
+ srcCount--;
+
+ if (maskCount == 0) {
+ maskCount = *tmsk++;
+ maskFlag = maskCount & 0x80;
+ if (maskFlag) {
+ maskCount &= 0x7F;
+ maskbits = *tmsk++;
+ }
+ }
+
+ if (maskFlag == 0) {
+ maskbits = *tmsk++;
+ }
+
+ maskCount--;
+
+ *dst |= srcbits;
+ *dst &= ~maskbits;
+
+ dst += _numStrips;
+ height--;
+ }
+}
+
+void Gdi::decompressMaskImgOr(byte *dst, const byte *src, int height) const {
+ byte b, c;
+
+ while (height) {
+ b = *src++;
+
+ if (b & 0x80) {
+ b &= 0x7F;
+ c = *src++;
+
+ do {
+ *dst |= c;
+ dst += _numStrips;
+ --height;
+ } while (--b && height);
+ } else {
+ do {
+ *dst |= *src++;
+ dst += _numStrips;
+ --height;
+ } while (--b && height);
+ }
+ }
+}
+
+void decodeNESTileData(const byte *src, byte *dest) {
+ int len = READ_LE_UINT16(src); src += 2;
+ const byte *end = src + len;
+ src++; // skip number-of-tiles byte, assume it is correct
+ while (src < end) {
+ byte data = *src++;
+ for (int j = 0; j < (data & 0x7F); j++)
+ *dest++ = (data & 0x80) ? (*src++) : (*src);
+ if (!(data & 0x80))
+ src++;
+ }
+}
+
+void ScummEngine::decodeNESBaseTiles() {
+ byte *basetiles = getResourceAddress(rtCostume, 37);
+ _NESBaseTiles = basetiles[2];
+ decodeNESTileData(basetiles, _NESPatTable[1]);
+}
+
+static const int v1MMNEScostTables[2][6] = {
+ /* desc lens offs data gfx pal */
+ { 25, 27, 29, 31, 33, 35},
+ { 26, 28, 30, 32, 34, 36}
+};
+void ScummEngine::NES_loadCostumeSet(int n) {
+ int i;
+ _NESCostumeSet = n;
+
+ _NEScostdesc = getResourceAddress(rtCostume, v1MMNEScostTables[n][0]) + 2;
+ _NEScostlens = getResourceAddress(rtCostume, v1MMNEScostTables[n][1]) + 2;
+ _NEScostoffs = getResourceAddress(rtCostume, v1MMNEScostTables[n][2]) + 2;
+ _NEScostdata = getResourceAddress(rtCostume, v1MMNEScostTables[n][3]) + 2;
+ decodeNESTileData(getResourceAddress(rtCostume, v1MMNEScostTables[n][4]), _NESPatTable[0]);
+ byte *palette = getResourceAddress(rtCostume, v1MMNEScostTables[n][5]) + 2;
+ for (i = 0; i < 16; i++) {
+ byte c = *palette++;
+ if (c == 0x1D) // HACK - switch around colors 0x00 and 0x1D
+ c = 0; // so we don't need a zillion extra checks
+ else if (c == 0)// for determining the proper background color
+ c = 0x1D;
+ _NESPalette[1][i] = c;
+ }
+
+}
+
+void Gdi::decodeNESGfx(const byte *room) {
+ const byte *gdata = room + READ_LE_UINT16(room + 0x0A);
+ int tileset = *gdata++;
+ int width = READ_LE_UINT16(room + 0x04);
+ // int height = READ_LE_UINT16(room + 0x06);
+ int i, j, n;
+
+ // We have narrow room. so expand it
+ if (width < 32) {
+ _vm->_NESStartStrip = (32 - width) >> 1;
+ } else {
+ _vm->_NESStartStrip = 0;
+ }
+
+ decodeNESTileData(_vm->getResourceAddress(rtCostume, 37 + tileset), _vm->_NESPatTable[1] + _vm->_NESBaseTiles * 16);
+ for (i = 0; i < 16; i++) {
+ byte c = *gdata++;
+ if (c == 0x0D)
+ c = 0x1D;
+
+ if (c == 0x1D) // HACK - switch around colors 0x00 and 0x1D
+ c = 0; // so we don't need a zillion extra checks
+ else if (c == 0) // for determining the proper background color
+ c = 0x1D;
+
+ _vm->_NESPalette[0][i] = c;
+ }
+ for (i = 0; i < 16; i++) {
+ _NES.nametable[i][0] = _NES.nametable[i][1] = 0;
+ n = 0;
+ while (n < width) {
+ byte data = *gdata++;
+ for (j = 0; j < (data & 0x7F); j++)
+ _NES.nametable[i][2 + n++] = (data & 0x80) ? (*gdata++) : (*gdata);
+ if (!(data & 0x80))
+ gdata++;
+ }
+ _NES.nametable[i][width+2] = _NES.nametable[i][width+3] = 0;
+ }
+ memcpy(_NES.nametableObj,_NES.nametable, 16*64);
+
+ const byte *adata = room + READ_LE_UINT16(room + 0x0C);
+ for (n = 0; n < 64;) {
+ byte data = *adata++;
+ for (j = 0; j < (data & 0x7F); j++)
+ _NES.attributes[n++] = (data & 0x80) ? (*adata++) : (*adata);
+ if (!(n & 7) && (width == 0x1C))
+ n += 8;
+ if (!(data & 0x80))
+ adata++;
+ }
+ memcpy(_NES.attributesObj, _NES.attributes, 64);
+
+ const byte *mdata = room + READ_LE_UINT16(room + 0x0E);
+ int mask = *mdata++;
+ if (mask == 0) {
+ _NES.hasmask = false;
+ return;
+ }
+ _NES.hasmask = true;
+ if (mask != 1)
+ debug(0,"NES room %i has irregular mask count %i!",_vm->_currentRoom,mask);
+ int mwidth = *mdata++;
+ for (i = 0; i < 16; i++) {
+ n = 0;
+ while (n < mwidth) {
+ byte data = *mdata++;
+ for (j = 0; j < (data & 0x7F); j++)
+ _NES.masktable[i][n++] = (data & 0x80) ? (*mdata++) : (*mdata);
+ if (!(data & 0x80))
+ mdata++;
+ }
+ }
+ memcpy(_NES.masktableObj, _NES.masktable, 16*8);
+}
+
+void Gdi::decodeNESObject(const byte *ptr, int xpos, int ypos, int width, int height) {
+ int x, y;
+
+ _NES.objX = xpos;
+
+ // decode tile update data
+ width /= 8;
+ ypos /= 8;
+ height /= 8;
+
+ for (y = ypos; y < ypos + height; y++) {
+ x = xpos;
+ while (x < xpos + width) {
+ byte len = *ptr++;
+ for (int i = 0; i < (len & 0x7F); i++)
+ _NES.nametableObj[y][2 + x++] = (len & 0x80) ? (*ptr++) : (*ptr);
+ if (!(len & 0x80))
+ ptr++;
+ }
+ }
+
+ int ax, ay;
+ // decode attribute update data
+ y = height / 2;
+ ay = ypos;
+ while (y) {
+ ax = xpos + 2;
+ x = 0;
+ int adata = 0;
+ while (x < (width >> 1)) {
+ if (!(x & 3))
+ adata = *ptr++;
+ byte *dest = &_NES.attributesObj[((ay << 2) & 0x30) | ((ax >> 2) & 0xF)];
+
+ int aand = 3;
+ int aor = adata & 3;
+ if (ay & 0x02) {
+ aand <<= 4;
+ aor <<= 4;
+ }
+ if (ax & 0x02) {
+ aand <<= 2;
+ aor <<= 2;
+ }
+ *dest = ((~aand) & *dest) | aor;
+
+ adata >>= 2;
+ ax += 2;
+ x++;
+ }
+ ay += 2;
+ y--;
+ }
+
+ // decode mask update data
+ if (!_NES.hasmask)
+ return;
+ int mx, mwidth;
+ int lmask, rmask;
+ mx = *ptr++;
+ mwidth = *ptr++;
+ lmask = *ptr++;
+ rmask = *ptr++;
+
+ y = 0;
+ do {
+ byte *dest = &_NES.masktableObj[y + ypos][mx];
+ *dest = (*dest & lmask) | *ptr++;
+ dest++;
+ for (x = 1; x < mwidth; x++) {
+ if (x + 1 == mwidth)
+ *dest = (*dest & rmask) | *ptr++;
+ else
+ *dest = *ptr++;
+ dest++;
+ }
+ y++;
+ } while (y < height);
+}
+
+void Gdi::drawStripNES(byte *dst, byte *mask, int dstPitch, int stripnr, int top, int height) {
+ top /= 8;
+ height /= 8;
+ int x = stripnr + 2; // NES version has a 2 tile gap on each edge
+
+ if (_objectMode)
+ x += _NES.objX; // for objects, need to start at the left edge of the object, not the screen
+ if (x > 63) {
+ debug(0,"NES tried to render invalid strip %i",stripnr);
+ return;
+ }
+ for (int y = top; y < top + height; y++) {
+ int palette = ((_objectMode ? _NES.attributesObj : _NES.attributes)[((y << 2) & 0x30) | ((x >> 2) & 0xF)] >> (((y & 2) << 1) | (x & 2))) & 0x3;
+ int tile = (_objectMode ? _NES.nametableObj : _NES.nametable)[y][x];
+
+ for (int i = 0; i < 8; i++) {
+ byte c0 = _vm->_NESPatTable[1][tile * 16 + i];
+ byte c1 = _vm->_NESPatTable[1][tile * 16 + i + 8];
+ for (int j = 0; j < 8; j++)
+ dst[j] = _vm->_NESPalette[0][((c0 >> (7 - j)) & 1) | (((c1 >> (7 - j)) & 1) << 1) | (palette << 2)];
+ dst += dstPitch;
+ *mask = c0 | c1;
+ mask += _numStrips;
+ }
+ }
+}
+
+void Gdi::drawStripNESMask(byte *dst, int stripnr, int top, int height) const {
+ top /= 8;
+ height /= 8;
+ int x = stripnr; // masks, unlike room graphics, should NOT be adjusted
+
+ if (_objectMode)
+ x += _NES.objX; // for objects, need to start at the left edge of the object, not the screen
+ if (x > 63) {
+ debug(0,"NES tried to mask invalid strip %i",stripnr);
+ return;
+ }
+ for (int y = top; y < top + height; y++) {
+ byte c;
+ if (_NES.hasmask)
+ c = (((_objectMode ? _NES.masktableObj : _NES.masktable)[y][x >> 3] >> (x & 7)) & 1) ? 0xFF : 0x00;
+ else
+ c = 0;
+
+ for (int i = 0; i < 8; i++) {
+ *dst &= c;
+ dst += _numStrips;
+ }
+ }
+}
+
+void Gdi::drawStripC64Background(byte *dst, int dstPitch, int stripnr, int height) {
+ int charIdx;
+ height /= 8;
+ for (int y = 0; y < height; y++) {
+ _C64.colors[3] = (_C64.colorMap[y + stripnr * height] & 7);
+ // Check for room color change in V1 zak
+ if (_roomPalette[0] == 255) {
+ _C64.colors[2] = _roomPalette[2];
+ _C64.colors[1] = _roomPalette[1];
+ }
+
+ charIdx = _C64.picMap[y + stripnr * height] * 8;
+ for (int i = 0; i < 8; i++) {
+ byte c = _C64.charMap[charIdx + i];
+ dst[0] = dst[1] = _C64.colors[(c >> 6) & 3];
+ dst[2] = dst[3] = _C64.colors[(c >> 4) & 3];
+ dst[4] = dst[5] = _C64.colors[(c >> 2) & 3];
+ dst[6] = dst[7] = _C64.colors[(c >> 0) & 3];
+ dst += dstPitch;
+ }
+ }
+}
+
+void Gdi::drawStripC64Object(byte *dst, int dstPitch, int stripnr, int width, int height) {
+ int charIdx;
+ height /= 8;
+ width /= 8;
+ for (int y = 0; y < height; y++) {
+ _C64.colors[3] = (_C64.objectMap[(y + height) * width + stripnr] & 7);
+ charIdx = _C64.objectMap[y * width + stripnr] * 8;
+ for (int i = 0; i < 8; i++) {
+ byte c = _C64.charMap[charIdx + i];
+ dst[0] = dst[1] = _C64.colors[(c >> 6) & 3];
+ dst[2] = dst[3] = _C64.colors[(c >> 4) & 3];
+ dst[4] = dst[5] = _C64.colors[(c >> 2) & 3];
+ dst[6] = dst[7] = _C64.colors[(c >> 0) & 3];
+ dst += dstPitch;
+ }
+ }
+}
+
+void Gdi::drawStripC64Mask(byte *dst, int stripnr, int width, int height) const {
+ int maskIdx;
+ height /= 8;
+ width /= 8;
+ for (int y = 0; y < height; y++) {
+ if (_objectMode)
+ maskIdx = _C64.objectMap[(y + 2 * height) * width + stripnr] * 8;
+ else
+ maskIdx = _C64.maskMap[y + stripnr * height] * 8;
+ for (int i = 0; i < 8; i++) {
+ byte c = _C64.maskChar[maskIdx + i];
+
+ // V1/C64 masks are inverted compared to what ScummVM expects
+ *dst = c ^ 0xFF;
+ dst += _numStrips;
+ }
+ }
+}
+
+void Gdi::decodeC64Gfx(const byte *src, byte *dst, int size) const {
+ int x, z;
+ byte color, run, common[4];
+
+ for (z = 0; z < 4; z++) {
+ common[z] = *src++;
+ }
+
+ x = 0;
+ while (x < size) {
+ run = *src++;
+ if (run & 0x80) {
+ color = common[(run >> 5) & 3];
+ run &= 0x1F;
+ for (z = 0; z <= run; z++) {
+ dst[x++] = color;
+ }
+ } else if (run & 0x40) {
+ run &= 0x3F;
+ color = *src++;
+ for (z = 0; z <= run; z++) {
+ dst[x++] = color;
+ }
+ } else {
+ for (z = 0; z <= run; z++) {
+ dst[x++] = *src++;
+ }
+ }
+ }
+}
+
+/**
+ * Create and fill a table with offsets to the graphic and mask strips in the
+ * given V2 EGA bitmap.
+ * @param src the V2 EGA bitmap
+ * @param width the width of the bitmap
+ * @param height the height of the bitmap
+ * @param table the strip table to fill
+ * @return filled strip table
+ */
+StripTable *Gdi::generateStripTable(const byte *src, int width, int height, StripTable *table) const {
+
+ // If no strip table was given to use, allocate a new one
+ if (table == 0)
+ table = (StripTable *)calloc(1, sizeof(StripTable));
+
+ const byte *bitmapStart = src;
+ byte color = 0, data = 0;
+ int x, y, length = 0;
+ byte run = 1;
+
+ // Decode the graphics strips, and memorize the run/color values
+ // as well as the byte offset.
+ for (x = 0 ; x < width; x++) {
+
+ if ((x % 8) == 0) {
+ assert(x / 8 < 160);
+ table->run[x / 8] = run;
+ table->color[x / 8] = color;
+ table->offsets[x / 8] = src - bitmapStart;
+ }
+
+ for (y = 0; y < height; y++) {
+ if (--run == 0) {
+ data = *src++;
+ if (data & 0x80) {
+ run = data & 0x7f;
+ } else {
+ run = data >> 4;
+ }
+ if (run == 0) {
+ run = *src++;
+ }
+ color = data & 0x0f;
+ }
+ }
+ }
+
+ // The mask data follows immediately after the graphics.
+ x = 0;
+ y = height;
+ width /= 8;
+
+ for (;;) {
+ length = *src++;
+ const byte runFlag = length & 0x80;
+ if (runFlag) {
+ length &= 0x7f;
+ data = *src++;
+ }
+ do {
+ if (!runFlag)
+ data = *src++;
+ if (y == height) {
+ assert(x < 120);
+ table->zoffsets[x] = src - bitmapStart - 1;
+ table->zrun[x] = length | runFlag;
+ }
+ if (--y == 0) {
+ if (--width == 0)
+ return table;
+ x++;
+ y = height;
+ }
+ } while (--length);
+ }
+
+ return table;
+}
+
+void Gdi::drawStripEGA(byte *dst, int dstPitch, const byte *src, int height) const {
+ byte color = 0;
+ int run = 0, x = 0, y = 0, z;
+
+ while (x < 8) {
+ color = *src++;
+
+ if (color & 0x80) {
+ run = color & 0x3f;
+
+ if (color & 0x40) {
+ color = *src++;
+
+ if (run == 0) {
+ run = *src++;
+ }
+ for (z = 0; z < run; z++) {
+ *(dst + y * dstPitch + x) = (z & 1) ? _roomPalette[color & 0xf] + _paletteMod : _roomPalette[color >> 4] + _paletteMod;
+
+ y++;
+ if (y >= height) {
+ y = 0;
+ x++;
+ }
+ }
+ } else {
+ if (run == 0) {
+ run = *src++;
+ }
+
+ for (z = 0; z < run; z++) {
+ *(dst + y * dstPitch + x) = *(dst + y * dstPitch + x - 1);
+
+ y++;
+ if (y >= height) {
+ y = 0;
+ x++;
+ }
+ }
+ }
+ } else {
+ run = color >> 4;
+ if (run == 0) {
+ run = *src++;
+ }
+
+ for (z = 0; z < run; z++) {
+ *(dst + y * dstPitch + x) = _roomPalette[color & 0xf] + _paletteMod;
+
+ y++;
+ if (y >= height) {
+ y = 0;
+ x++;
+ }
+ }
+ }
+ }
+}
+
+#define READ_BIT (shift--, dataBit = data & 1, data >>= 1, dataBit)
+#define FILL_BITS(n) do { \
+ if (shift < n) { \
+ data |= *src++ << shift; \
+ shift += 8; \
+ } \
+ } while (0)
+
+// NOTE: drawStripHE is actually very similar to drawStripComplex
+void Gdi::drawStripHE(byte *dst, int dstPitch, const byte *src, int width, int height, const bool transpCheck) const {
+ static const int delta_color[] = { -4, -3, -2, -1, 1, 2, 3, 4 };
+ uint32 dataBit, data;
+ byte color;
+ int shift;
+
+ color = *src++;
+ data = READ_LE_UINT24(src);
+ src += 3;
+ shift = 24;
+
+ int x = width;
+ while (1) {
+ if (!transpCheck || color != _transparentColor)
+ *dst = _roomPalette[color];
+ dst++;
+ --x;
+ if (x == 0) {
+ x = width;
+ dst += dstPitch - width;
+ --height;
+ if (height == 0)
+ return;
+ }
+ FILL_BITS(1);
+ if (READ_BIT) {
+ FILL_BITS(1);
+ if (READ_BIT) {
+ FILL_BITS(3);
+ color += delta_color[data & 7];
+ shift -= 3;
+ data >>= 3;
+ } else {
+ FILL_BITS(_decomp_shr);
+ color = data & _decomp_mask;
+ shift -= _decomp_shr;
+ data >>= _decomp_shr;
+ }
+ }
+ }
+}
+
+#undef READ_BIT
+#undef FILL_BITS
+
+
+void Gdi::drawStrip3DO(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const {
+ if (height == 0)
+ return;
+
+ int decSize = height * 8;
+ int curSize = 0;
+
+ do {
+ uint8 data = *src++;
+ uint8 rle = data & 1;
+ int len = (data >> 1) + 1;
+
+ len = MIN(decSize, len);
+ decSize -= len;
+
+ if (!rle) {
+ for (; len > 0; len--, src++, dst++) {
+ if (!transpCheck || *src != _transparentColor)
+ *dst = _roomPalette[*src];
+ curSize++;
+ if (!(curSize & 7))
+ dst += dstPitch - 8; // Next row
+ }
+ } else {
+ byte color = *src++;
+ for (; len > 0; len--, dst++) {
+ if (!transpCheck || color != _transparentColor)
+ *dst = _roomPalette[color];
+ curSize++;
+ if (!(curSize & 7))
+ dst += dstPitch - 8; // Next row
+ }
+ }
+ } while (decSize > 0);
+}
+
+
+#define READ_BIT (cl--, bit = bits & 1, bits >>= 1, bit)
+#define FILL_BITS do { \
+ if (cl <= 8) { \
+ bits |= (*src++ << cl); \
+ cl += 8; \
+ } \
+ } while (0)
+
+void Gdi::drawStripComplex(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const {
+ byte color = *src++;
+ uint bits = *src++;
+ byte cl = 8;
+ byte bit;
+ byte incm, reps;
+
+ do {
+ int x = 8;
+ do {
+ FILL_BITS;
+ if (!transpCheck || color != _transparentColor)
+ *dst = _roomPalette[color] + _paletteMod;
+ dst++;
+
+ againPos:
+ if (!READ_BIT) {
+ } else if (!READ_BIT) {
+ FILL_BITS;
+ color = bits & _decomp_mask;
+ bits >>= _decomp_shr;
+ cl -= _decomp_shr;
+ } else {
+ incm = (bits & 7) - 4;
+ cl -= 3;
+ bits >>= 3;
+ if (incm) {
+ color += incm;
+ } else {
+ FILL_BITS;
+ reps = bits & 0xFF;
+ do {
+ if (!--x) {
+ x = 8;
+ dst += dstPitch - 8;
+ if (!--height)
+ return;
+ }
+ if (!transpCheck || color != _transparentColor)
+ *dst = _roomPalette[color] + _paletteMod;
+ dst++;
+ } while (--reps);
+ bits >>= 8;
+ bits |= (*src++) << (cl - 8);
+ goto againPos;
+ }
+ }
+ } while (--x);
+ dst += dstPitch - 8;
+ } while (--height);
+}
+
+void Gdi::drawStripBasicH(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const {
+ byte color = *src++;
+ uint bits = *src++;
+ byte cl = 8;
+ byte bit;
+ int8 inc = -1;
+
+ do {
+ int x = 8;
+ do {
+ FILL_BITS;
+ if (!transpCheck || color != _transparentColor)
+ *dst = _roomPalette[color] + _paletteMod;
+ dst++;
+ if (!READ_BIT) {
+ } else if (!READ_BIT) {
+ FILL_BITS;
+ color = bits & _decomp_mask;
+ bits >>= _decomp_shr;
+ cl -= _decomp_shr;
+ inc = -1;
+ } else if (!READ_BIT) {
+ color += inc;
+ } else {
+ inc = -inc;
+ color += inc;
+ }
+ } while (--x);
+ dst += dstPitch - 8;
+ } while (--height);
+}
+
+void Gdi::drawStripBasicV(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const {
+ byte color = *src++;
+ uint bits = *src++;
+ byte cl = 8;
+ byte bit;
+ int8 inc = -1;
+
+ int x = 8;
+ do {
+ int h = height;
+ do {
+ FILL_BITS;
+ if (!transpCheck || color != _transparentColor)
+ *dst = _roomPalette[color] + _paletteMod;
+ dst += dstPitch;
+ if (!READ_BIT) {
+ } else if (!READ_BIT) {
+ FILL_BITS;
+ color = bits & _decomp_mask;
+ bits >>= _decomp_shr;
+ cl -= _decomp_shr;
+ inc = -1;
+ } else if (!READ_BIT) {
+ color += inc;
+ } else {
+ inc = -inc;
+ color += inc;
+ }
+ } while (--h);
+ dst -= _vertStripNextInc;
+ } while (--x);
+}
+
+#undef READ_BIT
+#undef FILL_BITS
+
+/* Ender - Zak256/Indy256 decoders */
+#define READ_BIT_256 \
+ do { \
+ if ((mask <<= 1) == 256) { \
+ buffer = *src++; \
+ mask = 1; \
+ } \
+ bits = ((buffer & mask) != 0); \
+ } while (0)
+
+#define READ_N_BITS(n, c) \
+ do { \
+ c = 0; \
+ for (int b = 0; b < n; b++) { \
+ READ_BIT_256; \
+ c += (bits << b); \
+ } \
+ } while (0)
+
+#define NEXT_ROW \
+ do { \
+ dst += dstPitch; \
+ if (--h == 0) { \
+ if (!--x) \
+ return; \
+ dst -= _vertStripNextInc; \
+ h = height; \
+ } \
+ } while (0)
+
+void Gdi::drawStripRaw(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const {
+ int x;
+
+ if (_vm->_features & GF_OLD256) {
+ uint h = height;
+ x = 8;
+ for (;;) {
+ *dst = *src++;
+ NEXT_ROW;
+ }
+ } else {
+ do {
+ for (x = 0; x < 8; x ++) {
+ byte color = *src++;
+ if (!transpCheck || color != _transparentColor)
+ dst[x] = _roomPalette[color] + _paletteMod;
+ }
+ dst += dstPitch;
+ } while (--height);
+ }
+}
+
+void Gdi::unkDecode8(byte *dst, int dstPitch, const byte *src, int height) const {
+ uint h = height;
+
+ int x = 8;
+ for (;;) {
+ uint run = (*src++) + 1;
+ byte color = *src++;
+
+ do {
+ *dst = _roomPalette[color];
+ NEXT_ROW;
+ } while (--run);
+ }
+}
+
+void Gdi::unkDecode9(byte *dst, int dstPitch, const byte *src, int height) const {
+ byte c, bits, color, run;
+ int i;
+ uint buffer = 0, mask = 128;
+ int h = height;
+ i = run = 0;
+
+ int x = 8;
+ for (;;) {
+ READ_N_BITS(4, c);
+
+ switch (c >> 2) {
+ case 0:
+ READ_N_BITS(4, color);
+ for (i = 0; i < ((c & 3) + 2); i++) {
+ *dst = _roomPalette[run * 16 + color];
+ NEXT_ROW;
+ }
+ break;
+
+ case 1:
+ for (i = 0; i < ((c & 3) + 1); i++) {
+ READ_N_BITS(4, color);
+ *dst = _roomPalette[run * 16 + color];
+ NEXT_ROW;
+ }
+ break;
+
+ case 2:
+ READ_N_BITS(4, run);
+ break;
+ }
+ }
+}
+
+void Gdi::unkDecode10(byte *dst, int dstPitch, const byte *src, int height) const {
+ int i;
+ byte local_palette[256], numcolors = *src++;
+ uint h = height;
+
+ for (i = 0; i < numcolors; i++)
+ local_palette[i] = *src++;
+
+ int x = 8;
+
+ for (;;) {
+ byte color = *src++;
+ if (color < numcolors) {
+ *dst = _roomPalette[local_palette[color]];
+ NEXT_ROW;
+ } else {
+ uint run = color - numcolors + 1;
+ color = *src++;
+ do {
+ *dst = _roomPalette[color];
+ NEXT_ROW;
+ } while (--run);
+ }
+ }
+}
+
+
+void Gdi::unkDecode11(byte *dst, int dstPitch, const byte *src, int height) const {
+ int bits, i;
+ uint buffer = 0, mask = 128;
+ byte inc = 1, color = *src++;
+
+ int x = 8;
+ do {
+ int h = height;
+ do {
+ *dst = _roomPalette[color];
+ dst += dstPitch;
+ for (i = 0; i < 3; i++) {
+ READ_BIT_256;
+ if (!bits)
+ break;
+ }
+ switch (i) {
+ case 1:
+ inc = -inc;
+ color -= inc;
+ break;
+
+ case 2:
+ color -= inc;
+ break;
+
+ case 3:
+ inc = 1;
+ READ_N_BITS(8, color);
+ break;
+ }
+ } while (--h);
+ dst -= _vertStripNextInc;
+ } while (--x);
+}
+
+#undef NEXT_ROW
+#undef READ_BIT_256
+
+
+#pragma mark -
+#pragma mark --- Transition effects ---
+#pragma mark -
+
+void ScummEngine::fadeIn(int effect) {
+ updatePalette();
+
+ switch (effect) {
+ case 0:
+ // seems to do nothing
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ // Some of the transition effects won't work properly unless
+ // the screen is marked as clean first. At first I thought I
+ // could safely do this every time fadeIn() was called, but
+ // that broke the FOA intro. Probably other things as well.
+ //
+ // Hopefully it's safe to do it at this point, at least.
+ virtscr[0].setDirtyRange(0, 0);
+ transitionEffect(effect - 1);
+ break;
+ case 128:
+ unkScreenEffect6();
+ break;
+ case 129:
+ break;
+ case 130:
+ case 131:
+ case 132:
+ case 133:
+ scrollEffect(133 - effect);
+ break;
+ case 134:
+ dissolveEffect(1, 1);
+ break;
+ case 135:
+ dissolveEffect(1, virtscr[0].h);
+ break;
+ default:
+ error("Unknown screen effect, %d", effect);
+ }
+ _screenEffectFlag = true;
+}
+
+void ScummEngine::fadeOut(int effect) {
+ VirtScreen *vs = &virtscr[0];
+
+ vs->setDirtyRange(0, 0);
+ if (!(_features & GF_NEW_CAMERA))
+ camera._last.x = camera._cur.x;
+
+ if (_switchRoomEffect >= 130 && _switchRoomEffect <= 133) {
+ // We're going to use scrollEffect(), so we'll need a copy of
+ // the current VirtScreen zero.
+
+ free(_scrollBuffer);
+ _scrollBuffer = (byte *) malloc(vs->h * vs->pitch);
+ memcpy(_scrollBuffer, vs->getPixels(0, 0), vs->h * vs->pitch);
+ }
+
+
+ if (_screenEffectFlag && effect != 0) {
+
+ // Fill screen 0 with black
+ memset(vs->getPixels(0, 0), 0, vs->pitch * vs->h);
+
+ // Fade to black with the specified effect, if any.
+ switch (effect) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ transitionEffect(effect - 1);
+ break;
+ case 128:
+ unkScreenEffect6();
+ break;
+ case 129:
+ // Just blit screen 0 to the display (i.e. display will be black)
+ vs->setDirtyRange(0, vs->h);
+ updateDirtyScreen(kMainVirtScreen);
+ break;
+ case 134:
+ dissolveEffect(1, 1);
+ break;
+ case 135:
+ dissolveEffect(1, virtscr[0].h);
+ break;
+ default:
+ error("fadeOut: default case %d", effect);
+ }
+ }
+
+ // Update the palette at the end (once we faded to black) to avoid
+ // some nasty effects when the palette is changed
+ updatePalette();
+
+ _screenEffectFlag = false;
+}
+
+/**
+ * Perform a transition effect. There are four different effects possible:
+ * 0: Iris effect
+ * 1: Box wipe (a black box expands from the upper-left corner to the lower-right corner)
+ * 2: Box wipe (a black box expands from the lower-right corner to the upper-left corner)
+ * 3: Inverse box wipe
+ * All effects operate on 8x8 blocks of the screen. These blocks are updated
+ * in a certain order; the exact order determines how the effect appears to the user.
+ * @param a the transition effect to perform
+ */
+void ScummEngine::transitionEffect(int a) {
+ int delta[16]; // Offset applied during each iteration
+ int tab_2[16];
+ int i, j;
+ int bottom;
+ int l, t, r, b;
+ const int height = MIN((int)virtscr[0].h, _screenHeight);
+
+ for (i = 0; i < 16; i++) {
+ delta[i] = transitionEffects[a].deltaTable[i];
+ j = transitionEffects[a].stripTable[i];
+ if (j == 24)
+ j = height / 8 - 1;
+ tab_2[i] = j;
+ }
+
+ bottom = height / 8;
+ for (j = 0; j < transitionEffects[a].numOfIterations; j++) {
+ for (i = 0; i < 4; i++) {
+ l = tab_2[i * 4];
+ t = tab_2[i * 4 + 1];
+ r = tab_2[i * 4 + 2];
+ b = tab_2[i * 4 + 3];
+
+ if (t == b) {
+ while (l <= r) {
+ if (l >= 0 && l < gdi._numStrips && t < bottom) {
+ virtscr[0].tdirty[l] = _screenTop + t * 8;
+ virtscr[0].bdirty[l] = _screenTop + (b + 1) * 8;
+ }
+ l++;
+ }
+ } else {
+ if (l < 0 || l >= gdi._numStrips || b <= t)
+ continue;
+ if (b > bottom)
+ b = bottom;
+ if (t < 0)
+ t = 0;
+ virtscr[0].tdirty[l] = _screenTop + t * 8;
+ virtscr[0].bdirty[l] = _screenTop + (b + 1) * 8;
+ }
+ updateDirtyScreen(kMainVirtScreen);
+ }
+
+ for (i = 0; i < 16; i++)
+ tab_2[i] += delta[i];
+
+ // Draw the current state to the screen and wait half a sec so the user
+ // can watch the effect taking place.
+ _system->updateScreen();
+ waitForTimer(30);
+ }
+}
+
+/**
+ * Update width*height areas of the screen, in random order, until the whole
+ * screen has been updated. For instance:
+ *
+ * dissolveEffect(1, 1) produces a pixel-by-pixel dissolve
+ * dissolveEffect(8, 8) produces a square-by-square dissolve
+ * dissolveEffect(virtsrc[0].width, 1) produces a line-by-line dissolve
+ */
+void ScummEngine::dissolveEffect(int width, int height) {
+#ifdef PALMOS_68K
+ // Remove this dissolve effect for now on PalmOS since it is a bit
+ // too slow using 68k emulation
+ if (width == 1 && height == 1) {
+ waitForTimer(30);
+ return;
+ }
+#endif
+
+ VirtScreen *vs = &virtscr[0];
+ int *offsets;
+ int blits_before_refresh, blits;
+ int x, y;
+ int w, h;
+ int i;
+
+ // There's probably some less memory-hungry way of doing this. But
+ // since we're only dealing with relatively small images, it shouldn't
+ // be too bad.
+
+ w = vs->w / width;
+ h = vs->h / height;
+
+ // When used correctly, vs->width % width and vs->height % height
+ // should both be zero, but just to be safe...
+
+ if (vs->w % width)
+ w++;
+
+ if (vs->h % height)
+ h++;
+
+ offsets = (int *) malloc(w * h * sizeof(int));
+ if (offsets == NULL)
+ error("dissolveEffect: out of memory");
+
+ // Create a permutation of offsets into the frame buffer
+
+ if (width == 1 && height == 1) {
+ // Optimized case for pixel-by-pixel dissolve
+
+ for (i = 0; i < vs->w * vs->h; i++)
+ offsets[i] = i;
+
+ for (i = 1; i < w * h; i++) {
+ int j;
+
+ j = _rnd.getRandomNumber(i - 1);
+ offsets[i] = offsets[j];
+ offsets[j] = i;
+ }
+ } else {
+ int *offsets2;
+
+ for (i = 0, x = 0; x < vs->w; x += width)
+ for (y = 0; y < vs->h; y += height)
+ offsets[i++] = y * vs->pitch + x;
+
+ offsets2 = (int *) malloc(w * h * sizeof(int));
+ if (offsets2 == NULL)
+ error("dissolveEffect: out of memory");
+
+ memcpy(offsets2, offsets, w * h * sizeof(int));
+
+ for (i = 1; i < w * h; i++) {
+ int j;
+
+ j = _rnd.getRandomNumber(i - 1);
+ offsets[i] = offsets[j];
+ offsets[j] = offsets2[i];
+ }
+
+ free(offsets2);
+ }
+
+ // Blit the image piece by piece to the screen. The idea here is that
+ // the whole update should take about a quarter of a second, assuming
+ // most of the time is spent in waitForTimer(). It looks good to me,
+ // but might still need some tuning.
+
+ blits = 0;
+ blits_before_refresh = (3 * w * h) / 25;
+
+ // Speed up the effect for CD Loom since it uses it so often. I don't
+ // think the original had any delay at all, so on modern hardware it
+ // wasn't even noticeable.
+ if (_gameId == GID_LOOM && (_version == 4))
+ blits_before_refresh *= 2;
+
+ for (i = 0; i < w * h; i++) {
+ x = offsets[i] % vs->pitch;
+ y = offsets[i] / vs->pitch;
+ _system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height);
+
+ if (++blits >= blits_before_refresh) {
+ blits = 0;
+ _system->updateScreen();
+ waitForTimer(30);
+ }
+ }
+
+ free(offsets);
+
+ if (blits != 0) {
+ _system->updateScreen();
+ waitForTimer(30);
+ }
+}
+
+void ScummEngine::scrollEffect(int dir) {
+ // It is at least technically possible that this function will be
+ // called without _scrollBuffer having been set up, but will it ever
+ // happen? I don't know.
+ if (!_scrollBuffer)
+ warning("scrollEffect: No scroll buffer. This may look bad");
+
+ VirtScreen *vs = &virtscr[0];
+
+ int x, y;
+ int step;
+
+ if ((dir == 0) || (dir == 1))
+ step = vs->h;
+ else
+ step = vs->w;
+
+ step = (step * kPictureDelay) / kScrolltime;
+
+ switch (dir) {
+ case 0:
+ //up
+ y = step;
+ while (y < vs->h) {
+ _system->copyRectToScreen(vs->getPixels(0, 0),
+ vs->pitch,
+ 0, vs->h - y,
+ vs->w, y);
+ if (_scrollBuffer)
+ _system->copyRectToScreen(_scrollBuffer + y * vs->w,
+ vs->pitch,
+ 0, 0,
+ vs->w, vs->h - y);
+ _system->updateScreen();
+ waitForTimer(kPictureDelay);
+
+ y += step;
+ }
+ break;
+ case 1:
+ // down
+ y = step;
+ while (y < vs->h) {
+ _system->copyRectToScreen(vs->getPixels(0, vs->h - y),
+ vs->pitch,
+ 0, 0,
+ vs->w, y);
+ if (_scrollBuffer)
+ _system->copyRectToScreen(_scrollBuffer,
+ vs->pitch,
+ 0, y,
+ vs->w, vs->h - y);
+ _system->updateScreen();
+ waitForTimer(kPictureDelay);
+
+ y += step;
+ }
+ break;
+ case 2:
+ // left
+ x = step;
+ while (x < vs->w) {
+ _system->copyRectToScreen(vs->getPixels(0, 0),
+ vs->pitch,
+ vs->w - x, 0,
+ x, vs->h);
+ if (_scrollBuffer)
+ _system->copyRectToScreen(_scrollBuffer + x,
+ vs->pitch,
+ 0, 0,
+ vs->w - x, vs->h);
+ _system->updateScreen();
+ waitForTimer(kPictureDelay);
+
+ x += step;
+ }
+ break;
+ case 3:
+ // right
+ x = step;
+ while (x < vs->w) {
+ _system->copyRectToScreen(vs->getPixels(vs->w - x, 0),
+ vs->pitch,
+ 0, 0,
+ x, vs->h);
+ if (_scrollBuffer)
+ _system->copyRectToScreen(_scrollBuffer,
+ vs->pitch,
+ x, 0,
+ vs->w - x, vs->h);
+ _system->updateScreen();
+ waitForTimer(kPictureDelay);
+
+ x += step;
+ }
+ break;
+ }
+
+ free(_scrollBuffer);
+ _scrollBuffer = NULL;
+}
+
+void ScummEngine::unkScreenEffect6() {
+ // CD Loom (but not EGA Loom!) uses a more fine-grained dissolve
+ if (_gameId == GID_LOOM && (_version == 4))
+ dissolveEffect(1, 1);
+ else
+ dissolveEffect(8, 4);
+}
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Gfx)
+_GSETPTR(Scumm::transitionEffects, GBVARS_TRANSITIONEFFECTS_INDEX, Scumm::TransitionEffect, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(Gfx)
+_GRELEASEPTR(GBVARS_TRANSITIONEFFECTS_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
new file mode 100644
index 0000000000..9aa8461c2c
--- /dev/null
+++ b/engines/scumm/gfx.h
@@ -0,0 +1,301 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GFX_H
+#define GFX_H
+
+#include "graphics/surface.h"
+
+namespace Scumm {
+
+class ScummEngine;
+
+/** Camera modes */
+enum {
+ kNormalCameraMode = 1,
+ kFollowActorCameraMode = 2,
+ kPanningCameraMode = 3
+};
+
+/** Camera state data */
+struct CameraData {
+ Common::Point _cur;
+ Common::Point _dest;
+ Common::Point _accel;
+ Common::Point _last;
+ int _leftTrigger, _rightTrigger;
+ byte _follows, _mode;
+ bool _movingToActor;
+};
+
+/** Virtual screen identifiers */
+enum VirtScreenNumber {
+ kMainVirtScreen = 0, // The 'stage'
+ kTextVirtScreen = 1, // In V1-V3 games: the area where text is printed
+ kVerbVirtScreen = 2, // The verb area
+ kUnkVirtScreen = 3 // ?? Not sure what this one is good for...
+};
+
+/**
+ * In all Scumm games, one to four virtual screen (or 'windows') together make
+ * up the content of the actual screen. Thinking of virtual screens as fixed
+ * size, fixed location windows might help understanding them. Typical, in all
+ * scumm games there is either one single virtual screen covering the entire
+ * real screen (mostly in all newer games, e.g. Sam & Max, and all V7+ games).
+ * The classic setup consists of three virtual screens: one at the top of the
+ * screen, where all conversation texts are printed; then the main one (which
+ * I like calling 'the stage', since all the actors are doing their stuff
+ * there), and finally the lower part of the real screen is taken up by the
+ * verb area.
+ * Finally, in V5 games and some V6 games, it's almost the same as in the
+ * original games, except that there is no separate conversation area.
+ *
+ * If you now wonder what the last screen is/was good for: I am not 100% sure,
+ * but it appears that it was used by the original engine to display stuff
+ * like the pause message, or questions ("Do you really want to restart?").
+ * It seems that it is not used at all by ScummVM, so we probably could just
+ * get rid of it and save a couple kilobytes of RAM.
+ *
+ * Each of these virtual screens has a fixed number or id (see also
+ * \ref VirtScreenNumber).
+ */
+struct VirtScreen : Graphics::Surface {
+ /**
+ * The unique id of this screen (corresponds to its position in the
+ * ScummEngine:virtscr array).
+ */
+ VirtScreenNumber number;
+
+ /**
+ * Vertical position of the virtual screen. Tells how much the virtual
+ * screen is shifted along the y axis relative to the real screen.
+ */
+ uint16 topline;
+
+ /**
+ * Horizontal scroll offset, tells how far the screen is scrolled to the
+ * right. Only used for the main screen. After all, verbs and the
+ * conversation text box don't have to scroll.
+ */
+ uint16 xstart;
+
+ /**
+ * Flag indicating whether this screen has a back buffer or not. This is
+ * yet another feature which is only used by the main screen.
+ * Strictly spoken one could remove this variable and replace checks
+ * on it with checks on backBuf. But since some code needs to temporarily
+ * disable the backBuf (so it can abuse drawBitmap; see drawVerbBitmap()
+ * and useIm01Cursor()), we keep it (at least for now).
+ */
+ bool hasTwoBuffers;
+
+ /**
+ * Pointer to the screen's back buffer, if it has one (see also
+ * the hasTwoBuffers member).
+ * The backBuf is used by drawBitmap to store the background graphics of
+ * the active room. This eases redrawing: whenever a portion of the screen
+ * has to be redrawn, first a copy from the backBuf content to screenPtr is
+ * performed. Then, any objects/actors in that area are redrawn atop that.
+ */
+ byte *backBuf;
+
+ /**
+ * Array containing for each visible strip of this virtual screen the
+ * coordinate at which the dirty region of that strip starts.
+ * 't' stands for 'top' - the top coordinate of the dirty region.
+ * This together with bdirty is used to do efficient redrawing of
+ * the screen.
+ */
+ uint16 tdirty[80 + 1];
+
+ /**
+ * Array containing for each visible strip of this virtual screen the
+ * coordinate at which the dirty region of that strip end.
+ * 'b' stands for 'bottom' - the bottom coordinate of the dirty region.
+ * This together with tdirty is used to do efficient redrawing of
+ * the screen.
+ */
+ uint16 bdirty[80 + 1];
+
+ /**
+ * Convenience method to set the whole tdirty and bdirty arrays to one
+ * specific value each. This is mostly used to mark every as dirty in
+ * a single step, like so:
+ * vs->setDirtyRange(0, vs->height);
+ * or to mark everything as clean, like so:
+ * vs->setDirtyRange(0, 0);
+ */
+ void setDirtyRange(int top, int bottom) {
+ for (int i = 0; i < 80 + 1; i++) {
+ tdirty[i] = top;
+ bdirty[i] = bottom;
+ }
+ }
+
+ byte *getPixels(int x, int y) const {
+ return (byte *)pixels + xstart + y * pitch + x;
+ }
+
+ byte *getBackPixels(int x, int y) const {
+ return (byte *)backBuf + xstart + y * pitch + x;
+ }
+};
+
+/** Palette cycles */
+struct ColorCycle {
+ uint16 delay;
+ uint16 counter;
+ uint16 flags;
+ byte start;
+ byte end;
+};
+
+/** Bomp graphics data, used as parameter to ScummEngine::drawBomp. */
+struct BompDrawData {
+ Graphics::Surface dst;
+
+ int x, y;
+ byte scale_x, scale_y;
+ const byte *dataptr;
+ int srcwidth, srcheight;
+ uint16 shadowMode;
+
+ byte *maskPtr;
+
+ BompDrawData() { memset(this, 0, sizeof(*this)); }
+};
+
+struct StripTable;
+
+#define CHARSET_MASK_TRANSPARENCY 253
+
+class Gdi {
+ ScummEngine *_vm;
+
+public:
+ int _numZBuffer;
+ int _imgBufOffs[8];
+ int32 _numStrips;
+
+ Gdi(ScummEngine *vm);
+ ~Gdi();
+
+protected:
+ byte _paletteMod;
+ byte *_roomPalette;
+ byte _transparentColor;
+ byte _decomp_shr, _decomp_mask;
+ uint32 _vertStripNextInc;
+
+ bool _zbufferDisabled;
+
+ /** Flag which is true when an object is being rendered, false otherwise. */
+ bool _objectMode;
+
+ /** Render settings which are specific to the C64 graphic decoders. */
+ struct {
+ byte colors[4];
+ byte charMap[2048], objectMap[2048], picMap[4096], colorMap[4096];
+ byte maskMap[4096], maskChar[4096];
+ } _C64;
+
+ struct {
+ byte nametable[16][64], nametableObj[16][64];
+ byte attributes[64], attributesObj[64];
+ byte masktable[16][8], masktableObj[16][8];
+ int objX;
+ bool hasmask;
+ } _NES;
+
+ /** For V2 games, we cache offsets into the room graphics, to speed up things. */
+ StripTable *_roomStrips;
+
+ /* Bitmap decompressors */
+ bool decompressBitmap(byte *dst, int dstPitch, const byte *src, int numLinesToProcess);
+
+ void drawStripEGA(byte *dst, int dstPitch, const byte *src, int height) const;
+ void drawStripC64Object(byte *dst, int dstPitch, int stripnr, int width, int height);
+ void drawStripC64Background(byte *dst, int dstPitch, int stripnr, int height);
+ void drawStripNES(byte *dst, byte *mask, int dstPitch, int stripnr, int top, int height);
+
+ void drawStripComplex(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
+ void drawStripBasicH(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
+ void drawStripBasicV(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
+
+ void drawStripRaw(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
+ void unkDecode8(byte *dst, int dstPitch, const byte *src, int height) const;
+ void unkDecode9(byte *dst, int dstPitch, const byte *src, int height) const;
+ void unkDecode10(byte *dst, int dstPitch, const byte *src, int height) const;
+ void unkDecode11(byte *dst, int dstPitch, const byte *src, int height) const;
+ void drawStrip3DO(byte *dst, int dstPitch, const byte *src, int height, const bool transpCheck) const;
+
+ void drawStripHE(byte *dst, int dstPitch, const byte *src, int width, int height, const bool transpCheck) const;
+
+ /* Mask decompressors */
+ void drawStripC64Mask(byte *dst, int stripnr, int width, int height) const;
+ void drawStripNESMask(byte *dst, int stripnr, int top, int height) const;
+ void decompressTMSK(byte *dst, const byte *tmsk, const byte *src, int height) const;
+ void decompressMaskImgOr(byte *dst, const byte *src, int height) const;
+ void decompressMaskImg(byte *dst, const byte *src, int height) const;
+
+ /* Misc */
+ void decodeC64Gfx(const byte *src, byte *dst, int size) const;
+
+ int getZPlanes(const byte *smap_ptr, const byte *zplane_list[9], bool bmapImage) const;
+
+ StripTable *generateStripTable(const byte *src, int width, int height, StripTable *table) const;
+ void drawBitmapV2Helper(const byte *ptr, VirtScreen *vs, int x, int y, const int width, const int height,
+ int stripnr, int numstrip);
+
+public:
+ void init();
+ void roomChanged(byte *roomptr, uint32 IM00_offs, byte transparentColor);
+
+ void drawBitmap(const byte *ptr, VirtScreen *vs, int x, int y, const int width, const int height,
+ int stripnr, int numstrip, byte flag);
+
+ void decodeNESGfx(const byte *room);
+ void decodeNESObject(const byte *ptr, int xpos, int ypos, int width, int height);
+
+ void drawBMAPBg(const byte *ptr, VirtScreen *vs);
+ void drawBMAPObject(const byte *ptr, VirtScreen *vs, int obj, int x, int y, int w, int h);
+
+ void copyVirtScreenBuffers(Common::Rect rect, int dirtybit = 0);
+
+ byte *getMaskBuffer(int x, int y, int z);
+ void disableZBuffer() { _zbufferDisabled = true; }
+ void enableZBuffer() { _zbufferDisabled = false; }
+
+ void resetBackground(int top, int bottom, int strip);
+
+ enum DrawBitmapFlags {
+ dbAllowMaskOr = 1 << 0,
+ dbDrawMaskOnAll = 1 << 1,
+ dbObjectMode = 2 << 2
+ };
+};
+
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/help.cpp b/engines/scumm/help.cpp
new file mode 100644
index 0000000000..acc9e67a6d
--- /dev/null
+++ b/engines/scumm/help.cpp
@@ -0,0 +1,350 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/str.h"
+#include "common/util.h"
+
+#include "scumm/help.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+int ScummHelp::numPages(byte gameId) {
+ switch (gameId) {
+ case GID_MANIAC:
+ case GID_ZAK:
+ return 4;
+ break;
+ case GID_INDY3:
+ return 6;
+ break;
+ case GID_LOOM:
+ case GID_MONKEY_EGA:
+ case GID_MONKEY_VGA:
+ case GID_MONKEY:
+ case GID_MONKEY2:
+ case GID_INDY4:
+ case GID_TENTACLE:
+ case GID_SAMNMAX:
+ case GID_DIG:
+ case GID_FT:
+ case GID_CMI:
+ return 3;
+ break;
+/* TODO - I don't know the controls for these games
+ case GID_PUTTDEMO:
+ case GID_PUTTPUTT:
+*/
+ default:
+ return 2;
+ }
+}
+
+#define ADD_BIND(k,d) do { key[i] = k; dsc[i] = d; i++; } while (0)
+#define ADD_TEXT(d) ADD_BIND("",d)
+#define ADD_LINE ADD_BIND("","")
+
+void ScummHelp::updateStrings(byte gameId, byte version, Common::Platform platform,
+ int page, String &title, String *&key, String *&dsc) {
+ key = new String[HELP_NUM_LINES];
+ dsc = new String[HELP_NUM_LINES];
+ int i = 0;
+ switch (page) {
+ case 1:
+ title = "Common keyboard commands:";
+ ADD_BIND("F5", "Save / Load dialog");
+ if (version >= 5)
+ ADD_BIND(".", "Skip line of text");
+ ADD_BIND("Esc", "Skip cutscene");
+ ADD_BIND("Space", "Pause game");
+ ADD_BIND("Ctrl 0-9", "Load game state 1-10");
+ ADD_BIND("Alt 0-9", "Save game state 1-10");
+#ifdef MACOSX
+ ADD_BIND("Cmd q", "Quit");
+#else
+ ADD_BIND("Alt x, Ctrl z", "Quit");
+#endif
+ ADD_BIND("Alt Enter", "Toggle fullscreen");
+ ADD_BIND("[, ]", "Music volume up / down");
+ ADD_BIND("-, +", "Text speed slower / faster");
+ ADD_BIND("Enter", "Simulate left mouse button");
+ ADD_BIND("Tab", "Simulate right mouse button");
+ break;
+ case 2:
+ title = "Special keyboard commands:";
+ ADD_BIND("~, #", "Show / Hide console");
+ ADD_BIND("Ctrl d", "Start the debugger");
+ ADD_BIND("Ctrl s", "Show memory consumption");
+ ADD_BIND("Ctrl f", "Run in fast mode (*)");
+ ADD_BIND("Ctrl g", "Run in really fast mode (*)");
+ ADD_BIND("Ctrl m", "Toggle mouse capture");
+ ADD_BIND("Ctrl Alt 1-8", "Switch between graphics filters");
+ ADD_BIND("Ctrl Alt +, -", "Increase / Decrease scale factor");
+ ADD_BIND("Ctrl Alt a", "Toggle aspect-ratio correction");
+ ADD_LINE;
+ ADD_LINE;
+ // FIXME: This should use word-wrapping, and should not assume
+ // that the font is mono-spaced.
+ ADD_TEXT("* Note that using ctrl-f and");
+ ADD_TEXT(" ctrl-g are not recommended");
+ ADD_TEXT(" since they may cause crashes");
+ ADD_TEXT(" or incorrect game behaviour.");
+ break;
+ case 3:
+ if (gameId == GID_LOOM)
+ title = "Spinning drafts on the keyboard:";
+ else
+ title = "Main game controls:";
+ switch (gameId) {
+ case GID_ZAK:
+ case GID_MANIAC:
+ // HACK. I know use of g_scumm here is evil, however,
+ // introducing new GID and putting it everywhere will
+ // pollute code much more that this single instance
+ if (g_scumm->_platform == Common::kPlatformNES) {
+ ADD_BIND("q", "Push");
+ ADD_BIND("a", "Pull");
+ ADD_BIND("z", "Give");
+ ADD_BIND("w", "Open");
+ ADD_BIND("s", "Close");
+ ADD_BIND("x", "Go to");
+ ADD_BIND("e", "Get");
+ ADD_BIND("d", "Use");
+ ADD_BIND("c", "Read");
+ ADD_BIND("r", "New kid");
+ ADD_BIND("f", "Turn on");
+ ADD_BIND("v", "Turn off");
+ break;
+ }
+
+ ADD_BIND("q", "Push");
+ ADD_BIND("a", "Pull");
+ ADD_BIND("z", "Give");
+ ADD_BIND("w", "Open");
+ ADD_BIND("s", "Close");
+ ADD_BIND("x", "Read");
+ ADD_BIND("e", "Walk to");
+ ADD_BIND("d", "Pick up");
+ ADD_BIND("c", "What is");
+ if (gameId == GID_MANIAC) {
+ ADD_BIND("r", "Unlock");
+ ADD_BIND("f", "New kid");
+ } else {
+ ADD_BIND("r", "Put on");
+ ADD_BIND("f", "Take off");
+ }
+ ADD_BIND("v", "Use");
+ ADD_BIND("t", "Turn on");
+ ADD_BIND("g", "Turn off");
+ if (gameId == GID_MANIAC)
+ ADD_BIND("b", "Fix");
+ else
+ ADD_BIND("b", "Switch");
+ break;
+ case GID_INDY3:
+ ADD_BIND("q", "Push");
+ ADD_BIND("a", "Pull");
+ ADD_BIND("z", "Give");
+ ADD_BIND("w", "Open");
+ ADD_BIND("s", "Close");
+ ADD_BIND("x", "Look");
+ ADD_BIND("e", "Walk to");
+ ADD_BIND("d", "Pick up");
+ ADD_BIND("c", "What is");
+ ADD_BIND("r", "Use");
+ ADD_BIND("f", "Turn on");
+ ADD_BIND("v", "Turn off");
+ ADD_BIND("t", "Talk");
+ ADD_BIND("g", "Travel");
+ ADD_BIND("b", "To Henry / To Indy");
+ break;
+ case GID_LOOM:
+ ADD_BIND("q, c", "play C minor on distaff");
+ ADD_BIND("w, d", "play D on distaff");
+ ADD_BIND("e, e", "play E on distaff");
+ ADD_BIND("r, f", "play F on distaff");
+ ADD_BIND("t, g", "play G on distaff");
+ ADD_BIND("y, a", "play A on distaff");
+ ADD_BIND("u, b", "play B on distaff");
+ ADD_BIND("i, C", "play C major on distaff");
+ break;
+ case GID_MONKEY_EGA:
+ case GID_MONKEY_VGA:
+ ADD_BIND("o", "Open");
+ ADD_BIND("c", "Close");
+ ADD_BIND("s", "puSh");
+ ADD_BIND("y", "pull (Yank)");
+ ADD_BIND("w", "Walk to");
+ ADD_BIND("p", "Pick up");
+ ADD_BIND("t", "Talk to");
+ ADD_BIND("g", "Give");
+ ADD_BIND("u", "Use");
+ ADD_BIND("l", "Look at");
+ ADD_BIND("n", "turn oN");
+ ADD_BIND("f", "turn oFf");
+ break;
+ case GID_MONKEY:
+ case GID_MONKEY2:
+ case GID_INDY4:
+ case GID_TENTACLE:
+ ADD_BIND("g", "Give");
+ ADD_BIND("o", "Open");
+ ADD_BIND("c", "Close");
+ ADD_BIND("p", "Pick up");
+ ADD_BIND("l", "Look at");
+ ADD_BIND("t", "Talk to");
+ ADD_BIND("u", "Use");
+ ADD_BIND("s", "puSh");
+ ADD_BIND("y", "pull (Yank)");
+ if (platform == Common::kPlatformSegaCD) {
+ // FIXME look at scripts to figure all options out...
+ // keys 1->4 seem to do something as well
+ ADD_BIND("6", "Highlight prev dialogue");
+ ADD_BIND("7", "Highlight next dialogue");
+ }
+ break;
+ case GID_SAMNMAX:
+ ADD_BIND("w", "Walk");
+ ADD_BIND("t", "Talk");
+ ADD_BIND("u", "Use");
+ ADD_BIND("i", "Inventory");
+ ADD_BIND("o", "Object");
+ ADD_BIND("p", "Pick up");
+ ADD_BIND("l", "Look");
+ ADD_BIND("b", "Black and White / Color");
+ break;
+ case GID_FT:
+ ADD_BIND("e", "Eyes");
+ ADD_BIND("t", "Tongue");
+ ADD_BIND("i", "Inventory");
+ ADD_BIND("p", "Punch");
+ ADD_BIND("k", "Kick");
+ break;
+ case GID_DIG:
+ ADD_BIND("e", "Examine");
+ ADD_BIND("t", "Regular cursor");
+ ADD_BIND("i", "Inventory");
+ ADD_BIND("c", "Comm");
+ break;
+ case GID_CMI:
+ ADD_BIND("F1", "Save / Load / Options");
+ ADD_BIND("e", "Examine");
+ ADD_BIND("t", "Talk to");
+ ADD_BIND("i", "Inventory");
+ ADD_BIND("u", "Use");
+ break;
+ }
+ break;
+ case 4:
+ title = "Other game controls:";
+ if (version <= 2) {
+ ADD_TEXT("Inventory: (not yet implemented)");
+ ADD_BIND("u", "Scroll list up");
+ ADD_BIND("j", "Scroll list down");
+ ADD_BIND("i", "Upper left item");
+ ADD_BIND("k", "Lower left item");
+ ADD_BIND("o", "Upper right item");
+ ADD_BIND("l", "Lower right item");
+ ADD_LINE;
+ ADD_TEXT("Switching characters:");
+ if (gameId == GID_MANIAC) {
+ ADD_BIND("F1", "Dave");
+ ADD_BIND("F2", "Second kid");
+ ADD_BIND("F3", "Third kid");
+ } else {
+ ADD_BIND("F1", "Zak");
+ ADD_BIND("F2", "Annie");
+ ADD_BIND("F3", "Melissa");
+ ADD_BIND("F4", "Leslie");
+ }
+ } else if (gameId == GID_INDY3 || gameId == GID_ZAK) {
+ // Indy3, or FM-TOWNS Zak
+ ADD_TEXT("Inventory:");
+ ADD_BIND("y", "Upper left item");
+ ADD_BIND("h", "Middle left item");
+ ADD_BIND("n", "Lower left item");
+ ADD_BIND("u", "Upper right item");
+ ADD_BIND("j", "Middle right item");
+ ADD_BIND("m", "Lower right item");
+ ADD_BIND("o", "Scroll list up");
+ ADD_BIND("l", "Scroll list down");
+ if (gameId == GID_ZAK) {
+ ADD_LINE;
+ ADD_TEXT("Switching characters:");
+ ADD_BIND("F1", "Zak");
+ ADD_BIND("F2", "Annie");
+ ADD_BIND("F3", "Melissa");
+ ADD_BIND("F4", "Leslie");
+ }
+ }
+ break;
+ case 5:
+ switch (gameId) {
+ case GID_INDY3:
+ title = "Fighting controls (numpad):";
+ ADD_BIND("7", "Step back");
+ ADD_BIND("4", "Step back");
+ ADD_BIND("1", "Step back");
+ ADD_BIND("8", "Block high");
+ ADD_BIND("5", "Block middle");
+ ADD_BIND("2", "Block low");
+ ADD_BIND("9", "Punch high");
+ ADD_BIND("6", "Punch middle");
+ ADD_BIND("3", "Punch low");
+ ADD_LINE;
+ ADD_LINE;
+ ADD_TEXT("These are for Indy on left.");
+ ADD_TEXT("When Indy is on the right,");
+ ADD_TEXT("7, 4, and 1 are switched with");
+ ADD_TEXT("9, 6, and 3, respectively.");
+ break;
+ }
+ break;
+ case 6:
+ switch (gameId) {
+ case GID_INDY3:
+ title = "Biplane controls (numpad):";
+ ADD_BIND("7", "Fly to upper left");
+ ADD_BIND("4", "Fly to left");
+ ADD_BIND("1", "Fly to lower left");
+ ADD_BIND("8", "Fly upwards");
+ ADD_BIND("5", "Fly straight");
+ ADD_BIND("2", "Fly down");
+ ADD_BIND("9", "Fly to upper right");
+ ADD_BIND("6", "Fly to right");
+ ADD_BIND("3", "Fly to lower right");
+ break;
+ }
+ break;
+ }
+ while (i < HELP_NUM_LINES) {
+ ADD_LINE;
+ }
+}
+
+#undef ADD_BIND
+#undef ADD_TEXT
+#undef ADD_LINE
+
+} // End of namespace Scumm
diff --git a/engines/scumm/help.h b/engines/scumm/help.h
new file mode 100644
index 0000000000..9691c09594
--- /dev/null
+++ b/engines/scumm/help.h
@@ -0,0 +1,45 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCUMM_HELP_H
+#define SCUMM_HELP_H
+
+#include "common/str.h"
+
+namespace Scumm {
+
+#define HELP_NUM_LINES 15
+
+class ScummHelp {
+protected:
+ typedef Common::String String;
+
+public:
+ static int numPages(byte gameId);
+ static void updateStrings(byte gameId, byte version, Common::Platform platform,
+ int page, String &title, String *&key, String *&dsc);
+};
+
+} // End of namespace Scumm
+
+#endif
+
diff --git a/engines/scumm/imuse.cpp b/engines/scumm/imuse.cpp
new file mode 100644
index 0000000000..5d170e98b8
--- /dev/null
+++ b/engines/scumm/imuse.cpp
@@ -0,0 +1,2043 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "base/version.h"
+
+#include "common/util.h"
+#include "common/system.h"
+
+#include "scumm/imuse.h"
+#include "scumm/imuse_internal.h"
+#include "scumm/instrument.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+#include "scumm/util.h"
+
+#include "sound/mididrv.h"
+
+
+namespace Scumm {
+
+////////////////////////////////////////
+//
+// IMuseInternal implementation
+//
+////////////////////////////////////////
+
+IMuseInternal::IMuseInternal() :
+_native_mt32(false),
+_enable_gs(false),
+_sc55(false),
+_midi_adlib(0),
+_midi_native(0),
+_base_sounds(0),
+_paused(false),
+_initialized(false),
+_tempoFactor(0),
+_player_limit(ARRAYSIZE(_players)),
+_recycle_players(false),
+_direct_passthrough(false),
+_queue_end(0),
+_queue_pos(0),
+_queue_sound(0),
+_queue_adding(0),
+_queue_marker(0),
+_queue_cleared(0),
+_master_volume(0),
+_music_volume(0),
+_trigger_count(0),
+_snm_trigger_index(0) {
+ memset(_channel_volume,0,sizeof(_channel_volume));
+ memset(_channel_volume_eff,0,sizeof(_channel_volume_eff));
+ memset(_volchan_table,0,sizeof(_volchan_table));
+}
+
+byte *IMuseInternal::findStartOfSound(int sound) {
+ byte *ptr = NULL;
+ int32 size, pos;
+
+ if (_base_sounds)
+ ptr = _base_sounds[sound];
+
+ if (ptr == NULL) {
+ debug(1, "IMuseInternal::findStartOfSound(): Sound %d doesn't exist!", sound);
+ return NULL;
+ }
+
+ // Check for old-style headers first, like 'RO'
+ if (ptr[4] == 'R' && ptr[5] == 'O'&& ptr[6] != 'L')
+ return ptr + 4;
+ if (ptr[8] == 'S' && ptr[9] == 'O')
+ return ptr + 8;
+
+ ptr += 8;
+ size = READ_BE_UINT32(ptr);
+ ptr += 4;
+
+ // Okay, we're looking for one of those things: either
+ // an 'MThd' tag (for SMF), or a 'FORM' tag (for XMIDI).
+ size = 48; // Arbitrary; we should find our tag within the first 48 bytes of the resource
+ pos = 0;
+ while (pos < size) {
+ if (!memcmp(ptr + pos, "MThd", 4) || !memcmp(ptr + pos, "FORM", 4))
+ return ptr + pos;
+ ++pos; // We could probably iterate more intelligently
+ }
+
+ debug(3, "IMuseInternal::findStartOfSound(): Failed to align on sound %d!", sound);
+ return 0;
+}
+
+bool IMuseInternal::isMT32(int sound) {
+ byte *ptr = NULL;
+ uint32 tag;
+
+ if (_base_sounds)
+ ptr = _base_sounds[sound];
+
+ if (ptr == NULL)
+ return false;
+
+ tag = *(((uint32 *)ptr) + 1);
+ switch (tag) {
+ case MKID('ADL '):
+ case MKID('ASFX'): // Special AD class for old Adlib sound effects
+ case MKID('SPK '):
+ return false;
+
+ case MKID('AMI '):
+ case MKID('ROL '):
+ return true;
+
+ case MKID('MAC '): // Occurs in the Mac version of FOA and MI2
+ return true;
+
+ case MKID('GMD '):
+ case MKID('MIDI'): // Occurs in Sam & Max
+ return false;
+ }
+
+ // Old style 'RO' has equivalent properties to 'ROL'
+ if (ptr[4] == 'R' && ptr[5] == 'O')
+ return true;
+ // Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
+ if (ptr[8] == 'S' && ptr[9] == 'O')
+ return false;
+
+ error("Unknown music type: '%s'", tag2str(tag));
+
+ return false;
+}
+
+bool IMuseInternal::isMIDI(int sound) {
+ byte *ptr = NULL;
+ uint32 tag;
+
+ if (_base_sounds)
+ ptr = _base_sounds[sound];
+
+ if (ptr == NULL)
+ return false;
+
+ tag = *(((uint32 *)ptr) + 1);
+ switch (tag) {
+ case MKID('ADL '):
+ case MKID('ASFX'): // Special AD class for old Adlib sound effects
+ case MKID('SPK '):
+ return false;
+
+ case MKID('AMI '):
+ case MKID('ROL '):
+ return true;
+
+ case MKID('MAC '): // Occurs in the Mac version of FOA and MI2
+ return true;
+
+ case MKID('GMD '):
+ case MKID('MIDI'): // Occurs in Sam & Max
+ return true;
+ }
+
+ // Old style 'RO' has equivalent properties to 'ROL'
+ if (ptr[4] == 'R' && ptr[5] == 'O')
+ return true;
+ // Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
+ // FIXME: Right now we're pretending it's GM.
+ if (ptr[8] == 'S' && ptr[9] == 'O')
+ return true;
+
+ error("Unknown music type: '%s'", tag2str(tag));
+
+ return false;
+}
+
+MidiDriver *IMuseInternal::getBestMidiDriver(int sound) {
+ MidiDriver *driver = NULL;
+
+ if (isMIDI(sound)) {
+ if (_midi_native) {
+ driver = _midi_native;
+ } else {
+ // Route it through Adlib anyway.
+ driver = _midi_adlib;
+ }
+ } else {
+ driver = _midi_adlib;
+ }
+ return driver;
+}
+
+bool IMuseInternal::startSound(int sound) {
+ Player *player;
+ void *ptr;
+
+ // Do not start a sound if it is already set to start on an ImTrigger
+ // event. This fixes carnival music problems where a sound has been set
+ // to trigger at the right time, but then is started up immediately
+ // anyway, only to be restarted later when the trigger occurs.
+ //
+ // However, we have to make sure the sound with the trigger is actually
+ // playing, otherwise the music may stop when Sam and Max are thrown
+ // out of Bumpusville, because entering the mansion sets up a trigger
+ // for a sound that isn't necessarily playing. This is somewhat related
+ // to bug #780918.
+
+ int i;
+ ImTrigger *trigger = _snm_triggers;
+ for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trigger) {
+ if (trigger->sound && trigger->id && trigger->command[0] == 8 && trigger->command[1] == sound && getSoundStatus(trigger->sound))
+ return false;
+ }
+
+ ptr = findStartOfSound(sound);
+ if (!ptr) {
+ debug(2, "IMuseInternal::startSound(): Couldn't find sound %d!", sound);
+ return false;
+ }
+
+ // Check which MIDI driver this track should use.
+ // If it's NULL, it ain't something we can play.
+ MidiDriver *driver = getBestMidiDriver(sound);
+ if (!driver)
+ return false;
+
+ // If the requested sound is already playing, start it over
+ // from scratch. This was originally a hack to prevent Sam & Max
+ // iMuse messiness while upgrading the iMuse engine, but it
+ // is apparently necessary to deal with fade-and-restart
+ // race conditions that were observed in MI2. Reference
+ // Bug #590511 and Patch #607175 (which was reversed to fix
+ // an FOA regression: Bug #622606).
+ player = findActivePlayer(sound);
+ if (!player)
+ player = allocate_player(128);
+ if (!player)
+ return false;
+
+ // HACK: This is to work around a problem at the Dino Bungie Memorial.
+ // There are three pieces of music involved here:
+ //
+ // 80 - Main theme (looping)
+ // 81 - Music when entering Rex's and Wally's room (not looping)
+ // 82 - Music when listening to Rex or Wally
+ //
+ // When entering, tune 81 starts, tune 80 is faded down (not out) and
+ // a trigger is set in tune 81 to fade tune 80 back up.
+ //
+ // When listening to Rex or Wally, tune 82 is started, tune 81 is faded
+ // out and tune 80 is faded down even further.
+ //
+ // However, when tune 81 is faded out its trigger will cause tune 80 to
+ // fade back up, resulting in two tunes being played simultaneously at
+ // full blast. It's no use trying to keep tune 81 playing at volume 0.
+ // It doesn't loop, so eventually it will terminate on its own.
+ //
+ // I don't know how the original interpreter handled this - or even if
+ // it handled it at all - but it looks like sloppy scripting to me. Our
+ // workaround is to clear the trigger if the player listens to Rex or
+ // Wally before tune 81 has finished on its own.
+
+ if (g_scumm->_gameId == GID_SAMNMAX && sound == 82 && getSoundStatus(81, false))
+ ImClearTrigger(81, 1);
+
+ player->clear();
+ return player->startSound(sound, driver, _direct_passthrough);
+}
+
+
+Player *IMuseInternal::allocate_player(byte priority) {
+ Player *player = _players, *best = NULL;
+ int i;
+ byte bestpri = 255;
+
+ for (i = _player_limit; i != 0; i--, player++) {
+ if (!player->isActive())
+ return player;
+ if (player->getPriority() < bestpri) {
+ best = player;
+ bestpri = player->getPriority();
+ }
+ }
+
+ if (bestpri < priority || _recycle_players)
+ return best;
+
+ debug(1, "Denying player request");
+ return NULL;
+}
+
+void IMuseInternal::init_players() {
+ Player *player = _players;
+ int i;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ player->_se = this;
+ player->clear(); // Used to just set _active to false
+ }
+}
+
+void IMuseInternal::init_parts() {
+ Part *part;
+ int i;
+
+ for (i = 0, part = _parts; i != ARRAYSIZE(_parts); i++, part++) {
+ part->init();
+ part->_se = this;
+ part->_slot = i;
+ }
+}
+
+int IMuseInternal::stopSound(int sound) {
+ int r = -1;
+ Player *player = findActivePlayer(sound);
+ if (player) {
+ player->clear();
+ r = 0;
+ }
+ return r;
+}
+
+int IMuseInternal::stopAllSounds() {
+ Player *player = _players;
+ int i;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive())
+ player->clear();
+ }
+ return 0;
+}
+
+void IMuseInternal::on_timer(MidiDriver *midi) {
+ if (_paused || !_initialized)
+ return;
+
+ if (midi == _midi_native || !_midi_native)
+ handleDeferredCommands(midi);
+ sequencer_timers(midi);
+}
+
+int IMuseInternal::getMusicTimer() const {
+ int best_time = 0;
+ const Player *player = _players;
+ int i;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive()) {
+ int timer = player->getMusicTimer();
+ if (timer > best_time)
+ best_time = timer;
+ }
+ }
+ return best_time;
+}
+
+void IMuseInternal::sequencer_timers(MidiDriver *midi) {
+ Player *player = _players;
+ int i;
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive() && player->getMidiDriver() == midi) {
+ player->onTimer();
+ }
+ }
+}
+
+void IMuseInternal::handle_marker(uint id, byte data) {
+ uint16 *p = 0;
+ uint pos;
+
+ if (_queue_adding && _queue_sound == id && data == _queue_marker)
+ return;
+
+ // Fix for bug #733401, revised for bug #761637:
+ // It would seem that sometimes a marker is in the queue
+ // but not at the head position. In the case of our bug,
+ // this seems to be the result of commands in the queue
+ // for songs that are no longer playing. So we skip
+ // ahead to the appropriate marker, effectively chomping
+ // anything in the queue before it. This fixes the FOA
+ // end credits music, but needs to be tested for inappopriate
+ // behavior elsewhere.
+ pos = _queue_end;
+ while (pos != _queue_pos) {
+ p = _cmd_queue[pos].array;
+ if (p[0] == TRIGGER_ID && p[1] == id && p[2] == data)
+ break;
+ pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
+ }
+
+ if (pos == _queue_pos)
+ return;
+
+ if (pos != _queue_end)
+ debug(0, "Skipping entries in iMuse command queue to reach marker");
+
+ _trigger_count--;
+ _queue_cleared = false;
+ do {
+ pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
+ if (_queue_pos == pos)
+ break;
+ p = _cmd_queue[pos].array;
+ if (*p++ != COMMAND_ID)
+ break;
+ _queue_end = pos;
+
+ doCommand(p[0], p[1], p[2], p[3], p[4], p[5], p[6], 0);
+
+ if (_queue_cleared)
+ return;
+ pos = _queue_end;
+ } while (1);
+
+ _queue_end = pos;
+}
+
+int IMuseInternal::get_channel_volume(uint a) {
+ if (a < 8)
+ return _channel_volume_eff[a];
+ return (_master_volume * _music_volume / 255) / 2;
+}
+
+Part *IMuseInternal::allocate_part(byte pri, MidiDriver *midi) {
+ Part *part, *best = NULL;
+ int i;
+
+ for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
+ if (!part->_player)
+ return part;
+ if (pri >= part->_pri_eff) {
+ pri = part->_pri_eff;
+ best = part;
+ }
+ }
+
+ if (best) {
+ best->uninit();
+ reallocateMidiChannels(midi);
+ } else {
+ debug(1, "Denying part request");
+ }
+ return best;
+}
+
+int IMuseInternal::getSoundStatus(int sound, bool ignoreFadeouts) const {
+ int i;
+ const Player *player = _players;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive() && (!ignoreFadeouts || !player->isFadingOut())) {
+ if (sound == -1)
+ return player->getID();
+ else if (player->getID() == (uint16)sound)
+ return 1;
+ }
+ }
+ return (sound == -1) ? 0 : get_queue_sound_status(sound);
+}
+
+int IMuseInternal::get_queue_sound_status(int sound) const {
+ const uint16 *a;
+ int i, j;
+
+ j = _queue_pos;
+ i = _queue_end;
+
+ while (i != j) {
+ a = _cmd_queue[i].array;
+ if (a[0] == COMMAND_ID && a[1] == 8 && a[2] == (uint16)sound)
+ return 2;
+ i = (i + 1) % ARRAYSIZE(_cmd_queue);
+ }
+
+ for (i = 0; i < ARRAYSIZE (_deferredCommands); ++i) {
+ if (_deferredCommands[i].time_left && _deferredCommands[i].a == 8 &&
+ _deferredCommands[i].b == sound) {
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+int IMuseInternal::set_volchan(int sound, int volchan) {
+ int r;
+ int i;
+ int num;
+ Player *player, *best, *sameid;
+
+ r = get_volchan_entry(volchan);
+ if (r == -1)
+ return -1;
+
+ if (r >= 8) {
+ player = findActivePlayer(sound);
+ if (player && player->_vol_chan != (uint16)volchan) {
+ player->_vol_chan = volchan;
+ player->setVolume(player->getVolume());
+ return 0;
+ }
+ return -1;
+ } else {
+ best = NULL;
+ num = 0;
+ sameid = NULL;
+ for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
+ if (player->isActive()) {
+ if (player->_vol_chan == (uint16)volchan) {
+ num++;
+ if (!best || player->getPriority() <= best->getPriority())
+ best = player;
+ } else if (player->getID() == (uint16)sound) {
+ sameid = player;
+ }
+ }
+ }
+ if (sameid == NULL)
+ return -1;
+ if (num >= r)
+ best->clear();
+ player->_vol_chan = volchan;
+ player->setVolume(player->getVolume());
+ return 0;
+ }
+}
+
+int IMuseInternal::clear_queue() {
+ _queue_adding = false;
+ _queue_cleared = true;
+ _queue_pos = 0;
+ _queue_end = 0;
+ _trigger_count = 0;
+ return 0;
+}
+
+int IMuseInternal::enqueue_command(int a, int b, int c, int d, int e, int f, int g) {
+ uint16 *p;
+ uint i;
+
+ i = _queue_pos;
+
+ if (i == _queue_end)
+ return -1;
+
+ if (a == -1) {
+ _queue_adding = false;
+ _trigger_count++;
+ return 0;
+ }
+
+ p = _cmd_queue[_queue_pos].array;
+ p[0] = COMMAND_ID;
+ p[1] = a;
+ p[2] = b;
+ p[3] = c;
+ p[4] = d;
+ p[5] = e;
+ p[6] = f;
+ p[7] = g;
+
+ i = (i + 1) % ARRAYSIZE(_cmd_queue);
+
+ if (_queue_end != i) {
+ _queue_pos = i;
+ return 0;
+ } else {
+ _queue_pos = (i - 1) % ARRAYSIZE(_cmd_queue);
+ return -1;
+ }
+}
+
+int IMuseInternal::query_queue(int param) {
+ switch (param) {
+ case 0: // Get trigger count
+ return _trigger_count;
+ case 1: // Get trigger type
+ if (_queue_end == _queue_pos)
+ return -1;
+ return _cmd_queue[_queue_end].array[1];
+ case 2: // Get trigger sound
+ if (_queue_end == _queue_pos)
+ return 0xFF;
+ return _cmd_queue[_queue_end].array[2];
+ default:
+ return -1;
+ }
+}
+
+int IMuseInternal::setMusicVolume(uint vol) {
+ if (vol > 255)
+ vol = 255;
+ if (_music_volume == vol)
+ return 0;
+ _music_volume = vol;
+ vol = _master_volume * _music_volume / 255;
+ for (uint i = 0; i < ARRAYSIZE(_channel_volume); i++) {
+ _channel_volume_eff[i] = _channel_volume[i] * vol / 255;
+ }
+ if (!_paused)
+ update_volumes();
+ return 0;
+}
+
+int IMuseInternal::setImuseMasterVolume(uint vol) {
+ if (vol > 255)
+ vol = 255;
+ if (_master_volume == vol)
+ return 0;
+ _master_volume = vol;
+ vol = _master_volume * _music_volume / 255;
+ for (uint i = 0; i < ARRAYSIZE(_channel_volume); i++) {
+ _channel_volume_eff[i] = _channel_volume[i] * vol / 255;
+ }
+ if (!_paused)
+ update_volumes();
+ return 0;
+}
+
+int IMuseInternal::terminate1() {
+ _initialized = false;
+ stopAllSounds();
+ return 0;
+}
+
+// This is the stuff that has to be done
+// outside the monitor's mutex, otherwise
+// a deadlock occurs.
+int IMuseInternal::terminate2() {
+ if (_midi_adlib) {
+ _midi_adlib->close();
+ delete _midi_adlib;
+ _midi_adlib = 0;
+ }
+
+ if (_midi_native) {
+ _midi_native->close();
+ delete _midi_native;
+ _midi_native = 0;
+ }
+
+ return 0;
+}
+
+int IMuseInternal::enqueue_trigger(int sound, int marker) {
+ uint16 *p;
+ uint pos;
+
+ pos = _queue_pos;
+
+ p = _cmd_queue[pos].array;
+ p[0] = TRIGGER_ID;
+ p[1] = sound;
+ p[2] = marker;
+
+ pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
+ if (_queue_end == pos) {
+ _queue_pos = (pos - 1) % ARRAYSIZE(_cmd_queue);
+ return -1;
+ }
+
+ _queue_pos = pos;
+ _queue_adding = true;
+ _queue_sound = sound;
+ _queue_marker = marker;
+ return 0;
+}
+
+int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) {
+ int args[8];
+ args[0] = a;
+ args[1] = b;
+ args[2] = c;
+ args[3] = d;
+ args[4] = e;
+ args[5] = f;
+ args[6] = g;
+ args[7] = h;
+ return doCommand(8, args);
+}
+
+int32 IMuseInternal::doCommand(int numargs, int a[]) {
+ int i;
+
+ if (numargs < 1)
+ return -1;
+ byte cmd = a[0] & 0xFF;
+ byte param = a[0] >> 8;
+ Player *player = NULL;
+
+ if (!_initialized && (cmd || param))
+ return -1;
+
+#ifdef IMUSE_DEBUG
+ {
+ char string[128];
+ sprintf(string, "doCommand - %d (%d/%d)", a[0], (int)param, (int)cmd);
+ for (i = 1; i < numargs; ++i)
+ sprintf(string + strlen(string), ", %d", a[i]);
+ debug(0, string);
+ }
+#endif
+
+ if (param == 0) {
+ switch (cmd) {
+ case 6:
+ if (a[1] > 127)
+ return -1;
+ else {
+ debug(0, "IMuse doCommand(6) - setImuseMasterVolume (%d)", a[1]);
+ return setImuseMasterVolume((a[1] << 1) | (a[1] ? 0 : 1)); // Convert from 0-127 to 0-255
+ }
+ case 7:
+ debug(0, "IMuse doCommand(7) - getMasterVolume (%d)", a[1]);
+ return _master_volume / 2; // Convert from 0-255 to 0-127
+ case 8:
+ return startSound(a[1]) ? 0 : -1;
+ case 9:
+ return stopSound(a[1]);
+ case 10: // FIXME: Sam and Max - Not sure if this is correct
+ return stopAllSounds();
+ case 11:
+ return stopAllSounds();
+ case 12:
+ // Sam & Max: Player-scope commands
+ player = findActivePlayer(a[1]);
+ if (!player)
+ return -1;
+
+ switch (a[3]) {
+ case 6:
+ // Set player volume.
+ return player->setVolume(a[4]);
+ default:
+ error("IMuseInternal::doCommand(12) unsupported sub-command %d", a[3]);
+ }
+ return -1;
+ case 13:
+ return getSoundStatus(a[1]);
+ case 14:
+ // Sam and Max: Parameter fade
+ player = findActivePlayer(a[1]);
+ if (player)
+ return player->addParameterFader(a[3], a[4], a[5]);
+ return -1;
+
+ case 15:
+ // Sam & Max: Set hook for a "maybe" jump
+ player = findActivePlayer(a[1]);
+ if (player) {
+ player->setHook(0, a[3], 0);
+ return 0;
+ }
+ return -1;
+ case 16:
+ debug(0, "IMuse doCommand(16) - set_volchan (%d, %d)", a[1], a[2]);
+ return set_volchan(a[1], a[2]);
+ case 17:
+ if (g_scumm->_gameId != GID_SAMNMAX) {
+ debug(0, "IMuse doCommand(17) - set_channel_volume (%d, %d)", a[1], a[2]);
+ return set_channel_volume(a[1], a[2]);
+ } else {
+ if (a[4]) {
+ int b[16];
+ memset(b, 0, sizeof(b));
+ for (i = 0; i < numargs; ++i)
+ b[i] = a[i];
+ return ImSetTrigger(b[1], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11]);
+ } else {
+ return ImClearTrigger(a[1], a[3]);
+ }
+ }
+ case 18:
+ if (g_scumm->_gameId != GID_SAMNMAX) {
+ return set_volchan_entry(a[1], a[2]);
+ } else {
+ // Sam & Max: ImCheckTrigger.
+ // According to Mike's notes to Ender,
+ // this function returns the number of triggers
+ // associated with a particular player ID and
+ // trigger ID.
+ a[0] = 0;
+ for (i = 0; i < ARRAYSIZE(_snm_triggers); ++i) {
+ if (_snm_triggers[i].sound == a[1] && _snm_triggers[i].id &&
+ (a[3] == -1 || _snm_triggers[i].id == a[3]))
+ {
+ ++a[0];
+ }
+ }
+ return a[0];
+ }
+ case 19:
+ // Sam & Max: ImClearTrigger
+ // This should clear a trigger that's been set up
+ // with ImSetTrigger(cmd == 17). Seems to work....
+ return ImClearTrigger(a[1], a[3]);
+ case 20:
+ // Sam & Max: Deferred Command
+ addDeferredCommand(a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ return 0;
+ case 2:
+ case 3:
+ return 0;
+ default:
+ error("doCommand(%d [%d/%d], %d, %d, %d, %d, %d, %d, %d) unsupported", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ }
+ } else if (param == 1) {
+ if ((1 << cmd) & 0x783FFF) {
+ player = findActivePlayer(a[1]);
+ if (!player)
+ return -1;
+ if ((1 << cmd) & (1 << 11 | 1 << 22)) {
+ assert(a[2] >= 0 && a[2] <= 15);
+ player = (Player *)player->getPart(a[2]);
+ if (!player)
+ return -1;
+ }
+ }
+
+ switch (cmd) {
+ case 0:
+ if (g_scumm->_gameId == GID_SAMNMAX) {
+ if (a[3] == 1) // Measure number
+ return ((player->getBeatIndex() - 1) >> 2) + 1;
+ else if (a[3] == 2) // Beat number
+ return player->getBeatIndex();
+ return -1;
+ } else {
+ return player->getParam(a[2], a[3]);
+ }
+ case 1:
+ if (g_scumm->_gameId == GID_SAMNMAX) {
+ // FIXME: Could someone verify this?
+ //
+ // This jump instruction is known to be used in
+ // the following cases:
+ //
+ // 1) Going anywhere on the USA map
+ // 2) Winning the Wak-A-Rat game
+ // 3) Losing or quitting the Wak-A-Rat game
+ // 4) Conroy hitting Max with a golf club
+ //
+ // For all these cases the position parameters
+ // are always the same: 2, 1, 0, 0.
+ //
+ // 5) When leaving the bigfoot party. The
+ // position parameters are: 3, 4, 300, 0
+ // 6) At Frog Rock, when the UFO appears. The
+ // position parameters are: 10, 4, 400, 1
+ //
+ // The last two cases used to be buggy, so I
+ // have made a change to how the last two
+ // position parameters are handled. I still do
+ // not know if it's correct, but it sounds
+ // good to me at least.
+
+ debug(0, "doCommand(%d [%d/%d], %d, %d, %d, %d, %d, %d, %d)", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ player->jump(a[3] - 1, (a[4] - 1) * 4 + a[5], a[6] + ((a[7] * player->getTicksPerBeat()) >> 2));
+ } else
+ player->setPriority(a[2]);
+ return 0;
+ case 2:
+ return player->setVolume(a[2]);
+ case 3:
+ player->setPan(a[2]);
+ return 0;
+ case 4:
+ return player->setTranspose(a[2], a[3]);
+ case 5:
+ player->setDetune(a[2]);
+ return 0;
+ case 6:
+ player->setSpeed(a[2]);
+ return 0;
+ case 7:
+ return player->jump(a[2], a[3], a[4]) ? 0 : -1;
+ case 8:
+ return player->scan(a[2], a[3], a[4]);
+ case 9:
+ return player->setLoop(a[2], a[3], a[4], a[5], a[6]) ? 0 : -1;
+ case 10:
+ player->clearLoop();
+ return 0;
+ case 11:
+ ((Part *)player)->set_onoff(a[3] != 0);
+ return 0;
+ case 12:
+ return player->setHook(a[2], a[3], a[4]);
+ case 13:
+ return player->addParameterFader(ParameterFader::pfVolume, a[2], a[3]);
+ case 14:
+ return enqueue_trigger(a[1], a[2]);
+ case 15:
+ return enqueue_command(a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ case 16:
+ return clear_queue();
+ case 19:
+ return player->getParam(a[2], a[3]);
+ case 20:
+ return player->setHook(a[2], a[3], a[4]);
+ case 21:
+ return -1;
+ case 22:
+ ((Part *)player)->volume(a[3]);
+ return 0;
+ case 23:
+ return query_queue(a[1]);
+ case 24:
+ return 0;
+ default:
+ error("doCommand(%d [%d/%d], %d, %d, %d, %d, %d, %d, %d) unsupported", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ return -1;
+ }
+ }
+
+ return -1;
+}
+
+int32 IMuseInternal::ImSetTrigger(int sound, int id, int a, int b, int c, int d, int e, int f, int g, int h) {
+ // Sam & Max: ImSetTrigger.
+ // Sets a trigger for a particular player and
+ // marker ID, along with doCommand parameters
+ // to invoke at the marker. The marker is
+ // represented by MIDI SysEx block 00 xx(F7)
+ // where "xx" is the marker ID.
+ uint16 oldest_trigger = 0;
+ ImTrigger *oldest_ptr = NULL;
+
+ int i;
+ ImTrigger *trig = _snm_triggers;
+ for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trig) {
+ if (!trig->id)
+ break;
+ // We used to only compare 'id' and 'sound' here, but at least
+ // at the Dino Bungie Memorial that causes the music to stop
+ // after getting the T-Rex tooth. See bug #888161.
+ if (trig->id == id && trig->sound == sound && trig->command[0] == a)
+ break;
+
+ uint16 diff;
+ if (trig->expire <= _snm_trigger_index)
+ diff = _snm_trigger_index - trig->expire;
+ else
+ diff = 0x10000 - trig->expire + _snm_trigger_index;
+
+ if (!oldest_ptr || oldest_trigger < diff) {
+ oldest_ptr = trig;
+ oldest_trigger = diff;
+ }
+ }
+
+ // If we didn't find a trigger, see if we can expire one.
+ if (!i) {
+ if (!oldest_ptr)
+ return -1;
+ trig = oldest_ptr;
+ }
+
+ trig->id = id;
+ trig->sound = sound;
+ trig->expire = (++_snm_trigger_index & 0xFFFF);
+ trig->command[0] = a;
+ trig->command[1] = b;
+ trig->command[2] = c;
+ trig->command[3] = d;
+ trig->command[4] = e;
+ trig->command[5] = f;
+ trig->command[6] = g;
+ trig->command[7] = h;
+
+ // If the command is to start a sound, stop that sound if it's already playing.
+ // This fixes some carnival music problems.
+ // NOTE: We ONLY do this if the sound that will trigger the command is actually
+ // playing. Otherwise, there's a problem when exiting and re-entering the
+ // Bumpusville mansion. Ref Bug #780918.
+ if (trig->command[0] == 8 && getSoundStatus(trig->command[1]) && getSoundStatus(sound))
+ stopSound(trig->command[1]);
+ return 0;
+}
+
+int32 IMuseInternal::ImClearTrigger(int sound, int id) {
+ int count = 0;
+ int i;
+ ImTrigger *trig = _snm_triggers;
+ for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trig) {
+ if ((sound == -1 || trig->sound == sound) && trig->id && (id == -1 || trig->id == id)) {
+ trig->sound = trig->id = 0;
+ ++count;
+ }
+ }
+ return (count > 0) ? 0 : -1;
+}
+
+int32 IMuseInternal::ImFireAllTriggers(int sound) {
+ if (!sound)
+ return 0;
+ int count = 0;
+ int i;
+ for (i = 0; i < ARRAYSIZE(_snm_triggers); ++i) {
+ if (_snm_triggers[i].sound == sound) {
+ _snm_triggers[i].sound = _snm_triggers[i].id = 0;
+ doCommand(8, _snm_triggers[i].command);
+ ++count;
+ }
+ }
+ return (count > 0) ? 0 : -1;
+}
+
+int IMuseInternal::set_channel_volume(uint chan, uint vol)
+{
+ if (chan >= 8 || vol > 127)
+ return -1;
+
+ _channel_volume[chan] = vol;
+ _channel_volume_eff[chan] = _master_volume * _music_volume * vol / 255 / 255;
+ update_volumes();
+ return 0;
+}
+
+void IMuseInternal::update_volumes() {
+ Player *player;
+ int i;
+
+ for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
+ if (player->isActive())
+ player->setVolume(player->getVolume());
+ }
+}
+
+int IMuseInternal::set_volchan_entry(uint a, uint b) {
+ if (a >= 8)
+ return -1;
+ _volchan_table[a] = b;
+ return 0;
+}
+
+int HookDatas::query_param(int param, byte chan) {
+ switch (param) {
+ case 18:
+ return _jump[0];
+ case 19:
+ return _transpose;
+ case 20:
+ return _part_onoff[chan];
+ case 21:
+ return _part_volume[chan];
+ case 22:
+ return _part_program[chan];
+ case 23:
+ return _part_transpose[chan];
+ default:
+ return -1;
+ }
+}
+
+int HookDatas::set(byte cls, byte value, byte chan) {
+ switch (cls) {
+ case 0:
+ if (value != _jump[0]) {
+ _jump[1] = _jump[0];
+ _jump[0] = value;
+ }
+ break;
+ case 1:
+ _transpose = value;
+ break;
+ case 2:
+ if (chan < 16)
+ _part_onoff[chan] = value;
+ else if (chan == 16)
+ memset(_part_onoff, value, 16);
+ break;
+ case 3:
+ if (chan < 16)
+ _part_volume[chan] = value;
+ else if (chan == 16)
+ memset(_part_volume, value, 16);
+ break;
+ case 4:
+ if (chan < 16)
+ _part_program[chan] = value;
+ else if (chan == 16)
+ memset(_part_program, value, 16);
+ break;
+ case 5:
+ if (chan < 16)
+ _part_transpose[chan] = value;
+ else if (chan == 16)
+ memset(_part_transpose, value, 16);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+Player *IMuseInternal::findActivePlayer(int id) {
+ int i;
+ Player *player = _players;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive() && player->getID() == (uint16)id)
+ return player;
+ }
+ return NULL;
+}
+
+int IMuseInternal::get_volchan_entry(uint a) {
+ if (a < 8)
+ return _volchan_table[a];
+ return -1;
+}
+
+uint32 IMuseInternal::property(int prop, uint32 value) {
+ switch (prop) {
+ case IMuse::PROP_TEMPO_BASE:
+ // This is a specified as a percentage of normal
+ // music speed. The number must be an integer
+ // ranging from 50 to 200(for 50% to 200% normal speed).
+ if (value >= 50 && value <= 200)
+ _tempoFactor = value;
+ break;
+
+ case IMuse::PROP_NATIVE_MT32:
+ _native_mt32 = (value > 0);
+ Instrument::nativeMT32(_native_mt32);
+ if (_midi_native && _native_mt32)
+ initMT32(_midi_native);
+ break;
+
+ case IMuse::PROP_GS:
+ _enable_gs = (value > 0);
+
+ // If True Roland MT-32 is not selected, run in GM or GS mode.
+ // If it is selected, change the Roland GS synth to MT-32 mode.
+ if (_midi_native && !_native_mt32)
+ initGM(_midi_native);
+ else if (_midi_native && _native_mt32 && _enable_gs) {
+ _sc55 = true;
+ initGM(_midi_native);
+ }
+ break;
+
+ case IMuse::PROP_LIMIT_PLAYERS:
+ if (value > 0 && value <= ARRAYSIZE(_players))
+ _player_limit = (int)value;
+ break;
+
+ case IMuse::PROP_RECYCLE_PLAYERS:
+ _recycle_players = (value != 0);
+ break;
+
+ case IMuse::PROP_DIRECT_PASSTHROUGH:
+ _direct_passthrough = (value != 0);
+ break;
+ }
+
+ return 0;
+}
+
+void IMuseInternal::setBase(byte **base) {
+ _base_sounds = base;
+}
+
+IMuseInternal *IMuseInternal::create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver) {
+ IMuseInternal *i = new IMuseInternal;
+ i->initialize(syst, nativeMidiDriver, adlibMidiDriver);
+ return i;
+}
+
+int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi, MidiDriver *adlib_midi) {
+ int i;
+
+ _midi_native = native_midi;
+ _midi_adlib = adlib_midi;
+ if (native_midi != NULL)
+ initMidiDriver(native_midi);
+ if (adlib_midi != NULL)
+ initMidiDriver(adlib_midi);
+
+ if (!_tempoFactor)
+ _tempoFactor = 100;
+ _master_volume = 255;
+
+ for (i = 0; i != 8; i++)
+ _channel_volume[i] = _channel_volume_eff[i] = _volchan_table[i] = 127;
+
+ init_players();
+ init_queue();
+ init_parts();
+
+ _initialized = true;
+
+ return 0;
+}
+
+void IMuseInternal::initMidiDriver(MidiDriver *midi) {
+ // Open MIDI driver
+ int result = midi->open();
+ if (result)
+ error("IMuse initialization - %s", MidiDriver::getErrorName(result));
+
+ // Connect to the driver's timer
+ midi->setTimerCallback(midi, &IMuseInternal::midiTimerCallback);
+}
+
+void IMuseInternal::initMT32(MidiDriver *midi) {
+ byte buffer[52];
+ char info[256] = "ScummVM ";
+ int len;
+
+ // Reset the MT-32
+ memcpy(&buffer[0], "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
+ midi->sysEx(buffer, 9);
+ g_system->delayMillis(100);
+
+ // Compute version string (truncated to 20 chars max.)
+ strcat(info, gScummVMVersion);
+ len = strlen(info);
+ if (len > 20)
+ len = 20;
+
+ // Display a welcome message on MT-32 displays.
+ memcpy(&buffer[4], "\x20\x00\x00", 3);
+ memcpy(&buffer[7], " ", 20);
+ memcpy(buffer + 7 +(20 - len) / 2, info, len);
+ byte checksum = 0;
+ for (int i = 4; i < 27; ++i)
+ checksum -= buffer[i];
+ buffer[27] = checksum & 0x7F;
+ midi->sysEx(buffer, 28);
+ g_system->delayMillis(500);
+
+ // Setup master tune, reverb mode, reverb time, reverb level,
+ // channel mapping, partial reserve and master volume
+ memcpy(&buffer[4], "\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 27);
+ midi->sysEx(buffer, 31);
+ g_system->delayMillis(250);
+
+ // Map percussion to notes 24 - 34 without reverb
+ memcpy(&buffer[4], "\x03\x01\x10\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00\x44", 48);
+ midi->sysEx(buffer, 52);
+ g_system->delayMillis(250);
+}
+
+void IMuseInternal::initGM(MidiDriver *midi) {
+ byte buffer[11];
+ int i;
+
+ // General MIDI System On message
+ // Resets all GM devices to default settings
+ memcpy(&buffer[0], "\xF0\x7E\x7F\x09\x01\xF7", 6);
+ midi->sysEx(buffer, 6);
+ debug(2, "GM SysEx: GM System On");
+ g_system->delayMillis(200);
+
+ if (_enable_gs) {
+
+ // All GS devices recognize the GS Reset command,
+ // even with Roland's ID. It is impractical to
+ // support other manufacturers' devices for
+ // further GS settings, as there are limitless
+ // numbers of them out there that would each
+ // require individual SysEx commands with unique IDs.
+
+ // Roland GS SysEx ID
+ memcpy(&buffer[0], "\xF0\x41\x10\x42\x12", 5);
+
+ // GS Reset
+ memcpy(&buffer[5], "\x40\x00\x7F\x00\x41\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: GS Reset");
+ g_system->delayMillis(200);
+
+ if (_sc55) {
+ // This mode is for GS devices that support an MT-32-compatible
+ // Map, such as the Roland Sound Canvas line of modules. It
+ // will allow them to work with True MT-32 mode, but will
+ // obviously still ignore MT-32 SysEx (and thus custom
+ // instruments).
+
+ // Set Channels 1-16 to SC-55 Map, then CM-64/32L Variation
+ for (i = 0; i < 16; ++i) {
+ midi->send(( 127 << 16) | (0 << 8) | (0xB0 | i));
+ midi->send(( 1 << 16) | (32 << 8) | (0xB0 | i));
+ midi->send(( 0 << 16) | (0 << 8) | (0xC0 | i));
+ }
+ debug(2, "GS Program Change: CM-64/32L Map Selected");
+
+ // Set Percussion Channel to SC-55 Map (CC#32, 01H), then
+ // Switch Drum Map to CM-64/32L (MT-32 Compatible Drums)
+ midi->getPercussionChannel()->controlChange(0, 0);
+ midi->getPercussionChannel()->controlChange(32, 1);
+ midi->send(127 << 8 | 0xC0 | 9);
+ debug(2, "GS Program Change: Drum Map is CM-64/32L");
+
+ }
+
+ // Set Master Chorus to 0. The MT-32 has no chorus capability.
+ memcpy(&buffer[5], "\x40\x01\x3A\x00\x05\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Master Chorus Level is 0");
+
+ // Set Channels 1-16 Reverb to 64, which is the
+ // equivalent of MT-32 default Reverb Level 5
+ for (i = 0; i < 16; ++i)
+ midi->send(( 64 << 16) | (91 << 8) | (0xB0 | i));
+ debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64");
+
+ // Set Channels 1-16 Pitch Bend Sensitivity to
+ // 12 semitones; then lock the RPN by setting null.
+ for (i = 0; i < 16; ++i) {
+ midi->send(( 0 << 16) | (100 << 8) | (0xB0 | i));
+ midi->send(( 0 << 16) | (101 << 8) | (0xB0 | i));
+ midi->send(( 12 << 16) | (6 << 8) | (0xB0 | i));
+ midi->send(( 0 << 16) | (38 << 8) | (0xB0 | i));
+ midi->send(( 127 << 16) | (100 << 8) | (0xB0 | i));
+ midi->send(( 127 << 16) | (101 << 8) | (0xB0 | i));
+ }
+ debug(2, "GM Controller 6 Change: Channels 1-16 Pitch Bend Sensitivity is 12 semitones");
+
+ // Set channels 1-16 Mod. LFO1 Pitch Depth to 4
+ memcpy(&buffer[5], "\x40\x20\x04\x04\x18\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x21\x04\x04\x17\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x22\x04\x04\x16\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x23\x04\x04\x15\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x24\x04\x04\x14\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x25\x04\x04\x13\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x26\x04\x04\x12\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x27\x04\x04\x11\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x28\x04\x04\x10\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x29\x04\x04\x0F\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2A\x04\x04\x0E\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2B\x04\x04\x0D\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2C\x04\x04\x0C\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2D\x04\x04\x0B\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2E\x04\x04\x0A\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2F\x04\x04\x09\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4");
+
+ // Set Percussion Channel Expression to 80
+ midi->getPercussionChannel()->controlChange(11, 80);
+ debug(2, "GM Controller 11 Change: Percussion Channel Expression Level is 80");
+
+ // Turn off Percussion Channel Rx. Expression so that
+ // Expression cannot be modified. I don't know why, but
+ // Roland does it this way.
+ memcpy(&buffer[5], "\x40\x10\x0E\x00\x22\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Percussion Channel Rx. Expression is OFF");
+
+ // Change Reverb Character to 0. I don't think this
+ // sounds most like MT-32, but apparently Roland does.
+ memcpy(&buffer[5], "\x40\x01\x31\x00\x0E\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Reverb Character is 0");
+
+ // Change Reverb Pre-LF to 4, which is similar to
+ // what MT-32 reverb does.
+ memcpy(&buffer[5], "\x40\x01\x32\x04\x09\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Reverb Pre-LF is 4");
+
+ // Change Reverb Time to 106; the decay on Hall 2
+ // Reverb is too fast compared to the MT-32's
+ memcpy(&buffer[5], "\x40\x01\x34\x6A\x21\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Reverb Time is 106");
+ }
+}
+
+void IMuseInternal::init_queue() {
+ _queue_adding = false;
+ _queue_pos = 0;
+ _queue_end = 0;
+ _trigger_count = 0;
+}
+
+void IMuseInternal::pause(bool paused) {
+ if (_paused == paused)
+ return;
+ int vol = _music_volume;
+ if (paused)
+ _music_volume = 0;
+ update_volumes();
+ _music_volume = vol;
+
+ // Fix for Bug #817871. The MT-32 apparently fails
+ // sometimes to respond to a channel volume message
+ // (or only uses it for subsequent note events).
+ // The result is hanging notes on pause. Reportedly
+ // happens in the original distro, too. To fix that,
+ // just send AllNotesOff to the channels.
+ if (_midi_native && _native_mt32) {
+ for (int i = 0; i < 16; ++i)
+ _midi_native->send(123 << 8 | 0xB0 | i);
+ }
+
+ _paused = paused;
+}
+
+void IMuseInternal::handleDeferredCommands(MidiDriver *midi) {
+ uint32 advance = midi->getBaseTempo();
+
+ DeferredCommand *ptr = &_deferredCommands[0];
+ int i;
+ for (i = ARRAYSIZE(_deferredCommands); i; --i, ++ptr) {
+ if (!ptr->time_left)
+ continue;
+ if (ptr->time_left <= advance) {
+ doCommand(ptr->a, ptr->b, ptr->c, ptr->d, ptr->e, ptr->f, 0, 0);
+ ptr->time_left = advance;
+ }
+ ptr->time_left -= advance;
+ }
+}
+
+// "time" is interpreted as hundredths of a second.
+// FIXME: Is that correct?
+// We convert it to microseconds before prceeding
+void IMuseInternal::addDeferredCommand(int time, int a, int b, int c, int d, int e, int f) {
+ DeferredCommand *ptr = &_deferredCommands[0];
+ int i;
+ for (i = ARRAYSIZE(_deferredCommands); i; --i, ++ptr) {
+ if (!ptr->time_left)
+ break;
+ }
+
+ if (i) {
+ ptr->time_left = time * 10000;
+ ptr->a = a;
+ ptr->b = b;
+ ptr->c = c;
+ ptr->d = d;
+ ptr->e = e;
+ ptr->f = f;
+ }
+}
+
+////////////////////////////////////////////////////////////
+//
+// IMuseInternal load/save implementation
+//
+////////////////////////////////////////////////////////////
+
+int IMuseInternal::save_or_load(Serializer *ser, ScummEngine *scumm) {
+ const SaveLoadEntry mainEntries[] = {
+ MKLINE(IMuseInternal, _queue_end, sleUint8, VER(8)),
+ MKLINE(IMuseInternal, _queue_pos, sleUint8, VER(8)),
+ MKLINE(IMuseInternal, _queue_sound, sleUint16, VER(8)),
+ MKLINE(IMuseInternal, _queue_adding, sleByte, VER(8)),
+ MKLINE(IMuseInternal, _queue_marker, sleByte, VER(8)),
+ MKLINE(IMuseInternal, _queue_cleared, sleByte, VER(8)),
+ MKLINE(IMuseInternal, _master_volume, sleByte, VER(8)),
+ MKLINE(IMuseInternal, _trigger_count, sleUint16, VER(8)),
+ MKLINE(IMuseInternal, _snm_trigger_index, sleUint16, VER(54)),
+ MKARRAY(IMuseInternal, _channel_volume[0], sleUint16, 8, VER(8)),
+ MKARRAY(IMuseInternal, _volchan_table[0], sleUint16, 8, VER(8)),
+ MKEND()
+ };
+
+ const SaveLoadEntry cmdQueueEntries[] = {
+ MKARRAY(CommandQueue, array[0], sleUint16, 8, VER(23)),
+ MKEND()
+ };
+
+ // VolumeFader is obsolete.
+ const SaveLoadEntry volumeFaderEntries[] = {
+ MK_OBSOLETE(VolumeFader, player, sleUint16, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, active, sleUint8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, curvol, sleUint8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, speed_lo_max, sleUint16, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, num_steps, sleUint16, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, speed_hi, sleInt8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, direction, sleInt8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, speed_lo, sleInt8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, speed_lo_counter, sleUint16, VER(8), VER(16)),
+ MKEND()
+ };
+
+ const SaveLoadEntry snmTriggerEntries[] = {
+ MKLINE(ImTrigger, sound, sleInt16, VER(54)),
+ MKLINE(ImTrigger, id, sleByte, VER(54)),
+ MKLINE(ImTrigger, expire, sleUint16, VER(54)),
+ MKARRAY(ImTrigger, command[0], sleUint16, 8, VER(54)),
+ MKEND()
+ };
+
+ int i;
+
+ ser->saveLoadEntries(this, mainEntries);
+ ser->saveLoadArrayOf(_cmd_queue, ARRAYSIZE(_cmd_queue), sizeof(_cmd_queue[0]), cmdQueueEntries);
+ ser->saveLoadArrayOf(_snm_triggers, ARRAYSIZE(_snm_triggers), sizeof(_snm_triggers[0]), snmTriggerEntries);
+
+ // The players
+ for (i = 0; i < ARRAYSIZE(_players); ++i)
+ _players[i].saveLoadWithSerializer(ser);
+
+ // The parts
+ for (i = 0; i < ARRAYSIZE(_parts); ++i)
+ _parts[i].saveLoadWithSerializer(ser);
+
+ { // Load/save the instrument definitions, which were revamped with V11.
+ Part *part = &_parts[0];
+ if (ser->getVersion() >= VER(11)) {
+ for (i = ARRAYSIZE(_parts); i; --i, ++part) {
+ part->_instrument.saveOrLoad(ser);
+ }
+ } else {
+ for (i = ARRAYSIZE(_parts); i; --i, ++part)
+ part->_instrument.clear();
+ }
+ }
+
+ // VolumeFader has been replaced with the more generic ParameterFader.
+ // FIXME: replace this loop by something like
+ // if (loading && version <= 16) ser->skip(XXX bytes);
+ for (i = 0; i < 8; ++i)
+ ser->saveLoadEntries(0, volumeFaderEntries);
+
+ if (ser->isLoading()) {
+ // Load all sounds that we need
+ fix_players_after_load(scumm);
+ fix_parts_after_load();
+ setImuseMasterVolume(_master_volume);
+
+ if (_midi_native)
+ reallocateMidiChannels(_midi_native);
+ if (_midi_adlib)
+ reallocateMidiChannels(_midi_adlib);
+ }
+
+ return 0;
+}
+
+void IMuseInternal::fix_parts_after_load() {
+ Part *part;
+ int i;
+
+ for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
+ if (part->_player)
+ part->fix_after_load();
+ }
+}
+
+// Only call this routine from the main thread,
+// since it uses getResourceAddress
+void IMuseInternal::fix_players_after_load(ScummEngine *scumm) {
+ Player *player = _players;
+ int i;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive()) {
+ scumm->getResourceAddress(rtSound, player->getID());
+ player->fixAfterLoad();
+ }
+ }
+}
+
+Part::Part() {
+ _slot = 0;
+ _next = 0;
+ _prev = 0;
+ _mc = 0;
+ _player = 0;
+ _pitchbend = 0;
+ _pitchbend_factor = 0;
+ _transpose = 0;
+ _transpose_eff = 0;
+ _vol = 0;
+ _vol_eff = 0;
+ _detune = 0;
+ _detune_eff = 0;
+ _pan = 0;
+ _pan_eff = 0;
+ _on = false;
+ _modwheel = 0;
+ _pedal = false;
+ _pri = 0;
+ _pri_eff = 0;
+ _chan = 0;
+ _effect_level = 0;
+ _chorus = 0;
+ _percussion = 0;
+ _bank = 0;
+ _unassigned_instrument = false;
+}
+
+void Part::saveLoadWithSerializer(Serializer *ser) {
+ const SaveLoadEntry partEntries[] = {
+ MKLINE(Part, _pitchbend, sleInt16, VER(8)),
+ MKLINE(Part, _pitchbend_factor, sleUint8, VER(8)),
+ MKLINE(Part, _transpose, sleInt8, VER(8)),
+ MKLINE(Part, _vol, sleUint8, VER(8)),
+ MKLINE(Part, _detune, sleInt8, VER(8)),
+ MKLINE(Part, _pan, sleInt8, VER(8)),
+ MKLINE(Part, _on, sleUint8, VER(8)),
+ MKLINE(Part, _modwheel, sleUint8, VER(8)),
+ MKLINE(Part, _pedal, sleUint8, VER(8)),
+ MK_OBSOLETE(Part, _program, sleUint8, VER(8), VER(16)),
+ MKLINE(Part, _pri, sleUint8, VER(8)),
+ MKLINE(Part, _chan, sleUint8, VER(8)),
+ MKLINE(Part, _effect_level, sleUint8, VER(8)),
+ MKLINE(Part, _chorus, sleUint8, VER(8)),
+ MKLINE(Part, _percussion, sleUint8, VER(8)),
+ MKLINE(Part, _bank, sleUint8, VER(8)),
+ MKEND()
+ };
+
+ int num;
+ if (ser->isSaving()) {
+ num = (_next ? (_next - _se->_parts + 1) : 0);
+ ser->saveUint16(num);
+
+ num = (_prev ? (_prev - _se->_parts + 1) : 0);
+ ser->saveUint16(num);
+
+ num = (_player ? (_player - _se->_players + 1) : 0);
+ ser->saveUint16(num);
+ } else {
+ num = ser->loadUint16();
+ _next = (num ? &_se->_parts[num - 1] : 0);
+
+ num = ser->loadUint16();
+ _prev = (num ? &_se->_parts[num - 1] : 0);
+
+ num = ser->loadUint16();
+ _player = (num ? &_se->_players[num - 1] : 0);
+ }
+ ser->saveLoadEntries(this, partEntries);
+}
+
+void Part::set_detune(int8 detune) {
+ _detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127);
+ if (_mc)
+ sendPitchBend();
+}
+
+void Part::pitchBend(int16 value) {
+ _pitchbend = value;
+ if (_mc)
+ sendPitchBend();
+}
+
+void Part::volume(byte value) {
+ _vol_eff = ((_vol = value) + 1) * _player->getEffectiveVolume() >> 7;
+ if (_mc)
+ _mc->volume(_vol_eff);
+}
+
+void Part::set_pri(int8 pri) {
+ _pri_eff = clamp((_pri = pri) + _player->getPriority(), 0, 255);
+ if (_mc)
+ _mc->priority(_pri_eff);
+}
+
+void Part::set_pan(int8 pan) {
+ _pan_eff = clamp((_pan = pan) + _player->getPan(), -64, 63);
+ if (_mc)
+ _mc->panPosition(_pan_eff + 0x40);
+}
+
+void Part::set_transpose(int8 transpose) {
+ _transpose_eff = transpose_clamp((_transpose = transpose) + _player->getTranspose(), -24, 24);
+ if (_mc)
+ sendPitchBend();
+}
+
+void Part::sustain(bool value) {
+ _pedal = value;
+ if (_mc)
+ _mc->sustain(value);
+}
+
+void Part::modulationWheel(byte value) {
+ _modwheel = value;
+ if (_mc)
+ _mc->modulationWheel(value);
+}
+
+void Part::chorusLevel(byte value) {
+ _chorus = value;
+ if (_mc)
+ _mc->chorusLevel(value);
+}
+
+void Part::effectLevel(byte value)
+{
+ _effect_level = value;
+ if (_mc)
+ _mc->effectLevel(value);
+}
+
+void Part::fix_after_load() {
+ set_transpose(_transpose);
+ volume(_vol);
+ set_detune(_detune);
+ set_pri(_pri);
+ set_pan(_pan);
+ sendAll();
+}
+
+void Part::pitchBendFactor(byte value) {
+ if (value > 12)
+ return;
+ pitchBend(0);
+ _pitchbend_factor = value;
+ if (_mc)
+ _mc->pitchBendFactor(value);
+}
+
+void Part::set_onoff(bool on) {
+ if (_on != on) {
+ _on = on;
+ if (!on)
+ off();
+ if (!_percussion)
+ _player->_se->reallocateMidiChannels(_player->getMidiDriver());
+ }
+}
+
+void Part::set_instrument(byte * data) {
+ _instrument.adlib(data);
+ if (clearToTransmit())
+ _instrument.send(_mc);
+}
+
+void Part::load_global_instrument(byte slot) {
+ _player->_se->copyGlobalAdlibInstrument(slot, &_instrument);
+ if (clearToTransmit())
+ _instrument.send(_mc);
+}
+
+void Part::noteOn(byte note, byte velocity) {
+ if (!_on)
+ return;
+
+ MidiChannel *mc = _mc;
+
+ // DEBUG
+ if (_unassigned_instrument && !_percussion) {
+ _unassigned_instrument = false;
+ if (!_instrument.isValid()) {
+ debug(0, "[%02d] No instrument specified", (int)_chan);
+ return;
+ }
+ }
+
+ if (mc && _instrument.isValid()) {
+ mc->noteOn(note, velocity);
+ } else if (_percussion) {
+ mc = _player->getMidiDriver()->getPercussionChannel();
+ if (!mc)
+ return;
+ static byte prev_vol_eff = 128;
+ if (_vol_eff != prev_vol_eff){
+ mc->volume(_vol_eff);
+ prev_vol_eff = _vol_eff;
+ }
+ if ((note < 35) && (!_player->_se->isNativeMT32()))
+ note = Instrument::_gmRhythmMap[note];
+
+ mc->noteOn(note, velocity);
+ }
+}
+
+void Part::noteOff(byte note) {
+ if (!_on)
+ return;
+
+ MidiChannel *mc = _mc;
+ if (mc) {
+ mc->noteOff(note);
+ } else if (_percussion) {
+ mc = _player->getMidiDriver()->getPercussionChannel();
+ if (mc)
+ mc->noteOff(note);
+ }
+}
+
+void Part::init() {
+ _player = NULL;
+ _next = NULL;
+ _prev = NULL;
+ _mc = NULL;
+}
+
+void Part::setup(Player *player) {
+ _player = player;
+
+ _percussion = (player->isMIDI() && _chan == 9); // true;
+ _on = true;
+ _pri_eff = player->getPriority();
+ _pri = 0;
+ _vol = 127;
+ _vol_eff = player->getEffectiveVolume();
+ _pan = clamp(player->getPan(), -64, 63);
+ _transpose_eff = player->getTranspose();
+ _transpose = 0;
+ _detune = 0;
+ _detune_eff = player->getDetune();
+ _pitchbend_factor = 2;
+ _pitchbend = 0;
+ _effect_level = 64;
+ _instrument.clear();
+ _unassigned_instrument = true;
+ _chorus = 0;
+ _modwheel = 0;
+ _bank = 0;
+ _pedal = false;
+ _mc = NULL;
+}
+
+void Part::uninit() {
+ if (!_player)
+ return;
+ off();
+ _player->removePart(this);
+ _player = NULL;
+}
+
+void Part::off() {
+ if (_mc) {
+ _mc->allNotesOff();
+ _mc->release();
+ _mc = NULL;
+ }
+}
+
+bool Part::clearToTransmit() {
+ if (_mc)
+ return true;
+ if (_instrument.isValid())
+ _player->_se->reallocateMidiChannels(_player->getMidiDriver());
+ return false;
+}
+
+void Part::sendAll() {
+ if (!clearToTransmit())
+ return;
+ _mc->pitchBendFactor(_pitchbend_factor);
+ sendPitchBend();
+ _mc->volume(_vol_eff);
+ _mc->sustain(_pedal);
+ _mc->modulationWheel(_modwheel);
+ _mc->panPosition(_pan_eff + 0x40);
+ _mc->effectLevel(_effect_level);
+ if (_instrument.isValid())
+ _instrument.send(_mc);
+ _mc->chorusLevel(_chorus);
+ _mc->priority(_pri_eff);
+}
+
+void Part::sendPitchBend() {
+ int16 bend = _pitchbend;
+ // RPN-based pitchbend range doesn't work for the MT32,
+ // so we'll do the scaling ourselves.
+ if (_player->_se->isNativeMT32())
+ bend = bend * _pitchbend_factor / 12;
+ _mc->pitchBend(clamp(bend + (_detune_eff * 64 / 12) + (_transpose_eff * 8192 / 12), -8192, 8191));
+}
+
+void Part::programChange(byte value) {
+ _bank = 0;
+ _instrument.program(value, _player->isMT32());
+ if (clearToTransmit())
+ _instrument.send(_mc);
+}
+
+void Part::set_instrument(uint b) {
+ _bank = (byte)(b >> 8);
+ if (_bank)
+ error("Non-zero instrument bank selection. Please report this");
+ _instrument.program((byte)b, _player->isMT32());
+ if (clearToTransmit())
+ _instrument.send(_mc);
+}
+
+void Part::allNotesOff() {
+ if (!_mc)
+ return;
+ _mc->allNotesOff();
+}
+
+////////////////////////////////////////
+//
+// Some more IMuseInternal stuff
+//
+////////////////////////////////////////
+
+void IMuseInternal::midiTimerCallback(void *data) {
+ MidiDriver *driver = (MidiDriver *)data;
+ if (g_scumm->_imuse)
+ g_scumm->_imuse->on_timer(driver);
+}
+
+void IMuseInternal::reallocateMidiChannels(MidiDriver *midi) {
+ Part *part, *hipart;
+ int i;
+ byte hipri, lopri;
+ Part *lopart;
+
+ while (true) {
+ hipri = 0;
+ hipart = NULL;
+ for (i = 32, part = _parts; i; i--, part++) {
+ if (part->_player && part->_player->getMidiDriver() == midi &&
+ !part->_percussion && part->_on &&
+ !part->_mc && part->_pri_eff >= hipri) {
+ hipri = part->_pri_eff;
+ hipart = part;
+ }
+ }
+
+ if (!hipart)
+ return;
+
+ if ((hipart->_mc = midi->allocateChannel()) == NULL) {
+ lopri = 255;
+ lopart = NULL;
+ for (i = 32, part = _parts; i; i--, part++) {
+ if (part->_mc && part->_mc->device() == midi && part->_pri_eff <= lopri) {
+ lopri = part->_pri_eff;
+ lopart = part;
+ }
+ }
+
+ if (lopart == NULL || lopri >= hipri)
+ return;
+ lopart->off();
+
+ if ((hipart->_mc = midi->allocateChannel()) == NULL)
+ return;
+ }
+ hipart->sendAll();
+ }
+}
+
+void IMuseInternal::setGlobalAdlibInstrument(byte slot, byte *data) {
+ if (slot < 32) {
+ _global_adlib_instruments[slot].adlib(data);
+ }
+}
+
+void IMuseInternal::copyGlobalAdlibInstrument(byte slot, Instrument *dest) {
+ if (slot >= 32)
+ return;
+ _global_adlib_instruments[slot].copy_to(dest);
+}
+
+////////////////////////////////////////////////////////////
+//
+// IMuse implementation
+//
+// IMuse actually serves as a concurency monitor front-end
+// to IMuseInternal and ensures that only one thread
+// accesses the object at a time. This is necessary to
+// prevent scripts and the MIDI parser from yanking objects
+// out from underneath each other.
+//
+////////////////////////////////////////////////////////////
+
+IMuse::IMuse(OSystem *system, IMuseInternal *target)
+ : _system(system), _target(target) {
+ _mutex = system->createMutex();
+}
+
+IMuse::~IMuse() {
+ if (_mutex)
+ _system->deleteMutex(_mutex);
+ if (_target)
+ delete _target;
+}
+
+inline void IMuse::in() const {
+ _system->lockMutex(_mutex);
+}
+inline void IMuse::out() const {
+ _system->unlockMutex(_mutex);
+}
+
+void IMuse::on_timer(MidiDriver *midi) { in(); _target->on_timer(midi); out(); }
+void IMuse::pause(bool paused) { in(); _target->pause(paused); out(); }
+int IMuse::save_or_load(Serializer *ser, ScummEngine *scumm) { in(); int ret = _target->save_or_load(ser, scumm); out(); return ret; }
+void IMuse::setMusicVolume(int vol) { in(); _target->setMusicVolume(vol); out(); }
+void IMuse::startSound(int sound) { in(); _target->startSound(sound); out(); }
+void IMuse::stopSound(int sound) { in(); _target->stopSound(sound); out(); }
+void IMuse::stopAllSounds() { in(); _target->stopAllSounds(); out(); }
+int IMuse::getSoundStatus(int sound) const { in(); int ret = _target->getSoundStatus(sound, true); out(); return ret; }
+bool IMuse::get_sound_active(int sound) const { in(); bool ret = _target->getSoundStatus(sound, false) ? 1 : 0; out(); return ret; }
+int IMuse::getMusicTimer() const { in(); int ret = _target->getMusicTimer(); out(); return ret; }
+int32 IMuse::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) { in(); int32 ret = _target->doCommand(a,b,c,d,e,f,g,h); out(); return ret; }
+int32 IMuse::doCommand(int numargs, int args[]) { in(); int32 ret = _target->doCommand(numargs, args); out(); return ret; }
+int IMuse::clear_queue() { in(); int ret = _target->clear_queue(); out(); return ret; }
+void IMuse::setBase(byte **base) { in(); _target->setBase(base); out(); }
+uint32 IMuse::property(int prop, uint32 value) { in(); uint32 ret = _target->property(prop, value); out(); return ret; }
+void IMuse::terminate() { in(); _target->terminate1(); out(); _target->terminate2(); }
+
+// The IMuse::create method provides a front-end factory
+// for creating IMuseInternal without exposing that class
+// to the client.
+IMuse *IMuse::create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver) {
+ IMuseInternal *engine = IMuseInternal::create(syst, nativeMidiDriver, adlibMidiDriver);
+ return new IMuse(syst, engine);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse.h b/engines/scumm/imuse.h
new file mode 100644
index 0000000000..0c9a9206ba
--- /dev/null
+++ b/engines/scumm/imuse.h
@@ -0,0 +1,85 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef IMUSE_H
+#define IMUSE_H
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "scumm/music.h"
+
+class MidiDriver;
+class OSystem;
+
+namespace Scumm {
+
+class IMuseInternal;
+class ScummEngine;
+class Serializer;
+
+class IMuse : public MusicEngine {
+private:
+ OSystem *_system;
+ IMuseInternal *_target;
+ mutable Common::MutexRef _mutex;
+
+ IMuse(OSystem *system, IMuseInternal *target);
+ void in() const;
+ void out() const;
+
+public:
+ ~IMuse();
+
+ enum {
+ PROP_TEMPO_BASE,
+ PROP_NATIVE_MT32,
+ PROP_GS,
+ PROP_LIMIT_PLAYERS,
+ PROP_RECYCLE_PLAYERS,
+ PROP_DIRECT_PASSTHROUGH
+ };
+
+ void on_timer(MidiDriver *midi);
+ void pause(bool paused);
+ int save_or_load(Serializer *ser, ScummEngine *scumm);
+ void setMusicVolume(int vol);
+ void startSound(int sound);
+ void stopSound(int sound);
+ void stopAllSounds();
+ int getSoundStatus(int sound) const;
+ bool get_sound_active(int sound) const;
+ int getMusicTimer() const;
+ int32 doCommand(int a, int b, int c, int d, int e, int f, int g, int h);
+ int32 doCommand(int numargs, int args[]);
+ int clear_queue();
+ void setBase(byte **base);
+ uint32 property(int prop, uint32 value);
+ void terminate();
+
+ // Factory methods
+ static IMuse *create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse.cpp b/engines/scumm/imuse_digi/dimuse.cpp
new file mode 100644
index 0000000000..c057cc8d85
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse.cpp
@@ -0,0 +1,412 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/timer.h"
+
+#include "scumm/actor.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+IMuseDigital::Track::Track()
+ : soundId(-1), used(false), stream(NULL), stream2(NULL) {
+}
+
+void IMuseDigital::timer_handler(void *refCon) {
+ IMuseDigital *imuseDigital = (IMuseDigital *)refCon;
+ imuseDigital->callback();
+}
+
+IMuseDigital::IMuseDigital(ScummEngine *scumm, int fps)
+ : _vm(scumm) {
+ _pause = false;
+ _sound = new ImuseDigiSndMgr(_vm);
+ _callbackFps = fps;
+ resetState();
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ _track[l] = new Track;
+ _track[l]->trackId = l;
+ _track[l]->used = false;
+ }
+ _vm->_timer->installTimerProc(timer_handler, 1000000 / _callbackFps, this);
+
+ _audioNames = NULL;
+ _numAudioNames = 0;
+}
+
+IMuseDigital::~IMuseDigital() {
+ stopAllSounds();
+ _vm->_timer->removeTimerProc(timer_handler);
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ delete _track[l];
+ }
+ delete _sound;
+ free(_audioNames);
+}
+
+void IMuseDigital::resetState() {
+ _curMusicState = 0;
+ _curMusicSeq = 0;
+ _curMusicCue = 0;
+ memset(_attributes, 0, sizeof(_attributes));
+ _nextSeqToPlay = 0;
+}
+
+void IMuseDigital::saveOrLoad(Serializer *ser) {
+ Common::StackLock lock(_mutex, "IMuseDigital::saveOrLoad()");
+
+ const SaveLoadEntry mainEntries[] = {
+ MK_OBSOLETE(IMuseDigital, _volVoice, sleInt32, VER(31), VER(42)),
+ MK_OBSOLETE(IMuseDigital, _volSfx, sleInt32, VER(31), VER(42)),
+ MK_OBSOLETE(IMuseDigital, _volMusic, sleInt32, VER(31), VER(42)),
+ MKLINE(IMuseDigital, _curMusicState, sleInt32, VER(31)),
+ MKLINE(IMuseDigital, _curMusicSeq, sleInt32, VER(31)),
+ MKLINE(IMuseDigital, _curMusicCue, sleInt32, VER(31)),
+ MKLINE(IMuseDigital, _nextSeqToPlay, sleInt32, VER(31)),
+ MKARRAY(IMuseDigital, _attributes[0], sleInt32, 188, VER(31)),
+ MKEND()
+ };
+
+ const SaveLoadEntry trackEntries[] = {
+ MKLINE(Track, pan, sleInt8, VER(31)),
+ MKLINE(Track, vol, sleInt32, VER(31)),
+ MKLINE(Track, volFadeDest, sleInt32, VER(31)),
+ MKLINE(Track, volFadeStep, sleInt32, VER(31)),
+ MKLINE(Track, volFadeDelay, sleInt32, VER(31)),
+ MKLINE(Track, volFadeUsed, sleByte, VER(31)),
+ MKLINE(Track, soundId, sleInt32, VER(31)),
+ MKARRAY(Track, soundName[0], sleByte, 15, VER(31)),
+ MKLINE(Track, used, sleByte, VER(31)),
+ MKLINE(Track, toBeRemoved, sleByte, VER(31)),
+ MKLINE(Track, souStream, sleByte, VER(31)),
+ MKLINE(Track, started, sleByte, VER(31)),
+ MKLINE(Track, priority, sleInt32, VER(31)),
+ MKLINE(Track, regionOffset, sleInt32, VER(31)),
+ MK_OBSOLETE(Track, trackOffset, sleInt32, VER(31), VER(31)),
+ MKLINE(Track, dataOffset, sleInt32, VER(31)),
+ MKLINE(Track, curRegion, sleInt32, VER(31)),
+ MKLINE(Track, curHookId, sleInt32, VER(31)),
+ MKLINE(Track, volGroupId, sleInt32, VER(31)),
+ MKLINE(Track, soundType, sleInt32, VER(31)),
+ MKLINE(Track, iteration, sleInt32, VER(31)),
+ MKLINE(Track, mod, sleInt32, VER(31)),
+ MKLINE(Track, mixerFlags, sleInt32, VER(31)),
+ MK_OBSOLETE(Track, mixerVol, sleInt32, VER(31), VER(42)),
+ MK_OBSOLETE(Track, mixerPan, sleInt32, VER(31), VER(42)),
+ MKLINE(Track, compressed, sleByte, VER(45)),
+ MKEND()
+ };
+
+ ser->saveLoadEntries(this, mainEntries);
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ Track *track = _track[l];
+ if (!ser->isSaving()) {
+ track->compressed = false;
+ }
+ ser->saveLoadEntries(track, trackEntries);
+ if (!ser->isSaving()) {
+ if (!track->used)
+ continue;
+ track->readyToRemove = false;
+ if ((track->toBeRemoved) || (track->souStream) || (track->curRegion == -1)) {
+ track->stream2 = NULL;
+ track->stream = NULL;
+ track->used = false;
+ continue;
+ }
+
+ track->soundHandle = _sound->openSound(track->soundId,
+ track->soundName, track->soundType,
+ track->volGroupId, -1);
+ if (!track->soundHandle) {
+ warning("IMuseDigital::saveOrLoad: Can't open sound so will not be resumed, propably on diffrent CD");
+ track->stream2 = NULL;
+ track->stream = NULL;
+ track->used = false;
+ continue;
+ }
+
+ if (track->compressed) {
+ track->regionOffset = 0;
+ }
+ track->compressed = _sound->isCompressed(track->soundHandle);
+ if (track->compressed) {
+ track->regionOffset = 0;
+ }
+ track->dataOffset = _sound->getRegionOffset(track->soundHandle, track->curRegion);
+ int bits = _sound->getBits(track->soundHandle);
+ int channels = _sound->getChannels(track->soundHandle);
+ int freq = _sound->getFreq(track->soundHandle);
+ track->iteration = freq * channels;
+ track->mixerFlags = 0;
+ if (channels == 2)
+ track->mixerFlags = Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_REVERSE_STEREO;
+
+ if ((bits == 12) || (bits == 16)) {
+ track->mixerFlags |= Audio::Mixer::FLAG_16BITS;
+ track->iteration *= 2;
+ } else if (bits == 8) {
+ track->mixerFlags |= Audio::Mixer::FLAG_UNSIGNED;
+ } else
+ error("IMuseDigital::saveOrLoad(): Can't handle %d bit samples", bits);
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ if (track->compressed)
+ track->mixerFlags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+
+ int32 streamBufferSize = track->iteration;
+ track->stream2 = NULL;
+ track->stream = makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize);
+
+ const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0;
+ const int vol = track->vol / 1000;
+ Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType;
+
+ if (track->volGroupId == 1)
+ type = Audio::Mixer::kSpeechSoundType;
+ if (track->volGroupId == 2)
+ type = Audio::Mixer::kSFXSoundType;
+ if (track->volGroupId == 3)
+ type = Audio::Mixer::kMusicSoundType;
+
+ _vm->_mixer->playInputStream(type, &track->handle, track->stream, -1, vol, pan, false);
+ }
+ }
+}
+
+void IMuseDigital::callback() {
+ Common::StackLock lock(_mutex, "IMuseDigital::callback()");
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->readyToRemove) {
+ if (track->toBeRemoved) {
+ track->readyToRemove = true;
+ continue;
+ }
+
+ if (_pause || !_vm)
+ return;
+
+ if (track->volFadeUsed) {
+ if (track->volFadeStep < 0) {
+ if (track->vol > track->volFadeDest) {
+ track->vol += track->volFadeStep;
+ if (track->vol < track->volFadeDest) {
+ track->vol = track->volFadeDest;
+ track->volFadeUsed = false;
+ }
+ if (track->vol == 0) {
+ track->toBeRemoved = true;
+ }
+ }
+ } else if (track->volFadeStep > 0) {
+ if (track->vol < track->volFadeDest) {
+ track->vol += track->volFadeStep;
+ if (track->vol > track->volFadeDest) {
+ track->vol = track->volFadeDest;
+ track->volFadeUsed = false;
+ }
+ }
+ }
+ debug(5, "Fade: sound(%d), Vol(%d)", track->soundId, track->vol / 1000);
+ }
+
+ const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0;
+ const int vol = track->vol / 1000;
+ Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType;
+
+ if (track->volGroupId == 1)
+ type = Audio::Mixer::kSpeechSoundType;
+ if (track->volGroupId == 2)
+ type = Audio::Mixer::kSFXSoundType;
+ if (track->volGroupId == 3)
+ type = Audio::Mixer::kMusicSoundType;
+
+ if (track->stream) {
+ byte *data = NULL;
+ int32 result = 0;
+
+ if (track->curRegion == -1) {
+ switchToNextRegion(track);
+ if (track->toBeRemoved)
+ continue;
+ }
+
+ int bits = _sound->getBits(track->soundHandle);
+ int channels = _sound->getChannels(track->soundHandle);
+
+ int32 mixer_size = track->iteration / _callbackFps;
+
+ if (track->stream->endOfData()) {
+ mixer_size *= 2;
+ }
+
+ if ((bits == 12) || (bits == 16)) {
+ if (channels == 1)
+ mixer_size &= ~1;
+ if (channels == 2)
+ mixer_size &= ~3;
+ } else {
+ if (channels == 2)
+ mixer_size &= ~1;
+ }
+
+ if (mixer_size == 0)
+ continue;
+
+ do {
+ if (bits == 12) {
+ byte *ptr = NULL;
+
+ mixer_size += track->mod;
+ int mixer_size_12 = (mixer_size * 3) / 4;
+ int length = (mixer_size_12 / 3) * 4;
+ track->mod = mixer_size - length;
+
+ int32 offset = (track->regionOffset * 3) / 4;
+ int result2 = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &ptr, offset, mixer_size_12);
+ result = BundleCodecs::decode12BitsSample(ptr, &data, result2);
+
+ free(ptr);
+ } else if (bits == 16) {
+ result = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &data, track->regionOffset, mixer_size);
+ if (channels == 1) {
+ result &= ~1;
+ }
+ if (channels == 2) {
+ result &= ~3;
+ }
+ } else if (bits == 8) {
+ result = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &data, track->regionOffset, mixer_size);
+ if (channels == 2) {
+ result &= ~1;
+ }
+ }
+
+ if (result > mixer_size)
+ result = mixer_size;
+
+ if (_vm->_mixer->isReady()) {
+ _vm->_mixer->setChannelVolume(track->handle, vol);
+ _vm->_mixer->setChannelBalance(track->handle, pan);
+ track->stream->append(data, result);
+ track->regionOffset += result;
+ }
+ free(data);
+
+ if (_sound->isEndOfRegion(track->soundHandle, track->curRegion)) {
+ switchToNextRegion(track);
+ if (track->toBeRemoved)
+ break;
+ }
+ mixer_size -= result;
+ assert(mixer_size >= 0);
+ } while (mixer_size != 0);
+ } else if (track->stream2) {
+ if (_vm->_mixer->isReady()) {
+ if (!track->started) {
+ track->started = true;
+ _vm->_mixer->playInputStream(type, &track->handle, track->stream2, -1, vol, pan, false);
+ } else {
+ _vm->_mixer->setChannelVolume(track->handle, vol);
+ _vm->_mixer->setChannelBalance(track->handle, pan);
+ }
+ }
+ }
+ }
+ }
+}
+
+void IMuseDigital::switchToNextRegion(Track *track) {
+ assert(track);
+ debug(5, "switchToNextRegion(track:%d)", track->trackId);
+
+ if (track->trackId >= MAX_DIGITAL_TRACKS) {
+ track->toBeRemoved = true;
+ debug(5, "exit (fadetrack can't go next region) switchToNextRegion(trackId:%d)", track->trackId);
+ return;
+ }
+
+ int num_regions = _sound->getNumRegions(track->soundHandle);
+
+ if (++track->curRegion == num_regions) {
+ track->toBeRemoved = true;
+ debug(5, "exit (end of regions) switchToNextRegion(track:%d)", track->trackId);
+ return;
+ }
+
+ ImuseDigiSndMgr::soundStruct *soundHandle = track->soundHandle;
+ int jumpId = _sound->getJumpIdByRegionAndHookId(soundHandle, track->curRegion, track->curHookId);
+ if (jumpId == -1)
+ jumpId = _sound->getJumpIdByRegionAndHookId(soundHandle, track->curRegion, 0);
+ if (jumpId != -1) {
+ int region = _sound->getRegionIdByJumpId(soundHandle, jumpId);
+ assert(region != -1);
+ int sampleHookId = _sound->getJumpHookId(soundHandle, jumpId);
+ assert(sampleHookId != -1);
+ int fadeDelay = (60 * _sound->getJumpFade(soundHandle, jumpId)) / 1000;
+ if (sampleHookId != 0) {
+ if (track->curHookId == sampleHookId) {
+ if (fadeDelay != 0) {
+ Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay);
+ if (fadeTrack) {
+ fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundHandle, fadeTrack->curRegion);
+ fadeTrack->regionOffset = 0;
+ debug(5, "switchToNextRegion-sound(%d) select region %d, curHookId: %d", fadeTrack->soundId, fadeTrack->curRegion, fadeTrack->curHookId);
+ fadeTrack->curHookId = 0;
+ }
+ }
+ track->curRegion = region;
+ debug(5, "switchToNextRegion-sound(%d) jump to region %d, curHookId: %d", track->soundId, track->curRegion, track->curHookId);
+ track->curHookId = 0;
+ }
+ } else {
+ if (fadeDelay != 0) {
+ Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay);
+ if (fadeTrack) {
+ fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundHandle, fadeTrack->curRegion);
+ fadeTrack->regionOffset = 0;
+ debug(5, "switchToNextRegion-sound(%d) select region %d, curHookId: %d", fadeTrack->soundId, fadeTrack->curRegion, fadeTrack->curHookId);
+ }
+ }
+ track->curRegion = region;
+ debug(5, "switchToNextRegion-sound(%d) jump to region %d, curHookId: %d", track->soundId, track->curRegion, track->curHookId);
+ }
+ }
+
+ debug(5, "switchToNextRegion-sound(%d) select region %d, curHookId: %d", track->soundId, track->curRegion, track->curHookId);
+ track->dataOffset = _sound->getRegionOffset(soundHandle, track->curRegion);
+ track->regionOffset = 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse.h b/engines/scumm/imuse_digi/dimuse.h
new file mode 100644
index 0000000000..fed2e48457
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse.h
@@ -0,0 +1,239 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#if !defined(IMUSE_DIGI_H) && !defined(DISABLE_SCUMM_7_8)
+#define IMUSE_DIGI_H
+
+#include "common/scummsys.h"
+#include "common/util.h"
+
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+#include "scumm/imuse_digi/dimuse_sndmgr.h"
+#include "scumm/music.h"
+#include "scumm/sound.h"
+
+#include "sound/mixer.h"
+#include "sound/audiostream.h"
+
+namespace Scumm {
+
+#define MAX_DIGITAL_TRACKS 8
+#define MAX_DIGITAL_FADETRACKS 8
+
+struct imuseDigTable;
+struct imuseComiTable;
+class Serializer;
+
+class IMuseDigital : public MusicEngine {
+private:
+
+ int _callbackFps;
+
+ struct Track {
+ int trackId;
+
+ int8 pan; // pan
+ int32 vol; // volume
+ int32 volFadeDest; //
+ int32 volFadeStep; //
+ int32 volFadeDelay; //
+ bool volFadeUsed; //
+
+ int32 soundId;
+ char soundName[15];
+ bool used;
+ bool toBeRemoved;
+ bool readyToRemove;
+ bool started;
+ bool souStream;
+ bool compressed;
+ int32 priority;
+ int32 regionOffset;
+ int32 dataOffset;
+ int32 curRegion;
+ int32 curHookId;
+ int32 volGroupId;
+ int32 soundType;
+ int32 iteration;
+ int32 mod;
+ int32 mixerFlags;
+
+ ImuseDigiSndMgr::soundStruct *soundHandle;
+ Audio::SoundHandle handle;
+ AppendableAudioStream *stream;
+ AudioStream *stream2;
+
+ Track();
+ };
+
+ Track *_track[MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS];
+
+ Common::Mutex _mutex;
+ ScummEngine *_vm;
+ ImuseDigiSndMgr *_sound;
+
+ char *_audioNames;
+ int32 _numAudioNames;
+
+ bool _pause;
+
+ int32 _attributes[188];
+ int32 _nextSeqToPlay;
+ int32 _curMusicState;
+ int32 _curMusicSeq;
+ int32 _curMusicCue;
+
+ static void timer_handler(void *refConf);
+ void callback();
+ void switchToNextRegion(Track *track);
+ int allocSlot(int priority);
+ void startSound(int soundId, const char *soundName, int soundType, int volGroupId, AudioStream *input, int hookId, int volume, int priority);
+ void selectVolumeGroup(int soundId, int volGroupId);
+
+ int32 getPosInMs(int soundId);
+ void getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height);
+
+ int getSoundIdByName(const char *soundName);
+ void fadeOutMusic(int fadeDelay);
+ Track *cloneToFadeOutTrack(Track *track, int fadeDelay);
+
+ void setFtMusicState(int stateId);
+ void setFtMusicSequence(int seqId);
+ void setFtMusicCuePoint(int cueId);
+ void playFtMusic(const char *songName, int opcode, int volume);
+
+ void setComiMusicState(int stateId);
+ void setComiMusicSequence(int seqId);
+ void playComiMusic(const char *songName, const imuseComiTable *table, int atribPos, bool sequence);
+
+ void setDigMusicState(int stateId);
+ void setDigMusicSequence(int seqId);
+ void playDigMusic(const char *songName, const imuseDigTable *table, int atribPos, bool sequence);
+
+public:
+ IMuseDigital(ScummEngine *scumm, int fps);
+ virtual ~IMuseDigital();
+
+ void setAudioNames(int32 num, char *names);
+
+ void startVoice(int soundId, AudioStream *input);
+ void startVoice(int soundId, const char *soundName);
+ void startMusic(int soundId, int volume);
+ void startMusic(const char *soundName, int soundId, int hookId, int volume);
+ void startSfx(int soundId, int priority);
+ void startSound(int sound)
+ { error("MusicEngine::startSound() Should be never called"); }
+
+ void saveOrLoad(Serializer *ser);
+ void resetState();
+
+ void setPriority(int soundId, int priority);
+ void setVolume(int soundId, int volume);
+ void setPan(int soundId, int pan);
+ void setFade(int soundId, int destVolume, int delay60HzTicks);
+ int getCurMusicSoundId();
+ char *getCurMusicSoundName();
+ void setHookId(int soundId, int hookId);
+ void setMusicVolume(int vol) {}
+ void stopSound(int sound);
+ void stopAllSounds();
+ void pause(bool pause);
+ void parseScriptCmds(int cmd, int soundId, int sub_cmd, int d, int e, int f, int g, int h);
+ void refreshScripts();
+ void flushTracks();
+ int getSoundStatus(int sound) const;
+ int32 getCurMusicPosInMs();
+ int32 getCurVoiceLipSyncWidth();
+ int32 getCurVoiceLipSyncHeight();
+ int32 getCurMusicLipSyncWidth(int syncId);
+ int32 getCurMusicLipSyncHeight(int syncId);
+};
+
+struct imuseRoomMap {
+ int8 roomId;
+ byte stateIndex1;
+ byte offset;
+ byte stateIndex2;
+ byte atribPos;
+ byte stateIndex3;
+};
+
+struct imuseDigTable {
+ byte opcode;
+ int16 soundId;
+ char name[20];
+ byte atribPos;
+ byte hookId;
+ char filename[13];
+};
+
+struct imuseComiTable {
+ byte opcode;
+ int16 soundId;
+ char name[20];
+ byte atribPos;
+ byte hookId;
+ int16 fadeOut60TicksDelay;
+ char filename[13];
+};
+
+
+struct imuseFtNames {
+ char name[20];
+};
+
+struct imuseFtStateTable {
+ char audioName[9];
+ byte opcode;
+ byte volume;
+ char name[21];
+};
+
+struct imuseFtSeqTable {
+ char audioName[9];
+ byte opcode;
+ byte volume;
+};
+
+#ifdef PALMOS_68K
+extern const imuseRoomMap *_digStateMusicMap;
+extern const imuseDigTable *_digStateMusicTable;
+extern const imuseDigTable *_digSeqMusicTable;
+extern const imuseComiTable *_comiStateMusicTable;
+extern const imuseComiTable *_comiSeqMusicTable;
+extern const imuseFtStateTable *_ftStateMusicTable;
+extern const imuseFtSeqTable *_ftSeqMusicTable;
+extern const imuseFtNames *_ftSeqNames;
+#else
+extern const imuseRoomMap _digStateMusicMap[];
+extern const imuseDigTable _digStateMusicTable[];
+extern const imuseDigTable _digSeqMusicTable[];
+extern const imuseComiTable _comiStateMusicTable[];
+extern const imuseComiTable _comiSeqMusicTable[];
+extern const imuseFtStateTable _ftStateMusicTable[];
+extern const imuseFtSeqTable _ftSeqMusicTable[];
+extern const imuseFtNames _ftSeqNames[];
+#endif
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.cpp b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
new file mode 100644
index 0000000000..d36d733ef2
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
@@ -0,0 +1,344 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "scumm/scumm.h"
+#include "scumm/util.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+namespace Scumm {
+
+BundleDirCache::BundleDirCache() {
+ for (int fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) {
+ _budleDirCache[fileId].bundleTable = NULL;
+ _budleDirCache[fileId].fileName[0] = 0;
+ _budleDirCache[fileId].numFiles = 0;
+ _budleDirCache[fileId].compressedBun = false;
+ _budleDirCache[fileId].indexTable = NULL;
+ }
+}
+
+BundleDirCache::~BundleDirCache() {
+ for (int fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) {
+ free(_budleDirCache[fileId].bundleTable);
+ free(_budleDirCache[fileId].indexTable);
+ }
+}
+
+BundleDirCache::AudioTable *BundleDirCache::getTable(int slot) {
+ return _budleDirCache[slot].bundleTable;
+}
+
+int32 BundleDirCache::getNumFiles(int slot) {
+ return _budleDirCache[slot].numFiles;
+}
+
+BundleDirCache::IndexNode *BundleDirCache::getIndexTable(int slot) {
+ return _budleDirCache[slot].indexTable;
+}
+
+bool BundleDirCache::isCompressed(int slot) {
+ return _budleDirCache[slot].compressedBun;
+}
+
+int BundleDirCache::matchFile(const char *filename) {
+ int32 tag, offset;
+ bool found = false;
+ int freeSlot = -1;
+ int fileId;
+
+ for (fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) {
+ if ((_budleDirCache[fileId].bundleTable == NULL) && (freeSlot == -1)) {
+ freeSlot = fileId;
+ }
+ if (scumm_stricmp(filename, _budleDirCache[fileId].fileName) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ScummFile file;
+
+ if (g_scumm->openFile(file, filename) == false) {
+ error("BundleDirCache::matchFile() Can't open bundle file: %s", filename);
+ return false;
+ }
+
+ if (freeSlot == -1)
+ error("BundleDirCache::matchFileFile() Can't find free slot for file bundle dir cache");
+
+ tag = file.readUint32BE();
+ if (tag == 'LB23')
+ _budleDirCache[freeSlot].compressedBun = true;
+ offset = file.readUint32BE();
+
+ strcpy(_budleDirCache[freeSlot].fileName, filename);
+ _budleDirCache[freeSlot].numFiles = file.readUint32BE();
+ _budleDirCache[freeSlot].bundleTable = (AudioTable *) malloc(_budleDirCache[freeSlot].numFiles * sizeof(AudioTable));
+
+ file.seek(offset, SEEK_SET);
+
+ _budleDirCache[freeSlot].indexTable =
+ (IndexNode *)calloc(_budleDirCache[freeSlot].numFiles, sizeof(IndexNode));
+
+ for (int32 i = 0; i < _budleDirCache[freeSlot].numFiles; i++) {
+ char name[24], c;
+ int32 z = 0;
+ int32 z2;
+
+ if (tag == 'LB23') {
+ file.read(_budleDirCache[freeSlot].bundleTable[i].filename, 24);
+ } else {
+ for (z2 = 0; z2 < 8; z2++)
+ if ((c = file.readByte()) != 0)
+ name[z++] = c;
+ name[z++] = '.';
+ for (z2 = 0; z2 < 4; z2++)
+ if ((c = file.readByte()) != 0)
+ name[z++] = c;
+
+ name[z] = '\0';
+ strcpy(_budleDirCache[freeSlot].bundleTable[i].filename, name);
+ }
+ _budleDirCache[freeSlot].bundleTable[i].offset = file.readUint32BE();
+ _budleDirCache[freeSlot].bundleTable[i].size = file.readUint32BE();
+ strcpy(_budleDirCache[freeSlot].indexTable[i].filename,
+ _budleDirCache[freeSlot].bundleTable[i].filename);
+ _budleDirCache[freeSlot].indexTable[i].index = i;
+ }
+ qsort(_budleDirCache[freeSlot].indexTable, _budleDirCache[freeSlot].numFiles,
+ sizeof(IndexNode), (int (*)(const void*, const void*))scumm_stricmp);
+ return freeSlot;
+ } else {
+ return fileId;
+ }
+}
+
+BundleMgr::BundleMgr(BundleDirCache *cache) {
+ _cache = cache;
+ _bundleTable = NULL;
+ _compTable = NULL;
+ _numFiles = 0;
+ _numCompItems = 0;
+ _curSample = -1;
+ _fileBundleId = -1;
+ _compInput = NULL;
+}
+
+BundleMgr::~BundleMgr() {
+ close();
+}
+
+Common::File *BundleMgr::getFile(const char *filename, int32 &offset, int32 &size) {
+ BundleDirCache::IndexNode target;
+ strcpy(target.filename, filename);
+ BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
+ sizeof(BundleDirCache::IndexNode), (int (*)(const void*, const void*))scumm_stricmp);
+ if (found) {
+ _file.seek(_bundleTable[found->index].offset, SEEK_SET);
+ offset = _bundleTable[found->index].offset;
+ size = _bundleTable[found->index].size;
+ return &_file;
+ }
+
+ return NULL;
+}
+
+bool BundleMgr::open(const char *filename, bool &compressed, bool errorFlag) {
+ if (_file.isOpen())
+ return true;
+
+ if (g_scumm->openFile(_file, filename) == false) {
+ if (errorFlag) {
+ error("BundleMgr::open() Can't open bundle file: %s", filename);
+ } else {
+ warning("BundleMgr::open() Can't open bundle file: %s", filename);
+ }
+ return false;
+ }
+
+ int slot = _cache->matchFile(filename);
+ assert(slot != -1);
+ compressed = _cache->isCompressed(slot);
+ _numFiles = _cache->getNumFiles(slot);
+ assert(_numFiles);
+ _bundleTable = _cache->getTable(slot);
+ _indexTable = _cache->getIndexTable(slot);
+ assert(_bundleTable);
+ _compTableLoaded = false;
+ _outputSize = 0;
+ _lastBlock = -1;
+
+ return true;
+}
+
+void BundleMgr::close() {
+ if (_file.isOpen()) {
+ _file.close();
+ _bundleTable = NULL;
+ _numFiles = 0;
+ _numCompItems = 0;
+ _compTableLoaded = false;
+ _lastBlock = -1;
+ _outputSize = 0;
+ _curSample = -1;
+ free(_compTable);
+ _compTable = NULL;
+ free(_compInput);
+ _compInput = NULL;
+ }
+}
+
+bool BundleMgr::loadCompTable(int32 index) {
+ _file.seek(_bundleTable[index].offset, SEEK_SET);
+ uint32 tag = _file.readUint32BE();
+ _numCompItems = _file.readUint32BE();
+ assert(_numCompItems > 0);
+ _file.seek(8, SEEK_CUR);
+
+ if (tag != MKID_BE('COMP')) {
+ error("BundleMgr::loadCompTable() Compressed sound %d invalid (%s)", index, tag2str(tag));
+ return false;
+ }
+
+ _compTable = (CompTable *)malloc(sizeof(CompTable) * _numCompItems);
+ int32 maxSize = 0;
+ for (int i = 0; i < _numCompItems; i++) {
+ _compTable[i].offset = _file.readUint32BE();
+ _compTable[i].size = _file.readUint32BE();
+ _compTable[i].codec = _file.readUint32BE();
+ _file.seek(4, SEEK_CUR);
+ if (_compTable[i].size > maxSize)
+ maxSize = _compTable[i].size;
+ }
+ // CMI hack: one more byte at the end of input buffer
+ _compInput = (byte *)malloc(maxSize + 1);
+
+ return true;
+}
+
+int32 BundleMgr::decompressSampleByCurIndex(int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside) {
+ return decompressSampleByIndex(_curSample, offset, size, comp_final, header_size, header_outside);
+}
+
+int32 BundleMgr::decompressSampleByIndex(int32 index, int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside) {
+ int32 i, final_size, output_size;
+ int skip, first_block, last_block;
+
+ assert(0 <= index && index < _numFiles);
+
+ if (_file.isOpen() == false) {
+ error("BundleMgr::decompressSampleByIndex() File is not open!");
+ return 0;
+ }
+
+ if (_curSample == -1)
+ _curSample = index;
+
+ assert(_curSample == index);
+
+ if (!_compTableLoaded) {
+ _compTableLoaded = loadCompTable(index);
+ if (!_compTableLoaded)
+ return 0;
+ }
+
+ first_block = (offset + header_size) / 0x2000;
+ last_block = (offset + header_size + size - 1) / 0x2000;
+
+ // Clip last_block by the total number of blocks (= "comp items")
+ if ((last_block >= _numCompItems) && (_numCompItems > 0))
+ last_block = _numCompItems - 1;
+
+ int32 blocks_final_size = 0x2000 * (1 + last_block - first_block);
+ *comp_final = (byte *)malloc(blocks_final_size);
+ final_size = 0;
+
+ skip = (offset + header_size) % 0x2000;
+
+ for (i = first_block; i <= last_block; i++) {
+ if (_lastBlock != i) {
+ // CMI hack: one more zero byte at the end of input buffer
+ _compInput[_compTable[i].size] = 0;
+ _file.seek(_bundleTable[index].offset + _compTable[i].offset, SEEK_SET);
+ _file.read(_compInput, _compTable[i].size);
+ _outputSize = BundleCodecs::decompressCodec(_compTable[i].codec, _compInput, _compOutput, _compTable[i].size);
+ if (_outputSize > 0x2000) {
+ error("_outputSize: %d", _outputSize);
+ }
+ _lastBlock = i;
+ }
+
+ output_size = _outputSize;
+
+ if (header_outside) {
+ output_size -= skip;
+ } else {
+ if ((header_size != 0) && (skip >= header_size))
+ output_size -= skip;
+ }
+
+ if ((output_size + skip) > 0x2000) // workaround
+ output_size -= (output_size + skip) - 0x2000;
+
+ if (output_size > size)
+ output_size = size;
+
+ assert(final_size + output_size <= blocks_final_size);
+
+ memcpy(*comp_final + final_size, _compOutput + skip, output_size);
+ final_size += output_size;
+
+ size -= output_size;
+ assert(size >= 0);
+ if (size == 0)
+ break;
+
+ skip = 0;
+ }
+
+ return final_size;
+}
+
+int32 BundleMgr::decompressSampleByName(const char *name, int32 offset, int32 size, byte **comp_final, bool header_outside) {
+ int32 final_size = 0;
+
+ if (!_file.isOpen()) {
+ error("BundleMgr::decompressSampleByName() File is not open!");
+ return 0;
+ }
+
+ BundleDirCache::IndexNode target;
+ strcpy(target.filename, name);
+ BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
+ sizeof(BundleDirCache::IndexNode), (int (*)(const void*, const void*))scumm_stricmp);
+ if (found) {
+ final_size = decompressSampleByIndex(found->index, offset, size, comp_final, 0, header_outside);
+ return final_size;
+ }
+
+ debug(2, "BundleMgr::decompressSampleByName() Failed finding voice %s", name);
+ return final_size;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.h b/engines/scumm/imuse_digi/dimuse_bndmgr.h
new file mode 100644
index 0000000000..36d4f5683e
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_bndmgr.h
@@ -0,0 +1,115 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef BUNDLE_MGR_H
+#define BUNDLE_MGR_H
+
+#include "common/scummsys.h"
+#include "common/file.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+class BundleDirCache {
+public:
+ struct AudioTable {
+ char filename[24];
+ int32 offset;
+ int32 size;
+ };
+
+ struct IndexNode {
+ char filename[24];
+ int32 index;
+ };
+
+private:
+
+ struct FileDirCache {
+ char fileName[20];
+ AudioTable *bundleTable;
+ int32 numFiles;
+ bool compressedBun;
+ IndexNode *indexTable;
+ } _budleDirCache[4];
+
+public:
+ BundleDirCache();
+ ~BundleDirCache();
+
+ int matchFile(const char *filename);
+ AudioTable *getTable(int slot);
+ IndexNode *getIndexTable(int slot);
+ int32 getNumFiles(int slot);
+ bool isCompressed(int slot);
+};
+
+class BundleMgr {
+
+private:
+
+ struct CompTable {
+ int32 offset;
+ int32 size;
+ int32 codec;
+ };
+
+ BundleDirCache *_cache;
+ BundleDirCache::AudioTable *_bundleTable;
+ BundleDirCache::IndexNode *_indexTable;
+ CompTable *_compTable;
+ int _numFiles;
+ int _numCompItems;
+ int _curSample;
+ ScummFile _file;
+ bool _compTableLoaded;
+ int _fileBundleId;
+ byte _compOutput[0x2000];
+ byte *_compInput;
+ int _outputSize;
+ int _lastBlock;
+
+ bool loadCompTable(int32 index);
+
+public:
+
+ BundleMgr(BundleDirCache *_cache);
+ ~BundleMgr();
+
+ bool open(const char *filename, bool &compressed, bool errorFlag=false);
+ void close();
+ Common::File *getFile(const char *filename, int32 &offset, int32 &size);
+ int32 decompressSampleByName(const char *name, int32 offset, int32 size, byte **comp_final, bool header_outside);
+ int32 decompressSampleByIndex(int32 index, int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside);
+ int32 decompressSampleByCurIndex(int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside);
+};
+
+namespace BundleCodecs {
+
+uint32 decode12BitsSample(const byte *src, byte **dst, uint32 size);
+void initializeImcTables();
+int32 decompressCodec(int32 codec, byte *comp_input, byte *comp_output, int32 input_size);
+
+} // End of namespace BundleCodecs
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_codecs.cpp b/engines/scumm/imuse_digi/dimuse_codecs.cpp
new file mode 100644
index 0000000000..dccae928b0
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_codecs.cpp
@@ -0,0 +1,655 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/util.h"
+
+namespace Scumm {
+
+namespace BundleCodecs {
+
+uint32 decode12BitsSample(const byte *src, byte **dst, uint32 size) {
+ uint32 loop_size = size / 3;
+ uint32 s_size = loop_size * 4;
+ byte *ptr = *dst = (byte *)malloc(s_size);
+
+ uint32 tmp;
+ while (loop_size--) {
+ byte v1 = *src++;
+ byte v2 = *src++;
+ byte v3 = *src++;
+ tmp = ((((v2 & 0x0f) << 8) | v1) << 4) - 0x8000;
+ WRITE_BE_UINT16(ptr, tmp); ptr += 2;
+ tmp = ((((v2 & 0xf0) << 4) | v3) << 4) - 0x8000;
+ WRITE_BE_UINT16(ptr, tmp); ptr += 2;
+ }
+ return s_size;
+}
+
+/*
+ * The "IMC" codec below (see cases 13 & 15 in decompressCodec) is actually a
+ * variant of the IMA codec, see also
+ * <http://www.multimedia.cx/simpleaudio.html>
+ *
+ * It is somewhat different, though: the standard ADPCM codecs use a fixed
+ * size for their data packets (4 bits), while the codec implemented here
+ * varies the size of each "packet" between 2 and 7 bits.
+ */
+
+static byte _imcTableEntryBitCount[89];
+
+#ifdef PALMOS_68K
+static const int16 *imcTable;
+#else
+static const int16 imcTable[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 16, 17, 19, 21, 23, 25, 28, 31,
+ 34, 37, 41, 45, 50, 55, 60, 66,
+ 73, 80, 88, 97, 107, 118, 130, 143,
+ 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658,
+ 724, 796, 876, 963, 1060, 1166, 1282, 1411,
+ 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
+ 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
+ 7132, 7845, 8630, 9493,10442,11487,12635,13899,
+ 15289,16818,18500,20350,22385,24623,27086,29794,
+ 32767
+};
+#endif
+
+static const byte imxOtherTable[6][64] = {
+ {
+ 0xFF,
+ 4
+ },
+
+ {
+ 0xFF, 0xFF,
+ 2, 8
+ },
+
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 1, 2, 4, 6
+ },
+
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 1, 2, 4, 6, 8, 12, 16, 32
+ },
+
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 1, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 32
+ },
+
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 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
+ }
+};
+
+void initializeImcTables() {
+ int pos;
+
+ for (pos = 0; pos < ARRAYSIZE(imcTable); ++pos) {
+ byte put = 0;
+ int32 tableValue = ((imcTable[pos] * 4) / 7) / 2;
+ while (tableValue != 0) {
+ tableValue /= 2;
+ put++;
+ }
+ if (put < 2) {
+ put = 2;
+ }
+ if (put > 7) {
+ put = 7;
+ }
+ _imcTableEntryBitCount[pos] = put;
+ }
+}
+
+#define NextBit \
+ do { \
+ bit = mask & 1; \
+ mask >>= 1; \
+ if (!--bitsleft) { \
+ mask = READ_LE_UINT16(srcptr); \
+ srcptr += 2; \
+ bitsleft = 16; \
+ } \
+ } while (0)
+
+static int32 compDecode(byte *src, byte *dst) {
+ byte *result, *srcptr = src, *dstptr = dst;
+ int data, size, bit, bitsleft = 16, mask = READ_LE_UINT16(srcptr);
+ srcptr += 2;
+
+ for (;;) {
+ NextBit;
+ if (bit) {
+ *dstptr++ = *srcptr++;
+ } else {
+ NextBit;
+ if (!bit) {
+ NextBit;
+ size = bit << 1;
+ NextBit;
+ size = (size | bit) + 3;
+ data = *srcptr++ | 0xffffff00;
+ } else {
+ data = *srcptr++;
+ size = *srcptr++;
+
+ data |= 0xfffff000 + ((size & 0xf0) << 4);
+ size = (size & 0x0f) + 3;
+
+ if (size == 3)
+ if (((*srcptr++) + 1) == 1)
+ return dstptr - dst;
+ }
+ result = dstptr + data;
+ while (size--)
+ *dstptr++ = *result++;
+ }
+ }
+}
+#undef NextBit
+
+int32 decompressCodec(int32 codec, byte *comp_input, byte *comp_output, int32 input_size) {
+ int32 output_size, channels;
+ int32 offset1, offset2, offset3, length, k, c, s, j, r, t, z;
+ byte *src, *t_table, *p, *ptr;
+ byte t_tmp1, t_tmp2;
+
+ switch (codec) {
+ case 0:
+ memcpy(comp_output, comp_input, input_size);
+ output_size = input_size;
+ break;
+
+ case 1:
+ output_size = compDecode(comp_input, comp_output);
+ break;
+
+ case 2:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+ break;
+
+ case 3:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+ break;
+
+ case 4:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 0;
+ if (length > 0) {
+ c = -12;
+ s = 0;
+ j = 0;
+ do {
+ ptr = src + length + (k >> 1);
+ t_tmp2 = src[j];
+ if (k & 1) {
+ r = c >> 3;
+ t_table[r + 2] = ((t_tmp2 & 0x0f) << 4) | (ptr[1] >> 4);
+ t_table[r + 1] = (t_tmp2 & 0xf0) | (t_table[r + 1]);
+ } else {
+ r = s >> 3;
+ t_table[r + 0] = ((t_tmp2 & 0x0f) << 4) | (ptr[0] & 0x0f);
+ t_table[r + 1] = t_tmp2 >> 4;
+ }
+ s += 12;
+ c += 12;
+ k++;
+ j++;
+ } while (k < length);
+ }
+ offset1 = ((length - 1) * 3) >> 1;
+ t_table[offset1 + 1] = (t_table[offset1 + 1]) | (src[length - 1] & 0xf0);
+ memcpy(src, t_table, output_size);
+ free(t_table);
+ break;
+
+ case 5:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 1;
+ c = 0;
+ s = 12;
+ t_table[0] = src[length] >> 4;
+ t = length + k;
+ j = 1;
+ if (t > k) {
+ do {
+ t_tmp1 = *(src + length + (k >> 1));
+ t_tmp2 = src[j - 1];
+ if (k & 1) {
+ r = c >> 3;
+ t_table[r + 0] = (t_tmp2 & 0xf0) | t_table[r];
+ t_table[r + 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 & 0x0f);
+ } else {
+ r = s >> 3;
+ t_table[r + 0] = t_tmp2 >> 4;
+ t_table[r - 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 >> 4);
+ }
+ s += 12;
+ c += 12;
+ k++;
+ j++;
+ } while (k < t);
+ }
+ memcpy(src, t_table, output_size);
+ free(t_table);
+ break;
+
+ case 6:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 0;
+ c = 0;
+ j = 0;
+ s = -12;
+ t_table[0] = src[output_size - 1];
+ t_table[output_size - 1] = src[length - 1];
+ t = length - 1;
+ if (t > 0) {
+ do {
+ t_tmp1 = *(src + length + (k >> 1));
+ t_tmp2 = src[j];
+ if (k & 1) {
+ r = s >> 3;
+ t_table[r + 2] = (t_tmp2 & 0xf0) | t_table[r + 2];
+ t_table[r + 3] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 >> 4);
+ } else {
+ r = c >> 3;
+ t_table[r + 2] = t_tmp2 >> 4;
+ t_table[r + 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 & 0x0f);
+ }
+ s += 12;
+ c += 12;
+ k++;
+ j++;
+ } while (k < t);
+ }
+ memcpy(src, t_table, output_size);
+ free(t_table);
+ break;
+
+ case 10:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+ memcpy(t_table, p, output_size);
+
+ offset1 = output_size / 3;
+ offset2 = offset1 << 1;
+ offset3 = offset2;
+ src = comp_output;
+
+ while (offset1--) {
+ offset2 -= 2;
+ offset3--;
+ t_table[offset2 + 0] = src[offset1];
+ t_table[offset2 + 1] = src[offset3];
+ }
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 0;
+ if (length > 0) {
+ c = -12;
+ s = 0;
+ do {
+ j = length + (k >> 1);
+ t_tmp1 = t_table[k];
+ if (k & 1) {
+ r = c >> 3;
+ t_tmp2 = t_table[j + 1];
+ src[r + 2] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4);
+ src[r + 1] = (src[r + 1]) | (t_tmp1 & 0xf0);
+ } else {
+ r = s >> 3;
+ t_tmp2 = t_table[j];
+ src[r + 0] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f);
+ src[r + 1] = t_tmp1 >> 4;
+ }
+ s += 12;
+ c += 12;
+ k++;
+ } while (k < length);
+ }
+ offset1 = ((length - 1) * 3) >> 1;
+ src[offset1 + 1] = (t_table[length] & 0xf0) | src[offset1 + 1];
+ free(t_table);
+ break;
+
+ case 11:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+ memcpy(t_table, p, output_size);
+
+ offset1 = output_size / 3;
+ offset2 = offset1 << 1;
+ offset3 = offset2;
+ src = comp_output;
+
+ while (offset1--) {
+ offset2 -= 2;
+ offset3--;
+ t_table[offset2 + 0] = src[offset1];
+ t_table[offset2 + 1] = src[offset3];
+ }
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 1;
+ c = 0;
+ s = 12;
+ t_tmp1 = t_table[length] >> 4;
+ src[0] = t_tmp1;
+ t = length + k;
+ if (t > k) {
+ do {
+ j = length + (k >> 1);
+ t_tmp1 = t_table[k - 1];
+ t_tmp2 = t_table[j];
+ if (k & 1) {
+ r = c >> 3;
+ src[r + 0] = (src[r]) | (t_tmp1 & 0xf0);
+ src[r + 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f);
+ } else {
+ r = s >> 3;
+ src[r + 0] = t_tmp1 >> 4;
+ src[r - 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4);
+ }
+ s += 12;
+ c += 12;
+ k++;
+ } while (k < t);
+ }
+ free(t_table);
+ break;
+
+ case 12:
+ output_size = compDecode(comp_input, comp_output);
+ p = comp_output;
+ for (z = 2; z < output_size; z++)
+ p[z] += p[z - 1];
+ for (z = 1; z < output_size; z++)
+ p[z] += p[z - 1];
+
+ t_table = (byte *)malloc(output_size);
+ memcpy(t_table, p, output_size);
+
+ offset1 = output_size / 3;
+ offset2 = offset1 << 1;
+ offset3 = offset2;
+ src = comp_output;
+
+ while (offset1--) {
+ offset2 -= 2;
+ offset3--;
+ t_table[offset2 + 0] = src[offset1];
+ t_table[offset2 + 1] = src[offset3];
+ }
+
+ src = comp_output;
+ length = (output_size << 3) / 12;
+ k = 0;
+ c = 0;
+ s = -12;
+ src[0] = t_table[output_size - 1];
+ src[output_size - 1] = t_table[length - 1];
+ t = length - 1;
+ if (t > 0) {
+ do {
+ j = length + (k >> 1);
+ t_tmp1 = t_table[k];
+ t_tmp2 = t_table[j];
+ if (k & 1) {
+ r = s >> 3;
+ src[r + 2] = (src[r + 2]) | (t_tmp1 & 0xf0);
+ src[r + 3] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4);
+ } else {
+ r = c >> 3;
+ src[r + 2] = t_tmp1 >> 4;
+ src[r + 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f);
+ }
+ s += 12;
+ c += 12;
+ k++;
+ } while (k < t);
+ }
+ free(t_table);
+ break;
+
+ case 13:
+ case 15:
+ if (codec == 13) {
+ channels = 1;
+ } else {
+ channels = 2;
+ }
+
+ {
+ // Decoder for the the IMA ADPCM variants used in COMI.
+ // Contrary to regular IMA ADPCM, this codec uses a variable
+ // bitsize for the encoded data.
+
+ const int MAX_CHANNELS = 2;
+ int32 outputSamplesLeft;
+ int32 destPos;
+ int16 firstWord;
+ byte initialTablePos[MAX_CHANNELS] = {0, 0};
+ int32 initialimcTableEntry[MAX_CHANNELS] = {7, 7};
+ int32 initialOutputWord[MAX_CHANNELS] = {0, 0};
+ int32 totalBitOffset, curTablePos, outputWord;
+ byte *dst;
+ int i;
+
+ // We only support mono and stereo
+ assert(channels == 1 || channels == 2);
+
+ src = comp_input;
+ dst = comp_output;
+ output_size = 0x2000;
+ outputSamplesLeft = 0x1000;
+
+ // Every data packet contains 0x2000 bytes of audio data
+ // when extracted. In order to encode bigger data sets,
+ // one has to split the data into multiple blocks.
+ //
+ // Every block starts with a 2 byte word. If that word is
+ // non-zero, it indicates the size of a block of raw audio
+ // data (not encoded) following it. That data we simply copy
+ // to the output buffer and the proceed by decoding the
+ // remaining data.
+ //
+ // If on the other hand the word is zero, then what follows
+ // are 7*channels bytes containing seed data for the decoder.
+ firstWord = READ_BE_UINT16(src);
+ src += 2;
+ if (firstWord != 0) {
+ // Copy raw data
+ memcpy(dst, src, firstWord);
+ dst += firstWord;
+ src += firstWord;
+ assert((firstWord & 1) == 0);
+ outputSamplesLeft -= firstWord / 2;
+ } else {
+ // Read the seed values for the decoder.
+ for (i = 0; i < channels; i++) {
+ initialTablePos[i] = *src;
+ src += 1;
+ initialimcTableEntry[i] = READ_BE_UINT32(src);
+ src += 4;
+ initialOutputWord[i] = READ_BE_UINT32(src);
+ src += 4;
+ }
+ }
+
+ totalBitOffset = 0;
+ // The channels are encoded separately.
+ for (int chan = 0; chan < channels; chan++) {
+ // Read initial state (this makes it possible for the data stream
+ // to be split & spread across multiple data chunks.
+ curTablePos = initialTablePos[chan];
+ //imcTableEntry = initialimcTableEntry[chan];
+ outputWord = initialOutputWord[chan];
+
+ // We need to interleave the channels in the output; we achieve
+ // that by using a variables dest offset:
+ destPos = chan * 2;
+
+ const int bound = (channels == 1)
+ ? outputSamplesLeft
+ : ((chan == 0)
+ ? (outputSamplesLeft+1) / 2
+ : outputSamplesLeft / 2);
+ for (i = 0; i < bound; ++i) {
+ // Determine the size (in bits) of the next data packet
+ const int32 curTableEntryBitCount = _imcTableEntryBitCount[curTablePos];
+ assert(2 <= curTableEntryBitCount && curTableEntryBitCount <= 7);
+
+ // Read the next data packet
+ const byte *readPos = src + (totalBitOffset >> 3);
+ const uint16 readWord = (uint16)(READ_BE_UINT16(readPos) << (totalBitOffset & 7));
+ const byte packet = (byte)(readWord >> (16 - curTableEntryBitCount));
+
+ // Advance read position to the next data packet
+ totalBitOffset += curTableEntryBitCount;
+
+ // Decode the data packet into a delta value for the output signal.
+ const byte signBitMask = (1 << (curTableEntryBitCount - 1));
+ const byte dataBitMask = (signBitMask - 1);
+ const byte data = (packet & dataBitMask);
+
+ int32 delta = imcTable[curTablePos] * (2 * data + 1) >> (curTableEntryBitCount - 1);
+
+ // The topmost bit in the data packet tells is a sign bit
+ if ((packet & signBitMask) != 0) {
+ delta = -delta;
+ }
+
+ // Accumulate the delta onto the output data
+ outputWord += delta;
+
+ // Clip outputWord to 16 bit signed, and write it into the destination stream
+ if (outputWord > 0x7fff)
+ outputWord = 0x7fff;
+ if (outputWord < -0x8000)
+ outputWord = -0x8000;
+ WRITE_BE_UINT16(dst + destPos, outputWord);
+ destPos += channels << 1;
+
+ // Adjust the curTablePos
+ curTablePos += (int8)imxOtherTable[curTableEntryBitCount - 2][data];
+ if (curTablePos < 0)
+ curTablePos = 0;
+ else if (curTablePos >= ARRAYSIZE(imcTable))
+ curTablePos = ARRAYSIZE(imcTable) - 1;
+ }
+ }
+ }
+ break;
+
+ default:
+ error("BundleCodecs::decompressCodec() Unknown codec %d!", (int)codec);
+ output_size = 0;
+ break;
+ }
+
+ return output_size;
+}
+
+} // End of namespace BundleCodecs
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(DimuseCodecs)
+_GSETPTR(Scumm::BundleCodecs::imcTable, GBVARS_IMCTABLE_INDEX, int16, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(DimuseCodecs)
+_GRELEASEPTR(GBVARS_IMCTABLE_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_music.cpp b/engines/scumm/imuse_digi/dimuse_music.cpp
new file mode 100644
index 0000000000..4c0cedc57c
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_music.cpp
@@ -0,0 +1,444 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "scumm/scumm.h"
+#include "scumm/imuse_digi/dimuse.h"
+
+namespace Scumm {
+
+#define DIG_STATE_OFFSET 11
+#define DIG_SEQ_OFFSET (DIG_STATE_OFFSET + 65)
+#define COMI_STATE_OFFSET 3
+
+void IMuseDigital::setDigMusicState(int stateId) {
+ int l, num = -1;
+
+ for (l = 0; _digStateMusicTable[l].soundId != -1; l++) {
+ if ((_digStateMusicTable[l].soundId == stateId)) {
+ debug(5, "Set music state: %s, %s", _digStateMusicTable[l].name, _digStateMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+
+ if (num == -1) {
+ for (l = 0; _digStateMusicMap[l].roomId != -1; l++) {
+ if ((_digStateMusicMap[l].roomId == stateId)) {
+ break;
+ }
+ }
+ num = l;
+
+ int offset = _attributes[_digStateMusicMap[num].offset];
+ if (offset == 0) {
+ if (_attributes[_digStateMusicMap[num].atribPos] != 0) {
+ num = _digStateMusicMap[num].stateIndex3;
+ } else {
+ num = _digStateMusicMap[num].stateIndex1;
+ }
+ } else {
+ int stateIndex2 = _digStateMusicMap[num].stateIndex2;
+ if (stateIndex2 == 0) {
+ num = _digStateMusicMap[num].stateIndex1 + offset;
+ } else {
+ num = stateIndex2;
+ }
+ }
+ }
+
+ debug(5, "Set music state: %s, %s", _digStateMusicTable[num].name, _digStateMusicTable[num].filename);
+
+ if (_curMusicState == num)
+ return;
+
+ if (_curMusicSeq == 0) {
+ if (num == 0)
+ playDigMusic(NULL, &_digStateMusicTable[0], num, false);
+ else
+ playDigMusic(_digStateMusicTable[num].name, &_digStateMusicTable[num], num, false);
+ }
+
+ _curMusicState = num;
+}
+
+void IMuseDigital::setDigMusicSequence(int seqId) {
+ int l, num = -1;
+
+ if (seqId == 0)
+ seqId = 2000;
+
+ for (l = 0; _digSeqMusicTable[l].soundId != -1; l++) {
+ if ((_digSeqMusicTable[l].soundId == seqId)) {
+ debug(5, "Set music sequence: %s, %s", _digSeqMusicTable[l].name, _digSeqMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+
+ if (num == -1)
+ return;
+
+ if (_curMusicSeq == num)
+ return;
+
+ if (num != 0) {
+ if (_curMusicSeq == 0) {
+ playDigMusic(_digSeqMusicTable[num].name, &_digSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ _attributes[DIG_SEQ_OFFSET + num] = 1;
+ } else {
+ if ((_digSeqMusicTable[_curMusicSeq].opcode == 4) || (_digSeqMusicTable[_curMusicSeq].opcode == 6)) {
+ _nextSeqToPlay = num;
+ return;
+ } else {
+ playDigMusic(_digSeqMusicTable[num].name, &_digSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ _attributes[DIG_SEQ_OFFSET + num] = 1;
+ }
+ }
+ } else {
+ if (_nextSeqToPlay != 0) {
+ playDigMusic(_digSeqMusicTable[_nextSeqToPlay].name, &_digSeqMusicTable[_nextSeqToPlay], 0, true);
+ _attributes[DIG_SEQ_OFFSET + _nextSeqToPlay] = 1;
+ num = _nextSeqToPlay;
+ _nextSeqToPlay = 0;
+ } else {
+ if (_curMusicState != 0) {
+ playDigMusic(_digStateMusicTable[_curMusicState].name, &_digStateMusicTable[_curMusicState], _curMusicState, true);
+ } else
+ playDigMusic(NULL, &_digStateMusicTable[0], _curMusicState, true);
+ num = 0;
+ }
+ }
+
+ _curMusicSeq = num;
+}
+
+void IMuseDigital::playDigMusic(const char *songName, const imuseDigTable *table, int atribPos, bool sequence) {
+ int hookId = 0;
+
+ if (songName != NULL) {
+ if ((_attributes[DIG_SEQ_OFFSET + 38] != 0) && (_attributes[DIG_SEQ_OFFSET + 41] == _attributes[DIG_SEQ_OFFSET + 38])) {
+ if ((atribPos == 43) || (atribPos == 44))
+ hookId = 3;
+ }
+
+ if ((_attributes[DIG_SEQ_OFFSET + 46] != 0) && (_attributes[DIG_SEQ_OFFSET + 48] == 0)) {
+ if ((atribPos == 38) || (atribPos == 39))
+ hookId = 3;
+ }
+
+ if ((_attributes[DIG_SEQ_OFFSET + 53] != 0)) {
+ if ((atribPos == 50) || (atribPos == 51))
+ hookId = 3;
+ }
+
+ if ((atribPos != 0) && (hookId == 0)) {
+ if (table->atribPos != 0)
+ atribPos = table->atribPos;
+ hookId = _attributes[DIG_STATE_OFFSET + atribPos];
+ if (table->hookId != 0) {
+ if ((hookId != 0) && (table->hookId > 1)) {
+ _attributes[DIG_STATE_OFFSET + atribPos] = 2;
+ } else {
+ _attributes[DIG_STATE_OFFSET + atribPos] = hookId + 1;
+ if (table->hookId < hookId + 1)
+ _attributes[DIG_STATE_OFFSET + atribPos] = 1;
+ }
+ }
+ }
+ }
+
+ fadeOutMusic(120);
+
+ switch(table->opcode) {
+ case 0:
+ case 5:
+ case 6:
+ break;
+ case 3:
+ case 4:
+ if (table->filename[0] == 0) {
+ return;
+ }
+ if ((!sequence) && (table->atribPos != 0) &&
+ (table->atribPos == _digStateMusicTable[_curMusicState].atribPos)) {
+ startMusic(table->filename, table->soundId, 0, 127);
+ return;
+ }
+ startMusic(table->filename, table->soundId, hookId, 127);
+ break;
+ }
+}
+
+void IMuseDigital::setComiMusicState(int stateId) {
+ int l, num = -1;
+
+ // This happens at the beginning of Part II, but should apparently not
+ // do anything since the correct music is already playing. A left-over
+ // of some kind?
+
+ if (stateId == 4)
+ return;
+
+ if (stateId == 0)
+ stateId = 1000;
+
+ for (l = 0; _comiStateMusicTable[l].soundId != -1; l++) {
+ if ((_comiStateMusicTable[l].soundId == stateId)) {
+ debug(5, "Set music state: %s, %s", _comiStateMusicTable[l].name, _comiStateMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+ assert(num != -1);
+
+ if (_curMusicState == num)
+ return;
+
+ if (_curMusicSeq == 0) {
+ if (num == 0)
+ playComiMusic(NULL, &_comiStateMusicTable[0], num, false);
+ else
+ playComiMusic(_comiStateMusicTable[num].name, &_comiStateMusicTable[num], num, false);
+ }
+
+ _curMusicState = num;
+}
+
+void IMuseDigital::setComiMusicSequence(int seqId) {
+ int l, num = -1;
+
+ if (seqId == 0)
+ seqId = 2000;
+
+ for (l = 0; _comiSeqMusicTable[l].soundId != -1; l++) {
+ if ((_comiSeqMusicTable[l].soundId == seqId)) {
+ debug(5, "Set music sequence: %s, %s", _comiSeqMusicTable[l].name, _comiSeqMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+ assert(num != -1);
+
+ if (_curMusicSeq == num)
+ return;
+
+ if (num != 0) {
+ if (_curMusicSeq == 0) {
+ playComiMusic(_comiSeqMusicTable[num].name, &_comiSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ } else {
+ if ((_comiSeqMusicTable[_curMusicSeq].opcode == 4) || (_comiSeqMusicTable[_curMusicSeq].opcode == 6)) {
+ _nextSeqToPlay = num;
+ return;
+ } else {
+ playComiMusic(_comiSeqMusicTable[num].name, &_comiSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ }
+ }
+ } else {
+ if (_nextSeqToPlay != 0) {
+ playComiMusic(_comiSeqMusicTable[_nextSeqToPlay].name, &_comiSeqMusicTable[_nextSeqToPlay], 0, true);
+ num = _nextSeqToPlay;
+ _nextSeqToPlay = 0;
+ } else {
+ if (_curMusicState != 0) {
+ playComiMusic(_comiStateMusicTable[_curMusicState].name, &_comiStateMusicTable[_curMusicState], _curMusicState, true);
+ } else
+ playComiMusic(NULL, &_comiStateMusicTable[0], _curMusicState, true);
+ num = 0;
+ }
+ }
+
+ _curMusicSeq = num;
+}
+
+void IMuseDigital::playComiMusic(const char *songName, const imuseComiTable *table, int atribPos, bool sequence) {
+ int hookId = 0;
+
+ if ((songName != NULL) && (atribPos != 0)) {
+ if (table->atribPos != 0)
+ atribPos = table->atribPos;
+ hookId = _attributes[COMI_STATE_OFFSET + atribPos];
+ if (table->hookId != 0) {
+ if ((hookId != 0) && (table->hookId > 1)) {
+ _attributes[COMI_STATE_OFFSET + atribPos] = 2;
+ } else {
+ _attributes[COMI_STATE_OFFSET + atribPos] = hookId + 1;
+ if (table->hookId < hookId + 1)
+ _attributes[COMI_STATE_OFFSET + atribPos] = 1;
+ }
+ }
+ }
+
+ switch(table->opcode) {
+ case 0:
+ case 8:
+ case 9:
+ fadeOutMusic(120);
+ break;
+ case 1:
+ if (table->filename[0] == 0) {
+ fadeOutMusic(120);
+ return;
+ }
+ fadeOutMusic(120);
+ startMusic(table->filename, table->soundId, 0, 1);
+ setFade(table->soundId, 127, 120);
+ break;
+ case 2:
+ if (table->filename[0] == 0) {
+ fadeOutMusic(60);
+ return;
+ }
+ fadeOutMusic(table->fadeOut60TicksDelay);
+ startMusic(table->filename, table->soundId, table->hookId, 127);
+ break;
+ case 3:
+ case 4:
+ case 12:
+ if (table->filename[0] == 0) {
+ fadeOutMusic(60);
+ return;
+ }
+ fadeOutMusic(table->fadeOut60TicksDelay);
+ if ((!sequence) && (table->atribPos != 0) &&
+ (table->atribPos == _comiStateMusicTable[_curMusicState].atribPos)) {
+ startMusic(table->filename, table->soundId, 0, 127);
+ return;
+ }
+ if (table->opcode == 12) {
+ startMusic(table->filename, table->soundId, table->hookId, 127);
+ } else {
+ startMusic(table->filename, table->soundId, hookId, 127);
+ }
+ break;
+ }
+}
+
+void IMuseDigital::setFtMusicState(int stateId) {
+ if (stateId > 48)
+ return;
+
+ debug(5, "State music: %s, %s", _ftStateMusicTable[stateId].name, _ftStateMusicTable[stateId].audioName);
+
+ if (_curMusicState == stateId)
+ return;
+
+ if (_curMusicSeq == 0) {
+ if (stateId == 0)
+ playFtMusic(NULL, 0, 0);
+ else
+ playFtMusic(_ftStateMusicTable[stateId].audioName, _ftStateMusicTable[stateId].opcode, _ftStateMusicTable[stateId].volume);
+ }
+
+ _curMusicState = stateId;
+}
+
+void IMuseDigital::setFtMusicSequence(int seqId) {
+ if (seqId > 52)
+ return;
+
+ debug(5, "Sequence music: %s", _ftSeqNames[seqId].name);
+
+ if (_curMusicSeq == seqId)
+ return;
+
+ if (seqId == 0) {
+ if (_curMusicState == 0)
+ playFtMusic(NULL, 0, 0);
+ else {
+ playFtMusic(_ftStateMusicTable[_curMusicState].audioName, _ftStateMusicTable[_curMusicState].opcode, _ftStateMusicTable[_curMusicState].volume);
+ }
+ } else {
+ int seq = (seqId - 1) * 4;
+ playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].opcode, _ftSeqMusicTable[seq].volume);
+ }
+
+ _curMusicSeq = seqId;
+ _curMusicCue = 0;
+}
+
+void IMuseDigital::setFtMusicCuePoint(int cueId) {
+ if (cueId > 3)
+ return;
+
+ debug(5, "Cue point sequence: %d", cueId);
+
+ if (_curMusicSeq == 0)
+ return;
+
+ if (_curMusicCue == cueId)
+ return;
+
+ if (cueId == 0)
+ playFtMusic(NULL, 0, 0);
+ else {
+ int seq = ((_curMusicSeq - 1) * 4) + cueId;
+ playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].opcode, _ftSeqMusicTable[seq].volume);
+ }
+
+ _curMusicCue = cueId;
+}
+
+void IMuseDigital::setAudioNames(int32 num, char *names) {
+ free(_audioNames);
+ _numAudioNames = num;
+ _audioNames = names;
+}
+
+int IMuseDigital::getSoundIdByName(const char *soundName) {
+ if (soundName && soundName[0] != 0) {
+ for (int r = 0; r < _numAudioNames; r++) {
+ if (strcmp(soundName, &_audioNames[r * 9]) == 0) {
+ return r;
+ }
+ }
+ }
+
+ return -1;
+}
+
+void IMuseDigital::playFtMusic(const char *songName, int opcode, int volume) {
+ fadeOutMusic(200);
+
+ switch(opcode) {
+ case 0:
+ case 4:
+ break;
+ case 1:
+ case 2:
+ case 3:
+ {
+ int soundId = getSoundIdByName(songName);
+ if (soundId != -1) {
+ startMusic(soundId, volume);
+ }
+ }
+ break;
+ }
+}
+
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_script.cpp b/engines/scumm/imuse_digi/dimuse_script.cpp
new file mode 100644
index 0000000000..83deb91e54
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_script.cpp
@@ -0,0 +1,409 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/timer.h"
+
+#include "scumm/actor.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+void IMuseDigital::parseScriptCmds(int cmd, int b, int c, int d, int e, int f, int g, int h) {
+ int soundId = b;
+ int sub_cmd = c;
+
+ if (!cmd)
+ return;
+
+ switch (cmd) {
+ case 10: // ImuseStopAllSounds
+ stopAllSounds();
+ break;
+ case 12: // ImuseSetParam
+ switch (sub_cmd) {
+ case 0x400: // select group volume
+ selectVolumeGroup(soundId, d);
+ break;
+ case 0x500: // set priority
+ setPriority(soundId, d);
+ break;
+ case 0x600: // set volume
+ setVolume(soundId, d);
+ break;
+ case 0x700: // set pan
+ setPan(soundId, d);
+ break;
+ default:
+ warning("IMuseDigital::doCommand SetParam DEFAULT command %d", sub_cmd);
+ break;
+ }
+ break;
+ case 14: // ImuseFadeParam
+ switch (sub_cmd) {
+ case 0x600: // set volume fading
+ if ((d != 0) && (e == 0))
+ setVolume(soundId, d);
+ else if ((d == 0) && (e == 0))
+ stopSound(soundId);
+ else
+ setFade(soundId, d, e);
+ break;
+ default:
+ warning("IMuseDigital::doCommand FadeParam DEFAULT sub command %d", sub_cmd);
+ break;
+ }
+ break;
+ case 25: // ImuseStartStream
+ debug(5, "ImuseStartStream (%d, %d, %d)", soundId, c, d);
+ break;
+ case 26: // ImuseSwitchStream
+ debug(5, "ImuseSwitchStream (%d, %d, %d, %d, %d)", soundId, c, d, e, f);
+ break;
+ case 0x1000: // ImuseSetState
+ debug(5, "ImuseSetState (%d)", b);
+ if ((_vm->_gameId == GID_DIG) && (_vm->_features & GF_DEMO)) {
+ if (b == 1) {
+ fadeOutMusic(200);
+ startMusic(1, 127);
+ } else {
+ if (getSoundStatus(2) == 0) {
+ fadeOutMusic(200);
+ startMusic(2, 127);
+ }
+ }
+ } else if ((_vm->_gameId == GID_CMI) && (_vm->_features & GF_DEMO)) {
+ fadeOutMusic(120);
+ if (b == 2) {
+ startMusic("in1.imx", 1100, 0, 127);
+ } else if (b == 4) {
+ startMusic("in2.imx", 1120, 0, 127);
+ } else if (b == 8) {
+ startMusic("out1.imx", 1140, 0, 127);
+ } else if (b == 9) {
+ startMusic("out2.imx", 1150, 0, 127);
+ } else if (b == 16) {
+ startMusic("gun.imx", 1210, 0, 127);
+ } else {
+ warning("imuse digital: set state unknown for cmi demo: %d, room: %d", b, _vm->_currentRoom);
+ }
+ } else if (_vm->_gameId == GID_DIG) {
+ setDigMusicState(b);
+ } else if (_vm->_gameId == GID_CMI) {
+ setComiMusicState(b);
+ } else if (_vm->_gameId == GID_FT) {
+ setFtMusicState(b);
+ }
+ break;
+ case 0x1001: // ImuseSetSequence
+ debug(5, "ImuseSetSequence (%d)", b);
+ if (_vm->_gameId == GID_DIG) {
+ setDigMusicSequence(b);
+ } else if (_vm->_gameId == GID_CMI) {
+ setComiMusicSequence(b);
+ } else if (_vm->_gameId == GID_FT) {
+ setFtMusicSequence(b);
+ }
+ break;
+ case 0x1002: // ImuseSetCuePoint
+ debug(5, "ImuseSetCuePoint (%d)", b);
+ if (_vm->_gameId == GID_FT) {
+ setFtMusicCuePoint(b);
+ }
+ break;
+ case 0x1003: // ImuseSetAttribute
+ debug(5, "ImuseSetAttribute (%d, %d)", b, c);
+ assert((_vm->_gameId == GID_DIG) || (_vm->_gameId == GID_FT));
+ if (_vm->_gameId == GID_DIG) {
+ _attributes[b] = c;
+ }
+ break;
+ case 0x2000: // ImuseSetGroupSfxVolume
+ debug(5, "ImuseSetGroupSFXVolume (%d)", b);
+// setGroupSfxVolume(b);
+ break;
+ case 0x2001: // ImuseSetGroupVoiceVolume
+ debug(5, "ImuseSetGroupVoiceVolume (%d)", b);
+// setGroupVoiceVolume(b);
+ break;
+ case 0x2002: // ImuseSetGroupMusicVolume
+ debug(5, "ImuseSetGroupMusicVolume (%d)", b);
+// setGroupMusicVolume(b);
+ break;
+ default:
+ error("IMuseDigital::doCommand DEFAULT command %d", cmd);
+ }
+}
+
+void IMuseDigital::flushTracks() {
+ Common::StackLock lock(_mutex, "IMuseDigital::flushTracks()");
+ debug(5, "flushTracks()");
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && (track->readyToRemove ||
+ (_vm->_insaneRunning && track->toBeRemoved))) { // INSANE hack for sync timer mode
+ if (track->stream) {
+ if (!track->stream->endOfStream()) {
+ track->stream->finish();
+ }
+ if (track->stream->endOfStream()
+ || _vm->_mixer->isPaused() // hack for paused Mixer
+ || _vm->_insaneRunning) { // INSANE hack for sync timer mode
+ _vm->_mixer->stopHandle(track->handle);
+ delete track->stream;
+ track->stream = NULL;
+ _sound->closeSound(track->soundHandle);
+ track->soundHandle = NULL;
+ track->used = false;
+ }
+ } else if (track->stream2) {
+ _vm->_mixer->stopHandle(track->handle);
+ delete track->stream2;
+ track->stream2 = NULL;
+ track->used = false;
+ }
+ }
+ }
+}
+
+void IMuseDigital::refreshScripts() {
+ Common::StackLock lock(_mutex, "IMuseDigital::refreshScripts()");
+ debug(5, "refreshScripts()");
+ bool found = false;
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ found = true;
+ }
+ }
+
+ if (!found && (_curMusicSeq != 0)) {
+ debug(5, "refreshScripts() Start Sequence");
+ parseScriptCmds(0x1001, 0, 0, 0, 0, 0, 0, 0);
+ }
+}
+
+void IMuseDigital::startVoice(int soundId, AudioStream *input) {
+ debug(5, "startVoiceStream(%d)", soundId);
+ startSound(soundId, "", 0, IMUSE_VOLGRP_VOICE, input, 0, 127, 127);
+}
+
+void IMuseDigital::startVoice(int soundId, const char *soundName) {
+ debug(5, "startVoiceBundle(%s)", soundName);
+ startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOLGRP_VOICE, NULL, 0, 127, 127);
+}
+
+void IMuseDigital::startMusic(int soundId, int volume) {
+ debug(5, "startMusicResource(%d)", soundId);
+ startSound(soundId, "", IMUSE_RESOURCE, IMUSE_VOLGRP_MUSIC, NULL, 0, volume, 126);
+}
+
+void IMuseDigital::startMusic(const char *soundName, int soundId, int hookId, int volume) {
+ debug(5, "startMusicBundle(%s)", soundName);
+ startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOLGRP_MUSIC, NULL, hookId, volume, 126);
+}
+
+void IMuseDigital::startSfx(int soundId, int priority) {
+ debug(5, "startSfx(%d)", soundId);
+ startSound(soundId, "", IMUSE_RESOURCE, IMUSE_VOLGRP_SFX, NULL, 0, 127, priority);
+}
+
+void IMuseDigital::getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height) {
+ int32 sync_size;
+ byte *sync_ptr;
+
+ msPos /= 16;
+ if (msPos < 65536) {
+ Common::StackLock lock(_mutex, "IMuseDigital::getLipSync()");
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ _sound->getSyncSizeAndPtrById(track->soundHandle, syncId, sync_size, &sync_ptr);
+ if ((sync_size != 0) && (sync_ptr != NULL)) {
+ sync_size /= 4;
+ while (sync_size--) {
+ if (READ_BE_UINT16(sync_ptr) >= msPos)
+ break;
+ sync_ptr += 4;
+ }
+ if (sync_size < 0)
+ sync_ptr -= 4;
+ else
+ if (READ_BE_UINT16(sync_ptr) > msPos)
+ sync_ptr -= 4;
+
+ width = sync_ptr[2];
+ height = sync_ptr[3];
+ return;
+ }
+ }
+ }
+ }
+}
+
+int32 IMuseDigital::getPosInMs(int soundId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::getPosInMs()");
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ int32 pos = (5 * (track->dataOffset + track->regionOffset)) / (track->iteration / 200);
+ return pos;
+ }
+ }
+
+ return 0;
+}
+
+int IMuseDigital::getSoundStatus(int sound) const {
+ Common::StackLock lock(_mutex, "IMuseDigital::getSoundStatus()");
+ debug(5, "IMuseDigital::getSoundStatus(%d)", sound);
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->soundId == sound) {
+ if ((track->stream2 && _vm->_mixer->isSoundHandleActive(track->handle)) ||
+ (track->stream && track->used && !track->readyToRemove)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void IMuseDigital::stopSound(int soundId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::stopSound()");
+ debug(5, "IMuseDigital::stopSound(%d)", soundId);
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->toBeRemoved = true;
+ }
+ }
+}
+
+int32 IMuseDigital::getCurMusicPosInMs() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicPosInMs()");
+ int soundId = -1;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundId = track->soundId;
+ }
+ }
+
+ int32 msPos = getPosInMs(soundId);
+ debug(5, "IMuseDigital::getCurMusicPosInMs(%d) = %d", soundId, msPos);
+ return msPos;
+}
+
+int32 IMuseDigital::getCurVoiceLipSyncWidth() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurVoiceLipSyncWidth()");
+ int32 msPos = getPosInMs(kTalkSoundID) + 50;
+ int32 width = 0, height = 0;
+
+ debug(5, "IMuseDigital::getCurVoiceLipSyncWidth(%d)", kTalkSoundID);
+ getLipSync(kTalkSoundID, 0, msPos, width, height);
+ return width;
+}
+
+int32 IMuseDigital::getCurVoiceLipSyncHeight() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurVoiceLipSyncHeight()");
+ int32 msPos = getPosInMs(kTalkSoundID) + 50;
+ int32 width = 0, height = 0;
+
+ debug(5, "IMuseDigital::getCurVoiceLipSyncHeight(%d)", kTalkSoundID);
+ getLipSync(kTalkSoundID, 0, msPos, width, height);
+ return height;
+}
+
+int32 IMuseDigital::getCurMusicLipSyncWidth(int syncId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncWidth()");
+ int soundId = -1;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundId = track->soundId;
+ }
+ }
+
+ int32 msPos = getPosInMs(soundId) + 50;
+ int32 width = 0, height = 0;
+
+ debug(5, "IMuseDigital::getCurVoiceLipSyncWidth(%d, %d)", soundId, msPos);
+ getLipSync(soundId, syncId, msPos, width, height);
+ return width;
+}
+
+int32 IMuseDigital::getCurMusicLipSyncHeight(int syncId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncHeight()");
+ int soundId = -1;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundId = track->soundId;
+ }
+ }
+
+ int32 msPos = getPosInMs(soundId) + 50;
+ int32 width = 0, height = 0;
+
+ debug(5, "IMuseDigital::getCurVoiceLipSyncHeight(%d, %d)", soundId, msPos);
+ getLipSync(soundId, syncId, msPos, width, height);
+ return height;
+}
+
+void IMuseDigital::stopAllSounds() {
+ debug(5, "IMuseDigital::stopAllSounds");
+
+ for (;;) {
+ bool foundNotRemoved = false;
+ for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used) {
+ track->toBeRemoved = true;
+ foundNotRemoved = true;
+ }
+ }
+ if (!foundNotRemoved)
+ break;
+ flushTracks();
+ _vm->_system->delayMillis(50);
+#if defined(_WIN32_WCE) || defined (PALMOS_MODE) || defined(__SYMBIAN32__)
+ _vm->parseEvents(); // timers are events, we need to consume them
+#endif
+ }
+}
+
+void IMuseDigital::pause(bool p) {
+ _pause = p;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
new file mode 100644
index 0000000000..7d3d06f16e
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
@@ -0,0 +1,625 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/util.h"
+
+#include "sound/voc.h"
+#include "sound/vorbis.h"
+#include "sound/mp3.h"
+
+#include "scumm/scumm.h"
+#include "scumm/util.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_sndmgr.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+namespace Scumm {
+
+ImuseDigiSndMgr::ImuseDigiSndMgr(ScummEngine *scumm) {
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ memset(&_sounds[l], 0, sizeof(soundStruct));
+ }
+ _vm = scumm;
+ _disk = 0;
+ _cacheBundleDir = new BundleDirCache();
+ BundleCodecs::initializeImcTables();
+}
+
+ImuseDigiSndMgr::~ImuseDigiSndMgr() {
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ closeSound(&_sounds[l]);
+ }
+
+ delete _cacheBundleDir;
+}
+
+void ImuseDigiSndMgr::countElements(byte *ptr, int &numRegions, int &numJumps, int &numSyncs) {
+ uint32 tag;
+ int32 size = 0;
+
+ do {
+ tag = READ_BE_UINT32(ptr); ptr += 4;
+ switch(tag) {
+ case MKID_BE('TEXT'):
+ case MKID_BE('STOP'):
+ case MKID_BE('FRMT'):
+ case MKID_BE('DATA'):
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ case MKID_BE('REGN'):
+ numRegions++;
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ case MKID_BE('JUMP'):
+ numJumps++;
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ case MKID_BE('SYNC'):
+ numSyncs++;
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ default:
+ error("ImuseDigiSndMgr::countElements() Unknown sfx header '%s'", tag2str(tag));
+ }
+ } while (tag != MKID_BE('DATA'));
+}
+
+void ImuseDigiSndMgr::prepareSoundFromRMAP(Common::File *file, soundStruct *sound, int32 offset, int32 size) {
+ int l;
+
+ file->seek(offset, SEEK_SET);
+ uint32 tag = file->readUint32BE();
+ assert(tag == 'RMAP');
+ int32 version = file->readUint32BE();
+ if (version != 2) {
+ error("ImuseDigiSndMgr::prepareSoundFromRMAP: Wrong version number, expected 2, but it's: %d.", version);
+ }
+ sound->bits = file->readUint32BE();
+ sound->freq = file->readUint32BE();
+ sound->channels = file->readUint32BE();
+ sound->numRegions = file->readUint32BE();
+ sound->numJumps = file->readUint32BE();
+ sound->numSyncs = file->readUint32BE();
+ sound->region = (_region *)malloc(sizeof(_region) * sound->numRegions);
+ sound->jump = (_jump *)malloc(sizeof(_jump) * sound->numJumps);
+ sound->sync = (_sync *)malloc(sizeof(_sync) * sound->numSyncs);
+ for (l = 0; l < sound->numRegions; l++) {
+ sound->region[l].offset = file->readUint32BE();
+ sound->region[l].length = file->readUint32BE();
+ }
+ for (l = 0; l < sound->numJumps; l++) {
+ sound->jump[l].offset = file->readUint32BE();
+ sound->jump[l].dest = file->readUint32BE();
+ sound->jump[l].hookId = file->readUint32BE();
+ sound->jump[l].fadeDelay = file->readUint32BE();
+ }
+ for (l = 0; l < sound->numSyncs; l++) {
+ sound->sync[l].size = file->readUint32BE();
+ sound->sync[l].ptr = (byte *)malloc(sound->sync[l].size);
+ file->read(sound->sync[l].ptr, sound->sync[l].size);
+ }
+}
+
+void ImuseDigiSndMgr::prepareSound(byte *ptr, soundStruct *sound) {
+ if (READ_UINT32(ptr) == MKID('Crea')) {
+ bool quit = false;
+ int len;
+
+ int32 offset = READ_LE_UINT16(ptr + 20);
+ int16 code = READ_LE_UINT16(ptr + 24);
+
+ sound->region = (_region *)malloc(sizeof(_region) * 70);
+ sound->jump = (_jump *)malloc(sizeof(_jump));
+ sound->resPtr = ptr;
+ sound->bits = 8;
+ sound->channels = 1;
+
+ while (!quit) {
+ len = READ_LE_UINT32(ptr + offset);
+ code = len & 0xFF;
+ if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) {
+ // try again with 2 bytes forward (workaround for some FT sounds (ex.362, 363)
+ offset += 2;
+ len = READ_LE_UINT32(ptr + offset);
+ code = len & 0xFF;
+ if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) {
+ error("Invalid code in VOC file : %d", code);
+ }
+ }
+ offset += 4;
+ len >>= 8;
+ switch(code) {
+ case 0:
+ quit = true;
+ break;
+ case 1:
+ {
+ int time_constant = ptr[offset];
+ offset += 2;
+ len -= 2;
+ sound->freq = getSampleRateFromVOCRate(time_constant);
+ sound->region[sound->numRegions].offset = offset;
+ sound->region[sound->numRegions].length = len;
+ sound->numRegions++;
+ }
+ break;
+ case 6: // begin of loop
+ sound->jump[0].dest = offset + 8;
+ sound->jump[0].hookId = 0;
+ sound->jump[0].fadeDelay = 0;
+ break;
+ case 7: // end of loop
+ sound->jump[0].offset = offset - 4;
+ sound->numJumps++;
+ sound->region[sound->numRegions].offset = offset - 4;
+ sound->region[sound->numRegions].length = 0;
+ sound->numRegions++;
+ break;
+ default:
+ error("Invalid code in VOC file : %d", code);
+ quit = true;
+ break;
+ }
+ offset += len;
+ }
+ } else if (READ_UINT32(ptr) == MKID('iMUS')) {
+ uint32 tag;
+ int32 size = 0;
+ byte *s_ptr = ptr;
+ ptr += 16;
+
+ int curIndexRegion = 0;
+ int curIndexJump = 0;
+ int curIndexSync = 0;
+
+ sound->numRegions = 0;
+ sound->numJumps = 0;
+ sound->numSyncs = 0;
+ countElements(ptr, sound->numRegions, sound->numJumps, sound->numSyncs);
+ sound->region = (_region *)malloc(sizeof(_region) * sound->numRegions);
+ sound->jump = (_jump *)malloc(sizeof(_jump) * sound->numJumps);
+ sound->sync = (_sync *)malloc(sizeof(_sync) * sound->numSyncs);
+
+ do {
+ tag = READ_BE_UINT32(ptr); ptr += 4;
+ switch(tag) {
+ case MKID_BE('FRMT'):
+ ptr += 12;
+ sound->bits = READ_BE_UINT32(ptr); ptr += 4;
+ sound->freq = READ_BE_UINT32(ptr); ptr += 4;
+ sound->channels = READ_BE_UINT32(ptr); ptr += 4;
+ break;
+ case MKID_BE('TEXT'):
+ case MKID_BE('STOP'):
+ size = READ_BE_UINT32(ptr); ptr += size + 4;
+ break;
+ case MKID_BE('REGN'):
+ ptr += 4;
+ sound->region[curIndexRegion].offset = READ_BE_UINT32(ptr); ptr += 4;
+ sound->region[curIndexRegion].length = READ_BE_UINT32(ptr); ptr += 4;
+ curIndexRegion++;
+ break;
+ case MKID_BE('JUMP'):
+ ptr += 4;
+ sound->jump[curIndexJump].offset = READ_BE_UINT32(ptr); ptr += 4;
+ sound->jump[curIndexJump].dest = READ_BE_UINT32(ptr); ptr += 4;
+ sound->jump[curIndexJump].hookId = READ_BE_UINT32(ptr); ptr += 4;
+ sound->jump[curIndexJump].fadeDelay = READ_BE_UINT32(ptr); ptr += 4;
+ curIndexJump++;
+ break;
+ case MKID_BE('SYNC'):
+ size = READ_BE_UINT32(ptr); ptr += 4;
+ sound->sync[curIndexSync].size = size;
+ sound->sync[curIndexSync].ptr = (byte *)malloc(size);
+ memcpy(sound->sync[curIndexSync].ptr, ptr, size);
+ curIndexSync++;
+ ptr += size;
+ break;
+ case MKID_BE('DATA'):
+ ptr += 4;
+ break;
+ default:
+ error("ImuseDigiSndMgr::prepareSound(%d/%s) Unknown sfx header '%s'", sound->soundId, sound->name, tag2str(tag));
+ }
+ } while (tag != MKID_BE('DATA'));
+ sound->offsetData = ptr - s_ptr;
+ } else {
+ error("ImuseDigiSndMgr::prepareSound(): Unknown sound format");
+ }
+}
+
+ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::allocSlot() {
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ if (!_sounds[l].inUse) {
+ _sounds[l].inUse = true;
+ return &_sounds[l];
+ }
+ }
+
+ return NULL;
+}
+
+bool ImuseDigiSndMgr::openMusicBundle(soundStruct *sound, int disk) {
+ bool result = false;
+
+ sound->bundle = new BundleMgr(_cacheBundleDir);
+ if (_vm->_gameId == GID_CMI) {
+ if (_vm->_features & GF_DEMO) {
+ result = sound->bundle->open("music.bun", sound->compressed);
+ } else {
+ char musicfile[20];
+ if (disk == -1)
+ disk = _vm->VAR(_vm->VAR_CURRENTDISK);
+ sprintf(musicfile, "musdisk%d.bun", disk);
+// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
+// _vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_imuseDigital->stopAllSounds();
+// sound->bundle->closeFile();
+// }
+
+ result = sound->bundle->open(musicfile, sound->compressed, true);
+
+ // FIXME: Shouldn't we only set _disk if result == true?
+ _disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
+ }
+ } else if (_vm->_gameId == GID_DIG)
+ result = sound->bundle->open("digmusic.bun", sound->compressed, true);
+ else
+ error("ImuseDigiSndMgr::openMusicBundle() Don't know which bundle file to load");
+
+ _vm->VAR(_vm->VAR_MUSIC_BUNDLE_LOADED) = result ? 1 : 0;
+
+ return result;
+}
+
+bool ImuseDigiSndMgr::openVoiceBundle(soundStruct *sound, int disk) {
+ bool result = false;
+
+ sound->bundle = new BundleMgr(_cacheBundleDir);
+ if (_vm->_gameId == GID_CMI) {
+ if (_vm->_features & GF_DEMO) {
+ result = sound->bundle->open("voice.bun", sound->compressed);
+ } else {
+ char voxfile[20];
+ if (disk == -1)
+ disk = _vm->VAR(_vm->VAR_CURRENTDISK);
+ sprintf(voxfile, "voxdisk%d.bun", disk);
+// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
+// _vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_imuseDigital->stopAllSounds();
+// sound->bundle->closeFile();
+// }
+
+ result = sound->bundle->open(voxfile, sound->compressed);
+
+ // FIXME: Shouldn't we only set _disk if result == true?
+ _disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
+ }
+ } else if (_vm->_gameId == GID_DIG)
+ result = sound->bundle->open("digvoice.bun", sound->compressed);
+ else
+ error("ImuseDigiSndMgr::openVoiceBundle() Don't know which bundle file to load");
+
+ _vm->VAR(_vm->VAR_VOICE_BUNDLE_LOADED) = result ? 1 : 0;
+
+ return result;
+}
+
+ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk) {
+ assert(soundId >= 0);
+ assert(soundType);
+
+ soundStruct *sound = allocSlot();
+ if (!sound) {
+ error("ImuseDigiSndMgr::openSound() can't alloc free sound slot");
+ }
+
+ const bool header_outside = ((_vm->_gameId == GID_CMI) && !(_vm->_features & GF_DEMO));
+ bool result = false;
+ byte *ptr = NULL;
+
+ switch (soundType) {
+ case IMUSE_RESOURCE:
+ assert(soundName[0] == 0); // Paranoia check
+
+ _vm->ensureResourceLoaded(rtSound, soundId);
+ _vm->res.lock(rtSound, soundId);
+ ptr = _vm->getResourceAddress(rtSound, soundId);
+ if (ptr == NULL) {
+ closeSound(sound);
+ return NULL;
+ }
+ sound->resPtr = ptr;
+ break;
+ case IMUSE_BUNDLE:
+ if (volGroupId == IMUSE_VOLGRP_VOICE)
+ result = openVoiceBundle(sound, disk);
+ else if (volGroupId == IMUSE_VOLGRP_MUSIC)
+ result = openMusicBundle(sound, disk);
+ else
+ error("ImuseDigiSndMgr::openSound() Don't know how load sound: %d", soundId);
+ if (!result) {
+ closeSound(sound);
+ return NULL;
+ }
+ if (sound->compressed) {
+ char fileName[24];
+ int32 offset = 0, size = 0;
+ sprintf(fileName, "%s.map", soundName);
+ Common::File *rmapFile = sound->bundle->getFile(fileName, offset, size);
+ if (!rmapFile) {
+ closeSound(sound);
+ return NULL;
+ }
+ prepareSoundFromRMAP(rmapFile, sound, offset, size);
+ strcpy(sound->name, soundName);
+ sound->soundId = soundId;
+ sound->type = soundType;
+ sound->volGroupId = volGroupId;
+ sound->disk = _disk;
+ return sound;
+ } else if (soundName[0] == 0) {
+ if (sound->bundle->decompressSampleByIndex(soundId, 0, 0x2000, &ptr, 0, header_outside) == 0 || ptr == NULL) {
+ closeSound(sound);
+ return NULL;
+ }
+ } else {
+ if (sound->bundle->decompressSampleByName(soundName, 0, 0x2000, &ptr, header_outside) == 0 || ptr == NULL) {
+ closeSound(sound);
+ return NULL;
+ }
+ }
+ sound->resPtr = 0;
+ break;
+ default:
+ error("ImuseDigiSndMgr::openSound() Unknown soundType %d (trying to load sound %d)", soundType, soundId);
+ }
+
+ strcpy(sound->name, soundName);
+ sound->soundId = soundId;
+ sound->type = soundType;
+ sound->volGroupId = volGroupId;
+ sound->disk = _disk;
+ prepareSound(ptr, sound);
+ if ((soundType == IMUSE_BUNDLE) && !sound->compressed) {
+ free(ptr);
+ }
+ return sound;
+}
+
+void ImuseDigiSndMgr::closeSound(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+
+ if (soundHandle->resPtr) {
+ bool found = false;
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ if ((_sounds[l].soundId == soundHandle->soundId) && (&_sounds[l] != soundHandle))
+ found = true;
+ }
+ if (!found)
+ _vm->res.unlock(rtSound, soundHandle->soundId);
+ }
+
+ if (soundHandle->compressedStream)
+ delete soundHandle->compressedStream;
+
+ delete soundHandle->bundle;
+
+ for (int r = 0; r < soundHandle->numSyncs; r++)
+ free(soundHandle->sync[r].ptr);
+ free(soundHandle->region);
+ free(soundHandle->jump);
+ free(soundHandle->sync);
+ memset(soundHandle, 0, sizeof(soundStruct));
+}
+
+ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::cloneSound(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+
+ return openSound(soundHandle->soundId, soundHandle->name, soundHandle->type, soundHandle->volGroupId, soundHandle->disk);
+}
+
+bool ImuseDigiSndMgr::checkForProperHandle(soundStruct *soundHandle) {
+ if (!soundHandle)
+ return false;
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ if (soundHandle == &_sounds[l])
+ return true;
+ }
+ return false;
+}
+
+bool ImuseDigiSndMgr::isCompressed(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->compressed;
+}
+
+int ImuseDigiSndMgr::getFreq(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->freq;
+}
+
+int ImuseDigiSndMgr::getBits(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->bits;
+}
+
+int ImuseDigiSndMgr::getChannels(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->channels;
+}
+
+bool ImuseDigiSndMgr::isEndOfRegion(soundStruct *soundHandle, int region) {
+ assert(checkForProperHandle(soundHandle));
+ assert(region >= 0 && region < soundHandle->numRegions);
+ return soundHandle->endFlag;
+}
+
+int ImuseDigiSndMgr::getNumRegions(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->numRegions;
+}
+
+int ImuseDigiSndMgr::getNumJumps(soundStruct *soundHandle) {
+ assert(checkForProperHandle(soundHandle));
+ return soundHandle->numJumps;
+}
+
+int ImuseDigiSndMgr::getRegionOffset(soundStruct *soundHandle, int region) {
+ debug(5, "getRegionOffset() region:%d", region);
+ assert(checkForProperHandle(soundHandle));
+ assert(region >= 0 && region < soundHandle->numRegions);
+ return soundHandle->region[region].offset;
+}
+
+int ImuseDigiSndMgr::getJumpIdByRegionAndHookId(soundStruct *soundHandle, int region, int hookId) {
+ debug(5, "getJumpIdByRegionAndHookId() region:%d, hookId:%d", region, hookId);
+ assert(checkForProperHandle(soundHandle));
+ assert(region >= 0 && region < soundHandle->numRegions);
+ int32 offset = soundHandle->region[region].offset;
+ for (int l = 0; l < soundHandle->numJumps; l++) {
+ if (offset == soundHandle->jump[l].offset) {
+ if (soundHandle->jump[l].hookId == hookId)
+ return l;
+ }
+ }
+
+ return -1;
+}
+
+void ImuseDigiSndMgr::getSyncSizeAndPtrById(soundStruct *soundHandle, int number, int32 &sync_size, byte **sync_ptr) {
+ assert(checkForProperHandle(soundHandle));
+ assert(number >= 0);
+ if (number < soundHandle->numSyncs) {
+ sync_size = soundHandle->sync[number].size;
+ *sync_ptr = soundHandle->sync[number].ptr;
+ } else {
+ sync_size = 0;
+ *sync_ptr = NULL;
+ }
+}
+
+int ImuseDigiSndMgr::getRegionIdByJumpId(soundStruct *soundHandle, int jumpId) {
+ debug(5, "getRegionIdByJumpId() jumpId:%d", jumpId);
+ assert(checkForProperHandle(soundHandle));
+ assert(jumpId >= 0 && jumpId < soundHandle->numJumps);
+ int32 dest = soundHandle->jump[jumpId].dest;
+ for (int l = 0; l < soundHandle->numRegions; l++) {
+ if (dest == soundHandle->region[l].offset) {
+ return l;
+ }
+ }
+
+ return -1;
+}
+
+int ImuseDigiSndMgr::getJumpHookId(soundStruct *soundHandle, int number) {
+ debug(5, "getJumpHookId() number:%d", number);
+ assert(checkForProperHandle(soundHandle));
+ assert(number >= 0 && number < soundHandle->numJumps);
+ return soundHandle->jump[number].hookId;
+}
+
+int ImuseDigiSndMgr::getJumpFade(soundStruct *soundHandle, int number) {
+ debug(5, "getJumpFade() number:%d", number);
+ assert(checkForProperHandle(soundHandle));
+ assert(number >= 0 && number < soundHandle->numJumps);
+ return soundHandle->jump[number].fadeDelay;
+}
+
+int32 ImuseDigiSndMgr::getDataFromRegion(soundStruct *soundHandle, int region, byte **buf, int32 offset, int32 size) {
+ debug(5, "getDataFromRegion() region:%d, offset:%d, size:%d, numRegions:%d", region, offset, size, soundHandle->numRegions);
+ assert(checkForProperHandle(soundHandle));
+ assert(buf && offset >= 0 && size >= 0);
+ assert(region >= 0 && region < soundHandle->numRegions);
+
+ int32 region_offset = soundHandle->region[region].offset;
+ int32 region_length = soundHandle->region[region].length;
+ int32 offset_data = soundHandle->offsetData;
+ int32 start = region_offset - offset_data;
+
+ if (offset + size + offset_data > region_length) {
+ size = region_length - offset;
+ soundHandle->endFlag = true;
+ } else {
+ soundHandle->endFlag = false;
+ }
+
+ int header_size = soundHandle->offsetData;
+ bool header_outside = ((_vm->_gameId == GID_CMI) && !(_vm->_features & GF_DEMO));
+ if ((soundHandle->bundle) && (!soundHandle->compressed)) {
+ size = soundHandle->bundle->decompressSampleByCurIndex(start + offset, size, buf, header_size, header_outside);
+ } else if (soundHandle->resPtr) {
+ *buf = (byte *)malloc(size);
+ memcpy(*buf, soundHandle->resPtr + start + offset + header_size, size);
+ } else if ((soundHandle->bundle) && (soundHandle->compressed)) {
+ *buf = (byte *)malloc(size);
+ char fileName[24];
+ sprintf(fileName, "%s_reg%03d", soundHandle->name, region);
+ if (scumm_stricmp(fileName, soundHandle->lastFileName) != 0) {
+ int32 offs = 0, len = 0;
+ Common::File *cmpFile;
+ bool oggMode = false;
+ sprintf(fileName, "%s_reg%03d.mp3", soundHandle->name, region);
+ cmpFile = soundHandle->bundle->getFile(fileName, offs, len);
+#ifndef USE_MAD
+ if (len)
+ error("Mad library compiled support needed!");
+#endif
+ if (!len) {
+ sprintf(fileName, "%s_reg%03d.ogg", soundHandle->name, region);
+ cmpFile = soundHandle->bundle->getFile(fileName, offs, len);
+#ifndef USE_VORBIS
+ if (len)
+ error("Vorbis library compiled support needed!");
+#endif
+ assert(len);
+ oggMode = true;
+ }
+ if (!soundHandle->compressedStream) {
+#ifdef USE_VORBIS
+ if (oggMode)
+ soundHandle->compressedStream = makeVorbisStream(cmpFile, len);
+#endif
+#ifdef USE_MAD
+ if (!oggMode)
+ soundHandle->compressedStream = makeMP3Stream(cmpFile, len);
+#endif
+ assert(soundHandle->compressedStream);
+ }
+ strcpy(soundHandle->lastFileName, fileName);
+ }
+ size = soundHandle->compressedStream->readBuffer((int16 *)*buf, size / 2) * 2;
+ if (soundHandle->compressedStream->endOfData()) {
+ delete soundHandle->compressedStream;
+ soundHandle->compressedStream = NULL;
+ soundHandle->lastFileName[0] = 0;
+ }
+ }
+
+ return size;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.h b/engines/scumm/imuse_digi/dimuse_sndmgr.h
new file mode 100644
index 0000000000..5844fa0c1b
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_sndmgr.h
@@ -0,0 +1,139 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef IMUSE_DIGI_SNDMGR_H
+#define IMUSE_DIGI_SNDMGR_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "sound/audiostream.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+namespace Scumm {
+
+class ScummEngine;
+class BundleMgr;
+
+class ImuseDigiSndMgr {
+public:
+
+#define MAX_IMUSE_SOUNDS 16
+
+#define IMUSE_RESOURCE 1
+#define IMUSE_BUNDLE 2
+
+#define IMUSE_VOLGRP_VOICE 1
+#define IMUSE_VOLGRP_SFX 2
+#define IMUSE_VOLGRP_MUSIC 3
+
+private:
+ struct _region {
+ int32 offset; // offset of region
+ int32 length; // lenght of region
+ };
+
+ struct _jump {
+ int32 offset; // jump offset position
+ int32 dest; // jump to dest position
+ byte hookId; // id of hook
+ int16 fadeDelay; // fade delay in ms
+ };
+
+ struct _sync {
+ int32 size; // size of sync
+ byte *ptr; // pointer to sync
+ };
+
+public:
+
+ struct soundStruct {
+ uint16 freq; // frequency
+ byte channels; // stereo or mono
+ byte bits; // 8, 12, 16
+ int numJumps; // number of Jumps
+ int numRegions; // number of Regions
+ int numSyncs; // number of Syncs
+ _region *region;
+ _jump *jump;
+ _sync *sync;
+ bool endFlag;
+ bool inUse;
+ byte *allData;
+ int32 offsetData;
+ byte *resPtr;
+ char name[15];
+ int16 soundId;
+ BundleMgr *bundle;
+ int type;
+ int volGroupId;
+ int disk;
+ AudioStream *compressedStream;
+ bool compressed;
+ char lastFileName[24];
+ };
+
+private:
+
+ soundStruct _sounds[MAX_IMUSE_SOUNDS];
+
+ bool checkForProperHandle(soundStruct *soundHandle);
+ soundStruct *allocSlot();
+ void prepareSound(byte *ptr, soundStruct *sound);
+ void prepareSoundFromRMAP(Common::File *file, soundStruct *sound, int32 offset, int32 size);
+
+ ScummEngine *_vm;
+ byte _disk;
+ BundleDirCache *_cacheBundleDir;
+
+ bool openMusicBundle(soundStruct *sound, int disk);
+ bool openVoiceBundle(soundStruct *sound, int disk);
+
+ void countElements(byte *ptr, int &numRegions, int &numJumps, int &numSyncs);
+
+public:
+
+ ImuseDigiSndMgr(ScummEngine *scumm);
+ ~ImuseDigiSndMgr();
+
+ soundStruct *openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk);
+ void closeSound(soundStruct *soundHandle);
+ soundStruct *cloneSound(soundStruct *soundHandle);
+
+ bool isCompressed(soundStruct *soundHandle);
+ int getFreq(soundStruct *soundHandle);
+ int getBits(soundStruct *soundHandle);
+ int getChannels(soundStruct *soundHandle);
+ bool isEndOfRegion(soundStruct *soundHandle, int region);
+ int getNumRegions(soundStruct *soundHandle);
+ int getNumJumps(soundStruct *soundHandle);
+ int getRegionOffset(soundStruct *soundHandle, int region);
+ int getJumpIdByRegionAndHookId(soundStruct *soundHandle, int region, int hookId);
+ int getRegionIdByJumpId(soundStruct *soundHandle, int jumpId);
+ int getJumpHookId(soundStruct *soundHandle, int number);
+ int getJumpFade(soundStruct *soundHandle, int number);
+ void getSyncSizeAndPtrById(soundStruct *soundHandle, int number, int32 &sync_size, byte **sync_ptr);
+
+ int32 getDataFromRegion(soundStruct *soundHandle, int region, byte **buf, int32 offset, int32 size);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_tables.cpp b/engines/scumm/imuse_digi/dimuse_tables.cpp
new file mode 100644
index 0000000000..f4fd25a160
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_tables.cpp
@@ -0,0 +1,881 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "scumm/imuse_digi/dimuse.h"
+
+namespace Scumm {
+
+#ifdef PALMOS_68K
+const imuseRoomMap *_digStateMusicMap;
+const imuseDigTable *_digStateMusicTable;
+const imuseDigTable *_digSeqMusicTable;
+const imuseComiTable *_comiStateMusicTable;
+const imuseComiTable *_comiSeqMusicTable;
+const imuseFtStateTable *_ftStateMusicTable;
+const imuseFtSeqTable *_ftSeqMusicTable;
+const imuseFtNames *_ftSeqNames;
+#else
+const imuseRoomMap _digStateMusicMap[] = {
+ {0, 0, 0, 0, 0, 0 },
+ {1, 0, 0, 0, 0, 0 },
+ {2, 2, 0, 0, 0, 0 },
+ {4, 3, 0, 0, 0, 0 },
+ {5, 3, 0, 0, 0, 0 },
+ {6, 3, 0, 0, 0, 0 },
+ {7, 3, 0, 0, 0, 0 },
+ {8, 4, 0, 0, 0, 0 },
+ {9, 5, 0, 0, 0, 0 },
+ {10, 4, 0, 0, 0, 0 },
+ {12, 5, 0, 0, 0, 0 },
+ {14, 5, 0, 0, 0, 0 },
+ {15, 6, 29, 7, 0, 0 },
+ {16, 8, 0, 0, 0, 0 },
+ {17, 1, 0, 0, 0, 0 },
+ {18, 9, 0, 0, 0, 0 },
+ {19, 9, 0, 0, 0, 0 },
+ {20, 6, 0, 0, 0, 0 },
+ {21, 6, 0, 0, 0, 0 },
+ {22, 44, 0, 0, 0, 0 },
+ {23, 10, 7, 0, 0, 0 },
+ {24, 26, 0, 0, 0, 0 },
+ {25, 17, 0, 0, 0, 0 },
+ {26, 17, 0, 0, 0, 0 },
+ {27, 18, 0, 0, 0, 0 },
+ {28, 1, 0, 0, 0, 0 },
+ {29, 20, 0, 0, 0, 0 },
+ {30, 22, 0, 0, 0, 0 },
+ {31, 23, 0, 0, 0, 0 },
+ {32, 22, 0, 0, 0, 0 },
+ {33, 26, 0, 0, 0, 0 },
+ {34, 24, 0, 0, 0, 0 },
+ {35, 1, 0, 0, 0, 0 },
+ {36, 1, 0, 0, 0, 0 },
+ {37, 42, 0, 0, 0, 0 },
+ {38, 43, 0, 0, 0, 0 },
+ {39, 44, 0, 0, 0, 0 },
+ {40, 1, 0, 0, 0, 0 },
+ {41, 43, 0, 0, 0, 0 },
+ {42, 44, 0, 0, 0, 0 },
+ {43, 43, 0, 0, 0, 0 },
+ {44, 45, 117,45, 114,46},
+ {47, 1, 0, 0, 0, 0 },
+ {48, 43, 0, 0, 0, 0 },
+ {49, 44, 0, 0, 0, 0 },
+ {51, 1, 0, 0, 0, 0 },
+ {53, 28, 0, 0, 0, 0 },
+ {54, 28, 0, 0, 0, 0 },
+ {55, 29, 0, 0, 0, 0 },
+ {56, 29, 0, 0, 0, 0 },
+ {57, 29, 0, 0, 0, 0 },
+ {58, 31, 0, 0, 0, 0 },
+ {59, 1, 0, 0, 0, 0 },
+ {60, 37, 0, 0, 0, 0 },
+ {61, 39, 0, 0, 0, 0 },
+ {62, 38, 0, 0, 0, 0 },
+ {63, 39, 0, 0, 0, 0 },
+ {64, 39, 0, 0, 0, 0 },
+ {65, 40, 0, 0, 0, 0 },
+ {67, 40, 0, 0, 0, 0 },
+ {68, 39, 0, 0, 0, 0 },
+ {69, 1, 0, 0, 0, 0 },
+ {70, 49, 0, 0, 0, 0 },
+ {73, 50, 0, 0, 0, 0 },
+ {75, 51, 0, 0, 0, 0 },
+ {76, 1, 0, 0, 0, 0 },
+ {77, 52, 7, 0, 0, 0 },
+ {78, 63, 0, 0, 0, 0 },
+ {79, 1, 0, 0, 0, 0 },
+ {82, 21, 0, 0, 0, 0 },
+ {85, 1, 0, 0, 0, 0 },
+ {86, 0, 0, 0, 0, 0 },
+ {89, 33, 6, 35, 5, 34},
+ {90, 16, 0, 0, 0, 0 },
+ {91, 57, 0, 0, 0, 0 },
+ {88, 32, 0, 0, 0, 0 },
+ {92, 25, 0, 0, 0, 0 },
+ {93, 0, 0, 0, 0, 0 },
+ {95, 19, 0, 0, 0, 0 },
+ {80, 41, 0, 0, 0, 0 },
+ {81, 48, 0, 0, 0, 0 },
+ {83, 27, 0, 0, 0, 0 },
+ {94, 36, 0, 0, 0, 0 },
+ {40, 1, 0, 0, 0, 0 },
+ {96, 13, 0, 0, 0, 0 },
+ {97, 14, 0, 0, 0, 0 },
+ {98, 11, 0, 0, 0, 0 },
+ {99, 15, 0, 0, 0, 0 },
+ {100, 17, 0, 0, 0, 0 },
+ {101, 38, 0, 0, 0, 0 },
+ {103, 0, 0, 0, 0, 0 },
+ {104, 0, 0, 0, 0, 0 },
+ {11, 44, 0, 0, 0, 0 },
+ {3, 47, 0, 0, 0, 0 },
+ {105, 30, 128,29, 0, 0 },
+ {106, 0, 0, 0, 0, 0 },
+ {107, 1, 0, 0, 0, 0 },
+ {108, 1, 0, 0, 0, 0 },
+ {47, 1, 0, 0, 0, 0 },
+ {50, 1, 0, 0, 0, 0 },
+ {52, 0, 0, 0, 0, 0 },
+ {71, 1, 0, 0, 0, 0 },
+ {13, 1, 0, 0, 0, 0 },
+ {72, 1, 0, 0, 0, 0 },
+ {46, 33, 6, 35, 5, 34},
+ {74, 1, 0, 0, 0, 0 },
+ {84, 1, 0, 0, 0, 0 },
+ {66, 1, 0, 0, 0, 0 },
+ {102, 1, 0, 0, 0, 0 },
+ {109, 1, 0, 0, 0, 0 },
+ {110, 2, 0, 0, 0, 0 },
+ {45, 1, 0, 0, 0, 0 },
+ {87, 1, 0, 0, 0, 0 },
+ {111, 1, 0, 0, 0, 0 },
+ {-1, 1, 0, 0, 0, 0 }
+};
+
+const imuseDigTable _digStateMusicTable[] = {
+ {0, 1000, "STATE_NULL", 0, 0, ""}, /* 00 */
+ {0, 1001, "stateNoChange", 0, 0, ""}, /* 01 */
+ {3, 1100, "stateAstShip", 2, 0, "ASTERO~1.IMU"}, /* 02 */
+ {3, 1120, "stateAstClose", 2, 0, "ASTERO~2.IMU"}, /* 03 */
+ {3, 1140, "stateAstInside", 0, 0, "ASTERO~3.IMU"}, /* 04 */
+ {3, 1150, "stateAstCore", 0, 2, "ASTERO~4.IMU"}, /* 05 */
+ {3, 1200, "stateCanyonClose", 0, 1, "CANYON~1.IMU"}, /* 06 */
+ {3, 1205, "stateCanyonClose_m", 0, 0, "CANYON~2.IMU"}, /* 07 */
+ {3, 1210, "stateCanyonOver", 0, 1, "CANYON~3.IMU"}, /* 08 */
+ {3, 1220, "stateCanyonWreck", 0, 1, "CANYON~4.IMU"}, /* 09 */
+ {3, 1300, "stateNexusCanyon", 10, 0, "NEXUS(~1.IMU"}, /* 10 */
+ {3, 1310, "stateNexusPlan", 10, 0, "NEXUS(~1.IMU"}, /* 11 */
+ {3, 1320, "stateNexusRamp", 10, 0, "NEXUS(~2.IMU"}, /* 12 */
+ {3, 1330, "stateNexusMuseum", 10, 0, "NEXUS(~3.IMU"}, /* 13 */
+ {3, 1340, "stateNexusMap", 10, 0, "NEXUS(~4.IMU"}, /* 14 */
+ {3, 1350, "stateNexusTomb", 10, 0, "NE3706~5.IMU"}, /* 15 */
+ {3, 1360, "stateNexusCath", 10, 0, "NE3305~5.IMU"}, /* 16 */
+ {3, 1370, "stateNexusAirlock", 0, 0, "NE2D3A~5.IMU"}, /* 17 */
+ {3, 1380, "stateNexusPowerOff", 0, 1, "NE8522~5.IMU"}, /* 18 */
+ {3, 1400, "stateMuseumTramNear", 0, 1, "TRAM(M~1.IMU"}, /* 19 */
+ {3, 1410, "stateMuseumTramFar", 0, 0, "TRAM(M~2.IMU"}, /* 20 */
+ {3, 1420, "stateMuseumLockup", 0, 0, "MUSEUM~1.IMU"}, /* 21 */
+ {3, 1433, "stateMuseumPool", 22, 1, "MUSEUM~2.IMU"}, /* 22 */
+ {3, 1436, "stateMuseumSpire", 22, 2, "MUSEUM~3.IMU"}, /* 23 */
+ {3, 1440, "stateMuseumMuseum", 22, 2, "MUSEUM~4.IMU"}, /* 24 */
+ {3, 1450, "stateMuseumLibrary", 0, 0, "MUB575~5.IMU"}, /* 25 */
+ {3, 1460, "stateMuseumCavern", 0, 0, "MUF9BE~5.IMU"}, /* 26 */
+ {3, 1500, "stateTombTramNear", 0, 1, "TRAM(T~1.IMU"}, /* 27 */
+ {3, 1510, "stateTombBase", 28, 2, "TOMB(A~1.IMU"}, /* 28 */
+ {3, 1520, "stateTombSpire", 28, 2, "TOMB(A~2.IMU"}, /* 29 */
+ {3, 1530, "stateTombCave", 28, 2, "TOMB(A~3.IMU"}, /* 30 */
+ {3, 1540, "stateTombCrypt", 31, 1, "TOMB(C~1.IMU"}, /* 31 */
+ {3, 1550, "stateTombGuards", 31, 1, "TOMB(C~2.IMU"}, /* 32 */
+ {3, 1560, "stateTombInner", 0, 1, "TOMB(I~1.IMU"}, /* 33 */
+ {3, 1570, "stateTombCreator1", 0, 0, "TOMB(C~3.IMU"}, /* 34 */
+ {3, 1580, "stateTombCreator2", 0, 0, "TOMB(C~4.IMU"}, /* 35 */
+ {3, 1600, "statePlanTramNear", 0, 1, "TRAM(P~1.IMU"}, /* 36 */
+ {3, 1610, "statePlanTramFar", 0, 0, "TRAM(P~2.IMU"}, /* 37 */
+ {3, 1620, "statePlanBase", 38, 2, "PLAN(A~1.IMU"}, /* 38 */
+ {3, 1630, "statePlanSpire", 38, 2, "PLAN(A~2.IMU"}, /* 39 */
+ {3, 1650, "statePlanDome", 0, 0, "PLAN(D~1.IMU"}, /* 40 */
+ {3, 1700, "stateMapTramNear", 0, 1, "TRAM(M~3.IMU"}, /* 41 */
+ {3, 1710, "stateMapTramFar", 0, 0, "TRAM(M~4.IMU"}, /* 42 */
+ {3, 1720, "stateMapCanyon", 43, 2, "MAP(AM~1.IMU"}, /* 43 */
+ {3, 1730, "stateMapExposed", 43, 2, "MAP(AM~2.IMU"}, /* 44 */
+ {3, 1750, "stateMapNestEmpty", 43, 2, "MAP(AM~4.IMU"}, /* 45 */
+ {3, 1760, "stateMapNestMonster", 0, 0, "MAP(MO~1.IMU"}, /* 46 */
+ {3, 1770, "stateMapKlein", 0, 0, "MAP(KL~1.IMU"}, /* 47 */
+ {3, 1800, "stateCathTramNear", 0, 1, "TRAM(C~1.IMU"}, /* 48 */
+ {3, 1810, "stateCathTramFar", 0, 0, "TRAM(C~2.IMU"}, /* 49 */
+ {3, 1820, "stateCathLab", 50, 1, "CATH(A~1.IMU"}, /* 50 */
+ {3, 1830, "stateCathOutside", 50, 1, "CATH(A~2.IMU"}, /* 51 */
+ {3, 1900, "stateWorldMuseum", 52, 0, "WORLD(~1.IMU"}, /* 52 */
+ {3, 1901, "stateWorldPlan", 52, 0, "WORLD(~2.IMU"}, /* 53 */
+ {3, 1902, "stateWorldTomb", 52, 0, "WORLD(~3.IMU"}, /* 54 */
+ {3, 1903, "stateWorldMap", 52, 0, "WORLD(~4.IMU"}, /* 55 */
+ {3, 1904, "stateWorldCath", 52, 0, "WO3227~5.IMU"}, /* 56 */
+ {3, 1910, "stateEye1", 0, 0, "EYE1~1.IMU"}, /* 57 */
+ {3, 1911, "stateEye2", 0, 0, "EYE2~1.IMU"}, /* 58 */
+ {3, 1912, "stateEye3", 0, 0, "EYE3~1.IMU"}, /* 59 */
+ {3, 1913, "stateEye4", 0, 0, "EYE4~1.IMU"}, /* 60 */
+ {3, 1914, "stateEye5", 0, 0, "EYE5~1.IMU"}, /* 61 */
+ {3, 1915, "stateEye6", 0, 0, "EYE6~1.IMU"}, /* 62 */
+ {3, 1916, "stateEye7", 0, 0, "EYE7~1.IMU"}, /* 63 */
+ {0, -1, "", 0, 0, ""}
+};
+
+const imuseDigTable _digSeqMusicTable[] = {
+ {0, 2000, "SEQ_NULL", 0, 0, ""},
+ {0, 2005, "seqLogo", 0, 0, ""},
+ {0, 2010, "seqIntro", 0, 0, ""},
+ {6, 2020, "seqExplosion1b", 0, 0, ""},
+ {3, 2030, "seqAstTunnel1a", 0, 0, "SEQ(AS~1.IMU"},
+ {6, 2031, "seqAstTunnel2b", 0, 0, ""},
+ {4, 2032, "seqAstTunnel3a", 0, 0, "SEQ(AS~2.IMU"},
+ {5, 2040, "seqToPlanet1b", 0, 0, ""},
+ {4, 2045, "seqArgBegin", 0, 0, "SEQ(AR~1.IMU"},
+ {4, 2046, "seqArgEnd", 0, 0, "SEQ(AR~2.IMU"},
+ {4, 2050, "seqWreckGhost", 0, 0, "SEQ(GH~1.IMU"},
+ {4, 2060, "seqCanyonGhost", 0, 0, "SEQ(GH~2.IMU"},
+ {0, 2070, "seqBrinkFall", 0, 0, ""},
+ {4, 2080, "seqPanUpCanyon", 0, 0, "SEQ(PA~1.IMU"},
+ {6, 2091, "seqAirlockTunnel1b", 0, 0, ""},
+ {6, 2100, "seqTramToMu", 0, 0, ""},
+ {6, 2101, "seqTramFromMu", 0, 0, ""},
+ {6, 2102, "seqTramToTomb", 0, 0, ""},
+ {6, 2103, "seqTramFromTomb", 0, 0, ""},
+ {6, 2104, "seqTramToPlan", 0, 0, ""},
+ {6, 2105, "seqTramFromPlan", 0, 0, ""},
+ {6, 2106, "seqTramToMap", 0, 0, ""},
+ {6, 2107, "seqTramFromMap", 0, 0, ""},
+ {6, 2108, "seqTramToCath", 0, 0, ""},
+ {6, 2109, "seqTramFromCath", 0, 0, ""},
+ {0, 2110, "seqMuseumGhost", 0, 0, ""},
+ {0, 2120, "seqSerpentAppears", 0, 0, ""},
+ {0, 2130, "seqSerpentEats", 0, 0, ""},
+ {6, 2140, "seqBrinkRes1b", 0, 0, ""},
+ {4, 2141, "seqBrinkRes2a", 0, 0, "SEQ(BR~1.IMU"},
+ {3, 2150, "seqLockupEntry", 0, 0, "SEQ(BR~1.IMU"},
+ {0, 2160, "seqSerpentExplodes", 0, 0, ""},
+ {4, 2170, "seqSwimUnderwater", 0, 0, "SEQ(DE~1.IMU"},
+ {4, 2175, "seqWavesPlunge", 0, 0, "SEQ(PL~1.IMU"},
+ {0, 2180, "seqCryptOpens", 0, 0, ""},
+ {0, 2190, "seqGuardsFight", 0, 0, ""},
+ {3, 2200, "seqCreatorRes1.1a", 0, 0, "SEQ(CR~1.IMU"},
+ {6, 2201, "seqCreatorRes1.2b", 0, 0, ""},
+ {6, 2210, "seqMaggieCapture1b", 0, 0, ""},
+ {3, 2220, "seqStealCrystals", 0, 0, "SEQ(BR~1.IMU"},
+ {0, 2230, "seqGetByMonster", 0, 0, ""},
+ {6, 2240, "seqKillMonster1b", 0, 0, ""},
+ {3, 2250, "seqCreatorRes2.1a", 0, 0, "SEQ(CR~2.IMU"},
+ {6, 2251, "seqCreatorRes2.2b", 0, 0, ""},
+ {4, 2252, "seqCreatorRes2.3a", 0, 0, "SEQ(CR~3.IMU"},
+ {0, 2260, "seqMaggieInsists", 0, 0, ""},
+ {0, 2270, "seqBrinkHelpCall", 0, 0, ""},
+ {3, 2280, "seqBrinkCrevice1a", 0, 0, "SEQ(BR~2.IMU"},
+ {3, 2281, "seqBrinkCrevice2a", 0, 0, "SEQ(BR~3.IMU"},
+ {6, 2290, "seqCathAccess1b", 0, 0, ""},
+ {4, 2291, "seqCathAccess2a", 0, 0, "SEQ(CA~1.IMU"},
+ {3, 2300, "seqBrinkAtGenerator", 0, 0, "SEQ(BR~1.IMU"},
+ {6, 2320, "seqFightBrink1b", 0, 0, ""},
+ {6, 2340, "seqMaggieDies1b", 0, 0, ""},
+ {6, 2346, "seqMaggieRes1b", 0, 0, ""},
+ {4, 2347, "seqMaggieRes2a", 0, 0, "SEQ(MA~1.IMU"},
+ {0, 2350, "seqCreatureFalls", 0, 0, ""},
+ {5, 2360, "seqFinale1b", 0, 0, ""},
+ {3, 2370, "seqFinale2a", 0, 0, "SEQ(FI~1.IMU"},
+ {6, 2380, "seqFinale3b1", 0, 0, ""},
+ {6, 2390, "seqFinale3b2", 0, 0, ""},
+ {3, 2400, "seqFinale4a", 0, 0, "SEQ(FI~2.IMU"},
+ {3, 2410, "seqFinale5a", 0, 0, "SEQ(FI~3.IMU"},
+ {3, 2420, "seqFinale6a", 0, 0, "SEQ(FI~4.IMU"},
+ {3, 2430, "seqFinale7a", 0, 0, "SE3D2B~5.IMU"},
+ {6, 2440, "seqFinale8b", 0, 0, ""},
+ {4, 2450, "seqFinale9a", 0, 0, "SE313B~5.IMU"},
+ {0, -1, "", 0, 0, ""}
+};
+
+const imuseComiTable _comiStateMusicTable[] = {
+ {0, 1000, "STATE_NULL", 0, 0, 0, ""}, /* 00 */
+ {0, 1001, "stateNoChange", 0, 0, 0, ""}, /* 01 */
+ {3, 1098, "stateCredits1", 0, 0, 60, "1098-C~1.IMX"}, /* 02 */
+ {3, 1099, "stateMenu", 0, 0, 60, "1099-M~1.IMX"}, /* 03 */
+ {3, 1100, "stateHold1", 4, 0, 60, "1100-H~1.IMX"}, /* 04 */
+ {3, 1101, "stateWaterline1", 4, 0, 60, "1101-W~1.IMX"}, /* 05 */
+ {3, 1102, "stateHold2", 6, 1, 60, "1102-H~1.IMX"}, /* 06 */
+ {3, 1103, "stateWaterline2", 6, 0, 60, "1103-W~1.IMX"}, /* 07 */
+ {3, 1104, "stateCannon", 0, 0, 60, "1104-C~1.IMX"}, /* 08 */
+ {3, 1105, "stateTreasure", 0, 0, 60, "1105-T~1.IMX"}, /* 09 */
+ {3, 1200, "stateFortBase", 10, 1, 60, "1200-F~1.IMX"}, /* 10 */
+ {3, 1201, "statePreFort", 10, 1, 60, "1201-P~1.IMX"}, /* 11 */
+ {3, 1202, "statePreVooOut", 12, 0, 60, "1202-P~1.IMX"}, /* 12 */
+ {3, 1203, "statePreVooIn", 12, 0, 60, "1203-P~1.IMX"}, /* 13 */
+ {3, 1204, "statePreVooLady", 12, 0, 60, "1204-P~1.IMX"}, /* 14 */
+ {3, 1205, "stateVoodooOut", 0, 0, 60, "1205-V~1.IMX"}, /* 15 */
+ {3, 1210, "stateVoodooIn", 0, 0, 60, "1210-V~1.IMX"}, /* 16 */
+ {12,1212, "stateVoodooInAlt", 0, 1, 42, "1210-V~1.IMX"}, /* 17 */
+ {3, 1215, "stateVoodooLady", 0, 0, 60, "1215-V~1.IMX"}, /* 18 */
+ {3, 1219, "statePrePlundermap", 0, 0, 60, "1219-P~1.IMX"}, /* 19 */
+ {3, 1220, "statePlundermap", 0, 0, 60, "1220-P~1.IMX"}, /* 20 */
+ {3, 1222, "statePreCabana", 0, 0, 60, "1222-P~1.IMX"}, /* 21 */
+ {3, 1223, "stateCabana", 0, 0, 60, "1223-C~1.IMX"}, /* 22 */
+ {3, 1224, "statePostCabana", 23, 0, 60, "1224-P~1.IMX"}, /* 23 */
+ {3, 1225, "stateBeachClub", 23, 0, 60, "1225-B~1.IMX"}, /* 24 */
+ {3, 1230, "stateCliff", 0, 0, 60, "1230-C~1.IMX"}, /* 25 */
+ {3, 1232, "stateBelly", 0, 0, 48, "1232-B~1.IMX"}, /* 26 */
+ {3, 1235, "stateQuicksand", 0, 0, 60, "1235-Q~1.IMX"}, /* 27 */
+ {3, 1240, "stateDangerBeach", 0, 0, 48, "1240-D~1.IMX"}, /* 28 */
+ {12,1241, "stateDangerBeachAlt",0, 2, 48, "1240-D~1.IMX"}, /* 29 */
+ {3, 1245, "stateRowBoat", 0, 0, 60, "1245-R~1.IMX"}, /* 30 */
+ {3, 1247, "stateAlongside", 0, 0, 48, "1247-A~1.IMX"}, /* 31 */
+ {12,1248, "stateAlongsideAlt", 0, 1, 48, "1247-A~1.IMX"}, /* 32 */
+ {3, 1250, "stateChimpBoat", 0, 0, 30, "1250-C~1.IMX"}, /* 33 */
+ {3, 1255, "stateMrFossey", 0, 0, 48, "1255-M~1.IMX"}, /* 34 */
+ {3, 1259, "statePreTown", 0, 0, 60, "1259-P~1.IMX"}, /* 35 */
+ {3, 1260, "stateTown", 0, 0, 60, "1260-T~1.IMX"}, /* 36 */
+ {3, 1264, "statePreMeadow", 0, 0, 60, "1264-P~1.IMX"}, /* 37 */
+ {3, 1265, "stateMeadow", 0, 0, 60, "1265-M~1.IMX"}, /* 38 */
+ {3, 1266, "stateMeadowAmb", 0, 0, 60, "1266-M~1.IMX"}, /* 39 */
+ {3, 1270, "stateWardrobePre", 40, 0, 60, "1270-W~1.IMX"}, /* 40 */
+ {3, 1272, "statePreShow", 40, 0, 60, "1272-P~1.IMX"}, /* 41 */
+ {3, 1274, "stateWardrobeShow", 42, 0, 60, "1274-W~1.IMX"}, /* 42 */
+ {3, 1276, "stateShow", 42, 0, 60, "1276-S~1.IMX"}, /* 43 */
+ {3, 1277, "stateWardrobeJug", 44, 0, 60, "1277-W~1.IMX"}, /* 44 */
+ {3, 1278, "stateJuggling", 44, 0, 60, "1278-J~1.IMX"}, /* 45 */
+ {3, 1279, "statePostShow", 0, 0, 60, "1279-P~1.IMX"}, /* 46 */
+ {3, 1280, "stateChickenShop", 0, 0, 60, "1280-C~1.IMX"}, /* 47 */
+ {3, 1285, "stateBarberShop", 48, 0, 60, "1285-B~1.IMX"}, /* 48 */
+ {3, 1286, "stateVanHelgen", 48, 0, 60, "1286-V~1.IMX"}, /* 49 */
+ {3, 1287, "stateBill", 48, 0, 60, "1287-B~1.IMX"}, /* 50 */
+ {3, 1288, "stateHaggis", 48, 0, 60, "1288-H~1.IMX"}, /* 51 */
+ {3, 1289, "stateRottingham", 48, 0, 60, "1289-R~1.IMX"}, /* 52 */
+ {3, 1305, "stateDeck", 0, 0, 60, "1305-D~1.IMX"}, /* 53 */
+ {3, 1310, "stateCombatMap", 0, 0, 60, "1310-C~1.IMX"}, /* 54 */
+ {3, 1320, "stateShipCombat", 0, 0, 60, "1320-S~1.IMX"}, /* 55 */
+ {3, 1325, "stateSwordfight", 0, 0, 60, "1325-S~1.IMX"}, /* 56 */
+ {3, 1327, "stateSwordRott", 0, 0, 60, "1327-S~1.IMX"}, /* 57 */
+ {3, 1330, "stateTownEdge", 0, 0, 60, "1330-T~1.IMX"}, /* 58 */
+ {3, 1335, "stateSwordLose", 0, 0, 60, "1335-S~1.IMX"}, /* 59 */
+ {3, 1340, "stateSwordWin", 0, 0, 60, "1340-S~1.IMX"}, /* 60 */
+ {3, 1345, "stateGetMap", 0, 0, 60, "1345-G~1.IMX"}, /* 61 */
+ {3, 1400, "stateWreckBeach", 0, 0, 60, "1400-W~1.IMX"}, /* 62 */
+ {3, 1405, "stateBloodMap", 63, 0, 60, "1405-B~1.IMX"}, /* 63 */
+ {3, 1410, "stateClearing", 0, 0, 60, "1410-C~1.IMX"}, /* 64 */
+ {3, 1415, "stateLighthouse", 63, 0, 60, "1415-L~1.IMX"}, /* 65 */
+ {3, 1420, "stateVillage", 66, 0, 60, "1420-V~1.IMX"}, /* 66 */
+ {3, 1423, "stateVolcano", 66, 0, 60, "1423-V~1.IMX"}, /* 67 */
+ {3, 1425, "stateAltar", 66, 0, 60, "1425-A~1.IMX"}, /* 68 */
+ {3, 1430, "stateHotelOut", 0, 0, 60, "1430-H~1.IMX"}, /* 69 */
+ {3, 1435, "stateHotelBar", 70, 0, 60, "1435-H~1.IMX"}, /* 70 */
+ {3, 1440, "stateHotelIn", 70, 0, 60, "1440-H~1.IMX"}, /* 71 */
+ {3, 1445, "stateTarotLady", 70, 0, 60, "1445-T~1.IMX"}, /* 72 */
+ {3, 1447, "stateGoodsoup", 70, 0, 60, "1447-G~1.IMX"}, /* 73 */
+ {3, 1448, "stateGuestRoom", 0, 0, 60, "1448-G~1.IMX"}, /* 74 */
+ {3, 1450, "stateWindmill", 63, 0, 60, "1450-W~1.IMX"}, /* 75 */
+ {3, 1455, "stateCemetary", 0, 0, 60, "1455-C~1.IMX"}, /* 76 */
+ {3, 1460, "stateCrypt", 77, 0, 60, "1460-C~1.IMX"}, /* 77 */
+ {3, 1463, "stateGraveDigger", 77, 0, 60, "1463-G~1.IMX"}, /* 78 */
+ {3, 1465, "stateMonkey1", 0, 0, 60, "1465-M~1.IMX"}, /* 79 */
+ {3, 1475, "stateStanDark", 0, 0, 60, "1475-S~1.IMX"}, /* 80 */
+ {3, 1477, "stateStanLight", 0, 0, 60, "1477-S~1.IMX"}, /* 81 */
+ {3, 1480, "stateEggBeach", 63, 0, 60, "1480-E~1.IMX"}, /* 82 */
+ {3, 1485, "stateSkullIsland", 0, 0, 60, "1485-S~1.IMX"}, /* 83 */
+ {3, 1490, "stateSmugglersCave", 0, 0, 60, "1490-S~1.IMX"}, /* 84 */
+ {3, 1500, "stateLeChuckTalk", 0, 0, 60, "1500-L~1.IMX"}, /* 85 */
+ {3, 1505, "stateCarnival", 0, 0, 60, "1505-C~1.IMX"}, /* 86 */
+ {3, 1511, "stateHang", 87, 0, 60, "1511-H~1.IMX"}, /* 87 */
+ {3, 1512, "stateRum", 87, 0, 60, "1512-RUM.IMX"}, /* 88 */
+ {3, 1513, "stateTorture", 87, 0, 60, "1513-T~1.IMX"}, /* 89 */
+ {3, 1514, "stateSnow", 87, 0, 60, "1514-S~1.IMX"}, /* 90 */
+ {3, 1515, "stateCredits", 0, 0, 60, "1515-C~1.IMX"}, /* 91 */
+ {3, 1520, "stateCarnAmb", 0, 0, 60, "1520-C~1.IMX"}, /* 92 */
+ {0, -1, "", 0, 0, 0, ""}
+};
+
+const imuseComiTable _comiSeqMusicTable[] = {
+ {0, 2000, "SEQ_NULL", 0, 0, 0, ""},
+ {0, 2100, "seqINTRO", 0, 0, 0, ""},
+ {3, 2105, "seqInterlude1", 0, 0, 60, "2105-I~1.IMX"},
+ {8, 2110, "seqLastBoat", 0, 1, 0, ""},
+ {0, 2115, "seqSINK_SHIP", 0, 0, 0, ""},
+ {0, 2120, "seqCURSED_RING", 0, 0, 60, ""},
+ {3, 2200, "seqInterlude2", 0, 0, 60, "2200-I~1.IMX"},
+ {3, 2210, "seqKidnapped", 0, 0, 60, "2210-K~1.IMX"},
+ {8, 2220, "seqSnakeVomits", 0, 1, 0, ""},
+ {8, 2222, "seqPopBalloon", 0, 1, 0, ""},
+ {3, 2225, "seqDropBalls", 0, 0, 60, "2225-D~1.IMX"},
+ {4, 2232, "seqArriveBarber", 0, 0, 60, "2232-A~1.IMX"},
+ {3, 2233, "seqAtonal", 0, 0, 60, "2233-A~1.IMX"},
+ {3, 2235, "seqShaveHead1", 0, 0, 60, "2235-S~1.IMX"},
+ {2, 2236, "seqShaveHead2", 0, 2, 60, "2235-S~1.IMX"},
+ {3, 2245, "seqCaberLose", 0, 0, 60, "2245-C~1.IMX"},
+ {3, 2250, "seqCaberWin", 0, 0, 60, "2250-C~1.IMX"},
+ {3, 2255, "seqDuel1", 0, 0, 60, "2255-D~1.IMX"},
+ {2, 2256, "seqDuel2", 0, 2, 60, "2255-D~1.IMX"},
+ {2, 2257, "seqDuel3", 0, 3, 60, "2255-D~1.IMX"},
+ {3, 2260, "seqBlowUpTree1", 0, 0, 60, "2260-B~1.IMX"},
+ {2, 2261, "seqBlowUpTree2", 0, 2, 60, "2260-B~1.IMX"},
+ {3, 2275, "seqMonkeys", 0, 0, 60, "2275-M~1.IMX"},
+ {9, 2277, "seqAttack", 0, 1, 0, ""},
+ {3, 2285, "seqSharks", 0, 0, 60, "2285-S~1.IMX"},
+ {3, 2287, "seqTowelWalk", 0, 0, 60, "2287-T~1.IMX"},
+ {0, 2293, "seqNICE_BOOTS", 0, 0, 0, ""},
+ {0, 2295, "seqBIG_BONED", 0, 0, 0, ""},
+ {3, 2300, "seqToBlood", 0, 0, 60, "2300-T~1.IMX"},
+ {3, 2301, "seqInterlude3", 0, 0, 60, "2301-I~1.IMX"},
+ {3, 2302, "seqRott1", 0, 0, 60, "2302-R~1.IMX"},
+ {2, 2304, "seqRott2", 0, 2, 60, "2302-R~1.IMX"},
+ {2, 2305, "seqRott2b", 0,21, 60, "2302-R~1.IMX"},
+ {2, 2306, "seqRott3", 0, 3, 60, "2302-R~1.IMX"},
+ {2, 2308, "seqRott4", 0, 4, 60, "2302-R~1.IMX"},
+ {2, 2309, "seqRott5", 0, 5, 60, "2302-R~1.IMX"},
+ {3, 2311, "seqVerse1", 0, 0, 60, "2311-S~1.IMX"},
+ {2, 2312, "seqVerse2", 0, 2, 60, "2311-S~1.IMX"},
+ {2, 2313, "seqVerse3", 0, 3, 60, "2311-S~1.IMX"},
+ {2, 2314, "seqVerse4", 0, 4, 60, "2311-S~1.IMX"},
+ {2, 2315, "seqVerse5", 0, 5, 60, "2311-S~1.IMX"},
+ {2, 2316, "seqVerse6", 0, 6, 60, "2311-S~1.IMX"},
+ {2, 2317, "seqVerse7", 0, 7, 60, "2311-S~1.IMX"},
+ {2, 2318, "seqVerse8", 0, 8, 60, "2311-S~1.IMX"},
+ {2, 2319, "seqSongEnd", 0, 9, 60, "2311-S~1.IMX"},
+ {2, 2336, "seqRiposteLose", 0, 0, 60, "2336-R~1.IMX"},
+ {2, 2337, "seqRiposteWin", 0, 0, 60, "2337-R~1.IMX"},
+ {2, 2338, "seqInsultLose", 0, 0, 60, "2338-I~1.IMX"},
+ {2, 2339, "seqInsultWin", 0, 0, 60, "2339-I~1.IMX"},
+ {3, 2340, "seqSwordLose", 0, 0, 60, "1335-S~1.IMX"},
+ {3, 2345, "seqSwordWin", 0, 0, 60, "1340-S~1.IMX"},
+ {3, 2347, "seqGetMap", 0, 0, 60, "1345-G~1.IMX"},
+ {3, 2400, "seqInterlude4", 0, 0, 60, "2400-I~1.IMX"},
+ {0, 2405, "seqSHIPWRECK", 0, 0, 0, ""},
+ {3, 2408, "seqFakeCredits", 0, 0, 60, "2408-F~1.IMX"},
+ {3, 2410, "seqPassOut", 0, 0, 60, "2410-P~1.IMX"},
+ {3, 2414, "seqGhostTalk", 0, 0, 60, "2414-G~1.IMX"},
+ {2, 2415, "seqGhostWedding", 0, 1, 60, "2414-G~1.IMX"},
+ {3, 2420, "seqEruption", 0, 0, 60, "2420-E~1.IMX"},
+ {3, 2425, "seqSacrifice", 0, 0, 60, "2425-S~1.IMX"},
+ {2, 2426, "seqSacrificeEnd", 0, 1, 60, "2425-S~1.IMX"},
+ {3, 2430, "seqScareDigger", 0, 0, 60, "2430-S~1.IMX"},
+ {3, 2445, "seqSkullArrive", 0, 0, 60, "2445-S~1.IMX"},
+ {3, 2450, "seqFloat", 0, 0, 60, "2450-C~1.IMX"},
+ {2, 2451, "seqFall", 0, 1, 60, "2450-C~1.IMX"},
+ {2, 2452, "seqUmbrella", 0, 0, 60, "2450-C~1.IMX"},
+ {3, 2460, "seqFight", 0, 0, 60, "2460-F~1.IMX"},
+ {0, 2465, "seqLAVE_RIDE", 0, 0, 0, ""},
+ {0, 2470, "seqMORE_SLAW", 0, 0, 0, ""},
+ {0, 2475, "seqLIFT_CURSE", 0, 0, 0, ""},
+ {3, 2500, "seqInterlude5", 0, 0, 60, "2500-I~1.IMX"},
+ {3, 2502, "seqExitSkycar", 0, 0, 60, "2502-E~1.IMX"},
+ {3, 2504, "seqGrow1", 0, 0, 60, "2504-G~1.IMX"},
+ {2, 2505, "seqGrow2", 0, 1, 60, "2504-G~1.IMX"},
+ {3, 2508, "seqInterlude6", 0, 0, 60, "2508-I~1.IMX"},
+ {0, 2515, "seqFINALE", 0, 0, 0, ""},
+ {3, 2520, "seqOut", 0, 0, 60, "2520-OUT.IMX"},
+ {3, 2530, "seqZap1a", 0, 0, 60, "2530-Z~1.IMX"},
+ {2, 2531, "seqZap1b", 0, 1, 60, "2530-Z~1.IMX"},
+ {2, 2532, "seqZap1c", 0, 2, 60, "2530-Z~1.IMX"},
+ {2, 2540, "seqZap2a", 0, 0, 60, "2540-Z~1.IMX"},
+ {2, 2541, "seqZap2b", 0, 1, 60, "2540-Z~1.IMX"},
+ {2, 2542, "seqZap2c", 0, 2, 60, "2540-Z~1.IMX"},
+ {3, 2550, "seqZap3a", 0, 0, 60, "2550-Z~1.IMX"},
+ {2, 2551, "seqZap3b", 0, 1, 60, "2550-Z~1.IMX"},
+ {2, 2552, "seqZap3c", 0, 2, 60, "2550-Z~1.IMX"},
+ {3, 2560, "seqZap4a", 0, 0, 60, "2560-Z~1.IMX"},
+ {2, 2561, "seqZap4b", 0, 1, 60, "2560-Z~1.IMX"},
+ {2, 2562, "seqZap4c", 0, 2, 60, "2560-Z~1.IMX"},
+ {0, -1, "", 0, 0, 0, ""}
+};
+
+const imuseFtStateTable _ftStateMusicTable[] = {
+ {"", 0, 0, "STATE_NULL" },
+ {"", 4, 127, "stateKstandOutside" },
+ {"kinside", 2, 127, "stateKstandInside" },
+ {"moshop", 3, 64, "stateMoesInside" },
+ {"melcut", 2, 127, "stateMoesOutside" },
+ {"mellover", 2, 127, "stateMellonAbove" },
+ {"radloop", 3, 28, "stateTrailerOutside" },
+ {"radloop", 3, 58, "stateTrailerInside" },
+ {"radloop", 3, 127, "stateTodShop" },
+ {"junkgate", 2, 127, "stateJunkGate" },
+ {"junkover", 3, 127, "stateJunkAbove" },
+ {"gastower", 2, 127, "stateGasTower" },
+ {"", 4, 0, "stateTowerAlarm" },
+ {"melcut", 2, 127, "stateCopsOnGround" },
+ {"melcut", 2, 127, "stateCopsAround" },
+ {"melcut", 2, 127, "stateMoesRuins" },
+ {"melcut", 2, 127, "stateKstandNight" },
+ {"trukblu2", 2, 127, "stateTruckerTalk" },
+ {"stretch", 2, 127, "stateMumblyPeg" },
+ {"kstand", 2, 100, "stateRanchOutside" },
+ {"kinside", 2, 127, "stateRanchInside" },
+ {"desert", 2, 127, "stateWreckedTruck" },
+ {"opening", 2, 100, "stateGorgeVista" },
+ {"caveopen", 2, 127, "stateCaveOpen" },
+ {"cavecut1", 2, 127, "stateCaveOuter" },
+ {"cavecut1", 1, 127, "stateCaveMiddle" },
+ {"cave", 2, 127, "stateCaveInner" },
+ {"corville", 2, 127, "stateCorvilleFront" },
+ {"mines", 2, 127, "stateMineField" },
+ {"bunyman3", 2, 127, "stateBunnyStore" },
+ {"stretch", 2, 127, "stateStretchBen" },
+ {"saveme", 2, 127, "stateBenPleas" },
+ {"", 4, 0, "stateBenConvinces" },
+ {"derby", 3, 127, "stateDemoDerby" },
+ {"fire", 3, 127, "stateLightMyFire" },
+ {"derby", 3, 127, "stateDerbyChase" },
+ {"carparts", 2, 127, "stateVultureCarParts"},
+ {"cavecut1", 2, 127, "stateVulturesInside" },
+ {"mines", 2, 127, "stateFactoryRear" },
+ {"croffice", 2, 127, "stateCorleyOffice" },
+ {"melcut", 2, 127, "stateCorleyHall" },
+ {"", 4, 0, "stateProjRoom" },
+ {"", 4, 0, "stateMMRoom" },
+ {"bumper", 2, 127, "stateBenOnBumper" },
+ {"benump", 2, 127, "stateBenOnBack" },
+ {"plane", 2, 127, "stateInCargoPlane" },
+ {"saveme", 2, 127, "statePlaneControls" },
+ {"", 4, 0, "stateCliffHanger1" },
+ {"", 4, 0, "stateCliffHanger2" },
+};
+
+const imuseFtNames _ftSeqNames[] = {
+ {"SEQ_NULL" },
+ {"seqLogo" },
+ {"seqOpenFlick" },
+ {"seqBartender" },
+ {"seqBenWakes" },
+ {"seqPhotoScram" },
+ {"seqClimbChain" },
+ {"seqDogChase" },
+ {"seqDogSquish" },
+ {"seqDogHoist" },
+ {"seqCopsArrive" },
+ {"seqCopsLand" },
+ {"seqCopsLeave" },
+ {"seqCopterFlyby" },
+ {"seqCopterCrash" },
+ {"seqMoGetsParts" },
+ {"seqMoFixesBike" },
+ {"seqFirstGoodbye" },
+ {"seqCopRoadblock" },
+ {"seqDivertCops" },
+ {"seqMurder" },
+ {"seqCorleyDies" },
+ {"seqTooLateAtMoes" },
+ {"seqPicture" },
+ {"seqNewsReel" },
+ {"seqCopsInspect" },
+ {"seqHijack" },
+ {"seqNestolusAtRanch" },
+ {"seqRipLimo" },
+ {"seqGorgeTurn" },
+ {"seqCavefishTalk" },
+ {"seqArriveCorville" },
+ {"seqSingleBunny" },
+ {"seqBunnyArmy" },
+ {"seqArriveAtMines" },
+ {"seqArriveAtVultures"},
+ {"seqMakePlan" },
+ {"seqShowPlan" },
+ {"seqDerbyStart" },
+ {"seqLightBales" },
+ {"seqNestolusBBQ" },
+ {"seqCallSecurity" },
+ {"seqFilmFail" },
+ {"seqFilmBurn" },
+ {"seqRipSpeech" },
+ {"seqExposeRip" },
+ {"seqRipEscape" },
+ {"seqRareMoment" },
+ {"seqFanBunnies" },
+ {"seqRipDead" },
+ {"seqFuneral" },
+ {"seqCredits" }
+};
+
+const imuseFtSeqTable _ftSeqMusicTable[] = {
+ {"", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"opening", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"barbeat", 2, 127},
+ {"barwarn", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0, },
+
+ {"benwakes", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"barwarn", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"swatben", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"dogattak", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"bunymrch", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 0, 0 },
+ {"melcut", 2, 127},
+ {"tada", 2, 127},
+ {"", 0, 0 },
+
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"trucker", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"barwarn", 2, 127},
+ {"murder", 2, 127},
+ {"murder2", 2, 127},
+ {"", 0, 0 },
+
+ {"corldie", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"barwarn", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"picture", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"ripintro", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"trucker", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"hosed", 2, 127},
+
+ {"ripdead", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"nesranch", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"scolding", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"desert", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"cavecut1", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"caveamb", 2, 80 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"castle", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"bunymrch", 2, 105},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"valkyrs", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"melcut", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"veltures", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"sorry", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"makeplan", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"castle", 2, 127},
+ {"derby", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"fire", 3, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"saveme", 3, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"scolding", 2, 127},
+
+ {"cops2", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"sorry", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"sorry", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"caveamb", 2, 85 },
+ {"tada", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"expose", 2, 127},
+ {"", 4, 0 },
+ {"", 0, 0 },
+ {"mocoup", 2, 127},
+
+ {"ripscram", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"valkyrs", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"ripdead", 2, 127},
+ {"", 0, 0 },
+ {"", 0, 0 },
+ {"", 0, 0 },
+
+ {"funeral", 2, 127},
+ {"", 2, 127},
+ {"moshop", 3, 64 },
+ {"", 0, 0 },
+
+ {"bornbad", 2, 127},
+ {"hammvox", 2, 127},
+ {"legavox", 2, 127},
+ {"chances", 2, 90 },
+};
+#endif
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(DimuseTables)
+_GSETPTR(Scumm::_digStateMusicMap, GBVARS_DIGSTATEMUSICMAP_INDEX, Scumm::imuseRoomMap , GBVARS_SCUMM)
+_GSETPTR(Scumm::_digStateMusicTable, GBVARS_DIGSTATEMUSICTABLE_INDEX, Scumm::imuseDigTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_digSeqMusicTable, GBVARS_DIGSEQMUSICTABLE_INDEX, Scumm::imuseDigTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_comiStateMusicTable, GBVARS_COMISTATEMUSICTABLE_INDEX, Scumm::imuseComiTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_comiSeqMusicTable, GBVARS_COMISEQMUSICTABLE_INDEX, Scumm::imuseComiTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_ftStateMusicTable, GBVARS_FTSTATEMUSICTABLE_INDEX, Scumm::imuseFtStateTable, GBVARS_SCUMM)
+_GSETPTR(Scumm::_ftSeqMusicTable, GBVARS_FTSEQMUSICTABLE_INDEX, Scumm::imuseFtSeqTable , GBVARS_SCUMM)
+_GSETPTR(Scumm::_ftSeqNames, GBVARS_FTSEQNAMES_INDEX, Scumm::imuseFtNames , GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(DimuseTables)
+_GRELEASEPTR(GBVARS_DIGSTATEMUSICMAP_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_DIGSTATEMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_DIGSEQMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_COMISTATEMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_COMISEQMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FTSTATEMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FTSEQMUSICTABLE_INDEX , GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FTSEQNAMES_INDEX , GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_track.cpp b/engines/scumm/imuse_digi/dimuse_track.cpp
new file mode 100644
index 0000000000..d1bd5b8923
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_track.cpp
@@ -0,0 +1,368 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/timer.h"
+
+#include "scumm/actor.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+int IMuseDigital::allocSlot(int priority) {
+ int l, lowest_priority = 127;
+ int trackId = -1;
+
+ for (l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ if (!_track[l]->used) {
+ trackId = l;
+ break;
+ }
+ }
+
+ if (trackId == -1) {
+ debug(5, "IMuseDigital::startSound(): All slots are full");
+ for (l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved &&
+ (lowest_priority > track->priority) && !track->stream2) {
+ lowest_priority = track->priority;
+ trackId = l;
+ }
+ }
+ if (lowest_priority <= priority) {
+ assert(trackId != -1);
+ _track[trackId]->toBeRemoved = true;
+ debug(5, "IMuseDigital::startSound(): Removed sound %d from track %d", _track[trackId]->soundId, trackId);
+ } else {
+ debug(5, "IMuseDigital::startSound(): Priority sound too low");
+ return -1;
+ }
+ }
+
+ return trackId;
+}
+
+void IMuseDigital::startSound(int soundId, const char *soundName, int soundType, int volGroupId, AudioStream *input, int hookId, int volume, int priority) {
+ debug(5, "IMuseDigital::startSound(%d)", soundId);
+
+ int l = allocSlot(priority);
+ if (l == -1) {
+ warning("IMuseDigital::startSound() Can't start sound - no free slots");
+ return;
+ }
+
+ Track *track = _track[l];
+ while (track->used) {
+ // The designated track is not yet available. So, we call flushTracks()
+ // to get it processed (and thus made ready for us). Since the actual
+ // processing is done by another thread, we also call parseEvents to
+ // give it some time (and to avoid busy waiting/looping).
+ flushTracks();
+#ifndef __PLAYSTATION2__
+ _vm->parseEvents();
+#endif
+ }
+
+ track->pan = 64;
+ track->vol = volume * 1000;
+ track->volFadeDest = 0;
+ track->volFadeStep = 0;
+ track->volFadeDelay = 0;
+ track->volFadeUsed = false;
+ track->soundId = soundId;
+ track->started = false;
+ track->volGroupId = volGroupId;
+ track->curHookId = hookId;
+ track->priority = priority;
+ track->curRegion = -1;
+ track->dataOffset = 0;
+ track->regionOffset = 0;
+ track->mod = 0;
+ track->mixerFlags = 0;
+ track->toBeRemoved = false;
+ track->readyToRemove = false;
+ track->soundType = soundType;
+
+ int bits = 0, freq = 0, channels = 0;
+
+ if (input) {
+ track->iteration = 0;
+ track->souStream = true;
+ track->soundName[0] = 0;
+ } else {
+ track->souStream = false;
+ strcpy(track->soundName, soundName);
+ track->soundHandle = _sound->openSound(soundId, soundName, soundType, volGroupId, -1);
+
+ if (track->soundHandle == NULL)
+ return;
+
+ track->compressed = _sound->isCompressed(track->soundHandle);
+
+ bits = _sound->getBits(track->soundHandle);
+ channels = _sound->getChannels(track->soundHandle);
+ freq = _sound->getFreq(track->soundHandle);
+
+ if ((soundId == kTalkSoundID) && (soundType == IMUSE_BUNDLE)) {
+ if (_vm->_actorToPrintStrFor != 0xFF && _vm->_actorToPrintStrFor != 0) {
+ Actor *a = _vm->derefActor(_vm->_actorToPrintStrFor, "IMuseDigital::startSound");
+ freq = (freq * a->_talkFrequency) / 256;
+ track->pan = a->_talkPan;
+ track->vol = a->_talkVolume * 1000;
+ }
+ }
+
+ assert(bits == 8 || bits == 12 || bits == 16);
+ assert(channels == 1 || channels == 2);
+ assert(0 < freq && freq <= 65535);
+
+ track->iteration = freq * channels;
+ if (channels == 2)
+ track->mixerFlags = Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_REVERSE_STEREO;
+
+ if ((bits == 12) || (bits == 16)) {
+ track->mixerFlags |= Audio::Mixer::FLAG_16BITS;
+ track->iteration *= 2;
+ } else if (bits == 8) {
+ track->mixerFlags |= Audio::Mixer::FLAG_UNSIGNED;
+ } else
+ error("IMuseDigital::startSound(): Can't handle %d bit samples", bits);
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ if (track->compressed)
+ track->mixerFlags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+ }
+
+ if (input) {
+ track->stream2 = input;
+ track->stream = NULL;
+ track->started = false;
+ } else {
+ const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0;
+ const int vol = track->vol / 1000;
+ Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType;
+
+ if (track->volGroupId == 1)
+ type = Audio::Mixer::kSpeechSoundType;
+ if (track->volGroupId == 2)
+ type = Audio::Mixer::kSFXSoundType;
+ if (track->volGroupId == 3)
+ type = Audio::Mixer::kMusicSoundType;
+
+ // setup 1 second stream wrapped buffer
+ int32 streamBufferSize = track->iteration;
+ track->stream2 = NULL;
+ track->stream = makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize);
+ _vm->_mixer->playInputStream(type, &track->handle, track->stream, -1, vol, pan, false);
+ track->started = true;
+ }
+
+ track->used = true;
+}
+
+void IMuseDigital::setPriority(int soundId, int priority) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setPriority()");
+ debug(5, "IMuseDigital::setPriority(%d, %d)", soundId, priority);
+ assert ((priority >= 0) && (priority <= 127));
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->priority = priority;
+ }
+ }
+}
+
+void IMuseDigital::setVolume(int soundId, int volume) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setVolume()");
+ debug(5, "IMuseDigital::setVolume(%d, %d)", soundId, volume);
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->vol = volume * 1000;
+ }
+ }
+}
+
+void IMuseDigital::setHookId(int soundId, int hookId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setHookId()");
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->curHookId = hookId;
+ }
+ }
+}
+
+int IMuseDigital::getCurMusicSoundId() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicSoundId()");
+ int soundId = -1;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundId = track->soundId;
+ }
+ }
+
+ return soundId;
+}
+
+char *IMuseDigital::getCurMusicSoundName() {
+ Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicSoundName()");
+ char *soundName = NULL;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ soundName = track->soundName;
+ }
+ }
+
+ return soundName;
+}
+
+void IMuseDigital::setPan(int soundId, int pan) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setPan()");
+ debug(5, "IMuseDigital::setPan(%d, %d)", soundId, pan);
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->pan = pan;
+ }
+ }
+}
+
+void IMuseDigital::selectVolumeGroup(int soundId, int volGroupId) {
+ Common::StackLock lock(_mutex, "IMuseDigital::selectVolumeGroup()");
+ debug(5, "IMuseDigital::setGroupVolume(%d, %d)", soundId, volGroupId);
+ assert((volGroupId >= 1) && (volGroupId <= 4));
+
+ if (volGroupId == 4)
+ volGroupId = 3;
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->volGroupId = volGroupId;
+ }
+ }
+}
+
+void IMuseDigital::setFade(int soundId, int destVolume, int delay60HzTicks) {
+ Common::StackLock lock(_mutex, "IMuseDigital::setFade()");
+ debug(5, "IMuseDigital::setFade(%d, %d, %d)", soundId, destVolume, delay60HzTicks);
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if ((track->soundId == soundId) && track->used && !track->toBeRemoved) {
+ track->volFadeDelay = delay60HzTicks;
+ track->volFadeDest = destVolume * 1000;
+ track->volFadeStep = (track->volFadeDest - track->vol) * 60 * (1000 / _callbackFps) / (1000 * delay60HzTicks);
+ track->volFadeUsed = true;
+ }
+ }
+}
+
+void IMuseDigital::fadeOutMusic(int fadeDelay) {
+ Common::StackLock lock(_mutex, "IMuseDigital::fadeOutMusic()");
+ debug(5, "IMuseDigital::fadeOutMusic");
+
+ for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
+ Track *track = _track[l];
+ if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
+ cloneToFadeOutTrack(track, fadeDelay);
+ track->toBeRemoved = true;
+ }
+ }
+}
+
+IMuseDigital::Track *IMuseDigital::cloneToFadeOutTrack(Track *track, int fadeDelay) {
+ Common::StackLock lock(_mutex, "IMuseDigital::cloneToFadeOutTrack()");
+ assert(track);
+ Track *fadeTrack = 0;
+
+ debug(5, "IMuseDigital::cloneToFadeOutTrack(%d, %d)", track->trackId, fadeDelay);
+
+ if (_track[track->trackId + MAX_DIGITAL_TRACKS]->used) {
+ warning("IMuseDigital::cloneToFadeOutTrack: Not free fade track");
+ return NULL;
+ }
+
+ fadeTrack = _track[track->trackId + MAX_DIGITAL_TRACKS];
+ fadeTrack->pan = track->pan;
+ fadeTrack->vol = track->vol;
+ fadeTrack->volGroupId = track->volGroupId;
+ fadeTrack->priority = track->priority;
+ fadeTrack->soundId = track->soundId;
+ fadeTrack->dataOffset = track->dataOffset;
+ fadeTrack->regionOffset = track->regionOffset;
+ fadeTrack->curRegion = track->curRegion;
+ fadeTrack->curHookId = track->curHookId;
+ fadeTrack->iteration = track->iteration;
+ fadeTrack->mixerFlags = track->mixerFlags;
+ fadeTrack->mod = track->mod;
+ fadeTrack->toBeRemoved = track->toBeRemoved;
+ fadeTrack->readyToRemove = track->readyToRemove;
+ fadeTrack->souStream = track->souStream;
+ fadeTrack->started = track->started;
+ fadeTrack->stream2 = track->stream2;
+ strcpy(fadeTrack->soundName, track->soundName);
+ fadeTrack->soundType = track->soundType;
+ fadeTrack->soundHandle = _sound->cloneSound(track->soundHandle);
+ assert(fadeTrack->soundHandle);
+
+ fadeTrack->volFadeDelay = fadeDelay;
+ fadeTrack->volFadeDest = 0;
+ fadeTrack->volFadeStep = (fadeTrack->volFadeDest - fadeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * fadeDelay);
+ fadeTrack->volFadeUsed = true;
+
+ Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType;
+
+ if (fadeTrack->volGroupId == 1)
+ type = Audio::Mixer::kSpeechSoundType;
+ if (fadeTrack->volGroupId == 2)
+ type = Audio::Mixer::kSFXSoundType;
+ if (fadeTrack->volGroupId == 3)
+ type = Audio::Mixer::kMusicSoundType;
+
+ // setup 1 second stream wrapped buffer
+ int32 streamBufferSize = fadeTrack->iteration;
+ fadeTrack->stream = makeAppendableAudioStream(_sound->getFreq(fadeTrack->soundHandle), fadeTrack->mixerFlags, streamBufferSize);
+ _vm->_mixer->playInputStream(type, &fadeTrack->handle, fadeTrack->stream, -1, fadeTrack->vol / 1000, fadeTrack->pan, false);
+ fadeTrack->started = true;
+ fadeTrack->used = true;
+
+ return fadeTrack;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_internal.h b/engines/scumm/imuse_internal.h
new file mode 100644
index 0000000000..aa9b49cb00
--- /dev/null
+++ b/engines/scumm/imuse_internal.h
@@ -0,0 +1,472 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef DEFINED_IMUSE_INTERNAL
+#define DEFINED_IMUSE_INTERNAL
+
+#include "common/scummsys.h"
+#include "scumm/instrument.h"
+#include "scumm/saveload.h"
+#include "sound/mididrv.h"
+
+class MidiParser;
+class OSystem;
+
+namespace Scumm {
+
+// Unremark this statement to activate some of
+// the most common iMuse diagnostic messages.
+// #define IMUSE_DEBUG
+
+struct ParameterFader;
+struct DeferredCommand;
+struct ImTrigger;
+struct SustainingNotes;
+struct CommandQueue;
+struct IsNoteCmdData;
+class Player;
+struct Part;
+class IMuseInternal;
+
+// Some entities also referenced
+class ScummEngine;
+
+
+
+//////////////////////////////////////////////////
+//
+// Some constants
+//
+//////////////////////////////////////////////////
+
+#define TICKS_PER_BEAT 480
+
+#define TRIGGER_ID 0
+#define COMMAND_ID 1
+
+#define MDPG_TAG "MDpg"
+
+
+////////////////////////////////////////
+//
+// Helper functions
+//
+////////////////////////////////////////
+
+inline int clamp(int val, int min, int max) {
+ if (val < min)
+ return min;
+ if (val > max)
+ return max;
+ return val;
+}
+
+inline int transpose_clamp(int a, int b, int c) {
+ if (b > a)
+ a += (b - a + 11) / 12 * 12;
+ if (c < a)
+ a -= (a - c + 11) / 12 * 12;
+ return a;
+}
+
+
+
+//////////////////////////////////////////////////
+//
+// Entity declarations
+//
+//////////////////////////////////////////////////
+
+struct HookDatas {
+ byte _jump[2];
+ byte _transpose;
+ byte _part_onoff[16];
+ byte _part_volume[16];
+ byte _part_program[16];
+ byte _part_transpose[16];
+
+ int query_param(int param, byte chan);
+ int set(byte cls, byte value, byte chan);
+ HookDatas() { memset(this, 0, sizeof(HookDatas)); }
+};
+
+struct ParameterFader {
+ enum {
+ pfVolume = 1,
+ pfTranspose = 3,
+ pfSpeed = 4
+ };
+
+ int param;
+ int start;
+ int end;
+ uint32 total_time;
+ uint32 current_time;
+
+ ParameterFader() { param = 0; }
+ void init() { param = 0; }
+};
+
+struct DeferredCommand {
+ uint32 time_left;
+ int a, b, c, d, e, f;
+ DeferredCommand() { memset(this, 0, sizeof(DeferredCommand)); }
+};
+
+struct ImTrigger {
+ int sound;
+ byte id;
+ uint16 expire;
+ int command [8];
+ ImTrigger() { memset(this, 0, sizeof(ImTrigger)); }
+};
+
+struct CommandQueue {
+ uint16 array[8];
+ CommandQueue() { memset(this, 0, sizeof(CommandQueue)); }
+};
+
+class Player : public MidiDriver {
+protected:
+ // Moved from IMuseInternal.
+ // This is only used by one player at a time.
+ static uint16 _active_notes[128];
+
+protected:
+ MidiDriver *_midi;
+ MidiParser *_parser;
+ bool _passThrough; // Only respond to EOT, all else direct to MidiDriver
+
+ Part *_parts;
+ bool _active;
+ bool _scanning;
+ int _id;
+ byte _priority;
+ byte _volume;
+ int8 _pan;
+ int8 _transpose;
+ int8 _detune;
+ byte _vol_eff;
+
+ uint _track_index;
+ uint _loop_to_beat;
+ uint _loop_from_beat;
+ uint _loop_counter;
+ uint _loop_to_tick;
+ uint _loop_from_tick;
+ byte _speed;
+ bool _abort;
+
+ // This does not get used by us! It is only
+ // here for save/load purposes, and gets
+ // passed on to the MidiParser during
+ // fixAfterLoad().
+ uint32 _music_tick;
+
+ HookDatas _hook;
+ ParameterFader _parameterFaders[4];
+
+ bool _isMT32;
+ bool _isMIDI;
+
+protected:
+ // Player part
+ void hook_clear();
+ void uninit_parts();
+ byte *parse_midi(byte *s);
+ void part_set_transpose(uint8 chan, byte relative, int8 b);
+ void parse_sysex(byte *p, uint len);
+ void maybe_jump(byte cmd, uint track, uint beat, uint tick);
+ void maybe_set_transpose(byte *data);
+ void maybe_part_onoff(byte *data);
+ void maybe_set_volume(byte *data);
+ void maybe_set_program(byte *data);
+ void maybe_set_transpose_part(byte *data);
+ void turn_off_pedals();
+ int query_part_param(int param, byte chan);
+ void turn_off_parts();
+ void play_active_notes();
+
+ void transitionParameters();
+
+ static void decode_sysex_bytes(const byte *src, byte *dst, int len);
+
+ // Sequencer part
+ int start_seq_sound(int sound, bool reset_vars = true);
+ int query_param(int param);
+
+public:
+ IMuseInternal *_se;
+ uint _vol_chan;
+
+public:
+ Player();
+ virtual ~Player();
+
+ int addParameterFader(int param, int target, int time);
+ void clear();
+ void clearLoop();
+ void fixAfterLoad();
+ Part * getActivePart(uint8 part);
+ uint getBeatIndex();
+ int8 getDetune() const { return _detune; }
+ byte getEffectiveVolume() const { return _vol_eff; }
+ int getID() const { return _id; }
+ MidiDriver *getMidiDriver() const { return _midi; }
+ int getParam(int param, byte chan);
+ int8 getPan() const { return _pan; }
+ Part * getPart(uint8 part);
+ byte getPriority() const { return _priority; }
+ uint getTicksPerBeat() const { return TICKS_PER_BEAT; }
+ int8 getTranspose() const { return _transpose; }
+ byte getVolume() const { return _volume; }
+ bool isActive() const { return _active; }
+ bool isFadingOut() const;
+ bool isMIDI() const { return _isMIDI; }
+ bool isMT32() const { return _isMT32; }
+ bool jump(uint track, uint beat, uint tick);
+ void onTimer();
+ void removePart(Part *part);
+ int scan(uint totrack, uint tobeat, uint totick);
+ void saveLoadWithSerializer(Serializer *ser);
+ int setHook(byte cls, byte value, byte chan) { return _hook.set(cls, value, chan); }
+ void setDetune(int detune);
+ bool setLoop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick);
+ void setPan(int pan);
+ void setPriority(int pri);
+ void setSpeed(byte speed);
+ int setTranspose(byte relative, int b);
+ int setVolume(byte vol);
+ bool startSound(int sound, MidiDriver *midi, bool passThrough);
+ int getMusicTimer() const;
+
+public:
+ // MidiDriver interface
+ int open() { return 0; }
+ void close() { }
+ void send(uint32 b);
+ const char *getErrorName(int error_code) { return "Unknown"; }
+ void sysEx(byte *msg, uint16 length);
+ void metaEvent(byte type, byte *data, uint16 length);
+ void setTimerCallback(void *timer_param, void(*timer_proc)(void *)) { }
+ uint32 getBaseTempo();
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+};
+
+struct Part : public Serializable {
+ IMuseInternal *_se;
+ int _slot;
+ Part *_next, *_prev;
+ MidiChannel *_mc;
+ Player *_player;
+ int16 _pitchbend;
+ byte _pitchbend_factor;
+ int8 _transpose, _transpose_eff;
+ byte _vol, _vol_eff;
+ int8 _detune, _detune_eff;
+ int8 _pan, _pan_eff;
+ bool _on;
+ byte _modwheel;
+ bool _pedal;
+ int8 _pri;
+ byte _pri_eff;
+ byte _chan;
+ byte _effect_level;
+ byte _chorus;
+ byte _percussion;
+ byte _bank;
+
+ // New abstract instrument definition
+ Instrument _instrument;
+ bool _unassigned_instrument; // For diagnostic reporting purposes only
+
+ // MidiChannel interface
+ // (We don't currently derive from MidiChannel,
+ // but if we ever do, this will make it easy.)
+ void noteOff(byte note);
+ void noteOn(byte note, byte velocity);
+ void programChange(byte value);
+ void pitchBend(int16 value);
+ void modulationWheel(byte value);
+ void volume(byte value);
+ void pitchBendFactor(byte value);
+ void sustain(bool value);
+ void effectLevel(byte value);
+ void chorusLevel(byte value);
+ void allNotesOff();
+
+ void set_param(byte param, int value) { }
+ void init();
+ void setup(Player *player);
+ void uninit();
+ void off();
+ void set_instrument(uint b);
+ void set_instrument(byte *data);
+ void load_global_instrument(byte b);
+
+ void set_transpose(int8 transpose);
+ void set_detune(int8 detune);
+ void set_pri(int8 pri);
+ void set_pan(int8 pan);
+
+ void set_onoff(bool on);
+ void fix_after_load();
+
+ void sendAll();
+ void sendPitchBend();
+ bool clearToTransmit();
+
+ Part();
+
+ void saveLoadWithSerializer(Serializer *ser);
+};
+
+// WARNING: This is the internal variant of the IMUSE class.
+// imuse.h contains a public version of the same class.
+// the public version, only contains a set of methods.
+class IMuseInternal {
+ friend class Player;
+ friend struct Part;
+
+protected:
+ bool _native_mt32;
+ bool _enable_gs;
+ bool _sc55;
+ MidiDriver *_midi_adlib;
+ MidiDriver *_midi_native;
+
+ byte **_base_sounds;
+
+protected:
+ bool _paused;
+ bool _initialized;
+
+ int _tempoFactor;
+
+ int _player_limit; // Limits how many simultaneous music tracks are played
+ bool _recycle_players; // Can we stop a player in order to start another one?
+ bool _direct_passthrough; // Pass data direct to MidiDriver (no interactivity)
+
+ uint _queue_end, _queue_pos, _queue_sound;
+ byte _queue_adding;
+
+ byte _queue_marker;
+ byte _queue_cleared;
+ byte _master_volume; // Master volume. 0-255
+ byte _music_volume; // Global music volume. 0-255
+
+ uint16 _trigger_count;
+ ImTrigger _snm_triggers[16]; // Sam & Max triggers
+ uint16 _snm_trigger_index;
+
+ uint16 _channel_volume[8];
+ uint16 _channel_volume_eff[8]; // No Save
+ uint16 _volchan_table[8];
+
+ Player _players[8];
+ Part _parts[32];
+
+ Instrument _global_adlib_instruments[32];
+ CommandQueue _cmd_queue[64];
+ DeferredCommand _deferredCommands[4];
+
+protected:
+ byte *findStartOfSound(int sound);
+ bool isMT32(int sound);
+ bool isMIDI(int sound);
+ int get_queue_sound_status(int sound) const;
+ void handle_marker(uint id, byte data);
+ int get_channel_volume(uint a);
+ void initMidiDriver(MidiDriver *midi);
+ void initGM(MidiDriver *midi);
+ void initMT32(MidiDriver *midi);
+ void init_players();
+ void init_parts();
+ void init_queue();
+
+ void sequencer_timers(MidiDriver *midi);
+
+ MidiDriver *getBestMidiDriver(int sound);
+ Player *allocate_player(byte priority);
+ Part *allocate_part(byte pri, MidiDriver *midi);
+
+ int32 ImSetTrigger(int sound, int id, int a, int b, int c, int d, int e, int f, int g, int h);
+ int32 ImClearTrigger(int sound, int id);
+ int32 ImFireAllTriggers(int sound);
+
+ void addDeferredCommand(int time, int a, int b, int c, int d, int e, int f);
+ void handleDeferredCommands(MidiDriver *midi);
+
+ int enqueue_command(int a, int b, int c, int d, int e, int f, int g);
+ int enqueue_trigger(int sound, int marker);
+ int query_queue(int param);
+ Player *findActivePlayer(int id);
+
+ int get_volchan_entry(uint a);
+ int set_volchan_entry(uint a, uint b);
+ int set_channel_volume(uint chan, uint vol);
+ void update_volumes();
+ void reset_tick();
+
+ int set_volchan(int sound, int volchan);
+
+ void fix_parts_after_load();
+ void fix_players_after_load(ScummEngine *scumm);
+
+ static void midiTimerCallback(void *data);
+
+public:
+ IMuseInternal();
+
+ int initialize(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
+ void reallocateMidiChannels(MidiDriver *midi);
+ void setGlobalAdlibInstrument(byte slot, byte *data);
+ void copyGlobalAdlibInstrument(byte slot, Instrument *dest);
+ bool isNativeMT32() { return _native_mt32; }
+
+ // IMuse interface
+
+ void on_timer(MidiDriver *midi);
+ void pause(bool paused);
+ int terminate1();
+ int terminate2();
+ int save_or_load(Serializer *ser, ScummEngine *scumm);
+ int setMusicVolume(uint vol);
+ int setImuseMasterVolume(uint vol);
+ bool startSound(int sound);
+ int stopSound(int sound);
+ int stopAllSounds();
+ int getSoundStatus(int sound, bool ignoreFadeouts = true) const;
+ int getMusicTimer() const;
+ int32 doCommand (int a, int b, int c, int d, int e, int f, int g, int h);
+ int32 doCommand (int numargs, int args[]);
+ int clear_queue();
+ void setBase(byte **base);
+ uint32 property(int prop, uint32 value);
+
+ static IMuseInternal *create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_player.cpp b/engines/scumm/imuse_player.cpp
new file mode 100644
index 0000000000..06ebd289cd
--- /dev/null
+++ b/engines/scumm/imuse_player.cpp
@@ -0,0 +1,1241 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+
+#include "common/util.h"
+#include "base/engine.h"
+
+#include "scumm/imuse_internal.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+
+#include "sound/midiparser.h"
+
+namespace Scumm {
+
+////////////////////////////////////////
+//
+// Miscellaneous
+//
+////////////////////////////////////////
+
+#define IMUSE_SYSEX_ID 0x7D
+#define YM2612_SYSEX_ID 0x7C
+#define ROLAND_SYSEX_ID 0x41
+#define PERCUSSION_CHANNEL 9
+
+extern MidiParser *MidiParser_createRO();
+extern MidiParser *MidiParser_createEUP();
+
+uint16 Player::_active_notes[128];
+
+
+
+//////////////////////////////////////////////////
+//
+// IMuse Player implementation
+//
+//////////////////////////////////////////////////
+
+Player::Player() :
+ _midi(0),
+ _parser(0),
+ _passThrough(0),
+ _parts(0),
+ _active(false),
+ _scanning(false),
+ _id(0),
+ _priority(0),
+ _volume(0),
+ _pan(0),
+ _transpose(0),
+ _detune(0),
+ _vol_eff(0),
+ _track_index(0),
+ _loop_to_beat(0),
+ _loop_from_beat(0),
+ _loop_counter(0),
+ _loop_to_tick(0),
+ _loop_from_tick(0),
+ _speed(128),
+ _isMT32(false),
+ _isMIDI(false),
+ _se(0),
+ _vol_chan(0){
+}
+
+Player::~Player() {
+ if (_parser) {
+ delete _parser;
+ _parser = 0;
+ }
+}
+
+bool Player::startSound(int sound, MidiDriver *midi, bool passThrough) {
+ void *ptr;
+ int i;
+
+ // Not sure what the old code was doing,
+ // but we'll go ahead and do a similar check.
+ ptr = _se->findStartOfSound(sound);
+ if (!ptr) {
+ error("Player::startSound(): Couldn't find start of sound %d!", sound);
+ return false;
+ }
+
+ _isMT32 = _se->isMT32(sound);
+ _isMIDI = _se->isMIDI(sound);
+
+ _parts = NULL;
+ _active = true;
+ _midi = midi;
+ _id = sound;
+ _priority = 0x80;
+ _volume = 0x7F;
+ _vol_chan = 0xFFFF;
+ _vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;
+ _pan = 0;
+ _transpose = 0;
+ _detune = 0;
+ _passThrough = passThrough;
+
+ for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i)
+ _parameterFaders[i].init();
+ hook_clear();
+
+ if (start_seq_sound(sound) != 0) {
+ _active = false;
+ _midi = NULL;
+ return false;
+ }
+
+#ifdef IMUSE_DEBUG
+ debug(0, "Starting music %d", sound);
+#endif
+ return true;
+}
+
+int Player::getMusicTimer() const {
+ return _parser ? (_parser->getTick() * 2 / _parser->getPPQN()) : 0;
+}
+
+bool Player::isFadingOut() const {
+ int i;
+ for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) {
+ if (_parameterFaders[i].param == ParameterFader::pfVolume &&
+ _parameterFaders[i].end == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Player::clear() {
+ if (!_active)
+ return;
+
+#ifdef IMUSE_DEBUG
+ debug(0, "Stopping music %d", _id);
+#endif
+
+ if (_parser) {
+ _parser->unloadMusic();
+ delete _parser;
+ _parser = 0;
+ }
+ uninit_parts();
+ _se->ImFireAllTriggers(_id);
+ _active = false;
+ _midi = NULL;
+ _id = 0;
+}
+
+void Player::hook_clear() {
+ memset(&_hook, 0, sizeof(_hook));
+}
+
+int Player::start_seq_sound(int sound, bool reset_vars) {
+ byte *ptr;
+
+ if (reset_vars) {
+ _loop_to_beat = 1;
+ _loop_from_beat = 1;
+ _track_index = 0;
+ _loop_counter = 0;
+ _loop_to_tick = 0;
+ _loop_from_tick = 0;
+ }
+
+ ptr = _se->findStartOfSound(sound);
+ if (ptr == NULL)
+ return -1;
+ if (_parser)
+ delete _parser;
+
+ if (!memcmp(ptr, "RO", 2)) {
+ // Old style 'RO' resource
+ _parser = MidiParser_createRO();
+ } else if (!memcmp(ptr, "SO", 2)) {
+ // Euphony (FM-TOWNS) resource
+ _parser = MidiParser_createEUP();
+ } else if (!memcmp(ptr, "FORM", 4)) {
+ // Humongous Games XMIDI resource
+ _parser = MidiParser::createParser_XMIDI();
+ } else {
+ // SCUMM SMF resource
+ _parser = MidiParser::createParser_SMF();
+ }
+
+ _parser->setMidiDriver(this);
+ _parser->property(MidiParser::mpSmartJump, 1);
+ _parser->loadMusic(ptr, 0);
+ _parser->setTrack(_track_index);
+ setSpeed(reset_vars ? 128 : _speed);
+
+ return 0;
+}
+
+void Player::uninit_parts() {
+ if (_parts && _parts->_player != this)
+ error("asd");
+ while (_parts)
+ _parts->uninit();
+
+ // In case another player is waiting to allocate parts
+ if (_midi)
+ _se->reallocateMidiChannels(_midi);
+}
+
+void Player::setSpeed(byte speed) {
+ _speed = speed;
+ if (_parser)
+ _parser->setTimerRate(((_midi->getBaseTempo() * speed) >> 7) * _se->_tempoFactor / 100);
+}
+
+void Player::send(uint32 b) {
+ if (_passThrough) {
+ _midi->send(b);
+ return;
+ }
+
+ byte cmd = (byte)(b & 0xF0);
+ byte chan = (byte)(b & 0x0F);
+ byte param1 = (byte)((b >> 8) & 0xFF);
+ byte param2 = (byte)((b >> 16) & 0xFF);
+ Part *part;
+
+ switch (cmd >> 4) {
+ case 0x8: // Key Off
+ if (!_scanning) {
+ if ((part = getPart(chan)) != 0)
+ part->noteOff(param1);
+ } else {
+ _active_notes[param1] &= ~(1 << chan);
+ }
+ break;
+
+ case 0x9: // Key On
+ if (!_scanning) {
+ if (_isMT32 && !_se->isNativeMT32())
+ param2 = (((param2 * 3) >> 2) + 32) & 0x7F;
+ if ((part = getPart(chan)) != 0)
+ part->noteOn(param1, param2);
+ } else {
+ _active_notes[param1] |= (1 << chan);
+ }
+ break;
+
+ case 0xB: // Control Change
+ part = (param1 == 123 ? getActivePart(chan) : getPart(chan));
+ if (!part)
+ break;
+
+ switch (param1) {
+ case 0: // Bank select. Not supported
+ break;
+ case 1: // Modulation Wheel
+ part->modulationWheel(param2);
+ break;
+ case 7: // Volume
+ part->volume(param2);
+ break;
+ case 10: // Pan Position
+ part->set_pan(param2 - 0x40);
+ break;
+ case 16: // Pitchbend Factor(non-standard)
+ part->pitchBendFactor(param2);
+ break;
+ case 17: // GP Slider 2
+ part->set_detune(param2 - 0x40);
+ break;
+ case 18: // GP Slider 3
+ part->set_pri(param2 - 0x40);
+ _se->reallocateMidiChannels(_midi);
+ break;
+ case 64: // Sustain Pedal
+ part->sustain(param2 != 0);
+ break;
+ case 91: // Effects Level
+ part->effectLevel(param2);
+ break;
+ case 93: // Chorus Level
+ part->chorusLevel(param2);
+ break;
+ case 116: // XMIDI For Loop. Not supported
+ // Used in the ending sequence of puttputt
+ break;
+ case 117: // XMIDI Next/Break. Not supported
+ // Used in the ending sequence of puttputt
+ break;
+ case 123: // All Notes Off
+ part->allNotesOff();
+ break;
+ default:
+ error("Player::send(): Invalid control change %d", param1);
+ }
+ break;
+
+ case 0xC: // Program Change
+ part = getPart(chan);
+ if (part) {
+ if (_isMIDI) {
+ if (param1 < 128)
+ part->programChange(param1);
+ } else {
+ if (param1 < 32)
+ part->load_global_instrument(param1);
+ }
+ }
+ break;
+
+ case 0xE: // Pitch Bend
+ part = getPart(chan);
+ if (part)
+ part->pitchBend(((param2 << 7) | param1) - 0x2000);
+ break;
+
+ case 0xA: // Aftertouch
+ case 0xD: // Channel Pressure
+ case 0xF: // Sequence Controls
+ break;
+
+ default:
+ if (!_scanning) {
+ error("Player::send(): Invalid command %d", cmd);
+ clear();
+ }
+ }
+ return;
+}
+
+void Player::sysEx(byte *p, uint16 len) {
+ byte code;
+ byte a;
+ uint b;
+ byte buf[128];
+ Part *part;
+
+ if (_passThrough) {
+ _midi->sysEx(p, len);
+ return;
+ }
+
+ // Check SysEx manufacturer.
+ a = *p++;
+ --len;
+ if (a != IMUSE_SYSEX_ID) {
+ if (a == ROLAND_SYSEX_ID) {
+ // Roland custom instrument definition.
+ part = getPart(p[0] & 0x0F);
+ if (part) {
+ part->_instrument.roland(p - 1);
+ if (part->clearToTransmit())
+ part->_instrument.send(part->_mc);
+ }
+ } else if (a == YM2612_SYSEX_ID) {
+ // FM-TOWNS custom instrument definition
+ _midi->sysEx_customInstrument(p[0], 'EUP ', p + 1);
+ } else {
+ error("Unknown SysEx manufacturer 0x%02X", (int)a);
+ }
+ return;
+ }
+ --len;
+
+ // Too big?
+ if (len >= sizeof(buf) * 2)
+ return;
+
+#ifdef IMUSE_DEBUG
+ if (!_scanning) {
+ for (a = 0; a < len + 1 && a < 19; ++a) {
+ sprintf((char *)&buf[a*3], " %02X", p[a]);
+ } // next for
+ if (a < len + 1) {
+ buf[a*3] = buf[a*3+1] = buf[a*3+2] = '.';
+ ++a;
+ } // end if
+ buf[a*3] = '\0';
+ debug(0, "[%02d] SysEx:%s", _id, buf);
+ }
+#endif
+
+ switch (code = *p++) {
+ case 0:
+ if (g_scumm->_gameId != GID_SAMNMAX) {
+ // There are 17 bytes of useful information beyond
+ // what we've read so far. All we know about them is
+ // as follows:
+ // BYTE 00: Channel #
+ // BYTE 02: BIT 01(0x01): Part on?(1 = yes)
+ // BYTE 04: Priority adjustment [guessing]
+ // BYTE 05: Volume(upper 4 bits) [guessing]
+ // BYTE 06: Volume(lower 4 bits) [guessing]
+ // BYTE 09: BIT 04(0x08): Percussion?(1 = yes)
+ // BYTE 15: Program(upper 4 bits)
+ // BYTE 16: Program(lower 4 bits)
+ part = getPart(p[0] & 0x0F);
+ if (part) {
+ part->set_onoff(p[2] & 0x01);
+ part->set_pri(p[4]);
+ part->volume((p[5] & 0x0F) << 4 |(p[6] & 0x0F));
+ part->_percussion = _isMIDI ? ((p[9] & 0x08) > 0) : false;
+ if (part->_percussion) {
+ if (part->_mc) {
+ part->off();
+ _se->reallocateMidiChannels(_midi);
+ }
+ } else {
+ // Even in cases where a program does not seem to be specified,
+ // i.e. bytes 15 and 16 are 0, we send a program change because
+ // 0 is a valid program number. MI2 tests show that in such
+ // cases, a regular program change message always seems to follow
+ // anyway.
+ if (_isMIDI)
+ part->_instrument.program((p[15] & 0x0F) << 4 |(p[16] & 0x0F), _isMT32);
+ part->sendAll();
+ }
+ }
+ } else {
+ // Sam & Max: Trigger Event
+ // Triggers are set by doCommand(ImSetTrigger).
+ // When a SysEx marker is encountered whose sound
+ // ID and marker ID match what was set by ImSetTrigger,
+ // something magical is supposed to happen....
+ for (a = 0; a < ARRAYSIZE(_se->_snm_triggers); ++a) {
+ if (_se->_snm_triggers[a].sound == _id &&
+ _se->_snm_triggers[a].id == *p)
+ {
+ _se->_snm_triggers[a].sound = _se->_snm_triggers[a].id = 0;
+ _se->doCommand(8, _se->_snm_triggers[a].command);
+ break;
+ }
+ }
+ } // end if
+ break;
+
+ case 1:
+ // This SysEx is used in Sam & Max for maybe_jump.
+ if (_scanning)
+ break;
+ maybe_jump(p[0], p[1] - 1, (READ_BE_UINT16(p + 2) - 1) * 4 + p[4], ((p[5] * TICKS_PER_BEAT) >> 2) + p[6]);
+ break;
+
+ case 2: // Start of song. Ignore for now.
+ break;
+
+ case 16: // Adlib instrument definition(Part)
+ a = *p++ & 0x0F;
+ ++p; // Skip hardware type
+ part = getPart(a);
+ if (part) {
+ if (len == 63) {
+ decode_sysex_bytes(p, buf, len - 3);
+ part->set_instrument((byte *)buf);
+ } else {
+ // SPK tracks have len == 49 here, and are not supported
+ part->programChange(254); // Must be invalid, but not 255 (which is reserved)
+ }
+ }
+ break;
+
+ case 17: // Adlib instrument definition(Global)
+ p += 2; // Skip hardware type and... whatever came right before it
+ a = *p++;
+ decode_sysex_bytes(p, buf, len - 4);
+ _se->setGlobalAdlibInstrument(a, buf);
+ break;
+
+ case 33: // Parameter adjust
+ a = *p++ & 0x0F;
+ ++p; // Skip hardware type
+ decode_sysex_bytes(p, buf, len - 3);
+ part = getPart(a);
+ if (part)
+ part->set_param(READ_BE_UINT16(buf), READ_BE_UINT16(buf + 2));
+ break;
+
+ case 48: // Hook - jump
+ if (_scanning)
+ break;
+ decode_sysex_bytes(p + 1, buf, len - 2);
+ maybe_jump(buf[0], READ_BE_UINT16(buf + 1), READ_BE_UINT16(buf + 3), READ_BE_UINT16(buf + 5));
+ break;
+
+ case 49: // Hook - global transpose
+ decode_sysex_bytes(p + 1, buf, len - 2);
+ maybe_set_transpose(buf);
+ break;
+
+ case 50: // Hook - part on/off
+ buf[0] = *p++ & 0x0F;
+ decode_sysex_bytes(p, buf + 1, len - 2);
+ maybe_part_onoff(buf);
+ break;
+
+ case 51: // Hook - set volume
+ buf[0] = *p++ & 0x0F;
+ decode_sysex_bytes(p, buf + 1, len - 2);
+ maybe_set_volume(buf);
+ break;
+
+ case 52: // Hook - set program
+ buf[0] = *p++ & 0x0F;
+ decode_sysex_bytes(p, buf + 1, len - 2);
+ maybe_set_program(buf);
+ break;
+
+ case 53: // Hook - set transpose
+ buf[0] = *p++ & 0x0F;
+ decode_sysex_bytes(p, buf + 1, len - 2);
+ maybe_set_transpose_part(buf);
+ break;
+
+ case 64: // Marker
+ p++;
+ len -= 2;
+ while (len--) {
+ _se->handle_marker(_id, *p++);
+ }
+ break;
+
+ case 80: // Loop
+ decode_sysex_bytes(p + 1, buf, len - 2);
+ setLoop(READ_BE_UINT16(buf), READ_BE_UINT16(buf + 2),
+ READ_BE_UINT16(buf + 4), READ_BE_UINT16(buf + 6),
+ READ_BE_UINT16(buf + 8));
+ break;
+
+ case 81: // End loop
+ clearLoop();
+ break;
+
+ case 96: // Set instrument
+ part = getPart(p[0] & 0x0F);
+ b = (p[1] & 0x0F) << 12 |(p[2] & 0x0F) << 8 |(p[4] & 0x0F) << 4 |(p[4] & 0x0F);
+ if (part)
+ part->set_instrument(b);
+ break;
+
+ default:
+ error("Unknown SysEx command %d", (int)code);
+ }
+}
+
+void Player::decode_sysex_bytes(const byte *src, byte *dst, int len) {
+ while (len >= 0) {
+ *dst++ = ((src[0] << 4)&0xFF) | (src[1] & 0xF);
+ src += 2;
+ len -= 2;
+ }
+}
+
+void Player::maybe_jump(byte cmd, uint track, uint beat, uint tick) {
+ // Is this the hook I'm waiting for?
+ if (cmd && _hook._jump[0] != cmd)
+ return;
+
+ // Reset hook?
+ if (cmd != 0 && cmd < 0x80) {
+ _hook._jump[0] = _hook._jump[1];
+ _hook._jump[1] = 0;
+ }
+
+ jump(track, beat, tick);
+}
+
+void Player::maybe_set_transpose(byte *data) {
+ byte cmd;
+
+ cmd = data[0];
+
+ // Is this the hook I'm waiting for?
+ if (cmd && _hook._transpose != cmd)
+ return;
+
+ // Reset hook?
+ if (cmd != 0 && cmd < 0x80)
+ _hook._transpose = 0;
+
+ setTranspose(data[1], (int8)data[2]);
+}
+
+void Player::maybe_part_onoff(byte *data) {
+ byte cmd, *p;
+ uint chan;
+ Part *part;
+
+ cmd = data[1];
+ chan = data[0];
+
+ p = &_hook._part_onoff[chan];
+
+ // Is this the hook I'm waiting for?
+ if (cmd && *p != cmd)
+ return;
+
+ if (cmd != 0 && cmd < 0x80)
+ *p = 0;
+
+ part = getPart(chan);
+ if (part)
+ part->set_onoff(data[2] != 0);
+}
+
+void Player::maybe_set_volume(byte *data) {
+ byte cmd;
+ byte *p;
+ uint chan;
+ Part *part;
+
+ cmd = data[1];
+ chan = data[0];
+
+ p = &_hook._part_volume[chan];
+
+ // Is this the hook I'm waiting for?
+ if (cmd && *p != cmd)
+ return;
+
+ // Reset hook?
+ if (cmd != 0 && cmd < 0x80)
+ *p = 0;
+
+ part = getPart(chan);
+ if (part)
+ part->volume(data[2]);
+}
+
+void Player::maybe_set_program(byte *data) {
+ byte cmd;
+ byte *p;
+ uint chan;
+ Part *part;
+
+ cmd = data[1];
+ chan = data[0];
+
+ // Is this the hook I'm waiting for?
+ p = &_hook._part_program[chan];
+
+ if (cmd && *p != cmd)
+ return;
+
+ if (cmd != 0 && cmd < 0x80)
+ *p = 0;
+
+ part = getPart(chan);
+ if (part)
+ part->programChange(data[2]);
+}
+
+void Player::maybe_set_transpose_part(byte *data) {
+ byte cmd;
+ byte *p;
+ uint chan;
+
+ cmd = data[1];
+ chan = data[0];
+
+ // Is this the hook I'm waiting for?
+ p = &_hook._part_transpose[chan];
+
+ if (cmd && *p != cmd)
+ return;
+
+ // Reset hook?
+ if (cmd != 0 && cmd < 0x80)
+ *p = 0;
+
+ part_set_transpose(chan, data[2], (int8)data[3]);
+}
+
+int Player::setTranspose(byte relative, int b) {
+ Part *part;
+
+ if (b > 24 || b < -24 || relative > 1)
+ return -1;
+ if (relative)
+ b = transpose_clamp(_transpose + b, -24, 24);
+
+ _transpose = b;
+
+ for (part = _parts; part; part = part->_next) {
+ part->set_transpose(part->_transpose);
+ }
+
+ return 0;
+}
+
+void Player::part_set_transpose(uint8 chan, byte relative, int8 b) {
+ Part *part;
+
+ if (b > 24 || b < -24)
+ return;
+
+ part = getPart(chan);
+ if (!part)
+ return;
+ if (relative)
+ b = transpose_clamp(b + part->_transpose, -7, 7);
+ part->set_transpose(b);
+}
+
+bool Player::jump(uint track, uint beat, uint tick) {
+ if (!_parser)
+ return false;
+ if (_parser->setTrack(track))
+ _track_index = track;
+ if (!_parser->jumpToTick((beat - 1) * TICKS_PER_BEAT + tick))
+ return false;
+ turn_off_pedals();
+ return true;
+}
+
+bool Player::setLoop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick) {
+ if (tobeat + 1 >= frombeat)
+ return false;
+
+ if (tobeat == 0)
+ tobeat = 1;
+
+ _loop_counter = 0; // Because of possible interrupts
+ _loop_to_beat = tobeat;
+ _loop_to_tick = totick;
+ _loop_from_beat = frombeat;
+ _loop_from_tick = fromtick;
+ _loop_counter = count;
+
+ return true;
+}
+
+void Player::clearLoop() {
+ _loop_counter = 0;
+}
+
+void Player::turn_off_pedals() {
+ Part *part;
+
+ for (part = _parts; part; part = part->_next) {
+ if (part->_pedal)
+ part->sustain(false);
+ }
+}
+
+Part *Player::getActivePart(uint8 chan) {
+ Part *part = _parts;
+ while (part) {
+ if (part->_chan == chan)
+ return part;
+ part = part->_next;
+ }
+ return 0;
+}
+
+Part *Player::getPart(uint8 chan) {
+ Part *part = getActivePart(chan);
+ if (part)
+ return part;
+
+ part = _se->allocate_part(_priority, _midi);
+ if (!part) {
+ debug(1, "No parts available");
+ return NULL;
+ }
+
+ // Insert part into front of parts list
+ part->_prev = NULL;
+ part->_next = _parts;
+ if (_parts)
+ _parts->_prev = part;
+ _parts = part;
+
+
+ part->_chan = chan;
+ part->setup(this);
+
+ return part;
+}
+
+void Player::setPriority(int pri) {
+ Part *part;
+
+ _priority = pri;
+ for (part = _parts; part; part = part->_next) {
+ part->set_pri(part->_pri);
+ }
+ _se->reallocateMidiChannels(_midi);
+}
+
+void Player::setPan(int pan) {
+ Part *part;
+
+ _pan = pan;
+ for (part = _parts; part; part = part->_next) {
+ part->set_pan(part->_pan);
+ }
+}
+
+void Player::setDetune(int detune) {
+ Part *part;
+
+ _detune = detune;
+ for (part = _parts; part; part = part->_next) {
+ part->set_detune(part->_detune);
+ }
+}
+
+int Player::scan(uint totrack, uint tobeat, uint totick) {
+ if (!_active || !_parser)
+ return -1;
+
+ if (tobeat == 0)
+ tobeat++;
+
+ turn_off_parts();
+ memset(_active_notes, 0, sizeof(_active_notes));
+ _scanning = true;
+
+ // If the scan involves a track switch, scan to the end of
+ // the current track so that our state when starting the
+ // new track is fully up to date.
+ if (totrack != _track_index)
+ _parser->jumpToTick((uint32)-1, true);
+ _parser->setTrack(totrack);
+ if (!_parser->jumpToTick((tobeat - 1) * TICKS_PER_BEAT + totick, true)) {
+ _scanning = false;
+ return -1;
+ }
+
+ _scanning = false;
+ _se->reallocateMidiChannels(_midi);
+ play_active_notes();
+
+ if (_track_index != totrack) {
+ _track_index = totrack;
+ _loop_counter = 0;
+ }
+ return 0;
+}
+
+void Player::turn_off_parts() {
+ Part *part;
+
+ for (part = _parts; part; part = part->_next)
+ part->off();
+ _se->reallocateMidiChannels(_midi);
+}
+
+void Player::play_active_notes() {
+ int i, j;
+ uint mask;
+ Part *part;
+
+ for (i = 0; i < 16; ++i) {
+ part = getPart(i);
+ if (part) {
+ mask = 1 << i;
+ for (j = 0; j < 128; ++j) {
+ if (_active_notes[j] & mask)
+ part->noteOn(j, 80);
+ }
+ }
+ }
+}
+
+int Player::setVolume(byte vol) {
+ Part *part;
+
+ if (vol > 127)
+ return -1;
+
+ _volume = vol;
+ _vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7;
+
+ for (part = _parts; part; part = part->_next) {
+ part->volume(part->_vol);
+ }
+
+ return 0;
+}
+
+int Player::getParam(int param, byte chan) {
+ switch (param) {
+ case 0:
+ return (byte)_priority;
+ case 1:
+ return (byte)_volume;
+ case 2:
+ return (byte)_pan;
+ case 3:
+ return (byte)_transpose;
+ case 4:
+ return (byte)_detune;
+ case 5:
+ return _speed;
+ case 6:
+ return _track_index;
+ case 7:
+ return getBeatIndex();
+ case 8:
+ return (_parser ? _parser->getTick() % TICKS_PER_BEAT : 0); // _tick_index;
+ case 9:
+ return _loop_counter;
+ case 10:
+ return _loop_to_beat;
+ case 11:
+ return _loop_to_tick;
+ case 12:
+ return _loop_from_beat;
+ case 13:
+ return _loop_from_tick;
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ return query_part_param(param, chan);
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ return _hook.query_param(param, chan);
+ default:
+ return -1;
+ }
+}
+
+int Player::query_part_param(int param, byte chan) {
+ Part *part;
+
+ part = _parts;
+ while (part) {
+ if (part->_chan == chan) {
+ switch (param) {
+ case 14:
+ return part->_on;
+ case 15:
+ return part->_vol;
+ case 16:
+// FIXME: Need to know where this occurs...
+error("Trying to cast instrument (%d, %d) -- please tell Fingolfin\n", param, chan);
+// In old versions of the code, this used to return part->_program.
+// This was changed in revision 2.29 of imuse.cpp (where this code used
+// to reside).
+// return (int)part->_instrument;
+ case 17:
+ return part->_transpose;
+ default:
+ return -1;
+ }
+ }
+ part = part->_next;
+ }
+ return 129;
+}
+
+void Player::onTimer() {
+ // First handle any parameter transitions
+ // that are occuring.
+ transitionParameters();
+
+ // Since the volume parameter can cause
+ // the player to be deactivated, check
+ // to make sure we're still active.
+ if (!_active || !_parser)
+ return;
+
+ uint32 target_tick = _parser->getTick();
+ uint beat_index = target_tick / TICKS_PER_BEAT + 1;
+ uint tick_index = target_tick % TICKS_PER_BEAT;
+
+ if (_loop_counter &&(beat_index > _loop_from_beat ||
+ (beat_index == _loop_from_beat && tick_index >= _loop_from_tick)))
+ {
+ _loop_counter--;
+ jump(_track_index, _loop_to_beat, _loop_to_tick);
+ }
+ _parser->onTimer();
+}
+
+// "time" is referenced as hundredths of a second.
+// IS THAT CORRECT??
+// We convert it to microseconds before proceeding
+int Player::addParameterFader(int param, int target, int time) {
+ int start;
+
+ switch (param) {
+ case ParameterFader::pfVolume:
+ // HACK: If volume is set to 0 with 0 time,
+ // set it so immediately but DON'T clear
+ // the player. This fixes a problem with
+ // music being cleared inappropriately
+ // in S&M when playing with the Dinosaur.
+ if (!target && !time) {
+ setVolume(0);
+ return 0;
+ }
+
+ // Volume fades are handled differently.
+ start = _volume;
+ break;
+
+ case ParameterFader::pfTranspose:
+ // FIXME: Is this transpose? And what's the scale?
+ // It's set to fade to -2400 in the tunnel of love.
+// debug(0, "parameterTransition(3) outside Tunnel of Love?");
+ start = _transpose;
+// target /= 200;
+ break;
+
+ case ParameterFader::pfSpeed: // impSpeed
+ // FIXME: Is the speed from 0-100?
+ // Right now I convert it to 0-128.
+ start = _speed;
+// target = target * 128 / 100;
+ break;
+
+ case 127:
+ { // FIXME? I *think* this clears all parameter faders.
+ ParameterFader *ptr = &_parameterFaders[0];
+ int i;
+ for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr)
+ ptr->param = 0;
+ return 0;
+ }
+ break;
+
+ default:
+ debug(0, "Player::addParameterFader (%d, %d, %d): Unknown parameter", param, target, time);
+ return 0; // Should be -1, but we'll let the script think it worked.
+ }
+
+ ParameterFader *ptr = &_parameterFaders[0];
+ ParameterFader *best = 0;
+ int i;
+ for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) {
+ if (ptr->param == param) {
+ best = ptr;
+ start = ptr->end;
+ break;
+ } else if (!ptr->param) {
+ best = ptr;
+ }
+ }
+
+ if (best) {
+ best->param = param;
+ best->start = start;
+ best->end = target;
+ if (!time)
+ best->total_time = 1;
+ else
+ best->total_time = (uint32)time * 10000;
+ best->current_time = 0;
+ } else {
+ debug(0, "IMuse Player %d: Out of parameter faders", _id);
+ return -1;
+ }
+
+ return 0;
+}
+
+void Player::transitionParameters() {
+ uint32 advance = _midi->getBaseTempo();
+ int value;
+
+ ParameterFader *ptr = &_parameterFaders[0];
+ int i;
+ for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) {
+ if (!ptr->param)
+ continue;
+
+ ptr->current_time += advance;
+ if (ptr->current_time > ptr->total_time)
+ ptr->current_time = ptr->total_time;
+ value = (int32)ptr->start + (int32)(ptr->end - ptr->start) * (int32)ptr->current_time / (int32)ptr->total_time;
+
+ switch (ptr->param) {
+ case ParameterFader::pfVolume:
+ // Volume.
+ if (!value && !ptr->end) {
+ clear();
+ return;
+ }
+ setVolume((byte)value);
+ break;
+
+ case ParameterFader::pfTranspose:
+ // FIXME: Is this really transpose?
+ setTranspose(0, value / 100);
+ setDetune(value % 100);
+ break;
+
+ case ParameterFader::pfSpeed: // impSpeed:
+ // Speed.
+ setSpeed((byte)value);
+ break;
+
+ default:
+ ptr->param = 0;
+ }
+
+ if (ptr->current_time >= ptr->total_time)
+ ptr->param = 0;
+ }
+}
+
+uint Player::getBeatIndex() {
+ return (_parser ? (_parser->getTick() / TICKS_PER_BEAT + 1) : 0);
+}
+
+void Player::removePart(Part *part) {
+ // Unlink
+ if (part->_next)
+ part->_next->_prev = part->_prev;
+ if (part->_prev)
+ part->_prev->_next = part->_next;
+ else
+ _parts = part->_next;
+ part->_next = part->_prev = 0;
+}
+
+void Player::fixAfterLoad() {
+ _midi = _se->getBestMidiDriver(_id);
+ if (!_midi) {
+ clear();
+ } else {
+ start_seq_sound(_id, false);
+ setSpeed(_speed);
+ if (_parser)
+ _parser->jumpToTick(_music_tick); // start_seq_sound already switched tracks
+ _isMT32 = _se->isMT32(_id);
+ _isMIDI = _se->isMIDI(_id);
+ }
+}
+
+uint32 Player::getBaseTempo() {
+ return (_midi ? _midi->getBaseTempo() : 0);
+}
+
+void Player::metaEvent(byte type, byte *msg, uint16 len) {
+ if (type == 0x2F)
+ clear();
+}
+
+
+
+////////////////////////////////////////
+//
+// Player save/load functions
+//
+////////////////////////////////////////
+
+void Player::saveLoadWithSerializer(Serializer *ser) {
+ static const SaveLoadEntry playerEntries[] = {
+ MKLINE(Player, _active, sleByte, VER(8)),
+ MKLINE(Player, _id, sleUint16, VER(8)),
+ MKLINE(Player, _priority, sleByte, VER(8)),
+ MKLINE(Player, _volume, sleByte, VER(8)),
+ MKLINE(Player, _pan, sleInt8, VER(8)),
+ MKLINE(Player, _transpose, sleByte, VER(8)),
+ MKLINE(Player, _detune, sleInt8, VER(8)),
+ MKLINE(Player, _vol_chan, sleUint16, VER(8)),
+ MKLINE(Player, _vol_eff, sleByte, VER(8)),
+ MKLINE(Player, _speed, sleByte, VER(8)),
+ MK_OBSOLETE(Player, _song_index, sleUint16, VER(8), VER(19)),
+ MKLINE(Player, _track_index, sleUint16, VER(8)),
+ MK_OBSOLETE(Player, _timer_counter, sleUint16, VER(8), VER(17)),
+ MKLINE(Player, _loop_to_beat, sleUint16, VER(8)),
+ MKLINE(Player, _loop_from_beat, sleUint16, VER(8)),
+ MKLINE(Player, _loop_counter, sleUint16, VER(8)),
+ MKLINE(Player, _loop_to_tick, sleUint16, VER(8)),
+ MKLINE(Player, _loop_from_tick, sleUint16, VER(8)),
+ MK_OBSOLETE(Player, _tempo, sleUint32, VER(8), VER(19)),
+ MK_OBSOLETE(Player, _cur_pos, sleUint32, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _next_pos, sleUint32, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _song_offset, sleUint32, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _tick_index, sleUint16, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _beat_index, sleUint16, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _ticks_per_beat, sleUint16, VER(8), VER(17)),
+ MKLINE(Player, _music_tick, sleUint32, VER(19)),
+ MKLINE(Player, _hook._jump[0], sleByte, VER(8)),
+ MKLINE(Player, _hook._transpose, sleByte, VER(8)),
+ MKARRAY(Player, _hook._part_onoff[0], sleByte, 16, VER(8)),
+ MKARRAY(Player, _hook._part_volume[0], sleByte, 16, VER(8)),
+ MKARRAY(Player, _hook._part_program[0], sleByte, 16, VER(8)),
+ MKARRAY(Player, _hook._part_transpose[0], sleByte, 16, VER(8)),
+ MKEND()
+ };
+
+ const SaveLoadEntry parameterFaderEntries[] = {
+ MKLINE(ParameterFader, param, sleInt16, VER(17)),
+ MKLINE(ParameterFader, start, sleInt16, VER(17)),
+ MKLINE(ParameterFader, end, sleInt16, VER(17)),
+ MKLINE(ParameterFader, total_time, sleUint32, VER(17)),
+ MKLINE(ParameterFader, current_time, sleUint32, VER(17)),
+ MKEND()
+ };
+
+ if (!ser->isSaving() && _parser) {
+ delete _parser;
+ _parser = 0;
+ }
+ _music_tick = _parser ? _parser->getTick() : 0;
+
+ int num;
+ if (ser->isSaving()) {
+ num = (_parts ? (_parts - _se->_parts + 1) : 0);
+ ser->saveUint16(num);
+ } else {
+ num = ser->loadUint16();
+ _parts = (num ? &_se->_parts[num - 1] : 0);
+ }
+ ser->saveLoadEntries(this, playerEntries);
+ ser->saveLoadArrayOf(_parameterFaders, ARRAYSIZE(_parameterFaders),
+ sizeof(ParameterFader), parameterFaderEntries);
+ return;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
new file mode 100644
index 0000000000..ad890604c4
--- /dev/null
+++ b/engines/scumm/input.cpp
@@ -0,0 +1,467 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+#include "common/system.h"
+
+#include "gui/message.h"
+#include "gui/newgui.h"
+
+#include "scumm/debugger.h"
+#include "scumm/dialogs.h"
+#include "scumm/insane/insane.h"
+#include "scumm/imuse.h"
+#ifndef DISABLE_HE
+#include "scumm/intern_he.h"
+#endif
+#include "scumm/logic_he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+
+
+#ifdef _WIN32_WCE
+#define KEY_ALL_SKIP 3457
+#endif
+
+namespace Scumm {
+
+enum MouseButtonStatus {
+ msDown = 1,
+ msClicked = 2
+};
+
+void ScummEngine::parseEvents() {
+ OSystem::Event event;
+
+ while (_system->pollEvent(event)) {
+
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.keycode >= '0' && event.kbd.keycode <= '9'
+ && (event.kbd.flags == OSystem::KBD_ALT ||
+ event.kbd.flags == OSystem::KBD_CTRL)) {
+ _saveLoadSlot = event.kbd.keycode - '0';
+
+ // don't overwrite autosave (slot 0)
+ if (_saveLoadSlot == 0)
+ _saveLoadSlot = 10;
+
+ sprintf(_saveLoadName, "Quicksave %d", _saveLoadSlot);
+ _saveLoadFlag = (event.kbd.flags == OSystem::KBD_ALT) ? 1 : 2;
+ _saveTemporaryState = false;
+ } else if (event.kbd.flags == OSystem::KBD_CTRL) {
+ if (event.kbd.keycode == 'f')
+ _fastMode ^= 1;
+ else if (event.kbd.keycode == 'g')
+ _fastMode ^= 2;
+ else if (event.kbd.keycode == 'd')
+ _debugger->attach();
+ else if (event.kbd.keycode == 's')
+ res.resourceStats();
+ else
+ _keyPressed = event.kbd.ascii; // Normal key press, pass on to the game.
+ } else if (event.kbd.flags & OSystem::KBD_ALT) {
+ // The result must be 273 for Alt-W
+ // because that's what MI2 looks for in
+ // its "instant win" cheat.
+ _keyPressed = event.kbd.keycode + 154;
+ } else if (event.kbd.ascii == 315 && (_gameId == GID_CMI && !(_features & GF_DEMO))) {
+ // FIXME: support in-game menu screen. For now, this remaps F1 to F5 in COMI
+ _keyPressed = 319;
+ } else if (event.kbd.ascii < 273 || event.kbd.ascii > 276 || _version >= 7) {
+ // don't let game have arrow keys as we currently steal them
+ // for keyboard cursor control
+ // this fixes bug with up arrow (273) corresponding to
+ // "instant win" cheat in MI2 mentioned above
+ //
+ // This is not applicable to Full Throttle as it processes keyboard
+ // cursor control by itself. Also it fixes derby scene
+ _keyPressed = event.kbd.ascii; // Normal key press, pass on to the game.
+ }
+
+ if (_heversion >= 80) {
+ // Keyboard is controlled via variable
+ int _keyState = 0;
+
+ if (event.kbd.ascii == 276) // Left
+ _keyState = 1;
+
+ if (event.kbd.ascii == 275) // Right
+ _keyState |= 2;
+
+ if (event.kbd.ascii == 273) // Up
+ _keyState |= 4;
+
+ if (event.kbd.ascii == 274) // Down
+ _keyState |= 8;
+
+ if (event.kbd.flags == OSystem::KBD_SHIFT)
+ _keyState |= 16;
+
+ if (event.kbd.flags == OSystem::KBD_CTRL)
+ _keyState |= 32;
+
+ VAR(VAR_KEY_STATE) = _keyState;
+ }
+
+ if (_keyPressed >= 512)
+ debugC(DEBUG_GENERAL, "_keyPressed > 512 (%d)", _keyPressed);
+ else
+ _keyDownMap[_keyPressed] = true;
+ break;
+
+ case OSystem::EVENT_KEYUP:
+ // FIXME: for some reason OSystem::KBD_ALT is set sometimes
+ // possible to a bug in sdl-common.cpp
+ if (event.kbd.ascii >= 512)
+ debugC(DEBUG_GENERAL, "keyPressed > 512 (%d)", event.kbd.ascii);
+ else
+ _keyDownMap[event.kbd.ascii] = false;
+ break;
+
+
+ // We update the mouse position whenever the mouse moves or a click occurs.
+ // The latter is done to accomodate systems with a touchpad / pen controller.
+ case OSystem::EVENT_LBUTTONDOWN:
+ case OSystem::EVENT_RBUTTONDOWN:
+ case OSystem::EVENT_MOUSEMOVE:
+ if (event.type == OSystem::EVENT_LBUTTONDOWN)
+ _leftBtnPressed |= msClicked|msDown;
+ else if (event.type == OSystem::EVENT_RBUTTONDOWN)
+ _rightBtnPressed |= msClicked|msDown;
+ _mouse.x = event.mouse.x;
+ _mouse.y = event.mouse.y;
+
+ if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
+ _mouse.x -= (Common::kHercW - _screenWidth * 2) / 2;
+ _mouse.x /= 2;
+ _mouse.y = _mouse.y * 4 / 7;
+ }
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _leftBtnPressed &= ~msDown;
+ break;
+
+ case OSystem::EVENT_RBUTTONUP:
+ _rightBtnPressed &= ~msDown;
+ break;
+
+ // The following two cases enable dialog choices to be
+ // scrolled through in the SegaCD version of MI
+ // as nothing else uses the wheel don't bother
+ // checking the gameid
+
+ case OSystem::EVENT_WHEELDOWN:
+ _keyPressed = 55;
+ break;
+
+ case OSystem::EVENT_WHEELUP:
+ _keyPressed = 54;
+ break;
+
+ case OSystem::EVENT_QUIT:
+ if (_confirmExit)
+ confirmExitDialog();
+ else
+ _quit = true;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void ScummEngine::clearClickedStatus() {
+ _keyPressed = 0;
+
+#ifndef DISABLE_HE
+ if (_heversion >= 98) {
+ ((ScummEngine_v90he *)this)->_logicHE->processKeyStroke(_keyPressed);
+ }
+#endif
+ _mouseAndKeyboardStat = 0;
+ _leftBtnPressed &= ~msClicked;
+ _rightBtnPressed &= ~msClicked;
+}
+
+void ScummEngine::processKbd(bool smushMode) {
+ int saveloadkey;
+
+#ifndef DISABLE_HE
+ if (_heversion >= 98) {
+ ((ScummEngine_v90he *)this)->_logicHE->processKeyStroke(_keyPressed);
+ }
+#endif
+
+ _lastKeyHit = _keyPressed;
+ _keyPressed = 0;
+ if (((_version <= 2) || (_platform == Common::kPlatformFMTowns && _version == 3)) && 315 <= _lastKeyHit && _lastKeyHit < 315+12) {
+ // Convert F-Keys for V1/V2 games (they start at 1 instead of at 315)
+ _lastKeyHit -= 314;
+ }
+
+
+ //
+ // Clip the mouse coordinates, and compute _virtualMouse.x (and clip it, too)
+ //
+ if (_mouse.x < 0)
+ _mouse.x = 0;
+ if (_mouse.x > _screenWidth-1)
+ _mouse.x = _screenWidth-1;
+ if (_mouse.y < 0)
+ _mouse.y = 0;
+ if (_mouse.y > _screenHeight-1)
+ _mouse.y = _screenHeight-1;
+
+ _virtualMouse.x = _mouse.x + virtscr[0].xstart;
+ _virtualMouse.y = _mouse.y - virtscr[0].topline;
+ if (_features & GF_NEW_CAMERA)
+ _virtualMouse.y += _screenTop;
+
+ if (_virtualMouse.y < 0)
+ _virtualMouse.y = -1;
+ if (_virtualMouse.y >= virtscr[0].h)
+ _virtualMouse.y = -1;
+
+ //
+ // Determine the mouse button state.
+ //
+ _mouseAndKeyboardStat = 0;
+
+ // Interpret 'return' as left click and 'tab' as right click
+ if (_lastKeyHit && _cursor.state > 0) {
+ if (_lastKeyHit == 9) {
+ _mouseAndKeyboardStat = MBS_RIGHT_CLICK;
+ _lastKeyHit = 0;
+ } else if (_lastKeyHit == 13) {
+ _mouseAndKeyboardStat = MBS_LEFT_CLICK;
+ _lastKeyHit = 0;
+ }
+ }
+
+ if (_leftBtnPressed & msClicked && _rightBtnPressed & msClicked && _version >= 4) {
+ // Pressing both mouse buttons is treated as if you pressed
+ // the cutscene exit key (i.e. ESC in most games). That mimicks
+ // the behaviour of the original engine where pressing both
+ // mouse buttons also skips the current cutscene.
+ _mouseAndKeyboardStat = 0;
+ _lastKeyHit = (uint)VAR(VAR_CUTSCENEEXIT_KEY);
+ } else if (_rightBtnPressed & msClicked && (_version <= 3 && _gameId != GID_LOOM)) {
+ // Pressing right mouse button is treated as if you pressed
+ // the cutscene exit key (i.e. ESC in most games). That mimicks
+ // the behaviour of the original engine where pressing right
+ // mouse button also skips the current cutscene.
+ _mouseAndKeyboardStat = 0;
+ _lastKeyHit = (VAR_CUTSCENEEXIT_KEY != 0xFF) ? (uint)VAR(VAR_CUTSCENEEXIT_KEY) : 27;
+ } else if (_leftBtnPressed & msClicked) {
+ _mouseAndKeyboardStat = MBS_LEFT_CLICK;
+ } else if (_rightBtnPressed & msClicked) {
+ _mouseAndKeyboardStat = MBS_RIGHT_CLICK;
+ }
+
+ if (_version >= 6) {
+ VAR(VAR_LEFTBTN_HOLD) = (_leftBtnPressed & msDown) != 0;
+ VAR(VAR_RIGHTBTN_HOLD) = (_rightBtnPressed & msDown) != 0;
+
+ if (_version >= 7) {
+ VAR(VAR_LEFTBTN_DOWN) = (_leftBtnPressed & msClicked) != 0;
+ VAR(VAR_RIGHTBTN_DOWN) = (_rightBtnPressed & msClicked) != 0;
+ }
+ }
+
+ _leftBtnPressed &= ~msClicked;
+ _rightBtnPressed &= ~msClicked;
+
+ if (!_lastKeyHit)
+ return;
+
+ // If a key script was specified (a V8 feature), and it's trigger
+ // key was pressed, run it.
+ if (_keyScriptNo && (_keyScriptKey == _lastKeyHit)) {
+ runScript(_keyScriptNo, 0, 0, 0);
+ return;
+ }
+
+#ifdef _WIN32_WCE
+ if (_lastKeyHit == KEY_ALL_SKIP) {
+ // Skip cutscene
+ if (smushMode) {
+ _lastKeyHit = (VAR_CUTSCENEEXIT_KEY != 0xFF) ? (uint)VAR(VAR_CUTSCENEEXIT_KEY) : 27;
+ }
+ else
+ if (vm.cutScenePtr[vm.cutSceneStackPointer])
+ _lastKeyHit = (VAR_CUTSCENEEXIT_KEY != 0xFF) ? (uint)VAR(VAR_CUTSCENEEXIT_KEY) : 27;
+ else
+ // Skip talk
+ if (VAR_TALKSTOP_KEY != 0xFF && _talkDelay > 0)
+ _lastKeyHit = (uint)VAR(VAR_TALKSTOP_KEY);
+ else
+ // Escape
+ _lastKeyHit = 27;
+ }
+#endif
+
+ if (_version >= 6 && _lastKeyHit == 20) {
+ char buf[256];
+
+ _voiceMode++;
+ if (_voiceMode == 3)
+ _voiceMode = 0;
+
+ switch(_voiceMode) {
+ case 0:
+ sprintf(buf, "Speech Only");
+ ConfMan.set("speech_mute", false);
+ ConfMan.set("subtitles", false);
+ break;
+ case 1:
+ sprintf(buf, "Speech and Subtitles");
+ ConfMan.set("speech_mute", false);
+ ConfMan.set("subtitles", true);
+ break;
+ case 2:
+ sprintf(buf, "Subtitles Only");
+ ConfMan.set("speech_mute", true);
+ ConfMan.set("subtitles", true);
+ break;
+ }
+
+ if (VAR_VOICE_MODE != 0xFF)
+ VAR(VAR_VOICE_MODE) = _voiceMode;
+
+ GUI::TimedMessageDialog dialog(buf, 1500);
+ runDialog(dialog);
+ return;
+ }
+
+ if (VAR_RESTART_KEY != 0xFF && _lastKeyHit == VAR(VAR_RESTART_KEY) ||
+ (((_version <= 2) || (_platform == Common::kPlatformFMTowns && _version == 3)) && _lastKeyHit == 8)) {
+ confirmRestartDialog();
+ return;
+ }
+
+ if ((VAR_PAUSE_KEY != 0xFF && _lastKeyHit == VAR(VAR_PAUSE_KEY)) ||
+ (VAR_PAUSE_KEY == 0xFF && _lastKeyHit == ' ')) {
+ pauseGame();
+ return;
+ }
+
+ // COMI version string is hard coded
+ // Dig/FT version strings are partly hard coded too
+ if (_version == 7 && _lastKeyHit == VAR(VAR_VERSION_KEY)) {
+ versionDialog();
+ return;
+ }
+
+ if ((_version <= 2) || (_platform == Common::kPlatformFMTowns && _version == 3))
+ saveloadkey = 5; // F5
+ else if ((_version <= 3) || (_gameId == GID_SAMNMAX) || (_gameId == GID_CMI) || (_heversion >= 72))
+ saveloadkey = 319; // F5
+ else
+ saveloadkey = VAR(VAR_MAINMENU_KEY);
+
+ if ((_platform == Common::kPlatformC64 && _gameId == GID_MANIAC && _lastKeyHit == 27) ||
+ (VAR_CUTSCENEEXIT_KEY != 0xFF && _lastKeyHit == VAR(VAR_CUTSCENEEXIT_KEY))) {
+#ifndef DISABLE_SCUMM_7_8
+ // Skip cutscene (or active SMUSH video). For the V2 games, which
+ // normally use F4 for this, we add in a hack that makes escape work,
+ // too (just for convenience).
+ if (smushMode) {
+ if (_gameId == GID_FT)
+ _insane->escapeKeyHandler();
+ else
+ _smushVideoShouldFinish = true;
+ }
+#endif
+ if (!smushMode || _smushVideoShouldFinish)
+ abortCutscene();
+ if (_version <= 2) {
+ // Ensure that the input script also sees the key press.
+ // This is necessary so you can abort the airplane travel
+ // in Zak.
+ if (VAR_KEYPRESS != 0xFF)
+ VAR(VAR_KEYPRESS) = VAR(VAR_CUTSCENEEXIT_KEY);
+ }
+ } else if (_lastKeyHit == saveloadkey) {
+ if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0)
+ runScript(VAR(VAR_SAVELOAD_SCRIPT), 0, 0, 0);
+
+ mainMenuDialog(); // Display NewGui
+
+ if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0)
+ runScript(VAR(VAR_SAVELOAD_SCRIPT2), 0, 0, 0);
+ return;
+ } else if (VAR_TALKSTOP_KEY != 0xFF && _lastKeyHit == VAR(VAR_TALKSTOP_KEY)) {
+ _talkDelay = 0;
+ if (_sound->_sfxMode & 2)
+ stopTalk();
+ return;
+ } else if (_lastKeyHit == '[' || _lastKeyHit == ']') { // Change music volume
+ int vol = ConfMan.getInt("music_volume") / 16;
+ if (_lastKeyHit == ']' && vol < 16)
+ vol++;
+ else if (_lastKeyHit == '[' && vol > 0)
+ vol--;
+
+ // Display the music volume
+ ValueDisplayDialog dlg("Music volume: ", 0, 16, vol, ']', '[');
+ vol = runDialog(dlg);
+
+ vol *= 16;
+ if (vol > Audio::Mixer::kMaxMixerVolume)
+ vol = Audio::Mixer::kMaxMixerVolume;
+
+ ConfMan.set("music_volume", vol);
+ setupVolumes();
+ } else if (_lastKeyHit == '-' || _lastKeyHit == '+') { // Change text speed
+ if (_lastKeyHit == '+' && _defaultTalkDelay > 0)
+ _defaultTalkDelay--;
+ else if (_lastKeyHit == '-' && _defaultTalkDelay < 9)
+ _defaultTalkDelay++;
+
+ // Display the talk speed
+ ValueDisplayDialog dlg("Text speed: ", 0, 9, 9 - _defaultTalkDelay, '+', '-');
+ _defaultTalkDelay = 9 - runDialog(dlg);
+
+ if (VAR_CHARINC != 0xFF)
+ VAR(VAR_CHARINC) = _defaultTalkDelay;
+ } else if (_lastKeyHit == '~' || _lastKeyHit == '#') { // Debug console
+ _debugger->attach();
+ } else if (_version <= 2) {
+ // Store the input type. So far we can't distinguish
+ // between 1, 3 and 5.
+ // 1) Verb 2) Scene 3) Inv. 4) Key
+ // 5) Sentence Bar
+
+ if (VAR_KEYPRESS != 0xFF && _lastKeyHit) { // Key Input
+ VAR(VAR_KEYPRESS) = _lastKeyHit;
+ }
+ }
+
+ _mouseAndKeyboardStat = _lastKeyHit;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp
new file mode 100644
index 0000000000..08c982b65f
--- /dev/null
+++ b/engines/scumm/insane/insane.cpp
@@ -0,0 +1,1466 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "base/engine.h"
+
+#include "common/system.h"
+
+#include "scumm/scumm.h"
+#include "scumm/actor.h"
+#include "scumm/sound.h"
+
+#include "scumm/imuse.h"
+#include "scumm/imuse_digi/dimuse.h"
+
+#include "scumm/smush/smush_player.h"
+#include "scumm/smush/smush_font.h"
+#include "scumm/smush/chunk_type.h"
+#include "scumm/smush/chunk.h"
+
+#include "scumm/insane/insane.h"
+
+// TODO (in no particular order):
+// o Code review/cleanup
+
+namespace Scumm {
+
+static const int actorAnimationData[21] = {20, 21, 22, 23, 24, 25, 26, 13, 14, 15, 16, 17,
+ 18, 19, 6, 7, 8, 9, 10, 11, 12};
+
+
+Insane::Insane(ScummEngine_v6 *scumm) {
+ _vm = scumm;
+
+ initvars();
+
+ if (!((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))) {
+ readFileToMem("roadrash.rip", &_smush_roadrashRip);
+ readFileToMem("roadrsh2.rip", &_smush_roadrsh2Rip);
+ readFileToMem("roadrsh3.rip", &_smush_roadrsh3Rip);
+ readFileToMem("goglpalt.rip", &_smush_goglpaltRip);
+ readFileToMem("tovista1.flu", &_smush_tovista1Flu);
+ readFileToMem("tovista2.flu", &_smush_tovista2Flu);
+ readFileToMem("toranch.flu", &_smush_toranchFlu);
+ readFileToMem("minedriv.flu", &_smush_minedrivFlu);
+ readFileToMem("minefite.flu", &_smush_minefiteFlu);
+ _smush_bensgoggNut = new NutRenderer(_vm);
+ _smush_bensgoggNut->loadFont("bensgogg.nut");
+ _smush_bencutNut = new NutRenderer(_vm);
+ _smush_bencutNut->loadFont("bencut.nut");
+ }
+
+ _smush_iconsNut = new NutRenderer(_vm);
+ _smush_iconsNut->loadFont("icons.nut");
+ _smush_icons2Nut = new NutRenderer(_vm);
+ _smush_icons2Nut->loadFont("icons2.nut");
+}
+
+Insane::~Insane(void) {
+ free(_smush_roadrashRip);
+ free(_smush_roadrsh2Rip);
+ free(_smush_roadrsh3Rip);
+ free(_smush_goglpaltRip);
+ free(_smush_tovista1Flu);
+ free(_smush_tovista2Flu);
+ free(_smush_toranchFlu);
+ free(_smush_minedrivFlu);
+ free(_smush_minefiteFlu);
+
+ delete _smush_bencutNut;
+ delete _smush_bensgoggNut;
+ delete _smush_iconsNut;
+ delete _smush_icons2Nut;
+}
+
+void Insane::setSmushParams(int speed) {
+ _speed = speed;
+}
+
+void Insane::initvars(void) {
+ int i, j;
+
+ _speed = 12;
+ _insaneIsRunning = false;
+
+ _numberArray = 0;
+ _emulateInterrupt = 0;
+ _flag1d = 0;
+ _objArray1Idx = 0;
+ _objArray1Idx2 = 0;
+ _objArray2Idx = 0;
+ _objArray2Idx2 = 0;
+ _currSceneId = 1;
+ _timer6Id = 0;
+ _timerSpriteId = 0;
+ _temp2SceneId = 0;
+ _tempSceneId = 0;
+ _currEnemy = -1;
+ _currScenePropIdx = 0;
+ _currScenePropSubIdx = 0;
+ _currTrsMsg = 0;
+ _sceneData2Loaded = 0;
+ _sceneData1Loaded = 0;
+ _keyboardDisable = 0;
+ _needSceneSwitch = false;
+ _idx2Exceeded = 0;
+ _lastKey = 0;
+ _tiresRustle = false;
+ _keybOldDx = 0;
+ _keybOldDy = 0;
+ _velocityX = 0;
+ _velocityY = 0;
+ _keybX = 0;
+ _keybY = 0;
+ _firstBattle = false;
+ _battleScene = true;
+ _kickBenProgress = false;
+ _weaponBenJustSwitched = false;
+ _kickEnemyProgress = false;
+ _weaponEnemyJustSwitched = false;
+ _beenCheated = 0;
+ _posBrokenTruck = 0;
+ _posBrokenCar = 0;
+ _posFatherTorque = 0;
+ _posCave = 0;
+ _posVista = 0;
+ _roadBranch = false;
+ _roadStop = false;
+ _carIsBroken = false;
+ _benHasGoggles = false;
+ _mineCaveIsNear = false;
+ _objectDetected = false;
+ _approachAnim = -1;
+ _val54d = 0;
+ _val57d = 0;
+ _val115_ = false;
+ _roadBumps = false;
+ _val211d = 0;
+ _val213d = 0;
+ _metEnemiesListTail = 0;
+ _smlayer_room = 0;
+ _smlayer_room2 = 0;
+ _isBenCut = 0;
+ _continueFrame = 0;
+ _continueFrame1 = 0;
+ _counter1 = 0;
+ _iactSceneId = 0;
+ _iactSceneId2 = 0;
+
+ for (i = 0; i < 12; i++)
+ _metEnemiesList[i] = 0;
+
+ for (i = 0; i < 9; i++)
+ for (j = 0; j < 9; j++)
+ _enHdlVar[i][j] = 0;
+
+ for (i = 0; i < 0x80; i++)
+ _iactBits[i] = 0;
+
+
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ init_enemyStruct(EN_ROTT1, EN_ROTT1, 0, 0, 160, 0, INV_MACE, 63, "endcrshr.san",
+ 25, 15, 16, 26, 11, 3);
+ } else {
+ init_enemyStruct(EN_ROTT1, EN_ROTT1, 0, 0, 160, 0, INV_MACE, 90, "wr2_rott.san",
+ 26, 16, 17, 27, 11, 3);
+ }
+
+ init_enemyStruct(EN_ROTT2, EN_ROTT2, 1, 0, 250, 0, INV_2X4, 90, "wr2_rott.san",
+ 28, 16, 17, 42, 11, 3);
+ init_enemyStruct(EN_ROTT3, EN_ROTT3, 2, 0, 120, 0, INV_HAND, 90, "wr2_rott.san",
+ 15, 16, 17, 43, 11, 3);
+ init_enemyStruct(EN_VULTF1, EN_VULTF1, 3, 0, 60, 0, INV_HAND, 91, "wr2_vltp.san",
+ 29, 33, 32, 37, 12, 4);
+ init_enemyStruct(EN_VULTM1, EN_VULTM1, 4, 0, 100, 0, INV_CHAIN, 91, "wr2_vltc.san",
+ 30, 33, 32, 36, 12, 4);
+ init_enemyStruct(EN_VULTF2, EN_VULTF2, 5, 0, 250, 0, INV_CHAINSAW, 91, "wr2_vlts.san",
+ 31, 33, 32, 35, 12, 4);
+ init_enemyStruct(EN_VULTM2, EN_VULTM2, 6, 0, 900, 0, INV_BOOT, 91, "wr2_rott.san",
+ 34, 33, 32, 45, 16, 4);
+ init_enemyStruct(EN_CAVEFISH, EN_CAVEFISH, 7, 0, 60, 0, INV_DUST, 92, "wr2_cave.san",
+ 39, 0, 0, 41, 13, 2);
+ init_enemyStruct(EN_TORQUE, EN_TORQUE, 8, 0, 900, 0, INV_HAND, 93, "wr2_vltp.san",
+ 57, 0, 0, 37, 12, 1);
+
+ init_fluConfStruct(1, 1, &_smush_minedrivFlu, "minedriv.san", 235, 1300);
+ init_fluConfStruct(2, 1, &_smush_minedrivFlu, "minedriv.san", 355, 1300);
+ init_fluConfStruct(3, 1, &_smush_minedrivFlu, "minedriv.san", 1255, 1300);
+ init_fluConfStruct(4, 1, &_smush_minedrivFlu, "minedriv.san", 565, 1300);
+ init_fluConfStruct(5, 1, &_smush_minedrivFlu, "minedriv.san", 1040, 1300);
+ init_fluConfStruct(8, 1, &_smush_minedrivFlu, "minedriv.san", 1040, 1300);
+ init_fluConfStruct(9, 1, &_smush_minedrivFlu, "minedriv.san", 655, 1300);
+ init_fluConfStruct(10, 1, &_smush_minedrivFlu, "minedriv.san", 115, 1300);
+ init_fluConfStruct(11, 1, &_smush_minedrivFlu, "minedriv.san", 315, 1300);
+ init_fluConfStruct(12, 1, &_smush_minedrivFlu, "minedriv.san", 235, 1300);
+ init_fluConfStruct(15, 6, &_smush_toranchFlu, "toranch.san", 115, 530);
+ init_fluConfStruct(16, 5, &_smush_tovista2Flu, "tovista2.san", 235, 290);
+ init_fluConfStruct(17, 4, &_smush_tovista1Flu, "tovista1.san", 175, 230);
+ init_fluConfStruct(18, 4, &_smush_tovista1Flu, "tovista1.san", 175, 230);
+ init_fluConfStruct(19, 6, &_smush_toranchFlu, "toranch.san", 115, 530);
+ init_fluConfStruct(20, 6, &_smush_toranchFlu, "toranch.san", 115, 530);
+
+ init_scenePropStruct( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 1, 0, 1, 128, 2001, 0, 0, 0, 0, 56, 2);
+ init_scenePropStruct( 2, 0, 0, 125, 1002, 0, 0, 0, 0, 35, 3);
+ init_scenePropStruct( 3, 0, 1, 129, 2002, 0, 0, 0, 0, 23, 4);
+ init_scenePropStruct( 4, 0, 1, 130, 2003, 0, 0, 0, 0, 40, 5);
+ init_scenePropStruct( 5, 0, 0, 126, 1005, 0, 0, 0, 0, 46, 6);
+ init_scenePropStruct( 6, 0, 1, 131, 2004, 0, 0, 0, 0, 39, 7);
+ init_scenePropStruct( 7, 0, 1, 132, 2005, 0, 0, 0, 0, 45, 8);
+ init_scenePropStruct( 8, 0, 1, 133, 2006, 0, 0, 0, 0, 14, 9);
+ init_scenePropStruct( 9, 0, 0, 127, 1009, 0, 0, 0, 0, 15, 10);
+ init_scenePropStruct( 10, 0, 1, 134, 501, 0, 0, 0, 0, 25, 11);
+ init_scenePropStruct( 11, 0, 1, 135, 502, 0, 0, 0, 0, 15, 0);
+ init_scenePropStruct( 12, 1, -1, 0, 0, 0xFF, 0xFF, 0xFF, 0, 0, 1);
+ init_scenePropStruct( 13, 1, 0, 291, 135, 0xFF, 0xFF, 0xFF, 0, 25, 0);
+ init_scenePropStruct( 14, 2, -1, 0, 0, 0xFC, 0, 0xFC, 0, 0, 1);
+ init_scenePropStruct( 15, 2, 1, 277, 17, 0xFC, 0, 0xFC, 0, 56, 2);
+ init_scenePropStruct( 16, 2, 0, 288, 18, 0xFF, 0xFF, 0xFF, 0, 56, 3);
+ init_scenePropStruct( 17, 2, 1, 278, 19, 0xFC, 0, 0xFC, 0, 56, 0);
+ init_scenePropStruct( 18, 3, -1, 0, 0, 0xFC, 0, 0xFC, 0, 0, 1);
+ init_scenePropStruct( 19, 3, 1, 282, 23, 0xFC, 0, 0xFC, 0, 56, 0);
+ init_scenePropStruct( 20, 4, -1, 0, 0, 0xFC, 0, 0xFC, 0, 0, 1);
+ init_scenePropStruct( 21, 4, 1, 283, 24, 0xFC, 0, 0xFC, 0, 56, 0);
+ init_scenePropStruct( 22, 5, -1, 0, 0, 0xFC, 0, 0xFC, 0, 0, 1);
+ init_scenePropStruct( 23, 5, 1, 284, 25, 0xFC, 0, 0xFC, 0, 56, 0);
+ init_scenePropStruct( 24, 6, -1, 0, 0, 0xFC, 0, 0xFC, 0, 0, 1);
+ init_scenePropStruct( 25, 6, 1, 285, 26, 0xFC, 0, 0xFC, 0, 56, 0);
+ init_scenePropStruct( 26, 7, -1, 0, 0, 0xFC, 0, 0xFC, 0, 0, 1);
+ init_scenePropStruct( 27, 7, 1, 286, 27, 0xFC, 0, 0xFC, 0, 56, 0);
+ init_scenePropStruct( 28, 8, -1, 0, 0, 0xFC, 0, 0xFC, 0, 0, 1);
+ init_scenePropStruct( 29, 8, 1, 287, 28, 0xFC, 0, 0xFC, 0, 56, 0);
+ init_scenePropStruct( 30, 9, -1, 0, 0, 0xFC, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 31, 9, 1, 261, 1, 0xFC, 0, 0, 0, 40, 2);
+ init_scenePropStruct( 32, 9, 1, 262, 2, 0xFC, 0, 0, 0, 40, 3);
+ init_scenePropStruct( 33, 9, 1, 263, 3, 0xFC, 0, 0, 0, 40, 0);
+ init_scenePropStruct( 34, 10, -1, 0, 0, 0xFC, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 35, 10, 1, 263, 3, 0xFC, 0, 0, 0, 30, 0);
+ init_scenePropStruct( 36, 11, -1, 0, 0, 0xFC, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 37, 11, 1, 264, 4, 0xFC, 0, 0, 0, 30, 0);
+ init_scenePropStruct( 38, 12, -1, 0, 0, 0xFC, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 39, 12, 1, 265, 5, 0xFC, 0, 0, 0, 30, 0);
+ init_scenePropStruct( 40, 13, -1, 0, 0, 0xFC, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 41, 13, 1, 266, 6, 0xFC, 0, 0, 0, 30, 0);
+ init_scenePropStruct( 42, 14, -1, 0, 0, 0xFC, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 43, 14, 1, 267, 7, 0xFC, 0, 0, 0, 30, 0);
+ init_scenePropStruct( 44, 15, -1, 0, 0, 0xFC, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 45, 15, 1, 268, 8, 0xFC, 0, 0, 0, 30, 0);
+ init_scenePropStruct( 46, 16, -1, 0, 0, 0xFC, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 47, 16, 1, 274, 14, 0xFC, 0, 0, 0, 30, 0);
+ init_scenePropStruct( 48, 17, -1, 0, 0, 0xFC, 0, 0, 0, 0, 1);
+ init_scenePropStruct( 49, 17, 1, 270, 10, 0xFC, 0, 0, 0, 30, 0);
+ init_scenePropStruct( 50, 18, -1, 0, 0, 0xFC, 0xFC, 0x54, 0, 0, 1);
+ init_scenePropStruct( 51, 18, 0, 289, 45, 0xFF, 0xFF, 0xFF, 0, 40, 2);
+ init_scenePropStruct( 52, 18, 1, 177, 49, 0xFC, 0xFC, 0x54, 0, 40, 3);
+ init_scenePropStruct( 53, 18, 1, 178, 50, 0xFC, 0xFC, 0x54, 0, 40, 4);
+ init_scenePropStruct( 54, 18, 0, 290, 47, 0xFF, 0xFF, 0xFF, 0, 40, 0);
+ init_scenePropStruct( 55, 19, -1, 0, 0, 0xFC, 0xFC, 0x54, 0, 0, 1);
+ init_scenePropStruct( 56, 19, 1, 179, 51, 0xFC, 0xFC, 0x54, 0, 40, 0);
+ init_scenePropStruct( 57, 20, -1, 0, 0, 0xFC, 0xFC, 0x54, 0, 0, 1);
+ init_scenePropStruct( 58, 20, 1, 183, 55, 0xFC, 0xFC, 0x54, 0, 40, 0);
+ init_scenePropStruct( 59, 21, -1, 0, 0, 0xFC, 0xFC, 0x54, 0, 0, 1);
+ init_scenePropStruct( 60, 21, 1, 184, 56, 0xFC, 0xFC, 0x54, 0, 40, 0);
+ init_scenePropStruct( 61, 22, -1, 0, 0, 0xFC, 0xFC, 0x54, 0, 0, 1);
+ init_scenePropStruct( 62, 22, 1, 186, 58, 0xFC, 0xFC, 0x54, 0, 40, 0);
+ init_scenePropStruct( 63, 23, -1, 0, 0, 0xFC, 0xFC, 0x54, 0, 0, 1);
+ init_scenePropStruct( 64, 23, 1, 191, 63, 0xFC, 0xFC, 0x54, 0, 40, 0);
+ init_scenePropStruct( 65, 24, -1, 0, 0, 0xFC, 0xFC, 0x54, 0, 0, 1);
+ init_scenePropStruct( 66, 24, 1, 192, 64, 0xFC, 0xFC, 0x54, 0, 40, 0);
+ init_scenePropStruct( 67, 25, -1, 0, 0, 0xBC, 0x78, 0x48, 0, 0, 1);
+ init_scenePropStruct( 68, 25, 1, 220, 93, 0xBC, 0x78, 0x48, 0, 40, 2);
+ init_scenePropStruct( 69, 25, 1, 221, 94, 0xBC, 0x78, 0x48, 0, 40, 3);
+ init_scenePropStruct( 70, 25, 1, 222, 95, 0xBC, 0x78, 0x48, 0, 40, 0);
+ init_scenePropStruct( 71, 26, -1, 0, 0, 0xBC, 0x78, 0x48, 0, 0, 1);
+ init_scenePropStruct( 72, 26, 1, 223, 96, 0xBC, 0x78, 0x48, 0, 40, 0);
+ init_scenePropStruct( 73, 27, -1, 0, 0, 0xBC, 0x78, 0x48, 0, 0, 1);
+ init_scenePropStruct( 74, 27, 1, 224, 97, 0xBC, 0x78, 0x48, 0, 40, 0);
+ init_scenePropStruct( 75, 28, -1, 0, 0, 0xBC, 0x78, 0x48, 0, 0, 1);
+ init_scenePropStruct( 76, 28, 1, 225, 98, 0xBC, 0x78, 0x48, 0, 40, 0);
+ init_scenePropStruct( 77, 29, -1, 0, 0, 0xBC, 0x78, 0x48, 0, 0, 1);
+ init_scenePropStruct( 78, 29, 1, 226, 99, 0xBC, 0x78, 0x48, 0, 40, 0);
+ init_scenePropStruct( 79, 30, -1, 0, 0, 0xBC, 0x78, 0x48, 0, 0, 1);
+ init_scenePropStruct( 80, 30, 1, 228, 101, 0xBC, 0x78, 0x48, 0, 40, 0);
+ init_scenePropStruct( 81, 31, -1, 0, 0, 0xBC, 0x78, 0x48, 0, 0, 1);
+ init_scenePropStruct( 82, 31, 1, 229, 102, 0xBC, 0x78, 0x48, 0, 40, 0);
+ init_scenePropStruct( 83, 32, -1, 0, 0, 0xA8, 0xA8, 0xA8, 0, 0, 1);
+ init_scenePropStruct( 84, 32, 1, 233, 106, 0xA8, 0xA8, 0xA8, 0, 40, 2);
+ init_scenePropStruct( 85, 32, 1, 234, 107, 0xA8, 0xA8, 0xA8, 0, 40, 0);
+ init_scenePropStruct( 86, 33, -1, 0, 0, 0xA8, 0xA8, 0xA8, 0, 0, 1);
+ init_scenePropStruct( 87, 33, 1, 241, 114, 0xA8, 0xA8, 0xA8, 0, 40, 2);
+ init_scenePropStruct( 88, 33, 1, 242, 115, 0xA8, 0xA8, 0xA8, 0, 40, 0);
+ init_scenePropStruct( 89, 34, -1, 0, 0, 0xA8, 0xA8, 0xA8, 0, 0, 1);
+ init_scenePropStruct( 90, 34, 1, 237, 110, 0xA8, 0xA8, 0xA8, 0, 40, 2);
+ init_scenePropStruct( 91, 34, 1, 238, 111, 0xA8, 0xA8, 0xA8, 0, 40, 3);
+ init_scenePropStruct( 92, 34, 1, 239, 112, 0xA8, 0xA8, 0xA8, 0, 40, 0);
+ init_scenePropStruct( 93, 35, -1, 0, 0, 0xA8, 0xA8, 0xA8, 0, 0, 1);
+ init_scenePropStruct( 94, 35, 1, 258, 131, 0xA8, 0xA8, 0xA8, 0, 40, 0);
+ init_scenePropStruct( 95, 36, -1, 0, 0, 0xA8, 0xA8, 0xA8, 0, 0, 1);
+ init_scenePropStruct( 96, 36, 1, 260, 133, 0xA8, 0xA8, 0xA8, 0, 40, 0);
+ init_scenePropStruct( 97, 37, -1, 0, 0, 0xA8, 0xA8, 0xA8, 0, 0, 1);
+ init_scenePropStruct( 98, 37, 1, 252, 125, 0xA8, 0xA8, 0xA8, 0, 40, 0);
+ init_scenePropStruct( 99, 38, -1, 0, 0, 0xA8, 0xA8, 0xA8, 0, 0, 1);
+ init_scenePropStruct(100, 38, 1, 254, 127, 0xA8, 0xA8, 0xA8, 0, 40, 0);
+ init_scenePropStruct(101, 39, -1, 0, 0, 0xA8, 0xA8, 0xA8, 0, 0, 1);
+ init_scenePropStruct(102, 39, 1, 236, 109, 0xA8, 0xA8, 0xA8, 0, 40, 0);
+ init_scenePropStruct(103, 40, -1, 0, 0, 4, 0xBC, 0, 0, 0, 1);
+ init_scenePropStruct(104, 40, 1, 174, 42, 4, 0xBC, 0, 0, 40, 0);
+ init_scenePropStruct(105, 41, -1, 0, 0, 4, 0xBC, 0, 0, 0, 1);
+ init_scenePropStruct(106, 41, 1, 167, 36, 4, 0xBC, 0, 0, 40, 0);
+ init_scenePropStruct(107, 42, -1, 0, 0, 4, 0xBC, 0, 0, 0, 1);
+ init_scenePropStruct(108, 42, 1, 160, 29, 4, 0xBC, 0, 0, 40, 0);
+ init_scenePropStruct(109, 43, -1, 0, 0, 4, 0xBC, 0, 0, 0, 1);
+ init_scenePropStruct(110, 43, 1, 161, 30, 4, 0xBC, 0, 0, 40, 0);
+ init_scenePropStruct(111, 44, -1, 0, 0, 4, 0xBC, 0, 0, 0, 1);
+ init_scenePropStruct(112, 44, 1, 163, 32, 4, 0xBC, 0, 0, 40, 0);
+ init_scenePropStruct(113, 45, -1, 0, 0, 4, 0xBC, 0, 0, 0, 1);
+ init_scenePropStruct(114, 45, 1, 164, 33, 4, 0xBC, 0, 0, 40, 0);
+ init_scenePropStruct(115, 46, -1, 0, 0, 4, 0xBC, 0, 0, 0, 1);
+ init_scenePropStruct(116, 46, 1, 170, 39, 4, 0xBC, 0, 0, 40, 0);
+ init_scenePropStruct(117, 47, -1, 0, 0, 4, 0xBC, 0, 0, 0, 1);
+ init_scenePropStruct(118, 47, 1, 166, 35, 4, 0xBC, 0, 0, 40, 0);
+ init_scenePropStruct(119, 48, -1, 0, 0, 4, 0xBC, 0, 0, 0, 1);
+ init_scenePropStruct(120, 48, 1, 175, 43, 4, 0xBC, 0, 0, 40, 0);
+ init_scenePropStruct(121, 49, -1, 0, 0, 0x40, 0x40, 0xFC, 0, 0, 1);
+ init_scenePropStruct(122, 49, 1, 203, 75, 0x40, 0x40, 0xFC, 0, 40, 0);
+ init_scenePropStruct(123, 50, -1, 0, 0, 0x40, 0x40, 0xFC, 0, 0, 1);
+ init_scenePropStruct(124, 50, 1, 194, 66, 0x40, 0x40, 0xFC, 0, 40, 0);
+ init_scenePropStruct(125, 51, -1, 0, 0, 0x40, 0x40, 0xFC, 0, 0, 1);
+ init_scenePropStruct(126, 51, 1, 195, 67, 0x40, 0x40, 0xFC, 0, 40, 0);
+ init_scenePropStruct(127, 52, -1, 0, 0, 0x40, 0x40, 0xFC, 0, 0, 1);
+ init_scenePropStruct(128, 52, 1, 199, 71, 0x40, 0x40, 0xFC, 0, 40, 0);
+ init_scenePropStruct(129, 53, -1, 0, 0, 0x40, 0x40, 0xFC, 0, 0, 1);
+ init_scenePropStruct(130, 53, 1, 205, 77, 0x40, 0x40, 0xFC, 0, 40, 0);
+ init_scenePropStruct(131, 54, -1, 0, 0, 0x40, 0x40, 0xFC, 0, 0, 1);
+ init_scenePropStruct(132, 54, 1, 212, 85, 0x40, 0x40, 0xFC, 0, 40, 0);
+ init_scenePropStruct(133, 55, -1, 0, 0, 0x40, 0x40, 0xFC, 0, 0, 1);
+ init_scenePropStruct(134, 55, 1, 201, 73, 0x40, 0x40, 0xFC, 0, 40, 0);
+ init_scenePropStruct(135, 56, -1, 0, 0, 0x40, 0x40, 0xFC, 0, 0, 1);
+ init_scenePropStruct(136, 56, 1, 198, 70, 0x40, 0x40, 0xFC, 0, 40, 0);
+ init_scenePropStruct(137, 57, -1, 0, 0, 0x40, 0x40, 0xFC, 0, 0, 1);
+ init_scenePropStruct(138, 57, 0, 59, 134, 0xFF, 0xFF, 0xFF, 0, 30, 0);
+
+ _actor[0].damage = 0;
+ _actor[0].maxdamage = 80;
+ _actor[0].field_8 = 1;
+ _actor[0].frame = 0;
+ _actor[0].tilt = 0;
+ _actor[0].cursorX = 0;
+ _actor[0].speed = 0;
+ _actor[0].x = 160;
+ _actor[0].y = 0;
+ _actor[0].y1 = -1;
+ _actor[0].x1 = -1;
+ _actor[0].weaponClass = 2;
+ _actor[0].animWeaponClass = 0;
+ _actor[0].newFacingFlag = 2;
+ _actor[0].curFacingFlag = 0;
+ _actor[0].lost = false;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = false;
+ _actor[0].field_48 = false;
+ _actor[0].defunct = 0;
+ _actor[0].scenePropSubIdx = 0;
+ _actor[0].field_54 = 0;
+ _actor[0].runningSound = 0;
+ _actor[0].weapon = INV_HAND;
+ _actor[0].inventory[INV_CHAIN] = 0;
+ _actor[0].inventory[INV_CHAINSAW] = 0;
+ _actor[0].inventory[INV_MACE] = 0;
+ _actor[0].inventory[INV_2X4] = 0;
+ _actor[0].inventory[INV_WRENCH] = 1;
+ _actor[0].inventory[INV_BOOT] = 1;
+ _actor[0].inventory[INV_HAND] = 1;
+ _actor[0].inventory[INV_DUST] = 0;
+ _actor[0].probability = 5;
+ _actor[0].enemyHandler = EN_BEN;
+ init_actStruct(0, 0, 11, 1, 1, 0, 0, 0);
+ init_actStruct(0, 1, 12, 1, 1, 0, 0, 0);
+ init_actStruct(0, 2, 1, 1, 1, 0, 0, 0);
+ init_actStruct(0, 3, 1, 1, 1, 0, 0, 0);
+
+ _actor[1].damage = 0;
+ _actor[1].maxdamage = -1;
+ _actor[1].field_8 = 1;
+ _actor[1].frame = 0;
+ _actor[1].tilt = 0;
+ _actor[1].cursorX = 0;
+ _actor[1].speed = 0;
+ _actor[1].x = 160;
+ _actor[1].y = 0;
+ _actor[1].y1 = -1;
+ _actor[1].x1 = -1;
+ _actor[1].weaponClass = 2;
+ _actor[1].animWeaponClass = 0;
+ _actor[1].newFacingFlag = 0;
+ _actor[1].curFacingFlag = 0;
+ _actor[1].lost = false;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = false;
+ _actor[1].field_48 = false;
+ _actor[1].defunct = 0;
+ _actor[1].scenePropSubIdx = 0;
+ _actor[1].field_54 = 0;
+ _actor[1].runningSound = 0;
+ _actor[1].weapon = INV_HAND;
+ _actor[1].inventory[INV_CHAIN] = 0;
+ _actor[1].inventory[INV_CHAINSAW] = 0;
+ _actor[1].inventory[INV_MACE] = 1;
+ _actor[1].inventory[INV_2X4] = 0;
+ _actor[1].inventory[INV_WRENCH] = 0;
+ _actor[1].inventory[INV_BOOT] = 0;
+ _actor[1].inventory[INV_HAND] = 0;
+ _actor[1].inventory[INV_DUST] = 0;
+ _actor[1].probability = 5;
+ _actor[1].enemyHandler = -1;
+
+ init_actStruct(1, 0, 14, 1, 1, 0, 0, 0);
+ init_actStruct(1, 1, 15, 1, 1, 0, 0, 0);
+ init_actStruct(1, 2, 13, 1, 1, 0, 0, 0);
+ init_actStruct(1, 3, 13, 1, 1, 0, 0, 0);
+
+ for (i = 0; i < 10; i++)
+ for (j = 0; j < 10; j++)
+ _enemyState[i][j] = 0;
+}
+
+void Insane::init_actStruct(int actornum, int actnum, int32 actorval, byte state,
+ int32 room, int32 animTilt, int32 tilt, int32 frame) {
+ _actor[actornum].act[actnum].actor = actorval;
+ _actor[actornum].act[actnum].state = state;
+ _actor[actornum].act[actnum].room = room;
+ _actor[actornum].act[actnum].animTilt = animTilt;
+ _actor[actornum].act[actnum].tilt = tilt;
+ _actor[actornum].act[actnum].frame = frame;
+}
+
+void Insane::init_enemyStruct(int n, int32 handler, int32 initializer,
+ int16 occurences, int32 maxdamage, int32 isEmpty,
+ int32 weapon, int32 sound, const char *filename,
+ int32 costume4, int32 costume6, int32 costume5,
+ int16 costumevar, int32 maxframe, int32 apprAnim) {
+ assert(strlen(filename) < 20);
+
+ _enemy[n].handler = handler;
+ _enemy[n].initializer = initializer;
+ _enemy[n].occurences = occurences;
+ _enemy[n].maxdamage = maxdamage;
+ _enemy[n].isEmpty = isEmpty;
+ _enemy[n].weapon = weapon;
+ _enemy[n].sound = sound;
+ strncpy(_enemy[n].filename, filename, 20);
+ _enemy[n].costume4 = costume4;
+ _enemy[n].costume6 = costume6;
+ _enemy[n].costume5 = costume5;
+ _enemy[n].costumevar = costumevar;
+ _enemy[n].maxframe = maxframe;
+ _enemy[n].apprAnim = apprAnim;
+}
+
+void Insane::init_fluConfStruct(int n, int sceneId, byte **fluPtr,
+ const char *filenamePtr, int startFrame, int numFrames) {
+ _fluConf[n].sceneId = sceneId;
+ _fluConf[n].fluPtr = fluPtr;
+ _fluConf[n].filenamePtr = filenamePtr;
+ _fluConf[n].startFrame = startFrame;
+ _fluConf[n].numFrames = numFrames;
+}
+
+void Insane::init_scenePropStruct(int32 n, int32 n1, int32 actornum, int32 sound, int32 trsId,
+ byte r, byte g, byte b, int32 counter, int32 maxCounter,
+ int32 index) {
+ _sceneProp[n].actor = actornum; // main actor number, -1 if not applicable
+ _sceneProp[n].sound = sound;
+ _sceneProp[n].trsId = trsId;
+ _sceneProp[n].r = r;
+ _sceneProp[n].g = g;
+ _sceneProp[n].b = b;
+ _sceneProp[n].counter = counter;
+ _sceneProp[n].maxCounter = maxCounter;
+ _sceneProp[n].index = index;
+}
+
+void Insane::setBenAnimation(int32 actornum, int anim) {
+ if (anim <= 12)
+ smlayer_setActorFacing(actornum, 1,
+ actorAnimationData[_actor[actornum].weaponClass * 7 + anim - 6], 180);
+}
+
+void Insane::setEnemyAnimation(int32 actornum, int anim) {
+ int d = 0;
+
+ if (_currEnemy == EN_VULTM1)
+ d = 14;
+
+ if (anim <= 12)
+ smlayer_setActorFacing(actornum, 1,
+ actorAnimationData[_actor[actornum].weaponClass * 7 + anim - 6] + d, 180);
+}
+
+int32 Insane::processMouse(void) {
+ int32 buttons = 0;
+
+ _enemyState[EN_BEN][0] = _vm->_mouse.x;
+ _enemyState[EN_BEN][1] = _vm->_mouse.y;
+
+ buttons = _vm->VAR(_vm->VAR_LEFTBTN_HOLD) ? 1 : 0;
+ buttons |= _vm->VAR(_vm->VAR_RIGHTBTN_HOLD) ? 2 : 0;
+
+ return buttons;
+}
+
+int32 Insane::processKeyboard(void) {
+ int32 retval = 0;
+ int dx = 0, dy = 0;
+ int tmpx, tmpy;
+
+ if (_vm->getKeyState(0x14f) || _vm->getKeyState(0x14b) || _vm->getKeyState(0x147))
+ dx--;
+
+ if (_vm->getKeyState(0x151) || _vm->getKeyState(0x14d) || _vm->getKeyState(0x149))
+ dx++;
+
+ if (_vm->getKeyState(0x147) || _vm->getKeyState(0x148) || _vm->getKeyState(0x149))
+ dy--;
+
+ if (_vm->getKeyState(0x14f) || _vm->getKeyState(0x150) || _vm->getKeyState(0x151))
+ dy++;
+
+ if (dx == _keybOldDx)
+ _velocityX += 4;
+ else
+ _velocityX = 3;
+
+ if (dy == _keybOldDy)
+ _velocityY += 4;
+ else
+ _velocityY = 2;
+
+ _keybOldDx = dx;
+ _keybOldDy = dy;
+
+ if (_velocityX > 48)
+ _velocityX = 48;
+
+ if (_velocityY > 32)
+ _velocityY = 32;
+
+ _keybX += dx * _velocityX;
+ _keybY += dy * _velocityY;
+
+ tmpx = _keybX / 4;
+ tmpy = _keybY / 4;
+
+ _keybX -= tmpx * 4;
+ _keybY -= tmpy * 4;
+
+ if (tmpx || tmpy) {
+ _enemyState[EN_BEN][0] += tmpx;
+ _enemyState[EN_BEN][1] += tmpy;
+ }
+
+ if (_vm->getKeyState(0x0d))
+ retval |= 1;
+
+ if (_vm->getKeyState(0x09))
+ retval |= 2;
+
+ return retval;
+}
+
+void Insane::readFileToMem(const char *name, byte **buf) {
+ ScummFile in;
+ uint32 len;
+
+ _vm->openFile(in, name);
+ len = in.size();
+ *buf = (byte *)malloc(len);
+ in.read(*buf, len);
+}
+
+void Insane::startVideo(const char *filename, int num, int argC, int frameRate,
+ int doMainLoop, byte *fluPtr, int32 startFrame) {
+ int32 offset = 0;
+
+ _smush_curFrame = 0;
+ _smush_isSanFileSetup = 0;
+ _smush_setupsan4 = 0;
+ _smush_smushState = 0;
+ _smush_setupsan1 = 0;
+ _smush_setupsan17 = 0;
+
+ if (fluPtr) {
+ offset = smush_setupSanWithFlu(filename, 0, -1, -1, 0, fluPtr, startFrame);
+ } else {
+ smush_setupSanFromStart(filename, 0, -1, -1, 0);
+ }
+
+ _player->play(filename, offset, startFrame);
+}
+
+void Insane::smush_warpMouse(int x, int y, int buttons) {
+ _player->warpMouse(x, y, buttons);
+}
+
+void Insane::putActors(void) {
+ smlayer_putActor(0, 2, _actor[0].x, _actor[0].y1, _smlayer_room);
+ smlayer_putActor(0, 0, _actor[0].x, _actor[0].y1, _smlayer_room);
+ smlayer_putActor(0, 1, _actor[0].x, _actor[0].y1, _smlayer_room);
+ smlayer_putActor(1, 2, _actor[0].x, _actor[0].y1, _smlayer_room);
+ smlayer_putActor(1, 0, _actor[0].x, _actor[0].y1, _smlayer_room);
+ smlayer_putActor(1, 1, _actor[0].x, _actor[0].y1, _smlayer_room);
+}
+
+void Insane::readState(void) { // PATCH
+
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ _actor[0].inventory[INV_CHAIN] = 0;
+ _actor[0].inventory[INV_CHAINSAW] = 0;
+ _actor[0].inventory[INV_MACE] = 0;
+ _actor[0].inventory[INV_2X4] = 0;
+ _actor[0].inventory[INV_WRENCH] = 1;
+ _actor[0].inventory[INV_DUST] = 0;
+ _actor[0].inventory[INV_HAND] = 1;
+ _actor[0].inventory[INV_BOOT] = 0;
+ _smlayer_room2 = 13;
+ } else {
+ _actor[0].inventory[INV_CHAIN] = readArray(50) != 0;
+ _actor[0].inventory[INV_CHAINSAW] = readArray(51) != 0;
+ _actor[0].inventory[INV_MACE] = readArray(52) != 0;
+ _actor[0].inventory[INV_2X4] = readArray(53) != 0;
+ _actor[0].inventory[INV_WRENCH] = readArray(54) != 0;
+ _actor[0].inventory[INV_DUST] = readArray(55) != 0;
+ _actor[0].inventory[INV_HAND] = 1;
+ _actor[0].inventory[INV_BOOT] = 1;
+ _smlayer_room = readArray(320);
+ _smlayer_room2 = readArray(321);
+ _posBrokenTruck = readArray(322);
+ _posVista = readArray(323);
+ _val57d = readArray(324);
+ _posCave = readArray(325);
+ _posBrokenCar = readArray(326);
+ _val54d = readArray(327);
+ _posFatherTorque = readArray(328);
+ _enemy[EN_TORQUE].occurences = readArray(337);
+ _enemy[EN_ROTT1].occurences = readArray(329);
+ _enemy[EN_ROTT2].occurences = readArray(330);
+ _enemy[EN_ROTT3].occurences = readArray(331);
+ _enemy[EN_VULTF1].occurences = readArray(332);
+ _enemy[EN_VULTM1].occurences = readArray(333);
+ _enemy[EN_VULTF2].occurences = readArray(334);
+ _enemy[EN_VULTM2].occurences = readArray(335);
+ _enemy[EN_CAVEFISH].occurences = readArray(336);
+ _enemy[EN_VULTM2].isEmpty = readArray(340);
+ _enemy[EN_VULTF2].isEmpty = readArray(339);
+ _enemy[EN_CAVEFISH].isEmpty = readArray(56);
+
+ // FIXME
+ // Some sanity checks. There were submitted savefiles where these values were wrong
+ // Still it is unknown what leads to this state. Most probably it is memory
+ // overwrite
+ if (_enemy[EN_VULTM2].isEmpty != readArray(7)) {
+ warning("Wrong INSANE parameters for EN_VULTM2 (%d %d)",
+ _enemy[EN_VULTM2].isEmpty, readArray(7));
+ _enemy[EN_VULTM2].isEmpty = readArray(7);
+ }
+
+ if (_enemy[EN_VULTF2].isEmpty != (_actor[0].inventory[INV_CHAINSAW] != 0)) {
+ warning("Wrong INSANE parameters for EN_VULTF2 (%d %d)",
+ _enemy[EN_VULTF2].isEmpty, _actor[0].inventory[INV_CHAINSAW]);
+ _enemy[EN_VULTF2].isEmpty = (_actor[0].inventory[INV_CHAINSAW] != 0);
+ }
+
+ // FIXME
+ // This used to be here but.
+ // - bootparam 551 gives googles without cavefish met
+ // - when you get the ramp, googles disappear, but you already won the cavefish
+ // Incorrect situation would be
+ // you won cavefish, don't have googles, don't have ramp
+ //
+ // So if you find out what how to check ramp presense, feel free to add check here
+ // (beware of FT ver a and ver b. In version b var311 is inserted and all vars >311
+ // are shifted),
+ //
+ //if (_enemy[EN_CAVEFISH].isEmpty != readArray(8))
+ // error("Wrong INSANE parameters for EN_CAVEFISH (%d %d). Please, report this",
+ // _enemy[EN_CAVEFISH].isEmpty, readArray(8));
+ }
+}
+
+void Insane::setupValues(void) {
+ _actor[0].x = 160;
+ _actor[0].y = 200;
+ _actor[0].tilt = 0;
+ _actor[0].field_8 = 1;
+ _actor[0].frame = 0;
+ _actor[0].act[2].state = 1;
+ _actor[0].act[0].state = 1;
+ _actor[0].act[1].state = 0;
+ _actor[0].act[2].room = 1;
+ _actor[0].act[1].room = 0;
+ _actor[0].act[0].room = 0;
+ _actor[0].cursorX = 0;
+ _actor[0].lost = false;
+ _currEnemy = -1;
+ _approachAnim = -1;
+ smush_warpMouse(160, 100, -1);
+}
+
+bool Insane::idx1Compare(void) {
+ return _objArray1Idx == _objArray1Idx2;
+}
+
+bool Insane::idx2Compare(void) {
+ return _objArray2Idx == _objArray2Idx2;
+}
+
+int32 Insane::idx1Tweak(void) {
+ _objArray1Idx++;
+ if (_objArray1Idx >= 100)
+ _objArray1Idx = 0;
+
+ return _objArray1[_objArray1Idx];
+}
+
+int32 Insane::idx2Tweak(void) {
+ if (!_idx2Exceeded)
+ if (_objArray2Idx >= _objArray2Idx2)
+ return false;
+
+ _objArray2Idx++;
+ if (_objArray2Idx >= 100) {
+ _idx2Exceeded = 0;
+ _objArray2Idx = 0;
+ }
+ return _objArray2[_objArray2Idx];
+}
+
+void Insane::smush_setToFinish(void) {
+ debugC(DEBUG_INSANE, "Video is set to finish");
+ _vm->_smushVideoShouldFinish = true;
+}
+
+// smlayer_stopSound
+void Insane::smlayer_stopSound(int idx) {
+ _vm->_imuseDigital->stopSound(readArray(idx));
+}
+
+void Insane::switchSceneIfNeeded(void) {
+ if (_needSceneSwitch && !_smush_isSanFileSetup) {
+ putActors();
+ stopSceneSounds(_currSceneId);
+ _tempSceneId = _currSceneId;
+ _currSceneId = _temp2SceneId;
+ _needSceneSwitch = false;
+ loadSceneData(_temp2SceneId, 0, 1);
+
+ if (loadSceneData(_temp2SceneId, 0, 2)) {
+ setSceneCostumes(_temp2SceneId);
+ _sceneData2Loaded = 0;
+ _sceneData1Loaded = 0;
+ return;
+ }
+
+ _sceneData2Loaded = 1;
+ if (_temp2SceneId == 13 || _temp2SceneId == 3)
+ _isBenCut = 1;
+ }
+
+ if (_sceneData2Loaded && !_sceneData1Loaded) {
+ setSceneCostumes(_currSceneId);
+ _sceneData2Loaded = 0;
+ }
+}
+
+void Insane::prepareScenePropScene(int32 scenePropNum, bool arg_4, bool arg_8) {
+ static const int scenePropIdx[58] = {0, 12, 14, 18, 20, 22, 24, 26, 28, 30, 34,
+ 36, 38, 40, 42, 44, 46, 48, 50, 55, 57, 59, 61, 63, 65, 67, 71,
+ 73, 75, 77, 79, 81, 83, 85, 89, 93, 95, 97, 99, 101, 103, 105, 107,
+ 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137};
+
+ int tmp, idx = scenePropIdx[scenePropNum];
+
+ debugC(DEBUG_INSANE, "Insane::prepareScenePropScene(%d, %d, %d)", scenePropNum, arg_4, arg_8);
+
+ if (((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) || !loadScenePropSounds(idx))
+ return;
+
+ _actor[0].defunct = arg_4;
+ _actor[1].defunct = arg_8;
+ _currScenePropIdx = idx;
+ _sceneProp[idx + 1].counter = 0;
+ _currScenePropSubIdx = 1;
+ if (_sceneProp[idx + 1].trsId)
+ _currTrsMsg = handleTrsTag(_sceneProp[idx + 1].trsId);
+ else
+ _currTrsMsg = 0;
+
+ tmp = _sceneProp[idx + 1].actor;
+ if (tmp != -1) {
+ _actor[tmp].field_54 = 1;
+ _actor[tmp].act[3].state = 117;
+ _actor[tmp].scenePropSubIdx = _currScenePropSubIdx;
+ }
+}
+
+int Insane::smush_changeState(int state) {
+ if (state == 2) {
+ if (_smush_smushState == 0)
+ _smush_smushState = 1;
+ else
+ _smush_smushState = 0;
+ } else if (state == 4) {
+ if (_smush_smushState)
+ _smush_smushState = 3;
+ } else if (state != 5) {
+ _smush_smushState = state;
+ }
+ return _smush_smushState;
+}
+
+void Insane::queueSceneSwitch(int32 sceneId, byte *fluPtr, const char *filename,
+ int32 arg_C, int32 arg_10, int32 startFrame, int32 numFrames) {
+ int32 tmp;
+
+ debugC(DEBUG_INSANE, "queueSceneSwitch(%d, *, %s, %d, %d, %d, %d)", sceneId, filename, arg_C, arg_10,
+ startFrame, numFrames);
+ if (_needSceneSwitch || _sceneData1Loaded)
+ return;
+
+ if (fluPtr) {
+ tmp = ((int)startFrame/30+1)*30;
+ if (tmp >= numFrames)
+ tmp = 0;
+
+ smush_setupSanWithFlu(filename, arg_C|32, -1, -1, 0, fluPtr, tmp);
+ } else {
+ smush_setupSanFromStart(filename, arg_C|32, -1, -1, 0);
+ }
+ _needSceneSwitch = true;
+ _temp2SceneId = sceneId;
+}
+
+void Insane::smush_rewindCurrentSan(int arg_0, int arg_4, int arg_8) {
+ debugC(DEBUG_INSANE, "smush_rewindCurrentSan(%d, %d, %d)", arg_0, arg_4, arg_8);
+ _smush_setupsan2 = arg_0;
+
+ smush_setupSanFile(0, 0, 0);
+ _smush_isSanFileSetup = 1;
+ smush_setFrameSteps(arg_4, arg_8);
+
+ _smush_curFrame = 0; // HACK
+}
+
+int32 Insane::weaponMaxRange(int32 actornum) {
+ static int map[8] = {135, 125, 130, 125, 120, 104, 104, 104};
+
+ if (_actor[actornum].weapon == -1)
+ return 104;
+
+ return map[_actor[actornum].weapon];
+}
+
+int32 Insane::weaponMinRange(int32 actornum) {
+ static int map[8] = {80, 40, 80, 40, 80, 80, 40, 50};
+
+ if (_actor[actornum].weapon == -1)
+ return 40;
+
+ return map[_actor[actornum].weapon];
+}
+
+int32 Insane::weaponDamage(int32 actornum) {
+ static int map[8] = {20, 300, 25, 40, 15, 10, 10, 5};
+
+ if (_actor[actornum].weapon == -1)
+ return 10;
+
+ return map[_actor[actornum].weapon];
+}
+
+void Insane::reinitActors(void) {
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ smlayer_setActorCostume(0, 2, readArray(11));
+ smlayer_setActorCostume(0, 0, readArray(13));
+ smlayer_setActorCostume(0, 1, readArray(12));
+ } else {
+ smlayer_setActorCostume(0, 2, readArray(12));
+ smlayer_setActorCostume(0, 0, readArray(14));
+ smlayer_setActorCostume(0, 1, readArray(13));
+ }
+ smlayer_setActorLayer(0, 1, 1);
+ smlayer_setActorLayer(0, 2, 5);
+ smlayer_setActorLayer(0, 0, 10);
+ _actor[0].weapon = INV_HAND;
+ _actor[0].weaponClass = 2;
+ _actor[0].animWeaponClass = 0;
+ _actor[0].newFacingFlag = 2;
+ _actor[0].curFacingFlag = 0;
+ _actor[0].tilt = 0;
+ _actor[0].field_8 = 1;
+ _actor[0].act[2].state = 1;
+ _actor[0].act[2].animTilt = 1;
+ _actor[0].act[0].state = 0;
+ _actor[0].act[1].state = 1;
+ _actor[0].act[2].room = 1;
+ _actor[0].act[1].room = 1;
+ _actor[0].act[0].room = 1;
+ _actor[0].cursorX = 0;
+}
+
+int Insane::calcTilt(int speed) {
+ const int tilt[7] = {-5, -4, -2, 0, 2, 4, 5};
+ if (speed + 3 > 6)
+ return 0;
+
+ return tilt[speed + 3];
+}
+
+bool Insane::actor1StateFlags(int state) {
+ // This is compressed table. It contains indexes where state
+ // changes. I.e. 0-33: true, 34-38: false 39-72: true, etc.
+ const int spans[] = {0, 34, 39, 73, 89, 90, 92, 93, 99, 100, 117};
+ bool retvalue = 0;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(spans); i++) {
+ retvalue = !retvalue;
+ if (spans[i] <= state)
+ break;
+ }
+ return retvalue;
+}
+
+void Insane::escapeKeyHandler(void) {
+ struct fluConf *flu;
+
+ // Demos have just one scene
+ if (!_insaneIsRunning || _vm->_features & GF_DEMO) {
+ smush_setToFinish();
+ return;
+ }
+
+ if (_needSceneSwitch || _keyboardDisable)
+ return;
+
+ debugC(DEBUG_INSANE, "scene: %d", _currSceneId);
+ switch (_currSceneId) {
+ case 1:
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0, _continueFrame1, 1300);
+ writeArray(9, 0);
+ break;
+ case 18:
+ queueSceneSwitch(17, _smush_minedrivFlu, "minedriv.san", 64, 0, _continueFrame1, 1300);
+ writeArray(9, 1);
+ break;
+ case 2:
+ flu = &_fluConf[14 + _iactSceneId2];
+ queueSceneSwitch(flu->sceneId, *flu->fluPtr, flu->filenamePtr, 64, 0,
+ flu->startFrame, flu->numFrames);
+ break;
+ case 3:
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0, _continueFrame, 1300);
+ break;
+ case 4:
+ if (_needSceneSwitch)
+ return;
+
+ if (readArray(6)) {
+ if (readArray(4)) {
+ queueSceneSwitch(14, 0, "hitdust2.san", 64, 0, 0, 0);
+ } else {
+ queueSceneSwitch(14, 0, "hitdust4.san", 64, 0, 0, 0);
+ }
+ } else {
+ if (readArray(4)) {
+ queueSceneSwitch(14, 0, "hitdust1.san", 64, 0, 0, 0);
+ } else {
+ queueSceneSwitch(14, 0, "hitdust3.san", 64, 0, 0, 0);
+ }
+ }
+ break;
+ case 5:
+ if (readArray(4)) {
+ if (_needSceneSwitch)
+ return;
+ queueSceneSwitch(15, 0, "vistthru.san", 64, 0, 0, 0);
+ } else {
+ writeArray(1, _posVista);
+ smush_setToFinish();
+ }
+ break;
+ case 6:
+ if (readArray(4)) {
+ if (_needSceneSwitch)
+ return;
+ queueSceneSwitch(15, 0, "chasthru.san", 64, 0, 0, 0);
+ } else {
+ if (readArray(5)) {
+ writeArray(1, _val57d);
+ smush_setToFinish();
+ } else {
+ writeArray(4, 1);
+ queueSceneSwitch(15, 0, "chasout.san", 64, 0, 0, 0);
+ }
+ }
+ break;
+ case 8:
+ flu = &_fluConf[7 + _iactSceneId2];
+ queueSceneSwitch(flu->sceneId, *flu->fluPtr, flu->filenamePtr, 64, 0,
+ flu->startFrame, flu->numFrames);
+ break;
+ case 7:
+ flu = &_fluConf[0 + _iactSceneId2];
+ queueSceneSwitch(flu->sceneId, *flu->fluPtr, flu->filenamePtr, 64, 0,
+ flu->startFrame, flu->numFrames);
+ break;
+ case 23:
+ _actor[0].damage = 0;
+ queueSceneSwitch(21, 0, "rottfite.san", 64, 0, 0, 0);
+ break;
+ case 9:
+ _actor[0].damage = 0;
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0, _continueFrame, 1300);
+ break;
+ case 10:
+ _actor[0].damage = 0;
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0, _continueFrame1, 1300);
+ break;
+ case 13:
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0, _continueFrame, 1300);
+ break;
+ case 24:
+ queueSceneSwitch(21, 0, "rottfite.san", 64, 0, 0, 0);
+ break;
+ case 16:
+ writeArray(4, 0);
+ writeArray(5, 1);
+ writeArray(1, _posBrokenCar);
+ writeArray(3, _posBrokenTruck);
+ smush_setToFinish();
+ break;
+ case 15:
+ switch (_tempSceneId) {
+ case 5:
+ queueSceneSwitch(6, 0, "toranch.san", 64, 0, 0, 530);
+ break;
+ case 6:
+ queueSceneSwitch(4, 0, "tovista1.san", 64, 0, 0, 230);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+bool Insane::actor0StateFlags1(int state) {
+ const int spans[] = {0, 2, 34, 35, 39, 69, 98, 100, 117};
+ bool retvalue = 1;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(spans); i++) {
+ retvalue = !retvalue;
+ if (spans[i] >= state)
+ break;
+ }
+ return retvalue;
+}
+
+bool Insane::actor0StateFlags2(int state) {
+ const int spans[] = {0, 10, 14, 34, 39, 73, 75, 79, 81, 90, 93, 94,
+ 98, 100, 117, 133, 136, 153, 158, 200, 202, 209, 212, 213, 217,
+ 219, 236, 256, 259, 272, 277, 311, 312, 315, 317, 328, 331, 332,
+ 336, 338, 355, 379, 382, 391, 396, 440, 441, 447, 450, 451, 455,
+ 457, 474, 502, 505, 510, 515, 549, 553, 566, 569, 570, 574, 576,
+ 593, 601, 604, 629, 634, 680, 682, 685, 688, 689, 693, 695, 712,
+ 716, 718, 748, 753, 787, 788, 804, 807, 808, 812, 814, 831, 863,
+ 866, 867, 872, 920, 922, 923, 926, 927, 931, 933, 950};
+ bool retvalue = 1;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(spans); i++) {
+ retvalue = !retvalue;
+ if (spans[i] >= state)
+ break;
+ }
+ return retvalue;
+}
+
+// smlayer_loadSound1 && smlayer_loadSound2
+int Insane::smlayer_loadSound(int id, int flag, int phase) {
+ int resid;
+
+ if (phase == 1) {
+ if (_idx2Exceeded != 0)
+ if (_objArray2Idx >= _objArray2Idx2)
+ return 0;
+ }
+ resid = readArray(id);
+
+ if (!resid && phase == 2)
+ return 0;
+
+ if (phase == 2)
+ _vm->ensureResourceLoaded(rtSound, resid);
+
+ _vm->res.setResourceCounter(rtSound, resid, 1);
+
+ if (phase == 1) {
+ _objArray2Idx2++;
+ _objArray2[_objArray2Idx2] = id;
+ if (_objArray2Idx2 >= 100) {
+ _idx2Exceeded = 1;
+ _objArray2Idx2 = 0;
+ }
+ }
+ return resid;
+}
+
+// smlayer_loadCostume1 && smlayer_loadCostume2
+int Insane::smlayer_loadCostume(int id, int phase) {
+ int resid;
+ resid = readArray(id);
+
+ if (!resid)
+ return 0;
+
+ _vm->ensureResourceLoaded(rtCostume, resid);
+ _vm->res.setResourceCounter(rtCostume, resid, 1);
+
+ // smlayer_lock(rtCostume, resid); // FIXME
+
+ if (phase == 1) {
+ _objArray1Idx2++;
+ _objArray1[_objArray1Idx2] = id;
+ if (_objArray1Idx2 == 100)
+ _objArray1Idx2 = 0;
+ }
+
+ return resid;
+}
+
+void Insane::smlayer_setActorCostume(int actornum, int actnum, int costume) {
+ Actor *a = _vm->derefActor(_actor[actornum].act[actnum].actor, "smlayer_setActorCostume");
+ a->setActorCostume(costume);
+ a->setDirection(180);
+ a->startAnimActor(1);
+ _actor[actornum].act[actnum].frame = 0;
+}
+
+void Insane::smlayer_putActor(int actornum, int actnum, int x, int y, byte room) {
+ Actor *a = _vm->derefActor(_actor[actornum].act[actnum].actor, "smlayer_putActor");
+ a->putActor(x, y, room);
+}
+
+void Insane::smlayer_setActorLayer(int actornum, int actnum, int layer) {
+ Actor *a = _vm->derefActor(_actor[actornum].act[actnum].actor, "smlayer_setActorLayer");
+ a->_layer = layer;
+}
+
+void Insane::smlayer_setFluPalette(byte *pal, int shut_flag) {
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ return;
+
+ // if (shut_flag)
+ // // FIXME: shut colors and make picture appear smoothly
+ // SmushPlayer::setPalette(pal);
+ // else
+ _player->setPalette(pal);
+}
+
+bool Insane::smlayer_isSoundRunning(int32 sound) {
+ return _vm->_imuseDigital->getSoundStatus(readArray(sound)) != 0;
+}
+
+bool Insane::smlayer_startSfx(int32 sound) {
+ if (smlayer_loadSound(sound, 0, 2)) {
+ _vm->_imuseDigital->startSfx(readArray(sound), 40);
+ return true;
+ } else
+ return false;
+}
+
+bool Insane::smlayer_startVoice(int32 sound) {
+ if (smlayer_loadSound(sound, 0, 2)) {
+ _vm->_imuseDigital->startSfx(readArray(sound), 126);
+ return true;
+ } else
+ return false;
+}
+
+void Insane::smlayer_soundSetPan(int32 soundId, int32 pan) {
+ _vm->_imuseDigital->setPan(soundId, pan);
+}
+
+void Insane::smlayer_soundSetPriority(int32 soundId, int32 priority) {
+ _vm->_imuseDigital->setPriority(soundId, priority);
+}
+
+void Insane::smlayer_drawSomething(byte *renderBitmap, int32 codecparam,
+ int32 x, int32 y, int32 arg_10, NutRenderer *nutfile,
+ int32 c, int32 arg_1C, int32 arg_20) {
+ nutfile->drawFrame(renderBitmap, c, x, y);
+}
+
+void Insane::smlayer_overrideDrawActorAt(byte *arg_0, byte arg_4, byte arg_8) {
+ // FIXME: doublecheck
+
+ // noop in current implementation
+}
+
+void Insane::smlayer_showStatusMsg(int32 arg_0, byte *renderBitmap, int32 codecparam,
+ int32 pos_x, int32 pos_y, int32 arg_14, int32 arg_18,
+ int32 flags, const char *formatString, const char *strng) {
+ SmushFont *sf = _player->_sf[0];
+ int color = 1;
+ int32 top = 0;
+ char *str = NULL, *string;
+ int len = strlen(formatString) + strlen(strng) + 16;
+
+ string = (char *)malloc(len);
+ str = string;
+
+ while (*strng == '/') {
+ strng++; // For text resources
+ }
+
+ snprintf(str, len, formatString, strng);
+
+ while (str[0] == '^') {
+ switch (str[1]) {
+ case 'f':
+ {
+ int id = str[3] - '0';
+ str += 4;
+ sf = _player->_sf[id];
+ }
+ break;
+ case 'c':
+ {
+ color = str[4] - '0' + 10 *(str[3] - '0');
+ str += 5;
+ }
+ break;
+ default:
+ error("invalid escape code in text string");
+ }
+ }
+
+ assert(sf != NULL);
+ sf->setColor(color);
+
+ // flags:
+ // bit 0 - center 1
+ // bit 1 - not used 2
+ // bit 2 - ??? 4
+ // bit 3 - wrap around 8
+ switch (flags) {
+ case 0:
+ sf->drawString(str, renderBitmap, _player->_width, _player->_height, pos_x, pos_y, false);
+ break;
+ case 1:
+ sf->drawString(str, renderBitmap, _player->_width, _player->_height, pos_x, MAX(pos_y, top), true);
+ break;
+ case 5:
+ sf->drawStringWrap(str, renderBitmap, _player->_width, _player->_height, pos_x, pos_y, 10, 300, true);
+ break;
+ default:
+ error("Insane::smlayer_showStatusMsg. Not handled flags: %d", flags);
+ }
+ free (string);
+}
+
+void Insane::procSKIP(Chunk &b) {
+ int16 par1, par2;
+ _player->_skipNext = false;
+
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ _player->checkBlock(b, TYPE_SKIP, 2);
+ par1 = b.getWord();
+ if (isBitSet(par1))
+ _player->_skipNext = true;
+ return;
+ }
+
+ _player->checkBlock(b, TYPE_SKIP, 4);
+
+ par1 = b.getWord();
+ par2 = b.getWord();
+
+
+ if (!par2) {
+ if (isBitSet(par1))
+ _player->_skipNext = true;
+ return;
+ }
+
+ if (isBitSet(par1) != isBitSet(par2)) {
+ _player->_skipNext = true;
+ return;
+ }
+}
+
+bool Insane::isBitSet(int n) {
+ assert (n < 0x80);
+
+ return (_iactBits[n] != 0);
+}
+
+void Insane::setBit(int n) {
+ assert (n < 0x80);
+
+ _iactBits[n] = 1;
+}
+
+void Insane::clearBit(int n) {
+ assert (n < 0x80);
+
+ _iactBits[n] = 0;
+}
+
+void Insane::smlayer_setActorFacing(int actornum, int actnum, int frame, int direction) {
+ if (_actor[actornum].act[actnum].room) {
+ Actor *a = _vm->derefActor(_actor[actornum].act[actnum].actor, "smlayer_setActorFacing");
+ a->setDirection(direction);
+ a->startAnimActor(frame);
+ _actor[actornum].act[actnum].frame = 0;
+ }
+}
+
+const char *Insane::handleTrsTag(int32 trsId) {
+ debugC(DEBUG_INSANE, "Insane::handleTrsTag(%d)", trsId);
+ return _player->getString(trsId);
+}
+
+bool Insane::smush_eitherNotStartNewFrame(void) {
+ if (_smush_setupsan17)
+ return false;
+
+ if (_smush_isSanFileSetup) {
+ if (_smush_frameStep < 0)
+ return false;
+
+ if (_smush_curFrame > _smush_frameStep + _smush_frameNum2)
+ return true;
+ else
+ return false;
+ } else {
+ if (_smush_frameNum1 < 0)
+ return false;
+
+ if (_smush_curFrame >= _smush_frameNum1) {
+ _smush_frameNum1 = -1;
+ return false;
+ } else
+ return true;
+ }
+}
+
+int32 Insane::getLastKey(bool arg_0) {
+ return _vm->_lastKeyHit;
+}
+
+bool Insane::smlayer_actorNeedRedraw(int actornum, int actnum) {
+ Actor *a = _vm->derefActor(_actor[actornum].act[actnum].actor, "smlayer_actorNeedRedraw");
+
+ return a->_needRedraw;
+}
+
+int32 Insane::readArray (int item) {
+ return _vm->readArray(_numberArray, 0, item);
+}
+
+void Insane::writeArray(int item, int value) {
+ _vm->writeArray(_numberArray, 0, item, value);
+}
+
+int32 Insane::smush_setupSanWithFlu(const char *filename, int32 setupsan2, int32 step1,
+ int32 step2, int32 setupsan1, byte *fluPtr,
+ int32 numFrames) {
+ byte *tmp = fluPtr;
+ int32 offset;
+
+ debugC(DEBUG_INSANE, "smush_setupSanWithFlu(%s, %d, %d, %d, %d, %p, %d)", filename, setupsan2,
+ step1, step2, setupsan1, fluPtr, numFrames);
+
+ _smush_setupsan1 = setupsan1;
+
+ /* skip FLUP marker */
+ if (READ_BE_UINT32(fluPtr) == 'FLUP')
+ tmp += 8;
+
+ _smush_setupsan2 = setupsan2;
+
+ if (tmp[2] <= 1) {
+ /* 0x300 -- palette, 0x8 -- header */
+ offset = READ_LE_UINT32(tmp + 0x308 + numFrames*4);
+ smush_setupSanFile(filename, offset, numFrames);
+ memcpy(_smush_earlyFluContents, tmp+2, 0x306);
+ _smush_earlyFluContents[0x30e] = 0;
+ _smush_earlyFluContents[0x30f] = 0;
+ _smush_earlyFluContents[0x310] = 0;
+ _smush_earlyFluContents[0x311] = 0;
+ _smush_earlyFluContents[0x306] = 0;
+ _smush_earlyFluContents[0x307] = 0;
+ } else {
+ offset = READ_LE_UINT32(tmp + 0x31c + numFrames*4);
+ smush_setupSanFile(filename, offset, numFrames);
+ memcpy(_smush_earlyFluContents, tmp+2, 0x31a);
+ }
+ _smush_isSanFileSetup = 1;
+ _smush_setupsan4 = 1;
+ _smush_curFrame = numFrames;
+ smush_setFrameSteps(step1, step2);
+ smush_warpMouse(160, 100, -1);
+
+ return offset;
+}
+
+void Insane::smush_setupSanFromStart(const char *filename, int32 setupsan2, int32 step1,
+ int32 step2, int32 setupsan1) {
+ debugC(DEBUG_INSANE, "Insane::smush_setupFromStart(%s)", filename);
+ _smush_setupsan1 = setupsan1;
+ _smush_setupsan2 = setupsan2;
+ smush_setupSanFile(filename, 0, 0);
+ _smush_isSanFileSetup = 1;
+ smush_setFrameSteps(step1, step2);
+ smush_warpMouse(160, 100, -1);
+}
+
+void Insane::smush_setFrameSteps(int32 step1, int32 step2) {
+ _smush_frameNum2 = _smush_curFrame;
+ _smush_frameNum1 = step2;
+ _smush_frameStep = step1;
+}
+
+void Insane::smush_setupSanFile(const char *filename, int32 offset, int32 contFrame) {
+ debugC(DEBUG_INSANE, "Insane::smush_setupSanFile(%s, %x, %d)", filename, offset, contFrame);
+
+ _player->seekSan(filename, offset, contFrame);
+}
+
+}
diff --git a/engines/scumm/insane/insane.h b/engines/scumm/insane/insane.h
new file mode 100644
index 0000000000..36567ae06e
--- /dev/null
+++ b/engines/scumm/insane/insane.h
@@ -0,0 +1,456 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(INSANE_H) && !defined(DISABLE_SCUMM_7_8)
+#define INSANE_H
+
+#include "base/engine.h"
+#include "scumm/intern.h"
+#include "scumm/nut_renderer.h"
+
+#include "scumm/smush/smush_player.h"
+#include "scumm/smush/chunk.h"
+
+namespace Scumm {
+
+#define INV_CHAIN 0
+#define INV_CHAINSAW 1
+#define INV_MACE 2
+#define INV_2X4 3
+#define INV_WRENCH 4
+#define INV_BOOT 5
+#define INV_HAND 6
+#define INV_DUST 7
+
+#define EN_ROTT1 0 // rottwheeler
+#define EN_ROTT2 1 // rottwheeler
+#define EN_ROTT3 2 // rottwheeler
+#define EN_VULTF1 3 // vulture (redhead female1)
+#define EN_VULTM1 4 // vulture (male with glasses)
+#define EN_VULTF2 5 // vulture (redhead female2)
+#define EN_VULTM2 6 // vulture (initialized as rottwheeler) (male)
+#define EN_CAVEFISH 7 // Cavefish Maximum Fish
+#define EN_TORQUE 8 // Father Torque
+#define EN_BEN 9 // used only with handler
+
+class Insane {
+ public:
+ Insane(ScummEngine_v6 *scumm);
+ ~Insane();
+
+ void setSmushParams(int speed);
+ void runScene(int arraynum);
+
+ void procPreRendering(void);
+ void procPostRendering(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags);
+ void procSKIP(Chunk &b);
+ void escapeKeyHandler(void);
+
+ private:
+
+ ScummEngine_v6 *_vm;
+ SmushPlayer *_player;
+
+ int32 _speed;
+ bool _insaneIsRunning;
+
+ int32 _numberArray;
+ int32 _emulTimerId;
+ int32 _emulateInterrupt;
+ byte _errbuf[0x7d0];
+ int32 _flag1d;
+ int32 _mainTimerId;
+ int32 _objArray1Idx;
+ int32 _objArray1Idx2;
+ int32 _objArray1[101];
+ int32 _objArray2Idx;
+ int32 _objArray2Idx2;
+ int32 _objArray2[101];
+ byte _currSceneId;
+ int32 _timer1Flag;
+ int32 _timer3Id;
+ int32 _timer4Id;
+ int32 _timer6Id;
+ int32 _timer7Id;
+ int32 _timerSpriteId;
+ byte _temp2SceneId;
+ byte _tempSceneId;
+ int32 _currEnemy;
+ int32 _currScenePropIdx;
+ int32 _currScenePropSubIdx;
+ const char *_currTrsMsg;
+ int16 _sceneData2Loaded;
+ int16 _sceneData1Loaded;
+ int16 _keyboardDisable;
+ bool _needSceneSwitch;
+ int32 _idx2Exceeded;
+ int32 _lastKey;
+ bool _beenCheated;
+ bool _tiresRustle;
+ int _keybOldDx;
+ int _keybOldDy;
+ int _velocityX;
+ int _velocityY;
+ int _keybX;
+ int _keybY;
+ int32 _firstBattle;
+ bool _weaponBenJustSwitched;
+ bool _kickBenProgress;
+ int32 _battleScene;
+ bool _kickEnemyProgress;
+ bool _weaponEnemyJustSwitched;
+ int32 _enHdlVar[9][9];
+ int32 _smlayer_room;
+ int32 _smlayer_room2;
+ byte *_smush_roadrashRip; // FIXME: combine them in array
+ byte *_smush_roadrsh2Rip;
+ byte *_smush_roadrsh3Rip;
+ byte *_smush_goglpaltRip;
+ byte *_smush_tovista1Flu;
+ byte *_smush_tovista2Flu;
+ byte *_smush_toranchFlu;
+ byte *_smush_minedrivFlu;
+ byte *_smush_minefiteFlu;
+ NutRenderer *_smush_bencutNut;
+ NutRenderer *_smush_bensgoggNut;
+ NutRenderer *_smush_iconsNut;
+ NutRenderer *_smush_icons2Nut;
+ bool _smush_isSanFileSetup;
+ bool _isBenCut;
+ int _smush_smushState;
+ int _continueFrame;
+ int _continueFrame1;
+ int _counter1;
+ int _iactSceneId;
+ int _iactSceneId2;
+ int _smush_setupsan17;
+ int32 _smush_setupsan1;
+ int16 _smush_setupsan2;
+ int32 _smush_setupsan4;
+ int16 _smush_frameStep;
+ int16 _smush_curFrame;
+ int16 _smush_frameNum1;
+ int16 _smush_frameNum2;
+ byte _smush_earlyFluContents[0x31a];
+ int16 _enemyState[10][10];
+ byte _iactBits[0x80];
+ int16 _mainRoadPos;
+ int16 _posBrokenCar;
+ int16 _posBrokenTruck;
+ int16 _posFatherTorque;
+ int16 _posCave;
+ int16 _posVista;
+ bool _roadBranch;
+ bool _roadStop;
+ bool _carIsBroken;
+ bool _benHasGoggles;
+ bool _mineCaveIsNear;
+ bool _objectDetected;
+ bool _roadBumps;
+ int32 _approachAnim;
+ int32 _val54d;
+ int32 _val57d;
+ bool _val115_;
+ int32 _val211d;
+ int32 _val213d;
+ int32 _metEnemiesListTail;
+ int32 _metEnemiesList[12];
+
+ struct enemy {
+ int32 handler;
+ int32 initializer;
+ int16 occurences;
+ int32 maxdamage;
+ int32 isEmpty;
+ int32 weapon;
+ int32 sound;
+ char filename[20];
+ int32 costume4;
+ int32 costume6;
+ int32 costume5;
+ int16 costumevar;
+ int32 maxframe;
+ int32 apprAnim;
+ };
+
+ struct enemy _enemy[9];
+
+ struct fluConf {
+ int sceneId;
+ byte **fluPtr;
+ const char *filenamePtr;
+ int startFrame;
+ int numFrames;
+ };
+
+ struct fluConf _fluConf[21];
+
+ struct sceneProp {
+ int32 actor; // main actor number, -1 if not applicable
+ int32 sound;
+ int32 trsId;
+ byte r;
+ byte g;
+ byte b;
+ int32 counter;
+ int32 maxCounter;
+ int32 index;
+ };
+
+ struct sceneProp _sceneProp[139];
+
+ struct act {
+ int32 actor;
+ byte state;
+ int32 room;
+ int32 animTilt;
+ int32 tilt;
+ int32 frame;
+ };
+
+ struct actor {
+ int32 damage;
+ int32 maxdamage;
+ int32 field_8;
+ int32 frame;
+ int32 tilt;
+ int32 cursorX;
+ int32 speed;
+ int32 x;
+ int32 y;
+ int32 y1;
+ int32 x1;
+ int16 weaponClass;
+ int16 animWeaponClass;
+ int16 newFacingFlag;
+ int16 curFacingFlag;
+ bool lost;
+ bool kicking;
+ bool field_44;
+ bool field_48; // unused
+ bool defunct;
+ int32 scenePropSubIdx;
+ int32 field_54;
+ int32 runningSound;
+ int32 weapon;
+ bool inventory[8];
+ int32 probability;
+ int32 enemyHandler;
+ struct act act[4];
+ };
+
+ struct actor _actor[2];
+
+ void initvars(void);
+ void readFileToMem(const char *name, byte **buf);
+ void startVideo(const char *filename, int num, int argC, int frameRate,
+ int doMainLoop, byte *fluPtr = 0, int32 startFrame = 0);
+ void smush_warpMouse(int x, int y, int buttons);
+ void putActors(void);
+ void readState(void);
+ int initScene(int sceneId);
+ void stopSceneSounds(int sceneId);
+ void shutCurrentScene(void);
+ int loadSceneData(int scene, int flag, int phase);
+ void setSceneCostumes(int sceneId);
+ void setupValues(void);
+ void setEnemyCostumes (void);
+ void smlayer_stopSound (int idx);
+ int smlayer_loadSound(int id, int flag, int phase);
+ int smlayer_loadCostume(int id, int phase);
+ void smlayer_setActorCostume(int actornum, int act, int costume);
+ void smlayer_putActor(int actornum, int act, int x, int y, byte room);
+ void smlayer_setActorLayer(int actornum, int act, int layer);
+ void smlayer_setFluPalette(byte *pal, int shut_flag);
+
+ int32 readArray(int item);
+ void writeArray(int item, int value);
+
+ bool idx1Compare(void);
+ bool idx2Compare(void);
+ int32 idx1Tweak(void);
+ int32 idx2Tweak(void);
+ void smush_setToFinish(void);
+ void postCase11(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase0(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase17(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase16(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase1(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase2(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase20(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase3(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase5(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase6(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase8(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase9(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase10(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase12(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase23(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCase14(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCaseAll(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void postCaseMore(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame);
+ void switchSceneIfNeeded(void);
+ int smush_changeState(int state);
+ void init_actStruct(int actornum, int actnum, int32 actorval, byte state,
+ int32 room, int32 animtilt, int32 tilt, int32 frame);
+ void init_enemyStruct(int n, int32 handler, int32 initializer,
+ int16 occurences, int32 maxdamage, int32 isEmpty,
+ int32 field_14, int32 sound, const char *filename,
+ int32 costume4, int32 costume6, int32 costume5,
+ int16 field_2C, int32 field_30, int32 field_34);
+ int32 enemyHandler(int n, int32, int32, int32);
+ int32 enemyInitializer(int n, int32, int32, int32);
+ int32 enemy0handler(int32, int32, int32);
+ int32 enemy0initializer(int32, int32, int32);
+ int32 enemy1handler(int32, int32, int32);
+ int32 enemy1initializer(int32, int32, int32);
+ int32 enemy2handler(int32, int32, int32);
+ int32 enemy2initializer(int32, int32, int32);
+ int32 enemy3handler(int32, int32, int32);
+ int32 enemy3initializer(int32, int32, int32);
+ int32 enemy4handler(int32, int32, int32);
+ int32 enemy4initializer(int32, int32, int32);
+ int32 enemy5handler(int32, int32, int32);
+ int32 enemy5initializer(int32, int32, int32);
+ int32 enemy6handler(int32, int32, int32);
+ int32 enemy6initializer(int32, int32, int32);
+ int32 enemy7handler(int32, int32, int32);
+ int32 enemy7initializer(int32, int32, int32);
+ int32 enemy8handler(int32, int32, int32);
+ int32 enemy8initializer(int32, int32, int32);
+ int32 enemyBenHandler(int32, int32, int32);
+ bool smlayer_isSoundRunning(int32 sound);
+ bool smlayer_startSfx(int32 sound);
+ bool smlayer_startVoice(int32 sound);
+ void smlayer_soundSetPan(int32 sound, int32 pan);
+ void smlayer_soundSetPriority(int32 sound, int32 priority);
+ void smlayer_drawSomething(byte *renderBitmap, int32 codecparam,
+ int32 arg_8, int32 arg_C, int32 arg_10, NutRenderer *nutfileptr,
+ int32 arg_18, int32 arg_1C, int32 arg_20);
+ void smlayer_overrideDrawActorAt(byte *, byte, byte);
+ void queueSceneSwitch(int32 sceneId, byte *fluPtr, const char *filename,
+ int32 arg_C, int32 arg_10, int32 startFrame, int32 numFrames);
+ void turnBen(bool battle);
+ void smush_rewindCurrentSan(int arg_0, int arg_4, int arg_8);
+ void smlayer_showStatusMsg(int32 arg_0, byte *renderBitmap, int32 codecparam,
+ int32 x, int32 y, int32 arg_14, int32 arg_18,
+ int32 arg_1C, const char *formatString, const char *str);
+ void init_fluConfStruct(int n, int sceneId, byte **fluPtr,
+ const char *filenamePtr, int startFrame, int numFrames);
+ int32 processBenOnRoad(bool flag);
+ void mineChooseRoad(int32 arg_0);
+ void actor02Reaction(int32 buttons);
+ void actor00Reaction(int32 buttons);
+ void actor01Reaction(int32 buttons);
+ void actor03Reaction(int32 buttons);
+ void turnEnemy(bool battle);
+ int32 actionBen(void);
+ void chooseBenWeaponAnim(int buttons);
+ void setBenAnimation(int32 actornum, int anim);
+ int calcTilt(int speed);
+ bool smush_eitherNotStartNewFrame(void);
+ void smlayer_setActorFacing(int actornum, int actnum, int frame, int direction);
+ int32 weaponMaxRange(int32 actornum);
+ int32 weaponMinRange(int32 actornum);
+ void switchBenWeapon(void);
+ void prepareScenePropScene(int32 scenePropNum, bool arg_4, bool arg_8);
+ int32 calcBenDamage(bool arg_0, bool arg_4);
+ int32 weaponDamage(int32 actornum);
+ void proc47(int32 actornum, int32 val);
+ bool weaponBenIsEffective(void);
+ bool actor1StateFlags(int state);
+ bool actor0StateFlags1(int state);
+ bool actor0StateFlags2(int state);
+ bool loadScenePropSounds(int32 scenePropNum);
+ void init_scenePropStruct(int32 n, int32 n1, int32 actornum, int32 sound, int32 trsId,
+ byte r, byte g, byte b, int32 counter, int32 maxCounter,
+ int32 index);
+ int32 setBenState(void);
+ bool smlayer_actorNeedRedraw(int actornum, int actnum);
+ void reinitActors(void);
+ const char *handleTrsTag(int32 trsId);
+ void ouchSoundBen(void);
+ int32 smush_setupSanWithFlu(const char *filename, int32 setupsan2, int32 step1,
+ int32 step2, int32 setupsan1, byte *fluPtr, int32 numFrames);
+ void smush_setupSanFromStart(const char *filename, int32 setupsan2, int32 step1,
+ int32 step2, int32 setupsan1);
+ void smush_setFrameSteps(int32 step1, int32 step2);
+ void smush_setupSanFile(const char *filename, int32 offset, int32 contFrame);
+ int32 getLastKey(bool arg_0);
+ void drawSpeedyActor(int32 arg_0);
+ void actor11Reaction(int32 buttons);
+ void actor12Reaction(int32 buttons);
+ void actor13Reaction(int32 buttons);
+ void actor10Reaction(int32 buttons);
+ int32 actionEnemy(void);
+ int32 processKeyboard(void);
+ int32 processMouse(void);
+ void setEnemyAnimation(int32 actornum, int anim);
+ void chooseEnemyWeaponAnim(int32 buttons);
+ void switchEnemyWeapon(void);
+ void setEnemyState(void);
+ int32 calcEnemyDamage(bool arg_0, bool arg_4);
+ void ouchSoundEnemy(void);
+ bool weaponEnemyIsEffective(void);
+ void iactScene1(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags);
+ void iactScene3(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags);
+ void iactScene4(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags);
+ void iactScene6(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags);
+ void iactScene17(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags);
+ void iactScene21(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags);
+ bool isBitSet(int n);
+ void setBit(int n);
+ void clearBit(int n);
+ void chooseEnemy(void);
+ void removeEmptyEnemies(void);
+ void removeEnemyFromMetList(int32);
+};
+} // End of namespace Insane
+
+#endif
diff --git a/engines/scumm/insane/insane_ben.cpp b/engines/scumm/insane/insane_ben.cpp
new file mode 100644
index 0000000000..4e58fa14c9
--- /dev/null
+++ b/engines/scumm/insane/insane_ben.cpp
@@ -0,0 +1,2009 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "base/engine.h"
+
+#include "scumm/insane/insane.h"
+
+namespace Scumm {
+
+int32 Insane::enemyBenHandler(int32 actor1, int32 actor2, int32 probability) {
+ int32 retval;
+ int32 tmp;
+
+ retval = processMouse();
+
+ // Joystick support is skipped
+
+ retval |= processKeyboard();
+
+ tmp = _enemyState[EN_BEN][0] - 160;
+ if (tmp < -160)
+ tmp = -160;
+
+ if (tmp > 160)
+ tmp = 160;
+
+ _actor[actor1].cursorX = tmp;
+
+ smush_warpMouse(_enemyState[EN_BEN][0], _enemyState[EN_BEN][1], -1);
+
+ return (retval & 3);
+}
+
+void Insane::turnBen(bool controllable) {
+ int32 buttons;
+
+ switch (_currSceneId) {
+ case 21:
+ case 25:
+ case 3:
+ case 13:
+ if (_actor[0].damage < _actor[0].maxdamage) {
+ _actor[0].lost = false;
+ } else {
+ if (!_actor[0].lost && !_actor[1].lost) {
+ _actor[0].lost = true;
+ _actor[0].act[2].state = 36;
+ _actor[0].act[1].state = 36;
+ _actor[0].act[1].room = 0;
+ _actor[0].act[0].state = 36;
+ _actor[0].act[0].room = 0;
+
+ if (smlayer_isSoundRunning(95))
+ smlayer_stopSound(95);
+ }
+ }
+ buttons = 0;
+ if (!_actor[0].lost && controllable) {
+ buttons = actionBen();
+ if (_currSceneId == 13)
+ buttons &= 2;
+ if (_currEnemy == EN_TORQUE)
+ buttons = 0;
+ }
+ debug(5, "00:%d 01:%d 02:%d 03:%d", _actor[0].act[0].state,
+ _actor[0].act[1].state, _actor[0].act[2].state, _actor[0].act[3].state);
+ actor01Reaction(buttons);
+ actor02Reaction(buttons);
+ actor03Reaction(buttons);
+ actor00Reaction(buttons);
+ break;
+ case 17:
+ mineChooseRoad(processBenOnRoad(false));
+ break;
+ default:
+ if (_actor[0].damage < _actor[0].maxdamage) {
+ _actor[0].lost = false;
+ } else {
+ if (!_actor[0].lost && !_actor[1].lost) {
+ queueSceneSwitch(10, 0, "wr2_ben.san", 64, 0, 0, 0);
+ _actor[0].lost = true;
+ _actor[0].act[2].state = 36;
+ _actor[0].act[2].room = 0;
+ _actor[0].act[0].state = 36;
+ _actor[0].act[0].room = 0;
+ _actor[0].act[1].state = 36;
+ _actor[0].act[1].room = 0;
+ mineChooseRoad(0);
+ return;
+ }
+ }
+
+ if (!_actor[0].lost && controllable)
+ mineChooseRoad(processBenOnRoad(true));
+ else
+ mineChooseRoad(0);
+ break;
+ }
+}
+
+int32 Insane::actionBen(void) {
+ int32 buttons, tmp;
+ bool doDamage = false;
+ int sound;
+
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ sound = 59;
+ else
+ sound = 95;
+
+ if (_actor[0].enemyHandler != -1)
+ buttons = enemyHandler(_actor[0].enemyHandler, 0, 1, _actor[0].probability);
+ else
+ buttons = enemyHandler(EN_TORQUE, 0, 1, _actor[0].probability);
+
+ if (_actor[0].tilt) {
+ _actor[0].speed += _actor[0].cursorX / 40;
+ } else {
+ if (_actor[0].speed < 0)
+ _actor[0].speed++;
+ else
+ _actor[0].speed--;
+ }
+
+ if (_actor[0].speed > 8)
+ _actor[0].speed = 8;
+
+ if (_actor[0].speed < -8)
+ _actor[0].speed = -8;
+
+ _actor[0].x += _actor[0].speed;
+
+ if (_actor[0].x > 100)
+ _actor[0].x--;
+ else
+ if (_actor[0].x < 100)
+ _actor[0].x++;
+
+ if (_actor[0].x >= 0) {
+ if (_actor[1].x - 90 <= _actor[0].x && !_actor[0].lost && !_actor[1].lost) {
+ _val213d++;
+ _actor[0].x = _actor[1].x - 90;
+
+ tmp = _actor[1].speed;
+ _actor[1].speed = _actor[0].speed;
+ _actor[0].speed = tmp;
+
+ if (_val213d > 50) {
+ _actor[0].cursorX = -320;
+ _val213d = 0;
+ }
+
+ if (!smlayer_isSoundRunning(sound))
+ smlayer_startSfx(sound);
+ } else {
+ if (smlayer_isSoundRunning(sound))
+ smlayer_stopSound(sound);
+
+ _val213d = 0;
+ }
+ } else {
+ _actor[0].x = 0;
+ _actor[0].damage++; // FIXME: apparently it is a bug in original
+ // and damage is doubled
+ doDamage = true;
+ }
+
+ if (_actor[0].x > 320) {
+ _actor[0].x = 320;
+ doDamage = true;
+ }
+
+ if (_actor[0].x < 10 || _actor[0].x > 310 || doDamage) {
+ _tiresRustle = 1;
+ _actor[0].x1 = -_actor[0].x1;
+ _actor[0].damage++; // PATCH
+ }
+
+ return buttons;
+}
+
+int32 Insane::processBenOnRoad(bool flag) {
+ int32 buttons;
+
+ if (_actor[0].enemyHandler != -1)
+ buttons = enemyHandler(_actor[0].enemyHandler, 0, 1, _actor[0].probability);
+ else
+ buttons = enemyHandler(EN_TORQUE, 0, 1, _actor[0].probability);
+
+ if (flag) {
+ _actor[0].speed = _actor[0].tilt;
+
+ if (_actor[0].speed > 8)
+ _actor[0].speed = 8;
+
+ if (_actor[0].speed < -8)
+ _actor[0].speed = -8;
+
+ _actor[0].x += _actor[0].speed / 2 + _actor[0].speed;
+
+ if (_actor[0].x < 0)
+ _actor[0].x = 0;
+
+ if (_actor[0].x > 320)
+ _actor[0].x = 320;
+ }
+
+ return buttons;
+}
+
+void Insane::mineChooseRoad(int32 buttons) {
+ int16 tmp;
+
+ if (_actor[0].field_8 < 1)
+ return;
+
+ if (_actor[0].field_8 == 112) {
+ if (_actor[0].frame < 18 || _needSceneSwitch)
+ return;
+
+ queueSceneSwitch(18, 0, "fishgogg.san", 64, 0, 0, 0);
+ } else if (_actor[0].field_8 == 1) {
+ tmp = _actor[0].cursorX / 22;
+
+ switch (_currSceneId) {
+ case 17:
+ if (buttons & 1) {
+ if (_mineCaveIsNear) {
+ writeArray(1, _posCave);
+ smush_setToFinish();
+ }
+
+ if (_roadBranch && !_needSceneSwitch) {
+ _iactSceneId2 = _iactSceneId;
+ queueSceneSwitch(2, 0, "mineexit.san", 64, 0, 0, 0);
+ }
+ }
+
+ if ((buttons & 2) == 0 || _needSceneSwitch)
+ return;
+
+ queueSceneSwitch(19, 0, "fishgog2.san", 64, 0, 0, 0);
+ break;
+ case 1:
+ _actor[0].tilt = tmp;
+
+ if (tmp < -7)
+ _actor[0].tilt = -7;
+ if (tmp > 7)
+ _actor[0].tilt = 7;
+
+ drawSpeedyActor(buttons);
+
+ if ((buttons & 1) && _currSceneId == 1 && _roadBranch && !_needSceneSwitch) {
+ _iactSceneId2 = _iactSceneId;
+ queueSceneSwitch(2, 0, "mineexit.san", 64, 0, 0, 0);
+ }
+
+ if ((buttons & 2) == 0 || !_benHasGoggles)
+ return;
+
+ _actor[0].frame = 0;
+ _actor[0].field_8 = 112;
+ smlayer_setActorFacing(0, 2, 26, 180);
+ break;
+ case 4:
+ case 5:
+ _actor[0].tilt = tmp;
+
+ if (tmp < -7)
+ _actor[0].tilt = -7;
+ if (tmp > 7)
+ _actor[0].tilt = 7;
+
+ drawSpeedyActor(buttons);
+
+ if ((buttons & 1) == 0)
+ return;
+
+ if (_roadBranch && !_needSceneSwitch) {
+ _iactSceneId2 = _iactSceneId;
+
+ if (readArray(4) && _val211d < 3) {
+ _val211d++;
+ queueSceneSwitch(8, 0, "fishfear.san", 64, 0, 0, 0);
+ } else {
+ queueSceneSwitch(8, 0, "tomine.san", 64, 0, 0, 0);
+ }
+ }
+
+ if (_roadStop) {
+ writeArray(1, _posBrokenTruck);
+ writeArray(3, _val57d);
+ smush_setToFinish();
+ }
+
+ if (!_carIsBroken)
+ return;
+
+ writeArray(1, _posBrokenCar);
+ writeArray(3, _val57d);
+ smush_setToFinish();
+ break;
+ case 6:
+ _actor[0].tilt = tmp;
+
+ if (tmp < -7)
+ _actor[0].tilt = -7;
+ if (tmp > 7)
+ _actor[0].tilt = 7;
+
+ drawSpeedyActor(buttons);
+
+ if ((buttons & 1) == 0)
+ return;
+
+ if (_roadBranch && !_needSceneSwitch) {
+ _iactSceneId2 = _iactSceneId;
+
+ if (readArray(4) && _val211d < 3) {
+ _val211d++;
+ queueSceneSwitch(7, 0, "fishfear.san", 64, 0, 0, 0);
+ } else {
+ queueSceneSwitch(7, 0, "tomine.san", 64, 0, 0, 0);
+ }
+ }
+
+ if (_roadStop) {
+ writeArray(1, _posBrokenTruck);
+ writeArray(3, _posVista);
+ smush_setToFinish();
+ }
+
+ if (!_carIsBroken)
+ return;
+
+ writeArray(1, _posBrokenCar);
+ writeArray(3, _posVista);
+ smush_setToFinish();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Insane::drawSpeedyActor(int32 buttons) {
+ switch (_actor[0].tilt) {
+ case -7:
+ if (_actor[0].act[2].state != 47) {
+ smlayer_setActorFacing(0, 2, 13, 180);
+ _actor[0].act[2].state = 47;
+ }
+ break;
+ case -6:
+ if (_actor[0].act[2].state != 44) {
+ smlayer_setActorFacing(0, 2, 11, 180);
+ _actor[0].act[2].state = 44;
+ }
+ break;
+ case -5:
+ if (_actor[0].act[2].state != 43) {
+ smlayer_setActorFacing(0, 2, 10, 180);
+ _actor[0].act[2].state = 43;
+ }
+ break;
+ case -4:
+ if (_actor[0].act[2].state != 42) {
+ smlayer_setActorFacing(0, 2, 9, 180);
+ _actor[0].act[2].state = 42;
+ }
+ break;
+ case -3:
+ if (_actor[0].act[2].state != 41) {
+ smlayer_setActorFacing(0, 2, 8, 180);
+ _actor[0].act[2].state = 41;
+ }
+ break;
+ case -2:
+ if (_actor[0].act[2].state != 40) {
+ smlayer_setActorFacing(0, 2, 7, 180);
+ _actor[0].act[2].state = 40;
+ }
+ break;
+ case -1:
+ if (_actor[0].act[2].state != 39) {
+ smlayer_setActorFacing(0, 2, 6, 180);
+ _actor[0].act[2].state = 39;
+ }
+ break;
+ case 0:
+ if (_actor[0].act[2].state != 1) {
+ smlayer_setActorFacing(0, 2, 22, 180);
+ _actor[0].act[2].state = 1;
+ }
+ break;
+ case 1:
+ if (_actor[0].act[2].state != 55) {
+ smlayer_setActorFacing(0, 2, 14, 180);
+ _actor[0].act[2].state = 55;
+ }
+ break;
+ case 2:
+ if (_actor[0].act[2].state != 56) {
+ smlayer_setActorFacing(0, 2, 15, 180);
+ _actor[0].act[2].state = 56;
+ }
+ break;
+ case 3:
+ if (_actor[0].act[2].state != 57) {
+ smlayer_setActorFacing(0, 2, 16, 180);
+ _actor[0].act[2].state = 57;
+ }
+ break;
+ case 4:
+ if (_actor[0].act[2].state != 58) {
+ smlayer_setActorFacing(0, 2, 17, 180);
+ _actor[0].act[2].state = 58;
+ }
+ break;
+ case 5:
+ if (_actor[0].act[2].state != 59) {
+ smlayer_setActorFacing(0, 2, 18, 180);
+ _actor[0].act[2].state = 59;
+ }
+ break;
+ case 6:
+ if (_actor[0].act[2].state != 60) {
+ smlayer_setActorFacing(0, 2, 19, 180);
+ _actor[0].act[2].state = 60;
+ }
+ break;
+ case 7:
+ if (_actor[0].act[2].state != 50) {
+ smlayer_setActorFacing(0, 2, 21, 180);
+ _actor[0].act[2].state = 50;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!_actor[0].act[2].room)
+ return;
+
+ smlayer_putActor(0, 2, _actor[0].x + _actor[0].x1, _actor[0].y + _actor[0].y1,
+ _smlayer_room2);
+}
+
+bool Insane::weaponBenIsEffective(void) {
+ if ((_actor[1].x - _actor[0].x > weaponMaxRange(0)) ||
+ (_actor[1].x - _actor[0].x < weaponMinRange(0)) ||
+ !_actor[1].kicking)
+ return false;
+
+ return true;
+}
+
+int32 Insane::calcBenDamage(bool arg_0, bool arg_4) {
+ if ((_actor[1].x - _actor[0].x > weaponMaxRange(1)) ||
+ (_actor[1].x - _actor[0].x < weaponMinRange(1)))
+ return 0;
+
+ if (_actor[0].field_44 && arg_4)
+ return 1000;
+
+ if (!actor1StateFlags(_actor[0].act[2].state))
+ return 0;
+
+ if (arg_0) {
+ ouchSoundBen();
+ _actor[0].damage += weaponDamage(1); // PATCH
+ }
+
+ return 1;
+}
+
+// Ben
+void Insane::actor02Reaction(int32 buttons) {
+ int32 tmp, tmp2;
+
+ switch(_actor[0].act[2].state) {
+ case 1:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 2;
+ _actor[0].kicking = false;
+
+ switch (_actor[0].tilt) {
+ case -3:
+ if (_actor[0].act[2].animTilt != -3) {
+ smlayer_setActorFacing(0, 2, 6, 180);
+ _actor[0].act[2].animTilt = -3;
+ }
+ break;
+ case -2:
+ if (_actor[0].field_8 == 48)
+ smlayer_setActorFacing(0, 2, 7, 180);
+ _actor[0].act[2].animTilt = -2;
+ break;
+ case -1:
+ if (_actor[0].field_8 == 46)
+ smlayer_setActorFacing(0, 2, 8, 180);
+ _actor[0].act[2].animTilt = -1;
+ break;
+ case 0:
+ if (_actor[0].act[2].animTilt) {
+ smlayer_setActorFacing(0, 2, 9, 180);
+ _actor[0].act[2].animTilt = 0;
+ }
+ break;
+ case 1:
+ if (_actor[0].field_8 == 49)
+ smlayer_setActorFacing(0, 2, 10, 180);
+ _actor[0].act[2].animTilt = 1;
+ break;
+ case 2:
+ if (_actor[0].field_8 == 51)
+ smlayer_setActorFacing(0, 2, 11, 180);
+ _actor[0].act[2].animTilt = 2;
+ break;
+ case 3:
+ if (_actor[0].act[2].animTilt != 3) {
+ smlayer_setActorFacing(0, 2, 12, 180);
+ _actor[0].act[2].animTilt = 3;
+ }
+ break;
+ default:
+ break;
+ }
+ _actor[0].act[2].tilt = 0;
+ break;
+ case 2:
+ smlayer_setActorLayer(0, 2, 4);
+ smlayer_setActorFacing(0, 2, 17, 180);
+ _actor[0].kicking = true;
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 3;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ smlayer_startSfx(63);
+ break;
+ case 3:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ if (_actor[0].act[2].frame == 2) {
+ if (_currEnemy != EN_CAVEFISH) {
+ tmp = calcEnemyDamage(1, 1);
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ if (tmp == 1)
+ smlayer_startSfx(50);
+ } else {
+ if (tmp == 1)
+ smlayer_startSfx(60);
+ if (tmp == 1000)
+ smlayer_startSfx(62);
+ }
+ } else {
+ if ((_actor[1].x - _actor[0].x <= weaponMaxRange(0)) &&
+ (_actor[1].x - _actor[0].x >= weaponMinRange(0)) &&
+ !_actor[0].field_54)
+ prepareScenePropScene(1, 0, 0);
+ }
+ }
+ if (_actor[0].act[2].frame >= 4) {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 4;
+ }
+
+ _actor[0].kicking = true;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 4:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 2) {
+ smlayer_setActorFacing(0, 2, 9, 180);
+ _actor[0].act[2].state = 1;
+ _actor[0].act[2].animTilt = -1000;
+ _actor[0].weaponClass = 2;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 5:
+ smlayer_setActorLayer(0, 2, 5);
+ break;
+ case 6:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 2;
+ _actor[0].newFacingFlag = 1;
+ _actor[0].kicking = false;
+ smlayer_setActorCostume(0, 2, readArray(22));
+ smlayer_setActorFacing(0, 2, 19, 180);
+ _actor[0].act[2].state = 7;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ smlayer_startSfx(66);
+ break;
+ case 7:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 2;
+ _actor[0].newFacingFlag = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 1) {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 8;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 8:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 2;
+ _actor[0].newFacingFlag = 1;
+ _actor[0].kicking = false;
+ if ((_actor[0].act[2].frame == 3) && (calcEnemyDamage(0, 0) == 1)) {
+ _actor[1].damage = weaponDamage(0);
+ smlayer_startSfx(64);
+ _actor[1].cursorX = 320;
+ }
+ if (_actor[0].act[2].frame >= 5) {
+ smlayer_setActorFacing(0, 2, 21, 180);
+ _actor[0].act[2].state = 9;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 9:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 2;
+ _actor[0].newFacingFlag = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 3) {
+ smlayer_setActorCostume(0, 2, readArray(12));
+ _actor[0].newFacingFlag = 2;
+ _actor[0].act[2].state = 1;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 10:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ smlayer_setActorFacing(0, 2, 19, 180);
+ _actor[0].act[2].state = 11;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ smlayer_startSfx(75);
+ break;
+ case 11:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 2) {
+ if (_currEnemy == EN_VULTM2) {
+ if ((_actor[1].x - _actor[0].x <= weaponMaxRange(0)) &&
+ (_actor[1].x - _actor[0].x >= weaponMinRange(0)) &&
+ calcEnemyDamage(0, 0)) {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 97;
+ _actor[0].act[2].room = 0;
+ _actor[0].act[1].room = 0;
+ _actor[0].act[0].room = 0;
+ smlayer_setActorLayer(0, 2, 25);
+ smlayer_setActorCostume(1, 2, readArray(45));
+ smlayer_setActorFacing(1, 2, 6, 180);
+ smlayer_startSfx(101);
+ _actor[1].act[2].state = 97;
+ _actor[1].lost = true;
+ _actor[1].act[2].room = 1;
+ _actor[1].act[1].room = 0;
+ _actor[1].act[0].room = 0;
+ } else {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 12;
+ }
+ } else {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 12;
+ }
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 12:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 1) {
+ if (_currEnemy != EN_CAVEFISH) {
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_DUST:
+ tmp = calcEnemyDamage(1, 1);
+ if (tmp == 1)
+ smlayer_startSfx(73);
+ if (tmp == 1000)
+ smlayer_startSfx(74);
+ break;
+ default:
+ if (calcEnemyDamage(1, 0) == 1)
+ smlayer_startSfx(73);
+ break;
+ }
+ } else {
+ if ((_actor[1].x - _actor[0].x <= weaponMaxRange(0)) &&
+ (_actor[1].x - _actor[0].x >= weaponMinRange(0)) &&
+ !_actor[0].field_54)
+ prepareScenePropScene(1, 0, 0);
+
+ }
+ smlayer_setActorFacing(0, 2, 21, 180);
+ _actor[0].act[2].state = 13;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 13:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 3) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].state = 63;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 14:
+ smlayer_setActorLayer(0, 2, 8);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ smlayer_setActorFacing(0, 2, 19, 180);
+ _actor[0].act[2].state = 15;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ smlayer_startSfx(78);
+ break;
+ case 15:
+ smlayer_setActorLayer(0, 2, 8);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 2) {
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ if (weaponBenIsEffective()) {
+ smlayer_setActorFacing(0, 2, 22, 180);
+ _actor[0].act[2].state = 81;
+ } else {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 16;
+ }
+ break;
+ case INV_MACE:
+ if (!_actor[1].kicking || _actor[1].field_44)
+ if (actor1StateFlags(_actor[1].act[2].state)) {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 106;
+ break;
+ }
+ default:
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 16;
+ break;
+ }
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 16:
+ smlayer_setActorLayer(0, 2, 8);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 1) {
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ tmp = calcEnemyDamage(1, 1);
+ if (tmp == 1)
+ smlayer_startSfx(76);
+ if (tmp == 1000)
+ smlayer_startSfx(77);
+ break;
+ case INV_BOOT:
+ calcEnemyDamage(0, 1);
+ break;
+ case INV_DUST:
+ if ((_actor[1].x - _actor[0].x <= weaponMaxRange(0)) &&
+ (_actor[1].x - _actor[0].x >= weaponMinRange(0))) {
+ smlayer_startSfx(76);
+ _actor[1].damage = weaponDamage(0);
+ }
+ break;
+ default:
+ if (calcEnemyDamage(1, 0))
+ smlayer_startSfx(76);
+ break;
+ }
+ smlayer_setActorFacing(0, 2, 21,180);
+ _actor[0].act[2].state = 17;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 17:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 2) {
+ smlayer_setActorFacing(0, 2, 26, 180);
+ _actor[0].act[2].state = 64;
+ smlayer_stopSound(76);
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 18:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ smlayer_setActorFacing(0, 2, 19, 180);
+ _actor[0].act[2].state = 19;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ smlayer_startSfx(69);
+ break;
+ case 19:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 1) {
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ if (_actor[1].kicking) {
+ _actor[1].act[2].state = 108;
+ _actor[0].act[2].state = 110;
+ } else {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 20;
+ }
+ break;
+ case INV_CHAINSAW:
+ if (_actor[1].kicking || _actor[1].field_44)
+ _actor[0].act[2].state = 20;
+ else {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 20;
+ }
+ break;
+ case INV_MACE:
+ case INV_2X4:
+ if (weaponBenIsEffective()) {
+ smlayer_setActorFacing(0, 2, 22, 180);
+ _actor[0].act[2].state = 77;
+ break;
+ }
+ // break skipped intentionally
+ default:
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 20;
+ break;
+ }
+ }
+
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 20:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 1) {
+ if (_currEnemy != EN_CAVEFISH) {
+ switch (_actor[1].weapon) {
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_BOOT:
+ tmp = calcEnemyDamage(1, 1);
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ if (tmp == 1)
+ smlayer_startSfx(52);
+ if (tmp == 1000)
+ smlayer_startSfx(56);
+ } else {
+ if (tmp == 1)
+ smlayer_startSfx(67);
+ if (tmp == 1000)
+ smlayer_startSfx(68);
+ }
+ break;
+ default:
+ if (calcEnemyDamage(1, 0))
+ smlayer_startSfx(67);
+ break;
+ }
+ } else {
+ if ((_actor[1].x - _actor[0].x <= weaponMaxRange(0)) &&
+ (_actor[1].x - _actor[0].x >= weaponMinRange(0)) &&
+ !_actor[0].field_54)
+ prepareScenePropScene(1, 0, 0);
+ }
+ smlayer_setActorFacing(0, 2, 21, 180);
+ _actor[0].act[2].state = 21;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 21:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 6) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].state = 65;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 22:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 0;
+ _actor[0].kicking = true;
+ smlayer_setActorFacing(0, 2, 19, 180);
+ _actor[0].act[2].state = 23;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ smlayer_startSfx(81);
+ break;
+ case 23:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 0;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 4) {
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_BOOT:
+ case INV_DUST:
+ if (weaponBenIsEffective()) {
+ smlayer_setActorFacing(0, 2, 22, 180);
+ _actor[0].act[2].state = 83;
+ break;
+ }
+ // break missed intentionally
+ default:
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 24;
+ break;
+ }
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 24:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 0;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 1) {
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_BOOT:
+ case INV_DUST:
+ tmp = calcEnemyDamage(1, 1);
+ if (tmp == 1) {
+ if (_currEnemy == EN_CAVEFISH) {
+ _actor[1].lost = true;
+ _actor[1].act[2].state = 102;
+ _actor[1].damage = _actor[1].maxdamage + 10;
+ }
+ smlayer_startSfx(79);
+ }
+ if (tmp == 1000)
+ smlayer_startSfx(80);
+ break;
+ default:
+ if (!calcEnemyDamage(1, 0))
+ smlayer_startSfx(79);
+ break;
+ }
+ smlayer_setActorFacing(0, 2, 21, 180);
+ _actor[0].act[2].state = 25;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 25:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 0;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 6) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].state = 66;
+ _actor[0].weaponClass = 1;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 26:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ smlayer_setActorFacing(0, 2, 19, 180);
+ _actor[0].act[2].state = 27;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ if (!((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)))
+ smlayer_startSfx(72);
+ break;
+ case 27:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 1) {
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_BOOT:
+ case INV_DUST:
+ if (weaponBenIsEffective()) {
+ smlayer_setActorFacing(0, 2, 22, 180);
+ _actor[0].act[2].state = 75;
+ break;
+ }
+ // break missed intentionaly
+ default:
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 28;
+ break;
+ }
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 28:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 3) {
+ if (_currEnemy != EN_CAVEFISH) {
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_BOOT:
+ case INV_DUST:
+ tmp = calcEnemyDamage(1, 1);
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ if (tmp == 1)
+ smlayer_startSfx(58);
+ if (tmp == 1000)
+ smlayer_startSfx(56);
+ } else {
+ if (tmp == 1)
+ smlayer_startSfx(70);
+ if (tmp == 1000)
+ smlayer_startSfx(71);
+ }
+ break;
+ case INV_HAND:
+ if (!calcEnemyDamage(1, 0))
+ smlayer_startSfx(70);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if ((_actor[1].x - _actor[0].x <= weaponMaxRange(0)) &&
+ (_actor[1].x - _actor[0].x >= weaponMinRange(0)) &&
+ !_actor[0].field_54)
+ prepareScenePropScene(1, 0, 0);
+ }
+ smlayer_setActorFacing(0, 2, 21, 180);
+ _actor[0].act[2].state = 29;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 29:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 6) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].state = 62;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 30:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ smlayer_setActorCostume(0, 2, readArray(21));
+ smlayer_setActorFacing(0, 2, 18, 180);
+ _actor[0].act[2].state = 31;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ smlayer_startSfx(84);
+ break;
+ case 31:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ if (_actor[0].act[2].frame >= 6) {
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].state = 32;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 32:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ if (_actor[0].act[2].frame >= 5) {
+ switch (_currEnemy) {
+ case EN_ROTT3:
+ if (calcEnemyDamage(0, 0))
+ _actor[1].act[2].state = 115;
+ break;
+ case EN_VULTF2:
+ if (calcEnemyDamage(0, 0))
+ _actor[1].act[2].state = 113;
+ break;
+ default:
+ tmp = calcEnemyDamage(1, 1);
+ if (tmp == 1)
+ smlayer_startSfx(82);
+ if (tmp == 1000)
+ smlayer_startSfx(83);
+ break;
+ }
+ smlayer_setActorFacing(0, 2, 21, 180);
+ _actor[0].act[2].state = 33;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 33:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 5) {
+ smlayer_setActorCostume(0, 2, readArray(12));
+ _actor[0].act[2].state = 1;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 34:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].kicking = false;
+
+ if (!smlayer_actorNeedRedraw(0, 2)) {
+ setBenState();
+ _actor[0].act[2].tilt = 0;
+ // for some reason there is no break at this
+ // place, so tilt gets overriden on next line
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 35:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].kicking = false;
+
+ if (!smlayer_actorNeedRedraw(0, 2)) {
+ switchBenWeapon();
+ _actor[0].act[2].tilt = 0;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 36:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].kicking = false;
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ smlayer_setActorCostume(0, 2, readArray(17));
+ else
+ smlayer_setActorCostume(0, 2, readArray(18));
+ smlayer_setActorFacing(0, 2, 6, 180);
+ smlayer_startSfx(96);
+ switch (_currEnemy) {
+ case EN_ROTT1:
+ prepareScenePropScene(33, 0, 0);
+ break;
+ case EN_ROTT2:
+ tmp = _vm->_rnd.getRandomNumber(4);
+ if (!tmp)
+ prepareScenePropScene(35, 0, 0);
+ if (tmp == 3)
+ prepareScenePropScene(36, 0, 0);
+ break;
+ case EN_VULTF1:
+ prepareScenePropScene(6, 0, 0);
+ break;
+ case EN_VULTM1:
+ tmp = _vm->_rnd.getRandomNumber(4);
+ if (!tmp)
+ prepareScenePropScene(40, 0, 0);
+ if (tmp == 3)
+ prepareScenePropScene(41, 0, 0);
+ break;
+ default:
+ break;
+ }
+ _actor[0].act[2].state = 37;
+ break;
+ case 37:
+ smlayer_setActorLayer(0, 2, 25);
+ _actor[0].cursorX = 0;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 18 ||
+ (_actor[0].x < 50 && _actor[0].act[2].frame >= 10) ||
+ (_actor[0].x > 270 && _actor[0].act[2].frame >= 10)) {
+ if (_currSceneId == 21) {
+ queueSceneSwitch(23, 0, "benflip.san", 64, 0, 0, 0);
+ } else {
+ switch (_currEnemy) {
+ case EN_ROTT1:
+ case EN_ROTT2:
+ case EN_ROTT3:
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ queueSceneSwitch(9, 0, "bencrshe.san", 64, 0, 0, 0);
+ else
+ queueSceneSwitch(9, 0, "wr2_benr.san", 64, 0, 0, 0);
+ break;
+ case EN_VULTF1:
+ case EN_VULTM1:
+ case EN_VULTF2:
+ case EN_VULTM2:
+ queueSceneSwitch(9, 0, "wr2_benv.san", 64, 0, 0, 0);
+ break;
+ case EN_CAVEFISH:
+ queueSceneSwitch(9, 0, "wr2_benc.san", 64, 0, 0, 0);
+ break;
+ default:
+ queueSceneSwitch(9, 0, "wr2_ben.san", 64, 0, 0, 0);
+ break;
+ }
+ }
+ _actor[0].act[2].state = 38;
+ }
+ break;
+ case 38:
+ if (_actor[0].act[2].frame >= 36) {
+ _actor[0].act[2].frame = 0;
+ if (_currSceneId == 21) {
+ queueSceneSwitch(23, 0, "benflip.san", 64, 0, 0, 0);
+ } else {
+ switch (_currEnemy) {
+ case EN_ROTT1:
+ case EN_ROTT2:
+ case EN_ROTT3:
+ queueSceneSwitch(9, 0, "wr2_benr.san", 64, 0, 0, 0);
+ break;
+ case EN_VULTF1:
+ case EN_VULTM1:
+ case EN_VULTF2:
+ case EN_VULTM2:
+ queueSceneSwitch(9, 0, "wr2_benv.san", 64, 0, 0, 0);
+ break;
+ case EN_CAVEFISH:
+ queueSceneSwitch(9, 0, "wr2_benc.san", 64, 0, 0, 0);
+ break;
+ default:
+ queueSceneSwitch(9, 0, "wr2_ben.san", 64, 0, 0, 0);
+ break;
+ }
+ }
+ _actor[0].act[2].state = 38;
+ }
+ break;
+ case 63:
+ smlayer_setActorLayer(0, 2, 5);
+ if (_actor[0].act[2].animTilt) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].animTilt = 0;
+ }
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 64:
+ smlayer_setActorLayer(0, 2, 5);
+ if (_actor[0].act[2].animTilt) {
+ smlayer_setActorFacing(0, 2, 26, 180);
+ _actor[0].act[2].animTilt = 0;
+ }
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 65:
+ smlayer_setActorLayer(0, 2, 5);
+ if (_actor[0].act[2].animTilt) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].animTilt = 0;
+ }
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 66:
+ smlayer_setActorLayer(0, 2, 5);
+ if (_actor[0].act[2].animTilt) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].animTilt = 0;
+ }
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 62:
+ smlayer_setActorLayer(0, 2, 5);
+ if (_actor[0].act[2].animTilt) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].animTilt = 0;
+ }
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 73:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = true;
+ if (_actor[0].act[2].frame >= 2 && !_kickBenProgress) {
+ smlayer_setActorFacing(0, 2, 19, 180);
+ _actor[0].act[2].state = 74;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 74:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = false;
+ if (_actor[0].act[2].frame >= 2) {
+ smlayer_setActorFacing(0, 2, 9, 180);
+ _actor[0].act[2].state = 1;
+ _actor[0].weaponClass = 2;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 75:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = true;
+ if (_actor[0].act[2].frame >= 4 && !_kickBenProgress) {
+ smlayer_setActorFacing(0, 2, 23, 180);
+ _actor[0].act[2].state = 76;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 76:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = false;
+ if (_actor[0].act[2].frame >= 4) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].state = 62;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 77:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = true;
+ if (_actor[0].act[2].frame >= 2) {
+ smlayer_setActorFacing(0, 2, 23, 180);
+ _actor[0].act[2].state = 78;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 78:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = false;
+ if (_actor[0].act[2].frame >= 5) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].state = 65;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 79:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = true;
+ if (_actor[0].act[2].frame >= 2) {
+ smlayer_setActorFacing(0, 2, 23, 180);
+ _actor[0].act[2].state = 80;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 80:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = false;
+ if (_actor[0].act[2].frame >= 6) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].state = 63;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 81:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = true;
+ if (_actor[0].act[2].frame >= 2 && !_kickBenProgress) {
+ smlayer_setActorFacing(0, 2, 23, 180);
+ _actor[0].act[2].state = 82;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 82:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = false;
+ if (_actor[0].act[2].frame >= 3) {
+ smlayer_setActorFacing(0, 2, 26, 180);
+ _actor[0].act[2].state = 64;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 83:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 0;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = true;
+ if (_actor[0].act[2].frame >= 2 && !_kickBenProgress) {
+ smlayer_setActorFacing(0, 2, 23, 180);
+ _actor[0].act[2].state = 84;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 84:
+ smlayer_setActorLayer(0, 2, 6);
+ _actor[0].weaponClass = 0;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = false;
+ if (_actor[0].act[2].frame >= 5) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].state = 66;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 97:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = true;
+ if (_actor[0].act[2].frame >= 5) {
+ _actor[0].act[2].room = 1;
+ _actor[0].act[1].room = 1;
+ _actor[0].act[0].room = 1;
+ smlayer_setActorFacing(0, 2, 21, 180);
+ _actor[0].act[2].state = 13;
+ _actor[0].x = _actor[1].x - 116;
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 104:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ smlayer_setActorFacing(0, 2, 28, 180);
+ _actor[0].act[2].state = 105;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 105:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 5) {
+ _actor[0].act[2].state = 1;
+ _actor[0].inventory[INV_MACE] = 0;
+ smlayer_startVoice(318);
+ switchBenWeapon();
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 106:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ smlayer_setActorFacing(0, 2, 29, 180);
+ _actor[0].act[2].state = 107;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 107:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 9) {
+ _actor[0].act[2].state = 1;
+ _actor[0].inventory[INV_MACE] = 0;
+ smlayer_startVoice(318);
+ switchBenWeapon();
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 108:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ smlayer_setActorFacing(0, 2, 28, 180);
+ _actor[0].act[2].state = 109;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 109:
+ smlayer_setActorLayer(0, 2, 5);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 5) {
+ _actor[0].act[2].state = 1;
+ _actor[0].inventory[INV_CHAIN] = 0; // Chain
+ smlayer_startVoice(318);
+ switchBenWeapon();
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 110:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ smlayer_setActorFacing(0, 2, 30, 180);
+ _actor[0].act[2].state = 111;
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ case 111:
+ smlayer_setActorLayer(0, 2, 4);
+ _actor[0].weaponClass = 1;
+ _actor[0].kicking = false;
+ if (_actor[0].act[2].frame >= 7) {
+ smlayer_setActorFacing(0, 2, 25, 180);
+ _actor[0].act[2].state = 65;
+ _actor[0].inventory[INV_CHAIN] = 1; // Chain
+ }
+ _actor[0].act[2].tilt = calcTilt(_actor[0].tilt);
+ break;
+ default:
+ break;
+ }
+ tmp = _actor[0].x + _actor[0].act[2].tilt + 17 + _actor[0].x1;
+ tmp2 = _actor[0].y + _actor[0].y1 - 98;
+
+ if (_actor[0].act[2].room)
+ smlayer_putActor(0, 2, tmp, tmp2, _smlayer_room2);
+ else
+ smlayer_putActor(0, 2, tmp, tmp2, _smlayer_room);
+}
+
+// Bike
+void Insane::actor00Reaction(int32 buttons) {
+ int32 tmpx, tmpy;
+
+ switch (_actor[0].tilt) {
+ case -3:
+ if (_actor[0].act[0].state != 41) {
+ smlayer_setActorFacing(0, 0, 6, 180);
+ _actor[0].act[0].state = 41;
+ }
+ break;
+ case -2:
+ if (_actor[0].act[0].state != 40) {
+ smlayer_setActorFacing(0, 0, 7, 180);
+ _actor[0].act[0].state = 40;
+ }
+ break;
+ case -1:
+ if (_actor[0].act[0].state != 39) {
+ smlayer_setActorFacing(0, 0, 8, 180);
+ _actor[0].act[0].state = 39;
+ }
+ break;
+ case 0:
+ if (_actor[0].act[0].state != 1) {
+ smlayer_setActorFacing(0, 0, 9, 180);
+ _actor[0].act[0].state = 1;
+ }
+ break;
+ case 1:
+ if (_actor[0].act[0].state != 55) {
+ smlayer_setActorFacing(0, 0, 10, 180);
+ _actor[0].act[0].state = 55;
+ }
+ break;
+ case 2:
+ if (_actor[0].act[0].state != 56) {
+ smlayer_setActorFacing(0, 0, 11, 180);
+ _actor[0].act[0].state = 56;
+ }
+ break;
+ case 3:
+ if (_actor[0].act[0].state != 57) {
+ smlayer_setActorFacing(0, 0, 12, 180);
+ _actor[0].act[0].state = 57;
+ }
+ break;
+ default:
+ break;
+ }
+ tmpx = _actor[0].x + _actor[0].x1;
+ tmpy = _actor[0].y + _actor[0].y1;
+
+ if (_actor[0].act[0].room)
+ smlayer_putActor(0, 0, tmpx, tmpy, _smlayer_room2);
+ else
+ smlayer_putActor(0, 0, tmpx, tmpy, _smlayer_room);
+}
+
+// Bike top
+void Insane::actor01Reaction(int32 buttons) {
+ int32 tmpx, tmpy;
+
+ chooseBenWeaponAnim(buttons);
+
+ switch (_actor[0].tilt) {
+ case -3:
+ if (_actor[0].act[1].state != 41 || _actor[0].weaponClass != _actor[0].animWeaponClass) {
+ setBenAnimation(0, 6);
+ _actor[0].act[1].state = 41;
+ }
+
+ if (_actor[0].cursorX >= -100) {
+ setBenAnimation(0, 7);
+ _actor[0].act[1].state = 40;
+ _actor[0].field_8 = 48;
+ _actor[0].tilt = -2;
+ }
+ break;
+ case -2:
+ if (_actor[0].act[1].state != 40 || _actor[0].weaponClass != _actor[0].animWeaponClass) {
+ setBenAnimation(0, 7);
+ _actor[0].act[1].state = 40;
+ }
+ if (_actor[0].field_8 == 48)
+ _actor[0].tilt = -1;
+ else
+ _actor[0].tilt = -3;
+ break;
+ case -1:
+ if (_actor[0].act[1].state != 39 || _actor[0].weaponClass != _actor[0].animWeaponClass) {
+ setBenAnimation(0, 8);
+ _actor[0].act[1].state = 39;
+ }
+
+ if (_actor[0].field_8 == 48)
+ _actor[0].tilt = 0;
+ else
+ _actor[0].tilt = -2;
+ break;
+ case 0:
+ if (_actor[0].act[1].state != 1 || _actor[0].weaponClass != _actor[0].animWeaponClass) {
+ setBenAnimation(0, 9);
+ _actor[0].act[1].state = 1;
+ }
+ _actor[0].field_8 = 1;
+ if (_actor[0].cursorX < -100) {
+ setBenAnimation(0, 8);
+ _actor[0].act[1].state = 39;
+ _actor[0].field_8 = 46;
+ _actor[0].tilt = -1;
+ } else {
+ if (_actor[0].cursorX > 100) {
+ setBenAnimation(0, 10);
+ _actor[0].act[1].state = 55;
+ _actor[0].field_8 = 49;
+ _actor[0].tilt = 1;
+ }
+ }
+ break;
+ case 1:
+ if (_actor[0].act[1].state != 55 || _actor[0].weaponClass != _actor[0].animWeaponClass) {
+ setBenAnimation(0, 10);
+ _actor[0].act[1].state = 55;
+ }
+ if (_actor[0].field_8 == 51)
+ _actor[0].tilt = 0;
+ else
+ _actor[0].tilt = 2;
+ break;
+ case 2:
+ if (_actor[0].act[1].state != 56 || _actor[0].weaponClass != _actor[0].animWeaponClass) {
+ setBenAnimation(0, 11);
+ _actor[0].act[1].state = 56;
+ }
+ if (_actor[0].field_8 == 51)
+ _actor[0].tilt = 1;
+ else
+ _actor[0].tilt = 3;
+ break;
+ case 3:
+ if (_actor[0].act[1].state != 57 || _actor[0].weaponClass != _actor[0].animWeaponClass) {
+ setBenAnimation(0, 12);
+ _actor[0].act[1].state = 57;
+ }
+
+ if (_actor[0].cursorX <= 100) {
+ setBenAnimation(0, 11);
+ _actor[0].act[1].state = 56;
+ _actor[0].field_8 = 51;
+ _actor[0].tilt = 2;
+ }
+ break;
+ }
+
+ if (_actor[0].curFacingFlag != _actor[0].newFacingFlag) {
+ if (_actor[0].newFacingFlag == 2)
+ smlayer_setActorFacing(0, 1, 28, 180);
+ else
+ smlayer_setActorFacing(0, 1, 27, 180);
+ }
+
+ tmpx = _actor[0].x + _actor[0].x1;
+ tmpy = _actor[0].y + _actor[0].y1;
+
+ if (_actor[0].act[1].room)
+ smlayer_putActor(0, 1, tmpx, tmpy, _smlayer_room2);
+ else
+ smlayer_putActor(0, 1, tmpx, tmpy, _smlayer_room);
+
+ _actor[0].animWeaponClass = _actor[0].weaponClass;
+ _actor[0].curFacingFlag = _actor[0].newFacingFlag;
+}
+
+void Insane::actor03Reaction(int32 buttons) {
+ int32 tmp;
+
+ switch (_actor[0].act[3].state) {
+ case 1:
+ _actor[0].field_54 = 0;
+ break;
+ case 52:
+ if (_actor[0].runningSound)
+ smlayer_stopSound(_actor[0].runningSound);
+
+ if (_currScenePropIdx)
+ shutCurrentScene();
+
+ _actor[0].runningSound = 0;
+ _actor[0].defunct = 0;
+ _actor[0].field_54 = 0;
+ smlayer_setActorFacing(0, 3, 15, 180);
+ _actor[0].act[3].state = 53;
+ break;
+ case 53:
+ if (_actor[0].act[3].frame >= 2) {
+ smlayer_setActorFacing(0, 3, 16, 180);
+ _actor[0].act[3].state = 54;
+ }
+ break;
+ case 54:
+ break;
+ case 69:
+ if (_actor[0].act[3].frame >= 2)
+ _actor[0].act[3].state = 70;
+ break;
+ case 70:
+ if (_actor[0].scenePropSubIdx) {
+ smlayer_setActorFacing(0, 3, 4, 180);
+ tmp = _currScenePropIdx + _actor[0].scenePropSubIdx;
+ if (!smlayer_startVoice(_sceneProp[tmp].sound))
+ _actor[0].runningSound = 0;
+ else
+ _actor[0].runningSound = _sceneProp[tmp].sound;
+ _actor[0].act[3].state = 72;
+ } else {
+ _actor[0].act[3].state = 118;
+ }
+ break;
+ case 71:
+ _actor[0].field_54 = 0;
+ if (_actor[0].act[3].frame >= 2)
+ _actor[0].act[3].state = 1;
+ break;
+ case 72:
+ if (_actor[0].runningSound) {
+ if (!smlayer_isSoundRunning(_actor[0].runningSound)) {
+ smlayer_setActorFacing(0, 3, 5, 180);
+ _actor[0].act[3].state = 70;
+ _actor[0].scenePropSubIdx = 0;
+ }
+ } else {
+ tmp = _currScenePropIdx + _actor[0].scenePropSubIdx;
+ if (_sceneProp[tmp].counter >= _sceneProp[tmp].maxCounter) {
+ smlayer_setActorFacing(0, 3, 5, 180);
+ _actor[0].act[3].state = 70;
+ _actor[0].scenePropSubIdx = 0;
+ _actor[0].runningSound = 0;
+ }
+ }
+ break;
+ case 117:
+ reinitActors();
+ smlayer_setActorFacing(0, 3, 13, 180);
+ _actor[0].act[3].state = 69;
+ break;
+ case 118:
+ smlayer_setActorFacing(0, 3, 14, 180);
+ _actor[0].act[3].state = 71;
+ break;
+ default:
+ break;
+ }
+}
+
+void Insane::chooseBenWeaponAnim(int buttons) {
+ // kick
+ if ((buttons & 1) && (_currEnemy != EN_TORQUE)) {
+ if (!_kickBenProgress && actor0StateFlags2(_actor[0].act[2].state + _actor[0].weapon * 119)) {
+ switch (_actor[0].weapon) {
+ case INV_CHAIN:
+ _actor[0].act[2].state = 10;
+ break;
+ case INV_CHAINSAW:
+ _actor[0].act[2].state = 14;
+ break;
+ case INV_MACE:
+ _actor[0].act[2].state = 18;
+ break;
+ case INV_2X4:
+ _actor[0].act[2].state = 22;
+ break;
+ case INV_WRENCH:
+ _actor[0].act[2].state = 26;
+ break;
+ case INV_BOOT:
+ _actor[0].act[2].state = 6;
+ break;
+ case INV_HAND:
+ _actor[0].act[2].state = 2;
+ break;
+ case INV_DUST:
+ _actor[0].act[2].state = 30;
+ break;
+ default:
+ break;
+ }
+ _actor[0].kicking = true;
+ _kickBenProgress = true;
+ }
+ } else {
+ _kickBenProgress = false;
+ }
+
+ // switch weapon
+ if ((buttons & 2) && (_currEnemy != EN_TORQUE)) {
+ if (_weaponBenJustSwitched)
+ return;
+
+ if (!actor0StateFlags1(_actor[0].act[2].state))
+ return;
+
+ switch (_actor[0].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_WRENCH:
+ _actor[0].act[2].state = 35;
+ smlayer_setActorFacing(0, 2, 24, 180);
+ break;
+ case INV_BOOT:
+ case INV_HAND:
+ case INV_DUST:
+ _actor[0].act[2].state = 0;
+ switchBenWeapon();
+ }
+
+ _weaponBenJustSwitched = true;
+ } else {
+ _weaponBenJustSwitched = false;
+ }
+}
+
+void Insane::switchBenWeapon(void) {
+ do {
+ _actor[0].weapon++;
+ if (_actor[0].weapon > 7)
+ _actor[0].weapon = INV_CHAIN;
+
+ } while (!_actor[0].inventory[_actor[0].weapon]);
+
+ switch (_actor[0].weapon) {
+ case INV_CHAIN:
+ smlayer_setActorCostume(0, 2, readArray(20));
+ smlayer_setActorFacing(0, 2, 18, 180);
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 34;
+ break;
+ case INV_CHAINSAW:
+ smlayer_setActorCostume(0, 2, readArray(24));
+ smlayer_setActorFacing(0, 2, 18, 180);
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 34;
+ break;
+ case INV_MACE:
+ smlayer_setActorCostume(0, 2, readArray(23));
+ smlayer_setActorFacing(0, 2, 18, 180);
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 34;
+ break;
+ case INV_2X4:
+ if (_currEnemy == EN_CAVEFISH)
+ smlayer_setActorCostume(0, 2, readArray(38));
+ else
+ smlayer_setActorCostume(0, 2, readArray(19));
+
+ smlayer_setActorFacing(0, 2, 18, 180);
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 34;
+ break;
+ case INV_WRENCH:
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ smlayer_setActorCostume(0, 2, readArray(24));
+ else
+ smlayer_setActorCostume(0, 2, readArray(25));
+ smlayer_setActorFacing(0, 2, 18, 180);
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 34;
+ break;
+ case INV_BOOT:
+ case INV_HAND:
+ case INV_DUST:
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ smlayer_setActorCostume(0, 2, readArray(11));
+ else
+ smlayer_setActorCostume(0, 2, readArray(12));
+ _actor[0].weaponClass = 2;
+ _actor[0].act[2].state = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+int32 Insane::setBenState(void) {
+ _actor[0].act[2].animTilt = -1000;
+
+ switch (_actor[0].weapon) {
+ case INV_CHAIN:
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 63;
+ break;
+ case INV_CHAINSAW:
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 64;
+ break;
+ case INV_MACE:
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 65;
+ break;
+ case INV_2X4:
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 66;
+ break;
+ case INV_WRENCH:
+ _actor[0].weaponClass = 1;
+ _actor[0].act[2].state = 62;
+ break;
+ case INV_BOOT:
+ case INV_HAND:
+ case INV_DUST:
+ _actor[0].weaponClass = 2;
+ _actor[0].act[2].state = 1;
+ break;
+ default:
+ break;
+ }
+ return _actor[0].act[2].state;
+}
+
+void Insane::ouchSoundBen(void) {
+ _actor[0].act[3].state = 52;
+
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ smlayer_startVoice(54);
+ return;
+ }
+
+ switch (_vm->_rnd.getRandomNumber(3)) {
+ case 0:
+ smlayer_startVoice(315);
+ break;
+ case 1:
+ smlayer_startVoice(316);
+ break;
+ case 2:
+ smlayer_startVoice(317);
+ break;
+ case 3:
+ smlayer_startVoice(98);
+ break;
+ }
+}
+
+}
diff --git a/engines/scumm/insane/insane_enemy.cpp b/engines/scumm/insane/insane_enemy.cpp
new file mode 100644
index 0000000000..d67fc7ae09
--- /dev/null
+++ b/engines/scumm/insane/insane_enemy.cpp
@@ -0,0 +1,2813 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "base/engine.h"
+
+#include "scumm/insane/insane.h"
+
+namespace Scumm {
+
+int32 Insane::enemyInitializer(int num, int32 actor1, int32 actor2, int32 probability) {
+ switch (num) {
+ case EN_ROTT1:
+ return enemy0initializer(actor1, actor2, probability);
+ break;
+ case EN_ROTT2:
+ return enemy1initializer(actor1, actor2, probability);
+ break;
+ case EN_ROTT3:
+ return enemy2initializer(actor1, actor2, probability);
+ break;
+ case EN_VULTF1:
+ return enemy3initializer(actor1, actor2, probability);
+ break;
+ case EN_VULTM1:
+ return enemy4initializer(actor1, actor2, probability);
+ break;
+ case EN_VULTF2:
+ return enemy5initializer(actor1, actor2, probability);
+ break;
+ case EN_VULTM2:
+ return enemy6initializer(actor1, actor2, probability);
+ break;
+ case EN_CAVEFISH:
+ return enemy7initializer(actor1, actor2, probability);
+ break;
+ case EN_TORQUE:
+ return enemy8initializer(actor1, actor2, probability);
+ break;
+ case -1:
+ // nothing
+ break;
+ }
+
+ return 0;
+}
+
+
+int32 Insane::enemyHandler(int num, int32 actor1, int32 actor2, int32 probability) {
+ switch (num) {
+ case EN_ROTT1:
+ return enemy0handler(actor1, actor2, probability);
+ break;
+ case EN_ROTT2:
+ return enemy1handler(actor1, actor2, probability);
+ break;
+ case EN_ROTT3:
+ return enemy2handler(actor1, actor2, probability);
+ break;
+ case EN_VULTF1:
+ return enemy3handler(actor1, actor2, probability);
+ break;
+ case EN_VULTM1:
+ return enemy4handler(actor1, actor2, probability);
+ break;
+ case EN_VULTF2:
+ return enemy5handler(actor1, actor2, probability);
+ break;
+ case EN_VULTM2:
+ return enemy6handler(actor1, actor2, probability);
+ break;
+ case EN_CAVEFISH:
+ return enemy7handler(actor1, actor2, probability);
+ break;
+ case EN_TORQUE:
+ return enemy8handler(actor1, actor2, probability);
+ break;
+ case EN_BEN:
+ return enemyBenHandler(actor1, actor2, probability);
+ break;
+ case -1:
+ // nothing
+ break;
+ }
+ return 0;
+}
+
+int32 Insane::enemy0handler(int32 actor1, int32 actor2, int32 probability) {
+ int32 act1damage, act2damage, act1x, act2x, retval;
+ int32 dist;
+
+ retval = 0;
+ act1damage = _actor[actor1].damage; // ebx
+ act2damage = _actor[actor2].damage; // ebp
+ act1x = _actor[actor1].x; // esi
+ act2x = _actor[actor2].x; // edi
+
+ if (!_actor[actor1].defunct) {
+ if (_enHdlVar[EN_ROTT1][1] > _enHdlVar[EN_ROTT1][2]) {
+ if (act1damage - act2damage >= 30) {
+ if (_vm->_rnd.getRandomNumber(probability - 1) != 1)
+ _enHdlVar[EN_ROTT1][0] = 0;
+ else
+ _enHdlVar[EN_ROTT1][0] = 1;
+ }
+ _enHdlVar[EN_ROTT1][1] = 0;
+ _enHdlVar[EN_ROTT1][2] = _vm->_rnd.getRandomNumber(probability * 2 - 1);
+ }
+
+ dist = ABS(act1x - act2x);
+
+ if (_enHdlVar[EN_ROTT1][3] > _enHdlVar[EN_ROTT1][4]) {
+ if (_enHdlVar[EN_ROTT1][0] == 1) {
+ if (weaponMaxRange(actor1) < dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = -101;
+ else
+ _actor[actor1].cursorX = 101;
+ } else {
+ if (weaponMinRange(actor1) > dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ } else {
+ if (weaponMaxRange(actor2) >= dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ _enHdlVar[EN_ROTT1][3] = 0;
+ _enHdlVar[EN_ROTT1][4] = _vm->_rnd.getRandomNumber(probability - 1);
+ }
+
+ if (_enHdlVar[EN_ROTT1][5] > _enHdlVar[EN_ROTT1][6]) {
+ if (weaponMaxRange(actor2) + 40 >= dist) {
+ if (_vm->_rnd.getRandomNumber(probability - 1) == 1)
+ retval = 1;
+ }
+ if (_actor[actor2].kicking) {
+ if (weaponMaxRange(actor2) >= dist)
+ if (_vm->_rnd.getRandomNumber(probability * 2 - 1) <= 1)
+ retval = 1;
+ }
+ _enHdlVar[EN_ROTT1][5] = 0;
+ _enHdlVar[EN_ROTT1][6] = _vm->_rnd.getRandomNumber(probability - 1) / 2;
+ }
+
+ if (_actor[actor1].weapon == -1)
+ retval = 2;
+
+ if ((_actor[actor1].field_54 == 0) &&
+ (_actor[actor2].lost == 0) &&
+ (_actor[actor1].lost == 0)) {
+ if (_actor[actor1].act[3].state == 54) {
+ switch (_vm->_rnd.getRandomNumber(9)) {
+ case 3:
+ if (!_enemyState[EN_ROTT1][6]) {
+ _enemyState[EN_ROTT1][6] = 1;
+ prepareScenePropScene(54, 0, 0);
+ }
+ break;
+ case 8:
+ if (!_enemyState[EN_ROTT1][4]) {
+ _enemyState[EN_ROTT1][4] = 1;
+ prepareScenePropScene(52, 0, 0);
+ }
+ break;
+ }
+ } else {
+ switch(_vm->_rnd.getRandomNumber(14)) {
+ case 2:
+ if (!_enemyState[EN_ROTT1][2]) {
+ _enemyState[EN_ROTT1][2] = 1;
+ prepareScenePropScene(50, 0, 0);
+ }
+ break;
+ case 4:
+ if (!_enemyState[EN_ROTT1][3]) {
+ _enemyState[EN_ROTT1][3] = 1;
+ prepareScenePropScene(51, 0, 0);
+ }
+ break;
+ case 6:
+ if (!_enemyState[EN_ROTT1][7]) {
+ _enemyState[EN_ROTT1][7] = 1;
+ if (_enemy[EN_ROTT1].occurences)
+ prepareScenePropScene(55, 0, 0);
+ }
+ break;
+ case 9:
+ if (!_enemyState[EN_ROTT1][5]) {
+ _enemyState[EN_ROTT1][5] = 1;
+ prepareScenePropScene(53, 0, 0);
+ }
+ break;
+ case 11:
+ if (!_enemyState[EN_ROTT1][8]) {
+ _enemyState[EN_ROTT1][8] = 1;
+ prepareScenePropScene(56, 0, 0);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ _enHdlVar[EN_ROTT1][1]++;
+ _enHdlVar[EN_ROTT1][3]++;
+ _enHdlVar[EN_ROTT1][5]++;
+ }
+
+ if (act1x > 310)
+ _actor[actor1].cursorX = -320;
+ else if (act1x < 10)
+ _actor[actor1].cursorX = 320;
+ else if (act1x > 280)
+ _actor[actor1].cursorX = -160;
+
+ // Shift+V cheat to win the battle
+ if (_vm->getKeyState(0x56) && !_beenCheated &&
+ !_actor[0].lost && !_actor[1].lost) {
+ _beenCheated = 1;
+ _actor[1].damage = _actor[1].maxdamage + 10;
+ }
+
+ return retval;
+}
+
+int32 Insane::enemy0initializer(int32 actor1, int32 actor2, int32 probability) {
+ int i;
+
+ for (i = 0; i < 9; i++)
+ _enemyState[EN_ROTT1][i] = 0;
+
+ for (i = 0; i < 9; i++)
+ _enHdlVar[EN_ROTT1][i] = 0;
+
+ _beenCheated = 0;
+
+ return 1;
+}
+
+int32 Insane::enemy1handler(int32 actor1, int32 actor2, int32 probability) {
+ int32 act1damage, act2damage, act1x, act2x, retval;
+ int32 dist;
+
+ retval = 0;
+ act1damage = _actor[actor1].damage; // ebx
+ act2damage = _actor[actor2].damage; // ebp
+ act1x = _actor[actor1].x; // esi
+ act2x = _actor[actor2].x; // edi
+
+ if (!_actor[actor1].defunct) {
+ if (_enHdlVar[EN_ROTT2][1] > _enHdlVar[EN_ROTT2][2]) {
+ if (act1damage - act2damage >= 30) {
+ if (_vm->_rnd.getRandomNumber(probability - 1) != 1)
+ _enHdlVar[EN_ROTT2][0] = 0;
+ else
+ _enHdlVar[EN_ROTT2][0] = 1;
+ }
+ _enHdlVar[EN_ROTT2][1] = 0;
+ _enHdlVar[EN_ROTT2][2] = _vm->_rnd.getRandomNumber(probability * 2 - 1);
+ }
+
+ dist = ABS(act1x - act2x);
+
+ if (_enHdlVar[EN_ROTT2][3] > _enHdlVar[EN_ROTT2][4]) {
+ if (_enHdlVar[EN_ROTT2][0] == 1) {
+ if (weaponMaxRange(actor1) < dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = -101;
+ else
+ _actor[actor1].cursorX = 101;
+ } else {
+ if (weaponMinRange(actor1) > dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ } else {
+ if (weaponMaxRange(actor2) >= dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ _enHdlVar[EN_ROTT2][3] = 0;
+ _enHdlVar[EN_ROTT2][4] = _vm->_rnd.getRandomNumber(probability - 1);
+ }
+
+ if (_enHdlVar[EN_ROTT2][5] > _enHdlVar[EN_ROTT2][6]) {
+ if (weaponMaxRange(actor2) + 40 >= dist) {
+ if (_vm->_rnd.getRandomNumber(probability - 1) == 1)
+ retval = 1;
+ }
+ if (_actor[actor2].kicking) {
+ if (weaponMaxRange(actor2) <= dist)
+ if (_vm->_rnd.getRandomNumber(probability * 2 - 1) <= 1)
+ retval = 1;
+ }
+ _enHdlVar[EN_ROTT1][5] = 0;
+ _enHdlVar[EN_ROTT1][6] = _vm->_rnd.getRandomNumber(probability - 1) / 2;
+ }
+
+ if (_actor[actor1].weapon == -1)
+ retval = 2;
+
+ if ((_actor[actor1].field_54 == 0) &&
+ (_actor[actor2].lost == 0) &&
+ (_actor[actor1].lost == 0)) {
+ if (_actor[actor1].act[3].state == 54) {
+ switch (_vm->_rnd.getRandomNumber(9)) {
+ case 3:
+ if (!_enemyState[EN_ROTT2][6]) {
+ _enemyState[EN_ROTT2][6] = 1;
+ prepareScenePropScene(38, 0, 0);
+ }
+ break;
+ case 8:
+ if (!_enemyState[EN_ROTT2][5]) {
+ _enemyState[EN_ROTT2][5] = 1;
+ prepareScenePropScene(37, 0, 0);
+ }
+ break;
+ }
+ } else {
+ switch(_vm->_rnd.getRandomNumber(14)) {
+ case 2:
+ if (!_enemyState[EN_ROTT2][2]) {
+ _enemyState[EN_ROTT2][2] = 1;
+ prepareScenePropScene(34, 0, 0);
+ }
+ break;
+ case 11:
+ if (!_enemyState[EN_ROTT1][7]) {
+ _enemyState[EN_ROTT1][7] = 1;
+ prepareScenePropScene(39, 0, 0);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ _enHdlVar[EN_ROTT2][1]++;
+ _enHdlVar[EN_ROTT2][3]++;
+ _enHdlVar[EN_ROTT2][5]++;
+ }
+
+ if (act1x > 310)
+ _actor[actor1].cursorX = -320;
+ else if (act1x < 10)
+ _actor[actor1].cursorX = 320;
+ else if (act1x > 280)
+ _actor[actor1].cursorX = -160;
+
+ // Shift+V cheat to win the battle
+ if (_vm->getKeyState(0x56) && !_beenCheated &&
+ !_actor[0].lost && !_actor[1].lost) {
+ _beenCheated = 1;
+ _actor[1].damage = _actor[1].maxdamage + 10;
+ }
+
+ return retval;
+}
+
+int32 Insane::enemy1initializer(int32 actor1, int32 actor2, int32 probability) {
+ int i;
+
+ for (i = 0; i < 9; i++)
+ _enemyState[EN_ROTT2][i] = 0;
+
+ for (i = 0; i < 9; i++)
+ _enHdlVar[EN_ROTT2][i] = 0;
+
+ _beenCheated = 0;
+
+ return 1;
+}
+
+int32 Insane::enemy2handler(int32 actor1, int32 actor2, int32 probability) {
+ int32 act1damage, act2damage, act1x, act2x, retval;
+ int32 dist;
+
+ retval = 0;
+ act1damage = _actor[actor1].damage; // ebx
+ act2damage = _actor[actor2].damage; // ebp
+ act1x = _actor[actor1].x; // esi
+ act2x = _actor[actor2].x; // edi
+
+ if (!_actor[actor1].defunct) {
+ if (_enHdlVar[EN_ROTT3][1] > _enHdlVar[EN_ROTT3][2]) {
+ if (act1damage - act2damage >= 30) {
+ if (_vm->_rnd.getRandomNumber(probability - 1) != 1)
+ _enHdlVar[EN_ROTT3][0] = 0;
+ else
+ _enHdlVar[EN_ROTT3][0] = 1;
+ }
+ _enHdlVar[EN_ROTT3][1] = 0;
+ _enHdlVar[EN_ROTT3][2] = _vm->_rnd.getRandomNumber(probability * 2 - 1);
+ }
+
+ dist = ABS(act1x - act2x);
+
+ if (_enHdlVar[EN_ROTT3][3] > _enHdlVar[EN_ROTT3][4]) {
+ if (_enHdlVar[EN_ROTT3][0] == 1) {
+ if (weaponMaxRange(actor1) < dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = -101;
+ else
+ _actor[actor1].cursorX = 101;
+ } else {
+ if (weaponMinRange(actor1) > dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ } else {
+ if (weaponMaxRange(actor2) >= dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ _enHdlVar[EN_ROTT3][3] = 0;
+ _enHdlVar[EN_ROTT3][4] = _vm->_rnd.getRandomNumber(probability - 1);
+ }
+ if (_enHdlVar[EN_ROTT3][5] > _enHdlVar[EN_ROTT3][6]) {
+ if (weaponMaxRange(actor2) + 40 >= dist) {
+ if (_vm->_rnd.getRandomNumber(probability - 1) == 1)
+ retval = 1;
+ }
+ if (_actor[actor2].kicking) {
+ if (weaponMaxRange(actor2) >= dist)
+ if (_vm->_rnd.getRandomNumber(probability * 2 - 1) <= 1)
+ retval = 1;
+ }
+ _enHdlVar[EN_ROTT3][5] = 0;
+ _enHdlVar[EN_ROTT3][6] = _vm->_rnd.getRandomNumber(probability - 1) / 2;
+ }
+
+ if (_actor[actor1].weapon == -1)
+ retval = 2;
+
+ if ((_actor[actor1].field_54 == 0) &&
+ (_actor[actor2].lost == 0) &&
+ (_actor[actor1].lost == 0)) {
+ if (_actor[actor1].act[3].state == 54) {
+ switch (_vm->_rnd.getRandomNumber(9)) {
+ case 3:
+ if (!_enemyState[EN_ROTT3][1]) {
+ _enemyState[EN_ROTT3][1] = 1;
+ prepareScenePropScene(26, 0, 0);
+ }
+ break;
+ case 5:
+ if (!_enemyState[EN_ROTT3][3]) {
+ _enemyState[EN_ROTT3][3] = 1;
+ prepareScenePropScene(28, 0, 0);
+ }
+ break;
+ case 8:
+ if (!_enemyState[EN_ROTT3][2]) {
+ _enemyState[EN_ROTT3][2] = 1;
+ prepareScenePropScene(27, 0, 0);
+ }
+ break;
+ }
+ } else {
+ if (_actor[actor1].kicking) {
+ if (_vm->_rnd.getRandomNumber(9) == 9) {
+ if (!_enemyState[EN_ROTT3][6]) {
+ _enemyState[EN_ROTT3][6] = 1;
+ prepareScenePropScene(31, 0, 0);
+ }
+ }
+ } else {
+ if (_vm->_rnd.getRandomNumber(14) == 7) {
+ if (!_enemyState[EN_ROTT3][5]) {
+ _enemyState[EN_ROTT3][5] = 1;
+ prepareScenePropScene(30, 0, 0);
+ }
+ }
+ }
+ }
+ }
+ _enHdlVar[EN_ROTT3][1]++;
+ _enHdlVar[EN_ROTT3][3]++;
+ _enHdlVar[EN_ROTT3][5]++;
+ }
+
+ if (act1x > 310)
+ _actor[actor1].cursorX = -320;
+ else if (act1x < 10)
+ _actor[actor1].cursorX = 320;
+ else if (act1x > 280)
+ _actor[actor1].cursorX = -160;
+
+ // Shift+V cheat to win the battle
+ if (_vm->getKeyState(0x56) && !_beenCheated &&
+ !_actor[0].lost && !_actor[1].lost) {
+ _beenCheated = 1;
+ _actor[1].damage = _actor[1].maxdamage + 10;
+ }
+
+ return retval;
+}
+
+int32 Insane::enemy2initializer(int32 actor1, int32 actor2, int32 probability) {
+ int i;
+
+ for (i = 0; i < 7; i++)
+ _enemyState[EN_ROTT3][i] = 0;
+
+ for (i = 0; i < 9; i++)
+ _enHdlVar[EN_ROTT3][i] = 0;
+
+ _beenCheated = 0;
+
+ return 1;
+}
+
+int32 Insane::enemy3handler(int32 actor1, int32 actor2, int32 probability) {
+ int32 act1damage, act2damage, act1x, act2x, retval;
+ int32 dist;
+
+ retval = 0;
+ act1damage = _actor[actor1].damage; // ebx
+ act2damage = _actor[actor2].damage; // ebp
+ act1x = _actor[actor1].x; // esi
+ act2x = _actor[actor2].x; // edi
+
+ if (!_actor[actor1].defunct) {
+ if (_enHdlVar[EN_VULTF1][1] > _enHdlVar[EN_VULTF1][2]) {
+ if ((act1damage - act2damage >= 30) && (_vm->_rnd.getRandomNumber(probability - 1) != 1))
+ _enHdlVar[EN_VULTF1][0] = 0;
+ else
+ _enHdlVar[EN_VULTF1][0] = 1;
+
+ _enHdlVar[EN_VULTF1][1] = 0;
+ _enHdlVar[EN_VULTF1][2] = _vm->_rnd.getRandomNumber(probability * 2 - 1);
+ }
+
+ dist = ABS(act1x - act2x);
+
+ if (_enHdlVar[EN_VULTF1][3] > _enHdlVar[EN_VULTF1][4]) {
+ if (_enHdlVar[EN_VULTF1][0] == 1) {
+ if (weaponMaxRange(actor1) < dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = -101;
+ else
+ _actor[actor1].cursorX = 101;
+ } else {
+ if (weaponMinRange(actor1) > dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ } else {
+ if (weaponMaxRange(actor2) >= dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ _enHdlVar[EN_VULTF1][3] = 0;
+ _enHdlVar[EN_VULTF1][4] = _vm->_rnd.getRandomNumber(probability - 1);
+ }
+
+ if (_enHdlVar[EN_VULTF1][5] > _enHdlVar[EN_VULTF1][6]) {
+ if (_enHdlVar[EN_VULTF1][0] == 1) {
+ if (weaponMaxRange(actor2) + 40 >= dist)
+ if (_vm->_rnd.getRandomNumber(probability - 1) == 1)
+ retval = 1;
+ } else {
+ if (_actor[actor1].kicking)
+ if (weaponMaxRange(actor2) >= dist)
+ if (_vm->_rnd.getRandomNumber(probability - 1) <= 1)
+ retval = 1;
+ }
+ _enHdlVar[EN_VULTF1][5] = 0;
+ _enHdlVar[EN_VULTF1][6] = _vm->_rnd.getRandomNumber(probability - 1) / 2;
+ }
+
+ if (_actor[actor1].weapon == -1)
+ retval = 2;
+
+ if ((_actor[actor1].field_54 == 0) &&
+ (_actor[actor2].lost == 0) &&
+ (_actor[actor1].lost == 0)) {
+ _enHdlVar[EN_VULTF1][8] = _vm->_rnd.getRandomNumber(25);
+ if (_enHdlVar[EN_VULTF1][8] != _enHdlVar[EN_VULTF1][7]) {
+ switch (_enHdlVar[EN_VULTF1][8]) {
+ case 0:
+ if (!_enemyState[EN_VULTF1][4]) {
+ _enemyState[EN_VULTF1][4] = 1;
+ prepareScenePropScene(3, 0, 0);
+ }
+ break;
+ case 1:
+ if (!_enemyState[EN_VULTF1][1]) {
+ _enemyState[EN_VULTF1][1] = 1;
+ prepareScenePropScene(4, 0, 0);
+ }
+ break;
+ case 2:
+ if (!_enemyState[EN_VULTF1][2]) {
+ _enemyState[EN_VULTF1][2] = 1;
+ prepareScenePropScene(5, 0, 0);
+ }
+ break;
+ case 3:
+ if (!_enemyState[EN_VULTF1][3]) {
+ _enemyState[EN_VULTF1][3] = 1;
+ prepareScenePropScene(6, 0, 0);
+ }
+ break;
+ case 4:
+ if (!_enemyState[EN_VULTF1][4]) {
+ _enemyState[EN_VULTF1][4] = 1;
+ prepareScenePropScene(7, 0, 0);
+ }
+ break;
+ case 5:
+ if (!_enemyState[EN_VULTF1][5]) {
+ _enemyState[EN_VULTF1][5] = 1;
+ prepareScenePropScene(8, 0, 0);
+ }
+ break;
+ }
+ _enHdlVar[EN_VULTF1][7] = _enHdlVar[EN_VULTF1][8];
+ }
+
+ }
+ _enHdlVar[EN_VULTF1][1]++;
+ _enHdlVar[EN_VULTF1][3]++;
+ _enHdlVar[EN_VULTF1][5]++;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+
+ if (act1x > 310)
+ _actor[actor1].cursorX = -320;
+ else if (act1x < 10)
+ _actor[actor1].cursorX = 320;
+ else if (act1x > 280)
+ _actor[actor1].cursorX = -160;
+
+ // Shift+V cheat to win the battle
+ if (_vm->getKeyState(0x56) && !_beenCheated &&
+ !_actor[0].lost && !_actor[1].lost) {
+ _beenCheated = 1;
+ _actor[1].damage = _actor[1].maxdamage + 10;
+ }
+
+ return retval;
+}
+
+int32 Insane::enemy3initializer(int32 actor1, int32 actor2, int32 probability) {
+ int i;
+
+ for (i = 0; i < 6; i++)
+ _enemyState[EN_VULTF1][i] = 0;
+
+ for (i = 0; i < 9; i++)
+ _enHdlVar[EN_VULTF1][i] = 0;
+
+ _beenCheated = 0;
+
+ return 1;
+}
+
+int32 Insane::enemy4handler(int32 actor1, int32 actor2, int32 probability) {
+ int32 act1damage, act2damage, act1x, act2x, retval;
+ int32 dist;
+
+ retval = 0;
+ act1damage = _actor[actor1].damage; // ebx
+ act2damage = _actor[actor2].damage; // ebp
+ act1x = _actor[actor1].x; // esi
+ act2x = _actor[actor2].x; // edi
+
+ if (!_actor[actor1].defunct) {
+ if (_enHdlVar[EN_VULTM1][1] > _enHdlVar[EN_VULTM1][2]) {
+ if (act1damage - act2damage >= 30) {
+ if (_vm->_rnd.getRandomNumber(probability - 1) != 1)
+ _enHdlVar[EN_VULTM1][0] = 0;
+ else
+ _enHdlVar[EN_VULTM1][0] = 1;
+ }
+ _enHdlVar[EN_VULTM1][1] = 0;
+ _enHdlVar[EN_VULTM1][2] = _vm->_rnd.getRandomNumber(probability * 2 - 1);
+ }
+
+ dist = ABS(act1x - act2x);
+
+ if (_enHdlVar[EN_VULTM1][3] > _enHdlVar[EN_VULTM1][4]) {
+ if (_enHdlVar[EN_VULTM1][0] == 1) {
+ if (weaponMaxRange(actor1) < dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = -101;
+ else
+ _actor[actor1].cursorX = 101;
+ } else {
+ if (weaponMinRange(actor1) > dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ } else {
+ if (weaponMaxRange(actor2) >= dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+ _enHdlVar[EN_VULTM1][3] = 0;
+ _enHdlVar[EN_VULTM1][4] = _vm->_rnd.getRandomNumber(probability - 1);
+ }
+ if (_enHdlVar[EN_VULTM1][5] > _enHdlVar[EN_VULTM1][6]) {
+ if (weaponMaxRange(actor2) + 40 >= dist) {
+ if (_vm->_rnd.getRandomNumber(probability - 1) == 1)
+ retval = 1;
+ }
+ if (_actor[actor2].kicking) {
+ if (weaponMaxRange(actor2) >= dist) // that's weird but original is >=
+ if (_vm->_rnd.getRandomNumber(probability * 2 - 1) <= 1)
+ retval = 1;
+ }
+ _enHdlVar[EN_VULTM1][5] = 0;
+ _enHdlVar[EN_VULTM1][6] = _vm->_rnd.getRandomNumber(probability - 1) / 2;
+ }
+
+ if (_actor[actor1].weapon == -1)
+ retval = 2;
+
+ if ((_actor[actor1].field_54 == 0) &&
+ (_actor[actor2].lost == 0) &&
+ (_actor[actor1].lost == 0)) {
+ if (_actor[actor1].act[3].state == 54) {
+ switch (_vm->_rnd.getRandomNumber(9)) {
+ case 4:
+ if (!_enemyState[EN_VULTM1][7]) {
+ _enemyState[EN_VULTM1][7] = 1;
+ prepareScenePropScene(46, 0, 0);
+ }
+ break;
+ case 8:
+ if (!_enemyState[EN_VULTM1][8]) {
+ _enemyState[EN_VULTM1][8] = 1;
+ prepareScenePropScene(47, 0, 0);
+ }
+ break;
+ }
+ } else {
+ if (_actor[actor1].kicking) {
+ switch (_vm->_rnd.getRandomNumber(9)) {
+ case 3:
+ prepareScenePropScene(44, 0, 0);
+ break;
+ case 9:
+ prepareScenePropScene(45, 0, 0);
+ break;
+ }
+ } else {
+ if (weaponMaxRange(actor2) <= dist) {
+ switch (_vm->_rnd.getRandomNumber(9)) {
+ case 3:
+ if (!_enemyState[EN_VULTM1][3]) {
+ _enemyState[EN_VULTM1][3] = 1;
+ prepareScenePropScene(42, 0, 0);
+ }
+ break;
+ case 9:
+ if (!_enemyState[EN_VULTM1][4]) {
+ _enemyState[EN_VULTM1][4] = 1;
+ prepareScenePropScene(43, 0, 0);
+ }
+ break;
+ }
+ } else {
+ switch (_vm->_rnd.getRandomNumber(14)) {
+ case 7:
+ if (!_enemyState[EN_VULTM1][9]) {
+ _enemyState[EN_VULTM1][9] = 1;
+ prepareScenePropScene(48, 0, 0);
+ }
+ break;
+ case 11:
+ if (!_enemyState[EN_VULTM1][1]) {
+ _enemyState[EN_VULTM1][1] = 1;
+ prepareScenePropScene(40, 0, 0);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ _enHdlVar[EN_VULTM1][1]++;
+ _enHdlVar[EN_VULTM1][3]++;
+ _enHdlVar[EN_VULTM1][5]++;
+ }
+
+ if (act1x > 310)
+ _actor[actor1].cursorX = -320;
+ else if (act1x < 10)
+ _actor[actor1].cursorX = 320;
+ else if (act1x > 280)
+ _actor[actor1].cursorX = -160;
+
+ // Shift+V cheat to win the battle
+ if (_vm->getKeyState(0x56) && !_beenCheated &&
+ !_actor[0].lost && !_actor[1].lost) {
+ _beenCheated = 1;
+ _actor[1].damage = _actor[1].maxdamage + 10;
+ }
+
+ return retval;
+}
+
+int32 Insane::enemy4initializer(int32 actor1, int32 actor2, int32 probability) {
+ int i;
+
+ for (i = 0; i < 10; i++)
+ _enemyState[EN_VULTM1][i] = 0;
+
+ for (i = 0; i < 9; i++)
+ _enHdlVar[EN_VULTM1][i] = 0;
+
+ _beenCheated = 0;
+
+ return 1;
+}
+
+int32 Insane::enemy5handler(int32 actor1, int32 actor2, int32 probability) {
+ int32 act1damage, act1x, act2x, retval;
+ int32 dist;
+
+ retval = 0;
+ act1damage = _actor[actor1].damage; // ebx
+ act1x = _actor[actor1].x; // esi
+ act2x = _actor[actor2].x; // ebp
+
+ dist = ABS(act1x - act2x);
+
+ if (weaponMaxRange(actor1) >= dist) {
+ if (!_enHdlVar[EN_VULTF2][2])
+ _enHdlVar[EN_VULTF2][3]++;
+ _enHdlVar[EN_VULTF2][1] = 1;
+ } else {
+ _enHdlVar[EN_VULTF2][1] = 0;
+ }
+
+ if (!_actor[actor1].defunct) {
+ if (_enHdlVar[EN_VULTF2][3] >= 2 || act1damage) {
+ _actor[actor1].damage = 10;
+
+ if (weaponMaxRange(actor1) <= dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = -101;
+ else
+ _actor[actor1].cursorX = 101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+
+ if (weaponMaxRange(actor1) + 20 >= dist)
+ if (_vm->_rnd.getRandomNumber(probability - 1) == 1)
+ retval = 1;
+ } else {
+ if (weaponMaxRange(actor2) >= dist && _actor[actor2].weapon == INV_CHAINSAW)
+ if (!_actor[actor2].kicking) {
+ if (_vm->_rnd.getRandomNumber(probability - 1) == 1)
+ retval = 1;
+ } else {
+ retval = 1;
+ }
+ _actor[actor1].cursorX = 0;
+ if (_enHdlVar[EN_VULTF2][0] >= 100)
+ _enHdlVar[EN_VULTF2][3] = 3;
+ }
+
+ if ((_actor[actor1].field_54 == 0) &&
+ (_actor[actor2].lost == 0) &&
+ (_actor[actor1].lost == 0)) {
+ if (_actor[actor1].act[3].state == 54)
+ switch (_vm->_rnd.getRandomNumber(9)) {
+ case 4:
+ if (!_enemyState[EN_VULTF2][6]) {
+ _enemyState[EN_VULTF2][6] = 1;
+ prepareScenePropScene(15, 0, 0);
+ }
+ break;
+ case 8:
+ if (!_enemyState[EN_VULTF2][3]) {
+ _enemyState[EN_VULTF2][3] = 1;
+ prepareScenePropScene(12, 0, 0);
+ }
+ break;
+ }
+ else {
+ if (_actor[actor1].kicking) {
+ switch (_vm->_rnd.getRandomNumber(9)) {
+ case 2:
+ if (!_enemyState[EN_VULTF2][8]) {
+ _enemyState[EN_VULTF2][8] = 1;
+ prepareScenePropScene(17, 0, 0);
+ }
+ break;
+ case 5:
+ prepareScenePropScene(11, 0, 0);
+ _enemyState[EN_VULTF2][2] = 1;
+ break;
+ case 9:
+ _enemyState[EN_VULTF2][1] = 1;
+ prepareScenePropScene(10, 0, 0);
+ break;
+ }
+ } else {
+ switch (_vm->_rnd.getRandomNumber(14)) {
+ case 3:
+ if (!_enemyState[EN_VULTF2][4]) {
+ _enemyState[EN_VULTF2][4] = 1;
+ prepareScenePropScene(13, 0, 0);
+ }
+ break;
+ case 11:
+ if (!_enemyState[EN_VULTF2][5]) {
+ _enemyState[EN_VULTF2][5] = 1;
+ prepareScenePropScene(14, 0, 0);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (_actor[actor1].defunct)
+ _actor[actor1].cursorX = 0;
+
+ if (_actor[actor1].weapon == -1)
+ retval = 2;
+
+ if (act1x > 310)
+ _actor[actor1].cursorX = -320;
+ else if (act1x < 10)
+ _actor[actor1].cursorX = 320;
+ else if (act1x > 280)
+ _actor[actor1].cursorX = -160;
+
+ _enHdlVar[EN_VULTF2][2] = _enHdlVar[EN_VULTF2][1];
+ _enHdlVar[EN_VULTF2][0]++;
+
+ // Shift+V cheat to win the battle
+ if (_vm->getKeyState(0x56) && !_beenCheated &&
+ !_actor[0].lost && !_actor[1].lost) {
+ _beenCheated = 1;
+ _actor[1].damage = _actor[1].maxdamage + 10;
+ _actor[1].act[2].state = 113;
+ }
+
+ return retval;
+}
+
+int32 Insane::enemy5initializer(int32 actor1, int32 actor2, int32 probability) {
+ int i;
+
+ for (i = 0; i < 9; i++)
+ _enemyState[EN_VULTF2][i] = 0;
+
+ for (i = 0; i < 9; i++)
+ _enHdlVar[EN_VULTF2][i] = 0;
+
+ _beenCheated = 0;
+
+ return 1;
+}
+
+int32 Insane::enemy6handler(int32 actor1, int32 actor2, int32 probability) {
+ int32 act1damage, act2damage, act1x, act2x, retval;
+ int32 dist;
+
+ retval = 0;
+ act1damage = _actor[actor1].damage; // ebx //ebx
+ act2damage = _actor[actor2].damage; // ebp // edi
+ act1x = _actor[actor1].x; // esi
+ act2x = _actor[actor2].x; // edi
+
+ if (_actor[actor2].weapon == INV_CHAINSAW)
+ retval = 1;
+
+ dist = ABS(act1x - act2x);
+
+ if (_actor[actor1].defunct) {
+ /* scenePropIdx[18] */
+ if (_currScenePropIdx == 50 && _currScenePropSubIdx == 3)
+ retval = 1;
+ } else {
+ if (act1damage > 0 || _enHdlVar[EN_VULTM2][0] > 20) {
+ _actor[actor1].damage = 10;
+ if (!_enHdlVar[EN_VULTM2][1] && !_actor[actor1].lost) {
+ if (!_actor[actor1].field_54) {
+ switch (_vm->_rnd.getRandomNumber(3)) {
+ case 0:
+ if (!_enemyState[EN_VULTM2][1]) {
+ _enemyState[EN_VULTM2][1] = 1;
+ prepareScenePropScene(19, 0, 0);
+ }
+ break;
+ case 1:
+ if (!_enemyState[EN_VULTM2][2]) {
+ _enemyState[EN_VULTM2][2] = 1;
+ prepareScenePropScene(20, 0, 0);
+ }
+ break;
+ case 2:
+ if (!_enemyState[EN_VULTM2][3]) {
+ _enemyState[EN_VULTM2][3] = 1;
+ prepareScenePropScene(21, 0, 0);
+ }
+ break;
+ case 3:
+ if (!_enemyState[EN_VULTM2][4]) {
+ _enemyState[EN_VULTM2][4] = 1;
+ prepareScenePropScene(22, 0, 0);
+ }
+ break;
+ }
+ _enHdlVar[EN_VULTM2][1] = 1;
+ goto _label1;
+ }
+ } else {
+ if (_actor[actor1].field_54 == 0 &&
+ _actor[actor1].lost == 0) {
+ retval = 1;
+ _enHdlVar[EN_VULTM2][0] = 0;
+ }
+ }
+ } else {
+ if (weaponMaxRange(actor2) >= dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ }
+
+ if ((_enHdlVar[EN_VULTM2][1] == 0) &&
+ ( _actor[actor1].field_54 == 0) &&
+ (_actor[actor2].lost == 0) &&
+ (_actor[actor1].lost == 0)) {
+ switch (_vm->_rnd.getRandomNumber(14)) {
+ case 2:
+ if (!_enemyState[EN_VULTM2][5]) {
+ _enemyState[EN_VULTM2][5] = 1;
+ prepareScenePropScene(23, 0, 0);
+ }
+ break;
+ case 7:
+ if (!_enemyState[EN_VULTF1][6]) {
+ _enemyState[EN_VULTF1][6] = 1;
+ prepareScenePropScene(24, 0, 0);
+ }
+ break;
+ }
+ }
+ }
+
+ _label1:
+ if (act1x > 310)
+ _actor[actor1].cursorX = -320;
+ else if (act1x < 219)
+ _actor[actor1].cursorX = 320;
+ else if (act1x > 280)
+ _actor[actor1].cursorX = -160;
+
+ if (_actor[actor1].weapon == -1)
+ retval = 2;
+
+ // Shift+V cheat to win the battle
+ if (_vm->getKeyState(0x56) && !_beenCheated &&
+ !_actor[0].lost && !_actor[1].lost) {
+ _beenCheated = 1;
+ _actor[0].act[2].state = 97;
+ smlayer_setActorFacing(0, 2, 20, 180);
+ _actor[0].act[2].room = 0;
+ _actor[0].act[1].room = 0;
+ _actor[0].act[0].room = 0;
+ smlayer_setActorLayer(1, 2, 25);
+ smlayer_setActorCostume(1, 2, readArray(45));
+ smlayer_setActorFacing(1, 2, 6, 180);
+ _actor[1].act[2].state = 97;
+ _actor[1].act[2].room = 1;
+ _actor[1].act[1].room = 0;
+ _actor[1].act[0].room = 0;
+ }
+
+ if (_actor[actor1].lost)
+ retval = 0;
+
+ return retval;
+}
+
+int32 Insane::enemy6initializer(int32 actor1, int32 actor2, int32 probability) {
+ int i;
+
+ for (i = 0; i < 7; i++)
+ _enemyState[EN_VULTM2][i] = 0;
+
+ for (i = 0; i < 9; i++)
+ _enHdlVar[EN_VULTM2][i] = 0;
+
+ _beenCheated = 0;
+
+ return 1;
+}
+
+int32 Insane::enemy7handler(int32 actor1, int32 actor2, int32 probability) {
+ int32 act1damage, act1x, act2x, retval;
+ int32 dist;
+
+ retval = 0;
+ act1damage = _actor[actor1].damage; // ebx
+ act1x = _actor[actor1].x; // ebp, esi
+ act2x = _actor[actor2].x; // edi
+
+ dist = ABS(act1x - act2x);
+
+ if (_enHdlVar[EN_CAVEFISH][1] >= 600) {
+ _enHdlVar[EN_CAVEFISH][2] = 1;
+ _enHdlVar[EN_CAVEFISH][1] = 0;
+ } else {
+ if (!_enHdlVar[EN_CAVEFISH][2]) {
+ if (weaponMaxRange(actor2) + 30 >= dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = 101;
+ else
+ _actor[actor1].cursorX = -101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+ goto _labelA;
+ }
+ }
+
+ if (weaponMaxRange(actor1) <= dist) {
+ if (act2x < act1x)
+ _actor[actor1].cursorX = -101;
+ else
+ _actor[actor1].cursorX = 101;
+ } else {
+ _actor[actor1].cursorX = 0;
+ }
+
+ _labelA:
+ if (act1x > 310)
+ _actor[actor1].cursorX = -320;
+ else if (act1x < 10)
+ _actor[actor1].cursorX = 320;
+
+ if (dist <= 95)
+ retval = 1;
+
+ if (_actor[actor1].weapon == -1)
+ retval = 2;
+
+ _enHdlVar[EN_CAVEFISH][1]++;
+ _enHdlVar[EN_CAVEFISH][0] = act1damage;
+
+ // Shift+V cheat to win the battle
+ if (_vm->getKeyState(0x56) && !_beenCheated &&
+ !_actor[0].lost && !_actor[1].lost) {
+ _beenCheated = 1;
+ _actor[1].damage = _actor[1].maxdamage + 10;
+ _actor[1].act[2].state = 102;
+ }
+
+ return retval;
+}
+
+int32 Insane::enemy7initializer(int32 actor1, int32 actor2, int32 probability) {
+ int i;
+
+ for (i = 0; i < 9; i++)
+ _enHdlVar[EN_CAVEFISH][i] = 0;
+
+ _beenCheated = 0;
+
+ return 1;
+}
+
+int32 Insane::enemy8handler(int32 actor1, int32 actor2, int32 probability) {
+ _actor[actor1].cursorX = 0;
+ return 0;
+}
+
+int32 Insane::enemy8initializer(int32 actor1, int32 actor2, int32 probability) {
+ return 1;
+}
+
+
+void Insane::ouchSoundEnemy(void) {
+ int32 tmp;
+
+ _actor[1].act[3].state = 52;
+
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ smlayer_startVoice(55);
+ return;
+ }
+
+ switch (_currEnemy) {
+ case EN_VULTF1:
+ if (_actor[0].weapon == INV_DUST) {
+ smlayer_startVoice(287);
+ } else {
+ if (_vm->_rnd.getRandomNumber(1)) {
+ smlayer_startVoice(279);
+ } else {
+ smlayer_startVoice(280);
+ }
+ }
+ break;
+ case EN_VULTF2:
+ smlayer_startVoice(271);
+ break;
+ case EN_VULTM1:
+ smlayer_startVoice(162);
+ break;
+ case EN_ROTT1:
+ tmp = _vm->_rnd.getRandomNumber(2);
+
+ if (!tmp) {
+ smlayer_startVoice(216);
+ } else if (tmp == 1) {
+ smlayer_startVoice(217);
+ } else {
+ smlayer_startVoice(218);
+ }
+ break;
+ case EN_ROTT2:
+ tmp = _vm->_rnd.getRandomNumber(2);
+
+ if (!tmp) {
+ smlayer_startVoice(243);
+ } else if (tmp == 1) {
+ smlayer_startVoice(244);
+ } else {
+ smlayer_startVoice(245);
+ }
+ break;
+ case EN_VULTM2:
+ smlayer_startVoice(180);
+ break;
+ default:
+ smlayer_startVoice(99);
+ break;
+ }
+}
+
+bool Insane::loadScenePropSounds(int32 scenePropNum) {
+ int32 num = 0;
+ int32 res = 1;
+
+ if (_sceneProp[scenePropNum + num].index != 1) {
+ while (num < 12) {
+ res &= smlayer_loadSound(_sceneProp[scenePropNum + num].sound, 0, 2);
+ num = _sceneProp[scenePropNum + num].index;
+
+ if (!num)
+ break;
+ }
+ }
+
+ return res != 0;
+}
+
+void Insane::turnEnemy(bool battle) {
+ int buttons;
+
+ if (_actor[1].damage < _actor[1].maxdamage) {
+ _actor[1].lost = false;
+ } else {
+ if (!_actor[1].lost && !_actor[1].lost) {
+ _actor[1].lost = true;
+ _actor[1].act[2].state = 36;
+ _actor[1].act[1].state = 36;
+ _actor[1].act[0].state = 36;
+ _actor[1].act[1].room = 0;
+ _actor[1].act[0].room = 0;
+ }
+ }
+
+ buttons = 0;
+
+ if (_actor[1].lost == 0)
+ if (battle)
+ buttons = actionEnemy();
+
+ debug(5, "11:%d 12:%d 13:%d 10:%d", _actor[1].act[1].state,
+ _actor[1].act[2].state, _actor[1].act[3].state, _actor[1].act[0].state);
+ actor11Reaction(buttons);
+ actor12Reaction(buttons);
+ actor13Reaction(buttons);
+ actor10Reaction(buttons);
+}
+
+void Insane::actor11Reaction(int32 buttons) {
+ int32 tmpx, tmpy;
+
+ chooseEnemyWeaponAnim(buttons);
+
+ switch(_actor[1].tilt) {
+ case -3:
+ if (_actor[1].act[1].state != 41 || _actor[1].weaponClass != _actor[1].animWeaponClass) {
+ setEnemyAnimation(1, 6);
+ _actor[1].act[1].state = 41;
+ }
+
+ if (_actor[1].cursorX >= -100) {
+ setEnemyAnimation(1, 7);
+ _actor[1].act[1].state = 40;
+ _actor[1].field_8 = 48;
+ _actor[1].tilt = -2;
+ }
+
+ _actor[1].x += _actor[1].cursorX / 32;
+ break;
+ case -2:
+ if (_actor[1].act[1].state != 40 || _actor[1].weaponClass != _actor[1].animWeaponClass) {
+ setEnemyAnimation(1, 7);
+ _actor[1].act[1].state = 40;
+ }
+ if (_actor[1].field_8 == 48)
+ _actor[1].tilt = -1;
+ else
+ _actor[1].tilt = -3;
+
+ _actor[1].x += _actor[1].cursorX / 32;
+ break;
+ case -1:
+ if (_actor[1].act[1].state != 39 || _actor[1].weaponClass != _actor[1].animWeaponClass) {
+ setEnemyAnimation(1, 8);
+ _actor[1].act[1].state = 39;
+ }
+
+ if (_actor[1].field_8 == 48)
+ _actor[1].tilt = 0;
+ else
+ _actor[1].tilt = -2;
+
+ _actor[1].x += _actor[1].cursorX / 32;
+ break;
+ case 0:
+ if (_actor[1].act[1].state != 1 || _actor[1].weaponClass != _actor[1].animWeaponClass) {
+ setEnemyAnimation(1, 9);
+ _actor[1].act[1].state = 1;
+ }
+ _actor[1].field_8 = 1;
+ if (_actor[1].cursorX < -100) {
+ setEnemyAnimation(1, 8);
+ _actor[1].act[1].state = 39;
+ _actor[1].field_8 = 46;
+ _actor[1].tilt = -1;
+ } else {
+ if (_actor[1].cursorX > 100) {
+ setEnemyAnimation(1, 10);
+ _actor[1].act[1].state = 55;
+ _actor[1].field_8 = 49;
+ _actor[1].tilt = 1;
+ }
+ }
+ break;
+ case 1:
+ if (_actor[1].act[1].state != 55 || _actor[1].weaponClass != _actor[1].animWeaponClass) {
+ setEnemyAnimation(1, 10);
+ _actor[1].act[1].state = 55;
+ }
+ if (_actor[1].field_8 == 51)
+ _actor[1].tilt = 0;
+ else
+ _actor[1].tilt = 2;
+
+ _actor[1].x += _actor[1].cursorX / 32;
+ break;
+ case 2:
+ if (_actor[1].act[1].state != 56 || _actor[1].weaponClass != _actor[1].animWeaponClass) {
+ setEnemyAnimation(1, 11);
+ _actor[1].act[1].state = 56;
+ }
+ if (_actor[1].field_8 == 51)
+ _actor[1].tilt = 1;
+ else
+ _actor[1].tilt = 3;
+
+ _actor[1].x += _actor[1].cursorX / 32;
+ break;
+ case 3:
+ if (_actor[1].act[1].state != 57 || _actor[1].weaponClass != _actor[1].animWeaponClass) {
+ setEnemyAnimation(1, 12);
+ _actor[1].act[1].state = 57;
+ }
+
+ if (_actor[1].cursorX <= 100) {
+ setEnemyAnimation(1, 11);
+ _actor[1].act[1].state = 56;
+ _actor[1].field_8 = 51;
+ _actor[1].tilt = 2;
+ }
+
+ _actor[1].x += _actor[1].cursorX / 32;
+ break;
+ }
+
+ tmpx = _actor[1].x;
+ tmpy = _actor[1].y + _actor[1].y1;
+
+ if (_actor[1].act[1].room)
+ smlayer_putActor(1, 1, tmpx, tmpy, _smlayer_room2);
+ else
+ smlayer_putActor(1, 1, tmpx, tmpy, _smlayer_room);
+
+ _actor[1].animWeaponClass = _actor[1].weaponClass;
+}
+
+void Insane::chooseEnemyWeaponAnim(int32 buttons) {
+ // kick
+ if ((buttons & 1) && (!_actor[0].lost)) {
+ if (!_kickEnemyProgress && actor0StateFlags2(_actor[1].act[2].state + _actor[1].weapon * 119)) {
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ _actor[1].act[2].state = 10;
+ break;
+ case INV_CHAINSAW:
+ _actor[1].act[2].state = 14;
+ break;
+ case INV_MACE:
+ _actor[1].act[2].state = 18;
+ break;
+ case INV_2X4:
+ _actor[1].act[2].state = 22;
+ break;
+ case INV_WRENCH:
+ _actor[1].act[2].state = 26;
+ break;
+ case INV_BOOT:
+ _actor[1].act[2].state = 93;
+ break;
+ case INV_HAND:
+ _actor[1].act[2].state = 2;
+ break;
+ case INV_DUST:
+ _actor[1].act[2].state = 89;
+ break;
+ default:
+ break;
+ }
+ _kickEnemyProgress = true;
+ }
+ } else {
+ _kickEnemyProgress = false;
+ }
+
+ // switch weapon
+ if ((buttons & 2) && (_currEnemy != EN_TORQUE)) {
+ if (_weaponEnemyJustSwitched || _actor[1].act[2].state == 35 ||
+ _actor[1].act[2].state == 34)
+ return;
+
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_WRENCH:
+ _actor[1].act[2].state = 35;
+ smlayer_setActorFacing(1, 2, 24, 180);
+ break;
+ case INV_BOOT:
+ case INV_HAND:
+ case INV_DUST:
+ _actor[1].act[2].state = 0;
+ switchEnemyWeapon();
+ default:
+ switchEnemyWeapon();
+ }
+
+ _weaponEnemyJustSwitched = true;
+ } else {
+ _weaponEnemyJustSwitched = false;
+ }
+}
+
+void Insane::switchEnemyWeapon(void) {
+ do {
+ _actor[1].weapon++;
+ if (_actor[1].weapon > 7)
+ _actor[1].weapon = INV_CHAIN;
+ } while (!_actor[1].inventory[_actor[1].weapon]);
+
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_WRENCH:
+ smlayer_setActorCostume(1, 2, readArray(_enemy[_currEnemy].costume4));
+ smlayer_setActorFacing(1, 2, 18, 180);
+ _actor[1].weaponClass = 1;
+ _actor[1].act[2].state = 34;
+ break;
+ case INV_BOOT:
+ _actor[1].weaponClass = 2;
+ _actor[1].act[2].state = 1;
+ break;
+ case INV_HAND:
+ smlayer_setActorCostume(1, 2, readArray(_enemy[_currEnemy].costume4));
+ _actor[1].weaponClass = 2;
+ _actor[1].act[2].state = 1;
+ break;
+ case INV_DUST:
+ setEnemyState();
+ break;
+ default:
+ break;
+ }
+}
+
+void Insane::setEnemyState(void) {
+ if (_actor[1].lost)
+ return;
+
+ _actor[1].act[2].animTilt = -1000;
+
+ if (_currEnemy == EN_CAVEFISH) {
+ _actor[1].weaponClass = 2;
+ if (!_roadBumps)
+ _actor[1].act[2].state = 98;
+ else
+ _actor[1].act[2].state = 99;
+
+ return;
+ }
+
+ switch (_actor[1].weapon) {
+ case INV_CHAIN:
+ _actor[1].weaponClass = 1;
+ _actor[1].act[2].state = 63;
+ break;
+ case INV_CHAINSAW:
+ _actor[1].weaponClass = 1;
+ _actor[1].act[2].state = 64;
+ break;
+ case INV_MACE:
+ _actor[1].weaponClass = 1;
+ _actor[1].act[2].state = 65;
+ break;
+ case INV_2X4:
+ _actor[1].weaponClass = 1;
+ _actor[1].act[2].state = 66;
+ break;
+ case INV_WRENCH:
+ _actor[1].weaponClass = 1;
+ _actor[1].act[2].state = 62;
+ break;
+ case INV_BOOT:
+ case INV_HAND:
+ case INV_DUST:
+ _actor[1].weaponClass = 2;
+ _actor[1].act[2].state = 1;
+ }
+}
+
+void Insane::actor12Reaction(int32 buttons) {
+ int32 tmp, tmp2;
+
+ switch(_actor[1].act[2].state) {
+ case 1:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 2;
+ _actor[1].kicking = false;
+
+ switch (_actor[1].tilt) {
+ case -3:
+ if (_actor[1].act[2].animTilt != -3) {
+ smlayer_setActorFacing(1, 2, 6, 180);
+ _actor[1].act[2].animTilt = -3;
+ }
+ break;
+ case -2:
+ if (_actor[1].field_8 == 48)
+ smlayer_setActorFacing(1, 2, 7, 180);
+ _actor[1].act[2].animTilt = -2;
+ break;
+ case -1:
+ if (_actor[1].field_8 == 46)
+ smlayer_setActorFacing(1, 2, 8, 180);
+ _actor[1].act[2].animTilt = -1;
+ break;
+ case 0:
+ if (_actor[1].act[2].animTilt) {
+ smlayer_setActorFacing(1, 2, 9, 180);
+ _actor[1].act[2].animTilt = 0;
+ }
+ break;
+ case 1:
+ if (_actor[1].field_8 == 49)
+ smlayer_setActorFacing(1, 2, 10, 180);
+ _actor[1].act[2].animTilt = 1;
+ break;
+ case 2:
+ if (_actor[1].field_8 == 51)
+ smlayer_setActorFacing(1, 2, 11, 180);
+ _actor[1].act[2].animTilt = 2;
+ break;
+ case 3:
+ if (_actor[1].act[2].animTilt != 3) {
+ smlayer_setActorFacing(1, 2, 12, 180);
+ _actor[1].act[2].animTilt = 3;
+ }
+ break;
+ default:
+ break;
+ }
+ _actor[1].act[2].tilt = 0;
+ break;
+ case 2:
+ smlayer_setActorLayer(1, 2, 4);
+ smlayer_setActorFacing(1, 2, 17, 180);
+ _actor[1].kicking = true;
+ _actor[1].weaponClass = 1;
+ _actor[1].act[2].state = 3;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ smlayer_startSfx(63);
+ break;
+ case 3:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ if (_actor[1].act[2].frame >= 6) {
+ tmp = calcBenDamage(1, 1);
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ if (tmp == 1)
+ smlayer_startSfx(50);
+ } else if (tmp == 1)
+ smlayer_startSfx(60);
+ else if (tmp == 1000)
+ smlayer_startSfx(62);
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 4;
+ }
+ _actor[1].kicking = true;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 4:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 2) {
+ smlayer_setActorFacing(1, 2, 9, 180);
+ _actor[1].act[2].state = 1;
+ _actor[1].act[2].animTilt = -1000;
+ _actor[1].weaponClass = 2;
+ _actor[1].kicking = false;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 5:
+ smlayer_setActorLayer(1, 2, 5);
+ break;
+ case 10:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ smlayer_setActorFacing(1, 2, 19, 180);
+ _actor[1].act[2].state = 11;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ smlayer_startSfx(75);
+ break;
+ case 11:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 2) {
+ if (weaponEnemyIsEffective()) {
+ smlayer_setActorFacing(1, 2, 22, 180);
+ _actor[1].act[2].state = 79;
+ } else {
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 12;
+ }
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 12:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 1) {
+ switch (_actor[0].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_DUST:
+ case INV_WRENCH:
+ case INV_BOOT:
+ tmp = calcBenDamage(1, 1);
+ if (tmp == 1)
+ smlayer_startSfx(73);
+ if (tmp == 1000)
+ smlayer_startSfx(74);
+ break;
+ case INV_HAND:
+ if (calcBenDamage(1, 0))
+ smlayer_startSfx(73);
+ break;
+ }
+ smlayer_setActorFacing(1, 2, 21, 180);
+ _actor[1].act[2].state = 13;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 13:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 3) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].state = 63;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 14:
+ smlayer_setActorLayer(1, 2, 8);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ smlayer_setActorFacing(1, 2, 19, 180);
+ _actor[1].act[2].state = 15;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ smlayer_startSfx(78);
+ break;
+ case 15:
+ smlayer_setActorLayer(1, 2, 8);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 5) {
+ switch (_actor[0].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_WRENCH:
+ if (weaponEnemyIsEffective()) {
+ smlayer_setActorFacing(1, 2, 22, 180);
+ _actor[1].act[2].state = 81;
+ } else {
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 16;
+ }
+ break;
+ default:
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 16;
+ break;
+ }
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 16:
+ smlayer_setActorLayer(1, 2, 8);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 3) {
+ switch (_actor[0].weapon) {
+ case INV_CHAIN:
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_WRENCH:
+ tmp = calcBenDamage(1, 1);
+ if (tmp == 1)
+ smlayer_startSfx(76);
+ if (tmp == 1000)
+ smlayer_startSfx(77);
+ break;
+ default:
+ calcBenDamage(1, 0);
+ break;
+ }
+ smlayer_setActorFacing(1, 2, 21,180);
+ _actor[1].act[2].state = 17;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 17:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 1) {
+ smlayer_setActorFacing(1, 2, 26, 180);
+ _actor[1].act[2].state = 64;
+ smlayer_stopSound(76);
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 18:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ smlayer_setActorFacing(1, 2, 19, 180);
+ _actor[1].act[2].state = 19;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ if (!((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))) {
+ smlayer_startSfx(69);
+ if (!_actor[1].field_54) {
+ tmp = _vm->_rnd.getRandomNumber(4);
+ if (tmp == 1)
+ smlayer_startSfx(213);
+ else if (tmp == 3)
+ smlayer_startSfx(215);
+ }
+ } else {
+ smlayer_startSfx(53);
+ }
+ break;
+ case 19:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ if (_actor[1].act[2].frame >= 3) {
+ switch (_actor[0].weapon) {
+ case INV_CHAIN:
+ if (_actor[0].kicking) {
+ _actor[0].act[2].state = 108;
+ _actor[1].act[2].state = 110;
+ } else {
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 20;
+ }
+ break;
+ case INV_CHAINSAW:
+ if (_actor[0].kicking || _actor[0].field_44)
+ _actor[1].act[2].state = 106;
+ else {
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 20;
+ }
+ break;
+ case INV_BOOT:
+ case INV_DUST:
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 20;
+ break;
+ default:
+ if (weaponEnemyIsEffective()) {
+ smlayer_setActorFacing(1, 2, 22, 180);
+ _actor[1].act[2].state = 77;
+ } else {
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 20;
+ }
+ break;
+ }
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 20:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 1) {
+ switch (_actor[1].weapon) {
+ case INV_CHAINSAW:
+ case INV_MACE:
+ case INV_2X4:
+ case INV_BOOT:
+ tmp = calcBenDamage(1, 1);
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ if (tmp == 1)
+ smlayer_startSfx(52);
+ else if (tmp == 1000)
+ smlayer_startSfx(56);
+ } else {
+ if (tmp == 1)
+ smlayer_startSfx(67);
+ else if (tmp == 1000)
+ smlayer_startSfx(68);
+ }
+ break;
+ default:
+ calcBenDamage(1, 0);
+ break;
+ }
+ smlayer_setActorFacing(1, 2, 21, 180);
+ _actor[1].act[2].state = 21;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 21:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 5) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].state = 65;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 22:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 0;
+ _actor[1].kicking = true;
+ smlayer_setActorFacing(1, 2, 19, 180);
+ _actor[1].act[2].state = 23;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ smlayer_startSfx(81);
+ break;
+ case 23:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 0;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 3) {
+ if (weaponEnemyIsEffective()) {
+ smlayer_setActorFacing(1, 2, 22, 180);
+ _actor[1].act[2].state = 83;
+ } else {
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 24;
+
+ if (!_actor[1].field_54)
+ smlayer_startSfx(246);
+ }
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 24:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 0;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 1) {
+ tmp = calcBenDamage(1, 1);
+
+ if (tmp == 1)
+ smlayer_startSfx(79);
+
+ if (tmp == 1000)
+ smlayer_startSfx(80);
+
+ smlayer_setActorFacing(1, 2, 21, 180);
+ _actor[1].act[2].state = 25;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 25:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 0;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 3) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].state = 66;
+ _actor[1].weaponClass = 1;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 26:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ smlayer_setActorFacing(1, 2, 19, 180);
+ _actor[1].act[2].state = 27;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ smlayer_startSfx(72);
+ break;
+ case 27:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 1) {
+ if (weaponEnemyIsEffective()) {
+ smlayer_setActorFacing(1, 2, 22, 180);
+ _actor[1].act[2].state = 75;
+ } else {
+ smlayer_setActorFacing(1, 2, 20, 180);
+ _actor[1].act[2].state = 28;
+ break;
+ }
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 28:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = true;
+ if (_actor[1].act[2].frame >= 3) {
+ tmp = calcBenDamage(1, 1);
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ if (tmp == 1)
+ smlayer_startSfx(57);
+ } else if (tmp == 1)
+ smlayer_startSfx(70);
+ else if (tmp == 1000)
+ smlayer_startSfx(71);
+
+ smlayer_setActorFacing(1, 2, 21, 180);
+ _actor[1].act[2].state = 29;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 29:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 6) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].state = 62;
+ _actor[1].kicking = false;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 34:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].kicking = false;
+
+ if (!smlayer_actorNeedRedraw(1, 2)) {
+ setEnemyState();
+ _actor[1].act[2].tilt = 0;
+ // for some reason there is no break at this
+ // place, so tilt gets overriden on next line
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 35:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].kicking = false;
+
+ if (!smlayer_actorNeedRedraw(1, 2)) {
+ switchEnemyWeapon();
+ _actor[1].act[2].tilt = 0;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 36:
+ _actor[1].lost = true;
+ _actor[1].field_54 = 1;
+ _actor[1].cursorX = 0;
+ _actor[1].kicking = false;
+ smlayer_setActorCostume(1, 2, readArray(_enemy[_currEnemy].costumevar));
+ smlayer_setActorFacing(1, 2, 6, 180);
+ smlayer_setActorLayer(1, 2, 25);
+ _actor[1].act[2].state = 37;
+
+ if (!((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))) {
+ smlayer_startSfx(96);
+ switch (_currEnemy) {
+ case EN_ROTT1:
+ smlayer_startVoice(212);
+ break;
+ case EN_ROTT2:
+ smlayer_startVoice(259);
+ break;
+ case EN_ROTT3:
+ smlayer_startVoice(232);
+ break;
+ case EN_VULTF1:
+ smlayer_startVoice(281);
+ break;
+ case EN_VULTF2:
+ smlayer_startVoice(276);
+ break;
+ }
+ }
+ break;
+ case 37:
+ _actor[1].cursorX = 0;
+ _actor[1].kicking = false;
+
+ if (_actor[1].act[2].frame < _enemy[_currEnemy].maxframe) {
+ if (_actor[1].x >= 50 && _actor[1].x <= 270)
+ break;
+
+ if (_actor[1].act[2].frame < _enemy[_currEnemy].maxframe / 2)
+ break;
+ }
+ if (_currSceneId == 21) {
+ queueSceneSwitch(22, 0, "rottflip.san", 64, 0, 0, 0);
+ _actor[1].act[2].state = 38;
+ } else {
+ queueSceneSwitch(11, 0, _enemy[_currEnemy].filename, 64, 0, 0, 0);
+ _actor[1].act[2].state = 38;
+ }
+ break;
+ case 38:
+ smlayer_setActorLayer(1, 2, 25);
+ _actor[1].kicking = false;
+
+ if (_actor[1].act[2].frame < _enemy[_currEnemy].maxframe + 20)
+ break;
+
+ _actor[1].act[2].frame = 0;
+
+ if (_currSceneId == 21) {
+ queueSceneSwitch(22, 0, "rottflip.san", 64, 0, 0, 0);
+ _actor[1].act[2].state = 38;
+ } else {
+ queueSceneSwitch(11, 0, _enemy[_currEnemy].filename, 64, 0, 0, 0);
+ _actor[1].act[2].state = 38;
+ }
+ break;
+ case 63:
+ smlayer_setActorLayer(1, 2, 5);
+ if (_actor[1].act[2].animTilt) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].animTilt = 0;
+ }
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 64:
+ smlayer_setActorLayer(1, 2, 5);
+ if (_actor[1].act[2].animTilt) {
+ smlayer_setActorFacing(1, 2, 26, 180);
+ _actor[1].act[2].animTilt = 0;
+ }
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 65:
+ smlayer_setActorLayer(1, 2, 5);
+ if (_actor[1].act[2].animTilt) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].animTilt = 0;
+ }
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 66:
+ smlayer_setActorLayer(1, 2, 5);
+ if (_actor[1].act[2].animTilt) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].animTilt = 0;
+ }
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 73:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = true;
+ if (_actor[1].act[2].frame >= 2 && !_kickEnemyProgress) {
+ smlayer_setActorFacing(1, 2, 19, 180);
+ _actor[1].act[2].state = 74;
+ }
+
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 74:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = false;
+ if (_actor[1].act[2].frame >= 2) {
+ smlayer_setActorFacing(1, 2, 9, 180);
+ _actor[1].act[2].state = 1;
+ _actor[1].weaponClass = 2;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 75:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = true;
+ if (_actor[1].act[2].frame >= 4 && !_kickEnemyProgress) {
+ smlayer_setActorFacing(1, 2, 23, 180);
+ _actor[1].act[2].state = 76;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 76:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = false;
+ if (_actor[1].act[2].frame >= 4) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].state = 62;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 77:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = true;
+ if (_actor[1].act[2].frame >= 1 && !_kickEnemyProgress) {
+ smlayer_setActorFacing(1, 2, 23, 180);
+ _actor[1].act[2].state = 78;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 78:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = false;
+ if (_actor[1].act[2].frame >= 5) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].state = 65;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 79:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = true;
+ if (_actor[1].act[2].frame >= 1 && !_kickEnemyProgress) {
+ smlayer_setActorFacing(1, 2, 23, 180);
+ _actor[1].act[2].state = 80;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 80:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = false;
+ if (_actor[1].act[2].frame >= 6) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].state = 63;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 81:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = true;
+ if (_actor[1].act[2].frame >= 2 && !_kickEnemyProgress) {
+ smlayer_setActorFacing(1, 2, 23, 180);
+ _actor[1].act[2].state = 82;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 82:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = false;
+ if (_actor[1].act[2].frame >= 3) {
+ smlayer_setActorFacing(1, 2, 26, 180);
+ _actor[1].act[2].state = 64;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 83:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 0;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = true;
+ if (_actor[1].act[2].frame >= 2 && !_kickEnemyProgress) {
+ smlayer_setActorFacing(1, 2, 23, 180);
+ _actor[1].act[2].state = 84;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 84:
+ smlayer_setActorLayer(1, 2, 6);
+ _actor[1].weaponClass = 0;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = false;
+ if (_actor[1].act[2].frame >= 5) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].state = 66;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 89:
+ smlayer_setActorLayer(1, 2, 26);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ if (_roadBumps)
+ smlayer_setActorFacing(1, 2, 13, 180);
+ else
+ smlayer_setActorFacing(1, 2, 12, 180);
+
+ smlayer_startSfx(100);
+ _actor[1].act[2].state = 90;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 90:
+ smlayer_setActorLayer(1, 2, 26);
+ _actor[1].weaponClass = 2;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 5)
+ if (_actor[1].x - _actor[0].x <= 125)
+ _actor[0].damage += 90;
+
+ if (_actor[1].act[2].frame >= 12) {
+ _actor[1].kicking = false;
+ setEnemyState();
+ smlayer_setActorLayer(1, 2, 5);
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 91:
+ smlayer_setActorLayer(1, 2, 26);
+ _actor[1].kicking = false;
+ break;
+ case 92:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].kicking = false;
+ break;
+ case 93:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ smlayer_setActorFacing(1, 2, 18, 180);
+ _actor[1].act[2].state = 94;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 94:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 15) {
+ smlayer_setActorCostume(1, 2, readArray(44));
+ smlayer_setActorFacing(1, 2, 6, 180);
+ _actor[1].act[2].state = 95;
+ _actor[1].act[0].room = 0;
+ _actor[1].act[1].room = 0;
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 95:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 19) {
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0,
+ _continueFrame1, 1300);
+ _actor[1].act[2].state = 96;
+ }
+ break;
+ case 97:
+ smlayer_setActorLayer(1, 2, 25);
+ _actor[1].lost = true;
+ if (_actor[1].act[2].frame >= 18) {
+ writeArray(7, 1);
+ _enemy[EN_VULTM2].isEmpty = 1;
+ queueSceneSwitch(12, 0, "getnitro.san", 0, 0, 0, 0);
+ }
+ break;
+ case 98:
+ smlayer_setActorLayer(1, 2, 5);
+ if (_actor[1].act[2].animTilt) {
+ smlayer_setActorFacing(1, 2, 6, 180);
+ _actor[1].act[2].animTilt = 0;
+ }
+ if (_roadBumps) {
+ smlayer_setActorFacing(1, 2, 7, 180);
+ _actor[1].act[2].state = 100;
+ }
+ _actor[1].kicking = false;
+ break;
+ case 99:
+ smlayer_setActorLayer(1, 2, 5);
+ if (_actor[1].act[2].animTilt) {
+ smlayer_setActorFacing(1, 2, 9, 180);
+ _actor[1].act[2].animTilt = 0;
+ }
+ if (!_roadBumps) {
+ smlayer_setActorFacing(1, 2, 8, 180);
+ _actor[1].act[2].state = 101;
+ }
+ _actor[1].kicking = false;
+ break;
+ case 100:
+ smlayer_setActorLayer(1, 2, 5);
+ if (_actor[1].act[2].frame >= 4) {
+ smlayer_setActorFacing(1, 2, 9, 180);
+ _actor[1].act[2].state = 99;
+ }
+ _actor[1].kicking = false;
+ break;
+ case 101:
+ smlayer_setActorLayer(1, 2, 5);
+ if (_actor[1].act[2].frame >= 4) {
+ smlayer_setActorFacing(1, 2, 6, 180);
+ _actor[1].act[2].state = 98;
+ }
+ _actor[1].kicking = false;
+ break;
+ case 102:
+ _actor[1].lost = true;
+ _actor[1].cursorX = 0;
+ _actor[1].kicking = false;
+ smlayer_setActorCostume(1, 2, readArray(40));
+ smlayer_setActorFacing(1, 2, 6, 180);
+ smlayer_setActorLayer(1, 2, 25);
+ _actor[1].act[2].state = 103;
+ case 103:
+ _actor[1].kicking = false;
+
+ if (_actor[1].act[2].frame >= 18 || ((_actor[1].x < 50 || _actor[1].x > 270) &&
+ _actor[1].act[2].frame >= 9)) {
+ _enemy[EN_CAVEFISH].isEmpty = 1;
+ queueSceneSwitch(20, 0, "wr2_cvko.san", 64, 0, 0, 0);
+ _actor[1].act[2].state = 38;
+ }
+ break;
+ case 106:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ smlayer_setActorFacing(1, 2, 29, 180);
+ _actor[1].act[2].state = 107;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 107:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 8)
+ _actor[1].damage = _actor[1].maxdamage + 10;
+
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 108:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ smlayer_setActorFacing(1, 2, 28, 180);
+ _actor[1].act[2].state = 109;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 109:
+ smlayer_setActorLayer(1, 2, 5);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 6)
+ _actor[1].damage = _actor[1].maxdamage + 10;
+
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 110:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ smlayer_setActorFacing(1, 2, 30, 180);
+ _actor[1].act[2].state = 111;
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 111:
+ smlayer_setActorLayer(1, 2, 4);
+ _actor[1].weaponClass = 1;
+ _actor[1].kicking = false;
+ if (_actor[1].act[2].frame >= 7) {
+ smlayer_setActorFacing(1, 2, 25, 180);
+ _actor[1].act[2].state = 65;
+ smlayer_setActorLayer(1, 2, 5);
+ }
+ _actor[1].act[2].tilt = calcTilt(_actor[1].tilt);
+ break;
+ case 113:
+ _actor[1].lost = true;
+ _actor[1].kicking = false;
+ smlayer_setActorCostume(1, 2, readArray(46));
+ smlayer_setActorFacing(1, 2, 6, 180);
+ smlayer_setActorLayer(1, 2, 25);
+ _actor[1].act[1].room = 0;
+ _actor[1].act[0].room = 0;
+ _actor[1].cursorX = 0;
+ _actor[1].act[2].state = 114;
+ _enemy[EN_VULTF2].isEmpty = 1;
+ smlayer_startVoice(275);
+ break;
+ case 114:
+ smlayer_setActorLayer(1, 2, 25);
+ _actor[1].kicking = false;
+
+ if (_actor[1].act[2].frame >= 16 || ((_actor[1].x < 50 || _actor[1].x > 270)
+ && (_actor[1].act[2].frame >= 8))) {
+ queueSceneSwitch(11, 0, _enemy[_currEnemy].filename, 64, 0, 0, 0);
+ _actor[1].act[2].state = 38;
+ }
+ break;
+ case 115:
+ _actor[1].lost = true;
+ _actor[1].kicking = false;
+ smlayer_setActorCostume(1, 2, readArray(47));
+ smlayer_setActorFacing(1, 2, 6, 180);
+ smlayer_setActorLayer(1, 2, 25);
+ _actor[1].act[1].room = 0;
+ _actor[1].act[0].room = 0;
+ _actor[1].cursorX = 0;
+ _actor[1].act[2].state = 116;
+ smlayer_startVoice(232);
+ break;
+ case 116:
+ smlayer_setActorLayer(1, 2, 25);
+ _actor[1].kicking = false;
+
+ if (_actor[1].act[2].frame >= 17 || ((_actor[1].x < 50 || _actor[1].x > 270)
+ && _actor[1].act[2].frame >= 8)) {
+ queueSceneSwitch(11, 0, _enemy[_currEnemy].filename, 64, 0, 0, 0);
+ _actor[1].act[2].state = 38;
+ }
+ break;
+ default:
+ break;
+ }
+ tmp = _actor[1].x + _actor[1].act[2].tilt - 17;
+ tmp2 = _actor[1].y + _actor[1].y1 - 98;
+
+ if (_actor[1].act[2].room)
+ smlayer_putActor(1, 2, tmp, tmp2, _smlayer_room2);
+ else
+ smlayer_putActor(1, 2, tmp, tmp2, _smlayer_room);
+}
+
+int32 Insane::calcEnemyDamage(bool arg_0, bool arg_4) {
+ if ((_actor[1].x - _actor[0].x > weaponMaxRange(0)) ||
+ (_actor[1].x - _actor[0].x < weaponMinRange(0)))
+ return 0;
+
+ if (_actor[1].field_44 && arg_4)
+ return 1000;
+
+ if (!actor1StateFlags(_actor[1].act[2].state))
+ return 0;
+
+ if (arg_0) {
+ ouchSoundEnemy();
+ _actor[1].damage += weaponDamage(0);
+ }
+
+ return 1;
+}
+
+bool Insane::weaponEnemyIsEffective(void) {
+ if ((_actor[1].x - _actor[0].x > weaponMaxRange(1)) ||
+ (_actor[1].x - _actor[0].x < weaponMinRange(1)) ||
+ !_actor[0].kicking)
+ return false;
+
+ return true;
+}
+
+void Insane::actor13Reaction(int32 buttons) {
+ int32 tmp;
+
+ switch (_actor[1].act[3].state) {
+ case 1:
+ case 54:
+ _actor[1].field_54 = 0;
+ break;
+ case 52:
+ if (_actor[1].runningSound)
+ smlayer_stopSound(_actor[1].runningSound);
+
+ if (_currScenePropIdx)
+ shutCurrentScene();
+
+ _actor[1].runningSound = 0;
+ _actor[1].defunct = 0;
+ _actor[1].field_54 = 0;
+ smlayer_setActorFacing(1, 3, 15, 180);
+ _actor[1].act[3].state = 53;
+ break;
+ case 53:
+ _actor[1].field_54 = 0;
+ if (_actor[1].act[3].frame >= 2) {
+ smlayer_setActorFacing(1, 3, 16, 180);
+ _actor[1].act[3].state = 54;
+ }
+ break;
+ case 69:
+ if (_actor[1].act[3].frame >= 2)
+ _actor[1].act[3].state = 70;
+ break;
+ case 70:
+ if (_actor[1].scenePropSubIdx) {
+ smlayer_setActorFacing(1, 3, 4, 180);
+ tmp = _currScenePropIdx + _actor[1].scenePropSubIdx;
+ if (!smlayer_startVoice(_sceneProp[tmp].sound))
+ _actor[1].runningSound = 0;
+ else
+ _actor[1].runningSound = _sceneProp[tmp].sound;
+ _actor[1].act[3].state = 72;
+ } else {
+ _actor[1].act[3].state = 118;
+ }
+ break;
+ case 71:
+ _actor[1].field_54 = 0;
+ if (_actor[1].act[3].frame >= 2)
+ _actor[1].act[3].state = 1;
+ break;
+ case 72:
+ if (_actor[1].runningSound) {
+ if (!smlayer_isSoundRunning(_actor[1].runningSound)) {
+ smlayer_setActorFacing(1, 3, 5, 180);
+ _actor[1].act[3].state = 70;
+ _actor[1].scenePropSubIdx = 0;
+ }
+ } else {
+ tmp = _currScenePropIdx + _actor[1].scenePropSubIdx;
+ if (_sceneProp[tmp].counter >= _sceneProp[tmp].maxCounter) {
+ smlayer_setActorFacing(1, 3, 5, 180);
+ _actor[1].act[3].state = 70;
+ _actor[1].scenePropSubIdx = 0;
+ _actor[1].runningSound = 0;
+ }
+ }
+ break;
+ case 117:
+ smlayer_setActorFacing(1, 3, 13, 180);
+ _actor[1].field_54 = 1;
+ _actor[1].act[3].state = 69;
+ break;
+ case 118:
+ smlayer_setActorFacing(1, 3, 14, 180);
+ _actor[1].act[3].state = 71;
+ break;
+ default:
+ break;
+ }
+}
+
+
+// FIXME: this is exact actor00Reaction. Combine
+void Insane::actor10Reaction(int32 buttons) {
+ int32 tmpx, tmpy;
+
+ switch (_actor[1].tilt) {
+ case -3:
+ if (_actor[1].act[0].state != 41) {
+ smlayer_setActorFacing(1, 0, 6, 180);
+ _actor[1].act[0].state = 41;
+ }
+ break;
+ case -2:
+ if (_actor[1].act[0].state != 40) {
+ smlayer_setActorFacing(1, 0, 7, 180);
+ _actor[1].act[0].state = 40;
+ }
+ break;
+ case -1:
+ if (_actor[1].act[0].state != 39) {
+ smlayer_setActorFacing(1, 0, 8, 180);
+ _actor[1].act[0].state = 39;
+ }
+ break;
+ case 0:
+ if (_actor[1].act[0].state != 1) {
+ smlayer_setActorFacing(1, 0, 9, 180);
+ _actor[1].act[0].state = 1;
+ }
+ break;
+ case 1:
+ if (_actor[1].act[0].state != 55) {
+ smlayer_setActorFacing(1, 0, 10, 180);
+ _actor[1].act[0].state = 55;
+ }
+ break;
+ case 2:
+ if (_actor[1].act[0].state != 56) {
+ smlayer_setActorFacing(1, 0, 11, 180);
+ _actor[1].act[0].state = 56;
+ }
+ break;
+ case 3:
+ if (_actor[1].act[0].state != 57) {
+ smlayer_setActorFacing(1, 0, 12, 180);
+ _actor[1].act[0].state = 57;
+ }
+ break;
+ default:
+ break;
+ }
+ tmpx = _actor[1].x + _actor[1].x1;
+ tmpy = _actor[1].y + _actor[1].y1;
+
+ if (_actor[1].act[0].room)
+ smlayer_putActor(1, 0, tmpx, tmpy, _smlayer_room2);
+ else
+ smlayer_putActor(1, 0, tmpx, tmpy, _smlayer_room);
+}
+
+int32 Insane::actionEnemy(void) {
+ int32 buttons;
+
+ if (_actor[1].enemyHandler != -1)
+ buttons = enemyHandler(_actor[1].enemyHandler, 1, 0, _actor[1].probability);
+ else
+ buttons = enemyHandler(EN_TORQUE, 1, 0, _actor[1].probability);
+
+ if (_actor[1].tilt) {
+ _actor[1].speed += _actor[1].cursorX / 40;
+ } else {
+ if (_actor[1].speed < 0)
+ _actor[1].speed++;
+ else
+ _actor[1].speed--;
+ }
+
+ if (_actor[1].speed > 8)
+ _actor[1].speed = 8;
+
+ if (_actor[1].speed < -8)
+ _actor[1].speed = -8;
+
+ _actor[1].x += _actor[0].speed;
+
+ if (_actor[1].x > 250)
+ _actor[1].x--;
+ else if (_actor[1].x < 250)
+ _actor[1].x++;
+
+ if (_actor[1].x > 320) {
+ _actor[1].x = 320;
+ _actor[1].damage++;
+ _actor[1].x1 = -_actor[1].x1;
+ _actor[1].damage++;
+
+ return buttons;
+ }
+
+ if (!_actor[1].lost) {
+ if (_actor[0].x + 90 > _actor[1].x)
+ _actor[1].x = _actor[0].x + 90;
+ }
+
+ if (_actor[1].x < 0) {
+ _actor[1].x = 0;
+ _actor[1].x1 = -_actor[1].x1;
+ _actor[1].damage++;
+ } else if (_actor[1].x > 310) {
+ _actor[1].x1 = -_actor[1].x1;
+ _actor[1].damage++;
+ }
+
+ return buttons;
+}
+
+}
+
diff --git a/engines/scumm/insane/insane_iact.cpp b/engines/scumm/insane/insane_iact.cpp
new file mode 100644
index 0000000000..c706c7d191
--- /dev/null
+++ b/engines/scumm/insane/insane_iact.cpp
@@ -0,0 +1,558 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "base/engine.h"
+
+#include "scumm/scumm.h"
+
+#include "scumm/smush/smush_player.h"
+#include "scumm/smush/chunk_type.h"
+#include "scumm/smush/chunk.h"
+
+#include "scumm/insane/insane.h"
+
+namespace Scumm {
+
+void Insane::procIACT(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags) {
+ if (_keyboardDisable)
+ return;
+
+ switch (_currSceneId) {
+ case 1:
+ iactScene1(renderBitmap, codecparam, setupsan12, setupsan13, b, size, flags);
+ break;
+ case 3:
+ case 13:
+ iactScene3(renderBitmap, codecparam, setupsan12, setupsan13, b, size, flags);
+ break;
+ case 4:
+ case 5:
+ iactScene4(renderBitmap, codecparam, setupsan12, setupsan13, b, size, flags);
+ break;
+ case 6:
+ iactScene6(renderBitmap, codecparam, setupsan12, setupsan13, b, size, flags);
+ break;
+ case 17:
+ iactScene17(renderBitmap, codecparam, setupsan12, setupsan13, b, size, flags);
+ break;
+ case 21:
+ iactScene21(renderBitmap, codecparam, setupsan12, setupsan13, b, size, flags);
+ break;
+ }
+}
+
+void Insane::iactScene1(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags) {
+ _player->checkBlock(b, TYPE_IACT, 8);
+
+ int16 par1, par2, par3, par4, par5, par6, par7, par9, par11, par13, tmp;
+
+ par1 = b.getWord(); // cx
+ par2 = b.getWord(); // dx
+ par3 = b.getWord(); // si
+ par4 = b.getWord(); // bx
+
+ switch (par1) {
+ case 2: // PATCH
+ if (par3 != 1)
+ break;
+
+ par5 = b.getWord(); // si
+ if (_actor[0].field_8 == 112) {
+ setBit(par5);
+ break;
+ }
+
+ if (_approachAnim == -1) {
+ chooseEnemy(); //PATCH
+ _approachAnim = _enemy[_currEnemy].apprAnim;
+ }
+
+ if (_approachAnim == par4)
+ clearBit(par5);
+ else
+ setBit(par5);
+ break;
+ case 3:
+ if (par3 == 1) {
+ setBit(b.getWord());
+ _approachAnim = -1;
+ }
+ break;
+ case 4:
+ if (par3 == 1 && (_approachAnim < 0 || _approachAnim > 4))
+ setBit(b.getWord());
+ break;
+ case 5:
+ if (par2 != 13)
+ break;
+
+ tmp = b.getWord(); // +8
+ tmp = b.getWord(); // +10
+ par7 = b.getWord(); // +12 dx
+ tmp = b.getWord(); // +14
+ par9 = b.getWord(); // +16 bx
+ tmp = b.getWord(); // +18
+ par11 = b.getWord(); // +20 cx
+ tmp = b.getWord(); // +22
+ par13 = b.getWord(); // +24 ax
+
+ if (par13 > _actor[0].x || par11 < _actor[0].x) {
+ _tiresRustle = true;
+ _actor[0].x1 = -_actor[0].x1;
+ _actor[0].damage++; // PATCH
+ }
+
+ if (par9 < _actor[0].x || par7 > _actor[0].x) {
+ _tiresRustle = true;
+ _actor[0].damage += 4; // PATCH
+ }
+ break;
+ case 6:
+ switch (par2) {
+ case 38:
+ smlayer_drawSomething(renderBitmap, codecparam, 50-19, 20-13, 3,
+ _smush_iconsNut, 7, 0, 0);
+ _roadBranch = true;
+ _iactSceneId = par4;
+ break;
+ case 25:
+ _roadBumps = true;
+ _actor[0].y1 = -_actor[0].y1;
+ break;
+ case 11:
+ if (_approachAnim >= 1 && _approachAnim <= 4 && !_needSceneSwitch)
+ queueSceneSwitch(13, _smush_minefiteFlu, "minefite.san", 64, 0,
+ _continueFrame1, 1300);
+ break;
+ case 9:
+ par5 = b.getWord(); // si
+ par6 = b.getWord(); // bx
+ smlayer_setFluPalette(_smush_roadrsh3Rip, 0);
+ if (par5 == par6 - 1)
+ smlayer_setFluPalette(_smush_roadrashRip, 0);
+ }
+ break;
+ case 7:
+ switch (par4) {
+ case 1:
+ _actor[0].x -= (b.getWord() - 160) / 10;
+ break;
+ case 2:
+ par5 = b.getWord();
+
+ if (par5 - 8 > _actor[0].x || par5 + 8 < _actor[0].x) {
+ if (smlayer_isSoundRunning(86))
+ smlayer_stopSound(86);
+ } else {
+ if (!smlayer_isSoundRunning(86))
+ smlayer_startSfx(86);
+ }
+ break;
+ }
+ break;
+ }
+
+ if (_approachAnim < 0 || _approachAnim > 4)
+ if (readArray(8)) {
+ smlayer_drawSomething(renderBitmap, codecparam, 270-19, 20-18, 3,
+ _smush_iconsNut, 20, 0, 0);
+ _benHasGoggles = true;
+ }
+}
+
+void Insane::chooseEnemy(void) {
+ if (readArray(58) != 0)
+ _enemy[EN_TORQUE].isEmpty = 1;
+
+ if (_enemy[EN_TORQUE].occurences == 0) {
+ _currEnemy = EN_TORQUE;
+ _metEnemiesListTail++;
+ _metEnemiesList[_metEnemiesListTail] = EN_TORQUE;
+ return;
+ }
+
+ removeEmptyEnemies();
+
+ int32 count, i, j, en, en2;
+ bool notfound;
+
+ en = 0;
+ for (i = 0; i < 9; i++)
+ if (_enemy[i].isEmpty == 0)
+ ++en;
+
+ en -= 4;
+ assert(en >= 0);
+
+ count = 0;
+ while (1) {
+ count++;
+ if (count < 14) {
+ en2 = _vm->_rnd.getRandomNumber(10);
+ if (en2 == 9)
+ en2 = 6;
+ else if (en2 > 9)
+ en2 = 7;
+
+ notfound = true;
+
+ if (_enemy[en2].isEmpty != 0)
+ continue;
+
+ if (0 < _metEnemiesListTail) {
+ i = 0;
+ do {
+ if (en2 == _metEnemiesList[i + 1])
+ notfound = false;
+ i++;
+ } while (i < _metEnemiesListTail && notfound);
+ }
+ if (!notfound) {
+ continue;
+ }
+ } else {
+ j = 0;
+ do {
+ notfound = true;
+ en2 = j;
+ if (0 < _metEnemiesListTail) {
+ i = 0;
+ do {
+ if (en2 == _metEnemiesList[i + 1])
+ notfound = false;
+ i++;
+ } while (i < _metEnemiesListTail && notfound);
+ }
+ j++;
+ } while (j < 9 && !notfound);
+ if (!notfound) {
+ _metEnemiesListTail = 0;
+ count = 0;
+ continue;
+ }
+ }
+
+ ++_metEnemiesListTail;
+ assert(_metEnemiesListTail < ARRAYSIZE(_metEnemiesList));
+ _metEnemiesList[_metEnemiesListTail] = en2;
+
+ if (_metEnemiesListTail >= en) {
+ removeEnemyFromMetList(0);
+ }
+
+ if (notfound)
+ break;
+ }
+
+ _currEnemy = en2;
+}
+
+void Insane::removeEmptyEnemies(void) {
+ if (_metEnemiesListTail > 0) {
+ for (int i = 0; i < _metEnemiesListTail; i++)
+ if (_enemy[i].isEmpty == 1)
+ removeEnemyFromMetList(i);
+ }
+}
+
+void Insane::removeEnemyFromMetList(int32 enemy1) {
+ if (enemy1 >= _metEnemiesListTail)
+ return;
+
+ int en = enemy1;
+ do {
+ ++en;
+ assert(en + 1 < ARRAYSIZE(_metEnemiesList));
+ _metEnemiesList[en] = _metEnemiesList[en + 1];
+ } while (en < _metEnemiesListTail);
+ _metEnemiesListTail--;
+}
+
+void Insane::iactScene3(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags) {
+ _player->checkBlock(b, TYPE_IACT, 8);
+
+ int command, par1, par2, par3, tmp;
+ command = b.getWord();
+ par1 = b.getWord();
+ if (command == 6) {
+ if (par1 == 9) {
+ tmp = b.getWord(); // ptr + 4
+ tmp = b.getWord(); // ptr + 6
+ par2 = b.getWord(); // ptr + 8
+ par3 = b.getWord(); // ptr + 10
+
+ if (!par2)
+ smlayer_setFluPalette(_smush_roadrsh3Rip, 0);
+ else {
+ if (par2 == par3 - 1)
+ smlayer_setFluPalette(_smush_roadrashRip, 0);
+ }
+ } else if (par1 == 25) {
+ _roadBumps = true;
+ _actor[0].y1 = -_actor[0].y1;
+ _actor[1].y1 = -_actor[1].y1;
+ }
+ }
+}
+
+void Insane::iactScene4(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags) {
+ _player->checkBlock(b, TYPE_IACT, 8);
+
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ return;
+
+ int16 par1, par2, par3, par4, par5;
+
+ par1 = b.getWord(); // edx
+ par2 = b.getWord(); // bx
+ par3 = b.getWord();
+ par4 = b.getWord(); // cx
+
+ switch (par1) {
+ case 2:
+ case 4:
+ par5 = b.getWord(); // si
+ switch (par3) {
+ case 1:
+ if (par4 == 1) {
+ if (readArray(6))
+ setBit(par5);
+ else
+ clearBit(par5);
+ } else {
+ if (readArray(6))
+ clearBit(par5);
+ else
+ setBit(par5);
+ }
+ break;
+ case 2:
+ if (readArray(5))
+ clearBit(par5);
+ else
+ setBit(par5);
+ break;
+ }
+ break;
+ case 6:
+ switch (par2) {
+ case 38:
+
+ smlayer_drawSomething(renderBitmap, codecparam, 270-19, 20-13, 3,
+ _smush_icons2Nut, 10, 0, 0);
+ _roadBranch = true;
+ _iactSceneId = par4;
+ break;
+ case 7:
+ if (readArray(4) != 0)
+ return;
+
+ smlayer_drawSomething(renderBitmap, codecparam, 160-13, 20-10, 3, // QW
+ _smush_icons2Nut, 8, 0, 0);
+ _roadStop = true;
+ break;
+ case 8:
+ if (readArray(4) == 0 || readArray(6) == 0)
+ return;
+
+ writeArray(1, _posBrokenTruck);
+ writeArray(3, _val57d);
+ smush_setToFinish();
+
+ break;
+ case 25:
+ if (readArray(5) == 0)
+ return;
+
+ _carIsBroken = true;
+ smlayer_drawSomething(renderBitmap, codecparam, 160-13, 20-10, 3, // QW
+ _smush_icons2Nut, 8, 0, 0);
+ break;
+ case 11:
+ smlayer_drawSomething(renderBitmap, codecparam, 50-19, 20-13, 3,
+ _smush_icons2Nut, 9, 0, 0);
+ _roadBranch = true;
+ _iactSceneId = par4;
+ break;
+ }
+ break;
+ }
+}
+
+void Insane::iactScene6(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags) {
+ _player->checkBlock(b, TYPE_IACT, 8);
+
+ int16 par1, par2, par3, par4, par5;
+
+ par1 = b.getWord();
+ par2 = b.getWord(); // bx
+ par3 = b.getWord();
+ par4 = b.getWord();
+
+ switch (par1) {
+ case 7:
+ par5 = b.getWord();
+ if (par4 != 3)
+ break;
+
+ if (par5 >= _actor[0].x)
+ break;
+
+ _actor[0].x = par5;
+ break;
+ case 2:
+ case 4:
+ par5 = b.getWord();
+ switch (par3) {
+ case 1:
+ if (par4 == 1) {
+ if (readArray(6))
+ setBit(par5);
+ else
+ clearBit(par5);
+ } else {
+ if (readArray(6))
+ clearBit(par5);
+ else
+ setBit(par5);
+ }
+ break;
+ case 2:
+ if (readArray(5))
+ clearBit(par5);
+ else
+ setBit(par5);
+ break;
+ }
+ break;
+ case 6:
+ switch (par2) {
+ case 38:
+ smlayer_drawSomething(renderBitmap, codecparam, 270-19, 20-13, 3,
+ _smush_icons2Nut, 10, 0, 0);
+ _roadBranch = true;
+ _iactSceneId = par4;
+ break;
+ case 7:
+ if (readArray(4) != 0)
+ return;
+
+ _roadStop = true;
+ smlayer_drawSomething(renderBitmap, codecparam, 160-13, 20-10, 3, //QW
+ _smush_icons2Nut, 8, 0, 0);
+ break;
+ case 8:
+ if (readArray(4) == 0 || readArray(6) == 0)
+ return;
+
+ writeArray(1, _posBrokenTruck);
+ writeArray(3, _posVista);
+ smush_setToFinish();
+
+ break;
+ case 25:
+ if (readArray(5) == 0)
+ return;
+
+ _carIsBroken = true;
+ smlayer_drawSomething(renderBitmap, codecparam, 160-13, 20-10, 3, //QW
+ _smush_icons2Nut, 8, 0, 0);
+ break;
+ case 11:
+ smlayer_drawSomething(renderBitmap, codecparam, 50-19, 20-13, 3,
+ _smush_icons2Nut, 9, 0, 0);
+ _roadBranch = true;
+ _iactSceneId = par4;
+ break;
+ }
+ break;
+ }
+}
+
+void Insane::iactScene17(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags) {
+ _player->checkBlock(b, TYPE_IACT, 8);
+ int16 par1, par2, par3, par4;
+
+ par1 = b.getWord(); // dx
+ par2 = b.getWord(); // cx
+ par3 = b.getWord(); // di
+ par4 = b.getWord();
+
+ switch (par1) {
+ case 2:
+ case 3:
+ case 4:
+ if (par3 == 1) {
+ setBit(b.getWord());
+ _approachAnim = -1;
+ }
+ break;
+ case 6:
+ switch (par2) {
+ case 38:
+ smlayer_drawSomething(renderBitmap, codecparam, 28, 48, 1,
+ _smush_iconsNut, 6, 0, 0);
+ _roadBranch = true;
+ _iactSceneId = par4;
+ if (_counter1 <= 4) {
+ if (_counter1 == 4)
+ smlayer_startSfx(94);
+
+ smlayer_showStatusMsg(-1, renderBitmap, codecparam, 24, 167, 1,
+ 2, 0, "%s", handleTrsTag(5000));
+ }
+ _objectDetected = true;
+ break;
+ case 10:
+ smlayer_drawSomething(renderBitmap, codecparam, 28, 48, 1,
+ _smush_iconsNut, 6, 0, 0);
+ if (_counter1 <= 4) {
+ if (_counter1 == 4)
+ smlayer_startSfx(94);
+
+ smlayer_showStatusMsg(-1, renderBitmap, codecparam, 24, 167, 1,
+ 2, 0, "%s", handleTrsTag(5001));
+ }
+ _objectDetected = true;
+ _mineCaveIsNear = true;
+ break;
+ }
+ break;
+ }
+}
+
+void Insane::iactScene21(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, Chunk &b, int32 size, int32 flags) {
+ // void implementation
+}
+
+}
+
diff --git a/engines/scumm/insane/insane_scenes.cpp b/engines/scumm/insane/insane_scenes.cpp
new file mode 100644
index 0000000000..7cc01fc2cf
--- /dev/null
+++ b/engines/scumm/insane/insane_scenes.cpp
@@ -0,0 +1,1499 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "base/engine.h"
+
+#include "common/config-manager.h"
+
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+
+#include "scumm/insane/insane.h"
+
+namespace Scumm {
+
+void Insane::runScene(int arraynum) {
+ _insaneIsRunning = true;
+ _player = new SmushPlayer(_vm, _speed);
+ _player->insanity(true);
+
+ _numberArray = arraynum;
+
+ // zeroValues1()
+ _objArray2Idx = 0;
+ _objArray2Idx2 = 0;
+ // zeroValues2()
+ _objArray1Idx = 0;
+ _objArray1Idx2 = 0;
+ // zeroValues3()
+ _currScenePropIdx = 0;
+ _currScenePropSubIdx = 0;
+ _currTrsMsg = 0;
+
+ smush_warpMouse(160, 100, -1);
+ putActors();
+ readState();
+
+ debugC(DEBUG_INSANE, "INSANE Arg: %d", readArray(0));
+
+ switch (readArray(0)) {
+ case 1:
+ initScene(1);
+ setupValues();
+ smlayer_setActorCostume(0, 2, readArray(10));
+ smlayer_putActor(0, 2, _actor[0].x, _actor[0].y1 + 190, _smlayer_room2);
+ startVideo("minedriv.san", 1, 32, 12, 0);
+ break;
+ case 2:
+ setupValues();
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ smlayer_setActorCostume(0, 2, readArray(10));
+ else
+ smlayer_setActorCostume(0, 2, readArray(11));
+ smlayer_putActor(0, 2, _actor[0].x, _actor[0].y1 + 190, _smlayer_room2);
+
+ _mainRoadPos = readArray(2);
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ initScene(5);
+ startVideo("tovista.san", 1, 32, 12, 0);
+ } else if (_mainRoadPos == _posBrokenTruck) {
+ initScene(5);
+ startVideo("tovista2.san", 1, 32, 12, 0);
+ } else if (_mainRoadPos == _posBrokenCar) {
+ initScene(5);
+ startVideo("tovista2.san", 1, 32, 12, 0, _smush_tovista2Flu, 60);
+ } else {
+ initScene(4);
+ startVideo("tovista1.san", 1, 32, 12, 0);
+ }
+ break;
+ case 3:
+ setupValues();
+ smlayer_setActorCostume(0, 2, readArray(11));
+ smlayer_putActor(0, 2, _actor[0].x, _actor[0].y1 + 190, _smlayer_room2);
+ _mainRoadPos = readArray(2);
+ if (_mainRoadPos == _posBrokenTruck) {
+ initScene(6);
+ startVideo("toranch.san", 1, 32, 12, 0, _smush_toranchFlu, 300);
+ } else if (_mainRoadPos == _posBrokenCar) {
+ initScene(6);
+ startVideo("toranch.san", 1, 32, 12, 0, _smush_toranchFlu, 240);
+ } else {
+ initScene(6);
+ startVideo("toranch.san", 1, 32, 12, 0);
+ }
+ break;
+ case 4:
+ _firstBattle = true;
+ _currEnemy = EN_ROTT1;
+ initScene(13);
+ startVideo("minefite.san", 1, 32, 12, 0);
+ break;
+ case 5:
+ writeArray(1, _val54d);
+ initScene(24);
+ startVideo("rottopen.san", 1, 32, 12, 0);
+ break;
+ case 6:
+ initScene(1);
+ setupValues();
+ smlayer_setFluPalette(_smush_roadrashRip, 1);
+ smlayer_setActorCostume(0, 2, readArray(10));
+ smlayer_putActor(0, 2, _actor[0].x, _actor[0].y1 + 190, _smlayer_room2);
+ startVideo("minedriv.san", 1, 32, 12, 0, _smush_minedrivFlu, 420);
+ break;
+ case 7:
+ case 8:
+ case 9:
+ break;
+ case 10:
+ initScene(26);
+ writeArray(1, _val54d);
+ startVideo("credits.san", 1, 32, 12, 0);
+ break;
+ default:
+ error("Unknown FT_INSANE mode %d", readArray(0));
+ }
+
+ putActors();
+ _enemy[EN_ROTT3].maxdamage = 120;
+
+ _insaneIsRunning = false;
+
+ if (!((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))) {
+ writeArray(50, _actor[0].inventory[INV_CHAIN]);
+ writeArray(51, _actor[0].inventory[INV_CHAINSAW]);
+ writeArray(52, _actor[0].inventory[INV_MACE]);
+ writeArray(53, _actor[0].inventory[INV_2X4]);
+ writeArray(54, _actor[0].inventory[INV_WRENCH]);
+ writeArray(55, _actor[0].inventory[INV_DUST]);
+ writeArray(56, _enemy[EN_CAVEFISH].isEmpty);
+ writeArray(337, _enemy[EN_TORQUE].occurences);
+ writeArray(329, _enemy[EN_ROTT1].occurences);
+ writeArray(330, _enemy[EN_ROTT2].occurences);
+ writeArray(331, _enemy[EN_ROTT3].occurences);
+ writeArray(332, _enemy[EN_VULTF1].occurences);
+ writeArray(333, _enemy[EN_VULTM1].occurences);
+ writeArray(334, _enemy[EN_VULTF2].occurences);
+ writeArray(335, _enemy[EN_VULTM2].occurences);
+ writeArray(336, _enemy[EN_CAVEFISH].occurences);
+ writeArray(339, _enemy[EN_VULTF2].isEmpty);
+ writeArray(340, _enemy[EN_VULTM2].isEmpty);
+ }
+ // insane_unlock(); // FIXME
+ _vm->_sound->stopAllSounds(); // IMUSE_StopAllSounds();
+
+ delete _player;
+}
+
+int Insane::initScene(int sceneId) {
+ debugC(DEBUG_INSANE, "initScene(%d)", sceneId);
+
+ if (_needSceneSwitch)
+ return 1;
+
+ stopSceneSounds(_currSceneId); // do it for previous scene
+ loadSceneData(sceneId, 0, 1);
+ if (loadSceneData(sceneId, 0, 2)) {
+ setSceneCostumes(sceneId);
+ _sceneData2Loaded = 0;
+ _sceneData1Loaded = 0;
+ } else
+ _sceneData2Loaded = 1;
+
+ _currSceneId = sceneId;
+
+ return 1;
+}
+
+void Insane::stopSceneSounds(int sceneId) {
+ int flag = 0;
+
+ debugC(DEBUG_INSANE, "stopSceneSounds(%d)", sceneId);
+
+ switch (sceneId) {
+ case 1:
+ smlayer_stopSound(88);
+ smlayer_stopSound(86);
+ smlayer_stopSound(87);
+ flag = 1;
+ break;
+ case 18:
+ case 19:
+ smlayer_stopSound(88);
+ flag = 1;
+ break;
+ case 17:
+ smlayer_stopSound(88);
+ smlayer_stopSound(94);
+ flag = 1;
+ break;
+ case 2:
+ case 7:
+ case 8:
+ flag = 1;
+ break;
+ case 3:
+ case 21:
+ flag = 1;
+ // break is omittted intentionally
+ case 13:
+ if (_actor[0].runningSound != 0)
+ smlayer_stopSound(_actor[0].runningSound);
+ _actor[0].runningSound = 0;
+
+ if (_actor[1].runningSound != 0)
+ smlayer_stopSound(_actor[1].runningSound);
+ _actor[1].runningSound = 0;
+
+ if (_currScenePropIdx != 0)
+ shutCurrentScene();
+
+ _currScenePropSubIdx = 0;
+ _currTrsMsg = 0;
+ _actor[0].defunct = 0;
+ _actor[0].scenePropSubIdx = 0;
+ _actor[0].field_54 = 0;
+ _actor[1].defunct = 0;
+ _actor[1].scenePropSubIdx = 0;
+ _actor[1].field_54 = 0;
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ smlayer_stopSound(59);
+ smlayer_stopSound(63);
+ } else {
+ smlayer_stopSound(89);
+ smlayer_stopSound(90);
+ smlayer_stopSound(91);
+ smlayer_stopSound(92);
+ smlayer_stopSound(93);
+ smlayer_stopSound(95);
+ smlayer_stopSound(87);
+ }
+ break;
+ case 4:
+ case 5:
+ case 6:
+ smlayer_stopSound(88);
+ smlayer_stopSound(86);
+ flag = 1;
+ break;
+ case 24:
+ smlayer_stopSound(90);
+ break;
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ case 16:
+ case 20:
+ case 22:
+ case 23:
+ break;
+ }
+ if (!flag)
+ return;
+
+ smlayer_setActorCostume(0, 2, 0);
+ smlayer_setActorCostume(0, 0, 0);
+ smlayer_setActorCostume(0, 1, 0);
+ smlayer_setActorCostume(1, 2, 0);
+ smlayer_setActorCostume(1, 0, 0);
+ smlayer_setActorCostume(1, 1, 0);
+
+ return;
+}
+
+void Insane::shutCurrentScene(void) {
+ debugC(DEBUG_INSANE, "shutCurrentScene()");
+
+ _currScenePropIdx = 0;
+ _currTrsMsg = 0;
+ _currScenePropSubIdx = 0;
+ _actor[1].scenePropSubIdx = 0;
+ _actor[1].defunct = 0;
+
+ if (_actor[1].runningSound != 0) {
+ smlayer_stopSound(_actor[1].runningSound);
+ _actor[1].runningSound = 0;
+ }
+
+ _actor[0].scenePropSubIdx = 0;
+ _actor[0].defunct = 0;
+
+ if (_actor[0].runningSound != 0) {
+ smlayer_stopSound(_actor[0].runningSound);
+ _actor[0].runningSound = 0;
+ }
+
+ _battleScene = true;
+}
+
+
+// insane_loadSceneData1 & insane_loadSceneData2
+int Insane::loadSceneData(int scene, int flag, int phase) {
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ return 1;
+
+ int retvalue = 1;
+
+ debugC(DEBUG_INSANE, "Insane::loadSceneData(%d, %d, %d)", scene, flag, phase);
+ //if (phase == 1) /// FIXME
+ // insane_unlock();
+ switch (scene) {
+ case 1:
+ smlayer_loadSound(88, flag, phase);
+ smlayer_loadSound(86, flag, phase);
+ smlayer_loadSound(87, flag, phase);
+ smlayer_loadCostume(10, phase);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ smlayer_loadSound(88, flag, phase);
+ smlayer_loadCostume(11, phase);
+ break;
+ case 3:
+ case 13:
+ switch(_currEnemy) {
+ case EN_TORQUE:
+ smlayer_loadSound(59, flag, phase);
+ smlayer_loadSound(93, flag, phase);
+ smlayer_loadCostume(57, phase);
+ smlayer_loadCostume(37, phase);
+ break;
+ case EN_ROTT1:
+ smlayer_loadSound(201, flag, phase);
+ smlayer_loadSound(194, flag, phase);
+ smlayer_loadSound(195, flag, phase);
+ smlayer_loadSound(199, flag, phase);
+ smlayer_loadSound(205, flag, phase);
+ smlayer_loadSound(212, flag, phase);
+ smlayer_loadSound(198, flag, phase);
+ smlayer_loadSound(203, flag, phase);
+ smlayer_loadSound(213, flag, phase);
+ smlayer_loadSound(215, flag, phase);
+ smlayer_loadSound(216, flag, phase);
+ smlayer_loadSound(217, flag, phase);
+ smlayer_loadSound(218, flag, phase);
+ smlayer_loadSound(90, flag, phase);
+ smlayer_loadCostume(26, phase);
+ smlayer_loadCostume(16, phase);
+ smlayer_loadCostume(17, phase);
+ smlayer_loadCostume(27, phase);
+ break;
+ case EN_ROTT2:
+ smlayer_loadSound(242, flag, phase);
+ smlayer_loadSound(244, flag, phase);
+ smlayer_loadSound(236, flag, phase);
+ smlayer_loadSound(238, flag, phase);
+ smlayer_loadSound(239, flag, phase);
+ smlayer_loadSound(240, flag, phase);
+ smlayer_loadSound(258, flag, phase);
+ smlayer_loadSound(259, flag, phase);
+ smlayer_loadSound(260, flag, phase);
+ smlayer_loadSound(243, flag, phase);
+ smlayer_loadSound(244, flag, phase);
+ smlayer_loadSound(245, flag, phase);
+ smlayer_loadSound(246, flag, phase);
+ smlayer_loadSound(233, flag, phase);
+ smlayer_loadSound(234, flag, phase);
+ smlayer_loadSound(241, flag, phase);
+ smlayer_loadSound(242, flag, phase);
+ smlayer_loadSound(90, flag, phase);
+ smlayer_loadCostume(28, phase);
+ smlayer_loadCostume(16, phase);
+ smlayer_loadCostume(17, phase);
+ smlayer_loadCostume(42, phase);
+ break;
+ case EN_ROTT3:
+ smlayer_loadSound(223, flag, phase);
+ smlayer_loadSound(224, flag, phase);
+ smlayer_loadSound(225, flag, phase);
+ smlayer_loadSound(226, flag, phase);
+ smlayer_loadSound(228, flag, phase);
+ smlayer_loadSound(229, flag, phase);
+ smlayer_loadSound(230, flag, phase);
+ smlayer_loadSound(232, flag, phase);
+ smlayer_loadSound(220, flag, phase);
+ smlayer_loadSound(221, flag, phase);
+ smlayer_loadSound(222, flag, phase);
+ smlayer_loadSound(90, flag, phase);
+ smlayer_loadCostume(15, phase);
+ smlayer_loadCostume(16, phase);
+ smlayer_loadCostume(17, phase);
+ smlayer_loadCostume(43, phase);
+ smlayer_loadCostume(47, phase);
+ break;
+ case EN_VULTF1:
+ smlayer_loadSound(282, flag, phase);
+ smlayer_loadSound(283, flag, phase);
+ smlayer_loadSound(284, flag, phase);
+ smlayer_loadSound(285, flag, phase);
+ smlayer_loadSound(286, flag, phase);
+ smlayer_loadSound(287, flag, phase);
+ smlayer_loadSound(279, flag, phase);
+ smlayer_loadSound(280, flag, phase);
+ smlayer_loadSound(281, flag, phase);
+ smlayer_loadSound(277, flag, phase);
+ smlayer_loadSound(288, flag, phase);
+ smlayer_loadSound(278, flag, phase);
+ smlayer_loadSound(91, flag, phase);
+ smlayer_loadCostume(29, phase);
+ smlayer_loadCostume(33, phase);
+ smlayer_loadCostume(32, phase);
+ smlayer_loadCostume(37, phase);
+ break;
+ case EN_VULTM1:
+ smlayer_loadSound(160, flag, phase);
+ smlayer_loadSound(161, flag, phase);
+ smlayer_loadSound(174, flag, phase);
+ smlayer_loadSound(167, flag, phase);
+ smlayer_loadSound(163, flag, phase);
+ smlayer_loadSound(164, flag, phase);
+ smlayer_loadSound(170, flag, phase);
+ smlayer_loadSound(166, flag, phase);
+ smlayer_loadSound(175, flag, phase);
+ smlayer_loadSound(162, flag, phase);
+ smlayer_loadSound(91, flag, phase);
+ smlayer_loadCostume(30, phase);
+ smlayer_loadCostume(33, phase);
+ smlayer_loadCostume(32, phase);
+ smlayer_loadCostume(36, phase);
+ break;
+ case EN_VULTF2:
+ smlayer_loadSound(263, flag, phase);
+ smlayer_loadSound(264, flag, phase);
+ smlayer_loadSound(265, flag, phase);
+ smlayer_loadSound(266, flag, phase);
+ smlayer_loadSound(267, flag, phase);
+ smlayer_loadSound(268, flag, phase);
+ smlayer_loadSound(270, flag, phase);
+ smlayer_loadSound(271, flag, phase);
+ smlayer_loadSound(275, flag, phase);
+ smlayer_loadSound(276, flag, phase);
+ smlayer_loadSound(261, flag, phase);
+ smlayer_loadSound(262, flag, phase);
+ smlayer_loadSound(263, flag, phase);
+ smlayer_loadSound(274, flag, phase);
+ smlayer_loadSound(91, flag, phase);
+ smlayer_loadCostume(31, phase);
+ smlayer_loadCostume(33, phase);
+ smlayer_loadCostume(32, phase);
+ smlayer_loadCostume(35, phase);
+ smlayer_loadCostume(46, phase);
+ break;
+ case EN_VULTM2:
+ smlayer_loadSound(179, flag, phase);
+ smlayer_loadSound(183, flag, phase);
+ smlayer_loadSound(184, flag, phase);
+ smlayer_loadSound(186, flag, phase);
+ smlayer_loadSound(191, flag, phase);
+ smlayer_loadSound(192, flag, phase);
+ smlayer_loadSound(180, flag, phase);
+ smlayer_loadSound(101, flag, phase);
+ smlayer_loadSound(289, flag, phase);
+ smlayer_loadSound(177, flag, phase);
+ smlayer_loadSound(178, flag, phase);
+ smlayer_loadSound(290, flag, phase);
+ smlayer_loadSound(102, flag, phase);
+ smlayer_loadSound(91, flag, phase);
+ smlayer_loadCostume(34, phase);
+ smlayer_loadCostume(33, phase);
+ smlayer_loadCostume(32, phase);
+ smlayer_loadCostume(44, phase);
+ smlayer_loadCostume(45, phase);
+ break;
+ case EN_CAVEFISH:
+ smlayer_loadSound(291, flag, phase);
+ smlayer_loadSound(100, flag, phase);
+ smlayer_loadSound(92, flag, phase);
+ smlayer_loadCostume(39, phase);
+ smlayer_loadCostume(40, phase);
+ smlayer_loadCostume(41, phase);
+ break;
+ default:
+ retvalue = 0;
+ break;
+ }
+ smlayer_loadSound(64, flag, phase);
+ smlayer_loadSound(65, flag, phase);
+ smlayer_loadSound(66, flag, phase);
+ smlayer_loadSound(67, flag, phase);
+ smlayer_loadSound(68, flag, phase);
+ smlayer_loadSound(69, flag, phase);
+ smlayer_loadSound(70, flag, phase);
+ smlayer_loadSound(71, flag, phase);
+ smlayer_loadSound(72, flag, phase);
+ smlayer_loadSound(73, flag, phase);
+ smlayer_loadSound(74, flag, phase);
+ smlayer_loadSound(75, flag, phase);
+ smlayer_loadSound(76, flag, phase);
+ smlayer_loadSound(77, flag, phase);
+ smlayer_loadSound(78, flag, phase);
+ smlayer_loadSound(79, flag, phase);
+ smlayer_loadSound(80, flag, phase);
+ smlayer_loadSound(81, flag, phase);
+ smlayer_loadSound(82, flag, phase);
+ smlayer_loadSound(83, flag, phase);
+ smlayer_loadSound(84, flag, phase);
+ smlayer_loadSound(85, flag, phase);
+ smlayer_loadSound(86, flag, phase);
+ smlayer_loadSound(87, flag, phase);
+ smlayer_loadSound(62, flag, phase);
+ smlayer_loadSound(63, flag, phase);
+ smlayer_loadSound(60, flag, phase);
+ smlayer_loadSound(61, flag, phase);
+ smlayer_loadSound(315, flag, phase);
+ smlayer_loadSound(316, flag, phase);
+ smlayer_loadSound(317, flag, phase);
+ smlayer_loadSound(98, flag, phase);
+ smlayer_loadSound(318, flag, phase);
+ smlayer_loadSound(96, flag, phase);
+ smlayer_loadSound(97, flag, phase);
+ smlayer_loadSound(95, flag, phase);
+ smlayer_loadSound(89, flag, phase);
+ smlayer_loadCostume(12, phase);
+ smlayer_loadCostume(13, phase);
+ smlayer_loadCostume(14, phase);
+ smlayer_loadCostume(18, phase);
+ smlayer_loadCostume(22, phase);
+ smlayer_loadCostume(19, phase);
+ smlayer_loadCostume(38, phase);
+ smlayer_loadCostume(20, phase);
+ smlayer_loadCostume(21, phase);
+ smlayer_loadCostume(23, phase);
+ smlayer_loadCostume(24, phase);
+ smlayer_loadCostume(25, phase);
+ break;
+ case 21:
+ case 24:
+ case 25:
+ smlayer_loadSound(223, flag, phase);
+ smlayer_loadSound(224, flag, phase);
+ smlayer_loadSound(225, flag, phase);
+ smlayer_loadSound(226, flag, phase);
+ smlayer_loadSound(228, flag, phase);
+ smlayer_loadSound(229, flag, phase);
+ smlayer_loadSound(230, flag, phase);
+ smlayer_loadSound(232, flag, phase);
+ smlayer_loadSound(90, flag, phase);
+ smlayer_loadCostume(15, phase);
+ smlayer_loadCostume(16, phase);
+ smlayer_loadCostume(17, phase);
+ smlayer_loadCostume(43, phase);
+ smlayer_loadSound(62, flag, phase);
+ smlayer_loadSound(63, flag, phase);
+ smlayer_loadSound(60, flag, phase);
+ smlayer_loadSound(61, flag, phase);
+ smlayer_loadSound(315, flag, phase);
+ smlayer_loadSound(316, flag, phase);
+ smlayer_loadSound(317, flag, phase);
+ smlayer_loadSound(98, flag, phase);
+ smlayer_loadSound(318, flag, phase);
+ smlayer_loadSound(96, flag, phase);
+ smlayer_loadSound(97, flag, phase);
+ smlayer_loadSound(95, flag, phase);
+ smlayer_loadSound(89, flag, phase);
+ smlayer_loadCostume(12, phase);
+ smlayer_loadCostume(13, phase);
+ smlayer_loadCostume(14, phase);
+ smlayer_loadCostume(18, phase);
+ smlayer_loadCostume(22, phase);
+ break;
+ case 17:
+ smlayer_loadSound(88, flag, phase);
+ smlayer_loadSound(94, flag, phase);
+ break;
+ case 2:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ case 16:
+ case 18:
+ case 19:
+ case 20:
+ case 22:
+ case 23:
+ break;
+ default:
+ retvalue = 0;
+ }
+ if (phase == 1) {
+ _sceneData1Loaded = 1;
+ }
+ return retvalue;
+}
+
+void Insane::setSceneCostumes(int sceneId) {
+ debugC(DEBUG_INSANE, "Insane::setSceneCostumes(%d)", sceneId);
+
+ switch (sceneId) {
+ case 1:
+ smlayer_setActorCostume(0, 2, readArray(10));
+ smlayer_putActor(0, 2, _actor[0].x, _actor[0].y1 + 190, _smlayer_room2);
+ smlayer_setFluPalette(_smush_roadrashRip, 0);
+ setupValues();
+ return;
+ break;
+ case 17:
+ smlayer_setFluPalette(_smush_goglpaltRip, 0);
+ setupValues();
+ return;
+ break;
+ case 2:
+ smlayer_setActorCostume(0, 2, readArray(10));
+ setupValues();
+ return;
+ break;
+ case 13:
+ setEnemyCostumes();
+ smlayer_setFluPalette(_smush_roadrashRip, 0);
+ return;
+ break;
+ case 21:
+ _currEnemy = EN_ROTT3; //PATCH
+ setEnemyCostumes();
+ _actor[1].y = 200;
+ smlayer_setFluPalette(_smush_roadrashRip, 0);
+ return;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ smlayer_setActorCostume(0, 2, readArray(10));
+ else
+ smlayer_setActorCostume(0, 2, readArray(11));
+ smlayer_putActor(0, 2, _actor[0].x, _actor[0].y1+190, _smlayer_room2);
+ setupValues();
+ return;
+ break;
+ case 7:
+ case 8:
+ writeArray(4, 0);
+ return;
+ }
+}
+
+void Insane::setEnemyCostumes(void) {
+ int i;
+
+ debugC(DEBUG_INSANE, "setEnemyCostumes(%d)", _currEnemy);
+
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)) {
+ smlayer_setActorCostume(0, 2, readArray(11));
+ smlayer_setActorCostume(0, 0, readArray(13));
+ smlayer_setActorCostume(0, 1, readArray(12));
+ } else {
+ smlayer_setActorCostume(0, 2, readArray(12));
+ smlayer_setActorCostume(0, 0, readArray(14));
+ smlayer_setActorCostume(0, 1, readArray(13));
+ }
+ smlayer_setActorLayer(0, 1, 1);
+ smlayer_setActorLayer(0, 2, 5);
+ smlayer_setActorLayer(0, 0, 10);
+ smlayer_putActor(0, 2, _actor[0].x+11, _actor[0].y1+102, _smlayer_room2);
+ smlayer_putActor(0, 1, _actor[0].x, _actor[0].y1+200, _smlayer_room2);
+ smlayer_putActor(0, 0, _actor[0].x, _actor[0].y1+200, _smlayer_room2);
+
+ if (_currEnemy == EN_CAVEFISH) {
+ smlayer_setActorCostume(1, 2, readArray(_enemy[_currEnemy].costume4));
+ _actor[1].act[2].room = 1;
+ _actor[1].act[1].room = 0;
+ _actor[1].act[0].room = 0;
+ _actor[1].act[2].animTilt = 1;
+ _actor[1].field_8 = 98;
+ _actor[1].act[2].state = 98;
+ _actor[1].act[0].state = 98;
+ _actor[1].act[1].state = 98;
+
+ smlayer_putActor(1, 2, _actor[1].x + _actor[1].act[2].tilt - 17,
+ _actor[1].y + _actor[1].y1 - 98, _smlayer_room2);
+ } else if (_currEnemy == EN_TORQUE) {
+ smlayer_setActorCostume(1, 2, readArray(_enemy[_currEnemy].costume4));
+ _actor[1].act[2].room = 1;
+ _actor[1].act[1].room = 0;
+ _actor[1].act[0].room = 0;
+ _actor[1].field_8 = 1;
+ _actor[1].act[2].state = 1;
+ _actor[1].act[0].state = 1;
+ _actor[1].act[1].state = 1;
+ smlayer_putActor(1, 2, _actor[1].x + _actor[1].act[2].tilt - 17,
+ _actor[1].y + _actor[1].y1 - 98, _smlayer_room2);
+ } else {
+ _actor[1].act[2].room = 1;
+ _actor[1].act[1].room = 1;
+ _actor[1].act[0].room = 1;
+
+ if (_enemy[_currEnemy].costume4)
+ smlayer_setActorCostume(1, 2, readArray(_enemy[_currEnemy].costume4));
+
+ if (_enemy[_currEnemy].costume5)
+ smlayer_setActorCostume(1, 0, readArray(_enemy[_currEnemy].costume5));
+
+ if (_enemy[_currEnemy].costume6)
+ smlayer_setActorCostume(1, 1, readArray(_enemy[_currEnemy].costume6));
+
+ _actor[1].field_8 = 1;
+ _actor[1].act[2].state = 1;
+ _actor[1].act[0].state = 1;
+ _actor[1].act[1].state = 1;
+
+ if (_actor[1].act[2].room != 0)
+ smlayer_putActor(1, 2, _actor[1].x + _actor[1].act[2].tilt - 17,
+ _actor[1].y + _actor[1].y1 - 98,
+ _smlayer_room2);
+ }
+
+ if (_actor[1].act[1].room != 0)
+ smlayer_putActor(1, 1, _actor[1].x, _actor[1].y + _actor[1].y1,
+ _smlayer_room2);
+
+ if (_actor[1].act[0].room != 0)
+ smlayer_putActor(1, 0, _actor[1].x, _actor[1].y + _actor[1].y1,
+ _smlayer_room2);
+
+ smlayer_setActorLayer(1, 1, 1);
+ smlayer_setActorLayer(1, 2, 5);
+ smlayer_setActorLayer(1, 0, 10);
+
+ _actor[1].damage = 0;
+ _actor[1].x = 250;
+ _actor[1].y = 300;
+ _actor[1].cursorX = 0;
+ _actor[1].tilt = 0;
+ _actor[1].weapon = -1;
+ _actor[1].weaponClass = 2;
+ _enemy[_currEnemy].occurences++;
+ _actor[1].maxdamage = _enemy[_currEnemy].maxdamage;
+ _actor[1].enemyHandler = _enemy[_currEnemy].handler;
+ _actor[1].animWeaponClass = 0;
+ for (i = 0; i < 8; i++)
+ _actor[1].inventory[i] = 0;
+ _actor[0].damage = 0;
+ _actor[0].x = 100;
+ _actor[0].y = 200;
+ _actor[0].weapon = INV_HAND;
+ _actor[0].weaponClass = 2;
+ _actor[0].animWeaponClass = 0;
+ _actor[0].newFacingFlag = 2;
+ _actor[0].curFacingFlag = 0;
+ _actor[0].tilt = 0;
+ _actor[0].field_8 = 1;
+ _actor[0].act[2].state = 1;
+ _actor[0].act[2].animTilt = 1;
+ _actor[0].act[0].state = 0;
+ _actor[0].act[1].state = 1;
+ _actor[0].act[2].room = 1;
+ _actor[0].act[1].room = 1;
+ _actor[0].act[0].room = 1;
+ _actor[0].cursorX = 0;
+ _actor[0].defunct = 0;
+ _actor[0].scenePropSubIdx = 0;
+ _actor[0].field_54 = 0;
+ _actor[0].runningSound = 0;
+ _actor[0].lost = false;
+ _actor[0].kicking = false;
+ _actor[0].field_44 = false;
+ _actor[1].inventory[_enemy[_currEnemy].weapon] = 1;
+ _actor[0].field_48 = false;
+ _actor[1].defunct = 0;
+ _actor[1].scenePropSubIdx = 0;
+ _actor[1].field_54 = 0;
+ _actor[1].runningSound = 0;
+ _actor[1].lost = false;
+ _actor[1].kicking = false;
+ _actor[1].field_44 = false;
+ _actor[1].field_48 = false;
+ if (_enemy[_currEnemy].initializer != -1)
+ enemyInitializer(_enemy[_currEnemy].initializer, _actor[1].damage,
+ _actor[0].damage, _actor[1].probability);
+
+ smush_warpMouse(160, 100, -1);
+}
+
+void Insane::procPreRendering(void) {
+ _smush_isSanFileSetup = 0; // FIXME: This shouldn't be here
+
+ switchSceneIfNeeded();
+
+ if (_sceneData1Loaded) {
+ _val115_ = true;
+ if (!_keyboardDisable) {
+ smush_changeState(1);
+ _keyboardDisable = 1;
+ }
+ } else {
+ _val115_ = false;
+ if (_keyboardDisable) {
+ smush_changeState(0);
+ _keyboardDisable = 0;
+ }
+ }
+
+ _lastKey = getLastKey(1);
+}
+
+void Insane::procPostRendering(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ int32 tmpSnd;
+ bool needMore = false;
+
+ if (!_keyboardDisable) {
+ switch (_currSceneId) {
+ case 12:
+ postCase11(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ break;
+ case 1:
+ postCase0(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ if (!smlayer_isSoundRunning(88))
+ smlayer_startSfx(88);
+ smlayer_soundSetPan(88, ((_actor[0].x+160)>>2)+64);
+ if (_tiresRustle) {
+ if (!smlayer_isSoundRunning(87))
+ smlayer_startSfx(87);
+ } else {
+ smlayer_stopSound(87);
+ }
+ break;
+ case 18:
+ case 19:
+ postCase17(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ smlayer_stopSound(95);
+ smlayer_stopSound(87);
+ smlayer_stopSound(88);
+ if (!smlayer_isSoundRunning(88))
+ smlayer_startSfx(88);
+ break;
+ case 17:
+ postCase16(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ if (!smlayer_isSoundRunning(88))
+ smlayer_startSfx(88);
+ break;
+ case 2:
+ postCase1(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ break;
+ case 3:
+ postCase2(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ needMore = true;
+ if (!smlayer_isSoundRunning(89)) {
+ smlayer_startSfx(89);
+ smlayer_soundSetPriority(89, 100);
+ }
+ tmpSnd = _enemy[_currEnemy].sound;
+ if (!smlayer_isSoundRunning(tmpSnd)) {
+ smlayer_startSfx(tmpSnd);
+ smlayer_soundSetPriority(tmpSnd, 100);
+ }
+ smlayer_soundSetPan(89, ((_actor[0].x+160)>>2)+64);
+ smlayer_soundSetPan(tmpSnd, ((_actor[1].x+160)>>2)+64);
+ if (!_tiresRustle) {
+ smlayer_stopSound(87);
+ } else {
+ if (!smlayer_isSoundRunning(87))
+ smlayer_startSfx(87);
+ }
+ break;
+ case 21:
+ postCase20(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ needMore = true;
+ if (!smlayer_isSoundRunning(89)) {
+ smlayer_startSfx(89);
+ smlayer_soundSetPriority(89, 100);
+ }
+ tmpSnd = _enemy[_currEnemy].sound;
+ if (!smlayer_isSoundRunning(tmpSnd)) {
+ smlayer_startSfx(tmpSnd);
+ smlayer_soundSetPriority(tmpSnd, 100);
+ }
+ smlayer_soundSetPan(89, ((_actor[0].x+160)>>2)+64);
+ smlayer_soundSetPan(tmpSnd, ((_actor[1].x+160)>>2)+64);
+ break;
+ case 4:
+ case 5:
+ postCase3(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ if (!smlayer_isSoundRunning(88))
+ smlayer_startSfx(88);
+ smlayer_soundSetPan(88, ((_actor[0].x+160)>>2)+64);
+ break;
+ case 6:
+ postCase5(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ if (!smlayer_isSoundRunning(88))
+ smlayer_startSfx(88);
+ smlayer_soundSetPan(88, ((_actor[0].x+160)>>2)+64);
+ break;
+ case 7:
+ case 8:
+ postCase6(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ break;
+ case 9:
+ case 23:
+ postCase8(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ break;
+ case 10:
+ postCase9(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ break;
+ case 11:
+ case 20:
+ case 22:
+ postCase10(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ break;
+ case 14:
+ postCase23(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ break;
+ case 13:
+ postCase12(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ needMore = true;
+ if (!smlayer_isSoundRunning(89)) {
+ smlayer_startSfx(89);
+ smlayer_soundSetPriority(89, 100);
+ }
+ tmpSnd = _enemy[_currEnemy].sound;
+ if (!smlayer_isSoundRunning(tmpSnd)) {
+ smlayer_startSfx(tmpSnd);
+ smlayer_soundSetPriority(tmpSnd, 100);
+ }
+ smlayer_soundSetPan(89, ((_actor[0].x+160)>>2)+64);
+ smlayer_soundSetPan(tmpSnd, ((_actor[1].x+160)>>2)+64);
+ break;
+ case 24:
+ if (!smlayer_isSoundRunning(90)) {
+ smlayer_startSfx(90);
+ smlayer_soundSetPriority(90, 100);
+ }
+ postCase23(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ break;
+ case 15:
+ case 16:
+ postCase14(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+ break;
+ case 25:
+ case 26:
+ break;
+ }
+
+ if (_currScenePropIdx)
+ postCaseAll(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+
+ _actor[0].frame++;
+ _actor[0].act[3].frame++;
+ _actor[0].act[2].frame++;
+ _actor[0].act[1].frame++;
+ _actor[0].act[0].frame++;
+ _actor[1].act[3].frame++;
+ _actor[1].frame++;
+ _actor[1].act[2].frame++;
+ _actor[1].act[1].frame++;
+ _actor[1].act[0].frame++;
+ }
+
+ if (!_val115_) {
+ smlayer_overrideDrawActorAt(&renderBitmap[0], renderBitmap[2], renderBitmap[3]);
+ _isBenCut = 0;
+ }
+
+ if (_isBenCut)
+ smlayer_drawSomething(renderBitmap, codecparam, 89, 56, 1, _smush_bencutNut, 0, 0, 0);
+
+ if (!_keyboardDisable)
+ _vm->processActors();
+
+ if (needMore)
+ postCaseMore(renderBitmap, codecparam, setupsan12, setupsan13, curFrame, maxFrame);
+
+ _lastKey = 0;
+ _tiresRustle = false;
+}
+
+void Insane::postCase11(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if (curFrame >= maxFrame && !_needSceneSwitch) {
+ if (_firstBattle) {
+ smush_setToFinish();
+ } else {
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0,
+ _continueFrame, 1300);
+ }
+ }
+ _roadBranch = false;
+ _roadStop = false;
+}
+
+void Insane::postCase0(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ turnBen(true);
+
+ if (!curFrame || curFrame == 420)
+ smlayer_setFluPalette(_smush_roadrashRip, 0);
+
+ if (curFrame >= maxFrame)
+ smush_rewindCurrentSan(1088, -1, -1);
+
+ _roadBumps = false;
+ _roadBranch = false;
+ _roadStop = false;
+ _benHasGoggles = false;
+ _mineCaveIsNear = false;
+ _continueFrame1 = curFrame;
+}
+
+void Insane::postCase17(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if (curFrame >= maxFrame && !_needSceneSwitch) {
+ if (_currSceneId == 18) {
+ queueSceneSwitch(17, _smush_minedrivFlu, "minedriv.san", 64, 0,
+ _continueFrame1, 1300);
+ writeArray(9, 1);
+ } else {
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0,
+ _continueFrame1, 1300);
+ writeArray(9, 0);
+ }
+ }
+ _roadBranch = false;
+ _roadStop = false;
+}
+
+void Insane::postCase16(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ char buf[12];
+ int32 tmp;
+
+ turnBen(true);
+ sprintf(buf, "^f01%02o", curFrame & 0x3f);
+ smlayer_showStatusMsg(-1, renderBitmap, codecparam, 180, 168, 1, 2, 0, "%s", buf);
+ tmp = 400-curFrame;
+
+ if (tmp < 0)
+ tmp += 1300;
+
+ sprintf(buf, "^f01%04d", tmp);
+ smlayer_showStatusMsg(-1, renderBitmap, codecparam, 202, 168, 1, 2, 0, "%s", buf);
+
+ sprintf(buf, "^f01%02o", curFrame & 0xff);
+ smlayer_showStatusMsg(-1, renderBitmap, codecparam, 240, 168, 1, 2, 0, "%s", buf);
+ smlayer_showStatusMsg(-1, renderBitmap, codecparam, 170, 43, 1, 2, 0, "%s", buf);
+
+ smlayer_drawSomething(renderBitmap, codecparam, 0, 0, 1, _smush_bensgoggNut, 0, 0, 0);
+
+ if (!_objectDetected)
+ smlayer_drawSomething(renderBitmap, codecparam, 24, 170, 1,
+ _smush_iconsNut, 23, 0, 0);
+
+ if (!curFrame)
+ smlayer_setFluPalette(_smush_goglpaltRip, 0);
+
+ if (curFrame >= maxFrame) {
+ smush_rewindCurrentSan(1088, -1, -1);
+ smlayer_setFluPalette(_smush_goglpaltRip, 0);
+ }
+ _roadBumps = false;
+ _mineCaveIsNear = false;
+ _roadBranch = false;
+ _roadStop = false;
+ _objectDetected = false;
+ _counter1++;
+ _continueFrame1 = curFrame;
+ if (_counter1 >= 10)
+ _counter1 = 0;
+}
+
+void Insane::postCase1(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ struct fluConf *flu;
+
+ if ((curFrame >= maxFrame) && !_needSceneSwitch) {
+ flu = &_fluConf[14 + _iactSceneId2];
+ queueSceneSwitch(flu->sceneId, *flu->fluPtr, flu->filenamePtr, 64, 0,
+ flu->startFrame, flu->numFrames);
+ }
+ _roadBranch = false;
+ _roadStop = false;
+}
+
+void Insane::postCase2(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ turnBen(_battleScene != 0);
+ turnEnemy(true);
+
+ if (!curFrame)
+ smlayer_setFluPalette(_smush_roadrashRip, 0);
+
+ if (curFrame >= maxFrame)
+ smush_rewindCurrentSan(1088, -1, -1);
+
+ _roadBumps = false;
+ _roadBranch = false;
+ _roadStop = false;
+ _continueFrame = curFrame;
+}
+
+void Insane::postCase20(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ turnBen(true);
+ turnEnemy(true);
+
+ if (curFrame >= maxFrame)
+ smush_rewindCurrentSan(1088, -1, -1);
+
+ _roadBumps = false;
+ _roadBranch = false;
+ _roadStop = false;
+ _continueFrame = curFrame;
+}
+
+void Insane::postCase3(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if ((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))
+ turnBen(false);
+ else
+ turnBen(true);
+
+ if (_actor[0].x >= 158 && _actor[0].x <= 168) {
+ if (!smlayer_isSoundRunning(86))
+ smlayer_startSfx(86);
+ } else {
+ if (smlayer_isSoundRunning(86))
+ smlayer_stopSound(86);
+ }
+
+ if (curFrame >= maxFrame) {
+ if (_currSceneId == 4) {
+ if (!_needSceneSwitch) {
+ if (readArray(6)) {
+ if (readArray(4))
+ queueSceneSwitch(14, 0, "hitdust2.san", 64, 0, 0, 0);
+ else
+ queueSceneSwitch(14, 0, "hitdust4.san", 64, 0, 0, 0);
+ } else {
+ if (readArray(4))
+ queueSceneSwitch(14, 0, "hitdust1.san", 64, 0, 0, 0);
+ else
+ queueSceneSwitch(14, 0, "hitdust3.san", 64, 0, 0, 0);
+ }
+ }
+ } else {
+ if (readArray(4)) {
+ if (!_needSceneSwitch)
+ queueSceneSwitch(15, 0, "vistthru.san", 64, 0, 0, 0);
+ } else {
+ writeArray(1, _posVista);
+ smush_setToFinish();
+ }
+ }
+ }
+
+ _carIsBroken = false;
+ _roadStop = false;
+ _roadBranch = false;
+ _iactSceneId = 0;
+}
+
+void Insane::postCase5(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ turnBen(true);
+
+ if (_actor[0].x >= 158 && _actor[0].x <= 168) {
+ if (!smlayer_isSoundRunning(86))
+ smlayer_startSfx(86);
+ } else {
+ if (smlayer_isSoundRunning(86))
+ smlayer_stopSound(86);
+ }
+
+ if (curFrame >= maxFrame) {
+ if (readArray(4)) {
+ if (!_needSceneSwitch)
+ queueSceneSwitch(15, 0, "chasthru.san", 64, 0, 0, 0);
+ } else {
+ if (readArray(5)) {
+ writeArray(1, _val57d);
+ smush_setToFinish();
+ } else {
+ writeArray(4, 1);
+ queueSceneSwitch(15, 0, "chasout.san", 64, 0, 0, 0);
+ }
+ }
+ }
+
+ _carIsBroken = false;
+ _roadStop = false;
+ _roadBranch = false;
+ _iactSceneId = 0;
+}
+
+void Insane::postCase6(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ struct fluConf *flu;
+
+ if ((curFrame >= maxFrame) && !_needSceneSwitch) {
+ if (_currSceneId == 8)
+ flu = &_fluConf[7 + _iactSceneId2];
+ else
+ flu = &_fluConf[0 + _iactSceneId2];
+
+ queueSceneSwitch(flu->sceneId, *flu->fluPtr, flu->filenamePtr, 64, 0,
+ flu->startFrame, flu->numFrames);
+ }
+ _roadBranch = false;
+ _roadStop = false;
+}
+
+void Insane::postCase8(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if (curFrame >= maxFrame && !_needSceneSwitch) {
+ _actor[0].damage = 0;
+
+ if (_firstBattle) {
+ queueSceneSwitch(13, _smush_minefiteFlu, "minefite.san", 64, 0,
+ _continueFrame, 1300);
+ } else {
+ if (_currSceneId == 23) {
+ queueSceneSwitch(21, 0, "rottfite.san", 64, 0, 0, 0);
+ } else {
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0,
+ _continueFrame, 1300);
+ }
+ }
+ }
+
+ _roadBranch = false;
+ _roadStop = false;
+}
+
+void Insane::postCase9(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if (curFrame >= maxFrame && !_needSceneSwitch) {
+ _actor[0].damage = 0;
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0,
+ _continueFrame1, 1300);
+ }
+ _roadBranch = false;
+ _roadStop = false;
+}
+
+void Insane::postCase10(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if (curFrame >= maxFrame && !_needSceneSwitch) {
+ _actor[0].damage = 0;
+
+ switch (_currSceneId) {
+ case 20:
+ writeArray(8, 1);
+ queueSceneSwitch(12, 0, "liftgog.san", 0, 0, 0, 0);
+ break;
+ case 22:
+ writeArray(1, _val54d);
+ smush_setToFinish();
+ break;
+ default:
+ if (_actor[0].inventory[_enemy[_currEnemy].weapon]) {
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0,
+ _continueFrame, 1300);
+ break;
+ }
+
+ switch (_enemy[_currEnemy].weapon) {
+ case INV_CHAIN:
+ _actor[0].inventory[INV_CHAIN] = 1;
+ queueSceneSwitch(12, 0, "liftchay.san", 0, 0, 0, 0);
+ break;
+ case INV_CHAINSAW:
+ _actor[0].inventory[INV_CHAINSAW] = 1;
+ queueSceneSwitch(12, 0, "liftsaw.san", 0, 0, 0, 0);
+ break;
+ case INV_MACE:
+ _actor[0].inventory[INV_MACE] = 1;
+ queueSceneSwitch(12, 0, "liftmace.san", 0, 0, 0, 0);
+ break;
+ case INV_2X4:
+ _actor[0].inventory[INV_2X4] = 1;
+ queueSceneSwitch(12, 0, "liftbord.san", 0, 0, 0, 0);
+ break;
+ default:
+ queueSceneSwitch(1, _smush_minedrivFlu, "minedriv.san", 64, 0,
+ _continueFrame, 1300);
+ break;
+ }
+ }
+ }
+
+ _roadBranch = false;
+ _roadStop = false;
+}
+
+void Insane::postCase12(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if (_actor[1].y <= 200) {
+ initScene(3);
+ _actor[1].y = 200;
+
+ switch (_currEnemy) {
+ case EN_ROTT2:
+ turnBen(true);
+
+ if (_enemy[EN_ROTT2].occurences <= 1)
+ prepareScenePropScene(32, 0, 1);
+ else
+ prepareScenePropScene(33, 0, 1);
+ break;
+ case EN_ROTT3:
+ turnBen(true);
+
+ if (_enemy[EN_ROTT3].occurences <= 1)
+ prepareScenePropScene(25, 0, 1);
+ break;
+ case EN_VULTF1:
+ turnBen(true);
+
+ if (_enemy[EN_VULTF1].occurences <= 1)
+ prepareScenePropScene(2, 0, 1);
+ break;
+ case EN_VULTF2:
+ turnBen(true);
+
+ if (_enemy[EN_VULTF2].occurences <= 1)
+ prepareScenePropScene(9, 0, 1);
+ else
+ prepareScenePropScene(16, 0, 1);
+ break;
+ case EN_VULTM2:
+ if (_enemy[EN_VULTM2].occurences <= 1) {
+ turnBen(false);
+ prepareScenePropScene(18, 0, 1);
+ _battleScene = false;
+ } else
+ turnBen(true);
+ break;
+ case EN_TORQUE:
+ turnBen(false);
+ writeArray(1, _posFatherTorque);
+ smush_setToFinish();
+ break;
+ case EN_ROTT1:
+ case EN_VULTM1:
+ case EN_CAVEFISH:
+ default:
+ turnBen(true);
+ break;
+ }
+ } else {
+ switch (_currEnemy) {
+ case EN_VULTM2:
+ if (_enemy[EN_VULTM2].occurences <= 1)
+ turnBen(false);
+ else
+ turnBen(true);
+ break;
+ case EN_TORQUE:
+ turnBen(false);
+ if (_actor[1].y != 300)
+ prepareScenePropScene(57, 1, 0);
+ break;
+ default:
+ turnBen(true);
+ }
+ _actor[1].y -= (_actor[1].y - 200) / 20 + 1;
+ }
+
+ turnEnemy(false);
+
+ if (curFrame == 0)
+ smlayer_setFluPalette(_smush_roadrashRip, 0);
+
+ if (curFrame >= maxFrame)
+ smush_rewindCurrentSan(1088, -1, -1);
+
+ _roadBranch = false;
+ _roadStop = false;
+ _continueFrame = curFrame;
+}
+
+void Insane::postCase23(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if (curFrame >= maxFrame) {
+ if (_currSceneId == 24) {
+ queueSceneSwitch(21, 0, "rottfite.san", 64, 0, 0, 0);
+ } else {
+ if (readArray(6) && readArray(4))
+ queueSceneSwitch(16, 0, "limocrsh.san", 64, 0, 0, 0);
+ else
+ queueSceneSwitch(5, 0, "tovista2.san", 64, 0, 0, 290);
+ }
+ }
+ _roadBranch = false;
+ _roadStop = false;
+}
+
+void Insane::postCase14(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if (curFrame >= maxFrame) {
+ if (_currSceneId == 16) {
+ writeArray(4, 0);
+ writeArray(5, 1);
+ writeArray(1, _posBrokenCar);
+ writeArray(3, _posBrokenTruck);
+ smush_setToFinish();
+ } else {
+ switch (_tempSceneId) {
+ case 5:
+ queueSceneSwitch(6, 0, "toranch.san", 64, 0, 0, 530);
+ break;
+ case 6:
+ queueSceneSwitch(4, 0, "tovista1.san", 64, 0, 0, 230);
+ break;
+ }
+ }
+ }
+
+ _roadBranch = false;
+ _roadStop = false;
+}
+
+void Insane::postCaseAll(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ struct sceneProp *tsceneProp;
+
+ tsceneProp = &_sceneProp[_currScenePropIdx + _currScenePropSubIdx];
+ if (tsceneProp->actor != -1) {
+ if (_actor[tsceneProp->actor].field_54) {
+ tsceneProp->counter++;
+ if (!_actor[tsceneProp->actor].runningSound || ConfMan.getBool("subtitles")) {
+ if (_actor[tsceneProp->actor].act[3].state == 72 &&
+ _currTrsMsg) {
+ _player->setPaletteValue(0, tsceneProp->r, tsceneProp->g, tsceneProp->b);
+ _player->setPaletteValue(1, tsceneProp->r, tsceneProp->g, tsceneProp->b);
+ _player->setPaletteValue(0, 0, 0, 0);
+ smlayer_showStatusMsg(-1, renderBitmap, codecparam, 160, 20, 1, 2, 5,
+ "^f00%s", _currTrsMsg);
+ }
+ }
+ } else {
+ _currScenePropSubIdx = tsceneProp->index;
+ if (_currScenePropSubIdx && _currScenePropIdx) {
+ tsceneProp = &_sceneProp[_currScenePropIdx + _currScenePropSubIdx];
+ tsceneProp->counter = 0;
+ if (tsceneProp->trsId)
+ _currTrsMsg = handleTrsTag(tsceneProp->trsId);
+ else
+ _currTrsMsg = 0;
+
+ if (tsceneProp->actor != -1) {
+ _actor[tsceneProp->actor].field_54 = 1;
+ _actor[tsceneProp->actor].act[3].state = 117;
+ _actor[tsceneProp->actor].scenePropSubIdx = _currScenePropSubIdx;
+ }
+ } else {
+ _currScenePropIdx = 0;
+ _currTrsMsg = 0;
+ _currScenePropSubIdx = 0;
+ _actor[0].defunct = 0;
+ _actor[1].defunct = 0;
+ _battleScene = true;
+ }
+ }
+ }
+ _roadBranch = false;
+ _roadStop = false;
+ _continueFrame = curFrame;
+}
+
+void Insane::postCaseMore(byte *renderBitmap, int32 codecparam, int32 setupsan12,
+ int32 setupsan13, int32 curFrame, int32 maxFrame) {
+ if (_actor[0].weapon <= 7) {
+ smlayer_drawSomething(renderBitmap, codecparam, 5, 160, 1, _smush_iconsNut,
+ _actor[0].weapon + 11, 0, 0);
+ }
+}
+
+}
+
diff --git a/engines/scumm/instrument.cpp b/engines/scumm/instrument.cpp
new file mode 100644
index 0000000000..6e688d89e5
--- /dev/null
+++ b/engines/scumm/instrument.cpp
@@ -0,0 +1,462 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/saveload.h"
+#include "scumm/instrument.h"
+#include "sound/mididrv.h"
+
+namespace Scumm {
+
+static bool _native_mt32 = false;
+
+static struct {
+ const char *name;
+ byte program;
+}
+
+roland_to_gm_map [] = {
+ // Monkey Island 2 instruments
+ // TODO: Complete
+ { "badspit ", 62 },
+ { "Big Drum ", 116 },
+ { "burp ", 58 },
+// { "dinkfall ", ??? },
+// { "Fire Pit ", ??? },
+ { "foghorn ", 60 },
+ { "glop ", 39 },
+// { "jacob's la", ??? },
+ { "LeshBass ", 33 },
+// { "lowsnort ", ??? },
+ { "ML explosn", 127 },
+ { "ReggaeBass", 32 },
+// { "rope fall ", ??? },
+ { "rumble ", 89 },
+ { "SdTrk Bend", 97 },
+// { "snort ", ??? },
+ { "spitting ", 62 },
+ { "Swell 1 ", 95 },
+ { "Swell 2 ", 95 },
+ { "thnderclap", 127 }
+
+ // Fate of Atlantis instruments
+ // TODO: Build
+// { "*aah! ", ??? },
+// { "*ooh! ", ??? },
+// { "*ShotFar4 ", ??? },
+// { "*splash3 ", ??? },
+// { "*torpedo5 ", ??? },
+// { "*whip3 ", ??? },
+// { "*woodknock", ??? },
+// { "35 lavabub", ??? },
+// { "49 bzzt! ", ??? },
+// { "applause ", ??? },
+// { "Arabongo ", ??? },
+// { "Big Drum ", ??? }, // DUPLICATE (todo: confirm)
+// { "bodythud1 ", ??? },
+// { "boneKLOK2 ", ??? },
+// { "boom10 ", ??? },
+// { "boom11 ", ??? },
+// { "boom15 ", ??? },
+// { "boxclik1a ", ??? },
+// { "brassbonk3", ??? },
+// { "carstart ", ??? },
+// { "cb tpt 2 ", ??? },
+// { "cell door ", ??? },
+// { "chains ", ??? },
+// { "crash ", ??? },
+// { "crsrt/idl3", ??? },
+// { "Fire Pit ", ??? }, // DUPLICATE (todo: confirm)
+// { "Fzooom ", ??? },
+// { "Fzooom 2 ", ??? },
+// { "ghostwhosh", ??? },
+// { "glasssmash", ??? },
+// { "gloop2 ", ??? },
+// { "gunShotNea", ??? },
+// { "idoorclse ", ??? },
+// { "knife ", ??? },
+// { "lavacmbl4 ", ??? },
+// { "Mellow Str", ??? },
+// { "mtlheater1", ??? },
+// { "pachinko5 ", ??? },
+// { "Ping1 ", ??? },
+// { "rockcrunch", ??? },
+// { "rumble ", ??? }, // DUPLICATE (todo: confirm)
+// { "runngwatr ", ??? },
+// { "scrape2 ", ??? },
+// { "snakeHiss ", ??? },
+// { "snort ", ??? }, // DUPLICATE (todo: confirm)
+// { "spindle4 ", ??? },
+// { "splash2 ", ??? },
+// { "squirel ", ??? },
+// { "steam3 ", ??? },
+// { "stonwheel6", ??? },
+// { "street ", ??? },
+// { "trickle4 ", ??? }
+};
+
+const byte Instrument::_gmRhythmMap[35] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 39, 40, 41, 66, 47,
+ 65, 48, 56};
+ // This emulates the percussion bank setup LEC used with the MT-32,
+ // where notes 24 - 34 were assigned instruments without reverb.
+ // It also fixes problems on GS devices that map sounds to these
+ // notes by default.
+
+class Instrument_Program : public InstrumentInternal {
+private:
+ byte _program;
+ bool _mt32;
+
+public:
+ Instrument_Program (byte program, bool mt32);
+ Instrument_Program (Serializer *s);
+ void saveOrLoad (Serializer *s);
+ void send (MidiChannel *mc);
+ void copy_to (Instrument *dest) { dest->program (_program, _mt32); }
+ bool is_valid() {
+ return (_program < 128) &&
+ ((_native_mt32 == _mt32) || _native_mt32
+ ? (MidiDriver::_gmToMt32[_program] < 128)
+ : (MidiDriver::_mt32ToGm[_program] < 128)); }
+};
+
+class Instrument_Adlib : public InstrumentInternal {
+private:
+ struct {
+ byte flags_1;
+ byte oplvl_1;
+ byte atdec_1;
+ byte sustrel_1;
+ byte waveform_1;
+ byte flags_2;
+ byte oplvl_2;
+ byte atdec_2;
+ byte sustrel_2;
+ byte waveform_2;
+ byte feedback;
+ byte flags_a;
+ struct { byte a,b,c,d,e,f,g,h; } extra_a;
+ byte flags_b;
+ struct { byte a,b,c,d,e,f,g,h; } extra_b;
+ byte duration;
+ } _instrument;
+
+public:
+ Instrument_Adlib (byte *data);
+ Instrument_Adlib (Serializer *s);
+ void saveOrLoad (Serializer *s);
+ void send (MidiChannel *mc);
+ void copy_to (Instrument *dest) { dest->adlib ((byte *) &_instrument); }
+ bool is_valid() { return true; }
+};
+
+class Instrument_Roland : public InstrumentInternal {
+private:
+ struct RolandInstrument {
+ byte roland_id;
+ byte device_id;
+ byte model_id;
+ byte command;
+ byte address[3];
+ struct {
+ byte name[10];
+ byte partial_struct12;
+ byte partial_struct34;
+ byte partial_mute;
+ byte env_mode;
+ } common;
+ struct {
+ byte wg_pitch_coarse;
+ byte wg_pitch_fine;
+ byte wg_pitch_keyfollow;
+ byte wg_pitch_bender_sw;
+ byte wg_waveform_pcm_bank;
+ byte wg_pcm_wave_num;
+ byte wg_pulse_width;
+ byte wg_pw_velo_sens;
+ byte p_env_depth;
+ byte p_evn_velo_sens;
+ byte p_env_time_keyf;
+ byte p_env_time[4];
+ byte p_env_level[3];
+ byte p_env_sustain_level;
+ byte end_level;
+ byte p_lfo_rate;
+ byte p_lfo_depth;
+ byte p_lfo_mod_sens;
+ byte tvf_cutoff_freq;
+ byte tvf_resonance;
+ byte tvf_keyfollow;
+ byte tvf_bias_point_dir;
+ byte tvf_bias_level;
+ byte tvf_env_depth;
+ byte tvf_env_velo_sens;
+ byte tvf_env_depth_keyf;
+ byte tvf_env_time_keyf;
+ byte tvf_env_time[5];
+ byte tvf_env_level[3];
+ byte tvf_env_sustain_level;
+ byte tva_level;
+ byte tva_velo_sens;
+ byte tva_bias_point_1;
+ byte tva_bias_level_1;
+ byte tva_bias_point_2;
+ byte tva_bias_level_2;
+ byte tva_env_time_keyf;
+ byte tva_env_time_v_follow;
+ byte tva_env_time[5];
+ byte tva_env_level[3];
+ byte tva_env_sustain_level;
+ } partial[4];
+ byte checksum;
+ } GNUPACK;
+ RolandInstrument _instrument;
+
+ char _instrument_name [11];
+
+ uint8 getEquivalentGM();
+
+public:
+ Instrument_Roland (byte *data);
+ Instrument_Roland (Serializer *s);
+ void saveOrLoad (Serializer *s);
+ void send (MidiChannel *mc);
+ void copy_to (Instrument *dest) { dest->roland ((byte *) &_instrument); }
+ bool is_valid() { return (_native_mt32 ? true : (_instrument_name[0] != '\0')); }
+};
+
+////////////////////////////////////////
+//
+// Instrument class members
+//
+////////////////////////////////////////
+
+void Instrument::nativeMT32 (bool native) {
+ _native_mt32 = native;
+}
+
+void Instrument::clear() {
+ if (_instrument)
+ delete _instrument;
+ _instrument = NULL;
+ _type = itNone;
+}
+
+void Instrument::program (byte prog, bool mt32) {
+ clear();
+ if (prog > 127)
+ return;
+ _type = itProgram;
+ _instrument = new Instrument_Program (prog, mt32);
+}
+
+void Instrument::adlib (byte *instrument) {
+ clear();
+ if (!instrument)
+ return;
+ _type = itAdlib;
+ _instrument = new Instrument_Adlib (instrument);
+}
+
+void Instrument::roland (byte *instrument) {
+ clear();
+ if (!instrument)
+ return;
+ _type = itRoland;
+ _instrument = new Instrument_Roland (instrument);
+}
+
+void Instrument::saveOrLoad (Serializer *s) {
+ if (s->isSaving()) {
+ s->saveByte (_type);
+ if (_instrument)
+ _instrument->saveOrLoad (s);
+ } else {
+ clear();
+ _type = s->loadByte();
+ switch (_type) {
+ case itNone:
+ break;
+ case itProgram:
+ _instrument = new Instrument_Program (s);
+ break;
+ case itAdlib:
+ _instrument = new Instrument_Adlib (s);
+ break;
+ case itRoland:
+ _instrument = new Instrument_Roland (s);
+ break;
+ default:
+ warning ("No known instrument classification #%d", (int) _type);
+ _type = itNone;
+ }
+ }
+}
+
+////////////////////////////////////////
+//
+// Instrument_Program class members
+//
+////////////////////////////////////////
+
+Instrument_Program::Instrument_Program (byte program, bool mt32) :
+_program (program),
+_mt32 (mt32) {
+ if (program > 127)
+ _program = 255;
+}
+
+Instrument_Program::Instrument_Program (Serializer *s) {
+ _program = 255;
+ if (!s->isSaving())
+ saveOrLoad (s);
+}
+
+void Instrument_Program::saveOrLoad (Serializer *s) {
+ if (s->isSaving()) {
+ s->saveByte (_program);
+ s->saveByte (_mt32 ? 1 : 0);
+ } else {
+ _program = s->loadByte();
+ _mt32 = (s->loadByte() > 0);
+ }
+}
+
+void Instrument_Program::send (MidiChannel *mc) {
+ if (_program > 127)
+ return;
+
+ byte program = _program;
+ if (_native_mt32 != _mt32)
+ program = _native_mt32 ? MidiDriver::_gmToMt32 [program] : MidiDriver::_mt32ToGm [program];
+ if (program < 128)
+ mc->programChange (program);
+}
+
+////////////////////////////////////////
+//
+// Instrument_Adlib class members
+//
+////////////////////////////////////////
+
+Instrument_Adlib::Instrument_Adlib (byte *data) {
+ memcpy (&_instrument, data, sizeof (_instrument));
+}
+
+Instrument_Adlib::Instrument_Adlib (Serializer *s) {
+ if (!s->isSaving())
+ saveOrLoad (s);
+ else
+ memset (&_instrument, 0, sizeof (_instrument));
+}
+
+void Instrument_Adlib::saveOrLoad (Serializer *s) {
+ if (s->isSaving())
+ s->saveBytes (&_instrument, sizeof (_instrument));
+ else
+ s->loadBytes (&_instrument, sizeof (_instrument));
+}
+
+void Instrument_Adlib::send (MidiChannel *mc) {
+ mc->sysEx_customInstrument ('ADL ', (byte *) &_instrument);
+}
+
+////////////////////////////////////////
+//
+// Instrument_Roland class members
+//
+////////////////////////////////////////
+
+Instrument_Roland::Instrument_Roland (byte *data) {
+ memcpy (&_instrument, data, sizeof (_instrument));
+ memcpy (&_instrument_name, &_instrument.common.name, sizeof (_instrument.common.name));
+ _instrument_name[10] = '\0';
+ if (!_native_mt32 && getEquivalentGM() >= 128) {
+ debug (0, "MT-32 instrument \"%s\" not supported yet", _instrument_name);
+ _instrument_name[0] = '\0';
+ }
+}
+
+Instrument_Roland::Instrument_Roland (Serializer *s) {
+ _instrument_name[0] = '\0';
+ if (!s->isSaving())
+ saveOrLoad (s);
+ else
+ memset (&_instrument, 0, sizeof (_instrument));
+}
+
+void Instrument_Roland::saveOrLoad (Serializer *s) {
+ if (s->isSaving()) {
+ s->saveBytes (&_instrument, sizeof (_instrument));
+ } else {
+ s->loadBytes (&_instrument, sizeof (_instrument));
+ memcpy (&_instrument_name, &_instrument.common.name, sizeof (_instrument.common.name));
+ _instrument_name[10] = '\0';
+ if (!_native_mt32 && getEquivalentGM() >= 128) {
+ debug (2, "MT-32 custom instrument \"%s\" not supported", _instrument_name);
+ _instrument_name[0] = '\0';
+ }
+ } // end if
+}
+
+void Instrument_Roland::send (MidiChannel *mc) {
+ if (_native_mt32) {
+ if (mc->getNumber() > 8)
+ return;
+ _instrument.device_id = mc->getNumber();
+
+ // Remap instrument to appropriate address space.
+ int address = 0x008000;
+ _instrument.address[0] = (address >> 14) & 0x7F;
+ _instrument.address[1] = (address >> 7) & 0x7F;
+ _instrument.address[2] = (address ) & 0x7F;
+
+ // Recompute the checksum.
+ byte checksum = 0;
+ byte *ptr = (byte *) &_instrument + 4;
+ int i;
+ for (i = 4; i < (int)sizeof (_instrument) - 1; ++i)
+ checksum -= *ptr++;
+ _instrument.checksum = checksum & 0x7F;
+
+ mc->device()->sysEx ((byte *) &_instrument, sizeof (_instrument));
+ } else {
+ // Convert to a GM program change.
+ byte program = getEquivalentGM();
+ if (program < 128)
+ mc->programChange (program);
+ }
+}
+
+uint8 Instrument_Roland::getEquivalentGM() {
+ byte i;
+ for (i = 0; i != ARRAYSIZE(roland_to_gm_map); ++i) {
+ if (!memcmp (roland_to_gm_map[i].name, _instrument.common.name, 10))
+ return roland_to_gm_map[i].program;
+ }
+ return 255;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/instrument.h b/engines/scumm/instrument.h
new file mode 100644
index 0000000000..eb1a30a1d1
--- /dev/null
+++ b/engines/scumm/instrument.h
@@ -0,0 +1,79 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef INSTRUMENT_H
+#define INSTRUMENT_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+
+class MidiChannel;
+
+namespace Scumm {
+
+class Serializer;
+class Instrument;
+
+class InstrumentInternal {
+public:
+ virtual ~InstrumentInternal() {}
+ virtual void saveOrLoad (Serializer *s) = 0;
+ virtual void send (MidiChannel *mc) = 0;
+ virtual void copy_to (Instrument *dest) = 0;
+ virtual bool is_valid() = 0;
+ virtual operator int() { return 255; }
+};
+
+class Instrument {
+private:
+ byte _type;
+ InstrumentInternal *_instrument;
+
+public:
+ enum {
+ itNone = 0,
+ itProgram = 1,
+ itAdlib = 2,
+ itRoland = 3
+ };
+
+ Instrument() : _type (0), _instrument (0) { }
+ ~Instrument() { delete _instrument; }
+ static void nativeMT32 (bool native);
+ static const byte _gmRhythmMap[35];
+
+ void clear();
+ void copy_to (Instrument *dest) { if (_instrument) _instrument->copy_to (dest); else dest->clear(); }
+
+ void program (byte program, bool mt32);
+ void adlib (byte *instrument);
+ void roland (byte *instrument);
+
+ byte getType() { return _type; }
+ bool isValid() { return (_instrument ? _instrument->is_valid() : false); }
+ void saveOrLoad (Serializer *s);
+ void send (MidiChannel *mc) { if (_instrument) _instrument->send (mc); }
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/intern.h b/engines/scumm/intern.h
new file mode 100644
index 0000000000..fae915a037
--- /dev/null
+++ b/engines/scumm/intern.h
@@ -0,0 +1,912 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCUMM_INTERN_H
+#define SCUMM_INTERN_H
+
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+// This is to help devices with small memory (PDA, smartphones, ...)
+// to save abit of memory used by opcode names in the Scumm engine.
+#ifndef REDUCE_MEMORY_USAGE
+# define _OPCODE(ver, x) { &ver::x, #x }
+#else
+# define _OPCODE(ver, x) { &ver::x, "" }
+#endif
+
+class ScummEngine_v5 : public ScummEngine {
+protected:
+ typedef void (ScummEngine_v5::*OpcodeProcV5)();
+ struct OpcodeEntryV5 {
+ OpcodeProcV5 proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryV5 *_opcodesV5;
+
+ uint16 _cursorImages[4][17];
+ byte _cursorHotspots[2 * 4];
+
+public:
+ ScummEngine_v5(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+ virtual void decodeParseString();
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void readMAXS(int blockSize);
+
+ int getWordVararg(int *ptr);
+ void saveVars();
+ void loadVars();
+
+ virtual int getVar();
+ virtual int getVarOrDirectByte(byte mask);
+ virtual int getVarOrDirectWord(byte mask);
+
+ virtual void animateCursor();
+
+ void setBuiltinCursor(int index);
+ void redefineBuiltinCursorFromChar(int index, int chr);
+ void redefineBuiltinCursorHotspot(int index, int x, int y);
+
+ /* Version 5 script opcodes */
+ void o5_actorFollowCamera();
+ void o5_actorFromPos();
+ void o5_actorOps();
+ void o5_add();
+ void o5_and();
+ void o5_animateActor();
+ void o5_breakHere();
+ void o5_chainScript();
+ void o5_cursorCommand();
+ void o5_cutscene();
+ void o5_debug();
+ void o5_decrement();
+ void o5_delay();
+ void o5_delayVariable();
+ void o5_divide();
+ void o5_doSentence();
+ void o5_drawBox();
+ void o5_drawObject();
+ void o5_endCutscene();
+ void o5_equalZero();
+ void o5_expression();
+ void o5_faceActor();
+ void o5_findInventory();
+ void o5_findObject();
+ void o5_freezeScripts();
+ void o5_getActorCostume();
+ void o5_getActorElevation();
+ void o5_getActorFacing();
+ void o5_getActorMoving();
+ void o5_getActorRoom();
+ void o5_getActorScale();
+ void o5_getActorWalkBox();
+ void o5_getActorWidth();
+ void o5_getActorX();
+ void o5_getActorY();
+ void o5_getAnimCounter();
+ void o5_getClosestObjActor();
+ void o5_getDist();
+ void o5_getInventoryCount();
+ void o5_getObjectOwner();
+ void o5_getObjectState();
+ void o5_getRandomNr();
+ void o5_getStringWidth();
+ void o5_getVerbEntrypoint();
+ void o5_ifClassOfIs();
+ void o5_ifNotState();
+ void o5_ifState();
+ void o5_increment();
+ void o5_isActorInBox();
+ void o5_isEqual();
+ void o5_isGreater();
+ void o5_isGreaterEqual();
+ void o5_isLess();
+ void o5_isNotEqual();
+ void o5_isScriptRunning();
+ void o5_isSoundRunning();
+ void o5_jumpRelative();
+ void o5_lessOrEqual();
+ void o5_lights();
+ void o5_loadRoom();
+ void o5_loadRoomWithEgo();
+ void o5_matrixOps();
+ void o5_move();
+ void o5_multiply();
+ void o5_notEqualZero();
+ void o5_oldRoomEffect();
+ void o5_or();
+ void o5_beginOverride();
+ void o5_panCameraTo();
+ void o5_pickupObject();
+ void o5_pickupObjectOld();
+ void o5_print();
+ void o5_printEgo();
+ void o5_pseudoRoom();
+ void o5_putActor();
+ void o5_putActorAtObject();
+ void o5_putActorInRoom();
+ void o5_systemOps();
+ void o5_resourceRoutines();
+ void o5_roomOps();
+ void o5_saveLoadGame();
+ void o5_saveLoadVars();
+ void o5_saveRestoreVerbs();
+ void o5_setCameraAt();
+ void o5_setClass();
+ void o5_setObjectName();
+ void o5_setOwnerOf();
+ void o5_setState();
+ void o5_setVarRange();
+ void o5_soundKludge();
+ void o5_startMusic();
+ void o5_startObject();
+ void o5_startScript();
+ void o5_startSound();
+ void o5_stopMusic();
+ void o5_stopObjectCode();
+ void o5_stopObjectScript();
+ void o5_stopScript();
+ void o5_stopSound();
+ void o5_stringOps();
+ void o5_subtract();
+ void o5_verbOps();
+ void o5_wait();
+ void o5_walkActorTo();
+ void o5_walkActorToActor();
+ void o5_walkActorToObject();
+};
+
+/**
+ * Engine for version 4 SCUMM games; GF_SMALL_HEADER is always set for these.
+ */
+class ScummEngine_v4 : public ScummEngine_v5 {
+public:
+ ScummEngine_v4(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+protected:
+ virtual void readIndexFile();
+ virtual void loadCharset(int no);
+ virtual void loadRoomObjects();
+ virtual void readMAXS(int blockSize);
+ virtual void readGlobalObjects();
+
+ virtual void setupRoomObject(ObjectData *od, const byte *room, const byte *searchptr = NULL);
+};
+
+/**
+ * Engine for version 3 SCUMM games; GF_SMALL_NAMES is always set for these.
+ */
+class ScummEngine_v3 : public ScummEngine_v4 {
+public:
+ ScummEngine_v3(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+protected:
+ virtual void readRoomsOffsets();
+ virtual void loadCharset(int no);
+};
+
+/**
+ * Engine for old format version 3 SCUMM games; GF_OLD_BUNDLE is always set for these.
+ */
+class ScummEngine_v3old : public ScummEngine_v3 {
+public:
+ ScummEngine_v3old(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+protected:
+ virtual void readResTypeList(int id, uint32 tag, const char *name);
+ virtual void readIndexFile();
+ virtual void loadRoomSubBlocks();
+ virtual void initRoomSubBlocks();
+ virtual void loadRoomObjects();
+};
+
+/**
+ * Engine for version 2 SCUMM games.
+ */
+class ScummEngine_v2 : public ScummEngine_v3old {
+protected:
+ typedef void (ScummEngine_v2::*OpcodeProcV2)();
+ struct OpcodeEntryV2 {
+ OpcodeProcV2 proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryV2 *_opcodesV2;
+
+ struct V2MouseoverBox {
+ Common::Rect rect;
+ byte color;
+ byte hicolor;
+ };
+
+ V2MouseoverBox _mouseOverBoxesV2[7];
+ int8 _mouseOverBoxV2;
+
+public:
+ ScummEngine_v2(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+ virtual void scummInit();
+
+ void checkV2MouseOver(Common::Point pos);
+ void checkV2Inventory(int x, int y);
+ void redrawV2Inventory();
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+ virtual void decodeParseString();
+
+ virtual void readIndexFile();
+ void readClassicIndexFile(); // V1
+ void readEnhancedIndexFile(); // V2
+ virtual void readGlobalObjects();
+ virtual void loadCharset(int no);
+
+ virtual void runInventoryScript(int i);
+
+ virtual int getVar();
+
+ void getResultPosIndirect();
+ virtual void getResultPos();
+ virtual int readVar(uint var);
+ virtual void writeVar(uint var, int value);
+
+ virtual void ifStateCommon(byte type);
+ virtual void ifNotStateCommon(byte type);
+ virtual void setStateCommon(byte type);
+ virtual void clearStateCommon(byte type);
+
+ void resetSentence();
+ void setUserState(byte state);
+
+ virtual void handleMouseOver(bool updateInventory);
+ void initV2MouseOver();
+ void initNESMouseOver();
+
+ /* Version 2 script opcodes */
+ void o2_actorFromPos();
+ void o2_actorOps();
+ void o2_add();
+ void o2_addIndirect();
+ void o2_animateActor();
+ void o2_assignVarByte();
+ void o2_assignVarWordIndirect();
+ void o2_beginOverride();
+ void o2_chainScript();
+ void o2_clearState01();
+ void o2_clearState02();
+ void o2_clearState04();
+ void o2_clearState08();
+ void o2_cursorCommand();
+ void o2_cutscene();
+ void o2_delay();
+ void o2_doSentence();
+ void o2_drawObject();
+ void o2_drawSentence();
+ void o2_dummy();
+ void o2_endCutscene();
+ void o2_findObject();
+ void o2_getActorWalkBox();
+ void o2_getActorX();
+ void o2_getActorY();
+ void o2_getBitVar();
+ void o2_getObjPreposition();
+ void o2_ifClassOfIs();
+ void o2_ifNotState01();
+ void o2_ifNotState02();
+ void o2_ifNotState04();
+ void o2_ifNotState08();
+ void o2_ifState01();
+ void o2_ifState02();
+ void o2_ifState04();
+ void o2_ifState08();
+ void o2_isGreater();
+ void o2_isGreaterEqual();
+ void o2_isLess();
+ void o2_isLessEqual();
+ void o2_lights();
+ void o2_loadRoomWithEgo();
+ void o2_setBoxFlags();
+ void o2_panCameraTo();
+ void o2_pickupObject();
+ void o2_putActor();
+ void o2_putActorAtObject();
+ void o2_resourceRoutines();
+ void o2_restart();
+ void o2_roomOps();
+ void o2_getActorElevation();
+ void o2_setActorElevation();
+ void o2_setBitVar();
+ void o2_setCameraAt();
+ void o2_setObjPreposition();
+ void o2_setOwnerOf();
+ void o2_setState01();
+ void o2_setState02();
+ void o2_setState04();
+ void o2_setState08();
+ void o2_startScript();
+ void o2_stopScript();
+ void o2_subtract();
+ void o2_subIndirect();
+ void o2_switchCostumeSet();
+ void o2_verbOps();
+ void o2_waitForActor();
+ void o2_waitForMessage();
+ void o2_waitForSentence();
+ void o2_walkActorTo();
+ void o2_walkActorToObject();
+
+ byte VAR_SENTENCE_VERB;
+ byte VAR_SENTENCE_OBJECT1;
+ byte VAR_SENTENCE_OBJECT2;
+ byte VAR_SENTENCE_PREPOSITION;
+ byte VAR_BACKUP_VERB;
+};
+
+/**
+ * Engine for Commodore 64 version of Maniac Mansion
+ */
+class ScummEngine_c64 : public ScummEngine_v2 {
+protected:
+ typedef void (ScummEngine_c64::*OpcodeProcC64)();
+ struct OpcodeEntryC64 {
+ OpcodeProcC64 proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryC64 *_opcodesC64;
+
+ int _currentAction;
+ int _currentMode;
+public:
+ ScummEngine_c64(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+ virtual void scummInit();
+
+protected:
+ virtual void setupRoomObject(ObjectData *od, const byte *room, const byte *searchptr = NULL);
+
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void decodeParseString();
+
+ void initC64Verbs();
+ virtual void checkExecVerbs();
+
+ virtual int getVarOrDirectWord(byte mask);
+ virtual uint fetchScriptWord();
+
+ virtual void ifStateCommon(byte type);
+ virtual void ifNotStateCommon(byte type);
+ virtual void setStateCommon(byte type);
+ virtual void clearStateCommon(byte type);
+
+ int getObjectFlag();
+
+ /* Version C64 script opcodes */
+ void o_setState08();
+ void o_clearState08();
+ void o_stopCurrentScript();
+ void o_loadSound();
+ void o_getActorMoving();
+ void o_animateActor();
+ void o_putActorAtObject();
+ void o_lockSound();
+ void o_lockActor();
+ void o_loadActor();
+ void o_loadRoom();
+ void o_loadRoomWithEgo();
+ void o_lockScript();
+ void o_loadScript();
+ void o_lockRoom();
+ void o_cursorCommand();
+ void o_lights();
+ void o_pickupObject();
+ void o_unlockActor();
+ void o_unlockScript();
+ void o_decrement();
+ void o_badOpcode();
+ void o_nop();
+ void o_getActorBitVar();
+ void o_setActorBitVar();
+ void o_doSentence();
+ void o_unknown2();
+ void o_unknown3();
+ void o_getClosestObjActor();
+ void o_printEgo_c64();
+ void o_print_c64();
+ void o_unlockRoom();
+ void o_unlockSound();
+ void o_beginOverride();
+ void o_isEqual();
+ void o_isGreater();
+ void o_isGreaterEqual();
+ void o_isLess();
+ void o_isLessEqual();
+ void o_isNotEqual();
+ void o_notEqualZero();
+ void o_equalZero();
+ void o_jumpRelative();
+};
+
+class ScummEngine_v6 : public ScummEngine {
+ friend class Insane;
+
+protected:
+ typedef void (ScummEngine_v6::*OpcodeProcV6)();
+ struct OpcodeEntryV6 {
+ OpcodeProcV6 proc;
+ const char *desc;
+ };
+
+ enum ArrayType {
+ kBitArray = 1,
+ kNibbleArray = 2,
+ kByteArray = 3,
+ kStringArray = 4,
+ kIntArray = 5,
+ kDwordArray = 6
+ };
+
+ #if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+ #endif
+
+ struct ArrayHeader {
+ int16 dim1;
+ int16 type;
+ int16 dim2;
+ byte data[1];
+ } GCC_PACK;
+
+ #if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+ #endif
+
+ const OpcodeEntryV6 *_opcodesV6;
+
+ int _smushFrameRate;
+
+ struct TextObject {
+ int16 xpos, ypos;
+ byte color;
+ byte charset;
+ byte text[256];
+ };
+
+ /** BlastObjects to draw */
+ struct BlastObject {
+ uint16 number;
+ Common::Rect rect;
+ uint16 scaleX, scaleY;
+ uint16 image;
+ uint16 mode;
+ };
+
+ int _blastObjectQueuePos;
+ BlastObject _blastObjectQueue[200];
+
+ struct BlastText : TextObject {
+ Common::Rect rect;
+ bool center;
+ };
+
+ int _blastTextQueuePos;
+ BlastText _blastTextQueue[50];
+
+
+public:
+ ScummEngine_v6(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+ virtual void scummInit();
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void decodeParseString(int a, int b);
+ virtual void readArrayFromIndexFile();
+
+ virtual byte *getStringAddress(int i);
+ virtual void readMAXS(int blockSize);
+
+ virtual void palManipulateInit(int resID, int start, int end, int time);
+ virtual void drawDirtyScreenParts();
+
+ int getStackList(int *args, uint maxnum);
+ int popRoomAndObj(int *room);
+
+ ArrayHeader *getArray(int array);
+ ArrayHeader *defineArray(int array, int type, int dim2, int dim1);
+ int findFreeArrayId();
+ void nukeArray(int array);
+ virtual int readArray(int array, int index, int base);
+ virtual void writeArray(int array, int index, int base, int value);
+ void shuffleArray(int num, int minIdx, int maxIdx);
+
+ void setDefaultCursor();
+ void setCursorTransparency(int a);
+ void setCursorHotspot(int x, int y);
+
+ virtual void setCursorFromImg(uint img, uint room, uint imgindex);
+ void useIm01Cursor(const byte *im, int w, int h);
+ void useBompCursor(const byte *im, int w, int h);
+ void grabCursor(int x, int y, int w, int h);
+
+ void enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center);
+ void drawBlastTexts();
+ void removeBlastTexts();
+
+ void enqueueObject(int objectNumber, int objectX, int objectY, int objectWidth,
+ int objectHeight, int scaleX, int scaleY, int image, int mode);
+ void drawBlastObjects();
+ void drawBlastObject(BlastObject *eo);
+ void removeBlastObjects();
+ void removeBlastObject(BlastObject *eo);
+
+ virtual void clearDrawQueues();
+
+
+ /* Version 6 script opcodes */
+ void o6_setBlastObjectWindow();
+ void o6_pushByte();
+ void o6_pushWord();
+ void o6_pushByteVar();
+ void o6_pushWordVar();
+ void o6_invalid();
+ void o6_byteArrayRead();
+ void o6_wordArrayRead();
+ void o6_byteArrayIndexedRead();
+ void o6_wordArrayIndexedRead();
+ void o6_dup();
+ void o6_pop();
+ void o6_not();
+ void o6_eq();
+ void o6_neq();
+ void o6_gt();
+ void o6_lt();
+ void o6_le();
+ void o6_ge();
+ void o6_add();
+ void o6_sub();
+ void o6_mul();
+ void o6_div();
+ void o6_land();
+ void o6_lor();
+ void o6_writeByteVar();
+ void o6_writeWordVar();
+ void o6_byteArrayWrite();
+ void o6_wordArrayWrite();
+ void o6_byteArrayIndexedWrite();
+ void o6_wordArrayIndexedWrite();
+ void o6_byteVarInc();
+ void o6_wordVarInc();
+ void o6_byteArrayInc();
+ void o6_wordArrayInc();
+ void o6_byteVarDec();
+ void o6_wordVarDec();
+ void o6_byteArrayDec();
+ void o6_wordArrayDec();
+ void o6_if();
+ void o6_ifNot();
+ void o6_jump();
+ void o6_startScript();
+ void o6_startScriptQuick();
+ void o6_startObject();
+ void o6_drawObject();
+ void o6_drawObjectAt();
+ void o6_stopObjectCode();
+ void o6_endCutscene();
+ void o6_cutscene();
+ void o6_stopMusic();
+ void o6_freezeUnfreeze();
+ void o6_cursorCommand();
+ void o6_breakHere();
+ void o6_ifClassOfIs();
+ void o6_setClass();
+ void o6_getState();
+ void o6_setState();
+ void o6_setOwner();
+ void o6_getOwner();
+ void o6_startSound();
+ void o6_stopSound();
+ void o6_startMusic();
+ void o6_stopObjectScript();
+ void o6_panCameraTo();
+ void o6_actorFollowCamera();
+ void o6_setCameraAt();
+ void o6_loadRoom();
+ void o6_stopScript();
+ void o6_walkActorToObj();
+ void o6_walkActorTo();
+ void o6_putActorAtXY();
+ void o6_putActorAtObject();
+ void o6_faceActor();
+ void o6_animateActor();
+ void o6_doSentence();
+ void o6_pickupObject();
+ void o6_loadRoomWithEgo();
+ void o6_getRandomNumber();
+ void o6_getRandomNumberRange();
+ void o6_getActorMoving();
+ void o6_isScriptRunning();
+ void o6_getActorRoom();
+ void o6_getObjectX();
+ void o6_getObjectY();
+ void o6_getObjectOldDir();
+ void o6_getObjectNewDir();
+ void o6_getActorWalkBox();
+ void o6_getActorCostume();
+ void o6_findInventory();
+ void o6_getInventoryCount();
+ void o6_getVerbFromXY();
+ void o6_beginOverride();
+ void o6_endOverride();
+ void o6_setObjectName();
+ void o6_isSoundRunning();
+ void o6_setBoxFlags();
+ void o6_createBoxMatrix();
+ void o6_resourceRoutines();
+ void o6_roomOps();
+ void o6_actorOps();
+ void o6_verbOps();
+ void o6_getActorFromXY();
+ void o6_findObject();
+ void o6_pseudoRoom();
+ void o6_getActorElevation();
+ void o6_getVerbEntrypoint();
+ void o6_arrayOps();
+ void o6_saveRestoreVerbs();
+ void o6_drawBox();
+ void o6_getActorWidth();
+ void o6_wait();
+ void o6_getActorScaleX();
+ void o6_getActorAnimCounter1();
+ void o6_soundKludge();
+ void o6_isAnyOf();
+ void o6_systemOps();
+ void o6_isActorInBox();
+ void o6_delay();
+ void o6_delaySeconds();
+ void o6_delayMinutes();
+ void o6_stopSentence();
+ void o6_printLine();
+ void o6_printText();
+ void o6_printDebug();
+ void o6_printSystem();
+ void o6_printActor();
+ void o6_printEgo();
+ void o6_talkActor();
+ void o6_talkEgo();
+ void o6_dimArray();
+ void o6_dummy();
+ void o6_startObjectQuick();
+ void o6_startScriptQuick2();
+ void o6_dim2dimArray();
+ void o6_abs();
+ void o6_distObjectObject();
+ void o6_distObjectPt();
+ void o6_distPtPt();
+ void o6_kernelSetFunctions();
+ void o6_delayFrames();
+ void o6_pickOneOf();
+ void o6_pickOneOfDefault();
+ void o6_jumpToScript();
+ void o6_isRoomScriptRunning();
+ void o6_kernelGetFunctions();
+ void o6_getAnimateVariable();
+ void o6_drawBlastObject();
+ void o6_getActorLayer();
+ void o6_stampObject();
+ void o6_bor();
+ void o6_band();
+ void o6_stopTalking();
+ void o6_findAllObjects();
+ void o6_pickVarRandom();
+ void o6_getDateTime();
+ void o6_getPixel();
+ void o6_setBoxSet();
+ void o6_shuffle();
+
+ byte VAR_VIDEONAME;
+ byte VAR_RANDOM_NR;
+ byte VAR_STRING2DRAW;
+
+ byte VAR_TIMEDATE_YEAR;
+ byte VAR_TIMEDATE_MONTH;
+ byte VAR_TIMEDATE_DAY;
+ byte VAR_TIMEDATE_HOUR;
+ byte VAR_TIMEDATE_MINUTE;
+ byte VAR_TIMEDATE_SECOND;
+};
+
+#ifndef DISABLE_SCUMM_7_8
+class ScummEngine_v7 : public ScummEngine_v6 {
+public:
+ ScummEngine_v7(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+ ~ScummEngine_v7();
+
+ struct LangIndexNode {
+ char tag[12+1];
+ int32 offset;
+ };
+
+ bool _existLanguageFile;
+ char *_languageBuffer;
+ LangIndexNode *_languageIndex;
+ int _languageIndexSize;
+ char _lastStringTag[12+1];
+
+#if defined(__SYMBIAN32__) || defined (_WIN32_WCE) // for some reason VC6 cannot find the base class TextObject
+ struct SubtitleText {
+ int16 xpos, ypos;
+ byte color;
+ byte charset;
+ byte text[256];
+ bool actorSpeechMsg;
+ };
+#else
+ struct SubtitleText : TextObject {
+ bool actorSpeechMsg;
+ };
+#endif
+
+ int _subtitleQueuePos;
+ SubtitleText _subtitleQueue[20];
+
+ void processSubtitleQueue();
+ void addSubtitleToQueue(const byte *text, const Common::Point &pos, byte color, byte charset);
+ void clearSubtitleQueue();
+
+protected:
+ virtual void setupScummVars();
+ virtual void initScummVars();
+
+ virtual void akos_processQueue();
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void readMAXS(int blockSize);
+ virtual void readGlobalObjects();
+ virtual void readIndexBlock(uint32 blocktype, uint32 itemsize);
+
+ virtual void setCameraAt(int pos_x, int pos_y);
+ virtual void setCameraFollows(Actor *a);
+ virtual void moveCamera();
+ virtual void panCameraTo(int x, int y);
+
+ virtual int getObjectIdFromOBIM(const byte *obim);
+
+ virtual void actorTalk(const byte *msg);
+ virtual void translateText(const byte *text, byte *trans_buff);
+ virtual void loadLanguageBundle();
+ void playSpeech(const byte *ptr);
+
+ virtual void drawVerb(int verb, int mode);
+};
+
+class ScummEngine_v8 : public ScummEngine_v7 {
+protected:
+ typedef void (ScummEngine_v8::*OpcodeProcV8)();
+ struct OpcodeEntryV8 {
+ OpcodeProcV8 proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryV8 *_opcodesV8;
+
+ struct ObjectNameId {
+ char name[40];
+ int id;
+ };
+ int _objectIDMapSize;
+ ObjectNameId *_objectIDMap;
+
+public:
+ ScummEngine_v8(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+ ~ScummEngine_v8();
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void decodeParseString(int m, int n);
+ virtual void readArrayFromIndexFile();
+
+ virtual void readMAXS(int blockSize);
+ virtual void readGlobalObjects();
+
+ virtual uint fetchScriptWord();
+ virtual int fetchScriptWordSigned();
+
+ virtual int readVar(uint var);
+ virtual void writeVar(uint var, int value);
+
+ virtual int getObjectIdFromOBIM(const byte *obim);
+
+
+ void desaturatePalette(int hueScale, int satScale, int lightScale, int startColor, int endColor);
+
+
+ /* Version 8 script opcodes */
+ void o8_mod();
+ void o8_wait();
+
+ void o8_dimArray();
+ void o8_dim2dimArray();
+ void o8_arrayOps();
+ void o8_blastText();
+
+ void o8_cursorCommand();
+ void o8_resourceRoutines();
+ void o8_roomOps();
+ void o8_actorOps();
+ void o8_cameraOps();
+ void o8_verbOps();
+
+ void o8_systemOps();
+ void o8_startVideo();
+ void o8_kernelSetFunctions();
+ void o8_kernelGetFunctions();
+
+ void o8_getActorChore();
+ void o8_getActorZPlane();
+
+ void o8_drawObject();
+ void o8_getObjectImageX();
+ void o8_getObjectImageY();
+ void o8_getObjectImageWidth();
+ void o8_getObjectImageHeight();
+
+ void o8_getStringWidth();
+
+};
+
+#endif
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/intern_he.h b/engines/scumm/intern_he.h
new file mode 100644
index 0000000000..26a9e0a40f
--- /dev/null
+++ b/engines/scumm/intern_he.h
@@ -0,0 +1,606 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCUMM_INTERN_HE_H
+#define SCUMM_INTERN_HE_H
+
+#include "scumm/intern.h"
+#ifndef DISABLE_HE
+#include "scumm/floodfill_he.h"
+#include "scumm/wiz_he.h"
+#endif
+
+namespace Scumm {
+
+#ifndef DISABLE_HE
+class ResExtractor;
+class LogicHE;
+class Sprite;
+#endif
+
+class ScummEngine_v60he : public ScummEngine_v6 {
+protected:
+ typedef void (ScummEngine_v60he::*OpcodeProcv60he)();
+ struct OpcodeEntryv60he {
+ OpcodeProcv60he proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryv60he *_opcodesv60he;
+
+ Common::File _hFileTable[17];
+
+public:
+ ScummEngine_v60he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex) : ScummEngine_v6(detector, syst, gs, md5sum, substResFileNameIndex) {}
+
+ virtual void scummInit();
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void saveOrLoad(Serializer *s);
+
+ void localizeArray(int slot, byte scriptSlot);
+ void redimArray(int arrayId, int newX, int newY, int d);
+ int readFileToArray(int slot, int32 size);
+ void writeFileFromArray(int slot, int resID);
+ int virtScreenSave(byte *dst, int x1, int y1, int x2, int y2);
+ void virtScreenLoad(int resIdx, int x1, int y1, int x2, int y2);
+ virtual void decodeParseString(int a, int b);
+ void swapObjects(int object1, int object2);
+
+ /* HE version 60 script opcodes */
+ void o60_setState();
+ void o60_roomOps();
+ void o60_actorOps();
+ void o60_wait();
+ void o60_kernelSetFunctions();
+ void o60_kernelGetFunctions();
+ void o60_openFile();
+ void o60_closeFile();
+ void o60_deleteFile();
+ void o60_readFile();
+ void o60_rename();
+ void o60_writeFile();
+ void o60_soundOps();
+ void o60_seekFilePos();
+ void o60_localizeArrayToScript();
+ void o60_redimArray();
+ void o60_readFilePos();
+};
+
+#ifndef DISABLE_HE
+class ScummEngine_v70he : public ScummEngine_v60he {
+ friend class ResExtractor;
+ friend class Wiz;
+
+protected:
+ typedef void (ScummEngine_v70he::*OpcodeProcv70he)();
+ struct OpcodeEntryv70he {
+ OpcodeProcv70he proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryv70he *_opcodesv70he;
+
+ ResExtractor *_resExtractor;
+
+ byte *_heV7RoomOffsets;
+
+ int32 _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndSoundFreq;
+
+ bool _skipProcessActors;
+
+public:
+ ScummEngine_v70he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+ ~ScummEngine_v70he();
+
+ Wiz *_wiz;
+
+ byte *heFindResourceData(uint32 tag, byte *ptr);
+ byte *heFindResource(uint32 tag, byte *ptr);
+ byte *findWrappedBlock(uint32 tag, byte *ptr, int state, bool flagError);
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void readRoomsOffsets();
+ virtual void readGlobalObjects();
+ virtual void readIndexBlock(uint32 blocktype, uint32 itemsize);
+
+ virtual int getActorFromPos(int x, int y);
+
+ int getStringCharWidth(byte chr);
+ virtual int setupStringArray(int size);
+ void appendSubstring(int dst, int src, int len2, int len);
+
+ virtual void setCursorFromImg(uint img, uint room, uint imgindex);
+
+ virtual void clearDrawQueues();
+
+ void remapHEPalette(const uint8 *src, uint8 *dst);
+
+ /* HE version 70 script opcodes */
+ void o70_startSound();
+ void o70_pickupObject();
+ void o70_getActorRoom();
+ void o70_resourceRoutines();
+ void o70_systemOps();
+ void o70_kernelSetFunctions();
+ void o70_seekFilePos();
+ void o70_copyString();
+ void o70_getStringWidth();
+ void o70_getStringLen();
+ void o70_appendString();
+ void o70_concatString();
+ void o70_compareString();
+ void o70_isResourceLoaded();
+ void o70_readINI();
+ void o70_writeINI();
+ void o70_getStringLenForWidth();
+ void o70_getCharIndexInString();
+ void o70_setFilePath();
+ void o70_setWindowCaption();
+ void o70_polygonOps();
+ void o70_polygonHit();
+
+ byte VAR_NUM_SOUND_CHANNELS;
+ byte VAR_WIZ_TCOLOR;
+};
+
+class ScummEngine_v71he : public ScummEngine_v70he {
+public:
+ ScummEngine_v71he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+protected:
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void redrawBGAreas();
+
+ virtual void processActors();
+ void preProcessAuxQueue();
+ void postProcessAuxQueue();
+
+public:
+ /* Actor AuxQueue stuff (HE) */
+ AuxBlock _auxBlocks[16];
+ uint16 _auxBlocksNum;
+ AuxEntry _auxEntries[16];
+ uint16 _auxEntriesNum;
+
+ void queueAuxBlock(Actor *a);
+ void queueAuxEntry(int actorNum, int subIndex);
+};
+
+class ScummEngine_v72he : public ScummEngine_v71he {
+protected:
+ typedef void (ScummEngine_v72he::*OpcodeProcV72he)();
+ struct OpcodeEntryV72he {
+ OpcodeProcV72he proc;
+ const char *desc;
+ };
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+ struct ArrayHeader {
+ int32 type; //0
+ int32 dim1start; //4
+ int32 dim1end; //8
+ int32 dim2start; //0C
+ int32 dim2end; //10
+ byte data[1]; //14
+ } GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+ const OpcodeEntryV72he *_opcodesV72he;
+
+ int _stringLength;
+ byte _stringBuffer[4096];
+
+ WizParameters _wizParams;
+
+public:
+ ScummEngine_v72he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+ virtual void scummInit();
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+ virtual void readArrayFromIndexFile();
+
+ virtual byte *getStringAddress(int i);
+ virtual void readMAXS(int blockSize);
+
+ virtual void redrawBGAreas();
+
+ ArrayHeader *defineArray(int array, int type, int dim2start, int dim2end, int dim1start, int dim1end);
+ virtual int readArray(int array, int idx2, int idx1);
+ virtual void writeArray(int array, int idx2, int idx1, int value);
+ void redimArray(int arrayId, int newDim2start, int newDim2end,
+ int newDim1start, int newDim1end, int type);
+ void checkArrayLimits(int array, int dim2start, int dim2end, int dim1start, int dim1end);
+ void copyArray(int array1, int a1_dim2start, int a1_dim2end, int a1_dim1start, int a1_dim1end,
+ int array2, int a2_dim2start, int a2_dim2end, int a2_dim1start, int a2_dim1end);
+ void copyArrayHelper(ArrayHeader *ah, int idx2, int idx1, int len1, byte **data, int *size, int *num);
+ virtual int setupStringArray(int size);
+ int readFileToArray(int slot, int32 size);
+ void writeFileFromArray(int slot, int32 resID);
+
+ virtual void decodeParseString(int a, int b);
+ void decodeScriptString(byte *dst, bool scriptString = false);
+ void copyScriptString(byte *dst, int dstSize);
+ int convertFilePath(byte *dst, bool setFilePath = false);
+
+ int findObject(int x, int y, int num, int *args);
+ int getSoundResourceSize(int id);
+
+ virtual bool handleNextCharsetCode(Actor *a, int *c);
+
+ /* HE version 72 script opcodes */
+ void o72_pushDWord();
+ void o72_getScriptString();
+ void o72_isAnyOf();
+ void o72_resetCutscene();
+ void o72_findObjectWithClassOf();
+ void o72_getObjectImageX();
+ void o72_getObjectImageY();
+ void o72_captureWizImage();
+ void o72_getTimer();
+ void o72_setTimer();
+ void o72_getSoundPosition();
+ void o72_startScript();
+ void o72_startObject();
+ void o72_drawObject();
+ void o72_printWizImage();
+ void o72_getArrayDimSize();
+ void o72_getNumFreeArrays();
+ void o72_roomOps();
+ void o72_actorOps();
+ void o72_verbOps();
+ void o72_findObject();
+ void o72_arrayOps();
+ void o72_systemOps();
+ void o72_talkActor();
+ void o72_talkEgo();
+ void o72_dimArray();
+ void o72_dim2dimArray();
+ void o72_traceStatus();
+ void o72_debugInput();
+ void o72_drawWizImage();
+ void o72_kernelGetFunctions();
+ void o72_jumpToScript();
+ void o72_openFile();
+ void o72_readFile();
+ void o72_writeFile();
+ void o72_findAllObjects();
+ void o72_deleteFile();
+ void o72_rename();
+ void o72_getPixel();
+ void o72_pickVarRandom();
+ void o72_redimArray();
+ void o72_readINI();
+ void o72_writeINI();
+ void o72_getResourceSize();
+ void o72_setFilePath();
+ void o72_setWindowCaption();
+
+ byte VAR_NUM_ROOMS;
+ byte VAR_NUM_SCRIPTS;
+ byte VAR_NUM_SOUNDS;
+ byte VAR_NUM_COSTUMES;
+ byte VAR_NUM_IMAGES;
+ byte VAR_NUM_CHARSETS;
+
+ byte VAR_POLYGONS_ONLY;
+};
+
+class ScummEngine_v80he : public ScummEngine_v72he {
+protected:
+ typedef void (ScummEngine_v80he::*OpcodeProcV80he)();
+ struct OpcodeEntryV80he {
+ OpcodeProcV80he proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryV80he *_opcodesV80he;
+
+ int32 _heSndResId, _curSndId, _sndPtrOffs, _sndTmrOffs;
+
+public:
+ ScummEngine_v80he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+
+ virtual void initCharset(int charset);
+
+ virtual void clearDrawQueues();
+
+ void createSound(int snd1id, int snd2id);
+
+ void drawLine(int x1, int y1, int x, int unk1, int unk2, int type, int id);
+ void drawPixel(int x, int y, int flags);
+
+ /* HE version 80 script opcodes */
+ void o80_createSound();
+ void o80_getFileSize();
+ void o80_stringToInt();
+ void o80_getSoundVar();
+ void o80_localizeArrayToRoom();
+ void o80_sourceDebug();
+ void o80_readConfigFile();
+ void o80_writeConfigFile();
+ void o80_cursorCommand();
+ void o80_setState();
+ void o80_drawWizPolygon();
+ void o80_drawLine();
+ void o80_pickVarRandom();
+
+ byte VAR_PLATFORM;
+ byte VAR_WINDOWS_VERSION;
+ byte VAR_CURRENT_CHARSET;
+ byte VAR_COLOR_DEPTH;
+};
+
+class ScummEngine_v90he : public ScummEngine_v80he {
+ friend class LogicHE;
+ friend class Sprite;
+
+protected:
+ typedef void (ScummEngine_v90he::*OpcodeProcV90he)();
+ struct OpcodeEntryV90he {
+ OpcodeProcV90he proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryV90he *_opcodesV90he;
+
+ FloodFillParameters _floodFillParams;
+
+ struct VideoParameters {
+ byte filename[260];
+ int32 status;
+ int32 flags;
+ int32 unk2;
+ int32 wizResNum;
+ };
+
+ VideoParameters _videoParams;
+
+ int32 _heObject, _heObjectNum;
+ int32 _hePaletteNum;
+
+ int32 _curMaxSpriteId;
+ int32 _curSpriteId;
+ int32 _curSpriteGroupId;
+
+public:
+ ScummEngine_v90he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+ ~ScummEngine_v90he();
+
+ virtual void scummInit();
+
+ LogicHE *_logicHE;
+ Sprite *_sprite;
+
+protected:
+ virtual void allocateArrays();
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void readMAXS(int blockSize);
+
+ virtual void processActors();
+
+ int computeWizHistogram(int resnum, int state, int x, int y, int w, int h);
+ void getArrayDim(int array, int *dim2start, int *dim2end, int *dim1start, int *dim1end);
+ void sortArray(int array, int dim2start, int dim2end, int dim1start, int dim1end, int sortOrder);
+
+public:
+ int getGroupSpriteArray(int spriteGroupId);
+
+protected:
+ uint8 *getHEPaletteIndex(int palSlot);
+ int getHEPaletteColor(int palSlot, int color);
+ int getHEPaletteSimilarColor(int palSlot, int red, int green, int start, int end);
+ int getHEPaletteColorComponent(int palSlot, int color, int component);
+ void setHEPaletteColor(int palSlot, uint8 color, uint8 r, uint8 g, uint8 b);
+ void setHEPaletteFromPtr(int palSlot, const uint8 *palData);
+ void setHEPaletteFromCostume(int palSlot, int resId);
+ void setHEPaletteFromImage(int palSlot, int resId, int state);
+ void setHEPaletteFromRoom(int palSlot, int resId, int state);
+ void restoreHEPalette(int palSlot);
+ void copyHEPalette(int dstPalSlot, int srcPalSlot);
+ void copyHEPaletteColor(int palSlot, uint8 dstColor, uint8 srcColor);
+
+
+ void setDefaultCursor();
+
+protected:
+ /* HE version 90 script opcodes */
+ void o90_dup_n();
+ void o90_min();
+ void o90_max();
+ void o90_sin();
+ void o90_cos();
+ void o90_sqrt();
+ void o90_atan2();
+ void o90_getSegmentAngle();
+ void o90_getActorData();
+ void o90_startScriptUnk();
+ void o90_jumpToScriptUnk();
+ void o90_videoOps();
+ void o90_getVideoData();
+ void o90_wizImageOps();
+ void o90_getDistanceBetweenPoints();
+ void o90_getSpriteInfo();
+ void o90_setSpriteInfo();
+ void o90_getSpriteGroupInfo();
+ void o90_setSpriteGroupInfo();
+ void o90_getWizData();
+ void o90_floodFill();
+ void o90_mod();
+ void o90_shl();
+ void o90_shr();
+ void o90_xor();
+ void o90_findAllObjectsWithClassOf();
+ void o90_getPolygonOverlap();
+ void o90_cond();
+ void o90_dim2dim2Array();
+ void o90_redim2dimArray();
+ void o90_getLinesIntersectionPoint();
+ void o90_sortArray();
+ void o90_getObjectData();
+ void o90_getPaletteData();
+ void o90_paletteOps();
+ void o90_fontUnk();
+ void o90_getActorAnimProgress();
+ void o90_kernelGetFunctions();
+ void o90_kernelSetFunctions();
+
+ byte VAR_NUM_SPRITE_GROUPS;
+ byte VAR_NUM_SPRITES;
+ byte VAR_NUM_PALETTES;
+ byte VAR_NUM_UNK;
+
+ byte VAR_U32_VERSION;
+ byte VAR_U32_ARRAY_UNK;
+};
+
+class ScummEngine_v99he : public ScummEngine_v90he {
+public:
+ ScummEngine_v99he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex) : ScummEngine_v90he(detector, syst, gs, md5sum, substResFileNameIndex) {}
+
+ virtual void scummInit();
+
+protected:
+ virtual void initScummVars();
+
+ virtual void readMAXS(int blockSize);
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void copyPalColor(int dst, int src);
+ virtual void darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor);
+ virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1);
+ virtual void setPalColor(int index, int r, int g, int b);
+ virtual void updatePalette();
+};
+
+class ScummEngine_v100he : public ScummEngine_v99he {
+protected:
+ typedef void (ScummEngine_v100he::*OpcodeProcV100he)();
+ struct OpcodeEntryV100he {
+ OpcodeProcV100he proc;
+ const char *desc;
+ };
+
+ int32 _heResId, _heResType;
+
+ const OpcodeEntryV100he *_opcodesV100he;
+
+public:
+ ScummEngine_v100he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex) : ScummEngine_v99he(detector, syst, gs, md5sum, substResFileNameIndex) {}
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void decodeParseString(int a, int b);
+
+ /* HE version 100 script opcodes */
+ void o100_actorOps();
+ void o100_arrayOps();
+ void o100_dim2dimArray();
+ void o100_redim2dimArray();
+ void o100_dimArray();
+ void o100_drawLine();
+ void o100_drawObject();
+ void o100_floodFill();
+ void o100_setSpriteGroupInfo();
+ void o100_resourceRoutines();
+ void o100_wizImageOps();
+ void o100_jumpToScript();
+ void o100_createSound();
+ void o100_dim2dim2Array();
+ void o100_paletteOps();
+ void o100_jumpToScriptUnk();
+ void o100_startScriptUnk();
+ void o100_redimArray();
+ void o100_roomOps();
+ void o100_startSound();
+ void o100_setSpriteInfo();
+ void o100_startScript();
+ void o100_systemOps();
+ void o100_cursorCommand();
+ void o100_videoOps();
+ void o100_wait();
+ void o100_writeFile();
+ void o100_isResourceLoaded();
+ void o100_getResourceSize();
+ void o100_getSpriteGroupInfo();
+ void o100_getPaletteData();
+ void o100_readFile();
+ void o100_getSpriteInfo();
+ void o100_getWizData();
+ void o100_getVideoData();
+};
+#endif
+
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/logic_he.cpp b/engines/scumm/logic_he.cpp
new file mode 100644
index 0000000000..76de561721
--- /dev/null
+++ b/engines/scumm/logic_he.cpp
@@ -0,0 +1,772 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/intern_he.h"
+#include "scumm/logic_he.h"
+
+namespace Scumm {
+
+LogicHE::LogicHE(ScummEngine_v90he *vm) : _vm(vm) {
+ // Originally it used 0x930 and stored both floats and doubles inside
+ _userData = (float *)calloc(550, sizeof(float));
+ _userDataD = (double *)calloc(30, sizeof(double));
+}
+
+LogicHE::~LogicHE() {
+ free(_userData);
+ free(_userDataD);
+}
+
+void LogicHE::writeScummVar(int var, int32 value) {
+ _vm->writeVar(var, value);
+}
+
+static int32 scumm_round(double arg) {
+ return (int32)(arg + 0.5);
+}
+
+int LogicHE::versionID() {
+ return 1;
+}
+
+int LogicHE::getFromArray(int arg0, int idx2, int idx1) {
+ _vm->VAR(_vm->VAR_U32_ARRAY_UNK) = arg0;
+ return _vm->readArray(116, idx2, idx1);
+}
+
+void LogicHE::putInArray(int arg0, int idx2, int idx1, int val) {
+ _vm->VAR(_vm->VAR_U32_ARRAY_UNK) = arg0;
+ _vm->writeArray(116, idx2, idx1, val);
+}
+
+int32 LogicHE::dispatch(int op, int numArgs, int32 *args) {
+#if 1
+ char tmp[32], str[256];
+
+ if (numArgs > 0)
+ snprintf(tmp, 32, "%d", args[0]);
+ else
+ *tmp = 0;
+
+ snprintf(str, 256, "LogicHE::dispatch(%d, %d, [%s", op, numArgs, tmp);
+
+ for (int i = 1; i < numArgs; i++) {
+ snprintf(tmp, 32, ", %d", args[i]);
+ strncat(str, tmp, 256);
+ }
+ strncat(str, "])", 256);
+
+ debug(0, str);
+#else
+ // Used for parallel trace utility
+ for (int i = 0; i < numArgs; i++)
+ debug(0, "args[%d] = %d;", i, args[i]);
+
+ debug(0, "dispatch(%d, %d, args);", op, numArgs);
+
+#endif
+
+ return 1;
+}
+
+/***********************
+ * Putt-Putt Joins the Race
+ *
+ */
+
+int LogicHErace::versionID() {
+ return 1;
+}
+
+int32 LogicHErace::dispatch(int op, int numArgs, int32 *args) {
+ int32 res;
+
+ switch (op) {
+ case 1003:
+ res = op_1003(args);
+ break;
+
+ case 1004:
+ res = op_1004(args);
+ break;
+
+ case 1100:
+ res = op_1100(args);
+ break;
+
+ case 1101:
+ res = op_1101(args);
+ break;
+
+ case 1102:
+ res = op_1102(args);
+ break;
+
+ case 1103:
+ res = op_1103(args);
+ break;
+
+ case 1110:
+ res = op_1110();
+ break;
+
+ case 1120:
+ res = op_1120(args);
+ break;
+
+ case 1130:
+ res = op_1130(args);
+ break;
+
+ case 1140:
+ res = op_1140(args);
+ break;
+
+ default:
+ res = 0;
+ break;
+
+ }
+
+ return res;
+}
+
+#define RAD2DEG (180 / PI)
+#define DEG2RAD (PI / 180)
+
+int32 LogicHErace::op_1003(int32 *args) {
+ int value = args[2] ? args[2] : 1;
+
+ writeScummVar(108, (int32)(atan2((double)args[0], (double)args[1]) * RAD2DEG * value));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1004(int32 *args) {
+ int value = args[1] ? args[1] : 1;
+
+ writeScummVar(108, (int32)(sqrt((float)args[0]) * value));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1100(int32 *args) {
+ _userData[516] = (float)args[0] / args[10];
+ _userData[517] = (float)args[1] / args[10];
+ _userData[518] = (float)args[2] / args[10];
+ _userData[519] = (float)args[3] / args[10];
+ _userData[520] = (float)args[4] / args[10];
+
+ op_sub1(_userData[520]);
+
+ _userData[521] = (float)args[5] / args[10];
+
+ op_sub2(_userData[521]);
+
+ _userData[532] = (float)args[10];
+
+ _userData[524] = (float)args[8];
+ _userData[525] = (float)args[9];
+ _userData[522] = (float)args[6] / args[10];
+ _userData[523] = (float)args[7] / args[10];
+ _userData[526] = (float)args[6] / args[8] / args[10];
+ _userData[527] = (float)args[7] / args[9] / args[10];
+
+ writeScummVar(108, (int32)((float)args[6] / args[8] * args[10]));
+
+ writeScummVar(109, (int32)((float)args[7] / args[9] * args[10]));
+
+ _userData[528] = (float)(_userData[519] - _userData[523] * 0.5);
+ _userData[529] = (float)(_userData[519] + _userData[523] * 0.5);
+
+ writeScummVar(110, (int32)(_userData[528] * args[10]));
+ writeScummVar(111, (int32)(_userData[529] * args[10]));
+
+ _userData[530] = (float)(_userData[517] / tan(_userData[529] * DEG2RAD));
+ _userData[531] = (float)(_userData[517] / tan(_userData[528] * DEG2RAD));
+
+ writeScummVar(112, (int32)(_userData[530] * args[10]));
+ writeScummVar(113, (int32)(_userData[531] * args[10]));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1101(int32 *args) {
+ int32 retval;
+ float temp;
+
+ temp = args[0] / _userData[532];
+
+ if (_userData[519] == temp) {
+ retval = (int32)temp;
+ } else {
+ _userData[519] = temp;
+ op_sub3(temp);
+ retval = 1;
+ }
+
+ temp = args[1] / _userData[532];
+
+ if (_userData[520] != temp) {
+ _userData[520] = temp;
+ op_sub1(temp);
+ retval = 1;
+ }
+
+ temp = args[2] / _userData[532];
+
+ if (_userData[521] != temp) {
+ _userData[521] = temp;
+ op_sub2(temp);
+ retval = 1;
+ }
+
+ return retval;
+}
+
+int32 LogicHErace::op_1102(int32 *args) {
+ int32 retval;
+ float temp;
+
+ temp = args[0] / _userData[532];
+ if (_userData[516] != temp) {
+ _userData[516] = temp;
+ retval = 1;
+ } else {
+ retval = (int32)_userData[532];
+ }
+
+ temp = args[1] / _userData[532];
+ if (_userData[517] != temp) {
+ _userData[517] = temp;
+ retval = 1;
+ }
+
+ temp = args[2] / _userData[532];
+ if (_userData[518] != temp) {
+ _userData[518] = temp;
+ retval = 1;
+ }
+
+ return retval;
+}
+
+int32 LogicHErace::op_1103(int32 *args) {
+ double angle = args[0] / args[1] * DEG2RAD;
+
+ writeScummVar(108, (int32)(sin(angle) * args[2]));
+ writeScummVar(109, (int32)(cos(angle) * args[2]));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1110() {
+ writeScummVar(108, (int32)(_userData[526] * _userData[532] * _userData[532]));
+ writeScummVar(109, (int32)(_userData[527] * _userData[532] * _userData[532]));
+ writeScummVar(110, (int32)(_userData[532]));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1120(int32 *args) {
+ double a0, a1, a2, expr;
+ double res1, res2;
+
+ a0 = args[0] / _userData[532] - _userData[516];
+ a1 = args[1] / _userData[532] - _userData[517];
+ a2 = args[2] / _userData[532] - _userData[518];
+
+ expr = a2 * _userDataD[17] + a1 * _userDataD[14] + a0 * _userDataD[11];
+
+ res1 = (atan2(a2 * _userDataD[15] + a1 * _userDataD[12] + a0 * _userDataD[9], expr) * RAD2DEG)
+ / _userData[526];
+ res2 = (atan2(a2 * _userDataD[16] + a1 * _userDataD[13] + a0 * _userDataD[10], expr) * RAD2DEG
+ - _userData[528]) / _userData[527];
+
+ writeScummVar(108, (int32)res1);
+ writeScummVar(109, (int32)res2);
+
+ return 1;
+}
+
+int32 LogicHErace::op_1130(int32 *args) {
+ double cs = cos(args[0] / _userData[532] * DEG2RAD);
+ double sn = sin(args[0] / _userData[532] * DEG2RAD);
+
+ writeScummVar(108, (int32)(cs * args[1] + sn * args[2]));
+
+ writeScummVar(109, (int32)(cs * args[2] - sn * args[1]));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1140(int32 *args) {
+ double arg2 = -args[2] * args[2];
+ double arg3 = -args[3] * args[3];
+ double sq = sqrt(arg2 + arg3);
+ double res;
+
+ arg2 = arg2 / sq;
+ arg3 = arg3 / sq;
+
+ res = (args[0] - 2 * (arg2 * args[0] + arg3 * args[1]) * arg2) * 0.86956525;
+
+ writeScummVar(108, (int32)res);
+
+ res = args[1] - 2 * (arg2 * args[0] + arg3 * args[1]) * arg3;
+
+ if (-args[3] * args[3] >= 0)
+ res *= 0.83333331f;
+
+ writeScummVar(109, (int32)res);
+
+ return 1;
+}
+
+void LogicHErace::op_sub1(float arg) {
+ _userDataD[10] = _userDataD[12] = _userDataD[14] = _userDataD[16] = 0;
+ _userDataD[13] = 1;
+
+ _userDataD[9] = cos(arg * DEG2RAD);
+ _userDataD[15] = sin(arg * DEG2RAD);
+ _userDataD[11] = -_userDataD[15];
+ _userDataD[17] = _userDataD[9];
+}
+
+void LogicHErace::op_sub2(float arg) {
+ _userDataD[20] = _userDataD[21] = _userDataD[24] = _userDataD[25] = 0;
+ _userDataD[26] = 1;
+
+ _userDataD[19] = sin(arg * DEG2RAD);
+ _userDataD[18] = cos(arg * DEG2RAD);
+ _userDataD[21] = -_userDataD[19];
+ _userDataD[22] = _userDataD[18];
+}
+
+void LogicHErace::op_sub3(float arg) {
+ _userDataD[1] = _userDataD[2] = _userDataD[3] = _userDataD[6] = 0;
+ _userDataD[0] = 1;
+
+ _userDataD[4] = cos(arg * DEG2RAD);
+ _userDataD[5] = sin(arg * DEG2RAD);
+ _userDataD[7] = -_userDataD[5];
+ _userDataD[8] = _userDataD[4];
+}
+
+/***********************
+ * Freddi Fish's One-Stop Fun Shop
+ * Pajama Sam's One-Stop Fun Shop
+ * Putt-Putt's One-Stop Fun Shop
+ *
+ */
+
+int LogicHEfunshop::versionID() {
+ return 1;
+}
+
+int32 LogicHEfunshop::dispatch(int op, int numArgs, int32 *args) {
+ switch (op) {
+ case 1004:
+ op_1004(args);
+ break;
+
+ case 1005:
+ op_1005(args);
+ break;
+
+ default:
+ break;
+
+ }
+
+ return 0;
+}
+
+void LogicHEfunshop::op_1004(int32 *args) {
+ double data[8], at, sq;
+ int32 x, y;
+ int i=0;
+
+ for (i = 0; i <= 6; i += 2) {
+ data[i] = getFromArray(args[0], 0, 519 + i);
+ data[i + 1] = getFromArray(args[0], 0, 519 + i + 1);
+ }
+ int s = checkShape((int32)data[0], (int32)data[1], (int32)data[4], (int32)data[5],
+ (int32)data[2], (int32)data[3], (int32)data[6], (int32)data[7], &x, &y);
+
+ if (s != 1) {
+ error("LogicHEfunshop::op_1004: Your shape has defied the laws of physics\n");
+ return;
+ }
+
+ for (i = 0; i <= 6; i += 2) {
+ data[i] -= (double)x;
+ data[i + 1] -= (double)y;
+ }
+
+ double a1 = (double)args[1] * DEG2RAD;
+
+ for (i = 0; i <= 6; i += 2) {
+ at = atan2(data[i + 1], data[i]);
+ sq = sqrt(data[i + 1] * data[i + 1] + data[i] * data[i]);
+
+ if (at <= 0)
+ at += 2 * PI;
+
+ data[i] = cos(at + a1) * sq;
+ data[i + 1] = sin(at + a1) * sq;
+ }
+
+ int minx = 2;
+ int miny = 3;
+
+ for (i = 0; i <= 6; i += 2) {
+ if (data[i] < data[minx])
+ minx = i;
+ if (data[i + 1] < data[miny])
+ miny = i + 1;
+ }
+
+ for (i = 0; i <= 6; i += 2) {
+ data[i] -= data[minx];
+ data[i + 1] -= data[miny];
+
+ putInArray(args[0], 0, 519 + i, scumm_round(data[i]));
+ putInArray(args[0], 0, 519 + i + 1, scumm_round(data[i + 1]));
+ }
+}
+
+void LogicHEfunshop::op_1005(int32 *args) {
+ double data[8];
+ double args1, args2;
+ int i=0;
+ for (i = 520; i <= 526; i += 2) {
+ data[i - 520] = getFromArray(args[0], 0, i - 1);
+ data[i - 520 + 1] = getFromArray(args[0], 0, i);
+ }
+
+ args1 = args[1] * 0.01 + 1;
+ args2 = args[2] * 0.01 + 1;
+
+ for (i = 0; i < 4; i++) {
+ data[2 * i] *= args1;
+ data[2 * i + 1] *= args2;
+ }
+
+ for (i = 520; i <= 526; i += 2) {
+ putInArray(args[0], 0, i - 1, scumm_round(data[i - 520]));
+ putInArray(args[0], 0, i, scumm_round(data[i - 520 + 1]));
+ }
+}
+
+int LogicHEfunshop::checkShape(int32 data0, int32 data1, int32 data4, int32 data5, int32 data2, int32 data3, int32 data6, int32 data7, int32 *x, int32 *y) {
+ int32 diff5_1, diff0_4, diff7_3, diff2_6;
+ int32 diff1, diff2;
+ int32 delta, delta2;
+ int32 sum1, sum2;
+
+ diff0_4 = data0 - data4;
+ diff5_1 = data5 - data1;
+ diff1 = data1 * data4 - data0 * data5;
+ sum1 = diff0_4 * data3 + diff1 + diff5_1 * data2;
+ sum2 = diff0_4 * data7 + diff1 + diff5_1 * data6;
+
+ if (sum1 != 0 && sum2 != 0) {
+ sum2 ^= sum1;
+
+ if (sum2 >= 0)
+ return 0;
+ }
+
+ diff2_6 = data2 - data6;
+ diff7_3 = data7 - data3;
+ diff2 = data3 * data6 - data2 * data7;
+ sum1 = diff2_6 * data1 + diff2 + diff7_3 * data0;
+ sum2 = diff2_6 * data5 + diff2 + diff7_3 * data4;;
+
+ if (sum1 != 0 && sum2 != 0) {
+ sum2 ^= sum1;
+
+ if (sum2 >= 0)
+ return 0;
+ }
+
+ delta = diff2_6 * diff5_1 - diff0_4 * diff7_3;
+
+ if (delta == 0) {
+ return 2;
+ }
+
+ if (delta < 0) {
+ data7 = -((delta + 1) >> 1);
+ } else {
+ data7 = delta >> 1;
+ }
+
+ delta2 = diff2 * diff0_4 - diff1 * diff2_6;
+
+ if (delta2 < 0) {
+ delta2 -= data7;
+ } else {
+ delta2 += data7;
+ }
+
+ *x = delta2 / delta;
+
+ delta2 = diff1 * diff7_3 - diff2 * diff5_1;
+
+ if (delta2 < 0) {
+ delta2 -= data7;
+ } else {
+ delta2 += data7;
+ }
+
+ *y = delta2 / delta;
+
+ return 1;
+}
+
+/***********************
+ * Backyard Football
+ * Backyard Football Demo
+ *
+ */
+
+int LogicHEfootball::versionID() {
+ return 1;
+}
+
+int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
+ int res = 0;
+
+ switch (op) {
+ case 1004:
+ res = op_1004(args);
+ break;
+
+ case 1006:
+ res = op_1006(args);
+ break;
+
+ case 1007:
+ res = op_1007(args);
+ break;
+
+ case 1010:
+ res = op_1010(args);
+ break;
+
+ case 1022:
+ res = op_1022(args);
+ break;
+
+ case 1023:
+ res = op_1023(args);
+ break;
+
+ case 1024:
+ res = op_1024(args);
+ break;
+
+ case 8221968:
+ // Someone had a fun and used his birthday as opcode number
+ res = getFromArray(args[0], args[1], args[2]);
+ break;
+
+ case 1492: case 1493: case 1494: case 1495: case 1496:
+ case 1497: case 1498: case 1499: case 1500: case 1501:
+ case 1502: case 1503: case 1504: case 1505: case 1506:
+ case 1507: case 1508: case 1509: case 1510: case 1511:
+ case 1512: case 1513: case 1514: case 1555:
+ // DirectPlay-related
+ // 1513: initialize
+ // 1555: set fake lag
+ break;
+
+ case 2200: case 2201: case 2202: case 2203: case 2204:
+ case 2205: case 2206: case 2207: case 2208: case 2209:
+ case 2210: case 2211: case 2212: case 2213: case 2214:
+ case 2215: case 2216: case 2217: case 2218: case 2219:
+ case 2220: case 2221: case 2222: case 2223: case 2224:
+ case 2225: case 2226: case 2227: case 2228:
+ // Boneyards-related
+ break;
+
+ case 3000: case 3001: case 3002: case 3003: case 3004:
+ // Internet-related
+ // 3000: check for updates
+ // 3001: check network status
+ // 3002: autoupdate
+ // 3003: close connection
+ break;
+
+ default:
+ LogicHE::dispatch(op, numArgs, args);
+ error("Tell me how to reproduce it");
+ }
+
+ return res;
+}
+
+int LogicHEfootball::op_1004(int32 *args) {
+ double res, a2, a4, a5;
+
+ a5 = ((double)args[4] - (double)args[1]) / ((double)args[5] - (double)args[2]);
+ a4 = ((double)args[3] - (double)args[0]) / ((double)args[5] - (double)args[2]);
+ a2 = (double)args[2] - (double)args[0] * a4 - args[1] * a5;
+
+ res = (double)args[6] * a4 + (double)args[7] * a5 + a2;
+ writeScummVar(108, (int32)res);
+
+ writeScummVar(109, (int32)a2);
+ writeScummVar(110, (int32)a5);
+ writeScummVar(111, (int32)a4);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1006(int32 *args) {
+ double res;
+
+ res = (1.0 - args[1] * 2.9411764e-4 * 5.3050399e-2) * args[0] * 1.2360656e-1 +
+ args[1] * 1.1764706e-2 + 46;
+ writeScummVar(108, (int32)res);
+
+ res = 640.0 - args[2] * 1.2360656e-1 - args[1] * 1.1588235e-1 - 26;
+ writeScummVar(109, (int32)res);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1007(int32 *args) {
+ double res, temp;
+
+ temp = (double)args[1] * 0.32;
+
+ if (temp > 304.0)
+ res = -args[2] * 0.142;
+ else
+ res = args[2] * 0.142;
+
+ res += temp;
+
+ writeScummVar(108, (int32)res);
+
+ res = (1000.0 - args[2]) * 0.48;
+
+ writeScummVar(109, (int32)res);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1010(int32 *args) {
+ double a1 = (640.0 - (double)args[1] - 26.0) * 8.6294413;
+ double res;
+
+ res = ((double)args[0] - 46 - a1 * 1.1764706e-2) /
+ ((1.0 - a1 * 2.9411764e-4 * 5.3050399e-2) * 1.2360656e-1);
+ writeScummVar(108, (int32)res);
+
+ writeScummVar(109, (int32)a1);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1022(int32 *args) {
+ double res;
+ double var10 = args[4] - args[1];
+ double var8 = args[5] - args[2];
+ double var6 = args[3] - args[0];
+
+ res = sqrt(var8 * var8 + var6 * var6 + var10 * var10);
+
+ if (res >= (double)args[6]) {
+ var8 = (double)args[6] * var8 / res;
+ var10 = (double)args[6] * var10 / res;
+ res = (double)args[6] * var6 / res;
+ }
+
+ writeScummVar(108, (int32)res);
+ writeScummVar(109, (int32)var10);
+ writeScummVar(110, (int32)var8);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1023(int32 *args) {
+ double var10, var18, var20, var28, var30, var30_;
+ double argf[7];
+
+ for (int i = 0; i < 7; i++)
+ argf[i] = args[i];
+
+ var10 = (argf[3] - argf[1]) / (argf[2] - argf[0]);
+ var28 = var10 * var10 + 1;
+ var20 = argf[0] * var10;
+ var18 = (argf[5] + argf[1] + var20) * argf[4] * var10 * 2 +
+ argf[6] * argf[6] * var28 + argf[4] * argf[4] -
+ argf[0] * argf[0] * var10 * var10 -
+ argf[5] * argf[0] * var10 * 2 -
+ argf[5] * argf[1] * 2 -
+ argf[1] * argf[1] - argf[5] * argf[5];
+
+ if (var18 >= 0) {
+ var18 = sqrt(var18);
+
+ var30_ = argf[4] + argf[5] * var10 + argf[1] * var10 + argf[0] * var10 * var10;
+ var30 = (var30_ - var18) / var28;
+ var18 = (var30_ + var18) / var28;
+
+ if ((argf[0] - var30 < 0) && (argf[0] - var18 < 0)) {
+ var30_ = var30;
+ var30 = var18;
+ var18 = var30_;
+ }
+ var28 = var18 * var10 - var20 - argf[1];
+ var20 = var30 * var10 - var20 - argf[1];
+ } else {
+ var18 = 0;
+ var20 = 0;
+ var28 = 0;
+ var30 = 0;
+ }
+
+ writeScummVar(108, (int32)var18);
+ writeScummVar(109, (int32)var28);
+ writeScummVar(110, (int32)var30);
+ writeScummVar(111, (int32)var20);
+
+ return 1;
+}
+int LogicHEfootball::op_1024(int32 *args) {
+ writeScummVar(108, 0);
+ writeScummVar(109, 0);
+ writeScummVar(110, 0);
+ writeScummVar(111, 0);
+
+ return 1;
+}
+
+
+} // End of namespace Scumm
diff --git a/engines/scumm/logic_he.h b/engines/scumm/logic_he.h
new file mode 100644
index 0000000000..c237f6182c
--- /dev/null
+++ b/engines/scumm/logic_he.h
@@ -0,0 +1,111 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(LOGIC_HE_H) && !defined(DISABLE_HE)
+#define LOGIC_HE_H
+
+#include "common/stdafx.h"
+
+namespace Scumm {
+
+class ScummEngine_v90he;
+
+class LogicHE {
+public:
+ float *_userData;
+ double *_userDataD;
+ ScummEngine_v90he *_vm;
+
+ LogicHE(ScummEngine_v90he *vm);
+ virtual ~LogicHE();
+
+ void writeScummVar(int var, int32 value);
+ int getFromArray(int arg0, int idx2, int idx1);
+ void putInArray(int arg0, int idx2, int idx1, int val);
+
+ void beforeBootScript(void) {};
+ void initOnce() {};
+ void startOfFrame() {};
+ void endOfFrame() {};
+ void processKeyStroke(int keyPressed) {};
+
+ virtual int versionID();
+ virtual int32 dispatch(int op, int numArgs, int32 *args);
+};
+
+class LogicHErace : public LogicHE {
+public:
+ LogicHErace(ScummEngine_v90he *vm) : LogicHE(vm) {}
+
+ int versionID();
+ int32 dispatch(int op, int numArgs, int32 *args);
+
+private:
+ int32 op_1003(int32 *args);
+ int32 op_1004(int32 *args);
+ int32 op_1100(int32 *args);
+ int32 op_1101(int32 *args);
+ int32 op_1102(int32 *args);
+ int32 op_1103(int32 *args);
+ int32 op_1110();
+ int32 op_1120(int32 *args);
+ int32 op_1130(int32 *args);
+ int32 op_1140(int32 *args);
+
+ void op_sub1(float arg);
+ void op_sub2(float arg);
+ void op_sub3(float arg);
+};
+
+class LogicHEfunshop : public LogicHE {
+public:
+ LogicHEfunshop(ScummEngine_v90he *vm) : LogicHE(vm) {}
+
+ int versionID();
+ int32 dispatch(int op, int numArgs, int32 *args);
+
+private:
+ void op_1004(int32 *args);
+ void op_1005(int32 *args);
+ int checkShape(int32 data0, int32 data1, int32 data4, int32 data5, int32 data2, int32 data3, int32 data6, int32 data7, int32 *x, int32 *y);
+};
+
+class LogicHEfootball : public LogicHE {
+public:
+ LogicHEfootball(ScummEngine_v90he *vm) : LogicHE(vm) {}
+
+ int versionID();
+ int32 dispatch(int op, int numArgs, int32 *args);
+
+private:
+ int op_1004(int32 *args);
+ int op_1006(int32 *args);
+ int op_1007(int32 *args);
+ int op_1010(int32 *args);
+ int op_1022(int32 *args);
+ int op_1023(int32 *args);
+ int op_1024(int32 *args);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/midiparser_eup.cpp b/engines/scumm/midiparser_eup.cpp
new file mode 100644
index 0000000000..a9c40f99c5
--- /dev/null
+++ b/engines/scumm/midiparser_eup.cpp
@@ -0,0 +1,219 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sound/midiparser.h"
+#include "sound/mididrv.h"
+#include "common/util.h"
+
+namespace Scumm {
+
+/**
+ * The FM-TOWNS Euphony version of MidiParser.
+ */
+class MidiParser_EUP : public MidiParser {
+protected:
+ byte _instruments[6][50]; // Two extra bytes for SysEx ID and channel #
+ byte *_instr_to_channel;
+ struct {
+ byte *enable;
+ int8 *channel;
+ int8 *volume;
+ int8 *transpose;
+ } _presets;
+ bool _loop;
+ byte _presend; // Tracks which startup implied events have been sent.
+ uint32 _base_tick; // Events times are relative to this base.
+
+protected:
+ void parseNextEvent (EventInfo &info);
+ void resetTracking();
+
+public:
+ bool loadMusic (byte *data, uint32 size);
+};
+
+
+
+//////////////////////////////////////////////////
+//
+// MidiParser_EUP implementation
+//
+//////////////////////////////////////////////////
+
+void MidiParser_EUP::parseNextEvent (EventInfo &info) {
+ byte *pos = _position._play_pos;
+
+ // FIXME: The presend is for sending init events
+ // that aren't actually in the stream. This would
+ // be for, e.g., instrument setup. Right now, we
+ // don't actually use the instruments specified
+ // in the music header. We're sending fixed GM
+ // program changes to get a reasonable "one-size-
+ // fits-all" sound until we actually support the
+ // FM synthesis capabilities of FM-TOWNS.
+ for (; _presend < 12; ++_presend) {
+ if (_instr_to_channel[_presend >> 1] >= 16)
+ continue;
+ info.start = pos;
+ info.delta = 0;
+ if (_presend & 1) {
+ byte *data = &_instruments[_presend >> 1][0];
+ data[1] = _instr_to_channel[_presend >> 1];
+ info.event = 0xF0;
+ info.ext.data = data;
+ info.length = 48;
+ } else {
+ info.event = 0xB0 | (_presend >> 1);
+ info.basic.param1 = 121;
+ info.basic.param2 = 0;
+ }
+ ++_presend;
+ return;
+ }
+
+ while (true) {
+ byte cmd = *pos;
+ if ((cmd & 0xF0) == 0x90) {
+ byte preset = pos[1];
+ byte channel = _presets.channel[preset];
+ if (channel >= 16)
+ channel = cmd & 0x0F;
+ uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _base_tick;
+ int note = (int) pos[4] + _presets.transpose[preset];
+ int volume = (int) pos[5];
+ // HACK: Loom-Towns distaff tracks seem to
+ // contain zero-volume note events, so change
+ // those to full volume.
+ if (!volume)
+ volume = 127;
+ volume += _presets.volume[preset];
+ if (volume > 127)
+ volume = 127;
+ else if (volume < 0)
+ volume = 0;
+ pos += 6;
+ if (_presets.enable[preset]) {
+ uint16 duration = pos[1] | (pos[2] << 4);
+ info.start = pos;
+ uint32 last = _position._last_event_tick;
+ info.delta = (tick < last) ? 0 : (tick - last);
+ info.event = 0x90 | channel;
+ info.length = duration;
+ info.basic.param1 = note;
+ info.basic.param2 = volume;
+ pos += 6;
+ break;
+ }
+ pos += 6;
+ } else if (cmd == 0xF2) {
+ // This is a "measure marker" of sorts.
+ // It advances the "base time", to which
+ // all event times are relative.
+ _base_tick += (pos[3] << 7) | pos[2];
+ pos += 6;
+ } else if (cmd == 0xF8) {
+ // TODO: Implement this.
+ pos += 6;
+ } else if (cmd == 0xFD || cmd == 0xFE) {
+ // End of track.
+ if (_loop && false) {
+ // TODO: Implement this.
+ } else {
+ info.start = pos;
+ uint32 last = _position._last_event_tick;
+ info.delta = (_base_tick < last) ? 0 : (_base_tick - last);
+ info.event = 0xFF;
+ info.length = 0;
+ info.ext.type = 0x2F;
+ info.ext.data = pos;
+ break;
+ }
+ } else {
+ error("Unknown Euphony music event 0x%02X", (int) cmd);
+ memset(&info, 0, sizeof(info));
+ pos = 0;
+ break;
+ }
+ }
+ _position._play_pos = pos;
+}
+
+bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
+ unloadMusic();
+ byte *pos = data;
+ int i;
+
+ if (memcmp(pos, "SO", 2)) {
+ error("'SO' header expected but found '%c%c' instead.", pos[0], pos[1]);
+ return false;
+ }
+
+ byte numInstruments = pos[16];
+ pos += 16 + 2;
+ for (i = 0; i < numInstruments; ++i) {
+ _instruments[i][0] = 0x7C;
+ memcpy (&_instruments[i][2], pos, 48);
+ pos += 48;
+ }
+
+ // Load the prest pointers
+ _presets.enable = pos;
+ pos += 32;
+ _presets.channel = (int8 *) pos;
+ pos += 32;
+ _presets.volume = (int8 *) pos;
+ pos += 32;
+ _presets.transpose = (int8 *) pos;
+ pos += 32;
+
+ pos += 8; // Unknown bytes
+ _instr_to_channel = pos; // Instrument-to-channel mapping
+ pos += 6;
+ pos += 4; // Skip the music size for now.
+ pos++; // Unknown byte
+ byte tempo = *pos++;
+ _loop = (*pos++ != 1);
+ pos++; // Unknown byte
+
+ _num_tracks = 1;
+ _ppqn = 120;
+ _tracks[0] = pos;
+
+ // Note that we assume the original data passed in
+ // will persist beyond this call, i.e. we do NOT
+ // copy the data to our own buffer. Take warning....
+ resetTracking();
+ setTempo (1000000 * 60 / tempo);
+ setTrack (0);
+ return true;
+}
+
+void MidiParser_EUP::resetTracking() {
+ MidiParser::resetTracking();
+ _presend = 0;
+ _base_tick = 0;
+}
+
+MidiParser *MidiParser_createEUP() { return new MidiParser_EUP; }
+
+} // End of namespace Scumm
diff --git a/engines/scumm/midiparser_ro.cpp b/engines/scumm/midiparser_ro.cpp
new file mode 100644
index 0000000000..b240ffb5fd
--- /dev/null
+++ b/engines/scumm/midiparser_ro.cpp
@@ -0,0 +1,143 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sound/midiparser.h"
+#include "sound/mididrv.h"
+#include "common/util.h"
+
+namespace Scumm {
+
+//////////////////////////////////////////////////
+//
+// The Standard MIDI File version of MidiParser
+//
+//////////////////////////////////////////////////
+
+class MidiParser_RO : public MidiParser {
+protected:
+ int _markerCount; // Number of markers encountered in stream so far
+ int _lastMarkerCount; // Cache markers until parsed event is actually consumed
+
+protected:
+ void compressToType0();
+ void parseNextEvent (EventInfo &info);
+
+public:
+ bool loadMusic (byte *data, uint32 size);
+ uint32 getTick() { return (uint32) _markerCount * _ppqn / 2; }
+};
+
+
+
+//////////////////////////////////////////////////
+//
+// MidiParser_RO implementation
+//
+//////////////////////////////////////////////////
+
+void MidiParser_RO::parseNextEvent (EventInfo &info) {
+ _markerCount += _lastMarkerCount;
+ _lastMarkerCount = 0;
+
+ info.delta = 0;
+ do {
+ info.start = _position._play_pos;
+ info.event = *(_position._play_pos++);
+ if (info.command() == 0xA) {
+ ++_lastMarkerCount;
+ info.event = 0xF0;
+ } else if (info.event == 0xF0) {
+ byte delay = *(_position._play_pos++);
+ info.delta += delay;
+ continue;
+ }
+ break;
+ } while (true);
+
+ // Seems to indicate EOT
+ if (info.event == 0) {
+ info.event = 0xFF;
+ info.ext.type = 0x2F;
+ info.length = 0;
+ info.ext.data = 0;
+ return;
+ }
+
+ if (info.event < 0x80)
+ return;
+
+ _position._running_status = info.event;
+ switch (info.command()) {
+ case 0xC:
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = 0;
+ break;
+
+ case 0x8: case 0x9: case 0xB:
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++);
+ if (info.command() == 0x9 && info.basic.param2 == 0)
+ info.event = info.channel() | 0x80;
+ info.length = 0;
+ break;
+
+ case 0xF: // Marker and EOT messages
+ info.length = 0;
+ info.ext.data = 0;
+ if (info.event == 0xFF) {
+ _autoLoop = true;
+ info.ext.type = 0x2F;
+ } else {
+ info.ext.type = 0x7F; // Bogus META
+ }
+ info.event = 0xFF;
+ break;
+ }
+}
+
+bool MidiParser_RO::loadMusic (byte *data, uint32 size) {
+ unloadMusic();
+ byte *pos = data;
+
+ if (memcmp (pos, "RO", 2)) {
+ error("'RO' header expected but found '%c%c' instead", pos[0], pos[1]);
+ return false;
+ }
+
+ _num_tracks = 1;
+ _ppqn = 120;
+ _tracks[0] = pos + 2;
+ _markerCount = _lastMarkerCount = 0;
+
+ // Note that we assume the original data passed in
+ // will persist beyond this call, i.e. we do NOT
+ // copy the data to our own buffer. Take warning....
+ resetTracking();
+ setTempo (500000);
+ setTrack (0);
+ return true;
+}
+
+MidiParser *MidiParser_createRO() { return new MidiParser_RO; }
+
+} // End of namespace Scumm
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
new file mode 100644
index 0000000000..6bf42512b3
--- /dev/null
+++ b/engines/scumm/module.mk
@@ -0,0 +1,110 @@
+MODULE := engines/scumm
+
+MODULE_OBJS := \
+ engines/scumm/actor.o \
+ engines/scumm/akos.o \
+ engines/scumm/base-costume.o \
+ engines/scumm/bomp.o \
+ engines/scumm/boxes.o \
+ engines/scumm/camera.o \
+ engines/scumm/charset.o \
+ engines/scumm/costume.o \
+ engines/scumm/cursor.o \
+ engines/scumm/debugger.o \
+ engines/scumm/dialogs.o \
+ engines/scumm/gfx.o \
+ engines/scumm/imuse.o \
+ engines/scumm/imuse_player.o \
+ engines/scumm/input.o \
+ engines/scumm/instrument.o \
+ engines/scumm/help.o \
+ engines/scumm/midiparser_ro.o \
+ engines/scumm/midiparser_eup.o \
+ engines/scumm/object.o \
+ engines/scumm/palette.o \
+ engines/scumm/player_mod.o \
+ engines/scumm/player_v1.o \
+ engines/scumm/player_nes.o \
+ engines/scumm/player_v2.o \
+ engines/scumm/player_v2a.o \
+ engines/scumm/player_v3a.o \
+ engines/scumm/resource.o \
+ engines/scumm/resource_v2.o \
+ engines/scumm/resource_v3.o \
+ engines/scumm/resource_v4.o \
+ engines/scumm/room.o \
+ engines/scumm/saveload.o \
+ engines/scumm/script.o \
+ engines/scumm/script_c64.o \
+ engines/scumm/script_v2.o \
+ engines/scumm/script_v5.o \
+ engines/scumm/script_v6.o \
+ engines/scumm/script_v6he.o \
+ engines/scumm/scumm.o \
+ engines/scumm/sound.o \
+ engines/scumm/sound_he.o \
+ engines/scumm/string.o \
+ engines/scumm/usage_bits.o \
+ engines/scumm/util.o \
+ engines/scumm/vars.o \
+ engines/scumm/verbs.o \
+ engines/scumm/thumbnail.o
+
+ifndef DISABLE_SCUMM_7_8
+MODULE_OBJS += \
+ engines/scumm/nut_renderer.o \
+ engines/scumm/script_v8.o \
+ engines/scumm/imuse_digi/dimuse.o \
+ engines/scumm/imuse_digi/dimuse_bndmgr.o \
+ engines/scumm/imuse_digi/dimuse_codecs.o \
+ engines/scumm/imuse_digi/dimuse_music.o \
+ engines/scumm/imuse_digi/dimuse_sndmgr.o \
+ engines/scumm/imuse_digi/dimuse_script.o \
+ engines/scumm/imuse_digi/dimuse_track.o \
+ engines/scumm/imuse_digi/dimuse_tables.o \
+ engines/scumm/insane/insane.o \
+ engines/scumm/insane/insane_ben.o \
+ engines/scumm/insane/insane_enemy.o \
+ engines/scumm/insane/insane_scenes.o \
+ engines/scumm/insane/insane_iact.o \
+ engines/scumm/smush/chunk.o \
+ engines/scumm/smush/codec1.o \
+ engines/scumm/smush/codec37.o \
+ engines/scumm/smush/codec47.o \
+ engines/scumm/smush/imuse_channel.o \
+ engines/scumm/smush/smush_player.o \
+ engines/scumm/smush/saud_channel.o \
+ engines/scumm/smush/smush_mixer.o \
+ engines/scumm/smush/smush_font.o
+endif
+
+ifndef DISABLE_HE
+MODULE_OBJS += \
+ engines/scumm/floodfill_he.o \
+ engines/scumm/logic_he.o \
+ engines/scumm/palette_he.o \
+ engines/scumm/resource_v7he.o \
+ engines/scumm/script_v7he.o \
+ engines/scumm/script_v72he.o \
+ engines/scumm/script_v80he.o \
+ engines/scumm/script_v90he.o \
+ engines/scumm/script_v100he.o \
+ engines/scumm/sprite_he.o \
+ engines/scumm/wiz_he.o
+endif
+
+MODULE_DIRS += \
+ engines/scumm \
+ engines/scumm/imuse_digi \
+ engines/scumm/insane \
+ engines/scumm/smush
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+# HACK HACK evil HACK HACK
+PLUGIN_LDFLAGS += -lz
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/scumm/music.h b/engines/scumm/music.h
new file mode 100644
index 0000000000..b837fced51
--- /dev/null
+++ b/engines/scumm/music.h
@@ -0,0 +1,91 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * /scummvm/scummvm/scumm/player_v3a.h
+ *
+ */
+
+#ifndef SCUMM_MUSIC_H
+#define SCUMM_MUSIC_H
+
+#include "common/scummsys.h"
+
+namespace Scumm {
+
+/**
+ * Pure virtual base class for the various music/sound engines used in Scumm
+ * games. In particular, the iMuse code provides a subclass of this. There are
+ * several other subclasses providing music and sound capabilities for
+ * several Scumm games.
+ * Having this base class for all music engines allows uniform access to the
+ * core music/sound functionality, thus simplifying the client code.
+ *
+ * Instantiated by class Scumm.
+ */
+class MusicEngine {
+public:
+ virtual ~MusicEngine() {}
+
+ /**
+ * Set the output volume.
+ * @param vol the new output volume
+ */
+ virtual void setMusicVolume(int vol) = 0;
+
+ /**
+ * Start playing the sound with the given id.
+ * @param sound the sound to start
+ */
+ virtual void startSound(int sound) = 0;
+
+ /**
+ * Stop playing the sound with the given id.
+ * @param sound the sound to stop
+ */
+ virtual void stopSound(int sound) = 0;
+
+ /**
+ * Start playing all currently playing sounds.
+ */
+ virtual void stopAllSounds() = 0;
+
+ /**
+ * Query the status of the sound with the given id. Usually this is just
+ * a boolean telling us whether the sound is playing or not.
+ * @param sound the sound to for which we want the status
+ * @return the status of the specified sound
+ */
+ virtual int getSoundStatus(int sound) const = 0;
+
+ /**
+ * Get the value of the music timer. Used for synchronising scripts with
+ * the music/sound.
+ * @return the music timer
+ */
+ virtual int getMusicTimer() const { return 0; }
+
+ /**
+ * Terminate the music engine. Called just before the music engine
+ * is deleted.
+ */
+ virtual void terminate() {}
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/nut_renderer.cpp b/engines/scumm/nut_renderer.cpp
new file mode 100644
index 0000000000..e7d3e6f7c9
--- /dev/null
+++ b/engines/scumm/nut_renderer.cpp
@@ -0,0 +1,303 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/nut_renderer.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+NutRenderer::NutRenderer(ScummEngine *vm) :
+ _vm(vm),
+ _loaded(false),
+ _numChars(0) {
+ memset(_chars, 0, sizeof(_chars));
+}
+
+NutRenderer::~NutRenderer() {
+ for (int i = 0; i < _numChars; i++) {
+ delete []_chars[i].src;
+ }
+}
+
+void smush_decode_codec1(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
+
+static void smush_decode_codec21(byte *dst, const byte *src, int width, int height, int pitch) {
+ while (height--) {
+ uint8 *dstPtrNext = dst + pitch;
+ const uint8 *srcPtrNext = src + 2 + READ_LE_UINT16(src); src += 2;
+ int len = width;
+ do {
+ int offs = READ_LE_UINT16(src); src += 2;
+ dst += offs;
+ len -= offs;
+ if (len <= 0) {
+ break;
+ }
+ int w = READ_LE_UINT16(src) + 1; src += 2;
+ len -= w;
+ if (len < 0) {
+ w += len;
+ }
+ // the original codec44 handles this part slightly differently (this is the only difference with codec21) :
+ // src bytes equal to 255 are replaced by 0 in dst
+ // src bytes equal to 1 are replaced by a color passed as an argument in the original function
+ // other src bytes values are copied as-is
+ memcpy(dst, src, w); dst += w; src += w;
+ } while (len > 0);
+ dst = dstPtrNext;
+ src = srcPtrNext;
+ }
+}
+
+bool NutRenderer::loadFont(const char *filename) {
+ if (_loaded) {
+ debug(0, "NutRenderer::loadFont() Font already loaded, ok, loading...");
+ }
+
+ ScummFile file;
+ _vm->openFile(file, filename);
+ if (!file.isOpen()) {
+ error("NutRenderer::loadFont() Can't open font file: %s", filename);
+ return false;
+ }
+
+ uint32 tag = file.readUint32BE();
+ if (tag != 'ANIM') {
+ error("NutRenderer::loadFont() there is no ANIM chunk in font header");
+ return false;
+ }
+
+ uint32 length = file.readUint32BE();
+ byte *dataSrc = (byte *)malloc(length);
+ file.read(dataSrc, length);
+ file.close();
+
+ if (READ_BE_UINT32(dataSrc) != 'AHDR') {
+ error("NutRenderer::loadFont() there is no AHDR chunk in font header");
+ free(dataSrc);
+ return false;
+ }
+
+ _numChars = READ_LE_UINT16(dataSrc + 10);
+ assert(_numChars <= ARRAYSIZE(_chars));
+ uint32 offset = 0;
+ for (int l = 0; l < _numChars; l++) {
+ offset += READ_BE_UINT32(dataSrc + offset + 4) + 8;
+ if (READ_BE_UINT32(dataSrc + offset) != 'FRME') {
+ error("NutRenderer::loadFont(%s) there is no FRME chunk %d (offset %x)", filename, l, offset);
+ break;
+ }
+ offset += 8;
+ if (READ_BE_UINT32(dataSrc + offset) != 'FOBJ') {
+ error("NutRenderer::loadFont(%s) there is no FOBJ chunk in FRME chunk %d (offset %x)", filename, l, offset);
+ break;
+ }
+ int codec = READ_LE_UINT16(dataSrc + offset + 8);
+ _chars[l].xoffs = READ_LE_UINT16(dataSrc + offset + 10);
+ _chars[l].yoffs = READ_LE_UINT16(dataSrc + offset + 12);
+ _chars[l].width = READ_LE_UINT16(dataSrc + offset + 14);
+ _chars[l].height = READ_LE_UINT16(dataSrc + offset + 16);
+ const int srcSize = _chars[l].width * _chars[l].height;
+ _chars[l].src = new byte[srcSize];
+ // If characters have transparency, then bytes just get skipped and
+ // so there may appear some garbage. That's why we have to fill it
+ // with zeroes first.
+ memset(_chars[l].src, 0, srcSize);
+
+ const uint8 *fobjptr = dataSrc + offset + 22;
+ switch (codec) {
+ case 1:
+ smush_decode_codec1(_chars[l].src, fobjptr, 0, 0, _chars[l].width, _chars[l].height, _chars[l].width);
+ break;
+ case 21:
+ case 44:
+ smush_decode_codec21(_chars[l].src, fobjptr, _chars[l].width, _chars[l].height, _chars[l].width);
+ break;
+ default:
+ error("NutRenderer::loadFont: unknown codec: %d", codec);
+ }
+ }
+
+ free(dataSrc);
+ _loaded = true;
+ return true;
+}
+
+int NutRenderer::getCharWidth(byte c) const {
+ if (!_loaded) {
+ error("NutRenderer::getCharWidth() Font is not loaded");
+ return 0;
+ }
+
+ if (c >= 0x80 && _vm->_useCJKMode)
+ return _vm->_2byteWidth / 2;
+
+ if (c >= _numChars)
+ error("invalid character in NutRenderer::getCharWidth : %d (%d)", c, _numChars);
+
+ return _chars[c].width;
+}
+
+int NutRenderer::getCharHeight(byte c) const {
+ if (!_loaded) {
+ error("NutRenderer::getCharHeight() Font is not loaded");
+ return 0;
+ }
+
+ if (c >= 0x80 && _vm->_useCJKMode)
+ return _vm->_2byteHeight;
+
+ if (c >= _numChars)
+ error("invalid character in NutRenderer::getCharHeight : %d (%d)", c, _numChars);
+
+ return _chars[c].height;
+}
+
+void NutRenderer::drawShadowChar(const Graphics::Surface &s, int c, int x, int y, byte color, bool showShadow) {
+ if (!_loaded) {
+ error("NutRenderer::drawShadowChar() Font is not loaded");
+ return;
+ }
+
+ // HACK: we draw the character a total of 7 times: 6 times shifted
+ // and in black for the shadow, and once in the right color and position.
+ // This way we achieve the exact look as the original CMI had. However,
+ // the question remains whether they did it this way, too, or if there is
+ // some "font shadow" resource we don't know yet.
+
+ static const int offsetX[7] = { -1, 0, 1, 0, 1, 2, 0 };
+ static const int offsetY[7] = { 0, -1, 0, 1, 2, 1, 0 };
+ const int cTable[7] = { 0, 0, 0, 0, 0, 0, color };
+ int i = 0;
+
+ if (!showShadow)
+ i = 6;
+
+ for (; i < 7; i++) {
+ x += offsetX[i];
+ y += offsetY[i];
+ color = cTable[i];
+
+ if (c >= 256 && _vm->_useCJKMode)
+ draw2byte(s, c, x, y, color);
+ else
+ drawChar(s, (byte)c, x, y, color);
+
+ x -= offsetX[i];
+ y -= offsetY[i];
+ }
+}
+
+void NutRenderer::drawFrame(byte *dst, int c, int x, int y) {
+ const int width = MIN(_chars[c].width, _vm->_screenWidth - x);
+ const int height = MIN(_chars[c].height, _vm->_screenHeight - y);
+ const byte *src = _chars[c].src;
+ const int srcPitch = _chars[c].width;
+ byte bits = 0;
+
+ const int minX = x < 0 ? -x : 0;
+ const int minY = y < 0 ? -y : 0;
+
+ if (height <= 0 || width <= 0) {
+ return;
+ }
+
+ dst += _vm->_screenWidth * y + x;
+ if (minY) {
+ src += minY * srcPitch;
+ dst += minY * _vm->_screenWidth;
+ }
+
+ for (int ty = minY; ty < height; ty++) {
+ for (int tx = minX; tx < width; tx++) {
+ bits = src[tx];
+ if (bits != 231 && bits) {
+ dst[tx] = bits;
+ }
+ }
+ src += srcPitch;
+ dst += _vm->_screenWidth;
+ }
+}
+
+void NutRenderer::drawChar(const Graphics::Surface &s, byte c, int x, int y, byte color) {
+ byte *dst = (byte *)s.pixels + y * s.pitch + x;
+ const int width = MIN(_chars[c].width, s.w - x);
+ const int height = MIN(_chars[c].height, s.h - y);
+ const byte *src = _chars[c].src;
+ const int srcPitch = _chars[c].width;
+
+ const int minX = x < 0 ? -x : 0;
+ const int minY = y < 0 ? -y : 0;
+
+ if (height <= 0 || width <= 0) {
+ return;
+ }
+
+ if (minY) {
+ src += minY * srcPitch;
+ dst += minY * s.pitch;
+ }
+
+ for (int ty = minY; ty < height; ty++) {
+ for (int tx = minX; tx < width; tx++) {
+ if (src[tx] != 0) {
+ dst[tx] = color;
+ }
+ }
+ src += srcPitch;
+ dst += s.pitch;
+ }
+}
+
+void NutRenderer::draw2byte(const Graphics::Surface &s, int c, int x, int y, byte color) {
+ if (!_loaded) {
+ error("NutRenderer::draw2byte() Font is not loaded");
+ return;
+ }
+
+ byte *dst = (byte *)s.pixels + y * s.pitch + x;
+ const int width = _vm->_2byteWidth;
+ const int height = MIN(_vm->_2byteHeight, s.h - y);
+ const byte *src = _vm->get2byteCharPtr(c);
+ byte bits = 0;
+
+ if (height <= 0 || width <= 0) {
+ return;
+ }
+
+ for (int ty = 0; ty < height; ty++) {
+ for (int tx = 0; tx < width; tx++) {
+ if ((tx & 7) == 0)
+ bits = *src++;
+ if (x + tx < 0 || x + tx >= s.w || y + ty < 0)
+ continue;
+ if (bits & revBitMask(tx % 8)) {
+ dst[tx] = color;
+ }
+ }
+ dst += s.pitch;
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/nut_renderer.h b/engines/scumm/nut_renderer.h
new file mode 100644
index 0000000000..8972fef949
--- /dev/null
+++ b/engines/scumm/nut_renderer.h
@@ -0,0 +1,64 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#if !defined(NUT_RENDERER_H) && !defined(DISABLE_SCUMM_7_8)
+#define NUT_RENDERER_H
+
+#include "common/file.h"
+#include "graphics/surface.h"
+
+namespace Scumm {
+
+class ScummEngine;
+
+class NutRenderer {
+protected:
+ ScummEngine *_vm;
+ bool _loaded;
+ int _numChars;
+ struct {
+ int xoffs;
+ int yoffs;
+ int width;
+ int height;
+ byte *src;
+ } _chars[256];
+
+ void drawChar(const Graphics::Surface &s, byte c, int x, int y, byte color);
+ void draw2byte(const Graphics::Surface &s, int c, int x, int y, byte color);
+
+public:
+ NutRenderer(ScummEngine *vm);
+ virtual ~NutRenderer();
+ int getNumChars() const { return _numChars; }
+
+ bool loadFont(const char *filename);
+
+ void drawFrame(byte *dst, int c, int x, int y);
+ void drawShadowChar(const Graphics::Surface &s, int c, int x, int y, byte color, bool showShadow);
+
+ int getCharWidth(byte c) const;
+ int getCharHeight(byte c) const;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp
new file mode 100644
index 0000000000..0472edcc15
--- /dev/null
+++ b/engines/scumm/object.cpp
@@ -0,0 +1,1779 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/actor.h"
+#include "scumm/bomp.h"
+#include "scumm/intern.h"
+#ifndef DISABLE_HE
+#include "scumm/intern_he.h"
+#endif
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/usage_bits.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct BompHeader { /* Bomp header */
+ union {
+ struct {
+ uint16 unk;
+ uint16 width, height;
+ } GCC_PACK old;
+
+ struct {
+ uint32 width, height;
+ } GCC_PACK v8;
+ } GCC_PACK;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+
+bool ScummEngine::getClass(int obj, int cls) const {
+ checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getClass");
+ cls &= 0x7F;
+ checkRange(32, 1, cls, "Class %d out of range in getClass");
+
+ if (_features & GF_SMALL_HEADER) {
+ // Translate the new (V5) object classes to the old classes
+ // (for those which differ).
+ switch (cls) {
+ case kObjectClassUntouchable:
+ cls = 24;
+ break;
+ case kObjectClassPlayer:
+ cls = 23;
+ break;
+ case kObjectClassXFlip:
+ cls = 19;
+ break;
+ case kObjectClassYFlip:
+ cls = 18;
+ break;
+ }
+ }
+
+ return (_classData[obj] & (1 << (cls - 1))) != 0;
+}
+
+void ScummEngine::putClass(int obj, int cls, bool set) {
+ checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in putClass");
+ cls &= 0x7F;
+ checkRange(32, 1, cls, "Class %d out of range in putClass");
+
+ if (_features & GF_SMALL_HEADER) {
+ // Translate the new (V5) object classes to the old classes
+ // (for those which differ).
+ switch (cls) {
+ case kObjectClassUntouchable:
+ cls = 24;
+ break;
+ case kObjectClassPlayer:
+ cls = 23;
+ break;
+ case kObjectClassXFlip:
+ cls = 19;
+ break;
+ case kObjectClassYFlip:
+ cls = 18;
+ break;
+ }
+ }
+
+ if (set)
+ _classData[obj] |= (1 << (cls - 1));
+ else
+ _classData[obj] &= ~(1 << (cls - 1));
+
+ if (_version <= 4 && obj >= 1 && obj < _numActors) {
+ _actors[obj].classChanged(cls, set);
+ }
+}
+
+int ScummEngine::getOwner(int obj) const {
+ checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getOwner");
+ return _objectOwnerTable[obj];
+}
+
+void ScummEngine::putOwner(int obj, int owner) {
+ checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in putOwner");
+ checkRange(0xFF, 0, owner, "Owner %d out of range in putOwner");
+ _objectOwnerTable[obj] = owner;
+}
+
+int ScummEngine::getState(int obj) {
+ checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getState");
+
+ if (!_copyProtection) {
+ // I knew LucasArts sold cracked copies of the original Maniac Mansion,
+ // at least as part of Day of the Tentacle. Apparently they also sold
+ // cracked versions of the enhanced version. At least in Germany.
+ //
+ // This will keep the security door open at all times. I can only
+ // assume that 182 and 193 each correspond to one particular side of
+ // the it. Fortunately it does not prevent frustrated players from
+ // blowing up the mansion, should they feel the urge to.
+
+ if (_gameId == GID_MANIAC && (obj == 182 || obj == 193))
+ _objectStateTable[obj] |= 0x08;
+ }
+
+ return _objectStateTable[obj];
+}
+
+void ScummEngine::putState(int obj, int state) {
+ checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in putState");
+ checkRange(0xFF, 0, state, "State %d out of range in putState");
+ _objectStateTable[obj] = state;
+}
+
+int ScummEngine::getObjectRoom(int obj) const {
+ checkRange(_numGlobalObjects - 1, 0, obj, "Object %d out of range in getObjectRoom");
+ return _objectRoomTable[obj];
+}
+
+int ScummEngine::getObjectIndex(int object) const {
+ int i;
+
+ if (object < 1)
+ return -1;
+
+ for (i = (_numLocalObjects-1); i > 0; i--) {
+ if (_objs[i].obj_nr == object)
+ return i;
+ }
+ return -1;
+}
+
+int ScummEngine::whereIsObject(int object) const {
+ int i;
+
+ if (object >= _numGlobalObjects)
+ return WIO_NOT_FOUND;
+
+ if (object < 1)
+ return WIO_NOT_FOUND;
+
+ if (_objectOwnerTable[object] != OF_OWNER_ROOM) {
+ for (i = 0; i < _numInventory; i++)
+ if (_inventory[i] == object)
+ return WIO_INVENTORY;
+ return WIO_NOT_FOUND;
+ }
+
+ for (i = (_numLocalObjects-1); i > 0; i--)
+ if (_objs[i].obj_nr == object) {
+ if (_objs[i].fl_object_index)
+ return WIO_FLOBJECT;
+ return WIO_ROOM;
+ }
+
+ return WIO_NOT_FOUND;
+}
+
+int ScummEngine::getObjectOrActorXY(int object, int &x, int &y) {
+ if (object < _numActors) {
+ Actor *act = derefActorSafe(object, "getObjectOrActorXY");
+ if (act)
+ return act->getActorXYPos(x, y);
+ else
+ return -1;
+ }
+
+ switch (whereIsObject(object)) {
+ case WIO_NOT_FOUND:
+ return -1;
+ case WIO_INVENTORY:
+ if (_objectOwnerTable[object] < _numActors)
+ return derefActor(_objectOwnerTable[object], "getObjectOrActorXY(2)")->getActorXYPos(x, y);
+ else
+ return -1;
+ }
+ getObjectXYPos(object, x, y);
+ return 0;
+}
+
+/**
+ * Return the position of an object.
+ * Returns X, Y and direction in angles
+ */
+void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) {
+ int idx = getObjectIndex(object);
+ assert(idx >= 0);
+ ObjectData &od = _objs[idx];
+ int state;
+ const byte *ptr;
+ const ImageHeader *imhd;
+
+ if (_version >= 6) {
+ state = getState(object) - 1;
+ if (state < 0)
+ state = 0;
+
+ ptr = getOBIMFromObjectData(od);
+ if (!ptr) {
+ // FIXME: We used to assert here, but it seems that in the nexus
+ // in The Dig, this can happen, at least with old savegames, and
+ // it's safe to continue...
+ debug(0, "getObjectXYPos: Can't find object %d", object);
+ return;
+ }
+ imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), ptr);
+ assert(imhd);
+ if (_version == 8) {
+ switch (FROM_LE_32(imhd->v8.version)) {
+ case 800:
+ x = od.x_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x44);
+ y = od.y_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x48);
+ break;
+ case 801:
+ x = od.x_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].x);
+ y = od.y_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].y);
+ break;
+ default:
+ error("Unsupported image header version %d\n", FROM_LE_32(imhd->v8.version));
+ }
+ } else if (_version == 7) {
+ x = od.x_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].x);
+ y = od.y_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].y);
+ } else {
+ x = od.x_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].x);
+ y = od.y_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].y);
+ }
+ } else {
+ x = od.walk_x;
+ y = od.walk_y;
+ }
+ if (_version == 8)
+ dir = fromSimpleDir(1, od.actordir);
+ else
+ dir = oldDirToNewDir(od.actordir & 3);
+}
+
+static int getDist(int x, int y, int x2, int y2) {
+ int a = ABS(y - y2);
+ int b = ABS(x - x2);
+ return MAX(a, b);
+}
+
+int ScummEngine::getObjActToObjActDist(int a, int b) {
+ int x, y, x2, y2;
+ Actor *acta = NULL;
+ Actor *actb = NULL;
+
+ if (a < _numActors)
+ acta = derefActorSafe(a, "getObjActToObjActDist");
+
+ if (b < _numActors)
+ actb = derefActorSafe(b, "getObjActToObjActDist(2)");
+
+ if (acta && actb && acta->getRoom() == actb->getRoom() && acta->getRoom() && !acta->isInCurrentRoom())
+ return 0;
+
+ if (getObjectOrActorXY(a, x, y) == -1)
+ return 0xFF;
+
+ if (getObjectOrActorXY(b, x2, y2) == -1)
+ return 0xFF;
+
+ // Perform adjustXYToBeInBox() *only* if the first item is an
+ // actor and the second is an object. This used to not check
+ // whether the second item is a non-actor, which caused bug
+ // #853874).
+ if (acta && !actb) {
+ AdjustBoxResult r = acta->adjustXYToBeInBox(x2, y2);
+ x2 = r.x;
+ y2 = r.y;
+ }
+
+ // Now compute the distance between the two points
+ if (_version <= 2) {
+ // For V1/V2 games, distances are measured in the original "character"
+ // based coordinate system, instead of pixels. Otherwise various scripts
+ // will break. See bugs #853874, #774529
+ x /= 8;
+ y /= 2;
+ x2 /= 8;
+ y2 /= 2;
+ }
+
+ return getDist(x, y, x2, y2);
+}
+
+int ScummEngine::findObject(int x, int y) {
+ int i, b;
+ byte a;
+ const int mask = (_version <= 2) ? 0x8 : 0xF;
+
+ for (i = 1; i < _numLocalObjects; i++) {
+ if ((_objs[i].obj_nr < 1) || getClass(_objs[i].obj_nr, kObjectClassUntouchable))
+ continue;
+
+ if (_platform == Common::kPlatformC64 && _gameId == GID_MANIAC) {
+ if (_objs[i].flags == 0 && _objs[i].state & 0x2)
+ continue;
+ } else {
+ if (_version <= 2 && _objs[i].state & 0x2)
+ continue;
+ }
+
+ b = i;
+ do {
+ a = _objs[b].parentstate;
+ b = _objs[b].parent;
+ if (b == 0) {
+#ifndef DISABLE_HE
+ if (_heversion >= 70) {
+ if (((ScummEngine_v70he *)this)->_wiz->polygonHit(_objs[i].obj_nr, x, y))
+ return _objs[i].obj_nr;
+ }
+#endif
+ if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x &&
+ _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y)
+ return _objs[i].obj_nr;
+ break;
+ }
+ } while ((_objs[b].state & mask) == a);
+ }
+
+ return 0;
+}
+
+void ScummEngine::drawRoomObject(int i, int arg) {
+ ObjectData *od;
+ byte a;
+ const int mask = (_version <= 2) ? 0x8 : 0xF;
+
+ od = &_objs[i];
+ if ((i < 1) || (od->obj_nr < 1) || !od->state)
+ return;
+
+ do {
+ a = od->parentstate;
+ if (!od->parent) {
+ if (_version <= 6 || od->fl_object_index == 0)
+ drawObject(i, arg);
+ break;
+ }
+ od = &_objs[od->parent];
+ } while ((od->state & mask) == a);
+}
+
+void ScummEngine::drawRoomObjects(int arg) {
+ int i;
+ const int mask = (_version <= 2) ? 0x8 : 0xF;
+
+ if (_heversion >= 60) {
+ // In HE games, normal objects are drawn, followed by FlObjects.
+ for (i = (_numLocalObjects-1); i > 0; i--) {
+ if (_objs[i].obj_nr > 0 && (_objs[i].state & mask) && _objs[i].fl_object_index == 0)
+ drawRoomObject(i, arg);
+ }
+ for (i = (_numLocalObjects-1); i > 0; i--) {
+ if (_objs[i].obj_nr > 0 && (_objs[i].state & mask) && _objs[i].fl_object_index != 0)
+ drawRoomObject(i, arg);
+ }
+ } else if (_gameId == GID_SAMNMAX) {
+ // In Sam & Max, objects are drawn in reverse order.
+ for (i = 1; i < _numLocalObjects; i++)
+ if (_objs[i].obj_nr > 0)
+ drawRoomObject(i, arg);
+ } else {
+ for (i = (_numLocalObjects-1); i > 0; i--)
+ if (_objs[i].obj_nr > 0 && (_objs[i].state & mask)) {
+ drawRoomObject(i, arg);
+ }
+ }
+}
+
+static const uint32 IMxx_tags[] = {
+ MKID('IM00'),
+ MKID('IM01'),
+ MKID('IM02'),
+ MKID('IM03'),
+ MKID('IM04'),
+ MKID('IM05'),
+ MKID('IM06'),
+ MKID('IM07'),
+ MKID('IM08'),
+ MKID('IM09'),
+ MKID('IM0A'),
+ MKID('IM0B'),
+ MKID('IM0C'),
+ MKID('IM0D'),
+ MKID('IM0E'),
+ MKID('IM0F'),
+ MKID('IM10')
+};
+
+void ScummEngine::drawObject(int obj, int arg) {
+ if (_skipDrawObject)
+ return;
+
+ ObjectData &od = _objs[obj];
+ int height, width;
+ const byte *ptr;
+ int x, a, numstrip;
+ int tmp;
+
+ if (_bgNeedsRedraw)
+ arg = 0;
+
+ if (od.obj_nr == 0)
+ return;
+
+ checkRange(_numGlobalObjects - 1, 0, od.obj_nr, "Object %d out of range in drawObject");
+
+ const int xpos = od.x_pos / 8;
+ const int ypos = od.y_pos;
+
+ width = od.width / 8;
+ height = od.height &= 0xFFFFFFF8; // Mask out last 3 bits
+
+ // Short circuit for objects which aren't visible at all.
+ if (width == 0 || xpos > _screenEndStrip || xpos + width < _screenStartStrip)
+ return;
+
+ ptr = getOBIMFromObjectData(od);
+
+ if (_features & GF_OLD_BUNDLE)
+ ptr += 0;
+ else
+ ptr = getObjectImage(ptr, getState(od.obj_nr));
+
+ if (!ptr)
+ return;
+
+ x = 0xFFFF;
+
+ for (a = numstrip = 0; a < width; a++) {
+ tmp = xpos + a;
+ if (tmp < _screenStartStrip || _screenEndStrip < tmp)
+ continue;
+ if (arg > 0 && _screenStartStrip + arg <= tmp)
+ continue;
+ if (arg < 0 && tmp <= _screenEndStrip + arg)
+ continue;
+ setGfxUsageBit(tmp, USAGE_BIT_DIRTY);
+ if (tmp < x)
+ x = tmp;
+ numstrip++;
+ }
+
+ if (numstrip != 0) {
+ byte flags = od.flags | Gdi::dbObjectMode;
+
+ // Sam & Max needs this to fix object-layering problems with
+ // the inventory and conversation icons.
+ if ((_gameId == GID_SAMNMAX && getClass(od.obj_nr, kObjectClassIgnoreBoxes)) ||
+ (_gameId == GID_FT && getClass(od.obj_nr, kObjectClassPlayer)))
+ flags |= Gdi::dbDrawMaskOnAll;
+
+ if (_heversion >= 70 && findResource(MKID('SMAP'), ptr) == NULL)
+ gdi.drawBMAPObject(ptr, &virtscr[0], obj, od.x_pos, od.y_pos, od.width, od.height);
+ else
+ gdi.drawBitmap(ptr, &virtscr[0], x, ypos, width * 8, height, x - xpos, numstrip, flags);
+ }
+}
+
+void ScummEngine::clearRoomObjects() {
+ int i;
+
+ if (_features & GF_SMALL_HEADER) {
+ for (i = 0; i < _numLocalObjects; i++) {
+ _objs[i].obj_nr = 0;
+ }
+ } else {
+ storeFlObject(-1);
+
+ for (i = 0; i < _numLocalObjects; i++) {
+ if (_objs[i].obj_nr < 1) // Optimise codepath
+ continue;
+
+ // Nuke all non-flObjects (flObjects are nuked in script.cpp)
+ if (_objs[i].fl_object_index == 0) {
+ _objs[i].obj_nr = 0;
+ } else {
+ // Nuke all unlocked flObjects
+ if (!res.isLocked(rtFlObject, _objs[i].fl_object_index)) {
+ res.nukeResource(rtFlObject, _objs[i].fl_object_index);
+ _objs[i].obj_nr = 0;
+ _objs[i].fl_object_index = 0;
+ } else if (_heversion >= 70) {
+ storeFlObject(i);
+ _objs[i].obj_nr = 0;
+ _objs[i].fl_object_index = 0;
+ }
+ }
+ }
+ }
+}
+
+void ScummEngine::storeFlObject(int slot) {
+ if (slot == -1) {
+ _numStoredFlObjects = 0;
+ } else {
+ memcpy(&_storedFlObjects[_numStoredFlObjects], &_objs[slot], sizeof(_objs[slot]));
+ _numStoredFlObjects++;
+ if (_numStoredFlObjects > 100)
+ error("Too many flobjects saved on room transition.");
+ }
+}
+
+void ScummEngine::restoreFlObjects() {
+ if (!_numStoredFlObjects)
+ return;
+
+ int i, slot;
+
+ for (i = 0; i < _numStoredFlObjects; i++) {
+ slot = findLocalObjectSlot();
+ memcpy(&_objs[slot], &_storedFlObjects[i], sizeof(_objs[slot]));
+ }
+
+ _numStoredFlObjects = 0;
+}
+
+void ScummEngine::loadRoomObjects() {
+ int i, j;
+ ObjectData *od;
+ const byte *ptr;
+ uint16 obim_id;
+ const byte *room, *searchptr, *rootptr;
+ const CodeHeader *cdhd;
+
+ CHECK_HEAP
+ room = getResourceAddress(rtRoom, _roomResource);
+
+ if (_numObjectsInRoom == 0)
+ return;
+
+ if (_numObjectsInRoom > _numLocalObjects)
+ error("More than %d objects in room %d", _numLocalObjects, _roomResource);
+
+ if (_version == 8)
+ searchptr = rootptr = getResourceAddress(rtRoomScripts, _roomResource);
+ else
+ searchptr = rootptr = room;
+ assert(searchptr);
+
+ // Load in new room objects
+ ResourceIterator obcds(searchptr, false);
+ for (i = 0; i < _numObjectsInRoom; i++) {
+ od = &_objs[findLocalObjectSlot()];
+
+ ptr = obcds.findNext(MKID('OBCD'));
+ if (ptr == NULL)
+ error("Room %d missing object code block(s)", _roomResource);
+
+ od->OBCDoffset = ptr - rootptr;
+ cdhd = (const CodeHeader *)findResourceData(MKID('CDHD'), ptr);
+
+ if (_version >= 7)
+ od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
+ else if (_version == 6)
+ od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id));
+ else
+ od->obj_nr = READ_LE_UINT16(&(cdhd->v5.obj_id));
+
+ if (_dumpScripts) {
+ char buf[32];
+ sprintf(buf, "roomobj-%d-", _roomResource);
+ ptr = findResource(MKID('VERB'), ptr);
+ dumpResource(buf, od->obj_nr, ptr);
+ }
+
+ }
+
+ searchptr = room;
+ ResourceIterator obims(room, false);
+ for (i = 0; i < _numObjectsInRoom; i++) {
+ ptr = obims.findNext(MKID('OBIM'));
+ if (ptr == NULL)
+ error("Room %d missing image blocks(s)", _roomResource);
+
+ obim_id = getObjectIdFromOBIM(ptr);
+
+ for (j = 1; j < _numLocalObjects; j++) {
+ if (_objs[j].obj_nr == obim_id)
+ _objs[j].OBIMoffset = ptr - room;
+ }
+ }
+
+ for (i = 1; i < _numLocalObjects; i++) {
+ if (_objs[i].obj_nr && !_objs[i].fl_object_index)
+ setupRoomObject(&_objs[i], room);
+ }
+
+ CHECK_HEAP
+}
+
+void ScummEngine_v3old::loadRoomObjects() {
+ int i;
+ ObjectData *od;
+ const byte *room, *ptr;
+
+ CHECK_HEAP
+ room = getResourceAddress(rtRoom, _roomResource);
+
+ if (_numObjectsInRoom == 0)
+ return;
+
+ if (_numObjectsInRoom > _numLocalObjects)
+ error("More than %d objects in room %d", _numLocalObjects, _roomResource);
+
+ if (_version <= 2)
+ ptr = room + 28;
+ else
+ ptr = room + 29;
+
+ for (i = 0; i < _numObjectsInRoom; i++) {
+ od = &_objs[findLocalObjectSlot()];
+
+ od->OBIMoffset = READ_LE_UINT16(ptr);
+ od->OBCDoffset = READ_LE_UINT16(ptr + 2 * _numObjectsInRoom);
+ setupRoomObject(od, room);
+
+ ptr += 2;
+
+ if (_dumpScripts) {
+ char buf[32];
+ sprintf(buf, "roomobj-%d-", _roomResource);
+ dumpResource(buf, od->obj_nr, room + od->OBCDoffset);
+ }
+ }
+
+ CHECK_HEAP
+}
+
+void ScummEngine_v4::loadRoomObjects() {
+ int i, j;
+ ObjectData *od;
+ const byte *ptr;
+ uint16 obim_id;
+ const byte *room;
+
+ CHECK_HEAP
+ room = getResourceAddress(rtRoom, _roomResource);
+
+ if (_numObjectsInRoom == 0)
+ return;
+
+ if (_numObjectsInRoom > _numLocalObjects)
+ error("More than %d objects in room %d", _numLocalObjects, _roomResource);
+
+ ResourceIterator obcds(room, true);
+ for (i = 0; i < _numObjectsInRoom; i++) {
+ od = &_objs[findLocalObjectSlot()];
+
+ ptr = obcds.findNext(MKID('OBCD'));
+ if (ptr == NULL)
+ error("Room %d missing object code block(s)", _roomResource);
+
+ od->OBCDoffset = ptr - room;
+ od->obj_nr = READ_LE_UINT16(ptr + 6);
+ if (_dumpScripts) {
+ char buf[32];
+ sprintf(buf, "roomobj-%d-", _roomResource);
+ dumpResource(buf, od->obj_nr, ptr);
+ }
+ }
+
+ ResourceIterator obims(room, true);
+ for (i = 0; i < _numObjectsInRoom; i++) {
+ ptr = obims.findNext(MKID('OBIM'));
+ if (ptr == NULL)
+ error("Room %d missing image blocks(s)", _roomResource);
+
+ obim_id = READ_LE_UINT16(ptr + 6);
+
+ for (j = 1; j < _numLocalObjects; j++) {
+ if (_objs[j].obj_nr == obim_id)
+ _objs[j].OBIMoffset = ptr - room;
+ }
+ }
+
+ for (i = 1; i < _numLocalObjects; i++) {
+ if (_objs[i].obj_nr && !_objs[i].fl_object_index) {
+ setupRoomObject(&_objs[i], room);
+ }
+ }
+
+ CHECK_HEAP
+}
+
+void ScummEngine_c64::setupRoomObject(ObjectData *od, const byte *room, const byte *searchptr) {
+ assert(room);
+ const byte *ptr = room + od->OBCDoffset;
+ ptr -= 2;
+
+ od->obj_nr = *(ptr + 6);
+ od->flags = *(ptr + 7);
+
+ od->x_pos = *(ptr + 8) * 8;
+ od->y_pos = ((*(ptr + 9)) & 0x7F) * 8;
+
+ od->parentstate = (*(ptr + 9) & 0x80) ? 1 : 0;
+ od->parentstate *= 8;
+
+ od->width = *(ptr + 10) * 8;
+
+ od->parent = *(ptr + 11);
+
+ od->walk_x = *(ptr + 12) * 8;
+ od->walk_y = (*(ptr + 13) & 0x1f) * 8;
+ od->actordir = (*(ptr + 14)) & 7;
+ od->height = *(ptr + 14) & 0xf8;
+}
+
+void ScummEngine_v4::setupRoomObject(ObjectData *od, const byte *room, const byte *searchptr) {
+ assert(room);
+ const byte *ptr = room + od->OBCDoffset;
+
+ if (_features & GF_OLD_BUNDLE)
+ ptr -= 2;
+
+ od->obj_nr = READ_LE_UINT16(ptr + 6);
+
+ od->x_pos = *(ptr + 9) * 8;
+ od->y_pos = ((*(ptr + 10)) & 0x7F) * 8;
+
+ od->parentstate = (*(ptr + 10) & 0x80) ? 1 : 0;
+ if (_version <= 2)
+ od->parentstate *= 8;
+
+ od->width = *(ptr + 11) * 8;
+
+ od->parent = *(ptr + 12);
+
+ if (_version <= 2) {
+ od->walk_x = *(ptr + 13) * 8;
+ od->walk_y = (*(ptr + 14) & 0x1f) * 8;
+ od->actordir = (*(ptr + 15)) & 7;
+ od->height = *(ptr + 15) & 0xf8;
+ } else {
+ od->walk_x = READ_LE_UINT16(ptr + 13);
+ od->walk_y = READ_LE_UINT16(ptr + 15);
+ od->actordir = (*(ptr + 17)) & 7;
+ od->height = *(ptr + 17) & 0xf8;
+ }
+}
+
+void ScummEngine::setupRoomObject(ObjectData *od, const byte *room, const byte *searchptr) {
+ const CodeHeader *cdhd = NULL;
+ const ImageHeader *imhd = NULL;
+
+ assert(room);
+
+ if (searchptr == NULL) {
+ if (_version == 8)
+ searchptr = getResourceAddress(rtRoomScripts, _roomResource);
+ else
+ searchptr = room;
+ }
+
+ cdhd = (const CodeHeader *)findResourceData(MKID('CDHD'), searchptr + od->OBCDoffset);
+ if (cdhd == NULL)
+ error("Room %d missing CDHD blocks(s)", _roomResource);
+ if (od->OBIMoffset)
+ imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), room + od->OBIMoffset);
+
+ od->flags = Gdi::dbAllowMaskOr;
+
+ if (_version == 8) {
+ od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
+
+ od->parent = cdhd->v7.parent;
+ od->parentstate = cdhd->v7.parentstate;
+
+ od->x_pos = (int)READ_LE_UINT32(&imhd->v8.x_pos);
+ od->y_pos = (int)READ_LE_UINT32(&imhd->v8.y_pos);
+ od->width = (uint)READ_LE_UINT32(&imhd->v8.width);
+ od->height = (uint)READ_LE_UINT32(&imhd->v8.height);
+ // HACK: This is done since an angle doesn't fit into a byte (360 > 256)
+ od->actordir = toSimpleDir(1, READ_LE_UINT32(&imhd->v8.actordir));
+ if (FROM_LE_32(imhd->v8.version) == 801)
+ od->flags = ((((byte)READ_LE_UINT32(&imhd->v8.flags)) & 16) == 0) ? Gdi::dbAllowMaskOr : 0;
+
+ } else if (_version == 7) {
+ od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id));
+
+ od->parent = cdhd->v7.parent;
+ od->parentstate = cdhd->v7.parentstate;
+
+ od->x_pos = READ_LE_UINT16(&imhd->v7.x_pos);
+ od->y_pos = READ_LE_UINT16(&imhd->v7.y_pos);
+ od->width = READ_LE_UINT16(&imhd->v7.width);
+ od->height = READ_LE_UINT16(&imhd->v7.height);
+ od->actordir = (byte)READ_LE_UINT16(&imhd->v7.actordir);
+
+ } else if (_version == 6) {
+ od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id));
+
+ od->width = READ_LE_UINT16(&cdhd->v6.w);
+ od->height = READ_LE_UINT16(&cdhd->v6.h);
+ od->x_pos = ((int16)READ_LE_UINT16(&cdhd->v6.x));
+ od->y_pos = ((int16)READ_LE_UINT16(&cdhd->v6.y));
+ if (cdhd->v6.flags == 0x80) {
+ od->parentstate = 1;
+ } else {
+ od->parentstate = (cdhd->v6.flags & 0xF);
+ }
+ od->parent = cdhd->v6.parent;
+ od->actordir = cdhd->v6.actordir;
+
+ if (_heversion >= 60 && imhd)
+ od->flags = ((imhd->old.flags & 1) != 0) ? Gdi::dbAllowMaskOr : 0;
+
+ } else {
+ od->obj_nr = READ_LE_UINT16(&(cdhd->v5.obj_id));
+
+ od->width = cdhd->v5.w * 8;
+ od->height = cdhd->v5.h * 8;
+ od->x_pos = cdhd->v5.x * 8;
+ od->y_pos = cdhd->v5.y * 8;
+ if (cdhd->v5.flags == 0x80) {
+ od->parentstate = 1;
+ } else {
+ od->parentstate = (cdhd->v5.flags & 0xF);
+ }
+ od->parent = cdhd->v5.parent;
+ od->walk_x = READ_LE_UINT16(&cdhd->v5.walk_x);
+ od->walk_y = READ_LE_UINT16(&cdhd->v5.walk_y);
+ od->actordir = cdhd->v5.actordir;
+ }
+
+ od->fl_object_index = 0;
+}
+
+void ScummEngine::updateObjectStates() {
+ int i;
+ ObjectData *od = &_objs[1];
+ for (i = 1; i < _numLocalObjects; i++, od++) {
+ if (od->obj_nr > 0)
+ od->state = getState(od->obj_nr);
+ }
+}
+
+void ScummEngine::processDrawQue() {
+ int i, j;
+ for (i = 0; i < _drawObjectQueNr; i++) {
+ j = _drawObjectQue[i];
+ if (j)
+ drawObject(j, 0);
+ }
+ _drawObjectQueNr = 0;
+}
+
+void ScummEngine::addObjectToDrawQue(int object) {
+ if ((unsigned int)_drawObjectQueNr >= ARRAYSIZE(_drawObjectQue))
+ error("Draw Object Que overflow");
+ _drawObjectQue[_drawObjectQueNr++] = object;
+}
+
+void ScummEngine::removeObjectFromDrawQue(int object) {
+ if (_drawObjectQueNr <= 0)
+ return;
+
+ int i;
+ for (i = 0; i < _drawObjectQueNr; i++) {
+ if (_drawObjectQue[i] == object)
+ _drawObjectQue[i] = 0;
+ }
+}
+
+void ScummEngine::clearDrawObjectQueue() {
+ _drawObjectQueNr = 0;
+}
+
+void ScummEngine::clearDrawQueues() {
+ clearDrawObjectQueue();
+}
+
+void ScummEngine_v6::clearDrawQueues() {
+ ScummEngine::clearDrawQueues();
+
+ _blastObjectQueuePos = 0;
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v70he::clearDrawQueues() {
+ ScummEngine_v6::clearDrawQueues();
+
+ _wiz->polygonClear();
+}
+
+void ScummEngine_v80he::clearDrawQueues() {
+ ScummEngine_v70he::clearDrawQueues();
+
+ _wiz->clearWizBuffer();
+}
+#endif
+
+void ScummEngine::clearOwnerOf(int obj) {
+ int i, j;
+ uint16 *a;
+
+ stopObjectScript(obj);
+
+ if (getOwner(obj) == OF_OWNER_ROOM) {
+ i = 0;
+ do {
+ if (_objs[i].obj_nr == obj) {
+ if (!_objs[i].fl_object_index)
+ return;
+ res.nukeResource(rtFlObject, _objs[i].fl_object_index);
+ _objs[i].obj_nr = 0;
+ _objs[i].fl_object_index = 0;
+ }
+ } while (++i < _numLocalObjects);
+ return;
+ }
+
+ for (i = 0; i < _numInventory; i++) {
+ if (_inventory[i] == obj) {
+ j = whereIsObject(obj);
+ if (j == WIO_INVENTORY) {
+ res.nukeResource(rtInventory, i);
+ _inventory[i] = 0;
+ }
+ a = _inventory;
+ for (i = 0; i < _numInventory - 1; i++, a++) {
+ if (!a[0] && a[1]) {
+ a[0] = a[1];
+ a[1] = 0;
+ res.address[rtInventory][i] = res.address[rtInventory][i + 1];
+ res.address[rtInventory][i + 1] = NULL;
+ }
+ }
+ return;
+ }
+ }
+}
+
+/**
+ * Mark the rectangle covered by the given object as dirty, thus eventually
+ * ensuring a redraw of that area. This function is typically invoked when an
+ * object gets removed from the current room, or when its state changed.
+ */
+void ScummEngine::markObjectRectAsDirty(int obj) {
+ int i, strip;
+
+ for (i = 1; i < _numLocalObjects; i++) {
+ if (_objs[i].obj_nr == (uint16)obj) {
+ if (_objs[i].width != 0) {
+ const int minStrip = MAX(_screenStartStrip, _objs[i].x_pos / 8);
+ const int maxStrip = MIN(_screenEndStrip+1, _objs[i].x_pos / 8 + _objs[i].width / 8);
+ for (strip = minStrip; strip < maxStrip; strip++) {
+ setGfxUsageBit(strip, USAGE_BIT_DIRTY);
+ }
+ }
+ _bgNeedsRedraw = true;
+ return;
+ }
+ }
+}
+
+const byte *ScummEngine::getObjOrActorName(int obj) {
+ byte *objptr;
+ int i;
+
+ if (obj < _numActors)
+ return derefActor(obj, "getObjOrActorName")->getActorName();
+
+ for (i = 0; i < _numNewNames; i++) {
+ if (_newNames[i] == obj) {
+ debug(5, "Found new name for object %d at _newNames[%d]", obj, i);
+ return getResourceAddress(rtObjectName, i);
+ }
+ }
+
+ objptr = getOBCDFromObject(obj);
+ if (objptr == NULL)
+ return NULL;
+
+ if (_features & GF_SMALL_HEADER) {
+ byte offset = 0;
+
+ if (_version <= 2)
+ offset = *(objptr + 14);
+ else if (_features & GF_OLD_BUNDLE)
+ offset = *(objptr + 16);
+ else
+ offset = *(objptr + 18);
+
+ return (objptr + offset);
+ }
+
+ return findResourceData(MKID('OBNA'), objptr);
+}
+
+void ScummEngine::setObjectName(int obj) {
+ int i;
+
+ if (obj < _numActors)
+ error("Can't set actor %d name with new-name-of", obj);
+
+ for (i = 0; i < _numNewNames; i++) {
+ if (_newNames[i] == obj) {
+ res.nukeResource(rtObjectName, i);
+ _newNames[i] = 0;
+ break;
+ }
+ }
+
+ for (i = 0; i < _numNewNames; i++) {
+ if (_newNames[i] == 0) {
+ loadPtrToResource(rtObjectName, i, NULL);
+ _newNames[i] = obj;
+ runInventoryScript(0);
+ return;
+ }
+ }
+
+ error("New name of %d overflows name table (max = %d)", obj, _numNewNames);
+}
+
+uint32 ScummEngine::getOBCDOffs(int object) const {
+ int i;
+
+ if (_objectOwnerTable[object] != OF_OWNER_ROOM)
+ return 0;
+ for (i = (_numLocalObjects-1); i > 0; i--) {
+ if (_objs[i].obj_nr == object) {
+ if (_objs[i].fl_object_index != 0)
+ return 8;
+ return _objs[i].OBCDoffset;
+ }
+ }
+ return 0;
+}
+
+byte *ScummEngine::getOBCDFromObject(int obj) {
+ int i;
+ byte *ptr;
+
+ if (_objectOwnerTable[obj] != OF_OWNER_ROOM) {
+ for (i = 0; i < _numInventory; i++) {
+ if (_inventory[i] == obj)
+ return getResourceAddress(rtInventory, i);
+ }
+ } else {
+ for (i = (_numLocalObjects-1); i > 0; --i) {
+ if (_objs[i].obj_nr == obj) {
+ if (_objs[i].fl_object_index) {
+ assert(_objs[i].OBCDoffset == 8);
+ ptr = getResourceAddress(rtFlObject, _objs[i].fl_object_index);
+ } else if (_version == 8)
+ ptr = getResourceAddress(rtRoomScripts, _roomResource);
+ else
+ ptr = getResourceAddress(rtRoom, _roomResource);
+ assert(ptr);
+ return ptr + _objs[i].OBCDoffset;
+ }
+ }
+ }
+ return 0;
+}
+
+const byte *ScummEngine::getOBIMFromObjectData(const ObjectData &od) {
+ const byte *ptr;
+
+ if (od.fl_object_index) {
+ ptr = getResourceAddress(rtFlObject, od.fl_object_index);
+ ptr = findResource(MKID('OBIM'), ptr);
+ } else {
+ ptr = getResourceAddress(rtRoom, _roomResource);
+ if (ptr)
+ ptr += od.OBIMoffset;
+ }
+ return ptr;
+}
+
+const byte *ScummEngine::getObjectImage(const byte *ptr, int state) {
+ assert(ptr);
+ if (_features & GF_OLD_BUNDLE)
+ ptr += 0;
+ else if (_features & GF_SMALL_HEADER) {
+ ptr += 8;
+ } else if (_version == 8) {
+ // The OBIM contains an IMAG, which in turn contains a WRAP, which contains
+ // an OFFS chunk and multiple BOMP/SMAP chunks. To find the right BOMP/SMAP,
+ // we use the offsets in the OFFS chunk,
+ ptr = findResource(MKID('IMAG'), ptr);
+ if (!ptr)
+ return 0;
+
+ ptr = findResource(MKID('WRAP'), ptr);
+ if (!ptr)
+ return 0;
+
+ ptr = findResource(MKID('OFFS'), ptr);
+ if (!ptr)
+ return 0;
+
+ // Get the address of the specified SMAP (corresponding to IMxx)
+ ptr += READ_LE_UINT32(ptr + 4 + 4*state);
+ } else {
+ ptr = findResource(IMxx_tags[state], ptr);
+ }
+
+ return ptr;
+}
+
+int ScummEngine::getObjectImageCount(int object) {
+ const byte *ptr;
+ const ImageHeader *imhd;
+ int objnum;
+
+ objnum = getObjectIndex(object);
+ if (objnum == -1)
+ return 0;
+
+ ptr = getOBIMFromObjectData(_objs[objnum]);
+ imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), ptr);
+ if (!imhd)
+ return 0;
+
+ if (_version == 8) {
+ return (READ_LE_UINT32(&imhd->v8.image_count));
+ } else if (_version == 7) {
+ return(READ_LE_UINT16(&imhd->v7.image_count));
+ } else {
+ return (READ_LE_UINT16(&imhd->old.image_count));
+ }
+}
+
+#ifndef DISABLE_SCUMM_7_8
+int ScummEngine_v8::getObjectIdFromOBIM(const byte *obim) {
+ // In V8, IMHD has no obj_id, but rather a name string. We map the name
+ // back to an object id using a table derived from the DOBJ resource.
+ const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), obim);
+ ObjectNameId *found = (ObjectNameId *)bsearch(imhd->v8.name, _objectIDMap, _objectIDMapSize,
+ sizeof(ObjectNameId), (int (*)(const void*, const void*))strcmp);
+ assert(found);
+ return found->id;
+}
+
+int ScummEngine_v7::getObjectIdFromOBIM(const byte *obim) {
+ const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), obim);
+ return READ_LE_UINT16(&imhd->v7.obj_id);
+}
+#endif
+
+int ScummEngine::getObjectIdFromOBIM(const byte *obim) {
+ if (_features & GF_SMALL_HEADER)
+ return READ_LE_UINT16(obim + 6);
+
+ const ImageHeader *imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), obim);
+ return READ_LE_UINT16(&imhd->old.obj_id);
+}
+
+void ScummEngine::addObjectToInventory(uint obj, uint room) {
+ int idx, slot;
+ uint32 size;
+ const byte *ptr;
+ byte *dst;
+ FindObjectInRoom foir;
+
+ debug(1, "Adding object %d from room %d into inventory", obj, room);
+
+ CHECK_HEAP
+ if (whereIsObject(obj) == WIO_FLOBJECT) {
+ idx = getObjectIndex(obj);
+ assert(idx >= 0);
+ ptr = getResourceAddress(rtFlObject, _objs[idx].fl_object_index) + 8;
+ size = READ_BE_UINT32(ptr + 4);
+ } else {
+ findObjectInRoom(&foir, foCodeHeader, obj, room);
+ if (_features & GF_OLD_BUNDLE)
+ size = READ_LE_UINT16(foir.obcd);
+ else if (_features & GF_SMALL_HEADER)
+ size = READ_LE_UINT32(foir.obcd);
+ else
+ size = READ_BE_UINT32(foir.obcd + 4);
+ ptr = foir.obcd;
+ }
+
+ slot = getInventorySlot();
+ _inventory[slot] = obj;
+ dst = res.createResource(rtInventory, slot, size);
+ assert(dst);
+ memcpy(dst, ptr, size);
+
+ CHECK_HEAP
+}
+
+void ScummEngine::findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint id, uint room) {
+
+ const CodeHeader *cdhd;
+ int i, numobj;
+ const byte *roomptr, *obcdptr, *obimptr, *searchptr;
+ int id2;
+ int obim_id;
+
+ id2 = getObjectIndex(id);
+ if (findWhat & foCheckAlreadyLoaded && id2 != -1) {
+ assert(_version >= 6);
+ if (findWhat & foCodeHeader) {
+ fo->obcd = obcdptr = getOBCDFromObject(id);
+ assert(obcdptr);
+ fo->cdhd = (const CodeHeader *)findResourceData(MKID('CDHD'), obcdptr);
+ }
+ if (findWhat & foImageHeader) {
+ fo->obim = obimptr = getOBIMFromObjectData(_objs[id2]);
+ assert(obimptr);
+ }
+ return;
+ }
+
+ fo->roomptr = roomptr = getResourceAddress(rtRoom, room);
+ if (!roomptr)
+ error("findObjectInRoom: failed getting roomptr to %d", room);
+
+ if (_features & GF_OLD_BUNDLE) {
+ numobj = roomptr[20];
+ } else {
+ const RoomHeader *roomhdr = (const RoomHeader *)findResourceData(MKID('RMHD'), roomptr);
+
+ if (_version == 8)
+ numobj = READ_LE_UINT32(&(roomhdr->v8.numObjects));
+ else if (_version == 7)
+ numobj = READ_LE_UINT16(&(roomhdr->v7.numObjects));
+ else
+ numobj = READ_LE_UINT16(&(roomhdr->old.numObjects));
+ }
+
+ if (numobj == 0)
+ error("findObjectInRoom: No object found in room %d", room);
+ if (numobj > _numLocalObjects)
+ error("findObjectInRoom: More (%d) than %d objects in room %d", numobj, _numLocalObjects, room);
+
+ if (_features & GF_OLD_BUNDLE) {
+ if (_version <= 2)
+ searchptr = roomptr + 28;
+ else
+ searchptr = roomptr + 29;
+
+ for (i = 0; i < numobj; i++) {
+ obimptr = roomptr + READ_LE_UINT16(searchptr);
+ obcdptr = roomptr + READ_LE_UINT16(searchptr + 2 * numobj);
+ id2 = READ_LE_UINT16(obcdptr + 4);
+
+ if (id2 == (uint16)id) {
+ if (findWhat & foCodeHeader) {
+ fo->obcd = obcdptr;
+ fo->cdhd = (const CodeHeader *)(obcdptr + 10); // TODO - FIXME
+ }
+ if (findWhat & foImageHeader) {
+ fo->obim = obimptr;
+ }
+ break;
+ }
+ searchptr += 2;
+ }
+ return;
+ }
+
+ if (findWhat & foCodeHeader) {
+ if (_version == 8)
+ searchptr = getResourceAddress(rtRoomScripts, room);
+ else
+ searchptr = roomptr;
+ assert(searchptr);
+ ResourceIterator obcds(searchptr, (_features & GF_SMALL_HEADER) != 0);
+ for (i = 0; i < numobj; i++) {
+ obcdptr = obcds.findNext(MKID('OBCD'));
+ if (obcdptr == NULL)
+ error("findObjectInRoom: Not enough code blocks in room %d", room);
+ cdhd = (const CodeHeader *)findResourceData(MKID('CDHD'), obcdptr);
+
+ if (_features & GF_SMALL_HEADER)
+ id2 = READ_LE_UINT16(obcdptr + 6);
+ else if (_version >= 7)
+ id2 = READ_LE_UINT16(&(cdhd->v7.obj_id));
+ else if (_version == 6)
+ id2 = READ_LE_UINT16(&(cdhd->v6.obj_id));
+ else
+ id2 = READ_LE_UINT16(&(cdhd->v5.obj_id));
+
+ if (id2 == (uint16)id) {
+ fo->obcd = obcdptr;
+ fo->cdhd = cdhd;
+ break;
+ }
+ }
+ if (i == numobj)
+ error("findObjectInRoom: Object %d not found in room %d", id, room);
+ }
+
+ roomptr = fo->roomptr;
+ if (findWhat & foImageHeader) {
+ ResourceIterator obims(roomptr, (_features & GF_SMALL_HEADER) != 0);
+ for (i = 0; i < numobj; i++) {
+ obimptr = obims.findNext(MKID('OBIM'));
+ if (obimptr == NULL)
+ error("findObjectInRoom: Not enough image blocks in room %d", room);
+ obim_id = getObjectIdFromOBIM(obimptr);
+
+ if (obim_id == (uint16)id) {
+ fo->obim = obimptr;
+ break;
+ }
+ }
+ if (i == numobj)
+ error("findObjectInRoom: Object %d image not found in room %d", id, room);
+ }
+}
+
+int ScummEngine::getInventorySlot() {
+ int i;
+ for (i = 0; i < _numInventory; i++) {
+ if (_inventory[i] == 0)
+ return i;
+ }
+ error("Inventory full, %d max items", _numInventory);
+ return -1;
+}
+
+void ScummEngine::setOwnerOf(int obj, int owner) {
+ ScriptSlot *ss;
+
+ // In Sam & Max this is necessary, or you won't get your stuff back
+ // from the Lost and Found tent after riding the Cone of Tragedy. But
+ // it probably applies to all V6+ games. See bugs #493153 and #907113.
+ // FT disassembly is checked, behaviour is correct. [sev]
+
+ int arg = (_version >= 6) ? obj : 0;
+
+ if (owner == 0) {
+ clearOwnerOf(obj);
+ ss = &vm.slot[_currentScript];
+ if (ss->where == WIO_INVENTORY && _inventory[ss->number] == obj) {
+ putOwner(obj, 0);
+ runInventoryScript(arg);
+ stopObjectCode();
+ return;
+ }
+ }
+
+ putOwner(obj, owner);
+ runInventoryScript(arg);
+}
+
+int ScummEngine::getObjX(int obj) {
+ if (obj < _numActors) {
+ if (obj < 1)
+ return 0; /* fix for indy4's map */
+ return derefActor(obj, "getObjX")->_pos.x;
+ } else {
+ if (whereIsObject(obj) == WIO_NOT_FOUND)
+ return -1;
+ int x, y;
+ getObjectOrActorXY(obj, x, y);
+ return x;
+ }
+}
+
+int ScummEngine::getObjY(int obj) {
+ if (obj < _numActors) {
+ if (obj < 1)
+ return 0; /* fix for indy4's map */
+ return derefActor(obj, "getObjY")->_pos.y;
+ } else {
+ if (whereIsObject(obj) == WIO_NOT_FOUND)
+ return -1;
+ int x, y;
+ getObjectOrActorXY(obj, x, y);
+ return y;
+ }
+}
+
+int ScummEngine::getObjOldDir(int obj) {
+ return newDirToOldDir(getObjNewDir(obj));
+}
+
+int ScummEngine::getObjNewDir(int obj) {
+ int dir;
+ if (obj < _numActors) {
+ dir = derefActor(obj, "getObjNewDir")->getFacing();
+ } else {
+ int x, y;
+ getObjectXYPos(obj, x, y, dir);
+ }
+ return dir;
+}
+
+int ScummEngine::findInventory(int owner, int idx) {
+ int count = 1, i, obj;
+ for (i = 0; i < _numInventory; i++) {
+ obj = _inventory[i];
+ if (obj && getOwner(obj) == owner && count++ == idx)
+ return obj;
+ }
+ return 0;
+}
+
+int ScummEngine::getInventoryCount(int owner) {
+ int i, obj;
+ int count = 0;
+ for (i = 0; i < _numInventory; i++) {
+ obj = _inventory[i];
+ if (obj && getOwner(obj) == owner)
+ count++;
+ }
+ return count;
+}
+
+void ScummEngine::setObjectState(int obj, int state, int x, int y) {
+ int i;
+
+ i = getObjectIndex(obj);
+ if (i == -1) {
+ debug(0, "setObjectState: no such object %d", obj);
+ return;
+ }
+
+ if (x != -1 && x != 0x7FFFFFFF) {
+ _objs[i].x_pos = x * 8;
+ _objs[i].y_pos = y * 8;
+ }
+
+ addObjectToDrawQue(i);
+ if (_version >= 7) {
+ int imagecount;
+ if (state == 0xFF) {
+ state = getState(obj);
+ imagecount = getObjectImageCount(obj);
+
+ if (state < imagecount)
+ state++;
+ else
+ state = 1;
+ }
+
+ if (state == 0xFE)
+ state = _rnd.getRandomNumber(getObjectImageCount(obj));
+ }
+ putState(obj, state);
+}
+
+int ScummEngine::getDistanceBetween(bool is_obj_1, int b, int c, bool is_obj_2, int e, int f) {
+ int i, j;
+ int x, y;
+ int x2, y2;
+
+ j = i = 0xFF;
+
+ if (is_obj_1) {
+ if (getObjectOrActorXY(b, x, y) == -1)
+ return -1;
+ if (b < _numActors)
+ i = derefActor(b, "getDistanceBetween_is_obj_1")->_scalex;
+ } else {
+ x = b;
+ y = c;
+ }
+
+ if (is_obj_2) {
+ if (getObjectOrActorXY(e, x2, y2) == -1)
+ return -1;
+ if (e < _numActors)
+ j = derefActor(e, "getDistanceBetween_is_obj_2")->_scalex;
+ } else {
+ x2 = e;
+ y2 = f;
+ }
+
+ return getDist(x, y, x2, y2) * 0xFF / ((i + j) / 2);
+}
+
+void ScummEngine::nukeFlObjects(int min, int max) {
+ ObjectData *od;
+ int i;
+
+ debug(0, "nukeFlObjects(%d,%d)", min, max);
+
+ for (i = (_numLocalObjects-1), od = _objs; --i >= 0; od++)
+ if (od->fl_object_index && od->obj_nr >= min && od->obj_nr <= max) {
+ res.nukeResource(rtFlObject, od->fl_object_index);
+ od->obj_nr = 0;
+ od->fl_object_index = 0;
+ }
+}
+
+void ScummEngine_v6::enqueueObject(int objectNumber, int objectX, int objectY, int objectWidth,
+ int objectHeight, int scaleX, int scaleY, int image, int mode) {
+ BlastObject *eo;
+
+ if (_blastObjectQueuePos >= (int)ARRAYSIZE(_blastObjectQueue)) {
+ error("enqueueObject: overflow");
+ }
+
+ int idx = getObjectIndex(objectNumber);
+ assert(idx >= 0);
+
+ eo = &_blastObjectQueue[_blastObjectQueuePos++];
+ eo->number = objectNumber;
+ eo->rect.left = objectX;
+ eo->rect.top = objectY + _screenTop;
+ if (objectWidth == 0) {
+ eo->rect.right = eo->rect.left + _objs[idx].width;
+ } else {
+ eo->rect.right = eo->rect.left + objectWidth;
+ }
+ if (objectHeight == 0) {
+ eo->rect.bottom = eo->rect.top + _objs[idx].height;
+ } else {
+ eo->rect.bottom = eo->rect.top + objectHeight;
+ }
+
+ eo->scaleX = scaleX;
+ eo->scaleY = scaleY;
+ eo->image = image;
+
+ eo->mode = mode;
+}
+
+void ScummEngine_v6::drawBlastObjects() {
+ BlastObject *eo;
+ int i;
+
+ eo = _blastObjectQueue;
+ for (i = 0; i < _blastObjectQueuePos; i++, eo++) {
+ drawBlastObject(eo);
+ }
+}
+
+void ScummEngine_v6::drawBlastObject(BlastObject *eo) {
+ VirtScreen *vs;
+ const byte *bomp, *ptr;
+ int objnum;
+ BompDrawData bdd;
+
+ vs = &virtscr[0];
+
+ checkRange(_numGlobalObjects - 1, 30, eo->number, "Illegal Blast object %d");
+
+ objnum = getObjectIndex(eo->number);
+ if (objnum == -1)
+ error("drawBlastObject: getObjectIndex on BlastObject %d failed", eo->number);
+
+ ptr = getOBIMFromObjectData(_objs[objnum]);
+ if (!ptr)
+ error("BlastObject object %d image not found", eo->number);
+
+ const byte *img = getObjectImage(ptr, eo->image);
+ if (_version == 8) {
+ assert(img);
+ bomp = img + 8;
+ } else {
+ if (!img)
+ img = getObjectImage(ptr, 1); // Backward compatibility with samnmax blast objects
+ assert(img);
+ bomp = findResourceData(MKID('BOMP'), img);
+ }
+
+ if (!bomp)
+ error("object %d is not a blast object", eo->number);
+
+ if (_version == 8) {
+ bdd.srcwidth = READ_LE_UINT32(&((const BompHeader *)bomp)->v8.width);
+ bdd.srcheight = READ_LE_UINT32(&((const BompHeader *)bomp)->v8.height);
+ } else {
+ bdd.srcwidth = READ_LE_UINT16(&((const BompHeader *)bomp)->old.width);
+ bdd.srcheight = READ_LE_UINT16(&((const BompHeader *)bomp)->old.height);
+ }
+
+ bdd.dst = *vs;
+ bdd.dst.pixels = vs->getPixels(0, 0);
+ // Skip the bomp header
+ if (_version == 8) {
+ bdd.dataptr = bomp + 8;
+ } else {
+ bdd.dataptr = bomp + 10;
+ }
+ bdd.x = eo->rect.left;
+ bdd.y = eo->rect.top;
+ bdd.scale_x = (byte)eo->scaleX;
+ bdd.scale_y = (byte)eo->scaleY;
+ bdd.maskPtr = NULL;
+
+ if ((bdd.scale_x != 255) || (bdd.scale_y != 255)) {
+ bdd.shadowMode = 0;
+ } else {
+ bdd.shadowMode = eo->mode;
+ }
+ drawBomp(bdd, false);
+
+ markRectAsDirty(vs->number, bdd.x, bdd.x + bdd.srcwidth, bdd.y, bdd.y + bdd.srcheight);
+}
+
+void ScummEngine_v6::removeBlastObjects() {
+ BlastObject *eo;
+ int i;
+
+ eo = _blastObjectQueue;
+ for (i = 0; i < _blastObjectQueuePos; i++, eo++) {
+ removeBlastObject(eo);
+ }
+ _blastObjectQueuePos = 0;
+}
+
+void ScummEngine_v6::removeBlastObject(BlastObject *eo) {
+ VirtScreen *vs = &virtscr[0];
+
+ Common::Rect r;
+ int left_strip, right_strip;
+ int i;
+
+ r = eo->rect;
+
+ r.clip(Common::Rect(vs->w, vs->h));
+
+ if (r.width() <= 0 || r.height() <= 0)
+ return;
+
+ left_strip = r.left / 8;
+ right_strip = (r.right + (vs->xstart % 8)) / 8;
+
+ if (left_strip < 0)
+ left_strip = 0;
+ if (right_strip > gdi._numStrips - 1)
+ right_strip = gdi._numStrips - 1;
+ for (i = left_strip; i <= right_strip; i++)
+ gdi.resetBackground(r.top, r.bottom, i);
+
+ markRectAsDirty(kMainVirtScreen, r, USAGE_BIT_RESTORED);
+}
+
+int ScummEngine::findLocalObjectSlot() {
+ int i;
+
+ for (i = 1; i < _numLocalObjects; i++) {
+ if (!_objs[i].obj_nr) {
+ memset(&_objs[i], 0, sizeof(_objs[i]));
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int ScummEngine::findFlObjectSlot() {
+ int i;
+ for (i = 1; i < _numFlObject; i++) {
+ if (res.address[rtFlObject][i] == NULL)
+ return i;
+ }
+ error("findFlObjectSlot: Out of FLObject slots");
+ return -1;
+}
+
+void ScummEngine::loadFlObject(uint object, uint room) {
+ FindObjectInRoom foir;
+ int i, slot, objslot;
+ ObjectData *od;
+ byte *flob;
+ uint32 obcd_size, obim_size, flob_size;
+ bool isRoomLocked, isRoomScriptsLocked;
+
+ // Don't load an already loaded object
+ if (getObjectIndex(object) != -1)
+ return;
+
+ // Don't load an already stored object
+ for (i = 0; i < _numStoredFlObjects; i++) {
+ if (_storedFlObjects[i].obj_nr == object)
+ return;
+ }
+
+ // Locate the object in the room resource
+ findObjectInRoom(&foir, foImageHeader | foCodeHeader, object, room);
+
+ // Add an entry for the new floating object in the local object table
+ objslot = findLocalObjectSlot();
+ if (objslot == -1)
+ error("loadFlObject: Local Object Table overflow");
+
+ od = &_objs[objslot];
+
+ // Dump object script
+ if (_dumpScripts) {
+ char buf[32];
+ const byte *ptr = foir.obcd;
+ sprintf(buf, "roomobj-%d-", room);
+ ptr = findResource(MKID('VERB'), ptr);
+ dumpResource(buf, object, ptr);
+ }
+
+ // Setup sizes
+ obcd_size = READ_BE_UINT32(foir.obcd + 4);
+ od->OBCDoffset = 8;
+ od->OBIMoffset = obcd_size + 8;
+ obim_size = READ_BE_UINT32(foir.obim + 4);
+ flob_size = obcd_size + obim_size + 8;
+
+ // Lock room/roomScripts for the given room. They contains the OBCD/OBIM
+ // data, and a call to createResource might expire them, hence we lock them.
+ isRoomLocked = res.isLocked(rtRoom, room);
+ isRoomScriptsLocked = res.isLocked(rtRoomScripts, room);
+ if (!isRoomLocked)
+ res.lock(rtRoom, room);
+ if (_version == 8 && !isRoomScriptsLocked)
+ res.lock(rtRoomScripts, room);
+
+ // Allocate slot & memory for floating object
+ slot = findFlObjectSlot();
+ flob = res.createResource(rtFlObject, slot, flob_size);
+ assert(flob);
+
+ // Copy object code + object image to floating object
+ ((uint32 *)flob)[0] = MKID('FLOB');
+ ((uint32 *)flob)[1] = TO_BE_32(flob_size);
+
+ memcpy(flob + 8, foir.obcd, obcd_size);
+ memcpy(flob + 8 + obcd_size, foir.obim, obim_size);
+
+ // Unlock room/roomScripts
+ if (!isRoomLocked)
+ res.unlock(rtRoom, room);
+ if (_version == 8 && !isRoomScriptsLocked)
+ res.unlock(rtRoomScripts, room);
+
+ // Setup local object flags
+ setupRoomObject(od, flob, flob);
+
+ od->fl_object_index = slot;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/object.h b/engines/scumm/object.h
new file mode 100644
index 0000000000..edb779b5eb
--- /dev/null
+++ b/engines/scumm/object.h
@@ -0,0 +1,182 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef OBJECT_H
+#define OBJECT_H
+
+namespace Scumm {
+
+enum ObjectClass {
+ kObjectClassNeverClip = 20,
+ kObjectClassAlwaysClip = 21,
+ kObjectClassIgnoreBoxes = 22,
+ kObjectClassYFlip = 29,
+ kObjectClassXFlip = 30,
+ kObjectClassPlayer = 31, // Actor is controlled by the player
+ kObjectClassUntouchable = 32
+};
+
+struct ObjectData {
+ uint32 OBIMoffset;
+ uint32 OBCDoffset;
+ int16 walk_x, walk_y;
+ uint16 obj_nr;
+ int16 x_pos;
+ int16 y_pos;
+ uint16 width;
+ uint16 height;
+ byte actordir;
+ byte parent;
+ byte parentstate;
+ byte state;
+ byte fl_object_index;
+ byte flags;
+};
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct RoomHeader {
+ union {
+ struct {
+ uint16 width, height;
+ uint16 numObjects;
+ } GCC_PACK old;
+
+ struct {
+ uint32 version;
+ uint16 width, height;
+ uint16 numObjects;
+ } GCC_PACK v7;
+
+ struct {
+ uint32 version;
+ uint32 width, height;
+ uint32 numObjects;
+ uint32 numZBuffer;
+ uint32 transparency;
+ } GCC_PACK v8;
+ } GCC_PACK;
+} GCC_PACK;
+
+struct CodeHeader {
+ union {
+ struct {
+ uint16 obj_id;
+ byte x, y, w, h;
+ byte flags;
+ byte parent;
+ int16 walk_x;
+ int16 walk_y;
+ byte actordir;
+ } GCC_PACK v5;
+
+ struct {
+ uint16 obj_id;
+ int16 x, y;
+ uint16 w, h;
+ byte flags, parent;
+ uint16 unk1;
+ uint16 unk2;
+ byte actordir;
+ } GCC_PACK v6;
+
+ struct {
+ uint32 version;
+ uint16 obj_id;
+ byte parent;
+ byte parentstate;
+ } GCC_PACK v7;
+
+ } GCC_PACK;
+} GCC_PACK;
+
+struct ImageHeader { /* file format */
+ union {
+ struct {
+ uint16 obj_id;
+ uint16 image_count;
+ uint16 unk[1];
+ byte flags;
+ byte unk1;
+ uint16 unk2[2];
+ uint16 width;
+ uint16 height;
+ uint16 hotspot_num;
+ struct {
+ int16 x, y;
+ } GCC_PACK hotspot[15];
+ } GCC_PACK old;
+
+ struct {
+ uint32 version;
+ uint16 obj_id;
+ uint16 image_count;
+ int16 x_pos, y_pos;
+ uint16 width, height;
+ byte unk2[3];
+ byte actordir;
+ uint16 hotspot_num;
+ struct {
+ int16 x, y;
+ } GCC_PACK hotspot[15];
+ } GCC_PACK v7;
+
+ struct {
+ char name[32];
+ uint32 unk_1[2];
+ uint32 version; // 801 in COMI, 800 in the COMI demo
+ uint32 image_count;
+ uint32 x_pos;
+ uint32 y_pos;
+ uint32 width;
+ uint32 height;
+ uint32 actordir;
+ uint32 flags; // This field is missing in the COMI demo (version == 800) !
+ struct {
+ int32 x, y;
+ } GCC_PACK hotspot[15];
+ } GCC_PACK v8;
+ } GCC_PACK;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+struct FindObjectInRoom {
+ const CodeHeader *cdhd;
+ const byte *obcd;
+ const byte *obim;
+ const byte *roomptr;
+};
+
+enum FindObjectWhat {
+ foCodeHeader = 1,
+ foImageHeader = 2,
+ foCheckAlreadyLoaded = 4
+};
+
+} // End of namespace Scumm
+
+
+#endif
diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
new file mode 100644
index 0000000000..d8b8f643f1
--- /dev/null
+++ b/engines/scumm/palette.cpp
@@ -0,0 +1,969 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/util.h"
+
+#include "scumm/scumm.h"
+#include "scumm/intern.h"
+#include "scumm/resource.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+void ScummEngine::setupC64Palette() {
+ setPalColor( 0, 0x00, 0x00, 0x00);
+ setPalColor( 1, 0xFD, 0xFE, 0xFC);
+ setPalColor( 2, 0xBE, 0x1A, 0x24);
+ setPalColor( 3, 0x30, 0xE6, 0xC6);
+ setPalColor( 4, 0xB4, 0x1A, 0xE2);
+ setPalColor( 5, 0x1F, 0xD2, 0x1E);
+ setPalColor( 6, 0x21, 0x1B, 0xAE);
+ setPalColor( 7, 0xDF, 0xF6, 0x0A);
+ setPalColor( 8, 0xB8, 0x41, 0x04);
+ setPalColor( 9, 0x6A, 0x33, 0x04);
+ setPalColor(10, 0xFE, 0x4A, 0x57);
+ setPalColor(11, 0x42, 0x45, 0x40);
+ setPalColor(12, 0x70, 0x74, 0x6F);
+ setPalColor(13, 0x59, 0xFE, 0x59);
+ setPalColor(14, 0x5F, 0x53, 0xFE);
+ setPalColor(15, 0xA4, 0xA7, 0xA2);
+
+ setPalColor(16, 255, 85, 255);
+}
+
+void ScummEngine::setupNESPalette() {
+ setPalColor(0x00,0x24,0x24,0x24); // 0x1D
+ setPalColor(0x01,0x00,0x24,0x92);
+ setPalColor(0x02,0x00,0x00,0xDB);
+ setPalColor(0x03,0x6D,0x49,0xDB);
+ setPalColor(0x04,0x92,0x00,0x6D);
+ setPalColor(0x05,0xB6,0x00,0x6D);
+ setPalColor(0x06,0xB6,0x24,0x00);
+ setPalColor(0x07,0x92,0x49,0x00);
+ setPalColor(0x08,0x6D,0x49,0x00);
+ setPalColor(0x09,0x24,0x49,0x00);
+ setPalColor(0x0A,0x00,0x6D,0x24);
+ setPalColor(0x0B,0x00,0x92,0x00);
+ setPalColor(0x0C,0x00,0x49,0x49);
+ setPalColor(0x0D,0x00,0x00,0x00);
+ setPalColor(0x0E,0x00,0x00,0x00);
+ setPalColor(0x0F,0x00,0x00,0x00);
+
+ setPalColor(0x10,0xB6,0xB6,0xB6);
+ setPalColor(0x11,0x00,0x6D,0xDB);
+ setPalColor(0x12,0x00,0x49,0xFF);
+ setPalColor(0x13,0x92,0x00,0xFF);
+ setPalColor(0x14,0xB6,0x00,0xFF);
+ setPalColor(0x15,0xFF,0x00,0x92);
+ setPalColor(0x16,0xFF,0x00,0x00);
+ setPalColor(0x17,0xDB,0x6D,0x00);
+ setPalColor(0x18,0x92,0x6D,0x00);
+ setPalColor(0x19,0x24,0x92,0x00);
+ setPalColor(0x1A,0x00,0x92,0x00);
+ setPalColor(0x1B,0x00,0xB6,0x6D);
+ setPalColor(0x1C,0x00,0x92,0x92);
+ setPalColor(0x1D,0x6D,0x6D,0x6D); // 0x00
+ setPalColor(0x1E,0x00,0x00,0x00);
+ setPalColor(0x1F,0x00,0x00,0x00);
+
+ setPalColor(0x20,0xFF,0xFF,0xFF);
+ setPalColor(0x21,0x6D,0xB6,0xFF);
+ setPalColor(0x22,0x92,0x92,0xFF);
+ setPalColor(0x23,0xDB,0x6D,0xFF);
+ setPalColor(0x24,0xFF,0x00,0xFF);
+ setPalColor(0x25,0xFF,0x6D,0xFF);
+ setPalColor(0x26,0xFF,0x92,0x00);
+ setPalColor(0x27,0xFF,0xB6,0x00);
+ setPalColor(0x28,0xDB,0xDB,0x00);
+ setPalColor(0x29,0x6D,0xDB,0x00);
+ setPalColor(0x2A,0x00,0xFF,0x00);
+ setPalColor(0x2B,0x49,0xFF,0xDB);
+ setPalColor(0x2C,0x00,0xFF,0xFF);
+ setPalColor(0x2D,0x49,0x49,0x49);
+ setPalColor(0x2E,0x00,0x00,0x00);
+ setPalColor(0x2F,0x00,0x00,0x00);
+
+ setPalColor(0x30,0xFF,0xFF,0xFF);
+ setPalColor(0x31,0xB6,0xDB,0xFF);
+ setPalColor(0x32,0xDB,0xB6,0xFF);
+ setPalColor(0x33,0xFF,0xB6,0xFF);
+ setPalColor(0x34,0xFF,0x92,0xFF);
+ setPalColor(0x35,0xFF,0xB6,0xB6);
+ setPalColor(0x36,0xFF,0xDB,0x92);
+ setPalColor(0x37,0xFF,0xFF,0x49);
+ setPalColor(0x38,0xFF,0xFF,0x6D);
+ setPalColor(0x39,0xB6,0xFF,0x49);
+ setPalColor(0x3A,0x92,0xFF,0x6D);
+ setPalColor(0x3B,0x49,0xFF,0xDB);
+ setPalColor(0x3C,0x92,0xDB,0xFF);
+ setPalColor(0x3D,0x92,0x92,0x92);
+ setPalColor(0x3E,0x00,0x00,0x00);
+ setPalColor(0x3F,0x00,0x00,0x00);
+}
+
+void ScummEngine::setupAmigaPalette() {
+ setPalColor( 0, 0, 0, 0);
+ setPalColor( 1, 0, 0, 187);
+ setPalColor( 2, 0, 187, 0);
+ setPalColor( 3, 0, 187, 187);
+ setPalColor( 4, 187, 0, 0);
+ setPalColor( 5, 187, 0, 187);
+ setPalColor( 6, 187, 119, 0);
+ setPalColor( 7, 187, 187, 187);
+ setPalColor( 8, 119, 119, 119);
+ setPalColor( 9, 119, 119, 255);
+ setPalColor(10, 0, 255, 0);
+ setPalColor(11, 0, 255, 255);
+ setPalColor(12, 255, 136, 136);
+ setPalColor(13, 255, 0, 255);
+ setPalColor(14, 255, 255, 0);
+ setPalColor(15, 255, 255, 255);
+}
+
+void ScummEngine::setupHercPalette() {
+ setPalColor( 0, 0, 0, 0);
+
+ if (_renderMode == Common::kRenderHercA)
+ setPalColor( 1, 0xAE, 0x69, 0x38);
+ else
+ setPalColor( 1, 0x00, 0xFF, 0x00);
+
+ // Setup cursor palette
+ setPalColor( 7, 170, 170, 170);
+ setPalColor( 8, 85, 85, 85);
+ setPalColor(15, 255, 255, 255);
+}
+
+void ScummEngine::setupCGAPalette() {
+ setPalColor( 0, 0, 0, 0);
+ setPalColor( 1, 0, 168, 168);
+ setPalColor( 2, 168, 0, 168);
+ setPalColor( 3, 168, 168, 168);
+
+ // Setup cursor palette
+ setPalColor( 7, 170, 170, 170);
+ setPalColor( 8, 85, 85, 85);
+ setPalColor(15, 255, 255, 255);
+}
+
+void ScummEngine::setupEGAPalette() {
+ setPalColor( 0, 0, 0, 0);
+ setPalColor( 1, 0, 0, 170);
+ setPalColor( 2, 0, 170, 0);
+ setPalColor( 3, 0, 170, 170);
+ setPalColor( 4, 170, 0, 0);
+ setPalColor( 5, 170, 0, 170);
+ setPalColor( 6, 170, 85, 0);
+ setPalColor( 7, 170, 170, 170);
+ setPalColor( 8, 85, 85, 85);
+ setPalColor( 9, 85, 85, 255);
+ setPalColor(10, 85, 255, 85);
+ setPalColor(11, 85, 255, 255);
+ setPalColor(12, 255, 85, 85);
+ setPalColor(13, 255, 85, 255);
+ setPalColor(14, 255, 255, 85);
+ setPalColor(15, 255, 255, 255);
+}
+
+void ScummEngine::setupV1Palette() {
+ setPalColor( 0, 0, 0, 0);
+ setPalColor( 1, 255, 255, 255);
+ setPalColor( 2, 170, 0, 0);
+ setPalColor( 3, 0, 170, 170);
+ setPalColor( 4, 170, 0, 170);
+ setPalColor( 5, 0, 170, 0);
+ setPalColor( 6, 0, 0, 170);
+ setPalColor( 7, 255, 255, 85);
+ setPalColor( 8, 255, 85, 85);
+ setPalColor( 9, 170, 85, 0);
+ setPalColor(10, 255, 85, 85);
+ setPalColor(11, 85, 85, 85);
+ setPalColor(12, 170, 170, 170);
+ setPalColor(13, 85, 255, 85);
+ setPalColor(14, 85, 85, 255);
+
+ if (_gameId == GID_ZAK)
+ setPalColor(15, 170, 170, 170);
+ else
+ setPalColor(15, 85, 85, 85);
+
+ setPalColor(16, 255, 85, 255);
+}
+
+void ScummEngine::setPaletteFromPtr(const byte *ptr, int numcolor) {
+ int i;
+ byte *dest, r, g, b;
+
+ if (numcolor < 0) {
+ if (_features & GF_SMALL_HEADER) {
+ if (_features & GF_OLD256)
+ numcolor = READ_LE_UINT16(ptr);
+ else
+ numcolor = READ_LE_UINT16(ptr) / 3;
+ ptr += 2;
+ } else {
+ numcolor = getResourceDataSize(ptr) / 3;
+ }
+ }
+
+ checkRange(256, 0, numcolor, "Too many colors (%d) in Palette");
+
+ dest = _currentPalette;
+
+ for (i = 0; i < numcolor; i++) {
+ r = *ptr++;
+ g = *ptr++;
+ b = *ptr++;
+
+ // Only SCUMM 5/6 games use 6/6/6 style palettes
+ if (_version >= 5 && _version <= 6) {
+ if ((_heversion <= 73 && i < 15) || i == 15 || r < 252 || g < 252 || b < 252) {
+ *dest++ = r;
+ *dest++ = g;
+ *dest++ = b;
+ } else {
+ dest += 3;
+ }
+ } else {
+ *dest++ = r;
+ *dest++ = g;
+ *dest++ = b;
+ }
+ }
+
+ if (_heversion >= 90 || _version == 8) {
+ memcpy(_darkenPalette, _currentPalette, 768);
+ }
+
+ setDirtyColors(0, numcolor - 1);
+}
+
+void ScummEngine::setDirtyColors(int min, int max) {
+ if (_palDirtyMin > min)
+ _palDirtyMin = min;
+ if (_palDirtyMax < max)
+ _palDirtyMax = max;
+}
+
+void ScummEngine::initCycl(const byte *ptr) {
+ int j;
+ ColorCycle *cycl;
+
+ memset(_colorCycle, 0, sizeof(_colorCycle));
+
+ if (_features & GF_SMALL_HEADER) {
+ cycl = _colorCycle;
+ for (j = 0; j < 16; ++j, ++cycl) {
+ uint16 delay = READ_BE_UINT16(ptr);
+ ptr += 2;
+ byte start = *ptr++;
+ byte end = *ptr++;
+
+ if (!delay || delay == 0x0aaa || start >= end)
+ continue;
+
+ cycl->counter = 0;
+ cycl->delay = 16384 / delay;
+ cycl->flags = 2;
+ cycl->start = start;
+ cycl->end = end;
+ }
+ } else {
+ memset(_colorUsedByCycle, 0, sizeof(_colorUsedByCycle));
+ while ((j = *ptr++) != 0) {
+ if (j < 1 || j > 16) {
+ error("Invalid color cycle index %d", j);
+ }
+ cycl = &_colorCycle[j - 1];
+
+ ptr += 2;
+ cycl->counter = 0;
+ cycl->delay = 16384 / READ_BE_UINT16(ptr);
+ ptr += 2;
+ cycl->flags = READ_BE_UINT16(ptr);
+ ptr += 2;
+ cycl->start = *ptr++;
+ cycl->end = *ptr++;
+
+ for (int i = cycl->start; i <= cycl->end; ++i) {
+ _colorUsedByCycle[i] = 1;
+ }
+ }
+ }
+}
+
+void ScummEngine::stopCycle(int i) {
+ ColorCycle *cycl;
+
+ checkRange(16, 0, i, "Stop Cycle %d Out Of Range");
+ if (i != 0) {
+ _colorCycle[i - 1].delay = 0;
+ return;
+ }
+
+ for (i = 0, cycl = _colorCycle; i < 16; i++, cycl++)
+ cycl->delay = 0;
+}
+
+/**
+ * Cycle the colors in the given palette in the intervael [cycleStart, cycleEnd]
+ * either one step forward or backward.
+ */
+static void doCyclePalette(byte *palette, int cycleStart, int cycleEnd, int size, bool forward) {
+ byte *start = palette + cycleStart * size;
+ byte *end = palette + cycleEnd * size;
+ int num = cycleEnd - cycleStart;
+ byte tmp[6];
+
+ assert(size <= 6);
+
+ if (forward) {
+ memmove(tmp, end, size);
+ memmove(start + size, start, num * size);
+ memmove(start, tmp, size);
+ } else {
+ memmove(tmp, start, size);
+ memmove(start, start + size, num * size);
+ memmove(end, tmp, size);
+ }
+}
+
+/**
+ * Adjust an 'indirect' color palette for the color cycling performed on its
+ * master palette. An indirect palette is a palette which contains indices
+ * pointing into another palette - it provides a level of indirection to map
+ * palette colors to other colors. Now when the target palette is cycled, the
+ * indirect palette suddenly point at the wrong color(s). This function takes
+ * care of adjusting an indirect palette by searching through it and replacing
+ * all indices that are in the cycle range by the new (cycled) index.
+ *
+ * Finally, the palette entries still have to be cycled normally.
+ */
+static void doCycleIndirectPalette(byte *palette, int cycleStart, int cycleEnd, bool forward) {
+ int num = cycleEnd - cycleStart + 1;
+ int i;
+ int offset = forward ? 1 : num - 1;
+
+ for (i = 0; i < 256; i++) {
+ if (cycleStart <= palette[i] && palette[i] <= cycleEnd) {
+ palette[i] = (palette[i] - cycleStart + offset) % num + cycleStart;
+ }
+ }
+
+ doCyclePalette(palette, cycleStart, cycleEnd, 1, forward);
+}
+
+
+void ScummEngine::cyclePalette() {
+ ColorCycle *cycl;
+ int valueToAdd;
+ int i, j;
+
+ valueToAdd = VAR(VAR_TIMER);
+ if (valueToAdd < VAR(VAR_TIMER_NEXT))
+ valueToAdd = VAR(VAR_TIMER_NEXT);
+
+ for (i = 0, cycl = _colorCycle; i < 16; i++, cycl++) {
+ if (!cycl->delay || cycl->start > cycl->end)
+ continue;
+ cycl->counter += valueToAdd;
+ if (cycl->counter >= cycl->delay) {
+ cycl->counter %= cycl->delay;
+
+ setDirtyColors(cycl->start, cycl->end);
+ moveMemInPalRes(cycl->start, cycl->end, cycl->flags & 2);
+
+ doCyclePalette(_currentPalette, cycl->start, cycl->end, 3, !(cycl->flags & 2));
+
+ if (_shadowPalette) {
+ if (_version >= 7) {
+ for (j = 0; j < NUM_SHADOW_PALETTE; j++)
+ doCycleIndirectPalette(_shadowPalette + j * 256, cycl->start, cycl->end, !(cycl->flags & 2));
+ } else {
+ doCycleIndirectPalette(_shadowPalette, cycl->start, cycl->end, !(cycl->flags & 2));
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Perform color cycling on the palManipulate data, too, otherwise
+ * color cycling will be disturbed by the palette fade.
+ */
+void ScummEngine::moveMemInPalRes(int start, int end, byte direction) {
+ if (!_palManipCounter)
+ return;
+
+ doCyclePalette(_palManipPalette, start, end, 3, !direction);
+ doCyclePalette(_palManipIntermediatePal, start, end, 6, !direction);
+}
+
+void ScummEngine::palManipulateInit(int resID, int start, int end, int time) {
+ byte *pal, *target, *between;
+ byte *string1, *string2, *string3;
+ int i;
+
+ string1 = getStringAddress(resID);
+ string2 = getStringAddress(resID + 1);
+ string3 = getStringAddress(resID + 2);
+ if (!string1 || !string2 || !string3) {
+ error("palManipulateInit(%d,%d,%d,%d): Cannot obtain string resources %d, %d and %d",
+ resID, start, end, time, resID, resID + 1, resID + 2);
+ return;
+ }
+
+ string1 += start;
+ string2 += start;
+ string3 += start;
+
+ _palManipStart = start;
+ _palManipEnd = end;
+ _palManipCounter = 0;
+
+ if (!_palManipPalette)
+ _palManipPalette = (byte *)calloc(0x300, 1);
+ if (!_palManipIntermediatePal)
+ _palManipIntermediatePal = (byte *)calloc(0x600, 1);
+
+ pal = _currentPalette + start * 3;
+ target = _palManipPalette + start * 3;
+ between = _palManipIntermediatePal + start * 6;
+
+ for (i = start; i < end; ++i) {
+ *target++ = *string1++;
+ *target++ = *string2++;
+ *target++ = *string3++;
+ *(uint16 *)between = ((uint16) *pal++) << 8;
+ between += 2;
+ *(uint16 *)between = ((uint16) *pal++) << 8;
+ between += 2;
+ *(uint16 *)between = ((uint16) *pal++) << 8;
+ between += 2;
+ }
+
+ _palManipCounter = time;
+}
+
+void ScummEngine_v6::palManipulateInit(int resID, int start, int end, int time) {
+ byte *pal, *target, *between;
+ const byte *new_pal;
+ int i;
+
+ new_pal = getPalettePtr(resID, _roomResource);
+
+ new_pal += start*3;
+
+ _palManipStart = start;
+ _palManipEnd = end;
+ _palManipCounter = 0;
+
+ if (!_palManipPalette)
+ _palManipPalette = (byte *)calloc(0x300, 1);
+ if (!_palManipIntermediatePal)
+ _palManipIntermediatePal = (byte *)calloc(0x600, 1);
+
+ pal = _currentPalette + start * 3;
+ target = _palManipPalette + start * 3;
+ between = _palManipIntermediatePal + start * 6;
+
+ for (i = start; i < end; ++i) {
+ *target++ = *new_pal++;
+ *target++ = *new_pal++;
+ *target++ = *new_pal++;
+ *(uint16 *)between = ((uint16) *pal++) << 8;
+ between += 2;
+ *(uint16 *)between = ((uint16) *pal++) << 8;
+ between += 2;
+ *(uint16 *)between = ((uint16) *pal++) << 8;
+ between += 2;
+ }
+
+ _palManipCounter = time;
+}
+
+
+void ScummEngine::palManipulate() {
+ byte *target, *pal, *between;
+ int i, j;
+
+ if (!_palManipCounter || !_palManipPalette || !_palManipIntermediatePal)
+ return;
+
+ target = _palManipPalette + _palManipStart * 3;
+ pal = _currentPalette + _palManipStart * 3;
+ between = _palManipIntermediatePal + _palManipStart * 6;
+
+ for (i = _palManipStart; i < _palManipEnd; ++i) {
+ j = (*((uint16 *)between) += ((*target++ << 8) - *((uint16 *)between)) / _palManipCounter);
+ *pal++ = j >> 8;
+ between += 2;
+ j = (*((uint16 *)between) += ((*target++ << 8) - *((uint16 *)between)) / _palManipCounter);
+ *pal++ = j >> 8;
+ between += 2;
+ j = (*((uint16 *)between) += ((*target++ << 8) - *((uint16 *)between)) / _palManipCounter);
+ *pal++ = j >> 8;
+ between += 2;
+ }
+ setDirtyColors(_palManipStart, _palManipEnd);
+ _palManipCounter--;
+}
+
+void ScummEngine::setupShadowPalette(int slot, int redScale, int greenScale, int blueScale, int startColor, int endColor) {
+ byte *table;
+ int i;
+ byte *curpal;
+
+ if (slot < 0 || slot >= NUM_SHADOW_PALETTE)
+ error("setupShadowPalette: invalid slot %d", slot);
+
+ if (startColor < 0 || startColor > 255 || endColor < 0 || startColor > 255 || endColor < startColor)
+ error("setupShadowPalette: invalid range from %d to %d", startColor, endColor);
+
+ table = _shadowPalette + slot * 256;
+ for (i = 0; i < 256; i++)
+ table[i] = i;
+
+ table += startColor;
+ curpal = _currentPalette + startColor * 3;
+ for (i = startColor; i <= endColor; i++) {
+ *table++ = remapPaletteColor((curpal[0] * redScale) >> 8,
+ (curpal[1] * greenScale) >> 8,
+ (curpal[2] * blueScale) >> 8,
+ -1);
+ curpal += 3;
+ }
+}
+
+static inline uint colorWeight(int red, int green, int blue) {
+ return 3 * red * red + 6 * green * green + 2 * blue * blue;
+}
+
+void ScummEngine::setupShadowPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor, int start, int end) {
+ const byte *basepal = getPalettePtr(_curPalIndex, _roomResource);
+ const byte *compareptr;
+ const byte *pal = basepal + start * 3;
+ byte *table = _shadowPalette + start;
+ int i;
+
+ // This is an implementation based on the original games code.
+ //
+ // The four known rooms where setupShadowPalette is used in atlantis are:
+ //
+ // 1) FOA Room 53: subway departing Knossos for Atlantis.
+ // 2) FOA Room 48: subway crashing into the Atlantis entrance area
+ // 3) FOA Room 82: boat/sub shadows while diving near Thera
+ // 4) FOA Room 23: the big machine room inside Atlantis
+ //
+ // There seems to be no explanation for why this function is called
+ // from within Room 23 (the big machine), as it has no shadow effects
+ // and thus doesn't result in any visual differences.
+
+ if (_gameId == GID_SAMNMAX) {
+ for (i = 0; i < 256; i++)
+ _shadowPalette[i] = i;
+ }
+
+ for (i = start; i < end; i++) {
+ int r = (int) ((pal[0] >> 2) * redScale) >> 8;
+ int g = (int) ((pal[1] >> 2) * greenScale) >> 8;
+ int b = (int) ((pal[2] >> 2) * blueScale) >> 8;
+ pal += 3;
+
+ uint8 bestitem = 0;
+ uint bestsum = 32000;
+
+ compareptr = basepal + startColor * 3;
+ for (int j = startColor; j <= endColor; j++, compareptr += 3) {
+ int ar = compareptr[0] >> 2;
+ int ag = compareptr[1] >> 2;
+ int ab = compareptr[2] >> 2;
+
+ uint sum = ABS(ar - r) + ABS(ag - g) + ABS(ab - b);
+
+ if (sum < bestsum) {
+ bestsum = sum;
+ bestitem = j;
+ }
+ }
+ *table++ = bestitem;
+ }
+}
+
+void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor) {
+ int max;
+ if (_version >= 5 && _version <= 6 && _heversion <= 60) {
+ max = 252;
+ } else {
+ max = 255;
+ }
+
+ if (startColor <= endColor) {
+ const byte *cptr;
+ const byte *palptr;
+ int color, idx, j;
+
+ if (_heversion >= 90 || _version == 8) {
+ palptr = _darkenPalette;
+ } else {
+ palptr = getPalettePtr(_curPalIndex, _roomResource);
+ }
+ for (j = startColor; j <= endColor; j++) {
+ idx = (_heversion == 70) ? _HEV7ActorPalette[j] : j;
+ cptr = palptr + idx * 3;
+
+ if (_heversion == 70)
+ setDirtyColors(idx, idx);
+
+ color = *cptr++;
+ color = color * redScale / 0xFF;
+ if (color > max)
+ color = max;
+ _currentPalette[idx * 3 + 0] = color;
+
+ color = *cptr++;
+ color = color * greenScale / 0xFF;
+ if (color > max)
+ color = max;
+ _currentPalette[idx * 3 + 1] = color;
+
+ color = *cptr++;
+ color = color * blueScale / 0xFF;
+ if (color > max)
+ color = max;
+ _currentPalette[idx * 3 + 2] = color;
+ }
+ if (_heversion != 70)
+ setDirtyColors(startColor, endColor);
+ }
+}
+
+#ifndef DISABLE_SCUMM_7_8
+static int HSL2RGBHelper(int n1, int n2, int hue) {
+ if (hue > 360)
+ hue = hue - 360;
+ else if (hue < 0)
+ hue = hue + 360;
+
+ if (hue < 60)
+ return n1 + (n2 - n1) * hue / 60;
+ if (hue < 180)
+ return n2;
+ if (hue < 240)
+ return n1 + (n2 - n1) * (240 - hue) / 60;
+ return n1;
+}
+
+/**
+ * This function scales the HSL (Hue, Saturation and Lightness)
+ * components of the palette colors. It's used in CMI when Guybrush
+ * walks from the beach towards the swamp.
+ */
+void ScummEngine_v8::desaturatePalette(int hueScale, int satScale, int lightScale, int startColor, int endColor) {
+
+ if (startColor <= endColor) {
+ const byte *cptr;
+ byte *cur;
+ int j;
+
+ cptr = _darkenPalette + startColor * 3;
+ cur = _currentPalette + startColor * 3;
+
+ for (j = startColor; j <= endColor; j++) {
+ int R = *cptr++;
+ int G = *cptr++;
+ int B = *cptr++;
+
+ // RGB to HLS (Foley and VanDam)
+
+ const int min = MIN(R, MIN(G, B));
+ const int max = MAX(R, MAX(G, B));
+ const int diff = (max - min);
+ const int sum = (max + min);
+
+ if (diff != 0) {
+ int H, S, L;
+
+ if (sum <= 255)
+ S = 255 * diff / sum;
+ else
+ S = 255 * diff / (255 * 2 - sum);
+
+ if (R == max)
+ H = 60 * (G - B) / diff;
+ else if (G == max)
+ H = 120 + 60 * (B - R) / diff;
+ else
+ H = 240 + 60 * (R - G) / diff;
+
+ if (H < 0)
+ H = H + 360;
+
+ // Scale the result
+
+ H = (H * hueScale) / 255;
+ S = (S * satScale) / 255;
+ L = (sum * lightScale) / 255;
+
+ // HLS to RGB (Foley and VanDam)
+
+ int m1, m2;
+ if (L <= 255)
+ m2 = L * (255 + S) / (255 * 2);
+ else
+ m2 = L * (255 - S) / (255 * 2) + S;
+
+ m1 = L - m2;
+
+ R = HSL2RGBHelper(m1, m2, H + 120);
+ G = HSL2RGBHelper(m1, m2, H);
+ B = HSL2RGBHelper(m1, m2, H - 120);
+ } else {
+ // Maximal color = minimal color -> R=G=B -> it's a grayscale.
+ R = G = B = (R * lightScale) / 255;
+ }
+
+ *cur++ = R;
+ *cur++ = G;
+ *cur++ = B;
+ }
+
+ setDirtyColors(startColor, endColor);
+ }
+}
+#endif
+
+
+int ScummEngine::remapPaletteColor(int r, int g, int b, int threshold) {
+ int i;
+ int ar, ag, ab;
+ uint sum, bestsum, bestitem = 0;
+
+ int startColor = (_version == 8) ? 24 : 1;
+ byte *pal = _currentPalette + startColor * 3;
+
+ if (r > 255)
+ r = 255;
+ if (g > 255)
+ g = 255;
+ if (b > 255)
+ b = 255;
+
+ bestsum = 0x7FFFFFFF;
+
+ r &= ~3;
+ g &= ~3;
+ b &= ~3;
+
+ for (i = startColor; i < 255; i++, pal += 3) {
+ if (_version == 7 && _colorUsedByCycle[i])
+ continue;
+
+ ar = pal[0] & ~3;
+ ag = pal[1] & ~3;
+ ab = pal[2] & ~3;
+ if (ar == r && ag == g && ab == b)
+ return i;
+
+ sum = colorWeight(ar - r, ag - g, ab - b);
+
+ if (sum < bestsum) {
+ bestsum = sum;
+ bestitem = i;
+ }
+ }
+
+ if (threshold != -1 && bestsum > colorWeight(threshold, threshold, threshold)) {
+ // Best match exceeded threshold. Try to find an unused palette entry and
+ // use it for our purpose.
+ pal = _currentPalette + (256 - 2) * 3;
+ for (i = 254; i > 48; i--, pal -= 3) {
+ if (pal[0] >= 252 && pal[1] >= 252 && pal[2] >= 252) {
+ setPalColor(i, r, g, b);
+ return i;
+ }
+ }
+ }
+
+ return bestitem;
+}
+
+void ScummEngine::swapPalColors(int a, int b) {
+ byte *ap, *bp;
+ byte t;
+
+ if ((uint) a >= 256 || (uint) b >= 256)
+ error("swapPalColors: invalid values, %d, %d", a, b);
+
+ ap = &_currentPalette[a * 3];
+ bp = &_currentPalette[b * 3];
+
+ t = ap[0];
+ ap[0] = bp[0];
+ bp[0] = t;
+ t = ap[1];
+ ap[1] = bp[1];
+ bp[1] = t;
+ t = ap[2];
+ ap[2] = bp[2];
+ bp[2] = t;
+
+ setDirtyColors(a, a);
+ setDirtyColors(b, b);
+}
+
+void ScummEngine::copyPalColor(int dst, int src) {
+ byte *dp, *sp;
+
+ if ((uint) dst >= 256 || (uint) src >= 256)
+ error("copyPalColor: invalid values, %d, %d", dst, src);
+
+ dp = &_currentPalette[dst * 3];
+ sp = &_currentPalette[src * 3];
+
+ dp[0] = sp[0];
+ dp[1] = sp[1];
+ dp[2] = sp[2];
+
+ setDirtyColors(dst, dst);
+}
+
+void ScummEngine::setPalColor(int idx, int r, int g, int b) {
+ if (_heversion == 70)
+ idx = _HEV7ActorPalette[idx];
+
+ _currentPalette[idx * 3 + 0] = r;
+ _currentPalette[idx * 3 + 1] = g;
+ _currentPalette[idx * 3 + 2] = b;
+ if (_version == 8) {
+ _darkenPalette[idx * 3 + 0] = r;
+ _darkenPalette[idx * 3 + 1] = g;
+ _darkenPalette[idx * 3 + 2] = b;
+ }
+ setDirtyColors(idx, idx);
+}
+
+void ScummEngine::setPalette(int palindex) {
+ const byte *pals;
+
+ _curPalIndex = palindex;
+ pals = getPalettePtr(_curPalIndex, _roomResource);
+ setPaletteFromPtr(pals);
+}
+
+void ScummEngine::setRoomPalette(int palindex, int room) {
+ const byte *roomptr = getResourceAddress(rtRoom, room);
+ assert(roomptr);
+ const byte *pals = findResource(MKID('PALS'), roomptr);
+ assert(pals);
+ const byte *rgbs = findPalInPals(pals, palindex);
+ assert(rgbs);
+ setPaletteFromPtr(rgbs);
+}
+
+const byte *ScummEngine::findPalInPals(const byte *pal, int idx) {
+ const byte *offs;
+ uint32 size;
+
+ pal = findResource(MKID('WRAP'), pal);
+ if (pal == NULL)
+ return NULL;
+
+ offs = findResourceData(MKID('OFFS'), pal);
+ if (offs == NULL)
+ return NULL;
+
+ size = getResourceDataSize(offs) / 4;
+ if ((uint32)idx >= (uint32)size)
+ return NULL;
+
+ return offs + READ_LE_UINT32(offs + idx * sizeof(uint32));
+}
+
+const byte *ScummEngine::getPalettePtr(int palindex, int room) {
+ const byte *cptr;
+
+ cptr = getResourceAddress(rtRoom, room);
+ assert(cptr);
+ if (_CLUT_offs) {
+ cptr += _CLUT_offs;
+ } else {
+ cptr = findPalInPals(cptr + _PALS_offs, palindex);
+ assert(cptr);
+ }
+ return cptr;
+}
+
+void ScummEngine::updatePalette() {
+ if (_palDirtyMax == -1)
+ return;
+
+ bool noir_mode = (_gameId == GID_SAMNMAX && readVar(0x8000));
+ int first = _palDirtyMin;
+ int num = _palDirtyMax - first + 1;
+ int i;
+
+ byte palette_colors[1024];
+ byte *p = palette_colors;
+
+ for (i = _palDirtyMin; i <= _palDirtyMax; i++) {
+ byte *data;
+
+ if (_features & GF_SMALL_HEADER && _version > 2)
+ data = _currentPalette + _shadowPalette[i] * 3;
+ else
+ data = _currentPalette + i * 3;
+
+ // Sam & Max film noir mode. Convert the colours to grayscale
+ // before uploading them to the backend.
+
+ if (noir_mode) {
+ int r, g, b;
+ byte brightness;
+
+ r = data[0];
+ g = data[1];
+ b = data[2];
+
+ brightness = (byte)((0.299 * r + 0.587 * g + 0.114 * b) + 0.5);
+
+ *p++ = brightness;
+ *p++ = brightness;
+ *p++ = brightness;
+ *p++ = 0;
+ } else {
+ *p++ = data[0];
+ *p++ = data[1];
+ *p++ = data[2];
+ *p++ = 0;
+ }
+ }
+
+ _system->setPalette(palette_colors, first, num);
+
+ _palDirtyMax = -1;
+ _palDirtyMin = 256;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/palette_he.cpp b/engines/scumm/palette_he.cpp
new file mode 100644
index 0000000000..2d6a471d79
--- /dev/null
+++ b/engines/scumm/palette_he.cpp
@@ -0,0 +1,317 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "scumm/scumm.h"
+#include "scumm/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+void ScummEngine_v70he::remapHEPalette(const uint8 *src, uint8 *dst) {
+ int r, g, b, sum, bestitem, bestsum;
+ int ar, ag, ab;
+ uint8 *palPtr;
+ src += 30;
+
+ if (_heversion >= 99) {
+ palPtr = _hePalettes + 1024 + 30;
+ } else {
+ palPtr = _currentPalette + 30;
+ }
+
+ for (int j = 10; j < 246; j++) {
+ bestitem = 0xFFFF;
+ bestsum = 0xFFFF;
+
+ r = *src++;
+ g = *src++;
+ b = *src++;
+
+ uint8 *curPal = palPtr;
+
+ for (int k = 10; k < 246; k++) {
+ ar = r - *curPal++;
+ ag = g - *curPal++;
+ ab = b - *curPal++;
+
+ sum = (ar * ar) + (ag * ag) + (ab * ab);
+
+ if (bestitem == 0xFFFF || sum <= bestsum) {
+ bestitem = k;
+ bestsum = sum;
+ }
+ }
+
+ dst[j] = bestitem;
+ }
+}
+
+uint8 *ScummEngine_v90he::getHEPaletteIndex(int palSlot) {
+ if (palSlot) {
+ assert(palSlot >= 1 && palSlot <= _numPalettes);
+ return _hePalettes + palSlot * 1024;
+ } else {
+ return _hePalettes + 1024;
+ }
+}
+
+int ScummEngine_v90he::getHEPaletteSimilarColor(int palSlot, int red, int green, int start, int end) {
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ checkRange(255, 0, start, "Invalid palette slot %d");
+ checkRange(255, 0, end, "Invalid palette slot %d");
+
+ uint8 *pal = _hePalettes + palSlot * 1024 + start * 3;
+
+ int bestsum = 0xFFFFFFFF;
+ int bestitem = start;
+
+ for (int i = start; i <= end; i++) {
+ int dr = red - pal[0];
+ int dg = green - pal[1];
+ int sum = dr * dr + dg * dg * 2;
+ if (sum == 0) {
+ return i;
+ }
+ if (sum < bestsum) {
+ bestsum = sum;
+ bestitem = i;
+ }
+ pal += 3;
+ }
+ return bestitem;
+}
+
+int ScummEngine_v90he::getHEPaletteColorComponent(int palSlot, int color, int component) {
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ checkRange(255, 0, color, "Invalid palette slot %d");
+
+ return _hePalettes[palSlot * 1024 + color * 3 + component % 3];
+}
+
+int ScummEngine_v90he::getHEPaletteColor(int palSlot, int color) {
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ checkRange(255, 0, color, "Invalid palette slot %d");
+
+ return _hePalettes[palSlot * 1024 + 768 + color];
+}
+
+void ScummEngine_v90he::setHEPaletteColor(int palSlot, uint8 color, uint8 r, uint8 g, uint8 b) {
+ debug(7, "setHEPaletteColor(%d, %d, %d, %d, %d)", palSlot, color, r, g, b);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ uint8 *p = _hePalettes + palSlot * 1024 + color * 3;
+ *(p + 0) = r;
+ *(p + 1) = g;
+ *(p + 2) = b;
+ _hePalettes[palSlot * 1024 + 768 + color] = color;
+}
+
+void ScummEngine_v90he::setHEPaletteFromPtr(int palSlot, const uint8 *palData) {
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ uint8 *pc = _hePalettes + palSlot * 1024;
+ uint8 *pi = pc + 768;
+ for (int i = 0; i < 256; ++i) {
+ *pc++ = *palData++;
+ *pc++ = *palData++;
+ *pc++ = *palData++;
+ *pi++ = i;
+ }
+}
+
+void ScummEngine_v90he::setHEPaletteFromCostume(int palSlot, int resId) {
+ debug(7, "setHEPaletteFromCostume(%d, %d)", palSlot, resId);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ const uint8 *data = getResourceAddress(rtCostume, resId);
+ assert(data);
+ const uint8 *rgbs = findResourceData(MKID('RGBS'), data);
+ assert(rgbs);
+ setHEPaletteFromPtr(palSlot, rgbs);
+}
+
+void ScummEngine_v90he::setHEPaletteFromImage(int palSlot, int resId, int state) {
+ debug(7, "setHEPaletteFromImage(%d, %d, %d)", palSlot, resId, state);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ uint8 *data = getResourceAddress(rtImage, resId);
+ assert(data);
+ const uint8 *rgbs = findWrappedBlock(MKID('RGBS'), data, state, 0);
+ assert(rgbs);
+ setHEPaletteFromPtr(palSlot, rgbs);
+}
+
+void ScummEngine_v90he::setHEPaletteFromRoom(int palSlot, int resId, int state) {
+ debug(7, "setHEPaletteFromRoom(%d, %d, %d)", palSlot, resId, state);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ const uint8 *data = getResourceAddress(rtRoom, resId);
+ assert(data);
+ const uint8 *pals = findResourceData(MKID('PALS'), data);
+ assert(pals);
+ const uint8 *rgbs = findPalInPals(pals, state);
+ assert(rgbs);
+ setHEPaletteFromPtr(palSlot, rgbs);
+}
+
+void ScummEngine_v90he::restoreHEPalette(int palSlot) {
+ debug(7, "restoreHEPalette(%d)", palSlot);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ if (palSlot != 1) {
+ memcpy(_hePalettes + palSlot * 1024, _hePalettes + 1024, 1024);
+ }
+}
+
+void ScummEngine_v90he::copyHEPalette(int dstPalSlot, int srcPalSlot) {
+ debug(7, "copyHEPalette(%d, %d)", dstPalSlot, srcPalSlot);
+ assert(dstPalSlot >= 1 && dstPalSlot <= _numPalettes);
+ assert(srcPalSlot >= 1 && srcPalSlot <= _numPalettes);
+ if (dstPalSlot != srcPalSlot) {
+ memcpy(_hePalettes + dstPalSlot * 1024, _hePalettes + srcPalSlot * 1024, 1024);
+ }
+}
+
+void ScummEngine_v90he::copyHEPaletteColor(int palSlot, uint8 dstColor, uint8 srcColor) {
+ debug(7, "copyHEPaletteColor(%d, %d, %d)", palSlot, dstColor, srcColor);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ uint8 *dstPal = _hePalettes + palSlot * 1024 + dstColor * 3;
+ uint8 *srcPal = _hePalettes + 1024 + srcColor * 3;
+ memcpy(dstPal, srcPal, 3);
+ _hePalettes[palSlot * 1024 + 768 + dstColor] = srcColor;
+}
+
+void ScummEngine_v99he::setPaletteFromPtr(const byte *ptr, int numcolor) {
+ int i;
+ byte *dest, r, g, b;
+
+ if (numcolor < 0) {
+ numcolor = getResourceDataSize(ptr) / 3;
+ }
+
+ checkRange(256, 0, numcolor, "Too many colors (%d) in Palette");
+
+ dest = _hePalettes + 1024;
+
+ for (i = 0; i < numcolor; i++) {
+ r = *ptr++;
+ g = *ptr++;
+ b = *ptr++;
+
+ if (i == 15 || r < 252 || g < 252 || b < 252) {
+ *dest++ = r;
+ *dest++ = g;
+ *dest++ = b;
+ _hePalettes[1792 + i] = i;
+ } else {
+ dest += 3;
+ }
+ }
+
+ memcpy(_hePalettes, _hePalettes + 1024, 768);
+
+ for (i = 0; i < 10; ++i)
+ _hePalettes[1792 + i] = i;
+ for (i = 246; i < 256; ++i)
+ _hePalettes[1792 + i] = i;
+
+ setDirtyColors(0, numcolor - 1);
+}
+
+void ScummEngine_v99he::darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor) {
+ uint8 *src, *dst;
+ int color, j;
+
+ src = _hePalettes + startColor * 3;
+ dst = _hePalettes + 1024 + startColor * 3;
+ for (j = startColor; j <= endColor; j++) {
+ color = *src++;
+ color = color * redScale / 0xFF;
+ if (color > 255)
+ color = 255;
+ *dst++ = color;
+
+ color = *src++;
+ color = color * greenScale / 0xFF;
+ if (color > 255)
+ color = 255;
+ *dst++ = color;
+
+ color = *src++;
+ color = color * blueScale / 0xFF;
+ if (color > 255)
+ color = 255;
+ *dst++ = color;
+
+ _hePalettes[1792 + j] = j;
+ setDirtyColors(j, endColor);
+ }
+}
+
+void ScummEngine_v99he::copyPalColor(int dst, int src) {
+ byte *dp, *sp;
+
+ if ((uint) dst >= 256 || (uint) src >= 256)
+ error("copyPalColor: invalid values, %d, %d", dst, src);
+
+ dp = &_hePalettes[1024 + dst * 3];
+ sp = &_hePalettes[1024 + src * 3];
+
+ dp[0] = sp[0];
+ dp[1] = sp[1];
+ dp[2] = sp[2];
+ _hePalettes[1792 + dst] = dst;
+
+ setDirtyColors(dst, dst);
+}
+
+void ScummEngine_v99he::setPalColor(int idx, int r, int g, int b) {
+ _hePalettes[1024 + idx * 3 + 0] = r;
+ _hePalettes[1024 + idx * 3 + 1] = g;
+ _hePalettes[1024 + idx * 3 + 2] = b;
+ _hePalettes[1792 + idx] = idx;
+ setDirtyColors(idx, idx);
+}
+
+void ScummEngine_v99he::updatePalette() {
+ if (_palDirtyMax == -1)
+ return;
+
+ int num = _palDirtyMax - _palDirtyMin + 1;
+ int i;
+
+ byte palette_colors[1024];
+ byte *p = palette_colors;
+
+ for (i = _palDirtyMin; i <= _palDirtyMax; i++) {
+ byte *data = _hePalettes + 1024 + i * 3;
+
+ *p++ = data[0];
+ *p++ = data[1];
+ *p++ = data[2];
+ *p++ = 0;
+ }
+
+ _system->setPalette(palette_colors, _palDirtyMin, num);
+
+ _palDirtyMax = -1;
+ _palDirtyMin = 256;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_mod.cpp b/engines/scumm/player_mod.cpp
new file mode 100644
index 0000000000..773d34732d
--- /dev/null
+++ b/engines/scumm/player_mod.cpp
@@ -0,0 +1,181 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/player_mod.h"
+#include "sound/mixer.h"
+#include "sound/rate.h"
+
+namespace Scumm {
+
+Player_MOD::Player_MOD(ScummEngine *scumm) {
+ int i;
+ _mixer = scumm->_mixer;
+ _samplerate = _mixer->getOutputRate();
+ _mixamt = 0;
+ _mixpos = 0;
+
+ for (i = 0; i < MOD_MAXCHANS; i++) {
+ _channels[i].id = 0;
+ _channels[i].vol = 0;
+ _channels[i].freq = 0;
+ _channels[i].converter = NULL;
+ _channels[i].input = NULL;
+ }
+
+ _playproc = NULL;
+ _playparam = NULL;
+
+ _mixer->setupPremix(this);
+}
+
+Player_MOD::~Player_MOD() {
+ // Detach the premix callback handler
+ _mixer->setupPremix(0);
+ for (int i = 0; i < MOD_MAXCHANS; i++) {
+ if (!_channels[i].id)
+ continue;
+ delete _channels[i].converter;
+ delete _channels[i].input;
+ }
+}
+
+void Player_MOD::setMusicVolume(int vol) {
+ _maxvol = vol;
+}
+
+void Player_MOD::setUpdateProc(ModUpdateProc *proc, void *param, int freq) {
+ _playproc = proc;
+ _playparam = param;
+ _mixamt = _samplerate / freq;
+}
+void Player_MOD::clearUpdateProc() {
+ _playproc = NULL;
+ _playparam = NULL;
+ _mixamt = 0;
+}
+
+void Player_MOD::startChannel(int id, void *data, int size, int rate, uint8 vol, int loopStart, int loopEnd, int8 pan) {
+ int i;
+ if (id == 0)
+ error("player_mod - attempted to start channel id 0");
+
+ for (i = 0; i < MOD_MAXCHANS; i++) {
+ if (!_channels[i].id)
+ break;
+ }
+ if (i == MOD_MAXCHANS) {
+ warning("player_mod - too many music channels playing (%i max)",MOD_MAXCHANS);
+ return;
+ }
+ _channels[i].id = id;
+ _channels[i].vol = vol;
+ _channels[i].pan = pan;
+ _channels[i].freq = rate;
+ _channels[i].input = makeLinearInputStream(rate, Audio::Mixer::FLAG_AUTOFREE | (loopStart != loopEnd ? Audio::Mixer::FLAG_LOOP : 0), (const byte*)data, size, loopStart, loopEnd - loopStart);
+ _channels[i].converter = Audio::makeRateConverter(rate, _mixer->getOutputRate(), false, false);
+}
+
+void Player_MOD::stopChannel(int id) {
+ if (id == 0)
+ error("player_mod - attempted to stop channel id 0");
+ for (int i = 0; i < MOD_MAXCHANS; i++) {
+ if (_channels[i].id == id) {
+ delete _channels[i].converter;
+ _channels[i].converter = NULL;
+ delete _channels[i].input;
+ _channels[i].input = NULL;
+ _channels[i].id = 0;
+ _channels[i].vol = 0;
+ _channels[i].freq = 0;
+ }
+ }
+}
+void Player_MOD::setChannelVol(int id, uint8 vol) {
+ if (id == 0)
+ error("player_mod - attempted to set volume for channel id 0");
+ for (int i = 0; i < MOD_MAXCHANS; i++) {
+ if (_channels[i].id == id) {
+ _channels[i].vol = vol;
+ break;
+ }
+ }
+}
+
+void Player_MOD::setChannelPan(int id, int8 pan) {
+ if (id == 0)
+ error("player_mod - attempted to set pan for channel id 0");
+ for (int i = 0; i < MOD_MAXCHANS; i++) {
+ if (_channels[i].id == id) {
+ _channels[i].pan = pan;
+ break;
+ }
+ }
+}
+
+void Player_MOD::setChannelFreq(int id, int freq) {
+ if (id == 0)
+ error("player_mod - attempted to set frequency for channel id 0");
+ for (int i = 0; i < MOD_MAXCHANS; i++) {
+ if (_channels[i].id == id) {
+ _channels[i].freq = freq;
+ delete _channels[i].converter;
+ _channels[i].converter = Audio::makeRateConverter(freq, _mixer->getOutputRate(), false, false);
+ break;
+ }
+ }
+}
+
+void Player_MOD::do_mix(int16 *data, uint len) {
+ int i;
+ int dpos = 0;
+ uint dlen = 0;
+ memset(data, 0, 2 * len * sizeof(int16));
+ while (len) {
+ if (_playproc) {
+ dlen = _mixamt - _mixpos;
+ if (!_mixpos)
+ _playproc(_playparam);
+ if (dlen <= len) {
+ _mixpos = 0;
+ len -= dlen;
+ } else {
+ _mixpos = _mixamt - len;
+ dlen = len;
+ len = 0;
+ }
+ } else {
+ dlen = len;
+ len = 0;
+ }
+ for (i = 0; i < MOD_MAXCHANS; i++)
+ if (_channels[i].id) {
+ Audio::st_volume_t vol_l = (127 - _channels[i].pan) * _channels[i].vol / 127;
+ Audio::st_volume_t vol_r = (127 + _channels[i].pan) * _channels[i].vol / 127;
+ _channels[i].converter->flow(*_channels[i].input, &data[dpos*2], dlen, vol_l, vol_r);
+ }
+ dpos += dlen;
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_mod.h b/engines/scumm/player_mod.h
new file mode 100644
index 0000000000..317bf7fd6e
--- /dev/null
+++ b/engines/scumm/player_mod.h
@@ -0,0 +1,97 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef PLAYER_MOD_H
+#define PLAYER_MOD_H
+
+#include "scumm/scumm.h"
+#include "sound/audiostream.h"
+
+namespace Audio {
+ class RateConverter;
+}
+
+namespace Scumm {
+
+/**
+ * Generic Amiga MOD mixer - provides a 60Hz 'update' routine.
+ */
+class Player_MOD : public AudioStream {
+public:
+ Player_MOD(ScummEngine *scumm);
+ virtual ~Player_MOD();
+ virtual void setMusicVolume(int vol);
+
+ virtual void startChannel(int id, void *data, int size, int rate, uint8 vol, int loopStart = 0, int loopEnd = 0, int8 pan = 0);
+ virtual void stopChannel(int id);
+ virtual void setChannelVol(int id, uint8 vol);
+ virtual void setChannelPan(int id, int8 pan);
+ virtual void setChannelFreq(int id, int freq);
+
+ typedef void ModUpdateProc(void *param);
+
+ virtual void setUpdateProc(ModUpdateProc *proc, void *param, int freq);
+ virtual void clearUpdateProc();
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples) {
+ do_mix(buffer, numSamples / 2);
+ return numSamples;
+ }
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _samplerate; }
+
+private:
+ enum {
+ MOD_MAXCHANS = 24
+ };
+
+ struct soundChan {
+ int id;
+ uint8 vol;
+ int8 pan;
+ uint16 freq;
+ Audio::RateConverter *converter;
+ AudioStream *input;
+ };
+
+ Audio::Mixer *_mixer;
+
+ uint32 _mixamt;
+ uint32 _mixpos;
+ int _samplerate;
+
+ soundChan _channels[MOD_MAXCHANS];
+
+ uint8 _maxvol;
+
+ virtual void do_mix(int16 *buf, uint len);
+
+ ModUpdateProc *_playproc;
+ void *_playparam;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_nes.cpp b/engines/scumm/player_nes.cpp
new file mode 100644
index 0000000000..8564ac1e41
--- /dev/null
+++ b/engines/scumm/player_nes.cpp
@@ -0,0 +1,1085 @@
+
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * aint32 with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "base/engine.h"
+#include "scumm/player_nes.h"
+#include "scumm/scumm.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+static const byte channelMask[4] = {1, 2, 4, 8};
+
+static const uint16 freqTable[64] = {
+ 0x07F0, 0x077E, 0x0712, 0x06AE, 0x064E, 0x05F3, 0x059E, 0x054D,
+ 0x0501, 0x04B9, 0x0475, 0x0435, 0x03F8, 0x03BF, 0x0389, 0x0357,
+ 0x0327, 0x02F9, 0x02CF, 0x02A6, 0x0280, 0x025C, 0x023A, 0x021A,
+ 0x01FC, 0x01DF, 0x01C4, 0x01AB, 0x0193, 0x017C, 0x0167, 0x0152,
+ 0x013F, 0x012D, 0x011C, 0x010C, 0x00FD, 0x00EE, 0x00E1, 0x00D4,
+ 0x00C8, 0x00BD, 0x00B2, 0x00A8, 0x009F, 0x0096, 0x008D, 0x0085,
+ 0x007E, 0x0076, 0x0070, 0x0069, 0x0063, 0x005E, 0x0058, 0x0053,
+ 0x004F, 0x004A, 0x0046, 0x0042, 0x003E, 0x003A, 0x0037, 0x0034
+};
+
+static const byte instChannel[16] = {
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 1, 3, 3, 3
+};
+static const byte startCmd[16] = {
+ 0x05, 0x03, 0x06, 0x08, 0x0B, 0x01, 0x01, 0x1A,
+ 0x16, 0x06, 0x04, 0x17, 0x02, 0x10, 0x0E, 0x0D
+};
+static const byte releaseCmd[16] = {
+ 0x0F, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x0F, 0x0F, 0x0F, 0x0F
+};
+static const byte nextCmd[28] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x17, 0xFF, 0x07, 0xFF,
+ 0xFF, 0x0A, 0x09, 0x0C, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x12, 0x11, 0x03, 0xFF, 0xFF, 0x18, 0x00,
+ 0x19, 0x00, 0x00, 0x00
+};
+static const byte nextDelay[28] = {
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
+ 0x00, 0x05, 0x08, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x03, 0x00, 0x00, 0x00
+};
+
+namespace APUe {
+
+struct tAPU {
+ int Cycles;
+ int BufPos;
+ int SampleRate;
+} APU;
+
+const byte LengthCounts[32] = {
+ 0x0A,0xFE,
+ 0x14,0x02,
+ 0x28,0x04,
+ 0x50,0x06,
+ 0xA0,0x08,
+ 0x3C,0x0A,
+ 0x0E,0x0C,
+ 0x1A,0x0E,
+
+ 0x0C,0x10,
+ 0x18,0x12,
+ 0x30,0x14,
+ 0x60,0x16,
+ 0xC0,0x18,
+ 0x48,0x1A,
+ 0x10,0x1C,
+ 0x20,0x1E
+};
+
+static struct {
+ byte volume, envelope, wavehold, duty, swpspeed, swpdir, swpstep, swpenab;
+ uint32 freq; // short
+ byte Vol;
+ byte CurD;
+ byte Timer;
+ byte EnvCtr, Envelope, BendCtr;
+ bool Enabled, ValidFreq, Active;
+ bool EnvClk, SwpClk;
+ uint32 Cycles; // short
+ int32 Pos;
+} Square0, Square1;
+
+const int8 Duties[4][8] = {
+ {-4,+4,-4,-4,-4,-4,-4,-4},
+ {-4,+4,+4,-4,-4,-4,-4,-4},
+ {-4,+4,+4,+4,+4,-4,-4,-4},
+ {+4,-4,-4,+4,+4,+4,+4,+4}
+};
+
+inline void Square0_CheckActive(void) {
+ Square0.ValidFreq = (Square0.freq >= 0x8) && ((Square0.swpdir) || !((Square0.freq + (Square0.freq >> Square0.swpstep)) & 0x800));
+ Square0.Active = Square0.Timer && Square0.ValidFreq;
+ Square0.Pos = Square0.Active ? (Duties[Square0.duty][Square0.CurD] * Square0.Vol) : 0;
+}
+
+inline void Square0_Write(int Reg, byte Val) {
+ switch (Reg) {
+ case 0:
+ Square0.volume = Val & 0xF;
+ Square0.envelope = Val & 0x10;
+ Square0.wavehold = Val & 0x20;
+ Square0.duty = (Val >> 6) & 0x3;
+ Square0.Vol = Square0.envelope ? Square0.volume : Square0.Envelope;
+ break;
+
+ case 1:
+ Square0.swpstep = Val & 0x07;
+ Square0.swpdir = Val & 0x08;
+ Square0.swpspeed = (Val >> 4) & 0x7;
+ Square0.swpenab = Val & 0x80;
+ Square0.SwpClk = true;
+ break;
+
+ case 2:
+ Square0.freq &= 0x700;
+ Square0.freq |= Val;
+ break;
+
+ case 3:
+ Square0.freq &= 0xFF;
+ Square0.freq |= (Val & 0x7) << 8;
+
+ if (Square0.Enabled)
+ Square0.Timer = LengthCounts[(Val >> 3) & 0x1F];
+
+ Square0.CurD = 0;
+ Square0.EnvClk = true;
+ break;
+
+ case 4:
+ if (!(Square0.Enabled = Val ? true : false))
+ Square0.Timer = 0;
+ break;
+ }
+ Square0_CheckActive();
+}
+
+inline void Square0_Run(void) {
+ if (!--Square0.Cycles) {
+ Square0.Cycles = (Square0.freq + 1) << 1;
+ Square0.CurD = (Square0.CurD + 1) & 0x7;
+
+ if (Square0.Active)
+ Square0.Pos = Duties[Square0.duty][Square0.CurD] * Square0.Vol;
+ }
+}
+
+inline void Square0_QuarterFrame(void) {
+ if (Square0.EnvClk) {
+ Square0.EnvClk = false;
+ Square0.Envelope = 0xF;
+ Square0.EnvCtr = Square0.volume + 1;
+ } else if (!--Square0.EnvCtr) {
+ Square0.EnvCtr = Square0.volume + 1;
+
+ if (Square0.Envelope)
+ Square0.Envelope--;
+ else
+ Square0.Envelope = Square0.wavehold ? 0xF : 0x0;
+ }
+
+ Square0.Vol = Square0.envelope ? Square0.volume : Square0.Envelope;
+ Square0_CheckActive();
+}
+
+inline void Square0_HalfFrame(void) {
+ if (!--Square0.BendCtr) {
+ Square0.BendCtr = Square0.swpspeed + 1;
+
+ if (Square0.swpenab && Square0.swpstep && Square0.ValidFreq) {
+ int sweep = Square0.freq >> Square0.swpstep;
+ Square0.freq += Square0.swpdir ? ~sweep : sweep;
+ }
+ }
+
+ if (Square0.SwpClk) {
+ Square0.SwpClk = false;
+ Square0.BendCtr = Square0.swpspeed + 1;
+ }
+
+ if (Square0.Timer && !Square0.wavehold)
+ Square0.Timer--;
+
+ Square0_CheckActive();
+}
+
+inline void Square1_CheckActive(void) {
+ Square1.ValidFreq = (Square1.freq >= 0x8) && ((Square1.swpdir) || !((Square1.freq + (Square1.freq >> Square1.swpstep)) & 0x800));
+ Square1.Active = Square1.Timer && Square1.ValidFreq;
+ Square1.Pos = Square1.Active ? (Duties[Square1.duty][Square1.CurD] * Square1.Vol) : 0;
+}
+
+inline void Square1_Write(int Reg, byte Val) {
+ switch (Reg) {
+ case 0:
+ Square1.volume = Val & 0xF;
+ Square1.envelope = Val & 0x10;
+ Square1.wavehold = Val & 0x20;
+ Square1.duty = (Val >> 6) & 0x3;
+ Square1.Vol = Square1.envelope ? Square1.volume : Square1.Envelope;
+ break;
+
+ case 1:
+ Square1.swpstep = Val & 0x07;
+ Square1.swpdir = Val & 0x08;
+ Square1.swpspeed = (Val >> 4) & 0x7;
+ Square1.swpenab = Val & 0x80;
+ Square1.SwpClk = true;
+ break;
+
+ case 2:
+ Square1.freq &= 0x700;
+ Square1.freq |= Val;
+ break;
+
+ case 3:
+ Square1.freq &= 0xFF;
+ Square1.freq |= (Val & 0x7) << 8;
+
+ if (Square1.Enabled)
+ Square1.Timer = LengthCounts[(Val >> 3) & 0x1F];
+
+ Square1.CurD = 0;
+ Square1.EnvClk = true;
+ break;
+
+ case 4:
+ if (!(Square1.Enabled = Val ? true : false))
+ Square1.Timer = 0;
+ break;
+ }
+ Square1_CheckActive();
+}
+
+inline void Square1_Run(void) {
+ if (!--Square1.Cycles) {
+ Square1.Cycles = (Square1.freq + 1) << 1;
+ Square1.CurD = (Square1.CurD + 1) & 0x7;
+
+ if (Square1.Active)
+ Square1.Pos = Duties[Square1.duty][Square1.CurD] * Square1.Vol;
+ }
+}
+
+inline void Square1_QuarterFrame(void) {
+ if (Square1.EnvClk) {
+ Square1.EnvClk = false;
+ Square1.Envelope = 0xF;
+ Square1.EnvCtr = Square1.volume + 1;
+ } else if (!--Square1.EnvCtr) {
+ Square1.EnvCtr = Square1.volume + 1;
+
+ if (Square1.Envelope)
+ Square1.Envelope--;
+ else
+ Square1.Envelope = Square1.wavehold ? 0xF : 0x0;
+ }
+
+ Square1.Vol = Square1.envelope ? Square1.volume : Square1.Envelope;
+ Square1_CheckActive();
+}
+
+inline void Square1_HalfFrame(void) {
+ if (!--Square1.BendCtr) {
+ Square1.BendCtr = Square1.swpspeed + 1;
+
+ if (Square1.swpenab && Square1.swpstep && Square1.ValidFreq) {
+ int sweep = Square1.freq >> Square1.swpstep;
+ Square1.freq += Square1.swpdir ? -sweep : sweep;
+ }
+ }
+
+ if (Square1.SwpClk) {
+ Square1.SwpClk = false;
+ Square1.BendCtr = Square1.swpspeed + 1;
+ }
+
+ if (Square1.Timer && !Square1.wavehold)
+ Square1.Timer--;
+
+ Square1_CheckActive();
+}
+
+static struct {
+ byte linear, wavehold;
+ uint32 freq; // short
+ byte CurD;
+ byte Timer, LinCtr;
+ bool Enabled, Active;
+ bool LinClk;
+ uint32 Cycles; // short
+ int32 Pos;
+} Triangle;
+
+const int8 TriDuty[32] = {
+ -8,-7,-6,-5,-4,-3,-2,-1,
+ +0,+1,+2,+3,+4,+5,+6,+7,
+ +7,+6,+5,+4,+3,+2,+1,+0,
+ -1,-2,-3,-4,-5,-6,-7,-8
+};
+
+inline void Triangle_CheckActive(void) {
+ Triangle.Active = Triangle.Timer && Triangle.LinCtr;
+
+ if (Triangle.freq < 4)
+ Triangle.Pos = 0; // beyond hearing range
+ else
+ Triangle.Pos = TriDuty[Triangle.CurD] * 8;
+}
+
+inline void Triangle_Write(int Reg, byte Val) {
+ switch (Reg) {
+ case 0:
+ Triangle.linear = Val & 0x7F;
+ Triangle.wavehold = (Val >> 7) & 0x1;
+ break;
+
+ case 2:
+ Triangle.freq &= 0x700;
+ Triangle.freq |= Val;
+ break;
+
+ case 3:
+ Triangle.freq &= 0xFF;
+ Triangle.freq |= (Val & 0x7) << 8;
+
+ if (Triangle.Enabled)
+ Triangle.Timer = LengthCounts[(Val >> 3) & 0x1F];
+
+ Triangle.LinClk = true;
+ break;
+
+ case 4:
+ if (!(Triangle.Enabled = Val ? true : false))
+ Triangle.Timer = 0;
+ break;
+ }
+ Triangle_CheckActive();
+}
+
+inline void Triangle_Run(void) {
+ if (!--Triangle.Cycles) {
+ Triangle.Cycles = Triangle.freq + 1;
+
+ if (Triangle.Active) {
+ Triangle.CurD++;
+ Triangle.CurD &= 0x1F;
+
+ if (Triangle.freq < 4)
+ Triangle.Pos = 0; // beyond hearing range
+ else
+ Triangle.Pos = TriDuty[Triangle.CurD] * 8;
+ }
+ }
+}
+
+inline void Triangle_QuarterFrame(void) {
+ if (Triangle.LinClk)
+ Triangle.LinCtr = Triangle.linear;
+ else if (Triangle.LinCtr)
+ Triangle.LinCtr--;
+
+ if (!Triangle.wavehold)
+ Triangle.LinClk = false;
+
+ Triangle_CheckActive();
+}
+
+inline void Triangle_HalfFrame(void) {
+ if (Triangle.Timer && !Triangle.wavehold)
+ Triangle.Timer--;
+
+ Triangle_CheckActive();
+}
+
+static struct {
+ byte volume, envelope, wavehold, datatype;
+ uint32 freq; // short
+ uint32 CurD; // short
+ byte Vol;
+ byte Timer;
+ byte EnvCtr, Envelope;
+ bool Enabled;
+ bool EnvClk;
+ uint32 Cycles; // short
+ int32 Pos;
+} Noise;
+
+const uint32 NoiseFreq[16] = {
+ 0x004,0x008,0x010,0x020,0x040,0x060,0x080,0x0A0,
+ 0x0CA,0x0FE,0x17C,0x1FC,0x2FA,0x3F8,0x7F2,0xFE4
+};
+
+inline void Noise_Write(int Reg, byte Val) {
+ switch (Reg) {
+ case 0:
+ Noise.volume = Val & 0x0F;
+ Noise.envelope = Val & 0x10;
+ Noise.wavehold = Val & 0x20;
+ Noise.Vol = Noise.envelope ? Noise.volume : Noise.Envelope;
+
+ if (Noise.Timer)
+ Noise.Pos = ((Noise.CurD & 0x4000) ? -2 : 2) * Noise.Vol;
+ break;
+
+ case 2:
+ Noise.freq = Val & 0xF;
+ Noise.datatype = Val & 0x80;
+ break;
+
+ case 3:
+ if (Noise.Enabled)
+ Noise.Timer = LengthCounts[(Val >> 3) & 0x1F];
+
+ Noise.EnvClk = true;
+ break;
+
+ case 4:
+ if (!(Noise.Enabled = Val ? true : false))
+ Noise.Timer = 0;
+ break;
+ }
+}
+
+inline void Noise_Run(void) {
+ if (!--Noise.Cycles) {
+ Noise.Cycles = NoiseFreq[Noise.freq]; /* no + 1 here */
+
+ if (Noise.datatype)
+ Noise.CurD = (Noise.CurD << 1) | (((Noise.CurD >> 14) ^ (Noise.CurD >> 8)) & 0x1);
+ else
+ Noise.CurD = (Noise.CurD << 1) | (((Noise.CurD >> 14) ^ (Noise.CurD >> 13)) & 0x1);
+
+ if (Noise.Timer)
+ Noise.Pos = ((Noise.CurD & 0x4000) ? -2 : 2) * Noise.Vol;
+ }
+}
+
+inline void Noise_QuarterFrame(void) {
+ if (Noise.EnvClk) {
+ Noise.EnvClk = false;
+ Noise.Envelope = 0xF;
+ Noise.EnvCtr = Noise.volume + 1;
+ } else if (!--Noise.EnvCtr) {
+ Noise.EnvCtr = Noise.volume + 1;
+
+ if (Noise.Envelope)
+ Noise.Envelope--;
+ else
+ Noise.Envelope = Noise.wavehold ? 0xF : 0x0;
+ }
+
+ Noise.Vol = Noise.envelope ? Noise.volume : Noise.Envelope;
+
+ if (Noise.Timer)
+ Noise.Pos = ((Noise.CurD & 0x4000) ? -2 : 2) * Noise.Vol;
+}
+
+inline void Noise_HalfFrame(void) {
+ if (Noise.Timer && !Noise.wavehold)
+ Noise.Timer--;
+}
+
+static struct {
+ uint32 Cycles;
+ int Num;
+} Frame;
+
+inline void Frame_Run(void) {
+ if (!--Frame.Cycles) {
+ Frame.Cycles = 7457;
+
+ if (Frame.Num < 4) {
+ Square0_QuarterFrame();
+ Square1_QuarterFrame();
+ Triangle_QuarterFrame();
+ Noise_QuarterFrame();
+
+ if (!(Frame.Num & 1)) {
+ Square0_HalfFrame();
+ Square1_HalfFrame();
+ Triangle_HalfFrame();
+ Noise_HalfFrame();
+ }
+ }
+
+ if (Frame.Num & 1)
+ Frame.Cycles++;
+
+ Frame.Num++;
+
+ if (Frame.Num == 5)
+ Frame.Num = 0;
+ }
+}
+
+void APU_WriteReg(int Addr, byte Val) {
+ switch (Addr) {
+ case 0x000: Square0_Write(0,Val); break;
+ case 0x001: Square0_Write(1,Val); break;
+ case 0x002: Square0_Write(2,Val); break;
+ case 0x003: Square0_Write(3,Val); break;
+ case 0x004: Square1_Write(0,Val); break;
+ case 0x005: Square1_Write(1,Val); break;
+ case 0x006: Square1_Write(2,Val); break;
+ case 0x007: Square1_Write(3,Val); break;
+ case 0x008: Triangle_Write(0,Val); break;
+ case 0x009: Triangle_Write(1,Val); break;
+ case 0x00A: Triangle_Write(2,Val); break;
+ case 0x00B: Triangle_Write(3,Val); break;
+ case 0x00C: Noise_Write(0,Val); break;
+ case 0x00D: Noise_Write(1,Val); break;
+ case 0x00E: Noise_Write(2,Val); break;
+ case 0x00F: Noise_Write(3,Val); break;
+ case 0x015: Square0_Write(4,Val & 0x1);
+ Square1_Write(4,Val & 0x2);
+ Triangle_Write(4,Val & 0x4);
+ Noise_Write(4,Val & 0x8);
+ break;
+ }
+}
+
+byte APU_Read4015(void) {
+ byte result =
+ (( Square0.Timer) ? 0x01 : 0) |
+ (( Square1.Timer) ? 0x02 : 0) |
+ ((Triangle.Timer) ? 0x04 : 0) |
+ (( Noise.Timer) ? 0x08 : 0);
+ return result;
+}
+
+void APU_Reset (void) {
+ APU.BufPos = 0;
+
+ memset(&Frame, 0, sizeof(Frame));
+ memset(&Square0, 0, sizeof(Square0));
+ memset(&Square1, 0, sizeof(Square1));
+ memset(&Triangle, 0, sizeof(Triangle));
+ memset(&Noise, 0, sizeof(Noise));
+
+ Noise.CurD = 1;
+ APU.Cycles = 1;
+ Square0.Cycles = 1;
+ Square0.EnvCtr = 1;
+ Square0.BendCtr = 1;
+ Square1.Cycles = 1;
+ Square1.EnvCtr = 1;
+ Square1.BendCtr = 1;
+ Triangle.Cycles = 1;
+ Noise.Cycles = 1;
+ Noise.EnvCtr = 1;
+ Frame.Cycles = 1;
+}
+
+int16 APU_GetSample(void) {
+ int sampcycles = 0, samppos = 0;
+ int NewBufPos = APU.BufPos;
+ while (NewBufPos == APU.BufPos) {
+ NewBufPos = APU.SampleRate * ++APU.Cycles / 1789773;
+ if (APU.Cycles == 1789773) // we've generated 1 second, so we can reset our counters now
+ APU.Cycles = NewBufPos = 0;
+
+ Frame_Run();
+ Square0_Run();
+ Square1_Run();
+ Triangle_Run();
+ Noise_Run();
+
+ samppos += Square0.Pos + Square1.Pos + Triangle.Pos + Noise.Pos;
+ sampcycles++;
+ }
+
+ APU.BufPos = NewBufPos;
+
+ return (samppos << 6) / sampcycles;
+}
+
+}
+
+Player_NES::Player_NES(ScummEngine *scumm) {
+ int i;
+ _vm = scumm;
+ _mixer = scumm->_mixer;
+ APUe::APU.SampleRate = _sample_rate = _mixer->getOutputRate();
+
+ _samples_per_frame = _sample_rate / 60;
+ _current_sample = 0;
+
+ for (i = 0; i < NUMSLOTS; i++) {
+ _slot[i].id = -1;
+ _slot[i].framesleft = 0;
+ _slot[i].type = 0;
+ _slot[i].offset = 0;
+ _slot[i].data = NULL;
+ }
+
+ for (i = 0; i < NUMCHANS; i++) {
+ _mchan[i].command = 0;
+ _mchan[i].framedelay = 0;
+ _mchan[i].pitch = 0;
+ _mchan[i].volume = 0;
+ _mchan[i].voldelta = 0;
+ _mchan[i].envflags = 0;
+ _mchan[i].cmdlock = 0;
+ }
+ isSFXplaying = wasSFXplaying = false;
+
+ auxData1 = auxData2 = NULL;
+ numNotes = 0;
+
+ APU_writeControl(0);
+
+ APUe::APU_Reset();
+
+ _mixer->setupPremix(this);
+}
+
+Player_NES::~Player_NES() {
+ _mixer->setupPremix(0);
+}
+
+void Player_NES::setMusicVolume (int vol) {
+ _maxvol = vol;
+}
+
+int Player_NES::readBuffer(int16 *buffer, const int numSamples) {
+ for (int n = 0; n < numSamples; n++) {
+ buffer[n] = APUe::APU_GetSample() * _maxvol / 255;
+ _current_sample++;
+
+ if (_current_sample == _samples_per_frame) {
+ _current_sample = 0;
+ sound_play();
+ }
+ }
+ return numSamples;
+}
+void Player_NES::stopAllSounds() {
+ for (int i = 0; i < NUMSLOTS; i++) {
+ _slot[i].framesleft = 0;
+ _slot[i].type = 0;
+ _slot[i].id = -1;
+ }
+
+ isSFXplaying = 0;
+ checkSilenceChannels(0);
+}
+
+void Player_NES::stopSound(int nr) {
+ if (nr == -1)
+ return;
+
+ for (int i = 0; i < NUMSLOTS; i++) {
+ if (_slot[i].id != nr)
+ continue;
+
+ isSFXplaying = 0;
+ _slot[i].framesleft = 0;
+ _slot[i].type = 0;
+ _slot[i].id = -1;
+ checkSilenceChannels(i);
+ }
+}
+
+void Player_NES::startSound(int nr) {
+ byte *data = _vm->getResourceAddress(rtSound, nr) + 2;
+ assert(data);
+
+ int soundType = data[1];
+ int chan = data[0];
+
+ if (chan == 4) {
+ if (_slot[2].framesleft)
+ return;
+ chan = 0;
+ }
+
+ if (soundType < _slot[chan].type)
+ return;
+
+ _slot[chan].type = soundType;
+ _slot[chan].id = nr;
+ _slot[chan].data = data;
+ _slot[chan].offset = 2;
+ _slot[chan].framesleft = 1;
+ checkSilenceChannels(chan);
+ if (chan == 2) {
+ numNotes = _slot[chan].data[2];
+ auxData1 = _slot[chan].data + 3;
+ auxData2 = auxData1 + numNotes;
+ _slot[chan].data = auxData2 + numNotes;
+ _slot[chan].offset = 0;
+
+ for (int i = 0; i < NUMCHANS; i++)
+ _mchan[i].cmdlock = 0;
+ }
+}
+
+void Player_NES::checkSilenceChannels(int chan) {
+ for (chan--; chan >= 0; chan--) {
+ if (_slot[chan].framesleft)
+ return;
+ }
+ APU_writeControl(0);
+}
+
+void Player_NES::sound_play() {
+ if (_slot[0].framesleft)
+ playSFX(0);
+ else if (_slot[1].framesleft)
+ playSFX(1);
+
+ playMusic();
+}
+
+void Player_NES::playSFX (int nr) {
+ if (--_slot[nr].framesleft)
+ return;
+
+ while (1) {
+ int a = _slot[nr].data[_slot[nr].offset++];
+ if (a < 16) {
+ a >>= 2;
+ APU_writeControl(APU_readStatus() | channelMask[a]);
+ isSFXplaying = true;
+ APU_writeChannel(a, 0, _slot[nr].data[_slot[nr].offset++]);
+ APU_writeChannel(a, 1, _slot[nr].data[_slot[nr].offset++]);
+ APU_writeChannel(a, 2, _slot[nr].data[_slot[nr].offset++]);
+ APU_writeChannel(a, 3, _slot[nr].data[_slot[nr].offset++]);
+ } else if (a == 0xFE) {
+ _slot[nr].offset = 2;
+ } else if (a == 0xFF) {
+ _slot[nr].id = -1;
+ _slot[nr].type = 0;
+ isSFXplaying = false;
+ APU_writeControl(0);
+
+ if (!nr && _slot[1].framesleft) {
+ _slot[1].framesleft = 1;
+ isSFXplaying = true;
+ }
+ return;
+ } else {
+ _slot[nr].framesleft = _slot[nr].data[_slot[nr].offset++];
+ return;
+ }
+ }
+}
+
+void Player_NES::playMusic() {
+ if (!_slot[2].framesleft)
+ return;
+
+ if (wasSFXplaying && !isSFXplaying)
+ for (int x = 1; x >= 0; x--)
+ if (_mchan[x].cmdlock) {
+ _mchan[x].command = _mchan[x].cmdlock;
+ _mchan[x].framedelay = 1;
+ }
+
+ wasSFXplaying = isSFXplaying;
+ if (!--_slot[2].framesleft) {
+top:
+ int b = _slot[2].data[_slot[2].offset++];
+ if (b == 0xFF) {
+ _slot[2].id = -1;
+ _slot[2].type = 0;
+ b = 0;
+ } else if (b == 0xFE) {
+ _slot[2].offset = 0;
+ goto top;
+ } else {
+ if (b < numNotes) {
+ int inst = auxData1[b];
+ int ch = instChannel[inst];
+ _mchan[ch].pitch = auxData2[b];
+ _mchan[ch].cmdlock = startCmd[inst];
+ _mchan[ch].command = startCmd[inst];
+ _mchan[ch].framedelay = 1;
+ goto top;
+ }
+ b -= numNotes;
+ if (b < 16) {
+ int inst = b;
+ int ch = instChannel[inst];
+ _mchan[ch].cmdlock = 0;
+ _mchan[ch].command = releaseCmd[inst];
+ _mchan[ch].framedelay = 1;
+ goto top;
+ }
+ b -= 16;
+ }
+ _slot[2].framesleft = b;
+ }
+
+ for (int x = NUMCHANS - 1; x >= 0; x--) {
+ if (_slot[0].framesleft || _slot[1].framesleft) {
+ _mchan[x].volume = 0;
+ _mchan[x].framedelay = 0;
+ continue;
+ }
+
+ if (_mchan[x].framedelay && !--_mchan[x].framedelay) {
+ switch (_mchan[x].command) {
+ case 0x00:
+ case 0x13:
+ _mchan[x].voldelta = -10;
+ break;
+
+ case 0x01:
+ case 0x03:
+ case 0x08:
+ case 0x16:
+ _mchan[x].envflags = 0x30;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x02:
+ _mchan[x].envflags = 0xB0;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x84);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x04:
+ _mchan[x].envflags = 0x80;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x05:
+ _mchan[x].envflags = 0xF0;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = -15;
+
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x06:
+ _mchan[x].pitch += 0x18;
+ _mchan[x].envflags = 0x80;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x07:
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch - 0x0C] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch - 0x0C] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x09:
+ _mchan[x].voldelta = -2;
+
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x0A:
+ APU_writeChannel(x, 1, 0x86);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x0B: case 0x1A:
+ _mchan[x].envflags = 0x70;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x0C:
+ _mchan[x].envflags = 0xB0;
+
+ chainCommand(x);
+ break;
+
+ case 0x0D:
+ _mchan[x].envflags = 0x30;
+ _mchan[x].volume = 0x5F;
+ _mchan[x].voldelta = -22;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, _mchan[x].pitch & 0xF);
+ APU_writeChannel(x, 3, 0xFF);
+
+ chainCommand(x);
+ break;
+
+ case 0x0E:
+ case 0x10:
+ _mchan[x].envflags = 0x30;
+ _mchan[x].volume = 0x5F;
+ _mchan[x].voldelta = -6;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, _mchan[x].pitch & 0xF);
+ APU_writeChannel(x, 3, 0xFF);
+
+ chainCommand(x);
+ break;
+
+ case 0x0F:
+ chainCommand(x);
+ break;
+
+ case 0x11:
+ APU_writeChannel(x, 2, _mchan[x].pitch & 0xF);
+ APU_writeChannel(x, 3, 0xFF);
+
+ chainCommand(x);
+ break;
+
+ case 0x12:
+ APU_writeChannel(x, 2, (_mchan[x].pitch + 3) & 0xF);
+ APU_writeChannel(x, 3, 0xFF);
+
+ chainCommand(x);
+ break;
+
+ case 0x14:
+ _mchan[x].voldelta = -12;
+
+ APU_writeChannel(x, 1, 0x8C);
+
+ chainCommand(x);
+ break;
+
+ case 0x15:
+ _mchan[x].voldelta = -12;
+
+ APU_writeChannel(x, 1, 0x84);
+
+ chainCommand(x);
+ break;
+
+ case 0x17:
+ _mchan[x].pitch += 0x0C;
+ _mchan[x].envflags = 0x80;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x18:
+ _mchan[x].envflags = 0x70;
+
+ chainCommand(x);
+ break;
+
+ case 0x19:
+ _mchan[x].envflags = 0xB0;
+
+ chainCommand(x);
+ break;
+
+ case 0x1B:
+ _mchan[x].envflags = 0x00;
+ _mchan[x].voldelta = -10;
+ break;
+ }
+ }
+
+ _mchan[x].volume += _mchan[x].voldelta;
+
+ if (_mchan[x].volume < 0)
+ _mchan[x].volume = 0;
+ if (_mchan[x].volume > MAXVOLUME)
+ _mchan[x].volume = MAXVOLUME;
+
+ APU_writeChannel(x, 0, (_mchan[x].volume >> 3) | _mchan[x].envflags);
+ }
+}
+
+void Player_NES::chainCommand(int c) {
+ int i = _mchan[c].command;
+ _mchan[c].command = nextCmd[i];
+ _mchan[c].framedelay = nextDelay[i];
+}
+
+int Player_NES::getSoundStatus(int nr) const {
+ for (int i = 0; i < NUMSLOTS; i++)
+ if (_slot[i].id == nr)
+ return 1;
+ return 0;
+}
+
+void Player_NES::APU_writeChannel(int chan, int offset, byte value) {
+ APUe::APU_WriteReg(0x000 + 4 * chan + offset, value);
+}
+void Player_NES::APU_writeControl(byte value) {
+ APUe::APU_WriteReg(0x015, value);
+}
+byte Player_NES::APU_readStatus() {
+ return APUe::APU_Read4015();
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_nes.h b/engines/scumm/player_nes.h
new file mode 100644
index 0000000000..17a11b2fe3
--- /dev/null
+++ b/engines/scumm/player_nes.h
@@ -0,0 +1,115 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef PLAYER_NES_H
+#define PLAYER_NES_H
+
+#include "common/scummsys.h"
+#include "scumm/music.h"
+#include "sound/audiostream.h"
+
+namespace Audio {
+ class Mixer;
+}
+
+namespace Scumm {
+
+class ScummEngine;
+
+static const int MAXVOLUME = 0x7F;
+static const int NUMSLOTS = 3;
+static const int NUMCHANS = 4;
+
+/**
+ * Scumm NES sound/music driver.
+ */
+class Player_NES : public AudioStream, public MusicEngine {
+public:
+ Player_NES(ScummEngine *scumm);
+ virtual ~Player_NES();
+
+ virtual void setMusicVolume(int vol);
+ virtual void startSound(int sound);
+ virtual void stopSound(int sound);
+ virtual void stopAllSounds();
+ virtual int getSoundStatus(int sound) const;
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return false; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _sample_rate; }
+
+private:
+
+ void sound_play();
+ void playSFX(int nr);
+ void playMusic();
+ byte fetchSoundByte(int nr);
+ void chainCommand(int chan);
+ void checkSilenceChannels(int chan);
+
+ void APU_writeChannel(int chan, int offset, byte value);
+ void APU_writeControl(byte value);
+ byte APU_readStatus();
+
+ void do_mix(int16 *buf, uint len);
+
+ ScummEngine *_vm;
+ Audio::Mixer *_mixer;
+ int _sample_rate;
+ int _samples_per_frame;
+ int _current_sample;
+ int _maxvol;
+
+ struct slot {
+ int framesleft;
+ int id;
+ int type;
+ byte *data;
+ int offset;
+ } _slot[NUMSLOTS];
+
+ struct mchan {
+ int command;
+ int framedelay;
+ int pitch;
+ int volume;
+ int voldelta;
+ int envflags;
+ int cmdlock;
+ } _mchan[NUMCHANS];
+
+ bool isSFXplaying, wasSFXplaying;
+
+ byte *dataStart;
+ int numNotes;
+ byte *auxData1;
+ byte *auxData2;
+
+ byte *soundptr;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_v1.cpp b/engines/scumm/player_v1.cpp
new file mode 100644
index 0000000000..74605b20d1
--- /dev/null
+++ b/engines/scumm/player_v1.cpp
@@ -0,0 +1,608 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "base/engine.h"
+#include "scumm/player_v1.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+#define FB_WNOISE 0x12000 /* feedback for white noise */
+#define FB_PNOISE 0x08000 /* feedback for periodic noise */
+
+#define TIMER_BASE_FREQ 1193000
+#define FIXP_SHIFT 16
+
+Player_V1::Player_V1(ScummEngine *scumm, bool pcjr) : Player_V2(scumm, pcjr) {
+ // Initialize channel code
+ for (int i = 0; i < 4; ++i)
+ clear_channel(i);
+
+ _mplex_step = (_sample_rate << FIXP_SHIFT) / 1193000;
+ _next_chunk = _repeat_chunk = 0;
+ _forced_level = 0;
+ _random_lsr = 0;
+}
+
+Player_V1::~Player_V1() {
+}
+
+void Player_V1::chainSound(int nr, byte *data) {
+ uint i;
+ for (i = 0; i < 4; ++i)
+ clear_channel(i);
+
+ _current_nr = nr;
+ _current_data = data;
+ _repeat_chunk = _next_chunk = data + (_pcjr ? 2 : 4);
+
+ debug(4, "Chaining new sound %d", nr);
+ if (_pcjr)
+ parsePCjrChunk();
+ else
+ parseSpeakerChunk();
+}
+
+void Player_V1::startSound(int nr) {
+ byte *data = _vm->getResourceAddress(rtSound, nr);
+ assert(data);
+
+ mutex_up();
+
+ int offset = _pcjr ? READ_LE_UINT16(data+4) : 6;
+ int cprio = _current_data ? *(_current_data) & 0x7f : 0;
+ int prio = *(data + offset) & 0x7f;
+ int restartable = *(data + offset) & 0x80;
+
+ debug(4, "startSound %d: prio %d%s, cprio %d",
+ nr, prio, restartable ? " restartable" : "", cprio);
+
+ if (!_current_nr || cprio <= prio) {
+ if (_current_data && (*(_current_data) & 0x80)) {
+ _next_nr = _current_nr;
+ _next_data = _current_data;
+ }
+
+ chainSound(nr, data + offset);
+ }
+ mutex_down();
+}
+
+void Player_V1::stopAllSounds() {
+ mutex_up();
+ for (int i = 0; i < 4; i++)
+ clear_channel(i);
+ _repeat_chunk = _next_chunk = 0;
+ _next_nr = _current_nr = 0;
+ _next_data = _current_data = 0;
+ mutex_down();
+}
+
+void Player_V1::stopSound(int nr) {
+ mutex_up();
+ if (_next_nr == nr) {
+ _next_nr = 0;
+ _next_data = 0;
+ }
+ if (_current_nr == nr) {
+ for (int i = 0; i < 4; i++) {
+ clear_channel(i);
+ }
+ _repeat_chunk = _next_chunk = 0;
+ _current_nr = 0;
+ _current_data = 0;
+ chainNextSound();
+ }
+ mutex_down();
+}
+
+void Player_V1::clear_channel(int i) {
+ _channels[i].freq = 0;
+ _channels[i].volume = 15;
+}
+
+int Player_V1::getMusicTimer() const {
+ /* Do V1 games have a music timer? */
+ return 0;
+}
+
+void Player_V1::parseSpeakerChunk() {
+ set_mplex(3000);
+ _forced_level = 0;
+
+ parse_again:
+ _chunk_type = READ_LE_UINT16(_next_chunk);
+ debug(6, "parseSpeakerChunk: sound %d, offset %4x, chunk %x",
+ _current_nr, _next_chunk - _current_data, _chunk_type);
+
+ _next_chunk += 2;
+ switch (_chunk_type) {
+ case 0xffff:
+ _current_nr = 0;
+ _current_data = 0;
+ _channels[0].freq = 0;
+ _next_chunk = 0;
+ chainNextSound();
+ break;
+ case 0xfffe:
+ _repeat_chunk = _next_chunk;
+ goto parse_again;
+
+ case 0xfffd:
+ _next_chunk = _repeat_chunk;
+ goto parse_again;
+
+ case 0xfffc:
+ /* handle reset. We don't need this do we? */
+ goto parse_again;
+
+ case 0:
+ _time_left = 1;
+ set_mplex(READ_LE_UINT16(_next_chunk));
+ _next_chunk += 2;
+ break;
+ case 1:
+ set_mplex(READ_LE_UINT16(_next_chunk));
+ _start = READ_LE_UINT16(_next_chunk + 2);
+ _end = READ_LE_UINT16(_next_chunk + 4);
+ _delta = (int16) READ_LE_UINT16(_next_chunk + 6);
+ _repeat_ctr = READ_LE_UINT16(_next_chunk + 8);
+ _channels[0].freq = _start;
+ _next_chunk += 10;
+ debug(6, "chunk 1: mplex %d, freq %d -> %d step %d x %d",
+ _mplex, _start, _end, _delta, _repeat_ctr);
+ break;
+ case 2:
+ _start = READ_LE_UINT16(_next_chunk);
+ _end = READ_LE_UINT16(_next_chunk + 2);
+ _delta = (int16) READ_LE_UINT16(_next_chunk + 4);
+ _channels[0].freq = 0;
+ _next_chunk += 6;
+ _forced_level = -1;
+ debug(6, "chunk 2: %d -> %d step %d",
+ _start, _end, _delta);
+ break;
+ case 3:
+ _start = READ_LE_UINT16(_next_chunk);
+ _end = READ_LE_UINT16(_next_chunk + 2);
+ _delta = (int16) READ_LE_UINT16(_next_chunk + 4);
+ _channels[0].freq = 0;
+ _next_chunk += 6;
+ _forced_level = -1;
+ debug(6, "chunk 3: %d -> %d step %d",
+ _start, _end, _delta);
+ break;
+ }
+}
+
+void Player_V1::nextSpeakerCmd() {
+ uint16 lsr;
+ switch (_chunk_type) {
+ case 0:
+ if (--_time_left)
+ return;
+ _time_left = READ_LE_UINT16(_next_chunk);
+ _next_chunk += 2;
+ if (_time_left == 0xfffb) {
+ /* handle 0xfffb?? */
+ _time_left = READ_LE_UINT16(_next_chunk);
+ _next_chunk += 2;
+ }
+ debug(7, "nextSpeakerCmd: chunk %d, offset %4x: notelen %d",
+ _chunk_type, _next_chunk - 2 - _current_data, _time_left);
+ if (_time_left == 0) {
+ parseSpeakerChunk();
+ } else {
+ _channels[0].freq = READ_LE_UINT16(_next_chunk);
+ _next_chunk += 2;
+ debug(7, "freq_current: %d", _channels[0].freq);
+ }
+ break;
+
+ case 1:
+ _channels[0].freq = (_channels[0].freq + _delta) & 0xffff;
+ if (_channels[0].freq == _end) {
+ if (!--_repeat_ctr) {
+ parseSpeakerChunk();
+ return;
+ }
+ _channels[0].freq = _start;
+ }
+ break;
+
+ case 2:
+ _start = (_start + _delta) & 0xffff;
+ if (_start == _end) {
+ parseSpeakerChunk();
+ return;
+ }
+ set_mplex(_start);
+ _forced_level = -_forced_level;
+ break;
+ case 3:
+ _start = (_start + _delta) & 0xffff;
+ if (_start == _end) {
+ parseSpeakerChunk();
+ return;
+ }
+ lsr = _random_lsr + 0x9248;
+ lsr = (lsr >> 3) | (lsr << 13);
+ _random_lsr = lsr;
+ set_mplex((_start & lsr) | 0x180);
+ _forced_level = -_forced_level;
+ break;
+ }
+}
+
+void Player_V1::parsePCjrChunk() {
+ uint tmp;
+ uint i;
+
+ set_mplex(3000);
+ _forced_level = 0;
+
+parse_again:
+
+ _chunk_type = READ_LE_UINT16(_next_chunk);
+ debug(6, "parsePCjrChunk: sound %d, offset %4x, chunk %x",
+ _current_nr, _next_chunk - _current_data, _chunk_type);
+
+ _next_chunk += 2;
+ switch (_chunk_type) {
+ case 0xffff:
+ for (i = 0; i < 4; ++i)
+ clear_channel(i);
+ _current_nr = 0;
+ _current_data = 0;
+ _repeat_chunk = _next_chunk = 0;
+ chainNextSound();
+ break;
+
+ case 0xfffe:
+ _repeat_chunk = _next_chunk;
+ goto parse_again;
+
+ case 0xfffd:
+ _next_chunk = _repeat_chunk;
+ goto parse_again;
+
+ case 0xfffc:
+ /* handle reset. We don't need this do we? */
+ goto parse_again;
+
+ case 0:
+ set_mplex(READ_LE_UINT16(_next_chunk));
+ _next_chunk += 2;
+ for (i = 0; i < 4; i++) {
+ tmp = READ_LE_UINT16(_next_chunk);
+ _next_chunk += 2;
+ if (tmp == 0xffff) {
+ _channels[i].cmd_ptr = 0;
+ continue;
+ }
+ _channels[i].attack = READ_LE_UINT16(_current_data + tmp);
+ _channels[i].decay = READ_LE_UINT16(_current_data + tmp + 2);
+ _channels[i].level = READ_LE_UINT16(_current_data + tmp + 4);
+ _channels[i].sustain_1 = READ_LE_UINT16(_current_data + tmp + 6);
+ _channels[i].sustain_2 = READ_LE_UINT16(_current_data + tmp + 8);
+ _channels[i].notelen = 1;
+ _channels[i].volume = 15;
+ _channels[i].cmd_ptr = _current_data + tmp + 10;
+ }
+ break;
+
+ case 1:
+ set_mplex(READ_LE_UINT16(_next_chunk));
+ tmp = READ_LE_UINT16(_next_chunk + 2);
+ _channels[0].cmd_ptr = tmp != 0xffff ? _current_data + tmp : NULL;
+ tmp = READ_LE_UINT16(_next_chunk + 4);
+ _start = READ_LE_UINT16(_next_chunk + 6);
+ _delta = (int16) READ_LE_UINT16(_next_chunk + 8);
+ _time_left = READ_LE_UINT16(_next_chunk + 10);
+ _next_chunk += 12;
+ if (tmp >= 0xe0) {
+ _channels[3].freq = tmp & 0xf;
+ _value_ptr = &_channels[3].volume;
+ } else {
+ assert(!(tmp & 0x10));
+ tmp = (tmp & 0x60) >> 5;
+ _value_ptr = &_channels[tmp].freq;
+ _channels[tmp].volume = 0;
+ }
+ *_value_ptr = _start;
+ if (_channels[0].cmd_ptr) {
+ tmp = READ_LE_UINT16(_channels[0].cmd_ptr);
+ _start_2 = READ_LE_UINT16(_channels[0].cmd_ptr + 2);
+ _delta_2 = (int16) READ_LE_UINT16(_channels[0].cmd_ptr + 4);
+ _time_left_2 = READ_LE_UINT16(_channels[0].cmd_ptr + 6);
+ _channels[0].cmd_ptr += 8;
+ if (_value_ptr == &_channels[3].volume) {
+ tmp = (tmp & 0x70) >> 4;
+ if (tmp & 1)
+ _value_ptr_2 = &_channels[tmp >> 1].volume;
+ else
+ _value_ptr_2 = &_channels[tmp >> 1].freq;
+ } else {
+ assert(!(tmp & 0x10));
+ tmp = (tmp & 0x60) >> 5;
+ _value_ptr_2 = &_channels[tmp].freq;
+ _channels[tmp].volume = 0;
+ }
+ *_value_ptr_2 = _start_2;
+ }
+ debug(6, "chunk 1: %d: %d step %d for %d, %d: %d step %d for %d",
+ _value_ptr - (uint*)_channels, _start, _delta, _time_left,
+ _value_ptr_2 - (uint*)_channels, _start_2, _delta_2, _time_left_2);
+ break;
+
+ case 2:
+ _start = READ_LE_UINT16(_next_chunk);
+ _end = READ_LE_UINT16(_next_chunk + 2);
+ _delta = (int16) READ_LE_UINT16(_next_chunk + 4);
+ _channels[0].freq = 0;
+ _next_chunk += 6;
+ _forced_level = -1;
+ debug(6, "chunk 2: %d -> %d step %d",
+ _start, _end, _delta);
+ break;
+ case 3:
+ set_mplex(READ_LE_UINT16(_next_chunk));
+ tmp = READ_LE_UINT16(_next_chunk + 2);
+ assert((tmp & 0xf0) == 0xe0);
+ _channels[3].freq = tmp & 0xf;
+ if ((tmp & 3) == 3) {
+ _next_chunk += 2;
+ _channels[2].freq = READ_LE_UINT16(_next_chunk + 2);
+ }
+ _channels[3].volume = READ_LE_UINT16(_next_chunk + 4);
+ _repeat_ctr = READ_LE_UINT16(_next_chunk + 6);
+ _delta = (int16) READ_LE_UINT16(_next_chunk + 8);
+ _next_chunk += 10;
+ break;
+ }
+}
+
+void Player_V1::nextPCjrCmd() {
+ uint i;
+ int dummy;
+ switch (_chunk_type) {
+ case 0:
+ for (i = 0; i < 4; i++) {
+ if (!_channels[i].cmd_ptr)
+ continue;
+ if (!--_channels[i].notelen) {
+ dummy = READ_LE_UINT16(_channels[i].cmd_ptr);
+ if (dummy >= 0xfffe) {
+ if (dummy == 0xfffe)
+ _next_chunk = _current_data + 2;
+ parsePCjrChunk();
+ return;
+ }
+ _channels[i].notelen = 4 * dummy;
+ dummy = READ_LE_UINT16(_channels[i].cmd_ptr + 2);
+ if (dummy == 0) {
+ _channels[i].hull_counter = 4;
+ _channels[i].sustctr = _channels[i].sustain_2;
+ } else {
+ _channels[i].hull_counter = 1;
+ _channels[i].freq = dummy;
+ }
+ debug(7, "chunk 0: channel %d play %d for %d",
+ i, dummy, _channels[i].notelen);
+ _channels[i].cmd_ptr += 4;
+ }
+
+
+ switch (_channels[i].hull_counter) {
+ case 1:
+ _channels[i].volume -= _channels[i].attack;
+ if ((int) _channels[i].volume <= 0) {
+ _channels[i].volume = 0;
+ _channels[i].hull_counter++;
+ }
+ break;
+ case 2:
+ _channels[i].volume += _channels[i].decay;
+ if (_channels[i].volume >= _channels[i].level) {
+ _channels[i].volume = _channels[i].level;
+ _channels[i].hull_counter++;
+ }
+ break;
+ case 4:
+ if (--_channels[i].sustctr < 0) {
+ _channels[i].sustctr = _channels[i].sustain_2;
+ _channels[i].volume += _channels[i].sustain_1;
+ if ((int) _channels[i].volume >= 15) {
+ _channels[i].volume = 15;
+ _channels[i].hull_counter++;
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case 1:
+ _start += _delta;
+ *_value_ptr = _start;
+ if (!--_time_left) {
+ _start = READ_LE_UINT16(_next_chunk);
+ _next_chunk += 2;
+ if (_start == 0xffff) {
+ parsePCjrChunk();
+ return;
+ }
+ _delta = (int16) READ_LE_UINT16(_next_chunk);
+ _time_left = READ_LE_UINT16(_next_chunk + 2);
+ _next_chunk += 4;
+ *_value_ptr = _start;
+ }
+
+ if (_channels[0].cmd_ptr) {
+ _start_2 += _delta_2;
+ *_value_ptr_2 = _start_2;
+ if (!--_time_left_2) {
+ _start_2 = READ_LE_UINT16(_channels[0].cmd_ptr);
+ if (_start_2 == 0xffff) {
+ _next_chunk = _channels[0].cmd_ptr + 2;
+ parsePCjrChunk();
+ return;
+ }
+ _delta_2 = (int16) READ_LE_UINT16(_channels[0].cmd_ptr + 2);
+ _time_left_2 = READ_LE_UINT16(_channels[0].cmd_ptr + 4);
+ _channels[0].cmd_ptr += 6;
+ }
+ }
+ break;
+
+ case 2:
+ _start += _delta;
+ if (_start == _end) {
+ parsePCjrChunk();
+ return;
+ }
+ set_mplex(_start);
+ debug(7, "chunk 2: mplex %d curve %d", _start, _forced_level);
+ _forced_level = -_forced_level;
+ break;
+ case 3:
+ dummy = _channels[3].volume + _delta;
+ if (dummy >= 15) {
+ _channels[3].volume = 15;
+ } else if (dummy <= 0) {
+ _channels[3].volume = 0;
+ } else {
+ _channels[3].volume = dummy;
+ break;
+ }
+
+ if (!--_repeat_ctr) {
+ parsePCjrChunk();
+ return;
+ }
+ _delta = READ_LE_UINT16(_next_chunk);
+ _next_chunk += 2;
+ break;
+ }
+}
+
+void Player_V1::set_mplex(uint mplex) {
+ if (mplex == 0)
+ mplex = 65536;
+ _mplex = mplex;
+ _tick_len = _mplex_step * mplex;
+}
+
+void Player_V1::nextTick() {
+ if (_next_chunk) {
+ if (_pcjr)
+ nextPCjrCmd();
+ else
+ nextSpeakerCmd();
+ }
+}
+
+void Player_V1::generateSpkSamples(int16 *data, uint len) {
+ uint i;
+
+ memset(data, 0, 2 * sizeof(int16) * len);
+ if (_channels[0].freq == 0) {
+ if (_forced_level) {
+ int sample = _forced_level * _volumetable[0];
+ for (i = 0; i < len; i++)
+ data[2*i] = data[2*i+1] = sample;
+ debug(9, "speaker: %8x: forced one", _tick_len);
+ } else if (!_level) {
+ return;
+ }
+ } else {
+ squareGenerator(0, _channels[0].freq, 0, 0, data, len);
+ debug(9, "speaker: %8x: freq %d %.1f", _tick_len,
+ _channels[0].freq, 1193000.0 / _channels[0].freq);
+ }
+ lowPassFilter(data, len);
+}
+
+void Player_V1::generatePCjrSamples(int16 *data, uint len) {
+ uint i, j;
+ uint freq, vol;
+ bool hasdata = false;
+
+ memset(data, 0, 2 * sizeof(int16) * len);
+
+ if (_forced_level) {
+ int sample = _forced_level * _volumetable[0];
+ for (i = 0; i < len; i++)
+ data[2*i] = data[2*i+1] = sample;
+ hasdata = true;
+ debug(9, "channel[4]: %8x: forced one", _tick_len);
+ }
+
+ for (i = 1; i < 3; i++) {
+ freq = _channels[i].freq;
+ if (freq) {
+ for (j = 0; j < i; j++) {
+ if (freq == _channels[j].freq) {
+ /* HACK: this channel is playing at
+ * the same frequency as another.
+ * Synchronize it to the same phase to
+ * prevent interference.
+ */
+ _timer_count[i] = _timer_count[j];
+ _timer_output ^= (1 << i) &
+ (_timer_output ^ _timer_output << (i - j));
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ freq = _channels[i].freq;
+ vol = _channels[i].volume;
+ if (!_volumetable[_channels[i].volume]) {
+ _timer_count[i] -= len << FIXP_SHIFT;
+ if (_timer_count[i] < 0)
+ _timer_count[i] = 0;
+ } else if (i < 3) {
+ hasdata = true;
+ squareGenerator(i, freq, vol, 0, data, len);
+ debug(9, "channel[%d]: %8x: freq %d %.1f ; volume %d",
+ i, _tick_len, freq, 111860.0 / freq, vol);
+ } else {
+ int noiseFB = (freq & 4) ? FB_WNOISE : FB_PNOISE;
+ int n = (freq & 3);
+
+ freq = (n == 3) ? 2 * (_channels[2].freq) : 1 << (5 + n);
+ hasdata = true;
+ squareGenerator(i, freq, vol, noiseFB, data, len);
+ debug(9, "channel[%d]: %x: noise freq %d %.1f ; volume %d",
+ i, _tick_len, freq, 111860.0 / freq, vol);
+ }
+ }
+
+ if (_level || hasdata)
+ lowPassFilter(data, len);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_v1.h b/engines/scumm/player_v1.h
new file mode 100644
index 0000000000..60ba8ad26d
--- /dev/null
+++ b/engines/scumm/player_v1.h
@@ -0,0 +1,99 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef PLAYER_V1_H
+#define PLAYER_V1_H
+
+#include "scumm/player_v2.h"
+
+namespace Scumm {
+
+/**
+ * Scumm V1 PC-Speaker player.
+ */
+class Player_V1 : public Player_V2 {
+public:
+ Player_V1(ScummEngine *scumm, bool pcjr);
+ ~Player_V1();
+
+ virtual void startSound(int sound);
+ virtual void stopSound(int sound);
+ virtual void stopAllSounds();
+ virtual int getMusicTimer() const;
+
+protected:
+ virtual void nextTick();
+ virtual void clear_channel(int i);
+ virtual void chainSound(int nr, byte *data);
+
+ virtual void generateSpkSamples(int16 *data, uint len);
+ virtual void generatePCjrSamples(int16 *data, uint len);
+
+ void restartSound();
+
+ void set_mplex(uint mplex);
+ void parseSpeakerChunk();
+ void nextSpeakerCmd();
+ void parsePCjrChunk();
+ void nextPCjrCmd();
+
+private:
+ struct channel_data_v1 {
+ uint freq;
+ uint volume;
+ byte *cmd_ptr;
+ uint notelen;
+ uint hull_counter;
+ uint attack;
+ uint decay;
+ uint level;
+ uint sustain_1;
+ uint sustain_2;
+ int sustctr;
+ };
+
+ channel_data_v1 _channels[4];
+
+ byte *_next_chunk;
+ byte *_repeat_chunk;
+ uint _chunk_type;
+ uint _mplex_step;
+ uint _mplex;
+ uint _repeat_ctr;
+ uint _freq_current;
+ int _forced_level;
+ uint16 _random_lsr;
+ uint *_value_ptr;
+ uint _time_left;
+ uint _start;
+ uint _end;
+ int _delta;
+ uint *_value_ptr_2;
+ uint _time_left_2;
+ uint _start_2;
+ int _delta_2;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_v2.cpp b/engines/scumm/player_v2.cpp
new file mode 100644
index 0000000000..70aff98bb9
--- /dev/null
+++ b/engines/scumm/player_v2.cpp
@@ -0,0 +1,1005 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "base/engine.h"
+#include "scumm/player_v2.h"
+#include "scumm/scumm.h"
+#include "sound/mididrv.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+#define FREQ_HZ 236 // Don't change!
+
+#define SPK_DECAY 0xa000 /* Depends on sample rate */
+#define PCJR_DECAY 0xa000 /* Depends on sample rate */
+
+#define FIXP_SHIFT 16
+#define MAX_OUTPUT 0x7fff
+
+#define NG_PRESET 0x0f35 /* noise generator preset */
+#define FB_WNOISE 0x12000 /* feedback for white noise */
+#define FB_PNOISE 0x08000 /* feedback for periodic noise */
+
+#ifdef PALMOS_68K
+const uint8 *note_lengths;
+static const uint16 *hull_offsets;
+static const int16 *hulls;
+static const uint16 *freqmod_lengths;
+static const uint16 *freqmod_offsets;
+static const int8 *freqmod_table;
+static const uint16 *spk_freq_table;
+static const uint16 *pcjr_freq_table;
+#else
+const uint8 note_lengths[] = {
+ 0,
+ 0, 0, 2,
+ 0, 3, 4,
+ 5, 6, 8,
+ 9, 12, 16,
+ 18, 24, 32,
+ 36, 48, 64,
+ 72, 96
+};
+
+static const uint16 hull_offsets[] = {
+ 0, 12, 24, 36, 48, 60,
+ 72, 88, 104, 120, 136, 256,
+ 152, 164, 180
+};
+
+static const int16 hulls[] = {
+ // hull 0
+ 3, -1, 0, 0, 0, 0, 0, 0,
+ 0, -1, 0, 0,
+ // hull 1 (staccato)
+ 3, -1, 0, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 2 (legato)
+ 3, -1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ // hull 3 (staccatissimo)
+ 3, -1, 0, 2, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 4
+ 3, -1, 0, 6, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 5
+ 3, -1, 0, 16, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ // hull 6
+ (int16) 60000, -1, -1000, 20, 0, 0, 0, 0,
+ (int16) 40000, -1, -5000, 5, 0, -1, 0, 0,
+ // hull 7
+ (int16) 50000, -1, 0, 8, 30000, -1, 0, 0,
+ 28000, -1, -5000, 5, 0, -1, 0, 0,
+ // hull 8
+ (int16) 60000, -1, -2000, 16, 0, 0, 0, 0,
+ 28000, -1, -6000, 5, 0, -1, 0, 0,
+ // hull 9
+ (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0,
+ (int16) 40000, -1, -2000, 10, 0, -1, 0, 0,
+ // hull 10
+ (int16) 60000, -1, 0, 4, -2000, 8, 0, 0,
+ (int16) 40000, -1, -6000, 5, 0, -1, 0, 0,
+ // hull 12
+ 0, -1, 150, 340, -150, 340, 0, -1,
+ 0, -1, 0, 0,
+ // hull 13 == 164
+ 20000, -1, 4000, 7, 1000, 15, 0, 0,
+ (int16) 35000, -1, -2000, 15, 0, -1, 0, 0,
+
+ // hull 14 == 180
+ (int16) 35000, -1, 500, 20, 0, 0, 0, 0,
+ (int16) 45000, -1, -500, 60, 0, -1, 0, 0,
+
+ // hull misc = 196
+ (int16) 44000, -1, -4400, 10, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, -5300, 10, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 63000, -1, -6300, 10, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 44000, -1, -1375, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, -1656, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ // hull 11 == 256
+ (int16) 63000, -1, -1968, 32, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 44000, -1, - 733, 60, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, - 883, 60, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 63000, -1, -1050, 60, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 44000, -1, - 488, 90, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 53000, -1, - 588, 90, 0, -1, 0, 0,
+ 0, -1, 0, 0,
+
+ (int16) 63000, -1, - 700, 90, 0, -1, 0, 0,
+ 0, -1, 0, 0
+};
+
+static const uint16 freqmod_lengths[] = {
+ 0x1000, 0x1000, 0x20, 0x2000, 0x1000
+};
+
+static const uint16 freqmod_offsets[] = {
+ 0, 0x100, 0x200, 0x302, 0x202
+};
+
+static const int8 freqmod_table[0x502] = {
+ 0, 3, 6, 9, 12, 15, 18, 21,
+ 24, 27, 30, 33, 36, 39, 42, 45,
+ 48, 51, 54, 57, 59, 62, 65, 67,
+ 70, 73, 75, 78, 80, 82, 85, 87,
+ 89, 91, 94, 96, 98, 100, 102, 103,
+ 105, 107, 108, 110, 112, 113, 114, 116,
+ 117, 118, 119, 120, 121, 122, 123, 123,
+ 124, 125, 125, 126, 126, 126, 126, 126,
+ 126, 126, 126, 126, 126, 126, 125, 125,
+ 124, 123, 123, 122, 121, 120, 119, 118,
+ 117, 116, 114, 113, 112, 110, 108, 107,
+ 105, 103, 102, 100, 98, 96, 94, 91,
+ 89, 87, 85, 82, 80, 78, 75, 73,
+ 70, 67, 65, 62, 59, 57, 54, 51,
+ 48, 45, 42, 39, 36, 33, 30, 27,
+ 24, 21, 18, 15, 12, 9, 6, 3,
+ 0, -3, -6, -9, -12, -15, -18, -21,
+ -24, -27, -30, -33, -36, -39, -42, -45,
+ -48, -51, -54, -57, -59, -62, -65, -67,
+ -70, -73, -75, -78, -80, -82, -85, -87,
+ -89, -91, -94, -96, -98,-100,-102,-103,
+ -105,-107,-108,-110,-112,-113,-114,-116,
+ -117,-118,-119,-120,-121,-122,-123,-123,
+ -124,-125,-125,-126,-126,-126,-126,-126,
+ -126,-126,-126,-126,-126,-126,-125,-125,
+ -124,-123,-123,-122,-121,-120,-119,-118,
+ -117,-116,-114,-113,-112,-110,-108,-107,
+ -105,-103,-102,-100, -98, -96, -94, -91,
+ -89, -87, -85, -82, -80, -78, -75, -73,
+ -70, -67, -65, -62, -59, -57, -54, -51,
+ -48, -45, -42, -39, -36, -33, -30, -27,
+ -24, -21, -18, -15, -12, -9, -6, -3,
+
+ 0, 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,-127,-126,-125,-124,-123,-122,-121,
+ -120,-119,-118,-117,-116,-115,-114,-113,
+ -112,-111,-110,-109,-108,-107,-106,-105,
+ -104,-103,-102,-101,-100, -99, -98, -97,
+ -96, -95, -94, -93, -92, -91, -90, -89,
+ -88, -87, -86, -85, -84, -83, -82, -81,
+ -80, -79, -78, -77, -76, -75, -74, -73,
+ -72, -71, -70, -69, -68, -67, -66, -65,
+ -64, -63, -62, -61, -60, -59, -58, -57,
+ -56, -55, -54, -53, -52, -51, -50, -49,
+ -48, -47, -46, -45, -44, -43, -42, -41,
+ -40, -39, -38, -37, -36, -35, -34, -33,
+ -32, -31, -30, -29, -28, -27, -26, -25,
+ -24, -23, -22, -21, -20, -19, -18, -17,
+ -16, -15, -14, -13, -12, -11, -10, -9,
+ -8, -7, -6, -5, -4, -3, -2, -1,
+
+ -120, 120,
+
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ -120,-120,-120,-120,-120,-120,-120,-120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 120, 120, 120, 120,
+
+ 41, 35, -66,-124, -31, 108, -42, -82,
+ 82,-112, 73, -15, -15, -69, -23, -21,
+ -77, -90, -37, 60,-121, 12, 62,-103,
+ 36, 94, 13, 28, 6, -73, 71, -34,
+ -77, 18, 77, -56, 67, -69,-117, -90,
+ 31, 3, 90, 125, 9, 56, 37, 31,
+ 93, -44, -53, -4,-106, -11, 69, 59,
+ 19, 13,-119, 10, 28, -37, -82, 50,
+ 32,-102, 80, -18, 64, 120, 54, -3,
+ 18, 73, 50, -10, -98, 125, 73, -36,
+ -83, 79, 20, -14, 68, 64, 102, -48,
+ 107, -60, 48, -73, 50, 59, -95, 34,
+ -10, 34,-111, -99, -31,-117, 31, -38,
+ -80, -54,-103, 2, -71, 114, -99, 73,
+ 44,-128, 126, -59,-103, -43, -23,-128,
+ -78, -22, -55, -52, 83, -65, 103, -42,
+ -65, 20, -42, 126, 45, -36,-114, 102,
+ -125, -17, 87, 73, 97, -1, 105,-113,
+ 97, -51, -47, 30, -99,-100, 22, 114,
+ 114, -26, 29, -16,-124, 79, 74, 119,
+ 2, -41, -24, 57, 44, 83, -53, -55,
+ 18, 30, 51, 116, -98, 12, -12, -43,
+ -44, -97, -44, -92, 89, 126, 53, -49,
+ 50, 34, -12, -52, -49, -45,-112, 45,
+ 72, -45,-113, 117, -26, -39, 29, 42,
+ -27, -64, -9, 43, 120,-127,-121, 68,
+ 14, 95, 80, 0, -44, 97,-115, -66,
+ 123, 5, 21, 7, 59, 51,-126, 31,
+ 24, 112,-110, -38, 100, 84, -50, -79,
+ -123, 62, 105, 21, -8, 70, 106, 4,
+ -106, 115, 14, -39, 22, 47, 103, 104,
+ -44, -9, 74, 74, -48, 87, 104, 118,
+ -6, 22, -69, 17, -83, -82, 36,-120,
+ 121, -2, 82, -37, 37, 67, -27, 60,
+ -12, 69, -45, -40, 40, -50, 11, -11,
+ -59, 96, 89, 61,-105, 39,-118, 89,
+ 118, 45, -48, -62, -55, -51, 104, -44,
+ 73, 106, 121, 37, 8, 97, 64, 20,
+ -79, 59, 106, -91, 17, 40, -63,-116,
+ -42, -87, 11,-121,-105,-116, 47, -15,
+ 21, 29,-102,-107, -63,-101, -31, -64,
+ 126, -23, -88,-102, -89,-122, -62, -75,
+ 84, -65,-102, -25, -39, 35, -47, 85,
+ -112, 56, 40, -47, -39, 108, -95, 102,
+ 94, 78, -31, 48,-100, -2, -39, 113,
+ -97, -30, -91, -30, 12,-101, -76, 71,
+ 101, 56, 42, 70,-119, -87,-126, 121,
+ 122, 118, 120, -62, 99, -79, 38, -33,
+ -38, 41, 109, 62, 98, -32,-106, 18,
+ 52, -65, 57, -90, 63,-119, 94, -15,
+ 109, 14, -29, 108, 40, -95, 30, 32,
+ 29, -53, -62, 3, 63, 65, 7,-124,
+ 15, 20, 5, 101, 27, 40, 97, -55,
+ -59, -25, 44,-114, 70, 54, 8, -36,
+ -13, -88,-115, -2, -66, -14, -21, 113,
+ -1, -96, -48, 59, 117, 6,-116, 126,
+ -121, 120, 115, 77, -48, -66,-126, -66,
+ -37, -62, 70, 65, 43,-116, -6, 48,
+ 127, 112, -16, -89, 84,-122, 50,-107,
+ -86, 91, 104, 19, 11, -26, -4, -11,
+ -54, -66, 125, -97,-119,-118, 65, 27,
+ -3, -72, 79, 104, -10, 114, 123, 20,
+ -103, -51, -45, 13, -16, 68, 58, -76,
+ -90, 102, 83, 51, 11, -53, -95, 16
+};
+
+
+static const uint16 spk_freq_table[12] = {
+ 36484, 34436, 32503, 30679, 28957, 27332,
+ 25798, 24350, 22983, 21693, 20476, 19326
+};
+
+static const uint16 pcjr_freq_table[12] = {
+ 65472, 61760, 58304, 55040, 52032, 49024,
+ 46272, 43648, 41216, 38912, 36736, 34624
+};
+#endif
+
+
+Player_V2::Player_V2(ScummEngine *scumm, bool pcjr) {
+ int i;
+
+ _isV3Game = (scumm->_version >= 3);
+ _vm = scumm;
+ _mixer = scumm->_mixer;
+ _sample_rate = _mixer->getOutputRate();
+
+ _header_len = (scumm->_features & GF_OLD_BUNDLE) ? 4 : 6;
+
+ // Initialize sound queue
+ _current_nr = _next_nr = 0;
+ _current_data = _next_data = 0;
+
+ // Initialize channel code
+ for (i = 0; i < 4; ++i)
+ clear_channel(i);
+
+ _next_tick = 0;
+ _tick_len = (_sample_rate << FIXP_SHIFT) / FREQ_HZ;
+
+ // Initialize V3 music timer
+ _music_timer_ctr = _music_timer = 0;
+ _ticks_per_music_timer = 65535;
+
+ // Initialize square generator
+ _level = 0;
+
+ _RNG = NG_PRESET;
+
+ set_pcjr(pcjr);
+ setMusicVolume(255);
+
+ _mixer->setupPremix(this);
+}
+
+Player_V2::~Player_V2() {
+ mutex_up();
+ // Detach the premix callback handler
+ _mixer->setupPremix(0);
+ mutex_down();
+}
+
+void Player_V2::set_pcjr(bool pcjr) {
+ mutex_up();
+ _pcjr = pcjr;
+
+ if (_pcjr) {
+ _decay = PCJR_DECAY;
+ _update_step = (_sample_rate << FIXP_SHIFT) / (111860 * 2);
+ _freqs_table = pcjr_freq_table;
+ } else {
+ _decay = SPK_DECAY;
+ _update_step = (_sample_rate << FIXP_SHIFT) / (1193000 * 2);
+ _freqs_table = spk_freq_table;
+ }
+
+ /* adapt _decay to sample rate. It must be squared when
+ * sample rate doubles.
+ */
+ int i;
+ for (i = 0; (_sample_rate << i) < 30000; i++)
+ _decay = _decay * _decay / 65536;
+
+
+ _timer_output = 0;
+ for (i = 0; i < 4; i++)
+ _timer_count[i] = 0;
+
+ mutex_down();
+}
+
+void Player_V2::setMusicVolume (int vol) {
+ if (vol > 255)
+ vol = 255;
+
+ /* scale to int16, FIXME: find best value */
+ double out = vol * 128 / 3;
+
+ /* build volume table (2dB per step) */
+ for (int i = 0; i < 15; i++) {
+ /* limit volume to avoid clipping */
+ if (out > 0xffff)
+ _volumetable[i] = 0xffff;
+ else
+ _volumetable[i] = (int) out;
+
+ out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
+ }
+ _volumetable[15] = 0;
+}
+
+void Player_V2::chainSound(int nr, byte *data) {
+ int offset = _header_len + (_pcjr ? 10 : 2);
+
+ _current_nr = nr;
+ _current_data = data;
+
+ for (int i = 0; i < 4; i++) {
+ clear_channel(i);
+
+ _channels[i].d.music_script_nr = nr;
+ if (data) {
+ _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i);
+ if (_channels[i].d.next_cmd)
+ _channels[i].d.time_left = 1;
+ }
+ }
+ _music_timer = 0;
+}
+
+void Player_V2::chainNextSound() {
+ if (_next_nr) {
+ chainSound(_next_nr, _next_data);
+ _next_nr = 0;
+ _next_data = 0;
+ }
+}
+
+void Player_V2::stopAllSounds() {
+ mutex_up();
+ for (int i = 0; i < 4; i++) {
+ clear_channel(i);
+ }
+ _next_nr = _current_nr = 0;
+ _next_data = _current_data = 0;
+ mutex_down();
+}
+
+void Player_V2::stopSound(int nr) {
+ mutex_up();
+ if (_next_nr == nr) {
+ _next_nr = 0;
+ _next_data = 0;
+ }
+ if (_current_nr == nr) {
+ for (int i = 0; i < 4; i++) {
+ clear_channel(i);
+ }
+ _current_nr = 0;
+ _current_data = 0;
+ chainNextSound();
+ }
+ mutex_down();
+}
+
+void Player_V2::startSound(int nr) {
+ byte *data = _vm->getResourceAddress(rtSound, nr);
+ assert(data);
+
+ mutex_up();
+
+ int cprio = _current_data ? *(_current_data + _header_len) : 0;
+ int prio = *(data + _header_len);
+ int nprio = _next_data ? *(_next_data + _header_len) : 0;
+
+ int restartable = *(data + _header_len + 1);
+
+ if (!_current_nr || cprio <= prio) {
+ int tnr = _current_nr;
+ int tprio = cprio;
+ byte *tdata = _current_data;
+
+ chainSound(nr, data);
+ nr = tnr;
+ prio = tprio;
+ data = tdata;
+ restartable = data ? *(data + _header_len + 1) : 0;
+ }
+
+ if (!_current_nr) {
+ nr = 0;
+ _next_nr = 0;
+ _next_data = 0;
+ }
+
+ if (nr != _current_nr
+ && restartable
+ && (!_next_nr
+ || nprio <= prio)) {
+
+ _next_nr = nr;
+ _next_data = data;
+ }
+
+ mutex_down();
+}
+
+int Player_V2::getSoundStatus(int nr) const {
+ return _current_nr == nr || _next_nr == nr;
+}
+
+
+void Player_V2::clear_channel(int i) {
+ ChannelInfo *channel = &_channels[i];
+ memset(channel, 0, sizeof(ChannelInfo));
+}
+
+int Player_V2::getMusicTimer() const {
+ if (_isV3Game)
+ return _music_timer;
+ else
+ return _channels[0].d.music_timer;
+}
+
+void Player_V2::execute_cmd(ChannelInfo *channel) {
+ uint16 value;
+ int16 offset;
+ uint8 *script_ptr;
+ ChannelInfo * current_channel;
+ ChannelInfo * dest_channel;
+
+ current_channel = channel;
+
+ if (channel->d.next_cmd == 0)
+ goto check_stopped;
+ script_ptr = &_current_data[channel->d.next_cmd];
+
+ for (;;) {
+ uint8 opcode = *script_ptr++;
+ if (opcode >= 0xf8) {
+ switch (opcode) {
+ case 0xf8: // set hull curve
+ debug(7, "channels[%d]: hull curve %2d",
+ channel - _channels, *script_ptr);
+ channel->d.hull_curve = hull_offsets[*script_ptr / 2];
+ script_ptr++;
+ break;
+
+ case 0xf9: // set freqmod curve
+ debug(7, "channels[%d]: freqmod curve %2d",
+ channel - _channels, *script_ptr);
+ channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4];
+ channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4];
+ script_ptr++;
+ break;
+
+ case 0xfd: // clear other channel
+ value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo);
+ debug(7, "clear channel %d", value);
+ script_ptr += 2;
+ // In Indy3, when traveling to Venice a command is
+ // issued to clear channel 4. So we introduce a 4th
+ // channel, which is never used. All OOB accesses are
+ // mapped to this channel.
+ //
+ // The original game had room for 8 channels, but only
+ // channels 0-3 are read, changes to other channels
+ // had no effect.
+ if (value >= ARRAYSIZE (_channels))
+ value = 4;
+ channel = &_channels[value];
+ // fall through
+
+ case 0xfa: // clear current channel
+ if (opcode == 0xfa)
+ debug(7, "clear channel");
+ channel->d.next_cmd = 0;
+ channel->d.base_freq = 0;
+ channel->d.freq_delta = 0;
+ channel->d.freq = 0;
+ channel->d.volume = 0;
+ channel->d.volume_delta = 0;
+ channel->d.inter_note_pause = 0;
+ channel->d.transpose = 0;
+ channel->d.hull_curve = 0;
+ channel->d.hull_offset = 0;
+ channel->d.hull_counter = 0;
+ channel->d.freqmod_table = 0;
+ channel->d.freqmod_offset = 0;
+ channel->d.freqmod_incr = 0;
+ channel->d.freqmod_multiplier = 0;
+ channel->d.freqmod_modulo = 0;
+ break;
+
+ case 0xfb: // ret from subroutine
+ debug(7, "ret from sub");
+ script_ptr = _retaddr;
+ break;
+
+ case 0xfc: // call subroutine
+ offset = READ_LE_UINT16 (script_ptr);
+ debug(7, "subroutine %d", offset);
+ script_ptr += 2;
+ _retaddr = script_ptr;
+ script_ptr = _current_data + offset;
+ break;
+
+ case 0xfe: // loop music
+ opcode = *script_ptr++;
+ offset = READ_LE_UINT16 (script_ptr);
+ script_ptr += 2;
+ debug(7, "loop if %d to %d", opcode, offset);
+ if (!channel->array[opcode / 2] || --channel->array[opcode/2])
+ script_ptr += offset;
+ break;
+
+ case 0xff: // set parameter
+ opcode = *script_ptr++;
+ value = READ_LE_UINT16 (script_ptr);
+ channel->array[opcode / 2] = value;
+ debug(7, "channels[%d]: set param %2d = %5d",
+ channel - &_channels[0], opcode, value);
+ script_ptr += 2;
+ if (opcode == 14) {
+ /* tempo var */
+ _ticks_per_music_timer = 125;
+ }
+ if (opcode == 0)
+ goto end;
+ break;
+ }
+ } else { // opcode < 0xf8
+ for (;;) {
+ int16 note, octave;
+ int is_last_note;
+ dest_channel = &_channels[(opcode >> 5) & 3];
+
+ if (!(opcode & 0x80)) {
+
+ int tempo = channel->d.tempo;
+ if (!tempo)
+ tempo = 1;
+ channel->d.time_left = tempo * note_lengths[opcode & 0x1f];
+
+ note = *script_ptr++;
+ is_last_note = note & 0x80;
+ note &= 0x7f;
+ if (note == 0x7f) {
+ debug(8, "channels[%d]: pause %d",
+ channel - _channels, channel->d.time_left);
+ goto end;
+ }
+ } else {
+
+ channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++;
+
+ if ((opcode & 0x10)) {
+ debug(8, "channels[%d]: pause %d",
+ channel - _channels, channel->d.time_left);
+ goto end;
+ }
+
+ is_last_note = 0;
+ note = (*script_ptr++) & 0x7f;
+ }
+
+ debug(8, "channels[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s",
+ dest_channel - channel, script_ptr ? script_ptr - _current_data - 2 : 0,
+ note, (signed short) dest_channel->d.transpose, channel->d.time_left,
+ dest_channel->d.hull_curve, dest_channel->d.freqmod_table,
+ dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier,
+ is_last_note ? "last":"");
+
+ uint16 myfreq;
+ dest_channel->d.time_left = channel->d.time_left;
+ dest_channel->d.note_length =
+ channel->d.time_left - dest_channel->d.inter_note_pause;
+ note += dest_channel->d.transpose;
+ while (note < 0)
+ note += 12;
+ octave = note / 12;
+ note = note % 12;
+ dest_channel->d.hull_offset = 0;
+ dest_channel->d.hull_counter = 1;
+ if (_pcjr && dest_channel == &_channels[3]) {
+ dest_channel->d.hull_curve = 196 + note * 12;
+ myfreq = 384 - 64 * octave;
+ } else {
+ myfreq = _freqs_table[note] >> octave;
+ }
+ dest_channel->d.freq = dest_channel->d.base_freq = myfreq;
+ if (is_last_note)
+ goto end;
+ opcode = *script_ptr++;
+ }
+ }
+ }
+
+end:
+ channel = current_channel;
+ if (channel->d.time_left) {
+ channel->d.next_cmd = script_ptr - _current_data;
+ return;
+ }
+
+ channel->d.next_cmd = 0;
+
+check_stopped:
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (_channels[i].d.time_left)
+ return;
+ }
+
+ _current_nr = 0;
+ _current_data = 0;
+ chainNextSound();
+ return;
+}
+
+void Player_V2::next_freqs(ChannelInfo *channel) {
+ channel->d.volume += channel->d.volume_delta;
+ channel->d.base_freq += channel->d.freq_delta;
+
+ channel->d.freqmod_offset += channel->d.freqmod_incr;
+ if (channel->d.freqmod_offset > channel->d.freqmod_modulo)
+ channel->d.freqmod_offset -= channel->d.freqmod_modulo;
+ channel->d.freq =
+ (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)])
+ * (int) channel->d.freqmod_multiplier / 256
+ + channel->d.base_freq;
+
+ debug(9, "Freq: %d/%d, %d/%d/%d*%d %d",
+ channel->d.base_freq, (int16)channel->d.freq_delta,
+ channel->d.freqmod_table, channel->d.freqmod_offset,
+ channel->d.freqmod_incr, channel->d.freqmod_multiplier,
+ channel->d.freq);
+
+ if (channel->d.note_length && !--channel->d.note_length) {
+ channel->d.hull_offset = 16;
+ channel->d.hull_counter = 1;
+ }
+
+ if (!--channel->d.time_left) {
+ execute_cmd(channel);
+ }
+
+#if 0
+ debug(9, "channels[%d]: freq %d hull %d/%d/%d",
+ channel - &_channels[0], channel->d.freq,
+ channel->d.hull_curve, channel->d.hull_offset,
+ channel->d.hull_counter);
+#endif
+
+ if (channel->d.hull_counter && !--channel->d.hull_counter) {
+ for (;;) {
+ const int16 *hull_ptr = hulls
+ + channel->d.hull_curve + channel->d.hull_offset / 2;
+ if (hull_ptr[1] == -1) {
+ channel->d.volume = hull_ptr[0];
+ if (hull_ptr[0] == 0)
+ channel->d.volume_delta = 0;
+ channel->d.hull_offset += 4;
+ } else {
+ channel->d.volume_delta = hull_ptr[0];
+ channel->d.hull_counter = hull_ptr[1];
+ channel->d.hull_offset += 4;
+ break;
+ }
+ }
+ }
+}
+
+void Player_V2::do_mix(int16 *data, uint len) {
+ mutex_up();
+ uint step;
+
+ do {
+ if (!(_next_tick >> FIXP_SHIFT)) {
+ _next_tick += _tick_len;
+ nextTick();
+ }
+
+ step = len;
+ if (step > (_next_tick >> FIXP_SHIFT))
+ step = (_next_tick >> FIXP_SHIFT);
+ if (_pcjr)
+ generatePCjrSamples(data, step);
+ else
+ generateSpkSamples(data, step);
+ data += 2 * step;
+ _next_tick -= step << FIXP_SHIFT;
+ } while (len -= step);
+
+ mutex_down();
+}
+
+void Player_V2::nextTick() {
+ for (int i = 0; i < 4; i++) {
+ if (!_channels[i].d.time_left)
+ continue;
+ next_freqs(&_channels[i]);
+ }
+ if (_music_timer_ctr++ >= _ticks_per_music_timer) {
+ _music_timer_ctr = 0;
+ _music_timer++;
+ }
+}
+
+void Player_V2::lowPassFilter(int16 *sample, uint len) {
+ for (uint i = 0; i < len; i++) {
+ _level = (int) (_level * _decay
+ + sample[0] * (0x10000 - _decay)) >> 16;
+ sample[0] = sample[1] = _level;
+ sample += 2;
+ }
+}
+
+void Player_V2::squareGenerator(int channel, int freq, int vol,
+ int noiseFeedback, int16 *sample, uint len) {
+ int period = _update_step * freq;
+ long nsample;
+ if (period == 0)
+ period = _update_step;
+
+ for (uint i = 0; i < len; i++) {
+ unsigned int duration = 0;
+
+ if (_timer_output & (1 << channel))
+ duration += _timer_count[channel];
+
+ _timer_count[channel] -= (1 << FIXP_SHIFT);
+ while (_timer_count[channel] <= 0) {
+
+ if (noiseFeedback) {
+ if (_RNG & 1) {
+ _RNG ^= noiseFeedback;
+ _timer_output ^= (1 << channel);
+ }
+ _RNG >>= 1;
+ } else {
+ _timer_output ^= (1 << channel);
+ }
+
+ if (_timer_output & (1 << channel))
+ duration += period;
+
+ _timer_count[channel] += period;
+ }
+
+ if (_timer_output & (1 << channel))
+ duration -= _timer_count[channel];
+
+ nsample = *sample +
+ (((signed long) (duration - (1 << (FIXP_SHIFT - 1)))
+ * (signed long) _volumetable[vol]) >> FIXP_SHIFT);
+ /* overflow: clip value */
+ if (nsample > 0x7fff)
+ nsample = 0x7fff;
+ if (nsample < -0x8000)
+ nsample = -0x8000;
+ *sample = nsample;
+ // The following write isn't necessary, because the lowPassFilter does it for us
+ //sample[1] = sample[0];
+ sample += 2;
+ }
+}
+
+void Player_V2::generateSpkSamples(int16 *data, uint len) {
+ int winning_channel = -1;
+ for (int i = 0; i < 4; i++) {
+ if (winning_channel == -1
+ && _channels[i].d.volume
+ && _channels[i].d.time_left) {
+ winning_channel = i;
+ }
+ }
+
+ memset(data, 0, 2 * sizeof(int16) * len);
+ if (winning_channel != -1) {
+ squareGenerator(0, _channels[winning_channel].d.freq, 0,
+ 0, data, len);
+ } else if (_level == 0)
+ /* shortcut: no sound is being played. */
+ return;
+
+ lowPassFilter(data, len);
+}
+
+void Player_V2::generatePCjrSamples(int16 *data, uint len) {
+ int i, j;
+ int freq, vol;
+
+ memset(data, 0, 2 * sizeof(int16) * len);
+ bool hasdata = false;
+
+ for (i = 1; i < 3; i++) {
+ freq = _channels[i].d.freq >> 6;
+ if (_channels[i].d.volume && _channels[i].d.time_left) {
+ for (j = 0; j < i; j++) {
+ if (_channels[j].d.volume
+ && _channels[j].d.time_left
+ && freq == (_channels[j].d.freq >> 6)) {
+ /* HACK: this channel is playing at
+ * the same frequency as another.
+ * Synchronize it to the same phase to
+ * prevent interference.
+ */
+ _timer_count[i] = _timer_count[j];
+ _timer_output ^= (1 << i) &
+ (_timer_output ^ _timer_output << (i - j));
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ freq = _channels[i].d.freq >> 6;
+ vol = (65535 - _channels[i].d.volume) >> 12;
+ if (!_channels[i].d.volume || !_channels[i].d.time_left) {
+ _timer_count[i] -= len << FIXP_SHIFT;
+ if (_timer_count[i] < 0)
+ _timer_count[i] = 0;
+ } else if (i < 3) {
+ hasdata = true;
+ squareGenerator(i, freq, vol, 0, data, len);
+ } else {
+ int noiseFB = (freq & 4) ? FB_WNOISE : FB_PNOISE;
+ int n = (freq & 3);
+
+ freq = (n == 3) ? 2 * (_channels[2].d.freq>>6) : 1 << (5 + n);
+ hasdata = true;
+ squareGenerator(i, freq, vol, noiseFB, data, len);
+ }
+#if 0
+ debug(9, "channel[%d]: freq %d %.1f ; volume %d",
+ i, freq, 111860.0 / freq, vol);
+#endif
+ }
+
+ if (_level || hasdata)
+ lowPassFilter(data, len);
+}
+
+void Player_V2::mutex_up() {
+ _mutex.lock();
+}
+
+void Player_V2::mutex_down() {
+ _mutex.unlock();
+}
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(PlayerV2)
+_GSETPTR(Scumm::note_lengths, GBVARS_NOTELENGTHS_INDEX, uint8, GBVARS_SCUMM)
+_GSETPTR(Scumm::hull_offsets, GBVARS_HULLOFFSETS_INDEX, uint16, GBVARS_SCUMM)
+_GSETPTR(Scumm::hulls, GBVARS_HULLS_INDEX, int16, GBVARS_SCUMM)
+_GSETPTR(Scumm::freqmod_lengths, GBVARS_FREQMODLENGTHS_INDEX, uint16, GBVARS_SCUMM)
+_GSETPTR(Scumm::freqmod_offsets, GBVARS_FREQMODOFFSETS_INDEX, uint16, GBVARS_SCUMM)
+_GSETPTR(Scumm::freqmod_table, GBVARS_FREQMODTABLE_INDEX, int8, GBVARS_SCUMM)
+_GSETPTR(Scumm::spk_freq_table, GBVARS_SPKFREQTABLE_INDEX, uint16, GBVARS_SCUMM)
+_GSETPTR(Scumm::pcjr_freq_table, GBVARS_PCJRFREQTABLE_INDEX, uint16, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(PlayerV2)
+_GRELEASEPTR(GBVARS_NOTELENGTHS_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_HULLOFFSETS_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_HULLS_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FREQMODLENGTHS_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FREQMODOFFSETS_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_FREQMODTABLE_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_SPKFREQTABLE_INDEX, GBVARS_SCUMM)
+_GRELEASEPTR(GBVARS_PCJRFREQTABLE_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/player_v2.h b/engines/scumm/player_v2.h
new file mode 100644
index 0000000000..160400e06a
--- /dev/null
+++ b/engines/scumm/player_v2.h
@@ -0,0 +1,167 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef PLAYER_V2_H
+#define PLAYER_V2_H
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "scumm/music.h"
+#include "sound/audiostream.h"
+
+namespace Audio {
+ class Mixer;
+}
+
+namespace Scumm {
+
+class ScummEngine;
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct channel_data {
+ uint16 time_left; // 00
+ uint16 next_cmd; // 02
+ uint16 base_freq; // 04
+ uint16 freq_delta; // 06
+ uint16 freq; // 08
+ uint16 volume; // 10
+ uint16 volume_delta; // 12
+ uint16 tempo; // 14
+ uint16 inter_note_pause; // 16
+ uint16 transpose; // 18
+ uint16 note_length; // 20
+ uint16 hull_curve; // 22
+ uint16 hull_offset; // 24
+ uint16 hull_counter; // 26
+ uint16 freqmod_table; // 28
+ uint16 freqmod_offset; // 30
+ uint16 freqmod_incr; // 32
+ uint16 freqmod_multiplier; // 34
+ uint16 freqmod_modulo; // 36
+ uint16 unknown[4]; // 38 - 44
+ uint16 music_timer; // 46
+ uint16 music_script_nr; // 48
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+
+/**
+ * Scumm V2 PC-Speaker MIDI driver.
+ * This simulates the pc speaker sound, which is driven by the 8253 (square
+ * wave generator) and a low-band filter.
+ */
+class Player_V2 : public AudioStream, public MusicEngine {
+public:
+ Player_V2(ScummEngine *scumm, bool pcjr);
+ virtual ~Player_V2();
+
+ virtual void setMusicVolume(int vol);
+ virtual void startSound(int sound);
+ virtual void stopSound(int sound);
+ virtual void stopAllSounds();
+ virtual int getMusicTimer() const;
+ virtual int getSoundStatus(int sound) const;
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples) {
+ do_mix(buffer, numSamples / 2);
+ return numSamples;
+ }
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _sample_rate; }
+
+protected:
+ bool _isV3Game;
+ Audio::Mixer *_mixer;
+ ScummEngine *_vm;
+
+ bool _pcjr;
+ int _header_len;
+
+ uint32 _sample_rate;
+ uint32 _next_tick;
+ uint32 _tick_len;
+ unsigned int _update_step;
+ unsigned int _decay;
+ int _level;
+ unsigned int _RNG;
+ unsigned int _volumetable[16];
+
+ int _timer_count[4];
+ int _timer_output;
+
+ int _current_nr;
+ byte *_current_data;
+ int _next_nr;
+ byte *_next_data;
+ byte *_retaddr;
+
+private:
+ union ChannelInfo {
+ channel_data d;
+ uint16 array[sizeof(channel_data)/2];
+ };
+
+ int _music_timer;
+ int _music_timer_ctr;
+ int _ticks_per_music_timer;
+
+ const uint16 *_freqs_table;
+
+ Common::Mutex _mutex;
+ ChannelInfo _channels[5];
+
+protected:
+ void mutex_up();
+ void mutex_down();
+
+ virtual void nextTick();
+ virtual void clear_channel(int i);
+ virtual void chainSound(int nr, byte *data);
+ virtual void chainNextSound();
+
+ virtual void generateSpkSamples(int16 *data, uint len);
+ virtual void generatePCjrSamples(int16 *data, uint len);
+
+ void lowPassFilter(int16 *data, uint len);
+ void squareGenerator(int channel, int freq, int vol,
+ int noiseFeedback, int16 *sample, uint len);
+
+private:
+ void do_mix(int16 *buf, uint len);
+
+ void set_pcjr(bool pcjr);
+ void execute_cmd(ChannelInfo *channel);
+ void next_freqs(ChannelInfo *channel);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_v2a.cpp b/engines/scumm/player_v2a.cpp
new file mode 100644
index 0000000000..36499ba527
--- /dev/null
+++ b/engines/scumm/player_v2a.cpp
@@ -0,0 +1,1420 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "base/engine.h"
+#include "scumm/player_v2a.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+#define BASE_FREQUENCY 3579545
+
+#ifdef PALMOS_68K
+static uint32 *CRCtable = NULL;
+#else
+static uint32 CRCtable[256];
+#endif
+
+static void InitCRC (void)
+{
+ const uint32 poly = 0xEDB88320;
+ int i, j;
+ uint32 n;
+
+ for (i = 0; i < 256; i++)
+ {
+ n = i;
+ for (j = 0; j < 8; j++)
+ n = (n & 1) ? ((n >> 1) ^ poly) : (n >> 1);
+ CRCtable[i] = n;
+ }
+}
+
+static uint32 GetCRC (byte *data, int len)
+{
+ uint32 CRC = 0xFFFFFFFF;
+ int i;
+ for (i = 0; i < len; i++)
+ CRC = (CRC >> 8) ^ CRCtable[(CRC ^ data[i]) & 0xFF];
+ return CRC ^ 0xFFFFFFFF;
+}
+
+class V2A_Sound {
+public:
+ V2A_Sound() : _id(0), _mod(NULL) { }
+ virtual ~V2A_Sound() {};
+ virtual void start(Player_MOD *mod, int id, const byte *data) = 0;
+ virtual bool update() = 0;
+ virtual void stop() = 0;
+protected:
+ int _id;
+ Player_MOD *_mod;
+};
+
+// unsupported sound effect, print warning message to console
+class V2A_Sound_Unsupported : public V2A_Sound {
+public:
+ V2A_Sound_Unsupported() { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ warning("player_v2a - sound %i not supported yet", id);
+ }
+ virtual bool update() { return false; }
+ virtual void stop() { }
+};
+
+// template, automatically stops all channels when a sound is silenced
+template<int numChan>
+class V2A_Sound_Base : public V2A_Sound {
+public:
+ V2A_Sound_Base() : _offset(0), _size(0), _data(0) { }
+ V2A_Sound_Base(uint16 offset, uint16 size) : _offset(offset), _size(size), _data(0) { }
+ virtual void stop() {
+ assert(_id);
+ for (int i = 0; i < numChan; i++)
+ _mod->stopChannel(_id | (i << 8));
+ _id = 0;
+ free(_data);
+ _data = 0;
+ }
+protected:
+ const uint16 _offset;
+ const uint16 _size;
+
+ char *_data;
+};
+
+// plays a music track
+class V2A_Sound_Music : public V2A_Sound {
+public:
+ V2A_Sound_Music(uint16 instoff, uint16 voloff, uint16 chan1off, uint16 chan2off, uint16 chan3off, uint16 chan4off, uint16 sampoff, bool looped) :
+ _instoff(instoff), _voloff(voloff), _chan1off(chan1off), _chan2off(chan2off), _chan3off(chan3off), _chan4off(chan4off), _sampoff(sampoff), _looped(looped) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+
+ _data = (char *)malloc(READ_LE_UINT16(data));
+ memcpy(_data, data, READ_LE_UINT16(data));
+
+ _chan[0].dataptr_i = _chan1off;
+ _chan[1].dataptr_i = _chan2off;
+ _chan[2].dataptr_i = _chan3off;
+ _chan[3].dataptr_i = _chan4off;
+ for (int i = 0; i < 4; i++) {
+ _chan[i].dataptr = _chan[i].dataptr_i;
+ _chan[i].volbase = 0;
+ _chan[i].volptr = 0;
+ _chan[i].chan = 0;
+ _chan[i].dur = 0;
+ _chan[i].ticks = 0;
+ }
+ update();
+ }
+ virtual bool update() {
+ assert(_id);
+ int i, j = 0;
+ for (i = 0; i < 4; i++) {
+ if (_chan[i].dur) {
+ if (!--_chan[i].dur) {
+ _mod->stopChannel(_id | (_chan[i].chan << 8));
+ } else {
+ _mod->setChannelVol(_id | (_chan[i].chan << 8),
+ READ_BE_UINT16(_data + _chan[i].volbase + (_chan[i].volptr++ << 1)));
+ if (_chan[i].volptr == 0) {
+ _mod->stopChannel(_id | (_chan[i].chan << 8));
+ _chan[i].dur = 0;
+ }
+ }
+ }
+ if (!_chan[i].dataptr) {
+ j++;
+ continue;
+ }
+ if (READ_BE_UINT16(_data + _chan[i].dataptr) <= _chan[i].ticks) {
+ if (READ_BE_UINT16(_data + _chan[i].dataptr + 2) == 0xFFFF) {
+ if (_looped) {
+ _chan[i].dataptr = _chan[i].dataptr_i;
+ _chan[i].ticks = 0;
+ if (READ_BE_UINT16(_data + _chan[i].dataptr) > 0) {
+ _chan[i].ticks++;
+ continue;
+ }
+ } else {
+ _chan[i].dataptr = 0;
+ j++;
+ continue;
+ }
+ }
+ int freq = BASE_FREQUENCY / READ_BE_UINT16(_data + _chan[i].dataptr + 2);
+ int inst = READ_BE_UINT16(_data + _chan[i].dataptr + 8);
+ _chan[i].volbase = _voloff + (READ_BE_UINT16(_data + _instoff + (inst << 5)) << 9);
+ _chan[i].volptr = 0;
+ _chan[i].chan = READ_BE_UINT16(_data + _chan[i].dataptr + 6) & 0x3;
+
+ if (_chan[i].dur) // if there's something playing, stop it
+ _mod->stopChannel(_id | (_chan[i].chan << 8));
+
+ _chan[i].dur = READ_BE_UINT16(_data + _chan[i].dataptr + 4);
+
+ int vol = READ_BE_UINT16(_data + _chan[i].volbase + (_chan[i].volptr++ << 1));
+
+ int pan;
+ if ((_chan[i].chan == 0) || (_chan[i].chan == 3))
+ pan = -127;
+ else pan = 127;
+ int offset = READ_BE_UINT16(_data + _instoff + (inst << 5) + 0x14);
+ int len = READ_BE_UINT16(_data + _instoff + (inst << 5) + 0x18);
+ int loopoffset = READ_BE_UINT16(_data + _instoff + (inst << 5) + 0x16);
+ int looplen = READ_BE_UINT16(_data + _instoff + (inst << 5) + 0x10);
+
+ int size = len + looplen;
+ char *data = (char *)malloc(size);
+ memcpy(data, _data + _sampoff + offset, len);
+ memcpy(data + len, _data + _sampoff + loopoffset, looplen);
+
+ _mod->startChannel(_id | (_chan[i].chan << 8), data, size, freq, vol, len, looplen + len, pan);
+ _chan[i].dataptr += 16;
+ }
+ _chan[i].ticks++;
+ }
+ if (j == 4)
+ return false;
+ return true;
+ }
+ virtual void stop() {
+ assert(_id);
+ for (int i = 0; i < 4; i++) {
+ if (_chan[i].dur)
+ _mod->stopChannel(_id | (_chan[i].chan << 8));
+ }
+ free(_data);
+ _id = 0;
+ }
+private:
+ const uint16 _instoff;
+ const uint16 _voloff;
+ const uint16 _chan1off;
+ const uint16 _chan2off;
+ const uint16 _chan3off;
+ const uint16 _chan4off;
+ const uint16 _sampoff;
+ const bool _looped;
+
+ char *_data;
+ struct tchan {
+ uint16 dataptr_i;
+ uint16 dataptr;
+ uint16 volbase;
+ uint8 volptr;
+ uint16 chan;
+ uint16 dur;
+ uint16 ticks;
+ } _chan[4];
+};
+
+// plays a single waveform
+class V2A_Sound_Single : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Single(uint16 offset, uint16 size, uint16 freq, uint8 vol) :
+ V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ int vol = (_vol << 2) | (_vol >> 4);
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, vol, 0, 0);
+ _ticks = 1 + (60 * _size * _freq) / BASE_FREQUENCY;
+ }
+ virtual bool update() {
+ assert(_id);
+ _ticks--;
+ if (!_ticks) {
+ return false;
+ }
+ return true;
+ }
+private:
+ const uint16 _freq;
+ const uint8 _vol;
+
+ int _ticks;
+};
+
+// plays a single looped waveform
+class V2A_Sound_SingleLooped : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_SingleLooped(uint16 offset, uint16 size, uint16 freq, uint8 vol, uint16 loopoffset, uint16 loopsize) :
+ V2A_Sound_Base<1>(offset, size), _loopoffset(loopoffset), _loopsize(loopsize), _freq(freq), _vol(vol) { }
+ V2A_Sound_SingleLooped(uint16 offset, uint16 size, uint16 freq, uint8 vol) :
+ V2A_Sound_Base<1>(offset, size), _loopoffset(0), _loopsize(size), _freq(freq), _vol(vol) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ int vol = (_vol << 2) | (_vol >> 4);
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, vol, _loopoffset, _loopoffset + _loopsize);
+ }
+ virtual bool update() {
+ assert(_id);
+ return true;
+ }
+private:
+ const uint16 _loopoffset;
+ const uint16 _loopsize;
+ const uint16 _freq;
+ const uint8 _vol;
+};
+
+// plays two looped waveforms
+class V2A_Sound_MultiLooped : public V2A_Sound_Base<2> {
+public:
+ V2A_Sound_MultiLooped(uint16 offset, uint16 size, uint16 freq1, uint8 vol1, uint16 freq2, uint8 vol2) :
+ V2A_Sound_Base<2>(offset, size), _freq1(freq1), _vol1(vol1), _freq2(freq2), _vol2(vol2) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data1 = (char *)malloc(_size);
+ char *tmp_data2 = (char *)malloc(_size);
+ memcpy(tmp_data1, data + _offset, _size);
+ memcpy(tmp_data2, data + _offset, _size);
+ int vol1 = (_vol1 << 1) | (_vol1 >> 5);
+ int vol2 = (_vol2 << 1) | (_vol2 >> 5);
+ _mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, vol1, 0, _size, -127);
+ _mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, vol2, 0, _size, 127);
+ }
+ virtual bool update() {
+ assert(_id);
+ return true;
+ }
+private:
+ const uint16 _freq1;
+ const uint8 _vol1;
+ const uint16 _freq2;
+ const uint8 _vol2;
+};
+
+// plays two looped waveforms for a fixed number of frames
+class V2A_Sound_MultiLoopedDuration : public V2A_Sound_MultiLooped {
+public:
+ V2A_Sound_MultiLoopedDuration(uint16 offset, uint16 size, uint16 freq1, uint8 vol1, uint16 freq2, uint8 vol2, uint16 numframes) :
+ V2A_Sound_MultiLooped(offset, size, freq1, vol1, freq2, vol2), _duration(numframes) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ V2A_Sound_MultiLooped::start(mod, id, data);
+ _ticks = 0;
+ }
+ virtual bool update() {
+ assert(_id);
+ _ticks++;
+ if (_ticks >= _duration)
+ return false;
+ return true;
+ }
+private:
+ const uint16 _duration;
+
+ int _ticks;
+};
+
+// plays a single looped waveform which starts at one frequency and bends to another frequency, where it remains until stopped
+class V2A_Sound_SingleLoopedPitchbend : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_SingleLoopedPitchbend(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint8 vol, uint8 step) :
+ V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2), _vol(vol), _step(step) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ int vol = (_vol << 2) | (_vol >> 4);
+ _curfreq = _freq1;
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, vol, 0, _size);
+ }
+ virtual bool update() {
+ assert(_id);
+ if (_freq1 < _freq2) {
+ _curfreq += _step;
+ if (_curfreq > _freq2)
+ _curfreq = _freq2;
+ else
+ _mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
+ } else {
+ _curfreq -= _step;
+ if (_curfreq < _freq2)
+ _curfreq = _freq2;
+ else
+ _mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
+ }
+ return true;
+ }
+private:
+ const uint16 _freq1;
+ const uint16 _freq2;
+ const uint8 _vol;
+ const uint16 _step;
+
+ uint16 _curfreq;
+};
+
+// plays a single looped waveform starting at a specific frequency/volume, dropping in frequency and fading volume to zero
+// used when Maniac Mansion explodes
+class V2A_Sound_Special_ManiacNuclear : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_ManiacNuclear(uint16 offset, uint16 size, uint16 freq, uint8 vol) :
+ V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ _curvol = (_vol << 3) | (_vol >> 3);
+ _curfreq = _freq;
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, _curvol >> 1, 0, _size);
+ }
+ virtual bool update() {
+ assert(_id);
+ _curfreq += 2;
+ _mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
+ _curvol--;
+ if (_curvol == 0)
+ return false;
+ _mod->setChannelVol(_id, _curvol >> 1);
+ return true;
+ }
+private:
+ const uint16 _freq;
+ const uint8 _vol;
+
+ uint16 _curfreq;
+ uint16 _curvol;
+};
+
+// plays a single looped waveform, fading the volume from zero to maximum at one rate, then back to zero at another rate
+// used when a microwave oven goes 'Ding'
+class V2A_Sound_Special_ManiacDing : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_ManiacDing(uint16 offset, uint16 size, uint16 freq, uint8 fadeinrate, uint8 fadeoutrate) :
+ V2A_Sound_Base<1>(offset, size), _freq(freq), _fade1(fadeinrate), _fade2(fadeoutrate) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ _curvol = 1;
+ _dir = 0;
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, _curvol, 0, _size);
+ }
+ virtual bool update() {
+ assert(_id);
+ if (_dir == 0) {
+ _curvol += _fade1;
+ if (_curvol > 0x3F) {
+ _curvol = 0x3F;
+ _dir = 1;
+ }
+ } else {
+ _curvol -= _fade2;
+ if (_curvol < 1)
+ return false;
+ }
+ _mod->setChannelVol(_id, (_curvol << 2) | (_curvol >> 4));
+ return true;
+ }
+private:
+ const uint16 _freq;
+ const uint16 _fade1;
+ const uint16 _fade2;
+
+ int _curvol;
+ int _dir;
+};
+
+// plays two looped waveforms, fading the volume from zero to maximum at one rate, then back to zero at another rate
+// used in Zak McKracken for several stereo 'Ding' sounds
+class V2A_Sound_Special_ZakStereoDing : public V2A_Sound_Base<2> {
+public:
+ V2A_Sound_Special_ZakStereoDing(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint8 fadeinrate, uint8 fadeoutrate) :
+ V2A_Sound_Base<2>(offset, size), _freq1(freq1), _freq2(freq2), _fade1(fadeinrate), _fade2(fadeoutrate) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data1 = (char *)malloc(_size);
+ char *tmp_data2 = (char *)malloc(_size);
+ memcpy(tmp_data1, data + _offset, _size);
+ memcpy(tmp_data2, data + _offset, _size);
+ _curvol = 1;
+ _dir = 0;
+ _mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, 1, 0, _size, -127);
+ _mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, 1, 0, _size, 127);
+ }
+ virtual bool update() {
+ assert(_id);
+ if (_dir == 0) {
+ _curvol += _fade1;
+ if (_curvol > 0x3F) {
+ _curvol = 0x3F;
+ _dir = 1;
+ }
+ } else {
+ _curvol -= _fade2;
+ if (_curvol < 1)
+ return false;
+ }
+ _mod->setChannelVol(_id | 0x000, (_curvol << 1) | (_curvol >> 5));
+ _mod->setChannelVol(_id | 0x100, (_curvol << 1) | (_curvol >> 5));
+ return true;
+ }
+private:
+ const uint16 _freq1;
+ const uint16 _freq2;
+ const uint16 _fade1;
+ const uint16 _fade2;
+
+ int _curvol;
+ int _dir;
+};
+
+// plays a single looped waveform, starting at one frequency and at full volume, bending down to another frequency, and then fading volume to zero
+// used in Maniac Mansion for the tentacle sounds
+class V2A_Sound_Special_ManiacTentacle : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_ManiacTentacle(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint16 step) :
+ V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2), _step(step) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ _curfreq = _freq1;
+ _curvol = 0x3F;
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, (_curvol << 2) | (_curvol >> 4), 0, _size);
+ }
+ virtual bool update() {
+ assert(_id);
+ if (_curfreq > _freq2)
+ _curvol = 0x3F + _freq2 - _curfreq;
+ if (_curvol < 1)
+ return false;
+ _curfreq += _step;
+ _mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
+ _mod->setChannelVol(_id, (_curvol << 2) | (_curvol >> 4));
+ return true;
+ }
+private:
+ const uint16 _freq1;
+ const uint16 _freq2;
+ const uint16 _step;
+
+ uint16 _curfreq;
+ int _curvol;
+};
+
+// plays a single looped waveform, starting at one frequency, bending down to another frequency, and then back up to the original frequency
+// used for electronic noises
+class V2A_Sound_Special_ManiacElectric : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_ManiacElectric(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint16 step, uint8 vol) :
+ V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2), _step(step), _vol(vol) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ int vol = (_vol << 2) | (_vol >> 4);
+ _curfreq = _freq1;
+ _dir = 2;
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, vol, 0, _size);
+ }
+ virtual bool update() {
+ assert(_id);
+ if (_dir == 2) {
+ _curfreq += _step;
+ if (_curfreq > _freq2) {
+ _curfreq = _freq2;
+ _dir = 1;
+ }
+ _mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
+ } else if (_dir == 1) {
+ _curfreq -= _step;
+ if (_curfreq < _freq1) {
+ _curfreq = _freq1;
+ _dir = 0;
+ }
+ _mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
+ }
+ return true;
+ }
+private:
+ const uint16 _freq1;
+ const uint16 _freq2;
+ const uint16 _step;
+ const uint8 _vol;
+
+ uint16 _curfreq;
+ int _dir;
+};
+
+// plays a single looped waveform, simultaneously bending the frequency downward and slowly fading volume to zero
+// don't remember where this one is used
+// old name: SlowPitchbendDownAndFadeout
+class V2A_Sound_Special_Maniac61 : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_Maniac61(uint16 offset, uint16 size, uint16 freq1, uint16 freq2) :
+ V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ _curfreq = _freq1;
+ _curvol = 0x3F;
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, (_curvol << 2) | (_curvol >> 4), 0, _size);
+ }
+ virtual bool update() {
+ assert(_id);
+ _curfreq++;
+ if (!(_curfreq & 3))
+ _curvol--;
+ if ((_curfreq == _freq2) || (_curvol == 0))
+ return false;
+ _mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
+ _mod->setChannelVol(_id, (_curvol << 2) | (_curvol >> 4));
+ return true;
+ }
+private:
+ const uint16 _freq1;
+ const uint16 _freq2;
+
+ uint16 _curfreq;
+ uint8 _curvol;
+};
+
+// intermittently plays two looped waveforms for a specific duration
+// used for ringing telephones
+class V2A_Sound_Special_ManiacPhone : public V2A_Sound_Base<2> {
+public:
+ V2A_Sound_Special_ManiacPhone(uint16 offset, uint16 size, uint16 freq1, uint8 vol1, uint16 freq2, uint8 vol2, uint16 numframes, uint8 playwidth, uint8 loopwidth) :
+ V2A_Sound_Base<2>(offset, size), _freq1(freq1), _vol1(vol1), _freq2(freq2), _vol2(vol2), _duration(numframes), _playwidth(playwidth), _loopwidth(loopwidth) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ _data = (char *)malloc(READ_LE_UINT16(data));
+ memcpy(_data, data, READ_LE_UINT16(data));
+ soundon();
+ _ticks = 0;
+ _loop = 0;
+ }
+ virtual bool update() {
+ assert(_id);
+ if (_loop == _playwidth) {
+ _mod->stopChannel(_id | 0x000);
+ _mod->stopChannel(_id | 0x100);
+ }
+ if (_loop == _loopwidth) {
+ _loop = 0;
+ soundon();
+ }
+ _loop++;
+ _ticks++;
+ if (_ticks >= _duration)
+ return false;
+ return true;
+ }
+private:
+ const uint16 _freq1;
+ const uint8 _vol1;
+ const uint16 _freq2;
+ const uint8 _vol2;
+ const uint16 _duration;
+ const uint8 _playwidth;
+ const uint8 _loopwidth;
+
+ int _ticks;
+ int _loop;
+
+ void soundon() {
+ char *tmp_data1 = (char *)malloc(_size);
+ char *tmp_data2 = (char *)malloc(_size);
+ memcpy(tmp_data1, _data + _offset, _size);
+ memcpy(tmp_data2, _data + _offset, _size);
+ int vol1 = (_vol1 << 1) | (_vol1 >> 5);
+ int vol2 = (_vol2 << 1) | (_vol2 >> 5);
+ _mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, vol1, 0, _size, -127);
+ _mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, vol2, 0, _size, 127);
+ }
+};
+
+// intermittently plays a single waveform for a specified duration
+// used when applying a wrench to a pipe
+class V2A_Sound_Special_ManiacWrench : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_ManiacWrench(uint16 offset, uint16 size, uint16 freq, uint8 vol, uint8 loopwidth, uint8 numloops) :
+ V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol), _loopwidth(loopwidth), _numloops(numloops) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ _data = (char *)malloc(READ_LE_UINT16(data));
+ memcpy(_data, data, READ_LE_UINT16(data));
+ soundon();
+ _loop = 0;
+ _loopctr = 0;
+ }
+ virtual bool update() {
+ assert(_id);
+ _loop++;
+ if (_loop == _loopwidth) {
+ _loop = 0;
+ _loopctr++;
+ if (_loopctr == _numloops)
+ return false;
+ _mod->stopChannel(_id);
+ soundon();
+ }
+ return true;
+ }
+private:
+ const uint16 _freq;
+ const uint8 _vol;
+ const uint8 _loopwidth;
+ const uint8 _numloops;
+
+ int _loop;
+ int _loopctr;
+
+ void soundon() {
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, _data + _offset, _size);
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, (_vol << 2) | (_vol >> 4), 0, 0);
+ }
+};
+
+// plays a single waveform at irregular intervals for a specified number of frames, possibly looped
+// used for typewriter noises, as well as tapping on the bus in Zak McKracken
+class V2A_Sound_Special_ManiacTypewriter : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_ManiacTypewriter(uint16 offset, uint16 size, uint16 freq, uint8 vol, uint8 numdurs, const uint8 *durations, bool looped) :
+ V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol), _numdurs(numdurs), _durations(durations), _looped(looped) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ _data = (char *)malloc(READ_LE_UINT16(data));
+ memcpy(_data, data, READ_LE_UINT16(data));
+ soundon();
+ _curdur = 0;
+ _ticks = _durations[_curdur++];
+ }
+ virtual bool update() {
+ assert(_id);
+ _ticks--;
+ if (!_ticks) {
+ if (_curdur == _numdurs) {
+ if (_looped)
+ _curdur = 0;
+ else
+ return false;
+ }
+ _mod->stopChannel(_id);
+ soundon();
+ _ticks = _durations[_curdur++];
+ }
+ return true;
+ }
+private:
+ const uint16 _freq;
+ const uint8 _vol;
+ const uint8 _numdurs;
+ const uint8 *_durations;
+ const bool _looped;
+
+ int _ticks;
+ int _curdur;
+
+ void soundon() {
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, _data + _offset, _size);
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, (_vol << 2) | (_vol >> 4), 0, 0);
+ }
+};
+
+// plays two looped waveforms pitch bending up at various predefined rates
+// used for some sort of siren-like noise in Maniac Mansion
+// old name: TwinSirenMulti
+class V2A_Sound_Special_Maniac44 : public V2A_Sound_Base<2> {
+public:
+ V2A_Sound_Special_Maniac44(uint16 offset1, uint16 size1, uint16 offset2, uint16 size2, uint16 freq1, uint16 freq2, uint8 vol) :
+ _offset1(offset1), _size1(size1), _offset2(offset2), _size2(size2), _freq1(freq1), _freq2(freq2), _vol(vol) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ _data = (char *)malloc(READ_LE_UINT16(data));
+ memcpy(_data, data, READ_LE_UINT16(data));
+
+ _loopnum = 1;
+ _step = 2;
+ _curfreq = _freq1;
+
+ soundon(_data + _offset1, _size1);
+ }
+ virtual bool update() {
+ assert(_id);
+ _mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / _curfreq);
+ _mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / (_curfreq + 3));
+ _curfreq -= _step;
+ if (_loopnum == 7) {
+ if ((BASE_FREQUENCY / _curfreq) >= 65536)
+ return false;
+ else
+ return true;
+ }
+ if (_curfreq >= _freq2)
+ return true;
+ const char steps[8] = {0, 2, 2, 3, 4, 8, 15, 2};
+ _curfreq = _freq1;
+ _step = steps[++_loopnum];
+ if (_loopnum == 7) {
+ _mod->stopChannel(_id | 0x000);
+ _mod->stopChannel(_id | 0x100);
+ soundon(_data + _offset2, _size2);
+ }
+ return true;
+ }
+private:
+ const uint16 _offset1;
+ const uint16 _size1;
+ const uint16 _offset2;
+ const uint16 _size2;
+ const uint16 _freq1;
+ const uint16 _freq2;
+ const uint8 _vol;
+
+ int _curfreq;
+ uint16 _loopnum;
+ uint16 _step;
+
+ void soundon(const char *data, int size) {
+ char *tmp_data1 = (char *)malloc(size);
+ char *tmp_data2 = (char *)malloc(size);
+ memcpy(tmp_data1, data, size);
+ memcpy(tmp_data2, data, size);
+ int vol = (_vol << 1) | (_vol >> 5);
+ _mod->startChannel(_id | 0x000, tmp_data1, size, BASE_FREQUENCY / _curfreq, vol, 0, size, -127);
+ _mod->startChannel(_id | 0x100, tmp_data2, size, BASE_FREQUENCY / (_curfreq + 3), vol, 0, size, 127);
+ }
+};
+
+// plays 4 looped waveforms, each at modulating frequencies
+// used for the siren noise in Maniac Mansion
+class V2A_Sound_Special_ManiacSiren : public V2A_Sound_Base<4> {
+public:
+ V2A_Sound_Special_ManiacSiren(uint16 offset1, uint16 size1, uint16 offset2, uint16 size2, uint8 vol) :
+ _offset1(offset1), _size1(size1), _offset2(offset2), _size2(size2), _vol(vol) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+
+ _freq1 = 0x02D0;
+ _step1 = -0x000A;
+ _freq2 = 0x0122;
+ _step2 = 0x000A;
+ _freq3 = 0x02BC;
+ _step3 = -0x0005;
+ _freq4 = 0x010E;
+ _step4 = 0x0007;
+
+ char *tmp_data1 = (char *)malloc(_size1);
+ char *tmp_data2 = (char *)malloc(_size2);
+ char *tmp_data3 = (char *)malloc(_size1);
+ char *tmp_data4 = (char *)malloc(_size2);
+ memcpy(tmp_data1, data + _offset1, _size1);
+ memcpy(tmp_data2, data + _offset2, _size2);
+ memcpy(tmp_data3, data + _offset1, _size1);
+ memcpy(tmp_data4, data + _offset2, _size2);
+ _mod->startChannel(_id | 0x000, tmp_data1, _size1, BASE_FREQUENCY / _freq1, _vol, 0, _size1, -127);
+ _mod->startChannel(_id | 0x100, tmp_data2, _size2, BASE_FREQUENCY / _freq2, _vol, 0, _size2, 127);
+ _mod->startChannel(_id | 0x200, tmp_data3, _size1, BASE_FREQUENCY / _freq3, _vol, 0, _size1, 127);
+ _mod->startChannel(_id | 0x300, tmp_data4, _size2, BASE_FREQUENCY / _freq4, _vol, 0, _size2, -127);
+ }
+ virtual bool update() {
+ assert(_id);
+ updatefreq(_freq1, _step1, 0x00AA, 0x00FA);
+ updatefreq(_freq2, _step2, 0x019A, 0x03B6);
+ updatefreq(_freq3, _step3, 0x00AA, 0x00FA);
+ updatefreq(_freq4, _step4, 0x019A, 0x03B6);
+ _mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / _freq1);
+ _mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / _freq2);
+ _mod->setChannelFreq(_id | 0x200, BASE_FREQUENCY / _freq3);
+ _mod->setChannelFreq(_id | 0x300, BASE_FREQUENCY / _freq4);
+ return true;
+ }
+private:
+ const uint16 _offset1;
+ const uint16 _size1;
+ const uint16 _offset2;
+ const uint16 _size2;
+ const uint8 _vol;
+
+ uint16 _freq1;
+ int16 _step1;
+ uint16 _freq2;
+ int16 _step2;
+ uint16 _freq3;
+ int16 _step3;
+ uint16 _freq4;
+ int16 _step4;
+
+ void updatefreq (uint16 &freq, int16 &step, uint16 min, uint16 max) {
+ freq += step;
+ if (freq <= min) {
+ freq = min;
+ step = -step;
+ }
+ if (freq >= max) {
+ freq = max;
+ step = -step;
+ }
+ }
+};
+
+// plays 4 looped waveforms
+// some sort of laserbeam-like sound effect in Zak
+// old name: QuadFreqLooped
+class V2A_Sound_Special_Zak70 : public V2A_Sound_Base<4> {
+public:
+ V2A_Sound_Special_Zak70(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint16 freq3, uint16 freq4, uint8 vol) :
+ V2A_Sound_Base<4>(offset, size), _freq1(freq1), _freq2(freq2), _freq3(freq3), _freq4(freq4), _vol(vol) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+
+ char *tmp_data1 = (char *)malloc(_size);
+ char *tmp_data2 = (char *)malloc(_size);
+ char *tmp_data3 = (char *)malloc(_size);
+ char *tmp_data4 = (char *)malloc(_size);
+ memcpy(tmp_data1, data + _offset, _size);
+ memcpy(tmp_data2, data + _offset, _size);
+ memcpy(tmp_data3, data + _offset, _size);
+ memcpy(tmp_data4, data + _offset, _size);
+ _mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, _vol, 0, _size, -127);
+ _mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, _vol, 0, _size, 127);
+ _mod->startChannel(_id | 0x200, tmp_data3, _size, BASE_FREQUENCY / _freq3, _vol, 0, _size, 127);
+ _mod->startChannel(_id | 0x300, tmp_data4, _size, BASE_FREQUENCY / _freq4, _vol, 0, _size, -127);
+ }
+ virtual bool update() {
+ assert(_id);
+ return true;
+ }
+protected:
+ const uint16 _freq1;
+ const uint16 _freq2;
+ const uint16 _freq3;
+ const uint16 _freq4;
+ const uint8 _vol;
+};
+
+// plays 4 looped waveforms and fades volume to zero after a specific delay
+// some whooshing-type sound in Zak
+// old name: QuadFreqFadeout
+class V2A_Sound_Special_Zak101 : public V2A_Sound_Special_Zak70 {
+public:
+ V2A_Sound_Special_Zak101(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint16 freq3, uint16 freq4, uint8 vol, uint16 dur) :
+ V2A_Sound_Special_Zak70(offset, size, freq1, freq2, freq3, freq4, vol), _dur(dur) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ V2A_Sound_Special_Zak70::start(mod, id, data);
+ _ticks = _dur;
+ }
+ virtual bool update() {
+ assert(_id);
+ if (!--_ticks)
+ return false;
+ if (_ticks < _vol) {
+ _mod->setChannelVol(_id | 0x000, _ticks);
+ _mod->setChannelVol(_id | 0x100, _ticks);
+ _mod->setChannelVol(_id | 0x200, _ticks);
+ _mod->setChannelVol(_id | 0x300, _ticks);
+ }
+ return true;
+ }
+private:
+ const uint16 _dur;
+
+ int _ticks;
+};
+
+// plays a single looped waveform and slowly fades volume to zero
+// another whooshing-type noise in Zak
+// old name: SingleFadeout
+class V2A_Sound_Special_Zak37 : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_Zak37(uint16 offset, uint16 size, uint16 freq, uint8 vol) :
+ V2A_Sound_Base<1>(offset, size), _freq(freq), _vol(vol) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ _curvol = _vol << 2;
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _freq, _curvol, 0, _size);
+ }
+ virtual bool update() {
+ assert(_id);
+ if (!--_curvol)
+ return false;
+ _mod->setChannelVol(_id, _curvol);
+ return true;
+ }
+private:
+ const uint16 _freq;
+ const uint8 _vol;
+
+ int _curvol;
+};
+
+// plays a single looped waveform, slowly bending from one frequency to another and then slowly fading volume from max to zero
+// used in Zak for airplane taking off and landing
+class V2A_Sound_Special_ZakAirplane : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_ZakAirplane(uint16 offset, uint16 size, uint16 freq1, uint16 freq2) :
+ V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ _curfreq = _freq1;
+ _curvol = 0x3F;
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, (_curvol << 2) | (_curvol >> 4), 0, _size);
+ _ticks = 0;
+ }
+ virtual bool update() {
+ assert(_id);
+ _ticks++;
+ if (_ticks < 4)
+ return true;
+ _ticks = 0;
+ if (_curfreq == _freq2) {
+ _curvol--;
+ if (_curvol == 0)
+ return false;
+ _mod->setChannelVol(_id, (_curvol << 2) | (_curvol >> 4));
+ }
+ else {
+ if (_freq1 < _freq2)
+ _curfreq++;
+ else
+ _curfreq--;
+ _mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
+ }
+ return true;
+ }
+private:
+ const uint16 _freq1;
+ const uint16 _freq2;
+
+ uint16 _curfreq;
+ int _curvol;
+ int _ticks;
+};
+
+// plays 4 looped waveforms, starting at specific frequencies and bending at different rates while fading volume to zero
+// used for some weird sound effect
+class V2A_Sound_Special_Zak71 : public V2A_Sound_Base<4> {
+public:
+ V2A_Sound_Special_Zak71(uint16 offset, uint16 size) :
+ _offset(offset), _size(size) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+
+ _freq1 = 0x00C8;
+ _freq2 = 0x0190;
+ _freq3 = 0x0320;
+ _freq4 = 0x0640;
+ _vol = 0x78;
+
+ char *tmp_data1 = (char *)malloc(_size);
+ char *tmp_data2 = (char *)malloc(_size);
+ char *tmp_data3 = (char *)malloc(_size);
+ char *tmp_data4 = (char *)malloc(_size);
+ memcpy(tmp_data1, data + _offset, _size);
+ memcpy(tmp_data2, data + _offset, _size);
+ memcpy(tmp_data3, data + _offset, _size);
+ memcpy(tmp_data4, data + _offset, _size);
+ _mod->startChannel(_id | 0x000, tmp_data1, _size, BASE_FREQUENCY / _freq1, MIN((_vol >> 1) + 3,0x32), 0, _size, -127);
+ _mod->startChannel(_id | 0x100, tmp_data2, _size, BASE_FREQUENCY / _freq2, MIN((_vol >> 1) + 3,0x32), 0, _size, 127);
+ _mod->startChannel(_id | 0x200, tmp_data3, _size, BASE_FREQUENCY / _freq3, MIN((_vol >> 1) + 3,0x32), 0, _size, 127);
+ _mod->startChannel(_id | 0x300, tmp_data4, _size, BASE_FREQUENCY / _freq4, MIN((_vol >> 1) + 3,0x32), 0, _size, -127);
+ }
+ virtual bool update() {
+ assert(_id);
+ _freq1 += 0x14;
+ _freq2 += 0x1E;
+ _freq3 += 0x32;
+ _freq4 += 0x50;
+ _mod->setChannelFreq(_id | 0x000, BASE_FREQUENCY / _freq1);
+ _mod->setChannelFreq(_id | 0x100, BASE_FREQUENCY / _freq2);
+ _mod->setChannelFreq(_id | 0x200, BASE_FREQUENCY / _freq3);
+ _mod->setChannelFreq(_id | 0x300, BASE_FREQUENCY / _freq4);
+ _vol--;
+ if (_vol == 0)
+ return false;
+ _mod->setChannelVol(_id | 0x000, MIN((_vol >> 1) + 3,0x32));
+ _mod->setChannelVol(_id | 0x100, MIN((_vol >> 1) + 3,0x32));
+ _mod->setChannelVol(_id | 0x200, MIN((_vol >> 1) + 3,0x32));
+ _mod->setChannelVol(_id | 0x300, MIN((_vol >> 1) + 3,0x32));
+ return true;
+ }
+private:
+ const uint16 _offset;
+ const uint16 _size;
+
+ uint16 _freq1;
+ uint16 _freq2;
+ uint16 _freq3;
+ uint16 _freq4;
+ uint8 _vol;
+};
+
+// plays a single looped waveform, bending the frequency upward at a varying rate
+// used in Zak for the tram on Mars (?)
+class V2A_Sound_Special_ZakTram : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_ZakTram(uint16 offset, uint16 size, uint16 freq1, uint16 freq2, uint8 vol) :
+ V2A_Sound_Base<1>(offset, size), _freq1(freq1), _freq2(freq2), _vol(vol) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ char *tmp_data = (char *)malloc(_size);
+ memcpy(tmp_data, data + _offset, _size);
+ _curfreq = _freq1;
+ _mod->startChannel(_id, tmp_data, _size, BASE_FREQUENCY / _curfreq, (_vol << 2) | (_vol >> 4), 0, _size);
+ _bendrate = 8;
+ _bendctr = 100;
+ _holdctr = 30;
+ }
+ virtual bool update() {
+ assert(_id);
+ if (_curfreq >= _freq2)
+ {
+ _mod->setChannelFreq(_id, BASE_FREQUENCY / _curfreq);
+ _curfreq -= _bendrate;
+ if (--_bendctr)
+ return true;
+ _bendrate--;
+ if (_bendrate < 2)
+ _bendrate = 2;
+ }
+ else
+ {
+ if (!--_holdctr)
+ return false;
+ }
+ return true;
+ }
+private:
+ const uint16 _freq1;
+ const uint16 _freq2;
+ const uint16 _vol;
+
+ uint16 _curfreq;
+ uint16 _bendrate;
+ uint16 _bendctr;
+ uint16 _holdctr;
+};
+
+// plays one waveform, then switches to a different looped waveform and slowly fades volume to zero
+// used for some odd sound effect
+class V2A_Sound_Special_Zak54 : public V2A_Sound_Base<1> {
+public:
+ V2A_Sound_Special_Zak54(uint16 offset1, uint16 size1, uint16 offset2, uint16 size2, uint16 freq) :
+ _offset1(offset1), _size1(size1), _offset2(offset2), _size2(size2), _freq(freq) { }
+ virtual void start(Player_MOD *mod, int id, const byte *data) {
+ _mod = mod;
+ _id = id;
+ _data = (char *)malloc(READ_LE_UINT16(data));
+ memcpy(_data, data, READ_LE_UINT16(data));
+ char *tmp_data = (char *)malloc(_size1);
+ memcpy(tmp_data, data + _offset1, _size1);
+ _vol = 0xFC;
+ _mod->startChannel(_id, tmp_data, _size1, BASE_FREQUENCY / _freq, _vol, 0, _size1);
+ _loop = _size1 * _freq * 60 / BASE_FREQUENCY;
+ }
+ virtual bool update() {
+ assert(_id);
+ if (!_loop)
+ {
+ _vol--;
+ if (_vol)
+ _mod->setChannelVol(_id, _vol);
+ else return false;
+ }
+ else if (!--_loop)
+ {
+ _mod->stopChannel(_id);
+ char *tmp_data = (char *)malloc(_size2);
+ memcpy(tmp_data, _data + _offset2, _size2);
+ _mod->startChannel(_id, tmp_data, _size2, BASE_FREQUENCY / _freq, _vol, 0, _size2);
+ }
+ return true;
+ }
+
+private:
+ const uint16 _offset1;
+ const uint16 _offset2;
+ const uint16 _size1;
+ const uint16 _size2;
+ const uint16 _freq;
+
+ int _vol;
+ int _loop;
+};
+
+#define CRCToSound(CRC, SOUND) \
+ if (crc == CRC) \
+ return new SOUND
+
+static V2A_Sound *findSound (unsigned long crc) {
+ CRCToSound(0x8FAB08C4, V2A_Sound_SingleLooped(0x006C,0x2B58,0x016E,0x3F)); // Maniac 17
+ CRCToSound(0xB673160A, V2A_Sound_SingleLooped(0x006C,0x1E78,0x01C2,0x1E)); // Maniac 38
+ CRCToSound(0x4DB1D0B2, V2A_Sound_MultiLooped(0x0072,0x1BC8,0x023D,0x3F,0x0224,0x3F)); // Maniac 20
+ CRCToSound(0x754D75EF, V2A_Sound_Single(0x0076,0x0738,0x01FC,0x3F)); // Maniac 10
+ CRCToSound(0x6E3454AF, V2A_Sound_Single(0x0076,0x050A,0x017C,0x3F)); // Maniac 12
+ CRCToSound(0x92F0BBB6, V2A_Sound_Single(0x0076,0x3288,0x012E,0x3F)); // Maniac 41
+ CRCToSound(0xE1B13982, V2A_Sound_MultiLoopedDuration(0x0078,0x0040,0x007C,0x3F,0x007B,0x3F,0x001E)); // Maniac 21
+ CRCToSound(0x288B16CF, V2A_Sound_MultiLoopedDuration(0x007A,0x0040,0x007C,0x3F,0x007B,0x3F,0x000A)); // Maniac 11
+ CRCToSound(0xA7565268, V2A_Sound_MultiLoopedDuration(0x007A,0x0040,0x00F8,0x3F,0x00F7,0x3F,0x000A)); // Maniac 19
+ CRCToSound(0x7D419BFC, V2A_Sound_MultiLoopedDuration(0x007E,0x0040,0x012C,0x3F,0x0149,0x3F,0x001E)); // Maniac 22
+ CRCToSound(0x1B52280C, V2A_Sound_Single(0x0098,0x0A58,0x007F,0x32)); // Maniac 6
+ CRCToSound(0x38D4A810, V2A_Sound_Single(0x0098,0x2F3C,0x0258,0x32)); // Maniac 7
+ CRCToSound(0x09F98FC2, V2A_Sound_Single(0x0098,0x0A56,0x012C,0x32)); // Maniac 16
+ CRCToSound(0x90440A65, V2A_Sound_Single(0x0098,0x0208,0x0078,0x28)); // Maniac 28
+ CRCToSound(0x985C76EF, V2A_Sound_Single(0x0098,0x0D6E,0x00C8,0x32)); // Maniac 30
+ CRCToSound(0x76156137, V2A_Sound_Single(0x0098,0x2610,0x017C,0x39)); // Maniac 39
+ CRCToSound(0x5D95F88C, V2A_Sound_Single(0x0098,0x0A58,0x007F,0x1E)); // Maniac 65
+ CRCToSound(0x92D704EA, V2A_Sound_SingleLooped(0x009C,0x29BC,0x012C,0x3F,0x1BD4,0x0DE8)); // Maniac 15
+ CRCToSound(0x92F5513C, V2A_Sound_Single(0x009E,0x0DD4,0x01F4,0x3F)); // Maniac 13
+ CRCToSound(0xCC2F3B5A, V2A_Sound_Single(0x009E,0x00DE,0x01AC,0x3F)); // Maniac 43
+ CRCToSound(0x153207D3, V2A_Sound_Single(0x009E,0x0E06,0x02A8,0x3F)); // Maniac 67
+ CRCToSound(0xC4F370CE, V2A_Sound_Single(0x00AE,0x0330,0x01AC,0x3F)); // Maniac 8
+ CRCToSound(0x928C4BAC, V2A_Sound_Single(0x00AE,0x08D6,0x01AC,0x3F)); // Maniac 9
+ CRCToSound(0x62D5B11F, V2A_Sound_Single(0x00AE,0x165C,0x01CB,0x3F)); // Maniac 27
+ CRCToSound(0x3AB22CB5, V2A_Sound_Single(0x00AE,0x294E,0x012A,0x3F)); // Maniac 62
+ CRCToSound(0x2D70BBE9, V2A_Sound_SingleLoopedPitchbend(0x00B4,0x1702,0x03E8,0x0190,0x3F,5)); // Maniac 64
+ CRCToSound(0xFA4C1B1C, V2A_Sound_Special_ManiacNuclear(0x00B2,0x1702,0x0190,0x3F)); // Maniac 69
+ CRCToSound(0x19D50D67, V2A_Sound_Special_ManiacDing(0x00B6,0x0020,0x00C8,16,2)); // Maniac 14
+ CRCToSound(0x3E6FBE15, V2A_Sound_Special_ManiacTentacle(0x00B2,0x0010,0x007C,0x016D,1)); // Maniac 25
+ CRCToSound(0x5305753C, V2A_Sound_Special_ManiacTentacle(0x00B2,0x0010,0x007C,0x016D,7)); // Maniac 36
+ CRCToSound(0x28895106, V2A_Sound_Special_ManiacElectric(0x00C0,0x00FE,0x00E9,0x0111,4,0x0A)); // Maniac 59
+ CRCToSound(0xB641ACF6, V2A_Sound_Special_Maniac61(0x00C8,0x0100,0x00C8,0x01C2)); // Maniac 61
+ CRCToSound(0xE1A91583, V2A_Sound_Special_ManiacPhone(0x00D0,0x0040,0x007C,0x3F,0x007B,0x3F,0x3C,5,6)); // Maniac 23
+ CRCToSound(0x64816ED5, V2A_Sound_Special_ManiacPhone(0x00D0,0x0040,0x00BE,0x37,0x00BD,0x37,0x3C,5,6)); // Maniac 24
+ CRCToSound(0x639D72C2, V2A_Sound_Special_ManiacWrench(0x00D0,0x10A4,0x0080,0x3F,0x28,3)); // Maniac 46
+ CRCToSound(0xE8826D92, V2A_Sound_Special_ManiacTypewriter(0x00EC,0x025A,0x023C,0x3F,8,(const uint8 *)"\x20\x41\x04\x21\x08\x10\x13\x07", true)); // Maniac 45
+ CRCToSound(0xEDFF3D41, V2A_Sound_Single(0x00F8,0x2ADE,0x01F8,0x3F)); // Maniac 42 (this should echo, but it's barely noticeable and I don't feel like doing it)
+ CRCToSound(0x15606D06, V2A_Sound_Special_ManiacSiren(0x0148,0x0020,0x0168,0x0020,0x3F)); // Maniac 32
+ CRCToSound(0x753EAFE3, V2A_Sound_Special_Maniac44(0x017C,0x0010,0x018C,0x0020,0x00C8,0x0080,0x3F)); // Maniac 44
+ CRCToSound(0xB1AB065C, V2A_Sound_Music(0x0032,0x00B2,0x08B2,0x1222,0x1A52,0x23C2,0x3074,false)); // Maniac 50
+ CRCToSound(0x091F5D9C, V2A_Sound_Music(0x0032,0x0132,0x0932,0x1802,0x23D2,0x3EA2,0x4F04,false)); // Maniac 58
+
+ CRCToSound(0x8E2C8AB3, V2A_Sound_SingleLooped(0x005C,0x0F26,0x0168,0x3C)); // Zak 41
+ CRCToSound(0x3792071F, V2A_Sound_SingleLooped(0x0060,0x1A18,0x06A4,0x3F)); // Zak 88
+ CRCToSound(0xF192EDE9, V2A_Sound_SingleLooped(0x0062,0x0054,0x01FC,0x1E)); // Zak 68
+ CRCToSound(0xC43B0245, V2A_Sound_Special_Zak70(0x006C,0x166E,0x00C8,0x0190,0x0320,0x0640,0x32)); // Zak 70
+ CRCToSound(0xCEB51670, V2A_Sound_SingleLooped(0x00AC,0x26DC,0x012C,0x3F)); // Zak 42
+ CRCToSound(0x10347B51, V2A_Sound_SingleLooped(0x006C,0x00E0,0x0594,0x3F)); // Zak 18
+ CRCToSound(0x9D2FADC0, V2A_Sound_MultiLooped(0x0072,0x1FC8,0x016A,0x3F,0x01CE,0x3F)); // Zak 80
+ CRCToSound(0xFAD2C676, V2A_Sound_MultiLooped(0x0076,0x0010,0x0080,0x3F,0x0090,0x3B)); // Zak 40
+ CRCToSound(0x01508B48, V2A_Sound_Single(0x0076,0x0D8C,0x017C,0x3F)); // Zak 90
+ CRCToSound(0x9C18DC46, V2A_Sound_Single(0x0076,0x0D8C,0x015E,0x3F)); // Zak 91
+ CRCToSound(0xF98F7EAC, V2A_Sound_Single(0x0076,0x0D8C,0x0140,0x3F)); // Zak 92
+ CRCToSound(0xC925FBEF, V2A_Sound_MultiLoopedDuration(0x0080,0x0010,0x0080,0x3F,0x0090,0x3B,0x0168)); // Zak 53
+ CRCToSound(0xCAB35257, V2A_Sound_Special_Zak101(0x00DA,0x425C,0x023C,0x08F0,0x0640,0x0478,0x3F,0x012C)); // Zak 101
+ CRCToSound(0xA31FE4FD, V2A_Sound_Single(0x0094,0x036A,0x00E1,0x3F)); // Zak 97
+ CRCToSound(0x0A1AE0F5, V2A_Sound_Single(0x009E,0x0876,0x0168,0x3F)); // Zak 5
+ CRCToSound(0xD01A66CB, V2A_Sound_Single(0x009E,0x04A8,0x0168,0x3F)); // Zak 47
+ CRCToSound(0x5497B912, V2A_Sound_Single(0x009E,0x0198,0x01F4,0x3F)); // Zak 39
+ CRCToSound(0x2B50362F, V2A_Sound_Single(0x009E,0x09B6,0x023D,0x3F)); // Zak 67
+ CRCToSound(0x7BFB6E72, V2A_Sound_Single(0x009E,0x0D14,0x0078,0x3F)); // Zak 69
+ CRCToSound(0xB803A792, V2A_Sound_Single(0x009E,0x2302,0x02BC,0x3F)); // Zak 78
+ CRCToSound(0x7AB82E39, V2A_Sound_SingleLooped(0x00A0,0x2A3C,0x016E,0x3F,0x1018,0x1A24)); // Zak 100
+ CRCToSound(0x28057CEC, V2A_Sound_Single(0x0098,0x0FEC,0x0140,0x32)); // Zak 63
+ CRCToSound(0x1180A2FC, V2A_Sound_Single(0x0098,0x0F06,0x0190,0x32)); // Zak 64
+ CRCToSound(0x12616755, V2A_Sound_Single(0x0098,0x14C8,0x023C,0x14)); // Zak 9
+ CRCToSound(0x642723AA, V2A_Sound_Special_Zak37(0x00A2,0x1702,0x01F4,0x3F)); // Zak 37
+ CRCToSound(0xDEE56848, V2A_Sound_Single(0x009A,0x0F86,0x0100,0x3F)); // Zak 93
+ CRCToSound(0xF9BE27B8, V2A_Sound_Special_Zak37(0x011C,0x1704,0x0228,0x3F)); // Zak 113
+ CRCToSound(0xC73487B2, V2A_Sound_Single(0x00B0,0x18BA,0x0478,0x3F)); // Zak 81
+ CRCToSound(0x32D8F925, V2A_Sound_Single(0x00B0,0x2E46,0x00F0,0x3F)); // Zak 94
+ CRCToSound(0x988C83A5, V2A_Sound_Single(0x00B0,0x0DE0,0x025B,0x3F)); // Zak 106
+ CRCToSound(0x8F1E3B3D, V2A_Sound_Single(0x00B0,0x05FE,0x04E2,0x3F)); // Zak 107
+ CRCToSound(0x0A2A7646, V2A_Sound_Single(0x00B0,0x36FE,0x016E,0x3F)); // Zak 43
+ CRCToSound(0x6F1FC435, V2A_Sound_Single(0x00B0,0x2808,0x044C,0x3F)); // Zak 108
+ CRCToSound(0x870EFC29, V2A_Sound_SingleLoopedPitchbend(0x00BA,0x0100,0x03E8,0x00C8,0x3F,3)); // Zak 55
+ CRCToSound(0xED773699, V2A_Sound_Special_ManiacDing(0x00B4,0x0020,0x012C,8,4)); // Zak 3
+ CRCToSound(0x0BF59774, V2A_Sound_Special_ZakStereoDing(0x00BE,0x0020,0x00F8,0x00F7,8,1)); // Zak 72
+ CRCToSound(0x656FFEDE, V2A_Sound_Special_ZakStereoDing(0x00BE,0x0020,0x00C4,0x00C3,8,1)); // Zak 73
+ CRCToSound(0xFC4D41E5, V2A_Sound_Special_ZakStereoDing(0x00BE,0x0020,0x00A5,0x00A4,8,1)); // Zak 74
+ CRCToSound(0xC0DD2089, V2A_Sound_Special_ZakStereoDing(0x00BE,0x0020,0x009C,0x009B,8,1)); // Zak 75
+ CRCToSound(0x627DFD92, V2A_Sound_Special_ZakStereoDing(0x00BE,0x0020,0x008B,0x008A,8,1)); // Zak 76
+ CRCToSound(0x703E05C1, V2A_Sound_Special_ZakStereoDing(0x00BE,0x0020,0x007C,0x007B,8,1)); // Zak 77
+ CRCToSound(0xB0F77006, V2A_Sound_Unsupported()); // Zak 52
+ CRCToSound(0x5AE9D6A7, V2A_Sound_Special_ZakAirplane(0x00CA,0x22A4,0x0113,0x0227)); // Zak 109
+ CRCToSound(0xABE0D3B0, V2A_Sound_Special_ZakAirplane(0x00CE,0x22A4,0x0227,0x0113)); // Zak 105
+ CRCToSound(0x788CC749, V2A_Sound_Special_Zak71(0x00C8,0x0B37)); // Zak 71
+ CRCToSound(0x2E2AB1FA, V2A_Sound_Special_ZakTram(0x00D4,0x04F0,0x0FE3,0x0080,0x3F)); // Zak 99
+ CRCToSound(0x1304CF20, V2A_Sound_Special_ManiacTypewriter(0x00DC,0x0624,0x023C,0x3C,2,(const uint8 *)"\x14\x11",false)); // Zak 79
+ CRCToSound(0xAE68ED91, V2A_Sound_Special_Zak54(0x00D4,0x1A25,0x1E1E,0x0B80,0x01F4)); // Zak 54
+ CRCToSound(0xA4F40F97, V2A_Sound_Unsupported()); // Zak 61
+ CRCToSound(0x348F85CE, V2A_Sound_Unsupported()); // Zak 62
+ CRCToSound(0xD473AB86, V2A_Sound_Special_ManiacTypewriter(0x0122,0x03E8,0x00BE,0x3F,7,(const uint8 *)"\x0F\x0B\x04\x0F\x1E\x0F\x66",false)); // Zak 46
+ CRCToSound(0x84A0BA90, V2A_Sound_Unsupported()); // Zak 110
+ CRCToSound(0x92680D9F, V2A_Sound_Unsupported()); // Zak 32
+ CRCToSound(0xABFFDB02, V2A_Sound_Unsupported()); // Zak 86
+ CRCToSound(0x41045447, V2A_Sound_Unsupported()); // Zak 98
+ CRCToSound(0xC8EEBD34, V2A_Sound_Unsupported()); // Zak 82
+ CRCToSound(0x42F9469F, V2A_Sound_Music(0x05F6,0x0636,0x0456,0x0516,0x05D6,0x05E6,0x0A36,true)); // Zak 96
+ CRCToSound(0x038BBD78, V2A_Sound_Music(0x054E,0x05CE,0x044E,0x04BE,0x052E,0x053E,0x0BCE,true)); // Zak 85
+ CRCToSound(0x06FFADC5, V2A_Sound_Music(0x0626,0x0686,0x0446,0x04F6,0x0606,0x0616,0x0C86,true)); // Zak 87
+ CRCToSound(0xCE20ECF0, V2A_Sound_Music(0x0636,0x0696,0x0446,0x0576,0x0616,0x0626,0x0E96,true)); // Zak 114
+ CRCToSound(0xBDA01BB6, V2A_Sound_Music(0x0678,0x06B8,0x0458,0x0648,0x0658,0x0668,0x0EB8,false)); // Zak 33
+ CRCToSound(0x59976529, V2A_Sound_Music(0x088E,0x092E,0x048E,0x05EE,0x074E,0x07EE,0x112E,true)); // Zak 49
+ CRCToSound(0xED1EED02, V2A_Sound_Music(0x08D0,0x0950,0x0440,0x07E0,0x08B0,0x08C0,0x1350,false)); // Zak 112
+ CRCToSound(0x5A16C037, V2A_Sound_Music(0x634A,0x64CA,0x049A,0x18FA,0x398A,0x511A,0x6CCA,false)); // Zak 95
+ return NULL;
+}
+
+Player_V2A::Player_V2A(ScummEngine *scumm) {
+ int i;
+ _vm = scumm;
+
+#ifdef PALMOS_68K
+ if (!CRCtable) CRCtable = (uint32 *)calloc(256, sizeof(uint32));
+#endif
+ InitCRC();
+
+ for (i = 0; i < V2A_MAXSLOTS; i++) {
+ _slot[i].id = 0;
+ _slot[i].sound = NULL;
+ }
+
+ _mod = new Player_MOD(scumm);
+ _mod->setUpdateProc(update_proc, this, 60);
+}
+
+Player_V2A::~Player_V2A() {
+ delete _mod;
+#ifdef PALMOS_68K
+ free(CRCtable);
+#endif
+}
+
+void Player_V2A::setMusicVolume (int vol) {
+ _mod->setMusicVolume(vol);
+}
+
+int Player_V2A::getSoundSlot (int id) const {
+ int i;
+ for (i = 0; i < V2A_MAXSLOTS; i++) {
+ if (_slot[i].id == id)
+ break;
+ }
+ if (i == V2A_MAXSLOTS) {
+ if (id == 0)
+ warning("player_v2a - out of sound slots");
+ return -1;
+ }
+ return i;
+}
+
+void Player_V2A::stopAllSounds() {
+ for (int i = 0; i < V2A_MAXSLOTS; i++) {
+ if (!_slot[i].id)
+ continue;
+ _slot[i].sound->stop();
+ delete _slot[i].sound;
+ _slot[i].sound = NULL;
+ _slot[i].id = 0;
+ }
+}
+
+void Player_V2A::stopSound(int nr) {
+ int i;
+ if (nr == 0)
+ return;
+ i = getSoundSlot(nr);
+ if (i == -1)
+ return;
+ _slot[i].sound->stop();
+ delete _slot[i].sound;
+ _slot[i].sound = NULL;
+ _slot[i].id = 0;
+}
+
+void Player_V2A::startSound(int nr) {
+ assert(_vm);
+ byte *data = _vm->getResourceAddress(rtSound, nr);
+ assert(data);
+ uint32 crc = GetCRC(data + 0x0A, READ_BE_UINT16(data + 0x08));
+ V2A_Sound *snd = findSound(crc);
+ if (snd == NULL) {
+ warning("player_v2a - sound %i not recognized yet (crc %08X)",nr,crc);
+ return;
+ }
+ stopSound(nr);
+ int i = getSoundSlot();
+ if (i == -1)
+ return;
+ _slot[i].id = nr;
+ _slot[i].sound = snd;
+ _slot[i].sound->start(_mod,nr,data);
+}
+
+void Player_V2A::update_proc(void *param) {
+ ((Player_V2A *)param)->updateSound();
+}
+
+void Player_V2A::updateSound() {
+ int i;
+ for (i = 0; i < V2A_MAXSLOTS; i++) {
+ if ((_slot[i].id) && (!_slot[i].sound->update())) {
+ _slot[i].sound->stop();
+ delete _slot[i].sound;
+ _slot[i].sound = NULL;
+ _slot[i].id = 0;
+ }
+ }
+}
+
+int Player_V2A::getMusicTimer() const {
+ return 0; // FIXME - need to keep track of playing music resources
+}
+
+int Player_V2A::getSoundStatus(int nr) const {
+ for (int i = 0; i < V2A_MAXSLOTS; i++) {
+ if (_slot[i].id == nr)
+ return 1;
+ }
+ return 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_v2a.h b/engines/scumm/player_v2a.h
new file mode 100644
index 0000000000..b2d25d67d5
--- /dev/null
+++ b/engines/scumm/player_v2a.h
@@ -0,0 +1,74 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef PLAYER_V2A_H
+#define PLAYER_V2A_H
+
+#include "common/scummsys.h"
+#include "scumm/music.h"
+#include "scumm/player_mod.h"
+
+class Mixer;
+
+namespace Scumm {
+
+class ScummEngine;
+class V2A_Sound;
+
+/**
+ * Scumm V2 Amiga sound/music driver.
+ */
+class Player_V2A : public MusicEngine {
+public:
+ Player_V2A(ScummEngine *scumm);
+ virtual ~Player_V2A();
+
+ virtual void setMusicVolume(int vol);
+ virtual void startSound(int sound);
+ virtual void stopSound(int sound);
+ virtual void stopAllSounds();
+ virtual int getMusicTimer() const;
+ virtual int getSoundStatus(int sound) const;
+
+private:
+ enum {
+ V2A_MAXSLOTS = 8
+ };
+
+ struct soundSlot {
+ int id;
+ V2A_Sound *sound;
+ };
+
+ ScummEngine *_vm;
+ Player_MOD *_mod;
+ soundSlot _slot[V2A_MAXSLOTS];
+
+ int getSoundSlot (int id = 0) const;
+ static void update_proc(void *param);
+ void updateSound();
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/player_v3a.cpp b/engines/scumm/player_v3a.cpp
new file mode 100644
index 0000000000..32ab9754af
--- /dev/null
+++ b/engines/scumm/player_v3a.cpp
@@ -0,0 +1,347 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "base/engine.h"
+#include "scumm/player_v3a.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+static const uint16 note_freqs[4][12] = {
+ {0x06B0, 0x0650, 0x05F4, 0x05A0, 0x054C, 0x0500, 0x04B8, 0x0474, 0x0434, 0x03F8, 0x03C0, 0x0388},
+ {0x0358, 0x0328, 0x02FA, 0x02D0, 0x02A6, 0x0280, 0x025C, 0x023A, 0x021A, 0x01FC, 0x01E0, 0x01C4},
+ {0x01AC, 0x0194, 0x017D, 0x0168, 0x0153, 0x0140, 0x012E, 0x011D, 0x010D, 0x00FE, 0x00F0, 0x00E2},
+ {0x00D6, 0x00CA, 0x00BE, 0x00B4, 0x00A9, 0x00A0, 0x0097, 0x008E, 0x0086, 0x007F, 0x00F0, 0x00E2}
+};
+
+Player_V3A::Player_V3A(ScummEngine *scumm) {
+ int i;
+ _vm = scumm;
+ for (i = 0; i < V3A_MAXMUS; i++) {
+ _mus[i].id = 0;
+ _mus[i].dur = 0;
+ }
+ for (i = 0; i < V3A_MAXSFX; i++) {
+ _sfx[i].id = 0;
+ _sfx[i].dur = 0;
+ }
+
+ _curSong = 0;
+ _songData = NULL;
+ _songPtr = 0;
+ _songDelay = 0;
+
+ _music_timer = 0;
+
+ _isinit = false;
+
+ _mod = new Player_MOD(scumm);
+ _mod->setUpdateProc(update_proc, this, 60);
+}
+
+Player_V3A::~Player_V3A() {
+ int i;
+ delete _mod;
+ if (_isinit) {
+ for (i = 0; _wavetable[i] != NULL; i++) {
+ for (int j = 0; j < 6; j++) {
+ free(_wavetable[i]->_idat[j]);
+ free(_wavetable[i]->_ldat[j]);
+ }
+ free(_wavetable[i]);
+ }
+ free(_wavetable);
+ }
+}
+
+void Player_V3A::setMusicVolume (int vol) {
+ _mod->setMusicVolume(vol);
+}
+
+int Player_V3A::getMusChan (int id) const {
+ int i;
+ for (i = 0; i < V3A_MAXMUS; i++) {
+ if (_mus[i].id == id)
+ break;
+ }
+ if (i == V3A_MAXMUS) {
+ if (id == 0)
+ warning("player_v3a - out of music channels");
+ return -1;
+ }
+ return i;
+}
+int Player_V3A::getSfxChan (int id) const {
+ int i;
+ for (i = 0; i < V3A_MAXSFX; i++) {
+ if (_sfx[i].id == id)
+ break;
+ }
+ if (i == V3A_MAXSFX) {
+ if (id == 0)
+ warning("player_v3a - out of sfx channels");
+ return -1;
+ }
+ return i;
+}
+
+void Player_V3A::stopAllSounds() {
+ int i;
+ for (i = 0; i < V3A_MAXMUS; i++) {
+ if (_mus[i].id)
+ _mod->stopChannel(_mus[i].id);
+ _mus[i].id = 0;
+ _mus[i].dur = 0;
+ }
+ _curSong = 0;
+ _songPtr = 0;
+ _songDelay = 0;
+ _songData = NULL;
+ for (i = 0; i < V3A_MAXSFX; i++) {
+ if (_sfx[i].id)
+ _mod->stopChannel(_sfx[i].id | 0x100);
+ _sfx[i].id = 0;
+ _sfx[i].dur = 0;
+ }
+}
+
+void Player_V3A::stopSound(int nr) {
+ int i;
+ if (nr == 0) { // Amiga Loom does this near the end, when Chaos casts SILENCE on Hetchel
+ stopAllSounds();
+ return;
+ }
+ if (nr == _curSong) {
+ for (i = 0; i < V3A_MAXMUS; i++) {
+ if (_mus[i].id)
+ _mod->stopChannel(_mus[i].id);
+ _mus[i].id = 0;
+ _mus[i].dur = 0;
+ }
+ _curSong = 0;
+ _songPtr = 0;
+ _songDelay = 0;
+ _songData = NULL;
+ } else {
+ i = getSfxChan(nr);
+ if (i != -1) {
+ _mod->stopChannel(nr | 0x100);
+ _sfx[i].id = 0;
+ _sfx[i].dur = 0;
+ }
+ }
+}
+
+void Player_V3A::startSound(int nr) {
+ assert(_vm);
+ byte *data = _vm->getResourceAddress(rtSound, nr);
+ assert(data);
+
+ if ((_vm->_gameId != GID_INDY3) && (_vm->_gameId != GID_LOOM))
+ error("player_v3a - unknown game!");
+
+ if (!_isinit) {
+ int i;
+ unsigned char *ptr;
+ int offset = 4;
+ int numInstruments;
+
+ if (_vm->_gameId == GID_INDY3) {
+ ptr = _vm->getResourceAddress(rtSound, 83);
+ numInstruments = 12;
+ } else {
+ ptr = _vm->getResourceAddress(rtSound, 79);
+ numInstruments = 9;
+ }
+ assert(ptr);
+ _wavetable = (instData **)malloc((numInstruments + 1) * sizeof(void *));
+ for (i = 0; i < numInstruments; i++) {
+ _wavetable[i] = (instData *)malloc(sizeof(instData));
+ for (int j = 0; j < 6; j++) {
+ int off, len;
+ off = READ_BE_UINT16(ptr + offset + 0);
+ _wavetable[i]->_ilen[j] = len = READ_BE_UINT16(ptr + offset + 2);
+ if (len) {
+ _wavetable[i]->_idat[j] = (char *)malloc(len);
+ memcpy(_wavetable[i]->_idat[j],ptr + off,len);
+ } else _wavetable[i]->_idat[j] = NULL;
+ off = READ_BE_UINT16(ptr + offset + 4);
+ _wavetable[i]->_llen[j] = len = READ_BE_UINT16(ptr + offset + 6);
+ if (len) {
+ _wavetable[i]->_ldat[j] = (char *)malloc(len);
+ memcpy(_wavetable[i]->_ldat[j],ptr + off,len);
+ } else _wavetable[i]->_ldat[j] = NULL;
+ _wavetable[i]->_oct[j] = READ_BE_UINT16(ptr + offset + 8);
+ offset += 10;
+ }
+ if (_vm->_gameId == GID_INDY3) {
+ _wavetable[i]->_pitadjust = 0;
+ offset += 2;
+ } else {
+ _wavetable[i]->_pitadjust = READ_BE_UINT16(ptr + offset + 2);
+ offset += 4;
+ }
+ }
+ _wavetable[i] = NULL;
+ _isinit = true;
+ }
+
+ if (getSoundStatus(nr))
+ stopSound(nr); // if a sound is playing, restart it
+
+ if (data[26]) {
+ if (_curSong)
+ stopSound(_curSong);
+ _curSong = nr;
+ _songData = data;
+ _songPtr = 0x1C;
+ _songDelay = 1;
+ _music_timer = 0;
+ } else {
+ int size = READ_BE_UINT16(data + 12);
+ int rate = 3579545 / READ_BE_UINT16(data + 20);
+ char *sound = (char *)malloc(size);
+ int vol = (data[24] << 1) | (data[24] >> 5); // if I boost this to 0-255, it gets too loud and starts to clip
+ memcpy(sound,data + READ_BE_UINT16(data + 8),size);
+ int loopStart = 0, loopEnd = 0;
+ bool looped = false;
+ if ((READ_BE_UINT16(data + 16) || READ_BE_UINT16(data + 6))) {
+ loopStart = READ_BE_UINT16(data + 10) - READ_BE_UINT16(data + 8);
+ loopEnd = READ_BE_UINT16(data + 14);
+ looped = true;
+ }
+ int i = getSfxChan();
+ if (i == -1)
+ {
+ free(sound);
+ return;
+ }
+ _sfx[i].id = nr;
+ _sfx[i].dur = looped ? -1 : (1 + 60 * size / rate);
+ if ((_vm->_gameId == GID_INDY3) && (nr == 60))
+ _sfx[i].dur = 240;
+ _mod->startChannel(nr | 0x100, sound, size, rate, vol, loopStart, loopEnd);
+ }
+}
+
+void Player_V3A::update_proc(void *param) {
+ ((Player_V3A *)param)->playMusic();
+}
+
+void Player_V3A::playMusic() {
+ int i;
+ for (i = 0; i < V3A_MAXMUS; i++) {
+ if (_mus[i].id) {
+ _mus[i].dur--;
+ if (_mus[i].dur)
+ continue;
+ _mod->stopChannel(_mus[i].id);
+ _mus[i].id = 0;
+ }
+ }
+ for (i = 0; i < V3A_MAXSFX; i++) {
+ if (_sfx[i].id) {
+ _sfx[i].dur--;
+ if (_sfx[i].dur)
+ continue;
+ _mod->stopChannel(_sfx[i].id | 0x100);
+ _sfx[i].id = 0;
+ }
+ }
+
+ _music_timer++;
+ if (!_curSong)
+ return;
+ if (_songDelay && --_songDelay)
+ return;
+ if (_songPtr == 0) {
+ // at the end of the song, and it wasn't looped - kill it
+ _curSong = 0;
+ return;
+ }
+ while (1) {
+ int inst, pit, vol, dur, oct;
+ inst = _songData[_songPtr++];
+ if ((inst & 0xF0) != 0x80) {
+ // tune is at the end - figure out what's still playing
+ // and see how long we have to wait until we stop/restart
+ for (i = 0; i < V3A_MAXMUS; i++) {
+ if (_songDelay < _mus[i].dur)
+ _songDelay = _mus[i].dur;
+ }
+ if (inst == 0xFB) // it's a looped song, restart it afterwards
+ _songPtr = 0x1C;
+ else _songPtr = 0; // otherwise, terminate it
+ break;
+ }
+ inst &= 0xF;
+ pit = _songData[_songPtr++];
+ vol = _songData[_songPtr++] & 0x7F; // if I boost this to 0-255, it gets too loud and starts to clip
+ dur = _songData[_songPtr++];
+ if (pit == 0) {
+ _songDelay = dur;
+ break;
+ }
+ pit += _wavetable[inst]->_pitadjust;
+ oct = (pit / 12) - 2;
+ pit = pit % 12;
+ if (oct < 0)
+ oct = 0;
+ if (oct > 5)
+ oct = 5;
+ int rate = 3579545 / note_freqs[_wavetable[inst]->_oct[oct]][pit];
+ if (!_wavetable[inst]->_llen[oct])
+ dur = _wavetable[inst]->_ilen[oct] * 60 / rate;
+ char *data = (char *)malloc(_wavetable[inst]->_ilen[oct] + _wavetable[inst]->_llen[oct]);
+ if (_wavetable[inst]->_idat[oct])
+ memcpy(data, _wavetable[inst]->_idat[oct], _wavetable[inst]->_ilen[oct]);
+ if (_wavetable[inst]->_ldat[oct])
+ memcpy(data + _wavetable[inst]->_ilen[oct], _wavetable[inst]->_ldat[oct], _wavetable[inst]->_llen[oct]);
+
+ i = getMusChan();
+ if (i == -1)
+ {
+ free(data);
+ return;
+ }
+ _mus[i].id = i + 1;
+ _mus[i].dur = dur + 1;
+ _mod->startChannel(_mus[i].id, data, _wavetable[inst]->_ilen[oct] + _wavetable[inst]->_llen[oct], rate, vol,
+ _wavetable[inst]->_ilen[oct], _wavetable[inst]->_ilen[oct] + _wavetable[inst]->_llen[oct]);
+ }
+}
+
+int Player_V3A::getMusicTimer() const {
+ return _music_timer / 30;
+}
+
+int Player_V3A::getSoundStatus(int nr) const {
+ if (nr == _curSong)
+ return 1;
+ if (getSfxChan(nr) != -1)
+ return 1;
+ return 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/player_v3a.h b/engines/scumm/player_v3a.h
new file mode 100644
index 0000000000..351d8ece60
--- /dev/null
+++ b/engines/scumm/player_v3a.h
@@ -0,0 +1,101 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef PLAYER_V3A_H
+#define PLAYER_V3A_H
+
+#include "common/scummsys.h"
+#include "scumm/music.h"
+#include "scumm/player_mod.h"
+
+class Mixer;
+
+namespace Scumm {
+
+class ScummEngine;
+
+/**
+ * Scumm V3 Amiga sound/music driver.
+ */
+class Player_V3A : public MusicEngine {
+public:
+ Player_V3A(ScummEngine *scumm);
+ virtual ~Player_V3A();
+
+ virtual void setMusicVolume(int vol);
+ virtual void startSound(int sound);
+ virtual void stopSound(int sound);
+ virtual void stopAllSounds();
+ virtual int getMusicTimer() const;
+ virtual int getSoundStatus(int sound) const;
+
+private:
+ enum {
+ V3A_MAXMUS = 24,
+ V3A_MAXSFX = 16
+ };
+
+ struct musChan {
+ int id;
+ int dur;
+ };
+
+ struct sfxChan {
+ int id;
+ int dur;
+ // SFX will eventually have pitch bends
+ };
+
+ struct instData {
+ char *_idat[6];
+ uint16 _ilen[6];
+ char *_ldat[6];
+ uint16 _llen[6];
+ uint16 _oct[6];
+ int16 _pitadjust;
+ };
+
+ ScummEngine *_vm;
+ Player_MOD *_mod;
+
+ musChan _mus[V3A_MAXMUS];
+ sfxChan _sfx[V3A_MAXSFX];
+
+ int _curSong;
+ uint8 *_songData;
+ uint16 _songPtr;
+ uint16 _songDelay;
+ int _music_timer;
+ bool _isinit;
+
+ instData **_wavetable;
+
+ int getMusChan (int id = 0) const;
+ int getSfxChan (int id = 0) const;
+ static void update_proc(void *param);
+ void playMusic();
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/resource.cpp b/engines/scumm/resource.cpp
new file mode 100644
index 0000000000..e1601ca991
--- /dev/null
+++ b/engines/scumm/resource.cpp
@@ -0,0 +1,1616 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/str.h"
+
+#include "scumm/charset.h"
+#include "scumm/dialogs.h"
+#include "scumm/imuse.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/intern.h"
+#ifndef DISABLE_HE
+#include "scumm/intern_he.h"
+#endif
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+enum {
+ RF_LOCK = 0x80,
+ RF_USAGE = 0x7F,
+ RF_USAGE_MAX = RF_USAGE,
+
+ RS_MODIFIED = 0x10
+};
+
+
+
+extern const char *resTypeFromId(int id);
+
+static uint16 newTag2Old(uint32 newTag);
+static const byte *findResourceSmall(uint32 tag, const byte *searchin);
+
+#ifndef DISABLE_HE
+static bool checkTryMedia(BaseScummFile *handle);
+#endif
+
+
+/* Open a room */
+void ScummEngine::openRoom(const int room) {
+ int room_offs;
+ bool result;
+ char buf[128];
+ char buf2[128] = "";
+ byte encByte = 0;
+
+ debugC(DEBUG_GENERAL, "openRoom(%d)", room);
+ assert(room >= 0);
+
+ /* Don't load the same room again */
+ if (_lastLoadedRoom == room)
+ return;
+ _lastLoadedRoom = room;
+
+ /* Room -1 means close file */
+ if (room == -1) {
+ deleteRoomOffsets();
+ _fileHandle->close();
+ return;
+ }
+
+ const int diskNumber = (room == 0 ? 0 : res.roomno[rtRoom][room]);
+
+ /* Either xxx.lfl or monkey.xxx file name */
+ while (1) {
+ room_offs = room ? res.roomoffs[rtRoom][room] : 0;
+
+ if (room_offs == -1)
+ break;
+
+ if (room_offs != 0 && room != 0 && _heversion < 98) {
+ _fileOffset = res.roomoffs[rtRoom][room];
+ return;
+ }
+ if (_version <= 3) {
+ sprintf(buf, "%.2d.lfl", room);
+ // Maniac Mansion demo has .man instead of .lfl
+ if (_gameId == GID_MANIAC)
+ sprintf(buf2, "%.2d.man", room);
+ encByte = (_features & GF_USE_KEY) ? 0xFF : 0;
+ } else if (_features & GF_SMALL_HEADER) {
+ if (room == 0 || room >= 900) {
+ sprintf(buf, "%.3d.lfl", room);
+ encByte = 0;
+ if (openResourceFile(buf, encByte)) {
+ return;
+ }
+ askForDisk(buf, diskNumber);
+
+ } else {
+ sprintf(buf, "disk%.2d.lec", diskNumber);
+ encByte = 0x69;
+ }
+ } else {
+
+ if (_heversion >= 70) { // Windows titles
+ if (_heversion >= 98) {
+ int disk = 0;
+ if (_heV7DiskOffsets)
+ disk = _heV7DiskOffsets[room];
+
+ switch(disk) {
+ case 2:
+ sprintf(buf, "%s.%s", _baseName.c_str(), "(b)");
+ break;
+ case 1:
+ sprintf(buf, "%s.%s", _baseName.c_str(), "(a)");
+ break;
+ default:
+ sprintf(buf, "%s.%s", _baseName.c_str(), "he0");
+ }
+ } else
+ sprintf(buf, "%s.he%.1d", _baseName.c_str(), room == 0 ? 0 : 1);
+ } else if (_version >= 7) {
+ if (room > 0 && (_version == 8))
+ VAR(VAR_CURRENTDISK) = diskNumber;
+ sprintf(buf, "%s.la%d", _baseName.c_str(), diskNumber);
+
+ sprintf(buf2, "%s.%.3d", _baseName.c_str(), diskNumber);
+ } else if (_heversion >= 60) {
+ sprintf(buf, "%s.he%.1d", _baseName.c_str(), diskNumber);
+ } else {
+ sprintf(buf, "%s.%.3d", _baseName.c_str(), diskNumber);
+ if (_gameId == GID_SAMNMAX)
+ sprintf(buf2, "%s.sm%.1d", _baseName.c_str(), diskNumber);
+ }
+
+ encByte = (_features & GF_USE_KEY) ? 0x69 : 0;
+ }
+
+ // If we have substitute
+ if (_substResFileNameIndex > 0 && !(_platform == Common::kPlatformNES || _platform == Common::kPlatformC64)) {
+ char tmpBuf[128];
+ generateSubstResFileName(buf, tmpBuf, sizeof(tmpBuf));
+ strcpy(buf, tmpBuf);
+ if (buf2[0]) {
+ generateSubstResFileName(buf2, tmpBuf, sizeof(tmpBuf));
+ strcpy(buf2, tmpBuf);
+ }
+ }
+
+ result = openResourceFile(buf, encByte);
+ if ((result == false) && (buf2[0])) {
+ result = openResourceFile(buf2, encByte);
+ // We have .man files so set demo mode
+ if (_gameId == GID_MANIAC)
+ _demoMode = true;
+ }
+
+ if (result) {
+ if (room == 0)
+ return;
+ deleteRoomOffsets();
+ readRoomsOffsets();
+ _fileOffset = res.roomoffs[rtRoom][room];
+
+ if (_fileOffset != 8)
+ return;
+
+ error("Room %d not in %s", room, buf);
+ return;
+ }
+ askForDisk(buf, diskNumber);
+ }
+
+ do {
+ sprintf(buf, "%.3d.lfl", room);
+ encByte = 0;
+ if (openResourceFile(buf, encByte))
+ break;
+ askForDisk(buf, diskNumber);
+ } while (1);
+
+ deleteRoomOffsets();
+ _fileOffset = 0; // start of file
+}
+
+void ScummEngine::closeRoom() {
+ if (_lastLoadedRoom != -1) {
+ _lastLoadedRoom = -1;
+ deleteRoomOffsets();
+ _fileHandle->close();
+ }
+}
+
+/** Delete the currently loaded room offsets. */
+void ScummEngine::deleteRoomOffsets() {
+ for (int i = 0; i < _numRooms; i++) {
+ if (res.roomoffs[rtRoom][i] != 0xFFFFFFFF)
+ res.roomoffs[rtRoom][i] = 0;
+ }
+}
+
+/** Read room offsets */
+void ScummEngine::readRoomsOffsets() {
+ int num, room;
+
+ debug(9, "readRoomOffsets()");
+
+ if (_features & GF_SMALL_HEADER) {
+ _fileHandle->seek(12, SEEK_SET); // Directly searching for the room offset block would be more generic...
+ } else {
+ _fileHandle->seek(16, SEEK_SET);
+ }
+
+ num = _fileHandle->readByte();
+ while (num--) {
+ room = _fileHandle->readByte();
+ if (res.roomoffs[rtRoom][room] != 0xFFFFFFFF) {
+ res.roomoffs[rtRoom][room] = _fileHandle->readUint32LE();
+ } else {
+ _fileHandle->readUint32LE();
+ }
+ }
+}
+
+bool ScummEngine::openFile(BaseScummFile &file, const char *filename, bool resourceFile) {
+ bool result = false;
+
+ if (!_containerFile.isEmpty()) {
+ char name[128];
+
+ file.close();
+ file.open(_containerFile.c_str());
+ assert(file.isOpen());
+
+ strncpy(name, filename, 128);
+
+ // Some Mac demos (i.e. DOTT) have bundled file names different
+ // from target name. dottdemo.000 vs tentacle.000. So we should
+ // substitute those names too
+ if (resourceFile == true) {
+ if (_substResFileNameIndexBundle == 0) {
+ int substLastIndex = 0;
+
+ while (substLastIndex != -1) {
+ if (file.openSubFile(name))
+ break;
+
+ substLastIndex = generateSubstResFileName(filename, name, sizeof(name), substLastIndex + 1);
+ }
+
+ if (substLastIndex == 0)
+ substLastIndex = -1;
+
+ _substResFileNameIndexBundle = substLastIndex;
+
+ if (substLastIndex != -1)
+ debug(5, "Generated substitute in Mac bundle: [%s -> %s]", filename, name);
+ }
+
+ if (_substResFileNameIndexBundle != -1)
+ generateSubstResFileName(filename, name, sizeof(name), _substResFileNameIndexBundle);
+ }
+
+ result = file.openSubFile(name);
+ }
+
+ if (!result) {
+ file.close();
+ result = file.open(filename);
+ }
+
+ return result;
+}
+
+bool ScummEngine::openResourceFile(const char *filename, byte encByte) {
+ debugC(DEBUG_GENERAL, "openResourceFile(%s)", filename);
+
+ if (openFile(*_fileHandle, filename, true)) {
+ _fileHandle->setEnc(encByte);
+ return true;
+ }
+ return false;
+}
+
+void ScummEngine::askForDisk(const char *filename, int disknum) {
+ char buf[128];
+
+ if (_version == 8) {
+#ifndef DISABLE_SCUMM_7_8
+ char result;
+
+ _imuseDigital->stopAllSounds();
+
+#ifdef MACOSX
+ sprintf(buf, "Cannot find file: '%s'\nPlease insert disc %d.\nPress OK to retry, Quit to exit", filename, disknum);
+#else
+ sprintf(buf, "Cannot find file: '%s'\nInsert disc %d into drive %s\nPress OK to retry, Quit to exit", filename, disknum, _gameDataPath.c_str());
+#endif
+
+ result = displayMessage("Quit", buf);
+ if (!result) {
+ error("Cannot find file: '%s'", filename);
+ }
+#endif
+ } else {
+ sprintf(buf, "Cannot find file: '%s'", filename);
+ InfoDialog dialog(this, (char*)buf);
+ runDialog(dialog);
+ error("Cannot find file: '%s'", filename);
+ }
+}
+
+void ScummEngine::readIndexFile() {
+ uint32 blocktype, itemsize;
+ int numblock = 0;
+
+ debugC(DEBUG_GENERAL, "readIndexFile()");
+
+ closeRoom();
+ openRoom(0);
+
+ if (_version <= 5) {
+ // Figure out the sizes of various resources
+ while (!_fileHandle->eof()) {
+ blocktype = fileReadDword();
+ itemsize = _fileHandle->readUint32BE();
+ if (_fileHandle->ioFailed())
+ break;
+ switch (blocktype) {
+ case MKID('DOBJ'):
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ itemsize -= 2;
+ break;
+ case MKID('DROO'):
+ _numRooms = _fileHandle->readUint16LE();
+ itemsize -= 2;
+ break;
+
+ case MKID('DSCR'):
+ _numScripts = _fileHandle->readUint16LE();
+ itemsize -= 2;
+ break;
+
+ case MKID('DCOS'):
+ _numCostumes = _fileHandle->readUint16LE();
+ itemsize -= 2;
+ break;
+
+ case MKID('DSOU'):
+ _numSounds = _fileHandle->readUint16LE();
+ itemsize -= 2;
+ break;
+ }
+ _fileHandle->seek(itemsize - 8, SEEK_CUR);
+ }
+ _fileHandle->clearIOFailed();
+ _fileHandle->seek(0, SEEK_SET);
+ }
+
+#ifndef DISABLE_HE
+ if (checkTryMedia(_fileHandle)) {
+ displayMessage(NULL, "You're trying to run game encrypted by ActiveMark. This is not supported.");
+ _quit = true;
+
+ return;
+ }
+#endif
+
+ while (true) {
+ blocktype = fileReadDword();
+ itemsize = _fileHandle->readUint32BE();
+
+ if (_fileHandle->ioFailed())
+ break;
+
+ numblock++;
+ readIndexBlock(blocktype, itemsize);
+ }
+
+// if (numblock!=9)
+// error("Not enough blocks read from directory");
+
+ closeRoom();
+}
+
+
+#ifndef DISABLE_HE
+
+#define TRYMEDIA_MARK_LEN 6
+
+bool checkTryMedia(BaseScummFile *handle) {
+ byte buf[TRYMEDIA_MARK_LEN];
+ bool matched = true;
+ const byte magic[2][TRYMEDIA_MARK_LEN] =
+ {{ 0x00, 'T', 'M', 'S', 'A', 'M' },
+ { 'i', '=', '$', ':', '(', '$' }}; // Same but 0x69 xored
+
+ handle->read(buf, TRYMEDIA_MARK_LEN);
+
+ for (int i = 0; i < 2; i++) {
+ matched = true;
+ for (int j = 0; j < TRYMEDIA_MARK_LEN; j++)
+ if (buf[j] != magic[i][j]) {
+ matched = false;
+ break;
+ }
+
+ if (matched)
+ break;
+ }
+
+ if (matched)
+ return true;
+
+ handle->seek(0, SEEK_SET);
+
+ return false;
+}
+#endif
+
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::readIndexBlock(uint32 blocktype, uint32 itemsize) {
+ int num;
+ char *ptr;
+ switch (blocktype) {
+ case MKID('ANAM'): // Used by: The Dig, FT
+ debug(9, "found ANAM block, reading audio names");
+ num = _fileHandle->readUint16LE();
+ ptr = (char*)malloc(num * 9);
+ _fileHandle->read(ptr, num * 9);
+ _imuseDigital->setAudioNames(num, ptr);
+ break;
+
+ case MKID('DRSC'): // Used by: COMI
+ readResTypeList(rtRoomScripts, MKID('RMSC'), "room script");
+ break;
+
+ default:
+ ScummEngine::readIndexBlock(blocktype, itemsize);
+ }
+}
+#endif
+
+#ifndef DISABLE_HE
+void ScummEngine_v70he::readIndexBlock(uint32 blocktype, uint32 itemsize) {
+ int i;
+ switch (blocktype) {
+ case MKID('DIRI'):
+ readResTypeList(rtRoomImage, MKID('RMIM'), "room image");
+ break;
+
+ case MKID('DIRM'):
+ readResTypeList(rtImage, MKID('AWIZ'), "images");
+ break;
+
+ case MKID('DIRT'):
+ readResTypeList(rtTalkie, MKID('TLKE'), "talkie");
+ break;
+
+ case MKID('DLFL'):
+ i = _fileHandle->readUint16LE();
+ _fileHandle->seek(-2, SEEK_CUR);
+ _heV7RoomOffsets = (byte *)calloc(2 + (i * 4), 1);
+ _fileHandle->read(_heV7RoomOffsets, (2 + (i * 4)) );
+ break;
+
+ case MKID('DISK'):
+ i = _fileHandle->readUint16LE();
+ _heV7DiskOffsets = (byte *)calloc(i, 1);
+ _fileHandle->read(_heV7DiskOffsets, i);
+ break;
+
+ case MKID('SVER'):
+ // Index version number
+ _fileHandle->seek(itemsize - 8, SEEK_CUR);
+ break;
+
+ case MKID('INIB'):
+ _fileHandle->seek(itemsize - 8, SEEK_CUR);
+ debug(2, "INIB index block not yet handled, skipping");
+ break;
+
+ default:
+ ScummEngine::readIndexBlock(blocktype, itemsize);
+ }
+}
+#endif
+
+void ScummEngine::readIndexBlock(uint32 blocktype, uint32 itemsize) {
+ int i;
+ switch (blocktype) {
+ case MKID('DCHR'):
+ case MKID('DIRF'):
+ readResTypeList(rtCharset, MKID('CHAR'), "charset");
+ break;
+
+ case MKID('DOBJ'):
+ debug(9, "found DOBJ block, reading object table");
+ readGlobalObjects();
+ break;
+
+ case MKID('RNAM'):
+ // Names of rooms. Maybe we should put them into a table, for use by the debugger?
+ if (_heversion >= 80) {
+ for (int room; (room = _fileHandle->readUint16LE()); ) {
+ char buf[100];
+ i = 0;
+ for (byte s; (s = _fileHandle->readByte()) && i < ARRAYSIZE(buf) - 1; ) {
+ buf[i++] = s;
+ }
+ buf[i] = 0;
+ debug(5, "Room %d: '%s'", room, buf);
+ }
+ } else {
+ for (int room; (room = _fileHandle->readByte()); ) {
+ char buf[10];
+ _fileHandle->read(buf, 9);
+ buf[9] = 0;
+ for (i = 0; i < 9; i++)
+ buf[i] ^= 0xFF;
+ debug(5, "Room %d: '%s'", room, buf);
+ }
+ }
+ break;
+
+ case MKID('DROO'):
+ case MKID('DIRR'):
+ readResTypeList(rtRoom, MKID('ROOM'), "room");
+ break;
+
+ case MKID('DSCR'):
+ case MKID('DIRS'):
+ readResTypeList(rtScript, MKID('SCRP'), "script");
+ break;
+
+ case MKID('DCOS'):
+ case MKID('DIRC'):
+ readResTypeList(rtCostume, MKID('COST'), "costume");
+ break;
+
+ case MKID('MAXS'):
+ readMAXS(itemsize);
+ allocateArrays();
+ break;
+
+ case MKID('DIRN'):
+ case MKID('DSOU'):
+ readResTypeList(rtSound, MKID('SOUN'), "sound");
+ break;
+
+ case MKID('AARY'):
+ readArrayFromIndexFile();
+ break;
+
+ default:
+ error("Bad ID %04X('%s') found in index file directory!", blocktype,
+ tag2str(blocktype));
+ }
+}
+
+void ScummEngine::readArrayFromIndexFile() {
+ error("readArrayFromIndexFile() not supported in pre-V6 games");
+}
+
+void ScummEngine::readResTypeList(int id, uint32 tag, const char *name) {
+ int num;
+ int i;
+
+ debug(9, "readResTypeList(%s,%s,%s)", resTypeFromId(id), tag2str(TO_BE_32(tag)), name);
+
+ if (_version == 8)
+ num = _fileHandle->readUint32LE();
+ else
+ num = _fileHandle->readUint16LE();
+
+ if (num != res.num[id]) {
+ error("Invalid number of %ss (%d) in directory", name, num);
+ }
+
+ if (_features & GF_SMALL_HEADER) {
+ for (i = 0; i < num; i++) {
+ res.roomno[id][i] = _fileHandle->readByte();
+ res.roomoffs[id][i] = _fileHandle->readUint32LE();
+ }
+ } else {
+ for (i = 0; i < num; i++) {
+ res.roomno[id][i] = _fileHandle->readByte();
+ }
+ for (i = 0; i < num; i++) {
+ res.roomoffs[id][i] = _fileHandle->readUint32LE();
+
+ if (id == rtRoom && _heversion >= 70)
+ _heV7RoomIntOffsets[i] = res.roomoffs[id][i];
+ }
+
+ if (_heversion >= 70) {
+ for (i = 0; i < num; i++) {
+ res.globsize[id][i] = _fileHandle->readUint32LE();
+ }
+ }
+ }
+}
+
+void ScummEngine::allocResTypeData(int id, uint32 tag, int num, const char *name, int mode) {
+ debug(9, "allocResTypeData(%s/%s,%s,%d,%d)", resTypeFromId(id), name, tag2str(TO_BE_32(tag)), num, mode);
+ assert(id >= 0 && id < (int)(ARRAYSIZE(res.mode)));
+
+ if (num >= 8000)
+ error("Too many %ss (%d) in directory", name, num);
+
+ res.mode[id] = mode;
+ res.num[id] = num;
+ res.tags[id] = tag;
+ res.name[id] = name;
+ res.address[id] = (byte **)calloc(num, sizeof(void *));
+ res.flags[id] = (byte *)calloc(num, sizeof(byte));
+ res.status[id] = (byte *)calloc(num, sizeof(byte));
+
+ if (mode) {
+ res.roomno[id] = (byte *)calloc(num, sizeof(byte));
+ res.roomoffs[id] = (uint32 *)calloc(num, sizeof(uint32));
+ }
+
+ if (_heversion >= 70) {
+ res.globsize[id] = (uint32 *)calloc(num, sizeof(uint32));
+
+ if (id == rtRoom)
+ _heV7RoomIntOffsets = (uint32 *)calloc(num, sizeof(uint32));
+ }
+}
+
+void ScummEngine::loadCharset(int no) {
+ int i;
+ byte *ptr;
+
+ debugC(DEBUG_GENERAL, "loadCharset(%d)", no);
+
+ /* FIXME - hack around crash in Indy4 (occurs if you try to load after dieing) */
+ if (_gameId == GID_INDY4 && no == 0)
+ no = 1;
+
+ /* for Humongous catalogs */
+ if (_heversion >= 70 && _numCharsets == 1) {
+ debug(0, "Not loading charset as it doesn't seem to exist?");
+ return;
+ }
+
+ assert(no < (int)sizeof(_charsetData) / 16);
+ checkRange(_numCharsets - 1, 1, no, "Loading illegal charset %d");
+
+ ptr = getResourceAddress(rtCharset, no);
+
+ for (i = 0; i < 15; i++) {
+ _charsetData[no][i + 1] = ptr[i + 14];
+ }
+}
+
+void ScummEngine::nukeCharset(int i) {
+ checkRange(_numCharsets - 1, 1, i, "Nuking illegal charset %d");
+ res.nukeResource(rtCharset, i);
+}
+
+void ScummEngine::ensureResourceLoaded(int type, int i) {
+ void *addr = NULL;
+
+ debugC(DEBUG_RESOURCE, "ensureResourceLoaded(%s,%d)", resTypeFromId(type), i);
+
+ if ((type == rtRoom) && i > 0x7F && _version < 7 && _heversion <= 71) {
+ i = _resourceMapper[i & 0x7F];
+ }
+
+ // FIXME - TODO: This check used to be "i==0". However, that causes
+ // problems when using this function to ensure charset 0 is loaded.
+ // This is done for many games, e.g. Zak256 or Indy3 (EGA and VGA).
+ // For now we restrict the check to anything which is not a charset.
+ // Question: Why was this check like that in the first place?
+ // Answer: costumes with an index of zero in the newer games at least.
+ // TODO: determine why the heck anything would try to load a costume
+ // with id 0. Is that "normal", or is it caused by yet another bug in
+ // our code base? After all we also have to add special cases for many
+ // of our script opcodes that check for the (invalid) actor 0... so
+ // maybe both issues are related...
+ if (type != rtCharset && i == 0)
+ return;
+
+ if (i <= res.num[type])
+ addr = res.address[type][i];
+
+ if (addr)
+ return;
+
+ loadResource(type, i);
+
+ if (_version == 5 && type == rtRoom && i == _roomResource)
+ VAR(VAR_ROOM_FLAG) = 1;
+}
+
+int ScummEngine::loadResource(int type, int idx) {
+ int roomNr;
+ uint32 fileOffs;
+ uint32 size, tag;
+
+ debugC(DEBUG_RESOURCE, "loadResource(%s,%d)", resTypeFromId(type),idx);
+
+ if (type == rtCharset && (_features & GF_SMALL_HEADER)) {
+ loadCharset(idx);
+ return (1);
+ }
+
+ roomNr = getResourceRoomNr(type, idx);
+
+ if (idx >= res.num[type])
+ error("%s %d undefined %d %d", res.name[type], idx, res.num[type], roomNr);
+
+ if (roomNr == 0)
+ roomNr = _roomResource;
+
+ if (type == rtRoom) {
+ if (_version == 8)
+ fileOffs = 8;
+ else if (_heversion >= 70)
+ fileOffs = _heV7RoomIntOffsets[idx];
+ else
+ fileOffs = 0;
+ } else {
+ fileOffs = res.roomoffs[type][idx];
+ if (fileOffs == 0xFFFFFFFF)
+ return 0;
+ }
+
+ openRoom(roomNr);
+
+ _fileHandle->seek(fileOffs + _fileOffset, SEEK_SET);
+
+ if (_features & GF_OLD_BUNDLE) {
+ if ((_version == 3) && !(_platform == Common::kPlatformAmiga) && (type == rtSound)) {
+ return readSoundResourceSmallHeader(type, idx);
+ } else {
+ size = _fileHandle->readUint16LE();
+ _fileHandle->seek(-2, SEEK_CUR);
+ }
+ } else if (_features & GF_SMALL_HEADER) {
+ if (_version == 4)
+ _fileHandle->seek(8, SEEK_CUR);
+ size = _fileHandle->readUint32LE();
+ tag = _fileHandle->readUint16LE();
+ _fileHandle->seek(-6, SEEK_CUR);
+ if ((type == rtSound) && !(_platform == Common::kPlatformAmiga) && !(_platform == Common::kPlatformFMTowns)) {
+ return readSoundResourceSmallHeader(type, idx);
+ }
+ } else {
+ if (type == rtSound) {
+ return readSoundResource(type, idx);
+ }
+
+ tag = fileReadDword();
+
+ if (tag != res.tags[type] && _heversion < 70) {
+ error("%s %d not in room %d at %d+%d in file %s",
+ res.name[type], idx, roomNr,
+ _fileOffset, fileOffs, _fileHandle->name());
+ }
+
+ size = _fileHandle->readUint32BE();
+ _fileHandle->seek(-8, SEEK_CUR);
+ }
+ _fileHandle->read(res.createResource(type, idx, size), size);
+
+ // dump the resource if requested
+ if (_dumpScripts && type == rtScript) {
+ dumpResource("script-", idx, getResourceAddress(rtScript, idx));
+ }
+
+ if (!_fileHandle->ioFailed()) {
+ return 1;
+ }
+
+ res.nukeResource(type, idx);
+
+ error("Cannot read resource");
+}
+
+int ScummEngine::getResourceRoomNr(int type, int idx) {
+ if (type == rtRoom && _heversion < 70)
+ return idx;
+ return res.roomno[type][idx];
+}
+
+int ScummEngine::getResourceSize(int type, int idx) {
+ byte *ptr = getResourceAddress(type, idx);
+ MemBlkHeader *hdr = (MemBlkHeader *)(ptr - sizeof(MemBlkHeader));
+
+ return hdr->size;
+}
+
+byte *ScummEngine::getResourceAddress(int type, int idx) {
+ byte *ptr;
+
+ CHECK_HEAP
+
+ if (_heversion >= 80 && type == rtString)
+ idx &= ~0x33539000;
+
+ if (!res.validateResource("getResourceAddress", type, idx))
+ return NULL;
+
+ if (!res.address[type]) {
+ debugC(DEBUG_RESOURCE, "getResourceAddress(%s,%d), res.address[type] == NULL", resTypeFromId(type), idx);
+ return NULL;
+ }
+
+ if (res.mode[type] && !res.address[type][idx]) {
+ ensureResourceLoaded(type, idx);
+ }
+
+ if (!(ptr = (byte *)res.address[type][idx])) {
+ debugC(DEBUG_RESOURCE, "getResourceAddress(%s,%d) == NULL", resTypeFromId(type), idx);
+ return NULL;
+ }
+
+ res.setResourceCounter(type, idx, 1);
+
+ debugC(DEBUG_RESOURCE, "getResourceAddress(%s,%d) == %p", resTypeFromId(type), idx, ptr + sizeof(MemBlkHeader));
+ return ptr + sizeof(MemBlkHeader);
+}
+
+byte *ScummEngine::getStringAddress(int i) {
+ byte *addr = getResourceAddress(rtString, i);
+ return addr;
+}
+
+byte *ScummEngine_v6::getStringAddress(int i) {
+ byte *addr = getResourceAddress(rtString, i);
+ if (addr == NULL)
+ return NULL;
+ return ((ScummEngine_v6::ArrayHeader *)addr)->data;
+}
+
+byte *ScummEngine::getStringAddressVar(int i) {
+ return getStringAddress(_scummVars[i]);
+}
+
+void ResourceManager::increaseResourceCounter() {
+ int i, j;
+ byte counter;
+
+ for (i = rtFirst; i <= rtLast; i++) {
+ for (j = num[i]; --j >= 0;) {
+ counter = flags[i][j] & RF_USAGE;
+ if (counter && counter < RF_USAGE_MAX) {
+ setResourceCounter(i, j, counter + 1);
+ }
+ }
+ }
+}
+
+void ResourceManager::setResourceCounter(int type, int idx, byte flag) {
+ flags[type][idx] &= ~RF_USAGE;
+ flags[type][idx] |= flag;
+}
+
+/* 2 bytes safety area to make "precaching" of bytes in the gdi drawer easier */
+#define SAFETY_AREA 2
+
+byte *ResourceManager::createResource(int type, int idx, uint32 size) {
+ byte *ptr;
+
+ CHECK_HEAP
+ debugC(DEBUG_RESOURCE, "res.createResource(%s,%d,%d)", resTypeFromId(type), idx, size);
+
+ if (!validateResource("allocating", type, idx))
+ return NULL;
+
+ if (_vm->_version <= 2) {
+ // Nuking and reloading a resource can be harmful in some
+ // cases. For instance, Zak tries to reload the intro music
+ // while it's playing. See bug #1253171.
+
+ if (address[type][idx] && (type == rtSound || type == rtScript || type == rtCostume))
+ return address[type][idx] + sizeof(MemBlkHeader);
+ }
+
+ nukeResource(type, idx);
+
+ expireResources(size);
+
+ CHECK_HEAP
+ ptr = (byte *)calloc(size + sizeof(MemBlkHeader) + SAFETY_AREA, 1);
+ if (ptr == NULL) {
+ error("Out of memory while allocating %d", size);
+ }
+
+ _allocatedSize += size;
+
+ address[type][idx] = ptr;
+ ((MemBlkHeader *)ptr)->size = size;
+ setResourceCounter(type, idx, 1);
+ return ptr + sizeof(MemBlkHeader); /* skip header */
+}
+
+ResourceManager::ResourceManager(ScummEngine *vm) {
+ memset(this, 0, sizeof(ResourceManager));
+ _vm = vm;
+// _allocatedSize = 0;
+}
+
+bool ResourceManager::validateResource(const char *str, int type, int idx) const {
+ if (type < rtFirst || type > rtLast || (uint) idx >= (uint)num[type]) {
+ error("%s Illegal Glob type %s (%d) num %d", str, resTypeFromId(type), type, idx);
+ return false;
+ }
+ return true;
+}
+
+void ResourceManager::nukeResource(int type, int idx) {
+ byte *ptr;
+
+ CHECK_HEAP
+ if (!address[type])
+ return;
+
+ assert(idx >= 0 && idx < num[type]);
+
+ ptr = address[type][idx];
+ if (ptr != NULL) {
+ debugC(DEBUG_RESOURCE, "nukeResource(%s,%d)", resTypeFromId(type), idx);
+ address[type][idx] = 0;
+ flags[type][idx] = 0;
+ status[type][idx] &= ~RS_MODIFIED;
+ _allocatedSize -= ((MemBlkHeader *)ptr)->size;
+ free(ptr);
+ }
+}
+
+const byte *ScummEngine::findResourceData(uint32 tag, const byte *ptr) {
+ if (_features & GF_OLD_BUNDLE)
+ error("findResourceData must not be used in GF_OLD_BUNDLE games");
+ else if (_features & GF_SMALL_HEADER)
+ ptr = findResourceSmall(tag, ptr);
+ else
+ ptr = findResource(tag, ptr);
+
+ if (ptr == NULL)
+ return NULL;
+ return ptr + _resourceHeaderSize;
+}
+
+int ScummEngine::getResourceDataSize(const byte *ptr) const {
+ if (ptr == NULL)
+ return 0;
+
+ if (_features & GF_OLD_BUNDLE)
+ return READ_LE_UINT16(ptr) - _resourceHeaderSize;
+ else if (_features & GF_SMALL_HEADER)
+ return READ_LE_UINT32(ptr) - _resourceHeaderSize;
+ else
+ return READ_BE_UINT32(ptr - 4) - _resourceHeaderSize;
+}
+
+void ResourceManager::lock(int type, int i) {
+ if (!validateResource("Locking", type, i))
+ return;
+ flags[type][i] |= RF_LOCK;
+}
+
+void ResourceManager::unlock(int type, int i) {
+ if (!validateResource("Unlocking", type, i))
+ return;
+ flags[type][i] &= ~RF_LOCK;
+}
+
+bool ResourceManager::isLocked(int type, int i) const {
+ if (!validateResource("isLocked", type, i))
+ return false;
+ return (flags[type][i] & RF_LOCK) != 0;
+}
+
+bool ScummEngine::isResourceInUse(int type, int i) const {
+ if (!res.validateResource("isResourceInUse", type, i))
+ return false;
+ switch (type) {
+ case rtRoom:
+ return _roomResource == (byte)i;
+ case rtRoomImage:
+ return _roomResource == (byte)i;
+ case rtRoomScripts:
+ return _roomResource == (byte)i;
+ case rtScript:
+ return isScriptInUse(i);
+ case rtCostume:
+ return isCostumeInUse(i);
+ case rtSound:
+ return _sound->isSoundInUse(i);
+ case rtCharset:
+ return _charset->getCurID() == i;
+ case rtImage:
+ return res.isModified(type, i) != 0;
+ case rtSpoolBuffer:
+ return _sound->isSoundRunning(10000 + i) != 0;
+ default:
+ return false;
+ }
+}
+
+void ResourceManager::setModified(int type, int i) {
+ if (!validateResource("Modified", type, i))
+ return;
+ status[type][i] |= RS_MODIFIED;
+}
+
+bool ResourceManager::isModified(int type, int i) const {
+ if (!validateResource("isModified", type, i))
+ return false;
+ return (status[type][i] & RS_MODIFIED) != 0;
+}
+
+void ResourceManager::expireResources(uint32 size) {
+ int i, j;
+ byte flag;
+ byte best_counter;
+ int best_type, best_res = 0;
+ uint32 oldAllocatedSize;
+
+ if (_expireCounter != 0xFF) {
+ _expireCounter = 0xFF;
+ increaseResourceCounter();
+ }
+
+ if (size + _allocatedSize < _maxHeapThreshold)
+ return;
+
+ oldAllocatedSize = _allocatedSize;
+
+ do {
+ best_type = 0;
+ best_counter = 2;
+
+ for (i = rtFirst; i <= rtLast; i++)
+ if (mode[i]) {
+ for (j = num[i]; --j >= 0;) {
+ flag = flags[i][j];
+ if (!(flag & RF_LOCK) && flag >= best_counter && address[i][j] && !_vm->isResourceInUse(i, j)) {
+ best_counter = flag;
+ best_type = i;
+ best_res = j;
+ }
+ }
+ }
+
+ if (!best_type)
+ break;
+ nukeResource(best_type, best_res);
+ } while (size + _allocatedSize > _minHeapThreshold);
+
+ increaseResourceCounter();
+
+ debugC(DEBUG_RESOURCE, "Expired resources, mem %d -> %d", oldAllocatedSize, _allocatedSize);
+}
+
+void ResourceManager::freeResources() {
+ int i, j;
+ for (i = rtFirst; i <= rtLast; i++) {
+ for (j = num[i]; --j >= 0;) {
+ if (isResourceLoaded(i, j))
+ nukeResource(i, j);
+ }
+ free(address[i]);
+ free(flags[i]);
+ free(status[i]);
+ free(roomno[i]);
+ free(roomoffs[i]);
+
+ free(globsize[i]);
+ }
+}
+
+void ScummEngine::loadPtrToResource(int type, int resindex, const byte *source) {
+ byte *alloced;
+ int i, len;
+
+ res.nukeResource(type, resindex);
+
+ len = resStrLen(source) + 1;
+
+ if (len <= 0)
+ return;
+
+ alloced = res.createResource(type, resindex, len);
+
+ if (!source) {
+ alloced[0] = fetchScriptByte();
+ for (i = 1; i < len; i++)
+ alloced[i] = *_scriptPointer++;
+ } else {
+ for (i = 0; i < len; i++)
+ alloced[i] = source[i];
+ }
+}
+
+bool ResourceManager::isResourceLoaded(int type, int idx) const {
+ if (!validateResource("isResourceLoaded", type, idx))
+ return false;
+ return address[type][idx] != NULL;
+}
+
+void ResourceManager::resourceStats() {
+ int i, j;
+ uint32 lockedSize = 0, lockedNum = 0;
+ byte flag;
+
+ for (i = rtFirst; i <= rtLast; i++)
+ for (j = num[i]; --j >= 0;) {
+ flag = flags[i][j];
+ if (flag & RF_LOCK && address[i][j]) {
+ lockedSize += ((MemBlkHeader *)address[i][j])->size;
+ lockedNum++;
+ }
+ }
+
+ debug(1, "Total allocated size=%d, locked=%d(%d)", _allocatedSize, lockedSize, lockedNum);
+}
+
+void ScummEngine_v5::readMAXS(int blockSize) {
+ debug(9, "ScummEngine_v5 readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE(); // 800
+ _fileHandle->readUint16LE(); // 16
+ _numBitVariables = _fileHandle->readUint16LE(); // 2048
+ _numLocalObjects = _fileHandle->readUint16LE(); // 200
+ _numArray = 50;
+ _numVerbs = 100;
+ // Used to be 50, which wasn't enough for MI2 and FOA. See bugs
+ // #933610, #936323 and #941275.
+ _numNewNames = 150;
+ _objectRoomTable = NULL;
+
+ _fileHandle->readUint16LE(); // 50
+ _numCharsets = _fileHandle->readUint16LE(); // 9
+ _fileHandle->readUint16LE(); // 100
+ _fileHandle->readUint16LE(); // 50
+ _numInventory = _fileHandle->readUint16LE(); // 80
+ _numGlobalScripts = 200;
+
+ _shadowPaletteSize = 256;
+
+ _numFlObject = 50;
+
+ if (_shadowPaletteSize)
+ _shadowPalette = (byte *)calloc(_shadowPaletteSize, 1);
+}
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v8::readMAXS(int blockSize) {
+ debug(9, "ScummEngine_v8 readMAXS: MAXS has blocksize %d", blockSize);
+
+ _fileHandle->seek(50, SEEK_CUR); // Skip over SCUMM engine version
+ _fileHandle->seek(50, SEEK_CUR); // Skip over data file version
+ _numVariables = _fileHandle->readUint32LE(); // 1500
+ _numBitVariables = _fileHandle->readUint32LE(); // 2048
+ _fileHandle->readUint32LE(); // 40
+ _numScripts = _fileHandle->readUint32LE(); // 458
+ _numSounds = _fileHandle->readUint32LE(); // 789
+ _numCharsets = _fileHandle->readUint32LE(); // 1
+ _numCostumes = _fileHandle->readUint32LE(); // 446
+ _numRooms = _fileHandle->readUint32LE(); // 95
+ _fileHandle->readUint32LE(); // 80
+ _numGlobalObjects = _fileHandle->readUint32LE(); // 1401
+ _fileHandle->readUint32LE(); // 60
+ _numLocalObjects = _fileHandle->readUint32LE(); // 200
+ _numNewNames = _fileHandle->readUint32LE(); // 100
+ _numFlObject = _fileHandle->readUint32LE(); // 128
+ _numInventory = _fileHandle->readUint32LE(); // 80
+ _numArray = _fileHandle->readUint32LE(); // 200
+ _numVerbs = _fileHandle->readUint32LE(); // 50
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ _numGlobalScripts = 2000;
+
+ _shadowPaletteSize = NUM_SHADOW_PALETTE * 256;
+ _shadowPalette = (byte *)calloc(_shadowPaletteSize, 1);
+}
+
+void ScummEngine_v7::readMAXS(int blockSize) {
+ debug(9, "ScummEngine_v7 readMAXS: MAXS has blocksize %d", blockSize);
+
+ _fileHandle->seek(50, SEEK_CUR); // Skip over SCUMM engine version
+ _fileHandle->seek(50, SEEK_CUR); // Skip over data file version
+ _numVariables = _fileHandle->readUint16LE();
+ _numBitVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numNewNames = _fileHandle->readUint16LE();
+ _numVerbs = _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+
+ if ((_gameId == GID_FT) && (_features & GF_DEMO) &&
+ (_platform == Common::kPlatformPC))
+ _numGlobalScripts = 300;
+ else
+ _numGlobalScripts = 2000;
+
+ _shadowPaletteSize = NUM_SHADOW_PALETTE * 256;
+ _shadowPalette = (byte *)calloc(_shadowPaletteSize, 1);
+}
+#endif
+
+void ScummEngine_v6::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v6 readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numBitVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numVerbs = _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numNewNames = 50;
+
+ _objectRoomTable = NULL;
+ _numGlobalScripts = 200;
+
+ if (_heversion >= 70) {
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ }
+
+ if (_heversion <= 70) {
+ _shadowPaletteSize = 256;
+ _shadowPalette = (byte *)calloc(_shadowPaletteSize, 1);
+ }
+}
+
+void ScummEngine::readGlobalObjects() {
+ int i;
+ int num = _fileHandle->readUint16LE();
+ assert(num == _numGlobalObjects);
+
+ _fileHandle->read(_objectOwnerTable, num);
+ for (i = 0; i < num; i++) {
+ _objectStateTable[i] = _objectOwnerTable[i] >> OF_STATE_SHL;
+ _objectOwnerTable[i] &= OF_OWNER_MASK;
+ }
+
+ _fileHandle->read(_classData, num * sizeof(uint32));
+
+#if defined(SCUMM_BIG_ENDIAN)
+ // Correct the endianess if necessary
+ for (i = 0; i != num; i++)
+ _classData[i] = FROM_LE_32(_classData[i]);
+#endif
+}
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v8::readGlobalObjects() {
+ int i;
+ int num = _fileHandle->readUint32LE();
+ assert(num == _numGlobalObjects);
+
+ _objectIDMap = new ObjectNameId[num];
+ _objectIDMapSize = num;
+ for (i = 0; i < num; i++) {
+ // Add to object name-to-id map
+ _fileHandle->read(_objectIDMap[i].name, 40);
+ _objectIDMap[i].id = i;
+
+ _objectStateTable[i] = _fileHandle->readByte();
+ _objectRoomTable[i] = _fileHandle->readByte();
+ _classData[i] = _fileHandle->readUint32LE();
+ }
+ memset(_objectOwnerTable, 0xFF, num);
+
+ // Finally, sort the object name->ID map, so we can later use
+ // bsearch on it. For this we (ab)use strcmp, which works fine
+ // since the table entries start with a string.
+ qsort(_objectIDMap, _objectIDMapSize, sizeof(ObjectNameId),
+ (int (*)(const void*, const void*))strcmp);
+}
+
+void ScummEngine_v7::readGlobalObjects() {
+ int num = _fileHandle->readUint16LE();
+ assert(num == _numGlobalObjects);
+
+ _fileHandle->read(_objectStateTable, num);
+ _fileHandle->read(_objectRoomTable, num);
+ memset(_objectOwnerTable, 0xFF, num);
+
+ _fileHandle->read(_classData, num * sizeof(uint32));
+
+#if defined(SCUMM_BIG_ENDIAN)
+ // Correct the endianess if necessary
+ for (int i = 0; i != num; i++)
+ _classData[i] = FROM_LE_32(_classData[i]);
+#endif
+}
+#endif
+
+void ScummEngine::allocateArrays() {
+ // Note: Buffers are now allocated in scummMain to allow for
+ // early GUI init.
+
+ _objectOwnerTable = (byte *)calloc(_numGlobalObjects, 1);
+ _objectStateTable = (byte *)calloc(_numGlobalObjects, 1);
+ _classData = (uint32 *)calloc(_numGlobalObjects, sizeof(uint32));
+ _newNames = (uint16 *)calloc(_numNewNames, sizeof(uint16));
+
+ _inventory = (uint16 *)calloc(_numInventory, sizeof(uint16));
+ _verbs = (VerbSlot *)calloc(_numVerbs, sizeof(VerbSlot));
+ _objs = (ObjectData *)calloc(_numLocalObjects, sizeof(ObjectData));
+ _roomVars = (int32 *)calloc(_numRoomVariables, sizeof(int32));
+ _scummVars = (int32 *)calloc(_numVariables, sizeof(int32));
+ _bitVars = (byte *)calloc(_numBitVariables >> 3, 1);
+ if (_heversion >= 60) {
+ _arraySlot = (byte *)calloc(_numArray, 1);
+ }
+ if (_heversion >= 70) {
+ _storedFlObjects = (ObjectData *)calloc(100, sizeof(ObjectData));
+ }
+
+ allocResTypeData(rtCostume, (_features & GF_NEW_COSTUMES) ? MKID('AKOS') : MKID('COST'),
+ _numCostumes, "costume", 1);
+ allocResTypeData(rtRoom, MKID('ROOM'), _numRooms, "room", 1);
+ allocResTypeData(rtRoomImage, MKID('RMIM'), _numRooms, "room image", 1);
+ allocResTypeData(rtRoomScripts, MKID('RMSC'), _numRooms, "room script", 1);
+ allocResTypeData(rtSound, MKID('SOUN'), _numSounds, "sound", 2);
+ allocResTypeData(rtScript, MKID('SCRP'), _numScripts, "script", 1);
+ allocResTypeData(rtCharset, MKID('CHAR'), _numCharsets, "charset", 1);
+ allocResTypeData(rtObjectName, MKID('NONE'), _numNewNames, "new name", 0);
+ allocResTypeData(rtInventory, MKID('NONE'), _numInventory, "inventory", 0);
+ allocResTypeData(rtTemp, MKID('NONE'), 10, "temp", 0);
+ allocResTypeData(rtScaleTable, MKID('NONE'), 5, "scale table", 0);
+ allocResTypeData(rtActorName, MKID('NONE'), _numActors, "actor name", 0);
+ allocResTypeData(rtVerb, MKID('NONE'), _numVerbs, "verb", 0);
+ allocResTypeData(rtString, MKID('NONE'), _numArray, "array", 0);
+ allocResTypeData(rtFlObject, MKID('NONE'), _numFlObject, "flobject", 0);
+ allocResTypeData(rtMatrix, MKID('NONE'), 10, "boxes", 0);
+ allocResTypeData(rtImage, MKID('AWIZ'), _numImages, "images", 1);
+ allocResTypeData(rtTalkie, MKID('TLKE'), _numTalkies, "talkie", 1);
+
+ if (_heversion >= 70) {
+ allocResTypeData(rtSpoolBuffer, MKID('NONE'), 9, "spool buffer", 0);
+ }
+}
+
+void ScummEngine::dumpResource(const char *tag, int idx, const byte *ptr, int length) {
+ char buf[256];
+ Common::File out;
+
+ uint32 size;
+ if (length >= 0)
+ size = length;
+ else if (_features & GF_OLD_BUNDLE)
+ size = READ_LE_UINT16(ptr);
+ else if (_features & GF_SMALL_HEADER)
+ size = READ_LE_UINT32(ptr);
+ else
+ size = READ_BE_UINT32(ptr + 4);
+
+#if defined(MACOS_CARBON)
+ sprintf(buf, ":dumps:%s%d.dmp", tag, idx);
+#else
+ sprintf(buf, "dumps/%s%d.dmp", tag, idx);
+#endif
+
+ out.open(buf, Common::File::kFileWriteMode);
+ if (out.isOpen() == false)
+ return;
+ out.write(ptr, size);
+ out.close();
+}
+
+ResourceIterator::ResourceIterator(const byte *searchin, bool smallHeader)
+ : _ptr(searchin), _smallHeader(smallHeader) {
+ assert(searchin);
+ if (_smallHeader) {
+ _size = READ_LE_UINT32(searchin);
+ _pos = 6;
+ _ptr = searchin + 6;
+ } else {
+ _size = READ_BE_UINT32(searchin + 4);
+ _pos = 8;
+ _ptr = searchin + 8;
+ }
+
+}
+
+const byte *ResourceIterator::findNext(uint32 tag) {
+ uint32 size = 0;
+ const byte *result = 0;
+
+ if (_smallHeader) {
+ uint16 smallTag = newTag2Old(tag);
+ do {
+ if (_pos >= _size)
+ return 0;
+
+ result = _ptr;
+ size = READ_LE_UINT32(result);
+ if ((int32)size <= 0)
+ return 0; // Avoid endless loop
+
+ _pos += size;
+ _ptr += size;
+ } while (READ_LE_UINT16(result + 4) != smallTag);
+ } else {
+ do {
+ if (_pos >= _size)
+ return 0;
+
+ result = _ptr;
+ size = READ_BE_UINT32(result + 4);
+ if ((int32)size <= 0)
+ return 0; // Avoid endless loop
+
+ _pos += size;
+ _ptr += size;
+ } while (READ_UINT32(result) != tag);
+ }
+
+ return result;
+}
+
+const byte *ScummEngine::findResource(uint32 tag, const byte *searchin) {
+ uint32 curpos, totalsize, size;
+
+ debugC(DEBUG_RESOURCE, "findResource(%s, %lx)", tag2str(tag), searchin);
+
+ if (!searchin) {
+ if (_heversion >= 70) {
+ searchin = _resourceLastSearchBuf;
+ totalsize = _resourceLastSearchSize;
+ curpos = 0;
+ } else {
+ assert(searchin);
+ return NULL;
+ }
+ } else {
+ searchin += 4;
+ _resourceLastSearchSize = totalsize = READ_BE_UINT32(searchin);
+ curpos = 8;
+ searchin += 4;
+ }
+
+ while (curpos < totalsize) {
+ if (READ_UINT32(searchin) == tag) {
+ _resourceLastSearchBuf = searchin;
+ return searchin;
+ }
+
+ size = READ_BE_UINT32(searchin + 4);
+ if ((int32)size <= 0) {
+ error("(%s) Not found in %d... illegal block len %d", tag2str(tag), 0, size);
+ return NULL;
+ }
+
+ curpos += size;
+ searchin += size;
+ }
+
+ return NULL;
+}
+
+const byte *findResourceSmall(uint32 tag, const byte *searchin) {
+ uint32 curpos, totalsize, size;
+ uint16 smallTag;
+
+ smallTag = newTag2Old(tag);
+ if (smallTag == 0)
+ return NULL;
+
+ assert(searchin);
+
+ totalsize = READ_LE_UINT32(searchin);
+ searchin += 6;
+ curpos = 6;
+
+ while (curpos < totalsize) {
+ size = READ_LE_UINT32(searchin);
+
+ if (READ_LE_UINT16(searchin + 4) == smallTag)
+ return searchin;
+
+ if ((int32)size <= 0) {
+ error("(%s) Not found in %d... illegal block len %d", tag2str(tag), 0, size);
+ return NULL;
+ }
+
+ curpos += size;
+ searchin += size;
+ }
+
+ return NULL;
+}
+
+uint16 newTag2Old(uint32 newTag) {
+ switch (newTag) {
+ case (MKID('RMHD')):
+ return (0x4448); // HD
+ case (MKID('IM00')):
+ return (0x4D42); // BM
+ case (MKID('EXCD')):
+ return (0x5845); // EX
+ case (MKID('ENCD')):
+ return (0x4E45); // EN
+ case (MKID('SCAL')):
+ return (0x4153); // SA
+ case (MKID('LSCR')):
+ return (0x534C); // LS
+ case (MKID('OBCD')):
+ return (0x434F); // OC
+ case (MKID('OBIM')):
+ return (0x494F); // OI
+ case (MKID('SMAP')):
+ return (0x4D42); // BM
+ case (MKID('CLUT')):
+ return (0x4150); // PA
+ case (MKID('BOXD')):
+ return (0x5842); // BX
+ case (MKID('CYCL')):
+ return (0x4343); // CC
+ case (MKID('EPAL')):
+ return (0x5053); // SP
+ default:
+ return (0);
+ }
+}
+
+const char *resTypeFromId(int id) {
+ static char buf[100];
+
+ switch (id) {
+ case rtRoom:
+ return "Room";
+ case rtScript:
+ return "Script";
+ case rtCostume:
+ return "Costume";
+ case rtSound:
+ return "Sound";
+ case rtInventory:
+ return "Inventory";
+ case rtCharset:
+ return "Charset";
+ case rtString:
+ return "String";
+ case rtVerb:
+ return "Verb";
+ case rtActorName:
+ return "ActorName";
+ case rtBuffer:
+ return "Buffer";
+ case rtScaleTable:
+ return "ScaleTable";
+ case rtTemp:
+ return "Temp";
+ case rtFlObject:
+ return "FlObject";
+ case rtMatrix:
+ return "Matrix";
+ case rtBox:
+ return "Box";
+ case rtObjectName:
+ return "ObjectName";
+ case rtRoomScripts:
+ return "RoomScripts";
+ case rtRoomImage:
+ return "RoomImage";
+ case rtImage:
+ return "Image";
+ case rtTalkie:
+ return "Talkie";
+ case rtSpoolBuffer:
+ return "SpoolBuffer";
+ case rtNumTypes:
+ return "NumTypes";
+ default:
+ sprintf(buf, "%d", id);
+ return buf;
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/resource.h b/engines/scumm/resource.h
new file mode 100644
index 0000000000..318582b5cb
--- /dev/null
+++ b/engines/scumm/resource.h
@@ -0,0 +1,46 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef RESOURCE_H
+#define RESOURCE_H
+
+namespace Scumm {
+
+enum {
+ OF_OWNER_MASK = 0x0F,
+ OF_STATE_MASK = 0xF0,
+
+ OF_STATE_SHL = 4
+};
+
+class ResourceIterator {
+ uint32 _size;
+ uint32 _pos;
+ const byte *_ptr;
+ bool _smallHeader;
+public:
+ ResourceIterator(const byte *searchin, bool smallHeader);
+ const byte *findNext(uint32 tag);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/resource_v2.cpp b/engines/scumm/resource_v2.cpp
new file mode 100644
index 0000000000..26e96ec9ef
--- /dev/null
+++ b/engines/scumm/resource_v2.cpp
@@ -0,0 +1,208 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/scumm.h"
+#include "scumm/intern.h"
+#include "scumm/resource.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+void ScummEngine_v2::readClassicIndexFile() {
+ int i;
+
+ if (_gameId == GID_MANIAC) {
+ if (_platform == Common::kPlatformC64) {
+ _numGlobalObjects = 256;
+ _numRooms = 55;
+ _numCostumes = 25;
+ _numScripts = 160;
+ _numSounds = 70;
+ } else if (_platform == Common::kPlatformNES) {
+ _numGlobalObjects = 775;
+ _numRooms = 55;
+
+ // costumes 25-36 are special. see v1MMNEScostTables[] in costume.cpp
+ // costumes 37-76 are room graphics resources
+ // costume 77 is a character set translation table
+ // costume 78 is a preposition list
+ // costume 79 is unused but allocated, so the total is a nice even number :)
+ _numCostumes = 80;
+ _numScripts = 200;
+ _numSounds = 100;
+ } else {
+ _numGlobalObjects = 800;
+ _numRooms = 55;
+ _numCostumes = 35;
+ _numScripts = 200;
+ _numSounds = 100;
+ }
+ } else if (_gameId == GID_ZAK) {
+ if (_platform == Common::kPlatformC64) {
+ _numGlobalObjects = 775;
+ _numRooms = 59;
+ _numCostumes = 38;
+ _numScripts = 155;
+ _numSounds = 127;
+ } else {
+ _numGlobalObjects = 775;
+ _numRooms = 61;
+ _numCostumes = 37;
+ _numScripts = 155;
+ _numSounds = 120;
+ }
+ }
+
+ _fileHandle->seek(0, SEEK_SET);
+
+ readMAXS(0);
+ allocateArrays();
+
+ _fileHandle->readUint16LE(); /* version magic number */
+ for (i = 0; i != _numGlobalObjects; i++) {
+ byte tmp = _fileHandle->readByte();
+ _objectOwnerTable[i] = tmp & OF_OWNER_MASK;
+ _objectStateTable[i] = tmp >> OF_STATE_SHL;
+ }
+
+ for (i = 0; i < _numRooms; i++) {
+ res.roomno[rtRoom][i] = i;
+ }
+ _fileHandle->seek(_numRooms, SEEK_CUR);
+ for (i = 0; i < _numRooms; i++) {
+ res.roomoffs[rtRoom][i] = _fileHandle->readUint16LE();
+ if (res.roomoffs[rtRoom][i] == 0xFFFF)
+ res.roomoffs[rtRoom][i] = 0xFFFFFFFF;
+ }
+
+ for (i = 0; i < _numCostumes; i++) {
+ res.roomno[rtCostume][i] = _fileHandle->readByte();
+ }
+ for (i = 0; i < _numCostumes; i++) {
+ res.roomoffs[rtCostume][i] = _fileHandle->readUint16LE();
+ if (res.roomoffs[rtCostume][i] == 0xFFFF)
+ res.roomoffs[rtCostume][i] = 0xFFFFFFFF;
+ }
+
+ for (i = 0; i < _numScripts; i++) {
+ res.roomno[rtScript][i] = _fileHandle->readByte();
+ }
+ for (i = 0; i < _numScripts; i++) {
+ res.roomoffs[rtScript][i] = _fileHandle->readUint16LE();
+ if (res.roomoffs[rtScript][i] == 0xFFFF)
+ res.roomoffs[rtScript][i] = 0xFFFFFFFF;
+ }
+
+ for (i = 0; i < _numSounds; i++) {
+ res.roomno[rtSound][i] = _fileHandle->readByte();
+ }
+ for (i = 0; i < _numSounds; i++) {
+ res.roomoffs[rtSound][i] = _fileHandle->readUint16LE();
+ if (res.roomoffs[rtSound][i] == 0xFFFF)
+ res.roomoffs[rtSound][i] = 0xFFFFFFFF;
+ }
+}
+
+void ScummEngine_v2::readEnhancedIndexFile() {
+
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _fileHandle->seek(_numGlobalObjects, SEEK_CUR);
+ _numRooms = _fileHandle->readByte();
+ _fileHandle->seek(_numRooms * 3, SEEK_CUR);
+ _numCostumes = _fileHandle->readByte();
+ _fileHandle->seek(_numCostumes * 3, SEEK_CUR);
+ _numScripts = _fileHandle->readByte();
+ _fileHandle->seek(_numScripts * 3, SEEK_CUR);
+ _numSounds = _fileHandle->readByte();
+
+ _fileHandle->clearIOFailed();
+ _fileHandle->seek(0, SEEK_SET);
+
+ readMAXS(0);
+ allocateArrays();
+
+ _fileHandle->readUint16LE(); /* version magic number */
+ readGlobalObjects();
+ readResTypeList(rtRoom, MKID('ROOM'), "room");
+ readResTypeList(rtCostume, MKID('COST'), "costume");
+ readResTypeList(rtScript, MKID('SCRP'), "script");
+ readResTypeList(rtSound, MKID('SOUN'), "sound");
+}
+
+void ScummEngine_v2::readGlobalObjects() {
+ int i;
+ int num = _fileHandle->readUint16LE();
+ assert(num == _numGlobalObjects);
+
+ for (i = 0; i != num; i++) {
+ byte tmp = _fileHandle->readByte();
+ _objectOwnerTable[i] = tmp & OF_OWNER_MASK;
+ _objectStateTable[i] = tmp >> OF_STATE_SHL;
+ }
+}
+
+void ScummEngine_v2::readIndexFile() {
+ int magic = 0;
+ debug(9, "readIndexFile()");
+
+ closeRoom();
+ openRoom(0);
+
+ magic = _fileHandle->readUint16LE();
+
+ switch (magic) {
+ case 0x0100:
+ printf("Enhanced V2 game detected\n");
+ readEnhancedIndexFile();
+ break;
+ case 0x0A31:
+ printf("Classic V1 game detected\n");
+ _version = 1;
+ readClassicIndexFile();
+ break;
+ case 0x4643:
+ if (!(_platform == Common::kPlatformNES))
+ error("Use maniac target");
+ printf("NES V1 game detected\n");
+ _version = 1;
+ readClassicIndexFile();
+ break;
+ case 0x132:
+ printf("C64 V1 game detected\n");
+ _version = 1;
+ readClassicIndexFile();
+ break;
+ default:
+ error("Unknown magic id (0x%X) - this version is unsupported", magic);
+ break;
+ }
+
+ closeRoom();
+}
+
+void ScummEngine_v2::loadCharset(int num) {
+ // Stub, V2 font resources are hardcoded into the engine.
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/resource_v3.cpp b/engines/scumm/resource_v3.cpp
new file mode 100644
index 0000000000..510f106550
--- /dev/null
+++ b/engines/scumm/resource_v3.cpp
@@ -0,0 +1,121 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/intern.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+extern const char *resTypeFromId(int id);
+
+void ScummEngine_v3old::readResTypeList(int id, uint32 tag, const char *name) {
+ int num;
+ int i;
+
+ debug(9, "readResTypeList(%s,%s,%s)", resTypeFromId(id), tag2str(TO_BE_32(tag)), name);
+
+ num = _fileHandle->readByte();
+
+ if (num >= 0xFF) {
+ error("Too many %ss (%d) in directory", name, num);
+ }
+
+ if (id == rtRoom) {
+ for (i = 0; i < num; i++)
+ res.roomno[id][i] = i;
+ _fileHandle->seek(num, SEEK_CUR);
+ } else {
+ for (i = 0; i < num; i++)
+ res.roomno[id][i] = _fileHandle->readByte();
+ }
+ for (i = 0; i < num; i++) {
+ res.roomoffs[id][i] = _fileHandle->readUint16LE();
+ if (res.roomoffs[id][i] == 0xFFFF)
+ res.roomoffs[id][i] = 0xFFFFFFFF;
+ }
+}
+
+void ScummEngine_v3old::readIndexFile() {
+ int magic = 0;
+ debug(9, "readIndexFile()");
+
+ closeRoom();
+ openRoom(0);
+
+ magic = _fileHandle->readUint16LE();
+ if (magic != 0x0100)
+ error("The magic id doesn't match (0x%X)", magic);
+
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _fileHandle->seek(_numGlobalObjects * 4, SEEK_CUR);
+ _numRooms = _fileHandle->readByte();
+ _fileHandle->seek(_numRooms * 3, SEEK_CUR);
+ _numCostumes = _fileHandle->readByte();
+ _fileHandle->seek(_numCostumes * 3, SEEK_CUR);
+ _numScripts = _fileHandle->readByte();
+ _fileHandle->seek(_numScripts * 3, SEEK_CUR);
+ _numSounds = _fileHandle->readByte();
+
+ _fileHandle->clearIOFailed();
+ _fileHandle->seek(0, SEEK_SET);
+
+ readMAXS(0);
+ allocateArrays();
+
+ _fileHandle->readUint16LE(); /* version magic number */
+ readGlobalObjects();
+ readResTypeList(rtRoom, MKID('ROOM'), "room");
+ readResTypeList(rtCostume, MKID('COST'), "costume");
+ readResTypeList(rtScript, MKID('SCRP'), "script");
+ readResTypeList(rtSound, MKID('SOUN'), "sound");
+
+ closeRoom();
+}
+
+void ScummEngine_v3::readRoomsOffsets() {
+}
+
+void ScummEngine_v3::loadCharset(int no) {
+ uint32 size;
+ memset(_charsetData, 0, sizeof(_charsetData));
+
+ checkRange(2, 0, no, "Loading illegal charset %d");
+ closeRoom();
+
+ Common::File file;
+ char buf[20];
+
+ sprintf(buf, "%02d.LFL", 99 - no);
+ file.open(buf);
+
+ if (file.isOpen() == false) {
+ error("loadCharset(%d): Missing file charset: %s", no, buf);
+ }
+
+ size = file.readUint16LE();
+ file.read(res.createResource(rtCharset, no, size), size);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/resource_v4.cpp b/engines/scumm/resource_v4.cpp
new file mode 100644
index 0000000000..0f8530af2d
--- /dev/null
+++ b/engines/scumm/resource_v4.cpp
@@ -0,0 +1,193 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/intern.h"
+#include "scumm/resource.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+void ScummEngine_v4::readIndexFile() {
+ uint16 blocktype;
+ uint32 itemsize;
+ int numblock = 0;
+
+ debug(9, "readIndexFile()");
+
+ closeRoom();
+ openRoom(0);
+
+ while (!_fileHandle->eof()) {
+ // Figure out the sizes of various resources
+ itemsize = _fileHandle->readUint32LE();
+ blocktype = _fileHandle->readUint16LE();
+ if (_fileHandle->ioFailed())
+ break;
+
+ switch (blocktype) {
+ case 0x4E52: // 'NR'
+ _fileHandle->readUint16LE();
+ break;
+ case 0x5230: // 'R0'
+ _numRooms = _fileHandle->readUint16LE();
+ break;
+ case 0x5330: // 'S0'
+ _numScripts = _fileHandle->readUint16LE();
+ break;
+ case 0x4E30: // 'N0'
+ _numSounds = _fileHandle->readUint16LE();
+ break;
+ case 0x4330: // 'C0'
+ _numCostumes = _fileHandle->readUint16LE();
+ break;
+ case 0x4F30: // 'O0'
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ break;
+ }
+ _fileHandle->seek(itemsize - 8, SEEK_CUR);
+ }
+
+ _fileHandle->clearIOFailed();
+ _fileHandle->seek(0, SEEK_SET);
+
+ readMAXS(0);
+ allocateArrays();
+
+ while (1) {
+ itemsize = _fileHandle->readUint32LE();
+
+ if (_fileHandle->ioFailed())
+ break;
+
+ blocktype = _fileHandle->readUint16LE();
+
+ numblock++;
+
+ switch (blocktype) {
+
+ case 0x4E52: // 'NR'
+ // Names of rooms. Maybe we should put them into a table, for use by the debugger?
+ for (int room; (room = _fileHandle->readByte()); ) {
+ char buf[10];
+ _fileHandle->read(buf, 9);
+ buf[9] = 0;
+ for (int i = 0; i < 9; i++)
+ buf[i] ^= 0xFF;
+ debug(5, "Room %d: '%s'", room, buf);
+ }
+ break;
+
+ case 0x5230: // 'R0'
+ readResTypeList(rtRoom, MKID('ROOM'), "room");
+ break;
+
+ case 0x5330: // 'S0'
+ readResTypeList(rtScript, MKID('SCRP'), "script");
+ break;
+
+ case 0x4E30: // 'N0'
+ readResTypeList(rtSound, MKID('SOUN'), "sound");
+ break;
+
+ case 0x4330: // 'C0'
+ readResTypeList(rtCostume, MKID('COST'), "costume");
+ break;
+
+ case 0x4F30: // 'O0'
+ readGlobalObjects();
+ break;
+
+ default:
+ // FIXME: this is a little hack because Indy3 FM-TOWNS has
+ // 32 extra bytes of unknown meaning appended to 00.LFL
+ if (!(_gameId == GID_INDY3 && _platform == Common::kPlatformFMTowns))
+ error("Bad ID %c%c found in directory!", blocktype & 0xFF, blocktype >> 8);
+ return;
+ }
+ }
+ closeRoom();
+}
+
+void ScummEngine_v4::loadCharset(int no) {
+ uint32 size;
+ memset(_charsetData, 0, sizeof(_charsetData));
+
+ checkRange(4, 0, no, "Loading illegal charset %d");
+ closeRoom();
+
+ Common::File file;
+ char buf[20];
+
+ sprintf(buf, "%03d.LFL", 900 + no);
+ file.open(buf);
+
+ if (file.isOpen() == false) {
+ error("loadCharset(%d): Missing file charset: %s", no, buf);
+ }
+
+ size = file.readUint32LE() + 11;
+ file.read(res.createResource(rtCharset, no, size), size);
+}
+
+void ScummEngine_v4::readMAXS(int blockSize) {
+ // FIXME - I'm not sure for those values yet, they will have to be rechecked
+
+ _numVariables = 800; // 800
+ _numBitVariables = 4096; // 2048
+ _numLocalObjects = 200; // 200
+ _numArray = 50;
+ _numVerbs = 100;
+ _numNewNames = 50;
+ _objectRoomTable = NULL;
+ _numCharsets = 9; // 9
+ _numInventory = 80; // 80
+ _numGlobalScripts = 200;
+ _numFlObject = 50;
+
+ _shadowPaletteSize = 256;
+
+ _shadowPalette = (byte *) calloc(_shadowPaletteSize, 1); // FIXME - needs to be removed later
+}
+
+void ScummEngine_v4::readGlobalObjects() {
+ int i;
+ int num = _fileHandle->readUint16LE();
+ assert(num == _numGlobalObjects);
+
+ uint32 bits;
+ byte tmp;
+ for (i = 0; i != num; i++) {
+ bits = _fileHandle->readByte();
+ bits |= _fileHandle->readByte() << 8;
+ bits |= _fileHandle->readByte() << 16;
+ _classData[i] = bits;
+ tmp = _fileHandle->readByte();
+ _objectOwnerTable[i] = tmp & OF_OWNER_MASK;
+ _objectStateTable[i] = tmp >> OF_STATE_SHL;
+ }
+}
+
+
+} // End of namespace Scumm
diff --git a/engines/scumm/resource_v7he.cpp b/engines/scumm/resource_v7he.cpp
new file mode 100644
index 0000000000..6ce8446a9a
--- /dev/null
+++ b/engines/scumm/resource_v7he.cpp
@@ -0,0 +1,1912 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * Parts of code heavily based on:
+ * icoutils - A set of programs dealing with MS Windows icons and cursors.
+ * Copyright (C) 1998-2001 Oskar Liljeblad
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/resource_v7he.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "sound/wave.h"
+
+#include "common/stream.h"
+#include "common/system.h"
+
+namespace Scumm {
+
+ResExtractor::ResExtractor(ScummEngine_v70he *scumm)
+ : _vm(scumm) {
+
+ _fileName[0] = 0;
+ memset(_cursorCache, 0, sizeof(_cursorCache));
+}
+
+ResExtractor::~ResExtractor() {
+ for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
+ CachedCursor *cc = &_cursorCache[i];
+ if (cc->valid) {
+ free(cc->bitmap);
+ free(cc->palette);
+ }
+ }
+ memset(_cursorCache, 0, sizeof(_cursorCache));
+}
+
+ResExtractor::CachedCursor *ResExtractor::findCachedCursor(int id) {
+ for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
+ CachedCursor *cc = &_cursorCache[i];
+ if (cc->valid && cc->id == id) {
+ return cc;
+ }
+ }
+ return NULL;
+}
+
+ResExtractor::CachedCursor *ResExtractor::getCachedCursorSlot() {
+ uint32 min_last_used = 0;
+ CachedCursor *r = NULL;
+ for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
+ CachedCursor *cc = &_cursorCache[i];
+ if (!cc->valid) {
+ return cc;
+ } else {
+ if (min_last_used == 0 || cc->last_used < min_last_used) {
+ min_last_used = cc->last_used;
+ r = cc;
+ }
+ }
+ }
+ assert(r);
+ free(r->bitmap);
+ free(r->palette);
+ memset(r, 0, sizeof(CachedCursor));
+ return r;
+}
+
+void ResExtractor::setCursor(int id) {
+ byte *cursorRes = 0;
+ int cursorsize;
+ int keycolor = 0;
+ CachedCursor *cc = findCachedCursor(id);
+ if (cc != NULL) {
+ debug(7, "Found cursor %d in cache slot %d", id, cc - _cursorCache);
+ } else {
+ cc = getCachedCursorSlot();
+ assert(cc && !cc->valid);
+ cursorsize = extractResource(id, &cursorRes);
+ convertIcons(cursorRes, cursorsize, &cc->bitmap, &cc->w, &cc->h, &cc->hotspot_x, &cc->hotspot_y, &keycolor, &cc->palette, &cc->palSize);
+ debug(7, "Adding cursor %d to cache slot %d", id, cc - _cursorCache);
+ free(cursorRes);
+ cc->valid = true;
+ cc->id = id;
+ cc->last_used = g_system->getMillis();
+ }
+
+ if (_vm->_system->hasFeature(OSystem::kFeatureCursorHasPalette) && cc->palette)
+ _vm->_system->setCursorPalette(cc->palette, 0, cc->palSize);
+
+ _vm->setCursorHotspot(cc->hotspot_x, cc->hotspot_y);
+ _vm->setCursorFromBuffer(cc->bitmap, cc->w, cc->h, cc->w);
+}
+
+
+/*
+ * Static variables
+ */
+const char *res_types[] = {
+ /* 0x01: */
+ "cursor", "bitmap", "icon", "menu", "dialog", "string",
+ "fontdir", "font", "accelerator", "rcdata", "messagelist",
+ "group_cursor", NULL, "group_icon", NULL,
+ /* the following are not defined in winbase.h, but found in wrc. */
+ /* 0x10: */
+ "version", "dlginclude", NULL, "plugplay", "vxd",
+ "anicursor", "aniicon"
+};
+#define RES_TYPE_COUNT (sizeof(res_types)/sizeof(char *))
+
+Win32ResExtractor::Win32ResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) {
+}
+
+int Win32ResExtractor::extractResource(int resId, byte **data) {
+ char buf[20];
+
+ snprintf(buf, sizeof(buf), "%d", resId);
+
+ return extractResource_("group_cursor", buf, data);
+}
+
+int Win32ResExtractor::extractResource_(const char *resType, char *resName, byte **data) {
+ char *arg_language = NULL;
+ const char *arg_type = resType;
+ char *arg_name = resName;
+ int arg_action = ACTION_LIST;
+ int ressize = 0;
+
+ _arg_raw = false;
+
+ /* translate --type option from resource type string to integer */
+ arg_type = res_type_string_to_id(arg_type);
+
+ WinLibrary fi;
+
+ /* initiate stuff */
+ fi.memory = NULL;
+ fi.file = new Common::File;
+
+ if (!_fileName[0]) { // We are running for the first time
+ snprintf(_fileName, 256, "%s.he3", _vm->getBaseName());
+
+ if (_vm->_substResFileNameIndex > 0) {
+ char buf1[128];
+
+ _vm->generateSubstResFileName(_fileName, buf1, sizeof(buf1));
+ strcpy(_fileName, buf1);
+ }
+ }
+
+
+ /* get file size */
+ fi.file->open(_fileName);
+ if (!fi.file->isOpen()) {
+ error("Cannot open file %s", _fileName);
+ }
+
+ fi.total_size = fi.file->size();
+ if (fi.total_size == -1) {
+ error("Cannot get size of file %s", fi.file->name());
+ goto cleanup;
+ }
+ if (fi.total_size == 0) {
+ error("%s: file has a size of 0", fi.file->name());
+ goto cleanup;
+ }
+
+ /* read all of file */
+ fi.memory = (byte *)malloc(fi.total_size);
+ if (fi.file->read(fi.memory, fi.total_size) == 0) {
+ error("Cannot read from file %s", fi.file->name());
+ goto cleanup;
+ }
+
+ /* identify file and find resource table */
+ if (!read_library(&fi)) {
+ /* error reported by read_library */
+ goto cleanup;
+ }
+
+ // verbose_printf("file is a %s\n",
+ // fi.is_PE_binary ? "Windows NT `PE' binary" : "Windows 3.1 `NE' binary");
+
+ /* errors will be printed by the callback */
+ ressize = do_resources(&fi, arg_type, arg_name, arg_language, arg_action, data);
+
+ /* free stuff and close file */
+ cleanup:
+ if (fi.file != NULL)
+ fi.file->close();
+ if (fi.memory != NULL)
+ free(fi.memory);
+
+ return ressize;
+}
+
+
+/* res_type_id_to_string:
+ * Translate a numeric resource type to it's corresponding string type.
+ * (For informative-ness.)
+ */
+const char *Win32ResExtractor::res_type_id_to_string(int id) {
+ if (id == 241)
+ return "toolbar";
+ if (id > 0 && id <= (int)RES_TYPE_COUNT)
+ return res_types[id-1];
+ return NULL;
+}
+
+/* res_type_string_to_id:
+ * Translate a resource type string to integer.
+ * (Used to convert the --type option.)
+ */
+const char *Win32ResExtractor::res_type_string_to_id(const char *type) {
+ static const char *res_type_ids[] = {
+ "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9", "-10",
+ "-11", "-12", NULL, "-14", NULL, "-16", "-17", NULL, "-19",
+ "-20", "-21", "-22"
+ };
+ int c;
+
+ if (type == NULL)
+ return NULL;
+
+ for (c = 0 ; c < (int)RES_TYPE_COUNT ; c++) {
+ if (res_types[c] != NULL && !scumm_stricmp(type, res_types[c]))
+ return res_type_ids[c];
+ }
+
+ return type;
+}
+
+int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr,
+ WinResource *type_wr, WinResource *name_wr,
+ WinResource *lang_wr, byte **data) {
+ int size;
+ bool free_it;
+ const char *type;
+ int32 id;
+
+ if (*data) {
+ error("Win32ResExtractor::extract_resources() more than one cursor");
+ return 0;
+ }
+
+ *data = extract_resource(fi, wr, &size, &free_it, type_wr->id, (lang_wr == NULL ? NULL : lang_wr->id), _arg_raw);
+
+ if (data == NULL) {
+ error("Win32ResExtractor::extract_resources() problem with resource extraction");
+ return 0;
+ }
+
+ /* get named resource type if possible */
+ type = NULL;
+ if ((id = strtol(type_wr->id, 0, 10)) != 0)
+ type = res_type_id_to_string(id);
+
+ debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s%s%s [size=%d]",
+ get_resource_id_quoted(name_wr),
+ (lang_wr->id[0] != '\0' ? " language: " : ""),
+ get_resource_id_quoted(lang_wr), size);
+
+ return size;
+}
+
+/* extract_resource:
+ * Extract a resource, returning pointer to data.
+ */
+byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int *size,
+ bool *free_it, char *type, char *lang, bool raw) {
+ char *str;
+ int32 intval;
+
+ /* just return pointer to data if raw */
+ if (raw) {
+ *free_it = false;
+ /* get_resource_entry will print possible error */
+ return get_resource_entry(fi, wr, size);
+ }
+
+ /* find out how to extract */
+ str = type;
+ if (str != NULL && (intval = strtol(STRIP_RES_ID_FORMAT(str), 0, 10))) {
+ if (intval == (int)RT_GROUP_ICON) {
+ *free_it = true;
+ return extract_group_icon_cursor_resource(fi, wr, lang, size, true);
+ }
+ if (intval == (int)RT_GROUP_CURSOR) {
+ *free_it = true;
+ return extract_group_icon_cursor_resource(fi, wr, lang, size, false);
+ }
+ }
+
+ return NULL;
+}
+
+/* extract_group_icon_resource:
+ * Create a complete RT_GROUP_ICON resource, that can be written to
+ * an `.ico' file without modifications. Returns an allocated
+ * memory block that should be freed with free() once used.
+ *
+ * `root' is the offset in file that specifies the resource.
+ * `base' is the offset that string pointers are calculated from.
+ * `ressize' should point to an integer variable where the size of
+ * the returned memory block will be placed.
+ * `is_icon' indicates whether resource to be extracted is icon
+ * or cursor group.
+ */
+byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinResource *wr, char *lang,
+ int *ressize, bool is_icon) {
+ Win32CursorIconDir *icondir;
+ Win32CursorIconFileDir *fileicondir;
+ byte *memory;
+ int c, offset, skipped;
+ int size;
+
+ /* get resource data and size */
+ icondir = (Win32CursorIconDir *)get_resource_entry(fi, wr, &size);
+ if (icondir == NULL) {
+ /* get_resource_entry will print error */
+ return NULL;
+ }
+
+ /* calculate total size of output file */
+ RETURN_IF_BAD_POINTER(NULL, icondir->count);
+ skipped = 0;
+ for (c = 0 ; c < icondir->count ; c++) {
+ int level;
+ int iconsize;
+ char name[14];
+ WinResource *fwr;
+
+ RETURN_IF_BAD_POINTER(NULL, icondir->entries[c]);
+ /*printf("%d. bytes_in_res=%d width=%d height=%d planes=%d bit_count=%d\n", c,
+ icondir->entries[c].bytes_in_res,
+ (is_icon ? icondir->entries[c].res_info.icon.width : icondir->entries[c].res_info.cursor.width),
+ (is_icon ? icondir->entries[c].res_info.icon.height : icondir->entries[c].res_info.cursor.height),
+ icondir->entries[c].plane_count,
+ icondir->entries[c].bit_count);*/
+
+ /* find the corresponding icon resource */
+ snprintf(name, sizeof(name)/sizeof(char), "-%d", icondir->entries[c].res_id);
+ fwr = find_resource(fi, (is_icon ? "-3" : "-1"), name, lang, &level);
+ if (fwr == NULL) {
+ error("%s: could not find `%s' in `%s' resource.",
+ fi->file->name(), &name[1], (is_icon ? "group_icon" : "group_cursor"));
+ return NULL;
+ }
+
+ if (get_resource_entry(fi, fwr, &iconsize) != NULL) {
+ if (iconsize == 0) {
+ debugC(DEBUG_RESOURCE, "%s: icon resource `%s' is empty, skipping", fi->file->name(), name);
+ skipped++;
+ continue;
+ }
+ if ((uint32)iconsize != icondir->entries[c].bytes_in_res) {
+ debugC(DEBUG_RESOURCE, "%s: mismatch of size in icon resource `%s' and group (%d != %d)",
+ fi->file->name(), name, iconsize, icondir->entries[c].bytes_in_res);
+ }
+ size += iconsize; /* size += icondir->entries[c].bytes_in_res; */
+
+ /* cursor resources have two additional WORDs that contain
+ * hotspot info */
+ if (!is_icon)
+ size -= sizeof(uint16)*2;
+ }
+ }
+ offset = sizeof(Win32CursorIconFileDir) + (icondir->count-skipped) * sizeof(Win32CursorIconFileDirEntry);
+ size += offset;
+ *ressize = size;
+
+ /* allocate that much memory */
+ memory = (byte *)malloc(size);
+ fileicondir = (Win32CursorIconFileDir *)memory;
+
+ /* transfer Win32CursorIconDir structure members */
+ fileicondir->reserved = icondir->reserved;
+ fileicondir->type = icondir->type;
+ fileicondir->count = icondir->count - skipped;
+
+ /* transfer each cursor/icon: Win32CursorIconDirEntry and data */
+ skipped = 0;
+ for (c = 0 ; c < icondir->count ; c++) {
+ int level;
+ char name[14];
+ WinResource *fwr;
+ byte *data;
+
+ /* find the corresponding icon resource */
+ snprintf(name, sizeof(name)/sizeof(char), "-%d", icondir->entries[c].res_id);
+ fwr = find_resource(fi, (is_icon ? "-3" : "-1"), name, lang, &level);
+ if (fwr == NULL) {
+ error("%s: could not find `%s' in `%s' resource.",
+ fi->file->name(), &name[1], (is_icon ? "group_icon" : "group_cursor"));
+ return NULL;
+ }
+
+ /* get data and size of that resource */
+ data = (byte *)get_resource_entry(fi, fwr, &size);
+ if (data == NULL) {
+ /* get_resource_entry has printed error */
+ return NULL;
+ }
+ if (size == 0) {
+ skipped++;
+ continue;
+ }
+
+ /* copy ICONDIRENTRY (not including last dwImageOffset) */
+ memcpy(&fileicondir->entries[c-skipped], &icondir->entries[c],
+ sizeof(Win32CursorIconFileDirEntry)-sizeof(uint32));
+
+ /* special treatment for cursors */
+ if (!is_icon) {
+ fileicondir->entries[c-skipped].width = icondir->entries[c].res_info.cursor.width;
+ fileicondir->entries[c-skipped].height = icondir->entries[c].res_info.cursor.height / 2;
+ fileicondir->entries[c-skipped].color_count = 0;
+ fileicondir->entries[c-skipped].reserved = 0;
+ }
+
+ /* set image offset and increase it */
+ fileicondir->entries[c-skipped].dib_offset = offset;
+
+ /* transfer resource into file memory */
+ if (is_icon) {
+ memcpy(&memory[offset], data, icondir->entries[c].bytes_in_res);
+ } else {
+ fileicondir->entries[c-skipped].hotspot_x = ((uint16 *) data)[0];
+ fileicondir->entries[c-skipped].hotspot_y = ((uint16 *) data)[1];
+ memcpy(&memory[offset], data+sizeof(uint16)*2,
+ icondir->entries[c].bytes_in_res-sizeof(uint16)*2);
+ offset -= sizeof(uint16)*2;
+ }
+
+ /* increase the offset pointer */
+ offset += icondir->entries[c].bytes_in_res;
+ }
+
+ return memory;
+}
+
+/* check_offset:
+ * Check if a chunk of data (determined by offset and size)
+ * is within the bounds of the WinLibrary file.
+ * Usually not called directly.
+ */
+bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *name, void *offset, int size) {
+ int need_size = (int)((byte *)offset - memory + size);
+
+ debugC(DEBUG_RESOURCE, "check_offset: size=%x vs %x offset=%x size=%x",
+ need_size, total_size, (byte *)offset - memory, size);
+
+ if (need_size < 0 || need_size > total_size) {
+ error("%s: premature end", name);
+ return false;
+ }
+
+ return true;
+}
+
+
+/* do_resources:
+ * Do something for each resource matching type, name and lang.
+ */
+int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name, char *lang, int action, byte **data) {
+ WinResource *type_wr;
+ WinResource *name_wr;
+ WinResource *lang_wr;
+ int size;
+
+ type_wr = (WinResource *)calloc(sizeof(WinResource)*3, 1);
+ name_wr = type_wr + 1;
+ lang_wr = type_wr + 2;
+
+ size = do_resources_recurs(fi, NULL, type_wr, name_wr, lang_wr, type, name, lang, action, data);
+
+ free(type_wr);
+
+ return size;
+}
+
+/* what is each entry in this directory level for? type, name or language? */
+#define WINRESOURCE_BY_LEVEL(x) ((x)==0 ? type_wr : ((x)==1 ? name_wr : lang_wr))
+
+/* does the id of this entry match the specified id? */
+#define LEVEL_MATCHES(x) (x == NULL || x ## _wr->id[0] == '\0' || compare_resource_id(x ## _wr, x))
+
+int Win32ResExtractor::do_resources_recurs(WinLibrary *fi, WinResource *base,
+ WinResource *type_wr, WinResource *name_wr, WinResource *lang_wr,
+ const char *type, char *name, char *lang, int action, byte **data) {
+ int c, rescnt;
+ WinResource *wr;
+ uint32 size = 0;
+
+ /* get a list of all resources at this level */
+ wr = list_resources(fi, base, &rescnt);
+ if (wr == NULL)
+ if (size != 0)
+ return size;
+ else
+ return 0;
+
+ /* process each resource listed */
+ for (c = 0 ; c < rescnt ; c++) {
+ /* (over)write the corresponding WinResource holder with the current */
+ memcpy(WINRESOURCE_BY_LEVEL(wr[c].level), wr+c, sizeof(WinResource));
+
+ /* go deeper unless there is something that does NOT match */
+ if (LEVEL_MATCHES(type) && LEVEL_MATCHES(name) && LEVEL_MATCHES(lang)) {
+ if (wr->is_directory)
+ size = do_resources_recurs(fi, wr+c, type_wr, name_wr, lang_wr, type, name, lang, action, data);
+ else
+ size = extract_resources(fi, wr+c, type_wr, name_wr, lang_wr, data);
+ }
+ }
+
+ /* since we're moving back one level after this, unset the
+ * WinResource holder used on this level */
+ memset(WINRESOURCE_BY_LEVEL(wr[0].level), 0, sizeof(WinResource));
+
+ return size;
+}
+
+/* return the resource id quoted if it's a string, otherwise just return it */
+char *Win32ResExtractor::get_resource_id_quoted(WinResource *wr) {
+ static char tmp[WINRES_ID_MAXLEN+2];
+
+ if (wr->numeric_id || wr->id[0] == '\0')
+ return wr->id;
+
+ sprintf(tmp, "'%s'", wr->id);
+ return tmp;
+}
+
+bool Win32ResExtractor::compare_resource_id(WinResource *wr, const char *id) {
+ if (wr->numeric_id) {
+ int32 cmp1, cmp2;
+ if (id[0] == '+')
+ return false;
+ if (id[0] == '-')
+ id++;
+ if (!(cmp1 = strtol(wr->id, 0, 10)) || !(cmp2 = strtol(id, 0, 10)) || cmp1 != cmp2)
+ return false;
+ } else {
+ if (id[0] == '-')
+ return false;
+ if (id[0] == '+')
+ id++;
+ if (strcmp(wr->id, id))
+ return false;
+ }
+
+ return true;
+}
+
+bool Win32ResExtractor::decode_pe_resource_id(WinLibrary *fi, WinResource *wr, uint32 value) {
+ if (value & IMAGE_RESOURCE_NAME_IS_STRING) { /* numeric id */
+ int c, len;
+ uint16 *mem = (uint16 *)
+ (fi->first_resource + (value & ~IMAGE_RESOURCE_NAME_IS_STRING));
+
+ /* copy each char of the string, and terminate it */
+ RETURN_IF_BAD_POINTER(false, *mem);
+ len = mem[0];
+ RETURN_IF_BAD_OFFSET(false, &mem[1], sizeof(uint16) * len);
+
+ len = MIN(mem[0], (uint16)WINRES_ID_MAXLEN);
+ for (c = 0 ; c < len ; c++)
+ wr->id[c] = mem[c+1] & 0x00FF;
+ wr->id[len] = '\0';
+ } else { /* Unicode string id */
+ /* translate id into a string */
+ snprintf(wr->id, WINRES_ID_MAXLEN, "%d", value);
+ }
+
+ wr->numeric_id = (value & IMAGE_RESOURCE_NAME_IS_STRING ? false:true);
+ return true;
+}
+
+byte *Win32ResExtractor::get_resource_entry(WinLibrary *fi, WinResource *wr, int *size) {
+ if (fi->is_PE_binary) {
+ Win32ImageResourceDataEntry *dataent;
+
+ dataent = (Win32ImageResourceDataEntry *) wr->children;
+ RETURN_IF_BAD_POINTER(NULL, *dataent);
+ *size = dataent->size;
+ RETURN_IF_BAD_OFFSET(NULL, fi->memory + dataent->offset_to_data, *size);
+
+ return fi->memory + dataent->offset_to_data;
+ } else {
+ Win16NENameInfo *nameinfo;
+ int sizeshift;
+
+ nameinfo = (Win16NENameInfo *) wr->children;
+ sizeshift = *((uint16 *) fi->first_resource - 1);
+ *size = nameinfo->length << sizeshift;
+ RETURN_IF_BAD_OFFSET(NULL, fi->memory + (nameinfo->offset << sizeshift), *size);
+
+ return fi->memory + (nameinfo->offset << sizeshift);
+ }
+}
+
+bool Win32ResExtractor::decode_ne_resource_id(WinLibrary *fi, WinResource *wr, uint16 value) {
+ if (value & NE_RESOURCE_NAME_IS_NUMERIC) { /* numeric id */
+ /* translate id into a string */
+ snprintf(wr->id, WINRES_ID_MAXLEN, "%d", value & ~NE_RESOURCE_NAME_IS_NUMERIC);
+ } else { /* ASCII string id */
+ int len;
+ char *mem = (char *)NE_HEADER(fi->memory)
+ + NE_HEADER(fi->memory)->rsrctab
+ + value;
+
+ /* copy each char of the string, and terminate it */
+ RETURN_IF_BAD_POINTER(false, *mem);
+ len = mem[0];
+ RETURN_IF_BAD_OFFSET(false, &mem[1], sizeof(char) * len);
+ memcpy(wr->id, &mem[1], len);
+ wr->id[len] = '\0';
+ }
+
+ wr->numeric_id = (value & NE_RESOURCE_NAME_IS_NUMERIC ? true:false);
+ return true;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary *fi, Win32ImageResourceDirectory *pe_res, int level, int *count) {
+ WinResource *wr;
+ int c, rescnt;
+ Win32ImageResourceDirectoryEntry *dirent
+ = (Win32ImageResourceDirectoryEntry *)(pe_res + 1);
+
+ /* count number of `type' resources */
+ RETURN_IF_BAD_POINTER(NULL, *dirent);
+ rescnt = pe_res->number_of_named_entries + pe_res->number_of_id_entries;
+ *count = rescnt;
+
+ /* allocate WinResource's */
+ wr = (WinResource *)malloc(sizeof(WinResource) * rescnt);
+
+ /* fill in the WinResource's */
+ for (c = 0 ; c < rescnt ; c++) {
+ RETURN_IF_BAD_POINTER(NULL, dirent[c]);
+ wr[c].this_ = pe_res;
+ wr[c].level = level;
+ wr[c].is_directory = (dirent[c].u2.s.data_is_directory);
+ wr[c].children = fi->first_resource + dirent[c].u2.s.offset_to_directory;
+
+ /* fill in wr->id, wr->numeric_id */
+ if (!decode_pe_resource_id (fi, wr + c, dirent[c].u1.name))
+ return NULL;
+ }
+
+ return wr;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::list_ne_name_resources(WinLibrary *fi, WinResource *typeres, int *count) {
+ int c, rescnt;
+ WinResource *wr;
+ Win16NETypeInfo *typeinfo = (Win16NETypeInfo *) typeres->this_;
+ Win16NENameInfo *nameinfo = (Win16NENameInfo *) typeres->children;
+
+ /* count number of `type' resources */
+ RETURN_IF_BAD_POINTER(NULL, typeinfo->count);
+ *count = rescnt = typeinfo->count;
+
+ /* allocate WinResource's */
+ wr = (WinResource *)malloc(sizeof(WinResource) * rescnt);
+
+ /* fill in the WinResource's */
+ for (c = 0 ; c < rescnt ; c++) {
+ RETURN_IF_BAD_POINTER(NULL, nameinfo[c]);
+ wr[c].this_ = nameinfo+c;
+ wr[c].is_directory = false;
+ wr[c].children = nameinfo+c;
+ wr[c].level = 1;
+
+ /* fill in wr->id, wr->numeric_id */
+ if (!decode_ne_resource_id(fi, wr + c, (nameinfo+c)->id))
+ return NULL;
+ }
+
+ return wr;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::list_ne_type_resources(WinLibrary *fi, int *count) {
+ int c, rescnt;
+ WinResource *wr;
+ Win16NETypeInfo *typeinfo;
+
+ /* count number of `type' resources */
+ typeinfo = (Win16NETypeInfo *) fi->first_resource;
+ RETURN_IF_BAD_POINTER(NULL, *typeinfo);
+ for (rescnt = 0 ; typeinfo->type_id != 0 ; rescnt++) {
+ typeinfo = NE_TYPEINFO_NEXT(typeinfo);
+ RETURN_IF_BAD_POINTER(NULL, *typeinfo);
+ }
+ *count = rescnt;
+
+ /* allocate WinResource's */
+ wr = (WinResource *)malloc(sizeof(WinResource) * rescnt);
+
+ /* fill in the WinResource's */
+ typeinfo = (Win16NETypeInfo *) fi->first_resource;
+ for (c = 0 ; c < rescnt ; c++) {
+ wr[c].this_ = typeinfo;
+ wr[c].is_directory = (typeinfo->count != 0);
+ wr[c].children = typeinfo+1;
+ wr[c].level = 0;
+
+ /* fill in wr->id, wr->numeric_id */
+ if (!decode_ne_resource_id(fi, wr + c, typeinfo->type_id))
+ return NULL;
+
+ typeinfo = NE_TYPEINFO_NEXT(typeinfo);
+ }
+
+ return wr;
+}
+
+/* list_resources:
+ * Return an array of WinResource's in the current
+ * resource level specified by res.
+ */
+Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi, WinResource *res, int *count) {
+ if (res != NULL && !res->is_directory)
+ return NULL;
+
+ if (fi->is_PE_binary) {
+ return list_pe_resources(fi, (Win32ImageResourceDirectory *)
+ (res == NULL ? fi->first_resource : res->children),
+ (res == NULL ? 0 : res->level+1),
+ count);
+ } else {
+ return (res == NULL
+ ? list_ne_type_resources(fi, count)
+ : list_ne_name_resources(fi, res, count));
+ }
+}
+
+/* read_library:
+ * Read header and get resource directory offset in a Windows library
+ * (AKA module).
+ *
+ */
+bool Win32ResExtractor::read_library(WinLibrary *fi) {
+ /* check for DOS header signature `MZ' */
+ RETURN_IF_BAD_POINTER(false, MZ_HEADER(fi->memory)->magic);
+ if (MZ_HEADER(fi->memory)->magic == IMAGE_DOS_SIGNATURE) {
+ DOSImageHeader *mz_header = MZ_HEADER(fi->memory);
+
+ RETURN_IF_BAD_POINTER(false, mz_header->lfanew);
+ if (mz_header->lfanew < sizeof(DOSImageHeader)) {
+ error("%s: not a Windows library", fi->file->name());
+ return false;
+ }
+ }
+
+ /* check for OS2 (Win16) header signature `NE' */
+ RETURN_IF_BAD_POINTER(false, NE_HEADER(fi->memory)->magic);
+ if (NE_HEADER(fi->memory)->magic == IMAGE_OS2_SIGNATURE) {
+ OS2ImageHeader *header = NE_HEADER(fi->memory);
+
+ RETURN_IF_BAD_POINTER(false, header->rsrctab);
+ RETURN_IF_BAD_POINTER(false, header->restab);
+ if (header->rsrctab >= header->restab) {
+ error("%s: no resource directory found", fi->file->name());
+ return false;
+ }
+
+ fi->is_PE_binary = false;
+ fi->first_resource = (byte *) NE_HEADER(fi->memory)
+ + header->rsrctab + sizeof(uint16);
+ RETURN_IF_BAD_POINTER(false, *(Win16NETypeInfo *) fi->first_resource);
+
+ return true;
+ }
+
+ /* check for NT header signature `PE' */
+ RETURN_IF_BAD_POINTER(false, PE_HEADER(fi->memory)->signature);
+ if (PE_HEADER(fi->memory)->signature == IMAGE_NT_SIGNATURE) {
+ Win32ImageSectionHeader *pe_sec;
+ Win32ImageDataDirectory *dir;
+ Win32ImageNTHeaders *pe_header;
+ int d;
+
+ /* allocate new memory */
+ fi->total_size = calc_vma_size(fi);
+ if (fi->total_size == 0) {
+ /* calc_vma_size has reported error */
+ return false;
+ }
+ fi->memory = (byte *)realloc(fi->memory, fi->total_size);
+
+ /* relocate memory, start from last section */
+ pe_header = PE_HEADER(fi->memory);
+ RETURN_IF_BAD_POINTER(false, pe_header->file_header.number_of_sections);
+
+ /* we don't need to do OFFSET checking for the sections.
+ * calc_vma_size has already done that */
+ for (d = pe_header->file_header.number_of_sections - 1; d >= 0 ; d--) {
+ pe_sec = PE_SECTIONS(fi->memory) + d;
+
+ if (pe_sec->characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
+ continue;
+
+ //if (pe_sec->virtual_address + pe_sec->size_of_raw_data > fi->total_size)
+
+ RETURN_IF_BAD_OFFSET(0, fi->memory + pe_sec->virtual_address, pe_sec->size_of_raw_data);
+ RETURN_IF_BAD_OFFSET(0, fi->memory + pe_sec->pointer_to_raw_data, pe_sec->size_of_raw_data);
+ if (pe_sec->virtual_address != pe_sec->pointer_to_raw_data) {
+ memmove(fi->memory + pe_sec->virtual_address,
+ fi->memory + pe_sec->pointer_to_raw_data,
+ pe_sec->size_of_raw_data);
+ }
+ }
+
+ /* find resource directory */
+ RETURN_IF_BAD_POINTER(false, pe_header->optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
+ dir = pe_header->optional_header.data_directory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
+ if (dir->size == 0) {
+ error("%s: file contains no resources", fi->file->name());
+ return false;
+ }
+
+ fi->first_resource = fi->memory + dir->virtual_address;
+ fi->is_PE_binary = true;
+ return true;
+ }
+
+ /* other (unknown) header signature was found */
+ error("%s: not a Windows library", fi->file->name());
+ return false;
+}
+
+/* calc_vma_size:
+ * Calculate the total amount of memory needed for a 32-bit Windows
+ * module. Returns -1 if file was too small.
+ */
+int Win32ResExtractor::calc_vma_size(WinLibrary *fi) {
+ Win32ImageSectionHeader *seg;
+ int c, segcount, size;
+
+ size = 0;
+ RETURN_IF_BAD_POINTER(-1, PE_HEADER(fi->memory)->file_header.number_of_sections);
+ segcount = PE_HEADER(fi->memory)->file_header.number_of_sections;
+
+ /* If there are no segments, just process file like it is.
+ * This is (probably) not the right thing to do, but problems
+ * will be delt with later anyway.
+ */
+ if (segcount == 0)
+ return fi->total_size;
+
+ seg = PE_SECTIONS(fi->memory);
+ RETURN_IF_BAD_POINTER(-1, *seg);
+ for (c = 0 ; c < segcount ; c++) {
+ RETURN_IF_BAD_POINTER(0, *seg);
+
+ size = MAX((uint32)size, seg->virtual_address + seg->size_of_raw_data);
+ /* I have no idea what misc.virtual_size is for... */
+ size = MAX((uint32)size, seg->virtual_address + seg->misc.virtual_size);
+ seg++;
+ }
+
+ return size;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::find_with_resource_array(WinLibrary *fi, WinResource *wr, const char *id) {
+ int c, rescnt;
+ WinResource *return_wr;
+
+ wr = list_resources(fi, wr, &rescnt);
+ if (wr == NULL)
+ return NULL;
+
+ for (c = 0 ; c < rescnt ; c++) {
+ if (compare_resource_id(&wr[c], id)) {
+ /* duplicate WinResource and return it */
+ return_wr = (WinResource *)malloc(sizeof(WinResource));
+ memcpy(return_wr, &wr[c], sizeof(WinResource));
+
+ /* free old WinResource */
+ free(wr);
+ return return_wr;
+ }
+ }
+
+ return NULL;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::find_resource(WinLibrary *fi, const char *type, const char *name, const char *language, int *level) {
+ WinResource *wr;
+
+ *level = 0;
+ if (type == NULL)
+ return NULL;
+ wr = find_with_resource_array(fi, NULL, type);
+ if (wr == NULL || !wr->is_directory)
+ return wr;
+
+ *level = 1;
+ if (name == NULL)
+ return wr;
+ wr = find_with_resource_array(fi, wr, name);
+ if (wr == NULL || !wr->is_directory)
+ return wr;
+
+ *level = 2;
+ if (language == NULL)
+ return wr;
+ wr = find_with_resource_array(fi, wr, language);
+ return wr;
+}
+
+#define ROW_BYTES(bits) ((((bits) + 31) >> 5) << 2)
+
+
+int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **pal, int *palSize) {
+ Win32CursorIconFileDir dir;
+ Win32CursorIconFileDirEntry *entries = NULL;
+ uint32 offset;
+ uint32 c, d;
+ int completed;
+ int matched = 0;
+ MemoryReadStream *in = new MemoryReadStream(data, datasize);
+
+ if (!in->read(&dir, sizeof(Win32CursorIconFileDir)- sizeof(Win32CursorIconFileDirEntry)))
+ goto cleanup;
+ fix_win32_cursor_icon_file_dir_endian(&dir);
+
+ if (dir.reserved != 0) {
+ error("not an icon or cursor file (reserved non-zero)");
+ goto cleanup;
+ }
+ if (dir.type != 1 && dir.type != 2) {
+ error("not an icon or cursor file (wrong type)");
+ goto cleanup;
+ }
+
+ entries = (Win32CursorIconFileDirEntry *)malloc(dir.count * sizeof(Win32CursorIconFileDirEntry));
+ for (c = 0; c < dir.count; c++) {
+ if (!in->read(&entries[c], sizeof(Win32CursorIconFileDirEntry)))
+ goto cleanup;
+ fix_win32_cursor_icon_file_dir_entry_endian(&entries[c]);
+ if (entries[c].reserved != 0)
+ error("reserved is not zero");
+ }
+
+ offset = sizeof(Win32CursorIconFileDir) + (dir.count - 1) * (sizeof(Win32CursorIconFileDirEntry));
+
+ for (completed = 0; completed < dir.count; ) {
+ uint32 min_offset = 0x7fffffff;
+ int previous = completed;
+
+ for (c = 0; c < dir.count; c++) {
+ if (entries[c].dib_offset == offset) {
+ Win32BitmapInfoHeader bitmap;
+ Win32RGBQuad *palette = NULL;
+ uint32 palette_count = 0;
+ uint32 image_size, mask_size;
+ uint32 width, height;
+ byte *image_data = NULL, *mask_data = NULL;
+ byte *row = NULL;
+
+ if (!in->read(&bitmap, sizeof(Win32BitmapInfoHeader)))
+ goto local_cleanup;
+
+ fix_win32_bitmap_info_header_endian(&bitmap);
+ if (bitmap.size < sizeof(Win32BitmapInfoHeader)) {
+ error("bitmap header is too short");
+ goto local_cleanup;
+ }
+ if (bitmap.compression != 0) {
+ error("compressed image data not supported");
+ goto local_cleanup;
+ }
+ if (bitmap.x_pels_per_meter != 0)
+ error("x_pels_per_meter field in bitmap should be zero");
+ if (bitmap.y_pels_per_meter != 0)
+ error("y_pels_per_meter field in bitmap should be zero");
+ if (bitmap.clr_important != 0)
+ error("clr_important field in bitmap should be zero");
+ if (bitmap.planes != 1)
+ error("planes field in bitmap should be one");
+ if (bitmap.size != sizeof(Win32BitmapInfoHeader)) {
+ uint32 skip = bitmap.size - sizeof(Win32BitmapInfoHeader);
+ error("skipping %d bytes of extended bitmap header", skip);
+ in->seek(skip, SEEK_CUR);
+ }
+ offset += bitmap.size;
+
+ if (bitmap.clr_used != 0 || bitmap.bit_count < 24) {
+ palette_count = (bitmap.clr_used != 0 ? bitmap.clr_used : 1 << bitmap.bit_count);
+ palette = (Win32RGBQuad *)malloc(sizeof(Win32RGBQuad) * palette_count);
+ if (!in->read(palette, sizeof(Win32RGBQuad) * palette_count))
+ goto local_cleanup;
+ offset += sizeof(Win32RGBQuad) * palette_count;
+ }
+
+ width = bitmap.width;
+ height = ABS(bitmap.height)/2;
+
+ image_size = height * ROW_BYTES(width * bitmap.bit_count);
+ mask_size = height * ROW_BYTES(width);
+
+ if (entries[c].dib_size != bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
+ debugC(DEBUG_RESOURCE, "incorrect total size of bitmap (%d specified; %d real)",
+ entries[c].dib_size,
+ bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)
+ );
+
+ image_data = (byte *)malloc(image_size);
+ if (!in->read(image_data, image_size))
+ goto local_cleanup;
+
+ mask_data = (byte *)malloc(mask_size);
+ if (!in->read(mask_data, mask_size))
+ goto local_cleanup;
+
+ offset += image_size;
+ offset += mask_size;
+ completed++;
+ matched++;
+
+ *hotspot_x = entries[c].hotspot_x;
+ *hotspot_y = entries[c].hotspot_y;
+ *w = width;
+ *h = height;
+ *keycolor = 0;
+ *cursor = (byte *)malloc(width * height);
+
+ row = (byte *)malloc(width * 4);
+
+ for (d = 0; d < height; d++) {
+ uint32 x;
+ uint32 y = (bitmap.height < 0 ? d : height - d - 1);
+ uint32 imod = y * (image_size / height) * 8 / bitmap.bit_count;
+ //uint32 mmod = y * (mask_size / height) * 8;
+
+ for (x = 0; x < width; x++) {
+
+ uint32 color = simple_vec(image_data, x + imod, bitmap.bit_count);
+
+ // FIXME?: This works only with b/w cursors and white index may be
+ // different. But now it's enough.
+ if (color) {
+ cursor[0][width * d + x] = 15; // white in SCUMM
+ } else {
+ cursor[0][width * d + x] = 255; // transparent
+ }
+ /*
+
+ if (bitmap.bit_count <= 16) {
+ if (color >= palette_count) {
+ error("color out of range in image data");
+ goto local_cleanup;
+ }
+ row[4*x+0] = palette[color].red;
+ row[4*x+1] = palette[color].green;
+ row[4*x+2] = palette[color].blue;
+
+ } else {
+ row[4*x+0] = (color >> 16) & 0xFF;
+ row[4*x+1] = (color >> 8) & 0xFF;
+ row[4*x+2] = (color >> 0) & 0xFF;
+ }
+ if (bitmap.bit_count == 32)
+ row[4*x+3] = (color >> 24) & 0xFF;
+ else
+ row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF;
+ */
+ }
+
+ }
+
+ if (row != NULL)
+ free(row);
+ if (palette != NULL)
+ free(palette);
+ if (image_data != NULL) {
+ free(image_data);
+ free(mask_data);
+ }
+ continue;
+
+ local_cleanup:
+
+ if (row != NULL)
+ free(row);
+ if (palette != NULL)
+ free(palette);
+ if (image_data != NULL) {
+ free(image_data);
+ free(mask_data);
+ }
+ goto cleanup;
+ } else {
+ if (entries[c].dib_offset > offset)
+ min_offset = MIN(min_offset, entries[c].dib_offset);
+ }
+ }
+
+ if (previous == completed) {
+ if (min_offset < offset) {
+ error("offset of bitmap header incorrect (too low)");
+ goto cleanup;
+ }
+ debugC(DEBUG_RESOURCE, "skipping %d bytes of garbage at %d", min_offset-offset, offset);
+ in->seek(min_offset - offset, SEEK_CUR);
+ offset = min_offset;
+ }
+ }
+
+ free(entries);
+ return matched;
+
+cleanup:
+
+ free(entries);
+ return -1;
+}
+
+uint32 Win32ResExtractor::simple_vec(byte *data, uint32 ofs, byte size) {
+ switch (size) {
+ case 1:
+ return (data[ofs/8] >> (7 - ofs%8)) & 1;
+ case 2:
+ return (data[ofs/4] >> ((3 - ofs%4) << 1)) & 3;
+ case 4:
+ return (data[ofs/2] >> ((1 - ofs%2) << 2)) & 15;
+ case 8:
+ return data[ofs];
+ case 16:
+ return data[2*ofs] | data[2*ofs+1] << 8;
+ case 24:
+ return data[3*ofs] | data[3*ofs+1] << 8 | data[3*ofs+2] << 16;
+ case 32:
+ return data[4*ofs] | data[4*ofs+1] << 8 | data[4*ofs+2] << 16 | data[4*ofs+3] << 24;
+ }
+
+ return 0;
+}
+
+#define LE16(x) ((x) = TO_LE_16(x))
+#define LE32(x) ((x) = TO_LE_32(x))
+
+void Win32ResExtractor::fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj) {
+ LE16(obj->reserved);
+ LE16(obj->type);
+ LE16(obj->count);
+}
+
+void Win32ResExtractor::fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj) {
+ LE32(obj->size);
+ LE32(obj->width);
+ LE32(obj->height);
+ LE16(obj->planes);
+ LE16(obj->bit_count);
+ LE32(obj->compression);
+ LE32(obj->size_image);
+ LE32(obj->x_pels_per_meter);
+ LE32(obj->y_pels_per_meter);
+ LE32(obj->clr_used);
+ LE32(obj->clr_important);
+}
+
+void Win32ResExtractor::fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj) {
+ LE16(obj->hotspot_x);
+ LE16(obj->hotspot_y);
+ LE32(obj->dib_size);
+ LE32(obj->dib_offset);
+}
+
+void Win32ResExtractor::fix_win32_image_section_header(Win32ImageSectionHeader *obj) {
+ LE32(obj->misc.physical_address);
+ LE32(obj->virtual_address);
+ LE32(obj->size_of_raw_data);
+ LE32(obj->pointer_to_raw_data);
+ LE32(obj->pointer_to_relocations);
+ LE32(obj->pointer_to_linenumbers);
+ LE16(obj->number_of_relocations);
+ LE16(obj->number_of_linenumbers);
+ LE32(obj->characteristics);
+}
+
+void Win32ResExtractor::fix_os2_image_header_endian(OS2ImageHeader *obj) {
+ LE16(obj->magic);
+ LE16(obj->enttab);
+ LE16(obj->cbenttab);
+ LE32(obj->crc);
+ LE16(obj->flags);
+ LE16(obj->autodata);
+ LE16(obj->heap);
+ LE16(obj->stack);
+ LE32(obj->csip);
+ LE32(obj->sssp);
+ LE16(obj->cseg);
+ LE16(obj->cmod);
+ LE16(obj->cbnrestab);
+ LE16(obj->segtab);
+ LE16(obj->rsrctab);
+ LE16(obj->restab);
+ LE16(obj->modtab);
+ LE16(obj->imptab);
+ LE32(obj->nrestab);
+ LE16(obj->cmovent);
+ LE16(obj->align);
+ LE16(obj->cres);
+ LE16(obj->fastload_offset);
+ LE16(obj->fastload_length);
+ LE16(obj->swaparea);
+ LE16(obj->expver);
+}
+
+/* fix_win32_image_header_endian:
+ * NOTE: This assumes that the optional header is always available.
+ */
+void Win32ResExtractor::fix_win32_image_header_endian(Win32ImageNTHeaders *obj) {
+ LE32(obj->signature);
+ LE16(obj->file_header.machine);
+ LE16(obj->file_header.number_of_sections);
+ LE32(obj->file_header.time_date_stamp);
+ LE32(obj->file_header.pointer_to_symbol_table);
+ LE32(obj->file_header.number_of_symbols);
+ LE16(obj->file_header.size_of_optional_header);
+ LE16(obj->file_header.characteristics);
+ LE16(obj->optional_header.magic);
+ LE32(obj->optional_header.size_of_code);
+ LE32(obj->optional_header.size_of_initialized_data);
+ LE32(obj->optional_header.size_of_uninitialized_data);
+ LE32(obj->optional_header.address_of_entry_point);
+ LE32(obj->optional_header.base_of_code);
+ LE32(obj->optional_header.base_of_data);
+ LE32(obj->optional_header.image_base);
+ LE32(obj->optional_header.section_alignment);
+ LE32(obj->optional_header.file_alignment);
+ LE16(obj->optional_header.major_operating_system_version);
+ LE16(obj->optional_header.minor_operating_system_version);
+ LE16(obj->optional_header.major_image_version);
+ LE16(obj->optional_header.minor_image_version);
+ LE16(obj->optional_header.major_subsystem_version);
+ LE16(obj->optional_header.minor_subsystem_version);
+ LE32(obj->optional_header.win32_version_value);
+ LE32(obj->optional_header.size_of_image);
+ LE32(obj->optional_header.size_of_headers);
+ LE32(obj->optional_header.checksum);
+ LE16(obj->optional_header.subsystem);
+ LE16(obj->optional_header.dll_characteristics);
+ LE32(obj->optional_header.size_of_stack_reserve);
+ LE32(obj->optional_header.size_of_stack_commit);
+ LE32(obj->optional_header.size_of_heap_reserve);
+ LE32(obj->optional_header.size_of_heap_commit);
+ LE32(obj->optional_header.loader_flags);
+ LE32(obj->optional_header.number_of_rva_and_sizes);
+}
+
+void Win32ResExtractor::fix_win32_image_data_directory(Win32ImageDataDirectory *obj) {
+ LE32(obj->virtual_address);
+ LE32(obj->size);
+}
+
+
+MacResExtractor::MacResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) {
+ _resOffset = -1;
+}
+
+int MacResExtractor::extractResource(int id, byte **buf) {
+ Common::File in;
+ int size;
+
+ if (!_fileName[0]) // We are running for the first time
+ if (_vm->_substResFileNameIndex > 0) {
+ char buf1[128];
+
+ snprintf(buf1, 128, "%s.he3", _vm->getBaseName());
+ _vm->generateSubstResFileName(buf1, _fileName, sizeof(buf1));
+
+ // Some programs write it as .bin. Try that too
+ if (!in.exists(_fileName)) {
+ strcpy(buf1, _fileName);
+ snprintf(_fileName, 128, "%s.bin", buf1);
+
+ if (!in.exists(_fileName)) {
+ // And finally check if we have dumped resource fork
+ snprintf(_fileName, 128, "%s.rsrc", buf1);
+ if (!in.exists(_fileName)) {
+ error("Cannot open file any of files '%s', '%s.bin', '%s.rsrc",
+ buf1, buf1, buf1);
+ }
+ }
+ }
+ }
+
+ in.open(_fileName);
+ if (!in.isOpen()) {
+ error("Cannot open file %s", _fileName);
+ }
+
+ // we haven't calculated it
+ if (_resOffset == -1) {
+ if (!init(in))
+ error("Resource fork is missing in file '%s'", _fileName);
+ in.close();
+ in.open(_fileName);
+ }
+
+ *buf = getResource(in, "crsr", 1000 + id, &size);
+
+ in.close();
+
+ if (*buf == NULL)
+ error("There is no cursor ID #%d", 1000 + id);
+
+ return size;
+}
+
+#define MBI_INFOHDR 128
+#define MBI_ZERO1 0
+#define MBI_NAMELEN 1
+#define MBI_ZERO2 74
+#define MBI_ZERO3 82
+#define MBI_DFLEN 83
+#define MBI_RFLEN 87
+#define MAXNAMELEN 63
+
+bool MacResExtractor::init(Common::File in) {
+ byte infoHeader[MBI_INFOHDR];
+ int32 data_size, rsrc_size;
+ int32 data_size_pad, rsrc_size_pad;
+ int filelen;
+
+ filelen = in.size();
+ in.read(infoHeader, MBI_INFOHDR);
+
+ // Maybe we have MacBinary?
+ if (infoHeader[MBI_ZERO1] == 0 && infoHeader[MBI_ZERO2] == 0 &&
+ infoHeader[MBI_ZERO3] == 0 && infoHeader[MBI_NAMELEN] <= MAXNAMELEN) {
+
+ // Pull out fork lengths
+ data_size = READ_BE_UINT32(infoHeader + MBI_DFLEN);
+ rsrc_size = READ_BE_UINT32(infoHeader + MBI_RFLEN);
+
+ data_size_pad = (((data_size + 127) >> 7) << 7);
+ rsrc_size_pad = (((rsrc_size + 127) >> 7) << 7);
+
+ // Length check
+ int sumlen = MBI_INFOHDR + data_size_pad + rsrc_size_pad;
+
+ if (sumlen == filelen)
+ _resOffset = MBI_INFOHDR + data_size_pad;
+ }
+
+ if (_resOffset == -1) // MacBinary check is failed
+ _resOffset = 0; // Maybe we have dumped fork?
+
+ in.seek(_resOffset);
+
+ _dataOffset = in.readUint32BE() + _resOffset;
+ _mapOffset = in.readUint32BE() + _resOffset;
+ _dataLength = in.readUint32BE();
+ _mapLength = in.readUint32BE();
+
+ // do sanity check
+ if (_dataOffset >= filelen || _mapOffset >= filelen ||
+ _dataLength + _mapLength > filelen) {
+ _resOffset = -1;
+ return false;
+ }
+
+ debug(7, "got header: data %d [%d] map %d [%d]",
+ _dataOffset, _dataLength, _mapOffset, _mapLength);
+
+ readMap(in);
+
+ return true;
+}
+
+byte *MacResExtractor::getResource(Common::File in, const char *typeID, int16 resID, int *size) {
+ int i;
+ int typeNum = -1;
+ int resNum = -1;
+ byte *buf;
+ int len;
+
+ for (i = 0; i < _resMap.numTypes; i++)
+ if (strcmp(_resTypes[i].id, typeID) == 0) {
+ typeNum = i;
+ break;
+ }
+
+ if (typeNum == -1)
+ return NULL;
+
+ for (i = 0; i < _resTypes[typeNum].items; i++)
+ if (_resLists[typeNum][i].id == resID) {
+ resNum = i;
+ break;
+ }
+
+ if (resNum == -1)
+ return NULL;
+
+ in.seek(_dataOffset + _resLists[typeNum][resNum].dataOffset);
+
+ len = in.readUint32BE();
+ buf = (byte *)malloc(len);
+
+ in.read(buf, len);
+
+ *size = len;
+
+ return buf;
+}
+
+void MacResExtractor::readMap(Common::File in) {
+ int i, j, len;
+
+ in.seek(_mapOffset + 22);
+
+ _resMap.resAttr = in.readUint16BE();
+ _resMap.typeOffset = in.readUint16BE();
+ _resMap.nameOffset = in.readUint16BE();
+ _resMap.numTypes = in.readUint16BE();
+ _resMap.numTypes++;
+
+ in.seek(_mapOffset + _resMap.typeOffset + 2);
+ _resTypes = new ResType[_resMap.numTypes];
+
+ for (i = 0; i < _resMap.numTypes; i++) {
+ in.read(_resTypes[i].id, 4);
+ _resTypes[i].id[4] = 0;
+ _resTypes[i].items = in.readUint16BE();
+ _resTypes[i].offset = in.readUint16BE();
+ _resTypes[i].items++;
+ }
+
+ _resLists = new ResPtr[_resMap.numTypes];
+
+ for (i = 0; i < _resMap.numTypes; i++) {
+ _resLists[i] = new Resource[_resTypes[i].items];
+ in.seek(_resTypes[i].offset + _mapOffset + _resMap.typeOffset);
+
+ for (j = 0; j < _resTypes[i].items; j++) {
+ ResPtr resPtr = _resLists[i] + j;
+
+ resPtr->id = in.readUint16BE();
+ resPtr->nameOffset = in.readUint16BE();
+ resPtr->dataOffset = in.readUint32BE();
+ in.readUint32BE();
+ resPtr->name = 0;
+
+ resPtr->attr = resPtr->dataOffset >> 24;
+ resPtr->dataOffset &= 0xFFFFFF;
+ }
+
+ for (j = 0; j < _resTypes[i].items; j++) {
+ if (_resLists[i][j].nameOffset != -1) {
+ in.seek(_resLists[i][j].nameOffset + _mapOffset + _resMap.nameOffset);
+
+ len = in.readByte();
+ _resLists[i][j].name = new byte[len + 1];
+ _resLists[i][j].name[len] = 0;
+ in.read(_resLists[i][j].name, len);
+ }
+ }
+ }
+}
+
+int MacResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize) {
+ Common::MemoryReadStream dis(data, datasize);
+ int i, b;
+ byte imageByte;
+ byte *iconData;
+ int numBytes;
+ int pixelsPerByte, bpp;
+ int ctSize;
+ byte bitmask;
+ int iconRowBytes, iconBounds[4];
+ int ignored;
+ int iconDataSize;
+
+ dis.readUint16BE(); // type
+ dis.readUint32BE(); // offset to pixel map
+ dis.readUint32BE(); // offset to pixel data
+ dis.readUint32BE(); // expanded cursor data
+ dis.readUint16BE(); // expanded data depth
+ dis.readUint32BE(); // reserved
+
+ // Grab B/W icon data
+ *cursor = (byte *)malloc(16 * 16);
+ for (i = 0; i < 32; i++) {
+ imageByte = dis.readByte();
+ for (b = 0; b < 8; b++)
+ cursor[0][i*8+b] = (byte)((imageByte &
+ (0x80 >> b)) > 0? 0x0F: 0x00);
+ }
+
+ // Apply mask data
+ for (i = 0; i < 32; i++) {
+ imageByte = dis.readByte();
+ for (b = 0; b < 8; b++)
+ if ((imageByte & (0x80 >> b)) == 0)
+ cursor[0][i*8+b] = 0xff;
+ }
+
+ *hotspot_y = dis.readUint16BE();
+ *hotspot_x = dis.readUint16BE();
+ *w = *h = 16;
+
+ // Use b/w cursor on backends which don't support cursor palettes
+ if (!_vm->_system->hasFeature(OSystem::kFeatureCursorHasPalette))
+ return 1;
+
+ dis.readUint32BE(); // reserved
+ dis.readUint32BE(); // cursorID
+
+ // Color version of cursor
+ dis.readUint32BE(); // baseAddr
+
+ // Keep only lowbyte for now
+ dis.readByte();
+ iconRowBytes = dis.readByte();
+
+ if (!iconRowBytes)
+ return 1;
+
+ iconBounds[0] = dis.readUint16BE();
+ iconBounds[1] = dis.readUint16BE();
+ iconBounds[2] = dis.readUint16BE();
+ iconBounds[3] = dis.readUint16BE();
+
+ dis.readUint16BE(); // pmVersion
+ dis.readUint16BE(); // packType
+ dis.readUint32BE(); // packSize
+
+ dis.readUint32BE(); // hRes
+ dis.readUint32BE(); // vRes
+
+ dis.readUint16BE(); // pixelType
+ dis.readUint16BE(); // pixelSize
+ dis.readUint16BE(); // cmpCount
+ dis.readUint16BE(); // cmpSize
+
+ dis.readUint32BE(); // planeByte
+ dis.readUint32BE(); // pmTable
+ dis.readUint32BE(); // reserved
+
+ // Pixel data for cursor
+ iconDataSize = iconRowBytes * (iconBounds[3] - iconBounds[1]);
+ iconData = (byte *)malloc(iconDataSize);
+ dis.read(iconData, iconDataSize);
+
+ // Color table
+ dis.readUint32BE(); // ctSeed
+ dis.readUint16BE(); // ctFlag
+ ctSize = dis.readUint16BE() + 1;
+
+ *palette = (byte *)malloc(ctSize * 4);
+
+ // Read just high byte of 16-bit color
+ for (int c = 0; c < ctSize; c++) {
+ // We just use indices 0..ctSize, so ignore color ID
+ dis.readUint16BE(); // colorID[c]
+
+ palette[0][c * 4 + 0] = dis.readByte();
+ ignored = dis.readByte();
+
+ palette[0][c * 4 + 1] = dis.readByte();
+ ignored = dis.readByte();
+
+ palette[0][c * 4 + 2] = dis.readByte();
+ ignored = dis.readByte();
+
+ palette[0][c * 4 + 3] = 0;
+ }
+
+ *palSize = ctSize;
+
+ numBytes =
+ (iconBounds[2] - iconBounds[0]) *
+ (iconBounds[3] - iconBounds[1]);
+
+ pixelsPerByte = (iconBounds[2] - iconBounds[0]) / iconRowBytes;
+ bpp = 8 / pixelsPerByte;
+
+ // build a mask to make sure the pixels are properly shifted out
+ bitmask = 0;
+ for (int m = 0; m < bpp; m++) {
+ bitmask <<= 1;
+ bitmask |= 1;
+ }
+
+ // Extract pixels from bytes
+ for (int j = 0; j < iconDataSize; j++)
+ for (b = 0; b < pixelsPerByte; b++) {
+ int idx = j * pixelsPerByte + (pixelsPerByte - 1 - b);
+
+ if (cursor[0][idx] != 0xff) // if mask is not there
+ cursor[0][idx] = (byte)((iconData[j] >> (b * bpp)) & bitmask);
+ }
+
+ free(iconData);
+
+ assert(datasize - dis.pos() == 0);
+
+ return 1;
+}
+
+
+
+void ScummEngine_v70he::readRoomsOffsets() {
+ int num, i;
+ byte *ptr;
+
+ debug(9, "readRoomOffsets()");
+
+ num = READ_LE_UINT16(_heV7RoomOffsets);
+ ptr = _heV7RoomOffsets + 2;
+ for (i = 0; i < num; i++) {
+ res.roomoffs[rtRoom][i] = READ_LE_UINT32(ptr);
+ ptr += 4;
+ }
+}
+
+void ScummEngine_v70he::readGlobalObjects() {
+ int num = _fileHandle->readUint16LE();
+ assert(num == _numGlobalObjects);
+
+ _fileHandle->read(_objectStateTable, num);
+ _fileHandle->read(_objectOwnerTable, num);
+ _fileHandle->read(_objectRoomTable, num);
+
+ _fileHandle->read(_classData, num * sizeof(uint32));
+
+#if defined(SCUMM_BIG_ENDIAN)
+ // Correct the endianess if necessary
+ for (int i = 0; i != num; i++)
+ _classData[i] = FROM_LE_32(_classData[i]);
+#endif
+}
+
+void ScummEngine_v99he::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v99he readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numRoomVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numImages = _fileHandle->readUint16LE();
+ _numSprites = _fileHandle->readUint16LE();
+ _numLocalScripts = _fileHandle->readUint16LE();
+ _HEHeapSize = _fileHandle->readUint16LE();
+ _numPalettes = _fileHandle->readUint16LE();
+ _numUnk = _fileHandle->readUint16LE();
+ _numTalkies = _fileHandle->readUint16LE();
+ _numNewNames = 10;
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ _numGlobalScripts = 2048;
+}
+
+void ScummEngine_v90he::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v90he readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numRoomVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numImages = _fileHandle->readUint16LE();
+ _numSprites = _fileHandle->readUint16LE();
+ _numLocalScripts = _fileHandle->readUint16LE();
+ _HEHeapSize = _fileHandle->readUint16LE();
+ _numNewNames = 10;
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ if (_features & GF_HE_985)
+ _numGlobalScripts = 2048;
+ else
+ _numGlobalScripts = 200;
+}
+
+void ScummEngine_v72he::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v72he readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numBitVariables = _numRoomVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numVerbs = _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numImages = _fileHandle->readUint16LE();
+ _numNewNames = 10;
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ _numGlobalScripts = 200;
+}
+
+byte *ScummEngine_v72he::getStringAddress(int i) {
+ byte *addr = getResourceAddress(rtString, i);
+ if (addr == NULL)
+ return NULL;
+ return ((ScummEngine_v72he::ArrayHeader *)addr)->data;
+}
+
+int ScummEngine_v72he::getSoundResourceSize(int id) {
+ const byte *ptr;
+ int offs, size;
+
+ if (id > _numSounds) {
+ if (!_sound->getHEMusicDetails(id, offs, size)) {
+ debug(0, "getSoundResourceSize: musicID %d not found", id);
+ return 0;
+ }
+ } else {
+ ptr = getResourceAddress(rtSound, id);
+ if (!ptr)
+ return 0;
+
+ if (READ_UINT32(ptr) == MKID('RIFF')) {
+ byte flags;
+ int rate;
+
+ size = READ_BE_UINT32(ptr + 4);
+ Common::MemoryReadStream stream(ptr, size);
+
+ if (!loadWAVFromStream(stream, size, rate, flags)) {
+ error("getSoundResourceSize: Not a valid WAV file");
+ }
+ } else {
+ ptr += 8 + READ_BE_UINT32(ptr + 12);
+ if (READ_UINT32(ptr) == MKID('SBNG')) {
+ ptr += READ_BE_UINT32(ptr + 4);
+ }
+
+ assert(READ_UINT32(ptr) == MKID('SDAT'));
+ size = READ_BE_UINT32(ptr + 4) - 8;
+ }
+ }
+
+ return size;
+}
+
+void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
+ debug(0, "createSound: snd1id %d snd2id %d", snd1id, snd2id);
+
+ byte *snd1Ptr, *snd2Ptr;
+ byte *sbng1Ptr, *sbng2Ptr;
+ byte *sdat1Ptr, *sdat2Ptr;
+ byte *src, *dst, *tmp;
+ int len, offs, size;
+ int sdat1size, sdat2size;
+
+ if (snd2id == -1) {
+ _sndPtrOffs = 0;
+ _sndTmrOffs = 0;
+ return;
+ }
+
+ if (snd1id != _curSndId) {
+ _curSndId = snd1id;
+ _sndPtrOffs = 0;
+ _sndTmrOffs = 0;
+ }
+
+ snd1Ptr = getResourceAddress(rtSound, snd1id);
+ assert(snd1Ptr);
+ snd2Ptr = getResourceAddress(rtSound, snd2id);
+ assert(snd2Ptr);
+
+ int i;
+ int chan = -1;
+ for (i = 0; i < ARRAYSIZE(_sound->_heChannel); i++) {
+ if (_sound->_heChannel[i].sound == snd1id)
+ chan = i;
+ }
+
+ sbng1Ptr = heFindResource(MKID('SBNG'), snd1Ptr);
+ sbng2Ptr = heFindResource(MKID('SBNG'), snd2Ptr);
+
+ if (sbng1Ptr != NULL && sbng2Ptr != NULL) {
+ if (chan != -1 && _sound->_heChannel[chan].codeOffs > 0) {
+ int curOffs = _sound->_heChannel[chan].codeOffs;
+
+ src = snd1Ptr + curOffs;
+ dst = sbng1Ptr + 8;
+ size = READ_BE_UINT32(sbng1Ptr + 4);
+ len = sbng1Ptr - snd1Ptr + size - curOffs;
+
+ byte *data = (byte *)malloc(len);
+ memcpy(data, src, len);
+ memcpy(dst, data, len);
+ free(data);
+
+ dst = sbng1Ptr + 8;
+ while ((size = READ_LE_UINT16(dst)) != 0)
+ dst += size;
+ } else {
+ dst = sbng1Ptr + 8;
+ }
+
+ _sound->_heChannel[chan].codeOffs = sbng1Ptr - snd1Ptr + 8;
+
+ tmp = sbng2Ptr + 8;
+ while ((offs = READ_LE_UINT16(tmp)) != 0) {
+ tmp += offs;
+ }
+
+ src = sbng2Ptr + 8;
+ len = tmp - sbng2Ptr - 6;
+ memcpy(dst, src, len);
+
+ int32 time;
+ while ((size = READ_LE_UINT16(dst)) != 0) {
+ time = READ_LE_UINT32(dst + 2);
+ time += _sndTmrOffs;
+ WRITE_LE_UINT32(dst + 2, time);
+ dst += size;
+ }
+ }
+
+ sdat1Ptr = heFindResource(MKID('SDAT'), snd1Ptr);
+ assert(sdat1Ptr);
+ sdat2Ptr = heFindResource(MKID('SDAT'), snd2Ptr);
+ assert(sdat2Ptr);
+
+ sdat1size = READ_BE_UINT32(sdat1Ptr + 4) - 8 - _sndPtrOffs;
+ sdat2size = READ_BE_UINT32(sdat2Ptr + 4) - 8;
+
+ debug(0, "SDAT size1 %d size2 %d", sdat1size, sdat2size);
+ if (sdat2size < sdat1size) {
+ src = sdat2Ptr + 8;
+ dst = sdat1Ptr + 8 + _sndPtrOffs;
+ len = sdat2size;
+
+ memcpy(dst, src, len);
+
+ _sndPtrOffs += sdat2size;
+ _sndTmrOffs += sdat2size;
+ } else {
+ src = sdat2Ptr + 8;
+ dst = sdat1Ptr + 8 + _sndPtrOffs;
+ len = sdat1size;
+
+ memcpy(dst, src, len);
+
+ if (sdat2size != sdat1size) {
+ src = sdat2Ptr + 8 + sdat1size;
+ dst = sdat1Ptr + 8;
+ len = sdat2size - sdat1size;
+
+ memcpy(dst, src, len);
+ }
+
+ _sndPtrOffs = sdat2size - sdat1size;
+ _sndTmrOffs += sdat2size;
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/resource_v7he.h b/engines/scumm/resource_v7he.h
new file mode 100644
index 0000000000..1496aa3d7f
--- /dev/null
+++ b/engines/scumm/resource_v7he.h
@@ -0,0 +1,556 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * Parts of code heavily based on:
+ * icoutils - A set of programs dealing with MS Windows icons and cursors.
+ * Copyright (C) 1998-2001 Oskar Liljeblad
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(RESOURCE_V7HE_H) && !defined(DISABLE_HE)
+#define RESOURCE_V7HE_H
+
+namespace Scumm {
+
+#define WINRES_ID_MAXLEN (256)
+
+/*
+ * Definitions
+ */
+
+#define ACTION_LIST 1 /* command: list resources */
+#define ACTION_EXTRACT 2 /* command: extract resources */
+#define CALLBACK_STOP 0 /* results of ResourceCallback */
+#define CALLBACK_CONTINUE 1
+#define CALLBACK_CONTINUE_RECURS 2
+
+#define MZ_HEADER(x) ((DOSImageHeader *)(x))
+#define NE_HEADER(x) ((OS2ImageHeader *)PE_HEADER(x))
+#define NE_TYPEINFO_NEXT(x) ((Win16NETypeInfo *)((byte *)(x) + sizeof(Win16NETypeInfo) + \
+ ((Win16NETypeInfo *)x)->count * sizeof(Win16NENameInfo)))
+#define NE_RESOURCE_NAME_IS_NUMERIC (0x8000)
+
+#define STRIP_RES_ID_FORMAT(x) (x != NULL && (x[0] == '-' || x[0] == '+') ? ++x : x)
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+#define IMAGE_SIZEOF_SHORT_NAME 8
+
+#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000
+#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000
+
+#define PE_HEADER(module) \
+ ((Win32ImageNTHeaders*)((byte *)(module) + \
+ (((DOSImageHeader*)(module))->lfanew)))
+
+#define PE_SECTIONS(module) \
+ ((Win32ImageSectionHeader *)((byte *) &PE_HEADER(module)->optional_header + \
+ PE_HEADER(module)->file_header.size_of_optional_header))
+
+#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */
+#define IMAGE_OS2_SIGNATURE 0x454E /* NE */
+#define IMAGE_OS2_SIGNATURE_LE 0x454C /* LE */
+#define IMAGE_OS2_SIGNATURE_LX 0x584C /* LX */
+#define IMAGE_VXD_SIGNATURE 0x454C /* LE */
+#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */
+
+#if !defined (WIN32)
+#define IMAGE_SCN_CNT_CODE 0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080
+#endif
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
+#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
+#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
+#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
+#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* (MIPS GP) */
+#define IMAGE_DIRECTORY_ENTRY_TLS 9
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
+#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
+#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */
+#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
+#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
+
+#if !defined (WIN32)
+#define RT_CURSOR 1
+#define RT_BITMAP 2
+#define RT_ICON 3
+#define RT_MENU 4
+#define RT_DIALOG 5
+#define RT_STRING 6
+#define RT_FONTDIR 7
+#define RT_FONT 8
+#define RT_ACCELERATOR 9
+#define RT_RCDATA 10
+#define RT_MESSAGELIST 11
+#define RT_GROUP_CURSOR 12
+#define RT_GROUP_ICON 14
+#endif
+
+#define RETURN_IF_BAD_POINTER(r, x) \
+ if (!check_offset(fi->memory, fi->total_size, fi->file->name(), &(x), sizeof(x))) \
+ return (r);
+#define RETURN_IF_BAD_OFFSET(r, x, s) \
+ if (!check_offset(fi->memory, fi->total_size, fi->file->name(), x, s)) \
+ return (r);
+
+class ScummEngine_v70he;
+
+class ResExtractor {
+public:
+ ResExtractor(ScummEngine_v70he *scumm);
+ virtual ~ResExtractor();
+
+ void setCursor(int id);
+
+ virtual int extractResource(int id, byte **buf) { return 0; };
+ virtual int convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor,
+ byte **palette, int *palSize) { return 0; };
+
+ enum {
+ MAX_CACHED_CURSORS = 10
+ };
+
+ struct CachedCursor {
+ bool valid;
+ int id;
+ byte *bitmap;
+ int w, h;
+ int hotspot_x, hotspot_y;
+ uint32 last_used;
+ byte *palette;
+ int palSize;
+ };
+
+ ScummEngine_v70he *_vm;
+
+ ResExtractor::CachedCursor *findCachedCursor(int id);
+ ResExtractor::CachedCursor *getCachedCursorSlot();
+
+ bool _arg_raw;
+ char _fileName[256];
+ CachedCursor _cursorCache[MAX_CACHED_CURSORS];
+
+ typedef Common::MemoryReadStream MemoryReadStream;
+
+};
+
+class Win32ResExtractor : public ResExtractor {
+ public:
+ Win32ResExtractor(ScummEngine_v70he *scumm);
+ ~Win32ResExtractor() {};
+ int extractResource(int id, byte **data);
+ void setCursor(int id);
+ int convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize);
+
+ private:
+ int extractResource_(const char *resType, char *resName, byte **data);
+/*
+ * Structures
+ */
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+ struct WinLibrary {
+ Common::File *file;
+ byte *memory;
+ byte *first_resource;
+ bool is_PE_binary;
+ int total_size;
+ };
+
+ struct WinResource {
+ char id[256];
+ void *this_;
+ void *children;
+ int level;
+ bool numeric_id;
+ bool is_directory;
+ };
+
+
+ struct Win32IconResDir {
+ byte width;
+ byte height;
+ byte color_count;
+ byte reserved;
+ };
+
+ struct Win32CursorDir {
+ uint16 width;
+ uint16 height;
+ };
+
+ struct Win32CursorIconDirEntry {
+ union {
+ Win32IconResDir icon;
+ Win32CursorDir cursor;
+ } res_info;
+ uint16 plane_count;
+ uint16 bit_count;
+ uint32 bytes_in_res;
+ uint16 res_id;
+ };
+
+ struct Win32CursorIconDir {
+ uint16 reserved;
+ uint16 type;
+ uint16 count;
+ Win32CursorIconDirEntry entries[1] GCC_PACK;
+ };
+
+ struct Win32CursorIconFileDirEntry {
+ byte width;
+ byte height;
+ byte color_count;
+ byte reserved;
+ uint16 hotspot_x;
+ uint16 hotspot_y;
+ uint32 dib_size;
+ uint32 dib_offset;
+ };
+
+ struct Win32CursorIconFileDir {
+ uint16 reserved;
+ uint16 type;
+ uint16 count;
+ Win32CursorIconFileDirEntry entries[1];
+ };
+
+ struct Win32BitmapInfoHeader {
+ uint32 size;
+ int32 width;
+ int32 height;
+ uint16 planes;
+ uint16 bit_count;
+ uint32 compression;
+ uint32 size_image;
+ int32 x_pels_per_meter;
+ int32 y_pels_per_meter;
+ uint32 clr_used;
+ uint32 clr_important;
+ };
+
+ struct Win32RGBQuad {
+ byte blue;
+ byte green;
+ byte red;
+ byte reserved;
+ };
+
+ struct Win32ImageResourceDirectoryEntry {
+ union {
+ struct {
+ #ifdef SCUMM_BIGENDIAN
+ unsigned name_is_string:1;
+ unsigned name_offset:31;
+ #else
+ unsigned name_offset:31;
+ unsigned name_is_string:1;
+ #endif
+ } s1;
+ uint32 name;
+ struct {
+ #ifdef SCUMM_BIG_ENDIAN
+ uint16 __pad;
+ uint16 id;
+ #else
+ uint16 id;
+ uint16 __pad;
+ #endif
+ } s2;
+ } u1;
+ union {
+ uint32 offset_to_data;
+ struct {
+ #ifdef SCUMM_BIG_ENDIAN
+ unsigned data_is_directory:1;
+ unsigned offset_to_directory:31;
+ #else
+ unsigned offset_to_directory:31;
+ unsigned data_is_directory:1;
+ #endif
+ } s;
+ } u2;
+ };
+
+ struct Win16NETypeInfo {
+ uint16 type_id;
+ uint16 count;
+ uint32 resloader; // FARPROC16 - smaller? uint16?
+ };
+
+ struct Win16NENameInfo {
+ uint16 offset;
+ uint16 length;
+ uint16 flags;
+ uint16 id;
+ uint16 handle;
+ uint16 usage;
+ };
+
+ struct OS2ImageHeader {
+ uint16 magic;
+ byte ver;
+ byte rev;
+ uint16 enttab;
+ uint16 cbenttab;
+ int32 crc;
+ uint16 flags;
+ uint16 autodata;
+ uint16 heap;
+ uint16 stack;
+ uint32 csip;
+ uint32 sssp;
+ uint16 cseg;
+ uint16 cmod;
+ uint16 cbnrestab;
+ uint16 segtab;
+ uint16 rsrctab;
+ uint16 restab;
+ uint16 modtab;
+ uint16 imptab;
+ uint32 nrestab;
+ uint16 cmovent;
+ uint16 align;
+ uint16 cres;
+ byte exetyp;
+ byte flagsothers;
+ uint16 fastload_offset;
+ uint16 fastload_length;
+ uint16 swaparea;
+ uint16 expver;
+ };
+
+ struct DOSImageHeader {
+ uint16 magic;
+ uint16 cblp;
+ uint16 cp;
+ uint16 crlc;
+ uint16 cparhdr;
+ uint16 minalloc;
+ uint16 maxalloc;
+ uint16 ss;
+ uint16 sp;
+ uint16 csum;
+ uint16 ip;
+ uint16 cs;
+ uint16 lfarlc;
+ uint16 ovno;
+ uint16 res[4];
+ uint16 oemid;
+ uint16 oeminfo;
+ uint16 res2[10];
+ uint32 lfanew;
+ };
+
+ struct Win32ImageFileHeader {
+ uint16 machine;
+ uint16 number_of_sections;
+ uint32 time_date_stamp;
+ uint32 pointer_to_symbol_table;
+ uint32 number_of_symbols;
+ uint16 size_of_optional_header;
+ uint16 characteristics;
+ };
+
+ struct Win32ImageDataDirectory {
+ uint32 virtual_address;
+ uint32 size;
+ };
+
+ struct Win32ImageOptionalHeader {
+ uint16 magic;
+ byte major_linker_version;
+ byte minor_linker_version;
+ uint32 size_of_code;
+ uint32 size_of_initialized_data;
+ uint32 size_of_uninitialized_data;
+ uint32 address_of_entry_point;
+ uint32 base_of_code;
+ uint32 base_of_data;
+ uint32 image_base;
+ uint32 section_alignment;
+ uint32 file_alignment;
+ uint16 major_operating_system_version;
+ uint16 minor_operating_system_version;
+ uint16 major_image_version;
+ uint16 minor_image_version;
+ uint16 major_subsystem_version;
+ uint16 minor_subsystem_version;
+ uint32 win32_version_value;
+ uint32 size_of_image;
+ uint32 size_of_headers;
+ uint32 checksum;
+ uint16 subsystem;
+ uint16 dll_characteristics;
+ uint32 size_of_stack_reserve;
+ uint32 size_of_stack_commit;
+ uint32 size_of_heap_reserve;
+ uint32 size_of_heap_commit;
+ uint32 loader_flags;
+ uint32 number_of_rva_and_sizes;
+ Win32ImageDataDirectory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+ };
+
+ struct Win32ImageNTHeaders {
+ uint32 signature;
+ Win32ImageFileHeader file_header;
+ Win32ImageOptionalHeader optional_header;
+ };
+
+ struct Win32ImageSectionHeader {
+ byte name[IMAGE_SIZEOF_SHORT_NAME];
+ union {
+ uint32 physical_address;
+ uint32 virtual_size;
+ } misc;
+ uint32 virtual_address;
+ uint32 size_of_raw_data;
+ uint32 pointer_to_raw_data;
+ uint32 pointer_to_relocations;
+ uint32 pointer_to_linenumbers;
+ uint16 number_of_relocations;
+ uint16 number_of_linenumbers;
+ uint32 characteristics;
+ };
+
+ struct Win32ImageResourceDataEntry {
+ uint32 offset_to_data;
+ uint32 size;
+ uint32 code_page;
+ uint32 resource_handle;
+ };
+
+ struct Win32ImageResourceDirectory {
+ uint32 characteristics;
+ uint32 time_date_stamp;
+ uint16 major_version;
+ uint16 minor_version;
+ uint16 number_of_named_entries;
+ uint16 number_of_id_entries;
+ };
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+/*
+ * Function Prototypes
+ */
+
+ WinResource *list_resources(WinLibrary *, WinResource *, int *);
+ bool read_library(WinLibrary *);
+ WinResource *find_resource(WinLibrary *, const char *, const char *, const char *, int *);
+ byte *get_resource_entry(WinLibrary *, WinResource *, int *);
+ int do_resources(WinLibrary *, const char *, char *, char *, int, byte **);
+ bool compare_resource_id(WinResource *, const char *);
+ const char *res_type_string_to_id(const char *);
+
+ const char *res_type_id_to_string(int);
+ char *get_destination_name(WinLibrary *, char *, char *, char *);
+
+ byte *extract_resource(WinLibrary *, WinResource *, int *, bool *, char *, char *, bool);
+ int extract_resources(WinLibrary *, WinResource *, WinResource *, WinResource *, WinResource *, byte **);
+ byte *extract_group_icon_cursor_resource(WinLibrary *, WinResource *, char *, int *, bool);
+
+ bool decode_pe_resource_id(WinLibrary *, WinResource *, uint32);
+ bool decode_ne_resource_id(WinLibrary *, WinResource *, uint16);
+ WinResource *list_ne_type_resources(WinLibrary *, int *);
+ WinResource *list_ne_name_resources(WinLibrary *, WinResource *, int *);
+ WinResource *list_pe_resources(WinLibrary *, Win32ImageResourceDirectory *, int, int *);
+ int calc_vma_size(WinLibrary *);
+ int do_resources_recurs(WinLibrary *, WinResource *, WinResource *, WinResource *, WinResource *, const char *, char *, char *, int, byte **);
+ char *get_resource_id_quoted(WinResource *);
+ WinResource *find_with_resource_array(WinLibrary *, WinResource *, const char *);
+
+ bool check_offset(byte *, int, const char *, void *, int);
+
+ uint32 simple_vec(byte *data, uint32 ofs, byte size);
+
+ void fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj);
+ void fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj);
+ void fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj);
+ void fix_win32_image_section_header(Win32ImageSectionHeader *obj);
+ void fix_os2_image_header_endian(OS2ImageHeader *obj);
+ void fix_win32_image_header_endian(Win32ImageNTHeaders *obj);
+ void fix_win32_image_data_directory(Win32ImageDataDirectory *obj);
+};
+
+class MacResExtractor : public ResExtractor {
+
+public:
+ MacResExtractor(ScummEngine_v70he *scumm);
+ ~MacResExtractor() { }
+ void setCursor(int id) ;
+
+private:
+ int extractResource(int id, byte **buf);
+ bool init(Common::File in);
+ void readMap(Common::File in);
+ byte *getResource(Common::File in, const char *typeID, int16 resID, int *size);
+ int convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize);
+
+ struct ResMap {
+ int16 resAttr;
+ int16 typeOffset;
+ int16 nameOffset;
+ int16 numTypes;
+ };
+
+ struct ResType {
+ char id[5];
+ int16 items;
+ int16 offset;
+ };
+
+ struct Resource {
+ int16 id;
+ int16 nameOffset;
+ byte attr;
+ int32 dataOffset;
+ byte *name;
+ };
+
+ typedef Resource *ResPtr;
+
+private:
+ int _resOffset;
+ int32 _dataOffset;
+ int32 _dataLength;
+ int32 _mapOffset;
+ int32 _mapLength;
+ ResMap _resMap;
+ ResType *_resTypes;
+ ResPtr *_resLists;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp
new file mode 100644
index 0000000000..e53b13c663
--- /dev/null
+++ b/engines/scumm/room.cpp
@@ -0,0 +1,751 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/actor.h"
+#include "scumm/boxes.h"
+#include "scumm/intern.h"
+#ifndef DISABLE_HE
+#include "scumm/intern_he.h"
+#endif
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+/**
+ * Start a 'scene' by loading the specified room with the given main actor.
+ * The actor is placed next to the object indicated by objectNr.
+ */
+void ScummEngine::startScene(int room, Actor *a, int objectNr) {
+ int i, where;
+
+ CHECK_HEAP;
+ debugC(DEBUG_GENERAL, "Loading room %d", room);
+
+ stopTalk();
+
+ fadeOut(_switchRoomEffect2);
+ _newEffect = _switchRoomEffect;
+
+ ScriptSlot *ss = &vm.slot[_currentScript];
+
+ if (_currentScript != 0xFF) {
+ if (ss->where == WIO_ROOM || ss->where == WIO_FLOBJECT) {
+ if (ss->cutsceneOverride && _version >= 5)
+ error("Object %d stopped with active cutscene/override in exit", ss->number);
+
+ nukeArrays(_currentScript);
+ _currentScript = 0xFF;
+ } else if (ss->where == WIO_LOCAL) {
+ if (ss->cutsceneOverride && _version >= 5)
+ error("Script %d stopped with active cutscene/override in exit", ss->number);
+
+ nukeArrays(_currentScript);
+ _currentScript = 0xFF;
+ }
+ }
+
+ if (VAR_NEW_ROOM != 0xFF)
+ VAR(VAR_NEW_ROOM) = room;
+
+ runExitScript();
+
+ killScriptsAndResources();
+ if (_version >= 4 && _heversion <= 61)
+ stopCycle(0);
+ _sound->processSound();
+
+ clearDrawQueues();
+
+ // For HE80+ games
+ for (i = 0; i < _numRoomVariables; i++)
+ _roomVars[i] = 0;
+ nukeArrays(0xFF);
+
+ for (i = 1; i < _numActors; i++) {
+ _actors[i].hideActor();
+ }
+
+ if (_version >= 7) {
+ // Set the shadow palette(s) to all black. This fixes
+ // bug #795940, and actually makes some sense (after all,
+ // shadows tend to be rather black, don't they? ;-)
+ memset(_shadowPalette, 0, NUM_SHADOW_PALETTE * 256);
+ } else {
+ for (i = 0; i < 256; i++) {
+ _roomPalette[i] = i;
+ if (_shadowPalette)
+ _shadowPalette[i] = i;
+ }
+ if (_features & GF_SMALL_HEADER)
+ setDirtyColors(0, 255);
+ }
+
+ VAR(VAR_ROOM) = room;
+ _fullRedraw = true;
+
+ res.increaseResourceCounter();
+
+ _activeObject = 0;
+ _currentRoom = room;
+ VAR(VAR_ROOM) = room;
+
+ if (room >= 0x80 && _version < 7 && _heversion <= 71)
+ _roomResource = _resourceMapper[room & 0x7F];
+ else
+ _roomResource = room;
+
+ if (VAR_ROOM_RESOURCE != 0xFF)
+ VAR(VAR_ROOM_RESOURCE) = _roomResource;
+
+ if (room != 0)
+ ensureResourceLoaded(rtRoom, room);
+
+ clearRoomObjects();
+
+ if (_currentRoom == 0) {
+ _ENCD_offs = _EXCD_offs = 0;
+ _numObjectsInRoom = 0;
+ restoreFlObjects();
+ return;
+ }
+
+ loadRoomSubBlocks();
+ initRoomSubBlocks();
+
+ initBGBuffers(_roomHeight);
+
+ loadRoomObjects();
+ restoreFlObjects();
+
+ if (VAR_ROOM_WIDTH != 0xFF && VAR_ROOM_HEIGHT != 0xFF) {
+ VAR(VAR_ROOM_WIDTH) = _roomWidth;
+ VAR(VAR_ROOM_HEIGHT) = _roomHeight;
+ }
+
+ VAR(VAR_CAMERA_MIN_X) = _screenWidth / 2;
+ VAR(VAR_CAMERA_MAX_X) = _roomWidth - (_screenWidth / 2);
+
+ if (_features & GF_NEW_CAMERA) {
+ VAR(VAR_CAMERA_MIN_Y) = _screenHeight / 2;
+ VAR(VAR_CAMERA_MAX_Y) = _roomHeight - (_screenHeight / 2);
+ setCameraAt(_screenWidth / 2, _screenHeight / 2);
+ } else {
+ camera._mode = kNormalCameraMode;
+ if (_version > 2)
+ camera._cur.x = camera._dest.x = _screenWidth / 2;
+ camera._cur.y = camera._dest.y = _screenHeight / 2;
+ }
+
+ if (_roomResource == 0)
+ return;
+
+ memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
+
+ if (_version >= 5 && a) {
+ where = whereIsObject(objectNr);
+ if (where != WIO_ROOM && where != WIO_FLOBJECT)
+ error("startScene: Object %d is not in room %d", objectNr,
+ _currentRoom);
+ int x, y, dir;
+ getObjectXYPos(objectNr, x, y, dir);
+ a->putActor(x, y, _currentRoom);
+ a->setDirection(dir + 180);
+ a->stopActorMoving();
+ if (_gameId == GID_SAMNMAX) {
+ camera._cur.x = camera._dest.x = a->_pos.x;
+ setCameraAt(a->_pos.x, a->_pos.y);
+ }
+ }
+
+ showActors();
+
+ _egoPositioned = false;
+ runEntryScript();
+ if ((_version <= 2) && !(_platform == Common::kPlatformC64)) {
+ runScript(5, 0, 0, 0);
+ } else if (_version >= 5 && _version <= 6) {
+ if (a && !_egoPositioned) {
+ int x, y;
+ getObjectXYPos(objectNr, x, y);
+ a->putActor(x, y, _currentRoom);
+ a->_moving = 0;
+ }
+ } else if (_version >= 7) {
+ if (camera._follows) {
+ a = derefActor(camera._follows, "startScene: follows");
+ setCameraAt(a->_pos.x, a->_pos.y);
+ }
+ }
+
+ _doEffect = true;
+
+ CHECK_HEAP;
+}
+
+/**
+ * Init some static room data after a room has been loaded.
+ * E.g. the room dimension, the offset to the graphics data, the room scripts,
+ * the offset to the room palette and other things which won't be changed
+ * late on.
+ * So it is possible to call this after loading a savegame.
+ */
+void ScummEngine::loadRoomSubBlocks() {
+ int i;
+ const byte *ptr;
+ byte *roomptr, *searchptr, *roomResPtr = 0;
+ const RoomHeader *rmhd;
+
+ _ENCD_offs = 0;
+ _EXCD_offs = 0;
+ _EPAL_offs = 0;
+ _CLUT_offs = 0;
+ _PALS_offs = 0;
+
+ // Determine the room and room script base address
+ roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
+ if (_version == 8)
+ roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
+ if (!roomptr || !roomResPtr)
+ error("Room %d: data not found (" __FILE__ ":%d)", _roomResource, __LINE__);
+
+ //
+ // Determine the room dimensions (width/height)
+ //
+ rmhd = (const RoomHeader *)findResourceData(MKID('RMHD'), roomptr);
+
+ if (_version == 8) {
+ _roomWidth = READ_LE_UINT32(&(rmhd->v8.width));
+ _roomHeight = READ_LE_UINT32(&(rmhd->v8.height));
+ _numObjectsInRoom = (byte)READ_LE_UINT32(&(rmhd->v8.numObjects));
+ } else if (_version == 7) {
+ _roomWidth = READ_LE_UINT16(&(rmhd->v7.width));
+ _roomHeight = READ_LE_UINT16(&(rmhd->v7.height));
+ _numObjectsInRoom = (byte)READ_LE_UINT16(&(rmhd->v7.numObjects));
+ } else {
+ _roomWidth = READ_LE_UINT16(&(rmhd->old.width));
+ _roomHeight = READ_LE_UINT16(&(rmhd->old.height));
+ _numObjectsInRoom = (byte)READ_LE_UINT16(&(rmhd->old.numObjects));
+ }
+
+ //
+ // Find the room image data
+ //
+ if (_version == 8) {
+ _IM00_offs = getObjectImage(roomptr, 1) - roomptr;
+ } else if (_features & GF_SMALL_HEADER) {
+ _IM00_offs = findResourceData(MKID('IM00'), roomptr) - roomptr;
+ } else if (_heversion >= 70) {
+ byte *roomImagePtr = getResourceAddress(rtRoomImage, _roomResource);
+ _IM00_offs = findResource(MKID('IM00'), roomImagePtr) - roomImagePtr;
+ } else {
+ _IM00_offs = findResource(MKID('IM00'), findResource(MKID('RMIM'), roomptr)) - roomptr;
+ }
+
+ //
+ // Look for an exit script
+ //
+ ptr = findResourceData(MKID('EXCD'), roomResPtr);
+ if (ptr)
+ _EXCD_offs = ptr - roomResPtr;
+ if (_dumpScripts && _EXCD_offs)
+ dumpResource("exit-", _roomResource, roomResPtr + _EXCD_offs - _resourceHeaderSize, -1);
+
+ //
+ // Look for an entry script
+ //
+ ptr = findResourceData(MKID('ENCD'), roomResPtr);
+ if (ptr)
+ _ENCD_offs = ptr - roomResPtr;
+ if (_dumpScripts && _ENCD_offs)
+ dumpResource("entry-", _roomResource, roomResPtr + _ENCD_offs - _resourceHeaderSize, -1);
+
+ //
+ // Setup local scripts
+ //
+
+ // Determine the room script base address
+ roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
+ if (_version == 8)
+ roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
+ searchptr = roomResPtr;
+
+ memset(_localScriptOffsets, 0, sizeof(_localScriptOffsets));
+
+ if (_features & GF_SMALL_HEADER) {
+ ResourceIterator localScriptIterator(searchptr, true);
+ while ((ptr = localScriptIterator.findNext(MKID('LSCR'))) != NULL) {
+ int id = 0;
+ ptr += _resourceHeaderSize; /* skip tag & size */
+ id = ptr[0];
+
+ if (_dumpScripts) {
+ char buf[32];
+ sprintf(buf, "room-%d-", _roomResource);
+ dumpResource(buf, id, ptr - _resourceHeaderSize);
+ }
+
+ _localScriptOffsets[id - _numGlobalScripts] = ptr + 1 - roomptr;
+ }
+ } else if (_heversion >= 90) {
+ ResourceIterator localScriptIterator2(searchptr, false);
+ while ((ptr = localScriptIterator2.findNext(MKID('LSC2'))) != NULL) {
+ int id = 0;
+
+ ptr += _resourceHeaderSize; /* skip tag & size */
+
+ id = READ_LE_UINT32(ptr);
+
+ checkRange(_numLocalScripts + _numGlobalScripts, _numGlobalScripts, id, "Invalid local script %d");
+ _localScriptOffsets[id - _numGlobalScripts] = ptr + 4 - roomResPtr;
+
+ if (_dumpScripts) {
+ char buf[32];
+ sprintf(buf, "room-%d-", _roomResource);
+ dumpResource(buf, id, ptr - _resourceHeaderSize);
+ }
+ }
+
+ ResourceIterator localScriptIterator(searchptr, false);
+ while ((ptr = localScriptIterator.findNext(MKID('LSCR'))) != NULL) {
+ int id = 0;
+
+ ptr += _resourceHeaderSize; /* skip tag & size */
+
+ id = ptr[0];
+ _localScriptOffsets[id - _numGlobalScripts] = ptr + 1 - roomResPtr;
+
+ if (_dumpScripts) {
+ char buf[32];
+ sprintf(buf, "room-%d-", _roomResource);
+ dumpResource(buf, id, ptr - _resourceHeaderSize);
+ }
+ }
+
+ } else {
+ ResourceIterator localScriptIterator(searchptr, false);
+ while ((ptr = localScriptIterator.findNext(MKID('LSCR'))) != NULL) {
+ int id = 0;
+
+ ptr += _resourceHeaderSize; /* skip tag & size */
+
+ if (_version == 8) {
+ id = READ_LE_UINT32(ptr);
+ checkRange(_numLocalScripts + _numGlobalScripts, _numGlobalScripts, id, "Invalid local script %d");
+ _localScriptOffsets[id - _numGlobalScripts] = ptr + 4 - roomResPtr;
+ } else if (_version == 7) {
+ id = READ_LE_UINT16(ptr);
+ checkRange(_numLocalScripts + _numGlobalScripts, _numGlobalScripts, id, "Invalid local script %d");
+ _localScriptOffsets[id - _numGlobalScripts] = ptr + 2 - roomResPtr;
+ } else {
+ id = ptr[0];
+ _localScriptOffsets[id - _numGlobalScripts] = ptr + 1 - roomResPtr;
+ }
+
+ if (_dumpScripts) {
+ char buf[32];
+ sprintf(buf, "room-%d-", _roomResource);
+ dumpResource(buf, id, ptr - _resourceHeaderSize);
+ }
+ }
+ }
+
+ // Locate the EGA palette (currently unused).
+ ptr = findResourceData(MKID('EPAL'), roomptr);
+ if (ptr)
+ _EPAL_offs = ptr - roomptr;
+
+ // Locate the standard room palette (for V3-V5 games).
+ ptr = findResourceData(MKID('CLUT'), roomptr);
+ if (ptr)
+ _CLUT_offs = ptr - roomptr;
+
+ // Locate the standard room palettes (for V6+ games).
+ if (_version >= 6) {
+ ptr = findResource(MKID('PALS'), roomptr);
+ if (ptr) {
+ _PALS_offs = ptr - roomptr;
+ }
+ }
+
+ // Transparent color
+ byte trans;
+ if (_version == 8)
+ trans = (byte)READ_LE_UINT32(&(rmhd->v8.transparency));
+ else {
+ ptr = findResourceData(MKID('TRNS'), roomptr);
+ if (ptr)
+ trans = ptr[0];
+ else
+ trans = 255;
+ }
+
+ // Actor Palette in HE 70 games
+ if (_heversion == 70) {
+ ptr = findResourceData(MKID('REMP'), roomptr);
+ if (ptr) {
+ for (i = 0; i < 256; i++)
+ _HEV7ActorPalette[i] = *ptr++;
+ } else {
+ for (i = 0; i < 256; i++)
+ _HEV7ActorPalette[i] = i;
+ }
+ }
+
+
+ // WORKAROUND bug #1074444: The dreaded DOTT "Can't get teeth" bug
+ // makes it impossible to go on playing w/o cheating in some way.
+ // It's not quite clear what causes it, but the effect is that object
+ // 182, the teeth, are still in class 32 (kObjectClassUntouchable),
+ // when they shouldn't be. Luckily, bitvar69 is set to 1 if and only if
+ // the teeth are trapped and have not yet been taken by the player. So
+ // we can make use of that fact to fix the object class of obj 182.
+ if (_gameId == GID_TENTACLE && _roomResource == 26 && readVar(0x8000 + 69)
+ && getClass(182, kObjectClassUntouchable)) {
+ putClass(182, kObjectClassUntouchable, 0);
+ }
+
+ gdi.roomChanged(roomptr, _IM00_offs, trans);
+}
+
+/**
+ * Init some dynamic room data after a room has been loaded.
+ * E.g. the initial box data is loaded, the initial palette is set etc.
+ * All of the things setup in here can be modified later on by scripts.
+ * So it is not appropriate to call it after loading a savegame.
+ */
+void ScummEngine::initRoomSubBlocks() {
+ int i;
+ const byte *ptr;
+ byte *roomptr;
+
+ // Determine the room and room script base address
+ roomptr = getResourceAddress(rtRoom, _roomResource);
+ if (!roomptr)
+ error("Room %d: data not found (" __FILE__ ":%d)", _roomResource, __LINE__);
+
+ //
+ // Load box data
+ //
+ memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));
+
+ res.nukeResource(rtMatrix, 1);
+ res.nukeResource(rtMatrix, 2);
+ if (_features & GF_SMALL_HEADER) {
+ ptr = findResourceData(MKID('BOXD'), roomptr);
+ if (ptr) {
+ byte numOfBoxes = *ptr;
+ int size;
+ if (_version == 3)
+ size = numOfBoxes * SIZEOF_BOX_V3 + 1;
+ else
+ size = numOfBoxes * SIZEOF_BOX + 1;
+
+ res.createResource(rtMatrix, 2, size);
+ memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
+ ptr += size;
+
+ size = getResourceDataSize(ptr - size - _resourceHeaderSize) - size;
+ if (size > 0) { // do this :)
+ res.createResource(rtMatrix, 1, size);
+ memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
+ }
+
+ }
+ } else {
+ ptr = findResourceData(MKID('BOXD'), roomptr);
+ if (ptr) {
+ int size = getResourceDataSize(ptr);
+ res.createResource(rtMatrix, 2, size);
+ roomptr = getResourceAddress(rtRoom, _roomResource);
+ ptr = findResourceData(MKID('BOXD'), roomptr);
+ memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
+ }
+
+ ptr = findResourceData(MKID('BOXM'), roomptr);
+ if (ptr) {
+ int size = getResourceDataSize(ptr);
+ res.createResource(rtMatrix, 1, size);
+ roomptr = getResourceAddress(rtRoom, _roomResource);
+ ptr = findResourceData(MKID('BOXM'), roomptr);
+ memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
+ }
+ }
+
+ //
+ // Load scale data
+ //
+ for (i = 1; i < res.num[rtScaleTable]; i++)
+ res.nukeResource(rtScaleTable, i);
+
+ ptr = findResourceData(MKID('SCAL'), roomptr);
+ if (ptr) {
+ int s1, s2, y1, y2;
+ if (_version == 8) {
+ for (i = 1; i < res.num[rtScaleTable]; i++, ptr += 16) {
+ s1 = READ_LE_UINT32(ptr);
+ y1 = READ_LE_UINT32(ptr + 4);
+ s2 = READ_LE_UINT32(ptr + 8);
+ y2 = READ_LE_UINT32(ptr + 12);
+ setScaleSlot(i, 0, y1, s1, 0, y2, s2);
+ }
+ } else {
+ for (i = 1; i < res.num[rtScaleTable]; i++, ptr += 8) {
+ s1 = READ_LE_UINT16(ptr);
+ y1 = READ_LE_UINT16(ptr + 2);
+ s2 = READ_LE_UINT16(ptr + 4);
+ y2 = READ_LE_UINT16(ptr + 6);
+ if (s1 || y1 || s2 || y2) {
+ setScaleSlot(i, 0, y1, s1, 0, y2, s2);
+ }
+ }
+ }
+ }
+
+ // Color cycling
+ // HE 7.0 games load resources but don't use them.
+ if (_version >= 4 && _heversion <= 61) {
+ ptr = findResourceData(MKID('CYCL'), roomptr);
+ if (ptr) {
+ initCycl(ptr);
+ }
+ }
+
+#ifndef DISABLE_HE
+ // Polygons in HE 80+ games
+ if (_heversion >= 80) {
+ ptr = findResourceData(MKID('POLD'), roomptr);
+ if (ptr) {
+ ((ScummEngine_v70he *)this)->_wiz->polygonLoad(ptr);
+ }
+ }
+#endif
+
+ if (_PALS_offs || _CLUT_offs)
+ setPalette(0);
+}
+
+
+void ScummEngine_v3old::loadRoomSubBlocks() {
+ const byte *ptr;
+ byte *roomptr, *searchptr = 0;
+ const RoomHeader *rmhd;
+
+ _ENCD_offs = 0;
+ _EXCD_offs = 0;
+ _EPAL_offs = 0;
+ _CLUT_offs = 0;
+ _PALS_offs = 0;
+
+ // Determine the room and room script base address
+ roomptr = getResourceAddress(rtRoom, _roomResource);
+ if (!roomptr)
+ error("Room %d: data not found (" __FILE__ ":%d)", _roomResource, __LINE__);
+
+ //
+ // Determine the room dimensions (width/height)
+ //
+ rmhd = (const RoomHeader *)(roomptr + 4);
+
+ if (_version == 1) {
+ if (_platform == Common::kPlatformNES) {
+ _roomWidth = READ_LE_UINT16(&(rmhd->old.width)) * 8;
+ _roomHeight = READ_LE_UINT16(&(rmhd->old.height)) * 8;
+
+ // HACK: To let our code work normal with narrow rooms we
+ // adjust width. It will render garbage on right edge but we do
+ // not render it anyway
+ if (_roomWidth < 32 * 8)
+ _roomWidth = 32 * 8;
+ } else {
+ _roomWidth = roomptr[4] * 8;
+ _roomHeight = roomptr[5] * 8;
+ }
+ } else {
+ _roomWidth = READ_LE_UINT16(&(rmhd->old.width));
+ _roomHeight = READ_LE_UINT16(&(rmhd->old.height));
+ }
+ _numObjectsInRoom = roomptr[20];
+
+ //
+ // Find the room image data
+ //
+ if (_version == 1) {
+ _IM00_offs = 0;
+ } else {
+ _IM00_offs = READ_LE_UINT16(roomptr + 0x0A);
+ }
+
+ //
+ // Look for an exit script
+ //
+ int EXCD_len = -1;
+ if (_version <= 2) {
+ _EXCD_offs = READ_LE_UINT16(roomptr + 0x18);
+ EXCD_len = READ_LE_UINT16(roomptr + 0x1A) - _EXCD_offs + _resourceHeaderSize; // HACK
+ } else {
+ _EXCD_offs = READ_LE_UINT16(roomptr + 0x19);
+ EXCD_len = READ_LE_UINT16(roomptr + 0x1B) - _EXCD_offs + _resourceHeaderSize; // HACK
+ }
+ if (_dumpScripts && _EXCD_offs)
+ dumpResource("exit-", _roomResource, roomptr + _EXCD_offs - _resourceHeaderSize, EXCD_len);
+
+ //
+ // Look for an entry script
+ //
+ int ENCD_len = -1;
+ if (_version <= 2) {
+ _ENCD_offs = READ_LE_UINT16(roomptr + 0x1A);
+ ENCD_len = READ_LE_UINT16(roomptr) - _ENCD_offs + _resourceHeaderSize; // HACK
+ } else {
+ _ENCD_offs = READ_LE_UINT16(roomptr + 0x1B);
+ // FIXME - the following is a hack which assumes that immediately after
+ // the entry script the first local script follows.
+ int num_objects = *(roomptr + 20);
+ int num_sounds = *(roomptr + 23);
+ int num_scripts = *(roomptr + 24);
+ ptr = roomptr + 29 + num_objects * 4 + num_sounds + num_scripts;
+ ENCD_len = READ_LE_UINT16(ptr + 1) - _ENCD_offs + _resourceHeaderSize; // HACK
+ }
+ if (_dumpScripts && _ENCD_offs)
+ dumpResource("entry-", _roomResource, roomptr + _ENCD_offs - _resourceHeaderSize, ENCD_len);
+
+ //
+ // Setup local scripts
+ //
+
+ // Determine the room script base address
+ roomptr = getResourceAddress(rtRoom, _roomResource);
+ searchptr = roomptr;
+
+ memset(_localScriptOffsets, 0, sizeof(_localScriptOffsets));
+
+ int num_objects = *(roomptr + 20);
+ int num_sounds;
+ int num_scripts;
+
+ if (_version <= 2) {
+ num_sounds = *(roomptr + 22);
+ num_scripts = *(roomptr + 23);
+ ptr = roomptr + 28 + num_objects * 4;
+ while (num_sounds--)
+ loadResource(rtSound, *ptr++);
+ while (num_scripts--)
+ loadResource(rtScript, *ptr++);
+ } else /* if (_version == 3) */ {
+ num_sounds = *(roomptr + 23);
+ num_scripts = *(roomptr + 24);
+ ptr = roomptr + 29 + num_objects * 4 + num_sounds + num_scripts;
+ while (*ptr) {
+ int id = *ptr;
+
+ _localScriptOffsets[id - _numGlobalScripts] = READ_LE_UINT16(ptr + 1);
+ ptr += 3;
+
+ if (_dumpScripts) {
+ char buf[32];
+ sprintf(buf, "room-%d-", _roomResource);
+
+ // HACK: to determine the sizes of the local scripts, we assume that
+ // a) their order in the data file is the same as in the index
+ // b) the last script at the same time is the last item in the room "header"
+ int len = - (int)_localScriptOffsets[id - _numGlobalScripts] + _resourceHeaderSize;
+ if (*ptr)
+ len += READ_LE_UINT16(ptr + 1);
+ else
+ len += READ_LE_UINT16(roomptr);
+ dumpResource(buf, id, roomptr + _localScriptOffsets[id - _numGlobalScripts] - _resourceHeaderSize, len);
+ }
+ }
+ }
+
+ gdi.roomChanged(roomptr, _IM00_offs, 255);
+}
+
+void ScummEngine_v3old::initRoomSubBlocks() {
+ int i;
+ const byte *ptr;
+ byte *roomptr;
+
+ // Determine the room and room script base address
+ roomptr = getResourceAddress(rtRoom, _roomResource);
+ if (!roomptr)
+ error("Room %d: data not found (" __FILE__ ":%d)", _roomResource, __LINE__);
+
+ // Reset room color for V1 zak
+ if (_version == 1)
+ _roomPalette[0] = 0;
+
+ //
+ // Load box data
+ //
+ res.nukeResource(rtMatrix, 1);
+ res.nukeResource(rtMatrix, 2);
+
+ // TODO: Convert older walkbox format
+ if (_gameId == GID_MANIAC && _platform == Common::kPlatformC64)
+ return;
+
+ if (_version <= 2)
+ ptr = roomptr + *(roomptr + 0x15);
+ else
+ ptr = roomptr + READ_LE_UINT16(roomptr + 0x15);
+ if (ptr) {
+ byte numOfBoxes = *ptr;
+ int size;
+ if (_version <= 2)
+ size = numOfBoxes * SIZEOF_BOX_V2 + 1;
+ else
+ size = numOfBoxes * SIZEOF_BOX_V3 + 1;
+
+ res.createResource(rtMatrix, 2, size);
+ memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
+ ptr += size;
+ if (_version <= 2) {
+ size = numOfBoxes * (numOfBoxes + 1);
+ } else {
+ // FIXME. This is an evil HACK!!!
+ size = (READ_LE_UINT16(roomptr + 0x0A) - READ_LE_UINT16(roomptr + 0x15)) - size;
+ }
+
+ if (size > 0) { // do this :)
+ res.createResource(rtMatrix, 1, size);
+ memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
+ }
+
+ }
+
+ //
+ // No scale data in old bundle games
+ //
+ for (i = 1; i < res.num[rtScaleTable]; i++)
+ res.nukeResource(rtScaleTable, i);
+
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
new file mode 100644
index 0000000000..9dca7abd40
--- /dev/null
+++ b/engines/scumm/saveload.cpp
@@ -0,0 +1,1648 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/system.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse.h"
+#include "scumm/intern.h"
+#include "scumm/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/sprite_he.h"
+#include "scumm/verbs.h"
+
+#include "sound/audiocd.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+struct SaveGameHeader {
+ uint32 type;
+ uint32 size;
+ uint32 ver;
+ char name[32];
+};
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct SaveInfoSection {
+ uint32 type;
+ uint32 version;
+ uint32 size;
+
+ uint32 timeTValue; // Obsolete since version 2, but kept for compatibility
+ uint32 playtime;
+
+ uint32 date;
+ uint16 time;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+#define INFOSECTION_VERSION 2
+
+void ScummEngine::requestSave(int slot, const char *name, bool temporary) {
+ _saveLoadSlot = slot;
+ _saveTemporaryState = temporary;
+ _saveLoadFlag = 1; // 1 for save
+ assert(name);
+ strncpy(_saveLoadName, name, sizeof(_saveLoadName));
+ _saveLoadName[sizeof(_saveLoadName) - 1] = 0;
+}
+
+void ScummEngine::requestLoad(int slot) {
+ _saveLoadSlot = slot;
+ _saveTemporaryState = false;
+ _saveLoadFlag = 2; // 2 for load
+}
+
+bool ScummEngine::saveState(int slot, bool compat) {
+ char filename[256];
+ Common::OutSaveFile *out;
+ SaveGameHeader hdr;
+
+ makeSavegameName(filename, slot, compat);
+
+ if (!(out = _saveFileMan->openForSaving(filename)))
+ return false;
+
+ memcpy(hdr.name, _saveLoadName, sizeof(hdr.name));
+
+ hdr.type = MKID('SCVM');
+ hdr.size = 0;
+ hdr.ver = TO_LE_32(CURRENT_VER);
+
+ out->write(&hdr, sizeof(hdr));
+ saveThumbnail(out);
+ saveInfos(out);
+
+ Serializer ser(0, out, CURRENT_VER);
+ saveOrLoad(&ser);
+ out->flush();
+ if(out->ioFailed()) {
+ delete out;
+ debug(1, "State save as '%s' FAILED", filename);
+ return false;
+ }
+ delete out;
+ debug(1, "State saved as '%s'", filename);
+ return true;
+}
+
+bool ScummEngine::loadState(int slot, bool compat) {
+ char filename[256];
+ Common::InSaveFile *in;
+ int i, j;
+ SaveGameHeader hdr;
+ int sb, sh;
+
+ makeSavegameName(filename, slot, compat);
+ if (!(in = _saveFileMan->openForLoading(filename)))
+ return false;
+
+ in->read(&hdr, sizeof(hdr));
+ if (hdr.type != MKID('SCVM')) {
+ warning("Invalid savegame '%s'", filename);
+ delete in;
+ return false;
+ }
+
+ // In older versions of ScummVM, the header version was not endian safe.
+ // We account for that by retrying once with swapped byte order.
+ if (hdr.ver > CURRENT_VER)
+ hdr.ver = SWAP_BYTES_32(hdr.ver);
+
+ // Reject save games which are too old or too new. Note that
+ // We do not really support V7 games, but still accept them here
+ // to work around a bug from the stone age (see below for more
+ // information).
+ if (hdr.ver < VER(7) || hdr.ver > CURRENT_VER) {
+ warning("Invalid version of '%s'", filename);
+ delete in;
+ return false;
+ }
+
+ // We (deliberately) broke HE savegame compatibility at some point.
+ if (hdr.ver < VER(50) && _heversion >= 71) {
+ warning("Unsupported version of '%s'", filename);
+ delete in;
+ return false;
+ }
+
+ // Since version 52 a thumbnail is saved directly after the header.
+ if (hdr.ver >= VER(52)) {
+ uint32 type;
+ in->read(&type, 4);
+
+ // Check for the THMB header. Also, work around a bug which caused
+ // the chunk type (incorrectly) to be written in LE on LE machines.
+ if (! (type == MKID('THMB') || (hdr.ver < VER(55) && type == MKID('BMHT')))){
+ warning("Can not load thumbnail");
+ delete in;
+ return false;
+ }
+ uint32 size = in->readUint32BE();
+ in->skip(size - 8);
+ }
+
+ // Since version 56 we save additional information about the creation of
+ // the save game and the save time.
+ if (hdr.ver >= VER(56)) {
+ InfoStuff infos;
+ if (!loadInfos(in, &infos)) {
+ warning("Info section could not be found");
+ delete in;
+ return false;
+ }
+
+ _engineStartTime = _system->getMillis() / 1000 - infos.playtime;
+ } else {
+ // start time counting
+ _engineStartTime = _system->getMillis() / 1000;
+ }
+
+ _dialogStartTime = _system->getMillis() / 1000;
+
+ // Due to a bug in scummvm up to and including 0.3.0, save games could be saved
+ // in the V8/V9 format but were tagged with a V7 mark. Ouch. So we just pretend V7 == V8 here
+ if (hdr.ver == VER(7))
+ hdr.ver = VER(8);
+
+ memcpy(_saveLoadName, hdr.name, sizeof(hdr.name));
+
+ // Unless specifically requested with _saveSound, we do not save the iMUSE
+ // state for temporary state saves - such as certain cutscenes in DOTT,
+ // FOA, Sam and Max, etc.
+ //
+ // Thusly, we should probably not stop music when restoring from one of
+ // these saves. This change stops the Mole Man theme from going quiet in
+ // Sam & Max when Doug tells you about the Ball of Twine, as mentioned in
+ // patch #886058.
+ //
+ // If we don't have iMUSE at all we may as well stop the sounds. The previous
+ // default behavior here was to stopAllSounds on all state restores.
+
+ if (!_imuse || _saveSound || !_saveTemporaryState)
+ _sound->stopAllSounds();
+
+#ifndef DISABLE_SCUMM_7_8
+ if (_imuseDigital) {
+ _imuseDigital->stopAllSounds();
+ _imuseDigital->resetState();
+ }
+#endif
+
+ _sound->stopCD();
+
+ _sound->pauseSounds(true);
+
+ CHECK_HEAP
+
+ closeRoom();
+
+ memset(_inventory, 0, sizeof(_inventory[0]) * _numInventory);
+ memset(_newNames, 0, sizeof(_newNames[0]) * _numNewNames);
+
+ // Because old savegames won't fill the entire gfxUsageBits[] array,
+ // clear it here just to be sure it won't hold any unforseen garbage.
+ memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
+
+ // Nuke all resources
+ for (i = rtFirst; i <= rtLast; i++)
+ if (i != rtTemp && i != rtBuffer && (i != rtSound || _saveSound || !compat))
+ for (j = 0; j < res.num[i]; j++) {
+ res.nukeResource(i, j);
+ }
+
+ initScummVars();
+
+ if (_features & GF_OLD_BUNDLE)
+ loadCharset(0); // FIXME - HACK ?
+
+ //
+ // Now do the actual loading
+ //
+ Serializer ser(in, 0, hdr.ver);
+ saveOrLoad(&ser);
+ delete in;
+
+ // Update volume settings
+ setupVolumes();
+
+ // Init NES costume data
+ if (_platform == Common::kPlatformNES) {
+ if (hdr.ver < VER(47))
+ _NESCostumeSet = 0;
+ NES_loadCostumeSet(_NESCostumeSet);
+ }
+
+ // Normally, _vm->_screenTop should always be >= 0, but for some old save games
+ // it is not, hence we check & correct it here.
+ if (_screenTop < 0)
+ _screenTop = 0;
+
+ if (hdr.ver < VER(33) && _version >= 7) {
+ // For a long time, we didn't set these vars to default values.
+ VAR(VAR_DEFAULT_TALK_DELAY) = 60;
+ if (_version == 7)
+ VAR(VAR_NUM_GLOBAL_OBJS) = _numGlobalObjects - 1;
+ }
+
+ if (hdr.ver < VER(30)) {
+ // For a long time, we used incorrect location, causing it to default to zero.
+ if (_version == 8)
+ _scummVars[VAR_CHARINC] = (_features & GF_DEMO) ? 3 : 1;
+ // Needed due to subtitle speed changes
+ _defaultTalkDelay /= 20;
+ }
+
+ // For a long time, we used incorrect locations for some camera related
+ // scumm vars. We now know the proper locations. To be able to properly use
+ // old save games, we update the old (bad) variables to the new (correct)
+ // ones.
+ if (hdr.ver < VER(28) && _version == 8) {
+ _scummVars[VAR_CAMERA_MIN_X] = _scummVars[101];
+ _scummVars[VAR_CAMERA_MAX_X] = _scummVars[102];
+ _scummVars[VAR_CAMERA_MIN_Y] = _scummVars[103];
+ _scummVars[VAR_CAMERA_MAX_Y] = _scummVars[104];
+ _scummVars[VAR_CAMERA_THRESHOLD_X] = _scummVars[105];
+ _scummVars[VAR_CAMERA_THRESHOLD_Y] = _scummVars[106];
+ _scummVars[VAR_CAMERA_SPEED_X] = _scummVars[107];
+ _scummVars[VAR_CAMERA_SPEED_Y] = _scummVars[108];
+ _scummVars[VAR_CAMERA_ACCEL_X] = _scummVars[109];
+ _scummVars[VAR_CAMERA_ACCEL_Y] = _scummVars[110];
+ }
+
+ // With version 22, we replaced the scale items with scale slots. So when
+ // loading such an old save game, try to upgrade the old to new format.
+ if (hdr.ver < VER(22)) {
+ // Convert all rtScaleTable resources to matching scale items
+ for (i = 1; i < res.num[rtScaleTable]; i++) {
+ convertScaleTableToScaleSlot(i);
+ }
+ }
+
+ // We could simply dirty colours 0-15 for 16-colour games -- nowadays
+ // they handle their palette pretty much like the more recent games
+ // anyway. There was a time, though, when re-initializing was necessary
+ // for backwards compatibility, and it may still prove useful if we
+ // ever add options for using different 16-colour palettes.
+ if (_version == 1) {
+ if (_platform == Common::kPlatformC64) {
+ setupC64Palette();
+ } else if (_platform == Common::kPlatformNES) {
+ setupNESPalette();
+ } else {
+ setupV1Palette();
+ }
+ } else if (_features & GF_16COLOR) {
+ switch (_renderMode) {
+ case Common::kRenderEGA:
+ setupEGAPalette();
+ break;
+
+ case Common::kRenderAmiga:
+ setupAmigaPalette();
+ break;
+
+ case Common::kRenderCGA:
+ setupCGAPalette();
+ break;
+
+ case Common::kRenderHercA:
+ case Common::kRenderHercG:
+ setupHercPalette();
+ break;
+
+ default:
+ if ((_platform == Common::kPlatformAmiga) || (_platform == Common::kPlatformAtariST))
+ setupAmigaPalette();
+ else
+ setupEGAPalette();
+ }
+ } else
+ setDirtyColors(0, 255);
+
+
+ if (hdr.ver < VER(35) && _gameId == GID_MANIAC && _version == 1)
+ setupV1ActorTalkColor();
+
+ // Load the static room data
+ loadRoomSubBlocks();
+
+ if (!(_features & GF_NEW_CAMERA)) {
+ camera._last.x = camera._cur.x;
+ }
+
+ sb = _screenB;
+ sh = _screenH;
+
+ // Restore the virtual screens and force a fade to black.
+ initScreens(0, _screenHeight);
+
+ VirtScreen *vs = &virtscr[kMainVirtScreen];
+ memset(vs->getPixels(0, 0), 0, vs->pitch * vs->h);
+ vs->setDirtyRange(0, vs->h);
+ updateDirtyScreen(kMainVirtScreen);
+ updatePalette();
+ initScreens(sb, sh);
+
+ _completeScreenRedraw = true;
+
+ // Reset charset mask
+ _charset->_hasMask = false;
+ _charset->clearTextSurface();
+
+ _lastCodePtr = NULL;
+ _drawObjectQueNr = 0;
+ _verbMouseOver = 0;
+
+ cameraMoved();
+
+ initBGBuffers(_roomHeight);
+
+ if (VAR_ROOM_FLAG != 0xFF)
+ VAR(VAR_ROOM_FLAG) = 1;
+
+ // Sync with current config setting
+ if (VAR_VOICE_MODE != 0xFF)
+ VAR(VAR_VOICE_MODE) = ConfMan.getBool("subtitles");
+
+ CHECK_HEAP
+ debug(1, "State loaded from '%s'", filename);
+
+ _sound->pauseSounds(false);
+
+ _engineStartTime += _system->getMillis() / 1000 - _dialogStartTime;
+ _dialogStartTime = 0;
+
+ return true;
+}
+
+void ScummEngine::makeSavegameName(char *out, int slot, bool temporary) {
+ sprintf(out, "%s.%c%.2d", _targetName.c_str(), temporary ? 'c' : 's', slot);
+}
+
+void ScummEngine::listSavegames(bool *marks, int num) {
+ char prefix[256];
+ makeSavegameName(prefix, 99, false);
+ prefix[strlen(prefix)-2] = 0;
+ _saveFileMan->listSavefiles(prefix, marks, num);
+}
+
+bool ScummEngine::getSavegameName(int slot, char *desc) {
+ char filename[256];
+ Common::InSaveFile *in;
+ SaveGameHeader hdr;
+ int len;
+
+ makeSavegameName(filename, slot, false);
+ if (!(in = _saveFileMan->openForLoading(filename))) {
+ strcpy(desc, "");
+ return false;
+ }
+ len = in->read(&hdr, sizeof(hdr));
+ delete in;
+
+ if (len != sizeof(hdr) || hdr.type != MKID('SCVM')) {
+ strcpy(desc, "Invalid savegame");
+ return false;
+ }
+
+ if (hdr.ver > CURRENT_VER)
+ hdr.ver = TO_LE_32(hdr.ver);
+ if (hdr.ver < VER(7) || hdr.ver > CURRENT_VER) {
+ strcpy(desc, "Invalid version");
+ return false;
+ }
+
+ // We (deliberately) broke HE savegame compatibility at some point.
+ if (hdr.ver < VER(57) && _heversion >= 60) {
+ strcpy(desc, "Unsupported version");
+ return false;
+ }
+
+ memcpy(desc, hdr.name, sizeof(hdr.name));
+ desc[sizeof(hdr.name) - 1] = 0;
+ return true;
+}
+
+Graphics::Surface *ScummEngine::loadThumbnailFromSlot(int slot) {
+ char filename[256];
+ Common::InSaveFile *in;
+ SaveGameHeader hdr;
+ int len;
+
+ makeSavegameName(filename, slot, false);
+ if (!(in = _saveFileMan->openForLoading(filename))) {
+ return 0;
+ }
+ len = in->read(&hdr, sizeof(hdr));
+
+ if (len != sizeof(hdr) || hdr.type != MKID('SCVM')) {
+ delete in;
+ return 0;
+ }
+
+ if (hdr.ver > CURRENT_VER)
+ hdr.ver = TO_LE_32(hdr.ver);
+ if (hdr.ver < VER(52)) {
+ delete in;
+ return 0;
+ }
+
+ Graphics::Surface *thumb = loadThumbnail(in);
+
+ delete in;
+ return thumb;
+}
+
+bool ScummEngine::loadInfosFromSlot(int slot, InfoStuff *stuff) {
+ char filename[256];
+ Common::InSaveFile *in;
+ SaveGameHeader hdr;
+ int len;
+
+ makeSavegameName(filename, slot, false);
+ if (!(in = _saveFileMan->openForLoading(filename))) {
+ return false;
+ }
+ len = in->read(&hdr, sizeof(hdr));
+
+ if (len != sizeof(hdr) || hdr.type != MKID('SCVM')) {
+ delete in;
+ return false;
+ }
+
+ if (hdr.ver > CURRENT_VER)
+ hdr.ver = TO_LE_32(hdr.ver);
+ if (hdr.ver < VER(56)) {
+ delete in;
+ return false;
+ }
+
+ uint32 type;
+ in->read(&type, 4);
+
+ // Check for the THMB header. Also, work around a bug which caused
+ // the chunk type (incorrectly) to be written in LE on LE machines.
+ if (! (type == MKID('THMB') || (hdr.ver < VER(55) && type == MKID('BMHT')))){
+ delete in;
+ return false;
+ }
+ uint32 size = in->readUint32BE();
+ in->skip(size - 8);
+
+ if (!loadInfos(in, stuff)) {
+ delete in;
+ return false;
+ }
+
+ delete in;
+ return true;
+}
+
+bool ScummEngine::loadInfos(Common::InSaveFile *file, InfoStuff *stuff) {
+ memset(stuff, 0, sizeof(InfoStuff));
+
+ SaveInfoSection section;
+ file->read(&section.type, 4);
+ if (section.type != MKID('INFO')) {
+ return false;
+ }
+
+ section.version = file->readUint32BE();
+ section.size = file->readUint32BE();
+
+ // if we extend this we should add a table for the special sizes at the versions to check it
+ if (section.version == INFOSECTION_VERSION && section.size != sizeof(SaveInfoSection)) {
+ warning("Info section is corrupt");
+ file->skip(section.size);
+ return false;
+ }
+
+ section.timeTValue = file->readUint32BE();
+ section.playtime = file->readUint32BE();
+
+ // for compatibility for older version we
+ // to load in with our old method
+ if (section.version == 1) {
+ time_t curTime_ = section.timeTValue;
+ tm *curTime = localtime(&curTime_);
+ stuff->date = (curTime->tm_mday & 0xFF) << 24 | ((curTime->tm_mon + 1) & 0xFF) << 16 | (curTime->tm_year + 1900) & 0xFFFF;
+ stuff->time = (curTime->tm_hour & 0xFF) << 8 | (curTime->tm_min) & 0xFF;
+ }
+
+ if (section.version >= 2) {
+ section.date = file->readUint32BE();
+ section.time = file->readUint16BE();
+
+ stuff->date = section.date;
+ stuff->time = section.time;
+ }
+
+ stuff->playtime = section.playtime;
+
+ // skip all newer features, this could make problems if some older version uses more space for
+ // saving informations, but this should NOT happen
+ if (section.size > sizeof(SaveInfoSection)) {
+ file->skip(section.size - sizeof(SaveInfoSection));
+ }
+
+ return true;
+}
+
+void ScummEngine::saveInfos(Common::OutSaveFile* file) {
+ SaveInfoSection section;
+ section.type = MKID('INFO');
+ section.version = INFOSECTION_VERSION;
+ section.size = sizeof(SaveInfoSection);
+
+ // still save old format for older versions
+ section.timeTValue = time(0);
+ section.playtime = _system->getMillis() / 1000 - _engineStartTime;
+
+ time_t curTime_ = time(0);
+ tm *curTime = localtime(&curTime_);
+ section.date = (curTime->tm_mday & 0xFF) << 24 | ((curTime->tm_mon + 1) & 0xFF) << 16 | (curTime->tm_year + 1900) & 0xFFFF;
+ section.time = (curTime->tm_hour & 0xFF) << 8 | (curTime->tm_min) & 0xFF;
+
+ file->write(&section.type, 4);
+ file->writeUint32BE(section.version);
+ file->writeUint32BE(section.size);
+ file->writeUint32BE(section.timeTValue);
+ file->writeUint32BE(section.playtime);
+ file->writeUint32BE(section.date);
+ file->writeUint16BE(section.time);
+}
+
+void ScummEngine::saveOrLoad(Serializer *s) {
+ const SaveLoadEntry objectEntries[] = {
+ MKLINE(ObjectData, OBIMoffset, sleUint32, VER(8)),
+ MKLINE(ObjectData, OBCDoffset, sleUint32, VER(8)),
+ MKLINE(ObjectData, walk_x, sleUint16, VER(8)),
+ MKLINE(ObjectData, walk_y, sleUint16, VER(8)),
+ MKLINE(ObjectData, obj_nr, sleUint16, VER(8)),
+ MKLINE(ObjectData, x_pos, sleInt16, VER(8)),
+ MKLINE(ObjectData, y_pos, sleInt16, VER(8)),
+ MKLINE(ObjectData, width, sleUint16, VER(8)),
+ MKLINE(ObjectData, height, sleUint16, VER(8)),
+ MKLINE(ObjectData, actordir, sleByte, VER(8)),
+ MKLINE(ObjectData, parentstate, sleByte, VER(8)),
+ MKLINE(ObjectData, parent, sleByte, VER(8)),
+ MKLINE(ObjectData, state, sleByte, VER(8)),
+ MKLINE(ObjectData, fl_object_index, sleByte, VER(8)),
+ MKLINE(ObjectData, flags, sleByte, VER(46)),
+ MKEND()
+ };
+
+ const SaveLoadEntry verbEntries[] = {
+ MKLINE(VerbSlot, curRect.left, sleInt16, VER(8)),
+ MKLINE(VerbSlot, curRect.top, sleInt16, VER(8)),
+ MKLINE(VerbSlot, curRect.right, sleInt16, VER(8)),
+ MKLINE(VerbSlot, curRect.bottom, sleInt16, VER(8)),
+ MKLINE(VerbSlot, oldRect.left, sleInt16, VER(8)),
+ MKLINE(VerbSlot, oldRect.top, sleInt16, VER(8)),
+ MKLINE(VerbSlot, oldRect.right, sleInt16, VER(8)),
+ MKLINE(VerbSlot, oldRect.bottom, sleInt16, VER(8)),
+
+ MKLINE_OLD(VerbSlot, verbid, sleByte, VER(8), VER(11)),
+ MKLINE(VerbSlot, verbid, sleInt16, VER(12)),
+
+ MKLINE(VerbSlot, color, sleByte, VER(8)),
+ MKLINE(VerbSlot, hicolor, sleByte, VER(8)),
+ MKLINE(VerbSlot, dimcolor, sleByte, VER(8)),
+ MKLINE(VerbSlot, bkcolor, sleByte, VER(8)),
+ MKLINE(VerbSlot, type, sleByte, VER(8)),
+ MKLINE(VerbSlot, charset_nr, sleByte, VER(8)),
+ MKLINE(VerbSlot, curmode, sleByte, VER(8)),
+ MKLINE(VerbSlot, saveid, sleByte, VER(8)),
+ MKLINE(VerbSlot, key, sleByte, VER(8)),
+ MKLINE(VerbSlot, center, sleByte, VER(8)),
+ MKLINE(VerbSlot, prep, sleByte, VER(8)),
+ MKLINE(VerbSlot, imgindex, sleUint16, VER(8)),
+ MKEND()
+ };
+
+ const SaveLoadEntry mainEntries[] = {
+ MKARRAY(ScummEngine, _gameMD5[0], sleUint8, 16, VER(39)),
+ MK_OBSOLETE(ScummEngine, _roomWidth, sleUint16, VER(8), VER(50)),
+ MK_OBSOLETE(ScummEngine, _roomHeight, sleUint16, VER(8), VER(50)),
+ MK_OBSOLETE(ScummEngine, _ENCD_offs, sleUint32, VER(8), VER(50)),
+ MK_OBSOLETE(ScummEngine, _EXCD_offs, sleUint32, VER(8), VER(50)),
+ MK_OBSOLETE(ScummEngine, _IM00_offs, sleUint32, VER(8), VER(50)),
+ MK_OBSOLETE(ScummEngine, _CLUT_offs, sleUint32, VER(8), VER(50)),
+ MK_OBSOLETE(ScummEngine, _EPAL_offs, sleUint32, VER(8), VER(9)),
+ MK_OBSOLETE(ScummEngine, _PALS_offs, sleUint32, VER(8), VER(50)),
+ MKLINE(ScummEngine, _curPalIndex, sleByte, VER(8)),
+ MKLINE(ScummEngine, _currentRoom, sleByte, VER(8)),
+ MKLINE(ScummEngine, _roomResource, sleByte, VER(8)),
+ MKLINE(ScummEngine, _numObjectsInRoom, sleByte, VER(8)),
+ MKLINE(ScummEngine, _currentScript, sleByte, VER(8)),
+ MK_OBSOLETE_ARRAY(ScummEngine, _localScriptOffsets[0], sleUint32, _numLocalScripts, VER(8), VER(50)),
+
+
+ // vm.localvar grew from 25 to 40 script entries and then from
+ // 16 to 32 bit variables (but that wasn't reflect here)... and
+ // THEN from 16 to 25 variables.
+ MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint16, 17, 25, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(8), VER(8)),
+ MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint16, 17, 40, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(9), VER(14)),
+
+ // We used to save 25 * 40 = 1000 blocks; but actually, each 'row consisted of 26 entry,
+ // i.e. 26 * 40 = 1040. Thus the last 40 blocks of localvar where not saved at all. To be
+ // able to load this screwed format, we use a trick: We load 26 * 38 = 988 blocks.
+ // Then, we mark the followin 12 blocks (24 bytes) as obsolete.
+ MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint16, 26, 38, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(15), VER(17)),
+ MK_OBSOLETE_ARRAY(ScummEngine, vm.localvar[39][0], sleUint16, 12, VER(15), VER(17)),
+
+ // This was the first proper multi dimensional version of the localvars, with 32 bit values
+ MKARRAY2_OLD(ScummEngine, vm.localvar[0][0], sleUint32, 26, 40, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(18), VER(19)),
+
+ // Then we doubled the script slots again, from 40 to 80
+ MKARRAY2(ScummEngine, vm.localvar[0][0], sleUint32, 26, NUM_SCRIPT_SLOT, (byte*)vm.localvar[1] - (byte*)vm.localvar[0], VER(20)),
+
+
+ MKARRAY(ScummEngine, _resourceMapper[0], sleByte, 128, VER(8)),
+ MKARRAY(ScummEngine, _charsetColorMap[0], sleByte, 16, VER(8)),
+
+ // _charsetData grew from 10*16 to 15*16 bytes
+ MKARRAY_OLD(ScummEngine, _charsetData[0][0], sleByte, 10 * 16, VER(8), VER(9)),
+ MKARRAY(ScummEngine, _charsetData[0][0], sleByte, 15 * 16, VER(10)),
+
+ MK_OBSOLETE(ScummEngine, _curExecScript, sleUint16, VER(8), VER(62)),
+
+ MKLINE(ScummEngine, camera._dest.x, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._dest.y, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._cur.x, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._cur.y, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._last.x, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._last.y, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._accel.x, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._accel.y, sleInt16, VER(8)),
+ MKLINE(ScummEngine, _screenStartStrip, sleInt16, VER(8)),
+ MKLINE(ScummEngine, _screenEndStrip, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._mode, sleByte, VER(8)),
+ MKLINE(ScummEngine, camera._follows, sleByte, VER(8)),
+ MKLINE(ScummEngine, camera._leftTrigger, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._rightTrigger, sleInt16, VER(8)),
+ MKLINE(ScummEngine, camera._movingToActor, sleUint16, VER(8)),
+
+ MKLINE(ScummEngine, _actorToPrintStrFor, sleByte, VER(8)),
+ MKLINE(ScummEngine, _charsetColor, sleByte, VER(8)),
+
+ // _charsetBufPos was changed from byte to int
+ MKLINE_OLD(ScummEngine, _charsetBufPos, sleByte, VER(8), VER(9)),
+ MKLINE(ScummEngine, _charsetBufPos, sleInt16, VER(10)),
+
+ MKLINE(ScummEngine, _haveMsg, sleByte, VER(8)),
+ MKLINE(ScummEngine, _haveActorSpeechMsg, sleByte, VER(61)),
+ MKLINE(ScummEngine, _useTalkAnims, sleByte, VER(8)),
+
+ MKLINE(ScummEngine, _talkDelay, sleInt16, VER(8)),
+ MKLINE(ScummEngine, _defaultTalkDelay, sleInt16, VER(8)),
+ MK_OBSOLETE(ScummEngine, _numInMsgStack, sleInt16, VER(8), VER(27)),
+ MKLINE(ScummEngine, _sentenceNum, sleByte, VER(8)),
+
+ MKLINE(ScummEngine, vm.cutSceneStackPointer, sleByte, VER(8)),
+ MKARRAY(ScummEngine, vm.cutScenePtr[0], sleUint32, 5, VER(8)),
+ MKARRAY(ScummEngine, vm.cutSceneScript[0], sleByte, 5, VER(8)),
+ MKARRAY(ScummEngine, vm.cutSceneData[0], sleInt16, 5, VER(8)),
+ MKLINE(ScummEngine, vm.cutSceneScriptIndex, sleInt16, VER(8)),
+
+ MKLINE(ScummEngine, vm.numNestedScripts, sleByte, VER(8)),
+ MKLINE(ScummEngine, _userPut, sleByte, VER(8)),
+ MKLINE(ScummEngine, _userState, sleUint16, VER(17)),
+ MKLINE(ScummEngine, _cursor.state, sleByte, VER(8)),
+ MK_OBSOLETE(ScummEngine, gdi._cursorActive, sleByte, VER(8), VER(20)),
+ MKLINE(ScummEngine, _currentCursor, sleByte, VER(8)),
+ MKARRAY(ScummEngine, _grabbedCursor[0], sleByte, 8192, VER(20)),
+ MKLINE(ScummEngine, _cursor.width, sleInt16, VER(20)),
+ MKLINE(ScummEngine, _cursor.height, sleInt16, VER(20)),
+ MKLINE(ScummEngine, _cursor.hotspotX, sleInt16, VER(20)),
+ MKLINE(ScummEngine, _cursor.hotspotY, sleInt16, VER(20)),
+ MKLINE(ScummEngine, _cursor.animate, sleByte, VER(20)),
+ MKLINE(ScummEngine, _cursor.animateIndex, sleByte, VER(20)),
+ MKLINE(ScummEngine, _mouse.x, sleInt16, VER(20)),
+ MKLINE(ScummEngine, _mouse.y, sleInt16, VER(20)),
+
+ MKARRAY(ScummEngine, _colorUsedByCycle[0], sleByte, 256, VER(60)),
+ MKLINE(ScummEngine, _doEffect, sleByte, VER(8)),
+ MKLINE(ScummEngine, _switchRoomEffect, sleByte, VER(8)),
+ MKLINE(ScummEngine, _newEffect, sleByte, VER(8)),
+ MKLINE(ScummEngine, _switchRoomEffect2, sleByte, VER(8)),
+ MKLINE(ScummEngine, _bgNeedsRedraw, sleByte, VER(8)),
+
+ // The state of palManipulate is stored only since V10
+ MKLINE(ScummEngine, _palManipStart, sleByte, VER(10)),
+ MKLINE(ScummEngine, _palManipEnd, sleByte, VER(10)),
+ MKLINE(ScummEngine, _palManipCounter, sleUint16, VER(10)),
+
+ // gfxUsageBits grew from 200 to 410 entries. Then 3 * 410 entries:
+ MKARRAY_OLD(ScummEngine, gfxUsageBits[0], sleUint32, 200, VER(8), VER(9)),
+ MKARRAY_OLD(ScummEngine, gfxUsageBits[0], sleUint32, 410, VER(10), VER(13)),
+ MKARRAY(ScummEngine, gfxUsageBits[0], sleUint32, 3 * 410, VER(14)),
+
+ MK_OBSOLETE(ScummEngine, gdi._transparentColor, sleByte, VER(8), VER(50)),
+ MKARRAY(ScummEngine, _currentPalette[0], sleByte, 768, VER(8)),
+ MKARRAY(ScummEngine, _darkenPalette[0], sleByte, 768, VER(53)),
+
+ // Sam & Max specific palette replaced by _shadowPalette now.
+ MK_OBSOLETE_ARRAY(ScummEngine, _proc_special_palette[0], sleByte, 256, VER(8), VER(33)),
+
+ MKARRAY(ScummEngine, _charsetBuffer[0], sleByte, 256, VER(8)),
+
+ MKLINE(ScummEngine, _egoPositioned, sleByte, VER(8)),
+
+ // gdi._imgBufOffs grew from 4 to 5 entries. Then one day we realized
+ // that we don't have to store it since initBGBuffers() recomputes it.
+ MK_OBSOLETE_ARRAY(ScummEngine, gdi._imgBufOffs[0], sleUint16, 4, VER(8), VER(9)),
+ MK_OBSOLETE_ARRAY(ScummEngine, gdi._imgBufOffs[0], sleUint16, 5, VER(10), VER(26)),
+
+ // See _imgBufOffs: _numZBuffer is recomputed by initBGBuffers().
+ MK_OBSOLETE(ScummEngine, gdi._numZBuffer, sleByte, VER(8), VER(26)),
+
+ MKLINE(ScummEngine, _screenEffectFlag, sleByte, VER(8)),
+
+ MK_OBSOLETE(ScummEngine, _randSeed1, sleUint32, VER(8), VER(9)),
+ MK_OBSOLETE(ScummEngine, _randSeed2, sleUint32, VER(8), VER(9)),
+
+ // Converted _shakeEnabled to boolean and added a _shakeFrame field.
+ MKLINE_OLD(ScummEngine, _shakeEnabled, sleInt16, VER(8), VER(9)),
+ MKLINE(ScummEngine, _shakeEnabled, sleByte, VER(10)),
+ MKLINE(ScummEngine, _shakeFrame, sleUint32, VER(10)),
+
+ MKLINE(ScummEngine, _keepText, sleByte, VER(8)),
+
+ MKLINE(ScummEngine, _screenB, sleUint16, VER(8)),
+ MKLINE(ScummEngine, _screenH, sleUint16, VER(8)),
+
+ MKLINE(ScummEngine, _NESCostumeSet, sleUint16, VER(47)),
+
+ MK_OBSOLETE(ScummEngine, _cd_track, sleInt16, VER(9), VER(9)),
+ MK_OBSOLETE(ScummEngine, _cd_loops, sleInt16, VER(9), VER(9)),
+ MK_OBSOLETE(ScummEngine, _cd_frame, sleInt16, VER(9), VER(9)),
+ MK_OBSOLETE(ScummEngine, _cd_end, sleInt16, VER(9), VER(9)),
+
+ MKEND()
+ };
+
+ const SaveLoadEntry scriptSlotEntries[] = {
+ MKLINE(ScriptSlot, offs, sleUint32, VER(8)),
+ MKLINE(ScriptSlot, delay, sleInt32, VER(8)),
+ MKLINE(ScriptSlot, number, sleUint16, VER(8)),
+ MKLINE(ScriptSlot, delayFrameCount, sleUint16, VER(8)),
+ MKLINE(ScriptSlot, status, sleByte, VER(8)),
+ MKLINE(ScriptSlot, where, sleByte, VER(8)),
+ MKLINE(ScriptSlot, freezeResistant, sleByte, VER(8)),
+ MKLINE(ScriptSlot, recursive, sleByte, VER(8)),
+ MKLINE(ScriptSlot, freezeCount, sleByte, VER(8)),
+ MKLINE(ScriptSlot, didexec, sleByte, VER(8)),
+ MKLINE(ScriptSlot, cutsceneOverride, sleByte, VER(8)),
+ MKLINE(ScriptSlot, cycle, sleByte, VER(46)),
+ MK_OBSOLETE(ScriptSlot, unk5, sleByte, VER(8), VER(10)),
+ MKEND()
+ };
+
+ const SaveLoadEntry nestedScriptEntries[] = {
+ MKLINE(NestedScript, number, sleUint16, VER(8)),
+ MKLINE(NestedScript, where, sleByte, VER(8)),
+ MKLINE(NestedScript, slot, sleByte, VER(8)),
+ MKEND()
+ };
+
+ const SaveLoadEntry sentenceTabEntries[] = {
+ MKLINE(SentenceTab, verb, sleUint8, VER(8)),
+ MKLINE(SentenceTab, preposition, sleUint8, VER(8)),
+ MKLINE(SentenceTab, objectA, sleUint16, VER(8)),
+ MKLINE(SentenceTab, objectB, sleUint16, VER(8)),
+ MKLINE(SentenceTab, freezeCount, sleUint8, VER(8)),
+ MKEND()
+ };
+
+ const SaveLoadEntry stringTabEntries[] = {
+ // Then _default/restore of a StringTab entry becomes a one liner.
+ MKLINE(StringTab, xpos, sleInt16, VER(8)),
+ MKLINE(StringTab, _default.xpos, sleInt16, VER(8)),
+ MKLINE(StringTab, ypos, sleInt16, VER(8)),
+ MKLINE(StringTab, _default.ypos, sleInt16, VER(8)),
+ MKLINE(StringTab, right, sleInt16, VER(8)),
+ MKLINE(StringTab, _default.right, sleInt16, VER(8)),
+ MKLINE(StringTab, color, sleInt8, VER(8)),
+ MKLINE(StringTab, _default.color, sleInt8, VER(8)),
+ MKLINE(StringTab, charset, sleInt8, VER(8)),
+ MKLINE(StringTab, _default.charset, sleInt8, VER(8)),
+ MKLINE(StringTab, center, sleByte, VER(8)),
+ MKLINE(StringTab, _default.center, sleByte, VER(8)),
+ MKLINE(StringTab, overhead, sleByte, VER(8)),
+ MKLINE(StringTab, _default.overhead, sleByte, VER(8)),
+ MKLINE(StringTab, no_talk_anim, sleByte, VER(8)),
+ MKLINE(StringTab, _default.no_talk_anim, sleByte, VER(8)),
+ MKEND()
+ };
+
+ const SaveLoadEntry colorCycleEntries[] = {
+ MKLINE(ColorCycle, delay, sleUint16, VER(8)),
+ MKLINE(ColorCycle, counter, sleUint16, VER(8)),
+ MKLINE(ColorCycle, flags, sleUint16, VER(8)),
+ MKLINE(ColorCycle, start, sleByte, VER(8)),
+ MKLINE(ColorCycle, end, sleByte, VER(8)),
+ MKEND()
+ };
+
+ const SaveLoadEntry scaleSlotsEntries[] = {
+ MKLINE(ScaleSlot, x1, sleUint16, VER(13)),
+ MKLINE(ScaleSlot, y1, sleUint16, VER(13)),
+ MKLINE(ScaleSlot, scale1, sleUint16, VER(13)),
+ MKLINE(ScaleSlot, x2, sleUint16, VER(13)),
+ MKLINE(ScaleSlot, y2, sleUint16, VER(13)),
+ MKLINE(ScaleSlot, scale2, sleUint16, VER(13)),
+ MKEND()
+ };
+
+ // MSVC6 FIX (Jamieson630):
+ // MSVC6 has a problem with any notation that involves
+ // more than one set of double colons ::
+ // The following MKLINE macros expand to such things
+ // as AudioCDManager::Status::playing, and MSVC6 has
+ // a fit with that. This typedef simplifies the notation
+ // to something MSVC6 can grasp.
+ typedef AudioCDManager::Status AudioCDManager_Status;
+ const SaveLoadEntry audioCDEntries[] = {
+ MKLINE(AudioCDManager_Status, playing, sleUint32, VER(24)),
+ MKLINE(AudioCDManager_Status, track, sleInt32, VER(24)),
+ MKLINE(AudioCDManager_Status, start, sleUint32, VER(24)),
+ MKLINE(AudioCDManager_Status, duration, sleUint32, VER(24)),
+ MKLINE(AudioCDManager_Status, numLoops, sleInt32, VER(24)),
+ MKEND()
+ };
+
+ int i, j;
+ int var120Backup;
+ int var98Backup;
+ uint8 md5Backup[16];
+
+ // MD5 Operations: Backup on load, compare, and reset.
+ if (s->isLoading())
+ memcpy(md5Backup, _gameMD5, 16);
+
+
+ //
+ // Save/load main state (many members of class ScummEngine get saved here)
+ //
+ s->saveLoadEntries(this, mainEntries);
+
+ // MD5 Operations: Backup on load, compare, and reset.
+ if (s->isLoading()) {
+ char md5str1[32+1], md5str2[32+1];
+ for (j = 0; j < 16; j++) {
+ sprintf(md5str1 + j*2, "%02x", (int)_gameMD5[j]);
+ sprintf(md5str2 + j*2, "%02x", (int)md5Backup[j]);
+ }
+
+ debug(2, "Save version: %d", s->getVersion());
+ debug(2, "Saved game MD5: %s", (s->getVersion() >= 39) ? md5str1 : "unknown");
+
+ if (memcmp(md5Backup, _gameMD5, 16) != 0) {
+ warning("Game was saved with different gamedata - you may encounter problems.");
+ debug(1, "You have %s and save is %s.", md5str2, md5str1);
+ memcpy(_gameMD5, md5Backup, 16);
+ }
+ }
+
+
+ // Starting V14, we extended the usage bits, to be able to cope with games
+ // that have more than 30 actors (up to 94 are supported now, in theory).
+ // Since the format of the usage bits was changed by this, we have to
+ // convert them when loading an older savegame.
+ if (s->isLoading() && s->getVersion() < VER(14))
+ upgradeGfxUsageBits();
+
+ // When loading, move the mouse to the saved mouse position.
+ if (s->isLoading() && s->getVersion() >= VER(20)) {
+ updateCursor();
+ _system->warpMouse(_mouse.x, _mouse.y);
+ }
+
+ // Before V61, we re-used the _haveMsg flag to handle "alternative" speech
+ // sound files (see charset code 10).
+ if (s->isLoading() && s->getVersion() < VER(61)) {
+ if (_haveMsg == 0xFE) {
+ _haveActorSpeechMsg = false;
+ _haveMsg = 0xFF;
+ } else {
+ _haveActorSpeechMsg = true;
+ }
+ }
+
+ //
+ // Save/load actors
+ //
+ for (i = 0; i < _numActors; i++)
+ _actors[i].saveLoadWithSerializer(s);
+
+
+ //
+ // Save/load sound data
+ //
+ _sound->saveLoadWithSerializer(s);
+
+
+ //
+ // Save/load script data
+ //
+ if (s->getVersion() < VER(9))
+ s->saveLoadArrayOf(vm.slot, 25, sizeof(vm.slot[0]), scriptSlotEntries);
+ else if (s->getVersion() < VER(20))
+ s->saveLoadArrayOf(vm.slot, 40, sizeof(vm.slot[0]), scriptSlotEntries);
+ else
+ s->saveLoadArrayOf(vm.slot, NUM_SCRIPT_SLOT, sizeof(vm.slot[0]), scriptSlotEntries);
+
+ if (s->getVersion() < VER(46)) {
+ // When loading an old savegame, make sure that the 'cycle'
+ // field is set to something sensible, otherwise the scripts
+ // that were running probably won't be.
+
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++) {
+ vm.slot[i].cycle = 1;
+ }
+ }
+
+
+ //
+ // Save/load local objects
+ //
+ s->saveLoadArrayOf(_objs, _numLocalObjects, sizeof(_objs[0]), objectEntries);
+ if (s->isLoading() && s->getVersion() < VER(13)) {
+ // Since roughly v13 of the save games, the objs storage has changed a bit
+ for (i = _numObjectsInRoom; i < _numLocalObjects; i++) {
+ _objs[i].obj_nr = 0;
+ }
+
+ }
+
+
+ //
+ // Save/load misc stuff
+ //
+ s->saveLoadArrayOf(_verbs, _numVerbs, sizeof(_verbs[0]), verbEntries);
+ s->saveLoadArrayOf(vm.nest, 16, sizeof(vm.nest[0]), nestedScriptEntries);
+ s->saveLoadArrayOf(_sentence, 6, sizeof(_sentence[0]), sentenceTabEntries);
+ s->saveLoadArrayOf(_string, 6, sizeof(_string[0]), stringTabEntries);
+ s->saveLoadArrayOf(_colorCycle, 16, sizeof(_colorCycle[0]), colorCycleEntries);
+ if (s->getVersion() >= VER(13))
+ s->saveLoadArrayOf(_scaleSlots, 20, sizeof(_scaleSlots[0]), scaleSlotsEntries);
+
+
+ //
+ // Save/load resources
+ //
+ int type, idx;
+ if (s->getVersion() >= VER(26)) {
+ // New, more robust resource save/load system. This stores the type
+ // and index of each resource. Thus if we increase e.g. the maximum
+ // number of script resources, savegames won't break.
+ if (s->isSaving()) {
+ for (type = rtFirst; type <= rtLast; type++) {
+ if (res.mode[type] != 1 && type != rtTemp && type != rtBuffer) {
+ s->saveUint16(type); // Save the res type...
+ for (idx = 0; idx < res.num[type]; idx++) {
+ // Only save resources which actually exist...
+ if (res.address[type][idx]) {
+ s->saveUint16(idx); // Save the index of the resource
+ saveResource(s, type, idx);
+ }
+ }
+ s->saveUint16(0xFFFF); // End marker
+ }
+ }
+ s->saveUint16(0xFFFF); // End marker
+ } else {
+ while ((type = s->loadUint16()) != 0xFFFF) {
+ while ((idx = s->loadUint16()) != 0xFFFF) {
+ assert(0 <= idx && idx < res.num[type]);
+ loadResource(s, type, idx);
+ }
+ }
+ }
+ } else {
+ // Old, fragile resource save/load system. Doesn't save resources
+ // with index 0, and breaks whenever we change the limit on a given
+ // resource type.
+ for (type = rtFirst; type <= rtLast; type++)
+ if (res.mode[type] != 1 && type != rtTemp && type != rtBuffer) {
+ // For V1-V5 games, there used to be no object name resources.
+ // At some point this changed. But since old savegames rely on
+ // unchanged resource counts, we have to hard code the following check
+ if (_version < 6 && type == rtObjectName)
+ continue;
+ for (idx = 1; idx < res.num[type]; idx++)
+ saveLoadResource(s, type, idx);
+ }
+ }
+
+
+ //
+ // Save/load global object state
+ //
+ s->saveLoadArrayOf(_objectOwnerTable, _numGlobalObjects, sizeof(_objectOwnerTable[0]), sleByte);
+ s->saveLoadArrayOf(_objectStateTable, _numGlobalObjects, sizeof(_objectStateTable[0]), sleByte);
+ if (_objectRoomTable)
+ s->saveLoadArrayOf(_objectRoomTable, _numGlobalObjects, sizeof(_objectRoomTable[0]), sleByte);
+
+
+ //
+ // Save/load palette data
+ //
+ if (_shadowPaletteSize) {
+ s->saveLoadArrayOf(_shadowPalette, _shadowPaletteSize, 1, sleByte);
+ // _roomPalette didn't show up until V21 save games
+ if (s->getVersion() >= VER(21) && _version < 5)
+ s->saveLoadArrayOf(_roomPalette, sizeof(_roomPalette), 1, sleByte);
+ }
+
+ // PalManip data was not saved before V10 save games
+ if (s->getVersion() < VER(10))
+ _palManipCounter = 0;
+ if (_palManipCounter) {
+ if (!_palManipPalette)
+ _palManipPalette = (byte *)calloc(0x300, 1);
+ if (!_palManipIntermediatePal)
+ _palManipIntermediatePal = (byte *)calloc(0x600, 1);
+ s->saveLoadArrayOf(_palManipPalette, 0x300, 1, sleByte);
+ s->saveLoadArrayOf(_palManipIntermediatePal, 0x600, 1, sleByte);
+ }
+
+ // darkenPalette was not saved before V53
+ if (s->isLoading() && s->getVersion() < VER(53)) {
+ memcpy(_darkenPalette, _currentPalette, 768);
+ }
+
+ // _colorUsedByCycle was not saved before V60
+ if (s->isLoading() && s->getVersion() < VER(60)) {
+ memset(_colorUsedByCycle, 0, sizeof(_colorUsedByCycle));
+ }
+
+ //
+ // Save/load more global object state
+ //
+ s->saveLoadArrayOf(_classData, _numGlobalObjects, sizeof(_classData[0]), sleUint32);
+
+
+ //
+ // Save/load script variables
+ //
+ var120Backup = _scummVars[120];
+ var98Backup = _scummVars[98];
+
+ if (s->getVersion() > VER(37))
+ s->saveLoadArrayOf(_roomVars, _numRoomVariables, sizeof(_roomVars[0]), sleInt32);
+
+ // The variables grew from 16 to 32 bit.
+ if (s->getVersion() < VER(15))
+ s->saveLoadArrayOf(_scummVars, _numVariables, sizeof(_scummVars[0]), sleInt16);
+ else
+ s->saveLoadArrayOf(_scummVars, _numVariables, sizeof(_scummVars[0]), sleInt32);
+
+ if (_gameId == GID_TENTACLE) // Maybe misplaced, but that's the main idea
+ _scummVars[120] = var120Backup;
+ if (_gameId == GID_INDY4)
+ _scummVars[98] = var98Backup;
+
+ s->saveLoadArrayOf(_bitVars, _numBitVariables >> 3, 1, sleByte);
+
+
+ //
+ // Save/load a list of the locked objects
+ //
+ if (s->isSaving()) {
+ for (i = rtFirst; i <= rtLast; i++)
+ for (j = 1; j < res.num[i]; j++) {
+ if (res.isLocked(i, j)) {
+ s->saveByte(i);
+ s->saveUint16(j);
+ }
+ }
+ s->saveByte(0xFF);
+ } else {
+ while ((i = s->loadByte()) != 0xFF) {
+ j = s->loadUint16();
+ res.lock(i, j);
+ }
+ }
+
+
+ //
+ // Save/load the Audio CD status
+ //
+ if (s->getVersion() >= VER(24)) {
+ AudioCDManager::Status info;
+ if (s->isSaving())
+ info = AudioCD.getStatus();
+ s->saveLoadArrayOf(&info, 1, sizeof(info), audioCDEntries);
+ // If we are loading, and the music being loaded was supposed to loop
+ // forever, then resume playing it. This helps a lot when the audio CD
+ // is used to provide ambient music (see bug #788195).
+ if (s->isLoading() && info.playing && info.numLoops < 0)
+ AudioCD.play(info.track, info.numLoops, info.start, info.duration);
+ }
+
+
+ //
+ // Save/load the iMuse status
+ //
+ if (_imuse && (_saveSound || !_saveTemporaryState)) {
+ _imuse->save_or_load(s, this);
+ }
+}
+
+void ScummEngine_v5::saveOrLoad(Serializer *s) {
+ ScummEngine::saveOrLoad(s);
+
+ const SaveLoadEntry cursorEntries[] = {
+ MKARRAY2(ScummEngine_v5, _cursorImages[0][0], sleUint16, 16, 4, (byte*)_cursorImages[1] - (byte*)_cursorImages[0], VER(44)),
+ MKARRAY(ScummEngine_v5, _cursorHotspots[0], sleByte, 8, VER(44)),
+ MKEND()
+ };
+
+ // This is probably only needed for Loom.
+ s->saveLoadEntries(this, cursorEntries);
+}
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::saveOrLoad(Serializer *s) {
+ ScummEngine::saveOrLoad(s);
+
+ const SaveLoadEntry subtitleQueueEntries[] = {
+ MKARRAY(SubtitleText, text[0], sleByte, 256, VER(61)),
+ MKLINE(SubtitleText, charset, sleByte, VER(61)),
+ MKLINE(SubtitleText, color, sleByte, VER(61)),
+ MKLINE(SubtitleText, xpos, sleInt16, VER(61)),
+ MKLINE(SubtitleText, ypos, sleInt16, VER(61)),
+ MKLINE(SubtitleText, actorSpeechMsg, sleByte, VER(61)),
+ MKEND()
+ };
+
+ const SaveLoadEntry V7Entries[] = {
+ MKLINE(ScummEngine_v7, _subtitleQueuePos, sleInt32, VER(61)),
+ MKEND()
+ };
+
+ _imuseDigital->saveOrLoad(s);
+
+ s->saveLoadArrayOf(_subtitleQueue, ARRAYSIZE(_subtitleQueue), sizeof(_subtitleQueue[0]), subtitleQueueEntries);
+ s->saveLoadEntries(this, V7Entries);
+}
+#endif
+
+void ScummEngine_v60he::saveOrLoad(Serializer *s) {
+ ScummEngine::saveOrLoad(s);
+
+ s->saveLoadArrayOf(_arraySlot, _numArray, sizeof(_arraySlot[0]), sleByte);
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v70he::saveOrLoad(Serializer *s) {
+ ScummEngine_v60he::saveOrLoad(s);
+
+ const SaveLoadEntry HE70Entries[] = {
+ MKLINE(ScummEngine_v70he, _heSndSoundId, sleInt32, VER(51)),
+ MKLINE(ScummEngine_v70he, _heSndOffset, sleInt32, VER(51)),
+ MKLINE(ScummEngine_v70he, _heSndChannel, sleInt32, VER(51)),
+ MKLINE(ScummEngine_v70he, _heSndFlags, sleInt32, VER(51)),
+ MKEND()
+ };
+
+ s->saveLoadEntries(this, HE70Entries);
+}
+
+void ScummEngine_v71he::saveOrLoad(Serializer *s) {
+ ScummEngine_v70he::saveOrLoad(s);
+
+ const SaveLoadEntry polygonEntries[] = {
+ MKLINE(WizPolygon, vert[0].x, sleInt16, VER(40)),
+ MKLINE(WizPolygon, vert[0].y, sleInt16, VER(40)),
+ MKLINE(WizPolygon, vert[1].x, sleInt16, VER(40)),
+ MKLINE(WizPolygon, vert[1].y, sleInt16, VER(40)),
+ MKLINE(WizPolygon, vert[2].x, sleInt16, VER(40)),
+ MKLINE(WizPolygon, vert[2].y, sleInt16, VER(40)),
+ MKLINE(WizPolygon, vert[3].x, sleInt16, VER(40)),
+ MKLINE(WizPolygon, vert[3].y, sleInt16, VER(40)),
+ MKLINE(WizPolygon, vert[4].x, sleInt16, VER(40)),
+ MKLINE(WizPolygon, vert[4].y, sleInt16, VER(40)),
+ MKLINE(WizPolygon, bound.left, sleInt16, VER(40)),
+ MKLINE(WizPolygon, bound.top, sleInt16, VER(40)),
+ MKLINE(WizPolygon, bound.right, sleInt16, VER(40)),
+ MKLINE(WizPolygon, bound.bottom, sleInt16, VER(40)),
+ MKLINE(WizPolygon, id, sleInt16, VER(40)),
+ MKLINE(WizPolygon, numVerts, sleInt16, VER(40)),
+ MKLINE(WizPolygon, flag, sleByte, VER(40)),
+ MKEND()
+ };
+
+ s->saveLoadArrayOf(_wiz->_polygons, ARRAYSIZE(_wiz->_polygons), sizeof(_wiz->_polygons[0]), polygonEntries);
+}
+
+void ScummEngine_v90he::saveOrLoad(Serializer *s) {
+ ScummEngine_v71he::saveOrLoad(s);
+
+ const SaveLoadEntry floodFillEntries[] = {
+ MKLINE(FloodFillParameters, box.left, sleInt32, VER(51)),
+ MKLINE(FloodFillParameters, box.top, sleInt32, VER(51)),
+ MKLINE(FloodFillParameters, box.right, sleInt32, VER(51)),
+ MKLINE(FloodFillParameters, box.bottom, sleInt32, VER(51)),
+ MKLINE(FloodFillParameters, x, sleInt32, VER(51)),
+ MKLINE(FloodFillParameters, y, sleInt32, VER(51)),
+ MKLINE(FloodFillParameters, flags, sleInt32, VER(51)),
+ MK_OBSOLETE(FloodFillParameters, unk1C, sleInt32, VER(51), VER(62)),
+ MKEND()
+ };
+
+ const SaveLoadEntry HE90Entries[] = {
+ MKLINE(ScummEngine_v90he, _curMaxSpriteId, sleInt32, VER(51)),
+ MKLINE(ScummEngine_v90he, _curSpriteId, sleInt32, VER(51)),
+ MKLINE(ScummEngine_v90he, _curSpriteGroupId, sleInt32, VER(51)),
+ MK_OBSOLETE(ScummEngine_v90he, _numSpritesToProcess, sleInt32, VER(51), VER(63)),
+ MKLINE(ScummEngine_v90he, _heObject, sleInt32, VER(51)),
+ MKLINE(ScummEngine_v90he, _heObjectNum, sleInt32, VER(51)),
+ MKLINE(ScummEngine_v90he, _hePaletteNum, sleInt32, VER(51)),
+ MKEND()
+ };
+
+ _sprite->saveOrLoadSpriteData(s);
+
+ s->saveLoadArrayOf(&_floodFillParams, 1, sizeof(_floodFillParams), floodFillEntries);
+
+ s->saveLoadEntries(this, HE90Entries);
+}
+
+void ScummEngine_v99he::saveOrLoad(Serializer *s) {
+ ScummEngine_v90he::saveOrLoad(s);
+
+ s->saveLoadArrayOf(_hePalettes, (_numPalettes + 1) * 1024, sizeof(_hePalettes[0]), sleUint8);
+}
+
+void ScummEngine_v100he::saveOrLoad(Serializer *s) {
+ ScummEngine_v99he::saveOrLoad(s);
+
+ const SaveLoadEntry HE100Entries[] = {
+ MKLINE(ScummEngine_v100he, _heResId, sleInt32, VER(51)),
+ MKLINE(ScummEngine_v100he, _heResType, sleInt32, VER(51)),
+ MKEND()
+ };
+
+ s->saveLoadEntries(this, HE100Entries);
+}
+#endif
+
+void ScummEngine::saveLoadResource(Serializer *ser, int type, int idx) {
+ byte *ptr;
+ uint32 size;
+
+ if (!res.mode[type]) {
+ if (ser->isSaving()) {
+ ptr = res.address[type][idx];
+ if (ptr == NULL) {
+ ser->saveUint32(0);
+ return;
+ }
+
+ size = ((MemBlkHeader *)ptr)->size;
+
+ ser->saveUint32(size);
+ ser->saveBytes(ptr + sizeof(MemBlkHeader), size);
+
+ if (type == rtInventory) {
+ ser->saveUint16(_inventory[idx]);
+ }
+ if (type == rtObjectName && ser->getVersion() >= VER(25)) {
+ ser->saveUint16(_newNames[idx]);
+ }
+ } else {
+ size = ser->loadUint32();
+ if (size) {
+ res.createResource(type, idx, size);
+ ser->loadBytes(getResourceAddress(type, idx), size);
+ if (type == rtInventory) {
+ _inventory[idx] = ser->loadUint16();
+ }
+ if (type == rtObjectName && ser->getVersion() >= VER(25)) {
+ // Paranoia: We increased the possible number of new names
+ // to fix bugs #933610 and #936323. The savegame format
+ // didn't change, but at least during the transition
+ // period there is a slight chance that we try to load
+ // more names than we have allocated space for. If so,
+ // discard them.
+ if (idx < _numNewNames)
+ _newNames[idx] = ser->loadUint16();
+ }
+ }
+ }
+ } else if (res.mode[type] == 2 && ser->getVersion() >= VER(23)) {
+ // Save/load only a list of resource numbers that need reloaded.
+ if (ser->isSaving()) {
+ ser->saveUint16(res.address[type][idx] ? 1 : 0);
+ } else {
+ if (ser->loadUint16())
+ ensureResourceLoaded(type, idx);
+ }
+ }
+}
+
+void ScummEngine::saveResource(Serializer *ser, int type, int idx) {
+ assert(res.address[type][idx]);
+
+ if ((res.mode[type] == 0) || (_heversion >= 60 && res.mode[type] == 2 && idx == 1)) {
+ byte *ptr = res.address[type][idx];
+ uint32 size = ((MemBlkHeader *)ptr)->size;
+
+ ser->saveUint32(size);
+ ser->saveBytes(ptr + sizeof(MemBlkHeader), size);
+
+ if (type == rtInventory) {
+ ser->saveUint16(_inventory[idx]);
+ }
+ if (type == rtObjectName) {
+ ser->saveUint16(_newNames[idx]);
+ }
+ }
+}
+
+void ScummEngine::loadResource(Serializer *ser, int type, int idx) {
+ if ((res.mode[type] == 0) || (_heversion >= 60 && res.mode[type] == 2 && idx == 1)) {
+ uint32 size = ser->loadUint32();
+ assert(size);
+ res.createResource(type, idx, size);
+ ser->loadBytes(getResourceAddress(type, idx), size);
+
+ if (type == rtInventory) {
+ _inventory[idx] = ser->loadUint16();
+ }
+ if (type == rtObjectName) {
+ _newNames[idx] = ser->loadUint16();
+ }
+ } else if (res.mode[type] == 2) {
+ ensureResourceLoaded(type, idx);
+ }
+}
+
+void Serializer::saveBytes(void *b, int len) {
+ _saveStream->write(b, len);
+}
+
+void Serializer::loadBytes(void *b, int len) {
+ _loadStream->read(b, len);
+}
+
+void Serializer::saveUint32(uint32 d) {
+ _saveStream->writeUint32LE(d);
+}
+
+void Serializer::saveUint16(uint16 d) {
+ _saveStream->writeUint16LE(d);
+}
+
+void Serializer::saveByte(byte b) {
+ _saveStream->writeByte(b);
+}
+
+uint32 Serializer::loadUint32() {
+ return _loadStream->readUint32LE();
+}
+
+uint16 Serializer::loadUint16() {
+ return _loadStream->readUint16LE();
+}
+
+byte Serializer::loadByte() {
+ return _loadStream->readByte();
+}
+
+void Serializer::saveArrayOf(void *b, int len, int datasize, byte filetype) {
+ byte *at = (byte *)b;
+ uint32 data;
+
+ // speed up byte arrays
+ if (datasize == 1 && filetype == sleByte) {
+ if (len > 0) {
+ saveBytes(b, len);
+ }
+ return;
+ }
+
+ while (--len >= 0) {
+ if (datasize == 0) {
+ // Do nothing for obsolete data
+ data = 0;
+ } else if (datasize == 1) {
+ data = *(byte *)at;
+ at += 1;
+ } else if (datasize == 2) {
+ data = *(uint16 *)at;
+ at += 2;
+ } else if (datasize == 4) {
+ data = *(uint32 *)at;
+ at += 4;
+ } else {
+ error("saveLoadArrayOf: invalid size %d", datasize);
+ }
+ switch (filetype) {
+ case sleByte:
+ saveByte((byte)data);
+ break;
+ case sleUint16:
+ case sleInt16:
+ saveUint16((int16)data);
+ break;
+ case sleInt32:
+ case sleUint32:
+ saveUint32(data);
+ break;
+ default:
+ error("saveLoadArrayOf: invalid filetype %d", filetype);
+ }
+ }
+}
+
+void Serializer::loadArrayOf(void *b, int len, int datasize, byte filetype) {
+ byte *at = (byte *)b;
+ uint32 data;
+
+ // speed up byte arrays
+ if (datasize == 1 && filetype == sleByte) {
+ loadBytes(b, len);
+ return;
+ }
+
+ while (--len >= 0) {
+ switch (filetype) {
+ case sleByte:
+ data = loadByte();
+ break;
+ case sleUint16:
+ data = loadUint16();
+ break;
+ case sleInt16:
+ data = (int16)loadUint16();
+ break;
+ case sleUint32:
+ data = loadUint32();
+ break;
+ case sleInt32:
+ data = (int32)loadUint32();
+ break;
+ default:
+ error("saveLoadArrayOf: invalid filetype %d", filetype);
+ }
+ if (datasize == 0) {
+ // Do nothing for obsolete data
+ } else if (datasize == 1) {
+ *(byte *)at = (byte)data;
+ at += 1;
+ } else if (datasize == 2) {
+ *(uint16 *)at = (uint16)data;
+ at += 2;
+ } else if (datasize == 4) {
+ *(uint32 *)at = data;
+ at += 4;
+ } else {
+ error("saveLoadArrayOf: invalid size %d", datasize);
+ }
+ }
+}
+
+void Serializer::saveLoadArrayOf(void *b, int num, int datasize, const SaveLoadEntry *sle) {
+ byte *data = (byte *)b;
+
+ if (isSaving()) {
+ while (--num >= 0) {
+ saveEntries(data, sle);
+ data += datasize;
+ }
+ } else {
+ while (--num >= 0) {
+ loadEntries(data, sle);
+ data += datasize;
+ }
+ }
+}
+
+void Serializer::saveLoadArrayOf(void *b, int len, int datasize, byte filetype) {
+ if (isSaving())
+ saveArrayOf(b, len, datasize, filetype);
+ else
+ loadArrayOf(b, len, datasize, filetype);
+}
+
+void Serializer::saveLoadEntries(void *d, const SaveLoadEntry *sle) {
+ if (isSaving())
+ saveEntries(d, sle);
+ else
+ loadEntries(d, sle);
+}
+
+void Serializer::saveEntries(void *d, const SaveLoadEntry *sle) {
+ byte type;
+ byte *at;
+ int size;
+
+ while (sle->offs != 0xFFFF) {
+ at = (byte *)d + sle->offs;
+ size = sle->size;
+ type = (byte) sle->type;
+
+ if (sle->maxVersion != CURRENT_VER) {
+ // Skip obsolete entries
+ if (type & 128)
+ sle++;
+ } else {
+ // save entry
+ int columns = 1;
+ int rows = 1;
+ int rowlen = 0;
+ if (type & 128) {
+ sle++;
+ columns = sle->offs;
+ rows = sle->type;
+ rowlen = sle->size;
+ type &= ~128;
+ }
+ while (rows--) {
+ saveArrayOf(at, columns, size, type);
+ at += rowlen;
+ }
+ }
+ sle++;
+ }
+}
+
+void Serializer::loadEntries(void *d, const SaveLoadEntry *sle) {
+ byte type;
+ byte *at;
+ int size;
+
+ while (sle->offs != 0xFFFF) {
+ at = (byte *)d + sle->offs;
+ size = sle->size;
+ type = (byte) sle->type;
+
+ if (_savegameVersion < sle->minVersion || _savegameVersion > sle->maxVersion) {
+ // Skip entries which are not present in this save game version
+ if (type & 128)
+ sle++;
+ } else {
+ // load entry
+ int columns = 1;
+ int rows = 1;
+ int rowlen = 0;
+
+ if (type & 128) {
+ sle++;
+ columns = sle->offs;
+ rows = sle->type;
+ rowlen = sle->size;
+ type &= ~128;
+ }
+ while (rows--) {
+ loadArrayOf(at, columns, size, type);
+ at += rowlen;
+ }
+ }
+ sle++;
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h
new file mode 100644
index 0000000000..dc2c3b5262
--- /dev/null
+++ b/engines/scumm/saveload.h
@@ -0,0 +1,169 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SAVELOAD_H
+#define SAVELOAD_H
+
+#include "common/scummsys.h"
+
+namespace Common {
+ class InSaveFile;
+ class OutSaveFile;
+}
+
+namespace Scumm {
+
+
+/**
+ * The current savegame format version.
+ * Our save/load system uses an elaborate scheme to allow us to modify the
+ * savegame while keeping full backward compatibility, in the sense that newer
+ * ScummVM versions always are able to load old savegames.
+ * In order to achieve that, we store a version in the savegame files, and whenever
+ * the savegame layout is modified, the version is incremented.
+ *
+ * This roughly works by marking each savegame entry with a range of versions
+ * for which it is valid; the save/load code iterates over all entries, but
+ * only saves/loads those which are valid for the version of the savegame
+ * which is being loaded/saved currently.
+ */
+#define CURRENT_VER 64
+
+/**
+ * An auxillary macro, used to specify savegame versions. We use this instead
+ * of just writing the raw version, because this way they stand out more to
+ * the reading eye, making it a bit easier to navigate through the code.
+ */
+#define VER(x) x
+
+
+/**
+ * The OFFS macro essentially provides the functionality of offsetof(), that
+ * is, it determines the offset of a struct/class member within instances of
+ * that class.
+ *
+ * This is a place where we cheat a bit and sacrifice some potential portability
+ * (although so far we haven't encountered any platform where this matters).
+ *
+ * To work around a warning in GCC 3.2 (and 3.1 ?) regarding non-POD types,
+ * we use a small trick: instead of 0 we use 42. Why? Well, it seems newer GCC
+ * versions have a heuristic built in to detect "offset-of" patterns - which is exactly
+ * what our OFFS macro does. Now, for non-POD types this is not really legal, because
+ * member need not be at a fixed offset relative to the variable, even if they are in
+ * current reality (many of our complex structs are non-POD; for an explanation of
+ * what POD means refer to http://www-cpd.fnal.gov/personal/wb/boost/ISOcxx/doc/POD.html)
+ */
+#define OFFS(type,item) (((long)(&((type*)42)->type::item))-42)
+
+/**
+ * Similar to the OFFS macro, this macro computes the size (in bytes) of a
+ * member of a given struct/class type.
+ */
+#define SIZE(type,item) sizeof(((type*)42)->type::item)
+
+// Any item that is still in use automatically gets a maxVersion equal to CURRENT_VER
+#define MKLINE(type,item,saveas,minVer) {OFFS(type,item),saveas,SIZE(type,item),minVer,CURRENT_VER}
+#define MKARRAY(type,item,saveas,dim,minVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,CURRENT_VER}, {dim,1,0,0,0}
+#define MKARRAY2(type,item,saveas,dim,dim2,rowlen,minVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,CURRENT_VER}, {dim,dim2,rowlen,0,0}
+
+// Use this if you have an entry that used to be smaller:
+#define MKLINE_OLD(type,item,saveas,minVer,maxVer) {OFFS(type,item),saveas,SIZE(type,item),minVer,maxVer}
+#define MKARRAY_OLD(type,item,saveas,dim,minVer,maxVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,maxVer}, {dim,1,0,0,0}
+#define MKARRAY2_OLD(type,item,saveas,dim,dim2,rowlen,minVer,maxVer) {OFFS(type,item),128|saveas,SIZE(type,item),minVer,maxVer}, {dim,dim2,rowlen,0,0}
+
+// An obsolete item/array, to be ignored upon load. We retain the type/item params to make it easier to debug.
+// Obsolete items have size == 0.
+#define MK_OBSOLETE(type,item,saveas,minVer,maxVer) {0,saveas,0,minVer,maxVer}
+#define MK_OBSOLETE_ARRAY(type,item,saveas,dim,minVer,maxVer) {0,128|saveas,0,minVer,maxVer}, {dim,1,0,0,0}
+#define MK_OBSOLETE_ARRAY2(type,item,saveas,dim,dim2,rowlen,minVer,maxVer) {0,128|saveas,0,minVer,maxVer}, {dim,dim2,rowlen,0,0}
+
+// End marker
+#define MKEND() {0xFFFF,0xFF,0xFF,0,0}
+
+
+enum {
+ sleByte = 1,
+ sleUint8 = 1,
+ sleInt8 = 1,
+ sleInt16 = 2,
+ sleUint16 = 3,
+ sleInt32 = 4,
+ sleUint32 = 5
+};
+
+struct SaveLoadEntry {
+ uint32 offs; // or: array dimension
+ uint16 type; // or: array dimension 2
+ uint16 size; // or: array row length
+ uint8 minVersion;
+ uint8 maxVersion;
+};
+
+class Serializer {
+public:
+ Serializer(Common::InSaveFile *in, Common::OutSaveFile *out, uint32 savegameVersion)
+ : _loadStream(in), _saveStream(out),
+ _savegameVersion(savegameVersion)
+ { }
+
+ void saveLoadArrayOf(void *b, int len, int datasize, byte filetype);
+ void saveLoadArrayOf(void *b, int num, int datasize, const SaveLoadEntry *sle);
+ void saveLoadEntries(void *d, const SaveLoadEntry *sle);
+
+ bool isSaving() { return (_saveStream != 0); }
+ bool isLoading() { return (_loadStream != 0); }
+ uint32 getVersion() { return _savegameVersion; }
+
+ void saveUint32(uint32 d);
+ void saveUint16(uint16 d);
+ void saveByte(byte b);
+
+ byte loadByte();
+ uint16 loadUint16();
+ uint32 loadUint32();
+
+ void saveBytes(void *b, int len);
+ void loadBytes(void *b, int len);
+
+protected:
+ Common::InSaveFile *_loadStream;
+ Common::OutSaveFile *_saveStream;
+ uint32 _savegameVersion;
+
+ void saveArrayOf(void *b, int len, int datasize, byte filetype);
+ void loadArrayOf(void *b, int len, int datasize, byte filetype);
+
+ void saveEntries(void *d, const SaveLoadEntry *sle);
+ void loadEntries(void *d, const SaveLoadEntry *sle);
+};
+
+
+// Mixin class / interface. Maybe call it ISerializable or SerializableMixin ?
+class Serializable {
+public:
+ virtual ~Serializable() {}
+ virtual void saveLoadWithSerializer(Serializer *ser) = 0;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
new file mode 100644
index 0000000000..c6a971837f
--- /dev/null
+++ b/engines/scumm/script.cpp
@@ -0,0 +1,1355 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+#include "common/util.h"
+
+#include "scumm/actor.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/util.h"
+#include "scumm/scumm.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+/* Start executing script 'script' with the given parameters */
+void ScummEngine::runScript(int script, bool freezeResistant, bool recursive, int *lvarptr, int cycle) {
+ ScriptSlot *s;
+ byte *scriptPtr;
+ uint32 scriptOffs;
+ byte scriptType;
+ int slot;
+
+ if (!script)
+ return;
+
+ if (!recursive)
+ stopScript(script);
+
+ if (script < _numGlobalScripts) {
+ scriptPtr = getResourceAddress(rtScript, script);
+ scriptOffs = _resourceHeaderSize;
+ scriptType = WIO_GLOBAL;
+
+ debugC(DEBUG_SCRIPTS, "runScript(Global-%d) from %d-%d", script,
+ vm.slot[_currentScript].number, _roomResource);
+ } else {
+ scriptOffs = _localScriptOffsets[script - _numGlobalScripts];
+ if (scriptOffs == 0)
+ error("Local script %d is not in room %d", script, _roomResource);
+ scriptType = WIO_LOCAL;
+
+ debugC(DEBUG_SCRIPTS, "runScript(%d) from %d-%d", script,
+ vm.slot[_currentScript].number, _roomResource);
+ }
+
+ if (cycle == 0)
+ cycle = (_heversion >= 90) ? VAR(VAR_SCRIPT_CYCLE) : 1;
+
+ slot = getScriptSlot();
+
+ s = &vm.slot[slot];
+ s->number = script;
+ s->offs = scriptOffs;
+ s->status = ssRunning;
+ s->where = scriptType;
+ s->freezeResistant = freezeResistant;
+ s->recursive = recursive;
+ s->freezeCount = 0;
+ s->delayFrameCount = 0;
+ s->cycle = cycle;
+
+ initializeLocals(slot, lvarptr);
+
+ runScriptNested(slot);
+}
+
+void ScummEngine::runObjectScript(int object, int entry, bool freezeResistant, bool recursive, int *vars, int slot, int cycle) {
+ ScriptSlot *s;
+ uint32 obcd;
+ int where, offs;
+
+ if (!object)
+ return;
+
+ if (!recursive && (_version >= 3))
+ stopObjectScript(object);
+
+ where = whereIsObject(object);
+
+ if (where == WIO_NOT_FOUND) {
+ warning("Code for object %d not in room %d", object, _roomResource);
+ return;
+ }
+
+ obcd = getOBCDOffs(object);
+
+ // Find a free object slot, unless one was specified
+ if (slot == -1)
+ slot = getScriptSlot();
+
+ offs = getVerbEntrypoint(object, entry);
+ if (offs == 0)
+ return;
+
+ if (cycle == 0)
+ cycle = (_heversion >= 90) ? VAR(VAR_SCRIPT_CYCLE) : 1;
+
+ s = &vm.slot[slot];
+ s->number = object;
+ s->offs = obcd + offs;
+ s->status = ssRunning;
+ s->where = where;
+ s->freezeResistant = freezeResistant;
+ s->recursive = recursive;
+ s->freezeCount = 0;
+ s->delayFrameCount = 0;
+ s->cycle = cycle;
+
+ initializeLocals(slot, vars);
+
+ runScriptNested(slot);
+}
+
+void ScummEngine::initializeLocals(int slot, int *vars) {
+ int i;
+ if (!vars) {
+ for (i = 0; i < 25; i++)
+ vm.localvar[slot][i] = 0;
+ } else {
+ for (i = 0; i < 25; i++)
+ vm.localvar[slot][i] = vars[i];
+ }
+}
+
+int ScummEngine::getVerbEntrypoint(int obj, int entry) {
+ const byte *objptr, *verbptr;
+ int verboffs;
+
+ if (whereIsObject(obj) == WIO_NOT_FOUND)
+ return 0;
+
+ objptr = getOBCDFromObject(obj);
+ assert(objptr);
+
+ if (_platform == Common::kPlatformC64 && _gameId == GID_MANIAC)
+ verbptr = objptr + 14;
+ else if (_version <= 2)
+ verbptr = objptr + 15;
+ else if (_features & GF_OLD_BUNDLE)
+ verbptr = objptr + 17;
+ else if (_features & GF_SMALL_HEADER)
+ verbptr = objptr + 19;
+ else
+ verbptr = findResource(MKID('VERB'), objptr);
+
+ assert(verbptr);
+
+ verboffs = verbptr - objptr;
+
+ if (!(_features & GF_SMALL_HEADER))
+ verbptr += _resourceHeaderSize;
+
+ if (_version == 8) {
+ const uint32 *ptr = (const uint32 *)verbptr;
+ uint32 verb;
+ do {
+ verb = READ_LE_UINT32(ptr);
+ if (!verb)
+ return 0;
+ if (verb == (uint32)entry || verb == 0xFFFFFFFF)
+ break;
+ ptr += 2;
+ } while (1);
+ return verboffs + 8 + READ_LE_UINT32(ptr + 1);
+ } else if (_version <= 2) {
+ do {
+ if (!*verbptr)
+ return 0;
+ if (*verbptr == entry || *verbptr == 0xFF)
+ break;
+ verbptr += 2;
+ } while (1);
+
+ return *(verbptr + 1);
+ } else {
+ do {
+ if (!*verbptr)
+ return 0;
+ if (*verbptr == entry || *verbptr == 0xFF)
+ break;
+ verbptr += 3;
+ } while (1);
+
+ if (_features & GF_SMALL_HEADER)
+ return READ_LE_UINT16(verbptr + 1);
+ else
+ return verboffs + READ_LE_UINT16(verbptr + 1);
+ }
+}
+
+/* Stop script 'script' */
+void ScummEngine::stopScript(int script) {
+ ScriptSlot *ss;
+ NestedScript *nest;
+ int i, num;
+
+ if (script == 0)
+ return;
+
+ ss = vm.slot;
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++) {
+ if (script == ss->number && ss->status != ssDead &&
+ (ss->where == WIO_GLOBAL || ss->where == WIO_LOCAL)) {
+ if (ss->cutsceneOverride)
+ if (_version >= 5)
+ error("Script %d stopped with active cutscene/override", script);
+ ss->number = 0;
+ ss->status = ssDead;
+ nukeArrays(i);
+ if (_currentScript == i)
+ _currentScript = 0xFF;
+ }
+ }
+
+ nest = vm.nest;
+ num = vm.numNestedScripts;
+
+ while (num > 0) {
+ if (nest->number == script &&
+ (nest->where == WIO_GLOBAL || nest->where == WIO_LOCAL)) {
+ nukeArrays(nest->slot);
+ nest->number = 0xFF;
+ nest->slot = 0xFF;
+ nest->where = 0xFF;
+ }
+ nest++;
+ num--;
+ }
+}
+
+/* Stop an object script 'script'*/
+void ScummEngine::stopObjectScript(int script) {
+ ScriptSlot *ss;
+ NestedScript *nest;
+ int i, num;
+
+ if (script == 0)
+ return;
+
+ ss = vm.slot;
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++) {
+ if (script == ss->number && ss->status != ssDead &&
+ (ss->where == WIO_ROOM || ss->where == WIO_INVENTORY || ss->where == WIO_FLOBJECT)) {
+ if (ss->cutsceneOverride)
+ if (_version >= 5)
+ error("Object %d stopped with active cutscene/override", script);
+ ss->number = 0;
+ ss->status = ssDead;
+ nukeArrays(i);
+ if (_currentScript == i)
+ _currentScript = 0xFF;
+ }
+ }
+
+ nest = vm.nest;
+ num = vm.numNestedScripts;
+
+ while (num > 0) {
+ if (nest->number == script &&
+ (nest->where == WIO_ROOM || nest->where == WIO_INVENTORY || nest->where == WIO_FLOBJECT)) {
+ nukeArrays(nest->slot);
+ nest->number = 0xFF;
+ nest->slot = 0xFF;
+ nest->where = 0xFF;
+ }
+ nest++;
+ num--;
+ }
+}
+
+/* Return a free script slot */
+int ScummEngine::getScriptSlot() {
+ ScriptSlot *s;
+ int i;
+
+ for (i = 1; i < NUM_SCRIPT_SLOT; i++) {
+ s = &vm.slot[i];
+ if (s->status == ssDead)
+ return i;
+ }
+ error("Too many scripts running, %d max", NUM_SCRIPT_SLOT);
+ return -1;
+}
+
+/* Run script 'script' nested - eg, within the parent script.*/
+void ScummEngine::runScriptNested(int script) {
+ NestedScript *nest;
+ ScriptSlot *slot;
+
+ updateScriptPtr();
+
+ nest = &vm.nest[vm.numNestedScripts];
+
+ if (_currentScript == 0xFF) {
+ nest->number = 0xFF;
+ nest->where = 0xFF;
+ } else {
+ // Store information about the currently running script
+ slot = &vm.slot[_currentScript];
+ nest->number = slot->number;
+ nest->where = slot->where;
+ nest->slot = _currentScript;
+ }
+
+ vm.numNestedScripts++;
+
+ if (vm.numNestedScripts > ARRAYSIZE(vm.nest))
+ error("Too many nested scripts");
+
+ _currentScript = script;
+ getScriptBaseAddress();
+ getScriptEntryPoint();
+ executeScript();
+
+ vm.numNestedScripts--;
+
+ if (nest->number != 0xFF) {
+ // Try to resume the script which called us, if its status has not changed
+ // since it invoked us. In particular, we only resume it if it hasn't been
+ // stopped in the meantime, and if it did not already move on.
+ slot = &vm.slot[nest->slot];
+ if (slot->number == nest->number && slot->where == nest->where &&
+ slot->status != ssDead && slot->freezeCount == 0) {
+ _currentScript = nest->slot;
+ getScriptBaseAddress();
+ getScriptEntryPoint();
+ return;
+ }
+ }
+ _currentScript = 0xFF;
+}
+
+void ScummEngine::updateScriptPtr() {
+ if (_currentScript == 0xFF)
+ return;
+
+ vm.slot[_currentScript].offs = _scriptPointer - _scriptOrgPointer;
+}
+
+/* Nuke arrays based on script */
+void ScummEngine::nukeArrays(byte scriptSlot) {
+ int i;
+
+ if (_heversion == 0 || scriptSlot == 0)
+ return;
+
+ for (i = 1; i < _numArray; i++) {
+ if (_arraySlot[i] == scriptSlot) {
+ res.nukeResource(rtString, i);
+ _arraySlot[i] = 0;
+ }
+ }
+}
+
+/* Get the code pointer to a script */
+void ScummEngine::getScriptBaseAddress() {
+ ScriptSlot *ss;
+ int idx;
+
+ if (_currentScript == 0xFF)
+ return;
+
+ ss = &vm.slot[_currentScript];
+ switch (ss->where) {
+ case WIO_INVENTORY: /* inventory script * */
+ for (idx = 0; idx < _numInventory; idx++)
+ if (_inventory[idx] == ss->number)
+ break;
+ _scriptOrgPointer = getResourceAddress(rtInventory, idx);
+ assert(idx < _numInventory);
+ _lastCodePtr = &res.address[rtInventory][idx];
+ break;
+
+ case WIO_LOCAL:
+ case WIO_ROOM: /* room script */
+ if (_version == 8) {
+ _scriptOrgPointer = getResourceAddress(rtRoomScripts, _roomResource);
+ assert(_roomResource < res.num[rtRoomScripts]);
+ _lastCodePtr = &res.address[rtRoomScripts][_roomResource];
+ } else {
+ _scriptOrgPointer = getResourceAddress(rtRoom, _roomResource);
+ assert(_roomResource < _numRooms);
+ _lastCodePtr = &res.address[rtRoom][_roomResource];
+ }
+ break;
+
+ case WIO_GLOBAL: /* global script */
+ _scriptOrgPointer = getResourceAddress(rtScript, ss->number);
+ assert(ss->number < _numScripts);
+ _lastCodePtr = &res.address[rtScript][ss->number];
+ break;
+
+ case WIO_FLOBJECT: /* flobject script */
+ idx = getObjectIndex(ss->number);
+ assert(idx != -1);
+ idx = _objs[idx].fl_object_index;
+ _scriptOrgPointer = getResourceAddress(rtFlObject, idx);
+ assert(idx < _numFlObject);
+ _lastCodePtr = &res.address[rtFlObject][idx];
+ break;
+ default:
+ error("Bad type while getting base address");
+ }
+
+ // The following fixes bug #1202487. Confirmed against disasm.
+ if (_version <= 2 && _scriptOrgPointer == NULL) {
+ ss->status = ssDead;
+ _currentScript = 0xFF;
+ }
+}
+
+
+void ScummEngine::getScriptEntryPoint() {
+ if (_currentScript == 0xFF)
+ return;
+ _scriptPointer = _scriptOrgPointer + vm.slot[_currentScript].offs;
+}
+
+/* Execute a script - Read opcode, and execute it from the table */
+void ScummEngine::executeScript() {
+ int c;
+ while (_currentScript != 0xFF) {
+
+ if (_showStack == 1) {
+ printf("Stack:");
+ for (c=0; c < _scummStackPos; c++) {
+ printf(" %d", _vmStack[c]);
+ }
+ printf("\n");
+ }
+ _opcode = fetchScriptByte();
+ vm.slot[_currentScript].didexec = 1;
+ debugC(DEBUG_OPCODES, "Script %d, offset 0x%x: [%X] %s()",
+ vm.slot[_currentScript].number,
+ _scriptPointer - _scriptOrgPointer,
+ _opcode,
+ getOpcodeDesc(_opcode));
+ if (_hexdumpScripts == true) {
+ for (c= -1; c < 15; c++) {
+ printf(" %02x", *(_scriptPointer + c));
+ }
+ printf("\n");
+ }
+
+ executeOpcode(_opcode);
+
+ }
+ CHECK_HEAP;
+}
+
+byte ScummEngine::fetchScriptByte() {
+ if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
+ long oldoffs = _scriptPointer - _scriptOrgPointer;
+ getScriptBaseAddress();
+ _scriptPointer = _scriptOrgPointer + oldoffs;
+ }
+ return *_scriptPointer++;
+}
+
+uint ScummEngine::fetchScriptWord() {
+ int a;
+ if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
+ long oldoffs = _scriptPointer - _scriptOrgPointer;
+ getScriptBaseAddress();
+ _scriptPointer = _scriptOrgPointer + oldoffs;
+ }
+ a = READ_LE_UINT16(_scriptPointer);
+ _scriptPointer += 2;
+ return a;
+}
+
+int ScummEngine::fetchScriptWordSigned() {
+ return (int16)fetchScriptWord();
+}
+
+uint ScummEngine::fetchScriptDWord() {
+ int a;
+ if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) {
+ uint32 oldoffs = _scriptPointer - _scriptOrgPointer;
+ getScriptBaseAddress();
+ _scriptPointer = _scriptOrgPointer + oldoffs;
+ }
+ a = READ_LE_UINT32(_scriptPointer);
+ _scriptPointer += 4;
+ return a;
+}
+
+int ScummEngine::fetchScriptDWordSigned() {
+ return (int32)fetchScriptDWord();
+}
+
+int ScummEngine::readVar(uint var) {
+ int a;
+
+ debugC(DEBUG_VARS, "readvar(%d)", var);
+
+ if ((var & 0x2000) && (_version <= 5)) {
+ a = fetchScriptWord();
+ if (a & 0x2000)
+ var += readVar(a & ~0x2000);
+ else
+ var += a & 0xFFF;
+ var &= ~0x2000;
+ }
+
+ if (!(var & 0xF000)) {
+ if (!_copyProtection) {
+ if (var == 490 && _gameId == GID_MONKEY2) {
+ var = 518;
+ }
+ }
+
+ if (VAR_SUBTITLES != 0xFF && var == VAR_SUBTITLES) {
+ return ConfMan.getBool("subtitles");
+ }
+ if (VAR_NOSUBTITLES != 0xFF && var == VAR_NOSUBTITLES) {
+ return !ConfMan.getBool("subtitles");
+ }
+
+ checkRange(_numVariables - 1, 0, var, "Variable %d out of range(r)");
+ return _scummVars[var];
+ }
+
+ if (var & 0x8000) {
+ if (_heversion >= 80) {
+ var &= 0xFFF;
+ checkRange(_numRoomVariables - 1, 0, var, "Room variable %d out of range(w)");
+ return _roomVars[var];
+
+ } else if (_version <= 3 && !(_gameId == GID_INDY3 && (_platform == Common::kPlatformFMTowns))) {
+ int bit = var & 0xF;
+ var = (var >> 4) & 0xFF;
+
+ if (!_copyProtection) {
+ if (_gameId == GID_LOOM && (_platform == Common::kPlatformFMTowns) && var == 214 && bit == 15) {
+ return 0;
+ } else if (_gameId == GID_ZAK && (_platform == Common::kPlatformFMTowns) && var == 151 && bit == 8) {
+ return 0;
+ }
+ }
+
+ checkRange(_numVariables - 1, 0, var, "Variable %d out of range(rzb)");
+ return (_scummVars[ var ] & ( 1 << bit ) ) ? 1 : 0;
+ } else {
+ var &= 0x7FFF;
+ if (!_copyProtection) {
+ if (_gameId == GID_INDY3 && (_platform == Common::kPlatformFMTowns) && var == 1508)
+ return 0;
+ }
+
+ checkRange(_numBitVariables - 1, 0, var, "Bit variable %d out of range(r)");
+ return (_bitVars[var >> 3] & (1 << (var & 7))) ? 1 : 0;
+ }
+ }
+
+ if (var & 0x4000) {
+ if (_features & GF_FEW_LOCALS) {
+ var &= 0xF;
+ } else {
+ var &= 0xFFF;
+ }
+
+ if (_heversion >= 80)
+ checkRange(25, 0, var, "Local variable %d out of range(r)");
+ else
+ checkRange(20, 0, var, "Local variable %d out of range(r)");
+ return vm.localvar[_currentScript][var];
+ }
+
+ error("Illegal varbits (r)");
+ return -1;
+}
+
+void ScummEngine::writeVar(uint var, int value) {
+ debugC(DEBUG_VARS, "writeVar(%d, %d)", var, value);
+
+ if (!(var & 0xF000)) {
+ checkRange(_numVariables - 1, 0, var, "Variable %d out of range(w)");
+
+ if (VAR_SUBTITLES != 0xFF && var == VAR_SUBTITLES) {
+ // Ignore default setting in HE72-73 games
+ if (_heversion <= 73 && vm.slot[_currentScript].number == 1)
+ return;
+ assert(value == 0 || value == 1);
+ ConfMan.set("subtitles", value);
+ }
+ if (VAR_NOSUBTITLES != 0xFF && var == VAR_NOSUBTITLES) {
+ // Ignore default setting in HE60-71 games
+ if (_heversion >= 60 && vm.slot[_currentScript].number == 1)
+ return;
+ assert(value == 0 || value == 1);
+ ConfMan.set("subtitles", !value);
+ }
+
+ if (var == VAR_CHARINC && ConfMan.hasKey("talkspeed")) {
+ uint talkspeed = ConfMan.getInt("talkspeed");
+ if (talkspeed <= 9)
+ VAR(VAR_CHARINC) = talkspeed;
+ } else {
+ _scummVars[var] = value;
+ }
+
+ if ((_varwatch == (int)var) || (_varwatch == 0)) {
+ if (vm.slot[_currentScript].number < 100)
+ debug(1, "vars[%d] = %d (via script-%d)", var, value, vm.slot[_currentScript].number);
+ else
+ debug(1, "vars[%d] = %d (via room-%d-%d)", var, value, _currentRoom,
+ vm.slot[_currentScript].number);
+ }
+ return;
+ }
+
+ if (var & 0x8000) {
+ if (_heversion >= 80) {
+ var &= 0xFFF;
+ checkRange(_numRoomVariables - 1, 0, var, "Room variable %d out of range(w)");
+ _roomVars[var] = value;
+
+ } else if (_version <= 3 && !(_gameId == GID_INDY3 && (_platform == Common::kPlatformFMTowns))) {
+ // In the old games, the bit variables were using the same memory
+ // as the normal variables!
+ int bit = var & 0xF;
+ var = (var >> 4) & 0xFF;
+ checkRange(_numVariables - 1, 0, var, "Variable %d out of range(wzb)");
+ if (value)
+ _scummVars[var] |= ( 1 << bit );
+ else
+ _scummVars[var] &= ~( 1 << bit );
+ } else {
+ var &= 0x7FFF;
+ checkRange(_numBitVariables - 1, 0, var, "Bit variable %d out of range(w)");
+
+ if (value)
+ _bitVars[var >> 3] |= (1 << (var & 7));
+ else
+ _bitVars[var >> 3] &= ~(1 << (var & 7));
+ }
+ return;
+ }
+
+ if (var & 0x4000) {
+ if (_features & GF_FEW_LOCALS) {
+ var &= 0xF;
+ } else {
+ var &= 0xFFF;
+ }
+
+ if (_heversion >= 80)
+ checkRange(25, 0, var, "Local variable %d out of range(w)");
+ else
+ checkRange(20, 0, var, "Local variable %d out of range(w)");
+
+ vm.localvar[_currentScript][var] = value;
+ return;
+ }
+
+ error("Illegal varbits (w)");
+}
+
+void ScummEngine::getResultPos() {
+ int a;
+
+ _resultVarNumber = fetchScriptWord();
+ if (_resultVarNumber & 0x2000) {
+ a = fetchScriptWord();
+ if (a & 0x2000) {
+ _resultVarNumber += readVar(a & ~0x2000);
+ } else {
+ _resultVarNumber += a & 0xFFF;
+ }
+ _resultVarNumber &= ~0x2000;
+ }
+}
+
+void ScummEngine::setResult(int value) {
+ writeVar(_resultVarNumber, value);
+}
+
+void ScummEngine::push(int a) {
+ assert(_scummStackPos >= 0 && _scummStackPos < ARRAYSIZE(_vmStack));
+ debug(9, "push %d", a);
+ _vmStack[_scummStackPos++] = a;
+}
+
+int ScummEngine::pop() {
+ if (_scummStackPos < 1 || _scummStackPos > ARRAYSIZE(_vmStack)) {
+ error("No items on stack to pop() for %s (0x%X) at [%d-%d]", getOpcodeDesc(_opcode), _opcode, _roomResource, vm.slot[_currentScript].number);
+ }
+ --_scummStackPos;
+ debug(9, "pop %d", _vmStack[_scummStackPos]);
+ return _vmStack[_scummStackPos];
+}
+
+void ScummEngine::stopObjectCode() {
+ ScriptSlot *ss;
+
+ ss = &vm.slot[_currentScript];
+ if (ss->cutsceneOverride == 255) { /* FIXME: What does this? */
+ warning("Cutscene for script %d has overflown. Resetting.", ss->number);
+ ss->cutsceneOverride = 0;
+ }
+
+ if (ss->where != WIO_GLOBAL && ss->where != WIO_LOCAL) {
+ if (ss->cutsceneOverride) {
+ if (_version >= 5)
+ warning("Object %d ending with active cutscene/override (%d)", ss->number, ss->cutsceneOverride);
+ ss->cutsceneOverride = 0;
+ }
+ } else {
+ if (ss->cutsceneOverride) {
+ if (_version >= 5)
+ warning("Script %d ending with active cutscene/override (%d)", ss->number, ss->cutsceneOverride);
+ ss->cutsceneOverride = 0;
+ }
+ }
+ nukeArrays(_currentScript);
+ ss->number = 0;
+ ss->status = ssDead;
+ _currentScript = 0xFF;
+}
+
+void ScummEngine::runInventoryScript(int i) {
+ int args[24];
+ memset(args, 0, sizeof(args));
+ args[0] = i;
+ if (VAR(VAR_INVENTORY_SCRIPT)) {
+ if (_gameId == GID_INDY3 && _platform == Common::kPlatformMacintosh) {
+ inventoryScript();
+ } else {
+ runScript(VAR(VAR_INVENTORY_SCRIPT), 0, 0, args);
+ }
+ }
+}
+
+void ScummEngine::inventoryScript() {
+ VerbSlot *vs;
+ int args[24];
+ int j, slot;
+
+ memset(args, 0, sizeof(args));
+
+ if (VAR(67) < 0) {
+ VAR(67) = 0;
+ }
+ args[5] = getInventoryCount(VAR(VAR_EGO));
+ if (args[5] <= 6) {
+ VAR(67) = 0;
+ }
+ if (args[5] >= 6) {
+ args[5] -= 6;
+ }
+ args[6] = 0;
+ if (VAR(67) >= args[5]) {
+ VAR(67) = args[5];
+ args[4] = args[5];
+ args[5] /= 2;
+ args[5] *= 2;
+ args[4] -= args[5];
+ if (args[4]) {
+ VAR(67)++;
+ }
+ args[6]++;
+ }
+ args[2] = 1;
+ for (j = 1; j < 7; j++) {
+ args[1] = (VAR(67) + args[2]);
+ args[3] = findInventory(VAR(VAR_EGO),args[1]);
+ VAR(82 + args[2]) = args[3];
+ args[2]++;
+ }
+
+ byte tmp[6];
+
+ tmp[0] = 0xFF;
+ tmp[1] = 0x06;
+ tmp[3] = 0x00;
+ tmp[4] = 0x00;
+
+ for (j = 0; j < 6; j++) {
+ tmp[2] = 0x53 + j;
+
+ slot = getVerbSlot(101 + j, 0);
+ vs = &_verbs[slot];
+ loadPtrToResource(rtVerb, slot, tmp);
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ vs->curmode = 1;
+ drawVerb(slot, 0);
+ }
+
+ args[5] = getInventoryCount(VAR(VAR_EGO));
+ if (args[5] > 6) {
+ slot = getVerbSlot(107, 0);
+ if (VAR(67)) {
+ vs = &_verbs[slot];
+ vs->curmode = 1;
+ } else {
+ vs = &_verbs[slot];
+ vs->curmode = 0;
+ }
+ drawVerb(slot, 0);
+ slot = getVerbSlot(108, 0);
+ if (!args[6]) {
+ vs = &_verbs[slot];
+ vs->curmode = 1;
+ } else {
+ vs = &_verbs[slot];
+ vs->curmode = 0;
+ }
+ drawVerb(slot, 0);
+ } else {
+ slot = getVerbSlot(107, 0);
+ vs = &_verbs[slot];
+ vs->curmode = 0;
+ drawVerb(slot, 0);
+ slot = getVerbSlot(108, 0);
+ vs = &_verbs[slot];
+ vs->curmode = 0;
+ drawVerb(slot, 0);
+ }
+
+ verbMouseOver(0);
+}
+
+void ScummEngine::freezeScripts(int flag) {
+ int i;
+
+ if (_version <= 2) {
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++) {
+ if (_currentScript != i && vm.slot[i].status != ssDead && !vm.slot[i].freezeResistant) {
+ vm.slot[i].status |= 0x80;
+ vm.slot[i].freezeCount = 1;
+ }
+ }
+ return;
+ }
+
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++) {
+ if (_currentScript != i && vm.slot[i].status != ssDead && (!vm.slot[i].freezeResistant || flag >= 0x80)) {
+ vm.slot[i].status |= 0x80;
+ vm.slot[i].freezeCount++;
+ }
+ }
+
+ for (i = 0; i < NUM_SENTENCE; i++)
+ _sentence[i].freezeCount++;
+
+ if (vm.cutSceneScriptIndex != 0xFF) {
+ vm.slot[vm.cutSceneScriptIndex].status &= 0x7F;
+ vm.slot[vm.cutSceneScriptIndex].freezeCount = 0;
+ }
+}
+
+void ScummEngine::unfreezeScripts() {
+ int i;
+
+ if (_version <= 2) {
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++) {
+ vm.slot[i].status &= 0x7F;
+ vm.slot[i].freezeCount = 0;
+ }
+ return;
+ }
+
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++) {
+ if (vm.slot[i].status & 0x80) {
+ if (!--vm.slot[i].freezeCount) {
+ vm.slot[i].status &= 0x7F;
+ }
+ }
+ }
+
+ for (i = 0; i < NUM_SENTENCE; i++) {
+ if (_sentence[i].freezeCount > 0)
+ _sentence[i].freezeCount--;
+ }
+}
+
+
+void ScummEngine::runAllScripts() {
+ int i;
+
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++)
+ vm.slot[i].didexec = 0;
+
+ _currentScript = 0xFF;
+ int numCycles = (_heversion >= 90) ? VAR(VAR_NUM_SCRIPT_CYCLES) : 1;
+
+ for (int cycle = 1; cycle <= numCycles; cycle++) {
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++) {
+ if (vm.slot[i].cycle == cycle && vm.slot[i].status == ssRunning && vm.slot[i].didexec == 0) {
+ _currentScript = (byte)i;
+ getScriptBaseAddress();
+ getScriptEntryPoint();
+ executeScript();
+ }
+ }
+ }
+}
+
+void ScummEngine::runExitScript() {
+ if (_version > 2 && VAR(VAR_EXIT_SCRIPT))
+ runScript(VAR(VAR_EXIT_SCRIPT), 0, 0, 0);
+ if (_EXCD_offs) {
+ int slot = getScriptSlot();
+ vm.slot[slot].status = ssRunning;
+ vm.slot[slot].number = 10001;
+ vm.slot[slot].where = WIO_ROOM;
+ vm.slot[slot].offs = _EXCD_offs;
+ vm.slot[slot].freezeResistant = false;
+ vm.slot[slot].recursive = false;
+ vm.slot[slot].freezeCount = 0;
+ vm.slot[slot].delayFrameCount = 0;
+ vm.slot[slot].cycle = 1;
+
+ // FIXME: the exit script of room 7 in indy3 only seems to have a size
+ // and tag not actual data not even a 00 (stop code). Maybe we should
+ // be limiting ourselves to strictly reading the size from the header?
+ if (_gameId == GID_INDY3 && !(_features & GF_OLD_BUNDLE)) {
+ byte *roomptr = getResourceAddress(rtRoom, _roomResource);
+ const byte *excd = findResourceData(MKID('EXCD'), roomptr) - _resourceHeaderSize;
+ if (!excd || (getResourceDataSize(excd) < 1)) {
+ debug(2, "Exit-%d is empty", _roomResource);
+ return;
+ }
+ }
+
+ initializeLocals(slot, 0);
+ runScriptNested(slot);
+ }
+ if (_version > 2 && VAR(VAR_EXIT_SCRIPT2))
+ runScript(VAR(VAR_EXIT_SCRIPT2), 0, 0, 0);
+}
+
+void ScummEngine::runEntryScript() {
+ if (_version > 2 && VAR(VAR_ENTRY_SCRIPT))
+ runScript(VAR(VAR_ENTRY_SCRIPT), 0, 0, 0);
+ if (_ENCD_offs) {
+ int slot = getScriptSlot();
+ vm.slot[slot].status = ssRunning;
+ vm.slot[slot].number = 10002;
+ vm.slot[slot].where = WIO_ROOM;
+ vm.slot[slot].offs = _ENCD_offs;
+ vm.slot[slot].freezeResistant = false;
+ vm.slot[slot].recursive = false;
+ vm.slot[slot].freezeCount = 0;
+ vm.slot[slot].delayFrameCount = 0;
+ vm.slot[slot].cycle = 1;
+ initializeLocals(slot, 0);
+ runScriptNested(slot);
+ }
+ if (_version > 2 && VAR(VAR_ENTRY_SCRIPT2))
+ runScript(VAR(VAR_ENTRY_SCRIPT2), 0, 0, 0);
+}
+
+void ScummEngine::killScriptsAndResources() {
+ ScriptSlot *ss;
+ int i;
+
+ ss = vm.slot;
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++) {
+ if (ss->where == WIO_ROOM || ss->where == WIO_FLOBJECT) {
+ if (ss->cutsceneOverride) {
+ if (_version >= 5)
+ warning("Object %d stopped with active cutscene/override in exit", ss->number);
+ ss->cutsceneOverride = 0;
+ }
+ nukeArrays(i);
+ ss->status = ssDead;
+ } else if (ss->where == WIO_LOCAL) {
+ if (ss->cutsceneOverride) {
+ if (_version >= 5)
+ warning("Script %d stopped with active cutscene/override in exit", ss->number);
+ ss->cutsceneOverride = 0;
+ }
+ nukeArrays(i);
+ ss->status = ssDead;
+ }
+ }
+
+ /* Nuke local object names */
+ if (_newNames) {
+ for (i = 0; i < _numNewNames; i++) {
+ const int obj = _newNames[i];
+ if (obj) {
+ const int owner = getOwner(obj);
+ // We can delete custom name resources if either the object is
+ // no longer in use (i.e. not owned by anyone anymore); or if
+ // it is an object which is owned by a room.
+ if (owner == 0 || (_version < 7 && owner == OF_OWNER_ROOM)) {
+ // WORKAROUND for a problem mentioned in bug report #941275:
+ // In FOA in the sentry room, in the chest plate of the statue,
+ // the pegs may be renamed to mouth: this custom name is lost
+ // when leaving the room; this hack prevents this).
+ if (owner == OF_OWNER_ROOM && _gameId == GID_INDY4 && 336 <= obj && obj <= 340)
+ continue;
+
+ _newNames[i] = 0;
+ res.nukeResource(rtObjectName, i);
+ }
+ }
+ }
+ }
+}
+
+void ScummEngine::killAllScriptsExceptCurrent() {
+ for (int i = 0; i < NUM_SCRIPT_SLOT; i++) {
+ if (i != _currentScript) {
+ vm.slot[i].status = ssDead;
+ if (_version == 6)
+ vm.slot[i].cutsceneOverride = 0;
+ }
+ }
+}
+
+void ScummEngine::doSentence(int verb, int objectA, int objectB) {
+ SentenceTab *st;
+
+ if (_version >= 7) {
+
+ if (objectA == objectB)
+ return;
+
+ if (_sentenceNum) {
+ st = &_sentence[_sentenceNum - 1];
+
+ // Check if this doSentence request is identical to the previous one;
+ // if yes, ignore this invocation.
+ if (_sentenceNum && st->verb == verb && st->objectA == objectA && st->objectB == objectB)
+ return;
+ }
+
+ }
+
+ st = &_sentence[_sentenceNum++];
+
+ st->verb = verb;
+ st->objectA = objectA;
+ st->objectB = objectB;
+ st->preposition = (objectB != 0);
+ st->freezeCount = 0;
+}
+
+void ScummEngine::checkAndRunSentenceScript() {
+ int i;
+ int localParamList[24];
+ const ScriptSlot *ss;
+ int sentenceScript;
+ if (_version <= 2)
+ sentenceScript = 2;
+ else
+ sentenceScript = VAR(VAR_SENTENCE_SCRIPT);
+
+ memset(localParamList, 0, sizeof(localParamList));
+ if (isScriptInUse(sentenceScript)) {
+ ss = vm.slot;
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++)
+ if (ss->number == sentenceScript && ss->status != ssDead && ss->freezeCount == 0)
+ return;
+ }
+
+ if (!_sentenceNum || _sentence[_sentenceNum - 1].freezeCount)
+ return;
+
+ _sentenceNum--;
+
+ if (_version < 7)
+ if (_sentence[_sentenceNum].preposition && _sentence[_sentenceNum].objectB == _sentence[_sentenceNum].objectA)
+ return;
+
+ if (_version <= 2) {
+ _scummVars[VAR_ACTIVE_VERB] = _sentence[_sentenceNum].verb;
+ _scummVars[VAR_ACTIVE_OBJECT1] = _sentence[_sentenceNum].objectA;
+ _scummVars[VAR_ACTIVE_OBJECT2] = _sentence[_sentenceNum].objectB;
+ _scummVars[VAR_VERB_ALLOWED] = (0 != getVerbEntrypoint(_sentence[_sentenceNum].objectA, _sentence[_sentenceNum].verb));
+ } else {
+ localParamList[0] = _sentence[_sentenceNum].verb;
+ localParamList[1] = _sentence[_sentenceNum].objectA;
+ localParamList[2] = _sentence[_sentenceNum].objectB;
+
+ // WORKAROUND for bug #1407789. The buggy script clearly
+ // assumes that one of the two objects is an actor. If that's
+ // not the case, fall back on the default sentence script.
+
+ if (_gameId == GID_FT && sentenceScript == _buggyFTSentenceScript && !isValidActor(localParamList[1]) && !isValidActor(localParamList[2])) {
+ sentenceScript = _defaultFTSentenceScript;
+ }
+ }
+ _currentScript = 0xFF;
+ if (sentenceScript)
+ runScript(sentenceScript, 0, 0, localParamList);
+}
+
+void ScummEngine::runInputScript(int a, int cmd, int mode) {
+ int args[24];
+ int verbScript;
+
+ if (_gameId == GID_MANIAC && _platform == Common::kPlatformC64) {
+ verbScript = 3;
+ //_scummVars[9] = cmd;
+
+ } else if (_version <= 2) {
+ verbScript = 4;
+ _scummVars[VAR_CLICK_AREA] = a;
+ switch (a) {
+ case 1: // Verb clicked
+ _scummVars[33] = cmd;
+ break;
+ case 3: // Inventory clicked
+ _scummVars[35] = cmd;
+ break;
+ }
+ } else {
+ verbScript = VAR(VAR_VERB_SCRIPT);
+ }
+
+ memset(args, 0, sizeof(args));
+ args[0] = a;
+ args[1] = cmd;
+ args[2] = mode;
+ // All HE 72+ games but only some HE 71 games.
+ if (_heversion >= 71) {
+ args[3] = VAR(VAR_VIRT_MOUSE_X);
+ args[4] = VAR(VAR_VIRT_MOUSE_Y);
+ }
+
+ // Macintosh verison of indy3ega used different interface, so adjust values.
+ if (_gameId == GID_INDY3 && _platform == Common::kPlatformMacintosh) {
+ if (a == 1 && (cmd >= 101 && cmd <= 108)) {
+ if (cmd == 107) {
+ VAR(67) -= 2;
+ inventoryScript();
+ return;
+ } else if (cmd == 108) {
+ VAR(67) += 2;
+ inventoryScript();
+ return;
+ } else {
+ args[0] = 3;
+ args[1] = VAR(83 + (cmd - 101));
+ }
+ }
+ }
+
+ if (verbScript)
+ runScript(verbScript, 0, 0, args);
+}
+
+void ScummEngine::decreaseScriptDelay(int amount) {
+ ScriptSlot *ss = vm.slot;
+ int i;
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++) {
+ if (ss->status == ssPaused) {
+ ss->delay -= amount;
+ if (ss->delay < 0) {
+ ss->status = ssRunning;
+ ss->delay = 0;
+ }
+ }
+ }
+}
+
+bool ScummEngine::isScriptInUse(int script) const {
+ int i;
+ const ScriptSlot *ss = vm.slot;
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++)
+ if (ss->number == script)
+ return true;
+ return false;
+}
+
+bool ScummEngine::isScriptRunning(int script) const {
+ int i;
+ const ScriptSlot *ss = vm.slot;
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++)
+ if (ss->number == script && (ss->where == WIO_GLOBAL || ss->where == WIO_LOCAL) && ss->status != ssDead)
+ return true;
+ return false;
+}
+
+bool ScummEngine::isRoomScriptRunning(int script) const {
+ int i;
+ const ScriptSlot *ss = vm.slot;
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++)
+ if (ss->number == script && ss->where == WIO_ROOM && ss->status != ssDead)
+ return true;
+ return false;
+}
+
+void ScummEngine::copyScriptString(byte *dst) {
+ int len = resStrLen(_scriptPointer) + 1;
+ while (len--)
+ *dst++ = fetchScriptByte();
+ *dst = 0;
+}
+
+//
+// Given a pointer to a Scumm string, this function returns the total byte length
+// of the string data in that resource. To do so it has to understand certain
+// special characters embedded into the string. The reason for this function is that
+// sometimes this embedded data contains zero bytes, thus we can't just use strlen.
+//
+int ScummEngine::resStrLen(const byte *src) const {
+ int num = 0;
+ byte chr;
+ if (src == NULL)
+ src = _scriptPointer;
+ while ((chr = *src++) != 0) {
+ num++;
+ if (chr == 255) {
+ chr = *src++;
+ num++;
+
+ // WORKAROUND for bug #985948, a script bug in Indy3. See also
+ // the corresponding code in ScummEngine::convertMessageToString().
+ if (_gameId == GID_INDY3 && chr == 0x2E) {
+ continue;
+ }
+
+ if (chr != 1 && chr != 2 && chr != 3 && chr != 8) {
+ if (_version == 8) {
+ src += 4;
+ num += 4;
+ } else {
+ src += 2;
+ num += 2;
+ }
+ }
+ }
+ }
+ return num;
+}
+
+void ScummEngine::beginCutscene(int *args) {
+ int scr = _currentScript;
+ vm.slot[scr].cutsceneOverride++;
+
+ if (++vm.cutSceneStackPointer > ARRAYSIZE(vm.cutSceneData))
+ error("Cutscene stack overflow");
+
+ vm.cutSceneData[vm.cutSceneStackPointer] = args[0];
+ vm.cutSceneScript[vm.cutSceneStackPointer] = 0;
+ vm.cutScenePtr[vm.cutSceneStackPointer] = 0;
+
+ vm.cutSceneScriptIndex = scr;
+ if (VAR(VAR_CUTSCENE_START_SCRIPT))
+ runScript(VAR(VAR_CUTSCENE_START_SCRIPT), 0, 0, args);
+ vm.cutSceneScriptIndex = 0xFF;
+}
+
+void ScummEngine::endCutscene() {
+ ScriptSlot *ss = &vm.slot[_currentScript];
+ int args[16];
+
+ if (ss->cutsceneOverride > 0) // Only terminate if active
+ ss->cutsceneOverride--;
+
+ memset(args, 0, sizeof(args));
+ args[0] = vm.cutSceneData[vm.cutSceneStackPointer];
+
+ VAR(VAR_OVERRIDE) = 0;
+
+ if (vm.cutScenePtr[vm.cutSceneStackPointer] && (ss->cutsceneOverride > 0)) // Only terminate if active
+ ss->cutsceneOverride--;
+
+ vm.cutSceneScript[vm.cutSceneStackPointer] = 0;
+ vm.cutScenePtr[vm.cutSceneStackPointer] = 0;
+ vm.cutSceneStackPointer--;
+
+ if (VAR(VAR_CUTSCENE_END_SCRIPT))
+ runScript(VAR(VAR_CUTSCENE_END_SCRIPT), 0, 0, args);
+}
+
+void ScummEngine::abortCutscene() {
+ const int idx = vm.cutSceneStackPointer;
+ assert(0 <= idx && idx < 5);
+
+ uint32 offs = vm.cutScenePtr[idx];
+ if (offs) {
+ ScriptSlot *ss = &vm.slot[vm.cutSceneScript[idx]];
+ ss->offs = offs;
+ ss->status = ssRunning;
+ ss->freezeCount = 0;
+
+ if (ss->cutsceneOverride > 0)
+ ss->cutsceneOverride--;
+
+ VAR(VAR_OVERRIDE) = 1;
+ vm.cutScenePtr[idx] = 0;
+
+ // HACK to fix issues with SMUSH and the way it does keyboard handling.
+ // In particular, normally abortCutscene() is being called while no
+ // scripts are active. But SMUSH runs from *inside* the script engine.
+ // And it calls abortCutscene() if ESC is pressed... not good.
+ // Proper fix might be to let SMUSH/INSANE run from outside the script
+ // engine but that would require lots of changes and may actually have
+ // negative effects, too. So we cheat here, to fix bug #751670.
+ if (_version == 7)
+ getScriptEntryPoint();
+
+ }
+}
+
+void ScummEngine::beginOverride() {
+ const int idx = vm.cutSceneStackPointer;
+ assert(0 <= idx && idx < 5);
+
+ vm.cutScenePtr[idx] = _scriptPointer - _scriptOrgPointer;
+ vm.cutSceneScript[idx] = _currentScript;
+
+ // Skip the jump instruction following the override instruction
+ // (the jump is responsible for "skipping" cutscenes, and the reason
+ // why we record the current script position in vm.cutScenePtr).
+ fetchScriptByte();
+ fetchScriptWord();
+
+ // This is based on disassembly
+ VAR(VAR_OVERRIDE) = 0;
+}
+
+void ScummEngine::endOverride() {
+ const int idx = vm.cutSceneStackPointer;
+ assert(0 <= idx && idx < 5);
+
+ vm.cutScenePtr[idx] = 0;
+ vm.cutSceneScript[idx] = 0;
+
+ if (_version > 3)
+ VAR(VAR_OVERRIDE) = 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script.h b/engines/scumm/script.h
new file mode 100644
index 0000000000..e832aee8d9
--- /dev/null
+++ b/engines/scumm/script.h
@@ -0,0 +1,84 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCRIPT_H
+#define SCRIPT_H
+
+#include "base/engine.h"
+
+namespace Scumm {
+
+/**
+ * The number of script slots, which determines the maximal number
+ * of concurrently running scripts.
+ * WARNING: Do NOT changes this value unless you really have to, as
+ * this will break savegame compatibility if done carelessly. If you
+ * have to change it, make sure you update saveload.cpp accordingly!
+ */
+enum {
+ NUM_SCRIPT_SLOT = 80
+};
+
+/* Script status type (slot.status) */
+enum {
+ ssDead = 0,
+ ssPaused = 1,
+ ssRunning = 2
+};
+
+struct ScriptSlot {
+ uint32 offs;
+ int32 delay;
+ uint16 number;
+ uint16 delayFrameCount;
+ bool freezeResistant, recursive;
+ bool didexec;
+ byte status;
+ byte where;
+ byte freezeCount;
+ byte cutsceneOverride;
+ byte cycle;
+};
+
+struct NestedScript {
+ uint16 number;
+ uint8 where;
+ uint8 slot;
+};
+
+struct VirtualMachineState {
+ uint32 cutScenePtr[5];
+ byte cutSceneScript[5];
+ int16 cutSceneData[5];
+ int16 cutSceneScriptIndex;
+ byte cutSceneStackPointer;
+ ScriptSlot slot[NUM_SCRIPT_SLOT];
+ int32 localvar[NUM_SCRIPT_SLOT][26];
+
+ NestedScript nest[15];
+ byte numNestedScripts;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/script_c64.cpp b/engines/scumm/script_c64.cpp
new file mode 100644
index 0000000000..2e8ff5f4d6
--- /dev/null
+++ b/engines/scumm/script_c64.cpp
@@ -0,0 +1,849 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern.h"
+#include "scumm/object.h"
+#include "scumm/scumm.h"
+#include "scumm/util.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_c64, x)
+
+void ScummEngine_c64::setupOpcodes() {
+ static const OpcodeEntryC64 opcodes[256] = {
+ /* 00 */
+ OPCODE(o5_stopObjectCode),
+ OPCODE(o2_putActor),
+ OPCODE(o5_startMusic),
+ OPCODE(o_doSentence),
+ /* 04 */
+ OPCODE(o_isGreaterEqual),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_getDist),
+ OPCODE(o5_getActorRoom),
+ /* 08 */
+ OPCODE(o_isNotEqual),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_setActorBitVar),
+ /* 0C */
+ OPCODE(o_loadSound),
+ OPCODE(o_printEgo_c64),
+ OPCODE(o_putActorAtObject),
+ OPCODE(o2_clearState02),
+ /* 10 */
+ OPCODE(o5_breakHere),
+ OPCODE(o_animateActor),
+ OPCODE(o2_panCameraTo),
+ OPCODE(o_lockActor),
+ /* 14 */
+ OPCODE(o_print_c64),
+ OPCODE(o2_actorFromPos),
+ OPCODE(o5_getRandomNr),
+ OPCODE(o_clearState08),
+ /* 18 */
+ OPCODE(o_jumpRelative),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_move),
+ OPCODE(o_getActorBitVar),
+ /* 1C */
+ OPCODE(o5_startSound),
+ OPCODE(o2_setBitVar),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifState04),
+ /* 20 */
+ OPCODE(o5_stopMusic),
+ OPCODE(o2_putActor),
+ OPCODE(o5_saveLoadGame),
+ OPCODE(o_stopCurrentScript),
+ /* 24 */
+ OPCODE(o_unknown2),
+ OPCODE(o5_loadRoom),
+ OPCODE(o_getClosestObjActor),
+ OPCODE(o2_getActorY),
+ /* 28 */
+ OPCODE(o_equalZero),
+ OPCODE(o2_setOwnerOf),
+ OPCODE(o2_delay),
+ OPCODE(o_setActorBitVar),
+ /* 2C */
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o_print_c64),
+ OPCODE(o2_ifState08),
+ /* 30 */
+ OPCODE(o_loadActor),
+ OPCODE(o2_getBitVar),
+ OPCODE(o2_setCameraAt),
+ OPCODE(o_lockScript),
+ /* 34 */
+ OPCODE(o5_getDist),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o2_walkActorToObject),
+ OPCODE(o2_clearState04),
+ /* 38 */
+ OPCODE(o_isLessEqual),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o2_subtract),
+ OPCODE(o_stopCurrentScript),
+ /* 3C */
+ OPCODE(o5_stopSound),
+ OPCODE(o2_setBitVar),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifState02),
+ /* 40 */
+ OPCODE(o2_cutscene),
+ OPCODE(o2_putActor),
+ OPCODE(o2_startScript),
+ OPCODE(o_doSentence),
+ /* 44 */
+ OPCODE(o_isLess),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_increment),
+ OPCODE(o2_getActorX),
+ /* 48 */
+ OPCODE(o_isEqual),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_loadRoom),
+ OPCODE(o_setActorBitVar),
+ /* 4C */
+ OPCODE(o_loadScript),
+ OPCODE(o_lockRoom),
+ OPCODE(o_putActorAtObject),
+ OPCODE(o2_clearState02),
+ /* 50 */
+ OPCODE(o_nop),
+ OPCODE(o_animateActor),
+ OPCODE(o5_actorFollowCamera),
+ OPCODE(o_lockSound),
+ /* 54 */
+ OPCODE(o5_setObjectName),
+ OPCODE(o2_actorFromPos),
+ OPCODE(o_getActorMoving),
+ OPCODE(o_clearState08),
+ /* 58 */
+ OPCODE(o_beginOverride),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o2_add),
+ OPCODE(o_getActorBitVar),
+ /* 5C */
+ OPCODE(o5_startSound),
+ OPCODE(o2_setBitVar),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifState04),
+ /* 60 */
+ OPCODE(o_cursorCommand),
+ OPCODE(o2_putActor),
+ OPCODE(o2_stopScript),
+ OPCODE(o_stopCurrentScript),
+ /* 64 */
+ OPCODE(o_unknown3),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_getClosestObjActor),
+ OPCODE(o5_getActorFacing),
+ /* 68 */
+ OPCODE(o5_isScriptRunning),
+ OPCODE(o2_setOwnerOf),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_setActorBitVar),
+ /* 6C */
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o2_dummy),
+ OPCODE(o2_ifState08),
+ /* 70 */
+ OPCODE(o_lights),
+ OPCODE(o2_getBitVar),
+ OPCODE(o_nop),
+ OPCODE(o5_getObjectOwner),
+ /* 74 */
+ OPCODE(o5_getDist),
+ OPCODE(o_printEgo_c64),
+ OPCODE(o2_walkActorToObject),
+ OPCODE(o2_clearState04),
+ /* 78 */
+ OPCODE(o_isGreater),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_stopCurrentScript),
+ /* 7C */
+ OPCODE(o5_isSoundRunning),
+ OPCODE(o2_setBitVar),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifNotState02),
+ /* 80 */
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o2_putActor),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_doSentence),
+ /* 84 */
+ OPCODE(o_isGreaterEqual),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_badOpcode),
+ OPCODE(o5_getActorRoom),
+ /* 88 */
+ OPCODE(o_isNotEqual),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_setActorBitVar),
+ /* 8C */
+ OPCODE(o_loadSound),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_putActorAtObject),
+ OPCODE(o2_setState02),
+ /* 90 */
+ OPCODE(o2_pickupObject),
+ OPCODE(o_animateActor),
+ OPCODE(o2_panCameraTo),
+ OPCODE(o_unlockActor),
+ /* 94 */
+ OPCODE(o5_print),
+ OPCODE(o2_actorFromPos),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_setState08),
+ /* 98 */
+ OPCODE(o2_restart),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_move),
+ OPCODE(o_getActorBitVar),
+ /* 9C */
+ OPCODE(o5_startSound),
+ OPCODE(o2_setBitVar),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifNotState04),
+ /* A0 */
+ OPCODE(o5_stopObjectCode),
+ OPCODE(o2_putActor),
+ OPCODE(o5_saveLoadGame),
+ OPCODE(o_stopCurrentScript),
+ /* A4 */
+ OPCODE(o_unknown2),
+ OPCODE(o5_loadRoom),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o2_getActorY),
+ /* A8 */
+ OPCODE(o_notEqualZero),
+ OPCODE(o2_setOwnerOf),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_setActorBitVar),
+ /* AC */
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o_print_c64),
+ OPCODE(o2_ifNotState08),
+ /* B0 */
+ OPCODE(o_loadActor),
+ OPCODE(o2_getBitVar),
+ OPCODE(o2_setCameraAt),
+ OPCODE(o_unlockScript),
+ /* B4 */
+ OPCODE(o5_getDist),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o2_walkActorToObject),
+ OPCODE(o2_setState04),
+ /* B8 */
+ OPCODE(o_isLessEqual),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o2_subtract),
+ OPCODE(o_stopCurrentScript),
+ /* BC */
+ OPCODE(o5_stopSound),
+ OPCODE(o2_setBitVar),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifNotState02),
+ /* C0 */
+ OPCODE(o2_endCutscene),
+ OPCODE(o2_putActor),
+ OPCODE(o2_startScript),
+ OPCODE(o_doSentence),
+ /* C4 */
+ OPCODE(o_isLess),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_decrement),
+ OPCODE(o2_getActorX),
+ /* C8 */
+ OPCODE(o_isEqual),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_loadRoom),
+ OPCODE(o_setActorBitVar),
+ /* CC */
+ OPCODE(o_loadScript),
+ OPCODE(o_unlockRoom),
+ OPCODE(o_putActorAtObject),
+ OPCODE(o2_setState02),
+ /* D0 */
+ OPCODE(o_nop),
+ OPCODE(o_animateActor),
+ OPCODE(o5_actorFollowCamera),
+ OPCODE(o_unlockSound),
+ /* D4 */
+ OPCODE(o5_setObjectName),
+ OPCODE(o2_actorFromPos),
+ OPCODE(o_getActorMoving),
+ OPCODE(o_setState08),
+ /* D8 */
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o2_add),
+ OPCODE(o_getActorBitVar),
+ /* DC */
+ OPCODE(o5_startSound),
+ OPCODE(o2_setBitVar),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifNotState04),
+ /* E0 */
+ OPCODE(o_cursorCommand),
+ OPCODE(o2_putActor),
+ OPCODE(o2_stopScript),
+ OPCODE(o_stopCurrentScript),
+ /* E4 */
+ OPCODE(o_unknown3),
+ OPCODE(o_loadRoomWithEgo),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_getActorFacing),
+ /* E8 */
+ OPCODE(o5_isScriptRunning),
+ OPCODE(o2_setOwnerOf),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_setActorBitVar),
+ /* EC */
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o2_dummy),
+ OPCODE(o2_ifNotState08),
+ /* F0 */
+ OPCODE(o_lights),
+ OPCODE(o2_getBitVar),
+ OPCODE(o_nop),
+ OPCODE(o5_getObjectOwner),
+ /* F4 */
+ OPCODE(o5_getDist),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o2_walkActorToObject),
+ OPCODE(o2_setState04),
+ /* F8 */
+ OPCODE(o_isGreater),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_stopCurrentScript),
+ OPCODE(o_stopCurrentScript),
+ /* FC */
+ OPCODE(o5_isSoundRunning),
+ OPCODE(o2_setBitVar),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifState02)
+ };
+
+ _opcodesC64 = opcodes;
+}
+
+#define SENTENCE_SCRIPT 2
+
+#define PARAM_1 0x80
+#define PARAM_2 0x40
+#define PARAM_3 0x20
+
+void ScummEngine_c64::executeOpcode(byte i) {
+ OpcodeProcC64 op = _opcodesC64[i].proc;
+ (this->*op) ();
+}
+
+int ScummEngine_c64::getVarOrDirectWord(byte mask) {
+ return getVarOrDirectByte(mask);
+}
+
+uint ScummEngine_c64::fetchScriptWord() {
+ return fetchScriptByte();
+}
+
+const char *ScummEngine_c64::getOpcodeDesc(byte i) {
+ return _opcodesC64[i].desc;
+}
+
+int ScummEngine_c64::getObjectFlag() {
+ if (_opcode & 0x40)
+ return _activeObject;
+ return fetchScriptByte();
+}
+
+void ScummEngine_c64::decodeParseString() {
+ byte buffer[512];
+ byte *ptr = buffer;
+ byte c;
+ bool insertSpace = false;
+
+ while ((c = fetchScriptByte())) {
+
+ insertSpace = (c & 0x80) != 0;
+ c &= 0x7f;
+
+ if (c == '/') {
+ *ptr++ = 13;
+ } else {
+ *ptr++ = c;
+ }
+
+ if (insertSpace)
+ *ptr++ = ' ';
+
+ }
+ *ptr = 0;
+
+ int textSlot = 0;
+ _string[textSlot].xpos = 0;
+ _string[textSlot].ypos = 0;
+ _string[textSlot].right = 320;
+ _string[textSlot].center = false;
+ _string[textSlot].overhead = false;
+
+ if (_actorToPrintStrFor == 0xFF)
+ _string[textSlot].color = 14;
+
+ actorTalk(buffer);
+}
+
+void ScummEngine_c64::setStateCommon(byte type) {
+ int obj = getObjectFlag();
+ putState(obj, getState(obj) | type);
+}
+
+void ScummEngine_c64::clearStateCommon(byte type) {
+ int obj = getObjectFlag();
+ putState(obj, getState(obj) & ~type);
+}
+
+void ScummEngine_c64::ifStateCommon(byte type) {
+ int obj = getObjectFlag();
+
+ if ((getState(obj) & type) == 0) {
+ o_jumpRelative();
+ } else {
+ fetchScriptByte();
+ fetchScriptByte();
+ }
+}
+
+void ScummEngine_c64::ifNotStateCommon(byte type) {
+ int obj = getObjectFlag();
+
+ if ((getState(obj) & type) != 0) {
+ o_jumpRelative();
+ } else {
+ fetchScriptByte();
+ fetchScriptByte();
+ }
+}
+
+void ScummEngine_c64::o_setState08() {
+ int obj = getObjectFlag();
+ putState(obj, getState(obj) | 0x08);
+ markObjectRectAsDirty(obj);
+ clearDrawObjectQueue();
+}
+
+void ScummEngine_c64::o_clearState08() {
+ int obj = getObjectFlag();
+ putState(obj, getState(obj) & ~0x08);
+ markObjectRectAsDirty(obj);
+ clearDrawObjectQueue();
+}
+
+void ScummEngine_c64::o_stopCurrentScript() {
+ int script;
+
+ script = vm.slot[_currentScript].number;
+
+ if (_currentScript != 0 && vm.slot[_currentScript].number == script)
+ stopObjectCode();
+ else
+ stopScript(script);
+}
+
+void ScummEngine_c64::o_loadSound() {
+ int resid = fetchScriptByte();
+ ensureResourceLoaded(rtSound, resid);
+}
+
+void ScummEngine_c64::o_lockSound() {
+ int resid = fetchScriptByte();
+ res.lock(rtSound, resid);
+ debug(0, "o_lockSound (%d)", resid);
+}
+
+void ScummEngine_c64::o_unlockSound() {
+ int resid = fetchScriptByte();
+ res.unlock(rtSound, resid);
+ debug(0, "o_unlockSound (%d)", resid);
+}
+
+void ScummEngine_c64::o_loadActor() {
+ debug(0, "o_loadActor (%d)", getVarOrDirectByte(PARAM_1));
+}
+
+void ScummEngine_c64::o_lockActor() {
+ debug(0, "o_lockActor (%d)", fetchScriptByte());
+}
+
+void ScummEngine_c64::o_unlockActor() {
+ debug(0, "o_unlockActor (%d)", fetchScriptByte());
+}
+
+void ScummEngine_c64::o_loadScript() {
+ int resid = getVarOrDirectByte(PARAM_1);
+ ensureResourceLoaded(rtScript, resid);
+}
+
+void ScummEngine_c64::o_lockScript() {
+ int resid = fetchScriptByte();
+ res.lock(rtScript, resid);
+ debug(0, "o_lockScript (%d)", resid);
+}
+
+void ScummEngine_c64::o_unlockScript() {
+ int resid = fetchScriptByte();
+ res.unlock(rtScript, resid);
+ debug(0, "o_unlockScript (%d)", resid);
+}
+
+void ScummEngine_c64::o_loadRoom() {
+ int resid = getVarOrDirectByte(PARAM_1);
+ ensureResourceLoaded(rtRoom, resid);
+}
+
+void ScummEngine_c64::o_loadRoomWithEgo() {
+ Actor *a;
+ int obj, room, x, y, dir;
+
+ obj = fetchScriptByte();
+ room = fetchScriptByte();
+
+ a = derefActor(VAR(VAR_EGO), "o_loadRoomWithEgo");
+
+ a->putActor(0, 0, room);
+ _egoPositioned = false;
+
+ startScene(a->_room, a, obj);
+
+ getObjectXYPos(obj, x, y, dir);
+ a->putActor(x, y, _currentRoom);
+ a->setDirection(dir + 180);
+
+ camera._dest.x = camera._cur.x = a->_pos.x;
+ setCameraAt(a->_pos.x, a->_pos.y);
+ setCameraFollows(a);
+
+ _fullRedraw = true;
+
+ resetSentence();
+
+ if (x >= 0 && y >= 0) {
+ a->startWalkActor(x, y, -1);
+ }
+}
+
+void ScummEngine_c64::o_lockRoom() {
+ int resid = fetchScriptByte();
+ res.lock(rtRoom, resid);
+ debug(0, "o_lockRoom (%d)", resid);
+}
+
+void ScummEngine_c64::o_unlockRoom() {
+ int resid = fetchScriptByte();
+ res.unlock(rtRoom, resid);
+ debug(0, "o_unlockRoom (%d)", resid);
+}
+
+void ScummEngine_c64::o_cursorCommand() {
+ // TODO
+ int state = 0;
+
+ _currentMode = fetchScriptByte();
+ switch (_currentMode) {
+ case 0:
+ state = 15;
+ break;
+ case 1:
+ state = 31;
+ break;
+ case 2:
+ break;
+ case 3:
+ state = 247;
+ break;
+ }
+
+ setUserState(state);
+ debug(0, "o_cursorCommand(%d)", _currentMode);
+}
+
+void ScummEngine_c64::o_lights() {
+ int a;
+
+ a = getVarOrDirectByte(PARAM_1);
+ // Convert older light mode values into
+ // equivalent values.of later games
+ // 0 Darkness
+ // 1 Flashlight
+ // 2 Lighted area
+ if (a == 2)
+ VAR(VAR_CURRENT_LIGHTS) = 11;
+ else if (a == 1)
+ VAR(VAR_CURRENT_LIGHTS) = 4;
+ else
+ VAR(VAR_CURRENT_LIGHTS) = 0;
+
+ _fullRedraw = true;
+}
+
+void ScummEngine_c64::o_animateActor() {
+ int act = getVarOrDirectByte(PARAM_1);
+ int anim = getVarOrDirectByte(PARAM_2);
+ int unk = fetchScriptByte();
+ debug(0,"o_animateActor: unk %d", unk);
+
+ Actor *a = derefActor(act, "o_animateActor");
+ a->animateActor(anim);
+}
+
+void ScummEngine_c64::o_getActorMoving() {
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o_getActorMoving");
+ if (a->_moving)
+ setResult(1);
+ else
+ setResult(2);
+}
+
+void ScummEngine_c64::o_putActorAtObject() {
+ int obj, x, y;
+ Actor *a;
+
+ a = derefActor(getVarOrDirectByte(PARAM_1), "o_putActorAtObject");
+
+ obj = fetchScriptByte();
+ if (whereIsObject(obj) != WIO_NOT_FOUND)
+ getObjectXYPos(obj, x, y);
+ else {
+ x = 30;
+ y = 60;
+ }
+
+ a->putActor(x, y, a->_room);
+}
+
+void ScummEngine_c64::o_badOpcode() {
+ warning("Bad opcode 0x86 encountered");
+}
+
+void ScummEngine_c64::o_nop() {
+}
+
+void ScummEngine_c64::o_setActorBitVar() {
+ byte flag = getVarOrDirectByte(PARAM_1);
+ byte mask = getVarOrDirectByte(PARAM_2);
+ byte mod = getVarOrDirectByte(PARAM_3);
+
+ //if (mod)
+ // _miscFlags[flag] |= mask;
+ //else
+ // _miscFlags[flag] &= ~mash;
+
+ warning("STUB: o_setActorBitVar(%d, %d, %d)", flag, mask, mod);
+}
+
+void ScummEngine_c64::o_getActorBitVar() {
+ getResultPos();
+ byte flag = getVarOrDirectByte(PARAM_1);
+ byte mask = getVarOrDirectByte(PARAM_2);
+
+ //setResult((_miscFlags[flag] & mask) ? 1 : 0);
+
+ setResult(0);
+ warning("STUB: o_getActorBitVar(%d, %d)", flag, mask);
+}
+
+void ScummEngine_c64::o_print_c64() {
+ _actorToPrintStrFor = fetchScriptByte();
+ decodeParseString();
+}
+
+void ScummEngine_c64::o_printEgo_c64() {
+ _actorToPrintStrFor = (byte)VAR(VAR_EGO);
+ decodeParseString();
+}
+
+void ScummEngine_c64::o_doSentence() {
+ byte var1 = fetchScriptByte();
+ byte var2 = fetchScriptByte();
+ byte var3 = fetchScriptByte();
+ warning("STUB: o_doSentence(%d, %d, %d)", var1, var2, var3);
+}
+
+void ScummEngine_c64::o_unknown2() {
+ byte var1 = fetchScriptByte();
+ warning("STUB: o_unknown2(%d)", var1);
+}
+
+void ScummEngine_c64::o_unknown3() {
+ byte var1 = fetchScriptByte();
+ warning("STUB: o_unknown3(%d)", var1);
+}
+
+void ScummEngine_c64::o_getClosestObjActor() {
+ int obj;
+ int act;
+ int dist;
+
+ // This code can't detect any actors farther away than 255 units
+ // (pixels in newer games, characters in older ones.) But this is
+ // perfectly OK, as it is exactly how the original behaved.
+
+ int closest_obj = 0xFF, closest_dist = 0xFF;
+
+ getResultPos();
+
+ act = getVarOrDirectByte(PARAM_1);
+ obj = (_opcode & 0x40) ? 25 : 7;
+
+ do {
+ dist = getObjActToObjActDist(act, obj);
+ if (dist < closest_dist) {
+ closest_dist = dist;
+ closest_obj = obj;
+ }
+ } while (--obj);
+
+ setResult(closest_obj);
+}
+
+void ScummEngine_c64::o_beginOverride() {
+ const int idx = vm.cutSceneStackPointer;
+ assert(0 <= idx && idx < 5);
+
+ vm.cutScenePtr[idx] = _scriptPointer - _scriptOrgPointer;
+ vm.cutSceneScript[idx] = _currentScript;
+
+ // Skip the jump instruction following the override instruction
+ // (the jump is responsible for "skipping" cutscenes, and the reason
+ // why we record the current script position in vm.cutScenePtr).
+ fetchScriptByte();
+ ScummEngine::fetchScriptWord();
+
+ // This is based on disassembly
+ VAR(VAR_OVERRIDE) = 0;
+}
+
+void ScummEngine_c64::o_isEqual() {
+ int16 a, b;
+ int var;
+
+ var = fetchScriptByte();
+ a = readVar(var);
+ b = getVarOrDirectByte(PARAM_1);
+
+ if (b == a)
+ ScummEngine::fetchScriptWord();
+ else
+ o_jumpRelative();
+
+}
+
+void ScummEngine_c64::o_isGreater() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectByte(PARAM_1);
+ if (b > a)
+ ScummEngine::fetchScriptWord();
+ else
+ o_jumpRelative();
+}
+
+void ScummEngine_c64::o_isGreaterEqual() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectByte(PARAM_1);
+ if (b >= a)
+ ScummEngine::fetchScriptWord();
+ else
+ o_jumpRelative();
+}
+
+void ScummEngine_c64::o_isLess() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectByte(PARAM_1);
+ if (b < a)
+ ScummEngine::fetchScriptWord();
+ else
+ o_jumpRelative();
+}
+
+void ScummEngine_c64::o_isLessEqual() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectByte(PARAM_1);
+
+ if (b <= a)
+ ScummEngine::fetchScriptWord();
+ else
+ o_jumpRelative();
+}
+
+void ScummEngine_c64::o_isNotEqual() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectByte(PARAM_1);
+ if (b != a)
+ ScummEngine::fetchScriptWord();
+ else
+ o_jumpRelative();
+}
+
+void ScummEngine_c64::o_notEqualZero() {
+ int a = getVar();
+ if (a != 0)
+ ScummEngine::fetchScriptWord();
+ else
+ o_jumpRelative();
+}
+
+void ScummEngine_c64::o_equalZero() {
+ int a = getVar();
+ if (a == 0)
+ ScummEngine::fetchScriptWord();
+ else
+ o_jumpRelative();
+}
+
+void ScummEngine_c64::o_jumpRelative() {
+ int16 offset = (int16)ScummEngine::fetchScriptWord();
+ _scriptPointer += offset;
+}
+
+
+
+#undef PARAM_1
+#undef PARAM_2
+#undef PARAM_3
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v100he.cpp b/engines/scumm/script_v100he.cpp
new file mode 100644
index 0000000000..790f65e021
--- /dev/null
+++ b/engines/scumm/script_v100he.cpp
@@ -0,0 +1,2958 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/sprite_he.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v100he, x)
+
+void ScummEngine_v100he::setupOpcodes() {
+ static const OpcodeEntryV100he opcodes[256] = {
+ /* 00 */
+ OPCODE(o100_actorOps),
+ OPCODE(o6_add),
+ OPCODE(o6_faceActor),
+ OPCODE(o90_sortArray),
+ /* 04 */
+ OPCODE(o100_arrayOps),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ OPCODE(o6_breakHere),
+ /* 08 */
+ OPCODE(o6_delayFrames),
+ OPCODE(o90_shl),
+ OPCODE(o90_shr),
+ OPCODE(o90_xor),
+ /* 0C */
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_loadRoom),
+ OPCODE(o6_panCameraTo),
+ /* 10 */
+ OPCODE(o72_captureWizImage),
+ OPCODE(o100_jumpToScript),
+ OPCODE(o6_setClass),
+ OPCODE(o60_closeFile),
+ /* 14 */
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o72_setFilePath),
+ OPCODE(o100_createSound),
+ /* 18 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_pop),
+ OPCODE(o72_traceStatus),
+ OPCODE(o6_wordVarDec),
+ /* 1C */
+ OPCODE(o6_wordArrayDec),
+ OPCODE(o72_deleteFile),
+ OPCODE(o100_dim2dimArray),
+ OPCODE(o100_dimArray),
+ /* 20 */
+ OPCODE(o6_div),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ OPCODE(o6_drawBox),
+ /* 24 */
+ OPCODE(o72_drawWizImage),
+ OPCODE(o80_drawWizPolygon),
+ OPCODE(o100_drawLine),
+ OPCODE(o100_drawObject),
+ /* 28 */
+ OPCODE(o6_dup),
+ OPCODE(o90_dup_n),
+ OPCODE(o6_endCutscene),
+ OPCODE(o6_stopObjectCode),
+ /* 2C */
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_eq),
+ OPCODE(o100_floodFill),
+ OPCODE(o6_freezeUnfreeze),
+ /* 30 */
+ OPCODE(o6_ge),
+ OPCODE(o6_getDateTime),
+ OPCODE(o100_setSpriteGroupInfo),
+ OPCODE(o6_gt),
+ /* 34 */
+ OPCODE(o100_resourceRoutines),
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o100_wizImageOps),
+ /* 38 */
+ OPCODE(o72_isAnyOf),
+ OPCODE(o6_wordVarInc),
+ OPCODE(o6_wordArrayInc),
+ OPCODE(o6_jump),
+ /* 3C */
+ OPCODE(o90_kernelSetFunctions),
+ OPCODE(o6_land),
+ OPCODE(o6_le),
+ OPCODE(o60_localizeArrayToScript),
+ /* 40 */
+ OPCODE(o6_wordArrayRead),
+ OPCODE(o6_wordArrayIndexedRead),
+ OPCODE(o6_lor),
+ OPCODE(o6_lt),
+ /* 44 */
+ OPCODE(o90_mod),
+ OPCODE(o6_mul),
+ OPCODE(o6_neq),
+ OPCODE(o100_dim2dim2Array),
+ /* 48 */
+ OPCODE(o6_setObjectName),
+ OPCODE(o100_redim2dimArray),
+ OPCODE(o6_not),
+ OPCODE(o6_invalid),
+ /* 4C */
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o72_resetCutscene),
+ OPCODE(o6_setOwner),
+ /* 50 */
+ OPCODE(o100_paletteOps),
+ OPCODE(o70_pickupObject),
+ OPCODE(o70_polygonOps),
+ OPCODE(o6_pop),
+ /* 54 */
+ OPCODE(o6_printDebug),
+ OPCODE(o72_printWizImage),
+ OPCODE(o6_printLine),
+ OPCODE(o6_printSystem),
+ /* 58 */
+ OPCODE(o6_printText),
+ OPCODE(o100_jumpToScriptUnk),
+ OPCODE(o100_startScriptUnk),
+ OPCODE(o6_pseudoRoom),
+ /* 5C */
+ OPCODE(o6_pushByte),
+ OPCODE(o72_pushDWord),
+ OPCODE(o72_getScriptString),
+ OPCODE(o6_pushWord),
+ /* 60 */
+ OPCODE(o6_pushWordVar),
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_putActorAtXY),
+ OPCODE(o6_invalid),
+ /* 64 */
+ OPCODE(o100_redimArray),
+ OPCODE(o72_rename),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o80_localizeArrayToRoom),
+ /* 68 */
+ OPCODE(o100_roomOps),
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o72_talkActor),
+ /* 6C */
+ OPCODE(o72_talkEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o6_setBoxFlags),
+ /* 70 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_setBoxSet),
+ OPCODE(o72_setWindowCaption),
+ OPCODE(o6_shuffle),
+ /* 74 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o100_startSound),
+ /* 78 */
+ OPCODE(o80_sourceDebug),
+ OPCODE(o100_setSpriteInfo),
+ OPCODE(o6_stampObject),
+ OPCODE(o72_startObject),
+ /* 7C */
+ OPCODE(o100_startScript),
+ OPCODE(o6_startScriptQuick),
+ OPCODE(o80_setState),
+ OPCODE(o6_stopObjectScript),
+ /* 80 */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_stopSentence),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_stopTalking),
+ /* 84 */
+ OPCODE(o6_writeWordVar),
+ OPCODE(o6_wordArrayWrite),
+ OPCODE(o6_wordArrayIndexedWrite),
+ OPCODE(o6_sub),
+ /* 88 */
+ OPCODE(o100_systemOps),
+ OPCODE(o6_invalid),
+ OPCODE(o72_setTimer),
+ OPCODE(o100_cursorCommand),
+ /* 8C */
+ OPCODE(o100_videoOps),
+ OPCODE(o100_wait),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ /* 90 */
+ OPCODE(o100_writeFile),
+ OPCODE(o72_writeINI),
+ OPCODE(o80_writeConfigFile),
+ OPCODE(o6_abs),
+ /* 94 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getObjectOldDir),
+ /* 98 */
+ OPCODE(o6_getActorMoving),
+ OPCODE(o90_getActorData),
+ OPCODE(o6_getActorRoom),
+ OPCODE(o6_getActorScaleX),
+ /* 9C */
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_getActorWidth),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ /* A0 */
+ OPCODE(o90_atan2),
+ OPCODE(o90_getSegmentAngle),
+ OPCODE(o90_getActorAnimProgress),
+ OPCODE(o90_getDistanceBetweenPoints),
+ /* A4 */
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_invalid),
+ OPCODE(o90_cond),
+ OPCODE(o90_cos),
+ /* A8 */
+ OPCODE(o6_invalid),
+ OPCODE(o80_getFileSize),
+ OPCODE(o6_getActorFromXY),
+ OPCODE(o72_findAllObjects),
+ /* AC */
+ OPCODE(o90_findAllObjectsWithClassOf),
+ OPCODE(o6_invalid),
+ OPCODE(o6_findInventory),
+ OPCODE(o72_findObject),
+ /* B0 */
+ OPCODE(o72_findObjectWithClassOf),
+ OPCODE(o70_polygonHit),
+ OPCODE(o90_getLinesIntersectionPoint),
+ OPCODE(o90_fontUnk),
+ /* B4 */
+ OPCODE(o72_getNumFreeArrays),
+ OPCODE(o72_getArrayDimSize),
+ OPCODE(o100_isResourceLoaded),
+ OPCODE(o100_getResourceSize),
+ /* B8 */
+ OPCODE(o100_getSpriteGroupInfo),
+ OPCODE(o6_invalid),
+ OPCODE(o100_getWizData),
+ OPCODE(o6_isActorInBox),
+ /* BC */
+ OPCODE(o6_isAnyOf),
+ OPCODE(o6_getInventoryCount),
+ OPCODE(o90_kernelGetFunctions),
+ OPCODE(o90_max),
+ /* C0 */
+ OPCODE(o90_min),
+ OPCODE(o72_getObjectImageX),
+ OPCODE(o72_getObjectImageY),
+ OPCODE(o6_isRoomScriptRunning),
+ /* C4 */
+ OPCODE(o90_getObjectData),
+ OPCODE(o72_openFile),
+ OPCODE(o90_getPolygonOverlap),
+ OPCODE(o6_getOwner),
+ /* C8 */
+ OPCODE(o100_getPaletteData),
+ OPCODE(o6_pickOneOf),
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o80_pickVarRandom),
+ /* CC */
+ OPCODE(o72_getPixel),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* D0 */
+ OPCODE(o6_getRandomNumber),
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o100_readFile),
+ /* D4 */
+ OPCODE(o72_readINI),
+ OPCODE(o80_readConfigFile),
+ OPCODE(o6_isScriptRunning),
+ OPCODE(o90_sin),
+ /* D8 */
+ OPCODE(o72_getSoundPosition),
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o80_getSoundVar),
+ OPCODE(o100_getSpriteInfo),
+ /* DC */
+ OPCODE(o90_sqrt),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ OPCODE(o6_getState),
+ /* E0 */
+ OPCODE(o70_compareString),
+ OPCODE(o70_copyString),
+ OPCODE(o70_appendString),
+ OPCODE(o70_concatString),
+ /* E4 */
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o80_stringToInt),
+ OPCODE(o70_getCharIndexInString),
+ /* E8 */
+ OPCODE(o70_getStringWidth),
+ OPCODE(o60_readFilePos),
+ OPCODE(o72_getTimer),
+ OPCODE(o6_getVerbEntrypoint),
+ /* EC */
+ OPCODE(o100_getVideoData),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F0 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F4 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* FC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV100he = opcodes;
+}
+
+void ScummEngine_v100he::executeOpcode(byte i) {
+ OpcodeProcV100he op = _opcodesV100he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v100he::getOpcodeDesc(byte i) {
+ return _opcodesV100he[i].desc;
+}
+
+void ScummEngine_v100he::o100_actorOps() {
+ Actor *a;
+ int i, j, k;
+ int args[32];
+ byte string[256];
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 129) {
+ _curActor = pop();
+ return;
+ }
+
+ a = derefActorSafe(_curActor, "o100_actorOps");
+ if (!a)
+ return;
+
+ switch (subOp) {
+ case 0:
+ // freddicove Ru Updated
+ // FIXME: check stack parameters
+ debug(0,"o100_actorOps: case 0 UNHANDLED");
+ break;
+ case 3:
+ pop();
+ pop();
+ pop();
+ break;
+ case 4: // SO_ANIMATION_SPEED
+ a->setAnimSpeed(pop());
+ break;
+ case 6:
+ j = pop();
+ i = pop();
+ a->putActor(i, j, a->_room);
+ break;
+ case 8:
+ a->_drawToBackBuf = false;
+ a->_needRedraw = true;
+ a->_needBgReset = true;
+ break;
+ case 9:
+ a->drawActorToBackBuf(a->_pos.x, a->_pos.y);
+ break;
+ case 14:
+ a->_charset = pop();
+ break;
+ case 18:
+ a->_clipOverride.bottom = pop();
+ a->_clipOverride.right = pop();
+ a->_clipOverride.top = pop();
+ a->_clipOverride.left = pop();
+ break;
+ case 22:
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; ++i) {
+ a->setUserCondition(args[i] & 0x7F, args[i] & 0x80);
+ }
+ break;
+ case 25: // SO_COSTUME
+ a->setActorCostume(pop());
+ break;
+ case 27: // SO_DEFAULT
+ a->initActor(0);
+ break;
+ case 32:
+ i = pop();
+ debug(0,"o100_actorOps: case 32 (%d)", i);
+ break;
+ case 52: // SO_ACTOR_NAME
+ copyScriptString(string, sizeof(string));
+ loadPtrToResource(rtActorName, a->_number, string);
+ break;
+ case 53: // SO_ACTOR_NEW
+ a->initActor(2);
+ break;
+ case 57: // SO_PALETTE
+ j = pop();
+ i = pop();
+ checkRange(255, 0, i, "o100_actorOps: Illegal palette slot %d");
+ a->remapActorPaletteColor(i, j);
+ a->_needRedraw = true;
+ break;
+ case 59:
+ // HE games use reverse order of layering, so we adjust
+ a->_layer = -pop();
+ a->_needRedraw = true;
+ break;
+ case 63:
+ a->_hePaletteNum = pop();
+ a->_needRedraw = true;
+ break;
+ case 65: // SO_SCALE
+ i = pop();
+ a->setScale(i, i);
+ break;
+ case 70: // SO_SHADOW
+ a->_heXmapNum = pop();
+ a->_needRedraw = true;
+ break;
+ case 74: // SO_STEP_DIST
+ j = pop();
+ i = pop();
+ a->setActorWalkSpeed(i, j);
+ break;
+ case 78:
+ {
+ copyScriptString(string, sizeof(string));
+ int slot = pop();
+
+ int len = resStrLen(string) + 1;
+ memcpy(a->_heTalkQueue[slot].sentence, string, len);
+
+ a->_heTalkQueue[slot].posX = a->_talkPosX;
+ a->_heTalkQueue[slot].posY = a->_talkPosY;
+ a->_heTalkQueue[slot].color = a->_talkColor;
+ }
+ break;
+ case 83: // SO_ACTOR_VARIABLE
+ i = pop();
+ a->setAnimVar(pop(), i);
+ break;
+ case 87: // SO_ALWAYS_ZCLIP
+ a->_forceClip = pop();
+ break;
+ case 89: // SO_NEVER_ZCLIP
+ a->_forceClip = 0;
+ break;
+ case 128:
+ _actorClipOverride.bottom = pop();
+ _actorClipOverride.right = pop();
+ _actorClipOverride.top = pop();
+ _actorClipOverride.left = pop();
+ break;
+ case 130: // SO_SOUND
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; i++)
+ a->_sound[i] = args[i];
+ break;
+ case 131: // SO_ACTOR_WIDTH
+ a->_width = pop();
+ break;
+ case 132: // SO_ANIMATION_DEFAULT
+ a->_initFrame = 1;
+ a->_walkFrame = 2;
+ a->_standFrame = 3;
+ a->_talkStartFrame = 4;
+ a->_talkStopFrame = 5;
+ break;
+ case 133: // SO_ELEVATION
+ a->setElevation(pop());
+ break;
+ case 134: // SO_FOLLOW_BOXES
+ a->_ignoreBoxes = 0;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 135: // SO_IGNORE_BOXES
+ a->_ignoreBoxes = 1;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 136: // SO_ACTOR_IGNORE_TURNS_OFF
+ a->_ignoreTurns = false;
+ break;
+ case 137: // SO_ACTOR_IGNORE_TURNS_ON
+ a->_ignoreTurns = true;
+ break;
+ case 138: // SO_INIT_ANIMATION
+ a->_initFrame = pop();
+ break;
+ case 139: // SO_STAND_ANIMATION
+ a->_standFrame = pop();
+ break;
+ case 140: // SO_TALK_ANIMATION
+ a->_talkStopFrame = pop();
+ a->_talkStartFrame = pop();
+ break;
+ case 141: // SO_TALK_COLOR
+ a->_talkColor = pop();
+ break;
+ case 142:
+ k = pop();
+ if (k == 0)
+ k = _rnd.getRandomNumberRng(1, 10);
+ a->_heNoTalkAnimation = 1;
+ a->setTalkCondition(k);
+ break;
+ case 143: // SO_TEXT_OFFSET
+ a->_talkPosY = pop();
+ a->_talkPosX = pop();
+ break;
+ case 144: // SO_WALK_ANIMATION
+ a->_walkFrame = pop();
+ break;
+ default:
+ error("o100_actorOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_arrayOps() {
+ ArrayHeader *ah;
+ byte string[1024];
+ int dim1end, dim1start, dim2end, dim2start;
+ int id, len, b, c, list[128];
+ int offs, tmp, tmp2;
+ uint tmp3;
+
+ byte subOp = fetchScriptByte();
+ int array = fetchScriptWord();
+ debug(9,"o100_arrayOps: array %d case %d", array, subOp);
+
+ switch (subOp) {
+ case 35:
+ decodeScriptString(string);
+ len = resStrLen(string);
+ ah = defineArray(array, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ break;
+ case 77: // SO_ASSIGN_STRING
+ copyScriptString(string, sizeof(string));
+ len = resStrLen(string);
+ ah = defineArray(array, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ break;
+
+ case 128: // SO_ASSIGN_2DIM_LIST
+ len = getStackList(list, ARRAYSIZE(list));
+ id = readVar(array);
+ if (id == 0)
+ error("Must DIM a two dimensional array before assigning");
+ c = pop();
+ while (--len >= 0) {
+ writeArray(array, c, len, list[len]);
+ }
+ break;
+ case 129: // SO_ASSIGN_INT_LIST
+ b = pop();
+ c = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, 0, 0, 0, b + c - 1);
+ }
+ while (c--) {
+ writeArray(array, 0, b + c, pop());
+ }
+ break;
+ case 130:
+ len = getStackList(list, ARRAYSIZE(list));
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
+ }
+ tmp2 = 0;
+ while (dim2start <= dim2end) {
+ tmp = dim1start;
+ while (tmp <= dim1end) {
+ writeArray(array, dim2start, tmp, list[tmp2++]);
+ if (tmp2 == len)
+ tmp2 = 0;
+ tmp++;
+ }
+ dim2start++;
+ }
+ break;
+ case 131:
+ {
+ int a2_dim1end = pop();
+ int a2_dim1start = pop();
+ int a2_dim2end = pop();
+ int a2_dim2start = pop();
+ int array2 = fetchScriptWord();
+ int a1_dim1end = pop();
+ int a1_dim1start = pop();
+ int a1_dim2end = pop();
+ int a1_dim2start = pop();
+ if (a1_dim1end - a1_dim1start != a2_dim1end - a2_dim1start || a2_dim2end - a2_dim2start != a1_dim2end - a1_dim2start) {
+ error("Source and dest ranges size are mismatched");
+ }
+ copyArray(array, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end, array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
+ }
+ break;
+ case 133:
+ b = pop();
+ c = pop();
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
+ }
+
+ offs = (b >= c) ? 1 : -1;
+ tmp2 = c;
+ tmp3 = c - b + 1;
+ while (dim2start <= dim2end) {
+ tmp = dim1start;
+ while (tmp <= dim1end) {
+ writeArray(array, dim2start, tmp, tmp2);
+ if (--tmp3 == 0) {
+ tmp2 = c;
+ tmp3 = c - b + 1;
+ } else {
+ tmp2 += offs;
+ }
+ tmp++;
+ }
+ dim2start++;
+ }
+ break;
+ default:
+ error("o100_arrayOps: default case %d (array %d)", subOp, array);
+ }
+}
+
+void ScummEngine_v100he::o100_jumpToScript() {
+ int args[25];
+ int script;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = fetchScriptByte();
+ stopObjectCode();
+ runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args);
+}
+
+void ScummEngine_v100he::o100_createSound() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _heSndResId = pop();
+ break;
+ case 53:
+ createSound(_heSndResId, -1);
+ break;
+ case 92:
+ // dummy case
+ break;
+ case 128:
+ createSound(_heSndResId, pop());
+ break;
+ default:
+ error("o100_createSound: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_dim2dimArray() {
+ int data, dim1end, dim2end;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 41: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 42: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 43:
+ data = kDwordArray;
+ break;
+ case 44: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 45: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 77: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ default:
+ error("o100_dim2dimArray: default case %d", subOp);
+ }
+
+ dim1end = pop();
+ dim2end = pop();
+ defineArray(fetchScriptWord(), data, 0, dim2end, 0, dim1end);
+}
+
+void ScummEngine_v100he::o100_dimArray() {
+ int data;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 41: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 42: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 43:
+ data = kDwordArray;
+ break;
+ case 44: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 45: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 77: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ case 135: // SO_UNDIM_ARRAY
+ nukeArray(fetchScriptWord());
+ return;
+ default:
+ error("o100_dimArray: default case %d", subOp);
+ }
+
+ defineArray(fetchScriptWord(), data, 0, 0, 0, pop());
+}
+
+void ScummEngine_v100he::o100_drawLine() {
+ int id, unk1, unk2, x, x1, y1;
+
+ unk2 = pop();
+ id = pop();
+ unk1 = pop();
+ x = pop();
+ y1 = pop();
+ x1 = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 1:
+ drawLine(x1, y1, x, unk1, unk2, 2, id);
+ break;
+ case 20:
+ drawLine(x1, y1, x, unk1, unk2, 1, id);
+ break;
+ case 40:
+ drawLine(x1, y1, x, unk1, unk2, 3, id);
+ break;
+ default:
+ error("o100_drawLine: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_drawObject() {
+ int state, y, x;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 6:
+ state = 1;
+ y = pop();
+ x = pop();
+ break;
+ case 7:
+ state = pop();
+ y = pop();
+ x = pop();
+ break;
+ case 40:
+ state = pop();
+ if (state == 0)
+ state = 1;
+ y = x = -100;
+ break;
+ default:
+ error("o100_drawObject: default case %d", subOp);
+ }
+
+ int object = pop();
+ int objnum = getObjectIndex(object);
+ if (objnum == -1)
+ return;
+
+ if (y != -100 && x != -100) {
+ _objs[objnum].x_pos = x * 8;
+ _objs[objnum].y_pos = y * 8;
+ }
+
+ if (state != -1) {
+ addObjectToDrawQue(objnum);
+ putState(object, state);
+ }
+}
+
+void ScummEngine_v100he::o100_floodFill() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ memset(&_floodFillParams, 0, sizeof(_floodFillParams));
+ _floodFillParams.box.left = 0;
+ _floodFillParams.box.top = 0;
+ _floodFillParams.box.right = 639;
+ _floodFillParams.box.bottom = 479;
+ break;
+ case 6:
+ _floodFillParams.y = pop();
+ _floodFillParams.x = pop();
+ break;
+ case 18:
+ _floodFillParams.box.bottom = pop();
+ _floodFillParams.box.right = pop();
+ _floodFillParams.box.top = pop();
+ _floodFillParams.box.left = pop();
+ break;
+ case 20:
+ _floodFillParams.flags = pop();
+ break;
+ case 67:
+ pop();
+ break;
+ case 92:
+ floodFill(&_floodFillParams, this);
+ break;
+ default:
+ error("o100_floodFill: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_setSpriteGroupInfo() {
+ byte string[260];
+ int type, value1, value2, value3, value4;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _curSpriteGroupId = pop();
+ break;
+ case 6:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupPosition(_curSpriteGroupId, value1, value2);
+ break;
+ case 18:
+ value4 = pop();
+ value3 = pop();
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupBounds(_curSpriteGroupId, value1, value2, value3, value4);
+ break;
+ case 38:
+ type = pop() - 1;
+ switch (type) {
+ case 0:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->moveGroupMembers(_curSpriteGroupId, value1, value2);
+ break;
+ case 1:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersPriority(_curSpriteGroupId, value1);
+ break;
+ case 2:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersGroup(_curSpriteGroupId, value1);
+ break;
+ case 3:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersUpdateType(_curSpriteGroupId, value1);
+ break;
+ case 4:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersResetSprite(_curSpriteGroupId);
+ break;
+ case 5:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersAnimationSpeed(_curSpriteGroupId, value1);
+ break;
+ case 6:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersAutoAnimFlag(_curSpriteGroupId, value1);
+ break;
+ case 7:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersShadow(_curSpriteGroupId, value1);
+ break;
+ default:
+ error("o100_setSpriteGroupInfo subOp 38: Unknown case %d", subOp);
+ }
+ break;
+ case 40:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupImage(_curSpriteGroupId, value1);
+ break;
+ case 49:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->moveGroup(_curSpriteGroupId, value1, value2);
+ break;
+ case 52:
+ copyScriptString(string, sizeof(string));
+ break;
+ case 53:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->resetGroup(_curSpriteGroupId);
+ break;
+ case 54:
+ // dummy case
+ pop();
+ pop();
+ break;
+ case 59:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupPriority(_curSpriteGroupId, value1);
+ break;
+ case 60:
+ type = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ switch (type) {
+ case 0:
+ _sprite->setGroupXMul(_curSpriteGroupId, value1);
+ break;
+ case 1:
+ _sprite->setGroupXDiv(_curSpriteGroupId, value1);
+ break;
+ case 2:
+ _sprite->setGroupYMul(_curSpriteGroupId, value1);
+ break;
+ case 3:
+ _sprite->setGroupYDiv(_curSpriteGroupId, value1);
+ break;
+ default:
+ error("o100_setSpriteGroupInfo subOp 60: Unknown case %d", subOp);
+ }
+ break;
+ case 89:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->resetGroupBounds(_curSpriteGroupId);
+ break;
+ default:
+ error("o100_setSpriteGroupInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_resourceRoutines() {
+ int objidx, room;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 14:
+ _heResType = rtCharset;
+ _heResId = pop();
+ break;
+ case 25:
+ _heResType = rtCostume;
+ _heResId = pop();
+ break;
+ case 34:
+ _heResType = rtFlObject;
+ _heResId = pop();
+ break;
+ case 40:
+ _heResType = rtImage;
+ _heResId = pop();
+ break;
+ case 47:
+ if (_heResType == rtFlObject) {
+ room = getObjectRoom(_heResId);
+ loadFlObject(_heResId, room);
+ } else if (_heResType == rtCharset) {
+ loadCharset(_heResId);
+ } else {
+ ensureResourceLoaded(_heResType, _heResId);
+ }
+ break;
+ case 62:
+ _heResType = rtRoom;
+ _heResId = pop();
+ break;
+ case 66:
+ _heResType = rtScript;
+ _heResId = pop();
+ break;
+ case 72:
+ _heResType = rtSound;
+ _heResId = pop();
+ break;
+ case 128:
+ break;
+ case 132:
+ if (_heResType == rtScript && _heResId >= _numGlobalScripts)
+ break;
+
+ if (_heResType == rtFlObject) {
+ objidx = getObjectIndex(_heResId);
+ if (objidx == -1)
+ break;
+ res.lock(rtFlObject, _objs[objidx].fl_object_index);
+ } else {
+ res.lock(_heResType, _heResId);
+ }
+ break;
+ case 133:
+ if (_heResType == rtCharset)
+ nukeCharset(_heResId);
+ else
+ res.nukeResource(_heResType, _heResId);
+ break;
+ case 134:
+ case 135:
+ // Heap related
+ break;
+ case 136:
+ if (_heResType == rtScript && _heResId >= _numGlobalScripts)
+ break;
+
+ //queueLoadResource(_heResType, _heResId);
+ break;
+ case 137:
+ if (_heResType == rtScript && _heResId >= _numGlobalScripts)
+ break;
+
+ if (_heResType == rtFlObject) {
+ objidx = getObjectIndex(_heResId);
+ if (objidx == -1)
+ break;
+ res.unlock(rtFlObject, _objs[objidx].fl_object_index);
+ } else {
+ res.unlock(_heResType, _heResId);
+ }
+ break;
+ default:
+ error("o100_resourceRoutines: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_wizImageOps() {
+ int a, b;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _wizParams.img.resNum = pop();
+ _wizParams.processMode = 0;
+ _wizParams.processFlags = 0;
+ _wizParams.remapNum = 0;
+ _wizParams.img.flags = 0;
+ _wizParams.field_184 = 0;
+ _wizParams.field_180 = 0;
+ _wizParams.spriteId = 0;
+ _wizParams.spriteGroup = 0;
+ break;
+ case 2:
+ _wizParams.processFlags |= kWPFRotate;
+ _wizParams.angle = pop();
+ break;
+ case 6:
+ case 132:
+ _wizParams.processFlags |= kWPFSetPos;
+ _wizParams.img.y1 = pop();
+ _wizParams.img.x1 = pop();
+ break;
+ case 7:
+ _wizParams.processFlags |= kWPFMaskImg;
+ _wizParams.sourceImage = pop();
+ break;
+ case 11:
+ _wizParams.processFlags |= kWPFClipBox | 0x100;
+ _wizParams.processMode = 2;
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ _wizParams.compType = pop();
+ break;
+ case 18:
+ _wizParams.processFlags |= kWPFClipBox;
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ break;
+ case 21:
+ b = pop();
+ a = pop();
+ _wizParams.processFlags |= kWPFRemapPalette;
+ _wizParams.processMode = 6;
+ if (_wizParams.remapNum == 0) {
+ memset(_wizParams.remapIndex, 0, sizeof(_wizParams.remapIndex));
+ } else {
+ assert(_wizParams.remapNum < ARRAYSIZE(_wizParams.remapIndex));
+ _wizParams.remapIndex[_wizParams.remapNum] = a;
+ _wizParams.remapColor[a] = b;
+ ++_wizParams.remapNum;
+ }
+ break;
+ case 29:
+ _wizParams.processMode = 1;
+ break;
+ case 36:
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ break;
+ case 37:
+ // Dummy case
+ pop();
+ break;
+ case 39:
+ _wizParams.processFlags |= kWPFUseDefImgHeight;
+ _wizParams.resDefImgH = pop();
+ break;
+ case 47:
+ _wizParams.processFlags |= kWPFUseFile;
+ _wizParams.processMode = 3;
+ copyScriptString(_wizParams.filename, sizeof(_wizParams.filename));
+ break;
+ case 53:
+ _wizParams.processMode = 8;
+ break;
+ case 54:
+ _wizParams.processFlags |= 0x100000;
+ _wizParams.field_180 = pop();
+ _wizParams.field_184 = pop();
+ break;
+ case 55:
+ _wizParams.img.flags = pop();
+ _wizParams.img.state = pop();
+ _wizParams.img.y1 = pop();
+ _wizParams.img.x1 = pop();
+ _wizParams.spriteId = 0;
+ _wizParams.spriteGroup = 0;
+ _wizParams.img.resNum = pop();
+ _wiz->displayWizImage(&_wizParams.img);
+ break;
+ case 57:
+ _wizParams.processFlags |= kWPFPaletteNum;
+ _wizParams.img.palette = pop();
+ break;
+ case 58:
+ _wizParams.processFlags |= 0x1000 | 0x100 | 0x2;
+ _wizParams.processMode = 7;
+ _wizParams.field_168 = pop();
+ _wizParams.field_164 = pop();
+ _wizParams.compType = pop();
+ break;
+ case 64:
+ _wizParams.processFlags |= kWPFUseFile;
+ _wizParams.processMode = 4;
+ copyScriptString(_wizParams.filename, sizeof(_wizParams.filename));
+ _wizParams.fileWriteMode = pop();
+ break;
+ case 65:
+ _wizParams.processFlags |= kWPFScaled;
+ _wizParams.scale = pop();
+ break;
+ case 67:
+ _wizParams.processFlags |= kWPFNewFlags;
+ _wizParams.img.flags |= pop();
+ break;
+ case 68:
+ _wizParams.processFlags |= kWPFNewFlags | kWPFSetPos | 2;
+ _wizParams.img.flags |= kWIFIsPolygon;
+ _wizParams.field_164 = _wizParams.img.y1 = _wizParams.img.x1 = pop();
+ break;
+ case 70:
+ _wizParams.processFlags |= kWPFShadow;
+ _wizParams.img.shadow = pop();
+ break;
+ case 73:
+ _wizParams.processFlags |= kWPFNewState;
+ _wizParams.img.state = pop();
+ break;
+ case 84:
+ _wizParams.processFlags |= kWPFUseDefImgWidth;
+ _wizParams.resDefImgW = pop();
+ break;
+ case 92:
+ if (_wizParams.img.resNum)
+ _wiz->processWizImage(&_wizParams);
+ break;
+ case 128:
+ _wizParams.field_239D = pop();
+ _wizParams.field_2399 = pop();
+ _wizParams.field_23A5 = pop();
+ _wizParams.field_23A1 = pop();
+ copyScriptString(_wizParams.string2, sizeof(_wizParams.string2));
+ _wizParams.processMode = 15;
+ break;
+ case 129:
+ _wizParams.processMode = 14;
+ break;
+ case 130:
+ _wizParams.processMode = 16;
+ _wizParams.field_23AD = pop();
+ _wizParams.field_23A9 = pop();
+ copyScriptString(_wizParams.string1, sizeof(_wizParams.string1));
+ break;
+ case 131:
+ _wizParams.processMode = 13;
+ break;
+ case 133:
+ _wizParams.processMode = 17;
+ _wizParams.field_23CD = pop();
+ _wizParams.field_23C9 = pop();
+ _wizParams.field_23C5 = pop();
+ _wizParams.field_23C1 = pop();
+ _wizParams.field_23BD = pop();
+ _wizParams.field_23B9 = pop();
+ _wizParams.field_23B5 = pop();
+ _wizParams.field_23B1 = pop();
+ break;
+ case 134:
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 12;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.top = _wizParams.box2.bottom = pop();
+ _wizParams.box2.left = _wizParams.box2.right = pop();
+ break;
+ case 135:
+ _wizParams.processFlags |= kWPFDstResNum;
+ _wizParams.dstResNum = pop();
+ break;
+ case 136:
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 10;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.bottom = pop();
+ _wizParams.box2.right = pop();
+ _wizParams.box2.top = pop();
+ _wizParams.box2.left = pop();
+ break;
+ case 137:
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 11;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.top = _wizParams.box2.bottom = pop();
+ _wizParams.box2.left = _wizParams.box2.right = pop();
+ break;
+ case 138:
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 9;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.bottom = pop();
+ _wizParams.box2.right = pop();
+ _wizParams.box2.top = pop();
+ _wizParams.box2.left = pop();
+ break;
+ default:
+ error("o100_wizImageOps: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_dim2dim2Array() {
+ int data, dim1start, dim1end, dim2start, dim2end;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 41: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 42: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 43:
+ data = kDwordArray;
+ break;
+ case 44: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 45: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 77: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ default:
+ error("o100_dim2dim2Array: default case %d", subOp);
+ }
+
+ if (pop() == 2) {
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ } else {
+ dim2end = pop();
+ dim2start = pop();
+ dim1end = pop();
+ dim1start = pop();
+ }
+
+ defineArray(fetchScriptWord(), data, dim2start, dim2end, dim1start, dim1end);
+}
+
+void ScummEngine_v100he::o100_redim2dimArray() {
+ int a, b, c, d;
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 42:
+ redimArray(fetchScriptWord(), a, b, c, d, kIntArray);
+ break;
+ case 43:
+ redimArray(fetchScriptWord(), a, b, c, d, kDwordArray);
+ break;
+ case 45:
+ redimArray(fetchScriptWord(), a, b, c, d, kByteArray);
+ break;
+ default:
+ error("o100_redim2dimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_paletteOps() {
+ int a, b, c, d, e;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _hePaletteNum = pop();
+ break;
+ case 20:
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ for (; a <= b; ++a) {
+ setHEPaletteColor(_hePaletteNum, a, c, d, e);
+ }
+ }
+ break;
+ case 25:
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromCostume(_hePaletteNum, a);
+ }
+ break;
+ case 40:
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromImage(_hePaletteNum, a, b);
+ }
+ break;
+ case 53:
+ if (_hePaletteNum != 0) {
+ restoreHEPalette(_hePaletteNum);
+ }
+ break;
+ case 57:
+ a = pop();
+ if (_hePaletteNum != 0) {
+ copyHEPalette(_hePaletteNum, a);
+ }
+ break;
+ case 63:
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromRoom(_hePaletteNum, a, b);
+ }
+ break;
+ case 81:
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_hePaletteNum) {
+ for (; a <= b; ++a) {
+ copyHEPaletteColor(_hePaletteNum, a, c);
+ }
+ }
+ break;
+ case 92:
+ _hePaletteNum = 0;
+ break;
+ default:
+ error("o100_paletteOps: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_jumpToScriptUnk() {
+ int args[25];
+ int script, cycle;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ cycle = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ stopObjectCode();
+ runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args, cycle);
+}
+
+void ScummEngine_v100he::o100_startScriptUnk() {
+ int args[25];
+ int script, cycle;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ cycle = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args, cycle);
+}
+
+void ScummEngine_v100he::o100_redimArray() {
+ int newX, newY;
+ newY = pop();
+ newX = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 42:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kIntArray);
+ break;
+ case 43:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kDwordArray);
+ break;
+ case 45:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kByteArray);
+ break;
+ default:
+ error("o100_redimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_roomOps() {
+ int a, b, c, d, e;
+ byte filename[100];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 63: // SO_ROOM_PALETTE
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ setPalColor(d, a, b, c);
+ break;
+
+ case 129:
+ b = pop();
+ a = pop();
+ swapObjects(a, b);
+ break;
+
+ case 130:
+ a = pop();
+ b = pop();
+ copyPalColor(a, b);
+ break;
+
+ case 131: // SO_ROOM_FADE
+ // Defaults to 1 but doesn't use fade effects
+ a = pop();
+ break;
+
+ case 132: // SO_ROOM_INTENSITY
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, a, a, b, c);
+ break;
+
+ case 133: // SO_RGB_ROOM_INTENSITY
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, b, c, d, e);
+ break;
+
+ case 134: // SO_ROOM_NEW_PALETTE
+ a = pop();
+ setPalette(a);
+ break;
+
+ case 135:
+ b = pop();
+ a = pop();
+ setRoomPalette(a, b);
+ break;
+
+ case 136: // SO_ROOM_SAVEGAME
+ _saveTemporaryState = true;
+ _saveLoadSlot = pop();
+ _saveLoadFlag = pop();
+ break;
+
+ case 137:
+ copyScriptString(filename, sizeof(filename));
+ _saveLoadFlag = pop();
+ _saveLoadSlot = 1;
+ _saveTemporaryState = true;
+ break;
+
+ case 138: // SO_ROOM_SCREEN
+ b = pop();
+ a = pop();
+ initScreens(a, _screenHeight);
+ break;
+
+ case 139: // SO_ROOM_SCROLL
+ b = pop();
+ a = pop();
+ if (a < (_screenWidth / 2))
+ a = (_screenWidth / 2);
+ if (b < (_screenWidth / 2))
+ b = (_screenWidth / 2);
+ if (a > _roomWidth - (_screenWidth / 2))
+ a = _roomWidth - (_screenWidth / 2);
+ if (b > _roomWidth - (_screenWidth / 2))
+ b = _roomWidth - (_screenWidth / 2);
+ VAR(VAR_CAMERA_MIN_X) = a;
+ VAR(VAR_CAMERA_MAX_X) = b;
+ break;
+
+ default:
+ error("o100_roomOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_startSound() {
+ byte filename[260];
+ int var, value;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 6:
+ _heSndFlags |= 16;
+ _heSndOffset = pop();
+ break;
+ case 47:
+ copyScriptString(filename, sizeof(filename));
+ _heSndSoundId = pop();
+ if (_heSndSoundId)
+ debug(0, "Load sound %d from file %s\n", _heSndSoundId, filename);
+ break;
+ case 55:
+ _heSndFlags |= 8;
+ break;
+ case 83:
+ value = pop();
+ var = pop();
+ _heSndSoundId = pop();
+ _sound->setSoundVar(_heSndSoundId, var, value);
+ break;
+ case 92:
+ _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
+ break;
+ case 128:
+ _heSndFlags |= 2;
+ break;
+ case 129:
+ _heSndChannel = pop();
+ break;
+ case 130:
+ _heSndFlags |= 64;
+ pop();
+ break;
+ case 131:
+ _heSndFlags |= 1;
+ break;
+ case 132: // Music
+ case 134: // Sound
+ _heSndSoundId = pop();
+ _heSndOffset = 0;
+ _heSndSoundFreq = 11025;
+ _heSndChannel = VAR(VAR_SOUND_CHANNEL);
+ _heSndFlags = 0;
+ break;
+ case 133:
+ _heSndFlags |= 128;
+ pop();
+ break;
+ case 135:
+ _heSndFlags |= 4;
+ break;
+ case 136:
+ _heSndFlags |= 32;
+ pop();
+ break;
+ default:
+ error("o100_startSound invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_setSpriteInfo() {
+ int args[16];
+ int spriteId, n;
+ int32 tmp[2];
+ byte string[80];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _curMaxSpriteId = pop();
+ _curSpriteId = pop();
+
+ if (_curSpriteId > _curMaxSpriteId)
+ SWAP(_curSpriteId, _curMaxSpriteId);
+ break;
+ case 2:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteAngle(spriteId, args[0]);
+ break;
+ case 3:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagAutoAnim(spriteId, args[0]);
+ break;
+ case 4:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteAnimSpeed(spriteId, args[0]);
+ break;
+ case 6:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePosition(spriteId, args[0], args[1]);
+ break;
+ case 7:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteSourceImage(spriteId, args[0]);
+ break;
+ case 16:
+ n = getStackList(args, ARRAYSIZE(args));
+ if (_curSpriteId != 0 && _curMaxSpriteId != 0 && n != 0) {
+ int *p = &args[n - 1];
+ do {
+ int code = *p;
+ if (code == 0) {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteResetClass(i);
+ }
+ } else if (code & 0x80) {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteSetClass(i, code & 0x7F, 1);
+ }
+ } else {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteSetClass(i, code & 0x7F, 0);
+ }
+ }
+ --p;
+ } while (--n);
+ }
+ break;
+ case 32:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagEraseType(spriteId, args[0]);
+ break;
+ case 38:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteGroup(spriteId, args[0]);
+ break;
+ case 40:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteImage(spriteId, args[0]);
+ break;
+ case 48:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteMaskImage(spriteId, args[0]);
+ break;
+ case 49:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->moveSprite(spriteId, args[0], args[1]);
+ break;
+ case 52:
+ copyScriptString(string, sizeof(string));
+ break;
+ case 53:
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->resetSprite(spriteId);
+ break;
+ case 54:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteGeneralProperty(spriteId, args[0], args[1]);
+ break;
+ case 57:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePalette(spriteId, args[0]);
+ break;
+ case 59:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePriority(spriteId, args[0]);
+ break;
+ case 60:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ switch(args[1]) {
+ case 0:
+ _sprite->setSpriteFlagXFlipped(spriteId, args[0]);
+ break;
+ case 1:
+ _sprite->setSpriteFlagYFlipped(spriteId, args[0]);
+ break;
+ case 2:
+ _sprite->setSpriteFlagActive(spriteId, args[0]);
+ break;
+ case 3:
+ _sprite->setSpriteFlagDoubleBuffered(spriteId, args[0]);
+ break;
+ case 4:
+ _sprite->setSpriteFlagRemapPalette(spriteId, args[0]);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 61:
+ _sprite->resetTables(true);
+ break;
+ case 65:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteScale(spriteId, args[0]);
+ break;
+ case 70:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteShadow(spriteId, args[0]);
+ break;
+ case 73:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteImageState(spriteId, args[0]);
+ break;
+ case 74:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteDist(spriteId, args[0], args[1]);
+ break;
+ case 75:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++) {
+ _sprite->getSpriteDist(spriteId, tmp[0], tmp[1]);
+ _sprite->setSpriteDist(spriteId, args[0], tmp[1]);
+ }
+ break;
+ case 76:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++) {
+ _sprite->getSpriteDist(spriteId, tmp[0], tmp[1]);
+ _sprite->setSpriteDist(spriteId, tmp[0], args[0]);
+ }
+ break;
+ case 82:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagUpdateType(spriteId, args[0]);
+ break;
+ case 83:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteUserValue(spriteId, args[0], args[1]);
+ break;
+ case 88:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteField84(spriteId, args[0]);
+ break;
+ case 89:
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteField84(spriteId, 0);
+ break;
+ default:
+ error("o100_setSpriteInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_startScript() {
+ int args[25];
+ int script;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = fetchScriptByte();
+ runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args);
+}
+
+void ScummEngine_v100he::o100_systemOps() {
+ byte string[1024];
+
+ byte subOp = fetchScriptByte();
+ subOp -= 61;
+
+ switch (subOp) {
+ case 0:
+ restart();
+ break;
+ case 67:
+ clearDrawObjectQueue();
+ break;
+ case 71:
+ // Confirm shutdown
+ shutDown();
+ break;
+ case 72:
+ shutDown();
+ break;
+ case 73:
+ copyScriptString(string, sizeof(string));
+ debug(0, "Start game (%s)", string);
+ break;
+ case 74:
+ copyScriptString(string, sizeof(string));
+ debug(0, "Start executable (%s)", string);
+ break;
+ case 75:
+ gdi.copyVirtScreenBuffers(Common::Rect(_screenWidth, _screenHeight));
+ updatePalette();
+ break;
+ default:
+ error("o100_systemOps invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_cursorCommand() {
+ int a, i;
+ int args[16];
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0xE: // SO_CHARSET_SET
+ initCharset(pop());
+ break;
+ case 0xF: // SO_CHARSET_COLOR
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ break;
+ case 0x80:
+ case 0x81:
+ a = pop();
+ _wiz->loadWizCursor(a);
+ break;
+ case 0x82:
+ pop();
+ a = pop();
+ _wiz->loadWizCursor(a);
+ break;
+ case 0x86: // SO_CURSOR_ON Turn cursor on
+ _cursor.state = 1;
+ break;
+ case 0x87: // SO_CURSOR_OFF Turn cursor off
+ _cursor.state = 0;
+ break;
+ case 0x88: // SO_CURSOR_SOFT_ON Turn soft cursor on
+ _cursor.state++;
+ if (_cursor.state > 1)
+ error("o100_cursorCommand: Cursor state greater than 1 in script");
+ break;
+
+ case 0x89: // SO_CURSOR_SOFT_OFF Turn soft cursor off
+ _cursor.state--;
+ break;
+ case 0x8B: // SO_USERPUT_ON
+ _userPut = 1;
+ break;
+ case 0x8C: // SO_USERPUT_OFF
+ _userPut = 0;
+ break;
+ case 0x8D: // SO_USERPUT_SOFT_ON
+ _userPut++;
+ break;
+ case 0x8E: // SO_USERPUT_SOFT_OFF
+ _userPut--;
+ break;
+ default:
+ error("o100_cursorCommand: default case %x", subOp);
+ }
+
+ VAR(VAR_CURSORSTATE) = _cursor.state;
+ VAR(VAR_USERPUT) = _userPut;
+}
+
+void ScummEngine_v100he::o100_videoOps() {
+ // Uses Bink video
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ memset(_videoParams.filename, 0, sizeof(_videoParams.filename));
+ _videoParams.unk2 = pop();
+ break;
+ case 19:
+ _videoParams.status = 19;
+ break;
+ case 40:
+ _videoParams.wizResNum = pop();
+ if (_videoParams.wizResNum)
+ _videoParams.flags |= 2;
+ break;
+ case 47:
+ copyScriptString(_videoParams.filename, sizeof(_videoParams.filename));
+ _videoParams.status = 47;
+ break;
+ case 67:
+ _videoParams.flags |= pop();
+ break;
+ case 92:
+ if (_videoParams.status == 47) {
+ // Start video
+ if (_videoParams.flags == 0)
+ _videoParams.flags = 4;
+
+ if (_videoParams.flags == 2) {
+ // result = startVideo(_videoParams.filename, _videoParams.flags, _videoParams.wizResNum);
+ // VAR(119) = result;
+ } else {
+ // result = startVideo(_videoParams.filename, _videoParams.flags);
+ // VAR(119) = result;
+ }
+ } else if (_videoParams.status == 19) {
+ // Stop video
+ }
+ break;
+ default:
+ error("o100_videoOps: unhandled case %d", subOp);
+ }
+
+ debug(1,"o100_videoOps stub (%d)", subOp);
+}
+
+void ScummEngine_v100he::o100_wait() {
+ int actnum;
+ int offs = -2;
+ Actor *a;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 128: // SO_WAIT_FOR_ACTOR Wait for actor
+ offs = fetchScriptWordSigned();
+ actnum = pop();
+ a = derefActor(actnum, "o100_wait:168");
+ if (a->_moving)
+ break;
+ return;
+ case 129: // SO_WAIT_FOR_CAMERA Wait for camera
+ if (camera._cur.x / 8 != camera._dest.x / 8)
+ break;
+ return;
+ case 130: // SO_WAIT_FOR_MESSAGE Wait for message
+ if (VAR(VAR_HAVE_MSG))
+ break;
+ return;
+ case 131: // SO_WAIT_FOR_SENTENCE
+ if (_sentenceNum) {
+ if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ }
+ if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ default:
+ error("o100_wait: default case 0x%x", subOp);
+ }
+
+ _scriptPointer += offs;
+ o6_breakHere();
+}
+
+void ScummEngine_v100he::o100_writeFile() {
+ int32 resID = pop();
+ int slot = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 5:
+ fetchScriptByte();
+ writeFileFromArray(slot, resID);
+ break;
+ case 42:
+ _hFileTable[slot].writeUint16LE(resID);
+ break;
+ case 43:
+ _hFileTable[slot].writeUint32LE(resID);
+ break;
+ case 45:
+ _hFileTable[slot].writeByte(resID);
+ break;
+ default:
+ error("o100_writeFile: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_isResourceLoaded() {
+ // Reports percentage of resource loaded by queue
+ int type;
+
+ byte subOp = fetchScriptByte();
+ /* int idx = */ pop();
+
+ switch (subOp) {
+ case 25:
+ type = rtCostume;
+ break;
+ case 40:
+ type = rtImage;
+ break;
+ case 62:
+ type = rtRoom;
+ break;
+ case 66:
+ type = rtScript;
+ break;
+ case 72:
+ type = rtSound;
+ break;
+ default:
+ error("o100_isResourceLoaded: default case %d", subOp);
+ }
+
+ push(100);
+}
+
+void ScummEngine_v100he::o100_getResourceSize() {
+ const byte *ptr;
+ int size, type;
+
+ int resid = pop();
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 25:
+ type = rtCostume;
+ break;
+ case 40:
+ type = rtImage;
+ break;
+ case 62:
+ type = rtRoomImage;
+ break;
+ case 66:
+ type = rtScript;
+ break;
+ case 72:
+ push (getSoundResourceSize(resid));
+ return;
+ default:
+ error("o100_getResourceSize: default type %d", subOp);
+ }
+
+ ptr = getResourceAddress(type, resid);
+ assert(ptr);
+ size = READ_BE_UINT32(ptr + 4) - 8;
+ push(size);
+}
+
+void ScummEngine_v100he::o100_getSpriteGroupInfo() {
+ int32 tx, ty;
+ int spriteGroupId, type;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 5:
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(getGroupSpriteArray(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 40:
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(_sprite->getGroupDstResNum(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 54:
+ // TODO: U32 related
+ pop();
+ pop();
+ push(0);
+ break;
+ case 59:
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(_sprite->getGroupPriority(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 60:
+ type = pop();
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ switch(type) {
+ case 0:
+ push(_sprite->getGroupXMul(spriteGroupId));
+ break;
+ case 1:
+ push(_sprite->getGroupXDiv(spriteGroupId));
+ break;
+ case 2:
+ push(_sprite->getGroupYMul(spriteGroupId));
+ break;
+ case 3:
+ push(_sprite->getGroupYDiv(spriteGroupId));
+ break;
+ default:
+ push(0);
+ }
+ } else {
+ push(0);
+ }
+ break;
+ case 85:
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ _sprite->getGroupPosition(spriteGroupId, tx, ty);
+ push(tx);
+ } else {
+ push(0);
+ }
+ break;
+ case 86:
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ _sprite->getGroupPosition(spriteGroupId, tx, ty);
+ push(ty);
+ } else {
+ push(0);
+ }
+ break;
+ default:
+ error("o100_getSpriteGroupInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_getWizData() {
+ byte filename[4096];
+ int resId, state, type;
+ int32 w, h;
+ int32 x, y;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 20;
+
+ switch (subOp) {
+ case 0:
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->getWizPixelColor(resId, state, x, y, 0));
+ break;
+ case 6:
+ resId = pop();
+ push(_wiz->getWizImageStates(resId));
+ break;
+ case 13:
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->isWizPixelNonTransparent(resId, state, x, y, 0));
+ break;
+ case 19:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageDim(resId, state, w, h);
+ push(h);
+ break;
+ case 34:
+ type = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->getWizImageData(resId, state, type));
+ break;
+ case 64:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageDim(resId, state, w, h);
+ push(w);
+ break;
+ case 65:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageSpot(resId, state, x, y);
+ push(x);
+ break;
+ case 66:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageSpot(resId, state, x, y);
+ push(y);
+ break;
+ case 111:
+ pop();
+ copyScriptString(filename, sizeof(filename));
+ pop();
+ push(0);
+ debug(0, "o100_getWizData() case 111 unhandled");
+ break;
+ case 112:
+ h = pop();
+ w = pop();
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ if (x == -1 && y == -1 && w == -1 && h == -1) {
+ _wiz->getWizImageDim(resId, state, w, h);
+ x = 0;
+ y = 0;
+ }
+ push(computeWizHistogram(resId, state, x, y, w, h));
+ break;
+ default:
+ error("o100_getWizData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_getPaletteData() {
+ int b, c, d, e;
+ int palSlot, color;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 13:
+ c = pop();
+ b = pop();
+ push(getHEPaletteColorComponent(1, b, c));
+ break;
+ case 20:
+ color = pop();
+ palSlot = pop();
+ push(getHEPaletteColor(palSlot, color));
+ break;
+ case 33:
+ e = pop();
+ d = pop();
+ palSlot = pop();
+ pop();
+ c = pop();
+ b = pop();
+ push(getHEPaletteSimilarColor(palSlot, b, c, d, e));
+ break;
+ case 53:
+ pop();
+ c = pop();
+ c = MAX(0, c);
+ c = MIN(c, 255);
+ b = pop();
+ b = MAX(0, b);
+ b = MIN(b, 255);
+ push(getHEPaletteSimilarColor(1, b, c, 10, 245));
+ break;
+ case 73:
+ c = pop();
+ b = pop();
+ palSlot = pop();
+ push(getHEPaletteColorComponent(palSlot, b, c));
+ break;
+ default:
+ error("o100_getPaletteData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_readFile() {
+ int slot, val;
+ int32 size;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 5:
+ fetchScriptByte();
+ size = pop();
+ slot = pop();
+ val = readFileToArray(slot, size);
+ push(val);
+ break;
+ case 42:
+ slot = pop();
+ val = _hFileTable[slot].readUint16LE();
+ push(val);
+ break;
+ case 43:
+ slot = pop();
+ val = _hFileTable[slot].readUint32LE();
+ push(val);
+ break;
+ case 45:
+ slot = pop();
+ val = _hFileTable[slot].readByte();
+ push(val);
+ break;
+ default:
+ error("o100_readFile: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_getSpriteInfo() {
+ int args[16];
+ int spriteId, flags, groupId, type;
+ int32 x, y;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 3:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagAutoAnim(spriteId));
+ else
+ push(0);
+ break;
+ case 4:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteAnimSpeed(spriteId));
+ else
+ push(1);
+ break;
+ case 7:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteSourceImage(spriteId));
+ else
+ push(0);
+ break;
+ case 16:
+ flags = getStackList(args, ARRAYSIZE(args));
+ spriteId = pop();
+ if (spriteId) {
+ push(_sprite->getSpriteClass(spriteId, flags, args));
+ } else {
+ push(0);
+ }
+ break;
+ case 26:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImageStateCount(spriteId));
+ else
+ push(0);
+ break;
+ case 30:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteDisplayX(spriteId));
+ else
+ push(0);
+ break;
+ case 31:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteDisplayY(spriteId));
+ else
+ push(0);
+ break;
+ case 32:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagEraseType(spriteId));
+ else
+ push(1);
+ break;
+ case 33:
+ flags = getStackList(args, ARRAYSIZE(args));
+ type = pop();
+ groupId = pop();
+ y = pop();
+ x = pop();
+ push(_sprite->findSpriteWithClassOf(x, y, groupId, type, flags, args));
+ break;
+ case 38:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteGroup(spriteId));
+ else
+ push(0);
+ break;
+ case 39:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteImageDim(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 40:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImage(spriteId));
+ else
+ push(0);
+ break;
+ case 48:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteMaskImage(spriteId));
+ else
+ push(0);
+ break;
+ case 54:
+ flags = pop();
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteGeneralProperty(spriteId, flags));
+ else
+ push(0);
+ break;
+ case 57:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpritePalette(spriteId));
+ else
+ push(0);
+ break;
+ case 59:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpritePriority(spriteId));
+ else
+ push(0);
+ break;
+ case 60:
+ flags = pop();
+ spriteId = pop();
+ if (spriteId) {
+ switch(flags) {
+ case 0:
+ push(_sprite->getSpriteFlagXFlipped(spriteId));
+ break;
+ case 1:
+ push(_sprite->getSpriteFlagYFlipped(spriteId));
+ break;
+ case 2:
+ push(_sprite->getSpriteFlagActive(spriteId));
+ break;
+ case 3:
+ push(_sprite->getSpriteFlagDoubleBuffered(spriteId));
+ break;
+ case 4:
+ push(_sprite->getSpriteFlagRemapPalette(spriteId));
+ break;
+ default:
+ push(0);
+ }
+ } else {
+ push(0);
+ }
+ break;
+ case 65:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteScale(spriteId));
+ else
+ push(0);
+ break;
+ case 70:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteShadow(spriteId));
+ else
+ push(0);
+ break;
+ case 73:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImageState(spriteId));
+ else
+ push(0);
+ break;
+ case 75:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteDist(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 76:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteDist(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 82:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagUpdateType(spriteId));
+ else
+ push(0);
+ break;
+ case 83:
+ pop();
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteUserValue(spriteId));
+ else
+ push(0);
+ break;
+ case 84:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteImageDim(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 85:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpritePosition(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 86:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpritePosition(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ default:
+ error("o100_getSpriteInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_getVideoData() {
+ // Uses Bink video
+ byte subOp = fetchScriptByte();
+ subOp -= 26;
+
+ switch (subOp) {
+ case 0:
+ pop();
+ break;
+ case 13:
+ pop();
+ break;
+ case 14:
+ pop();
+ break;
+ case 28:
+ pop();
+ pop();
+ break;
+ case 47:
+ pop();
+ break;
+ case 58:
+ pop();
+ break;
+ default:
+ error("o100_getVideoData: unhandled case %d", subOp);
+ }
+
+ push(-1);
+ debug(1,"o100_getVideoData stub (%d)", subOp);
+}
+
+void ScummEngine_v100he::decodeParseString(int m, int n) {
+ Actor *a;
+ int i, colors, size;
+ int args[31];
+ byte name[1024];
+
+ byte b = fetchScriptByte();
+
+ switch (b) {
+ case 6: // SO_AT
+ _string[m].ypos = pop();
+ _string[m].xpos = pop();
+ _string[m].overhead = false;
+ break;
+ case 12: // SO_CENTER
+ _string[m].center = true;
+ _string[m].overhead = false;
+ break;
+ case 18: // SO_CLIPPED
+ _string[m].right = pop();
+ break;
+ case 20: // SO_COLOR
+ _string[m].color = pop();
+ break;
+ case 21:
+ colors = pop();
+ if (colors == 1) {
+ _string[m].color = pop();
+ } else {
+ push(colors);
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ _string[m].color = _charsetColorMap[0];
+ }
+ break;
+ case 35:
+ decodeScriptString(name, true);
+ printString(m, name);
+ break;
+ case 46: // SO_LEFT
+ _string[m].center = false;
+ _string[m].overhead = false;
+ break;
+ case 51: // SO_MUMBLE
+ _string[m].no_talk_anim = true;
+ break;
+ case 56: // SO_OVERHEAD
+ _string[m].overhead = true;
+ _string[m].no_talk_anim = false;
+ break;
+ case 78:
+ {
+ byte *dataPtr = getResourceAddress(rtTalkie, pop());
+ byte *text = findWrappedBlock(MKID('TEXT'), dataPtr, 0, 0);
+ size = getResourceDataSize(text);
+ memcpy(name, text, size);
+ printString(m, name);
+ }
+ break;
+ case 79: // SO_TEXTSTRING
+ printString(m, _scriptPointer);
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+ break;
+ case 91:
+ _string[m].loadDefault();
+ if (n) {
+ _actorToPrintStrFor = pop();
+ if (_actorToPrintStrFor != 0xFF) {
+ a = derefActor(_actorToPrintStrFor, "decodeParseString");
+ _string[0].color = a->_talkColor;
+ }
+ }
+ break;
+ case 92:
+ _string[m].saveDefault();
+ break;
+ default:
+ error("decodeParseString: default case %d", b);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp
new file mode 100644
index 0000000000..880628f53e
--- /dev/null
+++ b/engines/scumm/script_v2.cpp
@@ -0,0 +1,1629 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern.h"
+#include "scumm/object.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v2, x)
+
+void ScummEngine_v2::setupOpcodes() {
+ static const OpcodeEntryV2 opcodes[256] = {
+ /* 00 */
+ OPCODE(o5_stopObjectCode),
+ OPCODE(o2_putActor),
+ OPCODE(o5_startMusic),
+ OPCODE(o5_getActorRoom),
+ /* 04 */
+ OPCODE(o2_isGreaterEqual),
+ OPCODE(o2_drawObject),
+ OPCODE(o2_getActorElevation),
+ OPCODE(o2_setState08),
+ /* 08 */
+ OPCODE(o5_isNotEqual),
+ OPCODE(o5_faceActor),
+ OPCODE(o2_assignVarWordIndirect),
+ OPCODE(o2_setObjPreposition),
+ /* 0C */
+ OPCODE(o2_resourceRoutines),
+ OPCODE(o5_walkActorToActor),
+ OPCODE(o2_putActorAtObject),
+ OPCODE(o2_ifNotState08),
+ /* 10 */
+ OPCODE(o5_getObjectOwner),
+ OPCODE(o2_animateActor),
+ OPCODE(o2_panCameraTo),
+ OPCODE(o2_actorOps),
+ /* 14 */
+ OPCODE(o5_print),
+ OPCODE(o2_actorFromPos),
+ OPCODE(o5_getRandomNr),
+ OPCODE(o2_clearState02),
+ /* 18 */
+ OPCODE(o5_jumpRelative),
+ OPCODE(o2_doSentence),
+ OPCODE(o5_move),
+ OPCODE(o2_setBitVar),
+ /* 1C */
+ OPCODE(o5_startSound),
+ OPCODE(o2_ifClassOfIs),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifState02),
+ /* 20 */
+ OPCODE(o5_stopMusic),
+ OPCODE(o2_putActor),
+ OPCODE(o5_saveLoadGame),
+ OPCODE(o2_getActorY),
+ /* 24 */
+ OPCODE(o2_loadRoomWithEgo),
+ OPCODE(o2_drawObject),
+ OPCODE(o5_setVarRange),
+ OPCODE(o2_setState04),
+ /* 28 */
+ OPCODE(o5_equalZero),
+ OPCODE(o2_setOwnerOf),
+ OPCODE(o2_addIndirect),
+ OPCODE(o5_delayVariable),
+ /* 2C */
+ OPCODE(o2_assignVarByte),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o2_delay),
+ OPCODE(o2_ifNotState04),
+ /* 30 */
+ OPCODE(o2_setBoxFlags),
+ OPCODE(o2_getBitVar),
+ OPCODE(o2_setCameraAt),
+ OPCODE(o2_roomOps),
+ /* 34 */
+ OPCODE(o5_getDist),
+ OPCODE(o2_findObject),
+ OPCODE(o2_walkActorToObject),
+ OPCODE(o2_setState01),
+ /* 38 */
+ OPCODE(o2_isLessEqual),
+ OPCODE(o2_doSentence),
+ OPCODE(o2_subtract),
+ OPCODE(o2_waitForActor),
+ /* 3C */
+ OPCODE(o5_stopSound),
+ OPCODE(o2_setActorElevation),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifNotState01),
+ /* 40 */
+ OPCODE(o2_cutscene),
+ OPCODE(o2_putActor),
+ OPCODE(o2_startScript),
+ OPCODE(o2_getActorX),
+ /* 44 */
+ OPCODE(o2_isLess),
+ OPCODE(o2_drawObject),
+ OPCODE(o5_increment),
+ OPCODE(o2_clearState08),
+ /* 48 */
+ OPCODE(o5_isEqual),
+ OPCODE(o5_faceActor),
+ OPCODE(o2_chainScript),
+ OPCODE(o2_setObjPreposition),
+ /* 4C */
+ OPCODE(o2_waitForSentence),
+ OPCODE(o5_walkActorToActor),
+ OPCODE(o2_putActorAtObject),
+ OPCODE(o2_ifState08),
+ /* 50 */
+ OPCODE(o2_pickupObject),
+ OPCODE(o2_animateActor),
+ OPCODE(o5_actorFollowCamera),
+ OPCODE(o2_actorOps),
+ /* 54 */
+ OPCODE(o5_setObjectName),
+ OPCODE(o2_actorFromPos),
+ OPCODE(o5_getActorMoving),
+ OPCODE(o2_setState02),
+ /* 58 */
+ OPCODE(o2_beginOverride),
+ OPCODE(o2_doSentence),
+ OPCODE(o2_add),
+ OPCODE(o2_setBitVar),
+ /* 5C */
+ OPCODE(o2_dummy),
+ OPCODE(o2_ifClassOfIs),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifNotState02),
+ /* 60 */
+ OPCODE(o2_cursorCommand),
+ OPCODE(o2_putActor),
+ OPCODE(o2_stopScript),
+ OPCODE(o5_getActorFacing),
+ /* 64 */
+ OPCODE(o2_loadRoomWithEgo),
+ OPCODE(o2_drawObject),
+ OPCODE(o5_getClosestObjActor),
+ OPCODE(o2_clearState04),
+ /* 68 */
+ OPCODE(o5_isScriptRunning),
+ OPCODE(o2_setOwnerOf),
+ OPCODE(o2_subIndirect),
+ OPCODE(o2_dummy),
+ /* 6C */
+ OPCODE(o2_getObjPreposition),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o2_dummy),
+ OPCODE(o2_ifState04),
+ /* 70 */
+ OPCODE(o2_lights),
+ OPCODE(o5_getActorCostume),
+ OPCODE(o5_loadRoom),
+ OPCODE(o2_roomOps),
+ /* 74 */
+ OPCODE(o5_getDist),
+ OPCODE(o2_findObject),
+ OPCODE(o2_walkActorToObject),
+ OPCODE(o2_clearState01),
+ /* 78 */
+ OPCODE(o2_isGreater),
+ OPCODE(o2_doSentence),
+ OPCODE(o2_verbOps),
+ OPCODE(o2_getActorWalkBox),
+ /* 7C */
+ OPCODE(o5_isSoundRunning),
+ OPCODE(o2_setActorElevation),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifState01),
+ /* 80 */
+ OPCODE(o5_breakHere),
+ OPCODE(o2_putActor),
+ OPCODE(o5_startMusic),
+ OPCODE(o5_getActorRoom),
+ /* 84 */
+ OPCODE(o2_isGreaterEqual),
+ OPCODE(o2_drawObject),
+ OPCODE(o2_getActorElevation),
+ OPCODE(o2_setState08),
+ /* 88 */
+ OPCODE(o5_isNotEqual),
+ OPCODE(o5_faceActor),
+ OPCODE(o2_assignVarWordIndirect),
+ OPCODE(o2_setObjPreposition),
+ /* 8C */
+ OPCODE(o2_resourceRoutines),
+ OPCODE(o5_walkActorToActor),
+ OPCODE(o2_putActorAtObject),
+ OPCODE(o2_ifNotState08),
+ /* 90 */
+ OPCODE(o5_getObjectOwner),
+ OPCODE(o2_animateActor),
+ OPCODE(o2_panCameraTo),
+ OPCODE(o2_actorOps),
+ /* 94 */
+ OPCODE(o5_print),
+ OPCODE(o2_actorFromPos),
+ OPCODE(o5_getRandomNr),
+ OPCODE(o2_clearState02),
+ /* 98 */
+ OPCODE(o2_restart),
+ OPCODE(o2_doSentence),
+ OPCODE(o5_move),
+ OPCODE(o2_setBitVar),
+ /* 9C */
+ OPCODE(o5_startSound),
+ OPCODE(o2_ifClassOfIs),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifState02),
+ /* A0 */
+ OPCODE(o5_stopObjectCode),
+ OPCODE(o2_putActor),
+ OPCODE(o5_saveLoadGame),
+ OPCODE(o2_getActorY),
+ /* A4 */
+ OPCODE(o2_loadRoomWithEgo),
+ OPCODE(o2_drawObject),
+ OPCODE(o5_setVarRange),
+ OPCODE(o2_setState04),
+ /* A8 */
+ OPCODE(o5_notEqualZero),
+ OPCODE(o2_setOwnerOf),
+ OPCODE(o2_addIndirect),
+ OPCODE(o2_switchCostumeSet),
+ /* AC */
+ OPCODE(o2_drawSentence),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o2_waitForMessage),
+ OPCODE(o2_ifNotState04),
+ /* B0 */
+ OPCODE(o2_setBoxFlags),
+ OPCODE(o2_getBitVar),
+ OPCODE(o2_setCameraAt),
+ OPCODE(o2_roomOps),
+ /* B4 */
+ OPCODE(o5_getDist),
+ OPCODE(o2_findObject),
+ OPCODE(o2_walkActorToObject),
+ OPCODE(o2_setState01),
+ /* B8 */
+ OPCODE(o2_isLessEqual),
+ OPCODE(o2_doSentence),
+ OPCODE(o2_subtract),
+ OPCODE(o2_waitForActor),
+ /* BC */
+ OPCODE(o5_stopSound),
+ OPCODE(o2_setActorElevation),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifNotState01),
+ /* C0 */
+ OPCODE(o2_endCutscene),
+ OPCODE(o2_putActor),
+ OPCODE(o2_startScript),
+ OPCODE(o2_getActorX),
+ /* C4 */
+ OPCODE(o2_isLess),
+ OPCODE(o2_drawObject),
+ OPCODE(o5_decrement),
+ OPCODE(o2_clearState08),
+ /* C8 */
+ OPCODE(o5_isEqual),
+ OPCODE(o5_faceActor),
+ OPCODE(o2_chainScript),
+ OPCODE(o2_setObjPreposition),
+ /* CC */
+ OPCODE(o5_pseudoRoom),
+ OPCODE(o5_walkActorToActor),
+ OPCODE(o2_putActorAtObject),
+ OPCODE(o2_ifState08),
+ /* D0 */
+ OPCODE(o2_pickupObject),
+ OPCODE(o2_animateActor),
+ OPCODE(o5_actorFollowCamera),
+ OPCODE(o2_actorOps),
+ /* D4 */
+ OPCODE(o5_setObjectName),
+ OPCODE(o2_actorFromPos),
+ OPCODE(o5_getActorMoving),
+ OPCODE(o2_setState02),
+ /* D8 */
+ OPCODE(o5_printEgo),
+ OPCODE(o2_doSentence),
+ OPCODE(o2_add),
+ OPCODE(o2_setBitVar),
+ /* DC */
+ OPCODE(o2_dummy),
+ OPCODE(o2_ifClassOfIs),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifNotState02),
+ /* E0 */
+ OPCODE(o2_cursorCommand),
+ OPCODE(o2_putActor),
+ OPCODE(o2_stopScript),
+ OPCODE(o5_getActorFacing),
+ /* E4 */
+ OPCODE(o2_loadRoomWithEgo),
+ OPCODE(o2_drawObject),
+ OPCODE(o5_getClosestObjActor),
+ OPCODE(o2_clearState04),
+ /* E8 */
+ OPCODE(o5_isScriptRunning),
+ OPCODE(o2_setOwnerOf),
+ OPCODE(o2_subIndirect),
+ OPCODE(o2_dummy),
+ /* EC */
+ OPCODE(o2_getObjPreposition),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o2_dummy),
+ OPCODE(o2_ifState04),
+ /* F0 */
+ OPCODE(o2_lights),
+ OPCODE(o5_getActorCostume),
+ OPCODE(o5_loadRoom),
+ OPCODE(o2_roomOps),
+ /* F4 */
+ OPCODE(o5_getDist),
+ OPCODE(o2_findObject),
+ OPCODE(o2_walkActorToObject),
+ OPCODE(o2_clearState01),
+ /* F8 */
+ OPCODE(o2_isGreater),
+ OPCODE(o2_doSentence),
+ OPCODE(o2_verbOps),
+ OPCODE(o2_getActorWalkBox),
+ /* FC */
+ OPCODE(o5_isSoundRunning),
+ OPCODE(o2_setActorElevation),
+ OPCODE(o2_walkActorTo),
+ OPCODE(o2_ifState01)
+ };
+
+ _opcodesV2 = opcodes;
+}
+
+#define SENTENCE_SCRIPT 2
+
+#define PARAM_1 0x80
+#define PARAM_2 0x40
+#define PARAM_3 0x20
+
+void ScummEngine_v2::executeOpcode(byte i) {
+ OpcodeProcV2 op = _opcodesV2[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v2::getOpcodeDesc(byte i) {
+ return _opcodesV2[i].desc;
+}
+
+int ScummEngine_v2::getVar() {
+ return readVar(fetchScriptByte());
+}
+
+void ScummEngine_v2::decodeParseString() {
+ byte buffer[512];
+ byte *ptr = buffer;
+ byte c;
+ bool insertSpace = false;
+
+ while ((c = fetchScriptByte())) {
+
+ insertSpace = (c & 0x80) != 0;
+ c &= 0x7f;
+
+ if (c < 8) {
+ // Special codes as seen in CHARSET_1 etc. My guess is that they
+ // have a similar function as the corresponding embedded stuff in modern
+ // games. Hence for now we convert them to the modern format.
+ // This might allow us to reuse the existing code.
+ *ptr++ = 0xFF;
+ *ptr++ = c;
+ if (c > 3) {
+ *ptr++ = fetchScriptByte();
+ *ptr++ = 0;
+ }
+ } else
+ *ptr++ = c;
+
+ if (insertSpace)
+ *ptr++ = ' ';
+
+ }
+ *ptr = 0;
+
+ int textSlot = 0;
+ _string[textSlot].xpos = 0;
+ _string[textSlot].ypos = 0;
+ if (_platform == Common::kPlatformNES)
+ _string[textSlot].right = 256;
+ else
+ _string[textSlot].right = 320;
+ _string[textSlot].center = false;
+ _string[textSlot].overhead = false;
+
+ if (_gameId == GID_MANIAC && _actorToPrintStrFor == 0xFF) {
+ if (_platform == Common::kPlatformC64) {
+ _string[textSlot].color = 14;
+ } else if (_demoMode) {
+ _string[textSlot].color = (_version == 2) ? 15 : 1;
+ }
+ }
+
+ actorTalk(buffer);
+}
+
+int ScummEngine_v2::readVar(uint var) {
+ if (var >= 14 && var <= 16)
+ var = _scummVars[var];
+
+ checkRange(_numVariables - 1, 0, var, "Variable %d out of range(r)");
+ debugC(DEBUG_VARS, "readvar(%d) = %d", var, _scummVars[var]);
+ return _scummVars[var];
+}
+
+void ScummEngine_v2::writeVar(uint var, int value) {
+ checkRange(_numVariables - 1, 0, var, "Variable %d out of range(r)");
+ debugC(DEBUG_VARS, "writeVar(%d) = %d", var, value);
+
+ if (VAR_CUTSCENEEXIT_KEY != 0xFF && var == VAR_CUTSCENEEXIT_KEY) {
+ // Remap the cutscene exit key in earlier games
+ if (value == 4 || value == 13 || value == 64)
+ value = 27;
+ }
+
+ _scummVars[var] = value;
+
+ // HACK: Ender's hack around a bug in Maniac. If you take the last dime from
+ // Weird Ed's piggybank, this disables the New Kid option and runs the Jail
+ // cutscene. Script 116 sets var[175] to 1, which disables New Kid in
+ // script 164. Unfortunatly, when New Kid is reenabled (var[175] = 0) in
+ // script 89, script 164 isn't reran to redraw it. Why? Dunno. Hack? Yes.
+ if ((var == 175) && (_gameId == GID_MANIAC) && (vm.slot[_currentScript].number == 89))
+ runScript(164, 0, 0, 0);
+}
+
+void ScummEngine_v2::getResultPosIndirect() {
+ _resultVarNumber = _scummVars[fetchScriptByte()];
+}
+
+void ScummEngine_v2::getResultPos() {
+ _resultVarNumber = fetchScriptByte();
+}
+
+void ScummEngine_v2::setStateCommon(byte type) {
+ int obj = getVarOrDirectWord(PARAM_1);
+ putState(obj, getState(obj) | type);
+}
+
+void ScummEngine_v2::clearStateCommon(byte type) {
+ int obj = getVarOrDirectWord(PARAM_1);
+ putState(obj, getState(obj) & ~type);
+}
+
+void ScummEngine_v2::o2_setState08() {
+ int obj = getVarOrDirectWord(PARAM_1);
+ putState(obj, getState(obj) | 0x08);
+ markObjectRectAsDirty(obj);
+ clearDrawObjectQueue();
+}
+
+void ScummEngine_v2::o2_clearState08() {
+ int obj = getVarOrDirectWord(PARAM_1);
+ putState(obj, getState(obj) & ~0x08);
+ markObjectRectAsDirty(obj);
+ clearDrawObjectQueue();
+}
+
+void ScummEngine_v2::o2_setState04() {
+ setStateCommon(0x04);
+}
+
+void ScummEngine_v2::o2_clearState04() {
+ clearStateCommon(0x04);
+}
+
+void ScummEngine_v2::o2_setState02() {
+ setStateCommon(0x02);
+}
+
+void ScummEngine_v2::o2_clearState02() {
+ clearStateCommon(0x02);
+}
+
+void ScummEngine_v2::o2_setState01() {
+ setStateCommon(0x01);
+}
+
+void ScummEngine_v2::o2_clearState01() {
+ clearStateCommon(0x01);
+}
+
+void ScummEngine_v2::o2_assignVarWordIndirect() {
+ getResultPosIndirect();
+ setResult(getVarOrDirectWord(PARAM_1));
+}
+
+void ScummEngine_v2::o2_assignVarByte() {
+ getResultPos();
+ setResult(fetchScriptByte());
+}
+
+void ScummEngine_v2::o2_setObjPreposition() {
+ int obj = getVarOrDirectWord(PARAM_1);
+ int unk = fetchScriptByte();
+
+ if (_platform == Common::kPlatformNES)
+ return;
+
+ if (whereIsObject(obj) != WIO_NOT_FOUND) {
+ // FIXME: this might not work properly the moment we save and restore the game.
+ byte *ptr = getOBCDFromObject(obj) + 12;
+ *ptr &= 0x1F;
+ *ptr |= unk << 5;
+ }
+}
+
+void ScummEngine_v2::o2_getObjPreposition() {
+ getResultPos();
+ int obj = getVarOrDirectWord(PARAM_1);
+
+ if (whereIsObject(obj) != WIO_NOT_FOUND) {
+ byte *ptr = getOBCDFromObject(obj) + 12;
+ setResult(*ptr >> 5);
+ } else {
+ setResult(0xFF);
+ }
+}
+
+void ScummEngine_v2::o2_setBitVar() {
+ int var = fetchScriptWord();
+ byte a = getVarOrDirectByte(PARAM_1);
+
+ int bit_var = var + a;
+ int bit_offset = bit_var & 0x0f;
+ bit_var >>= 4;
+
+ if (getVarOrDirectByte(PARAM_2))
+ _scummVars[bit_var] |= (1 << bit_offset);
+ else
+ _scummVars[bit_var] &= ~(1 << bit_offset);
+
+}
+
+void ScummEngine_v2::o2_getBitVar() {
+ getResultPos();
+ int var = fetchScriptWord();
+ byte a = getVarOrDirectByte(PARAM_1);
+
+ int bit_var = var + a;
+ int bit_offset = bit_var & 0x0f;
+ bit_var >>= 4;
+
+ setResult((_scummVars[bit_var] & (1 << bit_offset)) ? 1 : 0);
+}
+
+void ScummEngine_v2::ifStateCommon(byte type) {
+ int obj = getVarOrDirectWord(PARAM_1);
+
+ if ((getState(obj) & type) == 0)
+ o5_jumpRelative();
+ else
+ ignoreScriptWord();
+}
+
+void ScummEngine_v2::ifNotStateCommon(byte type) {
+ int obj = getVarOrDirectWord(PARAM_1);
+
+ if ((getState(obj) & type) != 0)
+ o5_jumpRelative();
+ else
+ ignoreScriptWord();
+}
+
+void ScummEngine_v2::o2_ifState08() {
+ ifStateCommon(0x08);
+}
+
+void ScummEngine_v2::o2_ifNotState08() {
+ ifNotStateCommon(0x08);
+}
+
+void ScummEngine_v2::o2_ifState04() {
+ ifStateCommon(0x04);
+}
+
+void ScummEngine_v2::o2_ifNotState04() {
+ ifNotStateCommon(0x04);
+}
+
+void ScummEngine_v2::o2_ifState02() {
+ ifStateCommon(0x02);
+}
+
+void ScummEngine_v2::o2_ifNotState02() {
+ ifNotStateCommon(0x02);
+}
+
+void ScummEngine_v2::o2_ifState01() {
+ ifStateCommon(0x01);
+}
+
+void ScummEngine_v2::o2_ifNotState01() {
+ ifNotStateCommon(0x01);
+}
+
+void ScummEngine_v2::o2_addIndirect() {
+ int a;
+ getResultPosIndirect();
+ a = getVarOrDirectWord(PARAM_1);
+ _scummVars[_resultVarNumber] += a;
+}
+
+void ScummEngine_v2::o2_subIndirect() {
+ int a;
+ getResultPosIndirect();
+ a = getVarOrDirectWord(PARAM_1);
+ _scummVars[_resultVarNumber] -= a;
+}
+
+void ScummEngine_v2::o2_add() {
+ int a;
+ getResultPos();
+ a = getVarOrDirectWord(PARAM_1);
+ _scummVars[_resultVarNumber] += a;
+}
+
+void ScummEngine_v2::o2_subtract() {
+ int a;
+ getResultPos();
+ a = getVarOrDirectWord(PARAM_1);
+ _scummVars[_resultVarNumber] -= a;
+}
+
+void ScummEngine_v2::o2_waitForActor() {
+ Actor *a = derefActor(getVarOrDirectByte(PARAM_1), "o2_waitForActor");
+ if (a->_moving) {
+ _scriptPointer -= 2;
+ o5_breakHere();
+ }
+}
+
+void ScummEngine_v2::o2_waitForMessage() {
+
+ if (VAR(VAR_HAVE_MSG)) {
+ _scriptPointer--;
+ o5_breakHere();
+ }
+}
+
+void ScummEngine_v2::o2_waitForSentence() {
+ if (!_sentenceNum && !isScriptInUse(SENTENCE_SCRIPT))
+ return;
+
+ _scriptPointer--;
+ o5_breakHere();
+}
+
+void ScummEngine_v2::o2_actorOps() {
+ int act = getVarOrDirectByte(PARAM_1);
+ int arg = getVarOrDirectByte(PARAM_2);
+ Actor *a;
+ int i;
+
+ _opcode = fetchScriptByte();
+ if (act == 0 && _opcode == 5) {
+ // This case happens in the Zak/MM bootscripts, to set the default talk color (9).
+ _string[0].color = arg;
+ return;
+ }
+
+ a = derefActor(act, "actorOps");
+
+ switch (_opcode) {
+ case 1: // SO_SOUND
+ a->_sound[0] = arg;
+ break;
+ case 2: // SO_PALETTE
+ if (_version == 1)
+ i = act;
+ else
+ i = fetchScriptByte();
+
+ a->setPalette(i, arg);
+ break;
+ case 3: // SO_ACTOR_NAME
+ loadPtrToResource(rtActorName, a->_number, NULL);
+ break;
+ case 4: // SO_COSTUME
+ a->setActorCostume(arg);
+ break;
+ case 5: // SO_TALK_COLOR
+ if (_gameId == GID_MANIAC && _version == 2 && _demoMode && arg == 1)
+ a->_talkColor = 15;
+ else
+ a->_talkColor = arg;
+ break;
+ default:
+ error("o2_actorOps: opcode %d not yet supported", _opcode);
+ }
+}
+
+void ScummEngine_v2::o2_restart() {
+ restart();
+}
+
+void ScummEngine_v2::o2_drawObject() {
+ int obj, idx, i;
+ ObjectData *od;
+ uint16 x, y, w, h;
+ int xpos, ypos;
+
+ obj = getVarOrDirectWord(PARAM_1);
+ xpos = getVarOrDirectByte(PARAM_2);
+ ypos = getVarOrDirectByte(PARAM_3);
+
+ idx = getObjectIndex(obj);
+ if (idx == -1)
+ return;
+
+ od = &_objs[idx];
+ if (xpos != 0xFF) {
+ od->walk_x += (xpos * 8) - od->x_pos;
+ od->x_pos = xpos * 8;
+ od->walk_y += (ypos * 8) - od->y_pos;
+ od->y_pos = ypos * 8;
+ }
+ addObjectToDrawQue(idx);
+
+ x = od->x_pos;
+ y = od->y_pos;
+ w = od->width;
+ h = od->height;
+
+ i = _numLocalObjects;
+ while (i--) {
+ if (_objs[i].obj_nr && _objs[i].x_pos == x && _objs[i].y_pos == y && _objs[i].width == w && _objs[i].height == h)
+ putState(_objs[i].obj_nr, getState(_objs[i].obj_nr) & ~0x08);
+ }
+
+ putState(obj, getState(od->obj_nr) | 0x08);
+}
+
+void ScummEngine_v2::o2_resourceRoutines() {
+ const ResTypes resTypes[] = {
+ rtNumTypes, // Invalid
+ rtNumTypes, // Invalid
+ rtCostume,
+ rtRoom,
+ rtNumTypes, // Invalid
+ rtScript,
+ rtSound
+ };
+ int resid = getVarOrDirectByte(PARAM_1);
+ int opcode = fetchScriptByte();
+
+ ResTypes type = rtNumTypes;
+ if (0 <= (opcode >> 4) && (opcode >> 4) < (int)ARRAYSIZE(resTypes))
+ type = resTypes[opcode >> 4];
+
+ if ((opcode & 0x0f) == 0 || type == rtNumTypes)
+ return;
+
+ // HACK V2 Maniac Mansion tries to load an invalid sound resource in demo script.
+ if (_gameId == GID_MANIAC && _version == 2 && vm.slot[_currentScript].number == 9 && type == rtSound && resid == 1)
+ return;
+
+ if ((opcode & 0x0f) == 1) {
+ ensureResourceLoaded(type, resid);
+ } else {
+ if (opcode & 1)
+ res.lock(type, resid);
+ else
+ res.unlock(type, resid);
+ }
+}
+
+void ScummEngine_v2::o2_verbOps() {
+ int verb = fetchScriptByte();
+ int slot, state;
+
+ switch (verb) {
+ case 0: // SO_DELETE_VERBS
+ slot = getVarOrDirectByte(PARAM_1) + 1;
+ assert(0 < slot && slot < _numVerbs);
+
+ //printf("o2_verbOps delete slot = %d\n", slot);
+ killVerb(slot);
+ break;
+
+ case 0xFF: // Verb On/Off
+ verb = fetchScriptByte();
+ state = fetchScriptByte();
+ slot = getVerbSlot(verb, 0);
+
+ //printf("o2_verbOps Verb On/Off: verb = %d, slot = %d, state = %d\n", verb, slot, state);
+
+ _verbs[slot].curmode = state;
+
+ break;
+
+ default: { // New Verb
+ int x = fetchScriptByte() * 8;
+ int y = fetchScriptByte() * 8;
+ slot = getVarOrDirectByte(PARAM_1) + 1;
+ int prep = fetchScriptByte(); // Only used in V1?
+ // V1 Maniac verbs are relative to the 'verb area' - under the sentence
+ if (_platform == Common::kPlatformNES)
+ x += 8;
+ else if ((_gameId == GID_MANIAC) && (_version == 1))
+ y += 8;
+
+ //printf("o2_verbOps: verb = %d, slot = %d, x = %d, y = %d, unk = %d, name = %s\n",
+ // verb, slot, x, y, prep, _scriptPointer);
+
+ VerbSlot *vs;
+ assert(0 < slot && slot < _numVerbs);
+
+ vs = &_verbs[slot];
+ vs->verbid = verb;
+ if (_platform == Common::kPlatformNES) {
+ vs->color = 1;
+ vs->hicolor = 1;
+ vs->dimcolor = 1;
+ } else if (_version == 1) {
+ vs->color = (_gameId == GID_MANIAC && _demoMode) ? 16 : 5;
+ vs->hicolor = 7;
+ vs->dimcolor = 11;
+ } else {
+ vs->color = (_gameId == GID_MANIAC && _demoMode) ? 13 : 2;
+ vs->hicolor = 14;
+ vs->dimcolor = 8;
+ }
+ vs->type = kTextVerbType;
+ vs->charset_nr = _string[0]._default.charset;
+ vs->curmode = 1;
+ vs->saveid = 0;
+ vs->key = 0;
+ vs->center = 0;
+ vs->imgindex = 0;
+ vs->prep = prep;
+
+ vs->curRect.left = x;
+ vs->curRect.top = y;
+
+ // FIXME: again, this map depends on the language of the game.
+ // E.g. a german keyboard has 'z' and 'y' swapped, while a french
+ // keyboard starts with "awert", etc.
+ const char keyboard[] = {
+ 'q','w','e','r','t',
+ 'a','s','d','f','g',
+ 'z','x','c','v','b'
+ };
+ if (1 <= slot && slot <= ARRAYSIZE(keyboard))
+ vs->key = keyboard[slot - 1];
+
+ // It follows the verb name
+ loadPtrToResource(rtVerb, slot, NULL);
+ }
+ break;
+ }
+
+ // Force redraw of the modified verb slot
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+}
+
+void ScummEngine_v2::o2_doSentence() {
+ int a;
+ SentenceTab *st;
+
+ a = getVarOrDirectByte(PARAM_1);
+ if (a == 0xFC) {
+ _sentenceNum = 0;
+ stopScript(SENTENCE_SCRIPT);
+ return;
+ }
+ if (a == 0xFB) {
+ resetSentence();
+ return;
+ }
+
+ st = &_sentence[_sentenceNum++];
+
+ st->verb = a;
+ st->objectA = getVarOrDirectWord(PARAM_2);
+ st->objectB = getVarOrDirectWord(PARAM_3);
+ st->preposition = (st->objectB != 0);
+ st->freezeCount = 0;
+
+ // Execute or print the sentence
+ _opcode = fetchScriptByte();
+ switch (_opcode) {
+ case 0:
+ // Do nothing (besides setting up the sentence above)
+ break;
+ case 1:
+ // Execute the sentence
+ _sentenceNum--;
+
+ if (st->verb == 254) {
+ ScummEngine::stopObjectScript(st->objectA);
+ } else {
+ bool isBackgroundScript;
+ bool isSpecialVerb;
+ if (st->verb != 253 && st->verb != 250) {
+ VAR(VAR_ACTIVE_VERB) = st->verb;
+ VAR(VAR_ACTIVE_OBJECT1) = st->objectA;
+ VAR(VAR_ACTIVE_OBJECT2) = st->objectB;
+
+ isBackgroundScript = false;
+ isSpecialVerb = false;
+ } else {
+ isBackgroundScript = (st->verb == 250);
+ isSpecialVerb = true;
+ st->verb = 253;
+ }
+
+ // Check if an object script for this object is already running. If
+ // so, reuse its script slot. Note that we abuse two script flags:
+ // freezeResistant and recursive. We use them to track two
+ // script flags used in V1/V2 games. The main reason we do it this
+ // ugly evil way is to avoid having to introduce yet another save
+ // game revision.
+ int slot = -1;
+ ScriptSlot *ss;
+ int i;
+
+ ss = vm.slot;
+ for (i = 0; i < NUM_SCRIPT_SLOT; i++, ss++) {
+ if (st->objectA == ss->number &&
+ ss->freezeResistant == isBackgroundScript &&
+ ss->recursive == isSpecialVerb &&
+ (ss->where == WIO_ROOM || ss->where == WIO_INVENTORY || ss->where == WIO_FLOBJECT)) {
+ slot = i;
+ break;
+ }
+ }
+
+ runObjectScript(st->objectA, st->verb, isBackgroundScript, isSpecialVerb, NULL, slot);
+ }
+ break;
+ case 2:
+ // Print the sentence
+ _sentenceNum--;
+
+ VAR(VAR_SENTENCE_VERB) = st->verb;
+ VAR(VAR_SENTENCE_OBJECT1) = st->objectA;
+ VAR(VAR_SENTENCE_OBJECT2) = st->objectB;
+
+ o2_drawSentence();
+ break;
+ default:
+ error("o2_doSentence: unknown subopcode %d", _opcode);
+ }
+}
+
+void ScummEngine_v2::o2_drawSentence() {
+ Common::Rect sentenceline;
+ static char sentence[256];
+ const byte *temp;
+ int slot = getVerbSlot(VAR(VAR_SENTENCE_VERB), 0);
+
+ if (!((_userState & 32) || (_platform == Common::kPlatformNES && _userState & 0xe0)))
+ return;
+
+ if (getResourceAddress(rtVerb, slot))
+ strcpy(sentence, (char*)getResourceAddress(rtVerb, slot));
+ else
+ return;
+
+ if (VAR(VAR_SENTENCE_OBJECT1) > 0) {
+ temp = getObjOrActorName(VAR(VAR_SENTENCE_OBJECT1));
+ if (temp) {
+ strcat(sentence, " ");
+ strcat(sentence, (const char*)temp);
+ }
+
+ // For V1 games, the engine must compute the preposition.
+ // In all other Scumm versions, this is done by the sentence script.
+ if ((_gameId == GID_MANIAC && _version == 1 && !(_platform == Common::kPlatformNES)) && (VAR(VAR_SENTENCE_PREPOSITION) == 0)) {
+ if (_verbs[slot].prep == 0xFF) {
+ byte *ptr = getOBCDFromObject(VAR(VAR_SENTENCE_OBJECT1));
+ assert(ptr);
+ VAR(VAR_SENTENCE_PREPOSITION) = (*(ptr + 12) >> 5);
+ } else
+ VAR(VAR_SENTENCE_PREPOSITION) = _verbs[slot].prep;
+ }
+ }
+
+ if (0 < VAR(VAR_SENTENCE_PREPOSITION) && VAR(VAR_SENTENCE_PREPOSITION) <= 4) {
+ // The prepositions, like the fonts, were hard code in the engine. Thus
+ // we have to do that, too, and provde localized versions for all the
+ // languages MM/Zak are available in.
+ //
+ // The order here matches the one defined in gameDetector.h
+ const char *prepositions[][5] = {
+ { " ", " in", " with", " on", " to" }, // English
+ { " ", " mit", " mit", " mit", " zu" }, // German
+ { " ", " dans", " avec", " sur", " <" }, // French
+ { " ", " in", " con", " su", " a" }, // Italian
+ { " ", " in", " with", " on", " to" }, // Portugese
+ { " ", " en", " con", " en", " a" }, // Spanish
+ { " ", " in", " with", " on", " to" }, // Japanese
+ { " ", " in", " with", " on", " to" }, // Chinese
+ { " ", " in", " with", " on", " to" } // Korean
+ };
+ int lang = (_language <= 8) ? _language : 0; // Default to english
+ if (_platform == Common::kPlatformNES) {
+ strcat(sentence, (const char *)(getResourceAddress(rtCostume, 78) + VAR(VAR_SENTENCE_PREPOSITION) * 8 + 2));
+ } else
+ strcat(sentence, prepositions[lang][VAR(VAR_SENTENCE_PREPOSITION)]);
+ }
+
+ if (VAR(VAR_SENTENCE_OBJECT2) > 0) {
+ temp = getObjOrActorName(VAR(VAR_SENTENCE_OBJECT2));
+ if (temp) {
+ strcat(sentence, " ");
+ strcat(sentence, (const char*)temp);
+ }
+ }
+
+ _string[2].charset = 1;
+ _string[2].ypos = virtscr[kVerbVirtScreen].topline;
+ _string[2].xpos = 0;
+ if (_platform == Common::kPlatformNES) {
+ _string[2].xpos = 16;
+ _string[2].color = 0;
+ } else if (_version == 1)
+ _string[2].color = 16;
+ else
+ _string[2].color = 13;
+
+ byte string[80];
+ char *ptr = sentence;
+ int i = 0, len = 0;
+
+ // Maximum length of printable characters
+ int maxChars = (_platform == Common::kPlatformNES) ? 60: 40;
+ while (*ptr) {
+ if (*ptr != '@')
+ len++;
+ if (len > maxChars) {
+ break;
+ }
+
+ string[i++] = *ptr++;
+
+ if (_platform == Common::kPlatformNES && len == 30) {
+ string[i++] = 0xFF;
+ string[i++] = 8;
+ }
+ }
+ string[i] = 0;
+
+ if (_platform == Common::kPlatformNES) {
+ sentenceline.top = virtscr[kVerbVirtScreen].topline;
+ sentenceline.bottom = virtscr[kVerbVirtScreen].topline + 16;
+ sentenceline.left = 16;
+ sentenceline.right = 255;
+ } else {
+ sentenceline.top = virtscr[kVerbVirtScreen].topline;
+ sentenceline.bottom = virtscr[kVerbVirtScreen].topline + 8;
+ sentenceline.left = 0;
+ sentenceline.right = 319;
+ }
+ restoreBG(sentenceline);
+
+ drawString(2, (byte*)string);
+}
+
+void ScummEngine_v2::o2_ifClassOfIs() {
+ int obj = getVarOrDirectWord(PARAM_1);
+ int clsop = getVarOrDirectByte(PARAM_2);
+ byte *obcd = getOBCDFromObject(obj);
+
+ if (obcd == 0) {
+ o5_jumpRelative();
+ return;
+ }
+
+ byte cls = *(obcd + 6);
+ if ((cls & clsop) != clsop) {
+ o5_jumpRelative();
+ return;
+ }
+ ignoreScriptWord();
+}
+
+void ScummEngine_v2::o2_walkActorTo() {
+ int x, y;
+ Actor *a;
+
+ int act = getVarOrDirectByte(PARAM_1);
+
+ // WORKAROUND bug #1252606
+ if (_gameId == GID_ZAK && _version == 1 && vm.slot[_currentScript].number == 115 && act == 249) {
+ act = VAR(VAR_EGO);
+ }
+
+ a = derefActor(act, "o2_walkActorTo");
+
+ x = getVarOrDirectByte(PARAM_2) * 8;
+ y = getVarOrDirectByte(PARAM_3) * 2;
+
+ a->startWalkActor(x, y, -1);
+}
+
+void ScummEngine_v2::o2_putActor() {
+ int act = getVarOrDirectByte(PARAM_1);
+ int x, y;
+ Actor *a;
+
+ a = derefActor(act, "o2_putActor");
+
+ x = getVarOrDirectByte(PARAM_2) * 8;
+ y = getVarOrDirectByte(PARAM_3) * 2;
+
+ a->putActor(x, y, a->_room);
+}
+
+void ScummEngine_v2::o2_startScript() {
+ int script = getVarOrDirectByte(PARAM_1);
+
+ if (!_copyProtection) {
+ // The enhanced version of Zak McKracken included in the
+ // SelectWare Classic Collection bundle used CD check instead
+ // of the usual key code check at airports.
+ if ((_gameId == GID_ZAK) && (script == 15) && (_roomResource == 45))
+ return;
+ }
+
+ runScript(script, 0, 0, 0);
+}
+
+void ScummEngine_v2::o2_stopScript() {
+ int script;
+
+ script = getVarOrDirectByte(PARAM_1);
+
+ if ((_gameId == GID_ZAK) && (_roomResource == 7) && (vm.slot[_currentScript].number == 10001)) {
+ // FIXME: Nasty hack for bug #771499
+ // Don't let the exit script for room 7 stop the buy script (24),
+ // switching to the number selection keypad (script 15)
+ if ((script == 24) && isScriptRunning(15))
+ return;
+ }
+
+ if (script == 0)
+ script = vm.slot[_currentScript].number;
+
+ if (_currentScript != 0 && vm.slot[_currentScript].number == script)
+ stopObjectCode();
+ else
+ stopScript(script);
+}
+
+void ScummEngine_v2::o2_panCameraTo() {
+ panCameraTo(getVarOrDirectByte(PARAM_1) * 8, 0);
+}
+
+void ScummEngine_v2::o2_walkActorToObject() {
+ int obj;
+ Actor *a;
+
+ a = derefActor(getVarOrDirectByte(PARAM_1), "o2_walkActorToObject");
+ obj = getVarOrDirectWord(PARAM_2);
+ if (whereIsObject(obj) != WIO_NOT_FOUND) {
+ int x, y, dir;
+ getObjectXYPos(obj, x, y, dir);
+ a->startWalkActor(x, y, dir);
+ }
+}
+
+void ScummEngine_v2::o2_putActorAtObject() {
+ int obj, x, y;
+ Actor *a;
+
+ a = derefActor(getVarOrDirectByte(PARAM_1), "o2_putActorAtObject");
+
+ obj = getVarOrDirectWord(PARAM_2);
+ if (whereIsObject(obj) != WIO_NOT_FOUND)
+ getObjectXYPos(obj, x, y);
+ else {
+ x = 240;
+ y = 120;
+ }
+
+ a->putActor(x, y, a->_room);
+}
+
+void ScummEngine_v2::o2_getActorElevation() {
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o2_getActorElevation");
+ setResult(a->getElevation() / 2);
+}
+
+void ScummEngine_v2::o2_setActorElevation() {
+ int act = getVarOrDirectByte(PARAM_1);
+ int elevation = (int8)getVarOrDirectByte(PARAM_2);
+
+ Actor *a = derefActor(act, "o2_setActorElevation");
+ a->setElevation(elevation * 2);
+}
+
+void ScummEngine_v2::o2_animateActor() {
+ int act = getVarOrDirectByte(PARAM_1);
+ int anim = getVarOrDirectByte(PARAM_2);
+
+ Actor *a = derefActor(act, "o2_animateActor");
+ a->animateActor(anim);
+}
+
+void ScummEngine_v2::o2_actorFromPos() {
+ int x, y;
+ getResultPos();
+ x = getVarOrDirectByte(PARAM_1) * 8;
+ y = getVarOrDirectByte(PARAM_2) * 2;
+ setResult(getActorFromPos(x, y));
+}
+
+void ScummEngine_v2::o2_findObject() {
+ int obj;
+ getResultPos();
+ int x = getVarOrDirectByte(PARAM_1) * 8;
+ int y = getVarOrDirectByte(PARAM_2) * 2;
+ obj = findObject(x, y);
+ if (obj == 0 && (_platform == Common::kPlatformNES) && (_userState & 0x40)) {
+ if (_mouseOverBoxV2 >= 0 && _mouseOverBoxV2 < 4)
+ obj = findInventory(VAR(VAR_EGO), _mouseOverBoxV2 + _inventoryOffset + 1);
+ }
+ setResult(obj);
+}
+
+void ScummEngine_v2::o2_getActorX() {
+ int a;
+ getResultPos();
+
+ a = getVarOrDirectByte(PARAM_1);
+ setResult(getObjX(a) / 8);
+}
+
+void ScummEngine_v2::o2_getActorY() {
+ int a;
+ getResultPos();
+
+ a = getVarOrDirectByte(PARAM_1);
+ setResult(getObjY(a) / 2);
+}
+
+void ScummEngine_v2::o2_isGreater() {
+ uint16 a = getVar();
+ uint16 b = getVarOrDirectWord(PARAM_1);
+ if (b > a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v2::o2_isGreaterEqual() {
+ uint16 a = getVar();
+ uint16 b = getVarOrDirectWord(PARAM_1);
+ if (b >= a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v2::o2_isLess() {
+ uint16 a = getVar();
+ uint16 b = getVarOrDirectWord(PARAM_1);
+
+ if (b < a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v2::o2_isLessEqual() {
+ uint16 a = getVar();
+ uint16 b = getVarOrDirectWord(PARAM_1);
+ if (b <= a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v2::o2_lights() {
+ int a, b, c;
+
+ a = getVarOrDirectByte(PARAM_1);
+ b = fetchScriptByte();
+ c = fetchScriptByte();
+
+ if (c == 0) {
+ if (_gameId == GID_MANIAC && _version == 1 && !(_platform == Common::kPlatformNES)) {
+ // Convert older light mode values into
+ // equivalent values.of later games
+ // 0 Darkness
+ // 1 Flashlight
+ // 2 Lighted area
+ if (a == 2)
+ VAR(VAR_CURRENT_LIGHTS) = 11;
+ else if (a == 1)
+ VAR(VAR_CURRENT_LIGHTS) = 4;
+ else
+ VAR(VAR_CURRENT_LIGHTS) = 0;
+ } else
+ VAR(VAR_CURRENT_LIGHTS) = a;
+ } else if (c == 1) {
+ _flashlight.xStrips = a;
+ _flashlight.yStrips = b;
+ }
+ _fullRedraw = true;
+}
+
+void ScummEngine_v2::o2_loadRoomWithEgo() {
+ Actor *a;
+ int obj, room, x, y, x2, y2, dir;
+
+ obj = getVarOrDirectWord(PARAM_1);
+ room = getVarOrDirectByte(PARAM_2);
+
+ a = derefActor(VAR(VAR_EGO), "o2_loadRoomWithEgo");
+
+ a->putActor(0, 0, room);
+ _egoPositioned = false;
+
+ x = (int8)fetchScriptByte() * 8;
+ y = (int8)fetchScriptByte() * 2;
+
+ startScene(a->_room, a, obj);
+
+ getObjectXYPos(obj, x2, y2, dir);
+ a->putActor(x2, y2, _currentRoom);
+ a->setDirection(dir + 180);
+
+ camera._dest.x = camera._cur.x = a->_pos.x;
+ setCameraAt(a->_pos.x, a->_pos.y);
+ setCameraFollows(a);
+
+ _fullRedraw = true;
+
+ resetSentence();
+
+ if (x >= 0 && y >= 0) {
+ a->startWalkActor(x, y, -1);
+ }
+ runScript(5, 0, 0, 0);
+}
+
+void ScummEngine_v2::o2_setOwnerOf() {
+ int obj, owner;
+
+ obj = getVarOrDirectWord(PARAM_1);
+ owner = getVarOrDirectByte(PARAM_2);
+
+ setOwnerOf(obj, owner);
+}
+
+void ScummEngine_v2::o2_delay() {
+ int delay = fetchScriptByte();
+ delay |= fetchScriptByte() << 8;
+ delay |= fetchScriptByte() << 16;
+ delay = 0xFFFFFF - delay;
+
+ vm.slot[_currentScript].delay = delay;
+ vm.slot[_currentScript].status = ssPaused;
+ o5_breakHere();
+}
+
+void ScummEngine_v2::o2_setBoxFlags() {
+ int a, b;
+
+ a = getVarOrDirectByte(PARAM_1);
+ b = fetchScriptByte();
+ setBoxFlags(a, b);
+}
+
+void ScummEngine_v2::o2_setCameraAt() {
+ setCameraAtEx(getVarOrDirectByte(PARAM_1) * 8);
+}
+
+void ScummEngine_v2::o2_roomOps() {
+ int a = getVarOrDirectByte(PARAM_1);
+ int b = getVarOrDirectByte(PARAM_2);
+
+ _opcode = fetchScriptByte();
+ switch (_opcode & 0x1F) {
+ case 1: // SO_ROOM_SCROLL
+ a *= 8;
+ b *= 8;
+ if (a < (_screenWidth / 2))
+ a = (_screenWidth / 2);
+ if (b < (_screenWidth / 2))
+ b = (_screenWidth / 2);
+ if (a > _roomWidth - (_screenWidth / 2))
+ a = _roomWidth - (_screenWidth / 2);
+ if (b > _roomWidth - (_screenWidth / 2))
+ b = _roomWidth - (_screenWidth / 2);
+ VAR(VAR_CAMERA_MIN_X) = a;
+ VAR(VAR_CAMERA_MAX_X) = b;
+ break;
+ case 2: // SO_ROOM_COLOR
+ if (_version == 1) {
+ // V1 zak needs to know when room color is changed
+ _roomPalette[0] = 255;
+ _roomPalette[1] = a;
+ _roomPalette[2] = b;
+ } else {
+ _roomPalette[b] = a;
+ }
+ _fullRedraw = true;
+ break;
+ }
+}
+
+void ScummEngine_v2::o2_cutscene() {
+ vm.cutSceneData[0] = _userState | (_userPut ? 16 : 0);
+ vm.cutSceneData[1] = (int16)VAR(VAR_CURSORSTATE);
+ vm.cutSceneData[2] = _currentRoom;
+ vm.cutSceneData[3] = camera._mode;
+
+ VAR(VAR_CURSORSTATE) = 200;
+
+ // FIXME allows quotes script (173) to start during introudction of
+ // demo mode of V1 Maniac Mansion. setUserState was halting script
+ // 173 before it started.
+ if (!(_gameId == GID_MANIAC && _demoMode))
+ // Hide inventory, freeze scripts, hide cursor
+ setUserState(15);
+
+ _sentenceNum = 0;
+ stopScript(SENTENCE_SCRIPT);
+ resetSentence();
+
+ vm.cutScenePtr[0] = 0;
+}
+
+void ScummEngine_v2::o2_endCutscene() {
+ vm.cutSceneStackPointer = 0;
+
+ VAR(VAR_OVERRIDE) = 0;
+ vm.cutSceneScript[0] = 0;
+ vm.cutScenePtr[0] = 0;
+
+ VAR(VAR_CURSORSTATE) = vm.cutSceneData[1];
+
+ // Reset user state to values before cutscene
+ setUserState(vm.cutSceneData[0] | 7);
+
+ if ((_gameId == GID_MANIAC) && !(_platform == Common::kPlatformNES)) {
+ camera._mode = (byte) vm.cutSceneData[3];
+ if (camera._mode == kFollowActorCameraMode) {
+ actorFollowCamera(VAR(VAR_EGO));
+ } else if (vm.cutSceneData[2] != _currentRoom) {
+ startScene(vm.cutSceneData[2], 0, 0);
+ }
+ } else {
+ actorFollowCamera(VAR(VAR_EGO));
+ }
+}
+
+void ScummEngine_v2::o2_beginOverride() {
+ vm.cutScenePtr[0] = _scriptPointer - _scriptOrgPointer;
+ vm.cutSceneScript[0] = _currentScript;
+
+ // Skip the jump instruction following the override instruction
+ fetchScriptByte();
+ fetchScriptWord();
+}
+
+void ScummEngine_v2::o2_chainScript() {
+ int data = getVarOrDirectByte(PARAM_1);
+ stopScript(vm.slot[_currentScript].number);
+ _currentScript = 0xFF;
+ runScript(data, 0, 0, 0);
+}
+
+void ScummEngine_v2::o2_pickupObject() {
+ int obj = getVarOrDirectWord(PARAM_1);
+
+ if (obj < 1) {
+ error("pickupObject received invalid index %d (script %d)", obj, vm.slot[_currentScript].number);
+ }
+
+ if (getObjectIndex(obj) == -1)
+ return;
+
+ if (whereIsObject(obj) == WIO_INVENTORY) /* Don't take an */
+ return; /* object twice */
+
+ addObjectToInventory(obj, _roomResource);
+ markObjectRectAsDirty(obj);
+ putOwner(obj, VAR(VAR_EGO));
+ putState(obj, getState(obj) | 0xA);
+ clearDrawObjectQueue();
+
+ runInventoryScript(1);
+ if (_platform == Common::kPlatformNES)
+ _sound->addSoundToQueue(51); // play 'pickup' sound
+}
+
+void ScummEngine_v2::o2_cursorCommand() { // TODO: Define the magic numbers
+ uint16 cmd = getVarOrDirectWord(PARAM_1);
+ byte state = cmd >> 8;
+
+ if (cmd & 0xFF) {
+ VAR(VAR_CURSORSTATE) = cmd & 0xFF;
+ }
+
+ setUserState(state);
+}
+
+void ScummEngine_v2::setUserState(byte state) {
+ if (state & 4) { // Userface
+ if (_platform == Common::kPlatformNES)
+ _userState = (_userState & ~0xE0) | (state & 0xE0);
+ else
+ _userState = state & (32 | 64 | 128);
+ }
+
+ if (state & 1) { // Freeze
+ if (state & 8)
+ freezeScripts(0);
+ else
+ unfreezeScripts();
+ }
+
+ if (state & 2) { // Cursor Show/Hide
+ if (_platform == Common::kPlatformNES)
+ _userState = (_userState & ~0x10) | (state & 0x10);
+ if (state & 16) {
+ _userPut = 1;
+ _cursor.state = 1;
+ } else {
+ _userPut = 0;
+ _cursor.state = 0;
+ }
+ }
+
+ // Hide all verbs and inventory
+ Common::Rect rect;
+ rect.top = virtscr[kVerbVirtScreen].topline;
+ rect.bottom = virtscr[kVerbVirtScreen].topline + 8 * 88;
+ if (_platform == Common::kPlatformNES) {
+ rect.left = 16;
+ rect.right = 255;
+ } else {
+ rect.left = 0;
+ rect.right = 319;
+ }
+ restoreBG(rect);
+
+ // Draw all verbs and inventory
+ redrawVerbs();
+ runInventoryScript(1);
+}
+
+void ScummEngine_v2::o2_getActorWalkBox() {
+ Actor *a;
+ getResultPos();
+ a = derefActor(getVarOrDirectByte(PARAM_1), "o2_getActorWalkbox");
+ setResult(a->_walkbox);
+}
+
+void ScummEngine_v2::o2_dummy() {
+ // Opcode 238 is used in maniac and zak but has no purpose
+ if (_opcode != 238)
+ warning("o2_dummy invoked (opcode %d)", _opcode);
+}
+
+void ScummEngine_v2::o2_switchCostumeSet() {
+ // NES version of maniac uses this to switch between the two
+ // groups of costumes it has
+ if (_platform == Common::kPlatformNES)
+ NES_loadCostumeSet(fetchScriptByte());
+ else if (_platform == Common::kPlatformC64)
+ fetchScriptByte();
+ else
+ o2_dummy();
+}
+
+void ScummEngine_v2::resetSentence() {
+ VAR(VAR_SENTENCE_VERB) = VAR(VAR_BACKUP_VERB);
+ VAR(VAR_SENTENCE_OBJECT1) = 0;
+ VAR(VAR_SENTENCE_OBJECT2) = 0;
+ VAR(VAR_SENTENCE_PREPOSITION) = 0;
+}
+
+void ScummEngine_v2::runInventoryScript(int i) {
+ redrawV2Inventory();
+}
+
+#undef PARAM_1
+#undef PARAM_2
+#undef PARAM_3
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
new file mode 100644
index 0000000000..7a048d0a01
--- /dev/null
+++ b/engines/scumm/script_v5.cpp
@@ -0,0 +1,2898 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern.h"
+#include "scumm/object.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "scumm/verbs.h"
+
+#include "common/savefile.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v5, x)
+
+void ScummEngine_v5::setupOpcodes() {
+ static const OpcodeEntryV5 opcodes[256] = {
+ /* 00 */
+ OPCODE(o5_stopObjectCode),
+ OPCODE(o5_putActor),
+ OPCODE(o5_startMusic),
+ OPCODE(o5_getActorRoom),
+ /* 04 */
+ OPCODE(o5_isGreaterEqual),
+ OPCODE(o5_drawObject),
+ OPCODE(o5_getActorElevation),
+ OPCODE(o5_setState),
+ /* 08 */
+ OPCODE(o5_isNotEqual),
+ OPCODE(o5_faceActor),
+ OPCODE(o5_startScript),
+ OPCODE(o5_getVerbEntrypoint),
+ /* 0C */
+ OPCODE(o5_resourceRoutines),
+ OPCODE(o5_walkActorToActor),
+ OPCODE(o5_putActorAtObject),
+ OPCODE(o5_getObjectState),
+ /* 10 */
+ OPCODE(o5_getObjectOwner),
+ OPCODE(o5_animateActor),
+ OPCODE(o5_panCameraTo),
+ OPCODE(o5_actorOps),
+ /* 14 */
+ OPCODE(o5_print),
+ OPCODE(o5_actorFromPos),
+ OPCODE(o5_getRandomNr),
+ OPCODE(o5_and),
+ /* 18 */
+ OPCODE(o5_jumpRelative),
+ OPCODE(o5_doSentence),
+ OPCODE(o5_move),
+ OPCODE(o5_multiply),
+ /* 1C */
+ OPCODE(o5_startSound),
+ OPCODE(o5_ifClassOfIs),
+ OPCODE(o5_walkActorTo),
+ OPCODE(o5_isActorInBox),
+ /* 20 */
+ OPCODE(o5_stopMusic),
+ OPCODE(o5_putActor),
+ OPCODE(o5_getAnimCounter),
+ OPCODE(o5_getActorY),
+ /* 24 */
+ OPCODE(o5_loadRoomWithEgo),
+ OPCODE(o5_pickupObject),
+ OPCODE(o5_setVarRange),
+ OPCODE(o5_stringOps),
+ /* 28 */
+ OPCODE(o5_equalZero),
+ OPCODE(o5_setOwnerOf),
+ OPCODE(o5_startScript),
+ OPCODE(o5_delayVariable),
+ /* 2C */
+ OPCODE(o5_cursorCommand),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o5_delay),
+ OPCODE(o5_ifNotState),
+ /* 30 */
+ OPCODE(o5_matrixOps),
+ OPCODE(o5_getInventoryCount),
+ OPCODE(o5_setCameraAt),
+ OPCODE(o5_roomOps),
+ /* 34 */
+ OPCODE(o5_getDist),
+ OPCODE(o5_findObject),
+ OPCODE(o5_walkActorToObject),
+ OPCODE(o5_startObject),
+ /* 38 */
+ OPCODE(o5_lessOrEqual),
+ OPCODE(o5_doSentence),
+ OPCODE(o5_subtract),
+ OPCODE(o5_getActorScale),
+ /* 3C */
+ OPCODE(o5_stopSound),
+ OPCODE(o5_findInventory),
+ OPCODE(o5_walkActorTo),
+ OPCODE(o5_drawBox),
+ /* 40 */
+ OPCODE(o5_cutscene),
+ OPCODE(o5_putActor),
+ OPCODE(o5_chainScript),
+ OPCODE(o5_getActorX),
+ /* 44 */
+ OPCODE(o5_isLess),
+ OPCODE(o5_drawObject),
+ OPCODE(o5_increment),
+ OPCODE(o5_setState),
+ /* 48 */
+ OPCODE(o5_isEqual),
+ OPCODE(o5_faceActor),
+ OPCODE(o5_startScript),
+ OPCODE(o5_getVerbEntrypoint),
+ /* 4C */
+ OPCODE(o5_soundKludge),
+ OPCODE(o5_walkActorToActor),
+ OPCODE(o5_putActorAtObject),
+ OPCODE(o5_ifState),
+ /* 50 */
+ OPCODE(o5_pickupObjectOld),
+ OPCODE(o5_animateActor),
+ OPCODE(o5_actorFollowCamera),
+ OPCODE(o5_actorOps),
+ /* 54 */
+ OPCODE(o5_setObjectName),
+ OPCODE(o5_actorFromPos),
+ OPCODE(o5_getActorMoving),
+ OPCODE(o5_or),
+ /* 58 */
+ OPCODE(o5_beginOverride),
+ OPCODE(o5_doSentence),
+ OPCODE(o5_add),
+ OPCODE(o5_divide),
+ /* 5C */
+ OPCODE(o5_oldRoomEffect),
+ OPCODE(o5_setClass),
+ OPCODE(o5_walkActorTo),
+ OPCODE(o5_isActorInBox),
+ /* 60 */
+ OPCODE(o5_freezeScripts),
+ OPCODE(o5_putActor),
+ OPCODE(o5_stopScript),
+ OPCODE(o5_getActorFacing),
+ /* 64 */
+ OPCODE(o5_loadRoomWithEgo),
+ OPCODE(o5_pickupObject),
+ OPCODE(o5_getClosestObjActor),
+ OPCODE(o5_getStringWidth),
+ /* 68 */
+ OPCODE(o5_isScriptRunning),
+ OPCODE(o5_setOwnerOf),
+ OPCODE(o5_startScript),
+ OPCODE(o5_debug),
+ /* 6C */
+ OPCODE(o5_getActorWidth),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o5_stopObjectScript),
+ OPCODE(o5_ifNotState),
+ /* 70 */
+ OPCODE(o5_lights),
+ OPCODE(o5_getActorCostume),
+ OPCODE(o5_loadRoom),
+ OPCODE(o5_roomOps),
+ /* 74 */
+ OPCODE(o5_getDist),
+ OPCODE(o5_findObject),
+ OPCODE(o5_walkActorToObject),
+ OPCODE(o5_startObject),
+ /* 78 */
+ OPCODE(o5_isGreater),
+ OPCODE(o5_doSentence),
+ OPCODE(o5_verbOps),
+ OPCODE(o5_getActorWalkBox),
+ /* 7C */
+ OPCODE(o5_isSoundRunning),
+ OPCODE(o5_findInventory),
+ OPCODE(o5_walkActorTo),
+ OPCODE(o5_drawBox),
+ /* 80 */
+ OPCODE(o5_breakHere),
+ OPCODE(o5_putActor),
+ OPCODE(o5_startMusic),
+ OPCODE(o5_getActorRoom),
+ /* 84 */
+ OPCODE(o5_isGreaterEqual),
+ OPCODE(o5_drawObject),
+ OPCODE(o5_getActorElevation),
+ OPCODE(o5_setState),
+ /* 88 */
+ OPCODE(o5_isNotEqual),
+ OPCODE(o5_faceActor),
+ OPCODE(o5_startScript),
+ OPCODE(o5_getVerbEntrypoint),
+ /* 8C */
+ OPCODE(o5_resourceRoutines),
+ OPCODE(o5_walkActorToActor),
+ OPCODE(o5_putActorAtObject),
+ OPCODE(o5_getObjectState),
+ /* 90 */
+ OPCODE(o5_getObjectOwner),
+ OPCODE(o5_animateActor),
+ OPCODE(o5_panCameraTo),
+ OPCODE(o5_actorOps),
+ /* 94 */
+ OPCODE(o5_print),
+ OPCODE(o5_actorFromPos),
+ OPCODE(o5_getRandomNr),
+ OPCODE(o5_and),
+ /* 98 */
+ OPCODE(o5_systemOps),
+ OPCODE(o5_doSentence),
+ OPCODE(o5_move),
+ OPCODE(o5_multiply),
+ /* 9C */
+ OPCODE(o5_startSound),
+ OPCODE(o5_ifClassOfIs),
+ OPCODE(o5_walkActorTo),
+ OPCODE(o5_isActorInBox),
+ /* A0 */
+ OPCODE(o5_stopObjectCode),
+ OPCODE(o5_putActor),
+ OPCODE(o5_getAnimCounter),
+ OPCODE(o5_getActorY),
+ /* A4 */
+ OPCODE(o5_loadRoomWithEgo),
+ OPCODE(o5_pickupObject),
+ OPCODE(o5_setVarRange),
+ OPCODE(o5_saveLoadVars),
+ /* A8 */
+ OPCODE(o5_notEqualZero),
+ OPCODE(o5_setOwnerOf),
+ OPCODE(o5_startScript),
+ OPCODE(o5_saveRestoreVerbs),
+ /* AC */
+ OPCODE(o5_expression),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o5_wait),
+ OPCODE(o5_ifNotState),
+ /* B0 */
+ OPCODE(o5_matrixOps),
+ OPCODE(o5_getInventoryCount),
+ OPCODE(o5_setCameraAt),
+ OPCODE(o5_roomOps),
+ /* B4 */
+ OPCODE(o5_getDist),
+ OPCODE(o5_findObject),
+ OPCODE(o5_walkActorToObject),
+ OPCODE(o5_startObject),
+ /* B8 */
+ OPCODE(o5_lessOrEqual),
+ OPCODE(o5_doSentence),
+ OPCODE(o5_subtract),
+ OPCODE(o5_getActorScale),
+ /* BC */
+ OPCODE(o5_stopSound),
+ OPCODE(o5_findInventory),
+ OPCODE(o5_walkActorTo),
+ OPCODE(o5_drawBox),
+ /* C0 */
+ OPCODE(o5_endCutscene),
+ OPCODE(o5_putActor),
+ OPCODE(o5_chainScript),
+ OPCODE(o5_getActorX),
+ /* C4 */
+ OPCODE(o5_isLess),
+ OPCODE(o5_drawObject),
+ OPCODE(o5_decrement),
+ OPCODE(o5_setState),
+ /* C8 */
+ OPCODE(o5_isEqual),
+ OPCODE(o5_faceActor),
+ OPCODE(o5_startScript),
+ OPCODE(o5_getVerbEntrypoint),
+ /* CC */
+ OPCODE(o5_pseudoRoom),
+ OPCODE(o5_walkActorToActor),
+ OPCODE(o5_putActorAtObject),
+ OPCODE(o5_ifState),
+ /* D0 */
+ OPCODE(o5_pickupObjectOld),
+ OPCODE(o5_animateActor),
+ OPCODE(o5_actorFollowCamera),
+ OPCODE(o5_actorOps),
+ /* D4 */
+ OPCODE(o5_setObjectName),
+ OPCODE(o5_actorFromPos),
+ OPCODE(o5_getActorMoving),
+ OPCODE(o5_or),
+ /* D8 */
+ OPCODE(o5_printEgo),
+ OPCODE(o5_doSentence),
+ OPCODE(o5_add),
+ OPCODE(o5_divide),
+ /* DC */
+ OPCODE(o5_oldRoomEffect),
+ OPCODE(o5_setClass),
+ OPCODE(o5_walkActorTo),
+ OPCODE(o5_isActorInBox),
+ /* E0 */
+ OPCODE(o5_freezeScripts),
+ OPCODE(o5_putActor),
+ OPCODE(o5_stopScript),
+ OPCODE(o5_getActorFacing),
+ /* E4 */
+ OPCODE(o5_loadRoomWithEgo),
+ OPCODE(o5_pickupObject),
+ OPCODE(o5_getClosestObjActor),
+ OPCODE(o5_getStringWidth),
+ /* E8 */
+ OPCODE(o5_isScriptRunning),
+ OPCODE(o5_setOwnerOf),
+ OPCODE(o5_startScript),
+ OPCODE(o5_debug),
+ /* EC */
+ OPCODE(o5_getActorWidth),
+ OPCODE(o5_putActorInRoom),
+ OPCODE(o5_stopObjectScript),
+ OPCODE(o5_ifNotState),
+ /* F0 */
+ OPCODE(o5_lights),
+ OPCODE(o5_getActorCostume),
+ OPCODE(o5_loadRoom),
+ OPCODE(o5_roomOps),
+ /* F4 */
+ OPCODE(o5_getDist),
+ OPCODE(o5_findObject),
+ OPCODE(o5_walkActorToObject),
+ OPCODE(o5_startObject),
+ /* F8 */
+ OPCODE(o5_isGreater),
+ OPCODE(o5_doSentence),
+ OPCODE(o5_verbOps),
+ OPCODE(o5_getActorWalkBox),
+ /* FC */
+ OPCODE(o5_isSoundRunning),
+ OPCODE(o5_findInventory),
+ OPCODE(o5_walkActorTo),
+ OPCODE(o5_drawBox)
+ };
+
+ _opcodesV5 = opcodes;
+}
+
+#define PARAM_1 0x80
+#define PARAM_2 0x40
+#define PARAM_3 0x20
+
+void ScummEngine_v5::executeOpcode(byte i) {
+ OpcodeProcV5 op = _opcodesV5[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v5::getOpcodeDesc(byte i) {
+ return _opcodesV5[i].desc;
+}
+
+int ScummEngine_v5::getVar() {
+ return readVar(fetchScriptWord());
+}
+
+int ScummEngine_v5::getVarOrDirectByte(byte mask) {
+ if (_opcode & mask)
+ return getVar();
+ return fetchScriptByte();
+}
+
+int ScummEngine_v5::getVarOrDirectWord(byte mask) {
+ if (_opcode & mask)
+ return getVar();
+ return (int16)fetchScriptWord();
+}
+
+void ScummEngine_v5::o5_actorFollowCamera() {
+ actorFollowCamera(getVarOrDirectByte(0x80));
+}
+
+void ScummEngine_v5::o5_actorFromPos() {
+ int x, y;
+ getResultPos();
+ x = getVarOrDirectWord(PARAM_1);
+ y = getVarOrDirectWord(PARAM_2);
+ setResult(getActorFromPos(x, y));
+}
+
+void ScummEngine_v5::o5_actorOps() {
+ static const byte convertTable[20] =
+ { 1, 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20 };
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o5_actorOps");
+ int i, j;
+
+ while ((_opcode = fetchScriptByte()) != 0xFF) {
+ if (_features & GF_SMALL_HEADER)
+ _opcode = (_opcode & 0xE0) | convertTable[(_opcode & 0x1F) - 1];
+
+ switch (_opcode & 0x1F) {
+ case 0: /* dummy case */
+ getVarOrDirectByte(PARAM_1);
+ break;
+ case 1: // SO_COSTUME
+ a->setActorCostume(getVarOrDirectByte(PARAM_1));
+ break;
+ case 2: // SO_STEP_DIST
+ i = getVarOrDirectByte(PARAM_1);
+ j = getVarOrDirectByte(PARAM_2);
+ a->setActorWalkSpeed(i, j);
+ break;
+ case 3: // SO_SOUND
+ a->_sound[0] = getVarOrDirectByte(PARAM_1);
+ break;
+ case 4: // SO_WALK_ANIMATION
+ a->_walkFrame = getVarOrDirectByte(PARAM_1);
+ break;
+ case 5: // SO_TALK_ANIMATION
+ a->_talkStartFrame = getVarOrDirectByte(PARAM_1);
+ a->_talkStopFrame = getVarOrDirectByte(PARAM_2);
+ break;
+ case 6: // SO_STAND_ANIMATION
+ a->_standFrame = getVarOrDirectByte(PARAM_1);
+ break;
+ case 7: // SO_ANIMATION
+ getVarOrDirectByte(PARAM_1);
+ getVarOrDirectByte(PARAM_2);
+ getVarOrDirectByte(PARAM_3);
+ break;
+ case 8: // SO_DEFAULT
+ a->initActor(0);
+ break;
+ case 9: // SO_ELEVATION
+ a->setElevation(getVarOrDirectWord(PARAM_1));
+ break;
+ case 10: // SO_ANIMATION_DEFAULT
+ a->_initFrame = 1;
+ a->_walkFrame = 2;
+ a->_standFrame = 3;
+ a->_talkStartFrame = 4;
+ a->_talkStopFrame = 5;
+ break;
+ case 11: // SO_PALETTE
+ i = getVarOrDirectByte(PARAM_1);
+ j = getVarOrDirectByte(PARAM_2);
+ checkRange(31, 0, i, "Illegal palette slot %d");
+ a->setPalette(i, j);
+ break;
+ case 12: // SO_TALK_COLOR
+
+ // Zak256 (and possibly other games) uses actor 0 to
+ // indicate that it's the default talk color that is
+ // to be changed.
+
+ if (act == 0)
+ _string[0].color = getVarOrDirectByte(PARAM_1);
+ else
+ a->_talkColor = getVarOrDirectByte(PARAM_1);
+ break;
+ case 13: // SO_ACTOR_NAME
+ loadPtrToResource(rtActorName, a->_number, NULL);
+ break;
+ case 14: // SO_INIT_ANIMATION
+ a->_initFrame = getVarOrDirectByte(PARAM_1);
+ break;
+ case 15: // SO_PALETTE_LIST
+ error("o5_actorOps:unk not implemented");
+#if 0
+ int args[16] =
+ {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ };
+ getWordVararg(args);
+ for (i = 0; i < 16; i++)
+ if (args[i] != 0xFF)
+ a->_palette[i] = args[i];
+#endif
+ break;
+ case 16: // SO_ACTOR_WIDTH
+ a->_width = getVarOrDirectByte(PARAM_1);
+ break;
+ case 17: // SO_ACTOR_SCALE
+ if (_version == 4) {
+ i = j = getVarOrDirectByte(PARAM_1);
+ } else {
+ i = getVarOrDirectByte(PARAM_1);
+ j = getVarOrDirectByte(PARAM_2);
+ }
+
+ a->_boxscale = i;
+ a->setScale(i, j);
+ break;
+ case 18: // SO_NEVER_ZCLIP
+ a->_forceClip = 0;
+ break;
+ case 19: // SO_ALWAYS_ZCLIP
+ a->_forceClip = getVarOrDirectByte(PARAM_1);
+ break;
+ case 20: // SO_IGNORE_BOXES
+ case 21: // SO_FOLLOW_BOXES
+ a->_ignoreBoxes = !(_opcode & 1);
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+
+ case 22: // SO_ANIMATION_SPEED
+ a->setAnimSpeed(getVarOrDirectByte(PARAM_1));
+ break;
+ case 23: // SO_SHADOW
+ a->_shadowMode = getVarOrDirectByte(PARAM_1);
+ break;
+ default:
+ error("o5_actorOps: default case %d", _opcode & 0x1F);
+ }
+ }
+}
+
+void ScummEngine_v5::o5_setClass() {
+ int obj = getVarOrDirectWord(PARAM_1);
+ int newClass;
+
+ while ((_opcode = fetchScriptByte()) != 0xFF) {
+ newClass = getVarOrDirectWord(PARAM_1);
+ if (newClass == 0) {
+ // Class '0' means: clean all class data
+ _classData[obj] = 0;
+ if ((_features & GF_SMALL_HEADER) && obj <= _numActors) {
+ Actor *a = derefActor(obj, "o5_setClass");
+ a->_ignoreBoxes = false;
+ a->_forceClip = 0;
+ }
+ } else
+ putClass(obj, newClass, (newClass & 0x80) ? true : false);
+ }
+}
+
+void ScummEngine_v5::o5_add() {
+ int a;
+ getResultPos();
+ a = getVarOrDirectWord(PARAM_1);
+
+ // WORKAROUND bug #770065: This works around a script bug in LoomCD. To
+ // understand the reasoning behind this, compare script 210 and 218 in
+ // room 20. Apparently they made a mistake when converting the absolute
+ // delays into relative ones.
+ if (_gameId == GID_LOOM && _version == 4 && vm.slot[_currentScript].number == 210 && _currentRoom == 20 && _resultVarNumber == 0x4000) {
+ switch (a) {
+ // Fix for the Var[250] == 11 case
+ case 138:
+ a = 145;
+ break;
+ case 324:
+ a = 324 - 138;
+ break;
+ // Fixes for the Var[250] == 14 case
+ case 130:
+ a = 170;
+ break;
+ case 342:
+ a = 342 - 130 + 15; // Small extra adjustment for the "OUCH"
+ break;
+ case 384:
+ a -= 342;
+ break;
+ case 564:
+ a -= 384;
+ break;
+ }
+ }
+
+ setResult(readVar(_resultVarNumber) + a);
+}
+
+void ScummEngine_v5::o5_and() {
+ int a;
+ getResultPos();
+ a = getVarOrDirectWord(PARAM_1);
+ setResult(readVar(_resultVarNumber) & a);
+}
+
+void ScummEngine_v5::o5_animateActor() {
+ int act = getVarOrDirectByte(PARAM_1);
+ int anim = getVarOrDirectByte(PARAM_2);
+
+ // WORKAROUND bug #820357: This seems to be yet another script bug which
+ // the original engine let slip by. For details, refer to the tracker item.
+ if (_gameId == GID_INDY4 && vm.slot[_currentScript].number == 206 && _currentRoom == 17 && (act == 31 || act == 86)) {
+ return;
+ }
+
+ Actor *a = derefActor(act, "o5_animateActor");
+ a->animateActor(anim);
+}
+
+void ScummEngine_v5::o5_breakHere() {
+ updateScriptPtr();
+ _currentScript = 0xFF;
+}
+
+void ScummEngine_v5::o5_chainScript() {
+ int vars[16];
+ int script;
+ int cur;
+
+ script = getVarOrDirectByte(PARAM_1);
+
+ getWordVararg(vars);
+
+ cur = _currentScript;
+
+ // WORKAROUND bug #743314: Work around a bug in script 33 in Indy3 VGA.
+ // That script is used for the fist fights in the Zeppelin. It uses
+ // Local[5], even though that is never set to any value. But script 33 is
+ // called via chainScript by script 32, and in there Local[5] is defined
+ // to the actor ID of the opposing soldier. So, we copy that value over
+ // to the Local[5] variable of script 33.
+ if (_gameId == GID_INDY3 && vm.slot[cur].number == 32 && script == 33) {
+ vars[5] = vm.localvar[cur][5];
+ }
+
+ vm.slot[cur].number = 0;
+ vm.slot[cur].status = ssDead;
+ _currentScript = 0xFF;
+
+ runScript(script, vm.slot[cur].freezeResistant, vm.slot[cur].recursive, vars);
+}
+
+void ScummEngine_v5::o5_cursorCommand() {
+ int i, j, k;
+ int table[16];
+ switch ((_opcode = fetchScriptByte()) & 0x1F) {
+ case 1: // SO_CURSOR_ON
+ _cursor.state = 1;
+ verbMouseOver(0);
+ break;
+ case 2: // SO_CURSOR_OFF
+ _cursor.state = 0;
+ verbMouseOver(0);
+ break;
+ case 3: // SO_USERPUT_ON
+ _userPut = 1;
+ break;
+ case 4: // SO_USERPUT_OFF
+ _userPut = 0;
+ break;
+ case 5: // SO_CURSOR_SOFT_ON
+ _cursor.state++;
+ verbMouseOver(0);
+ break;
+ case 6: // SO_CURSOR_SOFT_OFF
+ _cursor.state--;
+ verbMouseOver(0);
+ break;
+ case 7: // SO_USERPUT_SOFT_ON
+ _userPut++;
+ break;
+ case 8: // SO_USERPUT_SOFT_OFF
+ _userPut--;
+ break;
+ case 10: // SO_CURSOR_IMAGE
+ i = getVarOrDirectByte(PARAM_1); // Cursor number
+ j = getVarOrDirectByte(PARAM_2); // Charset letter to use
+ redefineBuiltinCursorFromChar(i, j);
+ break;
+ case 11: // SO_CURSOR_HOTSPOT
+ i = getVarOrDirectByte(PARAM_1);
+ j = getVarOrDirectByte(PARAM_2);
+ k = getVarOrDirectByte(PARAM_3);
+ redefineBuiltinCursorHotspot(i, j, k);
+ break;
+ case 12: // SO_CURSOR_SET
+ i = getVarOrDirectByte(PARAM_1);
+ if (i >= 0 && i <= 3)
+ _currentCursor = i;
+ else
+ error("SO_CURSOR_SET: unsupported cursor id %d", i);
+ break;
+ case 13: // SO_CHARSET_SET
+ initCharset(getVarOrDirectByte(PARAM_1));
+ break;
+ case 14: /* unk */
+ if (_version == 3) {
+ /*int a = */ getVarOrDirectByte(PARAM_1);
+ /*int b = */ getVarOrDirectByte(PARAM_2);
+ // This is some kind of "init charset" opcode. However, we don't have to do anything
+ // in here, as our initCharset automatically calls loadCharset for GF_SMALL_HEADER,
+ // games if needed.
+ } else {
+ getWordVararg(table);
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)table[i];
+ }
+ break;
+ }
+
+ if (_version >= 4) {
+ VAR(VAR_CURSORSTATE) = _cursor.state;
+ VAR(VAR_USERPUT) = _userPut;
+ }
+}
+
+void ScummEngine_v5::o5_cutscene() {
+ int args[16];
+ getWordVararg(args);
+ beginCutscene(args);
+}
+
+void ScummEngine_v5::o5_endCutscene() {
+ endCutscene();
+}
+
+void ScummEngine_v5::o5_debug() {
+ int a = getVarOrDirectWord(PARAM_1);
+ debugC(DEBUG_GENERAL, "o5_debug(%d)", a);
+}
+
+void ScummEngine_v5::o5_decrement() {
+ getResultPos();
+ setResult(readVar(_resultVarNumber) - 1);
+}
+
+void ScummEngine_v5::o5_delay() {
+ int delay = fetchScriptByte();
+ delay |= fetchScriptByte() << 8;
+ delay |= fetchScriptByte() << 16;
+ vm.slot[_currentScript].delay = delay;
+ vm.slot[_currentScript].status = ssPaused;
+ o5_breakHere();
+}
+
+void ScummEngine_v5::o5_delayVariable() {
+ vm.slot[_currentScript].delay = getVar();
+ vm.slot[_currentScript].status = ssPaused;
+ o5_breakHere();
+}
+
+void ScummEngine_v5::o5_divide() {
+ int a;
+ getResultPos();
+ a = getVarOrDirectWord(PARAM_1);
+ if (a == 0) {
+ error("Divide by zero");
+ setResult(0);
+ } else
+ setResult(readVar(_resultVarNumber) / a);
+}
+
+void ScummEngine_v5::o5_doSentence() {
+ int verb;
+ SentenceTab *st;
+
+ verb = getVarOrDirectByte(PARAM_1);
+ if (verb == 0xFE) {
+ _sentenceNum = 0;
+ stopScript(VAR(VAR_SENTENCE_SCRIPT));
+ clearClickedStatus();
+ return;
+ }
+
+ st = &_sentence[_sentenceNum++];
+
+ st->verb = verb;
+ st->objectA = getVarOrDirectWord(PARAM_2);
+ st->objectB = getVarOrDirectWord(PARAM_3);
+ st->preposition = (st->objectB != 0);
+ st->freezeCount = 0;
+}
+
+void ScummEngine_v5::o5_drawBox() {
+ int x, y, x2, y2, color;
+
+ x = getVarOrDirectWord(PARAM_1);
+ y = getVarOrDirectWord(PARAM_2);
+
+ _opcode = fetchScriptByte();
+ x2 = getVarOrDirectWord(PARAM_1);
+ y2 = getVarOrDirectWord(PARAM_2);
+ color = getVarOrDirectByte(PARAM_3);
+
+ drawBox(x, y, x2, y2, color);
+}
+
+void ScummEngine_v5::o5_drawObject() {
+ int state, obj, idx, i;
+ ObjectData *od;
+ uint16 x, y, w, h;
+ int xpos, ypos;
+
+ state = 1;
+ xpos = ypos = 255;
+ obj = getVarOrDirectWord(PARAM_1);
+
+ if (_features & GF_SMALL_HEADER) {
+ xpos = getVarOrDirectWord(PARAM_2);
+ ypos = getVarOrDirectWord(PARAM_3);
+ } else {
+ _opcode = fetchScriptByte();
+ switch (_opcode & 0x1F) {
+ case 1: /* draw at */
+ xpos = getVarOrDirectWord(PARAM_1);
+ ypos = getVarOrDirectWord(PARAM_2);
+ break;
+ case 2: /* set state */
+ state = getVarOrDirectWord(PARAM_1);
+ break;
+ case 0x1F: /* neither */
+ break;
+ default:
+ error("o5_drawObject: unknown subopcode %d", _opcode & 0x1F);
+ }
+ }
+
+ idx = getObjectIndex(obj);
+ if (idx == -1)
+ return;
+
+ od = &_objs[idx];
+ if (xpos != 0xFF) {
+ od->walk_x += (xpos * 8) - od->x_pos;
+ od->x_pos = xpos * 8;
+ od->walk_y += (ypos * 8) - od->y_pos;
+ od->y_pos = ypos * 8;
+ }
+ addObjectToDrawQue(idx);
+
+ x = od->x_pos;
+ y = od->y_pos;
+ w = od->width;
+ h = od->height;
+
+ i = _numLocalObjects - 1;
+ do {
+ if (_objs[i].obj_nr && _objs[i].x_pos == x && _objs[i].y_pos == y && _objs[i].width == w && _objs[i].height == h)
+ putState(_objs[i].obj_nr, 0);
+ } while (--i);
+
+ putState(obj, state);
+}
+
+void ScummEngine_v5::o5_getStringWidth() {
+ int string, width = 0;
+ byte *ptr;
+
+ getResultPos();
+ string = getVarOrDirectByte(PARAM_1);
+ ptr = getResourceAddress(rtString, string);
+ assert(ptr);
+
+ width = _charset->getStringWidth(0, ptr);
+
+ setResult(width);
+ debug(0, "o5_getStringWidth, result %d", width);
+}
+
+void ScummEngine_v5::o5_saveLoadVars() {
+ if (fetchScriptByte() == 1)
+ saveVars();
+ else
+ loadVars();
+}
+
+void ScummEngine_v5::saveVars() {
+ int a, b;
+
+ while ((_opcode = fetchScriptByte()) != 0) {
+ switch (_opcode & 0x1F) {
+ case 0x01: // write a range of variables
+ getResultPos();
+ a = _resultVarNumber;
+ getResultPos();
+ b = _resultVarNumber;
+ debug(0, "stub saveVars: vars %d -> %d", a, b);
+ break;
+ case 0x02: // write a range of string variables
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ debug(0, "stub saveVars: strings %d -> %d", a, b);
+ break;
+ case 0x03: // open file
+ a = resStrLen(_scriptPointer);
+ debug(0, "stub saveVars to %s", _scriptPointer);
+ _scriptPointer += a + 1;
+ break;
+ case 0x04:
+ return;
+ break;
+ case 0x1F: // close file
+ debug(0, "stub saveVars close file");
+ return;
+ break;
+ }
+ }
+}
+
+void ScummEngine_v5::loadVars() {
+ int a, b;
+
+// Common::hexdump(_scriptPointer, 64);
+ while ((_opcode = fetchScriptByte()) != 0) {
+ switch (_opcode & 0x1F) {
+ case 0x01: // read a range of variables
+ getResultPos();
+ a = _resultVarNumber;
+ getResultPos();
+ b = _resultVarNumber;
+ debug(0, "stub loadVars: vars %d -> %d", a, b);
+ break;
+ case 0x02: // read a range of string variables
+ a = getVarOrDirectByte(0x80);
+ b = getVarOrDirectByte(0x40);
+ debug(0, "stub loadVars: strings %d -> %d", a, b);
+ break;
+ case 0x03: // open file
+ a = resStrLen(_scriptPointer);
+ debug(0, "stub loadVars from %s", _scriptPointer);
+ _scriptPointer += a + 1;
+ break;
+ case 0x04:
+ return;
+ break;
+ case 0x1F: // close file
+ debug(0, "stub loadVars close file");
+ return;
+ break;
+ }
+ }
+}
+
+void ScummEngine_v5::o5_expression() {
+ int dst, i;
+
+ _scummStackPos = 0;
+ getResultPos();
+ dst = _resultVarNumber;
+
+ while ((_opcode = fetchScriptByte()) != 0xFF) {
+ switch (_opcode & 0x1F) {
+ case 1: /* varordirect */
+ push(getVarOrDirectWord(PARAM_1));
+ break;
+ case 2: /* add */
+ i = pop();
+ push(i + pop());
+ break;
+ case 3: /* sub */
+ i = pop();
+ push(pop() - i);
+ break;
+ case 4: /* mul */
+ i = pop();
+ push(i * pop());
+ break;
+ case 5: /* div */
+ i = pop();
+ if (i == 0)
+ error("Divide by zero");
+ push(pop() / i);
+ break;
+ case 6: /* normal opcode */
+ _opcode = fetchScriptByte();
+ executeOpcode(_opcode);
+ push(_scummVars[0]);
+ break;
+ }
+ }
+
+ _resultVarNumber = dst;
+ setResult(pop());
+}
+
+void ScummEngine_v5::o5_faceActor() {
+ int act = getVarOrDirectByte(PARAM_1);
+ int obj = getVarOrDirectWord(PARAM_2);
+ Actor *a = derefActor(act, "o5_faceActor");
+ a->faceToObject(obj);
+}
+
+void ScummEngine_v5::o5_findInventory() {
+ getResultPos();
+ int x = getVarOrDirectByte(PARAM_1);
+ int y = getVarOrDirectByte(PARAM_2);
+ setResult(findInventory(x, y));
+}
+
+void ScummEngine_v5::o5_findObject() {
+ getResultPos();
+ int x = getVarOrDirectByte(PARAM_1);
+ int y = getVarOrDirectByte(PARAM_2);
+ setResult(findObject(x, y));
+}
+
+void ScummEngine_v5::o5_freezeScripts() {
+ int scr = getVarOrDirectByte(PARAM_1);
+
+ if (scr != 0)
+ freezeScripts(scr);
+ else
+ unfreezeScripts();
+}
+
+void ScummEngine_v5::o5_getActorCostume() {
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o5_getActorCostume");
+ setResult(a->_costume);
+}
+
+void ScummEngine_v5::o5_getActorElevation() {
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o5_getActorElevation");
+ setResult(a->getElevation());
+}
+
+void ScummEngine_v5::o5_getActorFacing() {
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o5_getActorFacing");
+ setResult(newDirToOldDir(a->getFacing()));
+}
+
+void ScummEngine_v5::o5_getActorMoving() {
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o5_getActorMoving");
+ setResult(a->_moving);
+}
+
+void ScummEngine_v5::o5_getActorRoom() {
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ // WORKAROUND bug #746349. This is a really odd bug in either the script
+ // or in our script engine. Might be a good idea to investigate this
+ // further by e.g. looking at the FOA engine a bit closer.
+ if (_gameId == GID_INDY4 && _roomResource == 94 && vm.slot[_currentScript].number == 206 && !isValidActor(act)) {
+ setResult(0);
+ return;
+ }
+
+ Actor *a = derefActor(act, "o5_getActorRoom");
+ setResult(a->_room);
+}
+
+void ScummEngine_v5::o5_getActorScale() {
+ Actor *a;
+
+ // INDY3 uses this opcode for waitForActor
+ if (_gameId == GID_INDY3) {
+ const byte *oldaddr = _scriptPointer - 1;
+ a = derefActor(getVarOrDirectByte(PARAM_1), "o5_getActorScale (wait)");
+ if (a->_moving) {
+ _scriptPointer = oldaddr;
+ o5_breakHere();
+ }
+ return;
+ }
+
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ a = derefActor(act, "o5_getActorScale");
+ setResult(a->_scalex);
+}
+
+void ScummEngine_v5::o5_getActorWalkBox() {
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o5_getActorWalkBox");
+ setResult(a->_walkbox);
+}
+
+void ScummEngine_v5::o5_getActorWidth() {
+ getResultPos();
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o5_getActorWidth");
+ setResult(a->_width);
+}
+
+void ScummEngine_v5::o5_getActorX() {
+ int a;
+ getResultPos();
+
+ if ((_gameId == GID_INDY3) && !(_platform == Common::kPlatformMacintosh))
+ a = getVarOrDirectByte(PARAM_1);
+ else
+ a = getVarOrDirectWord(PARAM_1);
+
+ setResult(getObjX(a));
+}
+
+void ScummEngine_v5::o5_getActorY() {
+ int a;
+ getResultPos();
+
+ if ((_gameId == GID_INDY3) && !(_platform == Common::kPlatformMacintosh)) {
+ a = getVarOrDirectByte(PARAM_1);
+
+ // WORKAROUND bug #636433 (can't get into Zeppelin)
+ if (_roomResource == 36) {
+ setResult(getObjY(a) - 1);
+ return;
+ }
+ } else
+ a = getVarOrDirectWord(PARAM_1);
+
+ setResult(getObjY(a));
+}
+
+void ScummEngine_v5::o5_saveLoadGame() {
+ getResultPos();
+ byte a = getVarOrDirectByte(PARAM_1);
+ byte slot = (a & 0x1F) + 1;
+ byte result = 0;
+
+ if ((_gameId == GID_MANIAC) && (_version == 1)) {
+ // Convert older load/save screen
+ // 1 Load
+ // 2 Save
+ slot = 1;
+ if (a == 1)
+ _opcode = 0x40;
+ else if ((a == 2) || (_platform == Common::kPlatformNES))
+ _opcode = 0x80;
+ } else {
+ _opcode = a & 0xE0;
+ }
+
+ switch (_opcode) {
+ case 0x00: // num slots available
+ result = 100;
+ break;
+ case 0x20: // drive
+ if (_gameId == GID_INDY3) {
+ // 0 = hard drive
+ // 1 = disk drive
+ result = 0;
+ } else {
+ // set current drive
+ result = 1;
+ }
+ break;
+ case 0x40: // load
+ if (loadState(slot, _saveTemporaryState))
+ result = 3; // sucess
+ else
+ result = 5; // failed to load
+ break;
+ case 0x80: // save
+ if (saveState(slot, _saveTemporaryState))
+ result = 0;
+ else
+ result = 2;
+ break;
+ case 0xC0: // test if save exists
+ bool avail_saves[100];
+ char filename[256];
+
+ listSavegames(avail_saves, ARRAYSIZE(avail_saves));
+ makeSavegameName(filename, slot, false);
+ if (avail_saves[slot] && (_saveFileMan->openForLoading(filename)))
+ result = 6; // save file exists
+ else
+ result = 7; // save file does not exist
+ break;
+ default:
+ error("o5_saveLoadGame: unknown subopcode %d", _opcode);
+ }
+
+ setResult(result);
+}
+
+void ScummEngine_v5::o5_getAnimCounter() {
+ if (_version == 3) {
+ o5_saveLoadGame();
+ return;
+ }
+
+ getResultPos();
+
+ int act = getVarOrDirectByte(PARAM_1);
+ Actor *a = derefActor(act, "o5_getAnimCounter");
+ setResult(a->_cost.animCounter);
+}
+
+void ScummEngine_v5::o5_getClosestObjActor() {
+ int obj;
+ int act;
+ int dist;
+
+ // This code can't detect any actors farther away than 255 units
+ // (pixels in newer games, characters in older ones.) But this is
+ // perfectly OK, as it is exactly how the original behaved.
+
+ int closest_obj = 0xFF, closest_dist = 0xFF;
+
+ getResultPos();
+
+ act = getVarOrDirectWord(PARAM_1);
+ obj = VAR(VAR_ACTOR_RANGE_MAX);
+
+ do {
+ dist = getObjActToObjActDist(act, obj);
+ if (dist < closest_dist) {
+ closest_dist = dist;
+ closest_obj = obj;
+ }
+ } while (--obj >= VAR(VAR_ACTOR_RANGE_MIN));
+
+ setResult(closest_obj);
+}
+
+void ScummEngine_v5::o5_getDist() {
+ int o1, o2;
+ int r;
+ getResultPos();
+ o1 = getVarOrDirectWord(PARAM_1);
+ o2 = getVarOrDirectWord(PARAM_2);
+ r = getObjActToObjActDist(o1, o2);
+
+ // FIXME: MI2 race workaround, see bug #597022. We never quite figured out
+ // what the real cause of this, or if it maybe occurs in the original, too...
+ if (_gameId == GID_MONKEY2 && vm.slot[_currentScript].number == 40 && r < 60)
+ r = 60;
+
+ // WORKAROUND bug #795937
+ if ((_gameId == GID_MONKEY_EGA || _gameId == GID_PASS) && o1 == 1 && o2 == 307 && vm.slot[_currentScript].number == 205 && r == 2)
+ r = 3;
+
+ setResult(r);
+}
+
+void ScummEngine_v5::o5_getInventoryCount() {
+ getResultPos();
+ setResult(getInventoryCount(getVarOrDirectByte(PARAM_1)));
+}
+
+void ScummEngine_v5::o5_getObjectOwner() {
+ getResultPos();
+ setResult(getOwner(getVarOrDirectWord(PARAM_1)));
+}
+
+void ScummEngine_v5::o5_getObjectState() {
+ if (_features & GF_SMALL_HEADER) {
+ o5_ifState();
+ } else {
+ getResultPos();
+ setResult(getState(getVarOrDirectWord(PARAM_1)));
+ }
+}
+
+void ScummEngine_v5::o5_ifState() {
+ int a = getVarOrDirectWord(PARAM_1);
+ int b = getVarOrDirectByte(PARAM_2);
+
+ if (getState(a) != b)
+ o5_jumpRelative();
+ else
+ ignoreScriptWord();
+}
+
+void ScummEngine_v5::o5_ifNotState() {
+ int a = getVarOrDirectWord(PARAM_1);
+ int b = getVarOrDirectByte(PARAM_2);
+
+ if (getState(a) == b)
+ o5_jumpRelative();
+ else
+ ignoreScriptWord();
+}
+
+void ScummEngine_v5::o5_getRandomNr() {
+ getResultPos();
+ setResult(_rnd.getRandomNumber(getVarOrDirectByte(PARAM_1)));
+}
+
+void ScummEngine_v5::o5_isScriptRunning() {
+ getResultPos();
+ setResult(isScriptRunning(getVarOrDirectByte(PARAM_1)));
+}
+
+void ScummEngine_v5::o5_getVerbEntrypoint() {
+ int a, b;
+ getResultPos();
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+
+ setResult(getVerbEntrypoint(a, b));
+}
+
+void ScummEngine_v5::o5_ifClassOfIs() {
+ int act, cls, b = 0;
+ bool cond = true;
+
+ act = getVarOrDirectWord(PARAM_1);
+
+ while ((_opcode = fetchScriptByte()) != 0xFF) {
+ cls = getVarOrDirectWord(PARAM_1);
+
+ if (!cls) // FIXME: Ender can't remember why this is here,
+ b = false; // but it fixes an oddball zak256 crash
+ else
+ b = getClass(act, cls);
+
+ if (cls & 0x80 && !b || !(cls & 0x80) && b)
+ cond = false;
+ }
+ if (cond)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v5::o5_increment() {
+ getResultPos();
+ setResult(readVar(_resultVarNumber) + 1);
+}
+
+void ScummEngine_v5::o5_isActorInBox() {
+ int act = getVarOrDirectByte(PARAM_1);
+ int box = getVarOrDirectByte(PARAM_2);
+ Actor *a = derefActor(act, "o5_isActorInBox");
+
+ if (!checkXYInBoxBounds(box, a->_pos.x, a->_pos.y))
+ o5_jumpRelative();
+ else
+ ignoreScriptWord();
+}
+
+void ScummEngine_v5::o5_isEqual() {
+ int16 a, b;
+ int var;
+
+ if (_version <= 2)
+ var = fetchScriptByte();
+ else
+ var = fetchScriptWord();
+ a = readVar(var);
+ b = getVarOrDirectWord(PARAM_1);
+
+ // HACK: See bug report #602348. The sound effects for Largo's screams
+ // are only played on type 5 soundcards. However, there is at least one
+ // other sound effect (the bartender spitting) which is only played on
+ // type 3 soundcards.
+
+ if (_gameId == GID_MONKEY2 && var == VAR_SOUNDCARD && b == 5)
+ b = a;
+
+ // HACK: To allow demo script of Maniac Mansion V2
+ // The camera x position is only 100, instead of 180, after game title name scrolls.
+ if (_gameId == GID_MANIAC && _version == 2 && _demoMode && isScriptRunning(173) && b == 180)
+ b = 100;
+
+ if (b == a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+
+}
+
+void ScummEngine_v5::o5_isGreater() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectWord(PARAM_1);
+ if (b > a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v5::o5_isGreaterEqual() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectWord(PARAM_1);
+ if (b >= a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v5::o5_isLess() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectWord(PARAM_1);
+ if (b < a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v5::o5_lessOrEqual() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectWord(PARAM_1);
+
+ // WORKAROUND bug #820507 : Work around a bug in Indy3Town.
+ if (_gameId == GID_INDY3 && (_platform == Common::kPlatformFMTowns) &&
+ (vm.slot[_currentScript].number == 200 || vm.slot[_currentScript].number == 203) &&
+ _currentRoom == 70 && b == -256) {
+ o5_jumpRelative();
+ return;
+ }
+
+ if (b <= a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v5::o5_isNotEqual() {
+ int16 a = getVar();
+ int16 b = getVarOrDirectWord(PARAM_1);
+ if (b != a)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v5::o5_notEqualZero() {
+ int a = getVar();
+ if (a != 0)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v5::o5_equalZero() {
+ int a = getVar();
+ if (a == 0)
+ ignoreScriptWord();
+ else
+ o5_jumpRelative();
+}
+
+void ScummEngine_v5::o5_jumpRelative() {
+ // Note that calling fetchScriptWord() will also modify _scriptPointer,
+ // so *don't* do this: _scriptPointer += (int16)fetchScriptWord();
+ //
+ // I'm not enough of a language lawyer to say for certain that this is
+ // undefined, but I do know that GCC 4.0 doesn't think it means what
+ // we want it to mean.
+
+ int16 offset = (int16)fetchScriptWord();
+ _scriptPointer += offset;
+}
+
+void ScummEngine_v5::o5_lights() {
+ int a, b, c;
+
+ a = getVarOrDirectByte(PARAM_1);
+ b = fetchScriptByte();
+ c = fetchScriptByte();
+
+ if (c == 0)
+ VAR(VAR_CURRENT_LIGHTS) = a;
+ else if (c == 1) {
+ _flashlight.xStrips = a;
+ _flashlight.yStrips = b;
+ }
+ _fullRedraw = true;
+}
+
+void ScummEngine_v5::o5_loadRoom() {
+ int room;
+
+ room = getVarOrDirectByte(PARAM_1);
+
+ if (!_copyProtection) {
+ // Skip copy protection scheme
+ if (_gameId == GID_INDY3 && (_features & GF_OLD_BUNDLE) && room == 92) {
+ VAR(57) = 1;
+ return;
+ }
+ }
+
+ // For small header games, we only call startScene if the room
+ // actually changed. This avoid unwanted (wrong) fades in Zak256
+ // and others. OTOH, it seems to cause a problem in newer games.
+ if (!(_features & GF_SMALL_HEADER) || room != _currentRoom)
+ startScene(room, 0, 0);
+
+ _fullRedraw = true;
+}
+
+void ScummEngine_v5::o5_loadRoomWithEgo() {
+ Actor *a;
+ int obj, room, x, y;
+ int x2, y2, dir, oldDir;
+
+ obj = getVarOrDirectWord(PARAM_1);
+ room = getVarOrDirectByte(PARAM_2);
+
+ a = derefActor(VAR(VAR_EGO), "o5_loadRoomWithEgo");
+
+ a->putActor(a->_pos.x, a->_pos.y, room);
+ oldDir = a->getFacing();
+ _egoPositioned = false;
+
+ x = (int16)fetchScriptWord();
+ y = (int16)fetchScriptWord();
+
+ VAR(VAR_WALKTO_OBJ) = obj;
+ startScene(a->_room, a, obj);
+ VAR(VAR_WALKTO_OBJ) = 0;
+
+ if (_version <= 4) {
+ if (whereIsObject(obj) != WIO_ROOM)
+ error("o5_loadRoomWithEgo: Object %d is not in room %d", obj, _currentRoom);
+ if (!_egoPositioned) {
+ getObjectXYPos(obj, x2, y2, dir);
+ a->putActor(x2, y2, _currentRoom);
+ if (a->getFacing() == oldDir)
+ a->setDirection(dir + 180);
+ }
+ a->_moving = 0;
+ }
+
+ // This is based on disassembly
+ camera._cur.x = camera._dest.x = a->_pos.x;
+ setCameraFollows(a);
+
+ _fullRedraw = true;
+
+ if (x != -1) {
+ a->startWalkActor(x, y, -1);
+ }
+}
+
+void ScummEngine_v5::o5_matrixOps() {
+ int a, b;
+
+ if (_version == 3) {
+ a = getVarOrDirectByte(PARAM_1);
+ b = fetchScriptByte();
+ setBoxFlags(a, b);
+ return;
+ }
+
+ _opcode = fetchScriptByte();
+ switch (_opcode & 0x1F) {
+ case 1:
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ setBoxFlags(a, b);
+ break;
+ case 2:
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ setBoxScale(a, b);
+ break;
+ case 3:
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ setBoxScale(a, (b - 1) | 0x8000);
+ break;
+ case 4:
+ createBoxMatrix();
+ break;
+ }
+}
+
+void ScummEngine_v5::o5_move() {
+ getResultPos();
+ setResult(getVarOrDirectWord(PARAM_1));
+}
+
+void ScummEngine_v5::o5_multiply() {
+ int a;
+ getResultPos();
+ a = getVarOrDirectWord(PARAM_1);
+ setResult(readVar(_resultVarNumber) * a);
+}
+
+void ScummEngine_v5::o5_or() {
+ int a;
+ getResultPos();
+ a = getVarOrDirectWord(PARAM_1);
+ setResult(readVar(_resultVarNumber) | a);
+}
+
+void ScummEngine_v5::o5_beginOverride() {
+ if (fetchScriptByte() != 0)
+ beginOverride();
+ else
+ endOverride();
+}
+
+void ScummEngine_v5::o5_panCameraTo() {
+ panCameraTo(getVarOrDirectWord(PARAM_1), 0);
+}
+
+void ScummEngine_v5::o5_pickupObject() {
+ int obj, room;
+ if (_version == 3 || _version == 4) {
+ o5_drawObject();
+ return;
+ }
+
+ obj = getVarOrDirectWord(PARAM_1);
+ room = getVarOrDirectByte(PARAM_2);
+ if (room == 0)
+ room = _roomResource;
+ addObjectToInventory(obj, room);
+ putOwner(obj, VAR(VAR_EGO));
+ putClass(obj, kObjectClassUntouchable, 1);
+ putState(obj, 1);
+ markObjectRectAsDirty(obj);
+ clearDrawObjectQueue();
+ runInventoryScript(1);
+}
+
+void ScummEngine_v5::o5_print() {
+ _actorToPrintStrFor = getVarOrDirectByte(PARAM_1);
+ decodeParseString();
+}
+
+void ScummEngine_v5::o5_printEgo() {
+ _actorToPrintStrFor = (byte)VAR(VAR_EGO);
+ decodeParseString();
+}
+
+void ScummEngine_v5::o5_pseudoRoom() {
+ int i = fetchScriptByte(), j;
+ while ((j = fetchScriptByte()) != 0) {
+ if (j >= 0x80) {
+ _resourceMapper[j & 0x7F] = i;
+ }
+ }
+}
+
+void ScummEngine_v5::o5_putActor() {
+ int x, y;
+ Actor *a;
+
+ a = derefActor(getVarOrDirectByte(PARAM_1), "o5_putActor");
+ x = getVarOrDirectWord(PARAM_2);
+ y = getVarOrDirectWord(PARAM_3);
+ a->putActor(x, y, a->_room);
+}
+
+void ScummEngine_v5::o5_putActorAtObject() {
+ int obj, x, y;
+ Actor *a;
+
+ a = derefActor(getVarOrDirectByte(PARAM_1), "o5_putActorAtObject");
+ obj = getVarOrDirectWord(PARAM_2);
+ if (whereIsObject(obj) != WIO_NOT_FOUND)
+ getObjectXYPos(obj, x, y);
+ else {
+ x = 240;
+ y = 120;
+ }
+ a->putActor(x, y, a->_room);
+}
+
+void ScummEngine_v5::o5_putActorInRoom() {
+ Actor *a;
+ int act = getVarOrDirectByte(PARAM_1);
+ int room = getVarOrDirectByte(PARAM_2);
+
+ a = derefActor(act, "o5_putActorInRoom");
+
+ if (a->_visible && _currentRoom != room && getTalkingActor() == a->_number) {
+ stopTalk();
+ }
+ a->_room = room;
+ if (!room)
+ a->putActor(0, 0, 0);
+}
+
+void ScummEngine_v5::o5_systemOps() {
+ byte subOp = fetchScriptByte();
+ switch (subOp) {
+ case 1: // SO_RESTART
+ restart();
+ break;
+ case 2: // SO_PAUSE
+ pauseGame();
+ break;
+ case 3: // SO_QUIT
+ shutDown();
+ break;
+ default:
+ error("o5_systemOps: unknown subopcode %d", subOp);
+ }
+}
+
+void ScummEngine_v5::o5_resourceRoutines() {
+ const ResTypes resType[4] = { rtScript, rtSound, rtCostume, rtRoom };
+ int resid = 0;
+ int foo, bar;
+
+ _opcode = fetchScriptByte();
+ if (_opcode != 17)
+ resid = getVarOrDirectByte(PARAM_1);
+ if (!(_platform == Common::kPlatformFMTowns)) {
+ // FIXME - this probably can be removed eventually, I don't think the following
+ // check will ever be triggered, but then I could be wrong and it's better
+ // to play it safe.
+ if ((_opcode & 0x3F) != (_opcode & 0x1F))
+ error("Oops, this shouldn't happen: o5_resourceRoutines opcode %d", _opcode);
+ }
+
+ int op = _opcode & 0x3F;
+
+ switch (op) {
+ case 1: // SO_LOAD_SCRIPT
+ case 2: // SO_LOAD_SOUND
+ case 3: // SO_LOAD_COSTUME
+ ensureResourceLoaded(resType[op - 1], resid);
+ break;
+ case 4: // SO_LOAD_ROOM
+ if (_version == 3) {
+ ensureResourceLoaded(rtRoom, resid);
+ if (resid > 0x7F)
+ resid = _resourceMapper[resid & 0x7F];
+
+ if (_currentRoom != resid) {
+ res.setResourceCounter(rtRoom, resid, 1);
+ }
+ } else
+ ensureResourceLoaded(rtRoom, resid);
+ break;
+
+ case 5: // SO_NUKE_SCRIPT
+ case 6: // SO_NUKE_SOUND
+ case 7: // SO_NUKE_COSTUME
+ case 8: // SO_NUKE_ROOM
+ if (_gameId == GID_ZAK && (_platform == Common::kPlatformFMTowns))
+ error("o5_resourceRoutines %d should not occur in Zak256", op);
+ else
+ res.setResourceCounter(resType[op-5], resid, 0x7F);
+ break;
+ case 9: // SO_LOCK_SCRIPT
+ if (resid >= _numGlobalScripts)
+ break;
+ res.lock(rtScript, resid);
+ break;
+ case 10: // SO_LOCK_SOUND
+ res.lock(rtSound, resid);
+ break;
+ case 11: // SO_LOCK_COSTUME
+ res.lock(rtCostume, resid);
+ break;
+ case 12: // SO_LOCK_ROOM
+ if (resid > 0x7F)
+ resid = _resourceMapper[resid & 0x7F];
+ res.lock(rtRoom, resid);
+ break;
+
+ case 13: // SO_UNLOCK_SCRIPT
+ if (resid >= _numGlobalScripts)
+ break;
+ res.unlock(rtScript, resid);
+ break;
+ case 14: // SO_UNLOCK_SOUND
+ res.unlock(rtSound, resid);
+ break;
+ case 15: // SO_UNLOCK_COSTUME
+ res.unlock(rtCostume, resid);
+ break;
+ case 16: // SO_UNLOCK_ROOM
+ if (resid > 0x7F)
+ resid = _resourceMapper[resid & 0x7F];
+ res.unlock(rtRoom, resid);
+ break;
+
+ case 17: // SO_CLEAR_HEAP
+ //heapClear(0);
+ //unkHeapProc2(0, 0);
+ break;
+ case 18: // SO_LOAD_CHARSET
+ loadCharset(resid);
+ break;
+ case 19: // SO_NUKE_CHARSET
+ nukeCharset(resid);
+ break;
+ case 20: // SO_LOAD_OBJECT
+ loadFlObject(getVarOrDirectWord(PARAM_2), resid);
+ break;
+
+ // TODO: For the following see also Hibarnatus' information on bug #805691.
+ case 32:
+ // TODO (apparently never used in FM-TOWNS)
+ debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ break;
+ case 33:
+ // TODO (apparently never used in FM-TOWNS)
+ debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ break;
+ case 35:
+ // TODO: Might be used to set CD volume in FM-TOWNS Loom
+ foo = getVarOrDirectByte(PARAM_2);
+ debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ break;
+ case 36:
+ // TODO: Sets the loudness of a sound resource. Used in Indy3 and Zak.
+ foo = getVarOrDirectByte(PARAM_2);
+ bar = fetchScriptByte();
+ debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ break;
+ case 37:
+ // TODO: Sets the pitch of a sound resource (pitch = foo - center semitones.
+ // "center" is at 0x32 in the sfx resource (always 0x3C in zak256, but sometimes different in Indy3).
+ foo = getVarOrDirectByte(PARAM_2);
+ debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ break;
+
+ default:
+ error("o5_resourceRoutines: default case %d", op);
+ }
+}
+
+void ScummEngine_v5::o5_roomOps() {
+ int a = 0, b = 0, c, d, e;
+
+ if (_version == 3) {
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+ }
+
+ _opcode = fetchScriptByte();
+ switch (_opcode & 0x1F) {
+ case 1: // SO_ROOM_SCROLL
+ if (_version != 3) {
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+ }
+ if (a < (_screenWidth / 2))
+ a = (_screenWidth / 2);
+ if (b < (_screenWidth / 2))
+ b = (_screenWidth / 2);
+ if (a > _roomWidth - (_screenWidth / 2))
+ a = _roomWidth - (_screenWidth / 2);
+ if (b > _roomWidth - (_screenWidth / 2))
+ b = _roomWidth - (_screenWidth / 2);
+ VAR(VAR_CAMERA_MIN_X) = a;
+ VAR(VAR_CAMERA_MAX_X) = b;
+ break;
+ case 2: // SO_ROOM_COLOR
+ if (_features & GF_SMALL_HEADER) {
+ if (_version != 3) {
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+ }
+ checkRange(256, 0, a, "o5_roomOps: 2: Illegal room color slot (%d)");
+ _roomPalette[b] = a;
+ _fullRedraw = true;
+ } else {
+ error("room-color is no longer a valid command");
+ }
+ break;
+
+ case 3: // SO_ROOM_SCREEN
+ if (_version != 3) {
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+ }
+ initScreens(a, b);
+ break;
+ case 4: // SO_ROOM_PALETTE
+ if (_features & GF_SMALL_HEADER) {
+ if (_version != 3) {
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+ }
+ checkRange(256, 0, a, "o5_roomOps: 2: Illegal room color slot (%d)");
+ _shadowPalette[b] = a;
+ setDirtyColors(b, b);
+ } else {
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+ c = getVarOrDirectWord(PARAM_3);
+ _opcode = fetchScriptByte();
+ d = getVarOrDirectByte(PARAM_1);
+ setPalColor(d, a, b, c); /* index, r, g, b */
+ }
+ break;
+ case 5: // SO_ROOM_SHAKE_ON
+ setShake(1);
+ break;
+ case 6: // SO_ROOM_SHAKE_OFF
+ setShake(0);
+ break;
+ case 7: // SO_ROOM_SCALE
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ _opcode = fetchScriptByte();
+ c = getVarOrDirectByte(PARAM_1);
+ d = getVarOrDirectByte(PARAM_2);
+ _opcode = fetchScriptByte();
+ e = getVarOrDirectByte(PARAM_2);
+ setScaleSlot(e - 1, 0, b, a, 0, d, c);
+ break;
+ case 8: // SO_ROOM_INTENSITY
+ if (_features & GF_SMALL_HEADER) {
+ if (_version != 3) {
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+ }
+ c = getVarOrDirectWord(PARAM_3);
+ } else {
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ c = getVarOrDirectByte(PARAM_3);
+ }
+ darkenPalette(a, a, a, b, c);
+ break;
+ case 9: // SO_ROOM_SAVEGAME
+ _saveLoadFlag = getVarOrDirectByte(PARAM_1);
+ _saveLoadSlot = getVarOrDirectByte(PARAM_2);
+ _saveLoadSlot = 99; /* use this slot */
+ _saveTemporaryState = true;
+ break;
+ case 10: // SO_ROOM_FADE
+ a = getVarOrDirectWord(PARAM_1);
+ if (a) {
+ if (_platform == Common::kPlatformFMTowns) {
+ switch (a) {
+ case 8: // compose kMainVirtScreen over a screen buffer
+ case 9: // call 0x110:0x20 _ax=0x601 _edx=2
+ case 10: // call 0x110:0x20 _ax=0x601 _edx=3
+ case 11: // clear screen 0x1C:0x45000 sizeof(640 * 320)
+ case 12: // call 0x110:0x20 _ax=0x601 _edx=0
+ case 13: // call 0x110:0x20 _ax=0x601 _edx=1
+ case 16: // enable clearing of a screen buffer in drawBitmap()
+ case 17: // disable clearing of a screen buffer in drawBitmap()
+ case 18: // clear a screen buffer
+ case 19: // enable palette operations (palManipulate(), cyclePalette() etc.)
+ case 20: // disable palette operations
+ case 21: // disable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens()
+ case 22: // enable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens()
+ case 30:
+ debug(0, "o5_roomOps: unhandled FM-TOWNS fadeEffect %d", a);
+ return;
+ break;
+ }
+ }
+ _switchRoomEffect = (byte)(a & 0xFF);
+ _switchRoomEffect2 = (byte)(a >> 8);
+ } else {
+ fadeIn(_newEffect);
+ }
+ break;
+ case 11: // SO_RGB_ROOM_INTENSITY
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+ c = getVarOrDirectWord(PARAM_3);
+ _opcode = fetchScriptByte();
+ d = getVarOrDirectByte(PARAM_1);
+ e = getVarOrDirectByte(PARAM_2);
+ darkenPalette(a, b, c, d, e);
+ break;
+ case 12: // SO_ROOM_SHADOW
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectWord(PARAM_2);
+ c = getVarOrDirectWord(PARAM_3);
+ _opcode = fetchScriptByte();
+ d = getVarOrDirectByte(PARAM_1);
+ e = getVarOrDirectByte(PARAM_2);
+ setupShadowPalette(a, b, c, d, e, 0, 256);
+ break;
+
+ case 13: // SO_SAVE_STRING
+ {
+ Common::OutSaveFile *file;
+ char filename[256], *s;
+
+ a = getVarOrDirectByte(PARAM_1);
+ s = filename;
+ while ((*s++ = fetchScriptByte()));
+
+ file = _saveFileMan->openForSaving(filename);
+ if (file != NULL) {
+ byte *ptr;
+ ptr = getResourceAddress(rtString, a);
+ file->write(ptr, resStrLen(ptr) + 1);
+ delete file;
+ VAR(VAR_SOUNDRESULT) = 0;
+ }
+ break;
+ }
+ case 14: // SO_LOAD_STRING
+ {
+ Common::InSaveFile *file;
+ char filename[256], *s;
+
+ a = getVarOrDirectByte(PARAM_1);
+ s = filename;
+ while ((*s++ = fetchScriptByte()));
+
+ file = _saveFileMan->openForLoading(filename);
+ if (file != NULL) {
+ byte *ptr;
+ int len = 256, cnt = 0;
+ ptr = (byte *)malloc(len);
+ while (ptr) {
+ int r = file->read(ptr + cnt, len - cnt);
+ if ((cnt += r) < len) break;
+ ptr = (byte *)realloc(ptr, len *= 2);
+ }
+ ptr[cnt] = '\0';
+ loadPtrToResource(rtString, a, ptr);
+ free(ptr);
+ delete file;
+ }
+ break;
+ }
+ case 15: // SO_ROOM_TRANSFORM
+ a = getVarOrDirectByte(PARAM_1);
+ _opcode = fetchScriptByte();
+ b = getVarOrDirectByte(PARAM_1);
+ c = getVarOrDirectByte(PARAM_2);
+ _opcode = fetchScriptByte();
+ d = getVarOrDirectByte(PARAM_1);
+ palManipulateInit(a, b, c, d);
+ break;
+
+ case 16: // SO_CYCLE_SPEED
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ checkRange(16, 1, a, "o5_roomOps: 16: color cycle out of range (%d)");
+ _colorCycle[a - 1].delay = (b != 0) ? 0x4000 / (b * 0x4C) : 0;
+ break;
+ default:
+ error("o5_roomOps: unknown subopcode %d", _opcode & 0x1F);
+ }
+}
+
+void ScummEngine_v5::o5_saveRestoreVerbs() {
+ int a, b, c, slot, slot2;
+
+ _opcode = fetchScriptByte();
+
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ c = getVarOrDirectByte(PARAM_3);
+
+ switch (_opcode) {
+ case 1: // SO_SAVE_VERBS
+ while (a <= b) {
+ slot = getVerbSlot(a, 0);
+ if (slot && _verbs[slot].saveid == 0) {
+ _verbs[slot].saveid = c;
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+ }
+ a++;
+ }
+ break;
+ case 2: // SO_RESTORE_VERBS
+ while (a <= b) {
+ slot = getVerbSlot(a, c);
+ if (slot) {
+ slot2 = getVerbSlot(a, 0);
+ if (slot2)
+ killVerb(slot2);
+ slot = getVerbSlot(a, c);
+ _verbs[slot].saveid = 0;
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+ }
+ a++;
+ }
+ break;
+ case 3: // SO_DELETE_VERBS
+ while (a <= b) {
+ slot = getVerbSlot(a, c);
+ if (slot)
+ killVerb(slot);
+ a++;
+ }
+ break;
+ default:
+ error("o5_saveRestoreVerbs: unknown subopcode %d", _opcode);
+ }
+}
+
+void ScummEngine_v5::o5_setCameraAt() {
+ setCameraAtEx(getVarOrDirectWord(PARAM_1));
+}
+
+void ScummEngine_v5::o5_setObjectName() {
+ int obj = getVarOrDirectWord(PARAM_1);
+ setObjectName(obj);
+}
+
+void ScummEngine_v5::o5_setOwnerOf() {
+ int obj, owner;
+
+ obj = getVarOrDirectWord(0x80);
+ owner = getVarOrDirectByte(0x40);
+
+ setOwnerOf(obj, owner);
+}
+
+void ScummEngine_v5::o5_setState() {
+ int obj, state;
+ obj = getVarOrDirectWord(PARAM_1);
+ state = getVarOrDirectByte(PARAM_2);
+ putState(obj, state);
+ markObjectRectAsDirty(obj);
+ if (_bgNeedsRedraw)
+ clearDrawObjectQueue();
+}
+
+void ScummEngine_v5::o5_setVarRange() {
+ int a, b;
+
+ getResultPos();
+ a = fetchScriptByte();
+ do {
+ if (_opcode & 0x80)
+ b = fetchScriptWordSigned();
+ else
+ b = fetchScriptByte();
+
+ setResult(b);
+ _resultVarNumber++;
+ } while (--a);
+
+ // Macintosh verison of indy3ega used different interface, so adjust values.
+ if (_gameId == GID_INDY3 && _platform == Common::kPlatformMacintosh) {
+ VAR(68) = 0;
+ VAR(69) = 0;
+ VAR(70) = 168;
+ VAR(71) = 0;
+ VAR(72) = 168;
+ VAR(73) = 0;
+ VAR(74) = 168;
+ VAR(75) = 0;
+ VAR(76) = 176;
+ VAR(77) = 176;
+ VAR(78) = 184;
+ VAR(79) = 184;
+ VAR(80) = 192;
+ VAR(81) = 192;
+ }
+}
+
+void ScummEngine_v5::o5_startMusic() {
+ if (_platform == Common::kPlatformFMTowns && _version == 3) {
+ // In FM-TOWNS games this is some kind of Audio CD status query function.
+ // See also bug #762589 (thanks to Hibernatus for providing the information).
+ getResultPos();
+ int b = getVarOrDirectByte(PARAM_1);
+ int result = 0;
+ switch (b) {
+ case 0:
+ result = _sound->pollCD() == 0;
+ break;
+ case 0xFC:
+ // TODO: Unpause (resume) audio track. We'll have to extend Sound and OSystem for this.
+ break;
+ case 0xFD:
+ // TODO: Pause audio track. We'll have to extend Sound and OSystem for this.
+ break;
+ case 0xFE:
+ result = _sound->getCurrentCDSound();
+ break;
+ case 0xFF:
+ // TODO: Might return current CD volume in FM-TOWNS Loom. See also bug #805691.
+ break;
+ default:
+ // TODO: return track length in seconds. We'll have to extend Sound and OSystem for this.
+ // To check scummvm returns the right track length you
+ // can look at the global script #9 (0x888A in 49.LFL).
+ break;
+ }
+ debugC(DEBUG_GENERAL,"o5_startMusic(%d)", b);
+ setResult(result);
+ } else {
+ _sound->addSoundToQueue(getVarOrDirectByte(PARAM_1));
+ }
+}
+
+void ScummEngine_v5::o5_startSound() {
+ const byte *oldaddr = _scriptPointer - 1;
+ int sound = getVarOrDirectByte(PARAM_1);
+
+ // WORKAROUND: In the scene where Largo is talking to Mad Marty, the
+ // Woodtick music often resumes before Largo's theme has finished. As
+ // far as I can tell, this is a script bug.
+
+ if (_gameId == GID_MONKEY2 && sound == 110 && _sound->isSoundRunning(151)) {
+ debug(1, "Delaying Woodtick music until Largo's theme has finished\n");
+ _scriptPointer = oldaddr;
+ o5_breakHere();
+ return;
+ }
+
+ if (VAR_MUSIC_TIMER != 0xFF)
+ VAR(VAR_MUSIC_TIMER) = 0;
+ _sound->addSoundToQueue(sound);
+}
+
+void ScummEngine_v5::o5_stopMusic() {
+ _sound->stopAllSounds();
+}
+
+void ScummEngine_v5::o5_stopSound() {
+ _sound->stopSound(getVarOrDirectByte(PARAM_1));
+}
+
+void ScummEngine_v5::o5_isSoundRunning() {
+ int snd;
+ getResultPos();
+ snd = getVarOrDirectByte(PARAM_1);
+ if (snd)
+ snd = _sound->isSoundRunning(snd);
+ setResult(snd);
+}
+
+void ScummEngine_v5::o5_soundKludge() {
+ int items[16];
+
+ if (_features & GF_SMALL_HEADER) { // Is WaitForSentence in SCUMM V3
+ if (_sentenceNum) {
+ if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ } else if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+
+ _scriptPointer--;
+ o5_breakHere();
+ return;
+ }
+
+ int num = getWordVararg(items);
+ _sound->soundKludge(items, num);
+}
+
+void ScummEngine_v5::o5_startObject() {
+ int obj, script;
+ int data[16];
+
+ obj = getVarOrDirectWord(PARAM_1);
+ script = getVarOrDirectByte(PARAM_2);
+
+ getWordVararg(data);
+ runObjectScript(obj, script, 0, 0, data);
+}
+
+void ScummEngine_v5::o5_startScript() {
+ int op, script;
+ int data[16];
+
+ op = _opcode;
+ script = getVarOrDirectByte(PARAM_1);
+
+ getWordVararg(data);
+
+ // FIXME: Script 171 loads a complete room resource, instead of the actual script.
+ // Causing invalid opcode cases, see bug #1290485
+ if (_gameId == GID_ZAK && (_platform == Common::kPlatformFMTowns) && script == 171)
+ return;
+
+ if (!_copyProtection) {
+ // Method used by original games to skip copy protection scheme
+ if (_gameId == GID_LOOM && _version == 3 && _currentRoom == 69 && script == 201)
+ script = 205;
+ else if ((_gameId == GID_MONKEY_VGA || _gameId == GID_MONKEY_EGA) && script == 152)
+ return;
+ }
+
+ runScript(script, (op & 0x20) != 0, (op & 0x40) != 0, data);
+}
+
+void ScummEngine_v5::o5_stopObjectCode() {
+ stopObjectCode();
+}
+
+void ScummEngine_v5::o5_stopObjectScript() {
+ stopObjectScript(getVarOrDirectWord(PARAM_1));
+}
+
+void ScummEngine_v5::o5_stopScript() {
+ int script;
+
+ script = getVarOrDirectByte(PARAM_1);
+
+ if (!script)
+ stopObjectCode();
+ else
+ stopScript(script);
+}
+
+void ScummEngine_v5::o5_stringOps() {
+ int a, b, c, i;
+ byte *ptr;
+
+ _opcode = fetchScriptByte();
+ switch (_opcode & 0x1F) {
+ case 1: /* loadstring */
+ loadPtrToResource(rtString, getVarOrDirectByte(PARAM_1), NULL);
+ break;
+ case 2: /* copystring */
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ res.nukeResource(rtString, a);
+ ptr = getResourceAddress(rtString, b);
+ if (ptr)
+ loadPtrToResource(rtString, a, ptr);
+ break;
+ case 3: /* set string char */
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ c = getVarOrDirectByte(PARAM_3);
+ ptr = getResourceAddress(rtString, a);
+ if (!(_gameId == GID_LOOM && _version == 4)) { /* FIXME - LOOM256 */
+ if (ptr == NULL)
+ error("String %d does not exist", a);
+ ptr[b] = c;
+ }
+
+ break;
+
+ case 4: /* get string char */
+ getResultPos();
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ ptr = getResourceAddress(rtString, a);
+ if (ptr == NULL)
+ error("String %d does not exist", a);
+ setResult(ptr[b]);
+ break;
+
+ case 5: /* create empty string */
+ a = getVarOrDirectByte(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ res.nukeResource(rtString, a);
+ if (b) {
+ ptr = res.createResource(rtString, a, b);
+ if (ptr) {
+ for (i = 0; i < b; i++)
+ ptr[i] = 0;
+ }
+ }
+ break;
+ }
+}
+
+void ScummEngine_v5::o5_subtract() {
+ int a;
+ getResultPos();
+ a = getVarOrDirectWord(PARAM_1);
+ setResult(readVar(_resultVarNumber) - a);
+}
+
+void ScummEngine_v5::o5_verbOps() {
+ int verb, slot;
+ VerbSlot *vs;
+ int a, b;
+ byte *ptr;
+
+ verb = getVarOrDirectByte(PARAM_1);
+
+ slot = getVerbSlot(verb, 0);
+ checkRange(_numVerbs - 1, 0, slot, "Illegal new verb slot %d");
+
+ vs = &_verbs[slot];
+ vs->verbid = verb;
+
+ while ((_opcode = fetchScriptByte()) != 0xFF) {
+ switch (_opcode & 0x1F) {
+ case 1: // SO_VERB_IMAGE
+ a = getVarOrDirectWord(PARAM_1);
+ if (slot) {
+ setVerbObject(_roomResource, a, slot);
+ vs->type = kImageVerbType;
+ }
+ break;
+ case 2: // SO_VERB_NAME
+ loadPtrToResource(rtVerb, slot, NULL);
+ if (slot == 0)
+ res.nukeResource(rtVerb, slot);
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 3: // SO_VERB_COLOR
+ vs->color = getVarOrDirectByte(PARAM_1);
+ break;
+ case 4: // SO_VERB_HICOLOR
+ vs->hicolor = getVarOrDirectByte(PARAM_1);
+ break;
+ case 5: // SO_VERB_AT
+ vs->curRect.left = getVarOrDirectWord(PARAM_1);
+ vs->curRect.top = getVarOrDirectWord(PARAM_2);
+ // Macintosh verison of indy3ega used different interface, so adjust values.
+ if ((_platform == Common::kPlatformMacintosh) && (_gameId == GID_INDY3)) {
+ switch (verb) {
+ case 1:
+ case 2:
+ case 9:
+ vs->curRect.left += 16;
+ break;
+ case 10:
+ case 11:
+ case 12:
+ vs->curRect.left += 36;
+ break;
+ case 4:
+ case 5:
+ case 8:
+ vs->curRect.left += 60;
+ break;
+ case 13:
+ case 32:
+ case 33:
+ case 34:
+ vs->curRect.left += 90;
+ break;
+ case 107:
+ vs->curRect.left -= 54;
+ vs->curRect.top += 16;
+ break;
+ case 108:
+ vs->curRect.left -= 54;
+ vs->curRect.top += 8;
+ break;
+ }
+ } else if (_gameId == GID_LOOM && _version == 4) {
+ // FIXME: hack loom notes into right spot
+ if ((verb >= 90) && (verb <= 97)) { // Notes
+ switch (verb) {
+ case 90:
+ case 91:
+ vs->curRect.top -= 7;
+ break;
+ case 92:
+ vs->curRect.top -= 6;
+ break;
+ case 93:
+ vs->curRect.top -= 4;
+ break;
+ case 94:
+ vs->curRect.top -= 3;
+ break;
+ case 95:
+ vs->curRect.top -= 1;
+ break;
+ case 97:
+ vs->curRect.top -= 5;
+ }
+ }
+ }
+ break;
+ case 6: // SO_VERB_ON
+ vs->curmode = 1;
+ break;
+ case 7: // SO_VERB_OFF
+ vs->curmode = 0;
+ break;
+ case 8: // SO_VERB_DELETE
+ killVerb(slot);
+ break;
+ case 9: // SO_VERB_NEW
+ slot = getVerbSlot(verb, 0);
+ if (slot == 0) {
+ for (slot = 1; slot < _numVerbs; slot++) {
+ if (_verbs[slot].verbid == 0)
+ break;
+ }
+ if (slot == _numVerbs)
+ error("Too many verbs");
+ }
+ vs = &_verbs[slot];
+ vs->verbid = verb;
+ vs->color = 2;
+ vs->hicolor = (_version == 3) ? 14 : 0;
+ vs->dimcolor = 8;
+ vs->type = kTextVerbType;
+ vs->charset_nr = _string[0]._default.charset;
+ vs->curmode = 0;
+ vs->saveid = 0;
+ vs->key = 0;
+ vs->center = 0;
+ vs->imgindex = 0;
+ break;
+
+ case 16: // SO_VERB_DIMCOLOR
+ vs->dimcolor = getVarOrDirectByte(PARAM_1);
+ break;
+ case 17: // SO_VERB_DIM
+ vs->curmode = 2;
+ break;
+ case 18: // SO_VERB_KEY
+ vs->key = getVarOrDirectByte(PARAM_1);
+ break;
+ case 19: // SO_VERB_CENTER
+ vs->center = 1;
+ break;
+ case 20: // SO_VERB_NAME_STR
+ ptr = getResourceAddress(rtString, getVarOrDirectWord(PARAM_1));
+ if (!ptr)
+ res.nukeResource(rtVerb, slot);
+ else {
+ loadPtrToResource(rtVerb, slot, ptr);
+ }
+ if (slot == 0)
+ res.nukeResource(rtVerb, slot);
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 22: /* assign object */
+ a = getVarOrDirectWord(PARAM_1);
+ b = getVarOrDirectByte(PARAM_2);
+ if (slot && vs->imgindex != a) {
+ setVerbObject(b, a, slot);
+ vs->type = kImageVerbType;
+ vs->imgindex = a;
+ }
+ break;
+ case 23: /* set back color */
+ vs->bkcolor = getVarOrDirectByte(PARAM_1);
+ break;
+ default:
+ error("o5_verbOps: unknown subopcode %d", _opcode & 0x1F);
+ }
+ }
+
+ // Force redraw of the modified verb slot
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+}
+
+void ScummEngine_v5::o5_wait() {
+ const byte *oldaddr = _scriptPointer - 1;
+
+ if ((_gameId == GID_INDY3) && !(_platform == Common::kPlatformMacintosh)) {
+ _opcode = 2;
+ } else
+ _opcode = fetchScriptByte();
+
+ switch (_opcode & 0x1F) {
+ case 1: // SO_WAIT_FOR_ACTOR
+ {
+ Actor *a = derefActorSafe(getVarOrDirectByte(PARAM_1), "o5_wait");
+ if (a && a->isInCurrentRoom() && a->_moving)
+ break;
+ return;
+ }
+ case 2: // SO_WAIT_FOR_MESSAGE
+ if (VAR(VAR_HAVE_MSG))
+ break;
+ return;
+ case 3: // SO_WAIT_FOR_CAMERA
+ if (camera._cur.x / 8 != camera._dest.x / 8)
+ break;
+ return;
+ case 4: // SO_WAIT_FOR_SENTENCE
+ if (_sentenceNum) {
+ if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ }
+ if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ default:
+ error("o5_wait: unknown subopcode %d", _opcode & 0x1F);
+ return;
+ }
+
+ _scriptPointer = oldaddr;
+ o5_breakHere();
+}
+
+void ScummEngine_v5::o5_walkActorTo() {
+ int x, y;
+ Actor *a;
+
+ a = derefActor(getVarOrDirectByte(PARAM_1), "o5_walkActorTo");
+ x = getVarOrDirectWord(PARAM_2);
+ y = getVarOrDirectWord(PARAM_3);
+ a->startWalkActor(x, y, -1);
+}
+
+void ScummEngine_v5::o5_walkActorToActor() {
+ int x, y;
+ Actor *a, *a2;
+ int nr = getVarOrDirectByte(PARAM_1);
+ int nr2 = getVarOrDirectByte(PARAM_2);
+ int dist = fetchScriptByte();
+
+ if (nr == 106 && _gameId == GID_INDY4) {
+ printf("Bypassing Indy4 bug\n");
+ return;
+ }
+
+ if (_gameId == GID_LOOM && _version == 4 && nr == 1 && nr2 == 0 &&
+ dist == 255 && vm.slot[_currentScript].number == 98) {
+ // WORKAROUND bug #743615: LoomCD script 98 contains this:
+ // walkActorToActor(1,0,255)
+ // Once again this is either a script bug, or there is some hidden
+ // or unknown meaning to this odd walk request...
+ return;
+ }
+
+ if (_gameId == GID_INDY4 && nr == 1 && nr2 == 106 &&
+ dist == 255 && vm.slot[_currentScript].number == 210) {
+ // WORKAROUND bug: Work around an invalid actor bug when using the
+ // camel in Fate of Atlantis, the "wits" path. The room-65-210 script
+ // contains this:
+ // walkActorToActor(1,106,255)
+ // Once again this is either a script bug, or there is some hidden
+ // or unknown meaning to this odd walk request...
+ return;
+ }
+
+ a = derefActor(nr, "o5_walkActorToActor");
+ if (!a->isInCurrentRoom())
+ return;
+
+ a2 = derefActor(nr2, "o5_walkActorToActor(2)");
+ if (!a2->isInCurrentRoom())
+ return;
+
+ if (_version <= 2)
+ dist *= 8;
+ else if (dist == 0xFF) {
+ dist = a->_scalex * a->_width / 0xFF;
+ dist += (a2->_scalex * a2->_width / 0xFF) / 2;
+ }
+ x = a2->_pos.x;
+ y = a2->_pos.y;
+ if (x < a->_pos.x)
+ x += dist;
+ else
+ x -= dist;
+
+ if (_version <= 3) {
+ AdjustBoxResult abr = a->adjustXYToBeInBox(x, y);
+ x = abr.x;
+ y = abr.y;
+ }
+ a->startWalkActor(x, y, -1);
+}
+
+void ScummEngine_v5::o5_walkActorToObject() {
+ int obj;
+ Actor *a;
+
+ a = derefActor(getVarOrDirectByte(PARAM_1), "o5_walkActorToObject");
+ obj = getVarOrDirectWord(PARAM_2);
+ if (whereIsObject(obj) != WIO_NOT_FOUND) {
+ int x, y, dir;
+ getObjectXYPos(obj, x, y, dir);
+ a->startWalkActor(x, y, dir);
+ }
+}
+
+int ScummEngine_v5::getWordVararg(int *ptr) {
+ int i;
+
+ for (i = 0; i < 16; i++)
+ ptr[i] = 0;
+
+ i = 0;
+ while ((_opcode = fetchScriptByte()) != 0xFF) {
+ ptr[i++] = getVarOrDirectWord(PARAM_1);
+ }
+ return i;
+}
+
+void ScummEngine_v5::decodeParseString() {
+ int textSlot;
+
+ switch (_actorToPrintStrFor) {
+ case 252:
+ textSlot = 3;
+ break;
+ case 253:
+ textSlot = 2;
+ break;
+ case 254:
+ textSlot = 1;
+ break;
+ default:
+ textSlot = 0;
+ }
+
+ _string[textSlot].loadDefault();
+
+ while ((_opcode = fetchScriptByte()) != 0xFF) {
+ switch (_opcode & 0xF) {
+ case 0: // SO_AT
+ _string[textSlot].xpos = getVarOrDirectWord(PARAM_1);
+ _string[textSlot].ypos = getVarOrDirectWord(PARAM_2);
+ _string[textSlot].overhead = false;
+ break;
+ case 1: // SO_COLOR
+ _string[textSlot].color = getVarOrDirectByte(PARAM_1);
+ break;
+ case 2: // SO_CLIPPED
+ _string[textSlot].right = getVarOrDirectWord(PARAM_1);
+ break;
+ case 3: // SO_ERASE
+ {
+ int w = getVarOrDirectWord(PARAM_1);
+ int h = getVarOrDirectWord(PARAM_2);
+ // restoreCharsetBg(xpos, xpos + w, ypos, ypos + h)
+ error("ScummEngine_v5::decodeParseString: Unhandled case 3: %d, %d", w, h);
+ }
+ break;
+ case 4: // SO_CENTER
+ _string[textSlot].center = true;
+ _string[textSlot].overhead = false;
+ break;
+ case 6: // SO_LEFT
+ if (_version == 3) {
+ _string[textSlot].height = getVarOrDirectWord(PARAM_1);
+ } else {
+ _string[textSlot].center = false;
+ _string[textSlot].overhead = false;
+ }
+ break;
+ case 7: // SO_OVERHEAD
+ _string[textSlot].overhead = true;
+ break;
+ case 8:{ // SO_SAY_VOICE
+ int offset = (uint16)getVarOrDirectWord(PARAM_1);
+ int delay = (uint16)getVarOrDirectWord(PARAM_2);
+
+ if (_gameId == GID_LOOM && _version == 4) {
+ if (offset == 0 && delay == 0) {
+ VAR(VAR_MUSIC_TIMER) = 0;
+ _sound->stopCD();
+ } else {
+ // Loom specified the offset from the start of the CD;
+ // thus we have to subtract the length of the first track
+ // (22500 frames) plus the 2 second = 150 frame leadin.
+ // I.e. in total 22650 frames.
+ offset = (int)(offset * 7.5 - 22500 - 2*75);
+
+ // Slightly increase the delay (5 frames = 1/25 of a second).
+ // This noticably improves the experience in Loom CD.
+ delay = (int)(delay * 7.5 + 5);
+
+ _sound->playCDTrack(1, 0, offset, delay);
+ }
+ } else {
+ error("ScummEngine_v5::decodeParseString: Unhandled case 8");
+ }
+ }
+ break;
+ case 15: // SO_TEXTSTRING
+ // WORKAROUND: This happens when Chaos introduces
+ // herself to bishop Mandible. Of all the places to put
+ // a typo...
+ if (_gameId == GID_LOOM && strcmp((const char *) _scriptPointer, "I am Choas.") == 0)
+ printString(textSlot, (const byte *) "I am Chaos.");
+ else
+ printString(textSlot, _scriptPointer);
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+
+
+ // In SCUMM V1-V3, there were no 'default' values for the text slot
+ // values. Hence to achieve correct behaviour, we have to keep the
+ // 'default' values in sync with the active values.
+ //
+ // Note: This is needed for Indy3 (Grail Diary). It's also needed
+ // for Loom, or the lines Bobbin speaks during the intro are put
+ // at position 0,0.
+ //
+ // Note: We can't use saveDefault() here because we only want to
+ // save the position and color. In particular, we do not want to
+ // save the 'center' flag. See bug #933168.
+ if (_version <= 3) {
+ _string[textSlot]._default.xpos = _string[textSlot].xpos;
+ _string[textSlot]._default.ypos = _string[textSlot].ypos;
+ _string[textSlot]._default.height = _string[textSlot].height;
+ _string[textSlot]._default.color = _string[textSlot].color;
+ }
+ return;
+ default:
+ error("ScummEngine_v5::decodeParseString: Unhandled case %d", _opcode & 0xF);
+ }
+ }
+
+ _string[textSlot].saveDefault();
+}
+
+void ScummEngine_v5::o5_oldRoomEffect() {
+ int a;
+
+ _opcode = fetchScriptByte();
+ if ((_opcode & 0x1F) == 3) {
+ a = getVarOrDirectWord(PARAM_1);
+
+#if 1
+ if (_platform == Common::kPlatformFMTowns && _version == 3) {
+ // FIXME / TODO: OK the first thing to note is: at least in Zak256,
+ // maybe also in other games, this opcode does a bit more. I added
+ // some stubs here, but somebody with a full IDA or more knowledge
+ // about this will have to fill in the gaps. At least now we know
+ // that something is missing here :-)
+
+ if (a == 4) {
+ printf("o5_oldRoomEffect ODDBALL: _opcode = 0x%x, a = 0x%x\n", _opcode, a);
+ // No idea what byte_2FCCF is, but it's a globale boolean flag.
+ // I only add it here as a temporary hack to make the pseudo code compile.
+ // Maybe it is just there as a reentry protection guard, given
+ // how it is used? It might also correspond to _screenEffectFlag.
+ int byte_2FCCF = 0;
+
+ // For now, we force a redraw of the screen background. This
+ // way the Zak end credits seem to work mostly correct.
+ VirtScreen *vs = &virtscr[0];
+ restoreBG(Common::Rect(0,vs->topline, vs->w, vs->topline + vs->h));
+ vs->setDirtyRange(0, vs->h);
+ updateDirtyScreen(kMainVirtScreen);
+
+ if (byte_2FCCF) {
+ // Here now "sub_1C44" is called, which sets byte_2FCCF to 0 then
+ // calls yet another sub (which also reads byte_2FCCF):
+
+ byte_2FCCF = 0;
+ //call sub_0BB3
+
+
+ // Now sub_085C is called. This is quite simply: it sets
+ // 0xF000 bytes. starting at 0x40000 to 0. No idea what that
+ // buffer is, maybe a screen buffer, though. Note that
+ // 0xF000 = 320*192.
+ // Maybe this is also the charset mask being cleaned?
+
+ // call sub_085C
+
+
+ // And then sub_1C54 is called, which is almost identical to
+ // the above sub_1C44, only it sets byte_2FCCF to 1:
+
+ byte_2FCCF = 1;
+ // call sub_0BB3
+
+ } else {
+ // Here only sub_085C is called (see comment above)
+
+ // call sub_085C
+ }
+ return;
+ }
+#endif
+
+ }
+ if (a) {
+ _switchRoomEffect = (byte)(a & 0xFF);
+ _switchRoomEffect2 = (byte)(a >> 8);
+ } else {
+ fadeIn(_newEffect);
+ }
+ }
+}
+
+void ScummEngine_v5::o5_pickupObjectOld() {
+ int obj = getVarOrDirectWord(PARAM_1);
+
+ if (obj < 1) {
+ error("pickupObjectOld received invalid index %d (script %d)", obj, vm.slot[_currentScript].number);
+ }
+
+ if (getObjectIndex(obj) == -1)
+ return;
+
+ if (whereIsObject(obj) == WIO_INVENTORY) /* Don't take an */
+ return; /* object twice */
+
+ // debug(0, "adding %d from %d to inventoryOld", obj, _currentRoom);
+ addObjectToInventory(obj, _roomResource);
+ markObjectRectAsDirty(obj);
+ putOwner(obj, VAR(VAR_EGO));
+ putClass(obj, kObjectClassUntouchable, 1);
+ putState(obj, 1);
+ clearDrawObjectQueue();
+ runInventoryScript(1);
+}
+
+#undef PARAM_1
+#undef PARAM_2
+#undef PARAM_3
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
new file mode 100644
index 0000000000..77709eed21
--- /dev/null
+++ b/engines/scumm/script_v6.cpp
@@ -0,0 +1,3183 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/imuse.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/insane/insane.h"
+#include "scumm/intern.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+#include "scumm/smush/smush_player.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "scumm/verbs.h"
+
+#include "sound/mididrv.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v6, x)
+
+void ScummEngine_v6::setupOpcodes() {
+ static const OpcodeEntryV6 opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o6_pushByteVar),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayRead),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedRead),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o6_invalid),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeByteVar),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayWrite),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedWrite),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarInc),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayInc),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarDec),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayDec),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o6_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o6_startObject),
+ OPCODE(o6_drawObject),
+ OPCODE(o6_drawObjectAt),
+ OPCODE(o6_drawBlastObject),
+ /* 64 */
+ OPCODE(o6_setBlastObjectWindow),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_stopMusic),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o6_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o6_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o6_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_startMusic),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o6_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o6_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o6_getVerbFromXY),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_createBoxMatrix),
+ OPCODE(o6_resourceRoutines),
+ /* 9C */
+ OPCODE(o6_roomOps),
+ OPCODE(o6_actorOps),
+ OPCODE(o6_verbOps),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o6_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o6_arrayOps),
+ OPCODE(o6_saveRestoreVerbs),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o6_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorAnimCounter1),
+ /* AC */
+ OPCODE(o6_soundKludge),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o6_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o6_talkActor),
+ OPCODE(o6_talkEgo),
+ /* BC */
+ OPCODE(o6_dimArray),
+ OPCODE(o6_dummy),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o6_dim2dimArray),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o6_kernelGetFunctions),
+ OPCODE(o6_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o6_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* DC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_findAllObjects),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E0 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_getPixel),
+ OPCODE(o6_invalid),
+ OPCODE(o6_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* EC */
+ OPCODE(o6_getActorLayer),
+ OPCODE(o6_getObjectNewDir),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F0 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F4 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* FC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV6 = opcodes;
+}
+
+void ScummEngine_v6::executeOpcode(byte i) {
+ OpcodeProcV6 op = _opcodesV6[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v6::getOpcodeDesc(byte i) {
+ return _opcodesV6[i].desc;
+}
+
+int ScummEngine_v6::popRoomAndObj(int *room) {
+ int obj;
+
+ if (_version >= 7) {
+ obj = pop();
+ *room = getObjectRoom(obj);
+ } else {
+ *room = pop();
+ obj = pop();
+ }
+
+ return obj;
+}
+
+ScummEngine_v6::ArrayHeader *ScummEngine_v6::defineArray(int array, int type, int dim2, int dim1) {
+ int id;
+ int size;
+ ArrayHeader *ah;
+
+ assert(0 <= type && type <= 5);
+
+
+ if (_heversion >= 61) {
+ if (type == kBitArray || type == kNibbleArray)
+ type = kByteArray;
+ } else {
+ // NOTE: The following code turns all arrays except string arrays into
+ // integer arrays. There seems to be no reason for this, and it wastes
+ // space. However, we can't just remove this either, as that would
+ // break savegame compatibility. So do not touch this unless you are
+ // also adding code which updated old savegames, too. And of course
+ // readArray() and writeArray() would have to be updated, too...
+ if (type != kStringArray)
+ type = kIntArray;
+ }
+
+ nukeArray(array);
+
+ id = findFreeArrayId();
+
+ if (_version == 8) {
+ if (array & 0x40000000) {
+ }
+
+ if (array & 0x80000000) {
+ error("Can't define bit variable as array pointer");
+ }
+
+ size = (type == kIntArray) ? 4 : 1;
+ } else {
+ if (array & 0x4000) {
+ }
+
+ if (array & 0x8000) {
+ error("Can't define bit variable as array pointer");
+ }
+
+ size = (type == kIntArray) ? 2 : 1;
+ }
+
+ writeVar(array, id);
+
+ size *= dim2 + 1;
+ size *= dim1 + 1;
+
+ ah = (ArrayHeader *)res.createResource(rtString, id, size + sizeof(ArrayHeader));
+
+ ah->type = TO_LE_16(type);
+ ah->dim1 = TO_LE_16(dim1 + 1);
+ ah->dim2 = TO_LE_16(dim2 + 1);
+
+ return ah;
+}
+
+void ScummEngine_v6::nukeArray(int a) {
+ int data;
+
+ data = readVar(a);
+
+ if (_heversion >= 80)
+ data &= ~0x33539000;
+
+ if (data)
+ res.nukeResource(rtString, data);
+ if (_heversion >= 60)
+ _arraySlot[data] = 0;
+
+ writeVar(a, 0);
+}
+
+int ScummEngine_v6::findFreeArrayId() {
+ byte **addr = res.address[rtString];
+ int i;
+
+ for (i = 1; i < _numArray; i++) {
+ if (!addr[i])
+ return i;
+ }
+ error("Out of array pointers, %d max", _numArray);
+ return -1;
+}
+
+#define SWAP16(x) x = SWAP_BYTES_16(x)
+
+ScummEngine_v6::ArrayHeader *ScummEngine_v6::getArray(int array) {
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+ if (!ah)
+ return 0;
+
+ // Workaround for a long standing bug where we save array headers in native
+ // endianness, instead of a fixed endianness. We try to detect savegames
+ // which were created on a big endian system and convert them to little
+ // endian.
+ if ((FROM_LE_16(ah->dim1) & 0xF000) || (FROM_LE_16(ah->dim2) & 0xF000) || (FROM_LE_16(ah->type) & 0xFF00)) {
+ SWAP16(ah->dim1);
+ SWAP16(ah->dim2);
+ SWAP16(ah->type);
+ }
+
+ return ah;
+}
+
+int ScummEngine_v6::readArray(int array, int idx, int base) {
+ ArrayHeader *ah = getArray(array);
+
+ if (ah == NULL || ah->data == NULL)
+ error("readArray: invalid array %d (%d)", array, readVar(array));
+
+ // WORKAROUND bug #645711. This is clearly a script bug, as this script
+ // excerpt shows nicely:
+ // ...
+ // [03A7] (5D) if (isAnyOf(array-447[localvar13][localvar14],[0,4])) {
+ // [03BD] (5D) if ((localvar13 != -1) && (localvar14 != -1)) {
+ // [03CF] (B6) printDebug.begin()
+ // ...
+ if (_gameId == GID_FT && array == 447 && _currentRoom == 95 && vm.slot[_currentScript].number == 2010 && idx == -1 && base == -1) {
+ return 0;
+ }
+
+ const int offset = base + idx * FROM_LE_16(ah->dim1);
+
+ if (offset < 0 || offset >= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2)) {
+ error("readArray: array %d out of bounds: [%d,%d] exceeds [%d,%d]",
+ array, base, idx, FROM_LE_16(ah->dim1), FROM_LE_16(ah->dim2));
+ }
+
+ int val;
+ if (FROM_LE_16(ah->type) != kIntArray) {
+ val = ah->data[offset];
+ } else if (_version == 8) {
+ val = (int32)READ_LE_UINT32(ah->data + offset * 4);
+ } else {
+ val = (int16)READ_LE_UINT16(ah->data + offset * 2);
+ }
+ return val;
+}
+
+void ScummEngine_v6::writeArray(int array, int idx, int base, int value) {
+ ArrayHeader *ah = getArray(array);
+ if (!ah)
+ return;
+
+ const int offset = base + idx * FROM_LE_16(ah->dim1);
+
+ if (offset < 0 || offset >= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2)) {
+ error("writeArray: array %d out of bounds: [%d,%d] exceeds [%d,%d]",
+ array, base, idx, FROM_LE_16(ah->dim1), FROM_LE_16(ah->dim2));
+ }
+
+ if (FROM_LE_16(ah->type) != kIntArray) {
+ ah->data[offset] = value;
+ } else if (_version == 8) {
+ WRITE_LE_UINT32(ah->data + offset * 4, value);
+ } else {
+ WRITE_LE_UINT16(ah->data + offset * 2, value);
+ }
+}
+
+void ScummEngine_v6::readArrayFromIndexFile() {
+ int num;
+ int a, b, c;
+
+ while ((num = _fileHandle->readUint16LE()) != 0) {
+ a = _fileHandle->readUint16LE();
+ b = _fileHandle->readUint16LE();
+ c = _fileHandle->readUint16LE();
+ if (c == kBitArray)
+ defineArray(num, kBitArray, a, b);
+ else
+ defineArray(num, kIntArray, a, b);
+ }
+}
+
+int ScummEngine_v6::getStackList(int *args, uint maxnum) {
+ uint num, i;
+
+ for (i = 0; i < maxnum; i++)
+ args[i] = 0;
+
+ num = pop();
+
+ if (num > maxnum)
+ error("Too many items %d in stack list, max %d", num, maxnum);
+
+ i = num;
+ while (i--) {
+ args[i] = pop();
+ }
+
+ return num;
+}
+
+void ScummEngine_v6::o6_pushByte() {
+ push(fetchScriptByte());
+}
+
+void ScummEngine_v6::o6_pushWord() {
+ push(fetchScriptWordSigned());
+}
+
+void ScummEngine_v6::o6_pushByteVar() {
+ push(readVar(fetchScriptByte()));
+}
+
+void ScummEngine_v6::o6_pushWordVar() {
+ push(readVar(fetchScriptWord()));
+}
+
+void ScummEngine_v6::o6_invalid() {
+ error("Invalid opcode '%x' at %x", _opcode, _scriptPointer - _scriptOrgPointer);
+}
+
+void ScummEngine_v6::o6_byteArrayRead() {
+ int base = pop();
+ push(readArray(fetchScriptByte(), 0, base));
+}
+
+void ScummEngine_v6::o6_wordArrayRead() {
+ int base = pop();
+ push(readArray(fetchScriptWord(), 0, base));
+}
+
+void ScummEngine_v6::o6_byteArrayIndexedRead() {
+ int base = pop();
+ int idx = pop();
+ push(readArray(fetchScriptByte(), idx, base));
+}
+
+void ScummEngine_v6::o6_wordArrayIndexedRead() {
+ int base = pop();
+ int idx = pop();
+ push(readArray(fetchScriptWord(), idx, base));
+}
+
+void ScummEngine_v6::o6_dup() {
+ int a = pop();
+ push(a);
+ push(a);
+}
+
+void ScummEngine_v6::o6_not() {
+ push(pop() == 0);
+}
+
+void ScummEngine_v6::o6_eq() {
+ push(pop() == pop());
+}
+
+void ScummEngine_v6::o6_neq() {
+ push(pop() != pop());
+}
+
+void ScummEngine_v6::o6_gt() {
+ int a = pop();
+ push(pop() > a);
+}
+
+void ScummEngine_v6::o6_lt() {
+ int a = pop();
+ push(pop() < a);
+}
+
+void ScummEngine_v6::o6_le() {
+ int a = pop();
+ push(pop() <= a);
+}
+
+void ScummEngine_v6::o6_ge() {
+ int a = pop();
+ push(pop() >= a);
+}
+
+void ScummEngine_v6::o6_add() {
+ int a = pop();
+ push(pop() + a);
+}
+
+void ScummEngine_v6::o6_sub() {
+ int a = pop();
+ push(pop() - a);
+}
+
+void ScummEngine_v6::o6_mul() {
+ int a = pop();
+ push(pop() * a);
+}
+
+void ScummEngine_v6::o6_div() {
+ int a = pop();
+ if (a == 0)
+ error("division by zero");
+ push(pop() / a);
+}
+
+void ScummEngine_v6::o6_land() {
+ int a = pop();
+ push(pop() && a);
+}
+
+void ScummEngine_v6::o6_lor() {
+ int a = pop();
+ push(pop() || a);
+}
+
+void ScummEngine_v6::o6_bor() {
+ int a = pop();
+ push(pop() | a);
+}
+
+void ScummEngine_v6::o6_band() {
+ int a = pop();
+ push(pop() & a);
+}
+
+void ScummEngine_v6::o6_pop() {
+ pop();
+}
+
+void ScummEngine_v6::o6_writeByteVar() {
+ writeVar(fetchScriptByte(), pop());
+}
+
+void ScummEngine_v6::o6_writeWordVar() {
+ writeVar(fetchScriptWord(), pop());
+}
+
+void ScummEngine_v6::o6_byteArrayWrite() {
+ int a = pop();
+ writeArray(fetchScriptByte(), 0, pop(), a);
+}
+
+void ScummEngine_v6::o6_wordArrayWrite() {
+ int a = pop();
+ writeArray(fetchScriptWord(), 0, pop(), a);
+}
+
+void ScummEngine_v6::o6_byteArrayIndexedWrite() {
+ int val = pop();
+ int base = pop();
+ writeArray(fetchScriptByte(), pop(), base, val);
+}
+
+void ScummEngine_v6::o6_wordArrayIndexedWrite() {
+ int val = pop();
+ int base = pop();
+ writeArray(fetchScriptWord(), pop(), base, val);
+}
+
+void ScummEngine_v6::o6_byteVarInc() {
+ int var = fetchScriptByte();
+ writeVar(var, readVar(var) + 1);
+}
+
+void ScummEngine_v6::o6_wordVarInc() {
+ int var = fetchScriptWord();
+ writeVar(var, readVar(var) + 1);
+}
+
+void ScummEngine_v6::o6_byteArrayInc() {
+ int var = fetchScriptByte();
+ int base = pop();
+ writeArray(var, 0, base, readArray(var, 0, base) + 1);
+}
+
+void ScummEngine_v6::o6_wordArrayInc() {
+ int var = fetchScriptWord();
+ int base = pop();
+ writeArray(var, 0, base, readArray(var, 0, base) + 1);
+}
+
+void ScummEngine_v6::o6_byteVarDec() {
+ int var = fetchScriptByte();
+ writeVar(var, readVar(var) - 1);
+}
+
+void ScummEngine_v6::o6_wordVarDec() {
+ int var = fetchScriptWord();
+ writeVar(var, readVar(var) - 1);
+}
+
+void ScummEngine_v6::o6_byteArrayDec() {
+ int var = fetchScriptByte();
+ int base = pop();
+ writeArray(var, 0, base, readArray(var, 0, base) - 1);
+}
+
+void ScummEngine_v6::o6_wordArrayDec() {
+ int var = fetchScriptWord();
+ int base = pop();
+ writeArray(var, 0, base, readArray(var, 0, base) - 1);
+}
+
+void ScummEngine_v6::o6_if() {
+ if (pop())
+ o6_jump();
+ else
+ fetchScriptWord();
+}
+
+void ScummEngine_v6::o6_ifNot() {
+ if (!pop())
+ o6_jump();
+ else
+ fetchScriptWord();
+}
+
+void ScummEngine_v6::o6_jump() {
+ int offset = fetchScriptWordSigned();
+ _scriptPointer += offset;
+}
+
+void ScummEngine_v6::o6_startScript() {
+ int args[25];
+ int script, flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = pop();
+
+ // WORKAROUND bug #556558: At Dino Bungee National Memorial, the buttons for
+ // the Wally and Rex dinosaurs will always restart their speech, instead of
+ // stopping and starting their speech. This was a script bug in the original
+ // game.
+ if (_gameId == GID_SAMNMAX && _roomResource == 59 &&
+ vm.slot[_currentScript].number == 201 && script == 48) {
+ o6_breakHere();
+ }
+
+ // WORKAROUND bug #903223: In Puerto Pollo, if you have Guybrush examine
+ // the church clock, he'll read out the current time. Nice touch, only that
+ // it sounds crap in the german version (and maybe others, too). It seems
+ // the original engine of the german version played just a simple fixed
+ // text in this spot, for the above reason. Since the data files are
+ // unchanged, it must have been an engine hack job. No idea how they did
+ // it exactly, but this here is how we do it :-)
+ if (_gameId == GID_CMI && script == 204 &&
+ _currentRoom == 15 && vm.slot[_currentScript].number == 421 &&
+ _language == Common::DE_DEU) {
+
+ _actorToPrintStrFor = 1;
+ _string[0].loadDefault();
+ actorTalk((const byte *)"/VDSO325/Whoa! Look at the time. Gotta scoot.");
+
+ return;
+ }
+
+ runScript(script, (flags & 1) != 0, (flags & 2) != 0, args);
+}
+
+void ScummEngine_v6::o6_jumpToScript() {
+ int args[25];
+ int script, flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = pop();
+ stopObjectCode();
+ runScript(script, (flags & 1) != 0, (flags & 2) != 0, args);
+}
+
+void ScummEngine_v6::o6_startScriptQuick() {
+ int args[25];
+ int script;
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ runScript(script, 0, 0, args);
+}
+
+void ScummEngine_v6::o6_startScriptQuick2() {
+ int args[25];
+ int script;
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ runScript(script, 0, 1, args);
+}
+
+void ScummEngine_v6::o6_startObject() {
+ int args[25];
+ int script, entryp;
+ int flags;
+ getStackList(args, ARRAYSIZE(args));
+ entryp = pop();
+ script = pop();
+ flags = pop();
+ runObjectScript(script, entryp, (flags & 1) != 0, (flags & 2) != 0, args);
+}
+
+void ScummEngine_v6::o6_startObjectQuick() {
+ int args[25];
+ int script, entryp;
+ getStackList(args, ARRAYSIZE(args));
+ entryp = pop();
+ script = pop();
+ runObjectScript(script, entryp, 0, 1, args);
+}
+
+void ScummEngine_v6::o6_drawObject() {
+ int state = pop();
+ int obj = pop();
+
+ // This is based on disassembly
+ if (state == 0)
+ state = 1;
+
+ setObjectState(obj, state, -1, -1);
+}
+
+void ScummEngine_v6::o6_drawObjectAt() {
+ int y = pop();
+ int x = pop();
+ int obj = pop();
+ setObjectState(obj, 1, x, y);
+}
+
+void ScummEngine_v6::o6_stopObjectCode() {
+ stopObjectCode();
+}
+
+void ScummEngine_v6::o6_endCutscene() {
+ endCutscene();
+}
+
+void ScummEngine_v6::o6_cutscene() {
+ int args[25];
+ getStackList(args, ARRAYSIZE(args));
+ beginCutscene(args);
+}
+
+void ScummEngine_v6::o6_stopMusic() {
+ _sound->stopAllSounds();
+}
+
+void ScummEngine_v6::o6_freezeUnfreeze() {
+ int a = pop();
+
+ if (a)
+ freezeScripts(a);
+ else
+ unfreezeScripts();
+}
+
+void ScummEngine_v6::o6_cursorCommand() {
+ int a, i;
+ int args[16];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0x90: // SO_CURSOR_ON Turn cursor on
+ _cursor.state = 1;
+ verbMouseOver(0);
+ break;
+ case 0x91: // SO_CURSOR_OFF Turn cursor off
+ _cursor.state = 0;
+ verbMouseOver(0);
+ break;
+ case 0x92: // SO_USERPUT_ON
+ _userPut = 1;
+ break;
+ case 0x93: // SO_USERPUT_OFF
+ _userPut = 0;
+ break;
+ case 0x94: // SO_CURSOR_SOFT_ON Turn soft cursor on
+ _cursor.state++;
+ if (_cursor.state > 1)
+ error("Cursor state greater than 1 in script");
+ verbMouseOver(0);
+ break;
+ case 0x95: // SO_CURSOR_SOFT_OFF Turn soft cursor off
+ _cursor.state--;
+ verbMouseOver(0);
+ break;
+ case 0x96: // SO_USERPUT_SOFT_ON
+ _userPut++;
+ break;
+ case 0x97: // SO_USERPUT_SOFT_OFF
+ _userPut--;
+ break;
+ case 0x99: // SO_CURSOR_IMAGE Set cursor image
+ {
+ int room, obj;
+ if (_heversion >= 70) {
+ obj = pop();
+ room = getObjectRoom(obj);
+ } else {
+ obj = popRoomAndObj(&room);
+ }
+ setCursorFromImg(obj, room, 1);
+ break;
+ }
+ case 0x9A: // SO_CURSOR_HOTSPOT Set cursor hotspot
+ a = pop();
+ setCursorHotspot(pop(), a);
+ break;
+ case 0x9C: // SO_CHARSET_SET
+ initCharset(pop());
+ break;
+ case 0x9D: // SO_CHARSET_COLOR
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ break;
+ case 0xD6: // SO_CURSOR_TRANSPARENT Set cursor transparent color
+ setCursorTransparency(pop());
+ break;
+ default:
+ error("o6_cursorCommand: default case %x", subOp);
+ }
+
+ VAR(VAR_CURSORSTATE) = _cursor.state;
+ VAR(VAR_USERPUT) = _userPut;
+}
+
+void ScummEngine_v6::o6_breakHere() {
+ updateScriptPtr();
+ _currentScript = 0xFF;
+}
+
+void ScummEngine_v6::o6_ifClassOfIs() {
+ int args[16];
+ int num, obj, cls;
+ bool b;
+ int cond = 1;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ obj = pop();
+
+ if (_heversion >= 80 && num == 0) {
+ push(_classData[obj]);
+ return;
+ }
+
+ while (--num >= 0) {
+ cls = args[num];
+ b = getClass(obj, cls);
+ if ((cls & 0x80 && !b) || (!(cls & 0x80) && b))
+ cond = 0;
+ }
+ push(cond);
+}
+
+void ScummEngine_v6::o6_setClass() {
+ int args[16];
+ int num, obj, cls;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ obj = pop();
+
+ while (--num >= 0) {
+ cls = args[num];
+ if (cls == 0)
+ _classData[num] = 0;
+ else if (cls & 0x80)
+ putClass(obj, cls, 1);
+ else
+ putClass(obj, cls, 0);
+ }
+}
+
+void ScummEngine_v6::o6_getState() {
+ push(getState(pop()));
+}
+
+void ScummEngine_v6::o6_setState() {
+ int state = pop();
+ int obj = pop();
+
+ putState(obj, state);
+ markObjectRectAsDirty(obj);
+ if (_bgNeedsRedraw)
+ clearDrawObjectQueue();
+}
+
+void ScummEngine_v6::o6_setOwner() {
+ int owner = pop();
+ int obj = pop();
+ setOwnerOf(obj, owner);
+}
+
+void ScummEngine_v6::o6_getOwner() {
+ push(getOwner(pop()));
+}
+
+void ScummEngine_v6::o6_startSound() {
+ int offset = 0;
+
+ // In Fatty Bear's Birthday Surprise the piano uses offsets 1 - 23 to
+ // indicate which note to play, but only when using the standard piano
+ // sound. See also o60_soundOps()
+ if (_heversion >= 60 && (_gameId != GID_PUTTDEMO))
+ offset = pop();
+
+#ifndef DISABLE_SCUMM_7_8
+ if (_features & GF_DIGI_IMUSE)
+ _imuseDigital->startSfx(pop(), 64);
+ else
+#endif
+ _sound->addSoundToQueue(pop(), offset);
+}
+
+void ScummEngine_v6::o6_stopSound() {
+ _sound->stopSound(pop());
+}
+
+void ScummEngine_v6::o6_startMusic() {
+ if (_features & GF_DIGI_IMUSE)
+ error("o6_startMusic() It shouldn't be called here for imuse digital");
+
+ _sound->addSoundToQueue(pop());
+}
+
+void ScummEngine_v6::o6_stopObjectScript() {
+ stopObjectScript(pop());
+}
+
+void ScummEngine_v6::o6_panCameraTo() {
+ if (_version >= 7) {
+ int y = pop();
+ int x = pop();
+ panCameraTo(x, y);
+ } else {
+ panCameraTo(pop(), 0);
+ }
+}
+
+void ScummEngine_v6::o6_actorFollowCamera() {
+ if (_version >= 7)
+ setCameraFollows(derefActor(pop(), "actorFollowCamera"));
+ else
+ actorFollowCamera(pop());
+}
+
+void ScummEngine_v6::o6_setCameraAt() {
+ if (_version >= 7) {
+ int x, y;
+
+ camera._follows = 0;
+ VAR(VAR_CAMERA_FOLLOWED_ACTOR) = 0;
+
+ y = pop();
+ x = pop();
+
+ setCameraAt(x, y);
+ } else {
+ setCameraAtEx(pop());
+ }
+}
+
+void ScummEngine_v6::o6_loadRoom() {
+ int room = pop();
+ startScene(room, 0, 0);
+ if (_heversion >= 61) {
+ setCameraAt(camera._cur.x, 0);
+ }
+ _fullRedraw = true;
+}
+
+void ScummEngine_v6::o6_stopScript() {
+ int script = pop();
+ if (script == 0)
+ stopObjectCode();
+ else
+ stopScript(script);
+}
+
+void ScummEngine_v6::o6_walkActorToObj() {
+ int act, obj, dist;
+ Actor *a, *a2;
+ int x, y;
+
+ dist = pop();
+ obj = pop();
+ act = pop();
+ a = derefActor(act, "o6_walkActorToObj");
+
+ if (obj >= _numActors) {
+ int wio = whereIsObject(obj);
+
+ if (wio != WIO_FLOBJECT && wio != WIO_ROOM)
+ return;
+
+ int dir;
+ getObjectXYPos(obj, x, y, dir);
+ a->startWalkActor(x, y, dir);
+ } else {
+ a2 = derefActorSafe(obj, "o6_walkActorToObj(2)");
+ if (_gameId == GID_SAMNMAX && a2 == 0) {
+ // WORKAROUND bug #742676 SAM: Fish Farm. Note quite sure why it
+ // happens, whether it's normal or due to a bug in the ScummVM code.
+ debug(0, "o6_walkActorToObj: invalid actor %d", obj);
+ return;
+ }
+ if (!a->isInCurrentRoom() || !a2->isInCurrentRoom())
+ return;
+ if (dist == 0) {
+ dist = a2->_scalex * a2->_width / 0xFF;
+ dist += dist / 2;
+ }
+ x = a2->_pos.x;
+ y = a2->_pos.y;
+ if (x < a->_pos.x)
+ x += dist;
+ else
+ x -= dist;
+ a->startWalkActor(x, y, -1);
+ }
+}
+
+void ScummEngine_v6::o6_walkActorTo() {
+ int x, y;
+ y = pop();
+ x = pop();
+ Actor *a = derefActor(pop(), "o6_walkActorTo");
+ a->startWalkActor(x, y, -1);
+}
+
+void ScummEngine_v6::o6_putActorAtXY() {
+ int room, x, y, act;
+ Actor *a;
+
+ room = pop();
+ y = pop();
+ x = pop();
+ act = pop();
+ a = derefActor(act, "o6_putActorAtXY");
+
+ if (room == 0xFF || room == 0x7FFFFFFF) {
+ room = a->_room;
+ } else {
+ if (a->_visible && _currentRoom != room && getTalkingActor() == a->_number) {
+ stopTalk();
+ }
+ if (room != 0)
+ a->_room = room;
+ }
+ a->putActor(x, y, room);
+}
+
+
+void ScummEngine_v6::o6_putActorAtObject() {
+ int room, obj, x, y;
+ Actor *a;
+
+ obj = popRoomAndObj(&room);
+
+ a = derefActor(pop(), "o6_putActorAtObject");
+ if (whereIsObject(obj) != WIO_NOT_FOUND) {
+ getObjectXYPos(obj, x, y);
+ } else {
+ x = 160;
+ y = 120;
+ }
+ if (room == 0xFF)
+ room = a->_room;
+ a->putActor(x, y, room);
+}
+
+void ScummEngine_v6::o6_faceActor() {
+ int obj = pop();
+ Actor *a = derefActor(pop(), "o6_faceActor");
+ a->faceToObject(obj);
+}
+
+void ScummEngine_v6::o6_animateActor() {
+ int anim = pop();
+ int act = pop();
+ if (_gameId == GID_TENTACLE && _roomResource == 57 &&
+ vm.slot[_currentScript].number == 19 && act == 593) {
+ // WORKAROUND bug #743363: This very odd case (animateActor(593,250))
+ // occurs in DOTT, in the cutscene after George cuts down the "cherry
+ // tree" and the tree Laverne is trapped in vanishes...
+ // Not sure if this means animateActor somehow also must work for objects
+ // (593 is the time machine in room 57), or if this is simply a script bug.
+ act = 6;
+ }
+ if (_gameId == GID_SAMNMAX && _roomResource == 35 &&
+ vm.slot[_currentScript].number == 202 && act == 4 && anim == 14) {
+ // WORKAROUND bug #1223621 (Animation glitch at World of Fish).
+ // Before starting animation 14 of the fisherman, make sure he isn't
+ // talking anymore. This appears to be a bug in the original game as well.
+ if (getTalkingActor() == 4) {
+ stopTalk();
+ }
+ }
+ Actor *a = derefActor(act, "o6_animateActor");
+ a->animateActor(anim);
+}
+
+void ScummEngine_v6::o6_doSentence() {
+ int verb, objectA, objectB, dummy = 0;
+
+ objectB = pop();
+ if (_version < 8)
+ dummy = pop(); // dummy pop (in Sam&Max, seems to be always 0 or 130)
+ objectA = pop();
+ verb = pop();
+
+ doSentence(verb, objectA, objectB);
+}
+
+void ScummEngine_v6::o6_pickupObject() {
+ int obj, room;
+ int i;
+
+ obj = popRoomAndObj(&room);
+ if (room == 0)
+ room = _roomResource;
+
+ for (i = 0; i < _numInventory; i++) {
+ if (_inventory[i] == (uint16)obj) {
+ putOwner(obj, VAR(VAR_EGO));
+ runInventoryScript(obj);
+ return;
+ }
+ }
+
+ addObjectToInventory(obj, room);
+ putOwner(obj, VAR(VAR_EGO));
+ putClass(obj, kObjectClassUntouchable, 1);
+ putState(obj, 1);
+ markObjectRectAsDirty(obj);
+ clearDrawObjectQueue();
+ runInventoryScript(obj);
+}
+
+void ScummEngine_v6::o6_loadRoomWithEgo() {
+ Actor *a;
+ int obj, room, x, y;
+
+ y = pop();
+ x = pop();
+
+ obj = popRoomAndObj(&room);
+
+ a = derefActor(VAR(VAR_EGO), "o6_loadRoomWithEgo");
+ a->putActor(0, 0, room);
+ _egoPositioned = false;
+
+ VAR(VAR_WALKTO_OBJ) = obj;
+ startScene(a->_room, a, obj);
+ VAR(VAR_WALKTO_OBJ) = 0;
+
+ if (_version == 6) {
+ camera._cur.x = camera._dest.x = a->_pos.x;
+ setCameraFollows(a);
+ }
+
+ _fullRedraw = true;
+
+ if (x != -1 && x != 0x7FFFFFFF) {
+ a->startWalkActor(x, y, -1);
+ }
+}
+
+void ScummEngine_v6::o6_getRandomNumber() {
+ int rnd;
+ rnd = _rnd.getRandomNumber(pop());
+ if (VAR_RANDOM_NR != 0xFF)
+ VAR(VAR_RANDOM_NR) = rnd;
+ push(rnd);
+}
+
+void ScummEngine_v6::o6_getRandomNumberRange() {
+ int max = pop();
+ int min = pop();
+ int rnd = _rnd.getRandomNumberRng(min, max);
+ if (VAR_RANDOM_NR != 0xFF)
+ VAR(VAR_RANDOM_NR) = rnd;
+ push(rnd);
+}
+
+void ScummEngine_v6::o6_isScriptRunning() {
+ push(isScriptRunning(pop()));
+}
+
+void ScummEngine_v6::o6_isRoomScriptRunning() {
+ push(isRoomScriptRunning(pop()));
+}
+
+void ScummEngine_v6::o6_getActorMoving() {
+ Actor *a = derefActor(pop(), "o6_getActorMoving");
+ push(a->_moving);
+}
+
+void ScummEngine_v6::o6_getActorRoom() {
+ int act = pop();
+
+ if (act == 0) {
+ // This case occurs at the very least in COMI. That's because in COMI's script 28,
+ // there is a check which looks as follows:
+ // if (((VAR_TALK_ACTOR != 0) && (VAR_HAVE_MSG == 1)) &&
+ // (getActorRoom(VAR_TALK_ACTOR) == VAR_ROOM))
+ // Due to the way this is represented in bytecode, the engine cannot
+ // short circuit. Hence, even though this would be perfectly fine code
+ // in C/C++, here it can (and does) lead to getActorRoom(0) being
+ // invoked. We silently ignore this.
+ push(0);
+ return;
+ }
+
+ if (act == 255) {
+ // This case also occurs in COMI...
+ push(0);
+ return;
+ }
+
+ Actor *a = derefActor(act, "o6_getActorRoom");
+ push(a->_room);
+}
+
+void ScummEngine_v6::o6_getActorWalkBox() {
+ Actor *a = derefActor(pop(), "o6_getActorWalkBox");
+ push(a->_ignoreBoxes ? 0 : a->_walkbox);
+}
+
+void ScummEngine_v6::o6_getActorCostume() {
+ Actor *a = derefActor(pop(), "o6_getActorCostume");
+ push(a->_costume);
+}
+
+void ScummEngine_v6::o6_getActorElevation() {
+ Actor *a = derefActor(pop(), "o6_getActorElevation");
+ push(a->getElevation());
+}
+
+void ScummEngine_v6::o6_getActorWidth() {
+ Actor *a = derefActor(pop(), "o6_getActorWidth");
+ push(a->_width);
+}
+
+void ScummEngine_v6::o6_getActorScaleX() {
+ Actor *a = derefActor(pop(), "o6_getActorScale");
+ push(a->_scalex);
+}
+
+void ScummEngine_v6::o6_getActorAnimCounter1() {
+ Actor *a = derefActor(pop(), "o6_getActorAnimCounter");
+ push(a->_cost.animCounter);
+}
+
+void ScummEngine_v6::o6_getAnimateVariable() {
+ int var = pop();
+ Actor *a = derefActor(pop(), "o6_getAnimateVariable");
+ push(a->getAnimVar(var));
+}
+
+void ScummEngine_v6::o6_isActorInBox() {
+ int box = pop();
+ Actor *a = derefActor(pop(), "o6_isActorInBox");
+ push(checkXYInBoxBounds(box, a->_pos.x, a->_pos.y));
+}
+
+void ScummEngine_v6::o6_getActorLayer() {
+ Actor *a = derefActor(pop(), "getActorLayer");
+ push(a->_layer);
+}
+
+void ScummEngine_v6::o6_getObjectX() {
+ push(getObjX(pop()));
+}
+
+void ScummEngine_v6::o6_getObjectY() {
+ push(getObjY(pop()));
+}
+
+void ScummEngine_v6::o6_getObjectOldDir() {
+ push(getObjOldDir(pop()));
+}
+
+void ScummEngine_v6::o6_getObjectNewDir() {
+ push(getObjNewDir(pop()));
+}
+
+void ScummEngine_v6::o6_findInventory() {
+ int idx = pop();
+ int owner = pop();
+ push(findInventory(owner, idx));
+}
+
+void ScummEngine_v6::o6_getInventoryCount() {
+ push(getInventoryCount(pop()));
+}
+
+void ScummEngine_v6::o6_getVerbFromXY() {
+ int y = pop();
+ int x = pop();
+ int over = findVerbAtPos(x, y);
+ if (over)
+ over = _verbs[over].verbid;
+ push(over);
+}
+
+void ScummEngine_v6::o6_beginOverride() {
+ beginOverride();
+}
+
+void ScummEngine_v6::o6_endOverride() {
+ endOverride();
+}
+
+void ScummEngine_v6::o6_setObjectName() {
+ int obj = pop();
+ setObjectName(obj);
+}
+
+void ScummEngine_v6::o6_isSoundRunning() {
+ int snd = pop();
+
+ if (snd)
+ snd = _sound->isSoundRunning(snd);
+
+ push(snd);
+}
+
+void ScummEngine_v6::o6_setBoxFlags() {
+ int table[65];
+ int num, value;
+
+ value = pop();
+ num = getStackList(table, ARRAYSIZE(table));
+
+ while (--num >= 0) {
+ setBoxFlags(table[num], value);
+ }
+}
+
+void ScummEngine_v6::o6_createBoxMatrix() {
+ createBoxMatrix();
+
+ if ((_gameId == GID_DIG) || (_gameId == GID_CMI))
+ putActors();
+}
+
+void ScummEngine_v6::o6_resourceRoutines() {
+ int resid;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 100: // SO_LOAD_SCRIPT
+ resid = pop();
+ if (_version >= 7)
+ if (resid >= _numGlobalScripts)
+ break;
+ ensureResourceLoaded(rtScript, resid);
+ break;
+ case 101: // SO_LOAD_SOUND
+ resid = pop();
+ ensureResourceLoaded(rtSound, resid);
+ break;
+ case 102: // SO_LOAD_COSTUME
+ resid = pop();
+ ensureResourceLoaded(rtCostume, resid);
+ break;
+ case 103: // SO_LOAD_ROOM
+ resid = pop();
+ ensureResourceLoaded(rtRoom, resid);
+ break;
+ case 104: // SO_NUKE_SCRIPT
+ resid = pop();
+ if (_version >= 7)
+ if (resid >= _numGlobalScripts)
+ break;
+ res.setResourceCounter(rtScript, resid, 0x7F);
+ break;
+ case 105: // SO_NUKE_SOUND
+ resid = pop();
+ res.setResourceCounter(rtSound, resid, 0x7F);
+ break;
+ case 106: // SO_NUKE_COSTUME
+ resid = pop();
+ res.setResourceCounter(rtCostume, resid, 0x7F);
+ break;
+ case 107: // SO_NUKE_ROOM
+ resid = pop();
+ res.setResourceCounter(rtRoom, resid, 0x7F);
+ break;
+ case 108: // SO_LOCK_SCRIPT
+ resid = pop();
+ if (resid >= _numGlobalScripts)
+ break;
+ res.lock(rtScript, resid);
+ break;
+ case 109: // SO_LOCK_SOUND
+ resid = pop();
+ res.lock(rtSound, resid);
+ break;
+ case 110: // SO_LOCK_COSTUME
+ resid = pop();
+ res.lock(rtCostume, resid);
+ break;
+ case 111: // SO_LOCK_ROOM
+ resid = pop();
+ if (resid > 0x7F)
+ resid = _resourceMapper[resid & 0x7F];
+ res.lock(rtRoom, resid);
+ break;
+ case 112: // SO_UNLOCK_SCRIPT
+ resid = pop();
+ if (resid >= _numGlobalScripts)
+ break;
+ res.unlock(rtScript, resid);
+ break;
+ case 113: // SO_UNLOCK_SOUND
+ resid = pop();
+ res.unlock(rtSound, resid);
+ break;
+ case 114: // SO_UNLOCK_COSTUME
+ resid = pop();
+ res.unlock(rtCostume, resid);
+ break;
+ case 115: // SO_UNLOCK_ROOM
+ resid = pop();
+ if (resid > 0x7F)
+ resid = _resourceMapper[resid & 0x7F];
+ res.unlock(rtRoom, resid);
+ break;
+ case 116: // SO_CLEAR_HEAP
+ /* this is actually a scumm message */
+ error("clear heap not working yet");
+ break;
+ case 117: // SO_LOAD_CHARSET
+ resid = pop();
+ loadCharset(resid);
+ break;
+ case 118: // SO_NUKE_CHARSET
+ resid = pop();
+ nukeCharset(resid);
+ break;
+ case 119: // SO_LOAD_OBJECT
+ {
+ int room, obj = popRoomAndObj(&room);
+ loadFlObject(obj, room);
+ break;
+ }
+ default:
+ error("o6_resourceRoutines: default case %d", subOp);
+ }
+}
+
+
+void ScummEngine_v6::o6_roomOps() {
+ int a, b, c, d, e;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 172: // SO_ROOM_SCROLL
+ b = pop();
+ a = pop();
+ if (a < (_screenWidth / 2))
+ a = (_screenWidth / 2);
+ if (b < (_screenWidth / 2))
+ b = (_screenWidth / 2);
+ if (a > _roomWidth - (_screenWidth / 2))
+ a = _roomWidth - (_screenWidth / 2);
+ if (b > _roomWidth - (_screenWidth / 2))
+ b = _roomWidth - (_screenWidth / 2);
+ VAR(VAR_CAMERA_MIN_X) = a;
+ VAR(VAR_CAMERA_MAX_X) = b;
+ break;
+
+ case 174: // SO_ROOM_SCREEN
+ b = pop();
+ a = pop();
+ initScreens(a, b);
+ break;
+
+ case 175: // SO_ROOM_PALETTE
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ setPalColor(d, a, b, c);
+ break;
+
+ case 176: // SO_ROOM_SHAKE_ON
+ setShake(1);
+ break;
+
+ case 177: // SO_ROOM_SHAKE_OFF
+ setShake(0);
+ break;
+
+ case 179: // SO_ROOM_INTENSITY
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, a, a, b, c);
+ break;
+
+ case 180: // SO_ROOM_SAVEGAME
+ _saveTemporaryState = true;
+ _saveLoadSlot = pop();
+ _saveLoadFlag = pop();
+ if (_gameId == GID_TENTACLE)
+ _saveSound = (_saveLoadSlot != 0);
+ break;
+
+ case 181: // SO_ROOM_FADE
+ a = pop();
+ if (a) {
+ _switchRoomEffect = (byte)(a & 0xFF);
+ _switchRoomEffect2 = (byte)(a >> 8);
+ } else {
+ fadeIn(_newEffect);
+ }
+ break;
+
+ case 182: // SO_RGB_ROOM_INTENSITY
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, b, c, d, e);
+ break;
+
+ case 183: // SO_ROOM_SHADOW
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ setupShadowPalette(a, b, c, d, e, 0, 256);
+ break;
+
+ case 184: // SO_SAVE_STRING
+ error("save string not implemented");
+ break;
+
+ case 185: // SO_LOAD_STRING
+ error("load string not implemented");
+ break;
+
+ case 186: // SO_ROOM_TRANSFORM
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ palManipulateInit(a, b, c, d);
+ break;
+
+ case 187: // SO_CYCLE_SPEED
+ b = pop();
+ a = pop();
+ checkRange(16, 1, a, "o6_roomOps: 187: color cycle out of range (%d)");
+ _colorCycle[a - 1].delay = (b != 0) ? 0x4000 / (b * 0x4C) : 0;
+ break;
+
+ case 213: // SO_ROOM_NEW_PALETTE
+ a = pop();
+
+ // This opcode is used when turning off noir mode in Sam & Max,
+ // but since our implementation of this feature doesn't change
+ // the original palette there's no need to reload it. Doing it
+ // this way, we avoid some graphics glitches that the original
+ // interpreter had.
+
+ if (_gameId == GID_SAMNMAX && vm.slot[_currentScript].number == 64)
+ setDirtyColors(0, 255);
+ else
+ setPalette(a);
+ break;
+ default:
+ error("o6_roomOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v6::o6_actorOps() {
+ Actor *a;
+ int i, j, k;
+ int args[8];
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 197) {
+ _curActor = pop();
+ return;
+ }
+
+ a = derefActorSafe(_curActor, "o6_actorOps");
+ if (!a)
+ return;
+
+ switch (subOp) {
+ case 76: // SO_COSTUME
+ a->setActorCostume(pop());
+ break;
+ case 77: // SO_STEP_DIST
+ j = pop();
+ i = pop();
+ a->setActorWalkSpeed(i, j);
+ break;
+ case 78: // SO_SOUND
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; i++)
+ a->_sound[i] = args[i];
+ break;
+ case 79: // SO_WALK_ANIMATION
+ a->_walkFrame = pop();
+ break;
+ case 80: // SO_TALK_ANIMATION
+ a->_talkStopFrame = pop();
+ a->_talkStartFrame = pop();
+ break;
+ case 81: // SO_STAND_ANIMATION
+ a->_standFrame = pop();
+ break;
+ case 82: // SO_ANIMATION
+ // dummy case in scumm6
+ pop();
+ pop();
+ pop();
+ break;
+ case 83: // SO_DEFAULT
+ a->initActor(0);
+ break;
+ case 84: // SO_ELEVATION
+ a->setElevation(pop());
+ break;
+ case 85: // SO_ANIMATION_DEFAULT
+ a->_initFrame = 1;
+ a->_walkFrame = 2;
+ a->_standFrame = 3;
+ a->_talkStartFrame = 4;
+ a->_talkStopFrame = 5;
+ break;
+ case 86: // SO_PALETTE
+ j = pop();
+ i = pop();
+ checkRange(255, 0, i, "Illegal palette slot %d");
+ a->setPalette(i, j);
+ break;
+ case 87: // SO_TALK_COLOR
+ a->_talkColor = pop();
+ break;
+ case 88: // SO_ACTOR_NAME
+ loadPtrToResource(rtActorName, a->_number, NULL);
+ break;
+ case 89: // SO_INIT_ANIMATION
+ a->_initFrame = pop();
+ break;
+ case 91: // SO_ACTOR_WIDTH
+ a->_width = pop();
+ break;
+ case 92: // SO_SCALE
+ i = pop();
+ a->setScale(i, i);
+ break;
+ case 93: // SO_NEVER_ZCLIP
+ a->_forceClip = 0;
+ break;
+ case 225: // SO_ALWAYS_ZCLIP
+ case 94: // SO_ALWAYS_ZCLIP
+ a->_forceClip = pop();
+ break;
+ case 95: // SO_IGNORE_BOXES
+ a->_ignoreBoxes = 1;
+ a->_forceClip = (_version >= 7) ? 100 : 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 96: // SO_FOLLOW_BOXES
+ a->_ignoreBoxes = 0;
+ a->_forceClip = (_version >= 7) ? 100 : 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 97: // SO_ANIMATION_SPEED
+ a->setAnimSpeed(pop());
+ break;
+ case 98: // SO_SHADOW
+ a->_shadowMode = pop();
+ break;
+ case 99: // SO_TEXT_OFFSET
+ a->_talkPosY = pop();
+ a->_talkPosX = pop();
+ break;
+ case 198: // SO_ACTOR_VARIABLE
+ i = pop();
+ a->setAnimVar(pop(), i);
+ break;
+ case 215: // SO_ACTOR_IGNORE_TURNS_ON
+ a->_ignoreTurns = true;
+ break;
+ case 216: // SO_ACTOR_IGNORE_TURNS_OFF
+ a->_ignoreTurns = false;
+ break;
+ case 217: // SO_ACTOR_NEW
+ a->initActor(2);
+ break;
+ case 227: // SO_ACTOR_DEPTH
+ a->_layer = pop();
+ break;
+ case 228: // SO_ACTOR_WALK_SCRIPT
+ a->_walkScript = pop();
+ break;
+ case 229: // SO_ACTOR_STOP
+ a->stopActorMoving();
+ a->startAnimActor(a->_standFrame);
+ break;
+ case 230: /* set direction */
+ a->_moving &= ~MF_TURN;
+ a->setDirection(pop());
+ break;
+ case 231: /* turn to direction */
+ a->turnToDirection(pop());
+ break;
+ case 233: // SO_ACTOR_WALK_PAUSE
+ a->_moving |= MF_FROZEN;
+ break;
+ case 234: // SO_ACTOR_WALK_RESUME
+ a->_moving &= ~MF_FROZEN;
+ break;
+ case 235: // SO_ACTOR_TALK_SCRIPT
+ a->_talkScript = pop();
+ break;
+ default:
+ error("o6_actorOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v6::o6_verbOps() {
+ int slot, a, b;
+ VerbSlot *vs;
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 196) {
+ _curVerb = pop();
+ _curVerbSlot = getVerbSlot(_curVerb, 0);
+ checkRange(_numVerbs - 1, 0, _curVerbSlot, "Illegal new verb slot %d");
+ return;
+ }
+ vs = &_verbs[_curVerbSlot];
+ slot = _curVerbSlot;
+ switch (subOp) {
+ case 124: // SO_VERB_IMAGE
+ a = pop();
+ if (_curVerbSlot) {
+ setVerbObject(_roomResource, a, slot);
+ vs->type = kImageVerbType;
+ if (_heversion >= 61)
+ vs->imgindex = a;
+ }
+ break;
+ case 125: // SO_VERB_NAME
+ loadPtrToResource(rtVerb, slot, NULL);
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 126: // SO_VERB_COLOR
+ vs->color = pop();
+ break;
+ case 127: // SO_VERB_HICOLOR
+ vs->hicolor = pop();
+ break;
+ case 128: // SO_VERB_AT
+ vs->curRect.top = pop();
+ vs->curRect.left = pop();
+ break;
+ case 129: // SO_VERB_ON
+ vs->curmode = 1;
+ break;
+ case 130: // SO_VERB_OFF
+ vs->curmode = 0;
+ break;
+ case 131: // SO_VERB_DELETE
+ if (_heversion >= 60) {
+ slot = getVerbSlot(pop(), 0);
+ }
+ killVerb(slot);
+ break;
+ case 132: // SO_VERB_NEW
+ slot = getVerbSlot(_curVerb, 0);
+ if (slot == 0) {
+ for (slot = 1; slot < _numVerbs; slot++) {
+ if (_verbs[slot].verbid == 0)
+ break;
+ }
+ if (slot == _numVerbs)
+ error("Too many verbs");
+ _curVerbSlot = slot;
+ }
+ vs = &_verbs[slot];
+ vs->verbid = _curVerb;
+ vs->color = 2;
+ vs->hicolor = 0;
+ vs->dimcolor = 8;
+ vs->type = kTextVerbType;
+ vs->charset_nr = _string[0]._default.charset;
+ vs->curmode = 0;
+ vs->saveid = 0;
+ vs->key = 0;
+ vs->center = 0;
+ vs->imgindex = 0;
+ break;
+ case 133: // SO_VERB_DIMCOLOR
+ vs->dimcolor = pop();
+ break;
+ case 134: // SO_VERB_DIM
+ vs->curmode = 2;
+ break;
+ case 135: // SO_VERB_KEY
+ vs->key = pop();
+ break;
+ case 136: // SO_VERB_CENTER
+ vs->center = 1;
+ break;
+ case 137: // SO_VERB_NAME_STR
+ a = pop();
+ if (a == 0) {
+ loadPtrToResource(rtVerb, slot, (const byte *)"");
+ } else {
+ loadPtrToResource(rtVerb, slot, getStringAddress(a));
+ }
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 139: // SO_VERB_IMAGE_IN_ROOM
+ b = pop();
+ a = pop();
+
+ if (slot && a != vs->imgindex) {
+ setVerbObject(b, a, slot);
+ vs->type = kImageVerbType;
+ vs->imgindex = a;
+ }
+ break;
+ case 140: // SO_VERB_BAKCOLOR
+ vs->bkcolor = pop();
+ break;
+ case 255:
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+ break;
+ default:
+ error("o6_verbops: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v6::o6_getActorFromXY() {
+ int y = pop();
+ int x = pop();
+ int r = getActorFromPos(x, y);
+ push(r);
+}
+
+void ScummEngine_v6::o6_findObject() {
+ int y = pop();
+ int x = pop();
+ int r = findObject(x, y);
+ push(r);
+}
+
+void ScummEngine_v6::o6_pseudoRoom() {
+ int list[100];
+ int num, a, value;
+
+ num = getStackList(list, ARRAYSIZE(list));
+ value = pop();
+
+ while (--num >= 0) {
+ a = list[num];
+ if (a > 0x7F)
+ _resourceMapper[a & 0x7F] = value;
+ }
+}
+
+void ScummEngine_v6::o6_getVerbEntrypoint() {
+ int e = pop();
+ int v = pop();
+ push(getVerbEntrypoint(v, e));
+}
+
+void ScummEngine_v6::o6_arrayOps() {
+ byte subOp = fetchScriptByte();
+ int array = fetchScriptWord();
+ int b, c, d, len;
+ ArrayHeader *ah;
+ int list[128];
+
+ switch (subOp) {
+ case 205: // SO_ASSIGN_STRING
+ b = pop();
+ len = resStrLen(_scriptPointer);
+ ah = defineArray(array, kStringArray, 0, len + 1);
+ copyScriptString(ah->data + b);
+ break;
+ case 208: // SO_ASSIGN_INT_LIST
+ b = pop();
+ c = pop();
+ d = readVar(array);
+ if (d == 0) {
+ defineArray(array, kIntArray, 0, b + c);
+ }
+ while (c--) {
+ writeArray(array, 0, b + c, pop());
+ }
+ break;
+ case 212: // SO_ASSIGN_2DIM_LIST
+ b = pop();
+ len = getStackList(list, ARRAYSIZE(list));
+ d = readVar(array);
+ if (d == 0)
+ error("Must DIM a two dimensional array before assigning");
+ c = pop();
+ while (--len >= 0) {
+ writeArray(array, c, b + len, list[len]);
+ }
+ break;
+ default:
+ error("o6_arrayOps: default case %d (array %d)", subOp, array);
+ }
+}
+
+void ScummEngine_v6::o6_saveRestoreVerbs() {
+ int a, b, c;
+ int slot, slot2;
+
+ c = pop();
+ b = pop();
+ a = pop();
+
+ byte subOp = fetchScriptByte();
+ if (_version == 8) {
+ subOp = (subOp - 141) + 0xB4;
+ }
+
+ switch (subOp) {
+ case 141: // SO_SAVE_VERBS
+ while (a <= b) {
+ slot = getVerbSlot(a, 0);
+ if (slot && _verbs[slot].saveid == 0) {
+ _verbs[slot].saveid = c;
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+ }
+ a++;
+ }
+ break;
+ case 142: // SO_RESTORE_VERBS
+ while (a <= b) {
+ slot = getVerbSlot(a, c);
+ if (slot) {
+ slot2 = getVerbSlot(a, 0);
+ if (slot2)
+ killVerb(slot2);
+ slot = getVerbSlot(a, c);
+ _verbs[slot].saveid = 0;
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+ }
+ a++;
+ }
+ break;
+ case 143: // SO_DELETE_VERBS
+ while (a <= b) {
+ slot = getVerbSlot(a, c);
+ if (slot)
+ killVerb(slot);
+ a++;
+ }
+ break;
+ default:
+ error("o6_saveRestoreVerbs: default case");
+ }
+}
+
+void ScummEngine_v6::o6_drawBox() {
+ int x, y, x2, y2, color;
+ color = pop();
+ y2 = pop();
+ x2 = pop();
+ y = pop();
+ x = pop();
+ drawBox(x, y, x2, y2, color);
+}
+
+void ScummEngine_v6::o6_wait() {
+ int actnum;
+ int offs = -2;
+ Actor *a;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 168: // SO_WAIT_FOR_ACTOR Wait for actor
+ offs = fetchScriptWordSigned();
+ actnum = pop();
+ a = derefActor(actnum, "o6_wait:168");
+ if (a->isInCurrentRoom() && a->_moving)
+ break;
+ return;
+ case 169: // SO_WAIT_FOR_MESSAGE Wait for message
+ if (VAR(VAR_HAVE_MSG))
+ break;
+ return;
+ case 170: // SO_WAIT_FOR_CAMERA Wait for camera
+ if (_version >= 7) {
+ if (camera._dest != camera._cur)
+ break;
+ } else {
+ if (camera._cur.x / 8 != camera._dest.x / 8)
+ break;
+ }
+
+ return;
+ case 171: // SO_WAIT_FOR_SENTENCE
+ if (_sentenceNum) {
+ if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ }
+ if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ case 226: // SO_WAIT_FOR_ANIMATION
+ offs = fetchScriptWordSigned();
+ actnum = pop();
+ a = derefActor(actnum, "o6_wait:226");
+ if (a->isInCurrentRoom() && a->_needRedraw)
+ break;
+ return;
+ case 232: // SO_WAIT_FOR_TURN
+ // FIXME: This opcode is really odd. It's used a lot in The Dig.
+ // But sometimes it receives the actor ID as params, and sometimes an
+ // angle. However in (almost?) all cases, just before calling it, _curActor
+ // is set, so we can use it. I tried to add code that detects if an angle
+ // is passed, and if so, wait till that angle is reached, but that leads to hangs.
+ // It would be very good if somebody could disassmble the original code
+ // for this opcode so that we could figure out what's really going on here.
+ //
+ // For now, if the value passed in is divisible by 45, assume it is an
+ // angle, and use _curActor as the actor to wait for.
+ offs = fetchScriptWordSigned();
+ actnum = pop();
+ if (actnum % 45 == 0) {
+ actnum = _curActor;
+ }
+ a = derefActor(actnum, "o6_wait:232b");
+ if (a->isInCurrentRoom() && a->_moving & MF_TURN)
+ break;
+ return;
+ default:
+ error("o6_wait: default case 0x%x", subOp);
+ }
+
+ _scriptPointer += offs;
+ o6_breakHere();
+}
+
+void ScummEngine_v6::o6_soundKludge() {
+ int list[16];
+ int num = getStackList(list, ARRAYSIZE(list));
+
+ _sound->soundKludge(list, num);
+
+ // WORKAROUND for bug #1398195: The room-11-2016 script contains a
+ // slight bug causing it to busy-wait for a sound to finish. Even under
+ // the best of circumstances, this will cause the game to hang briefly.
+ // On platforms where threading is cooperative, it will cause the game
+ // to hang indefinitely. We identify the buggy part of the script by
+ // looking for a soundKludge() opcode immediately followed by a jump.
+
+ if (_gameId == GID_CMI && _roomResource == 11 && vm.slot[_currentScript].number == 2016 && *_scriptPointer == 0x66) {
+ debug(3, "Working around script bug in room-11-2016");
+ o6_breakHere();
+ }
+}
+
+void ScummEngine_v6::o6_isAnyOf() {
+ int list[100];
+ int num;
+ int32 val;
+
+ num = getStackList(list, ARRAYSIZE(list));
+ val = pop();
+
+ while (--num >= 0) {
+ if (list[num] == val) {
+ push(1);
+ return;
+ }
+ }
+
+ push(0);
+}
+
+void ScummEngine_v6::o6_systemOps() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 158: // SO_RESTART
+ restart();
+ break;
+ case 159: // SO_PAUSE
+ pauseGame();
+ break;
+ case 160: // SO_QUIT
+ shutDown();
+ break;
+ default:
+ error("o6_systemOps invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v6::o6_delay() {
+ uint32 delay = (uint16)pop();
+ vm.slot[_currentScript].delay = delay;
+ vm.slot[_currentScript].status = ssPaused;
+ o6_breakHere();
+}
+
+void ScummEngine_v6::o6_delaySeconds() {
+ uint32 delay = (uint32)pop();
+ delay = delay * 60;
+ vm.slot[_currentScript].delay = delay;
+ vm.slot[_currentScript].status = ssPaused;
+ o6_breakHere();
+}
+
+void ScummEngine_v6::o6_delayMinutes() {
+ uint32 delay = (uint16)pop() * 3600;
+ vm.slot[_currentScript].delay = delay;
+ vm.slot[_currentScript].status = ssPaused;
+ o6_breakHere();
+}
+
+void ScummEngine_v6::o6_stopSentence() {
+ _sentenceNum = 0;
+ stopScript(VAR(VAR_SENTENCE_SCRIPT));
+ clearClickedStatus();
+}
+
+void ScummEngine_v6::o6_printLine() {
+ _actorToPrintStrFor = 0xFF;
+ decodeParseString(0, 0);
+}
+
+void ScummEngine_v6::o6_printText() {
+ decodeParseString(1, 0);
+}
+
+void ScummEngine_v6::o6_printDebug() {
+ decodeParseString(2, 0);
+}
+
+void ScummEngine_v6::o6_printSystem() {
+ decodeParseString(3, 0);
+}
+
+void ScummEngine_v6::o6_printActor() {
+ decodeParseString(0, 1);
+}
+
+void ScummEngine_v6::o6_printEgo() {
+ push(VAR(VAR_EGO));
+ decodeParseString(0, 1);
+}
+
+void ScummEngine_v6::o6_talkActor() {
+ _actorToPrintStrFor = pop();
+
+ _string[0].loadDefault();
+ actorTalk(_scriptPointer);
+
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+}
+
+void ScummEngine_v6::o6_talkEgo() {
+ push(VAR(VAR_EGO));
+ o6_talkActor();
+}
+
+void ScummEngine_v6::o6_dimArray() {
+ int data;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 199: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 200: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 201: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 202: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 203: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ case 204: // SO_UNDIM_ARRAY
+ nukeArray(fetchScriptWord());
+ return;
+ default:
+ error("o6_dimArray: default case %d", subOp);
+ }
+
+ defineArray(fetchScriptWord(), data, 0, pop());
+}
+
+void ScummEngine_v6::o6_dummy() {
+ if (_heversion >= 60) {
+ stopObjectCode();
+ }
+}
+
+void ScummEngine_v6::o6_dim2dimArray() {
+ int a, b, data;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 199: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 200: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 201: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 202: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 203: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ default:
+ error("o6_dim2dimArray: default case %d", subOp);
+ }
+
+ b = pop();
+ a = pop();
+ defineArray(fetchScriptWord(), data, a, b);
+}
+
+void ScummEngine_v6::o6_abs() {
+ int a = pop();
+ push(ABS(a));
+}
+
+void ScummEngine_v6::o6_distObjectObject() {
+ int a, b;
+ b = pop();
+ a = pop();
+ push(getDistanceBetween(true, a, 0, true, b, 0));
+}
+
+void ScummEngine_v6::o6_distObjectPt() {
+ int a, b, c;
+ c = pop();
+ b = pop();
+ a = pop();
+ push(getDistanceBetween(true, a, 0, false, b, c));
+}
+
+void ScummEngine_v6::o6_distPtPt() {
+ int a, b, c, d;
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ push(getDistanceBetween(false, a, b, false, c, d));
+}
+
+void ScummEngine_v6::o6_drawBlastObject() {
+ int args[16];
+ int a, b, c, d, e;
+
+ getStackList(args, ARRAYSIZE(args));
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ enqueueObject(a, b, c, d, e, 0xFF, 0xFF, 1, 0);
+}
+
+// Set BOMP processing window
+void ScummEngine_v6::o6_setBlastObjectWindow() {
+ pop();
+ pop();
+ pop();
+ pop();
+
+ // None of the scripts of The Dig and Full Throttle use this opcode.
+ // Sam & Max only uses it at the beginning of the highway subgame. In
+ // the original interpreter pop'ed arguments are just ignored and the
+ // clipping blastObject window is defined with (0, 0, 320, 200)...
+ // which matches the screen dimensions and thus, doesn't require
+ // another clipping operation.
+ // So, we just handle this as no-op opcode.
+}
+
+void ScummEngine_v6::o6_kernelSetFunctions() {
+ int args[30];
+ int num;
+ Actor *a;
+
+ num = getStackList(args, ARRAYSIZE(args));
+
+ if (_version >= 7) {
+ switch (args[0]) {
+ case 4:
+ grabCursor(args[1], args[2], args[3], args[4]);
+ break;
+#ifndef DISABLE_SCUMM_7_8
+ case 6: {
+ if (_smushFrameRate == 0)
+ _smushFrameRate = 14;
+
+ // SMUSH movie playback
+ if (args[1] == 0) {
+ assert(getStringAddressVar(VAR_VIDEONAME));
+ if (strcmp((char *)getStringAddressVar(VAR_VIDEONAME), "sq3.san") == 0)
+ _smushFrameRate = 14;
+
+ SmushPlayer *sp = new SmushPlayer(this, _smushFrameRate);
+
+ // Correct incorrect smush filename in Macintosh FT demo
+ if ((_gameId == GID_FT) && (_features & GF_DEMO) && (_platform == Common::kPlatformMacintosh) &&
+ (strcmp((char *)getStringAddressVar(VAR_VIDEONAME), "jumpgorge.san") == 0))
+ sp->play("jumpgorg.san");
+ else
+ sp->play((char *)getStringAddressVar(VAR_VIDEONAME));
+ delete sp;
+ } else if (_gameId == GID_FT) {
+ const int insaneVarNum = ((_features & GF_DEMO) && (_platform == Common::kPlatformPC))
+ ? 232 : 233;
+
+ _insaneRunning = true;
+ _insane->setSmushParams(_smushFrameRate);
+ _insane->runScene(insaneVarNum);
+ _insaneRunning = false;
+ }
+ }
+ break;
+#endif
+ case 12:
+ setCursorFromImg(args[1], (uint) - 1, args[2]);
+ break;
+ case 13:
+ derefActor(args[1], "o6_kernelSetFunctions:13")->remapActorPalette(args[2], args[3], args[4], -1);
+ break;
+ case 14:
+ derefActor(args[1], "o6_kernelSetFunctions:14")->remapActorPalette(args[2], args[3], args[4], args[5]);
+ break;
+ case 15:
+ _smushFrameRate = args[1];
+ break;
+ case 16:
+ case 17:
+ enqueueText(getStringAddressVar(VAR_STRING2DRAW), args[3], args[4], args[2], args[1], (args[0] == 16));
+ break;
+ case 20:
+ // it's used for turn on/off 'RadioChatter' effect for voice in the dig, but i's not needed
+ break;
+ case 107:
+ a = derefActor(args[1], "o6_kernelSetFunctions: 107");
+ a->setScale((unsigned char)args[2], -1);
+ break;
+ case 108:
+ setupShadowPalette(args[1], args[2], args[3], args[4], args[5], args[6]);
+ break;
+ case 109:
+ setupShadowPalette(0, args[1], args[2], args[3], args[4], args[5]);
+ break;
+ case 114:
+ error("o6_kernelSetFunctions: stub114()");
+ break;
+ case 117:
+ freezeScripts(2);
+ break;
+ case 118:
+ enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 3);
+ break;
+ case 119:
+ enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0);
+ break;
+ case 124:
+ _saveSound = args[1];
+ break;
+ case 215:
+ ConfMan.set("subtitles", args[1] != 0);
+ break;
+ default:
+ error("o6_kernelSetFunctions: default case %d (param count %d)", args[0], num);
+ break;
+ }
+ } else {
+ switch (args[0]) {
+ case 3:
+ // Dummy case
+ break;
+ case 4:
+ grabCursor(args[1], args[2], args[3], args[4]);
+ break;
+ case 5:
+ fadeOut(args[1]);
+ break;
+ case 6:
+ _fullRedraw = true;
+ redrawBGAreas();
+ setActorRedrawFlags();
+ processActors();
+ fadeIn(args[1]);
+ break;
+ case 8:
+ startManiac();
+ break;
+ case 9:
+ killAllScriptsExceptCurrent();
+ break;
+ case 104: /* samnmax */
+ nukeFlObjects(args[2], args[3]);
+ break;
+ case 107: /* set actor scale */
+ a = derefActor(args[1], "o6_kernelSetFunctions: 107");
+ a->setScale((unsigned char)args[2], -1);
+ break;
+ case 108: /* create proc_special_palette */
+ case 109:
+ // Case 108 and 109 share the same function
+ if (num != 6)
+ error("o6_kernelSetFunctions sub op %d: expected 6 params but got %d", args[0], num);
+ setupShadowPalette(args[3], args[4], args[5], args[1], args[2], 0, 256);
+ break;
+ case 110:
+ _charset->clearCharsetMask();
+ break;
+ case 111:
+ a = derefActor(args[1], "o6_kernelSetFunctions: 111");
+ a->_shadowMode = args[2] + args[3];
+ break;
+ case 112: /* palette shift? */
+ setupShadowPalette(args[3], args[4], args[5], args[1], args[2], args[6], args[7]);
+ break;
+ case 114:
+ // Sam & Max film noir mode
+ if (_gameId == GID_SAMNMAX) {
+ // At this point ScummVM will already have set
+ // variable 0x8000 to indicate that the game is
+ // in film noir mode. All we have to do here is
+ // to mark the palette as "dirty", because
+ // updatePalette() will desaturate the colors
+ // as they are uploaded to the backend.
+ //
+ // This actually works better than the original
+ // interpreter, where actors would sometimes
+ // still be drawn in color.
+ setDirtyColors(0, 255);
+ } else
+ error("stub o6_kernelSetFunctions_114()");
+ break;
+ case 117:
+ // Sam & Max uses this opcode in script-43, right
+ // before a screensaver is selected.
+ //
+ // Sam & Max uses variable 132 to specify the number of
+ // minutes of inactivity (no mouse movements) before
+ // starting the screensaver, so setting it to 0 will
+ // help in debugging.
+ freezeScripts(0x80);
+ break;
+ case 119:
+ enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0);
+ break;
+ case 120:
+ swapPalColors(args[1], args[2]);
+ break;
+ case 122:
+ VAR(VAR_SOUNDRESULT) =
+ (short)_imuse->doCommand (num - 1, &args[1]);
+ break;
+ case 123:
+ copyPalColor(args[2], args[1]);
+ break;
+ case 124:
+ _saveSound = args[1];
+ break;
+ default:
+ error("o6_kernelSetFunctions: default case %d (param count %d)", args[0], num);
+ break;
+ }
+ }
+}
+
+void ScummEngine_v6::o6_kernelGetFunctions() {
+ int args[30];
+ int i;
+ int slot;
+ Actor *a;
+
+ getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 113:
+ // This is used for the Sam & Max paint-by-numbers mini-game
+ // to find out what color to change. I think that what we have
+ // the virtual mouse coordinates, because that's what used
+ // everywhere else in the script.
+
+ {
+ VirtScreen *vs = &virtscr[0];
+ if (args[1] < 0 || args[1] >= vs->w || args[2] < 0 || args[2] >= vs->h) {
+ // FIXME: Until we know what to do in this case...
+ debug(0, "o6_kernelGetFunctions:113: asking for pixel (%d, %d) outside of %dx%d screen", args[1], args[2], vs->w, vs->h);
+ push(0);
+ } else
+ push(*((byte *)vs->pixels + args[1] + args[2] * vs->pitch));
+ }
+ break;
+ case 115:
+ push(getSpecialBox(args[1], args[2]));
+ break;
+ case 116:
+ push(checkXYInBoxBounds(args[3], args[1], args[2]));
+ break;
+ case 206:
+ push(remapPaletteColor(args[1], args[2], args[3], -1));
+ break;
+ case 207:
+ i = getObjectIndex(args[1]);
+ assert(i);
+ push(_objs[i].x_pos);
+ break;
+ case 208:
+ i = getObjectIndex(args[1]);
+ assert(i);
+ push(_objs[i].y_pos);
+ break;
+ case 209:
+ i = getObjectIndex(args[1]);
+ assert(i);
+ push(_objs[i].width);
+ break;
+ case 210:
+ i = getObjectIndex(args[1]);
+ assert(i);
+ push(_objs[i].height);
+ break;
+ case 211:
+ /*
+ 13 = thrust
+ 336 = thrust
+ 328 = thrust
+ 27 = abort
+ 97 = left
+ 331 = left
+ 115 = right
+ 333 = right
+ */
+
+ push(getKeyState(args[1]));
+ break;
+ case 212:
+ a = derefActor(args[1], "o6_kernelGetFunctions:212");
+ // This is used by walk scripts
+ push(a->_frame);
+ break;
+ case 213:
+ slot = getVerbSlot(args[1], 0);
+ push(_verbs[slot].curRect.left);
+ break;
+ case 214:
+ slot = getVerbSlot(args[1], 0);
+ push(_verbs[slot].curRect.top);
+ break;
+ case 215:
+ if ((_extraBoxFlags[args[1]] & 0x00FF) == 0x00C0) {
+ push(_extraBoxFlags[args[1]]);
+ } else {
+ push(getBoxFlags(args[1]));
+ }
+ break;
+ default:
+ error("o6_kernelGetFunctions: default case %d", args[0]);
+ }
+}
+
+// FIXME: check either some warning will trigger. I am not sure that those
+// keys are queried in scripts at all
+int ScummEngine::getKeyState(int key) {
+ switch (key) {
+ case 0x145:
+ warning("ScummEngine::getKeyState(%x) 'numlock' is probed", key);
+ return 0;
+ break;
+ case 0x164:
+ warning("ScummEngine::getKeyState(%x) 'left shift' is probed", key);
+ return 0;
+ break;
+ case 0x165:
+ warning("ScummEngine::getKeyState(%x) 'right shift' is probed", key);
+ return 0;
+ break;
+ case 0x166:
+ case 0x167:
+ warning("ScummEngine::getKeyState(%x) 'alt' is probed", key);
+ return 0;
+ break;
+ case 0x168:
+ warning("ScummEngine::getKeyState(%x) 'left ctrl' is probed", key);
+ return 0;
+ break;
+ case 0x202a:
+ warning("ScummEngine::getKeyState(%x) 'gray *' is probed", key);
+ return 0;
+ break;
+ case 0x202d:
+ warning("ScummEngine::getKeyState(%x) 'gray -' is probed", key);
+ return 0;
+ break;
+ case 0x147: // Home
+ return (_keyDownMap[0x107] || _keyDownMap[0x115]) ? 1 : 0;
+ break;
+ case 0x148: // Up
+ return (_keyDownMap[0x108] || _keyDownMap[0x111] ||
+ _keyDownMap[0x38]) ? 1 : 0;
+ break;
+ case 0x149: // PgUp
+ return (_keyDownMap[0x109] || _keyDownMap[0x118]) ? 1 : 0;
+ break;
+ case 0x14A: // Gray-
+ return (_keyDownMap[0x10d] || _keyDownMap[0x2d]) ? 1 : 0;
+ break;
+ case 0x14B: // Left
+ return (_keyDownMap[0x104] || _keyDownMap[0x114] ||
+ _keyDownMap[0x34]) ? 1 : 0;
+ break;
+ case 0x14C: // 5
+ return (_keyDownMap[0x105]) ? 1 : 0;
+ break;
+ case 0x14D: // Right
+ return (_keyDownMap[0x106] || _keyDownMap[0x113] ||
+ _keyDownMap[0x36]) ? 1 : 0;
+ break;
+ case 0x14E: // Gray+
+ return (_keyDownMap[0x10e] ||
+ (_keyDownMap[0x13d] && _keyDownMap[0x12f])) ? 1 : 0;
+ break;
+ case 0x14F: // End
+ return (_keyDownMap[0x101] || _keyDownMap[0x117]) ? 1 : 0;
+ break;
+ case 0x150: // Down
+ return (_keyDownMap[0x102] || _keyDownMap[0x112] ||
+ _keyDownMap[0x32]) ? 1 : 0;
+ break;
+ case 0x151: // PgDn
+ return (_keyDownMap[0x103] || _keyDownMap[0x119]) ? 1 : 0;
+ break;
+ case 0x152: // Ins
+ return (_keyDownMap[0x100] || _keyDownMap[0x115]) ? 1 : 0;
+ break;
+ case 0x153: // Del
+ return (_keyDownMap[0x10a] || _keyDownMap[0x7f]) ? 1 : 0;
+ break;
+ default:
+ break;
+ }
+
+ if (key >= 0x13b && key <= 0x144) { // F1-F10
+ key -= 0x13b - 0x11a;
+ } else if (key >= 0x154 && key <= 0x15d) { // Shift+F1-F10
+ key -= 0x154 - 0x11a; // map it to just F1-F10
+
+ warning("ScummEngine::getKeyState(%x) 'Shift-F%d' is probed", key, key-0x153);
+ } else if (key > 0x8000) { // Alt
+ key -= 0x8000;
+ key += 154; // see ScummEngine::parseEvents()
+ } else if (key > 0x4000) { // Ctrl
+ key -= 0x4000;
+ key -= 0x40;
+ } else if (key > 0x2000) { // Gray keys
+ key -= 0x2000;
+ warning("ScummEngine::getKeyState(%x) 'gray key' is probed", key);
+ }
+
+ return (_keyDownMap[key]) ? 1 : 0;
+}
+
+void ScummEngine_v6::o6_delayFrames() {
+ ScriptSlot *ss = &vm.slot[_currentScript];
+ if (ss->delayFrameCount == 0) {
+ ss->delayFrameCount = pop();
+ } else {
+ ss->delayFrameCount--;
+ }
+ if (ss->delayFrameCount) {
+ _scriptPointer--;
+ o6_breakHere();
+ }
+}
+
+void ScummEngine_v6::o6_pickOneOf() {
+ int args[100];
+ int i, num;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ i = pop();
+ if (i < 0 || i > num)
+ error("o6_pickOneOf: %d out of range (0, %d)", i, num - 1);
+ push(args[i]);
+}
+
+void ScummEngine_v6::o6_pickOneOfDefault() {
+ int args[100];
+ int i, num, def;
+
+ def = pop();
+ num = getStackList(args, ARRAYSIZE(args));
+ i = pop();
+ if (i < 0 || i >= num)
+ i = def;
+ else
+ i = args[i];
+ push(i);
+}
+
+void ScummEngine_v6::o6_stampObject() {
+ int object, x, y, state;
+
+ state = pop();
+ y = pop();
+ x = pop();
+ object = pop();
+ if (_version >= 7 && object < 30) {
+ if (state == 0)
+ state = 255;
+
+ Actor *a = derefActor(object, "o6_stampObject");
+ a->_scalex = state;
+ a->_scaley = state;
+ a->putActor(x, y, _currentRoom);
+ a->_drawToBackBuf = true;
+ a->drawActorCostume();
+ a->_drawToBackBuf = false;
+ a->drawActorCostume();
+ return;
+ }
+
+ if (state == 0)
+ state = 1;
+
+ int objnum = getObjectIndex(object);
+ if (objnum == -1)
+ return;
+
+ if (x != -1) {
+ _objs[objnum].x_pos = x * 8;
+ _objs[objnum].y_pos = y * 8;
+ }
+
+ putState(object, state);
+ drawObject(objnum, 0);
+}
+
+void ScummEngine_v6::o6_stopTalking() {
+ stopTalk();
+}
+
+void ScummEngine_v6::o6_findAllObjects() {
+ int room = pop();
+ int i = 1;
+
+ if (room != _currentRoom)
+ error("o6_findAllObjects: current room is not %d", room);
+ writeVar(0, 0);
+ defineArray(0, kIntArray, 0, _numLocalObjects + 1);
+ writeArray(0, 0, 0, _numLocalObjects);
+
+ while (i < _numLocalObjects) {
+ writeArray(0, 0, i, _objs[i].obj_nr);
+ i++;
+ }
+
+ push(readVar(0));
+}
+
+void ScummEngine_v6::shuffleArray(int num, int minIdx, int maxIdx) {
+ int range = maxIdx - minIdx;
+ int count = range * 2;
+
+ // Shuffle the array 'num'
+ while (count--) {
+ // Determine two random elements...
+ int rand1 = _rnd.getRandomNumber(range) + minIdx;
+ int rand2 = _rnd.getRandomNumber(range) + minIdx;
+
+ // ...and swap them
+ int val1 = readArray(num, 0, rand1);
+ int val2 = readArray(num, 0, rand2);
+ writeArray(num, 0, rand1, val2);
+ writeArray(num, 0, rand2, val1);
+ }
+}
+
+void ScummEngine_v6::o6_shuffle() {
+ int b = pop();
+ int a = pop();
+ shuffleArray(fetchScriptWord(), a, b);
+}
+
+void ScummEngine_v6::o6_pickVarRandom() {
+ int num;
+ int args[100];
+ int dim1;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ int value = fetchScriptWord();
+
+ if (readVar(value) == 0) {
+ defineArray(value, kIntArray, 0, num);
+ if (num > 0) {
+ int16 counter = 0;
+ do {
+ writeArray(value, 0, counter + 1, args[counter]);
+ } while (++counter < num);
+ }
+
+ shuffleArray(value, 1, num);
+ writeArray(value, 0, 0, 2);
+ push(readArray(value, 0, 1));
+ return;
+ }
+
+ num = readArray(value, 0, 0);
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(value));
+ dim1 = FROM_LE_16(ah->dim1) - 1;
+
+ if (dim1 < num) {
+ int16 var_2 = readArray(value, 0, num - 1);
+ shuffleArray(value, 1, dim1);
+ if (readArray(value, 0, 1) == var_2) {
+ num = 2;
+ } else {
+ num = 1;
+ }
+ }
+
+ writeArray(value, 0, 0, num + 1);
+ push(readArray(value, 0, num));
+}
+
+void ScummEngine_v6::o6_getDateTime() {
+ struct tm *t;
+ time_t now = time(NULL);
+
+ t = localtime(&now);
+
+ VAR(VAR_TIMEDATE_YEAR) = t->tm_year;
+ VAR(VAR_TIMEDATE_MONTH) = t->tm_mon;
+ VAR(VAR_TIMEDATE_DAY) = t->tm_mday;
+ VAR(VAR_TIMEDATE_HOUR) = t->tm_hour;
+ VAR(VAR_TIMEDATE_MINUTE) = t->tm_min;
+
+ if (_version == 8)
+ VAR(VAR_TIMEDATE_SECOND) = t->tm_sec;
+}
+
+void ScummEngine_v6::o6_getPixel() {
+ // This opcode is used to check fir ground area in the "Asteroid Lander"
+ // minigame in "The Dig"
+ int x, y;
+
+ if (_heversion == 61) {
+ x = pop();
+ y = pop();
+ } else {
+ y = pop();
+ x = pop();
+ }
+
+ VirtScreen *vs = findVirtScreen(y);
+
+ if (vs == NULL || x > _screenWidth - 1 || x < 0) {
+ push(-1);
+ return;
+ }
+
+ byte area = *vs->getPixels(x, y - vs->topline);
+ push(area);
+}
+
+void ScummEngine_v6::o6_setBoxSet() {
+ int arg = pop() - 1;
+
+ const byte *room = getResourceAddress(rtRoom, _roomResource);
+ const byte *boxd = NULL, *boxm = NULL;
+ int32 dboxSize, mboxSize;
+ int i;
+
+ ResourceIterator boxds(room, false);
+ for (i = 0; i < arg; i++)
+ boxd = boxds.findNext(MKID('BOXD'));
+
+ if (!boxd)
+ error("ScummEngine_v6::o6_setBoxSet: Can't find dboxes for set %d", arg);
+
+ dboxSize = READ_BE_UINT32(boxd + 4) - 8;
+ byte *matrix = res.createResource(rtMatrix, 2, dboxSize);
+
+ assert(matrix);
+ memcpy(matrix, boxd + 8, dboxSize);
+
+ ResourceIterator boxms(room, false);
+ for (i = 0; i < arg; i++)
+ boxm = boxms.findNext(MKID('BOXM'));
+
+ if (!boxm)
+ error("ScummEngine_v6::o6_setBoxSet: Can't find mboxes for set %d", arg);
+
+ mboxSize = READ_BE_UINT32(boxm + 4) - 8;
+ matrix = res.createResource(rtMatrix, 1, mboxSize);
+
+ assert(matrix);
+ memcpy(matrix, boxm + 8, mboxSize);
+
+ if (_version == 7)
+ putActors();
+}
+
+void ScummEngine_v6::decodeParseString(int m, int n) {
+ byte b = fetchScriptByte();
+
+ switch (b) {
+ case 65: // SO_AT
+ _string[m].ypos = pop();
+ _string[m].xpos = pop();
+ _string[m].overhead = false;
+ break;
+ case 66: // SO_COLOR
+ _string[m].color = pop();
+ break;
+ case 67: // SO_CLIPPED
+ _string[m].right = pop();
+ break;
+ case 69: // SO_CENTER
+ _string[m].center = true;
+ _string[m].overhead = false;
+ break;
+ case 71: // SO_LEFT
+ _string[m].center = false;
+ _string[m].overhead = false;
+ break;
+ case 72: // SO_OVERHEAD
+ _string[m].overhead = true;
+ _string[m].no_talk_anim = false;
+ break;
+ case 73: // SO_SAY_VOICE
+ error("decodeParseString: case 73");
+ break;
+ case 74: // SO_MUMBLE
+ _string[m].no_talk_anim = true;
+ break;
+ case 75: // SO_TEXTSTRING
+ printString(m, _scriptPointer);
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+ break;
+ case 0xFE:
+ _string[m].loadDefault();
+ if (n)
+ _actorToPrintStrFor = pop();
+ break;
+ case 0xFF:
+ _string[m].saveDefault();
+ break;
+ default:
+ error("decodeParseString: default case 0x%x", b);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v6he.cpp b/engines/scumm/script_v6he.cpp
new file mode 100644
index 0000000000..ce3f76bc18
--- /dev/null
+++ b/engines/scumm/script_v6he.cpp
@@ -0,0 +1,1315 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/savefile.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/imuse.h"
+#include "scumm/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/usage_bits.h"
+#include "scumm/util.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+struct vsUnpackCtx {
+ uint8 size;
+ uint8 type;
+ uint8 b;
+ uint8 *ptr;
+};
+
+struct vsPackCtx {
+ int size;
+ uint8 buf[256];
+};
+
+static void virtScreenSavePackBuf(vsPackCtx *ctx, uint8 *&dst, int len);
+static void virtScreenSavePackByte(vsPackCtx *ctx, uint8 *&dst, int len, uint8 b);
+static uint8 virtScreenLoadUnpack(vsUnpackCtx *ctx, byte *data);
+static int virtScreenSavePack(byte *dst, byte *src, int len, int unk);
+
+// Compatibility notes:
+//
+// FBEAR (fbear, fbeardemo)
+// transparency in akos.cpp
+// negative size in file read/write
+
+#define OPCODE(x) _OPCODE(ScummEngine_v60he, x)
+
+void ScummEngine_v60he::setupOpcodes() {
+ static const OpcodeEntryv60he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o6_pushByteVar),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayRead),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedRead),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o6_invalid),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeByteVar),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayWrite),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedWrite),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarInc),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayInc),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarDec),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayDec),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o6_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o6_startObject),
+ OPCODE(o6_drawObject),
+ OPCODE(o6_drawObjectAt),
+ OPCODE(o6_invalid),
+ /* 64 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_stopMusic),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o6_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o60_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o6_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_startMusic),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o6_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o6_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o6_getVerbFromXY),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o6_resourceRoutines),
+ /* 9C */
+ OPCODE(o60_roomOps),
+ OPCODE(o60_actorOps),
+ OPCODE(o6_verbOps),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o6_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o6_arrayOps),
+ OPCODE(o6_saveRestoreVerbs),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o60_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorAnimCounter1),
+ /* AC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o6_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o6_talkActor),
+ OPCODE(o6_talkEgo),
+ /* BC */
+ OPCODE(o6_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o6_dim2dimArray),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o60_kernelGetFunctions),
+ OPCODE(o60_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o6_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o60_openFile),
+ OPCODE(o60_readFile),
+ /* DC */
+ OPCODE(o60_writeFile),
+ OPCODE(o6_findAllObjects),
+ OPCODE(o60_deleteFile),
+ OPCODE(o60_rename),
+ /* E0 */
+ OPCODE(o60_soundOps),
+ OPCODE(o6_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o6_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o60_seekFilePos),
+ OPCODE(o60_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F0 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F4 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* FC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesv60he = opcodes;
+}
+
+void ScummEngine_v60he::executeOpcode(byte i) {
+ OpcodeProcv60he op = _opcodesv60he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v60he::getOpcodeDesc(byte i) {
+ return _opcodesv60he[i].desc;
+}
+
+void ScummEngine_v60he::o60_setState() {
+ int state = pop();
+ int obj = pop();
+
+ if (state & 0x8000) {
+ state &= 0x7FFF;
+ putState(obj, state);
+ if (_heversion >= 72)
+ removeObjectFromDrawQue(obj);
+ } else {
+ putState(obj, state);
+ markObjectRectAsDirty(obj);
+ if (_bgNeedsRedraw)
+ clearDrawObjectQueue();
+ }
+}
+
+void ScummEngine_v60he::o60_roomOps() {
+ int a, b, c, d, e;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 172: // SO_ROOM_SCROLL
+ b = pop();
+ a = pop();
+ if (a < (_screenWidth / 2))
+ a = (_screenWidth / 2);
+ if (b < (_screenWidth / 2))
+ b = (_screenWidth / 2);
+ if (a > _roomWidth - (_screenWidth / 2))
+ a = _roomWidth - (_screenWidth / 2);
+ if (b > _roomWidth - (_screenWidth / 2))
+ b = _roomWidth - (_screenWidth / 2);
+ VAR(VAR_CAMERA_MIN_X) = a;
+ VAR(VAR_CAMERA_MAX_X) = b;
+ break;
+
+ case 174: // SO_ROOM_SCREEN
+ b = pop();
+ a = pop();
+ if (_heversion >= 71)
+ initScreens(a, _screenHeight);
+ else
+ initScreens(a, b);
+ break;
+
+ case 175: // SO_ROOM_PALETTE
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ setPalColor(d, a, b, c);
+ break;
+
+ case 176: // SO_ROOM_SHAKE_ON
+ setShake(1);
+ break;
+
+ case 177: // SO_ROOM_SHAKE_OFF
+ setShake(0);
+ break;
+
+ case 179: // SO_ROOM_INTENSITY
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, a, a, b, c);
+ break;
+
+ case 180: // SO_ROOM_SAVEGAME
+ _saveTemporaryState = true;
+ _saveLoadSlot = pop();
+ _saveLoadFlag = pop();
+ break;
+
+ case 181: // SO_ROOM_FADE
+ a = pop();
+ if (_heversion >= 70) {
+ // Defaults to 1 but doesn't use fade effects
+ } else if (a) {
+ _switchRoomEffect = (byte)(a & 0xFF);
+ _switchRoomEffect2 = (byte)(a >> 8);
+ } else {
+ fadeIn(_newEffect);
+ }
+ break;
+
+ case 182: // SO_RGB_ROOM_INTENSITY
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, b, c, d, e);
+ break;
+
+ case 183: // SO_ROOM_SHADOW
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_heversion == 60)
+ setupShadowPalette(a, b, c, d, e, 0, 256);
+ break;
+
+ case 186: // SO_ROOM_TRANSFORM
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ palManipulateInit(a, b, c, d);
+ break;
+
+ case 187: // SO_CYCLE_SPEED
+ b = pop();
+ a = pop();
+ checkRange(16, 1, a, "o60_roomOps: 187: color cycle out of range (%d)");
+ _colorCycle[a - 1].delay = (b != 0) ? 0x4000 / (b * 0x4C) : 0;
+ break;
+
+ case 213: // SO_ROOM_NEW_PALETTE
+ a = pop();
+ setPalette(a);
+ break;
+ case 220:
+ a = pop();
+ b = pop();
+ copyPalColor(a, b);
+ break;
+ case 221:
+ int len;
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+ _saveLoadFlag = pop();
+ _saveLoadSlot = 1;
+ _saveTemporaryState = true;
+ break;
+ case 234: // HE 7.2
+ b = pop();
+ a = pop();
+ swapObjects(a, b);
+ break;
+ case 236: // HE 7.2
+ b = pop();
+ a = pop();
+ setRoomPalette(a, b);
+ break;
+ default:
+ error("o60_roomOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v60he::swapObjects(int object1, int object2) {
+ int idx1 = -1, idx2 = -1;
+
+ for (int i = 0; i < _numLocalObjects; i++) {
+ if (_objs[i].obj_nr == object1)
+ idx1 = i;
+
+ if (_objs[i].obj_nr == object2)
+ idx2 = i;
+ }
+
+ if (idx1 == -1 || idx2 == -1 || idx1 <= idx2)
+ return;
+
+ stopObjectScript(object1);
+ stopObjectScript(object2);
+
+ ObjectData tmpOd;
+
+ memcpy(&tmpOd, &_objs[idx1], sizeof(tmpOd));
+ memcpy(&_objs[idx1], &_objs[idx2], sizeof(tmpOd));
+ memcpy(&_objs[idx2], &tmpOd, sizeof(tmpOd));
+}
+
+void ScummEngine_v60he::o60_actorOps() {
+ Actor *a;
+ int i, j, k;
+ int args[8];
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 197) {
+ _curActor = pop();
+ return;
+ }
+
+ a = derefActorSafe(_curActor, "o60_actorOps");
+ if (!a)
+ return;
+
+ switch (subOp) {
+ case 30:
+ // _heversion >= 70
+ _actorClipOverride.bottom = pop();
+ _actorClipOverride.right = pop();
+ _actorClipOverride.top = pop();
+ _actorClipOverride.left = pop();
+ break;
+ case 76: // SO_COSTUME
+ a->setActorCostume(pop());
+ break;
+ case 77: // SO_STEP_DIST
+ j = pop();
+ i = pop();
+ a->setActorWalkSpeed(i, j);
+ break;
+ case 78: // SO_SOUND
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; i++)
+ a->_sound[i] = args[i];
+ break;
+ case 79: // SO_WALK_ANIMATION
+ a->_walkFrame = pop();
+ break;
+ case 80: // SO_TALK_ANIMATION
+ a->_talkStopFrame = pop();
+ a->_talkStartFrame = pop();
+ break;
+ case 81: // SO_STAND_ANIMATION
+ a->_standFrame = pop();
+ break;
+ case 82: // SO_ANIMATION
+ // dummy case in scumm6
+ pop();
+ pop();
+ pop();
+ break;
+ case 83: // SO_DEFAULT
+ a->initActor(0);
+ break;
+ case 84: // SO_ELEVATION
+ a->setElevation(pop());
+ break;
+ case 85: // SO_ANIMATION_DEFAULT
+ a->_initFrame = 1;
+ a->_walkFrame = 2;
+ a->_standFrame = 3;
+ a->_talkStartFrame = 4;
+ a->_talkStopFrame = 5;
+ break;
+ case 86: // SO_PALETTE
+ j = pop();
+ i = pop();
+ checkRange(255, 0, i, "Illegal palette slot %d");
+ a->remapActorPaletteColor(i, j);
+ a->_needRedraw = true;
+ break;
+ case 87: // SO_TALK_COLOR
+ a->_talkColor = pop();
+ break;
+ case 88: // SO_ACTOR_NAME
+ loadPtrToResource(rtActorName, a->_number, NULL);
+ break;
+ case 89: // SO_INIT_ANIMATION
+ a->_initFrame = pop();
+ break;
+ case 91: // SO_ACTOR_WIDTH
+ a->_width = pop();
+ break;
+ case 92: // SO_SCALE
+ i = pop();
+ a->setScale(i, i);
+ break;
+ case 93: // SO_NEVER_ZCLIP
+ a->_forceClip = 0;
+ break;
+ case 94: // SO_ALWAYS_ZCLIP
+ a->_forceClip = pop();
+ break;
+ case 95: // SO_IGNORE_BOXES
+ a->_ignoreBoxes = 1;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 96: // SO_FOLLOW_BOXES
+ a->_ignoreBoxes = 0;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 97: // SO_ANIMATION_SPEED
+ a->setAnimSpeed(pop());
+ break;
+ case 98: // SO_SHADOW
+ a->_shadowMode = pop();
+ a->_needRedraw = true;
+ break;
+ case 99: // SO_TEXT_OFFSET
+ a->_talkPosY = pop();
+ a->_talkPosX = pop();
+ break;
+ case 156: // HE 7.2
+ a->_charset = pop();
+ break;
+ case 198: // SO_ACTOR_VARIABLE
+ i = pop();
+ a->setAnimVar(pop(), i);
+ break;
+ case 215: // SO_ACTOR_IGNORE_TURNS_ON
+ a->_ignoreTurns = true;
+ break;
+ case 216: // SO_ACTOR_IGNORE_TURNS_OFF
+ a->_ignoreTurns = false;
+ break;
+ case 217: // SO_ACTOR_NEW
+ a->initActor(2);
+ break;
+ case 218:
+ a->drawActorToBackBuf(a->_pos.x, a->_pos.y);
+ break;
+ case 219:
+ a->_drawToBackBuf = false;
+ a->_needRedraw = true;
+ a->_needBgReset = true;
+ break;
+ case 225:
+ {
+ byte string[128];
+ copyScriptString(string);
+ int slot = pop();
+
+ int len = resStrLen(string) + 1;
+ convertMessageToString(string, a->_heTalkQueue[slot].sentence, len);
+
+ a->_heTalkQueue[slot].posX = a->_talkPosX;
+ a->_heTalkQueue[slot].posY = a->_talkPosY;
+ a->_heTalkQueue[slot].color = a->_talkColor;
+ break;
+ }
+ default:
+ error("o60_actorOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v60he::o60_wait() {
+ int actnum;
+ int offs = -2;
+ Actor *a;
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 168: // SO_WAIT_FOR_ACTOR Wait for actor
+ offs = fetchScriptWordSigned();
+ actnum = pop();
+ a = derefActor(actnum, "o60_wait:168");
+ if (a->_moving)
+ break;
+ return;
+ case 169: // SO_WAIT_FOR_MESSAGE Wait for message
+ if (VAR(VAR_HAVE_MSG))
+ break;
+ return;
+ case 170: // SO_WAIT_FOR_CAMERA Wait for camera
+ if (camera._cur.x / 8 != camera._dest.x / 8)
+ break;
+ return;
+ case 171: // SO_WAIT_FOR_SENTENCE
+ if (_sentenceNum) {
+ if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ }
+ if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ default:
+ error("o60_wait: default case 0x%x", subOp);
+ }
+
+ _scriptPointer += offs;
+ o6_breakHere();
+}
+
+void ScummEngine_v60he::o60_kernelSetFunctions() {
+ int args[29];
+ int num;
+
+ num = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1:
+ // Used to restore images when decorating cake in
+ // Fatty Bear's Birthday Surprise
+ virtScreenLoad(args[1], args[2], args[3], args[4], args[5]);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ //Used before mini games in 3DO versions, seems safe to ignore.
+ break;
+ default:
+ error("o60_kernelSetFunctions: default case %d (param count %d)", args[0], num);
+ }
+}
+
+void ScummEngine_v60he::virtScreenLoad(int resIdx, int x1, int y1, int x2, int y2) {
+ vsUnpackCtx ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ VirtScreen &vs = virtscr[kMainVirtScreen];
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, resIdx);
+ virtScreenLoadUnpack(&ctx, ah->data);
+ for (int j = y1; j <= y2; ++j) {
+ uint8 *p1 = vs.getPixels(x1, j - vs.topline);
+ uint8 *p2 = vs.getBackPixels(x1, j - vs.topline);
+ if (x2 >= x1) {
+ uint32 w = x2 - x1 + 1;
+ while (w--) {
+ uint8 decByte = virtScreenLoadUnpack(&ctx, 0);
+ *p1++ = decByte;
+ *p2++ = decByte;
+ }
+ }
+ }
+ markRectAsDirty(kMainVirtScreen, x1, x2, y1, y2 + 1, USAGE_BIT_RESTORED);
+}
+
+uint8 virtScreenLoadUnpack(vsUnpackCtx *ctx, byte *data) {
+ uint8 decByte;
+ if (data != 0) {
+ ctx->type = 0;
+ ctx->ptr = data;
+ decByte = 0;
+ } else {
+ uint8 a;
+ if (ctx->type == 0) {
+ a = *(ctx->ptr)++;
+ if (a & 1) {
+ ctx->type = 1;
+ ctx->b = *(ctx->ptr)++;
+ } else {
+ ctx->type = 2;
+ }
+ ctx->size = a;
+ a = (a >> 1) + 1;
+ } else {
+ a = ctx->size;
+ }
+ if (ctx->type == 2) {
+ ctx->b = *(ctx->ptr)++;
+ }
+ ctx->size = a - 1;
+ if (ctx->size == 0) {
+ ctx->type = 0;
+ }
+ decByte = ctx->b;
+ }
+ return decByte;
+}
+
+
+void ScummEngine_v60he::o60_kernelGetFunctions() {
+ int args[29];
+ ArrayHeader *ah;
+ getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1:
+ // Used to store images when decorating cake in
+ // Fatty Bear's Birthday Surprise
+ writeVar(0, 0);
+ ah = defineArray(0, kByteArray, 0, virtScreenSave(0, args[1], args[2], args[3], args[4]));
+ virtScreenSave(ah->data, args[1], args[2], args[3], args[4]);
+ push(readVar(0));
+ break;
+ default:
+ error("o60_kernelGetFunctions: default case %d", args[0]);
+ }
+}
+
+int ScummEngine_v60he::virtScreenSave(byte *dst, int x1, int y1, int x2, int y2) {
+ int packedSize = 0;
+ VirtScreen &vs = virtscr[kMainVirtScreen];
+
+ for (int j = y1; j <= y2; ++j) {
+ uint8 *p = vs.getBackPixels(x1, j - vs.topline);
+
+ int size = virtScreenSavePack(dst, p, x2 - x1 + 1, 0);
+ if (dst != 0) {
+ dst += size;
+ }
+ packedSize += size;
+ }
+ return packedSize;
+}
+
+int virtScreenSavePack(byte *dst, byte *src, int len, int unk) {
+ vsPackCtx ctx;
+ memset(&ctx, 0, sizeof(ctx));
+
+ uint8 prevByte, curByte;
+
+ ctx.buf[0] = prevByte = *src++;
+ int flag = 0;
+ int iend = 1;
+ int ibeg = 0;
+
+ for (--len; len != 0; --len, prevByte = curByte) {
+ bool pass = false;
+
+ assert(iend < 0x100);
+ ctx.buf[iend] = curByte = *src++;
+ ++iend;
+
+ if (flag == 0) {
+ if (iend > 0x80) {
+ virtScreenSavePackBuf(&ctx, dst, iend - 1);
+ ctx.buf[0] = curByte;
+ iend = 1;
+ ibeg = 0;
+ continue;
+ }
+ if (prevByte != curByte) {
+ ibeg = iend - 1;
+ continue;
+ }
+ if (iend - ibeg < 3) {
+ if (ibeg != 0) {
+ pass = true;
+ } else {
+ flag = 1;
+ }
+ } else {
+ if (ibeg > 0) {
+ virtScreenSavePackBuf(&ctx, dst, ibeg);
+ }
+ flag = 1;
+ }
+ }
+ if (flag == 1 || pass) {
+ if (prevByte != curByte || iend - ibeg > 0x80) {
+ virtScreenSavePackByte(&ctx, dst, iend - ibeg - 1, prevByte);
+ ctx.buf[0] = curByte;
+ iend = 1;
+ ibeg = 0;
+ flag = 0;
+ }
+ }
+ }
+
+ if (flag == 0) {
+ virtScreenSavePackBuf(&ctx, dst, iend);
+ } else if (flag == 1) {
+ virtScreenSavePackByte(&ctx, dst, iend - ibeg, prevByte);
+ }
+ return ctx.size;
+}
+
+void virtScreenSavePackBuf(vsPackCtx *ctx, uint8 *&dst, int len) {
+ if (dst) {
+ *dst++ = (len - 1) * 2;
+ }
+ ++ctx->size;
+ if (len > 0) {
+ ctx->size += len;
+ if (dst) {
+ memcpy(dst, ctx->buf, len);
+ dst += len;
+ }
+ }
+}
+
+void virtScreenSavePackByte(vsPackCtx *ctx, uint8 *&dst, int len, uint8 b) {
+ if (dst) {
+ *dst++ = ((len - 1) * 2) | 1;
+ }
+ ++ctx->size;
+ if (dst) {
+ *dst++ = b;
+ }
+ ++ctx->size;
+}
+
+void ScummEngine_v60he::o60_openFile() {
+ int mode, len, slot, l, r;
+ byte filename[100];
+
+ convertMessageToString(_scriptPointer, filename, sizeof(filename));
+
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ for (r = strlen((char*)filename); r != 0; r--) {
+ if (filename[r - 1] == '\\')
+ break;
+ }
+
+ mode = pop();
+ slot = -1;
+ for (l = 0; l < 17; l++) {
+ if (_hFileTable[l].isOpen() == false) {
+ slot = l;
+ break;
+ }
+ }
+
+ if (slot != -1) {
+ switch(mode) {
+ case 1:
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileReadMode, _saveFileMan->getSavePath());
+ if (_hFileTable[slot].isOpen() == false)
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileReadMode);
+ break;
+ case 2:
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileWriteMode, _saveFileMan->getSavePath());
+ break;
+ default:
+ error("o60_openFile(): wrong open file mode %d", mode);
+ }
+
+ if (_hFileTable[slot].isOpen() == false)
+ slot = -1;
+
+ }
+ push(slot);
+}
+
+void ScummEngine_v60he::o60_closeFile() {
+ int slot = pop();
+ if (slot != -1)
+ _hFileTable[slot].close();
+}
+
+void ScummEngine_v60he::o60_deleteFile() {
+ int len, r;
+ byte filename[100];
+
+ convertMessageToString(_scriptPointer, filename, sizeof(filename));
+
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ for (r = strlen((char*)filename); r != 0; r--) {
+ if (filename[r - 1] == '\\')
+ break;
+ }
+
+ debug(1, "stub o60_deleteFile(\"%s\")", filename + r);
+}
+
+void ScummEngine_v60he::o60_rename() {
+ int len, r1, r2;
+ byte filename[100],filename2[100];
+
+ convertMessageToString(_scriptPointer, filename, sizeof(filename));
+
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ for (r1 = strlen((char*)filename); r1 != 0; r1--) {
+ if (filename[r1 - 1] == '\\')
+ break;
+ }
+
+ convertMessageToString(_scriptPointer, filename2, sizeof(filename2));
+
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ for (r2 = strlen((char*)filename2); r2 != 0; r2--) {
+ if (filename2[r2 - 1] == '\\')
+ break;
+ }
+
+ debug(1, "stub o60_rename(\"%s\" to \"%s\")", filename + r1, filename2 + r2);
+}
+
+int ScummEngine_v60he::readFileToArray(int slot, int32 size) {
+ if (size == 0)
+ size = _hFileTable[slot].size() - _hFileTable[slot].pos();
+
+ writeVar(0, 0);
+
+ ArrayHeader *ah = defineArray(0, kByteArray, 0, size);
+ _hFileTable[slot].read(ah->data, size);
+
+ return readVar(0);
+}
+
+void ScummEngine_v60he::o60_readFile() {
+ int32 size = pop();
+ int slot = pop();
+ int val;
+
+ // Fatty Bear uses positive values
+ if ((_platform == Common::kPlatformPC) && (_gameId == GID_FBEAR))
+ size = -size;
+
+ if (size == -2) {
+ val = _hFileTable[slot].readUint16LE();
+ push(val);
+ } else if (size == -1) {
+ val = _hFileTable[slot].readByte();
+ push(val);
+ } else {
+ val = readFileToArray(slot, size);
+ push(val);
+ }
+}
+
+void ScummEngine_v60he::writeFileFromArray(int slot, int resID) {
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, resID);
+ int32 size = FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2);
+
+ _hFileTable[slot].write(ah->data, size);
+}
+
+void ScummEngine_v60he::o60_writeFile() {
+ int32 size = pop();
+ int16 resID = pop();
+ int slot = pop();
+
+ // Fatty Bear uses positive values
+ if ((_platform == Common::kPlatformPC) && (_gameId == GID_FBEAR))
+ size = -size;
+
+ if (size == -2) {
+ _hFileTable[slot].writeUint16LE(resID);
+ } else if (size == -1) {
+ _hFileTable[slot].writeByte(resID);
+ } else {
+ writeFileFromArray(slot, resID);
+ }
+}
+
+void ScummEngine_v60he::o60_soundOps() {
+ byte subOp = fetchScriptByte();
+ int arg = pop();
+
+ switch (subOp) {
+ case 0xde:
+ _imuse->setMusicVolume(arg);
+ break;
+ case 0xdf:
+ // Used in fbear introduction
+ break;
+ case 0xe0:
+ // Fatty Bear's Birthday surprise uses this when playing the
+ // piano, but only when using one of the digitized instruments.
+ // See also o6_startSound().
+ _sound->setOverrideFreq(arg);
+ break;
+ default:
+ error("o60_soundOps: default case 0x%x", subOp);
+ }
+}
+
+void ScummEngine_v60he::localizeArray(int slot, byte scriptSlot) {
+ if (_heversion >= 80)
+ slot &= ~0x33539000;
+
+ if (slot >= _numArray)
+ error("o60_localizeArrayToScript(%d): array slot out of range", slot);
+
+ _arraySlot[slot] = scriptSlot;
+}
+
+void ScummEngine_v60he::o60_localizeArrayToScript() {
+ int slot = pop();
+ localizeArray(slot, _currentScript);
+}
+
+void ScummEngine_v60he::o60_seekFilePos() {
+ int mode, offset, slot;
+
+ mode = pop();
+ offset = pop();
+ slot = pop();
+
+ if (slot == -1)
+ return;
+
+ switch (mode) {
+ case 1:
+ _hFileTable[slot].seek(offset, SEEK_SET);
+ break;
+ case 2:
+ _hFileTable[slot].seek(offset, SEEK_CUR);
+ break;
+ case 3:
+ _hFileTable[slot].seek(offset, SEEK_END);
+ break;
+ default:
+ error("o60_seekFilePos: default case %d", mode);
+ }
+}
+
+void ScummEngine_v60he::o60_readFilePos() {
+ int slot = pop();
+
+ if (slot == -1) {
+ push(0);
+ return;
+ }
+
+ push(_hFileTable[slot].pos());
+}
+
+void ScummEngine_v60he::o60_redimArray() {
+ int newX, newY;
+ newY = pop();
+ newX = pop();
+
+ if (newY == 0)
+ SWAP(newX, newY);
+
+ byte subOp = fetchScriptByte();
+ switch (subOp) {
+ case 199:
+ redimArray(fetchScriptWord(), newX, newY, kIntArray);
+ break;
+ case 202:
+ redimArray(fetchScriptWord(), newX, newY, kByteArray);
+ break;
+ default:
+ error("o60_redimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v60he::redimArray(int arrayId, int newX, int newY, int type) {
+ // Used in mini game at Cosmic Dust Diner in puttmoon
+ int newSize, oldSize;
+
+ if (readVar(arrayId) == 0)
+ error("redimArray: Reference to zeroed array pointer");
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(arrayId));
+
+ if (!ah)
+ error("redimArray: Invalid array (%d) reference", readVar(arrayId));
+
+ newSize = (type == kIntArray) ? 2 : 1;
+ oldSize = (ah->type == kIntArray) ? 2 : 1;
+
+ newSize *= (newX + 1) * (newY + 1);
+ oldSize *= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2);
+
+ if (newSize != oldSize)
+ error("redimArray: array %d redim mismatch", readVar(arrayId));
+
+ ah->type = TO_LE_16(type);
+ ah->dim1 = TO_LE_16(newY + 1);
+ ah->dim2 = TO_LE_16(newX + 1);
+}
+
+void ScummEngine_v60he::decodeParseString(int m, int n) {
+ int i, colors;
+ int args[31];
+
+ byte b = fetchScriptByte();
+
+ switch (b) {
+ case 65: // SO_AT
+ _string[m].ypos = pop();
+ _string[m].xpos = pop();
+ _string[m].overhead = false;
+ break;
+ case 66: // SO_COLOR
+ _string[m].color = pop();
+ break;
+ case 67: // SO_CLIPPED
+ _string[m].right = pop();
+ break;
+ case 69: // SO_CENTER
+ _string[m].center = true;
+ _string[m].overhead = false;
+ break;
+ case 71: // SO_LEFT
+ _string[m].center = false;
+ _string[m].overhead = false;
+ break;
+ case 72: // SO_OVERHEAD
+ _string[m].overhead = true;
+ _string[m].no_talk_anim = false;
+ break;
+ case 74: // SO_MUMBLE
+ _string[m].no_talk_anim = true;
+ break;
+ case 75: // SO_TEXTSTRING
+ printString(m, _scriptPointer);
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+ break;
+ case 0xF9:
+ colors = pop();
+ if (colors == 1) {
+ _string[m].color = pop();
+ } else {
+ push(colors);
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ _string[m].color = _charsetColorMap[0];
+ }
+ break;
+ case 0xFE:
+ _string[m].loadDefault();
+ if (n)
+ _actorToPrintStrFor = pop();
+ break;
+ case 0xFF:
+ _string[m].saveDefault();
+ break;
+ default:
+ error("decodeParseString: default case 0x%x", b);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v72he.cpp b/engines/scumm/script_v72he.cpp
new file mode 100644
index 0000000000..021a74dcc3
--- /dev/null
+++ b/engines/scumm/script_v72he.cpp
@@ -0,0 +1,2352 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/system.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v72he, x)
+
+void ScummEngine_v72he::setupOpcodes() {
+ static const OpcodeEntryV72he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o72_pushDWord),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o72_getScriptString),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o72_isAnyOf),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o72_resetCutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o72_findObjectWithClassOf),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o72_getObjectImageX),
+ OPCODE(o72_getObjectImageY),
+ OPCODE(o72_captureWizImage),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o72_getTimer),
+ OPCODE(o72_setTimer),
+ OPCODE(o72_getSoundPosition),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o72_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o72_startObject),
+ OPCODE(o72_drawObject),
+ OPCODE(o72_printWizImage),
+ OPCODE(o72_getArrayDimSize),
+ /* 64 */
+ OPCODE(o72_getNumFreeArrays),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_stopMusic),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o6_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o60_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o70_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_startMusic),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o70_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o70_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o6_getVerbFromXY),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o70_resourceRoutines),
+ /* 9C */
+ OPCODE(o72_roomOps),
+ OPCODE(o72_actorOps),
+ OPCODE(o72_verbOps),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o72_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o72_arrayOps),
+ OPCODE(o6_saveRestoreVerbs),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o60_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorAnimCounter1),
+ /* AC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o72_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o72_talkActor),
+ OPCODE(o72_talkEgo),
+ /* BC */
+ OPCODE(o72_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o72_dim2dimArray),
+ OPCODE(o72_traceStatus),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o72_kernelGetFunctions),
+ OPCODE(o70_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o72_drawWizImage),
+ OPCODE(o72_debugInput),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o72_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o72_openFile),
+ OPCODE(o72_readFile),
+ /* DC */
+ OPCODE(o72_writeFile),
+ OPCODE(o72_findAllObjects),
+ OPCODE(o72_deleteFile),
+ OPCODE(o72_rename),
+ /* E0 */
+ OPCODE(o60_soundOps),
+ OPCODE(o72_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o72_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o72_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o70_copyString),
+ OPCODE(o70_getStringWidth),
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_appendString),
+ /* F0 */
+ OPCODE(o70_concatString),
+ OPCODE(o70_compareString),
+ OPCODE(o70_isResourceLoaded),
+ OPCODE(o72_readINI),
+ /* F4 */
+ OPCODE(o72_writeINI),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o70_getCharIndexInString),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o72_getResourceSize),
+ OPCODE(o72_setFilePath),
+ OPCODE(o72_setWindowCaption),
+ OPCODE(o70_polygonOps),
+ /* FC */
+ OPCODE(o70_polygonHit),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV72he = opcodes;
+}
+
+void ScummEngine_v72he::executeOpcode(byte i) {
+ OpcodeProcV72he op = _opcodesV72he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v72he::getOpcodeDesc(byte i) {
+ return _opcodesV72he[i].desc;
+}
+
+static const int arrayDataSizes[] = { 0, 1, 4, 8, 8, 16, 32 };
+
+ScummEngine_v72he::ArrayHeader *ScummEngine_v72he::defineArray(int array, int type, int dim2start, int dim2end,
+ int dim1start, int dim1end) {
+ int id;
+ int size;
+ ArrayHeader *ah;
+
+ assert(dim2start >= 0 && dim2start <= dim2end);
+ assert(dim1start >= 0 && dim1start <= dim1end);
+ assert(0 <= type && type <= 6);
+
+
+ if (type == kBitArray || type == kNibbleArray)
+ type = kByteArray;
+
+ nukeArray(array);
+
+ id = findFreeArrayId();
+
+ debug(9,"defineArray (array %d, dim2start %d, dim2end %d dim1start %d dim1end %d", id, dim2start, dim2end, dim1start, dim1end);
+
+ if (array & 0x80000000) {
+ error("Can't define bit variable as array pointer");
+ }
+
+ size = arrayDataSizes[type];
+
+ if (_heversion >= 80)
+ id |= 0x33539000;
+
+ writeVar(array, id);
+
+ if (_heversion >= 80)
+ id &= ~0x33539000;
+
+ size *= dim2end - dim2start + 1;
+ size *= dim1end - dim1start + 1;
+ size >>= 3;
+
+ ah = (ArrayHeader *)res.createResource(rtString, id, size + sizeof(ArrayHeader));
+
+ ah->type = TO_LE_32(type);
+ ah->dim1start = TO_LE_32(dim1start);
+ ah->dim1end = TO_LE_32(dim1end);
+ ah->dim2start = TO_LE_32(dim2start);
+ ah->dim2end = TO_LE_32(dim2end);
+
+ return ah;
+}
+
+int ScummEngine_v72he::readArray(int array, int idx2, int idx1) {
+ debug(9, "readArray (array %d, idx2 %d, idx1 %d)", readVar(array), idx2, idx1);
+
+ if (readVar(array) == 0)
+ error("readArray: Reference to zeroed array pointer");
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+
+ if (ah == NULL || ah->data == NULL)
+ error("readArray: invalid array %d (%d)", array, readVar(array));
+
+ if (idx2 < (int)FROM_LE_32(ah->dim2start) || idx2 > (int)FROM_LE_32(ah->dim2end) ||
+ idx1 < (int)FROM_LE_32(ah->dim1start) || idx1 > (int)FROM_LE_32(ah->dim1end)) {
+ error("readArray: array %d out of bounds: [%d, %d] exceeds [%d..%d, %d..%d]",
+ array, idx1, idx2, FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end),
+ FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end));
+ }
+
+ const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+ (idx2 - FROM_LE_32(ah->dim2start)) - FROM_LE_32(ah->dim1start) + idx1;
+
+ switch (FROM_LE_32(ah->type)) {
+ case kByteArray:
+ case kStringArray:
+ return ah->data[offset];
+
+ case kIntArray:
+ return (int16)READ_LE_UINT16(ah->data + offset * 2);
+
+ case kDwordArray:
+ return (int32)READ_LE_UINT32(ah->data + offset * 4);
+ }
+
+ return 0;
+}
+
+void ScummEngine_v72he::writeArray(int array, int idx2, int idx1, int value) {
+ debug(9, "writeArray (array %d, idx2 %d, idx1 %d, value %d)", readVar(array), idx2, idx1, value);
+
+ if (readVar(array) == 0)
+ error("writeArray: Reference to zeroed array pointer");
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+
+ if (!ah)
+ error("writeArray: Invalid array (%d) reference", readVar(array));
+
+ if (idx2 < (int)FROM_LE_32(ah->dim2start) || idx2 > (int)FROM_LE_32(ah->dim2end) ||
+ idx1 < (int)FROM_LE_32(ah->dim1start) || idx1 > (int)FROM_LE_32(ah->dim1end)) {
+ error("writeArray: array %d out of bounds: [%d, %d] exceeds [%d..%d, %d..%d]",
+ array, idx1, idx2, FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end),
+ FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end));
+ }
+
+ const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+ (idx2 - FROM_LE_32(ah->dim2start)) - FROM_LE_32(ah->dim1start) + idx1;
+
+ switch (FROM_LE_32(ah->type)) {
+ case kByteArray:
+ case kStringArray:
+ ah->data[offset] = value;
+ break;
+
+ case kIntArray:
+ WRITE_LE_UINT16(ah->data + offset * 2, value);
+ break;
+
+ case kDwordArray:
+ WRITE_LE_UINT32(ah->data + offset * 4, value);
+ break;
+ }
+}
+
+int ScummEngine_v72he::setupStringArray(int size) {
+ writeVar(0, 0);
+ defineArray(0, kStringArray, 0, 0, 0, size + 1);
+ writeArray(0, 0, 0, 0);
+ return readVar(0);
+}
+
+void ScummEngine_v72he::readArrayFromIndexFile() {
+ int num;
+ int a, b, c;
+
+ while ((num = _fileHandle->readUint16LE()) != 0) {
+ a = _fileHandle->readUint16LE();
+ b = _fileHandle->readUint16LE();
+ c = _fileHandle->readUint16LE();
+
+ if (c == 1)
+ defineArray(num, kBitArray, 0, a, 0, b);
+ else
+ defineArray(num, kDwordArray, 0, a, 0, b);
+ }
+}
+
+int ScummEngine_v72he::convertFilePath(byte *dst, bool setFilePath) {
+ debug(1, "convertFilePath: original filePath is %s", dst);
+
+ // Switch all \ to / for portablity
+ int len = resStrLen(dst) + 1;
+ for (int i = 0; i < len; i++) {
+ if (dst[i] == '\\')
+ dst[i] = '/';
+ }
+
+ // Strip path
+ int r = 0;
+ if (dst[0] == '.' && dst[1] == '/') {
+ r = 2;
+ } else if (dst[0] == 'c' && dst[1] == ':') {
+ for (r = len; r != 0; r--) {
+ if (dst[r - 1] == '/')
+ break;
+ }
+ }
+
+ if (setFilePath) {
+ char filePath[256];
+ sprintf(filePath, "%s%s", _gameDataPath.c_str(), dst + r);
+ if (!Common::File::exists(filePath)) {
+ sprintf(filePath, "%s%s", _saveFileMan->getSavePath(), dst + r);
+ }
+ strcpy((char *)dst, filePath);
+ debug(1, "convertFilePath: filePath is %s", dst);
+ }
+
+ return r;
+}
+
+void ScummEngine_v72he::copyScriptString(byte *dst, int dstSize) {
+ byte string[1024];
+ byte chr;
+ int pos = 0;
+
+ int array = pop();
+ if (array == -1) {
+ if (_stringLength == 1)
+ error("String stack underflow");
+
+ _stringLength -= 2;
+ while ((chr = _stringBuffer[_stringLength]) != 0) {
+ string[pos] = chr;
+ pos++;
+
+ if (pos > dstSize)
+ error("String too long to pop");
+
+ _stringLength--;
+ }
+
+ string[pos] = 0;
+ _stringLength++;
+
+ // Reverse string
+ int len = resStrLen(string);
+ while (len--)
+ *dst++ = string[len];
+ } else {
+ writeVar(0, array);
+ while ((chr = readArray(0, 0, pos)) != 0) {
+ *dst++ = chr;
+ pos++;
+ }
+ }
+ *dst = 0;
+}
+
+void ScummEngine_v72he::decodeScriptString(byte *dst, bool scriptString) {
+ const byte *src;
+ int args[31];
+ int num, len, val;
+ byte chr, string[1024];
+ memset(args, 0, sizeof(args));
+ memset(string, 0, sizeof(string));
+
+ // Get stack list, plus one
+ num = pop();
+ for (int i = num; i >= 0; i--)
+ args[i] = pop();
+
+ // Get string
+ if (scriptString) {
+ len = resStrLen(_scriptPointer) + 1;
+ memcpy(string, _scriptPointer, len);
+ _scriptPointer += len;
+ } else {
+ copyScriptString(string, sizeof(string));
+ len = resStrLen(string) + 1;
+ }
+
+ // Decode string
+ num = 0;
+ val = 0;
+ while (len--) {
+ chr = string[num++];
+ if (chr == '%') {
+ chr = string[num++];
+ switch(chr) {
+ case 'b':
+ //dst += sprintf((char *)dst, "%b", args[val++]);
+ break;
+ case 'c':
+ *dst++ = args[val++];
+ break;
+ case 'd':
+ dst += sprintf((char *)dst, "%d", args[val++]);
+ break;
+ case 's':
+ src = getStringAddress(args[val++]);
+ if (src) {
+ while (*src != 0)
+ *dst++ = *src++;
+ }
+ break;
+ case 'x':
+ dst += sprintf((char *)dst, "%x", args[val++]);
+ break;
+ default:
+ *dst++ = '%';
+ num--;
+ break;
+ }
+ } else {
+ *dst++ = chr;
+ }
+ }
+ *dst = 0;
+}
+
+byte *ScummEngine_v70he::heFindResourceData(uint32 tag, byte *ptr) {
+ ptr = heFindResource(tag, ptr);
+
+ if (ptr == NULL)
+ return NULL;
+ return ptr + _resourceHeaderSize;
+}
+
+byte *ScummEngine_v70he::heFindResource(uint32 tag, byte *searchin) {
+ uint32 curpos, totalsize, size;
+
+ debugC(DEBUG_RESOURCE, "heFindResource(%s, %lx)", tag2str(tag), searchin);
+
+ assert(searchin);
+ searchin += 4;
+ _resourceLastSearchSize = totalsize = READ_BE_UINT32(searchin);
+ curpos = 8;
+ searchin += 4;
+
+ while (curpos < totalsize) {
+ if (READ_UINT32(searchin) == tag) {
+ return searchin;
+ }
+
+ size = READ_BE_UINT32(searchin + 4);
+ if ((int32)size <= 0) {
+ error("(%s) Not found in %d... illegal block len %d", tag2str(tag), 0, size);
+ return NULL;
+ }
+
+ curpos += size;
+ searchin += size;
+ }
+
+ return NULL;
+}
+
+byte *ScummEngine_v70he::findWrappedBlock(uint32 tag, byte *ptr, int state, bool errorFlag) {
+ if (READ_UINT32(ptr) == MKID('MULT')) {
+ byte *offs, *wrap;
+ uint32 size;
+
+ wrap = heFindResource(MKID('WRAP'), ptr);
+ if (wrap == NULL)
+ return NULL;
+
+ offs = heFindResourceData(MKID('OFFS'), wrap);
+ if (offs == NULL)
+ return NULL;
+
+ size = getResourceDataSize(offs) / 4;
+ assert((uint32)state <= (uint32)size);
+
+
+ offs += READ_LE_UINT32(offs + state * sizeof(uint32));
+ offs = heFindResourceData(tag, offs - 8);
+ if (offs)
+ return offs;
+
+ offs = heFindResourceData(MKID('DEFA'), ptr);
+ if (offs == NULL)
+ return NULL;
+
+ return heFindResourceData(tag, offs - 8);
+ } else {
+ return heFindResourceData(tag, ptr);
+ }
+}
+
+int ScummEngine_v72he::findObject(int x, int y, int num, int *args) {
+ int b, cls, i, result;
+
+ for (i = 1; i < _numLocalObjects; i++) {
+ result = 0;
+ if ((_objs[i].obj_nr < 1) || getClass(_objs[i].obj_nr, kObjectClassUntouchable))
+ continue;
+
+ // Check polygon bounds
+ if (_wiz->polygonDefined(_objs[i].obj_nr)) {
+ if (_wiz->polygonHit(_objs[i].obj_nr, x, y))
+ result = _objs[i].obj_nr;
+ else if (VAR_POLYGONS_ONLY != 0xFF && VAR(VAR_POLYGONS_ONLY))
+ continue;
+ }
+
+ if (!result) {
+ // Check object bounds
+ if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x &&
+ _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y)
+ result = _objs[i].obj_nr;
+ }
+
+ if (result) {
+ if (!num)
+ return result;
+
+ // Check object class
+ cls = args[0];
+ b = getClass(_objs[i].obj_nr, cls);
+ if ((cls & 0x80 && b) || (!(cls & 0x80) && !b))
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+void ScummEngine_v72he::o72_pushDWord() {
+ push(fetchScriptDWordSigned());
+}
+
+void ScummEngine_v72he::o72_getScriptString() {
+ byte chr;
+
+ while ((chr = fetchScriptByte()) != 0) {
+ _stringBuffer[_stringLength] = chr;
+ _stringLength++;
+
+ if (_stringLength >= 4096)
+ error("String stack overflow");
+ }
+
+ _stringBuffer[_stringLength] = 0;
+ _stringLength++;
+}
+
+void ScummEngine_v72he::o72_isAnyOf() {
+ int args[128];
+ int num, value;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ value = pop();
+
+ for (int i = 0; i < num; i++) {
+ if (args[i] == value) {
+ push(1);
+ return;
+ }
+ }
+
+ push(0);
+}
+
+void ScummEngine_v72he::o72_resetCutscene() {
+ int idx;
+
+ idx = vm.cutSceneStackPointer;
+ vm.cutSceneStackPointer = 0;
+ vm.cutScenePtr[idx] = 0;
+ vm.cutSceneScript[idx] = 0;
+
+ VAR(VAR_OVERRIDE) = 0;
+}
+
+void ScummEngine_v72he::o72_findObjectWithClassOf() {
+ int args[16], num;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ int y = pop();
+ int x = pop();
+ int r = findObject(x, y, num, args);
+ push(r);
+}
+
+void ScummEngine_v72he::o72_getObjectImageX() {
+ int object = pop();
+ int objnum = getObjectIndex(object);
+
+ if (objnum == -1) {
+ push(0);
+ return;
+ }
+
+ push(_objs[objnum].x_pos / 8);
+}
+
+void ScummEngine_v72he::o72_getObjectImageY() {
+ int object = pop();
+ int objnum = getObjectIndex(object);
+
+ if (objnum == -1) {
+ push(0);
+ return;
+ }
+
+ push(_objs[objnum].y_pos / 8);
+}
+
+void ScummEngine_v72he::o72_captureWizImage() {
+ Common::Rect grab;
+ grab.bottom = pop() + 1;
+ grab.right = pop() + 1;
+ grab.top = pop();
+ grab.left = pop();
+ _wiz->captureWizImage(pop(), grab, false, true);
+}
+
+void ScummEngine_v72he::o72_getTimer() {
+ int timer = pop();
+ byte cmd = fetchScriptByte();
+
+ if (cmd == 10 || cmd == 50) {
+ push(getHETimer(timer));
+ } else {
+ push(0);
+ }
+}
+
+void ScummEngine_v72he::o72_setTimer() {
+ int timer = pop();
+ byte cmd = fetchScriptByte();
+
+ if (cmd == 158 || cmd == 61) {
+ setHETimer(timer);
+ } else {
+ error("TIMER command %d?", cmd);
+ }
+}
+
+void ScummEngine_v72he::o72_getSoundPosition() {
+ int snd = pop();
+ push(_sound->getSoundPos(snd));
+}
+
+void ScummEngine_v72he::o72_startScript() {
+ int args[25];
+ int script;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = fetchScriptByte();
+ runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
+}
+
+void ScummEngine_v72he::o72_startObject() {
+ int args[25];
+ int script, entryp;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ entryp = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ runObjectScript(script, entryp, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
+}
+
+void ScummEngine_v72he::o72_drawObject() {
+ byte subOp = fetchScriptByte();
+ int state, y, x;
+
+ switch (subOp) {
+ case 62:
+ state = pop();
+ y = pop();
+ x = pop();
+ break;
+ case 63:
+ state = pop();
+ if (state == 0)
+ state = 1;
+ y = x = -100;
+ break;
+ case 65:
+ state = 1;
+ y = pop();
+ x = pop();
+ break;
+ default:
+ error("o72_drawObject: default case %d", subOp);
+ }
+
+ int object = pop();
+ int objnum = getObjectIndex(object);
+ if (objnum == -1)
+ return;
+
+ if (y != -100 && x != -100) {
+ _objs[objnum].x_pos = x * 8;
+ _objs[objnum].y_pos = y * 8;
+ }
+
+ if (state != -1) {
+ addObjectToDrawQue(objnum);
+ putState(object, state);
+ }
+}
+
+void ScummEngine_v72he::o72_printWizImage() {
+ WizImage wi;
+ wi.resNum = pop();
+ wi.x1 = wi.y1 = 0;
+ wi.state = 0;
+ wi.flags = kWIFPrint;
+ _wiz->displayWizImage(&wi);
+}
+
+void ScummEngine_v72he::o72_getArrayDimSize() {
+ byte subOp = fetchScriptByte();
+ int32 val1, val2;
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(fetchScriptWord()));
+ if (!ah) {
+ push(0);
+ return;
+ }
+
+ switch (subOp) {
+ case 1:
+ case 3:
+ val1 = FROM_LE_32(ah->dim1end);
+ val2 = FROM_LE_32(ah->dim1start);
+ push(val1 - val2 + 1);
+ break;
+ case 2:
+ val1 = FROM_LE_32(ah->dim2end);
+ val2 = FROM_LE_32(ah->dim2start);
+ push(val1 - val2 + 1);
+ break;
+ case 4:
+ push(FROM_LE_32(ah->dim1start));
+ break;
+ case 5:
+ push(FROM_LE_32(ah->dim1end));
+ break;
+ case 6:
+ push(FROM_LE_32(ah->dim2start));
+ break;
+ case 7:
+ push(FROM_LE_32(ah->dim2end));
+ break;
+ default:
+ error("o72_getArrayDimSize: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_getNumFreeArrays() {
+ byte **addr = res.address[rtString];
+ int i, num = 0;
+
+ for (i = 1; i < _numArray; i++) {
+ if (!addr[i])
+ num++;
+ }
+
+ push (num);
+}
+
+void ScummEngine_v72he::o72_roomOps() {
+ int a, b, c, d, e;
+ byte filename[100];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 172: // SO_ROOM_SCROLL
+ b = pop();
+ a = pop();
+ if (a < (_screenWidth / 2))
+ a = (_screenWidth / 2);
+ if (b < (_screenWidth / 2))
+ b = (_screenWidth / 2);
+ if (a > _roomWidth - (_screenWidth / 2))
+ a = _roomWidth - (_screenWidth / 2);
+ if (b > _roomWidth - (_screenWidth / 2))
+ b = _roomWidth - (_screenWidth / 2);
+ VAR(VAR_CAMERA_MIN_X) = a;
+ VAR(VAR_CAMERA_MAX_X) = b;
+ break;
+
+ case 174: // SO_ROOM_SCREEN
+ b = pop();
+ a = pop();
+ initScreens(a, _screenHeight);
+ break;
+
+ case 175: // SO_ROOM_PALETTE
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ setPalColor(d, a, b, c);
+ break;
+
+ case 179: // SO_ROOM_INTENSITY
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, a, a, b, c);
+ break;
+
+ case 180: // SO_ROOM_SAVEGAME
+ _saveTemporaryState = true;
+ _saveLoadSlot = pop();
+ _saveLoadFlag = pop();
+ break;
+
+ case 181: // SO_ROOM_FADE
+ // Defaults to 1 but doesn't use fade effects
+ a = pop();
+ break;
+
+ case 182: // SO_RGB_ROOM_INTENSITY
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, b, c, d, e);
+ break;
+
+ case 213: // SO_ROOM_NEW_PALETTE
+ a = pop();
+ setPalette(a);
+ break;
+
+ case 220:
+ a = pop();
+ b = pop();
+ copyPalColor(a, b);
+ break;
+
+ case 221:
+ copyScriptString(filename, sizeof(filename));
+ _saveLoadFlag = pop();
+ _saveLoadSlot = 1;
+ _saveTemporaryState = true;
+ break;
+
+ case 234:
+ b = pop();
+ a = pop();
+ swapObjects(a, b);
+ break;
+
+ case 236:
+ b = pop();
+ a = pop();
+ setRoomPalette(a, b);
+ break;
+
+ default:
+ error("o72_roomOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_actorOps() {
+ Actor *a;
+ int i, j, k;
+ int args[32];
+ byte string[256];
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 197) {
+ _curActor = pop();
+ return;
+ }
+
+ a = derefActorSafe(_curActor, "o72_actorOps");
+ if (!a)
+ return;
+
+ switch (subOp) {
+ case 21: // HE 80+
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; ++i) {
+ a->setUserCondition(args[i] & 0x7F, args[i] & 0x80);
+ }
+ break;
+ case 24: // HE 80+
+ k = pop();
+ if (k == 0)
+ k = _rnd.getRandomNumberRng(1, 10);
+ a->_heNoTalkAnimation = 1;
+ a->setTalkCondition(k);
+ break;
+ case 43: // HE 90+
+ // HE games use reverse order of layering, so we adjust
+ a->_layer = -pop();
+ a->_needRedraw = true;
+ break;
+ case 64:
+ _actorClipOverride.bottom = pop();
+ _actorClipOverride.right = pop();
+ _actorClipOverride.top = pop();
+ _actorClipOverride.left = pop();
+ break;
+ case 67: // HE 99+
+ a->_clipOverride.bottom = pop();
+ a->_clipOverride.right = pop();
+ a->_clipOverride.top = pop();
+ a->_clipOverride.left = pop();
+ break;
+ case 65: // HE 98+
+ j = pop();
+ i = pop();
+ a->putActor(i, j, a->_room);
+ break;
+ case 68: // HE 90+
+ k = pop();
+ debug(0,"o72_actorOps: case 68 (%d)", k);
+ break;
+ case 76: // SO_COSTUME
+ a->setActorCostume(pop());
+ break;
+ case 77: // SO_STEP_DIST
+ j = pop();
+ i = pop();
+ a->setActorWalkSpeed(i, j);
+ break;
+ case 78: // SO_SOUND
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; i++)
+ a->_sound[i] = args[i];
+ break;
+ case 79: // SO_WALK_ANIMATION
+ a->_walkFrame = pop();
+ break;
+ case 80: // SO_TALK_ANIMATION
+ a->_talkStopFrame = pop();
+ a->_talkStartFrame = pop();
+ break;
+ case 81: // SO_STAND_ANIMATION
+ a->_standFrame = pop();
+ break;
+ case 82: // SO_ANIMATION
+ // dummy case in scumm6
+ pop();
+ pop();
+ pop();
+ break;
+ case 83: // SO_DEFAULT
+ a->initActor(0);
+ break;
+ case 84: // SO_ELEVATION
+ a->setElevation(pop());
+ break;
+ case 85: // SO_ANIMATION_DEFAULT
+ a->_initFrame = 1;
+ a->_walkFrame = 2;
+ a->_standFrame = 3;
+ a->_talkStartFrame = 4;
+ a->_talkStopFrame = 5;
+ break;
+ case 86: // SO_PALETTE
+ j = pop();
+ i = pop();
+ checkRange(255, 0, i, "Illegal palette slot %d");
+ a->remapActorPaletteColor(i, j);
+ a->_needRedraw = true;
+ break;
+ case 87: // SO_TALK_COLOR
+ a->_talkColor = pop();
+ break;
+ case 88: // SO_ACTOR_NAME
+ copyScriptString(string, sizeof(string));
+ loadPtrToResource(rtActorName, a->_number, string);
+ break;
+ case 89: // SO_INIT_ANIMATION
+ a->_initFrame = pop();
+ break;
+ case 91: // SO_ACTOR_WIDTH
+ a->_width = pop();
+ break;
+ case 92: // SO_SCALE
+ i = pop();
+ a->setScale(i, i);
+ break;
+ case 93: // SO_NEVER_ZCLIP
+ a->_forceClip = 0;
+ break;
+ case 94: // SO_ALWAYS_ZCLIP
+ a->_forceClip = pop();
+ break;
+ case 95: // SO_IGNORE_BOXES
+ a->_ignoreBoxes = 1;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 96: // SO_FOLLOW_BOXES
+ a->_ignoreBoxes = 0;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 97: // SO_ANIMATION_SPEED
+ a->setAnimSpeed(pop());
+ break;
+ case 98: // SO_SHADOW
+ a->_heXmapNum = pop();
+ a->_needRedraw = true;
+ break;
+ case 99: // SO_TEXT_OFFSET
+ a->_talkPosY = pop();
+ a->_talkPosX = pop();
+ break;
+ case 156: // HE 72+
+ a->_charset = pop();
+ break;
+ case 175: // HE 99+
+ a->_hePaletteNum = pop();
+ a->_needRedraw = true;
+ break;
+ case 198: // SO_ACTOR_VARIABLE
+ i = pop();
+ a->setAnimVar(pop(), i);
+ break;
+ case 215: // SO_ACTOR_IGNORE_TURNS_ON
+ a->_ignoreTurns = true;
+ break;
+ case 216: // SO_ACTOR_IGNORE_TURNS_OFF
+ a->_ignoreTurns = false;
+ break;
+ case 217: // SO_ACTOR_NEW
+ a->initActor(2);
+ break;
+ case 218:
+ a->drawActorToBackBuf(a->_pos.x, a->_pos.y);
+ break;
+ case 219:
+ a->_drawToBackBuf = false;
+ a->_needRedraw = true;
+ a->_needBgReset = true;
+ break;
+ case 225:
+ {
+ copyScriptString(string, sizeof(string));
+ int slot = pop();
+
+ int len = resStrLen(string) + 1;
+ memcpy(a->_heTalkQueue[slot].sentence, string, len);
+
+ a->_heTalkQueue[slot].posX = a->_talkPosX;
+ a->_heTalkQueue[slot].posY = a->_talkPosY;
+ a->_heTalkQueue[slot].color = a->_talkColor;
+ break;
+ }
+ default:
+ error("o72_actorOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_verbOps() {
+ int slot, a, b;
+ VerbSlot *vs;
+ byte name[200];
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 196) {
+ _curVerb = pop();
+ _curVerbSlot = getVerbSlot(_curVerb, 0);
+ checkRange(_numVerbs - 1, 0, _curVerbSlot, "Illegal new verb slot %d");
+ return;
+ }
+ vs = &_verbs[_curVerbSlot];
+ slot = _curVerbSlot;
+ switch (subOp) {
+ case 124: // SO_VERB_IMAGE
+ a = pop();
+ if (_curVerbSlot) {
+ setVerbObject(_roomResource, a, slot);
+ vs->type = kImageVerbType;
+ vs->imgindex = a;
+ }
+ break;
+ case 125: // SO_VERB_NAME
+ copyScriptString(name, sizeof(name));
+ loadPtrToResource(rtVerb, slot, name);
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 126: // SO_VERB_COLOR
+ vs->color = pop();
+ break;
+ case 127: // SO_VERB_HICOLOR
+ vs->hicolor = pop();
+ break;
+ case 128: // SO_VERB_AT
+ vs->curRect.top = pop();
+ vs->curRect.left = pop();
+ break;
+ case 129: // SO_VERB_ON
+ vs->curmode = 1;
+ break;
+ case 130: // SO_VERB_OFF
+ vs->curmode = 0;
+ break;
+ case 131: // SO_VERB_DELETE
+ slot = getVerbSlot(pop(), 0);
+ killVerb(slot);
+ break;
+ case 132: // SO_VERB_NEW
+ slot = getVerbSlot(_curVerb, 0);
+ if (slot == 0) {
+ for (slot = 1; slot < _numVerbs; slot++) {
+ if (_verbs[slot].verbid == 0)
+ break;
+ }
+ if (slot == _numVerbs)
+ error("Too many verbs");
+ _curVerbSlot = slot;
+ }
+ vs = &_verbs[slot];
+ vs->verbid = _curVerb;
+ vs->color = 2;
+ vs->hicolor = 0;
+ vs->dimcolor = 8;
+ vs->type = kTextVerbType;
+ vs->charset_nr = _string[0]._default.charset;
+ vs->curmode = 0;
+ vs->saveid = 0;
+ vs->key = 0;
+ vs->center = 0;
+ vs->imgindex = 0;
+ break;
+ case 133: // SO_VERB_DIMCOLOR
+ vs->dimcolor = pop();
+ break;
+ case 134: // SO_VERB_DIM
+ vs->curmode = 2;
+ break;
+ case 135: // SO_VERB_KEY
+ vs->key = pop();
+ break;
+ case 136: // SO_VERB_CENTER
+ vs->center = 1;
+ break;
+ case 137: // SO_VERB_NAME_STR
+ a = pop();
+ if (a == 0) {
+ loadPtrToResource(rtVerb, slot, (const byte *)"");
+ } else {
+ loadPtrToResource(rtVerb, slot, getStringAddress(a));
+ }
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 139: // SO_VERB_IMAGE_IN_ROOM
+ b = pop();
+ a = pop();
+
+ if (slot && a != vs->imgindex) {
+ setVerbObject(b, a, slot);
+ vs->type = kImageVerbType;
+ vs->imgindex = a;
+ }
+ break;
+ case 140: // SO_VERB_BAKCOLOR
+ vs->bkcolor = pop();
+ break;
+ case 255:
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+ break;
+ default:
+ error("o72_verbops: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_findObject() {
+ int y = pop();
+ int x = pop();
+ int r = findObject(x, y, 0, 0);
+ push(r);
+}
+
+void ScummEngine_v72he::o72_arrayOps() {
+ ArrayHeader *ah;
+ byte string[1024];
+ int dim1end, dim1start, dim2end, dim2start;
+ int id, len, b, c, list[128];
+ int offs, tmp, tmp2;
+ uint tmp3;
+
+ byte subOp = fetchScriptByte();
+ int array = fetchScriptWord();
+ debug(9,"o72_arrayOps: array %d case %d", array, subOp);
+
+ switch (subOp) {
+ case 7: // SO_ASSIGN_STRING
+ copyScriptString(string, sizeof(string));
+ len = resStrLen(string);
+ ah = defineArray(array, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ break;
+
+ case 126:
+ len = getStackList(list, ARRAYSIZE(list));
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
+ }
+ tmp2 = 0;
+ while (dim2start <= dim2end) {
+ tmp = dim1start;
+ while (tmp <= dim1end) {
+ writeArray(array, dim2start, tmp, list[tmp2++]);
+ if (tmp2 == len)
+ tmp2 = 0;
+ tmp++;
+ }
+ dim2start++;
+ }
+ break;
+ case 127:
+ {
+ int a2_dim1end = pop();
+ int a2_dim1start = pop();
+ int a2_dim2end = pop();
+ int a2_dim2start = pop();
+ int array2 = fetchScriptWord();
+ int a1_dim1end = pop();
+ int a1_dim1start = pop();
+ int a1_dim2end = pop();
+ int a1_dim2start = pop();
+ if (a1_dim1end - a1_dim1start != a2_dim1end - a2_dim1start || a2_dim2end - a2_dim2start != a1_dim2end - a1_dim2start) {
+ error("Source and dest ranges size are mismatched");
+ }
+ copyArray(array, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end, array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
+ }
+ break;
+ case 128:
+ b = pop();
+ c = pop();
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
+ }
+
+ offs = (b >= c) ? 1 : -1;
+ tmp2 = c;
+ tmp3 = c - b + 1;
+ while (dim2start <= dim2end) {
+ tmp = dim1start;
+ while (tmp <= dim1end) {
+ writeArray(array, dim2start, tmp, tmp2);
+ if (--tmp3 == 0) {
+ tmp2 = c;
+ tmp3 = c - b + 1;
+ } else {
+ tmp2 += offs;
+ }
+ tmp++;
+ }
+ dim2start++;
+ }
+ break;
+ case 194:
+ decodeScriptString(string);
+ len = resStrLen(string);
+ ah = defineArray(array, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ break;
+ case 208: // SO_ASSIGN_INT_LIST
+ b = pop();
+ c = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, 0, 0, 0, b + c - 1);
+ }
+ while (c--) {
+ writeArray(array, 0, b + c, pop());
+ }
+ break;
+ case 212: // SO_ASSIGN_2DIM_LIST
+ len = getStackList(list, ARRAYSIZE(list));
+ id = readVar(array);
+ if (id == 0)
+ error("Must DIM a two dimensional array before assigning");
+ c = pop();
+ while (--len >= 0) {
+ writeArray(array, c, len, list[len]);
+ }
+ break;
+ default:
+ error("o72_arrayOps: default case %d (array %d)", subOp, array);
+ }
+}
+
+void ScummEngine_v72he::o72_systemOps() {
+ byte string[1024];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 22: // HE80+
+ clearDrawObjectQueue();
+ break;
+ case 26: // HE80+
+ gdi.copyVirtScreenBuffers(Common::Rect(_screenWidth, _screenHeight));
+ updatePalette();
+ break;
+ case 158:
+ restart();
+ break;
+ case 160:
+ // Confirm shutdown
+ shutDown();
+ break;
+ case 244:
+ shutDown();
+ break;
+ case 251:
+ copyScriptString(string, sizeof(string));
+ debug(0, "Start executable (%s)", string);
+ break;
+ case 252:
+ copyScriptString(string, sizeof(string));
+ debug(0, "Start game (%s)", string);
+ break;
+ default:
+ error("o72_systemOps invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_talkActor() {
+ Actor *a;
+
+ int act = pop();
+
+ _string[0].loadDefault();
+
+ // A value of 225 can occur when examining the gold in the mine of pajama, after mining the gold.
+ // This is a script bug, the script should set the subtitle color, not actor number.
+ // This script bug was fixed in the updated version of pajama.
+ if (act == 225) {
+ _string[0].color = act;
+ } else {
+ _actorToPrintStrFor = act;
+ if (_actorToPrintStrFor != 0xFF) {
+ a = derefActor(_actorToPrintStrFor, "o72_talkActor");
+ _string[0].color = a->_talkColor;
+ }
+ }
+
+ actorTalk(_scriptPointer);
+
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+}
+
+void ScummEngine_v72he::o72_talkEgo() {
+ push(VAR(VAR_EGO));
+ o72_talkActor();
+}
+
+void ScummEngine_v72he::o72_dimArray() {
+ int data;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 2: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 3: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 4: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 5: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 6:
+ data = kDwordArray;
+ break;
+ case 7: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ case 204: // SO_UNDIM_ARRAY
+ nukeArray(fetchScriptWord());
+ return;
+ default:
+ error("o72_dimArray: default case %d", subOp);
+ }
+
+ defineArray(fetchScriptWord(), data, 0, 0, 0, pop());
+}
+
+
+void ScummEngine_v72he::o72_dim2dimArray() {
+ int data, dim1end, dim2end;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 2: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 3: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 4: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 5: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 6:
+ data = kDwordArray;
+ break;
+ case 7: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ default:
+ error("o72_dim2dimArray: default case %d", subOp);
+ }
+
+ dim1end = pop();
+ dim2end = pop();
+ defineArray(fetchScriptWord(), data, 0, dim2end, 0, dim1end);
+}
+
+void ScummEngine_v72he::o72_traceStatus() {
+ byte string[80];
+
+ copyScriptString(string, sizeof(string));
+ pop();
+}
+
+void ScummEngine_v72he::o72_kernelGetFunctions() {
+ int args[29];
+ ArrayHeader *ah;
+ getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1:
+ writeVar(0, 0);
+ ah = defineArray(0, kByteArray, 0, 0, 0, virtScreenSave(0, args[1], args[2], args[3], args[4]));
+ virtScreenSave(ah->data, args[1], args[2], args[3], args[4]);
+ push(readVar(0));
+ break;
+ default:
+ error("o72_kernelGetFunctions: default case %d", args[0]);
+ }
+}
+
+void ScummEngine_v72he::o72_drawWizImage() {
+ WizImage wi;
+ wi.flags = pop();
+ wi.y1 = pop();
+ wi.x1 = pop();
+ wi.resNum = pop();
+ wi.state = 0;
+ _wiz->displayWizImage(&wi);
+}
+
+void ScummEngine_v72he::o72_debugInput() {
+ byte string[255];
+
+ copyScriptString(string, sizeof(string));
+ int len = resStrLen(string) + 1;
+
+ writeVar(0, 0);
+ ArrayHeader *ah = defineArray(0, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ push(readVar(0));
+ debug(1,"o72_debugInput: String %s", string);
+}
+
+void ScummEngine_v72he::o72_jumpToScript() {
+ int args[25];
+ int script;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = fetchScriptByte();
+ stopObjectCode();
+ runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
+}
+
+void ScummEngine_v72he::o72_openFile() {
+ int mode, slot, i;
+ byte filename[256];
+
+ mode = pop();
+ copyScriptString(filename, sizeof(filename));
+
+ debug(1,"Original filename %s", filename);
+
+ // There are Macintosh specific versions of HE7.2 games.
+ if (_heversion >= 80 && _platform == Common::kPlatformMacintosh) {
+ // Work around for filename difference in HE7 file, needs to
+ // open 'Water (7)' instead of 'Water Worries (7)'.
+ if (_gameId == GID_WATER && _heversion == 99 && !strcmp((char *)filename, "Water.he7")) {
+ strcpy((char *)filename, "Water (7)");
+ } else {
+ char buf1[128];
+ buf1[0] = '\0';
+ generateSubstResFileName((char *)filename, buf1, sizeof(buf1));
+ if (buf1[0]) {
+ strcpy((char *)filename, buf1);
+ }
+ }
+ }
+
+ int r = convertFilePath(filename);
+ debug(1,"Final filename to %s", filename + r);
+
+ slot = -1;
+ for (i = 1; i < 17; i++) {
+ if (_hFileTable[i].isOpen() == false) {
+ slot = i;
+ break;
+ }
+ }
+
+ if (slot != -1) {
+ switch(mode) {
+ case 1:
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileReadMode, _saveFileMan->getSavePath());
+ if (_hFileTable[slot].isOpen() == false)
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileReadMode, _gameDataPath.c_str());
+ break;
+ case 2:
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileWriteMode, _saveFileMan->getSavePath());
+ break;
+ default:
+ error("o72_openFile(): wrong open file mode %d", mode);
+ }
+
+ if (_hFileTable[slot].isOpen() == false)
+ slot = -1;
+
+ }
+ debug(1, "o72_openFile: slot %d, mode %d", slot, mode);
+ push(slot);
+}
+
+int ScummEngine_v72he::readFileToArray(int slot, int32 size) {
+ if (size == 0)
+ size = _hFileTable[slot].size() - _hFileTable[slot].pos();
+
+ writeVar(0, 0);
+ ArrayHeader *ah = defineArray(0, kByteArray, 0, 0, 0, size);
+
+ if (_hFileTable[slot].isOpen())
+ _hFileTable[slot].read(ah->data, size + 1);
+
+ return readVar(0);
+}
+
+void ScummEngine_v72he::o72_readFile() {
+ int slot, val;
+ int32 size;
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 4:
+ slot = pop();
+ val = _hFileTable[slot].readByte();
+ push(val);
+ break;
+ case 5:
+ slot = pop();
+ val = _hFileTable[slot].readUint16LE();
+ push(val);
+ break;
+ case 6:
+ slot = pop();
+ val = _hFileTable[slot].readUint32LE();
+ push(val);
+ break;
+ case 8:
+ fetchScriptByte();
+ size = pop();
+ slot = pop();
+ val = readFileToArray(slot, size);
+ push(val);
+ break;
+ default:
+ error("o72_readFile: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::writeFileFromArray(int slot, int32 resID) {
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, resID);
+ int32 size = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+ (FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
+
+ _hFileTable[slot].write(ah->data, size);
+}
+
+void ScummEngine_v72he::o72_writeFile() {
+ int32 resID = pop();
+ int slot = pop();
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 4:
+ _hFileTable[slot].writeByte(resID);
+ break;
+ case 5:
+ _hFileTable[slot].writeUint16LE(resID);
+ break;
+ case 6:
+ _hFileTable[slot].writeUint32LE(resID);
+ break;
+ case 8:
+ fetchScriptByte();
+ writeFileFromArray(slot, resID);
+ break;
+ default:
+ error("o72_writeFile: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_findAllObjects() {
+ int room = pop();
+ int i;
+
+ if (room != _currentRoom)
+ error("o72_findAllObjects: current room is not %d", room);
+
+ writeVar(0, 0);
+ defineArray(0, kDwordArray, 0, 0, 0, _numLocalObjects);
+ writeArray(0, 0, 0, _numLocalObjects);
+
+ for (i = 1; i < _numLocalObjects; i++) {
+ writeArray(0, 0, i, _objs[i].obj_nr);
+ }
+
+ push(readVar(0));
+}
+
+void ScummEngine_v72he::o72_deleteFile() {
+ byte filename[256];
+
+ copyScriptString(filename, sizeof(filename));
+ debug(1, "stub o72_deleteFile(%s)", filename);
+}
+
+void ScummEngine_v72he::o72_rename() {
+ byte oldFilename[100],newFilename[100];
+
+ copyScriptString(newFilename, sizeof(newFilename));
+ copyScriptString(oldFilename, sizeof(oldFilename));
+
+ debug(1, "stub o72_rename(%s to %s)", oldFilename, newFilename);
+}
+
+void ScummEngine_v72he::o72_getPixel() {
+ byte area;
+
+ int y = pop();
+ int x = pop();
+ byte subOp = fetchScriptByte();
+
+ VirtScreen *vs = findVirtScreen(y);
+ if (vs == NULL || x > _screenWidth - 1 || x < 0) {
+ push(-1);
+ return;
+ }
+
+ switch (subOp) {
+ case 9: // HE 100
+ case 218:
+ area = *vs->getBackPixels(x, y - vs->topline);
+ break;
+ case 8: // HE 100
+ case 219:
+ area = *vs->getPixels(x, y - vs->topline);
+ break;
+ default:
+ error("o72_getPixel: default case %d", subOp);
+ }
+ push(area);
+}
+
+void ScummEngine_v72he::o72_pickVarRandom() {
+ int num;
+ int args[100];
+ int32 dim1end;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ int value = fetchScriptWord();
+
+ if (readVar(value) == 0) {
+ defineArray(value, kDwordArray, 0, 0, 0, num);
+ if (num > 0) {
+ int16 counter = 0;
+ do {
+ writeArray(value, 0, counter + 1, args[counter]);
+ } while (++counter < num);
+ }
+
+ shuffleArray(value, 1, num);
+ writeArray(value, 0, 0, 2);
+ push(readArray(value, 0, 1));
+ return;
+ }
+
+ num = readArray(value, 0, 0);
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(value));
+ dim1end = FROM_LE_32(ah->dim1end);
+
+ if (dim1end < num) {
+ int32 var_2 = readArray(value, 0, num - 1);
+ shuffleArray(value, 1, dim1end);
+ if (readArray(value, 0, 1) == var_2) {
+ num = 2;
+ } else {
+ num = 1;
+ }
+ }
+
+ writeArray(value, 0, 0, num + 1);
+ push(readArray(value, 0, num));
+}
+
+void ScummEngine_v72he::o72_redimArray() {
+ int newX, newY;
+ newY = pop();
+ newX = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 5:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kIntArray);
+ break;
+ case 4:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kByteArray);
+ break;
+ case 6:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kDwordArray);
+ break;
+ default:
+ error("o72_redimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::redimArray(int arrayId, int newDim2start, int newDim2end,
+ int newDim1start, int newDim1end, int type) {
+ int newSize, oldSize;
+
+ if (readVar(arrayId) == 0)
+ error("redimArray: Reference to zeroed array pointer");
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(arrayId));
+
+ if (!ah)
+ error("redimArray: Invalid array (%d) reference", readVar(arrayId));
+
+ newSize = arrayDataSizes[type];
+ oldSize = arrayDataSizes[FROM_LE_32(ah->type)];
+
+ newSize *= (newDim1end - newDim1start + 1) * (newDim2end - newDim2start + 1);
+ oldSize *= (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+ (FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
+
+ newSize >>= 3;
+ oldSize >>= 3;
+
+ if (newSize != oldSize)
+ error("redimArray: array %d redim mismatch", readVar(arrayId));
+
+ ah->type = TO_LE_32(type);
+ ah->dim1start = TO_LE_32(newDim1start);
+ ah->dim1end = TO_LE_32(newDim1end);
+ ah->dim2start = TO_LE_32(newDim2start);
+ ah->dim2end = TO_LE_32(newDim2end);
+}
+
+void ScummEngine_v72he::checkArrayLimits(int array, int dim2start, int dim2end, int dim1start, int dim1end) {
+ if (dim1end < dim1start) {
+ error("Across max %d smaller than min %d", dim1end, dim1start);
+ }
+ if (dim2end < dim2start) {
+ error("Down max %d smaller than min %d", dim2end, dim2start);
+ }
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+ assert(ah);
+ if (ah->dim2start > dim2start || ah->dim2end < dim2end || ah->dim1start > dim1start || ah->dim1end < dim1end) {
+ error("Invalid array access (%d,%d,%d,%d) limit (%d,%d,%d,%d)", dim2start, dim2end, dim1start, dim1end, ah->dim2start, ah->dim2end, ah->dim1start, ah->dim1end);
+ }
+}
+
+void ScummEngine_v72he::copyArray(int array1, int a1_dim2start, int a1_dim2end, int a1_dim1start, int a1_dim1end,
+ int array2, int a2_dim2start, int a2_dim2end, int a2_dim1start, int a2_dim1end)
+{
+ byte *dst, *src;
+ int dstPitch, srcPitch;
+ int rowSize;
+ checkArrayLimits(array1, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end);
+ checkArrayLimits(array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
+ int a12_num = a1_dim2end - a1_dim2start + 1;
+ int a11_num = a1_dim1end - a1_dim1start + 1;
+ int a22_num = a2_dim2end - a2_dim2start + 1;
+ int a21_num = a2_dim1end - a2_dim1start + 1;
+ if (a22_num != a12_num || a21_num != a11_num) {
+ error("Operation size mismatch (%d vs %d)(%d vs %d)", a12_num, a22_num, a11_num, a21_num);
+ }
+
+ if (array1 != array2) {
+ ArrayHeader *ah1 = (ArrayHeader *)getResourceAddress(rtString, readVar(array1));
+ assert(ah1);
+ ArrayHeader *ah2 = (ArrayHeader *)getResourceAddress(rtString, readVar(array2));
+ assert(ah2);
+ if (FROM_LE_32(ah1->type) == FROM_LE_32(ah2->type)) {
+ copyArrayHelper(ah1, a1_dim2start, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
+ copyArrayHelper(ah2, a2_dim2start, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
+ for (; a1_dim2start <= a1_dim2end; ++a1_dim2start) {
+ memcpy(dst, src, rowSize);
+ dst += dstPitch;
+ src += srcPitch;
+ }
+ } else {
+ for (; a1_dim2start <= a1_dim2end; ++a1_dim2start, ++a2_dim2start) {
+ int a2dim1 = a2_dim1start;
+ int a1dim1 = a1_dim1start;
+ for (; a1dim1 <= a1_dim1end; ++a1dim1, ++a2dim1) {
+ int val = readArray(array2, a2_dim2start, a2dim1);
+ writeArray(array1, a1_dim2start, a1dim1, val);
+ }
+ }
+ }
+ } else {
+ if (a2_dim2start != a1_dim2start || a2_dim1start != a1_dim1start) {
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array1));
+ assert(ah);
+ if (a2_dim2start > a1_dim2start) {
+ copyArrayHelper(ah, a1_dim2start, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
+ copyArrayHelper(ah, a2_dim2start, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
+ } else {
+ copyArrayHelper(ah, a1_dim2end, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
+ copyArrayHelper(ah, a2_dim2end, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
+ }
+ for (; a1_dim2start <= a1_dim2end; ++a1_dim2start) {
+ memcpy(dst, src, rowSize);
+ dst += dstPitch;
+ src += srcPitch;
+ }
+ }
+ }
+}
+
+void ScummEngine_v72he::copyArrayHelper(ArrayHeader *ah, int idx2, int idx1, int len1, byte **data, int *size, int *num) {
+ const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1;
+ const int offset = pitch * (idx2 - FROM_LE_32(ah->dim2start)) + idx1 - FROM_LE_32(ah->dim1start);
+
+ switch (FROM_LE_32(ah->type)) {
+ case kByteArray:
+ case kStringArray:
+ *num = len1 - idx1 + 1;
+ *size = pitch;
+ *data = ah->data + offset;
+ break;
+ case kIntArray:
+ *num = (len1 - idx1) * 2 + 2;
+ *size = pitch * 2;
+ *data = ah->data + offset * 2;
+ break;
+ case kDwordArray:
+ *num = (len1 - idx1) * 4 + 4;
+ *size = pitch * 4;
+ *data = ah->data + offset * 4;
+ break;
+ default:
+ error("Invalid array type", FROM_LE_32(ah->type));
+ }
+}
+
+void ScummEngine_v72he::o72_readINI() {
+ byte option[128];
+ ArrayHeader *ah;
+ const char *entry;
+ int len;
+
+ copyScriptString(option, sizeof(option));
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 43: // HE 100
+ case 6: // number
+ if (!strcmp((char *)option, "NoPrinting")) {
+ push(1);
+ } else if (!strcmp((char *)option, "TextOn")) {
+ push(ConfMan.getBool("subtitles"));
+ } else {
+ push(ConfMan.getInt((char *)option));
+ }
+ break;
+ case 77: // HE 100
+ case 7: // string
+ entry = (ConfMan.get((char *)option).c_str());
+
+ writeVar(0, 0);
+ len = resStrLen((const byte *)entry);
+ ah = defineArray(0, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, entry, len);
+
+ push(readVar(0));
+ break;
+ default:
+ error("o72_readINI: default type %d", subOp);
+ }
+
+ debug(1, "o72_readINI: Option %s", option);
+}
+
+void ScummEngine_v72he::o72_writeINI() {
+ int value;
+ byte option[256], string[1024];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 43: // HE 100
+ case 6: // number
+ value = pop();
+ copyScriptString(option, sizeof(option));
+ ConfMan.set((char *)option, value);
+ debug(1, "o72_writeINI: Option %s Value %d", option, value);
+ break;
+ case 77: // HE 100
+ case 7: // string
+ copyScriptString(string, sizeof(string));
+ copyScriptString(option, sizeof(option));
+
+ // Filter out useless settings
+ if (!strcmp((char *)option, "HETest") || !strcmp((char *)option, "Version"))
+ return;
+
+ // Filter out confusing subtitle setting
+ if (!strcmp((char *)option, "TextOn"))
+ return;
+
+ // Filter out confusing path settings
+ if (!strcmp((char *)option, "DownLoadPath") || !strcmp((char *)option, "GameResourcePath") || !strcmp((char *)option, "SaveGamePath"))
+ return;
+
+ ConfMan.set((char *)option, (char *)string);
+ debug(1, "o72_writeINI: Option %s String %s", option, string);
+ break;
+ default:
+ error("o72_writeINI: default type %d", subOp);
+ }
+
+ ConfMan.flushToDisk();
+}
+
+void ScummEngine_v72he::o72_getResourceSize() {
+ const byte *ptr;
+ int size, type;
+
+ int resid = pop();
+ if (_heversion == 72) {
+ push(getSoundResourceSize(resid));
+ return;
+ }
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 13:
+ push (getSoundResourceSize(resid));
+ return;
+ case 14:
+ type = rtRoomImage;
+ break;
+ case 15:
+ type = rtImage;
+ break;
+ case 16:
+ type = rtCostume;
+ break;
+ case 17:
+ type = rtScript;
+ break;
+ default:
+ error("o80_getResourceSize: default type %d", subOp);
+ }
+
+ ptr = getResourceAddress(type, resid);
+ assert(ptr);
+ size = READ_BE_UINT32(ptr + 4) - 8;
+ push(size);
+}
+
+void ScummEngine_v72he::o72_setFilePath() {
+ // File related
+ byte filename[255];
+ copyScriptString(filename, sizeof(filename));
+ debug(1,"o72_setFilePath: %s", filename);
+}
+
+void ScummEngine_v72he::o72_setWindowCaption() {
+ byte name[1024];
+ copyScriptString(name, sizeof(name));
+ byte subOp = fetchScriptByte();
+
+ debug(1,"o72_setWindowCaption: (%d) %s", subOp, name);
+}
+
+void ScummEngine_v72he::decodeParseString(int m, int n) {
+ Actor *a;
+ int i, colors, size;
+ int args[31];
+ byte name[1024];
+
+ byte b = fetchScriptByte();
+
+ switch (b) {
+ case 65: // SO_AT
+ _string[m].ypos = pop();
+ _string[m].xpos = pop();
+ _string[m].overhead = false;
+ break;
+ case 66: // SO_COLOR
+ _string[m].color = pop();
+ break;
+ case 67: // SO_CLIPPED
+ _string[m].right = pop();
+ break;
+ case 69: // SO_CENTER
+ _string[m].center = true;
+ _string[m].overhead = false;
+ break;
+ case 71: // SO_LEFT
+ _string[m].center = false;
+ _string[m].overhead = false;
+ break;
+ case 72: // SO_OVERHEAD
+ _string[m].overhead = true;
+ _string[m].no_talk_anim = false;
+ break;
+ case 73: // SO_SAY_VOICE
+ error("decodeParseString: case 73");
+ break;
+ case 74: // SO_MUMBLE
+ _string[m].no_talk_anim = true;
+ break;
+ case 75: // SO_TEXTSTRING
+ printString(m, _scriptPointer);
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+ break;
+ case 194:
+ decodeScriptString(name, true);
+ printString(m, name);
+ break;
+ case 0xE1:
+ {
+ byte *dataPtr = getResourceAddress(rtTalkie, pop());
+ byte *text = findWrappedBlock(MKID('TEXT'), dataPtr, 0, 0);
+ size = getResourceDataSize(text);
+ memcpy(name, text, size);
+ printString(m, name);
+ }
+ break;
+ case 0xF9:
+ colors = pop();
+ if (colors == 1) {
+ _string[m].color = pop();
+ } else {
+ push(colors);
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ _string[m].color = _charsetColorMap[0];
+ }
+ break;
+ case 0xFE:
+ _string[m].loadDefault();
+ if (n) {
+ _actorToPrintStrFor = pop();
+ if (_actorToPrintStrFor != 0xFF) {
+ a = derefActor(_actorToPrintStrFor, "decodeParseString");
+ _string[0].color = a->_talkColor;
+ }
+ }
+ break;
+ case 0xFF:
+ _string[m].saveDefault();
+ break;
+ default:
+ error("decodeParseString: default case 0x%x", b);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v7he.cpp b/engines/scumm/script_v7he.cpp
new file mode 100644
index 0000000000..f615266131
--- /dev/null
+++ b/engines/scumm/script_v7he.cpp
@@ -0,0 +1,1133 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v70he, x)
+
+void ScummEngine_v70he::setupOpcodes() {
+ static const OpcodeEntryv70he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o6_pushByteVar),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayRead),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedRead),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o6_invalid),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeByteVar),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayWrite),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedWrite),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarInc),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayInc),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarDec),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayDec),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o6_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o6_startObject),
+ OPCODE(o6_drawObject),
+ OPCODE(o6_drawObjectAt),
+ OPCODE(o6_invalid),
+ /* 64 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_stopMusic),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o6_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o60_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o70_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_startMusic),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o70_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o70_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o6_getVerbFromXY),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o70_resourceRoutines),
+ /* 9C */
+ OPCODE(o60_roomOps),
+ OPCODE(o60_actorOps),
+ OPCODE(o6_verbOps),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o6_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o6_arrayOps),
+ OPCODE(o6_saveRestoreVerbs),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o60_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorAnimCounter1),
+ /* AC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o70_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o6_talkActor),
+ OPCODE(o6_talkEgo),
+ /* BC */
+ OPCODE(o6_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o6_dim2dimArray),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o60_kernelGetFunctions),
+ OPCODE(o70_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o6_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o60_openFile),
+ OPCODE(o60_readFile),
+ /* DC */
+ OPCODE(o60_writeFile),
+ OPCODE(o6_findAllObjects),
+ OPCODE(o60_deleteFile),
+ OPCODE(o60_rename),
+ /* E0 */
+ OPCODE(o60_soundOps),
+ OPCODE(o6_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o6_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o60_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o70_copyString),
+ OPCODE(o70_getStringWidth),
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_appendString),
+ /* F0 */
+ OPCODE(o70_concatString),
+ OPCODE(o70_compareString),
+ OPCODE(o70_isResourceLoaded),
+ OPCODE(o70_readINI),
+ /* F4 */
+ OPCODE(o70_writeINI),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o70_getCharIndexInString),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_setFilePath),
+ OPCODE(o70_setWindowCaption),
+ OPCODE(o70_polygonOps),
+ /* FC */
+ OPCODE(o70_polygonHit),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesv70he = opcodes;
+}
+
+void ScummEngine_v70he::executeOpcode(byte i) {
+ OpcodeProcv70he op = _opcodesv70he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v70he::getOpcodeDesc(byte i) {
+ return _opcodesv70he[i].desc;
+}
+
+int ScummEngine_v70he::getStringCharWidth(byte chr) {
+ int charset = _string[0]._default.charset;
+
+ byte *ptr = getResourceAddress(rtCharset, charset);
+ assert(ptr);
+ ptr += 29;
+
+ int spacing = 0;
+
+ int offs = READ_LE_UINT32(ptr + chr * 4 + 4);
+ if (offs) {
+ spacing = ptr[offs] + (signed char)ptr[offs + 2];
+ }
+
+ return spacing;
+}
+
+int ScummEngine_v70he::setupStringArray(int size) {
+ writeVar(0, 0);
+ defineArray(0, kStringArray, 0, size + 1);
+ writeArray(0, 0, 0, 0);
+ return readVar(0);
+}
+
+void ScummEngine_v70he::appendSubstring(int dst, int src, int srcOffs, int len) {
+ int dstOffs, value;
+ int i = 0;
+
+ if (len == -1) {
+ len = resStrLen(getStringAddress(src));
+ srcOffs = 0;
+ }
+
+ dstOffs = resStrLen(getStringAddress(dst));
+
+ len -= srcOffs;
+ len++;
+
+ while (i < len) {
+ writeVar(0, src);
+ value = readArray(0, 0, srcOffs + i);
+ writeVar(0, dst);
+ writeArray(0, 0, dstOffs + i, value);
+ i++;
+ }
+
+ writeArray(0, 0, dstOffs + i, 0);
+}
+
+void ScummEngine_v70he::o70_startSound() {
+ int var, value;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 9:
+ _heSndFlags |= 4;
+ break;
+ case 23:
+ value = pop();
+ var = pop();
+ _heSndSoundId = pop();
+ _sound->setSoundVar(_heSndSoundId, var, value);
+ break;
+ case 25:
+ value = pop();
+ _heSndSoundId = pop();
+ _sound->addSoundToQueue(_heSndSoundId, 0, 0, 8);
+ case 56:
+ _heSndFlags |= 16;
+ break;
+ case 164:
+ _heSndFlags |= 2;
+ break;
+ case 224:
+ _heSndSoundFreq = pop();
+ break;
+ case 230:
+ _heSndChannel = pop();
+ break;
+ case 231:
+ _heSndOffset = pop();
+ break;
+ case 232:
+ _heSndSoundId = pop();
+ _heSndOffset = 0;
+ _heSndSoundFreq = 11025;
+ _heSndChannel = VAR(VAR_SOUND_CHANNEL);
+ break;
+ case 245:
+ _heSndFlags |= 1;
+ break;
+ case 255:
+ _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
+ _heSndFlags = 0;
+ break;
+
+ default:
+ error("o70_startSound invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v70he::o70_pickupObject() {
+ int obj, room;
+
+ room = pop();
+ obj = pop();
+ if (room == 0)
+ room = getObjectRoom(obj);
+
+ addObjectToInventory(obj, room);
+ putOwner(obj, VAR(VAR_EGO));
+ if (_heversion <= 70) {
+ putClass(obj, kObjectClassUntouchable, 1);
+ putState(obj, 1);
+ markObjectRectAsDirty(obj);
+ clearDrawObjectQueue();
+ }
+ runInventoryScript(obj); /* Difference */
+}
+
+void ScummEngine_v70he::o70_getActorRoom() {
+ int act = pop();
+
+ if (act < _numActors) {
+ Actor *a = derefActor(act, "o70_getActorRoom");
+ push(a->_room);
+ } else
+ push(getObjectRoom(act));
+}
+
+void ScummEngine_v70he::o70_resourceRoutines() {
+ int objidx, resid;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 100: // SO_LOAD_SCRIPT
+ resid = pop();
+ ensureResourceLoaded(rtScript, resid);
+ break;
+ case 101: // SO_LOAD_SOUND
+ resid = pop();
+ ensureResourceLoaded(rtSound, resid);
+ break;
+ case 102: // SO_LOAD_COSTUME
+ resid = pop();
+ ensureResourceLoaded(rtCostume, resid);
+ break;
+ case 103: // SO_LOAD_ROOM
+ resid = pop();
+ ensureResourceLoaded(rtRoomImage, resid);
+ ensureResourceLoaded(rtRoom, resid);
+ break;
+ case 104: // SO_NUKE_SCRIPT
+ resid = pop();
+ res.nukeResource(rtScript, resid);
+ break;
+ case 105: // SO_NUKE_SOUND
+ resid = pop();
+ res.nukeResource(rtSound, resid);
+ break;
+ case 106: // SO_NUKE_COSTUME
+ resid = pop();
+ res.nukeResource(rtCostume, resid);
+ break;
+ case 107: // SO_NUKE_ROOM
+ resid = pop();
+ res.nukeResource(rtRoom, resid);
+ res.nukeResource(rtRoomImage, resid);
+ break;
+ case 108: // SO_LOCK_SCRIPT
+ resid = pop();
+ if (resid >= _numGlobalScripts)
+ break;
+ res.lock(rtScript, resid);
+ break;
+ case 109: // SO_LOCK_SOUND
+ resid = pop();
+ res.lock(rtSound, resid);
+ break;
+ case 110: // SO_LOCK_COSTUME
+ resid = pop();
+ res.lock(rtCostume, resid);
+ break;
+ case 111: // SO_LOCK_ROOM
+ resid = pop();
+ if (_heversion <= 71 && resid > 0x7F)
+ resid = _resourceMapper[resid & 0x7F];
+ res.lock(rtRoom, resid);
+ res.lock(rtRoomImage, resid);
+ break;
+ case 112: // SO_UNLOCK_SCRIPT
+ resid = pop();
+ if (resid >= _numGlobalScripts)
+ break;
+ res.unlock(rtScript, resid);
+ break;
+ case 113: // SO_UNLOCK_SOUND
+ resid = pop();
+ res.unlock(rtSound, resid);
+ break;
+ case 114: // SO_UNLOCK_COSTUME
+ resid = pop();
+ res.unlock(rtCostume, resid);
+ break;
+ case 115: // SO_UNLOCK_ROOM
+ resid = pop();
+ if (_heversion <= 71 && resid > 0x7F)
+ resid = _resourceMapper[resid & 0x7F];
+ res.unlock(rtRoom, resid);
+ res.unlock(rtRoomImage, resid);
+ break;
+ case 116:
+ break;
+ case 117: // SO_LOAD_CHARSET
+ resid = pop();
+ loadCharset(resid);
+ break;
+ case 118: // SO_NUKE_CHARSET
+ resid = pop();
+ nukeCharset(resid);
+ break;
+ case 119: // SO_LOAD_OBJECT
+ {
+ int obj = pop();
+ int room = getObjectRoom(obj);
+ loadFlObject(obj, room);
+ break;
+ }
+ case 120:
+ resid = pop();
+ if (resid >= _numGlobalScripts)
+ break;
+ //queueLoadResource(rtScript, resid);
+ break;
+ case 121:
+ resid = pop();
+ //queueLoadResource(rtSound, resid);
+ break;
+ case 122:
+ resid = pop();
+ //queueLoadResource(rtCostume, resid);
+ break;
+ case 123:
+ resid = pop();
+ //queueLoadResource(rtRoomImage, resid);
+ break;
+ case 159:
+ resid = pop();
+ res.unlock(rtImage, resid);
+ break;
+ case 192:
+ resid = pop();
+ res.nukeResource(rtImage, resid);
+ break;
+ case 201:
+ resid = pop();
+ ensureResourceLoaded(rtImage, resid);
+ break;
+ case 202:
+ resid = pop();
+ res.lock(rtImage, resid);
+ break;
+ case 203:
+ resid = pop();
+ //queueLoadResource(rtImage, resid);
+ break;
+ case 233:
+ resid = pop();
+ objidx = getObjectIndex(resid);
+ if (objidx == -1)
+ break;
+ res.lock(rtFlObject, _objs[objidx].fl_object_index);
+ break;
+ case 235:
+ resid = pop();
+ objidx = getObjectIndex(resid);
+ if (objidx == -1)
+ break;
+ res.unlock(rtFlObject, _objs[objidx].fl_object_index);
+ break;
+ case 239:
+ // Used in airport
+ break;
+ default:
+ error("o70_resourceRoutines: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v70he::o70_systemOps() {
+ byte *src, string[256];
+ int id, len;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 158:
+ restart();
+ break;
+ case 160:
+ // Confirm shutdown
+ shutDown();
+ break;
+ case 244:
+ shutDown();
+ break;
+ case 250:
+ id = pop();
+ src = getStringAddress(id);
+ len = resStrLen(src) + 1;
+ memcpy(string, src, len);
+ debug(0, "Start executable (%s)", string);
+ break;
+ case 251:
+ convertMessageToString(_scriptPointer, string, sizeof(string));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+ debug(0, "Start executable (%s)", string);
+ break;
+ case 252:
+ convertMessageToString(_scriptPointer, string, sizeof(string));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+ debug(0, "Start game (%s)", string);
+ break;
+ case 253:
+ id = pop();
+ src = getStringAddress(id);
+ len = resStrLen(src) + 1;
+ memcpy(string, src, len);
+ debug(0, "Start game (%s)", string);
+ break;
+ default:
+ error("o70_systemOps invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v70he::o70_seekFilePos() {
+ int mode, offset, slot;
+ mode = pop();
+ offset = pop();
+ slot = pop();
+
+ if (slot == -1)
+ return;
+
+ switch (mode) {
+ case 1:
+ _hFileTable[slot].seek(offset, SEEK_SET);
+ break;
+ case 2:
+ _hFileTable[slot].seek(offset, SEEK_CUR);
+ break;
+ case 3:
+ _hFileTable[slot].seek(offset, SEEK_END);
+ break;
+ default:
+ error("o70_seekFilePos: default case 0x%x", mode);
+ }
+}
+
+void ScummEngine_v70he::o70_copyString() {
+ int dst, size;
+ int src = pop();
+
+ size = resStrLen(getStringAddress(src)) + 1;
+ dst = setupStringArray(size);
+
+ appendSubstring(dst, src, -1, -1);
+
+ push(dst);
+}
+
+void ScummEngine_v70he::o70_getStringWidth() {
+ int array, pos, len;
+ int chr, width = 0;
+
+ len = pop();
+ pos = pop();
+ array = pop();
+
+ if (len == -1) {
+ pos = 0;
+ len = resStrLen(getStringAddress(array));
+ }
+
+ writeVar(0, array);
+ while (pos <= len) {
+ chr = readArray(0, 0, pos);
+ if (chr == 0)
+ break;
+ width += getStringCharWidth(chr);
+ pos++;
+ }
+
+ push(width);
+}
+
+void ScummEngine_v70he::o70_kernelSetFunctions() {
+ int args[29];
+ int num;
+ Actor *a;
+
+ num = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1:
+ // Used to restore images when decorating cake in
+ // Fatty Bear's Birthday Surprise
+ virtScreenLoad(args[1], args[2], args[3], args[4], args[5]);
+ break;
+ case 20: // HE72+
+ a = derefActor(args[1], "o70_kernelSetFunctions: 20");
+ ((ScummEngine_v71he *)this)->queueAuxBlock(a);
+ break;
+ case 21:
+ _skipDrawObject = 1;
+ break;
+ case 22:
+ _skipDrawObject = 0;
+ break;
+ case 23:
+ _charset->clearCharsetMask();
+ _fullRedraw = true;
+ break;
+ case 24:
+ _skipProcessActors = 1;
+ redrawAllActors();
+ break;
+ case 25:
+ _skipProcessActors = 0;
+ redrawAllActors();
+ break;
+ case 26:
+ a = derefActor(args[1], "o70_kernelSetFunctions: 26");
+ a->_auxBlock.r.left = 0;
+ a->_auxBlock.r.right = -1;
+ a->_auxBlock.r.top = 0;
+ a->_auxBlock.r.bottom = -2;
+ break;
+ case 30:
+ a = derefActor(args[1], "o70_kernelSetFunctions: 30");
+ a->_clipOverride.bottom = args[2];
+ break;
+ case 42:
+ _wiz->_rectOverrideEnabled = true;
+ _wiz->_rectOverride.left = args[1];
+ _wiz->_rectOverride.top = args[2];
+ _wiz->_rectOverride.right = args[3];
+ _wiz->_rectOverride.bottom = args[4];
+ break;
+ case 43:
+ _wiz->_rectOverrideEnabled = false;
+ break;
+ default:
+ error("o70_kernelSetFunctions: default case %d (param count %d)", args[0], num);
+ }
+}
+
+void ScummEngine_v70he::o70_getStringLen() {
+ int id, len;
+ byte *addr;
+
+ id = pop();
+
+ addr = getStringAddress(id);
+ if (!addr)
+ error("o70_getStringLen: Reference to zeroed array pointer (%d)", id);
+
+ len = resStrLen(getStringAddress(id));
+ push(len);
+}
+
+void ScummEngine_v70he::o70_appendString() {
+ int dst, size;
+
+ int len = pop();
+ int srcOffs = pop();
+ int src = pop();
+
+ size = len - srcOffs + 2;
+ dst = setupStringArray(size);
+
+ appendSubstring(dst, src, srcOffs, len);
+
+ push(dst);
+}
+
+void ScummEngine_v70he::o70_concatString() {
+ int dst, size;
+
+ int src2 = pop();
+ int src1 = pop();
+
+ size = resStrLen(getStringAddress(src1));
+ size += resStrLen(getStringAddress(src2)) + 1;
+ dst = setupStringArray(size);
+
+ appendSubstring(dst, src1, 0, -1);
+ appendSubstring(dst, src2, 0, -1);
+
+ push(dst);
+}
+
+void ScummEngine_v70he::o70_compareString() {
+ int result;
+
+ int array1 = pop();
+ int array2 = pop();
+
+ byte *string1 = getStringAddress(array1);
+ if (!string1)
+ error("o70_compareString: Reference to zeroed array pointer (%d)", array1);
+
+ byte *string2 = getStringAddress(array2);
+ if (!string2)
+ error("o70_compareString: Reference to zeroed array pointer (%d)", array2);
+
+ while (*string1 == *string2) {
+ if (*string2 == 0) {
+ push(0);
+ return;
+ }
+
+ string1++;
+ string2++;
+ }
+
+ result = (*string1 > *string2) ? -1 : 1;
+ push(result);
+}
+
+void ScummEngine_v70he::o70_isResourceLoaded() {
+ // Reports percentage of resource loaded by queue
+ int type;
+
+ byte subOp = fetchScriptByte();
+ /* int idx = */ pop();
+
+ switch (subOp) {
+ case 18:
+ type = rtImage;
+ break;
+ case 226:
+ type = rtRoom;
+ break;
+ case 227:
+ type = rtCostume;
+ break;
+ case 228:
+ type = rtSound;
+ break;
+ case 229:
+ type = rtScript;
+ break;
+ default:
+ error("o70_isResourceLoaded: default case %d", subOp);
+ }
+
+ push(100);
+}
+
+void ScummEngine_v70he::o70_readINI() {
+ byte option[256];
+ ArrayHeader *ah;
+ const char *entry;
+ int len, type;
+
+ convertMessageToString(_scriptPointer, option, sizeof(option));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ type = pop();
+ switch (type) {
+ case 1: // number
+ if (!strcmp((char *)option, "NoPrinting")) {
+ push(1);
+ } else if (!strcmp((char *)option, "TextOn")) {
+ push(ConfMan.getBool("subtitles"));
+ } else {
+ push(ConfMan.getInt((char *)option));
+ }
+ break;
+ case 2: // string
+ entry = (ConfMan.get((char *)option).c_str());
+
+ writeVar(0, 0);
+ len = resStrLen((const byte *)entry);
+ ah = defineArray(0, kStringArray, 0, len);
+ memcpy(ah->data, entry, len);
+
+ push(readVar(0));
+ break;
+ default:
+ error("o70_readINI: default type %d", type);
+ }
+ debug(1, "o70_readINI: Option %s", option);
+}
+
+void ScummEngine_v70he::o70_writeINI() {
+ int type, value;
+ byte option[256], string[256];
+ int len;
+
+ type = pop();
+ value = pop();
+
+ convertMessageToString(_scriptPointer, option, sizeof(option));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ switch (type) {
+ case 1: // number
+ ConfMan.set((char *)option, value);
+ debug(1, "o70_writeINI: Option %s Value %d", option, value);
+ break;
+ case 2: // string
+ convertMessageToString(_scriptPointer, string, sizeof(string));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+ ConfMan.set((char *)option, (char *)string);
+ debug(1, "o70_writeINI: Option %s String %s", option, string);
+ break;
+ default:
+ error("o70_writeINI: default type %d", type);
+ }
+}
+
+void ScummEngine_v70he::o70_getStringLenForWidth() {
+ int chr, max;
+ int array, len, pos, width = 0;
+
+ max = pop();
+ pos = pop();
+ array = pop();
+
+ len = resStrLen(getStringAddress(array));
+
+ writeVar(0, array);
+ while (pos <= len) {
+ chr = readArray(0, 0, pos);
+ width += getStringCharWidth(chr);
+ if (width >= max) {
+ push(pos);
+ return;
+ }
+ pos++;
+ }
+
+ push(len);
+}
+
+void ScummEngine_v70he::o70_getCharIndexInString() {
+ int array, end, len, pos, value;
+
+ value = pop();
+ end = pop();
+ pos = pop();
+ array = pop();
+
+ if (end >= 0) {
+ len = resStrLen(getStringAddress(array));
+ if (len < end)
+ end = len;
+ } else {
+ end = 0;
+ }
+
+ if (pos < 0)
+ pos = 0;
+
+ writeVar(0, array);
+ if (end > pos) {
+ while (end >= pos) {
+ if (readArray(0, 0, pos) == value) {
+ push(pos);
+ return;
+ }
+ pos++;
+ }
+ } else {
+ while (end <= pos) {
+ if (readArray(0, 0, pos) == value) {
+ push(pos);
+ return;
+ }
+ pos--;
+ }
+ }
+
+ push(-1);
+}
+
+void ScummEngine_v70he::o70_setFilePath() {
+ // File related
+ int len;
+ byte filename[100];
+
+ convertMessageToString(_scriptPointer, filename, sizeof(filename));
+
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ debug(1,"stub o70_setFilePath(%s)", filename);
+}
+
+void ScummEngine_v70he::o70_setWindowCaption() {
+ byte subOp = fetchScriptByte();
+ int len = resStrLen(_scriptPointer);
+ debug(1,"stub o70_setWindowCaption(%d, \"%s\")", subOp, _scriptPointer);
+ _scriptPointer += len + 1;
+}
+
+void ScummEngine_v70he::o70_polygonOps() {
+ int vert1x, vert1y, vert2x, vert2y, vert3x, vert3y, vert4x, vert4y;
+ int id, fromId, toId;
+ bool flag;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 68: // HE 100
+ case 69: // HE 100
+ case 246:
+ case 248:
+ vert4y = pop();
+ vert4x = pop();
+ vert3y = pop();
+ vert3x = pop();
+ vert2y = pop();
+ vert2x = pop();
+ vert1y = pop();
+ vert1x = pop();
+ flag = (subOp == 69 || subOp == 248);
+ id = pop();
+ _wiz->polygonStore(id, flag, vert1x, vert1y, vert2x, vert2y, vert3x, vert3y, vert4x, vert4y);
+ break;
+ case 28: // HE 100
+ case 247:
+ toId = pop();
+ fromId = pop();
+ _wiz->polygonErase(fromId, toId);
+ break;
+ default:
+ error("o70_polygonOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v70he::o70_polygonHit() {
+ int y = pop();
+ int x = pop();
+ push(_wiz->polygonHit(0, x, y));
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v8.cpp b/engines/scumm/script_v8.cpp
new file mode 100644
index 0000000000..b5f9f48466
--- /dev/null
+++ b/engines/scumm/script_v8.cpp
@@ -0,0 +1,1496 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "scumm/actor.h"
+#include "scumm/akos.h"
+#include "scumm/charset.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/intern.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/verbs.h"
+#include "scumm/smush/smush_player.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v8, x)
+
+void ScummEngine_v8::setupOpcodes() {
+ static const OpcodeEntryV8 opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_pushWord),
+ OPCODE(o6_pushWordVar),
+ OPCODE(o6_wordArrayRead),
+ /* 04 */
+ OPCODE(o6_wordArrayIndexedRead),
+ OPCODE(o6_dup),
+ OPCODE(o6_pop),
+ OPCODE(o6_not),
+ /* 08 */
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ /* 0C */
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ /* 10 */
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ /* 14 */
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ OPCODE(o8_mod),
+ OPCODE(o6_invalid),
+ /* 18 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 48 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 4C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 50 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 54 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 58 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 5C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 60 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 64 */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o6_jump),
+ OPCODE(o6_breakHere),
+ /* 68 */
+ OPCODE(o6_delayFrames),
+ OPCODE(o8_wait),
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ /* 6C */
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_writeWordVar),
+ OPCODE(o6_wordVarInc),
+ OPCODE(o6_wordVarDec),
+ /* 70 */
+ OPCODE(o8_dimArray),
+ OPCODE(o6_wordArrayWrite),
+ OPCODE(o6_wordArrayInc),
+ OPCODE(o6_wordArrayDec),
+ /* 74 */
+ OPCODE(o8_dim2dimArray),
+ OPCODE(o6_wordArrayIndexedWrite),
+ OPCODE(o8_arrayOps),
+ OPCODE(o6_invalid),
+ /* 78 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_startScript),
+ OPCODE(o6_startScriptQuick),
+ OPCODE(o6_stopObjectCode),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_jumpToScript),
+ OPCODE(o6_dummy), // O_RETURN boils down to a NOP
+ OPCODE(o6_startObject),
+ /* 80 */
+ OPCODE(o6_stopObjectScript),
+ OPCODE(o6_cutscene),
+ OPCODE(o6_endCutscene),
+ OPCODE(o6_freezeUnfreeze),
+ /* 84 */
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_stopSentence),
+ OPCODE(o6_invalid),
+ /* 88 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_setClass),
+ OPCODE(o6_setState),
+ OPCODE(o6_setOwner),
+ /* 8C */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_printActor),
+ /* 90 */
+ OPCODE(o6_printEgo),
+ OPCODE(o6_talkActor),
+ OPCODE(o6_talkEgo),
+ OPCODE(o6_printLine),
+ /* 94 */
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ OPCODE(o8_blastText),
+ /* 98 */
+ OPCODE(o8_drawObject),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 9C */
+ OPCODE(o8_cursorCommand),
+ OPCODE(o6_loadRoom),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_walkActorToObj),
+ /* A0 */
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ /* A4 */
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ OPCODE(o6_pickupObject),
+ OPCODE(o6_setBoxFlags),
+ /* A8 */
+ OPCODE(o6_createBoxMatrix),
+ OPCODE(o6_invalid),
+ OPCODE(o8_resourceRoutines),
+ OPCODE(o8_roomOps),
+ /* AC */
+ OPCODE(o8_actorOps),
+ OPCODE(o8_cameraOps),
+ OPCODE(o8_verbOps),
+ OPCODE(o6_startSound),
+ /* B0 */
+ OPCODE(o6_startMusic),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_soundKludge),
+ OPCODE(o8_systemOps),
+ /* B4 */
+ OPCODE(o6_saveRestoreVerbs),
+ OPCODE(o6_setObjectName),
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_drawBox),
+ /* B8 */
+ OPCODE(o6_invalid),
+ OPCODE(o8_startVideo),
+ OPCODE(o8_kernelSetFunctions),
+ OPCODE(o6_invalid),
+ /* BC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C0 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C8 */
+ OPCODE(o6_startScriptQuick2),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_pickOneOf),
+ OPCODE(o6_pickOneOfDefault),
+ /* CC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o6_getRandomNumber),
+ OPCODE(o6_getRandomNumberRange),
+ /* D0 */
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_getState),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_isScriptRunning),
+ /* D4 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_abs),
+ OPCODE(o6_invalid),
+ /* D8 */
+ OPCODE(o8_kernelGetFunctions),
+ OPCODE(o6_isActorInBox),
+ OPCODE(o6_getVerbEntrypoint),
+ OPCODE(o6_getActorFromXY),
+ /* DC */
+ OPCODE(o6_findObject),
+ OPCODE(o6_getVerbFromXY),
+ OPCODE(o6_invalid),
+ OPCODE(o6_findInventory),
+ /* E0 */
+ OPCODE(o6_getInventoryCount),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_getActorRoom),
+ OPCODE(o6_getActorWalkBox),
+ /* E4 */
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorLayer),
+ /* E8 */
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getActorWidth),
+ OPCODE(o6_getObjectNewDir),
+ OPCODE(o6_getObjectX),
+ /* EC */
+ OPCODE(o6_getObjectY),
+ OPCODE(o8_getActorChore),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distPtPt),
+ /* F0 */
+ OPCODE(o8_getObjectImageX),
+ OPCODE(o8_getObjectImageY),
+ OPCODE(o8_getObjectImageWidth),
+ OPCODE(o8_getObjectImageHeight),
+ /* F4 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o8_getStringWidth),
+ OPCODE(o8_getActorZPlane),
+ /* F8 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* FC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV8 = opcodes;
+}
+
+void ScummEngine_v8::executeOpcode(byte i) {
+ OpcodeProcV8 op = _opcodesV8[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v8::getOpcodeDesc(byte i) {
+ return _opcodesV8[i].desc;
+}
+
+// In V8, the word size is 4 byte, not 2 bytes as in V6/V7 games
+uint ScummEngine_v8::fetchScriptWord() {
+ return fetchScriptDWord();
+}
+
+int ScummEngine_v8::fetchScriptWordSigned() {
+ return (int32)fetchScriptDWordSigned();
+}
+
+int ScummEngine_v8::readVar(uint var) {
+ debugC(DEBUG_VARS, "readvar(%d)", var);
+
+ if (!(var & 0xF0000000)) {
+ checkRange(_numVariables - 1, 0, var, "Variable %d out of range(r)");
+ return _scummVars[var];
+ }
+
+ if (var & 0x80000000) {
+ var &= 0x7FFFFFFF;
+ checkRange(_numBitVariables - 1, 0, var, "Bit variable %d out of range(r)");
+ return (_bitVars[var >> 3] & (1 << (var & 7))) ? 1 : 0;
+ }
+
+ if (var & 0x40000000) {
+ var &= 0xFFFFFFF;
+ checkRange(25, 0, var, "Local variable %d out of range(r)");
+ return vm.localvar[_currentScript][var];
+ }
+
+ error("Illegal varbits (r)");
+ return -1;
+}
+
+void ScummEngine_v8::writeVar(uint var, int value) {
+ debugC(DEBUG_VARS, "writeVar(%d, %d)", var, value);
+
+ if (!(var & 0xF0000000)) {
+ checkRange(_numVariables - 1, 0, var, "Variable %d out of range(w)");
+
+ if (var == VAR_CHARINC && ConfMan.hasKey("talkspeed")) {
+ uint talkspeed = ConfMan.getInt("talkspeed");
+ if (talkspeed <= 9)
+ VAR(VAR_CHARINC) = talkspeed;
+ } else
+ _scummVars[var] = value;
+
+ if ((_varwatch == (int)var) || (_varwatch == 0)) {
+ if (vm.slot[_currentScript].number < 100)
+ debugC(DEBUG_VARS, "vars[%d] = %d (via script-%d)", var, value, vm.slot[_currentScript].number);
+ else
+ debugC(DEBUG_VARS, "vars[%d] = %d (via room-%d-%d)", var, value, _currentRoom, vm.slot[_currentScript].number);
+ }
+ return;
+ }
+
+ if (var & 0x80000000) {
+ var &= 0x7FFFFFFF;
+ checkRange(_numBitVariables - 1, 0, var, "Bit variable %d out of range(w)");
+
+ if (value)
+ _bitVars[var >> 3] |= (1 << (var & 7));
+ else
+ _bitVars[var >> 3] &= ~(1 << (var & 7));
+ return;
+ }
+
+ if (var & 0x40000000) {
+ var &= 0xFFFFFFF;
+ checkRange(25, 0, var, "Local variable %d out of range(w)");
+ vm.localvar[_currentScript][var] = value;
+ return;
+ }
+
+ error("Illegal varbits (w)");
+}
+
+void ScummEngine_v8::decodeParseString(int m, int n) {
+ byte b = fetchScriptByte();
+
+ switch (b) {
+ case 0xC8: // SO_PRINT_BASEOP
+ _string[m].loadDefault();
+ if (n)
+ _actorToPrintStrFor = pop();
+ break;
+ case 0xC9: // SO_PRINT_END
+ _string[m].saveDefault();
+ break;
+ case 0xCA: // SO_PRINT_AT
+ _string[m].ypos = pop();
+ _string[m].xpos = pop();
+ _string[m].overhead = false;
+ break;
+ case 0xCB: // SO_PRINT_COLOR
+ _string[m].color = pop();
+ break;
+ case 0xCC: // SO_PRINT_CENTER
+ _string[m].center = true;
+ _string[m].overhead = false;
+ break;
+ case 0xCD: // SO_PRINT_CHARSET Set print character set
+ _string[m].charset = pop();
+ break;
+ case 0xCE: // SO_PRINT_LEFT
+ _string[m].center = false;
+ _string[m].overhead = false;
+ break;
+ case 0xCF: // SO_PRINT_OVERHEAD
+ _string[m].overhead = true;
+ _string[m].no_talk_anim = false;
+ break;
+ case 0xD0: // SO_PRINT_MUMBLE
+ _string[m].no_talk_anim = true;
+ break;
+ case 0xD1: // SO_PRINT_STRING
+ if (m == 5)
+ enqueueText(_scriptPointer, _string[m].xpos, _string[m].ypos, _string[m].color, _string[m].charset, _string[m].center);
+ else
+ printString(m, _scriptPointer);
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+
+ break;
+ case 0xD2: // SO_PRINT_WRAP Set print wordwrap
+ //debug(0, "decodeParseString: SO_PRINT_WRAP");
+ break;
+ default:
+ error("decodeParseString: default case 0x%x", b);
+ }
+}
+
+void ScummEngine_v8::readArrayFromIndexFile() {
+ int num;
+ int a, b;
+
+ while ((num = _fileHandle->readUint32LE()) != 0) {
+ a = _fileHandle->readUint32LE();
+ b = _fileHandle->readUint32LE();
+
+ if (b != 0)
+ defineArray(num, kIntArray, b, a);
+ else
+ defineArray(num, kIntArray, a, b);
+ }
+}
+
+void ScummEngine_v8::o8_mod() {
+ int a = pop();
+ push(pop() % a);
+}
+
+void ScummEngine_v8::o8_wait() {
+ int actnum;
+ int offs = -2;
+ Actor *a;
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0x1E: // SO_WAIT_FOR_ACTOR Wait for actor (to finish current action?)
+ offs = fetchScriptWordSigned();
+ actnum = pop();
+ a = derefActor(actnum, "o8_wait:SO_WAIT_FOR_ACTOR");
+ if (a->isInCurrentRoom() && a->_moving)
+ break;
+ return;
+ case 0x1F: // SO_WAIT_FOR_MESSAGE Wait for message
+ if (VAR(VAR_HAVE_MSG))
+ break;
+ return;
+ case 0x20: // SO_WAIT_FOR_CAMERA Wait for camera (to finish current action?)
+ if (camera._dest != camera._cur)
+ break;
+ return;
+ case 0x21: // SO_WAIT_FOR_SENTENCE
+ if (_sentenceNum) {
+ if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ }
+ if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ case 0x22: // SO_WAIT_FOR_ANIMATION
+ offs = fetchScriptWordSigned();
+ actnum = pop();
+ a = derefActor(actnum, "o8_wait:SO_WAIT_FOR_ANIMATION");
+ if (a->isInCurrentRoom() && a->_needRedraw)
+ break;
+ return;
+ case 0x23: // SO_WAIT_FOR_TURN
+ offs = fetchScriptWordSigned();
+ actnum = pop();
+ a = derefActor(actnum, "o8_wait:SO_WAIT_FOR_TURN");
+ if (a->isInCurrentRoom() && a->_moving & MF_TURN)
+ break;
+ return;
+ default:
+ error("o8_wait: default case 0x%x", subOp);
+ }
+
+ _scriptPointer += offs;
+ o6_breakHere();
+}
+
+void ScummEngine_v8::o8_dimArray() {
+ byte subOp = fetchScriptByte();
+ int array = fetchScriptWord();
+
+ switch (subOp) {
+ case 0x0A: // SO_ARRAY_SCUMMVAR
+ defineArray(array, kIntArray, 0, pop());
+ break;
+ case 0x0B: // SO_ARRAY_STRING
+ defineArray(array, kStringArray, 0, pop());
+ break;
+ case 0x0C: // SO_ARRAY_UNDIM
+ nukeArray(array);
+ break;
+ default:
+ error("o8_dimArray: default case 0x%x", subOp);
+ }
+}
+
+void ScummEngine_v8::o8_dim2dimArray() {
+ byte subOp = fetchScriptByte();
+ int array = fetchScriptWord(), a, b;
+
+ switch (subOp) {
+ case 0x0A: // SO_ARRAY_SCUMMVAR
+ b = pop();
+ a = pop();
+ defineArray(array, kIntArray, a, b);
+ break;
+ case 0x0B: // SO_ARRAY_STRING
+ b = pop();
+ a = pop();
+ defineArray(array, kStringArray, a, b);
+ break;
+ case 0x0C: // SO_ARRAY_UNDIM
+ nukeArray(array);
+ break;
+ default:
+ error("o8_dim2dimArray: default case 0x%x", subOp);
+ }
+}
+
+void ScummEngine_v8::o8_arrayOps() {
+ byte subOp = fetchScriptByte();
+ int array = fetchScriptWord();
+ int b, c, d, len;
+ ArrayHeader *ah;
+ int list[128];
+
+ switch (subOp) {
+ case 0x14: // SO_ASSIGN_STRING
+ b = pop();
+ len = resStrLen(_scriptPointer);
+ ah = defineArray(array, kStringArray, 0, len + 1);
+ copyScriptString(ah->data + b);
+ break;
+ case 0x15: // SO_ASSIGN_SCUMMVAR_LIST
+ b = pop();
+ len = getStackList(list, ARRAYSIZE(list));
+ d = readVar(array);
+ if (d == 0) {
+ defineArray(array, kIntArray, 0, b + len);
+ }
+ while (--len >= 0) {
+ writeArray(array, 0, b + len, list[len]);
+ }
+ break;
+ case 0x16: // SO_ASSIGN_2DIM_LIST
+ b = pop();
+ len = getStackList(list, ARRAYSIZE(list));
+ d = readVar(array);
+ if (d == 0)
+ error("Must DIM a two dimensional array before assigning");
+ c = pop();
+ while (--len >= 0) {
+ writeArray(array, c, b + len, list[len]);
+ }
+ break;
+ default:
+ error("o8_arrayOps: default case 0x%x (array %d)", subOp, array);
+ }
+}
+
+void ScummEngine_v8::o8_blastText() {
+ // FIXME
+ decodeParseString(5, 0);
+}
+
+void ScummEngine_v8::o8_cursorCommand() {
+ byte subOp = fetchScriptByte();
+ int a, i;
+ int args[16];
+
+ switch (subOp) {
+ case 0xDC: // SO_CURSOR_ON Turn cursor on
+ _cursor.state = 1;
+ verbMouseOver(0);
+ break;
+ case 0xDD: // SO_CURSOR_OFF Turn cursor off
+ _cursor.state = 0;
+ verbMouseOver(0);
+ break;
+ case 0xDE: // SO_CURSOR_SOFT_ON Turn soft cursor on
+ _cursor.state++;
+ verbMouseOver(0);
+ break;
+ case 0xDF: // SO_CURSOR_SOFT_OFF Turn soft cursor off
+ _cursor.state--;
+ verbMouseOver(0);
+ break;
+ case 0xE0: // SO_USERPUT_ON
+ _userPut = 1;
+ break;
+ case 0xE1: // SO_USERPUT_OFF
+ _userPut = 0;
+ break;
+ case 0xE2: // SO_USERPUT_SOFT_ON
+ _userPut++;
+ break;
+ case 0xE3: // SO_USERPUT_SOFT_OFF
+ _userPut--;
+ break;
+ case 0xE4: // SO_CURSOR_IMAGE Set cursor image
+ {
+ int idx = pop();
+ int room, obj;
+ obj = popRoomAndObj(&room);
+ setCursorFromImg(obj, room, idx);
+ }
+ break;
+ case 0xE5: // SO_CURSOR_HOTSPOT Set cursor hotspot
+ a = pop();
+ setCursorHotspot(pop(), a);
+ break;
+ case 0xE6: // SO_CURSOR_TRANSPARENT Set cursor transparent color
+ setCursorTransparency(pop());
+ break;
+ case 0xE7: { // SO_CHARSET_SET
+ int charset = pop();
+ debugC(DEBUG_GENERAL, "Set userface charset to %d", charset);
+// loadCharset(charset);
+ break;
+ }
+ case 0xE8: // SO_CHARSET_COLOR
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ break;
+ case 0xE9: // SO_CURSOR_PUT
+ {
+ int y = pop();
+ int x = pop();
+
+ _system->warpMouse(x, y);
+ }
+ break;
+ default:
+ error("o8_cursorCommand: default case 0x%x", subOp);
+ }
+
+ VAR(VAR_CURSORSTATE) = _cursor.state;
+ VAR(VAR_USERPUT) = _userPut;
+}
+
+void ScummEngine_v8::o8_resourceRoutines() {
+ byte subOp = fetchScriptByte();
+ int resid = pop();
+
+ switch (subOp) {
+ case 0x3C: // Dummy case
+ break;
+ case 0x3D: // SO_HEAP_LOAD_COSTUME Load costume to heap
+ ensureResourceLoaded(rtCostume, resid);
+ break;
+ case 0x3E: // SO_HEAP_LOAD_OBJECT Load object to heap
+ {
+ int room = getObjectRoom(resid);
+ loadFlObject(resid, room);
+ }
+ break;
+ case 0x3F: // SO_HEAP_LOAD_ROOM Load room to heap
+ ensureResourceLoaded(rtRoom, resid);
+ break;
+ case 0x40: // SO_HEAP_LOAD_SCRIPT Load script to heap
+ ensureResourceLoaded(rtScript, resid);
+ break;
+ case 0x41: // SO_HEAP_LOAD_SOUND Load sound to heap
+ ensureResourceLoaded(rtSound, resid);
+ break;
+
+ case 0x42: // SO_HEAP_LOCK_COSTUME Lock costume in heap
+ res.lock(rtCostume, resid);
+ break;
+ case 0x43: // SO_HEAP_LOCK_ROOM Lock room in heap
+ res.lock(rtRoom, resid);
+ break;
+ case 0x44: // SO_HEAP_LOCK_SCRIPT Lock script in heap
+ res.lock(rtScript, resid);
+ break;
+ case 0x45: // SO_HEAP_LOCK_SOUND Lock sound in heap
+ res.lock(rtSound, resid);
+ break;
+ case 0x46: // SO_HEAP_UNLOCK_COSTUME Unlock costume
+ res.unlock(rtCostume, resid);
+ break;
+ case 0x47: // SO_HEAP_UNLOCK_ROOM Unlock room
+ res.unlock(rtRoom, resid);
+ break;
+ case 0x48: // SO_HEAP_UNLOCK_SCRIPT Unlock script
+ res.unlock(rtScript, resid);
+ break;
+ case 0x49: // SO_HEAP_UNLOCK_SOUND Unlock sound
+ res.unlock(rtSound, resid);
+ break;
+ case 0x4A: // SO_HEAP_NUKE_COSTUME Remove costume from heap
+ res.setResourceCounter(rtCostume, resid, 0x7F);
+ break;
+ case 0x4B: // SO_HEAP_NUKE_ROOM Remove room from heap
+ res.setResourceCounter(rtRoom, resid, 0x7F);
+ break;
+ case 0x4C: // SO_HEAP_NUKE_SCRIPT Remove script from heap
+ res.setResourceCounter(rtScript, resid, 0x7F);
+ break;
+ case 0x4D: // SO_HEAP_NUKE_SOUND Remove sound from heap
+ res.setResourceCounter(rtSound, resid, 0x7F);
+ break;
+ default:
+ error("o8_resourceRoutines: default case 0x%x", subOp);
+ }
+}
+
+void ScummEngine_v8::o8_roomOps() {
+ byte subOp = fetchScriptByte();
+ int a, b, c, d, e;
+
+ switch (subOp) {
+ case 0x52: // SO_ROOM_PALETTE Set room palette
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ setPalColor(d, a, b, c);
+ break;
+ case 0x55: // SO_ROOM_INTENSITY Set room intensity
+ // Not used in CMI???
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, a, a, b, c);
+ break;
+ case 0x57: // SO_ROOM_FADE Fade room
+ a = pop();
+ if (a) {
+ _switchRoomEffect = (byte)(a);
+ _switchRoomEffect2 = (byte)(a >> 8);
+ } else {
+ fadeIn(_newEffect);
+ }
+ break;
+ case 0x58: // SO_ROOM_RGB_INTENSITY Set room color intensity
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, b, c, d, e);
+ break;
+ case 0x59: // SO_ROOM_TRANSFORM Transform room
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ palManipulateInit(a, b, c, d);
+ break;
+ case 0x5A: // SO_ROOM_CYCLE_SPEED Set palette cycling speed
+ case 0x5B: // SO_ROOM_COPY_PALETTE Copy palette
+ error("o8_roomOps: unimplemented case %d", subOp);
+ break;
+ case 0x5C: // SO_ROOM_NEW_PALETTE New palette
+ a = pop();
+ setPalette(a);
+ break;
+ case 0x5D: // SO_ROOM_SAVE_GAME Save game
+ _saveTemporaryState = true;
+ _saveLoadSlot = 1;
+ _saveLoadFlag = 1;
+ break;
+ case 0x5E: // SO_ROOM_LOAD_GAME Load game
+ _saveTemporaryState = true;
+ _saveLoadSlot = 1;
+ _saveLoadFlag = 2;
+ break;
+ case 0x5F: // SO_ROOM_SATURATION Set saturation of room colors
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ desaturatePalette(a, b, c, d, e);
+ break;
+ default:
+ error("o8_roomOps: default case 0x%x", subOp);
+ }
+}
+
+void ScummEngine_v8::o8_actorOps() {
+ byte subOp = fetchScriptByte();
+ Actor *a;
+ int i, j;
+
+ if (subOp == 0x7A) {
+ _curActor = pop();
+ //printf("Setting current actor to %d\n", _curActor);
+ return;
+ }
+
+ a = derefActorSafe(_curActor, "o8_actorOps");
+ if (!a)
+ return;
+
+ switch (subOp) {
+ case 0x64: // SO_ACTOR_COSTUME Set actor costume
+ a->setActorCostume(pop());
+ break;
+ case 0x65: // SO_ACTOR_STEP_DIST Set actor width of steps
+ j = pop();
+ i = pop();
+ a->setActorWalkSpeed(i, j);
+ break;
+ case 0x67: // SO_ACTOR_ANIMATION_DEFAULT Set actor animation to default
+ a->_initFrame = 1;
+ a->_walkFrame = 2;
+ a->_standFrame = 3;
+ a->_talkStartFrame = 4;
+ a->_talkStopFrame = 5;
+ break;
+ case 0x68: // SO_ACTOR_ANIMATION_INIT Initialize animation
+ a->_initFrame = pop();
+ break;
+ case 0x69: // SO_ACTOR_ANIMATION_TALK Set actor animation to talk animation
+ a->_talkStopFrame = pop();
+ a->_talkStartFrame = pop();
+ break;
+ case 0x6A: // SO_ACTOR_ANIMATION_WALK Set actor animation to walk animation
+ a->_walkFrame = pop();
+ break;
+ case 0x6B: // SO_ACTOR_ANIMATION_STAND Set actor animation to standing animation
+ a->_standFrame = pop();
+ break;
+ case 0x6C: // SO_ACTOR_ANIMATION_SPEED Set speed of animation
+ a->setAnimSpeed(pop());
+ break;
+ case 0x6D: // SO_ACTOR_DEFAULT
+ a->initActor(0);
+ break;
+ case 0x6E: // SO_ACTOR_ELEVATION
+ a->setElevation(pop());
+ break;
+ case 0x6F: // SO_ACTOR_PALETTE Set actor palette
+ j = pop();
+ i = pop();
+ checkRange(31, 0, i, "Illegal palette slot %d");
+ a->setPalette(i, j);
+ break;
+ case 0x70: // SO_ACTOR_TALK_COLOR Set actor talk color
+ a->_talkColor = pop();
+ break;
+ case 0x71: // SO_ACTOR_NAME Set name of actor
+ loadPtrToResource(rtActorName, a->_number, NULL);
+ break;
+ case 0x72: // SO_ACTOR_WIDTH Set width of actor
+ a->_width = pop();
+ break;
+ case 0x73: // SO_ACTOR_SCALE Set scaling of actor
+ i = pop();
+ a->setScale(i, i);
+ break;
+ case 0x74: // SO_ACTOR_NEVER_ZCLIP
+ a->_forceClip = 0;
+ break;
+ case 0x75: // SO_ACTOR_ALWAYS_ZCLIP
+ a->_forceClip = pop();
+ // V8 uses 255 where we used to use 100
+ if (a->_forceClip == 255)
+ a->_forceClip = 100;
+ break;
+ case 0x76: // SO_ACTOR_IGNORE_BOXES Make actor ignore boxes
+ a->_ignoreBoxes = true;
+ a->_forceClip = 100;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 0x77: // SO_ACTOR_FOLLOW_BOXES Make actor follow boxes
+ a->_ignoreBoxes = false;
+ a->_forceClip = 100;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 0x78: // SO_ACTOR_SPECIAL_DRAW
+ a->_shadowMode = pop();
+ break;
+ case 0x79: // SO_ACTOR_TEXT_OFFSET Set text offset relative to actor
+ a->_talkPosY = pop();
+ a->_talkPosX = pop();
+ break;
+// case 0x7A: // SO_ACTOR_INIT Set current actor (handled above)
+ case 0x7B: // SO_ACTOR_VARIABLE Set actor variable
+ i = pop();
+ a->setAnimVar(pop(), i);
+ break;
+ case 0x7C: // SO_ACTOR_IGNORE_TURNS_ON Make actor ignore turns
+ a->_ignoreTurns = true;
+ break;
+ case 0x7D: // SO_ACTOR_IGNORE_TURNS_OFF Make actor follow turns
+ a->_ignoreTurns = false;
+ break;
+ case 0x7E: // SO_ACTOR_NEW New actor
+ a->initActor(2);
+ break;
+ case 0x7F: // SO_ACTOR_DEPTH Set actor Z position
+ a->_layer = pop();
+ break;
+ case 0x80: // SO_ACTOR_STOP
+ a->stopActorMoving();
+ a->startAnimActor(a->_standFrame);
+ break;
+ case 0x81: // SO_ACTOR_FACE Make actor face angle
+ a->_moving &= ~MF_TURN;
+ a->setDirection(pop());
+ break;
+ case 0x82: // SO_ACTOR_TURN Turn actor
+ a->turnToDirection(pop());
+ break;
+ case 0x83: // SO_ACTOR_WALK_SCRIPT Set walk script for actor?
+ a->_walkScript = pop();
+ break;
+ case 0x84: // SO_ACTOR_TALK_SCRIPT Set talk script for actor?
+ a->_talkScript = pop();
+ break;
+ case 0x85: // SO_ACTOR_WALK_PAUSE
+ a->_moving |= MF_FROZEN;
+ break;
+ case 0x86: // SO_ACTOR_WALK_RESUME
+ a->_moving &= ~MF_FROZEN;
+ break;
+ case 0x87: // SO_ACTOR_VOLUME Set volume of actor speech
+ a->_talkVolume = pop();
+ break;
+ case 0x88: // SO_ACTOR_FREQUENCY Set frequency of actor speech
+ a->_talkFrequency = pop();
+ break;
+ case 0x89: // SO_ACTOR_PAN
+ a->_talkPan = pop();
+ break;
+ default:
+ error("o8_actorOps: default case 0x%x", subOp);
+ }
+}
+
+void ScummEngine_v8::o8_cameraOps() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0x32: // SO_CAMERA_PAUSE
+ //debug(0, "freezeCamera NYI");
+ break;
+ case 0x33: // SO_CAMERA_RESUME
+ //debug(0, "unfreezeCamera NYI");
+ break;
+ default:
+ error("o8_cameraOps: default case 0x%x", subOp);
+ }
+}
+
+void ScummEngine_v8::o8_verbOps() {
+ byte subOp = fetchScriptByte();
+ VerbSlot *vs = NULL;
+ int slot, a, b;
+
+ if (subOp == 0x96) {
+ _curVerb = pop();
+ _curVerbSlot = getVerbSlot(_curVerb, 0);
+ checkRange(_numVerbs - 1, 0, _curVerbSlot, "Illegal new verb slot %d");
+ //printf("Setting current actor to %d\n", _curActor);
+ return;
+ }
+
+ assert(0 <= _curVerbSlot && _curVerbSlot < _numVerbs);
+ vs = &_verbs[_curVerbSlot];
+ assert(vs);
+
+ switch (subOp) {
+ case 0x96: // SO_VERB_INIT Choose verb number for editing
+ // handled above!
+ break;
+ case 0x97: // SO_VERB_NEW New verb
+ if (_curVerbSlot == 0) {
+ for (slot = 1; slot < _numVerbs; slot++) {
+ if (_verbs[slot].verbid == 0)
+ break;
+ }
+ if (slot >= _numVerbs) {
+ error("Too many verbs");
+ }
+ _curVerbSlot = slot;
+ }
+ vs = &_verbs[_curVerbSlot];
+ vs->verbid = _curVerb;
+ vs->color = 2;
+ vs->hicolor = 0;
+ vs->dimcolor = 8;
+ vs->type = kTextVerbType;
+ vs->charset_nr = _string[0]._default.charset;
+ vs->curmode = 0;
+ vs->saveid = 0;
+ vs->key = 0;
+ vs->center = 0;
+ vs->imgindex = 0;
+ break;
+ case 0x98: // SO_VERB_DELETE Delete verb
+ killVerb(_curVerbSlot);
+ break;
+ case 0x99: // SO_VERB_NAME Set verb name
+ loadPtrToResource(rtVerb, _curVerbSlot, NULL);
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 0x9A: // SO_VERB_AT Set verb (X,Y) placement
+ vs->curRect.top = pop();
+ vs->curRect.left = pop();
+ break;
+ case 0x9B: // SO_VERB_ON Turn verb on
+ vs->curmode = 1;
+ break;
+ case 0x9C: // SO_VERB_OFF Turn verb off
+ vs->curmode = 0;
+ break;
+ case 0x9D: // SO_VERB_COLOR Set verb color
+ vs->color = pop();
+ break;
+ case 0x9E: // SO_VERB_HICOLOR Set verb highlighted color
+ vs->hicolor = pop();
+ break;
+ case 0xA0: // SO_VERB_DIMCOLOR Set verb dimmed (disabled) color
+ vs->dimcolor = pop();
+ break;
+ case 0xA1: // SO_VERB_DIM
+ vs->curmode = 2;
+ break;
+ case 0xA2: // SO_VERB_KEY Set keypress to associate with verb
+ vs->key = pop();
+ break;
+ case 0xA3: // SO_VERB_IMAGE Set verb image
+ b = pop();
+ a = pop();
+ if (_curVerbSlot && a != vs->imgindex) {
+ setVerbObject(b, a, _curVerbSlot);
+ vs->type = kImageVerbType;
+ vs->imgindex = a;
+ }
+ break;
+ case 0xA4: // SO_VERB_NAME_STR Set verb name
+ a = pop();
+ if (a == 0) {
+ loadPtrToResource(rtVerb, _curVerbSlot, (const byte *)"");
+ } else {
+ loadPtrToResource(rtVerb, _curVerbSlot, getStringAddress(a));
+ }
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 0xA5: // SO_VERB_CENTER Center verb
+ vs->center = 1;
+ break;
+ case 0xA6: // SO_VERB_CHARSET Choose charset for verb
+ vs->charset_nr = pop();
+ break;
+ case 0xA7: // SO_VERB_LINE_SPACING Choose linespacing for verb
+ // FIXME - TODO
+ // Note: it seems that var596 stores the "line spacing". It is used by various
+ // scripts that place verbs for that.
+ // Also, var595 contains the vertical position at which to start placing verbs (330)
+ a = pop();
+ debug(0, "SO_VERB_LINE_SPACING %d: not yet implemented", a);
+ break;
+ default:
+ error("o8_verbops: default case 0x%x", subOp);
+ }
+}
+
+void ScummEngine_v8::o8_systemOps() {
+ byte subOp = fetchScriptByte();
+ switch (subOp) {
+ case 0x28: // SO_SYSTEM_RESTART Restart game
+ restart();
+ break;
+ case 0x29: // SO_SYSTEM_QUIT Quit game
+ shutDown();
+ break;
+ default:
+ error("o8_systemOps: invalid case 0x%x", subOp);
+ }
+}
+
+
+void ScummEngine_v8::o8_startVideo() {
+ int len = resStrLen(_scriptPointer);
+
+ SmushPlayer *sp = new SmushPlayer(this, 12);
+ sp->play((const char*)_scriptPointer);
+ delete sp;
+
+ _scriptPointer += len + 1;
+}
+
+void ScummEngine_v8::o8_kernelSetFunctions() {
+ // TODO
+ Actor *a;
+ int args[30];
+ int len = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 11: { // lockObject
+ int objidx = getObjectIndex(args[1]);
+ assert(objidx != -1);
+ res.lock(rtFlObject, _objs[objidx].fl_object_index);
+ break;
+ }
+ case 12: { // unlockObject
+ int objidx = getObjectIndex(args[1]);
+ assert(objidx != -1);
+ res.unlock(rtFlObject, _objs[objidx].fl_object_index);
+ break;
+ }
+ case 13: // remapCostume
+ a = derefActor(args[1], "o8_kernelSetFunctions:remapCostume");
+ a->remapActorPalette(args[2], args[3], args[4], -1);
+ break;
+ case 14: // remapCostumeInsert
+ a = derefActor(args[1], "o8_kernelSetFunctions:remapCostumeInsert");
+ a->remapActorPalette(args[2], args[3], args[4], args[5]);
+ break;
+ case 15: // setVideoFrameRate
+ // not used anymore (was smush frame rate)
+ break;
+ case 20: // setBoxScaleSlot
+ setBoxScaleSlot(args[1], args[2]);
+ break;
+ case 21: // setScaleSlot
+ setScaleSlot(args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
+ break;
+ case 22: // setBannerColors
+// debug(0, "o8_kernelSetFunctions: setBannerColors(%d, %d, %d, %d)", args[1], args[2], args[3], args[4]);
+ break;
+ case 23: // setActorChoreLimbFrame
+ a = derefActor(args[1], "o8_kernelSetFunctions:setActorChoreLimbFrame");
+
+ a->startAnimActor(args[2]);
+ a->animateLimb(args[3], args[4]);
+ break;
+ case 24: // clearTextQueue
+ removeBlastTexts();
+ break;
+ case 25: { // saveGameReadName
+ char *address = (char*)getStringAddress(args[2]);
+ char name[30];
+
+ if (!address) {
+ error("o8_kernelSetFunctions: saveGameReadName failed finding slot string %d", args[2]);
+ break;
+ }
+ getSavegameName(args[1] - 1, name);
+ if (strlen(name) > 0 && strlen(name) < 30)
+ strcpy(address, name);
+ break;
+ }
+ case 26: { // saveGame?
+ //char *address = (char*)getStringAddress(args[2]);
+ char address[30];
+ debug(0, "o8_kernelSetFunctions: saveGame?(%d, %s)", args[1], address);
+ break;
+ }
+ case 27: { // FIXME: This doesn't work
+ // saveGameRead
+ _saveLoadSlot = args[1];
+ _saveLoadFlag = 2;
+ _saveTemporaryState = false;
+ debug(0, "Sgl: %d", args[1]);
+ break;
+ }
+ case 28: // saveGameStampScreenshot
+ debug(0, "o8_kernelSetFunctions: saveGameStampScreenshot(%d, %d, %d, %d, %d, %d)", args[1], args[2], args[3], args[4], args[5], args[6]);
+ break;
+ case 29: // setKeyScript
+ _keyScriptKey = args[1];
+ _keyScriptNo = args[2];
+ break;
+ case 30: // killAllScriptsButMe
+ killAllScriptsExceptCurrent();
+ break;
+ case 31: // stopAllVideo
+ debug(0, "o8_kernelSetFunctions: stopAllVideo()");
+ break;
+ case 32: // writeRegistryValue
+ {
+ int idx = args[1];
+ int value = args[2];
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, idx);
+
+ debugC(DEBUG_GENERAL,"o8_kernelSetFunctions: writeRegistryValue(%s, %d)", (char *)ah->data, value);
+ }
+ break;
+ case 33: // paletteSetIntensity
+ debug(0, "o8_kernelSetFunctions: paletteSetIntensity(%d, %d)", args[1], args[2]);
+ break;
+ case 34: // queryQuit
+ if (_confirmExit)
+ confirmExitDialog();
+ else
+ _quit = true;
+ break;
+ case 108: // buildPaletteShadow
+ setupShadowPalette(args[1], args[2], args[3], args[4], args[5], args[6]);
+ break;
+ case 109: // setPaletteShadow
+ setupShadowPalette(0, args[1], args[2], args[3], args[4], args[5]);
+ break;
+ case 118: // blastShadowObject
+ enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 3);
+ break;
+ case 119: // superBlastObject
+ enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0);
+ break;
+
+ default:
+ error("o8_kernelSetFunctions: default case 0x%x (len = %d)", args[0], len);
+ }
+}
+
+void ScummEngine_v8::o8_kernelGetFunctions() {
+ int args[30];
+ int len = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 0x73: // getWalkBoxAt
+ push(getSpecialBox(args[1], args[2]));
+ break;
+ case 0x74: // isPointInBox
+ push(checkXYInBoxBounds(args[3], args[1], args[2]));
+ break;
+ case 0xD3: // getKeyState
+ push(getKeyState(args[1]));
+ break;
+ case 0xCE: // getRGBSlot
+ push(remapPaletteColor(args[1], args[2], args[3], -1));
+ break;
+ case 0xD7: // getBox
+ push(checkXYInBoxBounds(args[3], args[1], args[2]));
+ break;
+ case 0xD8: { // findBlastObject
+ int x = args[1] + (camera._cur.x & 7);
+ int y = args[2] + _screenTop;
+ BlastObject *eo;
+
+ for (int i = _blastObjectQueuePos - 1; i >= 0; i--) {
+ eo = &_blastObjectQueue[i];
+
+ if (eo->rect.contains(x, y) && !getClass(eo->number, kObjectClassUntouchable)) {
+ push(eo->number);
+ return;
+ }
+ }
+ push(0);
+ break;
+ }
+ case 0xD9: { // actorHit - used, for example, to detect ship collision
+ // during ship-to-ship combat.
+ Actor *a = derefActor(args[1], "actorHit");
+ push(a->actorHitTest(args[2], args[3] + _screenTop));
+ break;
+ }
+ case 0xDA: // lipSyncWidth
+ push(_imuseDigital->getCurVoiceLipSyncWidth());
+ break;
+ case 0xDB: // lipSyncHeight
+ push(_imuseDigital->getCurVoiceLipSyncHeight());
+ break;
+ case 0xDC: // actorTalkAnimation
+ {
+ Actor *a = derefActor(args[1], "actorTalkAnimation");
+ push(a->_talkStartFrame);
+ }
+ break;
+ case 0xDD: // getGroupSfxVol
+ push(_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 2);
+ break;
+ case 0xDE: // getGroupVoiceVol
+ push(_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) / 2);
+ break;
+ case 0xDF: // getGroupMusicVol
+ push(_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 2);
+ break;
+ case 0xE0: // readRegistryValue
+ {
+ int idx = args[1];
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, idx);
+ if (!strcmp((char *)ah->data, "SFX Volume"))
+ push(ConfMan.getInt("sfx_volume") / 2);
+ else if (!strcmp((char *)ah->data, "Voice Volume"))
+ push(ConfMan.getInt("speech_volume") / 2);
+ else if (!strcmp((char *)ah->data, "Music Volume"))
+ push(ConfMan.getInt("music_volume") / 2);
+ else if (!strcmp((char *)ah->data, "Text Status"))
+ push(ConfMan.getBool("subtitles"));
+ else if (!strcmp((char *)ah->data, "Object Names"))
+ push(ConfMan.getBool("object_labels"));
+ else if (!strcmp((char *)ah->data, "Saveload Page"))
+ push(14);
+ else // Use defaults
+ push(-1);
+ debugC(DEBUG_GENERAL,"o8_kernelGetFunctions: readRegistryValue(%s)", (char *)ah->data);
+ }
+ break;
+ case 0xE1: // imGetMusicPosition
+ push(_imuseDigital->getCurMusicPosInMs());
+ break;
+ case 0xE2: // musicLipSyncWidth
+ push(_imuseDigital->getCurMusicLipSyncWidth(args[1]));
+ break;
+ case 0xE3: // musicLipSyncHeight
+ push(_imuseDigital->getCurMusicLipSyncHeight(args[1]));
+ break;
+ default:
+ error("o8_kernelGetFunctions: default case 0x%x (len = %d)", args[0], len);
+ }
+
+}
+
+void ScummEngine_v8::o8_getActorChore() {
+ int actnum = pop();
+ Actor *a = derefActor(actnum, "o8_getActorChore");
+ push(a->_frame);
+}
+
+void ScummEngine_v8::o8_getActorZPlane() {
+ int actnum = pop();
+ Actor *a = derefActor(actnum, "o8_getActorZPlane");
+
+ int z = a->_forceClip;
+ if (z == 100) {
+ z = getMaskFromBox(a->_walkbox);
+ if (z > gdi._numZBuffer - 1)
+ z = gdi._numZBuffer - 1;
+ }
+
+ push(z);
+}
+
+
+void ScummEngine_v8::o8_getObjectImageX() {
+ int i = getObjectIndex(pop());
+ assert(i);
+ push(_objs[i].x_pos);
+}
+
+void ScummEngine_v8::o8_getObjectImageY() {
+ int i = getObjectIndex(pop());
+ assert(i);
+ push(_objs[i].y_pos);
+}
+
+void ScummEngine_v8::o8_getObjectImageWidth() {
+ int i = getObjectIndex(pop());
+ assert(i);
+ push(_objs[i].width);
+}
+
+void ScummEngine_v8::o8_getObjectImageHeight() {
+ int i = getObjectIndex(pop());
+ assert(i);
+ push(_objs[i].height);
+}
+
+void ScummEngine_v8::o8_getStringWidth() {
+ int charset = pop();
+ int oldID = _charset->getCurID();
+ int width;
+ const byte *msg = _scriptPointer;
+ byte transBuf[512];
+
+ // Skip to the next instruction
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+
+ translateText(msg, transBuf);
+ msg = transBuf;
+
+ // Temporary set the specified charset id
+ _charset->setCurID(_string[charset].charset);
+ // Determine the strings width
+ width = _charset->getStringWidth(0, msg);
+ // Revert to old font
+ _charset->setCurID(oldID);
+
+ push(width);
+}
+
+void ScummEngine_v8::o8_drawObject() {
+ int state = pop();
+ int y = pop();
+ int x = pop();
+ int obj = pop();
+ setObjectState(obj, state, x, y);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v80he.cpp b/engines/scumm/script_v80he.cpp
new file mode 100644
index 0000000000..307ab75d3f
--- /dev/null
+++ b/engines/scumm/script_v80he.cpp
@@ -0,0 +1,811 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-file.h"
+#include "common/config-manager.h"
+#include "common/str.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v80he, x)
+
+void ScummEngine_v80he::setupOpcodes() {
+ static const OpcodeEntryV80he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o72_pushDWord),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o72_getScriptString),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o72_isAnyOf),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o80_createSound),
+ OPCODE(o80_getFileSize),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o80_stringToInt),
+ OPCODE(o80_getSoundVar),
+ OPCODE(o80_localizeArrayToRoom),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o80_sourceDebug),
+ OPCODE(o80_readConfigFile),
+ OPCODE(o80_writeConfigFile),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o72_resetCutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o72_findObjectWithClassOf),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o72_getObjectImageX),
+ OPCODE(o72_getObjectImageY),
+ OPCODE(o72_captureWizImage),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o72_getTimer),
+ OPCODE(o72_setTimer),
+ OPCODE(o72_getSoundPosition),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o72_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o72_startObject),
+ OPCODE(o72_drawObject),
+ OPCODE(o72_printWizImage),
+ OPCODE(o72_getArrayDimSize),
+ /* 64 */
+ OPCODE(o72_getNumFreeArrays),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o80_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o80_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o70_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_invalid),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o70_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o70_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o70_resourceRoutines),
+ /* 9C */
+ OPCODE(o72_roomOps),
+ OPCODE(o72_actorOps),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o72_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o72_arrayOps),
+ OPCODE(o6_invalid),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o60_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorAnimCounter1),
+ /* AC */
+ OPCODE(o80_drawWizPolygon),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o72_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o72_talkActor),
+ OPCODE(o72_talkEgo),
+ /* BC */
+ OPCODE(o72_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o72_dim2dimArray),
+ OPCODE(o72_traceStatus),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o72_kernelGetFunctions),
+ OPCODE(o70_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o72_drawWizImage),
+ OPCODE(o72_debugInput),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o72_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o72_openFile),
+ OPCODE(o72_readFile),
+ /* DC */
+ OPCODE(o72_writeFile),
+ OPCODE(o72_findAllObjects),
+ OPCODE(o72_deleteFile),
+ OPCODE(o72_rename),
+ /* E0 */
+ OPCODE(o80_drawLine),
+ OPCODE(o72_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o80_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o72_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o70_copyString),
+ OPCODE(o70_getStringWidth),
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_appendString),
+ /* F0 */
+ OPCODE(o70_concatString),
+ OPCODE(o70_compareString),
+ OPCODE(o70_isResourceLoaded),
+ OPCODE(o72_readINI),
+ /* F4 */
+ OPCODE(o72_writeINI),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o70_getCharIndexInString),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o72_getResourceSize),
+ OPCODE(o72_setFilePath),
+ OPCODE(o72_setWindowCaption),
+ OPCODE(o70_polygonOps),
+ /* FC */
+ OPCODE(o70_polygonHit),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV80he = opcodes;
+}
+
+void ScummEngine_v80he::executeOpcode(byte i) {
+ OpcodeProcV80he op = _opcodesV80he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v80he::getOpcodeDesc(byte i) {
+ return _opcodesV80he[i].desc;
+}
+
+void ScummEngine_v80he::o80_createSound() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 27:
+ createSound(_heSndResId, pop());
+ break;
+ case 217:
+ createSound(_heSndResId, -1);
+ break;
+ case 232:
+ _heSndResId = pop();
+ break;
+ case 255:
+ // dummy case
+ break;
+ default:
+ error("o80_createSound: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v80he::o80_getFileSize() {
+ byte filename[256];
+
+ copyScriptString(filename, sizeof(filename));
+
+ Common::File f;
+ if (!f.open((char *)filename)) {
+ push(-1);
+ } else {
+ push(f.size());
+ f.close();
+ }
+}
+
+void ScummEngine_v80he::o80_stringToInt() {
+ int id, len, val;
+ byte *addr;
+ char string[100];
+
+ id = pop();
+
+ addr = getStringAddress(id);
+ if (!addr)
+ error("o80_stringToInt: Reference to zeroed array pointer (%d)", id);
+
+ len = resStrLen(getStringAddress(id)) + 1;
+ memcpy(string, addr, len);
+ val = atoi(string);
+ push(val);
+}
+
+void ScummEngine_v80he::o80_getSoundVar() {
+ int var = pop();
+ int snd = pop();
+ push(_sound->getSoundVar(snd, var));
+}
+
+void ScummEngine_v80he::o80_localizeArrayToRoom() {
+ int slot = pop();
+ localizeArray(slot, 0xFF);
+}
+
+void ScummEngine_v80he::o80_sourceDebug() {
+ fetchScriptDWord();
+ fetchScriptDWord();
+}
+
+void ScummEngine_v80he::o80_readConfigFile() {
+ byte option[128], section[128], filename[256];
+ ArrayHeader *ah;
+ Common::String entry;
+ int len;
+
+ copyScriptString(option, sizeof(option));
+ copyScriptString(section, sizeof(section));
+ copyScriptString(filename, sizeof(filename));
+ convertFilePath(filename, true);
+
+ Common::ConfigFile ConfFile;
+ ConfFile.loadFromFile((const char *)filename);
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 43: // HE 100
+ case 6: // number
+ ConfFile.getKey((const char *)option, (const char *)section, entry);
+
+ push(atoi(entry.c_str()));
+ break;
+ case 77: // HE 100
+ case 7: // string
+ ConfFile.getKey((const char *)option, (const char *)section, entry);
+
+ writeVar(0, 0);
+ len = resStrLen((const byte *)entry.c_str());
+ ah = defineArray(0, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, entry.c_str(), len);
+ push(readVar(0));
+ break;
+ default:
+ error("o80_readConfigFile: default type %d", subOp);
+ }
+
+ debug(1, "o80_readConfigFile: Filename %s Section %s Option %s Value %s", filename, section, option, entry.c_str());
+}
+
+void ScummEngine_v80he::o80_writeConfigFile() {
+ byte filename[256], section[256], option[256], string[1024];
+ int value;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 43: // HE 100
+ case 6: // number
+ value = pop();
+ sprintf((char *)string, "%d", value);
+ copyScriptString(option, sizeof(option));
+ copyScriptString(section, sizeof(section));
+ copyScriptString(filename, sizeof(filename));
+ convertFilePath(filename, true);
+ break;
+ case 77: // HE 100
+ case 7: // string
+ copyScriptString(string, sizeof(string));
+ copyScriptString(option, sizeof(option));
+ copyScriptString(section, sizeof(section));
+ copyScriptString(filename, sizeof(filename));
+ convertFilePath(filename, true);
+ break;
+ default:
+ error("o80_writeConfigFile: default type %d", subOp);
+ }
+
+ Common::ConfigFile ConfFile;
+ ConfFile.loadFromFile((const char *)filename);
+ ConfFile.setKey((char *)option, (char *)section, (char *)string);
+ ConfFile.saveToFile((const char *)filename);
+ debug(1,"o80_writeConfigFile: Filename %s Section %s Option %s String %s", filename, section, option, string);
+}
+
+void ScummEngine_v80he::o80_cursorCommand() {
+ int a, i;
+ int args[16];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0x13:
+ case 0x14:
+ a = pop();
+ _wiz->loadWizCursor(a);
+ break;
+ case 0x3C:
+ pop();
+ a = pop();
+ _wiz->loadWizCursor(a);
+ break;
+ case 0x90: // SO_CURSOR_ON Turn cursor on
+ _cursor.state = 1;
+ break;
+ case 0x91: // SO_CURSOR_OFF Turn cursor off
+ _cursor.state = 0;
+ break;
+ case 0x92: // SO_USERPUT_ON
+ _userPut = 1;
+ break;
+ case 0x93: // SO_USERPUT_OFF
+ _userPut = 0;
+ break;
+ case 0x94: // SO_CURSOR_SOFT_ON Turn soft cursor on
+ _cursor.state++;
+ if (_cursor.state > 1)
+ error("Cursor state greater than 1 in script");
+ break;
+ case 0x95: // SO_CURSOR_SOFT_OFF Turn soft cursor off
+ _cursor.state--;
+ break;
+ case 0x96: // SO_USERPUT_SOFT_ON
+ _userPut++;
+ break;
+ case 0x97: // SO_USERPUT_SOFT_OFF
+ _userPut--;
+ break;
+ case 0x9C: // SO_CHARSET_SET
+ initCharset(pop());
+ break;
+ case 0x9D: // SO_CHARSET_COLOR
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ break;
+ default:
+ error("o80_cursorCommand: default case %x", subOp);
+ }
+
+ VAR(VAR_CURSORSTATE) = _cursor.state;
+ VAR(VAR_USERPUT) = _userPut;
+}
+
+void ScummEngine_v80he::o80_setState() {
+ int state = pop();
+ int obj = pop();
+
+ state &= 0x7FFF;
+ putState(obj, state);
+ removeObjectFromDrawQue(obj);
+}
+
+void ScummEngine_v80he::o80_drawWizPolygon() {
+ WizImage wi;
+ wi.x1 = wi.y1 = pop();
+ wi.resNum = pop();
+ wi.state = 0;
+ wi.flags = kWIFIsPolygon;
+ _wiz->displayWizImage(&wi);
+}
+
+/**
+ * Draw a 'line' between two points.
+ *
+ * @param x1 the starting x coordinate
+ * @param y1 the starting y coordinate
+ * @param x the ending x coordinate
+ * @param y the ending y coordinate
+ * @param step the step size used to render the line, only ever 'step'th point is drawn
+ * @param type the line type -- points are rendered by drawing actors (type == 2),
+ * wiz images (type == 3), or pixels (any other type)
+ * @param id the id of an actor, wizimage or color (low bit) & flag (high bit)
+ */
+void ScummEngine_v80he::drawLine(int x1, int y1, int x, int y, int step, int type, int id) {
+ if (step < 0) {
+ step = -step;
+ }
+ if (step == 0) {
+ step = 1;
+ }
+
+ const int dx = x - x1;
+ const int dy = y - y1;
+
+ const int absDX = ABS(dx);
+ const int absDY = ABS(dy);
+
+ const int maxDist = MAX(absDX, absDY);
+
+ y = y1;
+ x = x1;
+
+
+ if (type == 2) {
+ Actor *a = derefActor(id, "drawLine");
+ a->drawActorToBackBuf(x, y);
+ } else if (type == 3) {
+ WizImage wi;
+ wi.flags = 0;
+ wi.y1 = y;
+ wi.x1 = x;
+ wi.resNum = id;
+ wi.state = 0;
+ _wiz->displayWizImage(&wi);
+ } else {
+ drawPixel(x, y, id);
+ }
+
+ int stepCount = 0;
+ int tmpX = 0;
+ int tmpY = 0;
+ for (int i = 0; i <= maxDist; i++) {
+ tmpX += absDX;
+ tmpY += absDY;
+
+ int drawFlag = 0;
+
+ if (tmpX > maxDist) {
+ drawFlag = 1;
+ tmpX -= maxDist;
+
+ if (dx >= 0) {
+ x++;
+ } else {
+ x--;
+ }
+ }
+ if (tmpY > maxDist) {
+ drawFlag = dy;
+ tmpY -= maxDist;
+
+ if (dy >= 0) {
+ y++;
+ } else {
+ y--;
+ }
+ }
+
+ if (drawFlag == 0)
+ continue;
+
+ if ((stepCount++ % step) != 0 && maxDist != i)
+ continue;
+
+ if (type == 2) {
+ Actor *a = derefActor(id, "drawLine");
+ a->drawActorToBackBuf(x, y);
+ } else if (type == 3) {
+ WizImage wi;
+ wi.flags = 0;
+ wi.y1 = y;
+ wi.x1 = x;
+ wi.resNum = id;
+ wi.state = 0;
+ _wiz->displayWizImage(&wi);
+ } else {
+ drawPixel(x, y, id);
+ }
+ }
+}
+
+void ScummEngine_v80he::drawPixel(int x, int y, int flags) {
+ byte *src, *dst;
+ VirtScreen *vs;
+
+ if (x < 0 || x > 639)
+ return;
+
+ if (y < 0)
+ return;
+
+ if ((vs = findVirtScreen(y)) == NULL)
+ return;
+
+ markRectAsDirty(vs->number, x, y, x, y + 1);
+
+ if ((flags & 0x4000) || (flags & 0x2000000)) {
+ src = vs->getPixels(x, y);
+ dst = vs->getBackPixels(x, y);
+ *dst = *src;
+ } else if ((flags & 0x2000) || (flags & 4000000)) {
+ src = vs->getBackPixels(x, y);
+ dst = vs->getPixels(x, y);
+ *dst = *src;
+ } else if (flags & 0x8000000) {
+ error("drawPixel: unsupported flag 0x%x", flags);
+ } else {
+ dst = vs->getPixels(x, y);
+ *dst = flags;
+ if ((flags & 0x8000) || (flags & 0x1000000)) {
+ dst = vs->getBackPixels(x, y);
+ *dst = flags;
+ }
+ }
+}
+
+void ScummEngine_v80he::o80_drawLine() {
+ int id, step, x, y, x1, y1;
+
+ step = pop();
+ id = pop();
+ y = pop();
+ x = pop();
+ y1 = pop();
+ x1 = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 55:
+ drawLine(x1, y1, x, y, step, 2, id);
+ break;
+ case 63:
+ drawLine(x1, y1, x, y, step, 3, id);
+ break;
+ case 66:
+ drawLine(x1, y1, x, y, step, 1, id);
+ break;
+ default:
+ error("o80_drawLine: default case %d", subOp);
+ }
+
+}
+
+void ScummEngine_v80he::o80_pickVarRandom() {
+ int num;
+ int args[100];
+ int32 dim1end;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ int value = fetchScriptWord();
+
+ if (readVar(value) == 0) {
+ defineArray(value, kDwordArray, 0, 0, 0, num);
+ if (value & 0x8000)
+ localizeArray(readVar(value), 0xFF);
+ else if (value & 0x4000)
+ localizeArray(readVar(value), _currentScript);
+
+ if (num > 0) {
+ int16 counter = 0;
+ do {
+ writeArray(value, 0, counter + 1, args[counter]);
+ } while (++counter < num);
+ }
+
+ shuffleArray(value, 1, num);
+ writeArray(value, 0, 0, 2);
+ push(readArray(value, 0, 1));
+ return;
+ }
+
+ num = readArray(value, 0, 0);
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(value));
+ dim1end = FROM_LE_32(ah->dim1end);
+
+ if (dim1end < num) {
+ int32 var_2 = readArray(value, 0, num - 1);
+ shuffleArray(value, 1, dim1end);
+ num = 1;
+ if (readArray(value, 0, 1) == var_2 && dim1end >= 3) {
+ int32 tmp = readArray(value, 0, 2);
+ writeArray(value, 0, num, tmp);
+ writeArray(value, 0, 2, var_2);
+ }
+ }
+
+ writeArray(value, 0, 0, num + 1);
+ push(readArray(value, 0, num));
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/script_v90he.cpp b/engines/scumm/script_v90he.cpp
new file mode 100644
index 0000000000..0536506534
--- /dev/null
+++ b/engines/scumm/script_v90he.cpp
@@ -0,0 +1,2636 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern_he.h"
+#include "scumm/logic_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/sprite_he.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v90he, x)
+
+void ScummEngine_v90he::setupOpcodes() {
+ static const OpcodeEntryV90he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o72_pushDWord),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o72_getScriptString),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o90_dup_n),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o72_isAnyOf),
+ /* 1C */
+ OPCODE(o90_wizImageOps),
+ OPCODE(o90_min),
+ OPCODE(o90_max),
+ OPCODE(o90_sin),
+ /* 20 */
+ OPCODE(o90_cos),
+ OPCODE(o90_sqrt),
+ OPCODE(o90_atan2),
+ OPCODE(o90_getSegmentAngle),
+ /* 24 */
+ OPCODE(o90_getDistanceBetweenPoints),
+ OPCODE(o90_getSpriteInfo),
+ OPCODE(o90_setSpriteInfo),
+ OPCODE(o90_getSpriteGroupInfo),
+ /* 28 */
+ OPCODE(o90_setSpriteGroupInfo),
+ OPCODE(o90_getWizData),
+ OPCODE(o90_getActorData),
+ OPCODE(o90_startScriptUnk),
+ /* 2C */
+ OPCODE(o90_jumpToScriptUnk),
+ OPCODE(o90_videoOps),
+ OPCODE(o90_getVideoData),
+ OPCODE(o90_floodFill),
+ /* 30 */
+ OPCODE(o90_mod),
+ OPCODE(o90_shl),
+ OPCODE(o90_shr),
+ OPCODE(o90_xor),
+ /* 34 */
+ OPCODE(o90_findAllObjectsWithClassOf),
+ OPCODE(o90_getPolygonOverlap),
+ OPCODE(o90_cond),
+ OPCODE(o90_dim2dim2Array),
+ /* 38 */
+ OPCODE(o90_redim2dimArray),
+ OPCODE(o90_getLinesIntersectionPoint),
+ OPCODE(o90_sortArray),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o90_getObjectData),
+ OPCODE(o80_createSound),
+ OPCODE(o80_getFileSize),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o80_stringToInt),
+ OPCODE(o80_getSoundVar),
+ OPCODE(o80_localizeArrayToRoom),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o80_sourceDebug),
+ OPCODE(o80_readConfigFile),
+ OPCODE(o80_writeConfigFile),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o72_resetCutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o72_findObjectWithClassOf),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o72_getObjectImageX),
+ OPCODE(o72_getObjectImageY),
+ OPCODE(o72_captureWizImage),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o72_getTimer),
+ OPCODE(o72_setTimer),
+ OPCODE(o72_getSoundPosition),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o72_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o72_startObject),
+ OPCODE(o72_drawObject),
+ OPCODE(o72_printWizImage),
+ OPCODE(o72_getArrayDimSize),
+ /* 64 */
+ OPCODE(o72_getNumFreeArrays),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o80_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o80_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o70_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_invalid),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o70_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o70_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o90_getPaletteData),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o70_resourceRoutines),
+ /* 9C */
+ OPCODE(o72_roomOps),
+ OPCODE(o72_actorOps),
+ OPCODE(o90_paletteOps),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o72_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o72_arrayOps),
+ OPCODE(o90_fontUnk),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o60_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o90_getActorAnimProgress),
+ /* AC */
+ OPCODE(o80_drawWizPolygon),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o72_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o72_talkActor),
+ OPCODE(o72_talkEgo),
+ /* BC */
+ OPCODE(o72_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o72_dim2dimArray),
+ OPCODE(o72_traceStatus),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o90_kernelGetFunctions),
+ OPCODE(o90_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o72_drawWizImage),
+ OPCODE(o72_debugInput),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o72_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o72_openFile),
+ OPCODE(o72_readFile),
+ /* DC */
+ OPCODE(o72_writeFile),
+ OPCODE(o72_findAllObjects),
+ OPCODE(o72_deleteFile),
+ OPCODE(o72_rename),
+ /* E0 */
+ OPCODE(o80_drawLine),
+ OPCODE(o72_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o80_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o72_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o70_copyString),
+ OPCODE(o70_getStringWidth),
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_appendString),
+ /* F0 */
+ OPCODE(o70_concatString),
+ OPCODE(o70_compareString),
+ OPCODE(o70_isResourceLoaded),
+ OPCODE(o72_readINI),
+ /* F4 */
+ OPCODE(o72_writeINI),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o70_getCharIndexInString),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o72_getResourceSize),
+ OPCODE(o72_setFilePath),
+ OPCODE(o72_setWindowCaption),
+ OPCODE(o70_polygonOps),
+ /* FC */
+ OPCODE(o70_polygonHit),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV90he = opcodes;
+}
+
+void ScummEngine_v90he::executeOpcode(byte i) {
+ OpcodeProcV90he op = _opcodesV90he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v90he::getOpcodeDesc(byte i) {
+ return _opcodesV90he[i].desc;
+}
+
+void ScummEngine_v90he::o90_dup_n() {
+ int num;
+ int args[16];
+
+ push(fetchScriptWord());
+ num = getStackList(args, ARRAYSIZE(args));
+
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < num; j++)
+ push(args[j]);
+ }
+}
+
+void ScummEngine_v90he::o90_min() {
+ int a = pop();
+ int b = pop();
+
+ if (b < a) {
+ push(b);
+ } else {
+ push(a);
+ }
+}
+
+void ScummEngine_v90he::o90_max() {
+ int a = pop();
+ int b = pop();
+
+ if (b > a) {
+ push(b);
+ } else {
+ push(a);
+ }
+}
+
+void ScummEngine_v90he::o90_sin() {
+ double a = pop() * PI / 180.;
+ push((int)(sin(a) * 100000));
+}
+
+void ScummEngine_v90he::o90_cos() {
+ double a = pop() * PI / 180.;
+ push((int)(cos(a) * 100000));
+}
+
+void ScummEngine_v90he::o90_sqrt() {
+ int i = pop();
+ if (i < 2) {
+ push(i);
+ } else {
+ push((int)sqrt((double)(i + 1)));
+ }
+}
+
+void ScummEngine_v90he::o90_atan2() {
+ int y = pop();
+ int x = pop();
+ int a = (int)(atan2((double)y, (double)x) * 180. / PI);
+ if (a < 0) {
+ a += 360;
+ }
+ push(a);
+}
+
+void ScummEngine_v90he::o90_getSegmentAngle() {
+ int y1 = pop();
+ int x1 = pop();
+ int dy = y1 - pop();
+ int dx = x1 - pop();
+ int a = (int)(atan2((double)dy, (double)dx) * 180. / PI);
+ if (a < 0) {
+ a += 360;
+ }
+ push(a);
+}
+
+void ScummEngine_v90he::o90_getActorData() {
+ Actor *a;
+
+ int subOp = pop();
+ int val = pop();
+ int act = pop();
+
+ a = derefActor(act, "o90_getActorData");
+
+ switch (subOp) {
+ case 1:
+ push(a->isUserConditionSet(val));
+ break;
+ case 2:
+ checkRange(15, 0, val, "Limb %d out of range");
+ push(a->_cost.frame[val]);
+ break;
+ case 3:
+ push(a->getAnimSpeed());
+ break;
+ case 4:
+ push(a->_shadowMode);
+ break;
+ case 5:
+ push(a->_layer);
+ break;
+ case 6:
+ push(a->_hePaletteNum);
+ break;
+ default:
+ error("o90_getActorData: Unknown actor property %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_startScriptUnk() {
+ int args[25];
+ int script, cycle;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ cycle = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args, cycle);
+}
+
+void ScummEngine_v90he::o90_jumpToScriptUnk() {
+ int args[25];
+ int script, cycle;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ cycle = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ stopObjectCode();
+ runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args, cycle);
+}
+
+void ScummEngine_v90he::o90_videoOps() {
+ // Uses Smacker video
+ int status = fetchScriptByte();
+ int subOp = status - 49;
+
+ switch (subOp) {
+ case 0:
+ copyScriptString(_videoParams.filename, sizeof(_videoParams.filename));
+ _videoParams.status = status;
+ break;
+ case 5:
+ _videoParams.flags |= pop();
+ break;
+ case 8:
+ memset(_videoParams.filename, 0, sizeof(_videoParams.filename));
+ _videoParams.unk2 = pop();
+ break;
+ case 14:
+ _videoParams.wizResNum = pop();
+ if (_videoParams.wizResNum)
+ _videoParams.flags |= 2;
+ break;
+ case 116:
+ _videoParams.status = status;
+ break;
+ case 206:
+ if (_videoParams.status == 49) {
+ // Start video
+ if (_videoParams.flags == 0)
+ _videoParams.flags = 4;
+
+ if (_videoParams.flags == 2) {
+ // result = startVideo(_videoParams.filename, _videoParams.flags, _videoParams.wizResNum);
+ // VAR(119) = result;
+ } else {
+ // result = startVideo(_videoParams.filename, _videoParams.flags);
+ // VAR(119) = result;
+ }
+ } else if (_videoParams.status == 165) {
+ // Stop video
+ }
+ break;
+ default:
+ error("o90_videoOps: unhandled case %d", subOp);
+ }
+
+ debug(1, "o90_videoOps stub (%d)", subOp);
+}
+
+void ScummEngine_v90he::o90_getVideoData() {
+ // Uses Smacker video
+ byte subOp = fetchScriptByte();
+ subOp -= 32;
+
+ switch (subOp) {
+ case 0: // Get width
+ pop();
+ break;
+ case 1: // Get height
+ pop();
+ break;
+ case 4: // Get frame count
+ pop();
+ break;
+ case 20: // Get current frame
+ pop();
+ break;
+ case 31: // Get image number
+ pop();
+ break;
+ case 107: // Get statistics
+ pop();
+ pop();
+ break;
+ default:
+ error("o90_getVideoData: unhandled case %d", subOp);
+ }
+
+ push(-1);
+ debug(1, "o90_getVideoData stub (%d)", subOp);
+}
+
+void ScummEngine_v90he::o90_wizImageOps() {
+ int a, b;
+
+ int subOp = fetchScriptByte();
+ subOp -= 46;
+
+ switch (subOp) {
+ case -14: // HE99+
+ _wizParams.processFlags |= kWPFUseDefImgWidth;
+ _wizParams.resDefImgW = pop();
+ break;
+ case -13: // HE99+
+ _wizParams.processFlags |= kWPFUseDefImgHeight;
+ _wizParams.resDefImgH = pop();
+ break;
+ case 0:
+ // Dummy case
+ pop();
+ break;
+ case 1:
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ break;
+ case 2:
+ _wizParams.processMode = 1;
+ break;
+ case 3:
+ _wizParams.processFlags |= kWPFUseFile;
+ _wizParams.processMode = 3;
+ copyScriptString(_wizParams.filename, sizeof(_wizParams.filename));
+ break;
+ case 4:
+ _wizParams.processFlags |= kWPFUseFile;
+ _wizParams.processMode = 4;
+ copyScriptString(_wizParams.filename, sizeof(_wizParams.filename));
+ _wizParams.fileWriteMode = pop();
+ break;
+ case 5:
+ _wizParams.processFlags |= kWPFClipBox | 0x100;
+ _wizParams.processMode = 2;
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ _wizParams.compType = pop();
+ break;
+ case 6:
+ _wizParams.processFlags |= kWPFNewState;
+ _wizParams.img.state = pop();
+ break;
+ case 7:
+ _wizParams.processFlags |= kWPFRotate;
+ _wizParams.angle = pop();
+ break;
+ case 8:
+ _wizParams.processFlags |= kWPFNewFlags;
+ _wizParams.img.flags |= pop();
+ break;
+ case 10:
+ _wizParams.img.flags = pop();
+ _wizParams.img.state = pop();
+ _wizParams.img.y1 = pop();
+ _wizParams.img.x1 = pop();
+ _wizParams.img.resNum = pop();
+ _wiz->displayWizImage(&_wizParams.img);
+ break;
+ case 11:
+ _wizParams.img.resNum = pop();
+ _wizParams.processMode = 0;
+ _wizParams.processFlags = 0;
+ _wizParams.remapNum = 0;
+ _wizParams.img.flags = 0;
+ _wizParams.field_184 = 0;
+ _wizParams.field_180 = 0;
+ _wizParams.spriteId = 0;
+ _wizParams.spriteGroup = 0;
+ break;
+ case 16: // HE99+
+ _wizParams.processFlags |= kWPFMaskImg;
+ _wizParams.sourceImage = pop();
+ break;
+ case 19:
+ case 108:
+ _wizParams.processFlags |= kWPFSetPos;
+ _wizParams.img.y1 = pop();
+ _wizParams.img.x1 = pop();
+ break;
+ case 20:
+ case 203: // HE98+
+ b = pop();
+ a = pop();
+ _wizParams.processFlags |= kWPFRemapPalette;
+ _wizParams.processMode = 6;
+ if (_wizParams.remapNum == 0) {
+ memset(_wizParams.remapIndex, 0, sizeof(_wizParams.remapIndex));
+ } else {
+ assert(_wizParams.remapNum < ARRAYSIZE(_wizParams.remapIndex));
+ _wizParams.remapIndex[_wizParams.remapNum] = a;
+ _wizParams.remapColor[a] = b;
+ ++_wizParams.remapNum;
+ }
+ break;
+ case 21:
+ _wizParams.processFlags |= kWPFClipBox;
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ break;
+ case 40: // HE99+
+ _wizParams.processFlags |= kWPFPaletteNum;
+ _wizParams.img.palette = pop();
+ break;
+ case 46:
+ _wizParams.processFlags |= kWPFScaled;
+ _wizParams.scale = pop();
+ break;
+ case 52:
+ _wizParams.processFlags |= kWPFShadow;
+ _wizParams.img.shadow = pop();
+ break;
+ case 85: // HE99+
+ _wizParams.processFlags |= 0x1000 | 0x100 | 0x2;
+ _wizParams.processMode = 7;
+ _wizParams.field_168 = pop();
+ _wizParams.field_164 = pop();
+ _wizParams.compType = pop();
+ break;
+ case 87: // HE99+
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 9;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.bottom = pop();
+ _wizParams.box2.right = pop();
+ _wizParams.box2.top = pop();
+ _wizParams.box2.left = pop();
+ break;
+ case 88: // HE99+
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 10;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.bottom = pop();
+ _wizParams.box2.right = pop();
+ _wizParams.box2.top = pop();
+ _wizParams.box2.left = pop();
+ break;
+ case 89: // HE99+
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 11;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.top = _wizParams.box2.bottom = pop();
+ _wizParams.box2.left = _wizParams.box2.right = pop();
+ break;
+ case 90: // HE99+
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 12;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.top = _wizParams.box2.bottom = pop();
+ _wizParams.box2.left = _wizParams.box2.right = pop();
+ break;
+ case 91: // HE99+
+ _wizParams.processFlags |= kWPFDstResNum;
+ _wizParams.dstResNum = pop();
+ break;
+ case 93: // HE99+
+ _wizParams.processFlags |= 0x100000;
+ _wizParams.field_180 = pop();
+ _wizParams.field_184 = pop();
+ break;
+ case 95: // HE99+
+ _wizParams.processMode = 13;
+ break;
+ case 96: // HE99+
+ _wizParams.field_239D = pop();
+ _wizParams.field_2399 = pop();
+ _wizParams.field_23A5 = pop();
+ _wizParams.field_23A1 = pop();
+ copyScriptString(_wizParams.string2, sizeof(_wizParams.string2));
+ _wizParams.processMode = 15;
+ break;
+ case 97: // HE99+
+ _wizParams.processMode = 16;
+ _wizParams.field_23AD = pop();
+ _wizParams.field_23A9 = pop();
+ copyScriptString(_wizParams.string1, sizeof(_wizParams.string1));
+ break;
+ case 143: // HE99+
+ _wizParams.processMode = 17;
+ _wizParams.field_23CD = pop();
+ _wizParams.field_23C9 = pop();
+ _wizParams.field_23C5 = pop();
+ _wizParams.field_23C1 = pop();
+ _wizParams.field_23BD = pop();
+ _wizParams.field_23B9 = pop();
+ _wizParams.field_23B5 = pop();
+ _wizParams.field_23B1 = pop();
+ break;
+ case 150: // HE99+
+ _wizParams.processMode = 14;
+ break;
+ case 171: // HE99+
+ _wizParams.processMode = 8;
+ break;
+ case 200:
+ _wizParams.processFlags |= kWPFNewFlags | kWPFSetPos | 2;
+ _wizParams.img.flags |= kWIFIsPolygon;
+ _wizParams.field_164 = _wizParams.img.y1 = _wizParams.img.x1 = pop();
+ break;
+ case 209:
+ if (_wizParams.img.resNum)
+ _wiz->processWizImage(&_wizParams);
+ break;
+ default:
+ error("o90_wizImageOps: unhandled case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getDistanceBetweenPoints() {
+ int x1, y1, z1, x2, y2, z2, dx, dy, dz, d;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 23: // HE100
+ case 28:
+ y2 = pop();
+ x2 = pop();
+ y1 = pop();
+ x1 = pop();
+ dx = x2 - x1;
+ dy = y2 - y1;
+ d = dx * dx + dy * dy;
+ if (d < 2) {
+ push(d);
+ } else {
+ push((int)sqrt((double)(d + 1)));
+ }
+ break;
+ case 24: // HE100
+ case 29:
+ z2 = pop();
+ y2 = pop();
+ x2 = pop();
+ z1 = pop();
+ y1 = pop();
+ x1 = pop();
+ dx = x2 - x1;
+ dy = y2 - y1;
+ dz = z2 - z1;
+ d = dx * dx + dy * dy + dz * dz;
+ if (d < 2) {
+ push(d);
+ } else {
+ push((int)sqrt((double)(d + 1)));
+ }
+ break;
+ default:
+ error("o90_getDistanceBetweenPoints: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getSpriteInfo() {
+ int args[16];
+ int spriteId, flags, groupId, type;
+ int32 x, y;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 30;
+
+ switch (subOp) {
+ case 0:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpritePosition(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 1:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpritePosition(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 2:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteImageDim(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 3:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteImageDim(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 4:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteDist(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 5:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteDist(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 6:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImageStateCount(spriteId));
+ else
+ push(0);
+ break;
+ case 7:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteGroup(spriteId));
+ else
+ push(0);
+ break;
+ case 8:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteDisplayX(spriteId));
+ else
+ push(0);
+ break;
+ case 9:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteDisplayY(spriteId));
+ else
+ push(0);
+ break;
+ case 12:
+ flags = pop();
+ spriteId = pop();
+ if (spriteId) {
+ switch(flags) {
+ case 0:
+ push(_sprite->getSpriteFlagXFlipped(spriteId));
+ break;
+ case 1:
+ push(_sprite->getSpriteFlagYFlipped(spriteId));
+ break;
+ case 2:
+ push(_sprite->getSpriteFlagActive(spriteId));
+ break;
+ case 3:
+ push(_sprite->getSpriteFlagDoubleBuffered(spriteId));
+ break;
+ case 4:
+ push(_sprite->getSpriteFlagRemapPalette(spriteId));
+ break;
+ default:
+ push(0);
+ }
+ } else {
+ push(0);
+ }
+ break;
+ case 13:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpritePriority(spriteId));
+ else
+ push(0);
+ break;
+ case 15:
+ if (_heversion == 99) {
+ flags = getStackList(args, ARRAYSIZE(args));
+ type = pop();
+ groupId = pop();
+ y = pop();
+ x = pop();
+ push(_sprite->findSpriteWithClassOf(x, y, groupId, type, flags, args));
+ } else if (_heversion == 98) {
+ type = pop();
+ groupId = pop();
+ y = pop();
+ x = pop();
+ push(_sprite->findSpriteWithClassOf(x, y, groupId, type, 0, 0));
+ } else {
+ groupId = pop();
+ y = pop();
+ x = pop();
+ push(_sprite->findSpriteWithClassOf(x, y, groupId, 0, 0, 0));
+ }
+ break;
+ case 22:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImageState(spriteId));
+ else
+ push(0);
+ break;
+ case 32:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteSourceImage(spriteId));
+ else
+ push(0);
+ break;
+ case 33:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImage(spriteId));
+ else
+ push(0);
+ break;
+ case 38:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagEraseType(spriteId));
+ else
+ push(1);
+ break;
+ case 52:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagAutoAnim(spriteId));
+ else
+ push(0);
+ break;
+ case 56:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpritePalette(spriteId));
+ else
+ push(0);
+ break;
+ case 62:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteScale(spriteId));
+ else
+ push(0);
+ break;
+ case 67:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteAnimSpeed(spriteId));
+ else
+ push(1);
+ break;
+ case 68:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteShadow(spriteId));
+ else
+ push(0);
+ break;
+ case 94:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagUpdateType(spriteId));
+ else
+ push(0);
+ break;
+ case 95:
+ flags = getStackList(args, ARRAYSIZE(args));
+ spriteId = pop();
+ if (spriteId) {
+ push(_sprite->getSpriteClass(spriteId, flags, args));
+ } else {
+ push(0);
+ }
+ break;
+ case 109:
+ flags = pop();
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteGeneralProperty(spriteId, flags));
+ else
+ push(0);
+ break;
+ case 110:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteMaskImage(spriteId));
+ else
+ push(0);
+ break;
+ case 168:
+ pop();
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteUserValue(spriteId));
+ else
+ push(0);
+ break;
+ default:
+ error("o90_getSpriteInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_setSpriteInfo() {
+ int args[16];
+ int spriteId;
+ int32 tmp[2];
+ int n;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 34;
+
+ switch (subOp) {
+ case 0:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++) {
+ _sprite->getSpriteDist(spriteId, tmp[0], tmp[1]);
+ _sprite->setSpriteDist(spriteId, args[0], tmp[1]);
+ }
+ break;
+ case 1:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++) {
+ _sprite->getSpriteDist(spriteId, tmp[0], tmp[1]);
+ _sprite->setSpriteDist(spriteId, tmp[0], args[0]);
+ }
+ break;
+ case 3:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteGroup(spriteId, args[0]);
+ break;
+ case 8:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ switch(args[1]) {
+ case 0:
+ _sprite->setSpriteFlagXFlipped(spriteId, args[0]);
+ break;
+ case 1:
+ _sprite->setSpriteFlagYFlipped(spriteId, args[0]);
+ break;
+ case 2:
+ _sprite->setSpriteFlagActive(spriteId, args[0]);
+ break;
+ case 3:
+ _sprite->setSpriteFlagDoubleBuffered(spriteId, args[0]);
+ break;
+ case 4:
+ _sprite->setSpriteFlagRemapPalette(spriteId, args[0]);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 9:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePriority(spriteId, args[0]);
+ break;
+ case 10:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->moveSprite(spriteId, args[0], args[1]);
+ break;
+ case 18:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteImageState(spriteId, args[0]);
+ break;
+ case 19:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteAngle(spriteId, args[0]);
+ break;
+ case 23:
+ if (_features & GF_HE_985 || _heversion >= 99) {
+ _curMaxSpriteId = pop();
+ _curSpriteId = pop();
+
+ if (_curSpriteId > _curMaxSpriteId)
+ SWAP(_curSpriteId, _curMaxSpriteId);
+ } else {
+ _curSpriteId = pop();
+ _curMaxSpriteId = _curSpriteId; // to make all functions happy
+ }
+ break;
+ case 28: // HE99+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteSourceImage(spriteId, args[0]);
+ break;
+ case 29:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteImage(spriteId, args[0]);
+ break;
+ case 31:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePosition(spriteId, args[0], args[1]);
+ break;
+ case 34:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagEraseType(spriteId, args[0]);
+ break;
+ case 43:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteDist(spriteId, args[0], args[1]);
+ break;
+ case 48:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagAutoAnim(spriteId, args[0]);
+ break;
+ case 52: // HE 98+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePalette(spriteId, args[0]);
+ break;
+ case 58: // HE 99+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteScale(spriteId, args[0]);
+ break;
+ case 63: // HE 98+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteAnimSpeed(spriteId, args[0]);
+ break;
+ case 64:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteShadow(spriteId, args[0]);
+ break;
+ case 90:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagUpdateType(spriteId, args[0]);
+ break;
+ case 91:
+ n = getStackList(args, ARRAYSIZE(args));
+ if (_curSpriteId != 0 && _curMaxSpriteId != 0 && n != 0) {
+ int *p = &args[n - 1];
+ do {
+ int code = *p;
+ if (code == 0) {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteResetClass(i);
+ }
+ } else if (code & 0x80) {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteSetClass(i, code & 0x7F, 1);
+ }
+ } else {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteSetClass(i, code & 0x7F, 0);
+ }
+ }
+ --p;
+ } while (--n);
+ }
+ break;
+ case 105: // HE 99+
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteGeneralProperty(spriteId, args[0], args[1]);
+ break;
+ case 106: // HE 99+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteMaskImage(spriteId, args[0]);
+ break;
+ case 124:
+ _sprite->resetTables(true);
+ break;
+ case 164:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteUserValue(spriteId, args[0], args[1]);
+ break;
+ case 183:
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->resetSprite(spriteId);
+ break;
+ default:
+ error("o90_setSpriteInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getSpriteGroupInfo() {
+ int32 tx, ty;
+ int spriteGroupId, type;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 8: // HE 99+
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(getGroupSpriteArray(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 30:
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ _sprite->getGroupPosition(spriteGroupId, tx, ty);
+ push(tx);
+ } else {
+ push(0);
+ }
+ break;
+ case 31:
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ _sprite->getGroupPosition(spriteGroupId, tx, ty);
+ push(ty);
+ } else {
+ push(0);
+ }
+ break;
+ case 42: // HE 99+
+ type = pop();
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ switch(type) {
+ case 0:
+ push(_sprite->getGroupXMul(spriteGroupId));
+ break;
+ case 1:
+ push(_sprite->getGroupXDiv(spriteGroupId));
+ break;
+ case 2:
+ push(_sprite->getGroupYMul(spriteGroupId));
+ break;
+ case 3:
+ push(_sprite->getGroupYDiv(spriteGroupId));
+ break;
+ default:
+ push(0);
+ }
+ } else {
+ push(0);
+ }
+ break;
+ case 43:
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(_sprite->getGroupPriority(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 63: // HE 99+
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(_sprite->getGroupDstResNum(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 139: // HE 99+
+ // dummy case
+ pop();
+ pop();
+ push(0);
+ break;
+ default:
+ error("o90_getSpriteGroupInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_setSpriteGroupInfo() {
+ int type, value1, value2, value3, value4;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 37;
+
+ switch (subOp) {
+ case 0:
+ type = pop() - 1;
+ switch (type) {
+ case 0:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->moveGroupMembers(_curSpriteGroupId, value1, value2);
+ break;
+ case 1:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersPriority(_curSpriteGroupId, value1);
+ break;
+ case 2:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersGroup(_curSpriteGroupId, value1);
+ break;
+ case 3:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersUpdateType(_curSpriteGroupId, value1);
+ break;
+ case 4:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersResetSprite(_curSpriteGroupId);
+ break;
+ case 5:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersAnimationSpeed(_curSpriteGroupId, value1);
+ break;
+ case 6:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersAutoAnimFlag(_curSpriteGroupId, value1);
+ break;
+ case 7:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersShadow(_curSpriteGroupId, value1);
+ break;
+ default:
+ error("o90_setSpriteGroupInfo subOp 0: Unknown case %d", subOp);
+ }
+ break;
+ case 5:
+ type = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ switch (type) {
+ case 0:
+ _sprite->setGroupXMul(_curSpriteGroupId, value1);
+ break;
+ case 1:
+ _sprite->setGroupXDiv(_curSpriteGroupId, value1);
+ break;
+ case 2:
+ _sprite->setGroupYMul(_curSpriteGroupId, value1);
+ break;
+ case 3:
+ _sprite->setGroupYDiv(_curSpriteGroupId, value1);
+ break;
+ default:
+ error("o90_setSpriteGroupInfo subOp 5: Unknown case %d", subOp);
+ }
+ break;
+ case 6:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupPriority(_curSpriteGroupId, value1);
+ break;
+ case 7:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->moveGroup(_curSpriteGroupId, value1, value2);
+ break;
+ case 20:
+ _curSpriteGroupId = pop();
+ break;
+ case 26:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupImage(_curSpriteGroupId, value1);
+ break;
+ case 28:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupPosition(_curSpriteGroupId, value1, value2);
+ break;
+ case 30:
+ value4 = pop();
+ value3 = pop();
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupBounds(_curSpriteGroupId, value1, value2, value3, value4);
+ break;
+ case 56:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->resetGroupBounds(_curSpriteGroupId);
+ break;
+ case 180:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->resetGroup(_curSpriteGroupId);
+ break;
+ default:
+ error("o90_setSpriteGroupInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getWizData() {
+ byte filename[4096];
+ int state, resId;
+ int32 w, h;
+ int32 x, y;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 30;
+
+ switch (subOp) {
+ case 0:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageSpot(resId, state, x, y);
+ push(x);
+ break;
+ case 1:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageSpot(resId, state, x, y);
+ push(y);
+ break;
+ case 2:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageDim(resId, state, w, h);
+ push(w);
+ break;
+ case 3:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageDim(resId, state, w, h);
+ push(h);
+ break;
+ case 6:
+ resId = pop();
+ push(_wiz->getWizImageStates(resId));
+ break;
+ case 15:
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->isWizPixelNonTransparent(resId, state, x, y, 0));
+ break;
+ case 36:
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->getWizPixelColor(resId, state, x, y, 0));
+ break;
+ case 100:
+ h = pop();
+ w = pop();
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ if (x == -1 && y == -1 && w == -1 && h == -1) {
+ _wiz->getWizImageDim(resId, state, w, h);
+ x = 0;
+ y = 0;
+ }
+ push(computeWizHistogram(resId, state, x, y, w, h));
+ break;
+ case 109:
+ pop();
+ pop();
+ push(0);
+ break;
+ case 111:
+ pop();
+ copyScriptString(filename, sizeof(filename));
+ pop();
+ push(0);
+ debug(0, "o90_getWizData() case 111 unhandled");
+ break;
+ default:
+ error("o90_getWizData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_floodFill() {
+ byte subOp = fetchScriptByte();
+ subOp -= 54;
+
+ switch (subOp) {
+ case 0:
+ pop();
+ break;
+ case 3:
+ memset(&_floodFillParams, 0, sizeof(_floodFillParams));
+ _floodFillParams.box.left = 0;
+ _floodFillParams.box.top = 0;
+ _floodFillParams.box.right = 639;
+ _floodFillParams.box.bottom = 479;
+ break;
+ case 11:
+ _floodFillParams.y = pop();
+ _floodFillParams.x = pop();
+ break;
+ case 12:
+ _floodFillParams.flags = pop();
+ break;
+ case 13:
+ _floodFillParams.box.bottom = pop();
+ _floodFillParams.box.right = pop();
+ _floodFillParams.box.top = pop();
+ _floodFillParams.box.left = pop();
+ break;
+ case 201:
+ floodFill(&_floodFillParams, this);
+ break;
+ default:
+ error("o90_floodFill: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_shl() {
+ int a = pop();
+ push(pop() << a);
+}
+
+void ScummEngine_v90he::o90_shr() {
+ int a = pop();
+ push(pop() >> a);
+}
+
+void ScummEngine_v90he::o90_xor() {
+ int a = pop();
+ push(pop() ^ a);
+}
+
+void ScummEngine_v90he::o90_mod() {
+ int a = pop();
+ if (a == 0)
+ error("modulus by zero");
+ push(pop() % a);
+}
+
+void ScummEngine_v90he::o90_findAllObjectsWithClassOf() {
+ int args[16];
+ int cond, num, cls, tmp;
+ bool b;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ int room = pop();
+ int numObjs = 0;
+
+ if (room != _currentRoom)
+ error("o90_findAllObjectsWithClassOf: current room is not %d", room);
+
+ writeVar(0, 0);
+ defineArray(0, kDwordArray, 0, 0, 0, _numLocalObjects);
+ for (int i = 1; i < _numLocalObjects; i++) {
+ cond = 1;
+ tmp = num;
+ while (--tmp >= 0) {
+ cls = args[tmp];
+ b = getClass(_objs[i].obj_nr, cls);
+ if ((cls & 0x80 && !b) || (!(cls & 0x80) && b))
+ cond = 0;
+ }
+
+ if (cond) {
+ numObjs++;
+ writeArray(0, 0, numObjs, _objs[i].obj_nr);
+ }
+ }
+
+ writeArray(0, 0, 0, numObjs);
+
+ push(readVar(0));
+}
+
+void ScummEngine_v90he::o90_getPolygonOverlap() {
+ int args1[32];
+ int args2[32];
+
+ int n1 = getStackList(args1, ARRAYSIZE(args1));
+ int n2 = getStackList(args2, ARRAYSIZE(args2));
+
+ int subOp = pop();
+
+ switch (subOp) {
+ case 1:
+ {
+ Common::Rect r(args1[0], args1[1], args1[2] + 1, args1[3] + 1);
+ Common::Point p(args2[0], args2[1]);
+ push(r.contains(p) ? 1 : 0);
+ }
+ break;
+ case 2:
+ {
+ int dx = args2[0] - args1[0];
+ int dy = args2[1] - args1[1];
+ int dist = dx * dx + dy * dy;
+ if (dist >= 2) {
+ dist = (int)sqrt((double)(dist + 1));
+ }
+ if (_heversion >= 98) {
+ push((dist <= args1[2]) ? 1 : 0);
+ } else {
+ push((dist > args1[2]) ? 1 : 0);
+ }
+ }
+ break;
+ case 3:
+ {
+ Common::Rect r1(args1[0], args1[1], args1[2] + 1, args1[3] + 1);
+ Common::Rect r2(args2[0], args2[1], args2[2] + 1, args2[3] + 1);
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ case 4:
+ {
+ int dx = args2[0] - args1[0];
+ int dy = args2[1] - args1[1];
+ int dist = dx * dx + dy * dy;
+ if (dist >= 2) {
+ dist = (int)sqrt((double)(dist + 1));
+ }
+ push((dist < args1[2] && dist < args2[2]) ? 1 : 0);
+ }
+ break;
+ case 5:
+ {
+ assert((n1 & 1) == 0);
+ n1 /= 2;
+ if (n1 == 0) {
+ push(0);
+ } else {
+ WizPolygon wp;
+ memset(&wp, 0, sizeof(wp));
+ wp.numVerts = n1;
+ assert(n1 < ARRAYSIZE(wp.vert));
+ for (int i = 0; i < n1; ++i) {
+ wp.vert[i].x = args1[i * 2 + 0];
+ wp.vert[i].y = args1[i * 2 + 1];
+ }
+ push(_wiz->polygonContains(wp, args2[0], args2[1]) ? 1 : 0);
+ }
+ }
+ break;
+ // HE 98+
+ case 6:
+ {
+ Common::Rect r1, r2;
+ _sprite->getSpriteBounds(args2[0], false, r2);
+ _sprite->getSpriteBounds(args1[0], false, r1);
+ if (r2.isValidRect() == false) {
+ push(0);
+ break;
+ }
+
+ if (n2 == 3) {
+ r2.left += args2[1];
+ r2.right += args2[1];
+ r2.top += args2[2];
+ r2.bottom += args2[2];
+ }
+ if (n1 == 3) {
+ r1.left += args1[1];
+ r1.right += args1[1];
+ r1.top += args1[2];
+ r1.bottom += args1[2];
+ }
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ case 7:
+ {
+ Common::Rect r2;
+ _sprite->getSpriteBounds(args2[0], false, r2);
+ Common::Rect r1(args1[0], args1[1], args1[2], args1[3]);
+ if (r2.isValidRect() == false) {
+ push(0);
+ break;
+ }
+
+ if (n2 == 3) {
+ r2.left += args2[1];
+ r2.right += args2[1];
+ r2.top += args2[2];
+ r2.bottom += args2[2];
+ }
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ case 8:
+ case 10: // TODO: Draw sprites to buffer and compare.
+ {
+ Common::Rect r1, r2;
+ _sprite->getSpriteBounds(args2[0], true, r2);
+ _sprite->getSpriteBounds(args1[0], true, r1);
+ if (r2.isValidRect() == false) {
+ push(0);
+ break;
+ }
+
+ if (n2 == 3) {
+ r2.left += args2[1];
+ r2.right += args2[1];
+ r2.top += args2[2];
+ r2.bottom += args2[2];
+ }
+ if (n1 == 3) {
+ r1.left += args1[1];
+ r1.right += args1[1];
+ r1.top += args1[2];
+ r1.bottom += args1[2];
+ }
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ case 9:
+ {
+ Common::Rect r2;
+ _sprite->getSpriteBounds(args2[0], true, r2);
+ Common::Rect r1(args1[0], args1[1], args1[2], args1[3]);
+ if (r2.isValidRect() == false) {
+ push(0);
+ break;
+ }
+
+ if (n2 == 3) {
+ r2.left += args2[1];
+ r2.right += args2[1];
+ r2.top += args2[2];
+ r2.bottom += args2[2];
+ }
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ default:
+ error("o90_getPolygonOverlap: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_cond() {
+ int a = pop();
+ int b = pop();
+ int c = pop();
+
+ if (!c)
+ b = a;
+ push(b);
+}
+
+void ScummEngine_v90he::o90_dim2dim2Array() {
+ int data, dim1start, dim1end, dim2start, dim2end;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 2: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 3: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 4: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 5: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 6:
+ data = kDwordArray;
+ break;
+ case 7: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ default:
+ error("o90_dim2dim2Array: default case %d", subOp);
+ }
+
+ if (pop() == 2) {
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ } else {
+ dim2end = pop();
+ dim2start = pop();
+ dim1end = pop();
+ dim1start = pop();
+ }
+
+ defineArray(fetchScriptWord(), data, dim2start, dim2end, dim1start, dim1end);
+}
+
+void ScummEngine_v90he::o90_redim2dimArray() {
+ int a, b, c, d;
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 4:
+ redimArray(fetchScriptWord(), a, b, c, d, kByteArray);
+ break;
+ case 5:
+ redimArray(fetchScriptWord(), a, b, c, d, kIntArray);
+ break;
+ case 6:
+ redimArray(fetchScriptWord(), a, b, c, d, kDwordArray);
+ break;
+ default:
+ error("o90_redim2dimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getLinesIntersectionPoint() {
+ int var_ix = fetchScriptWord();
+ int var_iy = fetchScriptWord();
+ int line2_y2 = pop();
+ int line2_x2 = pop();
+ int line2_y1 = pop();
+ int line2_x1 = pop();
+ int line1_y2 = pop();
+ int line1_x2 = pop();
+ int line1_y1 = pop();
+ int line1_x1 = pop();
+
+ int result = 0;
+ int ix = 0;
+ int iy = 0;
+
+ bool isLine1Point = (line1_x1 == line1_x2 && line1_y1 == line1_y2);
+ bool isLine2Point = (line2_x1 == line2_x2 && line2_y1 == line2_y2);
+
+ if (isLine1Point) {
+ if (isLine2Point) {
+ if (line1_x1 == line2_x1 && line1_y1 == line2_y2) {
+ ix = line1_x1;
+ iy = line2_x1;
+ result = 1;
+ }
+ } else {
+ // 1 point and 1 line
+ int dx2 = line2_x2 - line2_x1;
+ if (dx2 != 0) {
+ int dy2 = line2_y2 - line2_y1;
+ float y = (float)dy2 / dx2 * (line1_x1 - line2_x1) + line2_y1 + .5f;
+ if (line1_y1 == (int)y) {
+ ix = line1_x1;
+ iy = line1_y1;
+ result = 1;
+ }
+ } else {
+ // vertical line
+ if (line1_x1 == line2_x1) {
+ if (line2_y1 > line2_y2) {
+ if (line1_y1 >= line2_y2 && line1_y1 <= line2_y1) {
+ ix = line1_x1;
+ iy = line1_y1;
+ result = 1;
+ }
+ } else {
+ if (line1_y1 >= line2_y1 && line1_y1 <= line2_y2) {
+ ix = line1_x1;
+ iy = line1_y1;
+ result = 1;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (isLine2Point) {
+ // 1 point and 1 line
+ int dx1 = line1_x2 - line1_x1;
+ if (dx1 != 0) {
+ int dy1 = line1_y2 - line1_y1;
+ float y = (float)dy1 / dx1 * (line2_x1 - line1_x1) + line1_y1 + .5f;
+ if (line2_y1 == (int)y) {
+ ix = line2_x1;
+ iy = line2_y1;
+ result = 1;
+ }
+ } else {
+ // vertical line
+ if (line2_x1 == line1_x1) {
+ if (line1_y1 > line1_y2) {
+ if (line2_y1 >= line1_y2 && line2_y1 <= line1_y1) {
+ ix = line2_x1;
+ iy = line2_y1;
+ result = 1;
+ }
+ } else {
+ if (line2_y1 >= line1_y1 && line2_y1 <= line1_y2) {
+ ix = line2_x2;
+ iy = line2_y1;
+ result = 1;
+ }
+ }
+ }
+ }
+ } else {
+ // 2 lines
+ int dy1 = line1_y2 - line1_y1;
+ int dx1 = line1_x2 - line1_x1;
+ int dy2 = line2_y2 - line2_y1;
+ int dx2 = line2_x2 - line2_x1;
+ int det = dx1 * dy2 - dx2 * dy1;
+ int cross_p1 = dx1 * (line1_y1 - line2_y1) - dy1 * (line1_x1 - line2_x1);
+ int cross_p2 = dx2 * (line1_y1 - line2_y1) - dy2 * (line1_x1 - line2_x1);
+ if (det == 0) {
+ // parallel lines
+ if (cross_p2 == 0) {
+ ix = ABS(line2_x2 + line2_x1) / 2;
+ iy = ABS(line2_y2 + line2_y1) / 2;
+ result = 2;
+ }
+ } else {
+ float rcp1 = (float)cross_p1 / det;
+ float rcp2 = (float)cross_p2 / det;
+ if (rcp1 >= 0 && rcp1 <= 1 && rcp2 >= 0 && rcp2 <= 1) {
+ ix = (int)(dx1 * rcp2 + line1_x1 + .5f);
+ iy = (int)(dy1 * rcp2 + line1_y1 + .5f);
+ result = 1;
+ }
+ }
+ }
+ }
+
+ writeVar(var_ix, ix);
+ writeVar(var_iy, iy);
+ push(result);
+}
+
+void ScummEngine_v90he::getArrayDim(int array, int *dim2start, int *dim2end, int *dim1start, int *dim1end) {
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+ assert(ah);
+ if (dim2start && *dim2start == -1) {
+ *dim2start = ah->dim2start;
+ }
+ if (dim2end && *dim2end == -1) {
+ *dim2end = ah->dim2end;
+ }
+ if (dim1start && *dim1start == -1) {
+ *dim1start = ah->dim1start;
+ }
+ if (dim1end && *dim1end == -1) {
+ *dim1end = ah->dim1end;
+ }
+}
+
+static int sortArrayOffset;
+
+static int compareByteArray(const void *a, const void *b) {
+ int va = *((const uint8 *)a + sortArrayOffset);
+ int vb = *((const uint8 *)a + sortArrayOffset);
+ return va - vb;
+}
+
+static int compareByteArrayReverse(const void *a, const void *b) {
+ int va = *((const uint8 *)a + sortArrayOffset);
+ int vb = *((const uint8 *)a + sortArrayOffset);
+ return vb - va;
+}
+
+static int compareIntArray(const void *a, const void *b) {
+ int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
+ int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
+ return va - vb;
+}
+
+static int compareIntArrayReverse(const void *a, const void *b) {
+ int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
+ int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
+ return vb - va;
+}
+
+static int compareDwordArray(const void *a, const void *b) {
+ int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
+ int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
+ return va - vb;
+}
+
+static int compareDwordArrayReverse(const void *a, const void *b) {
+ int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
+ int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
+ return vb - va;
+}
+
+void ScummEngine_v90he::sortArray(int array, int dim2start, int dim2end, int dim1start, int dim1end, int sortOrder) {
+ debug(9, "sortArray(%d, [%d,%d,%d,%d], %d)", array, dim2start, dim2end, dim1start, dim1end, sortOrder);
+
+ assert(dim1start == dim1end);
+ checkArrayLimits(array, dim2start, dim2end, dim1start, dim1end);
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+ assert(ah);
+
+ const int num = dim2end - dim2start + 1;
+ const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1;
+ const int offset = pitch * (dim2start - FROM_LE_32(ah->dim2start));
+ sortArrayOffset = dim1start - FROM_LE_32(ah->dim1start);
+
+ switch (FROM_LE_32(ah->type)) {
+ case kByteArray:
+ case kStringArray:
+ if (sortOrder <= 0) {
+ qsort(ah->data + offset, num, pitch, compareByteArray);
+ } else {
+ qsort(ah->data + offset, num, pitch, compareByteArrayReverse);
+ }
+ break;
+ case kIntArray:
+ if (sortOrder <= 0) {
+ qsort(ah->data + offset * 2, num, pitch * 2, compareIntArray);
+ } else {
+ qsort(ah->data + offset * 2, num, pitch * 2, compareIntArrayReverse);
+ }
+ break;
+ case kDwordArray:
+ if (sortOrder <= 0) {
+ qsort(ah->data + offset * 4, num, pitch * 4, compareDwordArray);
+ } else {
+ qsort(ah->data + offset * 4, num, pitch * 4, compareDwordArrayReverse);
+ }
+ break;
+ default:
+ error("Invalid array type", FROM_LE_32(ah->type));
+ }
+}
+
+void ScummEngine_v90he::o90_sortArray() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 129:
+ case 134: // HE100
+ {
+ int array = fetchScriptWord();
+ int sortOrder = pop();
+ int dim1end = pop();
+ int dim1start = pop();
+ int dim2end = pop();
+ int dim2start = pop();
+ getArrayDim(array, &dim2start, &dim2end, &dim1start, &dim1end);
+ sortArray(array, dim2start, dim2end, dim1start, dim1end, sortOrder);
+ }
+ break;
+ default:
+ error("o90_sortArray: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getObjectData() {
+ byte subOp = fetchScriptByte();
+ subOp -= 32;
+
+ switch (subOp) {
+ case 0:
+ if (_heObjectNum == -1)
+ push(0);
+ else
+ push(_objs[_heObjectNum].width);
+ break;
+ case 1:
+ if (_heObjectNum == -1)
+ push(0);
+ else
+ push(_objs[_heObjectNum].height);
+ break;
+ case 4:
+ push(getObjectImageCount(_heObject));
+ break;
+ case 6:
+ if (_heObjectNum == -1)
+ push(0);
+ else
+ push(_objs[_heObjectNum].x_pos);
+ break;
+ case 7:
+ if (_heObjectNum == -1)
+ push(0);
+ else
+ push(_objs[_heObjectNum].y_pos);
+ break;
+ case 20:
+ push(getState(_heObject));
+ break;
+ case 25:
+ _heObject = pop();
+ _heObjectNum = getObjectIndex(_heObject);
+ break;
+ case 107:
+ // Dummy case
+ pop();
+ push(0);
+ break;
+ default:
+ error("o90_getObjectData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getPaletteData() {
+ int b, c, d, e;
+ int palSlot, color;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 45;
+
+ switch (subOp) {
+ case 0:
+ e = pop();
+ d = pop();
+ palSlot = pop();
+ pop();
+ c = pop();
+ b = pop();
+ push(getHEPaletteSimilarColor(palSlot, b, c, d, e));
+ break;
+ case 7:
+ c = pop();
+ b = pop();
+ palSlot = pop();
+ push(getHEPaletteColorComponent(palSlot, b, c));
+ break;
+ case 21:
+ color = pop();
+ palSlot = pop();
+ push(getHEPaletteColor(palSlot, color));
+ break;
+ case 87:
+ c = pop();
+ b = pop();
+ push(getHEPaletteColorComponent(1, b, c));
+ break;
+ case 172:
+ pop();
+ c = pop();
+ c = MAX(0, c);
+ c = MIN(c, 255);
+ b = pop();
+ b = MAX(0, b);
+ b = MIN(b, 255);
+ push(getHEPaletteSimilarColor(1, b, c, 10, 245));
+ break;
+ default:
+ error("o90_getPaletteData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_paletteOps() {
+ int a, b, c, d, e;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 57;
+
+ switch (subOp) {
+ case 0:
+ _hePaletteNum = pop();
+ break;
+ case 6:
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromImage(_hePaletteNum, a, b);
+ }
+ break;
+ case 9:
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ for (; a <= b; ++a) {
+ setHEPaletteColor(_hePaletteNum, a, c, d, e);
+ }
+ }
+ break;
+ case 13:
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ for (; a <= b; ++a) {
+ copyHEPaletteColor(_hePaletteNum, a, c);
+ }
+ }
+ break;
+ case 19: //HE99+
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromCostume(_hePaletteNum, a);
+ }
+ break;
+ case 29:
+ a = pop();
+ if (_hePaletteNum != 0) {
+ copyHEPalette(_hePaletteNum, a);
+ }
+ break;
+ case 118:
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromRoom(_hePaletteNum, a, b);
+ }
+ break;
+ case 160:
+ if (_hePaletteNum != 0) {
+ restoreHEPalette(_hePaletteNum);
+ }
+ break;
+ case 198:
+ _hePaletteNum = 0;
+ break;
+ default:
+ error("o90_paletteOps: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_fontUnk() {
+ // Font related
+ byte string[80];
+ int a;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 60: // HE100
+ case 42:
+ a = pop();
+ if (a == 2) {
+ copyScriptString(string, sizeof(string));
+ push(-1);
+ } else if (a == 1) {
+ pop();
+ writeVar(0, 0);
+ defineArray(0, kStringArray, 0, 0, 0, 0);
+ writeArray(0, 0, 0, 0);
+ push(readVar(0));
+ }
+ break;
+ case 0: // HE100
+ case 57:
+ push(1);
+ break;
+ default:
+ error("o90_fontUnk: Unknown case %d", subOp);
+ }
+
+ debug(1, "o90_fontUnk stub (%d)", subOp);
+}
+
+void ScummEngine_v90he::o90_getActorAnimProgress() {
+ Actor *a = derefActor(pop(), "o90_getActorAnimProgress");
+ push(a->getAnimProgress());
+}
+
+void ScummEngine_v90he::o90_kernelGetFunctions() {
+ int args[29];
+ int num, tmp;
+ Actor *a;
+
+ num = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1001:
+ {
+ double b = args[1] * PI / 180.;
+ push((int)(sin(b) * 100000));
+ }
+ break;
+ case 1002:
+ {
+ double b = args[1] * PI / 180.;
+ push((int)(cos(b) * 100000));
+ }
+ break;
+ case 1969:
+ a = derefActor(args[1], "o90_kernelGetFunctions: 1969");
+ tmp = a->_heCondMask;
+ tmp &= 0x7FFF0000;
+ push(tmp);
+ break;
+ case 2001:
+ push(_logicHE->dispatch(args[1], num - 2, (int32 *)&args[2]));
+ break;
+ default:
+ error("o90_kernelGetFunctions: default case %d", args[0]);
+ }
+}
+
+void ScummEngine_v90he::o90_kernelSetFunctions() {
+ int args[29];
+ int num, tmp;
+ Actor *a;
+
+ num = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 20:
+ a = derefActor(args[1], "o90_kernelSetFunctions: 20");
+ queueAuxBlock(a);
+ break;
+ case 21:
+ _skipDrawObject = 1;
+ break;
+ case 22:
+ _skipDrawObject = 0;
+ break;
+ case 23:
+ _charset->clearCharsetMask();
+ _fullRedraw = true;
+ break;
+ case 24:
+ _skipProcessActors = 1;
+ redrawAllActors();
+ break;
+ case 25:
+ _skipProcessActors = 0;
+ redrawAllActors();
+ break;
+ case 27:
+ // Used in readdemo
+ break;
+ case 42:
+ _wiz->_rectOverrideEnabled = true;
+ _wiz->_rectOverride.left = args[1];
+ _wiz->_rectOverride.top = args[2];
+ _wiz->_rectOverride.right = args[3];
+ _wiz->_rectOverride.bottom = args[4];
+ break;
+ case 43:
+ _wiz->_rectOverrideEnabled = false;
+ break;
+ case 714:
+ debug(5, "o90_kernelSetFunctions: case 714: type %d resId %d unk1 %d", args[1], args[2], args[3]);
+ break;
+ case 1492:
+ // Remote start script function
+ break;
+ case 1969:
+ a = derefActor(args[1], "o90_kernelSetFunctions: 1969");
+ tmp = a->_heCondMask;
+ tmp ^= args[2];
+ tmp &= 0x7FFF0000;
+ a->_heCondMask ^= tmp;
+ break;
+ case 2001:
+ _logicHE->dispatch(args[1], num - 2, (int32 *)&args[2]);
+ break;
+ default:
+ error("o90_kernelSetFunctions: default case %d (param count %d)", args[0], num);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
new file mode 100644
index 0000000000..f442f38ba3
--- /dev/null
+++ b/engines/scumm/scumm-md5.h
@@ -0,0 +1,467 @@
+/*
+ This file was generated by the md5table tool on Sat Feb 11 00:38:53 2006
+ DO NOT EDIT MANUALLY!
+ */
+
+struct MD5Table {
+ const char *md5;
+ const char *gameid;
+ Common::Language language;
+ Common::Platform platform;
+};
+
+static const MD5Table md5table[] = {
+ { "0305e850382b812fec6e5998ef88a966", "pajama", Common::NL_NLD, Common::kPlatformWindows },
+ { "035deab53b47bc43abc763560d0f8d4b", "atlantis", Common::EN_USA, Common::kPlatformPC },
+ { "037385a953789190298494d92b89b3d0", "catalog", Common::EN_USA, Common::kPlatformWindows },
+ { "0425954a9db5c340861672892c3e678d", "samnmax", Common::EN_USA, Common::kPlatformMacintosh },
+ { "04401d747f1a2c1c4b388daff71ed378", "ft", Common::DE_DEU, Common::kPlatformMacintosh },
+ { "04687cdf7f975a89d2474929f7b80946", "indy3", Common::EN_USA, Common::kPlatformFMTowns },
+ { "0557df19f046a84c2fdc63507c6616cb", "farm", Common::NL_NLD, Common::kPlatformWindows },
+ { "055ffe4f47753e47594ac67823220c54", "puttrace", Common::DE_DEU, Common::kPlatformUnknown },
+ { "06b187468113f9ae5a400b148a847fac", "atlantis", Common::EN_USA, Common::kPlatformMacintosh },
+ { "07433205acdca3bc553d0e731588b35f", "airport", Common::EN_USA, Common::kPlatformWindows },
+ { "07b810e37be7489263f7bc7627d4765d", "freddi4", Common::RU_RUS, Common::kPlatformWindows },
+ { "084ed0fa98a6d1e9368d67fe9cfbd417", "freddi", Common::EN_USA, Common::kPlatformWindows },
+ { "0855496dde35356b1a9691e22ba84cdc", "freddi", Common::EN_USA, Common::kPlatformWindows },
+ { "08656dd9698ddf1023ba9bf8a195e37b", "monkey", Common::EN_USA, Common::kPlatformPC },
+ { "08cc5c3eedaf72ebe12734eee94f7fa2", "balloon", Common::EN_USA, Common::kPlatformUnknown },
+ { "09820417db26687bb7fe0c83cc4c553b", "ft", Common::EN_USA, Common::kPlatformUnknown },
+ { "0a212fa35fa8421f31c1f3961272caf0", "monkey", Common::DE_DEU, Common::kPlatformAmiga },
+ { "0a41311d462b6639fc45297b9044bf16", "monkey", Common::ES_ESP, Common::kPlatformAtariST },
+ { "0ab19be9e2a3f6938226638b2a3744fe", "PuttTime", Common::EN_USA, Common::kPlatformUnknown },
+ { "0ac41e2e3d2174e5a042a6b565328dba", "puttrace", Common::EN_USA, Common::kPlatformUnknown },
+ { "0b3222aaa7efcf283eb621e0cefd26cc", "puttputt", Common::RU_RUS, Common::kPlatformPC },
+ { "0c45eb4baff0c12c3d9dfa889c8070ab", "pajama3", Common::DE_DEU, Common::kPlatformUnknown },
+ { "0cccfa5223099a60e76cfcca57a1a141", "freddi3", Common::NL_NLD, Common::kPlatformWindows },
+ { "0d1b69471605201ef2fa9cec1f5f02d2", "maniac", Common::ES_ESP, Common::kPlatformPC },
+ { "0e4c5d54a0ad4b26132e78b5ea76642a", "samnmax", Common::EN_USA, Common::kPlatformPC },
+ { "0e9b01430e31d9fcd94071d433bbc6bf", "loom", Common::FR_FRA, Common::kPlatformAtariST },
+ { "0f5935bd5e88ba6f09e558d64459746d", "thinker1", Common::EN_USA, Common::kPlatformWindows },
+ { "0f6f2e716ba896a44e5059bba1de7ca9", "samnmax", Common::IT_ITA, Common::kPlatformUnknown },
+ { "0f9c7a76657f0840b8f7ccb5bffeb9f4", "indy3", Common::FR_FRA, Common::kPlatformAtariST },
+ { "0fb73eddfcf584c02ba097984df131ba", "samnmax", Common::DE_DEU, Common::kPlatformUnknown },
+ { "1005456bfe351c1b679e1ff2dc2849e9", "puttzoo", Common::UNK_LANG, Common::kPlatformWindows },
+ { "114acdc2659a273c220f86ee9edb24c1", "maniac", Common::FR_FRA, Common::kPlatformPC },
+ { "11ddf1fde76e3156eb3a38da213f484e", "monkey2", Common::IT_ITA, Common::kPlatformAmiga },
+ { "11e6e244078ff09b0f3832e35420e0a7", "catalog", Common::EN_USA, Common::kPlatformWindows },
+ { "132bff65e6367c09cc69318ce1b59333", "monkey2", Common::EN_USA, Common::kPlatformAmiga },
+ { "145bd3373574feb668cc2eea2ec6cf86", "balloon", Common::RU_RUS, Common::kPlatformWindows },
+ { "14d48c95b43ddeb983254cf6c43851f1", "freddi4", Common::NL_NLD, Common::kPlatformWindows },
+ { "151071053a1d0021198216713939521d", "freddi2", Common::EN_USA, Common::kPlatformWindows },
+ { "15240c59d3681ed53f714f8d925cb2d6", "maniac", Common::ES_ESP, Common::kPlatformAtariST },
+ { "157367c3c21e0d03a0cba44361b4cf65", "indy3", Common::EN_USA, Common::kPlatformAtariST },
+ { "15e03ffbfeddb9c2aebc13dcb2a4a8f4", "monkey", Common::EN_USA, Common::kPlatformPC },
+ { "15f588e887e857e8c56fe6ade4956168", "atlantis", Common::ES_ESP, Common::kPlatformAmiga },
+ { "16542a7342a918bfe4ba512007d36c47", "FreddisFunShop", Common::EN_USA, Common::kPlatformUnknown },
+ { "166553538ff320c69edafeee29525419", "samnmax", Common::EN_USA, Common::kPlatformMacintosh },
+ { "16effd200aa6b8abe9c569c3e578814d", "freddi4", Common::NL_NLD, Common::kPlatformWindows },
+ { "179879b6e35c1ead0d93aab26db0951b", "fbear", Common::EN_USA, Common::kPlatformWindows },
+ { "17b5d5e6af4ae89d62631641d66d5a05", "indy3", Common::IT_ITA, Common::kPlatformPC },
+ { "17f7296f63c78642724f057fd8e736a7", "maniac", Common::EN_USA, Common::kPlatformNES },
+ { "17fa250eb72dae2dad511ba79c0b6b0a", "tentacle", Common::FR_FRA, Common::kPlatformPC },
+ { "182344899c2e2998fca0bebcd82aa81a", "atlantis", Common::EN_USA, Common::kPlatformPC },
+ { "183d7464902d40d00800e8ee1f04117c", "maniac", Common::DE_DEU, Common::kPlatformPC },
+ { "1875b90fade138c9253a8e967007031a", "indy3", Common::EN_USA, Common::kPlatformPC },
+ { "187d315f6b5168f68680dfe8c3d76a3e", "loom", Common::HB_ISR, Common::kPlatformPC },
+ { "1900e501a52fbf55bde6e4196f6d2aa6", "zak", Common::IT_ITA, Common::kPlatformPC },
+ { "19263586f749a560c1adf8b3393a9593", "socks", Common::RU_RUS, Common::kPlatformWindows },
+ { "19bf6938a94698296bcb0c99c31c91a7", "spyfox2", Common::EN_USA, Common::kPlatformWindows },
+ { "1a6e5ae2777a6a33f06ffc0226210934", "atlantis", Common::EN_USA, Common::kPlatformMacintosh },
+ { "1c792d28376d45e145cb916bca0400a2", "spyfox2", Common::NL_NLD, Common::kPlatformUnknown },
+ { "1c7e7db2cfab1ad62746ab680a634204", "maniac", Common::FR_FRA, Common::kPlatformNES },
+ { "1ca86e2cf9aaa2068738a1e5ba477e60", "zak", Common::JA_JPN, Common::kPlatformFMTowns },
+ { "1d05cd189e4908f79b57e78a4402f292", "monkey", Common::EN_USA, Common::kPlatformPC },
+ { "1dd3c11ea4439adfe681e4e405b624e1", "monkey", Common::FR_FRA, Common::kPlatformPC },
+ { "1dd7aa088e09f96d06818aa9a9deabe0", "indy3", Common::EN_USA, Common::kPlatformMacintosh },
+ { "1fbebd7b2b692df5297870447a80cfed", "atlantis", Common::DE_DEU, Common::kPlatformPC },
+ { "2012f854d83d9cc6f73b2b544cd8bbf8", "water", Common::RU_RUS, Common::kPlatformWindows },
+ { "20da6fce37805423966aaa8f3c2426aa", "atlantis", Common::FR_FRA, Common::kPlatformAmiga },
+ { "2108d83dcf09f8adb4bc524669c8cf51", "PuttTime", Common::EN_USA, Common::kPlatformUnknown },
+ { "21a6592322f92550f144f68a8a4e685e", "dig", Common::FR_FRA, Common::kPlatformMacintosh },
+ { "21abe302e1b1e2b66d6f5c12e241ebfd", "freddicove", Common::RU_RUS, Common::kPlatformWindows },
+ { "2232b0b9411575b1f9961713ebc9de61", "balloon", Common::ES_ESP, Common::kPlatformWindows },
+ { "225e18566e810c634bf7de63e7568e3e", "mustard", Common::EN_USA, Common::kPlatformUnknown },
+ { "22c9eb04455440131ffc157aeb8d40a8", "fbear", Common::EN_USA, Common::kPlatformWindows },
+ { "22d07d6c386c9c25aca5dac2a0c0d94b", "maniac", Common::SE_SWE, Common::kPlatformNES },
+ { "22f4ea88a09da12df9308ba30bcb7d0f", "loom", Common::EN_USA, Common::kPlatformPC },
+ { "257f8c14d8c584f7ddd601bcb00920c7", "maniac", Common::DE_DEU, Common::kPlatformNES },
+ { "2723fea3dae0cb47768c424b145ae0e7", "tentacle", Common::EN_USA, Common::kPlatformPC },
+ { "27b3a4224ad63d5b04627595c1c1a025", "zak", Common::IT_ITA, Common::kPlatformAmiga },
+ { "28d24a33448fab6795850bc9f159a4a2", "atlantis", Common::JA_JPN, Common::kPlatformFMTowns },
+ { "28ef68ee3ed76d7e2ee8ee13c15fbd5b", "loom", Common::EN_USA, Common::kPlatformPC },
+ { "2a208ffbcd0e83e86f4356e6f64aa6e1", "loom", Common::ES_ESP, Common::kPlatformPC },
+ { "2a41b53cf1a90b6e6f26c10cc6041084", "tentacle", Common::EN_USA, Common::kPlatformMacintosh },
+ { "2a446817ffcabfef8716e0c456ecaf81", "puttzoo", Common::DE_DEU, Common::kPlatformWindows },
+ { "2ccd8891ce4d3f1a334d21bff6a88ca2", "monkey", Common::EN_USA, Common::kPlatformMacintosh },
+ { "2d1e891fe52df707c30185e52c50cd92", "monkey", Common::EN_USA, Common::kPlatformPC },
+ { "2d388339d6050d8ccaa757b64633954e", "zak", Common::EN_USA, Common::kPlatformFMTowns },
+ { "2d4536a56e01da4b02eb021e7770afa2", "zak", Common::EN_USA, Common::kPlatformFMTowns },
+ { "2d9d46f23cb07bbc90b8ad464d3e4ff8", "atlantis", Common::EN_USA, Common::kPlatformMacintosh },
+ { "2e85f7aa054930c692a5b1bed1dfc295", "football", Common::EN_USA, Common::kPlatformUnknown },
+ { "2e8a1f76ea33bc5e04347646feee173d", "pajama3", Common::DE_DEU, Common::kPlatformUnknown },
+ { "2fe369ad70f52a8cf7ad6077ee64f81a", "loom", Common::DE_DEU, Common::kPlatformAmiga },
+ { "305d3dd57c96c65b017bc70c8c7cfb5e", "monkey", Common::DE_DEU, Common::kPlatformPC },
+ { "30ba1e825d4ad2b448143ae8df18482a", "pajama2", Common::NL_NLD, Common::kPlatformUnknown },
+ { "319a4dde52c7960b5aae8a1ec348d918", "monkey", Common::DE_DEU, Common::kPlatformAmiga },
+ { "31aa57f460a3d12429f0552a46a90b39", "puttputt", Common::EN_USA, Common::kPlatformPC },
+ { "31b8fda4c8c7413fa6b39997e776eba4", "loom", Common::JA_JPN, Common::kPlatformFMTowns },
+ { "32709cbeeb3044b34129950860a83f14", "pajama2", Common::RU_RUS, Common::kPlatformWindows },
+ { "32a433dea56b86a55b59e4ff7d755711", "ft", Common::EN_USA, Common::kPlatformPC },
+ { "330f631502e381a4e199a3f7cb483c20", "indy3", Common::DE_DEU, Common::kPlatformAmiga },
+ { "3433be9866ca4261b2d5d25374e3f243", "monkey", Common::FR_FRA, Common::kPlatformAmiga },
+ { "3486ede0f904789267d4bcc5537a46d4", "puttzoo", Common::EN_USA, Common::kPlatformMacintosh },
+ { "35a2d3040fa512f8232d9e443319d84d", "dig", Common::EN_USA, Common::kPlatformMacintosh },
+ { "362c1d281fb9899254cda66ad246c66a", "dig", Common::EN_USA, Common::kPlatformUnknown },
+ { "3686cf8f89e102ececf4366e1d2c8126", "monkey2", Common::EN_USA, Common::kPlatformPC },
+ { "36a6750e03fb505fc19fc2bf3e4dbe91", "pajama2", Common::EN_USA, Common::kPlatformUnknown },
+ { "37aed3f91c1ef959e0bd265f9b13781f", "pajama", Common::EN_USA, Common::kPlatformUnknown },
+ { "37f56ceb13e401a7ac7d9e6b37fecaf7", "loom", Common::EN_USA, Common::kPlatformPC },
+ { "37ff1b308999c4cca7319edfcc1280a0", "puttputt", Common::EN_USA, Common::kPlatformWindows },
+ { "3824e60cdf639d22f6df92a03dc4b131", "fbear", Common::EN_USA, Common::kPlatformPC },
+ { "387a544b8b10b26912d8413bab63a853", "monkey2", Common::EN_USA, Common::kPlatformPC },
+ { "3905799e081b80a61d4460b7b733c206", "maniac", Common::EN_USA, Common::kPlatformNES },
+ { "3938ee1aa4433fca9d9308c9891172b1", "zak", Common::EN_USA, Common::kPlatformFMTowns },
+ { "399b217b0c8d65d0398076da486363a9", "indy3", Common::DE_DEU, Common::kPlatformPC },
+ { "39cb9dec16fa16f38d79acd80effb059", "loom", Common::FR_FRA, Common::kPlatformAmiga },
+ { "39cb9dec16fa16f38d79acd80effb059", "loom", Common::IT_ITA, Common::kPlatformAmiga },
+ { "39fd6db10d0222d817025c4d3346e3b4", "farm", Common::EN_USA, Common::kPlatformMacintosh },
+ { "3a03dab514e4038df192d8a8de469788", "atlantis", Common::EN_USA, Common::kPlatformAmiga },
+ { "3a0c35f3c147b98a2bdf8d400cfc4ab5", "indy3", Common::JA_JPN, Common::kPlatformFMTowns },
+ { "3a5ec90d556d4920976c5578bfbfaf79", "maniac", Common::DE_DEU, Common::kPlatformNES },
+ { "3b301b7892f883ce42ab4be6a274fea6", "samnmax", Common::EN_USA, Common::kPlatformPC },
+ { "3cce1913a3bc586b51a75c3892ff18dd", "indy3", Common::RU_RUS, Common::kPlatformPC },
+ { "3d219e7546039543307b55a91282bf18", "funpack", Common::EN_USA, Common::kPlatformPC },
+ { "3de99ef0523f8ca7958faa3afccd035a", "spyfox", Common::EN_USA, Common::kPlatformUnknown },
+ { "3df6ead57930488bc61e6e41901d0e97", "fbear", Common::EN_USA, Common::kPlatformMacintosh },
+ { "3e48298920fab9b7aec5a971e1bd1fab", "pajama3", Common::EN_USA, Common::kPlatformWindows },
+ { "40564ec47da48a67787d1f9bd043902a", "maniac", Common::EN_USA, Common::kPlatformPC },
+ { "4167a92a1d46baa4f4127d918d561f88", "tentacle", Common::EN_USA, Common::kPlatformUnknown },
+ { "425205754fa749f4f0b0dd9d09fa45fd", "football", Common::EN_USA, Common::kPlatformUnknown },
+ { "430bc518017b6fac046f58bab6baad5d", "monkey2", Common::JA_JPN, Common::kPlatformFMTowns },
+ { "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", Common::EN_USA, Common::kPlatformUnknown },
+ { "45152f7cf2ba8f43cf8a8ea2e740ae09", "monkey", Common::ES_ESP, Common::kPlatformPC },
+ { "4521138d15d1fd7649c31fb981746231", "pajama2", Common::DE_DEU, Common::kPlatformUnknown },
+ { "46b53fd430adcfbed791b48a0d4b079f", "funpack", Common::EN_USA, Common::kPlatformPC },
+ { "477dbafbd66a53c98416dc01aef019ad", "monkey", Common::IT_ITA, Common::kPlatformPC },
+ { "47e75b1bdcb44c78cb94883d1731ccf8", "fbear", Common::EN_USA, Common::kPlatformPC },
+ { "48b9f04b348bc5013327753f0d12a144", "loom", Common::ES_ESP, Common::kPlatformAmiga },
+ { "49210e124e4c2b30f1290a9ef6306301", "monkey", Common::EN_USA, Common::kPlatformPC },
+ { "4973bbc3899e3826dbf316e1d7271ec7", "zak", Common::DE_DEU, Common::kPlatformC64 },
+ { "499c958affc394f2a3868f1eb568c3ee", "freddi4", Common::NL_NLD, Common::kPlatformWindows },
+ { "4af4a6b248103c1fe9edef619677f540", "puttmoon", Common::EN_USA, Common::kPlatformMacintosh },
+ { "4ba37f835be11a59d969f90f272f575b", "water", Common::EN_USA, Common::kPlatformUnknown },
+ { "4ba7fb331296c283e73d8f5b2096e551", "samnmax", Common::ES_ESP, Common::kPlatformUnknown },
+ { "4bedb49943df95a9c900a5a82ccbe9de", "ft", Common::FR_FRA, Common::kPlatformUnknown },
+ { "4cb9c3618f71668f8e4346c8f323fa82", "monkey2", Common::EN_USA, Common::kPlatformMacintosh },
+ { "4ce2d5b355964bbcb5e5ce73236ef868", "freddicove", Common::RU_RUS, Common::kPlatformWindows },
+ { "4d34042713958b971cb139fba4658586", "atlantis", Common::JA_JPN, Common::kPlatformFMTowns },
+ { "4dbff3787aedcd96b0b325f2d92d7ad9", "maze", Common::EN_USA, Common::kPlatformUnknown },
+ { "4dc780f1bc587a193ce8a97652791438", "loom", Common::EN_USA, Common::kPlatformAmiga },
+ { "4e5867848ee61bc30d157e2c94eee9b4", "PuttTime", Common::EN_USA, Common::kPlatformUnknown },
+ { "4edbf9d03550f7ba01e7f34d69b678dd", "spyfox", Common::NL_NLD, Common::kPlatformWindows },
+ { "4f04b321a95d4315ce6d65f8e1dd0368", "maze", Common::EN_USA, Common::kPlatformUnknown },
+ { "4f267a901719623de7dde83e47d5b474", "atlantis", Common::DE_DEU, Common::kPlatformAmiga },
+ { "4f580a021eee026f3b4589e17d130d78", "freddi4", Common::UNK_LANG, Common::kPlatformUnknown },
+ { "4fa6870d9bc8c313b65d54b1da5a1891", "pajama", Common::NL_NLD, Common::kPlatformWindows },
+ { "4fbbe9f64b8bc547503a379a301183ce", "tentacle", Common::IT_ITA, Common::kPlatformUnknown },
+ { "5057fb0e99e5aa29df1836329232f101", "freddi2", Common::UNK_LANG, Common::kPlatformWindows },
+ { "507bb360688dc4180fdf0d7597352a69", "freddi", Common::SE_SWE, Common::kPlatformWindows },
+ { "50fcdc982a25063b78ad46bf389b8e8d", "tentacle", Common::IT_ITA, Common::kPlatformPC },
+ { "51305e929e330e24a75a0351c8f9975e", "freddi2", Common::EN_USA, Common::kPlatformUnknown },
+ { "5262a27afcaee04e5c4900220bd463e7", "PuttsFunShop", Common::EN_USA, Common::kPlatformUnknown },
+ { "52a4bae0746a11d7b1e8554e91a6645c", "zak", Common::FR_FRA, Common::kPlatformPC },
+ { "53e94115b55dd51d4b8ff0871aa1df1e", "spyfox", Common::EN_USA, Common::kPlatformUnknown },
+ { "54a936ad06161ff7bfefcb96200f7bff", "monkey", Common::EN_USA, Common::kPlatformAmiga },
+ { "55518cd73cf9c6d23ea29c51ee06bdfe", "ft", Common::IT_ITA, Common::kPlatformUnknown },
+ { "55d3987641bf229c83bc729210173383", "zak", Common::EN_USA, Common::kPlatformC64 },
+ { "55e4cc866ff9046824e1c638ba2b8c7f", "ft", Common::RU_RUS, Common::kPlatformUnknown },
+ { "566165a7338fa11029e7c14d94fa70d0", "freddi", Common::EN_USA, Common::kPlatformWindows },
+ { "5798972220cd458be2626d54c80f71d7", "atlantis", Common::IT_ITA, Common::kPlatformAmiga },
+ { "57b0d89af79befe1cabce3bece869e7f", "tentacle", Common::DE_DEU, Common::kPlatformPC },
+ { "58436e634f4fae1d9973591c2ffa1fcb", "spyfox", Common::EN_USA, Common::kPlatformUnknown },
+ { "589601b676c98b1c0c987bc031ab68b3", "chase", Common::EN_USA, Common::kPlatformUnknown },
+ { "58fdf4c7ad13540a734e18f8584cad89", "puttzoo", Common::EN_USA, Common::kPlatformMacintosh },
+ { "590e6546aacd0d374b7f3a4f53013ab1", "freddicove", Common::UNK_LANG, Common::kPlatformUnknown },
+ { "59d5cfcc5e672a6e07baae01328b918b", "PuttTime", Common::FR_FRA, Common::kPlatformUnknown },
+ { "5a35e36fd777e9c37a49c5b2faca52f9", "loom", Common::EN_USA, Common::kPlatformPC },
+ { "5b08000a9c47b2887df6506ac767ca68", "fbear", Common::EN_USA, Common::kPlatformUnknown },
+ { "5bd335265a61caa3d78956ad9f88ba23", "football", Common::EN_USA, Common::kPlatformUnknown },
+ { "5c21fc49aee8f46e58fef21579e614a1", "thinker1", Common::EN_USA, Common::kPlatformUnknown },
+ { "5d88b9d6a88e6f8e90cded9d01b7f082", "loom", Common::EN_USA, Common::kPlatformPC },
+ { "5e8fb66971a60e523e5afbc4c129c0e8", "socks", Common::EN_USA, Common::kPlatformUnknown },
+ { "5fbe557049892eb4b709d90916ec97ca", "indy3", Common::EN_USA, Common::kPlatformPC },
+ { "600abd3e9f47e63e670188b7e4e86ac7", "spyozon", Common::EN_USA, Common::kPlatformUnknown },
+ { "6027e9ca9c35746d95dee2068cec17e5", "zak", Common::DE_DEU, Common::kPlatformAmiga },
+ { "60ba818dc3bede86d40357e3913f8505", "ft", Common::EN_USA, Common::kPlatformUnknown },
+ { "613f64f78ea26c7353b2a5940eb61d6a", "zak", Common::FR_FRA, Common::kPlatformAtariST },
+ { "624cdb93654667c869d204a64af7e57f", "maniac", Common::EN_USA, Common::kPlatformPC },
+ { "6271130f440066830eca9056c1d7926f", "water", Common::RU_RUS, Common::kPlatformWindows },
+ { "62b8c16b6db226ba95aaa8be73f9885c", "indy3", Common::ES_ESP, Common::kPlatformAmiga },
+ { "63fdcdc95cdeea00060883aed38e5504", "PuttTime", Common::EN_USA, Common::kPlatformWindows },
+ { "6508fd55530e6915507e1cc37f7f045d", "indy3", Common::EN_USA, Common::kPlatformPC },
+ { "659942b9a6b519f123a13cca3c333a13", "jungle", Common::EN_USA, Common::kPlatformMacintosh },
+ { "66236cd1aec24e1d4aff4c4cc93b7e18", "indy3", Common::FR_FRA, Common::kPlatformPC },
+ { "663743c03ae0c007f3d665cf631c0e6b", "puttrace", Common::DE_DEU, Common::kPlatformUnknown },
+ { "66fd5ff9a810dfeb6d6bdada18221140", "monkey", Common::IT_ITA, Common::kPlatformPC },
+ { "672dec94b82f7f0877ebb5b5cf7f4bc1", "pajama", Common::EN_USA, Common::kPlatformUnknown },
+ { "675d71151e9b5a968c8ce46d9fbf4cbf", "zak", Common::EN_USA, Common::kPlatformPC },
+ { "68155a6bf082221525f431c2cbdac8ab", "SamsFunShop", Common::EN_USA, Common::kPlatformUnknown },
+ { "684732efb5799c0f78804c99d8de9aba", "puttputt", Common::EN_USA, Common::kPlatformMacintosh },
+ { "688328c5bdc4c8ec4145688dfa077bf2", "freddi4", Common::DE_DEU, Common::kPlatformUnknown },
+ { "6886e5d08cee329b1f2e743ae2e3ceed", "monkey2", Common::DE_DEU, Common::kPlatformPC },
+ { "695fe0b3963333b7e15b37514db3c745", "thinkerk", Common::EN_USA, Common::kPlatformUnknown },
+ { "697c9b7c55a05d8199c48b48e379d2c8", "puttmoon", Common::HB_ISR, Common::kPlatformPC },
+ { "69ea626f1f87eecb78ea0d6c6b983a1d", "monkey2", Common::IT_ITA, Common::kPlatformPC },
+ { "6a30a07f353a75cdc602db27d73e1b42", "puttputt", Common::EN_USA, Common::kPlatformWindows },
+ { "6af2419fe3db5c2fdb091ae4e5833770", "puttrace", Common::NL_NLD, Common::kPlatformUnknown },
+ { "6b19d0e25cbf720d05822379b8b90ed9", "PuttTime", Common::NL_NLD, Common::kPlatformWindows },
+ { "6b257bb2827dd894b8109a50a1a18b5a", "freddicove", Common::NL_NLD, Common::kPlatformUnknown },
+ { "6b3ec67da214f558dc5ceaa2acd47453", "indy3", Common::EN_USA, Common::kPlatformPC },
+ { "6b5a3fef241e90d4b2e77f1e222773ee", "maniac", Common::SE_SWE, Common::kPlatformNES },
+ { "6bf70eee5de3d24d2403e0dd3d267e8a", "spyfox", Common::UNK_LANG, Common::kPlatformWindows },
+ { "6c2bff0e327f2962e809c2e1a82d7309", "monkey", Common::EN_USA, Common::kPlatformAmiga },
+ { "6dead580b0ff14d5f7b33b4219f04159", "samnmax", Common::EN_USA, Common::kPlatformMacintosh },
+ { "6df20c50c1ab19799de9be7ae7716881", "fbear", Common::EN_USA, Common::kPlatformMacintosh },
+ { "6e959d65358eedf9b68b81e304b97fa4", "tentacle", Common::DE_DEU, Common::kPlatformUnknown },
+ { "6ea966b4d660c870b9ee790d1fbfc535", "monkey2", Common::ES_ESP, Common::kPlatformAmiga },
+ { "6f0be328c64d689bb606d22a389e1b0f", "loom", Common::EN_USA, Common::kPlatformMacintosh },
+ { "6f6ef668c608c7f534fea6e6d3878dde", "indy3", Common::DE_DEU, Common::kPlatformPC },
+ { "6f8a22bfa397be1f7ed4b74aba0e397e", "loom", Common::FR_FRA, Common::kPlatformPC },
+ { "701246819d1a70573f41bf33fc19214f", "soccer", Common::EN_USA, Common::kPlatformUnknown },
+ { "7020931d5a2be0a49d68e7a1882363e4", "zak", Common::EN_USA, Common::kPlatformPC },
+ { "71523b539491527d9860f4407faf0411", "monkey", Common::EN_USA, Common::kPlatformPC },
+ { "71fe97c3108678cf604f14abe342341b", "spyfox2", Common::NL_NLD, Common::kPlatformWindows },
+ { "7222f260253f325c21fcfa68b5bfab67", "spyfox2", Common::EN_USA, Common::kPlatformUnknown },
+ { "72ac6bc980d5101c2142189d746bd62f", "spyfox", Common::RU_RUS, Common::kPlatformWindows },
+ { "732845548b1d6c2da572cb6a1bf81b07", "spyfox2", Common::DE_DEU, Common::kPlatformUnknown },
+ { "73b8197e236da4bf49adc99fe8f5fa1b", "spyfox", Common::DE_DEU, Common::kPlatformUnknown },
+ { "73e5ab7dbb9a8061cc6d25df02dbd1e7", "loom", Common::EN_USA, Common::kPlatformPC },
+ { "746e88c172a5b7a1ae89ac0ee3ee681a", "freddi", Common::RU_RUS, Common::kPlatformWindows },
+ { "754feb59d3bf86b8a00840df74fd7b26", "freddi3", Common::NL_NLD, Common::kPlatformWindows },
+ { "75ba23fff4fd63fa446c02864f2a5a4b", "zak", Common::IT_ITA, Common::kPlatformPC },
+ { "75bff95816b84672b877d22a911ab811", "freddi3", Common::RU_RUS, Common::kPlatformWindows },
+ { "771bc18ec6f93837b839c992b211904b", "monkey", Common::DE_DEU, Common::kPlatformPC },
+ { "77f5c9cc0986eb729c1a6b4c8823bbae", "zak", Common::EN_USA, Common::kPlatformFMTowns },
+ { "780e4a0ae2ff17dc296f4a79543b44f8", "puttmoon", Common::UNK_LANG, Common::kPlatformPC },
+ { "78bd5f036ea35a878b74e4f47941f784", "freddi4", Common::RU_RUS, Common::kPlatformWindows },
+ { "7974365d3dc0f43a2748c975f91ff042", "monkey2", Common::ES_ESP, Common::kPlatformPC },
+ { "7bad72e332a59f9fcc1d437f4edad32a", "puttcircus", Common::RU_RUS, Common::kPlatformUnknown },
+ { "7c2e76087027eeee9c8f8985f93a1cc5", "freddi4", Common::EN_USA, Common::kPlatformUnknown },
+ { "7c8100e360e8ef05f88069d4cfa0afd1", "puttrace", Common::EN_USA, Common::kPlatformWindows },
+ { "7c980a1b1596a93f26917318884f48f7", "PuttTime", Common::DE_DEU, Common::kPlatformUnknown },
+ { "7ddeaf52c8b9a50551ce0aa2ac811d07", "BluesABCTimeDemo", Common::EN_USA, Common::kPlatformUnknown },
+ { "7e151c17adf624f1966c8fc5827c95e9", "puttputt", Common::EN_USA, Common::kPlatformUnknown },
+ { "7ea2da67ebabea4ac20cee9f4f9d2934", "airport", Common::EN_USA, Common::kPlatformMacintosh },
+ { "7edd665bbede7ea8b7233f8e650be6f8", "samnmax", Common::FR_FRA, Common::kPlatformUnknown },
+ { "7f45ddd6dbfbf8f80c0c0efea4c295bc", "maniac", Common::EN_USA, Common::kPlatformPC },
+ { "7f945525abcd48015adf1632637a44a1", "pajama", Common::FR_FRA, Common::kPlatformUnknown },
+ { "7fc6cdb46b4c9d384c52327f4bca6416", "football", Common::EN_USA, Common::kPlatformUnknown },
+ { "810a9da887aefa597b0cf3c77d262897", "BluesABCTimeDemo", Common::EN_USA, Common::kPlatformUnknown },
+ { "81bbfa181184cb494e7a81dcfa94fbd9", "maniac", Common::FR_FRA, Common::kPlatformNES },
+ { "8299d9b8a1b0e7b881bae7a9971dc5e2", "zak", Common::EN_USA, Common::kPlatformAtariST },
+ { "8368f552b1e3eba559f8d559bcc4cadb", "freddi3", Common::UNK_LANG, Common::kPlatformUnknown },
+ { "83cedbe26aa8b58988e984e3d34cac8e", "freddi3", Common::DE_DEU, Common::kPlatformUnknown },
+ { "84e3c23a49ded8a6f9197735c8eb3de7", "PuttTime", Common::DE_DEU, Common::kPlatformWindows },
+ { "861e59ed72a1cd0e6d454f7ee7e2bf3d", "comi", Common::RU_RUS, Common::kPlatformUnknown },
+ { "86be8ada36371d4fdc35659d0e912a26", "indy3", Common::ES_ESP, Common::kPlatformPC },
+ { "86c9902b7bec1a17926d4dae85beaa45", "airport", Common::EN_USA, Common::kPlatformWindows },
+ { "870d1e3c86bc50846d808d14a36b4e08", "monkey", Common::ES_ESP, Common::kPlatformAmiga },
+ { "87f6e8037b7cc996e13474b491a7a98e", "maniac", Common::IT_ITA, Common::kPlatformPC },
+ { "8801fb4a1200b347f7a38523339526dd", "jungle", Common::EN_USA, Common::kPlatformWindows },
+ { "883af4b0af4f77a92f1dcf1d0a283140", "tentacle", Common::ES_ESP, Common::kPlatformUnknown },
+ { "898ce8eb1234a955ef75e87141902bb3", "freddi3", Common::RU_RUS, Common::kPlatformWindows },
+ { "898eaa21f79cf8d4f08db856244689ff", "pajama", Common::EN_USA, Common::kPlatformWindows },
+ { "89cfc425566003ff74b7dc7b3e6fd469 ", "indy3", Common::FR_FRA, Common::kPlatformPC },
+ { "8a484262363a8e18be87112454f1456b", "pjgames", Common::EN_USA, Common::kPlatformUnknown },
+ { "8aa05d3cdb0e795436043f0546af2da2", "tentacle", Common::FR_FRA, Common::kPlatformUnknown },
+ { "8afb3cf9f95abf208358e984f0c9e738", "funpack", Common::EN_USA, Common::kPlatformUnknown },
+ { "8bdb0bf87b5e303dd35693afb9351215", "ft", Common::DE_DEU, Common::kPlatformUnknown },
+ { "8d479e36f35e80257dfc102cf4b8a912", "farm", Common::EN_USA, Common::kPlatformWindows },
+ { "8e3241ddd6c8dadf64305e8740d45e13", "balloon", Common::EN_USA, Common::kPlatformUnknown },
+ { "8e4ee4db46954bfe2912e259a16fad82", "monkey2", Common::FR_FRA, Common::kPlatformPC },
+ { "8eb84cee9b429314c7f0bdcf560723eb", "monkey", Common::EN_USA, Common::kPlatformFMTowns },
+ { "8ee63cafb1fe9d62aa0d5a23117e70e7", "freddi2", Common::EN_USA, Common::kPlatformUnknown },
+ { "8f3758ff98c9c5d78e5d635222cad026", "atlantis", Common::IT_ITA, Common::kPlatformPC },
+ { "8fec68383202d38c0d25e9e3b757c5df", "comi", Common::UNK_LANG, Common::kPlatformUnknown },
+ { "8ffd618a776a4c0d8922bb28b09f8ce8", "airport", Common::EN_USA, Common::kPlatformWindows },
+ { "90a329d8ad5b7ce0690429e98cfbb32f", "funpack", Common::HB_ISR, Common::kPlatformPC },
+ { "90c755e1c9b9b8a4129d37b2259d0655", "chase", Common::EN_USA, Common::kPlatformUnknown },
+ { "910e31cffb28226bd68c569668a0d6b4", "monkey", Common::ES_ESP, Common::kPlatformPC },
+ { "91469353f7be1b122fa88d23480a1320", "zak", Common::FR_FRA, Common::kPlatformAmiga },
+ { "91d5db93187fab54d823f73bd6441cb6", "maniac", Common::EN_USA, Common::kPlatformNES },
+ { "927a764615c7fcdd72f591355e089d8c", "monkey", Common::DE_DEU, Common::kPlatformAtariST },
+ { "92b078d9d6d9d751da9c26b8b3075779", "tentacle", Common::FR_FRA, Common::kPlatformPC },
+ { "92e7727e67f5cd979d8a1070e4eb8cb3", "puttzoo", Common::EN_USA, Common::kPlatformUnknown },
+ { "92fc0073a4cf259ff36070ecb8628ba8", "thinkerk", Common::EN_USA, Common::kPlatformUnknown },
+ { "94aaedbb8f26d71ed3ad6dd34490e29e", "tentacle", Common::FR_FRA, Common::kPlatformPC },
+ { "96a3069a3c63caa7329588ce1fef41ee", "spyozon", Common::RU_RUS, Common::kPlatformUnknown },
+ { "9708cf716ed8bcc9ff3fcfc69413b746", "puttputt", Common::EN_USA, Common::kPlatformPC },
+ { "981e1e1891f2be7e25a01f50ae55a5af", "puttrace", Common::EN_USA, Common::kPlatformUnknown },
+ { "98744fe66ff730e8c2b3b1f58803ab0b", "atlantis", Common::EN_USA, Common::kPlatformPC },
+ { "99a3699f80b8f776efae592b44b9b991", "maniac", Common::FR_FRA, Common::kPlatformPC },
+ { "99b6f822b0b2612415407865438697d6", "atlantis", Common::EN_USA, Common::kPlatformPC },
+ { "9b7452b5cd6d3ffb2b2f5118010af84f", "ft", Common::EN_USA, Common::kPlatformMacintosh },
+ { "9bc548e179cdb0767009401c094d0895", "maniac", Common::DE_DEU, Common::kPlatformAmiga },
+ { "9bd2a8f72613e715c199246dd511e10f", "atlantis", Common::ES_ESP, Common::kPlatformPC },
+ { "9bda5fee51d2fda5253d02c642016bf4", "spyfox", Common::NL_NLD, Common::kPlatformWindows },
+ { "9c0fee288ad564a7d25ec3e841810d79", "indy3", Common::EN_USA, Common::kPlatformAmiga },
+ { "9c143c5905055d5df7a0f014ab379aee", "puttmoon", Common::EN_USA, Common::kPlatformWindows },
+ { "9c92eeaf517a31b7221ec2546ab669fd", "puttmoon", Common::EN_USA, Common::kPlatformWindows },
+ { "9cdd327c1034c046cb595d251c44da2f", "chase", Common::RU_RUS, Common::kPlatformWindows },
+ { "9d4ab3e0e1d1ebc6ba8a6a4c470ed184", "spyfox", Common::EN_USA, Common::kPlatformUnknown },
+ { "9d7b67be003fea60be4dcbd193611936", "ft", Common::EN_USA, Common::kPlatformMacintosh },
+ { "9dc02577bf50d4cfaf3de3fbac06fbe2", "puttmoon", Common::EN_USA, Common::kPlatformMacintosh },
+ { "9e5e0fb43bd22f4628719b7501adb717", "monkey", Common::FR_FRA, Common::kPlatformAtariST },
+ { "a095e33061606d231ff37dca4c64c8ac", "pajama", Common::DE_DEU, Common::kPlatformUnknown },
+ { "a0a7dea72003933b8b3f8b99b9f7ddeb", "loom", Common::EN_USA, Common::kPlatformAtariST },
+ { "a194f15f51ee62badab74b9e7da97693", "baseball2001", Common::EN_USA, Common::kPlatformUnknown },
+ { "a28135a7ade38cc0208b04507c46efd1", "spyfox", Common::DE_DEU, Common::kPlatformUnknown },
+ { "a2bb6aa0537402c1b3c2ea899ccef64b", "lost", Common::EN_USA, Common::kPlatformWindows },
+ { "a3036878840720fbefa41e6965fa4a0a", "samnmax", Common::EN_USA, Common::kPlatformPC },
+ { "a525c1753c1db5011c00417da37887ef", "PuttTime", Common::EN_USA, Common::kPlatformUnknown },
+ { "a561d2e2413cc1c71d5a1bf87bf493ea", "lost", Common::EN_USA, Common::kPlatformUnknown },
+ { "a570381b028972d891052ee1e51dc011", "maniac", Common::EN_USA, Common::kPlatformAtariST },
+ { "a654fb60c3b67d6317a7894ffd9f25c5", "pajama3", Common::EN_USA, Common::kPlatformUnknown },
+ { "a7cacad9c40c4dc9e1812abf6c8af9d5", "puttcircus", Common::EN_USA, Common::kPlatformUnknown },
+ { "a85856675429fe88051744f755b72f93", "farm", Common::EN_USA, Common::kPlatformWindows },
+ { "a86f9c49355579c30d4a55b477c0d869", "baseball2001", Common::EN_USA, Common::kPlatformUnknown },
+ { "a9543ef0d79bcb47cd76ec197ad0a967", "puttmoon", Common::EN_USA, Common::kPlatformUnknown },
+ { "a9f2f04b1ecaab9495b59befffe9bf88", "pajama3", Common::EN_USA, Common::kPlatformUnknown },
+ { "aa6a91b7f6f119d1b7b1f2a4c9e24d59", "puttmoon", Common::EN_USA, Common::kPlatformPC },
+ { "aa7a07d94ae853f6460be4ce0a1bf530", "monkey", Common::FR_FRA, Common::kPlatformPC },
+ { "aa8a0cb65f3afbbe2c14c3f9f92775a3", "monkey", Common::FR_FRA, Common::kPlatformPC },
+ { "aaa587701cde7e74692c68c1024b85eb", "puttrace", Common::NL_NLD, Common::kPlatformUnknown },
+ { "ab0693e9324cfcf498fdcbb12acf8bb4", "puttcircus", Common::EN_USA, Common::kPlatformUnknown },
+ { "ac1642b6edfb8521ca03760126f1c250", "tentacle", Common::DE_DEU, Common::kPlatformPC },
+ { "ac62d50e39492ee3738b4e83a5ac780f", "freddi2", Common::NL_NLD, Common::kPlatformWindows },
+ { "acad97ab1c6fc2a5b2d98abf6db4a190", "tentacle", Common::EN_USA, Common::kPlatformUnknown },
+ { "ae94f110a14ce71fc515d5b648827a8f", "tentacle", Common::ES_ESP, Common::kPlatformPC },
+ { "b23f7cd7c304d7dff08e92a96120d5b4", "zak", Common::EN_USA, Common::kPlatformPC },
+ { "b250d0f9cc83f80ced56fe11a4fb057c", "maniac", Common::EN_USA, Common::kPlatformPC },
+ { "b289a2a8cbedbf45786e0b4ad2f510f1", "samnmax", Common::IT_ITA, Common::kPlatformPC },
+ { "b5298a5c15ffbe8b381d51ea4e26d35c", "freddi4", Common::DE_DEU, Common::kPlatformUnknown },
+ { "b597e0403cc0002f69170e6caba7edd9", "indy3", Common::EN_USA, Common::kPlatformPC },
+ { "b628506f7def772e40de0aa5440fb8e1", "activity", Common::EN_USA, Common::kPlatformWindows },
+ { "b886b0a5d909c7158a914e1d7c1c6c65", "loom", Common::FR_FRA, Common::kPlatformPC },
+ { "b8955d7d23b4972229060d1592489fef", "freddicove", Common::NL_NLD, Common::kPlatformWindows },
+ { "ba888e6831517597859e91aa173f945c", "spyfox", Common::FR_FRA, Common::kPlatformUnknown },
+ { "bbadf7309c4a2c2763e4bbba3c3be634", "freddi3", Common::FR_FRA, Common::kPlatformUnknown },
+ { "bc4700bc0e12879f6d25d14d6be6cfdd", "spyfox2", Common::DE_DEU, Common::kPlatformUnknown },
+ { "bd126753de619a495f9f22adc951c8d5", "monkey2", Common::IT_ITA, Common::kPlatformPC },
+ { "be39a5d4db60e8aa736b9086778cb45c", "spyozon", Common::EN_USA, Common::kPlatformWindows },
+ { "be83e882b44f2767bc08d4f766ebc347", "maniac", Common::DE_DEU, Common::kPlatformAtariST },
+ { "bf8b52fdd9a69c67f34e8e9fec72661c", "farm", Common::EN_USA, Common::kPlatformWindows },
+ { "bfdf584b01503f0762baded581f6a0a2", "SoccerMLS", Common::EN_USA, Common::kPlatformWindows },
+ { "c0039ad982999c92d0de81910d640fa0", "freddi", Common::NL_NLD, Common::kPlatformWindows },
+ { "c13225cb1bbd3bc9fe578301696d8021", "monkey", Common::EN_USA, Common::kPlatformSegaCD },
+ { "c24c490373aeb48fbd54caa8e7ae376d", "loom", Common::DE_DEU, Common::kPlatformAtariST },
+ { "c25755b08a8d0d47695e05f1e2111bfc", "freddi4", Common::EN_USA, Common::kPlatformUnknown },
+ { "c30ef068add4277104243c31ce46c12b", "monkey2", Common::FR_FRA, Common::kPlatformAmiga },
+ { "c3196c5349e53e387aaff1533d95e53a", "samnmax", Common::EN_USA, Common::kPlatformPC },
+ { "c3b22fa4654bb580b20325ebf4174841", "puttzoo", Common::NL_NLD, Common::kPlatformWindows },
+ { "c3df37df9d3b481b45f75283a9907c47", "loom", Common::IT_ITA, Common::kPlatformPC },
+ { "c4787c3e8b5e2dfda90850ee800af00f", "zak", Common::FR_FRA, Common::kPlatformPC },
+ { "c4a7f7398ac9ae588940f9912ea5fd8f", "maniac", Common::DE_DEU, Common::kPlatformC64 },
+ { "c4ffae9fac495475d6bc3343ccc8faf9", "Soccer2004", Common::EN_USA, Common::kPlatformUnknown },
+ { "c5d10e190d4b4d59114b824f2fdbd00e", "loom", Common::EN_USA, Common::kPlatformFMTowns },
+ { "c63ee46143ba65f9ce14cf539ca51bd7", "atlantis", Common::EN_USA, Common::kPlatformPC },
+ { "c666a998af90d81db447eccba9f72c8d", "monkey", Common::EN_USA, Common::kPlatformAtariST },
+ { "c6907d44f1166941d982864cd42cdc89", "pajama2", Common::DE_DEU, Common::kPlatformUnknown },
+ { "c7890e038806df2bb5c0c8c6f1986ea2", "monkey", Common::EN_USA, Common::kPlatformPC },
+ { "c7be10f775404fd9785a8b92a06d240c", "atlantis", Common::EN_USA, Common::kPlatformFMTowns },
+ { "c83079157ec765a28de445aec9768d60", "tentacle", Common::EN_USA, Common::kPlatformUnknown },
+ { "c8aac5e3e701874e2fa4117896f9e1b1", "freddi", Common::EN_USA, Common::kPlatformMacintosh },
+ { "cb1559e8405d17a5a278a6b5ad9338d1", "freddi3", Common::EN_USA, Common::kPlatformUnknown },
+ { "cc04a076779379524ed4d9c5ee3c6fb1", "tentacle", Common::EN_USA, Common::kPlatformMacintosh },
+ { "cc8ba2b0df2f9c450bcf055fe2711979", "samnmax", Common::DE_DEU, Common::kPlatformPC },
+ { "cd9c05e755d7bf8e9b9590ad1ebe273e", "dig", Common::EN_USA, Common::kPlatformMacintosh },
+ { "cdd760228cf1010c2903f37e788ea31c", "zak", Common::DE_DEU, Common::kPlatformPC },
+ { "ce6a4cef315b20fef58a95bc40a2d8d3", "monkey", Common::FR_FRA, Common::kPlatformPC },
+ { "ce7733f185b838e248927c7ba1a04204", "maniac", Common::FR_FRA, Common::kPlatformAmiga },
+ { "ce7fd0c382389a6791fc3e199c117ef4", "indy3", Common::ES_ESP, Common::kPlatformPC },
+ { "cea91e3dd47f2518ea418e41611aa77f", "spyfox2", Common::RU_RUS, Common::kPlatformUnknown },
+ { "cf4ef315214c7d8cdab6302cdb7e50db", "freddi", Common::DE_DEU, Common::kPlatformWindows },
+ { "cf8d13446ec6cb6222287a925fd47c1d", "baseball", Common::EN_USA, Common::kPlatformUnknown },
+ { "cf8ef3a1fb483c5c4b1c584d1167b2c4", "freddi", Common::DE_DEU, Common::kPlatformWindows },
+ { "cf90b4db5486ef798db78fe6fbf897e5", "pajama3", Common::EN_USA, Common::kPlatformWindows },
+ { "d06fbe28818fef7bfc45c2cdf0c0849d", "zak", Common::DE_DEU, Common::kPlatformPC },
+ { "d0b531227a27c6662018d2bd05aac52a", "monkey", Common::DE_DEU, Common::kPlatformPC },
+ { "d220d154aafbfa12bd6f3ab1b2dae420", "puttzoo", Common::DE_DEU, Common::kPlatformMacintosh },
+ { "d37c55388294b66e53e7ced3af88fa68", "freddi2", Common::EN_USA, Common::kPlatformUnknown },
+ { "d43352a805d78b5f4936c6d7779bf575", "samnmax", Common::RU_RUS, Common::kPlatformPC },
+ { "d4aac997e2f4e15341f0bfbf905419bd", "PuttTime", Common::EN_USA, Common::kPlatformWindows },
+ { "d4b8ee426b1afd3e53bc0cf020418cf6", "dog", Common::EN_USA, Common::kPlatformWindows },
+ { "d4cccb5af88f3e77f370896e9ba8c5f9", "freddi", Common::UNK_LANG, Common::kPlatformWindows },
+ { "d4e79c3d8645b8266cd78c325bc35154", "pajama2", Common::EN_USA, Common::kPlatformUnknown },
+ { "d55eff37c2100f5065cde9de428621fa", "zak", Common::EN_USA, Common::kPlatformAtariST },
+ { "d62047a6729349ab36f7ee065bf26509", "dig", Common::RU_RUS, Common::kPlatformUnknown },
+ { "d62d248c3df6ec177405e2cb23d923b2", "indy3", Common::IT_ITA, Common::kPlatformPC },
+ { "d6334a5a9b61afe18c368540fdf522ca", "airport", Common::EN_USA, Common::kPlatformMacintosh },
+ { "d6dd0646404768a63e963891a96daadd", "atlantis", Common::EN_USA, Common::kPlatformMacintosh },
+ { "d7ab7cd6105546016e6a0d46fb36b964", "pajama", Common::EN_USA, Common::kPlatformUnknown },
+ { "d7b247c26bf1f01f8f7daf142be84de3", "balloon", Common::EN_USA, Common::kPlatformWindows },
+ { "d831f7c048574dd9d5d85db2a1468099", "maniac", Common::EN_USA, Common::kPlatformC64 },
+ { "d8323015ecb8b10bf53474f6e6b0ae33", "dig", Common::UNK_LANG, Common::kPlatformUnknown },
+ { "d8d07efcb88f396bee0b402b10c3b1c9", "maniac", Common::EN_USA, Common::kPlatformNES },
+ { "d917f311a448e3cc7239c31bddb00dd2", "samnmax", Common::EN_USA, Common::kPlatformUnknown },
+ { "d9d0dd93d16ab4dec55cabc2b86bbd17", "samnmax", Common::EN_USA, Common::kPlatformPC },
+ { "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", Common::EN_USA, Common::kPlatformFMTowns },
+ { "da6269b18fcb08189c0aa9c95533cce2", "monkey", Common::IT_ITA, Common::kPlatformPC },
+ { "da669b20271b85182e9c17a2a37ea02e", "monkey2", Common::DE_DEU, Common::kPlatformAmiga },
+ { "dd30a53035393baa5a5e222e716559af", "maniac", Common::FR_FRA, Common::kPlatformAtariST },
+ { "de4efb910210736813c9a1185384bace", "puttzoo", Common::EN_USA, Common::kPlatformWindows },
+ { "debe337f73d660e951ece7c1f1c81add", "zak", Common::EN_USA, Common::kPlatformPC },
+ { "defb8cb9ec4b0f91acfb6b61c6129ad9", "PuttTime", Common::RU_RUS, Common::kPlatformWindows },
+ { "df03ee021aa9b81d90cab9c26da07614", "indy3", Common::IT_ITA, Common::kPlatformAmiga },
+ { "df047cc4792150f601290357566d36a6", "freddi", Common::EN_USA, Common::kPlatformUnknown },
+ { "e01acc8c12ef44e8f778fe87e5f90f4e", "fbpack", Common::EN_USA, Common::kPlatformUnknown },
+ { "e03ed1474ec14de78359970e0457a820", "freddi4", Common::EN_USA, Common::kPlatformWindows },
+ { "e144f5f49d9241d2a9dee2576b3d09cb", "airport", Common::EN_USA, Common::kPlatformWindows },
+ { "e17db1ddf91b39ca6bbc8ad3ed19e883", "monkey", Common::JA_JPN, Common::kPlatformFMTowns },
+ { "e246e02db9630533a40d99c9f54a8e01", "monkey2", Common::EN_USA, Common::kPlatformMacintosh },
+ { "e361a7058ed8e8ebb462663c0a3ae8d6", "puttputt", Common::HB_ISR, Common::kPlatformPC },
+ { "e41de1c2a15abbcdbf9977e2d7e8a340", "freddi2", Common::RU_RUS, Common::kPlatformWindows },
+ { "e534d29afb3c6e0ee9dc3d53c5956714", "atlantis", Common::DE_DEU, Common::kPlatformAmiga },
+ { "e63a0b9249b5ca4cc4d3ac34305ae360", "freddi", Common::NB_NOR, Common::kPlatformWindows },
+ { "e689bdf67f98b1d760ce4487ec0e8d06", "indy3", Common::FR_FRA, Common::kPlatformAmiga },
+ { "e6cd81b25ab1453a8a6d3482118c391e", "pass", Common::EN_USA, Common::kPlatformPC },
+ { "e72bb4c2b613db2cf50f89ff6350e70a", "ft", Common::ES_ESP, Common::kPlatformUnknown },
+ { "e781230da44a44e2f0770edb2b3b3633", "maniac", Common::EN_USA, Common::kPlatformAmiga },
+ { "e94c7cc3686fce406d3c91b5eae5a72d", "zak", Common::EN_USA, Common::kPlatformAmiga },
+ { "e98b982ceaf9d253d730bde8903233d6", "monkey", Common::DE_DEU, Common::kPlatformPC },
+ { "eae95b2b3546d8ba86ae1d397c383253", "dog", Common::EN_USA, Common::kPlatformUnknown },
+ { "ebd0b2c8a387f18887282afe6cad894a", "spyozon", Common::EN_USA, Common::kPlatformUnknown },
+ { "ebd324dcf06a4c49e1ba5c231eee1060", "freddi4", Common::EN_USA, Common::kPlatformUnknown },
+ { "ed2b074bc3166087a747acb2a3c6abb0", "freddi3", Common::DE_DEU, Common::kPlatformUnknown },
+ { "ed361270102e355afe5236954216aba2", "lost", Common::EN_USA, Common::kPlatformUnknown },
+ { "edfdb24a499d92c59f824c52987c0eec", "atlantis", Common::FR_FRA, Common::kPlatformPC },
+ { "ef347474f3c7be3b29584eaa133cca05", "samnmax", Common::FR_FRA, Common::kPlatformPC },
+ { "ef74d9071d4e564b037cb44bd6774de7", "fbear", Common::HB_ISR, Common::kPlatformPC },
+ { "efe0a04a703e765ebebe92b6c8aa6b86", "baseball2003", Common::EN_USA, Common::kPlatformUnknown },
+ { "f049e38c1f8302b5db6170f1872af89a", "monkey", Common::ES_ESP, Common::kPlatformPC },
+ { "f06e66fd45b2f8b0f4a2833ff4476050", "fbpack", Common::HB_ISR, Common::kPlatformPC },
+ { "f08145577e4f13584cc90b3d6e9caa55", "pajama3", Common::NL_NLD, Common::kPlatformUnknown },
+ { "f1b0e0d587b85052de5534a3847e68fe", "water", Common::EN_USA, Common::kPlatformUnknown },
+ { "f237bf8a5ef9af78b2a6a4f3901da341", "pajama", Common::EN_USA, Common::kPlatformUnknown },
+ { "f27b1ba0eadaf2a6617b2b58192d1dbf", "samnmax", Common::DE_DEU, Common::kPlatformPC },
+ { "f3d55aea441e260e9e9c7d2a187097e0", "puttzoo", Common::EN_USA, Common::kPlatformWindows },
+ { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", Common::EN_USA, Common::kPlatformMacintosh },
+ { "f73883f13b5a302749a5bad31d909780", "tentacle", Common::DE_DEU, Common::kPlatformMacintosh },
+ { "f7711f9264d4d43c2a1518ec7c10a607", "pajama3", Common::EN_USA, Common::kPlatformUnknown },
+ { "f79e60c17cca601e411f1f75e8ee9b5a", "spyfox2", Common::UNK_LANG, Common::kPlatformUnknown },
+ { "fa127d7c4bb47d05bb1c33ddcaa9f767", "loom", Common::DE_DEU, Common::kPlatformPC },
+ { "fb66aa42de21675116346213f176a366", "monkey", Common::IT_ITA, Common::kPlatformAmiga },
+ { "fbb697d89d2beca87360a145f467bdae", "PuttTime", Common::DE_DEU, Common::kPlatformUnknown },
+ { "fbbbb38a81fc9d6a61d509278390a290", "farm", Common::EN_USA, Common::kPlatformMacintosh },
+ { "fbdd947d21e8f5bac6d6f7a316af1c5a", "spyfox", Common::EN_USA, Common::kPlatformUnknown },
+ { "fc53ce0e5f6562b1c1e1b4b8203acafb", "samnmax", Common::ES_ESP, Common::kPlatformPC },
+ { "fc6b6148e80d67939d9a18697c0f626a", "monkey", Common::DE_DEU, Common::kPlatformPC },
+ { "fc8d197a22146e74766e9cb0cfcaf1da", "freddi2", Common::EN_USA, Common::kPlatformUnknown },
+ { "fcb78ebecab2757264c590890c319cc5", "PuttTime", Common::NL_NLD, Common::kPlatformWindows },
+ { "fce4b8010704b103acfeea9413788f32", "freddi2", Common::DE_DEU, Common::kPlatformUnknown },
+ { "fe381e45117878b1e942cb876b050fd6", "ft", Common::EN_USA, Common::kPlatformMacintosh },
+ { "fe60d6b5ff51b0553ac59963123b5777", "comi", Common::UNK_LANG, Common::kPlatformUnknown },
+ { "ff05c07990061d97647f059c48c1d05a", "zak", Common::DE_DEU, Common::kPlatformAtariST },
+ { 0, 0, Common::UNK_LANG, Common::kPlatformUnknown }
+};
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
new file mode 100644
index 0000000000..5cd9ef9111
--- /dev/null
+++ b/engines/scumm/scumm.cpp
@@ -0,0 +1,3472 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/md5.h"
+#include "common/system.h"
+
+#include "gui/message.h"
+#include "gui/newgui.h"
+
+#include "scumm/akos.h"
+#include "scumm/charset.h"
+#include "scumm/costume.h"
+#include "scumm/debugger.h"
+#include "scumm/dialogs.h"
+#include "scumm/imuse.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/insane/insane.h"
+#include "scumm/intern.h"
+#include "scumm/intern_he.h"
+#include "scumm/logic_he.h"
+#include "scumm/player_nes.h"
+#include "scumm/player_v1.h"
+#include "scumm/player_v2.h"
+#include "scumm/player_v2a.h"
+#include "scumm/player_v3a.h"
+#include "scumm/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/sprite_he.h"
+#include "scumm/util.h"
+
+#ifdef PALMOS_68K
+#include "extras/palm-scumm-md5.h"
+#else
+#include "scumm/scumm-md5.h"
+#endif
+#include "scumm/verbs.h"
+
+#include "sound/mixer.h"
+
+#ifdef MACOSX
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#ifdef _WIN32_WCE
+extern bool isSmartphone(void);
+#endif
+
+#if (defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
+namespace Graphics {
+ extern void initfonts();
+}
+#endif
+
+static int generateSubstResFileName_(const char *filename, char *buf, int bufsize, int index);
+
+using Common::File;
+
+namespace Scumm {
+
+// Use g_scumm from error() ONLY
+ScummEngine *g_scumm = 0;
+
+struct ScummGameSettings {
+ const char *gameid;
+ const char *description;
+ byte id, version, heversion;
+ int midi; // MidiDriverFlags values
+ uint32 features;
+ Common::Platform platform;
+
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { gameid, description, features };
+ return dummy;
+ }
+};
+
+
+enum {
+ // We only compute the MD5 of the first megabyte of our data files.
+ kMD5FileSizeLimit = 1024 * 1024
+};
+
+
+struct ObsoleteGameIDs {
+ const char *from;
+ const char *to;
+ Common::Platform platform;
+
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { from, "Obsolete game ID", 0 };
+ return dummy;
+ }
+};
+
+static const Common::Platform UNK = Common::kPlatformUnknown;
+
+/**
+ * Conversion table mapping old obsolete game IDs to the
+ * corresponding new game ID and platform combination.
+ *
+ * We use an ugly macro 'UNK' here to make the following table more readable.
+ */
+static ObsoleteGameIDs obsoleteGameIDsTable[] = {
+ {"comidemo", "comi", UNK},
+ {"digdemo", "dig", UNK},
+ {"digdemoMac", "dig", Common::kPlatformMacintosh},
+ {"dottdemo", "tentacle", UNK},
+ {"fate", "atlantis", UNK},
+ {"ftMac", "ft", Common::kPlatformMacintosh},
+ {"ftpcdemo", "ft", UNK},
+ {"ftdemo", "ft", Common::kPlatformMacintosh},
+ {"game", "monkey", UNK},
+ {"indy3ega", "indy3", UNK},
+ {"indy3towns", "indy3", Common::kPlatformFMTowns},
+ {"indy4", "atlantis", Common::kPlatformFMTowns},
+ {"indydemo", "atlantis", Common::kPlatformFMTowns},
+ {"loomcd", "loom", UNK},
+ {"loomTowns", "loom", Common::kPlatformFMTowns},
+ {"mi2demo", "monkey2", UNK},
+ {"monkey1", "monkey", UNK},
+ {"monkeyEGA", "monkey", UNK},
+ {"monkeyVGA", "monkey", UNK},
+ {"playfate", "atlantis", UNK},
+ {"samnmax-alt", "samnmax", UNK},
+ {"samnmaxMac", "samnmax", Common::kPlatformMacintosh},
+ {"samdemo", "samnmax", UNK},
+ {"samdemoMac", "samnmax", Common::kPlatformMacintosh},
+ {"snmdemo", "samnmax", UNK},
+ {"snmidemo", "samnmax", UNK},
+ {"tentacleMac", "tentacle", Common::kPlatformMacintosh},
+ {"zakTowns", "zak", Common::kPlatformFMTowns},
+ {NULL, NULL, UNK}
+};
+
+static const ScummGameSettings scumm_settings[] = {
+ /* Scumm Version 1 */
+ /* Scumm Version 2 */
+
+ {"maniac", "Maniac Mansion", GID_MANIAC, 2, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformPC},
+ {"zak", "Zak McKracken and the Alien Mindbenders", GID_ZAK, 2, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformPC},
+
+ /* Scumm Version 3 */
+ {"indy3", "Indiana Jones and the Last Crusade", GID_INDY3, 3, 0, MDT_PCSPK | MDT_ADLIB,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformPC},
+ {"loom", "Loom", GID_LOOM, 3, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformPC},
+
+ /* Scumm Version 4 */
+ {"pass", "Passport to Adventure", GID_PASS, 4, 0, MDT_PCSPK | MDT_ADLIB,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+
+ /* Scumm version 5, small header -- we treat these as V4 games, since internally
+ they really are much closer to the V4 games than to all other V5 games. */
+ {"monkey", "The Secret of Monkey Island", GID_MONKEY_VGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY, Common::kPlatformPC},
+
+ /* Scumm version 5 */
+ {"monkey2", "Monkey Island 2: LeChuck's Revenge", GID_MONKEY2, 5, 0, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY, Common::kPlatformPC},
+
+ {"atlantis", "Indiana Jones and the Fate of Atlantis", GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY, Common::kPlatformPC},
+
+ /* Scumm Version 6 */
+ {"tentacle", "Day of the Tentacle", GID_TENTACLE, 6, 0, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY, Common::kPlatformPC},
+
+ {"samnmax", "Sam & Max Hit the Road", GID_SAMNMAX, 6, 0, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY, Common::kPlatformPC},
+
+// {"test", "Test demo game", GID_SAMNMAX, 6, 0, /*MDT_PCSPK |*/ MDT_ADLIB | MDT_MIDI, GF_NEW_OPCODES, Common::kPlatformUnknown},
+
+#ifndef DISABLE_SCUMM_7_8
+ /* Scumm Version 7 */
+ {"ft", "Full Throttle", GID_FT, 7, 0, MDT_NONE,
+ GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE, Common::kPlatformPC},
+
+ {"dig", "The Dig", GID_DIG, 7, 0, MDT_NONE,
+ GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE, Common::kPlatformPC},
+
+ /* Scumm Version 8 */
+ {"comi", "The Curse of Monkey Island", GID_CMI, 8, 0, MDT_NONE,
+ GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEFAULT_TO_1X_SCALER, Common::kPlatformWindows},
+
+#endif
+
+ // Humongous Entertainment Scumm Version 6
+ {"puttputt", "Putt-Putt Joins the Parade", GID_HEGAME, 6, 61, MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformPC},
+ {"puttmoon", "Putt-Putt Goes to the Moon", GID_HEGAME, 6, 61, MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformPC},
+ {"funpack", "Putt-Putt's Fun Pack", GID_FUNPACK, 6, 61, MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformPC},
+ {"fbpack", "Fatty Bear's Fun Pack", GID_HEGAME, 6, 61, MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformPC},
+ {"fbear", "Fatty Bear's Birthday Surprise", GID_FBEAR, 6, 61, MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformPC},
+
+#ifndef DISABLE_HE
+ {"activity", "Putt-Putt & Fatty Bear's Activity Pack", GID_HEGAME, 6, 70, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ // Humongous Entertainment Scumm Version 7.1
+ // The first version to use 640x480 resolution
+ // There are also 7.1 versions of freddemo, airdemo and farmdemo
+ {"catalog", "Humongous Interactive Catalog", GID_HEGAME, 6, 71, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"freddi", "Freddi Fish 1: The Case of the Missing Kelp Seeds", GID_HEGAME, 6, 71, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ // Humongous Entertainment Scumm Version 7.2
+ {"airport", "Let's Explore the Airport with Buzzy", GID_HEGAME, 6, 72, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"puttzoo", "Putt-Putt Saves the Zoo", GID_HEGAME, 6, 72, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ // Changed o_getResourceSize to cover all resource types
+ {"farm", "Let's Explore the Farm with Buzzy", GID_HEGAME, 6, 73, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"jungle", "Let's Explore the Jungle with Buzzy", GID_HEGAME, 6, 73, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ // Humongous Entertainment Scumm Version 8.0 ? Scummsrc.80
+ {"freddi2", "Freddi Fish 2: The Case of the Haunted Schoolhouse", GID_HEGAME, 6, 80, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"pajama", "Pajama Sam 1: No Need to Hide When It's Dark Outside", GID_HEGAME, 6, 80, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"putttime", "Putt-Putt Travels Through Time", GID_HEGAME, 6, 80, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"balloon", "Putt-Putt and Pep's Balloon-O-Rama", GID_HEGAME, 6, 80, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"dog", "Putt-Putt and Pep's Dog on a Stick", GID_HEGAME, 6, 80, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"maze", "Freddi Fish and Luther's Maze Madness", GID_HEGAME, 6, 80, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"socks", "Pajama Sam's Sock Works", GID_HEGAME, 6, 80, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"water", "Freddi Fish and Luther's Water Worries", GID_WATER, 6, 80, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ // Humongous Entertainment Scumm Version 9.0 ? Scummsys.90
+ {"baseball", "Backyard Baseball", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"thinkerk", "Big Thinkers Kindergarten", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"thinker1", "Big Thinkers First Grade", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"freddi3", "Freddi Fish 3: The Case of the Stolen Conch Shell", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"spyfox", "Spy Fox 1: Dry Cereal", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ // Humongous Entertainment Scumm Version 9.5 ? Scummsys.95
+ {"pajama2", "Pajama Sam 2: Thunder and Lightning Aren't so Frightening", GID_HEGAME, 6, 95, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"chase", "Spy Fox in Cheese Chase", GID_HEGAME, 6, 95, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ // Humongous Entertainment Scumm Version 9.8 ? Scummsys.98
+ // these and later games can easily be identified by the .(a) file instead of a .he1
+ // and INIB chunk in the .he0
+ {"lost", "Pajama Sam's Lost & Found", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_CURSORLESS, Common::kPlatformWindows},
+ {"puttrace", "Putt-Putt Enters the Race", GID_PUTTRACE, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"BluesABCTimeDemo", "Blue's ABC Time (Demo)", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"soccer", "Backyard Soccer", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ // Global scripts increased to 2048
+ {"freddi4", "Freddi Fish 4: The Case of the Hogfish Rustlers of Briny Gulch", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows},
+
+ // Humongous Entertainment Scumm Version 9.9 ? Scummsys.99
+ {"football", "Backyard Football", GID_FOOTBALL, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"pajama3", "Pajama Sam 3: You Are What You Eat From Your Head to Your Feet", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_LOCALIZED, Common::kPlatformWindows},
+ {"puttcircus", "Putt-Putt Joins the Circus", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_LOCALIZED, Common::kPlatformWindows},
+ {"spyfox2", "Spy Fox 2: Some Assembly Required", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_LOCALIZED, Common::kPlatformWindows},
+ {"mustard", "Spy Fox in Hold the Mustard", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_LOCALIZED, Common::kPlatformWindows},
+
+ // Added the use of fonts
+ {"FreddisFunShop", "Freddi Fish's One-Stop Fun Shop", GID_FUNSHOP, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_LOCALIZED, Common::kPlatformWindows},
+ {"SamsFunShop", "Pajama Sam's One-Stop Fun Shop", GID_FUNSHOP, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_LOCALIZED, Common::kPlatformWindows},
+ {"PuttsFunShop", "Putt-Putt's One-Stop Fun Shop", GID_FUNSHOP, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_LOCALIZED, Common::kPlatformWindows},
+
+ // Added 16bit color
+ {"baseball2001", "Backyard Baseball 2001", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_16BIT_COLOR, Common::kPlatformWindows},
+ {"SoccerMLS", "Backyard Soccer MLS Edition", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_LOCALIZED | GF_16BIT_COLOR, Common::kPlatformWindows},
+ {"freddicove", "Freddi Fish 5: The Case of the Creature of Coral Cave", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_LOCALIZED | GF_HE_NOSUBTITLES | GF_16BIT_COLOR, Common::kPlatformWindows},
+ {"spyozon", "Spy Fox 3: Operation Ozone", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_NOSUBTITLES | GF_HE_LOCALIZED | GF_16BIT_COLOR, Common::kPlatformWindows},
+
+ // Restructured the Scumm engine
+ {"pjgames", "Pajama Sam: Games to Play On Any Day", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_NOSUBTITLES | GF_HE_LOCALIZED | GF_16BIT_COLOR, Common::kPlatformWindows},
+
+ // Uses bink in external files for logos
+ {"Baseball2003", "Backyard Baseball 2003", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_16BIT_COLOR, Common::kPlatformWindows},
+ {"basketball", "Backyard Basketball", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"Soccer2004", "Backyard Soccer 2004", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_16BIT_COLOR, Common::kPlatformWindows},
+
+ // Uses smacker in external files, for testing only
+ {"BluesBirthdayDemo", "Blue's Birthday Adventure (Demo)", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"artdemo", "Blue's Art Time Activities (Demo)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_16BIT_COLOR, Common::kPlatformWindows},
+ {"readdemo", "Blue's Reading Time Activities (Demo)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_16BIT_COLOR, Common::kPlatformWindows},
+
+
+#endif
+ {NULL, NULL, 0, 0, 0, MDT_NONE, 0, Common::kPlatformUnknown}
+};
+
+// This additional table is used for MD5-based search
+//
+// Use main table to specify default flags and this table to override defaults.
+//
+// Please, add new entries sorted alpabetically by string name
+static const ScummGameSettings multiple_versions_md5_settings[] = {
+#ifndef PALMOS_68K
+ {"2e85f7aa054930c692a5b1bed1dfc295", "Backyard Football 2002 (Demo Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformUnknown}, // Football2002
+
+ {"037385a953789190298494d92b89b3d0", "Humongous Interactive Catalog (Updated)", GID_HEGAME, 6, 72, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"8fec68383202d38c0d25e9e3b757c5df", "The Curse of Monkey Island (Demo)", GID_CMI, 8, 0, MDT_NONE,
+ GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEFAULT_TO_1X_SCALER | GF_DEMO, Common::kPlatformWindows},
+
+ {"362c1d281fb9899254cda66ad246c66a", "The Dig (Demo)", GID_DIG, 7, 0, MDT_NONE,
+ GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEMO, Common::kPlatformPC},
+ {"cd9c05e755d7bf8e9b9590ad1ebe273e", "The Dig (Demo Mac)", GID_DIG, 7, 0, MDT_NONE,
+ GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEMO, Common::kPlatformMacintosh},
+
+ {"179879b6e35c1ead0d93aab26db0951b", "Fatty Bear's Birthday Surprise (Windows)", GID_FBEAR, 6, 70, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"22c9eb04455440131ffc157aeb8d40a8", "Fatty Bear's Birthday Surprise (Windows Demo)", GID_FBEAR, 6, 70, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"cf8ef3a1fb483c5c4b1c584d1167b2c4", "Freddi Fish 1: The Case of the Missing Kelp Seeds (Updated German)", GID_HEGAME, 6, 73, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"df047cc4792150f601290357566d36a6", "Freddi Fish 1: The Case of the Missing Kelp Seeds (Updated)", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"746e88c172a5b7a1ae89ac0ee3ee681a", "Freddi Fish 1: The Case of the Missing Kelp Seeds (Updated Russian)", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"507bb360688dc4180fdf0d7597352a69", "Freddi Fish 1: The Case of the Missing Kelp Seeds (Updated Swedish)", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"0855496dde35356b1a9691e22ba84cdc", "Freddi Fish 1: The Case of the Missing Kelp Seeds (Demo)", GID_HEGAME, 6, 73, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"566165a7338fa11029e7c14d94fa70d0", "Freddi Fish 1: The Case of the Missing Kelp Seeds (Demo)", GID_HEGAME, 6, 73, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"cf4ef315214c7d8cdab6302cdb7e50db", "Freddi Fish 1: The Case of the Missing Kelp Seeds (German Demo)", GID_HEGAME, 6, 73, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"c8aac5e3e701874e2fa4117896f9e1b1", "Freddi Fish 1: The Case of the Missing Kelp Seeds (Macintosh Demo)", GID_HEGAME, 6, 73, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"8ee63cafb1fe9d62aa0d5a23117e70e7", "Freddi Fish 2: The Case of the Haunted Schoolhouse (Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // FreddiCHSH
+ {"51305e929e330e24a75a0351c8f9975e", "Freddi Fish 2: The Case of the Haunted Schoolhouse (Updated)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"e41de1c2a15abbcdbf9977e2d7e8a340", "Freddi Fish 2: The Case of the Haunted Schoolhouse (Updated Russian)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // FreddiCHSH
+ {"d37c55388294b66e53e7ced3af88fa68", "Freddi Fish 2: The Case of the Haunted Schoolhouse (Demo Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // FFHSDemo
+
+ {"83cedbe26aa8b58988e984e3d34cac8e", "Freddi Fish 3: The Case of the Stolen Conch Shell (Updated German)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"75bff95816b84672b877d22a911ab811", "Freddi Fish 3: The Case of the Stolen Conch Shell (Updated Russian)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"ed2b074bc3166087a747acb2a3c6abb0", "Freddi Fish 3: The Case of the Stolen Conch Shell (Updated German Demo)", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows}, // Fritzi3demo
+
+ {"07b810e37be7489263f7bc7627d4765d", "Freddi Fish 4: The Case of the Hogfish Rustlers of Briny Gulch (Unencrypted Russian)", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows},
+ {"b5298a5c15ffbe8b381d51ea4e26d35c", "Freddi Fish 4: The Case of the Hogfish Rustlers of Briny Gulch (Updated German)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"78bd5f036ea35a878b74e4f47941f784", "Freddi Fish 4: The Case of the Hogfish Rustlers of Briny Gulch (Updated Russian)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"16effd200aa6b8abe9c569c3e578814d", "Freddi Fish 4: The Case of the Hogfish Rustlers of Briny Gulch (Updated Dutch Demo)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // ff4demo
+ {"499c958affc394f2a3868f1eb568c3ee", "Freddi Fish 4: The Case of the Hogfish Rustlers of Briny Gulch (Updated Dutch Demo)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // ff4demo
+ {"ebd324dcf06a4c49e1ba5c231eee1060", "Freddi Fish 4: The Case of the Hogfish Rustlers of Briny Gulch (Updated Demo)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"e03ed1474ec14de78359970e0457a820", "Freddi Fish 4: The Case of the Hogfish Rustlers of Briny Gulch (Updated Demo)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"688328c5bdc4c8ec4145688dfa077bf2", "Freddi Fish 4: The Case of the Hogfish Rustlers of Briny Gulch (German Demo)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // Ff4demo
+
+ {"b8955d7d23b4972229060d1592489fef", "Freddi Fish 5: The Case of the Creature of Coral Cave (Updated Dutch)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_NOSUBTITLES | GF_HE_LOCALIZED | GF_16BIT_COLOR, Common::kPlatformWindows}, // FreddiDZZ
+ {"4ce2d5b355964bbcb5e5ce73236ef868", "Freddi Fish 5: The Case of the Creature of Coral Cave (Updated Russian)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_NOSUBTITLES | GF_HE_LOCALIZED | GF_16BIT_COLOR, Common::kPlatformWindows},
+ {"21abe302e1b1e2b66d6f5c12e241ebfd", "Freddi Fish 5: The Case of the Creature of Coral Cave (Unencrypted Russian)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_NEW_COSTUMES | GF_HE_NOSUBTITLES | GF_HE_LOCALIZED | GF_16BIT_COLOR, Common::kPlatformWindows},
+ {"45082a5c9f42ba14dacfe1fdeeba819d", "Freddi Fish 5: The Case of the Creature of Coral Cave (Updated Demo)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_NOSUBTITLES | GF_HE_LOCALIZED | GF_16BIT_COLOR, Common::kPlatformWindows},
+ {"6b257bb2827dd894b8109a50a1a18b5a", "Freddi Fish 5: The Case of the Creature of Coral Cave (Updated Dutch Demo)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_NOSUBTITLES | GF_HE_LOCALIZED | GF_16BIT_COLOR, Common::kPlatformWindows}, // FF5Demo
+
+ {"4dbff3787aedcd96b0b325f2d92d7ad9", "Freddi Fish and Luther's Maze Madness (Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"f1b0e0d587b85052de5534a3847e68fe", "Freddi Fish and Luther's Water Worries (Updated)", GID_WATER, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"9d7b67be003fea60be4dcbd193611936", "Full Throttle (Mac Demo)", GID_FT, 7, 0, MDT_NONE,
+ GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEMO, Common::kPlatformMacintosh},
+ {"32a433dea56b86a55b59e4ff7d755711", "Full Throttle (PC Demo)", GID_FT, 7, 0, MDT_NONE,
+ GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEMO, Common::kPlatformPC},
+
+ {"157367c3c21e0d03a0cba44361b4cf65", "Indiana Jones and the Last Crusade (AtariST)", GID_INDY3, 3, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformAtariST},
+ {"0f9c7a76657f0840b8f7ccb5bffeb9f4", "Indiana Jones and the Last Crusade (AtariST Fr)", GID_INDY3, 3, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformAtariST},
+ {"1dd7aa088e09f96d06818aa9a9deabe0", "Indiana Jones and the Last Crusade (Macintosh)", GID_INDY3, 3, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformMacintosh},
+
+ {"1875b90fade138c9253a8e967007031a", "Indiana Jones and the Last Crusade (VGA)", GID_INDY3, 3, 0, MDT_PCSPK | MDT_ADLIB,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS, Common::kPlatformPC},
+ {"399b217b0c8d65d0398076da486363a9", "Indiana Jones and the Last Crusade (VGA De)", GID_INDY3, 3, 0, MDT_PCSPK | MDT_ADLIB,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS, Common::kPlatformPC},
+ {"17b5d5e6af4ae89d62631641d66d5a05", "Indiana Jones and the Last Crusade (VGA It)", GID_INDY3, 3, 0, MDT_PCSPK | MDT_ADLIB,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS, Common::kPlatformPC},
+ {"3cce1913a3bc586b51a75c3892ff18dd", "Indiana Jones and the Last Crusade (VGA Ru)", GID_INDY3, 3, 0, MDT_PCSPK | MDT_ADLIB,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS, Common::kPlatformPC},
+
+ {"04687cdf7f975a89d2474929f7b80946", "Indiana Jones and the Last Crusade (FM-TOWNS)", GID_INDY3, 3, 0, MDT_TOWNS,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+ {"3a0c35f3c147b98a2bdf8d400cfc4ab5", "Indiana Jones and the Last Crusade (FM-TOWNS Jp)", GID_INDY3, 3, 0, MDT_TOWNS,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+
+ {"86c9902b7bec1a17926d4dae85beaa45", "Let's Explore the Airport with Buzzy (Demo)", GID_HEGAME, 6, 71, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"bf8b52fdd9a69c67f34e8e9fec72661c", "Let's Explore the Farm with Buzzy (Demo)", GID_HEGAME, 6, 71, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"8d479e36f35e80257dfc102cf4b8a912", "Let's Explore the Farm with Buzzy (Updated Demo)", GID_HEGAME, 6, 72, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"0557df19f046a84c2fdc63507c6616cb", "Let's Explore the Farm with Buzzy (Updated Dutch Demo)", GID_HEGAME, 6, 72, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"a0a7dea72003933b8b3f8b99b9f7ddeb", "Loom (AtariST)", GID_LOOM, 3, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformAtariST},
+ {"c24c490373aeb48fbd54caa8e7ae376d", "Loom (AtariST De)", GID_LOOM, 3, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformAtariST},
+ {"0e9b01430e31d9fcd94071d433bbc6bf", "Loom (AtariST Fr)", GID_LOOM, 3, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformAtariST},
+ {"6f0be328c64d689bb606d22a389e1b0f", "Loom (Macintosh)", GID_LOOM, 3, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_16COLOR | GF_USE_KEY | GF_OLD_BUNDLE, Common::kPlatformMacintosh},
+
+ {"5d88b9d6a88e6f8e90cded9d01b7f082", "Loom (256 color CD version)", GID_LOOM, 4, 0, MDT_NONE,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformPC},
+ {"c5d10e190d4b4d59114b824f2fdbd00e", "Loom (FM-TOWNS)", GID_LOOM, 3, 0, MDT_TOWNS,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+ {"31b8fda4c8c7413fa6b39997e776eba4", "Loom (FM-TOWNS Jp)", GID_LOOM, 3, 0, MDT_TOWNS,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+
+ {"3905799e081b80a61d4460b7b733c206", "Maniac Mansion (NES E)", GID_MANIAC, 1, 0, MDT_NONE,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, Common::kPlatformNES},
+ {"81bbfa181184cb494e7a81dcfa94fbd9", "Maniac Mansion (NES F)", GID_MANIAC, 1, 0, MDT_NONE,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, Common::kPlatformNES},
+ {"257f8c14d8c584f7ddd601bcb00920c7", "Maniac Mansion (NES G)", GID_MANIAC, 1, 0, MDT_NONE,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, Common::kPlatformNES},
+ {"22d07d6c386c9c25aca5dac2a0c0d94b", "Maniac Mansion (NES SW)", GID_MANIAC, 1, 0, MDT_NONE,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, Common::kPlatformNES},
+ {"d8d07efcb88f396bee0b402b10c3b1c9", "Maniac Mansion (NES U)", GID_MANIAC, 1, 0, MDT_NONE,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, Common::kPlatformNES},
+ {"7f45ddd6dbfbf8f80c0c0efea4c295bc", "Maniac Mansion (v1)", GID_MANIAC, 1, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, Common::kPlatformPC},
+
+ {"898eaa21f79cf8d4f08db856244689ff", "Pajama Sam: No Need To Hide When It's Dark Outside (Updated)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"a095e33061606d231ff37dca4c64c8ac", "Pajama Sam: No Need To Hide When It's Dark Outside (Updated German)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // PYJAMA
+ {"37aed3f91c1ef959e0bd265f9b13781f", "Pajama Sam: No Need To Hide When It's Dark Outside (Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // PajamaNHD
+ {"d7ab7cd6105546016e6a0d46fb36b964", "Pajama Sam: No Need To Hide When It's Dark Outside (Updated Demo)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // PJSamDemo
+
+ {"30ba1e825d4ad2b448143ae8df18482a", "Pajama Sam 2: Thunder and Lightning Aren't so Frightening (Updated Dutch Demo)", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows}, // Pjs2demo
+ {"32709cbeeb3044b34129950860a83f14", "Pajama Sam 2: Thunder and Lightning Aren't so Frightening (Updated Russian)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // PajamaTAL
+ {"c6907d44f1166941d982864cd42cdc89", "Pajama Sam 2: Thunder and Lightning Aren't so Frightening (Updated German)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // PyjamaDBMN
+ {"4521138d15d1fd7649c31fb981746231", "Pajama Sam 2: Thunder and Lightning Aren't so Frightening (Updated German Demo)", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows}, // PJP2DEMO
+
+ {"a2bb6aa0537402c1b3c2ea899ccef64b", "Pajama Sam's Lost & Found (Test)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_CURSORLESS, Common::kPlatformWindows},
+ {"a561d2e2413cc1c71d5a1bf87bf493ea", "Pajama Sam's Lost & Found (Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_CURSORLESS, Common::kPlatformWindows},
+
+ {"055ffe4f47753e47594ac67823220c54", "Putt-Putt Enters the Race (German)", GID_PUTTRACE, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows}, // ToffRennen
+ {"6af2419fe3db5c2fdb091ae4e5833770", "Putt-Putt Enters the Race (Dutch Demo)", GID_PUTTRACE, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows}, // 500demo
+ {"aaa587701cde7e74692c68c1024b85eb", "Putt-Putt Enters the Race (Updated Dutch Demo)", GID_PUTTRACE, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows},
+ {"663743c03ae0c007f3d665cf631c0e6b", "Putt-Putt Enters the Race (German Demo)", GID_PUTTRACE, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows}, // Rennen
+ {"7c8100e360e8ef05f88069d4cfa0afd1", "Putt-Putt Enters the Race (UK Demo)", GID_PUTTRACE, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows},
+
+ {"9c92eeaf517a31b7221ec2546ab669fd", "Putt-Putt Goes To The Moon (Windows)", GID_HEGAME, 6, 70, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"9c143c5905055d5df7a0f014ab379aee", "Putt-Putt Goes To The Moon (Windows Demo)", GID_HEGAME, 6, 70, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"0b3222aaa7efcf283eb621e0cefd26cc", "Putt-Putt Joins the Parade (Russian)", GID_HEGAME, 6, 60, MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY, Common::kPlatformWindows},
+ {"31aa57f460a3d12429f0552a46a90b39", "Putt-Putt Joins the Parade (Demo)", GID_PUTTDEMO, 6, 60, MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY, Common::kPlatformPC},
+ {"f40a7f495f59188ca57a9d1d50301bb6", "Putt-Putt Joins the Parade (Macintosh Demo)", GID_PUTTDEMO, 6, 60, MDT_ADLIB | MDT_MIDI,
+ GF_USE_KEY, Common::kPlatformPC},
+ {"6a30a07f353a75cdc602db27d73e1b42", "Putt-Putt Joins the Parade (Windows)", GID_HEGAME, 6, 70, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"37ff1b308999c4cca7319edfcc1280a0", "Putt-Putt Joins the Parade (Windows Demo)", GID_HEGAME, 6, 70, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"92e7727e67f5cd979d8a1070e4eb8cb3", "Putt-Putt Saves the Zoo (Updated)", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows},
+
+ {"2108d83dcf09f8adb4bc524669c8cf51", "Putt-Putt Travels Through Time (Updated)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"d4aac997e2f4e15341f0bfbf905419bd", "Putt-Putt Travels Through Time (Updated)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"7c980a1b1596a93f26917318884f48f7", "Putt-Putt Travels Through Time (Updated German)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"defb8cb9ec4b0f91acfb6b61c6129ad9", "Putt-Putt Travels Through Time (Updated Russian)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"a525c1753c1db5011c00417da37887ef", "Putt-Putt Travels Through Time (Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"fcb78ebecab2757264c590890c319cc5", "Putt-Putt Travels Through Time (Updated Dutch)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"4e5867848ee61bc30d157e2c94eee9b4", "Putt-Putt Travels Through Time (Demo)", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"6b19d0e25cbf720d05822379b8b90ed9", "Putt-Putt Travels Through Time (Dutch Demo)", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"59d5cfcc5e672a6e07baae01328b918b", "Putt-Putt Travels Through Time (French Demo)", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // TEMPDEMO
+ {"fbb697d89d2beca87360a145f467bdae", "Putt-Putt Travels Through Time (Updated German Demo)", GID_HEGAME, 6, 90, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // ZEITDEMO
+ {"0ab19be9e2a3f6938226638b2a3744fe", "Putt-Putt Travels Through Time (Updated Demo)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"d7b247c26bf1f01f8f7daf142be84de3", "Putt-Putt and Pep's Balloon-O-Rama (Updated)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"8e3241ddd6c8dadf64305e8740d45e13", "Putt-Putt and Pep's Balloon-O-Rama (Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"d4b8ee426b1afd3e53bc0cf020418cf6", "Putt-Putt and Pep's Dog on a Stick (Updated)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"1d05cd189e4908f79b57e78a4402f292", "The Secret of Monkey Island (EGA)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"49210e124e4c2b30f1290a9ef6306301", "The Secret of Monkey Island (EGA)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"e98b982ceaf9d253d730bde8903233d6", "The Secret of Monkey Island (EGA De)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"fc6b6148e80d67939d9a18697c0f626a", "The Secret of Monkey Island (EGA De)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"ce6a4cef315b20fef58a95bc40a2d8d3", "The Secret of Monkey Island (EGA Fr)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"aa7a07d94ae853f6460be4ce0a1bf530", "The Secret of Monkey Island (EGA Fr)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"1dd3c11ea4439adfe681e4e405b624e1", "The Secret of Monkey Island (EGA Fr)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"477dbafbd66a53c98416dc01aef019ad", "The Secret of Monkey Island (EGA It)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"910e31cffb28226bd68c569668a0d6b4", "The Secret of Monkey Island (EGA Sp)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"c666a998af90d81db447eccba9f72c8d", "The Secret of Monkey Island (Atari)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformAtariST},
+ {"927a764615c7fcdd72f591355e089d8c", "The Secret of Monkey Island (Atari De)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformAtariST},
+ {"9e5e0fb43bd22f4628719b7501adb717", "The Secret of Monkey Island (Atari Fr)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformAtariST},
+ {"0a41311d462b6639fc45297b9044bf16", "The Secret of Monkey Island (Atari Sp)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformAtariST},
+ {"71523b539491527d9860f4407faf0411", "The Secret of Monkey Island (Demo)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+ {"771bc18ec6f93837b839c992b211904b", "The Secret of Monkey Island (Demo De)", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR, Common::kPlatformPC},
+
+ {"2d1e891fe52df707c30185e52c50cd92", "The Secret of Monkey Island (CD)", GID_MONKEY, 5, 0, /*MDT_PCSPK |*/ MDT_ADLIB,
+ GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformPC},
+ {"305d3dd57c96c65b017bc70c8c7cfb5e", "The Secret of Monkey Island (CD De)", GID_MONKEY, 5, 0, /*MDT_PCSPK |*/ MDT_ADLIB,
+ GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformPC},
+ {"f049e38c1f8302b5db6170f1872af89a", "The Secret of Monkey Island (CD Sp)", GID_MONKEY, 5, 0, /*MDT_PCSPK |*/ MDT_ADLIB,
+ GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformPC},
+ {"da6269b18fcb08189c0aa9c95533cce2", "The Secret of Monkey Island (CD It)", GID_MONKEY, 5, 0, /*MDT_PCSPK |*/ MDT_ADLIB,
+ GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformPC},
+ {"aa8a0cb65f3afbbe2c14c3f9f92775a3", "The Secret of Monkey Island (CD Fr)", GID_MONKEY, 5, 0, /*MDT_PCSPK |*/ MDT_ADLIB,
+ GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformPC},
+ {"2ccd8891ce4d3f1a334d21bff6a88ca2", "The Secret of Monkey Island (Mac CD)", GID_MONKEY, 5, 0, /*MDT_PCSPK |*/ MDT_ADLIB,
+ GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformMacintosh},
+
+ {"8eb84cee9b429314c7f0bdcf560723eb", "The Secret of Monkey Island (FM-TOWNS)", GID_MONKEY, 5, 0, /*MDT_PCSPK |*/ MDT_ADLIB,
+ GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+
+ {"e17db1ddf91b39ca6bbc8ad3ed19e883", "The Secret of Monkey Island (FM-TOWNS Jp)", GID_MONKEY, 5, 0, /*MDT_PCSPK |*/ MDT_ADLIB,
+ GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+
+ {"c13225cb1bbd3bc9fe578301696d8021", "The Secret of Monkey Island (SegaCD)", GID_MONKEY, 5, 0, MDT_NONE,
+ GF_USE_KEY | GF_AUDIOTRACKS, Common::kPlatformSegaCD},
+
+ {"3de99ef0523f8ca7958faa3afccd035a", "Spy Fox 1: Dry Cereal (Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"58436e634f4fae1d9973591c2ffa1fcb", "Spy Fox 1: Dry Cereal (Updated)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"9bda5fee51d2fda5253d02c642016bf4", "Spy Fox 1: Dry Cereal (Updated Dutch)", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows},
+ {"a28135a7ade38cc0208b04507c46efd1", "Spy Fox 1: Dry Cereal (Updated German)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"72ac6bc980d5101c2142189d746bd62f", "Spy Fox 1: Dry Cereal (Updated Russian)", GID_HEGAME, 6, 99, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows}, // SPYFoxDC
+ {"9d4ab3e0e1d1ebc6ba8a6a4c470ed184", "Spy Fox 1: Dry Cereal (Updated Demo)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+ {"4edbf9d03550f7ba01e7f34d69b678dd", "Spy Fox 1: Dry Cereal (Updated Dutch Demo)", GID_HEGAME, 6, 98, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES | GF_HE_985, Common::kPlatformWindows},
+
+ {"90c755e1c9b9b8a4129d37b2259d0655", "Spy Fox in Cheese Chase (Updated)", GID_HEGAME, 6, 100, MDT_NONE,
+ GF_USE_KEY | GF_NEW_COSTUMES, Common::kPlatformWindows},
+
+ {"b23f7cd7c304d7dff08e92a96120d5b4", "Zak McKracken and the Alien Mindbenders (v1)", GID_ZAK, 1, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, Common::kPlatformPC},
+ {"7020931d5a2be0a49d68e7a1882363e4", "Zak McKracken and the Alien Mindbenders (v1)", GID_ZAK, 1, 0, MDT_PCSPK,
+ GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALING, Common::kPlatformPC},
+
+ {"2d4536a56e01da4b02eb021e7770afa2", "Zak McKracken and the Alien Mindbenders (FM-TOWNS)", GID_ZAK, 3, 0, MDT_TOWNS,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+ {"1ca86e2cf9aaa2068738a1e5ba477e60", "Zak McKracken and the Alien Mindbenders (FM-TOWNS Jp)", GID_ZAK, 3, 0, MDT_TOWNS,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+
+ {"2d388339d6050d8ccaa757b64633954e", "Indy/Loom Demo (FM-TOWNS)", GID_ZAK, 3, 0, MDT_TOWNS,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+ {"77f5c9cc0986eb729c1a6b4c8823bbae", "Zak/Loom Demo (FM-TOWNS)", GID_ZAK, 3, 0, MDT_TOWNS,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+ {"3938ee1aa4433fca9d9308c9891172b1", "Indy/Zak Demo (FM-TOWNS)", GID_ZAK, 3, 0, MDT_TOWNS,
+ GF_SMALL_HEADER | GF_NO_SCALING | GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns},
+#endif
+ {NULL, NULL, 0, 0, MDT_NONE, 0, 0, Common::kPlatformUnknown}
+};
+
+enum genMethods {
+ kGenMac,
+ kGenMacNoParens,
+ kGenPC,
+ kGenAsIs
+};
+
+struct SubstResFileNames {
+ const char *winName;
+ const char *macName;
+ int genMethod;
+};
+
+static SubstResFileNames substResFileNameTable[] = {
+ { "Intentionally/left/blank", "", kGenMacNoParens },
+ { "00.LFL", "Maniac Mansion (E).prg", kGenAsIs },
+ { "00.LFL", "Maniac Mansion (F).prg", kGenAsIs },
+ { "00.LFL", "Maniac Mansion (SW).prg", kGenAsIs },
+ { "00.LFL", "Maniac Mansion (U).prg", kGenAsIs },
+ { "00.LFL", "Maniac Mansion (G).prg", kGenAsIs },
+ { "00.LFL", "maniac1.d64", kGenAsIs }, // Do not
+ { "01.LFL", "maniac2.d64", kGenAsIs }, // swap
+ { "00.LFL", "zak1.d64", kGenAsIs }, // these
+ { "01.LFL", "zak2.d64", kGenAsIs }, // lines
+ { "atlantis.000", "Fate of Atlantis Data", kGenAsIs },
+ { "atlantis", "fate", kGenPC },
+ { "atlantis", "playfate", kGenPC },
+ { "atlantis", "indy4", kGenPC },
+ { "atlantis", "indydemo", kGenPC },
+ { "tentacle.000", "Day of the Tentacle Data", kGenAsIs },
+ { "tentacle", "dottdemo", kGenPC },
+ { "tentacle.000", "Day of the Tentacle Demo Data", kGenAsIs },
+ { "monkey", "monkey1", kGenPC },
+ { "monkey", "monkeyk", kGenPC }, // FM-TOWNS Jap
+ { "monkey", "game", kGenPC }, // SegaCD
+ { "monkey2", "mi2demo", kGenPC },
+ { "samnmax.000", "Sam & Max Data", kGenAsIs },
+ { "samnmax.000", "Sam & Max Demo Data", kGenAsIs },
+ { "samnmax", "ramnmax", kGenPC }, // Used in some releases of Russian Sam'n'Max
+ { "samnmax", "samdemo", kGenPC },
+ { "samnmax", "snmdemo", kGenPC },
+ { "samnmax", "snmidemo", kGenPC },
+ { "samnmax", "sdemo", kGenPC },
+#ifndef DISABLE_SCUMM_7_8
+ { "dig.la0", "The Dig Data", kGenAsIs },
+ { "dig.la0", "The Dig Demo Data", kGenAsIs },
+ { "ft.la0", "Full Throttle Data", kGenAsIs },
+ { "ft.la0", "Full Throttle Demo Data", kGenAsIs },
+ { "ft.la0", "Vollgas Data", kGenAsIs },
+ { "ft.la0", "Vollgas Demo Data", kGenAsIs },
+ { "ft", "ftdemo", kGenPC },
+#endif
+ { "fbear", "fbdemo", kGenPC },
+ { "fbear", "Fatty Bear Demo", kGenMacNoParens },
+ { "fbear", "Fatty Bear", kGenMacNoParens },
+ { "puttmoon", "moondemo", kGenPC },
+ { "puttmoon", "Putt-Putt Moon Demo", kGenMacNoParens },
+ { "puttmoon", "Putt-Putt Moon", kGenMacNoParens },
+ { "puttputt", "puttdemo", kGenPC },
+ { "puttputt", "Putt-Putt's Demo", kGenMacNoParens },
+ { "puttputt", "Putt-Putt Parade", kGenMacNoParens },
+#ifndef DISABLE_HE
+ { "airport", "airdemo", kGenPC },
+ { "airport", "Airport Demo", kGenMac },
+ { "airport", "The AirPort", kGenMac },
+ { "balloon", "Balloon-O-Rama", kGenMac },
+ { "baseball", "BaseBall", kGenMac },
+ { "baseball2001", "bb2demo", kGenPC },
+ { "baseball2001", "Baseball 2001 Demo", kGenMac },
+ { "baseball2001", "Baseball 2001", kGenMac },
+ { "baseball2001", "baseball 2001", kGenPC },
+ { "Baseball2003", "Baseball 2003", kGenMac },
+ { "basketball", "Basketball", kGenMac },
+ { "BluesABCTimeDemo", "BluesABCTimeDemo", kGenMac },
+ { "catalog", "catalog2", kGenPC },
+ { "chase", "Cheese Chase", kGenMac },
+ { "dog", "Dog on a Stick", kGenMac },
+ { "farm", "farmdemo", kGenPC },
+ { "farm", "Farm Demo", kGenMac },
+ { "football", "FootBall", kGenMac },
+ { "football", "FootBall Demo", kGenMac },
+ { "football", "FootBall2002", kGenPC },
+ { "football", "footdemo", kGenPC },
+ { "freddi", "freddemo", kGenPC },
+ { "freddi", "Freddi Demo", kGenMac },
+ { "freddi", "Freddi Fish", kGenMac },
+ { "freddi", "FreddiD", kGenPC },
+ { "freddi2", "ff2-demo", kGenPC },
+ { "freddi2", "FFHSDemo", kGenMac },
+ { "freddi2", "FFHSDemo", kGenPC },
+ { "freddi2", "Freddi Fish 2 Demo", kGenMac },
+ { "freddi2", "Freddi Fish 2", kGenMac },
+ { "freddi2", "FreddiCHSH", kGenPC },
+ { "freddi2", "Fritzi Fisch 2", kGenMac },
+ { "freddi3", "F3-mdemo", kGenMac },
+ { "freddi3", "F3-Mdemo", kGenMac },
+ { "freddi3", "f3-mdemo", kGenPC },
+ { "freddi3", "FF3-DEMO", kGenPC },
+ { "freddi3", "Freddi Fish 3", kGenMac },
+ { "freddi3", "FreddiFGT", kGenPC },
+ { "freddi3", "FreddiFGT", kGenMac },
+ { "freddi3", "FreddiSCS", kGenPC },
+ { "freddi3", "Fritzi3demo", kGenMac },
+ { "freddi3", "Fritzi3demo", kGenPC },
+ { "freddi3", "MM3-DEMO", kGenPC },
+ { "freddi3", "MM3-Demo", kGenMac }, // FR Mac demo
+ { "freddi4", "f4-demo", kGenPC },
+ { "freddi4", "ff4demo", kGenPC },
+ { "freddi4", "Ff4demo", kGenMac },
+ { "freddi4", "Freddi 4", kGenMac },
+ { "freddi4", "Freddi 4 Demo", kGenMac },
+ { "freddi4", "FreddiGS", kGenPC },
+ { "freddi4", "FreddiGS", kGenMac },
+ { "freddi4", "FreddiHRBG", kGenPC },
+ { "freddicove", "FreddiCCC", kGenPC },
+ { "freddicove", "FreddiCove", kGenMac },
+ { "freddicove", "FreddiDZZ", kGenPC },
+ { "freddicove", "ff5demo", kGenPC },
+ { "freddicove", "FFCoveDemo", kGenPC },
+ { "freddicove", "FreddiCoveDemo", kGenMac },
+ { "freddicove", "FF5Demo", kGenMac }, // NL Mac demo
+ { "FreddisFunShop", "Freddi's FunShop", kGenMac },
+ { "jungle", "The Jungle", kGenMac },
+ { "lost", "Lost and Found", kGenMac },
+ { "lost", "smaller", kGenPC },
+ { "maze", "Maze Madness", kGenMac},
+ { "mustard", "Mustard", kGenMac },
+ { "pajama", "Pyjama Pit", kGenMac },
+ { "pajama", "Pajama Sam", kGenMac },
+ { "pajama", "PajamaNHD", kGenPC },
+ { "pajama", "PJS-DEMO", kGenPC },
+ { "pajama", "PYJAMA", kGenPC },
+ { "pajama", "SAMDEMO", kGenPC },
+ { "pajama", "SAMDEMO", kGenMac }, // FR Mac demo
+ { "pajama2", "Pajama Sam 2", kGenMac },
+ { "pajama2", "PajamaTAL", kGenPC },
+ { "pajama2", "PyjamaDBMN", kGenPC },
+ { "pajama2", "PyjamaDBMN", kGenMac },
+ { "pajama2", "Pyjama Pit 2 Demo", kGenMac },
+ { "pajama2", "PJP2DEMO", kGenPC },
+ { "pajama2", "PJ2Demo", kGenMac },
+ { "pajama2", "pj2demo", kGenPC },
+ { "pajama2", "Pjs2demo", kGenPC },
+ { "pajama2", "PJ2 Demo", kGenMac }, // NL Mac demo
+ { "pajama3", "GPJ3Demo", kGenPC },
+ { "pajama3", "Pajama Sam 3", kGenMac },
+ { "pajama3", "Pajama Sam 3-Demo", kGenMac },
+ { "pajama3", "pj3-demo", kGenPC },
+ { "pajama3", "pj3demo", kGenPC },
+ { "pajama3", "PJ3Demo", kGenMac },
+ { "pajama3", "Pajama Sam Demo", kGenMac },
+ { "pajama3", "PjSamDemo", kGenMac },
+ { "pajama3", "PjSamDemo", kGenPC },
+ { "pajama3", "PyjamaSKS", kGenPC },
+ { "pajama3", "PyjamaSKS", kGenMac },
+ { "pjgames", "PJGames", kGenMac },
+ { "puttcircus", "circdemo", kGenPC },
+ { "puttcircus", "Putt Circus Demo", kGenMac },
+ { "puttcircus", "Putt Circus", kGenMac },
+ { "puttrace", "500demo", kGenPC },
+ { "puttrace", "racedemo", kGenPC },
+ { "puttrace", "RaceDemo", kGenMac },
+ { "puttrace", "Rennen", kGenPC },
+ { "puttrace", "Putt500 demo", kGenMac }, // NL Mac demo
+ { "puttrace", "Putt Race", kGenMac },
+ { "puttrace", "ToffRennen", kGenPC },
+ { "puttrace", "ToffRennen", kGenMac },
+ { "PuttsFunShop", "Putt's FunShop", kGenMac },
+ { "putttime", "PuttPuttTTT", kGenPC },
+ { "putttime", "PuttPuttTTT", kGenMac },
+ { "putttime", "PuttTijd", kGenPC },
+ { "putttime", "Putt Time", kGenMac },
+ { "putttime", "PuttTTT", kGenMac },
+ { "putttime", "PuttTTT", kGenPC },
+ { "putttime", "TIJDDEMO", kGenPC },
+ { "putttime", "timedemo", kGenPC },
+ { "putttime", "TimeDemo", kGenMac },
+ { "putttime", "TEMPDEMO", kGenPC },
+ { "putttime", "Tempdemo", kGenMac }, // FR Mac demo
+ { "putttime", "toffzeit", kGenPC }, // German T’¨¢öff-T’¨¢öff: Reist durch die Zeit
+ { "putttime", "toffzeit", kGenMac }, // German T’¨¢öff-T’¨¢öff: Reist durch die Zeit
+ { "putttime", "ZeitDemo", kGenMac },
+ { "putttime", "ZEITDEMO", kGenPC },
+ { "puttzoo", "Puttzoo Demo", kGenMac },
+ { "puttzoo", "PuttZoo", kGenMac },
+ { "puttzoo", "zoodemo", kGenPC },
+ { "puttzoo", "Zoo Demo", kGenMac },
+ { "SamsFunShop", "Sam's FunShop", kGenMac },
+ { "soccer", "Soccer", kGenMac },
+ { "Soccer2004", "Soccer 2004", kGenMac },
+ { "socks", "SockWorks", kGenMac },
+ { "spyfox", "Fuchsdem", kGenMac },
+ { "spyfox", "FUCHSDEM", kGenPC},
+ { "spyfox", "FoxDemo", kGenMac },
+ { "spyfox", "foxdemo", kGenPC},
+ { "spyfox", "JAMESDEM", kGenPC },
+ { "spyfox", "Spydemo", kGenMac},
+ { "spyfox", "Spydemo", kGenPC},
+ { "spyfox", "SPYFox", kGenMac },
+ { "spyfox", "SPYFoxDC", kGenPC },
+ { "spyfox", "SPYFoxDC", kGenMac },
+ { "spyfox", "SpyFoxDMK", kGenPC },
+ { "spyfox", "SpyFoxDMK", kGenMac },
+ { "spyfox", "Spy Fox Demo", kGenMac }, // NL Mac demo
+ { "spyfox", "JR-Demo", kGenMac }, // FR Mac demo
+ { "spyfox2", "sf2-demo", kGenPC },
+ { "spyfox2", "sf2demo", kGenPC },
+ { "spyfox2", "Sf2demo", kGenMac },
+ { "spyfox2", "Spy Fox 2 - Demo", kGenMac },
+ { "spyfox2", "Spy Fox 2", kGenMac },
+ { "spyfox2", "SpyFoxOR", kGenPC },
+ { "spyfox2", "SpyFoxOR", kGenMac },
+ { "spyfox2", "spyfoxsr", kGenPC },
+ { "spyozon", "sf3-demo", kGenPC },
+ { "spyozon", "Spy Ozone Demo", kGenMac },
+ { "spyozon", "SPYFoxOZU", kGenPC },
+ { "spyozon", "SpyOzon", kGenMac },
+ { "thinker1", "1grademo", kGenPC },
+ { "thinker1", "Thinker1", kGenMac },
+ { "thinkerk", "kinddemo", kGenPC },
+ { "thinkerk", "KindDemo", kGenMac },
+ { "thinkerk", "ThinkerK", kGenMac },
+ { "water", "Water Worries", kGenMac },
+#endif
+ { NULL, NULL, 0 }
+};
+
+static int compareMD5Table(const void *a, const void *b) {
+ const char *key = (const char *)a;
+ const MD5Table *elem = (const MD5Table *)b;
+ return strcmp(key, elem->md5);
+}
+
+ScummEngine::ScummEngine(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : Engine(syst),
+ _gameId(gs.id),
+ _version(gs.version),
+ _heversion(gs.heversion),
+ _features(gs.features),
+ _platform(gs.platform),
+ _midi(gs.midi),
+ _substResFileNameIndex(substResFileNameIndex),
+ _substResFileNameIndexBundle(0),
+ _debugger(0),
+ _currentScript(0xFF), // Let debug() work on init stage
+ gdi(this),
+ res(this),
+ _pauseDialog(0), _mainMenuDialog(0), _versionDialog(0),
+ _targetName(detector->_targetName) {
+
+ // Copy MD5 checksum
+ memcpy(_gameMD5, md5sum, 16);
+
+ // Check for unknown MD5
+ char md5str[32+1];
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+ const MD5Table *elem;
+#ifdef PALMOS_68K
+ uint32 arraySize = MemPtrSize((void *)md5table) / sizeof(MD5Table) - 1;
+#else
+ uint32 arraySize = ARRAYSIZE(md5table) - 1;
+#endif
+ elem = (const MD5Table *)bsearch(md5str, md5table, arraySize, sizeof(MD5Table), compareMD5Table);
+ if (!elem)
+ printf("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team\n", md5str);
+
+ if (_gameId == GID_FT) {
+ // WORKAROUND for bug #1407789. See checkAndRunSentenceScript()
+ // for the actual workaround.
+
+ // FIXME: We do not yet have all necessary information, but the
+ // following is known:
+ //
+ // * The US PC version uses scripts 28 and 103.
+ // * The French PC version uses scripts 29 and 104.
+ // * The German, Italian, Portuguese and Spanish PC versions
+ // use script 29. The other script is not currently known.
+ // * The US Mac demo uses script 28.
+ //
+ // For now, assume that the PC and Mac versions are the same,
+ // that all localized versions use scripts 29 and 104, and that
+ // any completely unknown version is localized.
+
+ if (elem && elem->language == Common::EN_USA) {
+ _defaultFTSentenceScript = 28;
+ _buggyFTSentenceScript = 103;
+ } else {
+ _defaultFTSentenceScript = 29;
+ _buggyFTSentenceScript = 104;
+ }
+ }
+
+ // Add default file directories.
+ if (((_platform == Common::kPlatformAmiga) || (_platform == Common::kPlatformAtariST)) && (_version <= 4)) {
+ // This is for the Amiga version of Indy3/Loom/Maniac/Zak
+ File::addDefaultDirectory(_gameDataPath + "ROOMS/");
+ File::addDefaultDirectory(_gameDataPath + "rooms/");
+ }
+
+ if ((_platform == Common::kPlatformMacintosh) && (_version == 3)) {
+ // This is for the Mac version of Indy3/Loom
+ File::addDefaultDirectory(_gameDataPath + "Rooms 1/");
+ File::addDefaultDirectory(_gameDataPath + "Rooms 2/");
+ File::addDefaultDirectory(_gameDataPath + "Rooms 3/");
+ }
+
+#ifndef DISABLE_SCUMM_7_8
+#ifdef MACOSX
+ if (_version == 8 && !memcmp(_gameDataPath.c_str(), "/Volumes/MONKEY3_", 17)) {
+ // Special case for COMI on Mac OS X. The mount points on OS X depend
+ // on the volume name. Hence if playing from CD, we'd get a problem.
+ // So if loading of a resource file fails, we fall back to the (fixed)
+ // CD mount points (/Volumes/MONKEY3_1 and /Volumes/MONKEY3_2).
+ //
+ // This check for whether we play from CD is very crude, though.
+
+ File::addDefaultDirectory("/Volumes/MONKEY3_1/RESOURCE/");
+ File::addDefaultDirectory("/Volumes/MONKEY3_1/resource/");
+ File::addDefaultDirectory("/Volumes/MONKEY3_2/");
+ File::addDefaultDirectory("/Volumes/MONKEY3_2/RESOURCE/");
+ File::addDefaultDirectory("/Volumes/MONKEY3_2/resource/");
+ } else
+#endif
+ if (_version == 8) {
+ // This is for COMI
+ File::addDefaultDirectory(_gameDataPath + "RESOURCE/");
+ File::addDefaultDirectory(_gameDataPath + "resource/");
+ }
+
+ if (_version == 7) {
+ // This is for Full Throttle & The Dig
+ File::addDefaultDirectory(_gameDataPath + "VIDEO/");
+ File::addDefaultDirectory(_gameDataPath + "video/");
+ File::addDefaultDirectory(_gameDataPath + "DATA/");
+ File::addDefaultDirectory(_gameDataPath + "data/");
+ }
+#endif
+
+ // We read data directly from NES ROM instead of extracting it with
+ // external tool
+ if ((_platform == Common::kPlatformNES) && _substResFileNameIndex) {
+ char tmpBuf[128];
+ generateSubstResFileName("00.LFL", tmpBuf, sizeof(tmpBuf));
+ _fileHandle = new ScummNESFile();
+ _containerFile = tmpBuf;
+ } else if ((_platform == Common::kPlatformC64) && _substResFileNameIndex) {
+ char tmpBuf1[128], tmpBuf2[128];
+ generateSubstResFileName("00.LFL", tmpBuf1, sizeof(tmpBuf1));
+ generateSubstResFileName("01.LFL", tmpBuf2, sizeof(tmpBuf2));
+
+ _fileHandle = new ScummC64File(tmpBuf1, tmpBuf2, _gameId == GID_MANIAC);
+
+ _containerFile = tmpBuf1;
+ } else
+ _fileHandle = new ScummFile();
+
+ // The mac versions of Indy4, Sam&Max, DOTT, FT and The Dig used a
+ // special meta (container) file format to store the actual SCUMM data
+ // files. The rescumm utility used to be used to extract those files.
+ // While that is still possible, we now support reading those files
+ // directly. The first step is to check whether one of them is present
+ // (we do that here); the rest is handled by the ScummFile class and
+ // code in openResourceFile() (and in the Sound class, for MONSTER.SOU
+ // handling).
+ if (_version >= 5 && _heversion == 0 && _substResFileNameIndex &&
+ _platform == Common::kPlatformMacintosh &&
+ substResFileNameTable[_substResFileNameIndex].genMethod == kGenAsIs) {
+ if (_fileHandle->open(substResFileNameTable[_substResFileNameIndex].macName)) {
+ _containerFile = substResFileNameTable[_substResFileNameIndex].macName;
+ _substResFileNameIndex = 0;
+ }
+ }
+
+ // Init all vars
+ _imuse = NULL;
+ _imuseDigital = NULL;
+ _musicEngine = NULL;
+ _verbs = NULL;
+ _objs = NULL;
+ _storedFlObjects = NULL;
+ _debugFlags = 0;
+ _sound = NULL;
+ memset(&vm, 0, sizeof(vm));
+ _smushVideoShouldFinish = false;
+ _smushPaused = false;
+ _insaneRunning = false;
+ _quit = false;
+ _pauseDialog = NULL;
+ _mainMenuDialog = NULL;
+ _versionDialog = NULL;
+ _fastMode = 0;
+ _actors = NULL;
+ _arraySlot = NULL;
+ _inventory = NULL;
+ _newNames = NULL;
+ _scummVars = NULL;
+ _roomVars = NULL;
+ _varwatch = 0;
+ _bitVars = NULL;
+ _numVariables = 0;
+ _numBitVariables = 0;
+ _numRoomVariables = 0;
+ _numLocalObjects = 0;
+ _numGlobalObjects = 0;
+ _numStoredFlObjects = 0;
+ _numArray = 0;
+ _numVerbs = 0;
+ _numFlObject = 0;
+ _numInventory = 0;
+ _numRooms = 0;
+ _numScripts = 0;
+ _numSounds = 0;
+ _numCharsets = 0;
+ _numNewNames = 0;
+ _numGlobalScripts = 0;
+ _numCostumes = 0;
+ _numImages = 0;
+ _numLocalScripts = 60;
+ _numSprites = 0;
+ _numTalkies = 0;
+ _numPalettes = 0;
+ _numUnk = 0;
+ _curActor = 0;
+ _curVerb = 0;
+ _curVerbSlot = 0;
+ _curPalIndex = 0;
+ _currentRoom = 0;
+ _egoPositioned = false;
+ _keyPressed = 0;
+ _lastKeyHit = 0;
+ _mouseAndKeyboardStat = 0;
+ _leftBtnPressed = 0;
+ _rightBtnPressed = 0;
+ _bootParam = 0;
+ _dumpScripts = false;
+ _debugMode = 0;
+ _heV7DiskOffsets = NULL;
+ _heV7RoomIntOffsets = NULL;
+ _objectOwnerTable = NULL;
+ _objectRoomTable = NULL;
+ _objectStateTable = NULL;
+ _numObjectsInRoom = 0;
+ _userPut = 0;
+ _userState = 0;
+ _activeObject = 0;
+ _resourceHeaderSize = 8;
+ _saveLoadFlag = 0;
+ _saveLoadSlot = 0;
+ _lastSaveTime = 0;
+ _saveTemporaryState = false;
+ memset(_saveLoadName, 0, sizeof(_saveLoadName));
+ memset(_localScriptOffsets, 0, sizeof(_localScriptOffsets));
+ _scriptPointer = NULL;
+ _scriptOrgPointer = NULL;
+ _opcode = 0;
+ vm.numNestedScripts = 0;
+ _lastCodePtr = NULL;
+ _resultVarNumber = 0;
+ _scummStackPos = 0;
+ memset(_vmStack, 0, sizeof(_vmStack));
+ _keyScriptKey = 0;
+ _keyScriptNo = 0;
+ _fileOffset = 0;
+ memset(_resourceMapper, 0, sizeof(_resourceMapper));
+ _lastLoadedRoom = 0;
+ _roomResource = 0;
+ OF_OWNER_ROOM = 0;
+ _verbMouseOver = 0;
+ _inventoryOffset = 0;
+ _classData = NULL;
+ _actorToPrintStrFor = 0;
+ _sentenceNum = 0;
+ memset(_sentence, 0, sizeof(_sentence));
+ memset(_string, 0, sizeof(_string));
+ _screenB = 0;
+ _screenH = 0;
+ _roomHeight = 0;
+ _roomWidth = 0;
+ _screenHeight = 0;
+ _screenWidth = 0;
+ memset(virtscr, 0, sizeof(virtscr));
+ memset(&camera, 0, sizeof(CameraData));
+ memset(_colorCycle, 0, sizeof(_colorCycle));
+ memset(_colorUsedByCycle, 0, sizeof(_colorUsedByCycle));
+ _ENCD_offs = 0;
+ _EXCD_offs = 0;
+ _CLUT_offs = 0;
+ _EPAL_offs = 0;
+ _IM00_offs = 0;
+ _PALS_offs = 0;
+ _fullRedraw = false;
+ _bgNeedsRedraw = false;
+ _screenEffectFlag = false;
+ _completeScreenRedraw = false;
+ memset(&_cursor, 0, sizeof(_cursor));
+ memset(_grabbedCursor, 0, sizeof(_grabbedCursor));
+ _currentCursor = 0;
+ _newEffect = 0;
+ _switchRoomEffect2 = 0;
+ _switchRoomEffect = 0;
+ _scrollBuffer = NULL;
+
+ _doEffect = false;
+ memset(&_flashlight, 0, sizeof(_flashlight));
+ _bompActorPalettePtr = NULL;
+ _shakeEnabled = false;
+ _shakeFrame = 0;
+ _screenStartStrip = 0;
+ _screenEndStrip = 0;
+ _screenTop = 0;
+ _drawObjectQueNr = 0;
+ memset(_drawObjectQue, 0, sizeof(_drawObjectQue));
+ _palManipStart = 0;
+ _palManipEnd = 0;
+ _palManipCounter = 0;
+ _palManipPalette = NULL;
+ _palManipIntermediatePal = NULL;
+ memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
+ _hePalettes = NULL;
+ _shadowPalette = NULL;
+ _shadowPaletteSize = 0;
+ memset(_currentPalette, 0, sizeof(_currentPalette));
+ memset(_darkenPalette, 0, sizeof(_darkenPalette));
+ memset(_HEV7ActorPalette, 0, sizeof(_HEV7ActorPalette));
+ _palDirtyMin = 0;
+ _palDirtyMax = 0;
+ _haveMsg = 0;
+ _haveActorSpeechMsg = false;
+ _useTalkAnims = false;
+ _defaultTalkDelay = 0;
+ _musicType = MDT_NONE;
+ _tempMusic = 0;
+ _saveSound = 0;
+ memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));
+ memset(_scaleSlots, 0, sizeof(_scaleSlots));
+ _charset = NULL;
+ _charsetColor = 0;
+ memset(_charsetColorMap, 0, sizeof(_charsetColorMap));
+ memset(_charsetData, 0, sizeof(_charsetData));
+ _charsetBufPos = 0;
+ memset(_charsetBuffer, 0, sizeof(_charsetBuffer));
+ _copyProtection = false;
+ _demoMode = false;
+ _confirmExit = false;
+ _voiceMode = 0;
+ _talkDelay = 0;
+ _NES_lastTalkingActor = 0;
+ _NES_talkColor = 0;
+ _keepText = false;
+ _costumeLoader = NULL;
+ _costumeRenderer = NULL;
+ _2byteFontPtr = 0;
+ _V1TalkingActor = 0;
+ _NESStartStrip = 0;
+
+ _actorClipOverride.top = 0;
+ _actorClipOverride.bottom = 480;
+ _actorClipOverride.left = 0;
+ _actorClipOverride.right = 640;
+
+ _skipDrawObject = 0;
+ memset(_heTimers, 0, sizeof(_heTimers));
+
+ memset(_akosQueue, 0, sizeof(_akosQueue));
+ _akosQueuePos = 0;
+
+ //
+ // Init all VARS to 0xFF
+ //
+ VAR_LANGUAGE = 0xFF;
+ VAR_KEYPRESS = 0xFF;
+ VAR_SYNC = 0xFF;
+ VAR_EGO = 0xFF;
+ VAR_CAMERA_POS_X = 0xFF;
+ VAR_HAVE_MSG = 0xFF;
+ VAR_ROOM = 0xFF;
+ VAR_OVERRIDE = 0xFF;
+ VAR_MACHINE_SPEED = 0xFF;
+ VAR_ME = 0xFF;
+ VAR_NUM_ACTOR = 0xFF;
+ VAR_CURRENT_LIGHTS = 0xFF;
+ VAR_CURRENTDRIVE = 0xFF; // How about merging this with VAR_CURRENTDISK?
+ VAR_CURRENTDISK = 0xFF;
+ VAR_TMR_1 = 0xFF;
+ VAR_TMR_2 = 0xFF;
+ VAR_TMR_3 = 0xFF;
+ VAR_MUSIC_TIMER = 0xFF;
+ VAR_ACTOR_RANGE_MIN = 0xFF;
+ VAR_ACTOR_RANGE_MAX = 0xFF;
+ VAR_CAMERA_MIN_X = 0xFF;
+ VAR_CAMERA_MAX_X = 0xFF;
+ VAR_TIMER_NEXT = 0xFF;
+ VAR_VIRT_MOUSE_X = 0xFF;
+ VAR_VIRT_MOUSE_Y = 0xFF;
+ VAR_ROOM_RESOURCE = 0xFF;
+ VAR_LAST_SOUND = 0xFF;
+ VAR_CUTSCENEEXIT_KEY = 0xFF;
+ VAR_OPTIONS_KEY = 0xFF;
+ VAR_TALK_ACTOR = 0xFF;
+ VAR_CAMERA_FAST_X = 0xFF;
+ VAR_SCROLL_SCRIPT = 0xFF;
+ VAR_ENTRY_SCRIPT = 0xFF;
+ VAR_ENTRY_SCRIPT2 = 0xFF;
+ VAR_EXIT_SCRIPT = 0xFF;
+ VAR_EXIT_SCRIPT2 = 0xFF;
+ VAR_VERB_SCRIPT = 0xFF;
+ VAR_SENTENCE_SCRIPT = 0xFF;
+ VAR_INVENTORY_SCRIPT = 0xFF;
+ VAR_CUTSCENE_START_SCRIPT = 0xFF;
+ VAR_CUTSCENE_END_SCRIPT = 0xFF;
+ VAR_CHARINC = 0xFF;
+ VAR_CHARCOUNT = 0xFF;
+ VAR_WALKTO_OBJ = 0xFF;
+ VAR_DEBUGMODE = 0xFF;
+ VAR_HEAPSPACE = 0xFF;
+ VAR_RESTART_KEY = 0xFF;
+ VAR_PAUSE_KEY = 0xFF;
+ VAR_MOUSE_X = 0xFF;
+ VAR_MOUSE_Y = 0xFF;
+ VAR_TIMER = 0xFF;
+ VAR_TMR_4 = 0xFF;
+ VAR_SOUNDCARD = 0xFF;
+ VAR_VIDEOMODE = 0xFF;
+ VAR_MAINMENU_KEY = 0xFF;
+ VAR_FIXEDDISK = 0xFF;
+ VAR_CURSORSTATE = 0xFF;
+ VAR_USERPUT = 0xFF;
+ VAR_SOUNDRESULT = 0xFF;
+ VAR_TALKSTOP_KEY = 0xFF;
+ VAR_FADE_DELAY = 0xFF;
+ VAR_NOSUBTITLES = 0xFF;
+
+ VAR_SOUNDPARAM = 0xFF;
+ VAR_SOUNDPARAM2 = 0xFF;
+ VAR_SOUNDPARAM3 = 0xFF;
+ VAR_MOUSEPRESENT = 0xFF;
+ VAR_MEMORY_PERFORMANCE = 0xFF;
+ VAR_VIDEO_PERFORMANCE = 0xFF;
+ VAR_ROOM_FLAG = 0xFF;
+ VAR_GAME_LOADED = 0xFF;
+ VAR_NEW_ROOM = 0xFF;
+ VAR_VERSION_KEY = 0xFF;
+
+ VAR_V5_TALK_STRING_Y = 0xFF;
+
+ VAR_ROOM_WIDTH = 0xFF;
+ VAR_ROOM_HEIGHT = 0xFF;
+ VAR_SUBTITLES = 0xFF;
+ VAR_V6_EMSSPACE = 0xFF;
+
+ VAR_CAMERA_POS_Y = 0xFF;
+ VAR_CAMERA_MIN_Y = 0xFF;
+ VAR_CAMERA_MAX_Y = 0xFF;
+ VAR_CAMERA_THRESHOLD_X = 0xFF;
+ VAR_CAMERA_THRESHOLD_Y = 0xFF;
+ VAR_CAMERA_SPEED_X = 0xFF;
+ VAR_CAMERA_SPEED_Y = 0xFF;
+ VAR_CAMERA_ACCEL_X = 0xFF;
+ VAR_CAMERA_ACCEL_Y = 0xFF;
+ VAR_CAMERA_DEST_X = 0xFF;
+ VAR_CAMERA_DEST_Y = 0xFF;
+ VAR_CAMERA_FOLLOWED_ACTOR = 0xFF;
+
+ VAR_LEFTBTN_DOWN = 0xFF;
+ VAR_RIGHTBTN_DOWN = 0xFF;
+ VAR_LEFTBTN_HOLD = 0xFF;
+ VAR_RIGHTBTN_HOLD = 0xFF;
+
+ VAR_SAVELOAD_SCRIPT = 0xFF;
+ VAR_SAVELOAD_SCRIPT2 = 0xFF;
+
+ VAR_DEFAULT_TALK_DELAY = 0xFF;
+ VAR_CHARSET_MASK = 0xFF;
+
+ VAR_CUSTOMSCALETABLE = 0xFF;
+ VAR_V6_SOUNDMODE = 0xFF;
+
+ VAR_ACTIVE_VERB = 0xFF;
+ VAR_ACTIVE_OBJECT1 = 0xFF;
+ VAR_ACTIVE_OBJECT2 = 0xFF;
+ VAR_VERB_ALLOWED = 0xFF;
+ VAR_CLICK_AREA = 0xFF;
+
+ VAR_BLAST_ABOVE_TEXT = 0xFF;
+ VAR_VOICE_MODE = 0xFF;
+ VAR_MUSIC_BUNDLE_LOADED = 0xFF;
+ VAR_VOICE_BUNDLE_LOADED = 0xFF;
+
+ VAR_REDRAW_ALL_ACTORS = 0xFF;
+ VAR_SKIP_RESET_TALK_ACTOR = 0xFF;
+
+ VAR_SOUND_CHANNEL = 0xFF;
+ VAR_TALK_CHANNEL = 0xFF;
+ VAR_SOUNDCODE_TMR = 0xFF;
+ VAR_RESERVED_SOUND_CHANNELS = 0xFF;
+
+ VAR_MAIN_SCRIPT = 0xFF;
+
+ VAR_NUM_SCRIPT_CYCLES = 0xFF;
+ VAR_SCRIPT_CYCLE = 0xFF;
+
+ VAR_NUM_GLOBAL_OBJS = 0xFF;
+ VAR_KEY_STATE = 0xFF;
+ VAR_MOUSE_STATE = 0xFF;
+
+ // Use g_scumm from error() ONLY
+ g_scumm = this;
+
+ // Read settings from the detector & config manager
+ _debugMode = (gDebugLevel >= 0);
+ _dumpScripts = detector->_dumpScripts;
+ _bootParam = ConfMan.getInt("boot_param");
+ // Boot params often need debugging switched on to work
+ if (_bootParam)
+ _debugMode = true;
+
+ // Allow the user to override the game name with a custom string.
+ // This allows some game versions to work which use filenames
+ // differing from the regular version(s) of that game.
+ _baseName = ConfMan.hasKey("basename") ? ConfMan.get("basename") : gs.gameid;
+
+ _copyProtection = ConfMan.getBool("copy_protection");
+ _demoMode = ConfMan.getBool("demo_mode");
+ if (ConfMan.hasKey("nosubtitles")) {
+ printf("Configuration key 'nosubtitles' is deprecated. Use 'subtitles' instead\n");
+ if (!ConfMan.hasKey("subtitles"))
+ ConfMan.set("subtitles", !ConfMan.getBool("nosubtitles"));
+ }
+
+ // Make sure that at least subtitles are enabled
+ if (ConfMan.getBool("speech_mute") && !ConfMan.getBool("subtitles"))
+ ConfMan.set("subtitles", 1);
+
+ // TODO Detect subtitle only versions of scumm6 games
+ if (ConfMan.getBool("speech_mute"))
+ _voiceMode = 2;
+ else
+ _voiceMode = ConfMan.getBool("subtitles");
+
+ _confirmExit = ConfMan.getBool("confirm_exit");
+
+ if (ConfMan.hasKey("render_mode")) {
+ _renderMode = Common::parseRenderMode(ConfMan.get("render_mode").c_str());
+ } else {
+ _renderMode = Common::kRenderDefault;
+ }
+
+ // Do some render mode restirctions
+ if (_version == 1)
+ _renderMode = Common::kRenderDefault;
+
+ switch (_renderMode) {
+ case Common::kRenderHercA:
+ case Common::kRenderHercG:
+ if (_version > 2 && _gameId != GID_MONKEY_EGA)
+ _renderMode = Common::kRenderDefault;
+ break;
+
+ case Common::kRenderCGA:
+ case Common::kRenderEGA:
+ case Common::kRenderAmiga:
+ if (!(_features & GF_16COLOR))
+ _renderMode = Common::kRenderDefault;
+ break;
+
+ default:
+ break;
+ }
+
+ _hexdumpScripts = false;
+ _showStack = false;
+
+ if (_platform == Common::kPlatformFMTowns && _version == 3) { // FM-TOWNS V3 games use 320x240
+ _screenWidth = 320;
+ _screenHeight = 240;
+ } else if (_features & GF_DEFAULT_TO_1X_SCALER) {
+ _screenWidth = 640;
+ _screenHeight = 480;
+ } else if (_platform == Common::kPlatformNES) {
+ _screenWidth = 256;
+ _screenHeight = 240;
+ } else if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
+ _features |= GF_DEFAULT_TO_1X_SCALER;
+ _screenWidth = 320;
+ _screenHeight = 200;
+ } else {
+ _screenWidth = 320;
+ _screenHeight = 200;
+ }
+
+ _compositeBuf = (byte *)malloc(_screenWidth * _screenHeight);
+
+ _herculesBuf = 0;
+ if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
+ _herculesBuf = (byte *)malloc(Common::kHercW * Common::kHercH);
+ }
+}
+
+ScummEngine::~ScummEngine() {
+ if (_musicEngine) {
+ _musicEngine->terminate();
+ delete _musicEngine;
+ }
+
+ _mixer->stopAll();
+
+ delete [] _actors;
+ delete [] _sortedActors;
+
+ delete _2byteFontPtr;
+ delete _charset;
+ delete _pauseDialog;
+ delete _mainMenuDialog;
+ delete _versionDialog;
+ delete _fileHandle;
+
+ delete _sound;
+
+ delete _costumeLoader;
+ delete _costumeRenderer;
+
+ free(_shadowPalette);
+
+ free(_palManipPalette);
+ free(_palManipIntermediatePal);
+
+ res.freeResources();
+
+ free(_objectStateTable);
+ free(_objectRoomTable);
+ free(_objectOwnerTable);
+ free(_inventory);
+ free(_verbs);
+ free(_objs);
+ free(_roomVars);
+ free(_scummVars);
+ free(_bitVars);
+ free(_newNames);
+ free(_classData);
+ free(_arraySlot);
+
+ free(_compositeBuf);
+ free(_herculesBuf);
+
+ delete _debugger;
+}
+
+ScummEngine_v4::ScummEngine_v4(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v5(detector, syst, gs, md5sum, substResFileNameIndex) {
+ _resourceHeaderSize = 6;
+}
+
+ScummEngine_v3::ScummEngine_v3(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v4(detector, syst, gs, md5sum, substResFileNameIndex) {
+}
+
+ScummEngine_v3old::ScummEngine_v3old(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v3(detector, syst, gs, md5sum, substResFileNameIndex) {
+ _resourceHeaderSize = 4;
+}
+
+ScummEngine_v2::ScummEngine_v2(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v3old(detector, syst, gs, md5sum, substResFileNameIndex) {
+}
+
+ScummEngine_c64::ScummEngine_c64(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v2(detector, syst, gs, md5sum, substResFileNameIndex) {
+
+ _currentAction = 0;
+ _currentMode = 0;
+}
+
+ScummEngine_v6::ScummEngine_v6(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine(detector, syst, gs, md5sum, substResFileNameIndex) {
+ _blastObjectQueuePos = 0;
+ memset(_blastObjectQueue, 0, sizeof(_blastObjectQueue));
+ _blastTextQueuePos = 0;
+ memset(_blastTextQueue, 0, sizeof(_blastTextQueue));
+
+ _smushFrameRate = 0;
+
+ VAR_VIDEONAME = 0xFF;
+ VAR_RANDOM_NR = 0xFF;
+ VAR_STRING2DRAW = 0xFF;
+
+ VAR_TIMEDATE_YEAR = 0xFF;
+ VAR_TIMEDATE_MONTH = 0xFF;
+ VAR_TIMEDATE_DAY = 0xFF;
+ VAR_TIMEDATE_HOUR = 0xFF;
+ VAR_TIMEDATE_MINUTE = 0xFF;
+ VAR_TIMEDATE_SECOND = 0xFF;
+}
+
+#ifndef DISABLE_HE
+ScummEngine_v70he::ScummEngine_v70he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v60he(detector, syst, gs, md5sum, substResFileNameIndex) {
+ if (_platform == Common::kPlatformMacintosh && (_heversion >= 72 && _heversion <= 73))
+ _resExtractor = new MacResExtractor(this);
+ else
+ _resExtractor = new Win32ResExtractor(this);
+
+ _wiz = new Wiz(this);
+
+ _heV7RoomOffsets = NULL;
+
+ _heSndSoundId = 0;
+ _heSndOffset = 0;
+ _heSndChannel = 0;
+ _heSndFlags = 0;
+ _heSndSoundFreq = 0;
+
+ _skipProcessActors = 0;
+
+ VAR_NUM_SOUND_CHANNELS = 0xFF;
+ VAR_WIZ_TCOLOR = 0xFF;
+}
+
+ScummEngine_v70he::~ScummEngine_v70he() {
+ delete _resExtractor;
+ delete _wiz;
+ free(_heV7DiskOffsets);
+ free(_heV7RoomIntOffsets);
+ free(_heV7RoomOffsets);
+ free(_storedFlObjects);
+}
+
+ScummEngine_v71he::ScummEngine_v71he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v70he(detector, syst, gs, md5sum, substResFileNameIndex) {
+ _auxBlocksNum = 0;
+ memset(_auxBlocks, 0, sizeof(_auxBlocks));
+ _auxEntriesNum = 0;
+ memset(_auxEntries, 0, sizeof(_auxEntries));
+}
+
+ScummEngine_v72he::ScummEngine_v72he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v71he(detector, syst, gs, md5sum, substResFileNameIndex) {
+ VAR_NUM_ROOMS = 0xFF;
+ VAR_NUM_SCRIPTS = 0xFF;
+ VAR_NUM_SOUNDS = 0xFF;
+ VAR_NUM_COSTUMES = 0xFF;
+ VAR_NUM_IMAGES = 0xFF;
+ VAR_NUM_CHARSETS = 0xFF;
+ VAR_POLYGONS_ONLY = 0xFF;
+}
+
+ScummEngine_v80he::ScummEngine_v80he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v72he(detector, syst, gs, md5sum, substResFileNameIndex) {
+ _heSndResId = 0;
+ _curSndId = 0;
+ _sndPtrOffs = 0;
+ _sndTmrOffs = 0;
+
+ VAR_PLATFORM = 0xFF;
+ VAR_WINDOWS_VERSION = 0xFF;
+ VAR_CURRENT_CHARSET = 0xFF;
+ VAR_COLOR_DEPTH = 0xFF;
+}
+
+ScummEngine_v90he::ScummEngine_v90he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v80he(detector, syst, gs, md5sum, substResFileNameIndex) {
+ _sprite = new Sprite(this);
+
+ VAR_NUM_SPRITE_GROUPS = 0xFF;
+ VAR_NUM_SPRITES = 0xFF;
+ VAR_NUM_PALETTES = 0xFF;
+ VAR_NUM_UNK = 0xFF;
+
+ VAR_U32_VERSION = 0xFF;
+ VAR_U32_ARRAY_UNK = 0xFF;
+}
+
+ScummEngine_v90he::~ScummEngine_v90he() {
+ delete _sprite;
+ if (_heversion >= 98) {
+ delete _logicHE;
+ }
+ if (_heversion >= 99) {
+ free(_hePalettes);
+ }
+}
+#endif
+
+#ifndef DISABLE_SCUMM_7_8
+ScummEngine_v7::ScummEngine_v7(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v6(detector, syst, gs, md5sum, substResFileNameIndex) {
+ _existLanguageFile = false;
+ _languageBuffer = NULL;
+ _languageIndex = NULL;
+ clearSubtitleQueue();
+}
+
+ScummEngine_v7::~ScummEngine_v7() {
+ free(_languageBuffer);
+ free(_languageIndex);
+}
+
+ScummEngine_v8::ScummEngine_v8(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex)
+ : ScummEngine_v7(detector, syst, gs, md5sum, substResFileNameIndex) {
+ _objectIDMap = 0;
+}
+
+ScummEngine_v8::~ScummEngine_v8() {
+ delete [] _objectIDMap;
+}
+#endif
+
+#pragma mark -
+#pragma mark --- Initialization ---
+#pragma mark -
+
+int ScummEngine::init(GameDetector &detector) {
+
+ // Initialize backend
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {
+ _system->initSize(Common::kHercW, Common::kHercH, 1);
+ _features |= GF_DEFAULT_TO_1X_SCALER;
+ _system->setGraphicsMode("1x");
+ } else {
+ _system->initSize(_screenWidth, _screenHeight, (detector._force1xOverlay ? 1 : 2));
+ if (_features & GF_DEFAULT_TO_1X_SCALER)
+ _system->setGraphicsMode("1x");
+ }
+ _system->endGFXTransaction();
+
+ // On some systems it's not safe to run CD audio games from the CD.
+ if (_features & GF_AUDIOTRACKS)
+ checkCD();
+
+ int cd_num = ConfMan.getInt("cdrom");
+ if (cd_num >= 0 && (_features & GF_AUDIOTRACKS))
+ _system->openCD(cd_num);
+
+ // Create the sound manager
+ _sound = new Sound(this);
+
+ // Setup the music engine
+ setupMusic(_midi);
+
+ // TODO: We shouldn't rely on the global Language values matching those COMI etc. expect.
+ // Rather we should explicitly translate them.
+ _language = Common::parseLanguage(ConfMan.get("language"));
+
+ // Load localization data, if present
+ loadLanguageBundle();
+
+ // Load CJK font, if present
+ loadCJKFont();
+
+ // Create the charset renderer
+ if (_platform == Common::kPlatformNES)
+ _charset = new CharsetRendererNES(this);
+ else if (_version <= 2)
+ _charset = new CharsetRendererV2(this, _language);
+ else if (_version == 3)
+ _charset = new CharsetRendererV3(this);
+#ifndef DISABLE_SCUMM_7_8
+ else if (_version == 8)
+ _charset = new CharsetRendererNut(this);
+#endif
+ else
+ _charset = new CharsetRendererClassic(this);
+
+ // Create the costume renderer
+ if (_features & GF_NEW_COSTUMES) {
+ _costumeRenderer = new AkosRenderer(this);
+ _costumeLoader = new AkosCostumeLoader(this);
+ } else if (_platform == Common::kPlatformC64 && _gameId == GID_MANIAC) {
+ _costumeRenderer = new C64CostumeRenderer(this);
+ _costumeLoader = new C64CostumeLoader(this);
+ } else if (_platform == Common::kPlatformNES) {
+ _costumeRenderer = new NESCostumeRenderer(this);
+ _costumeLoader = new NESCostumeLoader(this);
+ } else {
+ _costumeRenderer = new ClassicCostumeRenderer(this);
+ _costumeLoader = new ClassicCostumeLoader(this);
+ }
+
+#ifndef DISABLE_SCUMM_7_8
+ // Create FT INSANE object
+ if (_gameId == GID_FT)
+ _insane = new Insane((ScummEngine_v6 *)this);
+ else
+#endif
+ _insane = 0;
+
+ // Load game from specified slot, if any
+ if (ConfMan.hasKey("save_slot")) {
+ requestLoad(ConfMan.getInt("save_slot"));
+ }
+
+ allocResTypeData(rtBuffer, MKID('NONE'), 10, "buffer", 0);
+
+ setupScummVars();
+
+ setupOpcodes();
+
+ if (_version == 8)
+ _numActors = 80;
+ else if (_version == 7)
+ _numActors = 30;
+ else if (_gameId == GID_SAMNMAX)
+ _numActors = 30;
+ else if (_gameId == GID_MANIAC)
+ _numActors = 25;
+ else if (_heversion >= 80)
+ _numActors = 62;
+ else if (_heversion >= 72)
+ _numActors = 30;
+ else
+ _numActors = 13;
+
+ if (_version >= 7)
+ OF_OWNER_ROOM = 0xFF;
+ else
+ OF_OWNER_ROOM = 0x0F;
+
+ // if (_gameId==GID_MONKEY2 && _bootParam == 0)
+ // _bootParam = 10001;
+
+ if (!_copyProtection && _gameId == GID_INDY4 && _bootParam == 0) {
+ _bootParam = -7873;
+ }
+
+ if (!_copyProtection && _gameId == GID_SAMNMAX && _bootParam == 0) {
+ _bootParam = -1;
+ }
+
+ readIndexFile();
+
+#ifdef PALMOS_68K
+ if (_features & GF_NEW_COSTUMES)
+ res._maxHeapThreshold = gVars->memory[kMemScummNewCostGames];
+ else
+ res._maxHeapThreshold = gVars->memory[kMemScummOldCostGames];
+#else
+ if (_features & GF_NEW_COSTUMES) {
+ // Since the new costumes are very big, we increase the heap limit, to avoid having
+ // to constantly reload stuff from the data files.
+ res._maxHeapThreshold = 6 * 1024 * 1024;
+ } else {
+ res._maxHeapThreshold = 550000;
+ }
+#endif
+ res._minHeapThreshold = 400000;
+
+ scummInit();
+ initScummVars();
+
+ if (VAR_DEBUGMODE != 0xFF) {
+ VAR(VAR_DEBUGMODE) = _debugMode;
+ if (_heversion >= 80 && _debugMode)
+ VAR(85) = 1;
+ }
+
+ if (_imuse) {
+ _imuse->setBase(res.address[rtSound]);
+ }
+
+ if (_version >= 5)
+ _sound->setupSound();
+
+#if (defined(PALMOS_ARM) || defined(PALMOS_DEBUG) || defined(__GP32__))
+ Graphics::initfonts();
+#endif
+
+ // Create debugger
+ if (!_debugger)
+ _debugger = new ScummDebugger(this);
+
+ return 0;
+}
+
+void ScummEngine::scummInit() {
+ int i;
+
+ _tempMusic = 0;
+ debug(9, "scummInit");
+
+ if ((_gameId == GID_MANIAC) && (_version == 1) && !(_platform == Common::kPlatformNES)) {
+ if (_platform == Common::kPlatformC64)
+ initScreens(8, 144);
+ else
+ initScreens(16, 152);
+ } else if (_version >= 7 || _heversion >= 71) {
+ initScreens(0, _screenHeight);
+ } else {
+ initScreens(16, 144);
+ }
+
+ _palManipCounter = 0;
+
+ for (i = 0; i < 256; i++)
+ _roomPalette[i] = i;
+ if (_version == 1) {
+ // Use 17 color table for v1 games to allow
+ // correct color for inventory and sentence
+ // line
+ // Original games used some kind of dynamic
+ // color table remapping between rooms
+ if (_platform == Common::kPlatformC64) {
+ setupC64Palette();
+ } else if (_platform == Common::kPlatformNES) {
+ setupNESPalette();
+ } else {
+ setupV1Palette();
+ }
+ } else if (_features & GF_16COLOR) {
+ for (i = 0; i < 16; i++)
+ _shadowPalette[i] = i;
+
+ switch (_renderMode) {
+ case Common::kRenderEGA:
+ setupEGAPalette();
+ break;
+
+ case Common::kRenderAmiga:
+ setupAmigaPalette();
+ break;
+
+ case Common::kRenderCGA:
+ setupCGAPalette();
+ break;
+
+ case Common::kRenderHercA:
+ case Common::kRenderHercG:
+ setupHercPalette();
+ break;
+
+ default:
+ if ((_platform == Common::kPlatformAmiga) || (_platform == Common::kPlatformAtariST))
+ setupAmigaPalette();
+ else
+ setupEGAPalette();
+ }
+ }
+
+ if (_version > 3 && _version < 8)
+ loadCharset(1);
+
+ if (_features & GF_OLD_BUNDLE)
+ loadCharset(0); // FIXME - HACK ?
+
+ setShake(0);
+ setupCursor();
+
+ // Allocate and Initialize actors
+ Actor::initActorClass(this);
+ _actors = new Actor[_numActors];
+ _sortedActors = new Actor * [_numActors];
+ for (i = 0; i < _numActors; i++) {
+ _actors[i]._number = i;
+ _actors[i].initActor(1);
+
+ // this is from IDB
+ if ((_version == 1) || (_gameId == GID_MANIAC && _demoMode))
+ _actors[i].setActorCostume(i);
+ }
+
+ if (_gameId == GID_MANIAC && _version == 1) {
+ setupV1ActorTalkColor();
+ } else if (_gameId == GID_MANIAC && _version == 2 && _demoMode) {
+ // HACK Some palette changes needed for demo script
+ // in Maniac Mansion (Enhanced)
+ _actors[3].setPalette(3, 1);
+ _actors[9]._talkColor = 15;
+ _actors[10]._talkColor = 7;
+ _actors[11]._talkColor = 2;
+ _actors[13]._talkColor = 5;
+ _actors[23]._talkColor = 14;
+ }
+
+ vm.numNestedScripts = 0;
+ vm.cutSceneStackPointer = 0;
+
+ memset(vm.cutScenePtr, 0, sizeof(vm.cutScenePtr));
+ memset(vm.cutSceneData, 0, sizeof(vm.cutSceneData));
+
+ for (i = 0; i < _numVerbs; i++) {
+ _verbs[i].verbid = 0;
+ _verbs[i].curRect.right = _screenWidth - 1;
+ _verbs[i].oldRect.left = -1;
+ _verbs[i].type = 0;
+ _verbs[i].color = 2;
+ _verbs[i].hicolor = 0;
+ _verbs[i].charset_nr = 1;
+ _verbs[i].curmode = 0;
+ _verbs[i].saveid = 0;
+ _verbs[i].center = 0;
+ _verbs[i].key = 0;
+ }
+
+ if (_version == 7) {
+ VAR(VAR_CAMERA_THRESHOLD_X) = 100;
+ VAR(VAR_CAMERA_THRESHOLD_Y) = 70;
+ VAR(VAR_CAMERA_ACCEL_X) = 100;
+ VAR(VAR_CAMERA_ACCEL_Y) = 100;
+ } else if (!(_features & GF_NEW_CAMERA)) {
+ if (_platform == Common::kPlatformNES) {
+ camera._leftTrigger = 6; // 6
+ camera._rightTrigger = 21; // 25
+ } else {
+ camera._leftTrigger = 10;
+ camera._rightTrigger = (_heversion >= 71) ? 70 : 30;
+ }
+ camera._mode = 0;
+ }
+ camera._follows = 0;
+
+ virtscr[0].xstart = 0;
+
+ if (VAR_CURRENT_LIGHTS != 0xFF) {
+ // Setup light
+ _flashlight.xStrips = 7;
+ _flashlight.yStrips = 7;
+ _flashlight.buffer = NULL;
+ }
+
+ _mouse.x = 104;
+ _mouse.y = 56;
+
+ _ENCD_offs = 0;
+ _EXCD_offs = 0;
+
+ _currentScript = 0xFF;
+ _sentenceNum = 0;
+
+ _currentRoom = 0;
+ _numObjectsInRoom = 0;
+ _actorToPrintStrFor = 0;
+
+ _charsetBufPos = 0;
+ _haveMsg = 0;
+ _haveActorSpeechMsg = false;
+
+ _varwatch = -1;
+ _screenStartStrip = 0;
+
+ _defaultTalkDelay = 3;
+ _talkDelay = 0;
+ _keepText = false;
+
+ _currentCursor = 0;
+ _cursor.state = 0;
+ _userPut = 0;
+
+ _newEffect = 129;
+ _fullRedraw = true;
+
+ clearDrawObjectQueue();
+
+ if (_platform == Common::kPlatformNES)
+ decodeNESBaseTiles();
+
+ for (i = 0; i < 6; i++) {
+ if (_version == 3) { // FIXME - what is this?
+ _string[i]._default.xpos = 0;
+ _string[i]._default.ypos = 0;
+ } else {
+ _string[i]._default.xpos = 2;
+ _string[i]._default.ypos = 5;
+ }
+ _string[i]._default.right = _screenWidth - 1;
+ _string[i]._default.height = 0;
+ _string[i]._default.color = 0xF;
+ _string[i]._default.center = 0;
+ _string[i]._default.charset = 0;
+ }
+
+ // all keys are released
+ for (i = 0; i < 512; i++)
+ _keyDownMap[i] = false;
+
+ _lastSaveTime = _system->getMillis();
+}
+
+void ScummEngine_c64::scummInit() {
+ ScummEngine::scummInit();
+ initC64Verbs();
+}
+
+void ScummEngine_v2::scummInit() {
+ ScummEngine::scummInit();
+
+ if (_platform == Common::kPlatformNES) {
+ initNESMouseOver();
+ _switchRoomEffect2 = _switchRoomEffect = 6;
+ } else {
+ initV2MouseOver();
+ // Seems in V2 there was only a single room effect (iris),
+ // so we set that here.
+ _switchRoomEffect2 = 1;
+ _switchRoomEffect = 5;
+ }
+
+ _inventoryOffset = 0;
+}
+
+void ScummEngine_v6::scummInit() {
+ ScummEngine::scummInit();
+ setDefaultCursor();
+}
+
+void ScummEngine_v60he::scummInit() {
+ ScummEngine::scummInit();
+
+ // HACK cursor hotspot is wrong
+ // Original games used
+ // setCursorHotspot(8, 7);
+ if (_gameId == GID_FUNPACK)
+ setCursorHotspot(16, 16);
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v72he::scummInit() {
+ ScummEngine_v60he::scummInit();
+
+ _stringLength = 1;
+ memset(_stringBuffer, 0, sizeof(_stringBuffer));
+}
+
+void ScummEngine_v90he::scummInit() {
+ ScummEngine_v72he::scummInit();
+
+ _heObject = 0;
+ _heObjectNum = 0;
+ _hePaletteNum = 0;
+
+ _sprite->resetTables(0);
+ memset(&_wizParams, 0, sizeof(_wizParams));
+
+ if (_features & GF_HE_CURSORLESS)
+ setDefaultCursor();
+
+ if (_heversion >= 98) {
+ switch (_gameId) {
+ case GID_PUTTRACE:
+ _logicHE = new LogicHErace(this);
+ break;
+
+ case GID_FUNSHOP:
+ _logicHE = new LogicHEfunshop(this);
+ break;
+
+ case GID_FOOTBALL:
+ _logicHE = new LogicHEfootball(this);
+ break;
+
+ default:
+ _logicHE = new LogicHE(this);
+ break;
+ }
+ }
+}
+
+void ScummEngine_v99he::scummInit() {
+ ScummEngine_v90he::scummInit();
+
+ _hePalettes = (uint8 *)malloc((_numPalettes + 1) * 1024);
+ memset(_hePalettes, 0, (_numPalettes + 1) * 1024);
+
+ // Array 129 is set to base name
+ int len = resStrLen((const byte *)_baseName.c_str()) + 1;
+ ArrayHeader *ah = defineArray(129, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, _baseName.c_str(), len);
+
+}
+#endif
+
+void ScummEngine::setupMusic(int midi) {
+ int midiDriver = MidiDriver::detectMusicDriver(midi);
+ _native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+
+ switch (midiDriver) {
+ case MD_NULL:
+ _musicType = MDT_NONE;
+ break;
+ case MD_PCSPK:
+ case MD_PCJR:
+ _musicType = MDT_PCSPK;
+ break;
+ case MD_TOWNS:
+ _musicType = MDT_TOWNS;
+ break;
+ case MD_ADLIB:
+ _musicType = MDT_ADLIB;
+ break;
+ default:
+ _musicType = MDT_MIDI;
+ break;
+ }
+
+ // FIXME: MD_TOWNS should not be _midi_native in the first place!! iMuse code needs to be restructured.
+ if ((_gameId == GID_TENTACLE) || (_gameId == GID_SAMNMAX) || (midiDriver == MD_TOWNS))
+ _enable_gs = false;
+ else
+ _enable_gs = ConfMan.getBool("enable_gs");
+
+ /* Bind the mixer to the system => mixer will be invoked
+ * automatically when samples need to be generated */
+ if (!_mixer->isReady()) {
+ warning("Sound mixer initialization failed\n");
+ if (_musicType == MDT_ADLIB || _musicType == MDT_PCSPK) {
+ midiDriver = MD_NULL;
+ _musicType = MDT_NONE;
+ warning("MIDI driver depends on sound mixer, switching to null MIDI driver\n");
+ }
+ }
+
+ // Init iMuse
+ if (_features & GF_DIGI_IMUSE) {
+#ifndef DISABLE_SCUMM_7_8
+ _musicEngine = _imuseDigital = new IMuseDigital(this, 10);
+#endif
+ } else if (_platform == Common::kPlatformC64) {
+ // TODO
+ _musicEngine = NULL;
+ } else if (_platform == Common::kPlatformNES) {
+ _musicEngine = new Player_NES(this);
+ } else if ((_platform == Common::kPlatformAmiga) && (_version == 2)) {
+ _musicEngine = new Player_V2A(this);
+ } else if ((_platform == Common::kPlatformAmiga) && (_version == 3)) {
+ _musicEngine = new Player_V3A(this);
+ } else if ((_platform == Common::kPlatformAmiga) && (_version < 5)) {
+ _musicEngine = NULL;
+ } else if (_gameId == GID_MANIAC && (_version == 1)) {
+ _musicEngine = new Player_V1(this, midiDriver != MD_PCSPK);
+ } else if (_version <= 2) {
+ _musicEngine = new Player_V2(this, midiDriver != MD_PCSPK);
+ } else if ((_musicType == MDT_PCSPK) && ((_version > 2) && (_version < 5))) {
+ _musicEngine = new Player_V2(this, midiDriver != MD_PCSPK);
+ } else if (_version > 2 && _heversion <= 61) {
+ MidiDriver *nativeMidiDriver = 0;
+ MidiDriver *adlibMidiDriver = 0;
+
+ if (_musicType != MDT_ADLIB)
+ nativeMidiDriver = MidiDriver::createMidi(midiDriver);
+ if (nativeMidiDriver != NULL && _native_mt32)
+ nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+ bool multi_midi = ConfMan.getBool("multi_midi") && _musicType != MDT_NONE && (midi & MDT_ADLIB);
+ if (_musicType == MDT_ADLIB || multi_midi) {
+ adlibMidiDriver = MidiDriver_ADLIB_create(_mixer);
+ adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_features & GF_SMALL_HEADER) ? 1 : 0);
+ }
+
+ _musicEngine = _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver);
+ if (_imuse) {
+ if (ConfMan.hasKey("tempo"))
+ _imuse->property(IMuse::PROP_TEMPO_BASE, ConfMan.getInt("tempo"));
+ _imuse->property(IMuse::PROP_NATIVE_MT32, _native_mt32);
+ _imuse->property(IMuse::PROP_GS, _enable_gs);
+ if (_heversion >= 60 || midi == MDT_TOWNS) {
+ _imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1);
+ _imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1);
+ }
+ if (midi == MDT_TOWNS)
+ _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
+ }
+ }
+
+ setupVolumes();
+}
+
+void ScummEngine::setupVolumes() {
+
+ // Sync the engine with the config manager
+ int soundVolumeMusic = ConfMan.getInt("music_volume");
+ int soundVolumeSfx = ConfMan.getInt("sfx_volume");
+ int soundVolumeSpeech = ConfMan.getInt("speech_volume");
+
+ if (_musicEngine) {
+ _musicEngine->setMusicVolume(soundVolumeMusic);
+ }
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSfx);
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
+}
+
+
+
+#pragma mark -
+#pragma mark --- Main loop ---
+#pragma mark -
+
+int ScummEngine::go() {
+ _engineStartTime = _system->getMillis() / 1000;
+
+ // If requested, load a save game instead of running the boot script
+ if (_saveLoadFlag != 2 || !loadState(_saveLoadSlot, _saveTemporaryState)) {
+ int args[16];
+ memset(args, 0, sizeof(args));
+ args[0] = _bootParam;
+
+ _saveLoadFlag = 0;
+#ifndef DISABLE_HE
+ if (_heversion >= 98) {
+ ((ScummEngine_v90he *)this)->_logicHE->initOnce();
+ ((ScummEngine_v90he *)this)->_logicHE->beforeBootScript();
+ }
+#endif
+ if (_gameId == GID_MANIAC && _demoMode)
+ runScript(9, 0, 0, args);
+ else
+ runScript(1, 0, 0, args);
+ } else {
+ _saveLoadFlag = 0;
+ }
+
+ int delta = 0;
+ int diff = _system->getMillis();
+
+ while (!_quit) {
+
+ updatePalette();
+ _system->updateScreen();
+
+ diff -= _system->getMillis();
+ waitForTimer(delta * 15 + diff);
+ diff = _system->getMillis();
+ delta = scummLoop(delta);
+
+ if (delta < 1) // Ensure we don't get into a loop
+ delta = 1; // by not decreasing sleepers.
+
+ if (_quit) {
+ // TODO: Maybe perform an autosave on exit?
+ }
+ }
+
+ return 0;
+}
+
+void ScummEngine::waitForTimer(int msec_delay) {
+ uint32 start_time;
+
+ if (_fastMode & 2)
+ msec_delay = 0;
+ else if (_fastMode & 1)
+ msec_delay = 10;
+
+ start_time = _system->getMillis();
+
+ while (!_quit) {
+ _sound->updateCD(); // Loop CD Audio if needed
+ parseEvents();
+ if (_system->getMillis() >= start_time + msec_delay)
+ break;
+ _system->delayMillis(10);
+ }
+}
+
+int ScummEngine::scummLoop(int delta) {
+ if (_debugger->isAttached())
+ _debugger->onFrame();
+
+ // Randomize the PRNG by calling it at regular intervals. This ensures
+ // that it will be in a different state each time you run the program.
+ _rnd.getRandomNumber(2);
+
+#ifndef DISABLE_HE
+ if (_heversion >= 98) {
+ ((ScummEngine_v90he *)this)->_logicHE->startOfFrame();
+ }
+#endif
+ if (_version > 2) {
+ VAR(VAR_TMR_1) += delta;
+ VAR(VAR_TMR_2) += delta;
+ VAR(VAR_TMR_3) += delta;
+ if (_gameId == GID_ZAK || _gameId == GID_INDY3) {
+ // All versions of Indy3 set three extra timers
+ // FM-TOWNS version of Zak sets three extra timers
+ VAR(39) += delta;
+ VAR(40) += delta;
+ VAR(41) += delta;
+ }
+ }
+ if (VAR_TMR_4 != 0xFF)
+ VAR(VAR_TMR_4) += delta;
+
+ if (delta > 15)
+ delta = 15;
+
+ decreaseScriptDelay(delta);
+
+ _talkDelay -= delta;
+ if (_talkDelay < 0)
+ _talkDelay = 0;
+
+ // Record the current ego actor before any scripts (including input scripts)
+ // get a chance to run.
+ int oldEgo = 0;
+ if (VAR_EGO != 0xFF)
+ oldEgo = VAR(VAR_EGO);
+
+ // In V1-V3 games, CHARSET_1 is called much earlier than in newer games.
+ // See also bug #770042 for a case were this makes a difference.
+ if (_version <= 3)
+ CHARSET_1();
+
+ processKbd(false);
+
+ if (_features & GF_NEW_CAMERA) {
+ VAR(VAR_CAMERA_POS_X) = camera._cur.x;
+ VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
+ } else if (_version <= 2) {
+ VAR(VAR_CAMERA_POS_X) = camera._cur.x / 8;
+ } else {
+ VAR(VAR_CAMERA_POS_X) = camera._cur.x;
+ }
+ if (_version <= 7)
+ VAR(VAR_HAVE_MSG) = _haveMsg;
+
+ if (_platform == Common::kPlatformC64 && _gameId == GID_MANIAC) {
+ // TODO
+ } else if (_version <= 2) {
+ VAR(VAR_VIRT_MOUSE_X) = _virtualMouse.x / 8;
+ VAR(VAR_VIRT_MOUSE_Y) = _virtualMouse.y / 2;
+
+ // Adjust mouse coordinates as narrow rooms in NES are centered
+ if (_platform == Common::kPlatformNES && _NESStartStrip > 0) {
+ VAR(VAR_VIRT_MOUSE_X) -= 2;
+ if (VAR(VAR_VIRT_MOUSE_X) < 0)
+ VAR(VAR_VIRT_MOUSE_X) = 0;
+ }
+ } else {
+ VAR(VAR_VIRT_MOUSE_X) = _virtualMouse.x;
+ VAR(VAR_VIRT_MOUSE_Y) = _virtualMouse.y;
+ VAR(VAR_MOUSE_X) = _mouse.x;
+ VAR(VAR_MOUSE_Y) = _mouse.y;
+ if (VAR_DEBUGMODE != 0xFF) {
+ // This is NOT for the Mac version of Indy3/Loom
+ VAR(VAR_DEBUGMODE) = _debugMode;
+ }
+ }
+
+ if (_features & GF_AUDIOTRACKS) {
+ // Covered automatically by the Sound class
+ } else if (VAR_MUSIC_TIMER != 0xFF) {
+ if (_musicEngine) {
+ // The music engine generates the timer data for us.
+ VAR(VAR_MUSIC_TIMER) = _musicEngine->getMusicTimer();
+ } else {
+ // Used for Money Island 1 (Amiga)
+ // TODO: The music delay (given in milliseconds) might have to be tuned a little
+ // to get it correct for all games. Without the ability to watch/listen to the
+ // original games, I can't do that myself.
+ const int MUSIC_DELAY = 350;
+ _tempMusic += delta * 15; // Convert delta to milliseconds
+ if (_tempMusic >= MUSIC_DELAY) {
+ _tempMusic -= MUSIC_DELAY;
+ VAR(VAR_MUSIC_TIMER) += 1;
+ }
+ }
+ }
+
+ // Trigger autosave if necessary.
+ if (!_saveLoadFlag && shouldPerformAutoSave(_lastSaveTime)) {
+ _saveLoadSlot = 0;
+ sprintf(_saveLoadName, "Autosave %d", _saveLoadSlot);
+ _saveLoadFlag = 1;
+ _saveTemporaryState = false;
+ }
+
+ if (VAR_GAME_LOADED != 0xFF)
+ VAR(VAR_GAME_LOADED) = 0;
+ if (_saveLoadFlag) {
+load_game:
+ bool success;
+ const char *errMsg = 0;
+ char filename[256];
+
+ if (_saveLoadFlag == 1) {
+ success = saveState(_saveLoadSlot, _saveTemporaryState);
+ if (!success)
+ errMsg = "Failed to save game state to file:\n\n%s";
+
+ // Ender: Disabled for small_header games, as can overwrite game
+ // variables (eg, Zak256 cashcard values). Temp disabled for V8
+ // because of odd timing issue with scripts and the variable reset
+ if (success && _saveTemporaryState && !(_features & GF_SMALL_HEADER) && _version < 8)
+ VAR(VAR_GAME_LOADED) = 201;
+ } else {
+ success = loadState(_saveLoadSlot, _saveTemporaryState);
+ if (!success)
+ errMsg = "Failed to load game state from file:\n\n%s";
+
+ // Ender: Disabled for small_header games, as can overwrite game
+ // variables (eg, Zak256 cashcard values).
+ if (success && _saveTemporaryState && !(_features & GF_SMALL_HEADER))
+ VAR(VAR_GAME_LOADED) = 203;
+ }
+
+ makeSavegameName(filename, _saveLoadSlot, _saveTemporaryState);
+ if (!success) {
+ displayMessage(0, errMsg, filename);
+ } else if (_saveLoadFlag == 1 && _saveLoadSlot != 0 && !_saveTemporaryState) {
+ // Display "Save successful" message, except for auto saves
+ char buf[256];
+ snprintf(buf, sizeof(buf), "Successfully saved game state in file:\n\n%s", filename);
+
+ GUI::TimedMessageDialog dialog(buf, 1500);
+ runDialog(dialog);
+ }
+ if (success && _saveLoadFlag != 1)
+ clearClickedStatus();
+
+ _saveLoadFlag = 0;
+ _lastSaveTime = _system->getMillis();
+ }
+
+ if (_completeScreenRedraw) {
+ _charset->clearCharsetMask();
+ _charset->_hasMask = false;
+
+ // HACK as in game save stuff isn't supported currently
+ if (_gameId == GID_LOOM) {
+ int args[16];
+ uint value;
+ memset(args, 0, sizeof(args));
+ args[0] = 2;
+
+ if (_platform == Common::kPlatformMacintosh)
+ value = 105;
+ else if (_version == 4) // 256 color CD version
+ value = 150;
+ else
+ value = 100;
+ byte restoreScript = (_platform == Common::kPlatformFMTowns) ? 17 : 18;
+ // if verbs should be shown restore them
+ if (VAR(value) == 2)
+ runScript(restoreScript, 0, 0, args);
+ } else if (_version > 3) {
+ for (int i = 0; i < _numVerbs; i++)
+ drawVerb(i, 0);
+ } else {
+ redrawVerbs();
+ }
+
+ handleMouseOver(false);
+
+ _completeScreenRedraw = false;
+ _fullRedraw = true;
+ }
+
+ if (_heversion >= 80) {
+ _sound->processSoundCode();
+ }
+ runAllScripts();
+ checkExecVerbs();
+ checkAndRunSentenceScript();
+
+ if (_quit)
+ return 0;
+
+ // HACK: If a load was requested, immediately perform it. This avoids
+ // drawing the current room right after the load is request but before
+ // it is performed. That was annoying esp. if you loaded while a SMUSH
+ // cutscene was playing.
+ if (_saveLoadFlag && _saveLoadFlag != 1) {
+ goto load_game;
+ }
+
+ if (_currentRoom == 0) {
+ if (_version > 3)
+ CHARSET_1();
+ drawDirtyScreenParts();
+ } else {
+ walkActors();
+ moveCamera();
+ updateObjectStates();
+ if (_version > 3)
+ CHARSET_1();
+
+ if (camera._cur.x != camera._last.x || _bgNeedsRedraw || _fullRedraw
+ || ((_features & GF_NEW_CAMERA) && camera._cur.y != camera._last.y)) {
+ redrawBGAreas();
+ }
+
+ processDrawQue();
+
+ if (_heversion >= 99)
+ _fullRedraw = false;
+
+ // Full Throttle always redraws verbs and draws verbs before actors
+ if (_version >= 7)
+ redrawVerbs();
+
+#ifndef DISABLE_HE
+ if (_heversion >= 90) {
+ ((ScummEngine_v90he *)this)->_sprite->resetBackground();
+ ((ScummEngine_v90he *)this)->_sprite->sortActiveSprites();
+ }
+#endif
+
+ setActorRedrawFlags();
+ resetActorBgs();
+
+ if (VAR_CURRENT_LIGHTS != 0xFF &&
+ !(VAR(VAR_CURRENT_LIGHTS) & LIGHTMODE_screen) &&
+ VAR(VAR_CURRENT_LIGHTS) & LIGHTMODE_flashlight) {
+ drawFlashlight();
+ setActorRedrawFlags();
+ }
+
+ processActors();
+
+ _fullRedraw = false;
+
+ if (_version >= 4 && _heversion <= 61)
+ cyclePalette();
+ palManipulate();
+ if (_doEffect) {
+ _doEffect = false;
+ fadeIn(_newEffect);
+ clearClickedStatus();
+ }
+
+ if (VAR_MAIN_SCRIPT != 0xFF && VAR(VAR_MAIN_SCRIPT) != 0) {
+ runScript(VAR(VAR_MAIN_SCRIPT), 0, 0, 0);
+ }
+
+ // Handle mouse over effects (for verbs).
+ handleMouseOver(oldEgo != VAR(VAR_EGO));
+
+ // Render everything to the screen.
+ drawDirtyScreenParts();
+
+ if (_version <= 5)
+ playActorSounds();
+ }
+
+ _sound->processSound();
+
+#ifndef DISABLE_SCUMM_7_8
+ if (_imuseDigital) {
+ _imuseDigital->flushTracks();
+ if ( ((_gameId == GID_DIG) && (!(_features & GF_DEMO))) || (_gameId == GID_CMI) )
+ _imuseDigital->refreshScripts();
+ }
+#endif
+
+ camera._last = camera._cur;
+
+ if (!(++res._expireCounter)) {
+ res.increaseResourceCounter();
+ }
+
+ animateCursor();
+
+ /* show or hide mouse */
+ _system->showMouse(_cursor.state > 0);
+
+#ifndef DISABLE_HE
+ if (_heversion >= 90) {
+ ((ScummEngine_v90he *)this)->_sprite->updateImages();
+ }
+ if (_heversion >= 98) {
+ ((ScummEngine_v90he *)this)->_logicHE->endOfFrame();
+ }
+#endif
+
+ if (VAR_TIMER != 0xFF)
+ VAR(VAR_TIMER) = 0;
+ return VAR(VAR_TIMER_NEXT);
+
+}
+
+#pragma mark -
+#pragma mark --- SCUMM ---
+#pragma mark -
+
+int ScummEngine::getHETimer(int timer) {
+ checkRange(15, 1, timer, "getHETimer: Timer out of range(%d)");
+ int time = _system->getMillis() - _heTimers[timer];
+ return time;
+}
+
+void ScummEngine::setHETimer(int timer) {
+ checkRange(15, 1, timer, "setHETimer: Timer out of range(%d)");
+ _heTimers[timer] = _system->getMillis();
+}
+
+void ScummEngine::pauseGame() {
+ pauseDialog();
+}
+
+void ScummEngine::shutDown() {
+ _quit = true;
+}
+
+void ScummEngine::restart() {
+// TODO: Check this function - we should probably be reinitting a lot more stuff, and I suspect
+// this leaks memory like a sieve
+
+// Fingolfing seez: An alternate way to implement restarting would be to create
+// a save state right after startup ... to this end we could introduce a SaveFile
+// subclass which is implemented using a memory buffer (i.e. no actual file is
+// created). Then to restart we just have to load that pseudo save state.
+
+
+ int i;
+
+ // Reset some stuff
+ _currentRoom = 0;
+ _currentScript = 0xFF;
+ killAllScriptsExceptCurrent();
+ setShake(0);
+ _sound->stopAllSounds();
+
+ // Clear the script variables
+ for (i = 0; i < _numVariables; i++)
+ _scummVars[i] = 0;
+
+ // Empty inventory
+ for (i = 0; i < _numGlobalObjects; i++)
+ clearOwnerOf(i);
+
+ // Reallocate arrays
+ allocateArrays();
+
+ // Reread index (reset objectstate etc)
+ readIndexFile();
+
+ // Reinit scumm variables
+ scummInit();
+ initScummVars();
+
+ if (_imuse) {
+ _imuse->setBase(res.address[rtSound]);
+ }
+
+ // Reinit sound engine
+ if (_version >= 5)
+ _sound->setupSound();
+
+ // Re-run bootscript
+ int args[16];
+ memset(args, 0, sizeof(args));
+ args[0] = _bootParam;
+ if (_gameId == GID_MANIAC && _demoMode)
+ runScript(9, 0, 0, args);
+ else
+ runScript(1, 0, 0, args);
+}
+
+void ScummEngine::startManiac() {
+ debug(0, "stub startManiac()");
+ displayMessage(0, "Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' directory inside the Tentacle game directory.");
+}
+
+#pragma mark -
+#pragma mark --- GUI ---
+#pragma mark -
+
+int ScummEngine::runDialog(Dialog &dialog) {
+ _dialogStartTime = _system->getMillis() / 1000;
+
+ // Pause sound & video
+ bool old_soundsPaused = _sound->_soundsPaused;
+ _sound->pauseSounds(true);
+ bool oldSmushPaused = _smushPaused;
+ _smushPaused = true;
+
+ // Open & run the dialog
+ int result = dialog.runModal();
+
+ // Restore old cursor
+ updateCursor();
+
+ // Resume sound & video
+ _sound->pauseSounds(old_soundsPaused);
+ _smushPaused = oldSmushPaused;
+
+ _engineStartTime += (_system->getMillis() / 1000) - _dialogStartTime;
+ _dialogStartTime = 0;
+
+ // Return the result
+ return result;
+}
+
+void ScummEngine::pauseDialog() {
+ if (!_pauseDialog)
+ _pauseDialog = new PauseDialog(this, 10);
+ runDialog(*_pauseDialog);
+}
+
+void ScummEngine::versionDialog() {
+ if (!_versionDialog)
+ _versionDialog = new PauseDialog(this, 11);
+ runDialog(*_versionDialog);
+}
+
+void ScummEngine::mainMenuDialog() {
+ if (!_mainMenuDialog)
+ _mainMenuDialog = new MainMenuDialog(this);
+ runDialog(*_mainMenuDialog);
+}
+
+void ScummEngine::confirmExitDialog() {
+ ConfirmDialog d(this, "Do you really want to quit (y/n)?");
+
+ if (runDialog(d)) {
+ _quit = true;
+ }
+}
+
+void ScummEngine::confirmRestartDialog() {
+ ConfirmDialog d(this, "Do you really want to restart (y/n)?");
+
+ if (runDialog(d)) {
+ restart();
+ }
+}
+
+char ScummEngine::displayMessage(const char *altButton, const char *message, ...) {
+ char buf[STRINGBUFLEN];
+ va_list va;
+
+ va_start(va, message);
+ vsnprintf(buf, STRINGBUFLEN, message, va);
+ va_end(va);
+
+ GUI::MessageDialog dialog(buf, "OK", altButton);
+ return runDialog(dialog);
+}
+
+#pragma mark -
+#pragma mark --- Miscellaneous ---
+#pragma mark -
+
+
+uint32 ScummEngine::fileReadDword() {
+#if defined(SCUMM_LITTLE_ENDIAN)
+ return _fileHandle->readUint32LE();
+#elif defined(SCUMM_BIG_ENDIAN)
+ return _fileHandle->readUint32BE();
+#endif
+}
+
+void ScummEngine::errorString(const char *buf1, char *buf2) {
+ if (_currentScript != 0xFF) {
+ ScriptSlot *ss = &vm.slot[_currentScript];
+ sprintf(buf2, "(%d:%d:0x%X): %s", _roomResource,
+ ss->number, _scriptPointer - _scriptOrgPointer, buf1);
+ } else {
+ strcpy(buf2, buf1);
+ }
+
+#ifdef _WIN32_WCE
+ if (isSmartphone())
+ return;
+#endif
+
+ // Unless an error -originated- within the debugger, spawn the debugger. Otherwise
+ // exit out normally.
+ if (_debugger && !_debugger->isAttached()) {
+ printf("%s\n", buf2); // (Print it again in case debugger segfaults)
+ _debugger->attach(buf2);
+ _debugger->onFrame();
+ }
+}
+
+int ScummEngine::generateSubstResFileName(const char *filename, char *buf, int bufsize, int index) {
+ if (index == -3)
+ index = _substResFileNameIndex;
+
+ return generateSubstResFileName_(filename, buf, bufsize, index);
+}
+
+
+} // End of namespace Scumm
+
+using namespace Scumm;
+
+GameList Engine_SCUMM_gameList() {
+ const ScummGameSettings *g = scumm_settings;
+ const ObsoleteGameIDs *o = obsoleteGameIDsTable;
+ GameList games;
+ while (g->gameid) {
+ games.push_back(g->toGameSettings());
+ g++;
+ }
+
+ while (o->from) {
+ games.push_back(o->toGameSettings());
+ o++;
+ }
+ return games;
+}
+
+enum {
+ kDetectNameMethodsCount = 8
+};
+
+static bool generateDetectName(const ScummGameSettings *g, int method, char *detectName) {
+ detectName[0] = '\0';
+
+ switch (method) {
+ case 0:
+ if (g->version > 3)
+ return false;
+ strcpy(detectName, "00.LFL");
+ break;
+ case 1:
+ if (g->version < 3 || g->version > 5)
+ return false;
+ strcpy(detectName, "000.LFL");
+ break;
+ case 2:
+ if (g->version < 4 || g->version > 7)
+ return false;
+ strcpy(detectName, g->gameid);
+ strcat(detectName, ".000");
+ break;
+ case 3:
+ if (g->version < 7)
+ return false;
+ strcpy(detectName, g->gameid);
+ strcat(detectName, ".la0");
+ break;
+ case 4:
+ if (g->heversion == 0)
+ return false;
+ strcpy(detectName, g->gameid);
+ strcat(detectName, ".he0");
+ break;
+ case 5:
+ // FIXME: Fingolfin asks: For which games is this case used?
+ // Please document this. Also: Why was this case missing in
+ // Engine_SCUMM_create ?
+ strcpy(detectName, g->gameid);
+ break;
+ case 6:
+ if (g->id != GID_SAMNMAX)
+ return false;
+ strcpy(detectName, g->gameid);
+ strcat(detectName, ".sm0");
+ break;
+ case 7:
+ if (g->id != GID_MANIAC)
+ return false;
+ strcpy(detectName, "00.MAN");
+ break;
+ }
+
+ return true;
+}
+
+
+DetectedGameList Engine_SCUMM_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+ const ScummGameSettings *g;
+ char detectName[128];
+ char tempName[128];
+ int substLastIndex = 0;
+
+ typedef Common::Map<Common::String, bool> StringSet;
+ StringSet fileSet;
+
+ for (g = scumm_settings; g->gameid; ++g) {
+ // Determine the 'detectname' for this game, that is, the name of a
+ // file that *must* be presented if the directory contains the data
+ // for this game. For example, FOA requires atlantis.000
+
+ // TODO: we need to add cache here
+ for (int method = 0; method < kDetectNameMethodsCount; method++) {
+ if (!generateDetectName(g, method, detectName))
+ continue;
+
+ strcpy(tempName, detectName);
+
+ substLastIndex = 0;
+
+ while (substLastIndex != -1) {
+ // Iterate over all files in the given directory
+ for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (!file->isDirectory()) {
+ const char *name = file->displayName().c_str();
+
+ if (0 == scumm_stricmp(detectName, name)) {
+ byte buf[6];
+
+ if (g->version < 4) {
+ // We take a look at the file now, to narrow
+ // down the list of possible candidates a bit further.
+ // E.g. it's trivial to distinguish V1 from V3 games.
+ File tmp;
+ if (!tmp.open(file->path().c_str()))
+ break;
+ tmp.read(buf, 6);
+
+ if (buf[0] == 0xCE && buf[1] == 0xF5) {
+ // Looks like V1. However, we currently do not distinguish between V1 and V2
+ // in the scumm_settings list.
+ if (g->version != 1 && g->version != 2)
+ break;
+
+ // Candidates: maniac clasic, zak classic
+
+ // TODO: Maybe we can use the filesize to distinguish these two?
+ // English V1 Zak: 1896 bytes
+ // English V1 MM: 1972 bytes
+ // It would be interesting if those sizes are the same for other language
+ // variants of these games, or for demos?
+ } else if (buf[0] == 0xFF && buf[1] == 0xFE) {
+ // GF_OLD_BUNDLE: could be V2 or old V3.
+ if (!(g->features & GF_OLD_BUNDLE) || (g->version != 2 && g->version != 3))
+ break;
+ // Candidates: maniac enhanced, zak enhanced, indy3ega, loom
+ /*
+ TODO: MIght be possible to distinguish those by the script count.
+ Specifically, my versions of these games have this in their headers:
+
+ Loom (en; de; en demo; en MAC):
+ _numGlobalObjects 1000
+ _numRooms 100
+ _numCostumes 200
+ _numScripts 200
+ _numSounds 80
+
+ Indy3EGA (en PC; en Mac; en demo):
+ _numGlobalObjects 1000
+ _numRooms 99
+ _numCostumes 129
+ _numScripts 139
+ _numSounds 84
+
+ MM (en; de):
+ _numGlobalObjects 780
+ _numRooms 61
+ _numCostumes 40
+ _numScripts 179
+ _numSounds 120
+
+ Zak (de; en demo):
+ _numGlobalObjects 780
+ _numRooms 61
+ _numCostumes 40
+ _numScripts 155
+ _numSounds 120
+
+ So, they all have a different number of scripts.
+ */
+ } else if (buf[4] == '0' && buf[5] == 'R') {
+ // newer V3 game
+ if (g->version != 3)
+ break;
+ // Candidates: indy3, indy3Towns, zakTowns, loomTowns
+ /*
+ Considering that we know about *all* TOWNS versions,
+ and know their MD5s, we could simply rely on this and
+ if we find something which has an unknown MD5, assume
+ that it is an (so far unknown) version of Indy3.
+
+ We can combin this with a look at the resource headers:
+
+ Indy3:
+ _numGlobalObjects 1000
+ _numRooms 99
+ _numCostumes 129
+ _numScripts 139
+ _numSounds 84
+
+ Indy3Towns, ZakTowns, ZakLoom demo:
+ _numGlobalObjects 1000
+ _numRooms 99
+ _numCostumes 199
+ _numScripts 199
+ _numSounds 199
+
+ Assuming that all the town variants look like the latter, we can
+ do the chceck like this:
+ if (numScripts == 139)
+ assume Indy3
+ else if (numScripts == 199)
+ assume towns game
+ else
+ unknown, do not accept it
+ */
+ } else if (buf[4] == 'R' && buf[5] == 'N') {
+ // V4 game
+ if (g->version != 4)
+ break;
+ // Candidates: monkeyEGA, pass, monkeyVGA, loomcd
+ /*
+ For all of them, we have:
+ _numGlobalObjects 1000
+ _numRooms 99
+ _numCostumes 199
+ _numScripts 199
+ _numSounds 199
+ */
+ } else if (buf[0] == 0xa0 && buf[1] == 0x07 && buf[2] == 0xa5 &&
+ buf[3] == 0xbc) {
+ // MM NES .prg
+ if (g->id != GID_MANIAC)
+ break;
+ } else if (buf[0] == 0xbc && buf[1] == 0xb9) {
+ // MM NES 00.LFL
+ if (g->id != GID_MANIAC)
+ break;
+ } else if (buf[0] == 0x31 && buf[1] == 0x0a) {
+ // C64 MM & Zak disk1
+ if (g->version != 2)
+ break;
+ } else if (buf[0] == 0xcd && buf[1] == 0xfe) {
+ // C64 MM & Zak 00.LFL
+ if (g->version != 2)
+ break;
+ } else {
+ // This is not a V1-V4 game
+ break;
+ }
+ }
+
+ // Match found, add to list of candidates, then abort inner loop.
+ if (substLastIndex > 0 && // HE Mac versions.
+ (substResFileNameTable[substLastIndex].genMethod == kGenMac ||
+ substResFileNameTable[substLastIndex].genMethod == kGenMacNoParens)) {
+ detectedGames.push_back(DetectedGame(g->toGameSettings(),
+ Common::UNK_LANG,
+ Common::kPlatformMacintosh));
+ fileSet[file->path()] = true;
+ } else if (substLastIndex == 0 && g->id == GID_MANIAC &&
+ (buf[0] == 0xbc || buf[0] == 0xa0)) {
+ detectedGames.push_back(DetectedGame(g->toGameSettings(),
+ Common::UNK_LANG,
+ Common::kPlatformNES));
+ } else if ((g->id == GID_MANIAC || g->id == GID_ZAK) &&
+ ((buf[0] == 0x31 && buf[1] == 0x0a) ||
+ (buf[0] == 0xcd && buf[1] == 0xfe))) {
+ detectedGames.push_back(DetectedGame(g->toGameSettings(),
+ Common::UNK_LANG,
+ Common::kPlatformC64));
+ } else {
+ detectedGames.push_back(g->toGameSettings());
+ fileSet[file->path()] = false;
+ }
+ break;
+ }
+ }
+ }
+
+ substLastIndex = generateSubstResFileName_(tempName, detectName, sizeof(detectName), substLastIndex+1);
+ }
+ }
+ }
+
+ // Now, we check the MD5 sums of the 'candidate' files. If we have an exact match,
+ // only return that.
+ bool exactMatch = false;
+ for (StringSet::const_iterator iter = fileSet.begin(); iter != fileSet.end(); ++iter) {
+ uint8 md5sum[16];
+ const char *name = iter->_key.c_str();
+
+ if (Common::md5_file(name, md5sum, 0, kMD5FileSizeLimit)) {
+ char md5str[32+1];
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+
+ const MD5Table *elem;
+ elem = (const MD5Table *)bsearch(md5str, md5table, ARRAYSIZE(md5table)-1, sizeof(MD5Table), compareMD5Table);
+ if (elem) {
+ if (!exactMatch)
+ detectedGames.clear(); // Clear all the non-exact candidates
+
+ const char *gameid = elem->gameid;
+
+ // Find the GameSettings for that gameid
+ for (g = scumm_settings; g->gameid; ++g) {
+ if (0 == scumm_stricmp(g->gameid, gameid))
+ break;
+ }
+ assert(g->gameid);
+ // Insert the 'enhanced' game data into the candidate list
+ if (iter->_value == true) // This was HE Mac game
+ detectedGames.push_back(DetectedGame(g->toGameSettings(), elem->language, Common::kPlatformMacintosh));
+ else
+ detectedGames.push_back(DetectedGame(g->toGameSettings(), elem->language, elem->platform));
+ exactMatch = true;
+ }
+ }
+ }
+
+ return detectedGames;
+}
+
+static int generateSubstResFileName_(const char *filename, char *buf, int bufsize, int index) {
+ if (index <= 0)
+ return -1;
+
+ size_t len = strlen(filename);
+ assert(len > 0);
+
+ char num = filename[len - 1];
+
+ // In some cases we have .(a) and .(b) extensions
+ if (num == ')')
+ num = filename[len - 2];
+
+ const char *ext = strrchr(filename, '.');
+ if (ext)
+ len = ext - filename;
+
+ for (int i = index; substResFileNameTable[i].winName; i++) {
+ if (!scumm_strnicmp(filename, substResFileNameTable[i].winName, len)) {
+ switch (substResFileNameTable[i].genMethod) {
+ case kGenMac:
+ case kGenMacNoParens:
+ if (num == '3') { // special case for cursors
+ // For mac they're stored in game binary
+ strncpy(buf, substResFileNameTable[i].macName, bufsize);
+ } else {
+ if (substResFileNameTable[i].genMethod == kGenMac)
+ snprintf(buf, bufsize, "%s (%c)", substResFileNameTable[i].macName, num);
+ else
+ snprintf(buf, bufsize, "%s %c", substResFileNameTable[i].macName, num);
+ }
+ break;
+
+ case kGenPC:
+ if (ext)
+ snprintf(buf, bufsize, "%s%s", substResFileNameTable[i].macName, ext);
+ else
+ strncpy(buf, substResFileNameTable[i].macName, bufsize);
+ break;
+
+ case kGenAsIs:
+ strncpy(buf, substResFileNameTable[i].macName, bufsize);
+ break;
+
+ default:
+ *buf = 0;
+ break;
+ }
+
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Create a ScummEngine instance, based on the given detector data.
+ *
+ * This is heavily based on our MD5 detection scheme.
+ */
+Engine *Engine_SCUMM_create(GameDetector *detector, OSystem *syst) {
+ Engine *engine;
+
+ // We start by checking whether the specified game ID is obsolete.
+ // If that is the case, we automaticlaly upgrade the target to use
+ // the correct new game ID (and platform, if specified).
+ const ObsoleteGameIDs *o = obsoleteGameIDsTable;
+ while (o->from) {
+ if (!scumm_stricmp(detector->_game.gameid, o->from)) {
+ // Match found, perform upgrade
+ detector->_game.gameid = o->to;
+ ConfMan.set("gameid", o->to);
+
+ if (o->platform != Common::kPlatformUnknown)
+ ConfMan.set("platform", Common::getPlatformCode(o->platform));
+
+ warning("Target upgraded from game ID %s to %s", o->from, o->to);
+ ConfMan.flushToDisk();
+ break;
+ }
+ o++;
+ }
+
+ // Lookup the game ID in our database. If this lookup fails, then
+ // the game ID is unknown, and we have to abort.
+ const ScummGameSettings *g = scumm_settings;
+ while (g->gameid) {
+ if (!scumm_stricmp(detector->_game.gameid, g->gameid))
+ break;
+ g++;
+ }
+ if (!g->gameid) {
+ return 0;
+ }
+
+ // We now want to alculate the MD5 of the games detection file, so that we
+ // can store it in savegames etc..
+ const char *gameid = g->gameid;
+ char detectName[256], tempName[256], gameMD5[32+1];
+ uint8 md5sum[16];
+ int substLastIndex = 0;
+ bool found = false;
+
+ ScummGameSettings game = *g;
+
+ // To this end, we first have to figure out what the proper detection file
+ // is (00.LFL, 000.LFL, ...). So we iterate over all possible names,
+ // and once we find a matching file, we assume that's it.
+ for (int method = 0; method < kDetectNameMethodsCount && !found; method++) {
+ if (!generateDetectName(g, method, detectName))
+ continue;
+
+ strcpy(tempName, detectName);
+
+ substLastIndex = 0;
+ while (substLastIndex != -1) {
+ // FIXME: Repeatedly calling File::exists like this is a bad idea.
+ // Instead, use the fs.h code to get a list of all files in that
+ // directory and simply check whether that filename is contained
+ // in it.
+ if (File::exists(detectName, ConfMan.get("path").c_str())) {
+ found = true;
+ break;
+ }
+
+ substLastIndex = generateSubstResFileName_(tempName, detectName, sizeof(detectName), substLastIndex + 1);
+ }
+ if (found) {
+ if (substLastIndex != 0)
+ debug(5, "Generated filename substitute: %s -> %s", tempName, detectName);
+ break;
+ }
+ }
+
+ // Unable to locate game data
+ if (!found) {
+ return 0;
+ }
+
+ // Force game to have Mac platform if needed
+ if (substLastIndex > 0) {
+ if (substResFileNameTable[substLastIndex].genMethod == kGenMac ||
+ substResFileNameTable[substLastIndex].genMethod == kGenMacNoParens)
+ game.platform = Common::kPlatformMacintosh;
+ }
+
+ // Compute the MD5 of the file, and (if we succeeded) store a hex version
+ // of it in gameMD5 (useful to print it to the user in messages).
+ if (Common::md5_file(detectName, md5sum, ConfMan.get("path").c_str(), kMD5FileSizeLimit)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(gameMD5 + j*2, "%02x", (int)md5sum[j]);
+ }
+ }
+
+ // Check if the MD5 was overwritten, if so, use that in the subsequent search
+ const char *md5;
+ if (ConfMan.hasKey("target_md5")) {
+ md5 = ConfMan.get("target_md5").c_str();
+ } else {
+ md5 = gameMD5;
+ }
+
+ // Now search our 'database' for the MD5; if a match is found, we use
+ // the information in the 'database' to correct the GameSettings.
+ g = multiple_versions_md5_settings;
+ while (g->gameid) {
+ if (!scumm_stricmp(md5, g->gameid)) {
+ // Match found
+ game = *g;
+ game.gameid = gameid; // FIXME: Fingolfin wonders what this line is good for?
+ if (game.description)
+ g_system->setWindowCaption(game.description);
+ break;
+ }
+ g++;
+ }
+
+ // Starting from version 7.1, HE games use 640x480. We check this here since multiple
+ // versions _could_ use different resolutions (I haven't verified this, though).
+ if (game.heversion >= 71) {
+ game.features |= GF_DEFAULT_TO_1X_SCALER;
+ }
+
+
+ // Check for a user override of the platform. We allow the user to override
+ // the platform, to make it possible to add games which are not yet in
+ // our MD5 database but require a specific platform setting.
+ if (ConfMan.hasKey("platform"))
+ game.platform = Common::parsePlatform(ConfMan.get("platform"));
+
+
+ // V3 FM-TOWNS games *always* should use the corresponding music driver,
+ // anything else makes no sense for them.
+ if (game.platform == Common::kPlatformFMTowns && game.version == 3) {
+ game.midi = MDT_TOWNS;
+ }
+
+ // Finally, we have massaged the GameSettings to our satisfaction, and can
+ // instantiate the appropriate game engine. Hooray!
+ switch (game.version) {
+ case 1:
+ case 2:
+ if (game.id == GID_MANIAC && game.platform == Common::kPlatformC64)
+ engine = new ScummEngine_c64(detector, syst, game, md5sum, substLastIndex);
+ else
+ engine = new ScummEngine_v2(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 3:
+ if (game.features & GF_OLD_BUNDLE)
+ engine = new ScummEngine_v3old(detector, syst, game, md5sum, substLastIndex);
+ else
+ engine = new ScummEngine_v3(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 4:
+ engine = new ScummEngine_v4(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 5:
+ engine = new ScummEngine_v5(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 6:
+ switch (game.heversion) {
+#ifndef DISABLE_HE
+ case 100:
+ engine = new ScummEngine_v100he(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 99:
+ engine = new ScummEngine_v99he(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 98:
+ case 95:
+ case 90:
+ engine = new ScummEngine_v90he(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 80:
+ engine = new ScummEngine_v80he(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 73:
+ case 72:
+ engine = new ScummEngine_v72he(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 71:
+ engine = new ScummEngine_v71he(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 70:
+ engine = new ScummEngine_v70he(detector, syst, game, md5sum, substLastIndex);
+ break;
+#endif
+#ifndef PALMOS_68K
+ case 61:
+ engine = new ScummEngine_v60he(detector, syst, game, md5sum, substLastIndex);
+ break;
+#endif
+ default:
+ engine = new ScummEngine_v6(detector, syst, game, md5sum, substLastIndex);
+ }
+ break;
+#ifndef DISABLE_SCUMM_7_8
+ case 7:
+ engine = new ScummEngine_v7(detector, syst, game, md5sum, substLastIndex);
+ break;
+ case 8:
+ engine = new ScummEngine_v8(detector, syst, game, md5sum, substLastIndex);
+ break;
+#endif
+ default:
+ error("Engine_SCUMM_create(): Unknown version of game engine");
+ }
+
+ return engine;
+}
+
+REGISTER_PLUGIN(SCUMM, "Scumm Engine")
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Scumm_md5table)
+_GSETPTR(md5table, GBVARS_MD5TABLE_INDEX, MD5Table, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(Scumm_md5table)
+_GRELEASEPTR(GBVARS_MD5TABLE_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
new file mode 100644
index 0000000000..5509378371
--- /dev/null
+++ b/engines/scumm/scumm.h
@@ -0,0 +1,1369 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCUMM_H
+#define SCUMM_H
+
+#include "base/engine.h"
+#include "common/file.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "graphics/surface.h"
+
+#include "scumm/gfx.h"
+#include "scumm/script.h"
+
+#include "sound/mididrv.h"
+
+namespace GUI {
+ class Dialog;
+}
+using GUI::Dialog;
+class GameDetector;
+namespace Common {
+ class InSaveFile;
+ class OutSaveFile;
+}
+
+namespace Scumm {
+
+class Actor;
+class BaseCostumeLoader;
+class BaseCostumeRenderer;
+class BaseScummFile;
+class CharsetRenderer;
+class IMuse;
+class IMuseDigital;
+class Insane;
+class MusicEngine;
+class ScummEngine;
+class ScummDebugger;
+class Serializer;
+class Sound;
+
+struct Box;
+struct BoxCoords;
+struct FindObjectInRoom;
+struct ScummGameSettings;
+
+// Use g_scumm from error() ONLY
+extern ScummEngine *g_scumm;
+
+/* System Wide Constants */
+enum {
+ NUM_SENTENCE = 6,
+ NUM_SHADOW_PALETTE = 8
+};
+
+/**
+ * SCUMM feature flags define for every game which specific set of engine
+ * features are used by that game.
+ * Note that some of them could be replaced by checks for the SCUMM version.
+ */
+enum GameFeatures {
+ /** Games with the new camera system (ScummEngine_v7 and subclasses). */
+ GF_NEW_CAMERA = 1 << 1,
+
+ /** Games with the AKOS costume system (ScummEngine_v7 and subclasses, HE games). */
+ GF_NEW_COSTUMES = 1 << 2,
+
+ /** Games with digital iMUSE (ScummEngine_v7 and subclasses). */
+ GF_DIGI_IMUSE = 1 << 3,
+
+ /** Games using XOR encrypted data files. */
+ GF_USE_KEY = 1 << 4,
+
+ /** Small header games (ScummEngine_v4 and subclasses). */
+ GF_SMALL_HEADER = 1 << 5,
+
+ /** Old bundle games (ScummEngine_v3old and subclasses). */
+ GF_OLD_BUNDLE = 1 << 6,
+
+ /** EGA games. */
+ GF_16COLOR = 1 << 7,
+
+ /** VGA versions of V3 games. Equivalent to (version == 3 && not GF_16COLOR) */
+ GF_OLD256 = 1 << 8,
+
+ /** Games which have Audio CD tracks. */
+ GF_AUDIOTRACKS = 1 << 9,
+
+ /** Games without actor scaling (ScummEngine_v3 and subclasses). */
+ GF_NO_SCALING = 1 << 10,
+
+ /**
+ * Games using only very few local variables in scripts.
+ * Apparently that is only the case for 256 color version of Indy3.
+ */
+ GF_FEW_LOCALS = 1 << 11,
+
+ /** HE games without cursor resources */
+ GF_HE_CURSORLESS = 1 << 12,
+
+ /** HE games for which localized versions exist */
+ GF_HE_LOCALIZED = 1 << 13,
+
+ /**
+ * HE Games with more global scripts and different sprite handling
+ * i.e. read it as HE version 9.85. Used for HE98 only.
+ */
+ GF_HE_985 = 1 << 14,
+
+ /** HE games with 16 bit color */
+ GF_16BIT_COLOR = 1 << 15,
+
+ /** HE games which use sprites for subtitles */
+ GF_HE_NOSUBTITLES = 1 << 16,
+
+ /** A demo, not a full blown game. */
+ GF_DEMO = 1 << 17
+};
+
+/* SCUMM Debug Channels */
+void CDECL debugC(int level, const char *s, ...);
+
+struct dbgChannelDesc {
+ const char *channel, *desc;
+ uint32 flag;
+};
+
+enum {
+ DEBUG_GENERAL = 1 << 0, // General debug
+ DEBUG_SCRIPTS = 1 << 2, // Track script execution (start/stop/pause)
+ DEBUG_OPCODES = 1 << 3, // Track opcode invocations
+ DEBUG_VARS = 1 << 4, // Track variable changes
+ DEBUG_RESOURCE = 1 << 5, // Track resource loading / allocation
+ DEBUG_IMUSE = 1 << 6, // Track iMUSE events
+ DEBUG_SOUND = 1 << 7, // General Sound Debug
+ DEBUG_ACTORS = 1 << 8, // General Actor Debug
+ DEBUG_INSANE = 1 << 9, // Track INSANE
+ DEBUG_SMUSH = 1 << 10 // Track SMUSH
+};
+
+/**
+ * Internal header for any memory block allocated by the resource manager.
+ *
+ * @todo Hide MemBlkHeader; no code outside the resource manager should
+ * have to use it, ever. Currently script code needs it to detect whether
+ * some scripts have moved (in fetchScriptByte()).
+ */
+struct MemBlkHeader {
+ uint32 size;
+};
+
+struct VerbSlot;
+struct ObjectData;
+
+enum {
+ LIGHTMODE_dark = 0,
+ LIGHTMODE_actor_base = 1,
+ LIGHTMODE_screen = 2,
+ LIGHTMODE_flashlight = 4,
+ LIGHTMODE_actor_color = 8
+};
+
+enum {
+ MBS_LEFT_CLICK = 0x8000,
+ MBS_RIGHT_CLICK = 0x4000,
+ MBS_MOUSE_MASK = (MBS_LEFT_CLICK | MBS_RIGHT_CLICK),
+ MBS_MAX_KEY = 0x0200
+};
+
+enum ScummGameId {
+ GID_CMI,
+ GID_DIG,
+ GID_FT,
+ GID_INDY3,
+ GID_INDY4,
+ GID_LOOM,
+ GID_MANIAC,
+ GID_MONKEY_EGA,
+ GID_MONKEY_VGA,
+ GID_MONKEY,
+ GID_MONKEY2,
+ GID_PASS,
+ GID_SAMNMAX,
+ GID_TENTACLE,
+ GID_ZAK,
+
+ GID_HEGAME, // Generic name for all HE games with default behaviour
+ GID_PUTTDEMO,
+ GID_FBEAR,
+ GID_FUNPACK,
+ GID_WATER,
+ GID_PUTTRACE,
+ GID_FUNSHOP, // Used for all three funshops
+ GID_FOOTBALL
+};
+
+struct SentenceTab {
+ byte verb;
+ byte preposition;
+ uint16 objectA;
+ uint16 objectB;
+ uint8 freezeCount;
+};
+
+struct StringSlot {
+ int16 xpos;
+ int16 ypos;
+ int16 right;
+ int16 height;
+ byte color;
+ byte charset;
+ bool center;
+ bool overhead;
+ bool no_talk_anim;
+};
+
+struct StringTab : StringSlot {
+ // The 'default' values for this string slot. This is used so that the
+ // string slot can temporarily be set to different values, and then be
+ // easily reset to a previously set default.
+ StringSlot _default;
+
+ void saveDefault() {
+ StringSlot &s = *this;
+ _default = s;
+ }
+
+ void loadDefault() {
+ StringSlot &s = *this;
+ s = _default;
+ }
+};
+
+
+
+enum WhereIsObject {
+ WIO_NOT_FOUND = -1,
+ WIO_INVENTORY = 0,
+ WIO_ROOM = 1,
+ WIO_GLOBAL = 2,
+ WIO_LOCAL = 3,
+ WIO_FLOBJECT = 4
+};
+
+struct AuxBlock {
+ bool visible;
+ Common::Rect r;
+
+ void reset() {
+ visible = false;
+ r.left = r.top = 0;
+ r.right = r.bottom = -1;
+ }
+};
+
+struct AuxEntry {
+ int actorNum;
+ int subIndex;
+};
+
+// TODO: Rename InfoStuff to something more descriptive
+struct InfoStuff {
+ uint32 date;
+ uint16 time;
+ uint32 playtime;
+};
+
+/**
+ * A list of resource types.
+ * WARNING: Do not change the order of these, as the savegame format relies
+ * on it; any change made here will break savegame compatibility!
+ */
+enum ResTypes {
+ rtFirst = 1,
+ rtRoom = 1,
+ rtScript = 2,
+ rtCostume = 3,
+ rtSound = 4,
+ rtInventory = 5,
+ rtCharset = 6,
+ rtString = 7,
+ rtVerb = 8,
+ rtActorName = 9,
+ rtBuffer = 10,
+ rtScaleTable = 11,
+ rtTemp = 12,
+ rtFlObject = 13,
+ rtMatrix = 14,
+ rtBox = 15,
+ rtObjectName = 16,
+ rtRoomScripts = 17,
+ rtRoomImage = 18,
+ rtImage = 19,
+ rtTalkie = 20,
+ rtSpoolBuffer = 21,
+ rtLast = 21,
+ rtNumTypes = 22
+};
+
+class ResourceManager {
+ friend class ScummDebugger;
+ friend class ScummEngine;
+protected:
+ ScummEngine *_vm;
+
+public:
+ byte mode[rtNumTypes];
+ uint16 num[rtNumTypes];
+ uint32 tags[rtNumTypes];
+ const char *name[rtNumTypes];
+ byte **address[rtNumTypes];
+protected:
+ byte *flags[rtNumTypes];
+ byte *status[rtNumTypes];
+public:
+ byte *roomno[rtNumTypes];
+ uint32 *roomoffs[rtNumTypes];
+ uint32 *globsize[rtNumTypes];
+
+ uint32 _allocatedSize;
+ uint32 _maxHeapThreshold, _minHeapThreshold;
+ byte _expireCounter;
+
+public:
+ ResourceManager(ScummEngine *vm);
+
+ byte *createResource(int type, int index, uint32 size);
+ void nukeResource(int type, int i);
+
+ void freeResources();
+
+ bool isResourceLoaded(int type, int index) const;
+
+ void lock(int type, int i);
+ void unlock(int type, int i);
+ bool isLocked(int type, int i) const;
+
+ void setModified(int type, int i);
+ bool isModified(int type, int i) const;
+
+ void setResourceCounter(int type, int index, byte flag);
+ void increaseResourceCounter();
+
+ void resourceStats();
+ void expireResources(uint32 size);
+
+protected:
+ bool validateResource(const char *str, int type, int index) const;
+};
+
+class ScummEngine : public Engine {
+ friend class ScummDebugger;
+ friend class SmushPlayer;
+ friend class Insane;
+ friend class CharsetRenderer;
+ friend class ResourceManager;
+
+ void errorString(const char *buf_input, char *buf_output);
+public:
+ /* Put often used variables at the top.
+ * That results in a shorter form of the opcode
+ * on some architectures. */
+ IMuse *_imuse;
+ IMuseDigital *_imuseDigital;
+ MusicEngine *_musicEngine;
+ Sound *_sound;
+
+ VerbSlot *_verbs;
+ ObjectData *_objs;
+ ObjectData *_storedFlObjects;
+ ScummDebugger *_debugger;
+
+ // Core variables
+ byte _gameId;
+ byte _version;
+ uint8 _heversion;
+ uint32 _features; // Should only be accessed for reading (TODO enforce it compiler-wise with making it private and creating an accessor)
+ Common::Platform _platform;
+ uint8 _gameMD5[16];
+
+ /** Random number generator */
+ Common::RandomSource _rnd;
+
+ /** Graphics manager */
+ Gdi gdi;
+
+ /** Central resource data. */
+ ResourceManager res;
+
+protected:
+ VirtualMachineState vm;
+
+public:
+ // Constructor / Destructor
+ ScummEngine(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+ virtual ~ScummEngine();
+
+ /** Startup function, main loop. */
+ int go();
+
+ // Init functions
+ int init(GameDetector &detector);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+
+ virtual void scummInit();
+
+ void loadCJKFont();
+ void setupMusic(int midi);
+ void setupVolumes();
+
+ // Scumm main loop
+ int scummLoop(int delta);
+
+ // Event handling
+ void parseEvents();
+ void waitForTimer(int msec_delay);
+ void processKbd(bool smushMode);
+ void clearClickedStatus();
+
+ // Misc utility functions
+ uint32 _debugFlags;
+ const char *getBaseName() const { return _baseName.c_str(); }
+
+ // Cursor/palette
+ void updateCursor();
+ virtual void animateCursor() {}
+ virtual void updatePalette();
+
+ /**
+ * Flag which signals that the SMUSH video playback should end now
+ * (e.g. because it was aborted by the user or it's simply finished).
+ */
+ bool _smushVideoShouldFinish;
+ /** This flag is a hack to allow the pause dialog to pause SMUSH playback, too. */
+ bool _smushPaused;
+ /** This flag tells IMuseDigital that INSANE is running. */
+ bool _insaneRunning;
+
+protected:
+ Insane *_insane;
+
+public:
+ void pauseGame();
+ void restart();
+ void shutDown();
+
+ /** We keep running until this is set to true. */
+ bool _quit;
+
+protected:
+ Dialog *_pauseDialog;
+ Dialog *_versionDialog;
+ Dialog *_mainMenuDialog;
+
+ int runDialog(Dialog &dialog);
+ void confirmExitDialog();
+ void confirmRestartDialog();
+ void pauseDialog();
+ void versionDialog();
+ void mainMenuDialog();
+
+ char displayMessage(const char *altButton, const char *message, ...);
+
+ byte _fastMode;
+
+ byte _numActors;
+ Actor *_actors; // Has _numActors elements
+ Actor **_sortedActors;
+
+ byte *_arraySlot;
+ uint16 *_inventory;
+ uint16 *_newNames;
+public:
+ // VAR is a wrapper around scummVar, which attempts to include additional
+ // useful information should an illegal var access be detected.
+ #define VAR(x) scummVar(x, #x, __FILE__, __LINE__)
+ int32& scummVar(byte var, const char *varName, const char *file, int line)
+ {
+ if (var == 0xFF) {
+ error("Illegal access to variable %s in file %s, line %d", varName, file, line);
+ }
+ return _scummVars[var];
+ }
+ int32 scummVar(byte var, const char *varName, const char *file, int line) const
+ {
+ if (var == 0xFF) {
+ error("Illegal access to variable %s in file %s, line %d", varName, file, line);
+ }
+ return _scummVars[var];
+ }
+
+protected:
+ int16 _varwatch;
+ int32 *_roomVars;
+ int32 *_scummVars;
+ byte *_bitVars;
+
+ /* Global resource tables */
+ int _numVariables, _numBitVariables, _numLocalObjects;
+ int _numGlobalObjects, _numArray, _numVerbs, _numFlObject;
+ int _numInventory;
+ int _numNewNames, _numGlobalScripts;
+ int _numRoomVariables;
+ int _numPalettes, _numSprites, _numTalkies, _numUnk;
+ int _numStoredFlObjects;
+ int _HEHeapSize;
+public:
+ int _numLocalScripts, _numImages, _numRooms, _numScripts, _numSounds; // Used by HE games
+ int _numCostumes; // FIXME - should be protected, used by Actor::remapActorPalette
+ int _numCharsets; // FIXME - should be protected, used by CharsetRenderer
+
+ BaseCostumeLoader *_costumeLoader;
+ BaseCostumeRenderer *_costumeRenderer;
+
+ int _NESCostumeSet;
+ void NES_loadCostumeSet(int n);
+ byte *_NEScostdesc, *_NEScostlens, *_NEScostoffs, *_NEScostdata;
+ byte _NESPatTable[2][4096];
+ byte _NESPalette[2][16];
+ byte _NESBaseTiles;
+
+ int _NESStartStrip;
+
+protected:
+ /* Current objects - can go in their respective classes */
+ byte _curActor;
+ int _curVerb;
+ int _curVerbSlot;
+ int _curPalIndex;
+
+public:
+ byte _currentRoom; // FIXME - should be protected but Actor::isInCurrentRoom uses it
+ int _roomResource; // FIXME - should be protected but Sound::pauseSounds uses it
+ bool _egoPositioned; // Used by Actor::putActor, hence public
+
+ int generateSubstResFileName(const char *filename, char *buf, int bufsize, int index = -3);
+ int _substResFileNameIndex;
+ int _substResFileNameIndexBundle; // Used with Mac bundles
+
+protected:
+ int _keyPressed;
+ uint16 _lastKeyHit;
+ bool _keyDownMap[512]; // FIXME - 512 is a guess. it's max(kbd.ascii)
+
+ Common::Point _mouse;
+ Common::Point _virtualMouse;
+
+ uint16 _mouseAndKeyboardStat;
+ byte _leftBtnPressed, _rightBtnPressed;
+
+ /** The bootparam, to be passed to the script 1, the bootscript. */
+ int _bootParam;
+
+ // Various options useful for debugging
+ bool _dumpScripts;
+ bool _hexdumpScripts;
+ bool _showStack;
+ uint16 _debugMode;
+
+ // Save/Load class - some of this may be GUI
+ byte _saveLoadFlag, _saveLoadSlot;
+ uint32 _lastSaveTime;
+ bool _saveTemporaryState;
+ char _saveLoadName[32];
+
+ bool saveState(int slot, bool compat);
+ bool loadState(int slot, bool compat);
+ virtual void saveOrLoad(Serializer *s);
+ void saveLoadResource(Serializer *ser, int type, int index); // "Obsolete"
+ void saveResource(Serializer *ser, int type, int index);
+ void loadResource(Serializer *ser, int type, int index);
+ void makeSavegameName(char *out, int slot, bool temporary);
+
+ int getKeyState(int key);
+
+public:
+ bool getSavegameName(int slot, char *desc);
+ void listSavegames(bool *marks, int num);
+
+ void requestSave(int slot, const char *name, bool temporary = false);
+ void requestLoad(int slot);
+
+// thumbnail + info stuff
+public:
+ Graphics::Surface *loadThumbnailFromSlot(int slot);
+ bool loadInfosFromSlot(int slot, InfoStuff *stuff);
+
+protected:
+ Graphics::Surface *loadThumbnail(Common::InSaveFile *file);
+ bool loadInfos(Common::InSaveFile *file, InfoStuff *stuff);
+ void saveThumbnail(Common::OutSaveFile *file);
+ void saveInfos(Common::OutSaveFile* file);
+
+ int32 _engineStartTime;
+ int32 _dialogStartTime;
+
+protected:
+ /* Script VM - should be in Script class */
+ uint32 _localScriptOffsets[1024];
+ const byte *_scriptPointer, *_scriptOrgPointer;
+ byte _opcode, _currentScript;
+ const byte * const *_lastCodePtr;
+ int _resultVarNumber, _scummStackPos;
+ int _vmStack[150];
+ int _keyScriptKey, _keyScriptNo;
+
+ // See the ScummEngine constructor and checkAndRunSentenceScript()
+ int _defaultFTSentenceScript, _buggyFTSentenceScript;
+
+ virtual void setupOpcodes() = 0;
+ virtual void executeOpcode(byte i) = 0;
+ virtual const char *getOpcodeDesc(byte i) = 0;
+
+ void initializeLocals(int slot, int *vars);
+ int getScriptSlot();
+
+ void startScene(int room, Actor *a, int b);
+ void startManiac();
+
+public:
+ void runScript(int script, bool freezeResistant, bool recursive, int *lvarptr, int cycle = 0);
+ void stopScript(int script);
+ void nukeArrays(byte scriptSlot);
+
+protected:
+ void runObjectScript(int script, int entry, bool freezeResistant, bool recursive, int *vars, int slot = -1, int cycle = 0);
+ void runScriptNested(int script);
+ void executeScript();
+ void updateScriptPtr();
+ virtual void runInventoryScript(int i);
+ void inventoryScript();
+ void checkAndRunSentenceScript();
+ void runExitScript();
+ void runEntryScript();
+ void runAllScripts();
+ void freezeScripts(int scr);
+ void unfreezeScripts();
+
+ bool isScriptInUse(int script) const;
+ bool isRoomScriptRunning(int script) const;
+ bool isScriptRunning(int script) const;
+
+ void killAllScriptsExceptCurrent();
+ void killScriptsAndResources();
+ void decreaseScriptDelay(int amount);
+
+ void stopObjectCode();
+ void stopObjectScript(int script);
+
+ void getScriptBaseAddress();
+ void getScriptEntryPoint();
+ int getVerbEntrypoint(int obj, int entry);
+
+ byte fetchScriptByte();
+ virtual uint fetchScriptWord();
+ virtual int fetchScriptWordSigned();
+ uint fetchScriptDWord();
+ int fetchScriptDWordSigned();
+ void ignoreScriptWord() { fetchScriptWord(); }
+ void ignoreScriptByte() { fetchScriptByte(); }
+ virtual void getResultPos();
+ void setResult(int result);
+ void push(int a);
+ int pop();
+ virtual int readVar(uint var);
+ virtual void writeVar(uint var, int value);
+
+ void beginCutscene(int *args);
+ void endCutscene();
+ void abortCutscene();
+ void beginOverride();
+ void endOverride();
+
+ void copyScriptString(byte *dst);
+ int resStrLen(const byte *src) const;
+ void doSentence(int c, int b, int a);
+
+ /* Should be in Resource class */
+ BaseScummFile *_fileHandle;
+ uint32 _fileOffset;
+public:
+ /** The name of the (macintosh/rescumm style) container file, if any. */
+ Common::String _containerFile;
+
+ bool openFile(BaseScummFile &file, const char *filename, bool resourceFile = false);
+
+protected:
+ int _resourceHeaderSize;
+ Common::String _baseName; // This is the name we use for opening resource files
+ Common::String _targetName; // This is the game the user calls it, so use for saving
+ byte _resourceMapper[128];
+ byte *_heV7DiskOffsets;
+ uint32 *_heV7RoomIntOffsets;
+ const byte *_resourceLastSearchBuf; // FIXME: need to put it to savefile?
+ uint32 _resourceLastSearchSize; // FIXME: need to put it to savefile?
+
+ virtual void allocateArrays();
+ void openRoom(int room);
+ void closeRoom();
+ void deleteRoomOffsets();
+ virtual void readRoomsOffsets();
+ void askForDisk(const char *filename, int disknum);
+ bool openResourceFile(const char *filename, byte encByte);
+
+ void loadPtrToResource(int type, int i, const byte *ptr);
+ virtual void readResTypeList(int id, uint32 tag, const char *name);
+ void allocResTypeData(int id, uint32 tag, int num, const char *name, int mode);
+// byte *createResource(int type, int index, uint32 size);
+ int loadResource(int type, int i);
+// void nukeResource(int type, int i);
+ int getResourceSize(int type, int idx);
+
+public:
+ byte *getResourceAddress(int type, int i);
+ virtual byte *getStringAddress(int i);
+ byte *getStringAddressVar(int i);
+ void ensureResourceLoaded(int type, int i);
+ int getResourceRoomNr(int type, int index);
+
+protected:
+ int readSoundResource(int type, int index);
+ int convert_extraflags(byte *ptr, byte * src_ptr);
+ void convertMac0Resource(int type, int index, byte *ptr, int size);
+ void convertADResource(int type, int index, byte *ptr, int size);
+ int readSoundResourceSmallHeader(int type, int index);
+ bool isResourceInUse(int type, int i) const;
+ virtual void loadRoomSubBlocks();
+ virtual void initRoomSubBlocks();
+ void clearRoomObjects();
+ void storeFlObject(int slot);
+ void restoreFlObjects();
+ virtual void loadRoomObjects();
+
+ virtual void readArrayFromIndexFile();
+ virtual void readMAXS(int blockSize) = 0;
+ virtual void readGlobalObjects();
+ virtual void readIndexFile();
+ virtual void readIndexBlock(uint32 block, uint32 itemsize);
+ virtual void loadCharset(int i);
+ void nukeCharset(int i);
+
+ int _lastLoadedRoom;
+public:
+ const byte *findResourceData(uint32 tag, const byte *ptr);
+ const byte *findResource(uint32 tag, const byte *ptr);
+ int getResourceDataSize(const byte *ptr) const;
+ void dumpResource(const char *tag, int index, const byte *ptr, int length = -1);
+
+public:
+ /* Should be in Object class */
+ byte OF_OWNER_ROOM;
+ int getInventorySlot();
+ int findInventory(int owner, int index);
+ int getInventoryCount(int owner);
+
+protected:
+ byte *_objectOwnerTable, *_objectRoomTable, *_objectStateTable;
+ int _numObjectsInRoom;
+
+public:
+ uint32 *_classData;
+
+protected:
+ virtual void setupRoomObject(ObjectData *od, const byte *room, const byte *searchptr = NULL);
+ void markObjectRectAsDirty(int obj);
+ void loadFlObject(uint object, uint room);
+ void nukeFlObjects(int min, int max);
+ int findFlObjectSlot();
+ int findLocalObjectSlot();
+ void addObjectToInventory(uint obj, uint room);
+ void updateObjectStates();
+public:
+ bool getClass(int obj, int cls) const; // Used in actor.cpp, hence public
+protected:
+ void putClass(int obj, int cls, bool set);
+ int getState(int obj);
+ void putState(int obj, int state);
+ void setObjectState(int obj, int state, int x, int y);
+ int getOwner(int obj) const;
+ void putOwner(int obj, int owner);
+ void setOwnerOf(int obj, int owner);
+ void clearOwnerOf(int obj);
+ int getObjectRoom(int obj) const;
+ int getObjX(int obj);
+ int getObjY(int obj);
+ void getObjectXYPos(int object, int &x, int &y) { int dir; getObjectXYPos(object, x, y, dir); }
+ void getObjectXYPos(int object, int &x, int &y, int &dir);
+ int getObjOldDir(int obj);
+ int getObjNewDir(int obj);
+ int getObjectIndex(int object) const;
+ int getObjectImageCount(int object);
+ int whereIsObject(int object) const;
+ int findObject(int x, int y);
+ void findObjectInRoom(FindObjectInRoom *fo, byte findWhat, uint object, uint room);
+public:
+ int getObjectOrActorXY(int object, int &x, int &y); // Used in actor.cpp, hence public
+protected:
+ int getObjActToObjActDist(int a, int b); // Not sure how to handle
+ const byte *getObjOrActorName(int obj); // these three..
+ void setObjectName(int obj);
+
+ void addObjectToDrawQue(int object);
+ void removeObjectFromDrawQue(int object);
+ void clearDrawObjectQueue();
+ void processDrawQue();
+
+ virtual void clearDrawQueues();
+
+ uint32 getOBCDOffs(int object) const;
+ byte *getOBCDFromObject(int obj);
+ const byte *getOBIMFromObjectData(const ObjectData &od);
+ const byte *getObjectImage(const byte *ptr, int state);
+ virtual int getObjectIdFromOBIM(const byte *obim);
+
+ int getDistanceBetween(bool is_obj_1, int b, int c, bool is_obj_2, int e, int f);
+
+protected:
+ /* Should be in Verb class */
+ uint16 _verbMouseOver;
+ int _inventoryOffset;
+ int8 _userPut;
+ uint16 _userState;
+
+ int _activeObject;
+
+ virtual void handleMouseOver(bool updateInventory);
+ virtual void redrawVerbs();
+ virtual void checkExecVerbs();
+
+ void verbMouseOver(int verb);
+ int findVerbAtPos(int x, int y) const;
+ virtual void drawVerb(int verb, int mode);
+ void runInputScript(int a, int cmd, int mode);
+ void restoreVerbBG(int verb);
+ void drawVerbBitmap(int verb, int x, int y);
+ int getVerbSlot(int id, int mode) const;
+ void killVerb(int slot);
+ void setVerbObject(uint room, uint object, uint verb);
+
+public:
+ bool isValidActor(int id) const;
+
+ /* Should be in Actor class */
+ Actor *derefActor(int id, const char *errmsg = 0) const;
+ Actor *derefActorSafe(int id, const char *errmsg) const;
+
+ int getAngleFromPos(int x, int y) const;
+
+protected:
+ void walkActors();
+ void playActorSounds();
+ void redrawAllActors();
+ void setActorRedrawFlags();
+ void putActors();
+ void showActors();
+ void setupV1ActorTalkColor();
+ void resetActorBgs();
+ virtual void processActors();
+ void processUpperActors();
+ virtual int getActorFromPos(int x, int y);
+
+public:
+ /* Actor talking stuff */
+ byte _actorToPrintStrFor, _V1TalkingActor;
+ int _sentenceNum;
+ SentenceTab _sentence[NUM_SENTENCE];
+ StringTab _string[6];
+ int16 _talkDelay;
+ int _NES_lastTalkingActor;
+ int _NES_talkColor;
+
+ virtual void actorTalk(const byte *msg);
+ void stopTalk();
+ int getTalkingActor(); // Wrapper around VAR_TALK_ACTOR for V1 Maniac
+ void setTalkingActor(int variable);
+
+ // Generic costume code
+ bool isCostumeInUse(int i) const;
+
+ // Akos Class
+ struct {
+ int16 cmd;
+ int16 actor;
+ int16 param1;
+ int16 param2;
+ } _akosQueue[32];
+ int16 _akosQueuePos;
+
+ Common::Rect _actorClipOverride;
+
+ bool akos_increaseAnims(const byte *akos, Actor *a);
+ bool akos_increaseAnim(Actor *a, int i, const byte *aksq, const uint16 *akfo, int numakfo);
+ void akos_queCommand(byte cmd, Actor *a, int param_1, int param_2);
+ virtual void akos_processQueue();
+
+protected:
+ /* Should be in Graphics class? */
+ uint16 _screenB, _screenH;
+public:
+ int _roomHeight, _roomWidth;
+ int _screenHeight, _screenWidth;
+ VirtScreen virtscr[4]; // Virtual screen areas
+ CameraData camera; // 'Camera' - viewport
+
+ int _screenStartStrip, _screenEndStrip;
+ int _screenTop;
+
+ Common::RenderMode _renderMode;
+
+protected:
+ ColorCycle _colorCycle[16]; // Palette cycles
+ uint8 _colorUsedByCycle[256];
+
+ uint32 _ENCD_offs, _EXCD_offs;
+ uint32 _CLUT_offs, _EPAL_offs;
+ uint32 _IM00_offs, _PALS_offs;
+
+ //ender: fullscreen
+ bool _fullRedraw, _bgNeedsRedraw;
+ bool _screenEffectFlag, _completeScreenRedraw;
+
+ struct {
+ int hotspotX, hotspotY, width, height;
+ byte animate, animateIndex;
+ int8 state;
+ } _cursor;
+ byte _grabbedCursor[8192];
+ byte _currentCursor;
+
+ byte _newEffect, _switchRoomEffect2, _switchRoomEffect;
+ bool _doEffect;
+
+ byte *_scrollBuffer;
+
+ struct {
+ int x, y, w, h;
+ byte *buffer;
+ uint16 xStrips, yStrips;
+ bool isDrawn;
+ } _flashlight;
+
+public:
+ bool isLightOn() const;
+
+protected:
+ void initScreens(int b, int h);
+ void initVirtScreen(VirtScreenNumber slot, int top, int width, int height, bool twobufs, bool scrollable);
+ void initBGBuffers(int height);
+ void initCycl(const byte *ptr); // Color cycle
+
+ void decodeNESBaseTiles();
+
+ void drawObject(int obj, int arg);
+ void drawRoomObjects(int arg);
+ void drawRoomObject(int i, int arg);
+ void drawBox(int x, int y, int x2, int y2, int color);
+
+ void restoreBG(Common::Rect rect, byte backcolor = 0);
+ void redrawBGStrip(int start, int num);
+ virtual void redrawBGAreas();
+
+ void cameraMoved();
+ void setCameraAtEx(int at);
+ virtual void setCameraAt(int pos_x, int pos_y);
+ virtual void setCameraFollows(Actor *a);
+ virtual void moveCamera();
+ virtual void panCameraTo(int x, int y);
+ void clampCameraPos(Common::Point *pt);
+ void actorFollowCamera(int act);
+
+ const byte *getPalettePtr(int palindex, int room);
+ void setupC64Palette();
+ void setupNESPalette();
+ void setupAmigaPalette();
+ void setupHercPalette();
+ void setupCGAPalette();
+ void setupEGAPalette();
+ void setupV1Palette();
+ void setPalette(int pal);
+ void setRoomPalette(int pal, int room);
+ virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1);
+ virtual void setPalColor(int index, int r, int g, int b);
+ void setDirtyColors(int min, int max);
+ const byte *findPalInPals(const byte *pal, int index);
+ void swapPalColors(int a, int b);
+ virtual void copyPalColor(int dst, int src);
+ void cyclePalette();
+ void stopCycle(int i);
+ virtual void palManipulateInit(int resID, int start, int end, int time);
+ void palManipulate();
+public:
+ int remapPaletteColor(int r, int g, int b, int threshold); // Used by Actor::remapActorPalette
+protected:
+ void moveMemInPalRes(int start, int end, byte direction);
+ void setupShadowPalette(int slot, int redScale, int greenScale, int blueScale, int startColor, int endColor);
+ void setupShadowPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor, int start, int end);
+ virtual void darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor);
+
+ void setupCursor();
+
+ void setCursorFromBuffer(const byte *ptr, int width, int height, int pitch);
+
+public:
+ void markRectAsDirty(VirtScreenNumber virt, int left, int right, int top, int bottom, int dirtybit = 0);
+ void markRectAsDirty(VirtScreenNumber virt, const Common::Rect& rect, int dirtybit = 0) {
+ markRectAsDirty(virt, rect.left, rect.right, rect.top, rect.bottom, dirtybit);
+ }
+protected:
+ // Screen rendering
+ byte *_compositeBuf;
+ byte *_herculesBuf;
+ virtual void drawDirtyScreenParts();
+ void updateDirtyScreen(VirtScreenNumber slot);
+ void drawStripToScreen(VirtScreen *vs, int x, int w, int t, int b);
+ void ditherCGA(byte *dst, int dstPitch, int x, int y, int width, int height) const;
+ void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *width, int *height) const;
+
+public:
+ VirtScreen *findVirtScreen(int y);
+ byte *getMaskBuffer(int x, int y, int z);
+
+protected:
+ void drawFlashlight();
+
+ void fadeIn(int effect);
+ void fadeOut(int effect);
+
+ void unkScreenEffect6();
+ void transitionEffect(int a);
+ void dissolveEffect(int width, int height);
+ void scrollEffect(int dir);
+
+ // bomp
+public:
+ byte *_bompActorPalettePtr;
+ void drawBomp(const BompDrawData &bd, bool mirror);
+
+protected:
+ bool _shakeEnabled;
+ uint _shakeFrame;
+ void setShake(int mode);
+
+ int _drawObjectQueNr;
+ byte _drawObjectQue[200];
+
+ /* For each of the 410 screen strips, gfxUsageBits contains a
+ * bitmask. The lower 80 bits each correspond to one actor and
+ * signify if any part of that actor is currently contained in
+ * that strip.
+ *
+ * If the leftmost bit is set, the strip (background) is dirty
+ * needs to be redrawn.
+ *
+ * The second leftmost bit is set by removeBlastObject() and
+ * restoreBG(), but I'm not yet sure why.
+ */
+ uint32 gfxUsageBits[410 * 3];
+
+ void upgradeGfxUsageBits();
+ void setGfxUsageBit(int strip, int bit);
+ void clearGfxUsageBit(int strip, int bit);
+ bool testGfxUsageBit(int strip, int bit);
+ bool testGfxAnyUsageBits(int strip);
+ bool testGfxOtherUsageBits(int strip, int bit);
+
+public:
+ byte _roomPalette[256];
+ byte *_shadowPalette;
+ bool _skipDrawObject;
+ int _voiceMode;
+
+ // HE specific
+ byte _HEV7ActorPalette[256];
+ uint8 *_hePalettes;
+
+ int _heTimers[16];
+ int getHETimer(int timer);
+ void setHETimer(int timer);
+
+protected:
+ int _shadowPaletteSize;
+ byte _currentPalette[3 * 256];
+ byte _darkenPalette[3 * 256];
+
+ int _palDirtyMin, _palDirtyMax;
+
+ byte _palManipStart, _palManipEnd;
+ uint16 _palManipCounter;
+ byte *_palManipPalette;
+ byte *_palManipIntermediatePal;
+
+ byte _haveMsg;
+ bool _haveActorSpeechMsg;
+ bool _useTalkAnims;
+ uint16 _defaultTalkDelay;
+ int _tempMusic;
+ int _saveSound;
+ bool _native_mt32;
+ bool _enable_gs;
+ int _midi;
+ MidiDriverFlags _musicType;
+ bool _copyProtection;
+ bool _demoMode;
+ bool _confirmExit;
+
+public:
+ uint16 _extraBoxFlags[65];
+
+ byte getNumBoxes();
+ byte *getBoxMatrixBaseAddr();
+ int getPathToDestBox(byte from, byte to);
+ void getGates(int trap1, int trap2, Common::Point gateA[2], Common::Point gateB[2]);
+ bool inBoxQuickReject(int box, int x, int y, int threshold);
+ int getClosestPtOnBox(int box, int x, int y, int16& outX, int16& outY);
+ int getSpecialBox(int param1, int param2);
+
+ void setBoxFlags(int box, int val);
+ void setBoxScale(int box, int b);
+
+ bool checkXYInBoxBounds(int box, int x, int y);
+ uint distanceFromPt(int x, int y, int ptx, int pty);
+ void getBoxCoordinates(int boxnum, BoxCoords *bc);
+ byte getMaskFromBox(int box);
+ Box *getBoxBaseAddr(int box);
+ byte getBoxFlags(int box);
+ int getBoxScale(int box);
+
+ int getScale(int box, int x, int y);
+ int getScaleFromSlot(int slot, int x, int y);
+
+protected:
+ // Scaling slots/items
+ struct ScaleSlot {
+ int x1, y1, scale1;
+ int x2, y2, scale2;
+ };
+ ScaleSlot _scaleSlots[20];
+ void setScaleSlot(int slot, int x1, int y1, int scale1, int x2, int y2, int scale2);
+ void setBoxScaleSlot(int box, int slot);
+ void convertScaleTableToScaleSlot(int slot);
+
+ void createBoxMatrix();
+ bool areBoxesNeighbours(int i, int j);
+
+ /* String class */
+public:
+ CharsetRenderer *_charset;
+ byte _charsetColorMap[16];
+protected:
+ byte _charsetColor;
+ byte _charsetData[15][16];
+
+ int _charsetBufPos;
+ byte _charsetBuffer[512];
+
+ bool _keepText;
+
+ virtual void initCharset(int charset);
+
+ void printString(int m, const byte *msg);
+
+ virtual bool handleNextCharsetCode(Actor *a, int *c);
+ void CHARSET_1();
+ void drawString(int a, const byte *msg);
+ void debugMessage(const byte *msg);
+ void showMessageDialog(const byte *msg);
+
+ int convertMessageToString(const byte *msg, byte *dst, int dstSize);
+ int convertIntMessage(byte *dst, int dstSize, int var);
+ int convertVerbMessage(byte *dst, int dstSize, int var);
+ int convertNameMessage(byte *dst, int dstSize, int var);
+ int convertStringMessage(byte *dst, int dstSize, int var);
+
+ virtual void loadLanguageBundle() {}
+
+public:
+ Common::Language _language; // Accessed by a hack in NutRenderer::loadFont
+
+ // Used by class ScummDialog:
+ virtual void translateText(const byte *text, byte *trans_buff);
+
+ // Somewhat hackish stuff for 2 byte support (Chinese/Japanese/Korean)
+ bool _useCJKMode;
+ int _2byteHeight;
+ int _2byteWidth;
+ byte *get2byteCharPtr(int idx);
+
+protected:
+ byte *_2byteFontPtr;
+
+ uint32 fileReadDword();
+
+public:
+
+ /* Scumm Vars */
+ byte VAR_LANGUAGE;
+ byte VAR_KEYPRESS;
+ byte VAR_SYNC;
+ byte VAR_EGO;
+ byte VAR_CAMERA_POS_X;
+ byte VAR_HAVE_MSG;
+ byte VAR_ROOM;
+ byte VAR_OVERRIDE;
+ byte VAR_MACHINE_SPEED;
+ byte VAR_ME;
+ byte VAR_NUM_ACTOR;
+ byte VAR_CURRENT_LIGHTS;
+ byte VAR_CURRENTDRIVE;
+ byte VAR_CURRENTDISK;
+ byte VAR_TMR_1;
+ byte VAR_TMR_2;
+ byte VAR_TMR_3;
+ byte VAR_MUSIC_TIMER;
+ byte VAR_ACTOR_RANGE_MIN;
+ byte VAR_ACTOR_RANGE_MAX;
+ byte VAR_CAMERA_MIN_X;
+ byte VAR_CAMERA_MAX_X;
+ byte VAR_TIMER_NEXT;
+ byte VAR_VIRT_MOUSE_X;
+ byte VAR_VIRT_MOUSE_Y;
+ byte VAR_ROOM_RESOURCE;
+ byte VAR_LAST_SOUND;
+ byte VAR_CUTSCENEEXIT_KEY;
+ byte VAR_OPTIONS_KEY;
+ byte VAR_TALK_ACTOR;
+ byte VAR_CAMERA_FAST_X;
+ byte VAR_SCROLL_SCRIPT;
+ byte VAR_ENTRY_SCRIPT;
+ byte VAR_ENTRY_SCRIPT2;
+ byte VAR_EXIT_SCRIPT;
+ byte VAR_EXIT_SCRIPT2;
+ byte VAR_VERB_SCRIPT;
+ byte VAR_SENTENCE_SCRIPT;
+ byte VAR_INVENTORY_SCRIPT;
+ byte VAR_CUTSCENE_START_SCRIPT;
+ byte VAR_CUTSCENE_END_SCRIPT;
+ byte VAR_CHARINC;
+ byte VAR_WALKTO_OBJ;
+ byte VAR_DEBUGMODE;
+ byte VAR_HEAPSPACE;
+ byte VAR_RESTART_KEY;
+ byte VAR_PAUSE_KEY;
+ byte VAR_MOUSE_X;
+ byte VAR_MOUSE_Y;
+ byte VAR_TIMER;
+ byte VAR_TMR_4;
+ byte VAR_SOUNDCARD;
+ byte VAR_VIDEOMODE;
+ byte VAR_MAINMENU_KEY;
+ byte VAR_FIXEDDISK;
+ byte VAR_CURSORSTATE;
+ byte VAR_USERPUT;
+ byte VAR_SOUNDRESULT;
+ byte VAR_TALKSTOP_KEY;
+ byte VAR_FADE_DELAY;
+ byte VAR_NOSUBTITLES;
+
+ // V5+
+ byte VAR_SOUNDPARAM;
+ byte VAR_SOUNDPARAM2;
+ byte VAR_SOUNDPARAM3;
+ byte VAR_MOUSEPRESENT;
+ byte VAR_MEMORY_PERFORMANCE;
+ byte VAR_VIDEO_PERFORMANCE;
+ byte VAR_ROOM_FLAG;
+ byte VAR_GAME_LOADED;
+ byte VAR_NEW_ROOM;
+
+ // V4/V5
+ byte VAR_V5_TALK_STRING_Y;
+
+ // V6+
+ byte VAR_ROOM_WIDTH;
+ byte VAR_ROOM_HEIGHT;
+ byte VAR_SUBTITLES;
+ byte VAR_V6_EMSSPACE;
+
+ // V7/V8 (=GF_NEW_CAMERA) specific variables
+ byte VAR_CAMERA_POS_Y;
+ byte VAR_CAMERA_MIN_Y;
+ byte VAR_CAMERA_MAX_Y;
+ byte VAR_CAMERA_THRESHOLD_X;
+ byte VAR_CAMERA_THRESHOLD_Y;
+ byte VAR_CAMERA_SPEED_X;
+ byte VAR_CAMERA_SPEED_Y;
+ byte VAR_CAMERA_ACCEL_X;
+ byte VAR_CAMERA_ACCEL_Y;
+ byte VAR_CAMERA_DEST_X;
+ byte VAR_CAMERA_DEST_Y;
+ byte VAR_CAMERA_FOLLOWED_ACTOR;
+
+ // V7/V8 specific variables
+ byte VAR_VERSION_KEY;
+ byte VAR_DEFAULT_TALK_DELAY;
+ byte VAR_CUSTOMSCALETABLE;
+ byte VAR_BLAST_ABOVE_TEXT;
+ byte VAR_VOICE_MODE;
+ byte VAR_MUSIC_BUNDLE_LOADED;
+ byte VAR_VOICE_BUNDLE_LOADED;
+
+ byte VAR_LEFTBTN_DOWN; // V7/V8
+ byte VAR_RIGHTBTN_DOWN; // V7/V8
+ byte VAR_LEFTBTN_HOLD; // V6/V72HE/V7/V8
+ byte VAR_RIGHTBTN_HOLD; // V6/V72HE/V7/V8
+ byte VAR_SAVELOAD_SCRIPT; // V6/V7 (not HE)
+ byte VAR_SAVELOAD_SCRIPT2; // V6/V7 (not HE)
+
+ // V6/V7 specific variables (FT & Sam & Max specific)
+ byte VAR_CHARSET_MASK;
+
+ // V6 specific variables
+ byte VAR_V6_SOUNDMODE;
+
+ // V1/V2 specific variables
+ byte VAR_CHARCOUNT;
+ byte VAR_VERB_ALLOWED;
+ byte VAR_ACTIVE_VERB;
+ byte VAR_ACTIVE_OBJECT1;
+ byte VAR_ACTIVE_OBJECT2;
+ byte VAR_CLICK_AREA;
+
+ // HE specific variables
+ byte VAR_REDRAW_ALL_ACTORS; // Used in setActorRedrawFlags()
+ byte VAR_SKIP_RESET_TALK_ACTOR; // Used in setActorCostume()
+
+ byte VAR_SOUND_CHANNEL; // Used in o_startSound()
+ byte VAR_TALK_CHANNEL; // Used in startHETalkSound()
+ byte VAR_SOUNDCODE_TMR; // Used in processSoundCode()
+ byte VAR_RESERVED_SOUND_CHANNELS; // Used in findFreeSoundChannel()
+
+ byte VAR_MAIN_SCRIPT; // Used in scummLoop()
+
+ byte VAR_SCRIPT_CYCLE; // Used in runScript()/runObjectScript()
+ byte VAR_NUM_SCRIPT_CYCLES; // Used in runAllScripts()
+
+ byte VAR_KEY_STATE; // Used in parseEvents()
+ byte VAR_MOUSE_STATE; // Used in checkExecVerbs();
+
+ // Exists both in V7 and in V72HE:
+ byte VAR_NUM_GLOBAL_OBJS;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/smush/channel.h b/engines/scumm/smush/channel.h
new file mode 100644
index 0000000000..52e64c8b2d
--- /dev/null
+++ b/engines/scumm/smush/channel.h
@@ -0,0 +1,148 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SMUSH_CHANNEL_H
+#define SMUSH_CHANNEL_H
+
+#include "common/util.h"
+
+namespace Scumm {
+
+class Chunk;
+class ContChunk;
+
+class SmushChannel {
+public:
+
+ virtual ~SmushChannel() {};
+ virtual bool appendData(Chunk &b, int32 size) = 0;
+ virtual bool setParameters(int32, int32, int32, int32, int32) = 0;
+ virtual bool checkParameters(int32, int32, int32, int32, int32) = 0;
+ virtual bool isTerminated() const = 0;
+ virtual int32 availableSoundData() const = 0;
+ virtual void getSoundData(int16 *sound_buffer, int32 size) = 0;
+ virtual void getSoundData(int8 *sound_buffer, int32 size) = 0;
+ virtual int32 getRate() = 0;
+ virtual bool getParameters(int32 &rate, bool &stereo, bool &is_16bit, int32 &vol, int32 &pan) = 0;
+ virtual int32 getTrackIdentifier() const = 0;
+};
+
+class SaudChannel : public SmushChannel {
+private:
+ int32 _track;
+ int32 _nbframes;
+ int32 _dataSize;
+ int32 _frequency;
+ bool _inData;
+ bool _markReached;
+ int32 _flags;
+ int32 _volume;
+ int32 _pan;
+ int32 _index;
+ byte *_tbuffer;
+ int32 _tbufferSize;
+ byte *_sbuffer;
+ int32 _sbufferSize;
+ bool _keepSize;
+
+protected:
+ void handleStrk(Chunk &c);
+ void handleSmrk(Chunk &c);
+ void handleShdr(Chunk &c);
+ bool handleSubTags(int32 &offset);
+ bool processBuffer();
+
+public:
+ SaudChannel(int32 track, int32 freq);
+ virtual ~SaudChannel();
+ bool isTerminated() const;
+ bool setParameters(int32 duration, int32 flags, int32 vol1, int32 vol2, int32 index);
+ bool checkParameters(int32 index, int32 duration, int32 flags, int32 vol1, int32 vol2);
+ bool appendData(Chunk &b, int32 size);
+ int32 availableSoundData() const;
+ void getSoundData(int16 *sound_buffer, int32 size);
+ void getSoundData(int8 *sound_buffer, int32 size) { error("8bit request for SAUD channel should never happen"); };
+ int32 getRate() { return _frequency; }
+ bool getParameters(int32 &rate, bool &stereo, bool &is_16bit, int32 &vol, int32 &pan) {
+ rate = _frequency;
+ stereo = true;
+ is_16bit = true;
+ vol = _volume;
+ pan = _pan;
+ return true;
+ };
+ virtual int32 getTrackIdentifier() const { return _track; };
+};
+
+class ImuseChannel : public SmushChannel {
+private:
+ int32 _track; //!< the track number
+ byte *_tbuffer; //!< data temporary buffer
+ int32 _tbufferSize; //!< temporary buffer size
+ byte *_sbuffer; //!< sound buffer
+ int32 _sbufferSize; //!< sound buffer size
+ int32 _srbufferSize;
+ int32 _frequency; //!< the target frequency of the ::mixer
+ int32 _dataSize; //!< remaining size of sound data in the iMUS buffer
+ bool _inData;
+ int32 _volume;
+ int32 _pan;
+
+ int32 _bitsize; //!< the bitsize of the original data
+ int32 _rate; //!< the sampling rate of the original data
+ int32 _channels; //!< the number of channels of the original data
+
+protected:
+ int32 decode(int32 size, int32 &ret);
+ void decode();
+ bool processBuffer();
+ bool handleMap(Chunk &);
+ bool handleFormat(Chunk &);
+ bool handleRegion(Chunk &);
+ bool handleStop(Chunk &);
+ bool handleSubTags(int32 & offset);
+
+public:
+ ImuseChannel(int32 track, int32 freq);
+ virtual ~ImuseChannel();
+ bool isTerminated() const;
+ bool setParameters(int32 nbframes, int32 size, int32 track_flags, int32 unk1, int32);
+ bool checkParameters(int32 index, int32 nbframes, int32 size, int32 track_flags, int32 unk1);
+ bool appendData(Chunk &b, int32 size);
+ int32 availableSoundData() const;
+ void getSoundData(int16 *sound_buffer, int32 size);
+ void getSoundData(int8 *sound_buffer, int32 size);
+ int32 getRate() { return _rate; }
+ bool getParameters(int32 &rate, bool &stereo, bool &is_16bit, int32 &vol, int32 &pan) {
+ rate = _rate;
+ stereo = (_channels == 2);
+ is_16bit = (_bitsize > 8);
+ vol = _volume;
+ pan = _pan;
+ return true;
+ };
+ virtual int32 getTrackIdentifier() const { return _track; };
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/smush/chunk.cpp b/engines/scumm/smush/chunk.cpp
new file mode 100644
index 0000000000..60512cdf2f
--- /dev/null
+++ b/engines/scumm/smush/chunk.cpp
@@ -0,0 +1,242 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/smush/chunk.h"
+#include "scumm/scumm.h"
+
+#include "common/file.h"
+#include "common/str.h"
+#include "common/util.h"
+
+namespace Scumm {
+
+const char *Chunk::ChunkString(Chunk::type t) {
+ static char data[5];
+ data[0] = (char)((t >> 24) & 0xFF);
+ data[1] = (char)((t >> 16) & 0xFF);
+ data[2] = (char)((t >> 8) & 0xFF);
+ data[3] = (char)((t >> 0) & 0xFF);
+ data[4] = 0;
+ return data;
+}
+
+BaseChunk::BaseChunk() :
+ _type(0),
+ _size(0),
+ _curPos(0) {
+}
+
+bool BaseChunk::eof() const {
+ return _curPos >= _size;
+}
+
+uint32 BaseChunk::tell() const {
+ return _curPos;
+}
+
+Chunk::type BaseChunk::getType() const {
+ return _type;
+}
+
+uint32 BaseChunk::getSize() const {
+ return _size;
+}
+
+bool BaseChunk::seek(int32 delta, seek_type dir) {
+ switch(dir) {
+ case seek_cur:
+ _curPos += delta;
+ break;
+ case seek_start:
+ if (delta < 0)
+ error("invalid seek request");
+
+ _curPos = (uint32)delta;
+ break;
+ case seek_end:
+ if (delta > 0 || _size < (uint32)-delta)
+ error("invalid seek request");
+
+ _curPos = (uint32)(_size + delta);
+ break;
+ }
+ if (_curPos > _size) {
+ error("invalid seek request : %d > %d (delta == %d)", _curPos, _size, delta);
+ }
+ return true;
+}
+
+FileChunk::FileChunk(ScummFile *data, int offset) {
+ _data = data;
+ _deleteData = false;
+
+ _data->seek(offset, seek_start);
+ _type = _data->readUint32BE();
+ _size = _data->readUint32BE();
+ _offset = _data->pos();
+ _curPos = 0;
+}
+
+FileChunk::FileChunk(const Common::String &name, int offset) {
+ _data = new ScummFile();
+ _deleteData = true;
+ if (!g_scumm->openFile(*_data, name.c_str()))
+ error("FileChunk: Unable to open file %s", name.c_str());
+
+ _data->seek(offset, seek_start);
+ _type = _data->readUint32BE();
+ _size = _data->readUint32BE();
+ _offset = _data->pos();
+ _curPos = 0;
+}
+
+FileChunk::~FileChunk() {
+ if (_deleteData)
+ delete _data;
+}
+
+Chunk *FileChunk::subBlock() {
+ FileChunk *ptr = new FileChunk(_data, _offset + _curPos);
+ seek(sizeof(Chunk::type) + sizeof(uint32) + ptr->getSize());
+ return ptr;
+}
+
+void FileChunk::reseek() {
+ _data->seek(_offset + _curPos);
+}
+
+bool FileChunk::read(void *buffer, uint32 size) {
+ if (size <= 0 || (_curPos + size) > _size)
+ error("invalid buffer read request");
+
+ _data->read(buffer, size);
+ _curPos += size;
+ return true;
+}
+
+int8 FileChunk::getChar() {
+ return (int8)getByte();
+}
+
+byte FileChunk::getByte() {
+ _curPos++;
+
+ if (_curPos > _size)
+ error("invalid byte read request");
+
+ return _data->readByte();
+}
+
+int16 FileChunk::getShort() {
+ return (int16)getWord();
+}
+
+uint16 FileChunk::getWord() {
+ _curPos += 2;
+
+ if (_curPos > _size)
+ error("invalid word read request");
+
+ return _data->readUint16LE();
+}
+
+uint32 FileChunk::getDword() {
+ _curPos += 4;
+
+ if (_curPos > _size)
+ error("invalid dword read request");
+
+ return _data->readUint32LE();
+}
+
+MemoryChunk::MemoryChunk(byte *data) {
+ if (data == 0)
+ error("Chunk() called with NULL pointer");
+
+ _type = (Chunk::type)READ_BE_UINT32(data);
+ _size = READ_BE_UINT32(data + 4);
+ _data = data + sizeof(Chunk::type) + sizeof(uint32);
+ _curPos = 0;
+}
+
+Chunk *MemoryChunk::subBlock() {
+ MemoryChunk *ptr = new MemoryChunk(_data + _curPos);
+ seek(sizeof(Chunk::type) + sizeof(uint32) + ptr->getSize());
+ return ptr;
+}
+
+void MemoryChunk::reseek() {
+}
+
+bool MemoryChunk::read(void *buffer, uint32 size) {
+ if (size <= 0 || (_curPos + size) > _size)
+ error("invalid buffer read request");
+
+ memcpy(buffer, _data + _curPos, size);
+ _curPos += size;
+ return true;
+}
+
+int8 MemoryChunk::getChar() {
+ if (_curPos >= _size)
+ error("invalid char read request");
+
+ return _data[_curPos++];
+}
+
+byte MemoryChunk::getByte() {
+ if (_curPos >= _size)
+ error("invalid byte read request");
+
+ byte *ptr = (byte *)(_data + _curPos);
+ _curPos += 1;
+ return *ptr;
+}
+
+int16 MemoryChunk::getShort() {
+ if (_curPos >= _size - 1)
+ error("invalid int16 read request");
+
+ int16 buffer = getWord();
+ return *((int16 *)&buffer);
+}
+
+uint16 MemoryChunk::getWord() {
+ if (_curPos >= _size - 1)
+ error("invalid word read request");
+
+ uint16 *ptr = (uint16 *)(_data + _curPos);
+ _curPos += 2;
+ return READ_LE_UINT16(ptr);
+}
+
+uint32 MemoryChunk::getDword() {
+ if (_curPos >= _size - 3)
+ error("invalid dword read request");
+
+ uint32 *ptr = (uint32 *)(_data + _curPos);
+ _curPos += 4;
+ return READ_LE_UINT32(ptr);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/smush/chunk.h b/engines/scumm/smush/chunk.h
new file mode 100644
index 0000000000..26290538a6
--- /dev/null
+++ b/engines/scumm/smush/chunk.h
@@ -0,0 +1,110 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef CHUNK_H
+#define CHUNK_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+class ScummFile;
+
+class Chunk {
+public:
+ virtual ~Chunk() {};
+ enum seek_type { seek_start, seek_end, seek_cur };
+ typedef uint32 type;
+ static const char *ChunkString(type t);
+ virtual type getType() const = 0;
+ virtual uint32 getSize() const = 0;
+ virtual Chunk *subBlock() = 0;
+ virtual void reseek() = 0;
+ virtual bool eof() const = 0;
+ virtual uint32 tell() const = 0;
+ virtual bool seek(int32 delta, seek_type dir = seek_cur) = 0;
+ virtual bool read(void *buffer, uint32 size) = 0;
+ virtual int8 getChar() = 0;
+ virtual byte getByte() = 0;
+ virtual int16 getShort() = 0;
+ virtual uint16 getWord() = 0;
+ virtual uint32 getDword() = 0;
+};
+
+// Common functionality for concrete chunks (FileChunk, MemoryChunk)
+class BaseChunk : public Chunk {
+protected:
+ Chunk::type _type;
+ uint32 _size;
+ uint32 _curPos;
+
+ BaseChunk();
+
+public:
+ Chunk::type getType() const;
+ uint32 getSize() const;
+ bool eof() const;
+ uint32 tell() const;
+ bool seek(int32 delta, seek_type dir = seek_cur);
+};
+
+class FileChunk : public BaseChunk {
+private:
+ ScummFile *_data;
+ bool _deleteData;
+ uint32 _offset;
+
+ FileChunk(ScummFile *data, int offset);
+public:
+ FileChunk(const Common::String &name, int offset = 0);
+ virtual ~FileChunk();
+ Chunk *subBlock();
+ void reseek();
+ bool read(void *buffer, uint32 size);
+ int8 getChar();
+ byte getByte();
+ short getShort();
+ uint16 getWord();
+ uint32 getDword();
+};
+
+class MemoryChunk : public BaseChunk {
+private:
+ byte *_data;
+
+public:
+ MemoryChunk(byte *data);
+ Chunk *subBlock();
+ void reseek();
+ bool read(void *buffer, uint32 size);
+ int8 getChar();
+ byte getByte();
+ int16 getShort();
+ uint16 getWord();
+ uint32 getDword();
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/smush/chunk_type.h b/engines/scumm/smush/chunk_type.h
new file mode 100644
index 0000000000..18c49f0dbf
--- /dev/null
+++ b/engines/scumm/smush/chunk_type.h
@@ -0,0 +1,60 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef CHUNK_TYPE_H
+#define CHUNK_TYPE_H
+
+#include "common/scummsys.h"
+#include "scumm/smush/chunk.h"
+
+namespace Scumm {
+
+static const Chunk::type TYPE_ANIM = 'ANIM';
+static const Chunk::type TYPE_AHDR = 'AHDR';
+static const Chunk::type TYPE_FRME = 'FRME';
+static const Chunk::type TYPE_NPAL = 'NPAL';
+static const Chunk::type TYPE_FOBJ = 'FOBJ';
+static const Chunk::type TYPE_ZFOB = 'ZFOB';
+static const Chunk::type TYPE_PSAD = 'PSAD';
+static const Chunk::type TYPE_TRES = 'TRES';
+static const Chunk::type TYPE_XPAL = 'XPAL';
+static const Chunk::type TYPE_IACT = 'IACT';
+static const Chunk::type TYPE_STOR = 'STOR';
+static const Chunk::type TYPE_FTCH = 'FTCH';
+static const Chunk::type TYPE_SKIP = 'SKIP';
+static const Chunk::type TYPE_STRK = 'STRK';
+static const Chunk::type TYPE_SMRK = 'SMRK';
+static const Chunk::type TYPE_SHDR = 'SHDR';
+static const Chunk::type TYPE_SDAT = 'SDAT';
+static const Chunk::type TYPE_SAUD = 'SAUD';
+static const Chunk::type TYPE_iMUS = 'iMUS';
+static const Chunk::type TYPE_FRMT = 'FRMT';
+static const Chunk::type TYPE_TEXT = 'TEXT';
+static const Chunk::type TYPE_REGN = 'REGN';
+static const Chunk::type TYPE_STOP = 'STOP';
+static const Chunk::type TYPE_MAP_ = 'MAP ';
+static const Chunk::type TYPE_DATA = 'DATA';
+static const Chunk::type TYPE_ETRS = 'ETRS';
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/smush/codec1.cpp b/engines/scumm/smush/codec1.cpp
new file mode 100644
index 0000000000..04d55e2ed7
--- /dev/null
+++ b/engines/scumm/smush/codec1.cpp
@@ -0,0 +1,62 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+
+namespace Scumm {
+
+void smush_decode_codec1(byte *dst, const byte *src, int left, int top, int width, int height, int pitch) {
+ byte val, code;
+ int32 length;
+ int h = height, size_line;
+
+ dst += top * pitch;
+ for (h = 0; h < height; h++) {
+ size_line = READ_LE_UINT16(src);
+ src += 2;
+ dst += left;
+ while (size_line > 0) {
+ code = *src++;
+ size_line--;
+ length = (code >> 1) + 1;
+ if (code & 1) {
+ val = *src++;
+ size_line--;
+ if (val)
+ memset(dst, val, length);
+ dst += length;
+ } else {
+ size_line -= length;
+ while (length--) {
+ val = *src++;
+ if (val)
+ *dst = val;
+ dst++;
+ }
+ }
+ }
+ dst += pitch - left - width;
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/smush/codec37.cpp b/engines/scumm/smush/codec37.cpp
new file mode 100644
index 0000000000..732bffeeae
--- /dev/null
+++ b/engines/scumm/smush/codec37.cpp
@@ -0,0 +1,588 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "scumm/bomp.h"
+#include "scumm/smush/codec37.h"
+
+namespace Scumm {
+
+void Codec37Decoder::init(int width, int height) {
+ deinit();
+ _width = width;
+ _height = height;
+ _frameSize = _width * _height;
+ _deltaSize = _frameSize * 3 + 0x13600;
+ _deltaBuf = (byte *)calloc(_deltaSize, sizeof(byte));
+ if (_deltaBuf == 0)
+ error("unable to allocate decoder buffer");
+ _deltaBufs[0] = _deltaBuf + 0x4D80;
+ _deltaBufs[1] = _deltaBuf + 0xE880 + _frameSize;
+ _offsetTable = new int16[255];
+ _curtable = 0;
+ if (_offsetTable == 0)
+ error("unable to allocate decoder offset table");
+ _tableLastPitch = -1;
+ _tableLastIndex = -1;
+}
+
+Codec37Decoder::Codec37Decoder() {
+ _deltaSize = 0;
+ _deltaBuf = 0;
+ _deltaBufs[0] = 0;
+ _deltaBufs[1] = 0;
+ _curtable = 0;
+ _offsetTable = 0;
+ _tableLastPitch = -1;
+ _tableLastIndex = -1;
+ _prevSeqNb = 0;
+}
+
+void Codec37Decoder::deinit() {
+ if (_offsetTable) {
+ delete []_offsetTable;
+ _offsetTable = 0;
+ _tableLastPitch = -1;
+ _tableLastIndex = -1;
+ }
+ if (_deltaBuf) {
+ free(_deltaBuf);
+ _deltaSize = 0;
+ _deltaBuf = 0;
+ _deltaBufs[0] = 0;
+ _deltaBufs[1] = 0;
+ }
+}
+
+Codec37Decoder::~Codec37Decoder() {
+ deinit();
+}
+
+void Codec37Decoder::maketable(int pitch, int index) {
+ static const int8 maketable_bytes[] = {
+ 0, 0, 1, 0, 2, 0, 3, 0, 5, 0,
+ 8, 0, 13, 0, 21, 0, -1, 0, -2, 0,
+ -3, 0, -5, 0, -8, 0, -13, 0, -17, 0,
+ -21, 0, 0, 1, 1, 1, 2, 1, 3, 1,
+ 5, 1, 8, 1, 13, 1, 21, 1, -1, 1,
+ -2, 1, -3, 1, -5, 1, -8, 1, -13, 1,
+ -17, 1, -21, 1, 0, 2, 1, 2, 2, 2,
+ 3, 2, 5, 2, 8, 2, 13, 2, 21, 2,
+ -1, 2, -2, 2, -3, 2, -5, 2, -8, 2,
+ -13, 2, -17, 2, -21, 2, 0, 3, 1, 3,
+ 2, 3, 3, 3, 5, 3, 8, 3, 13, 3,
+ 21, 3, -1, 3, -2, 3, -3, 3, -5, 3,
+ -8, 3, -13, 3, -17, 3, -21, 3, 0, 5,
+ 1, 5, 2, 5, 3, 5, 5, 5, 8, 5,
+ 13, 5, 21, 5, -1, 5, -2, 5, -3, 5,
+ -5, 5, -8, 5, -13, 5, -17, 5, -21, 5,
+ 0, 8, 1, 8, 2, 8, 3, 8, 5, 8,
+ 8, 8, 13, 8, 21, 8, -1, 8, -2, 8,
+ -3, 8, -5, 8, -8, 8, -13, 8, -17, 8,
+ -21, 8, 0, 13, 1, 13, 2, 13, 3, 13,
+ 5, 13, 8, 13, 13, 13, 21, 13, -1, 13,
+ -2, 13, -3, 13, -5, 13, -8, 13, -13, 13,
+ -17, 13, -21, 13, 0, 21, 1, 21, 2, 21,
+ 3, 21, 5, 21, 8, 21, 13, 21, 21, 21,
+ -1, 21, -2, 21, -3, 21, -5, 21, -8, 21,
+ -13, 21, -17, 21, -21, 21, 0, -1, 1, -1,
+ 2, -1, 3, -1, 5, -1, 8, -1, 13, -1,
+ 21, -1, -1, -1, -2, -1, -3, -1, -5, -1,
+ -8, -1, -13, -1, -17, -1, -21, -1, 0, -2,
+ 1, -2, 2, -2, 3, -2, 5, -2, 8, -2,
+ 13, -2, 21, -2, -1, -2, -2, -2, -3, -2,
+ -5, -2, -8, -2, -13, -2, -17, -2, -21, -2,
+ 0, -3, 1, -3, 2, -3, 3, -3, 5, -3,
+ 8, -3, 13, -3, 21, -3, -1, -3, -2, -3,
+ -3, -3, -5, -3, -8, -3, -13, -3, -17, -3,
+ -21, -3, 0, -5, 1, -5, 2, -5, 3, -5,
+ 5, -5, 8, -5, 13, -5, 21, -5, -1, -5,
+ -2, -5, -3, -5, -5, -5, -8, -5, -13, -5,
+ -17, -5, -21, -5, 0, -8, 1, -8, 2, -8,
+ 3, -8, 5, -8, 8, -8, 13, -8, 21, -8,
+ -1, -8, -2, -8, -3, -8, -5, -8, -8, -8,
+ -13, -8, -17, -8, -21, -8, 0, -13, 1, -13,
+ 2, -13, 3, -13, 5, -13, 8, -13, 13, -13,
+ 21, -13, -1, -13, -2, -13, -3, -13, -5, -13,
+ -8, -13, -13, -13, -17, -13, -21, -13, 0, -17,
+ 1, -17, 2, -17, 3, -17, 5, -17, 8, -17,
+ 13, -17, 21, -17, -1, -17, -2, -17, -3, -17,
+ -5, -17, -8, -17, -13, -17, -17, -17, -21, -17,
+ 0, -21, 1, -21, 2, -21, 3, -21, 5, -21,
+ 8, -21, 13, -21, 21, -21, -1, -21, -2, -21,
+ -3, -21, -5, -21, -8, -21, -13, -21, -17, -21,
+ 0, 0, -8, -29, 8, -29, -18, -25, 17, -25,
+ 0, -23, -6, -22, 6, -22, -13, -19, 12, -19,
+ 0, -18, 25, -18, -25, -17, -5, -17, 5, -17,
+ -10, -15, 10, -15, 0, -14, -4, -13, 4, -13,
+ 19, -13, -19, -12, -8, -11, -2, -11, 0, -11,
+ 2, -11, 8, -11, -15, -10, -4, -10, 4, -10,
+ 15, -10, -6, -9, -1, -9, 1, -9, 6, -9,
+ -29, -8, -11, -8, -8, -8, -3, -8, 3, -8,
+ 8, -8, 11, -8, 29, -8, -5, -7, -2, -7,
+ 0, -7, 2, -7, 5, -7, -22, -6, -9, -6,
+ -6, -6, -3, -6, -1, -6, 1, -6, 3, -6,
+ 6, -6, 9, -6, 22, -6, -17, -5, -7, -5,
+ -4, -5, -2, -5, 0, -5, 2, -5, 4, -5,
+ 7, -5, 17, -5, -13, -4, -10, -4, -5, -4,
+ -3, -4, -1, -4, 0, -4, 1, -4, 3, -4,
+ 5, -4, 10, -4, 13, -4, -8, -3, -6, -3,
+ -4, -3, -3, -3, -2, -3, -1, -3, 0, -3,
+ 1, -3, 2, -3, 4, -3, 6, -3, 8, -3,
+ -11, -2, -7, -2, -5, -2, -3, -2, -2, -2,
+ -1, -2, 0, -2, 1, -2, 2, -2, 3, -2,
+ 5, -2, 7, -2, 11, -2, -9, -1, -6, -1,
+ -4, -1, -3, -1, -2, -1, -1, -1, 0, -1,
+ 1, -1, 2, -1, 3, -1, 4, -1, 6, -1,
+ 9, -1, -31, 0, -23, 0, -18, 0, -14, 0,
+ -11, 0, -7, 0, -5, 0, -4, 0, -3, 0,
+ -2, 0, -1, 0, 0, -31, 1, 0, 2, 0,
+ 3, 0, 4, 0, 5, 0, 7, 0, 11, 0,
+ 14, 0, 18, 0, 23, 0, 31, 0, -9, 1,
+ -6, 1, -4, 1, -3, 1, -2, 1, -1, 1,
+ 0, 1, 1, 1, 2, 1, 3, 1, 4, 1,
+ 6, 1, 9, 1, -11, 2, -7, 2, -5, 2,
+ -3, 2, -2, 2, -1, 2, 0, 2, 1, 2,
+ 2, 2, 3, 2, 5, 2, 7, 2, 11, 2,
+ -8, 3, -6, 3, -4, 3, -2, 3, -1, 3,
+ 0, 3, 1, 3, 2, 3, 3, 3, 4, 3,
+ 6, 3, 8, 3, -13, 4, -10, 4, -5, 4,
+ -3, 4, -1, 4, 0, 4, 1, 4, 3, 4,
+ 5, 4, 10, 4, 13, 4, -17, 5, -7, 5,
+ -4, 5, -2, 5, 0, 5, 2, 5, 4, 5,
+ 7, 5, 17, 5, -22, 6, -9, 6, -6, 6,
+ -3, 6, -1, 6, 1, 6, 3, 6, 6, 6,
+ 9, 6, 22, 6, -5, 7, -2, 7, 0, 7,
+ 2, 7, 5, 7, -29, 8, -11, 8, -8, 8,
+ -3, 8, 3, 8, 8, 8, 11, 8, 29, 8,
+ -6, 9, -1, 9, 1, 9, 6, 9, -15, 10,
+ -4, 10, 4, 10, 15, 10, -8, 11, -2, 11,
+ 0, 11, 2, 11, 8, 11, 19, 12, -19, 13,
+ -4, 13, 4, 13, 0, 14, -10, 15, 10, 15,
+ -5, 17, 5, 17, 25, 17, -25, 18, 0, 18,
+ -12, 19, 13, 19, -6, 22, 6, 22, 0, 23,
+ -17, 25, 18, 25, -8, 29, 8, 29, 0, 31,
+ 0, 0, -6, -22, 6, -22, -13, -19, 12, -19,
+ 0, -18, -5, -17, 5, -17, -10, -15, 10, -15,
+ 0, -14, -4, -13, 4, -13, 19, -13, -19, -12,
+ -8, -11, -2, -11, 0, -11, 2, -11, 8, -11,
+ -15, -10, -4, -10, 4, -10, 15, -10, -6, -9,
+ -1, -9, 1, -9, 6, -9, -11, -8, -8, -8,
+ -3, -8, 0, -8, 3, -8, 8, -8, 11, -8,
+ -5, -7, -2, -7, 0, -7, 2, -7, 5, -7,
+ -22, -6, -9, -6, -6, -6, -3, -6, -1, -6,
+ 1, -6, 3, -6, 6, -6, 9, -6, 22, -6,
+ -17, -5, -7, -5, -4, -5, -2, -5, -1, -5,
+ 0, -5, 1, -5, 2, -5, 4, -5, 7, -5,
+ 17, -5, -13, -4, -10, -4, -5, -4, -3, -4,
+ -2, -4, -1, -4, 0, -4, 1, -4, 2, -4,
+ 3, -4, 5, -4, 10, -4, 13, -4, -8, -3,
+ -6, -3, -4, -3, -3, -3, -2, -3, -1, -3,
+ 0, -3, 1, -3, 2, -3, 3, -3, 4, -3,
+ 6, -3, 8, -3, -11, -2, -7, -2, -5, -2,
+ -4, -2, -3, -2, -2, -2, -1, -2, 0, -2,
+ 1, -2, 2, -2, 3, -2, 4, -2, 5, -2,
+ 7, -2, 11, -2, -9, -1, -6, -1, -5, -1,
+ -4, -1, -3, -1, -2, -1, -1, -1, 0, -1,
+ 1, -1, 2, -1, 3, -1, 4, -1, 5, -1,
+ 6, -1, 9, -1, -23, 0, -18, 0, -14, 0,
+ -11, 0, -7, 0, -5, 0, -4, 0, -3, 0,
+ -2, 0, -1, 0, 0, -23, 1, 0, 2, 0,
+ 3, 0, 4, 0, 5, 0, 7, 0, 11, 0,
+ 14, 0, 18, 0, 23, 0, -9, 1, -6, 1,
+ -5, 1, -4, 1, -3, 1, -2, 1, -1, 1,
+ 0, 1, 1, 1, 2, 1, 3, 1, 4, 1,
+ 5, 1, 6, 1, 9, 1, -11, 2, -7, 2,
+ -5, 2, -4, 2, -3, 2, -2, 2, -1, 2,
+ 0, 2, 1, 2, 2, 2, 3, 2, 4, 2,
+ 5, 2, 7, 2, 11, 2, -8, 3, -6, 3,
+ -4, 3, -3, 3, -2, 3, -1, 3, 0, 3,
+ 1, 3, 2, 3, 3, 3, 4, 3, 6, 3,
+ 8, 3, -13, 4, -10, 4, -5, 4, -3, 4,
+ -2, 4, -1, 4, 0, 4, 1, 4, 2, 4,
+ 3, 4, 5, 4, 10, 4, 13, 4, -17, 5,
+ -7, 5, -4, 5, -2, 5, -1, 5, 0, 5,
+ 1, 5, 2, 5, 4, 5, 7, 5, 17, 5,
+ -22, 6, -9, 6, -6, 6, -3, 6, -1, 6,
+ 1, 6, 3, 6, 6, 6, 9, 6, 22, 6,
+ -5, 7, -2, 7, 0, 7, 2, 7, 5, 7,
+ -11, 8, -8, 8, -3, 8, 0, 8, 3, 8,
+ 8, 8, 11, 8, -6, 9, -1, 9, 1, 9,
+ 6, 9, -15, 10, -4, 10, 4, 10, 15, 10,
+ -8, 11, -2, 11, 0, 11, 2, 11, 8, 11,
+ 19, 12, -19, 13, -4, 13, 4, 13, 0, 14,
+ -10, 15, 10, 15, -5, 17, 5, 17, 0, 18,
+ -12, 19, 13, 19, -6, 22, 6, 22, 0, 23,
+ };
+
+ if (_tableLastPitch == pitch && _tableLastIndex == index)
+ return;
+
+ _tableLastPitch = pitch;
+ _tableLastIndex = index;
+ index *= 255;
+ assert(index + 254 < (int32)(sizeof(maketable_bytes) / 2));
+
+ for (int32 i = 0; i < 255; i++) {
+ int32 j = (i + index) * 2;
+ _offsetTable[i] = maketable_bytes[j + 1] * pitch + maketable_bytes[j];
+ }
+}
+
+#if defined(SCUMM_NEED_ALIGNMENT)
+
+#define DECLARE_LITERAL_TEMP(v) \
+ byte v
+
+#define READ_LITERAL_PIXEL(src, v) \
+ v = *src++
+
+#define WRITE_4X1_LINE(dst, v) \
+ do { \
+ int j; \
+ for (j=0; j<4; j++) \
+ (dst)[j] = v; \
+ } while (0)
+
+#define COPY_4X1_LINE(dst, src) \
+ do { \
+ int j; \
+ for (j=0; j<4; j++) \
+ (dst)[j] = (src)[j]; \
+ } while (0)
+
+#else /* SCUMM_NEED_ALIGNMENT */
+
+#define DECLARE_LITERAL_TEMP(v) \
+ uint32 v
+
+#define READ_LITERAL_PIXEL(src, v) \
+ do { \
+ v = *src++; \
+ v += (v << 8) + (v << 16) + (v << 24); \
+ } while (0)
+
+#define WRITE_4X1_LINE(dst, v) \
+ *(uint32 *)(dst) = v
+
+#define COPY_4X1_LINE(dst, src) \
+ *(uint32 *)(dst) = *(const uint32 *)(src)
+
+#endif /* SCUMM_NEED_ALIGNMENT */
+
+/* Fill a 4x4 pixel block with a literal pixel value */
+
+#define LITERAL_4X4(src, dst, pitch) \
+ do { \
+ int x; \
+ DECLARE_LITERAL_TEMP(t); \
+ READ_LITERAL_PIXEL(src, t); \
+ for (x=0; x<4; x++) { \
+ WRITE_4X1_LINE(dst + pitch * x, t); \
+ } \
+ dst += 4; \
+ } while (0)
+
+/* Fill four 4x1 pixel blocks with literal pixel values */
+
+#define LITERAL_4X1(src, dst, pitch) \
+ do { \
+ int x; \
+ DECLARE_LITERAL_TEMP(t); \
+ for (x=0; x<4; x++) { \
+ READ_LITERAL_PIXEL(src, t); \
+ WRITE_4X1_LINE(dst + pitch * x, t); \
+ } \
+ dst += 4; \
+ } while (0)
+
+/* Fill sixteen 1x1 pixel blocks with literal pixel values */
+
+#define LITERAL_1X1(src, dst, pitch) \
+ do { \
+ int x; \
+ for (x=0; x<4; x++) { \
+ COPY_4X1_LINE(dst + pitch * x, src); \
+ src += 4; \
+ } \
+ dst += 4; \
+ } while (0)
+
+/* Copy a 4x4 pixel block from a different place in the framebuffer */
+
+#define COPY_4X4(dst2, dst, pitch) \
+ do { \
+ int x; \
+ for (x=0; x<4; x++) { \
+ COPY_4X1_LINE(dst + pitch * x, dst2 + pitch * x); \
+ } \
+ dst += 4; \
+ } while (0)
+
+void Codec37Decoder::proc1(byte *dst, const byte *src, int32 next_offs, int bw, int bh, int pitch, int16 *offset_table) {
+ uint8 code;
+ bool filling, skipCode;
+ int32 len;
+ int i, p;
+ uint32 pitches[16];
+
+ i = bw;
+ for (p = 0; p < 16; ++p) {
+ pitches[p] = (p >> 2) * pitch + (p & 0x3);
+ }
+ code = 0;
+ filling = false;
+ len = -1;
+ while (1) {
+ if (len < 0) {
+ filling = (*src & 1) == 1;
+ len = *src++ >> 1;
+ skipCode = false;
+ } else {
+ skipCode = true;
+ }
+ if (!filling || !skipCode) {
+ code = *src++;
+ if (code == 0xFF) {
+ --len;
+ for (p = 0; p < 0x10; ++p) {
+ if (len < 0) {
+ filling = (*src & 1) == 1;
+ len = *src++ >> 1;
+ if (filling) {
+ code = *src++;
+ }
+ }
+ if (filling) {
+ *(dst + pitches[p]) = code;
+ } else {
+ *(dst + pitches[p]) = *src++;
+ }
+ --len;
+ }
+ dst += 4;
+ --i;
+ if (i == 0) {
+ dst += pitch * 3;
+ --bh;
+ if (bh == 0) return;
+ i = bw;
+ }
+ continue;
+ }
+ }
+ byte *dst2 = dst + offset_table[code] + next_offs;
+ COPY_4X4(dst2, dst, pitch);
+ --i;
+ if (i == 0) {
+ dst += pitch * 3;
+ --bh;
+ if (bh == 0) return;
+ i = bw;
+ }
+ --len;
+ }
+}
+
+void Codec37Decoder::proc3WithFDFE(byte *dst, const byte *src, int32 next_offs, int bw, int bh, int pitch, int16 *offset_table) {
+ do {
+ int32 i = bw;
+ do {
+ int32 code = *src++;
+ if (code == 0xFD) {
+ LITERAL_4X4(src, dst, pitch);
+ } else if (code == 0xFE) {
+ LITERAL_4X1(src, dst, pitch);
+ } else if (code == 0xFF) {
+ LITERAL_1X1(src, dst, pitch);
+ } else {
+ byte *dst2 = dst + _offsetTable[code] + next_offs;
+ COPY_4X4(dst2, dst, pitch);
+ }
+ } while (--i);
+ dst += pitch * 3;
+ } while (--bh);
+}
+
+void Codec37Decoder::proc3WithoutFDFE(byte *dst, const byte *src, int32 next_offs, int bw, int bh, int pitch, int16 *offset_table) {
+ do {
+ int32 i = bw;
+ do {
+ int32 code = *src++;
+ if (code == 0xFF) {
+ LITERAL_1X1(src, dst, pitch);
+ } else {
+ byte *dst2 = dst + _offsetTable[code] + next_offs;
+ COPY_4X4(dst2, dst, pitch);
+ }
+ } while (--i);
+ dst += pitch * 3;
+ } while (--bh);
+}
+
+void Codec37Decoder::proc4WithFDFE(byte *dst, const byte *src, int32 next_offs, int bw, int bh, int pitch, int16 *offset_table) {
+ do {
+ int32 i = bw;
+ do {
+ int32 code = *src++;
+ if (code == 0xFD) {
+ LITERAL_4X4(src, dst, pitch);
+ } else if (code == 0xFE) {
+ LITERAL_4X1(src, dst, pitch);
+ } else if (code == 0xFF) {
+ LITERAL_1X1(src, dst, pitch);
+ } else if (code == 0x00) {
+ int32 length = *src++ + 1;
+ for (int32 l = 0; l < length; l++) {
+ byte *dst2 = dst + next_offs;
+ COPY_4X4(dst2, dst, pitch);
+ i--;
+ if (i == 0) {
+ dst += pitch * 3;
+ bh--;
+ i = bw;
+ }
+ }
+ if (bh == 0) {
+ return;
+ }
+ i++;
+ } else {
+ byte *dst2 = dst + _offsetTable[code] + next_offs;
+ COPY_4X4(dst2, dst, pitch);
+ }
+ } while (--i);
+ dst += pitch * 3;
+ } while (--bh);
+}
+
+void Codec37Decoder::proc4WithoutFDFE(byte *dst, const byte *src, int32 next_offs, int bw, int bh, int pitch, int16 *offset_table) {
+ do {
+ int32 i = bw;
+ do {
+ int32 code = *src++;
+ if (code == 0xFF) {
+ LITERAL_1X1(src, dst, pitch);
+ } else if (code == 0x00) {
+ int32 length = *src++ + 1;
+ for (int32 l = 0; l < length; l++) {
+ byte *dst2 = dst + next_offs;
+ COPY_4X4(dst2, dst, pitch);
+ i--;
+ if (i == 0) {
+ dst += pitch * 3;
+ bh--;
+ i = bw;
+ }
+ }
+ if (bh == 0) {
+ return;
+ }
+ i++;
+ } else {
+ byte *dst2 = dst + _offsetTable[code] + next_offs;
+ COPY_4X4(dst2, dst, pitch);
+ }
+ } while (--i);
+ dst += pitch * 3;
+ } while (--bh);
+}
+
+void Codec37Decoder::decode(byte *dst, const byte *src) {
+ int32 bw = (_width + 3) / 4, bh = (_height + 3) / 4;
+ int32 pitch = bw * 4;
+
+ int16 seq_nb = READ_LE_UINT16(src + 2);
+ int32 decoded_size = READ_LE_UINT32(src + 4);
+ byte mask_flags = src[12];
+ maketable(pitch, src[1]);
+ int32 tmp;
+
+ switch(src[0]) {
+ case 0:
+ if ((_deltaBufs[_curtable] - _deltaBuf) > 0) {
+ memset(_deltaBuf, 0, _deltaBufs[_curtable] - _deltaBuf);
+ }
+ tmp = (_deltaBuf + _deltaSize) - _deltaBufs[_curtable] - decoded_size;
+ if (tmp > 0) {
+ memset(_deltaBufs[_curtable] + decoded_size, 0, tmp);
+ }
+ memcpy(_deltaBufs[_curtable], src + 16, decoded_size);
+ break;
+ case 1:
+ if ((seq_nb & 1) || !(mask_flags & 1)) {
+ _curtable ^= 1;
+ }
+ proc1(_deltaBufs[_curtable], src + 16, _deltaBufs[_curtable ^ 1] - _deltaBufs[_curtable],
+ bw, bh, pitch, _offsetTable);
+ break;
+ case 2:
+ bompDecodeLine(_deltaBufs[_curtable], src + 16, decoded_size);
+ if ((_deltaBufs[_curtable] - _deltaBuf) > 0) {
+ memset(_deltaBuf, 0, _deltaBufs[_curtable] - _deltaBuf);
+ }
+ tmp = (_deltaBuf + _deltaSize) - _deltaBufs[_curtable] - decoded_size;
+ if (tmp > 0) {
+ memset(_deltaBufs[_curtable] + decoded_size, 0, tmp);
+ }
+ break;
+ case 3:
+ if ((seq_nb & 1) || !(mask_flags & 1)) {
+ _curtable ^= 1;
+ }
+
+ if ((mask_flags & 4) != 0) {
+ proc3WithFDFE(_deltaBufs[_curtable], src + 16,
+ _deltaBufs[_curtable ^ 1] - _deltaBufs[_curtable], bw, bh,
+ pitch, _offsetTable);
+ } else {
+ proc3WithoutFDFE(_deltaBufs[_curtable], src + 16,
+ _deltaBufs[_curtable ^ 1] - _deltaBufs[_curtable], bw, bh,
+ pitch, _offsetTable);
+ }
+ break;
+ case 4:
+ if ((seq_nb & 1) || !(mask_flags & 1)) {
+ _curtable ^= 1;
+ }
+
+ if ((mask_flags & 4) != 0) {
+ proc4WithFDFE(_deltaBufs[_curtable], src + 16,
+ _deltaBufs[_curtable ^ 1] - _deltaBufs[_curtable], bw, bh,
+ pitch, _offsetTable);
+ } else {
+ proc4WithoutFDFE(_deltaBufs[_curtable], src + 16,
+ _deltaBufs[_curtable ^ 1] - _deltaBufs[_curtable], bw, bh,
+ pitch, _offsetTable);
+ }
+ break;
+ default:
+ break;
+ }
+ _prevSeqNb = seq_nb;
+
+ memcpy(dst, _deltaBufs[_curtable], _frameSize);
+}
+
+} // End of namespace Scumm
+
diff --git a/engines/scumm/smush/codec37.h b/engines/scumm/smush/codec37.h
new file mode 100644
index 0000000000..534316c1e5
--- /dev/null
+++ b/engines/scumm/smush/codec37.h
@@ -0,0 +1,62 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SMUSH_CODEC37_H
+#define SMUSH_CODEC37_H
+
+#include "common/scummsys.h"
+
+namespace Scumm {
+
+class Codec37Decoder {
+private:
+
+ int32 _deltaSize;
+ byte *_deltaBufs[2];
+ byte *_deltaBuf;
+ int16 *_offsetTable;
+ int _curtable;
+ uint16 _prevSeqNb;
+ int _tableLastPitch;
+ int _tableLastIndex;
+ int32 _frameSize;
+ int _width, _height;
+
+public:
+ Codec37Decoder();
+ ~Codec37Decoder();
+ void init(int width, int height);
+ void deinit();
+protected:
+ void maketable(int, int);
+ void proc1(byte *dst, const byte *src, int32, int, int, int, int16 *);
+ void proc3WithFDFE(byte *dst, const byte *src, int32, int, int, int, int16 *);
+ void proc3WithoutFDFE(byte *dst, const byte *src, int32, int, int, int, int16 *);
+ void proc4WithFDFE(byte *dst, const byte *src, int32, int, int, int, int16 *);
+ void proc4WithoutFDFE(byte *dst, const byte *src, int32, int, int, int, int16 *);
+public:
+ void decode(byte *dst, const byte *src);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/smush/codec47.cpp b/engines/scumm/smush/codec47.cpp
new file mode 100644
index 0000000000..ef10a282a9
--- /dev/null
+++ b/engines/scumm/smush/codec47.cpp
@@ -0,0 +1,630 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "scumm/bomp.h"
+#include "scumm/smush/codec47.h"
+
+namespace Scumm {
+
+#if defined(SCUMM_NEED_ALIGNMENT)
+
+#define COPY_4X1_LINE(dst, src) \
+ do { \
+ (dst)[0] = (src)[0]; \
+ (dst)[1] = (src)[1]; \
+ (dst)[2] = (src)[2]; \
+ (dst)[3] = (src)[3]; \
+ } while (0)
+
+#define COPY_2X1_LINE(dst, src) \
+ do { \
+ (dst)[0] = (src)[0]; \
+ (dst)[1] = (src)[1]; \
+ } while (0)
+
+
+#else /* SCUMM_NEED_ALIGNMENT */
+
+#define COPY_4X1_LINE(dst, src) \
+ *(uint32 *)(dst) = *(const uint32 *)(src)
+
+#define COPY_2X1_LINE(dst, src) \
+ *(uint16 *)(dst) = *(const uint16 *)(src)
+
+#endif
+
+#define FILL_4X1_LINE(dst, val) \
+ do { \
+ (dst)[0] = val; \
+ (dst)[1] = val; \
+ (dst)[2] = val; \
+ (dst)[3] = val; \
+ } while (0)
+
+#define FILL_2X1_LINE(dst, val) \
+ do { \
+ (dst)[0] = val; \
+ (dst)[1] = val; \
+ } while (0)
+
+static int8 codec47_table_small1[] = {
+ 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1,
+};
+
+static int8 codec47_table_small2[] = {
+ 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2,
+};
+
+static int8 codec47_table_big1[] = {
+ 0, 2, 5, 7, 7, 7, 7, 7, 7, 5, 2, 0, 0, 0, 0, 0,
+};
+
+static int8 codec47_table_big2[] = {
+ 0, 0, 0, 0, 1, 3, 4, 6, 7, 7, 7, 7, 6, 4, 3, 1,
+};
+
+#ifdef PALMOS_68K
+static const int8 *codec47_table;
+#else
+static const int8 codec47_table[] = {
+ 0, 0, -1, -43, 6, -43, -9, -42, 13, -41,
+ -16, -40, 19, -39, -23, -36, 26, -34, -2, -33,
+ 4, -33, -29, -32, -9, -32, 11, -31, -16, -29,
+ 32, -29, 18, -28, -34, -26, -22, -25, -1, -25,
+ 3, -25, -7, -24, 8, -24, 24, -23, 36, -23,
+ -12, -22, 13, -21, -38, -20, 0, -20, -27, -19,
+ -4, -19, 4, -19, -17, -18, -8, -17, 8, -17,
+ 18, -17, 28, -17, 39, -17, -12, -15, 12, -15,
+ -21, -14, -1, -14, 1, -14, -41, -13, -5, -13,
+ 5, -13, 21, -13, -31, -12, -15, -11, -8, -11,
+ 8, -11, 15, -11, -2, -10, 1, -10, 31, -10,
+ -23, -9, -11, -9, -5, -9, 4, -9, 11, -9,
+ 42, -9, 6, -8, 24, -8, -18, -7, -7, -7,
+ -3, -7, -1, -7, 2, -7, 18, -7, -43, -6,
+ -13, -6, -4, -6, 4, -6, 8, -6, -33, -5,
+ -9, -5, -2, -5, 0, -5, 2, -5, 5, -5,
+ 13, -5, -25, -4, -6, -4, -3, -4, 3, -4,
+ 9, -4, -19, -3, -7, -3, -4, -3, -2, -3,
+ -1, -3, 0, -3, 1, -3, 2, -3, 4, -3,
+ 6, -3, 33, -3, -14, -2, -10, -2, -5, -2,
+ -3, -2, -2, -2, -1, -2, 0, -2, 1, -2,
+ 2, -2, 3, -2, 5, -2, 7, -2, 14, -2,
+ 19, -2, 25, -2, 43, -2, -7, -1, -3, -1,
+ -2, -1, -1, -1, 0, -1, 1, -1, 2, -1,
+ 3, -1, 10, -1, -5, 0, -3, 0, -2, 0,
+ -1, 0, 1, 0, 2, 0, 3, 0, 5, 0,
+ 7, 0, -10, 1, -7, 1, -3, 1, -2, 1,
+ -1, 1, 0, 1, 1, 1, 2, 1, 3, 1,
+ -43, 2, -25, 2, -19, 2, -14, 2, -5, 2,
+ -3, 2, -2, 2, -1, 2, 0, 2, 1, 2,
+ 2, 2, 3, 2, 5, 2, 7, 2, 10, 2,
+ 14, 2, -33, 3, -6, 3, -4, 3, -2, 3,
+ -1, 3, 0, 3, 1, 3, 2, 3, 4, 3,
+ 19, 3, -9, 4, -3, 4, 3, 4, 7, 4,
+ 25, 4, -13, 5, -5, 5, -2, 5, 0, 5,
+ 2, 5, 5, 5, 9, 5, 33, 5, -8, 6,
+ -4, 6, 4, 6, 13, 6, 43, 6, -18, 7,
+ -2, 7, 0, 7, 2, 7, 7, 7, 18, 7,
+ -24, 8, -6, 8, -42, 9, -11, 9, -4, 9,
+ 5, 9, 11, 9, 23, 9, -31, 10, -1, 10,
+ 2, 10, -15, 11, -8, 11, 8, 11, 15, 11,
+ 31, 12, -21, 13, -5, 13, 5, 13, 41, 13,
+ -1, 14, 1, 14, 21, 14, -12, 15, 12, 15,
+ -39, 17, -28, 17, -18, 17, -8, 17, 8, 17,
+ 17, 18, -4, 19, 0, 19, 4, 19, 27, 19,
+ 38, 20, -13, 21, 12, 22, -36, 23, -24, 23,
+ -8, 24, 7, 24, -3, 25, 1, 25, 22, 25,
+ 34, 26, -18, 28, -32, 29, 16, 29, -11, 31,
+ 9, 32, 29, 32, -4, 33, 2, 33, -26, 34,
+ 23, 36, -19, 39, 16, 40, -13, 41, 9, 42,
+ -6, 43, 1, 43, 0, 0, 0, 0, 0, 0
+};
+#endif
+
+void Codec47Decoder::makeTablesInterpolation(int param) {
+ int32 variable1, variable2;
+ int32 b1, b2;
+ int32 value_table47_1_2, value_table47_1_1, value_table47_2_2, value_table47_2_1;
+ int32 tableSmallBig[64], tmp, s;
+ int8 *table47_1 = 0, *table47_2 = 0;
+ int32 *ptr_small_big;
+ byte *ptr;
+ int i, x, y;
+
+ if (param == 8) {
+ table47_1 = codec47_table_big1;
+ table47_2 = codec47_table_big2;
+ ptr = _tableBig + 384;
+ for (i = 0; i < 256; i++) {
+ *ptr = 0;
+ ptr += 388;
+ }
+ ptr = _tableBig + 385;
+ for (i = 0; i < 256; i++) {
+ *ptr = 0;
+ ptr += 388;
+ }
+ } else if (param == 4) {
+ table47_1 = codec47_table_small1;
+ table47_2 = codec47_table_small2;
+ ptr = _tableSmall + 96;
+ for (i = 0; i < 256; i++) {
+ *ptr = 0;
+ ptr += 128;
+ }
+ ptr = _tableSmall + 97;
+ for (i = 0; i < 256; i++) {
+ *ptr = 0;
+ ptr += 128;
+ }
+ } else {
+ error("Codec47Decoder::makeTablesInterpolation: unknown param %d", param);
+ }
+
+ s = 0;
+ for (x = 0; x < 16; x++) {
+ value_table47_1_1 = table47_1[x];
+ value_table47_2_1 = table47_2[x];
+ for (y = 0; y < 16; y++) {
+ value_table47_1_2 = table47_1[y];
+ value_table47_2_2 = table47_2[y];
+
+ if (value_table47_2_1 == 0) {
+ b1 = 0;
+ } else if (value_table47_2_1 == param - 1) {
+ b1 = 1;
+ } else if (value_table47_1_1 == 0) {
+ b1 = 2;
+ } else if (value_table47_1_1 == param - 1) {
+ b1 = 3;
+ } else {
+ b1 = 4;
+ }
+
+ if (value_table47_2_2 == 0) {
+ b2 = 0;
+ } else if (value_table47_2_2 == param - 1) {
+ b2 = 1;
+ } else if (value_table47_1_2 == 0) {
+ b2 = 2;
+ } else if (value_table47_1_2 == param - 1) {
+ b2 = 3;
+ } else {
+ b2 = 4;
+ }
+
+ memset(tableSmallBig, 0, param * param * 4);
+
+ variable2 = ABS(value_table47_2_2 - value_table47_2_1);
+ tmp = ABS(value_table47_1_2 - value_table47_1_1);
+ if (variable2 <= tmp) {
+ variable2 = tmp;
+ }
+
+ for (variable1 = 0; variable1 <= variable2; variable1++) {
+ int32 variable3, variable4;
+
+ if (variable2 > 0) {
+ // Linearly interpolate between value_table47_1_1 and value_table47_1_2
+ // respectively value_table47_2_1 and value_table47_2_2.
+ variable4 = (value_table47_1_1 * variable1 + value_table47_1_2 * (variable2 - variable1) + variable2 / 2) / variable2;
+ variable3 = (value_table47_2_1 * variable1 + value_table47_2_2 * (variable2 - variable1) + variable2 / 2) / variable2;
+ } else {
+ variable4 = value_table47_1_1;
+ variable3 = value_table47_2_1;
+ }
+ ptr_small_big = &tableSmallBig[param * variable3 + variable4];
+ *ptr_small_big = 1;
+
+ if ((b1 == 2 && b2 == 3) || (b2 == 2 && b1 == 3) ||
+ (b1 == 0 && b2 != 1) || (b2 == 0 && b1 != 1)) {
+ if (variable3 >= 0) {
+ i = variable3 + 1;
+ while (i--) {
+ *ptr_small_big = 1;
+ ptr_small_big -= param;
+ }
+ }
+ } else if ((b2 != 0 && b1 == 1) || (b1 != 0 && b2 == 1)) {
+ if (param > variable3) {
+ i = param - variable3;
+ while (i--) {
+ *ptr_small_big = 1;
+ ptr_small_big += param;
+ }
+ }
+ } else if ((b1 == 2 && b2 != 3) || (b2 == 2 && b1 != 3)) {
+ if (variable4 >= 0) {
+ i = variable4 + 1;
+ while (i--) {
+ *(ptr_small_big--) = 1;
+ }
+ }
+ } else if ((b1 == 0 && b2 == 1) || (b2 == 0 && b1 == 1) ||
+ (b1 == 3 && b2 != 2) || (b2 == 3 && b1 != 2)) {
+ if (param > variable4) {
+ i = param - variable4;
+ while (i--) {
+ *(ptr_small_big++) = 1;
+ }
+ }
+ }
+ }
+
+ if (param == 8) {
+ for (i = 64 - 1; i >= 0; i--) {
+ if (tableSmallBig[i] != 0) {
+ _tableBig[256 + s + _tableBig[384 + s]] = (byte)i;
+ _tableBig[384 + s]++;
+ } else {
+ _tableBig[320 + s + _tableBig[385 + s]] = (byte)i;
+ _tableBig[385 + s]++;
+ }
+ }
+ s += 388;
+ }
+ if (param == 4) {
+ for (i = 16 - 1; i >= 0; i--) {
+ if (tableSmallBig[i] != 0) {
+ _tableSmall[64 + s + _tableSmall[96 + s]] = (byte)i;
+ _tableSmall[96 + s]++;
+ } else {
+ _tableSmall[80 + s + _tableSmall[97 + s]] = (byte)i;
+ _tableSmall[97 + s]++;
+ }
+ }
+ s += 128;
+ }
+ }
+ }
+}
+
+void Codec47Decoder::makeTables47(int width) {
+ if (_lastTableWidth == width)
+ return;
+
+ _lastTableWidth = width;
+
+ int32 a, c, d;
+ int16 tmp;
+
+ for (int l = 0; l < 512; l += 2) {
+ _table[l / 2] = (int16)(codec47_table[l + 1] * width + codec47_table[l]);
+ }
+
+ a = 0;
+ c = 0;
+ do {
+ for (d = 0; d < _tableSmall[96 + c]; d++) {
+ tmp = _tableSmall[64 + c + d];
+ tmp = (int16)((byte)(tmp >> 2) * width + (tmp & 3));
+ _tableSmall[c + d * 2] = (byte)tmp;
+ _tableSmall[c + d * 2 + 1] = tmp >> 8;
+ }
+ for (d = 0; d < _tableSmall[97 + c]; d++) {
+ tmp = _tableSmall[80 + c + d];
+ tmp = (int16)((byte)(tmp >> 2) * width + (tmp & 3));
+ _tableSmall[32 + c + d * 2] = (byte)tmp;
+ _tableSmall[32 + c + d * 2 + 1] = tmp >> 8;
+ }
+ for (d = 0; d < _tableBig[384 + a]; d++) {
+ tmp = _tableBig[256 + a + d];
+ tmp = (int16)((byte)(tmp >> 3) * width + (tmp & 7));
+ _tableBig[a + d * 2] = (byte)tmp;
+ _tableBig[a + d * 2 + 1] = tmp >> 8;
+ }
+ for (d = 0; d < _tableBig[385 + a]; d++) {
+ tmp = _tableBig[320 + a + d];
+ tmp = (int16)((byte)(tmp >> 3) * width + (tmp & 7));
+ _tableBig[128 + a + d * 2] = (byte)tmp;
+ _tableBig[128 + a + d * 2 + 1] = tmp >> 8;
+ }
+
+ a += 388;
+ c += 128;
+ } while (c < 32768);
+}
+
+void Codec47Decoder::level3(byte *d_dst) {
+ int32 tmp;
+ byte code = *_d_src++;
+
+ if (code < 0xF8) {
+ tmp = _table[code] + _offset1;
+ COPY_2X1_LINE(d_dst, d_dst + tmp);
+ COPY_2X1_LINE(d_dst + _d_pitch, d_dst + _d_pitch + tmp);
+ } else if (code == 0xFF) {
+ COPY_2X1_LINE(d_dst, _d_src + 0);
+ COPY_2X1_LINE(d_dst + _d_pitch, _d_src + 2);
+ _d_src += 4;
+ } else if (code == 0xFE) {
+ byte t = *_d_src++;
+ FILL_2X1_LINE(d_dst, t);
+ FILL_2X1_LINE(d_dst + _d_pitch, t);
+ } else if (code == 0xFC) {
+ tmp = _offset2;
+ COPY_2X1_LINE(d_dst, d_dst + tmp);
+ COPY_2X1_LINE(d_dst + _d_pitch, d_dst + _d_pitch + tmp);
+ } else {
+ byte t = _paramPtr[code];
+ FILL_2X1_LINE(d_dst, t);
+ FILL_2X1_LINE(d_dst + _d_pitch, t);
+ }
+}
+
+void Codec47Decoder::level2(byte *d_dst) {
+ int32 tmp;
+ byte code = *_d_src++;
+ int i;
+
+ if (code < 0xF8) {
+ tmp = _table[code] + _offset1;
+ for (i = 0; i < 4; i++) {
+ COPY_4X1_LINE(d_dst, d_dst + tmp);
+ d_dst += _d_pitch;
+ }
+ } else if (code == 0xFF) {
+ level3(d_dst);
+ d_dst += 2;
+ level3(d_dst);
+ d_dst += _d_pitch * 2 - 2;
+ level3(d_dst);
+ d_dst += 2;
+ level3(d_dst);
+ } else if (code == 0xFE) {
+ byte t = *_d_src++;
+ for (i = 0; i < 4; i++) {
+ FILL_4X1_LINE(d_dst, t);
+ d_dst += _d_pitch;
+ }
+ } else if (code == 0xFD) {
+ byte *tmp_ptr = _tableSmall + *_d_src++ * 128;
+ int32 l = tmp_ptr[96];
+ byte val = *_d_src++;
+ int16 *tmp_ptr2 = (int16 *)tmp_ptr;
+ while (l--) {
+ *(d_dst + READ_LE_UINT16(tmp_ptr2)) = val;
+ tmp_ptr2++;
+ }
+ l = tmp_ptr[97];
+ val = *_d_src++;
+ tmp_ptr2 = (int16 *)(tmp_ptr + 32);
+ while (l--) {
+ *(d_dst + READ_LE_UINT16(tmp_ptr2)) = val;
+ tmp_ptr2++;
+ }
+ } else if (code == 0xFC) {
+ tmp = _offset2;
+ for (i = 0; i < 4; i++) {
+ COPY_4X1_LINE(d_dst, d_dst + tmp);
+ d_dst += _d_pitch;
+ }
+ } else {
+ byte t = _paramPtr[code];
+ for (i = 0; i < 4; i++) {
+ FILL_4X1_LINE(d_dst, t);
+ d_dst += _d_pitch;
+ }
+ }
+}
+
+void Codec47Decoder::level1(byte *d_dst) {
+ int32 tmp, tmp2;
+ byte code = *_d_src++;
+ int i;
+
+ if (code < 0xF8) {
+ tmp2 = _table[code] + _offset1;
+ for (i = 0; i < 8; i++) {
+ COPY_4X1_LINE(d_dst + 0, d_dst + tmp2);
+ COPY_4X1_LINE(d_dst + 4, d_dst + tmp2 + 4);
+ d_dst += _d_pitch;
+ }
+ } else if (code == 0xFF) {
+ level2(d_dst);
+ d_dst += 4;
+ level2(d_dst);
+ d_dst += _d_pitch * 4 - 4;
+ level2(d_dst);
+ d_dst += 4;
+ level2(d_dst);
+ } else if (code == 0xFE) {
+ byte t = *_d_src++;
+ for (i = 0; i < 8; i++) {
+ FILL_4X1_LINE(d_dst, t);
+ FILL_4X1_LINE(d_dst + 4, t);
+ d_dst += _d_pitch;
+ }
+ } else if (code == 0xFD) {
+ tmp = *_d_src++;
+ byte *tmp_ptr = _tableBig + tmp * 388;
+ byte l = tmp_ptr[384];
+ byte val = *_d_src++;
+ int16 *tmp_ptr2 = (int16 *)tmp_ptr;
+ while (l--) {
+ *(d_dst + READ_LE_UINT16(tmp_ptr2)) = val;
+ tmp_ptr2++;
+ }
+ l = tmp_ptr[385];
+ val = *_d_src++;
+ tmp_ptr2 = (int16 *)(tmp_ptr + 128);
+ while (l--) {
+ *(d_dst + READ_LE_UINT16(tmp_ptr2)) = val;
+ tmp_ptr2++;
+ }
+ } else if (code == 0xFC) {
+ tmp2 = _offset2;
+ for (i = 0; i < 8; i++) {
+ COPY_4X1_LINE(d_dst + 0, d_dst + tmp2);
+ COPY_4X1_LINE(d_dst + 4, d_dst + tmp2 + 4);
+ d_dst += _d_pitch;
+ }
+ } else {
+ byte t = _paramPtr[code];
+ for (i = 0; i < 8; i++) {
+ FILL_4X1_LINE(d_dst, t);
+ FILL_4X1_LINE(d_dst + 4, t);
+ d_dst += _d_pitch;
+ }
+ }
+}
+
+void Codec47Decoder::decode2(byte *dst, const byte *src, int width, int height, const byte *param_ptr) {
+ _d_src = src;
+ _paramPtr = param_ptr - 0xf8;
+ int bw = (width + 7) / 8;
+ int bh = (height + 7) / 8;
+ int next_line = width * 7;
+ _d_pitch = width;
+
+ do {
+ int tmp_bw = bw;
+ do {
+ level1(dst);
+ dst += 8;
+ } while (--tmp_bw);
+ dst += next_line;
+ } while (--bh);
+}
+
+void Codec47Decoder::init(int width, int height) {
+ deinit();
+ _width = width;
+ _height = height;
+ makeTablesInterpolation(4);
+ makeTablesInterpolation(8);
+
+ _frameSize = _width * _height;
+ _deltaSize = _frameSize * 3;
+ _deltaBuf = (byte *)malloc(_deltaSize);
+ _deltaBufs[0] = _deltaBuf;
+ _deltaBufs[1] = _deltaBuf + _frameSize;
+ _curBuf = _deltaBuf + _frameSize * 2;
+}
+
+Codec47Decoder::Codec47Decoder() {
+ _tableBig = (byte *)malloc(99328);
+ _tableSmall = (byte *)malloc(32768);
+ _deltaBuf = NULL;
+}
+
+void Codec47Decoder::deinit() {
+ _lastTableWidth = -1;
+ if (_deltaBuf) {
+ free(_deltaBuf);
+ _deltaSize = 0;
+ _deltaBuf = NULL;
+ _deltaBufs[0] = NULL;
+ _deltaBufs[1] = NULL;
+ }
+}
+
+Codec47Decoder::~Codec47Decoder() {
+ if (_tableBig) {
+ free(_tableBig);
+ _tableBig = NULL;
+ }
+ if (_tableSmall) {
+ free(_tableSmall);
+ _tableSmall = NULL;
+ }
+ deinit();
+}
+
+bool Codec47Decoder::decode(byte *dst, const byte *src) {
+ _offset1 = _deltaBufs[1] - _curBuf;
+ _offset2 = _deltaBufs[0] - _curBuf;
+
+ int32 seq_nb = READ_LE_UINT16(src + 0);
+
+ const byte *gfx_data = src + 26;
+ byte *tmp_ptr;
+
+ if (seq_nb == 0) {
+ makeTables47(_width);
+ memset(_deltaBufs[0], src[12], _frameSize);
+ memset(_deltaBufs[1], src[13], _frameSize);
+ _prevSeqNb = -1;
+ }
+
+ if ((src[4] & 1) != 0) {
+ gfx_data += 32896;
+ }
+
+ switch(src[2]) {
+ case 0:
+ memcpy(_curBuf, gfx_data, _frameSize);
+ break;
+ case 1:
+ error("codec47: not implemented decode1 proc");
+ break;
+ case 2:
+ if (seq_nb == _prevSeqNb + 1) {
+ decode2(_curBuf, gfx_data, _width, _height, src + 8);
+ }
+ break;
+ case 3:
+ memcpy(_curBuf, _deltaBufs[1], _frameSize);
+ break;
+ case 4:
+ memcpy(_curBuf, _deltaBufs[0], _frameSize);
+ break;
+ case 5:
+ bompDecodeLine(_curBuf, gfx_data, READ_LE_UINT32(src + 14));
+ break;
+ }
+
+ memcpy(dst, _curBuf, _frameSize);
+
+ if (seq_nb == _prevSeqNb + 1) {
+ if (src[3] == 1) {
+ tmp_ptr = _curBuf;
+ _curBuf = _deltaBufs[1];
+ _deltaBufs[1] = tmp_ptr;
+ } else if (src[3] == 2) {
+ tmp_ptr = _deltaBufs[0];
+ _deltaBufs[0] = _deltaBufs[1];
+ _deltaBufs[1] = _curBuf;
+ _curBuf = tmp_ptr;
+ }
+ }
+ _prevSeqNb = seq_nb;
+
+ return true;
+}
+
+} // End of namespace Scumm
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Codec47)
+_GSETPTR(Scumm::codec47_table, GBVARS_CODEC47TABLE_INDEX, int8, GBVARS_SCUMM)
+_GEND
+
+_GRELEASE(Codec47)
+_GRELEASEPTR(GBVARS_CODEC47TABLE_INDEX, GBVARS_SCUMM)
+_GEND
+
+#endif
diff --git a/engines/scumm/smush/codec47.h b/engines/scumm/smush/codec47.h
new file mode 100644
index 0000000000..ea71c22963
--- /dev/null
+++ b/engines/scumm/smush/codec47.h
@@ -0,0 +1,65 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SMUSH_CODEC_47_H
+#define SMUSH_CODEC_47_H
+
+#include "common/scummsys.h"
+
+namespace Scumm {
+
+class Codec47Decoder {
+private:
+
+ int32 _deltaSize;
+ byte *_deltaBufs[2];
+ byte *_deltaBuf;
+ byte *_curBuf;
+ int32 _prevSeqNb;
+ int _lastTableWidth;
+ const byte *_d_src, *_paramPtr;
+ int _d_pitch;
+ int32 _offset1, _offset2;
+ byte *_tableBig;
+ byte *_tableSmall;
+ int16 _table[256];
+ int32 _frameSize;
+ int _width, _height;
+
+ void makeTablesInterpolation(int param);
+ void makeTables47(int width);
+ void level1(byte *d_dst);
+ void level2(byte *d_dst);
+ void level3(byte *d_dst);
+ void decode2(byte *dst, const byte *src, int width, int height, const byte *param_ptr);
+
+public:
+ Codec47Decoder();
+ ~Codec47Decoder();
+ void init(int width, int height);
+ void deinit();
+ bool decode(byte *dst, const byte *src);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/smush/imuse_channel.cpp b/engines/scumm/smush/imuse_channel.cpp
new file mode 100644
index 0000000000..76a10a9e9e
--- /dev/null
+++ b/engines/scumm/smush/imuse_channel.cpp
@@ -0,0 +1,347 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/smush/channel.h"
+#include "scumm/smush/chunk.h"
+#include "scumm/smush/chunk_type.h"
+
+namespace Scumm {
+
+ImuseChannel::ImuseChannel(int32 track, int32 freq) :
+ _track(track),
+ _tbuffer(0),
+ _tbufferSize(0),
+ _sbuffer(0),
+ _sbufferSize(0),
+ _frequency(freq),
+ _dataSize(-1),
+ _inData(false) {
+}
+
+ImuseChannel::~ImuseChannel() {
+ if (_tbuffer) {
+ delete []_tbuffer;
+ }
+ if (_sbuffer) {
+ warning("_sbuffer should be 0 !!!");
+ delete []_sbuffer;
+ }
+}
+
+bool ImuseChannel::isTerminated() const {
+ return (_dataSize <= 0 && _sbuffer == 0);
+}
+
+bool ImuseChannel::setParameters(int32 nb, int32 size, int32 flags, int32 unk1, int32) {
+ if ((flags == 1) || (flags == 2) || (flags == 3)) {
+ _volume = 127;
+ } else if ((flags >= 100) && (flags <= 163)) {
+ _volume = flags * 2 - 200;
+ } else if ((flags >= 200) && (flags <= 263)) {
+ _volume = flags * 2 - 400;
+ } else if ((flags >= 300) && (flags <= 363)) {
+ _volume = flags * 2 - 600;
+ } else {
+ error("ImuseChannel::setParameters(): bad flags: %d", flags);
+ }
+ _pan = 0;
+ return true;
+}
+
+bool ImuseChannel::checkParameters(int32 index, int32 nbframes, int32 size, int32 track_flags, int32 unk1) {
+ return true;
+}
+
+bool ImuseChannel::appendData(Chunk &b, int32 size) {
+ if (_dataSize == -1) {
+ assert(size > 8);
+ Chunk::type imus_type = b.getDword(); imus_type = SWAP_BYTES_32(imus_type);
+ uint32 imus_size = b.getDword(); imus_size = SWAP_BYTES_32(imus_size);
+ if (imus_type != TYPE_iMUS)
+ error("Invalid Chunk for imuse_channel");
+ size -= 8;
+ _tbufferSize = size;
+ assert(_tbufferSize);
+ _tbuffer = new byte[_tbufferSize];
+ if (!_tbuffer)
+ error("imuse_channel failed to allocate memory");
+ b.read(_tbuffer, size);
+ _dataSize = -2;
+ } else {
+ if (_tbuffer) {
+ byte *old = _tbuffer;
+ int32 new_size = size + _tbufferSize;
+ _tbuffer = new byte[new_size];
+ if (!_tbuffer)
+ error("imuse_channel failed to allocate memory");
+ memcpy(_tbuffer, old, _tbufferSize);
+ delete []old;
+ b.read(_tbuffer + _tbufferSize, size);
+ _tbufferSize += size;
+ } else {
+ _tbufferSize = size;
+ _tbuffer = new byte[_tbufferSize];
+ if (!_tbuffer)
+ error("imuse_channel failed to allocate memory");
+ b.read(_tbuffer, size);
+ }
+ }
+ return processBuffer();
+}
+
+bool ImuseChannel::handleFormat(Chunk &src) {
+ if (src.getSize() != 20) error("invalid size for FRMT Chunk");
+ uint32 imuse_start = src.getDword();
+ imuse_start = SWAP_BYTES_32(imuse_start);
+ src.seek(4);
+ _bitsize = src.getDword();
+ _bitsize = SWAP_BYTES_32(_bitsize);
+ _rate = src.getDword();
+ _rate = SWAP_BYTES_32(_rate);
+ _channels = src.getDword();
+ _channels = SWAP_BYTES_32(_channels);
+ assert(_channels == 1 || _channels == 2);
+ return true;
+}
+
+bool ImuseChannel::handleRegion(Chunk &src) {
+ if (src.getSize() != 8)
+ error("invalid size for REGN Chunk");
+ return true;
+}
+
+bool ImuseChannel::handleStop(Chunk &src) {
+ if (src.getSize() != 4)
+ error("invalid size for STOP Chunk");
+ return true;
+}
+
+bool ImuseChannel::handleMap(Chunk &map) {
+ while (!map.eof()) {
+ Chunk *sub = map.subBlock();
+ switch(sub->getType()) {
+ case TYPE_FRMT:
+ handleFormat(*sub);
+ break;
+ case TYPE_TEXT:
+ break;
+ case TYPE_REGN:
+ handleRegion(*sub);
+ break;
+ case TYPE_STOP:
+ handleStop(*sub);
+ break;
+ default:
+ error("Unknown iMUS subChunk found : %s, %d", Chunk::ChunkString(sub->getType()), sub->getSize());
+ }
+ delete sub;
+ }
+ return true;
+}
+
+void ImuseChannel::decode() {
+ int remaining_size = _sbufferSize % 3;
+ if (remaining_size) {
+ _srbufferSize -= remaining_size;
+ assert(_inData);
+ if (_tbuffer == 0) {
+ _tbuffer = new byte[remaining_size];
+ memcpy(_tbuffer, _sbuffer + _sbufferSize - remaining_size, remaining_size);
+ _tbufferSize = remaining_size;
+ _sbufferSize -= remaining_size;
+ } else {
+ debugC(DEBUG_SMUSH, "impossible ! : %p, %d, %d, %p(%d), %p(%d, %d)",
+ this, _dataSize, _inData, _tbuffer, _tbufferSize, _sbuffer, _sbufferSize, _srbufferSize);
+ byte *old = _tbuffer;
+ int new_size = remaining_size + _tbufferSize;
+ _tbuffer = new byte[new_size];
+ if (!_tbuffer) error("imuse_channel failed to allocate memory");
+ memcpy(_tbuffer, old, _tbufferSize);
+ delete []old;
+ memcpy(_tbuffer + _tbufferSize, _sbuffer + _sbufferSize - remaining_size, remaining_size);
+ _tbufferSize += remaining_size;
+ }
+ }
+
+ // FIXME: Code duplication! See decode12BitsSample() in scumm/imuse_digi.cpp
+
+ int loop_size = _sbufferSize / 3;
+ int new_size = loop_size * 4;
+ byte *keep, *decoded;
+ uint32 value;
+ keep = decoded = new byte[new_size];
+ assert(keep);
+ unsigned char * source = _sbuffer;
+
+ while (loop_size--) {
+ byte v1 = *source++;
+ byte v2 = *source++;
+ byte v3 = *source++;
+ value = ((((v2 & 0x0f) << 8) | v1) << 4) - 0x8000;
+ WRITE_BE_UINT16(decoded, value); decoded += 2;
+ value = ((((v2 & 0xf0) << 4) | v3) << 4) - 0x8000;
+ WRITE_BE_UINT16(decoded, value); decoded += 2;
+ }
+ delete []_sbuffer;
+ _sbuffer = (byte *)keep;
+ _sbufferSize = new_size;
+}
+
+bool ImuseChannel::handleSubTags(int32 &offset) {
+ if (_tbufferSize - offset >= 8) {
+ Chunk::type type = READ_BE_UINT32(_tbuffer + offset);
+ uint32 size = READ_BE_UINT32(_tbuffer + offset + 4);
+ uint32 available_size = _tbufferSize - offset;
+ switch(type) {
+ case TYPE_MAP_:
+ _inData = false;
+ if (available_size >= (size + 8)) {
+ MemoryChunk c((byte *)_tbuffer + offset);
+ handleMap(c);
+ }
+ break;
+ case TYPE_DATA:
+ _inData = true;
+ _dataSize = size;
+ offset += 8;
+ {
+ int reqsize = 1;
+ if (_channels == 2)
+ reqsize *= 2;
+ if (_bitsize == 16)
+ reqsize *= 2;
+ else if (_bitsize == 12) {
+ if (reqsize > 1)
+ reqsize = reqsize * 3 / 2;
+ else reqsize = 3;
+ }
+ if ((size % reqsize) != 0) {
+ debugC(DEBUG_SMUSH, "Invalid iMUS sound data size : (%d %% %d) != 0, correcting...", size, reqsize);
+ size += 3 - (size % reqsize);
+ }
+ }
+ return false;
+ default:
+ error("unknown Chunk in iMUS track : %s ", Chunk::ChunkString(type));
+ }
+ offset += size + 8;
+ return true;
+ }
+ return false;
+}
+
+bool ImuseChannel::processBuffer() {
+ assert(_tbuffer != 0);
+ assert(_tbufferSize != 0);
+ assert(_sbuffer == 0);
+ assert(_sbufferSize == 0);
+
+ if (_inData) {
+ if (_dataSize < _tbufferSize) {
+ int32 offset= _dataSize;
+ while (handleSubTags(offset));
+ _sbufferSize = _dataSize;
+ _sbuffer = _tbuffer;
+ if (offset < _tbufferSize) {
+ int32 new_size = _tbufferSize - offset;
+ _tbuffer = new byte[new_size];
+ if (!_tbuffer) error("imuse_channel failed to allocate memory");
+ memcpy(_tbuffer, _sbuffer + offset, new_size);
+ _tbufferSize = new_size;
+ } else {
+ _tbuffer = 0;
+ _tbufferSize = 0;
+ }
+ if (_sbufferSize == 0) {
+ delete []_sbuffer;
+ _sbuffer = 0;
+ }
+ } else {
+ _sbufferSize = _tbufferSize;
+ _sbuffer = _tbuffer;
+ _tbufferSize = 0;
+ _tbuffer = 0;
+ }
+ } else {
+ int32 offset = 0;
+ while (handleSubTags(offset));
+ if (_inData) {
+ _sbufferSize = _tbufferSize - offset;
+ assert(_sbufferSize);
+ _sbuffer = new byte[_sbufferSize];
+ if (!_sbuffer) error("imuse_channel failed to allocate memory");
+ memcpy(_sbuffer, _tbuffer + offset, _sbufferSize);
+ delete []_tbuffer;
+ _tbuffer = 0;
+ _tbufferSize = 0;
+ } else {
+ if (offset) {
+ byte * old = _tbuffer;
+ int32 new_size = _tbufferSize - offset;
+ _tbuffer = new byte[new_size];
+ if (!_tbuffer) error("imuse_channel failed to allocate memory");
+ memcpy(_tbuffer, old + offset, new_size);
+ _tbufferSize = new_size;
+ delete []old;
+ }
+ }
+ }
+ _srbufferSize = _sbufferSize;
+ if (_sbuffer && _bitsize == 12) decode();
+ return true;
+}
+
+int32 ImuseChannel::availableSoundData(void) const {
+ int32 ret = _sbufferSize;
+ if (_channels == 2) ret /= 2;
+ if (_bitsize > 8) ret /= 2;
+ return ret;
+}
+
+void ImuseChannel::getSoundData(int16 *snd, int32 size) {
+ if (_dataSize <= 0 || _bitsize <= 8) error("invalid call to imuse_channel::read_sound_data()");
+ if (_channels == 2) size *= 2;
+
+ memcpy(snd, _sbuffer, size * 2);
+
+ delete []_sbuffer;
+ assert(_sbufferSize == 2 * size);
+ _sbuffer = 0;
+ _sbufferSize = 0;
+ _dataSize -= _srbufferSize;
+}
+
+void ImuseChannel::getSoundData(int8 *snd, int32 size) {
+ if (_dataSize <= 0 || _bitsize > 8) error("invalid call to imuse_channel::read_sound_data()");
+ if (_channels == 2) size *= 2;
+
+ memcpy(snd, _sbuffer, size);
+
+ delete []_sbuffer;
+ _sbuffer = 0;
+ _sbufferSize = 0;
+ _dataSize -= _srbufferSize;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/smush/saud_channel.cpp b/engines/scumm/smush/saud_channel.cpp
new file mode 100644
index 0000000000..2277336570
--- /dev/null
+++ b/engines/scumm/smush/saud_channel.cpp
@@ -0,0 +1,268 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/smush/channel.h"
+#include "scumm/smush/chunk.h"
+#include "scumm/smush/chunk_type.h"
+
+namespace Scumm {
+
+void SaudChannel::handleStrk(Chunk &b) {
+ int32 size = b.getSize();
+ if (size != 14 && size != 10) {
+ error("STRK has an invalid size : %d", size);
+ }
+}
+
+void SaudChannel::handleSmrk(Chunk &b) {
+ _markReached = true;
+}
+
+void SaudChannel::handleShdr(Chunk &b) {
+ int32 size = b.getSize();
+ if (size != 4)
+ error("SHDR has an invalid size : %d", size);
+}
+
+bool SaudChannel::handleSubTags(int32 &offset) {
+ if (_tbufferSize - offset >= 8) {
+ Chunk::type type = READ_BE_UINT32(_tbuffer + offset);
+ uint32 size = READ_BE_UINT32(_tbuffer + offset + 4);
+ uint32 available_size = _tbufferSize - offset;
+
+ switch(type) {
+ case TYPE_STRK:
+ _inData = false;
+ if (available_size >= (size + 8)) {
+ MemoryChunk c((byte *)_tbuffer + offset);
+ handleStrk(c);
+ } else
+ return false;
+ break;
+ case TYPE_SMRK:
+ _inData = false;
+ if (available_size >= (size + 8)) {
+ MemoryChunk c((byte *)_tbuffer + offset);
+ handleSmrk(c);
+ } else
+ return false;
+ break;
+ case TYPE_SHDR:
+ _inData = false;
+ if (available_size >= (size + 8)) {
+ MemoryChunk c((byte *)_tbuffer + offset);
+ handleShdr(c);
+ } else
+ return false;
+ break;
+ case TYPE_SDAT:
+ _inData = true;
+ _dataSize = size;
+ offset += 8;
+ return false;
+ default:
+ error("unknown Chunk in SAUD track : %s ", Chunk::ChunkString(type));
+ }
+ offset += size + 8;
+ return true;
+ }
+ return false;
+}
+
+bool SaudChannel::processBuffer() {
+ assert(_tbuffer != 0);
+ assert(_tbufferSize != 0);
+ assert(_sbuffer == 0);
+ assert(_sbufferSize == 0);
+
+ if (_keepSize) {
+ _sbufferSize = _tbufferSize;
+ _sbuffer = _tbuffer;
+ _tbufferSize = 0;
+ _tbuffer = 0;
+ } else if (_inData) {
+ if (_dataSize < _tbufferSize) {
+ int32 offset = _dataSize;
+ while (handleSubTags(offset)) ;
+ _sbufferSize = _dataSize;
+ _sbuffer = _tbuffer;
+ if (offset < _tbufferSize) {
+ int new_size = _tbufferSize - offset;
+ _tbuffer = new byte[new_size];
+ if (!_tbuffer)
+ error("SaudChannel failed to allocate memory");
+ memcpy(_tbuffer, _sbuffer + offset, new_size);
+ _tbufferSize = new_size;
+ } else {
+ _tbuffer = 0;
+ _tbufferSize = 0;
+ }
+ if (_sbufferSize == 0) {
+ delete []_sbuffer;
+ _sbuffer = 0;
+ }
+ } else {
+ _sbufferSize = _tbufferSize;
+ _sbuffer = _tbuffer;
+ _tbufferSize = 0;
+ _tbuffer = 0;
+ }
+ } else {
+ int32 offset = 0;
+ while (handleSubTags(offset));
+ if (_inData) {
+ _sbufferSize = _tbufferSize - offset;
+ assert(_sbufferSize);
+ _sbuffer = new byte[_sbufferSize];
+ if (!_sbuffer)
+ error("saud_channel failed to allocate memory");
+ memcpy(_sbuffer, _tbuffer + offset, _sbufferSize);
+ delete []_tbuffer;
+ _tbuffer = 0;
+ _tbufferSize = 0;
+ } else {
+ if (offset) {
+ byte *old = _tbuffer;
+ int32 new_size = _tbufferSize - offset;
+ _tbuffer = new byte[new_size];
+ if (!_tbuffer)
+ error("SaudChannel failed to allocate memory");
+ memcpy(_tbuffer, old + offset, new_size);
+ _tbufferSize = new_size;
+ delete []old;
+ }
+ }
+ }
+ return true;
+}
+
+SaudChannel::SaudChannel(int32 track, int32 freq) :
+ _track(track),
+ _nbframes(0),
+ _dataSize(-1),
+ _frequency(freq),
+ _inData(false),
+ _markReached(false),
+ _index(0),
+ _tbuffer(0),
+ _tbufferSize(0),
+ _sbuffer(0),
+ _sbufferSize(0),
+ _keepSize(false) {
+}
+
+SaudChannel::~SaudChannel() {
+ _dataSize = 0;
+ _tbufferSize = 0;
+ _sbufferSize = 0;
+ _markReached = true;
+ if (_tbuffer)
+ delete []_tbuffer;
+ if (_sbuffer) {
+ // _sbuffer can be not empty here with insane when it seeks in video
+ delete []_sbuffer;
+ }
+ _sbuffer = 0;
+}
+
+bool SaudChannel::isTerminated() const {
+ return (_markReached && _dataSize == 0 && _sbuffer == 0);
+}
+
+bool SaudChannel::setParameters(int32 nb, int32 flags, int32 volume, int32 pan, int32 index) {
+ _nbframes = nb;
+ _flags = flags; // bit 7 == IS_VOICE, bit 6 == IS_BACKGROUND_MUSIC, other ??
+ _volume = volume;
+ _pan = pan;
+ _index = index;
+ if (index != 0) {
+ _dataSize = -2;
+ _keepSize = true;
+ _inData = true;
+ }
+ return true;
+}
+
+bool SaudChannel::checkParameters(int32 index, int32 nb, int32 flags, int32 volume, int32 pan) {
+ if (++_index != index)
+ error("invalid index in SaudChannel::checkParameters()");
+ if (_nbframes != nb)
+ error("invalid duration in SaudChannel::checkParameters()");
+ if (_flags != flags)
+ error("invalid flags in SaudChannel::checkParameters()");
+ if (_volume != volume || _pan != pan) {
+ _volume = volume;
+ _pan = pan;
+ }
+ return true;
+}
+
+bool SaudChannel::appendData(Chunk &b, int32 size) {
+ if (_dataSize == -1) {
+ assert(size > 8);
+ Chunk::type saud_type = b.getDword();
+ saud_type = SWAP_BYTES_32(saud_type);
+ uint32 saud_size = b.getDword();
+ saud_size = SWAP_BYTES_32(saud_size);
+ if (saud_type != TYPE_SAUD)
+ error("Invalid Chunk for SaudChannel : %X", saud_type);
+ size -= 8;
+ _dataSize = -2;
+ }
+ if (_tbuffer) {
+ byte *old = _tbuffer;
+ _tbuffer = new byte[_tbufferSize + size];
+ if (!_tbuffer)
+ error("saud_channel failed to allocate memory");
+ memcpy(_tbuffer, old, _tbufferSize);
+ delete []old;
+ b.read(_tbuffer + _tbufferSize, size);
+ _tbufferSize += size;
+ } else {
+ _tbufferSize = size;
+ _tbuffer = new byte[_tbufferSize];
+ if (!_tbuffer)
+ error("saud_channel failed to allocate memory");
+ b.read(_tbuffer, _tbufferSize);
+ }
+ return processBuffer();
+}
+
+int32 SaudChannel::availableSoundData(void) const {
+ return _sbufferSize;
+}
+
+void SaudChannel::getSoundData(int16 *snd, int32 size) {
+ for (int32 i = 0; i < size; i++) {
+ snd[2 * i] = TO_LE_16(_sbuffer[i] ^ 0x80);
+ snd[2 * i + 1] = TO_LE_16(_sbuffer[i] ^ 0x80);
+ }
+ if (!_keepSize)
+ _dataSize -= size;
+ delete []_sbuffer;
+ _sbuffer = 0;
+ _sbufferSize = 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/smush/smush_font.cpp b/engines/scumm/smush/smush_font.cpp
new file mode 100644
index 0000000000..96d9e2f7c5
--- /dev/null
+++ b/engines/scumm/smush/smush_font.cpp
@@ -0,0 +1,269 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "scumm/scumm.h"
+#include "scumm/util.h"
+
+#include "scumm/smush/smush_font.h"
+
+namespace Scumm {
+
+SmushFont::SmushFont(ScummEngine *vm, bool use_original_colors, bool new_colors) :
+ NutRenderer(vm),
+ _color(-1),
+ _new_colors(new_colors),
+ _original(use_original_colors) {
+}
+
+int SmushFont::getStringWidth(const char *str) {
+ assert(str);
+ if (!_loaded) {
+ error("SmushFont::getStringWidth() Font is not loaded");
+ return 0;
+ }
+
+ int width = 0;
+ while (*str) {
+ if (*str & 0x80 && _vm->_useCJKMode) {
+ width += _vm->_2byteWidth + 1;
+ str += 2;
+ } else
+ width += getCharWidth(*str++);
+ }
+ return width;
+}
+
+int SmushFont::getStringHeight(const char *str) {
+ assert(str);
+ if (!_loaded) {
+ error("SmushFont::getStringHeight() Font is not loaded");
+ return 0;
+ }
+
+ int height = 0;
+ while (*str) {
+ int charHeight = getCharHeight(*str++);
+ if (height < charHeight)
+ height = charHeight;
+ }
+ return height;
+}
+
+int SmushFont::drawChar(byte *buffer, int dst_width, int x, int y, byte chr) {
+ int w = _chars[chr].width;
+ int h = _chars[chr].height;
+ const byte *src = _chars[chr].src;
+ byte *dst = buffer + dst_width * y + x;
+
+ assert(dst_width == _vm->_screenWidth);
+
+ if (_original) {
+ for (int j = 0; j < h; j++) {
+ for (int i = 0; i < w; i++) {
+ int8 value = *src++;
+ if (value)
+ dst[i] = value;
+ }
+ dst += dst_width;
+ }
+ } else {
+ char color = (_color != -1) ? _color : 1;
+ if (_new_colors) {
+ for (int j = 0; j < h; j++) {
+ for (int i = 0; i < w; i++) {
+ int8 value = *src++;
+ if (value == -color) {
+ dst[i] = 0xFF;
+ } else if (value == -31) {
+ dst[i] = 0;
+ } else if (value) {
+ dst[i] = value;
+ }
+ }
+ dst += dst_width;
+ }
+ } else {
+ for (int j = 0; j < h; j++) {
+ for (int i = 0; i < w; i++) {
+ int8 value = *src++;
+ if (value == 1) {
+ dst[i] = color;
+ } else if (value) {
+ dst[i] = 0;
+ }
+ }
+ dst += dst_width;
+ }
+ }
+ }
+ return w;
+}
+
+int SmushFont::draw2byte(byte *buffer, int dst_width, int x, int y, int idx) {
+ int w = _vm->_2byteWidth;
+ int h = _vm->_2byteHeight;
+
+ byte *src = _vm->get2byteCharPtr(idx);
+ byte *dst = buffer + dst_width * (y + (_vm->_gameId == GID_CMI ? 7 : (_vm->_gameId == GID_DIG ? 2 : 0))) + x;
+ byte bits = 0;
+
+ char color = (_color != -1) ? _color : 1;
+
+ if (_new_colors)
+ color = (char)0xff;
+
+ if (_vm->_gameId == GID_FT)
+ color = 1;
+
+ for (int j = 0; j < h; j++) {
+ for (int i = 0; i < w; i++) {
+ if ((i % 8) == 0)
+ bits = *src++;
+ if (bits & revBitMask(i % 8)) {
+ dst[i + 1] = 0;
+ dst[dst_width + i] = 0;
+ dst[dst_width + i + 1] = 0;
+ dst[i] = color;
+ }
+ }
+ dst += dst_width;
+ }
+ return w + 1;
+}
+
+void SmushFont::drawSubstring(const char *str, byte *buffer, int dst_width, int x, int y) {
+ // This happens in the Full Throttle intro. I don't know if our
+ // text-drawing functions are buggy, or if this function is supposed
+ // to have to check for it.
+ if (x < 0)
+ x = 0;
+
+ for (int i = 0; str[i] != 0; i++) {
+ if ((byte)str[i] >= 0x80 && _vm->_useCJKMode) {
+ x += draw2byte(buffer, dst_width, x, y, (byte)str[i] + 256 * (byte)str[i+1]);
+ i++;
+ } else
+ x += drawChar(buffer, dst_width, x, y, str[i]);
+ }
+}
+
+#define MAX_WORDS 60
+
+
+void SmushFont::drawString(const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, bool center) {
+ debugC(DEBUG_SMUSH, "SmushFont::drawString(%s, %d, %d, %d)", str, x, y, center);
+
+ while (str) {
+ char line[256];
+ char *pos = (char *)strchr(str, '\n');
+ if (pos) {
+ memcpy(line, str, pos - str - 1);
+ line[pos - str - 1] = 0;
+ str = pos + 1;
+ } else {
+ strcpy(line, str);
+ str = 0;
+ }
+ drawSubstring(line, buffer, dst_width, center ? (x - getStringWidth(line) / 2) : x, y);
+ y += getStringHeight(line);
+ }
+}
+
+void SmushFont::drawStringWrap(const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, int left, int right, bool center) {
+ debugC(DEBUG_SMUSH, "SmushFont::drawStringWrap(%s, %d, %d, %d, %d, %d)", str, x, y, left, right, center);
+
+ const int width = right - left;
+ char *s = strdup(str);
+ char *words[MAX_WORDS];
+ int word_count = 0;
+
+ char *tmp = s;
+ while (tmp) {
+ assert(word_count < MAX_WORDS);
+ words[word_count++] = tmp;
+ tmp = strpbrk(tmp, " \t\r\n");
+ if (tmp == 0)
+ break;
+ *tmp++ = 0;
+ }
+
+ int i = 0, max_width = 0, height = 0, line_count = 0;
+
+ char *substrings[MAX_WORDS];
+ int substr_widths[MAX_WORDS];
+ const int space_width = getCharWidth(' ');
+
+ i = 0;
+ while (i < word_count) {
+ char *substr = words[i++];
+ int substr_width = getStringWidth(substr);
+
+ while (i < word_count) {
+ int word_width = getStringWidth(words[i]);
+ if ((substr_width + space_width + word_width) >= width)
+ break;
+ substr_width += word_width + space_width;
+ *(words[i]-1) = ' '; // Convert 0 byte back to space
+ i++;
+ }
+
+ substrings[line_count] = substr;
+ substr_widths[line_count++] = substr_width;
+ if (max_width < substr_width)
+ max_width = substr_width;
+ height += getStringHeight(substr);
+ }
+
+ if (y > dst_height - height) {
+ y = dst_height - height;
+ }
+
+ if (center) {
+ max_width = (max_width + 1) / 2;
+ x = left + width / 2;
+
+ if (x < left + max_width)
+ x = left + max_width;
+ if (x > right - max_width)
+ x = right - max_width;
+
+ for (i = 0; i < line_count; i++) {
+ drawSubstring(substrings[i], buffer, dst_width, x - substr_widths[i] / 2, y);
+ y += getStringHeight(substrings[i]);
+ }
+ } else {
+ if (x > dst_width - max_width)
+ x = dst_width - max_width;
+
+ for (i = 0; i < line_count; i++) {
+ drawSubstring(substrings[i], buffer, dst_width, x, y);
+ y += getStringHeight(substrings[i]);
+ }
+ }
+
+ free(s);
+}
+
+} // End of namespace Scumm
+
diff --git a/engines/scumm/smush/smush_font.h b/engines/scumm/smush/smush_font.h
new file mode 100644
index 0000000000..724eb46658
--- /dev/null
+++ b/engines/scumm/smush/smush_font.h
@@ -0,0 +1,54 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SMUSH_FONT_H
+#define SMUSH_FONT_H
+
+#include "common/scummsys.h"
+#include "scumm/nut_renderer.h"
+
+namespace Scumm {
+
+class SmushFont : public NutRenderer {
+protected:
+ int16 _color;
+ bool _new_colors;
+ bool _original;
+
+
+ int getStringWidth(const char *str);
+ int getStringHeight(const char *str);
+ int draw2byte(byte *buffer, int dst_width, int x, int y, int idx);
+ int drawChar(byte *buffer, int dst_width, int x, int y, byte chr);
+ void drawSubstring(const char *str, byte *buffer, int dst_width, int x, int y);
+
+public:
+ SmushFont(ScummEngine *vm, bool use_original_colors, bool new_colors);
+
+ void setColor(byte c) { _color = c; }
+ void drawString (const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, bool center);
+ void drawStringWrap(const char *str, byte *buffer, int dst_width, int dst_height, int x, int y, int left, int right, bool center);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/smush/smush_mixer.cpp b/engines/scumm/smush/smush_mixer.cpp
new file mode 100644
index 0000000000..00a50321da
--- /dev/null
+++ b/engines/scumm/smush/smush_mixer.cpp
@@ -0,0 +1,163 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+
+#include "scumm/smush/smush_mixer.h"
+#include "scumm/smush/channel.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/imuse.h"
+
+#include "sound/mixer.h"
+
+
+namespace Scumm {
+
+SmushMixer::SmushMixer(Audio::Mixer *m) :
+ _mixer(m),
+ _soundFrequency(22050) {
+ for (int32 i = 0; i < NUM_CHANNELS; i++) {
+ _channels[i].id = -1;
+ _channels[i].chan = NULL;
+ _channels[i].stream = NULL;
+ }
+}
+
+SmushMixer::~SmushMixer() {
+ Common::StackLock lock(_mutex);
+ for (int32 i = 0; i < NUM_CHANNELS; i++) {
+ _mixer->stopHandle(_channels[i].handle);
+ }
+}
+
+SmushChannel *SmushMixer::findChannel(int32 track) {
+ Common::StackLock lock(_mutex);
+ debugC(DEBUG_SMUSH, "SmushMixer::findChannel(%d)", track);
+ for (int32 i = 0; i < NUM_CHANNELS; i++) {
+ if (_channels[i].id == track)
+ return _channels[i].chan;
+ }
+ return NULL;
+}
+
+void SmushMixer::addChannel(SmushChannel *c) {
+ Common::StackLock lock(_mutex);
+ int32 track = c->getTrackIdentifier();
+ int i;
+
+ debugC(DEBUG_SMUSH, "SmushMixer::addChannel(%d)", track);
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if (_channels[i].id == track)
+ debugC(DEBUG_SMUSH, "SmushMixer::addChannel(%d): channel already exists", track);
+ }
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ if ((_channels[i].chan == NULL || _channels[i].id == -1) && !_mixer->isSoundHandleActive(_channels[i].handle)) {
+ _channels[i].chan = c;
+ _channels[i].id = track;
+ return;
+ }
+ }
+
+ for (i = 0; i < NUM_CHANNELS; i++) {
+ debugC(DEBUG_SMUSH, "channel %d : %p(%d, %d)", i, (void *)_channels[i].chan,
+ _channels[i].chan ? _channels[i].chan->getTrackIdentifier() : -1,
+ _channels[i].chan ? _channels[i].chan->isTerminated() : 1);
+ }
+
+ error("SmushMixer::addChannel(%d): no channel available", track);
+}
+
+bool SmushMixer::handleFrame() {
+ Common::StackLock lock(_mutex);
+ debugC(DEBUG_SMUSH, "SmushMixer::handleFrame()");
+ for (int i = 0; i < NUM_CHANNELS; i++) {
+ if (_channels[i].id != -1) {
+ if (_channels[i].chan->isTerminated()) {
+ delete _channels[i].chan;
+ _channels[i].id = -1;
+ _channels[i].chan = NULL;
+ if (_channels[i].stream) {
+ _channels[i].stream->finish();
+ _channels[i].stream = 0;
+ }
+ } else {
+ int32 rate, vol, pan;
+ bool stereo, is_16bit;
+ void *data;
+
+ _channels[i].chan->getParameters(rate, stereo, is_16bit, vol, pan);
+ int32 size = _channels[i].chan->availableSoundData();
+ byte flags = stereo ? Audio::Mixer::FLAG_STEREO : 0;
+
+ if (is_16bit) {
+ data = malloc(size * (stereo ? 2 : 1) * 4);
+ _channels[i].chan->getSoundData((int16 *)data, size);
+ size *= stereo ? 4 : 2;
+
+ flags |= Audio::Mixer::FLAG_16BITS;
+
+ } else {
+ data = malloc(size * (stereo ? 2 : 1) * 2);
+ _channels[i].chan->getSoundData((int8 *)data, size);
+ size *= stereo ? 2 : 1;
+
+ flags |= Audio::Mixer::FLAG_UNSIGNED;
+ }
+
+ if (_mixer->isReady()) {
+ if (!_channels[i].stream) {
+ _channels[i].stream = makeAppendableAudioStream(rate, flags, 500000);
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_channels[i].handle, _channels[i].stream);
+ }
+ _mixer->setChannelVolume(_channels[i].handle, vol);
+ _mixer->setChannelBalance(_channels[i].handle, pan);
+ _channels[i].stream->append((byte *)data, size);
+ }
+ free(data);
+ }
+ }
+ }
+ return true;
+}
+
+bool SmushMixer::stop() {
+ Common::StackLock lock(_mutex);
+ debugC(DEBUG_SMUSH, "SmushMixer::stop()");
+ for (int i = 0; i < NUM_CHANNELS; i++) {
+ if (_channels[i].id != -1) {
+ delete _channels[i].chan;
+ _channels[i].id = -1;
+ _channels[i].chan = NULL;
+ if (_channels[i].stream) {
+ _channels[i].stream->finish();
+ _channels[i].stream = 0;
+ }
+ }
+ }
+ return true;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/smush/smush_mixer.h b/engines/scumm/smush/smush_mixer.h
new file mode 100644
index 0000000000..2a7c2d3ebc
--- /dev/null
+++ b/engines/scumm/smush/smush_mixer.h
@@ -0,0 +1,64 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SMUSH_MIXER_H
+#define SMUSH_MIXER_H
+
+#include "common/stdafx.h"
+#include "scumm/sound.h"
+
+namespace Scumm {
+
+class SmushChannel;
+
+class SmushMixer {
+ enum {
+ NUM_CHANNELS = 16
+ };
+private:
+
+ Audio::Mixer *_mixer;
+ struct channels {
+ int id;
+ SmushChannel *chan;
+ Audio::SoundHandle handle;
+ AppendableAudioStream *stream;
+ } _channels[NUM_CHANNELS];
+
+ int _soundFrequency;
+
+ Common::Mutex _mutex;
+
+public:
+
+ SmushMixer(Audio::Mixer *);
+ virtual ~SmushMixer();
+ SmushChannel *findChannel(int32 track);
+ void addChannel(SmushChannel *c);
+ bool handleFrame();
+ bool stop();
+ bool update();
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp
new file mode 100644
index 0000000000..926e7f9f87
--- /dev/null
+++ b/engines/scumm/smush/smush_player.cpp
@@ -0,0 +1,1359 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "base/engine.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/timer.h"
+#include "common/util.h"
+
+#include "scumm/bomp.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/smush/channel.h"
+#include "scumm/smush/chunk_type.h"
+#include "scumm/smush/chunk.h"
+#include "scumm/smush/smush_font.h"
+#include "scumm/smush/smush_mixer.h"
+#include "scumm/smush/smush_player.h"
+
+#include "scumm/insane/insane.h"
+
+#include "sound/mixer.h"
+#include "sound/vorbis.h"
+#include "sound/mp3.h"
+
+#ifdef DUMP_SMUSH_FRAMES
+#include <png.h>
+#endif
+
+#ifdef USE_ZLIB
+#include <zlib.h>
+#endif
+
+namespace Scumm {
+
+const int MAX_STRINGS = 200;
+
+class StringResource {
+private:
+
+ struct {
+ int id;
+ char *string;
+ } _strings[MAX_STRINGS];
+
+ int _nbStrings;
+ int _lastId;
+ const char *_lastString;
+
+public:
+
+ StringResource() :
+ _nbStrings(0),
+ _lastId(-1) {
+ };
+ ~StringResource() {
+ for (int32 i = 0; i < _nbStrings; i++) {
+ delete []_strings[i].string;
+ }
+ }
+
+ bool init(char *buffer, int32 length) {
+ char *def_start = strchr(buffer, '#');
+ while (def_start != NULL) {
+ char *def_end = strchr(def_start, '\n');
+ assert(def_end != NULL);
+
+ char *id_end = def_end;
+ while (id_end >= def_start && !isdigit(*(id_end-1))) {
+ id_end--;
+ }
+
+ assert(id_end > def_start);
+ char *id_start = id_end;
+ while (isdigit(*(id_start - 1))) {
+ id_start--;
+ }
+
+ char idstring[32];
+ memcpy(idstring, id_start, id_end - id_start);
+ idstring[id_end - id_start] = 0;
+ int32 id = atoi(idstring);
+ char *data_start = def_end;
+
+ while (*data_start == '\n' || *data_start == '\r') {
+ data_start++;
+ }
+ char *data_end = data_start;
+
+ while (1) {
+ if (data_end[-2] == '\r' && data_end[-1] == '\n' && data_end[0] == '\r' && data_end[1] == '\n') {
+ break;
+ }
+ // In Russian Full Throttle strings are finished with
+ // just one pair of CR-LF
+ if (data_end[-2] == '\r' && data_end[-1] == '\n' && data_end[0] == '#') {
+ break;
+ }
+ data_end++;
+ if (data_end >= buffer + length) {
+ data_end = buffer + length;
+ break;
+ }
+ }
+
+ data_end -= 2;
+ assert(data_end > data_start);
+ char *value = new char[data_end - data_start + 1];
+ assert(value);
+ memcpy(value, data_start, data_end - data_start);
+ value[data_end - data_start] = 0;
+ char *line_start = value;
+ char *line_end;
+
+ while ((line_end = strchr(line_start, '\n'))) {
+ line_start = line_end+1;
+ if (line_start[0] == '/' && line_start[1] == '/') {
+ line_start += 2;
+ if (line_end[-1] == '\r')
+ line_end[-1] = ' ';
+ else
+ *line_end++ = ' ';
+ memmove(line_end, line_start, strlen(line_start)+1);
+ }
+ }
+ _strings[_nbStrings].id = id;
+ _strings[_nbStrings].string = value;
+ _nbStrings ++;
+ def_start = strchr(data_end + 2, '#');
+ }
+ return true;
+ }
+
+ const char *get(int id) {
+ if (id == _lastId) {
+ return _lastString;
+ }
+ debugC(DEBUG_SMUSH, "StringResource::get(%d)", id);
+ for (int i = 0; i < _nbStrings; i++) {
+ if (_strings[i].id == id) {
+ _lastId = id;
+ _lastString = _strings[i].string;
+ return _strings[i].string;
+ }
+ }
+ warning("invalid string id : %d", id);
+ _lastId = -1;
+ _lastString = "unknown string";
+ return _lastString;
+ }
+};
+
+static StringResource *getStrings(ScummEngine *vm, const char *file, bool is_encoded) {
+ debugC(DEBUG_SMUSH, "trying to read text ressources from %s", file);
+ ScummFile theFile;
+
+ vm->openFile(theFile, file);
+ if (!theFile.isOpen()) {
+ return 0;
+ }
+ int32 length = theFile.size();
+ char *filebuffer = new char [length + 1];
+ assert(filebuffer);
+ theFile.read(filebuffer, length);
+ filebuffer[length] = 0;
+
+ if (is_encoded) {
+ enum {
+ ETRS_HEADER_LENGTH = 16
+ };
+ assert(length > ETRS_HEADER_LENGTH);
+ Chunk::type type = READ_BE_UINT32(filebuffer);
+
+ if (type != TYPE_ETRS) {
+ delete [] filebuffer;
+ return getStrings(vm, file, false);
+ }
+
+ char *old = filebuffer;
+ filebuffer = new char[length - ETRS_HEADER_LENGTH + 1];
+ for (int32 i = ETRS_HEADER_LENGTH; i < length; i++) {
+ filebuffer[i - ETRS_HEADER_LENGTH] = old[i] ^ 0xCC;
+ }
+ filebuffer[length - ETRS_HEADER_LENGTH] = '\0';
+ delete []old;
+ length -= ETRS_HEADER_LENGTH;
+ }
+ StringResource *sr = new StringResource;
+ assert(sr);
+ sr->init(filebuffer, length);
+ delete []filebuffer;
+ return sr;
+}
+
+void SmushPlayer::timerCallback(void *refCon) {
+ ((SmushPlayer *)refCon)->parseNextFrame();
+#ifdef _WIN32_WCE
+ ((SmushPlayer *)refCon)->_inTimer = true;
+ ((SmushPlayer *)refCon)->_inTimerCount++;
+#endif
+}
+
+SmushPlayer::SmushPlayer(ScummEngine_v6 *scumm, int speed) {
+ _vm = scumm;
+ _version = -1;
+ _nbframes = 0;
+ _smixer = 0;
+ _strings = NULL;
+ _sf[0] = NULL;
+ _sf[1] = NULL;
+ _sf[2] = NULL;
+ _sf[3] = NULL;
+ _sf[4] = NULL;
+ _base = NULL;
+ _frameBuffer = NULL;
+ _specialBuffer = NULL;
+
+ _seekPos = -1;
+
+ _skipNext = false;
+ _subtitles = ConfMan.getBool("subtitles");
+ _dst = NULL;
+ _storeFrame = false;
+ _compressedFileMode = false;
+ _width = 0;
+ _height = 0;
+ _IACTpos = 0;
+ _soundFrequency = 22050;
+ _initDone = false;
+ _speed = speed;
+ _insanity = false;
+ _middleAudio = false;
+ _skipPalette = false;
+ _IACTstream = NULL;
+#ifdef _WIN32_WCE
+ _inTimer = false;
+ _inTimerCount = 0;
+ _inTimerCountRedraw = ConfMan.getInt("Smush_force_redraw");
+#endif
+}
+
+SmushPlayer::~SmushPlayer() {
+ release();
+}
+
+void SmushPlayer::init() {
+ _frame = 0;
+ _alreadyInit = false;
+ _vm->_smushVideoShouldFinish = false;
+ _vm->setDirtyColors(0, 255);
+ _dst = _vm->virtscr[0].getPixels(0, 0);
+
+ // HACK HACK HACK: This is an *evil* trick, beware!
+ // We do this to fix bug #1037052. A proper solution would change all the
+ // drawing code to use the pitch value specified by the virtual screen.
+ // However, since a lot of the SMUSH code currently assumes the screen
+ // width and pitch to be equal, this will require lots of changes. So
+ // we resort to this hackish solution for now.
+ _origPitch = _vm->virtscr[0].pitch;
+ _origNumStrips = _vm->gdi._numStrips;
+ _vm->virtscr[0].pitch = _vm->virtscr[0].w;
+ _vm->gdi._numStrips = _vm->virtscr[0].w / 8;
+
+ _smixer = new SmushMixer(_vm->_mixer);
+ Common::g_timer->installTimerProc(&timerCallback, 1000000 / _speed, this);
+
+ _initDone = true;
+}
+
+void SmushPlayer::release() {
+ if (!_initDone)
+ return;
+
+ _vm->_timer->removeTimerProc(&timerCallback);
+
+ _vm->_smushVideoShouldFinish = true;
+
+ for (int i = 0; i < 5; i++) {
+ delete _sf[i];
+ _sf[i] = NULL;
+ }
+
+ delete _strings;
+ _strings = NULL;
+
+ if (_smixer)
+ _smixer->stop();
+
+ delete _smixer;
+ _smixer = NULL;
+
+ delete _base;
+ _base = NULL;
+
+ free(_specialBuffer);
+ _specialBuffer = NULL;
+
+ free(_frameBuffer);
+ _frameBuffer = NULL;
+
+ _vm->_mixer->stopHandle(_compressedFileSoundHandle);
+
+ _vm->_mixer->stopHandle(_IACTchannel);
+ _IACTstream = 0;
+
+ _vm->_fullRedraw = true;
+
+ // WORKAROUND bug #1035739: This is hack to workaround some ugly palette
+ // issues, see the mentioned bug report for details.
+ _vm->_doEffect = false;
+
+
+ // HACK HACK HACK: This is an *evil* trick, beware! See above for
+ // some explanation.
+ _vm->virtscr[0].pitch = _origPitch;
+ _vm->gdi._numStrips = _origNumStrips;
+
+
+ _initDone = false;
+}
+
+void SmushPlayer::checkBlock(const Chunk &b, Chunk::type type_expected, uint32 min_size) {
+ if (type_expected != b.getType()) {
+ error("Chunk type is different from expected : %x != %x", b.getType(), type_expected);
+ }
+ if (min_size > b.getSize()) {
+ error("Chunk size is inferior than minimum required size : %d < %d", b.getSize(), min_size);
+ }
+}
+
+void SmushPlayer::handleSoundBuffer(int32 track_id, int32 index, int32 max_frames, int32 flags, int32 vol, int32 pan, Chunk &b, int32 size) {
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleSoundBuffer(%d, %d)", track_id, index);
+// if ((flags & 128) == 128) {
+// return;
+// }
+// if ((flags & 64) == 64) {
+// return;
+// }
+ SmushChannel *c = _smixer->findChannel(track_id);
+ if (c == NULL) {
+ c = new SaudChannel(track_id, _soundFrequency);
+ _smixer->addChannel(c);
+ }
+
+ if (_middleAudio || (index == 0)) {
+ c->setParameters(max_frames, flags, vol, pan, index);
+ } else {
+ c->checkParameters(index, max_frames, flags, vol, pan);
+ }
+ _middleAudio = false;
+ c->appendData(b, size);
+}
+
+void SmushPlayer::handleSoundFrame(Chunk &b) {
+ checkBlock(b, TYPE_PSAD);
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleSoundFrame()");
+
+ int32 track_id = b.getWord();
+ int32 index = b.getWord();
+ int32 max_frames = b.getWord();
+ int32 flags = b.getWord();
+ int32 vol = b.getByte();
+ int32 pan = b.getChar();
+ if (index == 0) {
+ debugC(DEBUG_SMUSH, "track_id:%d, max_frames:%d, flags:%d, vol:%d, pan:%d", track_id, max_frames, flags, vol, pan);
+ }
+ int32 size = b.getSize() - 10;
+ handleSoundBuffer(track_id, index, max_frames, flags, vol, pan, b, size);
+}
+
+void SmushPlayer::handleSkip(Chunk &b) {
+ checkBlock(b, TYPE_SKIP, 4);
+ int32 code = b.getDword();
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleSkip(%d)", code);
+ if (code >= 0 && code < 37)
+ _skipNext = _skips[code];
+ else
+ _skipNext = true;
+}
+
+void SmushPlayer::handleStore(Chunk &b) {
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleStore()");
+ checkBlock(b, TYPE_STOR, 4);
+ _storeFrame = true;
+}
+
+void SmushPlayer::handleFetch(Chunk &b) {
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleFetch()");
+ checkBlock(b, TYPE_FTCH, 6);
+
+ if (_frameBuffer != NULL) {
+ memcpy(_dst, _frameBuffer, _width * _height);
+ }
+}
+
+void SmushPlayer::handleIACT(Chunk &b) {
+ checkBlock(b, TYPE_IACT, 8);
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleImuseAction()");
+
+ /* int code = */ b.getWord();
+ int flags = b.getWord();
+ int unknown = b.getShort();
+ int track_flags = b.getWord();
+
+ assert(flags == 46 && unknown == 0);
+ int track_id = b.getWord();
+ int index = b.getWord();
+ int nbframes = b.getWord();
+ int32 size = b.getDword();
+ int32 bsize = b.getSize() - 18;
+
+ if (_vm->_gameId != GID_CMI) {
+ int32 track = track_id;
+ if (track_flags == 1) {
+ track = track_id + 100;
+ } else if (track_flags == 2) {
+ track = track_id + 200;
+ } else if (track_flags == 3) {
+ track = track_id + 300;
+ } else if ((track_flags >= 100) && (track_flags <= 163)) {
+ track = track_id + 400;
+ } else if ((track_flags >= 200) && (track_flags <= 263)) {
+ track = track_id + 500;
+ } else if ((track_flags >= 300) && (track_flags <= 363)) {
+ track = track_id + 600;
+ } else {
+ error("ImuseChannel::handleIACT(): bad track_flags: %d", track_flags);
+ }
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleIACT(): %d, %d, %d", track, index, track_flags);
+
+ SmushChannel *c = _smixer->findChannel(track);
+ if (c == 0) {
+ c = new ImuseChannel(track, _soundFrequency);
+ _smixer->addChannel(c);
+ }
+ if (index == 0)
+ c->setParameters(nbframes, size, track_flags, unknown, 0);
+ else
+ c->checkParameters(index, nbframes, size, track_flags, unknown);
+ c->appendData(b, bsize);
+ } else {
+ byte output_data[4096];
+ byte *src = (byte *)malloc(bsize);
+ b.read(src, bsize);
+ byte *d_src = src;
+ byte value;
+
+ while (bsize > 0) {
+ if (_IACTpos >= 2) {
+ int32 len = READ_BE_UINT16(_IACToutput) + 2;
+ len -= _IACTpos;
+ if (len > bsize) {
+ memcpy(_IACToutput + _IACTpos, d_src, bsize);
+ _IACTpos += bsize;
+ bsize = 0;
+ } else {
+ memcpy(_IACToutput + _IACTpos, d_src, len);
+ byte *dst = output_data;
+ byte *d_src2 = _IACToutput;
+ d_src2 += 2;
+ int32 count = 1024;
+ byte variable1 = *d_src2++;
+ byte variable2 = variable1 / 16;
+ variable1 &= 0x0f;
+ do {
+ value = *(d_src2++);
+ if (value == 0x80) {
+ *dst++ = *d_src2++;
+ *dst++ = *d_src2++;
+ } else {
+ int16 val = (int8)value << variable2;
+ *dst++ = val >> 8;
+ *dst++ = (byte)(val);
+ }
+ value = *(d_src2++);
+ if (value == 0x80) {
+ *dst++ = *d_src2++;
+ *dst++ = *d_src2++;
+ } else {
+ int16 val = (int8)value << variable1;
+ *dst++ = val >> 8;
+ *dst++ = (byte)(val);
+ }
+ } while (--count);
+
+ if (!_IACTstream) {
+ _IACTstream = makeAppendableAudioStream(22050, Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_16BITS, 400000);
+ _vm->_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_IACTchannel, _IACTstream);
+ }
+ _IACTstream->append(output_data, 0x1000);
+
+ bsize -= len;
+ d_src += len;
+ _IACTpos = 0;
+ }
+ } else {
+ if (bsize > 1 && _IACTpos == 0) {
+ *(_IACToutput + 0) = *d_src++;
+ _IACTpos = 1;
+ bsize--;
+ }
+ *(_IACToutput + _IACTpos) = *d_src++;
+ _IACTpos++;
+ bsize--;
+ }
+ }
+
+ free(src);
+ }
+}
+
+void SmushPlayer::handleTextResource(Chunk &b) {
+ int pos_x = b.getShort();
+ int pos_y = b.getShort();
+ int flags = b.getShort();
+ int left = b.getShort();
+ int top = b.getShort();
+ int right = b.getShort();
+ /*int32 height =*/ b.getShort();
+ /*int32 unk2 =*/ b.getWord();
+
+ const char *str;
+ char *string = NULL, *string2 = NULL;
+ if (b.getType() == TYPE_TEXT) {
+ string = (char *)malloc(b.getSize() - 16);
+ str = string;
+ b.read(string, b.getSize() - 16);
+ } else {
+ int string_id = b.getWord();
+ if (!_strings)
+ return;
+ str = _strings->get(string_id);
+ }
+
+ // if subtitles disabled and bit 3 is set, then do not draw
+ if ((!_subtitles) && ((flags & 8) == 8))
+ return;
+
+ SmushFont *sf = _sf[0];
+ int color = 15;
+ while (*str == '/') {
+ str++; // For Full Throttle text resources
+ }
+
+ byte transBuf[512];
+ if (_vm->_gameId == GID_CMI) {
+ _vm->translateText((const byte *)str - 1, transBuf);
+ while (*str++ != '/')
+ ;
+ string2 = (char *)transBuf;
+
+ // If string2 contains formatting information there probably
+ // wasn't any translation for it in the language.tab file. In
+ // that case, pretend there is no string2.
+ if (string2[0] == '^')
+ string2[0] = 0;
+ }
+
+ while (str[0] == '^') {
+ switch (str[1]) {
+ case 'f':
+ {
+ int id = str[3] - '0';
+ str += 4;
+ sf = _sf[id];
+ }
+ break;
+ case 'c':
+ {
+ color = str[4] - '0' + 10 *(str[3] - '0');
+ str += 5;
+ }
+ break;
+ default:
+ error("invalid escape code in text string");
+ }
+ }
+
+ // HACK. This is to prevent bug #1310846. In updated Win95 dig
+ // there is such line:
+ //
+ // ^f01^c001LEAD TESTER
+ // Chris Purvis
+ // ^f01
+ // ^f01^c001WINDOWS COMPATIBILITY
+ // Chip Hinnenberg
+ // ^f01^c001WINDOWS TESTING
+ // Jim Davison
+ // Lynn Selk
+ //
+ // i.e. formatting exists not in the first line only
+ // We just strip that off and assume that neither font
+ // nor font color was altered. Proper fix would be to feed
+ // drawString() with each line sequentally
+ char *string3 = NULL, *sptr2;
+ const char *sptr;
+
+ if (strchr(str, '^')) {
+ string3 = (char *)malloc(strlen(str) + 1);
+
+ for (sptr = str, sptr2 = string3; *sptr;) {
+ if (*sptr == '^') {
+ switch (sptr[1]) {
+ case 'f':
+ sptr += 4;
+ break;
+ case 'c':
+ sptr += 5;
+ break;
+ default:
+ error("invalid escape code in text string");
+ }
+ } else {
+ *sptr2++ = *sptr++;
+ }
+ }
+ *sptr2++ = *sptr++; // copy zero character
+ str = string3;
+ }
+
+ assert(sf != NULL);
+ sf->setColor(color);
+
+ if (_vm->_gameId == GID_CMI && string2[0] != 0) {
+ str = string2;
+ }
+
+ // flags:
+ // bit 0 - center 1
+ // bit 1 - not used 2
+ // bit 2 - ??? 4
+ // bit 3 - wrap around 8
+ switch (flags & 9) {
+ case 0:
+ sf->drawString(str, _dst, _width, _height, pos_x, pos_y, false);
+ break;
+ case 1:
+ sf->drawString(str, _dst, _width, _height, pos_x, MAX(pos_y, top), true);
+ break;
+ case 8:
+ // FIXME: Is 'right' the maximum line width here, just
+ // as it is in the next case? It's used several times
+ // in The Dig's intro, where 'left' and 'right' are
+ // always 0 and 321 respectively, and apparently we
+ // handle that correctly.
+ sf->drawStringWrap(str, _dst, _width, _height, pos_x, MAX(pos_y, top), left, right, false);
+ break;
+ case 9:
+ // In this case, the 'right' parameter is actually the
+ // maximum line width. This explains why it's sometimes
+ // smaller than 'left'.
+ //
+ // Note that in The Dig's "Spacetime Six" movie it's
+ // 621. I have no idea what that means.
+ sf->drawStringWrap(str, _dst, _width, _height, pos_x, MAX(pos_y, top), left, MIN(left + right, _width), true);
+ break;
+ default:
+ error("SmushPlayer::handleTextResource. Not handled flags: %d", flags);
+ }
+
+ if (string != NULL) {
+ free (string);
+ }
+ if (string3 != NULL) {
+ free (string3);
+ }
+}
+
+const char *SmushPlayer::getString(int id) {
+ return _strings->get(id);
+}
+
+bool SmushPlayer::readString(const char *file) {
+ const char *i = strrchr(file, '.');
+ if (i == NULL) {
+ error("invalid filename : %s", file);
+ }
+ char fname[260];
+ memcpy(fname, file, i - file);
+ strcpy(fname + (i - file), ".trs");
+ if ((_strings = getStrings(_vm, fname, false)) != 0) {
+ return true;
+ }
+
+ if ((_strings = getStrings(_vm, "digtxt.trs", true)) != 0) {
+ return true;
+ }
+ return false;
+}
+
+void SmushPlayer::readPalette(byte *out, Chunk &in) {
+ in.read(out, 0x300);
+}
+
+static byte delta_color(byte org_color, int16 delta_color) {
+ int t = (org_color * 129 + delta_color) / 128;
+ if (t > 255)
+ t = 255;
+ if (t < 0)
+ t = 0;
+ return (byte)t;
+}
+
+void SmushPlayer::handleDeltaPalette(Chunk &b) {
+ checkBlock(b, TYPE_XPAL);
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleDeltaPalette()");
+
+ if (b.getSize() == 0x300 * 3 + 4) {
+
+ b.getWord();
+ b.getWord();
+
+ for (int i = 0; i < 0x300; i++) {
+ _deltaPal[i] = b.getWord();
+ }
+ readPalette(_pal, b);
+ setDirtyColors(0, 255);
+ } else if (b.getSize() == 6) {
+
+ b.getWord();
+ b.getWord();
+ b.getWord();
+
+ for (int i = 0; i < 0x300; i++) {
+ _pal[i] = delta_color(_pal[i], _deltaPal[i]);
+ }
+ setDirtyColors(0, 255);
+ } else {
+ error("SmushPlayer::handleDeltaPalette() Wrong size for DeltaPalette");
+ }
+}
+
+void SmushPlayer::handleNewPalette(Chunk &b) {
+ checkBlock(b, TYPE_NPAL, 0x300);
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleNewPalette()");
+
+ if (_skipPalette)
+ return;
+
+ readPalette(_pal, b);
+ setDirtyColors(0, 255);
+}
+
+void smush_decode_codec1(byte *dst, const byte *src, int left, int top, int width, int height, int pitch);
+
+#ifdef USE_ZLIB
+void SmushPlayer::handleZlibFrameObject(Chunk &b) {
+ if (_skipNext) {
+ _skipNext = false;
+ return;
+ }
+
+ int32 chunkSize = b.getSize();
+ byte *chunkBuffer = (byte *)malloc(chunkSize);
+ assert(chunkBuffer);
+ b.read(chunkBuffer, chunkSize);
+
+ unsigned long decompressedSize = READ_BE_UINT32(chunkBuffer);
+ byte *fobjBuffer = (byte *)malloc(decompressedSize);
+ int result = uncompress(fobjBuffer, &decompressedSize, chunkBuffer + 4, chunkSize - 4);
+ if (result != Z_OK)
+ error("SmushPlayer::handleZlibFrameObject() Zlib uncompress error");
+ free(chunkBuffer);
+
+ byte *ptr = fobjBuffer;
+ int codec = READ_LE_UINT16(ptr); ptr += 2;
+ int left = READ_LE_UINT16(ptr); ptr += 2;
+ int top = READ_LE_UINT16(ptr); ptr += 2;
+ int width = READ_LE_UINT16(ptr); ptr += 2;
+ int height = READ_LE_UINT16(ptr); ptr += 2;
+
+ if ((height == 242) && (width == 384)) {
+ if (_specialBuffer == 0)
+ _specialBuffer = (byte *)malloc(242 * 384);
+ _dst = _specialBuffer;
+ } else if ((height > _vm->_screenHeight) || (width > _vm->_screenWidth))
+ return;
+ // FT Insane uses smaller frames to draw overlays with moving objects
+ // Other .san files do have them as well but their purpose in unknown
+ // and often it causes memory overdraw. So just skip those frames
+ else if (!_insanity && ((height != _vm->_screenHeight) || (width != _vm->_screenWidth)))
+ return;
+
+ if (!_alreadyInit) {
+ _codec37.init(width, height);
+ _codec47.init(width, height);
+ _alreadyInit = true;
+ }
+
+ if ((height == 242) && (width == 384)) {
+ _width = width;
+ _height = height;
+ } else {
+ _width = _vm->_screenWidth;
+ _height = _vm->_screenHeight;
+ }
+
+ switch (codec) {
+ case 1:
+ case 3:
+ smush_decode_codec1(_dst, fobjBuffer + 14, left, top, width, height, _vm->_screenWidth);
+ break;
+ case 37:
+ _codec37.decode(_dst, fobjBuffer + 14);
+ break;
+ case 47:
+ _codec47.decode(_dst, fobjBuffer + 14);
+ break;
+ default:
+ error("Invalid codec for frame object : %d", (int)codec);
+ }
+
+ if (_storeFrame) {
+ if (_frameBuffer == NULL) {
+ _frameBuffer = (byte *)malloc(_width * _height);
+ }
+ memcpy(_frameBuffer, _dst, _width * _height);
+ _storeFrame = false;
+ }
+
+ free(fobjBuffer);
+}
+#endif
+
+void SmushPlayer::handleFrameObject(Chunk &b) {
+ checkBlock(b, TYPE_FOBJ, 14);
+ if (_skipNext) {
+ _skipNext = false;
+ return;
+ }
+
+ int codec = b.getWord();
+ int left = b.getWord();
+ int top = b.getWord();
+ int width = b.getWord();
+ int height = b.getWord();
+
+ if ((height == 242) && (width == 384)) {
+ if (_specialBuffer == 0)
+ _specialBuffer = (byte *)malloc(242 * 384);
+ _dst = _specialBuffer;
+ } else if ((height > _vm->_screenHeight) || (width > _vm->_screenWidth))
+ return;
+ // FT Insane uses smaller frames to draw overlays with moving objects
+ // Other .san files do have them as well but their purpose in unknown
+ // and often it causes memory overdraw. So just skip those frames
+ else if (!_insanity && ((height != _vm->_screenHeight) || (width != _vm->_screenWidth)))
+ return;
+
+ if (!_alreadyInit) {
+ _codec37.init(width, height);
+ _codec47.init(width, height);
+ _alreadyInit = true;
+ }
+
+ if ((height == 242) && (width == 384)) {
+ _width = width;
+ _height = height;
+ } else {
+ _width = _vm->_screenWidth;
+ _height = _vm->_screenHeight;
+ }
+
+ b.getWord();
+ b.getWord();
+
+ int32 chunk_size = b.getSize() - 14;
+ byte *chunk_buffer = (byte *)malloc(chunk_size);
+ assert(chunk_buffer);
+ b.read(chunk_buffer, chunk_size);
+
+ switch (codec) {
+ case 1:
+ case 3:
+ smush_decode_codec1(_dst, chunk_buffer, left, top, width, height, _vm->_screenWidth);
+ break;
+ case 37:
+ _codec37.decode(_dst, chunk_buffer);
+ break;
+ case 47:
+ _codec47.decode(_dst, chunk_buffer);
+ break;
+ default:
+ error("Invalid codec for frame object : %d", (int)codec);
+ }
+
+ if (_storeFrame) {
+ if (_frameBuffer == NULL) {
+ _frameBuffer = (byte *)malloc(_width * _height);
+ }
+ memcpy(_frameBuffer, _dst, _width * _height);
+ _storeFrame = false;
+ }
+
+ free(chunk_buffer);
+}
+
+void SmushPlayer::handleFrame(Chunk &b) {
+ checkBlock(b, TYPE_FRME);
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleFrame(%d)", _frame);
+ _skipNext = false;
+
+ uint32 start_time, end_time;
+ start_time = _vm->_system->getMillis();
+
+ if (_insanity) {
+ _vm->_insane->procPreRendering();
+ }
+
+ while (!b.eof()) {
+ Chunk *sub = b.subBlock();
+ switch (sub->getType()) {
+ case TYPE_NPAL:
+ handleNewPalette(*sub);
+ break;
+ case TYPE_FOBJ:
+ handleFrameObject(*sub);
+ break;
+#ifdef USE_ZLIB
+ case TYPE_ZFOB:
+ handleZlibFrameObject(*sub);
+ break;
+#endif
+ case TYPE_PSAD:
+ if (!_compressedFileMode)
+ handleSoundFrame(*sub);
+ break;
+ case TYPE_TRES:
+ handleTextResource(*sub);
+ break;
+ case TYPE_XPAL:
+ handleDeltaPalette(*sub);
+ break;
+ case TYPE_IACT:
+ // FIXME: check parameters
+ if (_insanity)
+ _vm->_insane->procIACT(_dst, 0, 0, 0, *sub, 0, 0);
+ else {
+ if (!_compressedFileMode)
+ handleIACT(*sub);
+ }
+ break;
+ case TYPE_STOR:
+ handleStore(*sub);
+ break;
+ case TYPE_FTCH:
+ handleFetch(*sub);
+ break;
+ case TYPE_SKIP:
+ if (_insanity)
+ _vm->_insane->procSKIP(*sub);
+ else
+ handleSkip(*sub);
+ break;
+ case TYPE_TEXT:
+ handleTextResource(*sub);
+ break;
+ default:
+ error("Unknown frame subChunk found : %s, %d", Chunk::ChunkString(sub->getType()), sub->getSize());
+ }
+
+ b.reseek();
+ if (sub->getSize() & 1)
+ b.seek(1);
+
+ delete sub;
+ }
+
+ if (_insanity) {
+ _vm->_insane->procPostRendering(_dst, 0, 0, 0, _frame, _nbframes-1);
+ }
+
+ end_time = _vm->_system->getMillis();
+
+ if (_width != 0 && _height != 0) {
+#ifdef _WIN32_WCE
+ if (!_inTimer || _inTimerCount == _inTimerCountRedraw) {
+ updateScreen();
+ _inTimerCount = 0;
+ }
+#else
+ updateScreen();
+#endif
+ }
+ _smixer->handleFrame();
+
+ debugC(DEBUG_SMUSH, "Smush stats: FRME( %03d ), Limit(%d)", end_time - start_time, _speed);
+
+ _frame++;
+}
+
+void SmushPlayer::handleAnimHeader(Chunk &b) {
+ checkBlock(b, TYPE_AHDR, 0x300 + 6);
+ debugC(DEBUG_SMUSH, "SmushPlayer::handleAnimHeader()");
+
+ _version = b.getWord();
+ _nbframes = b.getWord();
+ b.getWord();
+
+ if (_skipPalette)
+ return;
+
+ readPalette(_pal, b);
+ setDirtyColors(0, 255);
+}
+
+void SmushPlayer::setupAnim(const char *file) {
+ int i;
+ char file_font[11];
+
+ if (_insanity) {
+ if (!((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC)))
+ readString("mineroad.trs");
+ } else
+ readString(file);
+
+ if (_vm->_gameId == GID_FT) {
+ if (!((_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))) {
+ _sf[0] = new SmushFont(_vm, true, false);
+ _sf[1] = new SmushFont(_vm, true, false);
+ _sf[2] = new SmushFont(_vm, true, false);
+ _sf[3] = new SmushFont(_vm, true, false);
+ _sf[0]->loadFont("scummfnt.nut");
+ _sf[1]->loadFont("techfnt.nut");
+ _sf[2]->loadFont("titlfnt.nut");
+ _sf[3]->loadFont("specfnt.nut");
+ }
+ } else if (_vm->_gameId == GID_DIG) {
+ if (!(_vm->_features & GF_DEMO)) {
+ for (i = 0; i < 4; i++) {
+ sprintf(file_font, "font%d.nut", i);
+ _sf[i] = new SmushFont(_vm, i != 0, false);
+ _sf[i]->loadFont(file_font);
+ }
+ }
+ } else if (_vm->_gameId == GID_CMI) {
+ for (i = 0; i < 5; i++) {
+ if ((_vm->_features & GF_DEMO) && (i == 4))
+ break;
+ sprintf(file_font, "font%d.nut", i);
+ _sf[i] = new SmushFont(_vm, false, true);
+ _sf[i]->loadFont(file_font);
+ }
+ } else {
+ error("SmushPlayer::setupAnim() Unknown font setup for game");
+ }
+}
+
+void SmushPlayer::parseNextFrame() {
+ Common::StackLock lock(_mutex);
+
+ Chunk *sub;
+
+ if (_vm->_smushPaused)
+ return;
+
+ if (_seekPos >= 0) {
+ if (_smixer)
+ _smixer->stop();
+
+ if (_seekFile.size() > 0) {
+ delete _base;
+ _base = new FileChunk(_seekFile);
+
+ if (_seekPos > 0) {
+ assert(_seekPos > 8);
+ // In this case we need to get palette and number of frames
+ sub = _base->subBlock();
+ checkBlock(*sub, TYPE_AHDR);
+ handleAnimHeader(*sub);
+ delete sub;
+
+ _middleAudio = true;
+ _seekPos -= 8;
+ } else {
+ // We need this in Full Throttle when entering/leaving
+ // the old mine road.
+ tryCmpFile(_seekFile.c_str());
+ }
+ _skipPalette = false;
+ } else {
+ _skipPalette = true;
+ }
+
+ _base->seek(_seekPos, FileChunk::seek_start);
+ _frame = _seekFrame;
+
+ _seekPos = -1;
+ }
+
+ assert(_base);
+ if (_base->eof()) {
+ _vm->_smushVideoShouldFinish = true;
+ return;
+ }
+
+ sub = _base->subBlock();
+
+ switch (sub->getType()) {
+ case TYPE_AHDR: // FT INSANE may seek file to the beginning
+ handleAnimHeader(*sub);
+ break;
+ case TYPE_FRME:
+ handleFrame(*sub);
+ break;
+ default:
+ error("Unknown Chunk found at %x: %x, %d", _base->tell(), sub->getType(), sub->getSize());
+ }
+ delete sub;
+
+ _base->reseek();
+
+ if (_insanity)
+ _vm->_sound->processSound();
+
+ _vm->_imuseDigital->flushTracks();
+}
+
+void SmushPlayer::setPalette(const byte *palette) {
+ memcpy(_pal, palette, 0x300);
+ setDirtyColors(0, 255);
+}
+
+void SmushPlayer::setPaletteValue(int n, byte r, byte g, byte b) {
+ _pal[n * 3 + 0] = r;
+ _pal[n * 3 + 1] = g;
+ _pal[n * 3 + 2] = b;
+ setDirtyColors(n, n);
+}
+
+void SmushPlayer::setDirtyColors(int min, int max) {
+ if (_palDirtyMin > min)
+ _palDirtyMin = min;
+ if (_palDirtyMax < max)
+ _palDirtyMax = max;
+}
+
+void SmushPlayer::warpMouse(int x, int y, int buttons) {
+ _warpNeeded = true;
+ _warpX = x;
+ _warpY = y;
+ _warpButtons = buttons;
+}
+
+void SmushPlayer::updateScreen() {
+#ifdef DUMP_SMUSH_FRAMES
+ char fileName[100];
+ // change path below for dump png files
+ sprintf(fileName, "/path/to/somethere/%s%04d.png", _vm->getBaseName(), _frame);
+ FILE *file = fopen(fileName, "wb");
+ if (file == NULL)
+ error("can't open file for writing png");
+
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ if (png_ptr == NULL) {
+ fclose(file);
+ error("can't write png header");
+ }
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ fclose(file);
+ error("can't create png info struct");
+ }
+ if (setjmp(png_ptr->jmpbuf)) {
+ fclose(file);
+ error("png jmpbuf error");
+ }
+
+ png_init_io(png_ptr, file);
+
+ png_set_IHDR(png_ptr, info_ptr, _width, _height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ png_colorp palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color));
+ for (int i = 0; i != 256; ++i) {
+ (palette + i)->red = _pal[i * 3 + 0];
+ (palette + i)->green = _pal[i * 3 + 1];
+ (palette + i)->blue = _pal[i * 3 + 2];
+ }
+
+ png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
+
+ png_write_info(png_ptr, info_ptr);
+ png_set_flush(png_ptr, 10);
+
+ png_bytep row_pointers[480];
+ for (int y = 0 ; y < _height ; y++)
+ row_pointers[y] = (png_byte *) (_dst + y * _width);
+ png_write_image(png_ptr, row_pointers);
+ png_write_end(png_ptr, info_ptr);
+ png_free(png_ptr, palette);
+
+ fclose(file);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+#endif
+
+ uint32 end_time, start_time = _vm->_system->getMillis();
+ _updateNeeded = true;
+ end_time = _vm->_system->getMillis();
+ debugC(DEBUG_SMUSH, "Smush stats: updateScreen( %03d )", end_time - start_time);
+}
+
+void SmushPlayer::insanity(bool flag) {
+ _insanity = flag;
+}
+
+void SmushPlayer::seekSan(const char *file, int32 pos, int32 contFrame) {
+ Common::StackLock lock(_mutex);
+
+ _seekFile = file ? file : "";
+ _seekPos = pos;
+ _seekFrame = contFrame;
+}
+
+void SmushPlayer::tryCmpFile(const char *filename) {
+ if (_compressedFile.isOpen()) {
+ _vm->_mixer->stopHandle(_compressedFileSoundHandle);
+ _compressedFile.close();
+ }
+ _compressedFileMode = false;
+ const char *i = strrchr(filename, '.');
+ if (i == NULL) {
+ error("invalid filename : %s", filename);
+ }
+#if defined(USE_MAD) || defined(USE_VORBIS)
+ char fname[260];
+#endif
+#ifdef USE_MAD
+ memcpy(fname, filename, i - filename);
+ strcpy(fname + (i - filename), ".mp3");
+ _compressedFile.open(fname);
+ if (_compressedFile.isOpen()) {
+ int size = _compressedFile.size();
+ _compressedFileMode = true;
+ _vm->_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_compressedFileSoundHandle, makeMP3Stream(&_compressedFile, size));
+ return;
+ }
+#endif
+#ifdef USE_VORBIS
+ memcpy(fname, filename, i - filename);
+ strcpy(fname + (i - filename), ".ogg");
+ _compressedFile.open(fname);
+ if (_compressedFile.isOpen()) {
+ int size = _compressedFile.size();
+ _compressedFileMode = true;
+ _vm->_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_compressedFileSoundHandle, makeVorbisStream(&_compressedFile, size));
+ return;
+ }
+#endif
+}
+
+void SmushPlayer::play(const char *filename, int32 offset, int32 startFrame) {
+
+ // Verify the specified file exists
+ ScummFile f;
+ _vm->openFile(f, filename);
+ if (!f.isOpen()) {
+ warning("SmushPlayer::play() File not found %s", filename);
+ return;
+ }
+ f.close();
+
+ _updateNeeded = false;
+ _warpNeeded = false;
+ _palDirtyMin = 256;
+ _palDirtyMax = -1;
+
+ // Hide mouse
+ bool oldMouseState = _vm->_system->showMouse(false);
+
+ // Load the video
+ _seekFile = filename;
+ _seekPos = offset;
+ _seekFrame = startFrame;
+ _base = 0;
+
+ setupAnim(filename);
+ init();
+
+ for (;;) {
+ if (_warpNeeded) {
+ _vm->_system->warpMouse(_warpX, _warpY);
+ _warpNeeded = false;
+ }
+ _vm->parseEvents();
+ _vm->processKbd(true);
+ if (_palDirtyMax >= _palDirtyMin) {
+ byte palette_colors[1024];
+ byte *p = palette_colors;
+
+ for (int i = _palDirtyMin; i <= _palDirtyMax; i++) {
+ byte *data = _pal + i * 3;
+
+ *p++ = data[0];
+ *p++ = data[1];
+ *p++ = data[2];
+ *p++ = 0;
+ }
+
+ _vm->_system->setPalette(palette_colors, _palDirtyMin, _palDirtyMax - _palDirtyMin + 1);
+
+ _palDirtyMax = -1;
+ _palDirtyMin = 256;
+ }
+ if (_updateNeeded) {
+ uint32 end_time, start_time;
+
+ start_time = _vm->_system->getMillis();
+ _vm->_system->copyRectToScreen(_dst, _width, 0, 0, _width, _height);
+ _vm->_system->updateScreen();
+ _updateNeeded = false;
+#ifdef _WIN32_WCE
+ _inTimer = false;
+ _inTimerCount = 0;
+#endif
+
+ end_time = _vm->_system->getMillis();
+
+ debugC(DEBUG_SMUSH, "Smush stats: BackendUpdateScreen( %03d )", end_time - start_time);
+
+ }
+ if (_vm->_smushVideoShouldFinish || _vm->_quit || _vm->_saveLoadFlag)
+ break;
+ _vm->_system->delayMillis(10);
+ }
+
+ release();
+
+ // Reset mouse state
+ _vm->_system->showMouse(oldMouseState);
+}
+
+} // End of namespace Scumm
+
diff --git a/engines/scumm/smush/smush_player.h b/engines/scumm/smush/smush_player.h
new file mode 100644
index 0000000000..f697057d89
--- /dev/null
+++ b/engines/scumm/smush/smush_player.h
@@ -0,0 +1,148 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(SMUSH_PLAYER_H) && !defined(DISABLE_SCUMM_7_8)
+#define SMUSH_PLAYER_H
+
+#include "common/util.h"
+#include "scumm/smush/chunk.h"
+#include "scumm/smush/codec37.h"
+#include "scumm/smush/codec47.h"
+#include "scumm/sound.h"
+
+namespace Scumm {
+
+class ScummEngine_v6;
+class SmushFont;
+class SmushMixer;
+class StringResource;
+
+class SmushPlayer {
+ friend class Insane;
+private:
+ ScummEngine_v6 *_vm;
+ int _version;
+ int32 _nbframes;
+ SmushMixer *_smixer;
+ int16 _deltaPal[0x300];
+ byte _pal[0x300];
+ StringResource *_strings;
+ Codec37Decoder _codec37;
+ Codec47Decoder _codec47;
+ FileChunk *_base;
+ byte *_frameBuffer;
+ byte *_specialBuffer;
+
+ Common::String _seekFile;
+ int32 _seekPos;
+ int32 _seekFrame;
+
+ bool _skipNext;
+ bool _subtitles;
+ bool _skips[37];
+ int32 _frame;
+
+ Audio::SoundHandle _IACTchannel;
+ AppendableAudioStream *_IACTstream;
+
+ Audio::SoundHandle _compressedFileSoundHandle;
+ bool _compressedFileMode;
+ Common::File _compressedFile;
+ byte _IACToutput[4096];
+ int32 _IACTpos;
+ bool _storeFrame;
+ int _soundFrequency;
+ bool _alreadyInit;
+ bool _initDone;
+ int _speed;
+ bool _outputSound;
+
+ byte *_dst;
+ bool _updateNeeded;
+ bool _warpNeeded;
+ int _palDirtyMin, _palDirtyMax;
+ int _warpX, _warpY;
+ int _warpButtons;
+ bool _insanity;
+ bool _middleAudio;
+ bool _skipPalette;
+#ifdef _WIN32_WCE
+ bool _inTimer;
+ int16 _inTimerCount;
+ int16 _inTimerCountRedraw;
+#endif
+
+ Common::Mutex _mutex;
+
+public:
+ SmushPlayer(ScummEngine_v6 *scumm, int speed);
+ ~SmushPlayer();
+
+ void play(const char *filename, int32 offset = 0, int32 startFrame = 0);
+ void warpMouse(int x, int y, int buttons);
+
+protected:
+ SmushFont *_sf[5];
+ int _width, _height;
+
+ int _origPitch, _origNumStrips;
+
+ void insanity(bool);
+ void setPalette(const byte *palette);
+ void setPaletteValue(int n, byte r, byte g, byte b);
+ void setDirtyColors(int min, int max);
+ void seekSan(const char *file, int32 pos, int32 contFrame);
+ const char *getString(int id);
+
+private:
+ void parseNextFrame();
+ void init();
+ void release();
+ void setupAnim(const char *file);
+ void updateScreen();
+ void tryCmpFile(const char *filename);
+
+ bool readString(const char *file);
+ void checkBlock(const Chunk &, Chunk::type, uint32 = 0);
+ void handleAnimHeader(Chunk &);
+ void handleFrame(Chunk &);
+ void handleNewPalette(Chunk &);
+#ifdef USE_ZLIB
+ void handleZlibFrameObject(Chunk &b);
+#endif
+ void handleFrameObject(Chunk &);
+ void handleSoundBuffer(int32, int32, int32, int32, int32, int32, Chunk &, int32);
+ void handleSoundFrame(Chunk &);
+ void handleSkip(Chunk &);
+ void handleStore(Chunk &);
+ void handleFetch(Chunk &);
+ void handleIACT(Chunk &);
+ void handleTextResource(Chunk &);
+ void handleDeltaPalette(Chunk &);
+ void readPalette(byte *, Chunk &);
+
+ static void timerCallback(void *ptr);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
new file mode 100644
index 0000000000..26f0c712c8
--- /dev/null
+++ b/engines/scumm/sound.cpp
@@ -0,0 +1,2364 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/actor.h"
+#include "scumm/imuse.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+
+#include "common/config-manager.h"
+#include "common/timer.h"
+#include "common/util.h"
+
+#include "sound/adpcm.h"
+#include "sound/audiocd.h"
+#include "sound/flac.h"
+#include "sound/mididrv.h"
+#include "sound/mixer.h"
+#include "sound/mp3.h"
+#include "sound/voc.h"
+#include "sound/vorbis.h"
+#include "sound/wave.h"
+
+
+
+namespace Scumm {
+
+struct MP3OffsetTable { /* Compressed Sound (.SO3) */
+ int org_offset;
+ int new_offset;
+ int num_tags;
+ int compressed_size;
+};
+
+
+Sound::Sound(ScummEngine *parent)
+ :
+ _vm(parent),
+ _soundQuePos(0),
+ _soundQue2Pos(0),
+ _sfxFile(0),
+ _offsetTable(0),
+ _numSoundEffects(0),
+ _soundMode(kVOCMode),
+ _talk_sound_a1(0),
+ _talk_sound_a2(0),
+ _talk_sound_b1(0),
+ _talk_sound_b2(0),
+ _talk_sound_mode(0),
+ _talk_sound_channel(0),
+ _mouthSyncMode(false),
+ _endOfMouthSync(false),
+ _curSoundPos(0),
+ _overrideFreq(0),
+ _currentCDSound(0),
+ _currentMusic(0),
+ _soundsPaused(false),
+ _sfxMode(0),
+ _heMusic(0),
+ _heMusicTracks(0) {
+
+ memset(_heChannel, 0, sizeof(_heChannel));
+ memset(_soundQue, 0, sizeof(_soundQue));
+ memset(_soundQue2, 0, sizeof(_soundQue2));
+ memset(_mouthSyncTimes, 0, sizeof(_mouthSyncTimes));
+}
+
+Sound::~Sound() {
+ stopCDTimer();
+ delete _sfxFile;
+
+ // HE Specific
+ free(_heMusic);
+}
+
+void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) {
+ if (_vm->VAR_LAST_SOUND != 0xFF)
+ _vm->VAR(_vm->VAR_LAST_SOUND) = sound;
+
+ if (heFlags & 16) {
+ playHESound(sound, heOffset, heChannel, heFlags);
+ return;
+ }
+
+ // HE music resources are in separate file
+ if (sound <= _vm->_numSounds)
+ _vm->ensureResourceLoaded(rtSound, sound);
+
+ addSoundToQueue2(sound, heOffset, heChannel, heFlags);
+}
+
+void Sound::addSoundToQueue2(int sound, int heOffset, int heChannel, int heFlags) {
+ if (_vm->_heversion >= 60 && _soundQue2Pos) {
+ int i = _soundQue2Pos;
+ while (i--) {
+ if (_soundQue2[i].sound == sound && !(heFlags & 2))
+ return;
+ }
+ }
+
+ assert(_soundQue2Pos < ARRAYSIZE(_soundQue2));
+ _soundQue2[_soundQue2Pos].sound = sound;
+ _soundQue2[_soundQue2Pos].offset = heOffset;
+ _soundQue2[_soundQue2Pos].channel = heChannel;
+ _soundQue2[_soundQue2Pos].flags = heFlags;
+ _soundQue2Pos++;
+}
+
+void Sound::processSound() {
+ if (_vm->_heversion >= 60) {
+ processSoundQueues();
+ processSfxQueues();
+ } else {
+ processSfxQueues();
+
+ if (_vm->_features & GF_DIGI_IMUSE)
+ return;
+
+ processSoundQueues();
+ }
+}
+
+void Sound::processSoundQueues() {
+ int i = 0, num;
+ int snd, heOffset, heChannel, heFlags;
+ int data[16];
+
+ if (_vm->_heversion >= 72) {
+ for (i = 0; i <_soundQue2Pos; i++) {
+ snd = _soundQue2[i].sound;
+ heOffset = _soundQue2[i].offset;
+ heChannel = _soundQue2[i].channel;
+ heFlags = _soundQue2[i].flags;
+ if (snd) {
+ if (_vm->_heversion>= 60)
+ playHESound(snd, heOffset, heChannel, heFlags);
+ else
+ playSound(snd);
+ }
+ }
+ _soundQue2Pos = 0;
+ } else {
+ while (_soundQue2Pos) {
+ _soundQue2Pos--;
+ snd = _soundQue2[_soundQue2Pos].sound;
+ heOffset = _soundQue2[_soundQue2Pos].offset;
+ heChannel = _soundQue2[_soundQue2Pos].channel;
+ heFlags = _soundQue2[_soundQue2Pos].flags;
+ if (snd) {
+ if (_vm->_heversion>= 60)
+ playHESound(snd, heOffset, heChannel, heFlags);
+ else
+ playSound(snd);
+ }
+ }
+ }
+
+ while (i < _soundQuePos) {
+ num = _soundQue[i++];
+ if (i + num > _soundQuePos) {
+ error("processSoundQues: invalid num value");
+ break;
+ }
+ memset(data, 0, sizeof(data));
+ if (num > 0) {
+ for (int j = 0; j < num; j++)
+ data[j] = _soundQue[i + j];
+ i += num;
+
+ debugC(DEBUG_IMUSE, "processSoundQues(%d,%d,%d,%d,%d,%d,%d,%d,%d)",
+ data[0] >> 8, data[0] & 0xFF,
+ data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
+
+ if (_vm->_imuse) {
+ _vm->VAR(_vm->VAR_SOUNDRESULT) = (short)_vm->_imuse->doCommand (num, data);
+ }
+ }
+ }
+ _soundQuePos = 0;
+}
+
+void Sound::playSound(int soundID) {
+ byte *mallocedPtr = NULL;
+ byte *ptr;
+ char *sound;
+ int size = -1;
+ int rate;
+ byte flags = Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE;
+
+ debugC(DEBUG_SOUND, "playSound #%d (room %d)", soundID,
+ _vm->getResourceRoomNr(rtSound, soundID));
+
+ ptr = _vm->getResourceAddress(rtSound, soundID);
+
+ if (!ptr) {
+ return;
+ }
+
+ // Support for SFX in Monkey Island 1, Mac version
+ // This is rather hackish right now, but works OK. SFX are not sounding
+ // 100% correct, though, not sure right now what is causing this.
+ else if (READ_UINT32(ptr) == MKID('Mac1')) {
+ // Read info from the header
+ size = READ_BE_UINT32(ptr+0x60);
+ rate = READ_BE_UINT16(ptr+0x64);
+
+ // Skip over the header (fixed size)
+ ptr += 0x72;
+
+ // Allocate a sound buffer, copy the data into it, and play
+ sound = (char *)malloc(size);
+ memcpy(sound, ptr, size);
+ _vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
+ }
+ // WORKAROUND bug # 1311447
+ else if (READ_UINT32(ptr) == MKID(0x460e200d)) {
+ // This sound resource occurs in the Macintosh version of Monkey Island.
+ // I do now know whether it is used in any place other than the one
+ // mentioned in the bug report above; in case it is, I put a check here.
+ assert(soundID == 39);
+
+ // The samplerate is copied from the sound resouce 39 of the PC CD/VGA
+ // version of Monkey Island.
+
+ // Read info from the header
+ size = READ_BE_UINT32(ptr+4);
+ rate = 6849;
+
+ // Skip over the header (fixed size)
+ ptr += 0x26;
+
+ // Allocate a sound buffer, copy the data into it, and play
+ sound = (char *)malloc(size);
+ memcpy(sound, ptr, size);
+ _vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
+ }
+ // Support for sampled sound effects in Monkey Island 1 and 2
+ else if (READ_UINT32(ptr) == MKID('SBL ')) {
+ debugC(DEBUG_SOUND, "Using SBL sound effect");
+
+ // SBL resources essentially contain VOC sound data.
+ // There are at least two main variants: in one,
+ // there are two subchunks AUhd and AUdt, in the other
+ // the chunks are called WVhd and WVdt. Besides that,
+ // the two variants seem pretty similiar.
+
+ // The first subchunk (AUhd resp. WVhd) seems to always
+ // contain three bytes (00 00 80) of unknown meaning.
+ // After that, a second subchunk contains VOC data.
+ // Two real examples:
+ //
+ // 53 42 4c 20 00 00 11 ae |SBL ....|
+ // 41 55 68 64 00 00 00 03 |AUhd....|
+ // 00 00 80 41 55 64 74 00 |...AUdt.|
+ // 00 11 9b 01 96 11 00 a6 |........|
+ // 00 7f 7f 7e 7e 7e 7e 7e |...~~~~~|
+ // 7e 7f 7f 80 80 7f 7f 7f |~.......|
+ // 7f 80 80 7f 7e 7d 7d 7e |....~}}~|
+ // 7e 7e 7e 7e 7e 7e 7e 7f |~~~~~~~.|
+ //
+ // And from the non-interactive Sam & Max demo:
+ //
+ // 53 42 4c 20 00 01 15 6e |SBL ...n|
+ // 57 56 68 64 00 00 00 03 |WVhd....|
+ // 00 00 80 57 56 64 74 00 |...WVdt.|
+ // 01 15 5b 01 56 15 01 a6 |..[.V...|
+ // 00 80 80 80 80 80 80 80 |........|
+ // 80 80 80 80 80 80 80 80 |........|
+ // 80 80 80 80 80 80 80 80 |........|
+ // 80 80 80 80 80 80 80 80 |........|
+
+ size = READ_BE_UINT32(ptr + 4) - 27;
+ ptr += 27;
+
+ // Fingolfin says: after eyeballing a single SEGA
+ // SBL resource, it would seem as if the content of the
+ // data subchunk (AUdt) is XORed with 0x16. At least
+ // then a semi-sane VOC header is revealed, with
+ // a sampling rate of ~25000 Hz (does that make sense?).
+ // I'll add some code to test that theory for now.
+
+ // Check if the resource has already been demangled
+ if ((_vm->_platform == Common::kPlatformSegaCD) && (ptr[0] != 1)) {
+ for (int i = 0; i < size; i++) {
+ ptr[i] ^= 0x16;
+ if (ptr[i] >= 0x7F) {
+ ptr[i] = 0xFE - ptr[i];
+ ptr[i] ^= 0x80;
+ }
+ }
+ }
+
+ // TODO: It would be nice if we could use readVOCFromMemory() here.
+ // We'd have to add the 'Creative Voice File' header for this, though,
+ // or make readVOCFromMemory() less strict.
+
+ VocBlockHeader &voc_block_hdr = *(VocBlockHeader *)ptr;
+ assert(voc_block_hdr.blocktype == 1);
+ size = voc_block_hdr.size[0] + (voc_block_hdr.size[1] << 8) + (voc_block_hdr.size[2] << 16) - 2;
+ rate = getSampleRateFromVOCRate(voc_block_hdr.sr);
+ assert(voc_block_hdr.pack == 0);
+
+ // Allocate a sound buffer, copy the data into it, and play
+ sound = (char *)malloc(size);
+ memcpy(sound, ptr + 6, size);
+ _vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
+ }
+ else if ((_vm->_platform == Common::kPlatformFMTowns && _vm->_version == 3) || READ_UINT32(ptr) == MKID('SOUN') || READ_UINT32(ptr) == MKID('TOWS')) {
+
+ bool tows = READ_UINT32(ptr) == MKID('TOWS');
+ if (_vm->_version == 3) {
+ size = READ_LE_UINT32(ptr);
+ } else {
+ size = READ_BE_UINT32(ptr + 4) - 2;
+ if (tows)
+ size += 8;
+ ptr += 2;
+ }
+
+ rate = 11025;
+ int type = *(ptr + 0x0D);
+ int numInstruments;
+
+ if (tows)
+ type = 0;
+
+ switch (type) {
+ case 0: // Sound effect
+ numInstruments = *(ptr + 0x14);
+ if (tows)
+ numInstruments = 1;
+ ptr += 0x16;
+ size -= 0x16;
+
+ while (numInstruments--) {
+ int waveSize = READ_LE_UINT32(ptr + 0x0C);
+ int loopStart = READ_LE_UINT32(ptr + 0x10) * 2;
+ int loopEnd = READ_LE_UINT32(ptr + 0x14) - 1;
+ rate = READ_LE_UINT32(ptr + 0x18) * 1000 / 0x62;
+ ptr += 0x20;
+ size -= 0x20;
+ if (size < waveSize) {
+ warning("Wrong wave size in sound #%i: %i", soundID, waveSize);
+ waveSize = size;
+ }
+ sound = (char *)malloc(waveSize);
+ for (int x = 0; x < waveSize; x++) {
+ int b = *ptr++;
+ if (b < 0x80)
+ sound[x] = 0x7F - b;
+ else
+ sound[x] = b;
+ }
+ size -= waveSize;
+
+ if (loopEnd > 0)
+ flags |= Audio::Mixer::FLAG_LOOP;
+
+ _vm->_mixer->playRaw(NULL, sound, waveSize, rate, flags, soundID, 255, 0, loopStart, loopEnd);
+ }
+ break;
+ case 1:
+ // Music (Euphony format)
+ if (_vm->_musicEngine)
+ _vm->_musicEngine->startSound(soundID);
+ break;
+ case 2: // CD track resource
+ ptr += 0x16;
+
+ if (soundID == _currentCDSound && pollCD() == 1) {
+ free(mallocedPtr);
+ return;
+ }
+
+ {
+ int track = ptr[0];
+ int loops = ptr[1];
+ int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
+ int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7];
+
+ playCDTrack(track, loops == 0xff ? -1 : loops, start, end <= start ? 0 : end - start);
+ }
+
+ _currentCDSound = soundID;
+ break;
+ default:
+ // All other sound types are ignored
+ break;
+ }
+ }
+ else if ((_vm->_gameId == GID_LOOM) && (_vm->_platform == Common::kPlatformMacintosh)) {
+ // Mac version of Loom uses yet another sound format
+ /*
+ playSound #9 (room 70)
+ 000000: 55 00 00 45 73 6f 00 64 01 00 00 00 00 00 00 00 |U..Eso.d........|
+ 000010: 00 05 00 8e 2a 8f 2d 1c 2a 8f 2a 8f 2d 1c 00 28 |....*.-.*.*.-..(|
+ 000020: 00 31 00 3a 00 43 00 4c 00 01 00 00 00 01 00 64 |.1.:.C.L.......d|
+ 000030: 5a 00 01 00 00 00 01 00 64 00 00 01 00 00 00 01 |Z.......d.......|
+ 000040: 00 64 5a 00 01 00 00 00 01 00 64 5a 00 01 00 00 |.dZ.......dZ....|
+ 000050: 00 01 00 64 00 00 00 00 00 00 00 07 00 00 00 64 |...d...........d|
+ 000060: 64 00 00 4e 73 6f 00 64 01 00 00 00 00 00 00 00 |d..Nso.d........|
+ 000070: 00 05 00 89 3d 57 2d 1c 3d 57 3d 57 2d 1c 00 28 |....=W-.=W=W-..(|
+ playSound #16 (room 69)
+ 000000: dc 00 00 a5 73 6f 00 64 01 00 00 00 00 00 00 00 |....so.d........|
+ 000010: 00 05 00 00 2a 8f 03 e8 03 e8 03 e8 03 e8 00 28 |....*..........(|
+ 000020: 00 79 00 7f 00 85 00 d6 00 01 00 00 00 19 01 18 |.y..............|
+ 000030: 2f 00 18 00 01 18 32 00 18 00 01 18 36 00 18 00 |/.....2.....6...|
+ 000040: 01 18 3b 00 18 00 01 18 3e 00 18 00 01 18 42 00 |..;.....>.....B.|
+ 000050: 18 00 01 18 47 00 18 00 01 18 4a 00 18 00 01 18 |....G.....J.....|
+ 000060: 4e 00 10 00 01 18 53 00 10 00 01 18 56 00 10 00 |N.....S.....V...|
+ 000070: 01 18 5a 00 10 00 02 28 5f 00 01 00 00 00 00 00 |..Z....(_.......|
+ */
+ }
+ else if ((_vm->_platform == Common::kPlatformMacintosh) && (_vm->_gameId == GID_INDY3) && (ptr[26] == 0)) {
+ size = READ_BE_UINT16(ptr + 12);
+ rate = 3579545 / READ_BE_UINT16(ptr + 20);
+ sound = (char *)malloc(size);
+ int vol = ptr[24] * 4;
+ memcpy(sound, ptr + READ_BE_UINT16(ptr + 8), size);
+ _vm->_mixer->playRaw(NULL, sound, size, rate, Audio::Mixer::FLAG_AUTOFREE, soundID, vol, 0);
+ }
+ else {
+
+ if (_vm->_gameId == GID_MONKEY_VGA || _vm->_gameId == GID_MONKEY_EGA
+ || (_vm->_gameId == GID_MONKEY && _vm->_platform == Common::kPlatformMacintosh)) {
+ // Sound is currently not supported at all in the amiga versions of these games
+ if (_vm->_platform == Common::kPlatformAmiga) {
+ int track = -1;
+ if (soundID == 50)
+ track = 17;
+ else if (ptr[6] == 0x7F && ptr[7] == 0x00 && ptr[8] == 0x80) {
+ static const char tracks[16] = {13,14,10,3,4,9,16,5,1,8,2,15,6,7,11,12};
+ if (ptr[9] == 0x0E)
+ track = 18;
+ else
+ track = tracks[ptr[9] - 0x23];
+ }
+ if (track != -1) {
+ playCDTrack(track,((track < 5) || (track > 16)) ? 1 : -1,0,0);
+ stopCDTimer();
+ _currentCDSound = soundID;
+ }
+ return;
+ }
+
+ // Works around the fact that in some places in MonkeyEGA/VGA,
+ // the music is never explicitly stopped.
+ // Rather it seems that starting a new music is supposed to
+ // automatically stop the old song.
+ if (_vm->_imuse) {
+ if (READ_UINT32(ptr) != MKID('ASFX'))
+ _vm->_imuse->stopAllSounds();
+ }
+ }
+
+ if (_vm->_musicEngine) {
+ _vm->_musicEngine->startSound(soundID);
+ }
+ }
+
+ free(mallocedPtr);
+}
+
+void Sound::processSfxQueues() {
+
+ if (_talk_sound_mode != 0) {
+ if (_talk_sound_mode & 1)
+ startTalkSound(_talk_sound_a1, _talk_sound_b1, 1);
+ if (_talk_sound_mode & 2)
+ startTalkSound(_talk_sound_a2, _talk_sound_b2, 2, &_talkChannelHandle);
+ _talk_sound_mode = 0;
+ }
+
+ const int act = _vm->getTalkingActor();
+ if ((_sfxMode & 2) && act != 0) {
+ Actor *a;
+ bool finished;
+
+ if (_vm->_imuseDigital) {
+ finished = !isSoundRunning(kTalkSoundID);
+ } else if (_vm->_heversion >= 60) {
+ finished = !isSoundRunning(1);
+ } else {
+ finished = !_vm->_mixer->isSoundHandleActive(_talkChannelHandle);
+ }
+
+ if ((uint) act < 0x80 && ((_vm->_version == 8) || (_vm->_version <= 7 && !_vm->_string[0].no_talk_anim))) {
+ a = _vm->derefActor(act, "processSfxQueues");
+ if (a->isInCurrentRoom()) {
+ if (isMouthSyncOff(_curSoundPos) && !_mouthSyncMode) {
+ if (!_endOfMouthSync)
+ a->runActorTalkScript(a->_talkStopFrame);
+ _mouthSyncMode = 0;
+ } else if (isMouthSyncOff(_curSoundPos) == 0 && !_mouthSyncMode) {
+ a->runActorTalkScript(a->_talkStartFrame);
+ _mouthSyncMode = 1;
+ }
+
+ if (_vm->_version <= 6 && finished)
+ a->runActorTalkScript(a->_talkStopFrame);
+ }
+ }
+
+ if ((!ConfMan.getBool("subtitles") && finished && _vm->_version <= 6) || (finished && _vm->_talkDelay == 0)) {
+ if (!(_vm->_version == 8 && _vm->VAR(_vm->VAR_HAVE_MSG) == 0))
+ _vm->stopTalk();
+ }
+ }
+
+ if (_sfxMode & 1) {
+ if (isSfxFinished()) {
+ _sfxMode &= ~1;
+ }
+ }
+}
+
+static int compareMP3OffsetTable(const void *a, const void *b) {
+ return ((const MP3OffsetTable *)a)->org_offset - ((const MP3OffsetTable *)b)->org_offset;
+}
+
+void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle) {
+ int num = 0, i;
+ int size = 0;
+ int id = -1;
+
+ if (_vm->_gameId == GID_CMI) {
+ _sfxMode |= mode;
+ return;
+ } else if (_vm->_gameId == GID_DIG) {
+ _sfxMode |= mode;
+ if (!(_vm->_features & GF_DEMO))
+ return;
+
+ char filename[30];
+ char roomname[10];
+
+ if (offset == 1)
+ strcpy(roomname, "logo");
+ else if (offset == 15)
+ strcpy(roomname, "canyon");
+ else if (offset == 17)
+ strcpy(roomname, "pig");
+ else if (offset == 18)
+ strcpy(roomname, "derelict");
+ else if (offset == 19)
+ strcpy(roomname, "wreck");
+ else if (offset == 20)
+ strcpy(roomname, "grave");
+ else if (offset == 23)
+ strcpy(roomname, "nexus");
+ else if (offset == 79)
+ strcpy(roomname, "newton");
+ else {
+ warning("startTalkSound: dig demo: unknown room number: %d", offset);
+ return;
+ }
+
+ _sfxFile->close();
+ sprintf(filename, "audio/%s.%d/%d.voc", roomname, offset, b);
+ _vm->openFile(*_sfxFile, filename);
+ if (!_sfxFile->isOpen()) {
+ sprintf(filename, "%d.%d.voc", offset, b);
+ _vm->openFile(*_sfxFile, filename);
+ }
+ if (!_sfxFile->isOpen()) {
+ warning("startTalkSound: dig demo: voc file not found");
+ return;
+ }
+ } else {
+
+ if (!_sfxFile->isOpen()) {
+ warning("startTalkSound: SFX file is not open");
+ return;
+ }
+
+ // Some games frequently assume that starting one sound effect will
+ // automatically stop any other that may be playing at that time. So
+ // that is what we do here, but we make an exception for speech.
+
+ if (mode == 1 && (_vm->_gameId == GID_TENTACLE || _vm->_gameId == GID_SAMNMAX)) {
+ id = 777777 + _talk_sound_channel;
+ _vm->_mixer->stopID(id);
+ }
+
+ if (b > 8) {
+ num = (b - 8) >> 1;
+ }
+
+ if (_offsetTable != NULL) {
+ MP3OffsetTable *result = NULL, key;
+
+ key.org_offset = offset;
+ result = (MP3OffsetTable *)bsearch(&key, _offsetTable, _numSoundEffects,
+ sizeof(MP3OffsetTable), compareMP3OffsetTable);
+
+ if (result == NULL) {
+ warning("startTalkSound: did not find sound at offset %d !", offset);
+ return;
+ }
+ if (2 * num != result->num_tags) {
+ warning("startTalkSound: number of tags do not match (%d - %d) !", b,
+ result->num_tags);
+ num = result->num_tags;
+ }
+ offset = result->new_offset;
+ size = result->compressed_size;
+ } else {
+ offset += 8;
+ size = -1;
+ }
+
+ _sfxFile->seek(offset, SEEK_SET);
+
+ assert(num + 1 < (int)ARRAYSIZE(_mouthSyncTimes));
+ for (i = 0; i < num; i++)
+ _mouthSyncTimes[i] = _sfxFile->readUint16BE();
+
+ _mouthSyncTimes[i] = 0xFFFF;
+ _sfxMode |= mode;
+ _curSoundPos = 0;
+ _mouthSyncMode = true;
+ }
+
+ if (!_soundsPaused && _vm->_mixer->isReady()) {
+ AudioStream *input = NULL;
+
+ switch (_soundMode) {
+ case kMP3Mode:
+ #ifdef USE_MAD
+ assert(size > 0);
+ input = makeMP3Stream(_sfxFile, size);
+ #endif
+ break;
+ case kVorbisMode:
+ #ifdef USE_VORBIS
+ assert(size > 0);
+ input = makeVorbisStream(_sfxFile, size);
+ #endif
+ break;
+ case kFlacMode:
+ #ifdef USE_FLAC
+ assert(size > 0);
+ input = makeFlacStream(_sfxFile, size);
+ #endif
+ break;
+ default:
+ input = makeVOCStream(*_sfxFile);
+ }
+
+ if (!input) {
+ warning("startSfxSound failed to load sound");
+ return;
+ }
+
+ if (_vm->_imuseDigital) {
+#ifndef DISABLE_SCUMM_7_8
+ //_vm->_imuseDigital->stopSound(kTalkSoundID);
+ _vm->_imuseDigital->startVoice(kTalkSoundID, input);
+#endif
+ } else {
+ _vm->_mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, input, id);
+ }
+ }
+}
+
+void Sound::stopTalkSound() {
+ if (_sfxMode & 2) {
+ if (_vm->_imuseDigital) {
+#ifndef DISABLE_SCUMM_7_8
+ _vm->_imuseDigital->stopSound(kTalkSoundID);
+#endif
+ } else if (_vm->_heversion >= 60) {
+ stopSound(1);
+ } else {
+ _vm->_mixer->stopHandle(_talkChannelHandle);
+ }
+ _sfxMode &= ~2;
+ }
+}
+
+bool Sound::isMouthSyncOff(uint pos) {
+ uint j;
+ bool val = true;
+ uint16 *ms = _mouthSyncTimes;
+
+ _endOfMouthSync = false;
+ do {
+ val = !val;
+ j = *ms++;
+ if (j == 0xFFFF) {
+ _endOfMouthSync = true;
+ break;
+ }
+ } while (pos > j);
+ return val;
+}
+
+int Sound::isSoundRunning(int sound) const {
+#ifndef DISABLE_SCUMM_7_8
+ if (_vm->_imuseDigital)
+ return (_vm->_imuseDigital->getSoundStatus(sound) != 0);
+#endif
+
+ if (sound == _currentCDSound)
+ return pollCD();
+
+ if (_vm->_heversion >= 70) {
+ if (sound >= 10000) {
+ return _vm->_mixer->getSoundID(_heSoundChannels[sound - 10000]);
+ }
+ } else if (_vm->_heversion >= 60) {
+ if (sound == -2) {
+ return !isSfxFinished();
+ } else if (sound == -1) {
+ // getSoundStatus(), with a -1, will return the
+ // ID number of the first active music it finds.
+ if (_currentMusic)
+ return (_vm->_mixer->isSoundIDActive(_currentMusic) ? _currentMusic : 0);
+ else if (_vm->_imuse)
+ return (_vm->_imuse->getSoundStatus(sound));
+ }
+ }
+
+ if (_vm->_mixer->isSoundIDActive(sound))
+ return 1;
+
+ if (isSoundInQueue(sound))
+ return 1;
+
+ if (sound > _vm->_numSounds || !_vm->res.isResourceLoaded(rtSound, sound))
+ return 0;
+
+ if (_vm->_musicEngine)
+ return _vm->_musicEngine->getSoundStatus(sound);
+
+ return 0;
+}
+
+/**
+ * Check whether the sound resource with the specified ID is still
+ * used. This is invoked by ScummEngine::isResourceInUse, to determine
+ * which resources can be expired from memory.
+ * Technically, this works very similar to isSoundRunning, however it
+ * calls IMuse::get_sound_active() instead of IMuse::getSoundStatus().
+ * The difference between those two is in how they treat sounds which
+ * are being faded out: get_sound_active() returns true even when the
+ * sound is being faded out, while getSoundStatus() returns false in
+ * that case.
+ */
+bool Sound::isSoundInUse(int sound) const {
+
+#ifndef DISABLE_SCUMM_7_8
+ if (_vm->_imuseDigital)
+ return (_vm->_imuseDigital->getSoundStatus(sound) != 0);
+#endif
+
+ if (sound == _currentCDSound)
+ return pollCD() != 0;
+
+ if (isSoundInQueue(sound))
+ return true;
+
+ if (!_vm->res.isResourceLoaded(rtSound, sound))
+ return false;
+
+ if (_vm->_imuse)
+ return _vm->_imuse->get_sound_active(sound);
+
+ if (_vm->_mixer->isSoundIDActive(sound))
+ return 1;
+
+ return false;
+}
+
+bool Sound::isSoundInQueue(int sound) const {
+ int i, num;
+
+ i = _soundQue2Pos;
+ while (i--) {
+ if (_soundQue2[i].sound == sound)
+ return true;
+ }
+
+ i = 0;
+ while (i < _soundQuePos) {
+ num = _soundQue[i++];
+
+ if (num > 0) {
+ if (_soundQue[i + 0] == 0x10F && _soundQue[i + 1] == 8 && _soundQue[i + 2] == sound)
+ return true;
+ i += num;
+ }
+ }
+ return false;
+}
+
+void Sound::stopSound(int sound) {
+ int i;
+
+ if (_vm->_heversion >= 70) {
+ if ( sound >= 10000) {
+ int chan = sound - 10000;
+ _vm->_mixer->stopHandle(_heSoundChannels[chan]);
+ _heChannel[chan].sound = 0;
+ _heChannel[chan].priority = 0;
+ _heChannel[chan].sbngBlock = 0;
+ _heChannel[chan].codeOffs = 0;
+ memset(_heChannel[chan].soundVars, 0, sizeof(_heChannel[chan].soundVars));
+ }
+ } else if (_vm->_heversion >= 60) {
+ if (sound == -2) {
+ } else if (sound == -1) {
+ // Stop current music
+ if (_currentMusic)
+ _vm->_mixer->stopID(_currentMusic);
+ else if (_vm->_imuse)
+ _vm->_imuse->stopSound(_vm->_imuse->getSoundStatus(-1));
+ }
+ }
+
+ if (sound != 0 && sound == _currentCDSound) {
+ _currentCDSound = 0;
+ stopCD();
+ stopCDTimer();
+ }
+
+ if (!(_vm->_features & GF_DIGI_IMUSE))
+ _vm->_mixer->stopID(sound);
+
+ if (_vm->_musicEngine)
+ _vm->_musicEngine->stopSound(sound);
+
+ for (i = 0; i < ARRAYSIZE(_heChannel); i++) {
+ if (_heChannel[i].sound == sound) {
+ _heChannel[i].sound = 0;
+ _heChannel[i].priority = 0;
+ _heChannel[i].sbngBlock = 0;
+ _heChannel[i].codeOffs = 0;
+ memset(_heChannel[i].soundVars, 0, sizeof(_heChannel[i].soundVars));
+ }
+ }
+
+ for (i = 0; i < ARRAYSIZE(_soundQue2); i++) {
+ if (_soundQue2[i].sound == sound) {
+ _soundQue2[i].sound = 0;
+ _soundQue2[i].offset = 0;
+ _soundQue2[i].channel = 0;
+ _soundQue2[i].flags = 0;
+ }
+ }
+}
+
+void Sound::stopAllSounds() {
+ if (_currentCDSound != 0) {
+ _currentCDSound = 0;
+ stopCD();
+ stopCDTimer();
+ }
+
+ // Clear sound channels for HE games
+ memset(_heChannel, 0, sizeof(_heChannel));
+
+ // Clear the (secondary) sound queue
+ _soundQue2Pos = 0;
+ memset(_soundQue2, 0, sizeof(_soundQue2));
+
+ if (_vm->_musicEngine) {
+ _vm->_musicEngine->stopAllSounds();
+ }
+ if (_vm->_imuse) {
+ // FIXME: Maybe we could merge this call to clear_queue()
+ // into IMuse::stopAllSounds() ?
+ _vm->_imuse->clear_queue();
+ }
+
+ // Stop all SFX
+ if (!_vm->_imuseDigital) {
+ _vm->_mixer->stopAll();
+ }
+}
+
+void Sound::soundKludge(int *list, int num) {
+ int i;
+
+#ifndef DISABLE_SCUMM_7_8
+ if (_vm->_imuseDigital) {
+ _vm->_imuseDigital->parseScriptCmds(list[0], list[1], list[2], list[3], list[4],
+ list[5], list[6], list[7]);
+ return;
+ }
+#endif
+
+ if (list[0] == -1) {
+ processSound();
+ } else {
+ _soundQue[_soundQuePos++] = num;
+
+ for (i = 0; i < num; i++) {
+ _soundQue[_soundQuePos++] = list[i];
+ }
+ }
+}
+
+void Sound::talkSound(uint32 a, uint32 b, int mode, int channel) {
+ if (_vm->_version >= 6 && ConfMan.getBool("speech_mute"))
+ return;
+
+ if (mode == 1) {
+ _talk_sound_a1 = a;
+ _talk_sound_b1 = b;
+ _talk_sound_channel = channel;
+ } else {
+ _talk_sound_a2 = a;
+ _talk_sound_b2 = b;
+ }
+
+ _talk_sound_mode |= mode;
+}
+
+/* The sound code currently only supports General Midi.
+ * General Midi is used in Day Of The Tentacle.
+ * Roland music is also playable, but doesn't sound well.
+ * A mapping between roland instruments and GM instruments
+ * is needed.
+ */
+
+void Sound::setupSound() {
+ delete _sfxFile;
+
+ _sfxFile = openSfxFile();
+
+ if (_vm->_heversion >= 70) {
+ setupHEMusicFile();
+ }
+
+ if (_vm->_gameId == GID_FT) {
+ _vm->VAR(_vm->VAR_VOICE_BUNDLE_LOADED) = _sfxFile->isOpen();
+ }
+}
+
+void Sound::pauseSounds(bool pause) {
+ if (_vm->_imuse)
+ _vm->_imuse->pause(pause);
+
+ // Don't pause sounds if the game isn't active
+ // FIXME - this is quite a nasty hack, replace with something cleaner, and w/o
+ // having to access member vars directly!
+ if (!_vm->_roomResource)
+ return;
+
+ _soundsPaused = pause;
+
+#ifndef DISABLE_SCUMM_7_8
+ if (_vm->_imuseDigital) {
+ _vm->_imuseDigital->pause(pause);
+ }
+#endif
+
+ _vm->_mixer->pauseAll(pause);
+
+ if ((_vm->_features & GF_AUDIOTRACKS) && _vm->VAR(_vm->VAR_MUSIC_TIMER) > 0) {
+ if (pause)
+ stopCDTimer();
+ else
+ startCDTimer();
+ }
+}
+
+ScummFile *Sound::openSfxFile() {
+ struct SoundFileExtensions {
+ const char *ext;
+ SoundMode mode;
+ };
+
+ static const SoundFileExtensions extensions[] = {
+ { "sou", kVOCMode },
+ #ifdef USE_FLAC
+ { "sof", kFlacMode },
+ #endif
+ #ifdef USE_VORBIS
+ { "sog", kVorbisMode },
+ #endif
+ #ifdef USE_MAD
+ { "so3", kMP3Mode },
+ #endif
+ { 0, kVOCMode }
+ };
+
+ char buf[256];
+ char buf1[128];
+ ScummFile *file = new ScummFile();
+ _offsetTable = NULL;
+
+ /* Try opening the file <baseName>.sou first, e.g. tentacle.sou.
+ * That way, you can keep .sou files for multiple games in the
+ * same directory */
+
+ const char *basename[4] = { 0, 0, 0, 0 };
+ basename[0] = _vm->getBaseName();
+ basename[1] = "monster";
+
+ if (_vm->_substResFileNameIndex > 0) {
+
+ strcpy(buf, basename[0]);
+ _vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
+ strcpy(buf, buf1);
+ basename[2] = buf1;
+ }
+
+ for (int j = 0; basename[j] && !file->isOpen(); ++j) {
+ for (int i = 0; extensions[i].ext; ++i) {
+ sprintf(buf, "%s.%s", basename[j], extensions[i].ext);
+ if (_vm->openFile(*file, buf)) {
+ _soundMode = extensions[i].mode;
+ break;
+ }
+ }
+ }
+
+ if (!file->isOpen()) {
+ if ((_vm->_heversion <= 61 && _vm->_platform == Common::kPlatformMacintosh) || (_vm->_heversion >= 70)) {
+ sprintf(buf, "%s.he2", _vm->getBaseName());
+ } else {
+ sprintf(buf, "%s.tlk", _vm->getBaseName());
+ }
+
+ if (_vm->_substResFileNameIndex > 0) {
+ _vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
+ strcpy(buf, buf1);
+ }
+ if (file->open(buf) && _vm->_heversion <= 73)
+ file->setEnc(0x69);
+ _soundMode = kVOCMode;
+ }
+
+ if (_soundMode != kVOCMode) {
+ /* Now load the 'offset' index in memory to be able to find the MP3 data
+
+ The format of the .SO3 file is easy :
+ - number of bytes of the 'index' part
+ - N times the following fields (4 bytes each) :
+ + offset in the original sound file
+ + offset of the MP3 data in the .SO3 file WITHOUT taking into account
+ the index field and the 'size' field
+ + the number of 'tags'
+ + the size of the MP3 data
+ - and then N times :
+ + the tags
+ + the MP3 data
+ */
+ int size, compressed_offset;
+ MP3OffsetTable *cur;
+ compressed_offset = file->readUint32BE();
+ _offsetTable = (MP3OffsetTable *) malloc(compressed_offset);
+ _numSoundEffects = compressed_offset / 16;
+
+ size = compressed_offset;
+ cur = _offsetTable;
+ while (size > 0) {
+ cur->org_offset = file->readUint32BE();
+ cur->new_offset = file->readUint32BE() + compressed_offset + 4; /* The + 4 is to take into accound the 'size' field */
+ cur->num_tags = file->readUint32BE();
+ cur->compressed_size = file->readUint32BE();
+ size -= 4 * 4;
+ cur++;
+ }
+ }
+
+ return file;
+}
+
+bool Sound::isSfxFinished() const {
+ return !_vm->_mixer->hasActiveChannelOfType(Audio::Mixer::kSFXSoundType);
+}
+
+// We use a real timer in an attempt to get better sync with CD tracks. This is
+// necessary for games like Loom CD.
+
+static void cd_timer_handler(void *refCon) {
+ ScummEngine *scumm = (ScummEngine *)refCon;
+
+ // FIXME: Turn off the timer when it's no longer needed. In theory, it
+ // should be possible to check with pollCD(), but since CD sound isn't
+ // properly restarted when reloading a saved game, I don't dare to.
+
+ scumm->VAR(scumm->VAR_MUSIC_TIMER) += 6;
+}
+
+void Sound::startCDTimer() {
+ int timer_interval;
+
+ // The timer interval has been tuned for Loom CD and the Monkey 1
+ // intro. I have to use 100 for Loom, or there will be a nasty stutter
+ // when Chaos first appears, and I have to use 101 for Monkey 1 or the
+ // intro music will be cut short.
+
+ if (_vm->_gameId == GID_LOOM && _vm->_version == 4)
+ timer_interval = 100;
+ else
+ timer_interval = 101;
+
+ _vm->_timer->removeTimerProc(&cd_timer_handler);
+ _vm->_timer->installTimerProc(&cd_timer_handler, 1000 * timer_interval, _vm);
+}
+
+void Sound::stopCDTimer() {
+ _vm->_timer->removeTimerProc(&cd_timer_handler);
+}
+
+void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) {
+ // Reset the music timer variable at the start of a new track
+ _vm->VAR(_vm->VAR_MUSIC_TIMER) = 0;
+
+ // Play it
+ if (!_soundsPaused)
+ AudioCD.play(track, numLoops, startFrame, duration);
+
+ // Start the timer after starting the track. Starting an MP3 track is
+ // almost instantaneous, but a CD player may take some time. Hopefully
+ // playCD() will block during that delay.
+ startCDTimer();
+}
+
+void Sound::stopCD() {
+ AudioCD.stop();
+}
+
+int Sound::pollCD() const {
+ return AudioCD.isPlaying();
+}
+
+void Sound::updateCD() {
+ AudioCD.updateCD();
+}
+
+void Sound::saveLoadWithSerializer(Serializer *ser) {
+ static const SaveLoadEntry soundEntries[] = {
+ MKLINE(Sound, _currentCDSound, sleInt16, VER(35)),
+ MKLINE(Sound, _currentMusic, sleInt16, VER(35)),
+ MKEND()
+ };
+
+ ser->saveLoadEntries(this, soundEntries);
+}
+
+
+#pragma mark -
+#pragma mark --- Sound resource handling ---
+#pragma mark -
+
+/*
+ * TODO: The way we handle sound/music resources really is one huge hack.
+ * We probably should reconsider how we do this, and maybe come up with a
+ * better/cleaner solution. Even if we keep the existing code, it really
+ * could stand a thorough cleanup!
+ */
+
+
+int ScummEngine::readSoundResource(int type, int idx) {
+ uint32 pos, total_size, size, tag, basetag, max_total_size;
+ int pri, best_pri;
+ uint32 best_size = 0, best_offs = 0;
+ byte *ptr;
+
+ debugC(DEBUG_RESOURCE, "readSoundResource(%d)", idx);
+
+ pos = 0;
+
+ _fileHandle->readUint32LE();
+ max_total_size = _fileHandle->readUint32BE() - 8;
+ basetag = fileReadDword();
+ total_size = _fileHandle->readUint32BE();
+
+ debugC(DEBUG_RESOURCE, " basetag: %s, total_size=%d", tag2str(TO_BE_32(basetag)), total_size);
+
+ switch (basetag) {
+ case MKID('MIDI'):
+ case MKID('iMUS'):
+ if (_musicType != MDT_PCSPK) {
+ _fileHandle->seek(-8, SEEK_CUR);
+ _fileHandle->read(res.createResource(type, idx, total_size + 8), total_size + 8);
+ return 1;
+ }
+ break;
+ case MKID('SOU '):
+ best_pri = -1;
+ while (pos < total_size) {
+ tag = fileReadDword();
+ size = _fileHandle->readUint32BE() + 8;
+ pos += size;
+
+ pri = -1;
+
+ switch (tag) {
+ case MKID('TOWS'):
+ pri = 16;
+ break;
+ case MKID('SBL '):
+ pri = 15;
+ break;
+ case MKID('ADL '):
+ pri = 1;
+ if (_musicType == MDT_ADLIB)
+ pri = 10;
+ break;
+ case MKID('AMI '):
+ pri = 3;
+ break;
+ case MKID('ROL '):
+ pri = 3;
+ if (_native_mt32)
+ pri = 5;
+ break;
+ case MKID('GMD '):
+ pri = 4;
+ break;
+ case MKID('MAC '): // Occurs in Mac MI2, FOA
+ pri = 2;
+ break;
+ case MKID('SPK '):
+ pri = -1;
+// if (_musicType == MDT_PCSPK)
+// pri = 11;
+ break;
+ }
+
+ if ((_musicType == MDT_PCSPK) && pri != 11)
+ pri = -1;
+
+ debugC(DEBUG_RESOURCE, " tag: %s, total_size=%d, pri=%d", tag2str(TO_BE_32(tag)), size, pri);
+
+
+ if (pri > best_pri) {
+ best_pri = pri;
+ best_size = size;
+ best_offs = _fileHandle->pos();
+ }
+
+ _fileHandle->seek(size - 8, SEEK_CUR);
+ }
+
+ if (best_pri != -1) {
+ _fileHandle->seek(best_offs - 8, SEEK_SET);
+ ptr = res.createResource(type, idx, best_size);
+ _fileHandle->read(ptr, best_size);
+ //dumpResource("sound-", idx, ptr);
+ return 1;
+ }
+ break;
+ case MKID('Mac0'):
+ _fileHandle->seek(-12, SEEK_CUR);
+ total_size = _fileHandle->readUint32BE() - 8;
+ ptr = (byte *)calloc(total_size, 1);
+ _fileHandle->read(ptr, total_size);
+ //dumpResource("sound-", idx, ptr);
+ convertMac0Resource(type, idx, ptr, total_size);
+ free(ptr);
+ return 1;
+
+ case MKID('Mac1'):
+ case MKID('RIFF'):
+ case MKID('TALK'):
+ case MKID('DIGI'):
+ case MKID('Crea'):
+ case MKID(0x460e200d): // WORKAROUND bug # 1311447
+ _fileHandle->seek(-12, SEEK_CUR);
+ total_size = _fileHandle->readUint32BE();
+ ptr = res.createResource(type, idx, total_size);
+ _fileHandle->read(ptr, total_size - 8);
+ //dumpResource("sound-", idx, ptr);
+ return 1;
+
+ case MKID('HSHD'):
+ // HE sound type without SOUN header
+ _fileHandle->seek(-16, SEEK_CUR);
+ total_size = max_total_size + 8;
+ ptr = res.createResource(type, idx, total_size);
+ _fileHandle->read(ptr, total_size);
+ //dumpResource("sound-", idx, ptr);
+ return 1;
+
+ case MKID('FMUS'): {
+ // Used in 3DO version of puttputt joins the parade and probably others
+ // Specifies a separate file to be used for music from what I gather.
+ int tmpsize;
+ Common::File dmuFile;
+ char buffer[128];
+ debugC(DEBUG_SOUND, "Found base tag FMUS in sound %d, size %d", idx, total_size);
+ debugC(DEBUG_SOUND, "It was at position %d", _fileHandle->pos());
+
+ _fileHandle->seek(4, SEEK_CUR);
+ // HSHD size
+ tmpsize = _fileHandle->readUint32BE();
+ // skip to size part of the SDAT block
+ _fileHandle->seek(tmpsize - 4, SEEK_CUR);
+ // SDAT size
+ tmpsize = _fileHandle->readUint32BE();
+
+ // SDAT contains name of file we want
+ _fileHandle->read(buffer, tmpsize - 8);
+ // files seem to be 11 chars (8.3) unused space is replaced by spaces
+ *(strstr(buffer, " ")) = '\0';
+
+ debugC(DEBUG_SOUND, "FMUS file %s", buffer);
+ if (dmuFile.open(buffer) == false) {
+ error("Can't open music file %s*", buffer);
+ res.roomoffs[type][idx] = 0xFFFFFFFF;
+ return 0;
+ }
+ dmuFile.seek(4, SEEK_SET);
+ total_size = dmuFile.readUint32BE();
+ debugC(DEBUG_SOUND, "dmu file size %d", total_size);
+ dmuFile.seek(-8, SEEK_CUR);
+ dmuFile.read(res.createResource(type, idx, total_size), total_size);
+ dmuFile.close();
+ }
+ return 1;
+
+ default:
+ if (FROM_LE_32(basetag) == max_total_size) {
+ _fileHandle->seek(-12, SEEK_CUR);
+ total_size = _fileHandle->readUint32BE();
+ _fileHandle->seek(-8, SEEK_CUR);
+ ptr = res.createResource(type, idx, total_size);
+ _fileHandle->read(ptr, total_size);
+ //dumpResource("sound-", idx, ptr);
+ return 1;
+ }
+ error("Unrecognized base tag 0x%08x in sound %d", TO_BE_32(basetag), idx);
+ }
+ res.roomoffs[type][idx] = 0xFFFFFFFF;
+ return 0;
+}
+
+// Adlib MIDI-SYSEX to set MIDI instruments for small header games.
+static byte ADLIB_INSTR_MIDI_HACK[95] = {
+ 0x00, 0xf0, 0x14, 0x7d, 0x00, // sysex 00: part on/off
+ 0x00, 0x00, 0x03, // part/channel (offset 5)
+ 0x00, 0x00, 0x07, 0x0f, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xf7,
+ 0x00, 0xf0, 0x41, 0x7d, 0x10, // sysex 16: set instrument
+ 0x00, 0x01, // part/channel (offset 28)
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf7,
+ 0x00, 0xb0, 0x07, 0x64 // Controller 7 = 100 (offset 92)
+};
+
+static const byte map_param[7] = {
+ 0, 2, 3, 4, 8, 9, 0,
+};
+
+static const byte freq2note[128] = {
+ /*128*/ 6, 6, 6, 6,
+ /*132*/ 7, 7, 7, 7, 7, 7, 7,
+ /*139*/ 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ /*148*/ 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ /*157*/ 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ /*166*/ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ /*176*/ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ /*186*/ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ /*197*/ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ /*209*/ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ /*222*/ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ /*235*/ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ /*249*/ 18, 18, 18, 18, 18, 18, 18
+};
+
+static const uint16 num_steps_table[] = {
+ 1, 2, 4, 5,
+ 6, 7, 8, 9,
+ 10, 12, 14, 16,
+ 18, 21, 24, 30,
+ 36, 50, 64, 82,
+ 100, 136, 160, 192,
+ 240, 276, 340, 460,
+ 600, 860, 1200, 1600
+};
+
+int ScummEngine::convert_extraflags(byte * ptr, byte * src_ptr) {
+ int flags = src_ptr[0];
+
+ int t1, t2, t3, t4, time;
+ int v1, v2, v3;
+
+ if (!(flags & 0x80))
+ return -1;
+
+ t1 = (src_ptr[1] & 0xf0) >> 3;
+ t2 = (src_ptr[2] & 0xf0) >> 3;
+ t3 = (src_ptr[3] & 0xf0) >> 3 | (flags & 0x40 ? 0x80 : 0);
+ t4 = (src_ptr[3] & 0x0f) << 1;
+ v1 = (src_ptr[1] & 0x0f);
+ v2 = (src_ptr[2] & 0x0f);
+ v3 = 31;
+ if ((flags & 0x7) == 0) {
+ v1 = v1 + 31 + 8;
+ v2 = v2 + 31 + 8;
+ } else {
+ v1 = v1 * 2 + 31;
+ v2 = v2 * 2 + 31;
+ }
+
+ /* flags a */
+ if ((flags & 0x7) == 6)
+ ptr[0] = 0;
+ else {
+ ptr[0] = (flags >> 4) & 0xb;
+ ptr[1] = map_param[flags & 0x7];
+ }
+
+ /* extra a */
+ ptr[2] = 0;
+ ptr[3] = 0;
+ ptr[4] = t1 >> 4;
+ ptr[5] = t1 & 0xf;
+ ptr[6] = v1 >> 4;
+ ptr[7] = v1 & 0xf;
+ ptr[8] = t2 >> 4;
+ ptr[9] = t2 & 0xf;
+ ptr[10] = v2 >> 4;
+ ptr[11] = v2 & 0xf;
+ ptr[12] = t3 >> 4;
+ ptr[13] = t3 & 0xf;
+ ptr[14] = t4 >> 4;
+ ptr[15] = t4 & 0xf;
+ ptr[16] = v3 >> 4;
+ ptr[17] = v3 & 0xf;
+
+ time = num_steps_table[t1] + num_steps_table[t2]
+ + num_steps_table[t3 & 0x7f] + num_steps_table[t4];
+ if (flags & 0x20) {
+ int playtime = ((src_ptr[4] >> 4) & 0xf) * 118 +
+ (src_ptr[4] & 0xf) * 8;
+ if (playtime > time)
+ time = playtime;
+ }
+ /*
+ time = ((src_ptr[4] >> 4) & 0xf) * 118 +
+ (src_ptr[4] & 0xf) * 8;
+ */
+ return time;
+}
+
+#define kMIDIHeaderSize 46
+static inline byte *writeMIDIHeader(byte *ptr, const char *type, int ppqn, int total_size) {
+ uint32 dw = TO_BE_32(total_size);
+
+ memcpy(ptr, type, 4); ptr += 4;
+ memcpy(ptr, &dw, 4); ptr += 4;
+ memcpy(ptr, "MDhd", 4); ptr += 4;
+ ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; ptr[3] = 8;
+ ptr += 4;
+ memset(ptr, 0, 8), ptr += 8;
+ memcpy(ptr, "MThd", 4); ptr += 4;
+ ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; ptr[3] = 6;
+ ptr += 4;
+ ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; ptr[3] = 1; // MIDI format 0 with 1 track
+ ptr += 4;
+
+ *ptr++ = ppqn >> 8;
+ *ptr++ = ppqn & 0xFF;
+
+ memcpy(ptr, "MTrk", 4); ptr += 4;
+ memcpy(ptr, &dw, 4); ptr += 4;
+
+ return ptr;
+}
+
+static inline byte *writeVLQ(byte *ptr, int value) {
+ if (value > 0x7f) {
+ if (value > 0x3fff) {
+ *ptr++ = (value >> 14) | 0x80;
+ value &= 0x3fff;
+ }
+ *ptr++ = (value >> 7) | 0x80;
+ value &= 0x7f;
+ }
+ *ptr++ = value;
+ return ptr;
+}
+
+static inline byte Mac0ToGMInstrument(uint32 type, int &transpose) {
+ transpose = 0;
+ switch (type) {
+ case MKID('MARI'): return 12;
+ case MKID('PLUC'): return 45;
+ case MKID('HARM'): return 22;
+ case MKID('PIPE'): return 19;
+ case MKID('TROM'): transpose = -12; return 57;
+ case MKID('STRI'): return 48;
+ case MKID('HORN'): return 60;
+ case MKID('VIBE'): return 11;
+ case MKID('SHAK'): return 77;
+ case MKID('PANP'): return 75;
+ case MKID('WHIS'): return 76;
+ case MKID('ORGA'): return 17;
+ case MKID('BONG'): return 115;
+ case MKID('BASS'): transpose = -24; return 35;
+ default:
+ error("Unknown Mac0 instrument %s found", tag2str(type));
+ }
+}
+
+void ScummEngine::convertMac0Resource(int type, int idx, byte *src_ptr, int size) {
+ /*
+ From Markus Magnuson (superqult) we got this information:
+ Mac0
+ ---
+ 4 bytes - 'SOUN'
+ BE 4 bytes - block length
+
+ 4 bytes - 'Mac0'
+ BE 4 bytes - (blockLength - 27)
+ 28 bytes - ???
+
+ do this three times (once for each channel):
+ 4 bytes - 'Chan'
+ BE 4 bytes - channel length
+ 4 bytes - instrument name (e.g. 'MARI')
+
+ do this for ((chanLength-24)/4) times:
+ 2 bytes - note duration
+ 1 byte - note value
+ 1 byte - note velocity
+
+ 4 bytes - ???
+ 4 bytes - 'Loop'/'Done'
+ 4 bytes - ???
+
+ 1 byte - 0x09
+ ---
+
+ Instruments (General Midi):
+ "MARI" - Marimba (12)
+ "PLUC" - Pizzicato Strings (45)
+ "HARM" - Harmonica (22)
+ "PIPE" - Church Organ? (19) or Flute? (73) or Bag Pipe (109)
+ "TROM" - Trombone (57)
+ "STRI" - String Ensemble (48 or 49)
+ "HORN" - French Horn? (60) or English Horn? (69)
+ "VIBE" - Vibraphone (11)
+ "SHAK" - Shakuhachi? (77)
+ "PANP" - Pan Flute (75)
+ "WHIS" - Whistle (78) / Bottle (76)
+ "ORGA" - Drawbar Organ (16; but could also be 17-20)
+ "BONG" - Woodblock? (115)
+ "BASS" - Bass (32-39)
+
+
+ Now the task could be to convert this into MIDI, to be fed into iMuse.
+ Or we do something similiar to what is done in Player_V3, assuming
+ we can identify SFX in the MI datafiles for each of the instruments
+ listed above.
+ */
+
+#if 0
+ byte *ptr = res.createResource(type, idx, size);
+ memcpy(ptr, src_ptr, size);
+#else
+ const int ppqn = 480;
+ byte *ptr, *start_ptr;
+
+ int total_size = 0;
+ total_size += kMIDIHeaderSize; // Header
+ total_size += 7; // Tempo META
+ total_size += 3 * 3; // Three program change mesages
+ total_size += 22; // Possible jump SysEx
+ total_size += 5; // EOT META
+
+ int i, len;
+ byte track_instr[3];
+ byte *track_data[3];
+ int track_len[3];
+ int track_transpose[3];
+ bool looped = false;
+
+ src_ptr += 8;
+ // TODO: Decipher the unknown bytes in the header. For now, skip 'em
+ src_ptr += 28;
+
+ // Parse the three channels
+ for (i = 0; i < 3; i++) {
+ assert(*((uint32*)src_ptr) == MKID('Chan'));
+ len = READ_BE_UINT32(src_ptr + 4);
+ track_len[i] = len - 24;
+ track_instr[i] = Mac0ToGMInstrument(*(uint32*)(src_ptr + 8), track_transpose[i]);
+ track_data[i] = src_ptr + 12;
+ src_ptr += len;
+ looped = (*((uint32*)(src_ptr - 8)) == MKID('Loop'));
+
+ // For each note event, we need up to 6 bytes for the
+ // Note On (3 VLQ, 3 event), and 6 bytes for the Note
+ // Off (3 VLQ, 3 event). So 12 bytes total.
+ total_size += 12 * track_len[i];
+ }
+ assert(*src_ptr == 0x09);
+
+ // Create sound resource
+ start_ptr = res.createResource(type, idx, total_size);
+
+ // Insert MIDI header
+ ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size);
+
+ // Write a tempo change Meta event
+ // 473 / 4 Hz, convert to micro seconds.
+ uint32 dw = 1000000 * 437 / 4 / ppqn; // 1000000 * ppqn * 4 / 473;
+ memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
+ *ptr++ = (byte)((dw >> 16) & 0xFF);
+ *ptr++ = (byte)((dw >> 8) & 0xFF);
+ *ptr++ = (byte)(dw & 0xFF);
+
+ // Insert program change messages
+ *ptr++ = 0; // VLQ
+ *ptr++ = 0xC0;
+ *ptr++ = track_instr[0];
+ *ptr++ = 0; // VLQ
+ *ptr++ = 0xC1;
+ *ptr++ = track_instr[1];
+ *ptr++ = 0; // VLQ
+ *ptr++ = 0xC2;
+ *ptr++ = track_instr[2];
+
+ // And now, the actual composition. Please turn all cell phones
+ // and pagers off during the performance. Thank you.
+ uint16 nextTime[3] = { 1, 1, 1 };
+ int stage[3] = { 0, 0, 0 };
+
+ while (track_len[0] | track_len[1] | track_len[2]) {
+ int best = -1;
+ uint16 bestTime = 0xFFFF;
+ for (i = 0; i < 3; ++i) {
+ if (track_len[i] && nextTime[i] < bestTime) {
+ bestTime = nextTime[i];
+ best = i;
+ }
+ }
+ assert (best != -1);
+
+ if (!stage[best]) {
+ // We are STARTING this event.
+ if (track_data[best][2] > 1) {
+ // Note On
+ ptr = writeVLQ(ptr, nextTime[best]);
+ *ptr++ = 0x90 | best;
+ *ptr++ = track_data[best][2] + track_transpose[best];
+ *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity
+ for (i = 0; i < 3; ++i)
+ nextTime[i] -= bestTime;
+ }
+ nextTime[best] += READ_BE_UINT16 (track_data[best]);
+ stage[best] = 1;
+ } else {
+ // We are ENDING this event.
+ if (track_data[best][2] > 1) {
+ // There was a Note On, so do a Note Off
+ ptr = writeVLQ(ptr, nextTime[best]);
+ *ptr++ = 0x80 | best;
+ *ptr++ = track_data[best][2] + track_transpose[best];
+ *ptr++ = track_data[best][3] * 127 / 100; // Scale velocity
+ for (i = 0; i < 3; ++i)
+ nextTime[i] -= bestTime;
+ }
+ track_data[best] += 4;
+ track_len[best] -= 4;
+ stage[best] = 0;
+ }
+ }
+
+ // Is this a looped song? If so, effect a loop by
+ // using the S&M maybe_jump SysEx command.
+ // FIXME: Jamieson630: The jump seems to be happening
+ // too quickly! There should maybe be a pause after
+ // the last Note Off? But I couldn't find one in the
+ // MI1 Lookout music, where I was hearing problems.
+ if (looped) {
+ memcpy(ptr, "\x00\xf0\x13\x7d\x30\00", 6); ptr += 6; // maybe_jump
+ memcpy(ptr, "\x00\x00", 2); ptr += 2; // cmd -> 0 means always jump
+ memcpy(ptr, "\x00\x00\x00\x00", 4); ptr += 4; // track -> 0 (only track)
+ memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // beat -> 1 (first beat)
+ memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // tick -> 1
+ memcpy(ptr, "\x00\xf7", 2); ptr += 2; // SysEx end marker
+ }
+
+ // Insert end of song META
+ memcpy(ptr, "\x00\xff\x2f\x00\x00", 5); ptr += 5;
+
+ assert(ptr <= start_ptr + total_size);
+
+ // Rewrite MIDI header, this time with true size
+ total_size = ptr - start_ptr;
+ ptr = writeMIDIHeader(start_ptr, "GMD ", ppqn, total_size);
+#endif
+}
+
+void ScummEngine::convertADResource(int type, int idx, byte *src_ptr, int size) {
+
+ // We will ignore the PPQN in the original resource, because
+ // it's invalid anyway. We use a constant PPQN of 480.
+ const int ppqn = 480;
+ uint32 dw;
+ int i, ch;
+ byte *ptr;
+ int total_size = kMIDIHeaderSize + 7 + 8 * sizeof(ADLIB_INSTR_MIDI_HACK) + size;
+ total_size += 24; // Up to 24 additional bytes are needed for the jump sysex
+
+ ptr = res.createResource(type, idx, total_size);
+
+ src_ptr += 2;
+ size -= 2;
+
+ // 0x80 marks a music resource. Otherwise it's a SFX
+ if (*src_ptr == 0x80) {
+ byte ticks, play_once;
+ byte num_instr;
+ byte *channel, *instr, *track;
+
+ ptr = writeMIDIHeader(ptr, "ADL ", ppqn, total_size);
+
+ // The "speed" of the song
+ ticks = *(src_ptr + 1);
+
+ // Flag that tells us whether we should loop the song (0) or play it only once (1)
+ play_once = *(src_ptr + 2);
+
+ // Number of instruments used
+ num_instr = *(src_ptr + 8); // Normally 8
+
+ // copy the pointer to instrument data
+ channel = src_ptr + 9;
+ instr = src_ptr + 0x11;
+
+ // skip over the rest of the header and copy the MIDI data into a buffer
+ src_ptr += 0x11 + 8 * 16;
+ size -= 0x11 + 8 * 16;
+
+ CHECK_HEAP
+
+ track = src_ptr;
+
+ // Convert the ticks into a MIDI tempo.
+ // Unfortunate LOOM and INDY3 have different interpretation
+ // of the ticks value.
+ if (_gameId == GID_INDY3) {
+ // Note: since we fix ppqn at 480, ppqn/473 is almost 1
+ dw = 500000 * 256 / 473 * ppqn / ticks;
+ } else if (_gameId == GID_LOOM && _version == 3) {
+ dw = 500000 * ppqn / 4 / ticks;
+ } else {
+ dw = 500000 * 256 / ticks;
+ }
+ debugC(DEBUG_SOUND, " ticks = %d, speed = %ld", ticks, dw);
+
+ // Write a tempo change Meta event
+ memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
+ *ptr++ = (byte)((dw >> 16) & 0xFF);
+ *ptr++ = (byte)((dw >> 8) & 0xFF);
+ *ptr++ = (byte)(dw & 0xFF);
+
+ // Copy our hardcoded instrument table into it
+ // Then, convert the instrument table as given in this song resource
+ // And write it *over* the hardcoded table.
+ // Note: we deliberately.
+
+ /* now fill in the instruments */
+ for (i = 0; i < num_instr; i++) {
+ ch = channel[i] - 1;
+ if (ch < 0 || ch > 15)
+ continue;
+
+ if (instr[i*16 + 13])
+ debugC(DEBUG_SOUND, "Sound %d instrument %d uses percussion", idx, i);
+
+ debugC(DEBUG_SOUND, "Sound %d: instrument %d on channel %d.", idx, i, ch);
+
+ memcpy(ptr, ADLIB_INSTR_MIDI_HACK, sizeof(ADLIB_INSTR_MIDI_HACK));
+
+ ptr[5] += ch;
+ ptr[28] += ch;
+ ptr[92] += ch;
+
+ /* flags_1 */
+ ptr[30 + 0] = (instr[i * 16 + 3] >> 4) & 0xf;
+ ptr[30 + 1] = instr[i * 16 + 3] & 0xf;
+
+ /* oplvl_1 */
+ ptr[30 + 2] = (instr[i * 16 + 4] >> 4) & 0xf;
+ ptr[30 + 3] = instr[i * 16 + 4] & 0xf;
+
+ /* atdec_1 */
+ ptr[30 + 4] = ((~instr[i * 16 + 5]) >> 4) & 0xf;
+ ptr[30 + 5] = (~instr[i * 16 + 5]) & 0xf;
+
+ /* sustrel_1 */
+ ptr[30 + 6] = ((~instr[i * 16 + 6]) >> 4) & 0xf;
+ ptr[30 + 7] = (~instr[i * 16 + 6]) & 0xf;
+
+ /* waveform_1 */
+ ptr[30 + 8] = (instr[i * 16 + 7] >> 4) & 0xf;
+ ptr[30 + 9] = instr[i * 16 + 7] & 0xf;
+
+ /* flags_2 */
+ ptr[30 + 10] = (instr[i * 16 + 8] >> 4) & 0xf;
+ ptr[30 + 11] = instr[i * 16 + 8] & 0xf;
+
+ /* oplvl_2 */
+ ptr[30 + 12] = (instr[i * 16 + 9] >> 4) & 0xf;
+ ptr[30 + 13] = instr[i * 16 + 9] & 0xf;
+
+ /* atdec_2 */
+ ptr[30 + 14] = ((~instr[i * 16 + 10]) >> 4) & 0xf;
+ ptr[30 + 15] = (~instr[i * 16 + 10]) & 0xf;
+
+ /* sustrel_2 */
+ ptr[30 + 16] = ((~instr[i * 16 + 11]) >> 4) & 0xf;
+ ptr[30 + 17] = (~instr[i * 16 + 11]) & 0xf;
+
+ /* waveform_2 */
+ ptr[30 + 18] = (instr[i * 16 + 12] >> 4) & 0xf;
+ ptr[30 + 19] = instr[i * 16 + 12] & 0xf;
+
+ /* feedback */
+ ptr[30 + 20] = (instr[i * 16 + 2] >> 4) & 0xf;
+ ptr[30 + 21] = instr[i * 16 + 2] & 0xf;
+ ptr += sizeof(ADLIB_INSTR_MIDI_HACK);
+ }
+
+ // There is a constant delay of ppqn/3 before the music starts.
+ if (ppqn / 3 >= 128)
+ *ptr++ = (ppqn / 3 >> 7) | 0x80;
+ *ptr++ = ppqn / 3 & 0x7f;
+
+ // Now copy the actual music data
+ memcpy(ptr, track, size);
+ ptr += size;
+
+ if (!play_once) {
+ // The song is meant to be looped. We achieve this by inserting just
+ // before the song end a jump to the song start. More precisely we abuse
+ // a S&M sysex, "maybe_jump" to achieve this effect. We could also
+ // use a set_loop sysex, but it's a bit longer, a little more complicated,
+ // and has no advantage either.
+
+ // First, find the track end
+ byte *end = ptr;
+ ptr -= size;
+ for (; ptr < end; ptr++) {
+ if (*ptr == 0xff && *(ptr + 1) == 0x2f)
+ break;
+ }
+ assert(ptr < end);
+
+ // Now insert the jump. The jump offset is measured in ticks.
+ // We have ppqn/3 ticks before the first note.
+
+ const int jump_offset = ppqn / 3;
+ memcpy(ptr, "\xf0\x13\x7d\x30\00", 5); ptr += 5; // maybe_jump
+ memcpy(ptr, "\x00\x00", 2); ptr += 2; // cmd -> 0 means always jump
+ memcpy(ptr, "\x00\x00\x00\x00", 4); ptr += 4; // track -> there is only one track, 0
+ memcpy(ptr, "\x00\x00\x00\x01", 4); ptr += 4; // beat -> for now, 1 (first beat)
+ // Ticks
+ *ptr++ = (byte)((jump_offset >> 12) & 0x0F);
+ *ptr++ = (byte)((jump_offset >> 8) & 0x0F);
+ *ptr++ = (byte)((jump_offset >> 4) & 0x0F);
+ *ptr++ = (byte)(jump_offset & 0x0F);
+ memcpy(ptr, "\x00\xf7", 2); ptr += 2; // sysex end marker
+ }
+ } else {
+
+ /* This is a sfx resource. First parse it quickly to find the parallel
+ * tracks.
+ */
+ ptr = writeMIDIHeader(ptr, "ASFX", ppqn, total_size);
+
+ byte current_instr[3][14];
+ int current_note[3];
+ int track_time[3];
+ byte *track_data[3];
+
+ int track_ctr = 0;
+ byte chunk_type = 0;
+ int delay, delay2, olddelay;
+
+ // Write a tempo change Meta event
+ // 473 / 4 Hz, convert to micro seconds.
+ dw = 1000000 * ppqn * 4 / 473;
+ memcpy(ptr, "\x00\xFF\x51\x03", 4); ptr += 4;
+ *ptr++ = (byte)((dw >> 16) & 0xFF);
+ *ptr++ = (byte)((dw >> 8) & 0xFF);
+ *ptr++ = (byte)(dw & 0xFF);
+
+ for (i = 0; i < 3; i++) {
+ track_time[i] = -1;
+ current_note[i] = -1;
+ }
+ while (size > 0) {
+ assert(track_ctr < 3);
+ track_data[track_ctr] = src_ptr;
+ track_time[track_ctr] = 0;
+ track_ctr++;
+ while (size > 0) {
+ chunk_type = *(src_ptr);
+ if (chunk_type == 1) {
+ src_ptr += 15;
+ size -= 15;
+ } else if (chunk_type == 2) {
+ src_ptr += 11;
+ size -= 11;
+ } else if (chunk_type == 0x80) {
+ src_ptr ++;
+ size --;
+ } else {
+ break;
+ }
+ }
+ if (chunk_type == 0xff)
+ break;
+ src_ptr++;
+ }
+
+ int curtime = 0;
+ for (;;) {
+ int mintime = -1;
+ ch = -1;
+ for (i = 0; i < 3; i++) {
+ if (track_time[i] >= 0 &&
+ (mintime == -1 || mintime > track_time[i])) {
+ mintime = track_time[i];
+ ch = i;
+ }
+ }
+ if (mintime < 0)
+ break;
+
+ src_ptr = track_data[ch];
+ chunk_type = *src_ptr;
+
+ if (current_note[ch] >= 0) {
+ delay = mintime - curtime;
+ curtime = mintime;
+ ptr = writeVLQ(ptr, delay);
+ *ptr++ = 0x80 + ch; // key off channel;
+ *ptr++ = current_note[ch];
+ *ptr++ = 0;
+ current_note[ch] = -1;
+ }
+
+ switch (chunk_type) {
+ case 1:
+ /* Instrument definition */
+ memcpy(current_instr[ch], src_ptr + 1, 14);
+ src_ptr += 15;
+ break;
+
+ case 2:
+ /* tone/parammodulation */
+ memcpy(ptr, ADLIB_INSTR_MIDI_HACK,
+ sizeof(ADLIB_INSTR_MIDI_HACK));
+
+ ptr[5] += ch;
+ ptr[28] += ch;
+ ptr[92] += ch;
+
+ /* flags_1 */
+ ptr[30 + 0] = (current_instr[ch][3] >> 4) & 0xf;
+ ptr[30 + 1] = current_instr[ch][3] & 0xf;
+
+ /* oplvl_1 */
+ ptr[30 + 2] = (current_instr[ch][4] >> 4) & 0xf;
+ ptr[30 + 3] = current_instr[ch][4] & 0xf;
+
+ /* atdec_1 */
+ ptr[30 + 4] = ((~current_instr[ch][5]) >> 4) & 0xf;
+ ptr[30 + 5] = (~current_instr[ch][5]) & 0xf;
+
+ /* sustrel_1 */
+ ptr[30 + 6] = ((~current_instr[ch][6]) >> 4) & 0xf;
+ ptr[30 + 7] = (~current_instr[ch][6]) & 0xf;
+
+ /* waveform_1 */
+ ptr[30 + 8] = (current_instr[ch][7] >> 4) & 0xf;
+ ptr[30 + 9] = current_instr[ch][7] & 0xf;
+
+ /* flags_2 */
+ ptr[30 + 10] = (current_instr[ch][8] >> 4) & 0xf;
+ ptr[30 + 11] = current_instr[ch][8] & 0xf;
+
+ /* oplvl_2 */
+ ptr[30 + 12] = ((current_instr[ch][9]) >> 4) & 0xf;
+ ptr[30 + 13] = (current_instr[ch][9]) & 0xf;
+
+ /* atdec_2 */
+ ptr[30 + 14] = ((~current_instr[ch][10]) >> 4) & 0xf;
+ ptr[30 + 15] = (~current_instr[ch][10]) & 0xf;
+
+ /* sustrel_2 */
+ ptr[30 + 16] = ((~current_instr[ch][11]) >> 4) & 0xf;
+ ptr[30 + 17] = (~current_instr[ch][11]) & 0xf;
+
+ /* waveform_2 */
+ ptr[30 + 18] = (current_instr[ch][12] >> 4) & 0xf;
+ ptr[30 + 19] = current_instr[ch][12] & 0xf;
+
+ /* feedback */
+ ptr[30 + 20] = (current_instr[ch][2] >> 4) & 0xf;
+ ptr[30 + 21] = current_instr[ch][2] & 0xf;
+
+ delay = mintime - curtime;
+ curtime = mintime;
+
+ {
+ delay = convert_extraflags(ptr + 30 + 22, src_ptr + 1);
+ delay2 = convert_extraflags(ptr + 30 + 40, src_ptr + 6);
+ debugC(DEBUG_SOUND, "delays: %d / %d", delay, delay2);
+ if (delay2 >= 0 && delay2 < delay)
+ delay = delay2;
+ if (delay == -1)
+ delay = 0;
+ }
+
+ /* duration */
+ ptr[30 + 58] = 0; // ((delay * 17 / 63) >> 4) & 0xf;
+ ptr[30 + 59] = 0; // (delay * 17 / 63) & 0xf;
+
+ ptr += sizeof(ADLIB_INSTR_MIDI_HACK);
+
+ olddelay = mintime - curtime;
+ curtime = mintime;
+ ptr = writeVLQ(ptr, olddelay);
+
+ {
+ int freq = ((current_instr[ch][1] & 3) << 8)
+ | current_instr[ch][0];
+ if (!freq)
+ freq = 0x80;
+ freq <<= ((current_instr[ch][1] >> 2) & 7) + 1;
+ int note = -11;
+ while (freq >= 0x100) {
+ note += 12;
+ freq >>= 1;
+ }
+ debugC(DEBUG_SOUND, "Freq: %d (%x) Note: %d", freq, freq, note);
+ if (freq < 0x80)
+ note = 0;
+ else
+ note += freq2note[freq - 0x80];
+
+ debugC(DEBUG_SOUND, "Note: %d", note);
+ if (note <= 0)
+ note = 1;
+ else if (note > 127)
+ note = 127;
+
+ // Insert a note on event
+ *ptr++ = 0x90 + ch; // key on channel
+ *ptr++ = note;
+ *ptr++ = 63;
+ current_note[ch] = note;
+ track_time[ch] = curtime + delay;
+ }
+ src_ptr += 11;
+ break;
+
+ case 0x80:
+ track_time[ch] = -1;
+ src_ptr ++;
+ break;
+
+ default:
+ track_time[ch] = -1;
+ }
+ track_data[ch] = src_ptr;
+ }
+ }
+
+ // Insert end of song sysex
+ memcpy(ptr, "\x00\xff\x2f\x00\x00", 5); ptr += 5;
+}
+
+
+int ScummEngine::readSoundResourceSmallHeader(int type, int idx) {
+ uint32 pos, total_size, size, tag;
+ uint32 ad_size = 0, ad_offs = 0;
+ uint32 ro_size = 0, ro_offs = 0;
+ uint32 wa_size = 0, wa_offs = 0;
+
+ debug(4, "readSoundResourceSmallHeader(%d)", idx);
+
+ if ((_gameId == GID_LOOM) && (_version == 3) && (_platform == Common::kPlatformPC) && VAR(VAR_SOUNDCARD) == 4) {
+ // Roland resources in Loom are tagless
+ // So we add an RO tag to allow imuse to detect format
+ byte *ptr, *src_ptr;
+ ro_offs = _fileHandle->pos();
+ ro_size = _fileHandle->readUint16LE();
+
+ src_ptr = (byte *) calloc(ro_size - 4, 1);
+ _fileHandle->seek(ro_offs + 4, SEEK_SET);
+ _fileHandle->read(src_ptr, ro_size -4);
+
+ ptr = res.createResource(type, idx, ro_size + 2);
+ memcpy(ptr, "RO", 2); ptr += 2;
+ memcpy(ptr, src_ptr, ro_size - 4); ptr += ro_size - 4;
+ return 1;
+ } else if (_features & GF_OLD_BUNDLE) {
+ wa_offs = _fileHandle->pos();
+ wa_size = _fileHandle->readUint16LE();
+ _fileHandle->seek(wa_size - 2, SEEK_CUR);
+
+ if (!(_platform == Common::kPlatformAtariST || _platform == Common::kPlatformMacintosh)) {
+ ad_offs = _fileHandle->pos();
+ ad_size = _fileHandle->readUint16LE();
+ }
+ _fileHandle->seek(4, SEEK_CUR);
+ total_size = wa_size + ad_size;
+ } else {
+ total_size = size = _fileHandle->readUint32LE();
+ tag = _fileHandle->readUint16LE();
+ debug(4, " tag='%c%c', size=%d", (char) (tag & 0xff),
+ (char) ((tag >> 8) & 0xff), size);
+
+ if (tag == 0x4F52) { // RO
+ ro_offs = _fileHandle->pos();
+ ro_size = size;
+ } else {
+ pos = 6;
+ while (pos < total_size) {
+ size = _fileHandle->readUint32LE();
+ tag = _fileHandle->readUint16LE();
+ debug(4, " tag='%c%c', size=%d", (char) (tag & 0xff),
+ (char) ((tag >> 8) & 0xff), size);
+ pos += size;
+
+ // MI1 and Indy3 uses one or more nested SO resources, which contains AD and WA
+ // resources.
+ if ((tag == 0x4441) && !(ad_offs)) { // AD
+ ad_size = size;
+ ad_offs = _fileHandle->pos();
+ } else if ((tag == 0x4157) && !(wa_offs)) { // WA
+ wa_size = size;
+ wa_offs = _fileHandle->pos();
+ } else { // other AD, WA and nested SO resources
+ if (tag == 0x4F53) { // SO
+ pos -= size;
+ size = 6;
+ pos += 6;
+ }
+ }
+ _fileHandle->seek(size - 6, SEEK_CUR);
+ }
+ }
+ }
+
+ if ((_musicType == MDT_ADLIB) && ad_offs != 0) {
+ // AD resources have a header, instrument definitions and one MIDI track.
+ // We build an 'ADL ' resource from that:
+ // 8 bytes resource header
+ // 16 bytes MDhd header
+ // 14 bytes MThd header
+ // 8 bytes MTrk header
+ // 7 bytes MIDI tempo sysex
+ // + some default instruments
+ byte *ptr;
+ if (_features & GF_OLD_BUNDLE) {
+ ad_size -= 4;
+ _fileHandle->seek(ad_offs + 4, SEEK_SET);
+ } else {
+ ad_size -= 6;
+ _fileHandle->seek(ad_offs, SEEK_SET);
+ }
+ ptr = (byte *) calloc(ad_size, 1);
+ _fileHandle->read(ptr, ad_size);
+ convertADResource(type, idx, ptr, ad_size);
+ free(ptr);
+ return 1;
+ } else if ((_musicType == MDT_PCSPK) && wa_offs != 0) {
+ if (_features & GF_OLD_BUNDLE) {
+ _fileHandle->seek(wa_offs, SEEK_SET);
+ _fileHandle->read(res.createResource(type, idx, wa_size), wa_size);
+ } else {
+ _fileHandle->seek(wa_offs - 6, SEEK_SET);
+ _fileHandle->read(res.createResource(type, idx, wa_size + 6), wa_size + 6);
+ }
+ return 1;
+ } else if (ro_offs != 0) {
+ _fileHandle->seek(ro_offs - 2, SEEK_SET);
+ _fileHandle->read(res.createResource(type, idx, ro_size - 4), ro_size - 4);
+ return 1;
+ }
+ res.roomoffs[type][idx] = 0xFFFFFFFF;
+ return 0;
+}
+
+
+#pragma mark -
+#pragma mark --- Appendable audio stream ---
+#pragma mark -
+
+
+/**
+ * Wrapped memory stream.
+ */
+template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
+class AppendableMemoryStream : public AppendableAudioStream {
+protected:
+ Common::Mutex _mutex;
+
+ byte *_bufferStart;
+ byte *_bufferEnd;
+ byte *_pos;
+ byte *_end;
+ bool _finalized;
+ const int _rate;
+
+ inline bool eosIntern() const { return _end == _pos; };
+public:
+ AppendableMemoryStream(int rate, uint bufferSize);
+ ~AppendableMemoryStream();
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const { return stereo; }
+ bool endOfStream() const { return _finalized && eosIntern(); }
+ bool endOfData() const { return eosIntern(); }
+
+ int getRate() const { return _rate; }
+
+ void append(const byte *data, uint32 len);
+ void finish() { _finalized = true; }
+};
+
+template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
+AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::AppendableMemoryStream(int rate, uint bufferSize)
+ : _finalized(false), _rate(rate) {
+
+ // Verify the buffer size is sane
+ if (is16Bit && stereo)
+ assert((bufferSize & 3) == 0);
+ else if (is16Bit || stereo)
+ assert((bufferSize & 1) == 0);
+
+ _bufferStart = (byte *)malloc(bufferSize);
+ _pos = _end = _bufferStart;
+ _bufferEnd = _bufferStart + bufferSize;
+}
+
+template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
+AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::~AppendableMemoryStream() {
+ free(_bufferStart);
+}
+
+template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
+int AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
+ Common::StackLock lock(_mutex);
+
+ int samples = 0;
+ while (samples < numSamples && !eosIntern()) {
+ // Wrap around?
+ if (_pos >= _bufferEnd)
+ _pos = _pos - (_bufferEnd - _bufferStart);
+
+ const byte *endMarker = (_pos > _end) ? _bufferEnd : _end;
+ const int len = MIN(numSamples, samples + (int)(endMarker - _pos) / (is16Bit ? 2 : 1));
+ while (samples < len) {
+ *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _pos, isLE);
+ _pos += (is16Bit ? 2 : 1);
+ samples++;
+ }
+ }
+
+ return samples;
+}
+
+template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
+void AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::append(const byte *data, uint32 len) {
+ Common::StackLock lock(_mutex);
+
+ // Verify the buffer size is sane
+ if (is16Bit && stereo)
+ assert((len & 3) == 0);
+ else if (is16Bit || stereo)
+ assert((len & 1) == 0);
+
+ // Verify that the stream has not yet been finalized (by a call to finish())
+ assert(!_finalized);
+
+ if (_end + len > _bufferEnd) {
+ // Wrap-around case
+ uint32 size_to_end_of_buffer = _bufferEnd - _end;
+ len -= size_to_end_of_buffer;
+ if ((_end < _pos) || (_bufferStart + len >= _pos)) {
+ debug(2, "AppendableMemoryStream: buffer overflow (A)");
+ return;
+ }
+ memcpy(_end, data, size_to_end_of_buffer);
+ memcpy(_bufferStart, data + size_to_end_of_buffer, len);
+ _end = _bufferStart + len;
+ } else {
+ if ((_end < _pos) && (_end + len >= _pos)) {
+ debug(2, "AppendableMemoryStream: buffer overflow (B)");
+ return;
+ }
+ memcpy(_end, data, len);
+ _end += len;
+ }
+}
+
+
+#define MAKE_WRAPPED(STEREO, UNSIGNED) \
+ if (is16Bit) { \
+ if (isLE) \
+ return new AppendableMemoryStream<STEREO, true, UNSIGNED, true>(rate, len); \
+ else \
+ return new AppendableMemoryStream<STEREO, true, UNSIGNED, false>(rate, len); \
+ } else \
+ return new AppendableMemoryStream<STEREO, false, UNSIGNED, false>(rate, len)
+
+AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags, uint32 len) {
+ const bool isStereo = (_flags & Audio::Mixer::FLAG_STEREO) != 0;
+ const bool is16Bit = (_flags & Audio::Mixer::FLAG_16BITS) != 0;
+ const bool isUnsigned = (_flags & Audio::Mixer::FLAG_UNSIGNED) != 0;
+ const bool isLE = (_flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0;
+
+ if (isStereo) {
+ if (isUnsigned) {
+ MAKE_WRAPPED(true, true);
+ } else {
+ MAKE_WRAPPED(true, false);
+ }
+ } else {
+ if (isUnsigned) {
+ MAKE_WRAPPED(false, true);
+ } else {
+ MAKE_WRAPPED(false, false);
+ }
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
new file mode 100644
index 0000000000..0b913fe7e3
--- /dev/null
+++ b/engines/scumm/sound.h
@@ -0,0 +1,185 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef SOUND_H
+#define SOUND_H
+
+#include "common/scummsys.h"
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+#include "scumm/saveload.h"
+
+namespace Common {
+ class File;
+}
+
+namespace Scumm {
+
+class ScummEngine;
+class ScummFile;
+
+struct MP3OffsetTable;
+struct SaveLoadEntry;
+
+enum {
+ kTalkSoundID = 10000
+};
+
+class Sound : public Serializable {
+#ifdef PALMOS_MODE
+public:
+#else
+protected:
+#endif
+ enum SoundMode {
+ kVOCMode,
+ kMP3Mode,
+ kVorbisMode,
+ kFlacMode
+ };
+
+#ifdef PALMOS_MODE
+protected:
+#endif
+ ScummEngine *_vm;
+
+ int16 _soundQuePos, _soundQue[0x100];
+ int16 _soundQue2Pos;
+
+ struct {
+ int16 sound;
+ int32 offset;
+ int16 channel;
+ int16 flags;
+ } _soundQue2[10];
+
+ ScummFile *_sfxFile;
+ SoundMode _soundMode;
+ MP3OffsetTable *_offsetTable; // For compressed audio
+ int _numSoundEffects; // For compressed audio
+
+ uint32 _talk_sound_a1, _talk_sound_a2, _talk_sound_b1, _talk_sound_b2;
+ byte _talk_sound_mode, _talk_sound_channel;
+ bool _mouthSyncMode;
+ bool _endOfMouthSync;
+ uint16 _mouthSyncTimes[64];
+ uint _curSoundPos;
+
+ int _overrideFreq;
+
+ int16 _currentCDSound;
+ int16 _currentMusic;
+
+ struct HEMusic{
+ int32 id;
+ int32 offset;
+ int32 size;
+ };
+ HEMusic *_heMusic;
+ int16 _heMusicTracks;
+
+public: // Used by createSound()
+ struct {
+ int sound;
+ int codeOffs;
+ int priority;
+ int sbngBlock;
+ int soundVars[27];
+ } _heChannel[8];
+
+public:
+ Audio::SoundHandle _talkChannelHandle; // Handle of mixer channel actor is talking on
+ Audio::SoundHandle _heSoundChannels[8];
+
+ bool _soundsPaused;
+ byte _sfxMode;
+
+public:
+ Sound(ScummEngine *parent);
+ ~Sound();
+ void addSoundToQueue(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0);
+ void addSoundToQueue2(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0);
+ void processSound();
+ void processSoundQueues();
+
+ void playSound(int soundID);
+ void startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle = NULL);
+ void stopTalkSound();
+ bool isMouthSyncOff(uint pos);
+ int isSoundRunning(int sound) const;
+ bool isSoundInUse(int sound) const;
+ void stopSound(int sound);
+ void stopAllSounds();
+ void soundKludge(int *list, int num);
+ void talkSound(uint32 a, uint32 b, int mode, int channel = 0);
+ void setupSound();
+ void pauseSounds(bool pause);
+
+ void startCDTimer();
+ void stopCDTimer();
+
+ void playCDTrack(int track, int numLoops, int startFrame, int duration);
+ void stopCD();
+ int pollCD() const;
+ void updateCD();
+ int getCurrentCDSound() const { return _currentCDSound; }
+
+ // HE specific
+ bool getHEMusicDetails(int id, int &musicOffs, int &musicSize);
+ int findFreeSoundChannel();
+ int isSoundCodeUsed(int sound);
+ int getSoundPos(int sound);
+ int getSoundVar(int sound, int var);
+ void setSoundVar(int sound, int var, int val);
+ void playHESound(int soundID, int heOffset, int heChannel, int heFlags);
+ void processSoundCode();
+ void processSoundOpcodes(int sound, byte *codePtr, int *soundVars);
+ void setOverrideFreq(int freq);
+ void setupHEMusicFile();
+ void startHETalkSound(uint32 offset);
+
+ // Used by the save/load system:
+ void saveLoadWithSerializer(Serializer *ser);
+
+protected:
+ ScummFile *openSfxFile();
+ bool isSfxFinished() const;
+ void processSfxQueues();
+
+ bool isSoundInQueue(int sound) const;
+};
+
+/**
+ * An audio stream to which additional data can be appended on-the-fly.
+ * Used by SMUSH and iMuseDigital.
+ */
+class AppendableAudioStream : public AudioStream {
+public:
+ virtual void append(const byte *data, uint32 len) = 0;
+ virtual void finish() = 0;
+};
+
+AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags, uint32 len);
+
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/sound_he.cpp b/engines/scumm/sound_he.cpp
new file mode 100644
index 0000000000..879e34a99e
--- /dev/null
+++ b/engines/scumm/sound_he.cpp
@@ -0,0 +1,516 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/actor.h"
+#include "scumm/imuse.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+
+#include "common/config-manager.h"
+#include "common/timer.h"
+#include "common/util.h"
+
+#include "sound/adpcm.h"
+#include "sound/audiocd.h"
+#include "sound/flac.h"
+#include "sound/mididrv.h"
+#include "sound/mixer.h"
+#include "sound/mp3.h"
+#include "sound/voc.h"
+#include "sound/vorbis.h"
+#include "sound/wave.h"
+
+namespace Scumm {
+
+int Sound::findFreeSoundChannel() {
+ int chan, min;
+
+ min = _vm->VAR(_vm->VAR_RESERVED_SOUND_CHANNELS);
+ if (min == 0) {
+ _vm->VAR(_vm->VAR_RESERVED_SOUND_CHANNELS) = 8;
+ return 1;
+ }
+
+ if (min < 8) {
+ for (chan = min; chan < ARRAYSIZE(_heChannel); chan++) {
+ if (_vm->_mixer->isSoundHandleActive(_heSoundChannels[chan]) == 0)
+ return chan;
+ }
+ } else {
+ return 1;
+ }
+
+ return min;
+}
+
+int Sound::isSoundCodeUsed(int sound) {
+ int chan = -1;
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
+ if (_heChannel[i].sound == sound)
+ chan = i;
+ }
+
+ if (chan != -1) {
+ return _heChannel[chan].sbngBlock;
+ } else {
+ return 0;
+ }
+}
+
+int Sound::getSoundPos(int sound) {
+ int chan = -1;
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
+ if (_heChannel[i].sound == sound)
+ chan = i;
+ }
+
+ if (chan != -1) {
+ int time = _vm->getHETimer(chan + 4) * 11025 / 1000;
+ return time;
+ } else {
+ return 0;
+ }
+}
+
+int Sound::getSoundVar(int sound, int var) {
+ if (_vm->_heversion >= 90 && var == 26) {
+ return isSoundCodeUsed(sound);
+ }
+
+ checkRange(25, 0, var, "Illegal sound variable %d");
+
+ int chan = -1;
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
+ if (_heChannel[i].sound == sound)
+ chan = i;
+ }
+
+ if (chan != -1) {
+ debug(5, "getSoundVar: sound %d var %d result %d", sound, var, _heChannel[chan].soundVars[var]);
+ return _heChannel[chan].soundVars[var];
+ } else {
+ return 0;
+ }
+}
+
+void Sound::setSoundVar(int sound, int var, int val) {
+ checkRange(25, 0, var, "Illegal sound variable %d");
+
+ int chan = -1;
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
+ if (_heChannel[i].sound == sound)
+ chan = i;
+ }
+
+ if (chan != -1) {
+ debug(5, "setSoundVar: sound %d var %d val %d", sound, var, val);
+ _heChannel[chan].soundVars[var] = val;
+ }
+}
+
+void Sound::setOverrideFreq(int freq) {
+ _overrideFreq = freq;
+}
+
+void Sound::setupHEMusicFile() {
+ int i, total_size;
+ char buf[32], buf1[128];
+ Common::File musicFile;
+
+ sprintf(buf, "%s.he4", _vm->getBaseName());
+
+ if (_vm->_substResFileNameIndex > 0) {
+ _vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
+ strcpy(buf, buf1);
+ }
+ if (musicFile.open(buf) == true) {
+ musicFile.seek(4, SEEK_SET);
+ total_size = musicFile.readUint32BE();
+ musicFile.seek(16, SEEK_SET);
+ _heMusicTracks = musicFile.readUint32LE();
+ debug(5, "Total music tracks %d", _heMusicTracks);
+
+ int musicStart = (_vm->_heversion >= 80) ? 56 : 20;
+ musicFile.seek(musicStart, SEEK_SET);
+
+ _heMusic = (HEMusic *)malloc((_heMusicTracks + 1) * sizeof(HEMusic));
+ for (i = 0; i < _heMusicTracks; i++) {
+ _heMusic[i].id = musicFile.readUint32LE();
+ _heMusic[i].offset = musicFile.readUint32LE();
+ _heMusic[i].size = musicFile.readUint32LE();
+
+ if (_vm->_heversion >= 80) {
+ musicFile.seek(+9, SEEK_CUR);
+ } else {
+ musicFile.seek(+13, SEEK_CUR);
+ }
+ }
+
+ musicFile.close();
+ }
+}
+
+bool Sound::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
+ int i;
+
+ for (i = 0; i < _heMusicTracks; i++) {
+ if (_heMusic[i].id == id) {
+ musicOffs = _heMusic[i].offset;
+ musicSize = _heMusic[i].size;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void Sound::processSoundCode() {
+ byte *codePtr;
+ int chan, tmr, size, time;
+
+ for (chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
+ if (_heChannel[chan].sound == 0) {
+ continue;
+ }
+
+ if (_heChannel[chan].codeOffs == -1) {
+ continue;
+ }
+
+ tmr = _vm->getHETimer(chan + 4) * 11025 / 1000;
+ tmr += _vm->VAR(_vm->VAR_SOUNDCODE_TMR);
+ if (tmr < 0)
+ tmr = 0;
+
+ if (_heChannel[chan].sound > _vm->_numSounds) {
+ codePtr = _vm->getResourceAddress(rtSpoolBuffer, chan);
+ } else {
+ codePtr = _vm->getResourceAddress(rtSound, _heChannel[chan].sound);
+ }
+ assert(codePtr);
+ codePtr += _heChannel[chan].codeOffs;
+
+ while(1) {
+ size = READ_LE_UINT16(codePtr);
+ time = READ_LE_UINT32(codePtr + 2);
+
+ if (size == 0) {
+ _heChannel[chan].codeOffs = -1;
+ break;
+ }
+
+ debug(5, "Channel %d Timer %d Time %d", chan, tmr, time);
+ if (time >= tmr)
+ break;
+
+ processSoundOpcodes(_heChannel[chan].sound, codePtr + 6, _heChannel[chan].soundVars);
+
+ codePtr += size;
+ _heChannel[chan].codeOffs += size;
+ }
+ }
+}
+
+void Sound::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
+ int arg, opcode, var, val;
+
+ while(READ_LE_UINT16(codePtr) != 0) {
+ codePtr += 2;
+ opcode = READ_LE_UINT16(codePtr); codePtr += 2;
+ opcode = (opcode & 0xFFF) >> 4;
+ arg = opcode & 3;
+ opcode &= ~3;
+ debug(5, "processSoundOpcodes: sound %d opcode %d", sound, opcode);
+ switch (opcode) {
+ case 0: // Continue
+ break;
+ case 16: // Set talk state
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ setSoundVar(sound, 19, val);
+ break;
+ case 32: // Set var
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ setSoundVar(sound, var, val);
+ break;
+ case 48: // Add
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ val = getSoundVar(sound, var) + val;
+ setSoundVar(sound, var, val);
+ break;
+ case 56: // Subtract
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ val = getSoundVar(sound, var) - val;
+ setSoundVar(sound, var, val);
+ break;
+ case 64: // Multiple
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ val = getSoundVar(sound, var) * val;
+ setSoundVar(sound, var, val);
+ break;
+ case 80: // Divide
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ val = getSoundVar(sound, var) / val;
+ setSoundVar(sound, var, val);
+ break;
+ case 96: // Increment
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = getSoundVar(sound, var) + 1;
+ setSoundVar(sound, var, val);
+ break;
+ case 104: // Decrement
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = getSoundVar(sound, var) - 1;
+ setSoundVar(sound, var, val);
+ break;
+ default:
+ error("Illegal sound %d opcode %d", sound, opcode);
+ }
+ }
+}
+
+void Sound::playHESound(int soundID, int heOffset, int heChannel, int heFlags) {
+ byte *ptr, *spoolPtr;
+ char *sound;
+ int size = -1;
+ int priority, rate;
+ byte flags = Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE;
+
+ if (heChannel == -1)
+ heChannel = (_vm->VAR_RESERVED_SOUND_CHANNELS != 0xFF) ? findFreeSoundChannel() : 1;
+
+ debug(5,"playHESound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
+
+ if (soundID >= 10000) {
+ // Special codes, used in pjgames
+ return;
+ }
+
+ if (soundID > _vm->_numSounds) {
+ int music_offs;
+ char buf[32], buf1[128];
+ Common::File musicFile;
+
+ sprintf(buf, "%s.he4", _vm->getBaseName());
+
+ if (_vm->_substResFileNameIndex > 0) {
+ _vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
+ strcpy(buf, buf1);
+ }
+ if (musicFile.open(buf) == false) {
+ warning("playSound: Can't open music file %s", buf);
+ return;
+ }
+ if (!getHEMusicDetails(soundID, music_offs, size)) {
+ debug(0, "playSound: musicID %d not found", soundID);
+ return;
+ }
+
+ musicFile.seek(music_offs, SEEK_SET);
+
+ if (_vm->_heversion == 70) {
+ spoolPtr = (byte *)malloc(size);
+ musicFile.read(spoolPtr, size);
+ } else {
+ spoolPtr = _vm->res.createResource(rtSpoolBuffer, heChannel, size);
+ assert(spoolPtr);
+ musicFile.read(spoolPtr, size);
+ }
+ musicFile.close();
+
+ if (_vm->_heversion == 70) {
+ _vm->_mixer->stopHandle(_heSoundChannels[heChannel]);
+ _vm->_mixer->playRaw(&_heSoundChannels[heChannel], spoolPtr, size, 11025, flags, soundID);
+ return;
+ }
+ }
+
+ if (soundID > _vm->_numSounds) {
+ ptr = _vm->getResourceAddress(rtSpoolBuffer, heChannel);
+ } else {
+ ptr = _vm->getResourceAddress(rtSound, soundID);
+ }
+
+ if (!ptr) {
+ return;
+ }
+
+ // TODO: Extra sound flags
+ if (heFlags & 1) {
+ flags |= Audio::Mixer::FLAG_LOOP;
+ }
+
+ // Support for sound in later Backyard sports games
+ if (READ_UINT32(ptr) == MKID('RIFF') || READ_UINT32(ptr) == MKID('WSOU')) {
+ uint16 type;
+ int blockAlign;
+
+ if (READ_UINT32(ptr) == MKID('WSOU'))
+ ptr += 8;
+
+ size = READ_LE_UINT32(ptr + 4);
+ Common::MemoryReadStream stream(ptr, size);
+
+ if (!loadWAVFromStream(stream, size, rate, flags, &type, &blockAlign)) {
+ error("playSound: Not a valid WAV file");
+ }
+
+ if (type == 17) {
+ AudioStream *voxStream = new ADPCMInputStream(&stream, size, kADPCMIma, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
+
+ sound = (char *)malloc(size * 4);
+ size = voxStream->readBuffer((int16*)sound, size * 2);
+ size *= 2; // 16bits.
+ delete voxStream;
+ } else {
+ // Allocate a sound buffer, copy the data into it, and play
+ sound = (char *)malloc(size);
+ memcpy(sound, ptr + stream.pos(), size);
+ }
+ _vm->_mixer->stopHandle(_heSoundChannels[heChannel]);
+ _vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
+ }
+ // Support for sound in Humongous Entertainment games
+ else if (READ_UINT32(ptr) == MKID('DIGI') || READ_UINT32(ptr) == MKID('TALK')) {
+ byte *sndPtr = ptr;
+
+ priority = (soundID > _vm->_numSounds) ? 255 : *(ptr + 18);
+ rate = READ_LE_UINT16(ptr + 22);
+ ptr += 8 + READ_BE_UINT32(ptr + 12);
+
+ if (_vm->_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) {
+ int curSnd = _heChannel[heChannel].sound;
+ if (curSnd == 1 && soundID != 1)
+ return;
+ if (curSnd != 0 && curSnd != 1 && soundID != 1 && _heChannel[heChannel].priority > priority)
+ return;
+ }
+
+ int codeOffs = -1;
+ if (READ_UINT32(ptr) == MKID('SBNG')) {
+ codeOffs = ptr - sndPtr + 8;
+ ptr += READ_BE_UINT32(ptr + 4);
+ }
+
+ assert(READ_UINT32(ptr) == MKID('SDAT'));
+ size = READ_BE_UINT32(ptr+4) - 8;
+ if (heOffset < 0 || heOffset > size) {
+ // Occurs when making fireworks in puttmoon
+ debug(0, "playSound: Invalid sound offset (offset %d, size %d) in sound %d", heOffset, size, soundID);
+ heOffset = 0;
+ }
+ size -= heOffset;
+
+ if (_overrideFreq) {
+ // Used by the piano in Fatty Bear's Birthday Surprise
+ rate = _overrideFreq;
+ _overrideFreq = 0;
+ }
+
+ // Allocate a sound buffer, copy the data into it, and play
+ sound = (char *)malloc(size);
+ memcpy(sound, ptr + heOffset + 8, size);
+ _vm->_mixer->stopHandle(_heSoundChannels[heChannel]);
+ _vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
+
+ _vm->setHETimer(heChannel + 4);
+ _heChannel[heChannel].sound = soundID;
+ _heChannel[heChannel].priority = priority;
+ _heChannel[heChannel].sbngBlock = (codeOffs != -1) ? 1 : 0;
+ _heChannel[heChannel].codeOffs = codeOffs;
+ memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars));
+ }
+ // Support for PCM music in 3DO versions of Humongous Entertainment games
+ else if (READ_UINT32(ptr) == MKID('MRAW')) {
+ priority = *(ptr + 18);
+ rate = READ_LE_UINT16(ptr + 22);
+ ptr += 8 + READ_BE_UINT32(ptr+12);
+
+ assert(READ_UINT32(ptr) == MKID('SDAT'));
+ size = READ_BE_UINT32(ptr+4) - 8;
+
+ flags = Audio::Mixer::FLAG_AUTOFREE;
+
+ // Allocate a sound buffer, copy the data into it, and play
+ sound = (char *)malloc(size);
+ memcpy(sound, ptr + 8, size);
+ _vm->_mixer->stopID(_currentMusic);
+ _currentMusic = soundID;
+ _vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
+ }
+ else if (READ_UINT32(ptr) == MKID('MIDI')) {
+ if (_vm->_musicEngine) {
+ _vm->_musicEngine->startSound(soundID);
+ }
+ }
+}
+
+void Sound::startHETalkSound(uint32 offset) {
+ byte *ptr;
+ int32 size;
+
+ if (ConfMan.getBool("speech_mute"))
+ return;
+
+ if (!_sfxFile->isOpen()) {
+ error("startHETalkSound: Speech file is not open");
+ return;
+ }
+
+ _sfxMode |= 2;
+ _vm->res.nukeResource(rtSound, 1);
+
+ _sfxFile->seek(offset + 4, SEEK_SET);
+ size = _sfxFile->readUint32BE();
+ _sfxFile->seek(offset, SEEK_SET);
+
+ _vm->res.createResource(rtSound, 1, size);
+ ptr = _vm->getResourceAddress(rtSound, 1);
+ _sfxFile->read(ptr, size);
+
+ int channel = (_vm->VAR_TALK_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_TALK_CHANNEL) : 0;
+ addSoundToQueue2(1, 0, channel, 0);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/sprite_he.cpp b/engines/scumm/sprite_he.cpp
new file mode 100644
index 0000000000..a6065eb7be
--- /dev/null
+++ b/engines/scumm/sprite_he.cpp
@@ -0,0 +1,1440 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+#include "scumm/sprite_he.h"
+#include "scumm/usage_bits.h"
+#include "scumm/util.h"
+#include "scumm/wiz_he.h"
+
+namespace Scumm {
+
+Sprite::Sprite(ScummEngine_v90he *vm) : _vm(vm) {
+}
+
+Sprite::~Sprite() {
+ free(_spriteGroups);
+ free(_spriteTable);
+ free(_activeSpritesTable);
+}
+
+void ScummEngine_v90he::allocateArrays() {
+ ScummEngine::allocateArrays();
+ _sprite->allocTables(_numSprites, MAX(64, _numSprites / 4), 64);
+}
+
+void Sprite::getSpriteBounds(int spriteId, bool checkGroup, Common::Rect &bound) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ int32 spr_wiz_x, spr_wiz_y;
+ int angle, scale, x1, y1;
+ int32 w, h;
+
+ SpriteInfo *spi = &_spriteTable[spriteId];
+
+ _vm->_wiz->getWizImageSpot(spi->image, spi->imageState, spr_wiz_x, spr_wiz_y);
+ if (checkGroup && spi->group) {
+ SpriteGroup *spg = &_spriteGroups[spi->group];
+
+ if (spg->scaling) {
+ x1 = spi->tx * spg->scale_x_ratio_mul / spg->scale_x_ratio_div - spr_wiz_x + spg->tx;
+ y1 = spi->ty * spg->scale_y_ratio_mul / spg->scale_y_ratio_div - spr_wiz_y + spg->ty;
+ } else {
+ x1 = spi->tx - spr_wiz_x + spg->tx;
+ y1 = spi->ty - spr_wiz_y + spg->ty;
+ }
+ } else {
+ x1 = spi->tx - spr_wiz_x;
+ y1 = spi->ty - spr_wiz_y;
+ }
+
+ if (spi->image) {
+ angle = spi->angle;
+ scale = spi->scale;
+ _vm->_wiz->getWizImageDim(spi->image, spi->imageState, w, h);
+ if (spi->flags & (kSFScaled | kSFRotated)) {
+ Common::Point pts[4];
+ _vm->_wiz->polygonTransform(spi->image, spi->imageState, x1, y1, angle, scale, pts);
+ _vm->_wiz->polygonCalcBoundBox(pts, 4, bound);
+ } else {
+ bound.left = x1;
+ bound.top = y1;
+ bound.right = x1 + w;
+ bound.bottom = y1 + h;
+ }
+ } else {
+ bound.left = 1234;
+ bound.top = 1234;
+ bound.right = -1234;
+ bound.bottom = -1234;
+ }
+}
+
+//
+// spriteInfoGet functions
+//
+int Sprite::findSpriteWithClassOf(int x_pos, int y_pos, int spriteGroupId, int type, int num, int *args) {
+ debug(2, "findSprite: x %d, y %d, spriteGroup %d, type %d, num %d", x_pos, y_pos, spriteGroupId, type, num);
+ Common::Point pos[1];
+ bool cond;
+ int code, classId;
+
+ for (int i = (_numSpritesToProcess - 1); i >= 0; i--) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (!spi->curImage)
+ continue;
+
+ if (spriteGroupId && spi->group != spriteGroupId)
+ continue;
+
+ cond = true;
+ for (int j = 0; j < num; j++) {
+ code = classId = args[j];
+ classId &= 0x7F;
+ checkRange(32, 1, classId, "class %d out of range in statement");
+ if (code & 0x80) {
+ if (!(spi->classFlags & (1 << (classId - 1))))
+ cond = 0;
+ } else {
+ if ((spi->classFlags & (1 << (classId - 1))))
+ cond = 0;
+ }
+ }
+ if (!cond)
+ continue;
+
+ if (type) {
+ if (spi->bbox.left > spi->bbox.right)
+ continue;
+ if (spi->bbox.top > spi->bbox.bottom)
+ continue;
+ if (spi->bbox.left > x_pos)
+ continue;
+ if (spi->bbox.top > y_pos)
+ continue;
+ if (spi->bbox.right < x_pos)
+ continue;
+ if (spi->bbox.bottom < y_pos)
+ continue;
+ return spi->id;
+ } else {
+ int image, imageState, angle, scale;
+ int32 w, h;
+
+ image = spi->curImage;
+ if (spi->maskImage) {
+ int32 x1, x2, y1, y2;
+
+ imageState = spi->curImageState % _vm->_wiz->getWizImageStates(spi->maskImage);
+
+ pos[0].x = x_pos - spi->pos.x;
+ pos[0].y = y_pos - spi->pos.y;
+
+ _vm->_wiz->getWizImageSpot(spi->curImage, imageState, x1, y1);
+ _vm->_wiz->getWizImageSpot(spi->maskImage, imageState, x2, y2);
+
+ pos[0].x += (x2 - x1);
+ pos[0].y += (y2 - y1);
+ } else {
+ if (spi->bbox.left > spi->bbox.right)
+ continue;
+ if (spi->bbox.top > spi->bbox.bottom)
+ continue;
+ if (spi->bbox.left > x_pos)
+ continue;
+ if (spi->bbox.top > y_pos)
+ continue;
+ if (spi->bbox.right < x_pos)
+ continue;
+ if (spi->bbox.bottom < y_pos)
+ continue;
+
+ pos[0].x = x_pos - spi->pos.x;
+ pos[0].y = y_pos - spi->pos.y;
+ imageState = spi->curImageState;
+ }
+
+ angle = spi->curAngle;
+ scale = spi->curScale;
+ if ((spi->flags & kSFScaled) || (spi->flags & kSFRotated)) {
+ if (spi->flags & kSFScaled && scale) {
+ pos[0].x = pos[0].x * 256 / scale;
+ pos[0].y = pos[0].y * 256 / scale;
+ }
+ if (spi->flags & kSFRotated && angle) {
+ angle = (360 - angle) % 360;
+ _vm->_wiz->polygonRotatePoints(pos, 1, angle);
+ }
+
+ _vm->_wiz->getWizImageDim(image, imageState, w, h);
+ pos[0].x += w / 2;
+ pos[0].y += h / 2;
+ }
+
+ if (_vm->_wiz->isWizPixelNonTransparent(image, imageState, pos[0].x, pos[0].y, spi->curImgFlags))
+ return spi->id;
+ }
+ }
+
+ return 0;
+}
+
+int Sprite::getSpriteClass(int spriteId, int num, int *args) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ int code, classId;
+
+ if (num == 0)
+ return _spriteTable[spriteId].classFlags;
+
+ for (int i = 0; i < num; i++) {
+ code = classId = args[i];
+ classId &= 0x7F;
+ checkRange(32, 1, classId, "class %d out of range in statement");
+ if (code & 0x80) {
+ if (!(_spriteTable[spriteId].classFlags & (1 << (classId - 1))))
+ return 0;
+ } else {
+ if ((_spriteTable[spriteId].classFlags & (1 << (classId - 1))))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int Sprite::getSpriteFlagDoubleBuffered(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFDoubleBuffered) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagYFlipped(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFYFlipped) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagXFlipped(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFXFlipped) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagActive(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFActive) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagRemapPalette(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFRemapPalette) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagAutoAnim(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFAutoAnim) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagUpdateType(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFMarkDirty) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagEraseType(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFImageless) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteImage(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].image;
+}
+
+int Sprite::getSpriteImageState(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].imageState;
+}
+
+int Sprite::getSpriteGroup(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].group;
+}
+
+int Sprite::getSpritePalette(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].palette;
+}
+
+int Sprite::getSpritePriority(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].priority;
+}
+
+int Sprite::getSpriteDisplayX(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].group)
+ return _spriteTable[spriteId].tx + _spriteGroups[_spriteTable[spriteId].group].tx;
+ else
+ return _spriteTable[spriteId].tx;
+}
+
+int Sprite::getSpriteDisplayY(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].group)
+ return _spriteTable[spriteId].ty + _spriteGroups[_spriteTable[spriteId].group].ty;
+ else
+ return _spriteTable[spriteId].ty;
+}
+
+int Sprite::getSpriteUserValue(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].userValue;
+}
+
+int Sprite::getSpriteShadow(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].shadow;
+}
+
+int Sprite::getSpriteImageStateCount(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].imageStateCount;
+}
+
+int Sprite::getSpriteScale(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].scale;
+}
+
+int Sprite::getSpriteAnimSpeed(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].animSpeed;
+}
+
+int Sprite::getSpriteSourceImage(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].sourceImage;
+}
+
+int Sprite::getSpriteMaskImage(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].maskImage;
+}
+
+int Sprite::getSpriteGeneralProperty(int spriteId, int type) {
+ debug(0, "getSpriteGeneralProperty: spriteId %d type 0x%x", spriteId, type);
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ // XXX U32 related check
+
+ switch(type) {
+ case 0x7B:
+ return _spriteTable[spriteId].imgFlags;
+ case 0x7D:
+ return _spriteTable[spriteId].field_90;
+ case 0x7E:
+ return _spriteTable[spriteId].animProgress;
+ default:
+ error("getSpriteGeneralProperty: Invalid type %d", type);
+ }
+}
+
+void Sprite::getSpriteImageDim(int spriteId, int32 &w, int32 &h) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].image) {
+ _vm->_wiz->getWizImageDim(_spriteTable[spriteId].image, _spriteTable[spriteId].imageState, w, h);
+ } else {
+ w = 0;
+ h = 0;
+ }
+}
+
+void Sprite::getSpritePosition(int spriteId, int32 &tx, int32 &ty) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ tx = _spriteTable[spriteId].tx;
+ ty = _spriteTable[spriteId].ty;
+}
+
+void Sprite::getSpriteDist(int spriteId, int32 &dx, int32 &dy) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ dx = _spriteTable[spriteId].dx;
+ dy = _spriteTable[spriteId].dy;
+}
+
+//
+// spriteGroupGet functions
+//
+int ScummEngine_v90he::getGroupSpriteArray(int spriteGroupId) {
+ int i, numSprites = 0;
+
+ checkRange(_sprite->_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (i = (_sprite->_varNumSprites - 1); i > 0; i--) {
+ if (_sprite->_spriteTable[i].group == spriteGroupId)
+ numSprites++;
+ }
+
+ if (!numSprites)
+ return 0;
+
+ writeVar(0, 0);
+ defineArray(0, kDwordArray, 0, 0, 0, numSprites);
+ writeArray(0, 0, 0, numSprites);
+
+ numSprites = 1;
+ for (i = (_sprite->_varNumSprites - 1); i > 0; i--) {
+ if (_sprite->_spriteTable[i].group == spriteGroupId) {
+ writeArray(0, 0, numSprites, i);
+ numSprites++;
+ }
+ }
+
+ return readVar(0);
+}
+
+int Sprite::getGroupPriority(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].priority;
+}
+
+int Sprite::getGroupDstResNum(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].image;
+}
+
+int Sprite::getGroupXMul(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].scale_x_ratio_mul;
+}
+
+int Sprite::getGroupXDiv(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].scale_x_ratio_div;
+}
+
+int Sprite::getGroupYMul(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].scale_y_ratio_mul;
+}
+
+int Sprite::getGroupYDiv(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].scale_y_ratio_div;
+}
+
+void Sprite::getGroupPosition(int spriteGroupId, int32 &tx, int32 &ty) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ tx = _spriteGroups[spriteGroupId].tx;
+ ty = _spriteGroups[spriteGroupId].ty;
+}
+
+//
+// spriteInfoSet functions
+//
+void Sprite::setSpritePalette(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].palette != value) {
+ _spriteTable[spriteId].palette = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteSourceImage(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].sourceImage != value) {
+ _spriteTable[spriteId].sourceImage = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteMaskImage(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].maskImage = value;
+}
+
+void Sprite::setSpriteImageState(int spriteId, int state) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].image) {
+ int imageStateCount = _spriteTable[spriteId].imageStateCount - 1;
+ state = MAX(0, state);
+ state = MIN(state, imageStateCount);
+
+ if (_spriteTable[spriteId].imageState != state) {
+ _spriteTable[spriteId].imageState = state;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::setSpritePosition(int spriteId, int tx, int ty) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].tx != tx || _spriteTable[spriteId].ty != ty) {
+ _spriteTable[spriteId].tx = tx;
+ _spriteTable[spriteId].ty = ty;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteGroup(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ checkRange(_varNumSpriteGroups, 0, value, "Invalid sprite group %d");
+
+ _spriteTable[spriteId].group = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteDist(int spriteId, int value1, int value2) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].dx = value1;
+ _spriteTable[spriteId].dy = value2;
+}
+
+void Sprite::setSpriteShadow(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].shadow = value;
+ if (_spriteTable[spriteId].image)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteUserValue(int spriteId, int value1, int value2) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].userValue = value2;
+}
+
+void Sprite::setSpritePriority(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].priority = value;
+}
+
+void Sprite::moveSprite(int spriteId, int value1, int value2) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].tx += value1;
+ _spriteTable[spriteId].ty += value2;
+
+ if (value1 || value2)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteScale(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].flags |= kSFScaled;
+
+ if (_spriteTable[spriteId].scale != value) {
+ _spriteTable[spriteId].scale = value;
+
+ if (_spriteTable[spriteId].image)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteAngle(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].flags |= kSFRotated;
+
+ if (_spriteTable[spriteId].angle != value) {
+ _spriteTable[spriteId].angle = value;
+
+ if (_spriteTable[spriteId].image)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteFlagDoubleBuffered(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ int oldFlags = _spriteTable[spriteId].flags;
+ if (value)
+ _spriteTable[spriteId].flags |= kSFDoubleBuffered;
+ else
+ _spriteTable[spriteId].flags &= ~kSFDoubleBuffered;
+
+ if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteFlagYFlipped(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ int oldFlags = _spriteTable[spriteId].flags;
+ if (value)
+ _spriteTable[spriteId].flags |= kSFYFlipped;
+ else
+ _spriteTable[spriteId].flags &= ~kSFYFlipped;
+
+ if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteFlagXFlipped(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ int oldFlags = _spriteTable[spriteId].flags;
+ if (value)
+ _spriteTable[spriteId].flags |= kSFXFlipped;
+ else
+ _spriteTable[spriteId].flags &= ~kSFXFlipped;
+
+ if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteFlagActive(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (value)
+ _spriteTable[spriteId].flags |= kSFActive;
+ else
+ _spriteTable[spriteId].flags &= ~kSFActive;
+}
+
+void Sprite::setSpriteFlagRemapPalette(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ int oldFlags = _spriteTable[spriteId].flags;
+ if (value)
+ _spriteTable[spriteId].flags |= kSFRemapPalette;
+ else
+ _spriteTable[spriteId].flags &= ~kSFRemapPalette;
+
+ if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteFlagAutoAnim(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (value)
+ _spriteTable[spriteId].flags |= kSFAutoAnim;
+ else
+ _spriteTable[spriteId].flags &= ~kSFAutoAnim;
+}
+
+void Sprite::setSpriteFlagUpdateType(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ switch(value) {
+ case 2:
+ _spriteTable[spriteId].flags &= ~(kSFMarkDirty);
+ _spriteTable[spriteId].flags |= kSFBlitDirectly;
+ break;
+ case 1:
+ _spriteTable[spriteId].flags |= kSFMarkDirty | kSFBlitDirectly;
+ break;
+ case 0:
+ _spriteTable[spriteId].flags &= ~(kSFMarkDirty | kSFBlitDirectly);
+ break;
+ default:
+ error("setSpriteFlagUpdateType: Invalid value %d", value);
+ }
+}
+
+void Sprite::setSpriteFlagEraseType(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ // Note that condition is inverted
+ if (!value)
+ _spriteTable[spriteId].flags |= kSFImageless;
+ else
+ _spriteTable[spriteId].flags &= ~kSFImageless;
+}
+
+void Sprite::setSpriteAnimSpeed(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].animSpeed = value;
+ _spriteTable[spriteId].animProgress = value;
+}
+
+void Sprite::setSpriteSetClass(int spriteId, int classId, int toggle) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ checkRange(32, 1, classId, "class %d out of range in statement");
+
+ if (toggle) {
+ _spriteTable[spriteId].classFlags |= (1 << (classId - 1));
+ } else {
+ _spriteTable[spriteId].classFlags &= ~(1 << (classId - 1));
+ }
+}
+
+void Sprite::setSpriteResetClass(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].classFlags = 0;
+}
+
+void Sprite::setSpriteField84(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].field_84 = value;
+}
+
+void Sprite::setSpriteGeneralProperty(int spriteId, int type, int value) {
+ debug(0, "setSpriteGeneralProperty: spriteId %d type 0x%x", spriteId, type);
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ int32 delay;
+
+ // XXX U32 related check
+
+ switch(type) {
+ case 0x7B:
+ _spriteTable[spriteId].imgFlags = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ break;
+ case 0x7D:
+ _spriteTable[spriteId].field_90 = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ break;
+ case 0x7E:
+ delay = MAX(0, value);
+ delay = MIN(delay, _spriteTable[spriteId].animSpeed);
+
+ _spriteTable[spriteId].animProgress = delay;
+ break;
+ default:
+ error("setSpriteGeneralProperty: Invalid value %d", type);
+ }
+}
+
+void Sprite::resetSprite(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].angle = 0;
+ _spriteTable[spriteId].scale = 0;
+
+ setSpriteImage(spriteId, 0);
+
+ _spriteTable[spriteId].shadow = 0;
+ _spriteTable[spriteId].tx = 0;
+ _spriteTable[spriteId].ty = 0;
+
+ _spriteTable[spriteId].flags &= ~(kSFYFlipped | kSFXFlipped);
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ _spriteTable[spriteId].dx = 0;
+ _spriteTable[spriteId].dy = 0;
+ _spriteTable[spriteId].userValue = 0;
+ _spriteTable[spriteId].group = 0;
+ _spriteTable[spriteId].animSpeed = 0;
+ _spriteTable[spriteId].animProgress = 0;
+ _spriteTable[spriteId].classFlags = 0;
+ _spriteTable[spriteId].palette = 0;
+ _spriteTable[spriteId].sourceImage = 0;
+ _spriteTable[spriteId].maskImage = 0;
+ _spriteTable[spriteId].priority = 0;
+ _spriteTable[spriteId].field_84 = 0;
+ _spriteTable[spriteId].imgFlags = 0;
+ _spriteTable[spriteId].field_90 = 0;
+}
+
+void Sprite::setSpriteImage(int spriteId, int imageNum) {
+ int origResId, origResWizStates;
+
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ origResId = _spriteTable[spriteId].image;
+ origResWizStates = _spriteTable[spriteId].imageStateCount;
+
+ _spriteTable[spriteId].image = imageNum;
+ _spriteTable[spriteId].field_74 = 0;
+ _spriteTable[spriteId].imageState = 0;
+
+ if (_spriteTable[spriteId].image) {
+ _spriteTable[spriteId].imageStateCount = _vm->_wiz->getWizImageStates(_spriteTable[spriteId].image);
+ _spriteTable[spriteId].flags |= kSFActive | kSFAutoAnim | kSFMarkDirty | kSFBlitDirectly;
+
+ if (_spriteTable[spriteId].image != origResId || _spriteTable[spriteId].imageStateCount != origResWizStates)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ } else {
+ if (_spriteTable[spriteId].flags & kSFImageless)
+ _spriteTable[spriteId].flags = 0;
+ else
+ _spriteTable[spriteId].flags = kSFChanged | kSFBlitDirectly;
+ _spriteTable[spriteId].curImage = 0;
+ _spriteTable[spriteId].curImageState = 0;
+ _spriteTable[spriteId].imageStateCount = 0;
+ }
+}
+
+//
+// spriteGroupSet functions
+//
+void Sprite::redrawSpriteGroup(int spriteGroupId) {
+ for (int i = 0; i < _numSpritesToProcess; ++i) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (spi->group == spriteGroupId) {
+ spi->flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::moveGroupMembers(int spriteGroupId, int value1, int value2) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ _spriteTable[i].tx += value1;
+ _spriteTable[i].ty += value2;
+
+ if (value1 || value2)
+ _spriteTable[i].flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::setGroupMembersPriority(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId)
+ _spriteTable[i].priority = value;
+ }
+}
+
+void Sprite::setGroupMembersGroup(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ _spriteTable[i].group = value;
+ _spriteTable[i].flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::setGroupMembersUpdateType(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId)
+ setSpriteFlagUpdateType(i, value);
+ }
+}
+
+void Sprite::setGroupMembersResetSprite(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId)
+ resetSprite(i);
+ }
+}
+
+void Sprite::setGroupMembersAnimationSpeed(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ _spriteTable[i].animSpeed = value;
+ _spriteTable[i].animProgress = value;
+ }
+ }
+}
+
+void Sprite::setGroupMembersAutoAnimFlag(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ if (value)
+ _spriteTable[i].flags |= kSFAutoAnim;
+ else
+ _spriteTable[i].flags &= ~kSFAutoAnim;
+ }
+ }
+}
+
+void Sprite::setGroupMembersShadow(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ _spriteTable[i].shadow = value;
+ if (_spriteTable[i].image)
+ _spriteTable[i].flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::setGroupBounds(int spriteGroupId, int x1, int y1, int x2, int y2) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ _spriteGroups[spriteGroupId].flags |= kSGFClipBox;
+ _spriteGroups[spriteGroupId].bbox.left = x1;
+ _spriteGroups[spriteGroupId].bbox.top = y1;
+ _spriteGroups[spriteGroupId].bbox.right = x2;
+ _spriteGroups[spriteGroupId].bbox.bottom = y2;
+
+ redrawSpriteGroup(spriteGroupId);
+}
+
+void Sprite::setGroupPriority(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].priority != value) {
+ _spriteGroups[spriteGroupId].priority = value;
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupPosition(int spriteGroupId, int value1, int value2) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].tx != value1 || _spriteGroups[spriteGroupId].ty != value2) {
+ _spriteGroups[spriteGroupId].tx = value1;
+ _spriteGroups[spriteGroupId].ty = value2;
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::moveGroup(int spriteGroupId, int value1, int value2) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (value1 || value2) {
+ _spriteGroups[spriteGroupId].tx += value1;
+ _spriteGroups[spriteGroupId].ty += value2;
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupImage(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].image != value) {
+ _spriteGroups[spriteGroupId].image = value;
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupScaling(int spriteGroupId) {
+ if ((_spriteGroups[spriteGroupId].scale_x_ratio_mul != _spriteGroups[spriteGroupId].scale_x_ratio_div) || (_spriteGroups[spriteGroupId].scale_y_ratio_mul != _spriteGroups[spriteGroupId].scale_y_ratio_div))
+ _spriteGroups[spriteGroupId].scaling = 1;
+ else
+ _spriteGroups[spriteGroupId].scaling = 0;
+
+}
+
+void Sprite::setGroupXMul(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].scale_x_ratio_mul != value) {
+ _spriteGroups[spriteGroupId].scale_x_ratio_mul = value;
+ setGroupScaling(spriteGroupId);
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupXDiv(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (value == 0)
+ error("setGroupXDiv: Divisor must not be 0");
+
+ if (_spriteGroups[spriteGroupId].scale_x_ratio_div != value) {
+ _spriteGroups[spriteGroupId].scale_x_ratio_div = value;
+ setGroupScaling(spriteGroupId);
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupYMul(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].scale_y_ratio_mul != value) {
+ _spriteGroups[spriteGroupId].scale_y_ratio_mul = value;
+ setGroupScaling(spriteGroupId);
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupYDiv(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (value == 0)
+ error("setGroupYDiv: Divisor must not be 0");
+
+ if (_spriteGroups[spriteGroupId].scale_y_ratio_div != value) {
+ _spriteGroups[spriteGroupId].scale_y_ratio_div = value;
+ setGroupScaling(spriteGroupId);
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::resetGroupBounds(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ _spriteGroups[spriteGroupId].flags &= ~(kSGFClipBox);
+ redrawSpriteGroup(spriteGroupId);
+}
+
+void Sprite::allocTables(int numSprites, int numGroups, int numMaxSprites) {
+ _varNumSpriteGroups = numGroups;
+ _numSpritesToProcess = 0;
+ _varNumSprites = numSprites;
+ _varMaxSprites = numMaxSprites;
+ _spriteGroups = (SpriteGroup *)malloc((_varNumSpriteGroups + 1) * sizeof(SpriteGroup));
+ _spriteTable = (SpriteInfo *)malloc((_varNumSprites + 1) * sizeof(SpriteInfo));
+ _activeSpritesTable = (SpriteInfo **)malloc((_varNumSprites + 1) * sizeof(SpriteInfo *));
+}
+
+void Sprite::resetGroup(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+ SpriteGroup *spg = &_spriteGroups[spriteGroupId];
+
+ spg->priority = 0;
+ spg->tx = spg->ty = 0;
+
+ spg->flags &= ~kSGFClipBox;
+ redrawSpriteGroup(spriteGroupId);
+
+ spg->image = 0;
+ spg->scaling = 0;
+ spg->scale_x_ratio_mul = 1;
+ spg->scale_x_ratio_div = 1;
+ spg->scale_y_ratio_mul = 1;
+ spg->scale_y_ratio_div = 1;
+}
+
+void Sprite::resetTables(bool refreshScreen) {
+ memset(_spriteTable, 0, (_varNumSprites + 1) * sizeof(SpriteInfo));
+ memset(_spriteGroups, 0, (_varNumSpriteGroups + 1) * sizeof(SpriteGroup));
+ for (int curGrp = 1; curGrp < _varNumSpriteGroups; ++curGrp)
+ resetGroup(curGrp);
+
+ if (refreshScreen) {
+ _vm->gdi.copyVirtScreenBuffers(Common::Rect(_vm->_screenWidth, _vm->_screenHeight));
+ }
+ _numSpritesToProcess = 0;
+}
+
+void Sprite::resetBackground() {
+ int xmin, xmax, ymin, ymax;
+ xmin = ymin = 1234;
+ xmax = ymax = -1234;
+ bool firstLoop = true;
+ bool refreshScreen = false;
+
+ for (int i = 0; i < _numSpritesToProcess; ++i) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (!(spi->flags & kSFImageless) && (spi->flags & kSFChanged)) {
+ spi->flags &= ~kSFChanged;
+ if (spi->bbox.left <= spi->bbox.right && spi->bbox.top <= spi->bbox.bottom) {
+ if (spi->flags & kSFBlitDirectly) {
+ _vm->gdi.copyVirtScreenBuffers(spi->bbox, USAGE_BIT_RESTORED);
+ } else if (firstLoop) {
+ xmin = spi->bbox.left;
+ ymin = spi->bbox.top;
+ xmax = spi->bbox.right;
+ ymax = spi->bbox.bottom;
+ firstLoop = false;
+ refreshScreen = true;
+ } else {
+ if (xmin > spi->bbox.left) {
+ xmin = spi->bbox.left;
+ }
+ if (ymin > spi->bbox.top) {
+ ymin = spi->bbox.top;
+ }
+ if (xmax < spi->bbox.right) {
+ xmax = spi->bbox.right;
+ }
+ if (ymax < spi->bbox.bottom) {
+ ymax = spi->bbox.bottom;
+ }
+ refreshScreen = true;
+ }
+ if (!(spi->flags & kSFNeedRedraw) && spi->image)
+ spi->flags |= kSFNeedRedraw;
+ }
+ }
+ }
+ if (refreshScreen) {
+ _vm->gdi.copyVirtScreenBuffers(Common::Rect(xmin, ymin, xmax, ymax), USAGE_BIT_RESTORED);
+ }
+}
+
+void Sprite::setRedrawFlags(bool checkZOrder) {
+ VirtScreen *vs = &_vm->virtscr[kMainVirtScreen];
+ for (int i = 0; i < _numSpritesToProcess; ++i) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (!(spi->flags & kSFNeedRedraw)) {
+ if ((!checkZOrder || spi->priority >= 0) && (spi->flags & kSFMarkDirty)) {
+ int lp = spi->bbox.left / 8;
+ lp = MAX(0, lp);
+ lp = MIN(lp, 79);
+ int rp = (spi->bbox.right + 7) / 8;
+ rp = MAX(0, rp);
+ rp = MIN(rp, 79);
+ for (; lp <= rp; ++lp) {
+ if (vs->tdirty[lp] < vs->h && spi->bbox.bottom >= vs->tdirty[lp] && spi->bbox.top <= vs->bdirty[lp]) {
+ spi->flags |= kSFNeedRedraw;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void Sprite::updateImages() {
+ for (int i = 0; i < _numSpritesToProcess; ++i) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (spi->dx || spi->dy) {
+ int tx = spi->tx;
+ int ty = spi->ty;
+ spi->tx += spi->dx;
+ spi->ty += spi->dy;
+ if (tx != spi->tx || ty != spi->ty) {
+ spi->flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+ if (spi->flags & kSFAutoAnim) {
+ if (spi->animSpeed) {
+ --spi->animProgress;
+ if (spi->animProgress)
+ continue;
+
+ spi->animProgress = spi->animSpeed;
+ }
+ int imageState = spi->imageState;
+ ++spi->imageState;
+ if (spi->imageState >= spi->imageStateCount) {
+ spi->imageState = 0;
+ if (imageState == 0)
+ continue;
+ }
+ spi->flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+static int compareSprTable(const void *a, const void *b) {
+ const SpriteInfo *spr1 = *(const SpriteInfo *const*)a;
+ const SpriteInfo *spr2 = *(const SpriteInfo *const*)b;
+
+ if (spr1->zorder > spr2->zorder)
+ return 1;
+
+ if (spr1->zorder < spr2->zorder)
+ return -1;
+
+ return 0;
+}
+
+void Sprite::sortActiveSprites() {
+ int groupZorder;
+
+ _numSpritesToProcess = 0;
+
+ if (_varNumSprites <= 1)
+ return;
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ SpriteInfo *spi = &_spriteTable[i];
+
+ if (spi->flags & kSFActive) {
+ if (!(spi->flags & kSFMarkDirty)) {
+ spi->flags |= kSFNeedRedraw;
+ if (!(spi->flags & kSFImageless))
+ spi->flags |= kSFChanged;
+ }
+ if (spi->group)
+ groupZorder = _spriteGroups[spi->group].priority;
+ else
+ groupZorder = 0;
+
+ spi->id = i;
+ spi->zorder = spi->priority + groupZorder;
+
+ _activeSpritesTable[_numSpritesToProcess++] = spi;
+ }
+ }
+
+ if (_numSpritesToProcess < 2)
+ return;
+
+ qsort(_activeSpritesTable, _numSpritesToProcess, sizeof(SpriteInfo *), compareSprTable);
+}
+
+void Sprite::processImages(bool arg) {
+ int spr_flags;
+ int32 spr_wiz_x, spr_wiz_y;
+ int image, imageState;
+ Common::Rect *bboxPtr;
+ int angle, scale;
+ int32 w, h;
+ WizParameters wiz;
+
+ for (int i = 0; i < _numSpritesToProcess; i++) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+
+ if (!(spi->flags & kSFNeedRedraw))
+ continue;
+
+ spr_flags = spi->flags;
+
+ if (arg) {
+ if (spi->zorder >= 0)
+ return;
+ } else {
+ if (spi->zorder < 0)
+ continue;
+ }
+
+ spi->flags &= ~kSFNeedRedraw;
+ image = spi->image;
+ imageState = spi->imageState;
+ _vm->_wiz->getWizImageSpot(spi->image, spi->imageState, spr_wiz_x, spr_wiz_y);
+
+ if (spi->group) {
+ SpriteGroup *spg = &_spriteGroups[spi->group];
+
+ if (spg->scaling) {
+ wiz.img.x1 = spi->tx * spg->scale_x_ratio_mul / spg->scale_x_ratio_div - spr_wiz_x + spg->tx;
+ wiz.img.y1 = spi->ty * spg->scale_y_ratio_mul / spg->scale_y_ratio_div - spr_wiz_y + spg->ty;
+ } else {
+ wiz.img.x1 = spi->tx - spr_wiz_x + spg->tx;
+ wiz.img.y1 = spi->ty - spr_wiz_y + spg->ty;
+ }
+ } else {
+ wiz.img.x1 = spi->tx - spr_wiz_x;
+ wiz.img.y1 = spi->ty - spr_wiz_y;
+ }
+
+ wiz.spriteId = spi->id;
+ wiz.spriteGroup = spi->group;
+ wiz.field_23EA = spi->field_90;
+ spi->curImageState = wiz.img.state = imageState;
+ spi->curImage = wiz.img.resNum = image;
+ wiz.processFlags = kWPFNewState | kWPFSetPos;
+ spi->curAngle = spi->angle;
+ spi->curScale = spi->scale;
+ spi->pos.x = wiz.img.x1;
+ spi->pos.y = wiz.img.y1;
+ bboxPtr = &spi->bbox;
+ if (image) {
+ angle = spi->angle;
+ scale = spi->scale;
+ _vm->_wiz->getWizImageDim(image, imageState, w, h);
+ if (spi->flags & (kSFScaled | kSFRotated)) {
+ Common::Point pts[4];
+ _vm->_wiz->polygonTransform(image, imageState, wiz.img.x1, wiz.img.y1, angle, scale, pts);
+ _vm->_wiz->polygonCalcBoundBox(pts, 4, spi->bbox);
+ } else {
+ bboxPtr->left = wiz.img.x1;
+ bboxPtr->top = wiz.img.y1;
+ bboxPtr->right = wiz.img.x1 + w;
+ bboxPtr->bottom = wiz.img.y1 + h;
+ }
+ } else {
+ bboxPtr->left = 1234;
+ bboxPtr->top = 1234;
+ bboxPtr->right = -1234;
+ bboxPtr->bottom = -1234;
+ }
+
+ wiz.img.flags = kWIFMarkBufferDirty;
+ wiz.img.zorder = 0;
+ if (spr_flags & kSFXFlipped)
+ wiz.img.flags |= kWIFFlipX;
+ if (spr_flags & kSFYFlipped)
+ wiz.img.flags |= kWIFFlipY;
+ if (spr_flags & kSFDoubleBuffered) {
+ wiz.img.flags &= ~kWIFMarkBufferDirty;
+ wiz.img.flags |= kWIFBlitToFrontVideoBuffer;
+ }
+ if (spi->shadow) {
+ wiz.img.flags |= 0x200;
+ wiz.processFlags |= kWPFShadow;
+ wiz.img.shadow = spi->shadow;
+ }
+ if (spr_flags & kSFRemapPalette)
+ wiz.img.flags |= kWIFRemapPalette;
+ if (spi->field_84) {
+ wiz.processFlags |= 0x200000;
+ wiz.img.field_390 = spi->field_84;
+ wiz.img.zorder = spi->priority;
+ }
+ if (spi->sourceImage) {
+ wiz.processFlags |= kWPFMaskImg;
+ wiz.sourceImage = spi->sourceImage;
+ }
+ wiz.processFlags |= kWPFNewFlags;
+ wiz.img.flags |= spi->imgFlags;
+
+ if (spr_flags & kSFRotated) {
+ wiz.processFlags |= kWPFRotate;
+ wiz.angle = spi->angle;
+ }
+ if (spr_flags & kSFScaled) {
+ wiz.processFlags |= kWPFScaled;
+ wiz.scale = spi->scale;
+ }
+ spi->curImgFlags = wiz.img.flags;
+
+ if (spi->group && (_spriteGroups[spi->group].flags & kSGFClipBox)) {
+ Common::Rect &spgBbox = _spriteGroups[spi->group].bbox;
+ if (spgBbox.isValidRect() && spi->bbox.intersects(spgBbox)) {
+ spi->bbox.clip(spgBbox);
+ wiz.processFlags |= kWPFClipBox;
+ wiz.box = spi->bbox;
+ } else {
+ bboxPtr->left = 1234;
+ bboxPtr->top = 1234;
+ bboxPtr->right = -1234;
+ bboxPtr->bottom = -1234;
+ continue;
+ }
+ }
+ if (spi->palette) {
+ wiz.processFlags |= kWPFPaletteNum;
+ wiz.img.palette = spi->palette;
+ }
+ if (spi->image && spi->group && _spriteGroups[spi->group].image) {
+ wiz.processFlags |= kWPFDstResNum;
+ wiz.dstResNum = _spriteGroups[spi->group].image;
+ }
+ _vm->_wiz->displayWizComplexImage(&wiz);
+ }
+}
+
+void Sprite::saveOrLoadSpriteData(Serializer *s) {
+ static const SaveLoadEntry spriteEntries[] = {
+ MKLINE(SpriteInfo, id, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, zorder, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, flags, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, image, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, imageState, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, group, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, palette, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, priority, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, bbox.left, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, bbox.top, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, bbox.right, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, bbox.bottom, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, dx, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, dy, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, pos.x, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, pos.y, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, tx, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, ty, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, userValue, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curImageState, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curImage, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, imglistNum, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, shadow, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, imageStateCount, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, angle, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, scale, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, animProgress, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curAngle, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curScale, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curImgFlags, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, field_74, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, animSpeed, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, sourceImage, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, maskImage, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, field_84, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, classFlags, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, imgFlags, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, field_90, sleInt32, VER(48)),
+ MKEND()
+ };
+
+ static const SaveLoadEntry spriteGroupEntries[] = {
+ MKLINE(SpriteGroup, bbox.left, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, bbox.top, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, bbox.right, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, bbox.bottom, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, priority, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, flags, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, tx, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, ty, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, image, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scaling, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scale_x_ratio_mul, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scale_x_ratio_div, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scale_y_ratio_mul, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scale_y_ratio_div, sleInt32, VER(48)),
+ MKEND()
+ };
+
+ if (s->getVersion() >= VER(64)) {
+ s->saveLoadArrayOf(_spriteTable, _varNumSprites + 1, sizeof(_spriteTable[0]), spriteEntries);
+ s->saveLoadArrayOf(_spriteGroups, _varNumSpriteGroups + 1, sizeof(_spriteGroups[0]), spriteGroupEntries);
+ } else {
+ s->saveLoadArrayOf(_activeSpritesTable, _varNumSprites, sizeof(_activeSpritesTable[0]), spriteEntries);
+ s->saveLoadArrayOf(_spriteTable, _varNumSprites, sizeof(_spriteTable[0]), spriteEntries);
+ s->saveLoadArrayOf(_spriteGroups, _varNumSpriteGroups, sizeof(_spriteGroups[0]), spriteGroupEntries);
+ }
+
+ // Reset active sprite table
+ if (s->isLoading())
+ _numSpritesToProcess = 0;
+
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/sprite_he.h b/engines/scumm/sprite_he.h
new file mode 100644
index 0000000000..5396c1fed4
--- /dev/null
+++ b/engines/scumm/sprite_he.h
@@ -0,0 +1,222 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(SPRITE_HE_H) && !defined(DISABLE_HE)
+#define SPRITE_HE_H
+
+namespace Scumm {
+
+enum SpriteFlags {
+ kSFChanged = 0x1,
+ kSFNeedRedraw = 0x2,
+ kSFScaled = 0x10,
+ kSFRotated = 0x20,
+ kSFDoubleBuffered = 0x1000,
+ kSFYFlipped = 0x2000,
+ kSFXFlipped = 0x4000,
+ kSFActive = 0x8000,
+ kSFRemapPalette = 0x80000,
+ kSFAutoAnim = 0x200000,
+ kSFMarkDirty = 0x400000,
+ kSFBlitDirectly = 0x2000000,
+ kSFImageless = 0x40000000
+};
+
+enum SpriteGroupFlags {
+ kSGFClipBox = (1 << 0)
+};
+
+struct SpriteInfo {
+ int32 id;
+ int32 zorder;
+ int32 flags;
+ int32 image;
+ int32 imageState;
+ int32 group;
+ int32 palette;
+ int32 priority;
+ Common::Rect bbox;
+ int32 dx;
+ int32 dy;
+ Common::Point pos;
+ int32 tx;
+ int32 ty;
+ int32 userValue;
+ int32 curImageState;
+ int32 curImage;
+ int32 imglistNum;
+ int32 shadow;
+ int32 imageStateCount;
+ int32 angle;
+ int32 scale;
+ int32 animProgress;
+ int32 curAngle;
+ int32 curScale;
+ int32 curImgFlags;
+ int32 field_74;
+ int32 animSpeed;
+ int32 sourceImage;
+ int32 maskImage;
+ int32 field_84;
+ int32 classFlags;
+ int32 imgFlags;
+ int32 field_90;
+};
+
+struct SpriteGroup {
+ Common::Rect bbox;
+ int32 priority;
+ int32 flags;
+ int32 tx;
+ int32 ty;
+ int32 image;
+ int32 scaling;
+ int32 scale_x_ratio_mul;
+ int32 scale_x_ratio_div;
+ int32 scale_y_ratio_mul;
+ int32 scale_y_ratio_div;
+};
+
+class ScummEngine_v90he;
+
+class Sprite {
+public:
+ Sprite(ScummEngine_v90he *vm);
+ virtual ~Sprite();
+
+ SpriteInfo *_spriteTable;
+ SpriteGroup *_spriteGroups;
+ SpriteInfo **_activeSpritesTable;
+
+ int32 _numSpritesToProcess;
+ int32 _varNumSpriteGroups;
+ int32 _varNumSprites;
+ int32 _varMaxSprites;
+
+ void saveOrLoadSpriteData(Serializer *s);
+ void resetBackground();
+ void setRedrawFlags(bool checkZOrder);
+ void sortActiveSprites();
+ void processImages(bool arg);
+ void updateImages();
+
+ int findSpriteWithClassOf(int x, int y, int spriteGroupId, int d, int num, int *args);
+ int getSpriteClass(int spriteId, int num, int *args);
+ int getSpriteFlagDoubleBuffered(int spriteId);
+ int getSpriteFlagYFlipped(int spriteId);
+ int getSpriteFlagXFlipped(int spriteId);
+ int getSpriteFlagActive(int spriteId);
+ int getSpriteFlagRemapPalette(int spriteId);
+ int getSpriteFlagAutoAnim(int spriteId);
+ int getSpriteFlagUpdateType(int spriteId);
+ int getSpriteFlagEraseType(int spriteId);
+ int getSpriteImage(int spriteId);
+ int getSpriteImageState(int spriteId);
+ int getSpriteGroup(int spriteId);
+ int getSpritePalette(int spriteId);
+ int getSpritePriority(int spriteId);
+ int getSpriteDisplayX(int spriteId);
+ int getSpriteDisplayY(int spriteId);
+ int getSpriteUserValue(int spriteId);
+ int getSpriteShadow(int spriteId);
+ int getSpriteImageStateCount(int spriteId);
+ int getSpriteScale(int spriteId);
+ int getSpriteAnimSpeed(int spriteId);
+ int getSpriteSourceImage(int spriteId);
+ int getSpriteMaskImage(int spriteId);
+ int getSpriteGeneralProperty(int spriteId, int type);
+ void getSpriteBounds(int spriteId, bool checkGroup, Common::Rect &bound);
+ void getSpriteImageDim(int spriteId, int32 &w, int32 &h);
+ void getSpritePosition(int spriteId, int32 &tx, int32 &ty);
+ void getSpriteDist(int spriteId, int32 &dx, int32 &dy);
+
+ int getGroupPriority(int spriteGroupId);
+ int getGroupDstResNum(int spriteGroupId);
+ int getGroupXMul(int spriteGroupId);
+ int getGroupXDiv(int spriteGroupId);
+ int getGroupYMul(int spriteGroupId);
+ int getGroupYDiv(int spriteGroupId);
+ void getGroupPosition(int spriteGroupId, int32 &tx, int32 &ty);
+
+ void setSpritePalette(int spriteId, int value);
+ void setSpriteSourceImage(int spriteId, int value);
+ void setSpriteMaskImage(int spriteId, int value);
+ void resetSprite(int spriteId);
+ void setSpriteImageState(int spriteId, int value);
+ void setSpritePosition(int spriteId, int value1, int value2);
+ void setSpriteGroup(int spriteId, int value);
+ void setSpriteDist(int spriteId, int value1, int value2);
+ void setSpriteShadow(int spriteId, int value);
+ void setSpriteUserValue(int spriteId, int value1, int value2);
+ void setSpritePriority(int spriteId, int value);
+ void moveSprite(int spriteId, int value1, int value2);
+ void setSpriteScale(int spriteId, int value);
+ void setSpriteAngle(int spriteId, int value);
+ void setSpriteFlagDoubleBuffered(int spriteId, int value);
+ void setSpriteFlagYFlipped(int spriteId, int value);
+ void setSpriteFlagXFlipped(int spriteId, int value);
+ void setSpriteFlagActive(int spriteId, int value);
+ void setSpriteFlagRemapPalette(int spriteId, int value);
+ void setSpriteFlagAutoAnim(int spriteId, int value);
+ void setSpriteFlagUpdateType(int spriteId, int value);
+ void setSpriteFlagEraseType(int spriteId, int value);
+ void setSpriteAnimSpeed(int spriteId, int value);
+ void setSpriteSetClass(int spriteId, int classId, int toggle);
+ void setSpriteResetClass(int spriteId);
+ void setSpriteField84(int spriteId, int value);
+ void setSpriteGeneralProperty(int spriteId, int type, int value);
+
+ void moveGroupMembers(int spriteGroupId, int value1, int value2);
+ void redrawSpriteGroup(int spriteGroupId);
+ void setGroupMembersPriority(int spriteGroupId, int value);
+ void setGroupMembersGroup(int spriteGroupId, int value);
+ void setGroupMembersUpdateType(int spriteGroupId, int value);
+ void setGroupMembersResetSprite(int spriteGroupId);
+ void setGroupMembersAnimationSpeed(int spriteGroupId, int value);
+ void setGroupMembersAutoAnimFlag(int spriteGroupId, int value);
+ void setGroupMembersShadow(int spriteGroupId, int value);
+
+ void moveGroup(int spriteGroupId, int value1, int value2);
+ void setGroupBounds(int spriteGroupId, int x1, int y1, int x2, int y2);
+ void setGroupPriority(int spriteGroupId, int value);
+ void setGroupPosition(int spriteGroupId, int value1, int value2);
+ void setGroupImage(int spriteGroupId, int value);
+ void setGroupScaling(int spriteGroupId);
+ void setGroupXMul(int spriteGroupId, int value);
+ void setGroupXDiv(int spriteGroupId, int value);
+ void setGroupYMul(int spriteGroupId, int value);
+ void setGroupYDiv(int spriteGroupId, int value);
+ void resetGroupBounds(int spriteGroupId);
+
+ void allocTables(int numSprites, int numGroups, int numMaxSprites);
+ void resetGroup(int spriteGroupId);
+ void resetTables(bool refreshScreen);
+ void setSpriteImage(int spriteId, int imageNum);
+private:
+ ScummEngine_v90he *_vm;
+};
+
+} // End of namespace Scumm
+
+#endif
+
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
new file mode 100644
index 0000000000..c32413542d
--- /dev/null
+++ b/engines/scumm/string.cpp
@@ -0,0 +1,1247 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+
+#include "scumm/scumm.h"
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/dialogs.h"
+#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/intern.h"
+#ifndef DISABLE_HE
+#include "scumm/intern_he.h"
+#endif
+#include "scumm/verbs.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+
+
+#pragma mark -
+#pragma mark --- "High level" message code ---
+#pragma mark -
+
+
+void ScummEngine::printString(int m, const byte *msg) {
+ switch (m) {
+ case 0:
+ actorTalk(msg);
+ break;
+ case 1:
+ drawString(1, msg);
+ break;
+ case 2:
+ debugMessage(msg);
+ break;
+ case 3:
+ showMessageDialog(msg);
+ break;
+ }
+}
+
+
+void ScummEngine::debugMessage(const byte *msg) {
+ byte buffer[500];
+ convertMessageToString(msg, buffer, sizeof(buffer));
+
+// if ((_gameId == GID_CMI) && _debugMode) { // In CMI, debugMessage is used for printDebug output
+ if ((buffer[0] != 0xFF) && _debugMode) {
+ debug(0, "DEBUG: %s", buffer);
+ return;
+ }
+
+ if (buffer[0] == 0xFF && buffer[1] == 10) {
+ uint32 a, b;
+ int channel = 0;
+
+ a = buffer[2] | (buffer[3] << 8) | (buffer[6] << 16) | (buffer[7] << 24);
+ b = buffer[10] | (buffer[11] << 8) | (buffer[14] << 16) | (buffer[15] << 24);
+
+ // Sam and Max uses a caching system, printing empty messages
+ // and setting VAR_V6_SOUNDMODE beforehand. See patch 609791.
+ if (_gameId == GID_SAMNMAX)
+ channel = VAR(VAR_V6_SOUNDMODE);
+
+ if (channel != 2)
+ _sound->talkSound(a, b, 1, channel);
+ }
+}
+
+void ScummEngine::showMessageDialog(const byte *msg) {
+ // Original COMI used different code at this point.
+ // Seemed to use blastText for the messages
+ byte buf[500];
+
+ convertMessageToString(msg, buf, sizeof(buf));
+
+ if (_string[3].color == 0)
+ _string[3].color = 4;
+
+ InfoDialog dialog(this, (char*)buf);
+ VAR(VAR_KEYPRESS) = runDialog(dialog);
+}
+
+
+#pragma mark -
+#pragma mark --- V6 blast text queue code ---
+#pragma mark -
+
+
+void ScummEngine_v6::enqueueText(const byte *text, int x, int y, byte color, byte charset, bool center) {
+ BlastText &bt = _blastTextQueue[_blastTextQueuePos++];
+ assert(_blastTextQueuePos <= ARRAYSIZE(_blastTextQueue));
+
+ convertMessageToString(text, bt.text, sizeof(bt.text));
+ bt.xpos = x;
+ bt.ypos = y;
+ bt.color = color;
+ bt.charset = charset;
+ bt.center = center;
+}
+
+void ScummEngine_v6::drawBlastTexts() {
+ byte *buf;
+ int c;
+ int i;
+
+ for (i = 0; i < _blastTextQueuePos; i++) {
+
+ buf = _blastTextQueue[i].text;
+
+ _charset->_top = _blastTextQueue[i].ypos + _screenTop;
+ _charset->_right = _screenWidth - 1;
+ _charset->_center = _blastTextQueue[i].center;
+ _charset->setColor(_blastTextQueue[i].color);
+ _charset->_disableOffsX = _charset->_firstChar = true;
+ _charset->setCurID(_blastTextQueue[i].charset);
+
+ do {
+ _charset->_left = _blastTextQueue[i].xpos;
+
+ // Center text if necessary
+ if (_charset->_center) {
+ _charset->_left -= _charset->getStringWidth(0, buf) / 2;
+ if (_charset->_left < 0)
+ _charset->_left = 0;
+ }
+
+ do {
+ c = *buf++;
+
+ // FIXME: This is a workaround for bugs #864030 and #1399843:
+ // In COMI, some text contains ASCII character 11 = 0xB. It's
+ // not quite clear what it is good for; so for now we just ignore
+ // it, which seems to match the original engine (BTW, traditionally,
+ // this is a 'vertical tab').
+ if (c == 0x0B)
+ continue;
+
+ if (c != 0 && c != 0xFF && c != '\n') {
+ if (c & 0x80 && _useCJKMode) {
+ if (_language == Common::JA_JPN && !checkSJISCode(c)) {
+ c = 0x20; //not in S-JIS
+ } else {
+ c += *buf++ * 256;
+ }
+ }
+ _charset->printChar(c, true);
+ }
+ } while (c && c != '\n');
+
+ _charset->_top += _charset->getFontHeight();
+ } while (c);
+
+ _blastTextQueue[i].rect = _charset->_str;
+ }
+}
+
+void ScummEngine_v6::removeBlastTexts() {
+ int i;
+
+ for (i = 0; i < _blastTextQueuePos; i++) {
+ restoreBG(_blastTextQueue[i].rect);
+ }
+ _blastTextQueuePos = 0;
+}
+
+
+#pragma mark -
+#pragma mark --- V7 subtitle queue code ---
+#pragma mark -
+
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::processSubtitleQueue() {
+ for (int i = 0; i < _subtitleQueuePos; ++i) {
+ SubtitleText *st = &_subtitleQueue[i];
+ if (!ConfMan.getBool("subtitles") || VAR(VAR_VOICE_MODE) == 0)
+ // subtitles are disabled, don't display the text
+ continue;
+ if (!ConfMan.getBool("subtitles") && (!st->actorSpeechMsg || _mixer->isSoundHandleActive(_sound->_talkChannelHandle)))
+ // no subtitles and there's a speech variant of the message, don't display the text
+ continue;
+ enqueueText(st->text, st->xpos, st->ypos, st->color, st->charset, false);
+ }
+}
+
+void ScummEngine_v7::addSubtitleToQueue(const byte *text, const Common::Point &pos, byte color, byte charset) {
+ if (text[0] && strcmp((const char *)text, " ") != 0) {
+ assert(_subtitleQueuePos < ARRAYSIZE(_subtitleQueue));
+ SubtitleText *st = &_subtitleQueue[_subtitleQueuePos];
+ int i = 0;
+ while (1) {
+ st->text[i] = text[i];
+ if (!text[i])
+ break;
+ ++i;
+ }
+ st->xpos = pos.x;
+ st->ypos = pos.y;
+ st->color = color;
+ st->charset = charset;
+ st->actorSpeechMsg = _haveActorSpeechMsg;
+ ++_subtitleQueuePos;
+ }
+}
+
+void ScummEngine_v7::clearSubtitleQueue() {
+ memset(_subtitleQueue, 0, sizeof(_subtitleQueue));
+ _subtitleQueuePos = 0;
+}
+#endif
+
+
+
+#pragma mark -
+#pragma mark --- Core message/subtitle code ---
+#pragma mark -
+
+
+bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
+ uint32 talk_sound_a = 0;
+ uint32 talk_sound_b = 0;
+ int color, frme, c = 0, oldy;
+ bool endLoop = false;
+ byte *buffer = _charsetBuffer + _charsetBufPos;
+ while (!endLoop) {
+ c = *buffer++;
+ if (!(c == 0xFF || (_version <= 6 && c == 0xFE))) {
+ break;
+ }
+ c = *buffer++;
+ switch (c) {
+ case 1:
+ c = 13; // new line
+ endLoop = true;
+ break;
+ case 2:
+ _haveMsg = 0;
+ _keepText = true;
+ endLoop = true;
+ break;
+ case 3:
+ _haveMsg = (_version >= 7) ? 1 : 0xFF;
+ _keepText = false;
+ endLoop = true;
+ break;
+ case 8:
+ // Ignore this code here. Occurs e.g. in MI2 when you
+ // talk to the carpenter on scabb island. It works like
+ // code 1 (=newline) in verb texts, but is ignored in
+ // spoken text (i.e. here). Used for very long verb
+ // sentences.
+ break;
+ case 9:
+ frme = buffer[0] | (buffer[1] << 8);
+ buffer += 2;
+ if (a)
+ a->startAnimActor(frme);
+ break;
+ case 10:
+ // Note the similarity to the code in debugMessage()
+ talk_sound_a = buffer[0] | (buffer[1] << 8) | (buffer[4] << 16) | (buffer[5] << 24);
+ talk_sound_b = buffer[8] | (buffer[9] << 8) | (buffer[12] << 16) | (buffer[13] << 24);
+ buffer += 14;
+ if (_heversion >= 60) {
+ _sound->startHETalkSound(talk_sound_a);
+ } else {
+ _sound->talkSound(talk_sound_a, talk_sound_b, 2);
+ }
+ _haveActorSpeechMsg = false;
+ break;
+ case 12:
+ color = buffer[0] | (buffer[1] << 8);
+ buffer += 2;
+ if (color == 0xFF)
+ _charset->setColor(_charsetColor);
+ else
+ _charset->setColor(color);
+ break;
+ case 13:
+ debug(0, "handleNextCharsetCode: Unknown opcode 13 %d", READ_LE_UINT16(buffer));
+ buffer += 2;
+ break;
+ case 14:
+ oldy = _charset->getFontHeight();
+ _charset->setCurID(*buffer++);
+ buffer += 2;
+ memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
+ _charset->_nextTop -= _charset->getFontHeight() - oldy;
+ break;
+ default:
+ error("handleNextCharsetCode: invalid code %d", c);
+ }
+ }
+ _charsetBufPos = buffer - _charsetBuffer;
+ *code = c;
+ return (c != 2 && c != 3);
+}
+
+#ifndef DISABLE_HE
+bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
+ const int charsetCode = (_heversion >= 80) ? 127 : 64;
+ uint32 talk_sound_a = 0;
+ uint32 talk_sound_b = 0;
+ int i, c = 0;
+ char value[32];
+ bool endLoop = false;
+ bool endText = false;
+ byte *buffer = _charsetBuffer + _charsetBufPos;
+ while (!endLoop) {
+ c = *buffer++;
+ if (c != charsetCode) {
+ break;
+ }
+ c = *buffer++;
+ switch (c) {
+ case 84:
+ i = 0;
+ c = *buffer++;
+ while (c != 44) {
+ value[i] = c;
+ c = *buffer++;
+ i++;
+ }
+ value[i] = 0;
+ talk_sound_a = atoi(value);
+ i = 0;
+ c = *buffer++;
+ while (c != charsetCode) {
+ value[i] = c;
+ c = *buffer++;
+ i++;
+ }
+ value[i] = 0;
+ talk_sound_b = atoi(value);
+ _sound->startHETalkSound(talk_sound_a);
+ break;
+ case 104:
+ _haveMsg = 0;
+ _keepText = true;
+ endLoop = endText = true;
+ break;
+ case 110:
+ c = 13; // new line
+ endLoop = true;
+ break;
+ case 116:
+ i = 0;
+ memset(value, 0, sizeof(value));
+ c = *buffer++;
+ while (c != charsetCode) {
+ value[i] = c;
+ c = *buffer++;
+ i++;
+ }
+ value[i] = 0;
+ talk_sound_a = atoi(value);
+ talk_sound_b = 0;
+ _sound->startHETalkSound(talk_sound_a);
+ break;
+ case 119:
+ _haveMsg = 0xFF;
+ _keepText = false;
+ endLoop = endText = true;
+ break;
+ default:
+ error("handleNextCharsetCode: invalid code %d", c);
+ }
+ }
+ _charsetBufPos = buffer - _charsetBuffer;
+ *code = c;
+ return (endText == 0);
+}
+#endif
+
+void ScummEngine::CHARSET_1() {
+ Actor *a;
+ int t, c = 0;
+#ifndef DISABLE_SCUMM_7_8
+ byte subtitleBuffer[200];
+ byte *subtitleLine = subtitleBuffer;
+ Common::Point subtitlePos;
+
+ if (_version >= 7) {
+ ((ScummEngine_v7 *)this)->processSubtitleQueue();
+ }
+#endif
+
+ if (!_haveMsg)
+ return;
+
+ if (!(_features & GF_NEW_CAMERA) && !(_gameId == GID_ZAK && (_platform == Common::kPlatformFMTowns) && getTalkingActor() == 0xFF)) {
+ if ((camera._dest.x / 8) != (camera._cur.x / 8) || camera._cur.x != camera._last.x)
+ return;
+ }
+
+ a = NULL;
+ if (getTalkingActor() != 0xFF)
+ a = derefActorSafe(getTalkingActor(), "CHARSET_1");
+
+ if (a && _string[0].overhead != 0) {
+ int s;
+
+ _string[0].xpos = a->_pos.x - virtscr[0].xstart;
+ _string[0].ypos = a->_pos.y - a->getElevation() - _screenTop;
+
+ if (_version <= 5) {
+
+ if (VAR(VAR_V5_TALK_STRING_Y) < 0) {
+ s = (a->_scaley * (int)VAR(VAR_V5_TALK_STRING_Y)) / 0xFF;
+ _string[0].ypos += (int)(((VAR(VAR_V5_TALK_STRING_Y) - s) / 2) + s);
+ } else {
+ _string[0].ypos = (int)VAR(VAR_V5_TALK_STRING_Y);
+ }
+
+ } else {
+ s = a->_scalex * a->_talkPosX / 0xFF;
+ _string[0].xpos += ((a->_talkPosX - s) / 2) + s;
+
+ s = a->_scaley * a->_talkPosY / 0xFF;
+ _string[0].ypos += ((a->_talkPosY - s) / 2) + s;
+
+ if (_string[0].ypos > _screenHeight - 40)
+ _string[0].ypos = _screenHeight - 40;
+ }
+
+ if (_string[0].ypos < 1)
+ _string[0].ypos = 1;
+
+ if (_string[0].xpos < 80)
+ _string[0].xpos = 80;
+ if (_string[0].xpos > _screenWidth - 80)
+ _string[0].xpos = _screenWidth - 80;
+ }
+
+ _charset->_top = _string[0].ypos + _screenTop;
+ _charset->_startLeft = _charset->_left = _string[0].xpos;
+ _charset->_right = _string[0].right;
+ _charset->_center = _string[0].center;
+ _charset->setColor(_charsetColor);
+
+ if (a && a->_charset)
+ _charset->setCurID(a->_charset);
+ else
+ _charset->setCurID(_string[0].charset);
+
+ if (_version >= 5)
+ memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
+
+ if (_talkDelay)
+ return;
+
+ if ((_version <= 6 && _haveMsg == 1) || (_version == 7 && _haveMsg != 1) || (_version == 8 && VAR(VAR_HAVE_MSG))) {
+ if ((_sound->_sfxMode & 2) == 0)
+ stopTalk();
+ return;
+ }
+
+ if (a && !_string[0].no_talk_anim) {
+ a->runActorTalkScript(a->_talkStartFrame);
+ _useTalkAnims = true;
+ }
+
+ _talkDelay = (VAR_DEFAULT_TALK_DELAY != 0xFF) ? VAR(VAR_DEFAULT_TALK_DELAY) : 60;
+
+ if (!_keepText) {
+ if (_version >= 7) {
+#ifndef DISABLE_SCUMM_7_8
+ ((ScummEngine_v7 *)this)->clearSubtitleQueue();
+ _charset->_nextLeft = _string[0].xpos;
+ _charset->_nextTop = _string[0].ypos;
+#endif
+ } else {
+ _charset->restoreCharsetBg();
+ }
+ }
+
+ t = _charset->_right - _string[0].xpos - 1;
+ if (_charset->_center) {
+ if (t > _charset->_nextLeft)
+ t = _charset->_nextLeft;
+ t *= 2;
+ }
+
+ if (_version > 3)
+ _charset->addLinebreaks(0, _charsetBuffer + _charsetBufPos, 0, t);
+
+ if (_charset->_center) {
+ _charset->_nextLeft -= _charset->getStringWidth(0, _charsetBuffer + _charsetBufPos) / 2;
+ if (_charset->_nextLeft < 0)
+ _charset->_nextLeft = 0;
+ }
+
+ _charset->_disableOffsX = _charset->_firstChar = !_keepText;
+
+ while (handleNextCharsetCode(a, &c)) {
+ if (c == 0) {
+ // End of text reached, set _haveMsg accordingly
+ _haveMsg = (_version >= 7) ? 2 : 1;
+ _keepText = false;
+ break;
+ }
+
+ if (c == 13) {
+ newLine:;
+ _charset->_nextLeft = _string[0].xpos;
+#ifndef DISABLE_SCUMM_7_8
+ if (_version >= 7 && subtitleLine != subtitleBuffer) {
+ ((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
+ subtitleLine = subtitleBuffer;
+ }
+#endif
+ if (_charset->_center) {
+ _charset->_nextLeft -= _charset->getStringWidth(0, _charsetBuffer + _charsetBufPos) / 2;
+ }
+
+ if (_platform == Common::kPlatformC64 && _gameId == GID_MANIAC) {
+ break;
+ } else if (!(_platform == Common::kPlatformFMTowns) && _string[0].height) {
+ _charset->_nextTop += _string[0].height;
+ } else {
+ _charset->_nextTop += _charset->getFontHeight();
+ }
+ if (_version > 3) {
+ // FIXME - is this really needed?
+ _charset->_disableOffsX = true;
+ }
+ continue;
+ }
+
+ _charset->_left = _charset->_nextLeft;
+ _charset->_top = _charset->_nextTop;
+
+ if (_version >= 7) {
+#ifndef DISABLE_SCUMM_7_8
+ if (subtitleLine == subtitleBuffer) {
+ subtitlePos.x = _charset->_left;
+ subtitlePos.y = _charset->_top;
+ }
+ *subtitleLine++ = c;
+ *subtitleLine = '\0';
+#endif
+ } else {
+ if (c & 0x80 && _useCJKMode) {
+ if (_language == Common::JA_JPN && !checkSJISCode(c)) {
+ c = 0x20; //not in S-JIS
+ } else {
+ byte *buffer = _charsetBuffer + _charsetBufPos;
+ c += *buffer++ * 256; //LE
+ _charsetBufPos = buffer - _charsetBuffer;
+ }
+ }
+ if (_version <= 3) {
+ _charset->printChar(c, false);
+ } else {
+ if (_features & GF_HE_NOSUBTITLES) {
+ // HE games which use sprites for subtitles
+ } else if (_heversion >= 60 && !ConfMan.getBool("subtitles") && _sound->isSoundRunning(1)) {
+ // Special case for HE games
+ } else if (_gameId == GID_LOOM && !ConfMan.getBool("subtitles") && (_sound->pollCD())) {
+ // Special case for Loom (CD), since it only uses CD audio.for sound
+ } else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(_sound->_talkChannelHandle))) {
+ // Subtitles are turned off, and there is a voice version
+ // of this message -> don't print it.
+ } else {
+ _charset->printChar(c, false);
+ }
+ }
+ _charset->_nextLeft = _charset->_left;
+ _charset->_nextTop = _charset->_top;
+ }
+
+ if (_version <= 2) {
+ _talkDelay += _defaultTalkDelay;
+ VAR(VAR_CHARCOUNT)++;
+ } else {
+ _talkDelay += (int)VAR(VAR_CHARINC);
+ }
+ // Handle line overflow for V3
+ if (_version == 3 && _charset->_nextLeft > _screenWidth) {
+ _charset->_nextLeft = _screenWidth;
+ }
+ // Handle line breaks for V1-V2
+ if (_version <= 2 && _charset->_nextLeft > _screenWidth) {
+ goto newLine;
+ }
+ }
+
+#ifndef DISABLE_SCUMM_7_8
+ if (_version >= 7 && subtitleLine != subtitleBuffer) {
+ ((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID());
+ }
+#endif
+}
+
+void ScummEngine::drawString(int a, const byte *msg) {
+ byte buf[270];
+ byte *space;
+ int i, c;
+ byte fontHeight = 0;
+ uint color;
+ int code = (_heversion >= 80) ? 127 : 64;
+
+ bool cmi_pos_hack = false;
+
+ convertMessageToString(msg, buf, sizeof(buf));
+
+ if (_version >= 7) {
+ // I recently disabled charset mask related code for V7+ games, thinking
+ // that it should never be needed there. Well, I missed on case: In this
+ // method, it could potentially still be used. Now the question is:
+ // Does this actually ever happen? Basically, drawString is called from
+ // two spots: First off, from drawVerb, which I *think* is not used for
+ // V7+ games (but I am not 100% sure), and secondly from printString().
+ // The latter is much harder to predict. Maybe in some obscure place it
+ // is used after all?
+ //
+ // Hence I am adding this error message, hoping that either somebody
+ // triggers it (at which point I can investigate), or, if nobody ever
+ // triggers it, we can assume that it's safe to keep this error even
+ // after the release.
+ //
+ // TODO/FIXME: Remove or update this hack before the next release!
+ error("drawString(%d, '%s') -- please inform Fingolfin about this crash!", a, buf);
+ }
+
+ _charset->_top = _string[a].ypos + _screenTop;
+ _charset->_startLeft = _charset->_left = _string[a].xpos;
+ _charset->_right = _string[a].right;
+ _charset->_center = _string[a].center;
+ _charset->setColor(_string[a].color);
+ _charset->_disableOffsX = _charset->_firstChar = true;
+ _charset->setCurID(_string[a].charset);
+
+ if (_version >= 5)
+ memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4);
+
+ fontHeight = _charset->getFontHeight();
+
+ // trim from the right
+ byte *tmp = buf;
+ space = NULL;
+ while (*tmp) {
+ if (*tmp == ' ') {
+ if (!space)
+ space = tmp;
+ } else {
+ space = NULL;
+ }
+ tmp++;
+ }
+ if (space)
+ *space = '\0';
+ if (_charset->_center) {
+ _charset->_left -= _charset->getStringWidth(a, buf) / 2;
+ }
+
+ const bool ignoreCharsetMask = (_version < 7);
+
+ if (!buf[0]) {
+ buf[0] = ' ';
+ buf[1] = 0;
+ }
+
+ for (i = 0; (c = buf[i++]) != 0;) {
+ if (_heversion >= 72 && c == code) {
+ c = buf[i++];
+ switch (c) {
+ case 110:
+ if (_charset->_center) {
+ _charset->_left = _charset->_startLeft - _charset->getStringWidth(a, buf + i);
+ } else {
+ _charset->_left = _charset->_startLeft;
+ }
+ _charset->_top += fontHeight;
+ break;
+ }
+ } else if (c == 0xFF || (_version <= 6 && c == 0xFE)) {
+ c = buf[i++];
+ switch (c) {
+ case 9:
+ case 10:
+ case 13:
+ case 14:
+ i += 2;
+ break;
+ case 1:
+ case 8:
+ if (_charset->_center) {
+ _charset->_left = _charset->_startLeft - _charset->getStringWidth(a, buf + i);
+ } else {
+ _charset->_left = _charset->_startLeft;
+ }
+ if (!(_platform == Common::kPlatformFMTowns) && _string[0].height) {
+ _charset->_nextTop += _string[0].height;
+ } else {
+ _charset->_top += fontHeight;
+ }
+ break;
+ case 12:
+ color = buf[i] + (buf[i + 1] << 8);
+ i += 2;
+ if (color == 0xFF)
+ _charset->setColor(_string[a].color);
+ else
+ _charset->setColor(color);
+ break;
+ }
+ } else {
+ if (a == 1 && _version >= 6) {
+ // FIXME: The following code is a bit nasty. It is used for the
+ // Highway surfing game in Sam&Max; there, _blitAlso is set to
+ // true when writing the highscore numbers. It is also in DOTT
+ // for parts the intro and for drawing newspaper headlines. It
+ // is also used for scores in bowling mini game in fbear and
+ // for names in load/save screen of all HE games. Maybe it is
+ // also being used in other places.
+ //
+ // A better name for _blitAlso might be _imprintOnBackground
+
+ if (_string[a].no_talk_anim == false) {
+ //debug(0, "Would have set _charset->_blitAlso = true (wanted to print '%c' = %d)", c, c);
+ _charset->_blitAlso = true;
+ }
+ }
+ if (c & 0x80 && _useCJKMode) {
+ if (_language == Common::JA_JPN && !checkSJISCode(c)) {
+ c = 0x20; //not in S-JIS
+ } else {
+ c += buf[i++] * 256;
+ if (_gameId == GID_CMI) {
+ cmi_pos_hack = true;
+ _charset->_top += 6;
+ }
+ }
+ }
+ _charset->printChar(c, ignoreCharsetMask);
+ _charset->_blitAlso = false;
+
+ if (cmi_pos_hack) {
+ cmi_pos_hack = false;
+ _charset->_top -= 6;
+ }
+ }
+ }
+
+ if (a == 0) {
+ _charset->_nextLeft = _charset->_left;
+ _charset->_nextTop = _charset->_top;
+ }
+
+ _string[a].xpos = _charset->_str.right + 8; // Indy3: Fixes Grail Diary text positioning
+}
+
+int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize) {
+ uint num = 0;
+ uint32 val;
+ byte chr;
+ const byte *src;
+ byte *end;
+ byte transBuf[384];
+
+ assert(dst);
+ end = dst + dstSize;
+
+ if (msg == NULL) {
+ debug(0, "Bad message in convertMessageToString, ignoring");
+ return 0;
+ }
+
+ if (_version >= 7) {
+ translateText(msg, transBuf);
+ src = transBuf;
+ } else {
+ src = msg;
+ }
+
+ num = 0;
+
+ while (1) {
+ chr = src[num++];
+ if (_heversion >= 80 && (src[num - 1] == '(' && src[num] == 'P' && src[num + 1] == 'U')) {
+ while (src[num++] != ')');
+ continue;
+ }
+ if ((_features & GF_HE_LOCALIZED) && chr == '[') {
+ while (src[num++] != ']');
+ continue;
+ }
+
+ if (chr == 0)
+ break;
+ if (chr == 0xFF) {
+ chr = src[num++];
+
+ // WORKAROUND for bug #985948, a script bug in Indy3. Apparently,
+ // a german 'sz' was encoded incorrectly as 0xFF2E. We replace
+ // this by the correct encoding here. See also ScummEngine::resStrLen().
+ if (_gameId == GID_INDY3 && chr == 0x2E) {
+ *dst++ = 0xE1;
+ continue;
+ }
+
+ if (chr == 1 || chr == 2 || chr == 3 || chr == 8) {
+ // Simply copy these special codes
+ *dst++ = 0xFF;
+ *dst++ = chr;
+ } else {
+ val = (_version == 8) ? READ_LE_UINT32(src + num) : READ_LE_UINT16(src + num);
+ switch (chr) {
+ case 4:
+ dst += convertIntMessage(dst, end - dst, val);
+ break;
+ case 5:
+ dst += convertVerbMessage(dst, end - dst, val);
+ break;
+ case 6:
+ dst += convertNameMessage(dst, end - dst, val);
+ break;
+ case 7:
+ dst += convertStringMessage(dst, end - dst, val);
+ break;
+ case 9:
+ case 10:
+ case 12:
+ case 13:
+ case 14:
+ // Simply copy these special codes
+ *dst++ = 0xFF;
+ *dst++ = chr;
+ *dst++ = src[num+0];
+ *dst++ = src[num+1];
+ if (_version == 8) {
+ *dst++ = src[num+2];
+ *dst++ = src[num+3];
+ }
+ break;
+ default:
+ error("convertMessageToString(): string escape sequence %d unknown", chr);
+ }
+ num += (_version == 8) ? 4 : 2;
+ }
+ } else {
+ if (!(chr == '@' && _heversion <= 71)) {
+ *dst++ = chr;
+ }
+ }
+
+ // Check for a buffer overflow
+ if (dst >= end)
+ error("convertMessageToString: buffer overflow!");
+ }
+ *dst = 0;
+
+ return dstSize - (end - dst);
+}
+
+int ScummEngine::convertIntMessage(byte *dst, int dstSize, int var) {
+ int num;
+
+ num = readVar(var);
+ return snprintf((char *)dst, dstSize, "%d", num);
+}
+
+int ScummEngine::convertVerbMessage(byte *dst, int dstSize, int var) {
+ int num, k;
+
+ num = readVar(var);
+ if (num) {
+ for (k = 1; k < _numVerbs; k++) {
+ if (num == _verbs[k].verbid && !_verbs[k].type && !_verbs[k].saveid) {
+ const byte *ptr = getResourceAddress(rtVerb, k);
+ return convertMessageToString(ptr, dst, dstSize);
+ }
+ }
+ }
+ return 0;
+}
+
+int ScummEngine::convertNameMessage(byte *dst, int dstSize, int var) {
+ int num;
+
+ num = readVar(var);
+ if (num) {
+ const byte *ptr = getObjOrActorName(num);
+ if (ptr) {
+ return convertMessageToString(ptr, dst, dstSize);
+ }
+ }
+ return 0;
+}
+
+int ScummEngine::convertStringMessage(byte *dst, int dstSize, int var) {
+ const byte *ptr;
+
+ if (_version <= 2) {
+ byte chr;
+ int i = 0;
+ while ((chr = (byte)_scummVars[var++])) {
+ if (chr != '@') {
+ *dst++ = chr;
+ i++;
+ }
+ }
+
+ return i;
+ }
+
+ if (_version == 3 || (_version >= 6 && _heversion < 72))
+ var = readVar(var);
+
+ if (var) {
+ ptr = getStringAddress(var);
+ if (ptr) {
+ return convertMessageToString(ptr, dst, dstSize);
+ }
+ }
+ return 0;
+}
+
+
+#pragma mark -
+#pragma mark --- Charset initialisation ---
+#pragma mark -
+
+
+#ifndef DISABLE_HE
+void ScummEngine_v80he::initCharset(int charsetno) {
+ ScummEngine::initCharset(charsetno);
+ VAR(VAR_CURRENT_CHARSET) = charsetno;
+}
+#endif
+
+void ScummEngine::initCharset(int charsetno) {
+ if (_gameId == GID_FT) {
+ if (!res.isResourceLoaded(rtCharset, charsetno))
+ loadCharset(charsetno);
+ } else {
+ if (!getResourceAddress(rtCharset, charsetno))
+ loadCharset(charsetno);
+ }
+
+ _string[0]._default.charset = charsetno;
+ _string[1]._default.charset = charsetno;
+
+ memcpy(_charsetColorMap, _charsetData[charsetno], sizeof(_charsetColorMap));
+}
+
+
+#pragma mark -
+#pragma mark --- Translation/localization code ---
+#pragma mark -
+
+
+#ifndef DISABLE_SCUMM_7_8
+static int indexCompare(const void *p1, const void *p2) {
+ const ScummEngine_v7::LangIndexNode *i1 = (const ScummEngine_v7::LangIndexNode *) p1;
+ const ScummEngine_v7::LangIndexNode *i2 = (const ScummEngine_v7::LangIndexNode *) p2;
+
+ return strcmp(i1->tag, i2->tag);
+}
+
+// Create an index of the language file.
+void ScummEngine_v7::loadLanguageBundle() {
+ ScummFile file;
+ int32 size;
+
+ if (_gameId == GID_DIG) {
+ openFile(file, "language.bnd");
+ } else if (_gameId == GID_CMI) {
+ openFile(file, "language.tab");
+ } else {
+ return;
+ }
+ if (file.isOpen() == false) {
+ _existLanguageFile = false;
+ return;
+ }
+
+ _existLanguageFile = true;
+
+ size = file.size();
+ _languageBuffer = (char *)calloc(1, size+1);
+ file.read(_languageBuffer, size);
+ file.close();
+
+ int32 i;
+ char *ptr = _languageBuffer;
+
+ // Count the number of lines in the language file.
+ for (_languageIndexSize = 0; ; _languageIndexSize++) {
+ ptr = strpbrk(ptr, "\n\r");
+ if (ptr == NULL)
+ break;
+ while (*ptr == '\n' || *ptr == '\r')
+ ptr++;
+ }
+
+ // Fill the language file index. This is just an array of
+ // tags and offsets. I did consider using a balanced tree
+ // instead, but the extra overhead in the node structure would
+ // easily have doubled the memory consumption of the index.
+ // And anyway, using qsort + bsearch gives us the exact same
+ // O(log(n)) access time anyway ;-).
+
+ _languageIndex = (LangIndexNode *)calloc(_languageIndexSize, sizeof(LangIndexNode));
+
+ ptr = _languageBuffer;
+
+ if (_gameId == GID_DIG) {
+ int lineCount = _languageIndexSize;
+ const char *baseTag = "";
+ byte enc = 0; // Initially assume the language file is not encoded
+
+ // We'll determine the real index size as we go.
+ _languageIndexSize = 0;
+ for (i = 0; i < lineCount; i++) {
+ if (*ptr == '!') {
+ // Don't know what a line with '!' means, just ignore it
+ } else if (*ptr == 'h') {
+ // File contains Korean text (Hangul). just ignore it
+ } else if (*ptr == 'e') {
+ // File is encoded!
+ enc = 0x13;
+ } else if (*ptr == '@') {
+ // A new 'base tag'
+ baseTag = ptr + 1;
+ } else if (*ptr == '#') {
+ // Number of subtags following a given basetag. We don't need that
+ // information so we just skip it
+ } else if (isdigit(*ptr)) {
+ int idx = 0;
+ // A number (up to three digits)...
+ while (isdigit(*ptr)) {
+ idx = idx * 10 + (*ptr - '0');
+ ptr++;
+ }
+
+ // ...followed by a slash...
+ assert(*ptr == '/');
+ ptr++;
+
+ // ...and then the translated message, possibly encoded
+ _languageIndex[_languageIndexSize].offset = ptr - _languageBuffer;
+
+ // Decode string if necessary.
+ if (enc) {
+ while (*ptr != '\n' && *ptr != '\r')
+ *ptr++ ^= enc;
+ }
+
+ // The tag is the basetag, followed by a dot and then the index
+ sprintf(_languageIndex[_languageIndexSize].tag, "%s.%03d", baseTag, idx);
+
+ // That was another index entry
+ _languageIndexSize++;
+ } else {
+ error("Unknwon languag.bnd entry found: '%s'\n", ptr);
+ }
+
+ // Skip over newlines (and turn them into null bytes)
+ ptr = strpbrk(ptr, "\n\r");
+ if (ptr == NULL)
+ break;
+ while (*ptr == '\n' || *ptr == '\r')
+ *ptr++ = 0;
+ }
+ } else {
+ for (i = 0; i < _languageIndexSize; i++) {
+ // First 8 chars in the line give the string ID / 'tag'
+ int j;
+ for (j = 0; j < 8 && !isspace(*ptr); j++, ptr++)
+ _languageIndex[i].tag[j] = toupper(*ptr);
+ _languageIndex[i].tag[j] = 0;
+
+ // After that follows a single space which we skip
+ assert(isspace(*ptr));
+ ptr++;
+
+ // Then comes the translated string: we record an offset to that.
+ _languageIndex[i].offset = ptr - _languageBuffer;
+
+ // Skip over newlines (and turn them into null bytes)
+ ptr = strpbrk(ptr, "\n\r");
+ if (ptr == NULL)
+ break;
+ while (*ptr == '\n' || *ptr == '\r')
+ *ptr++ = 0;
+
+ // Convert '\n' code to a newline. See also bug #902415.
+ char *src, *dst;
+ src = dst = _languageBuffer + _languageIndex[i].offset;
+ while (*src) {
+ if (src[0] == '\\' && src[1] == 'n') {
+ *dst++ = '\n';
+ src += 2;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ *dst = 0;
+ }
+ }
+
+ // Sort the index nodes. We'll later use bsearch on it, which is just as efficient
+ // as using a binary tree, speed wise.
+ qsort(_languageIndex, _languageIndexSize, sizeof(LangIndexNode), indexCompare);
+}
+
+void ScummEngine_v7::playSpeech(const byte *ptr) {
+ if ((_gameId == GID_DIG || _gameId == GID_CMI) && ptr[0]) {
+ char pointer[20];
+ strcpy(pointer, (const char *)ptr);
+
+ // Play speech
+ if (!(_features & GF_DEMO) && (_gameId == GID_CMI)) // CMI demo does not have .IMX for voice
+ strcat(pointer, ".IMX");
+
+ _sound->stopTalkSound();
+ _imuseDigital->stopSound(kTalkSoundID);
+ _imuseDigital->startVoice(kTalkSoundID, pointer);
+ _sound->talkSound(0, 0, 2);
+ }
+}
+
+void ScummEngine_v7::translateText(const byte *text, byte *trans_buff) {
+ LangIndexNode target;
+ LangIndexNode *found = NULL;
+ int i;
+
+ trans_buff[0] = 0;
+ _lastStringTag[0] = 0;
+
+ // WORKAROUND for bug #1172655.
+ if (_gameId == GID_DIG && text[0] != '/') {
+ if (!strcmp((const char *)text, "faint light"))
+ text = (const byte *)"/NEW.007/faint light";
+ else if (!strcmp((const char *)text, "glowing crystal"))
+ text = (const byte *)"/NEW.008/glowing crystal";
+ else if (!strcmp((const char *)text, "glowing crystals"))
+ text = (const byte *)"/NEW.009/glowing crystals";
+ else if (!strcmp((const char *)text, "pit"))
+ text = (const byte *)"/NEW.010/pit";
+ else if (!strcmp((const char *)text, "You wish."))
+ text = (const byte *)"/NEW.011/You wish.";
+ else if (!strcmp((const char *)text, "In your dreams."))
+ text = (const byte *)"/NEW.012/In your dreams";
+ else if (!strcmp((const char *)text, "left"))
+ text = (const byte *)"/CATHPLAT.068/left";
+ else if (!strcmp((const char *)text, "right"))
+ text = (const byte *)"/CATHPLAT.070/right";
+ else if (!strcmp((const char *)text, "right"))
+ text = (const byte *)"/CATHPLAT.067/top";
+ else if (!strcmp((const char *)text, "exit"))
+ text = (const byte *)"/SKY.008/exit";
+ else if (!strcmp((const char *)text, "unattached lens"))
+ text = (const byte *)"/NEW.013/unattached lens";
+ else if (!strcmp((const char *)text, "lens slot"))
+ text = (const byte *)"/NEW.014/lens slot";
+ }
+
+
+ if (_version >= 7 && text[0] == '/') {
+ // Extract the string tag from the text: /..../
+ for (i = 0; (i < 12) && (text[i + 1] != '/'); i++)
+ _lastStringTag[i] = target.tag[i] = toupper(text[i + 1]);
+ _lastStringTag[i] = target.tag[i] = 0;
+ text += i + 2;
+
+ // If a language file was loaded, try to find a translated version
+ // by doing a lookup on the string tag.
+ if (_existLanguageFile) {
+ // HACK: These are used for the object line in COMI when
+ // using one object on another. I don't know if the
+ // text in the language file is a placeholder or if
+ // we're supposed to use it, but at least in the
+ // English version things will work so much better if
+ // we can't find translations for these.
+
+ if (*text && strcmp(target.tag, "PU_M001") != 0 && strcmp(target.tag, "PU_M002") != 0)
+ found = (LangIndexNode *)bsearch(&target, _languageIndex, _languageIndexSize, sizeof(LangIndexNode), indexCompare);
+ }
+ }
+
+ if (found != NULL) {
+ strcpy((char *)trans_buff, _languageBuffer + found->offset);
+
+ if ((_gameId == GID_DIG) && !(_features & GF_DEMO)) {
+ // Replace any '%___' by the corresponding special codes in the source text
+ const byte *src = text;
+ char *dst = (char *)trans_buff;
+
+ while ((dst = strstr(dst, "%___"))) {
+ // Search for a special code in the message.
+ while (*src && *src != 0xFF) {
+ src++;
+ }
+
+ // Replace the %___ by the special code. Luckily, we can do
+ // that in-place.
+ if (*src == 0xFF) {
+ memcpy(dst, src, 4);
+ src += 4;
+ dst += 4;
+ } else
+ break;
+ }
+ }
+ } else {
+ // Default: just copy the string
+ memcpy(trans_buff, text, resStrLen(text) + 1);
+ }
+}
+
+#endif
+
+void ScummEngine::translateText(const byte *text, byte *trans_buff) {
+ // Default: just copy the string
+ memcpy(trans_buff, text, resStrLen(text) + 1);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/thumbnail.cpp b/engines/scumm/thumbnail.cpp
new file mode 100644
index 0000000000..eefc34286a
--- /dev/null
+++ b/engines/scumm/thumbnail.cpp
@@ -0,0 +1,140 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed file the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/savefile.h"
+#include "common/scaler.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+#define THMB_VERSION 1
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct ThumbnailHeader {
+ uint32 type;
+ uint32 size;
+ byte version;
+ uint16 width, height;
+ byte bpp;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+
+inline void colorToRGB(uint16 color, uint8 &r, uint8 &g, uint8 &b) {
+ r = (((color >> 11) & 0x1F) << 3);
+ g = (((color >> 5) & 0x3F) << 2);
+ b = ((color&0x1F) << 3);
+}
+
+Graphics::Surface *ScummEngine::loadThumbnail(Common::InSaveFile *file) {
+ ThumbnailHeader header;
+ file->read(&header.type, 4);
+ // We also accept the bad 'BMHT' header here, for the sake of compatibility
+ // with some older savegames which were written incorrectly due to a bug in
+ // ScummVM which wrote the thumb header type incorrectly on LE systems.
+ if (header.type != MKID('THMB') && header.type != MKID('BMHT'))
+ return 0;
+
+ header.size = file->readUint32BE();
+ header.version = file->readByte();
+
+ if (header.version > THMB_VERSION) {
+ file->skip(header.size - 9);
+ warning("Loading a newer thumbnail version");
+ return 0;
+ }
+
+ header.width = file->readUint16BE();
+ header.height = file->readUint16BE();
+ header.bpp = file->readByte();
+
+ // TODO: support other bpp values than 2
+ if (header.bpp != 2) {
+ file->skip(header.size - 14);
+ return 0;
+ }
+
+ Graphics::Surface *thumb = new Graphics::Surface();
+ thumb->create(header.width, header.height, sizeof(uint16));
+
+ uint16* pixels = (uint16 *)thumb->pixels;
+
+ for (int y = 0; y < thumb->h; ++y) {
+ for (int x = 0; x < thumb->w; ++x) {
+ uint8 r, g, b;
+ colorToRGB(file->readUint16BE(), r, g, b);
+
+ // converting to current OSystem Color
+ *pixels++ = _system->RGBToColor(r, g, b);
+ }
+ }
+
+ return thumb;
+}
+
+void ScummEngine::saveThumbnail(Common::OutSaveFile *file) {
+ Graphics::Surface thumb;
+
+#ifndef PALMOS_68K
+ if (!createThumbnailFromScreen(&thumb))
+#endif
+ thumb.create(kThumbnailWidth, kThumbnailHeight2, sizeof(uint16));
+
+ ThumbnailHeader header;
+ header.type = MKID('THMB');
+#if defined(PALMOS_ARM) || defined(__GP32__)
+ // sizeof(header) is hardcoded here, because the compiler add padding to
+ // have a 4byte aligned struct and there is no easy way to pack it.
+ header.size = 14 + thumb.w*thumb.h*thumb.bytesPerPixel;
+#else
+ header.size = sizeof(header) + thumb.w*thumb.h*thumb.bytesPerPixel;
+#endif
+ header.version = THMB_VERSION;
+ header.width = thumb.w;
+ header.height = thumb.h;
+ header.bpp = thumb.bytesPerPixel;
+
+ file->write(&header.type, 4);
+ file->writeUint32BE(header.size);
+ file->writeByte(header.version);
+ file->writeUint16BE(header.width);
+ file->writeUint16BE(header.height);
+ file->writeByte(header.bpp);
+
+ // TODO: for later this shouldn't be casted to uint16...
+ uint16* pixels = (uint16 *)thumb.pixels;
+ for (uint16 p = 0; p < thumb.w*thumb.h; ++p, ++pixels)
+ file->writeUint16BE(*pixels);
+
+ thumb.free();
+}
+
+} // end of namespace Scumm
diff --git a/engines/scumm/usage_bits.cpp b/engines/scumm/usage_bits.cpp
new file mode 100644
index 0000000000..3fd90311fd
--- /dev/null
+++ b/engines/scumm/usage_bits.cpp
@@ -0,0 +1,95 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/usage_bits.h"
+
+namespace Scumm {
+
+void ScummEngine::upgradeGfxUsageBits() {
+ int i;
+
+ for (i = 409; i >= 0; i--) {
+ bool dirty_bit = ((gfxUsageBits[i] & 0x80000000) != 0);
+ bool restored_bit = ((gfxUsageBits[i] & 0x40000000) != 0);
+
+ gfxUsageBits[3 * i] = gfxUsageBits[i] & 0x3FFFFFFF;
+ if (dirty_bit)
+ setGfxUsageBit(i, USAGE_BIT_DIRTY);
+ if (restored_bit)
+ setGfxUsageBit(i, USAGE_BIT_RESTORED);
+ }
+}
+
+void ScummEngine::setGfxUsageBit(int strip, int bit) {
+ assert(strip >= 0 && strip < ARRAYSIZE(gfxUsageBits) / 3);
+ assert(1 <= bit && bit <= 96);
+ bit--;
+ gfxUsageBits[3 * strip + bit / 32] |= (1 << (bit % 32));
+}
+
+void ScummEngine::clearGfxUsageBit(int strip, int bit) {
+ assert(strip >= 0 && strip < ARRAYSIZE(gfxUsageBits) / 3);
+ assert(1 <= bit && bit <= 96);
+ bit--;
+ gfxUsageBits[3 * strip + bit / 32] &= ~(1 << (bit % 32));
+}
+
+bool ScummEngine::testGfxUsageBit(int strip, int bit) {
+ assert(strip >= 0 && strip < ARRAYSIZE(gfxUsageBits) / 3);
+ assert(1 <= bit && bit <= 96);
+ bit--;
+ return (gfxUsageBits[3 * strip + bit / 32] & (1 << (bit % 32))) != 0;
+}
+
+bool ScummEngine::testGfxAnyUsageBits(int strip) {
+ // Exclude the DIRTY and RESTORED bits from the test
+ uint32 bitmask[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0x3FFFFFFF };
+ int i;
+
+ assert(strip >= 0 && strip < ARRAYSIZE(gfxUsageBits) / 3);
+ for (i = 0; i < 3; i++)
+ if (gfxUsageBits[3 * strip + i] & bitmask[i])
+ return true;
+
+ return false;
+}
+
+bool ScummEngine::testGfxOtherUsageBits(int strip, int bit) {
+ // Don't exclude the DIRTY and RESTORED bits from the test
+ uint32 bitmask[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+ int i;
+
+ assert(strip >= 0 && strip < ARRAYSIZE(gfxUsageBits) / 3);
+ assert(1 <= bit && bit <= 96);
+ bit--;
+ bitmask[bit / 32] &= ~(1 << (bit % 32));
+
+ for (i = 0; i < 3; i++)
+ if (gfxUsageBits[3 * strip + i] & bitmask[i])
+ return true;
+
+ return false;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/usage_bits.h b/engines/scumm/usage_bits.h
new file mode 100644
index 0000000000..36fd7cc610
--- /dev/null
+++ b/engines/scumm/usage_bits.h
@@ -0,0 +1,35 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef USAGE_BITS_H
+#define USAGE_BITS_H
+
+namespace Scumm {
+
+enum {
+ USAGE_BIT_DIRTY = 96,
+ USAGE_BIT_RESTORED = 95
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/util.cpp b/engines/scumm/util.cpp
new file mode 100644
index 0000000000..70e3a3e467
--- /dev/null
+++ b/engines/scumm/util.cpp
@@ -0,0 +1,1818 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "scumm/util.h"
+#include "common/util.h"
+#include "common/md5.h"
+
+using Common::File;
+
+namespace Scumm {
+
+#pragma mark -
+#pragma mark --- ScummFile ---
+#pragma mark -
+
+ScummFile::ScummFile() : _encbyte(0), _subFileStart(0), _subFileLen(0) {
+}
+
+void ScummFile::setEnc(byte value) {
+ _encbyte = value;
+}
+
+void ScummFile::setSubfileRange(uint32 start, uint32 len) {
+ // TODO: Add sanity checks
+ const uint32 fileSize = File::size();
+ assert(start <= fileSize);
+ assert(start + len <= fileSize);
+ _subFileStart = start;
+ _subFileLen = len;
+ seek(0, SEEK_SET);
+}
+
+void ScummFile::resetSubfile() {
+ _subFileStart = 0;
+ _subFileLen = 0;
+ seek(0, SEEK_SET);
+}
+
+bool ScummFile::open(const char *filename, AccessMode mode) {
+ if (File::open(filename, mode)) {
+ resetSubfile();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ScummFile::openSubFile(const char *filename) {
+ assert(isOpen());
+
+ // Disable the XOR encryption and reset any current subfile range
+ setEnc(0);
+ resetSubfile();
+
+ // Read in the filename table and look for the specified file
+
+ unsigned long file_off, file_len;
+ char file_name[0x20+1];
+ unsigned long i;
+
+ // Get the length of the data file to use for consistency checks
+ const uint32 data_file_len = size();
+
+ // Read offset and length to the file records */
+ const uint32 file_record_off = readUint32BE();
+ const uint32 file_record_len = readUint32BE();
+
+ // Do a quick check to make sure the offset and length are good
+ if (file_record_off + file_record_len > data_file_len) {
+ return false;
+ }
+
+ // Do a little consistancy check on file_record_length
+ if (file_record_len % 0x28) {
+ return false;
+ }
+
+ // Scan through the files
+ for (i = 0; i < file_record_len; i += 0x28) {
+ // read a file record
+ seek(file_record_off + i, SEEK_SET);
+ file_off = readUint32BE();
+ file_len = readUint32BE();
+ read(file_name, 0x20);
+ file_name[0x20] = 0;
+
+ assert(file_name[0]);
+ //debug(7, " extracting \'%s\'", file_name);
+
+ // Consistency check. make sure the file data is in the file
+ if (file_off + file_len > data_file_len) {
+ return false;
+ }
+
+ if (scumm_stricmp(file_name, filename) == 0) {
+ // We got a match!
+ setSubfileRange(file_off, file_len);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool ScummFile::eof() {
+ return _subFileLen ? (pos() >= _subFileLen) : File::eof();
+}
+
+uint32 ScummFile::pos() {
+ return File::pos() - _subFileStart;
+}
+
+uint32 ScummFile::size() {
+ return _subFileLen ? _subFileLen : File::size();
+}
+
+void ScummFile::seek(int32 offs, int whence) {
+ if (_subFileLen) {
+ // Constrain the seek to the subfile
+ switch (whence) {
+ case SEEK_END:
+ offs = _subFileStart + _subFileLen - offs;
+ break;
+ case SEEK_SET:
+ offs += _subFileStart;
+ break;
+ case SEEK_CUR:
+ offs += File::pos();
+ break;
+ }
+ assert((int32)_subFileStart <= offs && offs <= (int32)(_subFileStart + _subFileLen));
+ whence = SEEK_SET;
+ }
+ File::seek(offs, whence);
+}
+
+uint32 ScummFile::read(void *dataPtr, uint32 dataSize) {
+ uint32 realLen;
+
+ if (_subFileLen) {
+ // Limit the amount we read by the subfile boundaries.
+ const uint32 curPos = pos();
+ assert(_subFileLen >= curPos);
+ uint32 newPos = curPos + dataSize;
+ if (newPos > _subFileLen) {
+ dataSize = _subFileLen - curPos;
+ _ioFailed = true;
+ }
+ }
+
+ realLen = File::read(dataPtr, dataSize);
+
+
+ // If an encryption byte was specified, XOR the data we just read by it.
+ // This simple kind of "encryption" was used by some of the older SCUMM
+ // games.
+ if (_encbyte) {
+ byte *p = (byte *)dataPtr;
+ byte *end = p + realLen;
+ while (p < end)
+ *p++ ^= _encbyte;
+ }
+
+ return realLen;
+}
+
+uint32 ScummFile::write(const void *, uint32) {
+ error("ScummFile does not support writing!");
+ return 0;
+}
+
+#pragma mark -
+#pragma mark --- Utilities ---
+#pragma mark -
+
+void checkRange(int max, int min, int no, const char *str) {
+ if (no < min || no > max) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), str, no);
+ error("Value %d is out of bounds (%d,%d) (%s)", no, min, max, buf);
+ }
+}
+
+/**
+ * Convert an old style direction to a new style one (angle),
+ */
+int newDirToOldDir(int dir) {
+ if (dir >= 71 && dir <= 109)
+ return 1;
+ if (dir >= 109 && dir <= 251)
+ return 2;
+ if (dir >= 251 && dir <= 289)
+ return 0;
+ return 3;
+}
+
+/**
+ * Convert an new style (angle) direction to an old style one.
+ */
+int oldDirToNewDir(int dir) {
+ assert(0 <= dir && dir <= 3);
+ const int new_dir_table[4] = { 270, 90, 180, 0 };
+ return new_dir_table[dir];
+}
+
+/**
+ * Convert an angle to a simple direction.
+ */
+int toSimpleDir(int dirType, int dir) {
+ if (dirType) {
+ const int16 directions[] = { 22, 72, 107, 157, 202, 252, 287, 337 };
+ for (int i = 0; i < 7; i++)
+ if (dir >= directions[i] && dir <= directions[i+1])
+ return i+1;
+ } else {
+ const int16 directions[] = { 71, 109, 251, 289 };
+ for (int i = 0; i < 3; i++)
+ if (dir >= directions[i] && dir <= directions[i+1])
+ return i+1;
+ }
+
+ return 0;
+}
+
+/**
+ * Convert a simple direction to an angle.
+ */
+int fromSimpleDir(int dirType, int dir) {
+ if (dirType)
+ return dir * 45;
+ else
+ return dir * 90;
+}
+
+/**
+ * Normalize the given angle - that means, ensure it is positive, and
+ * change it to the closest multiple of 45 degree by abusing toSimpleDir.
+ */
+int normalizeAngle(int angle) {
+ int temp;
+
+ temp = (angle + 360) % 360;
+
+ return toSimpleDir(1, temp) * 45;
+}
+
+const char *tag2str(uint32 tag) {
+ static char str[5];
+ str[0] = (char)(tag >> 24);
+ str[1] = (char)(tag >> 16);
+ str[2] = (char)(tag >> 8);
+ str[3] = (char)tag;
+ str[4] = '\0';
+ return str;
+}
+
+#pragma mark -
+#pragma mark --- ScummNESFile ---
+#pragma mark -
+
+enum ResType {
+ NES_UNKNOWN,
+ NES_GLOBDATA,
+ NES_ROOM,
+ NES_SCRIPT,
+ NES_SOUND,
+ NES_COSTUME,
+ NES_ROOMGFX,
+ NES_COSTUMEGFX,
+ NES_SPRPALS,
+ NES_SPRDESC,
+ NES_SPRLENS,
+ NES_SPROFFS,
+ NES_SPRDATA,
+ NES_CHARSET,
+ NES_PREPLIST
+};
+
+struct ScummNESFile::Resource {
+ uint32 offset;
+ uint16 length;
+ ResType type;
+};
+
+ScummNESFile::ScummNESFile() : _stream(0), _buf(0), _ROMset(kROMsetNum) {
+}
+
+uint32 ScummNESFile::write(const void *, uint32) {
+ error("ScummNESFile does not support writing!");
+ return 0;
+}
+
+void ScummNESFile::setEnc(byte enc) {
+ _stream->setEnc(enc);
+}
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_roomgfx_usa;
+static ScummNESFile::Resource *res_roomgfx_eur;
+static ScummNESFile::Resource *res_roomgfx_swe;
+static ScummNESFile::Resource *res_roomgfx_fra;
+static ScummNESFile::Resource *res_roomgfx_ger;
+#else
+static ScummNESFile::Resource res_roomgfx_usa[40] = {
+ { 0x04001, 0x03C9, NES_ROOMGFX }, { 0x043CA, 0x069E, NES_ROOMGFX }, { 0x04A68, 0x0327, NES_ROOMGFX }, { 0x04D8F, 0x053B, NES_ROOMGFX }, { 0x052CA, 0x06BE, NES_ROOMGFX },
+ { 0x05988, 0x0682, NES_ROOMGFX }, { 0x0600A, 0x0778, NES_ROOMGFX }, { 0x06782, 0x0517, NES_ROOMGFX }, { 0x06C99, 0x07FB, NES_ROOMGFX }, { 0x07494, 0x07BE, NES_ROOMGFX },
+ { 0x08001, 0x07A5, NES_ROOMGFX }, { 0x087A6, 0x06DD, NES_ROOMGFX }, { 0x08E83, 0x04EA, NES_ROOMGFX }, { 0x0936D, 0x0846, NES_ROOMGFX }, { 0x09BB3, 0x08C8, NES_ROOMGFX },
+ { 0x0A47B, 0x0844, NES_ROOMGFX }, { 0x0ACBF, 0x0515, NES_ROOMGFX }, { 0x0B1D4, 0x0799, NES_ROOMGFX }, { 0x0B96D, 0x04BB, NES_ROOMGFX }, { 0x07C52, 0x0319, NES_ROOMGFX },
+ { 0x0C001, 0x0464, NES_ROOMGFX }, { 0x0C465, 0x076D, NES_ROOMGFX }, { 0x0CBD2, 0x0827, NES_ROOMGFX }, { 0x0D3F9, 0x0515, NES_ROOMGFX }, { 0x0D90E, 0x064E, NES_ROOMGFX },
+ { 0x0DF5C, 0x0775, NES_ROOMGFX }, { 0x0E6D1, 0x06DD, NES_ROOMGFX }, { 0x0EDAE, 0x0376, NES_ROOMGFX }, { 0x0F124, 0x05F7, NES_ROOMGFX }, { 0x0F71B, 0x0787, NES_ROOMGFX },
+ { 0x10001, 0x02D6, NES_ROOMGFX }, { 0x102D7, 0x06A3, NES_ROOMGFX }, { 0x1097A, 0x099F, NES_ROOMGFX }, { 0x11319, 0x0361, NES_ROOMGFX }, { 0x1167A, 0x0489, NES_ROOMGFX },
+ { 0x11B03, 0x0437, NES_ROOMGFX }, { 0x11F3A, 0x084D, NES_ROOMGFX }, { 0x0BE28, 0x0199, NES_ROOMGFX }, { 0x12787, 0x09A7, NES_ROOMGFX }, { 0x1312E, 0x037A, NES_ROOMGFX }
+};
+static ScummNESFile::Resource res_roomgfx_eur[40] = {
+ { 0x04001, 0x03B9, NES_ROOMGFX }, { 0x043BA, 0x069E, NES_ROOMGFX }, { 0x04A58, 0x0327, NES_ROOMGFX }, { 0x04D7F, 0x053B, NES_ROOMGFX }, { 0x052BA, 0x06BE, NES_ROOMGFX },
+ { 0x05978, 0x0682, NES_ROOMGFX }, { 0x05FFA, 0x0778, NES_ROOMGFX }, { 0x06772, 0x0517, NES_ROOMGFX }, { 0x06C89, 0x07FB, NES_ROOMGFX }, { 0x07484, 0x07BE, NES_ROOMGFX },
+ { 0x08001, 0x07A5, NES_ROOMGFX }, { 0x087A6, 0x06DD, NES_ROOMGFX }, { 0x08E83, 0x04EA, NES_ROOMGFX }, { 0x0936D, 0x0846, NES_ROOMGFX }, { 0x09BB3, 0x08C8, NES_ROOMGFX },
+ { 0x0A47B, 0x0844, NES_ROOMGFX }, { 0x0ACBF, 0x0515, NES_ROOMGFX }, { 0x0B1D4, 0x0799, NES_ROOMGFX }, { 0x0B96D, 0x04BB, NES_ROOMGFX }, { 0x07C42, 0x0319, NES_ROOMGFX },
+ { 0x0C001, 0x0464, NES_ROOMGFX }, { 0x0C465, 0x076D, NES_ROOMGFX }, { 0x0CBD2, 0x0827, NES_ROOMGFX }, { 0x0D3F9, 0x0515, NES_ROOMGFX }, { 0x0D90E, 0x064E, NES_ROOMGFX },
+ { 0x0DF5C, 0x0775, NES_ROOMGFX }, { 0x0E6D1, 0x06DD, NES_ROOMGFX }, { 0x0EDAE, 0x0376, NES_ROOMGFX }, { 0x0F124, 0x05F7, NES_ROOMGFX }, { 0x0F71B, 0x0787, NES_ROOMGFX },
+ { 0x10001, 0x02D6, NES_ROOMGFX }, { 0x102D7, 0x06A3, NES_ROOMGFX }, { 0x1097A, 0x099F, NES_ROOMGFX }, { 0x11319, 0x0361, NES_ROOMGFX }, { 0x1167A, 0x0489, NES_ROOMGFX },
+ { 0x11B03, 0x0437, NES_ROOMGFX }, { 0x11F3A, 0x084D, NES_ROOMGFX }, { 0x12787, 0x0199, NES_ROOMGFX }, { 0x12920, 0x09A7, NES_ROOMGFX }, { 0x132C7, 0x037A, NES_ROOMGFX }
+};
+static ScummNESFile::Resource res_roomgfx_swe[40] = {
+ { 0x04001, 0x03F0, NES_ROOMGFX }, { 0x043F1, 0x069E, NES_ROOMGFX }, { 0x04A8F, 0x0327, NES_ROOMGFX }, { 0x04DB6, 0x053B, NES_ROOMGFX }, { 0x052F1, 0x06BE, NES_ROOMGFX },
+ { 0x059AF, 0x0682, NES_ROOMGFX }, { 0x06031, 0x0778, NES_ROOMGFX }, { 0x067A9, 0x0517, NES_ROOMGFX }, { 0x06CC0, 0x07FB, NES_ROOMGFX }, { 0x074BB, 0x07BE, NES_ROOMGFX },
+ { 0x08001, 0x07A5, NES_ROOMGFX }, { 0x087A6, 0x06DD, NES_ROOMGFX }, { 0x08E83, 0x04EA, NES_ROOMGFX }, { 0x0936D, 0x07E2, NES_ROOMGFX }, { 0x09B4F, 0x0791, NES_ROOMGFX },
+ { 0x0A2E0, 0x07B5, NES_ROOMGFX }, { 0x0AA95, 0x0515, NES_ROOMGFX }, { 0x0AFAA, 0x0799, NES_ROOMGFX }, { 0x0B743, 0x04BF, NES_ROOMGFX }, { 0x0BC02, 0x0319, NES_ROOMGFX },
+ { 0x0C001, 0x0464, NES_ROOMGFX }, { 0x0C465, 0x072C, NES_ROOMGFX }, { 0x0CB91, 0x0827, NES_ROOMGFX }, { 0x0D3B8, 0x0515, NES_ROOMGFX }, { 0x0D8CD, 0x064E, NES_ROOMGFX },
+ { 0x0DF1B, 0x0775, NES_ROOMGFX }, { 0x0E690, 0x06DD, NES_ROOMGFX }, { 0x0ED6D, 0x0376, NES_ROOMGFX }, { 0x0F0E3, 0x05F7, NES_ROOMGFX }, { 0x0F6DA, 0x0791, NES_ROOMGFX },
+ { 0x07C79, 0x02D6, NES_ROOMGFX }, { 0x10001, 0x06A3, NES_ROOMGFX }, { 0x106A4, 0x0921, NES_ROOMGFX }, { 0x10FC5, 0x0361, NES_ROOMGFX }, { 0x11326, 0x0489, NES_ROOMGFX },
+ { 0x117AF, 0x0437, NES_ROOMGFX }, { 0x11BE6, 0x084F, NES_ROOMGFX }, { 0x12435, 0x0199, NES_ROOMGFX }, { 0x125CE, 0x0947, NES_ROOMGFX }, { 0x12F15, 0x037A, NES_ROOMGFX }
+};
+static ScummNESFile::Resource res_roomgfx_fra[40] = {
+ { 0x04001, 0x0426, NES_ROOMGFX }, { 0x04427, 0x069E, NES_ROOMGFX }, { 0x04AC5, 0x0327, NES_ROOMGFX }, { 0x04DEC, 0x053B, NES_ROOMGFX }, { 0x05327, 0x06BE, NES_ROOMGFX },
+ { 0x059E5, 0x0682, NES_ROOMGFX }, { 0x06067, 0x0778, NES_ROOMGFX }, { 0x067DF, 0x0517, NES_ROOMGFX }, { 0x06CF6, 0x07FB, NES_ROOMGFX }, { 0x074F1, 0x07BE, NES_ROOMGFX },
+ { 0x08001, 0x07A5, NES_ROOMGFX }, { 0x087A6, 0x06DD, NES_ROOMGFX }, { 0x08E83, 0x04EA, NES_ROOMGFX }, { 0x0936D, 0x07E2, NES_ROOMGFX }, { 0x09B4F, 0x0791, NES_ROOMGFX },
+ { 0x0A2E0, 0x07B5, NES_ROOMGFX }, { 0x0AA95, 0x0515, NES_ROOMGFX }, { 0x0AFAA, 0x0799, NES_ROOMGFX }, { 0x0B743, 0x04BB, NES_ROOMGFX }, { 0x0BBFE, 0x0319, NES_ROOMGFX },
+ { 0x0C001, 0x0464, NES_ROOMGFX }, { 0x0C465, 0x072C, NES_ROOMGFX }, { 0x0CB91, 0x0827, NES_ROOMGFX }, { 0x0D3B8, 0x0515, NES_ROOMGFX }, { 0x0D8CD, 0x064E, NES_ROOMGFX },
+ { 0x0DF1B, 0x0775, NES_ROOMGFX }, { 0x0E690, 0x06DD, NES_ROOMGFX }, { 0x0ED6D, 0x0376, NES_ROOMGFX }, { 0x0F0E3, 0x05F7, NES_ROOMGFX }, { 0x0F6DA, 0x0787, NES_ROOMGFX },
+ { 0x10001, 0x02D6, NES_ROOMGFX }, { 0x102D7, 0x06A3, NES_ROOMGFX }, { 0x1097A, 0x0921, NES_ROOMGFX }, { 0x1129B, 0x0361, NES_ROOMGFX }, { 0x115FC, 0x0489, NES_ROOMGFX },
+ { 0x11A85, 0x0437, NES_ROOMGFX }, { 0x11EBC, 0x070D, NES_ROOMGFX }, { 0x07CAF, 0x0199, NES_ROOMGFX }, { 0x125C9, 0x0947, NES_ROOMGFX }, { 0x12F10, 0x037A, NES_ROOMGFX }
+};
+static ScummNESFile::Resource res_roomgfx_ger[40] = {
+ { 0x04001, 0x0406, NES_ROOMGFX }, { 0x04407, 0x069E, NES_ROOMGFX }, { 0x04AA5, 0x0327, NES_ROOMGFX }, { 0x04DCC, 0x053B, NES_ROOMGFX }, { 0x05307, 0x06BE, NES_ROOMGFX },
+ { 0x059C5, 0x0682, NES_ROOMGFX }, { 0x06047, 0x0778, NES_ROOMGFX }, { 0x067BF, 0x0517, NES_ROOMGFX }, { 0x06CD6, 0x07FB, NES_ROOMGFX }, { 0x074D1, 0x07BE, NES_ROOMGFX },
+ { 0x08001, 0x07A5, NES_ROOMGFX }, { 0x087A6, 0x06DD, NES_ROOMGFX }, { 0x08E83, 0x04EA, NES_ROOMGFX }, { 0x0936D, 0x07E2, NES_ROOMGFX }, { 0x09B4F, 0x0791, NES_ROOMGFX },
+ { 0x0A2E0, 0x07B5, NES_ROOMGFX }, { 0x0AA95, 0x0515, NES_ROOMGFX }, { 0x0AFAA, 0x0799, NES_ROOMGFX }, { 0x0B743, 0x04BB, NES_ROOMGFX }, { 0x0BBFE, 0x0319, NES_ROOMGFX },
+ { 0x0C001, 0x0464, NES_ROOMGFX }, { 0x0C465, 0x072C, NES_ROOMGFX }, { 0x0CB91, 0x0827, NES_ROOMGFX }, { 0x0D3B8, 0x0515, NES_ROOMGFX }, { 0x0D8CD, 0x064E, NES_ROOMGFX },
+ { 0x0DF1B, 0x0775, NES_ROOMGFX }, { 0x0E690, 0x06DD, NES_ROOMGFX }, { 0x0ED6D, 0x0376, NES_ROOMGFX }, { 0x0F0E3, 0x05F7, NES_ROOMGFX }, { 0x0F6DA, 0x0787, NES_ROOMGFX },
+ { 0x07C8F, 0x02D6, NES_ROOMGFX }, { 0x10001, 0x06A3, NES_ROOMGFX }, { 0x106A4, 0x0921, NES_ROOMGFX }, { 0x10FC5, 0x0361, NES_ROOMGFX }, { 0x11326, 0x0489, NES_ROOMGFX },
+ { 0x117AF, 0x0437, NES_ROOMGFX }, { 0x11BE6, 0x07A0, NES_ROOMGFX }, { 0x12386, 0x0199, NES_ROOMGFX }, { 0x1251F, 0x0947, NES_ROOMGFX }, { 0x12E66, 0x037A, NES_ROOMGFX }
+};
+#endif
+static ScummNESFile::Resource *res_roomgfx[ScummNESFile::kROMsetNum] = {
+ res_roomgfx_usa,
+ res_roomgfx_eur,
+ res_roomgfx_swe,
+ res_roomgfx_fra,
+ res_roomgfx_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_costumegfx_usa;
+static ScummNESFile::Resource *res_costumegfx_eur;
+static ScummNESFile::Resource *res_costumegfx_swe;
+static ScummNESFile::Resource *res_costumegfx_fra;
+static ScummNESFile::Resource *res_costumegfx_ger;
+#else
+static ScummNESFile::Resource res_costumegfx_usa[2] = { { 0x30001, 0x0EB8, NES_COSTUMEGFX }, { 0x2F9F1, 0x0340, NES_COSTUMEGFX } };
+static ScummNESFile::Resource res_costumegfx_eur[2] = { { 0x30001, 0x0EB8, NES_COSTUMEGFX }, { 0x2F9F1, 0x0340, NES_COSTUMEGFX } };
+static ScummNESFile::Resource res_costumegfx_swe[2] = { { 0x2EFE1, 0x0EB8, NES_COSTUMEGFX }, { 0x30001, 0x0340, NES_COSTUMEGFX } };
+static ScummNESFile::Resource res_costumegfx_fra[2] = { { 0x30001, 0x0EB8, NES_COSTUMEGFX }, { 0x2F608, 0x0340, NES_COSTUMEGFX } };
+static ScummNESFile::Resource res_costumegfx_ger[2] = { { 0x30001, 0x0EB8, NES_COSTUMEGFX }, { 0x2F4CE, 0x0340, NES_COSTUMEGFX } };
+#endif
+static ScummNESFile::Resource *res_costumegfx[ScummNESFile::kROMsetNum] = {
+ res_costumegfx_usa,
+ res_costumegfx_eur,
+ res_costumegfx_swe,
+ res_costumegfx_fra,
+ res_costumegfx_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_rooms_usa;
+static ScummNESFile::Resource *res_rooms_eur;
+static ScummNESFile::Resource *res_rooms_swe;
+static ScummNESFile::Resource *res_rooms_fra;
+static ScummNESFile::Resource *res_rooms_ger;
+#else
+static ScummNESFile::Resource res_rooms_usa[55] = {
+ { 0x00000, 0x0000, NES_ROOM }, { 0x14001, 0x0D0C, NES_ROOM }, { 0x134A8, 0x04B3, NES_ROOM }, { 0x15397, 0x0849, NES_ROOM }, { 0x15C68, 0x0685, NES_ROOM },
+ { 0x16381, 0x0715, NES_ROOM }, { 0x1395B, 0x04E7, NES_ROOM }, { 0x16CE8, 0x0AC0, NES_ROOM }, { 0x18001, 0x06BA, NES_ROOM }, { 0x17AED, 0x03CB, NES_ROOM },
+ { 0x18BE7, 0x0663, NES_ROOM }, { 0x192A6, 0x0580, NES_ROOM }, { 0x19A44, 0x0443, NES_ROOM }, { 0x1A106, 0x0563, NES_ROOM }, { 0x1A669, 0x0446, NES_ROOM },
+ { 0x1AAAF, 0x03A7, NES_ROOM }, { 0x1AE56, 0x07E3, NES_ROOM }, { 0x1B699, 0x0692, NES_ROOM }, { 0x1C001, 0x0B49, NES_ROOM }, { 0x1CD09, 0x04C6, NES_ROOM },
+ { 0x1D4C2, 0x0568, NES_ROOM }, { 0x1DF6C, 0x0514, NES_ROOM }, { 0x1E8FA, 0x05CC, NES_ROOM }, { 0x1EF83, 0x0389, NES_ROOM }, { 0x1F5E4, 0x0723, NES_ROOM },
+ { 0x20001, 0x049A, NES_ROOM }, { 0x20511, 0x04F8, NES_ROOM }, { 0x21666, 0x05CB, NES_ROOM }, { 0x21DD6, 0x046B, NES_ROOM }, { 0x222F0, 0x0460, NES_ROOM },
+ { 0x227B6, 0x0909, NES_ROOM }, { 0x24001, 0x0366, NES_ROOM }, { 0x23BDF, 0x03CA, NES_ROOM }, { 0x247DB, 0x050D, NES_ROOM }, { 0x25ACF, 0x0346, NES_ROOM },
+ { 0x1BDBD, 0x01CA, NES_ROOM }, { 0x25E15, 0x0457, NES_ROOM }, { 0x2626C, 0x0547, NES_ROOM }, { 0x267B3, 0x064A, NES_ROOM }, { 0x1FD72, 0x024B, NES_ROOM },
+ { 0x2739A, 0x01FA, NES_ROOM }, { 0x2766D, 0x0219, NES_ROOM }, { 0x28001, 0x02F4, NES_ROOM }, { 0x284D6, 0x045C, NES_ROOM }, { 0x289A3, 0x09CF, NES_ROOM },
+ { 0x293C6, 0x05A0, NES_ROOM }, { 0x27B65, 0x0201, NES_ROOM }, { 0x2ADD1, 0x0325, NES_ROOM }, { 0x2B339, 0x01FC, NES_ROOM }, { 0x2B535, 0x02A9, NES_ROOM },
+ { 0x2B7DE, 0x02DE, NES_ROOM }, { 0x2C001, 0x03CE, NES_ROOM }, { 0x2BBC0, 0x0205, NES_ROOM }, { 0x2C53A, 0x0170, NES_ROOM }, { 0x13E42, 0x0169, NES_ROOM }
+};
+static ScummNESFile::Resource res_rooms_eur[55] = {
+ { 0x00000, 0x0000, NES_ROOM }, { 0x14001, 0x0D0C, NES_ROOM }, { 0x13641, 0x04B3, NES_ROOM }, { 0x15397, 0x0849, NES_ROOM }, { 0x15C68, 0x0685, NES_ROOM },
+ { 0x16381, 0x0715, NES_ROOM }, { 0x16CE8, 0x04E7, NES_ROOM }, { 0x18001, 0x0ABF, NES_ROOM }, { 0x171CF, 0x06BA, NES_ROOM }, { 0x13AF4, 0x03D2, NES_ROOM },
+ { 0x18E1A, 0x0663, NES_ROOM }, { 0x194D9, 0x04A9, NES_ROOM }, { 0x19BA0, 0x0443, NES_ROOM }, { 0x1A262, 0x047C, NES_ROOM }, { 0x1A6DE, 0x0446, NES_ROOM },
+ { 0x1AB24, 0x03A7, NES_ROOM }, { 0x1AECB, 0x07E3, NES_ROOM }, { 0x1B70E, 0x0692, NES_ROOM }, { 0x1C001, 0x0ACA, NES_ROOM }, { 0x1CC8A, 0x04C6, NES_ROOM },
+ { 0x1D443, 0x0568, NES_ROOM }, { 0x1DEED, 0x0514, NES_ROOM }, { 0x1E87B, 0x05CC, NES_ROOM }, { 0x1EF04, 0x0389, NES_ROOM }, { 0x1F565, 0x0723, NES_ROOM },
+ { 0x20001, 0x049A, NES_ROOM }, { 0x20511, 0x04F8, NES_ROOM }, { 0x21666, 0x05D5, NES_ROOM }, { 0x21DE0, 0x046B, NES_ROOM }, { 0x222FA, 0x0460, NES_ROOM },
+ { 0x227C0, 0x0909, NES_ROOM }, { 0x24001, 0x0366, NES_ROOM }, { 0x247DB, 0x03CA, NES_ROOM }, { 0x24BA5, 0x050D, NES_ROOM }, { 0x23BE9, 0x0346, NES_ROOM },
+ { 0x17DB5, 0x01CA, NES_ROOM }, { 0x25E99, 0x0457, NES_ROOM }, { 0x262F0, 0x0547, NES_ROOM }, { 0x26837, 0x064A, NES_ROOM }, { 0x1FCF3, 0x024B, NES_ROOM },
+ { 0x2741E, 0x01FA, NES_ROOM }, { 0x276F1, 0x0219, NES_ROOM }, { 0x28001, 0x02F4, NES_ROOM }, { 0x284D6, 0x045C, NES_ROOM }, { 0x289A3, 0x09CF, NES_ROOM },
+ { 0x293C6, 0x05A0, NES_ROOM }, { 0x27BE9, 0x0201, NES_ROOM }, { 0x2ADE3, 0x0325, NES_ROOM }, { 0x2B34B, 0x01FC, NES_ROOM }, { 0x2B547, 0x02A9, NES_ROOM },
+ { 0x2B7F0, 0x02DE, NES_ROOM }, { 0x2C001, 0x03CE, NES_ROOM }, { 0x2BBD2, 0x0205, NES_ROOM }, { 0x2C53A, 0x0170, NES_ROOM }, { 0x2BDD7, 0x0169, NES_ROOM }
+};
+static ScummNESFile::Resource res_rooms_swe[55] = {
+ { 0x00000, 0x0000, NES_ROOM }, { 0x14001, 0x0D12, NES_ROOM }, { 0x1328F, 0x04B3, NES_ROOM }, { 0x15367, 0x0859, NES_ROOM }, { 0x13742, 0x0694, NES_ROOM },
+ { 0x15C45, 0x0707, NES_ROOM }, { 0x1658F, 0x04E0, NES_ROOM }, { 0x16A6F, 0x0AC8, NES_ROOM }, { 0x18001, 0x06C7, NES_ROOM }, { 0x1789C, 0x03EA, NES_ROOM },
+ { 0x18C09, 0x0649, NES_ROOM }, { 0x192AE, 0x04AB, NES_ROOM }, { 0x19982, 0x0447, NES_ROOM }, { 0x1A04D, 0x047E, NES_ROOM }, { 0x1A4CB, 0x0444, NES_ROOM },
+ { 0x1A90F, 0x03B9, NES_ROOM }, { 0x1ACC8, 0x07E9, NES_ROOM }, { 0x1B511, 0x06A4, NES_ROOM }, { 0x1C001, 0x0B1A, NES_ROOM }, { 0x1CCFD, 0x0486, NES_ROOM },
+ { 0x1D482, 0x0579, NES_ROOM }, { 0x1DF61, 0x051E, NES_ROOM }, { 0x1E8EC, 0x05CF, NES_ROOM }, { 0x1EF73, 0x0398, NES_ROOM }, { 0x1F5F0, 0x071A, NES_ROOM },
+ { 0x20001, 0x049C, NES_ROOM }, { 0x2051E, 0x051E, NES_ROOM }, { 0x21725, 0x05D5, NES_ROOM }, { 0x21EA5, 0x047F, NES_ROOM }, { 0x223D1, 0x0460, NES_ROOM },
+ { 0x22897, 0x090D, NES_ROOM }, { 0x24001, 0x0378, NES_ROOM }, { 0x247C9, 0x03CA, NES_ROOM }, { 0x24B93, 0x050D, NES_ROOM }, { 0x25267, 0x0346, NES_ROOM },
+ { 0x17CD0, 0x01CA, NES_ROOM }, { 0x255AD, 0x0453, NES_ROOM }, { 0x25A00, 0x053E, NES_ROOM }, { 0x25F3E, 0x0647, NES_ROOM }, { 0x1BC49, 0x024B, NES_ROOM },
+ { 0x26B58, 0x01FA, NES_ROOM }, { 0x26E27, 0x0217, NES_ROOM }, { 0x27345, 0x02F4, NES_ROOM }, { 0x27829, 0x045C, NES_ROOM }, { 0x28001, 0x098A, NES_ROOM },
+ { 0x289DF, 0x05A1, NES_ROOM }, { 0x2A442, 0x0201, NES_ROOM }, { 0x2A6E9, 0x0325, NES_ROOM }, { 0x1FD75, 0x01FC, NES_ROOM }, { 0x2AC64, 0x02A9, NES_ROOM },
+ { 0x2AF0D, 0x02D1, NES_ROOM }, { 0x2B2E6, 0x03CC, NES_ROOM }, { 0x23D61, 0x0205, NES_ROOM }, { 0x2B818, 0x0168, NES_ROOM }, { 0x27CF6, 0x0169, NES_ROOM }
+};
+static ScummNESFile::Resource res_rooms_fra[55] = {
+ { 0x00000, 0x0000, NES_ROOM }, { 0x14001, 0x0D76, NES_ROOM }, { 0x1328A, 0x04C6, NES_ROOM }, { 0x15451, 0x0885, NES_ROOM }, { 0x13750, 0x0693, NES_ROOM },
+ { 0x15D68, 0x0709, NES_ROOM }, { 0x166D4, 0x0528, NES_ROOM }, { 0x16BFC, 0x0ACC, NES_ROOM }, { 0x18001, 0x06E2, NES_ROOM }, { 0x17A63, 0x03E5, NES_ROOM },
+ { 0x18C3B, 0x066A, NES_ROOM }, { 0x19301, 0x049E, NES_ROOM }, { 0x199C8, 0x044B, NES_ROOM }, { 0x1A0B1, 0x0478, NES_ROOM }, { 0x1A529, 0x043F, NES_ROOM },
+ { 0x1A968, 0x03C8, NES_ROOM }, { 0x1AD30, 0x086F, NES_ROOM }, { 0x1B5FF, 0x069B, NES_ROOM }, { 0x1C001, 0x0AA9, NES_ROOM }, { 0x1CC97, 0x049E, NES_ROOM },
+ { 0x1D42C, 0x05A8, NES_ROOM }, { 0x1DF71, 0x054E, NES_ROOM }, { 0x1E9D1, 0x0606, NES_ROOM }, { 0x1F0A2, 0x039A, NES_ROOM }, { 0x1F74E, 0x071C, NES_ROOM },
+ { 0x20001, 0x04B5, NES_ROOM }, { 0x2052E, 0x04FF, NES_ROOM }, { 0x2172E, 0x05DB, NES_ROOM }, { 0x21EAD, 0x0489, NES_ROOM }, { 0x223E1, 0x0465, NES_ROOM },
+ { 0x228AC, 0x0957, NES_ROOM }, { 0x24001, 0x037E, NES_ROOM }, { 0x2481A, 0x03CA, NES_ROOM }, { 0x24BE4, 0x050D, NES_ROOM }, { 0x252C0, 0x0346, NES_ROOM },
+ { 0x1BD30, 0x01CA, NES_ROOM }, { 0x25606, 0x046D, NES_ROOM }, { 0x25A73, 0x055A, NES_ROOM }, { 0x25FCD, 0x0654, NES_ROOM }, { 0x26C98, 0x024B, NES_ROOM },
+ { 0x26EE3, 0x01FA, NES_ROOM }, { 0x271DD, 0x0217, NES_ROOM }, { 0x27713, 0x02F4, NES_ROOM }, { 0x28001, 0x045C, NES_ROOM }, { 0x284CE, 0x0975, NES_ROOM },
+ { 0x28E97, 0x05E6, NES_ROOM }, { 0x27C3A, 0x0201, NES_ROOM }, { 0x2A9D6, 0x0325, NES_ROOM }, { 0x2AF88, 0x01FC, NES_ROOM }, { 0x2B184, 0x02A9, NES_ROOM },
+ { 0x2B42D, 0x02DF, NES_ROOM }, { 0x2B818, 0x03EC, NES_ROOM }, { 0x2BD67, 0x0209, NES_ROOM }, { 0x2C001, 0x0168, NES_ROOM }, { 0x2C4BF, 0x0169, NES_ROOM }
+};
+static ScummNESFile::Resource res_rooms_ger[55] = {
+ { 0x00000, 0x0000, NES_ROOM }, { 0x14001, 0x0D63, NES_ROOM }, { 0x131E0, 0x04A9, NES_ROOM }, { 0x13689, 0x086B, NES_ROOM }, { 0x15421, 0x06A8, NES_ROOM },
+ { 0x15B5D, 0x0731, NES_ROOM }, { 0x16507, 0x0501, NES_ROOM }, { 0x16A08, 0x0AE9, NES_ROOM }, { 0x18001, 0x06DA, NES_ROOM }, { 0x17880, 0x03D0, NES_ROOM },
+ { 0x18C7B, 0x0651, NES_ROOM }, { 0x19328, 0x04A7, NES_ROOM }, { 0x199FE, 0x0447, NES_ROOM }, { 0x1A0F1, 0x0486, NES_ROOM }, { 0x1A577, 0x045D, NES_ROOM },
+ { 0x1A9D4, 0x03AE, NES_ROOM }, { 0x1AD82, 0x0840, NES_ROOM }, { 0x1B622, 0x06C3, NES_ROOM }, { 0x1C001, 0x0B07, NES_ROOM }, { 0x1CD05, 0x0494, NES_ROOM },
+ { 0x1D4A5, 0x05AC, NES_ROOM }, { 0x1DFD6, 0x0524, NES_ROOM }, { 0x1E9C0, 0x05F7, NES_ROOM }, { 0x1F09A, 0x038E, NES_ROOM }, { 0x1F75F, 0x0733, NES_ROOM },
+ { 0x20001, 0x04A9, NES_ROOM }, { 0x2052A, 0x052E, NES_ROOM }, { 0x2177C, 0x0621, NES_ROOM }, { 0x21F57, 0x0495, NES_ROOM }, { 0x2249A, 0x045E, NES_ROOM },
+ { 0x2295E, 0x0951, NES_ROOM }, { 0x24001, 0x036E, NES_ROOM }, { 0x247F9, 0x03CA, NES_ROOM }, { 0x24BC3, 0x050D, NES_ROOM }, { 0x252A8, 0x0346, NES_ROOM },
+ { 0x17CA2, 0x01CA, NES_ROOM }, { 0x255EE, 0x046F, NES_ROOM }, { 0x25A5D, 0x054D, NES_ROOM }, { 0x25FAA, 0x064B, NES_ROOM }, { 0x26BE2, 0x024B, NES_ROOM },
+ { 0x26E2D, 0x01FA, NES_ROOM }, { 0x2710F, 0x0217, NES_ROOM }, { 0x27663, 0x02F4, NES_ROOM }, { 0x28001, 0x045C, NES_ROOM }, { 0x284CE, 0x0A8F, NES_ROOM },
+ { 0x28FB1, 0x05FF, NES_ROOM }, { 0x27B69, 0x0201, NES_ROOM }, { 0x2AAA9, 0x0325, NES_ROOM }, { 0x1BD7C, 0x01FC, NES_ROOM }, { 0x2B031, 0x02A9, NES_ROOM },
+ { 0x2B2DA, 0x02D8, NES_ROOM }, { 0x2B6D2, 0x03D2, NES_ROOM }, { 0x2BC0D, 0x020D, NES_ROOM }, { 0x2C001, 0x0168, NES_ROOM }, { 0x27E11, 0x0169, NES_ROOM }
+};
+#endif
+static ScummNESFile::Resource *res_rooms[ScummNESFile::kROMsetNum] = {
+ res_rooms_usa,
+ res_rooms_eur,
+ res_rooms_swe,
+ res_rooms_fra,
+ res_rooms_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_scripts_usa;
+static ScummNESFile::Resource *res_scripts_eur;
+static ScummNESFile::Resource *res_scripts_swe;
+static ScummNESFile::Resource *res_scripts_fra;
+static ScummNESFile::Resource *res_scripts_ger;
+#else
+static ScummNESFile::Resource res_scripts_usa[179] = {
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x29966, 0x044D, NES_SCRIPT }, { 0x29DB3, 0x0207, NES_SCRIPT }, { 0x29FBA, 0x009F, NES_SCRIPT }, { 0x2A059, 0x03F4, NES_SCRIPT },
+ { 0x2A44D, 0x01A1, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A5EE, 0x004A, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A638, 0x0005, NES_SCRIPT },
+ { 0x2C6AA, 0x000D, NES_SCRIPT }, { 0x2C6B7, 0x000D, NES_SCRIPT }, { 0x186BB, 0x0040, NES_SCRIPT }, { 0x186FB, 0x0016, NES_SCRIPT }, { 0x1B639, 0x0046, NES_SCRIPT },
+ { 0x1EEC6, 0x00BD, NES_SCRIPT }, { 0x21C31, 0x0055, NES_SCRIPT }, { 0x177A8, 0x0027, NES_SCRIPT }, { 0x1FD07, 0x0027, NES_SCRIPT }, { 0x1FD2E, 0x0027, NES_SCRIPT },
+ { 0x1BD2B, 0x0022, NES_SCRIPT }, { 0x15BE0, 0x0088, NES_SCRIPT }, { 0x22241, 0x0020, NES_SCRIPT }, { 0x22261, 0x008F, NES_SCRIPT }, { 0x1924A, 0x002B, NES_SCRIPT },
+ { 0x1CB4A, 0x0061, NES_SCRIPT }, { 0x1CBAB, 0x003C, NES_SCRIPT }, { 0x1CBE7, 0x0042, NES_SCRIPT }, { 0x1CC29, 0x004F, NES_SCRIPT }, { 0x2049B, 0x0076, NES_SCRIPT },
+ { 0x16A96, 0x0035, NES_SCRIPT }, { 0x16ACB, 0x001C, NES_SCRIPT }, { 0x16AE7, 0x0014, NES_SCRIPT }, { 0x16AFB, 0x001C, NES_SCRIPT }, { 0x16B17, 0x0027, NES_SCRIPT },
+ { 0x16B3E, 0x01AA, NES_SCRIPT }, { 0x1D1CF, 0x0096, NES_SCRIPT }, { 0x1D265, 0x010E, NES_SCRIPT }, { 0x1D373, 0x001C, NES_SCRIPT }, { 0x1D38F, 0x0056, NES_SCRIPT },
+ { 0x1D3E5, 0x0072, NES_SCRIPT }, { 0x1E480, 0x0028, NES_SCRIPT }, { 0x1E4A8, 0x017D, NES_SCRIPT }, { 0x1E625, 0x0229, NES_SCRIPT }, { 0x28932, 0x0071, NES_SCRIPT },
+ { 0x17EB8, 0x004D, NES_SCRIPT }, { 0x162ED, 0x0039, NES_SCRIPT }, { 0x18711, 0x028B, NES_SCRIPT }, { 0x1899C, 0x00BB, NES_SCRIPT }, { 0x18A57, 0x018B, NES_SCRIPT },
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x19E87, 0x00ED, NES_SCRIPT }, { 0x21C86, 0x00F6, NES_SCRIPT }, { 0x1E84E, 0x009B, NES_SCRIPT }, { 0x21D7C, 0x0047, NES_SCRIPT },
+ { 0x2C6C4, 0x004D, NES_SCRIPT }, { 0x16326, 0x0024, NES_SCRIPT }, { 0x14D0D, 0x0014, NES_SCRIPT }, { 0x177CF, 0x0059, NES_SCRIPT }, { 0x17828, 0x0109, NES_SCRIPT },
+ { 0x17931, 0x0009, NES_SCRIPT }, { 0x14D21, 0x01B6, NES_SCRIPT }, { 0x2B0F6, 0x0243, NES_SCRIPT }, { 0x230BF, 0x067F, NES_SCRIPT }, { 0x2C711, 0x001C, NES_SCRIPT },
+ { 0x2C72D, 0x001A, NES_SCRIPT }, { 0x2C747, 0x0021, NES_SCRIPT }, { 0x2C768, 0x0024, NES_SCRIPT }, { 0x2C78C, 0x0017, NES_SCRIPT }, { 0x2C7A3, 0x0017, NES_SCRIPT },
+ { 0x2C7BA, 0x0014, NES_SCRIPT }, { 0x2C7CE, 0x0024, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2C7F2, 0x0011, NES_SCRIPT }, { 0x1793A, 0x009D, NES_SCRIPT },
+ { 0x22750, 0x0066, NES_SCRIPT }, { 0x14ED7, 0x0075, NES_SCRIPT }, { 0x1F30C, 0x0120, NES_SCRIPT }, { 0x1FD55, 0x001D, NES_SCRIPT }, { 0x1F42C, 0x008F, NES_SCRIPT },
+ { 0x1F4BB, 0x0097, NES_SCRIPT }, { 0x179D7, 0x006A, NES_SCRIPT }, { 0x17A41, 0x0030, NES_SCRIPT }, { 0x1F552, 0x0092, NES_SCRIPT }, { 0x2C803, 0x00CC, NES_SCRIPT },
+ { 0x2C8CF, 0x00BA, NES_SCRIPT }, { 0x2C989, 0x0088, NES_SCRIPT }, { 0x20A09, 0x01B0, NES_SCRIPT }, { 0x20BB9, 0x0168, NES_SCRIPT }, { 0x20D21, 0x006C, NES_SCRIPT },
+ { 0x20D8D, 0x0037, NES_SCRIPT }, { 0x20DC4, 0x00E4, NES_SCRIPT }, { 0x20EA8, 0x0045, NES_SCRIPT }, { 0x20EED, 0x00E1, NES_SCRIPT }, { 0x20FCE, 0x00F6, NES_SCRIPT },
+ { 0x210C4, 0x0141, NES_SCRIPT }, { 0x21205, 0x0183, NES_SCRIPT }, { 0x21388, 0x0034, NES_SCRIPT }, { 0x213BC, 0x00A9, NES_SCRIPT }, { 0x24367, 0x011B, NES_SCRIPT },
+ { 0x1BD4D, 0x0070, NES_SCRIPT }, { 0x1CC78, 0x0091, NES_SCRIPT }, { 0x29372, 0x0054, NES_SCRIPT }, { 0x19F74, 0x00CE, NES_SCRIPT }, { 0x1A042, 0x0077, NES_SCRIPT },
+ { 0x14F4C, 0x0057, NES_SCRIPT }, { 0x27886, 0x02DF, NES_SCRIPT }, { 0x1DA2A, 0x0219, NES_SCRIPT }, { 0x1DC43, 0x00F9, NES_SCRIPT }, { 0x1DD3C, 0x0056, NES_SCRIPT },
+ { 0x1DD92, 0x01C2, NES_SCRIPT }, { 0x14FA3, 0x004D, NES_SCRIPT }, { 0x27594, 0x00D9, NES_SCRIPT }, { 0x21DC3, 0x0013, NES_SCRIPT }, { 0x2A63D, 0x00F0, NES_SCRIPT },
+ { 0x24482, 0x00E7, NES_SCRIPT }, { 0x21465, 0x00F2, NES_SCRIPT }, { 0x24569, 0x002B, NES_SCRIPT }, { 0x2C3CF, 0x010F, NES_SCRIPT }, { 0x24594, 0x00AA, NES_SCRIPT },
+ { 0x24CE8, 0x0DAB, NES_SCRIPT }, { 0x1B67F, 0x000D, NES_SCRIPT }, { 0x1B68C, 0x000D, NES_SCRIPT }, { 0x2373E, 0x017C, NES_SCRIPT }, { 0x282F5, 0x01E1, NES_SCRIPT },
+ { 0x238BA, 0x0153, NES_SCRIPT }, { 0x23A0D, 0x019C, NES_SCRIPT }, { 0x23BA9, 0x0016, NES_SCRIPT }, { 0x2C4DE, 0x005C, NES_SCRIPT }, { 0x23BBF, 0x0020, NES_SCRIPT },
+ { 0x27D66, 0x00A5, NES_SCRIPT }, { 0x2A72D, 0x034D, NES_SCRIPT }, { 0x14FF0, 0x00E3, NES_SCRIPT }, { 0x2BABC, 0x005F, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT },
+ { 0x25A93, 0x003C, NES_SCRIPT }, { 0x1E8E9, 0x0011, NES_SCRIPT }, { 0x1634A, 0x0018, NES_SCRIPT }, { 0x26DFD, 0x001F, NES_SCRIPT }, { 0x26E1C, 0x0054, NES_SCRIPT },
+ { 0x26E70, 0x0149, NES_SCRIPT }, { 0x26FB9, 0x004B, NES_SCRIPT }, { 0x27004, 0x017D, NES_SCRIPT }, { 0x27181, 0x0027, NES_SCRIPT }, { 0x271A8, 0x0041, NES_SCRIPT },
+ { 0x271E9, 0x01B1, NES_SCRIPT }, { 0x16362, 0x001F, NES_SCRIPT }, { 0x2463E, 0x002A, NES_SCRIPT }, { 0x150D3, 0x019E, NES_SCRIPT }, { 0x19275, 0x0031, NES_SCRIPT },
+ { 0x17A71, 0x007C, NES_SCRIPT }, { 0x21557, 0x00DC, NES_SCRIPT }, { 0x1D457, 0x0018, NES_SCRIPT }, { 0x1D46F, 0x0053, NES_SCRIPT }, { 0x18BE2, 0x0005, NES_SCRIPT },
+ { 0x15271, 0x011B, NES_SCRIPT }, { 0x1538C, 0x000B, NES_SCRIPT }, { 0x24668, 0x0138, NES_SCRIPT }, { 0x247A0, 0x0014, NES_SCRIPT }, { 0x1DF54, 0x0018, NES_SCRIPT },
+ { 0x247B4, 0x0027, NES_SCRIPT }, { 0x1A0B9, 0x004D, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2BB1B, 0x00A5, NES_SCRIPT }, { 0x2AA7A, 0x00C1, NES_SCRIPT },
+ { 0x2AB3B, 0x0140, NES_SCRIPT }, { 0x19826, 0x00BF, NES_SCRIPT }, { 0x198E5, 0x014D, NES_SCRIPT }, { 0x19A32, 0x0012, NES_SCRIPT }, { 0x2AC7B, 0x0005, NES_SCRIPT },
+ { 0x2AC80, 0x0005, NES_SCRIPT }, { 0x2AC85, 0x0005, NES_SCRIPT }, { 0x2AC8A, 0x0005, NES_SCRIPT }, { 0x2AC8F, 0x0005, NES_SCRIPT }, { 0x21633, 0x0033, NES_SCRIPT },
+ { 0x2AC94, 0x0005, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2AC99, 0x009C, NES_SCRIPT }, { 0x2AD35, 0x009C, NES_SCRIPT }
+};
+static ScummNESFile::Resource res_scripts_eur[179] = {
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x29966, 0x044D, NES_SCRIPT }, { 0x29DB3, 0x0207, NES_SCRIPT }, { 0x29FBA, 0x009F, NES_SCRIPT }, { 0x2A059, 0x03F4, NES_SCRIPT },
+ { 0x2A44D, 0x01A1, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A5EE, 0x005C, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A64A, 0x0005, NES_SCRIPT },
+ { 0x2C6AA, 0x000D, NES_SCRIPT }, { 0x2C6B7, 0x000D, NES_SCRIPT }, { 0x17889, 0x0040, NES_SCRIPT }, { 0x178C9, 0x0016, NES_SCRIPT }, { 0x1B6AE, 0x0046, NES_SCRIPT },
+ { 0x1EE47, 0x00BD, NES_SCRIPT }, { 0x21C3B, 0x0055, NES_SCRIPT }, { 0x18AC0, 0x0027, NES_SCRIPT }, { 0x1FC88, 0x0027, NES_SCRIPT }, { 0x1FCAF, 0x0027, NES_SCRIPT },
+ { 0x1BDA0, 0x0022, NES_SCRIPT }, { 0x15BE0, 0x0088, NES_SCRIPT }, { 0x2224B, 0x0020, NES_SCRIPT }, { 0x2226B, 0x008F, NES_SCRIPT }, { 0x1947D, 0x002B, NES_SCRIPT },
+ { 0x1CACB, 0x0061, NES_SCRIPT }, { 0x1CB2C, 0x003C, NES_SCRIPT }, { 0x1CB68, 0x0042, NES_SCRIPT }, { 0x1CBAA, 0x004F, NES_SCRIPT }, { 0x2049B, 0x0076, NES_SCRIPT },
+ { 0x16A96, 0x0035, NES_SCRIPT }, { 0x16ACB, 0x001C, NES_SCRIPT }, { 0x16AE7, 0x0014, NES_SCRIPT }, { 0x16AFB, 0x001C, NES_SCRIPT }, { 0x16B17, 0x0027, NES_SCRIPT },
+ { 0x16B3E, 0x01AA, NES_SCRIPT }, { 0x1D150, 0x0096, NES_SCRIPT }, { 0x1D1E6, 0x010E, NES_SCRIPT }, { 0x1D2F4, 0x001C, NES_SCRIPT }, { 0x1D310, 0x0056, NES_SCRIPT },
+ { 0x1D366, 0x0072, NES_SCRIPT }, { 0x1E401, 0x0028, NES_SCRIPT }, { 0x1E429, 0x017D, NES_SCRIPT }, { 0x1E5A6, 0x0229, NES_SCRIPT }, { 0x28932, 0x0071, NES_SCRIPT },
+ { 0x13EC6, 0x004D, NES_SCRIPT }, { 0x162ED, 0x0039, NES_SCRIPT }, { 0x178DF, 0x028B, NES_SCRIPT }, { 0x17B6A, 0x00BB, NES_SCRIPT }, { 0x17C25, 0x018B, NES_SCRIPT },
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x19FE3, 0x00ED, NES_SCRIPT }, { 0x21C90, 0x00F6, NES_SCRIPT }, { 0x1E7CF, 0x009B, NES_SCRIPT }, { 0x21D86, 0x0047, NES_SCRIPT },
+ { 0x2C6C4, 0x004D, NES_SCRIPT }, { 0x16326, 0x0024, NES_SCRIPT }, { 0x14D0D, 0x0014, NES_SCRIPT }, { 0x18AE7, 0x0059, NES_SCRIPT }, { 0x18B40, 0x011E, NES_SCRIPT },
+ { 0x18C5E, 0x0009, NES_SCRIPT }, { 0x14D21, 0x01B6, NES_SCRIPT }, { 0x2B108, 0x0243, NES_SCRIPT }, { 0x230C9, 0x067F, NES_SCRIPT }, { 0x2C711, 0x001C, NES_SCRIPT },
+ { 0x2C72D, 0x001A, NES_SCRIPT }, { 0x2C747, 0x0021, NES_SCRIPT }, { 0x2C768, 0x0024, NES_SCRIPT }, { 0x2C78C, 0x0017, NES_SCRIPT }, { 0x2C7A3, 0x0017, NES_SCRIPT },
+ { 0x2C7BA, 0x0014, NES_SCRIPT }, { 0x2C7CE, 0x0024, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2C7F2, 0x0011, NES_SCRIPT }, { 0x18C67, 0x009D, NES_SCRIPT },
+ { 0x2275A, 0x0066, NES_SCRIPT }, { 0x14ED7, 0x0075, NES_SCRIPT }, { 0x1F28D, 0x0120, NES_SCRIPT }, { 0x1FCD6, 0x001D, NES_SCRIPT }, { 0x1F3AD, 0x008F, NES_SCRIPT },
+ { 0x1F43C, 0x0097, NES_SCRIPT }, { 0x18D04, 0x006A, NES_SCRIPT }, { 0x18D6E, 0x0030, NES_SCRIPT }, { 0x1F4D3, 0x0092, NES_SCRIPT }, { 0x2C803, 0x00CC, NES_SCRIPT },
+ { 0x2C8CF, 0x00BA, NES_SCRIPT }, { 0x2C989, 0x0088, NES_SCRIPT }, { 0x20A09, 0x01B0, NES_SCRIPT }, { 0x20BB9, 0x0168, NES_SCRIPT }, { 0x20D21, 0x006C, NES_SCRIPT },
+ { 0x20D8D, 0x0037, NES_SCRIPT }, { 0x20DC4, 0x00E4, NES_SCRIPT }, { 0x20EA8, 0x0045, NES_SCRIPT }, { 0x20EED, 0x00E1, NES_SCRIPT }, { 0x20FCE, 0x00F6, NES_SCRIPT },
+ { 0x210C4, 0x0141, NES_SCRIPT }, { 0x21205, 0x0183, NES_SCRIPT }, { 0x21388, 0x0034, NES_SCRIPT }, { 0x213BC, 0x00A9, NES_SCRIPT }, { 0x24367, 0x011B, NES_SCRIPT },
+ { 0x1BDC2, 0x0070, NES_SCRIPT }, { 0x1CBF9, 0x0091, NES_SCRIPT }, { 0x29372, 0x0054, NES_SCRIPT }, { 0x1A0D0, 0x00CE, NES_SCRIPT }, { 0x1A19E, 0x0077, NES_SCRIPT },
+ { 0x14F4C, 0x0057, NES_SCRIPT }, { 0x2790A, 0x02DF, NES_SCRIPT }, { 0x1D9AB, 0x0219, NES_SCRIPT }, { 0x1DBC4, 0x00F9, NES_SCRIPT }, { 0x1DCBD, 0x0056, NES_SCRIPT },
+ { 0x1DD13, 0x01C2, NES_SCRIPT }, { 0x14FA3, 0x004D, NES_SCRIPT }, { 0x27618, 0x00D9, NES_SCRIPT }, { 0x21DCD, 0x0013, NES_SCRIPT }, { 0x2A64F, 0x00F0, NES_SCRIPT },
+ { 0x24482, 0x00E7, NES_SCRIPT }, { 0x21465, 0x00F2, NES_SCRIPT }, { 0x24569, 0x002B, NES_SCRIPT }, { 0x2C3CF, 0x010F, NES_SCRIPT }, { 0x24594, 0x00AA, NES_SCRIPT },
+ { 0x250B2, 0x0DAB, NES_SCRIPT }, { 0x1B6F4, 0x000D, NES_SCRIPT }, { 0x1B701, 0x000D, NES_SCRIPT }, { 0x23748, 0x017C, NES_SCRIPT }, { 0x282F5, 0x01E1, NES_SCRIPT },
+ { 0x238C4, 0x0153, NES_SCRIPT }, { 0x23A17, 0x019C, NES_SCRIPT }, { 0x23BB3, 0x0016, NES_SCRIPT }, { 0x2C4DE, 0x005C, NES_SCRIPT }, { 0x23BC9, 0x0020, NES_SCRIPT },
+ { 0x27DEA, 0x00A5, NES_SCRIPT }, { 0x2A73F, 0x034D, NES_SCRIPT }, { 0x14FF0, 0x00E3, NES_SCRIPT }, { 0x2BACE, 0x005F, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT },
+ { 0x25E5D, 0x003C, NES_SCRIPT }, { 0x1E86A, 0x0011, NES_SCRIPT }, { 0x1634A, 0x0018, NES_SCRIPT }, { 0x26E81, 0x001F, NES_SCRIPT }, { 0x26EA0, 0x0054, NES_SCRIPT },
+ { 0x26EF4, 0x0149, NES_SCRIPT }, { 0x2703D, 0x004B, NES_SCRIPT }, { 0x27088, 0x017D, NES_SCRIPT }, { 0x27205, 0x0027, NES_SCRIPT }, { 0x2722C, 0x0041, NES_SCRIPT },
+ { 0x2726D, 0x01B1, NES_SCRIPT }, { 0x16362, 0x001F, NES_SCRIPT }, { 0x2463E, 0x002A, NES_SCRIPT }, { 0x150D3, 0x019E, NES_SCRIPT }, { 0x194A8, 0x0031, NES_SCRIPT },
+ { 0x18D9E, 0x007C, NES_SCRIPT }, { 0x21557, 0x00DC, NES_SCRIPT }, { 0x1D3D8, 0x0018, NES_SCRIPT }, { 0x1D3F0, 0x0053, NES_SCRIPT }, { 0x17DB0, 0x0005, NES_SCRIPT },
+ { 0x15271, 0x011B, NES_SCRIPT }, { 0x1538C, 0x000B, NES_SCRIPT }, { 0x24668, 0x0138, NES_SCRIPT }, { 0x247A0, 0x0014, NES_SCRIPT }, { 0x1DED5, 0x0018, NES_SCRIPT },
+ { 0x247B4, 0x0027, NES_SCRIPT }, { 0x1A215, 0x004D, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2BB2D, 0x00A5, NES_SCRIPT }, { 0x2AA8C, 0x00C1, NES_SCRIPT },
+ { 0x2AB4D, 0x0140, NES_SCRIPT }, { 0x19982, 0x00BF, NES_SCRIPT }, { 0x19A41, 0x014D, NES_SCRIPT }, { 0x19B8E, 0x0012, NES_SCRIPT }, { 0x2AC8D, 0x0005, NES_SCRIPT },
+ { 0x2AC92, 0x0005, NES_SCRIPT }, { 0x2AC97, 0x0005, NES_SCRIPT }, { 0x2AC9C, 0x0005, NES_SCRIPT }, { 0x2ACA1, 0x0005, NES_SCRIPT }, { 0x21633, 0x0033, NES_SCRIPT },
+ { 0x2ACA6, 0x0005, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2ACAB, 0x009C, NES_SCRIPT }, { 0x2AD47, 0x009C, NES_SCRIPT }
+};
+static ScummNESFile::Resource res_scripts_swe[179] = {
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x28F80, 0x043B, NES_SCRIPT }, { 0x293BB, 0x0209, NES_SCRIPT }, { 0x295C4, 0x00AB, NES_SCRIPT }, { 0x2966F, 0x03FD, NES_SCRIPT },
+ { 0x29A6C, 0x01A1, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x29C0D, 0x005C, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x29C69, 0x0005, NES_SCRIPT },
+ { 0x2B980, 0x000D, NES_SCRIPT }, { 0x2B98D, 0x000D, NES_SCRIPT }, { 0x186C8, 0x0040, NES_SCRIPT }, { 0x18708, 0x0016, NES_SCRIPT }, { 0x1B4B1, 0x0046, NES_SCRIPT },
+ { 0x1EEBB, 0x00B8, NES_SCRIPT }, { 0x21CFA, 0x005C, NES_SCRIPT }, { 0x17537, 0x0027, NES_SCRIPT }, { 0x1FD0A, 0x0027, NES_SCRIPT }, { 0x1FD31, 0x0027, NES_SCRIPT },
+ { 0x1BBB5, 0x0022, NES_SCRIPT }, { 0x15BC0, 0x0085, NES_SCRIPT }, { 0x22324, 0x001E, NES_SCRIPT }, { 0x22342, 0x008F, NES_SCRIPT }, { 0x19252, 0x002B, NES_SCRIPT },
+ { 0x1CB1B, 0x006D, NES_SCRIPT }, { 0x1CB88, 0x004C, NES_SCRIPT }, { 0x1CBD4, 0x0044, NES_SCRIPT }, { 0x1CC18, 0x0053, NES_SCRIPT }, { 0x2049D, 0x0081, NES_SCRIPT },
+ { 0x1634C, 0x0035, NES_SCRIPT }, { 0x16381, 0x001C, NES_SCRIPT }, { 0x1639D, 0x0014, NES_SCRIPT }, { 0x163B1, 0x001C, NES_SCRIPT }, { 0x163CD, 0x0027, NES_SCRIPT },
+ { 0x163F4, 0x019B, NES_SCRIPT }, { 0x1D183, 0x0094, NES_SCRIPT }, { 0x1D217, 0x0117, NES_SCRIPT }, { 0x1D32E, 0x001C, NES_SCRIPT }, { 0x1D34A, 0x0056, NES_SCRIPT },
+ { 0x1D3A0, 0x0072, NES_SCRIPT }, { 0x1E47F, 0x0028, NES_SCRIPT }, { 0x1E4A7, 0x0175, NES_SCRIPT }, { 0x1E61C, 0x022B, NES_SCRIPT }, { 0x27C85, 0x0071, NES_SCRIPT },
+ { 0x17C86, 0x004A, NES_SCRIPT }, { 0x13DD6, 0x0039, NES_SCRIPT }, { 0x1871E, 0x0270, NES_SCRIPT }, { 0x1898E, 0x00C0, NES_SCRIPT }, { 0x18A4E, 0x01B6, NES_SCRIPT },
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x19DC9, 0x00EE, NES_SCRIPT }, { 0x21D56, 0x00F5, NES_SCRIPT }, { 0x1E847, 0x0094, NES_SCRIPT }, { 0x21E4B, 0x0047, NES_SCRIPT },
+ { 0x2B99A, 0x004D, NES_SCRIPT }, { 0x13E0F, 0x0024, NES_SCRIPT }, { 0x14D13, 0x0014, NES_SCRIPT }, { 0x1755E, 0x0054, NES_SCRIPT }, { 0x175B2, 0x011A, NES_SCRIPT },
+ { 0x176CC, 0x0009, NES_SCRIPT }, { 0x14D27, 0x01B9, NES_SCRIPT }, { 0x2AA0E, 0x0256, NES_SCRIPT }, { 0x231A4, 0x06D2, NES_SCRIPT }, { 0x2B9E7, 0x001D, NES_SCRIPT },
+ { 0x2BA04, 0x0016, NES_SCRIPT }, { 0x2BA1A, 0x002D, NES_SCRIPT }, { 0x2BA47, 0x0027, NES_SCRIPT }, { 0x2BA6E, 0x0016, NES_SCRIPT }, { 0x2BA84, 0x0014, NES_SCRIPT },
+ { 0x2BA98, 0x0015, NES_SCRIPT }, { 0x2BAAD, 0x0029, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2BAD6, 0x0010, NES_SCRIPT }, { 0x176D5, 0x00A2, NES_SCRIPT },
+ { 0x22831, 0x0066, NES_SCRIPT }, { 0x14EE0, 0x0077, NES_SCRIPT }, { 0x1F30B, 0x011A, NES_SCRIPT }, { 0x1FD58, 0x001D, NES_SCRIPT }, { 0x1F425, 0x0095, NES_SCRIPT },
+ { 0x1F4BA, 0x009E, NES_SCRIPT }, { 0x17777, 0x006F, NES_SCRIPT }, { 0x177E6, 0x002F, NES_SCRIPT }, { 0x1F558, 0x0098, NES_SCRIPT }, { 0x2BAE6, 0x00C4, NES_SCRIPT },
+ { 0x2BBAA, 0x00AE, NES_SCRIPT }, { 0x2BC58, 0x0088, NES_SCRIPT }, { 0x20A3C, 0x01BB, NES_SCRIPT }, { 0x20BF7, 0x0197, NES_SCRIPT }, { 0x20D8E, 0x006E, NES_SCRIPT },
+ { 0x20DFC, 0x0028, NES_SCRIPT }, { 0x20E24, 0x00EA, NES_SCRIPT }, { 0x20F0E, 0x0049, NES_SCRIPT }, { 0x20F57, 0x00E7, NES_SCRIPT }, { 0x2103E, 0x010C, NES_SCRIPT },
+ { 0x2114A, 0x0151, NES_SCRIPT }, { 0x2129B, 0x01B0, NES_SCRIPT }, { 0x2144B, 0x0034, NES_SCRIPT }, { 0x2147F, 0x00A9, NES_SCRIPT }, { 0x24379, 0x010E, NES_SCRIPT },
+ { 0x1BBD7, 0x0072, NES_SCRIPT }, { 0x1CC6B, 0x0092, NES_SCRIPT }, { 0x2898B, 0x0054, NES_SCRIPT }, { 0x19EB7, 0x00D3, NES_SCRIPT }, { 0x19F8A, 0x0077, NES_SCRIPT },
+ { 0x14F57, 0x0057, NES_SCRIPT }, { 0x2703E, 0x0307, NES_SCRIPT }, { 0x1D9FB, 0x024F, NES_SCRIPT }, { 0x1DC4A, 0x00E4, NES_SCRIPT }, { 0x1DD2E, 0x0059, NES_SCRIPT },
+ { 0x1DD87, 0x01C2, NES_SCRIPT }, { 0x14FAE, 0x004D, NES_SCRIPT }, { 0x26D52, 0x00D5, NES_SCRIPT }, { 0x21E92, 0x0013, NES_SCRIPT }, { 0x29C6E, 0x00F0, NES_SCRIPT },
+ { 0x24487, 0x00E0, NES_SCRIPT }, { 0x21528, 0x00F2, NES_SCRIPT }, { 0x24567, 0x0023, NES_SCRIPT }, { 0x2B6B2, 0x010B, NES_SCRIPT }, { 0x2458A, 0x00A1, NES_SCRIPT },
+ { 0x250A0, 0x018B, NES_SCRIPT }, { 0x1B4F7, 0x000D, NES_SCRIPT }, { 0x1B504, 0x000D, NES_SCRIPT }, { 0x23876, 0x018E, NES_SCRIPT }, { 0x27639, 0x01F0, NES_SCRIPT },
+ { 0x23A04, 0x017B, NES_SCRIPT }, { 0x23B7F, 0x01AC, NES_SCRIPT }, { 0x23D2B, 0x0016, NES_SCRIPT }, { 0x2B7BD, 0x005B, NES_SCRIPT }, { 0x23D41, 0x0020, NES_SCRIPT },
+ { 0x2A643, 0x00A6, NES_SCRIPT }, { 0x29D5E, 0x0399, NES_SCRIPT }, { 0x14FFB, 0x00D2, NES_SCRIPT }, { 0x2B1DE, 0x0063, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT },
+ { 0x2522B, 0x003C, NES_SCRIPT }, { 0x1E8DB, 0x0011, NES_SCRIPT }, { 0x13E33, 0x0018, NES_SCRIPT }, { 0x26585, 0x001F, NES_SCRIPT }, { 0x265A4, 0x0054, NES_SCRIPT },
+ { 0x265F8, 0x017D, NES_SCRIPT }, { 0x26775, 0x004B, NES_SCRIPT }, { 0x267C0, 0x0165, NES_SCRIPT }, { 0x26925, 0x0027, NES_SCRIPT }, { 0x2694C, 0x0041, NES_SCRIPT },
+ { 0x2698D, 0x01CB, NES_SCRIPT }, { 0x13E4B, 0x001F, NES_SCRIPT }, { 0x2462B, 0x002A, NES_SCRIPT }, { 0x150CD, 0x0187, NES_SCRIPT }, { 0x1927D, 0x0031, NES_SCRIPT },
+ { 0x17815, 0x0087, NES_SCRIPT }, { 0x2161A, 0x00D8, NES_SCRIPT }, { 0x1D412, 0x0018, NES_SCRIPT }, { 0x1D42A, 0x0058, NES_SCRIPT }, { 0x18C04, 0x0005, NES_SCRIPT },
+ { 0x15254, 0x0108, NES_SCRIPT }, { 0x1535C, 0x000B, NES_SCRIPT }, { 0x24655, 0x0139, NES_SCRIPT }, { 0x2478E, 0x0014, NES_SCRIPT }, { 0x1DF49, 0x0018, NES_SCRIPT },
+ { 0x247A2, 0x0027, NES_SCRIPT }, { 0x1A001, 0x004C, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2B241, 0x00A5, NES_SCRIPT }, { 0x2A0F7, 0x00B5, NES_SCRIPT },
+ { 0x2A1AC, 0x0140, NES_SCRIPT }, { 0x19759, 0x00CA, NES_SCRIPT }, { 0x19823, 0x014D, NES_SCRIPT }, { 0x19970, 0x0012, NES_SCRIPT }, { 0x2A2EC, 0x0005, NES_SCRIPT },
+ { 0x2A2F1, 0x0005, NES_SCRIPT }, { 0x2A2F6, 0x0005, NES_SCRIPT }, { 0x2A2FB, 0x0005, NES_SCRIPT }, { 0x2A300, 0x0005, NES_SCRIPT }, { 0x216F2, 0x0033, NES_SCRIPT },
+ { 0x2A305, 0x0005, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A30A, 0x009C, NES_SCRIPT }, { 0x2A3A6, 0x009C, NES_SCRIPT }
+};
+static ScummNESFile::Resource res_scripts_fra[179] = {
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x2947D, 0x0480, NES_SCRIPT }, { 0x298FD, 0x0226, NES_SCRIPT }, { 0x29B23, 0x0092, NES_SCRIPT }, { 0x29BB5, 0x040C, NES_SCRIPT },
+ { 0x29FC1, 0x01A1, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A162, 0x005C, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A1BE, 0x0005, NES_SCRIPT },
+ { 0x2C169, 0x000D, NES_SCRIPT }, { 0x2C176, 0x000D, NES_SCRIPT }, { 0x186E3, 0x0040, NES_SCRIPT }, { 0x18723, 0x0016, NES_SCRIPT }, { 0x1B59F, 0x0046, NES_SCRIPT },
+ { 0x1EFD7, 0x00CB, NES_SCRIPT }, { 0x21D09, 0x0054, NES_SCRIPT }, { 0x176C8, 0x0027, NES_SCRIPT }, { 0x1FE6A, 0x0027, NES_SCRIPT }, { 0x1FE91, 0x0027, NES_SCRIPT },
+ { 0x1BC9A, 0x0022, NES_SCRIPT }, { 0x15CD6, 0x0092, NES_SCRIPT }, { 0x22336, 0x001C, NES_SCRIPT }, { 0x22352, 0x008F, NES_SCRIPT }, { 0x192A5, 0x002B, NES_SCRIPT },
+ { 0x1CAAA, 0x0069, NES_SCRIPT }, { 0x1CB13, 0x0054, NES_SCRIPT }, { 0x1CB67, 0x0048, NES_SCRIPT }, { 0x1CBAF, 0x0058, NES_SCRIPT }, { 0x204B6, 0x0078, NES_SCRIPT },
+ { 0x16471, 0x0035, NES_SCRIPT }, { 0x164A6, 0x001C, NES_SCRIPT }, { 0x164C2, 0x0014, NES_SCRIPT }, { 0x164D6, 0x001C, NES_SCRIPT }, { 0x164F2, 0x0027, NES_SCRIPT },
+ { 0x16519, 0x01BB, NES_SCRIPT }, { 0x1D135, 0x008D, NES_SCRIPT }, { 0x1D1C2, 0x0119, NES_SCRIPT }, { 0x1D2DB, 0x001C, NES_SCRIPT }, { 0x1D2F7, 0x0056, NES_SCRIPT },
+ { 0x1D34D, 0x0072, NES_SCRIPT }, { 0x1E4BF, 0x0028, NES_SCRIPT }, { 0x1E4E7, 0x01E0, NES_SCRIPT }, { 0x1E6C7, 0x0241, NES_SCRIPT }, { 0x2845D, 0x0071, NES_SCRIPT },
+ { 0x17E48, 0x004C, NES_SCRIPT }, { 0x13DE3, 0x0039, NES_SCRIPT }, { 0x18739, 0x0296, NES_SCRIPT }, { 0x189CF, 0x00C2, NES_SCRIPT }, { 0x18A91, 0x01A5, NES_SCRIPT },
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x19E13, 0x00F3, NES_SCRIPT }, { 0x21D5D, 0x00F6, NES_SCRIPT }, { 0x1E908, 0x00B8, NES_SCRIPT }, { 0x21E53, 0x0047, NES_SCRIPT },
+ { 0x2C183, 0x004D, NES_SCRIPT }, { 0x13E1C, 0x0024, NES_SCRIPT }, { 0x14D77, 0x0014, NES_SCRIPT }, { 0x176EF, 0x0059, NES_SCRIPT }, { 0x17748, 0x013F, NES_SCRIPT },
+ { 0x17887, 0x0009, NES_SCRIPT }, { 0x14D8B, 0x01D4, NES_SCRIPT }, { 0x2ACFB, 0x028D, NES_SCRIPT }, { 0x23203, 0x0779, NES_SCRIPT }, { 0x2C1D0, 0x001B, NES_SCRIPT },
+ { 0x2C1EB, 0x001F, NES_SCRIPT }, { 0x2C20A, 0x0024, NES_SCRIPT }, { 0x2C22E, 0x0019, NES_SCRIPT }, { 0x2C247, 0x0018, NES_SCRIPT }, { 0x2C25F, 0x001D, NES_SCRIPT },
+ { 0x2C27C, 0x0016, NES_SCRIPT }, { 0x2C292, 0x0027, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2C2B9, 0x0011, NES_SCRIPT }, { 0x17890, 0x00AA, NES_SCRIPT },
+ { 0x22846, 0x0066, NES_SCRIPT }, { 0x14F5F, 0x0083, NES_SCRIPT }, { 0x1F43C, 0x013A, NES_SCRIPT }, { 0x1FEB8, 0x001D, NES_SCRIPT }, { 0x1F576, 0x0098, NES_SCRIPT },
+ { 0x1F60E, 0x009B, NES_SCRIPT }, { 0x1793A, 0x006E, NES_SCRIPT }, { 0x179A8, 0x0033, NES_SCRIPT }, { 0x1F6A9, 0x00A5, NES_SCRIPT }, { 0x2C2CA, 0x00BA, NES_SCRIPT },
+ { 0x2C384, 0x00AC, NES_SCRIPT }, { 0x2C430, 0x008F, NES_SCRIPT }, { 0x20A2D, 0x01BE, NES_SCRIPT }, { 0x20BEB, 0x0158, NES_SCRIPT }, { 0x20D43, 0x0079, NES_SCRIPT },
+ { 0x20DBC, 0x002B, NES_SCRIPT }, { 0x20DE7, 0x00E8, NES_SCRIPT }, { 0x20ECF, 0x004A, NES_SCRIPT }, { 0x20F19, 0x0110, NES_SCRIPT }, { 0x21029, 0x0136, NES_SCRIPT },
+ { 0x2115F, 0x0152, NES_SCRIPT }, { 0x212B1, 0x01B3, NES_SCRIPT }, { 0x21464, 0x0032, NES_SCRIPT }, { 0x21496, 0x00A9, NES_SCRIPT }, { 0x2437F, 0x0133, NES_SCRIPT },
+ { 0x1BCBC, 0x0074, NES_SCRIPT }, { 0x1CC07, 0x0090, NES_SCRIPT }, { 0x28E43, 0x0054, NES_SCRIPT }, { 0x19F06, 0x00DB, NES_SCRIPT }, { 0x19FE1, 0x0080, NES_SCRIPT },
+ { 0x14FE2, 0x0057, NES_SCRIPT }, { 0x273F4, 0x031F, NES_SCRIPT }, { 0x1D9D4, 0x0238, NES_SCRIPT }, { 0x1DC0C, 0x00FE, NES_SCRIPT }, { 0x1DD0A, 0x005A, NES_SCRIPT },
+ { 0x1DD64, 0x01F5, NES_SCRIPT }, { 0x15039, 0x004D, NES_SCRIPT }, { 0x270DD, 0x0100, NES_SCRIPT }, { 0x21E9A, 0x0013, NES_SCRIPT }, { 0x2A1C3, 0x00F0, NES_SCRIPT },
+ { 0x244B2, 0x00E4, NES_SCRIPT }, { 0x2153F, 0x00EC, NES_SCRIPT }, { 0x24596, 0x0033, NES_SCRIPT }, { 0x2BC04, 0x0108, NES_SCRIPT }, { 0x245C9, 0x009F, NES_SCRIPT },
+ { 0x250F1, 0x0193, NES_SCRIPT }, { 0x1B5E5, 0x000D, NES_SCRIPT }, { 0x1B5F2, 0x000D, NES_SCRIPT }, { 0x2397C, 0x0199, NES_SCRIPT }, { 0x27A07, 0x0233, NES_SCRIPT },
+ { 0x23B15, 0x0171, NES_SCRIPT }, { 0x23C86, 0x01BC, NES_SCRIPT }, { 0x23E42, 0x0016, NES_SCRIPT }, { 0x2BD0C, 0x005B, NES_SCRIPT }, { 0x23E58, 0x0020, NES_SCRIPT },
+ { 0x27E3B, 0x00B9, NES_SCRIPT }, { 0x2A2B3, 0x03D3, NES_SCRIPT }, { 0x15086, 0x00E4, NES_SCRIPT }, { 0x2B70C, 0x0067, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT },
+ { 0x25284, 0x003C, NES_SCRIPT }, { 0x1E9C0, 0x0011, NES_SCRIPT }, { 0x13E40, 0x0018, NES_SCRIPT }, { 0x26621, 0x001F, NES_SCRIPT }, { 0x26640, 0x0054, NES_SCRIPT },
+ { 0x26694, 0x0173, NES_SCRIPT }, { 0x26807, 0x004B, NES_SCRIPT }, { 0x26852, 0x0190, NES_SCRIPT }, { 0x269E2, 0x0027, NES_SCRIPT }, { 0x26A09, 0x0041, NES_SCRIPT },
+ { 0x26A4A, 0x024E, NES_SCRIPT }, { 0x13E58, 0x001F, NES_SCRIPT }, { 0x24668, 0x002A, NES_SCRIPT }, { 0x1516A, 0x01C9, NES_SCRIPT }, { 0x192D0, 0x0031, NES_SCRIPT },
+ { 0x179DB, 0x0088, NES_SCRIPT }, { 0x2162B, 0x00D0, NES_SCRIPT }, { 0x1D3BF, 0x0018, NES_SCRIPT }, { 0x1D3D7, 0x0055, NES_SCRIPT }, { 0x18C36, 0x0005, NES_SCRIPT },
+ { 0x15333, 0x0113, NES_SCRIPT }, { 0x15446, 0x000B, NES_SCRIPT }, { 0x24692, 0x014D, NES_SCRIPT }, { 0x247DF, 0x0014, NES_SCRIPT }, { 0x1DF59, 0x0018, NES_SCRIPT },
+ { 0x247F3, 0x0027, NES_SCRIPT }, { 0x1A061, 0x0050, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2B773, 0x00A5, NES_SCRIPT }, { 0x2A686, 0x00BA, NES_SCRIPT },
+ { 0x2A740, 0x0140, NES_SCRIPT }, { 0x1979F, 0x00CA, NES_SCRIPT }, { 0x19869, 0x014D, NES_SCRIPT }, { 0x199B6, 0x0012, NES_SCRIPT }, { 0x2A880, 0x0005, NES_SCRIPT },
+ { 0x2A885, 0x0005, NES_SCRIPT }, { 0x2A88A, 0x0005, NES_SCRIPT }, { 0x2A88F, 0x0005, NES_SCRIPT }, { 0x2A894, 0x0005, NES_SCRIPT }, { 0x216FB, 0x0033, NES_SCRIPT },
+ { 0x2A899, 0x0005, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A89E, 0x009C, NES_SCRIPT }, { 0x2A93A, 0x009C, NES_SCRIPT }
+};
+static ScummNESFile::Resource res_scripts_ger[179] = {
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x295B0, 0x045A, NES_SCRIPT }, { 0x29A0A, 0x0218, NES_SCRIPT }, { 0x29C22, 0x00B1, NES_SCRIPT }, { 0x29CD3, 0x0408, NES_SCRIPT },
+ { 0x2A0DB, 0x01A1, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A27C, 0x005C, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A2D8, 0x0005, NES_SCRIPT },
+ { 0x2C169, 0x000D, NES_SCRIPT }, { 0x2C176, 0x000D, NES_SCRIPT }, { 0x186DB, 0x0040, NES_SCRIPT }, { 0x1871B, 0x0016, NES_SCRIPT }, { 0x1B5C2, 0x0046, NES_SCRIPT },
+ { 0x1EFB7, 0x00E3, NES_SCRIPT }, { 0x21D9D, 0x0069, NES_SCRIPT }, { 0x174F1, 0x0027, NES_SCRIPT }, { 0x1FE92, 0x0027, NES_SCRIPT }, { 0x1FEB9, 0x0027, NES_SCRIPT },
+ { 0x1BCE5, 0x0022, NES_SCRIPT }, { 0x13EF4, 0x0087, NES_SCRIPT }, { 0x223EC, 0x001F, NES_SCRIPT }, { 0x2240B, 0x008F, NES_SCRIPT }, { 0x192CC, 0x002B, NES_SCRIPT },
+ { 0x1CB08, 0x006E, NES_SCRIPT }, { 0x1CB76, 0x004E, NES_SCRIPT }, { 0x1CBC4, 0x004D, NES_SCRIPT }, { 0x1CC11, 0x0059, NES_SCRIPT }, { 0x204AA, 0x0080, NES_SCRIPT },
+ { 0x1628E, 0x0035, NES_SCRIPT }, { 0x162C3, 0x001C, NES_SCRIPT }, { 0x162DF, 0x0014, NES_SCRIPT }, { 0x162F3, 0x001C, NES_SCRIPT }, { 0x1630F, 0x0027, NES_SCRIPT },
+ { 0x16336, 0x01D1, NES_SCRIPT }, { 0x1D199, 0x00A0, NES_SCRIPT }, { 0x1D239, 0x011C, NES_SCRIPT }, { 0x1D355, 0x001C, NES_SCRIPT }, { 0x1D371, 0x0056, NES_SCRIPT },
+ { 0x1D3C7, 0x0072, NES_SCRIPT }, { 0x1E4FA, 0x0028, NES_SCRIPT }, { 0x1E522, 0x019D, NES_SCRIPT }, { 0x1E6BF, 0x023B, NES_SCRIPT }, { 0x2845D, 0x0071, NES_SCRIPT },
+ { 0x17C50, 0x0052, NES_SCRIPT }, { 0x15AC9, 0x0039, NES_SCRIPT }, { 0x18731, 0x02E7, NES_SCRIPT }, { 0x18A18, 0x00BC, NES_SCRIPT }, { 0x18AD4, 0x01A2, NES_SCRIPT },
+ { 0x00000, 0x0000, NES_SCRIPT }, { 0x19E45, 0x00F8, NES_SCRIPT }, { 0x21E06, 0x00F7, NES_SCRIPT }, { 0x1E8FA, 0x00B5, NES_SCRIPT }, { 0x21EFD, 0x0047, NES_SCRIPT },
+ { 0x2C183, 0x004D, NES_SCRIPT }, { 0x15B02, 0x0024, NES_SCRIPT }, { 0x14D64, 0x0014, NES_SCRIPT }, { 0x17518, 0x005E, NES_SCRIPT }, { 0x17576, 0x0125, NES_SCRIPT },
+ { 0x1769B, 0x0009, NES_SCRIPT }, { 0x14D78, 0x01C7, NES_SCRIPT }, { 0x2ADCE, 0x0263, NES_SCRIPT }, { 0x232AF, 0x077F, NES_SCRIPT }, { 0x2C1D0, 0x001E, NES_SCRIPT },
+ { 0x2C1EE, 0x0024, NES_SCRIPT }, { 0x2C212, 0x002E, NES_SCRIPT }, { 0x2C240, 0x0022, NES_SCRIPT }, { 0x2C262, 0x0013, NES_SCRIPT }, { 0x2C275, 0x001E, NES_SCRIPT },
+ { 0x2C293, 0x0016, NES_SCRIPT }, { 0x2C2A9, 0x0027, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2C2D0, 0x0012, NES_SCRIPT }, { 0x176A4, 0x00A4, NES_SCRIPT },
+ { 0x228F8, 0x0066, NES_SCRIPT }, { 0x14F3F, 0x007F, NES_SCRIPT }, { 0x1F428, 0x013A, NES_SCRIPT }, { 0x1FEE0, 0x001D, NES_SCRIPT }, { 0x1F562, 0x00A0, NES_SCRIPT },
+ { 0x1F602, 0x00A4, NES_SCRIPT }, { 0x17748, 0x0076, NES_SCRIPT }, { 0x177BE, 0x0036, NES_SCRIPT }, { 0x1F6A6, 0x00B9, NES_SCRIPT }, { 0x2C2E2, 0x00CB, NES_SCRIPT },
+ { 0x2C3AD, 0x00B7, NES_SCRIPT }, { 0x2C464, 0x008A, NES_SCRIPT }, { 0x20A58, 0x01BD, NES_SCRIPT }, { 0x20C15, 0x0181, NES_SCRIPT }, { 0x20D96, 0x0078, NES_SCRIPT },
+ { 0x20E0E, 0x003C, NES_SCRIPT }, { 0x20E4A, 0x00E9, NES_SCRIPT }, { 0x20F33, 0x0046, NES_SCRIPT }, { 0x20F79, 0x00F6, NES_SCRIPT }, { 0x2106F, 0x0118, NES_SCRIPT },
+ { 0x21187, 0x015B, NES_SCRIPT }, { 0x212E2, 0x01AC, NES_SCRIPT }, { 0x2148E, 0x003F, NES_SCRIPT }, { 0x214CD, 0x00A9, NES_SCRIPT }, { 0x2436F, 0x0126, NES_SCRIPT },
+ { 0x1BD07, 0x0075, NES_SCRIPT }, { 0x1CC6A, 0x009B, NES_SCRIPT }, { 0x28F5D, 0x0054, NES_SCRIPT }, { 0x19F3D, 0x00E1, NES_SCRIPT }, { 0x1A01E, 0x0086, NES_SCRIPT },
+ { 0x14FBE, 0x0057, NES_SCRIPT }, { 0x27326, 0x033D, NES_SCRIPT }, { 0x1DA51, 0x023B, NES_SCRIPT }, { 0x1DC8C, 0x00FB, NES_SCRIPT }, { 0x1DD87, 0x0056, NES_SCRIPT },
+ { 0x1DDDD, 0x01E1, NES_SCRIPT }, { 0x15015, 0x004D, NES_SCRIPT }, { 0x27027, 0x00E8, NES_SCRIPT }, { 0x21F44, 0x0013, NES_SCRIPT }, { 0x2A2DD, 0x00F0, NES_SCRIPT },
+ { 0x24495, 0x00F8, NES_SCRIPT }, { 0x21576, 0x00F9, NES_SCRIPT }, { 0x2458D, 0x002B, NES_SCRIPT }, { 0x2BAA4, 0x010F, NES_SCRIPT }, { 0x245B8, 0x00A5, NES_SCRIPT },
+ { 0x250D0, 0x019C, NES_SCRIPT }, { 0x1B608, 0x000D, NES_SCRIPT }, { 0x1B615, 0x000D, NES_SCRIPT }, { 0x23A2E, 0x0185, NES_SCRIPT }, { 0x27957, 0x0212, NES_SCRIPT },
+ { 0x23BB3, 0x0158, NES_SCRIPT }, { 0x23D0B, 0x01C4, NES_SCRIPT }, { 0x23ECF, 0x0016, NES_SCRIPT }, { 0x2BBB3, 0x005A, NES_SCRIPT }, { 0x23EE5, 0x0020, NES_SCRIPT },
+ { 0x27D6A, 0x00A7, NES_SCRIPT }, { 0x2A3CD, 0x038C, NES_SCRIPT }, { 0x15062, 0x00F6, NES_SCRIPT }, { 0x2B5B2, 0x007B, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT },
+ { 0x2526C, 0x003C, NES_SCRIPT }, { 0x1E9AF, 0x0011, NES_SCRIPT }, { 0x15B26, 0x0018, NES_SCRIPT }, { 0x265F5, 0x001F, NES_SCRIPT }, { 0x26614, 0x0054, NES_SCRIPT },
+ { 0x26668, 0x018E, NES_SCRIPT }, { 0x267F6, 0x004B, NES_SCRIPT }, { 0x26841, 0x0196, NES_SCRIPT }, { 0x269D7, 0x0027, NES_SCRIPT }, { 0x269FE, 0x0041, NES_SCRIPT },
+ { 0x26A3F, 0x01A3, NES_SCRIPT }, { 0x15B3E, 0x001F, NES_SCRIPT }, { 0x2465D, 0x002A, NES_SCRIPT }, { 0x15158, 0x0198, NES_SCRIPT }, { 0x192F7, 0x0031, NES_SCRIPT },
+ { 0x177F4, 0x008C, NES_SCRIPT }, { 0x2166F, 0x00DA, NES_SCRIPT }, { 0x1D439, 0x0018, NES_SCRIPT }, { 0x1D451, 0x0054, NES_SCRIPT }, { 0x18C76, 0x0005, NES_SCRIPT },
+ { 0x152F0, 0x0126, NES_SCRIPT }, { 0x15416, 0x000B, NES_SCRIPT }, { 0x24687, 0x0137, NES_SCRIPT }, { 0x247BE, 0x0014, NES_SCRIPT }, { 0x1DFBE, 0x0018, NES_SCRIPT },
+ { 0x247D2, 0x0027, NES_SCRIPT }, { 0x1A0A4, 0x004D, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2B62D, 0x00A5, NES_SCRIPT }, { 0x2A759, 0x00BA, NES_SCRIPT },
+ { 0x2A813, 0x0140, NES_SCRIPT }, { 0x197CF, 0x00D0, NES_SCRIPT }, { 0x1989F, 0x014D, NES_SCRIPT }, { 0x199EC, 0x0012, NES_SCRIPT }, { 0x2A953, 0x0005, NES_SCRIPT },
+ { 0x2A958, 0x0005, NES_SCRIPT }, { 0x2A95D, 0x0005, NES_SCRIPT }, { 0x2A962, 0x0005, NES_SCRIPT }, { 0x2A967, 0x0005, NES_SCRIPT }, { 0x21749, 0x0033, NES_SCRIPT },
+ { 0x2A96C, 0x0005, NES_SCRIPT }, { 0x00000, 0x0000, NES_SCRIPT }, { 0x2A971, 0x009C, NES_SCRIPT }, { 0x2AA0D, 0x009C, NES_SCRIPT }
+};
+#endif
+static ScummNESFile::Resource *res_scripts[ScummNESFile::kROMsetNum] = {
+ res_scripts_usa,
+ res_scripts_eur,
+ res_scripts_swe,
+ res_scripts_fra,
+ res_scripts_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_sounds_usa;
+static ScummNESFile::Resource *res_sounds_eur;
+static ScummNESFile::Resource *res_sounds_swe;
+static ScummNESFile::Resource *res_sounds_fra;
+static ScummNESFile::Resource *res_sounds_ger;
+#else
+static ScummNESFile::Resource res_sounds_usa[82] = {
+ { 0x0FFE8, 0x000A, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND },
+ { 0x30ECA, 0x0832, NES_SOUND }, { 0x17FCA, 0x0011, NES_SOUND }, { 0x27E0B, 0x0073, NES_SOUND }, { 0x17FDB, 0x0011, NES_SOUND }, { 0x17FEC, 0x0011, NES_SOUND },
+ { 0x27E7E, 0x0056, NES_SOUND }, { 0x27ED4, 0x001F, NES_SOUND }, { 0x23FEE, 0x0011, NES_SOUND }, { 0x0FFF2, 0x000A, NES_SOUND }, { 0x27EF3, 0x000A, NES_SOUND },
+ { 0x27EFD, 0x0019, NES_SOUND }, { 0x27F16, 0x004B, NES_SOUND }, { 0x27F61, 0x000A, NES_SOUND }, { 0x27F6B, 0x000F, NES_SOUND }, { 0x27F7A, 0x001D, NES_SOUND },
+ { 0x27F97, 0x0045, NES_SOUND }, { 0x27FDC, 0x000F, NES_SOUND }, { 0x2FD42, 0x001B, NES_SOUND }, { 0x2FD5D, 0x0033, NES_SOUND }, { 0x27FEB, 0x0011, NES_SOUND },
+ { 0x2BFEF, 0x000F, NES_SOUND }, { 0x2FD90, 0x0075, NES_SOUND }, { 0x2FE05, 0x0014, NES_SOUND }, { 0x0FFE8, 0x000A, NES_SOUND }, { 0x2FE19, 0x00FF, NES_SOUND },
+ { 0x2FF18, 0x000F, NES_SOUND }, { 0x2FF27, 0x000F, NES_SOUND }, { 0x2FF36, 0x0092, NES_SOUND }, { 0x2FF36, 0x0092, NES_SOUND }, { 0x2FFC8, 0x002D, NES_SOUND },
+ { 0x316FC, 0x00F8, NES_SOUND }, { 0x317F4, 0x0016, NES_SOUND }, { 0x3180A, 0x0011, NES_SOUND }, { 0x3181B, 0x004B, NES_SOUND }, { 0x31866, 0x0011, NES_SOUND },
+ { 0x31877, 0x003B, NES_SOUND }, { 0x318B2, 0x008A, NES_SOUND }, { 0x3193C, 0x0011, NES_SOUND }, { 0x3194D, 0x000F, NES_SOUND }, { 0x3195C, 0x00A2, NES_SOUND },
+ { 0x319FE, 0x00D3, NES_SOUND }, { 0x31AD1, 0x0097, NES_SOUND }, { 0x2BFEF, 0x000F, NES_SOUND }, { 0x3195C, 0x00A2, NES_SOUND }, { 0x31B68, 0x05D1, NES_SOUND },
+ { 0x31B68, 0x05D1, NES_SOUND }, { 0x32139, 0x0011, NES_SOUND }, { 0x0FFE8, 0x000A, NES_SOUND }, { 0x2FD90, 0x0075, NES_SOUND }, { 0x27ED4, 0x001F, NES_SOUND },
+ { 0x3214A, 0x098E, NES_SOUND }, { 0x3181B, 0x004B, NES_SOUND }, { 0x32AD8, 0x0011, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x32AE9, 0x000F, NES_SOUND },
+ { 0x32AF8, 0x002F, NES_SOUND }, { 0x32B27, 0x001D, NES_SOUND }, { 0x32B44, 0x0018, NES_SOUND }, { 0x32B5C, 0x0016, NES_SOUND }, { 0x32B72, 0x001B, NES_SOUND },
+ { 0x32B8D, 0x0088, NES_SOUND }, { 0x32C15, 0x0065, NES_SOUND }, { 0x32C7A, 0x0065, NES_SOUND }, { 0x32CDF, 0x0073, NES_SOUND }, { 0x32D52, 0x00F9, NES_SOUND },
+ { 0x32E4B, 0x049E, NES_SOUND }, { 0x34001, 0x0EA8, NES_SOUND }, { 0x332E9, 0x0B18, NES_SOUND }, { 0x34EA9, 0x0B9C, NES_SOUND }, { 0x35A45, 0x0C6B, NES_SOUND },
+ { 0x366B0, 0x0E56, NES_SOUND }, { 0x38001, 0x0C70, NES_SOUND }, { 0x38C71, 0x0DEC, NES_SOUND }, { 0x39A5D, 0x0B77, NES_SOUND }, { 0x37506, 0x042F, NES_SOUND },
+ { 0x3A5D4, 0x0AC5, NES_SOUND }, { 0x3B099, 0x0BE4, NES_SOUND }
+};
+static ScummNESFile::Resource res_sounds_eur[82] = {
+ { 0x0BF54, 0x000A, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND },
+ { 0x30ECA, 0x0832, NES_SOUND }, { 0x0BF5E, 0x0011, NES_SOUND }, { 0x27ECB, 0x0073, NES_SOUND }, { 0x0BF6F, 0x0011, NES_SOUND }, { 0x0FF5D, 0x0011, NES_SOUND },
+ { 0x316FC, 0x0056, NES_SOUND }, { 0x13F4E, 0x001F, NES_SOUND }, { 0x0FF6E, 0x0011, NES_SOUND }, { 0x13F6D, 0x000A, NES_SOUND }, { 0x1BF47, 0x000A, NES_SOUND },
+ { 0x1BF51, 0x0019, NES_SOUND }, { 0x31752, 0x004B, NES_SOUND }, { 0x1BF6A, 0x000A, NES_SOUND }, { 0x27F3E, 0x000F, NES_SOUND }, { 0x27F4D, 0x001D, NES_SOUND },
+ { 0x3179D, 0x0045, NES_SOUND }, { 0x27F6A, 0x000F, NES_SOUND }, { 0x2BF40, 0x001B, NES_SOUND }, { 0x317E2, 0x0033, NES_SOUND }, { 0x2BF5B, 0x0011, NES_SOUND },
+ { 0x2BF6C, 0x000F, NES_SOUND }, { 0x31815, 0x0075, NES_SOUND }, { 0x2FF6C, 0x0014, NES_SOUND }, { 0x0BF54, 0x000A, NES_SOUND }, { 0x3188A, 0x00FF, NES_SOUND },
+ { 0x31989, 0x000F, NES_SOUND }, { 0x31998, 0x000F, NES_SOUND }, { 0x319A7, 0x0092, NES_SOUND }, { 0x319A7, 0x0092, NES_SOUND }, { 0x31A39, 0x002D, NES_SOUND },
+ { 0x31A66, 0x00F8, NES_SOUND }, { 0x31B5E, 0x0016, NES_SOUND }, { 0x31B74, 0x0011, NES_SOUND }, { 0x31B85, 0x004B, NES_SOUND }, { 0x31BD0, 0x0011, NES_SOUND },
+ { 0x31BE1, 0x003B, NES_SOUND }, { 0x31C1C, 0x008A, NES_SOUND }, { 0x31CA6, 0x0011, NES_SOUND }, { 0x31CB7, 0x000F, NES_SOUND }, { 0x31CC6, 0x00A2, NES_SOUND },
+ { 0x31D68, 0x00D3, NES_SOUND }, { 0x31E3B, 0x0097, NES_SOUND }, { 0x2BF6C, 0x000F, NES_SOUND }, { 0x31CC6, 0x00A2, NES_SOUND }, { 0x31ED2, 0x05D1, NES_SOUND },
+ { 0x31ED2, 0x05D1, NES_SOUND }, { 0x324A3, 0x0011, NES_SOUND }, { 0x0BF54, 0x000A, NES_SOUND }, { 0x31815, 0x0075, NES_SOUND }, { 0x13F4E, 0x001F, NES_SOUND },
+ { 0x324B4, 0x098E, NES_SOUND }, { 0x31B85, 0x004B, NES_SOUND }, { 0x32E42, 0x0011, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x32E53, 0x000F, NES_SOUND },
+ { 0x32E62, 0x002F, NES_SOUND }, { 0x32E91, 0x001D, NES_SOUND }, { 0x32EAE, 0x0018, NES_SOUND }, { 0x32EC6, 0x0016, NES_SOUND }, { 0x32EDC, 0x001B, NES_SOUND },
+ { 0x32EF7, 0x0088, NES_SOUND }, { 0x32F7F, 0x0065, NES_SOUND }, { 0x32FE4, 0x0065, NES_SOUND }, { 0x33049, 0x0073, NES_SOUND }, { 0x330BC, 0x00F9, NES_SOUND },
+ { 0x331B5, 0x049E, NES_SOUND }, { 0x34001, 0x0EA8, NES_SOUND }, { 0x34EA9, 0x0B18, NES_SOUND }, { 0x359C1, 0x0B9C, NES_SOUND }, { 0x3655D, 0x0C6B, NES_SOUND },
+ { 0x38001, 0x0E56, NES_SOUND }, { 0x371C8, 0x0C70, NES_SOUND }, { 0x38E57, 0x0DEC, NES_SOUND }, { 0x39C43, 0x0B77, NES_SOUND }, { 0x33653, 0x042F, NES_SOUND },
+ { 0x3A7BA, 0x0AC5, NES_SOUND }, { 0x3B27F, 0x0BE4, NES_SOUND }
+};
+static ScummNESFile::Resource res_sounds_swe[82] = {
+ { 0x0BF58, 0x000A, NES_SOUND }, { 0x30352, 0x0832, NES_SOUND }, { 0x30352, 0x0832, NES_SOUND }, { 0x30352, 0x0832, NES_SOUND }, { 0x30352, 0x0832, NES_SOUND },
+ { 0x30352, 0x0832, NES_SOUND }, { 0x0BF62, 0x0011, NES_SOUND }, { 0x27E5F, 0x0073, NES_SOUND }, { 0x17F5A, 0x0011, NES_SOUND }, { 0x17F6B, 0x0011, NES_SOUND },
+ { 0x27ED2, 0x0056, NES_SOUND }, { 0x1BF55, 0x001F, NES_SOUND }, { 0x23F66, 0x0011, NES_SOUND }, { 0x0BF73, 0x000A, NES_SOUND }, { 0x1BF74, 0x000A, NES_SOUND },
+ { 0x27F28, 0x0019, NES_SOUND }, { 0x2BF0A, 0x004B, NES_SOUND }, { 0x1FF71, 0x000A, NES_SOUND }, { 0x27F41, 0x000F, NES_SOUND }, { 0x27F50, 0x001D, NES_SOUND },
+ { 0x2FEAA, 0x0045, NES_SOUND }, { 0x27F6D, 0x000F, NES_SOUND }, { 0x2BF55, 0x001B, NES_SOUND }, { 0x2FEEF, 0x0033, NES_SOUND }, { 0x2FF22, 0x0011, NES_SOUND },
+ { 0x2BF70, 0x000F, NES_SOUND }, { 0x30B84, 0x0075, NES_SOUND }, { 0x2FF33, 0x0014, NES_SOUND }, { 0x0BF58, 0x000A, NES_SOUND }, { 0x30BF9, 0x00FF, NES_SOUND },
+ { 0x2FF47, 0x000F, NES_SOUND }, { 0x2FF56, 0x000F, NES_SOUND }, { 0x30CF8, 0x0092, NES_SOUND }, { 0x30CF8, 0x0092, NES_SOUND }, { 0x30D8A, 0x002D, NES_SOUND },
+ { 0x30DB7, 0x00F8, NES_SOUND }, { 0x2FF65, 0x0016, NES_SOUND }, { 0x30EAF, 0x0011, NES_SOUND }, { 0x30EC0, 0x004B, NES_SOUND }, { 0x30F0B, 0x0011, NES_SOUND },
+ { 0x30F1C, 0x003B, NES_SOUND }, { 0x30F57, 0x008A, NES_SOUND }, { 0x30FE1, 0x0011, NES_SOUND }, { 0x30FF2, 0x000F, NES_SOUND }, { 0x31001, 0x00A2, NES_SOUND },
+ { 0x310A3, 0x00D3, NES_SOUND }, { 0x31176, 0x0097, NES_SOUND }, { 0x2BF70, 0x000F, NES_SOUND }, { 0x31001, 0x00A2, NES_SOUND }, { 0x3120D, 0x05D1, NES_SOUND },
+ { 0x3120D, 0x05D1, NES_SOUND }, { 0x317DE, 0x0011, NES_SOUND }, { 0x0BF58, 0x000A, NES_SOUND }, { 0x30B84, 0x0075, NES_SOUND }, { 0x1BF55, 0x001F, NES_SOUND },
+ { 0x317EF, 0x098E, NES_SOUND }, { 0x30EC0, 0x004B, NES_SOUND }, { 0x3217D, 0x0011, NES_SOUND }, { 0x30352, 0x0832, NES_SOUND }, { 0x3218E, 0x000F, NES_SOUND },
+ { 0x3219D, 0x002F, NES_SOUND }, { 0x321CC, 0x001D, NES_SOUND }, { 0x321E9, 0x0018, NES_SOUND }, { 0x32201, 0x0016, NES_SOUND }, { 0x32217, 0x001B, NES_SOUND },
+ { 0x32232, 0x0088, NES_SOUND }, { 0x322BA, 0x0065, NES_SOUND }, { 0x3231F, 0x0065, NES_SOUND }, { 0x32384, 0x0073, NES_SOUND }, { 0x323F7, 0x00F9, NES_SOUND },
+ { 0x324F0, 0x049E, NES_SOUND }, { 0x3298E, 0x0EA8, NES_SOUND }, { 0x34001, 0x0B18, NES_SOUND }, { 0x34B19, 0x0B9C, NES_SOUND }, { 0x356B5, 0x0C6B, NES_SOUND },
+ { 0x36320, 0x0E56, NES_SOUND }, { 0x37176, 0x0C70, NES_SOUND }, { 0x38001, 0x0DEC, NES_SOUND }, { 0x38DED, 0x0B77, NES_SOUND }, { 0x33836, 0x042F, NES_SOUND },
+ { 0x39964, 0x0AC5, NES_SOUND }, { 0x3A429, 0x0BE4, NES_SOUND }
+};
+static ScummNESFile::Resource res_sounds_fra[82] = {
+ { 0x07F74, 0x000A, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND },
+ { 0x30ECA, 0x0832, NES_SOUND }, { 0x0BF6C, 0x0011, NES_SOUND }, { 0x1BEFA, 0x0073, NES_SOUND }, { 0x17F10, 0x0011, NES_SOUND }, { 0x17F21, 0x0011, NES_SOUND },
+ { 0x1FED5, 0x0056, NES_SOUND }, { 0x17F32, 0x001F, NES_SOUND }, { 0x17F51, 0x0011, NES_SOUND }, { 0x0FF76, 0x000A, NES_SOUND }, { 0x17F62, 0x000A, NES_SOUND },
+ { 0x1FF2B, 0x0019, NES_SOUND }, { 0x23E78, 0x004B, NES_SOUND }, { 0x17F6C, 0x000A, NES_SOUND }, { 0x1BF6D, 0x000F, NES_SOUND }, { 0x1FF44, 0x001D, NES_SOUND },
+ { 0x23EC3, 0x0045, NES_SOUND }, { 0x1FF61, 0x000F, NES_SOUND }, { 0x23F08, 0x001B, NES_SOUND }, { 0x23F23, 0x0033, NES_SOUND }, { 0x23F56, 0x0011, NES_SOUND },
+ { 0x1FF70, 0x000F, NES_SOUND }, { 0x27EF4, 0x0075, NES_SOUND }, { 0x23F67, 0x0014, NES_SOUND }, { 0x07F74, 0x000A, NES_SOUND }, { 0x2FB83, 0x00FF, NES_SOUND },
+ { 0x27F69, 0x000F, NES_SOUND }, { 0x2BF70, 0x000F, NES_SOUND }, { 0x2FC82, 0x0092, NES_SOUND }, { 0x2FC82, 0x0092, NES_SOUND }, { 0x2FD14, 0x002D, NES_SOUND },
+ { 0x2FD41, 0x00F8, NES_SOUND }, { 0x2FE39, 0x0016, NES_SOUND }, { 0x2FE4F, 0x0011, NES_SOUND }, { 0x2FE60, 0x004B, NES_SOUND }, { 0x2FEAB, 0x0011, NES_SOUND },
+ { 0x2FEBC, 0x003B, NES_SOUND }, { 0x316FC, 0x008A, NES_SOUND }, { 0x2FEF7, 0x0011, NES_SOUND }, { 0x2FF08, 0x000F, NES_SOUND }, { 0x31786, 0x00A2, NES_SOUND },
+ { 0x31828, 0x00D3, NES_SOUND }, { 0x318FB, 0x0097, NES_SOUND }, { 0x1FF70, 0x000F, NES_SOUND }, { 0x31786, 0x00A2, NES_SOUND }, { 0x31992, 0x05D1, NES_SOUND },
+ { 0x31992, 0x05D1, NES_SOUND }, { 0x2FF17, 0x0011, NES_SOUND }, { 0x07F74, 0x000A, NES_SOUND }, { 0x27EF4, 0x0075, NES_SOUND }, { 0x17F32, 0x001F, NES_SOUND },
+ { 0x31F63, 0x098E, NES_SOUND }, { 0x2FE60, 0x004B, NES_SOUND }, { 0x2FF28, 0x0011, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x2FF39, 0x000F, NES_SOUND },
+ { 0x2FF48, 0x002F, NES_SOUND }, { 0x328F1, 0x001D, NES_SOUND }, { 0x3290E, 0x0018, NES_SOUND }, { 0x32926, 0x0016, NES_SOUND }, { 0x3293C, 0x001B, NES_SOUND },
+ { 0x32957, 0x0088, NES_SOUND }, { 0x329DF, 0x0065, NES_SOUND }, { 0x32A44, 0x0065, NES_SOUND }, { 0x32AA9, 0x0073, NES_SOUND }, { 0x32B1C, 0x00F9, NES_SOUND },
+ { 0x32C15, 0x049E, NES_SOUND }, { 0x330B3, 0x0EA8, NES_SOUND }, { 0x34001, 0x0B18, NES_SOUND }, { 0x34B19, 0x0B9C, NES_SOUND }, { 0x356B5, 0x0C6B, NES_SOUND },
+ { 0x36320, 0x0E56, NES_SOUND }, { 0x37176, 0x0C70, NES_SOUND }, { 0x38001, 0x0DEC, NES_SOUND }, { 0x38DED, 0x0B77, NES_SOUND }, { 0x39964, 0x042F, NES_SOUND },
+ { 0x39D93, 0x0AC5, NES_SOUND }, { 0x3A858, 0x0BE4, NES_SOUND }
+};
+static ScummNESFile::Resource res_sounds_ger[82] = {
+ { 0x0BF6D, 0x000A, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND },
+ { 0x30ECA, 0x0832, NES_SOUND }, { 0x23F05, 0x0011, NES_SOUND }, { 0x2FA49, 0x0073, NES_SOUND }, { 0x23F16, 0x0011, NES_SOUND }, { 0x23F27, 0x0011, NES_SOUND },
+ { 0x2FABC, 0x0056, NES_SOUND }, { 0x23F38, 0x001F, NES_SOUND }, { 0x23F57, 0x0011, NES_SOUND }, { 0x0FF76, 0x000A, NES_SOUND }, { 0x17F71, 0x000A, NES_SOUND },
+ { 0x2BF2F, 0x0019, NES_SOUND }, { 0x2FB12, 0x004B, NES_SOUND }, { 0x23F68, 0x000A, NES_SOUND }, { 0x2BF48, 0x000F, NES_SOUND }, { 0x2BF57, 0x001D, NES_SOUND },
+ { 0x2FB5D, 0x0045, NES_SOUND }, { 0x2FBA2, 0x000F, NES_SOUND }, { 0x2FBB1, 0x001B, NES_SOUND }, { 0x2FBCC, 0x0033, NES_SOUND }, { 0x2FBFF, 0x0011, NES_SOUND },
+ { 0x2FC10, 0x000F, NES_SOUND }, { 0x2FC1F, 0x0075, NES_SOUND }, { 0x2FC94, 0x0014, NES_SOUND }, { 0x0BF6D, 0x000A, NES_SOUND }, { 0x2FCA8, 0x00FF, NES_SOUND },
+ { 0x2FDA7, 0x000F, NES_SOUND }, { 0x2FDB6, 0x000F, NES_SOUND }, { 0x2FDC5, 0x0092, NES_SOUND }, { 0x2FDC5, 0x0092, NES_SOUND }, { 0x2FE57, 0x002D, NES_SOUND },
+ { 0x2FE84, 0x00F8, NES_SOUND }, { 0x316FC, 0x0016, NES_SOUND }, { 0x31712, 0x0011, NES_SOUND }, { 0x31723, 0x004B, NES_SOUND }, { 0x3176E, 0x0011, NES_SOUND },
+ { 0x3177F, 0x003B, NES_SOUND }, { 0x317BA, 0x008A, NES_SOUND }, { 0x31844, 0x0011, NES_SOUND }, { 0x31855, 0x000F, NES_SOUND }, { 0x31864, 0x00A2, NES_SOUND },
+ { 0x31906, 0x00D3, NES_SOUND }, { 0x319D9, 0x0097, NES_SOUND }, { 0x2FC10, 0x000F, NES_SOUND }, { 0x31864, 0x00A2, NES_SOUND }, { 0x31A70, 0x05D1, NES_SOUND },
+ { 0x31A70, 0x05D1, NES_SOUND }, { 0x32041, 0x0011, NES_SOUND }, { 0x0BF6D, 0x000A, NES_SOUND }, { 0x2FC1F, 0x0075, NES_SOUND }, { 0x23F38, 0x001F, NES_SOUND },
+ { 0x32052, 0x098E, NES_SOUND }, { 0x31723, 0x004B, NES_SOUND }, { 0x329E0, 0x0011, NES_SOUND }, { 0x30ECA, 0x0832, NES_SOUND }, { 0x329F1, 0x000F, NES_SOUND },
+ { 0x32A00, 0x002F, NES_SOUND }, { 0x32A2F, 0x001D, NES_SOUND }, { 0x32A4C, 0x0018, NES_SOUND }, { 0x32A64, 0x0016, NES_SOUND }, { 0x32A7A, 0x001B, NES_SOUND },
+ { 0x32A95, 0x0088, NES_SOUND }, { 0x32B1D, 0x0065, NES_SOUND }, { 0x32B82, 0x0065, NES_SOUND }, { 0x32BE7, 0x0073, NES_SOUND }, { 0x32C5A, 0x00F9, NES_SOUND },
+ { 0x32D53, 0x049E, NES_SOUND }, { 0x34001, 0x0EA8, NES_SOUND }, { 0x331F1, 0x0B18, NES_SOUND }, { 0x34EA9, 0x0B9C, NES_SOUND }, { 0x35A45, 0x0C6B, NES_SOUND },
+ { 0x366B0, 0x0E56, NES_SOUND }, { 0x38001, 0x0C70, NES_SOUND }, { 0x38C71, 0x0DEC, NES_SOUND }, { 0x39A5D, 0x0B77, NES_SOUND }, { 0x37506, 0x042F, NES_SOUND },
+ { 0x3A5D4, 0x0AC5, NES_SOUND }, { 0x3B099, 0x0BE4, NES_SOUND }
+};
+#endif
+static ScummNESFile::Resource *res_sounds[ScummNESFile::kROMsetNum] = {
+ res_sounds_usa,
+ res_sounds_eur,
+ res_sounds_swe,
+ res_sounds_fra,
+ res_sounds_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_costumes_usa;
+static ScummNESFile::Resource *res_costumes_eur;
+static ScummNESFile::Resource *res_costumes_swe;
+static ScummNESFile::Resource *res_costumes_fra;
+static ScummNESFile::Resource *res_costumes_ger;
+#else
+static ScummNESFile::Resource res_costumes_usa[25] = {
+ { 0x17F05, 0x0055, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME },
+ { 0x17F05, 0x0055, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME }, { 0x13FAB, 0x004B, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME },
+ { 0x17F05, 0x0055, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME }, { 0x17F5A, 0x0036, NES_COSTUME }, { 0x17F90, 0x003A, NES_COSTUME }, { 0x17F90, 0x003A, NES_COSTUME },
+ { 0x17F05, 0x0055, NES_COSTUME }, { 0x1BF87, 0x003B, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME }, { 0x23FA9, 0x0045, NES_COSTUME }, { 0x1FFBD, 0x0040, NES_COSTUME },
+ { 0x1BFC2, 0x003C, NES_COSTUME }, { 0x17F90, 0x003A, NES_COSTUME }, { 0x17F90, 0x003A, NES_COSTUME }, { 0x17F05, 0x0055, NES_COSTUME }, { 0x13FAB, 0x004B, NES_COSTUME }
+};
+static ScummNESFile::Resource res_costumes_eur[25] = {
+ { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME },
+ { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0FEA2, 0x004B, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME },
+ { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0FEED, 0x0036, NES_COSTUME }, { 0x0FF23, 0x003A, NES_COSTUME }, { 0x0FF23, 0x003A, NES_COSTUME },
+ { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x13F13, 0x003B, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x23F2F, 0x0045, NES_COSTUME }, { 0x1FF3E, 0x0040, NES_COSTUME },
+ { 0x27E8F, 0x003C, NES_COSTUME }, { 0x0FF23, 0x003A, NES_COSTUME }, { 0x0FF23, 0x003A, NES_COSTUME }, { 0x0BEFF, 0x0055, NES_COSTUME }, { 0x0FEA2, 0x004B, NES_COSTUME }
+};
+static ScummNESFile::Resource res_costumes_swe[25] = {
+ { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME },
+ { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x17E9A, 0x004B, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME },
+ { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x0FF4A, 0x0036, NES_COSTUME }, { 0x17EE5, 0x003A, NES_COSTUME }, { 0x17EE5, 0x003A, NES_COSTUME },
+ { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x17F1F, 0x003B, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x1BE94, 0x0045, NES_COSTUME }, { 0x1BED9, 0x0040, NES_COSTUME },
+ { 0x1BF19, 0x003C, NES_COSTUME }, { 0x17EE5, 0x003A, NES_COSTUME }, { 0x17EE5, 0x003A, NES_COSTUME }, { 0x0FEF5, 0x0055, NES_COSTUME }, { 0x17E9A, 0x004B, NES_COSTUME }
+};
+static ScummNESFile::Resource res_costumes_fra[25] = {
+ { 0x0BF17, 0x0055, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME },
+ { 0x0BF17, 0x0055, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME }, { 0x13E77, 0x004B, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME },
+ { 0x0BF17, 0x0055, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME }, { 0x07F3E, 0x0036, NES_COSTUME }, { 0x13EC2, 0x003A, NES_COSTUME }, { 0x13EC2, 0x003A, NES_COSTUME },
+ { 0x0BF17, 0x0055, NES_COSTUME }, { 0x13EFC, 0x003B, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME }, { 0x13F37, 0x0045, NES_COSTUME }, { 0x17E94, 0x0040, NES_COSTUME },
+ { 0x17ED4, 0x003C, NES_COSTUME }, { 0x13EC2, 0x003A, NES_COSTUME }, { 0x13EC2, 0x003A, NES_COSTUME }, { 0x0BF17, 0x0055, NES_COSTUME }, { 0x13E77, 0x004B, NES_COSTUME }
+};
+static ScummNESFile::Resource res_costumes_ger[25] = {
+ { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME },
+ { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x17E6C, 0x004B, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME },
+ { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x0FF40, 0x0036, NES_COSTUME }, { 0x17EB7, 0x003A, NES_COSTUME }, { 0x17EB7, 0x003A, NES_COSTUME },
+ { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x17EF1, 0x003B, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x17F2C, 0x0045, NES_COSTUME }, { 0x1FEFD, 0x0040, NES_COSTUME },
+ { 0x1FF3D, 0x003C, NES_COSTUME }, { 0x17EB7, 0x003A, NES_COSTUME }, { 0x17EB7, 0x003A, NES_COSTUME }, { 0x0FEEB, 0x0055, NES_COSTUME }, { 0x17E6C, 0x004B, NES_COSTUME }
+};
+#endif
+static ScummNESFile::Resource *res_costumes[ScummNESFile::kROMsetNum] = {
+ res_costumes_usa,
+ res_costumes_eur,
+ res_costumes_swe,
+ res_costumes_fra,
+ res_costumes_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_globdata_usa;
+static ScummNESFile::Resource *res_globdata_eur;
+static ScummNESFile::Resource *res_globdata_swe;
+static ScummNESFile::Resource *res_globdata_fra;
+static ScummNESFile::Resource *res_globdata_ger;
+#else
+static ScummNESFile::Resource res_globdata_usa[1] = { { 0x2CA11, 0x0307, NES_GLOBDATA } };
+static ScummNESFile::Resource res_globdata_eur[1] = { { 0x2CA11, 0x0307, NES_GLOBDATA } };
+static ScummNESFile::Resource res_globdata_swe[1] = { { 0x2C001, 0x0307, NES_GLOBDATA } };
+static ScummNESFile::Resource res_globdata_fra[1] = { { 0x2C628, 0x0307, NES_GLOBDATA } };
+static ScummNESFile::Resource res_globdata_ger[1] = { { 0x2C4EE, 0x0307, NES_GLOBDATA } };
+#endif
+static ScummNESFile::Resource *res_globdata[ScummNESFile::kROMsetNum] = {
+ res_globdata_usa,
+ res_globdata_eur,
+ res_globdata_swe,
+ res_globdata_fra,
+ res_globdata_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_sprpals_usa;
+static ScummNESFile::Resource *res_sprpals_eur;
+static ScummNESFile::Resource *res_sprpals_swe;
+static ScummNESFile::Resource *res_sprpals_fra;
+static ScummNESFile::Resource *res_sprpals_ger;
+#else
+// sprite palette data
+static ScummNESFile::Resource res_sprpals_usa[2] = { { 0x0BFC1, 0x0010, NES_SPRPALS }, { 0x0BFD1, 0x0010, NES_SPRPALS } };
+static ScummNESFile::Resource res_sprpals_eur[2] = { { 0x07F61, 0x0010, NES_SPRPALS }, { 0x0BEB2, 0x0010, NES_SPRPALS } };
+static ScummNESFile::Resource res_sprpals_swe[2] = { { 0x07F55, 0x0010, NES_SPRPALS }, { 0x07F65, 0x0010, NES_SPRPALS } };
+static ScummNESFile::Resource res_sprpals_fra[2] = { { 0x07ED8, 0x0010, NES_SPRPALS }, { 0x07EE8, 0x0010, NES_SPRPALS } };
+static ScummNESFile::Resource res_sprpals_ger[2] = { { 0x07F6B, 0x0010, NES_SPRPALS }, { 0x0BF17, 0x0010, NES_SPRPALS } };
+#endif
+static ScummNESFile::Resource *res_sprpals[ScummNESFile::kROMsetNum] = {
+ res_sprpals_usa,
+ res_sprpals_eur,
+ res_sprpals_swe,
+ res_sprpals_fra,
+ res_sprpals_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_sprdesc_usa;
+static ScummNESFile::Resource *res_sprdesc_eur;
+static ScummNESFile::Resource *res_sprdesc_swe;
+static ScummNESFile::Resource *res_sprdesc_fra;
+static ScummNESFile::Resource *res_sprdesc_ger;
+#else
+// associates costume IDs with sprite sets (indexes into SPRLENS/SPROFFS)
+static ScummNESFile::Resource res_sprdesc_usa[2] = { { 0x0FFB7, 0x0031, NES_SPRDESC }, { 0x0BFE1, 0x0009, NES_SPRDESC } };
+static ScummNESFile::Resource res_sprdesc_eur[2] = { { 0x0BEC2, 0x0031, NES_SPRDESC }, { 0x07F71, 0x0009, NES_SPRDESC } };
+static ScummNESFile::Resource res_sprdesc_swe[2] = { { 0x0BF1B, 0x0031, NES_SPRDESC }, { 0x07F75, 0x0009, NES_SPRDESC } };
+static ScummNESFile::Resource res_sprdesc_fra[2] = { { 0x07EF8, 0x0031, NES_SPRDESC }, { 0x07F29, 0x0009, NES_SPRDESC } };
+static ScummNESFile::Resource res_sprdesc_ger[2] = { { 0x0BF27, 0x0031, NES_SPRDESC }, { 0x0BF58, 0x0009, NES_SPRDESC } };
+#endif
+static ScummNESFile::Resource *res_sprdesc[ScummNESFile::kROMsetNum] = {
+ res_sprdesc_usa,
+ res_sprdesc_eur,
+ res_sprdesc_swe,
+ res_sprdesc_fra,
+ res_sprdesc_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_sprlens_usa;
+static ScummNESFile::Resource *res_sprlens_eur;
+static ScummNESFile::Resource *res_sprlens_swe;
+static ScummNESFile::Resource *res_sprlens_fra;
+static ScummNESFile::Resource *res_sprlens_ger;
+#else
+// number of sprites in each set (indicates length within SPRDATA)
+static ScummNESFile::Resource res_sprlens_usa[2] = { { 0x0FEA2, 0x0115, NES_SPRLENS }, { 0x07FF5, 0x0006, NES_SPRLENS } };
+static ScummNESFile::Resource res_sprlens_eur[2] = { { 0x1BE32, 0x0115, NES_SPRLENS }, { 0x07F5B, 0x0006, NES_SPRLENS } };
+static ScummNESFile::Resource res_sprlens_swe[2] = { { 0x13E6A, 0x0115, NES_SPRLENS }, { 0x07F4F, 0x0006, NES_SPRLENS } };
+static ScummNESFile::Resource res_sprlens_fra[2] = { { 0x0FE61, 0x0115, NES_SPRLENS }, { 0x07ED2, 0x0006, NES_SPRLENS } };
+static ScummNESFile::Resource res_sprlens_ger[2] = { { 0x2BE1A, 0x0115, NES_SPRLENS }, { 0x07F65, 0x0006, NES_SPRLENS } };
+#endif
+static ScummNESFile::Resource *res_sprlens[ScummNESFile::kROMsetNum] = {
+ res_sprlens_usa,
+ res_sprlens_eur,
+ res_sprlens_swe,
+ res_sprlens_fra,
+ res_sprlens_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_sproffs_usa;
+static ScummNESFile::Resource *res_sproffs_eur;
+static ScummNESFile::Resource *res_sproffs_swe;
+static ScummNESFile::Resource *res_sproffs_fra;
+static ScummNESFile::Resource *res_sproffs_ger;
+#else
+// offset of each sprite set (indexes into SPRDATA)
+static ScummNESFile::Resource res_sproffs_usa[2] = { { 0x2BDC5, 0x022A, NES_SPROFFS }, { 0x0BFEA, 0x000C, NES_SPROFFS } };
+static ScummNESFile::Resource res_sproffs_eur[2] = { { 0x2FD42, 0x022A, NES_SPROFFS }, { 0x0BEF3, 0x000C, NES_SPROFFS } };
+static ScummNESFile::Resource res_sproffs_swe[2] = { { 0x2BCE0, 0x022A, NES_SPROFFS }, { 0x0BF4C, 0x000C, NES_SPROFFS } };
+static ScummNESFile::Resource res_sproffs_fra[2] = { { 0x2F959, 0x022A, NES_SPROFFS }, { 0x07F32, 0x000C, NES_SPROFFS } };
+static ScummNESFile::Resource res_sproffs_ger[2] = { { 0x2F81F, 0x022A, NES_SPROFFS }, { 0x0BF61, 0x000C, NES_SPROFFS } };
+#endif
+static ScummNESFile::Resource *res_sproffs[ScummNESFile::kROMsetNum] = {
+ res_sproffs_usa,
+ res_sproffs_eur,
+ res_sproffs_swe,
+ res_sproffs_fra,
+ res_sproffs_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_sprdata_usa;
+static ScummNESFile::Resource *res_sprdata_eur;
+static ScummNESFile::Resource *res_sprdata_swe;
+static ScummNESFile::Resource *res_sprdata_fra;
+static ScummNESFile::Resource *res_sprdata_ger;
+#else
+// sprite data sets (packed NES sprite data)
+static ScummNESFile::Resource res_sprdata_usa[2] = { { 0x2CE11, 0x2BE0, NES_SPRDATA }, { 0x07F6B, 0x008A, NES_SPRDATA } };
+static ScummNESFile::Resource res_sprdata_eur[2] = { { 0x2CE11, 0x2BE0, NES_SPRDATA }, { 0x0BE28, 0x008A, NES_SPRDATA } };
+static ScummNESFile::Resource res_sprdata_swe[2] = { { 0x2C401, 0x2BE0, NES_SPRDATA }, { 0x0FE6B, 0x008A, NES_SPRDATA } };
+static ScummNESFile::Resource res_sprdata_fra[2] = { { 0x2CA28, 0x2BE0, NES_SPRDATA }, { 0x07E48, 0x008A, NES_SPRDATA } };
+static ScummNESFile::Resource res_sprdata_ger[2] = { { 0x2C8EE, 0x2BE0, NES_SPRDATA }, { 0x0FE61, 0x008A, NES_SPRDATA } };
+static ScummNESFile::Resource *res_sprdata[ScummNESFile::kROMsetNum] = {
+ res_sprdata_usa,
+ res_sprdata_eur,
+ res_sprdata_swe,
+ res_sprdata_fra,
+ res_sprdata_ger,
+};
+#endif
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_charset_usa;
+static ScummNESFile::Resource *res_charset_eur;
+static ScummNESFile::Resource *res_charset_swe;
+static ScummNESFile::Resource *res_charset_fra;
+static ScummNESFile::Resource *res_charset_ger;
+#else
+static ScummNESFile::Resource res_charset_usa[1] = { { 0x3F6EE, 0x0090, NES_CHARSET } };
+static ScummNESFile::Resource res_charset_eur[1] = { { 0x3F724, 0x0090, NES_CHARSET } };
+static ScummNESFile::Resource res_charset_swe[1] = { { 0x3F739, 0x0090, NES_CHARSET } };
+static ScummNESFile::Resource res_charset_fra[1] = { { 0x3F739, 0x0090, NES_CHARSET } };
+static ScummNESFile::Resource res_charset_ger[1] = { { 0x3F739, 0x0090, NES_CHARSET } };
+#endif
+static ScummNESFile::Resource *res_charset[ScummNESFile::kROMsetNum] = {
+ res_charset_usa,
+ res_charset_eur,
+ res_charset_swe,
+ res_charset_fra,
+ res_charset_ger,
+};
+
+#ifdef PALMOS_68K
+static ScummNESFile::Resource *res_preplist_usa;
+static ScummNESFile::Resource *res_preplist_eur;
+static ScummNESFile::Resource *res_preplist_swe;
+static ScummNESFile::Resource *res_preplist_fra;
+static ScummNESFile::Resource *res_preplist_ger;
+#else
+static ScummNESFile::Resource res_preplist_usa[1] = { { 0x3FB5A, 0x000E, NES_PREPLIST } };
+static ScummNESFile::Resource res_preplist_eur[1] = { { 0x3FB90, 0x000E, NES_PREPLIST } };
+static ScummNESFile::Resource res_preplist_swe[1] = { { 0x3FBA9, 0x000E, NES_PREPLIST } };
+static ScummNESFile::Resource res_preplist_fra[1] = { { 0x3FBAF, 0x0010, NES_PREPLIST } };
+static ScummNESFile::Resource res_preplist_ger[1] = { { 0x3FBAB, 0x000F, NES_PREPLIST } };
+#endif
+static ScummNESFile::Resource *res_preplist[ScummNESFile::kROMsetNum] = {
+ res_preplist_usa,
+ res_preplist_eur,
+ res_preplist_swe,
+ res_preplist_fra,
+ res_preplist_ger,
+};
+
+uint16 write_byte(Common::WriteStream *out, byte val) {
+ val ^= 0xFF;
+ if (out != 0)
+ out->writeByte(val);
+ return 1;
+}
+
+uint16 write_word(Common::WriteStream *out, uint16 val) {
+ val ^= 0xFFFF;
+ if (out != 0)
+ out->writeUint16LE(val);
+ return 2;
+}
+
+byte ScummNESFile::fileReadByte() {
+ byte b = 0;
+ File::read(&b, 1);
+ return b;
+}
+
+uint16 ScummNESFile::fileReadUint16LE() {
+ uint16 a = fileReadByte();
+ uint16 b = fileReadByte();
+ return a | (b << 8);
+}
+
+uint16 ScummNESFile::extractResource(Common::WriteStream *output, Resource *res) {
+ uint16 len, i, j;
+ byte val;
+ byte cnt;
+ uint16 reslen = 0;
+
+ if (res == NULL)
+ error("extract_resource - no resource specified");
+
+ if ((res->offset == 0) && (res->length == 0))
+ return 0; /* there are 8 scripts that are zero bytes long, so we should skip them */
+
+ File::seek(res->offset,SEEK_SET);
+
+ switch (res->type) {
+ case NES_GLOBDATA:
+ len = res->length;
+
+ for (i = 0; i < len; i++)
+ reslen += write_byte(output, fileReadByte());
+
+ break;
+
+ case NES_ROOMGFX:
+ case NES_COSTUMEGFX:
+ reslen += write_word(output, (uint16)(res->length + 2));
+ len = fileReadByte();
+ reslen += write_byte(output, (byte)len);
+
+ if (!len)
+ len = 256;
+ len = len << 4;
+
+ for (i = 0; i < len;) {
+ reslen += write_byte(output, cnt = fileReadByte());
+ for (j = 0; j < (cnt & 0x7F); j++, i++)
+ if ((cnt & 0x80) || (j == 0))
+ reslen += write_byte(output, fileReadByte());
+ }
+
+ if (File::pos() - res->offset != res->length)
+ error("extract_resource - length mismatch while extracting graphics resource (was %04X, should be %04X)", File::pos() - res->offset, res->length);
+
+ break;
+
+ case NES_ROOM:
+ case NES_SCRIPT:
+ len = fileReadUint16LE();
+
+ if (len != res->length)
+ error("extract_resource - length mismatch while extracting room/script resource (was %04X, should be %04X)", len, res->length);
+
+ File::seek(-2, SEEK_CUR);
+
+ for (i = 0; i < len; i++)
+ reslen += write_byte(output, fileReadByte());
+
+ break;
+
+ case NES_SOUND:
+ len = res->length + 2;
+ val = fileReadByte();
+ cnt = fileReadByte();
+
+ if ((val == 2) && (cnt == 100)) {
+ reslen += write_word(output, len);
+ reslen += write_byte(output, val);
+ reslen += write_byte(output, cnt);
+
+ cnt = fileReadByte();
+ reslen += write_byte(output, cnt);
+ for (i = 0; i < cnt; i++)
+ reslen += write_byte(output, fileReadByte());
+ for (i = 0; i < cnt; i++)
+ reslen += write_byte(output, fileReadByte());
+
+ while (1) {
+ reslen += write_byte(output, val = fileReadByte());
+ if (val >= 0xFE)
+ break;
+ }
+ } else if (((val == 0) || (val == 1) || (val == 4)) && (cnt == 10)) {
+ reslen += write_word(output, len);
+ reslen += write_byte(output, val);
+ reslen += write_byte(output, cnt);
+ while (1) {
+ reslen += write_byte(output, val = fileReadByte());
+
+ if (val >= 0xFE)
+ break;
+
+ if (val >= 0x10)
+ reslen += write_byte(output, fileReadByte());
+ else {
+ reslen += write_byte(output, fileReadByte());
+ reslen += write_byte(output, fileReadByte());
+ reslen += write_byte(output, fileReadByte());
+ reslen += write_byte(output, fileReadByte());
+ }
+ }
+ } else
+ error("extract_resource - unknown sound type %d/%d detected",val,cnt);
+
+ if (File::pos() - res->offset != res->length)
+ error("extract_resource - length mismatch while extracting sound resource (was %04X, should be %04X)", File::pos() - res->offset, res->length);
+
+ break;
+
+ case NES_COSTUME:
+ case NES_SPRPALS:
+ case NES_SPRDESC:
+ case NES_SPRLENS:
+ case NES_SPROFFS:
+ case NES_SPRDATA:
+ case NES_CHARSET:
+ len = res->length;
+ reslen += write_word(output, (uint16)(len + 2));
+
+ for (i = 0; i < len; i++)
+ reslen += write_byte(output, fileReadByte());
+
+ break;
+
+ case NES_PREPLIST:
+ len = res->length;
+ reslen += write_word(output, 0x002A);
+
+ reslen += write_byte(output, ' ');
+ for (i = 1; i < 8; i++)
+ reslen += write_byte(output, 0);
+
+ for (j = 0; j < 4; j++)
+ {
+ reslen += write_byte(output,' ');
+ for (i = 1; (val = fileReadByte()); i++)
+ reslen += write_byte(output, val);
+ for (; i < 8; i++)
+ reslen += write_byte(output, 0);
+ }
+ break;
+
+ default:
+ error("extract_resource - unknown resource type %d specified!", res->type);
+ }
+
+ return reslen;
+}
+
+struct ScummNESFile::LFLEntry {
+ Resource **type;
+ int index;
+};
+
+// based on structure of Classic PC Maniac Mansion LFL files
+// (roomgfx resources are arranged in order, one per file,
+// after the room blocks)
+static ScummNESFile::LFLEntry lfl_01[] = { {res_rooms, 1}, {res_roomgfx, 1}, {res_scripts, 57}, {res_scripts, 61}, {res_scripts, 76}, {res_scripts, 105}, {res_scripts, 111}, {res_sounds, 5}, {res_scripts, 132}, {res_scripts, 148}, {res_scripts, 155}, {res_scripts, 156}, {res_sounds, 39}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_02[] = { {res_rooms, 2}, {res_roomgfx, 2}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_03[] = { {res_rooms, 3}, {res_roomgfx, 3}, {res_scripts, 21}, {res_sounds, 26}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_04[] = { {res_rooms, 4}, {res_roomgfx, 4}, {res_scripts, 46}, {res_scripts, 56}, {res_scripts, 137}, {res_scripts, 146}, {res_sounds, 12}, {res_sounds, 11}, {res_sounds, 13}, {res_sounds, 42}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_05[] = { {res_rooms, 5}, {res_roomgfx, 5}, {res_scripts, 30}, {res_scripts, 31}, {res_scripts, 32}, {res_scripts, 33}, {res_scripts, 34}, {res_scripts, 35}, {res_sounds, 22}, {res_sounds, 23}, {res_sounds, 24}, {res_sounds, 21}, {res_sounds, 46}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_06[] = { {res_rooms, 6}, {res_roomgfx, 6}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_07[] = { {res_rooms, 7}, {res_roomgfx, 7}, {res_scripts, 17}, {res_scripts, 58}, {res_scripts, 59}, {res_scripts, 60}, {res_scripts, 74}, {res_scripts, 81}, {res_scripts, 82}, {res_scripts, 150}, {res_sounds, 14}, {res_sounds, 15}, {res_sounds, 16}, {res_sounds, 17}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_08[] = { {res_rooms, 8}, {res_roomgfx, 8}, {res_scripts, 7}, {res_scripts, 12}, {res_scripts, 13}, {res_scripts, 47}, {res_scripts, 48}, {res_scripts, 49}, {res_scripts, 154}, {res_sounds, 32}, {res_sounds, 33}, {res_sounds, 36}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_09[] = { {res_rooms, 9}, {res_roomgfx, 9}, {res_scripts, 10}, {res_scripts, 11}, {res_scripts, 45}, {res_scripts, 55}, {res_scripts, 84}, {res_scripts, 85}, {res_scripts, 86}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_10[] = { {res_rooms, 10}, {res_roomgfx, 10}, {res_scripts, 24}, {res_scripts, 149}, {res_sounds, 28}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_11[] = { {res_rooms, 11}, {res_roomgfx, 11}, {res_scripts, 166}, {res_scripts, 167}, {res_scripts, 168}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_12[] = { {res_rooms, 12}, {res_roomgfx, 12}, {res_scripts, 51}, {res_scripts, 103}, {res_scripts, 104}, {res_scripts, 161}, {res_sounds, 63}, {res_costumes, 14}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_13[] = { {res_rooms, 13}, {res_roomgfx, 13}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_14[] = { {res_rooms, 14}, {res_roomgfx, 14}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_15[] = { {res_rooms, 15}, {res_roomgfx, 15}, {res_sounds, 27}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_16[] = { {res_rooms, 16}, {res_roomgfx, 16}, {res_scripts, 14}, {res_scripts, 121}, {res_scripts, 122}, {res_sounds, 40}, {res_sounds, 64}, {res_sounds, 68}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_17[] = { {res_rooms, 17}, {res_roomgfx, 17}, {res_scripts, 20}, {res_scripts, 100}, {res_sounds, 25}, {res_sounds, 44}, {res_sounds, 2}, {res_sounds, 50}, {res_sounds, 52}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_18[] = { {res_rooms, 18}, {res_roomgfx, 18}, {res_scripts, 25}, {res_scripts, 26}, {res_scripts, 27}, {res_scripts, 28}, {res_scripts, 64}, {res_scripts, 65}, {res_scripts, 66}, {res_scripts, 67}, {res_scripts, 68}, {res_scripts, 69}, {res_scripts, 70}, {res_scripts, 71}, {res_scripts, 73}, {res_scripts, 101}, {res_sounds, 35}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_19[] = { {res_rooms, 19}, {res_roomgfx, 19}, {res_scripts, 36}, {res_scripts, 37}, {res_scripts, 38}, {res_scripts, 39}, {res_scripts, 40}, {res_scripts, 152}, {res_scripts, 153}, {res_costumes, 10}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_20[] = { {res_rooms, 20}, {res_roomgfx, 20}, {res_scripts, 107}, {res_scripts, 108}, {res_scripts, 109}, {res_scripts, 110}, {res_scripts, 159}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_21[] = { {res_rooms, 21}, {res_roomgfx, 21}, {res_scripts, 41}, {res_scripts, 42}, {res_scripts, 43}, {res_scripts, 53}, {res_scripts, 136}, {res_sounds, 29}, {res_sounds, 20}, {res_sounds, 37}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_22[] = { {res_rooms, 22}, {res_roomgfx, 22}, {res_scripts, 15}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_23[] = { {res_rooms, 23}, {res_roomgfx, 23}, {res_scripts, 77}, {res_scripts, 79}, {res_scripts, 80}, {res_scripts, 83}, {res_sounds, 41}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_24[] = { {res_rooms, 24}, {res_roomgfx, 24}, {res_scripts, 18}, {res_scripts, 19}, {res_scripts, 78}, {res_sounds, 7}, {res_sounds, 3}, {res_sounds, 18}, {res_sounds, 34}, {res_costumes, 12}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_25[] = { {res_rooms, 25}, {res_roomgfx, 25}, {res_scripts, 29}, {res_sounds, 30}, {res_sounds, 31}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_26[] = { {res_rooms, 26}, {res_roomgfx, 26}, {res_scripts, 87}, {res_scripts, 88}, {res_scripts, 89}, {res_scripts, 90}, {res_scripts, 91}, {res_scripts, 92}, {res_scripts, 93}, {res_scripts, 94}, {res_scripts, 95}, {res_scripts, 96}, {res_scripts, 97}, {res_scripts, 98}, {res_scripts, 116}, {res_scripts, 151}, {res_scripts, 174}, {res_costumes, 11}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_27[] = { {res_rooms, 27}, {res_roomgfx, 27}, {res_scripts, 16}, {res_scripts, 52}, {res_scripts, 54}, {res_scripts, 113}, {res_sounds, 45}, {res_costumes, 19}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_28[] = { {res_rooms, 28}, {res_roomgfx, 28}, {res_scripts, 22}, {res_scripts, 23}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_29[] = { {res_rooms, 29}, {res_roomgfx, 29}, {res_scripts, 75}, {res_sounds, 43}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_30[] = { {res_rooms, 30}, {res_roomgfx, 30}, {res_scripts, 63}, {res_sounds, 0}, {res_scripts, 123}, {res_scripts, 125}, {res_scripts, 126}, {res_scripts, 127}, {res_scripts, 129}, {res_sounds, 55}, {res_sounds, 59}, {res_sounds, 60}, {res_costumes, 8}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_31[] = { {res_rooms, 31}, {res_roomgfx, 31}, {res_scripts, 99}, {res_scripts, 115}, {res_scripts, 117}, {res_scripts, 119}, {res_scripts, 147}, {res_scripts, 157}, {res_scripts, 158}, {res_scripts, 160}, {res_costumes, 13}, {res_costumes, 9}, {res_costumes, 23}, {res_costumes, 24}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_32[] = { {res_rooms, 32}, {res_roomgfx, 32}, {res_costumes, 15}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_33[] = { {res_rooms, 33}, {res_roomgfx, 33}, {res_scripts, 120}, {res_scripts, 135}, {res_sounds, 56}, {res_sounds, 57}, {res_sounds, 58}, {res_sounds, 1}, {res_costumes, 22}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_34[] = { {res_rooms, 34}, {res_roomgfx, 34}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_35[] = { {res_rooms, 35}, {res_roomgfx, 35}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_36[] = { {res_rooms, 36}, {res_roomgfx, 36}, {res_sounds, 10}, {res_sounds, 4}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_37[] = { {res_rooms, 37}, {res_roomgfx, 37}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_38[] = { {res_rooms, 38}, {res_roomgfx, 38}, {res_scripts, 138}, {res_scripts, 139}, {res_scripts, 140}, {res_scripts, 141}, {res_scripts, 142}, {res_scripts, 143}, {res_scripts, 144}, {res_scripts, 145}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_39[] = { {res_rooms, 39}, {res_roomgfx, 39}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_40[] = { {res_rooms, 40}, {res_roomgfx, 0}, {res_scripts, 112}, {res_costumes, 17}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_41[] = { {res_rooms, 41}, {res_scripts, 106}, {res_sounds, 47}, {res_sounds, 48}, {res_sounds, 53}, {res_sounds, 49}, {res_sounds, 51}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_42[] = { {res_rooms, 42}, {res_scripts, 124}, {res_costumes, 18}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_43[] = { {res_rooms, 43}, {res_scripts, 44}, {res_sounds, 19}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_44[] = { {res_rooms, 44}, {res_scripts, 102}, {res_sounds, 6}, {res_sounds, 38}, {res_sounds, 8}, {res_sounds, 9}, {res_costumes, 1}, {res_costumes, 2}, {res_costumes, 5}, {res_costumes, 6}, {res_costumes, 3}, {res_costumes, 4}, {res_costumes, 7}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_45[] = { {res_rooms, 45}, {res_scripts, 1}, {res_scripts, 2}, {res_scripts, 3}, {res_scripts, 4}, {res_scripts, 5}, {res_scripts, 9}, {res_scripts, 114}, {res_scripts, 131}, {res_scripts, 164}, {res_scripts, 165}, {res_scripts, 169}, {res_scripts, 170}, {res_scripts, 171}, {res_scripts, 172}, {res_scripts, 173}, {res_scripts, 175}, {res_sounds, 54}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_46[] = { {res_rooms, 46}, {res_scripts, 130}, {res_sounds, 65}, {res_costumes, 0}, {res_costumes, 21}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_47[] = { {res_rooms, 47}, {res_scripts, 62}, {res_sounds, 69}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_48[] = { {res_rooms, 48}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_49[] = { {res_rooms, 49}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_50[] = { {res_rooms, 50}, {res_scripts, 133}, {res_scripts, 163}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_51[] = { {res_rooms, 51}, {res_scripts, 118}, {res_scripts, 128}, {res_sounds, 61}, {res_sounds, 62}, {res_sounds, 67}, {res_sounds, 66}, {res_costumes, 16}, {res_costumes, 20}, {NULL, 0} };
+static ScummNESFile::LFLEntry lfl_52[] = { {res_rooms, 52}, {NULL, 0} };
+// remaining 'standard' resources (not used by any of the original LFL files)
+static ScummNESFile::LFLEntry lfl_53[] = { {res_rooms, 53}, {res_scripts, 177}, {res_scripts, 178}, {res_sounds, 70}, {res_sounds, 71}, {res_sounds, 72}, {res_sounds, 73}, {res_sounds, 74}, {res_sounds, 75}, {res_sounds, 76}, {res_sounds, 77}, {res_sounds, 78}, {res_sounds, 79}, {res_sounds, 80}, {res_sounds, 81}, {NULL, 0} };
+// all 'non-standard' resources (the costume-related stuff)
+static ScummNESFile::LFLEntry lfl_54[] = { {res_rooms, 54}, {res_sprdesc, 0}, {res_sprdesc, 1}, {res_sprlens, 0}, {res_sprlens, 1}, {res_sproffs, 0}, {res_sproffs, 1}, {res_sprdata, 0}, {res_sprdata, 1}, {res_costumegfx, 0}, {res_costumegfx, 1}, {res_sprpals, 0}, {res_sprpals, 1}, {res_charset, 0}, {res_preplist, 0}, {NULL, 0} };
+
+struct ScummNESFile::LFL {
+ int num;
+ ScummNESFile::LFLEntry *entries;
+};
+
+static ScummNESFile::LFL lfls[] = {
+ { 1, lfl_01 },
+ { 2, lfl_02 },
+ { 3, lfl_03 },
+ { 4, lfl_04 },
+ { 5, lfl_05 },
+ { 6, lfl_06 },
+ { 7, lfl_07 },
+ { 8, lfl_08 },
+ { 9, lfl_09 },
+ { 10, lfl_10 },
+ { 11, lfl_11 },
+ { 12, lfl_12 },
+ { 13, lfl_13 },
+ { 14, lfl_14 },
+ { 15, lfl_15 },
+ { 16, lfl_16 },
+ { 17, lfl_17 },
+ { 18, lfl_18 },
+ { 19, lfl_19 },
+ { 20, lfl_20 },
+ { 21, lfl_21 },
+ { 22, lfl_22 },
+ { 23, lfl_23 },
+ { 24, lfl_24 },
+ { 25, lfl_25 },
+ { 26, lfl_26 },
+ { 27, lfl_27 },
+ { 28, lfl_28 },
+ { 29, lfl_29 },
+ { 30, lfl_30 },
+ { 31, lfl_31 },
+ { 32, lfl_32 },
+ { 33, lfl_33 },
+ { 34, lfl_34 },
+ { 35, lfl_35 },
+ { 36, lfl_36 },
+ { 37, lfl_37 },
+ { 38, lfl_38 },
+ { 39, lfl_39 },
+ { 40, lfl_40 },
+ { 41, lfl_41 },
+ { 42, lfl_42 },
+ { 43, lfl_43 },
+ { 44, lfl_44 },
+ { 45, lfl_45 },
+ { 46, lfl_46 },
+ { 47, lfl_47 },
+ { 48, lfl_48 },
+ { 49, lfl_49 },
+ { 50, lfl_50 },
+ { 51, lfl_51 },
+ { 52, lfl_52 },
+ { 53, lfl_53 },
+ { 54, lfl_54 },
+ { -1, NULL }
+};
+
+#pragma START_PACK_STRUCTS
+struct _lfl_index {
+ byte room_lfl[55];
+ uint16 room_addr[55];
+ byte costume_lfl[80];
+ uint16 costume_addr[80];
+ byte script_lfl[200];
+ uint16 script_addr[200];
+ byte sound_lfl[100];
+ uint16 sound_addr[100];
+} GCC_PACK lfl_index;
+#pragma END_PACK_STRUCTS
+
+
+bool ScummNESFile::generateResource(int res) {
+ LFL *lfl = &lfls[res - 1];
+ int j;
+ int bufsize = 2;
+
+ for (j = 0; lfl->entries[j].type != NULL; j++)
+ bufsize += extractResource(0, &lfl->entries[j].type[_ROMset][lfl->entries[j].index]);
+
+ free(_buf);
+ _buf = (byte *)calloc(1, bufsize);
+
+ Common::MemoryWriteStream out(_buf, bufsize);
+
+ for (j = 0; lfl->entries[j].type != NULL; j++) {
+ Resource *entry = &lfl->entries[j].type[_ROMset][lfl->entries[j].index];
+ extractResource(&out, entry);
+ }
+ write_byte(&out, 0xD1);
+ write_byte(&out, 0xF5);
+
+ if (_stream)
+ delete _stream;
+
+ _stream = new Common::MemoryReadStream(_buf, bufsize);
+
+ return true;
+}
+
+bool ScummNESFile::generateIndex() {
+ int i, j;
+
+ for (i = 0; lfls[i].num != -1; i++) {
+ LFL *lfl = &lfls[i];
+ uint16 respos = 0;
+
+ for (j = 0; lfl->entries[j].type != NULL; j++) {
+ LFLEntry *entry = &lfl->entries[j];
+
+ switch (entry->type[_ROMset][entry->index].type) {
+ case NES_ROOM:
+ lfl_index.room_lfl[entry->index] = lfl->num;
+ lfl_index.room_addr[entry->index] = TO_LE_16(respos);
+ break;
+ case NES_COSTUME:
+ lfl_index.costume_lfl[entry->index] = lfl->num;
+ lfl_index.costume_addr[entry->index] = TO_LE_16(respos);
+ break;
+ case NES_SPRDESC:
+ lfl_index.costume_lfl[entry->index + 25] = lfl->num;
+ lfl_index.costume_addr[entry->index + 25] = TO_LE_16(respos);
+ break;
+ case NES_SPRLENS:
+ lfl_index.costume_lfl[entry->index + 27] = lfl->num;
+ lfl_index.costume_addr[entry->index + 27] = TO_LE_16(respos);
+ break;
+ case NES_SPROFFS:
+ lfl_index.costume_lfl[entry->index + 29] = lfl->num;
+ lfl_index.costume_addr[entry->index + 29] = TO_LE_16(respos);
+ break;
+ case NES_SPRDATA:
+ lfl_index.costume_lfl[entry->index + 31] = lfl->num;
+ lfl_index.costume_addr[entry->index + 31] = TO_LE_16(respos);
+ break;
+ case NES_COSTUMEGFX:
+ lfl_index.costume_lfl[entry->index + 33] = lfl->num;
+ lfl_index.costume_addr[entry->index + 33] = TO_LE_16(respos);
+ break;
+ case NES_SPRPALS:
+ lfl_index.costume_lfl[entry->index + 35] = lfl->num;
+ lfl_index.costume_addr[entry->index + 35] = TO_LE_16(respos);
+ break;
+ case NES_ROOMGFX:
+ lfl_index.costume_lfl[entry->index + 37] = lfl->num;
+ lfl_index.costume_addr[entry->index + 37] = TO_LE_16(respos);
+ break;
+ case NES_SCRIPT:
+ lfl_index.script_lfl[entry->index] = lfl->num;
+ lfl_index.script_addr[entry->index] = TO_LE_16(respos);
+ break;
+ case NES_SOUND:
+ lfl_index.sound_lfl[entry->index] = lfl->num;
+ lfl_index.sound_addr[entry->index] = TO_LE_16(respos);
+ break;
+ case NES_CHARSET:
+ lfl_index.costume_lfl[77] = lfl->num;
+ lfl_index.costume_addr[77] = TO_LE_16(respos);
+ break;
+ case NES_PREPLIST:
+ lfl_index.costume_lfl[78] = lfl->num;
+ lfl_index.costume_addr[78] = TO_LE_16(respos);
+ break;
+ default:
+ error("Unindexed entry found!");
+ break;
+ }
+ respos += extractResource(0, &entry->type[_ROMset][entry->index]);
+ }
+ }
+
+ int bufsize = 2;
+
+ bufsize += 775;
+ bufsize += sizeof(lfl_index);
+
+ free(_buf);
+ _buf = (byte *)calloc(1, bufsize);
+
+ Common::MemoryWriteStream out(_buf, bufsize);
+
+ write_byte(&out, 0x43);
+ write_byte(&out, 0x46);
+
+ extractResource(&out, &res_globdata[_ROMset][0]);
+
+ for (i = 0; i < (int)sizeof(lfl_index); i++)
+ write_byte(&out, ((byte *)&lfl_index)[i]);
+
+ if (_stream)
+ delete _stream;
+
+ _stream = new Common::MemoryReadStream(_buf, bufsize);
+
+ return true;
+}
+
+bool ScummNESFile::open(const char *filename, AccessMode mode) {
+ uint8 md5sum[16];
+
+ if (_ROMset == kROMsetNum) {
+ if (Common::md5_file(filename, md5sum)) {
+ char md5str[32+1];
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+
+ if (!strcmp(md5str, "3905799e081b80a61d4460b7b733c206")) {
+ _ROMset = kROMsetUSA;
+ debug(1, "ROM contents verified as Maniac Mansion (USA)");
+ } else if (!strcmp(md5str, "d8d07efcb88f396bee0b402b10c3b1c9")) {
+ _ROMset = kROMsetEurope;
+ debug(1, "ROM contents verified as Maniac Mansion (Europe)");
+ } else if (!strcmp(md5str, "22d07d6c386c9c25aca5dac2a0c0d94b")) {
+ _ROMset = kROMsetSweden;
+ debug(1, "ROM contents verified as Maniac Mansion (Sweden)");
+ } else if (!strcmp(md5str, "81bbfa181184cb494e7a81dcfa94fbd9")) {
+ _ROMset = kROMsetFrance;
+ debug(2, "ROM contents verified as Maniac Mansion (France)");
+ } else if (!strcmp(md5str, "257f8c14d8c584f7ddd601bcb00920c7")) {
+ _ROMset = kROMsetGermany;
+ debug(2, "ROM contents verified as Maniac Mansion (Germany)");
+ } else {
+ error("Unsupported Maniac Mansion ROM, md5: %s", md5str);
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ if (File::open(filename, mode)) {
+ if (_stream)
+ delete _stream;
+ _stream = 0;
+
+ free(_buf);
+ _buf = 0;
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ScummNESFile::close() {
+ if (_stream)
+ delete _stream;
+ _stream = 0;
+
+ free(_buf);
+ _buf = 0;
+
+ File::close();
+}
+
+bool ScummNESFile::openSubFile(const char *filename) {
+ assert(isOpen());
+
+ const char *ext = strrchr(filename, '.');
+ char resNum[3];
+ int res;
+
+ // We always have file name in form of XX.lfl
+ resNum[0] = ext[-2];
+ resNum[1] = ext[-1];
+ resNum[2] = 0;
+
+ res = atoi(resNum);
+
+ if (res == 0) {
+ return generateIndex();
+ } else {
+ return generateResource(res);
+ }
+}
+
+#pragma mark -
+#pragma mark --- ScummC64File ---
+#pragma mark -
+
+static const int maniacResourcesPerFile[55] = {
+ 0, 11, 1, 3, 9, 12, 1, 13, 10, 6,
+ 4, 1, 7, 1, 1, 2, 7, 8, 19, 9,
+ 6, 9, 2, 6, 8, 4, 16, 8, 3, 3,
+ 12, 12, 2, 8, 1, 1, 2, 1, 9, 1,
+ 3, 7, 3, 3, 13, 5, 4, 3, 1, 1,
+ 3, 10, 1, 0, 0
+};
+
+static const int zakResourcesPerFile[59] = {
+ 0, 29, 12, 14, 13, 4, 4, 10, 7, 4,
+ 14, 19, 5, 4, 7, 6, 11, 9, 4, 4,
+ 1, 3, 3, 5, 1, 9, 4, 10, 13, 6,
+ 7, 10, 2, 6, 1, 11, 2, 5, 7, 1,
+ 7, 1, 4, 2, 8, 6, 6, 6, 4, 13,
+ 3, 1, 2, 1, 2, 1, 10, 1, 1
+};
+
+
+ScummC64File::ScummC64File(char *disk1, char *disk2, bool maniac) : _stream(0), _buf(0), _maniac(maniac) {
+ _disk1 = disk1;
+ _disk2 = disk2;
+
+ _openedDisk = 0;
+
+ if (maniac) {
+ _numGlobalObjects = 256;
+ _numRooms = 55;
+ _numCostumes = 25;
+ _numScripts = 160;
+ _numSounds = 70;
+ _resourcesPerFile = maniacResourcesPerFile;
+ } else {
+ _numGlobalObjects = 775;
+ _numRooms = 59;
+ _numCostumes = 38;
+ _numScripts = 155;
+ _numSounds = 127;
+ _resourcesPerFile = zakResourcesPerFile;
+ }
+}
+
+uint32 ScummC64File::write(const void *, uint32) {
+ error("ScummC64File does not support writing!");
+ return 0;
+}
+
+void ScummC64File::setEnc(byte enc) {
+ _stream->setEnc(enc);
+}
+
+byte ScummC64File::fileReadByte() {
+ byte b = 0;
+ File::read(&b, 1);
+ return b;
+}
+
+uint16 ScummC64File::fileReadUint16LE() {
+ uint16 a = fileReadByte();
+ uint16 b = fileReadByte();
+ return a | (b << 8);
+}
+
+bool ScummC64File::openDisk(char num) {
+ if (num == '1')
+ num = 1;
+ if (num == '2')
+ num = 2;
+
+ if (_openedDisk != num || !File::isOpen()) {
+ if (File::isOpen())
+ File::close();
+
+ if (num == 1)
+ File::open(_disk1.c_str());
+ else if (num == 2)
+ File::open(_disk2.c_str());
+ else {
+ error("ScummC64File::open(): wrong disk (%c)", num);
+ return false;
+ }
+
+ _openedDisk = num;
+
+ if (!File::isOpen()) {
+ error("ScummC64File::open(): cannot open disk (%d)", num);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ScummC64File::open(const char *filename, AccessMode mode) {
+ uint16 signature;
+
+ // check signature
+ openDisk(1);
+ File::seek(0);
+
+ signature = fileReadUint16LE();
+ if (signature != 0x0A31) {
+ error("ScummC64File::open(): signature not found in disk 1!");
+ return false;
+ }
+
+ extractIndex(0); // Fill in resource arrays
+
+ openDisk(2);
+ File::seek(0);
+
+ signature = fileReadUint16LE();
+ if (signature != 0x0132)
+ error("Error: signature not found in disk 2!\n");
+
+ return true;
+}
+
+
+uint16 ScummC64File::extractIndex(Common::WriteStream *out) {
+ int i;
+ uint16 reslen = 0;
+
+ openDisk(1);
+ File::seek(0);
+
+ // skip signature
+ fileReadUint16LE();
+
+ // write expected signature
+ reslen += write_word(out, 0x0132);
+
+ // copy object flags
+ for (i = 0; i < _numGlobalObjects; i++)
+ reslen += write_byte(out, fileReadByte());
+
+ // copy room offsets
+ for (i = 0; i < _numRooms; i++) {
+ _roomDisks[i] = fileReadByte();
+ reslen += write_byte(out, _roomDisks[i]);
+ }
+ for (i = 0; i < _numRooms; i++) {
+ _roomSectors[i] = fileReadByte();
+ reslen += write_byte(out, _roomSectors[i]);
+ _roomTracks[i] = fileReadByte();
+ reslen += write_byte(out, _roomTracks[i]);
+ }
+ for (i = 0; i < _numCostumes; i++)
+ reslen += write_byte(out, fileReadByte());
+ for (i = 0; i < _numCostumes; i++)
+ reslen += write_word(out, fileReadUint16LE());
+
+ for (i = 0; i < _numScripts; i++)
+ reslen += write_byte(out, fileReadByte());
+ for (i = 0; i < _numScripts; i++)
+ reslen += write_word(out, fileReadUint16LE());
+
+ for (i = 0; i < _numSounds; i++)
+ reslen += write_byte(out, fileReadByte());
+ for (i = 0; i < _numSounds; i++)
+ reslen += write_word(out, fileReadUint16LE());
+
+ return reslen;
+}
+
+bool ScummC64File::generateIndex() {
+ int bufsize;
+
+ bufsize = extractIndex(0);
+
+ free(_buf);
+ _buf = (byte *)calloc(1, bufsize);
+
+ Common::MemoryWriteStream out(_buf, bufsize);
+
+ extractIndex(&out);
+
+ if (_stream)
+ delete _stream;
+
+ _stream = new Common::MemoryReadStream(_buf, bufsize);
+
+ return true;
+}
+
+uint16 ScummC64File::extractResource(Common::WriteStream *out, int res) {
+ const int SectorOffset[36] = {
+ 0,
+ 0, 21, 42, 63, 84, 105, 126, 147, 168, 189, 210, 231, 252, 273, 294, 315, 336,
+ 357, 376, 395, 414, 433, 452, 471,
+ 490, 508, 526, 544, 562, 580,
+ 598, 615, 632, 649, 666
+ };
+ int i;
+ uint16 reslen = 0;
+
+ openDisk(_roomDisks[res]);
+
+ File::seek((SectorOffset[_roomTracks[res]] + _roomSectors[res]) * 256);
+
+ for (i = 0; i < _resourcesPerFile[res]; i++) {
+ uint16 len = fileReadUint16LE();
+ reslen += write_word(out, len);
+
+ for (len -= 2; len > 0; len--)
+ reslen += write_byte(out, fileReadByte());
+ }
+
+ return reslen;
+}
+
+bool ScummC64File::generateResource(int res) {
+ int bufsize;
+
+ if (res >= _numRooms)
+ return false;
+
+ bufsize = extractResource(0, res);
+
+ free(_buf);
+ _buf = (byte *)calloc(1, bufsize);
+
+ Common::MemoryWriteStream out(_buf, bufsize);
+
+ extractResource(&out, res);
+
+ if (_stream)
+ delete _stream;
+
+ _stream = new Common::MemoryReadStream(_buf, bufsize);
+
+ return true;
+}
+
+void ScummC64File::close() {
+ if (_stream)
+ delete _stream;
+ _stream = 0;
+
+ free(_buf);
+ _buf = 0;
+
+ File::close();
+}
+
+bool ScummC64File::openSubFile(const char *filename) {
+ assert(isOpen());
+
+ const char *ext = strrchr(filename, '.');
+ char resNum[3];
+ int res;
+
+ // We always have file name in form of XX.lfl
+ resNum[0] = ext[-2];
+ resNum[1] = ext[-1];
+ resNum[2] = 0;
+
+ res = atoi(resNum);
+
+ if (res == 0) {
+ return generateIndex();
+ } else {
+ return generateResource(res);
+ }
+
+ return true;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/util.h b/engines/scumm/util.h
new file mode 100644
index 0000000000..99d75cf127
--- /dev/null
+++ b/engines/scumm/util.h
@@ -0,0 +1,175 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCUMM_UTIL_H
+#define SCUMM_UTIL_H
+
+#include "common/file.h"
+#include "common/stream.h"
+
+namespace Scumm {
+
+#define revBitMask(x) (0x80 >> (x))
+
+class BaseScummFile : public Common::File {
+public:
+ virtual void setEnc(byte value) = 0;
+
+ virtual bool open(const char *filename, AccessMode mode = kFileReadMode) = 0;
+ virtual bool openSubFile(const char *filename) = 0;
+
+ virtual bool eof() = 0;
+ virtual uint32 pos() = 0;
+ virtual uint32 size() = 0;
+ virtual void seek(int32 offs, int whence = SEEK_SET) = 0;
+ virtual uint32 read(void *dataPtr, uint32 dataSize) = 0;
+ virtual uint32 write(const void *dataPtr, uint32 dataSize) = 0;
+};
+
+class ScummFile : public BaseScummFile {
+private:
+ byte _encbyte;
+ uint32 _subFileStart;
+ uint32 _subFileLen;
+public:
+ ScummFile();
+ void setEnc(byte value);
+
+ void setSubfileRange(uint32 start, uint32 len);
+ void resetSubfile();
+
+ bool open(const char *filename, AccessMode mode = kFileReadMode);
+ bool openSubFile(const char *filename);
+
+ bool eof();
+ uint32 pos();
+ uint32 size();
+ void seek(int32 offs, int whence = SEEK_SET);
+ uint32 read(void *dataPtr, uint32 dataSize);
+ uint32 write(const void *dataPtr, uint32 dataSize);
+};
+
+class ScummNESFile : public BaseScummFile {
+public:
+ enum ROMset {
+ kROMsetUSA,
+ kROMsetEurope,
+ kROMsetSweden,
+ kROMsetFrance,
+ kROMsetGermany,
+ kROMsetNum
+ };
+
+ struct Resource;
+ struct LFLEntry;
+ struct LFL;
+
+private:
+ Common::MemoryReadStream *_stream;
+ ROMset _ROMset;
+ byte *_buf;
+
+ bool generateIndex();
+ bool generateResource(int res);
+ uint16 extractResource(Common::WriteStream *out, Resource *res);
+
+ byte fileReadByte();
+ uint16 fileReadUint16LE();
+
+public:
+ ScummNESFile();
+ void setEnc(byte value);
+
+ bool open(const char *filename, AccessMode mode = kFileReadMode);
+ bool openSubFile(const char *filename);
+
+ void close();
+ bool eof() { return _stream->eos(); }
+ uint32 pos() { return _stream->pos(); }
+ uint32 size() { return _stream->size(); }
+ void seek(int32 offs, int whence = SEEK_SET) { _stream->seek(offs, whence); }
+ uint32 read(void *dataPtr, uint32 dataSize) { return _stream->read(dataPtr, dataSize); }
+ uint32 write(const void *dataPtr, uint32 dataSize);
+};
+
+
+class ScummC64File : public BaseScummFile {
+private:
+ Common::MemoryReadStream *_stream;
+ byte _roomDisks[59], _roomTracks[59], _roomSectors[59];
+
+ byte *_buf;
+
+ bool _maniac;
+ Common::String _disk1, _disk2;
+ int _openedDisk;
+
+ int _numGlobalObjects;
+ int _numRooms;
+ int _numCostumes;
+ int _numScripts;
+ int _numSounds;
+ const int *_resourcesPerFile;
+
+ bool openDisk(char num);
+
+ bool generateIndex();
+ bool generateResource(int res);
+
+ uint16 extractIndex(Common::WriteStream *out);
+ uint16 extractResource(Common::WriteStream *out, int res);
+
+ byte fileReadByte();
+ uint16 fileReadUint16LE();
+
+public:
+ ScummC64File(char *disk1, char *disk2, bool maniac);
+ void setEnc(byte value);
+
+ bool open(const char *filename, AccessMode mode = kFileReadMode);
+ bool openSubFile(const char *filename);
+
+ void close();
+ bool eof() { return _stream->eos(); }
+ uint32 pos() { return _stream->pos(); }
+ uint32 size() { return _stream->size(); }
+ void seek(int32 offs, int whence = SEEK_SET) { _stream->seek(offs, whence); }
+ uint32 read(void *dataPtr, uint32 dataSize) { return _stream->read(dataPtr, dataSize); }
+ uint32 write(const void *dataPtr, uint32 dataSize);
+};
+
+
+/* Direction conversion functions (between old dir and new dir format) */
+int newDirToOldDir(int dir);
+int oldDirToNewDir(int dir);
+
+int normalizeAngle(int angle);
+int fromSimpleDir(int dirtype, int dir);
+int toSimpleDir(int dirtype, int dir);
+
+void checkRange(int max, int min, int no, const char *str);
+
+const char *tag2str(uint32 tag);
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
new file mode 100644
index 0000000000..1cdc446637
--- /dev/null
+++ b/engines/scumm/vars.cpp
@@ -0,0 +1,736 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/config-manager.h"
+#include "scumm/scumm.h"
+#include "scumm/intern.h"
+#ifndef DISABLE_HE
+#include "scumm/intern_he.h"
+#endif
+#include "scumm/logic_he.h"
+#include "sound/mididrv.h"
+
+namespace Scumm {
+
+void ScummEngine::setupScummVars() {
+ VAR_KEYPRESS = 0;
+ VAR_EGO = 1;
+ VAR_CAMERA_POS_X = 2;
+ VAR_HAVE_MSG = 3;
+ VAR_ROOM = 4;
+ VAR_OVERRIDE = 5;
+ VAR_MACHINE_SPEED = 6;
+ VAR_ME = 7;
+ VAR_NUM_ACTOR = 8;
+ VAR_CURRENTDRIVE = 10;
+ VAR_TMR_1 = 11;
+ VAR_TMR_2 = 12;
+ VAR_TMR_3 = 13;
+ VAR_MUSIC_TIMER = 14;
+ VAR_ACTOR_RANGE_MIN = 15;
+ VAR_ACTOR_RANGE_MAX = 16;
+ VAR_CAMERA_MIN_X = 17;
+ VAR_CAMERA_MAX_X = 18;
+ VAR_TIMER_NEXT = 19;
+ VAR_VIRT_MOUSE_X = 20;
+ VAR_VIRT_MOUSE_Y = 21;
+ VAR_ROOM_RESOURCE = 22;
+ VAR_LAST_SOUND = 23;
+ VAR_CUTSCENEEXIT_KEY = 24;
+ VAR_TALK_ACTOR = 25;
+ VAR_CAMERA_FAST_X = 26;
+ VAR_ENTRY_SCRIPT = 28;
+ VAR_ENTRY_SCRIPT2 = 29;
+ VAR_EXIT_SCRIPT = 30;
+ VAR_EXIT_SCRIPT2 = 31;
+ VAR_VERB_SCRIPT = 32;
+ VAR_SENTENCE_SCRIPT = 33;
+ VAR_INVENTORY_SCRIPT = 34;
+ VAR_CUTSCENE_START_SCRIPT = 35;
+ VAR_CUTSCENE_END_SCRIPT = 36;
+ VAR_CHARINC = 37;
+ VAR_WALKTO_OBJ = 38;
+ VAR_HEAPSPACE = 40;
+ VAR_RESTART_KEY = 42;
+ VAR_PAUSE_KEY = 43;
+ VAR_MOUSE_X = 44;
+ VAR_MOUSE_Y = 45;
+ VAR_TIMER = 46;
+ VAR_TMR_4 = 47;
+ VAR_SOUNDCARD = 48;
+ VAR_VIDEOMODE = 49;
+
+ if (_version >= 4) {
+ VAR_SCROLL_SCRIPT = 27;
+ VAR_DEBUGMODE = 39;
+ VAR_MAINMENU_KEY = 50;
+ VAR_FIXEDDISK = 51;
+ VAR_CURSORSTATE = 52;
+ VAR_USERPUT = 53;
+ }
+
+ if (_version >= 5) {
+ VAR_SOUNDPARAM = 64;
+ VAR_SOUNDPARAM2 = 65;
+ VAR_SOUNDPARAM3 = 66;
+ }
+ if (_version >= 5) {
+ VAR_SOUNDRESULT = 56;
+ VAR_TALKSTOP_KEY = 57;
+ VAR_FADE_DELAY = 59;
+ VAR_MOUSEPRESENT = 67;
+ VAR_MEMORY_PERFORMANCE = 68;
+ VAR_VIDEO_PERFORMANCE = 69;
+ VAR_ROOM_FLAG = 70;
+ VAR_GAME_LOADED = 71;
+ VAR_NEW_ROOM = 72;
+ }
+}
+
+void ScummEngine_c64::setupScummVars() {
+ // TODO
+ VAR_EGO = 0;
+ VAR_CAMERA_POS_X = 2;
+ VAR_HAVE_MSG = 3;
+ VAR_ROOM = 4;
+ VAR_OVERRIDE = 6;
+ VAR_CHARCOUNT = 10;
+
+ // FIXME: Should be removed
+ VAR_CURRENT_LIGHTS = 12;
+ VAR_CURSORSTATE = 21;
+ VAR_CAMERA_MIN_X = 23;
+ VAR_CAMERA_MAX_X = 24;
+ VAR_TIMER_NEXT = 25;
+}
+
+void ScummEngine_v2::setupScummVars() {
+ VAR_EGO = 0;
+ VAR_CAMERA_POS_X = 2;
+ VAR_HAVE_MSG = 3;
+ VAR_ROOM = 4;
+ VAR_OVERRIDE = 5;
+ VAR_MACHINE_SPEED = 6;
+ VAR_CHARCOUNT = 7;
+ VAR_ACTIVE_VERB = 8;
+ VAR_ACTIVE_OBJECT1 = 9;
+ VAR_ACTIVE_OBJECT2 = 10;
+ VAR_NUM_ACTOR = 11;
+ VAR_CURRENT_LIGHTS = 12;
+ VAR_CURRENTDRIVE = 13;
+ VAR_MUSIC_TIMER = 17;
+ VAR_VERB_ALLOWED = 18;
+ VAR_ACTOR_RANGE_MIN = 19;
+ VAR_ACTOR_RANGE_MAX = 20;
+ VAR_CURSORSTATE = 21;
+ VAR_CAMERA_MIN_X = 23;
+ VAR_CAMERA_MAX_X = 24;
+ VAR_TIMER_NEXT = 25;
+ VAR_SENTENCE_VERB = 26;
+ VAR_SENTENCE_OBJECT1 = 27;
+ VAR_SENTENCE_OBJECT2 = 28;
+ VAR_SENTENCE_PREPOSITION = 29;
+ VAR_VIRT_MOUSE_X = 30;
+ VAR_VIRT_MOUSE_Y = 31;
+ VAR_CLICK_AREA = 32;
+ VAR_ROOM_RESOURCE = 36;
+ VAR_LAST_SOUND = 37;
+ VAR_BACKUP_VERB = 38;
+ VAR_KEYPRESS = 39;
+ VAR_CUTSCENEEXIT_KEY = 40;
+ VAR_TALK_ACTOR = 41;
+}
+
+void ScummEngine_v5::setupScummVars() {
+ // Many vars are the same as in V5 & V6 games, so just call the inherited method first
+ ScummEngine::setupScummVars();
+
+ VAR_CURRENT_LIGHTS = 9;
+
+ if (_version >= 4) {
+ VAR_V5_TALK_STRING_Y = 54;
+ }
+ if ((_gameId == GID_LOOM && _version == 4) || _version >= 5) {
+ VAR_NOSUBTITLES = 60;
+ }
+}
+
+void ScummEngine_v6::setupScummVars() {
+ // Many vars are the same as in V5 & V6 games, so just call the inherited method first
+ ScummEngine::setupScummVars();
+
+ VAR_ROOM_WIDTH = 41;
+ VAR_ROOM_HEIGHT = 54;
+
+ if (_heversion >= 60) {
+ VAR_NOSUBTITLES = 60;
+ } else {
+ VAR_VOICE_MODE = 60; // 0 is voice, 1 is voice+text, 2 is text only
+ VAR_SAVELOAD_SCRIPT = 61;
+ VAR_SAVELOAD_SCRIPT2 = 62;
+ }
+
+ VAR_LEFTBTN_HOLD = 74;
+ VAR_RIGHTBTN_HOLD = 75;
+
+ VAR_V6_EMSSPACE = 76;
+ VAR_RANDOM_NR = 118;
+
+ VAR_TIMEDATE_YEAR = 119;
+ VAR_TIMEDATE_MONTH = 129;
+ VAR_TIMEDATE_DAY = 128;
+ VAR_TIMEDATE_HOUR = 125;
+ VAR_TIMEDATE_MINUTE = 126;
+
+ // Sam & Max specific
+ if (_gameId == GID_SAMNMAX) {
+ VAR_V6_SOUNDMODE = 9;
+ VAR_CHARSET_MASK = 123;
+ }
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v70he::setupScummVars() {
+ ScummEngine_v6::setupScummVars();
+
+ VAR_CURRENTDRIVE = 0xFF;
+ VAR_MUSIC_TIMER = 0xFF;
+
+ VAR_NUM_SOUND_CHANNELS = 9;
+ VAR_TALK_CHANNEL = 10;
+ VAR_SOUND_CHANNEL = 14;
+}
+
+void ScummEngine_v72he::setupScummVars() {
+ VAR_KEYPRESS = 0;
+ VAR_DEBUGMODE = 1;
+ VAR_TIMER_NEXT = 2;
+ VAR_OVERRIDE = 3;
+ VAR_WALKTO_OBJ = 4;
+ VAR_RANDOM_NR = 5;
+
+ VAR_GAME_LOADED = 8;
+ VAR_EGO = 9;
+ VAR_NUM_ACTOR = 10;
+
+ VAR_VIRT_MOUSE_X = 13;
+ VAR_VIRT_MOUSE_Y = 14;
+ VAR_MOUSE_X = 15;
+ VAR_MOUSE_Y = 16;
+ VAR_LEFTBTN_HOLD = 17;
+ VAR_RIGHTBTN_HOLD = 18;
+
+ VAR_CURSORSTATE = 19;
+ VAR_USERPUT = 20;
+ VAR_ROOM = 21;
+ VAR_ROOM_WIDTH = 22;
+ VAR_ROOM_HEIGHT = 23;
+ VAR_CAMERA_POS_X = 24;
+ VAR_CAMERA_MIN_X = 25;
+ VAR_CAMERA_MAX_X = 26;
+ VAR_ROOM_RESOURCE = 27;
+ VAR_SCROLL_SCRIPT = 28;
+ VAR_ENTRY_SCRIPT = 29;
+ VAR_ENTRY_SCRIPT2 = 30;
+ VAR_EXIT_SCRIPT = 31;
+ VAR_EXIT_SCRIPT2 = 32;
+ VAR_VERB_SCRIPT = 33;
+ VAR_SENTENCE_SCRIPT = 34;
+ VAR_INVENTORY_SCRIPT = 35;
+ VAR_CUTSCENE_START_SCRIPT = 36;
+ VAR_CUTSCENE_END_SCRIPT = 37;
+
+ VAR_RESTART_KEY = 42;
+ VAR_PAUSE_KEY = 43;
+ VAR_CUTSCENEEXIT_KEY = 44;
+ VAR_TALKSTOP_KEY = 45;
+ VAR_HAVE_MSG = 46;
+ VAR_SUBTITLES = 47;
+ VAR_CHARINC = 48;
+ VAR_TALK_ACTOR = 49;
+ VAR_LAST_SOUND = 50;
+ VAR_TALK_CHANNEL = 51;
+ VAR_SOUND_CHANNEL = 52;
+
+ VAR_MEMORY_PERFORMANCE = 57;
+ VAR_VIDEO_PERFORMANCE = 58;
+ VAR_NEW_ROOM = 59;
+ VAR_TMR_1 = 60;
+ VAR_TMR_2 = 61;
+ VAR_TMR_3 = 62;
+ VAR_TIMEDATE_HOUR = 63;
+ VAR_TIMEDATE_MINUTE = 64;
+ VAR_TIMEDATE_DAY = 65;
+ VAR_TIMEDATE_MONTH = 66;
+ VAR_TIMEDATE_YEAR = 67;
+
+ VAR_NUM_ROOMS = 68;
+ VAR_NUM_SCRIPTS = 69;
+ VAR_NUM_SOUNDS = 70;
+ VAR_NUM_COSTUMES = 71;
+ VAR_NUM_IMAGES = 72;
+ VAR_NUM_CHARSETS = 73;
+ VAR_NUM_GLOBAL_OBJS = 74;
+ VAR_MOUSE_STATE = 75;
+ VAR_POLYGONS_ONLY = 76;
+
+ if (_heversion <= 73) {
+ VAR_NUM_SOUND_CHANNELS = 56;
+ }
+}
+
+void ScummEngine_v80he::setupScummVars() {
+ ScummEngine_v72he::setupScummVars();
+
+ VAR_PLATFORM = 78; // 1 is PC, 2 is Macintosh
+ VAR_WINDOWS_VERSION = 79; // 31 is Windows 3.1, 40 is Windows 95+
+ VAR_CURRENT_CHARSET = 80;
+ VAR_SOUNDCODE_TMR = 84;
+ VAR_KEY_STATE = 86;
+ VAR_NUM_SOUND_CHANNELS = 88;
+ VAR_COLOR_DEPTH = 89;
+ VAR_REDRAW_ALL_ACTORS = 95;
+}
+
+void ScummEngine_v90he::setupScummVars() {
+ ScummEngine_v80he::setupScummVars();
+
+ VAR_SCRIPT_CYCLE = 103;
+ VAR_NUM_SCRIPT_CYCLES = 104;
+
+ if (_heversion >= 95) {
+ VAR_NUM_SPRITE_GROUPS = 105;
+ VAR_NUM_SPRITES = 106;
+ VAR_U32_VERSION = 107;
+ VAR_U32_ARRAY_UNK = 116;
+ VAR_WIZ_TCOLOR = 117;
+ VAR_RESERVED_SOUND_CHANNELS = 120;
+ }
+ if (_heversion >= 98) {
+ VAR_SKIP_RESET_TALK_ACTOR = 125;
+ }
+ if (_heversion >= 99) {
+ VAR_MAIN_SCRIPT = 127;
+ VAR_NUM_PALETTES = 130;
+ VAR_NUM_UNK = 131;
+ }
+}
+#endif
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::setupScummVars() {
+ VAR_MOUSE_X = 1;
+ VAR_MOUSE_Y = 2;
+ VAR_VIRT_MOUSE_X = 3;
+ VAR_VIRT_MOUSE_Y = 4;
+ VAR_ROOM_WIDTH = 5;
+ VAR_ROOM_HEIGHT = 6;
+ VAR_CAMERA_POS_X = 7;
+ VAR_CAMERA_POS_Y = 8;
+ VAR_OVERRIDE = 9;
+ VAR_ROOM = 10;
+ VAR_ROOM_RESOURCE = 11;
+ VAR_TALK_ACTOR = 12;
+ VAR_HAVE_MSG = 13;
+ VAR_TIMER = 14;
+ VAR_TMR_4 = 15;
+
+ VAR_TIMEDATE_YEAR = 16;
+ VAR_TIMEDATE_MONTH = 17;
+ VAR_TIMEDATE_DAY = 18;
+ VAR_TIMEDATE_HOUR = 19;
+ VAR_TIMEDATE_MINUTE = 20;
+ VAR_TIMEDATE_SECOND = 21;
+
+ VAR_LEFTBTN_DOWN = 22;
+ VAR_RIGHTBTN_DOWN = 23;
+ VAR_LEFTBTN_HOLD = 24;
+ VAR_RIGHTBTN_HOLD = 25;
+
+ VAR_MEMORY_PERFORMANCE = 26;
+ VAR_VIDEO_PERFORMANCE = 27;
+ VAR_GAME_LOADED = 29;
+ VAR_V6_EMSSPACE = 32;
+ VAR_VOICE_MODE = 33; // 0 is voice, 1 is voice+text, 2 is text only
+ VAR_RANDOM_NR = 34;
+ VAR_NEW_ROOM = 35;
+ VAR_WALKTO_OBJ = 36;
+
+ VAR_NUM_GLOBAL_OBJS = 37;
+
+ VAR_CAMERA_DEST_X = 38;
+ VAR_CAMERA_DEST_Y = 39;
+ VAR_CAMERA_FOLLOWED_ACTOR = 40;
+
+ VAR_SCROLL_SCRIPT = 50;
+ VAR_ENTRY_SCRIPT = 51;
+ VAR_ENTRY_SCRIPT2 = 52;
+ VAR_EXIT_SCRIPT = 53;
+ VAR_EXIT_SCRIPT2 = 54;
+ VAR_VERB_SCRIPT = 55;
+ VAR_SENTENCE_SCRIPT = 56;
+ VAR_INVENTORY_SCRIPT = 57;
+ VAR_CUTSCENE_START_SCRIPT = 58;
+ VAR_CUTSCENE_END_SCRIPT = 59;
+ VAR_SAVELOAD_SCRIPT = 60;
+ VAR_SAVELOAD_SCRIPT2 = 61;
+
+ VAR_CUTSCENEEXIT_KEY = 62;
+ VAR_RESTART_KEY = 63;
+ VAR_PAUSE_KEY = 64;
+ VAR_MAINMENU_KEY = 65;
+ VAR_VERSION_KEY = 66;
+ VAR_TALKSTOP_KEY = 67;
+ VAR_KEYPRESS = 118;
+
+ VAR_TIMER_NEXT = 97;
+ VAR_TMR_1 = 98;
+ VAR_TMR_2 = 99;
+ VAR_TMR_3 = 100;
+
+ VAR_CAMERA_MIN_X = 101;
+ VAR_CAMERA_MAX_X = 102;
+ VAR_CAMERA_MIN_Y = 103;
+ VAR_CAMERA_MAX_Y = 104;
+ VAR_CAMERA_THRESHOLD_X = 105;
+ VAR_CAMERA_THRESHOLD_Y = 106;
+ VAR_CAMERA_SPEED_X = 107;
+ VAR_CAMERA_SPEED_Y = 108;
+ VAR_CAMERA_ACCEL_X = 109;
+ VAR_CAMERA_ACCEL_Y = 110;
+
+ VAR_EGO = 111;
+
+ VAR_CURSORSTATE = 112;
+ VAR_USERPUT = 113;
+ VAR_DEFAULT_TALK_DELAY = 114;
+ VAR_CHARINC = 115;
+ VAR_DEBUGMODE = 116;
+ VAR_FADE_DELAY = 117;
+
+ // Full Throttle specific
+ if (_gameId == GID_FT) {
+ VAR_CHARSET_MASK = 119;
+ }
+
+ VAR_VIDEONAME = 123;
+
+ VAR_STRING2DRAW = 130;
+ VAR_CUSTOMSCALETABLE = 131;
+
+ VAR_BLAST_ABOVE_TEXT = 133;
+
+ VAR_MUSIC_BUNDLE_LOADED = 135;
+ VAR_VOICE_BUNDLE_LOADED = 136;
+
+}
+
+void ScummEngine_v8::setupScummVars() {
+ VAR_ROOM_WIDTH = 1;
+ VAR_ROOM_HEIGHT = 2;
+
+ VAR_MOUSE_X = 3;
+ VAR_MOUSE_Y = 4;
+ VAR_VIRT_MOUSE_X = 5;
+ VAR_VIRT_MOUSE_Y = 6;
+
+ VAR_CURSORSTATE = 7;
+ VAR_USERPUT = 8;
+
+ VAR_CAMERA_POS_X = 9;
+ VAR_CAMERA_POS_Y = 10;
+ VAR_CAMERA_DEST_X = 11;
+ VAR_CAMERA_DEST_Y = 12;
+ VAR_CAMERA_FOLLOWED_ACTOR = 13;
+
+ VAR_TALK_ACTOR = 14;
+ VAR_HAVE_MSG = 15;
+
+ VAR_LEFTBTN_DOWN = 16;
+ VAR_RIGHTBTN_DOWN = 17;
+ VAR_LEFTBTN_HOLD = 18;
+ VAR_RIGHTBTN_HOLD = 19;
+
+ VAR_TIMEDATE_YEAR = 24;
+ VAR_TIMEDATE_MONTH = 25;
+ VAR_TIMEDATE_DAY = 26;
+ VAR_TIMEDATE_HOUR = 27;
+ VAR_TIMEDATE_MINUTE = 28;
+ VAR_TIMEDATE_SECOND = 29;
+
+ VAR_OVERRIDE = 30;
+ VAR_ROOM = 31;
+ VAR_NEW_ROOM = 32;
+ VAR_WALKTO_OBJ = 33;
+ VAR_TIMER = 34;
+
+ VAR_VOICE_MODE = 39; // 0 is voice, 1 is voice+text, 2 is text only
+ VAR_GAME_LOADED = 40;
+ VAR_LANGUAGE = 41;
+
+ VAR_CURRENTDISK = 42;
+ VAR_MUSIC_BUNDLE_LOADED = 45;
+ VAR_VOICE_BUNDLE_LOADED = 46;
+
+ VAR_SCROLL_SCRIPT = 50;
+ VAR_ENTRY_SCRIPT = 51;
+ VAR_ENTRY_SCRIPT2 = 52;
+ VAR_EXIT_SCRIPT = 53;
+ VAR_EXIT_SCRIPT2 = 54;
+ VAR_VERB_SCRIPT = 55;
+ VAR_SENTENCE_SCRIPT = 56;
+ VAR_INVENTORY_SCRIPT = 57;
+ VAR_CUTSCENE_START_SCRIPT = 58;
+ VAR_CUTSCENE_END_SCRIPT = 59;
+
+ VAR_CUTSCENEEXIT_KEY = 62;
+
+ VAR_PAUSE_KEY = 64;
+ VAR_MAINMENU_KEY = 65;
+ VAR_VERSION_KEY = 66;
+ VAR_TALKSTOP_KEY = 67;
+
+ VAR_CUSTOMSCALETABLE = 111;
+
+ VAR_TIMER_NEXT = 112;
+ VAR_TMR_1 = 113;
+ VAR_TMR_2 = 114;
+ VAR_TMR_3 = 115;
+
+ VAR_CAMERA_MIN_X = 116;
+ VAR_CAMERA_MAX_X = 117;
+ VAR_CAMERA_MIN_Y = 118;
+ VAR_CAMERA_MAX_Y = 119;
+ VAR_CAMERA_SPEED_X = 120;
+ VAR_CAMERA_SPEED_Y = 121;
+ VAR_CAMERA_ACCEL_X = 122;
+ VAR_CAMERA_ACCEL_Y = 123;
+ VAR_CAMERA_THRESHOLD_X = 124;
+ VAR_CAMERA_THRESHOLD_Y = 125;
+
+ VAR_EGO = 126;
+
+ VAR_DEFAULT_TALK_DELAY = 128;
+ VAR_CHARINC = 129;
+
+ VAR_DEBUGMODE = 130;
+ VAR_KEYPRESS = 132;
+ VAR_BLAST_ABOVE_TEXT = 133;
+ VAR_SYNC = 134;
+}
+#endif
+
+void ScummEngine_v2::initScummVars() {
+
+ if (_platform == Common::kPlatformC64 && _gameId == GID_MANIAC) {
+ VAR(VAR_EGO) = 3;
+ }
+
+ // This needs to be at least greater than 40 to get the more
+ // elaborate version of the EGA Zak into. I don't know where
+ // else it makes any difference.
+ if (_gameId == GID_ZAK)
+ VAR(VAR_MACHINE_SPEED) = 0x7FFF;
+}
+
+void ScummEngine_v5::initScummVars() {
+ ScummEngine::initScummVars();
+
+ if (_version >= 4 && _version <= 5)
+ VAR(VAR_V5_TALK_STRING_Y) = -0x50;
+
+ if (VAR_CURRENT_LIGHTS != 0xFF) {
+ // Setup light
+ VAR(VAR_CURRENT_LIGHTS) = LIGHTMODE_actor_base | LIGHTMODE_actor_color | LIGHTMODE_screen;
+ }
+
+ if (_gameId == GID_MONKEY)
+ _scummVars[74] = 1225;
+}
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::initScummVars() {
+ ScummEngine::initScummVars();
+
+ if (_version == 8) { // FIXME: How do we deal with non-cd installs?
+ VAR(VAR_CURRENTDISK) = 1;
+ VAR(VAR_LANGUAGE) = _language;
+ } else {
+ VAR(VAR_V6_EMSSPACE) = 10000;
+ VAR(VAR_NUM_GLOBAL_OBJS) = _numGlobalObjects - 1;
+ }
+
+ VAR(VAR_DEFAULT_TALK_DELAY) = 60;
+ VAR(VAR_VOICE_MODE) = ConfMan.getBool("subtitles");
+}
+#endif
+
+#ifndef DISABLE_HE
+void ScummEngine_v70he::initScummVars() {
+ ScummEngine::initScummVars();
+
+ if (VAR_MACHINE_SPEED != 0xFF)
+ VAR(VAR_MACHINE_SPEED) = 13;
+
+ VAR(VAR_NUM_SOUND_CHANNELS) = 8;
+ VAR(VAR_SOUND_CHANNEL) = 1;
+ VAR(VAR_TALK_CHANNEL) = 2;
+}
+
+void ScummEngine_v72he::initScummVars() {
+ ScummEngine_v70he::initScummVars();
+
+ VAR(VAR_VIDEO_PERFORMANCE) = 26;
+
+ VAR(VAR_NUM_ROOMS) = _numRooms - 1;
+ VAR(VAR_NUM_SCRIPTS) = _numScripts - 1;
+ VAR(VAR_NUM_SOUNDS) = _numSounds - 1;
+ VAR(VAR_NUM_COSTUMES) = _numCostumes - 1;
+ VAR(VAR_NUM_IMAGES) = _numImages - 1;
+ VAR(VAR_NUM_CHARSETS) = _numCharsets - 1;
+ VAR(VAR_NUM_GLOBAL_OBJS) = _numGlobalObjects - 1;
+}
+
+void ScummEngine_v80he::initScummVars() {
+ ScummEngine_v72he::initScummVars();
+
+ VAR(VAR_PLATFORM) = 1;
+ VAR(VAR_WINDOWS_VERSION) = 40;
+ VAR(VAR_COLOR_DEPTH) = 256;
+}
+
+void ScummEngine_v90he::initScummVars() {
+ ScummEngine_v80he::initScummVars();
+
+ VAR(VAR_SCRIPT_CYCLE) = 1;
+ VAR(VAR_NUM_SCRIPT_CYCLES) = 1;
+
+ if (_heversion >= 95) {
+ VAR(VAR_NUM_SPRITE_GROUPS) = MAX(64, _numSprites / 4) - 1;
+ VAR(VAR_NUM_SPRITES) = _numSprites - 1;
+ VAR(VAR_WIZ_TCOLOR) = 5;
+ VAR(VAR_RESERVED_SOUND_CHANNELS) = 9;
+ }
+ if (_heversion >= 98) {
+ VAR(VAR_U32_VERSION) = _logicHE->versionID();
+ VAR(VAR_U32_ARRAY_UNK) = 0;
+ }
+}
+
+void ScummEngine_v99he::initScummVars() {
+ ScummEngine_v90he::initScummVars();
+
+ VAR(VAR_NUM_PALETTES) = _numPalettes;
+ VAR(VAR_NUM_UNK) = _numUnk;
+}
+#endif
+
+void ScummEngine::initScummVars() {
+ if (_heversion < 70 && _version <= 6) {
+ switch (_musicType) {
+ case MDT_NONE:
+ VAR(VAR_SOUNDCARD) = 0;
+ break;
+ case MDT_PCSPK:
+ VAR(VAR_SOUNDCARD) = 1;
+ break;
+ case MDT_ADLIB:
+ VAR(VAR_SOUNDCARD) = 3;
+ break;
+ default:
+ if ((_gameId == GID_MONKEY_EGA || _gameId == GID_MONKEY_VGA || (_gameId == GID_LOOM && _version == 3))
+ && (_platform == Common::kPlatformPC)) {
+ if (_gameId == GID_LOOM) {
+ char buf[50];
+ Common::File f;
+ for (int i = 82; i < 85; i++) {
+ sprintf(buf, "%d.LFL", i);
+ if (!Common::File::exists(buf))
+ error("Native MIDI support requires Roland patch from LucasArts");
+ }
+ } else if (_gameId == GID_MONKEY_EGA) {
+ if (!Common::File::exists("DISK09.LEC"))
+ error("Native MIDI support requires Roland patch from LucasArts");
+ }
+ VAR(VAR_SOUNDCARD) = 4;
+ } else {
+ VAR(VAR_SOUNDCARD) = 3;
+ }
+ }
+
+ if (_platform == Common::kPlatformFMTowns)
+ VAR(VAR_VIDEOMODE) = 42;
+ else if (_platform == Common::kPlatformMacintosh)
+ VAR(VAR_VIDEOMODE) = 50;
+ else if (_platform == Common::kPlatformAmiga)
+ VAR(VAR_VIDEOMODE) = 82;
+ else if (_renderMode == Common::kRenderCGA)
+ VAR(VAR_VIDEOMODE) = 4;
+ else if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG)
+ VAR(VAR_VIDEOMODE) = 30;
+ else if (_renderMode == Common::kRenderEGA)
+ VAR(VAR_VIDEOMODE) = 13;
+ else
+ VAR(VAR_VIDEOMODE) = 19;
+
+ if (_platform == Common::kPlatformMacintosh && (_features & GF_OLD_BUNDLE)) {
+ // Set screen size for the Macintosh version of Indy3/Loom
+ VAR(39) = 320;
+ }
+ if (_platform == Common::kPlatformPC && _gameId == GID_LOOM && _version == 3) {
+ // Set number of sound resources
+ VAR(39) = 80;
+ }
+
+ if (_gameId == GID_LOOM || _version >= 4)
+ VAR(VAR_HEAPSPACE) = 1400;
+ if (_version >= 4)
+ VAR(VAR_FIXEDDISK) = 1;
+ if (_version >= 5)
+ VAR(VAR_MOUSEPRESENT) = 1;
+ if (_version == 6)
+ VAR(VAR_V6_EMSSPACE) = 10000;
+
+ if (_heversion >= 60) {
+ // Set fast speed, to enable all animations
+ VAR(VAR_MACHINE_SPEED) = 2;
+
+ VAR(VAR_SOUNDPARAM) = 1; // Soundblaster for music
+ VAR(VAR_SOUNDPARAM2) = 1; // Soundblaster for sound effects
+ }
+ }
+
+ if (VAR_ROOM_WIDTH != 0xFF && VAR_ROOM_HEIGHT != 0xFF) {
+ VAR(VAR_ROOM_WIDTH) = _screenWidth;
+ VAR(VAR_ROOM_HEIGHT) = _screenHeight;
+ }
+
+ if (VAR_FADE_DELAY != 0xFF)
+ VAR(VAR_FADE_DELAY) = 3;
+
+ VAR(VAR_CHARINC) = 4;
+ setTalkingActor(0);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp
new file mode 100644
index 0000000000..7b01527b92
--- /dev/null
+++ b/engines/scumm/verbs.cpp
@@ -0,0 +1,855 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/intern.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+enum {
+ kInventoryUpArrow = 4,
+ kInventoryDownArrow = 5,
+ kSentenceLine = 6
+};
+
+struct VerbSettings {
+ int id;
+ int x_pos;
+ int y_pos;
+ const char *name;
+};
+
+static const VerbSettings C64VerbTable[] =
+{
+ { 1, 8, 0, "Open"},
+ { 2, 8, 1, "Close"},
+ { 3, 0, 2, "Give"},
+ { 4, 32, 0, "Turn On"},
+ { 5, 32, 1, "Turn Off"},
+ { 6, 32, 2, "Fix"},
+ { 7, 24, 0, "New Kid"},
+ { 8, 24, 1, "Unlock"},
+ { 9, 0, 0, "Push"},
+ {10, 0, 1, "Pull"},
+ {11, 24, 2, "Use"},
+ {12, 8, 2, "Read"},
+ {13, 15, 0, "Walk To"},
+ {14, 15, 1, "Pick Up"},
+ {15, 15, 2, "What Is"}
+};
+
+void ScummEngine_c64::initC64Verbs() {
+ VirtScreen *virt = &virtscr[kVerbVirtScreen];
+ VerbSlot *vs;
+ int i;
+
+ for (i = 1; i < 16; i++) {
+ vs = &_verbs[i];
+ vs->verbid = C64VerbTable[i - 1].id;
+ vs->color = 5;
+ vs->hicolor = 7;
+ vs->dimcolor = 11;
+ vs->type = kTextVerbType;
+ vs->charset_nr = _string[0]._default.charset;
+ vs->curmode = 1;
+ vs->saveid = 0;
+ vs->key = 0;
+ vs->center = 0;
+ vs->imgindex = 0;
+
+ vs->curRect.left = C64VerbTable[i - 1].x_pos * 8;
+ vs->curRect.top = C64VerbTable[i - 1].y_pos * 8 + virt->topline + 8;
+
+ loadPtrToResource(rtVerb, i, (const byte*)C64VerbTable[i - 1].name);
+ }
+}
+
+void ScummEngine_v2::initV2MouseOver() {
+ int i;
+ int arrow_color, color, hi_color;
+
+ if (_version == 1) {
+ color = 16;
+ hi_color = 7;
+ arrow_color = 6;
+ } else {
+ color = 13;
+ hi_color = 14;
+ arrow_color = 1;
+ }
+
+ _mouseOverBoxV2 = -1;
+
+ // Inventory items
+
+ for (i = 0; i < 2; i++) {
+ _mouseOverBoxesV2[2 * i].rect.left = 0;
+ _mouseOverBoxesV2[2 * i].rect.right = 144;
+ _mouseOverBoxesV2[2 * i].rect.top = 32 + 8 * i;
+ _mouseOverBoxesV2[2 * i].rect.bottom = _mouseOverBoxesV2[2 * i].rect.top + 8;
+
+ _mouseOverBoxesV2[2 * i].color = color;
+ _mouseOverBoxesV2[2 * i].hicolor = hi_color;
+
+ _mouseOverBoxesV2[2 * i + 1].rect.left = 176;
+ _mouseOverBoxesV2[2 * i + 1].rect.right = 320;
+ _mouseOverBoxesV2[2 * i + 1].rect.top = _mouseOverBoxesV2[2 * i].rect.top;
+ _mouseOverBoxesV2[2 * i + 1].rect.bottom = _mouseOverBoxesV2[2 * i].rect.bottom;
+
+ _mouseOverBoxesV2[2 * i + 1].color = color;
+ _mouseOverBoxesV2[2 * i + 1].hicolor = hi_color;
+ }
+
+ // Inventory arrows
+
+ _mouseOverBoxesV2[kInventoryUpArrow].rect.left = 144;
+ _mouseOverBoxesV2[kInventoryUpArrow].rect.right = 176;
+ _mouseOverBoxesV2[kInventoryUpArrow].rect.top = 32;
+ _mouseOverBoxesV2[kInventoryUpArrow].rect.bottom = 40;
+
+ _mouseOverBoxesV2[kInventoryUpArrow].color = arrow_color;
+ _mouseOverBoxesV2[kInventoryUpArrow].hicolor = hi_color;
+
+ _mouseOverBoxesV2[kInventoryDownArrow].rect.left = 144;
+ _mouseOverBoxesV2[kInventoryDownArrow].rect.right = 176;
+ _mouseOverBoxesV2[kInventoryDownArrow].rect.top = 40;
+ _mouseOverBoxesV2[kInventoryDownArrow].rect.bottom = 48;
+
+ _mouseOverBoxesV2[kInventoryDownArrow].color = arrow_color;
+ _mouseOverBoxesV2[kInventoryDownArrow].hicolor = hi_color;
+
+ // Sentence line
+
+ _mouseOverBoxesV2[kSentenceLine].rect.left = 0;
+ _mouseOverBoxesV2[kSentenceLine].rect.right = 320;
+ _mouseOverBoxesV2[kSentenceLine].rect.top = 0;
+ _mouseOverBoxesV2[kSentenceLine].rect.bottom = 8;
+
+ _mouseOverBoxesV2[kSentenceLine].color = color;
+ _mouseOverBoxesV2[kSentenceLine].hicolor = hi_color;
+}
+
+void ScummEngine_v2::initNESMouseOver() {
+ int i;
+ int arrow_color, color, hi_color;
+
+ color = 0;
+ hi_color = 0;
+ arrow_color = 0;
+
+ _mouseOverBoxV2 = -1;
+
+ // Inventory items
+
+ for (i = 0; i < 2; i++) {
+ _mouseOverBoxesV2[2 * i].rect.left = 16;
+ _mouseOverBoxesV2[2 * i].rect.right = 120;
+ _mouseOverBoxesV2[2 * i].rect.top = 48 + 8 * i;
+ _mouseOverBoxesV2[2 * i].rect.bottom = _mouseOverBoxesV2[2 * i].rect.top + 8;
+
+ _mouseOverBoxesV2[2 * i].color = color;
+ _mouseOverBoxesV2[2 * i].hicolor = hi_color;
+
+ _mouseOverBoxesV2[2 * i + 1].rect.left = 152;
+ _mouseOverBoxesV2[2 * i + 1].rect.right = 256;
+ _mouseOverBoxesV2[2 * i + 1].rect.top = _mouseOverBoxesV2[2 * i].rect.top;
+ _mouseOverBoxesV2[2 * i + 1].rect.bottom = _mouseOverBoxesV2[2 * i].rect.bottom;
+
+ _mouseOverBoxesV2[2 * i + 1].color = color;
+ _mouseOverBoxesV2[2 * i + 1].hicolor = hi_color;
+ }
+
+ // Inventory arrows
+
+ _mouseOverBoxesV2[kInventoryUpArrow].rect.left = 128;
+ _mouseOverBoxesV2[kInventoryUpArrow].rect.right = 136;
+ _mouseOverBoxesV2[kInventoryUpArrow].rect.top = 48;
+ _mouseOverBoxesV2[kInventoryUpArrow].rect.bottom = 56;
+
+ _mouseOverBoxesV2[kInventoryUpArrow].color = arrow_color;
+ _mouseOverBoxesV2[kInventoryUpArrow].hicolor = hi_color;
+
+ _mouseOverBoxesV2[kInventoryDownArrow].rect.left = 136;
+ _mouseOverBoxesV2[kInventoryDownArrow].rect.right = 144;
+ _mouseOverBoxesV2[kInventoryDownArrow].rect.top = 48;
+ _mouseOverBoxesV2[kInventoryDownArrow].rect.bottom = 56;
+
+ _mouseOverBoxesV2[kInventoryDownArrow].color = arrow_color;
+ _mouseOverBoxesV2[kInventoryDownArrow].hicolor = hi_color;
+
+ // Sentence line
+
+ _mouseOverBoxesV2[kSentenceLine].rect.left = 16;
+ _mouseOverBoxesV2[kSentenceLine].rect.right = 256;
+ _mouseOverBoxesV2[kSentenceLine].rect.top = 0;
+ _mouseOverBoxesV2[kSentenceLine].rect.bottom = 8;
+
+ _mouseOverBoxesV2[kSentenceLine].color = color;
+ _mouseOverBoxesV2[kSentenceLine].hicolor = hi_color;
+}
+
+void ScummEngine_v2::checkV2MouseOver(Common::Point pos) {
+ VirtScreen *vs = &virtscr[kVerbVirtScreen];
+ Common::Rect rect;
+ byte *ptr, *dst;
+ int i, x, y, new_box = -1;
+
+ // Don't do anything unless the inventory is active
+ if (!(_userState & 64)) {
+ _mouseOverBoxV2 = -1;
+ return;
+ }
+
+ if (_cursor.state > 0) {
+ for (i = 0; i < ARRAYSIZE(_mouseOverBoxesV2); i++) {
+ if (_mouseOverBoxesV2[i].rect.contains(pos.x, pos.y - vs->topline)) {
+ new_box = i;
+ break;
+ }
+ }
+ }
+
+ if (new_box != _mouseOverBoxV2) {
+ if (_mouseOverBoxV2 != -1) {
+ rect = _mouseOverBoxesV2[_mouseOverBoxV2].rect;
+
+ dst = ptr = vs->getPixels(rect.left, rect.top);
+
+ // Remove highlight.
+ for (y = rect.height() - 1; y >= 0; y--) {
+ for (x = rect.width() - 1; x >= 0; x--) {
+ if (dst[x] == _mouseOverBoxesV2[_mouseOverBoxV2].hicolor)
+ dst[x] = _mouseOverBoxesV2[_mouseOverBoxV2].color;
+ }
+ dst += vs->pitch;
+ }
+
+ markRectAsDirty(kVerbVirtScreen, rect);
+ }
+
+ if (new_box != -1) {
+ rect = _mouseOverBoxesV2[new_box].rect;
+
+ dst = ptr = vs->getPixels(rect.left, rect.top);
+
+ // Apply highlight
+ for (y = rect.height() - 1; y >= 0; y--) {
+ for (x = rect.width() - 1; x >= 0; x--) {
+ if (dst[x] == _mouseOverBoxesV2[new_box].color)
+ dst[x] = _mouseOverBoxesV2[new_box].hicolor;
+ }
+ dst += vs->pitch;
+ }
+
+ markRectAsDirty(kVerbVirtScreen, rect);
+ }
+
+ _mouseOverBoxV2 = new_box;
+ }
+}
+
+void ScummEngine_v2::checkV2Inventory(int x, int y) {
+ int inventoryArea = (_platform == Common::kPlatformNES) ? 48: 32;
+ int object = 0;
+
+ y -= virtscr[kVerbVirtScreen].topline;
+
+ if ((y < inventoryArea) || !(_mouseAndKeyboardStat & MBS_LEFT_CLICK))
+ return;
+
+ if (_mouseOverBoxesV2[kInventoryUpArrow].rect.contains(x, y)) {
+ if (_inventoryOffset >= 2) {
+ _inventoryOffset -= 2;
+ redrawV2Inventory();
+ }
+ } else if (_mouseOverBoxesV2[kInventoryDownArrow].rect.contains(x, y)) {
+ if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
+ _inventoryOffset += 2;
+ redrawV2Inventory();
+ }
+ }
+
+ for (object = 0; object < 4; object++) {
+ if (_mouseOverBoxesV2[object].rect.contains(x, y)) {
+ break;
+ }
+ }
+
+ if (object >= 4)
+ return;
+
+ object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
+
+ if (object > 0) {
+ runInputScript(3, object, 0);
+ }
+}
+
+void ScummEngine_v2::redrawV2Inventory() {
+ VirtScreen *vs = &virtscr[kVerbVirtScreen];
+ int i;
+ int max_inv;
+ Common::Rect inventoryBox;
+ int inventoryArea = (_platform == Common::kPlatformNES) ? 48: 32;
+ int maxChars = (_platform == Common::kPlatformNES) ? 13: 18;
+
+ _mouseOverBoxV2 = -1;
+
+ if (!(_userState & 64)) // Don't draw inventory unless active
+ return;
+
+ // Clear on all invocations
+ inventoryBox.top = vs->topline + inventoryArea;
+ inventoryBox.bottom = vs->topline + virtscr[kVerbVirtScreen].h;
+ inventoryBox.left = 0;
+ inventoryBox.right = vs->w;
+ restoreBG(inventoryBox);
+
+ _string[1].charset = 1;
+
+ max_inv = getInventoryCount(_scummVars[VAR_EGO]) - _inventoryOffset;
+ if (max_inv > 4)
+ max_inv = 4;
+ for (i = 0; i < max_inv; i++) {
+ int obj = findInventory(_scummVars[VAR_EGO], i + 1 + _inventoryOffset);
+ if (obj == 0)
+ break;
+
+ _string[1].ypos = _mouseOverBoxesV2[i].rect.top + vs->topline;
+ _string[1].xpos = _mouseOverBoxesV2[i].rect.left;
+
+ _string[1].color = _mouseOverBoxesV2[i].color;
+
+ const byte *tmp = getObjOrActorName(obj);
+ assert(tmp);
+
+ // Prevent inventory entries from overflowing by truncating the text
+ byte msg[20];
+ msg[maxChars] = 0;
+ strncpy((char *)msg, (const char *)tmp, maxChars);
+
+ // Draw it
+ drawString(1, msg);
+ }
+
+
+ // If necessary, draw "up" arrow
+ if (_inventoryOffset > 0) {
+ _string[1].xpos = _mouseOverBoxesV2[kInventoryUpArrow].rect.left;
+ _string[1].ypos = _mouseOverBoxesV2[kInventoryUpArrow].rect.top + vs->topline;
+ _string[1].color = _mouseOverBoxesV2[kInventoryUpArrow].color;
+ if (_platform == Common::kPlatformNES)
+ drawString(1, (const byte *)"\x7E");
+ else
+ drawString(1, (const byte *)" \1\2");
+ }
+
+ // If necessary, draw "down" arrow
+ if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
+ _string[1].xpos = _mouseOverBoxesV2[kInventoryDownArrow].rect.left;
+ _string[1].ypos = _mouseOverBoxesV2[kInventoryDownArrow].rect.top + vs->topline;
+ _string[1].color = _mouseOverBoxesV2[kInventoryDownArrow].color;
+ if (_platform == Common::kPlatformNES)
+ drawString(1, (const byte *)"\x7F");
+ else
+ drawString(1, (const byte *)" \3\4");
+ }
+}
+
+void ScummEngine::redrawVerbs() {
+ if (_version <= 2 && !(_userState & 128)) // Don't draw verbs unless active
+ return;
+
+ int i, verb = 0;
+ if (_cursor.state > 0)
+ verb = findVerbAtPos(_mouse.x, _mouse.y);
+
+ // Iterate over all verbs.
+ // Note: This is the correct order (at least for MI EGA, MI2, Full Throttle).
+ // Do not change it! If you discover, based on disasm, that some game uses
+ // another (e.g. the reverse) order here, you have to use an if/else construct
+ // to add it as a special case!
+ for (i = 0; i < _numVerbs; i++) {
+ if (i == verb && _verbs[verb].hicolor)
+ drawVerb(i, 1);
+ else
+ drawVerb(i, 0);
+ }
+ _verbMouseOver = verb;
+}
+
+void ScummEngine::handleMouseOver(bool updateInventory) {
+ if (_completeScreenRedraw) {
+ verbMouseOver(0);
+ } else {
+ if (_cursor.state > 0)
+ verbMouseOver(findVerbAtPos(_mouse.x, _mouse.y));
+ }
+}
+
+void ScummEngine_v2::handleMouseOver(bool updateInventory) {
+ ScummEngine::handleMouseOver(updateInventory);
+
+ if (updateInventory) {
+ // FIXME/TODO: Reset and redraw the sentence line
+ _inventoryOffset = 0;
+ }
+ if (_completeScreenRedraw || updateInventory) {
+ redrawV2Inventory();
+ }
+ checkV2MouseOver(_mouse);
+}
+
+void ScummEngine::checkExecVerbs() {
+ int i, over;
+ VerbSlot *vs;
+
+ if (VAR_MOUSE_STATE != 0xFF)
+ VAR(VAR_MOUSE_STATE) = 0;
+
+ if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
+ return;
+
+ if (VAR_MOUSE_STATE != 0xFF)
+ VAR(VAR_MOUSE_STATE) = _mouseAndKeyboardStat;
+
+ if (_mouseAndKeyboardStat < MBS_MAX_KEY) {
+ /* Check keypresses */
+ vs = &_verbs[1];
+ for (i = 1; i < _numVerbs; i++, vs++) {
+ if (vs->verbid && vs->saveid == 0 && vs->curmode == 1) {
+ if (_mouseAndKeyboardStat == vs->key) {
+ // Trigger verb as if the user clicked it
+ runInputScript(1, vs->verbid, 1);
+ return;
+ }
+ }
+ }
+
+ if ((_gameId == GID_INDY4 || _gameId == GID_PASS) && _mouseAndKeyboardStat >= '0' && _mouseAndKeyboardStat <= '9') {
+ // To support keyboard fighting in FOA, we need to remap the number keys.
+ // FOA apparently expects PC scancode values (see script 46 if you want
+ // to know where I got these numbers from). Oddly enough, the The Indy 3
+ // part of the "Passport to Adventure" demo expects the same keyboard
+ // mapping, even though the full game doesn't.
+ static const int numpad[10] = {
+ '0',
+ 335, 336, 337,
+ 331, 332, 333,
+ 327, 328, 329
+ };
+ _mouseAndKeyboardStat = numpad[_mouseAndKeyboardStat - '0'];
+ }
+
+ // Generic keyboard input
+ runInputScript(4, _mouseAndKeyboardStat, 1);
+ } else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
+ VirtScreen *zone = findVirtScreen(_mouse.y);
+ byte code = _mouseAndKeyboardStat & MBS_LEFT_CLICK ? 1 : 2;
+ int inventoryArea = (_platform == Common::kPlatformNES) ? 48: 32;
+
+ if (_version <= 2 && zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
+ // Click into V2 sentence line
+ runInputScript(5, 0, 0);
+ } else if (_version <= 2 && zone->number == kVerbVirtScreen && _mouse.y > zone->topline + inventoryArea) {
+ // Click into V2 inventory
+ ((ScummEngine_v2 *)this)->checkV2Inventory(_mouse.x, _mouse.y);
+ } else {
+ over = findVerbAtPos(_mouse.x, _mouse.y);
+ if (over != 0) {
+ // Verb was clicked
+ runInputScript(1, _verbs[over].verbid, code);
+ } else {
+ // Scene was clicked
+ runInputScript((zone->number == kMainVirtScreen) ? 2 : 1, 0, code);
+ }
+ }
+ }
+}
+
+void ScummEngine_c64::checkExecVerbs() {
+ Actor *a;
+ VirtScreen *zone = findVirtScreen(_mouse.y);
+
+ if (_userPut <= 0 || _mouseAndKeyboardStat == 0)
+ return;
+
+ if (zone->number == kVerbVirtScreen && _mouse.y <= zone->topline + 8) {
+ // TODO
+ } else if (_version <= 2 && zone->number == kVerbVirtScreen && _mouse.y > zone->topline + 32) {
+ // Click into V2 inventory
+ checkV2Inventory(_mouse.x, _mouse.y);
+ } else {
+ int over = findVerbAtPos(_mouse.x, _mouse.y);
+ if (over) {
+ _currentAction = _verbs[over].verbid;
+ return;
+ }
+
+ // HACK: Reset value
+ VAR(VAR_EGO) = 3;
+
+ int object = findObject(_mouse.x, _mouse.y);
+ if (object) {
+ _activeObject = object;
+ if (_currentMode == 3) {
+ int x, y, dir;
+ a = derefActor(VAR(VAR_EGO), "checkExecVerbs");
+ getObjectXYPos(object, x, y, dir);
+ a->startWalkActor(x, y, dir);
+ }
+
+ int tmp = (_currentMode == 3) ? _currentAction : 15;
+ runObjectScript(object, tmp, false, false, NULL);
+ } else {
+ _activeObject = 0;
+ if (zone->number == kMainVirtScreen) {
+ a = derefActor(VAR(VAR_EGO), "checkExecVerbs");
+ a->startWalkActor(_mouse.x, _mouse.y, -1);
+ }
+ }
+ }
+}
+
+void ScummEngine::verbMouseOver(int verb) {
+ // Don't do anything unless verbs are active
+ if (_version <= 2 && !(_userState & 128))
+ return;
+
+ if (_gameId == GID_FT)
+ return;
+
+ if (_verbMouseOver == verb)
+ return;
+
+ if (_verbs[_verbMouseOver].type != kImageVerbType) {
+ drawVerb(_verbMouseOver, 0);
+ _verbMouseOver = verb;
+ }
+
+ if (_verbs[verb].type != kImageVerbType && _verbs[verb].hicolor) {
+ drawVerb(verb, 1);
+ _verbMouseOver = verb;
+ }
+}
+
+int ScummEngine::findVerbAtPos(int x, int y) const {
+ if (!_numVerbs)
+ return 0;
+
+ VerbSlot *vs;
+ int i = _numVerbs - 1;
+
+ vs = &_verbs[i];
+ do {
+ if (vs->curmode != 1 || !vs->verbid || vs->saveid || y < vs->curRect.top || y >= vs->curRect.bottom)
+ continue;
+ if (vs->center) {
+ if (x < -(vs->curRect.right - 2 * vs->curRect.left) || x >= vs->curRect.right)
+ continue;
+ } else {
+ if (x < vs->curRect.left || x >= vs->curRect.right)
+ continue;
+ }
+
+ return i;
+ } while (--vs, --i);
+
+ return 0;
+}
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::drawVerb(int verb, int mode) {
+ VerbSlot *vs;
+
+ if (!verb)
+ return;
+
+ vs = &_verbs[verb];
+
+ if (!vs->saveid && vs->curmode && vs->verbid) {
+ if (vs->type == kImageVerbType) {
+ drawVerbBitmap(verb, vs->curRect.left, vs->curRect.top);
+ return;
+ }
+
+ uint8 color = vs->color;
+ if (vs->curmode == 2)
+ color = vs->dimcolor;
+ else if (mode && vs->hicolor)
+ color = vs->hicolor;
+
+ const byte *msg = getResourceAddress(rtVerb, verb);
+ if (!msg)
+ return;
+
+ // Convert the message, and skip a few remaining 0xFF codes (they
+ // occur in FT; subtype 10, which is used for the speech associated
+ // with the string).
+ byte buf[384];
+ convertMessageToString(msg, buf, sizeof(buf));
+ msg = buf;
+ while (*msg == 0xFF)
+ msg += 4;
+
+ enqueueText(msg, vs->curRect.left, vs->curRect.top, color, vs->charset_nr, vs->center);
+
+ // Set the specified charset id
+ _charset->setCurID(vs->charset_nr);
+
+ // Compute the text rect
+ vs->curRect.right = 0;
+ vs->curRect.bottom = 0;
+ while (*msg) {
+ const int charWidth = _charset->getCharWidth(*msg);
+ const int charHeight = _charset->getCharHeight(*msg);
+ vs->curRect.right += charWidth;
+ if (vs->curRect.bottom < charHeight)
+ vs->curRect.bottom = charHeight;
+ msg++;
+ }
+ vs->curRect.right += vs->curRect.left;
+ vs->curRect.bottom += vs->curRect.top;
+ vs->oldRect = vs->curRect;
+ }
+}
+#endif
+
+void ScummEngine::drawVerb(int verb, int mode) {
+ VerbSlot *vs;
+ bool tmp;
+
+ if (!verb)
+ return;
+
+ vs = &_verbs[verb];
+
+ if (!vs->saveid && vs->curmode && vs->verbid) {
+ if (vs->type == kImageVerbType) {
+ drawVerbBitmap(verb, vs->curRect.left, vs->curRect.top);
+ return;
+ }
+
+ restoreVerbBG(verb);
+
+ _string[4].charset = vs->charset_nr;
+ _string[4].xpos = vs->curRect.left;
+ _string[4].ypos = vs->curRect.top;
+ _string[4].right = _screenWidth - 1;
+ _string[4].center = vs->center;
+
+ if (vs->curmode == 2)
+ _string[4].color = vs->dimcolor;
+ else if (mode && vs->hicolor)
+ _string[4].color = vs->hicolor;
+ else
+ _string[4].color = vs->color;
+
+ // FIXME For the future: Indy3 and under inv scrolling
+ /*
+ if (verb >= 31 && verb <= 36)
+ verb += _inventoryOffset;
+ */
+
+ const byte *msg = getResourceAddress(rtVerb, verb);
+ if (!msg)
+ return;
+
+ tmp = _charset->_center;
+ drawString(4, msg);
+ _charset->_center = tmp;
+
+ vs->curRect.right = _charset->_str.right;
+ vs->curRect.bottom = _charset->_str.bottom;
+ vs->oldRect = _charset->_str;
+ _charset->_str.left = _charset->_str.right;
+ } else if (_gameId != GID_FT) {
+ restoreVerbBG(verb);
+ }
+}
+
+void ScummEngine::restoreVerbBG(int verb) {
+
+ VerbSlot *vs;
+
+ vs = &_verbs[verb];
+
+ if (vs->oldRect.left != -1) {
+ restoreBG(vs->oldRect, vs->bkcolor);
+ vs->oldRect.left = -1;
+ }
+}
+
+void ScummEngine::drawVerbBitmap(int verb, int x, int y) {
+ VirtScreen *vs;
+ VerbSlot *vst;
+ bool twobufs;
+ const byte *imptr = 0;
+ int ydiff, xstrip;
+ int imgw, imgh;
+ int i, tmp;
+ byte *obim;
+ const ImageHeader *imhd;
+ uint32 size;
+
+ if ((vs = findVirtScreen(y)) == NULL)
+ return;
+
+ gdi.disableZBuffer();
+
+ twobufs = vs->hasTwoBuffers;
+ vs->hasTwoBuffers = false;
+
+ xstrip = x / 8;
+ ydiff = y - vs->topline;
+
+ obim = getResourceAddress(rtVerb, verb);
+ assert(obim);
+ if (_features & GF_OLD_BUNDLE) {
+ imgw = obim[0];
+ imgh = obim[1] / 8;
+ imptr = obim + 2;
+ } else if (_features & GF_SMALL_HEADER) {
+ size = READ_LE_UINT32(obim);
+
+ imgw = (*(obim + size + 11));
+ imgh = (*(obim + size + 17)) / 8;
+ imptr = getObjectImage(obim, 1);
+ } else {
+ imhd = (const ImageHeader *)findResourceData(MKID('IMHD'), obim);
+ if (_version >= 7) {
+ imgw = READ_LE_UINT16(&imhd->v7.width) / 8;
+ imgh = READ_LE_UINT16(&imhd->v7.height) / 8;
+ } else {
+ imgw = READ_LE_UINT16(&imhd->old.width) / 8;
+ imgh = READ_LE_UINT16(&imhd->old.height) / 8;
+ }
+ imptr = getObjectImage(obim, 1);
+ }
+ assert(imptr);
+ for (i = 0; i < imgw; i++) {
+ tmp = xstrip + i;
+ gdi.drawBitmap(imptr, vs, tmp, ydiff, imgw * 8, imgh * 8, i, 1, Gdi::dbAllowMaskOr | Gdi::dbObjectMode);
+ }
+
+ vst = &_verbs[verb];
+ vst->curRect.right = vst->curRect.left + imgw * 8;
+ vst->curRect.bottom = vst->curRect.top + imgh * 8;
+ vst->oldRect = vst->curRect;
+
+ gdi.enableZBuffer();
+
+ vs->hasTwoBuffers = twobufs;
+}
+
+int ScummEngine::getVerbSlot(int id, int mode) const {
+ int i;
+ for (i = 1; i < _numVerbs; i++) {
+ if (_verbs[i].verbid == id && _verbs[i].saveid == mode) {
+ return i;
+ }
+ }
+ return 0;
+}
+
+void ScummEngine::killVerb(int slot) {
+ VerbSlot *vs;
+
+ if (slot == 0)
+ return;
+
+ vs = &_verbs[slot];
+ vs->verbid = 0;
+ vs->curmode = 0;
+
+ res.nukeResource(rtVerb, slot);
+
+ if (_version <= 6 && vs->saveid == 0) {
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+ }
+ vs->saveid = 0;
+}
+
+void ScummEngine::setVerbObject(uint room, uint object, uint verb) {
+ const byte *obimptr;
+ const byte *obcdptr;
+ uint32 size, size2;
+ FindObjectInRoom foir;
+ int i;
+
+ if (_heversion >= 70) { // Windows titles. Here we always ignore room
+ room = getObjectRoom(object);
+ }
+
+ if (whereIsObject(object) == WIO_FLOBJECT)
+ error("Can't grab verb image from flobject");
+
+ if (_features & GF_OLD_BUNDLE) {
+ for (i = (_numLocalObjects-1); i > 0; i--) {
+ if (_objs[i].obj_nr == object) {
+ findObjectInRoom(&foir, foImageHeader, object, room);
+ size = READ_LE_UINT16(foir.obim);
+ byte *ptr = res.createResource(rtVerb, verb, size + 2);
+ obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
+ ptr[0] = *(obcdptr + 9); // Width
+ ptr[1] = *(obcdptr + 15); // Height
+ memcpy(ptr + 2, foir.obim, size);
+ return;
+ }
+ }
+ } else if (_features & GF_SMALL_HEADER) {
+ for (i = (_numLocalObjects-1); i > 0; i--) {
+ if (_objs[i].obj_nr == object) {
+ // FIXME - the only thing we need from the OBCD is the image size!
+ // So we could use almost the same code (save for offsets)
+ // as in the GF_OLD_BUNDLE code. But of course that would break save games
+ // unless we insert special conversion code... <sigh>
+ findObjectInRoom(&foir, foImageHeader, object, room);
+ size = READ_LE_UINT32(foir.obim);
+ obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
+ size2 = READ_LE_UINT32(obcdptr);
+ res.createResource(rtVerb, verb, size + size2);
+ obimptr = getResourceAddress(rtRoom, room) - foir.roomptr + foir.obim;
+ obcdptr = getResourceAddress(rtRoom, room) + getOBCDOffs(object);
+ memcpy(getResourceAddress(rtVerb, verb), obimptr, size);
+ memcpy(getResourceAddress(rtVerb, verb) + size, obcdptr, size2);
+ return;
+ }
+ }
+ } else {
+ findObjectInRoom(&foir, foImageHeader, object, room);
+ size = READ_BE_UINT32(foir.obim + 4);
+ res.createResource(rtVerb, verb, size);
+ obimptr = getResourceAddress(rtRoom, room) - foir.roomptr + foir.obim;
+ memcpy(getResourceAddress(rtVerb, verb), obimptr, size);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/verbs.h b/engines/scumm/verbs.h
new file mode 100644
index 0000000000..87cf7f3955
--- /dev/null
+++ b/engines/scumm/verbs.h
@@ -0,0 +1,50 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef VERBS_H
+#define VERBS_H
+
+#include "common/scummsys.h"
+#include "common/rect.h"
+
+namespace Scumm {
+
+enum {
+ kTextVerbType = 0,
+ kImageVerbType = 1
+};
+
+struct VerbSlot {
+ Common::Rect curRect;
+ Common::Rect oldRect;
+ uint16 verbid;
+ uint8 color, hicolor, dimcolor, bkcolor, type;
+ uint8 charset_nr, curmode;
+ uint16 saveid;
+ uint8 key;
+ bool center;
+ uint8 prep;
+ uint16 imgindex;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/wiz_he.cpp b/engines/scumm/wiz_he.cpp
new file mode 100644
index 0000000000..9b0f9c3f15
--- /dev/null
+++ b/engines/scumm/wiz_he.cpp
@@ -0,0 +1,2088 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+#include "scumm/wiz_he.h"
+
+namespace Scumm {
+
+Wiz::Wiz(ScummEngine_v70he *vm) : _vm(vm) {
+ _imagesNum = 0;
+ memset(&_images, 0, sizeof(_images));
+ memset(&_polygons, 0, sizeof(_polygons));
+ _rectOverrideEnabled = false;
+}
+
+void Wiz::clearWizBuffer() {
+ _imagesNum = 0;
+}
+
+void Wiz::polygonClear() {
+ for (int i = 0; i < ARRAYSIZE(_polygons); i++) {
+ if (_polygons[i].flag == 1)
+ memset(&_polygons[i], 0, sizeof(WizPolygon));
+ }
+}
+
+void Wiz::polygonLoad(const uint8 *polData) {
+ int slots = READ_LE_UINT32(polData);
+ polData += 4;
+
+ bool flag = 1;
+ int id, points, vert1x, vert1y, vert2x, vert2y, vert3x, vert3y, vert4x, vert4y;
+ while (slots--) {
+ id = READ_LE_UINT32(polData);
+ points = READ_LE_UINT32(polData + 4);
+ if (points != 4)
+ error("Illegal polygon with %d points", points);
+ vert1x = READ_LE_UINT32(polData + 8);
+ vert1y = READ_LE_UINT32(polData + 12);
+ vert2x = READ_LE_UINT32(polData + 16);
+ vert2y = READ_LE_UINT32(polData + 20);
+ vert3x = READ_LE_UINT32(polData + 24);
+ vert3y = READ_LE_UINT32(polData + 28);
+ vert4x = READ_LE_UINT32(polData + 32);
+ vert4y = READ_LE_UINT32(polData + 36);
+
+ polData += 40;
+ polygonStore(id, flag, vert1x, vert1y, vert2x, vert2y, vert3x, vert3y, vert4x, vert4y);
+ }
+}
+
+void Wiz::polygonStore(int id, bool flag, int vert1x, int vert1y, int vert2x, int vert2y, int vert3x, int vert3y, int vert4x, int vert4y) {
+ WizPolygon *wp = NULL;
+ for (int i = 0; i < ARRAYSIZE(_polygons); ++i) {
+ if (_polygons[i].id == 0) {
+ wp = &_polygons[i];
+ break;
+ }
+ }
+ if (!wp) {
+ error("Wiz::polygonStore: out of polygon slot, max = %d", ARRAYSIZE(_polygons));
+ }
+
+ wp->vert[0].x = vert1x;
+ wp->vert[0].y = vert1y;
+ wp->vert[1].x = vert2x;
+ wp->vert[1].y = vert2y;
+ wp->vert[2].x = vert3x;
+ wp->vert[2].y = vert3y;
+ wp->vert[3].x = vert4x;
+ wp->vert[3].y = vert4y;
+ wp->vert[4].x = vert1x;
+ wp->vert[4].y = vert1y;
+ wp->id = id;
+ wp->numVerts = 5;
+ wp->flag = flag;
+
+ polygonCalcBoundBox(wp->vert, wp->numVerts, wp->bound);
+}
+
+void Wiz::polygonRotatePoints(Common::Point *pts, int num, int angle) {
+ double alpha = angle * PI / 180.;
+ double cos_alpha = cos(alpha);
+ double sin_alpha = sin(alpha);
+
+ for (int i = 0; i < num; ++i) {
+ int16 x = pts[i].x;
+ int16 y = pts[i].y;
+ pts[i].x = (int16)(x * cos_alpha - y * sin_alpha);
+ pts[i].y = (int16)(y * cos_alpha + x * sin_alpha);
+ }
+}
+
+void Wiz::polygonTransform(int resNum, int state, int po_x, int po_y, int angle, int scale, Common::Point *pts) {
+ int32 w, h;
+
+ getWizImageDim(resNum, state, w, h);
+
+ // set the transformation origin to the center of the image
+ if (_vm->_heversion >= 99) {
+ pts[0].x = pts[3].x = -(w / 2);
+ pts[1].x = pts[2].x = w / 2 - 1;
+ pts[0].y = pts[1].y = -(h / 2);
+ pts[2].y = pts[3].y = h / 2 - 1;
+ } else {
+ pts[1].x = pts[2].x = w / 2 - 1;
+ pts[0].x = pts[0].y = pts[1].y = pts[3].x = -(w / 2);
+ pts[2].y = pts[3].y = h / 2 - 1;
+ }
+
+ // scale
+ if (scale != 0 && scale != 256) {
+ for (int i = 0; i < 4; ++i) {
+ pts[i].x = pts[i].x * scale / 256;
+ pts[i].y = pts[i].y * scale / 256;
+ }
+ }
+
+ // rotate
+ if (angle != 0)
+ polygonRotatePoints(pts, 4, angle);
+
+ // translate
+ for (int i = 0; i < 4; ++i) {
+ pts[i].x += po_x;
+ pts[i].y += po_y;
+ }
+}
+
+void Wiz::polygonCalcBoundBox(Common::Point *vert, int numVerts, Common::Rect &bound) {
+ bound.left = 10000;
+ bound.top = 10000;
+ bound.right = -10000;
+ bound.bottom = -10000;
+
+ // compute bounding box
+ for (int j = 0; j < numVerts; j++) {
+ Common::Rect r(vert[j].x, vert[j].y, vert[j].x + 1, vert[j].y + 1);
+ bound.extend(r);
+ }
+}
+
+void Wiz::polygonErase(int fromId, int toId) {
+ for (int i = 0; i < ARRAYSIZE(_polygons); i++) {
+ if (_polygons[i].id >= fromId && _polygons[i].id <= toId)
+ memset(&_polygons[i], 0, sizeof(WizPolygon));
+ }
+}
+
+int Wiz::polygonHit(int id, int x, int y) {
+ for (int i = 0; i < ARRAYSIZE(_polygons); i++) {
+ if ((id == 0 || _polygons[i].id == id) && _polygons[i].bound.contains(x, y)) {
+ if (polygonContains(_polygons[i], x, y)) {
+ return _polygons[i].id;
+ }
+ }
+ }
+ return 0;
+}
+
+bool Wiz::polygonDefined(int id) {
+ for (int i = 0; i < ARRAYSIZE(_polygons); i++)
+ if (_polygons[i].id == id)
+ return true;
+ return false;
+}
+
+bool Wiz::polygonContains(const WizPolygon &pol, int x, int y) {
+ int pi = pol.numVerts - 1;
+ bool diry = (y < pol.vert[pi].y);
+ bool curdir;
+ bool r = false;
+
+ for (int i = 0; i < pol.numVerts; i++) {
+ curdir = (y < pol.vert[i].y);
+
+ if (curdir != diry) {
+ if (((pol.vert[pi].y - pol.vert[i].y) * (pol.vert[i].x - x) <
+ (pol.vert[pi].x - pol.vert[i].x) * (pol.vert[i].y - y)) == diry)
+ r = !r;
+ }
+
+ pi = i;
+ diry = curdir;
+ }
+
+ // HE80+
+ int a, b;
+ pi = pol.numVerts - 1;
+ if (r == 0) {
+ for (int i = 0; i < pol.numVerts; i++) {
+ if (pol.vert[i].y == y && pol.vert[i].y == pol.vert[pi].y) {
+
+ a = pol.vert[i].x;
+ b = pol.vert[pi].x;
+
+ if (pol.vert[i].x >= pol.vert[pi].x)
+ a = pol.vert[pi].x;
+
+ if (pol.vert[i].x > pol.vert[pi].x)
+ b = pol.vert[i].x;
+
+ if (x >= a && x <= b)
+ return 1;
+
+ } else if (pol.vert[i].x == x && pol.vert[i].x == pol.vert[pi].x) {
+
+ a = pol.vert[i].y;
+ b = pol.vert[i].y;
+
+ if (pol.vert[i].y >= pol.vert[pi].y)
+ a = pol.vert[pi].y;
+
+ if (pol.vert[i].y <= pol.vert[pi].y)
+ b = pol.vert[pi].y;
+
+ if (y >= a && y <= b)
+ return 1;
+ }
+ pi = i;
+ }
+ }
+
+ return r;
+}
+
+void Wiz::copyAuxImage(uint8 *dst1, uint8 *dst2, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch) {
+ Common::Rect dstRect(srcx, srcy, srcx + srcw, srcy + srch);
+ dstRect.clip(dstw, dsth);
+
+ int rw = dstRect.width();
+ int rh = dstRect.height();
+ if (rh <= 0 || rw <= 0)
+ return;
+
+ uint8 *dst1Ptr = dst1 + dstRect.left + dstRect.top * dstw;
+ uint8 *dst2Ptr = dst2 + dstRect.left + dstRect.top * dstw;
+ const uint8 *dataPtr = src;
+
+ while (rh--) {
+ uint16 off = READ_LE_UINT16(dataPtr); dataPtr += 2;
+ const uint8 *dataPtrNext = off + dataPtr;
+ uint8 *dst1PtrNext = dst1Ptr + dstw;
+ uint8 *dst2PtrNext = dst2Ptr + dstw;
+ if (off != 0) {
+ int w = rw;
+ while (w > 0) {
+ uint8 code = *dataPtr++;
+ if (code & 1) {
+ code >>= 1;
+ dst1Ptr += code;
+ dst2Ptr += code;
+ w -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+ w -= code;
+ if (w >= 0) {
+ memset(dst1Ptr, *dataPtr++, code);
+ dst1Ptr += code;
+ dst2Ptr += code;
+ } else {
+ code += w;
+ memset(dst1Ptr, *dataPtr, code);
+ }
+ } else {
+ code = (code >> 2) + 1;
+ w -= code;
+ if (w >= 0) {
+ memcpy(dst1Ptr, dst2Ptr, code);
+ dst1Ptr += code;
+ dst2Ptr += code;
+ } else {
+ code += w;
+ memcpy(dst1Ptr, dst2Ptr, code);
+ }
+ }
+ }
+ }
+ dataPtr = dataPtrNext;
+ dst1Ptr = dst1PtrNext;
+ dst2Ptr = dst2PtrNext;
+ }
+}
+
+static bool calcClipRects(int dst_w, int dst_h, int src_x, int src_y, int src_w, int src_h, const Common::Rect *rect, Common::Rect &srcRect, Common::Rect &dstRect) {
+ srcRect = Common::Rect(src_w, src_h);
+ dstRect = Common::Rect(src_x, src_y, src_x + src_w, src_y + src_h);
+ Common::Rect r3;
+ int diff;
+
+ if (rect) {
+ r3 = *rect;
+ Common::Rect r4(dst_w, dst_h);
+ if (r3.intersects(r4)) {
+ r3.clip(r4);
+ } else {
+ return false;
+ }
+ } else {
+ r3 = Common::Rect(dst_w, dst_h);
+ }
+ diff = dstRect.left - r3.left;
+ if (diff < 0) {
+ srcRect.left -= diff;
+ dstRect.left -= diff;
+ }
+ diff = dstRect.right - r3.right;
+ if (diff > 0) {
+ srcRect.right -= diff;
+ dstRect.right -= diff;
+ }
+ diff = dstRect.top - r3.top;
+ if (diff < 0) {
+ srcRect.top -= diff;
+ dstRect.top -= diff;
+ }
+ diff = dstRect.bottom - r3.bottom;
+ if (diff > 0) {
+ srcRect.bottom -= diff;
+ dstRect.bottom -= diff;
+ }
+
+ return srcRect.isValidRect() && dstRect.isValidRect();
+}
+
+void Wiz::copyWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, const uint8 *xmapPtr) {
+ Common::Rect r1, r2;
+ if (calcClipRects(dstw, dsth, srcx, srcy, srcw, srch, rect, r1, r2)) {
+ dst += r2.left + r2.top * dstw;
+ decompressWizImage(dst, dstw, r2, src, r1, flags, palPtr, xmapPtr);
+ }
+}
+
+void Wiz::copyRaw16BitWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor) {
+ // RAW 16 bits in 555 format
+
+ // HACK: Skip every second bit for now
+ Common::Rect r1, r2;
+ if (calcClipRects(dstw, dsth, srcx, srcy, srcw, srch, rect, r1, r2)) {
+ if (flags & kWIFFlipX) {
+ int l = r1.left;
+ int r = r1.right;
+ r1.left = srcw - r;
+ r1.right = srcw - l;
+ }
+ if (flags & kWIFFlipY) {
+ int t = r1.top;
+ int b = r1.bottom;
+ r1.top = srch - b;
+ r1.bottom = srch - t;
+ }
+ byte imagePal[256];
+ if (!palPtr) {
+ for (int i = 0; i < 256; i++) {
+ imagePal[i] = i;
+ }
+ palPtr = imagePal;
+ }
+
+ int h = r1.height();
+ int w = r1.width();
+ src += r1.left + r1.top * srcw * 2;
+ dst += r2.left + r2.top * dstw;
+
+ while (h--) {
+ const uint8 *p = src;
+ for (int i = 0; i < w; ++i) {
+ uint8 col = *p;
+ if (transColor == -1 || transColor != col) {
+ dst[i] = palPtr[col];
+ }
+ p += 2;
+ }
+ src += srcw * 2;
+ dst += dstw;
+ }
+
+ }
+}
+
+void Wiz::copyRawWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor) {
+ Common::Rect r1, r2;
+ if (calcClipRects(dstw, dsth, srcx, srcy, srcw, srch, rect, r1, r2)) {
+ if (flags & kWIFFlipX) {
+ int l = r1.left;
+ int r = r1.right;
+ r1.left = srcw - r;
+ r1.right = srcw - l;
+ }
+ if (flags & kWIFFlipY) {
+ int t = r1.top;
+ int b = r1.bottom;
+ r1.top = srch - b;
+ r1.bottom = srch - t;
+ }
+ byte imagePal[256];
+ if (!palPtr) {
+ for (int i = 0; i < 256; i++) {
+ imagePal[i] = i;
+ }
+ palPtr = imagePal;
+ }
+ int h = r1.height();
+ int w = r1.width();
+ src += r1.left + r1.top * srcw;
+ dst += r2.left + r2.top * dstw;
+ while (h--) {
+ const uint8 *p = src;
+ for (int i = 0; i < w; ++i) {
+ uint8 col = *p++;
+ if (transColor == -1 || transColor != col) {
+ dst[i] = palPtr[col];
+ }
+ }
+ src += srcw;
+ dst += dstw;
+ }
+ }
+}
+
+void Wiz::decompressWizImage(uint8 *dst, int dstPitch, const Common::Rect &dstRect, const uint8 *src, const Common::Rect &srcRect, int flags, const uint8 *palPtr, const uint8 *xmapPtr) {
+ if (flags & kWIFFlipX) {
+ debug(1, "decompressWizImage: Unhandled flag kWIFFlipX");
+ }
+ if (flags & kWIFFlipY) {
+ debug(1, "decompressWizImage: Unhandled flag kWIFFlipY");
+ }
+
+ const uint8 *dataPtr, *dataPtrNext;
+ uint8 *dstPtr, *dstPtrNext;
+ uint32 code;
+ uint8 databit;
+ int h, w, xoff;
+ uint16 off;
+
+ byte imagePal[256];
+ if (!palPtr) {
+ for (int i = 0; i < 256; i++) {
+ imagePal[i] = i;
+ }
+ palPtr = imagePal;
+ }
+
+ dstPtr = dst;
+ dataPtr = src;
+
+ // Skip over the first 'srcRect->top' lines in the data
+ h = srcRect.top;
+ while (h--) {
+ dataPtr += READ_LE_UINT16(dataPtr) + 2;
+ }
+ h = srcRect.height();
+ w = srcRect.width();
+ if (h <= 0 || w <= 0)
+ return;
+
+ while (h--) {
+ xoff = srcRect.left;
+ off = READ_LE_UINT16(dataPtr);
+ w = srcRect.right - srcRect.left;
+ dstPtrNext = dstPitch + dstPtr;
+ dataPtrNext = off + 2 + dataPtr;
+ dataPtr += 2;
+ if (off == 0)
+ goto dec_next;
+
+ // Skip over the leftmost 'srcRect->left' pixels.
+ // TODO: This code could be merged (at a loss of efficency) with the
+ // loop below which does the actual drawing.
+ while (xoff > 0) {
+ code = *dataPtr++;
+ databit = code & 1;
+ code >>= 1;
+ if (databit) {
+ xoff -= code;
+ if (xoff < 0) {
+ code = -xoff;
+ goto dec_sub1;
+ }
+ } else {
+ databit = code & 1;
+ code = (code >> 1) + 1;
+ if (databit) {
+ ++dataPtr;
+ xoff -= code;
+ if (xoff < 0) {
+ code = -xoff;
+ --dataPtr;
+ goto dec_sub2;
+ }
+ } else {
+ dataPtr += code;
+ xoff -= code;
+ if (xoff < 0) {
+ dataPtr += xoff;
+ code = -xoff;
+ goto dec_sub3;
+ }
+ }
+ }
+ }
+
+ while (w > 0) {
+ code = *dataPtr++;
+ databit = code & 1;
+ code >>= 1;
+ if (databit) {
+dec_sub1: dstPtr += code;
+ w -= code;
+ } else {
+ databit = code & 1;
+ code = (code >> 1) + 1;
+ if (databit) {
+dec_sub2: w -= code;
+ if (w < 0) {
+ code += w;
+ }
+ while (code--) {
+ if (xmapPtr) {
+ *dstPtr = xmapPtr[palPtr[*dataPtr] * 256 + *dstPtr];
+ dstPtr++;
+ } else {
+ *dstPtr++ = palPtr[*dataPtr];
+ }
+ }
+ dataPtr++;
+ } else {
+dec_sub3: w -= code;
+ if (w < 0) {
+ code += w;
+ }
+ while (code--) {
+ if (xmapPtr) {
+ *dstPtr = xmapPtr[palPtr[*dataPtr++] * 256 + *dstPtr];
+ dstPtr++;
+ } else {
+ *dstPtr++ = palPtr[*dataPtr++];
+ }
+ }
+ }
+ }
+ }
+dec_next:
+ dataPtr = dataPtrNext;
+ dstPtr = dstPtrNext;
+ }
+}
+
+int Wiz::isWizPixelNonTransparent(const uint8 *data, int x, int y, int w, int h) {
+ if (x < 0 || x >= w || y < 0 || y >= h) {
+ return 0;
+ }
+ while (y != 0) {
+ data += READ_LE_UINT16(data) + 2;
+ --y;
+ }
+ uint16 off = READ_LE_UINT16(data); data += 2;
+ if (off == 0) {
+ return 0;
+ }
+ while (x > 0) {
+ uint8 code = *data++;
+ if (code & 1) {
+ code >>= 1;
+ if (code > x) {
+ return 0;
+ }
+ x -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+ if (code > x) {
+ return 1;
+ }
+ x -= code;
+ ++data;
+ } else {
+ code = (code >> 2) + 1;
+ if (code > x) {
+ return 1;
+ }
+ x -= code;
+ data += code;
+ }
+ }
+ return (~data[0]) & 1;
+}
+
+uint8 Wiz::getWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color) {
+ if (x < 0 || x >= w || y < 0 || y >= h) {
+ return color;
+ }
+ while (y != 0) {
+ data += READ_LE_UINT16(data) + 2;
+ --y;
+ }
+ uint16 off = READ_LE_UINT16(data); data += 2;
+ if (off == 0) {
+ return color;
+ }
+ while (x > 0) {
+ uint8 code = *data++;
+ if (code & 1) {
+ code >>= 1;
+ if (code > x) {
+ return color;
+ }
+ x -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+ if (code > x) {
+ return data[0];
+ }
+ x -= code;
+ ++data;
+ } else {
+ code = (code >> 2) + 1;
+ if (code > x) {
+ return data[x];
+ }
+ x -= code;
+ data += code;
+ }
+ }
+ return (data[0] & 1) ? color : data[1];
+}
+
+uint8 Wiz::getRawWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color) {
+ if (x < 0 || x >= w || y < 0 || y >= h) {
+ return color;
+ }
+ return data[y * w + x];
+}
+
+void Wiz::computeWizHistogram(uint32 *histogram, const uint8 *data, const Common::Rect &rCapt) {
+ int y = rCapt.top;
+ while (y != 0) {
+ data += READ_LE_UINT16(data) + 2;
+ --y;
+ }
+ int ih = rCapt.height();
+ while (ih--) {
+ uint16 off = READ_LE_UINT16(data); data += 2;
+ if (off != 0) {
+ const uint8 *p = data;
+ int x1 = rCapt.left;
+ int x2 = rCapt.right;
+ uint8 code;
+ while (x1 > 0) {
+ code = *p++;
+ if (code & 1) {
+ code >>= 1;
+ if (code > x1) {
+ code -= x1;
+ x2 -= code;
+ break;
+ }
+ x1 -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+ if (code > x1) {
+ code -= x1;
+ goto dec_sub2;
+ }
+ x1 -= code;
+ ++p;
+ } else {
+ code = (code >> 2) + 1;
+ if (code > x1) {
+ code -= x1;
+ p += x1;
+ goto dec_sub3;
+ }
+ x1 -= code;
+ p += code;
+ }
+ }
+ while (x2 > 0) {
+ code = *p++;
+ if (code & 1) {
+ code >>= 1;
+ x2 -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+dec_sub2: x2 -= code;
+ if (x2 < 0) {
+ code += x2;
+ }
+ histogram[*p++] += code;
+ } else {
+ code = (code >> 2) + 1;
+dec_sub3: x2 -= code;
+ if (x2 < 0) {
+ code += x2;
+ }
+ int n = code;
+ while (n--) {
+ ++histogram[*p++];
+ }
+ }
+ }
+ data += off;
+ }
+ }
+}
+
+void Wiz::computeRawWizHistogram(uint32 *histogram, const uint8 *data, int srcPitch, const Common::Rect &rCapt) {
+ data += rCapt.top * srcPitch + rCapt.left;
+ int iw = rCapt.width();
+ int ih = rCapt.height();
+ while (ih--) {
+ for (int i = 0; i < iw; ++i) {
+ ++histogram[data[i]];
+ }
+ data += srcPitch;
+ }
+}
+
+static int wizPackType1(uint8 *dst, const uint8 *src, int srcPitch, const Common::Rect& rCapt, uint8 transColor) {
+ debug(9, "wizPackType1(%d, [%d,%d,%d,%d])", transColor, rCapt.left, rCapt.top, rCapt.right, rCapt.bottom);
+ src += rCapt.top * srcPitch + rCapt.left;
+ int w = rCapt.width();
+ int h = rCapt.height();
+ int dataSize = 0;
+ while (h--) {
+ uint8 *dstLine = dst;
+ if (dst) {
+ dst += 2;
+ }
+ uint8 diffBuffer[0x40];
+ int runCountSame = 0;
+ int runCountDiff = 0;
+ uint8 prevColor = src[0];
+ for (int i = 1; i < w; ) {
+ uint8 color = src[i++];
+ if (i == 2) {
+ if (prevColor == color) {
+ runCountSame = 1;
+ } else {
+ diffBuffer[0] = prevColor;
+ runCountDiff = 1;
+ }
+ }
+ if (prevColor == color) {
+ if (runCountDiff != 0) {
+ runCountSame = 1;
+ if (runCountDiff > 1) {
+ --runCountDiff;
+ if (dst) {
+ *dst++ = ((runCountDiff - 1) << 2) | 0;
+ memcpy(dst, diffBuffer, runCountDiff);
+ dst += runCountDiff;
+ }
+ dataSize += runCountDiff + 1;
+ }
+ runCountDiff = 0;
+ }
+ ++runCountSame;
+ if (prevColor == transColor) {
+ if (runCountSame == 0x7F) {
+ if (dst) {
+ *dst++ = (runCountSame << 1) | 1;
+ }
+ ++dataSize;
+ runCountSame = 0;
+ }
+ } else {
+ if (runCountSame == 0x40) {
+ if (dst) {
+ *dst++ = ((runCountSame - 1) << 2) | 2;
+ *dst++ = prevColor;
+ }
+ dataSize += 2;
+ runCountSame = 0;
+ }
+ }
+ } else {
+ if (runCountSame != 0) {
+ if (prevColor == transColor) {
+ if (dst) {
+ *dst++ = (runCountSame << 1) | 1;
+ }
+ ++dataSize;
+ } else {
+ if (dst) {
+ *dst++ = ((runCountSame - 1) << 2) | 2;
+ *dst++ = prevColor;
+ }
+ dataSize += 2;
+ }
+ runCountSame = 0;
+ }
+ assert(runCountDiff < ARRAYSIZE(diffBuffer));
+ diffBuffer[runCountDiff++] = color;
+ if (runCountDiff == 0x40) {
+ if (dst) {
+ *dst++ = ((runCountDiff - 1) << 2) | 0;
+ memcpy(dst, diffBuffer, runCountDiff);
+ dst += runCountDiff + 1;
+ }
+ dataSize += runCountDiff + 1;
+ runCountDiff = 0;
+ }
+ }
+ prevColor = color;
+ }
+ if (runCountSame != 0) {
+ if (prevColor == transColor) {
+ if (dst) {
+ *dst++ = (runCountSame << 1) | 1;
+ }
+ ++dataSize;
+ } else {
+ if (dst) {
+ *dst++ = ((runCountSame - 1) << 2) | 2;
+ *dst++ = prevColor;
+ }
+ dataSize += 2;
+ }
+ }
+ if (runCountDiff != 0) {
+ if (dst) {
+ *dst++ = ((runCountDiff - 1) << 2) | 0;
+ memcpy(dst, diffBuffer, runCountDiff);
+ dst += runCountDiff;
+ }
+ dataSize += runCountDiff + 1;
+ }
+ if (dst) {
+ WRITE_LE_UINT16(dstLine, dst - dstLine - 2);
+ }
+ dataSize += 2;
+ src += srcPitch;
+ }
+ return dataSize;
+}
+
+static int wizPackType0(uint8 *dst, const uint8 *src, int srcPitch, const Common::Rect& rCapt) {
+ debug(9, "wizPackType0([%d,%d,%d,%d])", rCapt.left, rCapt.top, rCapt.right, rCapt.bottom);
+ int w = rCapt.width();
+ int h = rCapt.height();
+ int size = w * h;
+ if (dst) {
+ src += rCapt.top * srcPitch + rCapt.left;
+ while (h--) {
+ memcpy(dst, src, w);
+ dst += w;
+ src += srcPitch;
+ }
+ }
+ return size;
+}
+
+void Wiz::captureWizImage(int resNum, const Common::Rect& r, bool backBuffer, int compType) {
+ debug(5, "ScummEngine_v72he::captureWizImage(%d, %d, [%d,%d,%d,%d])", resNum, compType, r.left, r.top, r.right, r.bottom);
+ uint8 *src = NULL;
+ VirtScreen *pvs = &_vm->virtscr[kMainVirtScreen];
+ if (backBuffer) {
+ src = pvs->getBackPixels(0, 0);
+ } else {
+ src = pvs->getPixels(0, 0);
+ }
+ Common::Rect rCapt(pvs->w, pvs->h);
+ if (rCapt.intersects(r)) {
+ rCapt.clip(r);
+ const uint8 *palPtr;
+ if (_vm->_heversion >= 99) {
+ palPtr = _vm->_hePalettes + 1024;
+ } else {
+ palPtr = _vm->_currentPalette;
+ }
+
+ int w = rCapt.width();
+ int h = rCapt.height();
+ int transColor = (_vm->VAR_WIZ_TCOLOR != 0xFF) ? _vm->VAR(_vm->VAR_WIZ_TCOLOR) : 5;
+
+ // compute compressed size
+ int dataSize = 0;
+ int headerSize = palPtr ? 1080 : 36;
+ switch (compType) {
+ case 0:
+ dataSize = wizPackType0(0, src, pvs->pitch, rCapt);
+ break;
+ case 1:
+ dataSize = wizPackType1(0, src, pvs->pitch, rCapt, transColor);
+ break;
+ default:
+ error("unhandled compression type %d", compType);
+ break;
+ }
+
+ // alignment
+ dataSize = (dataSize + 1) & ~1;
+ int wizSize = headerSize + dataSize;
+ // write header
+ uint8 *wizImg = _vm->res.createResource(rtImage, resNum, dataSize + headerSize);
+ WRITE_BE_UINT32(wizImg + 0x00, 'AWIZ');
+ WRITE_BE_UINT32(wizImg + 0x04, wizSize);
+ WRITE_BE_UINT32(wizImg + 0x08, 'WIZH');
+ WRITE_BE_UINT32(wizImg + 0x0C, 0x14);
+ WRITE_LE_UINT32(wizImg + 0x10, compType);
+ WRITE_LE_UINT32(wizImg + 0x14, w);
+ WRITE_LE_UINT32(wizImg + 0x18, h);
+ int curSize = 0x1C;
+ if (palPtr) {
+ WRITE_BE_UINT32(wizImg + 0x1C, 'RGBS');
+ WRITE_BE_UINT32(wizImg + 0x20, 0x308);
+ memcpy(wizImg + 0x24, palPtr, 0x300);
+ WRITE_BE_UINT32(wizImg + 0x324, 'RMAP');
+ WRITE_BE_UINT32(wizImg + 0x328, 0x10C);
+ WRITE_BE_UINT32(wizImg + 0x32C, 0);
+ curSize = 0x330;
+ for (int i = 0; i < 256; ++i) {
+ wizImg[curSize] = i;
+ ++curSize;
+ }
+ }
+ WRITE_BE_UINT32(wizImg + curSize + 0x0, 'WIZD');
+ WRITE_BE_UINT32(wizImg + curSize + 0x4, dataSize + 8);
+ curSize += 8;
+
+ // write compressed data
+ switch (compType) {
+ case 0:
+ wizPackType0(wizImg + headerSize, src, pvs->pitch, rCapt);
+ break;
+ case 1:
+ wizPackType1(wizImg + headerSize, src, pvs->pitch, rCapt, transColor);
+ break;
+ default:
+ break;
+ }
+ }
+ _vm->res.setModified(rtImage, resNum);
+}
+
+void Wiz::displayWizImage(WizImage *pwi) {
+ if (_vm->_fullRedraw) {
+ assert(_imagesNum < ARRAYSIZE(_images));
+ WizImage *wi = &_images[_imagesNum];
+ wi->resNum = pwi->resNum;
+ wi->x1 = pwi->x1;
+ wi->y1 = pwi->y1;
+ wi->zorder = 0;
+ wi->state = pwi->state;
+ wi->flags = pwi->flags;
+ wi->shadow = 0;
+ wi->field_390 = 0;
+ wi->palette = 0;
+ ++_imagesNum;
+ } else if (pwi->flags & kWIFIsPolygon) {
+ drawWizPolygon(pwi->resNum, pwi->state, pwi->x1, pwi->flags, 0, 0, 0);
+ } else {
+ const Common::Rect *r = NULL;
+ drawWizImage(pwi->resNum, pwi->state, pwi->x1, pwi->y1, 0, 0, 0, r, pwi->flags, 0, 0);
+ }
+}
+
+uint8 *Wiz::drawWizImage(int resNum, int state, int x1, int y1, int zorder, int shadow, int field_390, const Common::Rect *clipBox, int flags, int dstResNum, int palette) {
+ debug(2, "drawWizImage(resNum %d, x1 %d y1 %d flags 0x%X zorder %d shadow %d field_390 %d dstResNum %d palette %d)", resNum, x1, y1, flags, zorder, shadow, field_390, dstResNum, palette);
+ uint8 *dataPtr;
+ uint8 *dst = NULL;
+
+ const uint8 *palPtr = NULL;
+ if (_vm->_heversion >= 99) {
+ if (palette) {
+ palPtr = _vm->_hePalettes + palette * 1024 + 768;
+ } else {
+ palPtr = _vm->_hePalettes + 1792;
+ }
+ }
+
+ const uint8 *xmapPtr = NULL;
+ if (shadow) {
+ dataPtr = _vm->getResourceAddress(rtImage, shadow);
+ assert(dataPtr);
+ xmapPtr = _vm->findResourceData(MKID('XMAP'), dataPtr);
+ assert(xmapPtr);
+ }
+
+ dataPtr = _vm->getResourceAddress(rtImage, resNum);
+ assert(dataPtr);
+
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ uint32 comp = READ_LE_UINT32(wizh + 0x0);
+ uint32 width = READ_LE_UINT32(wizh + 0x4);
+ uint32 height = READ_LE_UINT32(wizh + 0x8);
+ debug(2, "wiz_header.comp = %d wiz_header.w = %d wiz_header.h = %d", comp, width, height);
+
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+
+ if (flags & kWIFHasPalette) {
+ uint8 *pal = _vm->findWrappedBlock(MKID('RGBS'), dataPtr, state, 0);
+ assert(pal);
+ _vm->setPaletteFromPtr(pal, 256);
+ }
+
+ uint8 *rmap = NULL;
+ if (flags & kWIFRemapPalette) {
+ rmap = _vm->findWrappedBlock(MKID('RMAP'), dataPtr, state, 0);
+ assert(rmap);
+ if (_vm->_heversion <= 80 || READ_BE_UINT32(rmap) != 0x01234567) {
+ uint8 *rgbs = _vm->findWrappedBlock(MKID('RGBS'), dataPtr, state, 0);
+ assert(rgbs);
+ _vm->remapHEPalette(rgbs, rmap + 4);
+ }
+ }
+
+ if (flags & kWIFPrint) {
+ error("WizImage printing is unimplemented");
+ }
+
+ int32 cw, ch;
+ if (flags & kWIFBlitToMemBuffer) {
+ dst = (uint8 *)malloc(width * height);
+ int transColor = (_vm->VAR_WIZ_TCOLOR != 0xFF) ? (_vm->VAR(_vm->VAR_WIZ_TCOLOR)) : 5;
+ memset(dst, transColor, width * height);
+ cw = width;
+ ch = height;
+ } else {
+ if (dstResNum) {
+ uint8 *dstPtr = _vm->getResourceAddress(rtImage, dstResNum);
+ assert(dstPtr);
+ dst = _vm->findWrappedBlock(MKID('WIZD'), dstPtr, 0, 0);
+ assert(dst);
+ getWizImageDim(dstResNum, 0, cw, ch);
+ } else {
+ VirtScreen *pvs = &_vm->virtscr[kMainVirtScreen];
+ if (flags & kWIFMarkBufferDirty) {
+ dst = pvs->getPixels(0, pvs->topline);
+ } else {
+ dst = pvs->getBackPixels(0, pvs->topline);
+ }
+ cw = pvs->w;
+ ch = pvs->h;
+ }
+ }
+
+ Common::Rect rScreen(cw, ch);
+ if (clipBox) {
+ Common::Rect clip(clipBox->left, clipBox->top, clipBox->right, clipBox->bottom);
+ if (rScreen.intersects(clip)) {
+ rScreen.clip(clip);
+ } else {
+ return 0;
+ }
+ } else if (_rectOverrideEnabled) {
+ if (rScreen.intersects(_rectOverride)) {
+ rScreen.clip(_rectOverride);
+ } else {
+ return 0;
+ }
+ }
+
+ if (flags & kWIFRemapPalette) {
+ palPtr = rmap + 4;
+ }
+
+ int transColor = -1;
+ if (_vm->VAR_WIZ_TCOLOR != 0xFF) {
+ uint8 *trns = _vm->findWrappedBlock(MKID('TRNS'), dataPtr, state, 0);
+ transColor = (trns == NULL) ? _vm->VAR(_vm->VAR_WIZ_TCOLOR) : -1;
+ }
+
+ switch (comp) {
+ case 0:
+ copyRawWizImage(dst, wizd, cw, ch, x1, y1, width, height, &rScreen, flags, palPtr, transColor);
+ break;
+ case 1:
+ // TODO Adding masking for flags 0x80 and 0x100
+ if (flags & 0x80)
+ // Used in maze
+ debug(0, "drawWizImage: Unhandled flag 0x80");
+ if (flags & 0x100) {
+ // Used in readdemo
+ debug(0, "drawWizImage: Unhandled flag 0x100");
+ }
+ copyWizImage(dst, wizd, cw, ch, x1, y1, width, height, &rScreen, flags, palPtr, xmapPtr);
+ break;
+ case 2:
+ copyRaw16BitWizImage(dst, wizd, cw, ch, x1, y1, width, height, &rScreen, flags, palPtr, transColor);
+ break;
+ default:
+ error("drawWizImage: Unhandled wiz compression type %d", comp);
+ }
+
+ if (!(flags & kWIFBlitToMemBuffer) && dstResNum == 0) {
+ Common::Rect rImage(x1, y1, x1 + width, y1 + height);
+ if (rImage.intersects(rScreen)) {
+ rImage.clip(rScreen);
+ if (!(flags & kWIFBlitToFrontVideoBuffer) && (flags & (kWIFBlitToFrontVideoBuffer | kWIFMarkBufferDirty))) {
+ ++rImage.bottom;
+ _vm->markRectAsDirty(kMainVirtScreen, rImage);
+ } else {
+ _vm->gdi.copyVirtScreenBuffers(rImage);
+ }
+ }
+ }
+
+ return dst;
+}
+
+struct PolygonDrawData {
+ struct PolygonArea {
+ int32 xmin;
+ int32 xmax;
+ int32 x1;
+ int32 y1;
+ int32 x2;
+ int32 y2;
+ };
+ struct ResultArea {
+ int32 dst_offs;
+ int32 x_step;
+ int32 y_step;
+ int32 x_s;
+ int32 y_s;
+ int32 w;
+ };
+ Common::Point mat[4];
+ PolygonArea *pa;
+ ResultArea *ra;
+ int rAreasNum;
+ int pAreasNum;
+
+ PolygonDrawData(int n) {
+ memset(mat, 0, sizeof(mat));
+ pa = new PolygonArea[n];
+ for (int i = 0; i < n; ++i) {
+ pa[i].xmin = 0x7FFFFFFF;
+ pa[i].xmax = 0x80000000;
+ }
+ ra = new ResultArea[n];
+ rAreasNum = 0;
+ pAreasNum = n;
+ }
+
+ ~PolygonDrawData() {
+ delete[] pa;
+ delete[] ra;
+ }
+
+ void transform(const Common::Point *tp1, const Common::Point *tp2, const Common::Point *sp1, const Common::Point *sp2) {
+ int32 tx_acc = tp1->x << 16;
+ int32 sx_acc = sp1->x << 16;
+ int32 sy_acc = sp1->y << 16;
+ uint16 dy = ABS(tp2->y - tp1->y) + 1;
+ int32 tx_step = ((tp2->x - tp1->x) << 16) / dy;
+ int32 sx_step = ((sp2->x - sp1->x) << 16) / dy;
+ int32 sy_step = ((sp2->y - sp1->y) << 16) / dy;
+
+ int y = tp1->y - mat[0].y;
+ while (dy--) {
+ assert(y >= 0 && y < pAreasNum);
+ PolygonArea *ppa = &pa[y];
+ int32 ttx = tx_acc >> 16;
+ int32 tsx = sx_acc >> 16;
+ int32 tsy = sy_acc >> 16;
+
+ if (ppa->xmin > ttx) {
+ ppa->xmin = ttx;
+ ppa->x1 = tsx;
+ ppa->y1 = tsy;
+ }
+ if (ppa->xmax < ttx) {
+ ppa->xmax = ttx;
+ ppa->x2 = tsx;
+ ppa->y2 = tsy;
+ }
+
+ tx_acc += tx_step;
+ sx_acc += sx_step;
+ sy_acc += sy_step;
+
+ if (tp2->y <= tp1->y) {
+ --y;
+ } else {
+ ++y;
+ }
+ }
+ }
+};
+
+void Wiz::drawWizComplexPolygon(int resNum, int state, int po_x, int po_y, int shadow, int angle, int scale, const Common::Rect *r, int flags, int dstResNum, int palette) {
+ Common::Point pts[4];
+
+ polygonTransform(resNum, state, po_x, po_y, angle, scale, pts);
+ drawWizPolygonTransform(resNum, state, pts, flags, shadow, dstResNum, palette);
+}
+
+void Wiz::drawWizPolygon(int resNum, int state, int id, int flags, int shadow, int dstResNum, int palette) {
+ int i;
+ WizPolygon *wp = NULL;
+ for (i = 0; i < ARRAYSIZE(_polygons); ++i) {
+ if (_polygons[i].id == id) {
+ wp = &_polygons[i];
+ break;
+ }
+ }
+ if (!wp) {
+ error("Polygon %d is not defined", id);
+ }
+ if (wp->numVerts != 5) {
+ error("Invalid point count %d for Polygon %d", wp->numVerts, id);
+ }
+
+ drawWizPolygonTransform(resNum, state, wp->vert, flags, shadow, dstResNum, palette);
+}
+
+void Wiz::drawWizPolygonTransform(int resNum, int state, Common::Point *wp, int flags, int shadow, int dstResNum, int palette) {
+ debug(2, "drawWizPolygonTransform(resNum %d, flags 0x%X, shadow %d dstResNum %d palette %d)", resNum, flags, shadow, dstResNum, palette);
+ int i;
+
+ if (flags & 0x800000) {
+ warning("0x800000 flags not supported");
+ return;
+ }
+
+ const Common::Rect *r = NULL;
+ uint8 *srcWizBuf = drawWizImage(resNum, state, 0, 0, 0, shadow, 0, r, kWIFBlitToMemBuffer, 0, palette);
+ if (srcWizBuf) {
+ uint8 *dst;
+ int32 dstw, dsth, dstpitch, wizW, wizH;
+ VirtScreen *pvs = &_vm->virtscr[kMainVirtScreen];
+ int transColor = (_vm->VAR_WIZ_TCOLOR != 0xFF) ? _vm->VAR(_vm->VAR_WIZ_TCOLOR) : 5;
+
+ if (dstResNum) {
+ uint8 *dstPtr = _vm->getResourceAddress(rtImage, dstResNum);
+ assert(dstPtr);
+ dst = _vm->findWrappedBlock(MKID('WIZD'), dstPtr, 0, 0);
+ assert(dst);
+ getWizImageDim(dstResNum, 0, dstw, dsth);
+ dstpitch = dstw;
+ } else {
+ if (flags & kWIFMarkBufferDirty) {
+ dst = pvs->getPixels(0, 0);
+ } else {
+ dst = pvs->getBackPixels(0, 0);
+ }
+ dstw = pvs->w;
+ dsth = pvs->h;
+ dstpitch = pvs->pitch;
+ }
+
+ getWizImageDim(resNum, state, wizW, wizH);
+
+ Common::Point bbox[4];
+ bbox[0].x = 0;
+ bbox[0].y = 0;
+ bbox[1].x = wizW - 1;
+ bbox[1].y = 0;
+ bbox[2].x = wizW - 1;
+ bbox[2].y = wizH - 1;
+ bbox[3].x = 0;
+ bbox[3].y = wizH - 1;
+
+ int16 xmin_p, xmax_p, ymin_p, ymax_p;
+ xmin_p = ymin_p = (int16)0x7FFF;
+ xmax_p = ymax_p = (int16)0x8000;
+
+ for (i = 0; i < 4; ++i) {
+ xmin_p = MIN(wp[i].x, xmin_p);
+ xmax_p = MAX(wp[i].x, xmax_p);
+ ymin_p = MIN(wp[i].y, ymin_p);
+ ymax_p = MAX(wp[i].y, ymax_p);
+ }
+
+ int16 xmin_b, xmax_b, ymin_b, ymax_b;
+ xmin_b = ymin_b = (int16)0x7FFF;
+ xmax_b = ymax_b = (int16)0x8000;
+
+ for (i = 0; i < 4; ++i) {
+ xmin_b = MIN(bbox[i].x, xmin_b);
+ xmax_b = MAX(bbox[i].x, xmax_b);
+ ymin_b = MIN(bbox[i].y, ymin_b);
+ ymax_b = MAX(bbox[i].y, ymax_b);
+ }
+
+ PolygonDrawData pdd(ymax_p - ymin_p + 1);
+ pdd.mat[0].x = xmin_p;
+ pdd.mat[0].y = ymin_p;
+ pdd.mat[1].x = xmax_p;
+ pdd.mat[1].y = ymax_p;
+ pdd.mat[2].x = xmin_b;
+ pdd.mat[2].y = ymin_b;
+ pdd.mat[3].x = xmax_b;
+ pdd.mat[3].y = ymax_b;
+
+ // precompute the transformation which remaps 'bbox' pixels to 'wp'
+ for (i = 0; i < 3; ++i) {
+ pdd.transform(&wp[i], &wp[i + 1], &bbox[i], &bbox[i + 1]);
+ }
+ pdd.transform(&wp[3], &wp[0], &bbox[3], &bbox[0]);
+
+ pdd.rAreasNum = 0;
+ PolygonDrawData::ResultArea *pra = &pdd.ra[0];
+ int32 yoff = pdd.mat[0].y * dstpitch;
+ int16 y_start = pdd.mat[0].y;
+ for (i = 0; i < pdd.pAreasNum; ++i) {
+ PolygonDrawData::PolygonArea *ppa = &pdd.pa[i];
+ if (y_start >= 0 && y_start < dsth) {
+ int16 x1 = ppa->xmin;
+ if (x1 < 0) {
+ x1 = 0;
+ }
+ int16 x2 = ppa->xmax;
+ if (x2 >= dstw) {
+ x2 = dstw - 1;
+ }
+ int16 w = x2 - x1 + 1;
+ if (w > 0) {
+ int16 width = ppa->xmax - ppa->xmin + 1;
+ pra->x_step = ((ppa->x2 - ppa->x1) << 16) / width;
+ pra->y_step = ((ppa->y2 - ppa->y1) << 16) / width;
+ pra->dst_offs = yoff + x1;
+ pra->w = w;
+ pra->x_s = ppa->x1 << 16;
+ pra->y_s = ppa->y1 << 16;
+ int16 tmp = x1 - ppa->xmin;
+ if (tmp != 0) {
+ pra->x_s += pra->x_step * tmp;
+ pra->y_s += pra->y_step * tmp;
+ }
+ ++pra;
+ ++pdd.rAreasNum;
+ }
+ }
+ ++ppa;
+ yoff += dstpitch;
+ ++y_start;
+ }
+
+ pra = &pdd.ra[0];
+ for (i = 0; i < pdd.rAreasNum; ++i, ++pra) {
+ uint8 *dstPtr = dst + pra->dst_offs;
+ int32 w = pra->w;
+ int32 x_acc = pra->x_s;
+ int32 y_acc = pra->y_s;
+ while (--w) {
+ int32 src_offs = (y_acc >> 16) * wizW + (x_acc >> 16);
+ assert(src_offs < wizW * wizH);
+ x_acc += pra->x_step;
+ y_acc += pra->y_step;
+ if (transColor == -1 || transColor != srcWizBuf[src_offs]) {
+ *dstPtr = srcWizBuf[src_offs];
+ }
+ dstPtr++;
+ }
+ }
+
+ Common::Rect bound(xmin_p, ymin_p, xmax_p + 1, ymax_p + 1);
+ if (flags & kWIFMarkBufferDirty) {
+ _vm->markRectAsDirty(kMainVirtScreen, bound);
+ } else {
+ _vm->gdi.copyVirtScreenBuffers(bound);
+ }
+
+ free(srcWizBuf);
+ }
+}
+
+void Wiz::flushWizBuffer() {
+ for (int i = 0; i < _imagesNum; ++i) {
+ WizImage *pwi = &_images[i];
+ if (pwi->flags & kWIFIsPolygon) {
+ drawWizPolygon(pwi->resNum, pwi->state, pwi->x1, pwi->flags, pwi->shadow, 0, pwi->palette);
+ } else {
+ const Common::Rect *r = NULL;
+ drawWizImage(pwi->resNum, pwi->state, pwi->x1, pwi->y1, pwi->zorder, pwi->shadow, pwi->field_390, r, pwi->flags, 0, pwi->palette);
+ }
+ }
+ _imagesNum = 0;
+}
+
+void Wiz::loadWizCursor(int resId) {
+ int32 x, y;
+ getWizImageSpot(resId, 0, x, y);
+ if (x < 0) {
+ x = 0;
+ } else if (x > 32) {
+ x = 32;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y > 32) {
+ y = 32;
+ }
+
+ const Common::Rect *r = NULL;
+ uint8 *cursor = drawWizImage(resId, 0, 0, 0, 0, 0, 0, r, kWIFBlitToMemBuffer, 0, 0);
+ int32 cw, ch;
+ getWizImageDim(resId, 0, cw, ch);
+ _vm->setCursorFromBuffer(cursor, cw, ch, cw);
+ _vm->setCursorHotspot(x, y);
+ free(cursor);
+}
+
+void Wiz::displayWizComplexImage(const WizParameters *params) {
+ int sourceImage = 0;
+ if (params->processFlags & kWPFMaskImg) {
+ sourceImage = params->sourceImage;
+ debug(0, "displayWizComplexImage() unhandled flag 0x80000");
+ }
+ int palette = 0;
+ if (params->processFlags & kWPFPaletteNum) {
+ palette = params->img.palette;
+ }
+ int scale = 256;
+ if (params->processFlags & kWPFScaled) {
+ scale = params->scale;
+ }
+ int rotationAngle = 0;
+ if (params->processFlags & kWPFRotate) {
+ rotationAngle = params->angle;
+ }
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ int flags = 0;
+ if (params->processFlags & kWPFNewFlags) {
+ flags = params->img.flags;
+ }
+ int po_x = 0;
+ int po_y = 0;
+ if (params->processFlags & kWPFSetPos) {
+ po_x = params->img.x1;
+ po_y = params->img.y1;
+ }
+ int shadow = 0;
+ if (params->processFlags & kWPFShadow) {
+ shadow = params->img.shadow;
+ }
+ int field_390 = 0;
+ if (params->processFlags & 0x200000) {
+ field_390 = params->img.field_390;
+ debug(0, "displayWizComplexImage() unhandled flag 0x200000");
+ }
+ const Common::Rect *r = NULL;
+ if (params->processFlags & kWPFClipBox) {
+ r = &params->box;
+ }
+ int dstResNum = 0;
+ if (params->processFlags & kWPFDstResNum) {
+ dstResNum = params->dstResNum;
+ }
+ if (params->processFlags & kWPFRemapPalette) {
+ remapWizImagePal(params);
+ flags |= kWIFRemapPalette;
+ }
+
+ if (_vm->_fullRedraw && dstResNum == 0) {
+ if (sourceImage != 0 || (params->processFlags & (kWPFScaled | kWPFRotate)))
+ error("Can't do this command in the enter script.");
+
+ assert(_imagesNum < ARRAYSIZE(_images));
+ WizImage *pwi = &_images[_imagesNum];
+ pwi->resNum = params->img.resNum;
+ pwi->x1 = po_x;
+ pwi->y1 = po_y;
+ pwi->zorder = params->img.zorder;
+ pwi->state = state;
+ pwi->flags = flags;
+ pwi->shadow = shadow;
+ pwi->field_390 = field_390;
+ pwi->palette = palette;
+ ++_imagesNum;
+ } else {
+ if (sourceImage != 0) {
+ // TODO
+ } else if (params->processFlags & (kWPFScaled | kWPFRotate)) {
+ drawWizComplexPolygon(params->img.resNum, state, po_x, po_y, shadow, rotationAngle, scale, r, flags, dstResNum, palette);
+ } else {
+ if (flags & kWIFIsPolygon) {
+ drawWizPolygon(params->img.resNum, state, po_x, flags, shadow, dstResNum, palette); // XXX , VAR(VAR_WIZ_TCOLOR));
+ } else {
+ drawWizImage(params->img.resNum, state, po_x, po_y, params->img.zorder, shadow, field_390, r, flags, dstResNum, palette);
+ }
+ }
+ }
+}
+
+void Wiz::createWizEmptyImage(const WizParameters *params) {
+ int img_w = 640;
+ if (params->processFlags & kWPFUseDefImgWidth) {
+ img_w = params->resDefImgW;
+ }
+ int img_h = 480;
+ if (params->processFlags & kWPFUseDefImgHeight) {
+ img_h = params->resDefImgH;
+ }
+ int img_x = 0;
+ int img_y = 0;
+ if (params->processFlags & 1) {
+ img_x = params->img.x1;
+ img_y = params->img.y1;
+ }
+ const uint16 flags = 0xB;
+ int res_size = 0x1C;
+ if (flags & 1) {
+ res_size += 0x308;
+ }
+ if (flags & 2) {
+ res_size += 0x10;
+ }
+ if (flags & 8) {
+ res_size += 0x10C;
+ }
+ res_size += 8 + img_w * img_h;
+
+ const uint8 *palPtr;
+ if (_vm->_heversion >= 99) {
+ palPtr = _vm->_hePalettes + 1024;
+ } else {
+ palPtr = _vm->_currentPalette;
+ }
+ uint8 *res_data = _vm->res.createResource(rtImage, params->img.resNum, res_size);
+ if (!res_data) {
+ _vm->VAR(119) = -1;
+ } else {
+ _vm->VAR(119) = 0;
+ WRITE_BE_UINT32(res_data, 'AWIZ'); res_data += 4;
+ WRITE_BE_UINT32(res_data, res_size); res_data += 4;
+ WRITE_BE_UINT32(res_data, 'WIZH'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0x14); res_data += 4;
+ WRITE_LE_UINT32(res_data, 0); res_data += 4;
+ WRITE_LE_UINT32(res_data, img_w); res_data += 4;
+ WRITE_LE_UINT32(res_data, img_h); res_data += 4;
+ if (flags & 1) {
+ WRITE_BE_UINT32(res_data, 'RGBS'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0x308); res_data += 4;
+ memcpy(res_data, palPtr, 0x300); res_data += 0x300;
+ }
+ if (flags & 2) {
+ WRITE_BE_UINT32(res_data, 'SPOT'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0x10); res_data += 4;
+ WRITE_BE_UINT32(res_data, img_x); res_data += 4;
+ WRITE_BE_UINT32(res_data, img_y); res_data += 4;
+ }
+ if (flags & 8) {
+ WRITE_BE_UINT32(res_data, 'RMAP'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0x10C); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0); res_data += 4;
+ for (int i = 0; i < 256; ++i) {
+ *res_data++ = i;
+ }
+ }
+ WRITE_BE_UINT32(res_data, 'WIZD'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 8 + img_w * img_h); res_data += 4;
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::fillWizRect(const WizParameters *params) {
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, params->img.resNum);
+ if (dataPtr) {
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ assert(c == 0);
+ Common::Rect areaRect, imageRect(w, h);
+ if (params->processFlags & kWPFClipBox) {
+ if (!imageRect.intersects(params->box)) {
+ return;
+ }
+ imageRect.clip(params->box);
+ }
+ if (params->processFlags & kWPFClipBox2) {
+ areaRect = params->box2;
+ } else {
+ areaRect = imageRect;
+ }
+ uint8 color = _vm->VAR(93);
+ if (params->processFlags & kWPFFillColor) {
+ color = params->fillColor;
+ }
+ if (areaRect.intersects(imageRect)) {
+ areaRect.clip(imageRect);
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+ int dx = areaRect.width();
+ int dy = areaRect.height();
+ wizd += areaRect.top * w + areaRect.left;
+ while (dy--) {
+ memset(wizd, color, dx);
+ wizd += w;
+ }
+ }
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::fillWizLine(const WizParameters *params) {
+ if (params->processFlags & kWPFClipBox2) {
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, params->img.resNum);
+ if (dataPtr) {
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ assert(c == 0);
+ Common::Rect imageRect(w, h);
+ if (params->processFlags & kWPFClipBox) {
+ if (!imageRect.intersects(params->box)) {
+ return;
+ }
+ imageRect.clip(params->box);
+ }
+ uint8 color = _vm->VAR(93);
+ if (params->processFlags & kWPFFillColor) {
+ color = params->fillColor;
+ }
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+ int x1 = params->box2.left;
+ int y1 = params->box2.top;
+ int x2 = params->box2.right;
+ int y2 = params->box2.bottom;
+
+ int dx = x2 - x1;
+ int incx = 0;
+ if (dx > 0) {
+ incx = 1;
+ } else if (dx < 0) {
+ incx = -1;
+ }
+ int dy = y2 - y1;
+ int incy = 0;
+ if (dy > 0) {
+ incy = 1;
+ } else if (dy < 0) {
+ incy = -1;
+ }
+
+ dx = ABS(x2 - x1);
+ dy = ABS(y2 - y1);
+
+ if (imageRect.contains(x1, y1)) {
+ *(wizd + y1 * w + x1) = color;
+ }
+
+ if (dx >= dy) {
+ int step1_y = (dy - dx) * 2;
+ int step2_y = dy * 2;
+ int accum_y = dy * 2 - dx;
+ while (x1 != x2) {
+ if (accum_y <= 0) {
+ accum_y += step2_y;
+ } else {
+ accum_y += step1_y;
+ y1 += incy;
+ }
+ x1 += incx;
+ if (imageRect.contains(x1, y1)) {
+ *(wizd + y1 * w + x1) = color;
+ }
+ }
+ } else {
+ int step1_x = (dx - dy) * 2;
+ int step2_x = dx * 2;
+ int accum_x = dx * 2 - dy;
+ while (y1 != y2) {
+ if (accum_x <= 0) {
+ accum_x += step2_x;
+ } else {
+ accum_x += step1_x;
+ x1 += incx;
+ }
+ y1 += incy;
+ if (imageRect.contains(x1, y1)) {
+ *(wizd + y1 * w + x1) = color;
+ }
+ }
+ }
+ }
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::fillWizPixel(const WizParameters *params) {
+ if (params->processFlags & kWPFClipBox2) {
+ int px = params->box2.left;
+ int py = params->box2.top;
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, params->img.resNum);
+ if (dataPtr) {
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ assert(c == 0);
+ Common::Rect imageRect(w, h);
+ if (params->processFlags & kWPFClipBox) {
+ if (!imageRect.intersects(params->box)) {
+ return;
+ }
+ imageRect.clip(params->box);
+ }
+ uint8 color = _vm->VAR(93);
+ if (params->processFlags & kWPFFillColor) {
+ color = params->fillColor;
+ }
+ if (imageRect.contains(px, py)) {
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+ *(wizd + py * w + px) = color;
+ }
+ }
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::remapWizImagePal(const WizParameters *params) {
+ int st = (params->processFlags & kWPFNewState) ? params->img.state : 0;
+ int num = params->remapNum;
+ const uint8 *index = params->remapIndex;
+ uint8 *iwiz = _vm->getResourceAddress(rtImage, params->img.resNum);
+ assert(iwiz);
+ uint8 *rmap = _vm->findWrappedBlock(MKID('RMAP'), iwiz, st, 0) ;
+ assert(rmap);
+ WRITE_BE_UINT32(rmap, 0x01234567);
+ while (num--) {
+ uint8 idx = *index++;
+ rmap[4 + idx] = params->remapColor[idx];
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::processWizImage(const WizParameters *params) {
+ char buf[512];
+ unsigned int i;
+
+ debug(2, "processWizImage: processMode %d", params->processMode);
+ switch (params->processMode) {
+ case 0:
+ // Used in racedemo
+ break;
+ case 1:
+ displayWizComplexImage(params);
+ break;
+ case 2:
+ captureWizImage(params->img.resNum, params->box, (params->img.flags & kWIFBlitToFrontVideoBuffer) != 0, params->compType);
+ break;
+ case 3:
+ if (params->processFlags & kWPFUseFile) {
+ Common::File f;
+
+ // Convert Windows path separators to something more portable
+ strncpy(buf, (const char *)params->filename, 512);
+ for (i = 0; i < strlen(buf); i++) {
+ if (buf[i] == '\\')
+ buf[i] = '/';
+ }
+
+ if (f.open((const char *)buf, Common::File::kFileReadMode)) {
+ uint32 id = f.readUint32LE();
+ if (id == TO_LE_32(MKID('AWIZ')) || id == TO_LE_32(MKID('MULT'))) {
+ uint32 size = f.readUint32BE();
+ f.seek(0, SEEK_SET);
+ byte *p = _vm->res.createResource(rtImage, params->img.resNum, size);
+ if (f.read(p, size) != size) {
+ _vm->res.nukeResource(rtImage, params->img.resNum);
+ error("i/o error when reading '%s'", buf);
+ _vm->VAR(_vm->VAR_GAME_LOADED) = -2;
+ _vm->VAR(119) = -2;
+ } else {
+ _vm->res.setModified(rtImage, params->img.resNum);
+ _vm->VAR(_vm->VAR_GAME_LOADED) = 0;
+ _vm->VAR(119) = 0;
+ }
+ } else {
+ _vm->VAR(_vm->VAR_GAME_LOADED) = -1;
+ _vm->VAR(119) = -1;
+ }
+ f.close();
+ } else {
+ _vm->VAR(_vm->VAR_GAME_LOADED) = -3;
+ _vm->VAR(119) = -3;
+ debug(0, "Unable to open for read '%s'", buf);
+ }
+ }
+ break;
+ case 4:
+ if (params->processFlags & kWPFUseFile) {
+ Common::File f;
+
+ switch(params->fileWriteMode) {
+ case 2:
+ _vm->VAR(119) = -1;
+ break;
+ case 1:
+ // TODO Write image to file
+ break;
+ case 0:
+ // Convert Windows path separators to something more portable
+ strncpy(buf, (const char *)params->filename, 512);
+ for (i = 0; i < strlen(buf); i++) {
+ if (buf[i] == '\\')
+ buf[i] = '/';
+ }
+
+ if (!f.open((const char *)buf, Common::File::kFileWriteMode)) {
+ debug(0, "Unable to open for write '%s'", buf);
+ _vm->VAR(119) = -3;
+ } else {
+ byte *p = _vm->getResourceAddress(rtImage, params->img.resNum);
+ uint32 size = READ_BE_UINT32(p + 4);
+ if (f.write(p, size) != size) {
+ error("i/o error when writing '%s'", params->filename);
+ _vm->VAR(119) = -2;
+ } else {
+ _vm->VAR(119) = 0;
+ }
+ f.close();
+ }
+ break;
+ default:
+ error("processWizImage: processMode 4 unhandled fileWriteMode %d", params->fileWriteMode);
+ }
+ }
+ break;
+ case 6:
+ if (params->processFlags & kWPFRemapPalette) {
+ remapWizImagePal(params);
+ }
+ break;
+ // HE 99+
+ case 7:
+ // Used in PuttsFunShop/SamsFunShop/soccer2004
+ // TODO: Capture polygon
+ _vm->res.setModified(rtImage, params->img.resNum);
+ break;
+ case 8:
+ createWizEmptyImage(params);
+ break;
+ case 9:
+ fillWizRect(params);
+ break;
+ case 10:
+ fillWizLine(params);
+ break;
+ case 11:
+ fillWizPixel(params);
+ break;
+ case 12:
+ fillWizFlood(params);
+ break;
+ case 13:
+ // Used for text in FreddisFunShop/PuttsFunShop/SamsFunShop
+ // TODO: Start Font
+ break;
+ case 14:
+ // Used for text in FreddisFunShop/PuttsFunShop/SamsFunShop
+ // TODO: End Font
+ break;
+ case 15:
+ // Used for text in FreddisFunShop/PuttsFunShop/SamsFunShop
+ // TODO: Create Font
+ break;
+ case 16:
+ // TODO: Render Font String
+ error("Render Font String");
+ break;
+ case 17:
+ // Used in to draw circles in FreddisFunShop/PuttsFunShop/SamsFunShop
+ // TODO: Ellipse
+ _vm->res.setModified(rtImage, params->img.resNum);
+ break;
+ default:
+ error("Unhandled processWizImage mode %d", params->processMode);
+ }
+}
+
+void Wiz::getWizImageDim(int resNum, int state, int32 &w, int32 &h) {
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum);
+ assert(dataPtr);
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ w = READ_LE_UINT32(wizh + 0x4);
+ h = READ_LE_UINT32(wizh + 0x8);
+}
+
+void Wiz::getWizImageSpot(int resId, int state, int32 &x, int32 &y) {
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, resId);
+ assert(dataPtr);
+ uint8 *spotPtr = _vm->findWrappedBlock(MKID('SPOT'), dataPtr, state, 0);
+ if (spotPtr) {
+ x = READ_LE_UINT32(spotPtr + 0);
+ y = READ_LE_UINT32(spotPtr + 4);
+ } else {
+ x = 0;
+ y = 0;
+ }
+}
+
+int Wiz::getWizImageData(int resNum, int state, int type) {
+ uint8 *dataPtr, *wizh;
+
+ dataPtr = _vm->getResourceAddress(rtImage, resNum);
+ assert(dataPtr);
+
+ switch (type) {
+ case 0:
+ wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ return READ_LE_UINT32(wizh + 0x0);
+ case 1:
+ return (_vm->findWrappedBlock(MKID('RGBS'), dataPtr, state, 0) != NULL) ? 1 : 0;
+ case 2:
+ return (_vm->findWrappedBlock(MKID('RMAP'), dataPtr, state, 0) != NULL) ? 1 : 0;
+ case 3:
+ return (_vm->findWrappedBlock(MKID('TRNS'), dataPtr, state, 0) != NULL) ? 1 : 0;
+ case 4:
+ return (_vm->findWrappedBlock(MKID('XMAP'), dataPtr, state, 0) != NULL) ? 1 : 0;
+ default:
+ error("getWizImageData: Unknown type %d", type);
+ }
+}
+
+int Wiz::getWizImageStates(int resNum) {
+ const uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum);
+ assert(dataPtr);
+ if (READ_UINT32(dataPtr) == MKID('MULT')) {
+ const byte *offs, *wrap;
+
+ wrap = _vm->findResource(MKID('WRAP'), dataPtr);
+ if (wrap == NULL)
+ return 1;
+
+ offs = _vm->findResourceData(MKID('OFFS'), wrap);
+ if (offs == NULL)
+ return 1;
+
+ return _vm->getResourceDataSize(offs) / 4;
+ } else {
+ return 1;
+ }
+}
+
+int Wiz::isWizPixelNonTransparent(int resNum, int state, int x, int y, int flags) {
+ int ret = 0;
+ uint8 *data = _vm->getResourceAddress(rtImage, resNum);
+ assert(data);
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), data, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), data, state, 0);
+ assert(wizd);
+ if (x >= 0 && x < w && y >= 0 && y < h) {
+ if (flags & kWIFFlipX) {
+ x = w - x - 1;
+ }
+ if (flags & kWIFFlipY) {
+ y = h - y - 1;
+ }
+ switch (c) {
+ case 0:
+ if (_vm->_heversion >= 99) {
+ ret = getRawWizPixelColor(wizd, x, y, w, h, _vm->VAR(_vm->VAR_WIZ_TCOLOR)) != _vm->VAR(_vm->VAR_WIZ_TCOLOR) ? 1 : 0;
+ } else {
+ ret = 0;
+ }
+ break;
+ case 1:
+ ret = isWizPixelNonTransparent(wizd, x, y, w, h);
+ break;
+ case 2:
+ // Used baseball2003
+ debug(0, "isWizPixelNonTransparent: Unhandled wiz compression type %d", c);
+ break;
+ default:
+ error("isWizPixelNonTransparent: Unhandled wiz compression type %d", c);
+ break;
+ }
+ }
+ return ret;
+}
+
+uint8 Wiz::getWizPixelColor(int resNum, int state, int x, int y, int flags) {
+ uint8 color;
+ uint8 *data = _vm->getResourceAddress(rtImage, resNum);
+ assert(data);
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), data, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), data, state, 0);
+ assert(wizd);
+ switch (c) {
+ case 0:
+ if (_vm->_heversion >= 99) {
+ color = getRawWizPixelColor(wizd, x, y, w, h, _vm->VAR(_vm->VAR_WIZ_TCOLOR));
+ } else {
+ color = _vm->VAR(_vm->VAR_WIZ_TCOLOR);
+ }
+ break;
+ case 1:
+ color = getWizPixelColor(wizd, x, y, w, h, _vm->VAR(_vm->VAR_WIZ_TCOLOR));
+ break;
+ default:
+ error("getWizPixelColor: Unhandled wiz compression type %d", c);
+ break;
+ }
+ return color;
+}
+
+int ScummEngine_v90he::computeWizHistogram(int resNum, int state, int x, int y, int w, int h) {
+ writeVar(0, 0);
+ defineArray(0, kDwordArray, 0, 0, 0, 255);
+ if (readVar(0) != 0) {
+ Common::Rect rCapt(x, y, w + 1, h + 1);
+ uint8 *data = getResourceAddress(rtImage, resNum);
+ assert(data);
+ uint8 *wizh = findWrappedBlock(MKID('WIZH'), data, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ w = READ_LE_UINT32(wizh + 0x4);
+ h = READ_LE_UINT32(wizh + 0x8);
+ Common::Rect rWiz(w, h);
+ uint8 *wizd = findWrappedBlock(MKID('WIZD'), data, state, 0);
+ assert(wizd);
+ if (rCapt.intersects(rWiz)) {
+ rCapt.clip(rWiz);
+ uint32 histogram[256];
+ memset(histogram, 0, sizeof(histogram));
+ switch (c) {
+ case 0:
+ _wiz->computeRawWizHistogram(histogram, wizd, w, rCapt);
+ break;
+ case 1:
+ _wiz->computeWizHistogram(histogram, wizd, rCapt);
+ break;
+ default:
+ error("computeWizHistogram: Unhandled wiz compression type %d", c);
+ break;
+ }
+ for (int i = 0; i < 256; ++i) {
+ writeArray(0, 0, i, histogram[i]);
+ }
+ }
+ }
+ return readVar(0);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/wiz_he.h b/engines/scumm/wiz_he.h
new file mode 100644
index 0000000000..86d3e97721
--- /dev/null
+++ b/engines/scumm/wiz_he.h
@@ -0,0 +1,211 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(WIZ_HE_H) && !defined(DISABLE_HE)
+#define WIZ_HE_H
+
+#include "common/rect.h"
+
+namespace Scumm {
+
+struct WizPolygon {
+ Common::Point vert[5];
+ Common::Rect bound;
+ int id;
+ int numVerts;
+ bool flag;
+};
+
+struct WizImage {
+ int resNum;
+ int x1;
+ int y1;
+ int zorder;
+ int state;
+ int flags;
+ int shadow;
+ int field_390;
+ int palette;
+};
+
+struct WizParameters {
+ int field_0;
+ byte filename[260];
+ Common::Rect box;
+ int processFlags;
+ int processMode;
+ int field_11C;
+ int field_120;
+ int field_124;
+ int field_128;
+ int field_12C;
+ int field_130;
+ int field_134;
+ int field_138;
+ int compType;
+ int fileWriteMode;
+ int angle;
+ int scale;
+ int field_164;
+ int field_168;
+ int resDefImgW;
+ int resDefImgH;
+ int sourceImage;
+ int field_180;
+ int field_184;
+ uint8 remapColor[256];
+ uint8 remapIndex[256];
+ int remapNum;
+ int dstResNum;
+ byte fillColor;
+ byte string1[4096];
+ byte string2[4096];
+ int field_2399;
+ int field_239D;
+ int field_23A1;
+ int field_23A5;
+ int field_23A9;
+ int field_23AD;
+ int field_23B1;
+ int field_23B5;
+ int field_23B9;
+ int field_23BD;
+ int field_23C1;
+ int field_23C5;
+ int field_23C9;
+ int field_23CD;
+ Common::Rect box2;
+ int field_23DE;
+ int spriteId;
+ int spriteGroup;
+ int field_23EA;
+ WizImage img;
+};
+
+enum WizImageFlags {
+ kWIFHasPalette = 0x1,
+ kWIFRemapPalette = 0x2,
+ kWIFPrint = 0x4,
+ kWIFBlitToFrontVideoBuffer = 0x8,
+ kWIFMarkBufferDirty = 0x10,
+ kWIFBlitToMemBuffer = 0x20,
+ kWIFIsPolygon = 0x40,
+ kWIFFlipX = 0x400,
+ kWIFFlipY = 0x800
+};
+
+enum WizProcessFlags {
+ kWPFSetPos = 0x1,
+ kWPFShadow = 0x4,
+ kWPFScaled = 0x8,
+ kWPFRotate = 0x10,
+ kWPFNewFlags = 0x20,
+ kWPFRemapPalette = 0x40,
+ kWPFClipBox = 0x200,
+ kWPFNewState = 0x400,
+ kWPFUseFile = 0x800,
+ kWPFUseDefImgWidth = 0x2000,
+ kWPFUseDefImgHeight = 0x4000,
+ kWPFPaletteNum = 0x8000,
+ kWPFDstResNum = 0x10000,
+ kWPFFillColor = 0x20000,
+ kWPFClipBox2 = 0x40000,
+ kWPFMaskImg = 0x80000
+};
+
+class ScummEngine_v70he;
+
+class Wiz {
+public:
+ enum {
+ NUM_POLYGONS = 200,
+ NUM_IMAGES = 255
+ };
+
+ WizImage _images[NUM_IMAGES];
+ uint16 _imagesNum;
+ WizPolygon _polygons[NUM_POLYGONS];
+
+ Wiz(ScummEngine_v70he *vm);
+
+ void clearWizBuffer();
+ Common::Rect _rectOverride;
+ bool _rectOverrideEnabled;
+
+ void polygonClear();
+ void polygonLoad(const uint8 *polData);
+ void polygonStore(int id, bool flag, int vert1x, int vert1y, int vert2x, int vert2y, int vert3x, int vert3y, int vert4x, int vert4y);
+ void polygonCalcBoundBox(Common::Point *vert, int numVerts, Common::Rect & bound);
+ void polygonErase(int fromId, int toId);
+ int polygonHit(int id, int x, int y);
+ bool polygonDefined(int id);
+ bool polygonContains(const WizPolygon &pol, int x, int y);
+ void polygonRotatePoints(Common::Point *pts, int num, int alpha);
+ void polygonTransform(int resNum, int state, int po_x, int po_y, int angle, int zoom, Common::Point *vert);
+
+ void createWizEmptyImage(const WizParameters *params);
+ void fillWizRect(const WizParameters *params);
+ void fillWizLine(const WizParameters *params);
+ void fillWizPixel(const WizParameters *params);
+ void fillWizFlood(const WizParameters *params);
+ void remapWizImagePal(const WizParameters *params);
+
+ void getWizImageDim(int resNum, int state, int32 &w, int32 &h);
+ int getWizImageStates(int resnum);
+ int isWizPixelNonTransparent(int resnum, int state, int x, int y, int flags);
+ uint8 getWizPixelColor(int resnum, int state, int x, int y, int flags);
+ int getWizImageData(int resNum, int state, int type);
+
+ void flushWizBuffer();
+
+ void getWizImageSpot(int resId, int state, int32 &x, int32 &y);
+ void loadWizCursor(int resId);
+
+ void captureWizImage(int resNum, const Common::Rect& r, bool frontBuffer, int compType);
+ void displayWizComplexImage(const WizParameters *params);
+ void displayWizImage(WizImage *pwi);
+ void processWizImage(const WizParameters *params);
+
+ uint8 *drawWizImage(int resNum, int state, int x1, int y1, int zorder, int shadow, int field_390, const Common::Rect *clipBox, int flags, int dstResNum, int palette);
+ void drawWizPolygon(int resNum, int state, int id, int flags, int shadow, int dstResNum, int palette);
+ void drawWizComplexPolygon(int resNum, int state, int po_x, int po_y, int shadow, int angle, int zoom, const Common::Rect *r, int flags, int dstResNum, int palette);
+ void drawWizPolygonTransform(int resNum, int state, Common::Point *wp, int flags, int shadow, int dstResNum, int palette);
+
+ static void copyAuxImage(uint8 *dst1, uint8 *dst2, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch);
+ static void copyWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags = 0, const uint8 *palPtr = NULL, const uint8 *xmapPtr = NULL);
+ static void copyRawWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor);
+ static void copyRaw16BitWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor);
+ static void decompressWizImage(uint8 *dst, int dstPitch, const Common::Rect &dstRect, const uint8 *src, const Common::Rect &srcRect, int flags, const uint8 *palPtr = NULL, const uint8 *xmapPtr = NULL);
+ int isWizPixelNonTransparent(const uint8 *data, int x, int y, int w, int h);
+ uint8 getWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color);
+ uint8 getRawWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color);
+ void computeWizHistogram(uint32 *histogram, const uint8 *data, const Common::Rect& rCapt);
+ void computeRawWizHistogram(uint32 *histogram, const uint8 *data, int srcPitch, const Common::Rect& rCapt);
+
+private:
+ ScummEngine_v70he *_vm;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/simon/charset.cpp b/engines/simon/charset.cpp
new file mode 100644
index 0000000000..05ed2c0351
--- /dev/null
+++ b/engines/simon/charset.cpp
@@ -0,0 +1,1277 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "simon/simon.h"
+#include "simon/intern.h"
+
+namespace Simon {
+
+void SimonEngine::print_char_helper_1(const byte *src, uint len) {
+ uint ind;
+
+ if (_textWindow == NULL)
+ return;
+
+ while (len-- != 0) {
+ if (*src != 12 && _textWindow->fcs_data != NULL &&
+ _fcsData1[ind = get_fcs_ptr_3_index(_textWindow)] != 2) {
+
+ _fcsData1[ind] = 2;
+ _fcsData2[ind] = 1;
+ }
+
+ fcs_putchar(*src++);
+ }
+}
+
+void SimonEngine::print_char_helper_5(FillOrCopyStruct *fcs) {
+ uint index = get_fcs_ptr_3_index(fcs);
+ print_char_helper_6(index);
+ _fcsData1[index] = 0;
+}
+
+void SimonEngine::print_char_helper_6(uint i) {
+ FillOrCopyStruct *fcs;
+
+ if (_fcsData2[i]) {
+ mouseOff();
+ fcs = _windowArray[i];
+ drawIconArray(i, fcs->fcs_data->item_ptr, fcs->fcs_data->unk1, fcs->fcs_data->unk2);
+ _fcsData2[i] = 0;
+ mouseOn();
+ }
+}
+
+void SimonEngine::render_string_amiga(uint vga_sprite_id, uint color, uint width, uint height, const char *txt) {
+ VgaPointersEntry *vpe = &_vgaBufferPointers[2];
+ byte *src, *dst, *dst_org, chr;
+ uint count;
+
+ if (vga_sprite_id >= 100) {
+ vga_sprite_id -= 100;
+ vpe++;
+ }
+
+ src = dst = vpe->vgaFile2;
+
+ count = 499;
+ if (vga_sprite_id == 1)
+ count *= 2;
+
+ src += vga_sprite_id * 8;
+ dst += READ_BE_UINT32(src);
+ WRITE_BE_UINT16(dst + 4, height);
+ WRITE_BE_UINT16(dst + 6, width);
+
+ uint charsize = width/8 * height;
+ memset(dst, 0, count);
+ dst_org = dst;
+ int delta = 0;
+ while ((chr = *txt++) != 0) {
+ int tmp = chr;
+ if (chr == 10) {
+ dst_org += width * 10;
+ dst = dst_org;
+ delta = 0;
+ } else if ((tmp -= '!') < 0) {
+ delta += 6;
+ if (delta > 8)
+ {
+ delta -= 8;
+ dst_org++;
+ }
+ } else {
+ byte *img = src + chr * 41;
+ int CTR = img[40];
+ int D3 = 8 - delta;
+ for (int D2 = 9; D2 != 0; D2--)
+ {
+ byte *cur_dst = dst_org;
+ for (int D7 = 2; D7 != 0; D7--)
+ {
+ chr = *img >> delta;
+ if (chr)
+ {
+ if (color & 1) *(cur_dst + charsize * 0) |= chr;
+ if (color & 2) *(cur_dst + charsize * 1) |= chr;
+ if (color & 4) *(cur_dst + charsize * 2) |= chr;
+ if (color & 8) *(cur_dst + charsize * 3) |= chr;
+ }
+ if ((D3 >= CTR) && (chr = *img++ << (D3)))
+ {
+ if (color & 1) *(cur_dst + charsize * 0) |= chr;
+ if (color & 2) *(cur_dst + charsize * 1) |= chr;
+ if (color & 4) *(cur_dst + charsize * 2) |= chr;
+ if (color & 8) *(cur_dst + charsize * 3) |= chr;
+ }
+ color++;
+ }
+ chr = *img >> delta;
+ if (chr)
+ {
+ *(cur_dst + charsize * 0) |= chr;
+ *(cur_dst + charsize * 1) |= chr;
+ *(cur_dst + charsize * 2) |= chr;
+ *(cur_dst + charsize * 3) |= chr;
+ }
+ if ((D3 >= CTR) && (chr = *img++ << (D3)))
+ {
+ *(cur_dst + charsize * 0) |= chr;
+ *(cur_dst + charsize * 1) |= chr;
+ *(cur_dst + charsize * 2) |= chr;
+ *(cur_dst + charsize * 3) |= chr;
+ }
+ cur_dst += width/8;
+ }
+ delta += CTR;
+ if (delta > 8)
+ {
+ delta -= 8;
+ dst_org++;
+ }
+ }
+ }
+}
+
+void SimonEngine::render_string(uint vga_sprite_id, uint color, uint width, uint height, const char *txt) {
+ VgaPointersEntry *vpe = &_vgaBufferPointers[2];
+ byte *src, *dst, *p, *dst_org, chr;
+ uint count;
+
+ if (vga_sprite_id >= 100) {
+ vga_sprite_id -= 100;
+ vpe++;
+ }
+
+ src = dst = vpe->vgaFile2;
+
+ count = 4000;
+ if (vga_sprite_id == 1)
+ count *= 2;
+
+ p = dst + vga_sprite_id * 8;
+
+ WRITE_BE_UINT16(p + 4, height);
+ WRITE_BE_UINT16(p + 6, width);
+
+ dst += READ_BE_UINT32(p);
+
+ memset(dst, 0, count);
+ if (_language == Common::HB_ISR)
+ dst += width - 1; // For Hebrew, start at the right edge, not the left.
+
+ dst_org = dst;
+ while ((chr = *txt++) != 0) {
+ if (chr == 10) {
+ dst_org += width * 10;
+ dst = dst_org;
+ } else if ((chr -= ' ') == 0) {
+ dst += (_language == Common::HB_ISR ? -6 : 6); // Hebrew moves to the left, all others to the right
+ } else {
+ byte *img_hdr = src + 48 + chr * 4;
+ uint img_height = img_hdr[2];
+ uint img_width = img_hdr[3], i;
+ byte *img = src + READ_LE_UINT16(img_hdr);
+ if (_language == Common::HB_ISR)
+ dst -= img_width - 1; // For Hebrew, move from right edge to left edge of image.
+ byte *cur_dst = dst;
+
+ assert(img_width > 0 && img_width < 50 && img_height > 0 && img_height < 50);
+
+ do {
+ for (i = 0; i != img_width; i++) {
+ chr = *img++;
+ if (chr) {
+ if (chr == 0xF)
+ chr = 207;
+ else
+ chr += color;
+ cur_dst[i] = chr;
+ }
+ }
+ cur_dst += width;
+ } while (--img_height);
+
+ if (_language != Common::HB_ISR) // Hebrew character movement is done higher up
+ dst += img_width - 1;
+ }
+ }
+}
+
+void SimonEngine::showMessageFormat(const char *s, ...) {
+ char buf[STRINGBUFLEN];
+ char *str;
+ va_list va;
+
+ va_start(va, s);
+ vsnprintf(buf, STRINGBUFLEN, s, va);
+ va_end(va);
+
+ if (!_fcsData1[_curWindow]) {
+ showmessage_helper_2();
+ if (!_showMessageFlag) {
+ _windowArray[0] = _textWindow;
+ showmessage_helper_3(_textWindow->textLength,
+ _textWindow->textMaxLength);
+ }
+ _showMessageFlag = true;
+ _fcsData1[_curWindow] = 1;
+ }
+
+ for (str = buf; *str; str++)
+ showmessage_print_char(*str);
+}
+
+void SimonEngine::showmessage_print_char(byte chr) {
+ if (chr == 12) {
+ _numLettersToPrint = 0;
+ _printCharCurPos = 0;
+ print_char_helper_1(&chr, 1);
+ print_char_helper_5(_textWindow);
+ } else if (chr == 0 || chr == ' ' || chr == 10) {
+ if (_printCharMaxPos - _printCharCurPos >= _numLettersToPrint) {
+ _printCharCurPos += _numLettersToPrint;
+ print_char_helper_1(_lettersToPrintBuf, _numLettersToPrint);
+
+ if (_printCharCurPos == _printCharMaxPos) {
+ _printCharCurPos = 0;
+ } else {
+ if (chr)
+ print_char_helper_1(&chr, 1);
+ if (chr == 10)
+ _printCharCurPos = 0;
+ else if (chr != 0)
+ _printCharCurPos++;
+ }
+ } else {
+ const byte newline_character = 10;
+ _printCharCurPos = _numLettersToPrint;
+ print_char_helper_1(&newline_character, 1);
+ print_char_helper_1(_lettersToPrintBuf, _numLettersToPrint);
+ if (chr == ' ') {
+ print_char_helper_1(&chr, 1);
+ _printCharCurPos++;
+ } else {
+ print_char_helper_1(&chr, 1);
+ _printCharCurPos = 0;
+ }
+ }
+ _numLettersToPrint = 0;
+ } else {
+ _lettersToPrintBuf[_numLettersToPrint++] = chr;
+ }
+}
+
+void SimonEngine::showmessage_helper_2() {
+ if (_textWindow)
+ return;
+
+ _textWindow = openWindow(8, 0x90, 0x18, 6, 1, 0, 0xF);
+}
+
+void SimonEngine::showmessage_helper_3(uint a, uint b) {
+ _printCharCurPos = a;
+ _printCharMaxPos = b;
+ _numLettersToPrint = 0;
+}
+
+void SimonEngine::video_putchar(FillOrCopyStruct *fcs, byte c, byte b) {
+ byte width = 6;
+
+ if (c == 0xC) {
+ video_fill_or_copy_from_3_to_2(fcs);
+ } else if (c == 0xD || c == 0xA) {
+ video_putchar_newline(fcs);
+ } else if ((c == 1 && _language != Common::HB_ISR) || (c == 8)) {
+ if (_language == Common::HB_ISR) { //Hebrew
+ if (b >= 64 && b < 91)
+ width = _hebrew_char_widths [b - 64];
+
+ if (fcs->textLength != 0) {
+ fcs->textLength--;
+ fcs->textColumnOffset += width;
+ if (fcs->textColumnOffset >= 8) {
+ fcs->textColumnOffset -= 8;
+ fcs->textColumn--;
+ }
+ }
+ } else {
+ int8 val = (c == 8) ? 6 : 4;
+
+ if (fcs->textLength != 0) {
+ fcs->textLength--;
+ fcs->textColumnOffset -= val;
+ if ((int8)fcs->textColumnOffset < val) {
+ fcs->textColumnOffset += 8;
+ fcs->textColumn--;
+ }
+ }
+ }
+ } else if (c >= 0x20) {
+ if (fcs->textLength == fcs->textMaxLength) {
+ video_putchar_newline(fcs);
+ } else if (fcs->textRow == fcs->height) {
+ video_putchar_newline(fcs);
+ fcs->textRow--;
+ }
+
+ if (_language == Common::HB_ISR) { //Hebrew
+ if (c >= 64 && c < 91)
+ width = _hebrew_char_widths [c - 64];
+ fcs->textColumnOffset -= width;
+ if (fcs->textColumnOffset >= width) {
+ fcs->textColumn++;
+ fcs->textColumnOffset += 8;
+ }
+ video_putchar_drawchar(fcs, fcs->width + fcs->x - fcs->textColumn, fcs->textRow * 8 + fcs->y, c);
+ fcs->textLength++;
+ } else {
+ video_putchar_drawchar(fcs, fcs->textColumn + fcs->x, fcs->textRow * 8 + fcs->y, c);
+
+ fcs->textLength++;
+ fcs->textColumnOffset += 6;
+ if (c == 'i' || c == 'l')
+ fcs->textColumnOffset -= 2;
+
+ if (fcs->textColumnOffset >= 8) {
+ fcs->textColumnOffset -= 8;
+ fcs->textColumn++;
+ }
+ }
+ }
+}
+
+void SimonEngine::video_putchar_newline(FillOrCopyStruct *fcs) {
+ fcs->textColumnOffset = 0;
+ fcs->textLength = 0;
+ fcs->textColumn = 0;
+
+ if (fcs->textRow != fcs->height)
+ fcs->textRow++;
+}
+
+#ifdef PALMOS_68K
+static const byte *russian_video_font;
+static const byte *polish_video_font;
+static const byte *french_video_font;
+static const byte *german_video_font;
+static const byte *hebrew_video_font;
+static const byte *italian_video_font;
+static const byte *spanish_video_font;
+static const byte *video_font;
+#else
+static const byte russian_video_font[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 112, 112, 32, 32, 0, 32, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 0, 100, 40, 48, 40, 100, 0,
+ 0, 0, 96, 48, 40, 40, 112, 0,
+ 60, 68, 68, 60, 36, 68, 68, 0,
+ 0, 16, 40, 16, 42, 68, 58, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 4, 0,
+ 0, 32, 16, 16, 16, 16, 32, 0,
+ 72, 84, 84, 116, 84, 84, 72, 0,
+ 0, 0, 60, 68, 60, 36, 100, 0,
+ 0, 0, 0, 0, 0, 48, 48, 96,
+ 0, 0, 0, 240, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 48, 48, 0,
+ 16, 32, 0, 120, 112, 64, 56, 0,
+ 112, 136, 152, 168, 200, 136, 112, 0,
+ 32, 96, 32, 32, 32, 32, 112, 0,
+ 112, 136, 8, 48, 64, 136, 248, 0,
+ 112, 136, 8, 48, 8, 136, 112, 0,
+ 16, 48, 80, 144, 248, 16, 56, 0,
+ 248, 128, 240, 8, 8, 136, 112, 0,
+ 48, 64, 128, 240, 136, 136, 112, 0,
+ 248, 136, 8, 16, 32, 32, 32, 0,
+ 112, 136, 136, 112, 136, 136, 112, 0,
+ 112, 136, 136, 120, 8, 16, 96, 0,
+ 0, 0, 48, 48, 0, 48, 48, 0,
+ 32, 16, 0, 112, 8, 248, 120, 0,
+ 0, 0, 96, 48, 40, 40, 112, 0,
+ 0, 0, 112, 88, 20, 20, 56, 0,
+ 0, 0, 120, 4, 28, 4, 120, 0,
+ 112, 136, 8, 16, 32, 0, 32, 0,
+ 0, 0, 84, 84, 84, 84, 56, 4,
+ 56, 68, 68, 124, 68, 68, 68, 0,
+ 124, 36, 32, 56, 36, 36, 120, 0,
+ 120, 36, 36, 56, 36, 36, 120, 0,
+ 124, 36, 32, 32, 32, 32, 112, 0,
+ 56, 40, 40, 40, 40, 40, 124, 68,
+ 124, 36, 32, 56, 32, 36, 124, 0,
+ 84, 84, 84, 56, 84, 84, 84, 0,
+ 56, 68, 4, 24, 4, 68, 56, 0,
+ 68, 68, 76, 84, 100, 68, 68, 0,
+ 100, 40, 40, 48, 40, 36, 100, 0,
+ 28, 36, 36, 36, 36, 36, 100, 0,
+ 68, 108, 84, 68, 68, 68, 68, 0,
+ 68, 68, 68, 124, 68, 68, 68, 0,
+ 56, 68, 68, 68, 68, 68, 56, 0,
+ 124, 68, 68, 68, 68, 68, 68, 0,
+ 120, 36, 36, 56, 32, 32, 112, 0,
+ 56, 68, 64, 64, 64, 68, 56, 0,
+ 124, 84, 16, 16, 16, 16, 56, 0,
+ 100, 36, 36, 28, 4, 4, 56, 0,
+ 56, 84, 84, 84, 56, 16, 56, 0,
+ 108, 40, 16, 16, 40, 40, 108, 0,
+ 72, 72, 72, 72, 72, 72, 60, 4,
+ 76, 72, 72, 56, 8, 8, 28, 0,
+ 84, 84, 84, 84, 84, 84, 60, 0,
+ 84, 84, 84, 84, 84, 84, 56, 4,
+ 56, 68, 4, 28, 4, 68, 56, 0,
+ 0, 0, 68, 100, 84, 84, 100, 0,
+ 0, 0, 72, 84, 116, 84, 72, 0,
+ 0, 0, 60, 68, 60, 36, 100, 0,
+ 0, 0, 120, 4, 24, 4, 120, 0,
+ 0, 0, 100, 40, 48, 40, 100, 0,
+ 60, 68, 68, 60, 36, 68, 68, 0,
+ 0, 0, 56, 4, 60, 68, 60, 0,
+ 60, 64, 32, 56, 68, 68, 56, 0,
+ 48, 72, 80, 120, 68, 68, 56, 0,
+ 0, 0, 120, 4, 56, 64, 60, 0,
+ 56, 4, 4, 60, 68, 68, 56, 0,
+ 0, 0, 56, 68, 120, 64, 56, 0,
+ 40, 0, 56, 68, 120, 64, 56, 0,
+ 0, 0, 84, 84, 56, 84, 84, 0,
+ 64, 0, 192, 64, 64, 64, 224, 0,
+ 0, 0, 68, 68, 68, 68, 60, 0,
+ 56, 0, 68, 68, 68, 68, 60, 0,
+ 192, 64, 64, 64, 64, 64, 224, 0,
+ 0, 0, 28, 36, 36, 36, 100, 0,
+ 0, 0, 68, 108, 84, 68, 68, 0,
+ 0, 0, 56, 68, 68, 68, 56, 0,
+ 0, 0, 68, 68, 124, 68, 68, 0,
+ 0, 0, 124, 68, 68, 68, 68, 0,
+ 0, 0, 120, 36, 36, 56, 32, 112,
+ 0, 0, 60, 64, 64, 64, 60, 0,
+ 0, 0, 124, 84, 16, 16, 56, 0,
+ 0, 0, 68, 68, 60, 4, 56, 0,
+ 48, 16, 56, 84, 84, 56, 16, 56,
+ 0, 0, 68, 40, 16, 40, 68, 0,
+ 0, 0, 72, 72, 72, 72, 60, 4,
+ 0, 0, 76, 72, 72, 56, 8, 28,
+ 0, 0, 84, 84, 84, 84, 60, 0,
+ 32, 80, 0, 96, 144, 144, 96, 0,
+ 0, 14, 8, 48, 8, 8, 14, 0,
+ 0, 8, 8, 8, 8, 8, 8, 0,
+ 0, 112, 16, 12, 16, 16, 112, 0,
+ 0, 0, 0, 0, 0, 0, 248, 0,
+ 252, 252, 252, 252, 252, 252, 252, 252,
+ 240, 240, 240, 240, 240, 240, 240, 240,
+};
+
+static const byte polish_video_font[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 112, 112, 32, 32, 0, 32, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 0, 112, 136, 248, 128, 112, 8,
+ 0, 16, 120, 128, 112, 8, 240, 0,
+ 192, 64, 64, 96, 192, 64, 224, 0,
+ 0, 16, 40, 16, 42, 68, 58, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 4, 0,
+ 0, 32, 16, 16, 16, 16, 32, 0,
+ 0, 0, 20, 8, 62, 8, 20, 0,
+ 0, 32, 112, 136, 136, 136, 112, 0,
+ 0, 0, 0, 0, 0, 48, 48, 96,
+ 0, 0, 0, 240, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 48, 48, 0,
+ 0, 32, 112, 136, 128, 136, 112, 0,
+ 112, 136, 152, 168, 200, 136, 112, 0,
+ 32, 96, 32, 32, 32, 32, 112, 0,
+ 112, 136, 8, 48, 64, 136, 248, 0,
+ 112, 136, 8, 48, 8, 136, 112, 0,
+ 16, 48, 80, 144, 248, 16, 56, 0,
+ 248, 128, 240, 8, 8, 136, 112, 0,
+ 48, 64, 128, 240, 136, 136, 112, 0,
+ 248, 136, 8, 16, 32, 32, 32, 0,
+ 112, 136, 136, 112, 136, 136, 112, 0,
+ 112, 136, 136, 120, 8, 16, 96, 0,
+ 0, 0, 48, 48, 0, 48, 48, 0,
+ 0, 32, 240, 136, 136, 136, 136, 0,
+ 80, 0, 136, 136, 136, 136, 112, 0,
+ 0, 32, 248, 144, 32, 72, 248, 0,
+ 8, 32, 248, 144, 32, 72, 248, 0,
+ 112, 136, 8, 16, 32, 0, 32, 0,
+ 0, 0, 112, 8, 120, 136, 120, 4,
+ 112, 136, 136, 248, 136, 136, 136, 0,
+ 240, 72, 72, 112, 72, 72, 240, 0,
+ 48, 72, 128, 128, 128, 72, 48, 0,
+ 224, 80, 72, 72, 72, 80, 224, 0,
+ 248, 72, 64, 112, 64, 72, 248, 0,
+ 248, 72, 64, 112, 64, 64, 224, 0,
+ 48, 72, 128, 152, 136, 72, 56, 0,
+ 136, 136, 136, 248, 136, 136, 136, 0,
+ 248, 32, 32, 32, 32, 32, 248, 0,
+ 24, 8, 8, 8, 136, 136, 112, 0,
+ 200, 72, 80, 96, 80, 72, 200, 0,
+ 224, 64, 64, 64, 64, 72, 248, 0,
+ 136, 216, 168, 168, 136, 136, 136, 0,
+ 136, 200, 168, 152, 136, 136, 136, 0,
+ 112, 136, 136, 136, 136, 136, 112, 0,
+ 240, 72, 72, 112, 64, 64, 224, 0,
+ 112, 136, 136, 136, 136, 168, 112, 8,
+ 240, 72, 72, 112, 72, 72, 200, 0,
+ 112, 136, 128, 112, 8, 136, 112, 0,
+ 248, 168, 32, 32, 32, 32, 112, 0,
+ 136, 136, 136, 136, 136, 136, 120, 0,
+ 136, 136, 136, 80, 80, 32, 32, 0,
+ 136, 136, 136, 136, 168, 216, 136, 0,
+ 136, 136, 80, 32, 80, 136, 136, 0,
+ 136, 136, 136, 112, 32, 32, 112, 0,
+ 248, 136, 16, 32, 64, 136, 248, 0,
+ 0, 14, 8, 8, 8, 8, 14, 0,
+ 0, 128, 64, 32, 16, 8, 4, 0,
+ 224, 64, 64, 96, 192, 72, 248, 0,
+ 16, 120, 128, 112, 8, 136, 112, 0,
+ 248, 72, 64, 112, 64, 72, 248, 16,
+ 32, 248, 16, 32, 64, 136, 248, 0,
+ 0, 0, 112, 8, 120, 136, 120, 0,
+ 192, 64, 80, 104, 72, 72, 112, 0,
+ 0, 0, 112, 136, 128, 136, 112, 0,
+ 24, 16, 80, 176, 144, 144, 112, 0,
+ 0, 0, 112, 136, 248, 128, 112, 0,
+ 48, 72, 64, 224, 64, 64, 224, 0,
+ 0, 0, 104, 144, 144, 112, 136, 112,
+ 192, 64, 80, 104, 72, 72, 200, 0,
+ 64, 0, 192, 64, 64, 64, 224, 0,
+ 8, 0, 8, 8, 8, 8, 136, 112,
+ 192, 64, 72, 80, 96, 80, 200, 0,
+ 192, 64, 64, 64, 64, 64, 224, 0,
+ 0, 0, 144, 216, 168, 136, 136, 0,
+ 0, 0, 240, 136, 136, 136, 136, 0,
+ 0, 0, 112, 136, 136, 136, 112, 0,
+ 0, 0, 176, 72, 72, 112, 64, 224,
+ 0, 0, 104, 144, 144, 112, 16, 56,
+ 0, 0, 176, 72, 72, 64, 224, 0,
+ 0, 0, 120, 128, 112, 8, 240, 0,
+ 64, 64, 240, 64, 64, 72, 48, 0,
+ 0, 0, 144, 144, 144, 144, 104, 0,
+ 0, 0, 136, 136, 136, 80, 32, 0,
+ 0, 0, 136, 136, 168, 216, 144, 0,
+ 0, 0, 136, 80, 32, 80, 136, 0,
+ 0, 0, 136, 136, 136, 112, 32, 192,
+ 0, 0, 248, 144, 32, 72, 248, 0,
+ 32, 80, 0, 96, 144, 144, 96, 0,
+ 0, 14, 8, 48, 8, 8, 14, 0,
+ 0, 8, 8, 8, 8, 8, 8, 0,
+ 0, 112, 16, 12, 16, 16, 112, 0,
+ 0, 0, 0, 0, 0, 0, 248, 0,
+ 252, 252, 252, 252, 252, 252, 252, 252,
+ 240, 240, 240, 240, 240, 240, 240, 240,
+};
+
+static const byte french_video_font[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 112, 112, 32, 32, 0, 32, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 32, 80, 0, 112, 136, 136, 112, 0,
+ 32, 80, 0, 112, 8, 248, 120, 0,
+ 112, 136, 128, 128, 136, 112, 32, 96,
+ 0, 16, 40, 16, 42, 68, 58, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 4, 0,
+ 0, 32, 16, 16, 16, 16, 32, 0,
+ 0, 0, 20, 8, 62, 8, 20, 0,
+ 112, 136, 128, 128, 136, 112, 32, 96,
+ 0, 0, 0, 0, 0, 48, 48, 96,
+ 0, 0, 0, 240, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 48, 48, 0,
+ 32, 64, 0, 112, 248, 128, 112, 0,
+ 112, 136, 152, 168, 200, 136, 112, 0,
+ 32, 96, 32, 32, 32, 32, 112, 0,
+ 112, 136, 8, 48, 64, 136, 248, 0,
+ 112, 136, 8, 48, 8, 136, 112, 0,
+ 16, 48, 80, 144, 248, 16, 56, 0,
+ 248, 128, 240, 8, 8, 136, 112, 0,
+ 48, 64, 128, 240, 136, 136, 112, 0,
+ 248, 136, 8, 16, 32, 32, 32, 0,
+ 112, 136, 136, 112, 136, 136, 112, 0,
+ 112, 136, 136, 120, 8, 16, 96, 0,
+ 0, 0, 48, 48, 0, 48, 48, 0,
+ 32, 16, 0, 112, 8, 248, 120, 0,
+ 32, 80, 0, 144, 144, 144, 104, 0,
+ 32, 16, 0, 112, 248, 128, 112, 0,
+ 32, 80, 0, 112, 248, 128, 112, 0,
+ 112, 136, 8, 16, 32, 0, 32, 0,
+ 32, 80, 0, 192, 64, 64, 224, 0,
+ 112, 136, 136, 248, 136, 136, 136, 0,
+ 240, 72, 72, 112, 72, 72, 240, 0,
+ 48, 72, 128, 128, 128, 72, 48, 0,
+ 224, 80, 72, 72, 72, 80, 224, 0,
+ 248, 72, 64, 112, 64, 72, 248, 0,
+ 248, 72, 64, 112, 64, 64, 224, 0,
+ 48, 72, 128, 152, 136, 72, 56, 0,
+ 136, 136, 136, 248, 136, 136, 136, 0,
+ 248, 32, 32, 32, 32, 32, 248, 0,
+ 24, 8, 8, 8, 136, 136, 112, 0,
+ 200, 72, 80, 96, 80, 72, 200, 0,
+ 224, 64, 64, 64, 64, 72, 248, 0,
+ 136, 216, 168, 168, 136, 136, 136, 0,
+ 136, 200, 168, 152, 136, 136, 136, 0,
+ 112, 136, 136, 136, 136, 136, 112, 0,
+ 240, 72, 72, 112, 64, 64, 224, 0,
+ 112, 136, 136, 136, 136, 168, 112, 8,
+ 240, 72, 72, 112, 72, 72, 200, 0,
+ 112, 136, 128, 112, 8, 136, 112, 0,
+ 248, 168, 32, 32, 32, 32, 112, 0,
+ 136, 136, 136, 136, 136, 136, 120, 0,
+ 136, 136, 136, 80, 80, 32, 32, 0,
+ 136, 136, 136, 136, 168, 216, 136, 0,
+ 136, 136, 80, 32, 80, 136, 136, 0,
+ 136, 136, 136, 112, 32, 32, 112, 0,
+ 248, 136, 16, 32, 64, 136, 248, 0,
+ 0, 14, 8, 8, 8, 8, 14, 0,
+ 0, 128, 64, 32, 16, 8, 4, 0,
+ 0, 112, 16, 16, 16, 16, 112, 0,
+ 0, 0, 112, 136, 128, 112, 32, 96,
+ 160, 0, 192, 64, 64, 64, 224, 0,
+ 32, 16, 0, 144, 144, 144, 104, 0,
+ 0, 0, 112, 8, 120, 136, 120, 0,
+ 192, 64, 80, 104, 72, 72, 112, 0,
+ 0, 0, 112, 136, 128, 136, 112, 0,
+ 24, 16, 80, 176, 144, 144, 112, 0,
+ 0, 0, 112, 136, 248, 128, 112, 0,
+ 48, 72, 64, 224, 64, 64, 224, 0,
+ 0, 0, 104, 144, 144, 112, 136, 112,
+ 192, 64, 80, 104, 72, 72, 200, 0,
+ 64, 0, 192, 64, 64, 64, 224, 0,
+ 8, 0, 8, 8, 8, 8, 136, 112,
+ 192, 64, 72, 80, 96, 80, 200, 0,
+ 192, 64, 64, 64, 64, 64, 224, 0,
+ 0, 0, 144, 216, 168, 136, 136, 0,
+ 0, 0, 240, 136, 136, 136, 136, 0,
+ 0, 0, 112, 136, 136, 136, 112, 0,
+ 0, 0, 176, 72, 72, 112, 64, 224,
+ 0, 0, 104, 144, 144, 112, 16, 56,
+ 0, 0, 176, 72, 72, 64, 224, 0,
+ 0, 0, 120, 128, 112, 8, 240, 0,
+ 64, 64, 240, 64, 64, 72, 48, 0,
+ 0, 0, 144, 144, 144, 144, 104, 0,
+ 0, 0, 136, 136, 136, 80, 32, 0,
+ 0, 0, 136, 136, 168, 216, 144, 0,
+ 0, 0, 136, 80, 32, 80, 136, 0,
+ 0, 0, 136, 136, 136, 112, 32, 192,
+ 0, 0, 248, 144, 32, 72, 248, 0,
+ 32, 80, 0, 96, 144, 144, 96, 0,
+ 0, 14, 8, 48, 8, 8, 14, 0,
+ 0, 8, 8, 8, 8, 8, 8, 0,
+ 0, 112, 16, 12, 16, 16, 112, 0,
+ 0, 0, 0, 0, 0, 0, 248, 0,
+ 252, 252, 252, 252, 252, 252, 252, 252,
+ 240, 240, 240, 240, 240, 240, 240, 240,
+};
+
+static const byte german_video_font[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 112, 112, 32, 32, 0, 32, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 80, 0, 112, 8, 120, 136, 120, 0,
+ 80, 0, 112, 136, 136, 136, 112, 0,
+ 80, 0, 144, 144, 144, 144, 104, 0,
+ 0, 16, 40, 16, 42, 68, 58, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 4, 0,
+ 0, 32, 16, 16, 16, 16, 32, 0,
+ 0, 0, 20, 8, 62, 8, 20, 0,
+ 96, 144, 144, 160, 144, 144, 160, 128,
+ 0, 0, 0, 0, 0, 48, 48, 96,
+ 0, 0, 0, 240, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 48, 48, 0,
+ 80, 0, 112, 136, 248, 136, 136, 0,
+ 112, 136, 152, 168, 200, 136, 112, 0,
+ 32, 96, 32, 32, 32, 32, 112, 0,
+ 112, 136, 8, 48, 64, 136, 248, 0,
+ 112, 136, 8, 48, 8, 136, 112, 0,
+ 16, 48, 80, 144, 248, 16, 56, 0,
+ 248, 128, 240, 8, 8, 136, 112, 0,
+ 48, 64, 128, 240, 136, 136, 112, 0,
+ 248, 136, 8, 16, 32, 32, 32, 0,
+ 112, 136, 136, 112, 136, 136, 112, 0,
+ 112, 136, 136, 120, 8, 16, 96, 0,
+ 0, 0, 48, 48, 0, 48, 48, 0,
+ 136, 112, 136, 136, 136, 136, 112, 0,
+ 80, 0, 136, 136, 136, 136, 112, 0,
+ 80, 0, 144, 144, 144, 144, 104, 0,
+ 32, 64, 0, 112, 248, 128, 112, 0,
+ 112, 136, 8, 16, 32, 0, 32, 0,
+ 32, 80, 0, 192, 64, 64, 224, 0,
+ 112, 136, 136, 248, 136, 136, 136, 0,
+ 240, 72, 72, 112, 72, 72, 240, 0,
+ 48, 72, 128, 128, 128, 72, 48, 0,
+ 224, 80, 72, 72, 72, 80, 224, 0,
+ 248, 72, 64, 112, 64, 72, 248, 0,
+ 248, 72, 64, 112, 64, 64, 224, 0,
+ 48, 72, 128, 152, 136, 72, 56, 0,
+ 136, 136, 136, 248, 136, 136, 136, 0,
+ 248, 32, 32, 32, 32, 32, 248, 0,
+ 24, 8, 8, 8, 136, 136, 112, 0,
+ 200, 72, 80, 96, 80, 72, 200, 0,
+ 224, 64, 64, 64, 64, 72, 248, 0,
+ 136, 216, 168, 168, 136, 136, 136, 0,
+ 136, 200, 168, 152, 136, 136, 136, 0,
+ 112, 136, 136, 136, 136, 136, 112, 0,
+ 240, 72, 72, 112, 64, 64, 224, 0,
+ 112, 136, 136, 136, 136, 168, 112, 8,
+ 240, 72, 72, 112, 72, 72, 200, 0,
+ 112, 136, 128, 112, 8, 136, 112, 0,
+ 248, 168, 32, 32, 32, 32, 112, 0,
+ 136, 136, 136, 136, 136, 136, 120, 0,
+ 136, 136, 136, 80, 80, 32, 32, 0,
+ 136, 136, 136, 136, 168, 216, 136, 0,
+ 136, 136, 80, 32, 80, 136, 136, 0,
+ 136, 136, 136, 112, 32, 32, 112, 0,
+ 248, 136, 16, 32, 64, 136, 248, 0,
+ 0, 14, 8, 8, 8, 8, 14, 0,
+ 0, 128, 64, 32, 16, 8, 4, 0,
+ 0, 112, 16, 16, 16, 16, 112, 0,
+ 0, 48, 72, 64, 72, 48, 16, 48,
+ 0, 80, 0, 96, 32, 40, 48, 0,
+ 32, 16, 0, 152, 144, 144, 232, 0,
+ 0, 0, 112, 8, 120, 136, 120, 0,
+ 192, 64, 80, 104, 72, 72, 112, 0,
+ 0, 0, 112, 136, 128, 136, 112, 0,
+ 24, 16, 80, 176, 144, 144, 112, 0,
+ 0, 0, 112, 136, 248, 128, 112, 0,
+ 48, 72, 64, 224, 64, 64, 224, 0,
+ 0, 0, 104, 144, 144, 112, 136, 112,
+ 192, 64, 80, 104, 72, 72, 200, 0,
+ 64, 0, 192, 64, 64, 64, 224, 0,
+ 8, 0, 8, 8, 8, 8, 136, 112,
+ 192, 64, 72, 80, 96, 80, 200, 0,
+ 192, 64, 64, 64, 64, 64, 224, 0,
+ 0, 0, 144, 216, 168, 136, 136, 0,
+ 0, 0, 240, 136, 136, 136, 136, 0,
+ 0, 0, 112, 136, 136, 136, 112, 0,
+ 0, 0, 176, 72, 72, 112, 64, 224,
+ 0, 0, 104, 144, 144, 112, 16, 56,
+ 0, 0, 176, 72, 72, 64, 224, 0,
+ 0, 0, 120, 128, 112, 8, 240, 0,
+ 64, 64, 240, 64, 64, 72, 48, 0,
+ 0, 0, 144, 144, 144, 144, 104, 0,
+ 0, 0, 136, 136, 136, 80, 32, 0,
+ 0, 0, 136, 136, 168, 216, 144, 0,
+ 0, 0, 136, 80, 32, 80, 136, 0,
+ 0, 0, 136, 136, 136, 112, 32, 192,
+ 0, 0, 248, 144, 32, 72, 248, 0,
+ 32, 80, 0, 96, 144, 144, 96, 0,
+ 0, 14, 8, 48, 8, 8, 14, 0,
+ 0, 8, 8, 8, 8, 8, 8, 0,
+ 0, 112, 16, 12, 16, 16, 112, 0,
+ 0, 0, 0, 0, 0, 0, 248, 0,
+ 252, 252, 252, 252, 252, 252, 252, 252,
+ 240, 240, 240, 240, 240, 240, 240, 240,
+};
+
+static const byte hebrew_video_font[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 112, 112, 32, 32, 0, 32, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 144, 0, 96, 144, 144, 104, 0,
+ 0, 144, 0, 96, 144, 144, 96, 0,
+ 0, 144, 0, 144, 144, 144, 96, 0,
+ 0, 16, 40, 16, 42, 68, 58, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 4, 0,
+ 0, 32, 16, 16, 16, 16, 32, 0,
+ 0, 0, 20, 8, 62, 8, 20, 0,
+ 0, 112, 136, 240, 136, 136, 240, 0,
+ 0, 0, 0, 0, 0, 48, 48, 96,
+ 0, 0, 0, 240, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 48, 48, 0,
+ 16, 32, 0, 120, 112, 64, 56, 0,
+ 112, 136, 152, 168, 200, 136, 112, 0,
+ 32, 96, 32, 32, 32, 32, 112, 0,
+ 112, 136, 8, 48, 64, 136, 248, 0,
+ 112, 136, 8, 48, 8, 136, 112, 0,
+ 16, 48, 80, 144, 248, 16, 56, 0,
+ 248, 128, 240, 8, 8, 136, 112, 0,
+ 48, 64, 128, 240, 136, 136, 112, 0,
+ 248, 136, 8, 16, 32, 32, 32, 0,
+ 112, 136, 136, 112, 136, 136, 112, 0,
+ 112, 136, 136, 120, 8, 16, 96, 0,
+ 0, 0, 48, 48, 0, 48, 48, 0,
+ 5, 5, 4, 6, 5, 3, 4, 5,
+ 6, 3, 5, 5, 4, 6, 5, 3,
+ 4, 6, 5, 6, 6, 6, 5, 5,
+ 5, 6, 5, 6, 6, 6, 6, 6,
+ 112, 136, 8, 16, 32, 0, 32, 0,
+ 0, 0, 144, 80, 160, 144, 144, 0,
+ 0, 0, 224, 32, 32, 32, 240, 0,
+ 0, 0, 224, 32, 96, 160, 160, 0,
+ 0, 0, 248, 16, 16, 16, 16, 0,
+ 0, 0, 240, 16, 16, 144, 144, 0,
+ 0, 0, 192, 64, 64, 64, 64, 0,
+ 0, 0, 224, 64, 32, 64, 64, 0,
+ 0, 0, 240, 144, 144, 144, 144, 0,
+ 0, 0, 184, 168, 136, 136, 112, 0,
+ 0, 0, 192, 64, 0, 0, 0, 0,
+ 0, 0, 240, 16, 16, 16, 16, 16,
+ 0, 0, 224, 16, 16, 16, 224, 0,
+ 128, 128, 224, 32, 32, 32, 192, 0,
+ 0, 0, 248, 72, 72, 72, 120, 0,
+ 0, 0, 176, 208, 144, 144, 176, 0,
+ 0, 0, 192, 64, 64, 64, 64, 64,
+ 0, 0, 96, 32, 32, 32, 224, 0,
+ 0, 0, 248, 72, 72, 72, 48, 0,
+ 0, 0, 80, 80, 80, 80, 224, 0,
+ 0, 0, 248, 72, 104, 8, 8, 8,
+ 0, 0, 248, 72, 104, 8, 248, 0,
+ 0, 0, 216, 72, 48, 16, 16, 16,
+ 0, 0, 144, 80, 32, 16, 240, 0,
+ 0, 0, 240, 16, 144, 160, 128, 128,
+ 0, 0, 240, 16, 16, 16, 16, 0,
+ 0, 0, 168, 168, 200, 136, 112, 0,
+ 0, 0, 240, 80, 80, 80, 208, 0,
+ 0, 14, 8, 8, 8, 8, 14, 0,
+ 0, 128, 64, 32, 16, 8, 4, 0,
+ 0, 112, 16, 16, 16, 16, 112, 0,
+ 0, 48, 72, 64, 72, 48, 16, 48,
+ 0, 80, 0, 96, 32, 40, 48, 0,
+ 32, 16, 0, 152, 144, 144, 232, 0,
+ 0, 0, 112, 8, 120, 136, 120, 0,
+ 192, 64, 80, 104, 72, 72, 112, 0,
+ 0, 0, 112, 136, 128, 136, 112, 0,
+ 24, 16, 80, 176, 144, 144, 112, 0,
+ 0, 0, 112, 136, 248, 128, 112, 0,
+ 48, 72, 64, 224, 64, 64, 224, 0,
+ 0, 0, 104, 144, 144, 112, 136, 112,
+ 192, 64, 80, 104, 72, 72, 200, 0,
+ 64, 0, 192, 64, 64, 64, 224, 0,
+ 8, 0, 8, 8, 8, 8, 136, 112,
+ 192, 64, 72, 80, 96, 80, 200, 0,
+ 192, 64, 64, 64, 64, 64, 224, 0,
+ 0, 0, 144, 216, 168, 136, 136, 0,
+ 0, 0, 240, 136, 136, 136, 136, 0,
+ 0, 0, 112, 136, 136, 136, 112, 0,
+ 0, 0, 176, 72, 72, 112, 64, 224,
+ 0, 0, 104, 144, 144, 112, 16, 56,
+ 0, 0, 176, 72, 72, 64, 224, 0,
+ 0, 0, 120, 128, 112, 8, 240, 0,
+ 64, 64, 240, 64, 64, 72, 48, 0,
+ 0, 0, 144, 144, 144, 144, 104, 0,
+ 0, 0, 136, 136, 136, 80, 32, 0,
+ 0, 0, 136, 136, 168, 216, 144, 0,
+ 0, 0, 136, 80, 32, 80, 136, 0,
+ 0, 0, 136, 136, 136, 112, 32, 192,
+ 0, 0, 248, 144, 32, 72, 248, 0,
+ 32, 80, 0, 96, 144, 144, 96, 0,
+ 0, 14, 8, 48, 8, 8, 14, 0,
+ 0, 8, 8, 8, 8, 8, 8, 0,
+ 0, 112, 16, 12, 16, 16, 112, 0,
+ 0, 0, 0, 0, 0, 0, 248, 0,
+ 252, 252, 252, 252, 252, 252, 252, 252,
+ 240, 240, 240, 240, 240, 240, 240, 240,
+};
+
+static const byte italian_video_font[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 112, 112, 32, 32, 0, 32, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 80, 0, 112, 8, 120, 136, 120, 0,
+ 80, 0, 112, 136, 136, 136, 112, 0,
+ 32, 16, 0, 112, 136, 136, 112, 0,
+ 0, 16, 40, 16, 42, 68, 58, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 4, 0,
+ 0, 32, 16, 16, 16, 16, 32, 0,
+ 0, 0, 20, 8, 62, 8, 20, 0,
+ 32, 16, 0, 192, 64, 64, 224, 0,
+ 0, 0, 0, 0, 0, 48, 48, 96,
+ 0, 0, 0, 240, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 48, 48, 0,
+ 32, 64, 0, 112, 248, 128, 112, 0,
+ 112, 136, 152, 168, 200, 136, 112, 0,
+ 32, 96, 32, 32, 32, 32, 112, 0,
+ 112, 136, 8, 48, 64, 136, 248, 0,
+ 112, 136, 8, 48, 8, 136, 112, 0,
+ 16, 48, 80, 144, 248, 16, 56, 0,
+ 248, 128, 240, 8, 8, 136, 112, 0,
+ 48, 64, 128, 240, 136, 136, 112, 0,
+ 248, 136, 8, 16, 32, 32, 32, 0,
+ 112, 136, 136, 112, 136, 136, 112, 0,
+ 112, 136, 136, 120, 8, 16, 96, 0,
+ 0, 0, 48, 48, 0, 48, 48, 0,
+ 32, 16, 0, 112, 8, 248, 120, 0,
+ 32, 16, 0, 112, 136, 136, 112, 0,
+ 32, 16, 0, 112, 248, 128, 112, 0,
+ 32, 80, 0, 112, 248, 128, 112, 0,
+ 112, 136, 8, 16, 32, 0, 32, 0,
+ 32, 80, 0, 96, 32, 40, 48, 0,
+ 112, 136, 136, 248, 136, 136, 136, 0,
+ 240, 72, 72, 112, 72, 72, 240, 0,
+ 48, 72, 128, 128, 128, 72, 48, 0,
+ 224, 80, 72, 72, 72, 80, 224, 0,
+ 248, 72, 64, 112, 64, 72, 248, 0,
+ 248, 72, 64, 112, 64, 64, 224, 0,
+ 48, 72, 128, 152, 136, 72, 56, 0,
+ 136, 136, 136, 248, 136, 136, 136, 0,
+ 248, 32, 32, 32, 32, 32, 248, 0,
+ 24, 8, 8, 8, 136, 136, 112, 0,
+ 200, 72, 80, 96, 80, 72, 200, 0,
+ 224, 64, 64, 64, 64, 72, 248, 0,
+ 136, 216, 168, 168, 136, 136, 136, 0,
+ 136, 200, 168, 152, 136, 136, 136, 0,
+ 112, 136, 136, 136, 136, 136, 112, 0,
+ 240, 72, 72, 112, 64, 64, 224, 0,
+ 112, 136, 136, 136, 136, 168, 112, 8,
+ 240, 72, 72, 112, 72, 72, 200, 0,
+ 112, 136, 128, 112, 8, 136, 112, 0,
+ 248, 168, 32, 32, 32, 32, 112, 0,
+ 136, 136, 136, 136, 136, 136, 120, 0,
+ 136, 136, 136, 80, 80, 32, 32, 0,
+ 136, 136, 136, 136, 168, 216, 136, 0,
+ 136, 136, 80, 32, 80, 136, 136, 0,
+ 136, 136, 136, 112, 32, 32, 112, 0,
+ 248, 136, 16, 32, 64, 136, 248, 0,
+ 0, 14, 8, 8, 8, 8, 14, 0,
+ 0, 128, 64, 32, 16, 8, 4, 0,
+ 0, 112, 16, 16, 16, 16, 112, 0,
+ 0, 0, 112, 136, 128, 112, 32, 96,
+ 160, 0, 192, 64, 64, 64, 224, 0,
+ 32, 16, 0, 144, 144, 144, 104, 0,
+ 0, 0, 112, 8, 120, 136, 120, 0,
+ 192, 64, 80, 104, 72, 72, 112, 0,
+ 0, 0, 112, 136, 128, 136, 112, 0,
+ 24, 16, 80, 176, 144, 144, 112, 0,
+ 0, 0, 112, 136, 248, 128, 112, 0,
+ 48, 72, 64, 224, 64, 64, 224, 0,
+ 0, 0, 104, 144, 144, 112, 136, 112,
+ 192, 64, 80, 104, 72, 72, 200, 0,
+ 64, 0, 192, 64, 64, 64, 224, 0,
+ 8, 0, 8, 8, 8, 8, 136, 112,
+ 192, 64, 72, 80, 96, 80, 200, 0,
+ 192, 64, 64, 64, 64, 64, 224, 0,
+ 0, 0, 144, 216, 168, 136, 136, 0,
+ 0, 0, 240, 136, 136, 136, 136, 0,
+ 0, 0, 112, 136, 136, 136, 112, 0,
+ 0, 0, 176, 72, 72, 112, 64, 224,
+ 0, 0, 104, 144, 144, 112, 16, 56,
+ 0, 0, 176, 72, 72, 64, 224, 0,
+ 0, 0, 120, 128, 112, 8, 240, 0,
+ 64, 64, 240, 64, 64, 72, 48, 0,
+ 0, 0, 144, 144, 144, 144, 104, 0,
+ 0, 0, 136, 136, 136, 80, 32, 0,
+ 0, 0, 136, 136, 168, 216, 144, 0,
+ 0, 0, 136, 80, 32, 80, 136, 0,
+ 0, 0, 136, 136, 136, 112, 32, 192,
+ 0, 0, 248, 144, 32, 72, 248, 0,
+ 32, 80, 0, 96, 144, 144, 96, 0,
+ 0, 14, 8, 48, 8, 8, 14, 0,
+ 0, 8, 8, 8, 8, 8, 8, 0,
+ 0, 112, 16, 12, 16, 16, 112, 0,
+ 0, 0, 0, 0, 0, 0, 248, 0,
+ 252, 252, 252, 252, 252, 252, 252, 252,
+ 240, 240, 240, 240, 240, 240, 240, 240,
+};
+
+static const byte spanish_video_font[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 112, 112, 32, 32, 0, 32, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 80, 0, 112, 8, 120, 136, 120, 0,
+ 80, 0, 112, 136, 136, 136, 112, 0,
+ 80, 0, 144, 144, 144, 144, 104, 0,
+ 0, 16, 40, 16, 42, 68, 58, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 4, 0,
+ 0, 32, 16, 16, 16, 16, 32, 0,
+ 0, 0, 20, 8, 62, 8, 20, 0,
+ 96, 144, 144, 160, 144, 144, 160, 128,
+ 0, 0, 0, 0, 0, 48, 48, 96,
+ 0, 0, 0, 240, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 48, 48, 0,
+ 32, 64, 0, 112, 248, 128, 112, 0,
+ 112, 136, 152, 168, 200, 136, 112, 0,
+ 32, 96, 32, 32, 32, 32, 112, 0,
+ 112, 136, 8, 48, 64, 136, 248, 0,
+ 112, 136, 8, 48, 8, 136, 112, 0,
+ 16, 48, 80, 144, 248, 16, 56, 0,
+ 248, 128, 240, 8, 8, 136, 112, 0,
+ 48, 64, 128, 240, 136, 136, 112, 0,
+ 248, 136, 8, 16, 32, 32, 32, 0,
+ 112, 136, 136, 112, 136, 136, 112, 0,
+ 112, 136, 136, 120, 8, 16, 96, 0,
+ 0, 0, 48, 48, 0, 48, 48, 0,
+ 32, 64, 0, 112, 8, 248, 120, 0,
+ 32, 64, 0, 192, 64, 64, 224, 0,
+ 32, 64, 0, 112, 136, 136, 112, 0,
+ 32, 64, 0, 144, 144, 144, 104, 0,
+ 112, 136, 8, 16, 32, 0, 32, 0,
+ 80, 160, 0, 240, 136, 136, 136, 0,
+ 112, 136, 136, 248, 136, 136, 136, 0,
+ 240, 72, 72, 112, 72, 72, 240, 0,
+ 48, 72, 128, 128, 128, 72, 48, 0,
+ 224, 80, 72, 72, 72, 80, 224, 0,
+ 248, 72, 64, 112, 64, 72, 248, 0,
+ 248, 72, 64, 112, 64, 64, 224, 0,
+ 48, 72, 128, 152, 136, 72, 56, 0,
+ 136, 136, 136, 248, 136, 136, 136, 0,
+ 248, 32, 32, 32, 32, 32, 248, 0,
+ 24, 8, 8, 8, 136, 136, 112, 0,
+ 200, 72, 80, 96, 80, 72, 200, 0,
+ 224, 64, 64, 64, 64, 72, 248, 0,
+ 136, 216, 168, 168, 136, 136, 136, 0,
+ 136, 200, 168, 152, 136, 136, 136, 0,
+ 112, 136, 136, 136, 136, 136, 112, 0,
+ 240, 72, 72, 112, 64, 64, 224, 0,
+ 112, 136, 136, 136, 136, 168, 112, 8,
+ 240, 72, 72, 112, 72, 72, 200, 0,
+ 112, 136, 128, 112, 8, 136, 112, 0,
+ 248, 168, 32, 32, 32, 32, 112, 0,
+ 136, 136, 136, 136, 136, 136, 120, 0,
+ 136, 136, 136, 80, 80, 32, 32, 0,
+ 136, 136, 136, 136, 168, 216, 136, 0,
+ 136, 136, 80, 32, 80, 136, 136, 0,
+ 136, 136, 136, 112, 32, 32, 112, 0,
+ 248, 136, 16, 32, 64, 136, 248, 0,
+ 0, 14, 8, 8, 8, 8, 14, 0,
+ 0, 128, 64, 32, 16, 8, 4, 0,
+ 0, 112, 16, 16, 16, 16, 112, 0,
+ 32, 0, 32, 64, 128, 136, 112, 0,
+ 32, 0, 32, 32, 112, 112, 32, 0,
+ 80, 0, 144, 144, 144, 144, 104, 0,
+ 0, 0, 112, 8, 120, 136, 120, 0,
+ 192, 64, 80, 104, 72, 72, 112, 0,
+ 0, 0, 112, 136, 128, 136, 112, 0,
+ 24, 16, 80, 176, 144, 144, 112, 0,
+ 0, 0, 112, 136, 248, 128, 112, 0,
+ 48, 72, 64, 224, 64, 64, 224, 0,
+ 0, 0, 104, 144, 144, 112, 136, 112,
+ 192, 64, 80, 104, 72, 72, 200, 0,
+ 64, 0, 192, 64, 64, 64, 224, 0,
+ 8, 0, 8, 8, 8, 8, 136, 112,
+ 192, 64, 72, 80, 96, 80, 200, 0,
+ 192, 64, 64, 64, 64, 64, 224, 0,
+ 0, 0, 144, 216, 168, 136, 136, 0,
+ 0, 0, 240, 136, 136, 136, 136, 0,
+ 0, 0, 112, 136, 136, 136, 112, 0,
+ 0, 0, 176, 72, 72, 112, 64, 224,
+ 0, 0, 104, 144, 144, 112, 16, 56,
+ 0, 0, 176, 72, 72, 64, 224, 0,
+ 0, 0, 120, 128, 112, 8, 240, 0,
+ 64, 64, 240, 64, 64, 72, 48, 0,
+ 0, 0, 144, 144, 144, 144, 104, 0,
+ 0, 0, 136, 136, 136, 80, 32, 0,
+ 0, 0, 136, 136, 168, 216, 144, 0,
+ 0, 0, 136, 80, 32, 80, 136, 0,
+ 0, 0, 136, 136, 136, 112, 32, 192,
+ 0, 0, 248, 144, 32, 72, 248, 0,
+ 32, 80, 0, 96, 144, 144, 96, 0,
+ 0, 14, 8, 48, 8, 8, 14, 0,
+ 0, 8, 8, 8, 8, 8, 8, 0,
+ 0, 112, 16, 12, 16, 16, 112, 0,
+ 0, 0, 0, 0, 0, 0, 248, 0,
+ 252, 252, 252, 252, 252, 252, 252, 252,
+ 240, 240, 240, 240, 240, 240, 240, 240,
+};
+
+static const byte video_font[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 112, 112, 32, 32, 0, 32, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 144, 0, 96, 144, 144, 104, 0,
+ 0, 144, 0, 96, 144, 144, 96, 0,
+ 0, 144, 0, 144, 144, 144, 96, 0,
+ 0, 16, 40, 16, 42, 68, 58, 0,
+ 48, 48, 96, 0, 0, 0, 0, 0,
+ 0, 4, 8, 8, 8, 8, 4, 0,
+ 0, 32, 16, 16, 16, 16, 32, 0,
+ 0, 0, 20, 8, 62, 8, 20, 0,
+ 0, 112, 136, 240, 136, 136, 240, 0,
+ 0, 0, 0, 0, 0, 48, 48, 96,
+ 0, 0, 0, 240, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 48, 48, 0,
+ 16, 32, 0, 120, 112, 64, 56, 0,
+ 112, 136, 152, 168, 200, 136, 112, 0,
+ 32, 96, 32, 32, 32, 32, 112, 0,
+ 112, 136, 8, 48, 64, 136, 248, 0,
+ 112, 136, 8, 48, 8, 136, 112, 0,
+ 16, 48, 80, 144, 248, 16, 56, 0,
+ 248, 128, 240, 8, 8, 136, 112, 0,
+ 48, 64, 128, 240, 136, 136, 112, 0,
+ 248, 136, 8, 16, 32, 32, 32, 0,
+ 112, 136, 136, 112, 136, 136, 112, 0,
+ 112, 136, 136, 120, 8, 16, 96, 0,
+ 0, 0, 48, 48, 0, 48, 48, 0,
+ 32, 16, 0, 112, 8, 248, 120, 0,
+ 32, 80, 0, 144, 144, 144, 104, 0,
+ 80, 0, 144, 144, 144, 144, 104, 0,
+ 32, 80, 0, 112, 248, 128, 112, 0,
+ 112, 136, 8, 16, 32, 0, 32, 0,
+ 32, 80, 0, 192, 64, 64, 224, 0,
+ 112, 136, 136, 248, 136, 136, 136, 0,
+ 240, 72, 72, 112, 72, 72, 240, 0,
+ 48, 72, 128, 128, 128, 72, 48, 0,
+ 224, 80, 72, 72, 72, 80, 224, 0,
+ 248, 72, 64, 112, 64, 72, 248, 0,
+ 248, 72, 64, 112, 64, 64, 224, 0,
+ 48, 72, 128, 152, 136, 72, 56, 0,
+ 136, 136, 136, 248, 136, 136, 136, 0,
+ 248, 32, 32, 32, 32, 32, 248, 0,
+ 24, 8, 8, 8, 136, 136, 112, 0,
+ 200, 72, 80, 96, 80, 72, 200, 0,
+ 224, 64, 64, 64, 64, 72, 248, 0,
+ 136, 216, 168, 168, 136, 136, 136, 0,
+ 136, 200, 168, 152, 136, 136, 136, 0,
+ 112, 136, 136, 136, 136, 136, 112, 0,
+ 240, 72, 72, 112, 64, 64, 224, 0,
+ 112, 136, 136, 136, 136, 168, 112, 8,
+ 240, 72, 72, 112, 72, 72, 200, 0,
+ 112, 136, 128, 112, 8, 136, 112, 0,
+ 248, 168, 32, 32, 32, 32, 112, 0,
+ 136, 136, 136, 136, 136, 136, 120, 0,
+ 136, 136, 136, 80, 80, 32, 32, 0,
+ 136, 136, 136, 136, 168, 216, 136, 0,
+ 136, 136, 80, 32, 80, 136, 136, 0,
+ 136, 136, 136, 112, 32, 32, 112, 0,
+ 248, 136, 16, 32, 64, 136, 248, 0,
+ 0, 14, 8, 8, 8, 8, 14, 0,
+ 0, 128, 64, 32, 16, 8, 4, 0,
+ 0, 112, 16, 16, 16, 16, 112, 0,
+ 0, 48, 72, 64, 72, 48, 16, 48,
+ 0, 80, 0, 96, 32, 40, 48, 0,
+ 32, 16, 0, 152, 144, 144, 232, 0,
+ 0, 0, 112, 8, 120, 136, 120, 0,
+ 192, 64, 80, 104, 72, 72, 112, 0,
+ 0, 0, 112, 136, 128, 136, 112, 0,
+ 24, 16, 80, 176, 144, 144, 112, 0,
+ 0, 0, 112, 136, 248, 128, 112, 0,
+ 48, 72, 64, 224, 64, 64, 224, 0,
+ 0, 0, 104, 144, 144, 112, 136, 112,
+ 192, 64, 80, 104, 72, 72, 200, 0,
+ 64, 0, 192, 64, 64, 64, 224, 0,
+ 8, 0, 8, 8, 8, 8, 136, 112,
+ 192, 64, 72, 80, 96, 80, 200, 0,
+ 192, 64, 64, 64, 64, 64, 224, 0,
+ 0, 0, 144, 216, 168, 136, 136, 0,
+ 0, 0, 240, 136, 136, 136, 136, 0,
+ 0, 0, 112, 136, 136, 136, 112, 0,
+ 0, 0, 176, 72, 72, 112, 64, 224,
+ 0, 0, 104, 144, 144, 112, 16, 56,
+ 0, 0, 176, 72, 72, 64, 224, 0,
+ 0, 0, 120, 128, 112, 8, 240, 0,
+ 64, 64, 240, 64, 64, 72, 48, 0,
+ 0, 0, 144, 144, 144, 144, 104, 0,
+ 0, 0, 136, 136, 136, 80, 32, 0,
+ 0, 0, 136, 136, 168, 216, 144, 0,
+ 0, 0, 136, 80, 32, 80, 136, 0,
+ 0, 0, 136, 136, 136, 112, 32, 192,
+ 0, 0, 248, 144, 32, 72, 248, 0,
+ 32, 80, 0, 96, 144, 144, 96, 0,
+ 0, 14, 8, 48, 8, 8, 14, 0,
+ 0, 8, 8, 8, 8, 8, 8, 0,
+ 0, 112, 16, 12, 16, 16, 112, 0,
+ 0, 0, 0, 0, 0, 0, 248, 0,
+ 252, 252, 252, 252, 252, 252, 252, 252,
+ 240, 240, 240, 240, 240, 240, 240, 240,
+};
+#endif
+
+void SimonEngine::video_putchar_drawchar(FillOrCopyStruct *fcs, uint x, uint y, byte chr) {
+ const byte *src;
+ byte color, *dst;
+ uint h, i;
+
+ _lockWord |= 0x8000;
+
+ dst = dx_lock_2();
+ dst += y * _dxSurfacePitch + x * 8 + fcs->textColumnOffset;
+
+ switch(_language) {
+ case Common::RU_RUS:
+ src = russian_video_font + (chr - 0x20) * 8;
+ break;
+ case Common::PL_POL:
+ src = polish_video_font + (chr - 0x20) * 8;
+ break;
+ case Common::HB_ISR:
+ src = hebrew_video_font + (chr - 0x20) * 8;
+ break;
+ case Common::ES_ESP:
+ src = spanish_video_font + (chr - 0x20) * 8;
+ break;
+ case Common::IT_ITA:
+ src = italian_video_font + (chr - 0x20) * 8;
+ break;
+ case Common::FR_FRA:
+ src = french_video_font + (chr - 0x20) * 8;
+ break;
+ case Common::DE_DEU:
+ src = german_video_font + (chr - 0x20) * 8;
+ break;
+ case Common::EN_USA:
+ src = video_font + (chr - 0x20) * 8;
+ break;
+ default:
+ error("video_putchar_drawchar: Unknown language %d\n", _language);
+ }
+
+ color = fcs->text_color;
+
+ h = 8;
+ do {
+ int8 b = *src++;
+ i = 0;
+ do {
+ if (b < 0)
+ dst[i] = color;
+ b <<= 1;
+ } while (++i != 6);
+ dst += _dxSurfacePitch;
+ } while (--h);
+
+ dx_unlock_2();
+
+ _lockWord &= ~0x8000;
+}
+
+} // End of namespace Simon
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Simon_Charset)
+_GSETPTR(Simon::russian_video_font, GBVARS_RUSSIANVIDEOFONT_INDEX, byte, GBVARS_SIMON)
+//_GSETPTR(Simon::polish_video_font, GBVARS_POLISHVIDEOFONT_INDEX, byte, GBVARS_SIMON)
+_GSETPTR(Simon::french_video_font, GBVARS_FRENCHVIDEOFONT_INDEX, byte, GBVARS_SIMON)
+_GSETPTR(Simon::german_video_font, GBVARS_GERMANVIDEOFONT_INDEX, byte, GBVARS_SIMON)
+_GSETPTR(Simon::hebrew_video_font, GBVARS_HEBREWVIDEOFONT_INDEX, byte, GBVARS_SIMON)
+_GSETPTR(Simon::italian_video_font, GBVARS_ITALIANVIDEOFONT_INDEX, byte, GBVARS_SIMON)
+_GSETPTR(Simon::spanish_video_font, GBVARS_SPANISHVIDEOFONT_INDEX, byte, GBVARS_SIMON)
+_GSETPTR(Simon::video_font, GBVARS_VIDEOFONT_INDEX, byte, GBVARS_SIMON)
+_GEND
+
+_GRELEASE(Simon_Charset)
+_GRELEASEPTR(GBVARS_RUSSIANVIDEOFONT_INDEX, GBVARS_SIMON)
+//_GRELEASEPTR(GBVARS_POLISHVIDEOFONT_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_FRENCHVIDEOFONT_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_GERMANVIDEOFONT_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_HEBREWVIDEOFONT_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_ITALIANVIDEOFONT_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_SPANISHVIDEOFONT_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_VIDEOFONT_INDEX, GBVARS_SIMON)
+_GEND
+
+#endif
diff --git a/engines/simon/cursor.cpp b/engines/simon/cursor.cpp
new file mode 100644
index 0000000000..248039d05d
--- /dev/null
+++ b/engines/simon/cursor.cpp
@@ -0,0 +1,246 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "simon/simon.h"
+#include "simon/intern.h"
+#include "common/system.h"
+
+namespace Simon {
+
+#ifdef PALMOS_68K
+static const byte *_simon1_cursor;
+#else
+static const byte _simon1_cursor[256] = {
+ 0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xe1,0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xe1,0xe1,0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xe1,0xe1,0xe1,0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xe1,0xe1,0xe1,0xe1,0xe0,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xe1,0xff,0xff,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+#endif
+static const byte _simon2_cursors[10][256] = {
+ // cross hair
+ { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xec,0xec,0xec,0xec,0xec,0xef,0xff,0xea,0xff,0xef,0xec,0xec,0xec,0xec,0xec,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ // examine
+ { 0xff,0xff,0xef,0xef,0xef,0xef,0xef,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xef,0xee,0xeb,0xe4,0xe4,0xe4,0xee,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xef,0xee,0xeb,0xee,0xef,0xef,0xee,0xec,0xee,0xef,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xef,0xeb,0xee,0xef,0xee,0xee,0xef,0xee,0xe4,0xef,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xef,0xeb,0xef,0xef,0xef,0xec,0xee,0xef,0xe4,0xef,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xef,0xeb,0xef,0xef,0xee,0xef,0xef,0xef,0xe4,0xef,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xef,0xeb,0xee,0xef,0xef,0xef,0xef,0xee,0xe4,0xef,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xef,0xee,0xeb,0xee,0xef,0xef,0xee,0xe4,0xee,0xef,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xef,0xee,0xeb,0xeb,0xeb,0xeb,0xee,0xe4,0xec,0xef,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xef,0xef,0xef,0xef,0xef,0xef,0xeb,0xe4,0xee,0xef,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xee,0xe4,0xeb,0xef,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xeb,0xe4,0xeb,0xef,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xeb,0xec,0xeb,0xef,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xeb,0xe4,0xef,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xef,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ // pick up
+ { 0xff,0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe5,0xe6,0xe6,0xe7,0xe7,0xe6,0xe6,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe5,0xe7,0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xe8,0xe5,0xff,0xff,0xff,
+ 0xff,0xff,0xe5,0xe6,0xe7,0xe7,0xe7,0xe7,0xe7,0xe7,0xe8,0xe9,0xe7,0xe5,0xff,0xff,
+ 0xff,0xe5,0xe6,0xe7,0xe6,0xe5,0xff,0xff,0xff,0xff,0xe5,0xe6,0xe8,0xe6,0xe5,0xff,
+ 0xff,0xe5,0xe7,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe8,0xe7,0xe5,0xff,
+ 0xff,0xe5,0xe7,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe7,0xe7,0xe5,0xff,
+ 0xff,0xef,0xeb,0xeb,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xeb,0xeb,0xef,0xff,
+ 0xff,0xef,0xee,0xeb,0xee,0xef,0xff,0xff,0xff,0xff,0xef,0xee,0xeb,0xee,0xef,0xff,
+ 0xff,0xff,0xef,0xeb,0xeb,0xef,0xff,0xff,0xff,0xff,0xef,0xeb,0xeb,0xef,0xff,0xff,
+ 0xff,0xff,0xef,0xee,0xe4,0xee,0xef,0xff,0xff,0xef,0xee,0xe4,0xee,0xef,0xff,0xff,
+ 0xff,0xff,0xff,0xef,0xe4,0xeb,0xef,0xff,0xff,0xef,0xeb,0xe4,0xef,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xeb,0xeb,0xeb,0xef,0xef,0xeb,0xeb,0xeb,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xef,0xee,0xee,0xee,0xee,0xe1,0xe1,0xef,0xff,0xff,0xff,0xe4,
+ 0xef,0xee,0xeb,0xeb,0xeb,0xeb,0xeb,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xeb,0xec,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe4 },
+ // give
+ { 0xff,0xff,0xff,0xff,0xff,0xe5,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe5,0xe7,0xe8,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe9,0xe7,0xe8,0xe8,0xe8,0xe7,0xe9,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xe5,0xe7,0xea,0xe8,0xe8,0xe8,0xea,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xe5,0xe7,0xe8,0xe8,0xea,0xe9,0xea,0xe8,0xe8,0xe7,0xe5,0xff,0xff,0xff,0xff,
+ 0xe5,0xe7,0xe9,0xe8,0xe8,0xe9,0xec,0xe9,0xe8,0xe8,0xe8,0xe7,0xe5,0xff,0xff,0xff,
+ 0xe5,0xe7,0xe7,0xe9,0xe8,0xec,0xe9,0xec,0xe8,0xe9,0xe7,0xe6,0xe5,0xff,0xff,0xff,
+ 0xe5,0xe7,0xe7,0xe8,0xec,0xe9,0xe9,0xe9,0xec,0xe7,0xe6,0xe6,0xe5,0xff,0xff,0xff,
+ 0xe5,0xe7,0xe7,0xea,0xe8,0xe9,0xe9,0xe9,0xe7,0xec,0xec,0xe4,0xe5,0xff,0xff,0xff,
+ 0xe5,0xe7,0xe7,0xe9,0xe7,0xe8,0xe9,0xe7,0xe6,0xec,0xe4,0xec,0xe4,0xef,0xff,0xff,
+ 0xe5,0xe6,0xe7,0xe9,0xe7,0xe7,0xe8,0xe6,0xe6,0xe4,0xec,0xe4,0xec,0xe4,0xef,0xff,
+ 0xff,0xe5,0xe6,0xe9,0xe7,0xe7,0xe8,0xe6,0xe6,0xe8,0xe4,0xec,0xe4,0xec,0xeb,0xff,
+ 0xff,0xff,0xe5,0xe9,0xe7,0xe7,0xe8,0xe6,0xe6,0xe8,0xe6,0xe4,0xec,0xeb,0xef,0xff,
+ 0xff,0xff,0xff,0xe8,0xe7,0xe7,0xe8,0xe6,0xe6,0xe7,0xff,0xef,0xeb,0xef,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe5,0xe7,0xe8,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe5,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ // talk
+ { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xe5,0xe7,0xe8,0xe8,0xe8,0xe7,0xe6,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xe5,0xe6,0xe9,0xea,0xe6,0xea,0xe9,0xe8,0xe9,0xe8,0xe7,0xe5,0xff,0xff,0xff,
+ 0xff,0xe5,0xe7,0xe5,0xef,0xe5,0xec,0xea,0xe5,0xea,0xec,0xe5,0xe9,0xe6,0xff,0xff,
+ 0xff,0xe5,0xe6,0xe5,0xef,0xef,0xef,0xe5,0xef,0xef,0xe5,0xef,0xef,0xe8,0xe5,0xff,
+ 0xff,0xe5,0xe9,0xea,0xe5,0xe8,0xe7,0xe6,0xe6,0xe8,0xe7,0xe5,0xec,0xe9,0xe5,0xff,
+ 0xff,0xe5,0xe9,0xe8,0xe5,0xe7,0xe8,0xe8,0xe9,0xe9,0xe8,0xe5,0xe9,0xe9,0xe5,0xff,
+ 0xff,0xe5,0xe6,0xec,0xea,0xe5,0xe6,0xe6,0xe7,0xe7,0xe6,0xe5,0xec,0xe8,0xe5,0xff,
+ 0xff,0xff,0xe5,0xe9,0xe8,0xe9,0xe5,0xe8,0xe5,0xe8,0xe5,0xe9,0xe9,0xe7,0xe5,0xff,
+ 0xff,0xff,0xe5,0xe7,0xe9,0xec,0xe8,0xec,0xe8,0xec,0xe8,0xec,0xe8,0xe5,0xff,0xff,
+ 0xff,0xff,0xff,0xe5,0xe6,0xe8,0xe9,0xe9,0xe9,0xe9,0xe9,0xe8,0xe5,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ // use
+ { 0xff,0xff,0xff,0xff,0xff,0xee,0xe1,0xeb,0xee,0xef,0xef,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xef,0xef,0xef,0xe4,0xeb,0xee,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xeb,0xe4,0xe4,0xeb,0xe5,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xeb,0xe4,0xec,0xe4,0xef,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xeb,0xeb,0xe4,0xe4,0xee,0xef,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xee,0xeb,0xeb,0xeb,0xe1,0xef,0xee,0xef,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe6,0xef,0xef,0xee,0xeb,0xeb,0xe4,0xee,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe6,0xff,0xff,0xff,0xef,0xeb,0xec,0xeb,0xef,
+ 0xff,0xff,0xff,0xff,0xff,0xe5,0xe6,0xe5,0xff,0xff,0xff,0xee,0xe4,0xeb,0xef,0xff,
+ 0xff,0xff,0xff,0xe5,0xe5,0xe6,0xe5,0xff,0xff,0xff,0xff,0xef,0xee,0xef,0xff,0xff,
+ 0xff,0xff,0xe5,0xe6,0xe8,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xff,0xff,0xff,
+ 0xff,0xe5,0xe6,0xe8,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xe5,0xe6,0xe8,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xe5,0xe6,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ // wear
+ { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xef,0xeb,0xed,0xe4,0xe2,0xeb,0xee,0xee,0xee,0xef,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xef,0xe2,0xec,0xe2,0xe1,0xee,0xef,0xef,0xee,0xef,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xef,0xeb,0xed,0xeb,0xee,0xef,0xef,0xef,0xee,0xef,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xef,0xee,0xe4,0xeb,0xee,0xef,0xef,0xee,0xef,0xef,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xef,0xe4,0xeb,0xee,0xef,0xef,0xee,0xef,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xef,0xe2,0xeb,0xee,0xef,0xef,0xee,0xef,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xef,0xeb,0xe1,0xee,0xef,0xef,0xee,0xef,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xef,0xeb,0xe1,0xee,0xef,0xef,0xef,0xef,0xff,0xff,0xff,0xff,
+ 0xff,0xef,0xef,0xef,0xe1,0xe4,0xe4,0xe4,0xe1,0xeb,0xee,0xef,0xef,0xef,0xff,0xff,
+ 0xef,0xee,0xee,0xef,0xee,0xee,0xee,0xee,0xee,0xef,0xef,0xef,0xee,0xee,0xef,0xff,
+ 0xff,0xef,0xef,0xee,0xe1,0xe2,0xe4,0xe4,0xe4,0xeb,0xe1,0xee,0xef,0xef,0xff,0xff,
+ 0xff,0xff,0xff,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ // move
+ { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xff,
+ 0xff,0xe1,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe1,0xff,
+ 0xff,0xe1,0xe3,0xe3,0xe3,0xed,0xe3,0xe3,0xe3,0xe3,0xed,0xe3,0xe3,0xe3,0xe1,0xff,
+ 0xff,0xe1,0xe3,0xe3,0xed,0xec,0xe3,0xe3,0xe3,0xe3,0xec,0xed,0xe3,0xe3,0xe1,0xff,
+ 0xff,0xe1,0xe3,0xed,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xed,0xe3,0xe1,0xff,
+ 0xff,0xe1,0xed,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xed,0xe1,0xff,
+ 0xff,0xe1,0xe3,0xed,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xed,0xe3,0xe1,0xff,
+ 0xff,0xe1,0xe3,0xe3,0xed,0xec,0xe3,0xe3,0xe3,0xe3,0xec,0xed,0xe3,0xe3,0xe1,0xff,
+ 0xff,0xe1,0xe3,0xe3,0xe3,0xed,0xe3,0xe3,0xe3,0xe3,0xed,0xe3,0xe3,0xe3,0xe1,0xff,
+ 0xff,0xe1,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe1,0xff,
+ 0xff,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+ // open
+ { 0xff,0xff,0xe5,0xe8,0xe8,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xe5,0xe8,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xe5,0xe7,0xe5,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe5,0xff,0xe5,0xe7,0xe6,0xe9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xe6,0xea,0xe6,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe6,0xea,0xe6,0xe7,0xe5,0xff,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe6,0xea,0xe6,0xff,0xe5,0xe7,0xe5,0xe7,0xe5,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe6,0xea,0xe6,0xff,0xff,0xff,0xe5,0xe7,0xe8,0xe5,0xff,0xff,0xff,
+ 0xff,0xe5,0xe6,0xea,0xe6,0xff,0xff,0xff,0xe5,0xe7,0xe8,0xe8,0xe5,0xff,0xff,0xff,
+ 0xff,0xe5,0xe9,0xea,0xea,0xea,0xea,0xea,0xea,0xea,0xea,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xe5,0xe9,0xe7,0xe7,0xe7,0xe7,0xe7,0xe7,0xe7,0xea,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xe5,0xe9,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe9,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xe5,0xe9,0xe8,0xe8,0xe8,0xe8,0xe8,0xe7,0xe7,0xe9,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xe5,0xe9,0xe6,0xe6,0xe6,0xe6,0xe6,0xe6,0xe5,0xe9,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xe5,0xe9,0xe8,0xe8,0xe8,0xe8,0xe8,0xe8,0xe7,0xe9,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xe5,0xe9,0xe9,0xe9,0xe9,0xe9,0xe9,0xe9,0xe9,0xe9,0xe5,0xff,0xff,0xff,0xff },
+ // question mark
+ { 0xff,0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe5,0xe7,0xea,0xec,0xec,0xec,0xe9,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe5,0xe7,0xea,0xec,0xea,0xe9,0xea,0xec,0xe9,0xe5,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe5,0xe9,0xec,0xe9,0xe8,0xe7,0xe8,0xea,0xec,0xe5,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe5,0xe8,0xe9,0xe8,0xe5,0xe5,0xe8,0xe9,0xec,0xe5,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe8,0xe9,0xec,0xe9,0xe5,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe9,0xec,0xec,0xe9,0xe5,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe5,0xe8,0xec,0xea,0xe8,0xe5,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe5,0xe9,0xec,0xe9,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe5,0xe9,0xea,0xe9,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe5,0xe7,0xe9,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe5,0xe8,0xe9,0xe8,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe5,0xe9,0xec,0xe9,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xe5,0xe8,0xe9,0xe8,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
+};
+
+void SimonEngine::drawMousePointer() {
+ if (getGameType() == GType_SIMON2)
+ _system->setMouseCursor(_simon2_cursors[_mouseCursor], 16, 16, 7, 7);
+ else
+ _system->setMouseCursor(_simon1_cursor, 16, 16, 0, 0);
+}
+
+} // End of namespace Simon
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Simon_Cursor)
+_GSETPTR(Simon::_simon1_cursor, GBVARS_SIMON1CURSOR_INDEX, byte, GBVARS_SIMON)
+_GEND
+
+_GRELEASE(Simon_Cursor)
+_GRELEASEPTR(GBVARS_SIMON1CURSOR_INDEX, GBVARS_SIMON)
+_GEND
+
+#endif
diff --git a/engines/simon/debug.cpp b/engines/simon/debug.cpp
new file mode 100644
index 0000000000..39e462782f
--- /dev/null
+++ b/engines/simon/debug.cpp
@@ -0,0 +1,462 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Simon debug functions
+#include "common/stdafx.h"
+#include "simon/simon.h"
+#include "simon/intern.h"
+#include "simon/debug.h"
+#include "simon/vga.h"
+
+#include <sys/stat.h>
+
+namespace Simon {
+
+const byte *SimonEngine::dumpOpcode(const byte *p) {
+ byte opcode;
+ const char *s, *st;
+
+ opcode = *p++;
+ if (opcode == 255)
+ return NULL;
+ if (getGameType() == GType_FF) {
+ st = s = feeblefiles_opcode_name_table[opcode];
+ } else if (getGameType() == GType_SIMON2 && getFeatures() & GF_TALKIE) {
+ st = s = simon2talkie_opcode_name_table[opcode];
+ } else if (getFeatures() & GF_TALKIE) {
+ st = s = simon1talkie_opcode_name_table[opcode];
+ } else if (getGameType() == GType_SIMON2) {
+ st = s = simon2dos_opcode_name_table[opcode];
+ } else {
+ st = s = simon1dos_opcode_name_table[opcode];
+ }
+ if (s == NULL) {
+ //error("INVALID OPCODE %d", opcode);
+ return NULL;
+ }
+ while (*st != '|')
+ st++;
+ fprintf(_dumpFile, "%s ", st + 1);
+
+ for (;;) {
+ switch (*s++) {
+ case 'x':
+ fprintf(_dumpFile, "\n");
+ return NULL;
+ case '|':
+ fprintf(_dumpFile, "\n");
+ return p;
+ case 'B':{
+ byte b = *p++;
+ if (b == 255)
+ fprintf(_dumpFile, "[%d] ", *p++);
+ else
+ fprintf(_dumpFile, "%d ", b);
+ break;
+ }
+ case 'V':{
+ byte b = *p++;
+ if (b == 255)
+ fprintf(_dumpFile, "[[%d]] ", *p++);
+ else
+ fprintf(_dumpFile, "[%d] ", b);
+ break;
+ }
+
+ case 'W':{
+ int n = (int16)((p[0] << 8) | p[1]);
+ p += 2;
+ if (n >= 30000 && n < 30512)
+ fprintf(_dumpFile, "[%d] ", n - 30000);
+ else
+ fprintf(_dumpFile, "%d ", n);
+ break;
+ }
+
+ case 'w':{
+ int n = (int16)((p[0] << 8) | p[1]);
+ p += 2;
+ fprintf(_dumpFile, "%d ", n);
+ break;
+ }
+
+ case 'I':{
+ int n = (int16)((p[0] << 8) | p[1]);;
+ p += 2;
+ if (n == -1)
+ fprintf(_dumpFile, "ITEM_M1 ");
+ else if (n == -3)
+ fprintf(_dumpFile, "ITEM_M3 ");
+ else if (n == -5)
+ fprintf(_dumpFile, "ITEM_1 ");
+ else if (n == -7)
+ fprintf(_dumpFile, "ITEM_0 ");
+ else if (n == -9)
+ fprintf(_dumpFile, "ITEM_A_PARENT ");
+ else
+ fprintf(_dumpFile, "<%d> ", n);
+ break;
+ }
+
+ case 'J':{
+ fprintf(_dumpFile, "-> ");
+ }
+ break;
+
+ case 'T':{
+ uint n = ((p[0] << 8) | p[1]);
+ p += 2;
+ if (n != 0xFFFF)
+ fprintf(_dumpFile, "\"%s\"(%d) ", getStringPtrByID(n), n);
+ else
+ fprintf(_dumpFile, "NULL_STRING ");
+ }
+ break;
+ }
+ }
+}
+
+void SimonEngine::dumpSubroutineLine(SubroutineLine *sl, Subroutine *sub) {
+ const byte *p;
+
+ printf("; ****\n");
+
+ p = (byte *)sl + SUBROUTINE_LINE_SMALL_SIZE;
+ if (sub->id == 0) {
+ fprintf(_dumpFile, "; verb=%d, noun1=%d, noun2=%d\n", sl->verb, sl->noun1, sl->noun2);
+ p = (byte *)sl + SUBROUTINE_LINE_BIG_SIZE;
+ }
+
+ for (;;) {
+ p = dumpOpcode(p);
+ if (p == NULL)
+ break;
+ }
+}
+
+void SimonEngine::dumpSubroutine(Subroutine *sub) {
+ SubroutineLine *sl;
+
+ fprintf(_dumpFile, "\n******************************************\n;Subroutine, ID=%d:\nSUB_%d:\n", sub->id, sub->id);
+ sl = (SubroutineLine *)((byte *)sub + sub->first);
+ for (; (byte *)sl != (byte *)sub; sl = (SubroutineLine *)((byte *)sub + sl->next)) {
+ dumpSubroutineLine(sl, sub);
+ }
+ fprintf(_dumpFile, "\nEND ******************************************\n");
+ fflush(_dumpFile);
+}
+
+void SimonEngine::dumpSubroutines() {
+ Subroutine *sub = _subroutineList;
+ for (; sub; sub = sub->next) {
+ dumpSubroutine(sub);
+ }
+}
+
+void SimonEngine::dump_video_script(const byte *src, bool one_opcode_only) {
+ uint opcode;
+ const char *str, *strn;
+
+ do {
+ if (getGameType() == GType_SIMON1) {
+ opcode = READ_BE_UINT16(src);
+ src += 2;
+ } else {
+ opcode = *src++;
+ }
+
+ if (opcode >= NUM_VIDEO_OP_CODES) {
+ error("Invalid opcode %x\n", opcode);
+ return;
+ }
+
+ if (getGameType() == GType_FF) {
+ strn = str = feeblefiles_video_opcode_name_table[opcode];
+ } else if (getGameType() == GType_SIMON2) {
+ strn = str = simon2_video_opcode_name_table[opcode];
+ } else {
+ strn = str = simon1_video_opcode_name_table[opcode];
+ }
+
+ while (*strn != '|')
+ strn++;
+ fprintf(_dumpFile, "%.2d: %s ", opcode, strn + 1);
+
+ int end = (getGameType() == GType_FF) ? 9999 : 999;
+ for (; *str != '|'; str++) {
+ switch (*str) {
+ case 'x':
+ fprintf(_dumpFile, "\n");
+ return;
+ case 'b':
+ fprintf(_dumpFile, "%d ", *src++);
+ break;
+ case 'd': {
+ int16 tmp = (int16)readUint16Wrapper(src);
+ if (tmp < 0) tmp = vcReadVar(-tmp);
+ fprintf(_dumpFile, "%d ", tmp);
+ src += 2;
+ break;
+ }
+ case 'v':
+ fprintf(_dumpFile, "[%d] ", readUint16Wrapper(src));
+ src += 2;
+ break;
+ case 'i':
+ fprintf(_dumpFile, "%d ", (int16)readUint16Wrapper(src));
+ src += 2;
+ break;
+ case 'q':
+ while (readUint16Wrapper(src) != end) {
+ fprintf(_dumpFile, "(%d,%d) ", readUint16Wrapper(src),
+ readUint16Wrapper(src + 2));
+ src += 4;
+ }
+ src++;
+ break;
+ default:
+ error("Invalid fmt string '%c' in decompile VGA", *str);
+ }
+ }
+
+ fprintf(_dumpFile, "\n");
+ } while (!one_opcode_only);
+}
+
+void SimonEngine::dump_vga_file(const byte *vga) {
+ {
+ const byte *pp;
+ const byte *p;
+ int count;
+
+ pp = vga;
+ p = pp + READ_BE_UINT16(&((const VgaFileHeader_Simon *) pp)->hdr2_start);
+ count = READ_BE_UINT16(&((const VgaFileHeader2_Simon *) p)->animationCount);
+ p = pp + READ_BE_UINT16(&((const VgaFileHeader2_Simon *) p)->animationTable);
+ while (--count >= 0) {
+ int id = READ_BE_UINT16(&((const AnimationHeader_Simon *) p)->id);
+
+ dump_vga_script_always(vga + READ_BE_UINT16(&((const AnimationHeader_Simon *) p)->scriptOffs), id / 100, id);
+ p += sizeof(AnimationHeader_Simon);
+ }
+ }
+
+ {
+ const byte *bb, *b;
+ int c;
+
+ bb = vga;
+ b = bb + READ_BE_UINT16(&((const VgaFileHeader_Simon *) bb)->hdr2_start);
+ c = READ_BE_UINT16(&((const VgaFileHeader2_Simon *) b)->imageCount);
+ b = bb + READ_BE_UINT16(&((const VgaFileHeader2_Simon *) b)->imageTable);
+
+ while (--c >= 0) {
+ int id = READ_BE_UINT16(&((const ImageHeader_Simon *) b)->id);
+
+ dump_vga_script_always(vga + READ_BE_UINT16(&((const ImageHeader_Simon *) b)->scriptOffs), id / 100, id);
+ b += sizeof(ImageHeader_Simon);
+ }
+ }
+}
+
+static const byte bmp_hdr[] = {
+ 0x42, 0x4D,
+ 0x9E, 0x14, 0x00, 0x00, /* offset 2, file size */
+ 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x04, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00,
+
+ 0x3C, 0x00, 0x00, 0x00, /* image width */
+ 0x46, 0x00, 0x00, 0x00, /* image height */
+ 0x01, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+};
+
+void dump_bmp(const char *filename, int w, int h, const byte *bytes, const uint32 *palette) {
+ FILE *out = fopen(filename, "wb");
+ byte my_hdr[sizeof(bmp_hdr)];
+ int i;
+
+ if (out == NULL) {
+ printf("DUMP ERROR\n");
+ return;
+ }
+
+ memcpy(my_hdr, bmp_hdr, sizeof(bmp_hdr));
+
+ *(uint32 *)(my_hdr + 2) = w * h + 1024 + sizeof(bmp_hdr);
+ *(uint32 *)(my_hdr + 18) = w;
+ *(uint32 *)(my_hdr + 22) = h;
+
+
+ fwrite(my_hdr, 1, sizeof(my_hdr), out);
+
+ for (i = 0; i != 256; i++, palette++) {
+ byte color[4];
+ color[0] = (byte)(*palette >> 16);
+ color[1] = (byte)(*palette >> 8);
+ color[2] = (byte)(*palette);
+ color[3] = 0;
+ fwrite(color, 1, 4, out);
+ }
+
+ while (--h >= 0) {
+ fwrite(bytes + h * ((w + 3) & ~3), ((w + 3) & ~3), 1, out);
+ }
+
+ fclose(out);
+}
+
+static void dump_bitmap(const char *filename, const byte *offs, int w, int h, int flags, const byte *palette,
+ byte base)
+{
+ /* allocate */
+ byte *b = (byte *)malloc(w * h);
+ int i, j;
+
+ VC10_state state;
+
+ state.depack_cont = -0x80;
+ state.depack_src = offs;
+ state.dh = h;
+ state.y_skip = 0;
+
+ for (i = 0; i != w; i += 2) {
+ byte *c = vc10_depack_column(&state);
+ for (j = 0; j != h; j++) {
+ byte pix = c[j];
+ b[j * w + i] = (pix >> 4) | base;
+ b[j * w + i + 1] = (pix & 0xF) | base;
+
+ }
+ }
+
+ dump_bmp(filename, w, h, b, (const uint32 *)palette);
+ free(b);
+}
+
+void SimonEngine::dump_single_bitmap(int file, int image, const byte *offs, int w, int h, byte base) {
+ char buf[40];
+#if !defined(PALMOS_MODE) && !defined(__DC__) && !defined(__PSP__) && !defined(__PLAYSTATION2__)
+ struct stat statbuf;
+#endif
+
+#if defined(MACOS_CARBON)
+ sprintf(buf, ":dumps:File%d_Image%d.bmp", file, image);
+#else
+ sprintf(buf, "dumps/File%d_Image%d.bmp", file, image);
+#endif
+
+#if !defined(PALMOS_MODE) && !defined(__DC__) && !defined(__PSP__) && !defined(__PLAYSTATION2__)
+ if (stat(buf, &statbuf) == 0)
+ return;
+#endif
+
+ dump_bitmap(buf, offs, w, h, 0, _palette, base);
+}
+
+void pal_load(byte *pal, const byte *vga1, int a, int b) {
+ uint num = (a == 0) ? 0x20 : 0x10;
+ byte *palptr;
+ const byte *src;
+
+ palptr = (byte *)&pal[a << 4];
+ src = vga1 + 6 + b * 96;
+
+ do {
+ palptr[0] = src[0] << 2;
+ palptr[1] = src[1] << 2;
+ palptr[2] = src[2] << 2;
+ palptr[3] = 0;
+
+ palptr += 4;
+ src += 3;
+ } while (--num);
+}
+
+void SimonEngine::dump_vga_bitmaps(const byte *vga, byte *vga1, int res) {
+
+ int i;
+ uint32 offs;
+ const byte *p2;
+ byte pal[768];
+
+ {
+ memset(pal, 0, sizeof(pal));
+ pal_load(pal, vga1, 2, 0);
+ pal_load(pal, vga1, 3, 1);
+ pal_load(pal, vga1, 4, 2);
+ pal_load(pal, vga1, 5, 3);
+ }
+
+ int width, height, flags;
+
+ i = 538;
+
+ for (i = 1; ; i++) {
+ p2 = vga + i * 8;
+ offs = READ_BE_UINT32(p2);
+
+ /* try to detect end of images.
+ * assume the end when offset >= 200kb */
+ if (offs >= 200*1024)
+ return;
+
+ width = READ_BE_UINT16(p2 + 6);
+ height = p2[5];
+ flags = p2[4];
+
+ fprintf(_dumpFile, "Image %d. Width=%d, Height=%d, Flags=0x%X\n", i, width, height, flags);
+ fflush(_dumpFile);
+
+ /* dump bitmap */
+ {
+ char buf[40];
+#if defined(MACOS_CARBON)
+ sprintf(buf, ":dumps:Res%d_Image%d.bmp", res, i);
+#else
+ sprintf(buf, "dumps/Res%d_Image%d.bmp", res, i);
+#endif
+
+ dump_bitmap(buf, vga + offs, width, height, flags, pal, 0);
+ }
+ }
+}
+
+void SimonEngine::dump_vga_script_always(const byte *ptr, uint res, uint sprite_id) {
+ fprintf(_dumpFile, "; address=%x, vgafile=%d vgasprite=%d\n",
+ ptr - _vgaBufferPointers[res].vgaFile1, res, sprite_id);
+ dump_video_script(ptr, false);
+ fprintf(_dumpFile, "; end\n");
+}
+
+void SimonEngine::dump_vga_script(const byte *ptr, uint res, uint sprite_id) {
+ dump_vga_script_always(ptr, res, sprite_id);
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/debug.h b/engines/simon/debug.h
new file mode 100644
index 0000000000..5b7bf45eeb
--- /dev/null
+++ b/engines/simon/debug.h
@@ -0,0 +1,1530 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SIMON_DEBUG_H
+#define SIMON_DEBUG_H
+
+namespace Simon {
+
+static const char *const simon1dos_opcode_name_table[256] = {
+ /* 0 */
+ "|INV_COND",
+ "IJ|PTRA_PARENT_IS",
+ "IJ|PTRA_PARENT_ISNOT",
+ NULL,
+ /* 4 */
+ NULL,
+ "IJ|PARENT_IS_1",
+ "IJ|PARENT_ISNOT_1",
+ "IIJ|PARENT_IS",
+ /* 8 */
+ NULL,
+ NULL,
+ NULL,
+ "VJ|IS_ZERO",
+ /* 12 */
+ "VJ|ISNOT_ZERO",
+ "VWJ|IS_EQ",
+ "VWJ|IS_NEQ",
+ "VWJ|IS_LE",
+ /* 16 */
+ "VWJ|IS_GE",
+ "VVJ|IS_EQF",
+ "VVJ|IS_NEQF",
+ "VVJ|IS_LEF",
+ /* 20 */
+ "VVJ|IS_GEF",
+ NULL,
+ NULL,
+ "WJ|CHANCE",
+ /* 24 */
+ NULL,
+ "IJ|IS_ROOM",
+ "IJ|IS_OBJECT",
+ "IWJ|ITEM_UNK3_IS",
+ /* 28 */
+ "IBJ|CHILD_HAS_FLAG",
+ NULL,
+ NULL,
+ "I|SET_NO_PARENT",
+ /* 32 */
+ NULL,
+ "II|SET_PARENT",
+ NULL,
+ NULL,
+ /* 36 */
+ "VV|MOVE",
+ NULL,
+ NULL,
+ NULL,
+ /* 40 */
+ NULL,
+ "V|ZERO",
+ "VW|SET",
+ "VW|ADD",
+ /* 44 */
+ "VW|SUB",
+ "VV|ADDF",
+ "VV|SUBF",
+ "VW|MUL",
+ /* 48 */
+ "VW|DIV",
+ "VV|MULF",
+ "VV|DIVF",
+ "VW|MOD",
+ /* 52 */
+ "VV|MODF",
+ "VW|RANDOM",
+ NULL,
+ "I|SET_A_PARENT",
+ /* 56 */
+ "IB|SET_CHILD2_BIT",
+ "IB|CLEAR_CHILD2_BIT",
+ "II|MAKE_SIBLING",
+ "I|INC_UNK3",
+ /* 60 */
+ "I|DEC_UNK3",
+ "IW|SET_UNK3",
+ "V|SHOW_INT",
+ "T|SHOW_STRING_NL",
+ /* 64 */
+ "T|SHOW_STRING",
+ "WWWWWB|ADD_HITAREA",
+ "BT|SET_ITEM_NAME",
+ "BT|SET_ITEM_DESC",
+ /* 68 */
+ "x|HALT",
+ "x|RET1",
+ "V|SHOW_STRING_AR3",
+ "W|START_SUB",
+ /* 72 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 76 */
+ "WW|ADD_TIMEOUT",
+ "J|IS_M1_EMPTY",
+ "J|IS_M3_EMPTY",
+ "ITJ|CHILD_FR2_IS",
+ /* 80 */
+ "IIJ|IS_ITEM_EQ",
+ NULL,
+ "B|UNK_82",
+ "|RETM10",
+ /* 84 */
+ NULL,
+ NULL,
+ NULL,
+ "W|UNK_87",
+ /* 88 */
+ "|STOP_ANIMATION",
+ "|RESTART_ANIMATION",
+ "IB|SET_M_TO_PARENT",
+ "IB|SET_M_TO_SIBLING",
+ /* 92 */
+ "IB|SET_M_TO_CHILD",
+ NULL,
+ NULL,
+ NULL,
+ /* 96 */
+ "WB|UNK_96",
+ "W|LOAD_VGA",
+ "WBWWW|START_VGA",
+ "W|KILL_SPRITE",
+ /* 100 */
+ "|VGA_RESET",
+ "BWWWWWW|UNK_101",
+ "B|UNK_102",
+ "|UNK_103",
+ /* 104 */
+ "B|UNK_104",
+ NULL,
+ NULL,
+ "WWWWWIW|ADD_ITEM_HITAREA",
+ /* 108 */
+ "W|DEL_HITAREA",
+ "W|CLEAR_HITAREA_0x40",
+ "W|SET_HITAREA_0x40",
+ "WWW|SET_HITAREA_XY",
+ /* 112 */
+ NULL,
+ NULL,
+ "IB|UNK_114",
+ "IBJ|HAS_FLAG",
+ /* 116 */
+ "IB|SET_FLAG",
+ "IB|CLEAR_FLAG",
+ NULL,
+ "W|WAIT_VGA",
+ /* 120 */
+ "W|UNK_120",
+ "BI|SET_VGA_ITEM",
+ NULL,
+ NULL,
+ /* 124 */
+ NULL,
+ "IJ|IS_SIBLING_WITH_A",
+ "IBB|UNK_126",
+ "WW|PLAY_MUSIC_RESOURCE",
+ /* 128 */
+ "W|GET_DUMMY_WORD",
+ "W|GET_WORD_COND_TRUE",
+ "Bww|SET_ADJ_NOUN",
+ NULL,
+ /* 132 */
+ "|SAVE_GAME",
+ "|LOAD_GAME",
+ "|DUMMYPROC_134",
+ "|QUIT_IF_USER_PRESSES_Y",
+ /* 136 */
+ "IV|GET_ITEM_UNK3",
+ "B|UNK_137",
+ "|VGA_POINTER_OP_4",
+ "II|SET_PARENT_SPECIAL",
+ /* 140 */
+ "|DEL_TE_AND_ADD_ONE",
+ "BI|SET_M1_OR_M3",
+ "WJ|IS_HITAREA_0x40_CLEAR",
+ "I|START_ITEM_SUB",
+ /* 144 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 148 */
+ NULL,
+ NULL,
+ NULL,
+ "BI|SET_ARRAY6_TO",
+ /* 152 */
+ "BB|SET_M1_M3_TO_ARRAY6",
+ "B|SET_BIT",
+ "B|CLEAR_BIT",
+ "BJ|IS_BIT_CLEAR",
+ /* 156 */
+ "BJ|IS_BIT_SET",
+ "IBB|GET_ITEM_PROP",
+ "IBW|SET_ITEM_PROP",
+ NULL,
+ /* 160 */
+ "B|UNK_160",
+ "BWBW|SETUP_TEXT",
+ "BBT|PRINT_STR",
+ "W|SOUND_1",
+ /* 164 */
+ "|UNK_164",
+ "IWWJ|ITEM_UNK1_UNK2_IS",
+ "B|SET_BIT2",
+ "B|CLEAR_BIT2",
+ /* 168 */
+ "BJ|IS_BIT2_CLEAR",
+ "BJ|IS_BIT2_SET",
+ NULL,
+ NULL,
+ /* 172 */
+ NULL,
+ NULL,
+ NULL,
+ "|VGA_POINTER_OP_1",
+ /* 176 */
+ "|VGA_POINTER_OP_2",
+ "BBI|INVENTORY_DESCRIPTION",
+ "WWBB|PATHFIND",
+ "BBB|ROOM_DESCRIPTION",
+ /* 180 */
+ "|MOUSE_ON",
+ "|MOUSE_OFF",
+ "|LOAD_BEARD",
+ "|UNLOAD_BEARD",
+ /* 184 */
+ "W|CLEAR_VGAPOINTER_ENTRY",
+ "W|LOAD_SOUND_FILES",
+ "|VGA_POINTER_OP_3",
+ "|FADE_TO_BLACK",
+};
+
+static const char *const simon1talkie_opcode_name_table[256] = {
+ /* 0 */
+ "|INV_COND",
+ "IJ|PTRA_PARENT_IS",
+ "IJ|PTRA_PARENT_ISNOT",
+ NULL,
+ /* 4 */
+ NULL,
+ "IJ|PARENT_IS_1",
+ "IJ|PARENT_ISNOT_1",
+ "IIJ|PARENT_IS",
+ /* 8 */
+ NULL,
+ NULL,
+ NULL,
+ "VJ|IS_ZERO",
+ /* 12 */
+ "VJ|ISNOT_ZERO",
+ "VWJ|IS_EQ",
+ "VWJ|IS_NEQ",
+ "VWJ|IS_LE",
+ /* 16 */
+ "VWJ|IS_GE",
+ "VVJ|IS_EQF",
+ "VVJ|IS_NEQF",
+ "VVJ|IS_LEF",
+ /* 20 */
+ "VVJ|IS_GEF",
+ NULL,
+ NULL,
+ "WJ|CHANCE",
+ /* 24 */
+ NULL,
+ "IJ|IS_ROOM",
+ "IJ|IS_OBJECT",
+ "IWJ|ITEM_UNK3_IS",
+ /* 28 */
+ "IBJ|CHILD_HAS_FLAG",
+ NULL,
+ NULL,
+ "I|SET_NO_PARENT",
+ /* 32 */
+ NULL,
+ "II|SET_PARENT",
+ NULL,
+ NULL,
+ /* 36 */
+ "VV|MOVE",
+ NULL,
+ NULL,
+ NULL,
+ /* 40 */
+ NULL,
+ "V|ZERO",
+ "VW|SET",
+ "VW|ADD",
+ /* 44 */
+ "VW|SUB",
+ "VV|ADDF",
+ "VV|SUBF",
+ "VW|MUL",
+ /* 48 */
+ "VW|DIV",
+ "VV|MULF",
+ "VV|DIVF",
+ "VW|MOD",
+ /* 52 */
+ "VV|MODF",
+ "VW|RANDOM",
+ NULL,
+ "I|SET_A_PARENT",
+ /* 56 */
+ "IB|SET_CHILD2_BIT",
+ "IB|CLEAR_CHILD2_BIT",
+ "II|MAKE_SIBLING",
+ "I|INC_UNK3",
+ /* 60 */
+ "I|DEC_UNK3",
+ "IW|SET_UNK3",
+ "V|SHOW_INT",
+ "T|SHOW_STRING_NL",
+ /* 64 */
+ "T|SHOW_STRING",
+ "WWWWWB|ADD_HITAREA",
+ "BT|SET_ITEM_NAME",
+ "BTw|SET_ITEM_DESC",
+ /* 68 */
+ "x|HALT",
+ "x|RET1",
+ "V|SHOW_STRING_AR3",
+ "W|START_SUB",
+ /* 72 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 76 */
+ "WW|ADD_TIMEOUT",
+ "J|IS_M1_EMPTY",
+ "J|IS_M3_EMPTY",
+ "ITJ|CHILD_FR2_IS",
+ /* 80 */
+ "IIJ|IS_ITEM_EQ",
+ NULL,
+ "B|UNK_82",
+ "|RETM10",
+ /* 84 */
+ NULL,
+ NULL,
+ NULL,
+ "W|UNK_87",
+ /* 88 */
+ "|STOP_ANIMATION",
+ "|RESTART_ANIMATION",
+ "IB|SET_M_TO_PARENT",
+ "IB|SET_M_TO_SIBLING",
+ /* 92 */
+ "IB|SET_M_TO_CHILD",
+ NULL,
+ NULL,
+ NULL,
+ /* 96 */
+ "WB|UNK_96",
+ "W|LOAD_VGA",
+ "WBWWW|START_VGA",
+ "W|KILL_SPRITE",
+ /* 100 */
+ "|VGA_RESET",
+ "BWWWWWW|UNK_101",
+ "B|UNK_102",
+ "|UNK_103",
+ /* 104 */
+ "B|UNK_104",
+ NULL,
+ NULL,
+ "WWWWWIW|ADD_ITEM_HITAREA",
+ /* 108 */
+ "W|DEL_HITAREA",
+ "W|CLEAR_HITAREA_0x40",
+ "W|SET_HITAREA_0x40",
+ "WWW|SET_HITAREA_XY",
+ /* 112 */
+ NULL,
+ NULL,
+ "IB|UNK_114",
+ "IBJ|HAS_FLAG",
+ /* 116 */
+ "IB|SET_FLAG",
+ "IB|CLEAR_FLAG",
+ NULL,
+ "W|WAIT_VGA",
+ /* 120 */
+ "W|UNK_120",
+ "BI|SET_VGA_ITEM",
+ NULL,
+ NULL,
+ /* 124 */
+ NULL,
+ "IJ|IS_SIBLING_WITH_A",
+ "IBB|UNK_126",
+ "WW|PLAY_MUSIC_RESOURCE",
+ /* 128 */
+ "W|GET_DUMMY_WORD",
+ "W|GET_WORD_COND_TRUE",
+ "Bww|SET_ADJ_NOUN",
+ NULL,
+ /* 132 */
+ "|SAVE_GAME",
+ "|LOAD_GAME",
+ "|DUMMYPROC_134",
+ "|QUIT_IF_USER_PRESSES_Y",
+ /* 136 */
+ "IV|GET_ITEM_UNK3",
+ "B|UNK137",
+ "|VGA_POINTER_OP_4",
+ "II|SET_PARENT_SPECIAL",
+ /* 140 */
+ "|DEL_TE_AND_ADD_ONE",
+ "BI|SET_M1_OR_M3",
+ "WJ|IS_HITAREA_0x40_CLEAR",
+ "I|START_ITEM_SUB",
+ /* 144 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 148 */
+ NULL,
+ NULL,
+ NULL,
+ "BI|SET_ARRAY6_TO",
+ /* 152 */
+ "BB|SET_M1_M3_TO_ARRAY6",
+ "B|SET_BIT",
+ "B|CLEAR_BIT",
+ "BJ|IS_BIT_CLEAR",
+ /* 156 */
+ "BJ|IS_BIT_SET",
+ "IBB|GET_ITEM_PROP",
+ "IBW|SET_ITEM_PROP",
+ NULL,
+ /* 160 */
+ "B|UNK_160",
+ "BWBW|SETUP_TEXT",
+ "BBTW|PRINT_STR",
+ "W|SOUND_1",
+ /* 164 */
+ "|UNK_164",
+ "IWWJ|ITEM_UNK1_UNK2_IS",
+ "B|SET_BIT2",
+ "B|CLEAR_BIT2",
+ /* 168 */
+ "BJ|IS_BIT2_CLEAR",
+ "BJ|IS_BIT2_SET",
+ NULL,
+ NULL,
+ /* 172 */
+ NULL,
+ NULL,
+ NULL,
+ "|VGA_POINTER_OP_1",
+ /* 176 */
+ "|VGA_POINTER_OP_2",
+ "BBI|INVENTORY_DESCRIPTION",
+ "WWBB|PATHFIND",
+ "BBB|ROOM_DESCRIPTION",
+ /* 180 */
+ "|MOUSE_ON",
+ "|MOUSE_OFF",
+ "|LOAD_BEARD",
+ "|UNLOAD_BEARD",
+ /* 184 */
+ "W|CLEAR_VGAPOINTER_ENTRY",
+ "W|LOAD_SOUND_FILES",
+ "|VGA_POINTER_OP_3",
+ "|FADE_TO_BLACK",
+};
+
+static const char *const simon2dos_opcode_name_table[256] = {
+ /* 0 */
+ "|INV_COND",
+ "IJ|PTRA_PARENT_IS",
+ "IJ|PTRA_PARENT_ISNOT",
+ NULL,
+ /* 4 */
+ NULL,
+ "IJ|PARENT_IS_1",
+ "IJ|PARENT_ISNOT_1",
+ "IIJ|PARENT_IS",
+ /* 8 */
+ NULL,
+ NULL,
+ NULL,
+ "VJ|IS_ZERO",
+ /* 12 */
+ "VJ|ISNOT_ZERO",
+ "VWJ|IS_EQ",
+ "VWJ|IS_NEQ",
+ "VWJ|IS_LE",
+ /* 16 */
+ "VWJ|IS_GE",
+ "VVJ|IS_EQF",
+ "VVJ|IS_NEQF",
+ "VVJ|IS_LEF",
+ /* 20 */
+ "VVJ|IS_GEF",
+ NULL,
+ NULL,
+ "WJ|CHANCE",
+ /* 24 */
+ NULL,
+ "IJ|IS_ROOM",
+ "IJ|IS_OBJECT",
+ "IWJ|ITEM_UNK3_IS",
+ /* 28 */
+ "IBJ|CHILD_HAS_FLAG",
+ NULL,
+ NULL,
+ "I|SET_NO_PARENT",
+ /* 32 */
+ NULL,
+ "II|SET_PARENT",
+ NULL,
+ NULL,
+ /* 36 */
+ "VV|MOVE",
+ NULL,
+ NULL,
+ NULL,
+ /* 40 */
+ NULL,
+ "V|ZERO",
+ "VW|SET",
+ "VW|ADD",
+ /* 44 */
+ "VW|SUB",
+ "VV|ADDF",
+ "VV|SUBF",
+ "VW|MUL",
+ /* 48 */
+ "VW|DIV",
+ "VV|MULF",
+ "VV|DIVF",
+ "VW|MOD",
+ /* 52 */
+ "VV|MODF",
+ "VW|RANDOM",
+ NULL,
+ "I|SET_A_PARENT",
+ /* 56 */
+ "IB|SET_CHILD2_BIT",
+ "IB|CLEAR_CHILD2_BIT",
+ "II|MAKE_SIBLING",
+ "I|INC_UNK3",
+ /* 60 */
+ "I|DEC_UNK3",
+ "IW|SET_UNK3",
+ "V|SHOW_INT",
+ "T|SHOW_STRING_NL",
+ /* 64 */
+ "T|SHOW_STRING",
+ "WWWWWB|ADD_HITAREA",
+ "BT|SET_ITEM_NAME",
+ "BT|SET_ITEM_DESC",
+ /* 68 */
+ "x|HALT",
+ "x|RET1",
+ "V|SHOW_STRING_AR3",
+ "W|START_SUB",
+ /* 72 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 76 */
+ "WW|ADD_TIMEOUT",
+ "J|IS_M1_EMPTY",
+ "J|IS_M3_EMPTY",
+ "ITJ|CHILD_FR2_IS",
+ /* 80 */
+ "IIJ|IS_ITEM_EQ",
+ NULL,
+ "B|UNK_82",
+ "|RETM10",
+ /* 84 */
+ NULL,
+ NULL,
+ NULL,
+ "W|UNK_87",
+ /* 88 */
+ "|STOP_ANIMATION",
+ "|RESTART_ANIMATION",
+ "IB|SET_M_TO_PARENT",
+ "IB|SET_M_TO_SIBLING",
+ /* 92 */
+ "IB|SET_M_TO_CHILD",
+ NULL,
+ NULL,
+ NULL,
+ /* 96 */
+ "WB|UNK_96",
+ "W|LOAD_VGA",
+ "WWBWWW|START_VGA",
+ "WW|KILL_SPRITE",
+ /* 100 */
+ "|VGA_RESET",
+ "BWWWWWW|UNK_101",
+ "B|UNK_102",
+ "|UNK_103",
+ /* 104 */
+ "B|UNK_104",
+ NULL,
+ NULL,
+ "WWWWWIW|ADD_ITEM_HITAREA",
+ /* 108 */
+ "W|DEL_HITAREA",
+ "W|CLEAR_HITAREA_0x40",
+ "W|SET_HITAREA_0x40",
+ "WWW|SET_HITAREA_XY",
+ /* 112 */
+ NULL,
+ NULL,
+ "IB|UNK_114",
+ "IBJ|HAS_FLAG",
+ /* 116 */
+ "IB|SET_FLAG",
+ "IB|CLEAR_FLAG",
+ NULL,
+ "W|WAIT_VGA",
+ /* 120 */
+ "W|UNK_120",
+ "BI|SET_VGA_ITEM",
+ NULL,
+ NULL,
+ /* 124 */
+ NULL,
+ "IJ|IS_SIBLING_WITH_A",
+ "IBB|UNK_126",
+ "WW|PLAY_MUSIC_RESOURCE",
+ /* 128 */
+ "W|GET_DUMMY_WORD",
+ "W|GET_WORD_COND_TRUE",
+ "Bww|SET_ADJ_NOUN",
+ NULL,
+ /* 132 */
+ "|SAVE_GAME",
+ "|LOAD_GAME",
+ "|DUMMYPROC_134",
+ "|QUIT_IF_USER_PRESSES_Y",
+ /* 136 */
+ "IV|GET_ITEM_UNK3",
+ "B|UNK_137",
+ "|VGA_POINTER_OP_4",
+ "II|SET_PARENT_SPECIAL",
+ /* 140 */
+ "|DEL_TE_AND_ADD_ONE",
+ "BI|SET_M1_OR_M3",
+ "WJ|IS_HITAREA_0x40_CLEAR",
+ "I|START_ITEM_SUB",
+ /* 144 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 148 */
+ NULL,
+ NULL,
+ NULL,
+ "BI|SET_ARRAY6_TO",
+ /* 152 */
+ "BB|SET_M1_M3_TO_ARRAY6",
+ "B|SET_BIT",
+ "B|CLEAR_BIT",
+ "BJ|IS_BIT_CLEAR",
+ /* 156 */
+ "BJ|IS_BIT_SET",
+ "IBB|GET_ITEM_PROP",
+ "IBW|SET_ITEM_PROP",
+ NULL,
+ /* 160 */
+ "B|UNK_160",
+ "BWBW|SETUP_TEXT",
+ "BBT|PRINT_STR",
+ "W|SOUND_1",
+ /* 164 */
+ "|UNK_164",
+ "IWWJ|ITEM_UNK1_UNK2_IS",
+ "B|SET_BIT2",
+ "B|CLEAR_BIT2",
+ /* 168 */
+ "BJ|IS_BIT2_CLEAR",
+ "BJ|IS_BIT2_SET",
+ NULL,
+ NULL,
+ /* 172 */
+ NULL,
+ NULL,
+ NULL,
+ "|VGA_POINTER_OP_1",
+ /* 176 */
+ "|VGA_POINTER_OP_2",
+ "BBI|INVENTORY_DESCRIPTION",
+ "WWBB|PATHFIND",
+ "BBB|ROOM_DESCRIPTION",
+ /* 180 */
+ "|MOUSE_ON",
+ "|MOUSE_OFF",
+ NULL,
+ NULL,
+ /* 184 */
+ "W|CLEAR_VGAPOINTER_ENTRY",
+ NULL,
+ "|VGA_POINTER_OP_3",
+ NULL,
+ /* 188 */
+ "BSJ|STRING2_IS",
+ "|CLEAR_MARKS",
+ "B|WAIT_FOR_MARK",
+};
+
+static const char *const simon2talkie_opcode_name_table[256] = {
+ /* 0 */
+ "|INV_COND",
+ "IJ|PTRA_PARENT_IS",
+ "IJ|PTRA_PARENT_ISNOT",
+ NULL,
+ /* 4 */
+ NULL,
+ "IJ|PARENT_IS_1",
+ "IJ|PARENT_ISNOT_1",
+ "IIJ|PARENT_IS",
+ /* 8 */
+ NULL,
+ NULL,
+ NULL,
+ "VJ|IS_ZERO",
+ /* 12 */
+ "VJ|ISNOT_ZERO",
+ "VWJ|IS_EQ",
+ "VWJ|IS_NEQ",
+ "VWJ|IS_LE",
+ /* 16 */
+ "VWJ|IS_GE",
+ "VVJ|IS_EQF",
+ "VVJ|IS_NEQF",
+ "VVJ|IS_LEF",
+ /* 20 */
+ "VVJ|IS_GEF",
+ NULL,
+ NULL,
+ "WJ|CHANCE",
+ /* 24 */
+ NULL,
+ "IJ|IS_ROOM",
+ "IJ|IS_OBJECT",
+ "IWJ|ITEM_UNK3_IS",
+ /* 28 */
+ "IBJ|CHILD_HAS_FLAG",
+ NULL,
+ NULL,
+ "I|SET_NO_PARENT",
+ /* 32 */
+ NULL,
+ "II|SET_PARENT",
+ NULL,
+ NULL,
+ /* 36 */
+ "VV|MOVE",
+ NULL,
+ NULL,
+ NULL,
+ /* 40 */
+ NULL,
+ "V|ZERO",
+ "VW|SET",
+ "VW|ADD",
+ /* 44 */
+ "VW|SUB",
+ "VV|ADDF",
+ "VV|SUBF",
+ "VW|MUL",
+ /* 48 */
+ "VW|DIV",
+ "VV|MULF",
+ "VV|DIVF",
+ "VW|MOD",
+ /* 52 */
+ "VV|MODF",
+ "VW|RANDOM",
+ NULL,
+ "I|SET_A_PARENT",
+ /* 56 */
+ "IB|SET_CHILD2_BIT",
+ "IB|CLEAR_CHILD2_BIT",
+ "II|MAKE_SIBLING",
+ "I|INC_UNK3",
+ /* 60 */
+ "I|DEC_UNK3",
+ "IW|SET_UNK3",
+ "V|SHOW_INT",
+ "T|SHOW_STRING_NL",
+ /* 64 */
+ "T|SHOW_STRING",
+ "WWWWWB|ADD_HITAREA",
+ "BT|SET_ITEM_NAME",
+ "BTw|SET_ITEM_DESC",
+ /* 68 */
+ "x|HALT",
+ "x|RET1",
+ "V|SHOW_STRING_AR3",
+ "W|START_SUB",
+ /* 72 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 76 */
+ "WW|ADD_TIMEOUT",
+ "J|IS_M1_EMPTY",
+ "J|IS_M3_EMPTY",
+ "ITJ|CHILD_FR2_IS",
+ /* 80 */
+ "IIJ|IS_ITEM_EQ",
+ NULL,
+ "B|UNK_82",
+ "|RETM10",
+ /* 84 */
+ NULL,
+ NULL,
+ NULL,
+ "W|UNK_87",
+ /* 88 */
+ "|STOP_ANIMATION",
+ "|RESTART_ANIMATION",
+ "IB|SET_M_TO_PARENT",
+ "IB|SET_M_TO_SIBLING",
+ /* 92 */
+ "IB|SET_M_TO_CHILD",
+ NULL,
+ NULL,
+ NULL,
+ /* 96 */
+ "WB|UNK_96",
+ "W|LOAD_VGA",
+ "WWBWWW|START_VGA",
+ "WW|KILL_SPRITE",
+ /* 100 */
+ "|VGA_RESET",
+ "BWWWWWW|UNK_101",
+ "B|UNK_102",
+ "|UNK_103",
+ /* 104 */
+ "B|UNK_104",
+ NULL,
+ NULL,
+ "WWWWWIW|ADD_ITEM_HITAREA",
+ /* 108 */
+ "W|DEL_HITAREA",
+ "W|CLEAR_HITAREA_0x40",
+ "W|SET_HITAREA_0x40",
+ "WWW|SET_HITAREA_XY",
+ /* 112 */
+ NULL,
+ NULL,
+ "IB|UNK_114",
+ "IBJ|HAS_FLAG",
+ /* 116 */
+ "IB|SET_FLAG",
+ "IB|CLEAR_FLAG",
+ NULL,
+ "W|WAIT_VGA",
+ /* 120 */
+ "W|UNK_120",
+ "BI|SET_VGA_ITEM",
+ NULL,
+ NULL,
+ /* 124 */
+ NULL,
+ "IJ|IS_SIBLING_WITH_A",
+ "IBB|UNK_126",
+ "WW|PLAY_MUSIC_RESOURCE",
+ /* 128 */
+ "W|GET_DUMMY_WORD",
+ "W|GET_WORD_COND_TRUE",
+ "Bww|SET_ADJ_NOUN",
+ NULL,
+ /* 132 */
+ "|SAVE_GAME",
+ "|LOAD_GAME",
+ "|DUMMYPROC_134",
+ "|QUIT_IF_USER_PRESSES_Y",
+ /* 136 */
+ "IV|GET_ITEM_UNK3",
+ "B|UNK_137",
+ "|VGA_POINTER_OP_4",
+ "II|SET_PARENT_SPECIAL",
+ /* 140 */
+ "|DEL_TE_AND_ADD_ONE",
+ "BI|SET_M1_OR_M3",
+ "WJ|IS_HITAREA_0x40_CLEAR",
+ "I|START_ITEM_SUB",
+ /* 144 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 148 */
+ NULL,
+ NULL,
+ NULL,
+ "BI|SET_ARRAY6_TO",
+ /* 152 */
+ "BB|SET_M1_M3_TO_ARRAY6",
+ "B|SET_BIT",
+ "B|CLEAR_BIT",
+ "BJ|IS_BIT_CLEAR",
+ /* 156 */
+ "BJ|IS_BIT_SET",
+ "IBB|GET_ITEM_PROP",
+ "IBW|SET_ITEM_PROP",
+ NULL,
+ /* 160 */
+ "B|UNK_160",
+ "BWBW|SETUP_TEXT",
+ "BBTW|PRINT_STR",
+ "W|SOUND_1",
+ /* 164 */
+ "|UNK_164",
+ "IWWJ|ITEM_UNK1_UNK2_IS",
+ "B|SET_BIT2",
+ "B|CLEAR_BIT2",
+ /* 168 */
+ "BJ|IS_BIT2_CLEAR",
+ "BJ|IS_BIT2_SET",
+ NULL,
+ NULL,
+ /* 172 */
+ NULL,
+ NULL,
+ NULL,
+ "|VGA_POINTER_OP_1",
+ /* 176 */
+ "|VGA_POINTER_OP_2",
+ "BBI|INVENTORY_DESCRIPTION",
+ "WWBB|PATHFIND",
+ "BBB|ROOM_DESCRIPTION",
+ /* 180 */
+ "|MOUSE_ON",
+ "|MOUSE_OFF",
+ NULL,
+ NULL,
+ /* 184 */
+ "W|CLEAR_VGAPOINTER_ENTRY",
+ NULL,
+ "|VGA_POINTER_OP_3",
+ NULL,
+ /* 188 */
+ "BSJ|STRING2_IS",
+ "|CLEAR_MARKS",
+ "B|WAIT_FOR_MARK",
+};
+
+static const char *const feeblefiles_opcode_name_table[256] = {
+ /* 0 */
+ "|INV_COND",
+ "IJ|PTRA_PARENT_IS",
+ "IJ|PTRA_PARENT_ISNOT",
+ NULL,
+ /* 4 */
+ NULL,
+ "IJ|PARENT_IS_1",
+ "IJ|PARENT_ISNOT_1",
+ "IIJ|PARENT_IS",
+ /* 8 */
+ NULL,
+ NULL,
+ NULL,
+ "VJ|IS_ZERO",
+ /* 12 */
+ "VJ|ISNOT_ZERO",
+ "VWJ|IS_EQ",
+ "VWJ|IS_NEQ",
+ "VWJ|IS_LE",
+ /* 16 */
+ "VWJ|IS_GE",
+ "VVJ|IS_EQF",
+ "VVJ|IS_NEQF",
+ "VVJ|IS_LEF",
+ /* 20 */
+ "VVJ|IS_GEF",
+ NULL,
+ NULL,
+ "WJ|CHANCE",
+ /* 24 */
+ NULL,
+ "IJ|IS_ROOM",
+ "IJ|IS_OBJECT",
+ "IWJ|ITEM_UNK3_IS",
+ /* 28 */
+ "IBJ|CHILD_HAS_FLAG",
+ NULL,
+ NULL,
+ "I|SET_NO_PARENT",
+ /* 32 */
+ NULL,
+ "II|SET_PARENT",
+ NULL,
+ NULL,
+ /* 36 */
+ "VV|MOVE",
+ NULL,
+ NULL,
+ NULL,
+ /* 40 */
+ NULL,
+ "V|ZERO",
+ "VW|SET",
+ "VW|ADD",
+ /* 44 */
+ "VW|SUB",
+ "VV|ADDF",
+ "VV|SUBF",
+ "VW|MUL",
+ /* 48 */
+ "VW|DIV",
+ "VV|MULF",
+ "VV|DIVF",
+ "VW|MOD",
+ /* 52 */
+ "VV|MODF",
+ "VW|RANDOM",
+ NULL,
+ "I|SET_A_PARENT",
+ /* 56 */
+ "IB|SET_CHILD2_BIT",
+ "IB|CLEAR_CHILD2_BIT",
+ "II|MAKE_SIBLING",
+ "I|INC_UNK3",
+ /* 60 */
+ "I|DEC_UNK3",
+ "IW|SET_UNK3",
+ "V|SHOW_INT",
+ "T|SHOW_STRING_NL",
+ /* 64 */
+ "T|SHOW_STRING",
+ "WWWWWB|ADD_HITAREA",
+ "BT|SET_ITEM_NAME",
+ "BTw|SET_ITEM_DESC",
+ /* 68 */
+ "x|HALT",
+ "x|RET1",
+ "V|SHOW_STRING_AR3",
+ "W|START_SUB",
+ /* 72 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 76 */
+ "WW|ADD_TIMEOUT",
+ "J|IS_M1_EMPTY",
+ "J|IS_M3_EMPTY",
+ "ITJ|CHILD_FR2_IS",
+ /* 80 */
+ "IIJ|IS_ITEM_EQ",
+ NULL,
+ "B|UNK_82",
+ "|RETM10",
+ /* 84 */
+ NULL,
+ NULL,
+ NULL,
+ "W|UNK_87",
+ /* 88 */
+ "|STOP_ANIMATION",
+ "|RESTART_ANIMATION",
+ "IB|SET_M_TO_PARENT",
+ "IB|SET_M_TO_SIBLING",
+ /* 92 */
+ "IB|SET_M_TO_CHILD",
+ NULL,
+ NULL,
+ NULL,
+ /* 96 */
+ "WB|UNK_96",
+ "W|LOAD_VGA",
+ "WWBWWW|START_VGA",
+ "WW|KILL_SPRITE",
+ /* 100 */
+ "|VGA_RESET",
+ "BWWWWWW|UNK_101",
+ "B|UNK_102",
+ "|UNK_103",
+ /* 104 */
+ "B|UNK_104",
+ NULL,
+ NULL,
+ "WWWWWIW|ADD_ITEM_HITAREA",
+ /* 108 */
+ "W|DEL_HITAREA",
+ "W|CLEAR_HITAREA_0x40",
+ "W|SET_HITAREA_0x40",
+ "WWW|SET_HITAREA_XY",
+ /* 112 */
+ NULL,
+ NULL,
+ "IB|UNK_114",
+ "IBJ|HAS_FLAG",
+ /* 116 */
+ "IB|SET_FLAG",
+ "IB|CLEAR_FLAG",
+ NULL,
+ "W|WAIT_VGA",
+ /* 120 */
+ "W|UNK_120",
+ "BI|SET_VGA_ITEM",
+ "ORACLE_TEXT_DOWN",
+ "ORACLE_TEXT_UP",
+ /* 124 */
+ "WJ|IF_TIME",
+ "IJ|IS_SIBLING_WITH_A",
+ "IBB|UNK_126",
+ "WW|PLAY_MUSIC_RESOURCE",
+ /* 128 */
+ "W|GET_DUMMY_WORD",
+ "W|GET_WORD_COND_TRUE",
+ "Bww|SET_ADJ_NOUN",
+ NULL,
+ /* 132 */
+ "|SAVE_GAME",
+ "|LOAD_GAME",
+ "|LIST_SAVED_GAMES",
+ "|SWITCH_CD",
+ /* 136 */
+ "IV|GET_ITEM_UNK3",
+ "B|UNK_137",
+ "|VGA_POINTER_OP_4",
+ "II|SET_PARENT_SPECIAL",
+ /* 140 */
+ "|DEL_TE_AND_ADD_ONE",
+ "BI|SET_M1_OR_M3",
+ "WJ|IS_HITAREA_0x40_CLEAR",
+ "I|START_ITEM_SUB",
+ /* 144 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ /* 148 */
+ NULL,
+ NULL,
+ NULL,
+ "BI|SET_ARRAY6_TO",
+ /* 152 */
+ "BB|SET_M1_M3_TO_ARRAY6",
+ "B|SET_BIT",
+ "B|CLEAR_BIT",
+ "BJ|IS_BIT_CLEAR",
+ /* 156 */
+ "BJ|IS_BIT_SET",
+ "IBB|GET_ITEM_PROP",
+ "IBW|SET_ITEM_PROP",
+ NULL,
+ /* 160 */
+ "B|UNK_160",
+ "BWBW|SETUP_TEXT",
+ "BBTW|PRINT_STR",
+ "W|SOUND_1",
+ /* 164 */
+ "|UNK_164",
+ "IWWJ|ITEM_UNK1_UNK2_IS",
+ "B|SET_BIT2",
+ "B|CLEAR_BIT2",
+ /* 168 */
+ "BJ|IS_BIT2_CLEAR",
+ "BJ|IS_BIT2_SET",
+ NULL,
+ NULL,
+ /* 172 */
+ NULL,
+ NULL,
+ NULL,
+ "|VGA_POINTER_OP_1",
+ /* 176 */
+ "|VGA_POINTER_OP_2",
+ "BBI|INVENTORY_DESCRIPTION",
+ "WWBB|PATHFIND",
+ "BBB|ROOM_DESCRIPTION",
+ /* 180 */
+ "|MOUSE_ON",
+ "|MOUSE_OFF",
+ "T|LOAD_VIDEO",
+ "|PLAY_VIDEO",
+ /* 184 */
+ "W|CLEAR_VGAPOINTER_ENTRY",
+ NULL,
+ "|VGA_POINTER_OP_3",
+ NULL,
+ /* 188 */
+ "BSJ|STRING2_IS",
+ "|CLEAR_MARKS",
+ "B|WAIT_FOR_MARK",
+ "|RESET_PV_COUNT",
+ /* 192 */
+ "BBBB|SET_PATH_VALUES",
+ "|PAUSE_CLOCK",
+ "|RESUME_CLOCK",
+ "BBBB|SET_COLOR",
+ /* 196 */
+ "B|B3_SET",
+ "B|B3_CLEAR",
+ "B|B3_ZERO",
+ "B|B3_NOT_ZERO",
+};
+
+const char *const simon1_video_opcode_name_table[] = {
+ /* 0 */
+ "x|RET",
+ "ddd|FADEOUT",
+ "d|CALL",
+ "ddddd|NEW_SPRITE",
+ /* 4 */
+ "ddd|FADEIN",
+ "vd|SKIP_IF_NEQ",
+ "d|SKIP_IFN_SIB_WITH_A",
+ "d|SKIP_IF_SIB_WITH_A",
+ /* 8 */
+ "dd|SKIP_IF_PARENT_IS",
+ "dd|SKIP_IF_UNK3_IS",
+ "ddddd|DRAW",
+ "|CLEAR_PATHFIND_ARRAY",
+ /* 12 */
+ "d|DELAY",
+ "d|SET_SPRITE_OFFSET_X",
+ "d|SET_SPRITE_OFFSET_Y",
+ "d|IDENT_WAKEUP",
+ /* 16 */
+ "d|IDENT_SLEEP",
+ "dq|SET_PATHFIND_ITEM",
+ "i|JUMP_REL",
+ "|CHAIN_TO",
+ /* 20 */
+ "dd|SET_CODE_WORD",
+ "i|JUMP_IF_CODE_WORD",
+ "dd|SET_SPRITE_PALETTE",
+ "d|SET_SPRITE_PRIORITY",
+ /* 24 */
+ "diid|SET_SPRITE_XY",
+ "x|HALT_SPRITE",
+ "ddddd|SET_WINDOW",
+ "|RESET",
+ /* 28 */
+ "dddd|DUMMY_28",
+ "|STOP_ALL_SOUNDS",
+ "d|SET_BASE_DELAY",
+ "d|SET_PALETTE_MODE",
+ /* 32 */
+ "vv|COPY_VAR",
+ "|MOUSE_ON",
+ "|MOUSE_OFF",
+ "dd|VC35",
+ /* 36 */
+ "dd|SAVELOAD_THING",
+ "v|SET_SPRITE_OFFSET_Y",
+ "v|SKIP_IF_VAR_ZERO",
+ "vd|SET_VAR",
+ /* 40 */
+ "vd|ADD_VAR",
+ "vd|SUB_VAR",
+ "vd|DELAY_IF_NOT_EQ",
+ "d|SKIP_IF_BIT_CLEAR",
+ /* 44 */
+ "d|SKIP_IF_BIT_SET",
+ "v|SET_SPRITE_X",
+ "v|SET_SPRITE_Y",
+ "vv|ADD_VAR_F",
+ /* 48 */
+ "|VC_48",
+ "d|SET_BIT",
+ "d|CLEAR_BIT",
+ "d|CLEAR_HITAREA_BIT_0x40",
+ /* 52 */
+ "d|PLAY_SOUND",
+ "dd|DUMMY_53",
+ "ddd|DUMMY_54",
+ "ddd|OFFSET_HIT_AREA",
+ /* 56 */
+ "|DUMMY_56",
+ "|DUMMY_57",
+ "|UNK_58",
+ "|SKIP_IF_SPEECH_ENDED",
+ /* 60 */
+ "d|KILL_SPRITE",
+ "ddd|INIT_SPRITE",
+ "|FASTFADEOUT",
+ "|FASTFADEIN",
+};
+
+const char *const simon2_video_opcode_name_table[] = {
+ /* 0 */
+ "x|RET",
+ "ddd|FADEOUT",
+ "d|CALL",
+ "ddddd|NEW_SPRITE",
+ /* 4 */
+ "ddd|FADEIN",
+ "vd|SKIP_IF_NEQ",
+ "d|SKIP_IFN_SIB_WITH_A",
+ "d|SKIP_IF_SIB_WITH_A",
+ /* 8 */
+ "dd|SKIP_IF_PARENT_IS",
+ "dd|SKIP_IF_UNK3_IS",
+ "ddddb|DRAW",
+ "|CLEAR_PATHFIND_ARRAY",
+ /* 12 */
+ "b|DELAY",
+ "d|SET_SPRITE_OFFSET_X",
+ "d|SET_SPRITE_OFFSET_Y",
+ "d|IDENT_WAKEUP",
+ /* 16 */
+ "d|IDENT_SLEEP",
+ "dq|SET_PATHFIND_ITEM",
+ "i|JUMP_REL",
+ "|CHAIN_TO",
+ /* 20 */
+ "dd|SET_CODE_WORD",
+ "i|JUMP_IF_CODE_WORD",
+ "dd|SET_SPRITE_PALETTE",
+ "d|SET_SPRITE_PRIORITY",
+ /* 24 */
+ "diib|SET_SPRITE_XY",
+ "x|HALT_SPRITE",
+ "ddddd|SET_WINDOW",
+ "|RESET",
+ /* 28 */
+ "dddd|DUMMY_28",
+ "|STOP_ALL_SOUNDS",
+ "d|SET_BASE_DELAY",
+ "d|SET_PALETTE_MODE",
+ /* 32 */
+ "vv|COPY_VAR",
+ "|MOUSE_ON",
+ "|MOUSE_OFF",
+ "dd|VC35",
+ /* 36 */
+ "dd|SAVELOAD_THING",
+ "v|SET_SPRITE_OFFSET_Y",
+ "v|SKIP_IF_VAR_ZERO",
+ "vd|SET_VAR",
+ /* 40 */
+ "vd|ADD_VAR",
+ "vd|SUB_VAR",
+ "vd|DELAY_IF_NOT_EQ",
+ "d|SKIP_IF_BIT_CLEAR",
+ /* 44 */
+ "d|SKIP_IF_BIT_SET",
+ "v|SET_SPRITE_X",
+ "v|SET_SPRITE_Y",
+ "vv|ADD_VAR_F",
+ /* 48 */
+ "|VC_48",
+ "d|SET_BIT",
+ "d|CLEAR_BIT",
+ "d|CLEAR_HITAREA_BIT_0x40",
+ /* 52 */
+ "d|PLAY_SOUND",
+ "dd|DUMMY_53",
+ "ddd|DUMMY_54",
+ "ddd|OFFSET_HIT_AREA",
+ /* 56 */
+ "i|SLEEP_EX",
+ "|DUMMY_57",
+ "|UNK_58",
+ "ddd|KILL_MULTI_SPRITE",
+ /* 60 */
+ "dd|KILL_SPRITE",
+ "ddd|INIT_SPRITE",
+ "|FASTFADEOUT",
+ "|FASTFADEIN",
+ /* 64 */
+ "|SKIP_IF_SPEECH_ENDED",
+ "|SLOW_FADE_IN",
+ "|SKIP_IF_NZ",
+ "|SKIP_IF_GE",
+ /* 68 */
+ "|SKIP_IF_LE",
+ "dd|PLAY_TRACK",
+ "dd|QUEUE_MUSIC",
+ "|CHECK_MUSIC_QUEUE",
+ /* 72 */
+ "dd|PLAY_TRACK_2",
+ "bb|SET_MARK",
+ "bb|CLEAR_MARK",
+};
+
+const char *const feeblefiles_video_opcode_name_table[] = {
+ /* 0 */
+ "x|RET",
+ "ddd|FADEOUT",
+ "d|CALL",
+ "ddddd|NEW_SPRITE",
+ /* 4 */
+ "ddd|FADEIN",
+ "vd|SKIP_IF_NEQ",
+ "d|SKIP_IFN_SIB_WITH_A",
+ "d|SKIP_IF_SIB_WITH_A",
+ /* 8 */
+ "dd|SKIP_IF_PARENT_IS",
+ "dd|SKIP_IF_UNK3_IS",
+ "ddddb|DRAW",
+ "|CLEAR_PATHFIND_ARRAY",
+ /* 12 */
+ "b|DELAY",
+ "d|SET_SPRITE_OFFSET_X",
+ "d|SET_SPRITE_OFFSET_Y",
+ "d|IDENT_WAKEUP",
+ /* 16 */
+ "d|IDENT_SLEEP",
+ "dq|SET_PATHFIND_ITEM",
+ "i|JUMP_REL",
+ "|CHAIN_TO",
+ /* 20 */
+ "dd|SET_CODE_WORD",
+ "i|JUMP_IF_CODE_WORD",
+ "dd|SET_SPRITE_PALETTE",
+ "d|SET_SPRITE_PRIORITY",
+ /* 24 */
+ "diib|SET_SPRITE_XY",
+ "x|HALT_SPRITE",
+ "ddddd|SET_WINDOW",
+ "|RESET",
+ /* 28 */
+ "dddd|DUMMY_28",
+ "|STOP_ALL_SOUNDS",
+ "d|SET_BASE_DELAY",
+ "d|SET_PALETTE_MODE",
+ /* 32 */
+ "vv|COPY_VAR",
+ "|MOUSE_ON",
+ "|MOUSE_OFF",
+ "dd|VC35",
+ /* 36 */
+ "dd|SAVELOAD_THING",
+ "v|SET_SPRITE_OFFSET_Y",
+ "v|SKIP_IF_VAR_ZERO",
+ "vd|SET_VAR",
+ /* 40 */
+ "vd|ADD_VAR",
+ "vd|SUB_VAR",
+ "vd|DELAY_IF_NOT_EQ",
+ "d|SKIP_IF_BIT_CLEAR",
+ /* 44 */
+ "d|SKIP_IF_BIT_SET",
+ "v|SET_SPRITE_X",
+ "v|SET_SPRITE_Y",
+ "vv|ADD_VAR_F",
+ /* 48 */
+ "|VC_48",
+ "d|SET_BIT",
+ "d|CLEAR_BIT",
+ "d|CLEAR_HITAREA_BIT_0x40",
+ /* 52 */
+ "ddd|PLAY_SOUND",
+ "ddd|PLAY_SOUND_WITH_ANIM",
+ "ddd|DUMMY_54",
+ "ddd|OFFSET_HIT_AREA",
+ /* 56 */
+ "i|SLEEP_EX",
+ "|DUMMY_57",
+ "|UNK_58",
+ "ddd|KILL_MULTI_SPRITE",
+ /* 60 */
+ "dd|KILL_SPRITE",
+ "ddd|INIT_SPRITE",
+ "|FASTFADEOUT",
+ "|FASTFADEIN",
+ /* 64 */
+ "|SKIP_IF_SPEECH_ENDED",
+ "|SLOW_FADE_IN",
+ "|SKIP_IF_NZ",
+ "|SKIP_IF_GE",
+ /* 68 */
+ "|SKIP_IF_LE",
+ "dd|PLAY_TRACK",
+ "dd|QUEUE_MUSIC",
+ "|CHECK_MUSIC_QUEUE",
+ /* 72 */
+ "dd|PLAY_TRACK_2",
+ "bb|SET_MARK",
+ "bb|CLEAR_MARK",
+ "dd|SETSCALE",
+ /* 76 */
+ "ddd|SETSCALEXOFFS",
+ "ddd|SETSCALEYOFFS",
+ "|COMPUTEXY",
+ "|COMPUTEPOSNUM",
+ /* 80 */
+ "ddd|SETOVERLAYIMAGE",
+ "dd|SETRANDOM",
+ "d|GETPATHVALUE",
+ "ddd|PLAYSOUNDLOOP",
+ "|STOPSOUNDLOOP",
+};
+
+} // End of namespace Simon
+
+#endif
+
diff --git a/engines/simon/debugger.cpp b/engines/simon/debugger.cpp
new file mode 100644
index 0000000000..1384c60693
--- /dev/null
+++ b/engines/simon/debugger.cpp
@@ -0,0 +1,206 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/config-manager.h"
+#include "common/debugger.cpp"
+#include "simon/debugger.h"
+#include "simon/simon.h"
+
+namespace Simon {
+
+Debugger::Debugger(SimonEngine *vm)
+ : Common::Debugger<Debugger>() {
+ _vm = vm;
+
+ DCmd_Register("continue", &Debugger::Cmd_Exit);
+ DCmd_Register("exit", &Debugger::Cmd_Exit);
+ DCmd_Register("help", &Debugger::Cmd_Help);
+ DCmd_Register("quit", &Debugger::Cmd_Exit);
+ DCmd_Register("level", &Debugger::Cmd_DebugLevel);
+ DCmd_Register("music", &Debugger::Cmd_PlayMusic);
+ DCmd_Register("sound", &Debugger::Cmd_PlaySound);
+ DCmd_Register("voice", &Debugger::Cmd_PlayVoice);
+ DCmd_Register("bit", &Debugger::Cmd_SetBit);
+ DCmd_Register("var", &Debugger::Cmd_SetVar);
+ DCmd_Register("sub", &Debugger::Cmd_StartSubroutine);
+
+}
+
+
+void Debugger::preEnter() {
+ //_vm->midi.pause(1);
+}
+
+
+void Debugger::postEnter() {
+ //_vm->midi.pause(0);
+}
+
+
+bool Debugger::Cmd_Exit(int argc, const char **argv) {
+ _detach_now = true;
+ return false;
+}
+
+bool Debugger::Cmd_Help(int argc, const char **argv) {
+ // console normally has 39 line width
+ // wrap around nicely
+ int width = 0, size, i;
+
+ DebugPrintf("Commands are:\n");
+ for (i = 0 ; i < _dcmd_count ; i++) {
+ size = strlen(_dcmds[i].name) + 1;
+
+ if ((width + size) >= 39) {
+ DebugPrintf("\n");
+ width = size;
+ } else
+ width += size;
+
+ DebugPrintf("%s ", _dcmds[i].name);
+ }
+ DebugPrintf("\n");
+ return true;
+}
+
+bool Debugger::Cmd_DebugLevel(int argc, const char **argv) {
+ if (argc == 1) {
+ if (_vm->_debugMode == false)
+ DebugPrintf("Debugging is not enabled at this time\n");
+ else
+ DebugPrintf("Debugging is currently set at level %d\n", gDebugLevel);
+ } else { // set level
+ gDebugLevel = atoi(argv[1]);
+ if (gDebugLevel >= 0 && gDebugLevel < 10) {
+ _vm->_debugMode = true;
+ DebugPrintf("Debug level set to level %d\n", gDebugLevel);
+ } else if (gDebugLevel < 0) {
+ _vm->_debugMode = false;
+ DebugPrintf("Debugging is now disabled\n");
+ } else
+ DebugPrintf("Not a valid debug level (0 - 10)\n");
+ }
+
+ return true;
+}
+
+bool Debugger::Cmd_PlayMusic(int argc, const char **argv) {
+ if (argc > 1) {
+ uint music = atoi(argv[1]);
+ uint range = (_vm->getGameType() == GType_SIMON2) ? 93 : 34;
+ if (music <= range) {
+ _vm->loadMusic (music);
+ if (_vm->getGameType() == GType_SIMON2)
+ _vm->midi.startTrack (0);
+ } else
+ DebugPrintf("Music out of range (0 - %d)\n", range);
+ } else
+ DebugPrintf("Syntax: music <musicnum>\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_PlaySound(int argc, const char **argv) {
+ if (argc > 1) {
+ uint sound = atoi(argv[1]);
+ uint range = (_vm->getGameType() == GType_SIMON2) ? 222 : 127;
+ if (sound <= range)
+ _vm->_sound->playEffects(sound);
+ else
+ DebugPrintf("Sound out of range (0 - %d)\n", range);
+ } else
+ DebugPrintf("Syntax: sound <soundnum>\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_PlayVoice(int argc, const char **argv) {
+ if (argc > 1) {
+ uint voice = atoi(argv[1]);
+ uint range = (_vm->getGameType() == GType_SIMON2) ? 3632 : 1996;
+ if (voice <= range)
+ _vm->_sound->playVoice(voice);
+ else
+ DebugPrintf("Voice out of range (0 - %d)\n", range);
+ } else
+ DebugPrintf("Syntax: voice <voicenum>\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_SetBit(int argc, const char **argv) {
+ uint bit, value;
+ if (argc > 2) {
+ bit = atoi(argv[1]);
+ value = atoi(argv[2]);
+ if (value <= 1) {
+ _vm->vcSetBitTo(bit, value != 0);
+ DebugPrintf("Set bit %d to %d\n", bit, value);
+ } else
+ DebugPrintf("Bit value out of range (0 - 1)\n");
+ } else if (argc > 1) {
+ bit = atoi(argv[1]);
+ value = _vm->vcGetBit(bit);
+ DebugPrintf("Bit %d is %d\n", bit, value);
+ } else
+ DebugPrintf("Syntax: bit <bitnum> <value>\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_SetVar(int argc, const char **argv) {
+ uint var, value;
+ if (argc > 1) {
+ var = atoi(argv[1]);
+ if (var <= 254) {
+ if (argc > 2) {
+ value = atoi(argv[2]);
+ _vm->writeVariable(var, value);
+ DebugPrintf("Set var %d to %d\n", var, value);
+ } else {
+ value = _vm->readVariable(var);
+ DebugPrintf("Var %d is %d\n", var, value);
+ }
+ } else
+ DebugPrintf("Var out of range (0 - 254)\n");
+ } else
+ DebugPrintf("Syntax: var <varnum> <value>\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_StartSubroutine(int argc, const char **argv) {
+ if (argc > 1) {
+ uint subroutine = atoi(argv[1]);
+ Subroutine *sub;
+ sub = _vm->getSubroutineByID(subroutine);
+ if (sub != NULL)
+ _vm->startSubroutine(sub);
+ } else
+ DebugPrintf("Subroutine %d\n", _vm->_subroutine);
+
+ return true;
+}
+
+} // End of namespace Simon
+
diff --git a/engines/simon/debugger.h b/engines/simon/debugger.h
new file mode 100644
index 0000000000..837fbbccf8
--- /dev/null
+++ b/engines/simon/debugger.h
@@ -0,0 +1,56 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SIMON_DEBUGGER_H
+#define SIMON_DEBUGGER_H
+
+#include "common/debugger.h"
+
+namespace Simon {
+
+class SimonEngine;
+
+class Debugger : public Common::Debugger<Debugger> {
+public:
+ Debugger(SimonEngine *vm);
+ virtual ~Debugger() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ
+
+protected:
+ SimonEngine *_vm;
+
+ virtual void preEnter();
+ virtual void postEnter();
+
+ bool Cmd_Exit(int argc, const char **argv);
+ bool Cmd_Help(int argc, const char **argv);
+ bool Cmd_DebugLevel(int argc, const char **argv);
+ bool Cmd_PlayMusic(int argc, const char **argv);
+ bool Cmd_PlaySound(int argc, const char **argv);
+ bool Cmd_PlayVoice(int argc, const char **argv);
+ bool Cmd_SetBit(int argc, const char **argv);
+ bool Cmd_SetVar(int argc, const char **argv);
+ bool Cmd_StartSubroutine(int argc, const char **argv);
+};
+
+} // End of namespace Simon
+
+#endif
diff --git a/engines/simon/game.cpp b/engines/simon/game.cpp
new file mode 100644
index 0000000000..1cd7e4d729
--- /dev/null
+++ b/engines/simon/game.cpp
@@ -0,0 +1,1186 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/md5.h"
+
+#include "simon/simon.h"
+#include "simon/intern.h"
+
+using Common::File;
+
+namespace Simon {
+
+static int detectGame(const FSList &fslist, bool mode = false, int start = -1);
+
+struct GameMD5 {
+ GameIds id;
+ const char *md5;
+ const char *filename;
+ bool caseSensitive;
+};
+
+#define FILE_MD5_BYTES 5000
+
+static GameMD5 gameMD5[] = {
+ { GID_SIMON1ACORNDEMO, "b4a7526ced425ba8ad0d548d0ec69900", "data", false },
+ { GID_SIMON1ACORNDEMO, "425c7d1957699d35abca7e12a08c7422", "gamebase", false },
+ { GID_SIMON1ACORNDEMO, "22107c24dfb31b66ac503c28a6e20b19", "icondata", false},
+ { GID_SIMON1ACORNDEMO, "d9de7542612d9f4e0819ad0df5eac56b", "stripped", false},
+ { GID_SIMON1ACORNDEMO, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1ACORN, "64958b3a38afdcb85da1eeed85169806", "data", false },
+ { GID_SIMON1ACORN, "28261b99cd9da1242189b4f6f2841bd6", "gamebase", false },
+ { GID_SIMON1ACORN, "22107c24dfb31b66ac503c28a6e20b19", "icondata", false},
+ { GID_SIMON1ACORN, "f3b27a3fbb45dcd323a48159496e45e8", "stripped", false},
+ { GID_SIMON1ACORN, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1AMIGA, "6c9ad2ff571d34a4cf0c696cf4e13500", "gameamiga", true },
+ { GID_SIMON1AMIGA, "565ef7a98dcc21ef526a2bb10b6f42ed", "icon.pkd", true },
+ { GID_SIMON1AMIGA, "c649fcc0439766810e5097ee7e81d4c8", "stripped.txt", true},
+ { GID_SIMON1AMIGA, "f9d5bf2ce09f82289c791c3ca26e1e4b", "tbllist", true},
+
+ { GID_SIMON1AMIGA_FR, "bd9828b9d4e5d89b50fe8c47a8e6bc07", "gameamiga", true },
+ { GID_SIMON1AMIGA_FR, "565ef7a98dcc21ef526a2bb10b6f42ed", "icon.pkd", true },
+ { GID_SIMON1AMIGA_FR, "2297baec985617d0d5612a0124bac359", "stripped.txt", true},
+ { GID_SIMON1AMIGA_FR, "f9d5bf2ce09f82289c791c3ca26e1e4b", "tbllist", true},
+
+ { GID_SIMON1AMIGA_DE, "a2de9553f3b73064369948b5af38bb30", "gameamiga", true },
+ { GID_SIMON1AMIGA_DE, "565ef7a98dcc21ef526a2bb10b6f42ed", "icon.pkd", true },
+ { GID_SIMON1AMIGA_DE, "c649fcc0439766810e5097ee7e81d4c8", "stripped.txt", true},
+ { GID_SIMON1AMIGA_DE, "f9d5bf2ce09f82289c791c3ca26e1e4b", "tbllist", true},
+
+ { GID_SIMON1AMIGADEMO, "a12b696170f14eca5ff75f1549829251", "gameamiga", true }, // Unpacked version
+ { GID_SIMON1AMIGADEMO, "ebc96af15bfaf75ba8210326b9260d2f", "icon.pkd", true },
+ { GID_SIMON1AMIGADEMO, "8edde5b9498dc9f31da1093028da467c", "stripped.txt", true},
+ { GID_SIMON1AMIGADEMO, "1247e024e1f13ca54c1e354120c7519c", "tbllist", true},
+
+ { GID_SIMON1CD32, "bab7f19237cf7d7619b6c73631da1854", "gameamiga", true },
+ { GID_SIMON1CD32, "565ef7a98dcc21ef526a2bb10b6f42ed", "icon.pkd", true },
+ { GID_SIMON1CD32, "59be788020441e21861e284236fd08c1", "stripped.txt", true},
+ { GID_SIMON1CD32, "f9d5bf2ce09f82289c791c3ca26e1e4b", "tbllist", true},
+
+ { GID_SIMON1CD32_2, "ec5358680c117f29b128cbbb322111a4", "gameamiga", true },
+ { GID_SIMON1CD32_2, "8ce5a46466a4f8f6d0f780b0ef00d5f5", "icon.pkd", true },
+ { GID_SIMON1CD32_2, "59be788020441e21861e284236fd08c1", "stripped.txt", true},
+ { GID_SIMON1CD32_2, "f9d5bf2ce09f82289c791c3ca26e1e4b", "tbllist", true},
+
+ { GID_SIMON1DOS_INF, "9f93d27432ce44a787eef10adb640870", "gamepc", false },
+ { GID_SIMON1DOS_INF, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1DOS_INF, "2af9affc5981eec44b90d4c556145cb8", "stripped.txt", false},
+ { GID_SIMON1DOS_INF, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1DOS_INF_RU,"605fb866e03ec1c41b10c6a518ddfa49", "gamepc", false },
+ { GID_SIMON1DOS_INF_RU,"22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1DOS_INF_RU,"2af9affc5981eec44b90d4c556145cb8", "stripped.txt", false},
+ { GID_SIMON1DOS_INF_RU,"d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1DOS, "c392e494dcabed797b98cbcfc687b33a", "gamepc", false },
+ { GID_SIMON1DOS, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1DOS, "c95a0a1ee973e19c2a1c5d12026c139f", "stripped.txt", false},
+ { GID_SIMON1DOS, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1DOS_RU, "605fb866e03ec1c41b10c6a518ddfa49", "gamepc", false },
+ { GID_SIMON1DOS_RU, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1DOS_RU, "c95a0a1ee973e19c2a1c5d12026c139f", "stripped.txt", false},
+ { GID_SIMON1DOS_RU, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1DOS_FR, "34759d0d4285a2f4b21b8e03b8fcefb3", "gamepc", false },
+ { GID_SIMON1DOS_FR, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1DOS_FR, "aa01e7386057abc0c3e27dbaa9c4ba5b", "stripped.txt", false},
+ { GID_SIMON1DOS_FR, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1DOS_DE, "063015e6ce7d90b570dbc21fe0c667b1", "gamepc", false },
+ { GID_SIMON1DOS_DE, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1DOS_DE, "c95a0a1ee973e19c2a1c5d12026c139f", "stripped.txt", false},
+ { GID_SIMON1DOS_DE, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1DOS_IT, "65c9b2dea57df84ef55d1eaf384ebd30", "gamepc", false },
+ { GID_SIMON1DOS_IT, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1DOS_IT, "2af9affc5981eec44b90d4c556145cb8", "stripped.txt", false},
+ { GID_SIMON1DOS_IT, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1DOS_ES, "5374fafdea2068134f33deab225feed3", "gamepc", false },
+ { GID_SIMON1DOS_ES, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1DOS_ES, "2af9affc5981eec44b90d4c556145cb8", "stripped.txt", false},
+ { GID_SIMON1DOS_ES, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1DEMO, "2be4a21bc76e2fdc071867c130651439", "gdemo", false },
+ { GID_SIMON1DEMO, "55af3b4d93972bc58bfee38a86b76c3f", "icon.dat", false},
+ { GID_SIMON1DEMO, "33a2e329b97b2a349858d6a093159eb7", "stripped.txt", false},
+ { GID_SIMON1DEMO, "1247e024e1f13ca54c1e354120c7519c", "tbllist", false},
+
+ { GID_SIMON1TALKIE, "28261b99cd9da1242189b4f6f2841bd6", "gamepc", false },
+ { GID_SIMON1TALKIE, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1TALKIE, "64958b3a38afdcb85da1eeed85169806", "simon.gme", false },
+ { GID_SIMON1TALKIE, "f3b27a3fbb45dcd323a48159496e45e8", "stripped.txt", false},
+ { GID_SIMON1TALKIE, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1TALKIE2, "c0b948b6821d2140f8b977144f21027a", "gamepc", false },
+ { GID_SIMON1TALKIE2, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1TALKIE2, "64f73e94639b63af846ac4a8a94a23d8", "simon.gme", false },
+ { GID_SIMON1TALKIE2, "f3b27a3fbb45dcd323a48159496e45e8", "stripped.txt", false},
+ { GID_SIMON1TALKIE2, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1TALKIE_FR, "3cfb9d1ff4ec725af9924140126cf69f", "gamepc", false },
+ { GID_SIMON1TALKIE_FR, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1TALKIE_FR, "638049fa5d41b81fb6fb11671721b871", "simon.gme", false },
+ { GID_SIMON1TALKIE_FR, "ef51ac74c946881ae4d7ca66cc7a0d1e", "stripped.txt", false},
+ { GID_SIMON1TALKIE_FR, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1TALKIE_DE, "48b1f3499e2e0d731047f4d481ff7817", "gamepc", false },
+ { GID_SIMON1TALKIE_DE, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1TALKIE_DE, "7db9912acac4f1d965a64bdcfc370ba1", "simon.gme", false },
+ { GID_SIMON1TALKIE_DE, "40d68bec54042ef930f084ad9a4342a1", "stripped.txt", false},
+ { GID_SIMON1TALKIE_DE, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1TALKIE_HB, "bc66e9c0b296e1b155a246917133f71a", "gamepc", false },
+ { GID_SIMON1TALKIE_HB, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1TALKIE_HB, "a34b2c8642f2e3676d7088b5c8b3e884", "simon.gme", false },
+ { GID_SIMON1TALKIE_HB, "9d31bef42db1a8abe4e9f368014df1d5", "stripped.txt", false},
+ { GID_SIMON1TALKIE_HB, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1TALKIE_IT, "8d3ca654e158c91b860c7eae31d65312", "gamepc", false },
+ { GID_SIMON1TALKIE_IT, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1TALKIE_IT, "104efd83c8f3edf545982e07d87f66ac", "simon.gme", false },
+ { GID_SIMON1TALKIE_IT, "9d31bef42db1a8abe4e9f368014df1d5", "stripped.txt", false},
+ { GID_SIMON1TALKIE_IT, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1TALKIE_ES, "439f801ba52c02c9d1844600d1ce0f5e", "gamepc", false },
+ { GID_SIMON1TALKIE_ES, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1TALKIE_ES, "eff2774a73890b9eac533db90cd1afa1", "simon.gme", false },
+ { GID_SIMON1TALKIE_ES, "9d31bef42db1a8abe4e9f368014df1d5", "stripped.txt", false},
+ { GID_SIMON1TALKIE_ES, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1WIN, "c7c12fea7f6d0bfd22af5cdbc8166862", "gamepc", false },
+ { GID_SIMON1WIN, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1WIN, "b1b18d0731b64c0738c5cc4a2ee792fc", "simon.gme", false },
+ { GID_SIMON1WIN, "a27e87a9ba21212d769804b3df47bfb2", "stripped.txt", false},
+ { GID_SIMON1WIN, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON1WIN_DE, "48b1f3499e2e0d731047f4d481ff7817", "gamepc", false },
+ { GID_SIMON1WIN_DE, "22107c24dfb31b66ac503c28a6e20b19", "icon.dat", false},
+ { GID_SIMON1WIN_DE, "acd9cc438525b142d93b15c77a6f551b", "simon.gme", false },
+ { GID_SIMON1WIN_DE, "40d68bec54042ef930f084ad9a4342a1", "stripped.txt", false},
+ { GID_SIMON1WIN_DE, "d198a80de2c59e4a0cd24b98814849e8", "tbllist", false},
+
+ { GID_SIMON2DOS, "27c8e7feada80c75b70b9c2f6088d519", "game32", false },
+ { GID_SIMON2DOS, "ee92d1f84893195a60449f2430d07285", "icon.dat", false},
+ { GID_SIMON2DOS, "eefcc32b1f2c0482c1a59a963a146345", "simon2.gme", false},
+ { GID_SIMON2DOS, "e229f84d46fa83f99b4a7115679f3fb6", "stripped.txt", false},
+ { GID_SIMON2DOS, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2DOS_RU, "7edfc633dd50f8caa719c478443db70b", "game32", false },
+ { GID_SIMON2DOS_RU, "ee92d1f84893195a60449f2430d07285", "icon.dat", false},
+ { GID_SIMON2DOS_RU, "eefcc32b1f2c0482c1a59a963a146345", "simon2.gme", false},
+ { GID_SIMON2DOS_RU, "e229f84d46fa83f99b4a7115679f3fb6", "stripped.txt", false},
+ { GID_SIMON2DOS_RU, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2DOS2, "604d04315935e77624bd356ac926e068", "game32", false },
+ { GID_SIMON2DOS2, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2DOS2, "aa6840420899a31874204f90bb214108", "simon2.gme", false},
+ { GID_SIMON2DOS2, "e229f84d46fa83f99b4a7115679f3fb6", "stripped.txt", false},
+ { GID_SIMON2DOS2, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2DOS2_RU, "7edfc633dd50f8caa719c478443db70b", "game32", false },
+ { GID_SIMON2DOS2_RU, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2DOS2_RU, "aa6840420899a31874204f90bb214108", "simon2.gme", false},
+ { GID_SIMON2DOS2_RU, "e229f84d46fa83f99b4a7115679f3fb6", "stripped.txt", false},
+ { GID_SIMON2DOS2_RU, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2DOS_IT, "3e11d400bea0638f360a724687005cd1", "game32", false },
+ { GID_SIMON2DOS_IT, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2DOS_IT, "f306a397565d7f13bec7ecf14c723de7", "simon2.gme", false},
+ { GID_SIMON2DOS_IT, "bea6843fb9f3b2144fcb146d62db0b9a", "stripped.txt", false},
+ { GID_SIMON2DOS_IT, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2DEMO, "3794c15887539b8578bacab694ccf08a", "gsptr30", false },
+ { GID_SIMON2DEMO, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2DEMO, "f8c9e6df1e55923a749e115ba74210c4", "simon2.gme", false},
+ { GID_SIMON2DEMO, "e229f84d46fa83f99b4a7115679f3fb6", "stripped.txt", false},
+ { GID_SIMON2DEMO, "a0d5a494b5d3d209d1a1d76cc8d76601", "tbllist", false},
+
+ { GID_SIMON2TALKIE, "8c301fb9c4fcf119d2730ccd2a565eb3", "gsptr30", false },
+ { GID_SIMON2TALKIE, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2TALKIE, "9c535d403966750ae98bdaf698375a38", "simon2.gme", false },
+ { GID_SIMON2TALKIE, "e229f84d46fa83f99b4a7115679f3fb6", "stripped.txt", false},
+ { GID_SIMON2TALKIE, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2TALKIE2, "608e277904d87dd28725fa08eacc2c0d", "gsptr30", false },
+ { GID_SIMON2TALKIE2, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2TALKIE2, "8d6dcc65577e285dbca03ff6d7d9323c", "simon2.gme", false },
+ { GID_SIMON2TALKIE2, "e229f84d46fa83f99b4a7115679f3fb6", "stripped.txt", false},
+ { GID_SIMON2TALKIE2, "a0d5a494b5d3d209d1a1d76cc8d76601", "tbllist", false},
+
+ { GID_SIMON2TALKIE_FR, "43b3a04d2f0a0cbd1b024c814856561a", "gsptr30", false },
+ { GID_SIMON2TALKIE_FR, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2TALKIE_FR, "8af0e02c0c3344db64dffc12196eb59d", "simon2.gme", false },
+ { GID_SIMON2TALKIE_FR, "5ea27977b4d7dcfd50eb5074e162ebbf", "stripped.txt", false},
+ { GID_SIMON2TALKIE_FR, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2TALKIE_DE, "0d05c3f4c06c9a4ceb3d2f5bc0b18e11", "gsptr30", false },
+ { GID_SIMON2TALKIE_DE, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2TALKIE_DE, "6c5fdfdd0eab9038767c2d22858406b2", "simon2.gme", false },
+ { GID_SIMON2TALKIE_DE, "6de6292c9ac11bfb2e70fdb0f773ba85", "stripped.txt", false},
+ { GID_SIMON2TALKIE_DE, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2TALKIE_DE2,"a76ea940076b5d9316796dea225a9b69", "gsptr30", false },
+ { GID_SIMON2TALKIE_DE2,"72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2TALKIE_DE2,"ec9f0f24fd895e7ea72e3c8e448c0240", "simon2.gme", false },
+ { GID_SIMON2TALKIE_DE2,"6de6292c9ac11bfb2e70fdb0f773ba85", "stripped.txt", false},
+ { GID_SIMON2TALKIE_DE2,"2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2TALKIE_HB, "952a2b1be23c3c609ba8d988a9a1627d", "gsptr30", false },
+ { GID_SIMON2TALKIE_HB, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2TALKIE_HB, "a2b249a82ea182af09789eb95fb6c5be", "simon2.gme", false },
+ { GID_SIMON2TALKIE_HB, "de9dbc24158660e153483fa0cf6c3172", "stripped.txt", false},
+ { GID_SIMON2TALKIE_HB, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2TALKIE_IT, "3e11d400bea0638f360a724687005cd1", "gsptr30", false },
+ { GID_SIMON2TALKIE_IT, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2TALKIE_IT, "344aca58e5ad5e25c517d5eb1d85c435", "simon2.gme", false },
+ { GID_SIMON2TALKIE_IT, "bea6843fb9f3b2144fcb146d62db0b9a", "stripped.txt", false},
+ { GID_SIMON2TALKIE_IT, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2TALKIE_ES, "268dc322aa73bcf27bb016b8e8ceb889", "gsptr30", false },
+ { GID_SIMON2TALKIE_ES, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2TALKIE_ES, "4f43bd06b6cc78dbd25a7475ca964eb1", "simon2.gme", false },
+ { GID_SIMON2TALKIE_ES, "d13753796bd81bf313a2449f34d8b112", "stripped.txt", false},
+ { GID_SIMON2TALKIE_ES, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2WIN, "608e277904d87dd28725fa08eacc2c0d", "gsptr30", false },
+ { GID_SIMON2WIN, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2WIN, "e749c4c103d7e7d51b34620ed76c5a04", "simon2.gme", false },
+ { GID_SIMON2WIN, "e229f84d46fa83f99b4a7115679f3fb6", "stripped.txt", false},
+ { GID_SIMON2WIN, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2WIN_DE, "a76ea940076b5d9316796dea225a9b69", "gsptr30", false },
+ { GID_SIMON2WIN_DE, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2WIN_DE, "9609a933c541fed2e00c6c3479d7c181", "simon2.gme", false },
+ { GID_SIMON2WIN_DE, "6de6292c9ac11bfb2e70fdb0f773ba85", "stripped.txt", false},
+ { GID_SIMON2WIN_DE, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2WIN_DE2, "9e858b3bb189c134c3a5f34c3385a8d3", "gsptr30", false },
+ { GID_SIMON2WIN_DE2, "ee92d1f84893195a60449f2430d07285", "icon.dat", false},
+ { GID_SIMON2WIN_DE2, "16d574da07e93bcae43cee353dab8c7e", "simon2.gme", false },
+ { GID_SIMON2WIN_DE2, "6de6292c9ac11bfb2e70fdb0f773ba85", "stripped.txt", false},
+ { GID_SIMON2WIN_DE2, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_SIMON2WIN_PL, "657fd873f5d0637097ee02315b447e6f", "gsptr30", false },
+ { GID_SIMON2WIN_PL, "72096a62d36e6034ea9fecc13b2dbdab", "icon.dat", false},
+ { GID_SIMON2WIN_PL, "7b9afcf82a94722707e0d025c0192be8", "simon2.gme", false },
+ { GID_SIMON2WIN_PL, "e229f84d46fa83f99b4a7115679f3fb6", "stripped.txt", false},
+ { GID_SIMON2WIN_PL, "2082f8d02075e590300478853a91ffd9", "tbllist", false},
+
+ { GID_FEEBLEFILES_2CD, "629762ea9ca9ee9ff85f4774d219f5c7", "game22", false },
+ { GID_FEEBLEFILES_2CD, "0bbfee8e69739111eb36b0d138da8ddf", "tbllist", false},
+
+ { GID_FEEBLEFILES_4CD, "a8746407a5b20a7da0da0a14c380af1c", "game22", false },
+ { GID_FEEBLEFILES_4CD, "0bbfee8e69739111eb36b0d138da8ddf", "tbllist", false},
+
+ { GID_FEEBLEFILES_DE, "bcd76ac080003eee3649df18db25b60e", "game22", false },
+ { GID_FEEBLEFILES_DE, "0bbfee8e69739111eb36b0d138da8ddf", "tbllist", false},
+};
+
+// Simon the Sorcerer 1
+static GameFileDescription SIMON1CD32_GameFiles[] = {
+ {"gameamiga", GAME_BASEFILE},
+ {"icon.pkd", GAME_ICONFILE},
+ {"stripped.txt", GAME_STRFILE},
+ {"tbllist", GAME_TBLFILE},
+};
+
+static GameFileDescription SIMON1ACORN_GameFiles[] = {
+ {"data", GAME_GMEFILE},
+ {"gamebase", GAME_BASEFILE},
+ {"icondata", GAME_ICONFILE},
+ {"stripped", GAME_STRFILE},
+ {"tbllist", GAME_TBLFILE},
+};
+
+static GameFileDescription SIMON1DEMO_GameFiles[] = {
+ {"gdemo", GAME_BASEFILE},
+ {"icon.dat", GAME_ICONFILE},
+ {"stripped.txt", GAME_STRFILE},
+ {"tbllist", GAME_TBLFILE},
+};
+
+static GameFileDescription SIMON1DOS_GameFiles[] = {
+ {"gamepc", GAME_BASEFILE},
+ {"icon.dat", GAME_ICONFILE},
+ {"stripped.txt", GAME_STRFILE},
+ {"tbllist", GAME_TBLFILE},
+};
+
+static GameFileDescription SIMON1_GameFiles[] = {
+ {"gamepc", GAME_BASEFILE},
+ {"icon.dat", GAME_ICONFILE},
+ {"simon.gme", GAME_GMEFILE},
+ {"stripped.txt", GAME_STRFILE},
+ {"tbllist", GAME_TBLFILE},
+};
+
+// Simon the Sorcerer 2
+static GameFileDescription SIMON2DOS_GameFiles[] = {
+ {"game32", GAME_BASEFILE},
+ {"icon.dat", GAME_ICONFILE},
+ {"simon2.gme", GAME_GMEFILE},
+ {"stripped.txt", GAME_STRFILE},
+ {"tbllist", GAME_TBLFILE},
+};
+
+static GameFileDescription SIMON2_GameFiles[] = {
+ {"gsptr30", GAME_BASEFILE},
+ {"icon.dat", GAME_ICONFILE},
+ {"simon2.gme", GAME_GMEFILE},
+ {"stripped.txt", GAME_STRFILE},
+ {"tbllist", GAME_TBLFILE},
+};
+
+// The Feeble Files
+static GameFileDescription FEEBLEFILES_GameFiles[] = {
+ {"game22", GAME_BASEFILE},
+ {"tbllist", GAME_TBLFILE},
+};
+
+static GameDescription gameDescriptions[] = {
+ // Simon the Sorcerer 1 - English Acorn CD Demo
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1ACORNDEMO,
+ "Simon the Sorcerer 1 (English Acorn CD Demo)",
+ ARRAYSIZE(SIMON1ACORN_GameFiles),
+ SIMON1ACORN_GameFiles,
+ GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformAcorn,
+ },
+
+ // Simon the Sorcerer 1 - English Acorn CD
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1ACORN,
+ "Simon the Sorcerer 1 (English Acorn CD)",
+ ARRAYSIZE(SIMON1ACORN_GameFiles),
+ SIMON1ACORN_GameFiles,
+ GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformAcorn,
+ },
+
+ // Simon the Sorcerer 1 - English AGA Floppy
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1AMIGA,
+ "Simon the Sorcerer 1 (English Amiga AGA Floppy)",
+ ARRAYSIZE(SIMON1CD32_GameFiles),
+ SIMON1CD32_GameFiles,
+ GF_CRUNCHED | GF_OLD_BUNDLE,
+ Common::EN_USA,
+ Common::kPlatformAmiga,
+ },
+
+ // Simon the Sorcerer 1 - French AGA Floppy
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1AMIGA_FR,
+ "Simon the Sorcerer 1 (French Amiga AGA Floppy)",
+ ARRAYSIZE(SIMON1CD32_GameFiles),
+ SIMON1CD32_GameFiles,
+ GF_CRUNCHED | GF_OLD_BUNDLE,
+ Common::FR_FRA,
+ Common::kPlatformAmiga,
+ },
+
+ // Simon the Sorcerer 1 - German AGA Floppy
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1AMIGA_DE,
+ "Simon the Sorcerer 1 (German Amiga AGA Floppy)",
+ ARRAYSIZE(SIMON1CD32_GameFiles),
+ SIMON1CD32_GameFiles,
+ GF_CRUNCHED | GF_OLD_BUNDLE,
+ Common::DE_DEU,
+ Common::kPlatformAmiga,
+ },
+
+ // Simon the Sorcerer 1 - English Amiga ECS Demo
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1AMIGADEMO,
+ "Simon the Sorcerer 1 (English Amiga ECS Demo)",
+ ARRAYSIZE(SIMON1CD32_GameFiles),
+ SIMON1CD32_GameFiles,
+ GF_CRUNCHED | GF_OLD_BUNDLE,
+ Common::EN_USA,
+ Common::kPlatformAmiga,
+ },
+
+ // Simon the Sorcerer 1 - English Amiga CD32
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1CD32,
+ "Simon the Sorcerer 1 (English Amiga CD32)",
+ ARRAYSIZE(SIMON1CD32_GameFiles),
+ SIMON1CD32_GameFiles,
+ GF_TALKIE | GF_OLD_BUNDLE,
+ Common::EN_USA,
+ Common::kPlatformAmiga,
+ },
+
+ // Simon the Sorcerer 1 - English Amiga CD32 alternative?
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1CD32_2,
+ "Simon the Sorcerer 1 (English Amiga CD32)",
+ ARRAYSIZE(SIMON1CD32_GameFiles),
+ SIMON1CD32_GameFiles,
+ GF_TALKIE | GF_OLD_BUNDLE,
+ Common::EN_USA,
+ Common::kPlatformAmiga,
+ },
+
+ // Simon the Sorcerer 1 - English DOS Floppy Demo
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1DEMO,
+ "Simon the Sorcerer 1 (English DOS Floppy Demo)",
+ ARRAYSIZE(SIMON1DEMO_GameFiles),
+ SIMON1DEMO_GameFiles,
+ GF_OLD_BUNDLE,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - English DOS Floppy
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1DOS,
+ "Simon the Sorcerer 1 (English DOS Floppy)",
+ ARRAYSIZE(SIMON1DOS_GameFiles),
+ SIMON1DOS_GameFiles,
+ GF_OLD_BUNDLE,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - English DOS Floppy with Russian patch
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1DOS_RU,
+ "Simon the Sorcerer 1 (Russian DOS Floppy)",
+ ARRAYSIZE(SIMON1DOS_GameFiles),
+ SIMON1DOS_GameFiles,
+ GF_OLD_BUNDLE,
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - English DOS Floppy (Infocom)
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1DOS_INF,
+ "Simon the Sorcerer 1 (English DOS Floppy)",
+ ARRAYSIZE(SIMON1DOS_GameFiles),
+ SIMON1DOS_GameFiles,
+ GF_OLD_BUNDLE,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - English DOS Floppy (Infocom) with Russian patch
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1DOS_INF_RU,
+ "Simon the Sorcerer 1 (Russian DOS Floppy)",
+ ARRAYSIZE(SIMON1DOS_GameFiles),
+ SIMON1DOS_GameFiles,
+ GF_OLD_BUNDLE,
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - French DOS Floppy
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1DOS_FR,
+ "Simon the Sorcerer 1 (French DOS Floppy)",
+ ARRAYSIZE(SIMON1DOS_GameFiles),
+ SIMON1DOS_GameFiles,
+ GF_OLD_BUNDLE,
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - German DOS Floppy
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1DOS_DE,
+ "Simon the Sorcerer 1 (German DOS Floppy)",
+ ARRAYSIZE(SIMON1DOS_GameFiles),
+ SIMON1DOS_GameFiles,
+ GF_OLD_BUNDLE,
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - Italian DOS Floppy
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1DOS_IT,
+ "Simon the Sorcerer 1 (Italian DOS Floppy)",
+ ARRAYSIZE(SIMON1DOS_GameFiles),
+ SIMON1DOS_GameFiles,
+ GF_OLD_BUNDLE,
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - Spanish DOS Floppy
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1DOS_ES,
+ "Simon the Sorcerer 1 (Spanish DOS Floppy)",
+ ARRAYSIZE(SIMON1DOS_GameFiles),
+ SIMON1DOS_GameFiles,
+ GF_OLD_BUNDLE,
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - English DOS CD
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1TALKIE,
+ "Simon the Sorcerer 1 (English DOS CD)",
+ ARRAYSIZE(SIMON1_GameFiles),
+ SIMON1_GameFiles,
+ GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - English DOS CD alternate?
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1TALKIE2,
+ "Simon the Sorcerer 1 (English DOS CD)",
+ ARRAYSIZE(SIMON1_GameFiles),
+ SIMON1_GameFiles,
+ GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - French DOS CD
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1TALKIE_FR,
+ "Simon the Sorcerer 1 (French DOS CD)",
+ ARRAYSIZE(SIMON1_GameFiles),
+ SIMON1_GameFiles,
+ GF_TALKIE,
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - German DOS CD
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1TALKIE_DE,
+ "Simon the Sorcerer 1 (German DOS CD)",
+ ARRAYSIZE(SIMON1_GameFiles),
+ SIMON1_GameFiles,
+ GF_TALKIE,
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - Hebrew DOS CD
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1TALKIE_HB,
+ "Simon the Sorcerer 1 (Hebrew DOS CD)",
+ ARRAYSIZE(SIMON1_GameFiles),
+ SIMON1_GameFiles,
+ GF_TALKIE,
+ Common::HB_ISR,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - Italian DOS CD
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1TALKIE_IT,
+ "Simon the Sorcerer 1 (Italian DOS CD)",
+ ARRAYSIZE(SIMON1_GameFiles),
+ SIMON1_GameFiles,
+ GF_TALKIE,
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - Spanish DOS CD
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1TALKIE_ES,
+ "Simon the Sorcerer 1 (Spanish DOS CD)",
+ ARRAYSIZE(SIMON1_GameFiles),
+ SIMON1_GameFiles,
+ GF_TALKIE,
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 1 - English Windows CD
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1WIN,
+ "Simon the Sorcerer 1 (English Windows CD)",
+ ARRAYSIZE(SIMON1_GameFiles),
+ SIMON1_GameFiles,
+ GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformWindows,
+ },
+
+ // Simon the Sorcerer 1 - German Windows CD
+ {
+ "simon1",
+ GType_SIMON1,
+ GID_SIMON1WIN_DE,
+ "Simon the Sorcerer 1 (German Windows CD)",
+ ARRAYSIZE(SIMON1_GameFiles),
+ SIMON1_GameFiles,
+ GF_TALKIE,
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ },
+
+ // Simon the Sorcerer 2 - English DOS Floppy
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2DOS,
+ "Simon the Sorcerer 2 (English DOS Floppy)",
+ ARRAYSIZE(SIMON2DOS_GameFiles),
+ SIMON2DOS_GameFiles,
+ 0,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - English DOS Floppy with Russian patch
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2DOS_RU,
+ "Simon the Sorcerer 2 (Russian DOS Floppy)",
+ ARRAYSIZE(SIMON2DOS_GameFiles),
+ SIMON2DOS_GameFiles,
+ 0,
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - English DOS Floppy alternate?
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2DOS2,
+ "Simon the Sorcerer 2 (English DOS Floppy)",
+ ARRAYSIZE(SIMON2DOS_GameFiles),
+ SIMON2DOS_GameFiles,
+ 0,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - English DOS Floppy alternate? with Russian patch
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2DOS2_RU,
+ "Simon the Sorcerer 2 (Russian DOS Floppy)",
+ ARRAYSIZE(SIMON2DOS_GameFiles),
+ SIMON2DOS_GameFiles,
+ 0,
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - Italian DOS Floppy
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2DOS_IT,
+ "Simon the Sorcerer 2 (Italian DOS Floppy)",
+ ARRAYSIZE(SIMON2DOS_GameFiles),
+ SIMON2DOS_GameFiles,
+ 0,
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - English DOS CD Demo
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2DEMO,
+ "Simon the Sorcerer 2 (English DOS CD Demo)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - English DOS CD
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2TALKIE,
+ "Simon the Sorcerer 2 (English DOS CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+
+ // Simon the Sorcerer 2 - English DOS CD alternate?
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2TALKIE2,
+ "Simon the Sorcerer 2 (English DOS CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - French DOS CD
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2TALKIE_FR,
+ "Simon the Sorcerer 2 (French DOS CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - German DOS CD
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2TALKIE_DE,
+ "Simon the Sorcerer 2 (German DOS CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - German DOS CD alternate?
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2TALKIE_DE2,
+ "Simon the Sorcerer 2 (German DOS CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - Hebrew DOS CD
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2TALKIE_HB,
+ "Simon the Sorcerer 2 (Hebrew DOS CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::HB_ISR,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - Italian DOS CD
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2TALKIE_IT,
+ "Simon the Sorcerer 2 (Italian DOS CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::IT_ITA,
+ // FIXME: DOS version which uses WAV format
+ Common::kPlatformWindows,
+ },
+
+ // Simon the Sorcerer 2 - Spanish DOS CD
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2TALKIE_ES,
+ "Simon the Sorcerer 2 (Spanish DOS CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ },
+
+ // Simon the Sorcerer 2 - English Windows CD
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2WIN,
+ "Simon the Sorcerer 2 (English Windows CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformWindows,
+ },
+
+ // Simon the Sorcerer 2 - German Windows CD
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2WIN_DE,
+ "Simon the Sorcerer 2 (German Windows CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ },
+
+ // Simon the Sorcerer 2 - German Windows CD 1.1
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2WIN_DE2,
+ "Simon the Sorcerer 2 (German Windows CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ },
+
+ // Simon the Sorcerer 2 - Polish Windows CD
+ {
+ "simon2",
+ GType_SIMON2,
+ GID_SIMON2WIN_PL,
+ "Simon the Sorcerer 2 (Polish Windows CD)",
+ ARRAYSIZE(SIMON2_GameFiles),
+ SIMON2_GameFiles,
+ GF_TALKIE,
+ Common::PL_POL,
+ Common::kPlatformWindows,
+ },
+
+ // The Feeble Files - English 2CD
+ {
+ "feeble",
+ GType_FF,
+ GID_FEEBLEFILES_2CD,
+ "The Feeble Files (English 2CD)",
+ ARRAYSIZE(FEEBLEFILES_GameFiles),
+ FEEBLEFILES_GameFiles,
+ GF_OLD_BUNDLE | GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformWindows,
+ },
+
+ // The Feeble Files - English 4CD
+ {
+ "feeble",
+ GType_FF,
+ GID_FEEBLEFILES_4CD,
+ "The Feeble Files (English 4CD)",
+ ARRAYSIZE(FEEBLEFILES_GameFiles),
+ FEEBLEFILES_GameFiles,
+ GF_OLD_BUNDLE | GF_TALKIE,
+ Common::EN_USA,
+ Common::kPlatformWindows,
+ },
+
+ // The Feeble Files - German 4CD
+ {
+ "feeble",
+ GType_FF,
+ GID_FEEBLEFILES_DE,
+ "The Feeble Files (German 4CD)",
+ ARRAYSIZE(FEEBLEFILES_GameFiles),
+ FEEBLEFILES_GameFiles,
+ GF_OLD_BUNDLE | GF_TALKIE,
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ },
+};
+
+bool SimonEngine::initGame(void) {
+ int gameNumber;
+ FSList dummy;
+
+ if ((gameNumber = detectGame(dummy)) == -1) {
+ warning("No valid games were found in the specified directory.");
+ return false;
+ }
+
+ debug(0, "Running %s", gameDescriptions[gameNumber].title);
+
+ _gameDescription = &gameDescriptions[gameNumber];
+
+ return true;
+}
+
+DetectedGameList GAME_ProbeGame(const FSList &fslist, int **retmatches) {
+ DetectedGameList detectedGames;
+ int game_n;
+ int index = 0, i, j;
+ int matches[ARRAYSIZE(gameDescriptions)];
+ bool mode = retmatches ? false : true;
+
+ game_n = -1;
+ for (i = 0; i < ARRAYSIZE(gameDescriptions); i++)
+ matches[i] = -1;
+
+ while (1) {
+ game_n = detectGame(fslist, mode, game_n);
+ if (game_n == -1)
+ break;
+ matches[index++] = game_n;
+ }
+
+ // We have some resource sets which are superpositions of other
+ // Particularly it is ite-demo-linux vs ite-demo-win
+ // Now remove lesser set if bigger matches too
+
+ if (index > 1) {
+ // Search max number
+ int maxcount = 0;
+ for (i = 0; i < index; i++) {
+ int count = 0;
+ for (j = 0; j < ARRAYSIZE(gameMD5); j++)
+ if (gameMD5[j].id == gameDescriptions[matches[i]].gameId)
+ count++;
+ maxcount = MAX(maxcount, count);
+ }
+
+ // Now purge targets with number of files lesser than max
+ for (i = 0; i < index; i++) {
+ int count = 0;
+ for (j = 0; j < ARRAYSIZE(gameMD5); j++)
+ if (gameMD5[j].id == gameDescriptions[matches[i]].gameId)
+ count++;
+ if (count < maxcount) {
+ debug(2, "Purged: %s", gameDescriptions[matches[i]].title);
+ matches[i] = -1;
+ }
+ }
+
+ }
+
+ // and now push them into list of detected games
+ for (i = 0; i < index; i++)
+ if (matches[i] != -1)
+ detectedGames.push_back(DetectedGame(gameDescriptions[matches[i]].toGameSettings(),
+ gameDescriptions[matches[i]].language,
+ gameDescriptions[matches[i]].platform));
+
+ if (retmatches) {
+ *retmatches = (int *)calloc(ARRAYSIZE(gameDescriptions), sizeof(int));
+ for (i = 0; i < ARRAYSIZE(gameDescriptions); i++)
+ (*retmatches)[i] = matches[i];
+ }
+
+ return detectedGames;
+}
+
+int detectGame(const FSList &fslist, bool mode, int start) {
+ int game_count = ARRAYSIZE(gameDescriptions);
+ int game_n = -1;
+ typedef Common::Map<Common::String, Common::String> StringMap;
+ StringMap filesMD5;
+
+ typedef Common::Map<Common::String, bool> StringSet;
+ StringSet filesList;
+
+ uint16 file_count;
+ uint16 file_n;
+ Common::File test_file;
+ bool file_missing;
+
+ Common::String tstr, tstr1;
+ char md5str[32+1];
+ uint8 md5sum[16];
+
+ // First we compose list of files which we need MD5s for
+ for (int i = 0; i < ARRAYSIZE(gameMD5); i++) {
+ tstr = Common::String(gameMD5[i].filename);
+ tstr.toLowercase();
+
+ if (gameMD5[i].caseSensitive && !mode)
+ filesList[Common::String(gameMD5[i].filename)] = true;
+ else
+ filesList[tstr] = true;
+ }
+
+ if (mode) {
+ // Now count MD5s for required files
+ for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (!file->isDirectory()) {
+ tstr = file->displayName();
+ // FIXME: there is a bug in String class. tstr1 = tstr; tstr.toLowercase()
+ // makes tstr1 lowercase as well
+ tstr1 = Common::String(file->displayName().c_str());
+ tstr.toLowercase();
+
+ if (filesList.contains(tstr) || filesList.contains(tstr1)) {
+ if (Common::md5_file(file->path().c_str(), md5sum, NULL, FILE_MD5_BYTES)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+ filesMD5[tstr] = Common::String(md5str);
+ filesMD5[tstr1] = Common::String(md5str);
+ }
+ }
+ }
+ }
+ } else {
+ Common::File testFile;
+
+ for (StringSet::const_iterator file = filesList.begin(); file != filesList.end(); ++file) {
+ if (testFile.open(file->_key.c_str())) {
+ testFile.close();
+ if (Common::md5_file(file->_key.c_str(), md5sum, NULL, FILE_MD5_BYTES)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j*2, "%02x", (int)md5sum[j]);
+ }
+ filesMD5[file->_key] = Common::String(md5str);
+ }
+ }
+ }
+ }
+
+ for (game_n = start + 1; game_n < game_count; game_n++) {
+ file_count = gameDescriptions[game_n].filesCount;
+ file_missing = false;
+
+ // Try to open all files for this game
+ for (file_n = 0; file_n < file_count; file_n++) {
+ tstr = gameDescriptions[game_n].filesDescriptions[file_n].fileName;
+
+ if (!filesMD5.contains(tstr)) {
+ file_missing = true;
+ break;
+ }
+ }
+
+ // Try the next game, couldn't find all files for the current
+ // game
+ if (file_missing) {
+ continue;
+ } else {
+ bool match = true;
+
+ debug(0, "Probing game: %s", gameDescriptions[game_n].title);
+
+ for (int i = 0; i < ARRAYSIZE(gameMD5); i++) {
+ if (gameMD5[i].id == gameDescriptions[game_n].gameId) {
+ tstr = gameMD5[i].filename;
+
+ if (strcmp(gameMD5[i].md5, filesMD5[tstr].c_str())) {
+ match = false;
+ break;
+ }
+ }
+ }
+ if (!match)
+ continue;
+
+ debug(0, "Found game: %s", gameDescriptions[game_n].title);
+
+ return game_n;
+ }
+ }
+
+ if (!filesMD5.isEmpty() && start == -1) {
+ printf("MD5s of your game version are unknown. Please, report following data to\n");
+ printf("ScummVM team along with your game name and version:\n");
+
+ for (StringMap::const_iterator file = filesMD5.begin(); file != filesMD5.end(); ++file)
+ printf("%s: %s\n", file->_key.c_str(), file->_value.c_str());
+ }
+
+ return -1;
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/icons.cpp b/engines/simon/icons.cpp
new file mode 100644
index 0000000000..d9f7692e81
--- /dev/null
+++ b/engines/simon/icons.cpp
@@ -0,0 +1,229 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/file.h"
+
+#include "simon/simon.h"
+#include "simon/intern.h"
+
+namespace Simon {
+
+void SimonEngine::loadIconFile() {
+ Common::File in;
+ if (getPlatform() == Common::kPlatformAcorn)
+ in.open("ICONDATA");
+ else if (getPlatform() == Common::kPlatformAmiga)
+ in.open("icon.pkd");
+ else
+ in.open("ICON.DAT");
+ uint size;
+
+ if (in.isOpen() == false)
+ error("Can't open icons file 'ICON.DAT'");
+
+ size = in.size();
+
+ _iconFilePtr = (byte *)malloc(size);
+ if (_iconFilePtr == NULL)
+ error("Out of icon memory");
+
+ in.read(_iconFilePtr, size);
+ in.close();
+}
+
+// Thanks to Stuart Caie for providing the original
+// C conversion upon which this function is based.
+void decompress_icon_amiga (byte *dst, byte *src, byte base, uint pitch) {
+ byte icon_pln[288];
+ byte *i, *o, x, y;
+
+ // Decode RLE planar icon data
+ i = src;
+ o = icon_pln;
+ while (o < &icon_pln[288]) {
+ x = *i++;
+ if (x < 128) {
+ do {
+ *o++ = *i++;
+ *o++ = *i++;
+ *o++ = *i++;
+ } while (x-- > 0);
+ } else {
+ x = 256 - x;
+ do {
+ *o++ = i[0];
+ *o++ = i[1];
+ *o++ = i[2];
+ } while (x-- > 0);
+ i += 3;
+ }
+ }
+
+ // Translate planar data to chunky (very slow method)
+ for (y = 0; y < 24; y++) {
+ for (x = 0; x < 24; x++) {
+ byte pixel =
+ (icon_pln[(( y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 1 : 0)
+ | (icon_pln[((24 + y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 2 : 0)
+ | (icon_pln[((48 + y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 4 : 0)
+ | (icon_pln[((72 + y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 8 : 0);
+ if (pixel)
+ dst[x] = pixel | base;
+ }
+ dst += pitch;
+ }
+}
+
+static void decompress_icon(byte *dst, byte *src, uint w, uint h_org, byte base, uint pitch) {
+ int8 reps;
+ byte color_1, color_2;
+ byte *dst_org = dst;
+ uint h = h_org;
+
+ for (;;) {
+ reps = *src++;
+ if (reps < 0) {
+ reps--;
+ color_1 = *src >> 4;
+ if (color_1 != 0)
+ color_1 |= base;
+ color_2 = *src++ & 0xF;
+ if (color_2 != 0)
+ color_2 |= base;
+
+ do {
+ if (color_1 != 0)
+ *dst = color_1;
+ dst += pitch;
+ if (color_2 != 0)
+ *dst = color_2;
+ dst += pitch;
+
+ // reached bottom?
+ if (--h == 0) {
+ // reached right edge?
+ if (--w == 0)
+ return;
+ dst = ++dst_org;
+ h = h_org;
+ }
+ } while (++reps != 0);
+ } else {
+ do {
+ color_1 = *src >> 4;
+ if (color_1 != 0)
+ *dst = color_1 | base;
+ dst += pitch;
+
+ color_2 = *src++ & 0xF;
+ if (color_2 != 0)
+ *dst = color_2 | base;
+ dst += pitch;
+
+ // reached bottom?
+ if (--h == 0) {
+ // reached right edge?
+ if (--w == 0)
+ return;
+ dst = ++dst_org;
+ h = h_org;
+ }
+ } while (--reps >= 0);
+ }
+ }
+}
+
+
+void SimonEngine::draw_icon_c(FillOrCopyStruct *fcs, uint icon, uint x, uint y) {
+ byte *dst;
+ byte *src;
+
+ _lockWord |= 0x8000;
+ dst = dx_lock_2();
+
+ if (!(getGameType() == GType_SIMON2)) {
+ // Simon 1
+ dst += (x + fcs->x) * 8;
+ dst += (y * 25 + fcs->y) * _dxSurfacePitch;
+
+ if (getPlatform() == Common::kPlatformAmiga) {
+ src = _iconFilePtr;
+ src += READ_BE_UINT32(&((uint32 *)src)[icon]);
+ decompress_icon_amiga (dst, src, 0xE0, _dxSurfacePitch);
+ } else {
+ src = _iconFilePtr;
+ src += READ_LE_UINT16(&((uint16 *)src)[icon]);
+ decompress_icon(dst, src, 24, 12, 0xE0, _dxSurfacePitch);
+ }
+ } else {
+ // Simon 2
+ dst += 110;
+ dst += x;
+ dst += (y + fcs->y) * _dxSurfacePitch;
+
+ src = _iconFilePtr;
+ src += READ_LE_UINT16(&((uint16 *)src)[icon * 2 + 0]);
+ decompress_icon(dst, src, 20, 10, 0xE0, _dxSurfacePitch);
+
+ src = _iconFilePtr;
+ src += READ_LE_UINT16(&((uint16 *)src)[icon * 2 + 1]);
+ decompress_icon(dst, src, 20, 10, 0xD0, _dxSurfacePitch);
+ }
+
+ dx_unlock_2();
+ _lockWord &= ~0x8000;
+}
+
+uint SimonEngine::setup_icon_hit_area(FillOrCopyStruct *fcs, uint x, uint y, uint icon_number,
+ Item *item_ptr) {
+ HitArea *ha;
+
+ ha = findEmptyHitArea();
+
+ if (!(getGameType() == GType_SIMON2)) {
+ ha->x = (x + fcs->x) << 3;
+ ha->y = y * 25 + fcs->y;
+ ha->item_ptr = item_ptr;
+ ha->width = 24;
+ ha->height = 24;
+ ha->flags = 0xB0;
+ ha->id = 0x7FFD;
+ ha->layer = 100;
+ ha->unk3 = 0xD0;
+ } else {
+ ha->x = x + 110;
+ ha->y = fcs->y + y;
+ ha->item_ptr = item_ptr;
+ ha->width = 20;
+ ha->height = 20;
+ ha->flags = 0xB0;
+ ha->id = 0x7FFD;
+ ha->layer = 100;
+ ha->unk3 = 0xD0;
+ }
+
+ return ha - _hitAreas;
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/intern.h b/engines/simon/intern.h
new file mode 100644
index 0000000000..aa60d0245f
--- /dev/null
+++ b/engines/simon/intern.h
@@ -0,0 +1,217 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SIMON_INTERN_H
+#define SIMON_INTERN_H
+
+namespace Simon {
+
+struct Child {
+ Child *next;
+ uint16 type;
+};
+
+struct Child1 : Child {
+ uint16 subroutine_id;
+ uint16 fr2;
+ uint16 array[1];
+};
+
+struct Child2 : Child {
+ uint16 string_id;
+ uint32 avail_props;
+ int16 array[1];
+};
+
+struct Child9 : Child {
+ uint16 array[4];
+};
+
+enum {
+ CHILD1_SIZE = sizeof(Child1) - sizeof(uint16),
+ CHILD2_SIZE = sizeof(Child2) - sizeof(int16)
+};
+
+struct Item {
+ uint16 parent;
+ uint16 child;
+ uint16 sibling;
+ int16 noun;
+ int16 adjective;
+ int16 state; /* signed int */
+ uint16 classFlags;
+ Child *children;
+
+ Item() { memset(this, 0, sizeof(*this)); }
+};
+
+struct Subroutine {
+ uint16 id; /* subroutine ID */
+ uint16 first; /* offset from subroutine start to first subroutine line */
+ Subroutine *next; /* next subroutine in linked list */
+};
+
+struct FillOrCopyDataEntry {
+ Item *item;
+ uint16 hit_area;
+ uint16 xxx_1;
+};
+
+struct FillOrCopyData {
+ int16 unk1;
+ Item *item_ptr;
+ FillOrCopyDataEntry e[64];
+ int16 upArrow, downArrow;
+ uint16 unk2;
+};
+
+struct FillOrCopyStruct {
+ byte mode;
+ byte flags;
+ uint16 x, y;
+ uint16 width, height;
+ uint16 textColumn, textRow;
+ uint8 textColumnOffset, textLength, textMaxLength;
+ uint8 fill_color, text_color, unk5;
+ FillOrCopyData *fcs_data;
+ FillOrCopyStruct() { memset(this, 0, sizeof(*this)); }
+};
+// note on text offset:
+// the actual x-coordinate is: textColumn * 8 + textColumnOffset
+// the actual y-coordinate is: textRow * 8
+
+
+enum {
+ SUBROUTINE_LINE_SMALL_SIZE = 2,
+ SUBROUTINE_LINE_BIG_SIZE = 8
+};
+
+struct SubroutineLine {
+ uint16 next;
+ int16 verb;
+ int16 noun1;
+ int16 noun2;
+};
+
+struct TimeEvent {
+ uint32 time;
+ uint16 subroutine_id;
+ TimeEvent *next;
+};
+
+struct GameSpecificSettings {
+#ifndef PALMOS_68K
+ const char *gme_filename;
+ const char *wav_filename;
+ const char *voc_filename;
+ const char *mp3_filename;
+ const char *vorbis_filename;
+ const char *flac_filename;
+ const char *voc_effects_filename;
+ const char *mp3_effects_filename;
+ const char *vorbis_effects_filename;
+ const char *flac_effects_filename;
+ const char *gamepc_filename;
+ #else
+ const char gme_filename[12];
+ const char wav_filename[12];
+ const char voc_filename[12];
+ const char mp3_filename[12];
+ const char flac_filename[12];
+ const char vorbis_filename[12];
+ const char voc_effects_filename[12];
+ const char mp3_effects_filename[12];
+ const char vorbis_effects_filename[12];
+ const char flac_effects_filename[12];
+ const char gamepc_filename[12];
+ #endif
+};
+
+} // End of namespace Simon
+
+enum GameFeatures {
+ GF_TALKIE = 1 << 0,
+ GF_OLD_BUNDLE = 1 << 1,
+ GF_CRUNCHED = 1 << 2
+};
+
+enum GameFileTypes {
+ GAME_BASEFILE = 1 << 0,
+ GAME_ICONFILE = 1 << 1,
+ GAME_GMEFILE = 1 << 2,
+ GAME_STRFILE = 1 << 3,
+ GAME_TBLFILE = 1 << 4
+};
+
+enum GameIds {
+ GID_SIMON1DOS,
+ GID_SIMON1DOS_RU,
+ GID_SIMON1DOS_INF,
+ GID_SIMON1DOS_INF_RU,
+ GID_SIMON1DOS_DE,
+ GID_SIMON1DOS_FR,
+ GID_SIMON1DOS_IT,
+ GID_SIMON1DOS_ES,
+ GID_SIMON1DEMO,
+ GID_SIMON1AMIGA,
+ GID_SIMON1AMIGA_FR,
+ GID_SIMON1AMIGA_DE,
+ GID_SIMON1AMIGADEMO,
+ GID_SIMON1CD32,
+ GID_SIMON1CD32_2,
+ GID_SIMON1ACORN,
+ GID_SIMON1ACORNDEMO,
+ GID_SIMON1TALKIE,
+ GID_SIMON1TALKIE2,
+ GID_SIMON1TALKIE_DE,
+ GID_SIMON1TALKIE_FR,
+ GID_SIMON1TALKIE_HB,
+ GID_SIMON1TALKIE_IT,
+ GID_SIMON1TALKIE_ES,
+ GID_SIMON1WIN,
+ GID_SIMON1WIN_DE,
+
+ GID_SIMON2DOS,
+ GID_SIMON2DOS_RU,
+ GID_SIMON2DOS2,
+ GID_SIMON2DOS2_RU,
+ GID_SIMON2DOS_IT,
+ GID_SIMON2DEMO,
+ GID_SIMON2TALKIE,
+ GID_SIMON2TALKIE2,
+ GID_SIMON2TALKIE_DE,
+ GID_SIMON2TALKIE_DE2,
+ GID_SIMON2TALKIE_FR,
+ GID_SIMON2TALKIE_HB,
+ GID_SIMON2TALKIE_IT,
+ GID_SIMON2TALKIE_ES,
+ GID_SIMON2WIN,
+ GID_SIMON2WIN_DE,
+ GID_SIMON2WIN_DE2,
+ GID_SIMON2WIN_PL,
+
+ GID_FEEBLEFILES_2CD,
+ GID_FEEBLEFILES_4CD,
+ GID_FEEBLEFILES_DE
+};
+
+#endif
diff --git a/engines/simon/items.cpp b/engines/simon/items.cpp
new file mode 100644
index 0000000000..d1df1aa82c
--- /dev/null
+++ b/engines/simon/items.cpp
@@ -0,0 +1,1685 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Item script opcodes for Simon1/Simon2
+
+#include "common/stdafx.h"
+#include "simon/simon.h"
+#include "simon/intern.h"
+
+#include "common/system.h"
+
+#ifdef _WIN32_WCE
+extern bool isSmartphone(void);
+#endif
+
+namespace Simon {
+
+int SimonEngine::runScript() {
+ byte opcode;
+ bool flag, condition;
+
+ do {
+ if (_continousMainScript)
+ dumpOpcode(_codePtr);
+
+ opcode = getByte();
+ if (opcode == 0xFF)
+ return 0;
+
+ if (_runScriptReturn1)
+ return 1;
+
+ /* Invert condition? */
+ flag = false;
+ if (opcode == 0) {
+ flag = true;
+ opcode = getByte();
+ if (opcode == 0xFF)
+ return 0;
+ }
+
+ condition = true;
+
+ switch (opcode) {
+ case 1:{ /* ptrA parent is */
+ condition = (getItem1Ptr()->parent == getNextItemID());
+ }
+ break;
+
+ case 2:{ /* ptrA parent is not */
+ condition = (getItem1Ptr()->parent != getNextItemID());
+ }
+ break;
+
+ case 5:{ /* parent is 1 */
+ condition = (getNextItemPtr()->parent == getItem1ID());
+ }
+ break;
+
+ case 6:{ /* parent isnot 1 */
+ condition = (getNextItemPtr()->parent != getItem1ID());
+ }
+ break;
+
+ case 7:{ /* parent is */
+ Item *item = getNextItemPtr();
+ condition = (item->parent == getNextItemID());
+ }
+ break;
+
+ case 11:{ /* is zero */
+ condition = (getNextVarContents() == 0);
+ }
+ break;
+
+ case 12:{ /* isnot zero */
+ condition = (getNextVarContents() != 0);
+ }
+ break;
+
+ case 13:{ /* equal */
+ uint tmp = getNextVarContents();
+ condition = (tmp == getVarOrWord());
+ }
+ break;
+
+ case 14:{ /* not equal */
+ uint tmp = getNextVarContents();
+ condition = (tmp != getVarOrWord());
+ }
+ break;
+
+ case 15:{ /* is greater */
+ uint tmp = getNextVarContents();
+ condition = (tmp > getVarOrWord());
+ }
+ break;
+
+ case 16:{ /* is less */
+ uint tmp = getNextVarContents();
+ condition = (tmp < getVarOrWord());
+ }
+ break;
+
+ case 17:{ /* is eq f */
+ uint tmp = getNextVarContents();
+ condition = (tmp == getNextVarContents());
+ }
+ break;
+
+ case 18:{ /* is not equal f */
+ uint tmp = getNextVarContents();
+ condition = (tmp != getNextVarContents());
+ }
+ break;
+
+ case 19:{ /* is greater f */
+ uint tmp = getNextVarContents();
+ condition = (tmp < getNextVarContents());
+ }
+ break;
+
+ case 20:{ /* is less f */
+ uint tmp = getNextVarContents();
+ condition = (tmp > getNextVarContents());
+ }
+ break;
+
+ case 23:{
+ condition = o_chance(getVarOrWord());
+ }
+ break;
+
+ case 25:{ /* is room */
+ condition = isRoom(getNextItemPtr());
+ }
+ break;
+
+ case 26:{ /* is object */
+ condition = isObject(getNextItemPtr());
+ }
+ break;
+
+ case 27:{ /* item state is */
+ Item *item = getNextItemPtr();
+ condition = ((uint) item->state == getVarOrWord());
+ }
+ break;
+
+ case 28:{ /* item has prop */
+ Child2 *child = (Child2 *)findChildOfType(getNextItemPtr(), 2);
+ byte num = getVarOrByte();
+ condition = child != NULL && (child->avail_props & (1 << num)) != 0;
+ } break;
+
+ case 31:{ /* set no parent */
+ setItemParent(getNextItemPtr(), NULL);
+ }
+ break;
+
+ case 33:{ /* set item parent */
+ Item *item = getNextItemPtr();
+ setItemParent(item, getNextItemPtr());
+ }
+ break;
+
+ case 36:{ /* copy var */
+ uint value = getNextVarContents();
+ writeNextVarContents(value);
+ }
+ break;
+
+ case 41:{ /* zero var */
+ writeNextVarContents(0);
+ }
+ break;
+
+ case 42:{ /* set var */
+ uint var = getVarOrByte();
+ writeVariable(var, getVarOrWord());
+ }
+ break;
+
+ case 43:{ /* add */
+ uint var = getVarOrByte();
+ writeVariable(var, readVariable(var) + getVarOrWord());
+ }
+ break;
+
+ case 44:{ /* sub */
+ uint var = getVarOrByte();
+ writeVariable(var, readVariable(var) - getVarOrWord());
+ }
+ break;
+
+ case 45:{ /* add f */
+ uint var = getVarOrByte();
+ writeVariable(var, readVariable(var) + getNextVarContents());
+ }
+ break;
+
+ case 46:{ /* sub f */
+ uint var = getVarOrByte();
+ writeVariable(var, readVariable(var) - getNextVarContents());
+ }
+ break;
+
+ case 47:{ /* mul */
+ uint var = getVarOrByte();
+ writeVariable(var, readVariable(var) * getVarOrWord());
+ }
+ break;
+
+ case 48:{ /* div */
+ uint var = getVarOrByte();
+ int value = getVarOrWord();
+ if (value == 0)
+ error("Division by zero in div");
+ writeVariable(var, readVariable(var) / value);
+ }
+ break;
+
+ case 49:{ /* mul f */
+ uint var = getVarOrByte();
+ writeVariable(var, readVariable(var) * getNextVarContents());
+ }
+ break;
+
+ case 50:{ /* div f */
+ uint var = getVarOrByte();
+ int value = getNextVarContents();
+ if (value == 0)
+ error("Division by zero in div f");
+ writeVariable(var, readVariable(var) / value);
+ }
+ break;
+
+ case 51:{ /* mod */
+ uint var = getVarOrByte();
+ int value = getVarOrWord();
+ if (value == 0)
+ error("Division by zero in mod");
+ writeVariable(var, readVariable(var) % value);
+ }
+ break;
+
+ case 52:{ /* mod f */
+ uint var = getVarOrByte();
+ int value = getNextVarContents();
+ if (value == 0)
+ error("Division by zero in mod f");
+ writeVariable(var, readVariable(var) % value);
+ }
+ break;
+
+ case 53:{ /* random */
+ uint var = getVarOrByte();
+ uint value = (uint16)getVarOrWord();
+
+ // Disable random in simon1amiga for now
+ // Since copy protection screen is currently unreadable
+ if (getPlatform() == Common::kPlatformAmiga)
+ writeVariable(var, 4);
+ else
+ writeVariable(var, _rnd.getRandomNumber(value - 1));
+ }
+ break;
+
+ case 55:{ /* set itemA parent */
+ setItemParent(getItem1Ptr(), getNextItemPtr());
+ }
+ break;
+
+ case 56:{ /* set child2 fr bit */
+ Child2 *child = (Child2 *)findChildOfType(getNextItemPtr(), 2);
+ int value = getVarOrByte();
+ if (child != NULL && value >= 0x10)
+ child->avail_props |= 1 << value;
+ }
+ break;
+
+ case 57:{ /* clear child2 fr bit */
+ Child2 *child = (Child2 *)findChildOfType(getNextItemPtr(), 2);
+ int value = getVarOrByte();
+ if (child != NULL && value >= 0x10)
+ child->avail_props &= ~(1 << value);
+ }
+ break;
+
+ case 58:{ /* make siblings */
+ Item *item = getNextItemPtr();
+ setItemParent(item, derefItem(getNextItemPtr()->parent));
+ }
+ break;
+
+ case 59:{ /* item inc state */
+ Item *item = getNextItemPtr();
+ if (item->state <= 30000)
+ setItemState(item, item->state + 1);
+ }
+ break;
+
+ case 60:{ /* item dec state */
+ Item *item = getNextItemPtr();
+ if (item->state >= 0)
+ setItemState(item, item->state - 1);
+ }
+ break;
+
+ case 61:{ /* item set state */
+ Item *item = getNextItemPtr();
+ int value = getVarOrWord();
+ if (value < 0)
+ value = 0;
+ if (value > 30000)
+ value = 30000;
+ setItemState(item, value);
+ }
+ break;
+
+ case 62:{ /* show int */
+ showMessageFormat("%d", getNextVarContents());
+ }
+ break;
+
+ case 63:{ /* show string nl */
+ showMessageFormat("%s\n", getStringPtrByID(getNextStringID()));
+ }
+ break;
+
+ case 64:{ /* show string */
+ showMessageFormat("%s", getStringPtrByID(getNextStringID()));
+ }
+ break;
+
+ case 65:{ /* add hit area */
+ int id = getVarOrWord();
+ int x = getVarOrWord();
+ int y = getVarOrWord();
+ int w = getVarOrWord();
+ int h = getVarOrWord();
+ int number = getVarOrByte();
+ if (number < 20)
+ addNewHitArea(id, x, y, w, h, (number << 8) + 129, 0xD0, _dummyItem2);
+ }
+ break;
+
+ case 66:{ /* set item name */
+ uint var = getVarOrByte();
+ uint string_id = getNextStringID();
+ if (var < 20)
+ _stringIdArray2[var] = string_id;
+ }
+ break;
+
+ case 67:{ /* set item description */
+ uint var = getVarOrByte();
+ uint string_id = getNextStringID();
+ if (getFeatures() & GF_TALKIE) {
+ uint speechId = getNextWord();
+ if (var < 20) {
+ _stringIdArray3[var] = string_id;
+ _speechIdArray4[var] = speechId;
+ }
+ } else {
+ if (var < 20) {
+ _stringIdArray3[var] = string_id;
+ }
+ }
+ }
+ break;
+
+ case 68:{ /* exit interpreter */
+ shutdown();
+ }
+ break;
+
+ case 69:{ /* return 1 */
+ return 1;
+ }
+
+ case 70:{ /* show string from array */
+ const char *str = (const char *)getStringPtrByID(_stringIdArray3[getVarOrByte()]);
+
+ if (getGameType() == GType_SIMON2) {
+ writeVariable(51, strlen(str) / 53 * 8 + 8);
+ }
+
+ showMessageFormat("%s\n", str);
+ }
+ break;
+
+ case 71:{ /* start subroutine */
+ Subroutine *sub = getSubroutineByID(getVarOrWord());
+ if (sub != NULL)
+ startSubroutine(sub);
+ }
+ break;
+
+ case 76:{ /* add timeout */
+ uint timeout = getVarOrWord();
+ addTimeEvent(timeout, getVarOrWord());
+ }
+ break;
+
+ case 77:{ /* has item minus 1 */
+ condition = _subjectItem != NULL;
+ }
+ break;
+
+ case 78:{ /* has item minus 3 */
+ condition = _objectItem != NULL;
+ }
+ break;
+
+ case 79:{ /* childstruct fr2 is */
+ Child2 *child = (Child2 *)findChildOfType(getNextItemPtr(), 2);
+ uint string_id = getNextStringID();
+ condition = (child != NULL) && child->string_id == string_id;
+ }
+ break;
+
+ case 80:{ /* item equal */
+ condition = getNextItemPtr() == getNextItemPtr();
+ }
+ break;
+
+ case 82:{ /* debug opcode */
+ getVarOrByte();
+ }
+ break;
+
+ case 83:{ /* restart subroutine */
+ if (getGameType() == GType_SIMON2 || getGameType() == GType_FF)
+ o_83_helper();
+ return -10;
+ }
+
+ case 87:{ /* comment */
+ getNextStringID();
+ }
+ break;
+
+ case 88:{ /* stop animation */
+ _lockWord |= 0x10;
+ }
+ break;
+
+ case 89:{ /* restart animation */
+ _lockWord &= ~0x10;
+ }
+ break;
+
+ case 90:{ /* set minusitem to parent */
+ Item *item = derefItem(getNextItemPtr()->parent);
+ switch (getVarOrByte()) {
+ case 0:
+ _objectItem = item;
+ break;
+ case 1:
+ _subjectItem = item;
+ break;
+ default:
+ error("set minusitem to parent, invalid subcode");
+ }
+ }
+ break;
+
+ case 91:{ /* set minusitem to sibling */
+ Item *item = derefItem(getNextItemPtr()->sibling);
+ switch (getVarOrByte()) {
+ case 0:
+ _objectItem = item;
+ break;
+ case 1:
+ _subjectItem = item;
+ break;
+ default:
+ error("set minusitem to sibling, invalid subcode");
+ }
+ }
+ break;
+
+ case 92:{ /* set minusitem to child */
+ Item *item = derefItem(getNextItemPtr()->child);
+ switch (getVarOrByte()) {
+ case 0:
+ _objectItem = item;
+ break;
+ case 1:
+ _subjectItem = item;
+ break;
+ default:
+ error("set minusitem to child, invalid subcode");
+ }
+ }
+ break;
+
+ case 96:{
+ uint val = getVarOrWord();
+ o_set_video_mode(getVarOrByte(), val);
+ }
+ break;
+
+ case 97:{ /* load vga */
+ o_loadZone(getVarOrWord());
+ }
+ break;
+
+ case 98:{ /* start vga */
+ uint vga_res, vgaSpriteId, windowNum, x, y, palette;
+ if (getGameType() == GType_SIMON1) {
+ vgaSpriteId = getVarOrWord();
+ vga_res = vgaSpriteId / 100;
+ } else {
+ vga_res = getVarOrWord();
+ vgaSpriteId = getVarOrWord();
+ }
+ windowNum = getVarOrByte();
+ x = getVarOrWord();
+ y = getVarOrWord();
+ palette = getVarOrWord();
+ loadSprite(windowNum, vga_res, vgaSpriteId, x, y, palette);
+ }
+ break;
+
+ case 99:{ /* kill sprite */
+ if (getGameType() == GType_SIMON1) {
+ o_kill_sprite_simon1(getVarOrWord());
+ } else {
+ uint a = getVarOrWord();
+ uint b = getVarOrWord();
+ o_kill_sprite_simon2(a, b);
+ }
+ }
+ break;
+
+ case 100:{ /* vga reset */
+ o_vga_reset();
+ }
+ break;
+
+ case 101:{
+ uint a = getVarOrByte();
+ uint b = getVarOrWord();
+ uint c = getVarOrWord();
+ uint d = getVarOrWord();
+ uint e = getVarOrWord();
+ uint f = getVarOrWord();
+ uint g = getVarOrWord();
+ o_defineWindow(a, b, c, d, e, f, g, 0);
+ }
+ break;
+
+ case 102:{
+ changeWindow(getVarOrByte() & 7);
+ }
+ break;
+
+ case 103:{
+ o_unk_103();
+ }
+ break;
+
+ case 104:{
+ closeWindow(getVarOrByte() & 7);
+ }
+ break;
+
+ case 107:{ /* add item hitarea */
+ uint flags = 0;
+ uint id = getVarOrWord();
+ uint params = id / 1000;
+ uint x, y, w, h, unk3;
+ Item *item;
+
+ id = id % 1000;
+
+ if (params & 1)
+ flags |= 8;
+ if (params & 2)
+ flags |= 4;
+ if (params & 4)
+ flags |= 0x80;
+ if (params & 8)
+ flags |= 1;
+ if (params & 16)
+ flags |= 0x10;
+
+ x = getVarOrWord();
+ y = getVarOrWord();
+ w = getVarOrWord();
+ h = getVarOrWord();
+ item = getNextItemPtrStrange();
+ unk3 = getVarOrWord();
+ if (x >= 1000) {
+ unk3 += 0x4000;
+ x -= 1000;
+ }
+ addNewHitArea(id, x, y, w, h, flags, unk3, item);
+ }
+ break;
+
+ case 108:{ /* delete hitarea */
+ delete_hitarea(getVarOrWord());
+ }
+ break;
+
+ case 109:{ /* clear hitarea bit 0x40 */
+ clear_hitarea_bit_0x40(getVarOrWord());
+ }
+ break;
+
+ case 110:{ /* set hitarea bit 0x40 */
+ set_hitarea_bit_0x40(getVarOrWord());
+ }
+ break;
+
+ case 111:{ /* set hitarea xy */
+ uint hitarea_id = getVarOrWord();
+ uint x = getVarOrWord();
+ uint y = getVarOrWord();
+ set_hitarea_x_y(hitarea_id, x, y);
+ }
+ break;
+
+ case 114:{
+ Item *item = getNextItemPtr();
+ uint fcs_index = getVarOrByte();
+ mouseOff();
+ drawIconArray(fcs_index, item, 0, 0);
+ mouseOn();
+ }
+ break;
+
+ case 115:{ /* item has flag */
+ Item *item = getNextItemPtr();
+ condition = (item->classFlags & (1 << getVarOrByte())) != 0;
+ }
+ break;
+
+ case 116:{ /* item set flag */
+ Item *item = getNextItemPtr();
+ item->classFlags |= (1 << getVarOrByte());
+ }
+ break;
+
+ case 117:{ /* item clear flag */
+ Item *item = getNextItemPtr();
+ item->classFlags &= ~(1 << getVarOrByte());
+ }
+ break;
+
+ case 119:{ /* wait vga */
+ uint var = getVarOrWord();
+ _scriptVar2 = (var == 200);
+
+ if (var != 200 || !_skipVgaWait)
+ o_waitForSync(var);
+ _skipVgaWait = false;
+ }
+ break;
+
+ case 120:{
+ o_sync(getVarOrWord());
+ }
+ break;
+
+ case 121:{ /* set vga item */
+ uint slot = getVarOrByte();
+ _vcItemArray[slot] = getNextItemPtr();
+ }
+ break;
+
+ case 122:{ /* oracle text down */
+ if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2)
+ goto invalid_opcode;
+
+ warning("STUB: script opcode 122");
+ }
+ break;
+
+ case 123:{ /* oracle text down */
+ if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2)
+ goto invalid_opcode;
+
+ warning("STUB: script opcode 123");
+ }
+ break;
+
+ case 124:{ /* if time */
+ if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2)
+ goto invalid_opcode;
+
+ uint time = getVarOrWord();
+ condition = 1;
+ warning("STUB: script opcode 124 (%d)", time);
+ }
+ break;
+
+ case 125:{ /* item is sibling with item 1 */
+ Item *item = getNextItemPtr();
+ condition = (getItem1Ptr()->parent == item->parent);
+ }
+ break;
+
+ case 126:{
+ Item *item = getNextItemPtr();
+ uint fcs_index = getVarOrByte();
+ uint a = 1 << getVarOrByte();
+ mouseOff();
+ drawIconArray(fcs_index, item, 1, a);
+ mouseOn();
+ }
+ break;
+
+ case 127:{ /* deals with music */
+ o_playMusic();
+ }
+ break;
+
+ case 128:{ /* dummy instruction */
+ getVarOrWord();
+ }
+ break;
+
+ case 129:{ /* dummy instruction */
+ getVarOrWord();
+ condition = true;
+ }
+ break;
+
+ case 130:{ /* set adj noun */
+ uint var = getVarOrByte();
+ if (var == 1) {
+ _scriptAdj1 = getNextWord();
+ _scriptNoun1 = getNextWord();
+ } else {
+ _scriptAdj2 = getNextWord();
+ _scriptNoun2 = getNextWord();
+ }
+ }
+ break;
+
+ case 132:{ /* save game */
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+ o_saveGame();
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ }
+ break;
+
+ case 133:{ /* load game */
+ if (getGameType() == GType_FF) {
+ loadGame(readVariable(55));
+ } else {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+ o_loadGame();
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ }
+ }
+ break;
+
+ case 134:{ /* dummy opcode? */
+ if (getGameType() == GType_FF) {
+ warning("STUB: script opcode 134");
+ } else {
+ midi.stop();
+ _lastMusicPlayed = -1;
+ }
+ }
+ break;
+
+ case 135:{ /* quit if user presses y */
+ if (getGameType() == GType_FF) {
+ // Switch CD
+ debug(1, "Switch to CD number %d", readVariable(97));
+ } else {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+ o_confirmQuit();
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ }
+ }
+ break;
+
+ case 136:{ /* set var to item unk3 */
+ Item *item = getNextItemPtr();
+ writeNextVarContents(item->state);
+ }
+ break;
+
+ case 137:{
+ o_restoreIconArray(getVarOrByte());
+ }
+ break;
+
+ case 138:{ /* vga pointer op 4 */
+ o_freezeBottom();
+ }
+ break;
+
+ case 139:{ /* set parent special */
+ Item *item = getNextItemPtr();
+ _noParentNotify = true;
+ setItemParent(item, getNextItemPtr());
+ _noParentNotify = false;
+ }
+ break;
+
+ case 140:{ /* del te and add one */
+ killAllTimers();
+ addTimeEvent(3, 0xA0);
+ }
+ break;
+
+ case 141:{ /* set m1 or m3 */
+ uint which = getVarOrByte();
+ Item *item = getNextItemPtr();
+ if (which == 1) {
+ _subjectItem = item;
+ } else {
+ _objectItem = item;
+ }
+ }
+ break;
+
+ case 142:{ /* is hitarea 0x40 clear */
+ condition = is_hitarea_0x40_clear(getVarOrWord());
+ }
+ break;
+
+ case 143:{ /* start item sub */
+ Child1 *child = (Child1 *)findChildOfType(getNextItemPtr(), 1);
+ if (child != NULL) {
+ Subroutine *sub = getSubroutineByID(child->subroutine_id);
+ if (sub)
+ startSubroutine(sub);
+ }
+ }
+ break;
+
+ case 151:{ /* set array6 to item */
+ uint var = getVarOrByte();
+ Item *item = getNextItemPtr();
+ _itemArray6[var] = item;
+ }
+ break;
+
+ case 152:{ /* set m1 or m3 to array6 */
+ Item *item = _itemArray6[getVarOrByte()];
+ uint var = getVarOrByte();
+ if (var == 1) {
+ _subjectItem = item;
+ } else {
+ _objectItem = item;
+ }
+ }
+ break;
+
+ case 153:{ /* set bit */
+ uint bit = getVarOrByte();
+ _bitArray[bit / 16] |= 1 << (bit & 15);
+ break;
+ }
+
+ case 154:{ /* clear bit */
+ uint bit = getVarOrByte();
+ _bitArray[bit / 16] &= ~(1 << (bit & 15));
+ break;
+ }
+
+ case 155:{ /* is bit clear */
+ uint bit = getVarOrByte();
+ condition = (_bitArray[bit / 16] & (1 << (bit & 15))) == 0;
+ }
+ break;
+
+ case 156:{ /* is bit set */
+ uint bit = getVarOrByte();
+ if (getGameType() == GType_SIMON1 && _subroutine == 2962 && bit == 63) {
+ bit = 50;
+ }
+ condition = (_bitArray[bit / 16] & (1 << (bit & 15))) != 0;
+ }
+ break;
+
+ case 157:{ /* get item int prop */
+ Item *item = getNextItemPtr();
+ Child2 *child = (Child2 *)findChildOfType(item, 2);
+ uint prop = getVarOrByte();
+
+ if (child != NULL && child->avail_props & (1 << prop) && prop < 16) {
+ uint offs = getOffsetOfChild2Param(child, 1 << prop);
+ writeNextVarContents(child->array[offs]);
+ } else {
+ writeNextVarContents(0);
+ }
+ }
+ break;
+
+ case 158:{ /* set item prop */
+ Item *item = getNextItemPtr();
+ Child2 *child = (Child2 *)findChildOfType(item, 2);
+ uint prop = getVarOrByte();
+ int value = getVarOrWord();
+
+ if (child != NULL && child->avail_props & (1 << prop) && prop < 16) {
+ uint offs = getOffsetOfChild2Param(child, 1 << prop);
+ child->array[offs] = value;
+ }
+ }
+ break;
+
+ case 160:{
+ o_unk_160(getVarOrByte());
+ }
+ break;
+
+ case 161:{ /* setup text */
+ TextLocation *tl = getTextLocation(getVarOrByte());
+
+ tl->x = getVarOrWord();
+ tl->y = getVarOrByte();
+ tl->width = getVarOrWord();
+ }
+ break;
+
+ case 162:{ /* print string */
+ o_printStr();
+ }
+ break;
+
+ case 163:{ /* play sound */
+ o_playSFX(getVarOrWord());
+ }
+ break;
+
+ case 164:{
+ _showPreposition = true;
+ o_setup_cond_c();
+ _showPreposition = false;
+ }
+ break;
+
+ case 165:{ /* item unk1 unk2 is */
+ Item *item = getNextItemPtr();
+ int16 a = getNextWord(), b = getNextWord();
+ condition = (item->adjective == a && item->noun == b);
+ } break;
+
+ case 166:{ /* set bit2 */
+ uint bit = getVarOrByte();
+ _bitArray[(bit / 16) + 16] |= 1 << (bit & 15);
+ }
+ break;
+
+ case 167:{ /* clear bit2 */
+ uint bit = getVarOrByte();
+ _bitArray[(bit / 16) + 16] &= ~(1 << (bit & 15));
+ }
+ break;
+
+ case 168:{ /* is bit2 clear */
+ uint bit = getVarOrByte();
+ condition = (_bitArray[(bit / 16) + 16] & (1 << (bit & 15))) == 0;
+ }
+ break;
+
+ case 169:{ /* is bit2 set */
+ uint bit = getVarOrByte();
+ condition = (_bitArray[(bit / 16) + 16] & (1 << (bit & 15))) != 0;
+ }
+ break;
+
+ case 175:{ /* vga pointer op 1 */
+ o_lockZone();
+ }
+ break;
+
+ case 176:{ /* vga pointer op 2 */
+ o_unlockZone();
+ }
+ break;
+
+ case 177:{ /* inventory descriptions */
+ o_inventory_descriptions();
+ }
+ break;
+
+ case 178:{ /* path find */
+ uint a = getVarOrWord();
+ uint b = getVarOrWord();
+ uint c = getVarOrByte();
+ uint d = getVarOrByte();
+ o_pathfind(a, b, c, d);
+ }
+ break;
+
+ case 179:{ /* conversation responses */
+ uint vgaSpriteId = getVarOrByte(); /* and room descriptions */
+ uint color = getVarOrByte();
+ uint string_id = getVarOrByte();
+ uint speechId = 0;
+
+ const char *string_ptr = (const char *)getStringPtrByID(_stringIdArray3[string_id]);
+ TextLocation *tl = getTextLocation(vgaSpriteId);
+ if (getFeatures() & GF_TALKIE)
+ speechId = _speechIdArray4[string_id];
+
+ if (_speech && speechId != 0)
+ playSpeech(speechId, vgaSpriteId);
+ if (string_ptr != NULL && _subtitles)
+ printText(vgaSpriteId, color, string_ptr, tl->x, tl->y, tl->width);
+ }
+ break;
+
+ case 180:{ /* force mouseOn */
+ o_mouseOn();
+ }
+ break;
+
+ case 181:{ /* force mouseOff */
+ o_mouseOff();
+ if (getGameType() == GType_SIMON2) {
+ changeWindow(1);
+ showMessageFormat("\xC");
+ }
+ }
+ break;
+
+ case 182:{ /* load beard */
+ if (getGameType() == GType_FF) {
+ // Load video file
+ debug(1,"Load video file: %s", getStringPtrByID(getNextStringID()));
+ } else if (getGameType() == GType_SIMON2) {
+ goto invalid_opcode;
+ } else {
+ o_loadBeard();
+ }
+ }
+ break;
+
+ case 183:{ /* unload beard */
+ if (getGameType() == GType_FF) {
+ // Play video
+ debug(1, "Play video");
+ } else if (getGameType() == GType_SIMON2) {
+ goto invalid_opcode;
+ } else {
+ o_unloadBeard();
+ }
+ }
+ break;
+
+ case 184:{ /* clear vgapointer entry */
+ o_unloadZone(getVarOrWord());
+ }
+ break;
+
+ case 185:{ /* load sound files */
+ if (getGameType() == GType_SIMON2)
+ goto invalid_opcode;
+
+ _soundFileId = getVarOrWord();
+ if (getPlatform() == Common::kPlatformAmiga && getFeatures() & GF_TALKIE) {
+ char buf[10];
+ sprintf(buf, "%d%s", _soundFileId, "Effects");
+ _sound->readSfxFile(buf);
+ sprintf(buf, "%d%s", _soundFileId, "simon");
+ _sound->readVoiceFile(buf);
+ }
+
+ }
+ break;
+
+ case 186:{ /* vga pointer op 3 */
+ o_unfreezeBottom();
+ }
+ break;
+
+ case 187:{ /* fade to black */
+ if (getGameType() == GType_SIMON2)
+ goto invalid_opcode;
+ o_fadeToBlack();
+ }
+ break;
+
+ case 188: /* string2 is */
+ if (getGameType() == GType_SIMON1)
+ goto invalid_opcode;
+ {
+ uint i = getVarOrByte();
+ uint str = getNextStringID();
+ condition = (str < 20 && _stringIdArray2[i] == str);
+ }
+ break;
+
+ case 189:{ /* clear_op189_flag */
+ if (getGameType() == GType_SIMON1)
+ goto invalid_opcode;
+ _marks = 0;
+ }
+ break;
+
+ case 190:{
+ uint i;
+ if (getGameType() == GType_SIMON1)
+ goto invalid_opcode;
+ i = getVarOrByte();
+ if (!(_marks & (1 << i)))
+ o_waitForMark(i);
+ }
+ break;
+
+ // Feeble opcodes
+ case 191:
+ if (_bitArray[5] & 0x0008) {
+ _PVCount1 = 0;
+ _GPVCount1 = 0;
+ } else {
+ _PVCount = 0;
+ _GPVCount = 0;
+ }
+ break;
+
+ case 192:{
+ uint8 a = getVarOrByte();
+ uint8 b = getVarOrByte();
+ uint8 c = getVarOrByte();
+ uint8 d = getVarOrByte();
+ if (_bitArray[5] & 0x0008) {
+ _pathValues1[_PVCount1++] = a;
+ _pathValues1[_PVCount1++] = b;
+ _pathValues1[_PVCount1++] = c;
+ _pathValues1[_PVCount1++] = d;
+ } else {
+ _pathValues[_PVCount++] = a;
+ _pathValues[_PVCount++] = b;
+ _pathValues[_PVCount++] = c;
+ _pathValues[_PVCount++] = d;
+ }
+ }
+ break;
+
+ case 193:
+ // pause clock
+ warning("STUB: script opcode 193");
+ break;
+
+ case 194:
+ // resume clock
+ warning("STUB: script opcode 194");
+ break;
+
+ case 195:{
+ // Set palette colour?
+ uint blue = getVarOrByte();
+ uint green = getVarOrByte();
+ uint red = getVarOrByte();
+ uint color = getVarOrByte();
+ warning("STUB: script opcode 195 (%d, %d, %d, %d)", blue, green, red, color);
+ }
+ break;
+
+ case 196:{ /* set bit3 */
+ uint bit = getVarOrByte();
+ _bitArray[(bit / 16) + 32] |= 1 << (bit & 15);
+ }
+ break;
+
+ case 197:{ /* clear bit3 */
+ uint bit = getVarOrByte();
+ _bitArray[(bit / 16) + 32] &= ~(1 << (bit & 15));
+ }
+ break;
+
+ case 198:{ /* is bit3 clear */
+ uint bit = getVarOrByte();
+ condition = (_bitArray[(bit / 16) + 32] & (1 << (bit & 15))) == 0;
+ }
+ break;
+
+ case 199:{ /* is bit3 set */
+ uint bit = getVarOrByte();
+ condition = (_bitArray[(bit / 16) + 32] & (1 << (bit & 15))) != 0;
+ }
+ break;
+
+ default:
+ invalid_opcode:;
+ error("Invalid opcode '%d'", opcode);
+ }
+
+ } while (condition != flag);
+
+ return 0;
+}
+
+int SimonEngine::startSubroutine(Subroutine *sub) {
+ int result = -1;
+ SubroutineLine *sl;
+ const byte *old_code_ptr;
+
+ if (_startMainScript)
+ dumpSubroutine(sub);
+
+ old_code_ptr = _codePtr;
+
+ if (++_recursionDepth > 40)
+ error("Recursion error");
+
+ sl = (SubroutineLine *)((byte *)sub + sub->first);
+
+ while ((byte *)sl != (byte *)sub) {
+ if (checkIfToRunSubroutineLine(sl, sub)) {
+ result = 0;
+ _codePtr = (byte *)sl;
+ if (sub->id)
+ _codePtr += 2;
+ else
+ _codePtr += 8;
+
+ if (_continousMainScript)
+ fprintf(_dumpFile, "; %d\n", sub->id);
+ result = runScript();
+ if (result != 0) {
+ /* result -10 means restart subroutine */
+ if (result == -10) {
+ delay(0); /* maybe leave control to the VGA */
+ sl = (SubroutineLine *)((byte *)sub + sub->first);
+ continue;
+ }
+ break;
+ }
+ }
+ sl = (SubroutineLine *)((byte *)sub + sl->next);
+ }
+
+ _codePtr = old_code_ptr;
+
+ _recursionDepth--;
+ return result;
+}
+
+int SimonEngine::startSubroutineEx(Subroutine *sub) {
+ return startSubroutine(sub);
+}
+
+bool SimonEngine::checkIfToRunSubroutineLine(SubroutineLine *sl, Subroutine *sub) {
+ if (sub->id)
+ return true;
+
+ if (sl->verb != -1 && sl->verb != _scriptVerb &&
+ (sl->verb != -2 || _scriptVerb != -1))
+ return false;
+
+ if (sl->noun1 != -1 && sl->noun1 != _scriptNoun1 &&
+ (sl->noun1 != -2 || _scriptNoun1 != -1))
+ return false;
+
+ if (sl->noun2 != -1 && sl->noun2 != _scriptNoun2 &&
+ (sl->noun2 != -2 || _scriptNoun2 != -1))
+ return false;
+
+ return true;
+}
+
+void SimonEngine::o_83_helper() {
+ if (_exitCutscene) {
+ if (vcGetBit(9)) {
+ endCutscene();
+ }
+ } else {
+ processSpecialKeys();
+ }
+}
+
+void SimonEngine::o_waitForMark(uint i) {
+ _exitCutscene = false;
+ while (!(_marks & (1 << i))) {
+ if (_exitCutscene) {
+ if (vcGetBit(9)) {
+ endCutscene();
+ break;
+ }
+ } else {
+ processSpecialKeys();
+ }
+
+ delay(10);
+ }
+}
+
+
+bool SimonEngine::o_chance(uint a) {
+ if (a == 0)
+ return 0;
+
+ if (a == 100)
+ return 1;
+
+ a += _scriptUnk1;
+ if (a <= 0) {
+ _scriptUnk1 = 0;
+ return 0;
+ }
+
+ if ((uint)_rnd.getRandomNumber(99) < a) {
+ if (_scriptUnk1 <= 0)
+ _scriptUnk1 -= 5;
+ else
+ _scriptUnk1 = 0;
+ return 1;
+ }
+
+ if (_scriptUnk1 >= 0)
+ _scriptUnk1 += 5;
+ else
+ _scriptUnk1 = 0;
+
+ return 0;
+}
+
+void SimonEngine::o_inventory_descriptions() {
+ uint vgaSpriteId = getVarOrByte();
+ uint color = getVarOrByte();
+ const char *string_ptr = NULL;
+ TextLocation *tl = NULL;
+ char buf[256];
+
+ Child2 *child = (Child2 *)findChildOfType(getNextItemPtr(), 2);
+ if (child != NULL && child->avail_props & 1) {
+ string_ptr = (const char *)getStringPtrByID(child->array[0]);
+ tl = getTextLocation(vgaSpriteId);
+ }
+
+ if ((getGameType() == GType_SIMON2) && (getFeatures() & GF_TALKIE)) {
+ if (child != NULL && child->avail_props & 0x200) {
+ uint speechId = child->array[getOffsetOfChild2Param(child, 0x200)];
+
+ if (child->avail_props & 0x100) {
+ uint speechIdOffs = child->array[getOffsetOfChild2Param(child, 0x100)];
+
+ if (speechId == 116)
+ speechId = speechIdOffs + 115;
+ if (speechId == 92)
+ speechId = speechIdOffs + 98;
+ if (speechId == 99)
+ speechId = 9;
+ if (speechId == 97) {
+ switch (speechIdOffs) {
+ case 12:
+ speechId = 109;
+ break;
+ case 14:
+ speechId = 108;
+ break;
+ case 18:
+ speechId = 107;
+ break;
+ case 20:
+ speechId = 106;
+ break;
+ case 22:
+ speechId = 105;
+ break;
+ case 28:
+ speechId = 104;
+ break;
+ case 90:
+ speechId = 103;
+ break;
+ case 92:
+ speechId = 102;
+ break;
+ case 100:
+ speechId = 51;
+ break;
+ default:
+ error("o_177: invalid case %d", speechIdOffs);
+ }
+ }
+ }
+
+ if (_speech)
+ playSpeech(speechId, vgaSpriteId);
+ }
+
+ } else if (getFeatures() & GF_TALKIE) {
+ if (child != NULL && child->avail_props & 0x200) {
+ uint offs = getOffsetOfChild2Param(child, 0x200);
+ playSpeech(child->array[offs], vgaSpriteId);
+ } else if (child != NULL && child->avail_props & 0x100) {
+ uint offs = getOffsetOfChild2Param(child, 0x100);
+ playSpeech(child->array[offs] + 3550, vgaSpriteId);
+ }
+ }
+
+ if (child != NULL && (child->avail_props & 1) && _subtitles) {
+ if (child->avail_props & 0x100) {
+ sprintf(buf, "%d%s", child->array[getOffsetOfChild2Param(child, 0x100)], string_ptr);
+ string_ptr = buf;
+ }
+ if (string_ptr != NULL)
+ printText(vgaSpriteId, color, string_ptr, tl->x, tl->y, tl->width);
+ }
+}
+
+void SimonEngine::o_confirmQuit() {
+ // If all else fails, use English as fallback.
+ byte keyYes = 'y';
+ byte keyNo = 'n';
+
+ switch (_language) {
+ case Common::RU_RUS:
+ break;
+ case Common::PL_POL:
+ keyYes = 't';
+ break;
+ case Common::HB_ISR:
+ keyYes = 'f';
+ break;
+ case Common::ES_ESP:
+ keyYes = 's';
+ break;
+ case Common::IT_ITA:
+ keyYes = 's';
+ break;
+ case Common::FR_FRA:
+ keyYes = 'o';
+ break;
+ case Common::DE_DEU:
+ keyYes = 'j';
+ break;
+ default:
+ break;
+ }
+
+ for (;;) {
+ delay(1);
+#ifdef _WIN32_WCE
+ if (isSmartphone()) {
+ if (_keyPressed) {
+ if (_keyPressed == 13)
+ shutdown();
+ else
+ break;
+ }
+ }
+#endif
+ if (_keyPressed == keyYes)
+ shutdown();
+ else if (_keyPressed == keyNo)
+ break;
+ }
+}
+
+void SimonEngine::o_restoreIconArray(uint fcs_index) {
+ FillOrCopyStruct *fcs;
+
+ fcs = _windowArray[fcs_index & 7];
+ if (fcs->fcs_data == NULL)
+ return;
+ drawIconArray(fcs_index, fcs->fcs_data->item_ptr, fcs->fcs_data->unk1, fcs->fcs_data->unk2);
+}
+
+void SimonEngine::o_freezeBottom() {
+ _vgaBufStart = _vgaBufFreeStart;
+ _vgaFileBufOrg = _vgaBufFreeStart;
+}
+
+void SimonEngine::o_unfreezeBottom() {
+ _vgaBufFreeStart = _vgaFileBufOrg2;
+ _vgaBufStart = _vgaFileBufOrg2;
+ _vgaFileBufOrg = _vgaFileBufOrg2;
+}
+
+void SimonEngine::o_lockZone() {
+ _vgaBufStart = _vgaBufFreeStart;
+}
+
+void SimonEngine::o_unlockZone() {
+ _vgaBufFreeStart = _vgaFileBufOrg;
+ _vgaBufStart = _vgaFileBufOrg;
+}
+
+int SimonEngine::o_unk_132_helper(bool *b, char *buf) {
+ HitArea *ha;
+ *b = true;
+
+ if (!_saveLoadFlag) {
+ strange_jump:;
+ _saveLoadFlag = false;
+ saveGameDialog(buf);
+ }
+
+start_over:;
+ _keyPressed = 0;
+
+start_over_2:;
+ _lastHitArea = _lastHitArea3 = 0;
+
+ do {
+ if (_keyPressed != 0) {
+ if (_saveLoadFlag) {
+ *b = false;
+ return _keyPressed;
+ }
+ goto start_over;
+ }
+ delay(100);
+ } while (_lastHitArea3 == 0);
+
+ ha = _lastHitArea;
+
+ if (ha == NULL || ha->id < 205)
+ goto start_over_2;
+
+ if (ha->id == 205)
+ return ha->id;
+
+ if (ha->id == 206) {
+ if (_saveLoadRowCurPos == 1)
+ goto start_over_2;
+ if (_saveLoadRowCurPos < 7)
+ _saveLoadRowCurPos = 1;
+ else
+ _saveLoadRowCurPos -= 6;
+
+ goto strange_jump;
+ }
+
+ if (ha->id == 207) {
+ if (!_saveDialogFlag)
+ goto start_over_2;
+ _saveLoadRowCurPos += 6;
+ if (_saveLoadRowCurPos >= _numSaveGameRows)
+ _saveLoadRowCurPos = _numSaveGameRows;
+ goto strange_jump;
+ }
+
+ if (ha->id >= 214)
+ goto start_over_2;
+ return ha->id - 208;
+}
+
+void SimonEngine::o_unk_132_helper_3() {
+ for (int i = 208; i != 208 + 6; i++)
+ set_hitarea_bit_0x40(i);
+}
+
+void SimonEngine::o_clearCharacter(FillOrCopyStruct *fcs, int x, byte b) {
+ byte old_text;
+
+ video_putchar(fcs, x, b);
+ old_text = fcs->text_color;
+ fcs->text_color = fcs->fill_color;
+
+ if (_language == Common::HB_ISR) { //Hebrew
+ x = 128;
+ } else {
+ x += 120;
+ if (x != 128)
+ x = 129;
+
+ }
+
+ video_putchar(fcs, x);
+
+ fcs->text_color = old_text;
+ video_putchar(fcs, 8);
+}
+
+void SimonEngine::o_playMusic() {
+ int music = getVarOrWord();
+ int track = getVarOrWord();
+
+ // Jamieson630:
+ // This appears to be a "load or play music" command.
+ // The music resource is specified, and optionally
+ // a track as well. Normally we see two calls being
+ // made, one to load the resource and another to
+ // actually start a track (so the resource is
+ // effectively preloaded so there's no latency when
+ // starting playback).
+ if (getGameType() == GType_SIMON2) {
+ int loop = getVarOrByte();
+
+ midi.setLoop (loop != 0);
+ if (_lastMusicPlayed != music)
+ _nextMusicToPlay = music;
+ else
+ midi.startTrack (track);
+ } else {
+ if (music != _lastMusicPlayed) {
+ _lastMusicPlayed = music;
+ loadMusic (music);
+ midi.startTrack (track);
+ }
+ }
+}
+
+void SimonEngine::o_sync(uint a) {
+ uint16 id = to16Wrapper(a);
+ _lockWord |= 0x8000;
+ _vcPtr = (byte *)&id;
+ vc15_wakeup_id();
+ _lockWord &= ~0x8000;
+}
+
+void SimonEngine::o_playSFX(uint sound_id) {
+ if (getGameId() == GID_SIMON1DOS)
+ playSting(sound_id);
+ else
+ _sound->playEffects(sound_id);
+}
+
+void SimonEngine::o_unk_160(uint a) {
+ fcs_setTextColor(_windowArray[_curWindow], a);
+}
+
+void SimonEngine::o_unk_103() {
+ mouseOff();
+ removeIconArray(_curWindow);
+ if (getGameType() == GType_FF)
+ showMessageFormat("\x0E");
+ else
+ showMessageFormat("\x0C");
+ mouseOn();
+}
+
+void SimonEngine::o_kill_sprite_simon1(uint a) {
+ uint16 b = to16Wrapper(a);
+ _lockWord |= 0x8000;
+ _vcPtr = (byte *)&b;
+ vc60_killSprite();
+ _lockWord &= ~0x8000;
+}
+
+void SimonEngine::o_kill_sprite_simon2(uint a, uint b) {
+ uint16 items[2];
+
+ items[0] = to16Wrapper(a);
+ items[1] = to16Wrapper(b);
+
+ _lockWord |= 0x8000;
+ _vcPtr = (byte *)&items;
+ vc60_killSprite();
+ _lockWord &= ~0x8000;
+}
+
+/* OK */
+void SimonEngine::o_defineWindow(uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h) {
+ a &= 7;
+
+ if (_windowArray[a])
+ closeWindow(a);
+
+ _windowArray[a] = openWindow(b, c, d, e, f, g, h);
+
+ if (a == _curWindow) {
+ _textWindow = _windowArray[a];
+ showmessage_helper_3(_textWindow->textLength, _textWindow->textMaxLength);
+ }
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/midi.cpp b/engines/simon/midi.cpp
new file mode 100644
index 0000000000..915bc7108f
--- /dev/null
+++ b/engines/simon/midi.cpp
@@ -0,0 +1,546 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/file.h"
+#include "sound/mixer.h"
+#include "simon/simon.h"
+
+namespace Simon {
+
+
+// MidiParser_S1D is not considered part of the standard
+// MidiParser suite, but we still try to mask its details
+// and just provide a factory function.
+extern MidiParser *MidiParser_createS1D();
+
+MidiPlayer::MidiPlayer(OSystem *system) {
+ // Since initialize() is called every time the music changes,
+ // this is where we'll initialize stuff that must persist
+ // between songs.
+ _driver = 0;
+ _map_mt32_to_gm = false;
+ _passThrough = false;
+
+ _enable_sfx = true;
+ _current = 0;
+
+ _masterVolume = 255;
+ resetVolumeTable();
+ _paused = false;
+
+ _currentTrack = 255;
+ _loopTrack = 0;
+ _queuedTrack = 255;
+ _loopQueuedTrack = 0;
+}
+
+MidiPlayer::~MidiPlayer() {
+ _mutex.lock();
+ close();
+ _mutex.unlock();
+}
+
+int MidiPlayer::open() {
+ // Don't ever call open without first setting the output driver!
+ if (!_driver)
+ return 255;
+
+ int ret = _driver->open();
+ if (ret)
+ return ret;
+ _driver->setTimerCallback(this, &onTimer);
+ return 0;
+}
+
+void MidiPlayer::close() {
+ stop();
+// _system->lockMutex (_mutex);
+ if (_driver)
+ _driver->close();
+ _driver = NULL;
+ clearConstructs();
+// _system->unlockMutex (_mutex);
+}
+
+void MidiPlayer::send(uint32 b) {
+ if (!_current)
+ return;
+
+ if (_passThrough) {
+ _driver->send(b);
+ return;
+ }
+
+ byte channel = (byte) (b & 0x0F);
+ if ((b & 0xFFF0) == 0x07B0) {
+ // Adjust volume changes by master volume.
+ byte volume = (byte) ((b >> 16) & 0x7F);
+ _current->volume[channel] = volume;
+ volume = volume * _masterVolume / 255;
+ b = (b & 0xFF00FFFF) | (volume << 16);
+ } else if ((b & 0xF0) == 0xC0 && _map_mt32_to_gm) {
+ b = (b & 0xFFFF00FF) | (MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8);
+ } else if ((b & 0xFFF0) == 0x007BB0) {
+ // Only respond to an All Notes Off if this channel
+ // has already been allocated.
+ if (!_current->channel[b & 0x0F])
+ return;
+ }
+
+ if (!_current->channel[channel])
+ _current->channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+ if (_current->channel[channel]) {
+ if (channel == 9)
+ _current->channel[9]->volume (_current->volume[9] * _masterVolume / 255);
+ _current->channel[channel]->send(b);
+ }
+}
+
+void MidiPlayer::metaEvent (byte type, byte *data, uint16 length) {
+ // Only thing we care about is End of Track.
+ if (!_current || type != 0x2F) {
+ return;
+ } else if (_current == &_sfx) {
+ clearConstructs(_sfx);
+ } else if (_loopTrack) {
+ _current->parser->jumpToTick(0);
+ } else if (_queuedTrack != 255) {
+ _currentTrack = 255;
+ byte destination = _queuedTrack;
+ _queuedTrack = 255;
+ _loopTrack = _loopQueuedTrack;
+ _loopQueuedTrack = false;
+
+ // Remember, we're still inside the locked mutex.
+ // Have to unlock it before calling jump()
+ // (which locks it itself), and then relock it
+ // upon returning.
+ _mutex.unlock();
+ startTrack (destination);
+ _mutex.lock();
+ } else {
+ stop();
+ }
+}
+
+void MidiPlayer::onTimer (void *data) {
+ MidiPlayer *p = (MidiPlayer *) data;
+ Common::StackLock lock(p->_mutex);
+
+ if (!p->_paused) {
+ if (p->_music.parser && p->_currentTrack != 255) {
+ p->_current = &p->_music;
+ p->_music.parser->onTimer();
+ }
+ }
+ if (p->_sfx.parser) {
+ p->_current = &p->_sfx;
+ p->_sfx.parser->onTimer();
+ }
+ p->_current = 0;
+}
+
+void MidiPlayer::startTrack (int track) {
+ if (track == _currentTrack)
+ return;
+
+ if (_music.num_songs > 0) {
+ if (track >= _music.num_songs)
+ return;
+
+ _mutex.lock();
+
+ if (_music.parser) {
+ _current = &_music;
+ delete _music.parser;
+ _current = 0;
+ _music.parser = 0;
+ }
+
+ MidiParser *parser = MidiParser::createParser_SMF();
+ parser->property (MidiParser::mpMalformedPitchBends, 1);
+ parser->setMidiDriver (this);
+ parser->setTimerRate (_driver->getBaseTempo());
+ if (!parser->loadMusic (_music.songs[track], _music.song_sizes[track])) {
+ printf ("Error reading track!\n");
+ delete parser;
+ parser = 0;
+ }
+
+ _currentTrack = (byte) track;
+ _music.parser = parser; // That plugs the power cord into the wall
+ } else if (_music.parser) {
+ _mutex.lock();
+ if (!_music.parser->setTrack(track)) {
+ _mutex.unlock();
+ return;
+ }
+ _currentTrack = (byte) track;
+ _current = &_music;
+ _music.parser->jumpToTick(0);
+ _current = 0;
+ }
+
+ _mutex.unlock();
+}
+
+void MidiPlayer::stop() {
+ Common::StackLock lock(_mutex);
+
+ if (_music.parser) {
+ _current = &_music;
+ _music.parser->jumpToTick(0);
+ }
+ _current = 0;
+ _currentTrack = 255;
+}
+
+void MidiPlayer::pause (bool b) {
+ if (_paused == b || !_driver)
+ return;
+ _paused = b;
+
+ Common::StackLock lock(_mutex);
+ for (int i = 0; i < 16; ++i) {
+ if (_music.channel[i])
+ _music.channel[i]->volume (_paused ? 0 : (_music.volume[i] * _masterVolume / 255));
+ if (_sfx.channel[i])
+ _sfx.channel[i]->volume (_paused ? 0 : (_sfx.volume[i] * _masterVolume / 255));
+ }
+}
+
+void MidiPlayer::set_volume (int volume) {
+ if (volume < 0)
+ volume = 0;
+ else if (volume > 255)
+ volume = 255;
+
+ if (_masterVolume == volume)
+ return;
+ _masterVolume = volume;
+
+ // Now tell all the channels this.
+ Common::StackLock lock(_mutex);
+ if (_driver && !_paused) {
+ for (int i = 0; i < 16; ++i) {
+ if (_music.channel[i])
+ _music.channel[i]->volume(_music.volume[i] * _masterVolume / 255);
+ if (_sfx.channel[i])
+ _sfx.channel[i]->volume(_sfx.volume[i] * _masterVolume / 255);
+ }
+ }
+}
+
+void MidiPlayer::set_driver(MidiDriver *md) {
+ // Don't try to set this more than once.
+ if (_driver)
+ return;
+ _driver = md;
+}
+
+void MidiPlayer::mapMT32toGM (bool map) {
+ Common::StackLock lock(_mutex);
+
+ _map_mt32_to_gm = map;
+}
+
+void MidiPlayer::setLoop (bool loop) {
+ Common::StackLock lock(_mutex);
+
+ _loopTrack = loop;
+}
+
+void MidiPlayer::queueTrack (int track, bool loop) {
+ _mutex.lock();
+ if (_currentTrack == 255) {
+ _mutex.unlock();
+ setLoop(loop);
+ startTrack(track);
+ } else {
+ _queuedTrack = track;
+ _loopQueuedTrack = loop;
+ _mutex.unlock();
+ }
+}
+
+void MidiPlayer::clearConstructs() {
+ clearConstructs(_music);
+ clearConstructs(_sfx);
+}
+
+void MidiPlayer::clearConstructs(MusicInfo &info) {
+ int i;
+ if (info.num_songs > 0) {
+ for (i = 0; i < info.num_songs; ++i)
+ free (info.songs[i]);
+ info.num_songs = 0;
+ }
+
+ if (info.data) {
+ free(info.data);
+ info.data = 0;
+ } // end if
+
+ if (info.parser) {
+ delete info.parser;
+ info.parser = 0;
+ }
+
+ if (_driver) {
+ for (i = 0; i < 16; ++i) {
+ if (info.channel[i]) {
+ info.channel[i]->allNotesOff();
+ info.channel[i]->release();
+ }
+ }
+ }
+ info.clear();
+}
+
+void MidiPlayer::resetVolumeTable() {
+ int i;
+ for (i = 0; i < 16; ++i) {
+ _music.volume[i] = _sfx.volume[i] = 127;
+ if (_driver)
+ _driver->send (((_masterVolume >> 1) << 16) | 0x7B0 | i);
+ }
+}
+
+static int simon1_gmf_size[] = {
+ 8900, 12166, 2848, 3442, 4034, 4508, 7064, 9730, 6014, 4742, 3138,
+ 6570, 5384, 8909, 6457, 16321, 2742, 8968, 4804, 8442, 7717,
+ 9444, 5800, 1381, 5660, 6684, 2456, 4744, 2455, 1177, 1232,
+ 17256, 5103, 8794, 4884, 16
+};
+
+void MidiPlayer::loadSMF (Common::File *in, int song, bool sfx) {
+ Common::StackLock lock(_mutex);
+
+ MusicInfo *p = sfx ? &_sfx : &_music;
+ clearConstructs (*p);
+
+ uint32 startpos = in->pos();
+ byte header[4];
+ in->read(header, 4);
+ bool isGMF = !memcmp (header, "GMF\x1", 4);
+ in->seek(startpos, SEEK_SET);
+
+ uint32 size = in->size() - in->pos();
+ if (isGMF) {
+ if (sfx) {
+ // Multiple GMF resources are stored in the SFX files,
+ // but each one is referenced by a pointer at the
+ // beginning of the file. Those pointers can be used
+ // to determine file size.
+ in->seek(0, SEEK_SET);
+ uint16 value = in->readUint16LE() >> 2; // Number of resources
+ if (song != value - 1) {
+ in->seek(song * 2 + 2, SEEK_SET);
+ value = in->readUint16LE();
+ size = value - startpos;
+ }
+ in->seek(startpos, SEEK_SET);
+ } else if (size >= 64000) {
+ // For GMF resources not in separate
+ // files, we're going to have to use
+ // hardcoded size tables.
+ size = simon1_gmf_size[song];
+ }
+ }
+
+ // When allocating space, add 4 bytes in case
+ // this is a GMF and we have to tack on our own
+ // End of Track event.
+ p->data = (byte *) calloc(size + 4, 1);
+ in->read(p->data, size);
+
+ if (!memcmp(p->data, "GMF\x1", 4)) {
+ // BTW, here's what we know about the GMF header,
+ // the 7 bytes preceding the actual MIDI events.
+ // 3 BYTES: 'GMF'
+ // 1 BYTE : Always seems to be 0x01
+ // 1 BYTE : Always seems to be 0x00
+ // 1 BYTE : Ranges from 0x02 to 0x08 (always 0x02 for SFX, though)
+ // 1 BYTE : Loop control. 0 = no loop, 1 = loop
+ if (!sfx) {
+ // According to bug #1004919 calling setLoop() from
+ // within a lock causes a lockup, though I have no
+ // idea when this actually happens.
+ _loopTrack = (p->data[6] != 0);
+ }
+ }
+
+ MidiParser *parser = MidiParser::createParser_SMF();
+ parser->property(MidiParser::mpMalformedPitchBends, 1);
+ parser->setMidiDriver(this);
+ parser->setTimerRate(_driver->getBaseTempo());
+ if (!parser->loadMusic(p->data, size)) {
+ printf ("Error reading track!\n");
+ delete parser;
+ parser = 0;
+ }
+
+ if (!sfx) {
+ _currentTrack = 255;
+ resetVolumeTable();
+ }
+ p->parser = parser; // That plugs the power cord into the wall
+}
+
+void MidiPlayer::loadMultipleSMF (Common::File *in, bool sfx) {
+ // This is a special case for Simon 2 Windows.
+ // Instead of having multiple sequences as
+ // separate tracks in a Type 2 file, simon2win
+ // has multiple songs, each of which is a Type 1
+ // file. Thus, preceding the songs is a single
+ // byte specifying how many songs are coming.
+ // We need to load ALL the songs and then
+ // treat them as separate tracks -- for the
+ // purpose of jumps, anyway.
+ Common::StackLock lock(_mutex);
+
+ MusicInfo *p = sfx ? &_sfx : &_music;
+ clearConstructs(*p);
+
+ p->num_songs = in->readByte();
+ if (p->num_songs > 16) {
+ printf ("playMultipleSMF: %d is too many songs to keep track of!\n", (int) p->num_songs);
+ return;
+ }
+
+ byte i;
+ for (i = 0; i < p->num_songs; ++i) {
+ byte buf[5];
+ uint32 pos = in->pos();
+
+ // Make sure there's a MThd
+ in->read(buf, 4);
+ if (memcmp (buf, "MThd", 4)) {
+ printf ("Expected MThd but found '%c%c%c%c' instead!\n", buf[0], buf[1], buf[2], buf[3]);
+ return;
+ }
+ in->seek(in->readUint32BE() + in->pos(), SEEK_SET);
+
+ // Now skip all the MTrk blocks
+ while (true) {
+ in->read (buf, 4);
+ if (memcmp(buf, "MTrk", 4))
+ break;
+ in->seek(in->readUint32BE() + in->pos(), SEEK_SET);
+ }
+
+ uint32 pos2 = in->pos() - 4;
+ uint32 size = pos2 - pos;
+ p->songs[i] = (byte *) calloc(size, 1);
+ in->seek(pos, SEEK_SET);
+ in->read(p->songs[i], size);
+ p->song_sizes[i] = size;
+ }
+
+ if (!sfx) {
+ _currentTrack = 255;
+ resetVolumeTable();
+ }
+}
+
+void MidiPlayer::loadXMIDI(Common::File *in, bool sfx) {
+ Common::StackLock lock(_mutex);
+ MusicInfo *p = sfx ? &_sfx : &_music;
+ clearConstructs(*p);
+
+ char buf[4];
+ uint32 pos = in->pos();
+ uint32 size = 4;
+ in->read(buf, 4);
+ if (!memcmp(buf, "FORM", 4)) {
+ int i;
+ for (i = 0; i < 16; ++i) {
+ if (!memcmp(buf, "CAT ", 4))
+ break;
+ size += 2;
+ memcpy(buf, &buf[2], 2);
+ in->read(&buf[2], 2);
+ }
+ if (memcmp(buf, "CAT ", 4)) {
+ warning("Could not find 'CAT ' tag to determine resource size!");
+ return;
+ }
+ size += 4 + in->readUint32BE();
+ in->seek(pos, 0);
+ p->data = (byte *) calloc(size, 1);
+ in->read(p->data, size);
+ } else {
+ warning("Expected 'FORM' tag but found '%c%c%c%c' instead!", buf[0], buf[1], buf[2], buf[3]);
+ return;
+ }
+
+ MidiParser *parser = MidiParser::createParser_XMIDI();
+ parser->setMidiDriver(this);
+ parser->setTimerRate(_driver->getBaseTempo());
+ if (!parser->loadMusic(p->data, size)) {
+ warning("Error reading track!");
+ delete parser;
+ parser = 0;
+ }
+
+ if (!sfx) {
+ _currentTrack = 255;
+ resetVolumeTable();
+ }
+ p->parser = parser; // That plugs the power cord into the wall
+}
+
+void MidiPlayer::loadS1D (Common::File *in, bool sfx) {
+ Common::StackLock lock(_mutex);
+ MusicInfo *p = sfx ? &_sfx : &_music;
+ clearConstructs(*p);
+
+ uint16 size = in->readUint16LE();
+ if (size != in->size() - 2) {
+ warning("Size mismatch in simon1demo MUS file (%ld versus reported %d)", (long) in->size() - 2, (int) size);
+ return;
+ }
+
+ p->data = (byte *) calloc(size, 1);
+ in->read(p->data, size);
+
+ MidiParser *parser = MidiParser_createS1D();
+ parser->setMidiDriver(this);
+ parser->setTimerRate(_driver->getBaseTempo());
+ if (!parser->loadMusic(p->data, size)) {
+ warning("Error reading track!");
+ delete parser;
+ parser = 0;
+ }
+
+ if (!sfx) {
+ _currentTrack = 255;
+ resetVolumeTable();
+ }
+ p->parser = parser; // That plugs the power cord into the wall
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/midi.h b/engines/simon/midi.h
new file mode 100644
index 0000000000..bc903f9d17
--- /dev/null
+++ b/engines/simon/midi.h
@@ -0,0 +1,127 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SIMON_MIDI_H
+#define SIMON_MIDI_H
+
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "common/mutex.h"
+
+namespace Common {
+ class File;
+}
+
+namespace Simon {
+
+struct MusicInfo {
+ MidiParser *parser;
+ byte * data;
+ byte num_songs; // For Type 1 SMF resources
+ byte * songs[16]; // For Type 1 SMF resources
+ uint32 song_sizes[16]; // For Type 1 SMF resources
+
+ MidiChannel *channel[16]; // Dynamic remapping of channels to resolve conflicts
+ byte volume[16]; // Current channel volume
+
+ MusicInfo() { clear(); }
+ void clear() {
+ parser = 0; data = 0; num_songs = 0;
+ memset (songs, 0, sizeof (songs));
+ memset (song_sizes, 0, sizeof (song_sizes));
+ memset (channel, 0, sizeof (channel));
+ }
+};
+
+class MidiPlayer : public MidiDriver {
+protected:
+ Common::Mutex _mutex;
+ MidiDriver *_driver;
+ bool _map_mt32_to_gm;
+ bool _passThrough;
+
+ MusicInfo _music;
+ MusicInfo _sfx;
+ MusicInfo *_current; // Allows us to establish current context for operations.
+
+ // These are maintained for both music and SFX
+ byte _masterVolume; // 0-255
+ bool _paused;
+
+ // These are only used for music.
+ byte _currentTrack;
+ bool _loopTrack;
+ byte _queuedTrack;
+ bool _loopQueuedTrack;
+
+protected:
+ static void onTimer (void *data);
+ void clearConstructs();
+ void clearConstructs (MusicInfo &info);
+ void resetVolumeTable();
+
+public:
+ bool _enable_sfx;
+
+public:
+ MidiPlayer (OSystem *system);
+ virtual ~MidiPlayer();
+
+ void loadSMF (Common::File *in, int song, bool sfx = false);
+ void loadMultipleSMF (Common::File *in, bool sfx = false);
+ void loadXMIDI (Common::File *in, bool sfx = false);
+ void loadS1D (Common::File *in, bool sfx = false);
+
+ void mapMT32toGM (bool map);
+ void setLoop (bool loop);
+ void startTrack(int track);
+ void queueTrack (int track, bool loop);
+ bool isPlaying (bool check_queued = false) { return (_currentTrack != 255 && (_queuedTrack != 255 || !check_queued)); }
+
+ void stop();
+ void pause (bool b);
+
+ int get_volume() { return _masterVolume; }
+ void set_volume (int volume);
+ void set_driver (MidiDriver *md);
+
+public:
+ // MidiDriver interface implementation
+ int open();
+ void close();
+ void send(uint32 b);
+
+ void metaEvent (byte type, byte *data, uint16 length);
+ void setPassThrough(bool b) { _passThrough = b; }
+
+ // Timing functions - MidiDriver now operates timers
+ void setTimerCallback (void *timer_param, void (*timer_proc) (void *)) { }
+ uint32 getBaseTempo (void) { return _driver ? _driver->getBaseTempo() : 0; }
+
+ // Channel allocation functions
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+};
+
+} // End of namespace Simon
+
+#endif
diff --git a/engines/simon/midiparser_s1d.cpp b/engines/simon/midiparser_s1d.cpp
new file mode 100644
index 0000000000..926a8cb263
--- /dev/null
+++ b/engines/simon/midiparser_s1d.cpp
@@ -0,0 +1,155 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sound/midiparser.h"
+#include "sound/mididrv.h"
+#include "common/util.h"
+
+#include <stdio.h>
+
+namespace Simon {
+
+/**
+ * Simon 1 Demo version of MidiParser.
+ *
+ * This parser is the result of eyeballing the one MUS file that's included
+ * with simon1demo. So there might be some things missing. I've tried to notate
+ * question-mark areas where they occur.
+ */
+class MidiParser_S1D : public MidiParser {
+protected:
+ byte *_data;
+ bool _no_delta;
+
+protected:
+ void parseNextEvent (EventInfo &info);
+ void resetTracking();
+ uint32 readVLQ2(byte * &data);
+
+public:
+ MidiParser_S1D() : _data(0), _no_delta(false) {}
+
+ bool loadMusic(byte *data, uint32 size);
+};
+
+
+// The VLQs for simon1demo seem to be
+// in Little Endian format.
+uint32 MidiParser_S1D::readVLQ2(byte * &data) {
+ byte str;
+ uint32 value = 0;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ str = data[0];
+ ++data;
+ value |= (str & 0x7F) << (i * 7);
+ if (!(str & 0x80))
+ break;
+ }
+ return value;
+}
+
+void MidiParser_S1D::parseNextEvent(EventInfo &info) {
+ info.start = _position._play_pos;
+ info.delta = _no_delta ? 0 : readVLQ2(_position._play_pos);
+
+ _no_delta = false;
+ info.event = *(_position._play_pos++);
+ if (info.command() < 0x8) {
+ _no_delta = true;
+ info.event += 0x80;
+ }
+
+ switch (info.command()) {
+ case 0x8:
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = 0;
+ info.length = 0;
+ break;
+
+ case 0x9:
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++); // I'm ASSUMING this byte is velocity!
+ info.length = 0;
+ break;
+
+ case 0xC:
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = 0;
+ ++_position._play_pos; // I have NO IDEA what the second byte is for.
+ break;
+
+ case 0xF:
+ if (info.event == 0xFC) {
+ // This means End of Track.
+ // Rewrite in SMF (MIDI transmission) form.
+ info.event = 0xFF;
+ info.ext.type = 0x2F;
+ info.length = 0;
+ break;
+ }
+ // OTherwise fall through to default.
+
+ default:
+ printf ("MidiParser_S1D: Warning! Unexpected byte 0x%02X found!\n", (int) info.event);
+ _abort_parse = true;
+ _position._play_pos = 0;
+ }
+}
+
+bool MidiParser_S1D::loadMusic(byte *data, uint32 size) {
+ unloadMusic();
+
+ byte *pos = data;
+ if (*(pos++) != 0xFC) {
+ printf ("Warning: Expected 0xFC header but found 0x%02X instead\n", (int) *pos);
+ return false;
+ }
+
+ // The next 3 bytes MIGHT be tempo, but we skip them and use the default.
+// setTempo (*(pos++) | (*(pos++) << 8) | (*(pos++) << 16));
+ pos += 3;
+
+ // And now we're at the actual data. Only one track.
+ _num_tracks = 1;
+ _data = pos;
+ _tracks[0] = pos;
+
+ // Note that we assume the original data passed in
+ // will persist beyond this call, i.e. we do NOT
+ // copy the data to our own buffer. Take warning....
+ resetTracking();
+ setTempo(666667);
+ setTrack(0);
+ return true;
+}
+
+void MidiParser_S1D::resetTracking() {
+ MidiParser::resetTracking();
+ _no_delta = false;
+}
+
+MidiParser *MidiParser_createS1D() { return new MidiParser_S1D; }
+
+} // End of namespace Simon
diff --git a/engines/simon/module.mk b/engines/simon/module.mk
new file mode 100644
index 0000000000..3323f3a0de
--- /dev/null
+++ b/engines/simon/module.mk
@@ -0,0 +1,29 @@
+MODULE := engines/simon
+
+MODULE_OBJS := \
+ engines/simon/charset.o \
+ engines/simon/cursor.o \
+ engines/simon/debug.o \
+ engines/simon/debugger.o \
+ engines/simon/game.o \
+ engines/simon/icons.o \
+ engines/simon/items.o \
+ engines/simon/midi.o \
+ engines/simon/midiparser_s1d.o \
+ engines/simon/res.o \
+ engines/simon/saveload.o \
+ engines/simon/sound.o \
+ engines/simon/simon.o \
+ engines/simon/verb.o \
+ engines/simon/vga.o \
+
+MODULE_DIRS += \
+ engines/simon
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/simon/res.cpp b/engines/simon/res.cpp
new file mode 100644
index 0000000000..0be063c065
--- /dev/null
+++ b/engines/simon/res.cpp
@@ -0,0 +1,386 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Resource file routines for Simon1/Simon2
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "simon/simon.h"
+#include "simon/intern.h"
+
+namespace Simon {
+
+// Script opcodes to load into memory
+static const char *const opcode_arg_table_simon1win[256] = {
+ " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
+ "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
+ "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BTS ", "T ", " ", "B ",
+ "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
+ "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NBNNN ", "N ",
+ " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
+ "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
+ "NN ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", "N ",
+ "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", "B ",
+ "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", "T ",
+ "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ", " ",
+ " ",
+};
+
+static const char *const opcode_arg_table_simon1dos[256] = {
+ " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
+ "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
+ "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BT ", "T ", " ", "B ",
+ "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
+ "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NBNNN ", "N ",
+ " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
+ "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
+ "NN ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", "N ",
+ "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", "B ",
+ "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBT ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", "T ",
+ "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ", " ",
+ " ",
+};
+
+static const char *const opcode_arg_table_simon2win[256] = {
+ " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
+ "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
+ "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BTS ", "T ", " ", "B ",
+ "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
+ "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ",
+ " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
+ "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
+ "NNB ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ",
+ "N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ",
+ "B ", "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ",
+ "T ", "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ",
+ " ", " ", "BT ", " ", "B "
+};
+
+static const char *const opcode_arg_table_simon2dos[256] = {
+ " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
+ "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
+ "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BT ", "T ", " ", "B ",
+ "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
+ "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ",
+ " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
+ "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
+ "NNB ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ",
+ "N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ",
+ "B ", "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBT ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ",
+ "T ", "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ",
+ " ", " ", "BT ", " ", "B "
+};
+
+static const char *const opcode_arg_table_feeblefiles[256] = {
+ " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
+ "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
+ "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
+ "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BTS ", "T ", " ", "B ",
+ "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
+ "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ",
+ " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
+ "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
+ "NNB ", "N ", "N ", "Ban ", " ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ",
+ "N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ",
+ "B ", "IBB ", "IBN ", "IB ", "B ", "BNNN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ",
+ "T ", "N ", " ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", "T ", " ", "N ", "N ",
+ " ", " ", "BT ", " ", "B ", " ", "BBBB ", " ", " ", "BBBB ", "B ", "B ", "B ", "B "
+};
+
+uint16 SimonEngine::to16Wrapper(uint value) {
+ if (getGameType() == GType_FF)
+ return TO_LE_16(value);
+ else
+ return TO_BE_16(value);
+}
+
+uint16 SimonEngine::readUint16Wrapper(const void *src) {
+ if (getGameType() == GType_FF)
+ return READ_LE_UINT16(src);
+ else
+ return READ_BE_UINT16(src);
+}
+
+void SimonEngine::loadGamePcFile(const char *filename) {
+ Common::File in;
+ int num_inited_objects;
+ int i, file_size;
+
+ /* read main gamepc file */
+ in.open(filename);
+ if (in.isOpen() == false) {
+ char *filename2;
+ filename2 = (char *)malloc(strlen(filename) + 2);
+ strcpy(filename2, filename);
+ strcat(filename2, ".");
+ in.open(filename2);
+ free(filename2);
+ if (in.isOpen() == false)
+ error("Can't open gamepc file '%s' or '%s.'", gss->gamepc_filename, gss->gamepc_filename);
+ }
+
+ num_inited_objects = allocGamePcVars(&in);
+
+ loginPlayer();
+ readGamePcText(&in);
+
+ for (i = 2; i < num_inited_objects; i++) {
+ readItemFromGamePc(&in, _itemArrayPtr[i]);
+ }
+
+ readSubroutineBlock(&in);
+
+ in.close();
+
+ /* Read list of TABLE resources */
+ in.open("TBLLIST");
+ if (in.isOpen() == false) {
+ in.open("TBLLIST.");
+ if (in.isOpen() == false)
+ error("Can't open table resources file 'TBLLIST' or 'TBLLIST.'");
+ }
+
+ file_size = in.size();
+
+ _tblList = (byte *)malloc(file_size);
+ if (_tblList == NULL)
+ error("Out of memory for strip table list");
+ in.read(_tblList, file_size);
+ in.close();
+
+ /* Remember the current state */
+ _subroutineListOrg = _subroutineList;
+ _tablesHeapPtrOrg = _tablesHeapPtr;
+ _tablesHeapCurPosOrg = _tablesHeapCurPos;
+
+ if (getGameType() == GType_FF)
+ return;
+
+ /* Read list of TEXT resources */
+ if (getPlatform() == Common::kPlatformAcorn)
+ in.open("STRIPPED");
+ else
+ in.open("STRIPPED.TXT");
+ if (in.isOpen() == false)
+ error("Can't open text resources file 'STRIPPED.TXT'");
+
+ file_size = in.size();
+ _strippedTxtMem = (byte *)malloc(file_size);
+ if (_strippedTxtMem == NULL)
+ error("Out of memory for strip text list");
+ in.read(_strippedTxtMem, file_size);
+ in.close();
+}
+
+void SimonEngine::readGamePcText(Common::File *in) {
+ uint text_size;
+ byte *text_mem;
+
+ _textSize = text_size = in->readUint32BE();
+ text_mem = (byte *)malloc(text_size);
+ if (text_mem == NULL)
+ error("Out of text memory");
+
+ in->read(text_mem, text_size);
+
+ setupStringTable(text_mem, _stringTabNum);
+}
+
+void SimonEngine::readItemFromGamePc(Common::File *in, Item *item) {
+ uint32 type;
+
+ item->adjective = in->readUint16BE();
+ item->noun = in->readUint16BE();
+ item->state = in->readUint16BE();
+ item->sibling = (uint16)fileReadItemID(in);
+ item->child = (uint16)fileReadItemID(in);
+ item->parent = (uint16)fileReadItemID(in);
+ in->readUint16BE();
+ item->classFlags = in->readUint16BE();
+ item->children = NULL;
+
+ type = in->readUint32BE();
+ while (type) {
+ type = in->readUint16BE();
+ if (type != 0)
+ readItemChildren(in, item, type);
+ }
+}
+
+void SimonEngine::readItemChildren(Common::File *in, Item *item, uint type) {
+ if (type == 1) {
+ uint fr1 = in->readUint16BE();
+ uint fr2 = in->readUint16BE();
+ uint i, size;
+ uint j, k;
+ Child1 *child;
+
+ size = CHILD1_SIZE;
+ for (i = 0, j = fr2; i != 6; i++, j >>= 2)
+ if (j & 3)
+ size += sizeof(child->array[0]);
+
+ child = (Child1 *)allocateChildBlock(item, 1, size);
+ child->subroutine_id = fr1;
+ child->fr2 = fr2;
+
+ for (i = k = 0, j = fr2; i != 6; i++, j >>= 2)
+ if (j & 3)
+ child->array[k++] = (uint16)fileReadItemID(in);
+ } else if (type == 2) {
+ uint32 fr = in->readUint32BE();
+ uint i, k, size;
+ Child2 *child;
+
+ size = CHILD2_SIZE;
+ for (i = 0; i != 16; i++)
+ if (fr & (1 << i))
+ size += sizeof(child->array[0]);
+
+ child = (Child2 *)allocateChildBlock(item, 2, size);
+ child->avail_props = fr;
+
+ k = 0;
+ if (fr & 1) {
+ child->array[k++] = (uint16)in->readUint32BE();
+ }
+ for (i = 1; i != 16; i++)
+ if (fr & (1 << i))
+ child->array[k++] = in->readUint16BE();
+
+ child->string_id = (uint16)in->readUint32BE();
+ } else {
+ error("readItemChildren: invalid type %d", type);
+ }
+}
+
+uint fileReadItemID(Common::File *in) {
+ uint32 val = in->readUint32BE();
+ if (val == 0xFFFFFFFF)
+ return 0;
+ return val + 2;
+}
+
+byte *SimonEngine::readSingleOpcode(Common::File *in, byte *ptr) {
+ int i, l;
+ const char *string_ptr;
+ uint val;
+
+ const char *const *table;
+
+ if (getGameType() == GType_FF) {
+ table = opcode_arg_table_feeblefiles;
+ } else if ((getGameType() == GType_SIMON2) && (getFeatures() & GF_TALKIE))
+ table = opcode_arg_table_simon2win;
+ else if (getGameType() == GType_SIMON2)
+ table = opcode_arg_table_simon2dos;
+ else if (getFeatures() & GF_TALKIE)
+ table = opcode_arg_table_simon1win;
+ else
+ table = opcode_arg_table_simon1dos;
+
+ i = 0;
+
+ string_ptr = table[*ptr++];
+ if (!string_ptr)
+ error("Unable to locate opcode table. Perhaps you are using the wrong game target?");
+
+ for (;;) {
+ if (string_ptr[i] == ' ')
+ return ptr;
+
+ l = string_ptr[i++];
+ switch (l) {
+ case 'N':
+ case 'S':
+ case 'a':
+ case 'n':
+ case 'p':
+ case 'v':
+ val = in->readUint16BE();
+ *ptr++ = val >> 8;
+ *ptr++ = val & 255;
+ break;
+
+ case 'B':
+ *ptr++ = in->readByte();
+ if (ptr[-1] == 0xFF) {
+ *ptr++ = in->readByte();
+ }
+ break;
+
+ case 'I':
+ val = in->readUint16BE();
+ switch (val) {
+ case 1:
+ val = 0xFFFF;
+ break;
+ case 3:
+ val = 0xFFFD;
+ break;
+ case 5:
+ val = 0xFFFB;
+ break;
+ case 7:
+ val = 0xFFF9;
+ break;
+ case 9:
+ val = 0xFFF7;
+ break;
+ default:
+ val = fileReadItemID(in);;
+ }
+ *ptr++ = val >> 8;
+ *ptr++ = val & 255;
+ break;
+
+ case 'T':
+ val = in->readUint16BE();
+ switch (val) {
+ case 0:
+ val = 0xFFFF;
+ break;
+ case 3:
+ val = 0xFFFD;
+ break;
+ default:
+ val = (uint16)in->readUint32BE();
+ break;
+ }
+ *ptr++ = val >> 8;
+ *ptr++ = val & 255;
+ break;
+ default:
+ error("Bad cmd table entry %c", l);
+ }
+ }
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/saveload.cpp b/engines/simon/saveload.cpp
new file mode 100644
index 0000000000..9314a4bfca
--- /dev/null
+++ b/engines/simon/saveload.cpp
@@ -0,0 +1,634 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "gui/about.h"
+#include "gui/message.h"
+
+#include "simon/simon.h"
+#include "simon/intern.h"
+
+#include "common/savefile.h"
+#include "common/system.h"
+
+namespace Simon {
+
+void SimonEngine::o_saveGame() {
+ saveOrLoadDialog(false);
+}
+
+void SimonEngine::o_loadGame() {
+ saveOrLoadDialog(true);
+}
+
+int SimonEngine::countSaveGames() {
+ Common::InSaveFile *f;
+ uint i = 1;
+ bool marks[256];
+
+ char *prefix = gen_savename(999);
+ prefix[strlen(prefix)-3] = '\0';
+ _saveFileMan->listSavefiles(prefix, marks, 256);
+
+ while (i < 256) {
+ if (marks[i] &&
+ (f = _saveFileMan->openForLoading(gen_savename(i)))) {
+ i++;
+ delete f;
+ } else
+ break;
+ }
+ return i;
+}
+
+int SimonEngine::displaySaveGameList(int curpos, bool load, char *dst) {
+ int slot, last_slot;
+ Common::InSaveFile *in;
+
+ showMessageFormat("\xC");
+
+ memset(dst, 0, 18 * 6);
+
+ slot = curpos;
+
+ while (curpos + 6 > slot) {
+ if (!(in = _saveFileMan->openForLoading(gen_savename(slot))))
+ break;
+
+ in->read(dst, 18);
+ delete in;
+ last_slot = slot;
+ if (slot < 10)
+ showMessageFormat(" ");
+ showMessageFormat("%d", slot);
+ showMessageFormat(".%s\n", dst);
+ dst += 18;
+ slot++;
+ }
+ // while_break
+ if (!load) {
+ if (curpos + 6 == slot)
+ slot++;
+ else {
+ if (slot < 10)
+ showMessageFormat(" ");
+ showMessageFormat("%d.\n", slot);
+ }
+ } else {
+ if (curpos + 6 == slot) {
+ if ((in = _saveFileMan->openForLoading(gen_savename(slot)))) {
+ slot++;
+ delete in;
+ }
+ }
+ }
+
+ return slot - curpos;
+}
+
+void SimonEngine::quickLoadOrSave() {
+ // simon1demo subroutines are missing too many segments
+ // original demo didn't allow load or save either.
+ if (getGameId() == GID_SIMON1DEMO)
+ return;
+
+ bool success;
+ char buf[50];
+
+ char *filename = gen_savename(_saveLoadSlot);
+ if (_saveLoadType == 2) {
+ Subroutine *sub;
+ success = loadGame(_saveLoadSlot);
+ if (!success) {
+ sprintf(buf, "Failed to load game state to file:\n\n%s", filename);
+ } else {
+ // Redraw Inventory
+ mouseOff();
+ drawIconArray(2, getItem1Ptr(), 0, 0);
+ mouseOn();
+ // Reset engine?
+ vcSetBitTo(97, true);
+ sub = getSubroutineByID(100);
+ startSubroutine(sub);
+ }
+ } else {
+ success = saveGame(_saveLoadSlot, _saveLoadName);
+ if (!success)
+ sprintf(buf, "Failed to save game state to file:\n\n%s", filename);
+ }
+
+ if (!success) {
+ GUI::MessageDialog dialog(buf, "OK");
+ dialog.runModal();
+
+ } else if (_saveLoadType == 1) {
+ sprintf(buf, "Successfully saved game state in file:\n\n%s", filename);
+ GUI::TimedMessageDialog dialog(buf, 1500);
+ dialog.runModal();
+
+ }
+
+ _saveLoadType = 0;
+}
+
+void SimonEngine::saveGameDialog(char *buf) {
+ int i;
+
+ o_unk_132_helper_3();
+
+ i = displaySaveGameList(_saveLoadRowCurPos, _saveOrLoad, buf);
+
+ _saveDialogFlag = true;
+
+ if (i != 7) {
+ i++;
+ if (!_saveOrLoad)
+ i++;
+ _saveDialogFlag = false;
+ }
+
+ if (!--i)
+ return;
+
+ do {
+ clear_hitarea_bit_0x40(0xd0 + i - 1);
+ } while (--i);
+}
+
+void SimonEngine::saveOrLoadDialog(bool load) {
+ time_t save_time;
+ int number_of_savegames;
+ int i;
+ int unk132_result;
+ FillOrCopyStruct *fcs;
+ char *name;
+ int name_len;
+ bool b;
+ char buf[108];
+
+ _saveOrLoad = load;
+
+ save_time = time(NULL);
+
+ _copyPartialMode = 1;
+
+ number_of_savegames = countSaveGames();
+ if (!load)
+ number_of_savegames++;
+ number_of_savegames -= 6;
+ if (number_of_savegames < 0)
+ number_of_savegames = 0;
+ number_of_savegames++;
+ _numSaveGameRows = number_of_savegames;
+
+ _saveLoadRowCurPos = 1;
+ if (!load)
+ _saveLoadRowCurPos = number_of_savegames;
+
+ _saveLoadFlag = false;
+
+restart:;
+ do {
+ i = o_unk_132_helper(&b, buf);
+ } while (!b);
+
+ if (i == 205)
+ goto get_out;
+ if (!load) {
+ // if_1
+ if_1:;
+ unk132_result = i;
+
+ set_hitarea_bit_0x40(0xd0 + i);
+ leaveHitAreaById(0xd0 + i);
+
+ // some code here
+
+ fcs = _windowArray[5];
+
+ fcs->textRow = unk132_result;
+
+ if (_language == Common::HB_ISR) { //Hebrew
+ // init x offset with a 2 character savegame number + a period (18 pix)
+ fcs->textColumn = 3;
+ fcs->textColumnOffset = 6;
+ fcs->textLength = 3;
+ } else {
+ // init x offset with a 2 character savegame number + a period (18 pix)
+ fcs->textColumn = 2;
+ fcs->textColumnOffset = 2;
+ fcs->textLength = 3;
+ }
+
+ name = buf + i * 18;
+
+ // now process entire savegame name to get correct x offset for cursor
+ name_len = 0;
+ while (name[name_len]) {
+ if (_language == Common::HB_ISR) { //Hebrew
+ byte width = 6;
+ if (name[name_len] >= 64 && name[name_len] < 91)
+ width = _hebrew_char_widths [name[name_len] - 64];
+ fcs->textLength++;
+ fcs->textColumnOffset -= width;
+ if (fcs->textColumnOffset < width) {
+ fcs->textColumnOffset += 8;
+ fcs->textColumn++;
+ }
+ } else {
+ fcs->textLength++;
+ fcs->textColumnOffset += 6;
+ if (name[name_len] == 'i' || name[name_len] == 'l')
+ fcs->textColumnOffset -= 2;
+ if (fcs->textColumnOffset >= 8) {
+ fcs->textColumnOffset -= 8;
+ fcs->textColumn++;
+ }
+ }
+ name_len++;
+ }
+ // while_1_end
+
+ // do_3_start
+ for (;;) {
+ video_putchar(fcs, 0x7f);
+
+ _saveLoadFlag = true;
+
+ // do_2
+ do {
+ i = o_unk_132_helper(&b, buf);
+
+ if (b) {
+ if (i == 205)
+ goto get_out;
+ clear_hitarea_bit_0x40(0xd0 + unk132_result);
+ if (_saveLoadFlag) {
+ o_clearCharacter(_windowArray[5], 8);
+ // move code
+ }
+ goto if_1;
+ }
+
+ // is_not_b
+ if (!_saveLoadFlag) {
+ clear_hitarea_bit_0x40(0xd0 + unk132_result);
+ goto restart;
+ }
+ } while (i >= 0x80 || i == 0);
+
+ // after_do_2
+ o_clearCharacter(_windowArray[5], 8);
+ if (i == 10 || i == 13)
+ break;
+ if (i == 8) {
+ // do_backspace
+ if (name_len != 0) {
+ int x;
+ byte m;
+
+ name_len--;
+ m = name[name_len];
+
+ if (_language == Common::HB_ISR) //Hebrew
+ x = 8;
+ else
+ x = (name[name_len] == 'i' || name[name_len] == 'l') ? 1 : 8;
+
+ name[name_len] = 0;
+
+ o_clearCharacter(_windowArray[5], x, m);
+ }
+ } else if (i >= 32 && name_len != 17) {
+ name[name_len++] = i;
+
+ video_putchar(_windowArray[5], i);
+ }
+ }
+
+ // do_save
+ if (!saveGame(_saveLoadRowCurPos + unk132_result, buf + unk132_result * 18))
+ o_fileError(_windowArray[5], true);
+ } else {
+ if (!loadGame(_saveLoadRowCurPos + i))
+ o_fileError(_windowArray[5], false);
+ }
+
+get_out:;
+ o_unk_132_helper_3();
+
+ _base_time = time(NULL) - save_time + _base_time;
+ _copyPartialMode = 0;
+
+ dx_copy_rgn_from_3_to_2(94, 208, 46, 80);
+
+ i = _timer4;
+ do {
+ delay(10);
+ } while (i == _timer4);
+
+ g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+}
+
+void SimonEngine::o_fileError(FillOrCopyStruct *fcs, bool save_error) {
+ HitArea *ha;
+ const char *string, *string2;
+
+ if (save_error) {
+ string = "\r Save failed.";
+ string2 = "\r Disk error.";
+ } else {
+ string = "\r Load failed.";
+ string2 = "\r File not found.";
+ }
+
+ video_putchar(fcs, 0xC);
+ for (; *string; string++)
+ video_putchar(fcs, *string);
+ for (; *string2; string2++)
+ video_putchar(fcs, *string2);
+
+ fcs->textColumn = (fcs->width >> 1) - 3;
+ fcs->textRow = fcs->height - 1;
+ fcs->textLength = 0;
+
+ string = "[ OK ]";
+ for (; *string; string++)
+ video_putchar(fcs, *string);
+
+ ha = findEmptyHitArea();
+ ha->x = ((fcs->width >> 1) + (fcs->x - 3)) << 3;
+ ha->y = (fcs->height << 3) + fcs->y - 8;
+ ha->width = 48;
+ ha->height = 8;
+ ha->flags = 0x20;
+ ha->id = 0x7FFF;
+ ha->layer = 0x3EF;
+
+loop:;
+ _lastHitArea = _lastHitArea3 = 0;
+
+ do {
+ delay(1);
+ } while (_lastHitArea3 == 0);
+
+ ha = _lastHitArea;
+ if (ha == NULL || ha->id != 0x7FFF)
+ goto loop;
+
+ // Return
+ delete_hitarea(0x7FFF);
+}
+
+bool SimonEngine::saveGame(uint slot, char *caption) {
+ Common::OutSaveFile *f;
+ uint item_index, num_item, i, j;
+ TimeEvent *te;
+
+ _lockWord |= 0x100;
+
+ f = _saveFileMan->openForSaving(gen_savename(slot));
+ if (f == NULL) {
+ _lockWord &= ~0x100;
+ return false;
+ }
+
+ if (getGameType() == GType_FF) {
+ f->write(caption, 100);
+ } else {
+ f->write(caption, 18);
+ }
+
+ f->writeUint32BE(_itemArrayInited - 1);
+ f->writeUint32BE(0xFFFFFFFF);
+ f->writeUint32BE(0);
+ f->writeUint32BE(0);
+
+ i = 0;
+ for (te = _firstTimeStruct; te; te = te->next)
+ i++;
+ f->writeUint32BE(i);
+
+ for (te = _firstTimeStruct; te; te = te->next) {
+ f->writeUint32BE(te->time + _base_time);
+ f->writeUint16BE(te->subroutine_id);
+ }
+
+ item_index = 1;
+ for (num_item = _itemArrayInited - 1; num_item; num_item--) {
+ Item *item = _itemArrayPtr[item_index++];
+
+ f->writeUint16BE(item->parent);
+ f->writeUint16BE(item->sibling);
+ f->writeUint16BE(item->state);
+ f->writeUint16BE(item->classFlags);
+
+ Child1 *child1 = (Child1 *)findChildOfType(item, 1);
+ if (child1) {
+ f->writeUint16BE(child1->fr2);
+ }
+
+ Child2 *child2 = (Child2 *)findChildOfType(item, 2);
+ if (child2) {
+ f->writeUint32BE(child2->avail_props);
+ i = child2->avail_props & 1;
+
+ for (j = 1; j < 16; j++) {
+ if ((1 << j) & child2->avail_props) {
+ f->writeUint16BE(child2->array[i++]);
+ }
+ }
+ }
+
+ Child9 *child9 = (Child9 *)findChildOfType(item, 9);
+ if (child9) {
+ for (i = 0; i != 4; i++) {
+ f->writeUint16BE(child9->array[i]);
+ }
+ }
+ }
+
+ // write the 255 variables
+ for (i = 0; i != 255; i++) {
+ f->writeUint16BE(readVariable(i));
+ }
+
+ // write the items in array 6
+ for (i = 0; i != 10; i++) {
+ f->writeUint16BE(itemPtrToID(_itemArray6[i]));
+ }
+
+ // Write the bits in array 1 & 2
+ for (i = 0; i != 32; i++)
+ f->writeUint16BE(_bitArray[i]);
+
+ // Write the bits in array 3
+ if (getGameType() == GType_FF) {
+ for (i = 33; i != 48; i++)
+ f->writeUint16BE(_bitArray[i]);
+ }
+
+ f->flush();
+ bool result = !f->ioFailed();
+
+ delete f;
+ _lockWord &= ~0x100;
+
+ return result;
+}
+
+char *SimonEngine::gen_savename(int slot) {
+ static char buf[15];
+
+ if (getGameType() == GType_FF) {
+ if (slot == 999)
+ sprintf(buf, "save.%.3d", slot);
+ else
+ sprintf(buf, "feeble.%.3d", slot);
+ } else if (getGameType() == GType_SIMON2) {
+ sprintf(buf, "simon2.%.3d", slot);
+ } else {
+ sprintf(buf, "simon1.%.3d", slot);
+ }
+ return buf;
+}
+
+bool SimonEngine::loadGame(uint slot) {
+ char ident[100];
+ Common::InSaveFile *f;
+ uint num, item_index, i, j;
+
+ _lockWord |= 0x100;
+
+ f = _saveFileMan->openForLoading(gen_savename(slot));
+ if (f == NULL) {
+ _lockWord &= ~0x100;
+ return false;
+ }
+
+ if (getGameType() == GType_FF) {
+ f->read(ident, 100);
+ } else {
+ f->read(ident, 18);
+ }
+
+ num = f->readUint32BE();
+
+ if (f->readUint32BE() != 0xFFFFFFFF || num != _itemArrayInited - 1) {
+ delete f;
+ _lockWord &= ~0x100;
+ return false;
+ }
+
+ f->readUint32BE();
+ f->readUint32BE();
+ _noParentNotify = true;
+
+
+ // add all timers
+ killAllTimers();
+ for (num = f->readUint32BE(); num; num--) {
+ uint32 timeout = f->readUint32BE();
+ uint16 func_to_call = f->readUint16BE();
+ addTimeEvent(timeout, func_to_call);
+ }
+
+ item_index = 1;
+ for (num = _itemArrayInited - 1; num; num--) {
+ Item *item = _itemArrayPtr[item_index++], *parent_item;
+
+ uint parent = f->readUint16BE();
+ uint sibling = f->readUint16BE();
+
+ parent_item = derefItem(parent);
+
+ setItemParent(item, parent_item);
+
+ if (parent_item == NULL) {
+ item->parent = parent;
+ item->sibling = sibling;
+ }
+
+ item->state = f->readUint16BE();
+ item->classFlags = f->readUint16BE();
+
+ Child1 *child1 = (Child1 *)findChildOfType(item, 1);
+ if (child1 != NULL) {
+ child1->fr2 = f->readUint16BE();
+ }
+
+ Child2 *child2 = (Child2 *)findChildOfType(item, 2);
+ if (child2 != NULL) {
+ child2->avail_props = f->readUint32BE();
+ i = child2->avail_props & 1;
+
+ for (j = 1; j < 16; j++) {
+ if ((1 << j) & child2->avail_props) {
+ child2->array[i++] = f->readUint16BE();
+ }
+ }
+ }
+
+ Child9 *child9 = (Child9 *) findChildOfType(item, 9);
+ if (child9) {
+ for (i = 0; i != 4; i++) {
+ child9->array[i] = f->readUint16BE();
+ }
+ }
+ }
+
+
+ // read the 255 variables
+ for (i = 0; i != 255; i++) {
+ writeVariable(i, f->readUint16BE());
+ }
+
+ // read the items in array 6
+ for (i = 0; i != 10; i++) {
+ _itemArray6[i] = derefItem(f->readUint16BE());
+ }
+
+ // Read the bits in array 1 & 2
+ for (i = 0; i != 32; i++)
+ _bitArray[i] = f->readUint16BE();
+
+ // Read the bits in array 3
+ if (getGameType() == GType_FF) {
+ for (i = 33; i != 48; i++)
+ _bitArray[i] = f->readUint16BE();
+ }
+
+ if (f->ioFailed()) {
+ error("load failed");
+ }
+
+ delete f;
+
+ _noParentNotify = false;
+
+ _lockWord &= ~0x100;
+
+ return true;
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/simon.cpp b/engines/simon/simon.cpp
new file mode 100644
index 0000000000..78d0eac221
--- /dev/null
+++ b/engines/simon/simon.cpp
@@ -0,0 +1,4292 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "common/system.h"
+
+#include "gui/about.h"
+#include "gui/message.h"
+
+#include "simon/simon.h"
+#include "simon/intern.h"
+#include "simon/vga.h"
+#include "simon/debugger.h"
+
+#include "sound/mididrv.h"
+#ifdef _WIN32_WCE
+extern bool isSmartphone(void);
+#endif
+
+#ifdef PALMOS_68K
+#include "globals.h"
+#endif
+
+using Common::File;
+
+struct ObsoleteTargets {
+ const char *from;
+ const char *to;
+ Common::Platform platform;
+
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { from, "Obsolete Target", 0 };
+ return dummy;
+ }
+};
+
+/**
+ * Conversion table mapping old obsolete target names to the
+ * corresponding new target and platform combination.
+ *
+ */
+static ObsoleteTargets obsoleteTargetsTable[] = {
+ {"simon1acorn", "simon1", Common::kPlatformAcorn},
+ {"simon1amiga", "simon1", Common::kPlatformAmiga},
+ {"simon1cd32", "simon1", Common::kPlatformAmiga},
+ {"simon1dos", "simon1", Common::kPlatformPC},
+ {"simon1talkie", "simon1", Common::kPlatformPC},
+ {"simon1win", "simon1", Common::kPlatformWindows},
+ {"simon2dos", "simon2", Common::kPlatformPC},
+ {"simon2talkie", "simon2", Common::kPlatformPC},
+ {"simon2mac", "simon2", Common::kPlatformMacintosh},
+ {"simon2win", "simon2", Common::kPlatformWindows},
+ {NULL, NULL, Common::kPlatformUnknown}
+};
+
+static const GameSettings simonGames[] = {
+ // Simon the Sorcerer 1 & 2 (not SCUMM games)
+ {"feeble", "The Feeble Files", 0},
+ {"simon1", "Simon the Sorcerer 1", 0},
+ {"simon2", "Simon the Sorcerer 2", 0},
+
+ {"simon1acorn", "Simon the Sorcerer 1 (Acorn)", 0},
+ {"simon1amiga", "Simon the Sorcerer 1 (Amiga)", 0},
+ {"simon1cd32", "Simon the Sorcerer 1 Talkie (Amiga CD32)", 0},
+ {"simon1demo", "Simon the Sorcerer 1 (DOS Demo)", 0},
+ {"simon1dos", "Simon the Sorcerer 1 (DOS)", 0},
+ {"simon1talkie", "Simon the Sorcerer 1 Talkie", 0},
+ {"simon1win", "Simon the Sorcerer 1 Talkie (Windows)", 0},
+ {"simon2dos", "Simon the Sorcerer 2 (DOS)", 0},
+ {"simon2talkie", "Simon the Sorcerer 2 Talkie", 0},
+ {"simon2win", "Simon the Sorcerer 2 Talkie (Windows)", 0},
+ {"simon2mac", "Simon the Sorcerer 2 Talkie (Amiga or Mac)", 0},
+
+ {NULL, NULL, 0}
+};
+
+GameList Engine_SIMON_gameList() {
+ GameList games;
+ const GameSettings *g = simonGames;
+ while (g->gameid) {
+ games.push_back(*g);
+ g++;
+ }
+
+ return games;
+}
+
+DetectedGameList Engine_SIMON_detectGames(const FSList &fslist) {
+ return Simon::GAME_ProbeGame(fslist);
+}
+
+Engine *Engine_SIMON_create(GameDetector *detector, OSystem *syst) {
+ const ObsoleteTargets *o = obsoleteTargetsTable;
+ while (o->from) {
+ if (!scumm_stricmp(detector->_game.gameid, o->from)) {
+ detector->_game.gameid = o->to;
+
+ ConfMan.set("gameid", o->to);
+
+ if (o->platform != Common::kPlatformUnknown)
+ ConfMan.set("platform", Common::getPlatformCode(o->platform));
+
+ warning("Target upgraded from %s to %s", o->from, o->to);
+ ConfMan.flushToDisk();
+ break;
+ }
+ o++;
+ }
+
+ return new Simon::SimonEngine(detector, syst);
+}
+
+REGISTER_PLUGIN(SIMON, "Simon the Sorcerer")
+
+namespace Simon {
+
+#ifdef PALMOS_68K
+#define PTR(a) a
+static const GameSpecificSettings *simon1_settings;
+static const GameSpecificSettings *simon1acorn_settings;
+static const GameSpecificSettings *simon1amiga_settings;
+static const GameSpecificSettings *simon1demo_settings;
+static const GameSpecificSettings *simon2win_settings;
+static const GameSpecificSettings *simon2dos_settings;
+static const GameSpecificSettings *feeblefiles_settings;
+#else
+#define PTR(a) &a
+static const GameSpecificSettings simon1_settings = {
+ "SIMON.GME", // gme_filename
+ "SIMON.WAV", // wav_filename
+ "SIMON.VOC", // voc_filename
+ "SIMON.MP3", // mp3_filename
+ "SIMON.OGG", // vorbis_filename
+ "SIMON.FLA", // flac_filename
+ "EFFECTS.VOC", // voc_effects_filename
+ "EFFECTS.MP3", // mp3_effects_filename
+ "EFFECTS.OGG", // vorbis_effects_filename
+ "EFFECTS.FLA", // flac_effects_filename
+ "GAMEPC", // gamepc_filename
+};
+
+static const GameSpecificSettings simon1acorn_settings = {
+ "DATA", // gme_filename
+ "", // wav_filename
+ "SIMON", // voc_filename
+ "SIMON.MP3", // mp3_filename
+ "SIMON.OGG", // vorbis_filename
+ "SIMON.FLA", // flac_filename
+ "EFFECTS", // voc_effects_filename
+ "EFFECTS.MP3", // mp3_effects_filename
+ "EFFECTS.OGG", // vorbis_effects_filename
+ "EFFECTS.FLA", // flac_effects_filename
+ "GAMEBASE", // gamepc_filename
+};
+
+static const GameSpecificSettings simon1amiga_settings = {
+ "", // gme_filename
+ "", // wav_filename
+ "", // voc_filename
+ "SIMON.MP3", // mp3_filename
+ "SIMON.OGG", // vorbis_filename
+ "SIMON.FLA", // flac_filename
+ "", // voc_effects_filename
+ "", // mp3_effects_filename
+ "", // vorbis_effects_filename
+ "", // flac_effects_filename
+ "gameamiga", // gamepc_filename
+};
+
+static const GameSpecificSettings simon1demo_settings = {
+ "", // gme_filename
+ "", // wav_filename
+ "", // voc_filename
+ "", // mp3_filename
+ "", // vorbis_filename
+ "", // flac_filename
+ "", // voc_effects_filename
+ "", // mp3_effects_filename
+ "", // vorbis_effects_filename
+ "", // flac_effects_filename
+ "GDEMO", // gamepc_filename
+};
+
+static const GameSpecificSettings simon2win_settings = {
+ "SIMON2.GME", // gme_filename
+ "SIMON2.WAV", // wav_filename
+ "SIMON2.VOC", // voc_filename
+ "SIMON2.MP3", // mp3_filename
+ "SIMON2.OGG", // vorbis_filename
+ "SIMON2.FLA", // flac_filename
+ "", // voc_effects_filename
+ "", // mp3_effects_filename
+ "", // vorbis_effects_filename
+ "", // flac_effects_filename
+ "GSPTR30", // gamepc_filename
+};
+
+static const GameSpecificSettings simon2dos_settings = {
+ "SIMON2.GME", // gme_filename
+ "", // wav_filename
+ "", // voc_filename
+ "", // mp3_filename
+ "", // vorbis_filename
+ "", // flac_filename
+ "", // voc_effects_filename
+ "", // mp3_effects_filename
+ "", // vorbis_effects_filename
+ "", // flac_effects_filename
+ "GAME32", // gamepc_filename
+};
+
+static const GameSpecificSettings feeblefiles_settings = {
+ "", // gme_filename
+ "VOICES.WAV", // wav_filename
+ "VOICES.VOC", // voc_filename
+ "VOICES.MP3", // mp3_filename
+ "VOICES.OGG", // vorbis_filename
+ "VOICES.FLA", // flac_filename
+ "", // voc_effects_filename
+ "", // mp3_effects_filename
+ "", // vorbis_effects_filename
+ "", // flac_effects_filename
+ "GAME22", // gamepc_filename
+};
+#endif
+
+SimonEngine::SimonEngine(GameDetector *detector, OSystem *syst)
+ : Engine(syst), midi(syst) {
+ _vcPtr = 0;
+ _vc_get_out_of_code = 0;
+ _gameOffsetsPtr = 0;
+
+ _debugger = 0;
+ setupVgaOpcodes();
+
+ _keyPressed = 0;
+
+ _gameFile = 0;
+
+ _strippedTxtMem = 0;
+ _textSize = 0;
+ _stringTabNum = 0;
+ _stringTabPos = 0;
+ _stringtab_numalloc = 0;
+ _stringTabPtr = 0;
+
+ _itemArrayPtr = 0;
+ _itemArraySize = 0;
+ _itemArrayInited = 0;
+
+ _itemHeapPtr = 0;
+ _itemHeapCurPos = 0;
+ _itemHeapSize = 0;
+
+ _iconFilePtr = 0;
+
+ _tblList = 0;
+
+ _codePtr = 0;
+
+ _localStringtable = 0;
+ _stringIdLocalMin = 1;
+ _stringIdLocalMax = 0;
+
+ _tablesHeapPtr = 0;
+ _tablesHeapPtrOrg = 0;
+ _tablesheapPtrNew = 0;
+ _tablesHeapSize = 0;
+ _tablesHeapCurPos = 0;
+ _tablesHeapCurPosOrg = 0;
+ _tablesHeapCurPosNew = 0;
+
+ _subroutineList = 0;
+ _subroutineListOrg = 0;
+ _subroutine = 0;
+
+ _dxSurfacePitch = 0;
+
+ _recursionDepth = 0;
+
+ _lastVgaTick = 0;
+
+ _marks = 0;
+
+ _scriptVar2 = 0;
+ _runScriptReturn1 = 0;
+ _skipVgaWait = 0;
+ _noParentNotify = 0;
+ _beardLoaded = 0;
+ _hitarea_unk_3 = 0;
+ _mortalFlag = 0;
+ _updateScreen = 0;
+ _usePaletteDelay = 0;
+ _syncFlag2 = 0;
+ _inCallBack = 0;
+ _cepeFlag = 0;
+ _copyPartialMode = 0;
+ _speed = 1;
+ _fastMode = 0;
+ _dxUse3Or4ForLock = 0;
+
+ _debugMode = 0;
+ _pause = 0;
+ _startMainScript = 0;
+ _continousMainScript = 0;
+ _startVgaScript = 0;
+ _continousVgaScript = 0;
+ _drawImagesDebug = 0;
+ _dumpImages = 0;
+ _speech = true;
+ _subtitles = true;
+ _fade = true;
+ _mouseCursor = 0;
+ _vgaVar9 = 0;
+ _scriptUnk1 = 0;
+ _vgaVar6 = 0;
+ _scrollX = 0;
+ _scrollY = 0;
+ _scrollXMax = 0;
+ _scrollYMax = 0;
+ _scrollCount = 0;
+ _scrollFlag = 0;
+ _scrollHeight = 0;
+ _scrollWidth = 0;
+ _scrollImage = 0;
+ _vgaVar8 = 0;
+
+ _scriptVerb = 0;
+ _scriptNoun1 = 0;
+ _scriptNoun2 = 0;
+ _scriptAdj1 = 0;
+ _scriptAdj2 = 0;
+
+ _curWindow = 0;
+ _textWindow = 0;
+
+ _subjectItem = 0;
+ _objectItem = 0;
+ _item1 = 0;
+
+ _hitAreaObjectItem = 0;
+ _lastHitArea = 0;
+ _lastHitArea2Ptr = 0;
+ _lastHitArea3 = 0;
+ _leftButtonDown = 0;
+ _hitAreaSubjectItem = 0;
+ _hitAreaPtr5 = 0;
+ _hitAreaPtr7 = 0;
+ _needHitAreaRecalc = 0;
+ _verbHitArea = 0;
+ _hitAreaUnk4 = 0;
+ _mouseHideCount = 0;
+
+ _windowNum = 0;
+
+ _printCharCurPos = 0;
+ _printCharMaxPos = 0;
+ _numLettersToPrint = 0;
+
+ _lastTime = 0;
+
+ _firstTimeStruct = 0;
+ _pendingDeleteTimeEvent = 0;
+
+ _base_time = 0;
+
+ _mouseX = 0;
+ _mouseY = 0;
+ _mouseXOld = 0;
+ _mouseYOld = 0;
+
+ _dummyItem1 = new Item();
+ _dummyItem2 = new Item();
+ _dummyItem3 = new Item();
+
+ _lockWord = 0;
+ _scrollUpHitArea = 0;
+ _scrollDownHitArea = 0;
+
+ _videoVar7 = 0xFFFF;
+ _paletteColorCount = 0;
+
+ _videoVar4 = 0;
+ _videoVar5 = 0;
+ _fastFadeOutFlag = 0;
+ _unkPalFlag = 0;
+ _exitCutscene = 0;
+ _skipSpeech = 0;
+ _paletteFlag = 0;
+
+ _soundFileId = 0;
+ _lastMusicPlayed = -1;
+ _nextMusicToPlay = -1;
+
+ _showPreposition = 0;
+ _showMessageFlag = 0;
+
+ _videoNumPalColors = 0;
+
+ _vgaSpriteChanged = 0;
+
+ _vgaBufFreeStart = 0;
+ _vgaBufEnd = 0;
+ _vgaBufStart = 0;
+ _vgaFileBufOrg = 0;
+ _vgaFileBufOrg2 = 0;
+
+ _curVgaFile1 = 0;
+ _curVgaFile2 = 0;
+ _curSfxFile = 0;
+
+ _timer1 = 0;
+ _timer5 = 0;
+ _timer4 = 0;
+
+ _frameRate = 1;
+
+ _vgaCurFile2 = 0;
+ _vgaWaitFor = 0;
+ _vgaCurFileId = 0;
+ _vgaCurSpriteId = 0;
+
+ _nextVgaTimerToProcess = 0;
+
+ memset(_vcItemArray, 0, sizeof(_vcItemArray));
+ memset(_itemArray6, 0, sizeof(_itemArray6));
+
+ memset(_stringIdArray2, 0, sizeof(_stringIdArray2));
+ memset(_stringIdArray3, 0, sizeof(_stringIdArray3));
+ memset(_speechIdArray4, 0, sizeof(_speechIdArray4));
+
+ memset(_bitArray, 0, sizeof(_bitArray));
+ memset(_variableArray, 0, sizeof(_variableArray));
+
+ memset(_windowArray, 0, sizeof(_windowArray));
+
+ memset(_fcsData1, 0, sizeof(_fcsData1));
+ memset(_fcsData2, 0, sizeof(_fcsData2));
+
+ _freeStringSlot = 0;
+
+ memset(_stringReturnBuffer, 0, sizeof(_stringReturnBuffer));
+
+ memset(_pathFindArray, 0, sizeof(_pathFindArray));
+
+ memset(_pathValues, 0, sizeof(_pathValues));
+ _PVCount = 0;
+ _GPVCount = 0;
+
+ memset(_pathValues1, 0, sizeof(_pathValues1));
+ _PVCount1 = 0;
+ _GPVCount1 = 0;
+
+ memset(_paletteBackup, 0, sizeof(_paletteBackup));
+ memset(_palette, 0, sizeof(_palette));
+
+ memset(_videoBuf1, 0, sizeof(_videoBuf1));
+
+ _fcs_list = new FillOrCopyStruct[16];
+
+ memset(_lettersToPrintBuf, 0, sizeof(_lettersToPrintBuf));
+
+ _numScreenUpdates = 0;
+ _vgaTickCounter = 0;
+
+ _sound = 0;
+
+ _effectsPaused = false;
+ _ambientPaused = false;
+ _musicPaused = false;
+
+ _dumpFile = 0;
+
+ _saveLoadType = 0;
+ _saveLoadSlot = 0;
+ memset(_saveLoadName, 0, sizeof(_saveLoadName));
+
+ _saveLoadRowCurPos = 0;
+ _numSaveGameRows = 0;
+ _saveDialogFlag = false;
+ _saveOrLoad = false;
+ _saveLoadFlag = false;
+
+ _sdlMouseX = 0;
+ _sdlMouseY = 0;
+
+ _sdl_buf_3 = 0;
+ _sdl_buf = 0;
+ _sdl_buf_attached = 0;
+
+ _vc10BasePtrOld = 0;
+ memcpy (_hebrew_char_widths,
+ "\x5\x5\x4\x6\x5\x3\x4\x5\x6\x3\x5\x5\x4\x6\x5\x3\x4\x6\x5\x6\x6\x6\x5\x5\x5\x6\x5\x6\x6\x6\x6\x6", 32);
+
+}
+
+int SimonEngine::init(GameDetector &detector) {
+
+ // Add default file directories for Acorn version
+ File::addDefaultDirectory(_gameDataPath + "execute/");
+ File::addDefaultDirectory(_gameDataPath + "EXECUTE/");
+
+ // Detect game
+ if (!initGame()) {
+ return -1;
+ }
+
+ if (getGameType() == GType_FF) {
+ _screenWidth = 640;
+ _screenHeight = 480;
+ } else {
+ _screenWidth = 320;
+ _screenHeight = 200;
+ }
+
+ // Setup mixer
+ if (!_mixer->isReady())
+ warning("Sound initialization failed. "
+ "Features of the game that depend on sound synchronization will most likely break");
+ set_volume(ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ _system->initSize(_screenWidth, _screenHeight);
+ if (getGameType() == GType_FF)
+ _system->setGraphicsMode("1x");
+ _system->endGFXTransaction();
+
+ // Setup midi driver
+ MidiDriver *driver = 0;
+ if (getPlatform() == Common::kPlatformAmiga) {
+ driver = MidiDriver::createMidi(MD_NULL); // Create fake MIDI driver for Simon1Amiga and Simon2CD32 for now
+ _native_mt32 = false;
+ } else {
+ int midiDriver = MidiDriver::detectMusicDriver(MDT_ADLIB | MDT_MIDI);
+ _native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ driver = MidiDriver::createMidi(midiDriver);
+ if (_native_mt32) {
+ driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+ }
+ }
+
+ midi.mapMT32toGM (getGameType() == GType_SIMON1 && !_native_mt32);
+
+ midi.set_driver(driver);
+ int ret = midi.open();
+ if (ret)
+ warning ("MIDI Player init failed: \"%s\"", midi.getErrorName (ret));
+ midi.set_volume(ConfMan.getInt("music_volume"));
+
+ _debugMode = (gDebugLevel >= 0);
+ _language = Common::parseLanguage(ConfMan.get("language"));
+
+ if (ConfMan.hasKey("music_mute") && ConfMan.getBool("music_mute") == 1)
+ midi.pause(_musicPaused ^= 1);
+
+ if ((getGameType() == GType_SIMON2) && ConfMan.hasKey("speech_mute") && ConfMan.getBool("speech_mute") == 1)
+ _speech = 0;
+
+ if ((getGameType() == GType_SIMON1 && _language > 1) || ((getGameType() == GType_SIMON2) && _language == Common::HB_ISR)) {
+ if (ConfMan.hasKey("subtitles") && ConfMan.getBool("subtitles") == 0)
+ _subtitles = 0;
+ } else
+ _subtitles = ConfMan.getBool("subtitles");
+
+ // Make sure either speech or subtitles is enabled
+ if ((getFeatures() & GF_TALKIE) && !_speech && !_subtitles)
+ _subtitles = 1;
+
+ if (ConfMan.hasKey("fade") && ConfMan.getBool("fade") == 0)
+ _fade = 0;
+
+ if (ConfMan.hasKey("slow_down") && ConfMan.getInt("slow_down") >= 1)
+ _speed = ConfMan.getInt("slow_down");
+
+ // FIXME Use auto dirty rects cleanup code to reduce CPU usage
+ g_system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, true);
+
+ VGA_DELAY_BASE = 1;
+ if (getGameType() == GType_FF) {
+ NUM_VIDEO_OP_CODES = 85;
+#ifndef PALMOS_68K
+ VGA_MEM_SIZE = 7500000;
+#else
+ VGA_MEM_SIZE = gVars->memory[kMemSimon2Games];
+#endif
+ TABLES_MEM_SIZE = 200000;
+ } else if (getGameType() == GType_SIMON2) {
+ TABLE_INDEX_BASE = 1580 / 4;
+ TEXT_INDEX_BASE = 1500 / 4;
+ NUM_VIDEO_OP_CODES = 75;
+#ifndef PALMOS_68K
+ VGA_MEM_SIZE = 2000000;
+#else
+ VGA_MEM_SIZE = gVars->memory[kMemSimon2Games];
+#endif
+ TABLES_MEM_SIZE = 100000;
+ // Check whether to use MT-32 MIDI tracks in Simon the Sorcerer 2
+ if ((getGameType() == GType_SIMON2) && _native_mt32)
+ MUSIC_INDEX_BASE = (1128 + 612) / 4;
+ else
+ MUSIC_INDEX_BASE = 1128 / 4;
+ SOUND_INDEX_BASE = 1660 / 4;
+ } else {
+ TABLE_INDEX_BASE = 1576 / 4;
+ TEXT_INDEX_BASE = 1460 / 4;
+ NUM_VIDEO_OP_CODES = 64;
+#ifndef PALMOS_68K
+ VGA_MEM_SIZE = 1000000;
+#else
+ VGA_MEM_SIZE = gVars->memory[kMemSimon1Games];
+#endif
+ TABLES_MEM_SIZE = 50000;
+ MUSIC_INDEX_BASE = 1316 / 4;
+ SOUND_INDEX_BASE = 0;
+ }
+
+ if (getGameType() == GType_FF) {
+ gss = PTR(feeblefiles_settings);
+ } else if (getGameType() == GType_SIMON2) {
+ if (getFeatures() & GF_TALKIE) {
+ gss = PTR(simon2win_settings);
+
+ // Add default file directories
+ File::addDefaultDirectory(_gameDataPath + "voices/");
+ File::addDefaultDirectory(_gameDataPath + "VOICES/");
+ } else {
+ gss = PTR(simon2dos_settings);
+ }
+ } else if (getGameType() == GType_SIMON1) {
+ if (getPlatform() == Common::kPlatformAcorn) {
+ gss = PTR(simon1acorn_settings);
+ } else if (getPlatform() == Common::kPlatformAmiga) {
+ gss = PTR(simon1amiga_settings);
+ } else if (getGameId() == GID_SIMON1DEMO) {
+ gss = PTR(simon1demo_settings);
+ } else {
+ gss = PTR(simon1_settings);
+ }
+ }
+
+ if ((getGameType() == GType_SIMON1) && (getFeatures() & GF_TALKIE)) {
+ // Add default file directories
+ switch (_language) {
+ case Common::HB_ISR:
+ File::addDefaultDirectory(_gameDataPath + "hebrew/");
+ File::addDefaultDirectory(_gameDataPath + "HEBREW/");
+ break;
+ case Common::ES_ESP:
+ File::addDefaultDirectory(_gameDataPath + "spanish/");
+ File::addDefaultDirectory(_gameDataPath + "SPANISH/");
+ break;
+ case Common::IT_ITA:
+ File::addDefaultDirectory(_gameDataPath + "italian/");
+ File::addDefaultDirectory(_gameDataPath + "ITALIAN/");
+ break;
+ case Common::FR_FRA:
+ File::addDefaultDirectory(_gameDataPath + "french/");
+ File::addDefaultDirectory(_gameDataPath + "FRENCH/");
+ break;
+ }
+ }
+
+ return 0;
+}
+
+SimonEngine::~SimonEngine() {
+ delete _gameFile;
+
+ midi.close();
+
+ free(_stringTabPtr);
+ free(_itemArrayPtr);
+ free(_itemHeapPtr - _itemHeapCurPos);
+ free(_tablesHeapPtr - _tablesHeapCurPos);
+ free(_tblList);
+ free(_iconFilePtr);
+ free(_gameOffsetsPtr);
+
+ delete _dummyItem1;
+ delete _dummyItem2;
+ delete _dummyItem3;
+
+ delete [] _fcs_list;
+
+ delete _sound;
+ delete _debugger;
+}
+
+void SimonEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+
+#ifdef _WIN32_WCE
+ if (isSmartphone())
+ return;
+#endif
+
+ // Unless an error -originated- within the debugger, spawn the
+ // debugger. Otherwise exit out normally.
+ if (_debugger && !_debugger->isAttached()) {
+ // (Print it again in case debugger segfaults)
+ printf("%s\n", buf2);
+ _debugger->attach(buf2);
+ _debugger->onFrame();
+ }
+}
+
+void SimonEngine::palette_fadeout(uint32 *pal_values, uint num) {
+ byte *p = (byte *)pal_values;
+
+ do {
+ if (p[0] >= 8)
+ p[0] -= 8;
+ else
+ p[0] = 0;
+ if (p[1] >= 8)
+ p[1] -= 8;
+ else
+ p[1] = 0;
+ if (p[2] >= 8)
+ p[2] -= 8;
+ else
+ p[2] = 0;
+ p += 4;
+ } while (--num);
+}
+
+byte *SimonEngine::allocateItem(uint size) {
+ byte *org = _itemHeapPtr;
+ size = (size + 3) & ~3;
+
+ _itemHeapPtr += size;
+ _itemHeapCurPos += size;
+
+ if (_itemHeapCurPos > _itemHeapSize)
+ error("Itemheap overflow");
+
+ return org;
+}
+
+void SimonEngine::alignTableMem() {
+ if ((unsigned long)_tablesHeapPtr & 3) {
+ _tablesHeapPtr += 2;
+ _tablesHeapCurPos += 2;
+ }
+}
+
+byte *SimonEngine::allocateTable(uint size) {
+ byte *org = _tablesHeapPtr;
+
+ size = (size + 1) & ~1;
+
+ _tablesHeapPtr += size;
+ _tablesHeapCurPos += size;
+
+ if (_tablesHeapCurPos > _tablesHeapSize)
+ error("Tablesheap overflow");
+
+ return org;
+}
+
+int SimonEngine::allocGamePcVars(File *in) {
+ uint item_array_size, item_array_inited, stringtable_num;
+ uint32 version;
+ uint i;
+
+ item_array_size = in->readUint32BE();
+ version = in->readUint32BE();
+ item_array_inited = in->readUint32BE();
+ stringtable_num = in->readUint32BE();
+
+ item_array_inited += 2; // first two items are predefined
+ item_array_size += 2;
+
+ if (version != 0x80)
+ error("Not a runtime database");
+
+ _itemArrayPtr = (Item **)calloc(item_array_size, sizeof(Item *));
+ if (_itemArrayPtr == NULL)
+ error("Out of memory for Item array");
+
+ _itemArraySize = item_array_size;
+ _itemArrayInited = item_array_inited;
+
+ for (i = 1; i < item_array_inited; i++) {
+ _itemArrayPtr[i] = (Item *)allocateItem(sizeof(Item));
+ }
+
+ // The rest is cleared automatically by calloc
+ allocateStringTable(stringtable_num + 10);
+ _stringTabNum = stringtable_num;
+
+ return item_array_inited;
+}
+
+void SimonEngine::loginPlayerHelper(Item *item, int a, int b) {
+ Child9 *child;
+
+ child = (Child9 *) findChildOfType(item, 9);
+ if (child == NULL) {
+ child = (Child9 *) allocateChildBlock(item, 9, sizeof(Child9));
+ }
+
+ if (a >= 0 && a <= 3)
+ child->array[a] = b;
+}
+
+void SimonEngine::loginPlayer() {
+ Child *child;
+
+ _item1 = _itemArrayPtr[1];
+ _item1->adjective = -1;
+ _item1->noun = 10000;
+
+ child = (Child *)allocateChildBlock(_item1, 3, sizeof(Child));
+ if (child == NULL)
+ error("player create failure");
+
+ loginPlayerHelper(_item1, 0, 0);
+}
+
+void SimonEngine::allocateStringTable(int num) {
+ _stringTabPtr = (byte **)calloc(num, sizeof(byte *));
+ _stringTabPos = 0;
+ _stringtab_numalloc = num;
+}
+
+void SimonEngine::setupStringTable(byte *mem, int num) {
+ int i = 0;
+ for (;;) {
+ _stringTabPtr[i++] = mem;
+ if (--num == 0)
+ break;
+ for (; *mem; mem++);
+ mem++;
+ }
+
+ _stringTabPos = i;
+}
+
+void SimonEngine::setupLocalStringTable(byte *mem, int num) {
+ int i = 0;
+ for (;;) {
+ _localStringtable[i++] = mem;
+ if (--num == 0)
+ break;
+ for (; *mem; mem++);
+ mem++;
+ }
+}
+
+void SimonEngine::readSubroutineLine(File *in, SubroutineLine *sl, Subroutine *sub) {
+ byte line_buffer[1024], *q = line_buffer;
+ int size;
+
+ if (sub->id == 0) {
+ sl->verb = in->readUint16BE();
+ sl->noun1 = in->readUint16BE();
+ sl->noun2 = in->readUint16BE();
+ }
+
+ while ((*q = in->readByte()) != 0xFF) {
+ if (*q == 87) {
+ in->readUint16BE();
+ } else {
+ q = readSingleOpcode(in, q);
+ }
+ }
+
+ size = q - line_buffer + 1;
+
+ memcpy(allocateTable(size), line_buffer, size);
+}
+
+SubroutineLine *SimonEngine::createSubroutineLine(Subroutine *sub, int where) {
+ SubroutineLine *sl, *cur_sl = NULL, *last_sl = NULL;
+
+ if (sub->id == 0)
+ sl = (SubroutineLine *)allocateTable(SUBROUTINE_LINE_BIG_SIZE);
+ else
+ sl = (SubroutineLine *)allocateTable(SUBROUTINE_LINE_SMALL_SIZE);
+
+ // where is what offset to insert the line at, locate the proper beginning line
+ if (sub->first != 0) {
+ cur_sl = (SubroutineLine *)((byte *)sub + sub->first);
+ while (where) {
+ last_sl = cur_sl;
+ cur_sl = (SubroutineLine *)((byte *)sub + cur_sl->next);
+ if ((byte *)cur_sl == (byte *)sub)
+ break;
+ where--;
+ }
+ }
+
+ if (last_sl != NULL) {
+ // Insert the subroutine line in the middle of the link
+ last_sl->next = (byte *)sl - (byte *)sub;
+ sl->next = (byte *)cur_sl - (byte *)sub;
+ } else {
+ // Insert the subroutine line at the head of the link
+ sl->next = sub->first;
+ sub->first = (byte *)sl - (byte *)sub;
+ }
+
+ return sl;
+}
+
+void SimonEngine::readSubroutine(File *in, Subroutine *sub) {
+ while (in->readUint16BE() == 0) {
+ readSubroutineLine(in, createSubroutineLine(sub, 0xFFFF), sub);
+ }
+}
+
+Subroutine *SimonEngine::createSubroutine(uint id) {
+ Subroutine *sub;
+
+ alignTableMem();
+
+ sub = (Subroutine *)allocateTable(sizeof(Subroutine));
+ sub->id = id;
+ sub->first = 0;
+ sub->next = _subroutineList;
+ _subroutineList = sub;
+ return sub;
+}
+
+void SimonEngine::readSubroutineBlock(File *in) {
+ while (in->readUint16BE() == 0) {
+ readSubroutine(in, createSubroutine(in->readUint16BE()));
+ }
+}
+
+Child *SimonEngine::findChildOfType(Item *i, uint type) {
+ Child *child = i->children;
+ for (; child; child = child->next)
+ if (child->type == type)
+ return child;
+ return NULL;
+}
+
+bool SimonEngine::isRoom(Item *item) {
+ return findChildOfType(item, 1) != NULL;
+}
+
+bool SimonEngine::isObject(Item *item) {
+ return findChildOfType(item, 2) != NULL;
+}
+
+uint SimonEngine::getOffsetOfChild2Param(Child2 *child, uint prop) {
+ uint m = 1;
+ uint offset = 0;
+ while (m != prop) {
+ if (child->avail_props & m)
+ offset++;
+ m *= 2;
+ }
+ return offset;
+}
+
+Child *SimonEngine::allocateChildBlock(Item *i, uint type, uint size) {
+ Child *child = (Child *)allocateItem(size);
+ child->next = i->children;
+ i->children = child;
+ child->type = type;
+ return child;
+}
+
+void SimonEngine::allocItemHeap() {
+ _itemHeapSize = 10000;
+ _itemHeapCurPos = 0;
+ _itemHeapPtr = (byte *)calloc(10000, 1);
+}
+
+void SimonEngine::allocTablesHeap() {
+ _tablesHeapSize = TABLES_MEM_SIZE;
+ _tablesHeapCurPos = 0;
+ _tablesHeapPtr = (byte *)calloc(TABLES_MEM_SIZE, 1);
+}
+
+void SimonEngine::setItemState(Item *item, int value) {
+ item->state = value;
+}
+
+int SimonEngine::getNextWord() {
+ int16 a = (int16)READ_BE_UINT16(_codePtr);
+ _codePtr += 2;
+ return a;
+}
+
+uint SimonEngine::getNextStringID() {
+ return (uint16)getNextWord();
+}
+
+uint SimonEngine::getVarOrByte() {
+ uint a = *_codePtr++;
+ if (a != 255)
+ return a;
+ return readVariable(*_codePtr++);
+}
+
+uint SimonEngine::getVarOrWord() {
+ uint a = READ_BE_UINT16(_codePtr);
+ _codePtr += 2;
+ if (a >= 30000 && a < 30512)
+ return readVariable(a - 30000);
+ return a;
+}
+
+Item *SimonEngine::getNextItemPtr() {
+ int a = getNextWord();
+ switch (a) {
+ case -1:
+ return _subjectItem;
+ case -3:
+ return _objectItem;
+ case -5:
+ return getItem1Ptr();
+ case -7:
+ return getItemPtrB();
+ case -9:
+ return derefItem(getItem1Ptr()->parent);
+ default:
+ return derefItem(a);
+ }
+}
+
+Item *SimonEngine::getNextItemPtrStrange() {
+ int a = getNextWord();
+ switch (a) {
+ case -1:
+ return _subjectItem;
+ case -3:
+ return _objectItem;
+ case -5:
+ return _dummyItem2;
+ case -7:
+ return NULL;
+ case -9:
+ return _dummyItem3;
+ default:
+ return derefItem(a);
+ }
+}
+
+uint SimonEngine::getNextItemID() {
+ int a = getNextWord();
+ switch (a) {
+ case -1:
+ return itemPtrToID(_subjectItem);
+ case -3:
+ return itemPtrToID(_objectItem);
+ case -5:
+ return getItem1ID();
+ case -7:
+ return 0;
+ case -9:
+ return getItem1Ptr()->parent;
+ default:
+ return a;
+ }
+}
+
+Item *SimonEngine::getItem1Ptr() {
+ if (_item1)
+ return _item1;
+ return _dummyItem1;
+}
+
+Item *SimonEngine::getItemPtrB() {
+ error("getItemPtrB: is this code ever used?");
+ return _dummyItem1;
+}
+
+uint SimonEngine::getNextVarContents() {
+ return (uint16)readVariable(getVarOrByte());
+}
+
+uint SimonEngine::readVariable(uint variable) {
+ if (variable >= 255)
+ error("Variable %d out of range in read", variable);
+ return _variableArray[variable];
+}
+
+void SimonEngine::writeNextVarContents(uint16 contents) {
+ writeVariable(getVarOrByte(), contents);
+}
+
+void SimonEngine::writeVariable(uint variable, uint16 contents) {
+ if (variable >= 256)
+ error("Variable %d out of range in write", variable);
+ _variableArray[variable] = contents;
+}
+
+void SimonEngine::setItemParent(Item *item, Item *parent) {
+ Item *old_parent = derefItem(item->parent);
+
+ if (item == parent)
+ error("Trying to set item as its own parent");
+
+ // unlink it if it has a parent
+ if (old_parent)
+ unlinkItem(item);
+ itemChildrenChanged(old_parent);
+ linkItem(item, parent);
+ itemChildrenChanged(parent);
+}
+
+void SimonEngine::itemChildrenChanged(Item *item) {
+ int i;
+ FillOrCopyStruct *fcs;
+
+ if (_noParentNotify)
+ return;
+
+ mouseOff();
+
+ for (i = 0; i != 8; i++) {
+ fcs = _windowArray[i];
+ if (fcs && fcs->fcs_data && fcs->fcs_data->item_ptr == item) {
+ if (_fcsData1[i]) {
+ _fcsData2[i] = true;
+ } else {
+ _fcsData2[i] = false;
+ drawIconArray(i, item, fcs->fcs_data->unk1, fcs->fcs_data->unk2);
+ }
+ }
+ }
+
+ mouseOn();
+}
+
+void SimonEngine::unlinkItem(Item *item) {
+ Item *first, *parent, *next;
+
+ // can't unlink item without parent
+ if (item->parent == 0)
+ return;
+
+ // get parent and first child of parent
+ parent = derefItem(item->parent);
+ first = derefItem(parent->child);
+
+ // the node to remove is first in the parent's children?
+ if (first == item) {
+ parent->child = item->sibling;
+ item->parent = 0;
+ item->sibling = 0;
+ return;
+ }
+
+ for (;;) {
+ if (!first)
+ error("unlinkItem: parent empty");
+ if (first->sibling == 0)
+ error("unlinkItem: parent does not contain child");
+
+ next = derefItem(first->sibling);
+ if (next == item) {
+ first->sibling = next->sibling;
+ item->parent = 0;
+ item->sibling = 0;
+ return;
+ }
+ first = next;
+ }
+}
+
+void SimonEngine::linkItem(Item *item, Item *parent) {
+ uint id;
+ // Don't allow that an item that is already linked is relinked
+ if (item->parent)
+ return;
+
+ id = itemPtrToID(parent);
+ item->parent = id;
+
+ if (parent != 0) {
+ item->sibling = parent->child;
+ parent->child = itemPtrToID(item);
+ } else {
+ item->sibling = 0;
+ }
+}
+
+const byte *SimonEngine::getStringPtrByID(uint string_id) {
+ const byte *string_ptr;
+ byte *dst;
+
+ _freeStringSlot ^= 1;
+
+ if (string_id < 0x8000) {
+ string_ptr = _stringTabPtr[string_id];
+ } else {
+ string_ptr = getLocalStringByID(string_id);
+ }
+
+ dst = _stringReturnBuffer[_freeStringSlot];
+ strcpy((char *)dst, (const char *)string_ptr);
+ return dst;
+}
+
+const byte *SimonEngine::getLocalStringByID(uint string_id) {
+ if (string_id < _stringIdLocalMin || string_id >= _stringIdLocalMax) {
+ loadTextIntoMem(string_id);
+ }
+ return _localStringtable[string_id - _stringIdLocalMin];
+}
+
+void SimonEngine::loadTextIntoMem(uint string_id) {
+ byte *p;
+ char filename[30];
+ int i;
+ uint base_min = 0x8000, base_max, size;
+
+ _tablesHeapPtr = _tablesheapPtrNew;
+ _tablesHeapCurPos = _tablesHeapCurPosNew;
+
+ p = _strippedTxtMem;
+
+ // get filename
+ while (*p) {
+ for (i = 0; *p; p++, i++)
+ filename[i] = *p;
+ filename[i] = 0;
+ p++;
+
+ base_max = (p[0] * 256) | p[1];
+ p += 2;
+
+ if (string_id < base_max) {
+ _stringIdLocalMin = base_min;
+ _stringIdLocalMax = base_max;
+
+ _localStringtable = (byte **)_tablesHeapPtr;
+
+ size = (base_max - base_min + 1) * sizeof(byte *);
+ _tablesHeapPtr += size;
+ _tablesHeapCurPos += size;
+
+ size = loadTextFile(filename, _tablesHeapPtr);
+
+ setupLocalStringTable(_tablesHeapPtr, base_max - base_min + 1);
+
+ _tablesHeapPtr += size;
+ _tablesHeapCurPos += size;
+
+ if (_tablesHeapCurPos > _tablesHeapSize) {
+ error("loadTextIntoMem: Out of table memory");
+ }
+ return;
+ }
+
+ base_min = base_max;
+ }
+
+ error("loadTextIntoMem: didn't find %d", string_id);
+}
+
+void SimonEngine::loadTablesIntoMem(uint subr_id) {
+ byte *p;
+ int i;
+ uint min_num, max_num;
+ char filename[30];
+ File *in;
+
+ p = _tblList;
+ if (p == NULL)
+ return;
+
+ while (*p) {
+ for (i = 0; *p; p++, i++)
+ filename[i] = *p;
+ filename[i] = 0;
+ p++;
+
+ for (;;) {
+ min_num = (p[0] * 256) | p[1];
+ p += 2;
+
+ if (min_num == 0)
+ break;
+
+ max_num = (p[0] * 256) | p[1];
+ p += 2;
+
+ if (subr_id >= min_num && subr_id <= max_num) {
+ _subroutineList = _subroutineListOrg;
+ _tablesHeapPtr = _tablesHeapPtrOrg;
+ _tablesHeapCurPos = _tablesHeapCurPosOrg;
+ _stringIdLocalMin = 1;
+ _stringIdLocalMax = 0;
+
+ in = openTablesFile(filename);
+ readSubroutineBlock(in);
+ closeTablesFile(in);
+ if (getGameType() == GType_FF) {
+ // TODO
+ } else if (getGameType() == GType_SIMON2) {
+ _sound->loadSfxTable(_gameFile, _gameOffsetsPtr[atoi(filename + 6) - 1 + SOUND_INDEX_BASE]);
+ } else if (getPlatform() == Common::kPlatformWindows) {
+ memcpy(filename, "SFXXXX", 6);
+ _sound->readSfxFile(filename);
+ }
+
+ alignTableMem();
+
+ _tablesheapPtrNew = _tablesHeapPtr;
+ _tablesHeapCurPosNew = _tablesHeapCurPos;
+
+ if (_tablesHeapCurPos > _tablesHeapSize)
+ error("loadTablesIntoMem: Out of table memory");
+ return;
+ }
+ }
+ }
+
+ debug(1,"loadTablesIntoMem: didn't find %d", subr_id);
+}
+
+void SimonEngine::playSting(uint a) {
+ if (!midi._enable_sfx)
+ return;
+
+ char filename[15];
+
+ File mus_file;
+ uint16 mus_offset;
+
+ sprintf(filename, "STINGS%i.MUS", _soundFileId);
+ mus_file.open(filename);
+ if (!mus_file.isOpen()) {
+ warning("Can't load sound effect from '%s'", filename);
+ return;
+ }
+
+ mus_file.seek(a * 2, SEEK_SET);
+ mus_offset = mus_file.readUint16LE();
+ if (mus_file.ioFailed())
+ error("Can't read sting %d offset", a);
+
+ mus_file.seek(mus_offset, SEEK_SET);
+ midi.loadSMF(&mus_file, a, true);
+ midi.startTrack(0);
+}
+
+Subroutine *SimonEngine::getSubroutineByID(uint subroutine_id) {
+ Subroutine *cur;
+
+ _subroutine = subroutine_id;
+
+ for (cur = _subroutineList; cur; cur = cur->next) {
+ if (cur->id == subroutine_id)
+ return cur;
+ }
+
+ loadTablesIntoMem(subroutine_id);
+
+ for (cur = _subroutineList; cur; cur = cur->next) {
+ if (cur->id == subroutine_id)
+ return cur;
+ }
+
+ debug(1,"getSubroutineByID: subroutine %d not found", subroutine_id);
+ return NULL;
+}
+
+uint SimonEngine::loadTextFile_gme(const char *filename, byte *dst) {
+ uint res;
+ uint32 offs;
+ uint32 size;
+
+ res = atoi(filename + 4) + TEXT_INDEX_BASE - 1;
+ offs = _gameOffsetsPtr[res];
+ size = _gameOffsetsPtr[res + 1] - offs;
+
+ resfile_read(dst, offs, size);
+
+ return size;
+}
+
+File *SimonEngine::openTablesFile_gme(const char *filename) {
+ uint res;
+ uint32 offs;
+
+ res = atoi(filename + 6) + TABLE_INDEX_BASE - 1;
+ offs = _gameOffsetsPtr[res];
+
+ _gameFile->seek(offs, SEEK_SET);
+ return _gameFile;
+}
+
+uint SimonEngine::loadTextFile_simon1(const char *filename, byte *dst) {
+ File fo;
+ fo.open(filename);
+ uint32 size;
+
+ if (fo.isOpen() == false)
+ error("loadTextFile: Can't open '%s'", filename);
+
+ size = fo.size();
+
+ if (fo.read(dst, size) != size)
+ error("loadTextFile: fread failed");
+ fo.close();
+
+ return size;
+}
+
+File *SimonEngine::openTablesFile_simon1(const char *filename) {
+ File *fo = new File();
+ fo->open(filename);
+ if (fo->isOpen() == false)
+ error("openTablesFile: Can't open '%s'", filename);
+ return fo;
+}
+
+uint SimonEngine::loadTextFile(const char *filename, byte *dst) {
+ if (getFeatures() & GF_OLD_BUNDLE)
+ return loadTextFile_simon1(filename, dst);
+ else
+ return loadTextFile_gme(filename, dst);
+}
+
+File *SimonEngine::openTablesFile(const char *filename) {
+ if (getFeatures() & GF_OLD_BUNDLE)
+ return openTablesFile_simon1(filename);
+ else
+ return openTablesFile_gme(filename);
+}
+
+void SimonEngine::closeTablesFile(File *in) {
+ if (getFeatures() & GF_OLD_BUNDLE) {
+ in->close();
+ delete in;
+ }
+}
+
+void SimonEngine::addTimeEvent(uint timeout, uint subroutine_id) {
+ TimeEvent *te = (TimeEvent *)malloc(sizeof(TimeEvent)), *first, *last = NULL;
+ time_t cur_time;
+
+ time(&cur_time);
+
+ te->time = cur_time + timeout - _base_time;
+ te->subroutine_id = subroutine_id;
+
+ first = _firstTimeStruct;
+ while (first) {
+ if (te->time <= first->time) {
+ if (last) {
+ last->next = te;
+ te->next = first;
+ return;
+ }
+ te->next = _firstTimeStruct;
+ _firstTimeStruct = te;
+ return;
+ }
+
+ last = first;
+ first = first->next;
+ }
+
+ if (last) {
+ last->next = te;
+ te->next = NULL;
+ } else {
+ _firstTimeStruct = te;
+ te->next = NULL;
+ }
+}
+
+void SimonEngine::delTimeEvent(TimeEvent *te) {
+ TimeEvent *cur;
+
+ if (te == _pendingDeleteTimeEvent)
+ _pendingDeleteTimeEvent = NULL;
+
+ if (te == _firstTimeStruct) {
+ _firstTimeStruct = te->next;
+ free(te);
+ return;
+ }
+
+ cur = _firstTimeStruct;
+ if (cur == NULL)
+ error("delTimeEvent: none available");
+
+ for (;;) {
+ if (cur->next == NULL)
+ error("delTimeEvent: no such te");
+ if (te == cur->next) {
+ cur->next = te->next;
+ free(te);
+ return;
+ }
+ cur = cur->next;
+ }
+}
+
+void SimonEngine::killAllTimers() {
+ TimeEvent *cur, *next;
+
+ for (cur = _firstTimeStruct; cur; cur = next) {
+ next = cur->next;
+ delTimeEvent(cur);
+ }
+}
+
+bool SimonEngine::kickoffTimeEvents() {
+ time_t cur_time;
+ TimeEvent *te;
+ bool result = false;
+
+ time(&cur_time);
+ cur_time -= _base_time;
+
+ while ((te = _firstTimeStruct) != NULL && te->time <= (uint32)cur_time) {
+ result = true;
+ _pendingDeleteTimeEvent = te;
+ invokeTimeEvent(te);
+ if (_pendingDeleteTimeEvent) {
+ _pendingDeleteTimeEvent = NULL;
+ delTimeEvent(te);
+ }
+ }
+
+ return result;
+}
+
+void SimonEngine::invokeTimeEvent(TimeEvent *te) {
+ Subroutine *sub;
+
+ _scriptVerb = 0;
+ if (_runScriptReturn1)
+ return;
+ sub = getSubroutineByID(te->subroutine_id);
+ if (sub != NULL)
+ startSubroutineEx(sub);
+ _runScriptReturn1 = false;
+}
+
+void SimonEngine::o_setup_cond_c() {
+
+ setup_cond_c_helper();
+
+ _objectItem = _hitAreaObjectItem;
+
+ if (_objectItem == _dummyItem2)
+ _objectItem = getItem1Ptr();
+
+ if (_objectItem == _dummyItem3)
+ _objectItem = derefItem(getItem1Ptr()->parent);
+
+ if (_objectItem != NULL) {
+ _scriptNoun2 = _objectItem->noun;
+ _scriptAdj2 = _objectItem->adjective;
+ } else {
+ _scriptNoun2 = -1;
+ _scriptAdj2 = -1;
+ }
+}
+
+void SimonEngine::setup_cond_c_helper() {
+ HitArea *last;
+
+ if (getGameType() == GType_SIMON2) {
+ _mouseCursor = 0;
+ if (_hitAreaUnk4 != 999) {
+ _mouseCursor = 9;
+ _needHitAreaRecalc++;
+ _hitAreaUnk4 = 0;
+ }
+ }
+
+ _lastHitArea = 0;
+ _hitAreaObjectItem = NULL;
+
+ last = _lastHitArea2Ptr;
+ defocusHitarea();
+ _lastHitArea2Ptr = last;
+
+ for (;;) {
+ _lastHitArea = NULL;
+ _lastHitArea3 = 0;
+ _leftButtonDown = 0;
+
+ do {
+ if (_exitCutscene && (_bitArray[0] & 0x200)) {
+ endCutscene();
+ goto out_of_here;
+ }
+
+ if (getGameType() == GType_FF) {
+ if (_variableArray[254] == 63) {
+ hitarea_stuff_helper();
+ } else if (_variableArray[254] == 75) {
+ hitarea_stuff_helper();
+ _variableArray[60] = 9999;
+ goto out_of_here;
+ }
+ }
+
+ delay(100);
+ } while (_lastHitArea3 == (HitArea *) 0xFFFFFFFF || _lastHitArea3 == 0);
+
+ if (_lastHitArea == NULL) {
+ } else if (_lastHitArea->id == 0x7FFB) {
+ handle_uparrow_hitarea(_lastHitArea->fcs);
+ } else if (_lastHitArea->id == 0x7FFC) {
+ handle_downarrow_hitarea(_lastHitArea->fcs);
+ } else if (_lastHitArea->item_ptr != NULL) {
+ _hitAreaObjectItem = _lastHitArea->item_ptr;
+ _variableArray[60] = (_lastHitArea->flags & 1) ? (_lastHitArea->flags / 256) : 0xFFFF;
+ break;
+ }
+ }
+
+out_of_here:
+ _lastHitArea3 = 0;
+ _lastHitArea = 0;
+ _lastHitArea2Ptr = NULL;
+}
+
+void SimonEngine::endCutscene() {
+ Subroutine *sub;
+
+ _sound->stopVoice();
+
+ sub = getSubroutineByID(170);
+ if (sub != NULL)
+ startSubroutineEx(sub);
+
+ _runScriptReturn1 = true;
+}
+
+uint SimonEngine::get_fcs_ptr_3_index(FillOrCopyStruct *fcs) {
+ uint i;
+
+ for (i = 0; i != ARRAYSIZE(_windowArray); i++)
+ if (_windowArray[i] == fcs)
+ return i;
+
+ error("get_fcs_ptr_3_index: not found");
+ return 0;
+}
+
+void SimonEngine::mouseOff() {
+ _mouseHideCount++;
+}
+
+void SimonEngine::mouseOn() {
+ _lockWord |= 1;
+
+ if (_mouseHideCount != 0)
+ _mouseHideCount--;
+
+ _lockWord &= ~1;
+}
+
+void SimonEngine::handle_mouse_moved() {
+ uint x;
+
+ if (_mouseHideCount) {
+ _system->showMouse(false);
+ return;
+ }
+
+ _system->showMouse(true);
+ pollMouseXY();
+
+ if (_mouseX >= 32768)
+ _mouseX = 0;
+ if (_mouseX >= _screenWidth - 1)
+ _mouseX = _screenWidth - 1;
+
+ if (_mouseY >= 32768)
+ _mouseY = 0;
+ if (_mouseY >= _screenHeight - 1)
+ _mouseY = _screenHeight - 1;
+
+ if (_hitAreaUnk4) {
+ uint id = 101;
+ if (_mouseY >= 136)
+ id = 102;
+ if (_hitAreaUnk4 != id)
+ hitarea_proc_1();
+ }
+
+ if (getGameType() == GType_FF) {
+ if (_bitArray[6] & 0x8) { // Oracle
+ if (_mouseX >= 10 && _mouseX <= 635 && _mouseY >= 5 && _mouseX <= 475) {
+ _bitArray[6] |= 0x4;
+ } else {
+ if (_bitArray[6] & 0x4) {
+ _variableArray[254] = 63;
+ }
+ }
+ } else if (_bitArray[5] & 0x0100) { // Close Up
+ if (_mouseX >= 10 && _mouseX <= 635 && _mouseY >= 5 && _mouseX <= 475) {
+ _bitArray[5] |= 0x80;
+ } else {
+ if (_bitArray[5] & 0x80) {
+ _variableArray[254] = 75;
+ }
+ }
+ }
+ }
+
+ if (getGameType() == GType_SIMON2) {
+ if (_bitArray[4] & 0x8000) {
+ if (!_vgaVar9) {
+ if (_mouseX >= 630 / 2 || _mouseX < 9)
+ goto get_out2;
+ _vgaVar9 = 1;
+ }
+ if (_scrollCount == 0) {
+ if (_mouseX >= 631 / 2) {
+ if (_scrollX != _scrollXMax)
+ _scrollFlag = 1;
+ } else if (_mouseX < 8) {
+ if (_scrollX != 0)
+ _scrollFlag = -1;
+ }
+ }
+ } else {
+ get_out2:;
+ _vgaVar9 = 0;
+ }
+ }
+
+ if (_mouseX != _mouseXOld || _mouseY != _mouseYOld)
+ _needHitAreaRecalc++;
+
+ x = 0;
+ if (_lastHitArea3 == 0 && _leftButtonDown != 0) {
+ _leftButtonDown = 0;
+ x = 1;
+ } else {
+ if (_hitarea_unk_3 == 0 && _needHitAreaRecalc == 0)
+ goto get_out;
+ }
+
+ setup_hitarea_from_pos(_mouseX, _mouseY, x);
+ _lastHitArea3 = _lastHitArea;
+ if (x == 1 && _lastHitArea == NULL)
+ _lastHitArea3 = (HitArea *) - 1;
+
+get_out:
+ drawMousePointer();
+ _needHitAreaRecalc = 0;
+}
+
+void SimonEngine::drawIconArray(uint fcs_index, Item *item_ptr, int unk1, int unk2) {
+ Item *item_ptr_org = item_ptr;
+ FillOrCopyStruct *fcs_ptr;
+ uint width_div_3, height_div_3;
+ uint j, k, i, num_sibs_with_flag;
+ bool item_again;
+ uint x_pos, y_pos;
+
+ fcs_ptr = _windowArray[fcs_index & 7];
+
+ if (getGameType() == GType_SIMON1) {
+ width_div_3 = fcs_ptr->width / 3;
+ height_div_3 = fcs_ptr->height / 3;
+ } else {
+ width_div_3 = 100;
+ height_div_3 = 40;
+ }
+
+ i = 0;
+
+ if (fcs_ptr == NULL)
+ return;
+
+ if (fcs_ptr->fcs_data)
+ removeIconArray(fcs_index);
+
+ fcs_ptr->fcs_data = (FillOrCopyData *) malloc(sizeof(FillOrCopyData));
+ fcs_ptr->fcs_data->item_ptr = item_ptr;
+ fcs_ptr->fcs_data->upArrow = -1;
+ fcs_ptr->fcs_data->downArrow = -1;
+ fcs_ptr->fcs_data->unk1 = unk1;
+ fcs_ptr->fcs_data->unk2 = unk2;
+
+ item_ptr = derefItem(item_ptr->child);
+
+ while (item_ptr && unk1-- != 0) {
+ num_sibs_with_flag = 0;
+ while (item_ptr && width_div_3 > num_sibs_with_flag) {
+ if ((unk2 == 0 || item_ptr->classFlags & unk2) && has_item_childflag_0x10(item_ptr))
+ if (getGameType() == GType_SIMON1) {
+ num_sibs_with_flag++;
+ } else {
+ num_sibs_with_flag += 20;
+ }
+ item_ptr = derefItem(item_ptr->sibling);
+ }
+ }
+
+ if (item_ptr == NULL) {
+ fcs_ptr->fcs_data->unk1 = 0;
+ item_ptr = derefItem(item_ptr_org->child);
+ }
+
+ x_pos = 0;
+ y_pos = 0;
+ item_again = false;
+ k = 0;
+ j = 0;
+
+ while (item_ptr) {
+ if ((unk2 == 0 || item_ptr->classFlags & unk2) && has_item_childflag_0x10(item_ptr)) {
+ if (item_again == false) {
+ fcs_ptr->fcs_data->e[k].item = item_ptr;
+ if (getGameType() == GType_SIMON1) {
+ draw_icon_c(fcs_ptr, item_get_icon_number(item_ptr), x_pos * 3, y_pos);
+ fcs_ptr->fcs_data->e[k].hit_area =
+ setup_icon_hit_area(fcs_ptr, x_pos * 3, y_pos,
+ item_get_icon_number(item_ptr), item_ptr);
+ } else {
+ draw_icon_c(fcs_ptr, item_get_icon_number(item_ptr), x_pos, y_pos);
+ fcs_ptr->fcs_data->e[k].hit_area =
+ setup_icon_hit_area(fcs_ptr, x_pos, y_pos, item_get_icon_number(item_ptr), item_ptr);
+ }
+ k++;
+ } else {
+ fcs_ptr->fcs_data->e[k].item = NULL;
+ j = 1;
+ }
+ x_pos += (getGameType() == GType_SIMON1) ? 1 : 20;
+
+ if (x_pos >= width_div_3) {
+ x_pos = 0;
+
+ y_pos += (getGameType() == GType_SIMON1) ? 1 : 20;
+ if (y_pos >= height_div_3)
+ item_again = true;
+ }
+ }
+ item_ptr = derefItem(item_ptr->sibling);
+ }
+
+ fcs_ptr->fcs_data->e[k].item = NULL;
+
+ if (j != 0 || fcs_ptr->fcs_data->unk1 != 0) {
+ addArrows(fcs_ptr, fcs_index);
+ }
+}
+
+void SimonEngine::addArrows(FillOrCopyStruct *fcs, uint fcs_index) {
+ setArrowHitAreas(fcs, fcs_index);
+
+ fcs->fcs_data->upArrow = _scrollUpHitArea;
+ fcs->fcs_data->downArrow = _scrollDownHitArea;
+}
+
+void SimonEngine::setArrowHitAreas(FillOrCopyStruct *fcs, uint fcs_index) {
+ HitArea *ha;
+
+ ha = findEmptyHitArea();
+ _scrollUpHitArea = ha - _hitAreas;
+ if (getGameType() == GType_SIMON1) {
+ ha->x = 308;
+ ha->y = 149;
+ ha->width = 12;
+ ha->height = 17;
+ ha->flags = 0x24;
+ ha->id = 0x7FFB;
+ ha->layer = 100;
+ ha->fcs = fcs;
+ ha->unk3 = 1;
+ } else {
+ ha->x = 81;
+ ha->y = 158;
+ ha->width = 12;
+ ha->height = 26;
+ ha->flags = 36;
+ ha->id = 0x7FFB;
+ ha->layer = 100;
+ ha->fcs = fcs;
+ ha->unk3 = 1;
+ }
+
+ ha = findEmptyHitArea();
+ _scrollDownHitArea = ha - _hitAreas;
+
+ if (getGameType() == GType_SIMON1) {
+ ha->x = 308;
+ ha->y = 176;
+ ha->width = 12;
+ ha->height = 17;
+ ha->flags = 0x24;
+ ha->id = 0x7FFC;
+ ha->layer = 100;
+ ha->fcs = fcs;
+ ha->unk3 = 1;
+
+ // Simon1 specific
+ o_kill_sprite_simon1(128);
+ loadSprite(0, 1, 128, 0, 0, 14);
+ } else {
+ ha->x = 227;
+ ha->y = 162;
+ ha->width = 12;
+ ha->height = 26;
+ ha->flags = 36;
+ ha->id = 0x7FFC;
+ ha->layer = 100;
+ ha->fcs = fcs;
+ ha->unk3 = 1;
+ }
+}
+
+
+bool SimonEngine::has_item_childflag_0x10(Item *item) {
+ Child2 *child = (Child2 *)findChildOfType(item, 2);
+ return child && (child->avail_props & 0x10) != 0;
+}
+
+uint SimonEngine::item_get_icon_number(Item *item) {
+ Child2 *child = (Child2 *)findChildOfType(item, 2);
+ uint offs;
+
+ if (child == NULL || !(child->avail_props & 0x10))
+ return 0;
+
+ offs = getOffsetOfChild2Param(child, 0x10);
+ return child->array[offs];
+}
+
+void SimonEngine::f10_key() {
+ HitArea *ha, *dha;
+ uint count;
+ uint y_, x_;
+ byte *dst;
+ uint b, color;
+
+ _lockWord |= 0x8000;
+
+ if (getGameType() == GType_SIMON2)
+ color = 236;
+ else
+ color = 225;
+
+ uint limit = (getGameType() == GType_SIMON2) ? 200 : 134;
+
+ for (int i = 0; i < 5; i++) {
+ ha = _hitAreas;
+ count = ARRAYSIZE(_hitAreas);
+
+ timer_vga_sprites();
+
+ do {
+ if (ha->id != 0 && ha->flags & 0x20 && !(ha->flags & 0x40)) {
+
+ dha = _hitAreas;
+ if (ha->flags & 1) {
+ while (dha != ha && dha->flags != ha->flags)
+ ++dha;
+ if (dha != ha && dha->flags == ha->flags)
+ continue;
+ } else {
+ dha = _hitAreas;
+ while (dha != ha && dha->item_ptr != ha->item_ptr)
+ ++dha;
+ if (dha != ha && dha->item_ptr == ha->item_ptr)
+ continue;
+ }
+
+ if (ha->y >= limit || ((getGameType() == GType_SIMON2) && ha->y >= _vgaVar8))
+ continue;
+
+ y_ = (ha->height / 2) - 4 + ha->y;
+
+ x_ = (ha->width / 2) - 4 + ha->x - (_scrollX * 8);
+
+ if (x_ >= 311)
+ continue;
+
+ dst = dx_lock_attached();
+
+ dst += (((_dxSurfacePitch / 4) * y_) * 4) + x_;
+
+ b = _dxSurfacePitch;
+ dst[4] = color;
+ dst[b+1] = color;
+ dst[b+4] = color;
+ dst[b+7] = color;
+ b += _dxSurfacePitch;
+ dst[b+2] = color;
+ dst[b+4] = color;
+ dst[b+6] = color;
+ b += _dxSurfacePitch;
+ dst[b+3] = color;
+ dst[b+5] = color;
+ b += _dxSurfacePitch;
+ dst[b] = color;
+ dst[b+1] = color;
+ dst[b+2] = color;
+ dst[b+6] = color;
+ dst[b+7] = color;
+ dst[b+8] = color;
+ b += _dxSurfacePitch;
+ dst[b+3] = color;
+ dst[b+5] = color;
+ b += _dxSurfacePitch;
+ dst[b+2] = color;
+ dst[b+4] = color;
+ dst[b+6] = color;
+ b += _dxSurfacePitch;
+ dst[b+1] = color;
+ dst[b+4] = color;
+ dst[b+7] = color;
+ b += _dxSurfacePitch;
+ dst[b+4] = color;
+
+ dx_unlock_attached();
+ }
+ } while (ha++, --count);
+
+ dx_update_screen_and_palette();
+ delay(100);
+ timer_vga_sprites();
+ dx_update_screen_and_palette();
+ delay(100);
+ }
+
+ _lockWord &= ~0x8000;
+}
+
+void SimonEngine::hitarea_stuff() {
+ HitArea *ha;
+ uint id;
+
+ _leftButtonDown = 0;
+ _lastHitArea = 0;
+ _verbHitArea = 0;
+ _hitAreaSubjectItem = NULL;
+ _hitAreaObjectItem = NULL;
+
+ hitarea_proc_1();
+
+startOver:
+ for (;;) {
+ _lastHitArea = NULL;
+ _lastHitArea3 = NULL;
+
+ for (;;) {
+ if (_keyPressed == 35)
+ f10_key();
+ processSpecialKeys();
+ if (_lastHitArea3 == (HitArea *) 0xFFFFFFFF)
+ goto startOver;
+ if (_lastHitArea3 != 0)
+ break;
+ hitarea_stuff_helper();
+ delay(100);
+ }
+
+ ha = _lastHitArea;
+
+ if (ha == NULL) {
+ } else if (ha->id == 0x7FFB) {
+ handle_uparrow_hitarea(ha->fcs);
+ } else if (ha->id == 0x7FFC) {
+ handle_downarrow_hitarea(ha->fcs);
+ } else if (ha->id >= 101 && ha->id < 113) {
+ _verbHitArea = ha->unk3;
+ handle_verb_hitarea(ha);
+ _hitAreaUnk4 = 0;
+ } else {
+ if ((_verbHitArea != 0 || _hitAreaSubjectItem != ha->item_ptr && ha->flags & 0x80) &&
+ ha->item_ptr) {
+ if_1:;
+ _hitAreaSubjectItem = ha->item_ptr;
+ id = 0xFFFF;
+ if (ha->flags & 1)
+ id = ha->flags / 256;
+ _variableArray[60] = id;
+ new_current_hitarea(ha);
+ if (_verbHitArea != 0)
+ break;
+ } else {
+ // else 1
+ if (ha->unk3 == 0) {
+ if (ha->item_ptr)
+ goto if_1;
+ } else {
+ _verbHitArea = ha->unk3 & 0xBFFF;
+ if (ha->unk3 & 0x4000) {
+ _hitAreaSubjectItem = ha->item_ptr;
+ break;
+ }
+ if (_hitAreaSubjectItem != NULL)
+ break;
+ }
+ }
+ }
+ }
+
+ _needHitAreaRecalc++;
+}
+
+void SimonEngine::hitarea_stuff_helper() {
+ time_t cur_time;
+
+ if (getGameType() == GType_SIMON1) {
+ uint subr_id = _variableArray[254];
+ if (subr_id != 0) {
+ Subroutine *sub = getSubroutineByID(subr_id);
+ if (sub != NULL) {
+ startSubroutineEx(sub);
+ startUp_helper_2();
+ }
+ _variableArray[254] = 0;
+ _runScriptReturn1 = false;
+ }
+ } else {
+ if (_variableArray[254] || _variableArray[249]) {
+ hitarea_stuff_helper_2();
+ }
+ }
+
+ time(&cur_time);
+ if ((uint) cur_time != _lastTime) {
+ _lastTime = cur_time;
+ if (kickoffTimeEvents())
+ startUp_helper_2();
+ }
+}
+
+// Simon 2 specific
+void SimonEngine::hitarea_stuff_helper_2() {
+ uint subr_id;
+ Subroutine *sub;
+
+ subr_id = _variableArray[249];
+ if (subr_id != 0) {
+ sub = getSubroutineByID(subr_id);
+ if (sub != NULL) {
+ _variableArray[249] = 0;
+ startSubroutineEx(sub);
+ startUp_helper_2();
+ }
+ _variableArray[249] = 0;
+ }
+
+ subr_id = _variableArray[254];
+ if (subr_id != 0) {
+ sub = getSubroutineByID(subr_id);
+ if (sub != NULL) {
+ _variableArray[254] = 0;
+ startSubroutineEx(sub);
+ startUp_helper_2();
+ }
+ _variableArray[254] = 0;
+ }
+
+ _runScriptReturn1 = false;
+}
+
+void SimonEngine::startUp_helper_2() {
+ if (!_mortalFlag) {
+ _mortalFlag = true;
+ showmessage_print_char(0);
+ _curWindow = 0;
+ if (_windowArray[0] != 0) {
+ _textWindow = _windowArray[0];
+ showmessage_helper_3(_textWindow->textLength, _textWindow->textMaxLength);
+ }
+ _mortalFlag = false;
+ }
+}
+
+void SimonEngine::pollMouseXY() {
+ _mouseX = _sdlMouseX;
+ _mouseY = _sdlMouseY;
+}
+
+void SimonEngine::handle_verb_clicked(uint verb) {
+ Subroutine *sub;
+ int result;
+
+ _objectItem = _hitAreaObjectItem;
+ if (_objectItem == _dummyItem2) {
+ _objectItem = getItem1Ptr();
+ }
+ if (_objectItem == _dummyItem3) {
+ _objectItem = derefItem(getItem1Ptr()->parent);
+ }
+
+ _subjectItem = _hitAreaSubjectItem;
+ if (_subjectItem == _dummyItem2) {
+ _subjectItem = getItem1Ptr();
+ }
+ if (_subjectItem == _dummyItem3) {
+ _subjectItem = derefItem(getItem1Ptr()->parent);
+ }
+
+ if (_subjectItem) {
+ _scriptNoun1 = _subjectItem->noun;
+ _scriptAdj1 = _subjectItem->adjective;
+ } else {
+ _scriptNoun1 = -1;
+ _scriptAdj1 = -1;
+ }
+
+ if (_objectItem) {
+ _scriptNoun2 = _objectItem->noun;
+ _scriptAdj2 = _objectItem->adjective;
+ } else {
+ _scriptNoun2 = -1;
+ _scriptAdj2 = -1;
+ }
+
+ _scriptVerb = _verbHitArea;
+
+ sub = getSubroutineByID(0);
+ if (sub == NULL)
+ return;
+
+ result = startSubroutine(sub);
+ if (result == -1)
+ showMessageFormat("I don't understand");
+
+ _runScriptReturn1 = false;
+
+ sub = getSubroutineByID(100);
+ if (sub)
+ startSubroutine(sub);
+
+ if (getGameType() == GType_SIMON2 || getGameType() == GType_FF)
+ _runScriptReturn1 = false;
+
+ startUp_helper_2();
+}
+
+TextLocation *SimonEngine::getTextLocation(uint a) {
+ switch (a) {
+ case 1:
+ return &_textLocation1;
+ case 2:
+ return &_textLocation2;
+ case 101:
+ return &_textLocation3;
+ case 102:
+ return &_textLocation4;
+ default:
+ error("text, invalid value %d", a);
+ }
+ return NULL;
+}
+
+void SimonEngine::o_printStr() {
+ uint vgaSpriteId = getVarOrByte();
+ uint color = getVarOrByte();
+ uint string_id = getNextStringID();
+ const byte *string_ptr = NULL;
+ uint speech_id = 0;
+ TextLocation *tl;
+
+ if (string_id != 0xFFFF)
+ string_ptr = getStringPtrByID(string_id);
+
+ if (getFeatures() & GF_TALKIE)
+ speech_id = (uint16)getNextWord();
+
+ tl = getTextLocation(vgaSpriteId);
+
+ if (_speech && speech_id != 0)
+ playSpeech(speech_id, vgaSpriteId);
+ if ((getGameType() == GType_SIMON2) && (getFeatures() & GF_TALKIE) && speech_id == 0)
+ o_kill_sprite_simon2(2, vgaSpriteId + 2);
+
+ if (string_ptr != NULL && (speech_id == 0 || _subtitles))
+ printText(vgaSpriteId, color, (const char *)string_ptr, tl->x, tl->y, tl->width);
+
+}
+
+void SimonEngine::o_loadZone(uint vga_res) {
+ _lockWord |= 0x80;
+ loadZone(vga_res);
+ _lockWord &= ~0x80;
+}
+
+void SimonEngine::loadZone(uint vga_res) {
+ VgaPointersEntry *vpe;
+
+ CHECK_BOUNDS(vga_res, _vgaBufferPointers);
+
+ vpe = _vgaBufferPointers + vga_res;
+ if (vpe->vgaFile1 != NULL)
+ return;
+
+ vpe->vgaFile1 = read_vga_from_datfile_2(vga_res * 2, 1);
+ vpe->vgaFile2 = read_vga_from_datfile_2(vga_res * 2 + 1, 2);
+ vpe->sfxFile = read_vga_from_datfile_2(vga_res * 2, 3);
+
+}
+
+byte *SimonEngine::setup_vga_destination(uint32 size) {
+ byte *dest, *end;
+
+ _videoVar4 = 0;
+
+ for (;;) {
+ dest = _vgaBufFreeStart;
+
+ end = dest + size;
+
+ if (end >= _vgaBufEnd) {
+ _vgaBufFreeStart = _vgaBufStart;
+ } else {
+ _videoVar5 = false;
+ vga_buf_unk_proc3(end);
+ if (_videoVar5)
+ continue;
+ vga_buf_unk_proc1(end);
+ if (_videoVar5)
+ continue;
+ delete_memptr_range(end);
+ _vgaBufFreeStart = end;
+ return dest;
+ }
+ }
+}
+
+void SimonEngine::setup_vga_file_buf_pointers() {
+ byte *alloced;
+
+ alloced = (byte *)malloc(VGA_MEM_SIZE);
+
+ _vgaBufFreeStart = alloced;
+ _vgaBufStart = alloced;
+ _vgaFileBufOrg = alloced;
+ _vgaFileBufOrg2 = alloced;
+ _vgaBufEnd = alloced + VGA_MEM_SIZE;
+}
+
+void SimonEngine::vga_buf_unk_proc3(byte *end) {
+ VgaPointersEntry *vpe;
+
+ if (_videoVar7 == 0xFFFF)
+ return;
+
+ if (_videoVar4 == 2)
+ error("vga_buf_unk_proc3: _videoVar4 == 2");
+
+ vpe = &_vgaBufferPointers[_videoVar7];
+
+ if (_vgaBufFreeStart <= vpe->vgaFile1 && end >= vpe->vgaFile1 ||
+ _vgaBufFreeStart <= vpe->vgaFile2 && end >= vpe->vgaFile2) {
+ _videoVar5 = 1;
+ _videoVar4++;
+ _vgaBufFreeStart = vpe->vgaFile1 + 0x5000;
+ } else {
+ _videoVar5 = 0;
+ }
+}
+
+void SimonEngine::vga_buf_unk_proc1(byte *end) {
+ VgaSprite *vsp;
+ if (_lockWord & 0x20)
+ return;
+
+ for (vsp = _vgaSprites; vsp->id; vsp++) {
+ vga_buf_unk_proc2(vsp->fileId, end);
+ if (_videoVar5 == true)
+ return;
+ }
+}
+
+void SimonEngine::delete_memptr_range(byte *end) {
+ uint count = ARRAYSIZE(_vgaBufferPointers);
+ VgaPointersEntry *vpe = _vgaBufferPointers;
+ do {
+ if (_vgaBufFreeStart <= vpe->vgaFile1 && end >= vpe->vgaFile1 ||
+ _vgaBufFreeStart <= vpe->vgaFile2 && end >= vpe->vgaFile2) {
+ vpe->sfxFile = NULL;
+ vpe->vgaFile1 = NULL;
+ vpe->vgaFile2 = NULL;
+ }
+
+ } while (++vpe, --count);
+}
+
+void SimonEngine::vga_buf_unk_proc2(uint a, byte *end) {
+ VgaPointersEntry *vpe;
+
+ vpe = &_vgaBufferPointers[a];
+
+ if (_vgaBufFreeStart <= vpe->vgaFile1 && end >= vpe->vgaFile1 ||
+ _vgaBufFreeStart <= vpe->vgaFile2 && end >= vpe->vgaFile2) {
+ _videoVar5 = true;
+ _videoVar4++;
+ _vgaBufFreeStart = vpe->vgaFile1 + 0x5000;
+ } else {
+ _videoVar5 = false;
+ }
+}
+
+void SimonEngine::o_unloadZone(uint a) {
+ VgaPointersEntry *vpe;
+
+ vpe = &_vgaBufferPointers[a];
+
+ vpe->sfxFile = NULL;
+ vpe->vgaFile1 = NULL;
+ vpe->vgaFile2 = NULL;
+}
+
+void SimonEngine::o_set_video_mode(uint mode, uint vga_res) {
+ if (mode == 4)
+ vc29_stopAllSounds();
+
+ if (_lockWord & 0x10)
+ error("o_set_video_mode_ex: _lockWord & 0x10");
+
+ set_video_mode_internal(mode, vga_res);
+}
+
+void SimonEngine::set_video_mode_internal(uint mode, uint vga_res_id) {
+ uint num, num_lines;
+ VgaPointersEntry *vpe;
+ byte *bb, *b;
+ // uint16 count;
+ const byte *vc_ptr_org;
+
+ _windowNum = mode;
+ _lockWord |= 0x20;
+
+ if (vga_res_id == 0) {
+
+ if (getGameType() == GType_SIMON1) {
+ _unkPalFlag = true;
+ } else {
+ _dxUse3Or4ForLock = true;
+ _vgaVar6 = true;
+ }
+ }
+
+ _vgaCurFile2 = num = vga_res_id / 100;
+
+ for (;;) {
+ vpe = &_vgaBufferPointers[num];
+
+ _curVgaFile1 = vpe->vgaFile1;
+ _curVgaFile2 = vpe->vgaFile2;
+ _curSfxFile = vpe->sfxFile;
+
+ if (vpe->vgaFile1 != NULL)
+ break;
+
+ loadZone(num);
+ }
+
+ // ensure flipping complete
+
+ bb = _curVgaFile1;
+
+ if (getGameType() == GType_FF) {
+ b = bb + READ_LE_UINT16(&((VgaFileHeader_Feeble *) bb)->hdr2_start);
+ //count = READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageCount);
+ b = bb + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageTable);
+
+ while (READ_LE_UINT16(&((ImageHeader_Feeble *) b)->id) != vga_res_id)
+ b += sizeof(ImageHeader_Feeble);
+ } else {
+ b = bb + READ_BE_UINT16(&((VgaFileHeader_Simon *) bb)->hdr2_start);
+ //count = READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageCount);
+ b = bb + READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageTable);
+
+ while (READ_BE_UINT16(&((ImageHeader_Simon *) b)->id) != vga_res_id)
+ b += sizeof(ImageHeader_Simon);
+ }
+
+ if ((getGameType() == GType_SIMON1) && vga_res_id == 16300) {
+ dx_clear_attached_from_top(134);
+ _usePaletteDelay = true;
+ } else {
+ _scrollX = 0;
+ _scrollXMax = 0;
+ _scrollCount = 0;
+ _scrollFlag = 0;
+ _scrollHeight = 134;
+ if (_variableArray[34] != -1)
+ _variableArray[251] = 0;
+ }
+
+ vc_ptr_org = _vcPtr;
+
+ if (getGameType() == GType_FF) {
+ _vcPtr = _curVgaFile1 + READ_LE_UINT16(&((ImageHeader_Feeble *) b)->scriptOffs);
+ } else {
+ _vcPtr = _curVgaFile1 + READ_BE_UINT16(&((ImageHeader_Simon *) b)->scriptOffs);
+ }
+
+ //dump_vga_script(_vcPtr, num, vga_res_id);
+ run_vga_script();
+ _vcPtr = vc_ptr_org;
+
+
+ if (getGameType() == GType_SIMON1) {
+ // Allow one section of Simon the Sorcerer 1 introduction to be displayed
+ // in lower half of screen
+ if (_subroutine == 2923 || _subroutine == 2926)
+ num_lines = 200;
+ else
+ num_lines = _windowNum == 4 ? 134 : 200;
+
+ dx_copy_from_attached_to_2(0, 0, _screenWidth, num_lines);
+ dx_copy_from_attached_to_3(num_lines);
+
+ _syncFlag2 = 1;
+ _timer5 = 0;
+ } else {
+ if (!_dxUse3Or4ForLock) {
+ if (getGameType() == GType_FF)
+ num_lines = 480;
+ else
+ num_lines = _windowNum == 4 ? 134 : 200;
+
+ _vgaVar8 = num_lines;
+ dx_copy_from_attached_to_2(0, 0, _screenWidth, num_lines);
+ dx_copy_from_attached_to_3(num_lines);
+ _syncFlag2 = 1;
+ }
+ _dxUse3Or4ForLock = false;
+ }
+
+ _lockWord &= ~0x20;
+
+ if (getGameType() == GType_SIMON1) {
+ if (_unkPalFlag) {
+ _unkPalFlag = false;
+ while (_paletteColorCount != 0) {
+ delay(10);
+ }
+ }
+ }
+}
+
+void SimonEngine::o_fadeToBlack() {
+ uint i;
+
+ memcpy(_videoBuf1, _paletteBackup, 1024);
+
+ i = NUM_PALETTE_FADEOUT;
+ do {
+ palette_fadeout((uint32 *)_videoBuf1, 32);
+ palette_fadeout((uint32 *)_videoBuf1 + 32 + 16, 144);
+ palette_fadeout((uint32 *)_videoBuf1 + 32 + 16 + 144 + 16, 48);
+
+ _system->setPalette(_videoBuf1, 0, 256);
+ if (_fade)
+ _system->updateScreen();
+ delay(5);
+ } while (--i);
+
+ memcpy(_paletteBackup, _videoBuf1, 1024);
+ memcpy(_palette, _videoBuf1, 1024);
+}
+
+void SimonEngine::delete_vga_timer(VgaTimerEntry * vte) {
+ _lockWord |= 1;
+
+ if (vte + 1 <= _nextVgaTimerToProcess) {
+ _nextVgaTimerToProcess--;
+ }
+
+ do {
+ memcpy(vte, vte + 1, sizeof(VgaTimerEntry));
+ vte++;
+ } while (vte->delay);
+
+ _lockWord &= ~1;
+}
+
+void SimonEngine::expire_vga_timers() {
+ VgaTimerEntry *vte = _vgaTimerList;
+
+ _vgaTickCounter++;
+
+ while (vte->delay) {
+ if (!--vte->delay) {
+ uint16 cur_file = vte->cur_vga_file;
+ uint16 cur_unk = vte->sprite_id;
+ const byte *script_ptr = vte->script_pointer;
+
+ _nextVgaTimerToProcess = vte + 1;
+ delete_vga_timer(vte);
+
+ if ((getGameType() == GType_SIMON2) && script_ptr == NULL) {
+ // special scroll timer
+ scroll_timeout();
+ } else {
+ vcResumeSprite(script_ptr, cur_file, cur_unk);
+ }
+ vte = _nextVgaTimerToProcess;
+ } else {
+ vte++;
+ }
+ }
+}
+
+// Simon2 specific
+void SimonEngine::scroll_timeout() {
+ if (_scrollCount == 0)
+ return;
+
+ if (_scrollCount < 0) {
+ if (_scrollFlag != -1) {
+ _scrollFlag = -1;
+ if (++_scrollCount == 0)
+ return;
+ }
+ } else {
+ if (_scrollFlag != 1) {
+ _scrollFlag = 1;
+ if (--_scrollCount == 0)
+ return;
+ }
+ }
+
+ add_vga_timer(6, NULL, 0, 0);
+}
+
+void SimonEngine::vcResumeSprite(const byte *code_ptr, uint16 cur_file, uint16 cur_sprite) {
+ VgaPointersEntry *vpe;
+
+ _vgaCurSpriteId = cur_sprite;
+
+ _vgaCurFileId = cur_file;
+ _vgaCurFile2 = cur_file;
+ vpe = &_vgaBufferPointers[cur_file];
+
+ _curVgaFile1 = vpe->vgaFile1;
+ _curVgaFile2 = vpe->vgaFile2;
+ _curSfxFile = vpe->sfxFile;
+
+ _vcPtr = code_ptr;
+
+ run_vga_script();
+}
+
+void SimonEngine::add_vga_timer(uint num, const byte *code_ptr, uint cur_sprite, uint cur_file) {
+ VgaTimerEntry *vte;
+
+ // When Simon talks to the Golum about stew in French version of
+ // Simon the Sorcerer 1 the code_ptr is at wrong location for
+ // sprite 200. This was a bug in the original game, which
+ // caused several glitches in this scene.
+ // We work around the problem by correcting the code_ptr for sprite
+ // 200 in this scene, if it is wrong.
+ if (getGameType() == GType_SIMON1 && _language == Common::FR_FRA &&
+ (code_ptr - _vgaBufferPointers[cur_file].vgaFile1 == 4) && (cur_sprite == 200) && (cur_file == 2))
+ code_ptr += 0x66;
+
+ _lockWord |= 1;
+
+ for (vte = _vgaTimerList; vte->delay; vte++) {
+ }
+
+ vte->delay = num;
+ vte->script_pointer = code_ptr;
+ vte->sprite_id = cur_sprite;
+ vte->cur_vga_file = cur_file;
+
+ _lockWord &= ~1;
+}
+
+void SimonEngine::o_mouseOn() {
+ if (getGameType() == GType_SIMON2 && _bitArray[4] & 0x8000)
+ _mouseCursor = 0;
+ _mouseHideCount = 0;
+}
+
+void SimonEngine::o_mouseOff() {
+ _lockWord |= 0x8000;
+ vc34_setMouseOff();
+ _lockWord &= ~0x8000;
+}
+
+void SimonEngine::o_waitForSync(uint a) {
+ _vgaWaitFor = a;
+ _timer1 = 0;
+ _exitCutscene = false;
+ _skipSpeech = false;
+ while (_vgaWaitFor != 0) {
+ if (_skipSpeech && (getGameType() == GType_SIMON2 || getGameType() == GType_FF)) {
+ if (_vgaWaitFor == 200 && !vcGetBit(14)) {
+ skipSpeech();
+ break;
+ }
+ } else if (_exitCutscene) {
+ if (vcGetBit(9)) {
+ endCutscene();
+ break;
+ }
+ } else {
+ processSpecialKeys();
+ }
+
+ delay(10);
+
+ if (getGameType() == GType_SIMON2) {
+ if (_timer1 >= 1000) {
+ warning("wait timed out");
+ break;
+ }
+ } else if (_timer1 >= 500) {
+ warning("wait timed out");
+ break;
+ }
+
+ }
+}
+
+void SimonEngine::skipSpeech() {
+ _sound->stopVoice();
+ if (!(_bitArray[1] & 0x1000)) {
+ _bitArray[0] |= 0x4000;
+ _variableArray[100] = 5;
+ loadSprite(4, 1, 30, 0, 0, 0);
+ o_waitForSync(130);
+ o_kill_sprite_simon2(2, 1);
+ }
+}
+
+void SimonEngine::timer_vga_sprites() {
+ VgaSprite *vsp;
+ VgaPointersEntry *vpe;
+ const byte *vc_ptr_org = _vcPtr;
+ uint16 params[5]; // parameters to vc10
+
+ if (_paletteFlag == 2)
+ _paletteFlag = 1;
+
+ if (getGameType() == GType_SIMON2 && _scrollFlag) {
+ timer_vga_sprites_helper();
+ }
+
+ vsp = _vgaSprites;
+
+ while (vsp->id != 0) {
+ vsp->windowNum &= 0x7FFF;
+
+ vpe = &_vgaBufferPointers[vsp->fileId];
+ _curVgaFile1 = vpe->vgaFile1;
+ _curVgaFile2 = vpe->vgaFile2;
+ _curSfxFile = vpe->sfxFile;
+ _windowNum = vsp->windowNum;
+ _vgaCurSpriteId = vsp->id;
+
+ params[0] = readUint16Wrapper(&vsp->image);
+ params[1] = readUint16Wrapper(&vsp->palette);
+ params[2] = readUint16Wrapper(&vsp->x);
+ params[3] = readUint16Wrapper(&vsp->y);
+
+ if (getGameType() == GType_SIMON1) {
+ params[4] = READ_BE_UINT16(&vsp->flags);
+ } else {
+ *(byte *)(&params[4]) = (byte)vsp->flags;
+ }
+
+ _vcPtr = (const byte *)params;
+ vc10_draw();
+
+ vsp++;
+ }
+
+ if (_drawImagesDebug)
+ memset(_sdl_buf_attached, 0, _screenWidth * _screenHeight);
+
+ _updateScreen++;
+ _vcPtr = vc_ptr_org;
+}
+
+void SimonEngine::timer_vga_sprites_helper() {
+ byte *dst = dx_lock_2();
+ const byte *src;
+ uint x;
+
+ if (_scrollFlag < 0) {
+ memmove(dst + 8, dst, _screenWidth * _scrollHeight - 8);
+ } else {
+ memmove(dst, dst + 8, _screenWidth * _scrollHeight - 8);
+ }
+
+ x = _scrollX - 1;
+
+ if (_scrollFlag > 0) {
+ dst += _screenWidth - 8;
+ x += 41;
+ }
+
+ src = _scrollImage + x * 4;
+ decodeStripA(dst, src + READ_BE_UINT32(src), _scrollHeight);
+
+ dx_unlock_2();
+
+
+ memcpy(_sdl_buf_attached, _sdl_buf, _screenWidth * _screenHeight);
+ dx_copy_from_attached_to_3(_scrollHeight);
+
+
+ _scrollX += _scrollFlag;
+
+ vcWriteVar(251, _scrollX);
+
+ _scrollFlag = 0;
+}
+
+void SimonEngine::timer_vga_sprites_2() {
+ VgaSprite *vsp;
+ VgaPointersEntry *vpe;
+ const byte *vc_ptr_org = _vcPtr;
+ uint16 params[5]; // parameters to vc10_draw
+
+ if (_paletteFlag == 2)
+ _paletteFlag = 1;
+
+ vsp = _vgaSprites;
+ while (vsp->id != 0) {
+ vsp->windowNum &= 0x7FFF;
+
+ vpe = &_vgaBufferPointers[vsp->fileId];
+ _curVgaFile1 = vpe->vgaFile1;
+ _curVgaFile2 = vpe->vgaFile2;
+ _curSfxFile = vpe->sfxFile;
+ _windowNum = vsp->windowNum;
+ _vgaCurSpriteId = vsp->id;
+
+ if (vsp->image)
+ fprintf(_dumpFile, "id:%5d image:%3d base-color:%3d x:%3d y:%3d flags:%x\n",
+ vsp->id, vsp->image, vsp->palette, vsp->x, vsp->y, vsp->flags);
+ params[0] = readUint16Wrapper(&vsp->image);
+ params[1] = readUint16Wrapper(&vsp->palette);
+ params[2] = readUint16Wrapper(&vsp->x);
+ params[3] = readUint16Wrapper(&vsp->y);
+ params[4] = readUint16Wrapper(&vsp->flags);
+ _vcPtr = (const byte *)params;
+ vc10_draw();
+
+ vsp++;
+ }
+
+ _updateScreen++;
+ _vcPtr = vc_ptr_org;
+}
+
+void SimonEngine::timer_proc1() {
+ _timer4++;
+
+ if (_lockWord & 0x80E9 || _lockWord & 2)
+ return;
+
+ _timer1++;
+
+ _lockWord |= 2;
+
+ if (!(_lockWord & 0x10)) {
+ expire_vga_timers();
+ expire_vga_timers();
+ _syncFlag2 ^= 1;
+ _cepeFlag ^= 1;
+ if (!_cepeFlag)
+ expire_vga_timers();
+
+ if (_mouseHideCount != 0 && _syncFlag2) {
+ _lockWord &= ~2;
+ return;
+ }
+ }
+
+ timer_vga_sprites();
+ if (_drawImagesDebug)
+ timer_vga_sprites_2();
+
+ if (_copyPartialMode == 1) {
+ dx_copy_from_2_to_attached(80, 46, 208 - 80, 94 - 46);
+ }
+
+ if (_copyPartialMode == 2) {
+ // copy partial from attached to 2
+ dx_copy_from_attached_to_2(176, 61, _screenWidth - 176, 134 - 61);
+ _copyPartialMode = 0;
+ }
+
+ if (_updateScreen) {
+ handle_mouse_moved();
+ dx_update_screen_and_palette();
+ _updateScreen = false;
+ }
+
+ _lockWord &= ~2;
+}
+
+void SimonEngine::timer_callback() {
+ if (_timer5 != 0) {
+ _syncFlag2 = true;
+ _timer5--;
+ } else {
+ timer_proc1();
+ }
+}
+
+void SimonEngine::fcs_setTextColor(FillOrCopyStruct *fcs, uint value) {
+ fcs->text_color = value;
+}
+
+void SimonEngine::o_vga_reset() {
+ _lockWord |= 0x8000;
+ vc27_resetSprite();
+ _lockWord &= ~0x8000;
+}
+
+bool SimonEngine::itemIsSiblingOf(uint16 a) {
+ Item *item;
+
+ CHECK_BOUNDS(a, _vcItemArray);
+
+ item = _vcItemArray[a];
+ if (item == NULL)
+ return true;
+
+ return getItem1Ptr()->parent == item->parent;
+}
+
+bool SimonEngine::itemIsParentOf(uint16 a, uint16 b) {
+ Item *item_a, *item_b;
+
+ CHECK_BOUNDS(a, _vcItemArray);
+ CHECK_BOUNDS(b, _vcItemArray);
+
+ item_a = _vcItemArray[a];
+ item_b = _vcItemArray[b];
+
+ if (item_a == NULL || item_b == NULL)
+ return true;
+
+ return derefItem(item_a->parent) == item_b;
+}
+
+bool SimonEngine::vc_maybe_skip_proc_1(uint16 a, int16 b) {
+ Item *item;
+
+ CHECK_BOUNDS(a, _vcItemArray);
+
+ item = _vcItemArray[a];
+ if (item == NULL)
+ return true;
+ return item->state == b;
+}
+
+// OK
+void SimonEngine::closeWindow(uint a) {
+ if (_windowArray[a] == NULL)
+ return;
+ removeIconArray(a);
+ video_copy_if_flag_0x8_c(_windowArray[a]);
+ _windowArray[a] = NULL;
+ if (_curWindow == a) {
+ _textWindow = NULL;
+ changeWindow(0);
+ }
+}
+
+// OK
+void SimonEngine::changeWindow(uint a) {
+ a &= 7;
+
+ if (_windowArray[a] == NULL || _curWindow == a)
+ return;
+
+ _curWindow = a;
+ showmessage_print_char(0);
+ _textWindow = _windowArray[a];
+
+ showmessage_helper_3(_textWindow->textLength, _textWindow->textMaxLength);
+}
+
+// OK
+FillOrCopyStruct *SimonEngine::openWindow(uint x, uint y, uint w, uint h, uint flags, uint fill_color, uint text_color) {
+ FillOrCopyStruct *fcs;
+
+ fcs = _fcs_list;
+ while (fcs->mode != 0)
+ fcs++;
+
+ fcs->mode = 2;
+ fcs->x = x;
+ fcs->y = y;
+ fcs->width = w;
+ fcs->height = h;
+ fcs->flags = flags;
+ fcs->fill_color = fill_color;
+ fcs->text_color = text_color;
+ fcs->textColumn = 0;
+ fcs->textRow = 0;
+ fcs->textColumnOffset = 0;
+ fcs->textMaxLength = fcs->width * 8 / 6; // characters are 6 pixels
+ return fcs;
+}
+
+Item *SimonEngine::derefItem(uint item) {
+ if (item >= _itemArraySize)
+ error("derefItem: invalid item %d", item);
+ return _itemArrayPtr[item];
+}
+
+uint SimonEngine::itemPtrToID(Item *id) {
+ uint i;
+ for (i = 0; i != _itemArraySize; i++)
+ if (_itemArrayPtr[i] == id)
+ return i;
+ error("itemPtrToID: not found");
+ return 0;
+}
+
+void SimonEngine::o_pathfind(int x, int y, uint var_1, uint var_2) {
+ const uint16 *p;
+ uint i, j;
+ uint prev_i;
+ uint x_diff, y_diff;
+ uint best_i = 0, best_j = 0, best_dist = 0xFFFFFFFF;
+
+ if (getGameType() == GType_SIMON2) {
+ x += _scrollX * 8;
+ }
+
+ int end = (getGameType() == GType_FF) ? 9999 : 999;
+ prev_i = 21 - _variableArray[12];
+ for (i = 20; i != 0; --i) {
+ p = (const uint16 *)_pathFindArray[20 - i];
+ if (!p)
+ continue;
+ for (j = 0; readUint16Wrapper(&p[0]) != end; j++, p += 2) {
+ x_diff = abs((int)(readUint16Wrapper(&p[0]) - x));
+ y_diff = abs((int)(readUint16Wrapper(&p[1]) - 12 - y));
+
+ if (x_diff < y_diff) {
+ x_diff /= 4;
+ y_diff *= 4;
+ }
+ x_diff += y_diff /= 4;
+
+ if (x_diff < best_dist || x_diff == best_dist && prev_i == i) {
+ best_dist = x_diff;
+ best_i = 21 - i;
+ best_j = j;
+ }
+ }
+ }
+
+ _variableArray[var_1] = best_i;
+ _variableArray[var_2] = best_j;
+}
+
+// ok
+void SimonEngine::removeIconArray(uint fcs_index) {
+ FillOrCopyStruct *fcs;
+ uint16 fcsunk1;
+ uint16 i;
+
+ fcs = _windowArray[fcs_index & 7];
+ fcsunk1 = _curWindow;
+
+ if (fcs == NULL || fcs->fcs_data == NULL)
+ return;
+
+ changeWindow(fcs_index);
+ fcs_putchar(12);
+ changeWindow(fcsunk1);
+
+ for (i = 0; fcs->fcs_data->e[i].item != NULL; i++) {
+ delete_hitarea_by_index(fcs->fcs_data->e[i].hit_area);
+ }
+
+ if (fcs->fcs_data->upArrow != -1) {
+ delete_hitarea_by_index(fcs->fcs_data->upArrow);
+ }
+
+ if (fcs->fcs_data->downArrow != -1) {
+ delete_hitarea_by_index(fcs->fcs_data->downArrow);
+ if (getGameType() == GType_SIMON1)
+ removeArrows(fcs, fcs_index);
+ }
+
+ free(fcs->fcs_data);
+ fcs->fcs_data = NULL;
+
+ _fcsData1[fcs_index] = 0;
+ _fcsData2[fcs_index] = 0;
+}
+
+// ok
+void SimonEngine::removeArrows(FillOrCopyStruct *fcs, uint fcs_index) {
+ o_kill_sprite_simon1(128);
+}
+
+void SimonEngine::delete_hitarea_by_index(uint index) {
+ CHECK_BOUNDS(index, _hitAreas);
+ _hitAreas[index].flags = 0;
+}
+
+// ok
+void SimonEngine::fcs_putchar(uint a) {
+ if (_textWindow != _windowArray[0])
+ video_putchar(_textWindow, a);
+}
+
+// ok
+void SimonEngine::video_fill_or_copy_from_3_to_2(FillOrCopyStruct *fcs) {
+ if (fcs->flags & 0x10)
+ copy_img_from_3_to_2(fcs);
+ else
+ video_erase(fcs);
+
+ fcs->textColumn = 0;
+ fcs->textRow = 0;
+ fcs->textColumnOffset = 0;
+ fcs->textLength = 0;
+}
+
+// ok
+void SimonEngine::copy_img_from_3_to_2(FillOrCopyStruct *fcs) {
+ _lockWord |= 0x8000;
+
+ if (getGameType() == GType_SIMON1) {
+ dx_copy_rgn_from_3_to_2(fcs->y + fcs->height * 8 + ((fcs == _windowArray[2]) ? 1 : 0), (fcs->x + fcs->width) * 8, fcs->y, fcs->x * 8);
+ } else {
+ if (_vgaVar6 && _windowArray[2] == fcs) {
+ fcs = _windowArray[6];
+ _vgaVar6 = 0;
+ }
+
+ dx_copy_rgn_from_3_to_2(fcs->y + fcs->height * 8, (fcs->x + fcs->width) * 8, fcs->y, fcs->x * 8);
+ }
+
+ _lockWord &= ~0x8000;
+}
+
+void SimonEngine::video_erase(FillOrCopyStruct *fcs) {
+ byte *dst;
+ uint h;
+
+ _lockWord |= 0x8000;
+
+ dst = dx_lock_2();
+ dst += _dxSurfacePitch * fcs->y + fcs->x * 8;
+
+ h = fcs->height * 8;
+ do {
+ memset(dst, fcs->fill_color, fcs->width * 8);
+ dst += _dxSurfacePitch;
+ } while (--h);
+
+ dx_unlock_2();
+ _lockWord &= ~0x8000;
+}
+
+VgaSprite *SimonEngine::findCurSprite() {
+ VgaSprite *vsp = _vgaSprites;
+ while (vsp->id) {
+ if (getGameType() == GType_SIMON1) {
+ if (vsp->id == _vgaCurSpriteId)
+ break;
+ } else {
+ if (vsp->id == _vgaCurSpriteId && vsp->fileId == _vgaCurFileId)
+ break;
+ }
+ vsp++;
+ }
+ return vsp;
+}
+
+bool SimonEngine::isSpriteLoaded(uint16 id, uint16 fileId) {
+ VgaSprite *vsp = _vgaSprites;
+ while (vsp->id) {
+ if (getGameType() == GType_SIMON1) {
+ if (vsp->id == id)
+ return true;
+ } else {
+ if (vsp->id == id && vsp->fileId == fileId)
+ return true;
+ }
+ vsp++;
+ }
+ return false;
+}
+
+void SimonEngine::processSpecialKeys() {
+ switch (_keyPressed) {
+ case 27: // escape
+ _exitCutscene = true;
+ break;
+ case 59: // F1
+ if (getGameType() == GType_SIMON1) {
+ vcWriteVar(5, 40);
+ } else {
+ vcWriteVar(5, 50);
+ }
+ vcWriteVar(86, 0);
+ break;
+ case 60: // F2
+ if (getGameType() == GType_SIMON1) {
+ vcWriteVar(5, 60);
+ } else {
+ vcWriteVar(5, 75);
+ }
+ vcWriteVar(86, 1);
+ break;
+ case 61: // F3
+ if (getGameType() == GType_SIMON1) {
+ vcWriteVar(5, 100);
+ } else {
+ vcWriteVar(5, 125);
+ }
+ vcWriteVar(86, 2);
+ break;
+ case 63: // F5
+ if (getGameType() == GType_SIMON2 || getGameType() == GType_FF)
+ _exitCutscene = true;
+ break;
+ case 'p':
+ pause();
+ break;
+ case 't':
+ if ((getGameType() == GType_SIMON2 && getFeatures() & GF_TALKIE) || ( getFeatures() & GF_TALKIE && _language > 1))
+ if (_speech)
+ _subtitles ^= 1;
+ break;
+ case 'v':
+ if ((getGameType() == GType_SIMON2) && (getFeatures() & GF_TALKIE))
+ if (_subtitles)
+ _speech ^= 1;
+ case '+':
+ midi.set_volume(midi.get_volume() + 16);
+ break;
+ case '-':
+ midi.set_volume(midi.get_volume() - 16);
+ break;
+ case 'm':
+ midi.pause(_musicPaused ^= 1);
+ break;
+ case 's':
+ if (getGameId() == GID_SIMON1DOS)
+ midi._enable_sfx ^= 1;
+ else
+ _sound->effectsPause(_effectsPaused ^= 1);
+ break;
+ case 'b':
+ _sound->ambientPause(_ambientPaused ^= 1);
+ break;
+ case 'r':
+ if (_debugMode)
+ _startMainScript ^= 1;
+ break;
+ case 'o':
+ if (_debugMode)
+ _continousMainScript ^= 1;
+ break;
+ case 'a':
+ if (_debugMode)
+ _startVgaScript ^= 1;
+ break;
+ case 'g':
+ if (_debugMode)
+ _continousVgaScript ^= 1;
+ break;
+ case 'i':
+ if (_debugMode)
+ _drawImagesDebug ^= 1;
+ break;
+ case 'd':
+ if (_debugMode)
+ _dumpImages ^=1;
+ break;
+ }
+
+ _keyPressed = 0;
+}
+
+void SimonEngine::pause() {
+ _keyPressed = 1;
+ _pause = 1;
+ bool ambient_status = _ambientPaused;
+ bool music_status = _musicPaused;
+
+ midi.pause(true);
+ _sound->ambientPause(true);
+ while (_pause) {
+ delay(1);
+ if (_keyPressed == 'p')
+ _pause = 0;
+ }
+ midi.pause(music_status);
+ _sound->ambientPause(ambient_status);
+
+}
+
+void SimonEngine::video_toggle_colors(HitArea * ha, byte a, byte b, byte c, byte d) {
+ byte *src, color;
+ uint w, h, i;
+
+ _lockWord |= 0x8000;
+ src = dx_lock_2() + ha->y * _dxSurfacePitch + ha->x;
+
+ w = ha->width;
+ h = ha->height;
+
+ // Works around bug in original Simon the Sorcerer 2
+ // Animations continue in background when load/save dialog is open
+ // often causing the savegame name highlighter to be cut short
+ if (!(h > 0 && w > 0 && ha->x + w <= _screenWidth && ha->y + h <= _screenHeight)) {
+ debug(1,"Invalid coordinates in video_toggle_colors (%d,%d,%d,%d)", ha->x, ha->y, ha->width, ha->height);
+ _lockWord &= ~0x8000;
+ return;
+ }
+
+ do {
+ for (i = 0; i != w; ++i) {
+ color = src[i];
+ if (a >= color && b < color) {
+ if (c >= color)
+ color += d;
+ else
+ color -= d;
+ src[i] = color;
+ }
+ }
+ src += _dxSurfacePitch;
+ } while (--h);
+
+
+ dx_unlock_2();
+ _lockWord &= ~0x8000;
+}
+
+void SimonEngine::video_copy_if_flag_0x8_c(FillOrCopyStruct *fcs) {
+ if (fcs->flags & 8)
+ copy_img_from_3_to_2(fcs);
+ fcs->mode = 0;
+}
+
+void SimonEngine::loadSprite(uint windowNum, uint fileId, uint vgaSpriteId, uint x, uint y, uint palette) {
+ VgaSprite *vsp;
+ VgaPointersEntry *vpe;
+ byte *p, *pp;
+ uint count;
+
+ _lockWord |= 0x40;
+
+ if (isSpriteLoaded(vgaSpriteId, fileId)) {
+ _lockWord &= ~0x40;
+ return;
+ }
+
+ vsp = _vgaSprites;
+ while (vsp->id != 0)
+ vsp++;
+
+ vsp->windowNum = windowNum;
+ vsp->priority = 0;
+ vsp->flags = 0;
+
+ vsp->y = y;
+ vsp->x = x;
+ vsp->image = 0;
+ vsp->palette = palette;
+ vsp->id = vgaSpriteId;
+ if (getGameType() == GType_SIMON1)
+ vsp->fileId = fileId = vgaSpriteId / 100;
+ else
+ vsp->fileId = fileId;
+
+
+ for (;;) {
+ vpe = &_vgaBufferPointers[fileId];
+ _vgaCurFile2 = fileId;
+ _curVgaFile1 = vpe->vgaFile1;
+ if (vpe->vgaFile1 != NULL)
+ break;
+ loadZone(fileId);
+ }
+
+ pp = _curVgaFile1;
+ if (getGameType() == GType_FF) {
+ p = pp + READ_LE_UINT16(&((VgaFileHeader_Feeble *) pp)->hdr2_start);
+ count = READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationCount);
+ p = pp + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationTable);
+ } else {
+ p = pp + READ_BE_UINT16(&((VgaFileHeader_Simon *) pp)->hdr2_start);
+ count = READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationCount);
+ p = pp + READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationTable);
+ }
+
+ for (;;) {
+ if (getGameType() == GType_FF) {
+ if (READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->id) == vgaSpriteId) {
+ if (_startVgaScript)
+ dump_vga_script(pp + READ_LE_UINT16(&((AnimationHeader_Feeble*)p)->scriptOffs), fileId, vgaSpriteId);
+
+ add_vga_timer(VGA_DELAY_BASE, pp + READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->scriptOffs), vgaSpriteId, fileId);
+ break;
+ }
+ p += sizeof(AnimationHeader_Feeble);
+ } else {
+ if (READ_BE_UINT16(&((AnimationHeader_Simon *) p)->id) == vgaSpriteId) {
+ if (_startVgaScript)
+ dump_vga_script(pp + READ_BE_UINT16(&((AnimationHeader_Simon*)p)->scriptOffs), fileId, vgaSpriteId);
+
+ add_vga_timer(VGA_DELAY_BASE, pp + READ_BE_UINT16(&((AnimationHeader_Simon *) p)->scriptOffs), vgaSpriteId, fileId);
+ break;
+ }
+ p += sizeof(AnimationHeader_Simon);
+ }
+
+ if (!--count) {
+ vsp->id = 0;
+ break;
+ }
+ }
+
+ _lockWord &= ~0x40;
+}
+
+void SimonEngine::playSpeech(uint speech_id, uint vgaSpriteId) {
+ if (getGameType() == GType_SIMON1) {
+ if (speech_id == 9999) {
+ if (_subtitles)
+ return;
+ if (!(_bitArray[0] & 0x4000) && !(_bitArray[1] & 0x1000)) {
+ _bitArray[0] |= 0x4000;
+ _variableArray[100] = 15;
+ loadSprite(4, 1, 130, 0, 0, 0);
+ o_waitForSync(130);
+ }
+ _skipVgaWait = true;
+ } else {
+ if (_subtitles && _scriptVar2) {
+ loadSprite(4, 2, 204, 0, 0, 0);
+ o_waitForSync(204);
+ o_kill_sprite_simon1(204);
+ }
+ o_kill_sprite_simon1(vgaSpriteId + 201);
+ _sound->playVoice(speech_id);
+ loadSprite(4, 2, vgaSpriteId + 201, 0, 0, 0);
+ }
+ } else {
+ if (speech_id == 0xFFFF) {
+ if (_subtitles)
+ return;
+ if (!(_bitArray[0] & 0x4000) && !(_bitArray[1] & 0x1000)) {
+ _bitArray[0] |= 0x4000;
+ _variableArray[100] = 5;
+ loadSprite(4, 1, 30, 0, 0, 0);
+ o_waitForSync(130);
+ }
+ _skipVgaWait = true;
+ } else {
+ if (_subtitles && _language != Common::HB_ISR) {
+ _sound->playVoice(speech_id);
+ return;
+ } else if (_subtitles && _scriptVar2) {
+ loadSprite(4, 2, 5, 0, 0, 0);
+ o_waitForSync(205);
+ o_kill_sprite_simon2(2,5);
+ }
+
+ o_kill_sprite_simon2(2, vgaSpriteId + 2);
+ _sound->playVoice(speech_id);
+ loadSprite(4, 2, vgaSpriteId + 2, 0, 0, 0);
+ }
+ }
+}
+
+void SimonEngine::printText(uint vgaSpriteId, uint color, const char *string, int16 x, int16 y, int16 width) {
+ char convertedString[320];
+ char *convertedString2 = convertedString;
+ int16 height, talkDelay;
+ int stringLength = strlen(string);
+ int padding, lettersPerRow, lettersPerRowJustified;
+ const int textHeight = 10;
+
+ height = textHeight;
+ lettersPerRow = width / 6;
+ lettersPerRowJustified = stringLength / (stringLength / lettersPerRow + 1) + 1;
+
+ talkDelay = (stringLength + 3) / 3;
+ if ((getGameType() == GType_SIMON1) && (getFeatures() & GF_TALKIE)) {
+ if (_variableArray[141] == 0)
+ _variableArray[141] = 9;
+ _variableArray[85] = _variableArray[141] * talkDelay;
+ } else {
+ if (_variableArray[86] == 0)
+ talkDelay /= 2;
+ if (_variableArray[86] == 2)
+ talkDelay *= 2;
+ _variableArray[85] = talkDelay * 5;
+ }
+
+ assert(stringLength > 0);
+ while (stringLength > 0) {
+ int pos = 0;
+ if (stringLength > lettersPerRow) {
+ int removeLastWord = 0;
+ if (lettersPerRow > lettersPerRowJustified) {
+ pos = lettersPerRowJustified;
+ while (string[pos] != ' ')
+ pos++;
+ if (pos > lettersPerRow)
+ removeLastWord = 1;
+ }
+ if (lettersPerRow <= lettersPerRowJustified || removeLastWord) {
+ pos = lettersPerRow;
+ while (string[pos] != ' ' && pos > 0)
+ pos--;
+ }
+ height += textHeight;
+ y -= textHeight;
+ } else
+ pos = stringLength;
+ padding = (lettersPerRow - pos) % 2 ?
+ (lettersPerRow - pos) / 2 + 1 : (lettersPerRow - pos) / 2;
+ while (padding--)
+ *convertedString2++ = ' ';
+ stringLength -= pos;
+ while (pos--)
+ *convertedString2++ = *string++;
+ *convertedString2++ = '\n';
+ string++; // skip space
+ stringLength--; // skip space
+ }
+ *(convertedString2 - 1) = '\0';
+
+ if (getGameType() == GType_SIMON1)
+ o_kill_sprite_simon1(vgaSpriteId + 199);
+ else
+ o_kill_sprite_simon2(2, vgaSpriteId);
+
+ color = color * 3 + 192;
+ if (getPlatform() == Common::kPlatformAmiga)
+ render_string_amiga(vgaSpriteId, color, width, height, convertedString);
+ else
+ render_string(vgaSpriteId, color, width, height, convertedString);
+
+ int b = 4;
+ if (!(_bitArray[8] & 0x20))
+ b = 3;
+
+ x /= 8;
+ if (y < 2)
+ y = 2;
+
+ if (getGameType() == GType_SIMON1)
+ loadSprite(b, 2, vgaSpriteId + 199, x, y, 12);
+ else
+ loadSprite(b, 2, vgaSpriteId, x, y, 12);
+}
+
+// Thanks to Stuart Caie for providing the original
+// C conversion upon which this decruncher is based.
+
+#define SD_GETBIT(var) do { \
+ if (!bits--) { \
+ s -= 4; \
+ if (s < src) \
+ return false; \
+ bb = READ_BE_UINT32(s); \
+ bits = 31; \
+ } \
+ (var) = bb & 1; \
+ bb >>= 1; \
+}while (0)
+
+#define SD_GETBITS(var, nbits) do { \
+ bc = (nbits); \
+ (var) = 0; \
+ while (bc--) { \
+ (var) <<= 1; \
+ SD_GETBIT(bit); \
+ (var) |= bit; \
+ } \
+}while (0)
+
+#define SD_TYPE_LITERAL (0)
+#define SD_TYPE_MATCH (1)
+
+static bool decrunchFile(byte *src, byte *dst, uint32 size) {
+ byte *s = src + size - 4;
+ uint32 destlen = READ_BE_UINT32 (s);
+ uint32 bb, x, y;
+ byte *d = dst + destlen;
+ byte bc, bit, bits, type;
+
+ // Initialize bit buffer.
+ s -= 4;
+ bb = x = READ_BE_UINT32 (s);
+ bits = 0;
+ do {
+ x >>= 1;
+ bits++;
+ } while (x);
+ bits--;
+
+ while (d > dst) {
+ SD_GETBIT(x);
+ if (x) {
+ SD_GETBITS(x, 2);
+ switch (x) {
+ case 0:
+ type = SD_TYPE_MATCH;
+ x = 9;
+ y = 2;
+ break;
+
+ case 1:
+ type = SD_TYPE_MATCH;
+ x = 10;
+ y = 3;
+ break;
+
+ case 2:
+ type = SD_TYPE_MATCH;
+ x = 12;
+ SD_GETBITS(y, 8);
+ break;
+
+ default:
+ type = SD_TYPE_LITERAL;
+ x = 8;
+ y = 8;
+ }
+ } else {
+ SD_GETBIT(x);
+ if (x) {
+ type = SD_TYPE_MATCH;
+ x = 8;
+ y = 1;
+ } else {
+ type = SD_TYPE_LITERAL;
+ x = 3;
+ y = 0;
+ }
+ }
+
+ if (type == SD_TYPE_LITERAL) {
+ SD_GETBITS(x, x);
+ y += x;
+ if ((int)(y + 1) > (d - dst))
+ return false; // Overflow?
+ do {
+ SD_GETBITS(x, 8);
+ *--d = x;
+ } while (y-- > 0);
+ } else {
+ if ((int)(y + 1) > (d - dst))
+ return false; // Overflow?
+ SD_GETBITS(x, x);
+ if ((d + x) > (dst + destlen))
+ return false; // Offset overflow?
+ do {
+ d--;
+ *d = d[x];
+ } while (y-- > 0);
+ }
+ }
+
+ // Successful decrunch.
+ return true;
+}
+
+#undef SD_GETBIT
+#undef SD_GETBITS
+#undef SD_TYPE_LITERAL
+#undef SD_TYPE_MATCH
+
+void SimonEngine::read_vga_from_datfile_1(uint vga_id) {
+ if (getFeatures() & GF_OLD_BUNDLE) {
+ File in;
+ char buf[15];
+ uint32 size;
+ if (vga_id == 23)
+ vga_id = 112;
+ if (vga_id == 328)
+ vga_id = 119;
+
+ if (getPlatform() == Common::kPlatformAmiga) {
+ if (getFeatures() & GF_TALKIE)
+ sprintf(buf, "0%d.out", vga_id);
+ else
+ sprintf(buf, "0%d.pkd", vga_id);
+ } else {
+ sprintf(buf, "0%d.VGA", vga_id);
+ }
+
+ in.open(buf);
+ if (in.isOpen() == false)
+ error("read_vga_from_datfile_1: can't open %s", buf);
+ size = in.size();
+
+ if (getFeatures() & GF_CRUNCHED) {
+ byte *buffer = new byte[size];
+ if (in.read(buffer, size) != size)
+ error("read_vga_from_datfile_1: read failed");
+ decrunchFile(buffer, _vgaBufferPointers[11].vgaFile2, size);
+ delete [] buffer;
+ } else {
+ if (in.read(_vgaBufferPointers[11].vgaFile2, size) != size)
+ error("read_vga_from_datfile_1: read failed");
+ }
+ in.close();
+ } else {
+ uint32 offs_a = _gameOffsetsPtr[vga_id];
+ uint32 size = _gameOffsetsPtr[vga_id + 1] - offs_a;
+
+ resfile_read(_vgaBufferPointers[11].vgaFile2, offs_a, size);
+ }
+}
+
+byte *SimonEngine::read_vga_from_datfile_2(uint id, uint type) {
+ // !!! HACK !!!
+ // allocate more space for text to cope with foreign languages that use
+ // up more space than english. I hope 6400 bytes are enough. This number
+ // is base on: 2 (lines) * 320 (screen width) * 10 (textheight) -- olki
+ int extraBuffer = (id == 5 ? 6400 : 0);
+
+ if (getFeatures() & GF_OLD_BUNDLE) {
+ File in;
+ char buf[15];
+ uint32 size;
+ byte *dst = NULL;
+
+ if (getPlatform() == Common::kPlatformAmiga) {
+ if (getFeatures() & GF_TALKIE)
+ sprintf(buf, "%.3d%d.out", id / 2, type);
+ else
+ sprintf(buf, "%.3d%d.pkd", id / 2, type);
+ } else {
+ sprintf(buf, "%.3d%d.VGA", id / 2, type);
+ }
+
+ in.open(buf);
+ if (in.isOpen() == false) {
+ if (type == 3)
+ return NULL;
+ else
+ error("read_vga_from_datfile_2: can't open %s", buf);
+ }
+ size = in.size();
+
+ if (getFeatures() & GF_CRUNCHED) {
+ byte *buffer = new byte[size];
+ if (in.read(buffer, size) != size)
+ error("read_vga_from_datfile_2: read failed");
+ dst = setup_vga_destination (READ_BE_UINT32(buffer + size - 4) + extraBuffer);
+ decrunchFile(buffer, dst, size);
+ delete[] buffer;
+ } else {
+ dst = setup_vga_destination(size + extraBuffer);
+ if (in.read(dst, size) != size)
+ error("read_vga_from_datfile_2: read failed");
+ }
+ in.close();
+
+ return dst;
+ } else {
+ uint32 offs_a = _gameOffsetsPtr[id];
+ uint32 size = _gameOffsetsPtr[id + 1] - offs_a;
+ byte *dst;
+
+ dst = setup_vga_destination(size + extraBuffer);
+ resfile_read(dst, offs_a, size);
+
+ return dst;
+ }
+}
+
+void SimonEngine::resfile_read(void *dst, uint32 offs, uint32 size) {
+ _gameFile->seek(offs, SEEK_SET);
+ if (_gameFile->read(dst, size) != size)
+ error("resfile_read(%d,%d) read failed", offs, size);
+}
+
+void SimonEngine::openGameFile() {
+ if (!(getFeatures() & GF_OLD_BUNDLE)) {
+ _gameFile = new File();
+ _gameFile->open(gss->gme_filename);
+
+ if (_gameFile->isOpen() == false)
+ error("Can't open game file '%s'", gss->gme_filename);
+
+ uint32 size = _gameFile->readUint32LE();
+
+ _gameOffsetsPtr = (uint32 *)malloc(size);
+ if (_gameOffsetsPtr == NULL)
+ error("out of memory, game offsets");
+
+ resfile_read(_gameOffsetsPtr, 0, size);
+#if defined(SCUMM_BIG_ENDIAN)
+ for (uint r = 0; r < size / 4; r++)
+ _gameOffsetsPtr[r] = FROM_LE_32(_gameOffsetsPtr[r]);
+#endif
+ }
+
+ if (getGameType() != GType_FF)
+ loadIconFile();
+
+ vc34_setMouseOff();
+
+ runSubroutine101();
+ startUp_helper_2();
+}
+
+void SimonEngine::runSubroutine101() {
+ Subroutine *sub;
+
+ sub = getSubroutineByID(101);
+ if (sub != NULL)
+ startSubroutineEx(sub);
+
+ startUp_helper_2();
+}
+
+void SimonEngine::dx_copy_rgn_from_3_to_2(uint b, uint r, uint y, uint x) {
+ byte *dst, *src;
+ uint i;
+
+ dst = dx_lock_2();
+ src = _sdl_buf_3;
+
+ dst += y * _dxSurfacePitch;
+ src += y * _dxSurfacePitch;
+
+ while (y < b) {
+ for (i = x; i < r; i++)
+ dst[i] = src[i];
+ y++;
+ dst += _dxSurfacePitch;
+ src += _dxSurfacePitch;
+ }
+
+ dx_unlock_2();
+}
+
+void SimonEngine::dx_clear_surfaces(uint num_lines) {
+ memset(_sdl_buf_attached, 0, num_lines * _screenWidth);
+
+ _system->copyRectToScreen(_sdl_buf_attached, _screenWidth, 0, 0, _screenWidth, num_lines);
+
+ if (_dxUse3Or4ForLock) {
+ memset(_sdl_buf, 0, num_lines * _screenWidth);
+ memset(_sdl_buf_3, 0, num_lines * _screenWidth);
+ }
+}
+
+void SimonEngine::dx_clear_attached_from_top(uint lines) {
+ memset(_sdl_buf_attached, 0, lines * _screenWidth);
+}
+
+void SimonEngine::dx_copy_from_attached_to_2(uint x, uint y, uint w, uint h) {
+ uint offs = x + y * _screenWidth;
+ byte *s = _sdl_buf_attached + offs;
+ byte *d = _sdl_buf + offs;
+
+ do {
+ memcpy(d, s, w);
+ d += _screenWidth;
+ s += _screenWidth;
+ } while (--h);
+}
+
+void SimonEngine::dx_copy_from_2_to_attached(uint x, uint y, uint w, uint h) {
+ uint offs = x + y * _screenWidth;
+ byte *s = _sdl_buf + offs;
+ byte *d = _sdl_buf_attached + offs;
+
+ do {
+ memcpy(d, s, w);
+ d += _screenWidth;
+ s += _screenWidth;
+ } while (--h);
+}
+
+void SimonEngine::dx_copy_from_attached_to_3(uint lines) {
+ memcpy(_sdl_buf_3, _sdl_buf_attached, lines * _screenWidth);
+}
+
+void SimonEngine::dx_update_screen_and_palette() {
+ _numScreenUpdates++;
+
+ if (_paletteColorCount == 0 && _paletteFlag == 1) {
+ _paletteFlag = 0;
+ if (memcmp(_palette, _paletteBackup, 1024) != 0) {
+ memcpy(_paletteBackup, _palette, 1024);
+ _system->setPalette(_palette, 0, 256);
+ }
+ }
+
+ _system->copyRectToScreen(_sdl_buf_attached, _screenWidth, 0, 0, _screenWidth, _screenHeight);
+ _system->updateScreen();
+
+ memcpy(_sdl_buf_attached, _sdl_buf, _screenWidth * _screenHeight);
+
+ if (_paletteColorCount != 0) {
+ if (getGameType() == GType_SIMON1 && _usePaletteDelay) {
+ delay(100);
+ _usePaletteDelay = false;
+ }
+ fastFadeIn();
+ }
+}
+
+void SimonEngine::fastFadeIn() {
+ if (_paletteColorCount & 0x8000) {
+ slowFadeIn();
+ } else {
+ _paletteFlag = false;
+ memcpy(_paletteBackup, _palette, 1024);
+ _system->setPalette(_palette, 0, _paletteColorCount);
+ _paletteColorCount = 0;
+ }
+}
+
+void SimonEngine::slowFadeIn() {
+ uint8 paletteTmp[768];
+ uint8 *src, *dst;
+ int c, p;
+
+ _paletteColorCount &= 0x7fff;
+ _paletteFlag = false;
+
+ memcpy(_videoBuf1, _palette, 1024); // Difference
+ memset(_videoBuf1, 0, 768);
+
+ memcpy(_paletteBackup, _palette, 768);
+ memcpy(paletteTmp, _palette, 768);
+
+ for (c = 255; c > 0; c -= 4) {
+ src = paletteTmp;
+ dst = _videoBuf1;
+
+ for (p = _paletteColorCount; p !=0 ; p--) {
+ if (*src >= c)
+ *dst = *dst + 4;
+
+ src++;
+ dst++;
+ }
+ _system->setPalette(_videoBuf1, 0, _videoNumPalColors);
+ if (_fade)
+ _system->updateScreen();
+ delay(5);
+ }
+ _paletteColorCount = 0;
+}
+
+int SimonEngine::go() {
+ if (!_dumpFile)
+ _dumpFile = stdout;
+
+ // allocate buffers
+ _sdl_buf_3 = (byte *)calloc(_screenWidth * _screenHeight, 1);
+ _sdl_buf = (byte *)calloc(_screenWidth * _screenHeight, 1);
+ _sdl_buf_attached = (byte *)calloc(_screenWidth * _screenHeight, 1);
+
+ allocItemHeap();
+ allocTablesHeap();
+
+ setup_vga_file_buf_pointers();
+
+ _sound = new Sound(this, gss, _mixer);
+ _debugger = new Debugger(this);
+
+ if (ConfMan.hasKey("sfx_mute") && ConfMan.getBool("sfx_mute") == 1) {
+ if (getGameId() == GID_SIMON1DOS)
+ midi._enable_sfx ^= 1;
+ else
+ _sound->effectsPause(_effectsPaused ^= 1);
+ }
+
+ loadGamePcFile(gss->gamepc_filename);
+
+ addTimeEvent(0, 1);
+ openGameFile();
+
+ _lastMusicPlayed = -1;
+ _frameRate = 1;
+
+ _startMainScript = false;
+ _continousMainScript = false;
+ _startVgaScript = false;
+ _continousVgaScript = false;
+ _drawImagesDebug = false;
+
+ if (gDebugLevel == 2)
+ _continousMainScript = true;
+ if (gDebugLevel == 3)
+ _continousVgaScript = true;
+ if (gDebugLevel == 4)
+ _startMainScript = true;
+ if (gDebugLevel == 5)
+ _startVgaScript = true;
+
+ if (getFeatures() & GF_TALKIE) {
+ // English and German versions of Simon the Sorcerer 1 don't have full subtitles
+ if (getGameType() == GType_SIMON1 && (_language == Common::EN_USA || _language == Common::DE_DEU))
+ _subtitles = false;
+ } else {
+ _subtitles = true;
+ }
+
+ while (1) {
+ hitarea_stuff();
+ handle_verb_clicked(_verbHitArea);
+ delay(100);
+ }
+
+ return 0;
+}
+
+void SimonEngine::shutdown() {
+ delete _gameFile;
+
+ midi.close();
+
+ free(_stringTabPtr);
+ free(_itemArrayPtr);
+ free(_itemHeapPtr - _itemHeapCurPos);
+ free(_tablesHeapPtr - _tablesHeapCurPos);
+ free(_tblList);
+ free(_iconFilePtr);
+ free(_gameOffsetsPtr);
+
+ _system->quit();
+}
+
+void SimonEngine::delay(uint amount) {
+ OSystem::Event event;
+
+ uint32 start = _system->getMillis();
+ uint32 cur = start;
+ uint this_delay, vga_period;
+
+ if (_debugger->isAttached())
+ _debugger->onFrame();
+
+ if (_fastMode)
+ vga_period = 10;
+ else if (getGameType() == GType_SIMON2)
+ vga_period = 45 * _speed;
+ else
+ vga_period = 50 * _speed;
+
+ _rnd.getRandomNumber(2);
+
+ do {
+ while (!_inCallBack && cur >= _lastVgaTick + vga_period && !_pause) {
+ _lastVgaTick += vga_period;
+
+ // don't get too many frames behind
+ if (cur >= _lastVgaTick + vga_period * 2)
+ _lastVgaTick = cur;
+
+ _inCallBack = true;
+ timer_callback();
+ _inCallBack = false;
+ }
+
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.keycode >= '0' && event.kbd.keycode <='9'
+ && (event.kbd.flags == OSystem::KBD_ALT ||
+ event.kbd.flags == OSystem::KBD_CTRL)) {
+ _saveLoadSlot = event.kbd.keycode - '0';
+
+ // There is no save slot 0
+ if (_saveLoadSlot == 0)
+ _saveLoadSlot = 10;
+
+ sprintf(_saveLoadName, "Quicksave %d", _saveLoadSlot);
+ _saveLoadType = (event.kbd.flags == OSystem::KBD_ALT) ? 1 : 2;
+
+ // We should only allow a load or save when it was possible in original
+ // This stops load/save during copy protection, conversations and cut scenes
+ if (!_mouseHideCount && !_showPreposition)
+ quickLoadOrSave();
+ } else if (event.kbd.flags == OSystem::KBD_CTRL) {
+ if (event.kbd.keycode == 'a') {
+ GUI::Dialog *_aboutDialog;
+ _aboutDialog = new GUI::AboutDialog();
+ _aboutDialog->runModal();
+ } else if (event.kbd.keycode == 'f')
+ _fastMode ^= 1;
+ else if (event.kbd.keycode == 'd')
+ _debugger->attach();
+ }
+ // Make sure backspace works right (this fixes a small issue on OS X)
+ if (event.kbd.keycode == 8)
+ _keyPressed = 8;
+ else
+ _keyPressed = (byte)event.kbd.ascii;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _sdlMouseX = event.mouse.x;
+ _sdlMouseY = event.mouse.y;
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ _leftButtonDown++;
+#if defined (_WIN32_WCE) || defined(PALMOS_MODE)
+ _sdlMouseX = event.mouse.x;
+ _sdlMouseY = event.mouse.y;
+#endif
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+ if (getGameType() == GType_SIMON2 || getGameType() == GType_FF)
+ _skipSpeech = true;
+ else
+ _exitCutscene = true;
+ break;
+ case OSystem::EVENT_QUIT:
+ shutdown();
+ return;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (amount == 0)
+ break;
+
+ {
+ this_delay = _fastMode ? 1 : 20 * _speed;
+ if (this_delay > amount)
+ this_delay = amount;
+ _system->delayMillis(this_delay);
+ }
+ cur = _system->getMillis();
+ } while (cur < start + amount);
+}
+
+void SimonEngine::loadMusic(uint music) {
+ char buf[4];
+
+ if (getPlatform() == Common::kPlatformAmiga) {
+ if (getFeatures() & GF_CRUNCHED) {
+ // TODO Add support for decruncher
+ debug(5,"loadMusic - Decrunch %dtune attempt", music);
+ }
+ // TODO Add Protracker support for simon1amiga/cd32
+ debug(5,"playMusic - Load %dtune attempt", music);
+ } else if (getGameType() == GType_SIMON2) { // Simon 2 music
+ midi.stop();
+ _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music - 1], SEEK_SET);
+ _gameFile->read(buf, 4);
+ if (!memcmp(buf, "FORM", 4)) {
+ _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music - 1], SEEK_SET);
+ midi.loadXMIDI (_gameFile);
+ } else {
+ _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music - 1], SEEK_SET);
+ midi.loadMultipleSMF (_gameFile);
+ }
+
+ _lastMusicPlayed = music;
+ _nextMusicToPlay = -1;
+ } else if (getGameType() == GType_SIMON1) { // Simon 1 music
+ midi.stop();
+ midi.setLoop (true); // Must do this BEFORE loading music. (GMF may have its own override.)
+
+ if (getFeatures() & GF_TALKIE) {
+ // FIXME: The very last music resource, a cymbal crash for when the
+ // two demons crash into each other, should NOT be looped like the
+ // other music tracks. In simon1dos/talkie the GMF resource includes
+ // a loop override that acomplishes this, but there seems to be nothing
+ // for this in the SMF resources.
+ if (music == 35)
+ midi.setLoop (false);
+
+ _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music], SEEK_SET);
+ _gameFile->read(buf, 4);
+ if (!memcmp(buf, "GMF\x1", 4)) {
+ _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music], SEEK_SET);
+ midi.loadSMF (_gameFile, music);
+ } else {
+ _gameFile->seek(_gameOffsetsPtr[MUSIC_INDEX_BASE + music], SEEK_SET);
+ midi.loadMultipleSMF (_gameFile);
+ }
+
+ } else {
+ char filename[15];
+ File f;
+ sprintf(filename, "MOD%d.MUS", music);
+ f.open(filename);
+ if (f.isOpen() == false) {
+ warning("Can't load music from '%s'", filename);
+ return;
+ }
+ if (getGameId() == GID_SIMON1DEMO)
+ midi.loadS1D (&f);
+ else
+ midi.loadSMF (&f, music);
+ }
+
+ midi.startTrack (0);
+ }
+}
+
+byte *SimonEngine::dx_lock_2() {
+ _dxSurfacePitch = _screenWidth;
+ return _sdl_buf;
+}
+
+void SimonEngine::dx_unlock_2() {
+}
+
+byte *SimonEngine::dx_lock_attached() {
+ _dxSurfacePitch = _screenWidth;
+ return _dxUse3Or4ForLock ? _sdl_buf_3 : _sdl_buf_attached;
+}
+
+void SimonEngine::dx_unlock_attached() {
+}
+
+void SimonEngine::set_volume(int volume) {
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume);
+}
+
+byte SimonEngine::getByte() {
+ return *_codePtr++;
+}
+
+} // End of namespace Simon
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Simon_Simon)
+_GSETPTR(Simon::simon1_settings, GBVARS_SIMON1SETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON)
+_GSETPTR(Simon::simon1acorn_settings, GBVARS_SIMON1ACORNSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON)
+_GSETPTR(Simon::simon1amiga_settings, GBVARS_SIMON1AMIGASETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON)
+_GSETPTR(Simon::simon1demo_settings, GBVARS_SIMON1DEMOSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON)
+_GSETPTR(Simon::simon2win_settings, GBVARS_SIMON2WINSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON)
+_GSETPTR(Simon::simon2dos_settings, GBVARS_SIMON2DOSSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON)
+_GSETPTR(Simon::feeblefiles_settings, GBVARS_FEEBLEFILESSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON)
+_GEND
+
+_GRELEASE(Simon_Simon)
+_GRELEASEPTR(GBVARS_SIMON1SETTINGS_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_SIMON1ACORNSETTINGS_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_SIMON1AMIGASETTINGS_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_SIMON1DEMOSETTINGS_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_SIMON2WINSETTINGS_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_SIMON2DOSSETTINGS_INDEX, GBVARS_SIMON)
+_GRELEASEPTR(GBVARS_FEEBLEFILESSETTINGS_INDEX, GBVARS_SIMON)
+_GEND
+
+#endif
diff --git a/engines/simon/simon.h b/engines/simon/simon.h
new file mode 100644
index 0000000000..8fa27924b6
--- /dev/null
+++ b/engines/simon/simon.h
@@ -0,0 +1,903 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SIMON_H
+#define SIMON_H
+
+#include <stdio.h>
+#include "base/engine.h"
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+#include "common/util.h"
+#include "simon/midi.h"
+#include "simon/sound.h"
+#include "simon/vga.h"
+
+class GameDetector;
+
+namespace Simon {
+
+/* Various other settings */
+//#define DUMP_FILE_NR 8
+//#define DUMP_BITMAPS_FILE_NR 8
+
+uint fileReadItemID(Common::File *in);
+
+#define CHECK_BOUNDS(x, y) assert((uint)(x) < ARRAYSIZE(y))
+#define NUM_PALETTE_FADEOUT 32
+
+struct Child;
+struct Child2;
+
+struct Item;
+struct FillOrCopyStruct;
+struct Subroutine;
+struct SubroutineLine;
+struct TimeEvent;
+
+struct TextLocation {
+ int16 x, y, width;
+ TextLocation() { memset(this, 0, sizeof(*this)); }
+};
+
+struct HitArea {
+ uint16 x, y;
+ uint16 width, height;
+ uint16 flags;
+ uint16 id;
+ FillOrCopyStruct *fcs;
+ Item *item_ptr;
+ uint16 unk3;
+ uint16 layer;
+ HitArea() { memset(this, 0, sizeof(*this)); }
+};
+
+struct VgaPointersEntry {
+ byte *vgaFile1;
+ byte *vgaFile2;
+ byte *sfxFile;
+ VgaPointersEntry() { memset(this, 0, sizeof(*this)); }
+};
+
+struct VgaSprite {
+ uint16 id;
+ uint16 image;
+ uint16 palette;
+ uint16 x, y; /* actually signed numbers */
+ uint16 flags;
+ uint16 priority;
+ uint16 windowNum, fileId;
+ VgaSprite() { memset(this, 0, sizeof(*this)); }
+};
+
+struct VgaSleepStruct {
+ uint16 ident;
+ const byte *code_ptr;
+ uint16 sprite_id;
+ uint16 cur_vga_file;
+ VgaSleepStruct() { memset(this, 0, sizeof(*this)); }
+};
+
+struct VgaTimerEntry {
+ uint16 delay;
+ const byte *script_pointer;
+ uint16 sprite_id;
+ uint16 cur_vga_file;
+ VgaTimerEntry() { memset(this, 0, sizeof(*this)); }
+};
+
+enum SimonTypes {
+ GType_SIMON1,
+ GType_SIMON2,
+ GType_FF
+};
+
+struct GameFileDescription {
+ const char *fileName;
+ uint16 fileType;
+};
+
+struct GameDescription {
+ const char *name;
+ SimonTypes gameType;
+ GameIds gameId;
+ const char *title;
+ int filesCount;
+ GameFileDescription *filesDescriptions;
+ uint32 features;
+ Common::Language language;
+ Common::Platform platform;
+
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { name, title, features };
+ return dummy;
+ }
+};
+
+DetectedGameList GAME_ProbeGame(const FSList &fslist, int **matches = NULL);
+
+struct GameSpecificSettings;
+
+class Debugger;
+
+class SimonEngine : public Engine {
+ friend class Debugger;
+
+ void errorString(const char *buf_input, char *buf_output);
+
+ typedef void (SimonEngine::*VgaOpcodeProc) ();
+ void setupVgaOpcodes();
+ const VgaOpcodeProc *_vga_opcode_table;
+
+public:
+ GameDescription *_gameDescription;
+
+ bool initGame(void);
+
+ int getGameId() const { return _gameDescription->gameId; }
+ int getGameType() const { return _gameDescription->gameType; }
+ uint32 getFeatures() const { return _gameDescription->features; }
+ Common::Language getLanguage() const { return _gameDescription->language; }
+ Common::Platform getPlatform() const { return _gameDescription->platform; }
+
+protected:
+ void playSting(uint a);
+
+ const byte *_vcPtr; /* video code ptr */
+ uint16 _vc_get_out_of_code;
+
+
+ uint32 *_gameOffsetsPtr;
+
+ uint VGA_DELAY_BASE;
+ uint TABLE_INDEX_BASE;
+ uint TEXT_INDEX_BASE;
+ uint NUM_VIDEO_OP_CODES;
+ uint VGA_MEM_SIZE;
+ uint TABLES_MEM_SIZE;
+ uint MUSIC_INDEX_BASE;
+ uint SOUND_INDEX_BASE;
+ const GameSpecificSettings *gss;
+
+ byte _keyPressed;
+
+ typedef enum {
+ FORMAT_NONE,
+ FORMAT_MP3,
+ FORMAT_WAV,
+ FORMAT_VOC
+ } SoundFormat;
+
+ Common::File *_gameFile;
+
+ byte *_strippedTxtMem;
+ uint _textSize;
+ uint _stringTabNum, _stringTabPos, _stringtab_numalloc;
+ byte **_stringTabPtr;
+
+ Item **_itemArrayPtr;
+ uint _itemArraySize;
+ uint _itemArrayInited;
+
+ byte *_itemHeapPtr;
+ uint _itemHeapCurPos;
+ uint _itemHeapSize;
+
+ byte *_iconFilePtr;
+
+ byte *_tblList;
+
+ const byte *_codePtr;
+
+ byte **_localStringtable;
+ uint _stringIdLocalMin, _stringIdLocalMax;
+
+ byte *_tablesHeapPtr, *_tablesHeapPtrOrg, *_tablesheapPtrNew;
+ uint _tablesHeapSize, _tablesHeapCurPos, _tablesHeapCurPosOrg;
+ uint _tablesHeapCurPosNew;
+
+ Subroutine *_subroutineList, *_subroutineListOrg;
+ uint _subroutine;
+
+ uint _dxSurfacePitch;
+
+ uint _recursionDepth;
+
+ uint32 _lastVgaTick;
+
+ uint16 _marks;
+
+ bool _scriptVar2;
+ bool _runScriptReturn1;
+ bool _skipVgaWait;
+ bool _noParentNotify;
+ bool _beardLoaded;
+ bool _hitarea_unk_3;
+ bool _mortalFlag;
+ byte _updateScreen;
+ bool _usePaletteDelay;
+ bool _syncFlag2;
+ bool _inCallBack;
+ bool _cepeFlag;
+ byte _copyPartialMode;
+ uint _speed;
+ bool _fastMode;
+ bool _dxUse3Or4ForLock;
+
+ uint16 _debugMode;
+ uint16 _language;
+ bool _pause;
+ bool _startMainScript;
+ bool _continousMainScript;
+ bool _startVgaScript;
+ bool _continousVgaScript;
+ bool _drawImagesDebug;
+ bool _dumpImages;
+ bool _speech;
+ bool _subtitles;
+ bool _fade;
+ byte _mouseCursor;
+ bool _vgaVar9;
+ int16 _scriptUnk1;
+ bool _vgaVar6;
+ int _scrollX, _scrollXMax, _scrollHeight;
+ int _scrollY, _scrollYMax, _scrollWidth;
+ int _scrollCount, _scrollFlag;
+ const byte *_scrollImage;
+ byte _vgaVar8;
+
+ int16 _scriptVerb, _scriptNoun1, _scriptNoun2;
+ int16 _scriptAdj1, _scriptAdj2;
+
+ uint16 _curWindow;
+ FillOrCopyStruct *_textWindow;
+
+ Item *_subjectItem, *_objectItem;
+ Item *_item1;
+
+ Item *_hitAreaObjectItem;
+ HitArea *_lastHitArea;
+ HitArea *_lastHitArea2Ptr;
+ HitArea *_lastHitArea3;
+ byte _leftButtonDown;
+ Item *_hitAreaSubjectItem;
+ HitArea *_hitAreaPtr5, *_hitAreaPtr7;
+ uint _needHitAreaRecalc;
+ uint _verbHitArea;
+ uint16 _hitAreaUnk4;
+ uint _mouseHideCount;
+
+ uint16 _windowNum;
+
+ uint _printCharCurPos, _printCharMaxPos;
+ uint _numLettersToPrint;
+
+ uint _lastTime;
+
+ TimeEvent *_firstTimeStruct, *_pendingDeleteTimeEvent;
+
+ uint _base_time;
+
+ uint _mouseX, _mouseY;
+ uint _mouseXOld, _mouseYOld;
+
+ Item *_dummyItem1;
+ Item *_dummyItem2;
+ Item *_dummyItem3;
+
+ volatile uint16 _lockWord;
+ uint16 _scrollUpHitArea;
+ uint16 _scrollDownHitArea;
+
+ uint16 _videoVar7;
+ volatile uint16 _paletteColorCount;
+
+ uint _screenWidth, _screenHeight;
+
+ byte _videoVar4;
+ bool _videoVar5;
+ bool _fastFadeOutFlag;
+ bool _unkPalFlag;
+ bool _exitCutscene;
+ bool _skipSpeech;
+ byte _paletteFlag;
+
+ uint _soundFileId;
+ int16 _lastMusicPlayed;
+ int16 _nextMusicToPlay;
+
+ bool _showPreposition;
+ bool _showMessageFlag;
+
+ uint _videoNumPalColors;
+
+ uint _vgaSpriteChanged;
+
+ byte *_vgaBufFreeStart, *_vgaBufEnd, *_vgaBufStart;
+ byte *_vgaFileBufOrg, *_vgaFileBufOrg2;
+
+ byte *_curVgaFile1;
+ byte *_curVgaFile2;
+ byte *_curSfxFile;
+
+ uint16 _timer1, _timer5, _timer4;
+
+ uint16 _frameRate;
+
+ uint16 _vgaCurFile2;
+ uint16 _vgaWaitFor, _vgaCurFileId;
+ uint16 _vgaCurSpriteId;
+
+ int16 _baseY;
+ float _scale;
+
+ VgaTimerEntry *_nextVgaTimerToProcess;
+
+ Item *_vcItemArray[20];
+ Item *_itemArray6[20];
+
+ uint16 _stringIdArray2[20];
+ uint16 _stringIdArray3[20];
+ uint16 _speechIdArray4[20];
+
+ uint16 _bitArray[48];
+ int16 _variableArray[256];
+
+ FillOrCopyStruct *_windowArray[8];
+
+ byte _fcsData1[8];
+ bool _fcsData2[8];
+
+ TextLocation _textLocation1, _textLocation2, _textLocation3, _textLocation4;
+
+ int _freeStringSlot;
+
+ byte _stringReturnBuffer[2][180];
+
+ HitArea _hitAreas[90];
+
+ VgaPointersEntry _vgaBufferPointers[450];
+ VgaSprite _vgaSprites[180];
+ VgaSleepStruct _vgaSleepStructs[30];
+
+ const uint16 *_pathFindArray[100];
+
+ uint8 _pathValues[400];
+ uint8 _PVCount;
+ uint8 _GPVCount;
+
+ uint8 _pathValues1[400];
+ uint8 _PVCount1;
+ uint8 _GPVCount1;
+
+ uint8 _paletteBackup[1024];
+ uint8 _palette[1024];
+
+ byte _videoBuf1[3000];
+
+ VgaTimerEntry _vgaTimerList[95];
+
+ FillOrCopyStruct *_fcs_list;
+
+ byte _lettersToPrintBuf[80];
+
+ MidiPlayer midi;
+ bool _native_mt32;
+
+ int _numScreenUpdates;
+ int _vgaTickCounter;
+
+ Sound *_sound;
+
+ bool _effectsPaused;
+ bool _ambientPaused;
+ bool _musicPaused;
+
+ Debugger *_debugger;
+
+ FILE *_dumpFile;
+
+ int _saveLoadRowCurPos;
+ int _numSaveGameRows;
+ bool _saveDialogFlag;
+ bool _saveOrLoad;
+ bool _saveLoadFlag;
+
+ byte _saveLoadType, _saveLoadSlot;
+ char _saveLoadName[32];
+
+ int _sdlMouseX, _sdlMouseY;
+
+ byte *_sdl_buf_3;
+ byte *_sdl_buf;
+ byte *_sdl_buf_attached;
+
+ Common::RandomSource _rnd;
+
+ const byte *_vc10BasePtrOld;
+ byte _hebrew_char_widths[32];
+
+public:
+ SimonEngine(GameDetector *detector, OSystem *syst);
+ virtual ~SimonEngine();
+
+protected:
+ uint16 to16Wrapper(uint value);
+ uint16 readUint16Wrapper(const void *src);
+
+ int allocGamePcVars(Common::File *in);
+ void loginPlayerHelper(Item *item, int a, int b);
+ void loginPlayer();
+ void allocateStringTable(int num);
+ void setupStringTable(byte *mem, int num);
+ void setupLocalStringTable(byte *mem, int num);
+ void readGamePcText(Common::File *in);
+ void readItemChildren(Common::File *in, Item *item, uint tmp);
+ void readItemFromGamePc(Common::File *in, Item *item);
+ void loadGamePcFile(const char *filename);
+
+ void palette_fadeout(uint32 *pal_values, uint num);
+
+ byte *allocateItem(uint size);
+ byte *allocateTable(uint size);
+ void alignTableMem();
+
+ Child *findChildOfType(Item *i, uint child);
+ Child *allocateChildBlock(Item *i, uint type, uint size);
+
+ void allocItemHeap();
+ void allocTablesHeap();
+
+ Subroutine *createSubroutine(uint a);
+ void readSubroutine(Common::File *in, Subroutine *sub);
+ SubroutineLine *createSubroutineLine(Subroutine *sub, int a);
+ void readSubroutineLine(Common::File *in, SubroutineLine *new_table, Subroutine *sub);
+ byte *readSingleOpcode(Common::File *in, byte *ptr);
+ void readSubroutineBlock(Common::File *in);
+
+ Subroutine *getSubroutineByID(uint subroutine_id);
+
+ /* used in debugger */
+ void dumpSubroutines();
+ void dumpSubroutine(Subroutine *sub);
+ void dumpSubroutineLine(SubroutineLine *sl, Subroutine *sub);
+ const byte *dumpOpcode(const byte *p);
+
+ int startSubroutine(Subroutine *sub);
+ int startSubroutineEx(Subroutine *sub);
+
+ bool checkIfToRunSubroutineLine(SubroutineLine *sl, Subroutine *sub);
+
+ int runScript();
+
+ Item *getNextItemPtr();
+ uint getNextItemID();
+ uint getItem1ID() {return 1;}
+ Item *getItem1Ptr();
+ Item *getItemPtrB();
+
+ byte getByte();
+ int getNextWord();
+
+ uint getNextVarContents();
+ uint getVarOrWord();
+ uint getVarOrByte();
+ uint readVariable(uint variable);
+ void writeNextVarContents(uint16 contents);
+ void writeVariable(uint variable, uint16 contents);
+
+ void setItemParent(Item *item, Item *parent);
+
+ uint itemPtrToID(Item *id);
+
+ Item *derefItem(uint item);
+ void setItemState(Item *item, int value);
+
+ void showMessageFormat(const char *s, ...);
+ const byte *getStringPtrByID(uint string_id);
+ const byte *getLocalStringByID(uint string_id);
+ uint getNextStringID();
+
+ void addTimeEvent(uint timeout, uint subroutine_id);
+ void delTimeEvent(TimeEvent *te);
+
+ bool isRoom(Item *item);
+ bool isObject(Item *item);
+
+ void itemChildrenChanged(Item *item);
+ void unlinkItem(Item *item);
+ void linkItem(Item *item, Item *parent);
+
+ bool o_chance(uint a);
+
+ void o_kill_sprite_simon1(uint a);
+ void o_kill_sprite_simon2(uint a, uint b);
+
+ void o_vga_reset();
+ void changeWindow(uint a);
+ void o_unk_103();
+ void closeWindow(uint a);
+ void clear_hitarea_bit_0x40(uint hitarea);
+ void set_hitarea_bit_0x40(uint hitarea);
+ void set_hitarea_x_y(uint hitarea, int x, int y);
+ bool is_hitarea_0x40_clear(uint hitarea);
+ void delete_hitarea(uint hitarea);
+ void addNewHitArea(int id, int x, int y, int width, int height, int flags, int unk3, Item *item_ptr);
+ HitArea *findEmptyHitArea();
+ void hitarea_proc_1();
+ void handle_verb_hitarea(HitArea * ha);
+ void hitarea_leave(HitArea * ha);
+ void leaveHitAreaById(uint hitarea_id);
+
+ void o_waitForSync(uint a);
+ void skipSpeech();
+ void o_sync(uint a);
+ void o_playMusic();
+ void o_saveGame();
+ void o_loadGame();
+ void o_confirmQuit();
+ void o_restoreIconArray(uint a);
+ void o_freezeBottom();
+ void killAllTimers();
+
+ uint getOffsetOfChild2Param(Child2 *child, uint prop);
+ void o_unk_160(uint a);
+ void o_playSFX(uint a);
+ void o_lockZone();
+ void o_unlockZone();
+ void o_pathfind(int x, int y, uint var_1, uint var_2);
+ void o_mouseOn();
+ void o_mouseOff();
+ void o_loadBeard();
+ void o_unloadBeard();
+ void o_unloadZone(uint a);
+ void o_unfreezeBottom();
+ void o_fadeToBlack();
+
+ TextLocation *getTextLocation(uint a);
+ void o_printStr();
+ void o_setup_cond_c();
+ void setup_cond_c_helper();
+
+ void o_inventory_descriptions();
+
+ void mouseOff();
+ void mouseOn();
+
+ void drawIconArray(uint i, Item *item_ptr, int unk1, int unk2);
+
+ void loadTextIntoMem(uint string_id);
+ void loadTablesIntoMem(uint subr_id);
+
+ uint loadTextFile(const char *filename, byte *dst);
+ Common::File *openTablesFile(const char *filename);
+ void closeTablesFile(Common::File *in);
+
+ uint loadTextFile_simon1(const char *filename, byte *dst);
+ Common::File *openTablesFile_simon1(const char *filename);
+
+ uint loadTextFile_gme(const char *filename, byte *dst);
+ Common::File *openTablesFile_gme(const char *filename);
+
+ void invokeTimeEvent(TimeEvent *te);
+ bool kickoffTimeEvents();
+
+ void defocusHitarea();
+ void endCutscene();
+ void runSubroutine101();
+ void handle_uparrow_hitarea(FillOrCopyStruct *fcs);
+ void handle_downarrow_hitarea(FillOrCopyStruct *fcs);
+ void hitareaChangedHelper();
+ void focusVerb(uint hitarea_id);
+ HitArea *findHitAreaByID(uint hitarea_id);
+
+ void showActionString(uint x, const byte *string);
+ void video_putchar(FillOrCopyStruct *fcs, byte c, byte b = 0);
+ void video_fill_or_copy_from_3_to_2(FillOrCopyStruct *fcs);
+ void video_toggle_colors(HitArea * ha, byte a, byte b, byte c, byte d);
+
+ void read_vga_from_datfile_1(uint vga_id);
+
+ uint get_fcs_ptr_3_index(FillOrCopyStruct *fcs);
+
+ void setup_hitarea_from_pos(uint x, uint y, uint mode);
+ void new_current_hitarea(HitArea * ha);
+ bool hitarea_proc_2(uint a);
+ bool hitarea_proc_3(Item *item);
+ void f10_key();
+ void hitarea_stuff();
+
+ void handle_mouse_moved();
+ void pollMouseXY();
+ void drawMousePointer();
+
+ void removeIconArray(uint fcs_index);
+ void draw_icon_c(FillOrCopyStruct *fcs, uint icon, uint x, uint y);
+ bool has_item_childflag_0x10(Item *item);
+ uint item_get_icon_number(Item *item);
+ uint setup_icon_hit_area(FillOrCopyStruct *fcs, uint x, uint y, uint icon_number, Item *item_ptr);
+ void addArrows(FillOrCopyStruct *fcs, uint fcs_index);
+
+ void loadIconFile();
+ void processSpecialKeys();
+ void hitarea_stuff_helper();
+
+ void startUp_helper_2();
+ void showmessage_helper_3(uint a, uint b);
+ void showmessage_print_char(byte chr);
+
+ void handle_verb_clicked(uint verb);
+
+ void o_set_video_mode(uint mode, uint vga_res);
+ void set_video_mode_internal(uint mode, uint vga_res_id);
+
+ void o_loadZone(uint vga_res);
+ void loadZone(uint vga_res);
+
+ void loadSprite(uint windowNum, uint vga_res, uint vga_sprite_id, uint x, uint y, uint palette);
+ void o_defineWindow(uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h);
+ void playSpeech(uint speech_id, uint vga_sprite_id);
+ void printText(uint vga_sprite_id, uint color, const char *string_ptr, int16 x, int16 y, int16 width);
+ FillOrCopyStruct *openWindow(uint x, uint y, uint w, uint h, uint flags, uint fill_color, uint text_color);
+
+ void render_string_amiga(uint vga_sprite_id, uint color, uint width, uint height, const char *txt);
+ void render_string(uint vga_sprite_id, uint color, uint width, uint height, const char *txt);
+
+ void setArrowHitAreas(FillOrCopyStruct *fcs, uint fcs_index);
+
+ byte *setup_vga_destination(uint32 size);
+ void vga_buf_unk_proc3(byte *end);
+ void vga_buf_unk_proc1(byte *end);
+ void vga_buf_unk_proc2(uint a, byte *end);
+ void delete_memptr_range(byte *end);
+
+ void setup_vga_file_buf_pointers();
+
+ void run_vga_script();
+
+public:
+ // Simon1/Simon2 video script opcodes
+ void vc1_fadeOut();
+ void vc2_call();
+ void vc3_loadSprite();
+ void vc4_fadeIn();
+ void vc5_skip_if_neq();
+ void vc6_skip_ifn_sib_with_a();
+ void vc7_skip_if_sib_with_a();
+ void vc8_skip_if_parent_is();
+ void vc9_skip_if_unk3_is();
+ void vc10_draw();
+ void vc11_clearPathFinder();
+ void vc12_delay();
+ void vc13_addToSpriteX();
+ void vc14_addToSpriteY();
+ void vc15_wakeup_id();
+ void vc16_sleep_on_id();
+ void vc17_setPathfinderItem();
+ void vc18_jump();
+ void vc19_chain_to_script();
+ void vc20_setRepeat();
+ void vc21_endRepeat();
+ void vc22_setSpritePalette();
+ void vc23_setSpritePriority();
+ void vc24_setSpriteXY();
+ void vc25_halt_sprite();
+ void vc26_setSubWindow();
+ void vc27_resetSprite();
+ void vc28_dummy_op();
+ void vc29_stopAllSounds();
+ void vc30_setFrameRate();
+ void vc31_setWindow();
+ void vc32_copyVar();
+ void vc33_setMouseOn();
+ void vc34_setMouseOff();
+ void vc35_clearWindow();
+ void vc36_setWindowImage();
+ void vc37_addToSpriteY();
+ void vc38_skipIfVarZero();
+ void vc39_setVar();
+ void vc40();
+ void vc41();
+ void vc42_delayIfNotEQ();
+ void vc43_skipIfBitClear();
+ void vc44_skipIfBitSet();
+ void vc45_setSpriteX();
+ void vc46_setSpriteY();
+ void vc47_addToVar();
+ void vc48_setPathFinder();
+ void vc49_setBit();
+ void vc50_clearBit();
+ void vc51_clear_hitarea_bit_0x40();
+ void vc52_playSound();
+ void vc53_no_op();
+ void vc54_no_op();
+ void vc55_offset_hit_area();
+ void vc56_delay();
+ void vc57_no_op();
+ void vc58();
+ void vc59();
+ void vc60_killSprite();
+ void vc61_setMaskImage();
+ void vc62_fastFadeOut();
+ void vc63_fastFadeIn();
+
+ // Simon2 specific Video Script Opcodes
+ void vc64_skipIfSpeechEnded();
+ void vc65_slowFadeIn();
+ void vc66_skipIfNotEqual();
+ void vc67_skipIfGE();
+ void vc68_skipIfLE();
+ void vc69_playTrack();
+ void vc70_queueMusic();
+ void vc71_checkMusicQueue();
+ void vc72_play_track_2();
+ void vc73_setMark();
+ void vc74_clearMark();
+
+ // Feeble specific Video Script Opcodes
+ void vc75_setScale();
+ void vc76_setScaleXOffs();
+ void vc77_setScaleYOffs();
+ void vc78_computeXY();
+ void vc79_computePosNum();
+ void vc80_setOverlayImage();
+ void vc81_setRandom();
+ void vc82_getPathValue();
+ void vc83_playSoundLoop();
+ void vc84_stopSoundLoop();
+
+protected:
+ void drawImages(VC10_state *state);
+ void drawImages_Feeble(VC10_state *state);
+
+ void delete_vga_timer(VgaTimerEntry * vte);
+ void vcResumeSprite(const byte *code_ptr, uint16 cur_file, uint16 cur_sprite);
+ int vcReadVarOrWord();
+ uint vcReadNextWord();
+ uint vcReadNextByte();
+ uint vcReadVar(uint var);
+ void vcWriteVar(uint var, int16 value);
+ void vcSkipNextInstruction();
+
+ int getScale(int y, int x);
+
+ bool itemIsSiblingOf(uint16 val);
+ bool itemIsParentOf(uint16 a, uint16 b);
+ bool vc_maybe_skip_proc_1(uint16 a, int16 b);
+
+ void add_vga_timer(uint num, const byte *code_ptr, uint cur_sprite, uint cur_file);
+ VgaSprite *findCurSprite();
+
+ bool vcGetBit(uint bit);
+ void vcSetBitTo(uint bit, bool value);
+
+ void expire_vga_timers();
+
+ bool isSpriteLoaded(uint16 id, uint16 fileId);
+
+ void fcs_setTextColor(FillOrCopyStruct *fcs, uint value);
+
+ void video_copy_if_flag_0x8_c(FillOrCopyStruct *fcs);
+ void delete_hitarea_by_index(uint index);
+
+ void removeArrows(FillOrCopyStruct *fcs, uint fcs_index);
+ void fcs_putchar(uint a);
+
+ void copy_img_from_3_to_2(FillOrCopyStruct *fcs);
+ void video_erase(FillOrCopyStruct *fcs);
+
+ void dx_copy_rgn_from_3_to_2(uint b, uint r, uint y, uint x);
+
+ byte *dx_lock_2();
+ void dx_unlock_2();
+
+ byte *dx_lock_attached();
+ void dx_unlock_attached();
+
+ byte *read_vga_from_datfile_2(uint id, uint type);
+
+ void resfile_read(void *dst, uint32 offs, uint32 size);
+
+ int init(GameDetector &detector);
+ int go();
+ void openGameFile();
+
+ void timer_callback();
+ void timer_proc1();
+
+ void timer_vga_sprites();
+ void timer_vga_sprites_2();
+
+ void dx_clear_surfaces(uint num_lines);
+ void dx_update_screen_and_palette();
+
+ void dump_video_script(const byte *src, bool one_opcode_only);
+ void dump_vga_file(const byte *vga);
+ void dump_vga_script(const byte *ptr, uint res, uint sprite_id);
+ void dump_vga_script_always(const byte *ptr, uint res, uint sprite_id);
+ void dump_vga_bitmaps(const byte *vga, byte *vga1, int res);
+ void dump_single_bitmap(int file, int image, const byte *offs, int w, int h, byte base);
+
+ void dx_clear_attached_from_top(uint lines);
+ void dx_copy_from_attached_to_2(uint x, uint y, uint w, uint h);
+ void dx_copy_from_attached_to_3(uint lines);
+ void dx_copy_from_2_to_attached(uint x, uint y, uint w, uint h);
+
+ void print_char_helper_1(const byte *src, uint len);
+ void print_char_helper_5(FillOrCopyStruct *fcs);
+
+ void quickLoadOrSave();
+ void shutdown();
+
+ byte *vc10_uncompressFlip(const byte *src, uint w, uint h);
+ byte *vc10_flip(const byte *src, uint w, uint h);
+
+ Item *getNextItemPtrStrange();
+
+ bool saveGame(uint slot, char *caption);
+ bool loadGame(uint slot);
+
+ void showmessage_helper_2();
+ void print_char_helper_6(uint i);
+
+ void video_putchar_newline(FillOrCopyStruct *fcs);
+ void video_putchar_drawchar(FillOrCopyStruct *fcs, uint x, uint y, byte chr);
+
+ void loadMusic(uint music);
+ void checkTimerCallback();
+ void delay(uint delay);
+ void pause();
+
+ void o_83_helper();
+ void o_waitForMark(uint i);
+ void timer_vga_sprites_helper();
+
+ void decodeStripA(byte *dst, const byte *src, int height);
+ void scroll_timeout();
+ void hitarea_stuff_helper_2();
+ void fastFadeIn();
+ void slowFadeIn();
+
+ void vc_kill_sprite(uint file, uint sprite);
+
+ void set_dummy_cursor();
+
+ void set_volume(int volume);
+
+ void saveOrLoadDialog(bool load);
+ void o_unk_132_helper_3();
+ int o_unk_132_helper(bool *b, char *buf);
+ void o_clearCharacter(FillOrCopyStruct *fcs, int x, byte b = 0);
+ void saveGameDialog(char *buf);
+ void o_fileError(FillOrCopyStruct *fcs, bool save_error);
+
+ int countSaveGames();
+ int displaySaveGameList(int curpos, bool load, char *dst);
+
+ void show_it(void *buf);
+
+ char *gen_savename(int slot);
+};
+
+} // End of namespace Simon
+
+#endif
diff --git a/engines/simon/sound.cpp b/engines/simon/sound.cpp
new file mode 100644
index 0000000000..f40a164a12
--- /dev/null
+++ b/engines/simon/sound.cpp
@@ -0,0 +1,555 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/file.h"
+#include "common/util.h"
+
+#include "simon/simon.h"
+#include "simon/sound.h"
+
+#include "sound/flac.h"
+#include "sound/mp3.h"
+#include "sound/voc.h"
+#include "sound/vorbis.h"
+#include "sound/wave.h"
+
+using Common::File;
+
+namespace Simon {
+
+#define SOUND_BIG_ENDIAN true
+
+class BaseSound {
+protected:
+ File *_file;
+ uint32 *_offsets;
+ Audio::Mixer *_mixer;
+ bool _freeOffsets;
+
+public:
+ BaseSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false);
+ BaseSound(Audio::Mixer *mixer, File *file, uint32 *offsets, bool bigendian = false);
+ virtual ~BaseSound();
+ virtual void playSound(uint sound, Audio::SoundHandle *handle, byte flags) = 0;
+};
+
+class WavSound : public BaseSound {
+public:
+ WavSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false) : BaseSound(mixer, file, base, bigendian) {};
+ WavSound(Audio::Mixer *mixer, File *file, uint32 *offsets) : BaseSound(mixer, file, offsets) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+class VocSound : public BaseSound {
+public:
+ VocSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false) : BaseSound(mixer, file, base, bigendian) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+class RawSound : public BaseSound {
+public:
+ RawSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false) : BaseSound(mixer, file, base, bigendian) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 base, bool bigendian) {
+ _mixer = mixer;
+ _file = file;
+
+ uint res = 0;
+ uint32 size;
+
+ _file->seek(base + sizeof(uint32), SEEK_SET);
+ if (bigendian)
+ size = _file->readUint32BE();
+ else
+ size = _file->readUint32LE();
+
+ // The Feeble Files uses set amount of voice offsets
+ if (size == 0)
+ size = 40000;
+
+ res = size / sizeof(uint32);
+
+ _offsets = (uint32 *)malloc(size + sizeof(uint32));
+ _freeOffsets = true;
+
+ _file->seek(base, SEEK_SET);
+
+ if (_file->read(_offsets, size) != size)
+ error("Can't read offsets");
+
+ for (uint i = 0; i < res; i++) {
+#if defined(SCUMM_BIG_ENDIAN)
+ if (!(bigendian))
+ _offsets[i] = FROM_LE_32(_offsets[i]);
+#endif
+ if (bigendian)
+ _offsets[i] = TO_BE_32(_offsets[i]);
+ _offsets[i] += base;
+ }
+
+ // only needed for mp3
+ _file->seek(0, SEEK_END);
+ _offsets[res] = _file->pos();
+}
+
+BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 *offsets, bool bigendian) {
+ _mixer = mixer;
+ _file = file;
+ _offsets = offsets;
+ _freeOffsets = false;
+}
+
+BaseSound::~BaseSound() {
+ if (_freeOffsets)
+ free(_offsets);
+ delete _file;
+}
+
+void WavSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) {
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ byte wavFlags;
+ int size, rate;
+ if (!loadWAVFromStream(*_file, size, rate, wavFlags)) {
+ error("playSound: Not a valid WAV file");
+ }
+
+ byte *buffer = (byte *)malloc(size);
+ _file->read(buffer, size);
+ _mixer->playRaw(handle, buffer, size, rate, flags | wavFlags);
+}
+
+void VocSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) {
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ int size, rate;
+ byte *buffer = loadVOCFromStream(*_file, size, rate);
+
+ _mixer->playRaw(handle, buffer, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE);
+}
+
+void RawSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) {
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ uint size = _file->readUint32BE();
+ byte *buffer = (byte *)malloc(size);
+ _file->read(buffer, size);
+
+ _mixer->playRaw(handle, buffer, size, 22050, flags | Audio::Mixer::FLAG_AUTOFREE);
+}
+
+#ifdef USE_MAD
+class MP3Sound : public BaseSound {
+public:
+ MP3Sound(Audio::Mixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+void MP3Sound::playSound(uint sound, Audio::SoundHandle *handle, byte flags)
+{
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ int i = 1;
+ while (_offsets[sound + i] == _offsets[sound])
+ i++;
+
+ uint32 size = _offsets[sound + i] - _offsets[sound];
+
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, makeMP3Stream(_file, size));
+}
+#endif
+
+#ifdef USE_VORBIS
+class VorbisSound : public BaseSound {
+public:
+ VorbisSound(Audio::Mixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+void VorbisSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags)
+{
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ int i = 1;
+ while (_offsets[sound + i] == _offsets[sound])
+ i++;
+
+ uint32 size = _offsets[sound + i] - _offsets[sound];
+
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, makeVorbisStream(_file, size));
+}
+#endif
+
+#ifdef USE_FLAC
+class FlacSound : public BaseSound {
+public:
+ FlacSound(Audio::Mixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+void FlacSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags)
+{
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ int i = 1;
+ while (_offsets[sound + i] == _offsets[sound])
+ i++;
+
+ uint32 size = _offsets[sound + i] - _offsets[sound];
+
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, makeFlacStream(_file, size));
+}
+#endif
+
+Sound::Sound(SimonEngine *vm, const GameSpecificSettings *gss, Audio::Mixer *mixer)
+ : _vm(vm), _mixer(mixer) {
+ _voice = 0;
+ _effects = 0;
+
+ _effectsPaused = false;
+ _ambientPaused = false;
+
+ _filenums = 0;
+ _lastVoiceFile = 0;
+ _offsets = 0;
+
+ _hasEffectsFile = false;
+ _hasVoiceFile = false;
+ _ambientPlaying = 0;
+
+ // SIMON1CD32 uses separate speech files
+ if (!(_vm->getFeatures() & GF_TALKIE) || (_vm->getGameId() == GID_SIMON1CD32))
+ return;
+
+ File *file = new File();
+
+#ifdef USE_FLAC
+ if (!_hasVoiceFile && gss->flac_filename && gss->flac_filename[0]) {
+ file->open(gss->flac_filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new FlacSound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_MAD
+ if (!_hasVoiceFile && gss->mp3_filename && gss->mp3_filename[0]) {
+ file->open(gss->mp3_filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new MP3Sound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_VORBIS
+ if (!_hasVoiceFile && gss->vorbis_filename && gss->vorbis_filename[0]) {
+ file->open(gss->vorbis_filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new VorbisSound(_mixer, file);
+ }
+ }
+#endif
+ if (!_hasVoiceFile && _vm->getGameType() == GType_SIMON2) {
+ // for simon2 mac/amiga, only read index file
+ file->open("voices.idx");
+ if (file->isOpen() == true) {
+ file->seek(0, SEEK_END);
+ int end = file->pos();
+ file->seek(0, SEEK_SET);
+ _filenums = (uint16 *)malloc((end / 6 + 1) * 2);
+ _offsets = (uint32 *)malloc((end / 6 + 1) * 4);
+
+ for (int i = 1; i <= end / 6; i++) {
+ _filenums[i] = file->readUint16BE();
+ _offsets[i] = file->readUint32BE();
+ }
+ _hasVoiceFile = true;
+ }
+ }
+ if (!_hasVoiceFile && gss->wav_filename && gss->wav_filename[0]) {
+ file->open(gss->wav_filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new WavSound(_mixer, file);
+ }
+ }
+ if (!_hasVoiceFile && gss->voc_filename && gss->voc_filename[0]) {
+ file->open(gss->voc_filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new VocSound(_mixer, file);
+ }
+ }
+
+ if ((_vm->getGameType() == GType_SIMON1) && (_vm->getFeatures() & GF_TALKIE)) {
+ file = new File();
+#ifdef USE_MAD
+ if (!_hasEffectsFile && gss->mp3_effects_filename && gss->mp3_effects_filename[0]) {
+ file->open(gss->mp3_effects_filename);
+ if (file->isOpen()) {
+ _hasEffectsFile = true;
+ _effects = new MP3Sound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_VORBIS
+ if (!_hasEffectsFile && gss->vorbis_effects_filename && gss->vorbis_effects_filename[0]) {
+ file->open(gss->vorbis_effects_filename);
+ if (file->isOpen()) {
+ _hasEffectsFile = true;
+ _effects = new VorbisSound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_FLAC
+ if (!_hasEffectsFile && gss->flac_effects_filename && gss->flac_effects_filename[0]) {
+ file->open(gss->flac_effects_filename);
+ if (file->isOpen()) {
+ _hasEffectsFile = true;
+ _effects = new FlacSound(_mixer, file);
+ }
+ }
+#endif
+ if (!_hasEffectsFile && gss->voc_effects_filename && gss->voc_effects_filename[0]) {
+ file->open(gss->voc_effects_filename);
+ if (file->isOpen()) {
+ _hasEffectsFile = true;
+ _effects = new VocSound(_mixer, file);
+ }
+ }
+ }
+}
+
+Sound::~Sound() {
+ delete _voice;
+ delete _effects;
+
+ free(_filenums);
+ free(_offsets);
+}
+
+void Sound::readSfxFile(const char *filename) {
+ if (_hasEffectsFile)
+ return;
+
+ stopAll();
+
+ File *file = new File();
+ file->open(filename);
+
+ if (file->isOpen() == false) {
+ char *filename2;
+ filename2 = (char *)malloc(strlen(filename) + 2);
+ strcpy(filename2, filename);
+ strcat(filename2, ".");
+ file->open(filename2);
+ free(filename2);
+ if (file->isOpen() == false) {
+ if (atoi(filename + 6) != 1 && atoi(filename + 6) != 30)
+ warning("readSfxFile: Can't load sfx file %s", filename);
+ return;
+ }
+ }
+
+ delete _effects;
+ if (_vm->getGameId() == GID_SIMON1CD32) {
+ _effects = new VocSound(_mixer, file, 0, SOUND_BIG_ENDIAN);
+ } else
+ _effects = new WavSound(_mixer, file);
+}
+
+void Sound::loadSfxTable(File *gameFile, uint32 base) {
+ stopAll();
+
+ if (_vm->getPlatform() == Common::kPlatformWindows)
+ _effects = new WavSound(_mixer, gameFile, base);
+ else
+ _effects = new VocSound(_mixer, gameFile, base);
+}
+
+void Sound::readVoiceFile(const char *filename) {
+ stopAll();
+
+ File *file = new File();
+ file->open(filename);
+
+ if (file->isOpen() == false) {
+ char *filename2;
+ filename2 = (char *)malloc(strlen(filename) + 2);
+ strcpy(filename2, filename);
+ strcat(filename2, ".");
+ file->open(filename2);
+ free(filename2);
+ if (file->isOpen() == false) {
+ warning("readVoiceFile: Can't load voice file %s", filename);
+ return;
+ }
+ }
+
+ delete _voice;
+ _voice = new RawSound(_mixer, file, 0, SOUND_BIG_ENDIAN);
+}
+
+void Sound::playVoice(uint sound) {
+ if (_filenums) {
+ if (_lastVoiceFile != _filenums[sound]) {
+ stopAll();
+
+ char filename[16];
+ _lastVoiceFile = _filenums[sound];
+ sprintf(filename, "voices%d.dat", _filenums[sound]);
+ File *file = new File();
+ file->open(filename);
+ if (file->isOpen() == false) {
+ warning("playVoice: Can't load voice file %s", filename);
+ return;
+ }
+ delete _voice;
+ _voice = new WavSound(_mixer, file, _offsets);
+ }
+ }
+
+ if (!_voice)
+ return;
+
+ _mixer->stopHandle(_voiceHandle);
+ if (_vm->getGameType() == GType_FF || _vm->getGameId() == GID_SIMON1CD32) {
+ _voice->playSound(sound, &_voiceHandle, 0);
+ } else {
+ _voice->playSound(sound, &_voiceHandle, Audio::Mixer::FLAG_UNSIGNED);
+ }
+}
+
+void Sound::playSoundData(byte *soundData, uint sound, uint pan, uint vol, bool ambient) {
+ byte flags;
+ int rate;
+
+ if (ambient == true) {
+ if (sound == _ambientPlaying)
+ return;
+
+ _ambientPlaying = sound;
+
+ if (_ambientPaused)
+ return;
+ } else {
+ if (_effectsPaused)
+ return;
+ }
+
+ soundData += READ_LE_UINT32(soundData + sound * 4);
+ int size = READ_LE_UINT32(soundData + 4);
+ Common::MemoryReadStream stream(soundData, size);
+ if (!loadWAVFromStream(stream, size, rate, flags)) {
+ error("playSoundData: Not a valid WAV data");
+ }
+
+ byte *buffer = (byte *)malloc(size);
+ memcpy(buffer, soundData + stream.pos(), size);
+
+ if (ambient == true) {
+ _mixer->stopHandle(_ambientHandle);
+ _mixer->playRaw(&_ambientHandle, buffer, size, rate, Audio::Mixer::FLAG_LOOP|flags);
+ } else {
+ _mixer->playRaw(&_effectsHandle, buffer, size, rate, flags);
+ }
+}
+
+void Sound::playEffects(uint sound) {
+ if (!_effects)
+ return;
+
+ if (_effectsPaused)
+ return;
+
+ _effects->playSound(sound, &_effectsHandle, (_vm->getGameId() == GID_SIMON1CD32) ? 0 : Audio::Mixer::FLAG_UNSIGNED);
+}
+
+void Sound::playAmbient(uint sound) {
+ if (!_effects)
+ return;
+
+ if (sound == _ambientPlaying)
+ return;
+
+ _ambientPlaying = sound;
+
+ if (_ambientPaused)
+ return;
+
+ _mixer->stopHandle(_ambientHandle);
+ _effects->playSound(sound, &_ambientHandle, Audio::Mixer::FLAG_LOOP|Audio::Mixer::FLAG_UNSIGNED);
+}
+
+bool Sound::hasVoice() const {
+ return _hasVoiceFile;
+}
+
+bool Sound::isVoiceActive() const {
+ return _mixer->isSoundHandleActive(_voiceHandle) ;
+}
+
+void Sound::stopVoice() {
+ _mixer->stopHandle(_voiceHandle);
+}
+
+void Sound::stopAll() {
+ _mixer->stopAll();
+ _ambientPlaying = 0;
+}
+
+void Sound::effectsPause(bool b) {
+ _effectsPaused = b;
+}
+
+void Sound::ambientPause(bool b) {
+ _ambientPaused = b;
+
+ if (_ambientPaused && _ambientPlaying) {
+ _mixer->stopHandle(_ambientHandle);
+ } else if (_ambientPlaying) {
+ uint tmp = _ambientPlaying;
+ _ambientPlaying = 0;
+ playAmbient(tmp);
+ }
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/sound.h b/engines/simon/sound.h
new file mode 100644
index 0000000000..245c6ffb0c
--- /dev/null
+++ b/engines/simon/sound.h
@@ -0,0 +1,80 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SIMON_SOUND_H
+#define SIMON_SOUND_H
+
+#include "sound/mixer.h"
+#include "simon/intern.h"
+#include "common/str.h"
+
+namespace Simon {
+
+class BaseSound;
+
+class SimonEngine;
+
+class Sound {
+private:
+ SimonEngine *_vm;
+
+ Audio::Mixer *_mixer;
+
+ BaseSound *_voice;
+ BaseSound *_effects;
+
+ bool _effectsPaused;
+ bool _ambientPaused;
+
+ uint16 *_filenums;
+ uint32 *_offsets;
+ uint16 _lastVoiceFile;
+
+ Audio::SoundHandle _voiceHandle;
+ Audio::SoundHandle _effectsHandle;
+ Audio::SoundHandle _ambientHandle;
+
+ bool _hasEffectsFile;
+ bool _hasVoiceFile;
+ uint _ambientPlaying;
+
+public:
+ Sound(SimonEngine *vm, const GameSpecificSettings *gss, Audio::Mixer *mixer);
+ ~Sound();
+
+ void readSfxFile(const char *filename);
+ void loadSfxTable(Common::File *gameFile, uint32 base);
+ void readVoiceFile(const char *filename);
+
+ void playVoice(uint sound);
+ void playEffects(uint sound);
+ void playAmbient(uint sound);
+ void playSoundData(byte *soundData, uint sound, uint pan, uint vol, bool ambient);
+
+ bool hasVoice() const;
+ bool isVoiceActive() const;
+ void stopVoice();
+ void stopAll();
+ void effectsPause(bool b);
+ void ambientPause(bool b);
+};
+
+} // End of namespace Simon
+
+#endif
diff --git a/engines/simon/verb.cpp b/engines/simon/verb.cpp
new file mode 100644
index 0000000000..65f105f1f9
--- /dev/null
+++ b/engines/simon/verb.cpp
@@ -0,0 +1,608 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Verb and hitarea handling
+#include "common/stdafx.h"
+#include "simon/simon.h"
+#include "simon/intern.h"
+
+namespace Simon {
+
+static const char *const russian_verb_names[] = {
+ "Ietj _",
+ "Qnotrft< pa",
+ "Nt_r[t<",
+ "Ecjdat<",
+
+ "Q=fst<",
+ "C^]t<",
+ "Ha_r[t<",
+ "Isqom<^ocat<",
+
+ "Docorjt<",
+ "Qp]t<",
+ "Neft<",
+ "Eat<"
+};
+
+static const char *const hebrew_verb_names[] = {
+ "LJ @L",
+ "DQZKL RL",
+ "TZG",
+ "DFF",
+
+ "@KEL",
+ "DXM",
+ "QBEX",
+ "DYZNY",
+
+ "CAX @L",
+ "DQX",
+ "LAY",
+ "ZO"
+};
+
+static const char *const spanish_verb_names[] = {
+ "Caminar",
+ "Mirar",
+ "Abrir",
+ "Mover",
+
+ "Consumir",
+ "Coger",
+ "Cerrar",
+ "Usar",
+
+ "Hablar",
+ "Quitar",
+ "Llevar",
+ "Dar"
+};
+
+static const char *const italian_verb_names[] = {
+ "Vai verso",
+ "Osserva",
+ "Apri",
+ "Sposta",
+
+ "Mangia",
+ "Raccogli",
+ "Chiudi",
+ "Usa",
+
+ "Parla a",
+ "Togli",
+ "Indossa",
+ "Dai"
+};
+
+static const char *const french_verb_names[] = {
+ "Aller vers",
+ "Regarder",
+ "Ouvrir",
+ "D/placer",
+
+ "Consommer",
+ "Prendre",
+ "Fermer",
+ "Utiliser",
+
+ "Parler ;",
+ "Enlever",
+ "Mettre",
+ "Donner"
+};
+
+static const char *const german_verb_names[] = {
+ "Gehe zu",
+ "Schau an",
+ ";ffne",
+ "Bewege",
+
+ "Verzehre",
+ "Nimm",
+ "Schlie+e",
+ "Benutze",
+
+ "Rede mit",
+ "Entferne",
+ "Trage",
+ "Gib"
+};
+
+static const char *const english_verb_names[] = {
+ "Walk to",
+ "Look at",
+ "Open",
+ "Move",
+
+ "Consume",
+ "Pick up",
+ "Close",
+ "Use",
+
+ "Talk to",
+ "Remove",
+ "Wear",
+ "Give"
+};
+
+static const char *const russian_verb_prep_names[] = {
+ "", "", "", "",
+ "", "", "", "s yfn?",
+ "", "", "", "_onu ?"
+};
+
+static const char *const hebrew_verb_prep_names[] = {
+ "", "", "", "",
+ "", "", "", "RM ND ?",
+ "", "", "", "LNI ?"
+};
+
+static const char *const spanish_verb_prep_names[] = {
+ "", "", "", "",
+ "", "", "", "^con qu/?",
+ "", "", "", "^a qui/n?"
+};
+
+static const char *const italian_verb_prep_names[] = {
+ "", "", "", "",
+ "", "", "", "con cosa ?",
+ "", "", "", "a chi ?"
+};
+
+static const char *const french_verb_prep_names[] = {
+ "", "", "", "",
+ "", "", "", "avec quoi ?",
+ "", "", "", "; qui ?"
+};
+
+static const char *const german_verb_prep_names[] = {
+ "", "", "", "",
+ "", "", "", "mit was ?",
+ "", "", "", "zu wem ?"
+};
+
+static const char *const english_verb_prep_names[] = {
+ "", "", "", "",
+ "", "", "", "with what ?",
+ "", "", "", "to whom ?"
+};
+
+void SimonEngine::defocusHitarea() {
+ HitArea *last;
+ HitArea *ha;
+
+ if (getGameType() == GType_SIMON2) {
+ if (_bitArray[4] & 0x8000) {
+ o_sync(202);
+ _lastHitArea2Ptr = NULL;
+ return;
+ }
+ }
+
+ last = _hitAreaPtr5;
+
+ if (last == _hitAreaPtr7)
+ return;
+
+ hitareaChangedHelper();
+ _hitAreaPtr7 = last;
+
+ if (last != NULL && (ha = findHitAreaByID(200)) && (ha->flags & 0x40) && !(last->flags & 0x40))
+ focusVerb(last->id);
+}
+
+void SimonEngine::focusVerb(uint hitarea_id) {
+ uint x;
+ const char *txt;
+ const char * const *verb_names;
+ const char * const *verb_prep_names;
+
+ hitarea_id -= 101;
+
+ if (_showPreposition) {
+ switch (_language) {
+ case Common::RU_RUS:
+ verb_prep_names = russian_verb_prep_names;
+ break;
+ case Common::HB_ISR:
+ verb_prep_names = hebrew_verb_prep_names;
+ break;
+ case Common::ES_ESP:
+ verb_prep_names = spanish_verb_prep_names;
+ break;
+ case Common::IT_ITA:
+ verb_prep_names = italian_verb_prep_names;
+ break;
+ case Common::FR_FRA:
+ verb_prep_names = french_verb_prep_names;
+ break;
+ case Common::DE_DEU:
+ verb_prep_names = german_verb_prep_names;
+ break;
+ default:
+ verb_prep_names = english_verb_prep_names;
+ break;
+ }
+ CHECK_BOUNDS(hitarea_id, english_verb_prep_names);
+ txt = verb_prep_names[hitarea_id];
+ } else {
+ switch (_language) {
+ case Common::RU_RUS:
+ verb_names = russian_verb_names;
+ break;
+ case Common::HB_ISR:
+ verb_names = hebrew_verb_names;
+ break;
+ case Common::ES_ESP:
+ verb_names = spanish_verb_names;
+ break;
+ case Common::IT_ITA:
+ verb_names = italian_verb_names;
+ break;
+ case Common::FR_FRA:
+ verb_names = french_verb_names;
+ break;
+ case Common::DE_DEU:
+ verb_names = german_verb_names;
+ break;
+ default:
+ verb_names = english_verb_names;
+ break;
+ }
+ CHECK_BOUNDS(hitarea_id, english_verb_names);
+ txt = verb_names[hitarea_id];
+ }
+ x = (53 - strlen(txt)) * 3;
+ showActionString(x, (const byte *)txt);
+}
+
+void SimonEngine::showActionString(uint x, const byte *string) {
+ FillOrCopyStruct *fcs;
+
+ fcs = _windowArray[1];
+ if (fcs == NULL || fcs->text_color == 0)
+ return;
+
+ fcs->textColumn = x >> 3;
+ fcs->textColumnOffset = x & 7;
+
+ for (; *string; string++)
+ video_putchar(fcs, *string);
+}
+
+void SimonEngine::hitareaChangedHelper() {
+ FillOrCopyStruct *fcs;
+
+ if (getGameType() == GType_SIMON2) {
+ if (_bitArray[4] & 0x8000)
+ return;
+ }
+
+ fcs = _windowArray[1];
+ if (fcs != NULL && fcs->text_color != 0)
+ video_fill_or_copy_from_3_to_2(fcs);
+
+ _lastHitArea2Ptr = NULL;
+ _hitAreaPtr7 = NULL;
+}
+
+HitArea *SimonEngine::findHitAreaByID(uint hitarea_id) {
+ HitArea *ha = _hitAreas;
+ uint count = ARRAYSIZE(_hitAreas);
+
+ do {
+ if (ha->id == hitarea_id)
+ return ha;
+ } while (ha++, --count);
+ return NULL;
+}
+
+HitArea *SimonEngine::findEmptyHitArea() {
+ HitArea *ha = _hitAreas;
+ uint count = ARRAYSIZE(_hitAreas);
+
+ do {
+ if (ha->flags == 0)
+ return ha;
+ } while (ha++, --count);
+ return NULL;
+}
+
+void SimonEngine::clear_hitarea_bit_0x40(uint hitarea) {
+ HitArea *ha = findHitAreaByID(hitarea);
+ if (ha != NULL)
+ ha->flags &= ~0x40;
+}
+
+void SimonEngine::set_hitarea_bit_0x40(uint hitarea) {
+ HitArea *ha = findHitAreaByID(hitarea);
+ if (ha != NULL) {
+ ha->flags |= 0x40;
+ ha->flags &= ~2;
+ if (hitarea == 102)
+ hitarea_proc_1();
+ }
+}
+
+void SimonEngine::set_hitarea_x_y(uint hitarea, int x, int y) {
+ HitArea *ha = findHitAreaByID(hitarea);
+ if (ha != NULL) {
+ ha->x = x;
+ ha->y = y;
+ }
+}
+
+void SimonEngine::delete_hitarea(uint hitarea) {
+ HitArea *ha = findHitAreaByID(hitarea);
+ if (ha != NULL) {
+ ha->flags = 0;
+ if (ha == _lastHitArea2Ptr)
+ defocusHitarea();
+ _needHitAreaRecalc++;
+ }
+}
+
+bool SimonEngine::is_hitarea_0x40_clear(uint hitarea) {
+ HitArea *ha = findHitAreaByID(hitarea);
+ if (ha == NULL)
+ return false;
+ return (ha->flags & 0x40) == 0;
+}
+
+void SimonEngine::addNewHitArea(int id, int x, int y, int width, int height, int flags, int unk3, Item *item_ptr) {
+ HitArea *ha;
+ delete_hitarea(id);
+
+ ha = findEmptyHitArea();
+ ha->x = x;
+ ha->y = y;
+ ha->width = width;
+ ha->height = height;
+ ha->flags = flags | 0x20;
+ ha->id = ha->layer = id;
+ ha->unk3 = unk3;
+ ha->item_ptr = item_ptr;
+
+ _needHitAreaRecalc++;
+}
+
+void SimonEngine::hitarea_proc_1() {
+ uint id;
+ HitArea *ha;
+
+ if (getGameType() == GType_SIMON2) {
+ id = 2;
+ if (!(_bitArray[4] & 0x8000))
+ id = (_mouseY >= 136) ? 102 : 101;
+ } else {
+ id = (_mouseY >= 136) ? 102 : 101;
+ }
+
+ _hitAreaUnk4 = id;
+
+ ha = findHitAreaByID(id);
+ if (ha == NULL)
+ return;
+
+ if (ha->flags & 0x40) {
+ _hitAreaUnk4 = 999;
+ _hitAreaPtr5 = NULL;
+ } else {
+ _verbHitArea = ha->unk3;
+ handle_verb_hitarea(ha);
+ }
+}
+
+void SimonEngine::handle_verb_hitarea(HitArea *ha) {
+ HitArea *tmp = _hitAreaPtr5;
+
+ if (ha == tmp)
+ return;
+
+ if (!(getGameType() == GType_SIMON2)) {
+ if (tmp != NULL) {
+ tmp->flags |= 8;
+ video_toggle_colors(tmp, 0xd5, 0xd0, 0xd5, 0xA);
+ }
+
+ if (ha->flags & 2)
+ video_toggle_colors(ha, 0xda, 0xd5, 0xd5, 5);
+ else
+ video_toggle_colors(ha, 0xdf, 0xda, 0xda, 0xA);
+
+ ha->flags &= ~(2 + 8);
+
+ } else {
+ if (ha->id < 101)
+ return;
+ _mouseCursor = ha->id - 101;
+ _needHitAreaRecalc++;
+ }
+ _hitAreaPtr5 = ha;
+}
+
+void SimonEngine::hitarea_leave(HitArea *ha) {
+ if (!(getGameType() == GType_SIMON2)) {
+ video_toggle_colors(ha, 0xdf, 0xd5, 0xda, 5);
+ } else {
+ video_toggle_colors(ha, 0xe7, 0xe5, 0xe6, 1);
+ }
+}
+
+void SimonEngine::leaveHitAreaById(uint hitarea_id) {
+ HitArea *ha = findHitAreaByID(hitarea_id);
+ if (ha)
+ hitarea_leave(ha);
+}
+
+void SimonEngine::handle_uparrow_hitarea(FillOrCopyStruct *fcs) {
+ uint index;
+
+ index = get_fcs_ptr_3_index(fcs);
+
+ if (fcs->fcs_data->unk1 == 0)
+ return;
+
+ mouseOff();
+ drawIconArray(index, fcs->fcs_data->item_ptr, fcs->fcs_data->unk1 - 1, fcs->fcs_data->unk2);
+ mouseOn();
+}
+
+void SimonEngine::handle_downarrow_hitarea(FillOrCopyStruct *fcs) {
+ uint index;
+
+ index = get_fcs_ptr_3_index(fcs);
+
+ mouseOff();
+ drawIconArray(index, fcs->fcs_data->item_ptr, fcs->fcs_data->unk1 + 1, fcs->fcs_data->unk2);
+ mouseOn();
+}
+
+void SimonEngine::setup_hitarea_from_pos(uint x, uint y, uint mode) {
+ HitArea *best_ha;
+ HitArea *ha = _hitAreas;
+ uint count = ARRAYSIZE(_hitAreas);
+ uint16 layer = 0;
+ uint16 x_ = x;
+ const uint16 y_ = y;
+
+ if (getGameType() == GType_SIMON2) {
+ if (_bitArray[4] & 0x8000 || y < 134) {
+ x_ += _scrollX * 8;
+ }
+ }
+
+ best_ha = NULL;
+
+ do {
+ if (ha->flags & 0x20) {
+ if (!(ha->flags & 0x40)) {
+ if (x_ >= ha->x && y_ >= ha->y &&
+ x_ - ha->x < ha->width && y_ - ha->y < ha->height && layer <= ha->layer) {
+ layer = ha->layer;
+ best_ha = ha;
+ } else {
+ if (ha->flags & 2) {
+ hitarea_leave(ha);
+ ha->flags &= ~2;
+ }
+ }
+ } else {
+ ha->flags &= ~2;
+ }
+ }
+ } while (ha++, --count);
+
+ if (best_ha == NULL) {
+ defocusHitarea();
+ return;
+ }
+
+ if (mode != 0 && mode != 3) {
+ _lastHitArea = best_ha;
+ _variableArray[1] = x;
+ _variableArray[2] = y;
+ }
+
+ if (best_ha->flags & 4) {
+ defocusHitarea();
+ } else if (best_ha != _lastHitArea2Ptr) {
+ new_current_hitarea(best_ha);
+ }
+
+ if (best_ha->flags & 8 && !(best_ha->flags & 2)) {
+ hitarea_leave(best_ha);
+ best_ha->flags |= 2;
+ }
+
+ return;
+}
+
+void SimonEngine::new_current_hitarea(HitArea *ha) {
+ bool result;
+
+ hitareaChangedHelper();
+ if (ha->flags & 1) {
+ result = hitarea_proc_2(ha->flags >> 8);
+ } else {
+ result = hitarea_proc_3(ha->item_ptr);
+ }
+
+ if (result)
+ _lastHitArea2Ptr = ha;
+}
+
+bool SimonEngine::hitarea_proc_2(uint a) {
+ uint x;
+ const byte *string_ptr;
+
+ if (getGameType() == GType_SIMON2) {
+ if (_bitArray[4] & 0x8000) {
+ Subroutine *sub;
+ _variableArray[84] = a;
+ sub = getSubroutineByID(5003);
+ if (sub != NULL)
+ startSubroutineEx(sub);
+ return true;
+ }
+ }
+
+ if (a >= 20)
+ return false;
+
+ string_ptr = getStringPtrByID(_stringIdArray2[a]);
+ // Arisme : hack for long strings in the French version
+ if ((strlen((const char*)string_ptr) - 1) <= 53)
+ x = (53 - (strlen((const char *)string_ptr) - 1)) * 3;
+ else
+ x = 0;
+ showActionString(x, string_ptr);
+
+ return true;
+}
+
+bool SimonEngine::hitarea_proc_3(Item *item) {
+ Child2 *child2;
+ uint x;
+ const byte *string_ptr;
+
+ if (item == 0 || item == _dummyItem2 || item == _dummyItem3)
+ return false;
+
+ child2 = (Child2 *)findChildOfType(item, 2);
+ if (child2 == NULL)
+ return false;
+
+ string_ptr = getStringPtrByID(child2->string_id);
+ // Arisme : hack for long strings in the French version
+ if ((strlen((const char*)string_ptr) - 1) <= 53)
+ x = (53 - (strlen((const char *)string_ptr) - 1)) * 3;
+ else
+ x = 0;
+ showActionString(x, string_ptr);
+
+ return true;
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/vga.cpp b/engines/simon/vga.cpp
new file mode 100644
index 0000000000..d7d0731539
--- /dev/null
+++ b/engines/simon/vga.cpp
@@ -0,0 +1,2299 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// Video script opcodes for Simon1/Simon2
+#include "common/stdafx.h"
+#include "simon/simon.h"
+#include "simon/intern.h"
+#include "simon/vga.h"
+
+#include "common/system.h"
+
+namespace Simon {
+
+// Opcode tables
+void SimonEngine::setupVgaOpcodes() {
+ static const VgaOpcodeProc vga_opcode_table[] = {
+ NULL,
+ &SimonEngine::vc1_fadeOut,
+ &SimonEngine::vc2_call,
+ &SimonEngine::vc3_loadSprite,
+ &SimonEngine::vc4_fadeIn,
+ &SimonEngine::vc5_skip_if_neq,
+ &SimonEngine::vc6_skip_ifn_sib_with_a,
+ &SimonEngine::vc7_skip_if_sib_with_a,
+ &SimonEngine::vc8_skip_if_parent_is,
+ &SimonEngine::vc9_skip_if_unk3_is,
+ &SimonEngine::vc10_draw,
+ &SimonEngine::vc11_clearPathFinder,
+ &SimonEngine::vc12_delay,
+ &SimonEngine::vc13_addToSpriteX,
+ &SimonEngine::vc14_addToSpriteY,
+ &SimonEngine::vc15_wakeup_id,
+ &SimonEngine::vc16_sleep_on_id,
+ &SimonEngine::vc17_setPathfinderItem,
+ &SimonEngine::vc18_jump,
+ &SimonEngine::vc19_chain_to_script,
+ &SimonEngine::vc20_setRepeat,
+ &SimonEngine::vc21_endRepeat,
+ &SimonEngine::vc22_setSpritePalette,
+ &SimonEngine::vc23_setSpritePriority,
+ &SimonEngine::vc24_setSpriteXY,
+ &SimonEngine::vc25_halt_sprite,
+ &SimonEngine::vc26_setSubWindow,
+ &SimonEngine::vc27_resetSprite,
+ &SimonEngine::vc28_dummy_op,
+ &SimonEngine::vc29_stopAllSounds,
+ &SimonEngine::vc30_setFrameRate,
+ &SimonEngine::vc31_setWindow,
+ &SimonEngine::vc32_copyVar,
+ &SimonEngine::vc33_setMouseOn,
+ &SimonEngine::vc34_setMouseOff,
+ &SimonEngine::vc35_clearWindow,
+ &SimonEngine::vc36_setWindowImage,
+ &SimonEngine::vc37_addToSpriteY,
+ &SimonEngine::vc38_skipIfVarZero,
+ &SimonEngine::vc39_setVar,
+ &SimonEngine::vc40,
+ &SimonEngine::vc41,
+ &SimonEngine::vc42_delayIfNotEQ,
+ &SimonEngine::vc43_skipIfBitClear,
+ &SimonEngine::vc44_skipIfBitSet,
+ &SimonEngine::vc45_setSpriteX,
+ &SimonEngine::vc46_setSpriteY,
+ &SimonEngine::vc47_addToVar,
+ &SimonEngine::vc48_setPathFinder,
+ &SimonEngine::vc49_setBit,
+ &SimonEngine::vc50_clearBit,
+ &SimonEngine::vc51_clear_hitarea_bit_0x40,
+ &SimonEngine::vc52_playSound,
+ &SimonEngine::vc53_no_op,
+ &SimonEngine::vc54_no_op,
+ &SimonEngine::vc55_offset_hit_area,
+ &SimonEngine::vc56_delay,
+ &SimonEngine::vc57_no_op,
+ &SimonEngine::vc58,
+ &SimonEngine::vc59,
+ &SimonEngine::vc60_killSprite,
+ &SimonEngine::vc61_setMaskImage,
+ &SimonEngine::vc62_fastFadeOut,
+ &SimonEngine::vc63_fastFadeIn,
+ &SimonEngine::vc64_skipIfSpeechEnded,
+ &SimonEngine::vc65_slowFadeIn,
+ &SimonEngine::vc66_skipIfNotEqual,
+ &SimonEngine::vc67_skipIfGE,
+ &SimonEngine::vc68_skipIfLE,
+ &SimonEngine::vc69_playTrack,
+ &SimonEngine::vc70_queueMusic,
+ &SimonEngine::vc71_checkMusicQueue,
+ &SimonEngine::vc72_play_track_2,
+ &SimonEngine::vc73_setMark,
+ &SimonEngine::vc74_clearMark,
+ &SimonEngine::vc75_setScale,
+ &SimonEngine::vc76_setScaleXOffs,
+ &SimonEngine::vc77_setScaleYOffs,
+ &SimonEngine::vc78_computeXY,
+ &SimonEngine::vc79_computePosNum,
+ &SimonEngine::vc80_setOverlayImage,
+ &SimonEngine::vc81_setRandom,
+ &SimonEngine::vc82_getPathValue,
+ &SimonEngine::vc83_playSoundLoop,
+ &SimonEngine::vc84_stopSoundLoop,
+ };
+
+ _vga_opcode_table = vga_opcode_table;
+}
+
+// Script parser
+void SimonEngine::run_vga_script() {
+ for (;;) {
+ uint opcode;
+
+ if (_continousVgaScript) {
+ if (_vcPtr != (const byte *)&_vc_get_out_of_code) {
+ fprintf(_dumpFile, "%.5d %.5X: %5d %4d ", _vgaTickCounter, _vcPtr - _curVgaFile1, _vgaCurSpriteId, _vgaCurFileId);
+ dump_video_script(_vcPtr, true);
+ }
+ }
+
+ if (getGameType() == GType_SIMON1) {
+ opcode = READ_BE_UINT16(_vcPtr);
+ _vcPtr += 2;
+ } else {
+ opcode = *_vcPtr++;
+ }
+
+ if (opcode >= NUM_VIDEO_OP_CODES)
+ error("Invalid VGA opcode '%d' encountered", opcode);
+
+ if (opcode == 0)
+ return;
+
+ (this->*_vga_opcode_table[opcode]) ();
+ }
+}
+
+int SimonEngine::vcReadVarOrWord() {
+ int16 var = vcReadNextWord();
+ if (var < 0)
+ var = vcReadVar(-var);
+ return var;
+}
+
+uint SimonEngine::vcReadNextWord() {
+ uint a;
+ a = readUint16Wrapper(_vcPtr);
+ _vcPtr += 2;
+ return a;
+}
+
+uint SimonEngine::vcReadNextByte() {
+ return *_vcPtr++;
+}
+
+void SimonEngine::vcSkipNextInstruction() {
+ static const byte opcodeParamLenSimon1[] = {
+ 0, 6, 2, 10, 6, 4, 2, 2,
+ 4, 4, 10, 0, 2, 2, 2, 2,
+ 2, 0, 2, 0, 4, 2, 4, 2,
+ 8, 0, 10, 0, 8, 0, 2, 2,
+ 4, 0, 0, 4, 4, 2, 2, 4,
+ 4, 4, 4, 2, 2, 2, 2, 4,
+ 0, 2, 2, 2, 2, 4, 6, 6,
+ 0, 0, 0, 0, 2, 6, 0, 0,
+ };
+
+ static const byte opcodeParamLenSimon2[] = {
+ 0, 6, 2, 12, 6, 4, 2, 2,
+ 4, 4, 9, 0, 1, 2, 2, 2,
+ 2, 0, 2, 0, 4, 2, 4, 2,
+ 7, 0, 10, 0, 8, 0, 2, 2,
+ 4, 0, 0, 4, 4, 2, 2, 4,
+ 4, 4, 4, 2, 2, 2, 2, 4,
+ 0, 2, 2, 2, 2, 4, 6, 6,
+ 2, 0, 6, 6, 4, 6, 0, 0,
+ 0, 0, 4, 4, 4, 4, 4, 0,
+ 4, 2, 2
+ };
+
+ static const byte opcodeParamLenFeebleFiles[] = {
+ 0, 6, 2, 12, 6, 4, 2, 2,
+ 4, 4, 9, 0, 1, 2, 2, 2,
+ 2, 0, 2, 0, 4, 2, 4, 2,
+ 7, 0, 10, 0, 8, 0, 2, 2,
+ 4, 0, 0, 4, 4, 2, 2, 4,
+ 4, 4, 4, 2, 2, 2, 2, 4,
+ 0, 2, 2, 2, 6, 6, 6, 6,
+ 2, 0, 6, 6, 4, 6, 0, 0,
+ 0, 0, 4, 4, 4, 4, 4, 0,
+ 4, 2, 2, 4, 6, 6, 0, 0,
+ 6, 4, 2, 6, 0
+ };
+
+ if (getGameType() == GType_FF) {
+ uint opcode = vcReadNextByte();
+ _vcPtr += opcodeParamLenFeebleFiles[opcode];
+ } else if (getGameType() == GType_SIMON2) {
+ uint opcode = vcReadNextByte();
+ _vcPtr += opcodeParamLenSimon2[opcode];
+ } else {
+ uint opcode = vcReadNextWord();
+ _vcPtr += opcodeParamLenSimon1[opcode];
+ }
+
+ if (_continousVgaScript)
+ fprintf(_dumpFile, "; skipped\n");
+}
+
+void SimonEngine::o_unloadBeard() {
+ // Simon1 Only
+ if (_beardLoaded == true) {
+ _beardLoaded = false;
+ _lockWord |= 0x8000;
+ read_vga_from_datfile_1(23);
+ _lockWord &= ~0x8000;
+ }
+}
+
+void SimonEngine::o_loadBeard() {
+ // Simon1 Only
+ if (_beardLoaded == false) {
+ _beardLoaded = true;
+ _lockWord |= 0x8000;
+ read_vga_from_datfile_1(328);
+ _lockWord &= ~0x8000;
+ }
+}
+
+// VGA Script commands
+void SimonEngine::vc1_fadeOut() {
+ /* dummy opcode */
+ _vcPtr += 6;
+}
+
+void SimonEngine::vc2_call() {
+ VgaPointersEntry *vpe;
+ uint num;
+ uint res;
+ byte *old_file_1, *old_file_2;
+ byte *b, *bb;
+ const byte *vc_ptr_org;
+
+ num = vcReadVarOrWord();
+
+ old_file_1 = _curVgaFile1;
+ old_file_2 = _curVgaFile2;
+
+ for (;;) {
+ res = num / 100;
+ vpe = &_vgaBufferPointers[res];
+
+ _curVgaFile1 = vpe->vgaFile1;
+ _curVgaFile2 = vpe->vgaFile2;
+ if (vpe->vgaFile1 != NULL)
+ break;
+ if (_vgaCurFile2 != res)
+ _videoVar7 = _vgaCurFile2;
+
+ loadZone(res);
+ _videoVar7 = 0xFFFF;
+ }
+
+
+ bb = _curVgaFile1;
+ if (getGameType() == GType_FF) {
+ b = bb + READ_LE_UINT16(&((VgaFileHeader_Feeble *) bb)->hdr2_start);
+ b = bb + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageTable);
+
+ while (READ_LE_UINT16(&((ImageHeader_Feeble *) b)->id) != num)
+ b += sizeof(ImageHeader_Feeble);
+ } else {
+ b = bb + READ_BE_UINT16(&((VgaFileHeader_Simon *) bb)->hdr2_start);
+ b = bb + READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageTable);
+
+ while (READ_BE_UINT16(&((ImageHeader_Simon *) b)->id) != num)
+ b += sizeof(ImageHeader_Simon);
+ }
+
+ vc_ptr_org = _vcPtr;
+
+ if (getGameType() == GType_FF) {
+ _vcPtr = _curVgaFile1 + READ_LE_UINT16(&((ImageHeader_Feeble *) b)->scriptOffs);
+ } else {
+ _vcPtr = _curVgaFile1 + READ_BE_UINT16(&((ImageHeader_Simon *) b)->scriptOffs);
+ }
+
+ //dump_vga_script(_vcPtr, res, num);
+ run_vga_script();
+
+ _curVgaFile1 = old_file_1;
+ _curVgaFile2 = old_file_2;
+
+ _vcPtr = vc_ptr_org;
+}
+
+void SimonEngine::vc3_loadSprite() {
+ uint16 windowNum, fileId, palette, x, y, vgaSpriteId;
+ uint16 res;
+ VgaSprite *vsp;
+ VgaPointersEntry *vpe;
+ byte *p, *pp;
+ byte *old_file_1;
+
+ windowNum = vcReadNextWord(); /* 0 */
+
+ if (getGameType() == GType_SIMON1) {
+ vgaSpriteId = vcReadNextWord(); /* 2 */
+ fileId = vgaSpriteId / 100;
+ } else {
+ fileId = vcReadNextWord(); /* 0 */
+ vgaSpriteId = vcReadNextWord(); /* 2 */
+ }
+
+ x = vcReadNextWord(); /* 4 */
+ y = vcReadNextWord(); /* 6 */
+ palette = vcReadNextWord(); /* 8 */
+
+ /* 2nd param ignored with simon1 */
+ if (isSpriteLoaded(vgaSpriteId, fileId))
+ return;
+
+ vsp = _vgaSprites;
+ while (vsp->id)
+ vsp++;
+
+ vsp->palette = palette;
+ vsp->windowNum = windowNum;
+ vsp->priority = 0;
+ vsp->flags = 0;
+ vsp->image = 0;
+ vsp->x = x;
+ vsp->y = y;
+ vsp->id = vgaSpriteId;
+ vsp->fileId = res = fileId;
+
+ old_file_1 = _curVgaFile1;
+ for (;;) {
+ vpe = &_vgaBufferPointers[res];
+ _curVgaFile1 = vpe->vgaFile1;
+
+ if (vpe->vgaFile1 != NULL)
+ break;
+ if (_vgaCurFile2 != res)
+ _videoVar7 = _vgaCurFile2;
+
+ loadZone(res);
+ _videoVar7 = 0xFFFF;
+ }
+
+ pp = _curVgaFile1;
+ if (getGameType() == GType_FF) {
+ p = pp + READ_LE_UINT16(&((VgaFileHeader_Feeble *) pp)->hdr2_start);
+ p = pp + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationTable);
+
+ while (READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->id) != vgaSpriteId)
+ p += sizeof(AnimationHeader_Feeble);
+ } else {
+ p = pp + READ_BE_UINT16(&((VgaFileHeader_Simon *) pp)->hdr2_start);
+ p = pp + READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationTable);
+
+ while (READ_BE_UINT16(&((AnimationHeader_Simon *) p)->id) != vgaSpriteId)
+ p += sizeof(AnimationHeader_Simon);
+ }
+
+#ifdef DUMP_FILE_NR
+ {
+ static bool dumped = false;
+ if (res == DUMP_FILE_NR && !dumped) {
+ dumped = true;
+ dump_vga_file(_curVgaFile1);
+ }
+ }
+#endif
+
+#ifdef DUMP_BITMAPS_FILE_NR
+ {
+ static bool dumped = false;
+ if (res == DUMP_BITMAPS_FILE_NR && !dumped) {
+ dumped = true;
+ dump_vga_bitmaps(_curVgaFile2, _curVgaFile1, res);
+ }
+ }
+#endif
+
+ if (_startVgaScript) {
+ if (getGameType() == GType_FF) {
+ dump_vga_script(_curVgaFile1 + READ_LE_UINT16(&((AnimationHeader_Feeble*)p)->scriptOffs), res, vgaSpriteId);
+ } else {
+ dump_vga_script(_curVgaFile1 + READ_BE_UINT16(&((AnimationHeader_Simon*)p)->scriptOffs), res, vgaSpriteId);
+
+ }
+ }
+
+ if (getGameType() == GType_FF) {
+ add_vga_timer(VGA_DELAY_BASE, _curVgaFile1 + READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->scriptOffs), vgaSpriteId, res);
+ } else {
+ add_vga_timer(VGA_DELAY_BASE, _curVgaFile1 + READ_BE_UINT16(&((AnimationHeader_Simon *) p)->scriptOffs), vgaSpriteId, res);
+ }
+
+ _curVgaFile1 = old_file_1;
+}
+
+void SimonEngine::vc4_fadeIn() {
+ /* dummy opcode */
+ _vcPtr += 6;
+}
+
+void SimonEngine::vc5_skip_if_neq() {
+ uint var = vcReadNextWord();
+ uint value = vcReadNextWord();
+ if (vcReadVar(var) != value)
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc6_skip_ifn_sib_with_a() {
+ if (!itemIsSiblingOf(vcReadNextWord()))
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc7_skip_if_sib_with_a() {
+ if (itemIsSiblingOf(vcReadNextWord()))
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc8_skip_if_parent_is() {
+ uint a = vcReadNextWord();
+ uint b = vcReadNextWord();
+ if (!itemIsParentOf(a, b))
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc9_skip_if_unk3_is() {
+ uint a = vcReadNextWord();
+ uint b = vcReadNextWord();
+ if (!vc_maybe_skip_proc_1(a, b))
+ vcSkipNextInstruction();
+}
+
+byte *vc10_depack_column(VC10_state * vs) {
+ int8 a = vs->depack_cont;
+ const byte *src = vs->depack_src;
+ byte *dst = vs->depack_dest;
+ uint16 dh = vs->dh;
+ byte color;
+
+ if (a == -0x80)
+ a = *src++;
+
+ for (;;) {
+ if (a >= 0) {
+ color = *src++;
+ do {
+ *dst++ = color;
+ if (!--dh) {
+ if (--a < 0)
+ a = -0x80;
+ else
+ src--;
+ goto get_out;
+ }
+ } while (--a >= 0);
+ } else {
+ do {
+ *dst++ = *src++;
+ if (!--dh) {
+ if (++a == 0)
+ a = -0x80;
+ goto get_out;
+ }
+ } while (++a != 0);
+ }
+ a = *src++;
+ }
+
+get_out:;
+ vs->depack_src = src;
+ vs->depack_cont = a;
+ return vs->depack_dest + vs->y_skip;
+}
+
+void vc10_skip_cols(VC10_state *vs) {
+ vs->depack_cont = -0x80;
+ while (vs->x_skip) {
+ vc10_depack_column(vs);
+ vs->x_skip--;
+ }
+}
+
+byte *SimonEngine::vc10_uncompressFlip(const byte *src, uint w, uint h) {
+ w *= 8;
+
+ byte *src_org, *dst_org;
+ byte color;
+ int8 cur = -0x80;
+ uint i, w_cur = w;
+
+ dst_org = _videoBuf1 + w;
+
+ do {
+ byte *dst = dst_org;
+ uint h_cur = h;
+
+ if (cur == -0x80)
+ cur = *src++;
+
+ for (;;) {
+ if (cur >= 0) {
+ /* rle_same */
+ color = *src++;
+ do {
+ *dst = color;
+ dst += w;
+ if (!--h_cur) {
+ if (--cur < 0)
+ cur = -0x80;
+ else
+ src--;
+ goto next_line;
+ }
+ } while (--cur >= 0);
+ } else {
+ /* rle_diff */
+ do {
+ *dst = *src++;
+ dst += w;
+ if (!--h_cur) {
+ if (++cur == 0)
+ cur = -0x80;
+ goto next_line;
+ }
+ } while (++cur != 0);
+ }
+ cur = *src++;
+ }
+ next_line:
+ dst_org++;
+ } while (--w_cur);
+
+
+ src_org = dst_org = _videoBuf1 + w;
+
+ do {
+ byte *dst = dst_org;
+ for (i = 0; i != w; ++i) {
+ byte b = src_org[i];
+ b = (b >> 4) | (b << 4);
+ *--dst = b;
+ }
+
+ src_org += w;
+ dst_org += w;
+ } while (--h);
+
+ return _videoBuf1;
+}
+
+byte *SimonEngine::vc10_flip(const byte *src, uint w, uint h) {
+ if (src == _vc10BasePtrOld)
+ return _videoBuf1;
+
+ _vc10BasePtrOld = src;
+
+ byte *dst_org, *src_org;
+ uint i;
+
+ w *= 8;
+ src_org = dst_org = _videoBuf1 + w;
+
+ do {
+ byte *dst = dst_org;
+ for (i = 0; i != w; ++i) {
+ byte b = src_org[i];
+ b = (b >> 4) | (b << 4);
+ *--dst = b;
+ }
+
+ src_org += w;
+ dst_org += w;
+ } while (--h);
+
+ return _videoBuf1;
+}
+
+/* must not be const */
+static uint16 _video_windows[128] = {
+ 0, 0, 20, 200,
+ 0, 0, 3, 136,
+ 17, 0, 3, 136,
+ 0, 0, 20, 200,
+ 0, 0, 20, 134
+};
+
+/* simon2 specific */
+void SimonEngine::decodeStripA(byte *dst, const byte *src, int height) {
+ const uint pitch = _dxSurfacePitch;
+ int8 reps = (int8)0x80;
+ byte color;
+ byte *dst_org = dst;
+ uint h = height, w = 8;
+
+ for (;;) {
+ reps = *src++;
+ if (reps >= 0) {
+ color = *src++;
+
+ do {
+ *dst = color;
+ dst += pitch;
+
+ /* reached bottom? */
+ if (--h == 0) {
+ /* reached right edge? */
+ if (--w == 0)
+ return;
+ dst = ++dst_org;
+ h = height;
+ }
+ } while (--reps >= 0);
+ } else {
+
+ do {
+ *dst = *src++;
+ dst += pitch;
+
+ /* reached bottom? */
+ if (--h == 0) {
+ /* reached right edge? */
+ if (--w == 0)
+ return;
+ dst = ++dst_org;
+ h = height;
+ }
+ } while (++reps != 0);
+ }
+ }
+}
+
+void SimonEngine::vc10_draw() {
+ byte *p2;
+ uint width, height;
+ uint maxWidth, maxHeight;
+ byte flags;
+ const uint16 *vlut;
+ VC10_state state;
+
+ int cur;
+
+ state.image = (int16)vcReadNextWord();
+ if (state.image == 0)
+ return;
+
+ if (getGameType() == GType_FF) {
+ state.palette = (_vcPtr[0] * 16);
+ } else {
+ state.palette = (_vcPtr[1] * 16);
+ }
+ _vcPtr += 2;
+ state.x = (int16)vcReadNextWord();
+
+ if (getGameType() == GType_SIMON2) {
+ state.x -= _scrollX;
+ }
+ state.y = (int16)vcReadNextWord();
+
+ if (getGameType() == GType_SIMON1) {
+ state.flags = vcReadNextWord();
+ } else {
+ state.flags = vcReadNextByte();
+ }
+
+ if (state.image < 0)
+ state.image = vcReadVar(-state.image);
+
+ debug(1, "vc10_draw: image %d palette %d x %d y %d flags 0x0%x\n", state.image, state.palette, state.x, state.y, state.flags);
+
+ p2 = _curVgaFile2 + state.image * 8;
+ if (getGameType() == GType_FF) {
+ state.depack_src = _curVgaFile2 + READ_LE_UINT32(p2);
+ width = READ_LE_UINT16(p2 + 6);
+ height = READ_LE_UINT16(p2 + 4) & 0x7FFF;
+ flags = p2[5];
+
+ debug(1, "Width %d Height %d Flags 0x%x", width, height, flags);
+ } else {
+ state.depack_src = _curVgaFile2 + READ_BE_UINT32(p2);
+ width = READ_BE_UINT16(p2 + 6) / 16;
+ height = p2[5];
+ flags = p2[4];
+ }
+
+ if (height == 0 || width == 0)
+ return;
+
+ if (_dumpImages)
+ dump_single_bitmap(_vgaCurFileId, state.image, state.depack_src, width * 16, height,
+ state.palette);
+ // TODO::Add support for image overlay and scaling in Feeble Files
+
+ if (flags & 0x80 && !(state.flags & 0x10)) {
+ if (state.flags & 1) {
+ state.flags &= ~1;
+ state.flags |= 0x10;
+ } else {
+ state.flags |= 0x8;
+ }
+ }
+
+ if (getGameType() == GType_FF) {
+ if (width > 640) {
+ debug(0, "Horizontal scrolling not supported");
+ return;
+ }
+ if (height > 480) {
+ debug(0, "Vertical scrolling not supported");
+ return;
+ }
+ }
+ if (getGameType() == GType_SIMON2 && width > 20) {
+ const byte *src;
+ byte *dst;
+ uint w;
+
+ _scrollXMax = width * 2 - 40;
+ _scrollImage = state.depack_src;
+ _scrollHeight = height;
+ if (_variableArray[34] == -1)
+ state.x = _variableArray[251];
+
+ _scrollX = state.x;
+
+ vcWriteVar(251, _scrollX);
+
+ dst = dx_lock_attached();
+ src = state.depack_src + _scrollX * 4;
+
+ for (w = 0; w < 40; w++) {
+ decodeStripA(dst, src + READ_BE_UINT32(src), height);
+ dst += 8;
+ src += 4;
+ }
+
+ dx_unlock_attached();
+
+ return;
+ }
+
+ if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
+ if (state.flags & 0x10) {
+ state.depack_src = vc10_uncompressFlip(state.depack_src, width, height);
+ } else if (state.flags & 1) {
+ state.depack_src = vc10_flip(state.depack_src, width, height);
+ }
+ }
+
+ vlut = &_video_windows[_windowNum * 4];
+
+ state.width = state.draw_width = width; /* cl */
+ state.height = state.draw_height = height; /* ch */
+
+ if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
+ state.draw_width = width * 2;
+ }
+
+ state.x_skip = 0; /* colums to skip = bh */
+ state.y_skip = 0; /* rows to skip = bl */
+
+ cur = state.x;
+ if (cur < 0) {
+ do {
+ if (!--state.draw_width)
+ return;
+ state.x_skip++;
+ } while (++cur);
+ }
+ state.x = cur;
+
+ maxWidth = (getGameType() == GType_FF) ? 640 : (vlut[2] * 2);
+ cur += state.draw_width - maxWidth;
+ if (cur > 0) {
+ do {
+ if (!--state.draw_width)
+ return;
+ } while (--cur);
+ }
+
+ cur = state.y;
+ if (cur < 0) {
+ do {
+ if (!--state.draw_height)
+ return;
+ state.y_skip++;
+ } while (++cur);
+ }
+ state.y = cur;
+
+ maxHeight = (getGameType() == GType_FF) ? 480 : vlut[3];
+ cur += state.draw_height - maxHeight;
+ if (cur > 0) {
+ do {
+ if (!--state.draw_height)
+ return;
+ } while (--cur);
+ }
+
+ assert(state.draw_width != 0 && state.draw_height != 0);
+
+ if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
+ state.draw_width *= 4;
+ }
+
+ state.surf2_addr = dx_lock_2();
+ state.surf2_pitch = _dxSurfacePitch;
+
+ state.surf_addr = dx_lock_attached();
+ state.surf_pitch = _dxSurfacePitch;
+
+ if (getGameType() == GType_FF) {
+ drawImages_Feeble(&state);
+ } else {
+ drawImages(&state);
+ }
+
+ dx_unlock_2();
+ dx_unlock_attached();
+}
+
+void SimonEngine::drawImages_Feeble(VC10_state *state) {
+ state->surf2_addr += state->x + state->y * state->surf2_pitch;
+ state->surf_addr += state->x + state->y * state->surf_pitch;
+
+ if (state->flags & 0x20) {
+ if (vcGetBit(81) == false) {
+ // TODO: Compare Feeble rect
+ }
+
+ uint w, h;
+ byte *src, *dst, *dst_org;
+
+ state->dl = state->width;
+ state->dh = state->height;
+
+ vc10_skip_cols(state);
+
+ dst_org = state->surf_addr;
+ w = 0;
+ do {
+ byte color;
+
+ src = vc10_depack_column(state);
+ dst = dst_org;
+
+ h = 0;
+ do {
+ color = *src;
+ if (color)
+ *dst = color;
+ dst += _screenWidth;
+ src++;
+ } while (++h != state->draw_height);
+ dst_org++;
+ } while (++w != state->draw_width);
+ } else {
+ if (state->flags & 0x8) {
+ uint w, h;
+ byte *src, *dst, *dst_org;
+
+ state->dl = state->width;
+ state->dh = state->height;
+
+ vc10_skip_cols(state);
+
+ if (state->flags & 2) {
+ dst_org = state->surf_addr;
+ w = 0;
+ do {
+ src = vc10_depack_column(state);
+ dst = dst_org;
+
+ h = 0;
+ do {
+ *dst = *src;
+ dst += _screenWidth;
+ src++;
+ } while (++h != state->draw_height);
+ dst_org++;
+ } while (++w != state->draw_width);
+ } else {
+ dst_org = state->surf_addr;
+ w = 0;
+ do {
+ byte color;
+
+ src = vc10_depack_column(state);
+ dst = dst_org;
+
+ h = 0;
+ do {
+ color = *src;
+ if (color)
+ *dst = color;
+ dst += _screenWidth;
+ src++;
+ } while (++h != state->draw_height);
+ dst_org++;
+ } while (++w != state->draw_width);
+ }
+ } else {
+ const byte *src;
+ byte *dst;
+ uint count;
+
+ src = state->depack_src + state->width * state->y_skip;
+ dst = state->surf_addr;
+ if (state->flags & 0x80) {
+ do {
+ for (count = 0; count != state->draw_width; count++) {
+ byte color;
+ color = src[count + state->x_skip];
+ if (color) {
+ if (color == 220)
+ color = 244;
+
+ dst[count] = color;
+ }
+ }
+ dst += _screenWidth;
+ src += state->width;
+ } while (--state->draw_height);
+ } else {
+ do {
+ for (count = 0; count != state->draw_width; count++) {
+ byte color;
+ color = src[count + state->x_skip];
+ if (color)
+ dst[count] = color;
+ }
+ dst += _screenWidth;
+ src += state->width;
+ } while (--state->draw_height);
+ }
+ }
+ }
+}
+
+void SimonEngine::drawImages(VC10_state *state) {
+ const uint16 *vlut = &_video_windows[_windowNum * 4];
+
+ uint offs, offs2;
+ // Allow one section of Simon the Sorcerer 1 introduction to be displayed
+ // in lower half of screen
+ if ((getGameType() == GType_SIMON1) && _subroutine == 2926) {
+ offs = ((vlut[0]) * 2 + state->x) * 8;
+ offs2 = (vlut[1] + state->y);
+ } else {
+ offs = ((vlut[0] - _video_windows[16]) * 2 + state->x) * 8;
+ offs2 = (vlut[1] - _video_windows[17] + state->y);
+ }
+
+ state->surf2_addr += offs + offs2 * state->surf2_pitch;
+ state->surf_addr += offs + offs2 * state->surf_pitch;
+
+ if (state->flags & 0x20) {
+ byte *mask, *src, *dst;
+ byte h;
+ uint w;
+
+ state->x_skip *= 4;
+ state->dl = state->width;
+ state->dh = state->height;
+
+ vc10_skip_cols(state);
+
+ w = 0;
+ do {
+ mask = vc10_depack_column(state); /* esi */
+ src = state->surf2_addr + w * 2; /* ebx */
+ dst = state->surf_addr + w * 2; /* edi */
+
+ h = state->draw_height;
+ if ((getGameType() == GType_SIMON1) && vcGetBit(88)) {
+ /* transparency */
+ do {
+ if (mask[0] & 0xF0) {
+ if ((dst[0] & 0x0F0) == 0x20)
+ dst[0] = src[0];
+ }
+ if (mask[0] & 0x0F) {
+ if ((dst[1] & 0x0F0) == 0x20)
+ dst[1] = src[1];
+ }
+ mask++;
+ dst += state->surf_pitch;
+ src += state->surf2_pitch;
+ } while (--h);
+ } else {
+ /* no transparency */
+ do {
+ if (mask[0] & 0xF0)
+ dst[0] = src[0];
+ if (mask[0] & 0x0F)
+ dst[1] = src[1];
+ mask++;
+ dst += state->surf_pitch;
+ src += state->surf2_pitch;
+ } while (--h);
+ }
+ } while (++w != state->draw_width);
+
+ /* vc10_helper_5 */
+ } else if (((_lockWord & 0x20) && state->palette == 0) || state->palette == 0xC0) {
+ const byte *src;
+ byte *dst;
+ uint h, i;
+
+ if (!(state->flags & 8)) {
+ src = state->depack_src + (state->width * state->y_skip * 16) + (state->x_skip * 8);
+ dst = state->surf_addr;
+
+ state->draw_width *= 2;
+
+ if (state->flags & 2) {
+ /* no transparency */
+ h = state->draw_height;
+ do {
+ memcpy(dst, src, state->draw_width);
+ dst += _screenWidth;
+ src += state->width * 16;
+ } while (--h);
+ } else {
+ /* transparency */
+ h = state->draw_height;
+ do {
+ for (i = 0; i != state->draw_width; i++)
+ if (src[i])
+ dst[i] = src[i];
+ dst += _screenWidth;
+ src += state->width * 16;
+ } while (--h);
+ }
+
+ } else {
+ byte *dst_org = state->surf_addr;
+ src = state->depack_src;
+ /* AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE
+ * aaaaabbb bbcccccd ddddeeee efffffgg ggghhhhh
+ */
+
+ if (state->flags & 2) {
+ /* no transparency */
+ do {
+ uint count = state->draw_width / 4;
+
+ dst = dst_org;
+ do {
+ uint32 bits = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | (src[3]);
+
+ dst[0] = (byte)((bits >> (32 - 5)) & 31);
+ dst[1] = (byte)((bits >> (32 - 10)) & 31);
+ dst[2] = (byte)((bits >> (32 - 15)) & 31);
+ dst[3] = (byte)((bits >> (32 - 20)) & 31);
+ dst[4] = (byte)((bits >> (32 - 25)) & 31);
+ dst[5] = (byte)((bits >> (32 - 30)) & 31);
+
+ bits = (bits << 8) | src[4];
+
+ dst[6] = (byte)((bits >> (40 - 35)) & 31);
+ dst[7] = (byte)((bits) & 31);
+
+ dst += 8;
+ src += 5;
+ } while (--count);
+ dst_org += _screenWidth;
+ } while (--state->draw_height);
+ } else {
+ /* transparency */
+ do {
+ uint count = state->draw_width / 4;
+
+ dst = dst_org;
+ do {
+ uint32 bits = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | (src[3]);
+ byte tmp;
+
+ tmp = (byte)((bits >> (32 - 5)) & 31);
+ if (tmp)
+ dst[0] = tmp;
+ tmp = (byte)((bits >> (32 - 10)) & 31);
+ if (tmp)
+ dst[1] = tmp;
+ tmp = (byte)((bits >> (32 - 15)) & 31);
+ if (tmp)
+ dst[2] = tmp;
+ tmp = (byte)((bits >> (32 - 20)) & 31);
+ if (tmp)
+ dst[3] = tmp;
+ tmp = (byte)((bits >> (32 - 25)) & 31);
+ if (tmp)
+ dst[4] = tmp;
+ tmp = (byte)((bits >> (32 - 30)) & 31);
+ if (tmp)
+ dst[5] = tmp;
+
+ bits = (bits << 8) | src[4];
+
+ tmp = (byte)((bits >> (40 - 35)) & 31);
+ if (tmp)
+ dst[6] = tmp;
+ tmp = (byte)((bits) & 31);
+ if (tmp)
+ dst[7] = tmp;
+
+ dst += 8;
+ src += 5;
+ } while (--count);
+ dst_org += _screenWidth;
+ } while (--state->draw_height);
+ }
+ }
+ /* vc10_helper_4 */
+ } else {
+ if (getGameType() == GType_SIMON2 && state->flags & 0x4 && _bitArray[10] & 0x800) {
+ state->surf_addr = state->surf2_addr;
+ state->surf_pitch = state->surf2_pitch;
+ }
+
+ if (state->flags & 0x8) {
+ uint w, h;
+ byte *src, *dst, *dst_org;
+
+ state->x_skip *= 4; /* reached */
+
+ state->dl = state->width;
+ state->dh = state->height;
+
+ vc10_skip_cols(state);
+
+ if (state->flags & 2) {
+ dst_org = state->surf_addr;
+ w = 0;
+ do {
+ src = vc10_depack_column(state);
+ dst = dst_org;
+
+ h = 0;
+ do {
+ dst[0] = (*src / 16) | state->palette;
+ dst[1] = (*src & 15) | state->palette;
+ dst += _screenWidth;
+ src++;
+ } while (++h != state->draw_height);
+ dst_org += 2;
+ } while (++w != state->draw_width);
+ } else {
+ dst_org = state->surf_addr;
+ if (state->flags & 0x40) { /* reached */
+ dst_org += vcReadVar(252);
+ }
+ w = 0;
+ do {
+ byte color;
+
+ src = vc10_depack_column(state);
+ dst = dst_org;
+
+ h = 0;
+ do {
+ color = (*src / 16);
+ if (color)
+ dst[0] = color | state->palette;
+ color = (*src & 15);
+ if (color)
+ dst[1] = color | state->palette;
+ dst += _screenWidth;
+ src++;
+ } while (++h != state->draw_height);
+ dst_org += 2;
+ } while (++w != state->draw_width);
+ }
+ /* vc10_helper_6 */
+ } else {
+ const byte *src;
+ byte *dst;
+ uint count;
+
+ src = state->depack_src + (state->width * state->y_skip) * 8;
+ dst = state->surf_addr;
+ state->x_skip *= 4;
+ if (state->flags & 2) {
+ do {
+ for (count = 0; count != state->draw_width; count++) {
+ dst[count * 2] = (src[count + state->x_skip] / 16) | state->palette;
+ dst[count * 2 + 1] = (src[count + state->x_skip] & 15) | state->palette;
+ }
+ dst += _screenWidth;
+ src += state->width * 8;
+ } while (--state->draw_height);
+ } else {
+ do {
+ for (count = 0; count != state->draw_width; count++) {
+ byte color;
+ color = (src[count + state->x_skip] / 16);
+ if (color)
+ dst[count * 2] = color | state->palette;
+ color = (src[count + state->x_skip] & 15);
+ if (color)
+ dst[count * 2 + 1] = color | state->palette;
+ }
+ dst += _screenWidth;
+ src += state->width * 8;
+ } while (--state->draw_height);
+
+ }
+
+ /* vc10_helper_7 */
+ }
+ }
+
+}
+
+void SimonEngine::vc11_clearPathFinder() {
+ memset(&_pathFindArray, 0, sizeof(_pathFindArray));
+}
+
+void SimonEngine::vc12_delay() {
+ VgaSprite *vsp = findCurSprite();
+ uint num;
+
+ if (getGameType() == GType_SIMON1) {
+ num = vcReadVarOrWord();
+ } else {
+ num = vcReadNextByte() * _frameRate;
+ }
+
+ // Work around to allow inventory arrows to be
+ // shown in some versions of Simon the Sorcerer 1
+ if ((getGameType() == GType_SIMON1) && vsp->id == 128)
+ num = 0;
+ else
+ num += VGA_DELAY_BASE;
+
+ add_vga_timer(num, _vcPtr, _vgaCurSpriteId, _vgaCurFileId);
+ _vcPtr = (byte *)&_vc_get_out_of_code;
+}
+
+void SimonEngine::vc13_addToSpriteX() {
+ VgaSprite *vsp = findCurSprite();
+ vsp->x += (int16)vcReadNextWord();
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc14_addToSpriteY() {
+ VgaSprite *vsp = findCurSprite();
+ vsp->y += (int16)vcReadNextWord();
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc15_wakeup_id() {
+ VgaSleepStruct *vfs = _vgaSleepStructs, *vfs_tmp;
+ uint16 id = vcReadNextWord();
+ while (vfs->ident != 0) {
+ if (vfs->ident == id) {
+ add_vga_timer(VGA_DELAY_BASE, vfs->code_ptr, vfs->sprite_id, vfs->cur_vga_file);
+ vfs_tmp = vfs;
+ do {
+ memcpy(vfs_tmp, vfs_tmp + 1, sizeof(VgaSleepStruct));
+ vfs_tmp++;
+ } while (vfs_tmp->ident != 0);
+ } else {
+ vfs++;
+ }
+ }
+
+ /* clear a wait event */
+ if (id == _vgaWaitFor)
+ _vgaWaitFor = 0;
+}
+
+void SimonEngine::vc16_sleep_on_id() {
+ VgaSleepStruct *vfs = _vgaSleepStructs;
+ while (vfs->ident)
+ vfs++;
+
+ vfs->ident = vcReadNextWord();
+ vfs->code_ptr = _vcPtr;
+ vfs->sprite_id = _vgaCurSpriteId;
+ vfs->cur_vga_file = _vgaCurFileId;
+
+ _vcPtr = (byte *)&_vc_get_out_of_code;
+}
+
+void SimonEngine::vc17_setPathfinderItem() {
+ uint a = vcReadNextWord();
+ _pathFindArray[a - 1] = (const uint16 *)_vcPtr;
+
+ int end = (getGameType() == GType_FF) ? 9999 : 999;
+ while (readUint16Wrapper(_vcPtr) != end)
+ _vcPtr += 4;
+ _vcPtr += 2;
+}
+
+void SimonEngine::vc18_jump() {
+ int16 offs = vcReadNextWord();
+ _vcPtr += offs;
+}
+
+/* chain to script? */
+void SimonEngine::vc19_chain_to_script() {
+ /* unused */
+ error("vc19_chain_to_script: not implemented");
+}
+
+/* helper routines */
+
+void SimonEngine::vc20_setRepeat() {
+ /* FIXME: This opcode is somewhat strange: it first reads a BE word from
+ * the script (advancing the script pointer in doing so); then it writes
+ * back the same word, this time as LE, into the script.
+ */
+ uint16 a = vcReadNextWord();
+ WRITE_LE_UINT16(const_cast<byte *>(_vcPtr), a);
+ _vcPtr += 2;
+}
+
+void SimonEngine::vc21_endRepeat() {
+ int16 a = vcReadNextWord();
+ const byte *tmp = _vcPtr + a;
+ if (getGameType() == GType_SIMON1)
+ tmp += 4;
+ else
+ tmp += 3;
+
+ uint16 val = READ_LE_UINT16(tmp);
+ if (val != 0) {
+ // Decrement counter
+ WRITE_LE_UINT16(const_cast<byte *>(tmp), val - 1);
+ _vcPtr = tmp + 2;
+ }
+}
+
+void SimonEngine::vc22_setSpritePalette() {
+ uint a = vcReadNextWord();
+ uint b = vcReadNextWord();
+ uint num = a == 0 ? 32 : 16;
+ uint palSize = 96;
+ byte *palptr, *src;
+
+ if (getGameType() == GType_FF) {
+ a = 0;
+ num = 256;
+ palSize = 768;
+ }
+
+ palptr = &_palette[(a * 64)];
+ src = _curVgaFile1 + 6 + b * palSize;
+
+ do {
+ palptr[0] = src[0] * 4;
+ palptr[1] = src[1] * 4;
+ palptr[2] = src[2] * 4;
+ palptr[3] = 0;
+
+ palptr += 4;
+ src += 3;
+ } while (--num);
+
+ _paletteFlag = 2;
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc23_setSpritePriority() {
+ VgaSprite *vsp = findCurSprite(), *vus2;
+ uint16 pri = vcReadNextWord();
+ VgaSprite bak;
+
+ if (vsp->id == 0)
+ return;
+
+ memcpy(&bak, vsp, sizeof(bak));
+ bak.priority = pri;
+ bak.windowNum |= 0x8000;
+
+ vus2 = vsp;
+
+ if (vsp != _vgaSprites && pri < vsp[-1].priority) {
+ do {
+ vsp--;
+ } while (vsp != _vgaSprites && pri < vsp[-1].priority);
+ do {
+ memcpy(vus2, vus2 - 1, sizeof(VgaSprite));
+ } while (--vus2 != vsp);
+ memcpy(vus2, &bak, sizeof(VgaSprite));
+ } else if (vsp[1].id != 0 && pri >= vsp[1].priority) {
+ do {
+ vsp++;
+ } while (vsp[1].id != 0 && pri >= vsp[1].priority);
+ do {
+ memcpy(vus2, vus2 + 1, sizeof(VgaSprite));
+ } while (++vus2 != vsp);
+ memcpy(vus2, &bak, sizeof(VgaSprite));
+ } else {
+ vsp->priority = pri;
+ }
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc24_setSpriteXY() {
+ VgaSprite *vsp = findCurSprite();
+ vsp->image = vcReadVarOrWord();
+
+ vsp->x += (int16)vcReadNextWord();
+ vsp->y += (int16)vcReadNextWord();
+ if (getGameType() == GType_SIMON1) {
+ vsp->flags = vcReadNextWord();
+ } else {
+ vsp->flags = vcReadNextByte();
+ }
+
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc25_halt_sprite() {
+ VgaSprite *vsp = findCurSprite();
+ while (vsp->id != 0) {
+ memcpy(vsp, vsp + 1, sizeof(VgaSprite));
+ vsp++;
+ }
+ _vcPtr = (byte *)&_vc_get_out_of_code;
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc26_setSubWindow() {
+ uint16 *as = &_video_windows[vcReadNextWord() * 4]; // number
+ as[0] = vcReadNextWord(); // x
+ as[1] = vcReadNextWord(); // y
+ as[2] = vcReadNextWord(); // width
+ as[3] = vcReadNextWord(); // height
+}
+
+void SimonEngine::vc27_resetSprite() {
+ VgaSprite bak, *vsp;
+ VgaSleepStruct *vfs;
+ VgaTimerEntry *vte, *vte2;
+
+ _lockWord |= 8;
+
+ memset(&bak, 0, sizeof(bak));
+
+ vsp = _vgaSprites;
+ while (vsp->id) {
+ if ((getGameType() == GType_SIMON1) && vsp->id == 128) {
+ memcpy(&bak, vsp, sizeof(VgaSprite));
+ }
+ vsp->id = 0;
+ vsp++;
+ }
+
+ if (bak.id != 0)
+ memcpy(_vgaSprites, &bak, sizeof(VgaSprite));
+
+ vfs = _vgaSleepStructs;
+ while (vfs->ident) {
+ vfs->ident = 0;
+ vfs++;
+ }
+
+ vte = _vgaTimerList;
+ while (vte->delay) {
+ if ((getGameType() == GType_SIMON1) && vsp->id == 128) {
+ vte++;
+ } else {
+ vte2 = vte;
+ while (vte2->delay) {
+ memcpy(vte2, vte2 + 1, sizeof(VgaTimerEntry));
+ vte2++;
+ }
+ }
+ }
+
+ vcWriteVar(254, 0);
+
+ _lockWord &= ~8;
+}
+
+void SimonEngine::vc28_dummy_op() {
+ /* unused */
+ _vcPtr += 8;
+}
+
+void SimonEngine::vc29_stopAllSounds() {
+ _sound->stopAll();
+}
+
+void SimonEngine::vc30_setFrameRate() {
+ _frameRate = vcReadNextWord();
+}
+
+void SimonEngine::vc31_setWindow() {
+ _windowNum = vcReadNextWord();
+}
+
+uint SimonEngine::vcReadVar(uint var) {
+ assert(var < 255);
+ return (uint16)_variableArray[var];
+}
+
+void SimonEngine::vcWriteVar(uint var, int16 value) {
+ _variableArray[var] = value;
+}
+
+void SimonEngine::vc32_copyVar() {
+ uint16 a = vcReadVar(vcReadNextWord());
+ vcWriteVar(vcReadNextWord(), a);
+}
+
+void SimonEngine::vc33_setMouseOn() {
+ if (_mouseHideCount != 0) {
+ _mouseHideCount = 1;
+ mouseOn();
+ }
+}
+
+void SimonEngine::vc34_setMouseOff() {
+ mouseOff();
+ _mouseHideCount = 200;
+ _leftButtonDown = 0;
+}
+
+void SimonEngine::vc35_clearWindow() {
+ /* unused */
+ _vcPtr += 4;
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc36_setWindowImage() {
+ _updateScreen = false;
+ uint vga_res = vcReadNextWord();
+ uint windowNum = vcReadNextWord();
+
+ if (getGameType() == GType_SIMON1) {
+ if (windowNum == 16) {
+ _copyPartialMode = 2;
+ } else {
+ set_video_mode_internal(windowNum, vga_res);
+ }
+ } else {
+ set_video_mode_internal(windowNum, vga_res);
+ }
+}
+
+void SimonEngine::vc37_addToSpriteY() {
+ VgaSprite *vsp = findCurSprite();
+ vsp->y += vcReadVar(vcReadNextWord());
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc38_skipIfVarZero() {
+ uint var = vcReadNextWord();
+ if (vcReadVar(var) == 0)
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc39_setVar() {
+ uint var = vcReadNextWord();
+ int16 value = vcReadNextWord();
+ vcWriteVar(var, value);
+}
+
+void SimonEngine::vc40() {
+ uint var = vcReadNextWord();
+ int16 value = vcReadVar(var) + vcReadNextWord();
+
+ if ((getGameType() == GType_SIMON2) && var == 15 && !(_bitArray[5] & 1)) {
+ int16 tmp;
+
+ if (_scrollCount != 0) {
+ if (_scrollCount >= 0)
+ goto no_scroll;
+ _scrollCount = 0;
+ } else {
+ if (_scrollFlag != 0)
+ goto no_scroll;
+ }
+
+ if (value - _scrollX >= 30) {
+ _scrollCount = 20;
+ tmp = _scrollXMax - _scrollX;
+ if (tmp < 20)
+ _scrollCount = tmp;
+ add_vga_timer(6, NULL, 0, 0); /* special timer */
+ }
+ }
+no_scroll:;
+
+ vcWriteVar(var, value);
+}
+
+void SimonEngine::vc41() {
+ uint var = vcReadNextWord();
+ int16 value = vcReadVar(var) - vcReadNextWord();
+
+ if ((getGameType() == GType_SIMON2) && var == 15 && !(_bitArray[5] & 1)) {
+ int16 tmp;
+
+ if (_scrollCount != 0) {
+ if (_scrollCount < 0)
+ goto no_scroll;
+ _scrollCount = 0;
+ } else {
+ if (_scrollFlag != 0)
+ goto no_scroll;
+ }
+
+ if ((uint16)(value - _scrollX) < 11) {
+ _scrollCount = -20;
+ tmp = _scrollXMax - _scrollX;
+ if (_scrollX < 20)
+ _scrollCount = -_scrollX;
+ add_vga_timer(6, NULL, 0, 0); /* special timer */
+ }
+ }
+no_scroll:;
+
+ vcWriteVar(var, value);
+}
+
+void SimonEngine::vc42_delayIfNotEQ() {
+ uint val = vcReadVar(vcReadNextWord());
+ if (val != vcReadNextWord()) {
+
+ add_vga_timer(_frameRate + 1, _vcPtr - 4, _vgaCurSpriteId, _vgaCurFileId);
+ _vcPtr = (byte *)&_vc_get_out_of_code;
+ }
+}
+
+void SimonEngine::vc43_skipIfBitClear() {
+ if (!vcGetBit(vcReadNextWord())) {
+ vcSkipNextInstruction();
+ }
+}
+
+void SimonEngine::vc44_skipIfBitSet() {
+ if (vcGetBit(vcReadNextWord())) {
+ vcSkipNextInstruction();
+ }
+}
+
+void SimonEngine::vc45_setSpriteX() {
+ VgaSprite *vsp = findCurSprite();
+ vsp->x = vcReadVar(vcReadNextWord());
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc46_setSpriteY() {
+ VgaSprite *vsp = findCurSprite();
+ vsp->y = vcReadVar(vcReadNextWord());
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc47_addToVar() {
+ uint var = vcReadNextWord();
+ vcWriteVar(var, vcReadVar(var) + vcReadVar(vcReadNextWord()));
+}
+
+void SimonEngine::vc48_setPathFinder() {
+ uint a = (uint16)_variableArray[12];
+ const uint16 *p = _pathFindArray[a - 1];
+
+ if (getGameType() == GType_FF) {
+ VgaSprite *vsp = findCurSprite();
+ int16 x, x2, y, y1, y2, ydiff;
+ uint pos = 0;
+
+ while(vsp->x > readUint16Wrapper(p + 2)) {
+ p += 2;
+ pos++;
+ }
+
+ y1 = readUint16Wrapper(p + 1);
+ x2 = readUint16Wrapper(p + 2);
+ y2 = readUint16Wrapper(p + 3);
+
+ if (x2 != 9999) {
+ ydiff = y2 - y1;
+ if (ydiff < 0) {
+ ydiff = -ydiff;
+ x = vsp->x & 7;
+ ydiff *= x;
+ ydiff /= 8;
+ ydiff = -ydiff;
+ } else {
+ x = vsp->x & 7;
+ ydiff *= x;
+ ydiff /= 8;
+ }
+ y1 += ydiff;
+ }
+
+ y = vsp->y;
+ vsp->y = y1;
+ //checkScrollY(y, diff);
+
+ _variableArray[11] = readUint16Wrapper(p);
+ _variableArray[13] = pos;
+ } else {
+ uint b = (uint16)_variableArray[13];
+ p += b * 2 + 1;
+ int c = _variableArray[14];
+
+ int step;
+ int y1, y2;
+ int16 *vp;
+
+ step = 2;
+ if (c < 0) {
+ c = -c;
+ step = -2;
+ }
+
+ vp = &_variableArray[20];
+
+ do {
+ y2 = readUint16Wrapper(p);
+ p += step;
+ y1 = readUint16Wrapper(p) - y2;
+
+ vp[0] = y1 / 2;
+ vp[1] = y1 - (y1 / 2);
+
+ vp += 2;
+ } while (--c);
+ }
+}
+
+void SimonEngine::vcSetBitTo(uint bit, bool value) {
+ uint16 *bits = &_bitArray[bit >> 4];
+ *bits = (*bits & ~(1 << (bit & 15))) | (value << (bit & 15));
+}
+
+bool SimonEngine::vcGetBit(uint bit) {
+ uint16 *bits = &_bitArray[bit >> 4];
+ return (*bits & (1 << (bit & 15))) != 0;
+}
+
+void SimonEngine::vc49_setBit() {
+ vcSetBitTo(vcReadNextWord(), true);
+}
+
+void SimonEngine::vc50_clearBit() {
+ vcSetBitTo(vcReadNextWord(), false);
+}
+
+void SimonEngine::vc51_clear_hitarea_bit_0x40() {
+ clear_hitarea_bit_0x40(vcReadNextWord());
+}
+
+void SimonEngine::vc52_playSound() {
+ bool ambient = false;
+
+ uint16 sound = vcReadNextWord();
+ if (sound >= 0x8000) {
+ ambient = true;
+ sound = -sound;
+ }
+
+ if (getGameType() == GType_FF) {
+ uint16 pan = vcReadNextWord();
+ uint16 vol = vcReadNextWord();
+ _sound->playSoundData(_curSfxFile, sound, pan, vol, ambient);
+ } else if (getGameType() == GType_SIMON2) {
+ if (ambient) {
+ _sound->playAmbient(sound);
+ } else {
+ _sound->playEffects(sound);
+ }
+ } else if (getFeatures() & GF_TALKIE) {
+ _sound->playEffects(sound);
+ } else if (getGameId() == GID_SIMON1DOS) {
+ playSting(sound);
+ }
+}
+
+void SimonEngine::vc53_no_op() {
+ // Start sound effect, panning it with the animation
+ int snd = vcReadNextWord();
+ int xoffs = vcReadNextWord();
+ int vol = vcReadNextWord();
+ debug(0, "STUB: vc53_no_op: snd %d xoffs %d vol %d", snd, xoffs, vol);
+}
+
+void SimonEngine::vc54_no_op() {
+ /* unused */
+ _vcPtr += 6;
+}
+
+void SimonEngine::vc55_offset_hit_area() {
+ HitArea *ha = _hitAreas;
+ uint count = ARRAYSIZE(_hitAreas);
+ uint16 id = vcReadNextWord();
+ int16 x = vcReadNextWord();
+ int16 y = vcReadNextWord();
+
+ for (;;) {
+ if (ha->id == id) {
+ ha->x += x;
+ ha->y += y;
+ break;
+ }
+ ha++;
+ if (!--count)
+ break;
+ }
+
+ _needHitAreaRecalc++;
+}
+
+void SimonEngine::vc56_delay() {
+ uint num = vcReadVarOrWord() * _frameRate;
+
+ add_vga_timer(num + VGA_DELAY_BASE, _vcPtr, _vgaCurSpriteId, _vgaCurFileId);
+ _vcPtr = (byte *)&_vc_get_out_of_code;
+}
+
+void SimonEngine::vc59() {
+ if (getGameType() == GType_SIMON1) {
+ if (!_sound->isVoiceActive())
+ vcSkipNextInstruction();
+ } else {
+ uint file = vcReadNextWord();
+ uint start = vcReadNextWord();
+ uint end = vcReadNextWord() + 1;
+
+ do {
+ vc_kill_sprite(file, start);
+ } while (++start != end);
+ }
+}
+
+void SimonEngine::vc58() {
+ uint sprite = _vgaCurSpriteId;
+ uint file = _vgaCurFileId;
+ const byte *vc_ptr_org;
+ uint16 tmp;
+
+ _vgaCurFileId = vcReadNextWord();
+ _vgaCurSpriteId = vcReadNextWord();
+
+ tmp = to16Wrapper(vcReadNextWord());
+
+ vc_ptr_org = _vcPtr;
+ _vcPtr = (byte *)&tmp;
+ vc23_setSpritePriority();
+
+ _vcPtr = vc_ptr_org;
+ _vgaCurSpriteId = sprite;
+ _vgaCurFileId = file;
+}
+
+void SimonEngine::vc57_no_op() {
+ /* unused */
+}
+
+void SimonEngine::vc_kill_sprite(uint file, uint sprite) {
+ uint16 old_sprite_id, old_cur_file_id;
+ VgaSleepStruct *vfs;
+ VgaSprite *vsp;
+ VgaTimerEntry *vte;
+ const byte *vc_ptr_org;
+
+ old_sprite_id = _vgaCurSpriteId;
+ old_cur_file_id = _vgaCurFileId;
+ vc_ptr_org = _vcPtr;
+
+ _vgaCurFileId = file;
+ _vgaCurSpriteId = sprite;
+
+ vfs = _vgaSleepStructs;
+ while (vfs->ident != 0) {
+ if (vfs->sprite_id == _vgaCurSpriteId && ((getGameType() == GType_SIMON1) || vfs->cur_vga_file == _vgaCurFileId)) {
+ while (vfs->ident != 0) {
+ memcpy(vfs, vfs + 1, sizeof(VgaSleepStruct));
+ vfs++;
+ }
+ break;
+ }
+ vfs++;
+ }
+
+ vsp = findCurSprite();
+ if (vsp->id) {
+ vc25_halt_sprite();
+
+ vte = _vgaTimerList;
+ while (vte->delay != 0) {
+ if (vte->sprite_id == _vgaCurSpriteId && ((getGameType() == GType_SIMON1) || vte->cur_vga_file == _vgaCurFileId)) {
+ delete_vga_timer(vte);
+ break;
+ }
+ vte++;
+ }
+ }
+
+ _vgaCurFileId = old_cur_file_id;
+ _vgaCurSpriteId = old_sprite_id;
+ _vcPtr = vc_ptr_org;
+}
+
+void SimonEngine::vc60_killSprite() {
+ uint file;
+
+ if (getGameType() == GType_SIMON1) {
+ file = _vgaCurFileId;
+ } else {
+ file = vcReadNextWord();
+ }
+ uint sprite = vcReadNextWord();
+ vc_kill_sprite(file, sprite);
+}
+
+void SimonEngine::vc61_setMaskImage() {
+ VgaSprite *vsp = findCurSprite();
+
+ vsp->image = vcReadVarOrWord();
+
+ vsp->x += vcReadNextWord();
+ vsp->y += vcReadNextWord();
+ vsp->flags = 0x24;
+
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc62_fastFadeOut() {
+ uint i;
+
+ vc29_stopAllSounds();
+
+ if (!_fastFadeOutFlag) {
+ _fastFadeOutFlag = true;
+
+ _videoNumPalColors = 256;
+ if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
+ if (_windowNum == 4)
+ _videoNumPalColors = 208;
+ }
+
+ memcpy(_videoBuf1, _paletteBackup, _videoNumPalColors * 4);
+ for (i = NUM_PALETTE_FADEOUT; i != 0; --i) {
+ palette_fadeout((uint32 *)_videoBuf1, _videoNumPalColors);
+ _system->setPalette(_videoBuf1, 0, _videoNumPalColors);
+ if (_fade)
+ _system->updateScreen();
+ delay(5);
+ }
+
+ if (getGameType() == GType_SIMON1) {
+ uint16 params[5]; /* parameters to vc10_draw */
+ VgaSprite *vsp;
+ VgaPointersEntry *vpe;
+ const byte *vc_ptr_org = _vcPtr;
+
+ vsp = _vgaSprites;
+ while (vsp->id != 0) {
+ if (vsp->id == 128) {
+ byte *old_file_1 = _curVgaFile1;
+ byte *old_file_2 = _curVgaFile2;
+ uint palmode = _windowNum;
+
+ vpe = &_vgaBufferPointers[vsp->fileId];
+ _curVgaFile1 = vpe->vgaFile1;
+ _curVgaFile2 = vpe->vgaFile2;
+ _windowNum = vsp->windowNum;
+
+ params[0] = READ_BE_UINT16(&vsp->image);
+ params[1] = READ_BE_UINT16(&vsp->palette);
+ params[2] = READ_BE_UINT16(&vsp->x);
+ params[3] = READ_BE_UINT16(&vsp->y);
+ params[4] = READ_BE_UINT16(&vsp->flags);
+ _vcPtr = (byte *)params;
+ vc10_draw();
+
+ _windowNum = palmode;
+ _curVgaFile1 = old_file_1;
+ _curVgaFile2 = old_file_2;
+ break;
+ }
+ vsp++;
+ }
+ _vcPtr = vc_ptr_org;
+ }
+
+ // Allow one section of Simon the Sorcerer 1 introduction to be displayed
+ // in lower half of screen
+ if ((getGameType() == GType_SIMON1) && (_subroutine == 2923 || _subroutine == 2926)) {
+ dx_clear_surfaces(200);
+ } else if (getGameType() == GType_FF) {
+ dx_clear_surfaces(480);
+ } else {
+ dx_clear_surfaces(_windowNum == 4 ? 134 : 200);
+ }
+ }
+ if (getGameType() == GType_SIMON2) {
+ if (_nextMusicToPlay != -1)
+ loadMusic(_nextMusicToPlay);
+ }
+
+}
+
+void SimonEngine::vc63_fastFadeIn() {
+ if (getGameType() == GType_FF) {
+ _paletteColorCount = 256;
+ } else {
+ _paletteColorCount = 208;
+ if (_windowNum != 4) {
+ _paletteColorCount = 256;
+ }
+ }
+ _fastFadeOutFlag = false;
+}
+
+void SimonEngine::vc64_skipIfSpeechEnded() {
+ if (!_sound->isVoiceActive() || (_subtitles && _language != Common::HB_ISR))
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc65_slowFadeIn() {
+ _paletteColorCount = 624;
+ _videoNumPalColors = 208;
+ if (_windowNum != 4) {
+ _paletteColorCount = 768;
+ _videoNumPalColors = 256;
+ }
+ _paletteColorCount |= 0x8000;
+ _fastFadeOutFlag = false;
+}
+
+void SimonEngine::vc66_skipIfNotEqual() {
+ uint a = vcReadNextWord();
+ uint b = vcReadNextWord();
+
+ if (vcReadVar(a) != vcReadVar(b))
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc67_skipIfGE() {
+ uint a = vcReadNextWord();
+ uint b = vcReadNextWord();
+
+ if (vcReadVar(a) >= vcReadVar(b))
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc68_skipIfLE() {
+ uint a = vcReadNextWord();
+ uint b = vcReadNextWord();
+
+ if (vcReadVar(a) <= vcReadVar(b))
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc69_playTrack() {
+ int16 track = vcReadNextWord();
+ int16 loop = vcReadNextWord();
+
+ // Jamieson630:
+ // This is a "play track". The original
+ // design stored the track to play if one was
+ // already in progress, so that the next time a
+ // "fill MIDI stream" event occured, the MIDI
+ // player would find the change and switch
+ // tracks. We use a different architecture that
+ // allows for an immediate response here, but
+ // we'll simulate the variable changes so other
+ // scripts don't get thrown off.
+ // NOTE: This opcode looks very similar in function
+ // to vc72(), except that vc72() may allow for
+ // specifying a non-valid track number (999 or -1)
+ // as a means of stopping what music is currently
+ // playing.
+ midi.setLoop(loop != 0);
+ midi.startTrack(track);
+}
+
+void SimonEngine::vc70_queueMusic() {
+ // Simon2
+ uint16 track = vcReadNextWord();
+ uint16 loop = vcReadNextWord();
+
+ // Jamieson630:
+ // This sets the "on end of track" action.
+ // It specifies whether to loop the current
+ // track and, if not, whether to switch to
+ // a different track upon completion.
+ if (track != 0xFFFF && track != 999)
+ midi.queueTrack(track, loop != 0);
+ else
+ midi.setLoop(loop != 0);
+}
+
+void SimonEngine::vc71_checkMusicQueue() {
+ // Jamieson630:
+ // This command skips the next instruction
+ // unless (1) there is a track playing, AND
+ // (2) there is a track queued to play after it.
+ if (!midi.isPlaying (true))
+ vcSkipNextInstruction();
+}
+
+void SimonEngine::vc72_play_track_2() {
+ // Jamieson630:
+ // This is a "play or stop track". Note that
+ // this opcode looks very similar in function
+ // to vc69(), except that this opcode may allow
+ // for specifying a track of 999 or -1 in order to
+ // stop the music. We'll code it that way for now.
+
+ // NOTE: It's possible that when "stopping" a track,
+ // we're supposed to just go on to the next queued
+ // track, if any. Must find out if there is ANY
+ // case where this is used to stop a track in the
+ // first place.
+
+ int16 track = vcReadNextWord();
+ int16 loop = vcReadNextWord();
+
+ if (track == -1 || track == 999) {
+ midi.stop();
+ } else {
+ midi.setLoop (loop != 0);
+ midi.startTrack (track);
+ }
+}
+
+void SimonEngine::vc73_setMark() {
+ vcReadNextByte();
+ _marks |= 1 << vcReadNextByte();
+}
+
+void SimonEngine::vc74_clearMark() {
+ vcReadNextByte();
+ _marks &= ~(1 << vcReadNextByte());
+}
+
+int SimonEngine::getScale(int y, int x) {
+ int z;
+
+ if (y > _baseY) {
+ return((int)(x * (1 + ((y - _baseY) * _scale))));
+ } else {
+ if (x == 0)
+ return(0);
+ if (x < 0) {
+ z = ((int)((x * (1 - ((_baseY - y)* _scale))) - 0.5));
+ if (z >- 2)
+ return(-2);
+ return(z);
+ }
+
+ z=((int)((x * (1 - ((_baseY-y) * _scale))) + 0.5));
+ if (z < 2)
+ return(2);
+
+ return(z);
+ }
+}
+
+void SimonEngine::vc75_setScale() {
+ _baseY = vcReadNextWord();
+ _scale = (float)vcReadNextWord() / 1000000.;
+}
+
+void SimonEngine::vc76_setScaleXOffs() {
+ VgaSprite *vsp = findCurSprite();
+
+ vsp->image = vcReadNextWord();
+ int16 x = vcReadNextWord();
+ int var = vcReadNextWord();
+
+ vsp->x += getScale(vsp->y, x);
+ _variableArray[var] = vsp->x;
+
+ if (_scrollXMax) {
+ // TODO: Scroll check
+ }
+
+ vsp->flags = 0x40;
+}
+
+void SimonEngine::vc77_setScaleYOffs() {
+ VgaSprite *vsp = findCurSprite();
+
+ vsp->image = vcReadNextWord();
+ int16 x = vcReadNextWord();
+ int var = vcReadNextWord();
+
+ vsp->y += getScale(vsp->y, x);
+ _variableArray[var] = vsp->y;
+ vsp->flags = 0x40;
+}
+
+void SimonEngine::vc78_computeXY() {
+ VgaSprite *vsp = findCurSprite();
+
+ uint a = (uint16)_variableArray[12];
+ uint b = (uint16)_variableArray[13];
+
+ const uint16 *p = _pathFindArray[a - 1];
+ p += b * 2;
+
+ uint16 posx = readUint16Wrapper(p);
+ _variableArray[15] = posx;
+ vsp->x = posx;
+
+ uint16 posy = readUint16Wrapper(p + 1);
+ _variableArray[16] = posy;
+ vsp->y = posy;
+
+ vcSetBitTo(85, false);
+ if (vcGetBit(74) == true) {
+ //centreScroll();
+ }
+}
+
+void SimonEngine::vc79_computePosNum() {
+ uint a = (uint16)_variableArray[12];
+ const uint16 *p = _pathFindArray[a - 1];
+ uint pos = 0;
+
+ int16 y = _variableArray[16];
+ while(y > readUint16Wrapper(p + 1)) {
+ p += 2;
+ pos++;
+ }
+
+ _variableArray[13] = pos;
+}
+
+void SimonEngine::vc80_setOverlayImage() {
+ VgaSprite *vsp = findCurSprite();
+
+ vsp->image = vcReadVarOrWord();
+
+ vsp->x += vcReadNextWord();
+ vsp->y += vcReadNextWord();
+ vsp->flags = 0x10;
+
+ _vgaSpriteChanged++;
+}
+
+void SimonEngine::vc81_setRandom() {
+ uint var = vcReadNextWord();
+ uint value = vcReadNextWord();
+ writeVariable(var, _rnd.getRandomNumber(value - 1));
+}
+
+void SimonEngine::vc82_getPathValue() {
+ uint8 val;
+
+ uint16 var = vcReadNextWord();
+
+ if (vcGetBit(82) == true) {
+ val = _pathValues1[_GPVCount1++];
+ } else {
+ val = _pathValues[_GPVCount++];
+ }
+
+ writeVariable(var, val);
+}
+
+void SimonEngine::vc83_playSoundLoop() {
+ // Start looping sound effect
+ int snd = vcReadNextWord();
+ int vol = vcReadNextWord();
+ int pan = vcReadNextWord();
+ debug(0, "STUB: vc83_playSoundLoop: snd %d vol %d pan %d", snd, vol, pan);
+}
+
+void SimonEngine::vc84_stopSoundLoop() {
+ // Stop looping sound effect
+ debug(0, "STUB: vc84_stopSoundLoop");
+}
+
+} // End of namespace Simon
diff --git a/engines/simon/vga.h b/engines/simon/vga.h
new file mode 100644
index 0000000000..2dd77633de
--- /dev/null
+++ b/engines/simon/vga.h
@@ -0,0 +1,129 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SIMON_VGA_H
+#define SIMON_VGA_H
+
+namespace Simon {
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+// Feeble Files
+struct VgaFileHeader_Feeble {
+ uint16 x_1;
+ uint16 hdr2_start;
+ uint16 x_2, x_3;
+} GCC_PACK;
+
+struct VgaFileHeader2_Feeble {
+ uint16 imageCount;
+ uint16 x_2;
+ uint16 animationCount;
+ uint16 x_3;
+ uint16 imageTable;
+ uint16 x_4;
+ uint16 animationTable;
+ uint16 x_5;
+} GCC_PACK;
+
+struct ImageHeader_Feeble {
+ uint16 id;
+ uint16 x_1;
+ uint16 scriptOffs;
+ uint16 x_2;
+} GCC_PACK;
+
+struct AnimationHeader_Feeble {
+ uint16 scriptOffs;
+ uint16 x_2;
+ uint16 id;
+} GCC_PACK;
+
+// Simon 1/2
+struct VgaFileHeader_Simon {
+ uint16 x_1, x_2;
+ uint16 hdr2_start;
+ uint16 x_3, x_4;
+} GCC_PACK;
+
+struct VgaFileHeader2_Simon {
+ uint16 x_1;
+ uint16 imageCount;
+ uint16 x_2;
+ uint16 animationCount;
+ uint16 x_3;
+ uint16 imageTable;
+ uint16 x_4;
+ uint16 animationTable;
+ uint16 x_5;
+} GCC_PACK;
+
+struct ImageHeader_Simon {
+ uint16 id;
+ uint16 x_1;
+ uint16 x_2;
+ uint16 scriptOffs;
+} GCC_PACK;
+
+struct AnimationHeader_Simon {
+ uint16 id;
+ uint16 x_2;
+ uint16 scriptOffs;
+} GCC_PACK;
+
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+struct VC10_state {
+ int image;
+ uint16 flags;
+ int x, y;
+
+ byte palette;
+
+ uint width, height;
+ uint draw_width, draw_height;
+ uint x_skip, y_skip;
+
+ byte *surf2_addr;
+ uint surf2_pitch;
+
+ byte *surf_addr;
+ uint surf_pitch;
+
+ uint16 dl, dh;
+
+ const byte *depack_src;
+ int8 depack_cont;
+
+ byte depack_dest[480];
+};
+
+byte *vc10_depack_column(VC10_state *vs);
+
+} // End of namespace Simon
+
+#endif
diff --git a/engines/sky/autoroute.cpp b/engines/sky/autoroute.cpp
new file mode 100644
index 0000000000..15c00ff028
--- /dev/null
+++ b/engines/sky/autoroute.cpp
@@ -0,0 +1,285 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "sky/autoroute.h"
+#include "sky/compact.h"
+#include "sky/grid.h"
+#include "sky/skydefs.h"
+#include "sky/struc.h"
+
+namespace Sky {
+
+#define ROUTE_GRID_WIDTH ((GAME_SCREEN_WIDTH/8)+2)
+#define ROUTE_GRID_HEIGHT ((GAME_SCREEN_HEIGHT/8)+2)
+#define ROUTE_GRID_SIZE (ROUTE_GRID_WIDTH*ROUTE_GRID_HEIGHT*2)
+#define WALK_JUMP 8 // walk in blocks of 8
+
+const int16 AutoRoute::_routeDirections[4] = { -1, 1, -ROUTE_GRID_WIDTH, ROUTE_GRID_WIDTH };
+const uint16 AutoRoute::_logicCommands[4] = { RIGHTY, LEFTY, DOWNY, UPY };
+
+AutoRoute::AutoRoute(Grid *pGrid, SkyCompact *compact) {
+
+ _grid = pGrid;
+ _skyCompact = compact;
+ _routeGrid = (uint16 *)malloc(ROUTE_GRID_SIZE);
+ _routeBuf = (uint16 *)malloc(ROUTE_SPACE);
+}
+
+AutoRoute::~AutoRoute(void) {
+
+ free(_routeGrid);
+ free(_routeBuf);
+}
+
+uint16 AutoRoute::checkBlock(uint16 *blockPos) {
+
+ uint16 retVal = 0xFFFF;
+
+ for (uint8 cnt = 0; cnt < 4; cnt++) {
+ uint16 fieldVal = *(blockPos + _routeDirections[cnt]);
+ if (fieldVal && (fieldVal < retVal))
+ retVal = fieldVal;
+ }
+ return retVal;
+}
+
+void AutoRoute::clipCoordX(uint16 x, uint8 &blkX, int16 &initX) {
+ if (x < TOP_LEFT_X) {
+ blkX = 0;
+ initX = x - TOP_LEFT_X;
+ } else if (x >= TOP_LEFT_X + GAME_SCREEN_WIDTH) {
+ blkX = (GAME_SCREEN_WIDTH - 1) >> 3;
+ initX = x - (TOP_LEFT_X + GAME_SCREEN_WIDTH - 1);
+ } else {
+ blkX = (x - TOP_LEFT_X) >> 3;
+ initX = 0;
+ }
+}
+
+void AutoRoute::clipCoordY(uint16 y, uint8 &blkY, int16 &initY) {
+ if (y < TOP_LEFT_Y) {
+ blkY = 0;
+ initY = y - TOP_LEFT_Y;
+ } else if (y >= TOP_LEFT_Y + GAME_SCREEN_HEIGHT) {
+ blkY = (GAME_SCREEN_HEIGHT - 1) >> 3;
+ initY = y - (TOP_LEFT_Y + GAME_SCREEN_HEIGHT);
+ } else {
+ blkY = (y - TOP_LEFT_Y) >> 3;
+ initY = 0;
+ }
+}
+
+void AutoRoute::initWalkGrid(uint8 screen, uint8 width) {
+
+ uint16 *wGridPos;
+ uint8 stretch = 0;
+ uint8 *screenGrid = _grid->giveGrid(screen);
+ screenGrid += GRID_SIZE;
+ wGridPos = _routeGrid + (ROUTE_GRID_SIZE >> 1) - ROUTE_GRID_WIDTH - 2;
+
+ memset(_routeGrid, 0, ROUTE_GRID_SIZE);
+ uint8 bitsLeft = 0; uint32 gridData = 0;
+ for (uint8 gridCntY = 0; gridCntY < ROUTE_GRID_HEIGHT - 2; gridCntY++) {
+ for (uint8 gridCntX = 0; gridCntX < ROUTE_GRID_WIDTH - 2; gridCntX++) {
+ if (!bitsLeft) {
+ screenGrid -= 4;
+ gridData = READ_LE_UINT32(screenGrid);
+ bitsLeft = 32;
+ }
+ if (gridData & 1) {
+ *wGridPos = 0xFFFF; // block is not accessible
+ stretch = width;
+ } else if (stretch) {
+ *wGridPos = 0xFFFF;
+ stretch--;
+ }
+ wGridPos--;
+ bitsLeft--;
+ gridData >>= 1;
+ }
+ wGridPos -= 2;
+ stretch = 0;
+ }
+}
+
+bool AutoRoute::calcWalkGrid(uint8 startX, uint8 startY, uint8 destX, uint8 destY) {
+
+ int16 directionX, directionY;
+ uint8 roiX, roiY; // Rectangle Of Interest in the walk grid
+ if (startY > destY) {
+ directionY = -ROUTE_GRID_WIDTH;
+ roiY = startY;
+ } else {
+ directionY = ROUTE_GRID_WIDTH;
+ roiY = (ROUTE_GRID_HEIGHT-1) - startY;
+ }
+ if (startX > destX) {
+ directionX = -1;
+ roiX = startX + 2;
+ } else {
+ directionX = 1;
+ roiX = (ROUTE_GRID_WIDTH - 1) - startX;
+ }
+
+ uint16 *walkDest = _routeGrid + (destY + 1) * ROUTE_GRID_WIDTH + destX + 1;
+ uint16 *walkStart = _routeGrid + (startY + 1) * ROUTE_GRID_WIDTH + startX + 1;
+ *walkStart = 1;
+
+ // if we are on the edge, move diagonally from start
+ if (roiY < ROUTE_GRID_HEIGHT-3)
+ walkStart -= directionY;
+
+ if (roiX < ROUTE_GRID_WIDTH-2)
+ walkStart -= directionX;
+
+ bool gridChanged = true;
+ bool foundRoute = false;
+
+ while ((!foundRoute) && gridChanged) {
+ gridChanged = false;
+ uint16 *yWalkCalc = walkStart;
+ for (uint8 cnty = 0; cnty < roiY; cnty++) {
+ uint16 *xWalkCalc = yWalkCalc;
+ for (uint8 cntx = 0; cntx < roiX; cntx++) {
+ if (!*xWalkCalc) { // block wasn't done, yet
+ uint16 blockRet = checkBlock(xWalkCalc);
+ if (blockRet < 0xFFFF) {
+ *xWalkCalc = blockRet + 1;
+ gridChanged = true;
+ }
+ }
+ xWalkCalc += directionX;
+ }
+ yWalkCalc += directionY;
+ }
+ if (*walkDest) { // okay, finished
+ foundRoute = true;
+ } else { // we couldn't find the route, let's extend the ROI
+ if (roiY < ROUTE_GRID_HEIGHT - 4) {
+ walkStart -= directionY;
+ roiY++;
+ }
+ if (roiX < ROUTE_GRID_WIDTH - 4) {
+ walkStart -= directionX;
+ roiX++;
+ }
+ }
+ }
+ return foundRoute;
+}
+
+uint16 *AutoRoute::makeRouteData(uint8 startX, uint8 startY, uint8 destX, uint8 destY) {
+
+ memset(_routeBuf, 0, ROUTE_SPACE);
+
+ uint16 *routePos = _routeGrid + (destY + 1) * ROUTE_GRID_WIDTH + destX + 1;
+ uint16 *dataTrg = _routeBuf + (ROUTE_SPACE >> 1) - 2;
+
+ uint16 lastVal = (*routePos) - 1;
+ while (lastVal) { // lastVal == 0 means route is done.
+ dataTrg -= 2;
+
+ int16 walkDirection = 0;
+ for (uint8 cnt = 0; cnt < 4; cnt++)
+ if (lastVal == *(routePos + _routeDirections[cnt])) {
+ *(dataTrg + 1) = _logicCommands[cnt];
+ walkDirection = _routeDirections[cnt];
+ break;
+ }
+
+ if (!walkDirection)
+ error("makeRouteData:: can't find way through walkGrid (pos %d)", lastVal);
+ while (lastVal && (lastVal == *(routePos + walkDirection))) {
+ *dataTrg += WALK_JUMP;
+ lastVal--;
+ routePos += walkDirection;
+ }
+ }
+ return dataTrg;
+}
+
+uint16 *AutoRoute::checkInitMove(uint16 *data, int16 initStaX) {
+ if (initStaX < 0) {
+ data -= 2;
+ *(data + 1) = RIGHTY;
+ *data = ((-initStaX) + 7) & 0xFFF8;
+ } else if (initStaX > 0) {
+ data -= 2;
+ *(data + 1) = LEFTY;
+ *data = (initStaX + 7) & 0xFFF8;
+ }
+ return data;
+}
+
+uint16 AutoRoute::autoRoute(Compact *cpt) {
+
+ uint8 cptScreen = (uint8)cpt->screen;
+ uint8 cptWidth = (uint8)SkyCompact::getMegaSet(cpt)->gridWidth;
+ initWalkGrid(cptScreen, cptWidth);
+
+ uint8 startX, startY, destX, destY;
+ int16 initStaX, initStaY, initDestX, initDestY;
+
+ clipCoordX(cpt->xcood, startX, initStaX);
+ clipCoordY(cpt->ycood, startY, initStaY);
+ clipCoordX(cpt->arTargetX, destX, initDestX);
+ clipCoordY(cpt->arTargetY, destY, initDestY);
+
+ uint16 *routeDest = (uint16*)_skyCompact->fetchCpt(cpt->animScratchId);
+ memset(routeDest, 0, 64);
+ if ((startX == destX) && (startY == destY))
+ return 2;
+
+ if (_routeGrid[(destY + 1) * ROUTE_GRID_WIDTH + destX + 1]) {
+ //if ((cpt == &Sky::SkyCompact::foster) && (cptScreen == 12) && (destX == 2) && (destY == 14)) {
+ if (_skyCompact->cptIsId(cpt, CPT_FOSTER) && (cptScreen == 12) && (destX == 2) && (destY == 14)) {
+ /* workaround for Scriptbug #1043047
+ In screen 12 (the pipe factory) Joey can block Foster's target
+ coordinates (2/14). This is normally not too tragic, but in the
+ scene when foster gets thrown out by Lamb (first time you enter
+ the pipe factory), the game would enter an infinite loop. */
+ _routeGrid[(destY + 1) * ROUTE_GRID_WIDTH + destX + 1] = 0;
+ // hide this part joey from the grid
+ } else
+ return 1; // AR destination is an unaccessible block
+ }
+
+ if (!calcWalkGrid(startX, startY, destX, destY))
+ return 1; // can't find route to block
+
+ uint16 *routeData = makeRouteData(startX, startY, destX, destY);
+ // the route is done.
+ // if there was an initial x movement (due to clipping) tag it onto the start
+ routeData = checkInitMove(routeData, initStaX);
+
+ uint8 cnt = 0;
+ do {
+ routeDest[cnt] = routeData[cnt];
+ routeDest[cnt + 1] = routeData[cnt + 1];
+ cnt += 2;
+ } while (routeData[cnt - 2]);
+ return 0;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/autoroute.h b/engines/sky/autoroute.h
new file mode 100644
index 0000000000..060bc9acb1
--- /dev/null
+++ b/engines/sky/autoroute.h
@@ -0,0 +1,59 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef AUTOROUTE_H
+#define AUTOROUTE_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+
+namespace Sky {
+
+struct Compact;
+class Grid;
+class SkyCompact;
+
+class AutoRoute {
+public:
+ AutoRoute(Grid *pGrid, SkyCompact *compact);
+ ~AutoRoute(void);
+ uint16 autoRoute(Compact *cpt);
+private:
+ uint16 checkBlock(uint16 *blockPos);
+ void clipCoordX(uint16 x, uint8 &blkX, int16 &initX);
+ void clipCoordY(uint16 y, uint8 &blkY, int16 &initY);
+ void initWalkGrid(uint8 screen, uint8 width);
+ bool calcWalkGrid(uint8 startX, uint8 startY, uint8 destX, uint8 destY);
+ uint16 *makeRouteData(uint8 startX, uint8 startY, uint8 destX, uint8 destY);
+ uint16 *checkInitMove(uint16 *data, int16 initStaX);
+ Grid *_grid;
+ SkyCompact *_skyCompact;
+ uint16 *_routeGrid;
+ uint16 *_routeBuf;
+ static const int16 _routeDirections[4];
+ static const uint16 _logicCommands[4];
+};
+
+} // End of namespace Sky
+
+#endif // AUTOROUTE_H
+
diff --git a/engines/sky/compact.cpp b/engines/sky/compact.cpp
new file mode 100644
index 0000000000..a7d9ee69e6
--- /dev/null
+++ b/engines/sky/compact.cpp
@@ -0,0 +1,452 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "common/file.h"
+#include "sky/compact.h"
+#include "gui/message.h"
+
+namespace Sky {
+
+#define SKY_CPT_SIZE 419427
+
+#define OFFS(type,item) (((long)(&((type*)0)->item)))
+#define MK32(type,item) OFFS(type, item),0,0,0
+#define MK16(type,item) OFFS(type, item),0
+#define MK32_A5(type, item) MK32(type, item[0]), MK32(type, item[1]), \
+ MK32(type, item[2]), MK32(type, item[3]), MK32(type, item[4])
+
+static const uint32 compactOffsets[] = {
+ MK16(Compact, logic),
+ MK16(Compact, status),
+ MK16(Compact, sync),
+ MK16(Compact, screen),
+ MK16(Compact, place),
+ MK32(Compact, getToTableId),
+ MK16(Compact, xcood),
+ MK16(Compact, ycood),
+ MK16(Compact, frame),
+ MK16(Compact, cursorText),
+ MK16(Compact, mouseOn),
+ MK16(Compact, mouseOff),
+ MK16(Compact, mouseClick),
+ MK16(Compact, mouseRelX),
+ MK16(Compact, mouseRelY),
+ MK16(Compact, mouseSizeX),
+ MK16(Compact, mouseSizeY),
+ MK16(Compact, actionScript),
+ MK16(Compact, upFlag),
+ MK16(Compact, downFlag),
+ MK16(Compact, getToFlag),
+ MK16(Compact, flag),
+ MK16(Compact, mood),
+ MK32(Compact, grafixProgId),
+ MK16(Compact, offset),
+ MK16(Compact, mode),
+ MK16(Compact, baseSub),
+ MK16(Compact, baseSub_off),
+ MK16(Compact, actionSub),
+ MK16(Compact, actionSub_off),
+ MK16(Compact, getToSub),
+ MK16(Compact, getToSub_off),
+ MK16(Compact, extraSub),
+ MK16(Compact, extraSub_off),
+ MK16(Compact, dir),
+ MK16(Compact, stopScript),
+ MK16(Compact, miniBump),
+ MK16(Compact, leaving),
+ MK16(Compact, atWatch),
+ MK16(Compact, atWas),
+ MK16(Compact, alt),
+ MK16(Compact, request),
+ MK16(Compact, spWidth_xx),
+ MK16(Compact, spColour),
+ MK16(Compact, spTextId),
+ MK16(Compact, spTime),
+ MK16(Compact, arAnimIndex),
+ MK32(Compact, turnProgId),
+ MK16(Compact, waitingFor),
+ MK16(Compact, arTargetX),
+ MK16(Compact, arTargetY),
+ MK32(Compact, animScratchId),
+ MK16(Compact, megaSet),
+};
+
+static const uint32 megaSetOffsets[] = {
+ MK16(MegaSet, gridWidth),
+ MK16(MegaSet, colOffset),
+ MK16(MegaSet, colWidth),
+ MK16(MegaSet, lastChr),
+ MK32(MegaSet, animUpId),
+ MK32(MegaSet, animDownId),
+ MK32(MegaSet, animLeftId),
+ MK32(MegaSet, animRightId),
+ MK32(MegaSet, standUpId),
+ MK32(MegaSet, standDownId),
+ MK32(MegaSet, standLeftId),
+ MK32(MegaSet, standRightId),
+ MK32(MegaSet, standTalkId),
+};
+
+static const uint32 turnTableOffsets[] = {
+ MK32_A5(TurnTable, turnTableUp),
+ MK32_A5(TurnTable, turnTableDown),
+ MK32_A5(TurnTable, turnTableLeft),
+ MK32_A5(TurnTable, turnTableRight),
+ MK32_A5(TurnTable, turnTableTalk),
+};
+
+#define COMPACT_SIZE (sizeof(compactOffsets)/sizeof(uint32))
+#define MEGASET_SIZE (sizeof(megaSetOffsets)/sizeof(uint32))
+#define TURNTABLE_SIZE (sizeof(turnTableOffsets)/sizeof(uint32))
+
+SkyCompact::SkyCompact(void) {
+ _cptFile = new Common::File();
+ if (!_cptFile->open("sky.cpt")) {
+ GUI::MessageDialog dialog("Unable to find \"sky.cpt\" file!\n"
+ "Please download it from www.scummvm.org", "OK", NULL);
+ dialog.runModal();
+ error("Unable to find \"sky.cpt\" file\nPlease download it from www.scummvm.org");
+ }
+
+ uint16 fileVersion = _cptFile->readUint16LE();
+ if (fileVersion != 0)
+ error("unknown \"sky.cpt\" version");
+
+ if (SKY_CPT_SIZE != _cptFile->size()) {
+ GUI::MessageDialog dialog("The \"sky.cpt\" file has an incorrect size.\nPlease (re)download it from www.scummvm.org", "OK", NULL);
+ dialog.runModal();
+ error("Incorrect sky.cpt size (%d, expected: %d)", _cptFile->size(), SKY_CPT_SIZE);
+ }
+
+ // set the necessary data structs up...
+ _numDataLists = _cptFile->readUint16LE();
+ _cptNames = (char***)malloc(_numDataLists * sizeof(char**));
+ _dataListLen = (uint16 *)malloc(_numDataLists * sizeof(uint16));
+ _cptSizes = (uint16 **)malloc(_numDataLists * sizeof(uint16*));
+ _cptTypes = (uint16 **)malloc(_numDataLists * sizeof(uint16*));
+ _compacts = (Compact***)malloc(_numDataLists * sizeof(Compact**));
+
+ for (int i = 0; i < _numDataLists; i++) {
+ _dataListLen[i] = _cptFile->readUint16LE();
+ _cptNames[i] = (char**)malloc(_dataListLen[i] * sizeof(char*));
+ _cptSizes[i] = (uint16 *)malloc(_dataListLen[i] * sizeof(uint16));
+ _cptTypes[i] = (uint16 *)malloc(_dataListLen[i] * sizeof(uint16));
+ _compacts[i] = (Compact**)malloc(_dataListLen[i] * sizeof(Compact*));
+ }
+
+ uint32 rawSize = _cptFile->readUint32LE() * sizeof(uint16);
+ uint16 *rawPos = _rawBuf = (uint16*)malloc(rawSize);
+
+ uint32 srcSize = _cptFile->readUint32LE() * sizeof(uint16);
+ uint16 *srcBuf = (uint16*)malloc(srcSize);
+ uint16 *srcPos = srcBuf;
+ _cptFile->read(srcBuf, srcSize);
+
+ uint32 asciiSize = _cptFile->readUint32LE();
+ char *asciiPos = _asciiBuf = (char*)malloc(asciiSize);
+ _cptFile->read(_asciiBuf, asciiSize);
+
+ // and fill them with the compact data
+ for (uint32 lcnt = 0; lcnt < _numDataLists; lcnt++) {
+ for (uint32 ecnt = 0; ecnt < _dataListLen[lcnt]; ecnt++) {
+ _cptSizes[lcnt][ecnt] = READ_LE_UINT16(srcPos++);
+ if (_cptSizes[lcnt][ecnt]) {
+ _cptTypes[lcnt][ecnt] = READ_LE_UINT16(srcPos++);
+ _compacts[lcnt][ecnt] = (Compact*)rawPos;
+ _cptNames[lcnt][ecnt] = asciiPos;
+ asciiPos += strlen(asciiPos) + 1;
+
+ for (uint16 elemCnt = 0; elemCnt < _cptSizes[lcnt][ecnt]; elemCnt++)
+ *rawPos++ = READ_LE_UINT16(srcPos++);
+ } else {
+ _cptTypes[lcnt][ecnt] = 0;
+ _compacts[lcnt][ecnt] = NULL;
+ _cptNames[lcnt][ecnt] = NULL;
+ }
+ }
+ }
+ free(srcBuf);
+
+ uint16 numDlincs = _cptFile->readUint16LE();
+ uint16 *dlincBuf = (uint16*)malloc(numDlincs * 2 * sizeof(uint16));
+ uint16 *dlincPos = dlincBuf;
+ _cptFile->read(dlincBuf, numDlincs * 2 * sizeof(uint16));
+ // these compacts don't actually exist but only point to other ones...
+ uint16 cnt;
+ for (cnt = 0; cnt < numDlincs; cnt++) {
+ uint16 dlincId = READ_LE_UINT16(dlincPos++);
+ uint16 destId = READ_LE_UINT16(dlincPos++);
+ assert(((dlincId >> 12) < _numDataLists) && ((dlincId & 0xFFF) < _dataListLen[dlincId >> 12]) && (_compacts[dlincId >> 12][dlincId & 0xFFF] == NULL));
+ _compacts[dlincId >> 12][dlincId & 0xFFF] = _compacts[destId >> 12][destId & 0xFFF];
+
+ assert(_cptNames[dlincId >> 12][dlincId & 0xFFF] == NULL);
+ _cptNames[dlincId >> 12][dlincId & 0xFFF] = asciiPos;
+ asciiPos += strlen(asciiPos) + 1;
+ }
+ free(dlincBuf);
+
+ // if this is v0.0288, parse this diff data
+ uint16 numDiffs = _cptFile->readUint16LE();
+ uint16 diffSize = _cptFile->readUint16LE();
+ uint16 *diffBuf = (uint16*)malloc(diffSize * sizeof(uint16));
+ _cptFile->read(diffBuf, diffSize * sizeof(uint16));
+ if (SkyEngine::_systemVars.gameVersion == 288) {
+ uint16 *diffPos = diffBuf;
+ for (cnt = 0; cnt < numDiffs; cnt++) {
+ uint16 cptId = READ_LE_UINT16(diffPos++);
+ uint16 *rawCpt = (uint16*)fetchCpt(cptId);
+ rawCpt += READ_LE_UINT16(diffPos++);
+ uint16 len = READ_LE_UINT16(diffPos++);
+ for (uint16 elemCnt = 0; elemCnt < len; elemCnt++)
+ rawCpt[elemCnt] = READ_LE_UINT16(diffPos++);
+ }
+ assert(diffPos == (diffBuf + diffSize));
+ }
+ free(diffBuf);
+
+ // these are the IDs that have to be saved into savegame files.
+ _numSaveIds = _cptFile->readUint16LE();
+ _saveIds = (uint16*)malloc(_numSaveIds * sizeof(uint16));
+ _cptFile->read(_saveIds, _numSaveIds * sizeof(uint16));
+ for (cnt = 0; cnt < _numSaveIds; cnt++)
+ _saveIds[cnt] = FROM_LE_16(_saveIds[cnt]);
+ _resetDataPos = _cptFile->pos();
+}
+
+SkyCompact::~SkyCompact(void) {
+ free(_rawBuf);
+ free(_asciiBuf);
+ for (int i = 0; i < _numDataLists; i++) {
+ free(_compacts[i]);
+ free(_cptNames[i]);
+ free(_cptSizes[i]);
+ }
+ free(_compacts);
+ free(_cptNames);
+ free(_cptSizes);
+ _cptFile->close();
+ delete _cptFile;
+}
+
+// needed for some workaround where the engine has to check if it's currently processing joey, for example
+bool SkyCompact::cptIsId(Compact *cpt, uint16 id) {
+ return (cpt == fetchCpt(id));
+}
+
+Compact *SkyCompact::fetchCpt(uint16 cptId) {
+ if (cptId == 0xFFFF) // is this really still necessary?
+ return NULL;
+ assert(((cptId >> 12) < _numDataLists) && ((cptId & 0xFFF) < _dataListLen[cptId >> 12]));
+ return _compacts[cptId >> 12][cptId & 0xFFF];
+}
+
+Compact *SkyCompact::fetchCptInfo(uint16 cptId, uint16 *elems, uint16 *type, char *name) {
+ assert(((cptId >> 12) < _numDataLists) && ((cptId & 0xFFF) < _dataListLen[cptId >> 12]));
+ if (elems)
+ *elems = _cptSizes[cptId >> 12][cptId & 0xFFF];
+ if (type)
+ *type = _cptTypes[cptId >> 12][cptId & 0xFFF];
+ if (name)
+ strcpy(name, _cptNames[cptId >> 12][cptId & 0xFFF]);
+ return fetchCpt(cptId);
+}
+
+uint16 *SkyCompact::getSub(Compact *cpt, uint16 mode) {
+ switch (mode) {
+ case 0:
+ return &(cpt->baseSub);
+ case 2:
+ return &(cpt->baseSub_off);
+ case 4:
+ return &(cpt->actionSub);
+ case 6:
+ return &(cpt->actionSub_off);
+ case 8:
+ return &(cpt->getToSub);
+ case 10:
+ return &(cpt->getToSub_off);
+ case 12:
+ return &(cpt->extraSub);
+ case 14:
+ return &(cpt->extraSub_off);
+ default:
+ error("Invalid Mode (%d)", mode);
+ }
+}
+
+uint16 *SkyCompact::getGrafixPtr(Compact *cpt) {
+ uint16 *gfxBase = (uint16*)fetchCpt(cpt->grafixProgId);
+ if (gfxBase == NULL)
+ return NULL;
+
+ return gfxBase + cpt->grafixProgPos;
+}
+
+/**
+ * Returns the n'th mega set specified by \a megaSet from Compact \a cpt.
+ */
+MegaSet *SkyCompact::getMegaSet(Compact *cpt) {
+ switch (cpt->megaSet) {
+ case 0:
+ return &cpt->megaSet0;
+ case NEXT_MEGA_SET:
+ return &cpt->megaSet1;
+ case NEXT_MEGA_SET*2:
+ return &cpt->megaSet2;
+ case NEXT_MEGA_SET*3:
+ return &cpt->megaSet3;
+ default:
+ error("Invalid MegaSet (%d)", cpt->megaSet);
+ }
+}
+
+/**
+ \brief Returns the turn table for direction \a dir
+ from Compact \a cpt in \a megaSet.
+
+ Functionally equivalent to:
+ \verbatim
+ clear eax
+ mov al,20
+ mul (cpt[esi]).c_dir
+ add ax,(cpt[esi]).c_mega_set
+ lea eax,(cpt[esi+eax]).c_turn_table_up
+ \endverbatim
+*/
+uint16 *SkyCompact::getTurnTable(Compact *cpt, uint16 dir) {
+ MegaSet *m = getMegaSet(cpt);
+ TurnTable *turnTable = (TurnTable*)fetchCpt(m->turnTableId);
+ switch (dir) {
+ case 0:
+ return turnTable->turnTableUp;
+ case 1:
+ return turnTable->turnTableDown;
+ case 2:
+ return turnTable->turnTableLeft;
+ case 3:
+ return turnTable->turnTableRight;
+ case 4:
+ return turnTable->turnTableTalk;
+ default:
+ error("No TurnTable (%d) in MegaSet (%d)", dir, cpt->megaSet);
+ }
+}
+
+void *SkyCompact::getCompactElem(Compact *cpt, uint16 off) {
+ if (off < COMPACT_SIZE)
+ return((uint8 *)cpt + compactOffsets[off]);
+ off -= COMPACT_SIZE;
+
+ if (off < MEGASET_SIZE)
+ return((uint8 *)&(cpt->megaSet0) + megaSetOffsets[off]);
+
+ off -= MEGASET_SIZE;
+ if (off < TURNTABLE_SIZE)
+ return ((uint8 *)fetchCpt(cpt->megaSet0.turnTableId) + turnTableOffsets[off]);
+
+ off -= TURNTABLE_SIZE;
+ if (off < MEGASET_SIZE)
+ return((uint8 *)&(cpt->megaSet1) + megaSetOffsets[off]);
+
+ off -= MEGASET_SIZE;
+ if (off < TURNTABLE_SIZE)
+ return ((uint8 *)fetchCpt(cpt->megaSet1.turnTableId) + turnTableOffsets[off]);
+
+ off -= TURNTABLE_SIZE;
+ if (off < MEGASET_SIZE)
+ return((uint8 *)&(cpt->megaSet2) + megaSetOffsets[off]);
+
+ off -= MEGASET_SIZE;
+ if (off < TURNTABLE_SIZE)
+ return ((uint8 *)fetchCpt(cpt->megaSet2.turnTableId) + turnTableOffsets[off]);
+
+ off -= TURNTABLE_SIZE;
+ if (off < MEGASET_SIZE)
+ return((uint8 *)&(cpt->megaSet3) + megaSetOffsets[off]);
+
+ off -= MEGASET_SIZE;
+ if (off < TURNTABLE_SIZE)
+ return ((uint8 *)fetchCpt(cpt->megaSet3.turnTableId) + turnTableOffsets[off]);
+ off -= TURNTABLE_SIZE;
+
+ error("Offset %X out of bounds of compact", off + COMPACT_SIZE + 4 * MEGASET_SIZE + 4 * TURNTABLE_SIZE);
+}
+
+uint8 *SkyCompact::createResetData(uint16 gameVersion) {
+ _cptFile->seek(_resetDataPos);
+ uint32 dataSize = _cptFile->readUint16LE() * sizeof(uint16);
+ uint16 *resetBuf = (uint16*)malloc(dataSize);
+ _cptFile->read(resetBuf, dataSize);
+ uint16 numDiffs = _cptFile->readUint16LE();
+ for (uint16 cnt = 0; cnt < numDiffs; cnt++) {
+ uint16 version = _cptFile->readUint16LE();
+ uint16 diffFields = _cptFile->readUint16LE();
+ if (version == gameVersion) {
+ for (uint16 diffCnt = 0; diffCnt < diffFields; diffCnt++) {
+ uint16 pos = _cptFile->readUint16LE();
+ resetBuf[pos] = TO_LE_16(_cptFile->readUint16LE());
+ }
+ return (uint8*)resetBuf;
+ } else
+ _cptFile->seek(diffFields * 2 * sizeof(uint16), SEEK_CUR);
+ }
+ free(resetBuf);
+ error("Unable to find reset data for Beneath a Steel Sky Version 0.0%03d", gameVersion);
+}
+
+// - debugging functions
+
+uint16 SkyCompact::findCptId(void *cpt) {
+ for (uint16 listCnt = 0; listCnt < _numDataLists; listCnt++)
+ for (uint16 elemCnt = 0; elemCnt < _dataListLen[listCnt]; elemCnt++)
+ if (_compacts[listCnt][elemCnt] == cpt)
+ return (listCnt << 12) | elemCnt;
+ // not found
+ debug(1, "Id for Compact %p wasn't found!", cpt);
+ return 0;
+}
+
+uint16 SkyCompact::findCptId(const char *cptName) {
+ for (uint16 listCnt = 0; listCnt < _numDataLists; listCnt++)
+ for (uint16 elemCnt = 0; elemCnt < _dataListLen[listCnt]; elemCnt++)
+ if (_cptNames[listCnt][elemCnt] != 0)
+ if (scumm_stricmp(cptName, _cptNames[listCnt][elemCnt]) == 0)
+ return (listCnt << 12) | elemCnt;
+ // not found
+ debug(1, "Id for Compact %s wasn't found!", cptName);
+ return 0;
+}
+
+uint16 SkyCompact::giveNumDataLists(void) {
+ return _numDataLists;
+}
+
+uint16 SkyCompact::giveDataListLen(uint16 listNum) {
+ if (listNum >= _numDataLists) // list doesn't exist
+ return 0;
+ else
+ return _dataListLen[listNum];
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/compact.h b/engines/sky/compact.h
new file mode 100644
index 0000000000..7f8abbd516
--- /dev/null
+++ b/engines/sky/compact.h
@@ -0,0 +1,93 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYCOMPACT_H
+#define SKYCOMPACT_H
+
+#include "sky/sky.h"
+#include "sky/struc.h"
+#include "sky/skydefs.h"
+
+namespace Common {
+ class File;
+}
+
+enum CptIds {
+ CPT_JOEY = 1,
+ CPT_FOSTER = 3,
+ CPT_TEXT_1 = 0x17,
+ CPT_TEXT_11 = 0x21,
+ CPT_MENU_BAR = 0x2E,
+ CPT_REICH_DOOR_20 = 0x30AB,
+ CPT_MOVE_LIST = 0xBD,
+ CPT_TALK_TABLE_LIST = 0xBC
+};
+
+enum CptTypeIds {
+ CPT_NULL = 0,
+ COMPACT,
+ TURNTAB,
+ ANIMSEQ,
+ MISCBIN,
+ GETTOTAB,
+ ROUTEBUF,
+ MAINLIST
+};
+
+namespace Sky {
+
+class SkyCompact {
+public:
+ SkyCompact(void);
+ ~SkyCompact(void);
+ Compact *fetchCpt(uint16 cptId);
+ Compact *fetchCptInfo(uint16 cptId, uint16 *elems = NULL, uint16 *type = NULL, char *name = NULL);
+ static uint16 *getSub(Compact *cpt, uint16 mode);
+ static MegaSet *getMegaSet(Compact *cpt);
+ uint16 *getGrafixPtr(Compact *cpt);
+ uint16 *getTurnTable(Compact *cpt, uint16 dir);
+ void *getCompactElem(Compact *cpt, uint16 off);
+ bool cptIsId(Compact *cpt, uint16 id);
+ uint8 *createResetData(uint16 gameVersion);
+ uint16 _numSaveIds;
+ uint16 *_saveIds;
+ // - debugging functions
+ uint16 findCptId(void *cpt);
+ uint16 findCptId(const char *cptName);
+ uint16 giveNumDataLists(void);
+ uint16 giveDataListLen(uint16 listNum);
+private:
+ uint16 _numDataLists;
+ uint16 *_dataListLen;
+ uint16 *_rawBuf;
+ char *_asciiBuf;
+ Compact ***_compacts;
+ char ***_cptNames;
+ uint16 **_cptSizes;
+ uint16 **_cptTypes;
+ Common::File *_cptFile;
+ uint32 _resetDataPos;
+};
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sky/control.cpp b/engines/sky/control.cpp
new file mode 100644
index 0000000000..c2aa5ed02f
--- /dev/null
+++ b/engines/sky/control.cpp
@@ -0,0 +1,1665 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/savefile.h"
+#include "common/util.h"
+#include "gui/message.h"
+#include "sky/compact.h"
+#include "sky/control.h"
+#include "sky/disk.h"
+#include "sky/logic.h"
+#include "sky/music/musicbase.h"
+#include "sky/mouse.h"
+#include "sky/screen.h"
+#include "sky/sky.h"
+#include "sky/skydefs.h"
+#include "sky/sound.h"
+#include "sky/struc.h"
+#include "sky/text.h"
+#include "sky/compact.h"
+
+namespace Sky {
+
+ConResource::ConResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, uint16 pX, uint16 pY, uint32 pText, uint8 pOnClick, OSystem *system, uint8 *screen) {
+
+ _spriteData = (dataFileHeader *)pSpData;
+ _numSprites = pNSprites;
+ _curSprite = pCurSprite;
+ _x = pX;
+ _y = pY;
+ _text = pText;
+ _onClick = pOnClick;
+ _system = system;
+ _screen = screen;
+}
+
+bool ConResource::isMouseOver(uint32 mouseX, uint32 mouseY) {
+
+ if ((mouseX >= _x) && (mouseY >= _y) && ((uint16)mouseX <= _x + _spriteData->s_width) && ((uint16)mouseY <= _y + _spriteData->s_height))
+ return true;
+ else
+ return false;
+}
+
+void ConResource::drawToScreen(bool doMask) {
+
+ uint8 *screenPos = _y * GAME_SCREEN_WIDTH + _x + _screen;
+ uint8 *updatePos = screenPos;
+
+ if (!_spriteData)
+ return;
+ uint8 *spriteData = ((uint8 *)_spriteData) + sizeof(dataFileHeader);
+ spriteData += _spriteData->s_sp_size * _curSprite;
+ if (doMask) {
+ for (uint16 cnty = 0; cnty < _spriteData->s_height; cnty++) {
+ for (uint16 cntx = 0; cntx < _spriteData->s_width; cntx++) {
+ if (spriteData[cntx]) screenPos[cntx] = spriteData[cntx];
+ }
+ screenPos += GAME_SCREEN_WIDTH;
+ spriteData += _spriteData->s_width;
+ }
+ } else {
+ for (uint16 cnty = 0; cnty < _spriteData->s_height; cnty++) {
+ memcpy(screenPos, spriteData, _spriteData->s_width);
+ screenPos += GAME_SCREEN_WIDTH;
+ spriteData += _spriteData->s_width;
+ }
+ }
+ _system->copyRectToScreen(updatePos, GAME_SCREEN_WIDTH, _x, _y, _spriteData->s_width, _spriteData->s_height);
+}
+
+TextResource::TextResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, uint16 pX, uint16 pY, uint32 pText, uint8 pOnClick, OSystem *system, uint8 *screen) :
+ ConResource(pSpData, pNSprites, pCurSprite, pX, pY, pText, pOnClick, system, screen) {
+ _oldScreen = (uint8 *)malloc(PAN_CHAR_HEIGHT * 3 * PAN_LINE_WIDTH);
+ _oldY = 0;
+ _oldX = GAME_SCREEN_WIDTH;
+}
+
+TextResource::~TextResource(void) {
+ free(_oldScreen);
+}
+
+void TextResource::flushForRedraw(void) {
+
+ if (_oldX < GAME_SCREEN_WIDTH) {
+ uint16 cpWidth = (PAN_LINE_WIDTH > (GAME_SCREEN_WIDTH - _oldX))?(GAME_SCREEN_WIDTH - _oldX):(PAN_LINE_WIDTH);
+ for (uint8 cnty = 0; cnty < PAN_CHAR_HEIGHT; cnty++)
+ memcpy(_screen + (cnty + _oldY) * GAME_SCREEN_WIDTH + _oldX, _oldScreen + cnty * PAN_LINE_WIDTH, cpWidth);
+ }
+ _oldX = GAME_SCREEN_WIDTH;
+}
+
+void TextResource::drawToScreen(bool doMask) {
+
+ doMask = true;
+ uint16 cnty, cntx, cpWidth, cpHeight;
+ if ((_oldX == _x) && (_oldY == _y) && (_spriteData))
+ return;
+ if (_oldX < GAME_SCREEN_WIDTH) {
+ cpWidth = (PAN_LINE_WIDTH > (GAME_SCREEN_WIDTH - _oldX))?(GAME_SCREEN_WIDTH - _oldX):(PAN_LINE_WIDTH);
+ if (_spriteData && (cpWidth > _spriteData->s_width))
+ cpWidth = _spriteData->s_width;
+ if (_spriteData)
+ cpHeight = (_spriteData->s_height > (GAME_SCREEN_HEIGHT - _oldY))?(GAME_SCREEN_HEIGHT - _oldY):(_spriteData->s_height);
+ else
+ cpHeight = PAN_CHAR_HEIGHT;
+ for (cnty = 0; cnty < cpHeight; cnty++)
+ memcpy(_screen + (cnty + _oldY) * GAME_SCREEN_WIDTH + _oldX, _oldScreen + cnty * PAN_LINE_WIDTH, cpWidth);
+ _system->copyRectToScreen(_screen + _oldY * GAME_SCREEN_WIDTH + _oldX, GAME_SCREEN_WIDTH, _oldX, _oldY, cpWidth, PAN_CHAR_HEIGHT);
+ }
+ if (!_spriteData) {
+ _oldX = GAME_SCREEN_WIDTH;
+ return;
+ }
+ _oldX = _x;
+ _oldY = _y;
+ cpWidth = (PAN_LINE_WIDTH > (GAME_SCREEN_WIDTH - _x))?(GAME_SCREEN_WIDTH - _x):(PAN_LINE_WIDTH);
+ if (cpWidth > _spriteData->s_width)
+ cpWidth = _spriteData->s_width;
+ cpHeight = (_spriteData->s_height > (GAME_SCREEN_HEIGHT - _y))?(GAME_SCREEN_HEIGHT - _y):(_spriteData->s_height);
+
+ uint8 *screenPos = _screen + _y * GAME_SCREEN_WIDTH + _x;
+ uint8 *copyDest = _oldScreen;
+ uint8 *copySrc = ((uint8 *)_spriteData) + sizeof(dataFileHeader);
+ for (cnty = 0; cnty < cpHeight; cnty++) {
+ memcpy(copyDest, screenPos, cpWidth);
+ for (cntx = 0; cntx < cpWidth; cntx++)
+ if (copySrc[cntx]) screenPos[cntx] = copySrc[cntx];
+ copySrc += _spriteData->s_width;
+ copyDest += PAN_LINE_WIDTH;
+ screenPos += GAME_SCREEN_WIDTH;
+ }
+ _system->copyRectToScreen(_screen + _y * GAME_SCREEN_WIDTH + _x, GAME_SCREEN_WIDTH, _x, _y, cpWidth, cpHeight);
+}
+
+ControlStatus::ControlStatus(Text *skyText, OSystem *system, uint8 *scrBuf) {
+ _skyText = skyText;
+ _system = system;
+ _screenBuf = scrBuf;
+ _textData = NULL;
+ _statusText = new TextResource(NULL, 2, 1, 64, 163, 0, DO_NOTHING, _system, _screenBuf);
+}
+
+ControlStatus::~ControlStatus(void) {
+ if (_textData)
+ free(_textData);
+ delete _statusText;
+}
+
+void ControlStatus::setToText(const char *newText) {
+ char tmpLine[256];
+ strcpy(tmpLine, newText);
+ if (_textData) {
+ _statusText->flushForRedraw();
+ free(_textData);
+ }
+ displayText_t disText = _skyText->displayText(tmpLine, NULL, true, STATUS_WIDTH, 255);
+ _textData = (dataFileHeader *)disText.textData;
+ _statusText->setSprite(_textData);
+ _statusText->drawToScreen(WITH_MASK);
+}
+
+void ControlStatus::setToText(uint16 textNum) {
+ if (_textData)
+ free(_textData);
+ displayText_t disText = _skyText->displayText(textNum, NULL, true, STATUS_WIDTH, 255);
+ _textData = (dataFileHeader *)disText.textData;
+ _statusText->setSprite(_textData);
+ _statusText->drawToScreen(WITH_MASK);
+}
+
+void ControlStatus::drawToScreen(void) {
+ _statusText->flushForRedraw();
+ _statusText->drawToScreen(WITH_MASK);
+}
+
+Control::Control(Common::SaveFileManager *saveFileMan, Screen *screen, Disk *disk, Mouse *mouse, Text *text, MusicBase *music, Logic *logic, Sound *sound, SkyCompact *skyCompact, OSystem *system) {
+ _saveFileMan = saveFileMan;
+
+ _skyScreen = screen;
+ _skyDisk = disk;
+ _skyMouse = mouse;
+ _skyText = text;
+ _skyMusic = music;
+ _skyLogic = logic;
+ _skySound = sound;
+ _skyCompact = skyCompact;
+ _system = system;
+}
+
+ConResource *Control::createResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, int16 pX, int16 pY, uint32 pText, uint8 pOnClick, uint8 panelType) {
+
+ if (pText) pText += 0x7000;
+ if (panelType == MAINPANEL) {
+ pX += MPNL_X;
+ pY += MPNL_Y;
+ } else {
+ pX += SPNL_X;
+ pY += SPNL_Y;
+ }
+ return new ConResource(pSpData, pNSprites, pCurSprite, pX, pY, pText, pOnClick, _system, _screenBuf);
+}
+
+void Control::removePanel(void) {
+
+ free(_screenBuf);
+ free(_sprites.controlPanel); free(_sprites.button);
+ free(_sprites.buttonDown); free(_sprites.savePanel);
+ free(_sprites.yesNo); free(_sprites.slide);
+ free(_sprites.slide2); free(_sprites.slode);
+ free(_sprites.slode2); free(_sprites.musicBodge);
+ delete _controlPanel; delete _exitButton;
+ delete _slide; delete _slide2;
+ delete _slode; delete _restorePanButton;
+ delete _savePanButton; delete _dosPanButton;
+ delete _restartPanButton; delete _fxPanButton;
+ delete _musicPanButton; delete _bodge;
+ delete _yesNo; delete _text;
+ delete _statusBar; delete _restoreButton;
+
+ if (_textSprite) {
+ free(_textSprite);
+ _textSprite = NULL;
+ }
+}
+
+void Control::initPanel(void) {
+
+ _screenBuf = (uint8 *)malloc(GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+ memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+
+ uint16 volY = (127 - _skyMusic->giveVolume()) / 4 + 59 - MPNL_Y; // volume slider's Y coordinate
+ uint16 spdY = (SkyEngine::_systemVars.gameSpeed - 2) / SPEED_MULTIPLY;
+ spdY += MPNL_Y + 83; // speed slider's initial position
+
+ _sprites.controlPanel = _skyDisk->loadFile(60500);
+ _sprites.button = _skyDisk->loadFile(60501);
+ _sprites.buttonDown = _skyDisk->loadFile(60502);
+ _sprites.savePanel = _skyDisk->loadFile(60503);
+ _sprites.yesNo = _skyDisk->loadFile(60504);
+ _sprites.slide = _skyDisk->loadFile(60505);
+ _sprites.slode = _skyDisk->loadFile(60506);
+ _sprites.slode2 = _skyDisk->loadFile(60507);
+ _sprites.slide2 = _skyDisk->loadFile(60508);
+ if (SkyEngine::_systemVars.gameVersion < 368)
+ _sprites.musicBodge = NULL;
+ else
+ _sprites.musicBodge = _skyDisk->loadFile(60509);
+
+ //Main control panel: X Y Text OnClick
+ _controlPanel = createResource(_sprites.controlPanel, 1, 0, 0, 0, 0, DO_NOTHING, MAINPANEL);
+ _exitButton = createResource( _sprites.button, 3, 0, 16, 125, 50, EXIT, MAINPANEL);
+ _slide = createResource( _sprites.slide2, 1, 0, 19,spdY, 95, SPEED_SLIDE, MAINPANEL);
+ _slide2 = createResource( _sprites.slide2, 1, 0, 19,volY, 14, MUSIC_SLIDE, MAINPANEL);
+ _slode = createResource( _sprites.slode2, 1, 0, 9, 49, 0, DO_NOTHING, MAINPANEL);
+ _restorePanButton = createResource( _sprites.button, 3, 0, 58, 19, 51, REST_GAME_PANEL, MAINPANEL);
+ _savePanButton = createResource( _sprites.button, 3, 0, 58, 39, 48, SAVE_GAME_PANEL, MAINPANEL);
+ _dosPanButton = createResource( _sprites.button, 3, 0, 58, 59, 93, QUIT_TO_DOS, MAINPANEL);
+ _restartPanButton = createResource( _sprites.button, 3, 0, 58, 79, 94, RESTART, MAINPANEL);
+ if (SkyEngine::_systemVars.systemFlags & SF_FX_OFF)
+ _fxPanButton = createResource( _sprites.button, 3, 0, 58, 99, 87, TOGGLE_FX, MAINPANEL);
+ else
+ _fxPanButton = createResource( _sprites.button, 3, 2, 58, 99, 86, TOGGLE_FX, MAINPANEL);
+
+ if (SkyEngine::isCDVersion()) { // CD Version: Toggle text/speech
+ _musicPanButton = createResource( _sprites.button, 3, 0, 58, 119, 52, TOGGLE_TEXT, MAINPANEL);
+ } else { // disk version: toggle music on/off
+ _musicPanButton = createResource( _sprites.button, 3, 0, 58, 119, 91, TOGGLE_MS, MAINPANEL);
+ }
+ _bodge = createResource( _sprites.musicBodge, 2, 1, 98, 115, 0, DO_NOTHING, MAINPANEL);
+ _yesNo = createResource( _sprites.yesNo, 1, 0, -2, 40, 0, DO_NOTHING, MAINPANEL);
+
+ _text = new TextResource(NULL, 1, 0, 15, 137, 0, DO_NOTHING, _system, _screenBuf);
+ _controlPanLookList[0] = _exitButton;
+ _controlPanLookList[1] = _restorePanButton;
+ _controlPanLookList[2] = _savePanButton;
+ _controlPanLookList[3] = _dosPanButton;
+ _controlPanLookList[4] = _restartPanButton;
+ _controlPanLookList[5] = _fxPanButton;
+ _controlPanLookList[6] = _musicPanButton;
+ _controlPanLookList[7] = _slide;
+ _controlPanLookList[8] = _slide2;
+
+ // save/restore panel
+ _savePanel = createResource( _sprites.savePanel, 1, 0, 0, 0, 0, DO_NOTHING, SAVEPANEL);
+ _saveButton = createResource( _sprites.button, 3, 0, 29, 129, 48, SAVE_A_GAME, SAVEPANEL);
+ _downFastButton = createResource(_sprites.buttonDown, 1, 0, 212, 114, 0, SHIFT_DOWN_FAST, SAVEPANEL);
+ _downSlowButton = createResource(_sprites.buttonDown, 1, 0, 212, 104, 0, SHIFT_DOWN_SLOW, SAVEPANEL);
+ _upFastButton = createResource(_sprites.buttonDown, 1, 0, 212, 10, 0, SHIFT_UP_FAST, SAVEPANEL);
+ _upSlowButton = createResource(_sprites.buttonDown, 1, 0, 212, 21, 0, SHIFT_UP_SLOW, SAVEPANEL);
+ _quitButton = createResource( _sprites.button, 3, 0, 72, 129, 49, SP_CANCEL, SAVEPANEL);
+ _restoreButton = createResource( _sprites.button, 3, 0, 29, 129, 51, RESTORE_A_GAME, SAVEPANEL);
+ _autoSaveButton = createResource( _sprites.button, 3, 0, 115, 129, 0x8FFF, RESTORE_AUTO, SAVEPANEL);
+
+ _savePanLookList[0] = _saveButton;
+ _restorePanLookList[0] = _restoreButton;
+ _restorePanLookList[1] = _savePanLookList[1] = _downSlowButton;
+ _restorePanLookList[2] = _savePanLookList[2] = _downFastButton;
+ _restorePanLookList[3] = _savePanLookList[3] = _upFastButton;
+ _restorePanLookList[4] = _savePanLookList[4] = _upSlowButton;
+ _restorePanLookList[5] = _savePanLookList[5] = _quitButton;
+ _restorePanLookList[6] = _autoSaveButton;
+
+ _statusBar = new ControlStatus(_skyText, _system, _screenBuf);
+
+ _textSprite = NULL;
+}
+
+void Control::buttonControl(ConResource *pButton) {
+
+ char autoSave[] = "Restore Autosave";
+ if (pButton == NULL) {
+ if (_textSprite)
+ free(_textSprite);
+ _textSprite = NULL;
+ _curButtonText = 0;
+ _text->setSprite(NULL);
+ return;
+ }
+ if (_curButtonText != pButton->_text) {
+ if (_textSprite)
+ free(_textSprite);
+ _textSprite = NULL;
+ _curButtonText = pButton->_text;
+ if (pButton->_text) {
+ displayText_t textRes;
+ if (pButton->_text == 0xFFFF) // text for autosave button
+ textRes = _skyText->displayText(autoSave, NULL, false, PAN_LINE_WIDTH, 255);
+ else
+ textRes = _skyText->displayText(pButton->_text, NULL, false, PAN_LINE_WIDTH, 255);
+ _textSprite = (dataFileHeader *)textRes.textData;
+ _text->setSprite(_textSprite);
+ } else
+ _text->setSprite(NULL);
+ }
+ int destY = (_mouseY - 16 >= 0) ? _mouseY - 16 : 0;
+ _text->setXY(_mouseX + 12, destY);
+}
+
+void Control::drawTextCross(uint32 flags) {
+
+ _bodge->drawToScreen(NO_MASK);
+ if (!(flags & SF_ALLOW_SPEECH))
+ drawCross(151, 124);
+ if (!(flags & SF_ALLOW_TEXT))
+ drawCross(173, 124);
+}
+
+void Control::drawCross(uint16 x, uint16 y) {
+
+ _text->flushForRedraw();
+ uint8 *bufPos, *crossPos;
+ bufPos = _screenBuf + y * GAME_SCREEN_WIDTH + x;
+ crossPos = _crossImg;
+ for (uint16 cnty = 0; cnty < CROSS_SZ_Y; cnty++) {
+ for (uint16 cntx = 0; cntx < CROSS_SZ_X; cntx++)
+ if (crossPos[cntx] != 0xFF)
+ bufPos[cntx] = crossPos[cntx];
+ bufPos += GAME_SCREEN_WIDTH;
+ crossPos += CROSS_SZ_X;
+ }
+ bufPos = _screenBuf + y * GAME_SCREEN_WIDTH + x;
+ _system->copyRectToScreen(bufPos, GAME_SCREEN_WIDTH, x, y, CROSS_SZ_X, CROSS_SZ_Y);
+ _text->drawToScreen(WITH_MASK);
+}
+
+void Control::animClick(ConResource *pButton) {
+
+ if (pButton->_curSprite != pButton->_numSprites -1) {
+ pButton->_curSprite++;
+ _text->flushForRedraw();
+ pButton->drawToScreen(NO_MASK);
+ _text->drawToScreen(WITH_MASK);
+ _system->updateScreen();
+ delay(150);
+ pButton->_curSprite--;
+ _text->flushForRedraw();
+ pButton->drawToScreen(NO_MASK);
+ _text->drawToScreen(WITH_MASK);
+ _system->updateScreen();
+ }
+}
+
+void Control::drawMainPanel(void) {
+
+ memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+ _system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+ _controlPanel->drawToScreen(NO_MASK);
+ _exitButton->drawToScreen(NO_MASK);
+ _savePanButton->drawToScreen(NO_MASK);
+ _restorePanButton->drawToScreen(NO_MASK);
+ _dosPanButton->drawToScreen(NO_MASK);
+ _restartPanButton->drawToScreen(NO_MASK);
+ _fxPanButton->drawToScreen(NO_MASK);
+ _musicPanButton->drawToScreen(NO_MASK);
+ _slode->drawToScreen(WITH_MASK);
+ _slide->drawToScreen(WITH_MASK);
+ _slide2->drawToScreen(WITH_MASK);
+ _bodge->drawToScreen(WITH_MASK);
+ if (SkyEngine::isCDVersion())
+ drawTextCross(SkyEngine::_systemVars.systemFlags & TEXT_FLAG_MASK);
+ _statusBar->drawToScreen();
+}
+
+void Control::doLoadSavePanel(void) {
+ if (SkyEngine::isDemo())
+ return; // I don't think this can even happen
+ initPanel();
+ _skyScreen->clearScreen();
+ if (SkyEngine::_systemVars.gameVersion < 331)
+ _skyScreen->setPalette(60509);
+ else
+ _skyScreen->setPalette(60510);
+
+ _savedMouse = _skyMouse->giveCurrentMouseType();
+ _savedCharSet = _skyText->giveCurrentCharSet();
+ _skyText->fnSetFont(0);
+ _skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
+ _lastButton = -1;
+ _curButtonText = 0;
+
+ saveRestorePanel(false);
+
+ memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+ _system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+ _system->updateScreen();
+ _skyScreen->forceRefresh();
+ _skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
+ removePanel();
+ _skyMouse->spriteMouse(_savedMouse, 0, 0);
+ _skyText->fnSetFont(_savedCharSet);
+}
+
+void Control::doControlPanel(void) {
+
+ if (SkyEngine::isDemo()) {
+ return;
+ }
+ initPanel();
+
+ _savedCharSet = _skyText->giveCurrentCharSet();
+ _skyText->fnSetFont(0);
+
+ _skyScreen->clearScreen();
+ if (SkyEngine::_systemVars.gameVersion < 331)
+ _skyScreen->setPalette(60509);
+ else
+ _skyScreen->setPalette(60510);
+
+ drawMainPanel();
+
+ _savedMouse = _skyMouse->giveCurrentMouseType();
+
+ _skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
+ bool quitPanel = false;
+ _lastButton = -1;
+ _curButtonText = 0;
+ uint16 clickRes = 0;
+
+ while (!quitPanel && !SkyEngine::_systemVars.quitGame) {
+ _text->drawToScreen(WITH_MASK);
+ _system->updateScreen();
+ _mouseClicked = false;
+ delay(50);
+ if (_keyPressed == 27) { // escape pressed
+ _mouseClicked = false;
+ quitPanel = true;
+ }
+ bool haveButton = false;
+ for (uint8 lookCnt = 0; lookCnt < 9; lookCnt++) {
+ if (_controlPanLookList[lookCnt]->isMouseOver(_mouseX, _mouseY)) {
+ haveButton = true;
+ buttonControl(_controlPanLookList[lookCnt]);
+ if (_mouseClicked && _controlPanLookList[lookCnt]->_onClick) {
+ clickRes = handleClick(_controlPanLookList[lookCnt]);
+ _text->flushForRedraw();
+ drawMainPanel();
+ _text->drawToScreen(WITH_MASK);
+ if ((clickRes == QUIT_PANEL) || (clickRes == GAME_SAVED) ||
+ (clickRes == GAME_RESTORED))
+ quitPanel = true;
+ }
+ _mouseClicked = false;
+ }
+ }
+ if (!haveButton)
+ buttonControl(NULL);
+ }
+ memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+ _system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+ if (!SkyEngine::_systemVars.quitGame)
+ _system->updateScreen();
+ _skyScreen->forceRefresh();
+ _skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
+ removePanel();
+ _skyMouse->spriteMouse(_savedMouse, 0, 0);
+ _skyText->fnSetFont(_savedCharSet);
+}
+
+uint16 Control::handleClick(ConResource *pButton) {
+
+ char quitDos[] = "Quit to DOS?";
+ char restart[] = "Restart?";
+
+ switch(pButton->_onClick) {
+ case DO_NOTHING:
+ return 0;
+ case REST_GAME_PANEL:
+ if (!loadSaveAllowed())
+ return CANCEL_PRESSED; // can't save/restore while choosing
+ animClick(pButton);
+ return saveRestorePanel(false); // texts can't be edited
+ case SAVE_GAME_PANEL:
+ if (!loadSaveAllowed())
+ return CANCEL_PRESSED; // can't save/restore while choosing
+ animClick(pButton);
+ return saveRestorePanel(true); // texts can be edited
+ case SAVE_A_GAME:
+ animClick(pButton);
+ return saveGameToFile();
+ case RESTORE_A_GAME:
+ animClick(pButton);
+ return restoreGameFromFile(false);
+ case RESTORE_AUTO:
+ animClick(pButton);
+ return restoreGameFromFile(true);
+ case SP_CANCEL:
+ animClick(pButton);
+ return CANCEL_PRESSED;
+ case SHIFT_DOWN_FAST:
+ animClick(pButton);
+ return shiftDown(FAST);
+ case SHIFT_DOWN_SLOW:
+ animClick(pButton);
+ return shiftDown(SLOW);
+ case SHIFT_UP_FAST:
+ animClick(pButton);
+ return shiftUp(FAST);
+ case SHIFT_UP_SLOW:
+ animClick(pButton);
+ return shiftUp(SLOW);
+ case SPEED_SLIDE:
+ _mouseClicked = true;
+ return doSpeedSlide();
+ case MUSIC_SLIDE:
+ _mouseClicked = true;
+ return doMusicSlide();
+ case TOGGLE_FX:
+ return toggleFx(pButton);
+ case TOGGLE_MS:
+ animClick(pButton);
+ toggleMusic();
+ return TOGGLED;
+ case TOGGLE_TEXT:
+ animClick(pButton);
+ return toggleText();
+ case EXIT:
+ animClick(pButton);
+ return QUIT_PANEL;
+ case RESTART:
+ animClick(pButton);
+ if (getYesNo(restart)) {
+ restartGame();
+ return GAME_RESTORED;
+ } else
+ return 0;
+ case QUIT_TO_DOS:
+ animClick(pButton);
+ if (getYesNo(quitDos))
+ SkyEngine::_systemVars.quitGame = true;
+ return 0;
+ default:
+ error("Control::handleClick: unknown routine: %X",pButton->_onClick);
+ }
+}
+
+bool Control::getYesNo(char *text) {
+
+ bool retVal = false;
+ bool quitPanel = false;
+ uint8 mouseType = MOUSE_NORMAL;
+ uint8 wantMouse = MOUSE_NORMAL;
+ dataFileHeader *dlgTextDat;
+ uint16 textY = MPNL_Y;
+
+ _yesNo->drawToScreen(WITH_MASK);
+ if (text) {
+ displayText_t dlgLtm = _skyText->displayText(text, NULL, true, _yesNo->_spriteData->s_width - 8, 37);
+ dlgTextDat = (dataFileHeader *)dlgLtm.textData;
+ textY = MPNL_Y + 44 + (28 - dlgTextDat->s_height) / 2;
+ } else
+ dlgTextDat = NULL;
+
+ TextResource *dlgText = new TextResource(dlgTextDat, 1, 0, MPNL_X+2, textY, 0, DO_NOTHING, _system, _screenBuf);
+ dlgText->drawToScreen(WITH_MASK);
+
+ while (!quitPanel) {
+ if (mouseType != wantMouse) {
+ mouseType = wantMouse;
+ _skyMouse->spriteMouse(mouseType, 0, 0);
+ }
+ _system->updateScreen();
+ delay(50);
+ if ((_mouseY >= 83) && (_mouseY <= 110)) {
+ if ((_mouseX >= 77) && (_mouseX <= 114)) { // over 'yes'
+ wantMouse = MOUSE_CROSS;
+ if (_mouseClicked) {
+ quitPanel = true;
+ retVal = true;
+ }
+ } else if ((_mouseX >= 156) && (_mouseX <= 193)) { // over 'no'
+ wantMouse = MOUSE_CROSS;
+ if (_mouseClicked) {
+ quitPanel = true;
+ retVal = false;
+ }
+ } else
+ wantMouse = MOUSE_NORMAL;
+ } else
+ wantMouse = MOUSE_NORMAL;
+ }
+ _mouseClicked = false;
+ if (dlgTextDat)
+ free(dlgTextDat);
+ delete dlgText;
+ return retVal;
+}
+
+uint16 Control::doMusicSlide(void) {
+
+ int ofsY = _slide2->_y - _mouseY;
+ uint8 volume;
+ while (_mouseClicked) {
+ delay(50);
+ int newY = ofsY + _mouseY;
+ if (newY < 59) newY = 59;
+ if (newY > 91) newY = 91;
+ if (newY != _slide2->_y) {
+ _slode->drawToScreen(NO_MASK);
+ _slide2->setXY(_slide2->_x, (uint16)newY);
+ _slide2->drawToScreen(WITH_MASK);
+ _slide->drawToScreen(WITH_MASK);
+ volume = (newY - 59) * 4;
+ if (volume >= 128) volume = 0;
+ else volume = 127 - volume;
+ _skyMusic->setVolume(volume);
+ }
+ buttonControl(_slide2);
+ _text->drawToScreen(WITH_MASK);
+ _system->updateScreen();
+ }
+ return 0;
+}
+
+uint16 Control::doSpeedSlide(void) {
+
+ int ofsY = _slide->_y - _mouseY;
+ uint16 speedDelay = _slide->_y - (MPNL_Y + 93);
+ speedDelay *= SPEED_MULTIPLY;
+ speedDelay += 2;
+ while (_mouseClicked) {
+ delay(50);
+ int newY = ofsY + _mouseY;
+ if (newY < MPNL_Y + 93) newY = MPNL_Y + 93;
+ if (newY > MPNL_Y + 104) newY = MPNL_Y + 104;
+ if ((newY == 110) || (newY == 108)) newY = 109;
+ if (newY != _slide->_y) {
+ _slode->drawToScreen(NO_MASK);
+ _slide->setXY(_slide->_x, (uint16)newY);
+ _slide->drawToScreen(WITH_MASK);
+ _slide2->drawToScreen(WITH_MASK);
+ speedDelay = newY - (MPNL_Y + 93);
+ speedDelay *= SPEED_MULTIPLY;
+ speedDelay += 2;
+ }
+ buttonControl(_slide);
+ _text->drawToScreen(WITH_MASK);
+ _system->updateScreen();
+ }
+ SkyEngine::_systemVars.gameSpeed = speedDelay;
+ return SPEED_CHANGED;
+}
+
+uint16 Control::toggleFx(ConResource *pButton) {
+
+ SkyEngine::_systemVars.systemFlags ^= SF_FX_OFF;
+ if (SkyEngine::_systemVars.systemFlags & SF_FX_OFF) {
+ pButton->_curSprite = 0;
+ pButton->_text = 0x7000 + 87;
+ _statusBar->setToText(0x7000 + 87);
+ } else {
+ pButton->_curSprite = 2;
+ pButton->_text = 0x7000 + 86;
+ _statusBar->setToText(0x7000 + 86);
+ }
+
+ ConfMan.set("sfx_mute", (SkyEngine::_systemVars.systemFlags & SF_FX_OFF) != 0);
+
+ pButton->drawToScreen(WITH_MASK);
+ buttonControl(pButton);
+ _system->updateScreen();
+ return TOGGLED;
+}
+
+uint16 Control::toggleText(void) {
+
+ uint32 flags = SkyEngine::_systemVars.systemFlags & TEXT_FLAG_MASK;
+ SkyEngine::_systemVars.systemFlags &= ~TEXT_FLAG_MASK;
+
+ if (flags == SF_ALLOW_TEXT) {
+ flags = SF_ALLOW_SPEECH;
+ _statusBar->setToText(0x7000 + 21); // speech only
+ } else if (flags == SF_ALLOW_SPEECH) {
+ flags = SF_ALLOW_SPEECH | SF_ALLOW_TEXT;
+ _statusBar->setToText(0x7000 + 52); // text and speech
+ } else {
+ flags = SF_ALLOW_TEXT;
+ _statusBar->setToText(0x7000 + 35); // text only
+ }
+
+ ConfMan.set("subtitles", (flags & SF_ALLOW_TEXT) != 0);
+ ConfMan.set("speech_mute", (flags & SF_ALLOW_SPEECH) == 0);
+
+ SkyEngine::_systemVars.systemFlags |= flags;
+
+ drawTextCross(flags);
+
+ _system->updateScreen();
+ return TOGGLED;
+}
+
+void Control::toggleMusic(void) {
+
+ if (SkyEngine::_systemVars.systemFlags & SF_MUS_OFF) {
+ SkyEngine::_systemVars.systemFlags &= ~SF_MUS_OFF;
+ _skyMusic->startMusic(SkyEngine::_systemVars.currentMusic);
+ _statusBar->setToText(0x7000 + 88);
+ } else {
+ SkyEngine::_systemVars.systemFlags |= SF_MUS_OFF;
+ _skyMusic->startMusic(0);
+ _statusBar->setToText(0x7000 + 89);
+ }
+}
+
+uint16 Control::shiftDown(uint8 speed) {
+
+ if (speed == SLOW) {
+ if (_firstText >= MAX_SAVE_GAMES - MAX_ON_SCREEN)
+ return 0;
+ _firstText++;
+ } else {
+ if (_firstText <= MAX_SAVE_GAMES - 2 * MAX_ON_SCREEN)
+ _firstText += MAX_ON_SCREEN;
+ else if (_firstText < MAX_SAVE_GAMES - MAX_ON_SCREEN)
+ _firstText = MAX_SAVE_GAMES - MAX_ON_SCREEN;
+ else
+ return 0;
+ }
+ return SHIFTED;
+}
+
+uint16 Control::shiftUp(uint8 speed) {
+
+ if (speed == SLOW) {
+ if (_firstText > 0)
+ _firstText--;
+ else
+ return 0;
+ } else {
+ if (_firstText >= MAX_ON_SCREEN)
+ _firstText -= MAX_ON_SCREEN;
+ else if (_firstText > 0)
+ _firstText = 0;
+ else
+ return 0;
+ }
+ return SHIFTED;
+}
+
+bool Control::autoSaveExists(void) {
+
+ bool test = false;
+ Common::InSaveFile *f;
+ char fName[20];
+ if (SkyEngine::isCDVersion())
+ strcpy(fName, "SKY-VM-CD.ASD");
+ else
+ sprintf(fName, "SKY-VM%03d.ASD", SkyEngine::_systemVars.gameVersion);
+
+ f = _saveFileMan->openForLoading(fName);
+ if (f != NULL) {
+ test = true;
+ delete f;
+ }
+ return test;
+}
+
+uint16 Control::saveRestorePanel(bool allowSave) {
+
+ _keyPressed = 0;
+ buttonControl(NULL);
+ _text->drawToScreen(WITH_MASK); // flush text restore buffer
+
+ ConResource **lookList;
+ uint16 cnt;
+ uint8 lookListLen;
+ if (allowSave) {
+ lookList = _savePanLookList;
+ lookListLen = 6;
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+ } else {
+ lookList = _restorePanLookList;
+ if (autoSaveExists())
+ lookListLen = 7;
+ else
+ lookListLen = 6;
+ }
+ bool withAutoSave = (lookListLen == 7);
+
+ uint8 *saveGameTexts = (uint8 *)malloc(MAX_SAVE_GAMES * MAX_TEXT_LEN);
+ dataFileHeader *textSprites[MAX_ON_SCREEN + 1];
+ for (cnt = 0; cnt < MAX_ON_SCREEN + 1; cnt++)
+ textSprites[cnt] = NULL;
+ _firstText = 0;
+
+ loadDescriptions(saveGameTexts);
+ _selectedGame = 0;
+
+ bool quitPanel = false;
+ bool refreshNames = true;
+ bool refreshAll = true;
+ uint16 clickRes = 0;
+ while (!quitPanel && !SkyEngine::_systemVars.quitGame) {
+ clickRes = 0;
+ if (refreshNames || refreshAll) {
+ if (refreshAll) {
+ _text->flushForRedraw();
+ _savePanel->drawToScreen(NO_MASK);
+ _quitButton->drawToScreen(NO_MASK);
+ if (withAutoSave)
+ _autoSaveButton->drawToScreen(NO_MASK);
+ refreshAll = false;
+ }
+ for (cnt = 0; cnt < MAX_ON_SCREEN; cnt++)
+ if (textSprites[cnt])
+ free(textSprites[cnt]);
+ setUpGameSprites(saveGameTexts, textSprites, _firstText, _selectedGame);
+ showSprites(textSprites, allowSave);
+ refreshNames = false;
+ }
+
+ _text->drawToScreen(WITH_MASK);
+ _system->updateScreen();
+ _mouseClicked = false;
+ delay(50);
+ if (_keyPressed == 27) { // escape pressed
+ _mouseClicked = false;
+ clickRes = CANCEL_PRESSED;
+ quitPanel = true;
+ } else if ((_keyPressed == 13) || (_keyPressed == 15)) {
+ clickRes = handleClick(lookList[0]);
+ if (clickRes == GAME_SAVED)
+ saveDescriptions(saveGameTexts);
+ quitPanel = true;
+ _mouseClicked = false;
+ _keyPressed = 0;
+ } if (allowSave && _keyPressed) {
+ handleKeyPress(_keyPressed, _selectedGame * MAX_TEXT_LEN + saveGameTexts);
+ refreshNames = true;
+ _keyPressed = 0;
+ }
+
+ bool haveButton = false;
+ for (cnt = 0; cnt < lookListLen; cnt++)
+ if (lookList[cnt]->isMouseOver(_mouseX, _mouseY)) {
+ buttonControl(lookList[cnt]);
+ haveButton = true;
+
+ if (_mouseClicked && lookList[cnt]->_onClick) {
+ _mouseClicked = false;
+
+ clickRes = handleClick(lookList[cnt]);
+
+ if (clickRes == SHIFTED) {
+ _selectedGame = _firstText;
+ refreshNames = true;
+ }
+ if (clickRes == NO_DISK_SPACE) {
+ displayMessage(0, "Could not save game in directory '%s'", _saveFileMan->getSavePath());
+ quitPanel = true;
+ }
+ if ((clickRes == CANCEL_PRESSED) || (clickRes == GAME_RESTORED))
+ quitPanel = true;
+
+ if (clickRes == GAME_SAVED) {
+ saveDescriptions(saveGameTexts);
+ quitPanel = true;
+ }
+ if (clickRes == RESTORE_FAILED)
+ refreshAll = true;
+ }
+ }
+
+ if (_mouseClicked) {
+ if ((_mouseX >= GAME_NAME_X) && (_mouseX <= GAME_NAME_X + PAN_LINE_WIDTH) &&
+ (_mouseY >= GAME_NAME_Y) && (_mouseY <= GAME_NAME_Y + PAN_CHAR_HEIGHT * MAX_ON_SCREEN)) {
+
+ _selectedGame = (_mouseY - GAME_NAME_Y) / PAN_CHAR_HEIGHT + _firstText;
+ refreshNames = true;
+ }
+ }
+ if (!haveButton)
+ buttonControl(NULL);
+ }
+
+ for (cnt = 0; cnt < MAX_ON_SCREEN + 1; cnt++)
+ free(textSprites[cnt]);
+
+ free(saveGameTexts);
+
+ if (allowSave) {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ }
+
+ return clickRes;
+}
+
+bool Control::checkKeyList(uint8 key) {
+ static const uint8 charList[14] = " ,().='-&+!?\"";
+ for (uint chCnt = 0; chCnt < ARRAYSIZE(charList); chCnt++)
+ if (charList[chCnt] == key)
+ return true;
+ return false;
+}
+
+void Control::handleKeyPress(uint8 key, uint8 *textBuf) {
+
+ if (key == 8) { // backspace
+ for (uint8 cnt = 0; cnt < 6; cnt++)
+ if (!textBuf[cnt])
+ return;
+
+ while (textBuf[1])
+ textBuf++;
+ textBuf[0] = 0;
+ } else {
+ if (_enteredTextWidth >= PAN_LINE_WIDTH - 10)
+ return;
+ if (((key >= 'A') && (key <= 'Z')) || ((key >= 'a') && (key <= 'z')) ||
+ ((key >= '0') && (key <= '9')) || checkKeyList(key)) {
+ uint8 strLen = 0;
+ while (textBuf[0]) {
+ textBuf++;
+ strLen++;
+ }
+ if (strLen < MAX_TEXT_LEN) {
+ textBuf[0] = key;
+ textBuf[1] = 0;
+ }
+ }
+ }
+}
+
+void Control::setUpGameSprites(uint8 *nameBuf, dataFileHeader **nameSprites, uint16 firstNum, uint16 selectedGame) {
+
+ char cursorChar[2] = "-";
+ nameBuf += firstNum * MAX_TEXT_LEN;
+ displayText_t textSpr;
+ if (!nameSprites[MAX_ON_SCREEN]) {
+ textSpr = _skyText->displayText(cursorChar, NULL, false, 15, 0);
+ nameSprites[MAX_ON_SCREEN] = (dataFileHeader *)textSpr.textData;
+ }
+ for (uint16 cnt = 0; cnt < MAX_ON_SCREEN; cnt++) {
+ if (firstNum + cnt == selectedGame)
+ textSpr = _skyText->displayText((char*)nameBuf, NULL, false, PAN_LINE_WIDTH, 0);
+ else
+ textSpr = _skyText->displayText((char*)nameBuf, NULL, false, PAN_LINE_WIDTH, 37);
+ nameBuf += MAX_TEXT_LEN;
+ nameSprites[cnt] = (dataFileHeader *)textSpr.textData;
+ if (firstNum + cnt == selectedGame) {
+ nameSprites[cnt]->flag = 1;
+ _enteredTextWidth = (uint16)textSpr.textWidth;
+ } else
+ nameSprites[cnt]->flag = 0;
+ }
+}
+
+void Control::showSprites(dataFileHeader **nameSprites, bool allowSave) {
+
+ ConResource *drawResource = new ConResource(NULL, 1, 0, 0, 0, 0, 0, _system, _screenBuf);
+ for (uint16 cnt = 0; cnt < MAX_ON_SCREEN; cnt++) {
+ drawResource->setSprite(nameSprites[cnt]);
+ drawResource->setXY(GAME_NAME_X, GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT);
+ if (nameSprites[cnt]->flag) { // name is highlighted
+ for (uint16 cnty = GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT; cnty < GAME_NAME_Y + (cnt + 1) * PAN_CHAR_HEIGHT - 1; cnty++)
+ memset(_screenBuf + cnty * GAME_SCREEN_WIDTH + GAME_NAME_X, 37, PAN_LINE_WIDTH);
+ drawResource->drawToScreen(WITH_MASK);
+ if (allowSave) {
+ drawResource->setSprite(nameSprites[MAX_ON_SCREEN]);
+ drawResource->setXY(GAME_NAME_X + _enteredTextWidth + 1, GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT + 4);
+ drawResource->drawToScreen(WITH_MASK);
+ }
+ _system->copyRectToScreen(_screenBuf + (GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT) * GAME_SCREEN_WIDTH + GAME_NAME_X, GAME_SCREEN_WIDTH, GAME_NAME_X, GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT, PAN_LINE_WIDTH, PAN_CHAR_HEIGHT);
+ } else
+ drawResource->drawToScreen(NO_MASK);
+ }
+ delete drawResource;
+}
+
+void Control::loadDescriptions(uint8 *destBuf) {
+
+ memset(destBuf, 0, MAX_SAVE_GAMES * MAX_TEXT_LEN);
+
+ Common::InSaveFile *inf;
+ inf = _saveFileMan->openForLoading("SKY-VM.SAV");
+ if (inf != NULL) {
+ uint8 *tmpBuf = (uint8 *)malloc(MAX_SAVE_GAMES * MAX_TEXT_LEN);
+ inf->read(tmpBuf, MAX_SAVE_GAMES * MAX_TEXT_LEN);
+ uint8 *destPos = destBuf;
+ uint8 *inPos = tmpBuf;
+ for (uint16 cnt = 0; cnt < MAX_SAVE_GAMES; cnt++) {
+ sprintf((char*)destPos,"%3d: ", cnt + 1);
+ uint8 nameCnt = 0;
+ while ((destPos[nameCnt + 5] = inPos[nameCnt]))
+ nameCnt++;
+ destPos += MAX_TEXT_LEN;
+ inPos += nameCnt + 1;
+ }
+ free(tmpBuf);
+ delete inf;
+ } else {
+ uint8 *destPos = destBuf;
+ for (uint16 cnt = 0; cnt < MAX_SAVE_GAMES; cnt++) {
+ sprintf((char*)destPos,"%3d: ", cnt + 1);
+ destPos += MAX_TEXT_LEN;
+ }
+ }
+}
+
+bool Control::loadSaveAllowed(void) {
+
+ if (SkyEngine::_systemVars.systemFlags & SF_CHOOSING)
+ return false; // texts get lost during load/save, so don't allow it during choosing
+ if (Logic::_scriptVariables[SCREEN] >= 101)
+ return false; // same problem with LINC terminals
+ if ((Logic::_scriptVariables[SCREEN] >= 82) &&
+ (Logic::_scriptVariables[SCREEN] != 85) &&
+ (Logic::_scriptVariables[SCREEN] < 90))
+ return false; // don't allow saving in final rooms
+
+ return true;
+}
+
+int Control::displayMessage(const char *altButton, const char *message, ...) {
+ char buf[STRINGBUFLEN];
+ va_list va;
+
+ va_start(va, message);
+ vsnprintf(buf, STRINGBUFLEN, message, va);
+ va_end(va);
+
+ GUI::MessageDialog dialog(buf, "OK", altButton);
+ int result = dialog.runModal();
+ _skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
+ return result;
+}
+
+void Control::saveDescriptions(uint8 *srcBuf) {
+
+ uint8 *tmpBuf = (uint8 *)malloc(MAX_SAVE_GAMES * MAX_TEXT_LEN);
+ uint8 *tmpPos = tmpBuf;
+ uint8 *srcPos = srcBuf;
+ for (uint16 cnt = 0; cnt < MAX_SAVE_GAMES; cnt++) {
+ uint8 namePos = 5;
+ while (srcPos[namePos]) {
+ if (srcPos[namePos] != '_') {
+ *tmpPos = srcPos[namePos];
+ tmpPos++;
+ }
+ namePos++;
+ }
+ *tmpPos = 0;
+ tmpPos++;
+ srcPos += MAX_TEXT_LEN;
+ }
+ Common::OutSaveFile *outf;
+
+ outf = _saveFileMan->openForSaving("SKY-VM.SAV");
+ bool ioFailed = true;
+ if (outf) {
+ outf->write(tmpBuf, tmpPos - tmpBuf);
+ outf->flush();
+ if (!outf->ioFailed())
+ ioFailed = false;
+ delete outf;
+ }
+ if (ioFailed)
+ displayMessage(NULL, "Unable to store Savegame names to file SKY-VM.SAV in directory %s", _saveFileMan->getSavePath());
+ free(tmpBuf);
+}
+
+void Control::doAutoSave(void) {
+ char fName[20];
+ if (SkyEngine::isCDVersion())
+ strcpy(fName, "SKY-VM-CD.ASD");
+ else
+ sprintf(fName, "SKY-VM%03d.ASD", SkyEngine::_systemVars.gameVersion);
+ Common::OutSaveFile *outf;
+
+ outf = _saveFileMan->openForSaving(fName);
+ if (outf == NULL) {
+ displayMessage(0, "Unable to create autosave file '%s' in directory '%s'", fName, _saveFileMan->getSavePath());
+ return;
+ }
+ uint8 *saveData = (uint8 *)malloc(0x20000);
+ uint32 fSize = prepareSaveData(saveData);
+
+ outf->write(saveData, fSize);
+ outf->flush();
+
+ if (outf->ioFailed())
+ displayMessage(0, "Unable to write autosave file '%s' in directory '%s'. Disk full?", fName, _saveFileMan->getSavePath());
+
+ delete outf;
+ free(saveData);
+}
+
+uint16 Control::saveGameToFile(void) {
+ char fName[20];
+ sprintf(fName,"SKY-VM.%03d", _selectedGame);
+
+ Common::OutSaveFile *outf;
+ outf = _saveFileMan->openForSaving(fName);
+ if (outf == NULL)
+ return NO_DISK_SPACE;
+
+ uint8 *saveData = (uint8 *)malloc(0x20000);
+ uint32 fSize = prepareSaveData(saveData);
+
+ uint32 writeRes = outf->write(saveData, fSize);
+ outf->flush();
+ if (outf->ioFailed())
+ writeRes = 0;
+ free(saveData);
+ delete outf;
+
+ return (writeRes == fSize) ? GAME_SAVED : NO_DISK_SPACE;
+}
+
+#define STOSD(ptr, val) { *(uint32 *)(ptr) = TO_LE_32(val); (ptr) += 4; }
+#define STOSW(ptr, val) { *(uint16 *)(ptr) = TO_LE_16(val); (ptr) += 2; }
+
+uint32 Control::prepareSaveData(uint8 *destBuf) {
+
+ uint32 cnt;
+ memset(destBuf, 0, 4); // space for data size
+ uint8 *destPos = destBuf + 4;
+ STOSD(destPos, SAVE_FILE_REVISION);
+ STOSD(destPos, SkyEngine::_systemVars.gameVersion);
+
+ STOSW(destPos, _skySound->_saveSounds[0]);
+ STOSW(destPos, _skySound->_saveSounds[1]);
+
+ STOSD(destPos, _skyMusic->giveCurrentMusic());
+ STOSD(destPos, _savedCharSet);
+ STOSD(destPos, _savedMouse);
+ STOSD(destPos, SkyEngine::_systemVars.currentPalette);
+ for (cnt = 0; cnt < 838; cnt++)
+ STOSD(destPos, Logic::_scriptVariables[cnt]);
+ uint32 *loadedFilesList = _skyDisk->giveLoadedFilesList();
+
+ for (cnt = 0; cnt < 60; cnt++)
+ STOSD(destPos, loadedFilesList[cnt]);
+
+ for (cnt = 0; cnt < _skyCompact->_numSaveIds; cnt++) {
+ uint16 numElems;
+ uint16 *rawCpt = (uint16*)_skyCompact->fetchCptInfo(_skyCompact->_saveIds[cnt], &numElems, NULL, NULL);
+ for (uint16 elemCnt = 0; elemCnt < numElems; elemCnt++)
+ STOSW(destPos, rawCpt[elemCnt]);
+ }
+
+ *(uint32 *)destBuf = TO_LE_32(destPos - destBuf); // save size
+ return destPos - destBuf;
+}
+
+#undef STOSD
+#undef STOSW
+
+#define LODSD(strPtr, val) { val = READ_LE_UINT32(strPtr); (strPtr) += 4; }
+#define LODSW(strPtr, val) { val = READ_LE_UINT16(strPtr); (strPtr) += 2; }
+
+void Control::importOldMegaSet(uint8 **srcPos, MegaSet *mega) {
+ LODSW(*srcPos, mega->gridWidth);
+ LODSW(*srcPos, mega->colOffset);
+ LODSW(*srcPos, mega->colWidth);
+ LODSW(*srcPos, mega->lastChr);
+}
+
+void Control::importOldCompact(Compact* destCpt, uint8 **srcPos, uint16 numElems, uint16 type, char *name) {
+ uint16 saveType;
+ LODSW(*srcPos, saveType);
+ if ((saveType & (SAVE_EXT | SAVE_TURNP)) && (numElems < 54))
+ error("Cpt %s: Savedata doesn't match cpt size (%d)!\n", name, numElems);
+ if ((saveType & SAVE_MEGA0) && (numElems < 54 + 13))
+ error("Cpt %s: Savedata doesn't match cpt size (%d)!\n", name, numElems);
+ if ((saveType & SAVE_MEGA1) && (numElems < 54 + 13 + 13))
+ error("Cpt %s: Savedata doesn't match cpt size (%d)!\n", name, numElems);
+ if ((saveType & SAVE_MEGA2) && (numElems < 54 + 13 + 13 + 13))
+ error("Cpt %s: Savedata doesn't match cpt size (%d)!\n", name, numElems);
+ if ((saveType & SAVE_MEGA3) && (numElems < 54 + 13 + 13 + 13))
+ error("Cpt %s: Savedata doesn't match cpt size (%d)!\n", name, numElems);
+ if (saveType & SAVE_GRAFX) {
+ uint16 graphType, target, pos;
+ LODSW(*srcPos, graphType);
+ LODSW(*srcPos, target);
+ LODSW(*srcPos, pos);
+ // convert to new compact system..
+ destCpt->grafixProgPos = pos;
+ if (graphType == OG_PTR_NULL)
+ destCpt->grafixProgId = 0;
+ else if (graphType == OG_AUTOROUTE)
+ destCpt->grafixProgId = destCpt->animScratchId;
+ else if (graphType == OG_COMPACT)
+ destCpt->grafixProgId = target;
+ else if (graphType == OG_TALKTABLE)
+ destCpt->grafixProgId = ((uint16*)_skyCompact->fetchCpt(CPT_TALK_TABLE_LIST))[target];
+ else if (graphType == OG_COMPACTELEM)
+ destCpt->grafixProgId = *(uint16*)_skyCompact->getCompactElem(destCpt, target);
+ else
+ error("Illegal GrafixProg type encountered for compact %s", name);
+ }
+ if (saveType & SAVE_TURNP) {
+ // basically impossible to import these. simply set it to end-of-turn and hope the script
+ // will take care of it.
+ destCpt->turnProgId = 0x13B;
+ destCpt->turnProgPos = 1;
+ uint16 turnSkipLen;
+ LODSW(*srcPos, turnSkipLen);
+ *srcPos += 2 * turnSkipLen;
+ } else if (numElems >= 49) {
+ destCpt->turnProgId = 0;
+ destCpt->turnProgPos = 0;
+ }
+ LODSW(*srcPos, destCpt->logic);
+ LODSW(*srcPos, destCpt->status);
+ LODSW(*srcPos, destCpt->sync);
+ LODSW(*srcPos, destCpt->screen);
+ LODSW(*srcPos, destCpt->place);
+ // getToTable
+ LODSW(*srcPos, destCpt->xcood);
+ LODSW(*srcPos, destCpt->ycood);
+ LODSW(*srcPos, destCpt->frame);
+ LODSW(*srcPos, destCpt->cursorText);
+ LODSW(*srcPos, destCpt->mouseOn);
+ LODSW(*srcPos, destCpt->mouseOff);
+ LODSW(*srcPos, destCpt->mouseClick);
+ LODSW(*srcPos, destCpt->mouseRelX);
+ LODSW(*srcPos, destCpt->mouseRelY);
+ LODSW(*srcPos, destCpt->mouseSizeX);
+ LODSW(*srcPos, destCpt->mouseSizeY);
+ LODSW(*srcPos, destCpt->actionScript);
+ LODSW(*srcPos, destCpt->upFlag);
+ LODSW(*srcPos, destCpt->downFlag);
+ LODSW(*srcPos, destCpt->getToFlag);
+ LODSW(*srcPos, destCpt->flag);
+ LODSW(*srcPos, destCpt->mood);
+ // grafixProg
+ LODSW(*srcPos, destCpt->offset);
+ LODSW(*srcPos, destCpt->mode);
+ LODSW(*srcPos, destCpt->baseSub);
+ LODSW(*srcPos, destCpt->baseSub_off);
+ if (saveType & SAVE_EXT) {
+ LODSW(*srcPos, destCpt->actionSub);
+ LODSW(*srcPos, destCpt->actionSub_off);
+ LODSW(*srcPos, destCpt->getToSub);
+ LODSW(*srcPos, destCpt->getToSub_off);
+ LODSW(*srcPos, destCpt->extraSub);
+ LODSW(*srcPos, destCpt->extraSub_off);
+ LODSW(*srcPos, destCpt->dir);
+ LODSW(*srcPos, destCpt->stopScript);
+ LODSW(*srcPos, destCpt->miniBump);
+ LODSW(*srcPos, destCpt->leaving);
+ LODSW(*srcPos, destCpt->atWatch);
+ LODSW(*srcPos, destCpt->atWas);
+ LODSW(*srcPos, destCpt->alt);
+ LODSW(*srcPos, destCpt->request);
+ LODSW(*srcPos, destCpt->spWidth_xx);
+ LODSW(*srcPos, destCpt->spColour);
+ LODSW(*srcPos, destCpt->spTextId);
+ LODSW(*srcPos, destCpt->spTime);
+ LODSW(*srcPos, destCpt->arAnimIndex);
+ // turnProg
+ LODSW(*srcPos, destCpt->waitingFor);
+ LODSW(*srcPos, destCpt->arTargetX);
+ LODSW(*srcPos, destCpt->arTargetY);
+ // animScratch
+ LODSW(*srcPos, destCpt->megaSet);
+ if (saveType & SAVE_MEGA0)
+ importOldMegaSet(srcPos, &(destCpt->megaSet0));
+ if (saveType & SAVE_MEGA1)
+ importOldMegaSet(srcPos, &(destCpt->megaSet1));
+ if (saveType & SAVE_MEGA2)
+ importOldMegaSet(srcPos, &(destCpt->megaSet2));
+ if (saveType & SAVE_MEGA3)
+ importOldMegaSet(srcPos, &(destCpt->megaSet3));
+ }
+}
+
+uint16 Control::parseSaveData(uint8 *srcBuf) {
+ uint32 reloadList[60];
+ uint32 cnt;
+ uint8 *srcPos = srcBuf;
+ uint32 size;
+ uint32 saveRev;
+ uint32 gameVersion;
+ LODSD(srcPos, size);
+ LODSD(srcPos, saveRev);
+ if (saveRev > SAVE_FILE_REVISION) {
+ displayMessage(0, "Unknown save file revision (%d)", saveRev);
+ return RESTORE_FAILED;
+ } else if (saveRev < OLD_SAVEGAME_TYPE) {
+ displayMessage(0, "This savegame version is unsupported.");
+ return RESTORE_FAILED;
+ }
+ LODSD(srcPos, gameVersion);
+ if (gameVersion != SkyEngine::_systemVars.gameVersion) {
+ if ((!SkyEngine::isCDVersion()) || (gameVersion < 365)) { // cd versions are compatible
+ displayMessage(NULL, "This savegame was created by\n"
+ "Beneath a Steel Sky v0.0%03d\n"
+ "It cannot be loaded by this version (v0.0%3d)",
+ gameVersion, SkyEngine::_systemVars.gameVersion);
+ return RESTORE_FAILED;
+ }
+ }
+ SkyEngine::_systemVars.systemFlags |= SF_GAME_RESTORED;
+
+ LODSW(srcPos, _skySound->_saveSounds[0]);
+ LODSW(srcPos, _skySound->_saveSounds[1]);
+ _skySound->restoreSfx();
+
+ uint32 music, mouseType, palette;
+ LODSD(srcPos, music);
+ LODSD(srcPos, _savedCharSet);
+ LODSD(srcPos, mouseType);
+ LODSD(srcPos, palette);
+
+ _skyLogic->parseSaveData((uint32*)srcPos);
+ srcPos += NUM_SKY_SCRIPTVARS * sizeof(uint32);
+
+ for (cnt = 0; cnt < 60; cnt++)
+ LODSD(srcPos, reloadList[cnt]);
+
+ if (saveRev == SAVE_FILE_REVISION) {
+ for (cnt = 0; cnt < _skyCompact->_numSaveIds; cnt++) {
+ uint16 numElems;
+ uint16 *rawCpt = (uint16*)_skyCompact->fetchCptInfo(_skyCompact->_saveIds[cnt], &numElems, NULL, NULL);
+ for (uint16 elemCnt = 0; elemCnt < numElems; elemCnt++)
+ LODSW(srcPos, rawCpt[elemCnt]);
+ }
+ } else { // import old savegame revision
+ for (cnt = 0; cnt < (uint32)(_skyCompact->_numSaveIds - 2); cnt++) {
+ uint16 numElems;
+ uint16 type;
+ char name[128];
+ uint16 *rawCpt = (uint16*)_skyCompact->fetchCptInfo(_skyCompact->_saveIds[cnt], &numElems, &type, name);
+ if (type == COMPACT) {
+ importOldCompact((Compact*)rawCpt, &srcPos, numElems, type, name);
+ } else if (type == ROUTEBUF) {
+ assert(numElems == 32);
+ for (uint32 elemCnt = 0; elemCnt < numElems; elemCnt++)
+ LODSW(srcPos, rawCpt[elemCnt]);
+ }
+ }
+ uint16 *rawCpt = (uint16*)_skyCompact->fetchCpt(0xBF);
+ for (cnt = 0; cnt < 3; cnt++)
+ LODSW(srcPos, rawCpt[cnt]);
+ rawCpt = (uint16*)_skyCompact->fetchCpt(0xC2);
+ for (cnt = 0; cnt < 13; cnt++)
+ LODSW(srcPos, rawCpt[cnt]);
+ }
+
+ // make sure all text compacts are off
+ for (cnt = CPT_TEXT_1; cnt <= CPT_TEXT_11; cnt++)
+ _skyCompact->fetchCpt(cnt)->status = 0;
+
+ if (srcPos - srcBuf != (int32)size)
+ error("Restore failed! Savegame data = %d bytes. Expected size: %d", srcPos-srcBuf, size);
+
+ _skyDisk->refreshFilesList(reloadList);
+ SkyEngine::_systemVars.currentMusic = (uint16)music;
+ if (!(SkyEngine::_systemVars.systemFlags & SF_MUS_OFF))
+ _skyMusic->startMusic((uint16)music);
+ _savedMouse = (uint16)mouseType;
+ SkyEngine::_systemVars.currentPalette = palette; // will be set when doControlPanel ends
+
+ return GAME_RESTORED;
+}
+
+
+uint16 Control::restoreGameFromFile(bool autoSave) {
+ char fName[20];
+ if (autoSave) {
+ if (SkyEngine::isCDVersion())
+ strcpy(fName, "SKY-VM-CD.ASD");
+ else
+ sprintf(fName, "SKY-VM%03d.ASD", SkyEngine::_systemVars.gameVersion);
+ } else
+ sprintf(fName,"SKY-VM.%03d", _selectedGame);
+
+ Common::InSaveFile *inf;
+ inf = _saveFileMan->openForLoading(fName);
+ if (inf == NULL) {
+ return RESTORE_FAILED;
+ }
+
+ uint32 infSize = inf->readUint32LE();
+ if (infSize < 4) infSize = 4;
+ uint8 *saveData = (uint8 *)malloc(infSize);
+ *(uint32 *)saveData = TO_LE_32(infSize);
+
+ if (inf->read(saveData+4, infSize-4) != infSize-4) {
+ displayMessage(NULL, "Can't read from file '%s'", fName);
+ free(saveData);
+ delete inf;
+ return RESTORE_FAILED;
+ }
+
+ uint16 res = parseSaveData(saveData);
+ SkyEngine::_systemVars.pastIntro = true;
+ delete inf;
+ free(saveData);
+ return res;
+}
+
+uint16 Control::quickXRestore(uint16 slot) {
+ uint16 result;
+ initPanel();
+ _mouseClicked = false;
+
+ _savedCharSet = _skyText->giveCurrentCharSet();
+ _skyText->fnSetFont(0);
+
+ _system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+ _system->updateScreen();
+
+ if (SkyEngine::_systemVars.gameVersion < 331)
+ _skyScreen->setPalette(60509);
+ else
+ _skyScreen->setPalette(60510);
+
+ _savedMouse = _skyMouse->giveCurrentMouseType();
+ _skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
+
+ if (slot == 0)
+ result = restoreGameFromFile(true);
+ else {
+ _selectedGame = slot - 1;
+ result = restoreGameFromFile(false);
+ }
+ if (result == GAME_RESTORED) {
+ memset(_skyScreen->giveCurrent(), 0, GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT);
+ _skyScreen->showScreen(_skyScreen->giveCurrent());
+ _skyScreen->forceRefresh();
+ _skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
+ } else {
+ memset(_screenBuf, 0, FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+ _system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+ _system->updateScreen();
+ _skyScreen->showScreen(_skyScreen->giveCurrent());
+ _skyScreen->setPalette(60111);
+ }
+ _skyMouse->spriteMouse(_savedMouse, 0, 0);
+ _skyText->fnSetFont(_savedCharSet);
+
+ removePanel();
+ return result;
+}
+
+void Control::restartGame(void) {
+ if (SkyEngine::_systemVars.gameVersion <= 267)
+ return; // no restart for floppy demo
+
+ uint8 *resetData = _skyCompact->createResetData((uint16)SkyEngine::_systemVars.gameVersion);
+ parseSaveData((uint8*)resetData);
+ free(resetData);
+ _skyScreen->forceRefresh();
+
+ memset(_skyScreen->giveCurrent(), 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+ _skyScreen->showScreen(_skyScreen->giveCurrent());
+ _skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
+ _skyMouse->spriteMouse(_savedMouse, 0, 0);
+ SkyEngine::_systemVars.pastIntro = true;
+}
+
+void Control::delay(unsigned int amount) {
+
+ OSystem::Event event;
+
+ uint32 start = _system->getMillis();
+ uint32 cur = start;
+ _keyPressed = 0; //reset
+
+ do {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ // Make sure backspace works right (this fixes a small issue on OS X)
+ if (event.kbd.keycode == 8)
+ _keyPressed = 8;
+ else
+ _keyPressed = (byte)event.kbd.ascii;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+#ifdef PALMOS_MODE
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+#endif
+ _mouseClicked = true;
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _mouseClicked = false;
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+ break;
+ case OSystem::EVENT_QUIT:
+ SkyEngine::_systemVars.quitGame = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ uint this_delay = 20; // 1?
+#ifdef _WIN32_WCE
+ this_delay = 10;
+#endif
+ if (this_delay > amount)
+ this_delay = amount;
+
+ if (this_delay > 0) _system->delayMillis(this_delay);
+
+ cur = _system->getMillis();
+ } while (cur < start + amount);
+}
+
+void Control::showGameQuitMsg(void) {
+
+ _skyText->fnSetFont(0);
+ uint8 *textBuf1 = (uint8 *)malloc(GAME_SCREEN_WIDTH * 14 + sizeof(dataFileHeader));
+ uint8 *textBuf2 = (uint8 *)malloc(GAME_SCREEN_WIDTH * 14 + sizeof(dataFileHeader));
+ uint8 *screenData;
+ if (_skyScreen->sequenceRunning())
+ _skyScreen->stopSequence();
+
+ screenData = _skyScreen->giveCurrent();
+
+ _skyText->displayText(_quitTexts[SkyEngine::_systemVars.language * 2 + 0], textBuf1, true, 320, 255);
+ _skyText->displayText(_quitTexts[SkyEngine::_systemVars.language * 2 + 1], textBuf2, true, 320, 255);
+ uint8 *curLine1 = textBuf1 + sizeof(dataFileHeader);
+ uint8 *curLine2 = textBuf2 + sizeof(dataFileHeader);
+ uint8 *targetLine = screenData + GAME_SCREEN_WIDTH * 80;
+ for (uint8 cnty = 0; cnty < PAN_CHAR_HEIGHT; cnty++) {
+ for (uint16 cntx = 0; cntx < GAME_SCREEN_WIDTH; cntx++) {
+ if (curLine1[cntx])
+ targetLine[cntx] = curLine1[cntx];
+ if (curLine2[cntx])
+ (targetLine + 24 * GAME_SCREEN_WIDTH)[cntx] = curLine2[cntx];
+ }
+ curLine1 += GAME_SCREEN_WIDTH;
+ curLine2 += GAME_SCREEN_WIDTH;
+ targetLine += GAME_SCREEN_WIDTH;
+ }
+ _skyScreen->halvePalette();
+ _skyScreen->showScreen(screenData);
+ free(textBuf1);
+ free(textBuf2);
+}
+
+char Control::_quitTexts[16][35] = {
+ "Game over player one",
+ "BE VIGILANT",
+ "Das Spiel ist aus.",
+ "SEI WACHSAM",
+ "Game over joueur 1",
+ "SOYEZ VIGILANTS",
+ "Game over player one",
+ "BE VIGILANT",
+ "SPELET \x2Ar SLUT, Agent 1.",
+ "VAR VAKSAM",
+ "Game over giocatore 1",
+ "SIATE VIGILANTI",
+ "Fim de jogo para o jogador um",
+ "BE VIGILANT"
+ "Game over player one",
+ "BE VIGILANT",
+};
+
+uint8 Control::_crossImg[594] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x4D, 0x61,
+ 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x08, 0x4E, 0x53, 0x50, 0x4F, 0x0C, 0x4D, 0x4E, 0x51, 0x58, 0x58, 0x54, 0x4E, 0x08, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x54, 0x58, 0x50, 0x4E, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x50, 0x4E, 0x54, 0x58, 0x58, 0x54, 0x4E, 0x0C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x61, 0x53, 0x58, 0x54, 0x4E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x50, 0x4E, 0x55, 0x58, 0x58, 0x53, 0x4E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x51, 0x58, 0x58,
+ 0x51, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x51, 0x58,
+ 0x59, 0x58, 0x51, 0x61, 0xFF, 0xFF, 0x61, 0x54, 0x58, 0x58, 0x4F, 0x52, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x55, 0x58, 0x58, 0x57, 0x4E,
+ 0x4F, 0x56, 0x58, 0x57, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x51, 0x58, 0x58, 0x58, 0x58, 0x58, 0x54, 0x4E, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x6A, 0x4F, 0x58, 0x58, 0x58, 0x58, 0x52, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x54, 0x58,
+ 0x58, 0x58, 0x58, 0x57, 0x53, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x09, 0x58, 0x58, 0x58, 0x57, 0x56, 0x58, 0x58, 0x58,
+ 0x57, 0x4F, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x61, 0x55, 0x58, 0x58, 0x58, 0x58, 0x4E, 0x64, 0x57, 0x58, 0x58, 0x58, 0x58, 0x53, 0x61, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x57, 0x58, 0x58, 0x58, 0x58,
+ 0x50, 0xFF, 0xFF, 0x4E, 0x57, 0x58, 0x58, 0x58, 0x58, 0x56, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x58, 0x58, 0x58, 0x58, 0x58, 0x53, 0x09, 0xFF, 0xFF, 0xFF, 0x4E,
+ 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x57,
+ 0x58, 0x58, 0x58, 0x58, 0x56, 0x4E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x57, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x55, 0x58, 0x58, 0x58, 0x58, 0x58, 0x4E,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x58, 0x58, 0x58, 0x58, 0x4E, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x06, 0x58, 0x58, 0x58, 0x58, 0x58, 0x52, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x0C, 0x52, 0x58, 0x58, 0x51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x56, 0x58,
+ 0x58, 0x58, 0x58, 0x56, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x56,
+ 0x58, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x4D, 0x4D, 0x51, 0x56, 0x58, 0x58, 0x50, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x54, 0x09, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x50, 0x54, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF
+};
+
+} // End of namespace Sky
diff --git a/engines/sky/control.h b/engines/sky/control.h
new file mode 100644
index 0000000000..d219dab749
--- /dev/null
+++ b/engines/sky/control.h
@@ -0,0 +1,293 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef CONTROL_H
+#define CONTROL_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+
+class OSystem;
+namespace Common {
+ class SaveFileManager;
+}
+
+namespace Sky {
+
+class Disk;
+class Screen;
+class Logic;
+class Mouse;
+class Text;
+class MusicBase;
+class Sound;
+class SkyCompact;
+struct Compact;
+struct dataFileHeader;
+struct MegaSet;
+
+#define MAX_SAVE_GAMES 999
+#define MAX_TEXT_LEN 80
+#define PAN_LINE_WIDTH 184
+#define PAN_CHAR_HEIGHT 12
+#define STATUS_WIDTH 146
+#define MPNL_X 60 // Main Panel
+#define MPNL_Y 10
+
+#define SPNL_X 20 // Save Panel
+#define SPNL_Y 20
+#define SP_HEIGHT 149
+#define SP_TOP_GAP 12
+#define SP_BOT_GAP 27
+#define CROSS_SZ_X 27
+#define CROSS_SZ_Y 22
+
+#define TEXT_FLAG_MASK (SF_ALLOW_SPEECH | SF_ALLOW_TEXT)
+
+#define GAME_NAME_X (SPNL_X + 18) // x coordinate of game names
+#define GAME_NAME_Y (SPNL_Y + SP_TOP_GAP) // start y coord of game names
+#define MAX_ON_SCREEN ((SP_HEIGHT - SP_TOP_GAP - SP_BOT_GAP) / PAN_CHAR_HEIGHT) // no of save games on screen
+#define CP_PANEL 60400 // main panel sprite
+
+#define MAINPANEL 0
+#define SAVEPANEL 1
+
+#define NO_MASK false
+#define WITH_MASK true
+
+// resource's onClick routines
+#define DO_NOTHING 0
+#define REST_GAME_PANEL 1
+#define SAVE_GAME_PANEL 2
+#define SAVE_A_GAME 3
+#define RESTORE_A_GAME 4
+#define SP_CANCEL 5
+#define SHIFT_DOWN_FAST 6
+#define SHIFT_DOWN_SLOW 7
+#define SHIFT_UP_FAST 8
+#define SHIFT_UP_SLOW 9
+#define SPEED_SLIDE 10
+#define MUSIC_SLIDE 11
+#define TOGGLE_FX 12
+#define TOGGLE_MS 13
+#define TOGGLE_TEXT 14
+#define EXIT 15
+#define RESTART 16
+#define QUIT_TO_DOS 17
+#define RESTORE_AUTO 18
+
+// onClick return codes
+#define CANCEL_PRESSED 100
+#define NAME_TOO_SHORT 101
+#define GAME_SAVED 102
+#define SHIFTED 103
+#define TOGGLED 104
+#define RESTARTED 105
+#define GAME_RESTORED 106
+#define RESTORE_FAILED 107
+#define NO_DISK_SPACE 108
+#define SPEED_CHANGED 109
+#define QUIT_PANEL 110
+
+#define SLOW 0
+#define FAST 1
+
+#define SPEED_MULTIPLY 8
+
+//-
+#define SAVE_EXT 1
+#define SAVE_MEGA0 2
+#define SAVE_MEGA1 4
+#define SAVE_MEGA2 8
+#define SAVE_MEGA3 16
+#define SAVE_GRAFX 32
+#define SAVE_TURNP 64
+
+#define SAVE_FILE_REVISION 6
+#define OLD_SAVEGAME_TYPE 5
+
+struct AllocedMem {
+ uint16 *mem;
+ AllocedMem *next;
+};
+
+class ConResource {
+public:
+ ConResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, uint16 pX, uint16 pY, uint32 pText, uint8 pOnClick, OSystem *system, uint8 *screen);
+ virtual ~ConResource(void) {};
+ void setSprite(void *pSpData) { _spriteData = (dataFileHeader*)pSpData; };
+ void setText(uint32 pText) { if (pText) _text = pText + 0x7000; else _text = 0; };
+ void setXY(uint16 x, uint16 y) { _x = x; _y = y; };
+ bool isMouseOver(uint32 mouseX, uint32 mouseY);
+ virtual void drawToScreen(bool doMask);
+
+ dataFileHeader *_spriteData;
+ uint32 _numSprites, _curSprite;
+ uint16 _x, _y;
+ uint32 _text;
+ uint8 _onClick;
+ OSystem *_system;
+ uint8 *_screen;
+private:
+};
+
+class TextResource : public ConResource {
+public:
+ TextResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, uint16 pX, uint16 pY, uint32 pText, uint8 pOnClick, OSystem *system, uint8 *screen);
+ virtual ~TextResource(void);
+ virtual void drawToScreen(bool doMask);
+ void flushForRedraw(void);
+private:
+ uint16 _oldX, _oldY;
+ uint8 *_oldScreen;
+};
+
+class ControlStatus {
+public:
+ ControlStatus(Text *skyText, OSystem *system, uint8 *scrBuf);
+ ~ControlStatus(void);
+ void setToText(const char *newText);
+ void setToText(uint16 textNum);
+ void drawToScreen(void);
+private:
+ TextResource *_statusText;
+ dataFileHeader *_textData;
+ Text *_skyText;
+ OSystem *_system;
+ uint8 *_screenBuf;
+};
+
+class Control {
+public:
+ Control(Common::SaveFileManager *saveFileMan, Screen *screen, Disk *disk, Mouse *mouse, Text *text, MusicBase *music, Logic *logic, Sound *sound, SkyCompact *skyCompact, OSystem *system);
+ void doControlPanel(void);
+ void doLoadSavePanel(void);
+ void restartGame(void);
+ void showGameQuitMsg(void);
+ void doAutoSave(void);
+ uint16 quickXRestore(uint16 slot);
+ bool loadSaveAllowed(void);
+
+private:
+ int displayMessage(const char *altButton, const char *message, ...);
+
+ void initPanel(void);
+ void removePanel(void);
+
+ void drawMainPanel(void);
+
+ void delay(unsigned int amount);
+
+ void animClick(ConResource *pButton);
+ bool getYesNo(char *text);
+ void buttonControl(ConResource *pButton);
+ uint16 handleClick(ConResource *pButton);
+ uint16 doMusicSlide(void);
+ uint16 doSpeedSlide(void);
+ uint16 toggleFx(ConResource *pButton);
+ uint16 toggleText(void);
+ void toggleMusic(void);
+ uint16 shiftDown(uint8 speed);
+ uint16 shiftUp(uint8 speed);
+ void drawTextCross(uint32 flags);
+ void drawCross(uint16 x, uint16 y);
+
+ uint16 saveRestorePanel(bool allowSave);
+ void loadDescriptions(uint8 *destBuf);
+ void saveDescriptions(uint8 *srcBuf);
+ void setUpGameSprites(uint8 *nameBuf, dataFileHeader **nameSprites, uint16 firstNum, uint16 selectedGame);
+ void showSprites(dataFileHeader **nameSprites, bool allowSave);
+ bool checkKeyList(uint8 key);
+ void handleKeyPress(uint8 key, uint8 *textBuf);
+
+ uint16 _selectedGame;
+ uint16 saveGameToFile(void);
+ uint32 prepareSaveData(uint8 *destBuf);
+
+ bool autoSaveExists(void);
+ uint16 restoreGameFromFile(bool autoSave);
+ void importOldMegaSet(uint8 **srcPos, MegaSet *mega);
+ void importOldCompact(Compact* destCpt, uint8 **srcPos, uint16 numElems, uint16 type, char *name);
+ uint16 parseSaveData(uint8 *srcBuf);
+
+ Common::SaveFileManager *_saveFileMan;
+ SkyCompact *_skyCompact;
+ Screen *_skyScreen;
+ Disk *_skyDisk;
+ Mouse *_skyMouse;
+ Text *_skyText;
+ MusicBase *_skyMusic;
+ Logic *_skyLogic;
+ Sound *_skySound;
+ OSystem *_system;
+ int _mouseX, _mouseY;
+ bool _mouseClicked;
+ byte _keyPressed;
+
+ struct {
+ uint8 *controlPanel;
+ uint8 *button;
+ uint8 *buttonDown;
+ uint8 *savePanel;
+ uint8 *yesNo;
+ uint8 *slide;
+ uint8 *slode;
+ uint8 *slode2;
+ uint8 *slide2;
+ uint8 *musicBodge;
+ } _sprites;
+
+ uint8 *_screenBuf;
+ int _lastButton;
+ uint32 _curButtonText;
+ uint16 _firstText;
+ uint16 _savedMouse;
+ uint32 _savedCharSet;
+ uint16 _enteredTextWidth;
+
+ ConResource *createResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, int16 pX, int16 pY, uint32 pText, uint8 pOnClick, uint8 panelType);
+
+ dataFileHeader *_textSprite;
+ TextResource *_text;
+
+ ConResource *_controlPanel, *_exitButton, *_slide, *_slide2, *_slode;
+ ConResource *_restorePanButton, *_savePanButton, *_dosPanButton, *_restartPanButton, *_fxPanButton, *_musicPanButton;
+ ConResource *_bodge, *_yesNo;
+ ConResource *_controlPanLookList[9];
+
+ //- Save/restore panel
+ ConResource *_savePanel;
+ ConResource *_saveButton, *_downFastButton, *_downSlowButton;
+ ConResource *_upFastButton, *_upSlowButton, *_quitButton, *_restoreButton;
+ ConResource *_autoSaveButton;
+
+ ConResource *_savePanLookList[6], *_restorePanLookList[7];
+
+ ControlStatus *_statusBar;
+
+ static char _quitTexts[16][35];
+ static uint8 _crossImg[594];
+};
+
+} // End of namespace Sky
+
+#endif // CONTROL_H
diff --git a/engines/sky/debug.cpp b/engines/sky/debug.cpp
new file mode 100644
index 0000000000..05aec03db5
--- /dev/null
+++ b/engines/sky/debug.cpp
@@ -0,0 +1,1487 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "common/debugger.cpp"
+
+#include "sky/debug.h"
+#include "sky/grid.h"
+#include "sky/logic.h"
+#include "sky/mouse.h"
+#include "sky/screen.h"
+#include "sky/sky.h"
+#include "sky/struc.h"
+#include "sky/compact.h"
+
+namespace Sky {
+
+static const char *section_0_compacts[] = {
+ "UNDEFINED",
+ "joey",
+ "joey_park",
+ "foster",
+ "std_menu_logic",
+ "text_mouse",
+ "gallcard_menu",
+ "rs_mega_alive",
+ "citycard_menu",
+ "shades_menu",
+ "putty_menu",
+ "lightbulb_menu",
+ "low_get_seq",
+ "mini_shrug_seq",
+ "sml_up_mid_get_seq",
+ "new_grid",
+ "lamb",
+ "floor",
+ "coordinate_test",
+ "save_restore_mouse",
+ "whole_screen",
+ "l_talk_s2",
+ "r_talk_s2",
+ "text_1",
+ "text_2",
+ "text_3",
+ "text_4",
+ "text_5",
+ "text_6",
+ "text_7",
+ "text_8",
+ "text_9",
+ "text_10",
+ "text_11",
+ "wd40_menu",
+ "skey_menu",
+ "secateurs_menu",
+ "rope_menu",
+ "plaster_menu",
+ "new_cable_menu",
+ "shrug_seq",
+ "rad_shrug_seq",
+ "brick_menu",
+ "tongs_menu",
+ "talk1",
+ "talk2",
+ "menu_bar",
+ "left_arrow",
+ "right_arrow",
+ "dog_food_menu",
+ "UNDEFINED",
+ "blank1",
+ "blank2",
+ "blank3",
+ "blank4",
+ "blank5",
+ "blank6",
+ "blank7",
+ "blank8",
+ "blank9",
+ "blank10",
+ "blank11",
+ "UNDEFINED",
+ "crow_bar_menu",
+ "sarnie_menu",
+ "spanner_menu",
+ "joeyb_menu",
+ "low_floor",
+ "UNDEFINED",
+ "stairs",
+ "upstairs",
+ "anita_card_menu",
+ "rs_lamb_to_three",
+ "rs_lamb_start_2",
+ "anchor_menu",
+ "magazine_menu",
+ "tape_menu",
+ "glass_menu",
+ "rs_lamb_start_3",
+ "ticket_menu",
+ "s29_fast_list",
+ "s6_fast_list",
+ "fast_list_sc3",
+ "s9_fast_list",
+ "s10_fast_list",
+ "bar",
+ "s11_fast_list",
+ "fast_list_0",
+ "s0_fast_list",
+ "s7_fast_list",
+ "door",
+ "s28_fast_list",
+ "swing_list",
+ "UNDEFINED",
+ "UNDEFINED",
+ "outside_ledge",
+ "UNDEFINED",
+ "r1_door",
+ "UNDEFINED",
+ "UNDEFINED",
+ "UNDEFINED",
+ "UNDEFINED",
+ "fast_list_sc90",
+ "UNDEFINED",
+ "UNDEFINED",
+ "small_door",
+ "sc85_fast_list",
+ "sc85_chip_list",
+ "sc85_logic_list",
+ "sc85_mouse_list",
+ "sc85_palette",
+ "right_exit0",
+ "UNDEFINED",
+ "UNDEFINED",
+ "UNDEFINED",
+ "s2_floor",
+ "UNDEFINED",
+ "s101_chip_list",
+ "s101_pal",
+ "s101_mouse",
+ "s101_logic",
+ "full_screen",
+ "cancel_button",
+ "button_0",
+ "button_1",
+ "button_2",
+ "button_3",
+ "button_4",
+ "button_5",
+ "button_6",
+ "button_7",
+ "button_8",
+ "button_9",
+ "rs_left_arrow",
+ "rs_right_arrow",
+ "rs_blank",
+ "monitor",
+ "anita",
+ "UNDEFINED",
+ "UNDEFINED",
+ "UNDEFINED",
+ "baby_logic_list",
+ "rs_l_arr_linc",
+ "rs_r_arr_linc",
+ "rs_blanks_linc",
+ "s5_fast_list",
+ "but_e",
+ "but_0",
+ "but_1",
+ "but_2",
+ "but_3",
+ "but_4",
+ "but_5",
+ "but_6",
+ "but_7",
+ "but_8",
+ "but_9",
+ "UNDEFINED",
+ "s102_chip_list",
+ "s102_pal",
+ "s102_logic",
+ "s102_mouse",
+ "restart_butt",
+ "restart_seq",
+ "restore_butt",
+ "restore_seq",
+ "seq1_pal",
+ "seq2_pal",
+ "seq3_pal",
+ "fast_intro",
+ "chip_intro",
+ "fast_intro_2",
+ "fast_intro_3",
+ "retina_scan",
+ "retina_scan_cdt",
+ "exit_butt",
+ "exit_seq",
+ "forklift_cpt",
+ "forklift1_cdt",
+ "forklift2_cdt"
+};
+
+static const char *logic_table_names[] = {
+ "return",
+ "Logic::script",
+ "Logic::auto_route",
+ "Logic::ar_anim",
+ "Logic::ar_turn",
+ "Logic::alt",
+ "Logic::anim",
+ "Logic::turn",
+ "Logic::cursor",
+ "Logic::talk",
+ "Logic::listen",
+ "Logic::stopped",
+ "Logic::choose",
+ "Logic::frames",
+ "Logic::pause",
+ "Logic::wait_sync",
+ "Logic::simple_anim"
+};
+
+static const char opcode_par[] = {
+ 1,
+ 0,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 0,
+ 2,
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ 0
+};
+
+static const char *opcodes[] = {
+ "push_variable",
+ "less_than",
+ "push_number",
+ "not_equal",
+ "if_and",
+ "skip_zero",
+ "pop_var",
+ "minus",
+ "plus",
+ "skip_always",
+ "if_or",
+ "call_mcode",
+ "more_than",
+ "script_exit",
+ "switch",
+ "push_offset",
+ "pop_offset",
+ "is_equal",
+ "skip_nz",
+ "script_exit",
+ "restart_script"
+};
+
+static const char *mcodes[] = {
+ "fn_cache_chip",
+ "fn_cache_fast",
+ "fn_draw_screen",
+ "fn_ar",
+ "fn_ar_animate",
+ "fn_idle",
+ "fn_interact",
+ "fn_start_sub",
+ "fn_they_start_sub",
+ "fn_assign_base",
+ "fn_disk_mouse",
+ "fn_normal_mouse",
+ "fn_blank_mouse",
+ "fn_cross_mouse",
+ "fn_cursor_right",
+ "fn_cursor_left",
+ "fn_cursor_down",
+ "fn_open_hand",
+ "fn_close_hand",
+ "fn_get_to",
+ "fn_set_to_stand",
+ "fn_turn_to",
+ "fn_arrived",
+ "fn_leaving",
+ "fn_set_alternate",
+ "fn_alt_set_alternate",
+ "fn_kill_id",
+ "fn_no_human",
+ "fn_add_human",
+ "fn_add_buttons",
+ "fn_no_buttons",
+ "fn_set_stop",
+ "fn_clear_stop",
+ "fn_pointer_text",
+ "fn_quit",
+ "fn_speak_me",
+ "fn_speak_me_dir",
+ "fn_speak_wait",
+ "fn_speak_wait_dir",
+ "fn_chooser",
+ "fn_highlight",
+ "fn_text_kill",
+ "fn_stop_mode",
+ "fn_we_wait",
+ "fn_send_sync",
+ "fn_send_fast_sync",
+ "fn_send_request",
+ "fn_clear_request",
+ "fn_check_request",
+ "fn_start_menu",
+ "fn_unhighlight",
+ "fn_face_id",
+ "fn_foreground",
+ "fn_background",
+ "fn_new_background",
+ "fn_sort",
+ "fn_no_sprite_engine",
+ "fn_no_sprites_a6",
+ "fn_reset_id",
+ "fn_toggle_grid",
+ "fn_pause",
+ "fn_run_anim_mod",
+ "fn_simple_mod",
+ "fn_run_frames",
+ "fn_await_sync",
+ "fn_inc_mega_set",
+ "fn_dec_mega_set",
+ "fn_set_mega_set",
+ "fn_move_items",
+ "fn_new_list",
+ "fn_ask_this",
+ "fn_random",
+ "fn_person_here",
+ "fn_toggle_mouse",
+ "fn_mouse_on",
+ "fn_mouse_off",
+ "fn_fetch_x",
+ "fn_fetch_y",
+ "fn_test_list",
+ "fn_fetch_place",
+ "fn_custom_joey",
+ "fn_set_palette",
+ "fn_text_module",
+ "fn_change_name",
+ "fn_mini_load",
+ "fn_flush_buffers",
+ "fn_flush_chip",
+ "fn_save_coods",
+ "fn_plot_grid",
+ "fn_remove_grid",
+ "fn_eyeball",
+ "fn_cursor_up",
+ "fn_leave_section",
+ "fn_enter_section",
+ "fn_restore_game",
+ "fn_restart_game",
+ "fn_new_swing_seq",
+ "fn_wait_swing_end",
+ "fn_skip_intro_code",
+ "fn_blank_screen",
+ "fn_print_credit",
+ "fn_look_at",
+ "fn_linc_text_module",
+ "fn_text_kill2",
+ "fn_set_font",
+ "fn_start_fx",
+ "fn_stop_fx",
+ "fn_start_music",
+ "fn_stop_music",
+ "fn_fade_down",
+ "fn_fade_up",
+ "fn_quit_to_dos",
+ "fn_pause_fx",
+ "fn_un_pause_fx",
+ "fn_printf"
+};
+
+static const char *scriptVars[] = {
+ "result",
+ "screen",
+ "logic_list_no",
+ "safe_logic_list",
+ "low_list_no",
+ "high_list_no",
+ "mouse_list_no",
+ "safe_mouse_list",
+ "draw_list_no",
+ "second_draw_list",
+ "do_not_use",
+ "music_module",
+ "cur_id",
+ "mouse_status",
+ "mouse_stop",
+ "button",
+ "but_repeat",
+ "special_item",
+ "get_off",
+ "safe_click",
+ "click_id",
+ "player_id",
+ "cursor_id",
+ "pointer_pen",
+ "last_pal",
+ "safex",
+ "safey",
+ "player_x",
+ "player_y",
+ "player_mood",
+ "player_screen",
+ "old_x",
+ "old_y",
+ "joey_x",
+ "joey_y",
+ "joey_list",
+ "flag",
+ "hit_id",
+ "player_target",
+ "joey_target",
+ "mega_target",
+ "layer_0_id",
+ "layer_1_id",
+ "layer_2_id",
+ "layer_3_id",
+ "grid_1_id",
+ "grid_2_id",
+ "grid_3_id",
+ "stop_grid",
+ "text_rate",
+ "text_speed",
+ "the_chosen_one",
+ "chosen_anim",
+ "text1",
+ "anim1",
+ "text2",
+ "anim2",
+ "text3",
+ "anim3",
+ "text4",
+ "anim4",
+ "text5",
+ "anim5",
+ "text6",
+ "anim6",
+ "text7",
+ "anim7",
+ "text8",
+ "anim8",
+ "o0",
+ "o1",
+ "o2",
+ "o3",
+ "o4",
+ "o5",
+ "o6",
+ "o7",
+ "o8",
+ "o9",
+ "o10",
+ "o11",
+ "o12",
+ "o13",
+ "o14",
+ "o15",
+ "o16",
+ "o17",
+ "o18",
+ "o19",
+ "o20",
+ "o21",
+ "o22",
+ "o23",
+ "o24",
+ "o25",
+ "o26",
+ "o27",
+ "o28",
+ "o29",
+ "first_icon",
+ "menu_length",
+ "scroll_offset",
+ "menu",
+ "object_held",
+ "icon_lit",
+ "at_sign",
+ "fire_exit_flag",
+ "small_door_flag",
+ "jobs_greet",
+ "lamb_greet",
+ "knob_flag",
+ "lazer_flag",
+ "cupb_flag",
+ "jobs_loop",
+ "done_something",
+ "rnd",
+ "jobs_text",
+ "jobs_loc1",
+ "jobs_loc2",
+ "jobs_loc3",
+ "id_talking",
+ "alarm",
+ "alarm_count",
+ "clearing_alarm",
+ "jobs_friend",
+ "joey_born",
+ "joey_text",
+ "joey_peeved",
+ "knows_linc",
+ "linc_overmann",
+ "reich_entry",
+ "seen_lock",
+ "wshop_text",
+ "knows_firedoor",
+ "knows_furnace",
+ "jobs_got_spanner",
+ "jobs_got_sandwich",
+ "jobs_firedoor",
+ "knows_transporter",
+ "joey_loc1",
+ "joey_loc2",
+ "joey_loc3",
+ "joey_screen",
+ "cur_section",
+ "old_section",
+ "joey_section",
+ "lamb_section",
+ "knows_overmann",
+ "jobs_overmann",
+ "jobs_seen_joey",
+ "anita_text",
+ "anit_loc1",
+ "anit_loc2",
+ "anit_loc3",
+ "lamb_friend",
+ "lamb_sick",
+ "lamb_crawly",
+ "lamb_loc1",
+ "lamb_loc2",
+ "lamb_loc3",
+ "lamb_got_spanner",
+ "lamb_text",
+ "knows_auditor",
+ "lamb_security",
+ "lamb_auditor",
+ "fore_text",
+ "transporter_alive",
+ "anita_friend",
+ "anita_stop",
+ "anita_count",
+ "knows_security",
+ "fore_loc1",
+ "fore_loc2",
+ "fore_loc3",
+ "fore_friend",
+ "knows_dlinc",
+ "seen_lift",
+ "player_sound",
+ "guard_linc",
+ "guard_text",
+ "guar_loc1",
+ "guar_loc2",
+ "guar_loc3",
+ "guard_talk",
+ "lamb_out",
+ "guard_warning",
+ "wshp_loc1",
+ "wshp_loc2",
+ "wshp_loc3",
+ "jobs_linc",
+ "knows_port",
+ "jobs_port",
+ "joey_overmann",
+ "joey_count",
+ "knows_pipes",
+ "knows_hobart",
+ "fore_hobart",
+ "fore_overmann",
+ "anit_text",
+ "seen_eye",
+ "anita_dlinc",
+ "seen_dis_lift",
+ "lamb_move_anita",
+ "lamb_stat",
+ "machine_stops",
+ "guard_stat",
+ "guard_hobart",
+ "gordon_text",
+ "gord_loc1",
+ "gord_loc2",
+ "gord_loc3",
+ "lamb_hobart",
+ "anita_loc1",
+ "anita_loc2",
+ "anita_loc3",
+ "knows_elders",
+ "anita_elders",
+ "anita_overmann",
+ "stay_here",
+ "joey_pause",
+ "knows_break_in",
+ "joey_break_in",
+ "joey_lift",
+ "stair_talk",
+ "blown_top",
+ "tamper_flag",
+ "knows_reich",
+ "gordon_reich",
+ "open_panel",
+ "panel_count",
+ "wreck_text",
+ "press_button",
+ "touch_count",
+ "gordon_overmann",
+ "lamb_reich",
+ "exit_stores",
+ "henri_text",
+ "henr_loc1",
+ "henr_loc2",
+ "henr_loc3",
+ "got_sponsor",
+ "used_deodorant",
+ "lob_dad_text",
+ "lob_son_text",
+ "scan_talk",
+ "dady_loc1",
+ "dady_loc2",
+ "dady_loc3",
+ "samm_loc1",
+ "samm_loc2",
+ "samm_loc3",
+ "dirty_card",
+ "wrek_loc1",
+ "wrek_loc2",
+ "wrek_loc3",
+ "crushed_nuts",
+ "got_port",
+ "anita_port",
+ "got_jammer",
+ "knows_anita",
+ "anita_hobart",
+ "local_count",
+ "lamb_joey",
+ "stop_store",
+ "knows_suit",
+ "joey_box",
+ "asked_box",
+ "shell_count",
+ "got_cable",
+ "local_flag",
+ "search_flag",
+ "rad_count",
+ "rad_text",
+ "radm_loc1",
+ "radm_loc2",
+ "radm_loc3",
+ "gordon_off",
+ "knows_jobsworth",
+ "rad_back_flag",
+ "lamb_lift",
+ "knows_cat",
+ "lamb_screwed",
+ "tour_flag",
+ "foreman_reactor",
+ "foreman_anita",
+ "burke_text",
+ "burk_loc1",
+ "burk_loc2",
+ "burk_loc3",
+ "burke_anchor",
+ "jason_text",
+ "jaso_loc1",
+ "jaso_loc2",
+ "helg_loc2",
+ "say_to_helga",
+ "interest_count",
+ "anchor_text",
+ "anchor_overmann",
+ "anch_loc1",
+ "anch_loc2",
+ "anch_loc3",
+ "anchor_count",
+ "lamb_anchor",
+ "anchor_port",
+ "knows_stephen",
+ "knows_ghoul",
+ "anchor_talk",
+ "joey_hook",
+ "joey_done_dir",
+ "bios_loc1",
+ "bios_loc2",
+ "bios_loc3",
+ "got_hook",
+ "anchor_anita",
+ "trev_loc1",
+ "trev_loc2",
+ "trev_loc3",
+ "trevor_text",
+ "trev_text",
+ "trev_overmann",
+ "lamb_smell",
+ "art_flag",
+ "trev_computer",
+ "helga_text",
+ "helg_loc1",
+ "helg_loc3",
+ "bios_loc4",
+ "gallagher_text",
+ "gall_loc1",
+ "gall_loc2",
+ "gall_loc3",
+ "warn_lamb",
+ "open_apts",
+ "store_count",
+ "foreman_auditor",
+ "frozen_assets",
+ "read_report",
+ "seen_holo",
+ "knows_subway",
+ "exit_flag",
+ "father_text",
+ "lamb_fix",
+ "read_briefing",
+ "seen_shaft",
+ "knows_mother",
+ "console_type",
+ "hatch_selected",
+ "seen_walters",
+ "joey_fallen",
+ "jbel_loc1",
+ "lbel_loc1",
+ "lbel_loc2",
+ "jobsworth_speech",
+ "jobs_alert",
+ "jobs_alarmed_ref",
+ "safe_joey_recycle",
+ "safe_joey_sss",
+ "safe_joey_mission",
+ "safe_trans_mission",
+ "safe_slot_mission",
+ "safe_corner_mission",
+ "safe_joey_logic",
+ "safe_gordon_speech",
+ "safe_button_mission",
+ "safe_dad_speech",
+ "safe_son_speech",
+ "safe_skorl_speech",
+ "safe_uchar_speech",
+ "safe_wreck_speech",
+ "safe_anita_speech",
+ "safe_lamb_speech",
+ "safe_foreman_speech",
+ "joey_42_mission",
+ "joey_junction_mission",
+ "safe_welder_mission",
+ "safe_joey_weld",
+ "safe_radman_speech",
+ "safe_link_7_29",
+ "safe_link_29_7",
+ "safe_lamb_to_3",
+ "safe_lamb_to_2",
+ "safe_burke_speech",
+ "safe_burke_1",
+ "safe_burke_2",
+ "safe_dr_1",
+ "safe_body_speech",
+ "joey_bell",
+ "safe_anchor_speech",
+ "safe_anchor",
+ "safe_pc_mission",
+ "safe_hook_mission",
+ "safe_trevor_speech",
+ "joey_fact",
+ "safe_helga_speech",
+ "helga_mission",
+ "gal_bel_speech",
+ "safe_glass_mission",
+ "safe_lamb_fact_return",
+ "lamb_part_2",
+ "safe_lamb_bell_return",
+ "safe_lamb_bell",
+ "safe_cable_mission",
+ "safe_foster_tour",
+ "safe_lamb_tour",
+ "safe_foreman_logic",
+ "safe_lamb_leave",
+ "safe_lamb_3",
+ "safe_lamb_2",
+ "into_linc",
+ "out_10",
+ "out_74",
+ "safe_link_28_31",
+ "safe_link_31_28",
+ "safe_exit_linc",
+ "safe_end_game",
+ "which_linc",
+ "lift_moving",
+ "lift_on_screen",
+ "barrel_on_screen",
+ "convey_on_screen",
+ "shades_searched",
+ "joey_wiz",
+ "slot_slotted",
+ "motor_flag",
+ "panel_flag",
+ "switch_flag",
+ "steam_flag",
+ "steam_fx_no",
+ "factory_flag",
+ "power_door_open",
+ "left_skull_flag",
+ "right_skull_flag",
+ "monitor_watching",
+ "left_lever_flag",
+ "right_lever_flag",
+ "lobby_door_flag",
+ "weld_stop",
+ "cog_flag",
+ "sensor_flag",
+ "look_through",
+ "welder_nut_flag",
+ "s7_lift_flag",
+ "s29_lift_flag",
+ "whos_at_lift_7",
+ "whos_at_lift_29",
+ "lift_power",
+ "whats_joey",
+ "seen_box",
+ "seen_welder",
+ "flap_flag",
+ "s15_floor",
+ "foreman_friend",
+ "locker1_flag",
+ "locker2_flag",
+ "locker3_flag",
+ "whats_in_locker",
+ "knows_radsuit",
+ "radman_anita",
+ "at_anita",
+ "coat_flag",
+ "dressed_as",
+ "s14_take",
+ "reactor_door_flag",
+ "joey_in_lift",
+ "chair_27_flag",
+ "at_body_flag",
+ "at_gas_flag",
+ "anchor_seated",
+ "door_23_jam",
+ "door_20_jam",
+ "reich_door_flag",
+ "reich_door_jam",
+ "lamb_door_flag",
+ "lamb_door_jam",
+ "pillow_flag",
+ "cat_food_flag",
+ "helga_up",
+ "got_magazine",
+ "trevs_doing",
+ "card_status",
+ "card_fix",
+ "lamb_gallager",
+ "locker_11_flag",
+ "ever_opened",
+ "linc_10_flag",
+ "chair_10_flag",
+ "skorl_flag",
+ "lift_pause",
+ "lift_in_use",
+ "gordon_back",
+ "furnace_door_flag",
+ "whos_with_gall",
+ "read_news",
+ "whos_at_lift_28",
+ "s28_lift_flag",
+ "mission_state",
+ "anita_flag",
+ "card_used",
+ "gordon_catch",
+ "car_flag",
+ "first_jobs",
+ "jobs_removed",
+ "menu_id",
+ "tonys_tour_flag",
+ "joey_foster_phase",
+ "start_info_window",
+ "ref_slab_on",
+ "ref_up_mouse",
+ "ref_down_mouse",
+ "ref_left_mouse",
+ "ref_right_mouse",
+ "ref_disconnect_foster",
+ "k0",
+ "k1",
+ "k2",
+ "k3",
+ "k4",
+ "k5",
+ "k6",
+ "k7",
+ "k8",
+ "k9",
+ "k10",
+ "k11",
+ "k12",
+ "k13",
+ "k14",
+ "k15",
+ "k16",
+ "k17",
+ "k18",
+ "k19",
+ "k20",
+ "k21",
+ "k22",
+ "k23",
+ "k24",
+ "k25",
+ "k26",
+ "k27",
+ "k28",
+ "k29",
+ "a0",
+ "a1",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "a6",
+ "a7",
+ "a8",
+ "a9",
+ "a10",
+ "a11",
+ "a12",
+ "a13",
+ "a14",
+ "a15",
+ "a16",
+ "a17",
+ "a18",
+ "a19",
+ "a20",
+ "a21",
+ "a22",
+ "a23",
+ "a24",
+ "a25",
+ "a26",
+ "a27",
+ "a28",
+ "a29",
+ "g0",
+ "g1",
+ "g2",
+ "g3",
+ "g4",
+ "g5",
+ "g6",
+ "g7",
+ "g8",
+ "g9",
+ "g10",
+ "g11",
+ "g12",
+ "g13",
+ "g14",
+ "g15",
+ "g16",
+ "g17",
+ "g18",
+ "g19",
+ "g20",
+ "g21",
+ "g22",
+ "g23",
+ "g24",
+ "g25",
+ "g26",
+ "g27",
+ "g28",
+ "g29",
+ "window_subject",
+ "file_text",
+ "size_text",
+ "auth_text",
+ "note_text",
+ "id_head_compact",
+ "id_file_compact",
+ "id_size_compact",
+ "id_auth_compact",
+ "id_note_compact",
+ "pal_no",
+ "strikes",
+ "char_set_number",
+ "eye90_blinded",
+ "zap90",
+ "eye90_frame",
+ "eye91_blinded",
+ "zap91",
+ "eye91_frame",
+ "bag_open",
+ "bridge_a_on",
+ "bridge_b_on",
+ "bridge_c_on",
+ "bridge_d_on",
+ "bridge_e_on",
+ "bridge_f_on",
+ "bridge_g_on",
+ "bridge_h_on",
+ "green_slab",
+ "red_slab",
+ "foster_slab",
+ "circle_slab",
+ "slab1_mouse",
+ "slab2_mouse",
+ "slab3_mouse",
+ "slab4_mouse",
+ "slab5_mouse",
+ "at_guardian",
+ "guardian_there",
+ "crystal_shattered",
+ "virus_taken",
+ "fs_command",
+ "enter_digits",
+ "next_page",
+ "linc_digit_0",
+ "linc_digit_1",
+ "linc_digit_2",
+ "linc_digit_3",
+ "linc_digit_4",
+ "linc_digit_5",
+ "linc_digit_6",
+ "linc_digit_7",
+ "linc_digit_8",
+ "linc_digit_9",
+ "ref_std_on",
+ "ref_std_exit_left_on",
+ "ref_std_exit_right_on",
+ "ref_advisor_188",
+ "ref_shout_action",
+ "ref_mega_click",
+ "ref_mega_action",
+ "ref_walter_speech",
+ "ref_joey_medic",
+ "ref_joey_med_logic",
+ "ref_joey_med_mission72",
+ "ref_ken_logic",
+ "ref_ken_speech",
+ "ref_ken_mission_hand",
+ "ref_sc70_iris_opened",
+ "ref_sc70_iris_closed",
+ "ref_foster_enter_boardroom",
+ "ref_father_speech",
+ "ref_foster_enter_new_boardroom",
+ "ref_hobbins_speech",
+ "ref_sc82_jobs_sss",
+ "brickwork",
+ "door_67_68_flag",
+ "crowbar_in_clot",
+ "clot_ruptured",
+ "clot_repaired",
+ "walt_text",
+ "walt_loc1",
+ "walt_loc2",
+ "walt_loc3",
+ "walt_count",
+ "medic_text",
+ "seen_room_72",
+ "seen_tap",
+ "joey_med_seen72",
+ "seen_secure_door",
+ "ask_secure_door",
+ "sc70_iris_flag",
+ "sc70_iris_frame",
+ "foster_on_sc70_iris",
+ "sc70_grill_flag",
+ "sc71_charging_flag",
+ "sc72_slime_flag",
+ "sc72_witness_sees_foster",
+ "sc72_witness_killed",
+ "sc73_gallagher_killed",
+ "sc73_removed_board",
+ "sc73_searched_corpse",
+ "door_73_75_flag",
+ "sc74_sitting_flag",
+ "sc75_crashed_flag",
+ "sc75_tissue_infected",
+ "sc75_tongs_flag",
+ "sc76_cabinet1_flag",
+ "sc76_cabinet2_flag",
+ "sc76_cabinet3_flag",
+ "sc76_board_flag",
+ "sc76_ken_prog_flag",
+ "sc76_and2_up_flag",
+ "ken_text",
+ "ken_door_flag",
+ "sc77_foster_hand_flag",
+ "sc77_ken_hand_flag",
+ "door_77_78_flag",
+ "sc80_exit_flag",
+ "ref_danielle_speech",
+ "ref_danielle_go_home",
+ "ref_spunky_go_home",
+ "ref_henri_speech",
+ "ref_buzzer_speech",
+ "ref_foster_visit_dani",
+ "ref_danielle_logic",
+ "ref_jukebox_speech",
+ "ref_vincent_speech",
+ "ref_eddie_speech",
+ "ref_blunt_speech",
+ "ref_dani_answer_phone",
+ "ref_spunky_see_video",
+ "ref_spunky_bark_at_foster",
+ "ref_spunky_smells_food",
+ "ref_barry_speech",
+ "ref_colston_speech",
+ "ref_gallagher_speech",
+ "ref_babs_speech",
+ "ref_chutney_speech",
+ "ref_foster_enter_court",
+ "dani_text",
+ "dani_loc1",
+ "dani_loc2",
+ "dani_loc3",
+ "dani_buff",
+ "dani_huff",
+ "mother_hobart",
+ "foster_id_flag",
+ "knows_spunky",
+ "dog_fleas",
+ "op_flag",
+ "chat_up",
+ "buzz_loc1",
+ "buzz_loc2",
+ "blunt_text",
+ "blun_loc1",
+ "blun_loc2",
+ "blun_loc3",
+ "blunt_dan_info",
+ "vincent_text",
+ "vinc_loc1",
+ "vinc_loc2",
+ "vinc_loc3",
+ "eddie_text",
+ "eddi_loc1",
+ "eddi_loc2",
+ "eddi_loc3",
+ "knows_dandelions",
+ "barry_text",
+ "bazz_loc1",
+ "bazz_loc2",
+ "bazz_loc3",
+ "seen_cellar_door",
+ "babs_text",
+ "babs_loc1",
+ "babs_loc2",
+ "babs_loc3",
+ "colston_text",
+ "cols_loc1",
+ "cols_loc2",
+ "cols_loc3",
+ "jukebox",
+ "knows_soaking",
+ "knows_complaint",
+ "dog_bite",
+ "new_prints",
+ "knows_virus",
+ "been_to_court",
+ "danielle_target",
+ "spunky_target",
+ "henri_forward",
+ "sc31_lift_flag",
+ "sc31_food_on_plank",
+ "sc31_spunky_at_plank",
+ "dog_in_lake",
+ "sc32_lift_flag",
+ "sc33_shed_door_flag",
+ "gardener_up",
+ "babs_x",
+ "babs_y",
+ "foster_caching",
+ "colston_caching",
+ "band_playing",
+ "colston_at_table",
+ "sc36_next_dealer",
+ "sc36_door_flag",
+ "sc37_door_flag",
+ "sc37_lid_loosened",
+ "sc37_lid_used",
+ "sc37_standing_on_box",
+ "sc37_box_broken",
+ "sc37_grill_state",
+ "got_dog_biscuits",
+ "sc38_video_playing",
+ "dani_on_phone",
+ "sc40_locker_1_flag",
+ "sc40_locker_2_flag",
+ "sc40_locker_3_flag",
+ "sc40_locker_4_flag",
+ "sc40_locker_5_flag",
+ "seen_anita_corpse",
+ "spunky_at_lift",
+ "court_text",
+ "blunt_knew_jobs",
+ "credit_1_text",
+ "credit_2_text",
+ "id_credit_1",
+ "id_credit_2",
+ "glass_stolen",
+ "foster_at_plank",
+ "foster_at_guard",
+ "man_talk",
+ "man_loc1",
+ "man_loc2",
+ "man_loc3"
+};
+
+void Debug::fetchCompact(uint32 a) {
+ uint32 sectionNum = (a & 0xf000) >> 12;
+ uint32 compactNum = (a & 0x0fff);
+
+ if (sectionNum == 0)
+ debug(8, "Loading Compact %d (%s) from section %d", compactNum, section_0_compacts[compactNum], sectionNum);
+ else
+ debug(8, "Loading Compact %d from section %d", compactNum, sectionNum);
+}
+
+void Debug::logic(uint32 logic) {
+ debug(6, "LOGIC: %s", logic_table_names[logic]);
+}
+
+void Debug::script(uint32 command, uint16 *scriptData) {
+ debug(6, "SCRIPT: %s", opcodes[command]);
+ if (command == 0 || command == 6)
+ debug(6, " %s", scriptVars[READ_LE_UINT16(scriptData)/4]);
+ else {
+ int i;
+ for (i = 0; i < opcode_par[command]; i++) {
+ debug(6, " %d", READ_LE_UINT16(scriptData + i));
+ }
+ }
+ debug(6, "");
+}
+
+void Debug::mcode(uint32 mcode, uint32 a, uint32 b, uint32 c) {
+ debug(6, "MCODE: %s(%d, %d, %d)", mcodes[mcode], a, b, c);
+}
+
+
+
+
+Debugger::Debugger(Logic *logic, Mouse *mouse, Screen *screen, SkyCompact *skyCompact)
+: Common::Debugger<Debugger>(), _logic(logic), _mouse(mouse), _screen(screen), _skyCompact(skyCompact), _showGrid(false) {
+ DCmd_Register("exit", &Debugger::Cmd_Exit);
+ DCmd_Register("help", &Debugger::Cmd_Help);
+ DCmd_Register("info", &Debugger::Cmd_Info);
+ DCmd_Register("showgrid", &Debugger::Cmd_ShowGrid);
+ DCmd_Register("reloadgrid", &Debugger::Cmd_ReloadGrid);
+ DCmd_Register("compact", &Debugger::Cmd_ShowCompact);
+ DCmd_Register("logiccmd", &Debugger::Cmd_LogicCommand);
+ DCmd_Register("scriptvar", &Debugger::Cmd_ScriptVar);
+ DCmd_Register("section", &Debugger::Cmd_Section);
+}
+
+Debugger::~Debugger() {} // we need this here for __SYMBIAN32__
+
+void Debugger::preEnter() {
+
+}
+
+void Debugger::postEnter() {
+ _mouse->resetCursor();
+}
+
+bool Debugger::Cmd_Exit(int argc, const char **argv) {
+ _detach_now = true;
+ return false;
+}
+
+bool Debugger::Cmd_Help(int argc, const char **argv) {
+ // console normally has 39 line width
+ // wrap around nicely
+ int width = 0, size;
+
+ DebugPrintf("Commands are:\n");
+ for (int i = 0; i < _dcmd_count; ++i) {
+ size = strlen(_dcmds[i].name) + 1;
+
+ if ((width + size) >= 39) {
+ DebugPrintf("\n");
+ width = size;
+ } else {
+ width += size;
+ }
+
+ DebugPrintf("%s ", _dcmds[i].name);
+ }
+ DebugPrintf("\n");
+ return true;
+}
+
+bool Debugger::Cmd_ShowGrid(int argc, const char **argv) {
+ _showGrid = !_showGrid;
+ DebugPrintf("Show grid: %s\n", _showGrid ? "On" : "Off");
+ if (!_showGrid) _screen->forceRefresh();
+ return true;
+}
+
+bool Debugger::Cmd_ReloadGrid(int argc, const char **argv) {
+ _logic->_skyGrid->loadGrids();
+ DebugPrintf("Grid reloaded\n");
+ return true;
+}
+
+bool Debugger::Cmd_ShowCompact(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Example: %s foster\n", argv[0]);
+ return true;
+ }
+
+ int i;
+ int numCompacts = ARRAYSIZE(section_0_compacts);
+
+ if (0 == strcmp(argv[1], "list")) {
+ for (i = 0; i < numCompacts; ++i) {
+ DebugPrintf("%s\n", section_0_compacts[i]);
+ }
+ return true;
+ }
+
+ Compact *cpt = 0;
+
+ for (i = 0; i < numCompacts; ++i) {
+ if (0 == strcmp(section_0_compacts[i], argv[1])) {
+ cpt = _skyCompact->fetchCpt(i);
+ break;
+ }
+ }
+
+ if (cpt) {
+ DebugPrintf("------Compact %d ('%s')------\n", i, section_0_compacts[i]);
+ DebugPrintf("logic : %d\n", cpt->logic);
+ DebugPrintf("status : %d\n", cpt->status);
+ DebugPrintf("sync : %d\n", cpt->sync);
+ DebugPrintf("screen : %d\n", cpt->screen);
+ DebugPrintf("x/y : %d/%d\n", cpt->xcood, cpt->ycood);
+ DebugPrintf("place cpt : %d\n", cpt->place);
+ DebugPrintf("getToFlag : %d\n", cpt->getToFlag);
+ DebugPrintf("mode : %d\n", cpt->mode);
+ // Mega / extCompact info
+ /*if (cpt->extCompact) {
+ DebugPrintf("waitingFor : %d\n", cpt->extCompact->waitingFor);
+ DebugPrintf("arTargetX/Y: %d/%d\n", cpt->extCompact->arTargetX, cpt->extCompact->arTargetY);
+ }*/
+ } else {
+ DebugPrintf("Unknown compact: '%s'\n", argv[1]);
+ }
+
+ return true;
+}
+
+bool Debugger::Cmd_LogicCommand(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Example: %s fn_printf 42\n", argv[0]);
+ return true;
+ }
+
+ int numMCodes = ARRAYSIZE(mcodes);
+
+ if (0 == strcmp(argv[1], "list")) {
+ for (int i = 0; i < numMCodes; ++i) {
+ DebugPrintf("%s\n", mcodes[i]);
+ }
+ return true;
+ }
+
+ uint32 arg1 = 0, arg2 = 0, arg3 = 0;
+
+ switch (argc) {
+ case 5:
+ arg3 = atoi(argv[4]);
+ case 4:
+ arg2 = atoi(argv[3]);
+ case 3:
+ arg1 = atoi(argv[2]);
+ }
+
+ for (int i = 0; i < numMCodes; ++i) {
+ if (0 == strcmp(mcodes[i], argv[1])) {
+ _logic->fnExec(i, arg1, arg2, arg3);
+ return true;
+ }
+ }
+
+ DebugPrintf("Unknown function: '%s'\n", argv[1]);
+
+ return true;
+}
+
+bool Debugger::Cmd_Info(int argc, const char **argv) {
+ DebugPrintf("Beneath a Steel Sky version: 0.0%d\n", SkyEngine::_systemVars.gameVersion);
+ DebugPrintf("Speech: %s\n", (SkyEngine::_systemVars.systemFlags & SF_ALLOW_SPEECH) ? "on" : "off");
+ DebugPrintf("Text : %s\n", (SkyEngine::_systemVars.systemFlags & SF_ALLOW_TEXT) ? "on" : "off");
+ return true;
+}
+
+bool Debugger::Cmd_ScriptVar(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Example: %s lamb_friend <value>\n", argv[0]);
+ return true;
+ }
+
+ int numScriptVars = ARRAYSIZE(scriptVars);
+
+ if (0 == strcmp(argv[1], "list")) {
+ for (int i = 0; i < numScriptVars; ++i) {
+ DebugPrintf("%s\n", scriptVars[i]);
+ }
+ return true;
+ }
+
+ for (int i = 0; i < numScriptVars; ++i) {
+ if (0 == strcmp(scriptVars[i], argv[1])) {
+ if (argc == 3) {
+ Logic::_scriptVariables[i] = atoi(argv[2]);
+ }
+ DebugPrintf("%s = %d\n", argv[1], Logic::_scriptVariables[i]);
+
+ return true;
+ }
+ }
+
+ DebugPrintf("Unknown ScriptVar: '%s'\n", argv[1]);
+
+ return true;
+}
+
+bool Debugger::Cmd_Section(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Example: %s 4\n", argv[0]);
+ return true;
+ }
+
+ const int baseId[] = { START_ONE, START_S6, START_29, START_SC31, START_SC66, START_SC90, START_SC81 };
+ int section = atoi(argv[1]);
+
+ if (section >= 0 && section <= 6) {
+ _logic->fnEnterSection(section == 6 ? 4 : section, 0, 0);
+ _logic->fnAssignBase(ID_FOSTER, baseId[section], 0);
+ _skyCompact->fetchCpt(ID_FOSTER)->megaSet = 0;
+ } else {
+ DebugPrintf("Unknown section '%s'\n", argv[1]);
+ }
+
+ return true;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/debug.h b/engines/sky/debug.h
new file mode 100644
index 0000000000..8fdb46d2e1
--- /dev/null
+++ b/engines/sky/debug.h
@@ -0,0 +1,76 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKY_DEBUG_H
+#define SKY_DEBUG_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/debugger.h"
+
+namespace Sky {
+
+class Logic;
+class Mouse;
+class Screen;
+class SkyCompact;
+
+class Debugger : public Common::Debugger<Debugger> {
+public:
+ Debugger(Logic *logic, Mouse *mouse, Screen *screen, SkyCompact *skyCompact);
+ virtual ~Debugger(); // we need this here for __SYMBIAN32__ archaic gcc/UIQ
+ bool showGrid() { return _showGrid; }
+
+protected:
+ virtual void preEnter();
+ virtual void postEnter();
+
+ bool Cmd_Exit(int argc, const char **argv);
+ bool Cmd_Help(int argc, const char **argv);
+ bool Cmd_ShowGrid(int argc, const char **argv);
+ bool Cmd_ReloadGrid(int argc, const char **argv);
+ bool Cmd_ShowCompact(int argc, const char **argv);
+ bool Cmd_LogicCommand(int argc, const char **argv);
+ bool Cmd_Info(int argc, const char **argv);
+ bool Cmd_ScriptVar(int argc, const char **argv);
+ bool Cmd_Section(int argc, const char **argv);
+
+ Logic *_logic;
+ Mouse *_mouse;
+ Screen *_screen;
+ SkyCompact *_skyCompact;
+
+ bool _showGrid;
+};
+
+
+class Debug {
+public:
+ static void fetchCompact(uint32 a);
+ static void logic(uint32 logic);
+ static void script(uint32 command, uint16 *scriptData);
+ static void mcode(uint32 mcode, uint32 a, uint32 b, uint32 c);
+};
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sky/disk.cpp b/engines/sky/disk.cpp
new file mode 100644
index 0000000000..3c39a287d0
--- /dev/null
+++ b/engines/sky/disk.cpp
@@ -0,0 +1,375 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/util.h"
+#include "common/scummsys.h"
+#include "sky/disk.h"
+#include "sky/rnc_deco.h"
+#include "sky/sky.h"
+#include "sky/struc.h"
+
+namespace Sky {
+
+static const char *dataFilename = "sky.dsk";
+static const char *dinnerFilename = "sky.dnr";
+
+Disk::Disk(const Common::String &gameDataPath) {
+ _dataDiskHandle = new Common::File();
+ _dnrHandle = new Common::File();
+
+ _dnrHandle->open(dinnerFilename);
+ if (!_dnrHandle->isOpen())
+ error("Could not open %s%s", gameDataPath.c_str(), dinnerFilename);
+
+ if (!(_dinnerTableEntries = _dnrHandle->readUint32LE()))
+ error("Error reading from sky.dnr"); //even though it was opened correctly?!
+
+ _dinnerTableArea = (uint8 *)malloc(_dinnerTableEntries * 8);
+ uint32 entriesRead = _dnrHandle->read(_dinnerTableArea, 8 * _dinnerTableEntries) / 8;
+
+ if (entriesRead != _dinnerTableEntries)
+ error("entriesRead != dinnerTableEntries. [%d/%d]", entriesRead, _dinnerTableEntries);
+
+ _dataDiskHandle->open(dataFilename);
+ if (!_dataDiskHandle->isOpen())
+ error("Error opening %s%s", gameDataPath.c_str(), dataFilename);
+
+ printf("Found BASS version v0.0%d (%d dnr entries)\n", determineGameVersion(), _dinnerTableEntries);
+
+ memset(_buildList, 0, 60 * 2);
+ memset(_loadedFilesList, 0, 60 * 4);
+}
+
+Disk::~Disk(void) {
+ if (_dnrHandle->isOpen())
+ _dnrHandle->close();
+ if (_dataDiskHandle->isOpen())
+ _dataDiskHandle->close();
+ fnFlushBuffers();
+ free(_dinnerTableArea);
+ delete _dnrHandle;
+ delete _dataDiskHandle;
+}
+
+bool Disk::fileExists(uint16 fileNr) {
+ return (getFileInfo(fileNr) != NULL);
+}
+
+// allocate memory, load the file and return a pointer
+uint8 *Disk::loadFile(uint16 fileNr) {
+
+ uint8 cflag;
+
+ debug(2, "load file %d,%d (%d)", (fileNr >> 11), (fileNr & 2047), fileNr);
+
+ uint8 *fileInfoPtr = getFileInfo(fileNr);
+ if (fileInfoPtr == NULL) {
+ debug(1, "File %d not found", fileNr);
+ return NULL;
+ }
+
+ uint32 fileFlags = READ_LE_UINT24(fileInfoPtr + 5);
+ uint32 fileSize = fileFlags & 0x03fffff;
+ uint32 fileOffset = READ_LE_UINT32(fileInfoPtr + 2) & 0x0ffffff;
+
+ _lastLoadedFileSize = fileSize;
+ cflag = (uint8)((fileOffset >> 23) & 0x1);
+ fileOffset &= 0x7FFFFF;
+
+ if (cflag) {
+ if (SkyEngine::_systemVars.gameVersion == 331)
+ fileOffset <<= 3;
+ else
+ fileOffset <<= 4;
+ }
+
+ uint8 *fileDest = (uint8 *)malloc(fileSize + 4); // allocate memory for file
+
+ _dataDiskHandle->seek(fileOffset, SEEK_SET);
+
+ //now read in the data
+ int32 bytesRead = _dataDiskHandle->read(fileDest, fileSize);
+
+ if (bytesRead != (int32)fileSize)
+ warning("Unable to read %d bytes from datadisk (%d bytes read)", fileSize, bytesRead);
+
+ cflag = (uint8)((fileFlags >> 23) & 0x1);
+ //if cflag == 0 then file is compressed, 1 == uncompressed
+
+ dataFileHeader *fileHeader = (dataFileHeader*)fileDest;
+
+ if ((!cflag) && ((FROM_LE_16(fileHeader->flag) >> 7) & 1)) {
+ debug(2, "File is RNC compressed.");
+
+ uint32 decompSize = (FROM_LE_16(fileHeader->flag) & ~0xFF) << 8;
+ decompSize |= FROM_LE_16(fileHeader->s_tot_size);
+
+ uint8 *uncompDest = (uint8 *)malloc(decompSize);
+
+ int32 unpackLen;
+ void *output, *input = fileDest + sizeof(dataFileHeader);
+
+ if ((fileFlags >> 22) & 0x1) { //do we include the header?
+ // don't return the file's header
+ output = uncompDest;
+ unpackLen = _rncDecoder.unpackM1(input, output, 0);
+ } else {
+#ifdef SCUMM_BIG_ENDIAN
+ // Convert dataFileHeader to BE (it only consists of 16 bit words)
+ uint16 *headPtr = (uint16 *)fileDest;
+ for (uint i = 0; i < sizeof(struct dataFileHeader) / 2; i++)
+ *(headPtr + i) = READ_LE_UINT16(headPtr + i);
+#endif
+
+ memcpy(uncompDest, fileDest, sizeof(dataFileHeader));
+ output = uncompDest + sizeof(dataFileHeader);
+ unpackLen = _rncDecoder.unpackM1(input, output, 0);
+ if (unpackLen)
+ unpackLen += sizeof(dataFileHeader);
+ }
+
+ debug(3, "UnpackM1 returned: %d", unpackLen);
+
+ if (unpackLen == 0) { //Unpack returned 0: file was probably not packed.
+ free(uncompDest);
+ return fileDest;
+ } else {
+ if (unpackLen != (int32)decompSize)
+ debug(1, "ERROR: File %d: invalid decomp size! (was: %d, should be: %d)", fileNr, unpackLen, decompSize);
+ _lastLoadedFileSize = decompSize;
+
+ free(fileDest);
+ return uncompDest;
+ }
+ } else {
+#ifdef SCUMM_BIG_ENDIAN
+ if (!cflag) {
+ uint16 *headPtr = (uint16 *)fileDest;
+ for (uint i = 0; i < sizeof(struct dataFileHeader) / 2; i++)
+ *(headPtr + i) = READ_LE_UINT16(headPtr + i);
+ }
+#endif
+ return fileDest;
+ }
+}
+
+uint16 *Disk::loadScriptFile(uint16 fileNr) {
+ uint16 *buf = (uint16*)loadFile(fileNr);
+#ifdef SCUMM_BIG_ENDIAN
+ for (uint i = 0; i < _lastLoadedFileSize / 2; i++)
+ buf[i] = FROM_LE_16(buf[i]);
+#endif
+ return buf;
+}
+
+uint8 *Disk::getFileInfo(uint16 fileNr) {
+
+ uint16 i;
+ uint16 *dnrTbl16Ptr = (uint16 *)_dinnerTableArea;
+
+ for (i = 0; i < _dinnerTableEntries; i++) {
+ if (READ_LE_UINT16(dnrTbl16Ptr) == fileNr) {
+ debug(2, "file %d found!", fileNr);
+ return (uint8 *)dnrTbl16Ptr;
+ }
+ dnrTbl16Ptr += 4;
+ }
+
+ return 0; //not found
+}
+
+void Disk::fnCacheChip(uint16 *fList) {
+
+ // fnCacheChip is called after fnCacheFast
+ uint16 cnt = 0;
+ while (_buildList[cnt])
+ cnt++;
+ uint16 fCnt = 0;
+ do {
+ _buildList[cnt + fCnt] = fList[fCnt] & 0x7FFFU;
+ fCnt++;
+ } while (fList[fCnt-1]);
+ fnCacheFiles();
+}
+
+void Disk::fnCacheFast(uint16 *fList) {
+ if (fList != NULL) {
+ uint8 cnt = 0;
+ do {
+ _buildList[cnt] = fList[cnt] & 0x7FFFU;
+ cnt++;
+ } while (fList[cnt-1]);
+ }
+}
+
+void Disk::fnCacheFiles(void) {
+
+ uint16 lCnt, bCnt, targCnt;
+ targCnt = lCnt = 0;
+ bool found;
+ while (_loadedFilesList[lCnt]) {
+ bCnt = 0;
+ found = false;
+ while (_buildList[bCnt] && (!found)) {
+ if ((_buildList[bCnt] & 0x7FFFU) == _loadedFilesList[lCnt])
+ found = true;
+ else
+ bCnt++;
+ }
+ if (found) {
+ _loadedFilesList[targCnt] = _loadedFilesList[lCnt];
+ targCnt++;
+ } else {
+ free(SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047]);
+ SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047] = NULL;
+ }
+ lCnt++;
+ }
+ _loadedFilesList[targCnt] = 0; // mark end of list
+ bCnt = 0;
+ while (_buildList[bCnt]) {
+ if ((_buildList[bCnt] & 0x7FF) == 0x7FF) {
+ // amiga dummy files
+ bCnt++;
+ continue;
+ }
+ lCnt = 0;
+ found = false;
+ while (_loadedFilesList[lCnt] && (!found)) {
+ if (_loadedFilesList[lCnt] == (_buildList[bCnt] & 0x7FFFU))
+ found = true;
+ lCnt++;
+ }
+ if (found) {
+ bCnt++;
+ continue;
+ }
+ // ok, we really have to load the file.
+ _loadedFilesList[targCnt] = _buildList[bCnt] & 0x7FFFU;
+ targCnt++;
+ _loadedFilesList[targCnt] = 0;
+ SkyEngine::_itemList[_buildList[bCnt] & 2047] = (void**)loadFile(_buildList[bCnt] & 0x7FFF);
+ if (!SkyEngine::_itemList[_buildList[bCnt] & 2047])
+ warning("fnCacheFiles: Disk::loadFile() returned NULL for file %d",_buildList[bCnt] & 0x7FFF);
+ bCnt++;
+ }
+ _buildList[0] = 0;
+}
+
+void Disk::refreshFilesList(uint32 *list) {
+
+ uint8 cnt = 0;
+ while (_loadedFilesList[cnt]) {
+ if (SkyEngine::_itemList[_loadedFilesList[cnt] & 2047])
+ free(SkyEngine::_itemList[_loadedFilesList[cnt] & 2047]);
+ SkyEngine::_itemList[_loadedFilesList[cnt] & 2047] = NULL;
+ cnt++;
+ }
+ cnt = 0;
+ while (list[cnt]) {
+ _loadedFilesList[cnt] = list[cnt];
+ SkyEngine::_itemList[_loadedFilesList[cnt] & 2047] = (void**)loadFile((uint16)(_loadedFilesList[cnt] & 0x7FFF));
+ cnt++;
+ }
+ _loadedFilesList[cnt] = 0;
+}
+
+void Disk::fnMiniLoad(uint16 fileNum) {
+
+ uint16 cnt = 0;
+ while (_loadedFilesList[cnt]) {
+ if (_loadedFilesList[cnt] == fileNum)
+ return;
+ cnt++;
+ }
+ _loadedFilesList[cnt] = fileNum & 0x7FFFU;
+ _loadedFilesList[cnt + 1] = 0;
+ SkyEngine::_itemList[fileNum & 2047] = (void**)loadFile(fileNum);
+}
+
+void Disk::fnFlushBuffers(void) {
+
+ // dump all loaded sprites
+ uint8 lCnt = 0;
+ while (_loadedFilesList[lCnt]) {
+ free(SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047]);
+ SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047] = NULL;
+ lCnt++;
+ }
+ _loadedFilesList[0] = 0;
+}
+
+void Disk::dumpFile(uint16 fileNr) {
+ char buf[128];
+ Common::File out;
+ byte* filePtr;
+
+ filePtr = loadFile(fileNr);
+ sprintf(buf, "dumps/file-%d.dmp", fileNr);
+
+ if (!out.exists(buf, "")) {
+ if (out.open(buf, Common::File::kFileWriteMode, ""))
+ out.write(filePtr, _lastLoadedFileSize);
+ }
+ free(filePtr);
+}
+
+uint32 Disk::determineGameVersion() {
+ //determine game version based on number of entries in dinner table
+ switch (_dinnerTableEntries) {
+ case 243:
+ // pc gamer demo (v0.0109)
+ return 109;
+ case 247:
+ //floppy demo (v0.0267)
+ return 267;
+ case 1404:
+ //floppy (v0.0288)
+ return 288;
+ case 1413:
+ //floppy (v0.0303)
+ return 303;
+ case 1445:
+ //floppy (v0.0331 or v0.0348)
+ if (_dataDiskHandle->size() == 8830435)
+ return 348;
+ else
+ return 331;
+ case 1711:
+ //cd demo (v0.0365)
+ return 365;
+ case 5099:
+ //cd (v0.0368)
+ return 368;
+ case 5097:
+ //cd (v0.0372)
+ return 372;
+ default:
+ //unknown version
+ error("Unknown game version! %d dinner table entries", _dinnerTableEntries);
+ break;
+ }
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/disk.h b/engines/sky/disk.h
new file mode 100644
index 0000000000..4d2fe4c294
--- /dev/null
+++ b/engines/sky/disk.h
@@ -0,0 +1,76 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYDISK_H
+#define SKYDISK_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "sky/rnc_deco.h"
+
+#define MAX_FILES_IN_LIST 60
+
+namespace Common {
+ class File;
+}
+
+namespace Sky {
+
+class Disk {
+public:
+ Disk(const Common::String &gameDataPath);
+ ~Disk(void);
+
+ uint8 *loadFile(uint16 fileNr);
+ uint16 *loadScriptFile(uint16 fileNr);
+ bool fileExists(uint16 fileNr);
+
+ uint32 determineGameVersion();
+
+ uint32 _lastLoadedFileSize;
+
+ void fnMiniLoad(uint16 fileNum);
+ void fnCacheFast(uint16 *fList);
+ void fnCacheChip(uint16 *fList);
+ void fnCacheFiles(void);
+ void fnFlushBuffers(void);
+ uint32 *giveLoadedFilesList(void) { return _loadedFilesList; };
+ void refreshFilesList(uint32 *list);
+
+protected:
+ uint8 *getFileInfo(uint16 fileNr);
+ void dumpFile(uint16 fileNr);
+
+ uint32 _dinnerTableEntries;
+ uint8 *_dinnerTableArea;
+ Common::File *_dataDiskHandle;
+ Common::File *_dnrHandle;
+ RncDecoder _rncDecoder;
+
+ uint16 _buildList[MAX_FILES_IN_LIST];
+ uint32 _loadedFilesList[MAX_FILES_IN_LIST];
+};
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sky/grid.cpp b/engines/sky/grid.cpp
new file mode 100644
index 0000000000..7becff74d5
--- /dev/null
+++ b/engines/sky/grid.cpp
@@ -0,0 +1,260 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sky/compact.h"
+#include "sky/disk.h"
+#include "sky/grid.h"
+#include "sky/logic.h"
+#include "sky/compact.h"
+
+namespace Sky {
+
+#define GRID_FILE_START 60000
+
+int8 Grid::_gridConvertTable[] = {
+
+ 0, //0
+ 1, //1
+ 2, //2
+ 3, //3
+ 4, //4
+ 5, //5
+ 6, //6
+ 7, //7
+ 8, //8
+ 9, //9
+ 10, //10
+ 11, //11
+ 12, //12
+ 13, //13
+ 14, //14
+ 15, //15
+ 16, //16
+ 17, //17
+ 18, //18
+ 19, //19
+ 20, //20
+ 21, //21
+ 22, //22
+ 23, //23
+ 24, //24
+ 25, //25
+ 26, //26
+ 27, //27
+ 28, //28
+ 29, //29
+ 30, //30
+ 31, //31
+ 32, //32
+ 33, //33
+ 34, //34
+ -1, //35
+ 35, //36
+ 36, //37
+ 37, //38
+ 38, //39
+ 39, //40
+ 40, //41
+ 41, //42
+ -1, //43
+ 42, //44
+ 43, //45
+ 44, //46
+ 45, //47
+ 46, //48
+ -1, //49
+ -1, //50
+ -1, //51
+ -1, //52
+ -1, //53
+ -1, //54
+ -1, //55
+ -1, //56
+ -1, //57
+ -1, //58
+ -1, //59
+ -1, //60
+ -1, //61
+ -1, //62
+ -1, //63
+ -1, //64
+ 47, //65
+ TOT_NO_GRIDS, //66
+ 48, //67
+ 49, //68
+ 50, //69
+ 51, //70
+ 52, //71
+ 53, //72
+ 54, //73
+ 55, //74
+ 56, //75
+ 57, //76
+ 58, //77
+ 59, //78
+ 60, //79
+ -1, //80
+ 61, //81
+ 62, //82
+ -1, //83
+ -1, //84
+ -1, //85
+ -1, //86
+ -1, //87
+ -1, //88
+ TOT_NO_GRIDS, //89
+ 63, //90
+ 64, //91
+ 65, //92
+ 66, //93
+ 67, //94
+ 68, //95
+ 69, //96
+};
+
+Grid::Grid(Disk *pDisk, SkyCompact *skyCompact) {
+ for (int cnt = 0; cnt < TOT_NO_GRIDS; cnt++)
+ _gameGrids[cnt] = NULL;
+ _skyDisk = pDisk;
+ _skyCompact = skyCompact;
+}
+
+Grid::~Grid(void) {
+ for (uint8 cnt = 0; cnt < TOT_NO_GRIDS; cnt++)
+ if (_gameGrids[cnt])
+ free(_gameGrids[cnt]);
+}
+
+void Grid::loadGrids(void) {
+ // no endian conversion necessary as I'm using uint8* instead of uint32*
+ for (uint8 cnt = 0; cnt < TOT_NO_GRIDS; cnt++) {
+ if (_gameGrids[cnt])
+ free(_gameGrids[cnt]);
+ _gameGrids[cnt] = _skyDisk->loadFile(GRID_FILE_START + cnt);
+ }
+ if (!SkyEngine::isDemo()) { // single disk demos never get that far
+ // Reloading the grids can sometimes cause problems eg when reichs door is
+ // open the door grid bit gets replaced so you can't get back in (or out)
+ if (Logic::_scriptVariables[REICH_DOOR_FLAG])
+ removeGrid(256, 280, 1, _skyCompact->fetchCpt(CPT_REICH_DOOR_20));
+ //removeGrid(256, 280, 1, &SkyCompact::reich_door_20);
+ }
+}
+
+bool Grid::getGridValues(Compact *cpt, uint8 *resGrid, uint32 *resBitNum, uint32 *resWidth) {
+ uint16 width = SkyCompact::getMegaSet(cpt)->gridWidth;
+ return getGridValues(cpt->xcood, cpt->ycood, width, cpt, resGrid, resBitNum, resWidth);
+}
+
+bool Grid::getGridValues(uint32 x, uint32 y, uint32 width, Compact *cpt, uint8 *resGrid, uint32 *resBitNum, uint32 *resWidth) {
+ uint32 bitPos;
+ if (y < TOP_LEFT_Y)
+ return false; // off screen
+ y -= TOP_LEFT_Y;
+ y >>= 3; // convert to blocks
+ if (y >= GAME_SCREEN_HEIGHT >> 3)
+ return false; // off screen
+ bitPos = y * 40;
+ width++;
+ x >>= 3; // convert to blocks
+
+ if (x < (TOP_LEFT_X >> 3)) { // at least partially off screen
+ if (x + width < (TOP_LEFT_X >> 3))
+ return false; // completely off screen
+ else {
+ width -= (TOP_LEFT_X >> 3) - x;
+ x = 0;
+ }
+ } else
+ x -= TOP_LEFT_X >> 3;
+
+ if ((GAME_SCREEN_WIDTH >> 3) <= x)
+ return false; // off screen
+ if ((GAME_SCREEN_WIDTH >> 3) < x + width) // partially off screen
+ width = (GAME_SCREEN_WIDTH >> 3) - x;
+
+ bitPos += x;
+ assert((_gridConvertTable[cpt->screen] >= 0) && (_gridConvertTable[cpt->screen] < TOT_NO_GRIDS));
+ *resGrid = (uint8)_gridConvertTable[cpt->screen];
+
+ uint32 tmpBits = 0x1F - (bitPos&0x1F);
+ bitPos &= ~0x1F; // divide into dword address and bit number
+ bitPos += tmpBits;
+ *resBitNum = bitPos;
+ *resWidth = width;
+ return true;
+}
+
+void Grid::removeObjectFromWalk(Compact *cpt) {
+ uint32 bitNum, width;
+ uint8 gridIdx;
+ if (getGridValues(cpt, &gridIdx, &bitNum, &width))
+ removeObjectFromWalk(gridIdx, bitNum, width);
+}
+
+void Grid::removeObjectFromWalk(uint8 gridIdx, uint32 bitNum, uint32 width) {
+ for (uint32 cnt = 0; cnt < width; cnt++) {
+ _gameGrids[gridIdx][bitNum >> 3] &= ~(1 << (bitNum & 0x7));
+ if ((bitNum & 0x1F) == 0)
+ bitNum += 0x3F;
+ else
+ bitNum--;
+ }
+}
+
+void Grid::objectToWalk(Compact *cpt) {
+ uint32 bitNum, width;
+ uint8 gridIdx;
+ if (getGridValues(cpt, &gridIdx, &bitNum, &width))
+ objectToWalk(gridIdx, bitNum, width);
+}
+
+void Grid::objectToWalk(uint8 gridIdx, uint32 bitNum, uint32 width) {
+ for (uint32 cnt = 0; cnt < width; cnt++) {
+ _gameGrids[gridIdx][bitNum >> 3] |= (1 << (bitNum & 0x7));
+ if ((bitNum & 0x1F) == 0)
+ bitNum += 0x3F;
+ else
+ bitNum--;
+ }
+}
+
+void Grid::plotGrid(uint32 x, uint32 y, uint32 width, Compact *cpt) {
+ uint32 resBitPos, resWidth;
+ uint8 resGridIdx;
+ if (getGridValues(x, y, width-1, cpt, &resGridIdx, &resBitPos, &resWidth))
+ objectToWalk(resGridIdx, resBitPos, resWidth);
+}
+
+void Grid::removeGrid(uint32 x, uint32 y, uint32 width, Compact *cpt) {
+ uint32 resBitPos, resWidth;
+ uint8 resGridIdx;
+ if (getGridValues(x, y, width, cpt, &resGridIdx, &resBitPos, &resWidth))
+ removeObjectFromWalk(resGridIdx, resBitPos, resWidth);
+}
+
+uint8 *Grid::giveGrid(uint32 pScreen) {
+ assert((_gridConvertTable[pScreen] >= 0) && (_gridConvertTable[pScreen] < TOT_NO_GRIDS));
+ return _gameGrids[_gridConvertTable[pScreen]];
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/grid.h b/engines/sky/grid.h
new file mode 100644
index 0000000000..7c2ba557c0
--- /dev/null
+++ b/engines/sky/grid.h
@@ -0,0 +1,68 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYGRID_H
+#define SKYGRID_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "skydefs.h"
+
+namespace Sky {
+
+struct Compact;
+class Disk;
+class SkyCompact;
+
+class Grid {
+public:
+ Grid(Disk *pDisk, SkyCompact *skyCompact);
+ ~Grid(void);
+
+ // grid.asm routines
+ void loadGrids(void);
+ void removeObjectFromWalk(Compact *cpt);
+ void objectToWalk(Compact *cpt);
+
+ // function.asm
+ // note that this routine does the same as objectToWalk, it just doesn't get
+ // its x, y, width parameters from cpt.
+ void plotGrid(uint32 x, uint32 y, uint32 width, Compact *cpt);
+ // same here, it's basically the same as removeObjectFromWalk
+ void removeGrid(uint32 x, uint32 y, uint32 width, Compact *cpt);
+ uint8 *giveGrid(uint32 pScreen);
+
+private:
+ void objectToWalk(uint8 gridIdx, uint32 bitNum, uint32 width);
+ void removeObjectFromWalk(uint8 gridIdx, uint32 bitNum, uint32 width);
+ bool getGridValues(Compact *cpt, uint8 *resGrid, uint32 *resBitNum, uint32 *resWidth);
+ bool getGridValues(uint32 x, uint32 y, uint32 width, Compact *cpt, uint8 *resGrid, uint32 *resBitNum, uint32 *resWidth);
+
+ static int8 _gridConvertTable[];
+ uint8 *_gameGrids[TOT_NO_GRIDS];
+ Disk *_skyDisk;
+ SkyCompact *_skyCompact;
+};
+
+} // End of namespace Sky
+
+#endif //SKYGRID_H
diff --git a/engines/sky/hufftext.cpp b/engines/sky/hufftext.cpp
new file mode 100644
index 0000000000..01b0d4a679
--- /dev/null
+++ b/engines/sky/hufftext.cpp
@@ -0,0 +1,2046 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2002-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sky/text.h"
+
+namespace Sky {
+
+#ifdef PALMOS_68K
+const HuffTree *Text::_huffTree_00109;
+const HuffTree *Text::_huffTree_00267;
+const HuffTree *Text::_huffTree_00288;
+const HuffTree *Text::_huffTree_00303;
+const HuffTree *Text::_huffTree_00331;
+const HuffTree *Text::_huffTree_00348;
+const HuffTree *Text::_huffTree_00365;
+const HuffTree *Text::_huffTree_00368;
+const HuffTree *Text::_huffTree_00372;
+#else
+const HuffTree Text::_huffTree_00109[] = {
+ { 1, 22, 0 },
+ { 2, 9, 0 },
+ { 3, 6, 0 },
+ { 4, 5, 0 },
+ { 0, 0, 0x20 },
+ { 0, 0, 0x65 },
+ { 7, 8, 0 },
+ { 0, 0, 0x74 },
+ { 0, 0, 0x6F },
+ { 10, 17, 0 },
+ { 11, 14, 0 },
+ { 12, 13, 0 },
+ { 0, 0, 0x2E },
+ { 0, 0, 0x61 },
+ { 15, 16, 0 },
+ { 0, 0, 0x00 },
+ { 0, 0, 0x73 },
+ { 18, 21, 0 },
+ { 19, 20, 0 },
+ { 0, 0, 0x6E },
+ { 0, 0, 0x68 },
+ { 0, 0, 0x69 },
+ { 23, 46, 0 },
+ { 24, 35, 0 },
+ { 25, 30, 0 },
+ { 26, 29, 0 },
+ { 27, 28, 0 },
+ { 0, 0, 0x45 },
+ { 0, 0, 0x49 },
+ { 0, 0, 0x72 },
+ { 31, 34, 0 },
+ { 32, 33, 0 },
+ { 0, 0, 0x54 },
+ { 0, 0, 0x75 },
+ { 0, 0, 0x6D },
+ { 36, 41, 0 },
+ { 37, 40, 0 },
+ { 38, 39, 0 },
+ { 0, 0, 0x6C },
+ { 0, 0, 0x79 },
+ { 0, 0, 0x4F },
+ { 42, 45, 0 },
+ { 43, 44, 0 },
+ { 0, 0, 0x41 },
+ { 0, 0, 0x53 },
+ { 0, 0, 0x4E },
+ { 47, 66, 0 },
+ { 48, 57, 0 },
+ { 49, 54, 0 },
+ { 50, 53, 0 },
+ { 51, 52, 0 },
+ { 0, 0, 0x52 },
+ { 0, 0, 0x64 },
+ { 0, 0, 0x27 },
+ { 55, 56, 0 },
+ { 0, 0, 0x44 },
+ { 0, 0, 0x4C },
+ { 58, 63, 0 },
+ { 59, 62, 0 },
+ { 60, 61, 0 },
+ { 0, 0, 0x67 },
+ { 0, 0, 0x43 },
+ { 0, 0, 0x63 },
+ { 64, 65, 0 },
+ { 0, 0, 0x48 },
+ { 0, 0, 0x21 },
+ { 67, 84, 0 },
+ { 68, 77, 0 },
+ { 69, 74, 0 },
+ { 70, 73, 0 },
+ { 71, 72, 0 },
+ { 0, 0, 0x55 },
+ { 0, 0, 0x77 },
+ { 0, 0, 0x66 },
+ { 75, 76, 0 },
+ { 0, 0, 0x50 },
+ { 0, 0, 0x4D },
+ { 78, 81, 0 },
+ { 79, 80, 0 },
+ { 0, 0, 0x62 },
+ { 0, 0, 0x57 },
+ { 82, 83, 0 },
+ { 0, 0, 0x3F },
+ { 0, 0, 0x47 },
+ { 85, 94, 0 },
+ { 86, 91, 0 },
+ { 87, 90, 0 },
+ { 88, 89, 0 },
+ { 0, 0, 0x59 },
+ { 0, 0, 0x2C },
+ { 0, 0, 0x70 },
+ { 92, 93, 0 },
+ { 0, 0, 0x42 },
+ { 0, 0, 0x6B },
+ { 95, 100, 0 },
+ { 96, 99, 0 },
+ { 97, 98, 0 },
+ { 0, 0, 0x76 },
+ { 0, 0, 0x46 },
+ { 0, 0, 0x4B },
+ { 101, 106, 0 },
+ { 102, 105, 0 },
+ { 103, 104, 0 },
+ { 0, 0, 0x2D },
+ { 0, 0, 0x56 },
+ { 0, 0, 0x4A },
+ { 107, 120, 0 },
+ { 108, 113, 0 },
+ { 109, 112, 0 },
+ { 110, 111, 0 },
+ { 0, 0, 0x3A },
+ { 0, 0, 0x58 },
+ { 0, 0, 0x6A },
+ { 114, 117, 0 },
+ { 115, 116, 0 },
+ { 0, 0, 0x60 },
+ { 0, 0, 0x5A },
+ { 118, 119, 0 },
+ { 0, 0, 0x78 },
+ { 0, 0, 0x30 },
+ { 121, 132, 0 },
+ { 122, 127, 0 },
+ { 123, 126, 0 },
+ { 124, 125, 0 },
+ { 0, 0, 0x32 },
+ { 0, 0, 0x31 },
+ { 0, 0, 0x51 },
+ { 128, 131, 0 },
+ { 129, 130, 0 },
+ { 0, 0, 0x33 },
+ { 0, 0, 0x39 },
+ { 0, 0, 0x71 },
+ { 133, 142, 0 },
+ { 134, 137, 0 },
+ { 135, 136, 0 },
+ { 0, 0, 0x34 },
+ { 0, 0, 0x38 },
+ { 138, 141, 0 },
+ { 139, 140, 0 },
+ { 0, 0, 0x35 },
+ { 0, 0, 0x7A },
+ { 0, 0, 0x26 },
+ { 143, 150, 0 },
+ { 144, 147, 0 },
+ { 145, 146, 0 },
+ { 0, 0, 0x36 },
+ { 0, 0, 0x29 },
+ { 148, 149, 0 },
+ { 0, 0, 0x28 },
+ { 0, 0, 0x37 },
+ { 151, 158, 0 },
+ { 152, 155, 0 },
+ { 153, 154, 0 },
+ { 0, 0, 0x24 },
+ { 0, 0, 0x22 },
+ { 156, 157, 0 },
+ { 0, 0, 0x2F },
+ { 0, 0, 0x3C },
+ { 159, 162, 0 },
+ { 160, 161, 0 },
+ { 0, 0, 0x3E },
+ { 0, 0, 0x25 },
+ { 163, 164, 0 },
+ { 0, 0, 0x23 },
+ { 165, 166, 0 },
+ { 0, 0, 0x5F },
+ { 0, 0, 0x7C }
+};
+
+const HuffTree Text::_huffTree_00267[] = {
+ { 1, 20, 0 },
+ { 2, 7, 0 },
+ { 3, 6, 0 },
+ { 4, 5, 0 },
+ { 0, 0, 0x20 },
+ { 0, 0, 0x65 },
+ { 0, 0, 0x73 },
+ { 8, 15, 0 },
+ { 9, 12, 0 },
+ { 10, 11, 0 },
+ { 0, 0, 0x6d },
+ { 0, 0, 0x61 },
+ { 13, 14, 0 },
+ { 0, 0, 0x0 },
+ { 0, 0, 0x6e },
+ { 16, 19, 0 },
+ { 17, 18, 0 },
+ { 0, 0, 0x69 },
+ { 0, 0, 0x74 },
+ { 0, 0, 0x75 },
+ { 21, 44, 0 },
+ { 22, 31, 0 },
+ { 23, 28, 0 },
+ { 24, 27, 0 },
+ { 25, 26, 0 },
+ { 0, 0, 0x2e },
+ { 0, 0, 0x72 },
+ { 0, 0, 0x45 },
+ { 29, 30, 0 },
+ { 0, 0, 0x6f },
+ { 0, 0, 0x68 },
+ { 32, 39, 0 },
+ { 33, 36, 0 },
+ { 34, 35, 0 },
+ { 0, 0, 0x44 },
+ { 0, 0, 0x67 },
+ { 37, 38, 0 },
+ { 0, 0, 0x49 },
+ { 0, 0, 0x79 },
+ { 40, 43, 0 },
+ { 41, 42, 0 },
+ { 0, 0, 0x53 },
+ { 0, 0, 0x64 },
+ { 0, 0, 0x6c },
+ { 45, 64, 0 },
+ { 46, 53, 0 },
+ { 47, 50, 0 },
+ { 48, 49, 0 },
+ { 0, 0, 0x54 },
+ { 0, 0, 0x4e },
+ { 51, 52, 0 },
+ { 0, 0, 0x63 },
+ { 0, 0, 0x52 },
+ { 54, 59, 0 },
+ { 55, 58, 0 },
+ { 56, 57, 0 },
+ { 0, 0, 0x41 },
+ { 0, 0, 0x4f },
+ { 0, 0, 0x4c },
+ { 60, 63, 0 },
+ { 61, 62, 0 },
+ { 0, 0, 0x48 },
+ { 0, 0, 0x43 },
+ { 0, 0, 0x55 },
+ { 65, 84, 0 },
+ { 66, 75, 0 },
+ { 67, 72, 0 },
+ { 68, 71, 0 },
+ { 69, 70, 0 },
+ { 0, 0, 0x62 },
+ { 0, 0, 0x21 },
+ { 0, 0, 0x27 },
+ { 73, 74, 0 },
+ { 0, 0, 0x77 },
+ { 0, 0, 0x66 },
+ { 76, 81, 0 },
+ { 77, 80, 0 },
+ { 78, 79, 0 },
+ { 0, 0, 0x47 },
+ { 0, 0, 0x4d },
+ { 0, 0, 0x2c },
+ { 82, 83, 0 },
+ { 0, 0, 0x57 },
+ { 0, 0, 0x42 },
+ { 85, 98, 0 },
+ { 86, 93, 0 },
+ { 87, 90, 0 },
+ { 88, 89, 0 },
+ { 0, 0, 0x3f },
+ { 0, 0, 0x50 },
+ { 91, 92, 0 },
+ { 0, 0, 0x6b },
+ { 0, 0, 0x70 },
+ { 94, 97, 0 },
+ { 95, 96, 0 },
+ { 0, 0, 0x76 },
+ { 0, 0, 0x46 },
+ { 0, 0, 0x4b },
+ { 99, 110, 0 },
+ { 100, 105, 0 },
+ { 101, 104, 0 },
+ { 102, 103, 0 },
+ { 0, 0, 0x56 },
+ { 0, 0, 0x59 },
+ { 0, 0, 0x7a },
+ { 106, 109, 0 },
+ { 107, 108, 0 },
+ { 0, 0, 0x2d },
+ { 0, 0, 0x23 },
+ { 0, 0, 0x4a },
+ { 111, 124, 0 },
+ { 112, 117, 0 },
+ { 113, 116, 0 },
+ { 114, 115, 0 },
+ { 0, 0, 0x5a },
+ { 0, 0, 0x28 },
+ { 0, 0, 0x29 },
+ { 118, 121, 0 },
+ { 119, 120, 0 },
+ { 0, 0, 0x6a },
+ { 0, 0, 0x2b },
+ { 122, 123, 0 },
+ { 0, 0, 0x2a },
+ { 0, 0, 0x71 },
+ { 125, 138, 0 },
+ { 126, 131, 0 },
+ { 127, 130, 0 },
+ { 128, 129, 0 },
+ { 0, 0, 0x7d },
+ { 0, 0, 0x3a },
+ { 0, 0, 0x60 },
+ { 132, 135, 0 },
+ { 133, 134, 0 },
+ { 0, 0, 0x30 },
+ { 0, 0, 0x32 },
+ { 136, 137, 0 },
+ { 0, 0, 0x31 },
+ { 0, 0, 0x51 },
+ { 139, 150, 0 },
+ { 140, 145, 0 },
+ { 141, 144, 0 },
+ { 142, 143, 0 },
+ { 0, 0, 0x58 },
+ { 0, 0, 0x78 },
+ { 0, 0, 0x33 },
+ { 146, 149, 0 },
+ { 147, 148, 0 },
+ { 0, 0, 0x39 },
+ { 0, 0, 0x34 },
+ { 0, 0, 0x3c },
+ { 151, 160, 0 },
+ { 152, 157, 0 },
+ { 153, 156, 0 },
+ { 154, 155, 0 },
+ { 0, 0, 0x38 },
+ { 0, 0, 0x5d },
+ { 0, 0, 0x3d },
+ { 158, 159, 0 },
+ { 0, 0, 0x26 },
+ { 0, 0, 0x35 },
+ { 161, 168, 0 },
+ { 162, 165, 0 },
+ { 163, 164, 0 },
+ { 0, 0, 0x36 },
+ { 0, 0, 0x2f },
+ { 166, 167, 0 },
+ { 0, 0, 0x37 },
+ { 0, 0, 0x24 },
+ { 169, 174, 0 },
+ { 170, 173, 0 },
+ { 171, 172, 0 },
+ { 0, 0, 0x3e },
+ { 0, 0, 0x25 },
+ { 0, 0, 0x5e },
+ { 175, 180, 0 },
+ { 176, 179, 0 },
+ { 177, 178, 0 },
+ { 0, 0, 0x22 },
+ { 0, 0, 0x40 },
+ { 0, 0, 0x5b },
+ { 181, 184, 0 },
+ { 182, 183, 0 },
+ { 0, 0, 0x5f },
+ { 0, 0, 0x7b },
+ { 185, 186, 0 },
+ { 0, 0, 0x5c },
+ { 0, 0, 0x7c },
+};
+
+const HuffTree Text::_huffTree_00288[] = {
+ { 1, 20, 0 },
+ { 2, 7, 0 },
+ { 3, 4, 0 },
+ { 0, 0, 0x20 },
+ { 5, 6, 0 },
+ { 0, 0, 0x65 },
+ { 0, 0, 0x73 },
+ { 8, 15, 0 },
+ { 9, 12, 0 },
+ { 10, 11, 0 },
+ { 0, 0, 0x6e },
+ { 0, 0, 0x69 },
+ { 13, 14, 0 },
+ { 0, 0, 0x74 },
+ { 0, 0, 0x61 },
+ { 16, 19, 0 },
+ { 17, 18, 0 },
+ { 0, 0, 0x2e },
+ { 0, 0, 0x0 },
+ { 0, 0, 0x45 },
+ { 21, 44, 0 },
+ { 22, 31, 0 },
+ { 23, 28, 0 },
+ { 24, 27, 0 },
+ { 25, 26, 0 },
+ { 0, 0, 0x72 },
+ { 0, 0, 0x6f },
+ { 0, 0, 0x75 },
+ { 29, 30, 0 },
+ { 0, 0, 0x6d },
+ { 0, 0, 0x68 },
+ { 32, 39, 0 },
+ { 33, 36, 0 },
+ { 34, 35, 0 },
+ { 0, 0, 0x49 },
+ { 0, 0, 0x6c },
+ { 37, 38, 0 },
+ { 0, 0, 0x53 },
+ { 0, 0, 0x52 },
+ { 40, 43, 0 },
+ { 41, 42, 0 },
+ { 0, 0, 0x64 },
+ { 0, 0, 0x4e },
+ { 0, 0, 0x54 },
+ { 45, 68, 0 },
+ { 46, 55, 0 },
+ { 47, 52, 0 },
+ { 48, 51, 0 },
+ { 49, 50, 0 },
+ { 0, 0, 0x63 },
+ { 0, 0, 0x41 },
+ { 0, 0, 0x4f },
+ { 53, 54, 0 },
+ { 0, 0, 0x44 },
+ { 0, 0, 0x4c },
+ { 56, 63, 0 },
+ { 57, 60, 0 },
+ { 58, 59, 0 },
+ { 0, 0, 0x43 },
+ { 0, 0, 0x67 },
+ { 61, 62, 0 },
+ { 0, 0, 0x55 },
+ { 0, 0, 0x27 },
+ { 64, 67, 0 },
+ { 65, 66, 0 },
+ { 0, 0, 0x79 },
+ { 0, 0, 0x48 },
+ { 0, 0, 0x70 },
+ { 69, 88, 0 },
+ { 70, 79, 0 },
+ { 71, 76, 0 },
+ { 72, 75, 0 },
+ { 73, 74, 0 },
+ { 0, 0, 0x21 },
+ { 0, 0, 0x4d },
+ { 0, 0, 0x62 },
+ { 77, 78, 0 },
+ { 0, 0, 0x76 },
+ { 0, 0, 0x2c },
+ { 80, 85, 0 },
+ { 81, 84, 0 },
+ { 82, 83, 0 },
+ { 0, 0, 0x50 },
+ { 0, 0, 0x66 },
+ { 0, 0, 0x3f },
+ { 86, 87, 0 },
+ { 0, 0, 0x47 },
+ { 0, 0, 0x42 },
+ { 89, 104, 0 },
+ { 90, 97, 0 },
+ { 91, 94, 0 },
+ { 92, 93, 0 },
+ { 0, 0, 0x77 },
+ { 0, 0, 0x57 },
+ { 95, 96, 0 },
+ { 0, 0, 0x46 },
+ { 0, 0, 0x56 },
+ { 98, 101, 0 },
+ { 99, 100, 0 },
+ { 0, 0, 0x6b },
+ { 0, 0, 0x7a },
+ { 102, 103, 0 },
+ { 0, 0, 0x4b },
+ { 0, 0, 0x2d },
+ { 105, 116, 0 },
+ { 106, 111, 0 },
+ { 107, 110, 0 },
+ { 108, 109, 0 },
+ { 0, 0, 0x4a },
+ { 0, 0, 0x2b },
+ { 0, 0, 0x71 },
+ { 112, 115, 0 },
+ { 113, 114, 0 },
+ { 0, 0, 0x59 },
+ { 0, 0, 0x6a },
+ { 0, 0, 0x5a },
+ { 117, 132, 0 },
+ { 118, 125, 0 },
+ { 119, 122, 0 },
+ { 120, 121, 0 },
+ { 0, 0, 0x23 },
+ { 0, 0, 0x51 },
+ { 123, 124, 0 },
+ { 0, 0, 0x28 },
+ { 0, 0, 0x29 },
+ { 126, 129, 0 },
+ { 127, 128, 0 },
+ { 0, 0, 0x3c },
+ { 0, 0, 0x78 },
+ { 130, 131, 0 },
+ { 0, 0, 0x3a },
+ { 0, 0, 0x2a },
+ { 133, 146, 0 },
+ { 134, 141, 0 },
+ { 135, 138, 0 },
+ { 136, 137, 0 },
+ { 0, 0, 0x58 },
+ { 0, 0, 0x7d },
+ { 139, 140, 0 },
+ { 0, 0, 0x3d },
+ { 0, 0, 0x60 },
+ { 142, 145, 0 },
+ { 143, 144, 0 },
+ { 0, 0, 0x5d },
+ { 0, 0, 0x30 },
+ { 0, 0, 0x32 },
+ { 147, 158, 0 },
+ { 148, 153, 0 },
+ { 149, 152, 0 },
+ { 150, 151, 0 },
+ { 0, 0, 0x31 },
+ { 0, 0, 0x33 },
+ { 0, 0, 0x39 },
+ { 154, 157, 0 },
+ { 155, 156, 0 },
+ { 0, 0, 0x2f },
+ { 0, 0, 0x34 },
+ { 0, 0, 0x5e },
+ { 159, 168, 0 },
+ { 160, 165, 0 },
+ { 161, 164, 0 },
+ { 162, 163, 0 },
+ { 0, 0, 0x38 },
+ { 0, 0, 0x3e },
+ { 0, 0, 0x26 },
+ { 166, 167, 0 },
+ { 0, 0, 0x35 },
+ { 0, 0, 0x40 },
+ { 169, 176, 0 },
+ { 170, 173, 0 },
+ { 171, 172, 0 },
+ { 0, 0, 0x36 },
+ { 0, 0, 0x5f },
+ { 174, 175, 0 },
+ { 0, 0, 0x5c },
+ { 0, 0, 0x37 },
+ { 177, 182, 0 },
+ { 178, 181, 0 },
+ { 179, 180, 0 },
+ { 0, 0, 0x5b },
+ { 0, 0, 0x24 },
+ { 0, 0, 0x7b },
+ { 183, 186, 0 },
+ { 184, 185, 0 },
+ { 0, 0, 0x25 },
+ { 0, 0, 0x9 },
+ { 187, 188, 0 },
+ { 0, 0, 0x22 },
+ { 0, 0, 0x7c },
+};
+
+const HuffTree Text::_huffTree_00303[] = {
+ { 1, 22, 0 },
+ { 2, 9, 0 },
+ { 3, 6, 0 },
+ { 4, 5, 0 },
+ { 0, 0, 0x20 },
+ { 0, 0, 0x65 },
+ { 7, 8, 0 },
+ { 0, 0, 0x74 },
+ { 0, 0, 0x73 },
+ { 10, 17, 0 },
+ { 11, 14, 0 },
+ { 12, 13, 0 },
+ { 0, 0, 0x6e },
+ { 0, 0, 0x61 },
+ { 15, 16, 0 },
+ { 0, 0, 0x69 },
+ { 0, 0, 0x2e },
+ { 18, 21, 0 },
+ { 19, 20, 0 },
+ { 0, 0, 0x6f },
+ { 0, 0, 0x0 },
+ { 0, 0, 0x45 },
+ { 23, 46, 0 },
+ { 24, 35, 0 },
+ { 25, 30, 0 },
+ { 26, 29, 0 },
+ { 27, 28, 0 },
+ { 0, 0, 0x72 },
+ { 0, 0, 0x75 },
+ { 0, 0, 0x68 },
+ { 31, 34, 0 },
+ { 32, 33, 0 },
+ { 0, 0, 0x49 },
+ { 0, 0, 0x6d },
+ { 0, 0, 0x6c },
+ { 36, 41, 0 },
+ { 37, 40, 0 },
+ { 38, 39, 0 },
+ { 0, 0, 0x54 },
+ { 0, 0, 0x53 },
+ { 0, 0, 0x52 },
+ { 42, 45, 0 },
+ { 43, 44, 0 },
+ { 0, 0, 0x64 },
+ { 0, 0, 0x4e },
+ { 0, 0, 0x41 },
+ { 47, 70, 0 },
+ { 48, 59, 0 },
+ { 49, 54, 0 },
+ { 50, 53, 0 },
+ { 51, 52, 0 },
+ { 0, 0, 0x63 },
+ { 0, 0, 0x4f },
+ { 0, 0, 0x44 },
+ { 55, 58, 0 },
+ { 56, 57, 0 },
+ { 0, 0, 0x4c },
+ { 0, 0, 0x43 },
+ { 0, 0, 0x79 },
+ { 60, 65, 0 },
+ { 61, 64, 0 },
+ { 62, 63, 0 },
+ { 0, 0, 0x67 },
+ { 0, 0, 0x27 },
+ { 0, 0, 0x55 },
+ { 66, 69, 0 },
+ { 67, 68, 0 },
+ { 0, 0, 0x48 },
+ { 0, 0, 0x21 },
+ { 0, 0, 0x70 },
+ { 71, 88, 0 },
+ { 72, 81, 0 },
+ { 73, 78, 0 },
+ { 74, 77, 0 },
+ { 75, 76, 0 },
+ { 0, 0, 0x4d },
+ { 0, 0, 0x62 },
+ { 0, 0, 0x3f },
+ { 79, 80, 0 },
+ { 0, 0, 0x76 },
+ { 0, 0, 0x66 },
+ { 82, 85, 0 },
+ { 83, 84, 0 },
+ { 0, 0, 0x50 },
+ { 0, 0, 0x2c },
+ { 86, 87, 0 },
+ { 0, 0, 0x77 },
+ { 0, 0, 0x47 },
+ { 89, 102, 0 },
+ { 90, 95, 0 },
+ { 91, 94, 0 },
+ { 92, 93, 0 },
+ { 0, 0, 0x42 },
+ { 0, 0, 0x57 },
+ { 0, 0, 0x6b },
+ { 96, 99, 0 },
+ { 97, 98, 0 },
+ { 0, 0, 0x46 },
+ { 0, 0, 0x56 },
+ { 100, 101, 0 },
+ { 0, 0, 0x59 },
+ { 0, 0, 0x4b },
+ { 103, 114, 0 },
+ { 104, 109, 0 },
+ { 105, 108, 0 },
+ { 106, 107, 0 },
+ { 0, 0, 0x2d },
+ { 0, 0, 0x7a },
+ { 0, 0, 0x4a },
+ { 110, 113, 0 },
+ { 111, 112, 0 },
+ { 0, 0, 0x71 },
+ { 0, 0, 0x2b },
+ { 0, 0, 0x6a },
+ { 115, 132, 0 },
+ { 116, 123, 0 },
+ { 117, 120, 0 },
+ { 118, 119, 0 },
+ { 0, 0, 0x5a },
+ { 0, 0, 0x23 },
+ { 121, 122, 0 },
+ { 0, 0, 0x51 },
+ { 0, 0, 0x78 },
+ { 124, 129, 0 },
+ { 125, 128, 0 },
+ { 126, 127, 0 },
+ { 0, 0, 0x3a },
+ { 0, 0, 0x29 },
+ { 0, 0, 0x28 },
+ { 130, 131, 0 },
+ { 0, 0, 0x3c },
+ { 0, 0, 0x58 },
+ { 133, 148, 0 },
+ { 134, 141, 0 },
+ { 135, 138, 0 },
+ { 136, 137, 0 },
+ { 0, 0, 0x2a },
+ { 0, 0, 0x60 },
+ { 139, 140, 0 },
+ { 0, 0, 0x7d },
+ { 0, 0, 0x3d },
+ { 142, 145, 0 },
+ { 143, 144, 0 },
+ { 0, 0, 0x32 },
+ { 0, 0, 0x30 },
+ { 146, 147, 0 },
+ { 0, 0, 0x5d },
+ { 0, 0, 0x31 },
+ { 149, 160, 0 },
+ { 150, 155, 0 },
+ { 151, 154, 0 },
+ { 152, 153, 0 },
+ { 0, 0, 0x7e },
+ { 0, 0, 0x33 },
+ { 0, 0, 0x7f },
+ { 156, 159, 0 },
+ { 157, 158, 0 },
+ { 0, 0, 0x39 },
+ { 0, 0, 0x34 },
+ { 0, 0, 0x2f },
+ { 161, 172, 0 },
+ { 162, 167, 0 },
+ { 163, 166, 0 },
+ { 164, 165, 0 },
+ { 0, 0, 0x38 },
+ { 0, 0, 0x5e },
+ { 0, 0, 0x26 },
+ { 168, 171, 0 },
+ { 169, 170, 0 },
+ { 0, 0, 0x35 },
+ { 0, 0, 0x36 },
+ { 0, 0, 0x3e },
+ { 173, 182, 0 },
+ { 174, 179, 0 },
+ { 175, 178, 0 },
+ { 176, 177, 0 },
+ { 0, 0, 0x40 },
+ { 0, 0, 0x37 },
+ { 0, 0, 0x5f },
+ { 180, 181, 0 },
+ { 0, 0, 0x5c },
+ { 0, 0, 0x24 },
+ { 183, 190, 0 },
+ { 184, 187, 0 },
+ { 185, 186, 0 },
+ { 0, 0, 0x5b },
+ { 0, 0, 0x80 },
+ { 188, 189, 0 },
+ { 0, 0, 0x81 },
+ { 0, 0, 0x22 },
+ { 191, 194, 0 },
+ { 192, 193, 0 },
+ { 0, 0, 0x25 },
+ { 0, 0, 0x82 },
+ { 195, 196, 0 },
+ { 0, 0, 0x7b },
+ { 197, 198, 0 },
+ { 0, 0, 0x9 },
+ { 0, 0, 0x7c },
+};
+
+const HuffTree Text::_huffTree_00331[] = {
+ { 1, 20, 0 },
+ { 2, 7, 0 },
+ { 3, 4, 0 },
+ { 0, 0, 0x20 },
+ { 5, 6, 0 },
+ { 0, 0, 0x65 },
+ { 0, 0, 0x61 },
+ { 8, 15, 0 },
+ { 9, 12, 0 },
+ { 10, 11, 0 },
+ { 0, 0, 0x6f },
+ { 0, 0, 0x73 },
+ { 13, 14, 0 },
+ { 0, 0, 0x74 },
+ { 0, 0, 0x6e },
+ { 16, 19, 0 },
+ { 17, 18, 0 },
+ { 0, 0, 0x2e },
+ { 0, 0, 0x69 },
+ { 0, 0, 0x72 },
+ { 21, 44, 0 },
+ { 22, 31, 0 },
+ { 23, 28, 0 },
+ { 24, 27, 0 },
+ { 25, 26, 0 },
+ { 0, 0, 0x0 },
+ { 0, 0, 0x45 },
+ { 0, 0, 0x75 },
+ { 29, 30, 0 },
+ { 0, 0, 0x6d },
+ { 0, 0, 0x41 },
+ { 32, 39, 0 },
+ { 33, 36, 0 },
+ { 34, 35, 0 },
+ { 0, 0, 0x6c },
+ { 0, 0, 0x49 },
+ { 37, 38, 0 },
+ { 0, 0, 0x64 },
+ { 0, 0, 0x52 },
+ { 40, 43, 0 },
+ { 41, 42, 0 },
+ { 0, 0, 0x4e },
+ { 0, 0, 0x53 },
+ { 0, 0, 0x54 },
+ { 45, 68, 0 },
+ { 46, 55, 0 },
+ { 47, 52, 0 },
+ { 48, 51, 0 },
+ { 49, 50, 0 },
+ { 0, 0, 0x4f },
+ { 0, 0, 0x68 },
+ { 0, 0, 0x63 },
+ { 53, 54, 0 },
+ { 0, 0, 0x44 },
+ { 0, 0, 0x67 },
+ { 56, 63, 0 },
+ { 57, 60, 0 },
+ { 58, 59, 0 },
+ { 0, 0, 0x4c },
+ { 0, 0, 0x43 },
+ { 61, 62, 0 },
+ { 0, 0, 0x70 },
+ { 0, 0, 0x55 },
+ { 64, 67, 0 },
+ { 65, 66, 0 },
+ { 0, 0, 0x21 },
+ { 0, 0, 0x79 },
+ { 0, 0, 0x4d },
+ { 69, 88, 0 },
+ { 70, 79, 0 },
+ { 71, 76, 0 },
+ { 72, 75, 0 },
+ { 73, 74, 0 },
+ { 0, 0, 0x50 },
+ { 0, 0, 0x76 },
+ { 0, 0, 0x48 },
+ { 77, 78, 0 },
+ { 0, 0, 0x3f },
+ { 0, 0, 0x62 },
+ { 80, 85, 0 },
+ { 81, 84, 0 },
+ { 82, 83, 0 },
+ { 0, 0, 0x27 },
+ { 0, 0, 0x66 },
+ { 0, 0, 0x2c },
+ { 86, 87, 0 },
+ { 0, 0, 0x47 },
+ { 0, 0, 0x42 },
+ { 89, 108, 0 },
+ { 90, 99, 0 },
+ { 91, 96, 0 },
+ { 92, 95, 0 },
+ { 93, 94, 0 },
+ { 0, 0, 0x56 },
+ { 0, 0, 0x6b },
+ { 0, 0, 0x46 },
+ { 97, 98, 0 },
+ { 0, 0, 0x71 },
+ { 0, 0, 0x2a },
+ { 100, 105, 0 },
+ { 101, 104, 0 },
+ { 102, 103, 0 },
+ { 0, 0, 0x77 },
+ { 0, 0, 0x4b },
+ { 0, 0, 0x2d },
+ { 106, 107, 0 },
+ { 0, 0, 0x57 },
+ { 0, 0, 0x4a },
+ { 109, 126, 0 },
+ { 110, 117, 0 },
+ { 111, 114, 0 },
+ { 112, 113, 0 },
+ { 0, 0, 0x7a },
+ { 0, 0, 0x2b },
+ { 115, 116, 0 },
+ { 0, 0, 0x59 },
+ { 0, 0, 0x6a },
+ { 118, 123, 0 },
+ { 119, 122, 0 },
+ { 120, 121, 0 },
+ { 0, 0, 0x85 },
+ { 0, 0, 0x29 },
+ { 0, 0, 0x51 },
+ { 124, 125, 0 },
+ { 0, 0, 0x5a },
+ { 0, 0, 0x7e },
+ { 127, 148, 0 },
+ { 128, 137, 0 },
+ { 129, 134, 0 },
+ { 130, 133, 0 },
+ { 131, 132, 0 },
+ { 0, 0, 0x8b },
+ { 0, 0, 0x3c },
+ { 0, 0, 0x8a },
+ { 135, 136, 0 },
+ { 0, 0, 0x7f },
+ { 0, 0, 0x3a },
+ { 138, 143, 0 },
+ { 139, 142, 0 },
+ { 140, 141, 0 },
+ { 0, 0, 0x87 },
+ { 0, 0, 0x23 },
+ { 0, 0, 0x78 },
+ { 144, 147, 0 },
+ { 145, 146, 0 },
+ { 0, 0, 0x58 },
+ { 0, 0, 0x91 },
+ { 0, 0, 0x83 },
+ { 149, 168, 0 },
+ { 150, 159, 0 },
+ { 151, 156, 0 },
+ { 152, 155, 0 },
+ { 153, 154, 0 },
+ { 0, 0, 0x88 },
+ { 0, 0, 0x60 },
+ { 0, 0, 0x32 },
+ { 157, 158, 0 },
+ { 0, 0, 0x30 },
+ { 0, 0, 0x31 },
+ { 160, 165, 0 },
+ { 161, 164, 0 },
+ { 162, 163, 0 },
+ { 0, 0, 0x28 },
+ { 0, 0, 0x2f },
+ { 0, 0, 0x5d },
+ { 166, 167, 0 },
+ { 0, 0, 0x3d },
+ { 0, 0, 0x86 },
+ { 169, 184, 0 },
+ { 170, 177, 0 },
+ { 171, 174, 0 },
+ { 172, 173, 0 },
+ { 0, 0, 0x5e },
+ { 0, 0, 0x33 },
+ { 175, 176, 0 },
+ { 0, 0, 0x39 },
+ { 0, 0, 0x34 },
+ { 178, 181, 0 },
+ { 179, 180, 0 },
+ { 0, 0, 0x7d },
+ { 0, 0, 0x38 },
+ { 182, 183, 0 },
+ { 0, 0, 0x5c },
+ { 0, 0, 0x22 },
+ { 185, 198, 0 },
+ { 186, 193, 0 },
+ { 187, 190, 0 },
+ { 188, 189, 0 },
+ { 0, 0, 0x3e },
+ { 0, 0, 0x26 },
+ { 191, 192, 0 },
+ { 0, 0, 0x8d },
+ { 0, 0, 0x7b },
+ { 194, 197, 0 },
+ { 195, 196, 0 },
+ { 0, 0, 0x35 },
+ { 0, 0, 0x36 },
+ { 0, 0, 0x8f },
+ { 199, 210, 0 },
+ { 200, 205, 0 },
+ { 201, 204, 0 },
+ { 202, 203, 0 },
+ { 0, 0, 0x8e },
+ { 0, 0, 0x8c },
+ { 0, 0, 0x37 },
+ { 206, 209, 0 },
+ { 207, 208, 0 },
+ { 0, 0, 0x89 },
+ { 0, 0, 0x24 },
+ { 0, 0, 0x92 },
+ { 211, 218, 0 },
+ { 212, 215, 0 },
+ { 213, 214, 0 },
+ { 0, 0, 0x5b },
+ { 0, 0, 0x80 },
+ { 216, 217, 0 },
+ { 0, 0, 0x81 },
+ { 0, 0, 0x40 },
+ { 219, 222, 0 },
+ { 220, 221, 0 },
+ { 0, 0, 0x5f },
+ { 0, 0, 0x82 },
+ { 223, 224, 0 },
+ { 0, 0, 0x25 },
+ { 225, 226, 0 },
+ { 0, 0, 0x9 },
+ { 227, 228, 0 },
+ { 0, 0, 0x3b },
+ { 0, 0, 0x7c },
+};
+
+const HuffTree Text::_huffTree_00348[] = {
+ { 1, 20, 0 },
+ { 2, 7, 0 },
+ { 3, 4, 0 },
+ { 0, 0, 0x20 },
+ { 5, 6, 0 },
+ { 0, 0, 0x65 },
+ { 0, 0, 0x61 },
+ { 8, 15, 0 },
+ { 9, 12, 0 },
+ { 10, 11, 0 },
+ { 0, 0, 0x6F },
+ { 0, 0, 0x73 },
+ { 13, 14, 0 },
+ { 0, 0, 0x74 },
+ { 0, 0, 0x6E },
+ { 16, 19, 0 },
+ { 17, 18, 0 },
+ { 0, 0, 0x2E },
+ { 0, 0, 0x69 },
+ { 0, 0, 0x72 },
+ { 21, 44, 0 },
+ { 22, 31, 0 },
+ { 23, 28, 0 },
+ { 24, 27, 0 },
+ { 25, 26, 0 },
+ { 0, 0, 0x00 },
+ { 0, 0, 0x45 },
+ { 0, 0, 0x75 },
+ { 29, 30, 0 },
+ { 0, 0, 0x6D },
+ { 0, 0, 0x41 },
+ { 32, 39, 0 },
+ { 33, 36, 0 },
+ { 34, 35, 0 },
+ { 0, 0, 0x6C },
+ { 0, 0, 0x49 },
+ { 37, 38, 0 },
+ { 0, 0, 0x64 },
+ { 0, 0, 0x52 },
+ { 40, 43, 0 },
+ { 41, 42, 0 },
+ { 0, 0, 0x4E },
+ { 0, 0, 0x53 },
+ { 0, 0, 0x54 },
+ { 45, 68, 0 },
+ { 46, 55, 0 },
+ { 47, 52, 0 },
+ { 48, 51, 0 },
+ { 49, 50, 0 },
+ { 0, 0, 0x4F },
+ { 0, 0, 0x68 },
+ { 0, 0, 0x63 },
+ { 53, 54, 0 },
+ { 0, 0, 0x44 },
+ { 0, 0, 0x67 },
+ { 56, 63, 0 },
+ { 57, 60, 0 },
+ { 58, 59, 0 },
+ { 0, 0, 0x4C },
+ { 0, 0, 0x43 },
+ { 61, 62, 0 },
+ { 0, 0, 0x70 },
+ { 0, 0, 0x55 },
+ { 64, 67, 0 },
+ { 65, 66, 0 },
+ { 0, 0, 0x21 },
+ { 0, 0, 0x79 },
+ { 0, 0, 0x4D },
+ { 69, 88, 0 },
+ { 70, 79, 0 },
+ { 71, 76, 0 },
+ { 72, 75, 0 },
+ { 73, 74, 0 },
+ { 0, 0, 0x50 },
+ { 0, 0, 0x76 },
+ { 0, 0, 0x48 },
+ { 77, 78, 0 },
+ { 0, 0, 0x3F },
+ { 0, 0, 0x62 },
+ { 80, 85, 0 },
+ { 81, 84, 0 },
+ { 82, 83, 0 },
+ { 0, 0, 0x27 },
+ { 0, 0, 0x66 },
+ { 0, 0, 0x2C },
+ { 86, 87, 0 },
+ { 0, 0, 0x47 },
+ { 0, 0, 0x42 },
+ { 89, 108, 0 },
+ { 90, 99, 0 },
+ { 91, 96, 0 },
+ { 92, 95, 0 },
+ { 93, 94, 0 },
+ { 0, 0, 0x56 },
+ { 0, 0, 0x6B },
+ { 0, 0, 0x46 },
+ { 97, 98, 0 },
+ { 0, 0, 0x71 },
+ { 0, 0, 0x77 },
+ { 100, 105, 0 },
+ { 101, 104, 0 },
+ { 102, 103, 0 },
+ { 0, 0, 0x4B },
+ { 0, 0, 0x2D },
+ { 0, 0, 0x57 },
+ { 106, 107, 0 },
+ { 0, 0, 0x4A },
+ { 0, 0, 0x2A },
+ { 109, 128, 0 },
+ { 110, 117, 0 },
+ { 111, 114, 0 },
+ { 112, 113, 0 },
+ { 0, 0, 0x7A },
+ { 0, 0, 0x59 },
+ { 115, 116, 0 },
+ { 0, 0, 0x6A },
+ { 0, 0, 0x2B },
+ { 118, 123, 0 },
+ { 119, 122, 0 },
+ { 120, 121, 0 },
+ { 0, 0, 0x51 },
+ { 0, 0, 0x29 },
+ { 0, 0, 0x85 },
+ { 124, 127, 0 },
+ { 125, 126, 0 },
+ { 0, 0, 0x5A },
+ { 0, 0, 0x8B },
+ { 0, 0, 0x3C },
+ { 129, 150, 0 },
+ { 130, 139, 0 },
+ { 131, 136, 0 },
+ { 132, 135, 0 },
+ { 133, 134, 0 },
+ { 0, 0, 0x95 },
+ { 0, 0, 0x7E },
+ { 0, 0, 0x8A },
+ { 137, 138, 0 },
+ { 0, 0, 0x87 },
+ { 0, 0, 0x3A },
+ { 140, 145, 0 },
+ { 141, 144, 0 },
+ { 142, 143, 0 },
+ { 0, 0, 0x7F },
+ { 0, 0, 0x5D },
+ { 0, 0, 0x23 },
+ { 146, 149, 0 },
+ { 147, 148, 0 },
+ { 0, 0, 0x78 },
+ { 0, 0, 0x58 },
+ { 0, 0, 0x91 },
+ { 151, 172, 0 },
+ { 152, 163, 0 },
+ { 153, 158, 0 },
+ { 154, 157, 0 },
+ { 155, 156, 0 },
+ { 0, 0, 0x88 },
+ { 0, 0, 0x60 },
+ { 0, 0, 0x32 },
+ { 159, 162, 0 },
+ { 160, 161, 0 },
+ { 0, 0, 0x30 },
+ { 0, 0, 0x83 },
+ { 0, 0, 0x31 },
+ { 164, 169, 0 },
+ { 165, 168, 0 },
+ { 166, 167, 0 },
+ { 0, 0, 0x2F },
+ { 0, 0, 0x28 },
+ { 0, 0, 0x3D },
+ { 170, 171, 0 },
+ { 0, 0, 0x86 },
+ { 0, 0, 0x5E },
+ { 173, 190, 0 },
+ { 174, 181, 0 },
+ { 175, 178, 0 },
+ { 176, 177, 0 },
+ { 0, 0, 0x33 },
+ { 0, 0, 0x39 },
+ { 179, 180, 0 },
+ { 0, 0, 0x98 },
+ { 0, 0, 0x34 },
+ { 182, 187, 0 },
+ { 183, 186, 0 },
+ { 184, 185, 0 },
+ { 0, 0, 0x7D },
+ { 0, 0, 0x38 },
+ { 0, 0, 0x5C },
+ { 188, 189, 0 },
+ { 0, 0, 0x22 },
+ { 0, 0, 0x9B },
+ { 191, 206, 0 },
+ { 192, 199, 0 },
+ { 193, 196, 0 },
+ { 194, 195, 0 },
+ { 0, 0, 0x26 },
+ { 0, 0, 0x8D },
+ { 197, 198, 0 },
+ { 0, 0, 0x35 },
+ { 0, 0, 0x36 },
+ { 200, 203, 0 },
+ { 201, 202, 0 },
+ { 0, 0, 0x92 },
+ { 0, 0, 0x8F },
+ { 204, 205, 0 },
+ { 0, 0, 0x8E },
+ { 0, 0, 0x93 },
+ { 207, 222, 0 },
+ { 208, 215, 0 },
+ { 209, 212, 0 },
+ { 210, 211, 0 },
+ { 0, 0, 0x8C },
+ { 0, 0, 0x37 },
+ { 213, 214, 0 },
+ { 0, 0, 0x99 },
+ { 0, 0, 0x24 },
+ { 216, 219, 0 },
+ { 217, 218, 0 },
+ { 0, 0, 0x80 },
+ { 0, 0, 0x81 },
+ { 220, 221, 0 },
+ { 0, 0, 0x40 },
+ { 0, 0, 0x5B },
+ { 223, 232, 0 },
+ { 224, 229, 0 },
+ { 225, 228, 0 },
+ { 226, 227, 0 },
+ { 0, 0, 0x9A },
+ { 0, 0, 0x5F },
+ { 0, 0, 0x3E },
+ { 230, 231, 0 },
+ { 0, 0, 0x96 },
+ { 0, 0, 0x82 },
+ { 233, 238, 0 },
+ { 234, 237, 0 },
+ { 235, 236, 0 },
+ { 0, 0, 0x25 },
+ { 0, 0, 0x09 },
+ { 0, 0, 0x9C },
+ { 239, 240, 0 },
+ { 0, 0, 0x97 },
+ { 241, 242, 0 },
+ { 0, 0, 0x7B },
+ { 243, 244, 0 },
+ { 0, 0, 0x94 },
+ { 0, 0, 0x7C },
+};
+
+const HuffTree Text::_huffTree_00365[] = {
+ { 1, 20, 0 },
+ { 2, 7, 0 },
+ { 3, 4, 0 },
+ { 0, 0, 0x20 },
+ { 5, 6, 0 },
+ { 0, 0, 0x65 },
+ { 0, 0, 0x61 },
+ { 8, 15, 0 },
+ { 9, 12, 0 },
+ { 10, 11, 0 },
+ { 0, 0, 0x6F },
+ { 0, 0, 0x73 },
+ { 13, 14, 0 },
+ { 0, 0, 0x74 },
+ { 0, 0, 0x6E },
+ { 16, 19, 0 },
+ { 17, 18, 0 },
+ { 0, 0, 0x2E },
+ { 0, 0, 0x69 },
+ { 0, 0, 0x72 },
+ { 21, 44, 0 },
+ { 22, 31, 0 },
+ { 23, 28, 0 },
+ { 24, 27, 0 },
+ { 25, 26, 0 },
+ { 0, 0, 0x00 },
+ { 0, 0, 0x45 },
+ { 0, 0, 0x75 },
+ { 29, 30, 0 },
+ { 0, 0, 0x6D },
+ { 0, 0, 0x41 },
+ { 32, 39, 0 },
+ { 33, 36, 0 },
+ { 34, 35, 0 },
+ { 0, 0, 0x6C },
+ { 0, 0, 0x49 },
+ { 37, 38, 0 },
+ { 0, 0, 0x64 },
+ { 0, 0, 0x52 },
+ { 40, 43, 0 },
+ { 41, 42, 0 },
+ { 0, 0, 0x4E },
+ { 0, 0, 0x53 },
+ { 0, 0, 0x54 },
+ { 45, 68, 0 },
+ { 46, 55, 0 },
+ { 47, 52, 0 },
+ { 48, 51, 0 },
+ { 49, 50, 0 },
+ { 0, 0, 0x4F },
+ { 0, 0, 0x68 },
+ { 0, 0, 0x63 },
+ { 53, 54, 0 },
+ { 0, 0, 0x44 },
+ { 0, 0, 0x67 },
+ { 56, 63, 0 },
+ { 57, 60, 0 },
+ { 58, 59, 0 },
+ { 0, 0, 0x4C },
+ { 0, 0, 0x43 },
+ { 61, 62, 0 },
+ { 0, 0, 0x70 },
+ { 0, 0, 0x55 },
+ { 64, 67, 0 },
+ { 65, 66, 0 },
+ { 0, 0, 0x21 },
+ { 0, 0, 0x79 },
+ { 0, 0, 0x4D },
+ { 69, 88, 0 },
+ { 70, 79, 0 },
+ { 71, 76, 0 },
+ { 72, 75, 0 },
+ { 73, 74, 0 },
+ { 0, 0, 0x50 },
+ { 0, 0, 0x76 },
+ { 0, 0, 0x48 },
+ { 77, 78, 0 },
+ { 0, 0, 0x3F },
+ { 0, 0, 0x62 },
+ { 80, 85, 0 },
+ { 81, 84, 0 },
+ { 82, 83, 0 },
+ { 0, 0, 0x27 },
+ { 0, 0, 0x66 },
+ { 0, 0, 0x2C },
+ { 86, 87, 0 },
+ { 0, 0, 0x47 },
+ { 0, 0, 0x42 },
+ { 89, 108, 0 },
+ { 90, 99, 0 },
+ { 91, 96, 0 },
+ { 92, 95, 0 },
+ { 93, 94, 0 },
+ { 0, 0, 0x56 },
+ { 0, 0, 0x6B },
+ { 0, 0, 0x46 },
+ { 97, 98, 0 },
+ { 0, 0, 0x71 },
+ { 0, 0, 0x77 },
+ { 100, 105, 0 },
+ { 101, 104, 0 },
+ { 102, 103, 0 },
+ { 0, 0, 0x4B },
+ { 0, 0, 0x2D },
+ { 0, 0, 0x57 },
+ { 106, 107, 0 },
+ { 0, 0, 0x4A },
+ { 0, 0, 0x2A },
+ { 109, 128, 0 },
+ { 110, 117, 0 },
+ { 111, 114, 0 },
+ { 112, 113, 0 },
+ { 0, 0, 0x7A },
+ { 0, 0, 0x59 },
+ { 115, 116, 0 },
+ { 0, 0, 0x6A },
+ { 0, 0, 0x2B },
+ { 118, 123, 0 },
+ { 119, 122, 0 },
+ { 120, 121, 0 },
+ { 0, 0, 0x51 },
+ { 0, 0, 0x85 },
+ { 0, 0, 0x29 },
+ { 124, 127, 0 },
+ { 125, 126, 0 },
+ { 0, 0, 0x5A },
+ { 0, 0, 0x8B },
+ { 0, 0, 0x3C },
+ { 129, 150, 0 },
+ { 130, 139, 0 },
+ { 131, 136, 0 },
+ { 132, 135, 0 },
+ { 133, 134, 0 },
+ { 0, 0, 0x95 },
+ { 0, 0, 0x7E },
+ { 0, 0, 0x8A },
+ { 137, 138, 0 },
+ { 0, 0, 0x87 },
+ { 0, 0, 0x3A },
+ { 140, 145, 0 },
+ { 141, 144, 0 },
+ { 142, 143, 0 },
+ { 0, 0, 0x7F },
+ { 0, 0, 0x5D },
+ { 0, 0, 0x23 },
+ { 146, 149, 0 },
+ { 147, 148, 0 },
+ { 0, 0, 0x78 },
+ { 0, 0, 0x58 },
+ { 0, 0, 0x91 },
+ { 151, 172, 0 },
+ { 152, 163, 0 },
+ { 153, 158, 0 },
+ { 154, 157, 0 },
+ { 155, 156, 0 },
+ { 0, 0, 0x88 },
+ { 0, 0, 0x60 },
+ { 0, 0, 0x32 },
+ { 159, 162, 0 },
+ { 160, 161, 0 },
+ { 0, 0, 0x30 },
+ { 0, 0, 0x83 },
+ { 0, 0, 0x31 },
+ { 164, 169, 0 },
+ { 165, 168, 0 },
+ { 166, 167, 0 },
+ { 0, 0, 0x2F },
+ { 0, 0, 0x28 },
+ { 0, 0, 0x3D },
+ { 170, 171, 0 },
+ { 0, 0, 0x86 },
+ { 0, 0, 0x5E },
+ { 173, 190, 0 },
+ { 174, 181, 0 },
+ { 175, 178, 0 },
+ { 176, 177, 0 },
+ { 0, 0, 0x33 },
+ { 0, 0, 0x39 },
+ { 179, 180, 0 },
+ { 0, 0, 0x98 },
+ { 0, 0, 0x34 },
+ { 182, 187, 0 },
+ { 183, 186, 0 },
+ { 184, 185, 0 },
+ { 0, 0, 0x7D },
+ { 0, 0, 0x38 },
+ { 0, 0, 0x5C },
+ { 188, 189, 0 },
+ { 0, 0, 0x22 },
+ { 0, 0, 0x90 },
+ { 191, 206, 0 },
+ { 192, 199, 0 },
+ { 193, 196, 0 },
+ { 194, 195, 0 },
+ { 0, 0, 0x26 },
+ { 0, 0, 0x8D },
+ { 197, 198, 0 },
+ { 0, 0, 0x35 },
+ { 0, 0, 0x36 },
+ { 200, 203, 0 },
+ { 201, 202, 0 },
+ { 0, 0, 0x92 },
+ { 0, 0, 0x8F },
+ { 204, 205, 0 },
+ { 0, 0, 0x8E },
+ { 0, 0, 0x93 },
+ { 207, 220, 0 },
+ { 208, 213, 0 },
+ { 209, 212, 0 },
+ { 210, 211, 0 },
+ { 0, 0, 0x8C },
+ { 0, 0, 0x37 },
+ { 0, 0, 0x80 },
+ { 214, 217, 0 },
+ { 215, 216, 0 },
+ { 0, 0, 0x81 },
+ { 0, 0, 0x99 },
+ { 218, 219, 0 },
+ { 0, 0, 0x24 },
+ { 0, 0, 0x40 },
+ { 221, 230, 0 },
+ { 222, 227, 0 },
+ { 223, 226, 0 },
+ { 224, 225, 0 },
+ { 0, 0, 0x5B },
+ { 0, 0, 0x9A },
+ { 0, 0, 0x5F },
+ { 228, 229, 0 },
+ { 0, 0, 0x3E },
+ { 0, 0, 0x96 },
+ { 231, 236, 0 },
+ { 232, 235, 0 },
+ { 233, 234, 0 },
+ { 0, 0, 0x82 },
+ { 0, 0, 0x25 },
+ { 0, 0, 0x09 },
+ { 237, 240, 0 },
+ { 238, 239, 0 },
+ { 0, 0, 0x9C },
+ { 0, 0, 0x97 },
+ { 241, 242, 0 },
+ { 0, 0, 0x7B },
+ { 243, 244, 0 },
+ { 0, 0, 0x94 },
+ { 0, 0, 0x7C },
+};
+
+const HuffTree Text::_huffTree_00368[] = {
+ { 1, 20, 0 },
+ { 2, 7, 0 },
+ { 3, 4, 0 },
+ { 0, 0, 0x20 },
+ { 5, 6, 0 },
+ { 0, 0, 0x65 },
+ { 0, 0, 0x61 },
+ { 8, 15, 0 },
+ { 9, 12, 0 },
+ { 10, 11, 0 },
+ { 0, 0, 0x6F },
+ { 0, 0, 0x73 },
+ { 13, 14, 0 },
+ { 0, 0, 0x74 },
+ { 0, 0, 0x6E },
+ { 16, 19, 0 },
+ { 17, 18, 0 },
+ { 0, 0, 0x2E },
+ { 0, 0, 0x69 },
+ { 0, 0, 0x72 },
+ { 21, 44, 0 },
+ { 22, 31, 0 },
+ { 23, 28, 0 },
+ { 24, 27, 0 },
+ { 25, 26, 0 },
+ { 0, 0, 0x00 },
+ { 0, 0, 0x45 },
+ { 0, 0, 0x75 },
+ { 29, 30, 0 },
+ { 0, 0, 0x6D },
+ { 0, 0, 0x41 },
+ { 32, 39, 0 },
+ { 33, 36, 0 },
+ { 34, 35, 0 },
+ { 0, 0, 0x6C },
+ { 0, 0, 0x49 },
+ { 37, 38, 0 },
+ { 0, 0, 0x64 },
+ { 0, 0, 0x52 },
+ { 40, 43, 0 },
+ { 41, 42, 0 },
+ { 0, 0, 0x4E },
+ { 0, 0, 0x53 },
+ { 0, 0, 0x54 },
+ { 45, 68, 0 },
+ { 46, 55, 0 },
+ { 47, 52, 0 },
+ { 48, 51, 0 },
+ { 49, 50, 0 },
+ { 0, 0, 0x4F },
+ { 0, 0, 0x68 },
+ { 0, 0, 0x63 },
+ { 53, 54, 0 },
+ { 0, 0, 0x44 },
+ { 0, 0, 0x67 },
+ { 56, 63, 0 },
+ { 57, 60, 0 },
+ { 58, 59, 0 },
+ { 0, 0, 0x4C },
+ { 0, 0, 0x43 },
+ { 61, 62, 0 },
+ { 0, 0, 0x70 },
+ { 0, 0, 0x55 },
+ { 64, 67, 0 },
+ { 65, 66, 0 },
+ { 0, 0, 0x21 },
+ { 0, 0, 0x79 },
+ { 0, 0, 0x4D },
+ { 69, 88, 0 },
+ { 70, 79, 0 },
+ { 71, 76, 0 },
+ { 72, 75, 0 },
+ { 73, 74, 0 },
+ { 0, 0, 0x50 },
+ { 0, 0, 0x76 },
+ { 0, 0, 0x48 },
+ { 77, 78, 0 },
+ { 0, 0, 0x3F },
+ { 0, 0, 0x62 },
+ { 80, 85, 0 },
+ { 81, 84, 0 },
+ { 82, 83, 0 },
+ { 0, 0, 0x27 },
+ { 0, 0, 0x66 },
+ { 0, 0, 0x2C },
+ { 86, 87, 0 },
+ { 0, 0, 0x47 },
+ { 0, 0, 0x42 },
+ { 89, 108, 0 },
+ { 90, 99, 0 },
+ { 91, 96, 0 },
+ { 92, 95, 0 },
+ { 93, 94, 0 },
+ { 0, 0, 0x56 },
+ { 0, 0, 0x6B },
+ { 0, 0, 0x46 },
+ { 97, 98, 0 },
+ { 0, 0, 0x71 },
+ { 0, 0, 0x77 },
+ { 100, 105, 0 },
+ { 101, 104, 0 },
+ { 102, 103, 0 },
+ { 0, 0, 0x4B },
+ { 0, 0, 0x2D },
+ { 0, 0, 0x57 },
+ { 106, 107, 0 },
+ { 0, 0, 0x4A },
+ { 0, 0, 0x2A },
+ { 109, 128, 0 },
+ { 110, 117, 0 },
+ { 111, 114, 0 },
+ { 112, 113, 0 },
+ { 0, 0, 0x7A },
+ { 0, 0, 0x59 },
+ { 115, 116, 0 },
+ { 0, 0, 0x6A },
+ { 0, 0, 0x2B },
+ { 118, 123, 0 },
+ { 119, 122, 0 },
+ { 120, 121, 0 },
+ { 0, 0, 0x51 },
+ { 0, 0, 0x85 },
+ { 0, 0, 0x29 },
+ { 124, 127, 0 },
+ { 125, 126, 0 },
+ { 0, 0, 0x5A },
+ { 0, 0, 0x8B },
+ { 0, 0, 0x3C },
+ { 129, 152, 0 },
+ { 130, 139, 0 },
+ { 131, 136, 0 },
+ { 132, 135, 0 },
+ { 133, 134, 0 },
+ { 0, 0, 0x95 },
+ { 0, 0, 0x7E },
+ { 0, 0, 0x8A },
+ { 137, 138, 0 },
+ { 0, 0, 0x87 },
+ { 0, 0, 0x3A },
+ { 140, 145, 0 },
+ { 141, 144, 0 },
+ { 142, 143, 0 },
+ { 0, 0, 0x7F },
+ { 0, 0, 0x23 },
+ { 0, 0, 0x78 },
+ { 146, 149, 0 },
+ { 147, 148, 0 },
+ { 0, 0, 0x58 },
+ { 0, 0, 0x5D },
+ { 150, 151, 0 },
+ { 0, 0, 0x91 },
+ { 0, 0, 0x88 },
+ { 153, 174, 0 },
+ { 154, 165, 0 },
+ { 155, 160, 0 },
+ { 156, 159, 0 },
+ { 157, 158, 0 },
+ { 0, 0, 0x5F },
+ { 0, 0, 0x60 },
+ { 0, 0, 0x32 },
+ { 161, 164, 0 },
+ { 162, 163, 0 },
+ { 0, 0, 0x30 },
+ { 0, 0, 0x83 },
+ { 0, 0, 0x31 },
+ { 166, 171, 0 },
+ { 167, 170, 0 },
+ { 168, 169, 0 },
+ { 0, 0, 0x2F },
+ { 0, 0, 0x28 },
+ { 0, 0, 0x3D },
+ { 172, 173, 0 },
+ { 0, 0, 0x86 },
+ { 0, 0, 0x5E },
+ { 175, 190, 0 },
+ { 176, 183, 0 },
+ { 177, 180, 0 },
+ { 178, 179, 0 },
+ { 0, 0, 0x33 },
+ { 0, 0, 0x39 },
+ { 181, 182, 0 },
+ { 0, 0, 0x98 },
+ { 0, 0, 0x34 },
+ { 184, 187, 0 },
+ { 185, 186, 0 },
+ { 0, 0, 0x7D },
+ { 0, 0, 0x38 },
+ { 188, 189, 0 },
+ { 0, 0, 0x5C },
+ { 0, 0, 0x22 },
+ { 191, 206, 0 },
+ { 192, 199, 0 },
+ { 193, 196, 0 },
+ { 194, 195, 0 },
+ { 0, 0, 0x90 },
+ { 0, 0, 0x26 },
+ { 197, 198, 0 },
+ { 0, 0, 0x8D },
+ { 0, 0, 0x35 },
+ { 200, 203, 0 },
+ { 201, 202, 0 },
+ { 0, 0, 0x36 },
+ { 0, 0, 0x3E },
+ { 204, 205, 0 },
+ { 0, 0, 0x93 },
+ { 0, 0, 0x8C },
+ { 207, 220, 0 },
+ { 208, 215, 0 },
+ { 209, 212, 0 },
+ { 210, 211, 0 },
+ { 0, 0, 0x37 },
+ { 0, 0, 0x80 },
+ { 213, 214, 0 },
+ { 0, 0, 0x81 },
+ { 0, 0, 0x8E },
+ { 216, 219, 0 },
+ { 217, 218, 0 },
+ { 0, 0, 0x8F },
+ { 0, 0, 0x99 },
+ { 0, 0, 0x24 },
+ { 221, 230, 0 },
+ { 222, 227, 0 },
+ { 223, 226, 0 },
+ { 224, 225, 0 },
+ { 0, 0, 0x92 },
+ { 0, 0, 0x40 },
+ { 0, 0, 0x5B },
+ { 228, 229, 0 },
+ { 0, 0, 0x9A },
+ { 0, 0, 0x96 },
+ { 231, 236, 0 },
+ { 232, 235, 0 },
+ { 233, 234, 0 },
+ { 0, 0, 0x82 },
+ { 0, 0, 0x25 },
+ { 0, 0, 0x09 },
+ { 237, 240, 0 },
+ { 238, 239, 0 },
+ { 0, 0, 0x9C },
+ { 0, 0, 0x97 },
+ { 241, 242, 0 },
+ { 0, 0, 0x7B },
+ { 243, 244, 0 },
+ { 0, 0, 0x94 },
+ { 0, 0, 0x7C },
+};
+
+const HuffTree Text::_huffTree_00372[] = {
+ { 1, 20, 0 },
+ { 2, 7, 0 },
+ { 3, 4, 0 },
+ { 0, 0, ' ' },
+ { 5, 6, 0 },
+ { 0, 0, 'e' },
+ { 0, 0, 'a' },
+ { 8, 15, 0 },
+ { 9, 12, 0 },
+ { 10, 11, 0 },
+ { 0, 0, 'o' },
+ { 0, 0, 's' },
+ { 13, 14, 0 },
+ { 0, 0, 't' },
+ { 0, 0, 'n' },
+ { 16, 19, 0 },
+ { 17, 18, 0 },
+ { 0, 0, '.' },
+ { 0, 0, 'i' },
+ { 0, 0, 'r' },
+ { 21, 44, 0 },
+ { 22, 31, 0 },
+ { 23, 28, 0 },
+ { 24, 27, 0 },
+ { 25, 26, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 'E' },
+ { 0, 0, 'u' },
+ { 29, 30, 0 },
+ { 0, 0, 'm' },
+ { 0, 0, 'A' },
+ { 32, 39, 0 },
+ { 33, 36, 0 },
+ { 34, 35, 0 },
+ { 0, 0, 'l' },
+ { 0, 0, 'I' },
+ { 37, 38, 0 },
+ { 0, 0, 'd' },
+ { 0, 0, 'R' },
+ { 40, 43, 0 },
+ { 41, 42, 0 },
+ { 0, 0, 'N' },
+ { 0, 0, 'S' },
+ { 0, 0, 'T' },
+ { 45, 68, 0 },
+ { 46, 55, 0 },
+ { 47, 52, 0 },
+ { 48, 51, 0 },
+ { 49, 50, 0 },
+ { 0, 0, 'O' },
+ { 0, 0, 'h' },
+ { 0, 0, 'c' },
+ { 53, 54, 0 },
+ { 0, 0, 'D' },
+ { 0, 0, 'g' },
+ { 56, 63, 0 },
+ { 57, 60, 0 },
+ { 58, 59, 0 },
+ { 0, 0, 'L' },
+ { 0, 0, 'C' },
+ { 61, 62, 0 },
+ { 0, 0, 'p' },
+ { 0, 0, 'U' },
+ { 64, 67, 0 },
+ { 65, 66, 0 },
+ { 0, 0, '!' },
+ { 0, 0, 'y' },
+ { 0, 0, 'M' },
+ { 69, 88, 0 },
+ { 70, 79, 0 },
+ { 71, 76, 0 },
+ { 72, 75, 0 },
+ { 73, 74, 0 },
+ { 0, 0, 'P' },
+ { 0, 0, 'v' },
+ { 0, 0, 'H' },
+ { 77, 78, 0 },
+ { 0, 0, '?' },
+ { 0, 0, 'b' },
+ { 80, 85, 0 },
+ { 81, 84, 0 },
+ { 82, 83, 0 },
+ { 0, 0, 39 },
+ { 0, 0, 'f' },
+ { 0, 0, ',' },
+ { 86, 87, 0 },
+ { 0, 0, 'G' },
+ { 0, 0, 'B' },
+ { 89, 108, 0 },
+ { 90, 99, 0 },
+ { 91, 96, 0 },
+ { 92, 95, 0 },
+ { 93, 94, 0 },
+ { 0, 0, 'V' },
+ { 0, 0, 'k' },
+ { 0, 0, 'F' },
+ { 97, 98, 0 },
+ { 0, 0, 'q' },
+ { 0, 0, 'w' },
+ { 100, 105, 0 },
+ { 101, 104, 0 },
+ { 102, 103, 0 },
+ { 0, 0, 'K' },
+ { 0, 0, '-' },
+ { 0, 0, 'W' },
+ { 106, 107, 0 },
+ { 0, 0, 'J' },
+ { 0, 0, '*' },
+ { 109, 128, 0 },
+ { 110, 117, 0 },
+ { 111, 114, 0 },
+ { 112, 113, 0 },
+ { 0, 0, 'z' },
+ { 0, 0, 'Y' },
+ { 115, 116, 0 },
+ { 0, 0, 'j' },
+ { 0, 0, '+' },
+ { 118, 123, 0 },
+ { 119, 122, 0 },
+ { 120, 121, 0 },
+ { 0, 0, 'Q' },
+ { 0, 0, 133 },
+ { 0, 0, ')' },
+ { 124, 127, 0 },
+ { 125, 126, 0 },
+ { 0, 0, 'Z' },
+ { 0, 0, 139 },
+ { 0, 0, '<' },
+ { 129, 150, 0 },
+ { 130, 139, 0 },
+ { 131, 136, 0 },
+ { 132, 135, 0 },
+ { 133, 134, 0 },
+ { 0, 0, 149 },
+ { 0, 0, 126 },
+ { 0, 0, 138 },
+ { 137, 138, 0 },
+ { 0, 0, 135 },
+ { 0, 0, ':' },
+ { 140, 145, 0 },
+ { 141, 144, 0 },
+ { 142, 143, 0 },
+ { 0, 0, 127 },
+ { 0, 0, ']' },
+ { 0, 0, '#' },
+ { 146, 149, 0 },
+ { 147, 148, 0 },
+ { 0, 0, 'x' },
+ { 0, 0, 'X' },
+ { 0, 0, 145 },
+ { 151, 172, 0 },
+ { 152, 163, 0 },
+ { 153, 158, 0 },
+ { 154, 157, 0 },
+ { 155, 156, 0 },
+ { 0, 0, 136 },
+ { 0, 0, '`' },
+ { 0, 0, '2' },
+ { 159, 162, 0 },
+ { 160, 161, 0 },
+ { 0, 0, '0' },
+ { 0, 0, 131 },
+ { 0, 0, '1' },
+ { 164, 169, 0 },
+ { 165, 168, 0 },
+ { 166, 167, 0 },
+ { 0, 0, '/' },
+ { 0, 0, '(' },
+ { 0, 0, '=' },
+ { 170, 171, 0 },
+ { 0, 0, 134 },
+ { 0, 0, '^' },
+ { 173, 190, 0 },
+ { 174, 181, 0 },
+ { 175, 178, 0 },
+ { 176, 177, 0 },
+ { 0, 0, '3' },
+ { 0, 0, '9' },
+ { 179, 180, 0 },
+ { 0, 0, 152 },
+ { 0, 0, '4' },
+ { 182, 187, 0 },
+ { 183, 186, 0 },
+ { 184, 185, 0 },
+ { 0, 0, '}' },
+ { 0, 0, '8' },
+ { 0, 0, '\\' },
+ { 188, 189, 0 },
+ { 0, 0, '"' },
+ { 0, 0, 144 },
+ { 191, 206, 0 },
+ { 192, 199, 0 },
+ { 193, 196, 0 },
+ { 194, 195, 0 },
+ { 0, 0, '&' },
+ { 0, 0, 141 },
+ { 197, 198, 0 },
+ { 0, 0, '5' },
+ { 0, 0, '6' },
+ { 200, 203, 0 },
+ { 201, 202, 0 },
+ { 0, 0, 146 },
+ { 0, 0, 143 },
+ { 204, 205, 0 },
+ { 0, 0, 142 },
+ { 0, 0, 147 },
+ { 207, 220, 0 },
+ { 208, 213, 0 },
+ { 209, 212, 0 },
+ { 210, 211, 0 },
+ { 0, 0, 140 },
+ { 0, 0, '7' },
+ { 0, 0, 128 },
+ { 214, 217, 0 },
+ { 215, 216, 0 },
+ { 0, 0, 129 },
+ { 0, 0, 153 },
+ { 218, 219, 0 },
+ { 0, 0, '$' },
+ { 0, 0, '@' },
+ { 221, 230, 0 },
+ { 222, 227, 0 },
+ { 223, 226, 0 },
+ { 224, 225, 0 },
+ { 0, 0, '[' },
+ { 0, 0, 154 },
+ { 0, 0, '_' },
+ { 228, 229, 0 },
+ { 0, 0, '>' },
+ { 0, 0, 150 },
+ { 231, 236, 0 },
+ { 232, 235, 0 },
+ { 233, 234, 0 },
+ { 0, 0, 130 },
+ { 0, 0, '%' },
+ { 0, 0, 9 },
+ { 237, 240, 0 },
+ { 238, 239, 0 },
+ { 0, 0, 156 },
+ { 0, 0, 151 },
+ { 241, 242, 0 },
+ { 0, 0, '{' },
+ { 243, 244, 0 },
+ { 0, 0, 148 },
+ { 0, 0, '!' },
+};
+#endif
+
+} // End of namespace Sky
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Sky_Hufftext)
+_GSETPTR(Sky::Text::_huffTree_00109, GBVARS_HUFFTREE_00109_INDEX, const Sky::HuffTree, GBVARS_QUEEN)
+_GSETPTR(Sky::Text::_huffTree_00267, GBVARS_HUFFTREE_00267_INDEX, const Sky::HuffTree, GBVARS_QUEEN)
+_GSETPTR(Sky::Text::_huffTree_00288, GBVARS_HUFFTREE_00288_INDEX, const Sky::HuffTree, GBVARS_QUEEN)
+_GSETPTR(Sky::Text::_huffTree_00303, GBVARS_HUFFTREE_00303_INDEX, const Sky::HuffTree, GBVARS_QUEEN)
+_GSETPTR(Sky::Text::_huffTree_00331, GBVARS_HUFFTREE_00331_INDEX, const Sky::HuffTree, GBVARS_QUEEN)
+_GSETPTR(Sky::Text::_huffTree_00348, GBVARS_HUFFTREE_00348_INDEX, const Sky::HuffTree, GBVARS_QUEEN)
+_GSETPTR(Sky::Text::_huffTree_00365, GBVARS_HUFFTREE_00365_INDEX, const Sky::HuffTree, GBVARS_QUEEN)
+_GSETPTR(Sky::Text::_huffTree_00368, GBVARS_HUFFTREE_00368_INDEX, const Sky::HuffTree, GBVARS_QUEEN)
+_GSETPTR(Sky::Text::_huffTree_00372, GBVARS_HUFFTREE_00372_INDEX, const Sky::HuffTree, GBVARS_QUEEN)
+_GEND
+
+_GRELEASE(Sky_Hufftext)
+_GRELEASEPTR(GBVARS_HUFFTREE_00109_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_HUFFTREE_00267_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_HUFFTREE_00288_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_HUFFTREE_00303_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_HUFFTREE_00331_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_HUFFTREE_00348_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_HUFFTREE_00365_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_HUFFTREE_00368_INDEX, GBVARS_QUEEN)
+_GRELEASEPTR(GBVARS_HUFFTREE_00372_INDEX, GBVARS_QUEEN)
+_GEND
+
+#endif
diff --git a/engines/sky/intro.cpp b/engines/sky/intro.cpp
new file mode 100644
index 0000000000..ce6849fbd0
--- /dev/null
+++ b/engines/sky/intro.cpp
@@ -0,0 +1,926 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "common/system.h"
+#include "sky/disk.h"
+#include "sky/intro.h"
+#include "sky/music/musicbase.h"
+#include "sky/screen.h"
+#include "sky/sky.h"
+#include "sky/sound.h"
+#include "sky/struc.h"
+#include "sky/text.h"
+
+namespace Sky {
+
+#ifdef MACOSX
+// FIXME: DELAY is already defined in sys/param.h !
+// Better fix would be to remove sys/param.h froms stdafx.h, or
+// to change the name DELAY here to something else.
+#undef DELAY
+#endif
+
+#define SHOWSCREEN 0
+#define COMMANDEND 0 // end of COMMANDFLIRT block
+#define FADEUP 1 // fade up palette
+#define FADEDOWN 2
+#define DELAY 3
+#define DOFLIRT 4 // start flirt sequence (and wait for it to finish)
+#define SCROLLFLIRT 5 // start special floppy intro flirt sequence (and wait for it)
+#define COMMANDFLIRT 6 // start flirt sequence and wait for it, while processing command block
+#define BGFLIRT 7 // start flirt sequence without waiting for it
+#define WAITFLIRT 8 // wait for sequence started by BGFLIRT
+#define STOPFLIRT 9
+#define STARTMUSIC 10
+#define WAITMUSIC 11
+#define PLAYVOICE 12
+#define WAITVOICE 13
+#define LOADBG 14 // load new background sound
+#define PLAYBG 15 // play background sound
+#define LOOPBG 16 // loop background sound
+#define STOPBG 17 // stop background sound
+#define SEQEND 65535 // end of intro sequence
+
+#define IC_PREPARE_TEXT 20 // commands used in COMMANDFLIRT block
+#define IC_SHOW_TEXT 21
+#define IC_REMOVE_TEXT 22
+#define IC_MAKE_SOUND 23
+#define IC_FX_VOLUME 24
+
+#define FRAME_SIZE (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT)
+#define INTRO_TEXT_WIDTH 128
+//CD intro file defines
+#define CDV_00 59500
+#define CD_PAL 59501
+#define CD_1_LOG 59502
+#define CD_1 59503
+#define CDV_01 59504
+#define CDV_02 59505
+#define CD_2 59506
+#define CDV_03 59507
+#define CDV_04 59508
+#define CD_3 59509
+#define CDV_05 59510
+#define CDV_06 59511
+#define CD_5 59512
+#define CDV_07 59513
+#define CDV_08 59514
+#define CDV_09 59515
+#define CD_7 59516
+#define CDV_10 59518
+#define CD_11 59519
+#define CDV_11 59520
+#define CD_11_PAL 59521
+#define CD_11_LOG 59522
+#define CDV_12 59523
+#define CD_13 59524
+#define CDV_13 59525
+#define CDV_14 59527
+#define CDV_15 59528
+#define CD_15_PAL 59529
+#define CD_15_LOG 59530
+#define CDV_16 59531
+#define CD_17_LOG 59532
+#define CD_17 59533
+#define CDV_17 59534
+#define CDV_18 59535
+#define CDV_19 59536
+#define CD_19_PAL 59537
+#define CD_19_LOG 59538
+#define CDV_20 59539
+#define CD_20_LOG 59540
+#define CDV_21 59541
+#define CD_21_LOG 59542
+#define CDV_22 59545
+#define CDV_23 59546
+#define CD_23_PAL 59547
+#define CD_24_LOG 59550
+#define CDV_24 59551
+#define CDV_25 59554
+#define CDV_26 59556
+#define CD_27 59557
+#define CDV_27 59558
+#define CD_27_PAL 59559
+#define CD_27_LOG 59560
+#define CDV_28 59561
+#define CDV_29 59562
+#define CDV_30 59563
+#define CDV_31 59565
+#define CDV_32 59566
+#define CDV_33 59567
+#define CDV_34 59568
+#define CD_35 59569
+#define CDV_35 59570
+#define CD_35_PAL 59571
+#define CD_35_LOG 59572
+#define CDV_36 59574
+#define CD_37 59575
+#define CDV_37 59576
+#define CD_37_PAL 59577
+#define CD_37_LOG 59578
+#define CDV_38 59579
+#define CDV_39 59581
+#define CDV_40 59583
+#define CD_40_PAL 59584
+#define CD_40_LOG 59585
+#define CDV_41 59587
+#define CDV_42 59588
+#define CD_43 59589
+#define CDV_43 59590
+#define CD_43_PAL 59591
+#define CD_43_LOG 59592
+#define CDV_44 59594
+#define CD_45 59595
+#define CDV_45 59596
+#define CD_45_PAL 59597
+#define CD_45_LOG 59598
+#define CDV_46 59600
+#define CDV_47 59602
+#define CD_47_PAL 59603
+#define CD_47_LOG 59604
+#define CD_48 59605
+#define CDV_48 59606
+#define CD_48_PAL 59607
+#define CD_48_LOG 59608
+#define CD_49 59609
+#define CDV_49 59610
+#define CD_50 59611
+#define CDV_50 59612
+#define CDV_51 59613
+#define CDV_52 59614
+#define CDV_53 59615
+#define CDV_54 59616
+#define CDV_55 59618
+#define CD_55_PAL 59619
+#define CD_55_LOG 59620
+#define CDV_56 59621
+#define CDV_57 59622
+#define CD_58 59623
+#define CDV_58 59624
+#define CD_58_PAL 59625
+#define CD_58_LOG 59626
+#define CDV_59 59627
+#define CDV_60 59628
+#define CDV_61 59629
+#define CDV_62 59630
+#define CDV_63 59631
+#define CDV_64 59632
+#define CDV_65 59633
+#define CDV_66 59635
+#define CD_66_PAL 59636
+#define CD_66_LOG 59637
+#define CDV_67 59639
+#define CD_67_PAL 59640
+#define CD_67_LOG 59641
+#define CDV_68 59642
+#define CD_69 59643
+#define CDV_69 59644
+#define CD_69_PAL 59645
+#define CD_69_LOG 59646
+#define CDV_70 59647
+#define CDV_71 59648
+#define CDV_72 59649
+#define CD_72_PAL 59650
+#define CD_72_LOG 59651
+#define CD_73_PAL 59652
+#define CD_73_LOG 59653
+#define CDV_73 59654
+#define CDV_74 59655
+#define CDV_75 59656
+#define CD_76_PAL 59657
+#define CD_76_LOG 59658
+#define CDV_76 59659
+#define CDV_77 59660
+#define CD_78_PAL 59661
+#define CD_78_LOG 59662
+#define CDV_78 59663
+#define CDV_79 59664
+#define CDV_80 59665
+#define CDV_81 59666
+#define CDV_82 59667
+#define CDV_83 59668
+#define CDV_84 59669
+#define CDV_85 59670
+#define CDV_86 59671
+#define CDV_87 59672
+#define CD_100 60087
+#define CD_101_LOG 60088
+#define CD_101 60099
+#define CD_102_LOG 60090
+#define CD_102 60091
+#define CD_103_PAL 60092
+#define CD_103_LOG 60093
+#define CD_103 60094
+#define CD_104_PAL 60095
+#define CD_104_LOG 60096
+#define CD_104 60097
+#define CD_105 60098
+
+
+uint16 Intro::_mainIntroSeq[] = {
+ DELAY, 3000, // keep virgin screen up
+ FADEDOWN,
+ SHOWSCREEN, 60112, // revo screen + palette
+ FADEUP, 60113,
+ DELAY, 8000,
+ FADEDOWN,
+ SHOWSCREEN, 60114, // gibbo screen + palette
+ FADEUP, 60115,
+ DELAY, 2000,
+ FADEDOWN,
+ SEQEND
+};
+
+uint16 Intro::_cdIntroSeq[] = {
+ PLAYVOICE, 59500,
+ LOADBG, 59499,
+ LOOPBG,
+ WAITVOICE,
+ PLAYVOICE, 59504,
+ SHOWSCREEN, CD_1_LOG,
+ FADEUP, CD_PAL,
+ BGFLIRT, CD_1,
+ WAITVOICE,
+ PLAYVOICE, CDV_02,
+ WAITVOICE,
+ STOPFLIRT,
+ BGFLIRT, CD_2,
+ PLAYVOICE, CDV_03,
+ WAITVOICE,
+ PLAYVOICE, CDV_04,
+ WAITFLIRT,
+ WAITVOICE,
+ PLAYVOICE, CDV_05,
+ DELAY, 2000,
+ BGFLIRT, CD_3,
+ WAITVOICE,
+ PLAYVOICE, CDV_06,
+ WAITFLIRT,
+ WAITVOICE,
+ PLAYVOICE, CDV_07,
+ BGFLIRT, CD_5,
+ WAITVOICE,
+ PLAYVOICE, CDV_08,
+ WAITVOICE,
+ PLAYVOICE, CDV_09,
+ WAITFLIRT,
+ WAITVOICE,
+ PLAYVOICE, CDV_10,
+ BGFLIRT, CD_7,
+ WAITVOICE,
+ PLAYVOICE, CDV_11,
+ WAITFLIRT,
+ FADEDOWN,
+ SHOWSCREEN, CD_11_LOG,
+ FADEUP, CD_11_PAL,
+ WAITVOICE,
+ PLAYVOICE, CDV_12,
+ DELAY, 1600,
+ BGFLIRT, CD_11,
+ WAITVOICE,
+ PLAYVOICE, CDV_13,
+ WAITVOICE,
+ WAITFLIRT,
+ WAITVOICE,
+ PLAYVOICE, CDV_14,
+ LOADBG, 59498, // fade-in heli
+ PLAYBG,
+ DOFLIRT, CD_13,
+ WAITVOICE,
+ PLAYVOICE, CDV_15,
+ FADEDOWN,
+ SHOWSCREEN, CD_15_LOG,
+ FADEUP, CD_15_PAL,
+ WAITVOICE,
+ LOADBG, 59496, // quiet heli
+ LOOPBG,
+ PLAYVOICE, CDV_16,
+ WAITVOICE,
+ PLAYVOICE, CDV_17,
+ DELAY, 2000,
+ SHOWSCREEN, CD_17_LOG,
+ WAITVOICE,
+ BGFLIRT, CD_17,
+ PLAYVOICE, CDV_18,
+ LOADBG, 59497, // loud heli
+ LOOPBG,
+ WAITFLIRT,
+ WAITVOICE,
+ FADEDOWN,
+ SHOWSCREEN, CD_19_LOG,
+ FADEUP, CD_19_PAL,
+ PLAYVOICE, CDV_19,
+ WAITVOICE,
+ PLAYVOICE, CDV_20,
+ FADEDOWN,
+ SHOWSCREEN, CD_20_LOG,
+ FADEUP, CD_19_PAL,
+ WAITVOICE,
+ LOADBG, 59496, // quiet heli
+ LOOPBG,
+ PLAYVOICE, CDV_21,
+ FADEDOWN,
+ SHOWSCREEN, CD_21_LOG,
+ FADEUP, CD_19_PAL,
+ WAITVOICE,
+ PLAYVOICE, CDV_22,
+ LOADBG, 59494, // heli whine
+ PLAYBG,
+ WAITVOICE,
+ PLAYVOICE, CDV_23,
+ FADEDOWN,
+ WAITVOICE,
+ SHOWSCREEN, CD_24_LOG,
+ FADEUP, CD_23_PAL,
+ PLAYVOICE, CDV_24,
+ WAITVOICE,
+ PLAYVOICE, CDV_25,
+ WAITVOICE,
+ PLAYVOICE, CDV_26,
+ WAITVOICE,
+ FADEDOWN,
+ SHOWSCREEN, CD_27_LOG,
+ FADEUP, CD_27_PAL,
+ PLAYVOICE, CDV_27,
+ WAITVOICE,
+ PLAYVOICE, CDV_29,
+ WAITVOICE,
+ PLAYVOICE, CDV_30,
+ WAITVOICE,
+ BGFLIRT, CD_27,
+ PLAYVOICE, CDV_31,
+ WAITVOICE,
+ PLAYVOICE, CDV_32,
+ WAITVOICE,
+ PLAYVOICE, CDV_33,
+ WAITVOICE,
+ PLAYVOICE, CDV_34,
+ WAITFLIRT,
+ WAITVOICE,
+ PLAYVOICE, CDV_35,
+ WAITVOICE,
+ PLAYVOICE, CDV_36,
+ FADEDOWN,
+ SHOWSCREEN, CD_35_LOG,
+ FADEUP, CD_35_PAL,
+ WAITVOICE,
+ PLAYVOICE, CDV_37,
+ DOFLIRT, CD_35,
+ WAITVOICE,
+ PLAYVOICE, CDV_38,
+ DOFLIRT, CD_37,
+ WAITVOICE,
+ PLAYVOICE, CDV_39,
+ WAITVOICE,
+ FADEDOWN,
+ SHOWSCREEN, CD_40_LOG,
+ FADEUP, CD_40_PAL,
+ PLAYVOICE, CDV_40,
+ WAITVOICE,
+ PLAYVOICE, CDV_41,
+ WAITVOICE,
+ PLAYVOICE, CDV_42,
+ WAITVOICE,
+ FADEDOWN,
+ SHOWSCREEN, CD_43_LOG,
+ FADEUP, CD_43_PAL,
+ PLAYVOICE, CDV_43,
+ WAITVOICE,
+ DOFLIRT, CD_43,
+ PLAYVOICE, CDV_45,
+ FADEDOWN,
+ SHOWSCREEN, CD_45_LOG,
+ FADEUP, CD_45_PAL,
+ WAITVOICE,
+ PLAYVOICE, CDV_46,
+ DOFLIRT, CD_45,
+ WAITVOICE,
+ FADEDOWN,
+ SHOWSCREEN, CD_47_LOG,
+ FADEUP, CD_47_PAL,
+ PLAYVOICE, CDV_47,
+ WAITVOICE,
+ PLAYVOICE, CDV_48,
+ FADEDOWN,
+ SHOWSCREEN, CD_48_LOG,
+ FADEUP, CD_48_PAL,
+ WAITVOICE,
+ BGFLIRT, CD_48,
+ PLAYVOICE, CDV_49,
+ WAITVOICE,
+ PLAYVOICE, CDV_50,
+ WAITFLIRT,
+ WAITVOICE,
+ PLAYVOICE, CDV_51,
+ BGFLIRT, CD_49,
+ WAITVOICE,
+ PLAYVOICE, CDV_52,
+ WAITVOICE,
+ PLAYVOICE, CDV_53,
+ WAITVOICE,
+ WAITFLIRT,
+ PLAYVOICE, CDV_54,
+ DOFLIRT, CD_50,
+ WAITVOICE,
+ PLAYVOICE, CDV_55,
+ WAITVOICE,
+ PLAYVOICE, CDV_56,
+ FADEDOWN,
+ SHOWSCREEN, CD_55_LOG,
+ FADEUP, CD_55_PAL,
+ WAITVOICE,
+ PLAYVOICE, CDV_57,
+ WAITVOICE,
+ FADEDOWN,
+ SHOWSCREEN, CD_58_LOG,
+ FADEUP, CD_58_PAL,
+ PLAYVOICE, CDV_58,
+ WAITVOICE,
+ PLAYVOICE, CDV_59,
+ WAITVOICE,
+ PLAYVOICE, CDV_60,
+ WAITVOICE,
+ PLAYVOICE, CDV_61,
+ WAITVOICE,
+ PLAYVOICE, CDV_62,
+ BGFLIRT, CD_58,
+ WAITVOICE,
+ PLAYVOICE, CDV_63,
+ WAITVOICE,
+ PLAYVOICE, CDV_64,
+ WAITFLIRT,
+ WAITVOICE,
+ PLAYVOICE, CDV_65,
+ FADEDOWN,
+ WAITVOICE,
+ SHOWSCREEN, CD_66_LOG,
+ FADEUP, CD_66_PAL,
+ PLAYVOICE, CDV_66,
+ WAITVOICE,
+ PLAYVOICE, CDV_67,
+ FADEDOWN,
+ SHOWSCREEN, CD_67_LOG,
+ FADEUP, CD_67_PAL,
+ WAITVOICE,
+ PLAYVOICE, CDV_68,
+ WAITVOICE,
+ PLAYVOICE, CDV_69,
+ FADEDOWN,
+ SHOWSCREEN, CD_69_LOG,
+ FADEUP, CD_69_PAL,
+ WAITVOICE,
+ PLAYVOICE, CDV_70,
+ DOFLIRT, CD_69,
+ WAITVOICE,
+ FADEDOWN,
+ PLAYVOICE, CDV_71,
+ WAITVOICE,
+ SHOWSCREEN, CD_72_LOG,
+ FADEUP, CD_72_PAL,
+ PLAYVOICE, CDV_72,
+ WAITVOICE,
+ FADEDOWN,
+ SHOWSCREEN, CD_73_LOG,
+ FADEUP, CD_73_PAL,
+ PLAYVOICE, CDV_73,
+ WAITVOICE,
+ PLAYVOICE, CDV_74,
+ WAITVOICE,
+ PLAYVOICE, CDV_75,
+ FADEDOWN,
+ SHOWSCREEN, CD_76_LOG,
+ FADEUP, CD_76_PAL,
+ WAITVOICE,
+ PLAYVOICE, CDV_76,
+ WAITVOICE,
+ PLAYVOICE, CDV_77,
+ WAITVOICE,
+ FADEDOWN,
+ SHOWSCREEN, CD_78_LOG,
+ FADEUP, CD_78_PAL,
+ PLAYVOICE, CDV_78,
+ WAITVOICE,
+ PLAYVOICE, CDV_79,
+ WAITVOICE,
+ PLAYVOICE, CDV_80,
+ BGFLIRT, CD_100,
+ WAITVOICE,
+ PLAYVOICE, CDV_81,
+ WAITVOICE,
+ PLAYVOICE, CDV_82,
+ WAITVOICE,
+ WAITFLIRT,
+ SHOWSCREEN, CD_101_LOG,
+ BGFLIRT, CD_101,
+ PLAYVOICE, CDV_83,
+ WAITVOICE,
+ PLAYVOICE, CDV_84,
+ WAITVOICE,
+ PLAYVOICE, CDV_85,
+ WAITVOICE,
+ WAITFLIRT,
+ SHOWSCREEN, CD_102_LOG,
+ PLAYVOICE, CDV_86,
+ DOFLIRT, CD_102,
+ FADEDOWN,
+ SHOWSCREEN, CD_103_LOG,
+ FADEUP, CD_103_PAL,
+ BGFLIRT, CD_103,
+ WAITVOICE,
+ PLAYVOICE, CDV_87,
+ WAITFLIRT,
+ WAITVOICE,
+ STARTMUSIC, 2,
+ FADEDOWN,
+ SHOWSCREEN, CD_104_LOG,
+ FADEUP, CD_104_PAL,
+ DOFLIRT, CD_104,
+ DOFLIRT, CD_105,
+ SEQEND
+};
+
+uint16 Intro::_floppyIntroSeq[] = {
+ SHOWSCREEN, 60081,
+ FADEUP, 60080,
+ DOFLIRT, 60082,
+ DOFLIRT, 60083,
+ DOFLIRT, 60084, // Beneath a Steel Sky
+ DOFLIRT, 60085,
+ DOFLIRT, 60086,
+ SCROLLFLIRT,
+ COMMANDFLIRT, 60087, // => command list 4a
+ 136, IC_MAKE_SOUND, 1, 70,
+ 90, IC_FX_VOLUME, 80,
+ 50, IC_FX_VOLUME, 90,
+ 5, IC_FX_VOLUME, 100,
+ COMMANDEND,
+ SHOWSCREEN, 60088,
+ COMMANDFLIRT, 60089, // => command list 4b (cockpit)
+ 1000, IC_PREPARE_TEXT, 77,
+ 220, IC_SHOW_TEXT, 20, 160, // radar detects jamming signal
+ 105, IC_REMOVE_TEXT,
+ 105, IC_PREPARE_TEXT, 81,
+ 105, IC_SHOW_TEXT, 170, 86, // well switch to override you fool
+ 35, IC_REMOVE_TEXT,
+ 35, IC_PREPARE_TEXT, 477,
+ 35, IC_SHOW_TEXT, 30, 160,
+ 3, IC_REMOVE_TEXT,
+ COMMANDEND,
+ SHOWSCREEN, 60090,
+ COMMANDFLIRT, 60091, // => command list 4c
+ 1000, IC_FX_VOLUME, 100,
+ 25, IC_FX_VOLUME, 110,
+ 15, IC_FX_VOLUME, 120,
+ 4, IC_FX_VOLUME, 127,
+ COMMANDEND,
+ FADEDOWN,
+ SHOWSCREEN, 60093,
+ FADEUP, 60092,
+ COMMANDFLIRT, 60094, // => command list 5
+ 31, IC_MAKE_SOUND, 2, 127,
+ COMMANDEND,
+ WAITMUSIC,
+ FADEDOWN,
+ SHOWSCREEN, 60096,
+ STARTMUSIC, 2,
+ FADEUP, 60095,
+ COMMANDFLIRT, 60097, // => command list 6a
+ 1000, IC_PREPARE_TEXT, 478,
+ 13, IC_SHOW_TEXT, 175, 155,
+ COMMANDEND,
+ COMMANDFLIRT, 60098, // => command list 6b
+ 131, IC_REMOVE_TEXT,
+ 131, IC_PREPARE_TEXT, 479,
+ 74, IC_SHOW_TEXT, 175, 155,
+ 45, IC_REMOVE_TEXT,
+ 45, IC_PREPARE_TEXT, 162,
+ 44, IC_SHOW_TEXT, 175, 155,
+ 4, IC_REMOVE_TEXT,
+ COMMANDEND,
+ SEQEND
+};
+
+Intro::Intro(Disk *disk, Screen *screen, MusicBase *music, Sound *sound, Text *text, Audio::Mixer *mixer, OSystem *system) {
+
+ _skyDisk = disk;
+ _skyScreen = screen;
+ _skyMusic = music;
+ _skySound = sound;
+ _skyText = text;
+ _mixer = mixer;
+ _system = system;
+ _textBuf = (uint8*)malloc(10000);
+ _saveBuf = (uint8*)malloc(10000);
+ _bgBuf = NULL;
+ _quitProg = false;
+ _relDelay = 0;
+}
+
+Intro::~Intro(void) {
+
+ _mixer->stopAll();
+ _skyScreen->stopSequence();
+ if (_textBuf)
+ free(_textBuf);
+ if (_saveBuf)
+ free(_saveBuf);
+ if (_bgBuf)
+ free(_bgBuf);
+}
+
+bool Intro::doIntro(bool floppyIntro) {
+
+ if (!SkyEngine::isCDVersion())
+ floppyIntro = true;
+
+ _skyMusic->loadSection(0);
+ _skySound->loadSection(0);
+
+ if (!escDelay(3000))
+ return false;
+ if (floppyIntro)
+ _skyMusic->startMusic(1);
+
+ uint16 *seqData = _mainIntroSeq;
+ while (*seqData != SEQEND) {
+ if (!nextPart(seqData))
+ return false;
+ }
+ if (floppyIntro)
+ seqData = _floppyIntroSeq;
+ else
+ seqData = _cdIntroSeq;
+
+ while (*seqData != SEQEND) {
+ if (!nextPart(seqData))
+ return false;
+ }
+ return true;
+}
+
+bool Intro::nextPart(uint16 *&data) {
+
+ uint8 *vData = NULL;
+ // return false means cancel intro
+ uint16 command = *data++;
+ switch (command) {
+ case SHOWSCREEN:
+ _skyScreen->showScreen(*data++);
+ return true;
+ case FADEUP:
+ _skyScreen->paletteFadeUp(*data++);
+ _relDelay += 32 * 20; // hack: the screen uses a seperate delay function for the
+ // blocking fadeups. So add 32*20 msecs to out delay counter.
+ return true;
+ case FADEDOWN:
+ _skyScreen->fnFadeDown(0);
+ _relDelay += 32 * 20; // hack: see above.
+ return true;
+ case DELAY:
+ if (!escDelay(*data++))
+ return false;
+ return true;
+ case DOFLIRT:
+ _skyScreen->startSequence(*data++);
+ while (_skyScreen->sequenceRunning())
+ if (!escDelay(50))
+ return false;
+ return true;
+ case SCROLLFLIRT:
+ return floppyScrollFlirt();
+ case COMMANDFLIRT:
+ return commandFlirt(data);
+ case STOPFLIRT:
+ _skyScreen->stopSequence();
+ return true;
+ case STARTMUSIC:
+ _skyMusic->startMusic(*data++);
+ return true;
+ case WAITMUSIC:
+ while (_skyMusic->musicIsPlaying())
+ if (!escDelay(50))
+ return false;
+ return true;
+ case BGFLIRT:
+ _skyScreen->startSequence(*data++);
+ return true;
+ case WAITFLIRT:
+ while (_skyScreen->sequenceRunning())
+ if (!escDelay(50))
+ return false;
+ return true;
+ case PLAYVOICE:
+ if (!escDelay(200))
+ return false;
+ vData = _skyDisk->loadFile(*data++);
+ // HACK: Fill the header with silence. We should
+ // probably use _skySound instead of calling playRaw()
+ // directly, but this will have to do for now.
+ memset(vData, 127, sizeof(struct dataFileHeader));
+ _mixer->playRaw(&_voice, vData, _skyDisk->_lastLoadedFileSize, 11025,
+ Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED, SOUND_VOICE);
+ return true;
+ case WAITVOICE:
+ while (_mixer->isSoundHandleActive(_voice))
+ if (!escDelay(50))
+ return false;
+ return true;
+ case LOADBG:
+ _mixer->stopID(SOUND_BG);
+ if (_bgBuf)
+ free(_bgBuf);
+ _bgBuf = _skyDisk->loadFile(*data++);
+ _bgSize = _skyDisk->_lastLoadedFileSize;
+ return true;
+ case LOOPBG:
+ _mixer->stopID(SOUND_BG);
+ _mixer->playRaw(&_bgSfx, _bgBuf + 256, _bgSize - 768, 11025,
+ Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_LOOP, SOUND_BG);
+ return true;
+ case PLAYBG:
+ _mixer->stopID(SOUND_BG);
+ _mixer->playRaw(&_bgSfx, _bgBuf + 256, _bgSize - 768, 11025,
+ Audio::Mixer::FLAG_UNSIGNED, SOUND_BG);
+ return true;
+ case STOPBG:
+ _mixer->stopID(SOUND_BG);
+ return true;
+ default:
+ error("Unknown intro command %X", command);
+ }
+ return true;
+}
+
+bool Intro::floppyScrollFlirt(void) {
+
+ uint8 *scrollScreen = (uint8*)malloc(FRAME_SIZE * 2);
+ memset(scrollScreen, 0, FRAME_SIZE);
+ memcpy(scrollScreen + FRAME_SIZE, _skyScreen->giveCurrent(), FRAME_SIZE);
+ uint8 *scrollPos = scrollScreen + FRAME_SIZE;
+ uint8 *vgaData = _skyDisk->loadFile(60100);
+ uint8 *diffData = _skyDisk->loadFile(60101);
+ uint16 frameNum = READ_LE_UINT16(diffData);
+ uint8 *diffPtr = diffData + 2;
+ uint8 *vgaPtr = vgaData;
+ bool doContinue = true;
+
+ for (uint16 frameCnt = 1; (frameCnt < frameNum) && doContinue; frameCnt++) {
+ uint8 scrollVal = *diffPtr++;
+ if (scrollVal)
+ scrollPos -= scrollVal * GAME_SCREEN_WIDTH;
+
+ uint16 scrPos = 0;
+ while (scrPos < FRAME_SIZE) {
+ uint8 nrToDo, nrToSkip;
+ do {
+ nrToSkip = *diffPtr++;
+ scrPos += nrToSkip;
+ } while (nrToSkip == 255);
+ do {
+ nrToDo = *diffPtr++;
+ memcpy(scrollPos + scrPos, vgaPtr, nrToDo);
+ scrPos += nrToDo;
+ vgaPtr += nrToDo;
+ } while (nrToDo == 255);
+ }
+ _system->copyRectToScreen(scrollPos, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
+ _system->updateScreen();
+ if (!escDelay(60))
+ doContinue = false;
+ }
+ memcpy(_skyScreen->giveCurrent(), scrollPos, FRAME_SIZE);
+ free(diffData);
+ free(vgaData);
+ free(scrollScreen);
+ return doContinue;
+}
+
+bool Intro::commandFlirt(uint16 *&data) {
+
+ _skyScreen->startSequence(*data++);
+ while ((*data != COMMANDEND) || _skyScreen->sequenceRunning()) {
+ while ((_skyScreen->seqFramesLeft() < *data)) {
+ data++;
+ uint16 command = *data++;
+ switch(command) {
+ case IC_PREPARE_TEXT:
+ _skyText->displayText(*data++, _textBuf, true, INTRO_TEXT_WIDTH, 255);
+ break;
+ case IC_SHOW_TEXT:
+ ((dataFileHeader*)_textBuf)->s_x = *data++;
+ ((dataFileHeader*)_textBuf)->s_y = *data++;
+ showTextBuf();
+ break;
+ case IC_REMOVE_TEXT:
+ restoreScreen();
+ break;
+ case IC_MAKE_SOUND:
+ _skySound->playSound(data[0], data[1], 0);
+ data += 2;
+ break;
+ case IC_FX_VOLUME:
+ _skySound->playSound(1, *data++, 0);
+ break;
+ default:
+ error("Unknown FLIRT command %X\n", command);
+ }
+ }
+ if (!escDelay(50)) {
+ _skyScreen->stopSequence();
+ return false;
+ }
+ }
+ data++; // move pointer over "COMMANDEND"
+ return true;
+}
+
+void Intro::showTextBuf(void) {
+
+ uint16 x = ((dataFileHeader*)_textBuf)->s_x;
+ uint16 y = ((dataFileHeader*)_textBuf)->s_y;
+ uint16 width = ((dataFileHeader*)_textBuf)->s_width;
+ uint16 height = ((dataFileHeader*)_textBuf)->s_height;
+ uint8 *screenBuf = _skyScreen->giveCurrent() + y * GAME_SCREEN_WIDTH + x;
+ memcpy(_saveBuf, _textBuf, sizeof(dataFileHeader));
+ uint8 *saveBuf = _saveBuf + sizeof(dataFileHeader);
+ uint8 *textBuf = _textBuf + sizeof(dataFileHeader);
+ for (uint16 cnty = 0; cnty < height; cnty++) {
+ memcpy(saveBuf, screenBuf, width);
+ for (uint16 cntx = 0; cntx < width; cntx++)
+ if (textBuf[cntx])
+ screenBuf[cntx] = textBuf[cntx];
+ screenBuf += GAME_SCREEN_WIDTH;
+ textBuf += width;
+ saveBuf += width;
+ }
+ screenBuf = _skyScreen->giveCurrent() + y * GAME_SCREEN_WIDTH + x;
+ _system->copyRectToScreen(screenBuf, GAME_SCREEN_WIDTH, x, y, width, height);
+}
+
+void Intro::restoreScreen(void) {
+
+ uint16 x = ((dataFileHeader*)_saveBuf)->s_x;
+ uint16 y = ((dataFileHeader*)_saveBuf)->s_y;
+ uint16 width = ((dataFileHeader*)_saveBuf)->s_width;
+ uint16 height = ((dataFileHeader*)_saveBuf)->s_height;
+ uint8 *screenBuf = _skyScreen->giveCurrent() + y * GAME_SCREEN_WIDTH + x;
+ uint8 *saveBuf = _saveBuf + sizeof(dataFileHeader);
+ for (uint16 cnt = 0; cnt < height; cnt++) {
+ memcpy(screenBuf, saveBuf, width);
+ screenBuf += GAME_SCREEN_WIDTH;
+ saveBuf += width;
+ }
+ _system->copyRectToScreen(_saveBuf + sizeof(dataFileHeader), width, x, y, width, height);
+}
+
+bool Intro::escDelay(uint32 msecs) {
+
+ OSystem::Event event;
+ if (_relDelay == 0) // first call, init with system time
+ _relDelay = (int32)_system->getMillis();
+
+ _relDelay += msecs; // now wait until _system->getMillis() >= _relDelay
+
+ int32 nDelay = 0;
+ do {
+ while (_system->pollEvent(event)) {
+ if (event.type == OSystem::EVENT_KEYDOWN) {
+ if (event.kbd.keycode == 27)
+ return false;
+ } else if (event.type == OSystem::EVENT_QUIT) {
+ _quitProg = true;
+ return false;
+ }
+ }
+ nDelay = _relDelay - _system->getMillis();
+ if (nDelay < 0)
+ nDelay = 0;
+ else if (nDelay > 15)
+ nDelay = 15;
+ _system->delayMillis(nDelay);
+ } while (nDelay == 15);
+ return true;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/intro.h b/engines/sky/intro.h
new file mode 100644
index 0000000000..294c907887
--- /dev/null
+++ b/engines/sky/intro.h
@@ -0,0 +1,74 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef INTRO_H
+#define INTRO_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "sound/mixer.h"
+
+namespace Sky {
+
+class Disk;
+class Screen;
+class MusicBase;
+class Sound;
+class Text;
+
+class Intro {
+public:
+ Intro(Disk *disk, Screen *screen, MusicBase *music, Sound *sound, Text *text, Audio::Mixer *mixer, OSystem *system);
+ ~Intro(void);
+ bool doIntro(bool floppyIntro);
+ bool _quitProg;
+private:
+ static uint16 _mainIntroSeq[];
+ static uint16 _floppyIntroSeq[];
+ static uint16 _cdIntroSeq[];
+
+ Disk *_skyDisk;
+ Screen *_skyScreen;
+ MusicBase *_skyMusic;
+ Sound *_skySound;
+ Text *_skyText;
+ OSystem *_system;
+ Audio::Mixer *_mixer;
+
+ uint8 *_textBuf, *_saveBuf;
+ uint8 *_bgBuf;
+ uint32 _bgSize;
+ Audio::SoundHandle _voice, _bgSfx;
+
+ int32 _relDelay;
+
+ bool escDelay(uint32 msecs);
+ bool nextPart(uint16 *&data);
+ bool floppyScrollFlirt(void);
+ bool commandFlirt(uint16 *&data);
+ void showTextBuf(void);
+ void restoreScreen(void);
+};
+
+} // End of namespace Sky
+
+#endif // INTRO_H
diff --git a/engines/sky/logic.cpp b/engines/sky/logic.cpp
new file mode 100644
index 0000000000..9ea560a5bf
--- /dev/null
+++ b/engines/sky/logic.cpp
@@ -0,0 +1,2570 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sky/autoroute.h"
+#include "sky/compact.h"
+#include "sky/control.h"
+#include "sky/debug.h"
+#include "sky/disk.h"
+#include "sky/grid.h"
+#include "sky/logic.h"
+#include "sky/mouse.h"
+#include "sky/music/musicbase.h"
+#include "sky/text.h"
+#include "sky/screen.h"
+#include "sky/sky.h"
+#include "sky/sound.h"
+#include "sky/struc.h"
+
+namespace Sky {
+
+uint32 Logic::_scriptVariables[NUM_SKY_SCRIPTVARS];
+
+void Logic::setupLogicTable() {
+ static const LogicTable logicTable[] = {
+ &Logic::nop,
+ &Logic::logicScript, // 1 script processor
+ &Logic::autoRoute, // 2 Make a route
+ &Logic::arAnim, // 3 Follow a route
+ &Logic::arTurn, // 4 Mega turns araound
+ &Logic::alt, // 5 Set up new get-to script
+ &Logic::anim, // 6 Follow a sequence
+ &Logic::turn, // 7 Mega turning
+ &Logic::cursor, // 8 id tracks the pointer
+ &Logic::talk, // 9 count down and animate
+ &Logic::listen, // 10 player waits for talking id
+ &Logic::stopped, // 11 wait for id to move
+ &Logic::choose, // 12 wait for player to click
+ &Logic::frames, // 13 animate just frames
+ &Logic::pause, // 14 Count down to 0 and go
+ &Logic::waitSync, // 15 Set to l_script when sync!=0
+ &Logic::simpleAnim, // 16 Module anim without x,y's
+ };
+
+ _logicTable = logicTable;
+}
+
+Logic::Logic(SkyCompact *skyCompact, Screen *skyScreen, Disk *skyDisk, Text *skyText, MusicBase *skyMusic, Mouse *skyMouse, Sound *skySound) {
+ _skyCompact = skyCompact;
+ _skyScreen = skyScreen;
+ _skyDisk = skyDisk;
+ _skyText = skyText;
+ _skyMusic = skyMusic;
+ _skySound = skySound;
+ _skyMouse = skyMouse;
+ _skyGrid = new Grid(_skyDisk, _skyCompact);
+ _skyAutoRoute = new AutoRoute(_skyGrid, _skyCompact);
+
+ setupLogicTable();
+ setupMcodeTable();
+
+ memset(_objectList, 0, 30 * sizeof(uint32));
+
+ for (int i = 0; i < ARRAYSIZE(_moduleList); i++)
+ _moduleList[i] = 0;
+ _stackPtr = 0;
+
+ _currentSection = 0xFF; //force music & sound reload
+ initScriptVariables();
+}
+
+Logic::~Logic(void) {
+ delete _skyGrid;
+ delete _skyAutoRoute;
+
+ for (int i = 0; i < ARRAYSIZE(_moduleList); i++)
+ if (_moduleList[i])
+ free(_moduleList[i]);
+}
+
+void Logic::initScreen0(void) {
+ fnEnterSection(0, 0, 0);
+ _skyMusic->startMusic(2);
+ SkyEngine::_systemVars.currentMusic = 2;
+}
+
+void Logic::parseSaveData(uint32 *data) {
+ if (!SkyEngine::isDemo())
+ fnLeaveSection(_scriptVariables[CUR_SECTION], 0, 0);
+ for (uint16 cnt = 0; cnt < NUM_SKY_SCRIPTVARS; cnt++)
+ _scriptVariables[cnt] = READ_LE_UINT32(data++);
+ fnEnterSection(_scriptVariables[CUR_SECTION], 0, 0);
+}
+
+bool Logic::checkProtection(void) {
+ if (_scriptVariables[ENTER_DIGITS]) {
+ if (_scriptVariables[CONSOLE_TYPE] == 5) // reactor code
+ _scriptVariables[FS_COMMAND] = 240;
+ else // copy protection
+ _scriptVariables[FS_COMMAND] = 337;
+ _scriptVariables[ENTER_DIGITS] = 0;
+ return true;
+ } else
+ return false;
+}
+
+void Logic::engine() {
+ do {
+ uint16 *logicList = (uint16 *)_skyCompact->fetchCpt(_scriptVariables[LOGIC_LIST_NO]);
+
+ while (uint16 id = *logicList++) { // 0 means end of list
+ if (id == 0xffff) {
+ // Change logic data address
+ logicList = (uint16 *)_skyCompact->fetchCpt(*logicList);
+ continue;
+ }
+
+ _scriptVariables[CUR_ID] = id;
+ _compact = _skyCompact->fetchCpt(id);
+
+ // check the id actually wishes to be processed
+ if (!(_compact->status & (1 << 6)))
+ continue;
+
+ // ok, here we process the logic bit system
+
+ if (_compact->status & (1 << 7))
+ _skyGrid->removeObjectFromWalk(_compact);
+
+ Debug::logic(_compact->logic);
+ (this->*_logicTable[_compact->logic]) ();
+
+ if (_compact->status & (1 << 7))
+ _skyGrid->objectToWalk(_compact);
+
+ // a sync sent to the compact is available for one cycle
+ // only. that cycle has just ended so remove the sync.
+ // presumably the mega has just reacted to it.
+ _compact->sync = 0;
+ }
+ // usually this loop is run only once, it'll only be run a second time if the game
+ // script just asked the user to enter a copy protection code.
+ // this is done to prevent the copy protection screen from flashing up.
+ // (otherwise it would be visible for 1/50 second)
+ } while (checkProtection());
+}
+
+void Logic::nop() {}
+
+/**
+ * This function is basicly a wrapper around the real script engine. It runs
+ * the script engine until a script has finished.
+ * @see script()
+ */
+void Logic::logicScript() {
+ /// Process the current mega's script
+ /// If the script finishes then drop back a level
+
+ for (;;) {
+ uint16 mode = _compact->mode; // get pointer to current script
+ uint16 *scriptNo = SkyCompact::getSub(_compact, mode);
+ uint16 *offset = SkyCompact::getSub(_compact, mode + 2);
+
+ *offset = script(*scriptNo, *offset);
+
+ if (!*offset) // script finished
+ _compact->mode -= 4;
+ else if (_compact->mode == mode)
+ return;
+ }
+}
+
+void Logic::autoRoute() {
+
+ _compact->downFlag = _skyAutoRoute->autoRoute(_compact);
+ if ((_compact->downFlag == 2) && _skyCompact->cptIsId(_compact, CPT_JOEY) &&
+ (_compact->mode == 0) && (_compact->baseSub == JOEY_OUT_OF_LIFT)) {
+ // workaround for script bug #1064113. Details unclear...
+ _compact->downFlag = 0;
+ }
+ if (_compact->downFlag != 1) { // route ok
+ _compact->grafixProgId = _compact->animScratchId;
+ _compact->grafixProgPos = 0;
+ }
+
+ _compact->logic = L_SCRIPT; // continue the script
+
+ logicScript();
+ return;
+}
+
+void Logic::arAnim() {
+ /// Follow a route
+ /// Mega should be in getToMode
+
+ // only check collisions on character boundaries
+ if ((_compact->xcood & 7) || (_compact->ycood & 7)) {
+ mainAnim();
+ return;
+ }
+
+ // On character boundary. Have we been told to wait?
+ // if not - are WE colliding?
+
+ if (_compact->waitingFor == 0xffff) { // 1st cycle of re-route does
+ mainAnim();
+ return;
+ }
+
+ if (_compact->waitingFor) {
+ // ok, we've been told we've hit someone
+ // we will wait until we are no longer colliding
+ // with them. here we check to see if we are (still) colliding.
+ // if we are then run the stop script. if not clear the flag
+ // and continue.
+
+ // remember - this could be the first ar cycle for some time,
+ // we might have been told to wait months ago. if we are
+ // waiting for one person then another hits us then
+ // c_waiting_for will be replaced by the new mega - this is
+ // fine because the later collision will almost certainly
+ // take longer to clear than the earlier one.
+
+ if (collide(_skyCompact->fetchCpt(_compact->waitingFor))) {
+ stopAndWait();
+ return;
+ }
+
+ // we are not in fact hitting this person so clr & continue
+ // it must have registered some time ago
+
+ _compact->waitingFor = 0; // clear id flag
+ }
+
+ // ok, our turn to check for collisions
+
+ uint16 *logicList = (uint16 *)_skyCompact->fetchCpt(_scriptVariables[LOGIC_LIST_NO]);
+ Compact *cpt = 0;
+
+ while (uint16 id = *logicList++) { // get an id
+
+ if (id == 0xffff) { // address change?
+ logicList = (uint16 *)_skyCompact->fetchCpt(*logicList); // get new logic list
+ continue;
+ }
+
+ if (id == (uint16)(_scriptVariables[CUR_ID] & 0xffff)) // is it us?
+ continue;
+
+ _scriptVariables[HIT_ID] = id; // save target id for any possible c_mini_bump
+ cpt = _skyCompact->fetchCpt(id); // let's have a closer look
+
+ if (!(cpt->status & (1 << ST_COLLISION_BIT))) // can it collide?
+ continue;
+
+ if (cpt->screen != _compact->screen) // is it on our screen?
+ continue;
+
+ if (collide(cpt)) { // check for a hit
+ // ok, we've hit a mega
+ // is it moving... or something else?
+
+ if (cpt->logic != L_AR_ANIM) { // check for following route
+ // it is doing something else
+ // we restart our get-to script
+ // first tell it to wait for us - in case it starts moving
+ // ( *it may have already hit us and stopped to wait )
+
+ _compact->waitingFor = 0xffff; // effect 1 cycle collision skip
+ // tell it it is waiting for us
+ cpt->waitingFor = (uint16)(_scriptVariables[CUR_ID] & 0xffff);
+ // restart current script
+ *SkyCompact::getSub(_compact, _compact->mode + 2) = 0;
+ _compact->logic = L_SCRIPT;
+ logicScript();
+ return;
+ }
+
+ script(_compact->miniBump, 0);
+ return;
+ }
+ }
+
+ // ok, there was no collisions
+ // now check for interaction request
+ // *note: the interaction is always set up as an action script
+
+ if (_compact->request) {
+ _compact->mode = C_ACTION_MODE; // put into action mode
+ _compact->actionSub = _compact->request;
+ _compact->actionSub_off = 0;
+ _compact->request = 0; // trash request
+ _compact->logic = L_SCRIPT;
+ logicScript();
+ return;
+ }
+
+ // any flag? - or any change?
+ // if change then re-run the current script, which must be
+ // a position independent get-to ----
+
+ if (!_compact->atWatch) { // any flag set?
+ mainAnim();
+ return;
+ }
+
+ // ok, there is an at watch - see if it's changed
+
+ if (_compact->atWas == _scriptVariables[_compact->atWatch/4]) { // still the same?
+ mainAnim();
+ return;
+ }
+
+ // changed so restart the current script
+ // *not suitable for base initiated ARing
+ *SkyCompact::getSub(_compact, _compact->mode + 2) = 0;
+
+ _compact->logic = L_SCRIPT;
+ logicScript();
+}
+
+void Logic::mainAnim() {
+ /// Extension of arAnim()
+ _compact->waitingFor = 0; // clear possible zero-zero skip
+
+ uint16 *sequence = _skyCompact->getGrafixPtr(_compact);
+ if (!*sequence) {
+ // ok, move to new anim segment
+ sequence += 2;
+ _compact->grafixProgPos += 2;
+ if (!*sequence) { // end of route?
+ // ok, sequence has finished
+
+ // will start afresh if new sequence continues in last direction
+ _compact->arAnimIndex = 0;
+
+ _compact->downFlag = 0; // pass back ok to script
+ _compact->logic = L_SCRIPT;
+ logicScript();
+ return;
+ }
+
+ _compact->arAnimIndex = 0; // reset position
+ }
+
+ uint16 dir;
+ while ((dir = _compact->dir) != *(sequence + 1)) {
+ // ok, setup turning
+ _compact->dir = *(sequence + 1);
+
+ uint16 *tt = _skyCompact->getTurnTable(_compact, dir);
+ if (tt[_compact->dir]) {
+ _compact->turnProgId = tt[_compact->dir];
+ _compact->turnProgPos = 0;
+ _compact->logic = L_AR_TURNING;
+ arTurn();
+ return;
+ }
+ };
+
+ uint16 animId = *(uint16*)_skyCompact->getCompactElem(_compact, C_ANIM_UP + _compact->megaSet + dir * 4);
+ uint16 *animList = (uint16*)_skyCompact->fetchCpt(animId);
+
+ uint16 arAnimIndex = _compact->arAnimIndex;
+ if (!animList[arAnimIndex / 2]) {
+ arAnimIndex = 0;
+ _compact->arAnimIndex = 0; // reset
+ }
+
+ _compact->arAnimIndex += S_LENGTH;
+
+ *sequence -= animList[(S_COUNT + arAnimIndex)/2]; // reduce the distance to travel
+ _compact->frame = animList[(S_FRAME + arAnimIndex)/2]; // new graphic frame
+ _compact->xcood += animList[(S_AR_X + arAnimIndex)/2]; // update x coordinate
+ _compact->ycood += animList[(S_AR_Y + arAnimIndex)/2]; // update y coordinate
+}
+
+void Logic::arTurn() {
+ uint16 *turnData = (uint16*)_skyCompact->fetchCpt(_compact->turnProgId) + _compact->turnProgPos;
+ _compact->frame = *turnData++;
+ _compact->turnProgPos++;
+
+ if (!*turnData) { // turn done?
+ // Back to ar mode
+ _compact->arAnimIndex = 0;
+ _compact->logic = L_AR_ANIM;
+ }
+}
+
+void Logic::alt() {
+ /// change the current script
+ _compact->logic = L_SCRIPT;
+ *SkyCompact::getSub(_compact, _compact->mode) = _compact->alt;
+ *SkyCompact::getSub(_compact, _compact->mode + 2) = 0;
+ logicScript();
+}
+
+void Logic::anim() {
+ /// Follow an animation sequence
+ uint16 *grafixProg = _skyCompact->getGrafixPtr(_compact);
+
+ while (*grafixProg) {
+ _compact->grafixProgPos += 3; // all types are 3 words.
+ if (*grafixProg == LF_START_FX) { // do fx
+ grafixProg++;
+ uint16 sound = *grafixProg++;
+ uint16 volume = *grafixProg++;
+
+ // channel 0
+ fnStartFx(sound, 0, volume);
+ } else if (*grafixProg >= LF_START_FX) { // do sync
+ grafixProg++;
+
+ Compact *cpt = _skyCompact->fetchCpt(*grafixProg++);
+
+ cpt->sync = *grafixProg++;
+ } else { // put coordinates and frame in
+ _compact->xcood = *grafixProg++;
+ _compact->ycood = *grafixProg++;
+
+ _compact->frame = *grafixProg++ | _compact->offset;
+ return;
+ }
+ }
+
+ _compact->downFlag = 0;
+ _compact->logic = L_SCRIPT;
+ logicScript();
+}
+
+void Logic::turn() {
+ uint16 *turnData = (uint16*)_skyCompact->fetchCpt(_compact->turnProgId) + _compact->turnProgPos;
+ if (*turnData) {
+ _compact->frame = *turnData;
+ _compact->turnProgPos++;
+ return;
+ }
+
+ // turn_to_script:
+ _compact->arAnimIndex = 0;
+ _compact->logic = L_SCRIPT;
+
+ logicScript();
+}
+
+void Logic::cursor() {
+ _skyText->logicCursor(_compact, _skyMouse->giveMouseX(), _skyMouse->giveMouseY());
+}
+
+static uint16 clickTable[46] = {
+ ID_FOSTER,
+ ID_JOEY,
+ ID_JOBS,
+ ID_LAMB,
+ ID_ANITA,
+ ID_SON,
+ ID_DAD,
+ ID_MONITOR,
+ ID_SHADES,
+ MINI_SS,
+ FULL_SS,
+ ID_FOREMAN,
+ ID_RADMAN,
+ ID_GALLAGER_BEL,
+ ID_BURKE,
+ ID_BODY,
+ ID_HOLO,
+ ID_TREVOR,
+ ID_ANCHOR,
+ ID_WRECK_GUARD,
+ ID_SKORL_GUARD,
+
+ // BASE LEVEL
+ ID_SC30_HENRI,
+ ID_SC31_GUARD,
+ ID_SC32_VINCENT,
+ ID_SC32_GARDENER,
+ ID_SC32_BUZZER,
+ ID_SC36_BABS,
+ ID_SC36_BARMAN,
+ ID_SC36_COLSTON,
+ ID_SC36_GALLAGHER,
+ ID_SC36_JUKEBOX,
+ ID_DANIELLE,
+ ID_SC42_JUDGE,
+ ID_SC42_CLERK,
+ ID_SC42_PROSECUTION,
+ ID_SC42_JOBSWORTH,
+
+ // UNDERWORLD
+ ID_MEDI,
+ ID_WITNESS,
+ ID_GALLAGHER,
+ ID_KEN,
+ ID_SC76_ANDROID_2,
+ ID_SC76_ANDROID_3,
+ ID_SC81_FATHER,
+ ID_SC82_JOBSWORTH,
+
+ // LINC WORLD
+ ID_HOLOGRAM_B,
+ 12289
+};
+
+void Logic::talk() {
+ // first count through the frames
+ // just frames - nothing tweeky
+ // the speech finishes when the timer runs out &
+ // not when the animation finishes
+ // this routine is very task specific
+
+ // TODO: Check for mouse clicking
+
+ // Are we allowed to click
+
+ if (_skyMouse->wasClicked())
+ for (int i = 0; i < ARRAYSIZE(clickTable); i++)
+ if (clickTable[i] == (uint16)_scriptVariables[CUR_ID]) {
+ if ((SkyEngine::_systemVars.systemFlags & SF_ALLOW_SPEECH) && (!_skySound->speechFinished()))
+ _skySound->stopSpeech();
+ if ((_compact->spTextId > 0) &&
+ (_compact->spTextId < 0xFFFF)) {
+
+ _skyCompact->fetchCpt(_compact->spTextId)->status = 0;
+ }
+ if (_skyCompact->getGrafixPtr(_compact)) {
+ _compact->frame = _compact->getToFlag; // set character to stand
+ _compact->grafixProgId = 0;
+ }
+
+ _compact->logic = L_SCRIPT;
+ logicScript();
+ return;
+ }
+
+ // If speech is allowed then check for it to finish before finishing animations
+
+ if ((_compact->spTextId == 0xFFFF) && // is this a voc file?
+ (_skySound->speechFinished())) { // finished?
+
+ _compact->logic = L_SCRIPT; // restart character control
+
+ if (_skyCompact->getGrafixPtr(_compact)) {
+ _compact->frame = _compact->getToFlag; // set character to stand
+ _compact->grafixProgId = 0;
+ }
+
+ logicScript();
+ return;
+ }
+
+ uint16 *graphixProg = _skyCompact->getGrafixPtr(_compact);
+ if (graphixProg) {
+ if ((*graphixProg) && ((_compact->spTime != 3) || (!_skySound->speechFinished()))) {
+ // we will force the animation to finish 3 game cycles
+ // before the speech actually finishes - because it looks good.
+
+ _compact->frame = *(graphixProg + 2) + _compact->offset;
+ graphixProg += 3;
+ _compact->grafixProgPos += 3;
+ } else {
+ // we ran out of frames or finished speech, let actor stand still.
+ _compact->frame = _compact->getToFlag;
+ _compact->grafixProgId = 0;
+ }
+ }
+
+ if (_skySound->speechFinished()) _compact->spTime--;
+
+ if (_compact->spTime == 0) {
+
+ // ok, speech has finished
+
+ if (_compact->spTextId) {
+ Compact *cpt = _skyCompact->fetchCpt(_compact->spTextId); // get text id to kill
+ cpt->status = 0; // kill the text
+ }
+
+ _compact->logic = L_SCRIPT;
+ logicScript();
+ }
+}
+
+void Logic::listen() {
+ /// Stay in this mode until id in getToFlag leaves L_TALK mode
+
+ Compact *cpt = _skyCompact->fetchCpt(_compact->flag);
+
+ if (cpt->logic == L_TALK)
+ return;
+
+ _compact->logic = L_SCRIPT;
+ logicScript();
+}
+
+void Logic::stopped() {
+ /// waiting for another mega to move or give-up trying
+ ///
+ /// this mode will always be set up from a special script
+ /// that will be one level higher than the script we
+ /// would wish to restart from
+
+ Compact *cpt = _skyCompact->fetchCpt(_compact->waitingFor);
+
+ if (cpt)
+ if (!cpt->mood && collide(cpt))
+ return;
+
+ // we are free, continue processing the script
+
+ // restart script one level below
+ *SkyCompact::getSub(_compact, _compact->mode - 2) = 0;
+ _compact->waitingFor = 0xffff;
+
+ _compact->logic = L_SCRIPT;
+ logicScript();
+}
+
+void Logic::choose() {
+ // Remain in this mode until player selects some text
+ if (!_scriptVariables[THE_CHOSEN_ONE])
+ return;
+
+ fnNoHuman(0, 0, 0); // kill mouse again
+
+ SkyEngine::_systemVars.systemFlags &= ~SF_CHOOSING; // restore save/restore
+
+ _compact->logic = L_SCRIPT; // and continue script
+ logicScript();
+}
+
+void Logic::frames() {
+ if (!_compact->sync)
+ simpleAnim();
+ else {
+ _compact->downFlag = 0; // return 'ok' to script
+ _compact->logic = L_SCRIPT;
+ logicScript();
+ }
+}
+
+void Logic::pause() {
+ if (--_compact->flag)
+ return;
+
+ _compact->logic = L_SCRIPT;
+ logicScript();
+ return;
+}
+
+void Logic::waitSync() {
+ /// checks c_sync, when its non 0
+ /// the id is put back into script mode
+ // use this instead of loops in the script
+
+ if (!_compact->sync)
+ return;
+
+ _compact->logic = L_SCRIPT;
+ logicScript();
+}
+
+void Logic::simpleAnim() {
+ /// follow an animation sequence module whilst ignoring the coordinate data
+
+ uint16 *grafixProg = _skyCompact->getGrafixPtr(_compact);
+
+ // *grafix_prog: command
+ while (*grafixProg) {
+ _compact->grafixProgPos += 3;
+ if (*grafixProg != SEND_SYNC) {
+ grafixProg++;
+ grafixProg++; // skip coordinates
+
+ // *grafix_prog: frame
+ if (*grafixProg >= 64)
+ _compact->frame = *grafixProg;
+ else
+ _compact->frame = *grafixProg + _compact->offset;
+
+ return;
+ }
+
+ grafixProg++;
+ // *grafix_prog: id to sync
+ Compact *compact2 = _skyCompact->fetchCpt(*grafixProg);
+ grafixProg++;
+
+ // *grafix_prog: sync
+ compact2->sync = *grafixProg;
+ grafixProg++;
+ }
+
+ _compact->downFlag = 0; // return 'ok' to script
+ _compact->logic = L_SCRIPT;
+ logicScript();
+}
+
+bool Logic::collide(Compact *cpt) {
+ MegaSet *m1 = SkyCompact::getMegaSet(_compact);
+ MegaSet *m2 = SkyCompact::getMegaSet(cpt);
+
+ // target's base coordinates
+ uint16 x = cpt->xcood & 0xfff8;
+ uint16 y = cpt->ycood & 0xfff8;
+
+ // The collision is direction dependent
+ switch (_compact->dir) {
+ case 0: // looking up
+ x -= m1->colOffset; // compensate for inner x offsets
+ x += m2->colOffset;
+
+ if ((x + m2->colWidth) < _compact->xcood) // their rightmost
+ return false;
+
+ x -= m1->colWidth; // our left, their right
+ if (x >= _compact->xcood)
+ return false;
+
+ y += 8; // bring them down a line
+ if (y == _compact->ycood)
+ return true;
+
+ y += 8; // bring them down a line
+ if (y == _compact->ycood)
+ return true;
+
+ return false;
+ case 1: // looking down
+ x -= m1->colOffset; // compensate for inner x offsets
+ x += m2->colOffset;
+
+ if ((x + m2->colWidth) < _compact->xcood) // their rightmoast
+ return false;
+
+ x -= m1->colWidth; // our left, their right
+ if (x >= _compact->xcood)
+ return false;
+
+ y -= 8; // bring them up a line
+ if (y == _compact->ycood)
+ return true;
+
+ y -= 8; // bring them up a line
+ if (y == _compact->ycood)
+ return true;
+
+ return false;
+ case 2: // looking left
+
+ if (y != _compact->ycood)
+ return false;
+
+ x += m2->lastChr;
+ if (x == _compact->xcood)
+ return true;
+
+ x -= 8; // out another one
+ if (x == _compact->xcood)
+ return true;
+
+ return false;
+ case 3: // looking right
+ case 4: // talking (not sure if this makes sense...)
+
+ if (y != _compact->ycood)
+ return false;
+
+ x -= m1->lastChr; // last block
+ if (x == _compact->xcood)
+ return true;
+
+ x -= 8; // out another block
+ if (x != _compact->xcood)
+ return false;
+
+ return true;
+ default:
+ error("Unknown Direction: %d", _compact->dir);
+ }
+}
+
+void Logic::runGetOff() {
+ uint32 getOff = _scriptVariables[GET_OFF];
+ _scriptVariables[GET_OFF] = 0;
+ if (getOff)
+ script((uint16)(getOff & 0xffff), (uint16)(getOff >> 16));
+}
+
+void Logic::stopAndWait() {
+ _compact->mode += 4;
+
+ uint16 *scriptNo = SkyCompact::getSub(_compact, _compact->mode);
+ uint16 *offset = SkyCompact::getSub(_compact, _compact->mode + 2);
+
+ *scriptNo = _compact->stopScript;
+ *offset = 0;
+
+ _compact->logic = L_SCRIPT;
+ logicScript();
+}
+
+void Logic::checkModuleLoaded(uint16 moduleNo) {
+ if (!_moduleList[moduleNo])
+ _moduleList[moduleNo] = (uint16 *)_skyDisk->loadFile((uint16)moduleNo + F_MODULE_0);
+}
+
+void Logic::push(uint32 a) {
+ if (_stackPtr > ARRAYSIZE(_stack) - 2)
+ error("Stack overflow");
+ _stack[_stackPtr++] = a;
+}
+
+uint32 Logic::pop() {
+ if (_stackPtr < 1 || _stackPtr > ARRAYSIZE(_stack) - 1)
+ error("No items on Stack to pop");
+ return _stack[--_stackPtr];
+}
+
+void Logic::setupMcodeTable() {
+ static const McodeTable mcodeTable[] = {
+ &Logic::fnCacheChip,
+ &Logic::fnCacheFast,
+ &Logic::fnDrawScreen,
+ &Logic::fnAr,
+ &Logic::fnArAnimate,
+ &Logic::fnIdle,
+ &Logic::fnInteract,
+ &Logic::fnStartSub,
+ &Logic::fnTheyStartSub,
+ &Logic::fnAssignBase,
+ &Logic::fnDiskMouse,
+ &Logic::fnNormalMouse,
+ &Logic::fnBlankMouse,
+ &Logic::fnCrossMouse,
+ &Logic::fnCursorRight,
+ &Logic::fnCursorLeft,
+ &Logic::fnCursorDown,
+ &Logic::fnOpenHand,
+ &Logic::fnCloseHand,
+ &Logic::fnGetTo,
+ &Logic::fnSetToStand,
+ &Logic::fnTurnTo,
+ &Logic::fnArrived,
+ &Logic::fnLeaving,
+ &Logic::fnSetAlternate,
+ &Logic::fnAltSetAlternate,
+ &Logic::fnKillId,
+ &Logic::fnNoHuman,
+ &Logic::fnAddHuman,
+ &Logic::fnAddButtons,
+ &Logic::fnNoButtons,
+ &Logic::fnSetStop,
+ &Logic::fnClearStop,
+ &Logic::fnPointerText,
+ &Logic::fnQuit,
+ &Logic::fnSpeakMe,
+ &Logic::fnSpeakMeDir,
+ &Logic::fnSpeakWait,
+ &Logic::fnSpeakWaitDir,
+ &Logic::fnChooser,
+ &Logic::fnHighlight,
+ &Logic::fnTextKill,
+ &Logic::fnStopMode,
+ &Logic::fnWeWait,
+ &Logic::fnSendSync,
+ &Logic::fnSendFastSync,
+ &Logic::fnSendRequest,
+ &Logic::fnClearRequest,
+ &Logic::fnCheckRequest,
+ &Logic::fnStartMenu,
+ &Logic::fnUnhighlight,
+ &Logic::fnFaceId,
+ &Logic::fnForeground,
+ &Logic::fnBackground,
+ &Logic::fnNewBackground,
+ &Logic::fnSort,
+ &Logic::fnNoSpriteEngine,
+ &Logic::fnNoSpritesA6,
+ &Logic::fnResetId,
+ &Logic::fnToggleGrid,
+ &Logic::fnPause,
+ &Logic::fnRunAnimMod,
+ &Logic::fnSimpleMod,
+ &Logic::fnRunFrames,
+ &Logic::fnAwaitSync,
+ &Logic::fnIncMegaSet,
+ &Logic::fnDecMegaSet,
+ &Logic::fnSetMegaSet,
+ &Logic::fnMoveItems,
+ &Logic::fnNewList,
+ &Logic::fnAskThis,
+ &Logic::fnRandom,
+ &Logic::fnPersonHere,
+ &Logic::fnToggleMouse,
+ &Logic::fnMouseOn,
+ &Logic::fnMouseOff,
+ &Logic::fnFetchX,
+ &Logic::fnFetchY,
+ &Logic::fnTestList,
+ &Logic::fnFetchPlace,
+ &Logic::fnCustomJoey,
+ &Logic::fnSetPalette,
+ &Logic::fnTextModule,
+ &Logic::fnChangeName,
+ &Logic::fnMiniLoad,
+ &Logic::fnFlushBuffers,
+ &Logic::fnFlushChip,
+ &Logic::fnSaveCoods,
+ &Logic::fnPlotGrid,
+ &Logic::fnRemoveGrid,
+ &Logic::fnEyeball,
+ &Logic::fnCursorUp,
+ &Logic::fnLeaveSection,
+ &Logic::fnEnterSection,
+ &Logic::fnRestoreGame,
+ &Logic::fnRestartGame,
+ &Logic::fnNewSwingSeq,
+ &Logic::fnWaitSwingEnd,
+ &Logic::fnSkipIntroCode,
+ &Logic::fnBlankScreen,
+ &Logic::fnPrintCredit,
+ &Logic::fnLookAt,
+ &Logic::fnLincTextModule,
+ &Logic::fnTextKill2,
+ &Logic::fnSetFont,
+ &Logic::fnStartFx,
+ &Logic::fnStopFx,
+ &Logic::fnStartMusic,
+ &Logic::fnStopMusic,
+ &Logic::fnFadeDown,
+ &Logic::fnFadeUp,
+ &Logic::fnQuitToDos,
+ &Logic::fnPauseFx,
+ &Logic::fnUnPauseFx,
+ &Logic::fnPrintf
+ };
+
+ _mcodeTable = mcodeTable;
+}
+
+static const uint32 forwardList1b[] = {
+ JOBS_SPEECH,
+ JOBS_S4,
+ JOBS_ALARMED,
+ JOEY_RECYCLE,
+ SHOUT_SSS,
+ JOEY_MISSION,
+ TRANS_MISSION,
+ SLOT_MISSION,
+ CORNER_MISSION,
+ JOEY_LOGIC,
+ GORDON_SPEECH,
+ JOEY_BUTTON_MISSION,
+ LOB_DAD_SPEECH,
+ LOB_SON_SPEECH,
+ GUARD_SPEECH,
+ MANTRACH_SPEECH,
+ WRECK_SPEECH,
+ ANITA_SPEECH,
+ LAMB_FACTORY,
+ FORE_SPEECH,
+ JOEY_42_MISS,
+ JOEY_JUNCTION_MISS,
+ WELDER_MISSION,
+ JOEY_WELD_MISSION,
+ RADMAN_SPEECH,
+ LINK_7_29,
+ LINK_29_7,
+ LAMB_TO_3,
+ LAMB_TO_2,
+ BURKE_SPEECH,
+ BURKE_1,
+ BURKE_2,
+ DR_BURKE_1,
+ JASON_SPEECH,
+ JOEY_BELLEVUE,
+ ANCHOR_SPEECH,
+ ANCHOR_MISSION,
+ JOEY_PC_MISSION,
+ HOOK_MISSION,
+ TREVOR_SPEECH,
+ JOEY_FACTORY,
+ HELGA_SPEECH,
+ JOEY_HELGA_MISSION,
+ GALL_BELLEVUE,
+ GLASS_MISSION,
+ LAMB_FACT_RETURN,
+ LAMB_LEAVE_GARDEN,
+ LAMB_START_29,
+ LAMB_BELLEVUE,
+ CABLE_MISSION,
+ FOSTER_TOUR,
+ LAMB_TOUR,
+ FOREMAN_LOGIC,
+ LAMB_LEAVE_FACTORY,
+ LAMB_BELL_LOGIC,
+ LAMB_FACT_2,
+ START90,
+ 0,
+ 0,
+ LINK_28_31,
+ LINK_31_28,
+ EXIT_LINC,
+ DEATH_SCRIPT
+};
+
+static uint32 forwardList1b288[] = {
+ JOBS_SPEECH,
+ JOBS_S4,
+ JOBS_ALARMED,
+ JOEY_RECYCLE,
+ SHOUT_SSS,
+ JOEY_MISSION,
+ TRANS_MISSION,
+ SLOT_MISSION,
+ CORNER_MISSION,
+ JOEY_LOGIC,
+ GORDON_SPEECH,
+ JOEY_BUTTON_MISSION,
+ LOB_DAD_SPEECH,
+ LOB_SON_SPEECH,
+ GUARD_SPEECH,
+ 0x68,
+ WRECK_SPEECH,
+ ANITA_SPEECH,
+ LAMB_FACTORY,
+ FORE_SPEECH,
+ JOEY_42_MISS,
+ JOEY_JUNCTION_MISS,
+ WELDER_MISSION,
+ JOEY_WELD_MISSION,
+ RADMAN_SPEECH,
+ LINK_7_29,
+ LINK_29_7,
+ LAMB_TO_3,
+ LAMB_TO_2,
+ 0x3147,
+ 0x3100,
+ 0x3101,
+ 0x3102,
+ 0x3148,
+ 0x3149,
+ 0x314A,
+ 0x30C5,
+ 0x30C6,
+ 0x30CB,
+ 0x314B,
+ JOEY_FACTORY,
+ 0x314C,
+ 0x30E2,
+ 0x314D,
+ 0x310C,
+ LAMB_FACT_RETURN,
+ 0x3139,
+ 0x313A,
+ 0x004F,
+ CABLE_MISSION,
+ FOSTER_TOUR,
+ LAMB_TOUR,
+ FOREMAN_LOGIC,
+ LAMB_LEAVE_FACTORY,
+ 0x3138,
+ LAMB_FACT_2,
+ 0x004D,
+ 0,
+ 0,
+ LINK_28_31,
+ LINK_31_28,
+ 0x004E,
+ DEATH_SCRIPT
+};
+
+static const uint32 forwardList2b[] = {
+ STD_ON,
+ STD_EXIT_LEFT_ON,
+ STD_EXIT_RIGHT_ON,
+ ADVISOR_188,
+ SHOUT_ACTION,
+ MEGA_CLICK,
+ MEGA_ACTION
+};
+
+static const uint32 forwardList3b[] = {
+ DANI_SPEECH,
+ DANIELLE_GO_HOME,
+ SPUNKY_GO_HOME,
+ HENRI_SPEECH,
+ BUZZER_SPEECH,
+ FOSTER_VISIT_DANI,
+ DANIELLE_LOGIC,
+ JUKEBOX_SPEECH,
+ VINCENT_SPEECH,
+ EDDIE_SPEECH,
+ BLUNT_SPEECH,
+ DANI_ANSWER_PHONE,
+ SPUNKY_SEE_VIDEO,
+ SPUNKY_BARK_AT_FOSTER,
+ SPUNKY_SMELLS_FOOD,
+ BARRY_SPEECH,
+ COLSTON_SPEECH,
+ GALL_SPEECH,
+ BABS_SPEECH,
+ CHUTNEY_SPEECH,
+ FOSTER_ENTER_COURT
+};
+
+static const uint32 forwardList4b[] = {
+ WALTER_SPEECH,
+ JOEY_MEDIC,
+ JOEY_MED_LOGIC,
+ JOEY_MED_MISSION72,
+ KEN_LOGIC,
+ KEN_SPEECH,
+ KEN_MISSION_HAND,
+ SC70_IRIS_OPENED,
+ SC70_IRIS_CLOSED,
+ FOSTER_ENTER_BOARDROOM,
+ BORED_ROOM,
+ FOSTER_ENTER_NEW_BOARDROOM,
+ HOBS_END,
+ SC82_JOBS_SSS
+};
+
+static const uint32 forwardList5b[] = {
+ SET_UP_INFO_WINDOW,
+ SLAB_ON,
+ UP_MOUSE,
+ DOWN_MOUSE,
+ LEFT_MOUSE,
+ RIGHT_MOUSE,
+ DISCONNECT_FOSTER
+};
+
+void Logic::fnExec(uint16 num, uint32 a, uint32 b, uint32 c) {
+ (this->*_mcodeTable[num])(a, b, c);
+}
+
+void Logic::initScriptVariables() {
+ for (int i = 0; i < ARRAYSIZE(_scriptVariables); i++)
+ _scriptVariables[i] = 0;
+
+ _scriptVariables[LOGIC_LIST_NO] = 141;
+ _scriptVariables[LAMB_GREET] = 62;
+ _scriptVariables[JOEY_SECTION] = 1;
+ _scriptVariables[LAMB_SECTION] = 2;
+ _scriptVariables[S15_FLOOR] = 8371;
+ _scriptVariables[GUARDIAN_THERE] = 1;
+ _scriptVariables[DOOR_67_68_FLAG] = 1;
+ _scriptVariables[SC70_IRIS_FLAG] = 3;
+ _scriptVariables[DOOR_73_75_FLAG] = 1;
+ _scriptVariables[SC76_CABINET1_FLAG] = 1;
+ _scriptVariables[SC76_CABINET2_FLAG] = 1;
+ _scriptVariables[SC76_CABINET3_FLAG] = 1;
+ _scriptVariables[DOOR_77_78_FLAG] = 1;
+ _scriptVariables[SC80_EXIT_FLAG] = 1;
+ _scriptVariables[SC31_LIFT_FLAG] = 1;
+ _scriptVariables[SC32_LIFT_FLAG] = 1;
+ _scriptVariables[SC33_SHED_DOOR_FLAG] = 1;
+ _scriptVariables[BAND_PLAYING] = 1;
+ _scriptVariables[COLSTON_AT_TABLE] = 1;
+ _scriptVariables[SC36_NEXT_DEALER] = 16731;
+ _scriptVariables[SC36_DOOR_FLAG] = 1;
+ _scriptVariables[SC37_DOOR_FLAG] = 2;
+ _scriptVariables[SC40_LOCKER_1_FLAG] = 1;
+ _scriptVariables[SC40_LOCKER_2_FLAG] = 1;
+ _scriptVariables[SC40_LOCKER_3_FLAG] = 1;
+ _scriptVariables[SC40_LOCKER_4_FLAG] = 1;
+ _scriptVariables[SC40_LOCKER_5_FLAG] = 1;
+
+ if (SkyEngine::_systemVars.gameVersion == 288)
+ memcpy(_scriptVariables + 352, forwardList1b288, sizeof(forwardList1b288));
+ else
+ memcpy(_scriptVariables + 352, forwardList1b, sizeof(forwardList1b));
+
+ memcpy(_scriptVariables + 656, forwardList2b, sizeof(forwardList2b));
+ memcpy(_scriptVariables + 721, forwardList3b, sizeof(forwardList3b));
+ memcpy(_scriptVariables + 663, forwardList4b, sizeof(forwardList4b));
+ memcpy(_scriptVariables + 505, forwardList5b, sizeof(forwardList5b));
+}
+
+uint16 Logic::mouseScript(uint32 scrNum, Compact *scriptComp) {
+
+ Compact *tmpComp = _compact;
+ _compact = scriptComp;
+ uint16 retVal = script((uint16)(scrNum & 0xFFFF), (uint16)(scrNum >> 16));
+ _compact = tmpComp;
+ return retVal;
+}
+
+/**
+ * This is the actual script engine. It interprets script \a scriptNo starting at \a offset
+ *
+ * @param scriptNo The script to interpret.
+ * @arg Bits 0-11 - Script number
+ * @arg Bits 12-15 - Module number
+ * @param offset At which offset to start interpreting the script.
+ *
+ * @return 0 if script finished. Else offset where to continue.
+ */
+uint16 Logic::script(uint16 scriptNo, uint16 offset) {
+script:
+ /// process a script
+ /// low level interface to interpreter
+
+ uint16 moduleNo = scriptNo >> 12;
+ debug(3, "Doing Script %x", (offset << 16) | scriptNo);
+ uint16 *scriptData = _moduleList[moduleNo]; // get module address
+
+ if (!scriptData) { // We need to load the script module
+ _moduleList[moduleNo] = _skyDisk->loadScriptFile(moduleNo + F_MODULE_0);
+ scriptData = _moduleList[moduleNo]; // module has been loaded
+ }
+
+ uint16 *moduleStart = scriptData;
+
+ // Check whether we have an offset or what
+ if (offset)
+ scriptData = moduleStart + offset;
+ else
+ scriptData += scriptData[scriptNo & 0x0fff];
+
+ uint32 a = 0, b = 0, c = 0;
+ uint16 command, s;
+
+ for (;;) {
+ command = *scriptData++; // get a command
+ Debug::script(command, scriptData);
+
+ switch (command) {
+ case 0: // push_variable
+ push( _scriptVariables[*scriptData++ / 4] );
+ break;
+ case 1: // less_than
+ a = pop();
+ b = pop();
+ if (a > b)
+ push(1);
+ else
+ push(0);
+ break;
+ case 2: // push_number
+ push(*scriptData++);
+ break;
+ case 3: // not_equal
+ a = pop();
+ b = pop();
+ if (a != b)
+ push(1);
+ else
+ push(0);
+ break;
+ case 4: // if_and
+ a = pop();
+ b = pop();
+ if (a && b)
+ push(1);
+ else
+ push(0);
+ break;
+ case 5: // skip_zero
+ s = *scriptData++;
+
+ a = pop();
+ if (!a)
+ scriptData += s / 2;
+ break;
+ case 6: // pop_var
+ b = _scriptVariables[*scriptData++ / 4] = pop();
+ break;
+ case 7: // minus
+ a = pop();
+ b = pop();
+ push(b-a);
+ break;
+ case 8: // plus
+ a = pop();
+ b = pop();
+ push(b+a);
+ break;
+ case 9: // skip_always
+ s = *scriptData++;
+ scriptData += s / 2;
+ break;
+ case 10: // if_or
+ a = pop();
+ b = pop();
+ if (a || b)
+ push(1);
+ else
+ push(0);
+ break;
+ case 11: // call_mcode
+ {
+ a = *scriptData++;
+ assert(a <= 3);
+ // No, I did not forget the "break"s
+ switch (a) {
+ case 3:
+ c = pop();
+ case 2:
+ b = pop();
+ case 1:
+ a = pop();
+ }
+
+ uint16 mcode = *scriptData++ / 4; // get mcode number
+ Debug::mcode(mcode, a, b, c);
+
+ Compact *saveCpt = _compact;
+ bool ret = (this->*_mcodeTable[mcode]) (a, b, c);
+ _compact = saveCpt;
+
+ if (!ret)
+ return (scriptData - moduleStart);
+ }
+ break;
+ case 12: // more_than
+ a = pop();
+ b = pop();
+ if (a < b)
+ push(1);
+ else
+ push(0);
+ break;
+ case 14: // switch
+ c = s = *scriptData++; // get number of cases
+
+ a = pop(); // and value to switch on
+
+ do {
+ if (a == *scriptData) {
+ scriptData += scriptData[1] / 2;
+ scriptData++;
+ break;
+ }
+ scriptData += 2;
+ } while (--s);
+
+ if (s == 0)
+ scriptData += *scriptData / 2; // use the default
+ break;
+ case 15: // push_offset
+ push( *(uint16 *)_skyCompact->getCompactElem(_compact, *scriptData++) );
+ break;
+ case 16: // pop_offset
+ // pop a value into a compact
+ *(uint16 *)_skyCompact->getCompactElem(_compact, *scriptData++) = (uint16)pop();
+ break;
+ case 17: // is_equal
+ a = pop();
+ b = pop();
+ if (a == b)
+ push(1);
+ else
+ push(0);
+ break;
+ case 18: { // skip_nz
+ int16 t = *scriptData++;
+ a = pop();
+ if (a)
+ scriptData += t / 2;
+ break;
+ }
+ case 13:
+ case 19: // script_exit
+ return 0;
+ case 20: // restart_script
+ offset = 0;
+ goto script;
+ default:
+ error("Unknown script command: %d", command);
+ }
+ }
+}
+
+bool Logic::fnCacheChip(uint32 a, uint32 b, uint32 c) {
+ _skySound->fnStopFx();
+ _skyDisk->fnCacheChip((uint16*)_skyCompact->fetchCpt((uint16)a));
+ return true;
+}
+
+bool Logic::fnCacheFast(uint32 a, uint32 b, uint32 c) {
+ _skyDisk->fnCacheFast((uint16*)_skyCompact->fetchCpt((uint16)a));
+ return true;
+}
+
+bool Logic::fnDrawScreen(uint32 a, uint32 b, uint32 c) {
+ debug(5, "Call: fnDrawScreen(%X, %X)",a,b);
+ SkyEngine::_systemVars.currentPalette = a;
+ _skyScreen->fnDrawScreen(a, b);
+
+ if (Logic::_scriptVariables[SCREEN] == 32) {
+ /* workaround for script bug #786482
+ Under certain circumstances, which never got completely cleared,
+ the gardener can get stuck in an animation, waiting for a sync
+ signal from foster.
+ This is most probably caused by foster leaving the screen before
+ sending the sync.
+ To work around that, we simply send a sync to the gardener every time
+ we enter the screen. If he isn't stuck (and thus not waiting for sync)
+ it will be ignored anyways */
+
+ debug(1, "sending gardener sync");
+ fnSendSync(ID_SC32_GARDENER, 1, 0);
+ }
+ return true;
+}
+
+bool Logic::fnAr(uint32 x, uint32 y, uint32 c) {
+ _compact->downFlag = 1; // assume failure in-case logic is interupted by speech (esp Joey)
+
+ _compact->arTargetX = (uint16)x;
+ _compact->arTargetY = (uint16)y;
+ _compact->logic = L_AR; // Set to AR mode
+
+ _compact->xcood &= 0xfff8;
+ _compact->ycood &= 0xfff8;
+
+ return false; // drop out of script
+}
+
+bool Logic::fnArAnimate(uint32 a, uint32 b, uint32 c) {
+ _compact->mood = 0; // high level 'not stood still'
+ _compact->logic = L_AR_ANIM;
+ return false; // drop out of script
+}
+
+bool Logic::fnIdle(uint32 a, uint32 b, uint32 c) {
+ // set the player idling
+ _compact->logic = 0;
+ return true;
+}
+
+bool Logic::fnInteract(uint32 targetId, uint32 b, uint32 c) {
+ _compact->mode += 4; // next level up
+ _compact->logic = L_SCRIPT;
+ Compact *cpt = _skyCompact->fetchCpt(targetId);
+
+ *SkyCompact::getSub(_compact, _compact->mode) = cpt->actionScript;
+ *SkyCompact::getSub(_compact, _compact->mode + 2) = 0;
+
+ return false;
+}
+
+bool Logic::fnStartSub(uint32 scr, uint32 b, uint32 c) {
+ _compact->mode += 4;
+ *SkyCompact::getSub(_compact, _compact->mode) = (uint16)(scr & 0xffff);
+ *SkyCompact::getSub(_compact, _compact->mode + 2) = (uint16)(scr >> 16);
+ return false;
+}
+
+bool Logic::fnTheyStartSub(uint32 mega, uint32 scr, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(mega);
+ cpt->mode += 4;
+ *SkyCompact::getSub(cpt, cpt->mode) = (uint16)(scr & 0xffff);
+ *SkyCompact::getSub(cpt, cpt->mode + 2) = (uint16)(scr >> 16);
+ return true;
+}
+
+bool Logic::fnAssignBase(uint32 id, uint32 scr, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ cpt->mode = C_BASE_MODE;
+ cpt->logic = L_SCRIPT;
+ cpt->baseSub = (uint16)(scr & 0xffff);
+ cpt->baseSub_off = (uint16)(scr >> 16);
+ return true;
+}
+
+bool Logic::fnDiskMouse(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->spriteMouse(MOUSE_DISK, 11, 11);
+ return true;
+}
+
+bool Logic::fnNormalMouse(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
+ return true;
+}
+
+bool Logic::fnBlankMouse(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->spriteMouse(MOUSE_BLANK, 0, 0);
+ return true;
+}
+
+bool Logic::fnCrossMouse(uint32 a, uint32 b, uint32 c) {
+ if (_scriptVariables[OBJECT_HELD])
+ _skyMouse->fnOpenCloseHand(false);
+ else
+ _skyMouse->spriteMouse(MOUSE_CROSS, 4, 4);
+ return true;
+}
+
+bool Logic::fnCursorRight(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->spriteMouse(MOUSE_RIGHT, 9, 4);
+ return true;
+}
+
+bool Logic::fnCursorLeft(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->spriteMouse(MOUSE_LEFT, 0, 5);
+ return true;
+}
+
+bool Logic::fnCursorDown(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->spriteMouse(MOUSE_DOWN, 9, 4);
+ return true;
+}
+
+bool Logic::fnCursorUp(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->spriteMouse(MOUSE_UP, 9, 4);
+ return true;
+}
+
+bool Logic::fnOpenHand(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->fnOpenCloseHand(true);
+ return true;
+}
+
+bool Logic::fnCloseHand(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->fnOpenCloseHand(false);
+ return true;
+}
+
+bool Logic::fnGetTo(uint32 targetPlaceId, uint32 mode, uint32 c) {
+ _compact->upFlag = (uint16)mode; // save mode for action script
+ _compact->mode += 4; // next level up
+ Compact *cpt = _skyCompact->fetchCpt(_compact->place);
+ if (!cpt) {
+ warning("can't find _compact's getToTable. Place compact is NULL");
+ return false;
+ }
+ uint16 *getToTable = (uint16*)_skyCompact->fetchCpt(cpt->getToTableId);
+ if (!getToTable) {
+ warning("Place compact's getToTable is NULL!");
+ return false;
+ }
+
+ while (*getToTable != targetPlaceId)
+ getToTable += 2;
+
+ // get new script
+ *SkyCompact::getSub(_compact, _compact->mode) = *(getToTable + 1);
+ *SkyCompact::getSub(_compact, _compact->mode + 2) = 0;
+
+ return false; // drop out of script
+}
+
+bool Logic::fnSetToStand(uint32 a, uint32 b, uint32 c) {
+ _compact->mood = 1; // high level stood still
+
+ _compact->grafixProgId = *(uint16*)_skyCompact->getCompactElem(_compact, C_STAND_UP + _compact->megaSet + _compact->dir * 4);
+ _compact->grafixProgPos = 0;
+
+ uint16 *standList = _skyCompact->getGrafixPtr(_compact);
+
+ _compact->offset = *standList; // get frames offset
+ _compact->logic = L_SIMPLE_MOD;
+ _compact->grafixProgPos++;
+ simpleAnim();
+ return false; // drop out of script
+}
+
+bool Logic::fnTurnTo(uint32 dir, uint32 b, uint32 c) {
+ /// turn compact to direction dir
+
+ uint16 curDir = _compact->dir; // get current direction
+ _compact->dir = (uint16)(dir & 0xffff); // set new direction
+
+ uint16 *tt = _skyCompact->getTurnTable(_compact, curDir);
+
+ if (!tt[dir])
+ return true; // keep going
+
+ _compact->turnProgId = tt[dir]; // put turn program in
+ _compact->turnProgPos = 0;
+ _compact->logic = L_TURNING;
+
+ turn();
+
+ return false; // drop out of script
+}
+
+bool Logic::fnArrived(uint32 scriptVar, uint32 b, uint32 c) {
+ _compact->leaving = (uint16)(scriptVar & 0xffff);
+ _scriptVariables[scriptVar/4]++;
+ return true;
+}
+
+bool Logic::fnLeaving(uint32 a, uint32 b, uint32 c) {
+ _compact->atWatch = 0;
+
+ if (_compact->leaving) {
+ _scriptVariables[_compact->leaving/4]--;
+ _compact->leaving = 0; // I shall do this only once
+ }
+
+ return true; // keep going
+}
+
+bool Logic::fnSetAlternate(uint32 scr, uint32 b, uint32 c) {
+ _compact->alt = (uint16)(scr & 0xffff);
+ _compact->logic = L_ALT;
+ return false;
+}
+
+bool Logic::fnAltSetAlternate(uint32 target, uint32 scr, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(target);
+ cpt->alt = (uint16)(scr & 0xffff);
+ cpt->logic = L_ALT;
+ return false;
+}
+
+bool Logic::fnKillId(uint32 id, uint32 b, uint32 c) {
+ if (id) {
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ if (cpt->status & (1 << 7))
+ _skyGrid->removeObjectFromWalk(cpt);
+ cpt->status = 0;
+ }
+ return true;
+}
+
+bool Logic::fnNoHuman(uint32 a, uint32 b, uint32 c) {
+ if (!_scriptVariables[MOUSE_STOP]) {
+ _scriptVariables[MOUSE_STATUS] &= 1;
+ runGetOff();
+ fnBlankMouse(0, 0, 0);
+ }
+ return true;
+}
+
+bool Logic::fnAddHuman(uint32 a, uint32 b, uint32 c) {
+ return _skyMouse->fnAddHuman();
+}
+
+bool Logic::fnAddButtons(uint32 a, uint32 b, uint32 c) {
+ _scriptVariables[MOUSE_STATUS] |= 4;
+ return true;
+}
+
+bool Logic::fnNoButtons(uint32 a, uint32 b, uint32 c) {
+ //remove the mouse buttons
+ _scriptVariables[MOUSE_STATUS] &= 0xFFFFFFFB;
+ return true;
+}
+
+bool Logic::fnSetStop(uint32 a, uint32 b, uint32 c) {
+ _scriptVariables[MOUSE_STOP] |= 1;
+ return true;
+}
+
+bool Logic::fnClearStop(uint32 a, uint32 b, uint32 c) {
+ _scriptVariables[MOUSE_STOP] = 0;
+ return true;
+}
+
+bool Logic::fnPointerText(uint32 a, uint32 b, uint32 c) {
+ _skyText->fnPointerText(a, _skyMouse->giveMouseX(), _skyMouse->giveMouseY());
+ return true;
+}
+
+bool Logic::fnQuit(uint32 a, uint32 b, uint32 c) {
+ return false;
+}
+
+bool Logic::fnSpeakMe(uint32 targetId, uint32 mesgNum, uint32 animNum) {
+ stdSpeak(_skyCompact->fetchCpt(targetId), mesgNum, animNum, 0);
+ return false; //drop out of script
+}
+
+bool Logic::fnSpeakMeDir(uint32 targetId, uint32 mesgNum, uint32 animNum) {
+ //must be player so don't cause script to drop out
+ //this function sets the directional option whereby
+ //the anim chosen is linked to c_dir
+ animNum += _compact->dir << 1; //2 sizes (large and small)
+ return fnSpeakMe(targetId, mesgNum, animNum);
+}
+
+bool Logic::fnSpeakWait(uint32 id, uint32 message, uint32 animation) {
+ // non player mega char speaks
+ // player will wait for it to finish before continuing script processing
+ _compact->flag = (uint16)(id & 0xffff);
+ _compact->logic = L_LISTEN;
+ return fnSpeakMe(id, message, animation);
+}
+
+bool Logic::fnSpeakWaitDir(uint32 a, uint32 b, uint32 c) {
+ /* non player mega chr$ speaks S2(20Jan93tw)
+ the player will wait for it to finish
+ before continuing script processing
+ this function sets the directional option whereby
+ the anim chosen is linked to c_dir -
+
+ _compact is player
+ a is ID to speak (not us)
+ b is text message number
+ c is base of mini table within anim_talk_table */
+
+#ifdef __DC__
+ __builtin_alloca(4); // Works around a gcc bug (wrong-code/11736)
+#endif
+
+ _compact->flag = (uint16)a;
+ _compact->logic = L_LISTEN;
+
+ Compact *speaker = _skyCompact->fetchCpt(a);
+ if (c) {
+ c += speaker->dir << 1;
+ stdSpeak(speaker, b, c, speaker->dir << 1);
+ } else
+ stdSpeak(speaker, b, c, 0);
+
+ return false;
+}
+
+bool Logic::fnChooser(uint32 a, uint32 b, uint32 c) {
+
+ // setup the text questions to be clicked on
+ // read from TEXT1 until 0
+
+ SkyEngine::_systemVars.systemFlags |= SF_CHOOSING; // can't save/restore while choosing
+
+ _scriptVariables[THE_CHOSEN_ONE] = 0; // clear result
+
+ uint32 *p = _scriptVariables + TEXT1;
+ uint16 ycood = TOP_LEFT_Y; // rolling coordinate
+
+ while (*p) {
+ uint32 textNum = *p++;
+
+ struct lowTextManager_t lowText = _skyText->lowTextManager(textNum, GAME_SCREEN_WIDTH, 0, 241, 0);
+
+ uint8 *data = lowText.textData;
+
+ // stipple the text
+
+ uint32 size = ((dataFileHeader *)data)->s_height * ((dataFileHeader *)data)->s_width;
+ uint32 index = 0;
+ uint32 width = ((dataFileHeader *)data)->s_width;
+
+ data += sizeof(dataFileHeader);
+
+ while (index < size) {
+ if (index % width <= 1)
+ index ^= 1; //index++;
+ if (!data[index])
+ data[index] = 1;
+ index += 2;
+ }
+
+ Compact *textCompact = _skyCompact->fetchCpt(lowText.compactNum);
+
+ textCompact->getToFlag = (uint16)textNum;
+ textCompact->downFlag = (uint16)*p++; // get animation number
+
+ textCompact->status |= ST_MOUSE; // mouse detects
+
+ textCompact->xcood = TOP_LEFT_X; // set coordinates
+ textCompact->ycood = ycood;
+ ycood += 12;
+ }
+
+ if (p == _scriptVariables + TEXT1)
+ return true;
+
+ _compact->logic = L_CHOOSE; // player frozen until choice made
+ fnAddHuman(0, 0, 0); // bring back mouse
+
+ return false;
+}
+
+bool Logic::fnHighlight(uint32 itemNo, uint32 pen, uint32 c) {
+ pen -= 11;
+ pen ^= 1;
+ pen += 241;
+ Compact *textCompact = _skyCompact->fetchCpt(itemNo);
+ uint8 *sprData = (uint8 *)SkyEngine::fetchItem(textCompact->flag);
+ _skyText->changeTextSpriteColour(sprData, (uint8)pen);
+ return true;
+}
+
+bool Logic::fnTextKill(uint32 a, uint32 b, uint32 c) {
+ /// Kill of text items that are mouse detectable
+
+ uint32 id = FIRST_TEXT_COMPACT;
+
+ for (int i = 10; i > 0; i--) {
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ if (cpt->status & (1 << 4))
+ cpt->status = 0;
+ id++;
+ }
+ return true;
+}
+
+bool Logic::fnStopMode(uint32 a, uint32 b, uint32 c) {
+ _compact->logic = L_STOPPED;
+ return false;
+}
+
+bool Logic::fnWeWait(uint32 id, uint32 b, uint32 c) {
+ /// We have hit another mega
+ /// we are going to wait for it to move
+
+ _compact->waitingFor = (uint16) id;
+ stopAndWait();
+ return true; // not sure about this
+}
+
+bool Logic::fnSendSync(uint32 mega, uint32 sync, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(mega);
+ cpt->sync = (uint16)(sync & 0xffff);
+ return false;
+}
+
+bool Logic::fnSendFastSync(uint32 mega, uint32 sync, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(mega);
+ cpt->sync = (uint16)(sync & 0xffff);
+ return true;
+}
+
+bool Logic::fnSendRequest(uint32 target, uint32 scr, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(target);
+ cpt->request = (uint16)(scr & 0xffff);
+ return false;
+}
+
+bool Logic::fnClearRequest(uint32 target, uint32 b, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(target);
+ cpt->request = 0;
+ return true;
+}
+
+bool Logic::fnCheckRequest(uint32 a, uint32 b, uint32 c) {
+ /// check for interaction request
+
+ if (!_compact->request)
+ return true;
+
+ _compact->mode = C_ACTION_MODE; // into action mode
+
+ _compact->actionSub = _compact->request;
+ _compact->actionSub_off = 0;
+
+ _compact->request = 0; // trash request
+ return false; // drop from script
+}
+
+bool Logic::fnStartMenu(uint32 firstObject, uint32 b, uint32 c) {
+ /// initialise the top menu bar
+ // firstObject is o0 for game menu, k0 for linc
+
+ uint i;
+ firstObject /= 4;
+
+ // (1) FIRST, SET UP THE 2 ARROWS SO THEY APPEAR ON SCREEN
+
+ Compact *cpt = _skyCompact->fetchCpt(47);
+ cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE;
+ cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff);
+
+ cpt = _skyCompact->fetchCpt(48);
+ cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE;
+ cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff);
+
+ // (2) COPY OBJECTS FROM NON-ZERO INVENTORY VARIABLES INTO OBJECT DISPLAY LIST (& COUNT THEM)
+
+ // sort the objects and pad with blanks
+
+ uint32 menuLength = 0;
+ for (i = firstObject; i < firstObject + ARRAYSIZE(_objectList); i++) {
+ if ( _scriptVariables[i] )
+ _objectList[menuLength++] = _scriptVariables[i];
+ }
+ _scriptVariables[MENU_LENGTH] = menuLength;
+
+ // (3) OK, NOW TOP UP THE LIST WITH THE REQUIRED NO. OF BLANK OBJECTS (for min display length 11)
+
+ uint32 blankID = 51;
+ for (i = menuLength; i < 11; i++)
+ _objectList[i] = blankID++;
+
+ // (4) KILL ID's OF ALL 20 OBJECTS SO UNWANTED ICONS (SCROLLED OFF) DON'T REMAIN ON SCREEN
+ // (There should be a better way of doing this - only kill id of 12th item when menu has scrolled right)
+
+ for (i = 0; i < ARRAYSIZE(_objectList); i++) {
+ if (_objectList[i])
+ (_skyCompact->fetchCpt(_objectList[i]))->status = ST_LOGIC;
+ else break;
+ }
+
+ // (5) NOW FIND OUT WHICH OBJECT TO START THE DISPLAY FROM (depending on scroll offset)
+
+ if (menuLength < 11) // check we can scroll
+ _scriptVariables[SCROLL_OFFSET] = 0;
+ else if (menuLength < _scriptVariables[SCROLL_OFFSET] + 11)
+ _scriptVariables[SCROLL_OFFSET] = menuLength - 11;
+
+ // (6) AND FINALLY, INITIALISE THE 11 OBJECTS SO THEY APPEAR ON SCREEEN
+
+ uint16 rollingX = TOP_LEFT_X + 28;
+ for (i = 0; i < 11; i++) {
+ cpt = _skyCompact->fetchCpt(
+ _objectList[_scriptVariables[SCROLL_OFFSET] + i]);
+
+ cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE;
+ cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff);
+
+ cpt->xcood = rollingX;
+ rollingX += 24;
+
+ if (_scriptVariables[MENU] == 2)
+ cpt->ycood = 136;
+ else
+ cpt->ycood = 112;
+ }
+
+ return true;
+}
+
+bool Logic::fnUnhighlight(uint32 item, uint32 b, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(item);
+ cpt->frame--;
+ cpt->getToFlag = 0;
+ return true;
+}
+
+bool Logic::fnFaceId(uint32 otherId, uint32 b, uint32 c) {
+ /// return the direction to turn to face another id
+ /// pass back result in c_just_flag
+
+ Compact *cpt = _skyCompact->fetchCpt(otherId);
+
+ int16 x = _compact->xcood - cpt->xcood;
+
+ if (x < 0) { // we're to the left
+ x = -x;
+ _compact->getToFlag = 3;
+ } else { // it's to the left
+ _compact->getToFlag = 2;
+ }
+
+ // now check y
+
+ // we must find the true bottom of the sprite
+ // it is not enough to use y coord because changing
+ // sprite offsets can ruin the formula - instead we
+ // will use the bottom of the mouse collision area
+
+ int16 y = _compact->ycood - (cpt->ycood + cpt->mouseRelY + cpt->mouseSizeY);
+
+ if (y < 0) { // it's below
+ y = -y;
+ if (y >= x)
+ _compact->getToFlag = 1;
+ } else { // it's above
+ if (y >= x)
+ _compact->getToFlag = 0;
+ }
+ return true;
+}
+
+bool Logic::fnForeground(uint32 sprite, uint32 b, uint32 c) {
+ /// Make sprite a foreground sprite
+ Compact *cpt = _skyCompact->fetchCpt(sprite);
+ cpt->status &= 0xfff8;
+ cpt->status |= ST_FOREGROUND;
+ return true;
+}
+
+bool Logic::fnBackground(uint32 a, uint32 b, uint32 c) {
+ /// Make us a background sprite
+ _compact->status &= 0xfff8;
+ _compact->status |= ST_BACKGROUND;
+ return true;
+}
+
+bool Logic::fnNewBackground(uint32 sprite, uint32 b, uint32 c) {
+ /// Make sprite a background sprite
+ Compact *cpt = _skyCompact->fetchCpt(sprite);
+ cpt->status &= 0xfff8;
+ cpt->status |= ST_BACKGROUND;
+ return true;
+}
+
+bool Logic::fnSort(uint32 mega, uint32 b, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(mega);
+ cpt->status &= 0xfff8;
+ cpt->status |= ST_SORT;
+ return true;
+}
+
+bool Logic::fnNoSpriteEngine(uint32 a, uint32 b, uint32 c) {
+ /// stop the compact printing
+ /// remove foreground, background & sort
+ _compact->status &= 0xfff8;
+ return true;
+}
+
+bool Logic::fnNoSpritesA6(uint32 us, uint32 b, uint32 c) {
+ /// stop the compact printing
+ /// remove foreground, background & sort
+ Compact *cpt = _skyCompact->fetchCpt(us);
+ cpt->status &= 0xfff8;
+ return true;
+}
+
+bool Logic::fnResetId(uint32 id, uint32 resetBlock, uint32 c) {
+ /// used when a mega is to be restarted
+ /// eg - when a smaller mega turn to larger
+ /// - a mega changes rooms...
+
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ uint16 *rst = (uint16 *)_skyCompact->fetchCpt(resetBlock);
+
+ if (!cpt) {
+ warning("fnResetId(): Compact %d (id) == NULL", id);
+ return true;
+ }
+ if (!rst) {
+ warning("fnResetId(): Compact %d (resetBlock) == NULL", resetBlock);
+ return true;
+ }
+
+ uint16 off;
+ while ((off = *rst++) != 0xffff)
+ *(uint16 *)_skyCompact->getCompactElem(cpt, off) = *rst++;
+ return true;
+}
+
+bool Logic::fnToggleGrid(uint32 a, uint32 b, uint32 c) {
+ /// Toggle a mega's grid plotting
+ _compact->status ^= ST_GRID_PLOT;
+ return true;
+}
+
+bool Logic::fnPause(uint32 cycles, uint32 b, uint32 c) {
+ /// Set mega to L_PAUSE
+ _compact->flag = (uint16)(cycles & 0xffff);
+ _compact->logic = L_PAUSE;
+ return false; // drop out of script
+}
+
+bool Logic::fnRunAnimMod(uint32 animNo, uint32 b, uint32 c) {
+ _compact->grafixProgId = animNo;
+ _compact->grafixProgPos = 0;
+
+ _compact->offset = *_skyCompact->getGrafixPtr(_compact);
+ _compact->grafixProgPos++;
+ _compact->logic = L_MOD_ANIMATE;
+ anim();
+ return false; // drop from script
+}
+
+bool Logic::fnSimpleMod(uint32 animSeqNo, uint32 b, uint32 c) {
+ _compact->grafixProgId = animSeqNo;
+ _compact->grafixProgPos = 0;
+
+ _compact->logic = L_SIMPLE_MOD;
+ _compact->offset = *_skyCompact->getGrafixPtr(_compact);
+ _compact->grafixProgPos++;
+ simpleAnim();
+ return false;
+}
+
+bool Logic::fnRunFrames(uint32 sequenceNo, uint32 b, uint32 c) {
+ _compact->grafixProgId = sequenceNo;
+ _compact->grafixProgPos = 0;
+
+ _compact->logic = L_FRAMES;
+ _compact->offset = *_skyCompact->getGrafixPtr(_compact);
+ _compact->grafixProgPos++;
+ simpleAnim();
+ return false;
+}
+
+bool Logic::fnAwaitSync(uint32 a, uint32 b, uint32 c) {
+ if (_compact->sync)
+ return true;
+
+ _compact->logic = L_WAIT_SYNC;
+ return false;
+}
+
+bool Logic::fnIncMegaSet(uint32 a, uint32 b, uint32 c) {
+ _compact->megaSet += NEXT_MEGA_SET;
+ return true;
+}
+
+bool Logic::fnDecMegaSet(uint32 a, uint32 b, uint32 c) {
+ _compact->megaSet -= NEXT_MEGA_SET;
+ return true;
+}
+
+bool Logic::fnSetMegaSet(uint32 mega, uint32 setNo, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(mega);
+ cpt->megaSet = (uint16) (setNo * NEXT_MEGA_SET);
+ return true;
+}
+
+bool Logic::fnMoveItems(uint32 listNo, uint32 screenNo, uint32 c) {
+ // Move a list of id's to another screen
+ uint16 *p = (uint16*)_skyCompact->fetchCpt(CPT_MOVE_LIST);
+ p = (uint16*)_skyCompact->fetchCpt(p[listNo]);
+ for (int i = 0; i < 2; i++) {
+ if (!*p)
+ return true;
+ Compact *cpt = _skyCompact->fetchCpt(*p++);
+ cpt->screen = (uint16)(screenNo & 0xffff);
+ }
+ return true;
+}
+
+bool Logic::fnNewList(uint32 a, uint32 b, uint32 c) {
+ /// Reset the chooser list
+ for (int i = 0; i < 16; i++)
+ _scriptVariables[TEXT1 + i] = 0;
+ return true;
+}
+
+bool Logic::fnAskThis(uint32 textNo, uint32 animNo, uint32 c) {
+ // find first free position
+ uint32 *p = _scriptVariables + TEXT1;
+ while (*p)
+ p += 2;
+ *p++ = textNo;
+ *p = animNo;
+ return true;
+}
+
+bool Logic::fnRandom(uint32 a, uint32 b, uint32 c) {
+ _scriptVariables[RND] = _rnd.getRandomNumber(65536) & a;
+ return true;
+}
+
+bool Logic::fnPersonHere(uint32 id, uint32 room, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ _scriptVariables[RESULT] = cpt->screen == room ? 1 : 0;
+ return true;
+}
+
+bool Logic::fnToggleMouse(uint32 a, uint32 b, uint32 c) {
+
+ _skyCompact->fetchCpt(a)->status ^= ST_MOUSE;
+ return true;
+}
+
+bool Logic::fnMouseOn(uint32 a, uint32 b, uint32 c) {
+ //switch on the mouse highlight
+ Compact *cpt = _skyCompact->fetchCpt(a);
+ cpt->status |= ST_MOUSE;
+ return true;
+}
+
+bool Logic::fnMouseOff(uint32 a, uint32 b, uint32 c) {
+ //switch off the mouse highlight
+ Compact *cpt = _skyCompact->fetchCpt(a);
+ cpt->status &= ~ST_MOUSE;
+ return true;
+}
+
+bool Logic::fnFetchX(uint32 id, uint32 b, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ _scriptVariables[RESULT] = cpt->xcood;
+ return true;
+}
+
+bool Logic::fnFetchY(uint32 id, uint32 b, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ _scriptVariables[RESULT] = cpt->ycood;
+ return true;
+}
+
+bool Logic::fnTestList(uint32 id, uint32 x, uint32 y) {
+ _scriptVariables[RESULT] = 0; // assume fail
+ uint16 *list = (uint16 *)_skyCompact->fetchCpt(id);
+
+ while (*list) {
+ if ((x >= list[0]) && (x < list[1]) && (y >= list[2]) && (y < list[3]))
+ _scriptVariables[RESULT] = list[4];
+ list += 5;
+ }
+ return true;
+}
+
+bool Logic::fnFetchPlace(uint32 id, uint32 b, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ _scriptVariables[RESULT] = cpt->place;
+ return true;
+}
+
+bool Logic::fnCustomJoey(uint32 id, uint32 b, uint32 c) {
+ /// return id's x & y coordinate & c_mood (i.e. stood still yes/no)
+ /// used by Joey-Logic - done in code like this because scripts can't
+ /// get access to another megas compact as easily
+
+ Compact *cpt = _skyCompact->fetchCpt(id);
+
+ _scriptVariables[PLAYER_X] = cpt->xcood;
+ _scriptVariables[PLAYER_Y] = cpt->ycood;
+ _scriptVariables[PLAYER_MOOD] = cpt->mood;
+ _scriptVariables[PLAYER_SCREEN] = cpt->screen;
+ return true;
+}
+
+bool Logic::fnSetPalette(uint32 a, uint32 b, uint32 c) {
+ _skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(a));
+ SkyEngine::_systemVars.currentPalette = a;
+ return true;
+}
+
+bool Logic::fnTextModule(uint32 a, uint32 b, uint32 c) {
+ _skyText->fnTextModule(a, b);
+ return true;
+}
+
+bool Logic::fnChangeName(uint32 id, uint32 textNo, uint32 c) {
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ cpt->cursorText = (uint16) textNo;
+ return true;
+}
+
+bool Logic::fnMiniLoad(uint32 a, uint32 b, uint32 c) {
+ _skyDisk->fnMiniLoad((uint16)a);
+ return true;
+}
+
+bool Logic::fnFlushBuffers(uint32 a, uint32 b, uint32 c) {
+ _skyDisk->fnFlushBuffers();
+ return true;
+}
+
+bool Logic::fnFlushChip(uint32 a, uint32 b, uint32 c) {
+ // this should be done automatically
+ return true;
+}
+
+bool Logic::fnSaveCoods(uint32 a, uint32 b, uint32 c) {
+ _skyMouse->fnSaveCoods();
+ return true;
+}
+
+bool Logic::fnPlotGrid(uint32 x, uint32 y, uint32 width) {
+ _skyGrid->plotGrid(x, y, width, _compact);
+ return true;
+}
+
+bool Logic::fnRemoveGrid(uint32 x, uint32 y, uint32 width) {
+ _skyGrid->removeGrid(x, y, width, _compact);
+ return true;
+}
+
+bool Logic::fnEyeball(uint32 id, uint32 b, uint32 c) {
+ // set 'result' to frame no. pointing to foster, according to table used
+ // eg. FN_eyeball (id_eye_90_table);
+
+ uint16 *eyeTable = (uint16 *)_skyCompact->fetchCpt(id);
+ Compact *cpt = _skyCompact->fetchCpt(ID_BLUE_FOSTER);
+
+ uint32 x = cpt->xcood; // 168 < x < 416
+ x -= 168;
+ x >>= 3;
+
+ uint32 y = cpt->ycood; // 256 < y < 296
+ y -= 256;
+ y <<= 2;
+
+ _scriptVariables[RESULT] = eyeTable[x + y] + S91;
+ return true;
+}
+
+bool Logic::fnLeaveSection(uint32 sectionNo, uint32 b, uint32 c) {
+ if (SkyEngine::isDemo())
+ _skyControl->showGameQuitMsg();
+
+ if (sectionNo == 5) //linc section - has different mouse icons
+ _skyMouse->replaceMouseCursors(60301);
+
+ return true;
+}
+
+bool Logic::fnEnterSection(uint32 sectionNo, uint32 b, uint32 c) {
+
+ if (SkyEngine::isDemo() && (sectionNo > 2))
+ _skyControl->showGameQuitMsg();
+
+ _scriptVariables[CUR_SECTION] = sectionNo;
+ SkyEngine::_systemVars.currentMusic = 0;
+
+ if (sectionNo == 5) //linc section - has different mouse icons
+ _skyMouse->replaceMouseCursors(60302);
+
+ if ((sectionNo != _currentSection) || (SkyEngine::_systemVars.systemFlags & SF_GAME_RESTORED)) {
+ _currentSection = sectionNo;
+
+ sectionNo++;
+ _skyMusic->loadSection((byte)sectionNo);
+ _skySound->loadSection((byte)sectionNo);
+ _skyGrid->loadGrids();
+ SkyEngine::_systemVars.systemFlags &= ~SF_GAME_RESTORED;
+ }
+
+ return true;
+}
+
+bool Logic::fnRestoreGame(uint32 a, uint32 b, uint32 c) {
+ _skyControl->doLoadSavePanel();
+ return false;
+}
+
+bool Logic::fnRestartGame(uint32 a, uint32 b, uint32 c) {
+ _skyControl->restartGame();
+ return false;
+}
+
+bool Logic::fnNewSwingSeq(uint32 a, uint32 b, uint32 c) {
+ // only certain files work on pc. (huh?! something we should take care of?)
+ if ((a == 85) || (a == 106) || (a == 75) || (a == 15)) {
+ _skyScreen->startSequenceItem((uint16)a);
+ } else {
+ debug(1,"Logic::fnNewSwingSeq: ignored seq %d",a);
+ }
+ return true;
+}
+
+bool Logic::fnWaitSwingEnd(uint32 a, uint32 b, uint32 c) {
+ _skyScreen->waitForSequence();
+ return true;
+}
+
+bool Logic::fnSkipIntroCode(uint32 a, uint32 b, uint32 c) {
+ SkyEngine::_systemVars.pastIntro = true;
+ return true;
+}
+
+bool Logic::fnBlankScreen(uint32 a, uint32 b, uint32 c) {
+ _skyScreen->clearScreen();
+ return true;
+}
+
+bool Logic::fnPrintCredit(uint32 a, uint32 b, uint32 c) {
+
+ lowTextManager_t creditText = _skyText->lowTextManager(a, 240, 0, 248, true);
+ Compact *credCompact = _skyCompact->fetchCpt(creditText.compactNum);
+ credCompact->xcood = 168;
+ if ((a == 558) && (c == 215))
+ credCompact->ycood = 211;
+ else
+ credCompact->ycood = (uint16)c;
+ _scriptVariables[RESULT] = creditText.compactNum;
+ return true;
+}
+
+bool Logic::fnLookAt(uint32 a, uint32 b, uint32 c) {
+
+ struct lowTextManager_t textInfo = _skyText->lowTextManager(a, 240, 0, 248, true);
+ Compact *textCpt = _skyCompact->fetchCpt(textInfo.compactNum);
+ textCpt->xcood = 168;
+ textCpt->ycood = (uint16)c;
+
+ _skyScreen->recreate();
+ _skyScreen->spriteEngine();
+ _skyScreen->flip();
+
+ fnNoHuman(0, 0, 0);
+ _skyMouse->lockMouse();
+
+ _skyMouse->waitMouseNotPressed();
+
+ _skyMouse->unlockMouse();
+ fnAddHuman(0, 0, 0);
+ textCpt->status = 0;
+
+ return true;
+}
+
+bool Logic::fnLincTextModule(uint32 textPos, uint32 textNo, uint32 buttonAction) {
+
+ uint16 cnt;
+ if (buttonAction & 0x8000)
+ for (cnt = LINC_DIGIT_0; cnt <= LINC_DIGIT_9; cnt++)
+ _scriptVariables[cnt] = 0;
+ buttonAction &= 0x7FFF;
+ if (buttonAction < 10)
+ _scriptVariables[LINC_DIGIT_0 + buttonAction] = textNo;
+
+ lowTextManager_t text = _skyText->lowTextManager(textNo, 220, 0, 215, false);
+
+ Compact *textCpt = _skyCompact->fetchCpt(text.compactNum);
+
+ if (textPos < 20) { // line number (for text)
+ textCpt->xcood = 152;
+ textCpt->ycood = (uint16)textPos * 13 + 170;
+ } else if (textPos > 20) { // x coordinate (for numbers)
+ textCpt->xcood = (uint16)textPos;
+ textCpt->ycood = 214;
+ } else warning("::fnLincTextModule: textPos == 20");
+ textCpt->getToFlag = (uint16)textNo;
+ return true;
+}
+
+bool Logic::fnTextKill2(uint32 a, uint32 b, uint32 c) {
+ /// Kill all text items
+
+ uint32 id = FIRST_TEXT_COMPACT;
+
+ for (int i = 10; i > 0; i--) {
+ Compact *cpt = _skyCompact->fetchCpt(id);
+ cpt->status = 0;
+ id++;
+ }
+ return true;
+}
+
+bool Logic::fnSetFont(uint32 font, uint32 b, uint32 c) {
+ _skyText->fnSetFont(font);
+ return true;
+}
+
+bool Logic::fnStartFx(uint32 sound, uint32 b, uint32 c) {
+ _skySound->fnStartFx(sound, (uint8)(b & 1));
+ return true;
+}
+
+bool Logic::fnStopFx(uint32 a, uint32 b, uint32 c) {
+ _skySound->fnStopFx();
+ return true;
+}
+
+bool Logic::fnStartMusic(uint32 a, uint32 b, uint32 c) {
+ if (!(SkyEngine::_systemVars.systemFlags & SF_MUS_OFF))
+ _skyMusic->startMusic((uint16)a);
+ SkyEngine::_systemVars.currentMusic = (uint16)a;
+ return true;
+}
+
+bool Logic::fnStopMusic(uint32 a, uint32 b, uint32 c) {
+ _skyMusic->startMusic(0);
+ SkyEngine::_systemVars.currentMusic = 0;
+ return true;
+}
+
+bool Logic::fnFadeDown(uint32 a, uint32 b, uint32 c) {
+ _skyScreen->fnFadeDown(a);
+ return true;
+}
+
+bool Logic::fnFadeUp(uint32 a, uint32 b, uint32 c) {
+ SkyEngine::_systemVars.currentPalette = a;
+ _skyScreen->fnFadeUp(a,b);
+ return true;
+}
+
+bool Logic::fnQuitToDos(uint32 a, uint32 b, uint32 c) {
+ SkyEngine::_systemVars.quitGame = true;
+ return false;
+}
+
+bool Logic::fnPauseFx(uint32 a, uint32 b, uint32 c) {
+ _skySound->fnPauseFx();
+ return true;
+}
+
+bool Logic::fnUnPauseFx(uint32 a, uint32 b, uint32 c) {
+ _skySound->fnUnPauseFx();
+ return true;
+}
+
+bool Logic::fnPrintf(uint32 a, uint32 b, uint32 c) {
+ printf("fnPrintf: %d\n", a);
+ return true;
+}
+
+void Logic::stdSpeak(Compact *target, uint32 textNum, uint32 animNum, uint32 base) {
+
+ animNum += target->megaSet / NEXT_MEGA_SET;
+ animNum &= 0xFF;
+
+ uint16 *talkTable = (uint16*)_skyCompact->fetchCpt(CPT_TALK_TABLE_LIST);
+ target->grafixProgId = talkTable[animNum];
+ target->grafixProgPos = 0;
+ uint16 *animPtr = _skyCompact->getGrafixPtr(target);
+
+ if (animPtr) {
+ target->offset = *animPtr++;
+ target->getToFlag = *animPtr++;
+ target->grafixProgPos += 2;
+ } else
+ target->grafixProgId = 0;
+
+ bool speechFileFound = false;
+ if (SkyEngine::isCDVersion())
+ speechFileFound = _skySound->startSpeech((uint16)textNum);
+
+ if ((SkyEngine::_systemVars.systemFlags & SF_ALLOW_TEXT) || !speechFileFound) {
+ // form the text sprite, if player wants subtitles or
+ // if we couldn't find the speech file
+ struct lowTextManager_t textInfo;
+ textInfo = _skyText->lowTextManager(textNum, FIXED_TEXT_WIDTH, 0, (uint8)target->spColour, true);
+ Compact *textCompact = _skyCompact->fetchCpt(textInfo.compactNum);
+ target->spTextId = textInfo.compactNum; //So we know what text to kill
+ byte *textGfx = textInfo.textData;
+
+ textCompact->screen = target->screen; //put it on our screen
+
+ if (_scriptVariables[SCREEN] == target->screen) { // Only use coordinates if we are on the current screen
+ //talking on-screen
+ //create the x coordinate for the speech text
+ //we need the talkers sprite information
+ byte *targetGfx = (byte *)SkyEngine::fetchItem(target->frame >> 6);
+ uint16 xPos = target->xcood + ((struct dataFileHeader *)targetGfx)->s_offset_x;
+ uint16 width = (((struct dataFileHeader *)targetGfx)->s_width >> 1);
+
+ xPos += width - (FIXED_TEXT_WIDTH / 2); //middle of talker
+
+ if (xPos < TOP_LEFT_X)
+ xPos = TOP_LEFT_X;
+
+ width = xPos + FIXED_TEXT_WIDTH;
+ if ((TOP_LEFT_X + FULL_SCREEN_WIDTH) <= width) {
+ xPos = TOP_LEFT_X + FULL_SCREEN_WIDTH;
+ xPos -= FIXED_TEXT_WIDTH;
+ }
+
+ textCompact->xcood = xPos;
+ uint16 yPos = target->ycood + ((struct dataFileHeader *)targetGfx)->s_offset_y - 6 - ((struct dataFileHeader *)textGfx)->s_height;
+
+ if (yPos < TOP_LEFT_Y)
+ yPos = TOP_LEFT_Y;
+
+ textCompact->ycood = yPos;
+
+ } else {
+ //talking off-screen
+ target->spTextId = 0; //don't kill any text 'cos none was made
+ textCompact->status = 0; //don't display text
+ }
+ // In CD version, we're doing the timing by checking when the VOC has stopped playing.
+ // Setting spTime to 10 thus means that we're doing a pause of 10 gamecycles between
+ // each sentence.
+ if (speechFileFound)
+ target->spTime = 10;
+ else
+ target->spTime = (uint16)_skyText->_numLetters + 5;
+ } else {
+ target->spTime = 10;
+ target->spTextId = 0;
+ }
+ target->logic = L_TALK;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/logic.h b/engines/sky/logic.h
new file mode 100644
index 0000000000..7d10be6d8c
--- /dev/null
+++ b/engines/sky/logic.h
@@ -0,0 +1,338 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYLOGIC_H
+#define SKYLOGIC_H
+
+#include "common/stdafx.h"
+#include "common/util.h"
+
+namespace Sky {
+
+struct Compact;
+
+enum scriptVariableOffsets {
+ RESULT = 0,
+ SCREEN = 1,
+ LOGIC_LIST_NO = 2,
+ MOUSE_LIST_NO = 6,
+ DRAW_LIST_NO = 8,
+ CUR_ID = 12,
+ MOUSE_STATUS = 13,
+ MOUSE_STOP = 14,
+ BUTTON = 15,
+ SPECIAL_ITEM = 17,
+ GET_OFF = 18,
+ CURSOR_ID = 22,
+ SAFEX = 25,
+ SAFEY = 26,
+ PLAYER_X = 27,
+ PLAYER_Y = 28,
+ PLAYER_MOOD = 29,
+ PLAYER_SCREEN = 30,
+ HIT_ID = 37,
+ LAYER_0_ID = 41,
+ LAYER_1_ID = 42,
+ LAYER_2_ID = 43,
+ LAYER_3_ID = 44,
+ GRID_1_ID = 45,
+ GRID_2_ID = 46,
+ GRID_3_ID = 47,
+ THE_CHOSEN_ONE = 51,
+ TEXT1 = 53,
+ MENU_LENGTH = 100,
+ SCROLL_OFFSET = 101,
+ MENU = 102,
+ OBJECT_HELD = 103,
+ LAMB_GREET = 109,
+ RND = 115,
+ CUR_SECTION = 143,
+ JOEY_SECTION = 145,
+ LAMB_SECTION = 146,
+ KNOWS_PORT = 190,
+ GOT_SPONSOR = 240,
+ GOT_JAMMER = 258,
+ CONSOLE_TYPE = 345,
+ S15_FLOOR = 450,
+ FOREMAN_FRIEND = 451,
+ REICH_DOOR_FLAG = 470,
+ CARD_STATUS = 479,
+ CARD_FIX = 480,
+ GUARDIAN_THERE = 640,
+ FS_COMMAND = 643,
+ ENTER_DIGITS = 644,
+ LINC_DIGIT_0 = 646,
+ LINC_DIGIT_1 = 647,
+ LINC_DIGIT_2 = 648,
+ LINC_DIGIT_3 = 649,
+ LINC_DIGIT_4 = 650,
+ LINC_DIGIT_5 = 651,
+ LINC_DIGIT_6 = 651,
+ LINC_DIGIT_7 = 653,
+ LINC_DIGIT_8 = 654,
+ LINC_DIGIT_9 = 655,
+ DOOR_67_68_FLAG = 678,
+ SC70_IRIS_FLAG = 693,
+ DOOR_73_75_FLAG = 704,
+ SC76_CABINET1_FLAG = 709,
+ SC76_CABINET2_FLAG = 710,
+ SC76_CABINET3_FLAG = 711,
+ DOOR_77_78_FLAG = 719,
+ SC80_EXIT_FLAG = 720,
+ SC31_LIFT_FLAG = 793,
+ SC32_LIFT_FLAG = 797,
+ SC33_SHED_DOOR_FLAG = 798,
+ BAND_PLAYING = 804,
+ COLSTON_AT_TABLE = 805,
+ SC36_NEXT_DEALER = 806,
+ SC36_DOOR_FLAG = 807,
+ SC37_DOOR_FLAG = 808,
+ SC40_LOCKER_1_FLAG = 817,
+ SC40_LOCKER_2_FLAG = 818,
+ SC40_LOCKER_3_FLAG = 819,
+ SC40_LOCKER_4_FLAG = 820,
+ SC40_LOCKER_5_FLAG = 821
+};
+
+#define NUM_SKY_SCRIPTVARS 838
+
+class AutoRoute;
+class Control;
+class Disk;
+class Grid;
+class Mouse;
+class MusicBase;
+class Screen;
+class Sound;
+class Text;
+class SkyCompact;
+
+class Logic;
+
+typedef void (Logic::*LogicTable) ();
+typedef bool (Logic::*McodeTable) (uint32, uint32, uint32);
+
+class Logic {
+public:
+ Logic(
+ SkyCompact *skyCompact,
+ Screen *skyScreen,
+ Disk *skyDisk,
+ Text *skyText,
+ MusicBase *skyMusic,
+ Mouse *skyMouse,
+ Sound *skySound);
+ ~Logic(void);
+ void engine();
+ void useControlInstance(Control *control) { _skyControl = control; };
+
+ uint16 mouseScript(uint32 scrNum, Compact *scriptComp);
+
+ static uint32 _scriptVariables[NUM_SKY_SCRIPTVARS];
+ Grid *_skyGrid;
+
+ uint16 script(uint16 scriptNo, uint16 offset);
+ void initScreen0(void);
+ void parseSaveData(uint32 *data);
+
+private:
+ void setupLogicTable();
+ void setupMcodeTable();
+ const LogicTable *_logicTable;
+ const McodeTable *_mcodeTable;
+
+protected:
+ void push(uint32);
+ uint32 pop();
+ void checkModuleLoaded(uint16 moduleNo);
+ bool collide(Compact *cpt);
+ void initScriptVariables();
+ void mainAnim();
+ void runGetOff();
+ void stopAndWait();
+ bool checkProtection(void);
+
+ void nop();
+ void logicScript();
+ void autoRoute();
+ void arAnim();
+ void arTurn();
+ void alt();
+ void anim();
+ void turn();
+ void cursor();
+ void talk();
+ void listen();
+ void stopped();
+ void choose();
+ void frames();
+ void pause();
+ void waitSync();
+ void simpleAnim();
+
+ bool fnCacheChip(uint32 a, uint32 b, uint32 c);
+ bool fnCacheFast(uint32 a, uint32 b, uint32 c);
+ bool fnDrawScreen(uint32 a, uint32 b, uint32 c);
+ bool fnAr(uint32 a, uint32 b, uint32 c);
+ bool fnArAnimate(uint32 a, uint32 b, uint32 c);
+ bool fnIdle(uint32 a, uint32 b, uint32 c);
+ bool fnInteract(uint32 a, uint32 b, uint32 c);
+ bool fnStartSub(uint32 a, uint32 b, uint32 c);
+ bool fnTheyStartSub(uint32 a, uint32 b, uint32 c);
+ bool fnAssignBase(uint32 a, uint32 b, uint32 c);
+ bool fnDiskMouse(uint32 a, uint32 b, uint32 c);
+ bool fnNormalMouse(uint32 a, uint32 b, uint32 c);
+ bool fnBlankMouse(uint32 a, uint32 b, uint32 c);
+ bool fnCrossMouse(uint32 a, uint32 b, uint32 c);
+ bool fnCursorRight(uint32 a, uint32 b, uint32 c);
+ bool fnCursorLeft(uint32 a, uint32 b, uint32 c);
+ bool fnCursorDown(uint32 a, uint32 b, uint32 c);
+ bool fnOpenHand(uint32 a, uint32 b, uint32 c);
+ bool fnCloseHand(uint32 a, uint32 b, uint32 c);
+ bool fnGetTo(uint32 a, uint32 b, uint32 c);
+ bool fnSetToStand(uint32 a, uint32 b, uint32 c);
+ bool fnTurnTo(uint32 a, uint32 b, uint32 c);
+ bool fnArrived(uint32 a, uint32 b, uint32 c);
+ bool fnLeaving(uint32 a, uint32 b, uint32 c);
+ bool fnSetAlternate(uint32 a, uint32 b, uint32 c);
+ bool fnAltSetAlternate(uint32 a, uint32 b, uint32 c);
+ bool fnKillId(uint32 a, uint32 b, uint32 c);
+ bool fnNoHuman(uint32 a, uint32 b, uint32 c);
+ bool fnAddHuman(uint32 a, uint32 b, uint32 c);
+ bool fnAddButtons(uint32 a, uint32 b, uint32 c);
+ bool fnNoButtons(uint32 a, uint32 b, uint32 c);
+ bool fnSetStop(uint32 a, uint32 b, uint32 c);
+ bool fnClearStop(uint32 a, uint32 b, uint32 c);
+ bool fnPointerText(uint32 a, uint32 b, uint32 c);
+ bool fnQuit(uint32 a, uint32 b, uint32 c);
+ bool fnSpeakMe(uint32 targetId, uint32 mesgNum, uint32 animNum);
+ bool fnSpeakMeDir(uint32 targetId, uint32 mesgNum, uint32 animNum);
+ bool fnSpeakWait(uint32 a, uint32 b, uint32 c);
+ bool fnSpeakWaitDir(uint32 a, uint32 b, uint32 c);
+ bool fnChooser(uint32 a, uint32 b, uint32 c);
+ bool fnHighlight(uint32 a, uint32 b, uint32 c);
+ bool fnTextKill(uint32 a, uint32 b, uint32 c);
+ bool fnStopMode(uint32 a, uint32 b, uint32 c);
+ bool fnWeWait(uint32 a, uint32 b, uint32 c);
+ bool fnSendSync(uint32 a, uint32 b, uint32 c);
+ bool fnSendFastSync(uint32 a, uint32 b, uint32 c);
+ bool fnSendRequest(uint32 a, uint32 b, uint32 c);
+ bool fnClearRequest(uint32 a, uint32 b, uint32 c);
+ bool fnCheckRequest(uint32 a, uint32 b, uint32 c);
+ bool fnStartMenu(uint32 a, uint32 b, uint32 c);
+ bool fnUnhighlight(uint32 a, uint32 b, uint32 c);
+ bool fnFaceId(uint32 a, uint32 b, uint32 c);
+ bool fnForeground(uint32 a, uint32 b, uint32 c);
+ bool fnBackground(uint32 a, uint32 b, uint32 c);
+ bool fnNewBackground(uint32 a, uint32 b, uint32 c);
+ bool fnSort(uint32 a, uint32 b, uint32 c);
+ bool fnNoSpriteEngine(uint32 a, uint32 b, uint32 c);
+ bool fnNoSpritesA6(uint32 a, uint32 b, uint32 c);
+ bool fnResetId(uint32 a, uint32 b, uint32 c);
+ bool fnToggleGrid(uint32 a, uint32 b, uint32 c);
+ bool fnPause(uint32 a, uint32 b, uint32 c);
+ bool fnRunAnimMod(uint32 a, uint32 b, uint32 c);
+ bool fnSimpleMod(uint32 a, uint32 b, uint32 c);
+ bool fnRunFrames(uint32 a, uint32 b, uint32 c);
+ bool fnAwaitSync(uint32 a, uint32 b, uint32 c);
+ bool fnIncMegaSet(uint32 a, uint32 b, uint32 c);
+ bool fnDecMegaSet(uint32 a, uint32 b, uint32 c);
+ bool fnSetMegaSet(uint32 a, uint32 b, uint32 c);
+ bool fnMoveItems(uint32 a, uint32 b, uint32 c);
+ bool fnNewList(uint32 a, uint32 b, uint32 c);
+ bool fnAskThis(uint32 a, uint32 b, uint32 c);
+ bool fnRandom(uint32 a, uint32 b, uint32 c);
+ bool fnPersonHere(uint32 a, uint32 b, uint32 c);
+ bool fnToggleMouse(uint32 a, uint32 b, uint32 c);
+ bool fnMouseOn(uint32 a, uint32 b, uint32 c);
+ bool fnMouseOff(uint32 a, uint32 b, uint32 c);
+ bool fnFetchX(uint32 a, uint32 b, uint32 c);
+ bool fnFetchY(uint32 a, uint32 b, uint32 c);
+ bool fnTestList(uint32 a, uint32 b, uint32 c);
+ bool fnFetchPlace(uint32 a, uint32 b, uint32 c);
+ bool fnCustomJoey(uint32 a, uint32 b, uint32 c);
+ bool fnSetPalette(uint32 a, uint32 b, uint32 c);
+ bool fnTextModule(uint32 a, uint32 b, uint32 c);
+ bool fnChangeName(uint32 a, uint32 b, uint32 c);
+ bool fnMiniLoad(uint32 a, uint32 b, uint32 c);
+ bool fnFlushBuffers(uint32 a, uint32 b, uint32 c);
+ bool fnFlushChip(uint32 a, uint32 b, uint32 c);
+ bool fnSaveCoods(uint32 a, uint32 b, uint32 c);
+ bool fnPlotGrid(uint32 a, uint32 b, uint32 c);
+ bool fnRemoveGrid(uint32 a, uint32 b, uint32 c);
+ bool fnEyeball(uint32 a, uint32 b, uint32 c);
+ bool fnCursorUp(uint32 a, uint32 b, uint32 c);
+ bool fnLeaveSection(uint32 a, uint32 b, uint32 c);
+ bool fnEnterSection(uint32 sectionNo, uint32 b, uint32 c);
+ bool fnRestoreGame(uint32 a, uint32 b, uint32 c);
+ bool fnRestartGame(uint32 a, uint32 b, uint32 c);
+ bool fnNewSwingSeq(uint32 a, uint32 b, uint32 c);
+ bool fnWaitSwingEnd(uint32 a, uint32 b, uint32 c);
+ bool fnSkipIntroCode(uint32 a, uint32 b, uint32 c);
+ bool fnBlankScreen(uint32 a, uint32 b, uint32 c);
+ bool fnPrintCredit(uint32 a, uint32 b, uint32 c);
+ bool fnLookAt(uint32 a, uint32 b, uint32 c);
+ bool fnLincTextModule(uint32 a, uint32 b, uint32 c);
+ bool fnTextKill2(uint32 a, uint32 b, uint32 c);
+ bool fnSetFont(uint32 a, uint32 b, uint32 c);
+ bool fnStartFx(uint32 a, uint32 b, uint32 c);
+ bool fnStopFx(uint32 a, uint32 b, uint32 c);
+ bool fnStartMusic(uint32 a, uint32 b, uint32 c);
+ bool fnStopMusic(uint32 a, uint32 b, uint32 c);
+ bool fnFadeDown(uint32 a, uint32 b, uint32 c);
+ bool fnFadeUp(uint32 a, uint32 b, uint32 c);
+ bool fnQuitToDos(uint32 a, uint32 b, uint32 c);
+ bool fnPauseFx(uint32 a, uint32 b, uint32 c);
+ bool fnUnPauseFx(uint32 a, uint32 b, uint32 c);
+ bool fnPrintf(uint32 a, uint32 b, uint32 c);
+
+ void stdSpeak(Compact *target, uint32 textNum, uint32 animNum, uint32 base);
+ void fnExec(uint16 num, uint32 a, uint32 b, uint32 c);
+
+ uint16 *_moduleList[16];
+ uint32 _stack[20];
+ byte _stackPtr;
+
+ Compact *_compact;
+
+ uint32 _objectList[30];
+
+ uint32 _currentSection;
+
+ Common::RandomSource _rnd;
+
+ SkyCompact *_skyCompact;
+ Screen *_skyScreen;
+ Disk *_skyDisk;
+ Text *_skyText;
+ MusicBase *_skyMusic;
+ Sound *_skySound;
+ AutoRoute *_skyAutoRoute;
+ Mouse *_skyMouse;
+ Control *_skyControl;
+
+ friend class Debugger;
+};
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sky/module.mk b/engines/sky/module.mk
new file mode 100644
index 0000000000..875cf65e47
--- /dev/null
+++ b/engines/sky/module.mk
@@ -0,0 +1,37 @@
+MODULE := engines/sky
+
+MODULE_OBJS := \
+ engines/sky/autoroute.o \
+ engines/sky/compact.o \
+ engines/sky/control.o \
+ engines/sky/debug.o \
+ engines/sky/disk.o \
+ engines/sky/grid.o \
+ engines/sky/hufftext.o \
+ engines/sky/intro.o \
+ engines/sky/logic.o \
+ engines/sky/mouse.o \
+ engines/sky/rnc_deco.o \
+ engines/sky/screen.o \
+ engines/sky/sky.o \
+ engines/sky/sound.o \
+ engines/sky/text.o \
+ engines/sky/music/adlibchannel.o \
+ engines/sky/music/adlibmusic.o \
+ engines/sky/music/gmchannel.o \
+ engines/sky/music/gmmusic.o \
+ engines/sky/music/mt32music.o \
+ engines/sky/music/musicbase.o
+
+MODULE_DIRS += \
+ engines/sky \
+ engines/sky/music \
+ engines/sky/compacts
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/sky/mouse.cpp b/engines/sky/mouse.cpp
new file mode 100644
index 0000000000..dd906b5b2d
--- /dev/null
+++ b/engines/sky/mouse.cpp
@@ -0,0 +1,323 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "sky/disk.h"
+#include "sky/logic.h"
+#include "sky/mouse.h"
+#include "sky/sky.h"
+#include "sky/skydefs.h"
+#include "sky/struc.h"
+#include "sky/compact.h"
+
+namespace Sky {
+
+#define MICE_FILE 60300
+#define NO_MAIN_OBJECTS 24
+#define NO_LINC_OBJECTS 21
+
+uint32 Mouse::_mouseMainObjects[24] = {
+ 65,
+ 9,
+ 66,
+ 64,
+ 8,
+ 63,
+ 10,
+ 11,
+ 71,
+ 76,
+ 37,
+ 36,
+ 42,
+ 75,
+ 79,
+ 6,
+ 74,
+ 39,
+ 49,
+ 43,
+ 34,
+ 35,
+ 77,
+ 38
+};
+
+uint32 Mouse::_mouseLincObjects[21] = {
+ 24625,
+ 24649,
+ 24827,
+ 24651,
+ 24583,
+ 24581,
+ 24582,
+ 24628,
+ 24650,
+ 24629,
+ 24732,
+ 24631,
+ 24584,
+ 24630,
+ 24626,
+ 24627,
+ 24632,
+ 24643,
+ 24828,
+ 24830,
+ 24829
+};
+
+Mouse::Mouse(OSystem *system, Disk *skyDisk, SkyCompact *skyCompact) {
+
+ _skyDisk = skyDisk;
+ _skyCompact = skyCompact;
+ _system = system;
+ _mouseB = 0;
+ _currentCursor = 6;
+ _mouseX = GAME_SCREEN_WIDTH / 2;
+ _mouseY = GAME_SCREEN_HEIGHT / 2;
+
+ _miceData = _skyDisk->loadFile(MICE_FILE);
+
+ //load in the object mouse file
+ _objectMouseData = _skyDisk->loadFile(MICE_FILE + 1);
+}
+
+Mouse::~Mouse( ){
+ free (_miceData);
+ free (_objectMouseData);
+}
+
+void Mouse::replaceMouseCursors(uint16 fileNo) {
+ free(_objectMouseData);
+ _objectMouseData = _skyDisk->loadFile(fileNo);
+}
+
+bool Mouse::fnAddHuman(void) {
+ //reintroduce the mouse so that the human can control the player
+ //could still be switched out at high-level
+
+ if (!Logic::_scriptVariables[MOUSE_STOP]) {
+ Logic::_scriptVariables[MOUSE_STATUS] |= 6; //cursor & mouse
+
+ if (_mouseY < 2) //stop mouse activating top line
+ _mouseY = 2;
+
+ _system->warpMouse(_mouseX, _mouseY);
+
+ //force the pointer engine into running a get-off
+ //even if it's over nothing
+
+ //KWIK-FIX
+ //get off may contain script to remove mouse pointer text
+ //surely this script should be run just in case
+ //I am going to try it anyway
+ if (Logic::_scriptVariables[GET_OFF])
+ _skyLogic->script((uint16)Logic::_scriptVariables[GET_OFF],(uint16)(Logic::_scriptVariables[GET_OFF] >> 16));
+
+ Logic::_scriptVariables[SPECIAL_ITEM] = 0xFFFFFFFF;
+ Logic::_scriptVariables[GET_OFF] = RESET_MOUSE;
+ }
+
+ return true;
+}
+
+void Mouse::fnSaveCoods(void) {
+ Logic::_scriptVariables[SAFEX] = _mouseX + TOP_LEFT_X;
+ Logic::_scriptVariables[SAFEY] = _mouseY + TOP_LEFT_Y;
+}
+
+void Mouse::lockMouse(void) {
+ SkyEngine::_systemVars.systemFlags |= SF_MOUSE_LOCKED;
+}
+
+void Mouse::unlockMouse(void) {
+ SkyEngine::_systemVars.systemFlags &= ~SF_MOUSE_LOCKED;
+}
+
+void Mouse::restoreMouseData(uint16 frameNum) {
+ warning("Stub: Mouse::restoreMouseData");
+}
+
+void Mouse::drawNewMouse() {
+ warning("Stub: Mouse::drawNewMouse");
+ //calculateMouseValues();
+ //saveMouseData();
+ //drawMouse();
+}
+
+void Mouse::waitMouseNotPressed(void) {
+
+ bool mousePressed = true;
+ OSystem::Event event;
+ while (mousePressed) {
+ _system->delayMillis(20);
+ while (_system->pollEvent(event)) {
+ if ((event.type == OSystem::EVENT_LBUTTONUP) ||
+ (event.type == OSystem::EVENT_QUIT))
+ mousePressed = false;
+ }
+ }
+}
+
+void Mouse::spriteMouse(uint16 frameNum, uint8 mouseX, uint8 mouseY) {
+
+ _currentCursor = frameNum;
+
+ byte *newCursor = _miceData;
+ newCursor += ((struct dataFileHeader *)_miceData)->s_sp_size * frameNum;
+ newCursor += sizeof(struct dataFileHeader);
+
+ uint16 mouseWidth = ((struct dataFileHeader *)_miceData)->s_width;
+ uint16 mouseHeight = ((struct dataFileHeader *)_miceData)->s_height;
+
+ _system->setMouseCursor(newCursor, mouseWidth, mouseHeight, mouseX, mouseY, 0);
+ if (frameNum == MOUSE_BLANK)
+ _system->showMouse(false);
+ else
+ _system->showMouse(true);
+}
+
+void Mouse::mouseEngine(uint16 mouseX, uint16 mouseY) {
+ _mouseX = mouseX;
+ _mouseY = mouseY;
+
+ _logicClick = (_mouseB > 0); // click signal is available for Logic for one gamecycle
+
+ if (!Logic::_scriptVariables[MOUSE_STOP]) {
+ if (Logic::_scriptVariables[MOUSE_STATUS] & (1 << 1)) {
+ pointerEngine(mouseX + TOP_LEFT_X, mouseY + TOP_LEFT_Y);
+ if (Logic::_scriptVariables[MOUSE_STATUS] & (1 << 2)) //buttons enabled?
+ buttonEngine1();
+ }
+ }
+ _mouseB = 0; //don't save up buttons
+}
+
+void Mouse::pointerEngine(uint16 xPos, uint16 yPos) {
+ uint32 currentListNum = Logic::_scriptVariables[MOUSE_LIST_NO];
+ uint16 *currentList;
+ do {
+ currentList = (uint16 *)_skyCompact->fetchCpt(currentListNum);
+ while ((*currentList != 0) && (*currentList != 0xFFFF)) {
+ uint16 itemNum = *currentList;
+ Compact *itemData = _skyCompact->fetchCpt(itemNum);
+ currentList++;
+ if ((itemData->screen == Logic::_scriptVariables[SCREEN]) && (itemData->status & 16)) {
+ if (itemData->xcood + ((int16)itemData->mouseRelX) > xPos) continue;
+ if (itemData->xcood + ((int16)itemData->mouseRelX) + itemData->mouseSizeX < xPos) continue;
+ if (itemData->ycood + ((int16)itemData->mouseRelY) > yPos) continue;
+ if (itemData->ycood + ((int16)itemData->mouseRelY) + itemData->mouseSizeY < yPos) continue;
+ // we've hit the item
+ if (Logic::_scriptVariables[SPECIAL_ITEM] == itemNum)
+ return;
+ Logic::_scriptVariables[SPECIAL_ITEM] = itemNum;
+ if (Logic::_scriptVariables[GET_OFF])
+ _skyLogic->mouseScript(Logic::_scriptVariables[GET_OFF], itemData);
+ Logic::_scriptVariables[GET_OFF] = itemData->mouseOff;
+ if (itemData->mouseOn)
+ _skyLogic->mouseScript(itemData->mouseOn, itemData);
+ return;
+ }
+ }
+ if (*currentList == 0xFFFF)
+ currentListNum = currentList[1];
+ } while (*currentList != 0);
+ if (Logic::_scriptVariables[SPECIAL_ITEM] != 0) {
+ Logic::_scriptVariables[SPECIAL_ITEM] = 0;
+
+ if (Logic::_scriptVariables[GET_OFF])
+ _skyLogic->script((uint16)Logic::_scriptVariables[GET_OFF],(uint16)(Logic::_scriptVariables[GET_OFF] >> 16));
+ Logic::_scriptVariables[GET_OFF] = 0;
+ }
+}
+
+void Mouse::buttonPressed(uint8 button) {
+
+ _mouseB = button;
+}
+
+void Mouse::buttonEngine1(void) {
+ //checks for clicking on special item
+ //"compare the size of this routine to S1 mouse_button"
+
+ if (_mouseB) { //anything pressed?
+ Logic::_scriptVariables[BUTTON] = _mouseB;
+ if (Logic::_scriptVariables[SPECIAL_ITEM]) { //over anything?
+ Compact *item = _skyCompact->fetchCpt(Logic::_scriptVariables[SPECIAL_ITEM]);
+ if (item->mouseClick)
+ _skyLogic->mouseScript(item->mouseClick, item);
+ }
+ }
+}
+
+void Mouse::resetCursor() {
+ spriteMouse(_currentCursor, 0, 0);
+}
+
+uint16 Mouse::findMouseCursor(uint32 itemNum) {
+
+ uint8 cnt;
+ for (cnt = 0; cnt < NO_MAIN_OBJECTS; cnt++) {
+ if (itemNum == _mouseMainObjects[cnt]) {
+ return cnt;
+ }
+ }
+ for (cnt = 0; cnt < NO_LINC_OBJECTS; cnt++) {
+ if (itemNum == _mouseLincObjects[cnt]) {
+ return cnt;
+ }
+ }
+ return 0;
+}
+
+void Mouse::fnOpenCloseHand(bool open) {
+
+ if ((!open) && (!Logic::_scriptVariables[OBJECT_HELD])) {
+ spriteMouse(1, 0, 0);
+ return;
+ }
+ uint16 cursor = findMouseCursor(Logic::_scriptVariables[OBJECT_HELD]) << 1;
+ if (open)
+ cursor++;
+
+ uint32 size = ((dataFileHeader*)_objectMouseData)->s_sp_size;
+ uint8 *srcData;
+ uint8 *destData;
+
+ srcData = (uint8 *)_objectMouseData + size * cursor + sizeof(dataFileHeader);
+ destData = (uint8 *)_miceData + sizeof(dataFileHeader);
+ memcpy(destData, srcData, size);
+ spriteMouse(0, 5, 5);
+}
+
+bool Mouse::wasClicked(void) {
+
+ if (_logicClick) {
+ _logicClick = false;
+ return true;
+ } else
+ return false;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/mouse.h b/engines/sky/mouse.h
new file mode 100644
index 0000000000..9e713cce03
--- /dev/null
+++ b/engines/sky/mouse.h
@@ -0,0 +1,92 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYMOUSE_H
+#define SKYMOUSE_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+
+class OSystem;
+
+namespace Sky {
+
+class Disk;
+class Logic;
+class SkyCompact;
+
+class Mouse {
+
+public:
+
+ Mouse(OSystem *system, Disk *skyDisk, SkyCompact *skyCompact);
+ ~Mouse(void);
+
+ void mouseEngine(uint16 mouseX, uint16 mouseY);
+ void replaceMouseCursors(uint16 fileNo);
+ bool fnAddHuman(void);
+ void fnSaveCoods(void);
+ void fnOpenCloseHand(bool open);
+ uint16 findMouseCursor(uint32 itemNum);
+ void lockMouse(void);
+ void unlockMouse(void);
+ void restoreMouseData(uint16 frameNum);
+ void drawNewMouse(void);
+ void spriteMouse(uint16 frameNum, uint8 mouseX, uint8 mouseY);
+ void useLogicInstance(Logic *skyLogic) { _skyLogic = skyLogic; };
+ void buttonPressed(uint8 button);
+ void waitMouseNotPressed(void);
+ uint16 giveMouseX(void) { return _mouseX; };
+ uint16 giveMouseY(void) { return _mouseY; };
+ uint16 giveCurrentMouseType(void) { return _currentCursor; };
+ bool wasClicked(void);
+ void logicClick(void) { _logicClick = true; };
+ void resetCursor();
+
+protected:
+
+ void pointerEngine(uint16 xPos, uint16 yPos);
+ void buttonEngine1(void);
+
+ bool _logicClick;
+
+ uint16 _mouseB; //mouse button
+ uint16 _mouseX; //actual mouse coordinates
+ uint16 _mouseY;
+
+ uint16 _currentCursor;
+
+ byte *_miceData; //address of mouse sprites
+ byte *_objectMouseData; //address of object mouse sprites
+
+ static uint32 _mouseMainObjects[24];
+ static uint32 _mouseLincObjects[21];
+
+ OSystem *_system;
+ Disk *_skyDisk;
+ Logic *_skyLogic;
+ SkyCompact *_skyCompact;
+};
+
+} // End of namespace Sky
+
+#endif //SKYMOUSE_H
diff --git a/engines/sky/music/adlibchannel.cpp b/engines/sky/music/adlibchannel.cpp
new file mode 100644
index 0000000000..9b28f832bb
--- /dev/null
+++ b/engines/sky/music/adlibchannel.cpp
@@ -0,0 +1,344 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "sky/music/adlibchannel.h"
+#include "sky/sky.h"
+
+namespace Sky {
+
+AdlibChannel::AdlibChannel(FM_OPL *opl, uint8 *pMusicData, uint16 startOfData) {
+ _opl = opl;
+ _musicData = pMusicData;
+ _channelData.startOfData = startOfData;
+ _channelData.eventDataPtr = startOfData;
+ _channelData.channelActive = 1;
+ _channelData.freqDataSize = 2;
+ _channelData.tremoVibro = 0;
+ _channelData.assignedInstrument = 0xFF;
+ _channelData.channelVolume = 0x7F;
+ _channelData.nextEventTime = getNextEventTime();
+
+ _channelData.adlibChannelNumber = _channelData.lastCommand = _channelData.note =
+ _channelData.adlibReg1 = _channelData.adlibReg2 = _channelData.freqOffset = 0;
+ _channelData.frequency = 0;
+ _channelData.instrumentData = NULL;
+
+ uint16 instrumentDataLoc;
+
+ if (SkyEngine::_systemVars.gameVersion == 109) {
+ //instrumentDataLoc = (_musicData[0x11D0] << 8) | _musicData[0x11CF];
+ //_frequenceTable = (uint16*)(_musicData + 0x835);
+ //_registerTable = _musicData + 0xE35;
+ //_opOutputTable = _musicData + 0xE47;
+ //_adlibRegMirror = _musicData + 0xF4A;
+
+ instrumentDataLoc = READ_LE_UINT16(_musicData + 0x1204);
+ _frequenceTable = (uint16*)(_musicData + 0x868);
+ _registerTable = _musicData + 0xE68;
+ _opOutputTable = _musicData + 0xE7A;
+ _adlibRegMirror = _musicData + 0xF7D;
+ } else if (SkyEngine::_systemVars.gameVersion == 267) {
+ instrumentDataLoc = READ_LE_UINT16(_musicData + 0x11FB);
+ _frequenceTable = (uint16*)(_musicData + 0x7F4);
+ _registerTable = _musicData + 0xDF4;
+ _opOutputTable = _musicData + 0xE06;
+ _adlibRegMirror = _musicData + 0xF55;
+ } else {
+ instrumentDataLoc = READ_LE_UINT16(_musicData + 0x1205);
+ _frequenceTable = (uint16*)(_musicData + 0x7FE);
+ _registerTable = _musicData + 0xDFE;
+ _opOutputTable = _musicData + 0xE10;
+ _adlibRegMirror = _musicData + 0xF5F;
+ }
+
+ _instrumentMap = _musicData+instrumentDataLoc;
+ _instruments = (InstrumentStruct*)(_instrumentMap+0x80);
+
+ _musicVolume = 0x100;
+}
+
+bool AdlibChannel::isActive(void) {
+
+ return _channelData.channelActive != 0;
+}
+
+void AdlibChannel::updateVolume(uint16 pVolume) {
+
+ _musicVolume = pVolume * 3;
+}
+
+/* This class uses the same area for the register mirror as the original
+ asm driver did (_musicData[0xF5F..0x105E]), so the cache is indeed shared
+ by all instances of the class.
+*/
+void AdlibChannel::setRegister(uint8 regNum, uint8 value) {
+
+ if (_adlibRegMirror[regNum] != value) {
+ OPLWriteReg (_opl, regNum, value);
+ _adlibRegMirror[regNum] = value;
+ }
+}
+
+void AdlibChannel::stopNote(void) {
+
+ if (_channelData.note & 0x20) {
+ _channelData.note &= ~0x20;
+ setRegister(0xB0 | _channelData.adlibChannelNumber, _channelData.note);
+ }
+}
+
+int32 AdlibChannel::getNextEventTime(void) {
+ int32 retV = 0;
+ uint8 cnt, lVal = 0;
+ for (cnt = 0; cnt < 4; cnt++) {
+ lVal = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+ retV = (retV << 7) | (lVal & 0x7F);
+ if (!(lVal & 0x80)) break;
+ }
+ if (lVal & 0x80) { // should never happen
+ return -1;
+ } else return retV;
+
+}
+
+uint8 AdlibChannel::process(uint16 aktTime) {
+
+ if (!_channelData.channelActive) {
+ return 0;
+ }
+
+ uint8 returnVal = 0;
+
+ _channelData.nextEventTime -= aktTime;
+ uint8 opcode;
+ while ((_channelData.nextEventTime < 0) && (_channelData.channelActive)) {
+ opcode = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+ if (opcode & 0x80) {
+ if (opcode == 0xFF) {
+ // dummy opcode
+ } else if (opcode >= 0x90) {
+ switch (opcode&0xF) {
+ case 0: com90_caseNoteOff(); break;
+ case 1: com90_stopChannel(); break;
+ case 2: com90_setupInstrument(); break;
+ case 3:
+ returnVal = com90_updateTempo();
+ break;
+ case 5: com90_getFreqOffset(); break;
+ case 6: com90_getChannelVolume(); break;
+ case 7: com90_getTremoVibro(); break;
+ case 8: com90_rewindMusic(); break;
+ case 9: com90_keyOff(); break;
+ case 12: com90_setStartOfData(); break;
+ case 4: //com90_dummy();
+ case 10: //com90_error();
+ case 11: //com90_doLodsb();
+ case 13: //com90_do_two_Lodsb();
+ error("Channel: dummy music routine 0x%02X was called",opcode);
+ _channelData.channelActive = 0;
+ break;
+ default:
+ // these opcodes aren't implemented in original music driver
+ error("Channel: Not existent routine 0x%02X was called",opcode);
+ _channelData.channelActive = 0;
+ break;
+ }
+ } else {
+ // new adlib channel assignment
+ _channelData.adlibChannelNumber = opcode&0xF;
+ _channelData.adlibReg1 = _registerTable[(opcode&0xF)<<1];
+ _channelData.adlibReg2 = _registerTable[((opcode&0xF)<<1)|1];
+ }
+ } else {
+ _channelData.lastCommand = opcode;
+ stopNote();
+ // not sure why this "if" is necessary...either a bug in my
+ // code or a bug in the music data (section 1, music 2)
+ if (_channelData.instrumentData || _channelData.tremoVibro) {
+ setupInstrument(opcode);
+
+ opcode = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+ setupChannelVolume(opcode);
+ } else _channelData.eventDataPtr++;
+ }
+ if (_channelData.channelActive)
+ _channelData.nextEventTime += getNextEventTime();
+ }
+ return returnVal;
+}
+
+void AdlibChannel::setupInstrument(uint8 opcode) {
+
+ uint16 nextNote;
+ if (_channelData.tremoVibro) {
+ uint8 newInstrument = _instrumentMap[opcode];
+ if (newInstrument != _channelData.assignedInstrument) {
+ _channelData.assignedInstrument = newInstrument;
+ _channelData.instrumentData = _instruments + newInstrument;
+ adlibSetupInstrument();
+ }
+ _channelData.lastCommand = _channelData.instrumentData->bindedEffect;
+ nextNote = getNextNote(_channelData.lastCommand);
+ } else {
+ nextNote = getNextNote(opcode - 0x18 + _channelData.instrumentData->bindedEffect);
+ }
+ _channelData.frequency = nextNote;
+ setRegister(0xA0 | _channelData.adlibChannelNumber, (uint8)nextNote);
+ setRegister(0xB0 | _channelData.adlibChannelNumber, (uint8)((nextNote >> 8) | 0x20));
+ _channelData.note = (uint8)((nextNote >> 8) | 0x20);
+}
+
+void AdlibChannel::setupChannelVolume(uint8 volume) {
+
+ uint8 resultOp;
+ uint32 resVol = ((volume + 1) * (_channelData.instrumentData->totOutLev_Op2 + 1)) << 1;
+ resVol &= 0xFFFF;
+ resVol *= (_channelData.channelVolume+1)<<1;
+ resVol >>= 8;
+ resVol *= _musicVolume;
+ resVol >>= 16;
+ resultOp = ((_channelData.instrumentData->scalingLevel << 6) & 0xC0) | _opOutputTable[resVol];
+ setRegister(0x40 | _channelData.adlibReg2, resultOp);
+ if (_channelData.instrumentData->feedBack & 1) {
+ resVol = ((volume + 1) * (_channelData.instrumentData->totOutLev_Op1 + 1)) << 1;
+ resVol &= 0xFFFF;
+ resVol *= (_channelData.channelVolume + 1)<<1;
+ resVol >>= 8;
+ resVol *= (_musicVolume & 0xFF);
+ resVol >>= 16;
+ } else resVol = _channelData.instrumentData->totOutLev_Op1;
+ resultOp = ((_channelData.instrumentData->scalingLevel << 2) & 0xC0) | _opOutputTable[resVol];
+ setRegister(0x40 | _channelData.adlibReg1, resultOp);
+}
+
+void AdlibChannel::adlibSetupInstrument(void) {
+
+ setRegister(0x60 | _channelData.adlibReg1, _channelData.instrumentData->ad_Op1);
+ setRegister(0x60 | _channelData.adlibReg2, _channelData.instrumentData->ad_Op2);
+ setRegister(0x80 | _channelData.adlibReg1, _channelData.instrumentData->sr_Op1);
+ setRegister(0x80 | _channelData.adlibReg2, _channelData.instrumentData->sr_Op2);
+ setRegister(0xE0 | _channelData.adlibReg1, _channelData.instrumentData->waveSelect_Op1);
+ setRegister(0xE0 | _channelData.adlibReg2, _channelData.instrumentData->waveSelect_Op2);
+ setRegister(0xC0 | _channelData.adlibChannelNumber, _channelData.instrumentData->feedBack);
+ setRegister(0x20 | _channelData.adlibReg1, _channelData.instrumentData->ampMod_Op1);
+ setRegister(0x20 | _channelData.adlibReg2, _channelData.instrumentData->ampMod_Op2);
+}
+
+#ifdef SCUMM_BIG_ENDIAN
+#define ENDIAN16(x) ((x >> 8) | ((x & 0xFF) << 8))
+#else
+#define ENDIAN16(x) (x)
+#endif
+
+uint16 AdlibChannel::getNextNote(uint8 param) {
+
+ int16 freqIndex = ((int16)_channelData.freqOffset) - 0x40;
+ if (freqIndex >= 0x3F) freqIndex++;
+ freqIndex *= _channelData.freqDataSize;
+ freqIndex += param<<6;
+ uint16 freqData = ENDIAN16(_frequenceTable[freqIndex % 0x300]);
+ if ((freqIndex%0x300 >= 0x1C0) || (freqIndex/0x300 > 0)) {
+ return (((freqIndex / 0x300) - 1) << 10) + (freqData & 0x7FF);
+ } else {
+ // looks like a bug. dunno why. It's what the ASM code says.
+ return (uint16)(((int16)freqData) >> 1);
+ }
+}
+
+//- command 90h routines
+
+void AdlibChannel::com90_caseNoteOff(void) {
+
+ if (_musicData[_channelData.eventDataPtr] == _channelData.lastCommand)
+ stopNote();
+ _channelData.eventDataPtr++;
+}
+
+void AdlibChannel::com90_stopChannel(void) {
+
+ stopNote();
+ _channelData.channelActive = 0;
+}
+
+void AdlibChannel::com90_setupInstrument(void) {
+
+ _channelData.channelVolume = 0x7F;
+ _channelData.freqOffset = 0x40;
+ _channelData.assignedInstrument = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+ _channelData.instrumentData = _instruments + _channelData.assignedInstrument;
+ adlibSetupInstrument();
+}
+
+uint8 AdlibChannel::com90_updateTempo(void) {
+
+ uint8 retV = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+ return retV;
+}
+
+void AdlibChannel::com90_getFreqOffset(void) {
+
+ _channelData.freqOffset = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+ if (_channelData.note & 0x20) {
+ uint16 nextNote = getNextNote(
+ _channelData.lastCommand - 0x18 + _channelData.instrumentData->bindedEffect);
+ setRegister(0xA0 | _channelData.adlibChannelNumber, (uint8)nextNote);
+ setRegister(0xB0 | _channelData.adlibChannelNumber, (uint8)((nextNote >> 8) | 0x20));
+ _channelData.note = (uint8)(nextNote >> 8) | 0x20;
+ }
+}
+
+void AdlibChannel::com90_getChannelVolume(void) {
+
+ _channelData.channelVolume = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+}
+
+void AdlibChannel::com90_getTremoVibro(void) {
+
+ _channelData.tremoVibro = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+}
+
+void AdlibChannel::com90_rewindMusic(void) {
+
+ _channelData.eventDataPtr = _channelData.startOfData;
+}
+
+void AdlibChannel::com90_keyOff(void) {
+
+ stopNote();
+}
+
+void AdlibChannel::com90_setStartOfData(void) {
+
+ _channelData.startOfData = _channelData.eventDataPtr;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/music/adlibchannel.h b/engines/sky/music/adlibchannel.h
new file mode 100644
index 0000000000..661f3a20dc
--- /dev/null
+++ b/engines/sky/music/adlibchannel.h
@@ -0,0 +1,106 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef ADLIBCHANNEL_H
+#define ADLIBCHANNEL_H
+
+#include "sky/music/musicbase.h"
+#include "sound/fmopl.h"
+
+namespace Sky {
+
+typedef struct {
+ uint8 ad_Op1, ad_Op2;
+ uint8 sr_Op1, sr_Op2;
+ uint8 ampMod_Op1, ampMod_Op2;
+ uint8 waveSelect_Op1, waveSelect_Op2;
+ uint8 bindedEffect;
+ uint8 feedBack;
+ uint8 totOutLev_Op1, totOutLev_Op2;
+ uint8 scalingLevel;
+ uint8 pad1, pad2, pad3;
+} InstrumentStruct;
+
+typedef struct {
+ uint16 eventDataPtr;
+ int32 nextEventTime;
+ uint16 startOfData;
+ uint8 adlibChannelNumber;
+ uint8 lastCommand;
+ uint8 channelActive;
+ uint8 note;
+ uint8 adlibReg1, adlibReg2;
+ InstrumentStruct *instrumentData;
+ uint8 assignedInstrument;
+ uint8 channelVolume;
+ uint8 padding; // field_12 / not used by original driver
+ uint8 tremoVibro;
+ uint8 freqDataSize;
+ uint8 freqOffset;
+ uint16 frequency;
+} AdlibChannelType;
+
+class AdlibChannel : public ChannelBase {
+public:
+ AdlibChannel (FM_OPL *opl, uint8 *pMusicData, uint16 startOfData);
+ virtual void stopNote(void);
+ virtual uint8 process(uint16 aktTime);
+ virtual void updateVolume(uint16 pVolume);
+ virtual bool isActive(void);
+private:
+ FM_OPL *_opl;
+ uint8 *_musicData;
+ uint16 _musicVolume;
+ AdlibChannelType _channelData;
+ //-
+ InstrumentStruct *_instruments;
+ uint16 *_frequenceTable;
+ uint8 *_instrumentMap;
+ uint8 *_registerTable, *_opOutputTable;
+ uint8 *_adlibRegMirror;
+ //- normal subs
+ void setRegister(uint8 regNum, uint8 value);
+ int32 getNextEventTime(void);
+ uint16 getNextNote(uint8 param);
+ void adlibSetupInstrument(void);
+ void setupInstrument(uint8 opcode);
+ void setupChannelVolume(uint8 volume);
+ //- Streamfunctions from Command90hTable
+ void com90_caseNoteOff(void); // 0
+ void com90_stopChannel(void); // 1
+ void com90_setupInstrument(void); // 2
+ uint8 com90_updateTempo(void); // 3
+ //void com90_dummy(void); // 4
+ void com90_getFreqOffset(void); // 5
+ void com90_getChannelVolume(void); // 6
+ void com90_getTremoVibro(void); // 7
+ void com90_rewindMusic(void); // 8
+ void com90_keyOff(void); // 9
+ //void com90_error(void); // 10
+ //void com90_doLodsb(void); // 11
+ void com90_setStartOfData(void); // 12
+ //void com90_do_two_Lodsb(void); // 13
+};
+
+} // End of namespace Sky
+
+#endif //ADLIBCHANNEL_H
diff --git a/engines/sky/music/adlibmusic.cpp b/engines/sky/music/adlibmusic.cpp
new file mode 100644
index 0000000000..e7ee4ecb6d
--- /dev/null
+++ b/engines/sky/music/adlibmusic.cpp
@@ -0,0 +1,121 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sky/music/adlibmusic.h"
+#include "sky/music/adlibchannel.h"
+#include "sound/mixer.h"
+#include "sky/sky.h"
+
+namespace Sky {
+
+AdlibMusic::AdlibMusic(Audio::Mixer *pMixer, Disk *pDisk)
+ : MusicBase(pDisk) {
+
+ _driverFileBase = 60202;
+ _mixer = pMixer;
+ _sampleRate = pMixer->getOutputRate();
+
+ _opl = makeAdlibOPL(_sampleRate);
+
+ _mixer->setupPremix(this);
+}
+
+AdlibMusic::~AdlibMusic(void) {
+
+ _mixer->setupPremix(0);
+}
+
+void AdlibMusic::premixerCall(int16 *data, uint len) {
+
+ if (_musicData == NULL) {
+ // no music loaded
+ memset(data, 0, 2 * len * sizeof(int16));
+ } else if ((_currentMusic == 0) || (_numberOfChannels == 0)) {
+ // music loaded but not played as of yet
+ memset(data, 0, 2 * len * sizeof(int16));
+ // poll anyways as pollMusic() can activate the music
+ pollMusic();
+ _nextMusicPoll = _sampleRate/50;
+ } else {
+ uint32 render;
+ int16 *origData = data;
+ uint origLen = len;
+ while (len) {
+ render = (len > _nextMusicPoll) ? (_nextMusicPoll) : (len);
+ len -= render;
+ _nextMusicPoll -= render;
+ YM3812UpdateOne(_opl, data, render);
+ data += render;
+ if (_nextMusicPoll == 0) {
+ pollMusic();
+ _nextMusicPoll = _sampleRate/50;
+ }
+ }
+
+ // Convert mono data to stereo
+ for (int i = (origLen - 1); i >= 0; i--) {
+ origData[2 * i] = origData[2 * i + 1] = origData[i];
+ }
+ }
+}
+
+void AdlibMusic::setupPointers(void) {
+
+ if (SkyEngine::_systemVars.gameVersion == 109) {
+ // disk demo uses a different adlib driver version, some offsets have changed
+ //_musicDataLoc = (_musicData[0x11CC] << 8) | _musicData[0x11CB];
+ //_initSequence = _musicData + 0xEC8;
+
+ _musicDataLoc = READ_LE_UINT16(_musicData + 0x1200);
+ _initSequence = _musicData + 0xEFB;
+ } else if (SkyEngine::_systemVars.gameVersion == 267) {
+ _musicDataLoc = READ_LE_UINT16(_musicData + 0x11F7);
+ _initSequence = _musicData + 0xE87;
+ } else {
+ _musicDataLoc = READ_LE_UINT16(_musicData + 0x1201);
+ _initSequence = _musicData + 0xE91;
+ }
+ _nextMusicPoll = 0;
+}
+
+void AdlibMusic::setupChannels(uint8 *channelData) {
+
+ _numberOfChannels = channelData[0];
+ channelData++;
+ for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
+ uint16 chDataStart = ((channelData[(cnt << 1) | 1] << 8) | channelData[cnt << 1]) + _musicDataLoc;
+ _channels[cnt] = new AdlibChannel(_opl, _musicData, chDataStart);
+ _channels[cnt]->updateVolume(_musicVolume);
+ }
+}
+
+void AdlibMusic::startDriver(void) {
+
+ uint16 cnt = 0;
+ while (_initSequence[cnt] || _initSequence[cnt+1]) {
+ OPLWriteReg (_opl, _initSequence[cnt], _initSequence[cnt+1]);
+ cnt += 2;
+ }
+ _allowedCommands = 0xD;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/music/adlibmusic.h b/engines/sky/music/adlibmusic.h
new file mode 100644
index 0000000000..effa19dc51
--- /dev/null
+++ b/engines/sky/music/adlibmusic.h
@@ -0,0 +1,64 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef ADLIBMUSIC_H
+#define ADLIBMUSIC_H
+
+#include "sky/music/musicbase.h"
+#include "sound/audiostream.h"
+#include "sound/fmopl.h"
+
+namespace Audio {
+ class Mixer;
+}
+
+namespace Sky {
+
+class AdlibMusic : public AudioStream, public MusicBase {
+public:
+ AdlibMusic(Audio::Mixer *pMixer, Disk *pDisk);
+ ~AdlibMusic(void);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples) {
+ premixerCall(buffer, numSamples / 2);
+ return numSamples;
+ }
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _sampleRate; }
+
+private:
+ FM_OPL *_opl;
+ Audio::Mixer *_mixer;
+ uint8 *_initSequence;
+ uint32 _sampleRate, _nextMusicPoll;
+ virtual void setupPointers(void);
+ virtual void setupChannels(uint8 *channelData);
+ virtual void startDriver(void);
+
+ void premixerCall(int16 *buf, uint len);
+};
+
+} // End of namespace Sky
+
+#endif //ADLIBMUSIC_H
diff --git a/engines/sky/music/gmchannel.cpp b/engines/sky/music/gmchannel.cpp
new file mode 100644
index 0000000000..66fbdfb69f
--- /dev/null
+++ b/engines/sky/music/gmchannel.cpp
@@ -0,0 +1,217 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "gmchannel.h"
+#include "common/util.h"
+#include "sound/mididrv.h"
+
+namespace Sky {
+
+GmChannel::GmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv, const byte *pInstMap, const byte *veloTab) {
+
+ _musicData = pMusicData;
+ _midiDrv = pMidiDrv;
+ _channelData.midiChannelNumber = 0;
+ _channelData.startOfData = startOfData;
+ _channelData.eventDataPtr = startOfData;
+ _channelData.channelActive = 1;
+ _channelData.nextEventTime = getNextEventTime();
+ _instMap = pInstMap;
+ _veloTab = veloTab;
+
+ _musicVolume = 0x7F;
+ _lastVolume = 0xFF;
+}
+
+bool GmChannel::isActive(void) {
+
+ return _channelData.channelActive != 0;
+}
+
+void GmChannel::updateVolume(uint16 pVolume) {
+
+ _musicVolume = pVolume;
+ if (_musicVolume > 0)
+ _musicVolume = (_musicVolume * 2) / 3 + 43;
+ if (_lastVolume < 0xFF) {
+ uint8 newVol = (_lastVolume * _musicVolume) >> 7;
+ _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x700 | (newVol << 16));
+ }
+}
+
+void GmChannel::stopNote(void) {
+
+ // All Notes Off
+ _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x7B00 | 0 | 0x79000000);
+ // Reset the Pitch Wheel. See bug #1016556.
+ _midiDrv->send((0xE0 | _channelData.midiChannelNumber) | 0x400000);
+}
+
+int32 GmChannel::getNextEventTime(void) {
+
+ int32 retV = 0;
+ uint8 cnt, lVal = 0;
+ for (cnt = 0; cnt < 4; cnt++) {
+ lVal = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+ retV = (retV << 7) | (lVal & 0x7F);
+ if (!(lVal & 0x80)) break;
+ }
+ if (lVal & 0x80) { // should never happen
+ return -1;
+ } else return retV;
+
+}
+
+uint8 GmChannel::process(uint16 aktTime) {
+
+ if (!_channelData.channelActive)
+ return 0;
+
+ uint8 returnVal = 0;
+
+ _channelData.nextEventTime -= aktTime;
+ uint8 opcode;
+
+ while ((_channelData.nextEventTime < 0) && (_channelData.channelActive)) {
+ opcode = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+ if (opcode&0x80) {
+ if (opcode == 0xFF) {
+ // dummy opcode
+ } else if (opcode >= 0x90) {
+ switch (opcode&0xF) {
+ case 0: com90_caseNoteOff(); break;
+ case 1: com90_stopChannel(); break;
+ case 2: com90_setupInstrument(); break;
+ case 3:
+ returnVal = com90_updateTempo();
+ break;
+ case 5: com90_getPitch(); break;
+ case 6: com90_getChannelVolume(); break;
+ case 8: com90_rewindMusic(); break;
+ case 9: com90_keyOff(); break;
+ case 11: com90_getChannelPanValue(); break;
+ case 12: com90_setStartOfData(); break;
+ case 13: com90_getChannelControl(); break;
+ case 4: //com90_dummy();
+ case 7: //com90_skipTremoVibro();
+ case 10: //com90_error();
+ error("Channel: dummy music routine 0x%02X was called",opcode);
+ _channelData.channelActive = 0;
+ break;
+ default:
+ // these opcodes aren't implemented in original music driver
+ error("Channel: Not existent routine 0x%02X was called",opcode);
+ _channelData.channelActive = 0;
+ break;
+ }
+ } else {
+ // new midi channel assignment
+ _channelData.midiChannelNumber = opcode&0xF;
+ }
+ } else {
+ _channelData.note = opcode;
+ byte velocity = _musicData[_channelData.eventDataPtr];
+ if (_veloTab)
+ velocity = _veloTab[velocity];
+ _channelData.eventDataPtr++;
+ _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (opcode << 8) | (velocity << 16));
+ }
+ if (_channelData.channelActive)
+ _channelData.nextEventTime += getNextEventTime();
+ }
+ return returnVal;
+}
+
+//- command 90h routines
+
+void GmChannel::com90_caseNoteOff(void) {
+
+ _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_musicData[_channelData.eventDataPtr] << 8));
+ _channelData.eventDataPtr++;
+}
+
+void GmChannel::com90_stopChannel(void) {
+
+ stopNote();
+ _channelData.channelActive = 0;
+}
+
+void GmChannel::com90_setupInstrument(void) {
+ byte instrument = _musicData[_channelData.eventDataPtr];
+ if (_instMap)
+ instrument = _instMap[instrument];
+ _midiDrv->send((0xC0 | _channelData.midiChannelNumber) | (instrument << 8));
+ _channelData.eventDataPtr++;
+}
+
+uint8 GmChannel::com90_updateTempo(void) {
+
+ uint8 retV = _musicData[_channelData.eventDataPtr];
+ _channelData.eventDataPtr++;
+ return retV;
+}
+
+void GmChannel::com90_getPitch(void) {
+
+ _midiDrv->send((0xE0 | _channelData.midiChannelNumber) | 0 | (_musicData[_channelData.eventDataPtr] << 16));
+ _channelData.eventDataPtr++;
+}
+
+void GmChannel::com90_getChannelVolume(void) {
+
+ _lastVolume = _musicData[_channelData.eventDataPtr];
+ uint8 newVol = (uint8)((_musicData[_channelData.eventDataPtr++] * _musicVolume) >> 7);
+ _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x700 | (newVol << 16));
+}
+
+void GmChannel::com90_rewindMusic(void) {
+
+ _channelData.eventDataPtr = _channelData.startOfData;
+}
+
+void GmChannel::com90_keyOff(void) {
+
+ _midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_channelData.note << 8) | 0);
+}
+
+void GmChannel::com90_setStartOfData(void) {
+
+ _channelData.startOfData = _channelData.eventDataPtr;
+}
+
+void GmChannel::com90_getChannelPanValue(void) {
+
+ _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x0A00 | (_musicData[_channelData.eventDataPtr] << 16));
+ _channelData.eventDataPtr++;
+}
+
+void GmChannel::com90_getChannelControl(void) {
+
+ uint8 conNum = _musicData[_channelData.eventDataPtr];
+ uint8 conDat = _musicData[_channelData.eventDataPtr + 1];
+ _channelData.eventDataPtr += 2;
+ _midiDrv->send((0xB0 | _channelData.midiChannelNumber) | (conNum << 8) | (conDat << 16));
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/music/gmchannel.h b/engines/sky/music/gmchannel.h
new file mode 100644
index 0000000000..051b5d0648
--- /dev/null
+++ b/engines/sky/music/gmchannel.h
@@ -0,0 +1,82 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYGMCHANNEL_H
+#define SKYGMCHANNEL_H
+
+#include "sky/music/musicbase.h"
+
+class MidiDriver;
+
+namespace Sky {
+
+typedef struct {
+ uint16 eventDataPtr;
+ int32 nextEventTime;
+ uint16 startOfData;
+ uint8 midiChannelNumber;
+ uint8 note;
+ uint8 channelActive;
+} MidiChannelType;
+
+class GmChannel : public ChannelBase {
+public:
+ GmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv, const byte *pInstMap, const byte *veloTab);
+ virtual void stopNote(void);
+ virtual uint8 process(uint16 aktTime);
+ virtual void updateVolume(uint16 pVolume);
+ virtual bool isActive(void);
+private:
+ const byte *_instMap;
+ const byte *_veloTab;
+ MidiDriver *_midiDrv;
+ uint8 *_musicData;
+ uint16 _musicVolume;
+ MidiChannelType _channelData;
+ uint8 _lastVolume;
+ //- normal subs
+ void setRegister(uint8 regNum, uint8 value);
+ int32 getNextEventTime(void);
+ uint16 getNextNote(uint8 param);
+ void adlibSetupInstrument(void);
+ void setupInstrument(uint8 opcode);
+ void setupChannelVolume(uint8 volume);
+ //- Streamfunctions from Command90hTable
+ void com90_caseNoteOff(void); // 0
+ void com90_stopChannel(void); // 1
+ void com90_setupInstrument(void); // 2
+ uint8 com90_updateTempo(void); // 3
+ //void com90_dummy(void); // 4
+ void com90_getPitch(void); // 5
+ void com90_getChannelVolume(void); // 6
+ //void com90_skipTremoVibro(void); // 7
+ void com90_rewindMusic(void); // 8
+ void com90_keyOff(void); // 9
+ //void com90_error(void); // 10
+ void com90_getChannelPanValue(void); // 11
+ void com90_setStartOfData(void); // 12
+ void com90_getChannelControl(void); // 13
+};
+
+} // End of namespace Sky
+
+#endif //SKYGMCHANNEL_H
diff --git a/engines/sky/music/gmmusic.cpp b/engines/sky/music/gmmusic.cpp
new file mode 100644
index 0000000000..bedb8acc7c
--- /dev/null
+++ b/engines/sky/music/gmmusic.cpp
@@ -0,0 +1,118 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sky/music/gmmusic.h"
+#include "sky/music/gmchannel.h"
+#include "sky/sky.h"
+#include "common/util.h"
+#include "sound/mididrv.h"
+
+namespace Sky {
+
+void GmMusic::passTimerFunc(void *param) {
+
+ ((GmMusic*)param)->timerCall();
+}
+
+GmMusic::GmMusic(MidiDriver *pMidiDrv, Disk *pDisk)
+ : MusicBase(pDisk) {
+
+ _driverFileBase = 60200;
+ _midiDrv = pMidiDrv;
+ int midiRes = _midiDrv->open();
+ if (midiRes != 0)
+ error("Can't open midi device. Errorcode: %d", midiRes);
+ _timerCount = 0;
+ _midiDrv->setTimerCallback(this, passTimerFunc);
+}
+
+GmMusic::~GmMusic(void) {
+
+ _midiDrv->setTimerCallback(NULL, NULL);
+ if (_currentMusic)
+ stopMusic();
+ // Send All Sound Off and All Notes Off (for external synths)
+ for (int i = 0; i < 16; ++i) {
+ _midiDrv->send ((120 << 8) | 0xB0 | i);
+ _midiDrv->send ((123 << 8) | 0xB0 | i);
+ }
+ _midiDrv->close();
+ delete _midiDrv;
+}
+
+void GmMusic::timerCall(void) {
+ _timerCount += _midiDrv->getBaseTempo();
+ if (_timerCount > (1000000 / 50)) {
+ // call pollMusic() 50 times per second
+ _timerCount -= 1000000 / 50;
+ if (_musicData != NULL)
+ pollMusic();
+ }
+}
+
+void GmMusic::setupPointers(void) {
+
+ if (SkyEngine::_systemVars.gameVersion == 109) {
+ _musicDataLoc = (_musicData[0x79C] << 8) | _musicData[0x79B];
+ _sysExSequence = _musicData + 0x1EF2;
+ } else {
+ _musicDataLoc = (_musicData[0x7DD] << 8) | _musicData[0x7DC];
+ _sysExSequence = ((_musicData[0x7E1] << 8) | _musicData[0x7E0]) + _musicData;
+ }
+}
+
+void GmMusic::setupChannels(uint8 *channelData) {
+
+ _numberOfChannels = channelData[0];
+ channelData++;
+ for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
+ uint16 chDataStart = ((channelData[(cnt << 1) | 1] << 8) | channelData[cnt << 1]) + _musicDataLoc;
+ _channels[cnt] = new GmChannel(_musicData, chDataStart, _midiDrv, MidiDriver::_mt32ToGm, _veloTab);
+ _channels[cnt]->updateVolume(_musicVolume);
+ }
+}
+
+void GmMusic::startDriver(void) {
+ // Send GM System On to reset channel parameters on external and capa$
+ uint8 sysEx[] = "\xf0\x7e\x7f\x09\x01\xf7";
+ _midiDrv->sysEx(sysEx, 6);
+ //_midiDrv->send(0xFF); //ALSA can't handle this.
+ // skip all sysEx as it can't be handled anyways.
+}
+
+const byte GmMusic::_veloTab[128] = {
+ 0x00, 0x40, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44,
+ 0x45, 0x45, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4A, 0x4A, 0x4B, 0x4B, 0x4C, 0x4C, 0x4D, 0x4D, 0x4E, 0x4E,
+ 0x4F, 0x4F, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x57, 0x57, 0x58, 0x58,
+ 0x59, 0x59, 0x5A, 0x5A, 0x5B, 0x5B, 0x5C, 0x5C, 0x5D, 0x5D,
+ 0x5E, 0x5E, 0x5F, 0x5F, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62,
+ 0x63, 0x63, 0x64, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67,
+ 0x68, 0x68, 0x69, 0x69, 0x6A, 0x6A, 0x6B, 0x6B, 0x6C, 0x6C,
+ 0x6D, 0x6D, 0x6E, 0x6E, 0x6F, 0x6F, 0x70, 0x70, 0x71, 0x71,
+ 0x72, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x76, 0x76,
+ 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7B,
+ 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x7F
+};
+
+} // End of namespace Sky
diff --git a/engines/sky/music/gmmusic.h b/engines/sky/music/gmmusic.h
new file mode 100644
index 0000000000..2623c6b5cd
--- /dev/null
+++ b/engines/sky/music/gmmusic.h
@@ -0,0 +1,52 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef GMMUSIC_H
+#define GMMUSIC_H
+
+#include "sky/music/musicbase.h"
+
+class MidiDriver;
+
+namespace Sky {
+
+class GmMusic : public MusicBase {
+public:
+ GmMusic(MidiDriver *pMidiDrv, Disk *pDisk);
+ ~GmMusic(void);
+private:
+ static void passTimerFunc(void *param);
+ void timerCall(void);
+
+ uint32 _timerCount;
+ uint8 *_sysExSequence;
+ MidiDriver *_midiDrv;
+ static const byte _veloTab[128];
+
+ virtual void setupPointers(void);
+ virtual void setupChannels(uint8 *channelData);
+ virtual void startDriver(void);
+};
+
+} // End of namespace Sky
+
+#endif //GMMUSIC_H
diff --git a/engines/sky/music/mt32music.cpp b/engines/sky/music/mt32music.cpp
new file mode 100644
index 0000000000..a6bcbf4d05
--- /dev/null
+++ b/engines/sky/music/mt32music.cpp
@@ -0,0 +1,169 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sky/music/mt32music.h"
+#include "sky/music/gmchannel.h"
+#include "common/util.h"
+#include "common/system.h"
+#include "sound/mididrv.h"
+
+namespace Sky {
+
+void MT32Music::passTimerFunc(void *param) {
+
+ ((MT32Music*)param)->timerCall();
+}
+
+MT32Music::MT32Music(MidiDriver *pMidiDrv, Disk *pDisk)
+ : MusicBase(pDisk) {
+
+ _driverFileBase = 60200;
+ _midiDrv = pMidiDrv;
+ int midiRes = _midiDrv->open();
+ if (midiRes != 0)
+ error("Can't open midi device. Errorcode: %d",midiRes);
+ _timerCount = 0;
+ _midiDrv->setTimerCallback(this, passTimerFunc);
+}
+
+MT32Music::~MT32Music(void) {
+
+ _midiDrv->close();
+ _midiDrv->setTimerCallback(NULL, NULL);
+ delete _midiDrv;
+}
+
+void MT32Music::timerCall(void) {
+ _timerCount += _midiDrv->getBaseTempo();
+ if (_timerCount > (1000000 / 50)) {
+ // call pollMusic() 50 times per second
+ _timerCount -= 1000000 / 50;
+ if (_musicData != NULL)
+ pollMusic();
+ }
+}
+
+void MT32Music::setVolume(uint8 volume) {
+ uint8 sysEx[10] = "\x41\x10\x16\x12\x10\x00\x16\x00\x00";
+ _musicVolume = volume;
+ sysEx[7] = (volume > 100) ? 100 : volume;
+ sysEx[8] = 0x00;
+ for (uint8 cnt = 4; cnt < 8; cnt++)
+ sysEx[8] -= sysEx[cnt];
+ sysEx[8] &= 0x7F;
+ _midiDrv->sysEx(sysEx, 9);
+}
+
+void MT32Music::setupPointers(void) {
+
+ _musicDataLoc = (_musicData[0x7DD] << 8) | _musicData[0x7DC];
+ _sysExSequence = ((_musicData[0x7E1] << 8) | _musicData[0x7E0]) + _musicData;
+}
+
+void MT32Music::setupChannels(uint8 *channelData) {
+
+ _numberOfChannels = channelData[0];
+ channelData++;
+ for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
+ uint16 chDataStart = ((channelData[(cnt << 1) | 1] << 8) | channelData[cnt << 1]) + _musicDataLoc;
+ _channels[cnt] = new GmChannel(_musicData, chDataStart, _midiDrv, NULL, NULL);
+ _channels[cnt]->updateVolume(_musicVolume);
+ }
+}
+
+bool MT32Music::processPatchSysEx(uint8 *sysExData) {
+
+ uint8 sysExBuf[15];
+ uint8 crc = 0;
+ if (sysExData[0] & 0x80)
+ return false;
+
+ // decompress data from stream
+ sysExBuf[0] = 0x41; sysExBuf[1] = 0x10; sysExBuf[2] = 0x16; sysExBuf[3] = 0x12; sysExBuf[4] = 0x5;
+ sysExBuf[5] = sysExData[0] >> 4; // patch offset part 1
+ sysExBuf[6] = (sysExData[0] & 0xF) << 3; // patch offset part 2
+ sysExBuf[7] = sysExData[1] >> 6; // timbre group
+ sysExBuf[8] = sysExData[1] & 0x3F; // timbre num
+ sysExBuf[9] = sysExData[2] & 0x3F; // key shift
+ sysExBuf[10] = sysExData[3] & 0x7F; // fine tune
+ sysExBuf[11] = sysExData[4] & 0x7F; // bender range
+ sysExBuf[12] = sysExData[2] >> 6; // assign mode
+ sysExBuf[13] = sysExData[3] >> 7; // reverb switch
+ for (uint8 cnt = 4; cnt < 14; cnt++)
+ crc -= sysExBuf[cnt];
+ sysExBuf[14] = crc & 0x7F; // crc
+ _midiDrv->sysEx(sysExBuf, 15);
+ g_system->delayMillis(5);
+ return true;
+}
+
+void MT32Music::startDriver(void) {
+
+ // setup timbres and patches using SysEx data
+ uint8* sysExData = _sysExSequence;
+ uint8 timbreNum = sysExData[0];
+ uint8 cnt, crc;
+ sysExData++;
+ uint8 sendBuf[256];
+ uint8 len;
+ sendBuf[0] = 0x41; sendBuf[1] = 0x10; sendBuf[2] = 0x16; sendBuf[3] = 0x12;
+ for (cnt = 0; cnt < timbreNum; cnt++) {
+ len = 7;
+ crc = 0;
+ // Timbre address
+ sendBuf[4] = 0x8 | (sysExData[0] >> 6);
+ sendBuf[5] = (sysExData[0] & 0x3F) << 1;
+ sendBuf[6] = 0xA;
+ sysExData++;
+ crc -= sendBuf[4] + sendBuf[5] + sendBuf[6];
+ uint8 dataLen = sysExData[0];
+ sysExData++;
+ // Timbre data:
+ do {
+ uint8 rlVal = 1;
+ uint8 codeVal = sysExData[0];
+ sysExData++;
+
+ if (codeVal & 0x80) {
+ codeVal &= 0x7F;
+ rlVal = sysExData[0];
+ sysExData++;
+ dataLen--;
+ }
+ for (uint8 cnt2 = 0; cnt2 < rlVal; cnt2++) {
+ sendBuf[len] = codeVal;
+ len++;
+ crc -= codeVal;
+ }
+ dataLen--;
+ } while (dataLen > 0);
+ sendBuf[len] = crc & 0x7F;
+ len++;
+ _midiDrv->sysEx(sendBuf, len);
+ g_system->delayMillis (5);
+ }
+
+ while (processPatchSysEx(sysExData))
+ sysExData += 5;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/music/mt32music.h b/engines/sky/music/mt32music.h
new file mode 100644
index 0000000000..a64663a15f
--- /dev/null
+++ b/engines/sky/music/mt32music.h
@@ -0,0 +1,53 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef MT32MUSIC_H
+#define MT32MUSIC_H
+
+#include "sky/music/musicbase.h"
+
+class MidiDriver;
+
+namespace Sky {
+
+class MT32Music : public MusicBase {
+public:
+ MT32Music(MidiDriver *pMidiDrv, Disk *pDisk);
+ ~MT32Music(void);
+private:
+ static void passTimerFunc(void *param);
+ void timerCall(void);
+ bool processPatchSysEx(uint8 *sysExData);
+ virtual void setVolume(uint8 volume);
+
+ uint32 _timerCount;
+ uint8 *_sysExSequence;
+ MidiDriver *_midiDrv;
+
+ virtual void setupPointers(void);
+ virtual void setupChannels(uint8 *channelData);
+ virtual void startDriver(void);
+};
+
+} // End of namespace Sky
+
+#endif //MT32MUSIC_H
diff --git a/engines/sky/music/musicbase.cpp b/engines/sky/music/musicbase.cpp
new file mode 100644
index 0000000000..9d308ebc7f
--- /dev/null
+++ b/engines/sky/music/musicbase.cpp
@@ -0,0 +1,148 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sky/music/musicbase.h"
+#include "sky/disk.h"
+#include "common/util.h"
+
+namespace Sky {
+
+MusicBase::MusicBase(Disk *pDisk) {
+
+ _musicData = NULL;
+ _allowedCommands = 0;
+ _skyDisk = pDisk;
+ _currentMusic = 0;
+ _musicVolume = 127;
+ _numberOfChannels = _currentMusic = 0;
+}
+
+MusicBase::~MusicBase(void) {
+
+ stopMusic();
+ if (_musicData)
+ free(_musicData);
+}
+
+void MusicBase::loadSection(uint8 pSection) {
+
+ _mutex.lock();
+ if (_currentMusic)
+ stopMusic();
+ if (_musicData)
+ free(_musicData);
+ _currentSection = pSection;
+ _musicData = _skyDisk->loadFile(_driverFileBase + FILES_PER_SECTION * pSection);
+ _allowedCommands = 0;
+ _musicTempo0 = 0x78; // init constants taken from idb file, area ~0x1060
+ _musicTempo1 = 0xC0;
+ _onNextPoll.doReInit = false;
+ _onNextPoll.doStopMusic = false;
+ _onNextPoll.musicToProcess = 0;
+ _tempo = _aktTime = 0x10001;
+ _numberOfChannels = _currentMusic = 0;
+ setupPointers();
+ startDriver();
+ _mutex.unlock();
+}
+
+bool MusicBase::musicIsPlaying(void) {
+
+ for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
+ if (_channels[cnt]->isActive())
+ return true;
+ return false;
+}
+
+void MusicBase::setVolume(uint16 param) {
+
+ _musicVolume = param;
+ for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
+ _channels[cnt]->updateVolume(_musicVolume);
+}
+
+void MusicBase::stopMusic(void) {
+
+ for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
+ _channels[cnt]->stopNote();
+ delete _channels[cnt];
+ }
+ _numberOfChannels = 0;
+}
+
+void MusicBase::updateTempo(void) {
+
+ uint16 tempoMul = _musicTempo0 * _musicTempo1;
+ uint16 divisor = 0x4446390/ 23864;
+ _tempo = (tempoMul / divisor) << 16;
+ _tempo |= (((tempoMul%divisor) << 16) | (tempoMul / divisor)) / divisor;
+}
+
+void MusicBase::loadNewMusic(void) {
+
+ uint16 musicPos;
+ if (_onNextPoll.musicToProcess > _musicData[_musicDataLoc]) {
+ error("Music %d requested but doesn't exist in file.", _onNextPoll.musicToProcess);
+ return;
+ }
+ if (_currentMusic != 0)
+ stopMusic();
+
+ _currentMusic = _onNextPoll.musicToProcess;
+
+ if (_currentMusic != 0) {
+ musicPos = (_musicData[_musicDataLoc + 2] << 8) | _musicData[_musicDataLoc+1];
+ musicPos += _musicDataLoc+((_currentMusic-1) << 1);
+ musicPos = ((_musicData[musicPos+1] << 8) | _musicData[musicPos]) + _musicDataLoc;
+
+ _musicTempo0 = _musicData[musicPos];
+ _musicTempo1 = _musicData[musicPos+1];
+
+ setupChannels(_musicData + musicPos + 2);
+
+ updateTempo();
+ }
+}
+
+void MusicBase::pollMusic(void) {
+
+ _mutex.lock();
+ uint8 newTempo;
+ if (_onNextPoll.doReInit) startDriver();
+ if (_onNextPoll.doStopMusic) stopMusic();
+ if (_onNextPoll.musicToProcess != _currentMusic)
+ loadNewMusic();
+
+ _aktTime += _tempo;
+
+ for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
+ newTempo = _channels[cnt]->process((uint16)(_aktTime >> 16));
+ if (newTempo) {
+ _musicTempo1 = newTempo;
+ updateTempo();
+ }
+ }
+ _mutex.unlock();
+ _aktTime &= 0xFFFF;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/music/musicbase.h b/engines/sky/music/musicbase.h
new file mode 100644
index 0000000000..d9f3e22beb
--- /dev/null
+++ b/engines/sky/music/musicbase.h
@@ -0,0 +1,92 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef MUSICBASE_H
+#define MUSICBASE_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/mutex.h"
+
+namespace Sky {
+
+class Disk;
+
+#define FILES_PER_SECTION 4
+
+typedef struct {
+ bool doReInit, doStopMusic;
+ uint8 musicToProcess;
+} Actions;
+
+class ChannelBase {
+public:
+ virtual ~ChannelBase() {};
+ virtual void stopNote(void) = 0;
+ virtual uint8 process(uint16 aktTime) = 0;
+ virtual void updateVolume(uint16 pVolume) = 0;
+ virtual bool isActive(void) = 0;
+private:
+};
+
+class MusicBase {
+public:
+ MusicBase(Disk *pDisk);
+ virtual ~MusicBase(void);
+ void loadSection(uint8 pSection);
+ void startMusic(uint16 param) { _onNextPoll.musicToProcess = param & 0xF; }; // 4
+ void stopMusic(); // 7
+ bool musicIsPlaying(void);
+ uint8 giveVolume(void) { return (uint8)_musicVolume; };
+ uint8 giveCurrentMusic(void) { return _currentMusic; };
+ void setVolume(uint16 param);
+
+protected:
+
+ Disk *_skyDisk;
+ uint8 *_musicData;
+ uint8 _allowedCommands;
+ uint16 _musicDataLoc;
+ uint16 _driverFileBase;
+
+ uint16 _musicVolume, _numberOfChannels;
+ uint8 _currentMusic, _currentSection;
+ uint8 _musicTempo0; // can be changed by music stream
+ uint8 _musicTempo1; // given once per music
+ uint32 _tempo; // calculated from musicTempo0 and musicTempo1
+ uint32 _aktTime;
+ Actions _onNextPoll;
+ ChannelBase *_channels[10];
+ Common::Mutex _mutex;
+
+ virtual void setupPointers(void) = 0;
+ virtual void setupChannels(uint8 *channelData) = 0;
+ virtual void startDriver(void) = 0;
+
+ void updateTempo(void);
+ void loadNewMusic(void);
+ void pollMusic(void);
+};
+
+} // End of namespace Sky
+
+#endif //MUSICBASE_H
diff --git a/engines/sky/rnc_deco.cpp b/engines/sky/rnc_deco.cpp
new file mode 100644
index 0000000000..06b7721d74
--- /dev/null
+++ b/engines/sky/rnc_deco.cpp
@@ -0,0 +1,262 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "sky/rnc_deco.h"
+
+namespace Sky {
+
+//return codes
+#define NOT_PACKED 0
+#define PACKED_CRC -1
+#define UNPACKED_CRC -2
+
+//other defines
+#define TABLE_SIZE (16 * 8)
+#define MIN_LENGTH 2
+#define HEADER_LEN 18
+
+RncDecoder::RncDecoder() {
+ initCrc();
+}
+
+RncDecoder::~RncDecoder() { }
+
+void RncDecoder::initCrc() {
+ uint16 cnt = 0;
+ uint16 tmp1 = 0;
+ uint16 tmp2 = 0;
+
+ for (tmp2 = 0; tmp2 < 0x100; tmp2++) {
+ tmp1 = tmp2;
+ for (cnt = 8; cnt > 0; cnt--) {
+ if (tmp1 % 2) {
+ tmp1 >>= 1;
+ tmp1 ^= 0x0a001;
+ } else
+ tmp1 >>= 1;
+ }
+ _crcTable[tmp2] = tmp1;
+ }
+}
+
+//calculate 16 bit crc of a block of memory
+uint16 RncDecoder::crcBlock(const uint8 *block, uint32 size) {
+ uint16 crc = 0;
+ uint8 *crcTable8 = (uint8 *)_crcTable; //make a uint8* to crc_table
+ uint8 tmp;
+ uint32 i;
+
+ for (i = 0; i < size; i++) {
+ tmp = *block++;
+ crc ^= tmp;
+ tmp = (uint8)((crc >> 8) & 0x00FF);
+ crc &= 0x00FF;
+ crc = *(uint16 *)&crcTable8[crc << 1];
+ crc ^= tmp;
+ }
+
+ return crc;
+}
+
+uint16 RncDecoder::inputBits(uint8 amount) {
+ uint16 newBitBuffh = _bitBuffh;
+ uint16 newBitBuffl = _bitBuffl;
+ int16 newBitCount = _bitCount;
+ uint16 remBits, returnVal;
+
+ returnVal = ((1 << amount) - 1) & newBitBuffl;
+ newBitCount -= amount;
+
+ if (newBitCount < 0) {
+ newBitCount += amount;
+ remBits = (newBitBuffh << (16 - newBitCount));
+ newBitBuffh >>= newBitCount;
+ newBitBuffl >>= newBitCount;
+ newBitBuffl |= remBits;
+ _srcPtr += 2;
+ newBitBuffh = READ_LE_UINT16(_srcPtr);
+ amount -= newBitCount;
+ newBitCount = 16 - amount;
+ }
+ remBits = (newBitBuffh << (16 - amount));
+ _bitBuffh = newBitBuffh >> amount;
+ _bitBuffl = (newBitBuffl >> amount) | remBits;
+ _bitCount = (uint8)newBitCount;
+
+ return returnVal;
+}
+
+void RncDecoder::makeHufftable(uint16 *table) {
+ uint16 bitLength, i, j;
+ uint16 numCodes = inputBits(5);
+
+ if (!numCodes)
+ return;
+
+ uint8 huffLength[16];
+ for (i = 0; i < numCodes; i++)
+ huffLength[i] = (uint8)(inputBits(4) & 0x00FF);
+
+ uint16 huffCode = 0;
+
+ for (bitLength = 1; bitLength < 17; bitLength++) {
+ for (i = 0; i < numCodes; i++) {
+ if (huffLength[i] == bitLength) {
+ *table++ = (1 << bitLength) - 1;
+
+ uint16 b = huffCode >> (16 - bitLength);
+ uint16 a = 0;
+
+ for (j = 0; j < bitLength; j++)
+ a |= ((b >> j) & 1) << (bitLength - j - 1);
+ *table++ = a;
+
+ *(table + 0x1e) = (huffLength[i] << 8) | (i & 0x00FF);
+ huffCode += 1 << (16 - bitLength);
+ }
+ }
+ }
+}
+
+uint16 RncDecoder::inputValue(uint16 *table) {
+ uint16 valOne, valTwo, value = _bitBuffl;
+
+ do {
+ valTwo = (*table++) & value;
+ valOne = *table++;
+
+ } while (valOne != valTwo);
+
+ value = *(table + 0x1e);
+ inputBits((uint8)((value>>8) & 0x00FF));
+ value &= 0x00FF;
+
+ if (value >= 2) {
+ value--;
+ valOne = inputBits((uint8)value & 0x00FF);
+ valOne |= (1 << value);
+ value = valOne;
+ }
+
+ return value;
+}
+
+int32 RncDecoder::unpackM1(const void *input, void *output, uint16 key) {
+ uint8 *outputLow, *outputHigh;
+ const uint8 *inputHigh, *inputptr = (const uint8 *)input;
+
+ uint32 unpackLen = 0;
+ uint32 packLen = 0;
+ uint16 counts = 0;
+ uint16 crcUnpacked = 0;
+ uint16 crcPacked = 0;
+
+
+ _bitBuffl = 0;
+ _bitBuffh = 0;
+ _bitCount = 0;
+
+ //Check for "RNC "
+ if (READ_BE_UINT32(inputptr) != RNC_SIGNATURE)
+ return NOT_PACKED;
+
+ inputptr += 4;
+
+ // read unpacked/packed file length
+ unpackLen = READ_BE_UINT32(inputptr); inputptr += 4;
+ packLen = READ_BE_UINT32(inputptr); inputptr += 4;
+
+ uint8 blocks = *(inputptr + 5);
+
+ //read CRC's
+ crcUnpacked = READ_BE_UINT16(inputptr); inputptr += 2;
+ crcPacked = READ_BE_UINT16(inputptr); inputptr += 2;
+ inputptr = (inputptr + HEADER_LEN - 16);
+
+ if (crcBlock(inputptr, packLen) != crcPacked)
+ return PACKED_CRC;
+
+ inputptr = (((const uint8 *)input) + HEADER_LEN);
+ _srcPtr = inputptr;
+
+ inputHigh = ((const uint8 *)input) + packLen + HEADER_LEN;;
+ outputLow = (uint8 *)output;
+ outputHigh = *(((const uint8 *)input) + 16) + unpackLen + outputLow;
+
+ if (! ((inputHigh <= outputLow) || (outputHigh <= inputHigh)) ) {
+ _srcPtr = inputHigh;
+ _dstPtr = outputHigh;
+ memcpy((_dstPtr-packLen), (_srcPtr-packLen), packLen);
+ _srcPtr = (_dstPtr-packLen);
+ }
+
+ _dstPtr = (uint8 *)output;
+ _bitCount = 0;
+
+ _bitBuffl = READ_LE_UINT16(_srcPtr);
+ inputBits(2);
+
+ do {
+ makeHufftable(_rawTable);
+ makeHufftable(_posTable);
+ makeHufftable(_lenTable);
+
+ counts = inputBits(16);
+
+ do {
+ uint32 inputLength = inputValue(_rawTable);
+ uint32 inputOffset;
+
+ if (inputLength) {
+ memcpy(_dstPtr, _srcPtr, inputLength); //memcpy is allowed here
+ _dstPtr += inputLength;
+ _srcPtr += inputLength;
+ uint16 a = READ_LE_UINT16(_srcPtr);
+ uint16 b = READ_LE_UINT16(_srcPtr + 2);
+
+ _bitBuffl &= ((1 << _bitCount) - 1);
+ _bitBuffl |= (a << _bitCount);
+ _bitBuffh = (a >> (16 - _bitCount)) | (b << _bitCount);
+ }
+
+ if (counts > 1) {
+ inputOffset = inputValue(_posTable) + 1;
+ inputLength = inputValue(_lenTable) + MIN_LENGTH;
+
+ // Don't use memcpy here! because input and output overlap.
+ uint8 *tmpPtr = (_dstPtr-inputOffset);
+ while (inputLength--)
+ *_dstPtr++ = *tmpPtr++;
+ }
+ } while (--counts);
+ } while (--blocks);
+
+ if (crcBlock((uint8 *)output, unpackLen) != crcUnpacked)
+ return UNPACKED_CRC;
+
+ // all is done..return the amount of unpacked bytes
+ return unpackLen;
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/rnc_deco.h b/engines/sky/rnc_deco.h
new file mode 100644
index 0000000000..1a05d95531
--- /dev/null
+++ b/engines/sky/rnc_deco.h
@@ -0,0 +1,63 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef RNC_DECO_H
+#define RNC_DECO_H
+
+#include "common/stdafx.h"
+
+#define RNC_SIGNATURE 0x524E4301 // "RNC\001"
+
+namespace Sky {
+
+class RncDecoder {
+
+protected:
+ uint16 _rawTable[64];
+ uint16 _posTable[64];
+ uint16 _lenTable[64];
+ uint16 _crcTable[256];
+
+ uint16 _bitBuffl;
+ uint16 _bitBuffh;
+ uint8 _bitCount;
+
+ const uint8 *_srcPtr;
+ uint8 *_dstPtr;
+
+public:
+ RncDecoder();
+ ~RncDecoder();
+ int32 unpackM1(const void *input, void *output, uint16 key);
+
+protected:
+ void initCrc();
+ uint16 crcBlock(const uint8 *block, uint32 size);
+ uint16 inputBits(uint8 amount);
+ void makeHufftable(uint16 *table);
+ uint16 inputValue(uint16 *table);
+
+};
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sky/screen.cpp b/engines/sky/screen.cpp
new file mode 100644
index 0000000000..8688969de5
--- /dev/null
+++ b/engines/sky/screen.cpp
@@ -0,0 +1,822 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "sky/disk.h"
+#include "sky/logic.h"
+#include "sky/screen.h"
+#include "sky/compact.h"
+#include "sky/sky.h"
+#include "sky/skydefs.h"
+#include "sky/struc.h"
+
+namespace Sky {
+
+uint8 Screen::_top16Colours[16*3] = {
+ 0, 0, 0,
+ 38, 38, 38,
+ 63, 63, 63,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 54, 54, 54,
+ 45, 47, 49,
+ 32, 31, 41,
+ 29, 23, 37,
+ 23, 18, 30,
+ 49, 11, 11,
+ 39, 5, 5,
+ 29, 1, 1,
+ 63, 63, 63
+};
+
+Screen::Screen(OSystem *pSystem, Disk *pDisk, SkyCompact *skyCompact) {
+
+ _system = pSystem;
+ _skyDisk = pDisk;
+ _skyCompact = skyCompact;
+
+ int i;
+ uint8 tmpPal[1024];
+
+ _system->initSize(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+ _gameGrid = (uint8 *)malloc(GRID_X * GRID_Y * 2);
+ forceRefresh();
+
+ _currentScreen = NULL;
+ _scrollScreen = NULL;
+
+ //blank the first 240 colors of the palette
+ memset(tmpPal, 0, GAME_COLOURS * 4);
+
+ //set the remaining colors
+ for (i = 0; i < (VGA_COLOURS-GAME_COLOURS); i++) {
+ tmpPal[4 * GAME_COLOURS + i * 4] = (_top16Colours[i * 3] << 2) + (_top16Colours[i * 3] >> 4);
+ tmpPal[4 * GAME_COLOURS + i * 4 + 1] = (_top16Colours[i * 3 + 1] << 2) + (_top16Colours[i * 3 + 1] >> 4);
+ tmpPal[4 * GAME_COLOURS + i * 4 + 2] = (_top16Colours[i * 3 + 2] << 2) + (_top16Colours[i * 3 + 2] >> 4);
+ tmpPal[4 * GAME_COLOURS + i * 4 + 3] = 0x00;
+ }
+
+ //set the palette
+ _system->setPalette(tmpPal, 0, VGA_COLOURS);
+ _currentPalette = 0;
+
+ _seqInfo.framesLeft = 0;
+ _seqInfo.seqData = _seqInfo.seqDataPos = NULL;
+ _seqInfo.running = false;
+}
+
+Screen::~Screen(void) {
+
+ free(_gameGrid);
+ if (_currentScreen)
+ free(_currentScreen);
+ if (_scrollScreen)
+ free(_scrollScreen);
+}
+
+void Screen::clearScreen(void) {
+
+ memset(_currentScreen, 0, FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+ _system->copyRectToScreen(_currentScreen, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
+ _system->updateScreen();
+}
+
+//set a new palette, pal is a pointer to dos vga rgb components 0..63
+void Screen::setPalette(uint8 *pal) {
+
+ convertPalette(pal, _palette);
+ _system->setPalette(_palette, 0, GAME_COLOURS);
+ _system->updateScreen();
+}
+
+void Screen::setPaletteEndian(uint8 *pal) {
+
+#ifdef SCUMM_BIG_ENDIAN
+ uint8 endPalette[256 * 3];
+ for (uint16 cnt = 0; cnt < 256 * 3; cnt++)
+ endPalette[cnt] = pal[cnt ^ 1];
+ convertPalette(endPalette, _palette);
+#else
+ convertPalette(pal, _palette);
+#endif
+ _system->setPalette(_palette, 0, GAME_COLOURS);
+ _system->updateScreen();
+}
+
+void Screen::halvePalette(void) {
+
+ uint8 halfPalette[1024];
+ for (uint8 cnt = 0; cnt < GAME_COLOURS; cnt++) {
+ halfPalette[(cnt << 2) | 0] = _palette[(cnt << 2) | 0] >> 1;
+ halfPalette[(cnt << 2) | 1] = _palette[(cnt << 2) | 1] >> 1;
+ halfPalette[(cnt << 2) | 2] = _palette[(cnt << 2) | 2] >> 1;
+ halfPalette[(cnt << 2) | 3] = 0;
+ }
+ _system->setPalette(halfPalette, 0, GAME_COLOURS);
+}
+
+void Screen::setPalette(uint16 fileNum) {
+
+ uint8 *tmpPal = _skyDisk->loadFile(fileNum);
+ if (tmpPal) {
+ setPalette(tmpPal);
+ free(tmpPal);
+ } else
+ warning("Screen::setPalette: can't load file nr. %d",fileNum);
+}
+
+void Screen::showScreen(uint16 fileNum) {
+
+ if (_currentScreen)
+ free(_currentScreen);
+ _currentScreen = _skyDisk->loadFile(fileNum);
+
+ if (_currentScreen)
+ showScreen(_currentScreen);
+ else
+ warning("Screen::showScreen: can't load file nr. %d",fileNum);
+}
+
+void Screen::showScreen(uint8 *pScreen) {
+
+ _system->copyRectToScreen(pScreen, 320, 0, 0, GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
+ _system->updateScreen();
+}
+
+void Screen::convertPalette(uint8 *inPal, uint8* outPal) { //convert 3 byte 0..63 rgb to 4byte 0..255 rgbx
+
+ int i;
+
+ for (i = 0; i < VGA_COLOURS; i++) {
+ outPal[4 * i] = (inPal[3 * i] << 2) + (inPal[3 * i] >> 4);
+ outPal[4 * i + 1] = (inPal[3 * i + 1] << 2) + (inPal[3 * i + 1] >> 4);
+ outPal[4 * i + 2] = (inPal[3 * i + 2] << 2) + (inPal[3 * i + 2] >> 4);
+ outPal[4 * i + 3] = 0x00;
+ }
+}
+
+void Screen::recreate(void) {
+
+ // check the game grid for changed blocks
+ if (!Logic::_scriptVariables[LAYER_0_ID])
+ return;
+ uint8 *gridPos = _gameGrid;
+ uint8 *screenData = (uint8 *)SkyEngine::fetchItem(Logic::_scriptVariables[LAYER_0_ID]);
+ if (!screenData) {
+ error("Screen::recreate():\nSkyEngine::fetchItem(Logic::_scriptVariables[LAYER_0_ID](%X)) returned NULL", Logic::_scriptVariables[LAYER_0_ID]);
+ }
+ uint8 *screenPos = _currentScreen;
+
+ for (uint8 cnty = 0; cnty < GRID_Y; cnty++) {
+ for (uint8 cntx = 0; cntx < GRID_X; cntx++) {
+ if (gridPos[0] & 0x80) {
+ gridPos[0] &= 0x7F; // reset recreate flag
+ gridPos[0] |= 1; // set bit for flip routine
+ uint8 *savedScreenY = screenPos;
+ for (uint8 gridCntY = 0; gridCntY < GRID_H; gridCntY++) {
+ memcpy(screenPos, screenData, GRID_W);
+ screenPos += GAME_SCREEN_WIDTH;
+ screenData += GRID_W;
+ }
+ screenPos = savedScreenY + GRID_W;
+ } else {
+ screenPos += GRID_W;
+ screenData += GRID_W * GRID_H;
+ }
+ gridPos++;
+ }
+ screenPos += (GRID_H - 1) * GAME_SCREEN_WIDTH;
+ }
+}
+
+void Screen::flip(bool doUpdate) {
+
+ uint32 copyX, copyWidth;
+ copyX = copyWidth = 0;
+ for (uint8 cnty = 0; cnty < GRID_Y; cnty++) {
+ for (uint8 cntx = 0; cntx < GRID_X; cntx++) {
+ if (_gameGrid[cnty * GRID_X + cntx] & 1) {
+ _gameGrid[cnty * GRID_X + cntx] &= ~1;
+ if (!copyWidth)
+ copyX = cntx * GRID_W;
+ copyWidth += GRID_W;
+ } else if (copyWidth) {
+ _system->copyRectToScreen(_currentScreen + cnty * GRID_H * GAME_SCREEN_WIDTH + copyX, GAME_SCREEN_WIDTH, copyX, cnty * GRID_H, copyWidth, GRID_H);
+ copyWidth = 0;
+ }
+ }
+ if (copyWidth) {
+ _system->copyRectToScreen(_currentScreen + cnty * GRID_H * GAME_SCREEN_WIDTH + copyX, GAME_SCREEN_WIDTH, copyX, cnty * GRID_H, copyWidth, GRID_H);
+ copyWidth = 0;
+ }
+ }
+ if (doUpdate)
+ _system->updateScreen();
+}
+
+void Screen::fnDrawScreen(uint32 palette, uint32 scroll) {
+
+ // set up the new screen
+ fnFadeDown(scroll);
+ forceRefresh();
+ recreate();
+ spriteEngine();
+ flip(false);
+ fnFadeUp(palette, scroll);
+}
+
+void Screen::fnFadeDown(uint32 scroll) {
+
+ if (((scroll != 123) && (scroll != 321)) || (SkyEngine::_systemVars.systemFlags & SF_NO_SCROLL)) {
+ uint32 delayTime = _system->getMillis();
+ for (uint8 cnt = 0; cnt < 32; cnt++) {
+ delayTime += 20;
+ palette_fadedown_helper((uint32 *)_palette, GAME_COLOURS);
+ _system->setPalette(_palette, 0, GAME_COLOURS);
+ _system->updateScreen();
+ int32 waitTime = (int32)delayTime - _system->getMillis();
+ if (waitTime < 0)
+ waitTime = 0;
+ _system->delayMillis((uint)waitTime);
+ }
+ } else {
+ // scrolling is performed by fnFadeUp. It's just prepared here
+ _scrollScreen = _currentScreen;
+ _currentScreen = (uint8 *)malloc(FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
+ // the game will draw the new room into _currentScreen which
+ // will be scrolled into the visible screen by fnFadeUp
+ // fnFadeUp also frees the _scrollScreen
+ }
+}
+
+void Screen::palette_fadedown_helper(uint32 *pal, uint num) {
+ byte *p = (byte *)pal;
+
+ do {
+ if (p[0] >= 8)
+ p[0] -= 8;
+ else
+ p[0] = 0;
+ if (p[1] >= 8)
+ p[1] -= 8;
+ else
+ p[1] = 0;
+ if (p[2] >= 8)
+ p[2] -= 8;
+ else
+ p[2] = 0;
+ p += sizeof(uint32);
+ } while (--num);
+}
+
+void Screen::paletteFadeUp(uint16 fileNr) {
+
+ uint8 *pal = _skyDisk->loadFile(fileNr);
+ if (pal) {
+ paletteFadeUp(pal);
+ free(pal);
+ } else
+ warning("Screen::paletteFadeUp: Can't load palette #%d",fileNr);
+}
+
+void Screen::paletteFadeUp(uint8 *pal) {
+
+ byte tmpPal[1024];
+
+ convertPalette(pal, tmpPal);
+
+ uint32 delayTime = _system->getMillis();
+ for (uint8 cnt = 1; cnt <= 32; cnt++) {
+ delayTime += 20;
+ for (uint8 colCnt = 0; colCnt < GAME_COLOURS; colCnt++) {
+ _palette[(colCnt << 2) | 0] = (tmpPal[(colCnt << 2) | 0] * cnt) >> 5;
+ _palette[(colCnt << 2) | 1] = (tmpPal[(colCnt << 2) | 1] * cnt) >> 5;
+ _palette[(colCnt << 2) | 2] = (tmpPal[(colCnt << 2) | 2] * cnt) >> 5;
+ }
+ _system->setPalette(_palette, 0, GAME_COLOURS);
+ _system->updateScreen();
+ int32 waitTime = (int32)delayTime - _system->getMillis();
+ if (waitTime < 0)
+ waitTime = 0;
+ _system->delayMillis((uint)waitTime);
+ }
+}
+
+void Screen::fnFadeUp(uint32 palNum, uint32 scroll) {
+
+ //_currentScreen points to new screen,
+ //_scrollScreen points to graphic showing old room
+ if ((scroll != 123) && (scroll != 321))
+ scroll = 0;
+
+ if ((scroll == 0) || (SkyEngine::_systemVars.systemFlags & SF_NO_SCROLL)) {
+ uint8 *palette = (uint8 *)_skyCompact->fetchCpt(palNum);
+ if (palette == NULL)
+ error("Screen::fnFadeUp: can't fetch compact %X", palNum);
+#ifdef SCUMM_BIG_ENDIAN
+ byte tmpPal[256 * 3];
+ for (uint16 cnt = 0; cnt < 256*3; cnt++)
+ tmpPal[cnt] = palette[cnt ^ 1];
+ paletteFadeUp(tmpPal);
+#else
+ paletteFadeUp(palette);
+#endif
+ } else if (scroll == 123) { // scroll left (going right)
+ assert(_currentScreen && _scrollScreen);
+ uint8 *scrNewPtr, *scrOldPtr;
+ for (uint8 scrollCnt = 0; scrollCnt < (GAME_SCREEN_WIDTH / SCROLL_JUMP) - 1; scrollCnt++) {
+ scrNewPtr = _currentScreen + scrollCnt * SCROLL_JUMP;
+ scrOldPtr = _scrollScreen;
+ for (uint8 lineCnt = 0; lineCnt < GAME_SCREEN_HEIGHT; lineCnt++) {
+ memmove(scrOldPtr, scrOldPtr + SCROLL_JUMP, GAME_SCREEN_WIDTH - SCROLL_JUMP);
+ memcpy(scrOldPtr + GAME_SCREEN_WIDTH - SCROLL_JUMP, scrNewPtr, SCROLL_JUMP);
+ scrNewPtr += GAME_SCREEN_WIDTH;
+ scrOldPtr += GAME_SCREEN_WIDTH;
+ }
+ showScreen(_scrollScreen);
+ waitForTimer();
+ }
+ showScreen(_currentScreen);
+ } else if (scroll == 321) { // scroll right (going left)
+ assert(_currentScreen && _scrollScreen);
+ uint8 *scrNewPtr, *scrOldPtr;
+ for (uint8 scrollCnt = 0; scrollCnt < (GAME_SCREEN_WIDTH / SCROLL_JUMP) - 1; scrollCnt++) {
+ scrNewPtr = _currentScreen + GAME_SCREEN_WIDTH - (scrollCnt + 1) * SCROLL_JUMP;
+ scrOldPtr = _scrollScreen;
+ for (uint8 lineCnt = 0; lineCnt < GAME_SCREEN_HEIGHT; lineCnt++) {
+ memmove(scrOldPtr + SCROLL_JUMP, scrOldPtr, GAME_SCREEN_WIDTH - SCROLL_JUMP);
+ memcpy(scrOldPtr, scrNewPtr, SCROLL_JUMP);
+ scrNewPtr += GAME_SCREEN_WIDTH;
+ scrOldPtr += GAME_SCREEN_WIDTH;
+ }
+ showScreen(_scrollScreen);
+ waitForTimer();
+ }
+ showScreen(_currentScreen);
+ }
+ if (_scrollScreen) {
+ free(_scrollScreen);
+ _scrollScreen = NULL;
+ }
+}
+
+void Screen::waitForTimer(void) {
+
+ _gotTick = false;
+ while (!_gotTick) {
+ OSystem::Event event;
+
+ _system->delayMillis(10);
+ while (_system->pollEvent(event));
+ }
+}
+
+void Screen::waitForSequence(void) {
+ while (_seqInfo.running) {
+ OSystem::Event event;
+
+ _system->delayMillis(20);
+ while (_system->pollEvent(event));
+ }
+}
+
+void Screen::handleTimer(void) {
+
+ _gotTick = true;
+ if (_seqInfo.running)
+ processSequence();
+}
+
+void Screen::startSequence(uint16 fileNum) {
+
+ _seqInfo.seqData = _skyDisk->loadFile(fileNum);
+ _seqInfo.framesLeft = _seqInfo.seqData[0];
+ _seqInfo.seqDataPos = _seqInfo.seqData + 1;
+ _seqInfo.delay = SEQ_DELAY;
+ _seqInfo.running = true;
+ _seqInfo.runningItem = false;
+}
+
+void Screen::startSequenceItem(uint16 itemNum) {
+
+ _seqInfo.seqData = (uint8 *)SkyEngine::fetchItem(itemNum);
+ _seqInfo.framesLeft = _seqInfo.seqData[0] - 1;
+ _seqInfo.seqDataPos = _seqInfo.seqData + 1;
+ _seqInfo.delay = SEQ_DELAY;
+ _seqInfo.running = true;
+ _seqInfo.runningItem = true;
+}
+
+void Screen::stopSequence() {
+
+ _seqInfo.running = false;
+ waitForTimer();
+ waitForTimer();
+ _seqInfo.framesLeft = 0;
+ free(_seqInfo.seqData);
+ _seqInfo.seqData = _seqInfo.seqDataPos = NULL;
+}
+
+void Screen::processSequence(void) {
+
+ uint32 screenPos = 0;
+
+ _seqInfo.delay--;
+ if (_seqInfo.delay == 0) {
+ _seqInfo.delay = SEQ_DELAY;
+ memset(_seqGrid, 0, 12 * 20);
+
+ uint8 nrToSkip, nrToDo, cnt;
+ do {
+ do {
+ nrToSkip = _seqInfo.seqDataPos[0];
+ _seqInfo.seqDataPos++;
+ screenPos += nrToSkip;
+ } while (nrToSkip == 0xFF);
+ do {
+ nrToDo = _seqInfo.seqDataPos[0];
+ _seqInfo.seqDataPos++;
+
+ uint8 gridSta = (uint8)((screenPos / (GAME_SCREEN_WIDTH * 16))*20 + ((screenPos % GAME_SCREEN_WIDTH) >> 4));
+ uint8 gridEnd = (uint8)(((screenPos+nrToDo) / (GAME_SCREEN_WIDTH * 16))*20 + (((screenPos+nrToDo) % GAME_SCREEN_WIDTH) >> 4));
+ gridSta = MIN(gridSta, (uint8)(12 * 20 - 1));
+ gridEnd = MIN(gridEnd, (uint8)(12 * 20 - 1));
+ if (gridEnd >= gridSta)
+ for (cnt = gridSta; cnt <= gridEnd; cnt++)
+ _seqGrid[cnt] = 1;
+ else {
+ for (cnt = gridSta; cnt < (gridSta / 20 + 1) * 20; cnt++)
+ _seqGrid[cnt] = 1;
+ for (cnt = (gridEnd / 20) * 20; cnt <= gridEnd; cnt++)
+ _seqGrid[cnt] = 1;
+ }
+
+ for (cnt = 0; cnt < nrToDo; cnt++) {
+ _currentScreen[screenPos] = _seqInfo.seqDataPos[0];
+ _seqInfo.seqDataPos++;
+ screenPos++;
+ }
+ } while (nrToDo == 0xFF);
+ } while (screenPos < (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT));
+ uint8 *gridPtr = _seqGrid; uint8 *scrPtr = _currentScreen; uint8 *rectPtr = NULL;
+ uint8 rectWid = 0, rectX = 0, rectY = 0;
+ for (uint8 cnty = 0; cnty < 12; cnty++) {
+ for (uint8 cntx = 0; cntx < 20; cntx++) {
+ if (*gridPtr) {
+ if (!rectWid) {
+ rectX = cntx;
+ rectY = cnty;
+ rectPtr = scrPtr;
+ }
+ rectWid++;
+ } else if (rectWid) {
+ _system->copyRectToScreen(rectPtr, GAME_SCREEN_WIDTH, rectX << 4, rectY << 4, rectWid << 4, 16);
+ rectWid = 0;
+ }
+ scrPtr += 16;
+ gridPtr++;
+ }
+ if (rectWid) {
+ _system->copyRectToScreen(rectPtr, GAME_SCREEN_WIDTH, rectX << 4, rectY << 4, rectWid << 4, 16);
+ rectWid = 0;
+ }
+ scrPtr += 15 * GAME_SCREEN_WIDTH;
+ }
+ _system->updateScreen();
+ _seqInfo.framesLeft--;
+ }
+ if (_seqInfo.framesLeft == 0) {
+ _seqInfo.running = false;
+ if (!_seqInfo.runningItem)
+ free(_seqInfo.seqData);
+ _seqInfo.seqData = _seqInfo.seqDataPos = NULL;
+ }
+}
+
+//- sprites.asm routines
+
+void Screen::spriteEngine(void) {
+
+ doSprites(BACK);
+ sortSprites();
+ doSprites(FORE);
+}
+
+void Screen::sortSprites(void) {
+
+ StSortList sortList[30];
+ uint32 currDrawList = DRAW_LIST_NO;
+ uint32 loadDrawList;
+
+ bool nextDrawList = false;
+ while (Logic::_scriptVariables[currDrawList]) {
+ // big_sort_loop
+ uint32 spriteCnt = 0;
+ loadDrawList = Logic::_scriptVariables[currDrawList];
+ currDrawList++;
+
+ do { // a_new_draw_list:
+ uint16 *drawListData = (uint16 *)_skyCompact->fetchCpt(loadDrawList);
+ nextDrawList = false;
+ while ((!nextDrawList) && (drawListData[0])) {
+ if (drawListData[0] == 0xFFFF) {
+ loadDrawList = drawListData[1];
+ nextDrawList = true;
+ } else {
+ // process_this_id:
+ Compact *spriteComp = _skyCompact->fetchCpt(drawListData[0]);
+ if ((spriteComp->status & 4) && // is it sortable playfield?(!?!)
+ (spriteComp->screen == Logic::_scriptVariables[SCREEN])) { // on current screen
+ dataFileHeader *spriteData =
+ (dataFileHeader *)SkyEngine::fetchItem(spriteComp->frame >> 6);
+ if (!spriteData) {
+ debug(9,"Missing file %d", spriteComp->frame >> 6);
+ spriteComp->status = 0;
+ } else {
+ sortList[spriteCnt].yCood = spriteComp->ycood + spriteData->s_offset_y + spriteData->s_height;
+ sortList[spriteCnt].compact = spriteComp;
+ sortList[spriteCnt].sprite = spriteData;
+ spriteCnt++;
+ }
+ }
+ drawListData++;
+ }
+ }
+ } while (nextDrawList);
+ // made_list:
+ if (spriteCnt > 1) { // bubble sort
+ for (uint32 cnt1 = 0; cnt1 < spriteCnt - 1; cnt1++)
+ for (uint32 cnt2 = cnt1 + 1; cnt2 < spriteCnt; cnt2++)
+ if (sortList[cnt1].yCood > sortList[cnt2].yCood) {
+ StSortList tmp;
+ tmp.yCood = sortList[cnt1].yCood;
+ tmp.sprite = sortList[cnt1].sprite;
+ tmp.compact = sortList[cnt1].compact;
+ sortList[cnt1].yCood = sortList[cnt2].yCood;
+ sortList[cnt1].sprite = sortList[cnt2].sprite;
+ sortList[cnt1].compact = sortList[cnt2].compact;
+ sortList[cnt2].yCood = tmp.yCood;
+ sortList[cnt2].sprite = tmp.sprite;
+ sortList[cnt2].compact = tmp.compact;
+ }
+ }
+ for (uint32 cnt = 0; cnt < spriteCnt; cnt++) {
+ drawSprite((uint8 *)sortList[cnt].sprite, sortList[cnt].compact);
+ if (sortList[cnt].compact->status & 8)
+ vectorToGame(0x81);
+ else
+ vectorToGame(1);
+ if (!(sortList[cnt].compact->status & 0x200))
+ verticalMask();
+ }
+ }
+}
+
+void Screen::doSprites(uint8 layer) {
+
+ uint16 drawListNum = DRAW_LIST_NO;
+ uint32 idNum;
+ uint16* drawList;
+ while (Logic::_scriptVariables[drawListNum]) { // std sp loop
+ idNum = Logic::_scriptVariables[drawListNum];
+ drawListNum++;
+
+ drawList = (uint16 *)_skyCompact->fetchCpt(idNum);
+ while (drawList[0]) {
+ // new_draw_list:
+ while ((drawList[0] != 0) && (drawList[0] != 0xFFFF)) {
+ // back_loop:
+ // not_new_list
+ Compact *spriteData = _skyCompact->fetchCpt(drawList[0]);
+ drawList++;
+ if ((spriteData->status & (1 << layer)) &&
+ (spriteData->screen == Logic::_scriptVariables[SCREEN])) {
+ uint8 *toBeDrawn = (uint8 *)SkyEngine::fetchItem(spriteData->frame >> 6);
+ if (!toBeDrawn) {
+ debug(9, "Spritedata %d not loaded", spriteData->frame >> 6);
+ spriteData->status = 0;
+ } else {
+ drawSprite(toBeDrawn, spriteData);
+ if (layer == BACK)
+ verticalMask();
+ if (spriteData->status & 8)
+ vectorToGame(0x81);
+ else
+ vectorToGame(1);
+ }
+ }
+ }
+ while (drawList[0] == 0xFFFF)
+ drawList = (uint16 *)_skyCompact->fetchCpt(drawList[1]);
+ }
+ }
+}
+
+void Screen::drawSprite(uint8 *spriteInfo, Compact *sprCompact) {
+
+ if (spriteInfo == NULL) {
+ warning("Screen::drawSprite Can't draw sprite. Data %d was not loaded", sprCompact->frame >> 6);
+ sprCompact->status = 0;
+ return;
+ }
+ dataFileHeader *sprDataFile = (dataFileHeader *)spriteInfo;
+ _sprWidth = sprDataFile->s_width;
+ _sprHeight = sprDataFile->s_height;
+ _maskX1 = _maskX2 = 0;
+ uint8 *spriteData = spriteInfo + (sprCompact->frame & 0x3F) * sprDataFile->s_sp_size;
+ spriteData += sizeof(dataFileHeader);
+ int32 spriteY = sprCompact->ycood + sprDataFile->s_offset_y - TOP_LEFT_Y;
+ if (spriteY < 0) {
+ spriteY = -spriteY;
+ if (_sprHeight <= (uint32)spriteY) {
+ _sprWidth = 0;
+ return;
+ }
+ _sprHeight -= spriteY;
+ spriteData += sprDataFile->s_width * spriteY;
+ spriteY = 0;
+ } else {
+ int32 botClip = GAME_SCREEN_HEIGHT - sprDataFile->s_height - spriteY;
+ if (botClip < 0) {
+ botClip = -botClip;
+ if (_sprHeight <= (uint32)botClip) {
+ _sprWidth = 0;
+ return;
+ }
+ _sprHeight -= botClip;
+ }
+ }
+ _sprY = (uint32)spriteY;
+ int32 spriteX = sprCompact->xcood + sprDataFile->s_offset_x - TOP_LEFT_X;
+ if (spriteX < 0) {
+ spriteX = -spriteX;
+ if (_sprWidth <= (uint32)spriteX) {
+ _sprWidth = 0;
+ return;
+ }
+ _sprWidth -= spriteX;
+ _maskX1 = spriteX;
+ spriteX = 0;
+ } else {
+ int32 rightClip = GAME_SCREEN_WIDTH - (sprDataFile->s_width + spriteX);
+ if (rightClip < 0) {
+ rightClip = (-rightClip) + 1;
+ if (_sprWidth <= (uint32)rightClip) {
+ _sprWidth = 0;
+ return;
+ }
+ _sprWidth -= rightClip;
+ _maskX2 = rightClip;
+ }
+ }
+ _sprX = (uint32)spriteX;
+ uint8 *screenPtr = _currentScreen + _sprY * GAME_SCREEN_WIDTH + _sprX;
+ if ((_sprHeight > 192) || (_sprY > 192)) {
+ _sprWidth = 0;
+ return;
+ }
+ if ((_sprX + _sprWidth > 320) || (_sprY + _sprHeight > 192)) {
+ warning("Screen::drawSprite fatal error: got x = %d, y = %d, w = %d, h = %d",_sprX, _sprY, _sprWidth, _sprHeight);
+ _sprWidth = 0;
+ return;
+ }
+
+ for (uint16 cnty = 0; cnty < _sprHeight; cnty++) {
+ for (uint16 cntx = 0; cntx < _sprWidth; cntx++)
+ if (spriteData[cntx + _maskX1])
+ screenPtr[cntx] = spriteData[cntx + _maskX1];
+ spriteData += _sprWidth + _maskX2 + _maskX1;
+ screenPtr += GAME_SCREEN_WIDTH;
+ }
+ // Convert the sprite coordinate/size values to blocks for vertical mask and/or vector to game
+ _sprWidth += _sprX + GRID_W-1;
+ _sprHeight += _sprY + GRID_H-1;
+
+ _sprX >>= GRID_W_SHIFT;
+ _sprWidth >>= GRID_W_SHIFT;
+ _sprY >>= GRID_H_SHIFT;
+ _sprHeight >>= GRID_H_SHIFT;
+
+ _sprWidth -= _sprX;
+ _sprHeight -= _sprY;
+}
+
+void Screen::vectorToGame(uint8 gridVal) {
+
+ if (_sprWidth == 0)
+ return;
+ uint8 *trgGrid = _gameGrid + _sprY * GRID_X +_sprX;
+ for (uint32 cnty = 0; cnty < _sprHeight; cnty++) {
+ for (uint32 cntx = 0; cntx < _sprWidth; cntx++)
+ trgGrid[cntx] |= gridVal;
+ trgGrid += GRID_X;
+ }
+}
+
+void Screen::vertMaskSub(uint16 *grid, uint32 gridOfs, uint8 *screenPtr, uint32 layerId) {
+
+ for (uint32 cntx = 0; cntx < _sprHeight; cntx++) { // start_x | block_loop
+ if (grid[gridOfs]) {
+ if (!(FROM_LE_16(grid[gridOfs]) & 0x8000)) {
+ uint32 gridVal = FROM_LE_16(grid[gridOfs]) - 1;
+ gridVal *= GRID_W * GRID_H;
+ uint8 *dataSrc = (uint8 *)SkyEngine::fetchItem(Logic::_scriptVariables[layerId]) + gridVal;
+ uint8 *dataTrg = screenPtr;
+ for (uint32 grdCntY = 0; grdCntY < GRID_H; grdCntY++) {
+ for (uint32 grdCntX = 0; grdCntX < GRID_W; grdCntX++)
+ if (dataSrc[grdCntX])
+ dataTrg[grdCntX] = dataSrc[grdCntX];
+ dataSrc += GRID_W;
+ dataTrg += GAME_SCREEN_WIDTH;
+ }
+ } // dummy_end:
+ screenPtr -= GRID_H * GAME_SCREEN_WIDTH;
+ gridOfs -= GRID_X;
+ } else
+ return;
+ } // next_x
+}
+
+void Screen::verticalMask(void) {
+
+ if (_sprWidth == 0)
+ return;
+ uint32 startGridOfs = (_sprY + _sprHeight - 1) * GRID_X + _sprX;
+ uint8 *startScreenPtr = (_sprY + _sprHeight - 1) * GRID_H * GAME_SCREEN_WIDTH + _sprX * GRID_W + _currentScreen;
+
+ for (uint32 layerCnt = LAYER_1_ID; layerCnt <= LAYER_3_ID; layerCnt++) {
+ uint32 gridOfs = startGridOfs;
+ uint8 *screenPtr = startScreenPtr;
+ for (uint32 widCnt = 0; widCnt < _sprWidth; widCnt++) { // x_loop
+ uint32 nLayerCnt = layerCnt;
+ while (Logic::_scriptVariables[nLayerCnt + 3]) {
+ uint16 *scrGrid;
+ scrGrid = (uint16 *)SkyEngine::fetchItem(Logic::_scriptVariables[layerCnt + 3]);
+ if (scrGrid[gridOfs]) {
+ vertMaskSub(scrGrid, gridOfs, screenPtr, layerCnt);
+ break;
+ } else
+ nLayerCnt++;
+ }
+ // next_x:
+ screenPtr += GRID_W;
+ gridOfs++;
+ }
+ }
+}
+
+void Screen::paintBox(uint16 x, uint16 y) {
+
+ uint8 *screenPos = _currentScreen + y * GAME_SCREEN_WIDTH + x;
+ memset(screenPos, 255, 8);
+ for (uint8 cnt = 1; cnt < 8; cnt++) {
+ *(screenPos + cnt * GAME_SCREEN_WIDTH) = 255;
+ *(screenPos + cnt * GAME_SCREEN_WIDTH + 7) = 255;
+ }
+ memset(screenPos + 7 * GAME_SCREEN_WIDTH, 255, 7);
+}
+
+void Screen::showGrid(uint8 *gridBuf) {
+
+ uint32 gridData = 0;
+ uint8 bitsLeft = 0;
+ for (uint16 cnty = 0; cnty < GAME_SCREEN_HEIGHT >> 3; cnty++) {
+ for (uint16 cntx = 0; cntx < GAME_SCREEN_WIDTH >> 3; cntx++) {
+ if (!bitsLeft) {
+ bitsLeft = 32;
+ gridData = *(uint32 *)gridBuf;
+ gridBuf += 4;
+ }
+ if (gridData & 0x80000000)
+ paintBox(cntx << 3, cnty << 3);
+ bitsLeft--;
+ gridData <<= 1;
+ }
+ }
+ _system->copyRectToScreen(_currentScreen, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
+
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/screen.h b/engines/sky/screen.h
new file mode 100644
index 0000000000..c1e504a87f
--- /dev/null
+++ b/engines/sky/screen.h
@@ -0,0 +1,134 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYSCREEN_H
+#define SKYSCREEN_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "sky/skydefs.h"
+
+class OSystem;
+
+namespace Sky {
+
+class Disk;
+class SkyEngine;
+class SkyCompact;
+struct Compact;
+struct dataFileHeader;
+
+#define SCROLL_JUMP 16
+#define VGA_COLOURS 256
+#define GAME_COLOURS 240
+#define SEQ_DELAY 3
+
+#define FORE 1
+#define BACK 0
+
+typedef struct {
+ uint16 yCood;
+ Compact *compact;
+ dataFileHeader *sprite;
+} StSortList;
+
+class Screen {
+public:
+ Screen(OSystem *pSystem, Disk *pDisk, SkyCompact *skyCompact);
+ ~Screen(void);
+ void setPalette(uint8 *pal);
+ void setPaletteEndian(uint8 *pal);
+ void setPalette(uint16 fileNum);
+ void paletteFadeUp(uint8 *pal);
+ void paletteFadeUp(uint16 fileNr);
+
+ void showScreen(uint16 fileNum);
+ void showScreen(uint8 *pScreen);
+
+ void handleTimer(void);
+ void startSequence(uint16 fileNum);
+ void startSequenceItem(uint16 itemNum);
+ void stopSequence(void);
+ bool sequenceRunning(void) { return _seqInfo.running; };
+ void waitForSequence(void);
+ uint32 seqFramesLeft(void) { return _seqInfo.framesLeft; };
+ uint8 *giveCurrent(void) { return _currentScreen; };
+ void halvePalette(void);
+
+ //- regular screen.asm routines
+ void forceRefresh(void) { memset(_gameGrid, 0x80, GRID_X * GRID_Y); };
+ void fnFadeUp(uint32 palNum, uint32 scroll);
+ void fnFadeDown(uint32 scroll);
+ void fnDrawScreen(uint32 palette, uint32 scroll);
+ void clearScreen(void);
+
+ void recreate(void);
+ void flip(bool doUpdate = true);
+
+ void spriteEngine(void);
+
+ void paintBox(uint16 x, uint16 y);
+ void showGrid(uint8 *gridBuf);
+
+private:
+ OSystem *_system;
+ Disk *_skyDisk;
+ SkyCompact *_skyCompact;
+ static uint8 _top16Colours[16*3];
+ uint8 _palette[1024];
+ uint32 _currentPalette;
+ uint8 _seqGrid[20 * 12];
+
+ bool volatile _gotTick;
+ void waitForTimer(void);
+ void processSequence(void);
+
+ uint8 *_gameGrid;
+ uint8 *_currentScreen;
+ uint8 *_scrollScreen;
+ struct {
+ uint32 framesLeft;
+ uint32 delay;
+ uint8 *seqData;
+ uint8 *seqDataPos;
+ volatile bool running;
+ bool runningItem; // when playing an item, don't free it afterwards.
+ } _seqInfo;
+
+ //- more regular screen.asm + layer.asm routines
+ void convertPalette(uint8 *inPal, uint8* outPal);
+ void palette_fadedown_helper(uint32 *pal, uint num);
+
+ //- sprite.asm routines
+ // fixme: get rid of these globals
+ uint32 _sprWidth, _sprHeight, _sprX, _sprY, _maskX1, _maskX2;
+ void doSprites(uint8 layer);
+ void sortSprites(void);
+ void drawSprite(uint8 *spriteData, Compact *sprCompact);
+ void verticalMask(void);
+ void vertMaskSub(uint16 *grid, uint32 gridOfs, uint8 *screenPtr, uint32 layerId);
+ void vectorToGame(uint8 gridVal);
+};
+
+} // End of namespace Sky
+
+#endif //SKYSCREEN_H
diff --git a/engines/sky/sky.cpp b/engines/sky/sky.cpp
new file mode 100644
index 0000000000..af904097ed
--- /dev/null
+++ b/engines/sky/sky.cpp
@@ -0,0 +1,556 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/timer.h"
+
+#include "sky/control.h"
+#include "sky/debug.h"
+#include "sky/disk.h"
+#include "sky/grid.h"
+#include "sky/intro.h"
+#include "sky/logic.h"
+#include "sky/mouse.h"
+#include "sky/music/adlibmusic.h"
+#include "sky/music/gmmusic.h"
+#include "sky/music/mt32music.h"
+#include "sky/music/musicbase.h"
+#include "sky/screen.h"
+#include "sky/sky.h"
+#include "sky/skydefs.h"
+#include "sky/sound.h"
+#include "sky/text.h"
+#include "sky/compact.h"
+
+#include "sound/mididrv.h"
+#include "sound/mixer.h"
+
+#ifdef _WIN32_WCE
+
+extern bool toolbar_drawn;
+extern bool draw_keyboard;
+extern bool isSmartphone(void);
+#endif
+
+/*
+ At the beginning the reverse engineers were happy, and did rejoice at
+ their task, for the engine before them did shineth and was full of
+ promise. But then they did look closer and see'th the aweful truth;
+ it's code was assembly and messy (rareth was its comments). And so large
+ were it's includes that did at first seem small; queereth also was its
+ compact(s). Then they did findeth another version, and this was slightly
+ different from the first. Then a third, and this was different again.
+ All different, but not really better, for all were not really compatible.
+ But, eventually, it did come to pass that Steel Sky was implemented on
+ a modern platform. And the programmers looked and saw that it was indeed a
+ miracle. But they were not joyous and instead did weep for nobody knew
+ just what had been done. Except people who read the source. Hello.
+
+ With apologies to the CD32 SteelSky file.
+*/
+
+static const GameSettings skySetting =
+ {"sky", "Beneath a Steel Sky", 0 };
+
+GameList Engine_SKY_gameList() {
+ GameList games;
+ games.push_back(skySetting);
+ return games;
+}
+
+DetectedGameList Engine_SKY_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+ // Iterate over all files in the given directory
+ for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (!file->isDirectory()) {
+ const char *fileName = file->displayName().c_str();
+
+ if (0 == scumm_stricmp("sky.dsk", fileName)) {
+ // Match found, add to list of candidates, then abort inner loop.
+ // The game detector uses US English by default. We want British
+ // English to match the recorded voices better.
+ detectedGames.push_back(DetectedGame(skySetting, Common::EN_GRB, Common::kPlatformUnknown));
+ break;
+ }
+ }
+ }
+ return detectedGames;
+}
+
+Engine *Engine_SKY_create(GameDetector *detector, OSystem *syst) {
+ return new Sky::SkyEngine(detector, syst);
+}
+
+REGISTER_PLUGIN(SKY, "Beneath a Steel Sky")
+
+
+namespace Sky {
+
+void *SkyEngine::_itemList[300];
+
+SystemVars SkyEngine::_systemVars = {0, 0, 0, 0, 4316, 0, 0, false, false, false };
+
+SkyEngine::SkyEngine(GameDetector *detector, OSystem *syst)
+ : Engine(syst), _fastMode(0), _debugger(0) {
+}
+
+SkyEngine::~SkyEngine() {
+
+ _timer->removeTimerProc(&timerHandler);
+
+ delete _skyLogic;
+ delete _skySound;
+ delete _skyMusic;
+ delete _skyText;
+ delete _skyMouse;
+ delete _skyScreen;
+ delete _debugger;
+ delete _skyDisk;
+ delete _skyControl;
+ delete _skyCompact;
+
+ for (int i = 0; i < 300; i++)
+ if (_itemList[i])
+ free(_itemList[i]);
+}
+
+void SkyEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+
+#ifdef _WIN32_WCE
+ if (isSmartphone())
+ return;
+#endif
+
+ // Unless an error -originated- within the debugger, spawn the
+ // debugger. Otherwise exit out normally.
+ if (_debugger && !_debugger->isAttached()) {
+ // (Print it again in case debugger segfaults)
+ printf("%s\n", buf2);
+ _debugger->attach(buf2);
+ _debugger->onFrame();
+ }
+}
+
+void SkyEngine::initVirgin() {
+
+ _skyScreen->setPalette(60111);
+ _skyScreen->showScreen(60110);
+}
+
+void SkyEngine::handleKey(void) {
+
+ if (_keyPressed && _systemVars.paused) {
+ _skySound->fnUnPauseFx();
+ _systemVars.paused = false;
+ _skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
+ _keyFlags = _keyPressed = 0;
+ return;
+ }
+
+ if (_keyFlags == OSystem::KBD_CTRL) {
+ if (_keyPressed == 'f')
+ _fastMode ^= 1;
+ else if (_keyPressed == 'g')
+ _fastMode ^= 2;
+ else if (_keyPressed == 'd')
+ _debugger->attach();
+ } else {
+ switch (_keyPressed) {
+ case '`':
+ case '~':
+ case '#':
+ _debugger->attach();
+ break;
+ case 63:
+ _skyControl->doControlPanel();
+ break;
+
+ case 27:
+ if (!_systemVars.pastIntro)
+ _skyControl->restartGame();
+ break;
+
+ case '.':
+ _skyMouse->logicClick();
+ break;
+
+ case 'p':
+ _skyScreen->halvePalette();
+ _skySound->fnPauseFx();
+ _systemVars.paused = true;
+ break;
+
+ }
+ }
+ _keyFlags = _keyPressed = 0;
+}
+
+int SkyEngine::go() {
+
+ _systemVars.quitGame = false;
+
+ _mouseX = GAME_SCREEN_WIDTH / 2;
+ _mouseY = GAME_SCREEN_HEIGHT / 2;
+ _keyFlags = _keyPressed = 0;
+
+ uint16 result = 0;
+ if (ConfMan.hasKey("save_slot") && ConfMan.getInt("save_slot") >= 0)
+ result = _skyControl->quickXRestore(ConfMan.getInt("save_slot"));
+
+ if (result != GAME_RESTORED) {
+ bool introSkipped = false;
+ if (_systemVars.gameVersion > 267) { // don't do intro for floppydemos
+ _skyIntro = new Intro(_skyDisk, _skyScreen, _skyMusic, _skySound, _skyText, _mixer, _system);
+ introSkipped = !_skyIntro->doIntro(_floppyIntro);
+ _systemVars.quitGame = _skyIntro->_quitProg;
+
+ delete _skyIntro;
+ }
+
+ if (!_systemVars.quitGame) {
+ _skyLogic->initScreen0();
+ if (introSkipped)
+ _skyControl->restartGame();
+ }
+ }
+
+ _lastSaveTime = _system->getMillis();
+
+ uint32 delayCount = _system->getMillis();
+ while (!_systemVars.quitGame) {
+ if (_debugger->isAttached())
+ _debugger->onFrame();
+
+ if (shouldPerformAutoSave(_lastSaveTime)) {
+ if (_skyControl->loadSaveAllowed()) {
+ _lastSaveTime = _system->getMillis();
+ _skyControl->doAutoSave();
+ } else
+ _lastSaveTime += 30 * 1000; // try again in 30 secs
+ }
+ _skySound->checkFxQueue();
+ _skyMouse->mouseEngine((uint16)_mouseX, (uint16)_mouseY);
+ handleKey();
+ if (_systemVars.paused) {
+ do {
+ _system->updateScreen();
+ delay(50);
+ handleKey();
+ } while (_systemVars.paused);
+ delayCount = _system->getMillis();
+ }
+
+ _skyLogic->engine();
+ _skyScreen->recreate();
+ _skyScreen->spriteEngine();
+ if (_debugger->showGrid()) {
+ _skyScreen->showGrid(_skyLogic->_skyGrid->giveGrid(Logic::_scriptVariables[SCREEN]));
+ _skyScreen->forceRefresh();
+ }
+ _skyScreen->flip();
+
+ if (_fastMode & 2)
+ delay(0);
+ else if (_fastMode & 1)
+ delay(10);
+ else {
+ delayCount += _systemVars.gameSpeed;
+ int needDelay = delayCount - (int)_system->getMillis();
+ if ((needDelay < 0) || (needDelay > 4 * _systemVars.gameSpeed)) {
+ needDelay = 0;
+ delayCount = _system->getMillis();
+ }
+ delay(needDelay);
+ }
+ }
+
+ _skyControl->showGameQuitMsg();
+ _skyMusic->stopMusic();
+ ConfMan.flushToDisk();
+ delay(1500);
+ return 0;
+}
+
+int SkyEngine::init(GameDetector &detector) {
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ _system->initSize(320, 200);
+ _system->endGFXTransaction();
+
+ if (!_mixer->isReady())
+ warning("Sound initialisation failed");
+
+ if (ConfMan.getBool("sfx_mute")) {
+ SkyEngine::_systemVars.systemFlags |= SF_FX_OFF;
+ }
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ _floppyIntro = ConfMan.getBool("alt_intro");
+
+ _skyDisk = new Disk(_gameDataPath);
+ _skySound = new Sound(_mixer, _skyDisk, ConfMan.getInt("sfx_volume"));
+
+ _systemVars.gameVersion = _skyDisk->determineGameVersion();
+
+ int midiDriver = MidiDriver::detectMusicDriver(MDT_ADLIB | MDT_MIDI | MDT_PREFER_MIDI);
+ if (midiDriver == MD_ADLIB) {
+ _systemVars.systemFlags |= SF_SBLASTER;
+ _skyMusic = new AdlibMusic(_mixer, _skyDisk);
+ } else {
+ _systemVars.systemFlags |= SF_ROLAND;
+ if ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"))
+ _skyMusic = new MT32Music(MidiDriver::createMidi(midiDriver), _skyDisk);
+ else
+ _skyMusic = new GmMusic(MidiDriver::createMidi(midiDriver), _skyDisk);
+ }
+
+ if (isCDVersion()) {
+ if (ConfMan.hasKey("nosubtitles")) {
+ warning("Configuration key 'nosubtitles' is deprecated. Use 'subtitles' instead");
+ if (!ConfMan.getBool("nosubtitles"))
+ _systemVars.systemFlags |= SF_ALLOW_TEXT;
+ }
+
+ if (ConfMan.getBool("subtitles"))
+ _systemVars.systemFlags |= SF_ALLOW_TEXT;
+
+ if (!ConfMan.getBool("speech_mute"))
+ _systemVars.systemFlags |= SF_ALLOW_SPEECH;
+
+ } else
+ _systemVars.systemFlags |= SF_ALLOW_TEXT;
+
+ _systemVars.systemFlags |= SF_PLAY_VOCS;
+ _systemVars.gameSpeed = 50;
+
+ _skyCompact = new SkyCompact();
+ _skyText = new Text(_skyDisk, _skyCompact);
+ _skyMouse = new Mouse(_system, _skyDisk, _skyCompact);
+ _skyScreen = new Screen(_system, _skyDisk, _skyCompact);
+
+ initVirgin();
+ initItemList();
+ loadFixedItems();
+ _skyLogic = new Logic(_skyCompact, _skyScreen, _skyDisk, _skyText, _skyMusic, _skyMouse, _skySound);
+ _skyMouse->useLogicInstance(_skyLogic);
+
+ // initialize timer *after* _skyScreen has been initialized.
+ _timer->installTimerProc(&timerHandler, 1000000 / 50, this); //call 50 times per second
+
+ _skyControl = new Control(_saveFileMan, _skyScreen, _skyDisk, _skyMouse, _skyText, _skyMusic, _skyLogic, _skySound, _skyCompact, _system);
+ _skyLogic->useControlInstance(_skyControl);
+
+ switch (Common::parseLanguage(ConfMan.get("language"))) {
+ case Common::EN_USA:
+ _systemVars.language = SKY_USA;
+ break;
+ case Common::DE_DEU:
+ _systemVars.language = SKY_GERMAN;
+ break;
+ case Common::FR_FRA:
+ _systemVars.language = SKY_FRENCH;
+ break;
+ case Common::IT_ITA:
+ _systemVars.language = SKY_ITALIAN;
+ break;
+ case Common::PT_BRA:
+ _systemVars.language = SKY_PORTUGUESE;
+ break;
+ case Common::ES_ESP:
+ _systemVars.language = SKY_SPANISH;
+ break;
+ case Common::SE_SWE:
+ _systemVars.language = SKY_SWEDISH;
+ break;
+ case Common::EN_GRB:
+ _systemVars.language = SKY_ENGLISH;
+ break;
+ default:
+ _systemVars.language = SKY_ENGLISH;
+ break;
+ }
+
+ if (!_skyDisk->fileExists(60600 + SkyEngine::_systemVars.language * 8)) {
+ warning("The language you selected does not exist in your BASS version.");
+ if (_skyDisk->fileExists(60600))
+ SkyEngine::_systemVars.language = SKY_ENGLISH; // default to GB english if it exists..
+ else if (_skyDisk->fileExists(60600 + SKY_USA * 8))
+ SkyEngine::_systemVars.language = SKY_USA; // try US english...
+ else
+ for (uint8 cnt = SKY_ENGLISH; cnt <= SKY_SPANISH; cnt++)
+ if (_skyDisk->fileExists(60600 + cnt * 8)) { // pick the first language we can find
+ SkyEngine::_systemVars.language = cnt;
+ break;
+ }
+ }
+
+ _skyMusic->setVolume(ConfMan.getInt("music_volume") >> 1);
+
+ _debugger = new Debugger(_skyLogic, _skyMouse, _skyScreen, _skyCompact);
+ return 0;
+}
+
+void SkyEngine::initItemList() {
+
+ //See List.asm for (cryptic) item # descriptions
+
+ for (int i = 0; i < 300; i++)
+ _itemList[i] = NULL;
+}
+
+void SkyEngine::loadFixedItems(void) {
+
+ _itemList[49] = _skyDisk->loadFile(49);
+ _itemList[50] = _skyDisk->loadFile(50);
+ _itemList[73] = _skyDisk->loadFile(73);
+ _itemList[262] = _skyDisk->loadFile(262);
+
+ if (!isDemo()) {
+ _itemList[36] = _skyDisk->loadFile(36);
+ _itemList[263] = _skyDisk->loadFile(263);
+ _itemList[264] = _skyDisk->loadFile(264);
+ _itemList[265] = _skyDisk->loadFile(265);
+ _itemList[266] = _skyDisk->loadFile(266);
+ _itemList[267] = _skyDisk->loadFile(267);
+ _itemList[269] = _skyDisk->loadFile(269);
+ _itemList[271] = _skyDisk->loadFile(271);
+ _itemList[272] = _skyDisk->loadFile(272);
+ }
+}
+
+void *SkyEngine::fetchItem(uint32 num) {
+
+ return _itemList[num];
+}
+
+void SkyEngine::timerHandler(void *refCon) {
+
+ ((SkyEngine *)refCon)->gotTimerTick();
+}
+
+void SkyEngine::gotTimerTick(void) {
+
+ _skyScreen->handleTimer();
+}
+
+void SkyEngine::delay(int32 amount) {
+
+ OSystem::Event event;
+
+ uint32 start = _system->getMillis();
+ _keyFlags = _keyPressed = 0; //reset
+
+ if (amount < 0)
+ amount = 0;
+
+ do {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ _keyFlags = event.kbd.flags;
+ if (_keyFlags == OSystem::KBD_CTRL)
+ _keyPressed = event.kbd.keycode;
+ else
+ _keyPressed = (byte)event.kbd.ascii;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ if (!(_systemVars.systemFlags & SF_MOUSE_LOCKED)) {
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ }
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+#ifdef PALMOS_MODE
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+#endif
+ _skyMouse->buttonPressed(2);
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+#ifdef PALMOS_MODE
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+#endif
+ _skyMouse->buttonPressed(1);
+ break;
+ case OSystem::EVENT_QUIT:
+ _systemVars.quitGame = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (amount > 0)
+ _system->delayMillis((amount > 10) ? 10 : amount);
+
+ } while (_system->getMillis() < start + amount);
+}
+
+bool SkyEngine::isDemo(void) {
+ switch (_systemVars.gameVersion) {
+ case 109: // pc gamer demo
+ case 267: // floppy demo
+ case 365: // cd demo
+ return true;
+ case 288:
+ case 303:
+ case 331:
+ case 348:
+ case 368:
+ case 372:
+ return false;
+ default:
+ error("Unknown game version %d", _systemVars.gameVersion);
+ }
+}
+
+bool SkyEngine::isCDVersion(void) {
+
+ switch (_systemVars.gameVersion) {
+ case 109:
+ case 267:
+ case 288:
+ case 303:
+ case 331:
+ case 348:
+ return false;
+ case 365:
+ case 368:
+ case 372:
+ return true;
+ default:
+ error("Unknown game version %d", _systemVars.gameVersion);
+ }
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/sky.h b/engines/sky/sky.h
new file mode 100644
index 0000000000..71dcfa533c
--- /dev/null
+++ b/engines/sky/sky.h
@@ -0,0 +1,112 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYMAIN_H
+#define SKYMAIN_H
+
+#include "common/stdafx.h"
+#include "base/engine.h"
+
+class GameDetector;
+
+namespace Sky {
+
+struct SystemVars {
+ uint32 systemFlags;
+ uint32 gameVersion;
+ uint32 mouseFlag;
+ uint16 language;
+ uint32 currentPalette;
+ uint16 gameSpeed;
+ uint16 currentMusic;
+ bool pastIntro;
+ bool quitGame;
+ bool paused;
+};
+
+struct Compact;
+class Sound;
+class Disk;
+class Text;
+class Logic;
+class Mouse;
+class Screen;
+class Control;
+class MusicBase;
+class Intro;
+class Debugger;
+class SkyCompact;
+
+class SkyEngine : public Engine {
+ void errorString(const char *buf_input, char *buf_output);
+protected:
+ byte _keyPressed, _keyFlags;
+ bool _floppyIntro;
+
+ int _mouseX, _mouseY;
+
+ Sound *_skySound;
+ Disk *_skyDisk;
+ Text *_skyText;
+ Logic *_skyLogic;
+ Mouse *_skyMouse;
+ Screen *_skyScreen;
+ Control *_skyControl;
+ SkyCompact *_skyCompact;
+ Debugger *_debugger;
+
+ MusicBase *_skyMusic;
+ Intro *_skyIntro;
+
+public:
+ SkyEngine(GameDetector *detector, OSystem *syst);
+ virtual ~SkyEngine();
+
+ static bool isDemo(void);
+ static bool isCDVersion(void);
+
+ static void *fetchItem(uint32 num);
+ static void *_itemList[300];
+
+ static SystemVars _systemVars;
+
+protected:
+ byte _fastMode;
+
+ void delay(int32 amount);
+ int go();
+ void handleKey(void);
+
+ uint32 _lastSaveTime;
+
+ int init(GameDetector &detector);
+ void initItemList();
+
+ void initVirgin();
+ static void timerHandler(void *ptr);
+ void gotTimerTick();
+ void loadFixedItems();
+};
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sky/skydefs.h b/engines/sky/skydefs.h
new file mode 100644
index 0000000000..4d56999f95
--- /dev/null
+++ b/engines/sky/skydefs.h
@@ -0,0 +1,4314 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYDEFS_H
+#define SKYDEFS_H
+
+#include "common/stdafx.h"
+
+namespace Sky {
+
+//This file is incomplete, several flags still missing.
+
+// grafixProg pointer types:
+#define OG_PTR_NULL 0
+#define OG_AUTOROUTE 1
+#define OG_COMPACT 2
+#define OG_COMPACTELEM 3 // needed by fnSetToStand
+#define OG_TALKTABLE 4
+
+// language codes:
+#define SKY_ENGLISH 0
+#define SKY_GERMAN 1
+#define SKY_FRENCH 2
+#define SKY_USA 3
+#define SKY_SWEDISH 4
+#define SKY_ITALIAN 5
+#define SKY_PORTUGUESE 6
+#define SKY_SPANISH 7
+
+#define ST_COLLISION_BIT 5
+
+#define S_COUNT 0
+#define S_FRAME 2
+#define S_AR_X 4
+#define S_AR_Y 6
+#define S_LENGTH 8
+
+#define KEY_BUFFER_SIZE 80
+#define SEQUENCE_COUNT 3
+
+#define FIRST_TEXT_COMPACT 23
+#define LAST_TEXT_COMPACT 33
+#define FIXED_TEXT_WIDTH 128
+
+//screen/grid defines
+#define GAME_SCREEN_WIDTH 320
+#define GAME_SCREEN_HEIGHT 192
+#define FULL_SCREEN_WIDTH 320
+#define FULL_SCREEN_HEIGHT 200
+
+#define TOT_NO_GRIDS 70 //total no. of grids supported
+#define GRID_SIZE 120 //grid size in bytes
+
+#define GRID_X 20
+#define GRID_Y 24
+#define GRID_W 16
+#define GRID_H 8
+
+#define GRID_W_SHIFT 4
+#define GRID_H_SHIFT 3
+
+#define TOP_LEFT_X 128
+#define TOP_LEFT_Y 136
+
+//item list defines
+#define section_0_item 119
+
+#define MAIN_CHAR_HEIGHT 12
+
+#define C_BASE_MODE 0
+#define C_BASE_MODE56 56
+#define C_ACTION_MODE 4
+#define C_SP_COLOUR 90
+#define C_MEGA_SET 112
+#define C_GRID_WIDTH 114
+#define C_ANIM_UP 122
+#define C_STAND_UP 138
+#define C_TURN_TABLE 158
+
+#define SECTION_0_ITEM 119 //item number of first item section
+#define NEXT_MEGA_SET (258 - C_GRID_WIDTH)
+
+#define SEND_SYNC 0xFFFF
+#define LF_START_FX 0xFFFE
+#define SAFE_START_SCREEN 0
+
+//autoroute defines
+#define UPY 0
+#define DOWNY 1
+#define LEFTY 2
+#define RIGHTY 3
+
+#define ROUTE_SPACE 64
+
+#define PCONLY_F_R3_1 0
+#define PCONLY_F_R3_2 0
+
+#define RESTART_BUTT_X 147
+#define RESTART_BUTT_Y 309
+#define RESTORE_BUTT_X 246
+#define RESTORE_BUTT_Y 309
+#define EXIT_BUTT_X 345
+#define EXIT_BUTT_Y 309
+
+#define L_SCRIPT 1
+#define L_AR 2
+#define L_AR_ANIM 3
+#define L_AR_TURNING 4
+#define L_ALT 5
+#define L_MOD_ANIMATE 6
+#define L_TURNING 7
+#define L_CURSOR 8
+#define L_TALK 9
+#define L_LISTEN 10
+#define L_STOPPED 11
+#define L_CHOOSE 12
+#define L_FRAMES 13
+#define L_PAUSE 14
+#define L_WAIT_SYNC 15
+#define L_SIMPLE_MOD 16
+
+// characters with own colour set
+#define SP_COL_FOSTER 194
+#define SP_COL_JOEY 216
+#define SP_COL_JOBS 209
+#define SP_COL_SO 218
+#define SP_COL_HOLO 234
+#define SP_COL_LAMB 203
+#define SP_COL_FOREMAN 205
+#define SP_COL_SHADES 217
+#define SP_COL_MONITOR 224
+#define SP_COL_WRECK 219 //wreck guard
+#define SP_COL_ANITA 73
+#define SP_COL_DAD 224
+#define SP_COL_SON 223
+#define SP_COL_GALAG 194
+#define SP_COL_ANCHOR 85 //194
+#define SP_COL_BURKE 192
+#define SP_COL_BODY 234
+#define SP_COL_MEDI 235
+#define SP_COL_SKORL 241 //skorl guard will probably go
+#define SP_COL_ANDROID2 222
+#define SP_COL_ANDROID3 222
+#define SP_COL_KEN 222
+#define SP_COL_HENRI30 128 //207
+#define SP_COL_GUARD31 216
+#define SP_DAN_COL 228
+#define SP_COL_BUZZER32 228 //124
+#define SP_COL_VINCENT32 193
+#define SP_COL_GARDENER32 145
+#define SP_COL_WITNESS 195
+#define SP_COL_JOBS82 209
+#define SP_COL_KEN81 224
+#define SP_COL_FATHER81 177
+#define SP_COL_TREVOR 216
+#define SP_COL_RADMAN 193
+#define SP_COL_BARMAN36 144
+#define SP_COL_BABS36 202
+#define SP_COL_GALLAGHER36 145
+#define SP_COL_COLSTON36 146
+#define SP_COL_JUKEBOX36 176
+#define SP_COL_JUDGE42 193
+#define SP_COL_CLERK42 195
+#define SP_COL_PROS42 217
+#define SP_COL_JOBS42 209
+
+#define SP_COL_HOLOGRAM_B 255
+#define SP_COL_BLUE 255
+#define SP_COL_LOADER 255
+
+#define SP_COL_UCHAR 255
+
+#define ST_NO_VMASK 0x200
+
+// Files.asm
+#define DISK_1 (2048)
+#define DISK_2 (2048*2)
+#define DISK_3 (2048*3)
+#define DISK_4 (2048*4)
+#define DISK_5 (2048*5)
+#define DISK_6 (2048*6)
+#define DISK_7 (2048*7)
+#define DISK_8 (2048*8)
+#define DISK_9 (2048*9)
+#define DISK_10 (2048*10)
+#define DISK_12 (2048*12)
+#define DISK_13 (2048*13)
+#define DISK_14 (2048*14)
+#define DISK_15 (2048*15)
+#define F_MODULE_0 60400
+#define F_MODULE_1 60401
+#define CHAR_SET_FILE 60150
+
+// Script.equ
+#define STD_BASE (0x0000+0x1)
+#define ADVISOR_188 (0x0000+0x2)
+#define JUST_INTERACT (0x0000+0x3)
+#define MEGA_CLICK (0x0000+0x4)
+#define STD_EXIT_RIGHT_ON (0x0000+0x5)
+#define STD_EXIT_LEFT_ON (0x0000+0x6)
+#define STD_EXIT_DOWN_ON (0x0000+0x7)
+#define STD_EXIT_UP_ON (0x0000+0x8)
+#define STD_ON (0x0000+0x9)
+#define STD_OFF (0x0000+0xa)
+#define TEXT_ON (0x0000+0xb)
+#define TEXT_OFF (0x0000+0xc)
+#define START_MENU (0x0000+0xd)
+#define MENU_SELECT (0x0000+0xe)
+#define CLICK_LEFT_ARROW (0x0000+0xf)
+#define CLICK_RIGHT_ARROW (0x0000+0x10)
+#define TOUCH_MENU (0x0000+0x11)
+#define UNTOUCH_MENU (0x0000+0x12)
+#define TOUCH_ARROW (0x0000+0x13)
+#define UNTOUCH_ARROW (0x0000+0x14)
+#define MENU_SCRIPT (0x0000+0x15)
+#define TEXT_CLICK (0x0000+0x16)
+#define RESET_MOUSE (0x0000+0x17)
+#define CLICK_DEBUG (0x0000+0x18)
+#define STD_MEGA_STOP (0x0000+0x19)
+#define STD_PLAYER_STOP (0x0000+0x1a)
+#define STD_MINI_BUMP (0x0000+0x1b)
+#define RET_OK (0x0000+0x1c)
+#define RET_FAIL (0x0000+0x1d)
+#define STD_ADJOIN_FLOOR (0x0000+0x1e)
+#define FLOOR_ACTION (0x0000+0x1f)
+#define ANIMATE_LOGIC (0x0000+0x20)
+#define STORE_188 (0x0000+0x21)
+#define TEXT_EDIT (0x0000+0x22)
+#define SHOUT_SSS (0x0000+0x23)
+#define SHOUT_ACTION (0x0000+0x24)
+#define MEGA_SSS (0x0000+0x25)
+#define MEGA_ACTION (0x0000+0x26)
+#define BASE_INTRO (0x0000+0x27)
+#define FURNACE_D_ACTION (0x0000+0x28)
+#define STAIR6_ACTION (0x0000+0x29)
+#define GET_TO_JP2 (0x0000+0x2a)
+#define JOEY_EXTRA (0x0000+0x2b)
+#define JOEY_LOGIC (0x0000+0x2c)
+#define SECURITY_EXIT_ACTION (0x0000+0x2d)
+#define SMALL_DOOR_ACTION (0x0000+0x2e)
+#define LINK_7_29 (0x0000+0x2f)
+#define LINK_29_7 (0x0000+0x30)
+#define LAMB_TO_3 (0x0000+0x31)
+#define LAMB_TO_2 (0x0000+0x32)
+#define SS_SIGN_ACTION (0x0000+0x33)
+#define SCANNER_10_LOGIC (0x0000+0x34)
+#define SLOT_10_ACTION (0x0000+0x35)
+#define DAD_SCAN_SSS (0x0000+0x36)
+#define LOBBY_SLOT_ACTION (0x0000+0x37)
+#define LINK_28_31 (0x0000+0x38)
+#define LINK_31_28 (0x0000+0x39)
+#define LINK_65_66 (0x0000+0x3a)
+#define DEATH_SCRIPT (0x0000+0x3b)
+#define RESTART_BUTTON_LOGIC (0x0000+0x3c)
+#define RESTORE_BUTTON_LOGIC (0x0000+0x3d)
+#define EXIT_BUTTON_LOGIC (0x0000+0x3e)
+#define DEATH_CLICK (0x0000+0x3f)
+#define STD_GIVE_UP (0x1000+0x1)
+#define GET_TO_TALK1 (0x1000+0x2)
+#define GET_TO_TALK2 (0x1000+0x3)
+#define STAIRS_FROM_LOW (0x1000+0x4)
+#define STAIRS_FROM_HIGH (0x1000+0x5)
+#define STAIR_ACTION (0x1000+0x6)
+#define CLIMB_STAIRS (0x1000+0x7)
+#define SS_SHOOTS (0x1000+0x8)
+#define UPSTAIR_ACTION (0x1000+0x9)
+#define DECEND (0x1000+0xa)
+#define START_MINI_SS (0x1000+0xb)
+#define ALERT_SS (0x1000+0xc)
+#define MORE_SS (0x1000+0xd)
+#define BAR_ACTION (0x1000+0xe)
+#define GET_TO_BAR (0x1000+0xf)
+#define FULL_SS_CUT (0x1000+0x10)
+#define CUT_SEQ (0x1000+0x11)
+#define FIRE_EXIT_ACTION (0x1000+0x12)
+#define GET_TO_FEXIT (0x1000+0x13)
+#define FEXIT_DOOR (0x1000+0x14)
+#define FEXIT_ON (0x1000+0x15)
+#define SMALL_DOOR_LOGIC (0x1000+0x16)
+#define GET_TO_SMALL_DOOR (0x1000+0x17)
+#define ER0_ACTION (0x1000+0x18)
+#define ER0_ALT (0x1000+0x19)
+#define GET_TO_ER0 (0x1000+0x1a)
+#define FAN1_LOGIC (0x1000+0x1b)
+#define FAN2_LOGIC (0x1000+0x1c)
+#define FAN3_LOGIC (0x1000+0x1d)
+#define FAN4_LOGIC (0x1000+0x1e)
+#define FAN5_LOGIC (0x1000+0x1f)
+#define FAN6_LOGIC (0x1000+0x20)
+#define FAN7_LOGIC (0x1000+0x21)
+#define UPLOAD_WAIT (0x1000+0x22)
+#define GET_TO_UPLOAD (0x1000+0x23)
+#define LAZER_GUN_LOGIC (0x1000+0x24)
+#define LOAD_POINT_LOGIC (0x1000+0x25)
+#define NOTICE_LOGIC (0x1000+0x26)
+#define NOTICE_ACTION (0x1000+0x27)
+#define GET_TO_NOTICE (0x1000+0x28)
+#define PRESS_LOGIC (0x1000+0x29)
+#define PRESS_ACTION (0x1000+0x2a)
+#define GET_TO_PRESS (0x1000+0x2b)
+#define LOAD_POINT_ACTION (0x1000+0x2c)
+#define SMALL_DOOR_ON (0x1000+0x2d)
+#define OUT_EXIT_ON (0x1000+0x2e)
+#define R1EXIT_DOOR (0x1000+0x2f)
+#define GET_TO_R1_DOOR (0x1000+0x30)
+#define S1_DOOR_ACTION (0x1000+0x31)
+#define GET_TO_NOTICE2 (0x1000+0x32)
+#define NOTICE2_ACTION (0x1000+0x33)
+#define GET_TO_SS_SIGN (0x1000+0x34)
+#define LFAN1_LOGIC (0x1000+0x35)
+#define LFAN2_LOGIC (0x1000+0x36)
+#define SMOKE1_LOGIC (0x1000+0x37)
+#define SMOKE2_LOGIC (0x1000+0x38)
+#define FIRE1_LOGIC (0x1000+0x39)
+#define FIRE2_LOGIC (0x1000+0x3a)
+#define CAR_LOGIC (0x1000+0x3b)
+#define ER0_WALK_ON (0x1000+0x3c)
+#define S2_WALK_ON (0x1000+0x3d)
+#define EL2_ACTION (0x1000+0x3e)
+#define GET_TO_EL2 (0x1000+0x3f)
+#define GET_TO_TALK21 (0x1000+0x40)
+#define MEGA_FAILED_S2 (0x1000+0x41)
+#define GET_TO_TALK22 (0x1000+0x42)
+#define GET_TO_ER2 (0x1000+0x43)
+#define FIRST_INTERACT (0x1000+0x44)
+#define ER2_ACTION (0x1000+0x45)
+#define TOP_LIFT_ACTION (0x1000+0x46)
+#define LIFT_WAIT (0x1000+0x47)
+#define GET_TO_LIFTER (0x1000+0x48)
+#define LIFT_TO_FLOOR (0x1000+0x49)
+#define TOP_BARREL_LOGIC (0x1000+0x4a)
+#define LIGHT_LOGIC (0x1000+0x4b)
+#define GET_TO_LIGHT1 (0x1000+0x4c)
+#define PANEL_LOGIC (0x1000+0x4d)
+#define ALARM_LOGIC (0x1000+0x4e)
+#define GET_TO_HOLE (0x1000+0x4f)
+#define HOLE_ACTION (0x1000+0x50)
+#define DEAD_LOSS (0x1000+0x51)
+#define GET_TO_TRANSPORTER (0x1000+0x52)
+#define TRANSPORTER_ACTION (0x1000+0x53)
+#define TRANS_ALIVE_ACTION (0x1000+0x54)
+#define JOEY_START (0x1000+0x55)
+#define JOEY_MISSION (0x1000+0x56)
+#define TRANS_MISSION (0x1000+0x57)
+#define SHELL_ACTION (0x1000+0x58)
+#define GET_TO_SHELL (0x1000+0x59)
+#define LIGHT1_ACTION (0x1000+0x5a)
+#define GET_TO_LEDS (0x1000+0x5b)
+#define GT_PANEL2 (0x1000+0x5c)
+#define PANEL2_ACTION (0x1000+0x5d)
+#define GT_JUNK1 (0x1000+0x5e)
+#define JUNK1_ACTION (0x1000+0x5f)
+#define GT_JUNK2 (0x1000+0x60)
+#define JUNK2_ACTION (0x1000+0x61)
+#define TOP_LIFT_LOGIC (0x1000+0x62)
+#define TOP_LIFT_2_LOGIC (0x1000+0x63)
+#define LOW_LIFT_LOGIC (0x1000+0x64)
+#define LOW_LIFT2_LOGIC (0x1000+0x65)
+#define LOW_LIFT3_LOGIC (0x1000+0x66)
+#define STEVE_SPY_LOGIC (0x1000+0x67)
+#define LOW_BARREL_LOGIC (0x1000+0x68)
+#define CONVEY_LOGIC (0x1000+0x69)
+#define FLY_LOGIC (0x1000+0x6a)
+#define FURNACE_LOGIC (0x1000+0x6b)
+#define LIGHTS1_LOGIC (0x1000+0x6c)
+#define EYE_BALL_LOGIC (0x1000+0x6d)
+#define NEW_EYE_BALL (0x1000+0x6e)
+#define FURNACE_DOOR_LOGIC (0x1000+0x6f)
+#define GET_TO_FURNACE_DOOR (0x1000+0x70)
+#define GET_TO_SLOT (0x1000+0x71)
+#define SLOT_ACTION (0x1000+0x72)
+#define SLOT_MISSION (0x1000+0x73)
+#define SHADES_LOGIC (0x1000+0x74)
+#define CORNER_MISSION (0x1000+0x75)
+#define EYE_BOLT_LOGIC (0x1000+0x76)
+#define SMOULDER_LOGIC (0x1000+0x77)
+#define GET_TO_BODY (0x1000+0x78)
+#define SMOULDER_ACTION (0x1000+0x79)
+#define GET_TO_EYE (0x1000+0x7a)
+#define EYE_ACTION (0x1000+0x7b)
+#define GET_TO_FURNACE (0x1000+0x7c)
+#define FURNACE_ACTION (0x1000+0x7d)
+#define FURNACE_EXIT_ON (0x1000+0x7e)
+#define GET_TO_EL4 (0x1000+0x7f)
+#define S4_WALK_ON (0x1000+0x80)
+#define EL4_ACTION (0x1000+0x81)
+#define PLAYER_FAIL_S4 (0x1000+0x82)
+#define GET_TO_TALK41 (0x1000+0x83)
+#define MEGA_FAILED_S4 (0x1000+0x84)
+#define GET_TO_TALK42 (0x1000+0x85)
+#define TV_LOGIC (0x1000+0x86)
+#define KNOB_ACTION (0x1000+0x87)
+#define KNOB_LOGIC (0x1000+0x88)
+#define CHUCK_LOGIC (0x1000+0x89)
+#define LAZER_LOGIC (0x1000+0x8a)
+#define GT_LAZER (0x1000+0x8b)
+#define LAZER_ACTION (0x1000+0x8c)
+#define CUPBOARD_LOGIC (0x1000+0x8d)
+#define CUPBOARD_ALERT (0x1000+0x8e)
+#define CUPBOARD_ACTION (0x1000+0x8f)
+#define GET_TO_CUPBOARD (0x1000+0x90)
+#define GET_TO_SHELVES (0x1000+0x91)
+#define GET_TO_KNOB (0x1000+0x92)
+#define GET_TO_CHUCK (0x1000+0x93)
+#define GET_TO_SCREENS (0x1000+0x94)
+#define SPANNER_ACTION (0x1000+0x95)
+#define SARNIE_ACTION (0x1000+0x96)
+#define GET_TO_BUTTONS (0x1000+0x97)
+#define MONITOR_LOGIC (0x1000+0x98)
+#define BUTTON_ACTION (0x1000+0x99)
+#define POSTCARD_ACTION (0x1000+0x9a)
+#define GET_TO_POSTCARD (0x1000+0x9b)
+#define NOTICE4_ACTION (0x1000+0x9c)
+#define CHUCK_ACTION (0x1000+0x9d)
+#define TV_1_ACTION (0x1000+0x9e)
+#define TV_2_ACTION (0x1000+0x9f)
+#define GET_TO_MONITOR (0x1000+0xa0)
+#define JOBS_ALARMED (0x1000+0xa1)
+#define JOBS_S4 (0x1000+0xa2)
+#define MORE_JOBS (0x1000+0xa3)
+#define BASIC_JOBS (0x1000+0xa4)
+#define START_JOBS (0x1000+0xa5)
+#define DEAD_LOGIC (0x1000+0xa6)
+#define LOADER_LOGIC (0x1000+0xa7)
+#define LOADER_START (0x1000+0xa8)
+#define JOBS_SPEECH (0x1000+0xa9)
+#define UNUSED (0x1000+0xaa)
+#define GET_TO_STAIRS6 (0x2000+0x1)
+#define GET_SECURITY_S6 (0x2000+0x2)
+#define JOEY_FLY_TO_6 (0x2000+0x3)
+#define GET_TO_L_EXIT_S6 (0x2000+0x4)
+#define EL6_ACTION (0x2000+0x5)
+#define S5_WALK_ON (0x2000+0x6)
+#define S5_STROLL_ON (0x2000+0x7)
+#define GET_TO_R_EXIT_S5 (0x2000+0x8)
+#define ER5_ACTION (0x2000+0x9)
+#define S6_WALK_ON (0x2000+0xa)
+#define GET_TO_R_EXIT_S6 (0x2000+0xb)
+#define S6_RWALK_ON (0x2000+0xc)
+#define INTO_18_FAIL (0x2000+0xd)
+#define GET_TO_L_EXIT_S5 (0x2000+0xe)
+#define LDOOR_5_ACTION (0x2000+0xf)
+#define S6_DOOR_ACTION (0x2000+0x10)
+#define S6_SEC_WALK_ON (0x2000+0x11)
+#define SKORL_LOGIC (0x2000+0x12)
+#define SKORL_SSS (0x2000+0x13)
+#define SKORL_ACTION (0x2000+0x14)
+#define GET_TO_SKORL (0x2000+0x15)
+#define GET_TO_SECURITY_EXIT (0x2000+0x16)
+#define ER6_ACTION (0x2000+0x17)
+#define EL7_ACTION (0x2000+0x18)
+#define GET_TO_L_EXIT_S7 (0x2000+0x19)
+#define S7_WALK_ON (0x2000+0x1a)
+#define GET_TO_R_EXIT_S7 (0x2000+0x1b)
+#define ER7_ACTION (0x2000+0x1c)
+#define S8_WALK_ON (0x2000+0x1d)
+#define ED8_ACTION (0x2000+0x1e)
+#define GET_TO_EXIT_S8 (0x2000+0x1f)
+#define S7_RIGHT_ON (0x2000+0x20)
+#define GET_TO_WRECK (0x2000+0x21)
+#define WRECK_ACTION (0x2000+0x22)
+#define WRECK_LOGIC (0x2000+0x23)
+#define FACT_FAIL (0x2000+0x24)
+#define GET_TO_FACTORY (0x2000+0x25)
+#define FACTORY_ENTRY_ACTION (0x2000+0x26)
+#define S12_WALK_ON (0x2000+0x27)
+#define GT_S7_LINC (0x2000+0x28)
+#define GT_S7_SLOT (0x2000+0x29)
+#define GT_LIFT_NOTICE (0x2000+0x2a)
+#define LIFT_NOTICE_ACTION (0x2000+0x2b)
+#define S7_SLOT_ACTION (0x2000+0x2c)
+#define LINC_S7_ACTION (0x2000+0x2d)
+#define LEFT_FAIL_7 (0x2000+0x2e)
+#define GT_L_TALK_7 (0x2000+0x2f)
+#define RIGHT_FAIL_7 (0x2000+0x30)
+#define GT_R_TALK_7 (0x2000+0x31)
+#define JOEY_OUT_OF_LIFT7 (0x2000+0x32)
+#define CABLE_7_LOGIC (0x2000+0x33)
+#define GT_CABLE_7 (0x2000+0x34)
+#define CABLE_7_ACTION (0x2000+0x35)
+#define CABLE_MISSION (0x2000+0x36)
+#define GT_S7_LIFT (0x2000+0x37)
+#define S7_LIFT_LOGIC (0x2000+0x38)
+#define JOEY_TO_LIFT (0x2000+0x39)
+#define LIFT_S7_ACTION (0x2000+0x3a)
+#define COPTER_ACTION (0x2000+0x3b)
+#define S9_WALK_ON (0x2000+0x3c)
+#define FANS_LOGIC (0x2000+0x3d)
+#define GET_TO_L_EXIT_S9 (0x2000+0x3e)
+#define EL9_ACTION (0x2000+0x3f)
+#define LOBBY_DOOR_LOGIC (0x2000+0x40)
+#define LOBBY_DOOR_ON (0x2000+0x41)
+#define GET_TO_LOBBY_DOOR (0x2000+0x42)
+#define LOBBY_DOOR_ACTION (0x2000+0x43)
+#define SCANNER_LOGIC (0x2000+0x44)
+#define GET_TO_SCANNER (0x2000+0x45)
+#define SCANNER_ACTION (0x2000+0x46)
+#define DAD_LOGIC (0x2000+0x47)
+#define DAD_SSS (0x2000+0x48)
+#define DAD_ACTION (0x2000+0x49)
+#define GET_TO_DAD (0x2000+0x4a)
+#define GET_TO_SON (0x2000+0x4b)
+#define SON_LOGIC (0x2000+0x4c)
+#define SON_ACTION (0x2000+0x4d)
+#define GT_LINC_S9 (0x2000+0x4e)
+#define LINC_S9_ACTION (0x2000+0x4f)
+#define GET_TO_R_EXIT_S18 (0x2000+0x50)
+#define S18_WALK_ON (0x2000+0x51)
+#define ER18_ACTION (0x2000+0x52)
+#define MONITOR_SLEEP (0x2000+0x53)
+#define SON_SSS (0x2000+0x54)
+#define STEAM_FRIGHT (0x2000+0x55)
+#define MONITOR_ALERT (0x2000+0x56)
+#define STEAM_LOGIC (0x2000+0x57)
+#define POWER_DOOR_LOGIC (0x2000+0x58)
+#define POWER_MOTOR_LOGIC (0x2000+0x59)
+#define POWER_PANEL_LOGIC (0x2000+0x5a)
+#define GET_TO_POWER_PANEL (0x2000+0x5b)
+#define SOCKET_ACTION (0x2000+0x5c)
+#define POWER_SWITCH_LOGIC (0x2000+0x5d)
+#define GET_TO_POWER_SWITCH (0x2000+0x5e)
+#define SWITCH_ACTION (0x2000+0x5f)
+#define CHAIR_FAIL (0x2000+0x60)
+#define GET_TO_POWER_CHAIR (0x2000+0x61)
+#define GET_TO_LEFT_SKULL (0x2000+0x62)
+#define GET_TO_RIGHT_SKULL (0x2000+0x63)
+#define LEFT_SKULL_ACTION (0x2000+0x64)
+#define POWER_BANG_LOGIC (0x2000+0x65)
+#define RIGHT_SKULL_ACTION (0x2000+0x66)
+#define GET_TO_TALK (0x2000+0x67)
+#define GORDON_SSS (0x2000+0x68)
+#define SAT_GORDON_ACTION (0x2000+0x69)
+#define GET_TO_POWER_DOOR (0x2000+0x6a)
+#define POWER_DOOR_ACTION (0x2000+0x6b)
+#define JOEY_BUTTON_MISSION (0x2000+0x6c)
+#define LEFT_LEVER_LOGIC (0x2000+0x6d)
+#define RIGHT_LEVER_LOGIC (0x2000+0x6e)
+#define LEFT_LEVER_ACTION (0x2000+0x6f)
+#define RIGHT_LEVER_ACTION (0x2000+0x70)
+#define GET_TO_RIGHT_LEVER (0x2000+0x71)
+#define GET_TO_LEFT_LEVER (0x2000+0x72)
+#define CHAIR_ACTION (0x2000+0x73)
+#define S12_LEFT_ON (0x2000+0x74)
+#define GET_TO_FACTORY_EXIT (0x2000+0x75)
+#define FACTORY_EXIT_ACTION (0x2000+0x76)
+#define LAMB_LEAVE_FACTORY (0x2000+0x77)
+#define LAMB_FACT_2 (0x2000+0x78)
+#define LAMB_FACT_RETURN (0x2000+0x79)
+#define LAMB_FACTORY_START (0x2000+0x7a)
+#define LAMB_FORCE_START (0x2000+0x7b)
+#define GT_FACT1_EXIT (0x2000+0x7c)
+#define FACT1_EXIT_ACTION (0x2000+0x7d)
+#define S12_RIGHT_ON (0x2000+0x7e)
+#define ANITA_WORK (0x2000+0x7f)
+#define ANITA_SSS (0x2000+0x80)
+#define ANITA_ACTION (0x2000+0x81)
+#define GT_ANITA (0x2000+0x82)
+#define BOTBELT_LOGIC (0x2000+0x83)
+#define STD_FACT_LOGIC (0x2000+0x84)
+#define ANITA_ALERT (0x2000+0x85)
+#define ANITA_SPY_ACTION (0x2000+0x86)
+#define ANITA_SPY_LOGIC (0x2000+0x87)
+#define TICK_OFF_II (0x2000+0x88)
+#define GT_TICK_OFF (0x2000+0x89)
+#define WELDER_LOGIC (0x2000+0x8a)
+#define GT_WELDER (0x2000+0x8b)
+#define WELDER_ACTION (0x2000+0x8c)
+#define LEFT_FAIL_12 (0x2000+0x8d)
+#define GT_L_TALK_12 (0x2000+0x8e)
+#define RIGHT_FAIL_12 (0x2000+0x8f)
+#define GT_R_TALK_12 (0x2000+0x90)
+#define WELDER_MISSION (0x2000+0x91)
+#define JOEY_WELD_MISSION (0x2000+0x92)
+#define GT_STUMP (0x2000+0x93)
+#define STUMP_ACTION (0x2000+0x94)
+#define GT_CONSOLE_12 (0x2000+0x95)
+#define CONSOLE_12_ACTION (0x2000+0x96)
+#define GT_TOUR_1 (0x2000+0x97)
+#define GT_TOUR_2 (0x2000+0x98)
+#define FOREMAN_LAMB (0x2000+0x99)
+#define FOSTER_TOUR (0x2000+0x9a)
+#define LAMB_TOUR (0x2000+0x9b)
+#define ON_FROM_S15 (0x2000+0x9c)
+#define S13_LEFT_ON (0x2000+0x9d)
+#define GT_FACT2_L_EXIT (0x2000+0x9e)
+#define FACT2_ACTION (0x2000+0x9f)
+#define FACT2_RIGHT_ACTION (0x2000+0xa0)
+#define GT_FACT2_R_EXIT (0x2000+0xa1)
+#define S13_RIGHT_ON (0x2000+0xa2)
+#define GT_FACT2_STORE_EXIT (0x2000+0xa3)
+#define FACT2_STORE_ACTION (0x2000+0xa4)
+#define GT_COGS (0x2000+0xa5)
+#define FOREMAN_LOGIC (0x2000+0xa6)
+#define FOREMAN_ALERT (0x2000+0xa7)
+#define COGS_ACTION (0x2000+0xa8)
+#define GT_LITE1 (0x2000+0xa9)
+#define STORE_ROOM_STOP (0x2000+0xaa)
+#define SENSOR_LOGIC (0x2000+0xab)
+#define RIGHT_FAIL_13 (0x2000+0xac)
+#define GT_R_TALK_13 (0x2000+0xad)
+#define LEFT_FAIL_13 (0x2000+0xae)
+#define GT_L_TALK_13 (0x2000+0xaf)
+#define GT_STORE_STOP (0x2000+0xb0)
+#define FACT2_FOREMAN_ALERT (0x2000+0xb1)
+#define FACT2_STOP (0x2000+0xb2)
+#define FACT2_SPY_LOGIC (0x2000+0xb3)
+#define GT_WINDOW (0x2000+0xb4)
+#define WINDOW_ACTION (0x2000+0xb5)
+#define FACT_CONSOLE_LOGIC (0x2000+0xb6)
+#define FOREMAN_STORE_CHECK (0x2000+0xb7)
+#define GT_SENSORS (0x2000+0xb8)
+#define SENSORS_ACTION (0x2000+0xb9)
+#define GT_FACT_CONSOLE (0x2000+0xba)
+#define CONSOLE_13_ACTION (0x2000+0xbb)
+#define RAD_BACK (0x2000+0xbc)
+#define FACT3_ACTION (0x2000+0xbd)
+#define S14_LEFT_ON (0x2000+0xbe)
+#define GT_FACT3_L_EXIT (0x2000+0xbf)
+#define ALT_NU_ANITA (0x2000+0xc0)
+#define GT_NU_ANITA (0x2000+0xc1)
+#define GT_FACT3_R_EXIT (0x2000+0xc2)
+#define LOCKER3_LOGIC (0x2000+0xc3)
+#define LOCKER2_LOGIC (0x2000+0xc4)
+#define LOCKER1_LOGIC (0x2000+0xc5)
+#define LOCKER3_ACTION (0x2000+0xc6)
+#define GT_LOCKER3 (0x2000+0xc7)
+#define LOCKER2_ACTION (0x2000+0xc8)
+#define GT_LOCKER2 (0x2000+0xc9)
+#define GT_LOCKER1 (0x2000+0xca)
+#define LOCKER1_ACTION (0x2000+0xcb)
+#define GT_MACHINE (0x2000+0xcc)
+#define MACHINE_ACTION (0x2000+0xcd)
+#define FACT3_R_ACTION (0x2000+0xce)
+#define S14_RIGHT_ON (0x2000+0xcf)
+#define NU_ANITA_SSS (0x2000+0xd0)
+#define RADMAN_LOGIC (0x2000+0xd1)
+#define LEFT_FAIL_14 (0x2000+0xd2)
+#define GT_L_TALK_14 (0x2000+0xd3)
+#define RIGHT_FAIL_14 (0x2000+0xd4)
+#define GT_R_TALK_14 (0x2000+0xd5)
+#define RAD_SCREEN_ACTION (0x2000+0xd6)
+#define GT_RAD_SCREEN (0x2000+0xd7)
+#define GT_14_CONSOLE (0x2000+0xd8)
+#define CONSOLE_14_ACTION (0x2000+0xd9)
+#define COAT_LOGIC (0x2000+0xda)
+#define GT_COAT (0x2000+0xdb)
+#define COAT_ACTION (0x2000+0xdc)
+#define S15_WALK_ON (0x2000+0xdd)
+#define GT_STORE_EXIT (0x2000+0xde)
+#define STORE_EXIT_ACTION (0x2000+0xdf)
+#define JOEY_42_MISS (0x2000+0xe0)
+#define JOEY_JUNCTION_MISS (0x2000+0xe1)
+#define GT_JUNCTION_BOX (0x2000+0xe2)
+#define JUNCTION_ACTION (0x2000+0xe3)
+#define FLAP_LOGIC (0x2000+0xe4)
+#define GT_FLAP (0x2000+0xe5)
+#define FLAP_ACTION (0x2000+0xe6)
+#define GT_SKEY (0x2000+0xe7)
+#define GT_WD40 (0x2000+0xe8)
+#define SKEY_ACTION (0x2000+0xe9)
+#define WD40_ACTION (0x2000+0xea)
+#define SHELF_OBJECT_LOGIC (0x2000+0xeb)
+#define FLOOR_PUTTY_ACTION (0x2000+0xec)
+#define GT_PUTTY (0x2000+0xed)
+#define FORKLIFT_LOGIC (0x2000+0xee)
+#define S16_LEFT_ON (0x2000+0xef)
+#define ENTRANCE_EXIT_ACTION (0x2000+0xf0)
+#define GT_ENTRANCE_EXIT (0x2000+0xf1)
+#define GT_ALT_CONSOLE (0x2000+0xf2)
+#define GT_REACTOR_CONSOLE (0x2000+0xf3)
+#define REACTOR_PC_ACTION (0x2000+0xf4)
+#define REACTOR_DOOR_ACTION (0x2000+0xf5)
+#define GT_REACTOR_DOOR (0x2000+0xf6)
+#define LEFT_FAIL_16 (0x2000+0xf7)
+#define GT_L_TALK_16 (0x2000+0xf8)
+#define RIGHT_FAIL_16 (0x2000+0xf9)
+#define GT_R_TALK_16 (0x2000+0xfa)
+#define REACTOR_ON (0x2000+0xfb)
+#define S17_LEFT_ON (0x2000+0xfc)
+#define GT_CORE_EXIT (0x2000+0xfd)
+#define CORE_EXIT_ACTION (0x2000+0xfe)
+#define S16_CORE_ON (0x2000+0xff)
+#define GT_ANITA_CARD (0x2000+0x100)
+#define ANITA_CARD_ACTION (0x2000+0x101)
+#define GT_RODS (0x2000+0x102)
+#define RODS_ACTION (0x2000+0x103)
+#define CONSOLE_16_LOGIC (0x2000+0x104)
+#define S10_RIGHT_ON (0x3000+0x1)
+#define GT_LEFT_EXIT_10 (0x3000+0x2)
+#define EL10_ACTION (0x3000+0x3)
+#define LIFT_10_LOGIC (0x3000+0x4)
+#define GT_SLOT_10 (0x3000+0x5)
+#define POD_LOGIC (0x3000+0x6)
+#define POD_LIGHT_LOGIC (0x3000+0x7)
+#define POD_LIGHT_ACTION (0x3000+0x8)
+#define GT_POD_LIGHT (0x3000+0x9)
+#define GT_LINC_10 (0x3000+0xa)
+#define LINC_10_ACTION (0x3000+0xb)
+#define GT_FLOOR_FROM_CHAIR10 (0x3000+0xc)
+#define GT_TERMINAL_10 (0x3000+0xd)
+#define TERMINAL_10_ACTION (0x3000+0xe)
+#define GT_SCANNER_10 (0x3000+0xf)
+#define SCANNER_10_ACTION (0x3000+0x10)
+#define GT_DOOR_10 (0x3000+0x11)
+#define DOOR_10_ACTION (0x3000+0x12)
+#define GT_SLOT_11 (0x3000+0x13)
+#define SLOT_11_ACTION (0x3000+0x14)
+#define GT_SOCCER_1 (0x3000+0x15)
+#define SOCCER_1_ACTION (0x3000+0x16)
+#define GT_SOCCER_2 (0x3000+0x17)
+#define GT_SOCCER_3 (0x3000+0x18)
+#define GT_SOCCER_4 (0x3000+0x19)
+#define GT_SOCCER_5 (0x3000+0x1a)
+#define SLAT_ACTION (0x3000+0x1b)
+#define GT_RIGHT_EXIT_11 (0x3000+0x1c)
+#define ER11_ACTION (0x3000+0x1d)
+#define S11_LEFT_ON (0x3000+0x1e)
+#define GT_LEFT_EXIT_19 (0x3000+0x1f)
+#define EL19_ACTION (0x3000+0x20)
+#define GT_TOP_RIGHT_19 (0x3000+0x21)
+#define TOP_R19_ACTION (0x3000+0x22)
+#define UCHAR_SSS (0x3000+0x23)
+#define UCHAR_ACTION (0x3000+0x24)
+#define GET_TO_UCHAR (0x3000+0x25)
+#define UCHAR_LOGIC (0x3000+0x26)
+#define S20_START_ON (0x3000+0x27)
+#define DOWN_20_FAIL (0x3000+0x28)
+#define GT_DOWN_EXIT_20 (0x3000+0x29)
+#define ED20_ACTION (0x3000+0x2a)
+#define REICH_20_ON (0x3000+0x2b)
+#define REICH_DOOR_FAIL (0x3000+0x2c)
+#define GT_REICH_DOOR_20 (0x3000+0x2d)
+#define REICH_DOOR_20_ACTION (0x3000+0x2e)
+#define GT_REICH_SLOT (0x3000+0x2f)
+#define REICH_SLOT_ACTION (0x3000+0x30)
+#define S20_REICH_ON (0x3000+0x31)
+#define REICH_DOOR_20_LOGIC (0x3000+0x32)
+#define LAMB_DOOR_20_LOGIC (0x3000+0x33)
+#define LAMB_SLOT_FAIL (0x3000+0x34)
+#define GT_LAMB_SLOT (0x3000+0x35)
+#define LAMB_SLOT_ACTION (0x3000+0x36)
+#define LAMB_20_ON (0x3000+0x37)
+#define LAMB_DOOR_FAIL (0x3000+0x38)
+#define GT_LAMB_DOOR_20 (0x3000+0x39)
+#define LAMB_DOOR_20_ACTION (0x3000+0x3a)
+#define S20_LAMB_ON (0x3000+0x3b)
+#define GT_SHRUB_1 (0x3000+0x3c)
+#define SHRUB_1_ACTION (0x3000+0x3d)
+#define GT_SHRUB_2 (0x3000+0x3e)
+#define SHRUB_2_ACTION (0x3000+0x3f)
+#define GT_SHRUB_3 (0x3000+0x40)
+#define SHRUB_3_ACTION (0x3000+0x41)
+#define START_20 (0x3000+0x42)
+#define GAL_LOGIC (0x3000+0x43)
+#define GT_GALLAGER_BEL (0x3000+0x44)
+#define GAL_BEL_ACTION (0x3000+0x45)
+#define GT_REICH_WINDOW (0x3000+0x46)
+#define REICH_WINDOW_ACTION (0x3000+0x47)
+#define GT_LAMB_WINDOW (0x3000+0x48)
+#define LAMB_WINDOW_ACTION (0x3000+0x49)
+#define LEFT_FAIL_20 (0x3000+0x4a)
+#define GT_L_TALK_20 (0x3000+0x4b)
+#define RIGHT_FAIL_20 (0x3000+0x4c)
+#define GT_R_TALK_20 (0x3000+0x4d)
+#define S21_START_ON (0x3000+0x4e)
+#define GT_LEFT_EXIT_21 (0x3000+0x4f)
+#define EL21_ACTION (0x3000+0x50)
+#define GT_LAMBS_BOOKS (0x3000+0x51)
+#define LAMBS_BOOKS_ACTION (0x3000+0x52)
+#define GT_LAMBS_CHAIR (0x3000+0x53)
+#define LAMBS_CHAIR_ACTION (0x3000+0x54)
+#define GT_DISPENSOR (0x3000+0x55)
+#define DISPENSOR_ACTION (0x3000+0x56)
+#define CAT_FOOD_LOGIC (0x3000+0x57)
+#define GT_CAT_FOOD (0x3000+0x58)
+#define CAT_FOOD_ACTION (0x3000+0x59)
+#define VIDEO_LOGIC (0x3000+0x5a)
+#define GT_VIDEO (0x3000+0x5b)
+#define VIDEO_ACTION (0x3000+0x5c)
+#define GT_CASSETTE (0x3000+0x5d)
+#define CASSETTE_ACTION (0x3000+0x5e)
+#define GT_BIG_PICT1 (0x3000+0x5f)
+#define BIG_PICT1_ACTION (0x3000+0x60)
+#define GT_VIDEO_SCREEN (0x3000+0x61)
+#define VIDEO_SCREEN_ACTION (0x3000+0x62)
+#define VIDEO_SCREEN_LOGIC (0x3000+0x63)
+#define GT_BIG_PICT2 (0x3000+0x64)
+#define BIG_PICT2_ACTION (0x3000+0x65)
+#define GT_BIG_PICT3 (0x3000+0x66)
+#define BIG_PICT3_ACTION (0x3000+0x67)
+#define CAT_LOGIC (0x3000+0x68)
+#define GT_CAT (0x3000+0x69)
+#define CAT_ACTION (0x3000+0x6a)
+#define INNER_LAMB_DOOR_LOGIC (0x3000+0x6b)
+#define S22_START_ON (0x3000+0x6c)
+#define GT_RIGHT_EXIT_22 (0x3000+0x6d)
+#define ER22_ACTION (0x3000+0x6e)
+#define GT_LAMB_BED (0x3000+0x6f)
+#define BED_ACTION (0x3000+0x70)
+#define GT_LAMB_TV (0x3000+0x71)
+#define LAMB_TV_ACTION (0x3000+0x72)
+#define GT_FISH_TANK (0x3000+0x73)
+#define FISH_TANK_ACTION (0x3000+0x74)
+#define FISH_POSTER_ACTION (0x3000+0x75)
+#define PILLOW_LOGIC (0x3000+0x76)
+#define GT_PILLOW (0x3000+0x77)
+#define PILLOW_ACTION (0x3000+0x78)
+#define GT_MAGAZINE (0x3000+0x79)
+#define MAGAZINE_ACTION (0x3000+0x7a)
+#define FISH_LOGIC (0x3000+0x7b)
+#define GT_REICH_CHAIR (0x3000+0x7c)
+#define REICH_CHAIR_ACTION (0x3000+0x7d)
+#define GT_CABINET (0x3000+0x7e)
+#define CABINET_ACTION (0x3000+0x7f)
+#define GT_CERT (0x3000+0x80)
+#define CERT_ACTION (0x3000+0x81)
+#define GT_REICH_PICTURE (0x3000+0x82)
+#define REICH_PICTURE_ACTION (0x3000+0x83)
+#define GT_FISH_FOOD (0x3000+0x84)
+#define FISH_FOOD_ACTION (0x3000+0x85)
+#define INNER_R_DOOR_LOGIC (0x3000+0x86)
+#define S23_LEFT_ON (0x3000+0x87)
+#define S23_ANCHOR_ON (0x3000+0x88)
+#define S23_TRAVEL_ON (0x3000+0x89)
+#define GT_LEFT_EXIT_23 (0x3000+0x8a)
+#define EL23_ACTION (0x3000+0x8b)
+#define GT_ANCHOR_EXIT_23 (0x3000+0x8c)
+#define ANCHOR23_ACTION (0x3000+0x8d)
+#define GT_TRAVEL_FAIL (0x3000+0x8e)
+#define GT_TRAVEL_EXIT_23 (0x3000+0x8f)
+#define TRAVEL_23_ACTION (0x3000+0x90)
+#define GT_BIN_23 (0x3000+0x91)
+#define BIN_23_ACTION (0x3000+0x92)
+#define GT_SCULPTURE (0x3000+0x93)
+#define SCULPTURE_ACTION (0x3000+0x94)
+#define GT_LINK_23 (0x3000+0x95)
+#define LINK_23_ACTION (0x3000+0x96)
+#define GT_WRECK_23 (0x3000+0x97)
+#define WRECK_23_ACTION (0x3000+0x98)
+#define GT_SMALL_23 (0x3000+0x99)
+#define SML_EXIT_S23_ACTION (0x3000+0x9a)
+#define S24_LEFT_ON (0x3000+0x9b)
+#define GT_LEFT_EXIT_24 (0x3000+0x9c)
+#define EL24_ACTION (0x3000+0x9d)
+#define GT_LONDON_POSTER (0x3000+0x9e)
+#define LONDON_ACTION (0x3000+0x9f)
+#define GT_NEW_YORK (0x3000+0xa0)
+#define NEW_YORK_ACTION (0x3000+0xa1)
+#define GT_MURAL (0x3000+0xa2)
+#define MURAL_ACTION (0x3000+0xa3)
+#define GT_PIDGEONS (0x3000+0xa4)
+#define PIDGEONS_ACTION (0x3000+0xa5)
+#define TREVOR_LOGIC (0x3000+0xa6)
+#define TREVOR_SSS (0x3000+0xa7)
+#define TREVOR_ACTION (0x3000+0xa8)
+#define GT_TREVOR (0x3000+0xa9)
+#define TICKET_LOGIC (0x3000+0xaa)
+#define TICKET_ACTION (0x3000+0xab)
+#define GT_TICKET (0x3000+0xac)
+#define GT_GLOBE (0x3000+0xad)
+#define GLOBE_ACTION (0x3000+0xae)
+#define GLOBE_LOGIC (0x3000+0xaf)
+#define S25_LEFT_ON (0x3000+0xb0)
+#define GT_ANCHOR_EXIT_25 (0x3000+0xb1)
+#define ANCHOR25_ACTION (0x3000+0xb2)
+#define ANCHOR_LOGIC (0x3000+0xb3)
+#define ANCHOR_SSS (0x3000+0xb4)
+#define ANCHOR_ACTION (0x3000+0xb5)
+#define GT_ANCHOR (0x3000+0xb6)
+#define ANCHOR_MISSION (0x3000+0xb7)
+#define JOEY_PC_MISSION (0x3000+0xb8)
+#define GT_ANCHOR_PC (0x3000+0xb9)
+#define HOOK_LOGIC (0x3000+0xba)
+#define GT_STATUE_25 (0x3000+0xbb)
+#define STATUE_25_ACTION (0x3000+0xbc)
+#define HOOK_MISSION (0x3000+0xbd)
+#define LAZER_25_LOGIC (0x3000+0xbe)
+#define SPARK_25_LOGIC (0x3000+0xbf)
+#define GT_HOOK (0x3000+0xc0)
+#define HOOK_ACTION (0x3000+0xc1)
+#define GT_SALES_CHART (0x3000+0xc2)
+#define SALES_CHART_ACTION (0x3000+0xc3)
+#define S26_LEFT_ON (0x3000+0xc4)
+#define S26_RIGHT_ON (0x3000+0xc5)
+#define GT_RIGHT_EXIT_26 (0x3000+0xc6)
+#define ER26_ACTION (0x3000+0xc7)
+#define START_26 (0x3000+0xc8)
+#define GT_POSTER (0x3000+0xc9)
+#define POSTER1_ACTION (0x3000+0xca)
+#define POSTER2_ACTION (0x3000+0xcb)
+#define POSTER3_ACTION (0x3000+0xcc)
+#define POSTER4_ACTION (0x3000+0xcd)
+#define GT_PLANT (0x3000+0xce)
+#define PLANT_26_ACTION (0x3000+0xcf)
+#define NU_GT_HOLO (0x3000+0xd0)
+#define GT_HOLO (0x3000+0xd1)
+#define HOLO_ACTION (0x3000+0xd2)
+#define HELGA_LOGIC (0x3000+0xd3)
+#define JOEY_HELGA_MISSION (0x3000+0xd4)
+#define GT_LEFT_EXIT_26 (0x3000+0xd5)
+#define EL26_ACTION (0x3000+0xd6)
+#define HELGA_ACTION (0x3000+0xd7)
+#define BIO_DOOR_LOGIC (0x3000+0xd8)
+#define GT_BIO_DOOR (0x3000+0xd9)
+#define BIO_DOOR_ACTION (0x3000+0xda)
+#define GT_LEAFLET (0x3000+0xdb)
+#define LEAFLET_LOGIC (0x3000+0xdc)
+#define LEAFLET_ACTION (0x3000+0xdd)
+#define S27_RIGHT_ON (0x3000+0xde)
+#define GT_RIGHT_EXIT_27 (0x3000+0xdf)
+#define ER27_ACTION (0x3000+0xe0)
+#define GT_CHART1 (0x3000+0xe1)
+#define CHART1_ACTION (0x3000+0xe2)
+#define GT_CHART2 (0x3000+0xe3)
+#define CHART2_ACTION (0x3000+0xe4)
+#define GT_GAS (0x3000+0xe5)
+#define GAS_ACTION (0x3000+0xe6)
+#define GT_CHAIR_27 (0x3000+0xe7)
+#define CHAIR_27_ACTION (0x3000+0xe8)
+#define GT_FLOOR_FROM_CHAIR (0x3000+0xe9)
+#define GT_SCANNER_27 (0x3000+0xea)
+#define SCANNER_27_ACTION (0x3000+0xeb)
+#define GT_L_TALK_27 (0x3000+0xec)
+#define RIGHT_FAIL_27 (0x3000+0xed)
+#define GT_R_TALK_27 (0x3000+0xee)
+#define BURKE_LOGIC (0x3000+0xef)
+#define GT_MEDI_COMP (0x3000+0xf0)
+#define MEDI_COMP_ACTION (0x3000+0xf1)
+#define BURKE_1 (0x3000+0xf2)
+#define BURKE_2 (0x3000+0xf3)
+#define DR_BURKE_1 (0x3000+0xf4)
+#define SCANNER_27_LOGIC (0x3000+0xf5)
+#define HELMET_LOGIC (0x3000+0xf6)
+#define BODY_SSS (0x3000+0xf7)
+#define BODY_ACTION (0x3000+0xf8)
+#define BUSY_BODY (0x3000+0xf9)
+#define GT_BODY (0x3000+0xfa)
+#define GT_HELMET (0x3000+0xfb)
+#define HELMET_ACTION (0x3000+0xfc)
+#define GLASS_SLOT_LOGIC (0x3000+0xfd)
+#define GLASS_MISSION (0x3000+0xfe)
+#define MEDIC_LOGIC (0x3000+0xff)
+#define S28_RIGHT_ON (0x3000+0x100)
+#define GT_RIGHT_EXIT_28 (0x3000+0x101)
+#define ER28_ACTION (0x3000+0x102)
+#define GT_LEFT_EXIT_28 (0x3000+0x103)
+#define EL28_ACTION (0x3000+0x104)
+#define S28_LEFT_ON (0x3000+0x105)
+#define GT_DUSTBIN_28 (0x3000+0x106)
+#define DUSTBIN_ACTION (0x3000+0x107)
+#define UP_28_FAIL (0x3000+0x108)
+#define GT_UP_EXIT_28 (0x3000+0x109)
+#define EU28_ACTION (0x3000+0x10a)
+#define S28_UP_ON (0x3000+0x10b)
+#define LEFT_FAIL_28 (0x3000+0x10c)
+#define GT_L_TALK_28 (0x3000+0x10d)
+#define RIGHT_FAIL_28 (0x3000+0x10e)
+#define GT_R_TALK_28 (0x3000+0x10f)
+#define GT_SML_R_28 (0x3000+0x110)
+#define R_28_SML_ACTION (0x3000+0x111)
+#define GT_SML_L_28 (0x3000+0x112)
+#define L_28_SML_ACTION (0x3000+0x113)
+#define SML_RIGHT_28 (0x3000+0x114)
+#define SML_LEFT_28 (0x3000+0x115)
+#define LIFT_28_LOGIC (0x3000+0x116)
+#define GT_LIFT_28 (0x3000+0x117)
+#define LIFT_28_ACTION (0x3000+0x118)
+#define S28_SLOT_ACTION (0x3000+0x119)
+#define GT_SLOT_28 (0x3000+0x11a)
+#define S29_LIFT_LOGIC (0x3000+0x11b)
+#define JOEY_TO_LIFT29 (0x3000+0x11c)
+#define LIFT_29_ACTION (0x3000+0x11d)
+#define GT_29_LIFT (0x3000+0x11e)
+#define S29_SLOT_ACTION (0x3000+0x11f)
+#define GT_29_CARD_SLOT (0x3000+0x120)
+#define JOEY_OUT_OF_LIFT (0x3000+0x121)
+#define GT_RIGHT_EXIT_29 (0x3000+0x122)
+#define ER29_ACTION (0x3000+0x123)
+#define S29_RIGHT_ON (0x3000+0x124)
+#define GT_LEFT_EXIT_29 (0x3000+0x125)
+#define EL29_ACTION (0x3000+0x126)
+#define S29_LEFT_ON (0x3000+0x127)
+#define OTHER_LIFT_WAIT (0x3000+0x128)
+#define GT_LIFT_WAIT (0x3000+0x129)
+#define LAMB_BELL_LOGIC (0x3000+0x12a)
+#define LAMB_LEAVE_GARDEN (0x3000+0x12b)
+#define LAMB_START_29 (0x3000+0x12c)
+#define LEFT_FAIL_29 (0x3000+0x12d)
+#define GT_L_TALK_29 (0x3000+0x12e)
+#define RIGHT_FAIL_29 (0x3000+0x12f)
+#define GT_R_TALK_29 (0x3000+0x130)
+#define GT_CABLE_29 (0x3000+0x131)
+#define CABLE_29_ACTION (0x3000+0x132)
+#define GT_SML_R_29 (0x3000+0x133)
+#define R_29_SML_ACTION (0x3000+0x134)
+#define GT_SML_L_29 (0x3000+0x135)
+#define L_29_SML_ACTION (0x3000+0x136)
+#define SML_RIGHT_29 (0x3000+0x137)
+#define SML_LEFT_29 (0x3000+0x138)
+#define DANI_SPEECH (0x4000+0x1)
+#define HENRI_SPEECH (0x4000+0x2)
+#define BUZZER_SPEECH (0x4000+0x3)
+#define JUKEBOX_SPEECH (0x4000+0x4)
+#define VINCENT_SPEECH (0x0000+0x40)
+#define EDDIE_SPEECH (0x0000+0x41)
+#define BLUNT_SPEECH (0x0000+0x42)
+#define BARRY_SPEECH (0x4000+0x5)
+#define COLSTON_SPEECH (0x0000+0x43)
+#define GALL_SPEECH (0x0000+0x44)
+#define BABS_SPEECH (0x4000+0x6)
+#define CHUTNEY_SPEECH (0x0000+0x45)
+#define DOG_BARK_LOGIC (0x4000+0x7)
+#define SPUNKY_SMELLS_FOOD (0x4000+0x8)
+#define SPUNKY_EXTRA (0x4000+0x9)
+#define SPUNKY_LOGIC (0x4000+0xa)
+#define SPUNKY_EAT_FOOD (0x4000+0xb)
+#define SPUNKY_BARK_AT_FOSTER (0x4000+0xc)
+#define SPUNKY_GO_HOME (0x4000+0xd)
+#define SPUNKY_SEE_VIDEO (0x4000+0xe)
+#define DANIELLE_SSS (0x4000+0xf)
+#define GT_SC31_DANIELLE (0x4000+0x10)
+#define SC31_DANI_ACTION (0x4000+0x11)
+#define DANI_CHAT_TO_GUARD (0x4000+0x12)
+#define GT_DANI_WAIT (0x4000+0x13)
+#define DANIELLE_EXTRA (0x4000+0x14)
+#define DANIELLE_LOGIC (0x4000+0x15)
+#define GT_SC32_DANIELLE_AT_LIFT (0x4000+0x16)
+#define SC32_DANIELLE_AT_LIFT_ACTION (0x4000+0x17)
+#define DANIELLE_GO_HOME (0x4000+0x18)
+#define GT_SC38_DANIELLE (0x4000+0x19)
+#define SC38_DANIELLE_ACTION (0x4000+0x1a)
+#define GT_SC38_HAND_SET (0x4000+0x1b)
+#define DANI_ANSWER_PHONE (0x4000+0x1c)
+#define FOSTER_VISIT_DANI (0x4000+0x1d)
+#define GN_SC30_LEFT_TALK (0x4000+0x1e)
+#define GT_SC30_LEFT_TALK (0x4000+0x1f)
+#define GN_SC30_RIGHT_TALK (0x4000+0x20)
+#define GT_SC30_RIGHT_TALK (0x4000+0x21)
+#define SC30_EXIT_31_WALK_ON (0x4000+0x22)
+#define GT_SC30_EXIT_31 (0x4000+0x23)
+#define SC30_EXIT_31_ACTION (0x4000+0x24)
+#define SC30_EXIT_33_WALK_ON (0x4000+0x25)
+#define GT_SC30_EXIT_33 (0x4000+0x26)
+#define SC30_EXIT_33_ACTION (0x4000+0x27)
+#define SC30_COURT_DOOR_MOUSE_ON (0x4000+0x28)
+#define SC30_COURT_DOOR_WALK_ON (0x4000+0x29)
+#define GT_SC30_COURT_DOOR (0x4000+0x2a)
+#define SC30_COURT_DOOR_ACTION (0x4000+0x2b)
+#define SC30_COURT_DOOR_LOGIC (0x4000+0x2c)
+#define GT_SC30_NOTICE (0x4000+0x2d)
+#define SC30_NOTICE_ACTION (0x4000+0x2e)
+#define GT_SC30_STATUE_1 (0x4000+0x2f)
+#define SC30_STATUE_1_ACTION (0x4000+0x30)
+#define GT_SC30_STATUE_2 (0x4000+0x31)
+#define SC30_STATUE_2_ACTION (0x4000+0x32)
+#define SC30_HENRI_LOGIC (0x4000+0x33)
+#define SC30_HENRI_SSS (0x4000+0x34)
+#define GT_SC30_HENRI (0x4000+0x35)
+#define SC30_HENRI_ACTION (0x4000+0x36)
+#define SC30_EXIT_36_WALK_ON (0x4000+0x37)
+#define GT_SC30_EXIT_36 (0x4000+0x38)
+#define SC30_EXIT_36_ACTION (0x4000+0x39)
+#define GN_SC31_LEFT_TALK (0x4000+0x3a)
+#define GT_SC31_LEFT_TALK (0x4000+0x3b)
+#define GN_SC31_RIGHT_TALK (0x4000+0x3c)
+#define GT_SC31_RIGHT_TALK (0x4000+0x3d)
+#define SC31_EXIT_30_WALK_ON (0x4000+0x3e)
+#define GT_SC31_EXIT_30 (0x4000+0x3f)
+#define SC31_EXIT_30_ACTION (0x4000+0x40)
+#define SC31_EXIT_32_WALK_ON (0x4000+0x41)
+#define GT_SC31_EXIT_32 (0x4000+0x42)
+#define SC31_EXIT_32_ACTION (0x4000+0x43)
+#define GT_SC31_LIFT_SLOT (0x4000+0x44)
+#define SC31_LIFT_SLOT_ACTION (0x4000+0x45)
+#define GT_SC31_LIFT (0x4000+0x46)
+#define SC31_LIFT_ACTION (0x4000+0x47)
+#define SC31_LIFT_LOGIC (0x4000+0x48)
+#define SC31_GUARD_RESCUE_DOG (0x4000+0x49)
+#define GT_SC31_END_OF_ROPE (0x4000+0x4a)
+#define SC31_END_OF_ROPE_ACTION (0x4000+0x4b)
+#define SC31_ROPE_LOGIC (0x4000+0x4c)
+#define SC31_BRICKS_LOGIC (0x4000+0x4d)
+#define SC31_PLANK_LOGIC (0x4000+0x4e)
+#define SC31_BISCUITS_LOGIC (0x4000+0x4f)
+#define GT_SC31_BRICKS (0x4000+0x50)
+#define SC31_BRICKS_ACTION (0x4000+0x51)
+#define GN_SC31_PLANK (0x4000+0x52)
+#define GT_SC31_PLANK (0x4000+0x53)
+#define SC31_PLANK_ACTION (0x4000+0x54)
+#define SC31_GUARD_LOGIC (0x4000+0x55)
+#define SC31_GUARD_CHATTING_LOGIC (0x4000+0x56)
+#define SC31_GUARD_SSS (0x4000+0x57)
+#define GN_SC31_GUARD (0x4000+0x58)
+#define GT_SC31_GUARD (0x4000+0x59)
+#define SC31_GUARD_ACTION (0x4000+0x5a)
+#define SC31_GUARD_CHATTING_ACTION (0x4000+0x5b)
+#define SC31_EXIT_39_WALK_ON (0x4000+0x5c)
+#define GT_SC31_EXIT_39 (0x4000+0x5d)
+#define SC31_EXIT_39_ACTION (0x4000+0x5e)
+#define SC31_JOEY_LOGIC (0x4000+0x5f)
+#define GT_SC31_JOEY (0x4000+0x60)
+#define SC31_JOEY_ACTION (0x4000+0x61)
+#define SC31_AT_WATCHER_LOGIC (0x4000+0x62)
+#define GN_SC32_LEFT_TALK (0x4000+0x63)
+#define GT_SC32_LEFT_TALK (0x4000+0x64)
+#define GN_SC32_RIGHT_TALK (0x4000+0x65)
+#define GT_SC32_RIGHT_TALK (0x4000+0x66)
+#define SC32_EXIT_31_WALK_ON (0x4000+0x67)
+#define GT_SC32_EXIT_31 (0x4000+0x68)
+#define SC32_EXIT_31_ACTION (0x4000+0x69)
+#define SC32_EXIT_33_WALK_ON (0x4000+0x6a)
+#define GT_SC32_EXIT_33 (0x4000+0x6b)
+#define SC32_EXIT_33_ACTION (0x4000+0x6c)
+#define GT_SC32_TERMINAL (0x4000+0x6d)
+#define SC32_TERMINAL_ACTION (0x4000+0x6e)
+#define GT_SC32_PLANT_1 (0x4000+0x6f)
+#define SC32_PLANT_1_ACTION (0x4000+0x70)
+#define GT_SC32_PLANT_2 (0x4000+0x71)
+#define SC32_PLANT_2_ACTION (0x4000+0x72)
+#define GT_SC32_PLANT_3 (0x4000+0x73)
+#define SC32_PLANT_3_ACTION (0x4000+0x74)
+#define SC32_BUZZER_LOGIC (0x4000+0x75)
+#define GT_SC32_BUZZER (0x4000+0x76)
+#define SC32_BUZZER_ACTION (0x4000+0x77)
+#define GT_SC32_LIFT (0x4000+0x78)
+#define SC32_LIFT_ACTION (0x4000+0x79)
+#define SC32_LIFT_LOGIC (0x4000+0x7a)
+#define SC32_VINCENT_LOGIC (0x4000+0x7b)
+#define SC32_VINCENT_SSS (0x4000+0x7c)
+#define GT_SC32_VINCENT (0x4000+0x7d)
+#define SC32_VINCENT_ACTION (0x4000+0x7e)
+#define SC32_GARDENER_LOGIC (0x4000+0x7f)
+#define SC32_GARDENER_SSS (0x4000+0x80)
+#define GT_SC32_GARDENER (0x4000+0x81)
+#define SC32_GARDENER_ACTION (0x4000+0x82)
+#define GN_SC33_LEFT_TALK (0x4000+0x83)
+#define GT_SC33_LEFT_TALK (0x4000+0x84)
+#define GN_SC33_RIGHT_TALK (0x4000+0x85)
+#define GT_SC33_RIGHT_TALK (0x4000+0x86)
+#define SC33_EXIT_30_WALK_ON (0x4000+0x87)
+#define GT_SC33_EXIT_30 (0x4000+0x88)
+#define SC33_EXIT_30_ACTION (0x4000+0x89)
+#define SC33_EXIT_32_WALK_ON (0x4000+0x8a)
+#define GT_SC33_EXIT_32 (0x4000+0x8b)
+#define SC33_EXIT_32_ACTION (0x4000+0x8c)
+#define SC33_SHED_DOOR_LOGIC (0x4000+0x8d)
+#define SC33_SHED_DOOR_MOUSE_ON (0x4000+0x8e)
+#define SC33_SHED_DOOR_WALK_ON (0x4000+0x8f)
+#define GT_SC33_SHED_DOOR (0x4000+0x90)
+#define SC33_SHED_DOOR_ACTION (0x4000+0x91)
+#define GT_SC33_LOCK (0x4000+0x92)
+#define SC33_LOCK_ACTION (0x4000+0x93)
+#define SC34_DOOR_WALK_ON (0x4000+0x94)
+#define GT_SC34_DOOR (0x4000+0x95)
+#define SC34_DOOR_ACTION (0x4000+0x96)
+#define GT_SC34_SECATEURS (0x4000+0x97)
+#define SC34_SECATEURS_ACTION (0x4000+0x98)
+#define SC34_SECATEURS_LOGIC (0x4000+0x99)
+#define GT_SC34_TKT_MACHINE (0x4000+0x9a)
+#define SC34_TKT_MACHINE_ACTION (0x4000+0x9b)
+#define GT_SC34_MAP (0x4000+0x9c)
+#define SC34_MAP_ACTION (0x4000+0x9d)
+#define GT_SC34_BRICKS (0x4000+0x9e)
+#define SC34_BRICKS_ACTION (0x4000+0x9f)
+#define GN_SC36_LEFT_TALK (0x4000+0xa0)
+#define GT_SC36_LEFT_TALK (0x4000+0xa1)
+#define GN_SC36_RIGHT_TALK (0x4000+0xa2)
+#define GT_SC36_RIGHT_TALK (0x4000+0xa3)
+#define GT_SC36_LOW_FLOOR (0x4000+0xa4)
+#define GT_SC36_FLOOR (0x4000+0xa5)
+#define SC36_EXIT_30_WALK_ON (0x4000+0xa6)
+#define GT_SC36_EXIT_30 (0x4000+0xa7)
+#define SC36_EXIT_30_ACTION (0x4000+0xa8)
+#define GT_SC36_SENSOR (0x4000+0xa9)
+#define SC36_SENSOR_ACTION (0x4000+0xaa)
+#define SC36_DOOR_WALK_ON (0x4000+0xab)
+#define GT_SC36_DOOR (0x4000+0xac)
+#define SC36_DOOR_ACTION (0x4000+0xad)
+#define SC36_DOOR_LOGIC (0x4000+0xae)
+#define SC36_BAND_LOGIC (0x4000+0xaf)
+#define GT_SC36_BAND (0x4000+0xb0)
+#define SC36_BAND_ACTION (0x4000+0xb1)
+#define SC36_JUKE_LIGHT_LOGIC (0x4000+0xb2)
+#define SC36_COLSTON_FIX_JUKEBOX (0x4000+0xb3)
+#define PLAY_TUNE_1 (0x4000+0xb4)
+#define PLAY_TUNE_2 (0x4000+0xb5)
+#define PLAY_TUNE_3 (0x4000+0xb6)
+#define SC36_JUKEBOX_SSS (0x4000+0xb7)
+#define SC36_JUKEBOX_LOGIC (0x4000+0xb8)
+#define GT_SC36_JUKEBOX (0x4000+0xb9)
+#define SC36_JUKEBOX_ACTION (0x4000+0xba)
+#define SC36_BARMAN_LOGIC (0x4000+0xbb)
+#define SC36_BARMAN_SSS (0x4000+0xbc)
+#define GT_SC36_BARMAN (0x4000+0xbd)
+#define SC36_BARMAN_ACTION (0x4000+0xbe)
+#define SC36_COLSTON_LOGIC (0x4000+0xbf)
+#define SC36_COLSTON_SSS (0x4000+0xc0)
+#define GT_SC36_COLSTON (0x4000+0xc1)
+#define SC36_COLSTON_ACTION (0x4000+0xc2)
+#define SC36_GALLAGHER_LOGIC (0x4000+0xc3)
+#define SC36_GALLAGHER_SSS (0x4000+0xc4)
+#define GT_SC36_GALLAGHER (0x4000+0xc5)
+#define SC36_GALLAGHER_ACTION (0x4000+0xc6)
+#define SC36_CARDS_LOGIC (0x4000+0xc7)
+#define GT_SC36_GLASS (0x4000+0xc8)
+#define SC36_COLSTON_PROTEST (0x4000+0xc9)
+#define SC36_GLASS_ACTION (0x4000+0xca)
+#define MOVE_BABS (0x4000+0xcb)
+#define SC36_BABS_LOGIC (0x4000+0xcc)
+#define BABS_LOGIC_AMIGA (0x4000+0xcd)
+#define GT_BABS_AMIGA (0x4000+0xce)
+#define BABS_SSS_AMIGA (0x4000+0xcf)
+#define BABS_ACTION_AMIGA (0x4000+0xd0)
+#define GT_SC37_SENSOR (0x4000+0xd1)
+#define SC37_SENSOR_ACTION (0x4000+0xd2)
+#define SC37_DOOR_WALK_ON (0x4000+0xd3)
+#define GT_SC37_DOOR (0x4000+0xd4)
+#define SC37_DOOR_ACTION (0x4000+0xd5)
+#define SC37_DOOR_LOGIC (0x4000+0xd6)
+#define GT_SC37_GRILL (0x4000+0xd7)
+#define SC37_GRILL_ACTION (0x4000+0xd8)
+#define SC37_GRILL_LOGIC (0x4000+0xd9)
+#define GT_SC37_FLIMSY_BOX (0x4000+0xda)
+#define SC37_FLIMSY_BOX_ACTION (0x4000+0xdb)
+#define STEP_OFF_BOX (0x4000+0xdc)
+#define GT_SC37_BIG_BOX (0x4000+0xdd)
+#define SC37_BIG_BOX_ACTION (0x4000+0xde)
+#define GT_SC37_LID (0x4000+0xdf)
+#define SC37_LID_ACTION (0x4000+0xe0)
+#define PUT_LID_BACK (0x4000+0xe1)
+#define USE_LID_ON_FLIMSY_BOX (0x4000+0xe2)
+#define SC37_LID_LOGIC (0x4000+0xe3)
+#define GT_SC37_WINE_RACK (0x4000+0xe4)
+#define SC37_WINE_RACK_ACTION (0x4000+0xe5)
+#define GN_SC38_LEFT_TALK (0x4000+0xe6)
+#define GT_SC38_LEFT_TALK (0x4000+0xe7)
+#define GN_SC38_RIGHT_TALK (0x4000+0xe8)
+#define GT_SC38_RIGHT_TALK (0x4000+0xe9)
+#define GT_SC38_LIFT (0x4000+0xea)
+#define SC38_LIFT_ACTION (0x4000+0xeb)
+#define SC38_LIFT_LOGIC (0x4000+0xec)
+#define GT_SC38_STATUE (0x4000+0xed)
+#define SC38_STATUE_ACTION (0x4000+0xee)
+#define GT_SC38_VIDEO (0x4000+0xef)
+#define SC38_VIDEO_ACTION (0x4000+0xf0)
+#define SC38_VIDEO_LOGIC (0x4000+0xf1)
+#define GT_SC38_MONITOR (0x4000+0xf2)
+#define SC38_MONITOR_ACTION (0x4000+0xf3)
+#define SC38_MONITOR_LOGIC (0x4000+0xf4)
+#define GT_SC38_SOFA (0x4000+0xf5)
+#define SC38_SOFA_ACTION (0x4000+0xf6)
+#define GT_SC38_DOG_TRAY (0x4000+0xf7)
+#define SC38_DOG_TRAY_ACTION (0x4000+0xf8)
+#define GT_SC38_BISCUITS (0x4000+0xf9)
+#define SC38_BISCUITS_ACTION (0x4000+0xfa)
+#define SC38_RINGER_LOGIC (0x4000+0xfb)
+#define SC39_EXIT_31_WALK_ON (0x4000+0xfc)
+#define GT_SC39_EXIT_31 (0x4000+0xfd)
+#define SC39_EXIT_31_ACTION (0x4000+0xfe)
+#define GT_SC39_EXIT_40 (0x4000+0xff)
+#define SC39_EXIT_40_ACTION (0x4000+0x100)
+#define SC39_EXIT_41_WALK_ON (0x4000+0x101)
+#define GT_SC39_EXIT_41 (0x4000+0x102)
+#define SC39_EXIT_41_ACTION (0x4000+0x103)
+#define GT_SC39_WALTERS (0x4000+0x104)
+#define SC39_WALTERS_ACTION (0x4000+0x105)
+#define GT_SC40_EXIT_39 (0x4000+0x106)
+#define SC40_EXIT_39_ACTION (0x4000+0x107)
+#define GT_SC40_CABINET (0x4000+0x108)
+#define SC40_CABINET_ACTION (0x4000+0x109)
+#define GT_SC40_TROLLEY (0x4000+0x10a)
+#define SC40_TROLLEY_ACTION (0x4000+0x10b)
+#define GT_SC40_LOCKER_1 (0x4000+0x10c)
+#define SC40_LOCKER_1_ACTION (0x4000+0x10d)
+#define GT_SC40_LOCKER_2 (0x4000+0x10e)
+#define SC40_LOCKER_2_ACTION (0x4000+0x10f)
+#define GT_SC40_LOCKER_3 (0x4000+0x110)
+#define SC40_LOCKER_3_ACTION (0x4000+0x111)
+#define GT_SC40_LOCKER_4 (0x4000+0x112)
+#define SC40_LOCKER_4_ACTION (0x4000+0x113)
+#define GT_SC40_LOCKER_5 (0x4000+0x114)
+#define SC40_LOCKER_5_ACTION (0x4000+0x115)
+#define GT_SC40_BODY_1 (0x4000+0x116)
+#define SC40_BODY_1_ACTION (0x4000+0x117)
+#define GT_SC40_BODY_2 (0x4000+0x118)
+#define SC40_BODY_2_ACTION (0x4000+0x119)
+#define GT_SC40_BODY_3 (0x4000+0x11a)
+#define SC40_BODY_3_ACTION (0x4000+0x11b)
+#define GT_SC40_BODY_4 (0x4000+0x11c)
+#define SC40_BODY_4_ACTION (0x4000+0x11d)
+#define GT_SC40_BODY_5 (0x4000+0x11e)
+#define SC40_BODY_5_ACTION (0x4000+0x11f)
+#define SC40_LOCKER_1_LOGIC (0x4000+0x120)
+#define SC40_LOCKER_2_LOGIC (0x4000+0x121)
+#define SC40_LOCKER_3_LOGIC (0x4000+0x122)
+#define SC40_LOCKER_4_LOGIC (0x4000+0x123)
+#define SC40_LOCKER_5_LOGIC (0x4000+0x124)
+#define SC41_EXIT_39_WALK_ON (0x4000+0x125)
+#define GT_SC41_EXIT_39 (0x4000+0x126)
+#define SC41_EXIT_39_ACTION (0x4000+0x127)
+#define SC41_HEAT_1_LOGIC (0x4000+0x128)
+#define SC41_HEAT_2_LOGIC (0x4000+0x129)
+#define SC41_HEAT_3_LOGIC (0x4000+0x12a)
+#define FOSTER_ENTER_COURT (0x4000+0x12b)
+#define SC42_JUDGE_LOGIC (0x4000+0x12c)
+#define SC42_CLERK_LOGIC (0x4000+0x12d)
+#define SC42_PROSECUTION_LOGIC (0x4000+0x12e)
+#define SC42_JOBSWORTH_LOGIC (0x4000+0x12f)
+#define SC42_BLUNT_LOGIC (0x4000+0x130)
+#define SC42_DANI_LOGIC (0x4000+0x131)
+#define SC42_SIGN_LOGIC (0x4000+0x132)
+#define SC44_EXIT_45_WALK_ON (0x4000+0x133)
+#define GT_SC44_EXIT_45 (0x4000+0x134)
+#define SC44_EXIT_45_ACTION (0x4000+0x135)
+#define GT_SC44_GRILL (0x4000+0x136)
+#define SC44_GRILL_ACTION (0x4000+0x137)
+#define GT_SC44_RUBBLE (0x4000+0x138)
+#define SC44_RUBBLE_ACTION (0x4000+0x139)
+#define SC45_EXIT_44_WALK_ON (0x4000+0x13a)
+#define GT_SC45_EXIT_44 (0x4000+0x13b)
+#define SC45_EXIT_44_ACTION (0x4000+0x13c)
+#define SC45_EXIT_46_WALK_ON (0x4000+0x13d)
+#define GT_SC45_EXIT_46 (0x4000+0x13e)
+#define SC45_EXIT_46_ACTION (0x4000+0x13f)
+#define SC45_EXIT_47_WALK_ON (0x4000+0x140)
+#define GT_SC45_EXIT_47 (0x4000+0x141)
+#define SC45_EXIT_47_ACTION (0x4000+0x142)
+#define SC46_EXIT_45_WALK_ON (0x4000+0x143)
+#define GT_SC46_EXIT_45 (0x4000+0x144)
+#define SC46_EXIT_45_ACTION (0x4000+0x145)
+#define GT_SC46_RUBBLE (0x4000+0x146)
+#define SC46_RUBBLE_ACTION (0x4000+0x147)
+#define SC47_EXIT_45_WALK_ON (0x4000+0x148)
+#define GT_SC47_EXIT_45 (0x4000+0x149)
+#define SC47_EXIT_45_ACTION (0x4000+0x14a)
+#define SC47_EXIT_48_WALK_ON (0x4000+0x14b)
+#define GT_SC47_EXIT_48 (0x4000+0x14c)
+#define SC47_EXIT_48_ACTION (0x4000+0x14d)
+#define SC48_EXIT_47_WALK_ON (0x4000+0x14e)
+#define GT_SC48_EXIT_47 (0x4000+0x14f)
+#define SC48_EXIT_47_ACTION (0x4000+0x150)
+#define SC48_EXIT_65_WALK_ON (0x4000+0x151)
+#define GT_SC48_EXIT_65 (0x4000+0x152)
+#define SC48_EXIT_65_ACTION (0x4000+0x153)
+#define GT_SC48_SOCKET (0x4000+0x154)
+#define SC48_SOCKET_ACTION (0x4000+0x155)
+#define SC48_SOCKET_LOGIC (0x4000+0x156)
+#define GT_SC48_HOLE (0x4000+0x157)
+#define SC48_HOLE_ACTION (0x4000+0x158)
+#define FOSTER_SEES_EYES (0x4000+0x159)
+#define SC48_HOLE_LOGIC (0x4000+0x15a)
+#define SC48_EYES_LOGIC (0x4000+0x15b)
+#define SC65_EXIT_48_WALK_ON (0x4000+0x15c)
+#define GT_SC65_EXIT_48 (0x4000+0x15d)
+#define SC65_EXIT_48_ACTION (0x4000+0x15e)
+#define GT_SC65_EXIT_66 (0x4000+0x15f)
+#define GT_SC65_POSTER1 (0x4000+0x160)
+#define SC65_POSTER1_ACTION (0x4000+0x161)
+#define GT_SC65_POSTER2 (0x4000+0x162)
+#define SC65_POSTER2_ACTION (0x4000+0x163)
+#define GT_SC65_SIGN (0x4000+0x164)
+#define SC65_SIGN_ACTION (0x4000+0x165)
+#define WALTER_SPEECH (0x5000+0x1)
+#define JOEY_MEDIC (0x5000+0x2)
+#define KEN_SPEECH (0x5000+0x3)
+#define BORED_ROOM (0x5000+0x4)
+#define HOBS_END (0x0000+0x46)
+#define GT_JOEY_PARK (0x5000+0x5)
+#define JOEY_MED_EXTRA (0x5000+0x6)
+#define JOEY_MED_LOGIC (0x5000+0x7)
+#define JOEY_MISSION72_EXTRA (0x5000+0x8)
+#define JOEY_MED_MISSION72 (0x5000+0x9)
+#define GT_RECHARGING_MEDI (0x5000+0xa)
+#define RECHARGING_MEDI_ACTION (0x5000+0xb)
+#define MEDI_LOGIC (0x5000+0xc)
+#define SC67_MEND_LOGIC (0x5000+0xd)
+#define MEDI_ACTION (0x5000+0xe)
+#define GT_SC71_MEDI_SLOT (0x5000+0xf)
+#define SC71_MEDI_SLOT_ACTION (0x5000+0x10)
+#define SC66_FOSTER_GETS_CRUSHED (0x5000+0x11)
+#define SC66_TIMER_LOGIC (0x5000+0x12)
+#define SC66_DOOR_LOGIC (0x5000+0x13)
+#define SC66_STONES_LOGIC (0x5000+0x14)
+#define SC66_LO_BEAM_LOGIC (0x5000+0x15)
+#define SC66_HI_BEAM_LOGIC (0x5000+0x16)
+#define SC66_ROCK1_LOGIC (0x5000+0x17)
+#define SC66_ROCK2_LOGIC (0x5000+0x18)
+#define SC66_ROCK3_LOGIC (0x5000+0x19)
+#define SC66_HOLE_ACTION (0x5000+0x1a)
+#define SC67_PULSE1_LOGIC (0x5000+0x1b)
+#define SC67_PULSE2_LOGIC (0x5000+0x1c)
+#define SC67_PULSE3_LOGIC (0x5000+0x1d)
+#define SC67_PULSE4_LOGIC (0x5000+0x1e)
+#define SC67_ROCK_LOGIC (0x5000+0x1f)
+#define GT_SC67_BRICKWORK (0x5000+0x20)
+#define SC67_BRICKWORK_ACTION (0x5000+0x21)
+#define SC67_CLOT_LOGIC (0x5000+0x22)
+#define GN_SC67_CLOT (0x5000+0x23)
+#define GT_SC67_CLOT (0x5000+0x24)
+#define SC67_CLOT_ACTION (0x5000+0x25)
+#define GT_SC67_VEIN (0x5000+0x26)
+#define SC67_VEIN_ACTION (0x5000+0x27)
+#define SC67_DOOR_MOUSE_ON (0x5000+0x28)
+#define SC67_DOOR_LOGIC (0x5000+0x29)
+#define SC67_DOOR_WALK_ON (0x5000+0x2a)
+#define GN_SC67_DOOR (0x5000+0x2b)
+#define GT_SC67_DOOR (0x5000+0x2c)
+#define SC67_DOOR_ACTION (0x5000+0x2d)
+#define SC67_PLASTER_LOGIC (0x5000+0x2e)
+#define GT_SC67_PLASTER (0x5000+0x2f)
+#define SC67_PLASTER_ACTION (0x5000+0x30)
+#define SC67_BRICK_LOGIC (0x5000+0x31)
+#define GT_SC67_BRICK (0x5000+0x32)
+#define SC67_BRICK_ACTION (0x5000+0x33)
+#define SC67_CROWBAR_LOGIC (0x5000+0x34)
+#define GT_SC67_CROWBAR (0x5000+0x35)
+#define SC67_CROWBAR_ACTION (0x5000+0x36)
+#define GT_SC68_JOEY_WAIT (0x5000+0x37)
+#define SC68_PULSE1_LOGIC (0x5000+0x38)
+#define SC68_PULSE2_LOGIC (0x5000+0x39)
+#define SC68_PULSE3_LOGIC (0x5000+0x3a)
+#define SC68_PULSE4_LOGIC (0x5000+0x3b)
+#define SC68_PULSE5_LOGIC (0x5000+0x3c)
+#define SC68_PULSE6_LOGIC (0x5000+0x3d)
+#define SC68_SENSOR_LOGIC (0x5000+0x3e)
+#define SC68_DOOR_WALK_ON (0x5000+0x3f)
+#define GN_SC68_DOOR (0x5000+0x40)
+#define GT_SC68_DOOR (0x5000+0x41)
+#define SC68_DOOR_ACTION (0x5000+0x42)
+#define SC68_DOOR_MOUSE_ON (0x5000+0x43)
+#define SC68_DOOR_LOGIC (0x5000+0x44)
+#define GT_SC68_SENSOR (0x5000+0x45)
+#define SC68_SENSOR_ACTION (0x5000+0x46)
+#define GT_SC68_STAIRS (0x5000+0x47)
+#define SC68_STAIRS_ACTION (0x5000+0x48)
+#define SC68_EXIT_WALK_ON (0x5000+0x49)
+#define GT2_SC68_EXIT (0x5000+0x4a)
+#define GT_SC68_EXIT (0x5000+0x4b)
+#define SC68_EXIT_ACTION (0x5000+0x4c)
+#define GT_SC68_GRILL (0x5000+0x4d)
+#define SC68_GRILL_ACTION (0x5000+0x4e)
+#define SC69_PULSE1_LOGIC (0x5000+0x4f)
+#define SC69_PULSE2_LOGIC (0x5000+0x50)
+#define SC69_PULSE3_LOGIC (0x5000+0x51)
+#define SC69_PULSE4_LOGIC (0x5000+0x52)
+#define SC69_PULSE5_LOGIC (0x5000+0x53)
+#define SC69_PULSE6_LOGIC (0x5000+0x54)
+#define SC69_EXIT_WALK_ON (0x5000+0x55)
+#define GT2_SC69_EXIT (0x5000+0x56)
+#define GT_SC69_EXIT (0x5000+0x57)
+#define SC69_EXIT_ACTION (0x5000+0x58)
+#define SC69_DOOR_WALK_ON (0x5000+0x59)
+#define GN_SC69_DOOR (0x5000+0x5a)
+#define GT_SC69_DOOR (0x5000+0x5b)
+#define SC69_DOOR_ACTION (0x5000+0x5c)
+#define GT_SC69_GRILL (0x5000+0x5d)
+#define SC69_GRILL_ACTION (0x5000+0x5e)
+#define GT_SC70_DOOR (0x5000+0x5f)
+#define SC70_DOOR_ACTION (0x5000+0x60)
+#define SC70_IRIS_LOGIC (0x5000+0x61)
+#define SC70_IRIS_OPENED (0x5000+0x62)
+#define SC70_IRIS_CLOSED (0x5000+0x63)
+#define GT_SC70_IRIS (0x5000+0x64)
+#define SC70_IRIS_ACTION (0x5000+0x65)
+#define SC70_BAR_LOGIC (0x5000+0x66)
+#define GT_SC70_BAR (0x5000+0x67)
+#define SC70_BAR_ACTION (0x5000+0x68)
+#define GT_SC70_GRILL (0x5000+0x69)
+#define SC70_GRILL_ACTION (0x5000+0x6a)
+#define SC70_CONTROL_LOGIC (0x5000+0x6b)
+#define GT_SC70_CONTROL (0x5000+0x6c)
+#define SC70_CONTROL_ACTION (0x5000+0x6d)
+#define SC70_PIT_LOGIC (0x5000+0x6e)
+#define GT_SC70_PIT (0x5000+0x6f)
+#define SC70_PIT_ACTION (0x5000+0x70)
+#define GT_SC70_FLOOR (0x5000+0x71)
+#define SC71_DOOR69_WALK_ON (0x5000+0x72)
+#define GN_SC71_DOOR69 (0x5000+0x73)
+#define GT_SC71_DOOR69 (0x5000+0x74)
+#define SC71_DOOR69_ACTION (0x5000+0x75)
+#define SC71_DOOR72_WALK_ON (0x5000+0x76)
+#define GN_SC71_DOOR72 (0x5000+0x77)
+#define GT_SC71_DOOR72 (0x5000+0x78)
+#define SC71_DOOR72_ACTION (0x5000+0x79)
+#define GN_INTO_RECHARGING_UNIT (0x5000+0x7a)
+#define GET_INTO_RECHARGING_UNIT (0x5000+0x7b)
+#define GT_SC71_RECHARGER (0x5000+0x7c)
+#define SC71_RECHARGER_ACTION (0x5000+0x7d)
+#define GT_SC71_MONITOR (0x5000+0x7e)
+#define SC71_MONITOR_ACTION (0x5000+0x7f)
+#define GT_SC71_CONTROLS (0x5000+0x80)
+#define SC71_CONTROLS_ACTION (0x5000+0x81)
+#define GT_SC71_LOCKED_DOOR (0x5000+0x82)
+#define SC71_LOCKED_DOOR_ACTION (0x5000+0x83)
+#define SC71_RECHARGER_LOGIC (0x5000+0x84)
+#define SC71_PANEL2_LOGIC (0x5000+0x85)
+#define SC71_LIGHT1_LOGIC (0x5000+0x86)
+#define SC71_CHLITE_LOGIC (0x5000+0x87)
+#define SC71_MONITOR_LOGIC (0x5000+0x88)
+#define SC71_CONTROLS_LOGIC (0x5000+0x89)
+#define WITNESS_LOGIC (0x5000+0x8a)
+#define SC72_FOSTER_DEATH (0x5000+0x8b)
+#define WITNESS_CATCHES_FOSTER (0x5000+0x8c)
+#define SC72_DOOR_WALK_ON (0x5000+0x8d)
+#define GN_SC72_DOOR (0x5000+0x8e)
+#define GT_SC72_DOOR (0x5000+0x8f)
+#define SC72_DOOR_ACTION (0x5000+0x90)
+#define SC72_EXIT_WALK_ON (0x5000+0x91)
+#define GN_SC72_EXIT (0x5000+0x92)
+#define GT_SC72_EXIT (0x5000+0x93)
+#define SC72_EXIT_ACTION (0x5000+0x94)
+#define SC72_TANK_LOGIC (0x5000+0x95)
+#define GT_SC72_TANK (0x5000+0x96)
+#define SC72_TANK_ACTION (0x5000+0x97)
+#define GT_SC72_TAP (0x5000+0x98)
+#define SC72_TAP_ACTION (0x5000+0x99)
+#define SC72_SPILL_LOGIC (0x5000+0x9a)
+#define GT_SC72_SPILL (0x5000+0x9b)
+#define SC72_SPILL_ACTION (0x5000+0x9c)
+#define SC72_GRILL_LOGIC (0x5000+0x9d)
+#define GT_SC72_GRILL (0x5000+0x9e)
+#define SC72_GRILL_ACTION (0x5000+0x9f)
+#define SC72_CHAMBER1_LOGIC (0x5000+0xa0)
+#define SC72_CHAM1_LIGHT_LOGIC (0x5000+0xa1)
+#define SC72_CHAMBER2_LOGIC (0x5000+0xa2)
+#define SC72_CHAM2_LIGHT_LOGIC (0x5000+0xa3)
+#define SC72_CHAMBER3_LOGIC (0x5000+0xa4)
+#define GT_SC72_CHAMBER1 (0x5000+0xa5)
+#define GT_SC72_CHAMBER2 (0x5000+0xa6)
+#define GT_SC72_CHAMBER3 (0x5000+0xa7)
+#define SC72_CHAMBERS_ACTION (0x5000+0xa8)
+#define GT_SC72_LIGHT1 (0x5000+0xa9)
+#define GT_SC72_LIGHT2 (0x5000+0xaa)
+#define GT_SC72_LIGHT3 (0x5000+0xab)
+#define SC72_ROT_LIGHT_LOGIC (0x5000+0xac)
+#define SC72_COMPUTER_LOGIC (0x5000+0xad)
+#define SC72_COMPUTER2_LOGIC (0x5000+0xae)
+#define GT_SC72_COMPUTER (0x5000+0xaf)
+#define SC72_COMPUTER_ACTION (0x5000+0xb0)
+#define GN_SC72_WITNESS_TALK (0x5000+0xb1)
+#define GT_SC72_WITNESS_TALK (0x5000+0xb2)
+#define GN_SC72_FOSTER_TALK (0x5000+0xb3)
+#define GT_SC72_FOSTER_TALK (0x5000+0xb4)
+#define GT_SC72_WITNESS_KILL (0x5000+0xb5)
+#define GT_SC73_CORPSE (0x5000+0xb6)
+#define SC73_CORPSE_ACTION (0x5000+0xb7)
+#define GALLAGHER_LOGIC73 (0x5000+0xb8)
+#define GT_SC73_GALL_1 (0x5000+0xb9)
+#define GT_SC73_GALL_2 (0x5000+0xba)
+#define GT_SC73_JOEY_WAIT (0x5000+0xbb)
+#define SC73_SENSOR_LOGIC (0x5000+0xbc)
+#define SC73_EXIT_WALK_ON (0x5000+0xbd)
+#define GT2_SC73_EXIT (0x5000+0xbe)
+#define GT_SC73_EXIT (0x5000+0xbf)
+#define SC73_EXIT_ACTION (0x5000+0xc0)
+#define SC73_DOOR_WALK_ON (0x5000+0xc1)
+#define GT_SC73_DOOR (0x5000+0xc2)
+#define SC73_DOOR_ACTION (0x5000+0xc3)
+#define SC73_CHAMBER3_LOGIC (0x5000+0xc4)
+#define SC73_CHAMBER4_LOGIC (0x5000+0xc5)
+#define SC73_CHAM4_LIGHT_LOGIC (0x5000+0xc6)
+#define GT_SC73_CHAMBER4 (0x5000+0xc7)
+#define SC73_CHAMBER4_ACTION (0x5000+0xc8)
+#define SC73_CHAMBER5_LOGIC (0x5000+0xc9)
+#define SC73_CHAM5_LIGHT_LOGIC (0x5000+0xca)
+#define GT_SC73_CHAMBER5 (0x5000+0xcb)
+#define SC73_CHAMBER5_ACTION (0x5000+0xcc)
+#define SC73_BIG_DOOR_MOUSE_ON (0x5000+0xcd)
+#define SC73_BIG_DOOR_WALK_ON (0x5000+0xce)
+#define GT_SC73_BIG_DOOR (0x5000+0xcf)
+#define SC73_BIG_DOOR_ACTION (0x5000+0xd0)
+#define GT_SC73_SENSOR (0x5000+0xd1)
+#define SC73_SENSOR_ACTION (0x5000+0xd2)
+#define GT_SC73_LOCKED_DOOR (0x5000+0xd3)
+#define SC73_LOCKED_DOOR_ACTION (0x5000+0xd4)
+#define SC73_BITS_LOGIC (0x5000+0xd5)
+#define SC73_BITS2_LOGIC (0x5000+0xd6)
+#define SC73_SPRAY_LOGIC (0x5000+0xd7)
+#define GT_SC73_WRECKED_DROID (0x5000+0xd8)
+#define SC73_WRECKED_DROID_ACTION (0x5000+0xd9)
+#define JOEY_MED_LOGIC73 (0x5000+0xda)
+#define SC74_DOOR_WALK_ON (0x5000+0xdb)
+#define GT_SC74_DOOR (0x5000+0xdc)
+#define SC74_DOOR_ACTION (0x5000+0xdd)
+#define SC74_MONITOR1_LOGIC (0x5000+0xde)
+#define SC74_MONITOR2_LOGIC (0x5000+0xdf)
+#define SC74_MONITOR3_LOGIC (0x5000+0xe0)
+#define SC74_MONITOR4_LOGIC (0x5000+0xe1)
+#define SC74_LEFT_TV_LOGIC (0x5000+0xe2)
+#define SC74_RIGHT_TV_LOGIC (0x5000+0xe3)
+#define SC74_LIGHTS_LOGIC (0x5000+0xe4)
+#define GT_SC74_MONITOR1 (0x5000+0xe5)
+#define GT_SC74_LEFT_TV (0x5000+0xe6)
+#define GT_SC74_RIGHT_TV (0x5000+0xe7)
+#define SC74_MONITORS_ACTION (0x5000+0xe8)
+#define GT_SC74_INTERFACE (0x5000+0xe9)
+#define SC74_INTERFACE_ACTION (0x5000+0xea)
+#define GT_SC74_FLOOR (0x5000+0xeb)
+#define GT_SC74_INT_SLOT (0x5000+0xec)
+#define SC74_INT_SLOT_ACTION (0x5000+0xed)
+#define SC74_INT_SLOT_LOGIC (0x5000+0xee)
+#define GT_SC74_TERMINAL (0x5000+0xef)
+#define SC74_TERMINAL_ACTION (0x5000+0xf0)
+#define SC74_POD_LOGIC (0x5000+0xf1)
+#define SC75_BIG_DOOR_WALK_ON (0x5000+0xf2)
+#define GT_SC75_BIG_DOOR (0x5000+0xf3)
+#define SC75_BIG_DOOR_ACTION (0x5000+0xf4)
+#define SC75_DOOR_WALK_ON (0x5000+0xf5)
+#define GT_SC75_DOOR (0x5000+0xf6)
+#define SC75_DOOR_ACTION (0x5000+0xf7)
+#define SC75_NITRO_TANK_LOGIC (0x5000+0xf8)
+#define GT_SC75_NITRO_TANK (0x5000+0xf9)
+#define SC75_NITRO_TANK_ACTION (0x5000+0xfa)
+#define SC75_LIVE_TANK_LOGIC (0x5000+0xfb)
+#define GT_SC75_LIVE_TANK (0x5000+0xfc)
+#define SC75_TISSUE_LOGIC (0x5000+0xfd)
+#define SC75_LIVE_TANK_ACTION (0x5000+0xfe)
+#define SC75_CONSOLE_LOGIC (0x5000+0xff)
+#define SC75_CRASH_LOGIC (0x5000+0x100)
+#define GT_SC75_CONSOLE (0x5000+0x101)
+#define SC75_CONSOLE_ACTION (0x5000+0x102)
+#define SC75_TONGS_LOGIC (0x5000+0x103)
+#define GT_SC75_TONGS (0x5000+0x104)
+#define SC75_TONGS_ACTION (0x5000+0x105)
+#define SC75_LIGHT1_LOGIC (0x5000+0x106)
+#define SC75_LIGHT2_LOGIC (0x5000+0x107)
+#define SC76_DOOR75_WALK_ON (0x5000+0x108)
+#define GT_SC76_DOOR75 (0x5000+0x109)
+#define SC76_DOOR75_ACTION (0x5000+0x10a)
+#define SC76_DOOR77_WALK_ON (0x5000+0x10b)
+#define GT_SC76_DOOR77 (0x5000+0x10c)
+#define SC76_DOOR77_ACTION (0x5000+0x10d)
+#define GT_SC76_ANYTHING (0x5000+0x10e)
+#define SC76_ANDROID_ACTION (0x5000+0x10f)
+#define SC76_CONSOLE_1_ACTION (0x5000+0x110)
+#define SC76_CONSOLE_2_ACTION (0x5000+0x111)
+#define SC76_CONSOLE_3_ACTION (0x5000+0x112)
+#define SC76_BOARD_1_ACTION (0x5000+0x113)
+#define SC76_BOARD_2_ACTION (0x5000+0x114)
+#define SC76_BOARD_3_ACTION (0x5000+0x115)
+#define SC76_BOARD_1_LOGIC (0x5000+0x116)
+#define SC76_BOARD_2_LOGIC (0x5000+0x117)
+#define SC76_BOARD_3_LOGIC (0x5000+0x118)
+#define SC76_CABINET_1_ACTION (0x5000+0x119)
+#define SC76_CABINET_2_ACTION (0x5000+0x11a)
+#define SC76_CABINET_3_ACTION (0x5000+0x11b)
+#define SC76_CABINET_1_LOGIC (0x5000+0x11c)
+#define SC76_CABINET_2_LOGIC (0x5000+0x11d)
+#define SC76_CABINET_3_LOGIC (0x5000+0x11e)
+#define SC76_LIGHT1_LOGIC (0x5000+0x11f)
+#define SC76_LIGHT2_LOGIC (0x5000+0x120)
+#define SC76_LIGHT3_LOGIC (0x5000+0x121)
+#define SC76_LIGHT4_LOGIC (0x5000+0x122)
+#define SC76_LIGHT5_LOGIC (0x5000+0x123)
+#define SC76_LIGHT6_LOGIC (0x5000+0x124)
+#define SC76_LIGHT7_LOGIC (0x5000+0x125)
+#define SC76_LIGHT8_LOGIC (0x5000+0x126)
+#define SC76_LIGHT9_LOGIC (0x5000+0x127)
+#define SC76_ANDROID_1_LOGIC (0x5000+0x128)
+#define SC76_ANDROID_2_LOGIC (0x5000+0x129)
+#define KEN_START_LOGIC (0x5000+0x12a)
+#define SC76_ANDROID_3_LOGIC (0x5000+0x12b)
+#define KEN_EXTRA (0x5000+0x12c)
+#define KEN_LOGIC (0x5000+0x12d)
+#define KEN_STUCK_LOGIC (0x5000+0x12e)
+#define STUCK_SSS (0x5000+0x12f)
+#define GT_SC77_STUCK_KEN (0x5000+0x130)
+#define STUCK_KEN_ACTION (0x5000+0x131)
+#define KEN_MISSION_HAND_EXTRA (0x5000+0x132)
+#define KEN_MISSION_HAND (0x5000+0x133)
+#define SC77_DOOR76_WALK_ON (0x5000+0x134)
+#define GT_SC77_DOOR76 (0x5000+0x135)
+#define SC77_DOOR76_ACTION (0x5000+0x136)
+#define SC77_BIG_DOOR_MOUSE_ON (0x5000+0x137)
+#define SC77_BIG_DOOR_LOGIC (0x5000+0x138)
+#define SC77_BIG_DOOR_WALK_ON (0x5000+0x139)
+#define GT_SC77_BIG_DOOR (0x5000+0x13a)
+#define SC77_BIG_DOOR_ACTION (0x5000+0x13b)
+#define GT_SC77_TANKS (0x5000+0x13c)
+#define SC77_TANKS_ACTION (0x5000+0x13d)
+#define GT_SC77_HAND_1 (0x5000+0x13e)
+#define SC77_HAND_1_ACTION (0x5000+0x13f)
+#define GN_SC77_HAND_2 (0x5000+0x140)
+#define GT_SC77_HAND_2 (0x5000+0x141)
+#define SC77_HAND_2_ACTION (0x5000+0x142)
+#define GT_SC78_LEDGE (0x5000+0x143)
+#define GT_SC78_PIPE (0x5000+0x144)
+#define SC78_BIG_DOOR_WALK_ON (0x5000+0x145)
+#define GT_SC78_BIG_DOOR (0x5000+0x146)
+#define SC78_BIG_DOOR_ACTION (0x5000+0x147)
+#define SC78_EXIT_WALK_ON (0x5000+0x148)
+#define GT_SC78_EXIT (0x5000+0x149)
+#define SC78_EXIT_ACTION (0x5000+0x14a)
+#define GT_SC78_SUPPORT (0x5000+0x14b)
+#define SC78_SUPPORT_ACTION (0x5000+0x14c)
+#define SC79_EXIT_WALK_ON (0x5000+0x14d)
+#define GT_SC79_EXIT (0x5000+0x14e)
+#define SC79_EXIT_ACTION (0x5000+0x14f)
+#define GT_SC79_SUPPORT (0x5000+0x150)
+#define SC79_SUPPORT_ACTION (0x5000+0x151)
+#define SC79_KNOT_LOGIC (0x5000+0x152)
+#define GT_SC79_KNOT (0x5000+0x153)
+#define SC79_KNOT_ACTION (0x5000+0x154)
+#define SC79_ROPE_LOGIC (0x5000+0x155)
+#define GT_SC79_ROPE (0x5000+0x156)
+#define SC79_ROPE_ACTION (0x5000+0x157)
+#define GT_SC79_LADDER (0x5000+0x158)
+#define SC79_LADDER_ACTION (0x5000+0x159)
+#define SC80_LADDER_ACTION (0x5000+0x15a)
+#define SC80_ROPE_ACTION (0x5000+0x15b)
+#define SC80_ROPE_LOGIC (0x5000+0x15c)
+#define SC80_SPOUT_ACTION (0x5000+0x15d)
+#define SC80_ORIFICE_ACTION (0x5000+0x15e)
+#define SC80_SAMPLE_LOGIC (0x5000+0x15f)
+#define SC80_EXIT_LOGIC (0x5000+0x160)
+#define SC80_EXIT_ACTION (0x5000+0x161)
+#define SC80_GOO_LOGIC (0x5000+0x162)
+#define SC80_BUBBLE1_LOGIC (0x5000+0x163)
+#define SC80_BUBBLE2_LOGIC (0x5000+0x164)
+#define SC80_BUBBLE3_LOGIC (0x5000+0x165)
+#define SC80_BUBBLE4_LOGIC (0x5000+0x166)
+#define SC80_BUBBLE5_LOGIC (0x5000+0x167)
+#define SC80_BUBBLE6_LOGIC (0x5000+0x168)
+#define SC80_BUBBLE7_LOGIC (0x5000+0x169)
+#define SC80_BUBBLE8_LOGIC (0x5000+0x16a)
+#define SC80_BUBBLE9_LOGIC (0x5000+0x16b)
+#define SC80_BUBBLE10_LOGIC (0x5000+0x16c)
+#define SC80_BUBBLE11_LOGIC (0x5000+0x16d)
+#define SC80_BUBBLE12_LOGIC (0x5000+0x16e)
+#define SC81_PULSE_LOGIC (0x5000+0x16f)
+#define SC81_FATHER_CHAIR_LOGIC (0x5000+0x170)
+#define SC81_FATHER_FLOOR_LOGIC (0x5000+0x171)
+#define SC81_FATHER_FINISHED (0x5000+0x172)
+#define SC81_FATHER_SSS (0x5000+0x173)
+#define SC81_FATHER_FALL (0x5000+0x174)
+#define FOSTER_ENTER_BOARDROOM (0x5000+0x175)
+#define SC81_FATHER_ACTION (0x5000+0x176)
+#define LAST_WORDS_WITH_FATHER (0x5000+0x177)
+#define SC81_KEN_SSS (0x5000+0x178)
+#define SC81_KEN_ACTION (0x5000+0x179)
+#define SC81_DOOR_LOGIC (0x5000+0x17a)
+#define SC81_KEN_LOGIC (0x5000+0x17b)
+#define SC81_FOSTER_ABSORBED (0x5000+0x17c)
+#define SC81_FOSTER_GRABBED (0x5000+0x17d)
+#define SC81_CHAIR_ACTION (0x5000+0x17e)
+#define SC81_TENT1_LOGIC (0x5000+0x17f)
+#define SC81_TENT2_LOGIC (0x5000+0x180)
+#define SC81_TENT3_LOGIC (0x5000+0x181)
+#define SC81_TENT4_LOGIC (0x5000+0x182)
+#define SC81_TENT5_LOGIC (0x5000+0x183)
+#define SC81_TENT6_LOGIC (0x5000+0x184)
+#define SC81_BIG_TENT1_LOGIC (0x5000+0x185)
+#define SC81_BIG_TENT2_LOGIC (0x5000+0x186)
+#define SC81_BIG_TENT3_LOGIC (0x5000+0x187)
+#define SC82_JOBS_SSS (0x0000+0x47)
+#define SC82_JOBSWORTH_LOGIC (0x0000+0x48)
+#define PRINT_CREDITS (0x0000+0x49)
+#define END_SEQUENCE (0x0000+0x4a)
+#define FOSTER_ENTER_NEW_BOARDROOM (0x0000+0x4b)
+#define SC82_KEN_LOGIC (0x0000+0x4c)
+#define S19_LEFT_ON (0x0000+0x4d)
+#define S19_RIGHT_ON (0x0000+0x4e)
+#define GT_RIGHT_EXIT_19 (0x0000+0x4f)
+#define ER19_ACTION (0x0000+0x50)
+#define CABLE2_LOGIC (0x0000+0x51)
+#define CABLE_FALL_LOGIC (0x0000+0x52)
+#define SMASHED_WINDOW_LOGIC (0x0000+0x53)
+#define BITS_LOGIC (0x0000+0x54)
+#define GT_CABLE_11 (0x0000+0x55)
+#define CABLE_11_ACTION (0x0000+0x56)
+#define SPY11_LOGIC (0x0000+0x57)
+#define LOCKER_11_LOGIC (0x0000+0x58)
+#define GT_LOCKER_11 (0x0000+0x59)
+#define LOCKER_11_ACTION (0x0000+0x5a)
+#define START90 (0x0000+0x5b)
+#define EXIT_LINC (0x0000+0x5c)
+#define LINC_MENU_SCRIPT (0x6000+0x1)
+#define LINC_MENU_SELECT (0x6000+0x2)
+#define INFO_MENU_SELECT (0x6000+0x3)
+#define DIS_MENU_SELECT (0x6000+0x4)
+#define JOIN_MENU_SELECT (0x6000+0x5)
+#define DECOMP_MENU_SELECT (0x6000+0x6)
+#define DECRYPT_MENU_SELECT (0x6000+0x7)
+#define DOC_MENU_SELECT (0x6000+0x8)
+#define SET_UP_INFO_WINDOW (0x6000+0x9)
+#define INFO_WINDOW_LOGIC (0x6000+0xa)
+#define INFO_BUTTON_LOGIC (0x6000+0xb)
+#define CLOSE_WINDOW (0x6000+0xc)
+#define NORMAL_MOUSE (0x6000+0xd)
+#define BUTTON_MOUSE (0x6000+0xe)
+#define DISCONNECT_FOSTER (0x6000+0xf)
+#define DOOR_L90_LOGIC (0x6000+0x10)
+#define DOOR_L90F_LOGIC (0x6000+0x11)
+#define GET_TO_DOOR_L90 (0x6000+0x12)
+#define DOOR_L90_ACTION (0x6000+0x13)
+#define DOOR_R90_LOGIC (0x6000+0x14)
+#define DOOR_R90F_LOGIC (0x6000+0x15)
+#define GET_TO_DOOR_R90 (0x6000+0x16)
+#define DOOR_R90_ACTION (0x6000+0x17)
+#define GET_TO_JOIN_OBJECT (0x6000+0x18)
+#define JOIN_OBJECT_ACTION (0x6000+0x19)
+#define JOIN_OBJECT_LOGIC (0x6000+0x1a)
+#define GET_TO_OSCILLATOR (0x6000+0x1b)
+#define OSCILLATOR_ACTION (0x6000+0x1c)
+#define OSCILLATOR_LOGIC (0x6000+0x1d)
+#define GET_TO_EYEBALL_90 (0x6000+0x1e)
+#define EYEBALL_90_ACTION (0x6000+0x1f)
+#define EYEBALL_90_LOGIC (0x6000+0x20)
+#define DOOR_L91_LOGIC (0x6000+0x21)
+#define DOOR_L91F_LOGIC (0x6000+0x22)
+#define GET_TO_DOOR_L91 (0x6000+0x23)
+#define DOOR_L91_ACTION (0x6000+0x24)
+#define DOOR_R91_LOGIC (0x6000+0x25)
+#define DOOR_R91F_LOGIC (0x6000+0x26)
+#define GET_TO_DOOR_R91 (0x6000+0x27)
+#define DOOR_R91_ACTION (0x6000+0x28)
+#define DOOR_T91_LOGIC (0x6000+0x29)
+#define DOOR_T91R_LOGIC (0x6000+0x2a)
+#define GET_TO_DOOR_T91 (0x6000+0x2b)
+#define DOOR_T91_ACTION (0x6000+0x2c)
+#define GET_TO_BAG_91 (0x6000+0x2d)
+#define BAG_91_ACTION (0x6000+0x2e)
+#define BAG_91_LOGIC (0x6000+0x2f)
+#define GET_TO_DECOMP_OBJ (0x6000+0x30)
+#define DECOMP_OBJ_ACTION (0x6000+0x31)
+#define DECOMP_OBJ_LOGIC (0x6000+0x32)
+#define GET_TO_DECRYPT_OBJ (0x6000+0x33)
+#define DECRYPT_OBJ_ACTION (0x6000+0x34)
+#define DECRYPT_OBJ_LOGIC (0x6000+0x35)
+#define GET_TO_REPORT_BOOK (0x6000+0x36)
+#define REPORT_BOOK_ACTION (0x6000+0x37)
+#define REPORT_BOOK_LOGIC (0x6000+0x38)
+#define GET_TO_EYEBALL_91 (0x6000+0x39)
+#define EYEBALL_91_ACTION (0x6000+0x3a)
+#define EYEBALL_91_LOGIC (0x6000+0x3b)
+#define DOOR_L92_LOGIC (0x6000+0x3c)
+#define DOOR_L92F_LOGIC (0x6000+0x3d)
+#define GET_TO_DOOR_L92 (0x6000+0x3e)
+#define DOOR_L92_ACTION (0x6000+0x3f)
+#define DOOR_R92_LOGIC (0x6000+0x40)
+#define DOOR_R92R_LOGIC (0x6000+0x41)
+#define GET_TO_DOOR_R92 (0x6000+0x42)
+#define DOOR_R92_ACTION (0x6000+0x43)
+#define SLAB1_LOGIC (0x6000+0x44)
+#define SLAB2_LOGIC (0x6000+0x45)
+#define SLAB3_LOGIC (0x6000+0x46)
+#define SLAB4_LOGIC (0x6000+0x47)
+#define SLAB5_LOGIC (0x6000+0x48)
+#define SLAB6_LOGIC (0x6000+0x49)
+#define SLAB7_LOGIC (0x6000+0x4a)
+#define SLAB8_LOGIC (0x6000+0x4b)
+#define SLAB9_LOGIC (0x6000+0x4c)
+#define GET_TO_SLAB (0x6000+0x4d)
+#define SLAB_ACTION (0x6000+0x4e)
+#define SLAB_6_9_ACTION (0x6000+0x4f)
+#define BRIDGE_A_LOGIC (0x6000+0x50)
+#define BRIDGE_B_LOGIC (0x6000+0x51)
+#define BRIDGE_C_LOGIC (0x6000+0x52)
+#define BRIDGE_D_LOGIC (0x6000+0x53)
+#define BRIDGE_E_LOGIC (0x6000+0x54)
+#define BRIDGE_F_LOGIC (0x6000+0x55)
+#define BRIDGE_G_LOGIC (0x6000+0x56)
+#define BRIDGE_H_LOGIC (0x6000+0x57)
+#define GET_TO_CIRCLE (0x6000+0x58)
+#define GREEN_CIRCLE_LOGIC (0x6000+0x59)
+#define GREEN_CIRCLE_ACTION (0x6000+0x5a)
+#define RED_CIRCLE_LOGIC (0x6000+0x5b)
+#define RED_CIRCLE_ACTION (0x6000+0x5c)
+#define SLAB_ON (0x6000+0x5d)
+#define SLAB_OFF (0x6000+0x5e)
+#define LEFT_MOUSE (0x6000+0x5f)
+#define RIGHT_MOUSE (0x6000+0x60)
+#define UP_MOUSE (0x6000+0x61)
+#define DOWN_MOUSE (0x6000+0x62)
+#define DOOR_L93_LOGIC (0x6000+0x63)
+#define DOOR_L93F_LOGIC (0x6000+0x64)
+#define GET_TO_DOOR_L93 (0x6000+0x65)
+#define DOOR_L93_ACTION (0x6000+0x66)
+#define GET_TO_PERSONA (0x6000+0x67)
+#define PERSONA_ACTION (0x6000+0x68)
+#define PERSONA_LOGIC (0x6000+0x69)
+#define GET_TO_ADJUST_BOOK (0x6000+0x6a)
+#define ADJUST_BOOK_ACTION (0x6000+0x6b)
+#define ADJUST_BOOK_LOGIC (0x6000+0x6c)
+#define DOOR_L94_LOGIC (0x6000+0x6d)
+#define DOOR_L94R_LOGIC (0x6000+0x6e)
+#define GET_TO_DOOR_L94 (0x6000+0x6f)
+#define DOOR_L94_ACTION (0x6000+0x70)
+#define DOOR_R94_LOGIC (0x6000+0x71)
+#define DOOR_R94R_LOGIC (0x6000+0x72)
+#define GET_TO_DOOR_R94 (0x6000+0x73)
+#define DOOR_R94_ACTION (0x6000+0x74)
+#define GET_TO_HOLOGRAM_PAD (0x6000+0x75)
+#define HOLOGRAM_PAD_ACTION (0x6000+0x76)
+#define HOLOGRAM_A_LOGIC (0x6000+0x77)
+#define HOLOGRAM_B_LOGIC (0x6000+0x78)
+#define DOOR_L95_LOGIC (0x6000+0x79)
+#define DOOR_L95F_LOGIC (0x6000+0x7a)
+#define GET_TO_DOOR_L95 (0x6000+0x7b)
+#define DOOR_L95_ACTION (0x6000+0x7c)
+#define DOOR_R95_LOGIC (0x6000+0x7d)
+#define DOOR_R95F_LOGIC (0x6000+0x7e)
+#define GET_TO_DOOR_R95 (0x6000+0x7f)
+#define DOOR_R95_ACTION (0x6000+0x80)
+#define DOOR_T95_LOGIC (0x6000+0x81)
+#define DOOR_T95R_LOGIC (0x6000+0x82)
+#define GET_TO_DOOR_T95 (0x6000+0x83)
+#define DOOR_T95_ACTION (0x6000+0x84)
+#define GET_TO_GUARDIAN (0x6000+0x85)
+#define GUARDIAN_ACTION (0x6000+0x86)
+#define GUARDIAN_LOGIC (0x6000+0x87)
+#define WEIGHT_LOGIC (0x6000+0x88)
+#define DOOR_L96_LOGIC (0x6000+0x89)
+#define DOOR_L96F_LOGIC (0x6000+0x8a)
+#define GET_TO_DOOR_L96 (0x6000+0x8b)
+#define DOOR_L96_ACTION (0x6000+0x8c)
+#define CRYSTAL_LOGIC (0x6000+0x8d)
+#define GET_TO_CRYSTAL (0x6000+0x8e)
+#define CRYSTAL_ACTION (0x6000+0x8f)
+#define VIRUS_LOGIC (0x6000+0x90)
+#define GET_TO_VIRUS (0x6000+0x91)
+#define VIRUS_ACTION (0x6000+0x92)
+#define ANITA_SPEECH (0x2000+0x105)
+#define LAMB_FACTORY (0x2000+0x106)
+#define LAMB_UNUSED (0x2000+0x107)
+#define LAMB_BELLEVUE (0x0000+0x5d)
+#define FORE_SPEECH (0x2000+0x108)
+#define GORDON_SPEECH (0x2000+0x109)
+#define GUARD_SPEECH (0x2000+0x10a)
+#define WANK (0x2000+0x10b)
+#define WRECK_SPEECH (0x2000+0x10c)
+#define LOB_DAD_SPEECH (0x2000+0x10d)
+#define LOB_SON_SPEECH (0x2000+0x10e)
+#define RADMAN_SPEECH (0x2000+0x10f)
+#define BURKE_SPEECH (0x3000+0x139)
+#define JASON_SPEECH (0x3000+0x13a)
+#define JOEY_RECYCLE (0x1000+0xab)
+#define JOEY_UNUSED (0x1000+0xac)
+#define JOEY_FACTORY (0x2000+0x110)
+#define JOEY_BELLEVUE (0x3000+0x13b)
+#define ANCHOR_SPEECH (0x3000+0x13c)
+#define TREVOR_SPEECH (0x3000+0x13d)
+#define HELGA_SPEECH (0x3000+0x13e)
+#define GALL_BELLEVUE (0x3000+0x13f)
+#define FULL_SCREEN_LOGIC (0x0000+0x5e)
+#define CANCEL_ACTION_101 (0x0000+0x5f)
+#define BUTTON_ACTION_101 (0x0000+0x60)
+#define FS_BUTTON_LOGIC (0x0000+0x61)
+#define FS_RETINA_SCAN_LOGIC (0x0000+0x62)
+#define START_0 (0x0000+0x63)
+#define START_S4 (0x0000+0x64)
+#define START_S2 (0x0000+0x65)
+#define START_S3 (0x0000+0x66)
+#define START_S6 (0x0000+0x67)
+#define START_29 (0x0000+0x68)
+#define START_TEN (0x0000+0x69)
+#define START_ONE (0x0000+0x6a)
+#define START_IN_FACTORY (0x0000+0x6b)
+#define START_14 (0x0000+0x6c)
+#define START_SC31 (0x0000+0x6d)
+#define START_SC37 (0x0000+0x6e)
+#define START_SC42 (0x0000+0x6f)
+#define START_SC48 (0x0000+0x70)
+#define START_SC66 (0x0000+0x71)
+#define START_SC73 (0x0000+0x72)
+#define START_SC81 (0x0000+0x73)
+#define START_SC82 (0x0000+0x74)
+#define START_SC90 (0x0000+0x75)
+#define MANTRACH_SPEECH (0x0000+0x76)
+#define ID_GRID81 21010
+#define ID_SC81_DOOR 21011
+#define ID_SC81_CHAIR 21012
+#define ID_SC81_HELMET 21013
+#define ID_SC81_FATHER 21014
+#define ID_SC81_FATHER_SAT 21015
+#define ID_SC81_FOSTER_SAT 21016
+#define ID_SC81_KEN_SAT 21017
+#define ID_SC81_TENT1 21025
+#define ID_SC81_TENT2 21026
+#define ID_SC81_TENT3 21027
+#define ID_SC81_TENT4 21028
+#define ID_SC81_TENT5 21029
+#define ID_SC81_TENT6 21030
+#define ID_SC81_BIG_TENT1 21037
+#define ID_SC81_BIG_TENT2 21038
+#define ID_SC81_BIG_TENT3 21039
+#define ID_SC39_WALTERS 16809
+#define ID_SC31_JOEY 16851
+#define ID_SC82_JOBSWORTH 21069
+#define DISQ_1 2048
+#define DISQ_2 4096
+#define DISQ_3 6144
+#define DISQ_5 10240
+#define DISQ_6 12288
+#define DISQ_7 14336
+#define DISQ_8 16384
+#define DISQ_9 18432
+#define DISQ_10 20480
+#define DISQ_11 22528
+#define DISQ_12 24576
+#define DISQ_13 26624
+#define DISQ_14 28672
+#define DISQ_15 30720
+#define T0 0
+#define T1 4096
+#define T2 8192
+#define T3 12288
+#define T4 16384
+#define T5 20480
+#define T6 24576
+#define T7 28672
+#define UP 0
+#define DOWN 1
+#define LEFT 2
+#define RIGHT 3
+#define TALK 4
+#define ID_FOSTER 3
+#define ID_JOEY 1
+#define JOBS 4122
+#define ID_JOBS 4122
+#define MINI_SS 4100
+#define FULL_SS 4101
+#define ID_S2_FLOOR 115
+#define ID_L_EXIT_S2 4315
+#define FOSTER_BIG 4098
+#define ID_R_EXIT_S2 4103
+#define ID_S4_FLOOR 4104
+#define ID_S4_L_EXIT 4105
+#define ID_TV_SCREENS 4108
+#define ID_KNOB 4109
+#define ID_CHUCK 4110
+#define ID_LAZER 4111
+#define ID_CUPBOARD 4112
+#define ID_SARNIE 4113
+#define ID_SPANNER 4114
+#define ID_BUTTONS 4115
+#define ID_TOP_LIFT 4116
+#define ID_MONITORS 4117
+#define ID_HOLE 4119
+#define ID_TOP_BARREL 4120
+#define ID_LOADER 4121
+#define ID_UPLOAD 4125
+#define ID_LIGHT1 4126
+#define ID_PANEL 4127
+#define ID_ALARM 4128
+#define ID_S3_FLOOR 4130
+#define ID_ROBOT_SHELL 4131
+#define ID_JOEY_PARK 2
+#define ID_DEAD_LOADER 4133
+#define IDO_CROW_BAR 63
+#define IDO_SARNIE 64
+#define IDO_SPANNER 65
+#define IDO_JOEY_BOARD 66
+#define IDO_CITYCARD 8
+#define IDO_SHADES 9
+#define IDO_PUTTY 10
+#define IDO_LIGHTBULB 11
+#define IDO_ANITA_CARD 71
+#define IDO_ANCHOR 74
+#define IDO_MAGAZINE 75
+#define IDO_TAPE 76
+#define IDO_GLASS 77
+#define IDO_TICKET 79
+#define IDO_SECATEURS 36
+#define IDO_ROPE 37
+#define IDO_PLASTER 38
+#define IDO_NEW_CABLE 39
+#define IDO_BRICK 42
+#define IDO_TONGS 43
+#define IDO_GALLCARD 6
+#define ID_LOW_LIFT 4137
+#define ID_STEVE_SPY 4138
+#define ID_LOW_BARREL 4139
+#define ID_CONVEY 4140
+#define ID_JOEY_FLY 4141
+#define ID_FURNACE 4142
+#define ID_LIGHTS1 4143
+#define ID_EYE_BALL 4144
+#define ID_EYE_BOLT 4145
+#define ID_FURNACE_DOOR 4146
+#define ID_SLOT 4147
+#define ID_SHADES 4148
+#define ID_LAZER_GUN 4149
+#define ID_SMOULDER 4150
+#define ID_NOTICE 4151
+#define ID_NOTICE2 4152
+#define ID_SS_SIGN 4153
+#define ID_POSTCARD 4154
+#define ID_NOTICE4 4155
+#define ID_SHRUG_SEQ 40
+#define ID_SMALL_SHRUG 13
+#define ID_SML_UP_GET_SEQ 14
+#define ID_TEXT_MOUSE 5
+#define ID_S_AND_R 19
+#define ID_MENU_LOGIC 4
+#define ID_STD_MENU_LOGIC 4
+#define ID_ANIM 4186
+#define ID_RESET 4282
+#define ID_RESET_MEGA 7
+#define ID_FAN1 4102
+#define ID_FAN2 4303
+#define ID_FAN3 4305
+#define ID_FAN4 4307
+#define ID_FAN5 4309
+#define ID_FAN6 4311
+#define ID_FAN7 4313
+#define ID_S6_FLOOR 8200
+#define ID_S6_STAIRS 8210
+#define ID_S6_JOEY_FLY 8215
+#define ID_LEFT_EXIT_S6 8221
+#define ID_S5_FLOOR 8223
+#define ID_RIGHT_EXIT_S5 8224
+#define ID_RIGHT_EXIT_S6 8226
+#define ID_S7_FLOOR 8231
+#define ID_LEFT_EXIT_S7 8234
+#define ID_LEFT_EXIT_S5 8238
+#define ID_S18_FLOOR 8243
+#define ID_RIGHT_EXIT_S18 8246
+#define ID_SECURITY_EXIT 8248
+#define ID_S9_FLOOR 8253
+#define ID_LEFT_EXIT_S9 8256
+#define ID_STEAM 8259
+#define ID_POWER_DOOR 8264
+#define ID_POWER_MOTOR 8266
+#define ID_POWER_PANEL 8270
+#define PANEL_FRAME 4160
+#define SWITCH_FRAME 4032
+#define ID_POWER_SWITCH 8271
+#define ID_POWER_CHAIR 8272
+#define ID_LEFT_SKULL 8273
+#define ID_RIGHT_SKULL 8274
+#define ID_POWER_BANG 8275
+#define ID_MONITOR 136
+#define ID_LEFT_LEVER 8290
+#define ID_RIGHT_LEVER 8291
+#define LEFT_LEVER_FRAME 5760
+#define RIGHT_LEVER_FRAME 5824
+#define ID_FANS 8292
+#define ID_LOBBY_DOOR 8295
+#define ID_SCANNER 8298
+#define ID_LOBBY_SLOT 8299
+#define NO_TEXT_MESSAGE 28707
+#define ID_DAD 8301
+#define ID_SON 8211
+#define ID_LOW_GET_SEQ 12
+#define ID_PRESS 4321
+#define ID_LOW_FLOOR 67
+#define ID_SMALL_DOOR 105
+#define ID_LFAN1 4326
+#define ID_LFAN2 4328
+#define ID_SMOKE1 4330
+#define ID_SMOKE2 4332
+#define ID_SKORL_GUARD 8309
+#define S5_SECURITY_EXIT 8310
+#define ID_S8_FLOOR 8316
+#define ID_S7_RIGHT_EXIT 8317
+#define ID_DOWN_EXIT_S8 8320
+#define ID_WRECK_GUARD 8324
+#define ID_FACTORY_ENTRY 8331
+#define ID_S12_FLOOR 8336
+#define ID_FACTORY_EXIT 8341
+#define ID_FACT1_EXIT 8344
+#define ID_S13_FLOOR 8349
+#define ID_FACT2_L_EXIT 8353
+#define ID_FACT2_R_EXIT 8355
+#define ID_S14_FLOOR 8360
+#define ID_FACT3_L_EXIT 8364
+#define ID_F2_STORE_EXIT 8366
+#define ID_S15_FLOOR 8371
+#define ID_NU_FLOOR 8441
+#define ID_STORE_EXIT 8375
+#define ID_ANITA 137
+#define ID_TOP_BELT 8379
+#define ID_BOT_BELT 8381
+#define ID_PIPES 8383
+#define ID_ANITA_SPY 8385
+#define ID_WELDER 8388
+#define ID_LAMB 16
+#define ID_COGS 8393
+#define ID_GEARS 8395
+#define ID_BELT1 8397
+#define ID_BELT2 8399
+#define ID_PIPE1 8401
+#define ID_PIPE2 8403
+#define ID_PIPE3 8405
+#define ID_PIPE4 8407
+#define ID_STD_LEFT_TALK 23
+#define ID_STD_RIGHT_TALK 24
+#define ID_SENSOR 8410
+#define ID_LITE1 8412
+#define ID_LITE2 8414
+#define ID_FOREMAN 8544
+#define ID_FACT2_SPY 8418
+#define ID_S7_CARD_SLOT 8420
+#define ID_LIFT_NOTICE 8421
+#define ID_LIFT_S7 8422
+#define ID_LINC_S7 8425
+#define ID_JUNCTION_BOX 8426
+#define ID_FAKE_FLOOR 8427
+#define ID_FACT_CONSOLE 8435
+#define ID_FLAP 8438
+#define ID_SKEY 8442
+#define ID_WD40 8443
+#define IDO_WD40 34
+#define IDO_SKEY 35
+#define ID_FLOOR_PUTTY 8446
+#define ID_NEW_GRID 15
+#define ST_BACKGROUND 1
+#define ST_FOREGROUND 2
+#define ST_SORT 4
+#define ST_RECREATE 8
+#define ST_MOUSE 16
+#define ST_COLLISION 32
+#define ST_LOGIC 64
+#define ST_GRID_PLOT 128
+#define ST_AR_PRIORITY 256
+#define S62 3968
+#define S91 5824
+#define S94 6016
+#define S95 6080
+#define S96 6144
+#define S106 6784
+#define S108 6912
+#define S137 8768
+#define S152 9728
+#define S182 11648
+#define S191 12224
+#define HEAD_TEXT 24681
+#define PAL_90 24717
+#define PAL_90A 24718
+#define PAL_90B 24719
+#define PAL_91 24720
+#define PAL_92 24721
+#define PAL_93 24722
+#define PAL_94 24723
+#define PAL_95 24724
+#define PAL_96 24725
+#define RST_L_ARR_LINC 142
+#define RST_R_ARR_LINC 143
+#define RST_BLANKS_LINC 144
+#define RST_FOST_S90 24656
+#define RST_FOST_90_91 24657
+#define RST_FOST_90_94 24658
+#define RST_FOST_91_90 24659
+#define RST_FOST_91_92 24660
+#define RST_FOST_91_95 24661
+#define RST_FOST_92_91 24662
+#define RST_FOST_92_93 24663
+#define RST_FOST_93_92 24664
+#define RST_FOST_94_90 24665
+#define RST_FOST_94_95 24666
+#define RST_FOST_95_91 24667
+#define RST_FOST_95_94 24668
+#define RST_FOST_95_96 24669
+#define RST_FOST_96_95 24670
+#define AMT_ENTER_TOP 24814
+#define AMT_EXIT_TOP 24815
+#define AMT_LOGON 24824
+#define AMT_LOGOFF 24825
+#define AMT_CROUCH_LEFT 24759
+#define AMT_CROUCH_RIGHT 24760
+#define AMT_CROUCH_RIGHT_A 24805
+#define AMT_CROUCH_RIGHT_B 24806
+#define AMT_CROUCH_DOWN 24704
+#define AMT_SHRUG 24618
+#define AMT_LIGHT1 24671
+#define AMT_LIGHT2 24672
+#define AMT_LIGHT3A 24673
+#define AMT_LIGHT3B 24674
+#define AMT_LIGHT3C 24675
+#define AMT_LIGHT4 24676
+#define AMT_LIGHT5 24677
+#define AMT_LIGHT6 24678
+#define AMT_LIGHT7 24679
+#define AMT_LIGHT8 24680
+#define AMT_LIGHT9A 24681
+#define AMT_LIGHT9B 24682
+#define AMT_LIGHT9C 24683
+#define AMT_LIGHT10A 24684
+#define AMT_LIGHT10B 24685
+#define AMT_LIGHT10C 24686
+#define AMT_LIGHT10D 24687
+#define AMT_DOOR_L90 24688
+#define AMT_DOOR_L90F 24689
+#define AMT_DOOR_R90 24690
+#define AMT_DOOR_R90F 24691
+#define AMT_GET_JOIN 24692
+#define AMT_GET_OSCILL 24693
+#define AMT_BLIND_EYE 24694
+#define AMT_SEE_EYE 24695
+#define AMT_GET_EYE 24758
+#define AMT_EYE90_ZAP 24802
+#define AMT_FOST_DIE90 24793
+#define AMT_DOOR_L91 24696
+#define AMT_DOOR_L91F 24697
+#define AMT_DOOR_R91 24796
+#define AMT_DOOR_R91F 24797
+#define AMT_DOOR_T91 24698
+#define AMT_DOOR_T91R 24699
+#define AMT_GET_DECOMP 24702
+#define AMT_GET_DECRYPT 24703
+#define AMT_GET_REPORT 24733
+#define AMT_EYE91_ZAP 24800
+#define AMT_FOST_DIE91 24728
+#define AMT_DOOR_L92 24822
+#define AMT_DOOR_L92F 24823
+#define AMT_DOOR_R92 24808
+#define AMT_DOOR_R92R 24809
+#define AMT_DOOR_L93 24812
+#define AMT_DOOR_L93F 24813
+#define AMT_GET_PERSONA 24705
+#define AMT_GET_ADJUST 24706
+#define AMT_DOOR_L94 24707
+#define AMT_DOOR_L94R 24708
+#define AMT_DOOR_R94 24709
+#define AMT_DOOR_R94R 24710
+#define AMT_HOLO1_A 24726
+#define AMT_HOLO1_B 24727
+#define AMT_HOLO3 24768
+#define AMT_DOOR_L95 24711
+#define AMT_DOOR_L95F 24712
+#define AMT_DOOR_R95 24771
+#define AMT_DOOR_R95F 24772
+#define AMT_DOOR_T95 24713
+#define AMT_DOOR_T95R 24714
+#define AMT_GUARDIAN_UP 24791
+#define AMT_GUARDIAN_DOWN 24801
+#define AMT_WEIGHT_ANIM 24826
+#define AMT_DOOR_L96 24780
+#define AMT_DOOR_L96F 24781
+#define AMT_CRYSTAL_SPIN 24788
+#define AMT_CRYSTAL_BREAK 24789
+#define AMT_VIRUS_SPIN 24790
+#define AMT_GET_VIRUS 24794
+#define ID_LINC_MENU_LOGIC 24831
+#define ID_LINC_MENU_MOUSE 24832
+#define IT_BLUE_FOSTER 182
+#define IT_LOGOFF 117
+#define IT_LINK_ARROWS 190
+#define IT_LINK_OBJECTS 191
+#define IT_WINDOW 26
+#define IT_INFO_BUTTON 137
+#define IT_WINDOW_LOGIC 24765
+#define IT_WINDOW_MOUSE 24766
+#define IT_GET_EYE 18
+#define IT_CROUCH_LEFT 16
+#define IT_CROUCH_RIGHT 17
+#define IT_CROUCH_DOWN 20
+#define IT_ENTER_TOP 135
+#define IT_EXIT_TOP 136
+#define IT_LIGHT1 64
+#define IT_LIGHT2 65
+#define IT_LIGHT3A 66
+#define IT_LIGHT3B 67
+#define IT_LIGHT3C 68
+#define IT_LIGHT4 69
+#define IT_LIGHT5 70
+#define IT_LIGHT6 71
+#define IT_LIGHT7 72
+#define IT_LIGHT8 73
+#define IT_LIGHT9A 74
+#define IT_LIGHT9B 75
+#define IT_LIGHT9C 76
+#define IT_LIGHT10A 85
+#define IT_LIGHT10B 86
+#define IT_LIGHT10C 87
+#define IT_LIGHT10D 88
+#define IT_SC90_LAYER_0 175
+#define IT_SC90_LAYER_1 176
+#define IT_SC90_GRID_1 177
+#define IT_SC90_FAST 102
+#define IT_SC90_CHIP 24735
+#define IT_SC90_LOGIC 24736
+#define IT_SC90_MOUSE 24737
+#define IT_DOOR_L90 45
+#define IT_DOOR_L90F 46
+#define IT_DOOR_R90 258
+#define IT_DOOR_R90F 259
+#define IT_JOIN_OBJECT 22
+#define IT_OSCILLATOR 132
+#define IT_EYEBALL 91
+#define IT_BLIND_EYE 89
+#define IT_SEE_EYE 90
+#define IT_EYE90_ZAP 113
+#define IT_FOST_DIE90 115
+#define IT_SC91_LAYER_0 183
+#define IT_SC91_LAYER_1 184
+#define IT_SC91_GRID_1 185
+#define IT_SC91_FAST 24738
+#define IT_SC91_CHIP 24739
+#define IT_SC91_LOGIC 24740
+#define IT_SC91_MOUSE 24741
+#define IT_DOOR_L91 260
+#define IT_DOOR_L91F 261
+#define IT_DOOR_R91 111
+#define IT_DOOR_R91F 112
+#define IT_DOOR_T91 31
+#define IT_DOOR_T91R 32
+#define IT_BAG_91 47
+#define IT_DECOMP_OBJ 48
+#define IT_DECRYPT_OBJ 131
+#define IT_REPORT_BOOK 95
+#define IT_EYE91_ZAP 114
+#define IT_FOST_DIE91 116
+#define IT_SC92_LAYER_0 192
+#define IT_SC92_LAYER_1 193
+#define IT_SC92_GRID_1 194
+#define IT_SC92_FAST 24742
+#define IT_SC92_CHIP 24743
+#define IT_SC92_LOGIC 24744
+#define IT_SC92_MOUSE 24745
+#define IT_BRIDGES 44
+#define IT_CIRCLES 62
+#define IT_DOOR_L92 54
+#define IT_DOOR_L92F 55
+#define IT_DOOR_R92 129
+#define IT_DOOR_R92R 130
+#define IT_SC93_LAYER_0 199
+#define IT_SC93_LAYER_1 250
+#define IT_SC93_GRID_1 251
+#define IT_SC93_FAST 24746
+#define IT_SC93_CHIP 24747
+#define IT_SC93_LOGIC 24748
+#define IT_SC93_MOUSE 24749
+#define IT_DOOR_L93 133
+#define IT_DOOR_L93F 134
+#define IT_PERSONA 51
+#define IT_ADJUST_BOOK 63
+#define IT_SC94_LAYER_0 13
+#define IT_SC94_FAST 24750
+#define IT_SC94_CHIP 24751
+#define IT_SC94_LOGIC 24752
+#define IT_SC94_MOUSE 24753
+#define IT_DOOR_L94 58
+#define IT_DOOR_L94R 59
+#define IT_DOOR_R94 60
+#define IT_DOOR_R94R 61
+#define IT_HOLO1_A 92
+#define IT_HOLO1_B 93
+#define IT_HOLO2_A 94
+#define IT_HOLO2_B 96
+#define IT_HOLO3 97
+#define IT_SC95_LAYER_0 23
+#define IT_SC95_LAYER_1 24
+#define IT_SC95_GRID_1 25
+#define IT_SC95_FAST 24754
+#define IT_SC95_CHIP 24755
+#define IT_SC95_LOGIC 24756
+#define IT_SC95_MOUSE 24757
+#define IT_DOOR_L95 56
+#define IT_DOOR_L95F 57
+#define IT_DOOR_R95 100
+#define IT_DOOR_R95F 101
+#define IT_DOOR_T95 52
+#define IT_DOOR_T95R 53
+#define IT_GUARDIAN 102
+#define IT_WEIGHT 103
+#define IT_SC96_LAYER_0 27
+#define IT_SC96_LAYER_1 28
+#define IT_SC96_GRID_1 29
+#define IT_SC96_FAST 24773
+#define IT_SC96_CHIP 24774
+#define IT_SC96_LOGIC 24775
+#define IT_SC96_MOUSE 24776
+#define IT_DOOR_L96 98
+#define IT_DOOR_L96F 99
+#define IT_CRYSTAL_SPIN 106
+#define IT_CRYSTAL_BREAK 107
+#define IT_VIRUS_SPIN 108
+#define IT_GET_VIRUS 110
+#define ID_BLUE_FOSTER 3
+#define ID_WINDOW_1 24761
+#define ID_WINDOW_2 24762
+#define ID_WINDOW_3 24763
+#define ID_WINDOW_4 24764
+#define ID_INFO_BUTTON 24810
+#define ID_HEAD_MODULE 24816
+#define ID_FILE_MODULE 24817
+#define ID_SIZE_MODULE 24818
+#define ID_AUTH_MODULE 24819
+#define ID_NOTE_MODULE 24820
+#define ID_SKY 24640
+#define ID_LIGHTNING 24645
+#define ID_LIGHTNING1 24646
+#define ID_LIGHTNING2 24647
+#define ID_LIGHTNING3 24648
+#define ID_GRID90 24701
+#define ID_GRID91 24715
+#define ID_GRID92 24716
+#define ID_GRID93 24782
+#define ID_GRID94 24783
+#define ID_GRID95 24784
+#define ID_GRID96 24785
+#define ID_INFO_MENU 24581
+#define ID_READ_MENU 24582
+#define ID_OPEN_MENU 24583
+#define ID_ORDERS_MENU 24630
+#define ID_ORDERS2_MENU 24828
+#define ID_CHARON_MENU 24628
+#define ID_JOIN_MENU 24584
+#define ID_GREEN_MENU 24626
+#define ID_RED_MENU 24627
+#define ID_REPORT_MENU 24732
+#define ID_REPORT2_MENU 24829
+#define ID_DECOMP_MENU 24629
+#define ID_DECRYPT_MENU 24631
+#define ID_PERSONA_MENU 24632
+#define ID_ADJUST_MENU 24643
+#define ID_ADJUST2_MENU 24830
+#define ID_PLAYBAK_MENU 24650
+#define ID_BLIND_MENU 24625
+#define ID_OSCILL_MENU 24649
+#define ID_KILL_MENU 24827
+#define ID_VIRUS_MENU 24651
+#define ID_SC90_FLOOR 24577
+#define ID_SC90_SMFLOOR 24591
+#define ID_DOOR_L90 24635
+#define ID_DOOR_L90F 24636
+#define ID_DOOR_R90 24579
+#define ID_DOOR_R90F 24600
+#define ID_JOIN_OBJECT 24604
+#define ID_OSCILLATOR 24641
+#define ID_EYEBALL_90 24644
+#define ID_EYE_90_TABLE 24652
+#define ID_SC91_FLOOR 24578
+#define ID_DOOR_L91 24580
+#define ID_DOOR_L91F 24601
+#define ID_DOOR_R91 24585
+#define ID_DOOR_R91F 24795
+#define ID_DOOR_T91 24606
+#define ID_DOOR_T91R 24607
+#define ID_BAG_91 24637
+#define ID_DECOMP_OBJ 24638
+#define ID_DECRYPT_OBJ 24639
+#define ID_REPORT_BOOK 24731
+#define ID_EYEBALL_91 24798
+#define ID_EYE_91_TABLE 24799
+#define ID_SLAB1 24586
+#define ID_SLAB2 24592
+#define ID_SLAB3 24593
+#define ID_SLAB4 24594
+#define ID_SLAB5 24595
+#define ID_SLAB6 24596
+#define ID_SLAB7 24597
+#define ID_SLAB8 24598
+#define ID_SLAB9 24599
+#define ID_BRIDGE_A 24610
+#define ID_BRIDGE_B 24611
+#define ID_BRIDGE_C 24612
+#define ID_BRIDGE_D 24613
+#define ID_BRIDGE_E 24614
+#define ID_BRIDGE_F 24615
+#define ID_BRIDGE_G 24616
+#define ID_BRIDGE_H 24617
+#define ID_DOOR_L92 24587
+#define ID_DOOR_L92F 24821
+#define ID_DOOR_R92 24588
+#define ID_DOOR_R92R 24807
+#define ID_GREEN_CIRCLE 24633
+#define ID_RED_CIRCLE 24634
+#define ID_SC93_FLOOR 24589
+#define ID_DOOR_L93 24590
+#define ID_DOOR_L93F 24792
+#define ID_PERSONA 24602
+#define ID_ADJUST_BOOK 24642
+#define ID_SC94_FLOOR 24603
+#define ID_DOOR_L94 24623
+#define ID_DOOR_L94R 24624
+#define ID_DOOR_R94 24621
+#define ID_DOOR_R94R 24622
+#define ID_HOLOGRAM_A 24729
+#define ID_HOLOGRAM_B 24767
+#define ID_HOLOGRAM_PAD 24730
+#define ID_SC95_FLOOR 24605
+#define ID_DOOR_L95 24608
+#define ID_DOOR_L95F 24609
+#define ID_DOOR_R95 24769
+#define ID_DOOR_R95F 24770
+#define ID_DOOR_T95 24619
+#define ID_DOOR_T95R 24620
+#define ID_GUARDIAN 24804
+#define ID_WEIGHT 24811
+#define ID_SC96_FLOOR 24777
+#define ID_DOOR_L96 24778
+#define ID_DOOR_L96F 24779
+#define ID_CRYSTAL 24786
+#define ID_VIRUS 24787
+#define BEFORE_SHRUG 5
+#define OFF_LEFT 104
+#define OFF_RIGHT 472
+#define DOOR_SHUT 1
+#define DOOR_OPEN 2
+#define DOOR_MOVING 3
+#define AR_OK 0
+#define AR_FAIL 1
+#define AR_ZERO 2
+#define L_BUTTON 2
+#define R_BUTTON 1
+#define F_UP 9
+#define W_UP 86
+#define M_UP 104
+#define G_UP 114
+#define K_UP 134
+#define ID_PIPE_TALK 144
+#define ID_MEDI 20511
+#define ID_WITNESS 20754
+#define ID_GALLAGHER 20812
+#define ID_KEN 20911
+#define ID_WALTER_TALK_UP 20983
+#define ID_WALTER_TALK_DWN 20984
+#define ID_WALTER_TALK_LFT 20985
+#define ID_WALTER_CONV 20986
+#define ID_MEDI_TALK_UP 20987
+#define ID_MEDI_TALK_DOWN 20988
+#define ID_MEDI_TALK_LEFT 20989
+#define ID_MEDI_TALK_RIGHT 20990
+#define ID_FOST_CONV_LEFT 20991
+#define ID_GALL_TALK_UP 20992
+#define ID_GALL_TALK_LEFT 20993
+#define ID_SC75_FREEZE_TLK 20994
+#define ID_SC75_DEAD_TLK 20995
+#define ID_KEN_TALK_UP 20996
+#define ID_KEN_TALK_DOWN 20997
+#define ID_KEN_TALK_LEFT 20998
+#define ID_KEN_TALK_RIGHT 20999
+#define ID_ANDROID_BABBLE 21000
+#define ID_STUCK_TALK 21001
+#define ID_FOST_PIPE_TALK 21002
+#define ID_SC66_FAST_LIST 20481
+#define ID_SC66_CHIP_LIST 20482
+#define ID_SC66_LOGIC_LIST 20483
+#define ID_SC66_MOUSE_LIST 20484
+#define ID_SC66_PALETTE 20485
+#define ID_RESET_66 20486
+#define ID_SC66_DOOR 20977
+#define ID_SC66_DOOR_CLOSE 20978
+#define ID_SC66_HOLE 20487
+#define ID_SC66_FOS_WALK_IN 20982
+#define ID_SC66_FOS_CRUSHED 20981
+#define ID_SC66_LO_BEAM 20969
+#define ID_SC66_LO_BEAM_ANM 20970
+#define ID_SC66_HI_BEAM 20966
+#define ID_SC66_HI_BEAM_AN1 20967
+#define ID_SC66_HI_BEAM_AN2 20968
+#define ID_SC66_ROCK1 20971
+#define ID_SC66_ROCK1_ANIM 20972
+#define ID_SC66_ROCK2 20973
+#define ID_SC66_ROCK2_ANIM 20974
+#define ID_SC66_ROCK3 20975
+#define ID_SC66_ROCK3_ANIM 20976
+#define ID_SC66_STONES 20979
+#define ID_SC66_STONES_ANIM 20980
+#define ID_SC67_FAST_LIST 20488
+#define ID_SC67_CHIP_LIST 20489
+#define ID_SC67_LOGIC_LIST 20490
+#define ID_SC67_MOUSE_LIST 20491
+#define ID_SC67_PALETTE 20492
+#define ID_GRID67 20502
+#define ID_RESET_66_67 20523
+#define ID_RESET_68_67 20529
+#define ID_SC67_FLOOR 20501
+#define ID_SC67_DOOR 20506
+#define ID_SC67_DOOR_OPEN 20508
+#define ID_SC67_DOOR_CLOSE 20509
+#define ID_SC67_BRICKWORK 20503
+#define ID_SC67_VEIN 20510
+#define ID_SC67_CLOT 20507
+#define ID_SC67_CRAWL 20521
+#define ID_SC67_DUSTOFF 20522
+#define ID_SC67_GETBRICK 20524
+#define ID_SC67_PLASTER 20526
+#define ID_SC67_PLAST_FALL 20525
+#define ID_SC67_PICK_PLAST 20651
+#define ID_SC67_BRICK 20527
+#define ID_SC67_BRICK_FALL 20528
+#define ID_SC67_PICK_BRICK 20650
+#define ID_SC67_STICK_IN 20652
+#define ID_SC67_PULL_OUT 20653
+#define ID_SC67_BRICK_HIT 20654
+#define ID_SC67_PLAST_HIT 20655
+#define ID_SC67_LPOCKET 20661
+#define ID_SC67_RPOCKET 20662
+#define ID_SC67_RUB_HEAD 20663
+#define ID_SC67_TRY_STICK 20677
+#define ID_SC67_CROWBAR 20678
+#define ID_SC67_BAR_FALL 20679
+#define ID_SC67_PUSS_LEAK 20680
+#define ID_SC67_MEDIFIX 20681
+#define ID_SC67_MEND 20682
+#define ID_SC67_MENDING 20683
+#define ID_SC67_ROCK 20504
+#define ID_SC67_ROCK_ANIM 20505
+#define ID_SC67_PULSE1 20493
+#define ID_SC67_PULSE1_ANIM 20497
+#define ID_SC67_PULSE2 20494
+#define ID_SC67_PULSE2_ANIM 20498
+#define ID_SC67_PULSE3 20495
+#define ID_SC67_PULSE3_ANIM 20499
+#define ID_SC67_PULSE4 20496
+#define ID_SC67_PULSE4_ANIM 20500
+#define ID_SC68_FAST_LIST 20512
+#define ID_SC68_CHIP_LIST 20513
+#define ID_SC68_LOGIC_LIST 20514
+#define ID_SC68_MOUSE_LIST 20515
+#define ID_SC68_PALETTE 20516
+#define ID_GRID68 20520
+#define ID_SC68_JOEY_LIST 20784
+#define ID_RESET_67_68 20517
+#define ID_RESET_69_68 20561
+#define ID_RESET_70_68 20599
+#define ID_SC68_FLOOR 20519
+#define ID_SC68_DOOR 20518
+#define ID_SC68_DOOR_CLOSE 20533
+#define ID_SC68_STAIRS 20532
+#define ID_SC68_DESCEND 20684
+#define ID_SC68_ASCEND 20685
+#define ID_SC68_EXIT 20558
+#define ID_SC68_SENSOR 20531
+#define ID_SC68_SENSOR_ANIM 20656
+#define ID_SC68_GRILL 20530
+#define ID_SC68_PULSE1 20534
+#define ID_SC68_PULSE1_ANIM 20540
+#define ID_SC68_PULSE2 20535
+#define ID_SC68_PULSE2_ANIM 20541
+#define ID_SC68_PULSE3 20536
+#define ID_SC68_PULSE3_ANIM 20542
+#define ID_SC68_PULSE4 20537
+#define ID_SC68_PULSE4_ANIM 20543
+#define ID_SC68_PULSE5 20538
+#define ID_SC68_PULSE5_ANIM 20544
+#define ID_SC68_PULSE6 20539
+#define ID_SC68_PULSE6_ANIM 20545
+#define ID_SC69_FAST_LIST 20552
+#define ID_SC69_CHIP_LIST 20553
+#define ID_SC69_LOGIC_LIST 20554
+#define ID_SC69_MOUSE_LIST 20555
+#define ID_SC69_PALETTE 20556
+#define ID_GRID69 20563
+#define ID_SC69_JOEY_LIST 20813
+#define ID_RESET_68_69 20559
+#define ID_RESET_71_69 20574
+#define ID_SC69_FLOOR 20557
+#define ID_SC69_EXIT 20560
+#define ID_SC69_GRILL 20562
+#define ID_SC69_DOOR 20570
+#define ID_SC69_PULSE1 20635
+#define ID_SC69_PULSE1_ANIM 20641
+#define ID_SC69_PULSE2 20636
+#define ID_SC69_PULSE2_ANIM 20642
+#define ID_SC69_PULSE3 20637
+#define ID_SC69_PULSE3_ANIM 20643
+#define ID_SC69_PULSE4 20638
+#define ID_SC69_PULSE4_ANIM 20644
+#define ID_SC69_PULSE5 20639
+#define ID_SC69_PULSE5_ANIM 20645
+#define ID_SC69_PULSE6 20640
+#define ID_SC69_PULSE6_ANIM 20646
+#define ID_SC70_FAST_LIST 20589
+#define ID_SC70_CHIP_LIST 20590
+#define ID_SC70_LOGIC_LIST 20591
+#define ID_SC70_MOUSE_LIST 20592
+#define ID_SC70_PALETTE 20593
+#define ID_GRID70 20597
+#define ID_RESET_68_70 20594
+#define ID_SC70_FLOOR 20595
+#define ID_SC70_DOOR 20598
+#define ID_SC70_IRIS 20600
+#define ID_SC70_BAR 20601
+#define ID_SC70_CONTROL 20602
+#define ID_SC70_GRILL 20603
+#define ID_SC70_CONSOL_ANIM 20647
+#define ID_SC70_PIT 20648
+#define ID_SC70_PIT_ANIM 20649
+#define ID_SC70_STEP_UP 20756
+#define ID_SC70_STEP_DOWN 20757
+#define ID_SC70_BAR_ANIM 20758
+#define ID_SC70_PULL_BAR 20759
+#define ID_SC70_ENTER_ANIM 20778
+#define ID_SC70_EXIT_ANIM 20779
+#define ID_SC710_FAST_LIST 20687
+#define ID_SC71_FAST_LIST 20564
+#define ID_SC71_CHIP_LIST 20565
+#define ID_SC71_LOGIC_LIST 20566
+#define ID_SC71_MOUSE_LIST 20567
+#define ID_SC71_PALETTE 20568
+#define ID_GRID71 20575
+#define ID_SC71_JOEY_LIST 20814
+#define ID_RESET_69_71 20571
+#define ID_RESET_72_71 20582
+#define ID_SC71_FAKE_FLOOR 20569
+#define ID_SC71_FLOOR 20572
+#define ID_SC71_DOOR69 20573
+#define ID_SC71_DOOR72 20579
+#define ID_SC71_LOCKED_DOOR 20674
+#define ID_SC71_MONITOR 20576
+#define ID_SC71_RECHARGER 20577
+#define ID_SC71_PANEL_ANIM 20776
+#define ID_SC71_PANEL2 20775
+#define ID_SC71_PANEL2_ANIM 20777
+#define ID_SC71_CONTROLS 20578
+#define ID_SC71_LIGHT2_ANIM 20768
+#define ID_SC71_LIGHT1 20766
+#define ID_SC71_LIGHT1_ANIM 20767
+#define ID_SC71_CHLITE 20769
+#define ID_SC71_CHLITE_ANIM 20770
+#define ID_SC71_MON_ANIM 20771
+#define ID_SC71_MEDI_CHARGE 20772
+#define ID_SC71_MEDI_GET_UP 20774
+#define ID_SC71_MEDI_SLOT 20675
+#define ID_SC71_USE_BOARD 20773
+#define ID_SC720_FAST_LIST 20686
+#define ID_SC72_FAST_LIST 20546
+#define ID_SC72_CHIP_LIST 20547
+#define ID_SC72_LOGIC_LIST 20548
+#define ID_SC72_MOUSE_LIST 20549
+#define ID_SC72_PALETTE 20550
+#define ID_GRID72 20596
+#define ID_SC72_JOEY_LIST 20815
+#define ID_RESET_71_72 20581
+#define ID_RESET_73_72 20614
+#define ID_SC72_FAKE_FLOOR 20551
+#define ID_SC72_FLOOR 20580
+#define ID_SC72_DOOR 20583
+#define ID_SC72_EXIT 20604
+#define ID_SC72_TAP 20588
+#define ID_SC72_TANK 20584
+#define ID_SC72_TANK_ANIM 20785
+#define ID_SC72_ROT_LIGHT 20792
+#define ID_SC72_ROTATING 20793
+#define ID_SC72_CHAMBER1 20585
+#define ID_SC72_CHAM1_ANIM 20789
+#define ID_SC72_CHAM1_LIGHT 20780
+#define ID_SC72_CHAM1_FLASH 20781
+#define ID_SC72_CHAMBER2 20586
+#define ID_SC72_CHAM2_ANIM 20790
+#define ID_SC72_CHAM2_LIGHT 20782
+#define ID_SC72_CHAM2_FLASH 20783
+#define ID_SC72_CHAMBER3 20761
+#define ID_SC72_CHAM3_ANIM 20791
+#define ID_SC72_COMPUTER 20765
+#define ID_SC72_COMP_FLASH 20786
+#define ID_SC72_COMPUTER2 20787
+#define ID_SC72_COMP2_FLASH 20788
+#define ID_SC72_LIGHT1 20762
+#define ID_SC72_LIGHT2 20763
+#define ID_SC72_LIGHT3 20764
+#define ID_SC72_GRILL 20587
+#define ID_SC72_WALTER_KILL 20794
+#define ID_SC72_FOSTER_DIE 20795
+#define ID_SC72_WALTER_DIE 20800
+#define ID_SC72_JOEY_TAP 20796
+#define ID_SC72_SPILL 20798
+#define ID_SC72_SPILL_ANIM 20799
+#define ID_SC72_DRIP_ANIM 20797
+#define ID_SC73_FAST_LIST 20605
+#define ID_SC73_CHIP_LIST 20606
+#define ID_SC73_LOGIC_LIST 20607
+#define ID_SC73_MOUSE_LIST 20608
+#define ID_SC73_PALETTE 20609
+#define ID_GRID73 20612
+#define ID_SC73_JOEY_LIST 20816
+#define ID_RESET_72_73 20610
+#define ID_RESET_74_73 20629
+#define ID_RESET_75_73 20673
+#define ID_SC73_FLOOR 20611
+#define ID_SC73_EXIT 20613
+#define ID_SC73_BIG_DOOR 20617
+#define ID_SC73_SENSOR 20618
+#define ID_SC73_SENSOR_ANIM 20657
+#define ID_SC73_DOOR 20619
+#define ID_SC73_LOCKED_DOOR 20676
+#define ID_SC73_CHAMBER3 20817
+#define ID_SC73_CHAM3_ANIM 20818
+#define ID_SC73_CHAMBER4 20615
+#define ID_SC73_CHAM4_ANIM 20658
+#define ID_SC73_CHAM4_LIGHT 20819
+#define ID_SC73_CHAM4_FLASH 20820
+#define ID_SC73_CHAMBER5 20616
+#define ID_SC73_CHAM5_ANIM 20755
+#define ID_SC73_CHAM5_LIGHT 20821
+#define ID_SC73_CHAM5_FLASH 20822
+#define ID_SC73_JOEY_LUNGE 20846
+#define ID_SC73_JOEY_FIGHT1 20847
+#define ID_SC73_GALL_FIGHT1 20848
+#define ID_SC73_JOEY_FIGHT2 20849
+#define ID_SC73_GALL_FIGHT2 20850
+#define ID_SC73_GET_BOARD 20859
+#define ID_SC73_SEARCH 20860
+#define ID_SC73_BITS 20851
+#define ID_SC73_BITS_ANIM 20852
+#define ID_SC73_BITS2 20853
+#define ID_SC73_BITS2_ANIM 20854
+#define ID_SC73_SPRAY 20855
+#define ID_SC73_SPRAY_ANIM 20856
+#define ID_SC74_FAST_LIST 20620
+#define ID_SC74_CHIP_LIST 20621
+#define ID_SC74_LOGIC_LIST 20622
+#define ID_SC74_MOUSE_LIST 20623
+#define ID_SC74_PALETTE 20624
+#define ID_GRID74 20627
+#define ID_RESET_73_74 20625
+#define ID_SC74_FLOOR 20626
+#define ID_SC74_DOOR 20628
+#define ID_SC74_INTERFACE 20633
+#define ID_SC74_INT_SLOT 20659
+#define ID_SC74_TERMINAL 20634
+#define ID_SC74_POD 20823
+#define ID_SC74_POD_DOWN 20824
+#define ID_SC74_POD_UP 20825
+#define ID_SC74_FOST_SIT 20826
+#define ID_SC74_GET_UP 20827
+#define ID_SC74_USECARD 20702
+#define ID_SC74_USECARD2 20704
+#define ID_SC74_RPOCKET 20862
+#define ID_SC74_MONITOR1 20630
+#define ID_SC74_MON1_ANIM 20801
+#define ID_SC74_MONITOR2 20802
+#define ID_SC74_MON2_ANIM 20803
+#define ID_SC74_MONITOR3 20804
+#define ID_SC74_MON3_ANIM 20805
+#define ID_SC74_MONITOR4 20806
+#define ID_SC74_MON4_ANIM 20807
+#define ID_SC74_LEFT_TV 20631
+#define ID_SC74_LTV_ANIM 20808
+#define ID_SC74_RIGHT_TV 20632
+#define ID_SC74_RTV_ANIM 20809
+#define ID_SC74_LIGHTS 20810
+#define ID_SC74_LIGHTS_ANIM 20811
+#define ID_SC75_FAST_LIST 20664
+#define ID_SC75_CHIP_LIST 20665
+#define ID_SC75_LOGIC_LIST 20666
+#define ID_SC75_MOUSE_LIST 20667
+#define ID_SC75_PALETTE 20668
+#define ID_GRID75 20671
+#define ID_SC75_JOEY_LIST 20912
+#define ID_RESET_73_75 20669
+#define ID_RESET_76_75 20698
+#define ID_RS_TONGS_EMPTY 20868
+#define ID_RS_TONGS_LIVE 20869
+#define ID_RS_TONGS_FROZEN 20870
+#define ID_RS_TONGS_DEAD 20871
+#define ID_SC75_FLOOR 20670
+#define ID_SC75_BIG_DOOR 20672
+#define ID_SC75_DOOR 20696
+#define ID_SC75_TONGS 20660
+#define ID_SC75_GET_TONGS 20857
+#define ID_SC75_NITRO_TANK 20699
+#define ID_SC75_NITRO_ANIM 20828
+#define ID_SC75_LIVE_TANK 20700
+#define ID_SC75_TANK_ANIM 20872
+#define ID_SC75_CONSOLE 20703
+#define ID_SC75_MON_ANIM 20829
+#define ID_SC75_CRASH_ANIM 20701
+#define ID_SC75_LIGHT1 20830
+#define ID_SC75_LIGHT1_ANIM 20831
+#define ID_SC75_LIGHT2 20832
+#define ID_SC75_LIGHT2_ANIM 20833
+#define ID_SC75_USECARD 20858
+#define ID_SC75_RPOCKET 20861
+#define ID_SC75_HAND_TANK 20875
+#define ID_SC75_GET_TISS 20863
+#define ID_SC75_FREEZE_IT 20864
+#define ID_SC75_FREEZE_IT2 20865
+#define ID_SC75_FREEZE_DED 20866
+#define ID_SC75_FREEZE_DED2 20867
+#define ID_SC75_FREEZE_TALK 126
+#define ID_SC75_DEAD_TALK 128
+#define ID_SC76_FAST_LIST 20688
+#define ID_SC76_CHIP_LIST 20689
+#define ID_SC76_LOGIC_LIST 20690
+#define ID_SC76_MOUSE_LIST 20691
+#define ID_SC76_PALETTE 20692
+#define ID_GRID76 20695
+#define ID_SC76_JOEY_LIST 20913
+#define ID_RESET_75_76 20693
+#define ID_RESET_77_76 20727
+#define ID_SC76_FLOOR 20694
+#define ID_SC76_DOOR75 20697
+#define ID_SC76_DOOR77 20725
+#define ID_SC76_ANDROID_1 20705
+#define ID_SC76_HATCH_1 20909
+#define ID_SC76_ANDROID_2 20706
+#define ID_SC76_HATCH_2 20910
+#define ID_SC76_ANDROID_3 20707
+#define ID_SC76_HATCH_3 20908
+#define ID_SC76_PUNCH 20916
+#define ID_SC76_FOSTFALL 20917
+#define ID_SC76_CONSOLE_1 20711
+#define ID_SC76_CONSOLE_2 20712
+#define ID_SC76_CONSOLE_3 20713
+#define ID_SC76_CABINET_1 20714
+#define ID_SC76_CAB1_OPEN 20890
+#define ID_SC76_CAB1_CLOSE 20891
+#define ID_SC76_CABINET_2 20715
+#define ID_SC76_CAB2_OPEN 20892
+#define ID_SC76_CAB2_CLOSE 20893
+#define ID_SC76_CABINET_3 20716
+#define ID_SC76_CAB3_OPEN 20894
+#define ID_SC76_CAB3_CLOSE 20895
+#define ID_SC76_BOARD_1 20896
+#define ID_SC76_BOARD_2 20897
+#define ID_SC76_BOARD_3 20898
+#define ID_SC76_OPEN_CAB 20899
+#define ID_SC76_SHUT_CAB 20900
+#define ID_SC76_LOW_GET 20901
+#define ID_SC76_LIGHT1 20834
+#define ID_SC76_LIGHT1_ANIM 20835
+#define ID_SC76_LIGHT2 20836
+#define ID_SC76_LIGHT2_ANIM 20837
+#define ID_SC76_LIGHT3 20838
+#define ID_SC76_LIGHT3_ANIM 20839
+#define ID_SC76_LIGHT4 20840
+#define ID_SC76_LIGHT4_ANIM 20841
+#define ID_SC76_LIGHT5 20842
+#define ID_SC76_LIGHT5_ANIM 20843
+#define ID_SC76_LIGHT6 20844
+#define ID_SC76_LIGHT6_ANIM 20845
+#define ID_SC76_LIGHT7 20902
+#define ID_SC76_LIGHT7_ANIM 20903
+#define ID_SC76_LIGHT8 20904
+#define ID_SC76_LIGHT8_ANIM 20905
+#define ID_SC76_LIGHT9 20906
+#define ID_SC76_LIGHT9_ANIM 20907
+#define ID_SC76_AND2_BABBLE 142
+#define ID_SC77_FAST_LIST 20717
+#define ID_SC77_CHIP_LIST 20718
+#define ID_SC77_LOGIC_LIST 20719
+#define ID_SC77_MOUSE_LIST 20720
+#define ID_SC77_PALETTE 20721
+#define ID_GRID77 20724
+#define ID_SC77_JOEY_LIST 20914
+#define ID_RESET_76_77 20722
+#define ID_RESET_78_77 20742
+#define ID_SC77_FLOOR 20723
+#define ID_SC77_DOOR76 20726
+#define ID_SC77_BIG_DOOR 20728
+#define ID_SC77_TANK_1 20729
+#define ID_SC77_TANK_2 20730
+#define ID_SC77_HAND_1 20731
+#define ID_SC77_HAND_2 20732
+#define ID_SC77_DOOR_OPEN 20760
+#define ID_SC77_FPUSHL_1 20918
+#define ID_SC77_FPUSHL_2 20919
+#define ID_SC77_FPUSHR_1 20920
+#define ID_SC77_FPUSHR_2 20921
+#define ID_SC77_KPUSHR_1 20922
+#define ID_SC77_KPUSHR_2 20923
+#define ID_SC77_STRETCH 20924
+#define ID_SC78_FAST_LIST 20733
+#define ID_SC78_CHIP_LIST 20734
+#define ID_SC78_LOGIC_LIST 20735
+#define ID_SC78_MOUSE_LIST 20736
+#define ID_SC78_PALETTE 20737
+#define ID_SC781_PALETTE 20964
+#define ID_GRID78 20740
+#define ID_RESET_77_78 20738
+#define ID_RESET_79_78 20753
+#define ID_SC78_LEDGE 20739
+#define ID_SC78_PIPE 20915
+#define ID_SC78_BIG_DOOR 20741
+#define ID_SC78_EXIT 20743
+#define ID_SC78_SUPPORT 20874
+#define ID_SC78_JUMP_DOWN 20881
+#define ID_SC78_CLIMB_UP 20883
+#define ID_SC79_FAST_LIST 20744
+#define ID_SC79_CHIP_LIST 20745
+#define ID_SC79_LOGIC_LIST 20746
+#define ID_SC79_MOUSE_LIST 20747
+#define ID_SC79_PALETTE 20748
+#define ID_SC791_PALETTE 20963
+#define ID_GRID79 20751
+#define ID_RESET_78_79 20749
+#define ID_RESET_80_79 20889
+#define ID_SC79_PIPE 20750
+#define ID_SC79_EXIT 20752
+#define ID_SC79_SUPPORT 20873
+#define ID_SC79_LADDER 20940
+#define ID_SC79_CROUCH_DOWN 20941
+#define ID_SC79_CROUCH_UP 20942
+#define ID_SC79_CLIMB_DOWN 20943
+#define ID_SC79_CLIMB_UP 20944
+#define ID_SC79_TIE_ROPE 20951
+#define ID_SC79_TOSS_ROPE 20952
+#define ID_SC79_KNOT 20955
+#define ID_SC79_ROPE 20887
+#define ID_SC79_ROPE_ANIM 20953
+#define ID_SC80_FAST_LIST 20876
+#define ID_SC80_CHIP_LIST 20877
+#define ID_SC80_LOGIC_LIST 20878
+#define ID_SC80_MOUSE_LIST 20879
+#define ID_SC80_PALETTE 20880
+#define ID_SC801_PALETTE 20959
+#define ID_SC802_PALETTE 20960
+#define ID_SC803_PALETTE 20961
+#define ID_SC804_PALETTE 20962
+#define ID_RESET_79_80 20882
+#define ID_SC80_SPOUT 20884
+#define ID_SC80_LADDER 20950
+#define ID_SC80_ROPE 20888
+#define ID_SC80_ORIFICE 20885
+#define ID_SC80_EXIT 20886
+#define ID_SC80_EXIT_OPEN 20954
+#define ID_SC80_GOO 20925
+#define ID_SC80_GOO_ANIM 20926
+#define ID_SC80_BUBBLE1 20927
+#define ID_SC80_BUB1_ANIM 20928
+#define ID_SC80_BUBBLE2 20929
+#define ID_SC80_BUBBLE3 20930
+#define ID_SC80_BUBBLE4 20931
+#define ID_SC80_BUBBLE5 20932
+#define ID_SC80_BUBBLE6 20933
+#define ID_SC80_BUBBLE7 20934
+#define ID_SC80_BUBBLE8 20935
+#define ID_SC80_BUBBLE9 20936
+#define ID_SC80_BUBBLE10 20937
+#define ID_SC80_BUBBLE11 20938
+#define ID_SC80_BUBBLE12 20939
+#define ID_SC80_CLIMB_DOWN 20945
+#define ID_SC80_CLIMB_UP 20946
+#define ID_SC80_PIPE_SHRUG 20965
+#define ID_SC80_CLAMBER 20947
+#define ID_SC80_GET_ROPE 20948
+#define ID_SC80_SWING 20949
+#define ID_SC80_DROP 20956
+#define ID_SC80_SAMPLE 20957
+#define ID_SC80_SAMPLE_FALL 20958
+#define IT_MEDI 32
+#define IT_MEDI_TALK 180
+#define IT_WITNESS 159
+#define IT_GALLAGHER 90
+#define IT_GALL_TALK 91
+#define IT_SC66_LAYER_0 20
+#define IT_SC66_DOOR 105
+#define IT_SC66_HI_BEAM_AN1 98
+#define IT_SC66_HI_BEAM_AN2 99
+#define IT_SC66_LO_BEAM_ANM 22
+#define IT_SC66_ROCK1 100
+#define IT_SC66_ROCK2 101
+#define IT_SC66_ROCK3 102
+#define IT_SC66_STONES 103
+#define IT_SC66_FOS_CRUSHED 104
+#define IT_SC66_FOS_WALK_IN 21
+#define IT_SC67_LAYER_0 23
+#define IT_SC67_LAYER_1 24
+#define IT_SC67_GRID_1 25
+#define IT_SC67_PULSE1 26
+#define IT_SC67_PULSE2 27
+#define IT_SC67_PULSE3 28
+#define IT_SC67_PULSE4 29
+#define IT_SC67_DOOR 30
+#define IT_SC67_ROCK 31
+#define IT_SC67_CRAWL 46
+#define IT_SC67_DUSTOFF 47
+#define IT_SC67_GETBRICK 48
+#define IT_SC67_BRICK 55
+#define IT_SC67_PLASTER 56
+#define IT_SC67_PICK_BRICK 129
+#define IT_SC67_PICK_PLAST 130
+#define IT_SC67_STICK_IN 131
+#define IT_SC67_PULL_OUT 132
+#define IT_SC67_BRICK_HIT 133
+#define IT_SC67_PLAST_HIT 134
+#define IT_SC67_LPOCKET 141
+#define IT_SC67_RPOCKET 142
+#define IT_SC67_RUB_HEAD 143
+#define IT_SC67_PUSS 149
+#define IT_SC67_MEDIFIX 150
+#define IT_SC67_MENDING 151
+#define IT_SC67_CROWBAR 152
+#define IT_SC68_LAYER_0 43
+#define IT_SC68_LAYER_1 44
+#define IT_SC68_GRID_1 45
+#define IT_SC68_DOOR 57
+#define IT_SC68_PULSE1 58
+#define IT_SC68_PULSE2 59
+#define IT_SC68_PULSE3 60
+#define IT_SC68_PULSE4 61
+#define IT_SC68_PULSE5 62
+#define IT_SC68_PULSE6 63
+#define IT_SC68_SENSOR 137
+#define IT_SC68_DESCEND 153
+#define IT_SC68_ASCEND 154
+#define IT_SC69_LAYER_0 71
+#define IT_SC69_LAYER_1 72
+#define IT_SC69_LAYER_2 74
+#define IT_SC69_GRID_1 75
+#define IT_SC69_GRID_2 76
+#define IT_SC69_PULSE1 109
+#define IT_SC69_PULSE2 110
+#define IT_SC69_PULSE3 111
+#define IT_SC69_PULSE4 112
+#define IT_SC69_PULSE5 113
+#define IT_SC69_PULSE6 114
+#define IT_SC70_LAYER_0 90
+#define IT_SC70_LAYER_1 91
+#define IT_SC70_LAYER_2 92
+#define IT_SC70_GRID_1 93
+#define IT_SC70_GRID_2 94
+#define IT_SC70_IRIS 95
+#define IT_SC70_BAR 96
+#define IT_SC70_CONSOLE 115
+#define IT_SC70_GRILL 116
+#define IT_SC70_PIT 117
+#define IT_SC70_STEP_UP 14
+#define IT_SC70_STEP_DOWN 15
+#define IT_SC70_PULL_BAR 18
+#define IT_SC70_ENTER_ANIM 97
+#define IT_SC71_LAYER_0 85
+#define IT_SC71_LAYER_1 86
+#define IT_SC71_LAYER_2 87
+#define IT_SC71_GRID_1 88
+#define IT_SC71_GRID_2 89
+#define IT_SC710_LAYER_0 70
+#define IT_SC710_LAYER_1 16
+#define IT_SC710_LAYER_2 157
+#define IT_SC710_GRID_1 17
+#define IT_SC710_GRID_2 158
+#define IT_SC71_LIGHT1 162
+#define IT_SC71_LIGHT2 163
+#define IT_SC71_SCREEN 164
+#define IT_SC71_CHARGE_LIGHT 165
+#define IT_SC71_MEDI_CHARGE 166
+#define IT_SC71_USE_BOARD 148
+#define IT_SC71_PANEL 167
+#define IT_SC71_PANEL2 168
+#define IT_SC72_LAYER_0 64
+#define IT_SC72_LAYER_1 65
+#define IT_SC72_LAYER_2 66
+#define IT_SC72_GRID_1 68
+#define IT_SC72_GRID_2 69
+#define IT_SC720_LAYER_0 67
+#define IT_SC720_LAYER_1 160
+#define IT_SC720_LAYER_2 155
+#define IT_SC720_GRID_1 161
+#define IT_SC720_GRID_2 156
+#define IT_WALTER_CONVERSATION 129
+#define IT_WALTER_TALK_UP 130
+#define IT_WALTER_TALK_DOWN 131
+#define IT_WALTER_TALK_LEFT 132
+#define IT_SC72_CHAM1_LIGHT 171
+#define IT_SC72_CHAM2_LIGHT 172
+#define IT_SC72_TANK 173
+#define IT_SC72_ROT_LIGHT 177
+#define IT_SC72_COMPUTER 169
+#define IT_SC72_COMPUTER2 170
+#define IT_SC72_CHAMBER1 174
+#define IT_SC72_CHAMBER2 175
+#define IT_SC72_CHAMBER3 176
+#define IT_SC72_WALTER_KILL 178
+#define IT_SC72_FOSTER_DIE 179
+#define IT_SC72_WALTER_DIE 30
+#define IT_SC72_GRILL 28
+#define IT_SC72_JOEY_TAP 29
+#define IT_SC72_SPILL 31
+#define IT_SC73_LAYER_0 97
+#define IT_SC73_LAYER_1 98
+#define IT_SC73_LAYER_2 99
+#define IT_SC73_GRID_1 101
+#define IT_SC73_GRID_2 102
+#define IT_SC73_BIG_DOOR 138
+#define IT_SC73_SENSOR 139
+#define IT_SC73_CHAMBER3 142
+#define IT_SC73_CHAMBER4 140
+#define IT_SC73_CHAMBER5 141
+#define IT_SC73_CHAM4_LIGHT 95
+#define IT_SC73_CHAM5_LIGHT 96
+#define IT_SC73_JOEY_LUNGE 85
+#define IT_SC73_JOEY_FIGHT1 86
+#define IT_SC73_GALL_FIGHT1 87
+#define IT_SC73_JOEY_FIGHT2 88
+#define IT_SC73_GALL_FIGHT2 89
+#define IT_SC73_GET_BOARD 43
+#define IT_SC73_SEARCH 44
+#define IT_SC73_DEAD_GALL 72
+#define IT_SC73_BITS 74
+#define IT_SC73_BITS2 75
+#define IT_SC73_SPRAY 76
+#define IT_SC74_LAYER_0 104
+#define IT_SC74_LAYER_1 105
+#define IT_SC74_LAYER_2 106
+#define IT_SC74_GRID_1 107
+#define IT_SC74_GRID_2 108
+#define IT_SC74_POD_DOWN 109
+#define IT_SC74_POD_UP 110
+#define IT_SC74_FOST_SIT 92
+#define IT_SC74_GET_UP 93
+#define IT_SC74_USECARD 70
+#define IT_SC74_USECARD2 71
+#define IT_SC74_RPOCKET 62
+#define IT_SC74_MONITOR1 55
+#define IT_SC74_MONITOR2 56
+#define IT_SC74_MONITOR3 57
+#define IT_SC74_MONITOR4 58
+#define IT_SC74_LEFT_TV 59
+#define IT_SC74_RIGHT_TV 60
+#define IT_SC74_LIGHTS 61
+#define IT_SC75_LAYER_0 144
+#define IT_SC75_LAYER_1 145
+#define IT_SC75_LAYER_2 146
+#define IT_SC75_GRID_1 147
+#define IT_SC75_GRID_2 148
+#define IT_SC75_MONITOR 149
+#define IT_SC75_CRASH 164
+#define IT_SC75_TANK 165
+#define IT_SC75_STEAM 150
+#define IT_SC75_LIGHT1 151
+#define IT_SC75_LIGHT2 152
+#define IT_SC75_TONGS 153
+#define IT_SC75_GET_TONGS 154
+#define IT_SC75_USECARD 155
+#define IT_SC75_RPOCKET 156
+#define IT_SC75_HAND_TANK 166
+#define IT_SC75_GET_TISS 157
+#define IT_SC75_FREEZE_IT 158
+#define IT_SC75_FREEZE_TALK 159
+#define IT_SC75_FREEZE_IT2 160
+#define IT_SC75_FREEZE_DED 161
+#define IT_SC75_DEAD_TALK 162
+#define IT_SC75_FREEZE_DED2 163
+#define IT_SC76_LAYER_0 14
+#define IT_SC76_LAYER_1 15
+#define IT_SC76_LAYER_2 16
+#define IT_SC76_GRID_1 17
+#define IT_SC76_GRID_2 18
+#define IT_SC76_LIGHT1 29
+#define IT_SC76_LIGHT2 30
+#define IT_SC76_LIGHT3 31
+#define IT_SC76_LIGHT4 32
+#define IT_SC76_LIGHT5 43
+#define IT_SC76_LIGHT6 44
+#define IT_SC76_LIGHT7 45
+#define IT_SC76_LIGHT8 46
+#define IT_SC76_LIGHT9 47
+#define IT_SC76_CABINET_1 55
+#define IT_SC76_CABINET_2 56
+#define IT_SC76_CABINET_3 57
+#define IT_SC76_BOARD_1 58
+#define IT_SC76_BOARD_2 59
+#define IT_SC76_BOARD_3 60
+#define IT_SC76_OPEN_CAB 62
+#define IT_SC76_LOW_GET 63
+#define IT_KEN 61
+#define IT_SC76_HATCH_1 66
+#define IT_SC76_ANDROID_2 67
+#define IT_SC76_HATCH_2 68
+#define IT_SC76_ANDROID_3 64
+#define IT_SC76_HATCH_3 65
+#define IT_SC76_PUNCH 69
+#define IT_SC76_FOSTFALL 70
+#define IT_SC76_KEN_TALK 71
+#define IT_SC76_AND2_BABBLE 72
+#define IT_SC77_LAYER_0 20
+#define IT_SC77_LAYER_1 21
+#define IT_SC77_GRID_1 22
+#define IT_SC77_BIG_DOOR 28
+#define IT_SC77_FPUSHL 74
+#define IT_SC77_FPUSHR 75
+#define IT_SC77_KPUSHR 76
+#define IT_SC77_STRETCH 87
+#define IT_SC78_LAYER_0 23
+#define IT_SC78_LAYER_1 24
+#define IT_SC78_GRID_1 25
+#define IT_SC78_JUMP_DOWN 85
+#define IT_SC78_CLIMB_UP 86
+#define IT_SC79_LAYER_0 26
+#define IT_SC79_SUPPORT 48
+#define IT_SC79_CROUCH 91
+#define IT_SC79_CLIMB 92
+#define IT_SC79_TIE_ROPE 96
+#define IT_SC79_TOSS_ROPE 97
+#define IT_SC79_ROPE 98
+#define IT_SC79_KNOT 101
+#define IT_SC80_LAYER_0 27
+#define IT_SC80_EXIT 100
+#define IT_SC80_ROPE 99
+#define IT_SC80_GOO 88
+#define IT_SC80_BUBBLE 89
+#define IT_SC80_CLIMB 90
+#define IT_SC80_CLAMBER 93
+#define IT_SC80_GET_ROPE 94
+#define IT_SC80_SWING 95
+#define IT_SC80_DROP 103
+#define IT_SC80_SAMPLE 104
+#define IT_SC80_PIPE_TALK 105
+#define IT_SC80_PIPE_SHRUG 106
+#define IDO_DOG_FOOD 49
+#define ID_SC30_FAST_LIST 16385
+#define ID_SC30_CHIP_LIST 16386
+#define ID_SC30_LOGIC_LIST 16387
+#define ID_SC30_MOUSE_LIST 16388
+#define ID_SC30_PALETTE 16389
+#define ID_SC30_WALK_GRID 16390
+#define ID_SC30_JOEY_LIST 16547
+#define ID_RESET_31_30 16413
+#define ID_RESET_33_30 16414
+#define ID_RESET_36_30 16539
+#define ID_RESET_42_30 16811
+#define ID_RESET_COURT_OPEN 16813
+#define ID_SC30_FLOOR 16392
+#define ID_SC30_EXIT_31 16393
+#define ID_SC30_EXIT_33 16394
+#define ID_SC30_EXIT_36 16492
+#define ID_SC30_COURT_DOOR 16487
+#define ID_SC30_COURT_CLOSE 16814
+#define ID_SC30_NOTICE 16489
+#define ID_SC30_STATUE_1 16490
+#define ID_SC30_STATUE_2 16491
+#define ID_SC30_HENRI 16516
+#define ID_SC30_HENRI_TALK 16517
+#define ID_SC30_HENRI_TIE 16518
+#define ID_SC30_HEN_STEP_F 16519
+#define ID_SC30_HEN_STEP_B 16520
+#define ID_SC30_HEN_BLINK 16521
+#define ID_SC30_PUSH_DOOR 16522
+#define ID_SC31_FAST_LIST 16774
+#define ID_SC31_CHIP_LIST 16396
+#define ID_SC31_LOGIC_LIST 16397
+#define ID_SC31_MOUSE_LIST 16398
+#define ID_SC31_PALETTE 16399
+#define ID_SC31_WALK_GRID 16400
+#define ID_SC31_JOEY_LIST 16548
+#define ID_RESET_START_31 16391
+#define ID_RESET_30_31 16401
+#define ID_RESET_32_31 16425
+#define ID_RESET_39_31 16463
+#define ID_RS_GUARD_CHAT 16699
+#define ID_RS_GUARD_AVAIL 16700
+#define ID_SC31_FLOOR 16402
+#define ID_SC31_EXIT_30 16403
+#define ID_SC31_EXIT_32 16415
+#define ID_SC31_EXIT_39 16453
+#define ID_SC31_LIFT_SLOT 16493
+#define ID_SC31_LIFT 16513
+#define ID_SC31_LIFT_OPEN 16514
+#define ID_SC31_LIFT_CLOSE 16515
+#define ID_SC31_USE_CARD 16523
+#define ID_SC31_PULL_ROPE 16551
+#define ID_SC31_LOWER_ROPE 16552
+#define ID_SC31_DROP_ROPE 16553
+#define ID_SC31_END_OF_ROPE 16554
+#define ID_SC31_ROPE 16555
+#define ID_SC31_ROPE_PULLED 16556
+#define ID_SC31_ROPE_LOWER 16557
+#define ID_SC31_ROPE_DROP 16558
+#define ID_SC31_BRICKS 16559
+#define ID_SC31_BRICKS_UP 16560
+#define ID_SC31_BRICKS_DOWN 16561
+#define ID_SC31_BRICKS_FALL 16562
+#define ID_SC31_PLANK 16563
+#define ID_SC31_PLANK_DROP 16564
+#define ID_SC31_PLANK_RAISE 16565
+#define ID_SC31_PLANK_FLICK 16566
+#define ID_SC31_GUARD 16601
+#define ID_SC31_GUARD_TALK 16607
+#define ID_SC31_GUARD_BLINK 16693
+#define ID_SC31_GUARD_MOVE 16694
+#define ID_SC31_GUARD_REACH 16695
+#define ID_SC31_GUARD_TALK2 16696
+#define ID_SC31_GET_BRICKS 16683
+#define ID_SC31_GET_PLANK 16687
+#define ID_SC31_CLIMB_PLANK 16684
+#define ID_SC31_DOG_FLY 16685
+#define ID_SC31_DOG_RISE 16697
+#define ID_SC31_DOG_SWIM 16698
+#define ID_SC31_PUT_BISC 16688
+#define ID_SC31_BISCUITS 16692
+#define ID_SC31_BISC_PLACED 16689
+#define ID_SC31_BISC_DROP 16690
+#define ID_SC31_BISC_RAISE 16691
+#define ID_SC32_FAST_LIST 16416
+#define ID_SC32_CHIP_LIST 16417
+#define ID_SC32_LOGIC_LIST 16418
+#define ID_SC32_MOUSE_LIST 16419
+#define ID_SC32_PALETTE 16420
+#define ID_SC32_WALK_GRID 16421
+#define ID_SC32_JOEY_LIST 16549
+#define ID_RESET_31_32 16422
+#define ID_RESET_33_32 16429
+#define ID_RESET_38_32 16452
+#define ID_SC32_FLOOR 16423
+#define ID_SC32_EXIT_31 16424
+#define ID_SC32_EXIT_33 16426
+#define ID_SC32_LIFT 16442
+#define ID_SC32_LIFT_OPEN 16540
+#define ID_SC32_LIFT_CLOSE 16541
+#define ID_SC32_TERMINAL 16494
+#define ID_SC32_BUZZER 16495
+#define ID_SC32_PLANT_1 16496
+#define ID_SC32_PLANT_2 16497
+#define ID_SC32_PLANT_3 16498
+#define ID_SC32_USE_CARD 16524
+#define ID_SC32_USE_COM 16525
+#define ID_SC32_VINCENT 16599
+#define ID_SC32_VINC_ANIM 16602
+#define ID_SC32_GARDENER 16600
+#define ID_SC32_GARDENING1 16622
+#define ID_SC32_GARDENING2 16623
+#define ID_SC32_GARDENER_UP 16624
+#define ID_SC32_GARDENER_DN 16625
+#define ID_SC32_GARD_TURN_D 16626
+#define ID_SC32_GARD_TURN_U 16627
+#define ID_SC32_GARDEN_TALK 16628
+#define ID_SC33_FAST_LIST 16404
+#define ID_SC33_CHIP_LIST 16405
+#define ID_SC33_LOGIC_LIST 16406
+#define ID_SC33_MOUSE_LIST 16407
+#define ID_SC33_PALETTE 16408
+#define ID_SC33_WALK_GRID 16409
+#define ID_SC33_JOEY_LIST 16550
+#define ID_RESET_30_33 16410
+#define ID_RESET_32_33 16427
+#define ID_RESET_34_33 16440
+#define ID_SC33_FLOOR 16411
+#define ID_SC33_EXIT_30 16412
+#define ID_SC33_EXIT_32 16428
+#define ID_SC33_SHED_DOOR 16430
+#define ID_SC33_LOCK 16499
+#define ID_SC33_USE_CARD 16526
+#define ID_SC33_PUSH_DOOR1 16527
+#define ID_SC33_PUSH_DOOR2 16528
+#define ID_SC33_DOOR_OPEN 16529
+#define ID_SC34_FAST_LIST 16431
+#define ID_SC34_CHIP_LIST 16432
+#define ID_SC34_LOGIC_LIST 16433
+#define ID_SC34_MOUSE_LIST 16434
+#define ID_SC34_PALETTE 16435
+#define ID_SC34_WALK_GRID 16436
+#define ID_RESET_33_34 16437
+#define ID_SC34_FLOOR 16438
+#define ID_SC34_DOOR 16439
+#define ID_SC34_SECATEURS 16501
+#define ID_SC34_TKT_MACHINE 16502
+#define ID_SC34_MAP 16503
+#define ID_SC34_BRICKS 16504
+#define ID_SC34_GET_SECS 16544
+#define ID_SC34_STAIRS1 16545
+#define ID_SC34_STAIRS2 16546
+#define ID_SC36_FAST_LIST 16530
+#define ID_SC36_CHIP_LIST 16531
+#define ID_SC36_LOGIC_LIST 16532
+#define ID_SC36_MOUSE_LIST 16533
+#define ID_SC36_PALETTE 16534
+#define ID_SC36_WALK_GRID 16535
+#define ID_RESET_30_36 16536
+#define ID_RESET_37_36 16593
+#define ID_RESET_COLSTON 16812
+#define ID_SC36_FLOOR 16537
+#define ID_SC36_LOW_FLOOR 16596
+#define ID_SC36_EXIT_30 16538
+#define ID_SC36_DOOR 16583
+#define ID_SC36_DOOROPEN 16792
+#define ID_SC36_DOORSHUT 16793
+#define ID_SC36_FOS_DOWN1 16748
+#define ID_SC36_FOS_DOWN2 16749
+#define ID_SC36_FOS_UP1 16750
+#define ID_SC36_FOS_UP2 16751
+#define ID_SC36_PRESS_PLATE 16752
+#define ID_SC36_USE_JUKEBOX 16753
+#define ID_SC36_REACH_GLASS 16759
+#define ID_SC36_GET_GLASS 16760
+#define ID_SC36_JUKEBOX 16597
+#define ID_SC36_JUKE_TALK 16756
+#define ID_SC36_JUKE_STUCK 16770
+#define ID_SC36_JUKE_BREAK 16771
+#define ID_SC36_JUKE_LIGHT 16757
+#define ID_SC36_JUKEBOX_ON 16754
+#define ID_SC36_JUKEBOX_OFF 16755
+#define ID_SC36_JUKE_KICKED 16758
+#define ID_SC36_CARDS 16742
+#define ID_SC36_GLASS 16747
+#define ID_SC36_SENSOR 16594
+#define ID_SC36_BAND 16598
+#define ID_SC36_BAND_ANIM 16686
+#define ID_SC36_BARMAN 16701
+#define ID_BAR_BLINK 16702
+#define ID_BAR_GET_DRINK 16703
+#define ID_BAR_DRINK 16704
+#define ID_BAR_GET_CLOTH 16705
+#define ID_BAR_PUT_CLOTH 16706
+#define ID_BAR_WIPE 16707
+#define ID_BAR_WIPE2 16708
+#define ID_BARMAN_TALK 16709
+#define ID_SC36_BABS 16772
+#define ID_SC36_BABS_TALK 16773
+#define ID_SC36_COLSTON 16731
+#define ID_SC36_COL_FEET 16732
+#define ID_SC36_COL_TALK1 16743
+#define ID_SC36_COL_TALK2 16744
+#define ID_SC36_COL_DRINK 16746
+#define ID_SC36_COL_DEAL 16733
+#define ID_SC36_COL_THINK 16734
+#define ID_SC36_COL_BLINK1 16735
+#define ID_SC36_COL_BLINK2 16736
+#define ID_SC36_COL_DOWN1 16761
+#define ID_SC36_COL_DOWN2 16762
+#define ID_SC36_COL_DOWN3 16763
+#define ID_SC36_COL_DOWN4 16764
+#define ID_SC36_COL_UP1 16765
+#define ID_SC36_COL_UP2 16766
+#define ID_SC36_COL_UP3 16767
+#define ID_SC36_COL_UP4 16768
+#define ID_SC36_COL_KICK 16769
+#define ID_SC36_GALLAGHER 16737
+#define ID_SC36_GAL_TALK 16745
+#define ID_SC36_GAL_LEGS 16738
+#define ID_SC36_GAL_DEAL 16739
+#define ID_SC36_GAL_LOOK1 16740
+#define ID_SC36_GAL_LOOK2 16741
+#define ID_SC37_FAST_LIST 16584
+#define ID_SC37_CHIP_LIST 16585
+#define ID_SC37_LOGIC_LIST 16586
+#define ID_SC37_MOUSE_LIST 16587
+#define ID_SC37_PALETTE 16588
+#define ID_SC37_WALK_GRID 16589
+#define ID_RESET_36_37 16590
+#define ID_SC37_FLOOR 16591
+#define ID_SC37_HOLDING_LID 16794
+#define ID_SC37_DOOR 16592
+#define ID_SC37_DOOROPEN 16790
+#define ID_SC37_DOORSHUT 16791
+#define ID_SC37_SENSOR 16595
+#define ID_SC37_BIG_BOX 16620
+#define ID_SC37_FLIMSY_BOX 16619
+#define ID_SC37_WINE_RACK 16799
+#define ID_SC37_LID 16621
+#define ID_SC37_LIDUP 16786
+#define ID_SC37_LIDDOWN 16787
+#define ID_SC37_LIDUSED 16788
+#define ID_SC37_GRILL 16617
+#define ID_SC37_GRILLOPEN 16789
+#define ID_SC37_CRBARBOX 16775
+#define ID_SC37_GETLID 16776
+#define ID_SC37_PUTLID 16777
+#define ID_SC37_USELID 16778
+#define ID_SC37_STEPUP 16779
+#define ID_SC37_FOOTDROP 16780
+#define ID_SC37_STEPDOWN 16781
+#define ID_SC37_USEBAR 16782
+#define ID_SC37_USESEC 16783
+#define ID_SC37_CLIMBOUT 16784
+#define ID_SC37_THUMBSUP 16785
+#define ID_SC38_FAST_LIST 16443
+#define ID_SC38_CHIP_LIST 16444
+#define ID_SC38_LOGIC_LIST 16445
+#define ID_SC38_MOUSE_LIST 16446
+#define ID_SC38_PALETTE 16447
+#define ID_SC38_WALK_GRID 16448
+#define ID_RESET_32_38 16449
+#define ID_RESET_SPUNKY_38 16616
+#define ID_RESET_DANI_SIT 16608
+#define ID_RESET_DANI_STAND 16609
+#define ID_RESET_DANI_32 16629
+#define ID_RESET_SPUNKY_32 16630
+#define ID_SC38_FLOOR 16450
+#define ID_SC38_LIFT 16451
+#define ID_SC38_LIFT_UP 16542
+#define ID_SC38_LIFT_DOWN 16543
+#define ID_SC38_STATUE 16505
+#define ID_SC38_MONITOR 16506
+#define ID_SC38_VIDEO 16507
+#define ID_SC38_SOFA 16508
+#define ID_SC38_DOG_TRAY 16615
+#define ID_SC38_BISCUITS 16618
+#define ID_SC38_HAND_SET 16610
+#define ID_SC38_RINGER 16611
+#define ID_SC38_RINGER_ANIM 16612
+#define ID_DANIELLE 16441
+#define ID_DANI_CONV 16603
+#define ID_SC38_DANI_ANIM_1 16395
+#define ID_SC38_DANI_ANIM_2 16488
+#define ID_SC38_DANI_ANIM_3 16500
+#define ID_SC38_DANI_GET_UP 16605
+#define ID_SC38_DANI_SATTLK 16604
+#define ID_SC38_GET_PHONE 16613
+#define ID_SC38_PHONE_TALK 16614
+#define ID_SPUNKY 16486
+#define ID_SNIFF_LEFT 16509
+#define ID_SNIFF_RIGHT 16510
+#define ID_PISS_LEFT 16511
+#define ID_PISS_RIGHT 16512
+#define ID_BARK 16637
+#define ID_SC38_USE_VIDEO 16631
+#define ID_SC38_VIDEO_ANIM 16632
+#define ID_SC38_SCREEN_1 16633
+#define ID_SC38_SCREEN_2 16634
+#define ID_SC38_SCREEN_3 16635
+#define ID_SC38_SCREEN_4 16636
+#define ID_SC38_REACH_FOOD 16638
+#define ID_SC38_GET_FOOD 16639
+#define ID_SC39_FAST_LIST 16454
+#define ID_SC39_CHIP_LIST 16455
+#define ID_SC39_LOGIC_LIST 16456
+#define ID_SC39_MOUSE_LIST 16457
+#define ID_SC39_PALETTE 16458
+#define ID_SC39_WALK_GRID 16459
+#define ID_RESET_31_39 16460
+#define ID_RESET_40_39 16475
+#define ID_RESET_41_39 16485
+#define ID_SC39_FLOOR 16461
+#define ID_SC39_EXIT_31 16462
+#define ID_SC39_EXIT_40 16464
+#define ID_SC39_EXIT_41 16465
+#define ID_SC40_FAST_LIST 16466
+#define ID_SC40_CHIP_LIST 16467
+#define ID_SC40_LOGIC_LIST 16468
+#define ID_SC40_MOUSE_LIST 16469
+#define ID_SC40_PALETTE 16470
+#define ID_SC40_WALK_GRID 16471
+#define ID_RESET_39_40 16472
+#define ID_SC40_FLOOR 16473
+#define ID_SC40_EXIT_39 16474
+#define ID_SC40_CABINET 16567
+#define ID_SC40_TROLLEY 16568
+#define ID_SC40_LOCKER_1 16569
+#define ID_SC40_LOCKER_2 16570
+#define ID_SC40_LOCKER_3 16571
+#define ID_SC40_LOCKER_4 16572
+#define ID_SC40_LOCKER_5 16573
+#define ID_SC40_LOCKER_OPEN 16574
+#define ID_SC40_LOCKER_SHUT 16575
+#define ID_SC40_BODY_1 16576
+#define ID_SC40_BODY_2 16577
+#define ID_SC40_BODY_3 16578
+#define ID_SC40_BODY_4 16579
+#define ID_SC40_BODY_5 16580
+#define ID_SC40_OPEN_DOOR 16581
+#define ID_SC40_CLOSE_DOOR 16582
+#define ID_SC41_FAST_LIST 16476
+#define ID_SC41_CHIP_LIST 16477
+#define ID_SC41_LOGIC_LIST 16478
+#define ID_SC41_MOUSE_LIST 16479
+#define ID_SC41_PALETTE 16480
+#define ID_SC41_WALK_GRID 16481
+#define ID_RESET_39_41 16482
+#define ID_SC41_FLOOR 16483
+#define ID_SC41_EXIT_39 16484
+#define ID_SC42_FAST_LIST 16802
+#define ID_SC42_CHIP_LIST 16803
+#define ID_SC42_LOGIC_LIST 16804
+#define ID_SC42_MOUSE_LIST 16805
+#define ID_SC42_PALETTE 16806
+#define ID_SC42_WALK_GRID 16807
+#define ID_RESET_30_42 16808
+#define ID_SC44_FAST_LIST 16640
+#define ID_SC44_CHIP_LIST 16641
+#define ID_SC44_LOGIC_LIST 16642
+#define ID_SC44_MOUSE_LIST 16643
+#define ID_SC44_PALETTE 16644
+#define ID_SC44_WALK_GRID 16645
+#define ID_RESET_37_44 16646
+#define ID_RESET_45_44 16647
+#define ID_SC44_FLOOR 16648
+#define ID_SC44_EXIT_45 16649
+#define ID_SC44_GRILL 16795
+#define ID_SC44_RUBBLE 16800
+#define ID_SC45_FAST_LIST 16650
+#define ID_SC45_CHIP_LIST 16651
+#define ID_SC45_LOGIC_LIST 16652
+#define ID_SC45_MOUSE_LIST 16653
+#define ID_SC45_PALETTE 16654
+#define ID_SC45_WALK_GRID 16655
+#define ID_RESET_44_45 16656
+#define ID_RESET_46_45 16657
+#define ID_RESET_47_45 16658
+#define ID_SC45_FLOOR 16659
+#define ID_SC45_EXIT_44 16660
+#define ID_SC45_EXIT_46 16661
+#define ID_SC45_EXIT_47 16662
+#define ID_SC46_FAST_LIST 16663
+#define ID_SC46_CHIP_LIST 16664
+#define ID_SC46_LOGIC_LIST 16665
+#define ID_SC46_MOUSE_LIST 16666
+#define ID_SC46_PALETTE 16667
+#define ID_SC46_WALK_GRID 16668
+#define ID_RESET_45_46 16669
+#define ID_SC46_FLOOR 16670
+#define ID_SC46_EXIT_45 16671
+#define ID_SC46_RUBBLE 16801
+#define ID_SC47_FAST_LIST 16672
+#define ID_SC47_CHIP_LIST 16673
+#define ID_SC47_LOGIC_LIST 16674
+#define ID_SC47_MOUSE_LIST 16675
+#define ID_SC47_PALETTE 16676
+#define ID_SC47_WALK_GRID 16677
+#define ID_RESET_45_47 16678
+#define ID_RESET_48_47 16679
+#define ID_SC47_FLOOR 16680
+#define ID_SC47_EXIT_45 16681
+#define ID_SC47_EXIT_48 16682
+#define ID_SC48_FAST_LIST 16710
+#define ID_SC48_CHIP_LIST 16711
+#define ID_SC48_LOGIC_LIST 16712
+#define ID_SC48_MOUSE_LIST 16713
+#define ID_SC48_PALETTE 16714
+#define ID_SC48_WALK_GRID 16715
+#define ID_RESET_47_48 16716
+#define ID_RESET_65_48 16717
+#define ID_SC48_FLOOR 16718
+#define ID_SC48_EXIT_47 16719
+#define ID_SC48_EXIT_65 16720
+#define ID_SC65_FAST_LIST 16721
+#define ID_SC65_CHIP_LIST 16722
+#define ID_SC65_LOGIC_LIST 16723
+#define ID_SC65_MOUSE_LIST 16724
+#define ID_SC65_PALETTE 16725
+#define ID_SC65_WALK_GRID 16726
+#define ID_RESET_48_65 16727
+#define ID_SC65_FLOOR 16728
+#define ID_SC65_EXIT_48 16729
+#define ID_SC65_EXIT_66 16730
+#define ID_SC65_POSTER1 16796
+#define ID_SC65_POSTER2 16797
+#define ID_SC65_SIGN 16798
+#define IT_SC30_LAYER_0 14
+#define IT_SC30_LAYER_1 15
+#define IT_SC30_LAYER_2 16
+#define IT_SC30_GRID_1 17
+#define IT_SC30_GRID_2 18
+#define IT_SC30_HENRI_TALK 87
+#define IT_SC30_HENRI_TIE 88
+#define IT_SC30_HENRI_STEP 89
+#define IT_SC30_HENRI_BLINK 90
+#define IT_SC30_COURT_DOOR 134
+#define IT_SC30_PUSH_DOOR 91
+#define IT_SC31_LAYER_0 20
+#define IT_SC31_LAYER_1 21
+#define IT_SC31_GRID_1 22
+#define IT_SC31_LIFT 86
+#define IT_SC31_USE_CARD 92
+#define IT_SC31_PULL_ROPE 31
+#define IT_SC31_HOLD_ROPE 108
+#define IT_SC31_LOWER_ROPE 32
+#define IT_SC31_DROP_ROPE 43
+#define IT_SC31_PLANK 44
+#define IT_SC31_BRICK_UP 45
+#define IT_SC31_BRICK_FALL 46
+#define IT_SC31_END_OF_ROPE 61
+#define IT_SC31_ROPE_PULLED 62
+#define IT_SC31_ROPE_LOWER 63
+#define IT_SC31_ROPE_DROP 64
+#define IT_SC31_GUARD_TALK 101
+#define IT_SC31_GUARD_BLINK 110
+#define IT_SC31_GUARD_MOVE 129
+#define IT_SC31_GUARD_REACH 130
+#define IT_SC31_GUARD_TALK2 131
+#define IT_SC31_GET_BRICKS 102
+#define IT_SC31_GET_PLANK 106
+#define IT_SC31_CLIMB_PLANK 103
+#define IT_SC31_DOG_FLY 104
+#define IT_SC31_DOG_RISE 132
+#define IT_SC31_DOG_SWIM 133
+#define IT_SC31_PUT_BISC 109
+#define IT_SC31_HAND 107
+#define IT_SC31_BISCUITS 105
+#define IT_SC32_LAYER_0 23
+#define IT_SC32_LAYER_1 24
+#define IT_SC32_LAYER_2 98
+#define IT_SC32_GRID_1 25
+#define IT_SC32_GRID_2 99
+#define IT_SC32_USE_CARD 93
+#define IT_SC32_USE_COM 94
+#define IT_SC32_LIFT 100
+#define IT_SC32_VINCENT 56
+#define IT_SC32_VINC_TALK 57
+#define IT_SC32_GARDENER 58
+#define IT_SC32_GARD_TURN 59
+#define IT_SC32_GARDEN_TALK 60
+#define IT_SC33_LAYER_0 26
+#define IT_SC33_LAYER_1 27
+#define IT_SC33_LAYER_2 28
+#define IT_SC33_GRID_1 29
+#define IT_SC33_GRID_2 30
+#define IT_SC33_USE_CARD 95
+#define IT_SC33_PUSH_DOOR 96
+#define IT_SC33_SHED_DOOR 97
+#define IT_SC34_LAYER_0 31
+#define IT_SC34_LAYER_1 32
+#define IT_SC34_GRID_1 43
+#define IT_SC34_STAIRS1 102
+#define IT_SC34_STAIRS2 103
+#define IT_SC34_SECATEURS 104
+#define IT_SC34_GET_SECS 105
+#define IT_SC36_LAYER_0 20
+#define IT_SC36_LAYER_1 21
+#define IT_SC36_LAYER_2 22
+#define IT_SC36_LAYER_3 56
+#define IT_SC36_GRID_1 23
+#define IT_SC36_GRID_2 24
+#define IT_SC36_GRID_3 57
+#define IT_SC36_BABS 106
+#define IT_SC36_BABS_TALK 107
+#define IT_SC36_FOS_DOWN1 92
+#define IT_SC36_FOS_DOWN2 93
+#define IT_SC36_FOS_UP1 94
+#define IT_SC36_FOS_UP2 95
+#define IT_SC36_PRESS_PLATE 70
+#define IT_SC36_USE_JUKEBOX 67
+#define IT_SC36_GET_GLASS 96
+#define IT_SC36_JUKE_LIGHT 68
+#define IT_SC36_JUKEBOX 86
+#define IT_SC36_GLASS 66
+#define IT_SC36_BAND 31
+#define IT_SC36_BARMAN 32
+#define IT_BARMAN_TALK 61
+#define IT_SC36_COLSTON 47
+#define IT_SC36_COL_FEET 48
+#define IT_SC36_COL_TALK1 62
+#define IT_SC36_COL_TALK2 63
+#define IT_SC36_COL_DRINK 69
+#define IT_SC36_COL_DOWN1 97
+#define IT_SC36_COL_DOWN2 98
+#define IT_SC36_COL_DOWN3 99
+#define IT_SC36_COL_DOWN4 100
+#define IT_SC36_COL_UP1 101
+#define IT_SC36_COL_UP2 102
+#define IT_SC36_COL_UP3 103
+#define IT_SC36_COL_UP4 104
+#define IT_SC36_COL_KICK 105
+#define IT_SC36_GALLAGHER 58
+#define IT_SC36_GAL_LEGS 59
+#define IT_SC36_GAL_TALK 64
+#define IT_SC36_CARDS 60
+#define IT_SC36_DOOR 144
+#define IT_SC37_LAYER_0 14
+#define IT_SC37_LAYER_1 15
+#define IT_SC37_LAYER_2 16
+#define IT_SC37_GRID_1 17
+#define IT_SC37_GRID_2 18
+#define IT_SC37_CRBARBOX 108
+#define IT_SC37_GETLID 109
+#define IT_SC37_USELID 110
+#define IT_SC37_STEPUP 129
+#define IT_SC37_FOOTDROP 130
+#define IT_SC37_STEPDOWN 131
+#define IT_SC37_USEBAR 132
+#define IT_SC37_USESEC 133
+#define IT_SC37_CLIMBOUT 134
+#define IT_SC37_THUMBSUP 136
+#define IT_SC37_BOXLID 137
+#define IT_SC37_LIDUP 138
+#define IT_SC37_LIDUSED 139
+#define IT_SC37_LOOSEBIT 140
+#define IT_SC37_GRILL 141
+#define IT_SC37_GRILLOPEN 142
+#define IT_SC37_DOOR 143
+#define IT_SC38_LAYER_0 44
+#define IT_SC38_LAYER_1 45
+#define IT_SC38_LAYER_2 46
+#define IT_SC38_GRID_1 47
+#define IT_SC38_GRID_2 48
+#define IT_DANIELLE 55
+#define IT_DANI_CONV 85
+#define IT_SPUNKY 71
+#define IT_SC38_SEXY_DANI 106
+#define IT_SC38_DANI_ANIMS 107
+#define IT_SC38_DANI_SATTLK 108
+#define IT_SC38_DANI_GET_UP 109
+#define IT_SNIFF_LEFT 72
+#define IT_SNIFF_RIGHT 74
+#define IT_PISS_LEFT 75
+#define IT_PISS_RIGHT 76
+#define IT_BARK 65
+#define IT_SC38_FOSTER_LIFT 101
+#define IT_SC38_HAND_SET 110
+#define IT_SC38_RINGER 129
+#define IT_SC38_GET_PHONE 130
+#define IT_SC38_PHONE_TALK 131
+#define IT_SC38_USE_VIDEO 132
+#define IT_SC38_VIDEO_ANIM 133
+#define IT_SC38_SCREEN_1 134
+#define IT_SC38_SCREEN_2 136
+#define IT_SC38_SCREEN_3 137
+#define IT_SC38_SCREEN_4 138
+#define IT_SC38_GET_FOOD 139
+#define IT_SC39_LAYER_0 56
+#define IT_SC39_LAYER_1 57
+#define IT_SC39_LAYER_2 58
+#define IT_SC39_GRID_1 59
+#define IT_SC39_GRID_2 60
+#define IT_SC40_LAYER_0 61
+#define IT_SC40_LAYER_1 62
+#define IT_SC40_LAYER_2 63
+#define IT_SC40_GRID_1 64
+#define IT_SC40_GRID_2 65
+#define IT_SC40_LOCKER 14
+#define IT_SC40_OPEN_DOOR 15
+#define IT_SC40_CLOSE_DOOR 16
+#define IT_SC41_LAYER_0 66
+#define IT_SC41_LAYER_1 67
+#define IT_SC41_LAYER_2 68
+#define IT_SC41_GRID_1 69
+#define IT_SC41_GRID_2 70
+#define IT_SC42_LAYER_0 20
+#define IT_SC42_LAYER_1 21
+#define IT_SC42_GRID_1 23
+#define IT_SC44_LAYER_0 25
+#define IT_SC45_LAYER_0 26
+#define IT_SC45_LAYER_1 27
+#define IT_SC45_GRID_1 28
+#define IT_SC46_LAYER_0 29
+#define IT_SC47_LAYER_0 30
+#define IT_SC48_LAYER_0 43
+#define IT_SC65_LAYER_0 44
+#define IT_SC65_LAYER_1 45
+#define IT_SC65_GRID_1 46
+#define ID_RADMAN 8205
+#define ID_FACT3_R_EXIT 8459
+#define ID_LOCKER3 8460
+#define ID_LOCKER2 8463
+#define ID_LOCKER1 8464
+#define ID_MACHINE 8467
+#define ID_STUMP 8468
+#define ID_S16_FLOOR 8473
+#define ID_ENTRANCE_EXIT 8478
+#define ID_REACTOR_PC 8480
+#define ID_REACTOR_DOOR 8481
+#define ID_RAD_SCREEN 8482
+#define ID_14_CONSOLE 8483
+#define ID_COAT 8484
+#define ID_SENSORS 8500
+#define ID_REACTOR_LOWER 8502
+#define ID_S17_FLOOR 8507
+#define ID_CORE_EXIT 8511
+#define ID_PULSE 8513
+#define ID_PULSEB 8515
+#define ID_ANITA_CARD 8517
+#define ID_CONSOLE_12 8524
+#define ID_JUNK1 4339
+#define ID_JUNK2 4340
+#define ID_LIFT7_LIGHT 8529
+#define ID_S29_FLOOR 12292
+#define ID_LIFT_29 12296
+#define ID_S29_CARD_SLOT 12299
+#define ID_LIFT29_LIGHT 12301
+#define ID_RIGHT_EXIT_29 12304
+#define ID_S23_FLOOR 12309
+#define ID_LEFT_EXIT_23 12313
+#define ID_ANCHOR_EXIT_23 12315
+#define ID_S25_FLOOR 12320
+#define ID_ANCHOR_EXIT_25 12324
+#define ID_TRAVEL_EXIT_23 12327
+#define ID_S24_FLOOR 12332
+#define ID_LEFT_EXIT_24 12336
+#define ID_LEFT_EXIT_29 12338
+#define ID_S28_FLOOR 12343
+#define ID_RIGHT_EXIT_28 12347
+#define ID_LEFT_EXIT_28 12349
+#define ID_S19_FLOOR 12354
+#define ID_RIGHT_EXIT_19 12358
+#define ID_LEFT_EXIT_19 12361
+#define ID_S26_FLOOR 12366
+#define ID_RIGHT_EXIT_26 12370
+#define ID_DUSTBIN_28 12372
+#define ID_POSTER1 12375
+#define ID_POSTER2 12376
+#define ID_POSTER3 12377
+#define ID_POSTER4 12378
+#define ID_26_PLANT 12379
+#define ID_LEAFLET 12380
+#define ID_HOLO 12381
+#define ID_BIN_23 12382
+#define ID_SCULPTURE 12383
+#define ID_LINK_23 12384
+#define ID_WRECK_23 12385
+#define ID_LONDON_POSTER 12386
+#define ID_NEW_YORK 12387
+#define ID_MURAL 12388
+#define ID_PIDGEONS 12405
+#define ID_LEFT_EXIT_26 12390
+#define ID_S27_FLOOR 12395
+#define ID_RIGHT_EXIT_27 12399
+#define ID_CHART1 12401
+#define ID_CHART2 12402
+#define ID_GAS 12403
+#define ID_SCANNER_27 12404
+#define ID_BURKE 12407
+#define ID_MEDI_COMP 12425
+#define ID_CHAIR_27 12417
+#define ID_HELMET_COLE 12420
+#define ID_BODY 12429
+#define ID_ANCHOR 12430
+#define ID_ANCHOR_PC 3
+#define ID_HOOK 12434
+#define ID_STATUE_25 12435
+#define ID_LAZER_25 12437
+#define ID_SPARK_25 12439
+#define ID_TREVOR 12442
+#define ID_UP_EXIT_28 12447
+#define ID_S20_FLOOR 12452
+#define ID_DOWN_EXIT_20 12456
+#define ID_REICH_DOOR_20 12459
+#define ID_REICH_SLOT 12462
+#define ID_S22_FLOOR 12467
+#define ID_RIGHT_EXIT_22 12471
+#define ID_LAMB_DOOR_20 12474
+#define ID_LAMB_SLOT 12477
+#define ID_S21_FLOOR 12482
+#define ID_LEFT_EXIT_21 12486
+#define ID_SHRUB_1 12488
+#define ID_SHRUB_2 12489
+#define ID_SHRUB_3 12490
+#define ID_LAMB_BED 12492
+#define ID_LAMB_TV 12493
+#define ID_FISH_TANK 12494
+#define ID_FISH_POSTER 12495
+#define ID_PILLOW 12496
+#define ID_MAGAZINE 12501
+#define ID_REICH_CHAIR 12505
+#define ID_CABINET 12506
+#define ID_CERT 12507
+#define ID_REICH_PICTURE 12508
+#define ID_FISH_FOOD 12509
+#define ID_LAMBS_BOOKS 12510
+#define ID_LAMBS_CHAIR 12511
+#define ID_DISPENSOR 12512
+#define ID_CATFOOD 12514
+#define ID_VIDEO 12516
+#define ID_CASSETTE 12517
+#define ID_BIG_PICT1 12524
+#define ID_VIDEO_SCREEN 12525
+#define ID_BIG_PICT2 12526
+#define ID_BIG_PICT3 12527
+#define ID_CAT 12534
+#define ID_BIO_DOOR 12541
+#define ID_SALES_CHART 12545
+#define ID_GALLAGER_BEL 12546
+#define ID_FAKE_FLOOR_22 12554
+#define ID_REICH_WINDOW 12555
+#define ID_LAMB_WINDOW 12556
+#define ID_FAKE_FLOOR_21 12557
+#define ID_INNER_LAMB_DOOR 12558
+#define ID_TICKET 12565
+#define ID_GLOBE 12568
+#define ID_INNER_R_DOOR 12571
+#define ID_GLASS_SLOT 12574
+#define ID_LIFT_WAIT 42
+#define ID_CABLE_7 8204
+#define ID_CABLE_29 12588
+#define ID_S11_FLOOR 12594
+#define ID_CABLE_FALL 12601
+#define ID_CABLE_FALL2 12604
+#define ID_SMASHED_WINDOW 12605
+#define ID_BITS 12607
+#define ID_BITS2 12609
+#define ID_LOCKER_11 12613
+#define ID_SPY_11 12612
+#define ID_SLOT_11 12616
+#define ID_SOCCER_1 12620
+#define ID_SOCCER_2 12621
+#define ID_SOCCER_3 12622
+#define ID_SOCCER_4 12623
+#define ID_SOCCER_5 12624
+#define ID_SLAT_1 12626
+#define ID_SLAT_2 12627
+#define ID_SLAT_3 12628
+#define ID_SLAT_4 12629
+#define ID_SLAT_5 12630
+#define ID_RIGHT_EXIT_11 12631
+#define ID_S10_FLOOR 12636
+#define ID_LEFT_EXIT_10 12641
+#define ID_LIFT_10 12642
+#define ID_LIFT_SLOT_10 12644
+#define ID_SCANNER_10 12647
+#define ID_POD 12648
+#define ID_LINC_10 12651
+#define ID_POD_LIGHT 12652
+#define ID_MONITOR_10 12657
+#define ID_LIYT_1 12659
+#define ID_LIYT_2 12661
+#define ID_LIYT_3 12663
+#define ID_LIYT_4 12665
+#define ID_LITEBANK 12667
+#define ID_TERMINAL_10 12670
+#define ID_FAKE_FLOOR_9 8536
+#define ID_FAKE_FLOOR_10 12672
+#define ID_LINC_S9 8543
+#define ID_SMALL_23 12676
+#define ID_SMALL_R_29 12677
+#define ID_SMALL_L_29 12678
+#define ID_SMALL_R_28 12679
+#define ID_SMALL_L_28 12680
+#define ID_SMALL_19 12681
+#define ID_S29_SML_FLOOR 12682
+#define ID_S28_SML_FLOOR 12686
+#define ID_LIFT_28 12694
+#define ID_SLOT_28 12697
+#define ID_COPTER 8209
+#define ID_SC48_SOCKET 16815
+#define ID_SC48_HOLE 16816
+#define ID_SC48_EYES 16817
+#define ID_SC48_LIGHT_PAL 16820
+#define ID_SC42_JUDGE 16821
+#define ID_SC42_CLERK 16822
+#define ID_SC42_PROSECUTION 16823
+#define ID_SC42_JOBSWORTH 16824
+#define ID_SC42_SIGN 16849
+#define ID_DOG_BARK_THING 16855
+#define ID_SC41_HEAT_1 16856
+#define ID_SC41_HEAT_2 16857
+#define ID_SC41_HEAT_3 16858
+#define ID_FIRE1 4360
+#define ID_FIRE2 4362
+#define ID_CAR_UP 4364
+#define ID_CAR_DOWN 4366
+#define C_LOGIC 0
+#define C_STATUS 2
+#define C_SYNC 4
+#define C_SCREEN 6
+#define C_PLACE 8
+#define C_GET_TO_TABLE 10
+#define C_XCOOD 14
+#define C_YCOOD 16
+#define C_FRAME 18
+#define C_CURSOR_TEXT 20
+#define C_MOUSE_ON 22
+#define C_MOUSE_OFF 24
+#define C_MOUSE_CLICK 26
+#define C_MOUSE_REL_X 28
+#define C_MOUSE_REL_Y 30
+#define C_MOUSE_SIZE_X 32
+#define C_MOUSE_SIZE_Y 34
+#define C_ACTION_SCRIPT 36
+#define C_UP_FLAG 38
+#define C_DOWN_FLAG 40
+#define C_GET_TO_FLAG 42
+#define C_FLAG 44
+#define C_MOOD 46
+#define C_GRAFIX_PROG 48
+#define C_OFFSET 52
+#define C_MODE 54
+#define C_BASE_SUB 56
+#define C_ACTION_SUB 60
+#define C_GET_TO_SUB 64
+#define C_EXTRA_SUB 68
+#define C_DIR 72
+#define C_STOP_SCRIPT 74
+#define C_MINI_BUMP 76
+#define C_LEAVING 78
+#define C_AT_WATCH 80
+#define C_AT_WAS 82
+#define C_ALT 84
+#define C_REQUEST 86
+
+//system flags
+#define SF_TIMER (1 << 0) // set if timer interrupt redirected
+#define SF_GRAPHICS (1 << 1) // set if screen is in graphics mode
+#define SF_MOUSE (1 << 2) // set if mouse handler installed
+#define SF_KEYBOARD (1 << 3) // set if keyboard interrupt redirected
+#define SF_MUSIC_BOARD (1 << 4) // set if a music board detected
+#define SF_ROLAND (1 << 5) // set if roland board present
+#define SF_ADLIB (1 << 6) // set if adlib board present
+#define SF_SBLASTER (1 << 7) // set if sblaster present
+#define SF_TANDY (1 << 8) // set if tandy present
+#define SF_MUSIC_BIN (1 << 9) // set if music driver is loaded
+#define SF_PLUS_FX (1 << 10) // set if extra fx module needed
+#define SF_FX_OFF (1 << 11) // set if fx disabled
+#define SF_MUS_OFF (1 << 12) // set if music disabled
+#define SF_TIMER_TICK (1 << 13) // set every timer interupt
+
+// Status flags
+#define SF_CHOOSING (1 << 14) // set when choosing text
+#define SF_NO_SCROLL (1 << 15) // when set don't scroll
+#define SF_SPEED (1 << 16) // when set allow speed options
+#define SF_GAME_RESTORED (1 << 17) // set when game restored or restarted
+#define SF_REPLAY_RST (1 << 18) // set when loading restart data (used to stop rewriting of replay file)
+#define SF_SPEECH_FILE (1 << 19) // set when loading speech file
+#define SF_VOC_PLAYING (1 << 20) // set when a voc file is playing
+#define SF_PLAY_VOCS (1 << 21) // set when we want speech instead of text
+#define SF_CRIT_ERR (1 << 22) // set when critical error routine trapped
+#define SF_ALLOW_SPEECH (1 << 23) // speech allowes on cd sblaster version
+#define SF_ALLOW_TEXT (1 << 24) // text allowed on cd sblaster version
+#define SF_ALLOW_QUICK (1 << 25) // when set allow speed playing
+#define SF_TEST_DISK (1 << 26) // set when loading files
+#define SF_MOUSE_LOCKED (1 << 27) // set if coordinates are locked
+
+// Mouse flags
+#define MF_NO_UPDATE (1 << 0) // set to disable mouse updating
+#define MF_IN_INT (1 << 1) // set when in mouse interrupt
+#define MF_SAVED (1 << 2) // set when saved data is valid
+#define MF_GOT_INT (1 << 3) // set when mouse interrupt received
+
+#define MOUSE_NORMAL 1 // normal mouse
+#define MOUSE_DISK 2 // disk mouse
+#define MOUSE_DOWN 3
+#define MOUSE_RIGHT 4 // right pointer
+#define MOUSE_LEFT 5 // left pointer
+#define MOUSE_BLANK 6 // blank mouse
+#define MOUSE_CROSS 7 // angry mouse
+#define MOUSE_UP 8 // mouse up
+
+#define TEXT_MOUSE_WIDTH 0x80
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sky/sound.cpp b/engines/sky/sound.cpp
new file mode 100644
index 0000000000..0cd015d49d
--- /dev/null
+++ b/engines/sky/sound.cpp
@@ -0,0 +1,1259 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sky/disk.h"
+#include "sky/logic.h"
+#include "sky/sky.h"
+#include "sky/skydefs.h"
+#include "sky/sound.h"
+#include "sky/struc.h"
+
+namespace Sky {
+
+#define SOUND_FILE_BASE 60203
+#define MAX_FX_NUMBER 393
+#define SFXF_START_DELAY 0x80
+#define SFXF_SAVE 0x20
+
+#if !defined(__GNUC__)
+#pragma START_PACK_STRUCTS
+#endif
+
+struct RoomList {
+ uint8 room;
+ uint8 adlibVolume;
+ uint8 rolandVolume;
+} GCC_PACK;
+
+struct Sfx {
+ uint8 soundNo;
+ uint8 flags;
+ RoomList roomList[10];
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+#pragma END_PACK_STRUCTS
+#endif
+
+uint16 Sound::_speechConvertTable[8] = {
+ 0, //;Text numbers to file numbers
+ 600, //; 553 lines in section 0
+ 600+500, //; 488 lines in section 1
+ 600+500+1330, //;1303 lines in section 2
+ 600+500+1330+950, //; 922 lines in section 3
+ 600+500+1330+950+1150, //;1140 lines in section 4
+ 600+500+1330+950+1150+550, //; 531 lines in section 5
+ 600+500+1330+950+1150+550+150, //; 150 lines in section 6
+};
+
+
+static Sfx fx_null = {
+ 0,
+ 0,
+ {
+ { 200,127,127 },
+ { 255,0,0 }
+ }
+};
+
+static Sfx fx_level_3_ping = {
+ 1,
+ 0,
+ {
+ { 28,63,63 },
+ { 29,63,63 },
+ { 31,63,63 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_factory_sound = {
+ 1,
+ SFXF_SAVE,
+ {
+ { 255,30,30 },
+ }
+};
+
+static Sfx fx_crowbar_plaster = {
+ 1,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_masonry_fall = {
+ 1,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_prise_brick = {
+ 2,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_rope_creak = {
+ 2,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_ping = {
+ 3,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_force_fire_door = {
+ 3,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_brick_hit_foster = {
+ 3,
+ 10+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_brick_hit_plank = {
+ 3,
+ 8+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_rm3_lift_moving = {
+ 4,
+ SFXF_SAVE,
+ {
+ { 3,127,127 },
+ { 2,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_weld = {
+ 4,
+ 0,
+ {
+ { 15,127,127 },
+ { 7,127,127 },
+ { 6,60,60 },
+ { 12,60,60 },
+ { 13,60,60 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_weld12 = {
+ 4,
+ 0,
+ {
+ { 12,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_spray_on_skin = {
+ 4,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_plank_vibrating = {
+ 4,
+ 6+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_press_bang = {
+ 5,
+ 0,
+ {
+ { 0,50,100 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_spanner_clunk = {
+ 5,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_break_crystals = {
+ 5,
+ 0,
+ {
+ { 96,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_press_hiss = {
+ 6,
+ 0,
+ {
+ { 0,40,40 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_open_door = {
+ 6,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_open_lamb_door = {
+ 6,
+ 0,
+ {
+ { 20,127,127 },
+ { 21,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_splash = {
+ 6,
+ 22+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_disintegrate = {
+ 7,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_buzzer = {
+ 7,
+ 4+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_lathe = {
+ 7,
+ SFXF_SAVE,
+ {
+ { 4,60,60 },
+ { 2,20,20 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_hit_crowbar_brick = {
+ 7,
+ 9+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_hello_helga = {
+ 8,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_statue_on_armour = {
+ 8,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_lift_alarm = {
+ 8,
+ SFXF_SAVE,
+ {
+ { 2,63,63 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_drop_crowbar = {
+ 8,
+ 5+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_byee_helga = {
+ 9,
+ 3+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_shed_door_creak = {
+ 10,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_explosion = {
+ 10,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_fire_crackle_in_pit = {
+ 9,
+ SFXF_SAVE,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_remove_bar_grill = {
+ 10,
+ 7+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_grill_creak = {
+ 10,
+ 43+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_steam1 = {
+ 11,
+ SFXF_SAVE,
+ {
+ { 18,20,20 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_steam2 = {
+ 11,
+ SFXF_SAVE,
+ {
+ { 18,63,63 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_steam3 = {
+ 11,
+ SFXF_SAVE,
+ {
+ { 18,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_crowbar_wooden = {
+ 11,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_helmet_down_3 = {
+ 11,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_guard_fall = {
+ 11,
+ 4,
+ {
+ { 255,127,127 },
+ }
+};
+
+#if 0
+static Sfx fx_furnace = {
+ 11,
+ 0,
+ {
+ { 3,90,90 },
+ { 255,0,0 },
+ }
+};
+#endif
+
+static Sfx fx_fall_thru_box = {
+ 12,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_lazer = {
+ 12,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_scanner = {
+ 12,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_helmet_up_3 = {
+ 12,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_liquid_bubble = {
+ 12,
+ SFXF_SAVE,
+ {
+ { 80,127,127 },
+ { 72,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_liquid_drip = {
+ 13,
+ 6+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_goo_drip = {
+ 13,
+ 5+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_comp_bleeps = {
+ 13,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_use_crowbar_grill = {
+ 13,
+ 34+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_helmet_grind = {
+ 14,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_lift_moving = {
+ 14,
+ SFXF_SAVE,
+ {
+ { 7,127,127 },
+ { 29,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_use_secateurs = {
+ 14,
+ 18+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_hit_joey1 = {
+ 14,
+ 7+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_hit_joey2 = {
+ 14,
+ 13+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_dani_phone_ring = {
+ 15,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_sc74_pod_down = {
+ 15,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_phone = {
+ 15,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_25_weld = {
+ 15,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_lift_open_7 = {
+ 15,
+ 0,
+ {
+ { 7,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_lift_close_7 = {
+ 16,
+ 0,
+ {
+ { 7,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_s2_helmet = {
+ 16,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_hiss_in_nitrogen = {
+ 16,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_dog_yap_indoors = {
+ 16,
+ 0,
+ {
+ { 38,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_dog_yap_outdoors = {
+ 16,
+ 0,
+ {
+ { 31,127,127 },
+ { 30,40,40 },
+ { 32,40,40 },
+ { 33,40,40 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_locker_creak_open = {
+ 17,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_big_tent_gurgle = {
+ 17,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_wind_howl = {
+ 17,
+ SFXF_SAVE,
+ {
+ { 1,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_lift_open_29 = {
+ 17,
+ 0,
+ {
+ { 29,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_lift_arrive_7 = {
+ 17,
+ 0,
+ {
+ { 7,63,63 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_lift_close_29 = {
+ 18,
+ 0,
+ {
+ { 29,127,127 },
+ { 28,127,127 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_shaft_industrial_noise = {
+ 18,
+ SFXF_SAVE,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_gall_drop = {
+ 18,
+ 29+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_door_slam_under = {
+ 19,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_reichs_fish = {
+ 19,
+ SFXF_SAVE,
+ {
+ { 255,60,60 },
+ }
+};
+
+static Sfx fx_judges_gavel1 = {
+ 19,
+ 13+SFXF_START_DELAY,
+ {
+ { 255,60,60 },
+ }
+};
+
+static Sfx fx_judges_gavel2 = {
+ 19,
+ 16+SFXF_START_DELAY,
+ {
+ { 255,90,90 },
+ }
+};
+
+static Sfx fx_judges_gavel3 = {
+ 19,
+ 19+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_wind_3 = {
+ 20,
+ SFXF_SAVE,
+ {
+ { 255,60,60 },
+ }
+};
+
+static Sfx fx_fact_sensor = {
+ 20,
+ SFXF_SAVE,
+ {
+ { 255,60,60 },
+ }
+};
+
+static Sfx fx_medi_stab_gall = {
+ 20,
+ 17+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_computer_3 = {
+ 21,
+ SFXF_SAVE,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_timber_cracking = {
+ 21,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_anchor_fall = {
+ 22,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_elevator_4 = {
+ 22,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_star_trek_2 = {
+ 22,
+ SFXF_SAVE,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_lift_closing = {
+ 23,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_heartbeat = {
+ 23,
+ 11+SFXF_START_DELAY,
+ {
+ { 67,60,60 },
+ { 68,60,60 },
+ { 69,60,60 },
+ { 77,20,20 },
+ { 78,50,50 },
+ { 79,70,70 },
+ { 80,127,127 },
+ { 81,60,60 },
+ { 255,0,0 },
+ }
+};
+
+static Sfx fx_pos_key = {
+ 25,
+ 2+SFXF_START_DELAY,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx fx_neg_key = {
+ 26,
+ 2+SFXF_START_DELAY,
+ {
+ { 255,100,100 },
+ }
+};
+
+static Sfx fx_orifice_swallow_drip = {
+ 28,
+ 0,
+ {
+ { 255,127,127 },
+ }
+};
+
+static Sfx *musicList[] = {
+ &fx_press_bang, // 256 banging of the press
+ &fx_press_hiss, // 257 hissing press
+ &fx_wind_howl, // 258 howling wind
+ &fx_spanner_clunk, // 259 spanner in works
+ &fx_reichs_fish, // 260 Reichs fish
+ &fx_explosion, // 261 panel blows open
+ &fx_wind_3, // 262 single steam
+ &fx_open_door, // 263 general open door
+ &fx_open_lamb_door, // 264 lamb door opens
+ &fx_comp_bleeps, // 265 scanner bleeps
+ &fx_helmet_down_3, // 266
+ &fx_helmet_up_3, // 267
+ &fx_helmet_grind, // 268
+ &fx_lift_close_29, // 269 rm 29 lift closes
+ &fx_lift_open_29, // 270 rm 29 lift opens
+ &fx_computer_3, // 271 rm 29 lift arrives
+ &fx_level_3_ping, // 272 background noise in room 4
+ &fx_lift_alarm, // 273 loader alarm
+ &fx_null, // 274 furnace room background noise
+ &fx_rm3_lift_moving, // 275 lift moving in room 3
+ &fx_lathe, // 276 jobsworth lathe
+ &fx_factory_sound, // 277 factory background sound
+ &fx_weld, // 278 do some welding
+ &fx_lift_close_7, // 279 rm 7 lift closes
+ &fx_lift_open_7, // 280 rm 7 lift opens
+ &fx_lift_arrive_7, // 281 rm 7 lift arrives
+ &fx_lift_moving, // 282 lift moving
+ &fx_scanner, // 283 scanner operating
+ &fx_force_fire_door, // 284 Force fire door open
+ &fx_null, // 285 General door creak
+ &fx_phone, // 286 telephone
+ &fx_lazer, // 287 lazer
+ &fx_lazer, // 288 lazer
+ &fx_anchor_fall, // 289 electric ;not used on amiga
+ &fx_weld12, // 290 welding in room 12 (not joey)
+ &fx_hello_helga, // 291 helga appears
+ &fx_byee_helga, // 292 helga disapears
+ &fx_null, // 293 smash through window ;doesn't exist
+ &fx_pos_key, // 294
+ &fx_neg_key, // 295
+ &fx_s2_helmet, // 296 ;helmet down section 2
+ &fx_s2_helmet, // 297 ; " up " "
+ &fx_lift_arrive_7, // 298 ;security door room 7
+ &fx_null, // 299
+ &fx_rope_creak, // 300
+ &fx_crowbar_wooden, // 301
+ &fx_fall_thru_box, // 302
+ &fx_use_crowbar_grill, // 303
+ &fx_use_secateurs, // 304
+ &fx_grill_creak, // 305
+ &fx_timber_cracking, // 306
+ &fx_masonry_fall, // 307
+ &fx_masonry_fall, // 308
+ &fx_crowbar_plaster, // 309
+ &fx_prise_brick, // 310
+ &fx_brick_hit_foster, // 311
+ &fx_spray_on_skin, // 312
+ &fx_hit_crowbar_brick, // 313
+ &fx_drop_crowbar, // 314
+ &fx_fire_crackle_in_pit, // 315
+ &fx_remove_bar_grill, // 316
+ &fx_liquid_bubble, // 317
+ &fx_liquid_drip, // 318
+ &fx_guard_fall, // 319
+ &fx_sc74_pod_down, // 320
+ &fx_hiss_in_nitrogen, // 321
+ &fx_null, // 322
+ &fx_hit_joey1, // 323
+ &fx_hit_joey2, // 324
+ &fx_medi_stab_gall, // 325
+ &fx_gall_drop, // 326
+ &fx_null, // 327
+ &fx_null, // 328
+ &fx_null, // 329
+ &fx_big_tent_gurgle, // 330
+ &fx_null, // 331
+ &fx_orifice_swallow_drip, // 332
+ &fx_brick_hit_plank, // 333
+ &fx_goo_drip, // 334
+ &fx_plank_vibrating, // 335
+ &fx_splash, // 336
+ &fx_buzzer, // 337
+ &fx_shed_door_creak, // 338
+ &fx_dog_yap_outdoors, // 339
+ &fx_dani_phone_ring, // 340
+ &fx_locker_creak_open, // 341
+ &fx_judges_gavel1, // 342
+ &fx_dog_yap_indoors, // 343
+ &fx_brick_hit_plank, // 344
+ &fx_brick_hit_plank, // 345
+ &fx_shaft_industrial_noise, // 346
+ &fx_judges_gavel2, // 347
+ &fx_judges_gavel3, // 348
+ &fx_elevator_4, // 349
+ &fx_lift_closing, // 350
+ &fx_null, // 351
+ &fx_null, // 352
+ &fx_sc74_pod_down, // 353
+ &fx_null, // 354
+ &fx_null, // 355
+ &fx_heartbeat, // 356
+ &fx_star_trek_2, // 357
+ &fx_null, // 358
+ &fx_null, // 359
+ &fx_null, // 350
+ &fx_null, // 361
+ &fx_null, // 362
+ &fx_null, // 363
+ &fx_null, // 364
+ &fx_null, // 365
+ &fx_break_crystals, // 366
+ &fx_disintegrate, // 367
+ &fx_statue_on_armour, // 368
+ &fx_null, // 369
+ &fx_null, // 360
+ &fx_ping, // 371
+ &fx_null, // 372
+ &fx_door_slam_under, // 373
+ &fx_null, // 374
+ &fx_null, // 375
+ &fx_null, // 376
+ &fx_null, // 377
+ &fx_null, // 378
+ &fx_null, // 379
+ &fx_steam1, // 380
+ &fx_steam2, // 381
+ &fx_steam2, // 382
+ &fx_steam3, // 383
+ &fx_null, // 384
+ &fx_null, // 385
+ &fx_fact_sensor, // 386 Sensor in Potts' room
+ &fx_null, // 387
+ &fx_null, // 388
+ &fx_null, // 389
+ &fx_null, // 390
+ &fx_null, // 391
+ &fx_null, // 392
+ &fx_null, // 393
+ &fx_25_weld // 394 my anchor weld bodge
+};
+
+SfxQueue Sound::_sfxQueue[MAX_QUEUED_FX] = {
+ { 0, 0, 0, 0},
+ { 0, 0, 0, 0},
+ { 0, 0, 0, 0},
+ { 0, 0, 0, 0}
+};
+
+Sound::Sound(Audio::Mixer *mixer, Disk *pDisk, uint8 pVolume) {
+ _skyDisk = pDisk;
+ _soundData = NULL;
+ _mixer = mixer;
+ _saveSounds[0] = _saveSounds[1] = 0xFFFF;
+ _mainSfxVolume = pVolume;
+}
+
+Sound::~Sound(void) {
+
+ _mixer->stopAll();
+ if (_soundData)
+ free(_soundData);
+}
+
+void Sound::playSound(uint32 id, byte *sound, uint32 size, Audio::SoundHandle *handle) {
+
+ byte flags = 0;
+ flags |= Audio::Mixer::FLAG_UNSIGNED|Audio::Mixer::FLAG_AUTOFREE;
+ size -= sizeof(struct dataFileHeader);
+ byte *buffer = (byte *)malloc(size);
+ memcpy(buffer, sound+sizeof(struct dataFileHeader), size);
+
+ _mixer->stopID(id);
+ _mixer->playRaw(handle, buffer, size, 11025, flags, id);
+}
+
+void Sound::loadSection(uint8 pSection) {
+
+ fnStopFx();
+ _mixer->stopAll();
+
+ if (_soundData)
+ free(_soundData);
+ _soundData = _skyDisk->loadFile(pSection * 4 + SOUND_FILE_BASE);
+ uint16 asmOfs;
+ if (SkyEngine::_systemVars.gameVersion == 109) {
+ if (pSection == 0)
+ asmOfs = 0x78;
+ else
+ asmOfs = 0x7C;
+ } else
+ asmOfs = 0x7E;
+
+ if ((_soundData[asmOfs] != 0x3C) || (_soundData[asmOfs + 0x27] != 0x8D) ||
+ (_soundData[asmOfs + 0x28] != 0x1E) || (_soundData[asmOfs + 0x2F] != 0x8D) ||
+ (_soundData[asmOfs + 0x30] != 0x36))
+ error("Unknown sounddriver version!");
+
+ _soundsTotal = _soundData[asmOfs + 1];
+ _sfxBaseOfs = READ_LE_UINT16(_soundData + asmOfs + 0x31);
+
+ _sfxInfo = _soundData + _sfxBaseOfs;
+ // if we just restored a savegame, the sfxqueue holds the sound we need to restart
+ if (!(SkyEngine::_systemVars.systemFlags & SF_GAME_RESTORED))
+ for (uint8 cnt = 0; cnt < 4; cnt++)
+ _sfxQueue[cnt].count = 0;
+}
+
+void Sound::playSound(uint16 sound, uint16 volume, uint8 channel) {
+
+ if (channel == 0)
+ _mixer->stopID(SOUND_CH0);
+ else
+ _mixer->stopID(SOUND_CH1);
+
+ if (!_soundData) {
+ warning("Sound::playSound(%04X, %04X) called with a section having been loaded", sound, volume);
+ return;
+ }
+
+ if (sound > _soundsTotal) {
+ debug(5, "Sound::playSound %d ignored, only %d sfx in file", sound, _soundsTotal);
+ return;
+ }
+
+ volume = (volume & 0x7F) << 1;
+ sound &= 0xFF;
+
+ // note: all those tables are big endian. Don't ask me why. *sigh*
+ uint32 dataOfs = READ_BE_UINT16(_sfxInfo + (sound << 3) + 0) << 4;
+ uint32 dataSize = READ_BE_UINT16(_sfxInfo + (sound << 3) + 2);
+ uint32 dataLoop = READ_BE_UINT16(_sfxInfo + (sound << 3) + 6);
+ dataOfs += _sfxBaseOfs;
+
+ byte flags = Audio::Mixer::FLAG_UNSIGNED;
+
+ uint32 loopSta = 0, loopEnd = 0;
+ if (dataLoop) {
+ loopSta = dataSize - dataLoop;
+ loopEnd = dataSize;
+ flags |= Audio::Mixer::FLAG_LOOP;
+ }
+
+ if (channel == 0)
+ _mixer->playRaw(&_ingameSound0, _soundData + dataOfs, dataSize, 11025, flags, SOUND_CH0, volume, 0, loopSta, loopEnd);
+ else
+ _mixer->playRaw(&_ingameSound1, _soundData + dataOfs, dataSize, 11025, flags, SOUND_CH1, volume, 0, loopSta, loopEnd);
+}
+
+void Sound::fnStartFx(uint32 sound, uint8 channel) {
+
+ _saveSounds[channel] = 0xFFFF;
+ if (sound < 256 || sound > MAX_FX_NUMBER || (SkyEngine::_systemVars.systemFlags & SF_FX_OFF))
+ return;
+
+ uint8 screen = (uint8)(Logic::_scriptVariables[SCREEN] & 0xff);
+ if (sound == 278 && screen == 25) // is this weld in room 25
+ sound= 394;
+
+ sound &= ~(1 << 8);
+
+ Sfx *sfx = musicList[sound];
+ RoomList *roomList = sfx->roomList;
+
+ int i = 0;
+ if (roomList[i].room != 0xff) // if room list empty then do all rooms
+ while (roomList[i].room != screen) { // check rooms
+ i++;
+ if (roomList[i].room == 0xff)
+ return;
+ }
+
+ // get fx volume
+
+ uint8 volume = _mainSfxVolume; // start with standard vol
+
+ if (SkyEngine::_systemVars.systemFlags & SF_SBLASTER)
+ volume = roomList[i].adlibVolume;
+ else if (SkyEngine::_systemVars.systemFlags & SF_ROLAND)
+ volume = roomList[i].rolandVolume;
+ volume = (volume * _mainSfxVolume) >> 8;
+
+ // Check the flags, the sound may come on after a delay.
+ if (sfx->flags & SFXF_START_DELAY) {
+ for (uint8 cnt = 0; cnt < MAX_QUEUED_FX; cnt++) {
+ if (_sfxQueue[cnt].count == 0) {
+ _sfxQueue[cnt].chan = channel;
+ _sfxQueue[cnt].fxNo = sfx->soundNo;
+ _sfxQueue[cnt].vol = volume;
+ _sfxQueue[cnt].count = sfx->flags & 0x7F;
+ return;
+ }
+ }
+ return; // ignore sound if it can't be queued
+ }
+
+ if (sfx->flags & SFXF_SAVE)
+ _saveSounds[channel] = sfx->soundNo | (volume << 8);
+
+ playSound(sfx->soundNo, volume, channel);
+}
+
+void Sound::checkFxQueue(void) {
+ for (uint8 cnt = 0; cnt < MAX_QUEUED_FX; cnt++) {
+ if (_sfxQueue[cnt].count) {
+ _sfxQueue[cnt].count--;
+ if (_sfxQueue[cnt].count == 0)
+ playSound(_sfxQueue[cnt].fxNo, _sfxQueue[cnt].vol, _sfxQueue[cnt].chan);
+ }
+ }
+}
+
+void Sound::restoreSfx(void) {
+
+ // queue sfx, so they will be started when the player exits the control panel
+ memset(_sfxQueue, 0, sizeof(_sfxQueue));
+ uint8 queueSlot = 0;
+ if (_saveSounds[0] != 0xFFFF) {
+ _sfxQueue[queueSlot].fxNo = (uint8)_saveSounds[0];
+ _sfxQueue[queueSlot].vol = (uint8)(_saveSounds[0] >> 8);
+ _sfxQueue[queueSlot].chan = 0;
+ _sfxQueue[queueSlot].count = 1;
+ queueSlot++;
+ }
+ if (_saveSounds[1] != 0xFFFF) {
+ _sfxQueue[queueSlot].fxNo = (uint8)_saveSounds[1];
+ _sfxQueue[queueSlot].vol = (uint8)(_saveSounds[1] >> 8);
+ _sfxQueue[queueSlot].chan = 1;
+ _sfxQueue[queueSlot].count = 1;
+ }
+}
+
+void Sound::fnStopFx(void) {
+ _mixer->stopID(SOUND_CH0);
+ _mixer->stopID(SOUND_CH1);
+ _saveSounds[0] = _saveSounds[1] = 0xFFFF;
+}
+
+void Sound::stopSpeech(void) {
+ _mixer->stopID(SOUND_SPEECH);
+}
+
+bool Sound::startSpeech(uint16 textNum) {
+
+ if (!(SkyEngine::_systemVars.systemFlags & SF_ALLOW_SPEECH))
+ return false;
+ uint16 speechFileNum = _speechConvertTable[textNum >> 12] + (textNum & 0xFFF);
+
+ uint8 *speechData = _skyDisk->loadFile(speechFileNum + 50000);
+ if (!speechData) {
+ debug(9,"File %d (speechFile %d from section %d) wasn't found", speechFileNum + 50000, textNum & 0xFFF, textNum >> 12);
+ return false;
+ }
+
+ uint32 speechSize = ((dataFileHeader *)speechData)->s_tot_size - sizeof(dataFileHeader);
+ uint8 *playBuffer = (uint8 *)malloc(speechSize);
+ memcpy(playBuffer, speechData + sizeof(dataFileHeader), speechSize);
+
+ free(speechData);
+
+ // Workaround for BASS bug #897775 - some voice-overs are played at
+ // half speed in 0.0368 (the freeware CD version), in 0.0372 they sound
+ // just fine.
+
+ uint rate;
+ if (_skyDisk->determineGameVersion() == 368 && (textNum == 20905 || textNum == 20906))
+ rate = 22050;
+ else
+ rate = 11025;
+
+ _mixer->stopID(SOUND_SPEECH);
+ _mixer->playRaw(&_ingameSpeech, playBuffer, speechSize, rate, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE, SOUND_SPEECH);
+ return true;
+}
+
+void Sound::fnPauseFx(void) {
+
+ _mixer->pauseID(SOUND_CH0, true);
+ _mixer->pauseID(SOUND_CH1, true);
+}
+
+void Sound::fnUnPauseFx(void) {
+
+ _mixer->pauseID(SOUND_CH0, false);
+ _mixer->pauseID(SOUND_CH1, false);
+}
+
+} // End of namespace Sky
diff --git a/engines/sky/sound.h b/engines/sky/sound.h
new file mode 100644
index 0000000000..6d8283e33a
--- /dev/null
+++ b/engines/sky/sound.h
@@ -0,0 +1,95 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYSOUND_H
+#define SKYSOUND_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "sound/mixer.h"
+
+namespace Sky {
+
+class Disk;
+
+enum {
+ SOUND_CH0 = 0,
+ SOUND_CH1 = 1,
+ SOUND_BG = 2,
+ SOUND_VOICE = 3,
+ SOUND_SPEECH = 4
+};
+
+struct SfxQueue {
+ uint8 count, fxNo, chan, vol;
+};
+
+#define MAX_QUEUED_FX 4
+
+class Sound {
+protected:
+
+public:
+
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _voiceHandle;
+ Audio::SoundHandle _effectHandle;
+ Audio::SoundHandle _bgSoundHandle;
+ Audio::SoundHandle _ingameSound0, _ingameSound1, _ingameSpeech;
+
+ uint16 _saveSounds[2];
+
+protected:
+
+ void playSound(uint32 id, byte *sound, uint32 size, Audio::SoundHandle *handle);
+
+public:
+ Sound(Audio::Mixer *mixer, Disk *pDisk, uint8 pVolume);
+ ~Sound(void);
+
+ void loadSection(uint8 pSection);
+ void playSound(uint16 sound, uint16 volume, uint8 channel);
+ void fnStartFx(uint32 sound, uint8 channel);
+ bool startSpeech(uint16 textNum);
+ bool speechFinished(void) { return !_mixer->isSoundHandleActive(_ingameSpeech); };
+ void fnPauseFx(void);
+ void fnUnPauseFx(void);
+ void fnStopFx(void);
+ void stopSpeech(void);
+ void checkFxQueue(void);
+ void restoreSfx(void);
+ uint8 _soundsTotal;
+
+private:
+ Disk *_skyDisk;
+ uint16 _sfxBaseOfs;
+ uint8 *_soundData;
+ uint8 *_sfxInfo;
+ uint8 _mainSfxVolume;
+
+ static uint16 _speechConvertTable[8];
+ static SfxQueue _sfxQueue[MAX_QUEUED_FX];
+};
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sky/struc.h b/engines/sky/struc.h
new file mode 100644
index 0000000000..45d1d946ca
--- /dev/null
+++ b/engines/sky/struc.h
@@ -0,0 +1,175 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYSTRUC_H
+#define SKYSTRUC_H
+
+namespace Sky {
+
+struct lowTextManager_t {
+ byte *textData;
+ uint32 textWidth;
+ uint16 compactNum;
+};
+
+struct displayText_t {
+ byte *textData;
+ uint32 textWidth;
+};
+
+#if !defined(__GNUC__)
+#pragma START_PACK_STRUCTS
+#endif
+
+struct dataFileHeader {
+ uint16 flag; // bit 0: set for colour data, clear for not
+ // bit 1: set for compressed, clear for uncompressed
+ // bit 2: set for 32 colours, clear for 16 colours
+ uint16 s_x;
+ uint16 s_y;
+ uint16 s_width;
+ uint16 s_height;
+ uint16 s_sp_size;
+ uint16 s_tot_size;
+ uint16 s_n_sprites;
+ int16 s_offset_x;
+ int16 s_offset_y;
+ uint16 s_compressed_size;
+} GCC_PACK;
+
+struct TurnTable {
+ uint16 turnTableUp[5];
+ uint16 turnTableDown[5];
+ uint16 turnTableLeft[5];
+ uint16 turnTableRight[5];
+ uint16 turnTableTalk[5];
+} GCC_PACK;
+
+struct MegaSet {
+ uint16 gridWidth; // 0
+ uint16 colOffset; // 1
+ uint16 colWidth; // 2
+ uint16 lastChr; // 3
+
+ uint16 animUpId; // 4
+ uint16 animDownId; // 5
+ uint16 animLeftId; // 6
+ uint16 animRightId; // 7
+
+ uint16 standUpId; // 8
+ uint16 standDownId; // 9
+ uint16 standLeftId; // 10
+ uint16 standRightId; // 11
+ uint16 standTalkId; // 12
+ uint16 turnTableId; // 13
+} GCC_PACK;
+
+struct Compact {
+ uint16 logic; // 0: Entry in logic table to run (byte as <256entries in logic table
+ uint16 status; // 1
+ uint16 sync; // 2: flag sent to compacts by other things
+
+ uint16 screen; // 3: current screen
+ uint16 place; // 4: so's this one
+ uint16 getToTableId; // 5: Address of how to get to things table
+
+ uint16 xcood; // 6
+ uint16 ycood; // 7
+
+ uint16 frame; // 8
+
+ uint16 cursorText; // 9
+ uint16 mouseOn; // 10
+ uint16 mouseOff; // 11
+ uint16 mouseClick; // 12
+
+ int16 mouseRelX; // 13
+ int16 mouseRelY; // 14
+ uint16 mouseSizeX; // 15
+ uint16 mouseSizeY; // 16
+
+ uint16 actionScript; // 17
+
+ uint16 upFlag; // 18: usually holds the Action Mode
+ uint16 downFlag; // 19: used for passing back
+ uint16 getToFlag; // 20: used by action script for get to attempts, also frame store (hence word)
+ uint16 flag; // 21: a use any time flag
+
+ uint16 mood; // 22: high level - stood or not
+
+ uint16 grafixProgId; // 23
+ uint16 grafixProgPos;// 24
+
+ uint16 offset; // 25
+
+ uint16 mode; // 26: which mcode block
+
+ uint16 baseSub; // 27: 1st mcode block relative to start of compact
+ uint16 baseSub_off; // 28
+ uint16 actionSub; // 29
+ uint16 actionSub_off;// 30
+ uint16 getToSub; // 31
+ uint16 getToSub_off; // 32
+ uint16 extraSub; // 33
+ uint16 extraSub_off; // 34
+
+ uint16 dir; // 35
+
+ uint16 stopScript; // 36
+ uint16 miniBump; // 37
+ uint16 leaving; // 38
+ uint16 atWatch; // 39: pointer to script variable
+ uint16 atWas; // 40: pointer to script variable
+ uint16 alt; // 41: alternate script
+ uint16 request; // 42
+
+ uint16 spWidth_xx; // 43
+ uint16 spColour; // 44
+ uint16 spTextId; // 45
+ uint16 spTime; // 46
+
+ uint16 arAnimIndex; // 47
+ uint16 turnProgId; // 48
+ uint16 turnProgPos; // 49
+
+ uint16 waitingFor; // 50
+
+ uint16 arTargetX; // 51
+ uint16 arTargetY; // 52
+
+ uint16 animScratchId;// 53: data area for AR
+
+ uint16 megaSet; // 54
+
+ MegaSet megaSet0; // 55
+ MegaSet megaSet1; //
+ MegaSet megaSet2; //
+ MegaSet megaSet3; //
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+#pragma END_PACK_STRUCTS
+#endif
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sky/text.cpp b/engines/sky/text.cpp
new file mode 100644
index 0000000000..2c997340ab
--- /dev/null
+++ b/engines/sky/text.cpp
@@ -0,0 +1,703 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sky/disk.h"
+#include "sky/logic.h"
+#include "sky/text.h"
+#include "sky/sky.h"
+#include "sky/skydefs.h"
+#include "sky/struc.h"
+#include "sky/compact.h"
+
+namespace Sky {
+
+#define FIRST_TEXT_SEC 77
+#define FIRST_TEXT_BUFFER 274
+#define LAST_TEXT_BUFFER 284
+#define NO_OF_TEXT_SECTIONS 8 // 8 sections per language
+#define CHAR_SET_FILE 60150
+#define MAX_SPEECH_SECTION 7
+#define CHAR_SET_HEADER 128
+#define MAX_NO_LINES 10
+
+Text::Text(Disk *skyDisk, SkyCompact *skyCompact) {
+ _skyDisk = skyDisk;
+ _skyCompact = skyCompact;
+
+ initHuffTree();
+
+ _mainCharacterSet.addr = _skyDisk->loadFile(CHAR_SET_FILE);
+ _mainCharacterSet.charHeight = MAIN_CHAR_HEIGHT;
+ _mainCharacterSet.charSpacing = 0;
+
+ fnSetFont(0);
+
+ if (!SkyEngine::isDemo()) {
+ _controlCharacterSet.addr = _skyDisk->loadFile(60520);
+ _controlCharacterSet.charHeight = 12;
+ _controlCharacterSet.charSpacing = 0;
+
+ _linkCharacterSet.addr = _skyDisk->loadFile(60521);
+ _linkCharacterSet.charHeight = 12;
+ _linkCharacterSet.charSpacing = 1;
+
+ patchLINCCharset();
+ } else {
+ _controlCharacterSet.addr = NULL;
+ _linkCharacterSet.addr = NULL;
+ }
+}
+
+Text::~Text(void) {
+ for (int i = FIRST_TEXT_BUFFER; i <= LAST_TEXT_BUFFER; i++)
+ if (SkyEngine::_itemList[i]) {
+ free(SkyEngine::_itemList[i]);
+ SkyEngine::_itemList[i] = NULL;
+ }
+
+ if (_mainCharacterSet.addr)
+ free(_mainCharacterSet.addr);
+ if (_controlCharacterSet.addr)
+ free(_controlCharacterSet.addr);
+ if (_linkCharacterSet.addr)
+ free(_linkCharacterSet.addr);
+}
+
+void Text::patchChar(byte *charSetPtr, int width, int height, int c, const uint16 *data) {
+ byte *ptr = charSetPtr + (CHAR_SET_HEADER + (height << 2) * c);
+
+ charSetPtr[c] = width;
+
+ for (int i = 0; i < height; i++) {
+ ptr[i * 4 + 0] = (data[i] & 0xFF00) >> 8;
+ ptr[i * 4 + 1] = data[i] & 0x00FF;
+ ptr[i * 4 + 2] = (data[i + height] & 0xFF00) >> 8;
+ ptr[i * 4 + 3] = data[i + height] & 0x00FF;
+ }
+}
+
+void Text::patchLINCCharset() {
+ // The LINC terminal charset looks strange in some cases. This
+ // function attempts to patch up the worst blemishes.
+
+ byte *charSetPtr = _controlCharacterSet.addr;
+ int charHeight = _controlCharacterSet.charHeight;
+
+ // In v0.0288, the character spacing is too wide. Decrease the width
+ // for every character by one, except for space which needs to be one
+ // pixel wider than before.
+
+ if (SkyEngine::_systemVars.gameVersion == 288) {
+ for (int i = 1; i < CHAR_SET_HEADER; i++)
+ charSetPtr[i]--;
+ charSetPtr[0]++;
+ }
+
+ // NOTE: I have only tested this part of the code with v0.0372
+
+ // Several characters are different in this charset than in the other
+ // two. This is particularly noticeable when using a non-English
+ // version.
+
+ // Since the same character data is used both in LINC terminals and
+ // in LINC-space, we need to provide a mask (for the black outline)
+ // even though it's only visible in the latter. We store the mask
+ // as the second half of the array to make it more human-readable.
+ // In the actual game data, the character and mask are interleaved.
+
+ const uint16 slash[] = {
+ 0x0000, 0x0000, 0x0000, 0x0800, 0x1000, 0x1000,
+ 0x2000, 0x2000, 0x4000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0800, 0x1C00, 0x3800, 0x3800,
+ 0x7000, 0x7000, 0xE000, 0x4000, 0x0000, 0x0000
+ };
+
+ const uint16 lt[] = {
+ 0x0000, 0x0000, 0x0800, 0x1000, 0x2000, 0x4000,
+ 0x2000, 0x1000, 0x0800, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0800, 0x1C00, 0x3800, 0x7000, 0xE000,
+ 0x7000, 0x3800, 0x1C00, 0x0800, 0x0000, 0x0000
+ };
+
+ const uint16 gt[] = {
+ 0x0000, 0x0000, 0x4000, 0x2000, 0x1000, 0x0800,
+ 0x1000, 0x2000, 0x4000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x4000, 0xE000, 0x7000, 0x3800, 0x1C00,
+ 0x3800, 0x7000, 0xE000, 0x4000, 0x0000, 0x0000
+ };
+
+ const uint16 a_umlaut[] = {
+ 0x0000, 0x0000, 0x2800, 0x0000, 0x3000, 0x0800,
+ 0x3800, 0x4800, 0x3800, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2800, 0x7C00, 0x3800, 0x7800, 0x3C00,
+ 0x7C00, 0xFC00, 0x7C00, 0x3800, 0x0000, 0x0000
+ };
+
+ const uint16 o_umlaut[] = {
+ 0x0000, 0x0000, 0x4800, 0x0000, 0x3000, 0x4800,
+ 0x4800, 0x4800, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x4800, 0xFC00, 0x7800, 0x7800, 0xFC00,
+ 0xFC00, 0xFC00, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 u_umlaut[] = {
+ 0x0000, 0x0000, 0x4800, 0x0000, 0x4800, 0x4800,
+ 0x4800, 0x4800, 0x3800, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x4800, 0xFC00, 0x4800, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0x7C00, 0x3800, 0x0000, 0x0000
+ };
+
+ const uint16 A_umlaut[] = {
+ 0x0000, 0x4800, 0x0000, 0x3000, 0x4800, 0x4800,
+ 0x7800, 0x4800, 0x4800, 0x0000, 0x0000, 0x0000,
+ 0x4800, 0xFC00, 0x7800, 0x7800, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0xFC00, 0x4800, 0x0000, 0x0000
+ };
+
+ const uint16 O_umlaut[] = {
+ 0x0000, 0x4800, 0x0000, 0x3000, 0x4800, 0x4800,
+ 0x4800, 0x4800, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x4800, 0xFC00, 0x7800, 0x7800, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 U_umlaut[] = {
+ 0x0000, 0x4800, 0x0000, 0x4800, 0x4800, 0x4800,
+ 0x4800, 0x4800, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x4800, 0xFC00, 0x4800, 0xFC00, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 normal_j[] = {
+ 0x0000, 0x0000, 0x0000, 0x0800, 0x0000, 0x0800,
+ 0x0800, 0x0800, 0x0800, 0x4800, 0x3000, 0x0000,
+ 0x0000, 0x0000, 0x0800, 0x1C00, 0x0800, 0x1C00,
+ 0x1C00, 0x1C00, 0x5C00, 0xFC00, 0x7800, 0x3000
+ };
+
+ const uint16 german_sz[] = {
+ 0x0000, 0x0000, 0x2000, 0x5000, 0x5000, 0x4800,
+ 0x4800, 0x4800, 0x5000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2000, 0x7000, 0xF800, 0xF800, 0xFC00,
+ 0xFC00, 0xFC00, 0xF800, 0x7000, 0x0000, 0x0000
+ };
+
+ const uint16 normal_1[] = {
+ 0x0000, 0x0000, 0x0000, 0x1000, 0x7000, 0x1000,
+ 0x1000, 0x1000, 0x7c00, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x1000, 0x7800, 0xF800, 0x7800,
+ 0x3800, 0x7c00, 0xFE00, 0x7c00, 0x0000, 0x0000
+ };
+
+ // The next five are needed for at least the French version
+
+ const uint16 e_acute[] = {
+ 0x0000, 0x1000, 0x2000, 0x0000, 0x3000, 0x4800,
+ 0x7800, 0x4000, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x3800, 0x7000, 0x7000, 0x7800, 0xFC00,
+ 0xFC00, 0xF800, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 e_grave[] = {
+ 0x0000, 0x2000, 0x1000, 0x0000, 0x3000, 0x4800,
+ 0x7800, 0x4000, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x2000, 0x7000, 0x3800, 0x3800, 0x7800, 0xFC00,
+ 0xFC00, 0xF800, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 e_circumflex[] = {
+ 0x0000, 0x1000, 0x2800, 0x0000, 0x3000, 0x4800,
+ 0x7800, 0x4000, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x3800, 0x7C00, 0x7800, 0x7800, 0xFC00,
+ 0xFC00, 0xF800, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 o_circumflex[] = {
+ 0x0000, 0x1000, 0x2800, 0x0000, 0x3000, 0x4800,
+ 0x4800, 0x4800, 0x3000, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x3800, 0x7C00, 0x3800, 0x7800, 0xFC00,
+ 0xFC00, 0xFC00, 0x7800, 0x3000, 0x0000, 0x0000
+ };
+
+ const uint16 u_circumflex[] = {
+ 0x0000, 0x1000, 0x2800, 0x0000, 0x4800, 0x4800,
+ 0x4800, 0x4800, 0x3800, 0x0000, 0x0000, 0x0000,
+ 0x1000, 0x3800, 0x7C00, 0x7800, 0xFC00, 0xFC00,
+ 0xFC00, 0xFC00, 0x7C00, 0x3800, 0x0000, 0x0000
+ };
+
+ patchChar(charSetPtr, 5, charHeight, 3, u_umlaut);
+ patchChar(charSetPtr, 5, charHeight, 8, german_sz);
+ patchChar(charSetPtr, 5, charHeight, 9, o_umlaut);
+ patchChar(charSetPtr, 5, charHeight, 93, U_umlaut);
+ patchChar(charSetPtr, 5, charHeight, 74, normal_j);
+ patchChar(charSetPtr, 6, charHeight, 17, normal_1);
+
+ patchChar(charSetPtr, 5, charHeight, 11, e_acute);
+ patchChar(charSetPtr, 5, charHeight, 61, e_grave);
+ patchChar(charSetPtr, 5, charHeight, 29, e_circumflex);
+ patchChar(charSetPtr, 5, charHeight, 32, o_circumflex);
+ patchChar(charSetPtr, 5, charHeight, 30, u_circumflex);
+
+ if (SkyEngine::_systemVars.gameVersion <= 303) {
+ patchChar(charSetPtr, 5, charHeight, 10, a_umlaut);
+ } else {
+ patchChar(charSetPtr, 5, charHeight, 94, A_umlaut);
+ patchChar(charSetPtr, 5, charHeight, 95, O_umlaut);
+
+ // Used by, for instance, the BRIEFING.DOC file in all (?) languages
+ patchChar(charSetPtr, 5, charHeight, 96, lt);
+ patchChar(charSetPtr, 5, charHeight, 97, gt);
+ patchChar(charSetPtr, 5, charHeight, 98, slash);
+ }
+}
+
+void Text::fnSetFont(uint32 fontNr) {
+
+ struct charSet *newCharSet;
+
+ switch (fontNr) {
+ case 0:
+ newCharSet = &_mainCharacterSet;
+ break;
+ case 1:
+ newCharSet = &_controlCharacterSet;
+ break;
+ case 2:
+ newCharSet = &_linkCharacterSet;
+ break;
+ default:
+ error("Tried to set invalid font (%d)", fontNr);
+ }
+
+ _curCharSet = fontNr;
+ _characterSet = newCharSet->addr;
+ _charHeight = (byte)newCharSet->charHeight;
+ _dtCharSpacing = newCharSet->charSpacing;
+}
+
+void Text::fnTextModule(uint32 textInfoId, uint32 textNo) {
+
+ fnSetFont(1);
+ uint16* msgData = (uint16 *)_skyCompact->fetchCpt(textInfoId);
+ lowTextManager_t textId = lowTextManager(textNo, msgData[1], msgData[2], 209, false);
+ Logic::_scriptVariables[RESULT] = textId.compactNum;
+ Compact *textCompact = _skyCompact->fetchCpt(textId.compactNum);
+ textCompact->xcood = msgData[3];
+ textCompact->ycood = msgData[4];
+ fnSetFont(0);
+}
+
+void Text::getText(uint32 textNr) { //load text #"textNr" into textBuffer
+
+ if (patchMessage(textNr))
+ return;
+
+ uint32 sectionNo = (textNr & 0x0F000) >> 12;
+
+ if (SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo] == NULL) { //check if already loaded
+ debug(5, "Loading Text item(s) for Section %d", (sectionNo >> 2));
+
+ uint32 fileNo = sectionNo + ((SkyEngine::_systemVars.language * NO_OF_TEXT_SECTIONS) + 60600);
+ SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo] = (void **)_skyDisk->loadFile((uint16)fileNo);
+ }
+ uint8 *textDataPtr = (uint8 *)SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo];
+
+ uint32 offset = 0;
+
+ uint32 blockNr = textNr & 0xFE0;
+ textNr &= 0x1F;
+
+ if (blockNr) {
+ uint16 *blockPtr = (uint16*)(textDataPtr + 4);
+ uint32 nr32MsgBlocks = blockNr >> 5;
+
+ do {
+ offset += READ_LE_UINT16(blockPtr);
+ blockPtr++;
+ } while (--nr32MsgBlocks);
+ }
+
+ if (textNr) {
+ uint8 *blockPtr = textDataPtr + blockNr + READ_LE_UINT16(textDataPtr);
+ do {
+ uint8 skipBytes = *blockPtr++;
+ if (skipBytes & 0x80) {
+ skipBytes &= 0x7F;
+ skipBytes <<= 3;
+ }
+ offset += skipBytes;
+ } while (--textNr);
+ }
+
+ uint32 bitPos = offset & 3;
+ offset >>= 2;
+ offset += READ_LE_UINT16(textDataPtr + 2);
+
+ textDataPtr += offset;
+
+ //bit pointer: 0->8, 1->6, 2->4 ...
+ bitPos ^= 3;
+ bitPos++;
+ bitPos <<= 1;
+
+ char *dest = (char *)_textBuffer;
+ char textChar;
+
+ do {
+ textChar = getTextChar(&textDataPtr, &bitPos);
+ *dest++ = textChar;
+ } while (textChar);
+}
+
+void Text::fnPointerText(uint32 pointedId, uint16 mouseX, uint16 mouseY) {
+
+ Compact *ptrComp = _skyCompact->fetchCpt(pointedId);
+ lowTextManager_t text = lowTextManager(ptrComp->cursorText, TEXT_MOUSE_WIDTH, L_CURSOR, 242, false);
+ Logic::_scriptVariables[CURSOR_ID] = text.compactNum;
+ if (Logic::_scriptVariables[MENU]) {
+ _mouseOfsY = TOP_LEFT_Y - 2;
+ if (mouseX < 150)
+ _mouseOfsX = TOP_LEFT_X + 24;
+ else
+ _mouseOfsX = TOP_LEFT_X - 8 - text.textWidth;
+ } else {
+ _mouseOfsY = TOP_LEFT_Y - 10;
+ if (mouseX < 150)
+ _mouseOfsX = TOP_LEFT_X + 13;
+ else
+ _mouseOfsX = TOP_LEFT_X - 8 - text.textWidth;
+ }
+ Compact *textCompact = _skyCompact->fetchCpt(text.compactNum);
+ logicCursor(textCompact, mouseX, mouseY);
+}
+
+void Text::logicCursor(Compact *textCompact, uint16 mouseX, uint16 mouseY) {
+
+ textCompact->xcood = (uint16)(mouseX + _mouseOfsX);
+ textCompact->ycood = (uint16)(mouseY + _mouseOfsY);
+ if (textCompact->ycood < TOP_LEFT_Y)
+ textCompact->ycood = TOP_LEFT_Y;
+}
+
+bool Text::getTextBit(uint8 **data, uint32 *bitPos) {
+
+ if (*bitPos) {
+ (*bitPos)--;
+ } else {
+ (*data)++;
+ *bitPos = 7;
+ }
+
+ return (bool)(((**data) >> (*bitPos)) & 1);
+}
+
+char Text::getTextChar(uint8 **data, uint32 *bitPos) {
+ int pos = 0;
+ while (1) {
+ if (getTextBit(data, bitPos))
+ pos = _huffTree[pos].rChild;
+ else
+ pos = _huffTree[pos].lChild;
+
+ if (_huffTree[pos].lChild == 0 && _huffTree[pos].rChild == 0) {
+ return _huffTree[pos].value;
+ }
+ }
+}
+
+displayText_t Text::displayText(uint32 textNum, uint8 *dest, bool centre, uint16 pixelWidth, uint8 color) {
+ //Render text into buffer *dest
+ getText(textNum);
+ return displayText(_textBuffer, dest, centre, pixelWidth, color);
+}
+
+displayText_t Text::displayText(char *textPtr, uint8 *dest, bool centre, uint16 pixelWidth, uint8 color) {
+
+ //Render text pointed to by *textPtr in buffer *dest
+ uint32 centerTable[10];
+ uint16 lineWidth = 0;
+
+ uint32 numLines = 0;
+ _numLetters = 2;
+
+ // work around bug #778105 (line width exceeded)
+ char *tmpPtr = strstr(textPtr, "MUND-BEATMUNG!");
+ if (tmpPtr)
+ strcpy(tmpPtr, "MUND BEATMUNG!");
+
+ // work around bug #1151924 (line width exceeded when talking to gardener using spanish text)
+ // This text apparently only is broken in the floppy versions, the CD versions contain
+ // the correct string "MANIFESTACION - ARTISTICA.", which doesn't break the algorithm/game.
+ tmpPtr = strstr(textPtr, "MANIFESTACION-ARTISTICA.");
+ if (tmpPtr)
+ strcpy(tmpPtr, "MANIFESTACION ARTISTICA.");
+
+ char *curPos = textPtr;
+ char *lastSpace = textPtr;
+ uint8 textChar = (uint8)*curPos++;
+
+ while (textChar >= 0x20) {
+ if ((_curCharSet == 1) && (textChar >= 0x80))
+ textChar = 0x20;
+
+ textChar -= 0x20;
+ if (textChar == 0) {
+ lastSpace = curPos; //keep track of last space
+ centerTable[numLines] = lineWidth;
+ }
+
+ lineWidth += _characterSet[textChar]; //add character width
+ lineWidth += (uint16)_dtCharSpacing; //include character spacing
+
+ if (pixelWidth <= lineWidth) {
+
+ if (*(lastSpace-1) == 10)
+ error("line width exceeded!");
+
+ *(lastSpace-1) = 10;
+ lineWidth = 0;
+ numLines++;
+ curPos = lastSpace; //go back for new count
+ }
+
+ textChar = (uint8)*curPos++;
+ _numLetters++;
+ }
+
+ uint32 dtLastWidth = lineWidth; //save width of last line
+ centerTable[numLines] = lineWidth; //and update centering table
+ numLines++;
+
+ if (numLines > MAX_NO_LINES)
+ error("Maximum no. of lines exceeded!");
+
+ uint32 dtLineSize = pixelWidth * _charHeight;
+ uint32 numBytes = (dtLineSize * numLines) + sizeof(struct dataFileHeader) + 4;
+
+ if (!dest)
+ dest = (uint8*)malloc(numBytes);
+
+ // clear text sprite buffer
+ memset(dest + sizeof(struct dataFileHeader), 0, numBytes - sizeof(struct dataFileHeader));
+
+ //make the header
+ ((struct dataFileHeader *)dest)->s_width = pixelWidth;
+ ((struct dataFileHeader *)dest)->s_height = (uint16)(_charHeight * numLines);
+ ((struct dataFileHeader *)dest)->s_sp_size = (uint16)(pixelWidth * _charHeight * numLines);
+ ((struct dataFileHeader *)dest)->s_offset_x = 0;
+ ((struct dataFileHeader *)dest)->s_offset_y = 0;
+
+ //reset position
+ curPos = textPtr;
+
+ uint8 *curDest = dest + sizeof(struct dataFileHeader); //point to where pixels start
+ byte *prevDest = curDest;
+ uint32 *centerTblPtr = centerTable;
+
+ do {
+ if (centre) {
+ uint32 width = (pixelWidth - *centerTblPtr) >> 1;
+ centerTblPtr++;
+ curDest += width;
+ }
+
+ textChar = (uint8)*curPos++;
+ while (textChar >= 0x20) {
+ makeGameCharacter(textChar - 0x20, _characterSet, curDest, color, pixelWidth);
+ textChar = *curPos++;
+ }
+
+ prevDest = curDest = prevDest + dtLineSize; //start of last line + start of next
+
+ } while (textChar >= 10);
+
+ struct displayText_t ret;
+ ret.textData = dest;
+ ret.textWidth = dtLastWidth;
+ return ret;
+}
+
+void Text::makeGameCharacter(uint8 textChar, uint8 *charSetPtr, uint8 *&dest, uint8 color, uint16 bufPitch) {
+
+ bool maskBit, dataBit;
+ uint8 charWidth = (uint8)((*(charSetPtr + textChar)) + 1 - _dtCharSpacing);
+ uint16 data, mask;
+ byte *charSpritePtr = charSetPtr + (CHAR_SET_HEADER + ((_charHeight << 2) * textChar));
+ byte *startPos = dest;
+ byte *curPos = startPos;
+
+ for (int i = 0; i < _charHeight; i++) {
+
+ byte *prevPos = curPos;
+
+ data = READ_BE_UINT16(charSpritePtr);
+ mask = READ_BE_UINT16(charSpritePtr + 2);
+ charSpritePtr += 4;
+
+ for (int j = 0; j < charWidth; j++) {
+
+ maskBit = (mask & 0x8000) != 0; //check mask
+ mask <<= 1;
+ dataBit = (data & 0x8000) != 0; //check data
+ data <<= 1;
+
+ if (maskBit)
+ if (dataBit)
+ *curPos = color;
+ else
+ *curPos = 240; //black edge
+
+ curPos++;
+ }
+ //advance a line
+ curPos = prevPos + bufPitch;
+ }
+ //update position
+ dest = startPos + charWidth + _dtCharSpacing * 2 - 1;
+}
+
+lowTextManager_t Text::lowTextManager(uint32 textNum, uint16 width, uint16 logicNum, uint8 color, bool centre) {
+
+ getText(textNum);
+ struct displayText_t textInfo = displayText(_textBuffer, NULL, centre, width, color);
+
+ uint32 compactNum = FIRST_TEXT_COMPACT;
+ Compact *cpt = _skyCompact->fetchCpt(compactNum);
+ while (cpt->status != 0) {
+ compactNum++;
+ cpt = _skyCompact->fetchCpt(compactNum);
+ }
+
+ cpt->flag = (uint16)(compactNum - FIRST_TEXT_COMPACT) + FIRST_TEXT_BUFFER;
+
+ if (SkyEngine::_itemList[cpt->flag])
+ free(SkyEngine::_itemList[cpt->flag]);
+
+ SkyEngine::_itemList[cpt->flag] = textInfo.textData;
+
+ cpt->logic = logicNum;
+ cpt->status = ST_LOGIC | ST_FOREGROUND | ST_RECREATE;
+ cpt->screen = (uint16) Logic::_scriptVariables[SCREEN];
+
+ struct lowTextManager_t ret;
+ ret.textData = textInfo.textData;
+ ret.textWidth = textInfo.textWidth;
+ ret.compactNum = (uint16)compactNum;
+
+ return ret;
+}
+
+void Text::changeTextSpriteColour(uint8 *sprData, uint8 newCol) {
+
+ dataFileHeader *header = (dataFileHeader *)sprData;
+ sprData += sizeof(dataFileHeader);
+ for (uint16 cnt = 0; cnt < header->s_sp_size; cnt++)
+ if (sprData[cnt] >= 241)
+ sprData[cnt] = newCol;
+}
+
+uint32 Text::giveCurrentCharSet(void) {
+ return _curCharSet;
+}
+
+void Text::initHuffTree() {
+ switch (SkyEngine::_systemVars.gameVersion) {
+ case 109:
+ _huffTree = _huffTree_00109;
+ break;
+ case 267:
+ _huffTree = _huffTree_00267;
+ break;
+ case 288:
+ _huffTree = _huffTree_00288;
+ break;
+ case 303:
+ _huffTree = _huffTree_00303;
+ break;
+ case 331:
+ _huffTree = _huffTree_00331;
+ break;
+ case 348:
+ _huffTree = _huffTree_00348;
+ break;
+ case 365:
+ _huffTree = _huffTree_00365;
+ break;
+ case 368:
+ _huffTree = _huffTree_00368;
+ break;
+ case 372:
+ _huffTree = _huffTree_00372;
+ break;
+ default:
+ error("Unknown game version %d", SkyEngine::_systemVars.gameVersion);
+ }
+}
+
+bool Text::patchMessage(uint32 textNum) {
+
+ uint16 patchIdx = _patchLangIdx[SkyEngine::_systemVars.language];
+ uint16 patchNum = _patchLangNum[SkyEngine::_systemVars.language];
+ for (uint16 cnt = 0; cnt < patchNum; cnt++) {
+ if (_patchedMessages[cnt + patchIdx].textNr == textNum) {
+ strcpy(_textBuffer, _patchedMessages[cnt + patchIdx].text);
+ return true;
+ }
+ }
+ return false;
+}
+
+const PatchMessage Text::_patchedMessages[NUM_PATCH_MSG] = {
+ { 28724, "Testo e Parlato" }, // - italian
+ { 28707, "Solo Testo" },
+ { 28693, "Solo Parlato" },
+ { 28724, "Text och tal" }, // - swedish
+ { 28707, "Endast text" },
+ { 28693, "Endast tal" },
+ { 28686, "Musikvolym" },
+ { 4336, "Wir befinden uns EINHUNDERTZWANZIG METER #ber dem ERBODEN!" }, // - german
+};
+
+const uint16 Text::_patchLangIdx[8] = {
+ 0xFFFF, // SKY_ENGLISH
+ 7, // SKY_GERMAN
+ 0xFFFF, // SKY_FRENCH
+ 0xFFFF, // SKY_USA
+ 3, // SKY_SWEDISH
+ 0, // SKY_ITALIAN
+ 0xFFFF, // SKY_PORTUGUESE
+ 0xFFFF // SKY_SPANISH
+};
+
+const uint16 Text::_patchLangNum[8] = {
+ 0, // SKY_ENGLISH
+ 1, // SKY_GERMAN
+ 0, // SKY_FRENCH
+ 0, // SKY_USA
+ 4, // SKY_SWEDISH
+ 3, // SKY_ITALIAN
+ 0, // SKY_PORTUGUESE
+ 0 // SKY_SPANISH
+};
+
+} // End of namespace Sky
diff --git a/engines/sky/text.h b/engines/sky/text.h
new file mode 100644
index 0000000000..33aa6f8df7
--- /dev/null
+++ b/engines/sky/text.h
@@ -0,0 +1,124 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SKYTEXT_H
+#define SKYTEXT_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+
+namespace Sky {
+
+struct Compact;
+class Disk;
+class SkyCompact;
+
+struct HuffTree {
+ unsigned char lChild;
+ unsigned char rChild;
+ unsigned char value;
+};
+
+#define NUM_PATCH_MSG 8
+
+struct PatchMessage {
+ uint32 textNr;
+ char text[100];
+};
+
+class Text {
+public:
+ Text(Disk *skyDisk, SkyCompact *skyCompact);
+ ~Text(void);
+ struct displayText_t displayText(uint32 textNum, uint8 *dest, bool centre, uint16 pixelWidth, uint8 color);
+ struct displayText_t displayText(char *textPtr, uint8 *dest, bool centre, uint16 pixelWidth, uint8 color);
+ struct lowTextManager_t lowTextManager(uint32 textNum, uint16 width, uint16 logicNum, uint8 color, bool centre);
+ void fnSetFont(uint32 fontNr);
+ void fnTextModule(uint32 textInfoId, uint32 textNo);
+ void fnPointerText(uint32 pointedId, uint16 mouseX, uint16 mouseY);
+ void logicCursor(Compact *textCompact, uint16 mouseX, uint16 mouseY);
+ void changeTextSpriteColour(uint8 *sprData, uint8 newCol);
+ uint32 giveCurrentCharSet(void);
+
+ uint32 _numLetters; //no of chars in message
+
+private:
+ void initHuffTree();
+ void getText(uint32 textNr);
+ char getTextChar(uint8 **data, uint32 *bitPos);
+ bool getTextBit(uint8 **data, uint32 *bitPos);
+ void makeGameCharacter(uint8 textChar, uint8 *charSetPtr, uint8 *&data, uint8 color, uint16 bufPitch);
+
+ void patchChar(byte *charSetPtr, int width, int height, int c, const uint16 *data);
+ void patchLINCCharset();
+ bool patchMessage(uint32 textNum);
+
+ Disk *_skyDisk;
+ SkyCompact *_skyCompact;
+
+ const HuffTree *_huffTree;
+
+ struct charSet {
+ uint8 *addr;
+ uint32 charHeight;
+ uint32 charSpacing;
+ } _mainCharacterSet, _linkCharacterSet, _controlCharacterSet;
+
+ uint32 _curCharSet;
+ uint8 *_characterSet;
+ uint8 _charHeight;
+
+ char _textBuffer[1024];
+
+ uint32 _dtCharSpacing; //character separation adjustment
+ uint32 _mouseOfsX, _mouseOfsY;
+ static const PatchMessage _patchedMessages[NUM_PATCH_MSG];
+ static const uint16 _patchLangIdx[8];
+ static const uint16 _patchLangNum[8];
+
+#ifndef PALMOS_68K
+ static const HuffTree _huffTree_00109[]; // trees moved to hufftext.cpp
+ static const HuffTree _huffTree_00267[];
+ static const HuffTree _huffTree_00288[];
+ static const HuffTree _huffTree_00303[];
+ static const HuffTree _huffTree_00331[];
+ static const HuffTree _huffTree_00348[];
+ static const HuffTree _huffTree_00365[];
+ static const HuffTree _huffTree_00368[];
+ static const HuffTree _huffTree_00372[];
+#else
+public:
+ static const HuffTree *_huffTree_00109; // trees moved to hufftext.cpp
+ static const HuffTree *_huffTree_00267;
+ static const HuffTree *_huffTree_00288;
+ static const HuffTree *_huffTree_00303;
+ static const HuffTree *_huffTree_00331;
+ static const HuffTree *_huffTree_00348;
+ static const HuffTree *_huffTree_00365;
+ static const HuffTree *_huffTree_00368;
+ static const HuffTree *_huffTree_00372;
+#endif
+};
+
+} // End of namespace Sky
+
+#endif
diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp
new file mode 100644
index 0000000000..f6865cc5e2
--- /dev/null
+++ b/engines/sword1/animation.cpp
@@ -0,0 +1,307 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "sword1/sword1.h"
+#include "sword1/animation.h"
+#include "sword1/credits.h"
+#include "sound/vorbis.h"
+
+#include "common/config-manager.h"
+#include "common/str.h"
+#include "common/system.h"
+
+namespace Sword1 {
+
+AnimationState::AnimationState(Screen *scr, Audio::Mixer *snd, OSystem *sys)
+ : BaseAnimationState(snd, sys, 640, 400), _scr(scr) {
+}
+
+AnimationState::~AnimationState() {
+}
+
+
+#ifdef BACKEND_8BIT
+void AnimationState::setPalette(byte *pal) {
+ _sys->setPalette(pal, 0, 256);
+}
+#endif
+
+void AnimationState::drawYUV(int width, int height, byte *const *dat) {
+#ifdef BACKEND_8BIT
+ _scr->plotYUV(_lut, width, height, dat);
+#else
+ plotYUV(width, height, dat);
+#endif
+}
+
+void AnimationState::updateScreen(void) {
+#ifndef BACKEND_8BIT
+ _sys->copyRectToOverlay(_overlay, _movieWidth, 0, 40, _movieWidth, _movieHeight);
+#endif
+ _sys->updateScreen();
+}
+
+OverlayColor *AnimationState::giveRgbBuffer(void) {
+#ifdef BACKEND_8BIT
+ return NULL;
+#else
+ return _overlay;
+#endif
+}
+
+bool AnimationState::soundFinished(void) {
+ return !_snd->isSoundHandleActive(_bgSound);
+}
+
+AudioStream *AnimationState::createAudioStream(const char *name, void *arg) {
+ if (arg)
+ return (AudioStream*)arg;
+ else
+ return AudioStream::openStreamFile(name);
+}
+
+MoviePlayer::MoviePlayer(Screen *scr, Audio::Mixer *snd, OSystem *sys)
+ : _scr(scr), _snd(snd), _sys(sys) {
+ for (uint8 cnt = 0; cnt < INTRO_LOGO_OVLS; cnt++)
+ _logoOvls[cnt] = NULL;
+ _introPal = NULL;
+}
+
+MoviePlayer::~MoviePlayer(void) {
+ if (_introPal)
+ free(_introPal);
+ for (uint8 cnt = 0; cnt < INTRO_LOGO_OVLS; cnt++)
+ if (_logoOvls[cnt])
+ free(_logoOvls[cnt]);
+}
+
+/**
+ * Plays an animated cutscene.
+ * @param id the id of the file
+ */
+void MoviePlayer::play(uint32 id) {
+#if defined(USE_MPEG2) && defined(USE_VORBIS)
+ AnimationState *anim = new AnimationState(_scr, _snd, _sys);
+ AudioStream *stream = NULL;
+ if (SwordEngine::_systemVars.cutscenePackVersion == 1) {
+ if ((id == SEQ_INTRO) || (id == SEQ_FINALE) || (id == SEQ_HISTORY) || (id == SEQ_FERRARI)) {
+ // these sequences are language specific
+ char sndName[20];
+ sprintf(sndName, "%s.snd", _sequenceList[id]);
+ Common::File *oggSource = new Common::File();
+ if (oggSource->open(sndName)) {
+ SplittedAudioStream *sStream = new SplittedAudioStream();
+ uint32 numSegs = oggSource->readUint32LE(); // number of audio segments, either 1 or 2.
+ // for each segment and each of the 7 languages, we've got fileoffset and size
+ uint32 *header = (uint32*)malloc(numSegs * 7 * 2 * 4);
+ for (uint32 cnt = 0; cnt < numSegs * 7 * 2; cnt++)
+ header[cnt] = oggSource->readUint32LE();
+ for (uint32 segCnt = 0; segCnt < numSegs; segCnt++) {
+ oggSource->seek( header[SwordEngine::_systemVars.language * 2 + 0 + segCnt * 14]);
+ uint32 segSize = header[SwordEngine::_systemVars.language * 2 + 1 + segCnt * 14];
+ AudioStream *apStream = makeVorbisStream(oggSource, segSize);
+ if (!apStream)
+ error("Can't create Vorbis Stream from file %s", sndName);
+ sStream->appendStream(apStream);
+ }
+ free(header);
+ stream = sStream;
+ } else
+ warning("Sound file \"%s\" not found", sndName);
+ initOverlays(id);
+ oggSource->decRef();
+ }
+ }
+ bool initOK = anim->init(_sequenceList[id], stream);
+
+ uint32 frameCount = 0;
+ if (initOK) {
+ while (anim->decodeFrame()) {
+ processFrame(id, anim, frameCount);
+ anim->updateScreen();
+ frameCount++;
+ OSystem::Event event;
+ while (_sys->pollEvent(event)) {
+ switch (event.type) {
+#ifndef BACKEND_8BIT
+ case OSystem::EVENT_SCREEN_CHANGED:
+ anim->buildLookup();
+ break;
+#endif
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.keycode == 27) {
+ delete anim;
+ return;
+ }
+ break;
+ case OSystem::EVENT_QUIT:
+ _sys->quit();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ while (!anim->soundFinished())
+ _sys->delayMillis(100);
+ delete anim;
+#endif // USE_MPEG2 && USE_VORBIS
+}
+
+void MoviePlayer::insertOverlay(OverlayColor *buf, uint8 *ovl, OverlayColor *pal) {
+ if (ovl != NULL)
+ for (uint32 cnt = 0; cnt < 640 * 400; cnt++)
+ if (ovl[cnt])
+ buf[cnt] = pal[ovl[cnt]];
+}
+
+void MoviePlayer::processFrame(uint32 animId, AnimationState *anim, uint32 frameNo) {
+#if defined(USE_MPEG2) && !defined(BACKEND_8BIT)
+ if ((animId != 4) || (SwordEngine::_systemVars.cutscenePackVersion == 0))
+ return;
+ OverlayColor *buf = anim->giveRgbBuffer();
+ if ((frameNo > 397) && (frameNo < 444)) { // Broken Sword Logo
+ if (frameNo <= 403)
+ insertOverlay(buf, _logoOvls[frameNo - 398], _introPal); // fade up
+ else if (frameNo <= 437)
+ insertOverlay(buf, _logoOvls[(frameNo - 404) % 6 + 6], _introPal); // animation
+ else {
+ insertOverlay(buf, _logoOvls[5 - (frameNo - 438)], _introPal); // fade down
+ }
+ }
+#endif
+}
+
+bool MoviePlayer::initOverlays(uint32 id) {
+#if defined(USE_MPEG2) && !defined(BACKEND_8BIT)
+ if (id == SEQ_INTRO) {
+ ArcFile ovlFile;
+ if (!ovlFile.open("intro.dat")) {
+ warning("\"intro.dat\" not found");
+ return false;
+ }
+ ovlFile.enterPath(SwordEngine::_systemVars.language);
+ for (uint8 fcnt = 0; fcnt < 12; fcnt++) {
+ _logoOvls[fcnt] = ovlFile.decompressFile(fcnt);
+ if (fcnt > 0)
+ for (uint32 cnt = 0; cnt < 640 * 400; cnt++)
+ if (_logoOvls[fcnt - 1][cnt] && !_logoOvls[fcnt][cnt])
+ _logoOvls[fcnt][cnt] = _logoOvls[fcnt - 1][cnt];
+ }
+ uint8 *pal = ovlFile.fetchFile(12);
+ _introPal = (OverlayColor*)malloc(256 * sizeof(OverlayColor));
+ for (uint16 cnt = 0; cnt < 256; cnt++)
+ _introPal[cnt] = _sys->RGBToColor(pal[cnt * 3 + 0], pal[cnt * 3 + 1], pal[cnt * 3 + 2]);
+ }
+#endif
+
+ return true;
+}
+
+SplittedAudioStream::SplittedAudioStream(void) {
+ _queue = NULL;
+}
+
+SplittedAudioStream::~SplittedAudioStream(void) {
+ while (_queue) {
+ delete _queue->stream;
+ FileQueue *que = _queue->next;
+ delete _queue;
+ _queue = que;
+ }
+}
+
+int SplittedAudioStream::getRate(void) const {
+ if (_queue)
+ return _queue->stream->getRate();
+ else
+ return 22050;
+}
+
+void SplittedAudioStream::appendStream(AudioStream *stream) {
+ FileQueue **que = &_queue;
+ while (*que)
+ que = &((*que)->next);
+ *que = new FileQueue;
+ (*que)->stream = stream;
+ (*que)->next = NULL;
+}
+
+bool SplittedAudioStream::endOfData(void) const {
+ if (_queue)
+ return _queue->stream->endOfData();
+ else
+ return true;
+}
+
+bool SplittedAudioStream::isStereo(void) const {
+ if (_queue)
+ return _queue->stream->isStereo();
+ else
+ return false; // all the BS1 files are mono, anyways.
+}
+
+int SplittedAudioStream::readBuffer(int16 *buffer, const int numSamples) {
+ int retVal = 0;
+ int needSamples = numSamples;
+ while (needSamples && _queue) {
+ int retSmp = _queue->stream->readBuffer(buffer, needSamples);
+ needSamples -= retSmp;
+ retVal += retSmp;
+ buffer += retSmp;
+ if (_queue->stream->endOfData()) {
+ delete _queue->stream;
+ FileQueue *que = _queue->next;
+ delete _queue;
+ _queue = que;
+ }
+ }
+ return retVal;
+}
+
+const char * MoviePlayer::_sequenceList[20] = {
+ "ferrari", // 0 CD2 ferrari running down fitz in sc19
+ "ladder", // 1 CD2 george walking down ladder to dig sc24->sc$
+ "steps", // 2 CD2 george walking down steps sc23->sc24
+ "sewer", // 3 CD1 george entering sewer sc2->sc6
+ "intro", // 4 CD1 intro sequence ->sc1
+ "river", // 5 CD1 george being thrown into river by flap & g$
+ "truck", // 6 CD2 truck arriving at bull's head sc45->sc53/4
+ "grave", // 7 BOTH george's grave in scotland, from sc73 + from sc38 $
+ "montfcon", // 8 CD2 monfaucon clue in ireland dig, sc25
+ "tapestry", // 9 CD2 tapestry room beyond spain well, sc61
+ "ireland", // 10 CD2 ireland establishing shot europe_map->sc19
+ "finale", // 11 CD2 grand finale at very end, from sc73
+ "history", // 12 CD1 George's history lesson from Nico, in sc10
+ "spanish", // 13 CD2 establishing shot for 1st visit to Spain, europe_m$
+ "well", // 14 CD2 first time being lowered down well in Spai$
+ "candle", // 15 CD2 Candle burning down in Spain mausoleum sc59
+ "geodrop", // 16 CD2 from sc54, George jumping down onto truck
+ "vulture", // 17 CD2 from sc54, vultures circling George's dead body
+ "enddemo", // 18 --- for end of single CD demo
+ "credits", // 19 CD2 credits, to follow "finale" sequence
+};
+
+} // End of namespace Sword1
diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h
new file mode 100644
index 0000000000..2200212384
--- /dev/null
+++ b/engines/sword1/animation.h
@@ -0,0 +1,120 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef ANIMATION_H
+#define ANIMATION_H
+
+#include "graphics/animation.h"
+
+#include "sword1/screen.h"
+#include "sword1/sound.h"
+#include "sound/audiostream.h"
+
+namespace Sword1 {
+
+enum {
+ SEQ_FERRARI = 0,
+ SEQ_LADDER,
+ SEQ_STEPS,
+ SEQ_SEWER,
+ SEQ_INTRO,
+ SEQ_RIVER,
+ SEQ_TRUCK,
+ SEQ_GRAVE,
+ SEQ_MONTFCON,
+ SEQ_TAPESTRY,
+ SEQ_IRELAND,
+ SEQ_FINALE,
+ SEQ_HISTORY,
+ SEQ_SPANISH,
+ SEQ_WELL,
+ SEQ_CANDLE,
+ SEQ_GEODROP,
+ SEQ_VULTURE,
+ SEQ_ENDDEMO,
+ SEQ_CREDITS
+};
+
+#define INTRO_LOGO_OVLS 12
+#define INTRO_TEXT_OVLS 8
+
+class AnimationState : public Graphics::BaseAnimationState {
+private:
+ Screen *_scr;
+
+public:
+ AnimationState(Screen *scr, Audio::Mixer *snd, OSystem *sys);
+ ~AnimationState();
+ void updateScreen();
+ OverlayColor *giveRgbBuffer(void);
+ bool soundFinished();
+
+private:
+ void drawYUV(int width, int height, byte *const *dat);
+
+#ifdef BACKEND_8BIT
+ void setPalette(byte *pal);
+#endif
+
+protected:
+ virtual AudioStream *createAudioStream(const char *name, void *arg);
+};
+
+class MoviePlayer {
+public:
+ MoviePlayer(Screen *scr, Audio::Mixer *snd, OSystem *sys);
+ ~MoviePlayer(void);
+ void play(uint32 id);
+private:
+ void insertOverlay(OverlayColor *buf, uint8 *ovl, OverlayColor *pal);
+ void processFrame(uint32 animId, AnimationState *anim, uint32 frameNo);
+ bool initOverlays(uint32 id);
+ Screen *_scr;
+ Audio::Mixer *_snd;
+ OSystem *_sys;
+
+ static const char *_sequenceList[20];
+ uint8 *_logoOvls[INTRO_LOGO_OVLS];
+ OverlayColor *_introPal;
+};
+
+struct FileQueue {
+ AudioStream *stream;
+ FileQueue *next;
+};
+
+class SplittedAudioStream : public AudioStream {
+public:
+ SplittedAudioStream(void);
+ ~SplittedAudioStream(void);
+ void appendStream(AudioStream *stream);
+ virtual int readBuffer(int16 *buffer, const int numSamples);
+ virtual bool isStereo(void) const;
+ virtual bool endOfData(void) const;
+ virtual int getRate(void) const;
+private:
+ FileQueue *_queue;
+};
+
+} // End of namespace Sword1
+
+#endif
diff --git a/engines/sword1/collision.h b/engines/sword1/collision.h
new file mode 100644
index 0000000000..0795298ec6
--- /dev/null
+++ b/engines/sword1/collision.h
@@ -0,0 +1,50 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSCOLLISION_H
+#define BSCOLLISION_H
+
+/*#include "objectman.h"
+
+namespace Sword1 {
+
+class Logic;
+
+class Collision {
+public:
+ Collision(ObjectMan *pObjMan, Logic *pLogic);
+ ~Collision(void);
+ void checkCollisions(void);
+ void fnBumpOff(void);
+ void fnBumpOn(void);
+private:
+ int32 getIntersect(int32 x0, int32 y0, int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3);
+ int noCol;
+ ObjectMan *_objMan;
+ Logic *_logic; // for CFN_preset_script
+};
+
+} // End of namespace Sword1
+*/
+// maybe it's better to make this part of Router
+
+#endif // BSCOLLISION_H
diff --git a/engines/sword1/control.cpp b/engines/sword1/control.cpp
new file mode 100644
index 0000000000..bf101e9638
--- /dev/null
+++ b/engines/sword1/control.cpp
@@ -0,0 +1,1301 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/util.h"
+#include "common/savefile.h"
+#include "common/system.h"
+
+#include "gui/message.h"
+
+#include "sword1/control.h"
+#include "sword1/logic.h"
+#include "sword1/mouse.h"
+#include "sword1/music.h"
+#include "sword1/objectman.h"
+#include "sword1/resman.h"
+#include "sword1/sound.h"
+#include "sword1/sword1.h"
+#include "sword1/sworddefs.h"
+#include "sword1/swordres.h"
+
+namespace Sword1 {
+
+enum {
+ kKeyRepeatInitialDelay = 400,
+ kKeyRepeatSustainDelay = 100
+};
+
+enum LangStrings {
+ STR_PAUSED = 0,
+ STR_INSERT_CD_A,
+ STR_INSERT_CD_B,
+ STR_INCORRECT_CD,
+ STR_SAVE,
+ STR_RESTORE,
+ STR_RESTART,
+ STR_START,
+ STR_QUIT,
+ STR_SPEED,
+ STR_VOLUME,
+ STR_TEXT,
+ STR_DONE,
+ STR_OK,
+ STR_CANCEL,
+ STR_MUSIC,
+ STR_SPEECH,
+ STR_FX,
+ STR_THE_END,
+ STR_DRIVE_FULL
+};
+
+enum ButtonIds {
+ BUTTON_DONE = 1,
+ BUTTON_MAIN_PANEL,
+ BUTTON_SAVE_PANEL,
+ BUTTON_RESTORE_PANEL,
+ BUTTON_RESTART,
+ BUTTON_QUIT,
+ BUTTON_SPEED,
+ BUTTON_VOLUME_PANEL,
+ BUTTON_TEXT,
+ BUTTON_CONFIRM,
+//-
+ BUTTON_SCROLL_UP_FAST,
+ BUTTON_SCROLL_UP_SLOW,
+ BUTTON_SCROLL_DOWN_SLOW,
+ BUTTON_SCROLL_DOWN_FAST,
+ BUTTON_SAVE_SELECT1,
+ BUTTON_SAVE_SELECT2,
+ BUTTON_SAVE_SELECT3,
+ BUTTON_SAVE_SELECT4,
+ BUTTON_SAVE_SELECT5,
+ BUTTON_SAVE_SELECT6,
+ BUTTON_SAVE_SELECT7,
+ BUTTON_SAVE_SELECT8,
+ BUTTON_SAVE_RESTORE_OKAY,
+ BUTTON_SAVE_CANCEL,
+//-
+ CONFIRM_OKAY,
+ CONFIRM_CANCEL
+};
+
+enum TextModes {
+ TEXT_LEFT_ALIGN = 0,
+ TEXT_CENTER,
+ TEXT_RIGHT_ALIGN,
+ TEXT_RED_FONT = 128
+};
+
+ControlButton::ControlButton(uint16 x, uint16 y, uint32 resId, uint8 id, uint8 flag, ResMan *pResMan, uint8 *screenBuf, OSystem *system) {
+ _x = x;
+ _y = y;
+ _id = id;
+ _flag = flag;
+ _resId = resId;
+ _resMan = pResMan;
+ _frameIdx = 0;
+ _resMan->resOpen(_resId);
+ FrameHeader *tmp = _resMan->fetchFrame(_resMan->fetchRes(_resId), 0);
+ _width = FROM_LE_16(tmp->width);
+ _height = FROM_LE_16(tmp->height);
+ if ((x == 0) && (y == 0)) { // center the frame (used for panels);
+ _x = (640 - _width) / 2;
+ _y = (480 - _height) / 2;
+ }
+ _dstBuf = screenBuf + _y * SCREEN_WIDTH + _x;
+ _system = system;
+}
+
+ControlButton::~ControlButton(void) {
+ _resMan->resClose(_resId);
+}
+
+bool ControlButton::isSaveslot(void) {
+ return ((_resId >= SR_SLAB1) && (_resId <= SR_SLAB4));
+}
+
+void ControlButton::draw(void) {
+ FrameHeader *fHead = _resMan->fetchFrame(_resMan->fetchRes(_resId), _frameIdx);
+ uint8 *src = (uint8*)fHead + sizeof(FrameHeader);
+ uint8 *dst = _dstBuf;
+ for (uint16 cnt = 0; cnt < FROM_LE_16(fHead->height); cnt++) {
+ for (uint16 cntx = 0; cntx < FROM_LE_16(fHead->width); cntx++)
+ if (src[cntx])
+ dst[cntx] = src[cntx];
+ dst += SCREEN_WIDTH;
+ src += FROM_LE_16(fHead->width);
+ }
+ _system->copyRectToScreen(_dstBuf, SCREEN_WIDTH, _x, _y, _width, _height);
+}
+
+bool ControlButton::wasClicked(uint16 mouseX, uint16 mouseY) {
+ if ((_x <= mouseX) && (_y <= mouseY) && (_x + _width >= mouseX) && (_y + _height >= mouseY))
+ return true;
+ else
+ return false;
+}
+
+void ControlButton::setSelected(uint8 selected) {
+ _frameIdx = selected;
+ draw();
+}
+
+Control::Control(Common::SaveFileManager *saveFileMan, ResMan *pResMan, ObjectMan *pObjMan, OSystem *system, Mouse *pMouse, Sound *pSound, Music *pMusic) {
+ _saveFileMan = saveFileMan;
+ _resMan = pResMan;
+ _objMan = pObjMan;
+ _system = system;
+ _mouse = pMouse;
+ _music = pMusic;
+ _sound = pSound;
+ _lStrings = _languageStrings + SwordEngine::_systemVars.language * 20;
+ _keyRepeat = 0;
+ _keyRepeatTime = 0;
+}
+
+void Control::askForCd(void) {
+ _screenBuf = (uint8*)malloc(640 * 480);
+ uint32 fontId = SR_FONT;
+ if (SwordEngine::_systemVars.language == BS1_CZECH)
+ fontId = CZECH_SR_FONT;
+ _font = (uint8*)_resMan->openFetchRes(fontId);
+ uint8 *pal = (uint8*)_resMan->openFetchRes(SR_PALETTE);
+ uint8 *palOut = (uint8*)malloc(256 * 4);
+ for (uint16 cnt = 1; cnt < 256; cnt++) {
+ palOut[cnt * 4 + 0] = pal[cnt * 3 + 0] << 2;
+ palOut[cnt * 4 + 1] = pal[cnt * 3 + 1] << 2;
+ palOut[cnt * 4 + 2] = pal[cnt * 3 + 2] << 2;
+ }
+ palOut[0] = palOut[1] = palOut[2] = palOut[3] = 0;
+ _resMan->resClose(SR_PALETTE);
+ _system->setPalette(palOut, 0, 256);
+ free(palOut);
+
+ Common::File test;
+ char fName[10];
+ uint8 textA[50];
+ sprintf(fName, "cd%d.id", SwordEngine::_systemVars.currentCD);
+ sprintf((char*)textA, "%s%d", _lStrings[STR_INSERT_CD_A], SwordEngine::_systemVars.currentCD);
+ bool notAccepted = true;
+ bool refreshText = true;
+ do {
+ if (refreshText) {
+ memset(_screenBuf, 0, 640 * 480);
+ renderText(textA, 320, 220, TEXT_CENTER);
+ renderText(_lStrings[STR_INSERT_CD_B], 320, 240, TEXT_CENTER);
+ _system->copyRectToScreen(_screenBuf, 640, 0, 0, 640, 480);
+ _system->updateScreen();
+ }
+ delay(300);
+ if (_keyPressed) {
+ test.open(fName);
+ if (!test.isOpen()) {
+ memset(_screenBuf, 0, 640 * 480);
+ renderText(_lStrings[STR_INCORRECT_CD], 320, 230, TEXT_CENTER);
+ _system->copyRectToScreen(_screenBuf, 640, 0, 0, 640, 480);
+ _system->updateScreen();
+ delay(2000);
+ refreshText = true;
+ } else {
+ test.close();
+ notAccepted = false;
+ }
+ }
+ } while (notAccepted && (!SwordEngine::_systemVars.engineQuit));
+
+ _resMan->resClose(fontId);
+ free(_screenBuf);
+}
+
+uint8 Control::runPanel(void) {
+ _mouseDown = false;
+ _restoreBuf = NULL;
+ _keyPressed = _numButtons = 0;
+ _screenBuf = (uint8*)malloc(640 * 480);
+ memset(_screenBuf, 0, 640 * 480);
+ _system->copyRectToScreen(_screenBuf, 640, 0, 0, 640, 480);
+ _sound->quitScreen();
+
+ uint32 fontId = SR_FONT, redFontId = SR_REDFONT;
+ if (SwordEngine::_systemVars.language == BS1_CZECH) {
+ fontId = CZECH_SR_FONT;
+ redFontId = CZECH_SR_REDFONT;
+ }
+ _font = (uint8*)_resMan->openFetchRes(fontId);
+ _redFont = (uint8*)_resMan->openFetchRes(redFontId);
+
+ uint8 *pal = (uint8*)_resMan->openFetchRes(SR_PALETTE);
+ uint8 *palOut = (uint8*)malloc(256 * 4);
+ for (uint16 cnt = 1; cnt < 256; cnt++) {
+ palOut[cnt * 4 + 0] = pal[cnt * 3 + 0] << 2;
+ palOut[cnt * 4 + 1] = pal[cnt * 3 + 1] << 2;
+ palOut[cnt * 4 + 2] = pal[cnt * 3 + 2] << 2;
+ }
+ palOut[0] = palOut[1] = palOut[2] = palOut[3] = 0;
+ _resMan->resClose(SR_PALETTE);
+ _system->setPalette(palOut, 0, 256);
+ free(palOut);
+ uint8 mode = 0, newMode = BUTTON_MAIN_PANEL;
+ bool fullRefresh = false;
+ _mouse->controlPanel(true);
+ uint8 retVal = CONTROL_NOTHING_DONE;
+ _music->startMusic(61, 1);
+
+ do {
+ if (newMode) {
+ mode = newMode;
+ fullRefresh = true;
+ destroyButtons();
+ memset(_screenBuf, 0, 640 * 480);
+ if (mode != BUTTON_SAVE_PANEL)
+ _cursorVisible = false;
+ }
+ switch (mode) {
+ case BUTTON_MAIN_PANEL:
+ if (fullRefresh)
+ setupMainPanel();
+ break;
+ case BUTTON_SAVE_PANEL:
+ if (fullRefresh) {
+ setupSaveRestorePanel(true);
+ }
+ if (_selectedSavegame < 255) {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
+ bool visible = _cursorVisible;
+ _cursorTick++;
+ if (_cursorTick == 7)
+ _cursorVisible = true;
+ else if (_cursorTick == 14) {
+ _cursorVisible = false;
+ _cursorTick = 0;
+ }
+ if (_keyPressed)
+ handleSaveKey(_keyPressed);
+ else if (_cursorVisible != visible)
+ showSavegameNames();
+ } else {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ }
+ break;
+ case BUTTON_RESTORE_PANEL:
+ if (fullRefresh)
+ setupSaveRestorePanel(false);
+ break;
+ case BUTTON_VOLUME_PANEL:
+ if (fullRefresh)
+ setupVolumePanel();
+ break;
+ default:
+ break;
+ }
+ if (fullRefresh) {
+ fullRefresh = false;
+ _system->copyRectToScreen(_screenBuf, SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, 480);
+ }
+ _system->updateScreen();
+ delay(1000 / 12);
+ newMode = getClicks(mode, &retVal);
+ } while ((newMode != BUTTON_DONE) && (retVal == 0) && (!SwordEngine::_systemVars.engineQuit));
+ destroyButtons();
+ _resMan->resClose(fontId);
+ _resMan->resClose(redFontId);
+ memset(_screenBuf, 0, 640 * 480);
+ _system->copyRectToScreen(_screenBuf, 640, 0, 0, 640, 480);
+ free(_screenBuf);
+ _mouse->controlPanel(false);
+ _music->startMusic(Logic::_scriptVars[CURRENT_MUSIC], 1);
+ return retVal;
+}
+
+uint8 Control::getClicks(uint8 mode, uint8 *retVal) {
+ uint8 checkButtons = _numButtons;
+ if (mode == BUTTON_VOLUME_PANEL) {
+ handleVolumeClicks();
+ checkButtons = 1;
+ }
+
+ uint8 flag = 0;
+ if (_keyPressed == 27)
+ flag = kButtonCancel;
+ else if (_keyPressed == '\r' || _keyPressed == '\n')
+ flag = kButtonOk;
+
+ if (flag) {
+ for (uint8 cnt = 0; cnt < checkButtons; cnt++)
+ if (_buttons[cnt]->_flag == flag)
+ return handleButtonClick(_buttons[cnt]->_id, mode, retVal);
+ }
+
+ if (!_mouseState)
+ return 0;
+ if (_mouseState & BS1L_BUTTON_DOWN)
+ for (uint8 cnt = 0; cnt < checkButtons; cnt++)
+ if (_buttons[cnt]->wasClicked(_mouseX, _mouseY)) {
+ _selectedButton = cnt;
+ _buttons[cnt]->setSelected(1);
+ if (_buttons[cnt]->isSaveslot())
+ showSavegameNames();
+ }
+ if (_mouseState & BS1L_BUTTON_UP) {
+ for (uint8 cnt = 0; cnt < checkButtons; cnt++)
+ if (_buttons[cnt]->wasClicked(_mouseX, _mouseY))
+ if (_selectedButton == cnt) {
+ // saveslots stay selected after clicking
+ if (!_buttons[cnt]->isSaveslot())
+ _buttons[cnt]->setSelected(0);
+ _selectedButton = 255;
+ return handleButtonClick(_buttons[cnt]->_id, mode, retVal);
+ }
+ if (_selectedButton < checkButtons) {
+ _buttons[_selectedButton]->setSelected(0);
+ if (_buttons[_selectedButton]->isSaveslot())
+ showSavegameNames();
+ }
+ _selectedButton = 255;
+ }
+ if (_mouseState & BS1_WHEEL_UP) {
+ for (uint8 cnt = 0; cnt < checkButtons; cnt++)
+ if (_buttons[cnt]->_id == BUTTON_SCROLL_UP_SLOW)
+ return handleButtonClick(_buttons[cnt]->_id, mode, retVal);
+ }
+ if (_mouseState & BS1_WHEEL_DOWN) {
+ for (uint8 cnt = 0; cnt < checkButtons; cnt++)
+ if (_buttons[cnt]->_id == BUTTON_SCROLL_DOWN_SLOW)
+ return handleButtonClick(_buttons[cnt]->_id, mode, retVal);
+ }
+ return 0;
+}
+
+uint8 Control::handleButtonClick(uint8 id, uint8 mode, uint8 *retVal) {
+ switch (mode) {
+ case BUTTON_MAIN_PANEL:
+ if (id == BUTTON_RESTART) {
+ if (SwordEngine::_systemVars.controlPanelMode) // if player is dead or has just started, don't ask for confirmation
+ *retVal |= CONTROL_RESTART_GAME;
+ else if (getConfirm(_lStrings[STR_RESTART]))
+ *retVal |= CONTROL_RESTART_GAME;
+ else
+ return mode;
+ } else if ((id == BUTTON_RESTORE_PANEL) || (id == BUTTON_SAVE_PANEL) ||
+ (id == BUTTON_DONE) || (id == BUTTON_VOLUME_PANEL))
+ return id;
+ else if (id == BUTTON_TEXT) {
+ SwordEngine::_systemVars.showText ^= 1;
+ _buttons[5]->setSelected(SwordEngine::_systemVars.showText);
+ } else if (id == BUTTON_QUIT) {
+ if (getConfirm(_lStrings[STR_QUIT]))
+ SwordEngine::_systemVars.engineQuit = true;
+ return mode;
+ }
+ break;
+ case BUTTON_SAVE_PANEL:
+ case BUTTON_RESTORE_PANEL:
+ if ((id >= BUTTON_SCROLL_UP_FAST) && (id <= BUTTON_SCROLL_DOWN_FAST))
+ saveNameScroll(id, mode == BUTTON_SAVE_PANEL);
+ else if ((id >= BUTTON_SAVE_SELECT1) && (id <= BUTTON_SAVE_SELECT8))
+ saveNameSelect(id, mode == BUTTON_SAVE_PANEL);
+ else if (id == BUTTON_SAVE_RESTORE_OKAY) {
+ if (mode == BUTTON_SAVE_PANEL) {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ if (saveToFile()) // don't go back to main panel if save fails.
+ return BUTTON_DONE;
+ } else {
+ if (restoreFromFile()) { // don't go back to main panel if restore fails.
+ *retVal |= CONTROL_GAME_RESTORED;
+ return BUTTON_MAIN_PANEL;
+ }
+ }
+ } else if (id == BUTTON_SAVE_CANCEL) {
+ _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
+ return BUTTON_MAIN_PANEL; // mode down to main panel
+ }
+ break;
+ case BUTTON_VOLUME_PANEL:
+ return id;
+ }
+ return 0;
+}
+
+void Control::deselectSaveslots(void) {
+ for (uint8 cnt = 0; cnt < 8; cnt++)
+ _buttons[cnt]->setSelected(0);
+}
+
+void Control::setupMainPanel(void) {
+ uint32 panelId;
+
+ if (SwordEngine::_systemVars.controlPanelMode == CP_DEATHSCREEN)
+ panelId = SR_DEATHPANEL;
+ else {
+ if (SwordEngine::_systemVars.language <= BS1_SPANISH)
+ panelId = SR_PANEL_ENGLISH + SwordEngine::_systemVars.language;
+ else
+ panelId = SR_PANEL_ENGLISH;
+ }
+
+ ControlButton *panel = new ControlButton(0, 0, panelId, 0, 0, _resMan, _screenBuf, _system);
+ panel->draw();
+ delete panel;
+
+ if (SwordEngine::_systemVars.controlPanelMode != CP_NORMAL)
+ createButtons(_deathButtons, 3);
+ else {
+ createButtons(_panelButtons, 7);
+ _buttons[5]->setSelected(SwordEngine::_systemVars.showText);
+ }
+
+ if (SwordEngine::_systemVars.controlPanelMode == CP_THEEND) // end of game
+ renderText(_lStrings[STR_THE_END], 480, 188 + 40, TEXT_RIGHT_ALIGN);
+
+ if (SwordEngine::_systemVars.controlPanelMode == CP_NORMAL) { // normal panel
+ renderText(_lStrings[STR_SAVE], 180, 188 + 40, TEXT_LEFT_ALIGN);
+ renderText(_lStrings[STR_DONE], 460, 332 + 40, TEXT_RIGHT_ALIGN);
+ renderText(_lStrings[STR_RESTORE], 180, 224 + 40, TEXT_LEFT_ALIGN);
+ renderText(_lStrings[STR_RESTART], 180, 260 + 40, TEXT_LEFT_ALIGN);
+ renderText(_lStrings[STR_QUIT], 180, 296 + 40, TEXT_LEFT_ALIGN);
+
+ renderText(_lStrings[STR_VOLUME], 460, 188 + 40, TEXT_RIGHT_ALIGN);
+ renderText(_lStrings[STR_TEXT], 460, 224 + 40, TEXT_RIGHT_ALIGN);
+ } else {
+ renderText(_lStrings[STR_RESTORE], 285, 224 + 40, TEXT_LEFT_ALIGN);
+ if (SwordEngine::_systemVars.controlPanelMode == CP_NEWGAME) // just started game
+ renderText(_lStrings[STR_START], 285, 260 + 40, TEXT_LEFT_ALIGN);
+ else
+ renderText(_lStrings[STR_RESTART], 285, 260 + 40, TEXT_LEFT_ALIGN);
+ renderText(_lStrings[STR_QUIT], 285, 296 + 40, TEXT_LEFT_ALIGN);
+ }
+}
+
+void Control::setupSaveRestorePanel(bool saving) {
+ FrameHeader *savePanel = _resMan->fetchFrame(_resMan->openFetchRes(SR_WINDOW), 0);
+ uint16 panelX = (640 - FROM_LE_16(savePanel->width)) / 2;
+ uint16 panelY = (480 - FROM_LE_16(savePanel->height)) / 2;
+ ControlButton *panel = new ControlButton(panelX, panelY, SR_WINDOW, 0, 0, _resMan, _screenBuf, _system);
+ panel->draw();
+ delete panel;
+ _resMan->resClose(SR_WINDOW);
+ createButtons(_saveButtons, 14);
+ renderText(_lStrings[STR_CANCEL], _saveButtons[13].x - 10, _saveButtons[13].y, TEXT_RIGHT_ALIGN);
+ if (saving) {
+ renderText(_lStrings[STR_SAVE], _saveButtons[12].x + 30, _saveButtons[13].y, TEXT_LEFT_ALIGN);
+ } else {
+ renderText(_lStrings[STR_RESTORE], _saveButtons[12].x + 30, _saveButtons[13].y, TEXT_LEFT_ALIGN);
+ }
+ readSavegameDescriptions();
+ _selectedSavegame = 255;
+ showSavegameNames();
+}
+
+void Control::setupVolumePanel(void) {
+ ControlButton *panel = new ControlButton(0, 0, SR_VOLUME, 0, 0, _resMan, _screenBuf, _system);
+ panel->draw();
+ delete panel;
+
+ renderText(_lStrings[STR_MUSIC], 149, 39 + 40, TEXT_LEFT_ALIGN);
+ renderText(_lStrings[STR_SPEECH], 320, 39 + 40, TEXT_CENTER);
+ renderText(_lStrings[STR_FX], 438, 39 + 40, TEXT_LEFT_ALIGN);
+
+ createButtons(_volumeButtons, 4);
+ renderText(_lStrings[STR_DONE], _volumeButtons[0].x - 10, _volumeButtons[0].y, TEXT_RIGHT_ALIGN);
+
+ uint8 volL, volR;
+ _music->giveVolume(&volL, &volR);
+ renderVolumeBar(1, volL, volR);
+ _sound->giveSpeechVol(&volL, &volR);
+ renderVolumeBar(2, volL, volR);
+ _sound->giveSfxVol(&volL, &volR);
+ renderVolumeBar(3, volL, volR);
+}
+
+void Control::handleVolumeClicks(void) {
+ if (_mouseDown) {
+ uint8 clickedId = 0;
+ for (uint8 cnt = 1; cnt < 4; cnt++)
+ if (_buttons[cnt]->wasClicked(_mouseX, _mouseY))
+ clickedId = cnt;
+ if (clickedId) { // these are circle shaped, so check again if it was clicked.
+ uint8 clickDest = 0;
+ int16 mouseDiffX = _mouseX - (_volumeButtons[clickedId].x + 48);
+ int16 mouseDiffY = _mouseY - (_volumeButtons[clickedId].y + 48);
+ int16 mouseOffs = (int16)sqrt((double)(mouseDiffX * mouseDiffX + mouseDiffY * mouseDiffY));
+ // check if the player really hit the button (but not the center).
+ if ((mouseOffs <= 42) && (mouseOffs >= 8)) {
+ if (mouseDiffX > 8) { // right part
+ if (mouseDiffY < -8) // upper right
+ clickDest = 2;
+ else if (ABS(mouseDiffY) <= 8) // right
+ clickDest = 3;
+ else // lower right
+ clickDest = 4;
+ } else if (mouseDiffX < -8) { // left part
+ if (mouseDiffY < -8) // upper left
+ clickDest = 8;
+ else if (ABS(mouseDiffY) <= 8) // left
+ clickDest = 7;
+ else // lower left
+ clickDest = 6;
+ } else { // middle
+ if (mouseDiffY < -8)
+ clickDest = 1; // upper
+ else if (mouseDiffY > 8)
+ clickDest = 5; // lower
+ }
+ }
+ _buttons[clickedId]->setSelected(clickDest);
+ changeVolume(clickedId, clickDest);
+ }
+ } else if (_mouseState & BS1L_BUTTON_UP) {
+ _buttons[1]->setSelected(0);
+ _buttons[2]->setSelected(0);
+ _buttons[3]->setSelected(0);
+ }
+}
+
+void Control::changeVolume(uint8 id, uint8 action) {
+ // ids: 1 = music, 2 = speech, 3 = sfx
+ uint8 volL = 0, volR = 0;
+ if (id == 1)
+ _music->giveVolume(&volL, &volR);
+ else if (id == 2)
+ _sound->giveSpeechVol(&volL, &volR);
+ else if (id == 3)
+ _sound->giveSfxVol(&volL, &volR);
+
+ int8 direction = 0;
+ if ((action >= 4) && (action <= 6)) // lower part of the button => decrease volume
+ direction = -1;
+ else if ((action == 8) || (action == 1) || (action == 2)) // upper part => increase volume
+ direction = 1;
+ else if ((action == 3) || (action == 7)) // middle part => pan volume
+ direction = 1;
+ int8 factorL = 8, factorR = 8;
+ if ((action >= 6) && (action <= 8)) { // left part => left pan
+ factorL = 8;
+ factorR = (action == 7) ? -8 : 0;
+ } else if ((action >= 2) && (action <= 4)) { // right part
+ factorR = 8;
+ factorL = (action == 3) ? -8 : 0;
+ }
+ int16 resVolL = volL + direction * factorL;
+ int16 resVolR = volR + direction * factorR;
+
+ volL = (uint8)MAX((int16)0, MIN(resVolL, (int16)255));
+ volR = (uint8)MAX((int16)0, MIN(resVolR, (int16)255));
+
+ if (id == 1)
+ _music->setVolume(volL, volR);
+ else if (id == 2)
+ _sound->setSpeechVol(volL, volR);
+ else if (id == 3)
+ _sound->setSfxVol(volL, volR);
+
+ renderVolumeBar(id, volL, volR);
+}
+
+bool Control::getConfirm(const uint8 *title) {
+ ControlButton *panel = new ControlButton(0, 0, SR_CONFIRM, 0, 0, _resMan, _screenBuf, _system);
+ panel->draw();
+ delete panel;
+ renderText(title, 320, 160, TEXT_CENTER);
+ ControlButton *buttons[2];
+ buttons[0] = new ControlButton(260, 192 + 40, SR_BUTTON, 0, 0, _resMan, _screenBuf, _system);
+ renderText(_lStrings[STR_OK], 640 - 260, 192 + 40, TEXT_RIGHT_ALIGN);
+ buttons[1] = new ControlButton(260, 256 + 40, SR_BUTTON, 0, 0, _resMan, _screenBuf, _system);
+ renderText(_lStrings[STR_CANCEL], 640 - 260, 256 + 40, TEXT_RIGHT_ALIGN);
+ uint8 retVal = 0;
+ uint8 clickVal = 0;
+ do {
+ buttons[0]->draw();
+ buttons[1]->draw();
+ _system->updateScreen();
+ delay(1000 / 12);
+ if (_keyPressed == 27)
+ retVal = 2;
+ else if (_keyPressed == '\r' || _keyPressed == '\n')
+ retVal = 1;
+ if (_mouseState & BS1L_BUTTON_DOWN) {
+ if (buttons[0]->wasClicked(_mouseX, _mouseY))
+ clickVal = 1;
+ else if (buttons[1]->wasClicked(_mouseX, _mouseY))
+ clickVal = 2;
+ else
+ clickVal = 0;
+ if (clickVal)
+ buttons[clickVal - 1]->setSelected(1);
+ }
+ if ((_mouseState & BS1L_BUTTON_UP) && (clickVal)) {
+ if (buttons[clickVal - 1]->wasClicked(_mouseX, _mouseY))
+ retVal = clickVal;
+ else
+ buttons[clickVal - 1]->setSelected(0);
+ clickVal = 0;
+ }
+ } while (!retVal);
+ delete buttons[0];
+ delete buttons[1];
+ return retVal == 1;
+}
+
+bool Control::keyAccepted(uint8 key) {
+ // this routine needs changes for Czech keys... No idea how to do that, though.
+ // FIXME: It is not a good idea to put non-ASCII chars into a C source file,
+ // since there is no way to specify which encoding you are using.
+ // It is better to encode them as hex/octal. Although in this particular
+ // case, it seems questionable to do this at all, since we currently
+ // do not at all specify which encoding keyboard events use, so this
+ // check here is probably not portable anyway...
+ static const char allowedSpecials[] = "éèáàúùäöüÄÖÜß,.:-()?! \"\'";
+ if (((key >= 'A') && (key <= 'Z')) ||
+ ((key >= 'a') && (key <= 'z')) ||
+ ((key >= '0') && (key <= '9')) ||
+ strchr(allowedSpecials, key))
+ return true;
+ else
+ return false;
+}
+
+void Control::handleSaveKey(uint8 key) {
+ if (_selectedSavegame < 255) {
+ uint8 len = strlen((char*)_saveNames[_selectedSavegame]);
+ if ((key == 8) && len) // backspace
+ _saveNames[_selectedSavegame][len - 1] = '\0';
+ else if (keyAccepted(key) && (len < 31)) {
+ _saveNames[_selectedSavegame][len] = key;
+ _saveNames[_selectedSavegame][len + 1] = '\0';
+ }
+ showSavegameNames();
+ }
+}
+
+bool Control::saveToFile(void) {
+ if ((_selectedSavegame == 255) || !strlen((char*)_saveNames[_selectedSavegame]))
+ return false; // no saveslot selected or no name entered
+ saveGameToFile(_selectedSavegame);
+ writeSavegameDescriptions();
+ return true;
+}
+
+bool Control::restoreFromFile(void) {
+ if (_selectedSavegame < 255) {
+ return restoreGameFromFile(_selectedSavegame);
+ } else
+ return false;
+}
+
+void Control::readSavegameDescriptions(void) {
+ Common::InSaveFile *inf;
+ inf = _saveFileMan->openForLoading("SAVEGAME.INF");
+ _saveScrollPos = _saveFiles = 0;
+ _selectedSavegame = 255;
+ if (inf) {
+ uint8 curFileNum = 0;
+ uint8 ch;
+ do {
+ uint8 pos = 0;
+ do {
+ ch = inf->readByte();
+ if ((ch == 10) || (ch == 255))
+ _saveNames[curFileNum][pos] = '\0';
+ else
+ _saveNames[curFileNum][pos] = ch;
+ pos++;
+ } while ((ch != 10) && (ch != 255));
+ curFileNum++;
+ } while (ch != 255);
+ _saveFiles = curFileNum;
+ for (uint8 cnt = _saveFiles; cnt < 64; cnt++)
+ _saveNames[cnt][0] = '\0';
+ } else
+ for (uint8 cnt = 0; cnt < 64; cnt++)
+ _saveNames[cnt][0] = '\0';
+ delete inf;
+}
+
+int Control::displayMessage(const char *altButton, const char *message, ...) {
+ char buf[STRINGBUFLEN];
+ va_list va;
+
+ va_start(va, message);
+ vsnprintf(buf, STRINGBUFLEN, message, va);
+ va_end(va);
+
+ GUI::MessageDialog dialog(buf, "OK", altButton);
+ int result = dialog.runModal();
+ _mouse->setPointer(MSE_POINTER, 0);
+ return result;
+}
+
+void Control::writeSavegameDescriptions(void) {
+ Common::OutSaveFile *outf;
+ outf = _saveFileMan->openForSaving("SAVEGAME.INF");
+
+ if (!outf) {
+ // Display an error message, and do nothing
+ displayMessage(0, "Can't create SAVEGAME.INF in directory '%s'", _saveFileMan->getSavePath());
+ return;
+ }
+
+ // if the player accidently clicked the last slot and then deselected it again,
+ // we'd still have _saveFiles == 64, so get rid of the empty end.
+ while (strlen((char*)_saveNames[_saveFiles - 1]) == 0)
+ _saveFiles--;
+ for (uint8 cnt = 0; cnt < _saveFiles; cnt++) {
+ int len = strlen((char*)_saveNames[cnt]);
+ if (len > 0)
+ outf->write(_saveNames[cnt], len);
+ if (cnt < _saveFiles - 1)
+ outf->writeByte(10);
+ else
+ outf->writeByte(255);
+ }
+ outf->flush();
+ if (outf->ioFailed())
+ displayMessage(0, "Can't write to SAVEGAME.INF in directory '%s'. Device full?", _saveFileMan->getSavePath());
+ delete outf;
+}
+
+bool Control::savegamesExist(void) {
+ bool retVal = false;
+ Common::InSaveFile *inf;
+ inf = _saveFileMan->openForLoading("SAVEGAME.INF");
+ if (inf)
+ retVal = true;
+ delete inf;
+ return retVal;
+}
+
+void Control::showSavegameNames(void) {
+ for (uint8 cnt = 0; cnt < 8; cnt++) {
+ _buttons[cnt]->draw();
+ uint8 textMode = TEXT_LEFT_ALIGN;
+ uint16 ycoord = _saveButtons[cnt].y + 2;
+ uint8 str[40];
+ sprintf((char*)str, "%d. %s", cnt + _saveScrollPos + 1, _saveNames[cnt + _saveScrollPos]);
+ if (cnt + _saveScrollPos == _selectedSavegame) {
+ textMode |= TEXT_RED_FONT;
+ ycoord += 2;
+ if (_cursorVisible)
+ strcat((char*)str, "_");
+ }
+ renderText(str, _saveButtons[cnt].x + 6, ycoord, textMode);
+ }
+}
+
+void Control::saveNameSelect(uint8 id, bool saving) {
+ deselectSaveslots();
+ _buttons[id - BUTTON_SAVE_SELECT1]->setSelected(1);
+ uint8 num = (id - BUTTON_SAVE_SELECT1) + _saveScrollPos;
+ if (saving && (_selectedSavegame != 255)) // the player may have entered something, clear it again
+ strcpy((char*)_saveNames[_selectedSavegame], (char*)_oldName);
+ if (num < _saveFiles) {
+ _selectedSavegame = num;
+ strcpy((char*)_oldName, (char*)_saveNames[num]); // save for later
+ } else {
+ if (!saving)
+ _buttons[id - BUTTON_SAVE_SELECT1]->setSelected(0); // no save in slot, deselect it
+ else {
+ if (_saveFiles <= num)
+ _saveFiles = num + 1;
+ _selectedSavegame = num;
+ _oldName[0] = '\0';
+ }
+ }
+ if (_selectedSavegame < 255)
+ _cursorTick = 0;
+ showSavegameNames();
+}
+
+void Control::saveNameScroll(uint8 scroll, bool saving) {
+ uint16 maxScroll;
+ if (saving)
+ maxScroll = 64;
+ else
+ maxScroll = _saveFiles; // for loading, we can only scroll as far as there are savegames
+ if (scroll == BUTTON_SCROLL_UP_FAST) {
+ if (_saveScrollPos >= 8)
+ _saveScrollPos -= 8;
+ else
+ _saveScrollPos = 0;
+ } else if (scroll == BUTTON_SCROLL_UP_SLOW) {
+ if (_saveScrollPos >= 1)
+ _saveScrollPos--;
+ } else if (scroll == BUTTON_SCROLL_DOWN_SLOW) {
+ if (_saveScrollPos + 8 < maxScroll)
+ _saveScrollPos++;
+ } else if (scroll == BUTTON_SCROLL_DOWN_FAST) {
+ if (_saveScrollPos + 16 < maxScroll)
+ _saveScrollPos += 8;
+ else {
+ if (maxScroll >= 8)
+ _saveScrollPos = maxScroll - 8;
+ else
+ _saveScrollPos = 0;
+ }
+ }
+ _selectedSavegame = 255; // deselect savegame
+ deselectSaveslots();
+ showSavegameNames();
+}
+
+void Control::createButtons(const ButtonInfo *buttons, uint8 num) {
+ for (uint8 cnt = 0; cnt < num; cnt++) {
+ _buttons[cnt] = new ControlButton(buttons[cnt].x, buttons[cnt].y, buttons[cnt].resId, buttons[cnt].id, buttons[cnt].flag, _resMan, _screenBuf, _system);
+ _buttons[cnt]->draw();
+ }
+ _numButtons = num;
+}
+
+void Control::destroyButtons(void) {
+ for (uint8 cnt = 0; cnt < _numButtons; cnt++)
+ delete _buttons[cnt];
+ _numButtons = 0;
+}
+
+uint16 Control::getTextWidth(const uint8 *str) {
+ uint16 width = 0;
+ while (*str) {
+ width += FROM_LE_16(_resMan->fetchFrame(_font, *str - 32)->width) - 3;
+ str++;
+ }
+ return width;
+}
+
+void Control::renderText(const uint8 *str, uint16 x, uint16 y, uint8 mode) {
+ uint8 *font = _font;
+ if (mode & TEXT_RED_FONT) {
+ mode &= ~TEXT_RED_FONT;
+ font = _redFont;
+ }
+
+ if (mode == TEXT_RIGHT_ALIGN) // negative x coordinate means right-aligned.
+ x -= getTextWidth(str);
+ else if (mode == TEXT_CENTER)
+ x -= getTextWidth(str) / 2;
+
+ uint16 destX = x;
+ while (*str) {
+ uint8 *dst = _screenBuf + y * SCREEN_WIDTH + destX;
+
+ FrameHeader *chSpr = _resMan->fetchFrame(font, *str - 32);
+ uint8 *sprData = (uint8*)chSpr + sizeof(FrameHeader);
+ for (uint16 cnty = 0; cnty < FROM_LE_16(chSpr->height); cnty++) {
+ for (uint16 cntx = 0; cntx < FROM_LE_16(chSpr->width); cntx++) {
+ if (sprData[cntx])
+ dst[cntx] = sprData[cntx];
+ }
+ sprData += FROM_LE_16(chSpr->width);
+ dst += SCREEN_WIDTH;
+ }
+ destX += FROM_LE_16(chSpr->width) - 3;
+ str++;
+ }
+ _system->copyRectToScreen(_screenBuf + y * SCREEN_WIDTH + x, SCREEN_WIDTH, x, y, (destX - x) + 3, 28);
+}
+
+void Control::renderVolumeBar(uint8 id, uint8 volL, uint8 volR) {
+ uint16 destX = _volumeButtons[id].x + 20;
+ uint16 destY = _volumeButtons[id].y + 116;
+
+ for (uint8 chCnt = 0; chCnt < 2; chCnt++) {
+ uint8 vol = (chCnt == 0) ? volL : volR;
+ FrameHeader *frHead = _resMan->fetchFrame(_resMan->openFetchRes(SR_VLIGHT), (vol + 15) >> 4);
+ uint8 *destMem = _screenBuf + destY * SCREEN_WIDTH + destX;
+ uint8 *srcMem = (uint8*)frHead + sizeof(FrameHeader);
+ for (uint16 cnty = 0; cnty < FROM_LE_16(frHead->height); cnty++) {
+ memcpy(destMem, srcMem, FROM_LE_16(frHead->width));
+ srcMem += FROM_LE_16(frHead->width);
+ destMem += SCREEN_WIDTH;
+ }
+ _system->copyRectToScreen(_screenBuf + destY * SCREEN_WIDTH + destX, SCREEN_WIDTH, destX, destY, FROM_LE_16(frHead->width), FROM_LE_16(frHead->height));
+ _resMan->resClose(SR_VLIGHT);
+ destX += 32;
+ }
+}
+
+void Control::saveGameToFile(uint8 slot) {
+ char fName[15];
+ uint16 cnt;
+ sprintf(fName, "SAVEGAME.%03d", slot);
+ uint16 liveBuf[TOTAL_SECTIONS];
+ Common::OutSaveFile *outf;
+ outf = _saveFileMan->openForSaving(fName);
+ if (!outf) {
+ // Display an error message and do nothing
+ displayMessage(0, "Unable to create file '%s' in directory '%s'", fName, _saveFileMan->getSavePath());
+ return;
+ }
+
+ _objMan->saveLiveList(liveBuf);
+ for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
+ outf->writeUint16LE(liveBuf[cnt]);
+
+ Object *cpt = _objMan->fetchObject(PLAYER);
+ Logic::_scriptVars[CHANGE_DIR] = cpt->o_dir;
+ Logic::_scriptVars[CHANGE_X] = cpt->o_xcoord;
+ Logic::_scriptVars[CHANGE_Y] = cpt->o_ycoord;
+ Logic::_scriptVars[CHANGE_STANCE] = STAND;
+ Logic::_scriptVars[CHANGE_PLACE] = cpt->o_place;
+
+ for (cnt = 0; cnt < NUM_SCRIPT_VARS; cnt++)
+ outf->writeUint32LE(Logic::_scriptVars[cnt]);
+
+ uint32 playerSize = (sizeof(Object) - 12000) / 4;
+ uint32 *playerRaw = (uint32*)cpt;
+ for (uint32 cnt2 = 0; cnt2 < playerSize; cnt2++)
+ outf->writeUint32LE(playerRaw[cnt2]);
+ outf->flush();
+ if (outf->ioFailed())
+ displayMessage(0, "Couldn't write to file '%s' in directory '%s'. Device full?", fName, _saveFileMan->getSavePath());
+ delete outf;
+}
+
+bool Control::restoreGameFromFile(uint8 slot) {
+ char fName[15];
+ uint16 cnt;
+ sprintf(fName, "SAVEGAME.%03d", slot);
+ Common::InSaveFile *inf;
+ inf = _saveFileMan->openForLoading(fName);
+ if (!inf) {
+ // Display an error message, and do nothing
+ displayMessage(0, "Can't open file '%s' in directory '%s'", fName, _saveFileMan->getSavePath());
+ return false;
+ }
+
+ _restoreBuf = (uint8*)malloc(
+ TOTAL_SECTIONS * 2 +
+ NUM_SCRIPT_VARS * 4 +
+ (sizeof(Object) - 12000));
+
+ uint16 *liveBuf = (uint16*)_restoreBuf;
+ uint32 *scriptBuf = (uint32*)(_restoreBuf + 2 * TOTAL_SECTIONS);
+ uint32 *playerBuf = (uint32*)(_restoreBuf + 2 * TOTAL_SECTIONS + 4 * NUM_SCRIPT_VARS);
+
+ for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
+ liveBuf[cnt] = inf->readUint16LE();
+
+ for (cnt = 0; cnt < NUM_SCRIPT_VARS; cnt++)
+ scriptBuf[cnt] = inf->readUint32LE();
+
+ uint32 playerSize = (sizeof(Object) - 12000) / 4;
+ for (uint32 cnt2 = 0; cnt2 < playerSize; cnt2++)
+ playerBuf[cnt2] = inf->readUint32LE();
+
+ if (inf->ioFailed()) {
+ displayMessage(0, "Can't read from file '%s' in directory '%s'", fName, _saveFileMan->getSavePath());
+ delete inf;
+ free(_restoreBuf);
+ _restoreBuf = NULL;
+ return false;
+ }
+ delete inf;
+ return true;
+}
+
+void Control::doRestore(void) {
+ uint8 *bufPos = _restoreBuf;
+ _objMan->loadLiveList((uint16*)bufPos);
+ bufPos += TOTAL_SECTIONS * 2;
+ for (uint16 cnt = 0; cnt < NUM_SCRIPT_VARS; cnt++) {
+ Logic::_scriptVars[cnt] = *(uint32*)bufPos;
+ bufPos += 4;
+ }
+ uint32 playerSize = (sizeof(Object) - 12000) / 4;
+ uint32 *playerRaw = (uint32*)_objMan->fetchObject(PLAYER);
+ Object *cpt = _objMan->fetchObject(PLAYER);
+ for (uint32 cnt2 = 0; cnt2 < playerSize; cnt2++) {
+ *playerRaw = *(uint32*)bufPos;
+ playerRaw++;
+ bufPos += 4;
+ }
+ free(_restoreBuf);
+ Logic::_scriptVars[CHANGE_DIR] = cpt->o_dir;
+ Logic::_scriptVars[CHANGE_X] = cpt->o_xcoord;
+ Logic::_scriptVars[CHANGE_Y] = cpt->o_ycoord;
+ Logic::_scriptVars[CHANGE_STANCE] = STAND;
+ Logic::_scriptVars[CHANGE_PLACE] = cpt->o_place;
+ SwordEngine::_systemVars.justRestoredGame = 1;
+ if (SwordEngine::_systemVars.isDemo)
+ Logic::_scriptVars[PLAYINGDEMO] = 1;
+}
+
+void Control::delay(uint32 msecs) {
+ OSystem::Event event;
+
+ uint32 now = _system->getMillis();
+ uint32 endTime = now + msecs;
+ _keyPressed = 0; //reset
+ _mouseState = 0;
+
+ do {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+
+ // Make sure backspace works right (this fixes a small issue on OS X)
+ if (event.kbd.keycode == 8)
+ _keyPressed = 8;
+ else
+ _keyPressed = (byte)event.kbd.ascii;
+ _keyRepeatTime = now + kKeyRepeatInitialDelay;
+ _keyRepeat = _keyPressed;
+ // we skip the rest of the delay and return immediately
+ // to handle keyboard input
+ return;
+ case OSystem::EVENT_KEYUP:
+ _keyRepeatTime = 0;
+ _keyRepeat = 0;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ _mouseDown = true;
+ _mouseState |= BS1L_BUTTON_DOWN;
+#if defined(_WIN32_WCE) || defined(PALMOS_MODE)
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+#endif
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _mouseDown = false;
+ _mouseState |= BS1L_BUTTON_UP;
+ break;
+ case OSystem::EVENT_WHEELUP:
+ _mouseDown = false;
+ _mouseState |= BS1_WHEEL_UP;
+ break;
+ case OSystem::EVENT_WHEELDOWN:
+ _mouseDown = false;
+ _mouseState |= BS1_WHEEL_DOWN;
+ break;
+ case OSystem::EVENT_QUIT:
+ SwordEngine::_systemVars.engineQuit = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (_keyRepeatTime && now > _keyRepeatTime) {
+ _keyRepeatTime += kKeyRepeatSustainDelay;
+ _keyPressed = _keyRepeat;
+ }
+
+ _system->delayMillis(10);
+ } while (_system->getMillis() < endTime);
+}
+
+const ButtonInfo Control::_deathButtons[3] = {
+ {250, 224 + 40, SR_BUTTON, BUTTON_RESTORE_PANEL, 0 },
+ {250, 260 + 40, SR_BUTTON, BUTTON_RESTART, kButtonOk },
+ {250, 296 + 40, SR_BUTTON, BUTTON_QUIT, kButtonCancel }
+};
+
+const ButtonInfo Control::_panelButtons[7] = {
+ {145, 188 + 40, SR_BUTTON, BUTTON_SAVE_PANEL, 0 },
+ {145, 224 + 40, SR_BUTTON, BUTTON_RESTORE_PANEL, 0 },
+ {145, 260 + 40, SR_BUTTON, BUTTON_RESTART, 0 },
+ {145, 296 + 40, SR_BUTTON, BUTTON_QUIT, kButtonCancel },
+ {475, 188 + 40, SR_BUTTON, BUTTON_VOLUME_PANEL, 0 },
+ {475, 224 + 40, SR_TEXT_BUTTON, BUTTON_TEXT, 0 },
+ {475, 332 + 40, SR_BUTTON, BUTTON_DONE, kButtonOk }
+};
+
+const ButtonInfo Control::_saveButtons[16] = {
+ {114, 32 + 40, SR_SLAB1, BUTTON_SAVE_SELECT1, 0 },
+ {114, 68 + 40, SR_SLAB2, BUTTON_SAVE_SELECT2, 0 },
+ {114, 104 + 40, SR_SLAB3, BUTTON_SAVE_SELECT3, 0 },
+ {114, 140 + 40, SR_SLAB4, BUTTON_SAVE_SELECT4, 0 },
+ {114, 176 + 40, SR_SLAB1, BUTTON_SAVE_SELECT5, 0 },
+ {114, 212 + 40, SR_SLAB2, BUTTON_SAVE_SELECT6, 0 },
+ {114, 248 + 40, SR_SLAB3, BUTTON_SAVE_SELECT7, 0 },
+ {114, 284 + 40, SR_SLAB4, BUTTON_SAVE_SELECT8, 0 },
+
+ {516, 25 + 40, SR_BUTUF, BUTTON_SCROLL_UP_FAST, 0 },
+ {516, 45 + 40, SR_BUTUS, BUTTON_SCROLL_UP_SLOW, 0 },
+ {516, 289 + 40, SR_BUTDS, BUTTON_SCROLL_DOWN_SLOW, 0 },
+ {516, 310 + 40, SR_BUTDF, BUTTON_SCROLL_DOWN_FAST, 0 },
+
+ {125, 338 + 40, SR_BUTTON, BUTTON_SAVE_RESTORE_OKAY, kButtonOk},
+ {462, 338 + 40, SR_BUTTON, BUTTON_SAVE_CANCEL, kButtonCancel }
+};
+
+const ButtonInfo Control::_volumeButtons[4] = {
+ { 478, 338 + 40, SR_BUTTON, BUTTON_MAIN_PANEL, kButtonOk },
+ { 138, 135, SR_VKNOB, 0, 0 },
+ { 273, 135, SR_VKNOB, 0, 0 },
+ { 404, 135, SR_VKNOB, 0, 0 },
+};
+
+const uint8 Control::_languageStrings[8 * 20][43] = {
+ // BS1_ENGLISH:
+ "PAUSED",
+ "PLEASE INSERT CD-",
+ "THEN PRESS A KEY",
+ "INCORRECT CD",
+ "Save",
+ "Restore",
+ "Restart",
+ "Start",
+ "Quit",
+ "Speed",
+ "Volume",
+ "Text",
+ "Done",
+ "OK",
+ "Cancel",
+ "Music",
+ "Speech",
+ "Fx",
+ "The End",
+ "DRIVE FULL!",
+// BS1_FRENCH:
+ "PAUSE",
+ "INS\xC9REZ LE CD-",
+ "ET APPUYES SUR UNE TOUCHE",
+ "CD INCORRECT",
+ "Sauvegarder",
+ "Recharger",
+ "Recommencer",
+ "Commencer",
+ "Quitter",
+ "Vitesse",
+ "Volume",
+ "Texte",
+ "Termin\xE9",
+ "OK",
+ "Annuler",
+ "Musique",
+ "Voix",
+ "Fx",
+ "Fin",
+ "DISQUE PLEIN!",
+//BS1_GERMAN:
+ "PAUSE",
+ "BITTE LEGEN SIE CD-",
+ "EIN UND DR\xDC CKEN SIE EINE BELIEBIGE TASTE",
+ "FALSCHE CD",
+ "Speichern",
+ "Laden",
+ "Neues Spiel",
+ "Start",
+ "Beenden",
+ "Geschwindigkeit",
+ "Lautst\xE4rke",
+ "Text",
+ "Fertig",
+ "OK",
+ "Abbrechen",
+ "Musik",
+ "Sprache",
+ "Fx",
+ "Ende",
+ "DRIVE FULL!",
+//BS1_ITALIAN:
+ "PAUSA",
+ "INSERITE IL CD-",
+ "E PREMETE UN TASTO",
+ "CD ERRATO",
+ "Salva",
+ "Ripristina",
+ "Ricomincia",
+ "Inizio",
+ "Esci",
+ "Velocit\xE0",
+ "Volume",
+ "Testo",
+ "Fatto",
+ "OK",
+ "Annula",
+ "Musica",
+ "Parlato",
+ "Fx",
+ "Fine",
+ "DISCO PIENO!",
+//BS1_SPANISH:
+ "PAUSA",
+ "POR FAVOR INTRODUCE EL CD-",
+ "Y PULSA UNA TECLA",
+ "CD INCORRECTO",
+ "Guardar",
+ "Recuperar",
+ "Reiniciar",
+ "Empezar",
+ "Abandonar",
+ "Velocidad",
+ "Volumen",
+ "Texto",
+ "Hecho",
+ "OK",
+ "Cancelar",
+ "M\xFAsica",
+ "Di\xE1logo",
+ "Fx",
+ "Fin",
+ "DISCO LLENO",
+// BS1_CZECH:
+ "\xAC\x41S SE ZASTAVIL",
+ "VLO\xA6TE DO MECHANIKY CD DISK",
+ "PAK STISKN\xB7TE LIBOVOLNOU KL\xB5VESU",
+ "TO NEBUDE TO SPR\xB5VN\x90 CD",
+ "Ulo\xA7it pozici",
+ "Nahr\xA0t pozici",
+ "Za\x9F\xA1t znovu",
+ "Start",
+ "Ukon\x9Fit hru",
+ "Rychlost",
+ "Hlasitost",
+ "Titulky",
+ "Souhlas\xA1m",
+ "Ano",
+ "Ne",
+ "Hudba",
+ "Mluven, slovo",
+ "Zvuky",
+ "Konec",
+ "Disk pln\xEC",
+//BS1_PORTUGESE:
+ "PAUSA",
+ "FAVOR INSERIR CD",
+ "E DIGITAR UMA TECLA",
+ "CD INCORRETO",
+ "Salvar",
+ "Restaurar",
+ "Reiniciar",
+ "Iniciar",
+ "Sair",
+ "Velocidade",
+ "Volume",
+ "Texto",
+ "Feito",
+ "OK",
+ "Cancelar",
+ "M\xFAsica",
+ "Voz",
+ "Efeitos",
+ "Fim",
+ "UNIDADE CHEIA!",
+};
+
+} // End of namespace Sword1
diff --git a/engines/sword1/control.h b/engines/sword1/control.h
new file mode 100644
index 0000000000..b1f81165be
--- /dev/null
+++ b/engines/sword1/control.h
@@ -0,0 +1,152 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSCONTROL_H
+#define BSCONTROL_H
+
+#include "common/scummsys.h"
+#include "sword1/sworddefs.h"
+
+class OSystem;
+namespace Common {
+ class SaveFileManager;
+}
+
+namespace Sword1 {
+
+class ObjectMan;
+class ResMan;
+class Mouse;
+class Music;
+class Sound;
+
+#define MAX_BUTTONS 16
+
+#define CONTROL_NOTHING_DONE 0
+#define CONTROL_GAME_RESTORED 1
+#define CONTROL_RESTART_GAME 2
+
+class ControlButton {
+public:
+ ControlButton(uint16 x, uint16 y, uint32 resId, uint8 id, uint8 flag, ResMan *pResMan, uint8 *screenBuf, OSystem *system);
+ ~ControlButton(void);
+ void draw(void);
+ bool wasClicked(uint16 mouseX, uint16 mouseY);
+ void setSelected(uint8 selected);
+ bool isSaveslot(void);
+ uint8 _id;
+ uint8 _flag;
+private:
+ int _frameIdx;
+ uint16 _x, _y;
+ uint16 _width, _height;
+ uint32 _resId;
+ ResMan *_resMan;
+ uint8 *_dstBuf;
+ OSystem *_system;
+};
+
+enum {
+ kButtonOk = 1,
+ kButtonCancel = 2
+};
+
+struct ButtonInfo {
+ uint16 x, y;
+ uint32 resId, id;
+ uint8 flag;
+};
+
+class Control {
+public:
+ Control(Common::SaveFileManager *saveFileMan, ResMan *pResMan, ObjectMan *pObjMan, OSystem *system, Mouse *pMouse, Sound *pSound, Music *pMusic);
+ uint8 runPanel(void);
+ void doRestore(void);
+ void askForCd(void);
+ bool savegamesExist(void);
+private:
+ int displayMessage(const char *altButton, const char *message, ...);
+
+ void saveGameToFile(uint8 slot);
+ bool restoreGameFromFile(uint8 slot);
+ void readSavegameDescriptions(void);
+ void writeSavegameDescriptions(void);
+ void showSavegameNames(void);
+ void deselectSaveslots(void);
+ uint8 *_restoreBuf;
+ uint8 _saveFiles;
+ uint8 _saveScrollPos;
+ uint8 _selectedSavegame;
+ uint8 _saveNames[64][32];
+ uint8 _oldName[32];
+ uint8 _cursorTick;
+ bool _cursorVisible;
+
+ uint8 getClicks(uint8 mode, uint8 *retVal);
+ uint8 handleButtonClick(uint8 id, uint8 mode, uint8 *retVal);
+ void handleVolumeClicks(void);
+ void changeVolume(uint8 id, uint8 action);
+
+ void setupMainPanel(void);
+ void setupSaveRestorePanel(bool saving);
+ void setupVolumePanel(void);
+ bool getConfirm(const uint8 *title);
+
+ void saveNameScroll(uint8 scroll, bool saving);
+ void saveNameSelect(uint8 id, bool saving);
+ bool saveToFile(void);
+ bool restoreFromFile(void);
+ bool keyAccepted(uint8 key);
+ void handleSaveKey(uint8 key);
+
+ void renderVolumeBar(uint8 id, uint8 volL, uint8 volR);
+ uint16 getTextWidth(const uint8 *str);
+ void renderText(const uint8 *str, uint16 x, uint16 y, uint8 mode);
+ uint8 _numButtons;
+ uint8 _selectedButton;
+ void createButtons(const ButtonInfo *buttons, uint8 num);
+ void destroyButtons(void);
+ ControlButton *_buttons[MAX_BUTTONS];
+ static const ButtonInfo _deathButtons[3], _panelButtons[7], _saveButtons[16], _volumeButtons[4];
+ static const uint8 _languageStrings[8 * 20][43];
+ const uint8 (*_lStrings)[43];
+ Common::SaveFileManager *_saveFileMan;
+ ObjectMan *_objMan;
+ ResMan *_resMan;
+ OSystem *_system;
+ Mouse *_mouse;
+ Music *_music;
+ Sound *_sound;
+ uint8 *_font, *_redFont;
+ uint8 *_screenBuf;
+ uint8 _keyPressed;
+ uint8 _keyRepeat;
+ uint32 _keyRepeatTime;
+ void delay(uint32 msecs);
+ uint16 _mouseX, _mouseY, _mouseState;
+ bool _mouseDown;
+};
+
+} // End of namespace Sword1
+
+#endif //BSCONTROL_H
+
diff --git a/engines/sword1/credits.cpp b/engines/sword1/credits.cpp
new file mode 100644
index 0000000000..117430b96f
--- /dev/null
+++ b/engines/sword1/credits.cpp
@@ -0,0 +1,346 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "sword1/credits.h"
+#include "sword1/screen.h"
+#include "sword1/sword1.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+#include "common/file.h"
+#include "common/util.h"
+#include "common/system.h"
+
+
+#define CREDITS_X 480
+#define CREDITS_Y 300
+#define BUFSIZE_Y 640
+
+#define START_X ((640 - CREDITS_X) / 2)
+#define START_Y ((480 - CREDITS_Y) / 2)
+
+#define SCROLL_TIMING (2000 / 59) // 29.5 frames per second
+
+#define LOGO_FADEUP_TIME (133 * 1000)
+#define LOGO_FADEDOWN_TIME (163 * 1000)
+
+namespace Sword1 {
+
+enum {
+ FONT_PAL = 0,
+ FONT,
+ TEXT,
+ REVO_PAL,
+ REVO_LOGO,
+ F_EOF
+};
+
+enum {
+ FNT_LFT = 0, // left column
+ FNT_RGT, // right column
+ FNT_CEN, // centered
+ FNT_BIG = 64, // big font
+ FNT_EOL = 128, // linebreak
+ FNT_EOB = 255 // end of textblock
+};
+
+
+CreditsPlayer::CreditsPlayer(OSystem *pSystem, Audio::Mixer *pMixer) {
+ _system = pSystem;
+ _mixer = pMixer;
+ _smlFont = _bigFont = NULL;
+}
+
+bool spaceInBuf(uint16 blitSta, uint16 blitEnd, uint16 renderDest) {
+ if (blitEnd > blitSta) {
+ if ((renderDest > blitEnd) || (renderDest + 15 < blitSta))
+ return true;
+ } else {
+ if ((renderDest > blitEnd) && (renderDest + 15 < blitSta))
+ return true;
+ }
+ return false;
+}
+
+void CreditsPlayer::play(void) {
+ AudioStream *bgSoundStream = AudioStream::openStreamFile("credits");
+ if (bgSoundStream == NULL) {
+ warning("\"credits.ogg\" not found, skipping credits sequence");
+ return;
+ }
+ ArcFile credFile;
+ if (!credFile.open("credits.dat")) {
+ warning("\"credits.dat\" not found, skipping credits sequence");
+ return;
+ }
+
+ uint8 *palSrc = credFile.fetchFile(FONT_PAL, &_palLen);
+ for (uint32 cnt = 0; cnt < _palLen; cnt++)
+ _palette[(cnt / 3) * 4 + cnt % 3] = palSrc[cnt];
+ _palLen /= 3;
+
+ generateFonts(&credFile);
+
+ uint8 *textData = credFile.fetchFile(TEXT);
+ textData += READ_LE_UINT32(textData + SwordEngine::_systemVars.language * 4);
+
+ uint8 *screenBuf = (uint8*)malloc(CREDITS_X * BUFSIZE_Y);
+ memset(screenBuf, 0, CREDITS_X * BUFSIZE_Y);
+ _system->copyRectToScreen(screenBuf, 640, 0, 0, 640, 480);
+ _system->setPalette(_palette, 0, _palLen);
+ _system->updateScreen();
+
+ // everything's initialized, time to render and show the credits.
+ Audio::SoundHandle bgSound;
+ _mixer->playInputStream(Audio::Mixer::kMusicSoundType, &bgSound, bgSoundStream, 0);
+
+ int relDelay = 0;
+ uint16 scrollY = 0;
+ uint16 renderY = BUFSIZE_Y / 2;
+ uint16 clearY = 0xFFFF;
+ bool clearLine = false;
+ while (((*textData != FNT_EOB) || (scrollY != renderY)) && !SwordEngine::_systemVars.engineQuit) {
+ if ((int32)_mixer->getSoundElapsedTime(bgSound) - relDelay < (SCROLL_TIMING * 2)) { // sync to audio
+ if (scrollY < BUFSIZE_Y - CREDITS_Y)
+ _system->copyRectToScreen(screenBuf + scrollY * CREDITS_X, CREDITS_X, START_X, START_Y, CREDITS_X, CREDITS_Y);
+ else {
+ _system->copyRectToScreen(screenBuf + scrollY * CREDITS_X, CREDITS_X, START_X, START_Y, CREDITS_X, BUFSIZE_Y - scrollY);
+ _system->copyRectToScreen(screenBuf, CREDITS_X, START_X, START_Y + BUFSIZE_Y - scrollY, CREDITS_X, CREDITS_Y - (BUFSIZE_Y - scrollY));
+ }
+ _system->updateScreen();
+ } else
+ warning("frame skipped");
+
+ while (spaceInBuf(scrollY, (scrollY + CREDITS_Y) % BUFSIZE_Y, renderY) && (*textData != FNT_EOB)) {
+ if (*textData & FNT_EOL) {
+ renderY = (renderY + 16) % BUFSIZE_Y; // linebreak
+ clearLine = true;
+ *textData &= ~FNT_EOL;
+ }
+ if (spaceInBuf(scrollY, (scrollY + CREDITS_Y) % BUFSIZE_Y, renderY)) {
+ if (clearLine)
+ memset(screenBuf + renderY * CREDITS_X, 0, 16 * CREDITS_X);
+ clearLine = false;
+ renderLine(screenBuf, textData + 1, renderY, *textData);
+ if (*textData & FNT_BIG)
+ renderY += 16;
+ while (*++textData != 0) // search for the start of next string
+ ;
+ textData++;
+ }
+ if (*textData == FNT_EOB)
+ clearY = renderY;
+ }
+ if ((*textData == FNT_EOB) && spaceInBuf(scrollY, (scrollY + CREDITS_Y) % BUFSIZE_Y, clearY)) {
+ memset(screenBuf + clearY * CREDITS_X, 0, 16 * CREDITS_X);
+ clearY = (clearY + 16) % BUFSIZE_Y;
+ }
+
+ relDelay += SCROLL_TIMING;
+ delay(relDelay - (int32)_mixer->getSoundElapsedTime(bgSound));
+ scrollY = (scrollY + 1) % BUFSIZE_Y;
+ }
+ free(_smlFont);
+ free(_bigFont);
+ _smlFont = _bigFont = NULL;
+ free(screenBuf);
+
+ // credits done, now show the revolution logo
+ uint8 *revoBuf = credFile.decompressFile(REVO_LOGO);
+ uint8 *revoPal = credFile.fetchFile(REVO_PAL, &_palLen);
+ _palLen /= 3;
+ while ((_mixer->getSoundElapsedTime(bgSound) < LOGO_FADEUP_TIME) && !SwordEngine::_systemVars.engineQuit) {
+ _system->updateScreen();
+ delay(100);
+ }
+ memset(_palette, 0, 256 * 4);
+ _system->setPalette(_palette, 0, 256);
+ _system->copyRectToScreen(revoBuf, 480, START_X, START_Y, CREDITS_X, CREDITS_Y);
+ _system->updateScreen();
+
+ fadePalette(revoPal, true, _palLen);
+ while ((_mixer->getSoundElapsedTime(bgSound) < LOGO_FADEDOWN_TIME) && !SwordEngine::_systemVars.engineQuit) {
+ _system->updateScreen();
+ delay(100);
+ }
+ fadePalette(revoPal, false, _palLen);
+ delay(3000);
+
+ if (SwordEngine::_systemVars.engineQuit)
+ _mixer->stopAll();
+ free(revoBuf);
+}
+
+void CreditsPlayer::fadePalette(uint8 *srcPal, bool fadeup, uint16 len) {
+ int8 fadeDir = fadeup ? 1 : -1;
+ int fadeStart = fadeup ? 0 : 12;
+
+ int relDelay = _system->getMillis();
+ for (int fadeStep = fadeStart; (fadeStep >= 0) && (fadeStep <= 12) && !SwordEngine::_systemVars.engineQuit; fadeStep += fadeDir) {
+ for (uint16 cnt = 0; cnt < len * 3; cnt++)
+ _palette[(cnt / 3) * 4 + (cnt % 3)] = (srcPal[cnt] * fadeStep) / 12;
+ _system->setPalette(_palette, 0, 256);
+ _system->updateScreen();
+ relDelay += 1000 / 12;
+ delay(relDelay - _system->getMillis());
+ }
+}
+
+void CreditsPlayer::renderLine(uint8 *screenBuf, uint8 *line, uint16 yBufPos, uint8 flags) {
+ uint8 *font;
+ uint16 fntSize = 16;
+ if (flags & FNT_BIG) {
+ font = _bigFont;
+ fntSize = 32;
+ flags &= ~FNT_BIG;
+ } else
+ font = _smlFont;
+
+ uint16 width = getWidth(font, line);
+ uint16 xBufPos = (flags == FNT_CEN) ? (CREDITS_X - width) / 2 : ((flags == FNT_LFT) ? (234 - width) : 255);
+ uint8 *bufDest = screenBuf + yBufPos * CREDITS_X + xBufPos;
+ while (*line) {
+ uint8 *chrSrc = font + _numChars + (*line - 1) * fntSize * fntSize;
+ for (uint16 cnty = 0; cnty < fntSize; cnty++) {
+ for (uint16 cntx = 0; cntx < fntSize; cntx++)
+ bufDest[cnty * CREDITS_X + cntx] = chrSrc[cntx];
+ chrSrc += fntSize;
+ }
+ bufDest += font[*line++ - 1];
+ }
+}
+
+uint16 CreditsPlayer::getWidth(uint8 *font, uint8 *line) {
+ uint16 width = 0;
+ while (*line)
+ width += font[*line++ - 1];
+ return width;
+}
+
+void CreditsPlayer::generateFonts(ArcFile *arcFile) {
+ _bigFont = arcFile->decompressFile(FONT);
+ _numChars = *_bigFont;
+ memmove(_bigFont, _bigFont + 1, _numChars * (32 * 32 + 1));
+ _smlFont = (uint8*)malloc(_numChars * (32 * 32 + 1));
+ uint8 *src = _bigFont + _numChars;
+ uint8 *dst = _smlFont + _numChars;
+ for (uint16 cnt = 0; cnt < _numChars; cnt++) {
+ _smlFont[cnt] = (_bigFont[cnt]++ + 1) / 2; // width table
+ for (uint16 cnty = 0; cnty < 16; cnty++) {
+ for (uint16 cntx = 0; cntx < 16; cntx++) {
+ uint8 resR = (uint8)((_palette[src[0] * 4 + 0] + _palette[src[1] * 4 + 0] + _palette[src[32] * 4 + 0] + _palette[src[33] * 4 + 0]) >> 2);
+ uint8 resG = (uint8)((_palette[src[0] * 4 + 1] + _palette[src[1] * 4 + 1] + _palette[src[32] * 4 + 1] + _palette[src[33] * 4 + 1]) >> 2);
+ uint8 resB = (uint8)((_palette[src[0] * 4 + 2] + _palette[src[1] * 4 + 2] + _palette[src[32] * 4 + 2] + _palette[src[33] * 4 + 2]) >> 2);
+ *dst++ = getPalIdx(resR, resG, resB);
+ src += 2;
+ }
+ src += 32;
+ }
+ }
+}
+
+uint8 CreditsPlayer::getPalIdx(uint8 r, uint8 g, uint8 b) {
+ for (uint16 cnt = 0; cnt < _palLen; cnt++)
+ if ((_palette[cnt * 4 + 0] == r) && (_palette[cnt * 4 + 1] == g) && (_palette[cnt * 4 + 2] == b))
+ return (uint8)cnt;
+ assert(_palLen < 256);
+ _palette[_palLen * 4 + 0] = r;
+ _palette[_palLen * 4 + 1] = g;
+ _palette[_palLen * 4 + 2] = b;
+ return (uint8)_palLen++;
+}
+
+void CreditsPlayer::delay(int msecs) {
+
+ OSystem::Event event;
+ uint32 start = _system->getMillis();
+ do {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_QUIT:
+ SwordEngine::_systemVars.engineQuit = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (msecs > 0)
+ _system->delayMillis(10);
+
+ } while ((_system->getMillis() < start + msecs) && !SwordEngine::_systemVars.engineQuit);
+}
+
+ArcFile::ArcFile(void) {
+ _buf = NULL;
+}
+
+ArcFile::~ArcFile(void) {
+ if (_buf)
+ free(_buf);
+}
+
+bool ArcFile::open(const char *name) {
+ Common::File arc;
+ if (!arc.open(name))
+ return false;
+ _bufPos = _buf = (uint8*)malloc(arc.size());
+ arc.read(_buf, arc.size());
+ arc.close();
+ return true;
+}
+
+void ArcFile::enterPath(uint32 id) {
+ _bufPos += READ_LE_UINT32(_bufPos + id * 4);
+}
+
+uint8 *ArcFile::fetchFile(uint32 fileId, uint32 *size) {
+ if (size)
+ *size = READ_LE_UINT32(_bufPos + (fileId + 1) * 4) - READ_LE_UINT32(_bufPos + fileId * 4);
+ return _bufPos + READ_LE_UINT32(_bufPos + fileId * 4);
+}
+
+uint8 *ArcFile::decompressFile(uint32 fileId) {
+ uint32 size;
+ uint8 *srcBuf = fetchFile(fileId, &size);
+ uint8 *dstBuf = (uint8*)malloc(READ_LE_UINT32(srcBuf));
+ uint8 *srcPos = srcBuf + 4;
+ uint8 *dstPos = dstBuf;
+ while (srcPos < srcBuf + size) {
+ uint16 len = READ_LE_UINT16(srcPos);
+ memset(dstPos, 0, len);
+ dstPos += len;
+ srcPos += 2;
+ if (srcPos < srcBuf + size) {
+ len = *srcPos++;
+ memcpy(dstPos, srcPos, len);
+ dstPos += len;
+ srcPos += len;
+ }
+ }
+ return dstBuf;
+}
+
+} // end of namespace Sword1
diff --git a/engines/sword1/credits.h b/engines/sword1/credits.h
new file mode 100644
index 0000000000..a964744f22
--- /dev/null
+++ b/engines/sword1/credits.h
@@ -0,0 +1,72 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BS1CREDITS_H
+#define BS1CREDITS_H
+
+#include "common/util.h"
+
+namespace Audio {
+ class Mixer;
+}
+class OSystem;
+
+namespace Sword1 {
+
+class ArcFile {
+public:
+ ArcFile(void);
+ ~ArcFile(void);
+ bool open(const char *name);
+ uint8 *fetchFile(uint32 fileId, uint32 *size = NULL);
+ uint8 *decompressFile(uint32 fileId);
+ void enterPath(uint32 id);
+ void backToRoot(void) { _bufPos = _buf; };
+private:
+ uint8 *_bufPos;
+ uint8 *_buf;
+};
+
+class CreditsPlayer {
+public:
+ CreditsPlayer(OSystem *pSystem, Audio::Mixer *pMixer);
+ void play(void);
+private:
+ void generateFonts(ArcFile *arcFile);
+ void renderLine(uint8 *screenBuf, uint8 *line, uint16 yBufPos, uint8 flags);
+ void fadePalette(uint8 *srcPal, bool fadeup, uint16 len);
+ void delay(int msecs);
+ uint16 getWidth(uint8 *font, uint8 *line);
+ uint8 getPalIdx(uint8 r, uint8 g, uint8 b);
+ uint8 _palette[256 * 4];
+ uint32 _palLen;
+ uint8 _numChars;
+
+ OSystem *_system;
+ Audio::Mixer *_mixer;
+
+ uint8 *_smlFont, *_bigFont;
+};
+
+} // end of namespace Sword1
+
+#endif // BS1CREDITS_H
diff --git a/engines/sword1/debug.cpp b/engines/sword1/debug.cpp
new file mode 100644
index 0000000000..d5ef5f87da
--- /dev/null
+++ b/engines/sword1/debug.cpp
@@ -0,0 +1,140 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "sword1/debug.h"
+
+namespace Sword1 {
+
+void Debug::interpretScript(uint32 id, uint32 level, uint32 script, uint32 pc) {
+ debug(8, "\nInterpreting %d@%d: script %X from %X", id, level, script, pc);
+}
+
+void Debug::callMCode(uint32 mcodeNum, uint32 paramCount, int32 a, int32 b, int32 c, int32 d, int32 e, int32 f) {
+ debug(9, "mcode: %s(%d, %d, %d, %d, %d, %d) [%d]", _mCodeNames[mcodeNum], a, b, c, d, e, f, paramCount);
+}
+
+const char Debug::_mCodeNames[100][35] = {
+ "fnBackground",
+ "fnForeground",
+ "fnSort",
+ "fnNoSprite",
+ "fnMegaSet",
+ "fnAnim",
+ "fnSetFrame",
+ "fnFullAnim",
+ "fnFullSetFrame",
+ "fnFadeDown",
+ "fnFadeUp",
+ "fnCheckFade",
+ "fnSetSpritePalette",
+ "fnSetWholePalette",
+ "fnSetFadeTargetPalette",
+ "fnSetPaletteToFade",
+ "fnSetPaletteToCut",
+ "fnPlaySequence",
+ "fnIdle",
+ "fnPause",
+ "fnPauseSeconds",
+ "fnQuit",
+ "fnKillId",
+ "fnSuicide",
+ "fnNewScript",
+ "fnSubScript",
+ "fnRestartScript",
+ "fnSetBookmark",
+ "fnGotoBookmark",
+ "fnSendSync",
+ "fnWaitSync",
+ "cfnClickInteract",
+ "cfnSetScript",
+ "cfnPresetScript",
+ "fnInteract",
+ "fnIssueEvent",
+ "fnCheckForEvent",
+ "fnWipeHands",
+ "fnISpeak",
+ "fnTheyDo",
+ "fnTheyDoWeWait",
+ "fnWeWait",
+ "fnChangeSpeechText",
+ "fnTalkError",
+ "fnStartTalk",
+ "fnCheckForTextLine",
+ "fnAddTalkWaitStatusBit",
+ "fnRemoveTalkWaitStatusBit",
+ "fnNoHuman",
+ "fnAddHuman",
+ "fnBlankMouse",
+ "fnNormalMouse",
+ "fnLockMouse",
+ "fnUnlockMouse",
+ "fnSetMousePointer",
+ "fnSetMouseLuggage",
+ "fnMouseOn",
+ "fnMouseOff",
+ "fnChooser",
+ "fnEndChooser",
+ "fnStartMenu",
+ "fnEndMenu",
+ "cfnReleaseMenu",
+ "fnAddSubject",
+ "fnAddObject",
+ "fnRemoveObject",
+ "fnEnterSection",
+ "fnLeaveSection",
+ "fnChangeFloor",
+ "fnWalk",
+ "fnTurn",
+ "fnStand",
+ "fnStandAt",
+ "fnFace",
+ "fnFaceXy",
+ "fnIsFacing",
+ "fnGetTo",
+ "fnGetToError",
+ "fnGetPos",
+ "fnGetGamepadXy",
+ "fnPlayFx",
+ "fnStopFx",
+ "fnPlayMusic",
+ "fnStopMusic",
+ "fnInnerSpace",
+ "fnRandom",
+ "fnSetScreen",
+ "fnPreload",
+ "fnCheckCD",
+ "fnRestartGame",
+ "fnQuitGame",
+ "fnDeathScreen",
+ "fnSetParallax",
+ "fnTdebug",
+ "fnRedFlash",
+ "fnBlueFlash",
+ "fnYellow",
+ "fnGreen",
+ "fnPurple",
+ "fnBlack"
+};
+
+} // End of namespace Sword1
diff --git a/engines/sword1/debug.h b/engines/sword1/debug.h
new file mode 100644
index 0000000000..36ddf26973
--- /dev/null
+++ b/engines/sword1/debug.h
@@ -0,0 +1,42 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSDEBUG_H
+#define BSDEBUG_H
+
+#include "common/scummsys.h"
+
+namespace Sword1 {
+
+class Debug {
+public:
+ static void interpretScript(uint32 id, uint32 level, uint32 script, uint32 pc);
+ static void callMCode(uint32 mcodeNum, uint32 paramCount, int32 a, int32 b, int32 c, int32 d, int32 e, int32 f);
+
+private:
+ static const char _mCodeNames[100][35];
+};
+
+} // End of namespace Sword1
+
+#endif // BSDEBUG_H
+
diff --git a/engines/sword1/eventman.cpp b/engines/sword1/eventman.cpp
new file mode 100644
index 0000000000..c13007e8fc
--- /dev/null
+++ b/engines/sword1/eventman.cpp
@@ -0,0 +1,103 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/eventman.h"
+#include "sword1/sworddefs.h"
+#include "common/util.h"
+
+namespace Sword1 {
+
+EventManager::EventManager(void) {
+ for (uint8 cnt = 0; cnt < TOTAL_EVENT_SLOTS; cnt++)
+ _eventPendingList[cnt].delay = _eventPendingList[cnt].eventNumber = 0;
+}
+
+void EventManager::serviceGlobalEventList(void) {
+ for (uint8 slot = 0; slot < TOTAL_EVENT_SLOTS; slot++)
+ if (_eventPendingList[slot].delay)
+ _eventPendingList[slot].delay--;
+}
+
+void EventManager::checkForEvent(Object *compact) {
+ for (uint8 objCnt = 0; objCnt < O_TOTAL_EVENTS; objCnt++) {
+ if (compact->o_event_list[objCnt].o_event)
+ for (uint8 globCnt = 0; globCnt < TOTAL_EVENT_SLOTS; globCnt++) {
+ if (_eventPendingList[globCnt].delay &&
+ (_eventPendingList[globCnt].eventNumber == compact->o_event_list[objCnt].o_event)) {
+ compact->o_logic = LOGIC_script; //force into script mode
+ _eventPendingList[globCnt].delay = 0; //started, so remove from queue
+ compact->o_tree.o_script_level++;
+ compact->o_tree.o_script_id[compact->o_tree.o_script_level] =
+ compact->o_event_list[objCnt].o_event_script;
+ compact->o_tree.o_script_pc[compact->o_tree.o_script_level] =
+ compact->o_event_list[objCnt].o_event_script;
+ }
+ }
+ }
+}
+
+bool EventManager::eventValid(int32 event) {
+ for (uint8 slot = 0; slot < TOTAL_EVENT_SLOTS; slot++)
+ if ((_eventPendingList[slot].eventNumber == event) &&
+ (_eventPendingList[slot].delay))
+ return true;
+ return false;
+}
+
+int EventManager::fnCheckForEvent(Object *cpt, int32 id, int32 pause) {
+ if (pause) {
+ cpt->o_pause = pause;
+ cpt->o_logic = LOGIC_pause_for_event;
+ return SCRIPT_STOP;
+ }
+
+ for (uint8 objCnt = 0; objCnt < O_TOTAL_EVENTS; objCnt++) {
+ if (cpt->o_event_list[objCnt].o_event)
+ for (uint8 globCnt = 0; globCnt < TOTAL_EVENT_SLOTS; globCnt++) {
+ if (_eventPendingList[globCnt].delay &&
+ (_eventPendingList[globCnt].eventNumber == cpt->o_event_list[objCnt].o_event)) {
+ cpt->o_logic = LOGIC_script; //force into script mode
+ _eventPendingList[globCnt].delay = 0; //started, so remove from queue
+ cpt->o_tree.o_script_level++;
+ cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] =
+ cpt->o_event_list[objCnt].o_event_script;
+ cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] =
+ cpt->o_event_list[objCnt].o_event_script;
+ return SCRIPT_STOP;
+ }
+ }
+ }
+ return SCRIPT_CONT;
+}
+
+void EventManager::fnIssueEvent(Object *compact, int32 id, int32 event, int32 delay) {
+ uint8 evSlot = 0;
+ while (_eventPendingList[evSlot].delay)
+ evSlot++;
+ if (evSlot >= TOTAL_EVENT_SLOTS)
+ error("EventManager ran out of event slots!");
+ _eventPendingList[evSlot].delay = delay;
+ _eventPendingList[evSlot].eventNumber = event;
+}
+
+} // End of namespace Sword1
diff --git a/engines/sword1/eventman.h b/engines/sword1/eventman.h
new file mode 100644
index 0000000000..c04b12e711
--- /dev/null
+++ b/engines/sword1/eventman.h
@@ -0,0 +1,51 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSEVENTMAN_H
+#define BSEVENTMAN_H
+
+#include "sword1/object.h"
+
+namespace Sword1 {
+
+#define TOTAL_EVENT_SLOTS 20
+
+struct GlobalEvent {
+ int32 eventNumber;
+ int32 delay;
+};
+
+class EventManager {
+public:
+ EventManager(void);
+ void serviceGlobalEventList(void);
+ void checkForEvent(Object *compact);
+ int fnCheckForEvent(Object *cpt, int32 id, int32 pause);
+ void fnIssueEvent(Object *compact, int32 id, int32 event, int32 delay);
+ bool eventValid(int32 event);
+private:
+ GlobalEvent _eventPendingList[TOTAL_EVENT_SLOTS];
+};
+
+} // End of namespace Sword1
+
+#endif // BSEVENTMAN_H
diff --git a/engines/sword1/logic.cpp b/engines/sword1/logic.cpp
new file mode 100644
index 0000000000..27108e9e51
--- /dev/null
+++ b/engines/sword1/logic.cpp
@@ -0,0 +1,1803 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+
+#include "sword1/logic.h"
+#include "sword1/text.h"
+#include "sword1/sound.h"
+#include "sword1/eventman.h"
+#include "sword1/menu.h"
+#include "sword1/router.h"
+#include "sword1/screen.h"
+#include "sword1/mouse.h"
+#include "sword1/sword1.h"
+#include "sword1/music.h"
+#include "sword1/swordres.h"
+#include "sword1/animation.h"
+#include "sword1/credits.h"
+
+#include "sword1/debug.h"
+
+#include "gui/message.h"
+
+namespace Sword1 {
+
+#define MAX_STACK_SIZE 10
+#define SCRIPT_VERSION 13
+#define LAST_FRAME 999
+
+uint32 Logic::_scriptVars[NUM_SCRIPT_VARS];
+
+Logic::Logic(ObjectMan *pObjMan, ResMan *resMan, Screen *pScreen, Mouse *pMouse, Sound *pSound, Music *pMusic, Menu *pMenu, OSystem *system, Audio::Mixer *mixer) {
+ _objMan = pObjMan;
+ _resMan = resMan;
+ _screen = pScreen;
+ _mouse = pMouse;
+ _music = pMusic;
+ _sound = pSound;
+ _menu = pMenu;
+ _textMan = NULL;
+ _screen->useTextManager(_textMan);
+ _router = new Router(_objMan, _resMan);
+ _eventMan = NULL;
+ _system = system;
+ _mixer = mixer;
+}
+
+Logic::~Logic(void) {
+ delete _textMan;
+ delete _router;
+ delete _eventMan;
+}
+
+void Logic::initialize(void) {
+ memset(_scriptVars, 0, NUM_SCRIPT_VARS * sizeof(uint32));
+ for (uint8 cnt = 0; cnt < NON_ZERO_SCRIPT_VARS; cnt++)
+ _scriptVars[_scriptVarInit[cnt][0]] = _scriptVarInit[cnt][1];
+ if (SwordEngine::_systemVars.isDemo)
+ _scriptVars[PLAYINGDEMO] = 1;
+
+ delete _eventMan;
+ _eventMan = new EventManager();
+
+ delete _textMan;
+ _textMan = new Text(_objMan, _resMan,
+ (SwordEngine::_systemVars.language == BS1_CZECH) ? true : false);
+ _screen->useTextManager(_textMan);
+ _textRunning = _speechRunning = false;
+ _speechFinished = true;
+ _router->resetExtraData();
+}
+
+void Logic::newScreen(uint32 screen) {
+ Object *compact = (Object*)_objMan->fetchObject(PLAYER);
+
+ // work around script bug #911508
+ if (((screen == 25) || (_scriptVars[SCREEN] == 25)) && (_scriptVars[SAND_FLAG] == 4)) {
+ Object *cpt = _objMan->fetchObject(SAND_25);
+ Object *george = _objMan->fetchObject(PLAYER);
+ if (george->o_place == HOLDING_REPLICA_25) // is george holding the replica in his hands?
+ fnFullSetFrame(cpt, SAND_25, IMPFLRCDT, IMPFLR, 0, 0, 0, 0); // empty impression in floor
+ else
+ fnFullSetFrame(cpt, SAND_25, IMPPLSCDT, IMPPLS, 0, 0, 0, 0); // impression filled with plaster
+ }
+
+ if (SwordEngine::_systemVars.justRestoredGame) { // if we've just restored a game - we want George to be exactly as saved
+ fnAddHuman(NULL, 0, 0, 0, 0, 0, 0, 0);
+ if (_scriptVars[GEORGE_WALKING]) { // except that if George was walking when we saveed the game
+ fnStandAt(compact, PLAYER, _scriptVars[CHANGE_X], _scriptVars[CHANGE_Y], _scriptVars[CHANGE_DIR], _scriptVars[CHANGE_STANCE], 0,0);
+ fnIdle(compact,PLAYER,0,0,0,0,0,0);
+ _scriptVars[GEORGE_WALKING] = 0;
+ }
+ SwordEngine::_systemVars.justRestoredGame = 0;
+ _music->startMusic(_scriptVars[CURRENT_MUSIC], 1);
+ } else { // if we haven't just restored a game, set George to stand, etc
+ compact->o_screen = _scriptVars[NEW_SCREEN]; //move the mega/player at this point between screens
+ fnStandAt(compact, PLAYER, _scriptVars[CHANGE_X], _scriptVars[CHANGE_Y], _scriptVars[CHANGE_DIR], _scriptVars[CHANGE_STANCE], 0,0);
+ fnChangeFloor(compact, PLAYER, _scriptVars[CHANGE_PLACE], 0, 0, 0, 0, 0);
+ }
+}
+
+void Logic::engine(void) {
+ debug(8, "\n\nNext logic cycle");
+ _eventMan->serviceGlobalEventList();
+
+ for (uint16 sectCnt = 0; sectCnt < TOTAL_SECTIONS; sectCnt++) {
+ if (_objMan->sectionAlive(sectCnt)) {
+ uint32 numCpts = _objMan->fetchNoObjects(sectCnt);
+ for (uint32 cptCnt = 0; cptCnt < numCpts; cptCnt++) {
+ uint32 currentId = sectCnt * ITM_PER_SEC + cptCnt;
+ Object *compact = _objMan->fetchObject(currentId);
+
+ if (compact->o_status & STAT_LOGIC) { // does the object want to be processed?
+ if (compact->o_status & STAT_EVENTS) {
+ //subscribed to the global-event-switcher? and in logic mode
+ switch (compact->o_logic) {
+ case LOGIC_pause_for_event:
+ case LOGIC_idle:
+ case LOGIC_AR_animate:
+ _eventMan->checkForEvent(compact);
+ break;
+ }
+ }
+ debug(7, "Logic::engine: handling compact %d (%X)", currentId, currentId);
+ processLogic(compact, currentId);
+ compact->o_sync = 0; // syncs are only available for 1 cycle.
+ }
+
+ if ((uint32)compact->o_screen == _scriptVars[SCREEN]) {
+ if (compact->o_status & STAT_FORE)
+ _screen->addToGraphicList(0, currentId);
+ if (compact->o_status & STAT_SORT)
+ _screen->addToGraphicList(1, currentId);
+ if (compact->o_status & STAT_BACK)
+ _screen->addToGraphicList(2, currentId);
+
+ if (compact->o_status & STAT_MOUSE)
+ _mouse->addToList(currentId, compact);
+ }
+ }
+ }
+ }
+ //_collision->checkCollisions();
+
+}
+
+void Logic::processLogic(Object *compact, uint32 id) {
+ int logicRet;
+ do {
+ switch (compact->o_logic) {
+ case LOGIC_idle:
+ logicRet = 0;
+ break;
+ case LOGIC_pause:
+ case LOGIC_pause_for_event:
+ if (compact->o_pause) {
+ compact->o_pause--;
+ logicRet = 0;
+ } else {
+ compact->o_logic = LOGIC_script;
+ logicRet = 1;
+ }
+ break;
+ case LOGIC_quit:
+ compact->o_logic = LOGIC_script;
+ logicRet = 0;
+ break;
+ case LOGIC_wait_for_sync:
+ if (compact->o_sync) {
+ logicRet = 1;
+ compact->o_logic = LOGIC_script;
+ } else
+ logicRet = 0;
+ break;
+ case LOGIC_choose:
+ _scriptVars[CUR_ID] = id;
+ logicRet = _menu->logicChooser(compact);
+ break;
+ case LOGIC_wait_for_talk:
+ logicRet = logicWaitTalk(compact);
+ break;
+ case LOGIC_start_talk:
+ logicRet = logicStartTalk(compact);
+ break;
+ case LOGIC_script:
+ _scriptVars[CUR_ID] = id;
+ logicRet = scriptManager(compact, id);
+ break;
+ case LOGIC_new_script:
+ compact->o_tree.o_script_pc[compact->o_tree.o_script_level] = _newScript;
+ compact->o_tree.o_script_id[compact->o_tree.o_script_level] = _newScript;
+ compact->o_logic = LOGIC_script;
+ logicRet = 1;
+ break;
+ case LOGIC_AR_animate:
+ logicRet = logicArAnimate(compact, id);
+ break;
+ case LOGIC_restart:
+ compact->o_tree.o_script_pc[compact->o_tree.o_script_level] = compact->o_tree.o_script_id[compact->o_tree.o_script_level];
+ compact->o_logic = LOGIC_script;
+ logicRet=1;
+ break;
+ case LOGIC_bookmark:
+ memcpy(&(compact->o_tree.o_script_level), &(compact->o_bookmark.o_script_level), sizeof(ScriptTree));
+ if (id == GMASTER_79) {
+ // workaround for ending script.
+ // GMASTER_79 is not prepared for mega_interact receiving INS_quit
+ fnSuicide(compact, id, 0, 0, 0, 0, 0, 0);
+ logicRet = 0;
+ } else {
+ compact->o_logic = LOGIC_script;
+ logicRet = 1;
+ }
+ break;
+ case LOGIC_speech:
+ logicRet = speechDriver(compact);
+ break;
+ case LOGIC_full_anim:
+ logicRet = fullAnimDriver(compact);
+ break;
+ case LOGIC_anim:
+ logicRet = animDriver(compact);
+ break;
+
+ default:
+ error("Fatal error: compact %d's logic == %X!", id, compact->o_logic);
+ break;
+ }
+ } while (logicRet);
+}
+
+int Logic::logicWaitTalk(Object *compact) {
+ Object *target = _objMan->fetchObject(compact->o_down_flag);
+
+ if (target->o_status & STAT_TALK_WAIT) {
+ compact->o_logic = LOGIC_script;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int Logic::logicStartTalk(Object *compact) {
+ Object *target = _objMan->fetchObject(compact->o_down_flag); //holds id of person we're waiting for
+ if (target->o_status & STAT_TALK_WAIT) { //response?
+ compact->o_logic = LOGIC_script; //back to script again
+ return SCRIPT_CONT;
+ }
+ if (_eventMan->eventValid(compact->o_down_flag))
+ return SCRIPT_STOP; //event still valid - keep waiting
+ //the event has gone - so back to script with error code
+ compact->o_down_flag = 0;
+ compact->o_logic = LOGIC_script;
+ return SCRIPT_CONT;
+}
+
+int Logic::logicArAnimate(Object *compact, uint32 id) {
+ WalkData *route;
+ int32 walkPc;
+ if ((_scriptVars[GEORGE_WALKING] == 0) && (id == PLAYER))
+ _scriptVars[GEORGE_WALKING] = 1;
+
+ compact->o_resource = compact->o_walk_resource;
+ compact->o_status |= STAT_SHRINK;
+ route = compact->o_route;
+
+ walkPc =compact->o_walk_pc;
+ compact->o_frame =route[walkPc].frame;
+ compact->o_dir =route[walkPc].dir;
+ compact->o_xcoord =route[walkPc].x;
+ compact->o_ycoord =route[walkPc].y;
+ compact->o_anim_x =compact->o_xcoord;
+ compact->o_anim_y =compact->o_ycoord;
+
+ if (((_scriptVars[GEORGE_WALKING] == 2) && (walkPc > 5) && (id == PLAYER) &&
+ (route[walkPc - 1].step == 5) && (route[walkPc].step == 0)) ||
+ ((_scriptVars[GEORGE_WALKING] == 3) && (id == PLAYER))) {
+
+ compact->o_frame = 96 + compact->o_dir; //reset
+ if ((compact->o_dir != 2) && (compact->o_dir != 6)) { // on verticals and diagonals stand where george is
+ compact->o_xcoord = route[walkPc - 1].x;
+ compact->o_ycoord = route[walkPc - 1].y;
+ compact->o_anim_x = compact->o_xcoord;
+ compact->o_anim_y = compact->o_ycoord;
+ }
+ compact->o_logic = LOGIC_script;
+ compact->o_down_flag = 0; //0 means error
+ _scriptVars[GEORGE_WALKING] = 0;
+ route[compact->o_walk_pc+1].frame = 512; //end of sequence
+ if (_scriptVars[MEGA_ON_GRID] == 2)
+ _scriptVars[MEGA_ON_GRID] = 0;
+ }
+ compact->o_walk_pc++;
+
+ if (route[compact->o_walk_pc].frame == 512) //end of sequence
+ {
+ compact->o_logic = LOGIC_script;
+ if (((_scriptVars[GEORGE_WALKING] == 2) || (_scriptVars[GEORGE_WALKING] == 1)) &&
+ (id == PLAYER)) {
+ _scriptVars[GEORGE_WALKING] = 0;
+ if (_scriptVars[MEGA_ON_GRID] == 2)
+ _scriptVars[MEGA_ON_GRID] = 0;
+ }
+ }
+ return 0;
+}
+
+int Logic::speechDriver(Object *compact) {
+ if ((!_speechClickDelay) && (_mouse->testEvent() & BS1L_BUTTON_DOWN))
+ _speechFinished = true;
+ if (_speechClickDelay)
+ _speechClickDelay--;
+
+ if (_speechRunning) {
+ if (_sound->speechFinished())
+ _speechFinished = true;
+ } else {
+ if (!compact->o_speech_time)
+ _speechFinished = true;
+ else
+ compact->o_speech_time--;
+ }
+ if (_speechFinished) {
+ if (_speechRunning)
+ _sound->stopSpeech();
+ compact->o_logic = LOGIC_script;
+ if (_textRunning) {
+ _textMan->releaseText(compact->o_text_id);
+ _objMan->fetchObject(compact->o_text_id)->o_status = 0; // kill compact linking text sprite
+ }
+ _speechRunning = _textRunning = false;
+ _speechFinished = true;
+ }
+ if (compact->o_anim_resource) {
+ uint8 *animData = ((uint8*)_resMan->openFetchRes(compact->o_anim_resource)) + sizeof(Header);
+ int32 numFrames = READ_LE_UINT32(animData);
+ animData += 4;
+ compact->o_anim_pc++; // go to next frame of anim
+
+ if (_speechFinished || (compact->o_anim_pc >= numFrames) ||
+ (_speechRunning && (_sound->amISpeaking() == 0)))
+ compact->o_anim_pc = 0; //set to frame 0, closed mouth
+
+ AnimUnit *animPtr = (AnimUnit*)(animData + sizeof(AnimUnit) * compact->o_anim_pc);
+ if (!(compact->o_status & STAT_SHRINK)) {
+ compact->o_anim_x = FROM_LE_32(animPtr->animX);
+ compact->o_anim_y = FROM_LE_32(animPtr->animY);
+ }
+ compact->o_frame = FROM_LE_32(animPtr->animFrame);
+ _resMan->resClose(compact->o_anim_resource);
+ }
+ return 0;
+}
+
+int Logic::fullAnimDriver(Object *compact) {
+ if (compact->o_sync) { // return to script immediately if we've received a sync
+ compact->o_logic = LOGIC_script;
+ return 1;
+ }
+ uint8 *data = ((uint8*)_resMan->openFetchRes(compact->o_anim_resource)) + sizeof(Header);
+ uint32 numFrames = READ_LE_UINT32(data);
+ data += 4;
+ AnimUnit *animPtr = (AnimUnit*)(data + compact->o_anim_pc * sizeof(AnimUnit));
+
+ compact->o_anim_x = compact->o_xcoord = FROM_LE_32(animPtr->animX);
+ compact->o_anim_y = compact->o_ycoord = FROM_LE_32(animPtr->animY);
+ compact->o_frame = FROM_LE_32(animPtr->animFrame);
+
+ compact->o_anim_pc++;
+ if (compact->o_anim_pc == (int)numFrames)
+ compact->o_logic = LOGIC_script;
+
+ _resMan->resClose(compact->o_anim_resource);
+ return 0;
+}
+
+int Logic::animDriver(Object *compact) {
+ if (compact->o_sync) {
+ compact->o_logic = LOGIC_script;
+ return 1;
+ }
+ uint8 *data = ((uint8*)_resMan->openFetchRes(compact->o_anim_resource)) + sizeof(Header);
+ uint32 numFrames = READ_LE_UINT32(data);
+ AnimUnit *animPtr = (AnimUnit*)(data + 4 + compact->o_anim_pc * sizeof(AnimUnit));
+
+ if (!(compact->o_status & STAT_SHRINK)) {
+ compact->o_anim_x = FROM_LE_32(animPtr->animX);
+ compact->o_anim_y = FROM_LE_32(animPtr->animY);
+ }
+
+ compact->o_frame = FROM_LE_32(animPtr->animFrame);
+ compact->o_anim_pc++;
+ if (compact->o_anim_pc == (int)numFrames)
+ compact->o_logic = LOGIC_script;
+
+ _resMan->resClose(compact->o_anim_resource);
+ return 0;
+}
+
+void Logic::updateScreenParams(void) {
+ Object *compact = (Object*)_objMan->fetchObject(PLAYER);
+ _screen->setScrolling((int16)(compact->o_xcoord - _scriptVars[FEET_X]),
+ (int16)(compact->o_ycoord - _scriptVars[FEET_Y]));
+}
+
+int Logic::scriptManager(Object *compact, uint32 id) {
+ int ret;
+ do {
+ uint32 level = compact->o_tree.o_script_level;
+ uint32 script = compact->o_tree.o_script_id[level];
+ Debug::interpretScript(id, level, script, compact->o_tree.o_script_pc[level] & ITM_ID);
+ ret = interpretScript(compact, id, _resMan->lockScript(script), script, compact->o_tree.o_script_pc[level] & ITM_ID);
+ _resMan->unlockScript(script);
+ if (!ret) {
+ if (compact->o_tree.o_script_level)
+ compact->o_tree.o_script_level--;
+ else
+ error("ScriptManager: basescript %d for cpt %d ended!", script, id);
+ } else
+ compact->o_tree.o_script_pc[level] = ret;
+ } while (!ret);
+ return 1;
+ //Logic continues - but the script must have changed logic mode
+ //this is a radical change from S2.0 where once a script finished there
+ //was no more processing for that object on that cycle - the Logic_engine terminated.
+ //This meant that new logics that needed immediate action got a first call from the
+ //setup function. This was a bit tweeky. This technique ensures that the script is a
+ //totally seamless concept that takes up zero cycle time. The only downside is that
+ //an FN_quit becomes slightly more convoluted, but so what you might ask.
+}
+
+void Logic::runMouseScript(Object *cpt, int32 scriptId) {
+ Header *script = _resMan->lockScript(scriptId);
+ debug(9, "running mouse script %d", scriptId);
+ interpretScript(cpt, _scriptVars[SPECIAL_ITEM], script, scriptId, scriptId);
+ _resMan->unlockScript(scriptId);
+}
+
+int Logic::interpretScript(Object *compact, int id, Header *scriptModule, int scriptBase, int scriptNum) {
+ int32 *scriptCode = (int32*)(((uint8*)scriptModule) + sizeof(Header));
+ int32 stack[MAX_STACK_SIZE];
+ int32 stackIdx = 0;
+ int32 offset;
+ int32 pc;
+ if (memcmp(scriptModule->type, "Script", 6))
+ error("Invalid script module!");
+ if (scriptModule->version != SCRIPT_VERSION)
+ error("Illegal script version!");
+ if (scriptNum < 0)
+ error("negative script number");
+ if ((uint32)scriptNum >= scriptModule->decomp_length)
+ error("Script number out of bounds");
+
+ if (scriptNum < scriptCode[0])
+ pc = scriptCode[scriptNum + 1];
+ else
+ pc = scriptNum;
+ int32 startOfScript = scriptCode[(scriptBase & ITM_ID) + 1];
+
+ int32 a, b, c, d, e, f;
+ int mCodeReturn = 0;
+ int32 mCodeNumber = 0, mCodeArguments = 0;
+ uint32 varNum = 0;
+ while (1) {
+ assert((stackIdx >= 0) && (stackIdx <= MAX_STACK_SIZE));
+ switch (scriptCode[pc++]) {
+ case IT_MCODE:
+ a = b = c = d = e = f = 0;
+ mCodeNumber = scriptCode[pc++];
+ mCodeArguments = scriptCode[pc++];
+ switch (mCodeArguments) {
+ case 6: f = stack[--stackIdx];
+ case 5: e = stack[--stackIdx];
+ case 4: d = stack[--stackIdx];
+ case 3: c = stack[--stackIdx];
+ case 2: b = stack[--stackIdx];
+ case 1: a = stack[--stackIdx];
+ case 0:
+ Debug::callMCode(mCodeNumber, mCodeArguments, a, b, c, d, e, f);
+ mCodeReturn = (this->*_mcodeTable[mCodeNumber])(compact, id, a, b, c, d, e, f);
+ break;
+ default:
+ warning("mcode[%d]: too many arguments(%d)", mCodeNumber, mCodeArguments);
+ }
+ if (mCodeReturn == 0)
+ return pc;
+ break;
+ case IT_PUSHNUMBER:
+ debug(9, "IT_PUSH: %d", scriptCode[pc]);
+ stack[stackIdx++] = scriptCode[pc++];
+ break;
+ case IT_PUSHVARIABLE:
+ debug(9, "IT_PUSHVARIABLE: ScriptVar[%d] => %d", scriptCode[pc], _scriptVars[scriptCode[pc]]);
+ varNum = scriptCode[pc++];
+ if (SwordEngine::_systemVars.isDemo) {
+ if (varNum >= 397) // BS1 Demo has different number of script variables
+ varNum++;
+ if (varNum >= 699)
+ varNum++;
+ }
+ stack[stackIdx++] = _scriptVars[varNum];
+ break;
+ case IT_NOTEQUAL:
+ stackIdx--;
+ debug(9, "IT_NOTEQUAL: RESULT = %d", stack[stackIdx - 1] != stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] != stack[stackIdx]);
+ break;
+ case IT_ISEQUAL:
+ stackIdx--;
+ debug(9, "IT_ISEQUAL: RESULT = %d", stack[stackIdx - 1] == stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] == stack[stackIdx]);
+ break;
+ case IT_PLUS:
+ stackIdx--;
+ debug(9, "IT_PLUS: RESULT = %d", stack[stackIdx - 1] + stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] + stack[stackIdx]);
+ break;
+ case IT_TIMES:
+ stackIdx--;
+ debug(9, "IT_TIMES: RESULT = %d", stack[stackIdx - 1] * stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] * stack[stackIdx]);
+ break;
+ case IT_ANDAND:
+ stackIdx--;
+ debug(9, "IT_ANDAND: RESULT = %d", stack[stackIdx - 1] && stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] && stack[stackIdx]);
+ break;
+ case IT_OROR: // ||
+ stackIdx--;
+ debug(9, "IT_OROR: RESULT = %d", stack[stackIdx - 1] || stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] || stack[stackIdx]);
+ break;
+ case IT_LESSTHAN:
+ stackIdx--;
+ debug(9, "IT_LESSTHAN: RESULT = %d", stack[stackIdx - 1] < stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] < stack[stackIdx]);
+ break;
+ case IT_NOT:
+ debug(9, "IT_NOT: RESULT = %d", stack[stackIdx - 1] ? 0 : 1);
+ if (stack[stackIdx - 1])
+ stack[stackIdx - 1] = 0;
+ else
+ stack[stackIdx - 1] = 1;
+ break;
+ case IT_MINUS:
+ stackIdx--;
+ debug(9, "IT_MINUS: RESULT = %d", stack[stackIdx - 1] - stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] - stack[stackIdx]);
+ break;
+ case IT_AND:
+ stackIdx--;
+ debug(9, "IT_AND: RESULT = %d", stack[stackIdx - 1] & stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] & stack[stackIdx]);
+ break;
+ case IT_OR:
+ stackIdx--;
+ debug(9, "IT_OR: RESULT = %d", stack[stackIdx - 1] | stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] | stack[stackIdx]);
+ break;
+ case IT_GTE:
+ stackIdx--;
+ debug(9, "IT_GTE: RESULT = %d", stack[stackIdx - 1] >= stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] >= stack[stackIdx]);
+ break;
+ case IT_LTE:
+ stackIdx--;
+ debug(9, "IT_LTE: RESULT = %d", stack[stackIdx - 1] <= stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] <= stack[stackIdx]);
+ break;
+ case IT_DEVIDE:
+ stackIdx--;
+ debug(9, "IT_DEVIDE: RESULT = %d", stack[stackIdx - 1] / stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] / stack[stackIdx]);
+ break;
+ case IT_GT:
+ stackIdx--;
+ debug(9, "IT_GT: RESULT = %d", stack[stackIdx - 1] > stack[stackIdx]);
+ stack[stackIdx - 1] = (stack[stackIdx - 1] > stack[stackIdx]);
+ break;
+ case IT_SCRIPTEND:
+ debug(9, "IT_SCRIPTEND");
+ return 0;
+ case IT_POPVAR: // pop a variable
+ debug(9, "IT_POPVAR: ScriptVars[%d] = %d", scriptCode[pc], stack[stackIdx-1]);
+ varNum = scriptCode[pc++];
+ if (SwordEngine::_systemVars.isDemo) {
+ if (varNum >= 397) // BS1 Demo has different number of script variables
+ varNum++;
+ if (varNum >= 699)
+ varNum++;
+ }
+ _scriptVars[varNum] = stack[--stackIdx];
+ break;
+ case IT_POPLONGOFFSET:
+ offset = scriptCode[pc++];
+ debug(9, "IT_POPLONGOFFSET: Cpt[%d] = %d", offset, stack[stackIdx - 1]);
+ *((int32 *)((uint8*)compact + offset)) = stack[--stackIdx];
+ break;
+ case IT_PUSHLONGOFFSET:
+ offset = scriptCode[pc++];
+ debug(9, "IT_PUSHLONGOFFSET: PUSH Cpt[%d] (==%d)", offset, *((int32 *)((uint8*)compact + offset)));
+ stack[stackIdx++] = *((int32 *)((uint8*)compact + offset));
+ break;
+ case IT_SKIPONFALSE:
+ debug(9, "IT_SKIPONFALSE: %d (%s)", scriptCode[pc], (stack[stackIdx-1] ? "IS TRUE (NOT SKIPPED)" : "IS FALSE (SKIPPED)"));
+ if (stack[--stackIdx])
+ pc++;
+ else
+ pc += scriptCode[pc];
+ break;
+ case IT_SKIP:
+ debug(9, "IT_SKIP: %d", scriptCode[pc]);
+ pc += scriptCode[pc];
+ break;
+ case IT_SWITCH: // The mega switch statement
+ debug(9, "IT_SWITCH: [SORRY, NO DEBUG INFO]");
+ {
+ int switchValue = stack[--stackIdx];
+ int switchCount = scriptCode[pc++];
+ int doneSwitch=0;
+
+ for (int cnt = 0; (cnt < switchCount) && (doneSwitch==0); cnt++) {
+ if (switchValue == scriptCode[pc]) {
+ pc += scriptCode[pc+1];
+ doneSwitch=1;
+ } else
+ pc += 2;
+ }
+ if (doneSwitch == 0)
+ pc += scriptCode[pc];
+ }
+ break;
+ case IT_SKIPONTRUE: // skip if expression true
+ debug(9, "IT_SKIPONTRUE: %d (%s)", scriptCode[pc], (stack[stackIdx-1] ? "IS TRUE (SKIPPED)" : "IS FALSE (NOT SKIPPED)"));
+ stackIdx--;
+ if (stack[stackIdx])
+ pc += scriptCode[pc];
+ else
+ pc++;
+ break;
+ case IT_PRINTF:
+ debug(0, "IT_PRINTF(%d)",stack[stackIdx]);
+ break;
+ case IT_RESTARTSCRIPT:
+ debug(9, "IT_RESTARTSCRIPT");
+ pc = startOfScript;
+ break;
+ case IT_POPWORDOFFSET:
+ offset = scriptCode[pc++];
+ debug(9, "IT_POPWORDOFFSET: Cpt[%d] = %d", offset, stack[stackIdx - 1] & 0xFFFF);
+ *((int32 *)((uint8*)compact + offset)) = stack[--stackIdx] & 0xffff;
+ break;
+ case IT_PUSHWORDOFFSET:
+ offset = scriptCode[pc++];
+ debug(9, "IT_PUSHWORDOFFSET: PUSH Cpt[%d] == %d", offset, (*((int32 *)((uint8*)compact + offset))) & 0xffff);
+ stack[stackIdx++] = (*((int32 *)((uint8*)compact + offset))) & 0xffff;
+ break;
+ default:
+ error("Invalid operator %d",scriptCode[pc-1]);
+ return 0;
+ }
+ }
+}
+
+BSMcodeTable Logic::_mcodeTable[100] = {
+ &Logic::fnBackground,
+ &Logic::fnForeground,
+ &Logic::fnSort,
+ &Logic::fnNoSprite,
+ &Logic::fnMegaSet,
+ &Logic::fnAnim,
+ &Logic::fnSetFrame,
+ &Logic::fnFullAnim,
+ &Logic::fnFullSetFrame,
+ &Logic::fnFadeDown,
+ &Logic::fnFadeUp,
+ &Logic::fnCheckFade,
+ &Logic::fnSetSpritePalette,
+ &Logic::fnSetWholePalette,
+ &Logic::fnSetFadeTargetPalette,
+ &Logic::fnSetPaletteToFade,
+ &Logic::fnSetPaletteToCut,
+ &Logic::fnPlaySequence,
+ &Logic::fnIdle,
+ &Logic::fnPause,
+ &Logic::fnPauseSeconds,
+ &Logic::fnQuit,
+ &Logic::fnKillId,
+ &Logic::fnSuicide,
+ &Logic::fnNewScript,
+ &Logic::fnSubScript,
+ &Logic::fnRestartScript,
+ &Logic::fnSetBookmark,
+ &Logic::fnGotoBookmark,
+ &Logic::fnSendSync,
+ &Logic::fnWaitSync,
+ &Logic::cfnClickInteract,
+ &Logic::cfnSetScript,
+ &Logic::cfnPresetScript,
+ &Logic::fnInteract,
+ &Logic::fnIssueEvent,
+ &Logic::fnCheckForEvent,
+ &Logic::fnWipeHands,
+ &Logic::fnISpeak,
+ &Logic::fnTheyDo,
+ &Logic::fnTheyDoWeWait,
+ &Logic::fnWeWait,
+ &Logic::fnChangeSpeechText,
+ &Logic::fnTalkError,
+ &Logic::fnStartTalk,
+ &Logic::fnCheckForTextLine,
+ &Logic::fnAddTalkWaitStatusBit,
+ &Logic::fnRemoveTalkWaitStatusBit,
+ &Logic::fnNoHuman,
+ &Logic::fnAddHuman,
+ &Logic::fnBlankMouse,
+ &Logic::fnNormalMouse,
+ &Logic::fnLockMouse,
+ &Logic::fnUnlockMouse,
+ &Logic::fnSetMousePointer,
+ &Logic::fnSetMouseLuggage,
+ &Logic::fnMouseOn,
+ &Logic::fnMouseOff,
+ &Logic::fnChooser,
+ &Logic::fnEndChooser,
+ &Logic::fnStartMenu,
+ &Logic::fnEndMenu,
+ &Logic::cfnReleaseMenu,
+ &Logic::fnAddSubject,
+ &Logic::fnAddObject,
+ &Logic::fnRemoveObject,
+ &Logic::fnEnterSection,
+ &Logic::fnLeaveSection,
+ &Logic::fnChangeFloor,
+ &Logic::fnWalk,
+ &Logic::fnTurn,
+ &Logic::fnStand,
+ &Logic::fnStandAt,
+ &Logic::fnFace,
+ &Logic::fnFaceXy,
+ &Logic::fnIsFacing,
+ &Logic::fnGetTo,
+ &Logic::fnGetToError,
+ &Logic::fnGetPos,
+ &Logic::fnGetGamepadXy,
+ &Logic::fnPlayFx,
+ &Logic::fnStopFx,
+ &Logic::fnPlayMusic,
+ &Logic::fnStopMusic,
+ &Logic::fnInnerSpace,
+ &Logic::fnRandom,
+ &Logic::fnSetScreen,
+ &Logic::fnPreload,
+ &Logic::fnCheckCD,
+ &Logic::fnRestartGame,
+ &Logic::fnQuitGame,
+ &Logic::fnDeathScreen,
+ &Logic::fnSetParallax,
+ &Logic::fnTdebug,
+ &Logic::fnRedFlash,
+ &Logic::fnBlueFlash,
+ &Logic::fnYellow,
+ &Logic::fnGreen,
+ &Logic::fnPurple,
+ &Logic::fnBlack
+};
+
+int Logic::fnBackground(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+
+ cpt->o_status &= ~(STAT_FORE | STAT_SORT);
+ cpt->o_status |= STAT_BACK;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnForeground(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_status &= ~(STAT_BACK | STAT_SORT);
+ cpt->o_status |= STAT_FORE;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnSort(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_status &= ~(STAT_BACK | STAT_FORE);
+ cpt->o_status |= STAT_SORT;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnNoSprite(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_status &= ~(STAT_BACK | STAT_FORE | STAT_SORT);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnMegaSet(Object *cpt, int32 id, int32 walk_data, int32 spr, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_mega_resource = walk_data;
+ cpt->o_walk_resource = spr;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnAnim(Object *cpt, int32 id, int32 cdt, int32 spr, int32 e, int32 f, int32 z, int32 x) {
+ AnimSet *animTab;
+
+ if (cdt && (!spr)) {
+ animTab = (AnimSet*)((uint8*)_resMan->openFetchRes(cdt) + sizeof(Header));
+ animTab += cpt->o_dir;
+
+ cpt->o_anim_resource = FROM_LE_32(animTab->cdt);
+ cpt->o_resource = FROM_LE_32(animTab->spr);
+ _resMan->resClose(cdt);
+ } else {
+ cpt->o_anim_resource = cdt;
+ cpt->o_resource = spr;
+ }
+ if ((cpt->o_anim_resource == 0) || (cpt->o_resource == 0))
+ error("fnAnim called width (%d/%d) => (%d/%d)", cdt, spr, cpt->o_anim_resource, cpt->o_resource);
+
+ FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(cpt->o_resource), 0);
+ if (frameHead->offsetX || frameHead->offsetY) { // boxed mega anim?
+ cpt->o_status |= STAT_SHRINK;
+ cpt->o_anim_x = cpt->o_xcoord; // set anim coords to 'feet' coords - only need to do this once
+ cpt->o_anim_y = cpt->o_ycoord;
+ } else {
+ // Anim_driver sets anim coords to cdt coords for every frame of a loose anim
+ cpt->o_status &= ~STAT_SHRINK;
+ }
+ _resMan->resClose(cpt->o_resource);
+
+ cpt->o_logic = LOGIC_anim;
+ cpt->o_anim_pc = 0;
+ cpt->o_sync = 0;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnSetFrame(Object *cpt, int32 id, int32 cdt, int32 spr, int32 frameNo, int32 f, int32 z, int32 x) {
+
+ AnimUnit *animPtr;
+
+ uint8 *data = (uint8*)_resMan->openFetchRes(cdt);
+ data += sizeof(Header);
+ if (frameNo == LAST_FRAME)
+ frameNo = READ_LE_UINT32(data) - 1;
+
+ data += 4;
+ animPtr = (AnimUnit*)(data + frameNo * sizeof(AnimUnit));
+
+ cpt->o_anim_x = FROM_LE_32(animPtr->animX);
+ cpt->o_anim_y = FROM_LE_32(animPtr->animY);
+ cpt->o_frame = FROM_LE_32(animPtr->animFrame);
+
+ cpt->o_resource = spr;
+ cpt->o_status &= ~STAT_SHRINK;
+ _resMan->resClose(cdt);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnFullAnim(Object *cpt, int32 id, int32 anim, int32 graphic, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_logic = LOGIC_full_anim;
+
+ cpt->o_anim_pc = 0;
+ cpt->o_anim_resource = anim;
+ cpt->o_resource = graphic;
+ cpt->o_status &= ~STAT_SHRINK;
+ cpt->o_sync = 0;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnFullSetFrame(Object *cpt, int32 id, int32 cdt, int32 spr, int32 frameNo, int32 f, int32 z, int32 x) {
+ uint8 *data = (uint8*)_resMan->openFetchRes(cdt) + sizeof(Header);
+
+ if (frameNo == LAST_FRAME)
+ frameNo = READ_LE_UINT32(data) - 1;
+ data += 4;
+
+ AnimUnit *animPtr = (AnimUnit*)(data + sizeof(AnimUnit) * frameNo);
+ cpt->o_anim_x = cpt->o_xcoord = FROM_LE_32(animPtr->animX);
+ cpt->o_anim_y = cpt->o_ycoord = FROM_LE_32(animPtr->animY);
+ cpt->o_frame = FROM_LE_32(animPtr->animFrame);
+
+ cpt->o_resource = spr;
+ cpt->o_status &= ~STAT_SHRINK;
+
+ _resMan->resClose(cdt);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnFadeDown(Object *cpt, int32 id, int32 speed, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _screen->fadeDownPalette();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnFadeUp(Object *cpt, int32 id, int32 speed, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _screen->fadeUpPalette();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnCheckFade(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _scriptVars[RETURN_VALUE] = (uint8)_screen->stillFading();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnSetSpritePalette(Object *cpt, int32 id, int32 spritePal, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _screen->fnSetPalette(184, 72, spritePal, false);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnSetWholePalette(Object *cpt, int32 id, int32 spritePal, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _screen->fnSetPalette(0, 256, spritePal, false);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnSetFadeTargetPalette(Object *cpt, int32 id, int32 spritePal, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _screen->fnSetPalette(0, 184, spritePal, true);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnSetPaletteToFade(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ SwordEngine::_systemVars.wantFade = true;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnSetPaletteToCut(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ SwordEngine::_systemVars.wantFade = false;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnPlaySequence(Object *cpt, int32 id, int32 sequenceId, int32 d, int32 e, int32 f, int32 z, int32 x) {
+
+ // A cutscene usually (always?) means the room will change. In the
+ // meantime, we don't want any looping sound effects still playing.
+ _sound->quitScreen();
+
+ if ((SwordEngine::_systemVars.cutscenePackVersion == 1) && (sequenceId == SEQ_CREDITS)) {
+ CreditsPlayer player(_system, _mixer);
+ player.play();
+ } else {
+ MoviePlayer player(_screen, _mixer, _system);
+ player.play(sequenceId);
+ }
+ return SCRIPT_CONT;
+}
+
+int Logic::fnIdle(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_tree.o_script_level = 0; // force to level 0
+ cpt->o_logic = LOGIC_idle;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnPause(Object *cpt, int32 id, int32 pause, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_pause = pause;
+ cpt->o_logic = LOGIC_pause;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnPauseSeconds(Object *cpt, int32 id, int32 pause, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_pause = pause * FRAME_RATE;
+ cpt->o_logic = LOGIC_pause;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnQuit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_logic = LOGIC_quit;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnKillId(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ Object *targetObj = _objMan->fetchObject(target);
+ targetObj->o_status = 0;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnSuicide(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_status = 0;
+ cpt->o_logic = LOGIC_quit;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnNewScript(Object *cpt, int32 id, int32 script, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_logic = LOGIC_new_script;
+ _newScript = script;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnSubScript(Object *cpt, int32 id, int32 script, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_tree.o_script_level++;
+ if (cpt->o_tree.o_script_level == TOTAL_script_levels)
+ error("Compact %d: script level exceeded in fnSubScript.", id);
+ cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = script;
+ cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = script;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnRestartScript(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_logic = LOGIC_restart;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnSetBookmark(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ memcpy(&cpt->o_bookmark.o_script_level, &cpt->o_tree.o_script_level, sizeof(ScriptTree));
+ return SCRIPT_CONT;
+}
+
+int Logic::fnGotoBookmark(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_logic = LOGIC_bookmark;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnSendSync(Object *cpt, int32 id, int32 sendId, int32 syncValue, int32 e, int32 f, int32 z, int32 x) {
+ Object *target = _objMan->fetchObject(sendId);
+ target->o_sync = syncValue;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnWaitSync(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_logic = LOGIC_wait_for_sync;
+ return SCRIPT_STOP;
+}
+
+int Logic::cfnClickInteract(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ Object *tar = _objMan->fetchObject(target);
+ cpt = _objMan->fetchObject(PLAYER);
+ cpt->o_tree.o_script_level = 0;
+ cpt->o_tree.o_script_pc[0] = tar->o_interact;
+ cpt->o_tree.o_script_id[0] = tar->o_interact;
+ cpt->o_logic = LOGIC_script;
+ return SCRIPT_STOP;
+}
+
+int Logic::cfnSetScript(Object *cpt, int32 id, int32 target, int32 script, int32 e, int32 f, int32 z, int32 x) {
+ Object *tar = _objMan->fetchObject(target);
+ tar->o_tree.o_script_level = 0;
+ tar->o_tree.o_script_pc[0] = script;
+ tar->o_tree.o_script_id[0] = script;
+ tar->o_logic = LOGIC_script;
+ return SCRIPT_CONT;
+}
+
+int Logic::cfnPresetScript(Object *cpt, int32 id, int32 target, int32 script, int32 e, int32 f, int32 z, int32 x) {
+ Object *tar = _objMan->fetchObject(target);
+ tar->o_tree.o_script_level = 0;
+ tar->o_tree.o_script_pc[0] = script;
+ tar->o_tree.o_script_id[0] = script;
+ if (tar->o_logic == LOGIC_idle)
+ tar->o_logic = LOGIC_script;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnInteract(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ Object *tar = _objMan->fetchObject(target);
+ cpt->o_place = tar->o_place;
+
+ Object *floorObject = _objMan->fetchObject(tar->o_place);
+ cpt->o_scale_a = floorObject->o_scale_a;
+ cpt->o_scale_b = floorObject->o_scale_b;
+
+ cpt->o_tree.o_script_level++;
+ cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = tar->o_interact;
+ cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = tar->o_interact;
+
+ return SCRIPT_STOP;
+}
+
+int Logic::fnIssueEvent(Object *cpt, int32 id, int32 event, int32 delay, int32 e, int32 f, int32 z, int32 x) {
+ _eventMan->fnIssueEvent(cpt, id, event, delay);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnCheckForEvent(Object *cpt, int32 id, int32 pause, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ return _eventMan->fnCheckForEvent(cpt, id, pause);
+}
+
+int Logic::fnWipeHands(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _scriptVars[OBJECT_HELD] = 0;
+ _mouse->setLuggage(0, 0);
+ _menu->refresh(MENU_TOP);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnISpeak(Object *cpt, int32 id, int32 cdt, int32 textNo, int32 spr, int32 f, int32 z, int32 x) {
+ _speechClickDelay = 3;
+ if (((textNo & ~1) == 0x3f0012) && (!cdt) && (!spr)) {
+ cdt = GEOSTDLCDT; // workaround for missing animation when examining
+ spr = GEOSTDL; // the conductor on the train roof
+ }
+ cpt->o_logic = LOGIC_speech;
+
+ // first setup the talk animation
+ if (cdt && (!spr)) { // if 'cdt' is non-zero but 'spr' is zero - 'cdt' is an anim table tag
+ AnimSet *animTab = (AnimSet*)((uint8*)_resMan->openFetchRes(cdt) + sizeof(Header));
+ animTab += cpt->o_dir;
+
+ cpt->o_anim_resource = FROM_LE_32(animTab->cdt);
+ if (animTab->cdt)
+ cpt->o_resource = FROM_LE_32(animTab->spr);
+ _resMan->resClose(cdt);
+ } else {
+ cpt->o_anim_resource = cdt;
+ if (cdt)
+ cpt->o_resource = spr;
+ }
+ cpt->o_anim_pc = 0; // start anim from first frame
+ if (cpt->o_anim_resource) {
+ if (!cpt->o_resource)
+ error("ID %d: Can't run anim with cdt=%d, spr=%d", id, cdt, spr);
+
+ FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(cpt->o_resource), 0);
+ if (frameHead->offsetX && frameHead->offsetY) { // is this a boxed mega?
+ cpt->o_status |= STAT_SHRINK;
+ cpt->o_anim_x = cpt->o_xcoord;
+ cpt->o_anim_y = cpt->o_ycoord;
+ } else
+ cpt->o_status &= ~STAT_SHRINK;
+
+ _resMan->resClose(cpt->o_resource);
+ }
+ if (SwordEngine::_systemVars.playSpeech)
+ _speechRunning = _sound->startSpeech(textNo >> 16, textNo & 0xFFFF);
+ else
+ _speechRunning = false;
+ _speechFinished = false;
+ if (SwordEngine::_systemVars.showText || (!_speechRunning)) {
+ _textRunning = true;
+
+ char *text = _objMan->lockText(textNo);
+ cpt->o_speech_time = strlen(text) + 5;
+ uint32 textCptId = _textMan->lowTextManager((uint8*)text, cpt->o_speech_width, (uint8)cpt->o_speech_pen);
+ _objMan->unlockText(textNo);
+
+ Object * textCpt = _objMan->fetchObject(textCptId);
+ textCpt->o_screen = cpt->o_screen;
+ textCpt->o_target = textCptId;
+
+ // the graphic is a property of Text, so we don't lock/unlock it.
+ uint16 textSpriteWidth = FROM_LE_16(_textMan->giveSpriteData(textCpt->o_target)->width);
+ uint16 textSpriteHeight = FROM_LE_16(_textMan->giveSpriteData(textCpt->o_target)->height);
+
+ cpt->o_text_id = textCptId;
+
+ // now set text coords, above the player, usually
+
+#define TEXT_MARGIN 3 // distance kept from edges of screen
+#define ABOVE_HEAD 20 // distance kept above talking sprite
+ uint16 textX, textY;
+ if (((id == GEORGE) || ((id == NICO) && (_scriptVars[SCREEN] == 10))) && (!cpt->o_anim_resource)) {
+ // if George is doing Voice-Over text (centered at the bottom of the screen)
+ textX = _scriptVars[SCROLL_OFFSET_X] + 128 + (640 / 2) - textSpriteWidth / 2;
+ textY = _scriptVars[SCROLL_OFFSET_Y] + 128 + 400;
+ } else {
+ if ((id == GEORGE) && (_scriptVars[SCREEN] == 79))
+ textX = cpt->o_mouse_x2; // move it off george's head
+ else
+ textX = (cpt->o_mouse_x1 + cpt->o_mouse_x2) / 2 - textSpriteWidth / 2;
+
+ textY = cpt->o_mouse_y1 - textSpriteHeight - ABOVE_HEAD;
+ }
+ // now ensure text is within visible screen
+ uint16 textLeftMargin, textRightMargin, textTopMargin, textBottomMargin;
+ textLeftMargin = SCREEN_LEFT_EDGE + TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_X];
+ textRightMargin = SCREEN_RIGHT_EDGE - TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_X] - textSpriteWidth;
+ textTopMargin = SCREEN_TOP_EDGE + TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_Y];
+ textBottomMargin = SCREEN_BOTTOM_EDGE - TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_Y] - textSpriteHeight;
+
+ textCpt->o_anim_x = textCpt->o_xcoord = inRange(textLeftMargin, textX, textRightMargin);
+ textCpt->o_anim_y = textCpt->o_ycoord = inRange(textTopMargin, textY, textBottomMargin);
+ }
+ return SCRIPT_STOP;
+}
+
+//send instructions to mega in conversation with player
+//the instruction is interpreted by the script mega_interact
+int Logic::fnTheyDo(Object *cpt, int32 id, int32 tar, int32 instruc, int32 param1, int32 param2, int32 param3, int32 x) {
+ Object *target;
+ target = _objMan->fetchObject(tar);
+ target->o_down_flag = instruc; // instruction for the mega
+ target->o_ins1 = param1;
+ target->o_ins2 = param2;
+ target->o_ins3 = param3;
+ return SCRIPT_CONT;
+}
+
+//send an instruction to mega we're talking to and wait
+//until it has finished before returning to script
+int Logic::fnTheyDoWeWait(Object *cpt, int32 id, int32 tar, int32 instruc, int32 param1, int32 param2, int32 param3, int32 x) {
+ // workaround for scriptbug #928791: Freeze at hospital
+ // in at least one game version, a script forgets to set sam_returning back to zero
+ if ((tar == SAM) && (instruc == INS_talk) && (param2 == 2162856))
+ _scriptVars[SAM_RETURNING] = 0;
+ Object *target = _objMan->fetchObject(tar);
+ target->o_down_flag = instruc; // instruction for the mega
+ target->o_ins1 = param1;
+ target->o_ins2 = param2;
+ target->o_ins3 = param3;
+ target->o_status &= ~STAT_TALK_WAIT;
+
+ cpt->o_logic = LOGIC_wait_for_talk;
+ cpt->o_down_flag = tar;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnWeWait(Object *cpt, int32 id, int32 tar, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ Object *target = _objMan->fetchObject(tar);
+ target->o_status &= ~STAT_TALK_WAIT;
+
+ cpt->o_logic = LOGIC_wait_for_talk;
+ cpt->o_down_flag = tar;
+
+ return SCRIPT_STOP;
+}
+
+int Logic::fnChangeSpeechText(Object *cpt, int32 id, int32 tar, int32 width, int32 pen, int32 f, int32 z, int32 x) {
+ Object *target = _objMan->fetchObject(tar);
+ target->o_speech_width = width;
+ target->o_speech_pen = pen;
+ return SCRIPT_STOP;
+}
+
+//mega_interact has received an instruction it does not understand -
+//The game is halted for debugging. Maybe we'll remove this later.
+int Logic::fnTalkError(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ error("fnTalkError for id %d, instruction %d", id, cpt->o_down_flag);
+ return SCRIPT_STOP;
+}
+
+int Logic::fnStartTalk(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_down_flag = target;
+ cpt->o_logic = LOGIC_start_talk;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnCheckForTextLine(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _scriptVars[RETURN_VALUE] = _objMan->fnCheckForTextLine(id);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnAddTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_status |= STAT_TALK_WAIT;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnRemoveTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_status &= ~STAT_TALK_WAIT;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnNoHuman(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _mouse->fnNoHuman();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnAddHuman(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _mouse->fnAddHuman();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnBlankMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _mouse->fnBlankMouse();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnNormalMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _mouse->fnNormalMouse();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnLockMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _mouse->fnLockMouse();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnUnlockMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _mouse->fnUnlockMouse();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnSetMousePointer(Object *cpt, int32 id, int32 tag, int32 rate, int32 e, int32 f, int32 z, int32 x) {
+ _mouse->setPointer(tag, rate);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnSetMouseLuggage(Object *cpt, int32 id, int32 tag, int32 rate, int32 e, int32 f, int32 z, int32 x) {
+ _mouse->setLuggage(tag, rate);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnMouseOn(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_status |= STAT_MOUSE;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnMouseOff(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_status &= ~STAT_MOUSE;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnChooser(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _menu->fnChooser(cpt);
+ return SCRIPT_STOP;
+}
+
+int Logic::fnEndChooser(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _menu->fnEndChooser();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnStartMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _menu->fnStartMenu();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnEndMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _menu->fnEndMenu();
+ return SCRIPT_CONT;
+}
+
+int Logic::cfnReleaseMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _menu->cfnReleaseMenu();
+ return SCRIPT_STOP;
+}
+
+int Logic::fnAddSubject(Object *cpt, int32 id, int32 sub, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _menu->fnAddSubject(sub);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnAddObject(Object *cpt, int32 id, int32 objectNo, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _scriptVars[POCKET_1 + objectNo - 1] = 1; // basically means: carrying object objectNo = true;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnRemoveObject(Object *cpt, int32 id, int32 objectNo, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ _scriptVars[POCKET_1 + objectNo - 1] = 0;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnEnterSection(Object *cpt, int32 id, int32 screen, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ if (screen >= TOTAL_SECTIONS)
+ error("mega %d tried entering section %d", id, screen);
+
+ /* if (cpt->o_type == TYPE_PLAYER)
+ ^= this was the original condition from the game sourcecode.
+ not sure why it doesn't work*/
+ if (id == PLAYER)
+ _scriptVars[NEW_SCREEN] = screen;
+ else
+ cpt->o_screen = screen; // move the mega
+ _objMan->megaEntering(screen);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnLeaveSection(Object *cpt, int32 id, int32 oldScreen, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ if (oldScreen >= TOTAL_SECTIONS)
+ error("mega %d leaving section %d", id, oldScreen);
+ _objMan->megaLeaving(oldScreen, id);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnChangeFloor(Object *cpt, int32 id, int32 floor, int32 d, int32 e, int32 f, int32 z, int32 x) {
+ cpt->o_place = floor;
+ Object *floorCpt = _objMan->fetchObject(floor);
+ cpt->o_scale_a = floorCpt->o_scale_a;
+ cpt->o_scale_b = floorCpt->o_scale_b;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnWalk(Object *cpt, int32 id, int32 x, int32 y, int32 dir, int32 stance, int32 a, int32 b) {
+ if (stance > 0)
+ dir = 9;
+ cpt->o_walk_pc = 0;
+ cpt->o_route[1].frame = 512; // end of sequence
+ if (id == PLAYER)
+ _router->setPlayerTarget(x, y, dir, stance);
+
+ int32 routeRes = _router->routeFinder(id, cpt, x, y, dir);
+
+ if (id == PLAYER) {
+ _router->resetExtraData();
+ if ((routeRes == 1) || (routeRes == 2)) {
+ _scriptVars[MEGA_ON_GRID] = 0;
+ _scriptVars[REROUTE_GEORGE] = 0;
+ }
+ }
+ if ((routeRes == 1) || (routeRes == 2)) {
+ cpt->o_down_flag = 1; // 1 means okay.
+ // if both mouse buttons were pressed on an exit => skip george's walk
+ if ((id == GEORGE) && (_mouse->testEvent() == MOUSE_BOTH_BUTTONS)) {
+ int32 target = _scriptVars[CLICK_ID];
+ // exceptions: compacts that use hand pointers but are not actually exits
+ if ((target != LEFT_SCROLL_POINTER) && (target != RIGHT_SCROLL_POINTER) &&
+ (target != FLOOR_63) && (target != ROOF_63) && (target != GUARD_ROOF_63) &&
+ (target != LEFT_TREE_POINTER_71) && (target != RIGHT_TREE_POINTER_71)) {
+
+ target = _objMan->fetchObject(_scriptVars[CLICK_ID])->o_mouse_on;
+ if ((target >= SCR_exit0) && (target <= SCR_exit9)) {
+ fnStandAt(cpt,id,x,y,dir,stance,0,0);
+ return SCRIPT_STOP;
+ }
+ }
+ }
+ cpt->o_logic = LOGIC_AR_animate;
+ return SCRIPT_STOP;
+ } else if (routeRes == 3)
+ cpt->o_down_flag = 1; // pretend it was successful
+ else
+ cpt->o_down_flag = 0; // 0 means error
+
+ return SCRIPT_CONT;
+}
+
+int Logic::fnTurn(Object *cpt, int32 id, int32 dir, int32 stance, int32 c, int32 d, int32 a, int32 b) {
+ if (stance > 0)
+ dir = 9;
+ int route = _router->routeFinder(id, cpt, cpt->o_xcoord, cpt->o_ycoord, dir);
+
+ if (route)
+ cpt->o_down_flag = 1; //1 means ok
+ else
+ cpt->o_down_flag = 0; //0 means error
+
+ cpt->o_logic = LOGIC_AR_animate;
+ cpt->o_walk_pc = 0; //reset
+
+ return SCRIPT_STOP;
+}
+
+int Logic::fnStand(Object *cpt, int32 id, int32 dir, int32 stance, int32 c, int32 d, int32 a, int32 b) {
+ if ((dir < 0) || (dir > 8)) {
+ warning("fnStand:: invalid direction %d", dir);
+ return SCRIPT_CONT;
+ }
+ if (dir == 8)
+ dir = cpt->o_dir;
+ cpt->o_resource = cpt->o_walk_resource;
+ cpt->o_status |= STAT_SHRINK;
+ cpt->o_anim_x = cpt->o_xcoord;
+ cpt->o_anim_y = cpt->o_ycoord;
+ cpt->o_frame = 96 + dir;
+ cpt->o_dir = dir;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnStandAt(Object *cpt, int32 id, int32 x, int32 y, int32 dir, int32 stance, int32 a, int32 b) {
+ if ((dir < 0) || (dir > 8)) {
+ warning("fnStandAt:: invalid direction %d", dir);
+ return SCRIPT_CONT;
+ }
+ if (dir == 8)
+ dir = cpt->o_dir;
+ cpt->o_xcoord = x;
+ cpt->o_ycoord = y;
+ return fnStand(cpt, id, dir, stance, 0, 0, 0, 0);
+}
+
+int Logic::fnFace(Object *cpt, int32 id, int32 targetId, int32 b, int32 c, int32 d, int32 a, int32 z) {
+ Object *target = _objMan->fetchObject(targetId);
+ int32 x, y;
+ if ((target->o_type == TYPE_MEGA) || (target->o_type == TYPE_PLAYER)) {
+ x = target->o_xcoord;
+ y = target->o_ycoord;
+ } else {
+ x = (target->o_mouse_x1 + target->o_mouse_x2) / 2;
+ y = target->o_mouse_y2;
+ }
+ int32 megaTarDir = whatTarget(cpt->o_xcoord, cpt->o_ycoord, x, y);
+ fnTurn(cpt, id, megaTarDir, 0, 0, 0, 0, 0);
+ return SCRIPT_STOP;
+}
+
+int Logic::fnFaceXy(Object *cpt, int32 id, int32 x, int32 y, int32 c, int32 d, int32 a, int32 b) {
+ int megaTarDir = whatTarget(cpt->o_xcoord, cpt->o_ycoord, x, y);
+ fnTurn(cpt, id, megaTarDir, 0, 0, 0, 0, 0);
+ return SCRIPT_STOP;
+}
+
+int Logic::fnIsFacing(Object *cpt, int32 id, int32 targetId, int32 b, int32 c, int32 d, int32 a, int32 z) {
+ Object *target = _objMan->fetchObject(targetId);
+ int32 x, y, dir;
+ if ((target->o_type == TYPE_MEGA) || (target->o_type == TYPE_PLAYER)) {
+ x = target->o_xcoord;
+ y = target->o_ycoord;
+ dir = target->o_dir;
+ } else
+ error("fnIsFacing:: Target isn't a mega!");
+
+ int32 lookDir = whatTarget(x, y, cpt->o_xcoord, cpt->o_ycoord);
+ lookDir -= dir;
+ lookDir = ABS(lookDir);
+
+ if (lookDir > 4)
+ lookDir = 8 - lookDir;
+
+ _scriptVars[RETURN_VALUE] = lookDir;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnGetTo(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ Object *place = _objMan->fetchObject(cpt->o_place);
+
+ cpt->o_tree.o_script_level++;
+ cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = place->o_get_to_script;
+ cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = place->o_get_to_script;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnGetToError(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ debug(1, "fnGetToError: compact %d at place %d no get-to for target %d, click_id %d\n", id, cpt->o_place, cpt->o_target, _scriptVars[CLICK_ID]);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnRandom(Object *compact, int32 id, int32 min, int32 max, int32 e, int32 f, int32 z, int32 x) {
+ _scriptVars[RETURN_VALUE] = _rnd.getRandomNumberRng(min, max);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnGetPos(Object *cpt, int32 id, int32 targetId, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ Object *target = _objMan->fetchObject(targetId);
+ if ((target->o_type == TYPE_MEGA) || (target->o_type == TYPE_PLAYER)) {
+ _scriptVars[RETURN_VALUE] = target->o_xcoord;
+ _scriptVars[RETURN_VALUE_2] = target->o_ycoord;
+ } else {
+ _scriptVars[RETURN_VALUE] = (target->o_mouse_x1 + target->o_mouse_x2) / 2;
+ _scriptVars[RETURN_VALUE_2] = target->o_mouse_y2;
+ }
+ _scriptVars[RETURN_VALUE_3] = target->o_dir;
+
+ int32 megaSeperation;
+ if (targetId == DUANE)
+ megaSeperation = 70; // George & Duane stand with feet 70 pixels apart when at full scale
+ else if (targetId == BENOIR)
+ megaSeperation = 61; // George & Benoir
+ else
+ megaSeperation = 42; // George & Nico/Goinfre stand with feet 42 pixels apart when at full scale
+
+ if (target->o_status & STAT_SHRINK) {
+ int32 scale = (target->o_scale_a * target->o_ycoord + target->o_scale_b) / 256;
+ _scriptVars[RETURN_VALUE_4] = (megaSeperation * scale) / 256;
+ } else
+ _scriptVars[RETURN_VALUE_4] = megaSeperation;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnGetGamepadXy(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ // playstation only
+ return SCRIPT_CONT;
+}
+
+int Logic::fnPlayFx(Object *cpt, int32 id, int32 fxNo, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _scriptVars[RETURN_VALUE] = _sound->addToQueue(fxNo);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnStopFx(Object *cpt, int32 id, int32 fxNo, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _sound->fnStopFx(fxNo);
+ //_sound->removeFromQueue(fxNo);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnPlayMusic(Object *cpt, int32 id, int32 tuneId, int32 loopFlag, int32 c, int32 d, int32 z, int32 x) {
+ if (tuneId == 153)
+ return SCRIPT_CONT;
+ if (loopFlag == LOOPED)
+ _scriptVars[CURRENT_MUSIC] = tuneId; // so it gets restarted when saving & reloading
+ else
+ _scriptVars[CURRENT_MUSIC] = 0;
+
+ _music->startMusic(tuneId, loopFlag);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnStopMusic(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _scriptVars[CURRENT_MUSIC] = 0;
+ _music->fadeDown();
+ return SCRIPT_CONT;
+}
+
+int Logic::fnInnerSpace(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ error("fnInnerSpace() not working.");
+ return SCRIPT_STOP;
+}
+
+int Logic::fnSetScreen(Object *cpt, int32 id, int32 target, int32 screen, int32 c, int32 d, int32 z, int32 x) {
+ _objMan->fetchObject(target)->o_screen = screen;
+ return SCRIPT_CONT;
+}
+
+int Logic::fnPreload(Object *cpt, int32 id, int32 resId, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _resMan->resOpen(resId);
+ _resMan->resClose(resId);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnCheckCD(Object *cpt, int32 id, int32 screen, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ // only a dummy, here.
+ // the check is done in the mainloop
+ return SCRIPT_CONT;
+}
+
+int Logic::fnRestartGame(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ SwordEngine::_systemVars.forceRestart = true;
+ cpt->o_logic = LOGIC_quit;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnQuitGame(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ if (SwordEngine::_systemVars.isDemo) {
+ GUI::MessageDialog dialog("This is the end of the Broken Sword 1 Demo", "OK", NULL);
+ dialog.runModal();
+ SwordEngine::_systemVars.engineQuit = true;
+ } else
+ error("fnQuitGame() called");
+ return SCRIPT_STOP;
+}
+
+int Logic::fnDeathScreen(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+
+ if (_scriptVars[FINALE_OPTION_FLAG] == 4) // successful end of game!
+ SwordEngine::_systemVars.controlPanelMode = CP_THEEND;
+ else
+ SwordEngine::_systemVars.controlPanelMode = CP_DEATHSCREEN;
+
+ cpt->o_logic = LOGIC_quit;
+ return SCRIPT_STOP;
+}
+
+int Logic::fnSetParallax(Object *cpt, int32 id, int32 screen, int32 resId, int32 c, int32 d, int32 z, int32 x) {
+ _screen->fnSetParallax(screen, resId);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnTdebug(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ debug(1, "Script TDebug id %d code %d, %d", id, a, b);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnRedFlash(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _screen->fnFlash(FLASH_RED);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnBlueFlash(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _screen->fnFlash(FLASH_BLUE);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnYellow(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _screen->fnFlash(BORDER_YELLOW);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnGreen(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _screen->fnFlash(BORDER_GREEN);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnPurple(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _screen->fnFlash(BORDER_PURPLE);
+ return SCRIPT_CONT;
+}
+
+int Logic::fnBlack(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
+ _screen->fnFlash(BORDER_BLACK);
+ return SCRIPT_CONT;
+}
+
+uint16 Logic::inRange(uint16 a, uint16 b, uint16 c) {
+ return (a > b)? a : (((b > c) ? c : b));
+}
+
+void Logic::startPosCallFn(uint8 fnId, uint32 param1, uint32 param2, uint32 param3) {
+ Object *obj = NULL;
+ switch (fnId) {
+ case opcPlaySequence:
+ fnPlaySequence(NULL, 0, param1, 0, 0, 0, 0, 0);
+ break;
+ case opcAddObject:
+ fnAddObject(NULL, 0, param1, 0, 0, 0, 0, 0);
+ break;
+ case opcRemoveObject:
+ fnRemoveObject(NULL, 0, param1, 0, 0, 0, 0, 0);
+ break;
+ case opcMegaSet:
+ obj = _objMan->fetchObject(param1);
+ fnMegaSet(obj, param1, param2, param3, 0, 0, 0, 0);
+ break;
+ case opcNoSprite:
+ obj = _objMan->fetchObject(param1);
+ fnNoSprite(obj, param1, param2, param3, 0, 0, 0, 0);
+ break;
+ default:
+ error("Illegal fnCallfn argument %d", fnId);
+ }
+}
+
+void Logic::runStartScript(const uint8 *data) {
+ uint16 varId = 0;
+ uint8 fnId = 0;
+ uint32 param1 = 0;
+ while (*data != opcSeqEnd) {
+ switch (*data++) {
+ case opcCallFn:
+ fnId = *data++;
+ param1 = *data++;
+ startPosCallFn(fnId, param1, 0, 0);
+ break;
+ case opcCallFnLong:
+ fnId = *data++;
+ startPosCallFn(fnId, READ_LE_UINT32(data), READ_LE_UINT32(data + 4), READ_LE_UINT32(data + 8));
+ data += 12;
+ break;
+ case opcSetVar8:
+ varId = READ_LE_UINT16(data);
+ _scriptVars[varId] = data[2];
+ data += 3;
+ break;
+ case opcSetVar16:
+ varId = READ_LE_UINT16(data);
+ _scriptVars[varId] = READ_LE_UINT16(data + 2);
+ data += 4;
+ break;
+ case opcSetVar32:
+ varId = READ_LE_UINT16(data);
+ _scriptVars[varId] = READ_LE_UINT32(data + 2);
+ data += 6;
+ break;
+ case opcGeorge:
+ _scriptVars[CHANGE_X] = READ_LE_UINT16(data + 0);
+ _scriptVars[CHANGE_Y] = READ_LE_UINT16(data + 2);
+ _scriptVars[CHANGE_DIR] = data[4];
+ _scriptVars[CHANGE_PLACE] = READ_LE_UINT24(data + 5);
+ data += 8;
+ break;
+ case opcRunStart:
+ data = _startData[*data];
+ break;
+ case opcRunHelper:
+ data = _helperData[*data];
+ break;
+ default:
+ error("Unexpected opcode in StartScript");
+ }
+ }
+}
+
+void Logic::startPositions(uint32 pos) {
+ bool spainVisit2 = false;
+ if ((pos >= 956) && (pos <= 962)) {
+ spainVisit2 = true;
+ pos -= 900;
+ }
+ if ((pos > 80) || (_startData[pos] == NULL))
+ error("Starting in Section %d is not supported", pos);
+
+ Logic::_scriptVars[CHANGE_STANCE] = STAND;
+ Logic::_scriptVars[GEORGE_CDT_FLAG] = GEO_TLK_TABLE;
+
+ runStartScript(_startData[pos]);
+ if (spainVisit2)
+ runStartScript(_helperData[HELP_SPAIN2]);
+
+ if (pos == 0)
+ pos = 1;
+ Object *compact = _objMan->fetchObject(PLAYER);
+ fnEnterSection(compact, PLAYER, pos, 0, 0, 0, 0, 0); // (automatically opens the compact resource for that section)
+ SwordEngine::_systemVars.controlPanelMode = CP_NORMAL;
+ SwordEngine::_systemVars.wantFade = true;
+}
+
+const uint32 Logic::_scriptVarInit[NON_ZERO_SCRIPT_VARS][2] = {
+ { 42, 448}, { 43, 378}, { 51, 1}, { 92, 1}, { 147, 71}, { 201, 1},
+ { 209, 1}, { 215, 1}, { 242, 2}, { 244, 1}, { 246, 3}, { 247, 1},
+ { 253, 1}, { 297, 1}, { 398, 1}, { 508, 1}, { 605, 1}, { 606, 1},
+ { 701, 1}, { 709, 1}, { 773, 1}, { 843, 1}, { 907, 1}, { 923, 1},
+ { 966, 1}, { 988, 2}, {1058, 1}, {1059, 2}, {1060, 3}, {1061, 4},
+ {1062, 5}, {1063, 6}, {1064, 7}, {1065, 8}, {1066, 9}, {1067, 10},
+ {1068, 11}, {1069, 12}, {1070, 13}, {1071, 14}, {1072, 15}, {1073, 16},
+ {1074, 17}, {1075, 18}, {1076, 19}, {1077, 20}, {1078, 21}, {1079, 22},
+ {1080, 23}, {1081, 24}, {1082, 25}, {1083, 26}, {1084, 27}, {1085, 28},
+ {1086, 29}, {1087, 30}, {1088, 31}, {1089, 32}, {1090, 33}, {1091, 34},
+ {1092, 35}, {1093, 36}, {1094, 37}, {1095, 38}, {1096, 39}, {1097, 40},
+ {1098, 41}, {1099, 42}, {1100, 43}, {1101, 44}, {1102, 48}, {1103, 45},
+ {1104, 47}, {1105, 49}, {1106, 50}, {1107, 52}, {1108, 54}, {1109, 56},
+ {1110, 57}, {1111, 58}, {1112, 59}, {1113, 60}, {1114, 61}, {1115, 62},
+ {1116, 63}, {1117, 64}, {1118, 65}, {1119, 66}, {1120, 67}, {1121, 68},
+ {1122, 69}, {1123, 71}, {1124, 72}, {1125, 73}, {1126, 74}
+};
+
+} // End of namespace Sword1
diff --git a/engines/sword1/logic.h b/engines/sword1/logic.h
new file mode 100644
index 0000000000..32977dc83c
--- /dev/null
+++ b/engines/sword1/logic.h
@@ -0,0 +1,239 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSLOGIC_H
+#define BSLOGIC_H
+// combination of logic.c and scr_int.c
+
+#include "sword1/sworddefs.h"
+#include "sword1/objectman.h"
+#include "common/util.h"
+#include "sound/mixer.h"
+
+namespace Sword1 {
+
+#define NON_ZERO_SCRIPT_VARS 95
+#define NUM_SCRIPT_VARS 1179
+
+class Text;
+class Sound;
+class EventManager;
+class Menu;
+class Router;
+class Screen;
+class Mouse;
+class Music;
+
+class Logic;
+typedef int (Logic::*BSMcodeTable)(Object *, int32, int32, int32, int32, int32, int32, int32);
+
+class Logic {
+public:
+ Logic(ObjectMan *pObjMan, ResMan *resMan, Screen *pScreen, Mouse *pMouse, Sound *pSound, Music *pMusic, Menu *pMenu, OSystem *system, Audio::Mixer *mixer);
+ ~Logic(void);
+ void initialize(void);
+ void newScreen(uint32 screen);
+ void engine(void);
+ void updateScreenParams(void);
+ void runMouseScript(Object *cpt, int32 scriptId);
+ void startPositions(uint32 pos);
+
+ static uint32 _scriptVars[NUM_SCRIPT_VARS];
+// public for mouse (menu looking)
+ int cfnPresetScript (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+private:
+ ObjectMan *_objMan;
+ OSystem *_system;
+ Audio::Mixer *_mixer;
+ ResMan *_resMan;
+ Screen *_screen;
+ Sound *_sound;
+ Mouse *_mouse;
+ Router *_router;
+ Text *_textMan;
+ EventManager *_eventMan;
+ Menu *_menu;
+ Music *_music;
+ uint32 _newScript; // <= ugly, but I can't avoid it.
+ bool _speechRunning, _speechFinished, _textRunning;
+ uint8 _speechClickDelay;
+ Common::RandomSource _rnd;
+
+ int scriptManager(Object *compact, uint32 id);
+ void processLogic(Object *compact, uint32 id);
+ int interpretScript(Object *compact, int id, Header *scriptModule, int scriptBase, int scriptNum);
+
+ int logicWaitTalk(Object *compact);
+ int logicStartTalk(Object *compact);
+ int logicArAnimate(Object *compact, uint32 id);
+ int speechDriver(Object *compact);
+ int fullAnimDriver(Object *compact);
+ int animDriver(Object *compact);
+
+ static BSMcodeTable _mcodeTable[100];
+
+ uint16 inRange(uint16 a, uint16 b, uint16 c);
+
+//- mcodeTable:
+ int fnBackground (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnForeground (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSort (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnNoSprite (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnMegaSet (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnAnim (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetFrame (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnFullAnim (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnFullSetFrame (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnFadeDown (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnFadeUp (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnCheckFade (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetSpritePalette(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetWholePalette(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetFadeTargetPalette(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetPaletteToFade(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetPaletteToCut(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnPlaySequence (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+
+ int fnIdle (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnPause (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnPauseSeconds (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnQuit (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnKillId (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSuicide (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnNewScript (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSubScript (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnRestartScript (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetBookmark (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnGotoBookmark (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSendSync (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnWaitSync (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+
+ int cfnClickInteract(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int cfnSetScript (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+
+ int fnInteract (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnIssueEvent (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnCheckForEvent (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnWipeHands (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnISpeak (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnTheyDo (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnTheyDoWeWait (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnWeWait (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnChangeSpeechText(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnTalkError (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnStartTalk (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnCheckForTextLine(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnAddTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnRemoveTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+
+ int fnNoHuman (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnAddHuman (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnBlankMouse (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnNormalMouse (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnLockMouse (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnUnlockMouse (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetMousePointer(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetMouseLuggage(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnMouseOn (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnMouseOff (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnChooser (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnEndChooser (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnStartMenu (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnEndMenu (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+
+ int cfnReleaseMenu (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+
+ int fnAddSubject (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnAddObject (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnRemoveObject (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnEnterSection (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnLeaveSection (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnChangeFloor (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnWalk (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnTurn (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnStand (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnStandAt (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnFace (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnFaceXy (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnIsFacing (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnGetTo (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnGetToError (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnGetPos (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnGetGamepadXy (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnPlayFx (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnStopFx (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnPlayMusic (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnStopMusic (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnInnerSpace (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnRandom (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetScreen (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnPreload (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnCheckCD (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnRestartGame (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnQuitGame (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnDeathScreen (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnSetParallax (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnTdebug (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+
+ int fnRedFlash (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnBlueFlash (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnYellow (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnGreen (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnPurple (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ int fnBlack (Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
+ static const uint32 _scriptVarInit[NON_ZERO_SCRIPT_VARS][2];
+ static const uint8 *_startData[];
+ static const uint8 *_helperData[];
+ void startPosCallFn(uint8 fnId, uint32 param1, uint32 param2, uint32 param3);
+ void runStartScript(const uint8 *data);
+};
+
+enum StartPosOpcodes {
+ opcSeqEnd = 0,
+ opcCallFn,
+ opcCallFnLong,
+ opcSetVar8,
+ opcSetVar16,
+ opcSetVar32,
+ opcGeorge,
+ opcRunStart,
+ opcRunHelper,
+ opcPlaySequence,
+ opcAddObject,
+ opcRemoveObject,
+ opcMegaSet,
+ opcNoSprite
+};
+
+enum HelperScripts {
+ HELP_IRELAND = 0,
+ HELP_SYRIA,
+ HELP_SPAIN,
+ HELP_NIGHTTRAIN,
+ HELP_SCOTLAND,
+ HELP_WHITECOAT,
+ HELP_SPAIN2
+};
+
+} // End of namespace Sword1
+
+#endif //BSLOGIC_H
diff --git a/engines/sword1/memman.cpp b/engines/sword1/memman.cpp
new file mode 100644
index 0000000000..b9b0dc5d24
--- /dev/null
+++ b/engines/sword1/memman.cpp
@@ -0,0 +1,129 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/memman.h"
+#include "common/util.h"
+
+namespace Sword1 {
+
+MemMan::MemMan(void) {
+ _alloced = 0;
+ _memListFree = _memListFreeEnd = NULL;
+}
+
+MemMan::~MemMan(void) {
+ flush();
+ if (_alloced)
+ warning("deleting MemMan, still %d bytes alloced\n", _alloced);
+}
+
+void MemMan::alloc(MemHandle *bsMem, uint32 pSize, uint16 pCond) {
+ _alloced += pSize;
+ bsMem->data = (void*)malloc(pSize);
+ if (!bsMem->data)
+ error("MemMan::alloc(): Can't alloc %d bytes of memory.", pSize);
+ bsMem->cond = pCond;
+ bsMem->size = pSize;
+ if (pCond == MEM_CAN_FREE) {
+ warning("%d Bytes alloced as FREEABLE.", pSize); // why should one want to alloc mem if it can be freed?
+ addToFreeList(bsMem);
+ } else if (bsMem->next || bsMem->prev) // it's in our _freeAble list, remove it from there
+ removeFromFreeList(bsMem);
+ checkMemoryUsage();
+}
+
+void MemMan::freeNow(MemHandle *bsMem) {
+ if (bsMem->cond != MEM_FREED) {
+ _alloced -= bsMem->size;
+ removeFromFreeList(bsMem);
+ free(bsMem->data);
+ bsMem->cond = MEM_FREED;
+ }
+}
+
+void MemMan::setCondition(MemHandle *bsMem, uint16 pCond) {
+ if ((pCond == MEM_FREED) || (pCond > MEM_DONT_FREE))
+ error("MemMan::setCondition: program tried to set illegal memory condition");
+ if (bsMem->cond != pCond) {
+ bsMem->cond = pCond;
+ if (pCond == MEM_DONT_FREE)
+ removeFromFreeList(bsMem);
+ else if (pCond == MEM_CAN_FREE)
+ addToFreeList(bsMem);
+ }
+}
+
+void MemMan::flush(void) {
+ while (_memListFree) {
+ free(_memListFreeEnd->data);
+ _memListFreeEnd->data = NULL;
+ _memListFreeEnd->cond = MEM_FREED;
+ _alloced -= _memListFreeEnd->size;
+ removeFromFreeList(_memListFreeEnd);
+ }
+ if (_alloced)
+ warning("MemMan::flush: Something's wrong: still %d bytes alloced", _alloced);
+}
+
+void MemMan::checkMemoryUsage(void) {
+ while ((_alloced > MAX_ALLOC) && _memListFree) {
+ free(_memListFreeEnd->data);
+ _memListFreeEnd->data = NULL;
+ _memListFreeEnd->cond = MEM_FREED;
+ _alloced -= _memListFreeEnd->size;
+ removeFromFreeList(_memListFreeEnd);
+ }
+}
+
+void MemMan::addToFreeList(MemHandle *bsMem) {
+ if (bsMem->next || bsMem->prev) {
+ warning("addToFreeList: mem block is already in freeList");
+ return;
+ }
+ bsMem->prev = NULL;
+ bsMem->next = _memListFree;
+ if (bsMem->next)
+ bsMem->next->prev = bsMem;
+ _memListFree = bsMem;
+ if (!_memListFreeEnd)
+ _memListFreeEnd = _memListFree;
+}
+
+void MemMan::removeFromFreeList(MemHandle *bsMem) {
+ if (_memListFree == bsMem)
+ _memListFree = bsMem->next;
+ if (_memListFreeEnd == bsMem)
+ _memListFreeEnd = bsMem->prev;
+
+ if (bsMem->next)
+ bsMem->next->prev = bsMem->prev;
+ if (bsMem->prev)
+ bsMem->prev->next = bsMem->next;
+ bsMem->next = bsMem->prev = NULL;
+}
+
+void MemMan::initHandle(MemHandle *bsMem) {
+ memset(bsMem, 0, sizeof(MemHandle));
+}
+
+} // End of namespace Sword1
diff --git a/engines/sword1/memman.h b/engines/sword1/memman.h
new file mode 100644
index 0000000000..e230fac1af
--- /dev/null
+++ b/engines/sword1/memman.h
@@ -0,0 +1,64 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef MEMMAN_H
+#define MEMMAN_H
+
+#include "common/scummsys.h"
+
+namespace Sword1 {
+
+struct MemHandle {
+ void *data;
+ uint32 size;
+ uint32 refCount;
+ uint16 cond;
+ MemHandle *next, *prev;
+};
+// mem conditions:
+#define MEM_FREED 0
+#define MEM_CAN_FREE 1
+#define MEM_DONT_FREE 2
+
+#define MAX_ALLOC (6*1024*1024) // max amount of mem we want to alloc().
+
+class MemMan {
+public:
+ MemMan(void);
+ ~MemMan(void);
+ void alloc(MemHandle *bsMem, uint32 pSize, uint16 pCond = MEM_DONT_FREE);
+ void setCondition(MemHandle *bsMem, uint16 pCond);
+ void freeNow(MemHandle *bsMem);
+ void initHandle(MemHandle *bsMem);
+ void flush(void);
+private:
+ void addToFreeList(MemHandle *bsMem);
+ void removeFromFreeList(MemHandle *bsMem);
+ void checkMemoryUsage(void);
+ uint32 _alloced; //currently allocated memory
+ MemHandle *_memListFree;
+ MemHandle *_memListFreeEnd;
+};
+
+} // End of namespace Sword1
+
+#endif //MEMMAN_H
diff --git a/engines/sword1/menu.cpp b/engines/sword1/menu.cpp
new file mode 100644
index 0000000000..1266d39daa
--- /dev/null
+++ b/engines/sword1/menu.cpp
@@ -0,0 +1,393 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/menu.h"
+#include "sword1/resman.h"
+#include "common/scummsys.h"
+#include "common/util.h"
+#include "common/system.h"
+#include "sword1/mouse.h"
+#include "sword1/screen.h"
+#include "sword1/logic.h"
+
+namespace Sword1 {
+
+enum {
+ MENU_CLOSED,
+ MENU_CLOSING,
+ MENU_OPENING,
+ MENU_OPEN
+};
+
+const byte Menu::_fadeEffectTop[64] = {
+ 1, 7, 5, 3, 2, 4, 6, 0,
+ 3, 1, 7, 5, 4, 6, 0, 2,
+ 5, 3, 1, 7, 6, 0, 2, 4,
+ 7, 5, 3, 1, 0, 2, 4, 6,
+ 7, 5, 3, 1, 0, 2, 4, 6,
+ 5, 3, 1, 7, 6, 0, 2, 4,
+ 3, 1, 7, 5, 4, 6, 0, 2,
+ 1, 7, 5, 3, 2, 4, 6, 0
+};
+
+const byte Menu::_fadeEffectBottom[64] = {
+ 7, 6, 5, 4, 3, 2, 1, 0,
+ 0, 7, 6, 5, 4, 3, 2, 1,
+ 1, 0, 7, 6, 5, 4, 3, 2,
+ 2, 1, 0, 7, 6, 5, 4, 3,
+ 3, 2, 1, 0, 7, 6, 5, 4,
+ 4, 3, 2, 1, 0, 7, 6, 5,
+ 5, 4, 3, 2, 1, 0, 7, 6,
+ 6, 5, 4, 3, 2, 1, 0, 7
+};
+
+MenuIcon::MenuIcon(uint8 menuType, uint8 menuPos, uint32 resId, uint32 frame, Screen *screen) {
+ _menuType = menuType;
+ _menuPos = menuPos;
+ _resId = resId;
+ _frame = frame;
+ _screen = screen;
+ _selected = false;
+}
+
+bool MenuIcon::wasClicked(uint16 mouseX, uint16 mouseY) {
+ if (((_menuType == MENU_TOP) && (mouseY >= 40)) || ((_menuType == MENU_BOT) && (mouseY < 440)))
+ return false;
+ if ((mouseX >= _menuPos * 40) && (mouseX < (_menuPos + 1) * 40))
+ return true;
+ else
+ return false;
+}
+
+void MenuIcon::setSelect(bool pSel) {
+ _selected = pSel;
+}
+
+void MenuIcon::draw(const byte *fadeMask, int8 fadeStatus) {
+ uint16 x = _menuPos * 40;
+ uint16 y = (_menuType == MENU_TOP)?(0):(440);
+ _screen->showFrame(x, y, _resId, _frame + (_selected ? 1 : 0), fadeMask, fadeStatus);
+}
+
+Menu::Menu(Screen *pScreen, Mouse *pMouse) {
+ uint8 cnt;
+ _screen = pScreen;
+ _mouse = pMouse;
+ _subjectBarStatus = MENU_CLOSED;
+ _objectBarStatus = MENU_CLOSED;
+ _fadeSubject = 0;
+ _fadeObject = 0;
+ for (cnt = 0; cnt < 16; cnt++)
+ _subjects[cnt] = NULL;
+ for (cnt = 0; cnt < TOTAL_pockets; cnt++)
+ _objects[cnt] = NULL;
+ _inMenu = 0;
+}
+
+Menu::~Menu(void) {
+ int i;
+ // the menu may be open, so delete the icons
+ for (i = 0; i < TOTAL_pockets; i++) {
+ delete _objects[i];
+ _objects[i] = NULL;
+ }
+ for (i = 0; i < 16; i++) {
+ delete _subjects[i];
+ _subjects[i] = NULL;
+ }
+}
+
+void Menu::refreshMenus() {
+ if (_objectBarStatus == MENU_OPEN) {
+ buildMenu();
+ for (uint8 cnt = 0; cnt < 16; cnt++) {
+ if (_objects[cnt])
+ _objects[cnt]->draw();
+ else
+ _screen->showFrame(cnt * 40, 0, 0xffffffff, 0);
+ }
+ }
+ if (_subjectBarStatus == MENU_OPEN) {
+ buildSubjects();
+ for (uint8 cnt = 0; cnt < 16; cnt++) {
+ if (_subjects[cnt])
+ _subjects[cnt]->draw();
+ else
+ _screen->showFrame(cnt * 40, 440, 0xffffffff, 0);
+ }
+ }
+}
+
+uint8 Menu::checkMenuClick(uint8 menuType) {
+ uint16 mouseEvent = _mouse->testEvent();
+ if (!mouseEvent)
+ return 0;
+ uint16 x, y;
+ _mouse->giveCoords(&x, &y);
+ if (_subjectBarStatus == MENU_OPEN) {
+ // Conversation mode. Icons are highlighted on mouse-down, but
+ // the actual response is made on mouse-up.
+ if (menuType == MENU_BOT) {
+ if (Logic::_scriptVars[OBJECT_HELD] && (mouseEvent & BS1L_BUTTON_UP)) {
+ for (uint8 cnt = 0; cnt < Logic::_scriptVars[IN_SUBJECT]; cnt++) {
+ if (_subjectBar[cnt] == Logic::_scriptVars[OBJECT_HELD])
+ return cnt + 1;
+ }
+ } else if (mouseEvent & BS1L_BUTTON_DOWN) {
+ for (uint8 cnt = 0; cnt < Logic::_scriptVars[IN_SUBJECT]; cnt++) {
+ if (_subjects[cnt]->wasClicked(x, y)) {
+ Logic::_scriptVars[OBJECT_HELD] = _subjectBar[cnt];
+ refreshMenus();
+ break;
+ }
+ }
+ }
+ } else {
+ if (Logic::_scriptVars[OBJECT_HELD] && (mouseEvent & BS1L_BUTTON_UP)) {
+ for (uint8 cnt = 0; cnt < _inMenu; cnt++) {
+ if (_menuList[cnt] == Logic::_scriptVars[OBJECT_HELD])
+ return cnt + 1;
+ }
+ } else if (mouseEvent & BS1L_BUTTON_DOWN) {
+ for (uint8 cnt = 0; cnt < _inMenu; cnt++) {
+ if (_objects[cnt]->wasClicked(x, y)) {
+ Logic::_scriptVars[OBJECT_HELD] = _menuList[cnt];
+ refreshMenus();
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ // Normal use, i.e. inventory. Things happen on mouse-down.
+ if (menuType == MENU_TOP) {
+ for (uint8 cnt = 0; cnt < _inMenu; cnt++) {
+ if (_objects[cnt]->wasClicked(x, y)) {
+ if (mouseEvent & BS1R_BUTTON_DOWN) { // looking at item
+ Logic::_scriptVars[OBJECT_HELD] = _menuList[cnt];
+ Logic::_scriptVars[MENU_LOOKING] = 1;
+ Logic::_scriptVars[DEFAULT_ICON_TEXT] = _objectDefs[_menuList[cnt]].textDesc;
+ } else if (mouseEvent & BS1L_BUTTON_DOWN) {
+ if (Logic::_scriptVars[OBJECT_HELD]) {
+ if (Logic::_scriptVars[OBJECT_HELD] == _menuList[cnt]) {
+ _mouse->setLuggage(0, 0);
+ Logic::_scriptVars[OBJECT_HELD] = 0; // reselected => deselect it
+ } else { // the player is clicking another item on this one.
+ // run its use-script, if there is one
+ Logic::_scriptVars[SECOND_ITEM] = _menuList[cnt];
+ _mouse->setLuggage(0, 0);
+ }
+ } else {
+ Logic::_scriptVars[OBJECT_HELD] = _menuList[cnt];
+ _mouse->setLuggage(_objectDefs[_menuList[cnt]].luggageIconRes, 0);
+ }
+ }
+ refreshMenus();
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void Menu::buildSubjects(void) {
+ uint8 cnt;
+ for (cnt = 0; cnt < 16; cnt++)
+ if (_subjects[cnt]) {
+ delete _subjects[cnt];
+ _subjects[cnt] = NULL;
+ }
+ for (cnt = 0; cnt < Logic::_scriptVars[IN_SUBJECT]; cnt++) {
+ uint32 res = _subjectList[(_subjectBar[cnt] & 65535) - BASE_SUBJECT].subjectRes;
+ uint32 frame = _subjectList[(_subjectBar[cnt] & 65535) - BASE_SUBJECT].frameNo;
+ _subjects[cnt] = new MenuIcon(MENU_BOT, cnt, res, frame, _screen);
+ if (Logic::_scriptVars[OBJECT_HELD])
+ _subjects[cnt]->setSelect(_subjectBar[cnt] == Logic::_scriptVars[OBJECT_HELD]);
+ else
+ _subjects[cnt]->setSelect(true);
+ }
+}
+
+void Menu::refresh(uint8 menuType) {
+ uint i;
+
+ if (menuType == MENU_TOP) {
+ if (_objectBarStatus == MENU_OPENING || _objectBarStatus == MENU_CLOSING) {
+ for (i = 0; i < 16; i++) {
+ if (_objects[i])
+ _objects[i]->draw(_fadeEffectTop, _fadeObject);
+ else
+ _screen->showFrame(i * 40, 0, 0xffffffff, 0, _fadeEffectTop, _fadeObject);
+ }
+ }
+ if (_objectBarStatus == MENU_OPENING) {
+ if (_fadeObject < 8)
+ _fadeObject++;
+ else
+ _objectBarStatus = MENU_OPEN;
+ } else if (_objectBarStatus == MENU_CLOSING) {
+ if (_fadeObject > 0)
+ _fadeObject--;
+ else {
+ for (i = 0; i < _inMenu; i++) {
+ delete _objects[i];
+ _objects[i] = NULL;
+ }
+ _objectBarStatus = MENU_CLOSED;
+ }
+ }
+ } else {
+ if (_subjectBarStatus == MENU_OPENING || _subjectBarStatus == MENU_CLOSING) {
+ for (i = 0; i < 16; i++) {
+ if (_subjects[i])
+ _subjects[i]->draw(_fadeEffectBottom, _fadeSubject);
+ else
+ _screen->showFrame(i * 40, 440, 0xffffffff, 0, _fadeEffectBottom, _fadeSubject);
+ }
+ }
+ if (_subjectBarStatus == MENU_OPENING) {
+ if (_fadeSubject < 8)
+ _fadeSubject++;
+ else
+ _subjectBarStatus = MENU_OPEN;
+ } else if (_subjectBarStatus == MENU_CLOSING) {
+ if (_fadeSubject > 0)
+ _fadeSubject--;
+ else {
+ for (i = 0; i < Logic::_scriptVars[IN_SUBJECT]; i++) {
+ delete _subjects[i];
+ _subjects[i] = NULL;
+ }
+ _subjectBarStatus = MENU_CLOSED;
+ }
+ }
+ }
+}
+
+void Menu::buildMenu(void) {
+ uint32 *pockets = Logic::_scriptVars + POCKET_1;
+ for (uint8 cnt = 0; cnt < _inMenu; cnt++)
+ if (_objects[cnt]) {
+ delete _objects[cnt];
+ _objects[cnt] = NULL;
+ }
+ _inMenu = 0;
+ for (uint32 pocketNo = 0; pocketNo < TOTAL_pockets; pocketNo++)
+ if (pockets[pocketNo]) {
+ _menuList[_inMenu] = pocketNo + 1;
+ _inMenu++;
+ }
+ for (uint32 menuSlot = 0; menuSlot < _inMenu; menuSlot++) {
+ _objects[menuSlot] = new MenuIcon(MENU_TOP, menuSlot, _objectDefs[_menuList[menuSlot]].bigIconRes, _objectDefs[_menuList[menuSlot]].bigIconFrame, _screen);
+ uint32 objHeld = Logic::_scriptVars[OBJECT_HELD];
+
+ // check highlighting
+ if (Logic::_scriptVars[MENU_LOOKING] || _subjectBarStatus == MENU_OPEN) { // either we're in the chooser or we're doing a 'LOOK AT'
+ if ((!objHeld) || (objHeld == _menuList[menuSlot]))
+ _objects[menuSlot]->setSelect(true);
+ } else if (Logic::_scriptVars[SECOND_ITEM]) { // clicked luggage onto 2nd icon - we need to colour-highlight the 2 relevant icons & grey out the rest
+ if ((_menuList[menuSlot] == objHeld) || (_menuList[menuSlot] == Logic::_scriptVars[SECOND_ITEM]))
+ _objects[menuSlot]->setSelect(true);
+ } else { // this object is selected - ie. GREYED OUT
+ if (objHeld != _menuList[menuSlot])
+ _objects[menuSlot]->setSelect(true);
+ }
+ }
+}
+
+void Menu::showMenu(uint8 menuType) {
+ if (menuType == MENU_TOP) {
+ if (_objectBarStatus == MENU_OPEN) {
+ for (uint8 cnt = 0; cnt < 16; cnt++) {
+ if (_objects[cnt])
+ _objects[cnt]->draw();
+ else
+ _screen->showFrame(cnt * 40, 0, 0xffffffff, 0);
+ }
+ } else if (_objectBarStatus == MENU_CLOSED) {
+ _objectBarStatus = MENU_OPENING;
+ _fadeObject = 0;
+ } else if (_objectBarStatus == MENU_CLOSING)
+ _objectBarStatus = MENU_OPENING;
+ }
+}
+
+void Menu::fnStartMenu(void) {
+ Logic::_scriptVars[OBJECT_HELD] = 0; // icon no longer selected
+ Logic::_scriptVars[SECOND_ITEM] = 0; // second icon no longer selected (after using one on another)
+ Logic::_scriptVars[MENU_LOOKING] = 0; // no longer 'looking at' an icon
+ buildMenu();
+ showMenu(MENU_TOP);
+}
+
+void Menu::fnEndMenu(void) {
+ if (_objectBarStatus != MENU_CLOSED)
+ _objectBarStatus = MENU_CLOSING;
+}
+
+void Menu::fnChooser(Object *compact) {
+ Logic::_scriptVars[OBJECT_HELD] = 0;
+ _mouse->setLuggage(0, 0);
+ buildSubjects();
+ compact->o_logic = LOGIC_choose;
+ _mouse->controlPanel(true); // so the mouse cursor will be shown.
+ _subjectBarStatus = MENU_OPENING;
+}
+
+void Menu::fnEndChooser(void) {
+ Logic::_scriptVars[OBJECT_HELD] = 0;
+ _subjectBarStatus = MENU_CLOSING;
+ _objectBarStatus = MENU_CLOSING;
+ _mouse->controlPanel(false);
+ _mouse->setLuggage(0, 0);
+}
+
+void Menu::checkTopMenu(void) {
+ if (_objectBarStatus == MENU_OPEN)
+ checkMenuClick(MENU_TOP);
+}
+
+int Menu::logicChooser(Object *compact) {
+ uint8 objSelected = 0;
+ if (_objectBarStatus == MENU_OPEN)
+ objSelected = checkMenuClick(MENU_TOP);
+ if (!objSelected)
+ objSelected = checkMenuClick(MENU_BOT);
+ if (objSelected) {
+ compact->o_logic = LOGIC_script;
+ return 1;
+ }
+ return 0;
+}
+
+void Menu::fnAddSubject(int32 sub) {
+ _subjectBar[Logic::_scriptVars[IN_SUBJECT]] = sub;
+ Logic::_scriptVars[IN_SUBJECT]++;
+}
+
+void Menu::cfnReleaseMenu(void) {
+ _objectBarStatus = MENU_CLOSING;
+}
+
+} // End of namespace Sword1
diff --git a/engines/sword1/menu.h b/engines/sword1/menu.h
new file mode 100644
index 0000000000..134ad9ab30
--- /dev/null
+++ b/engines/sword1/menu.h
@@ -0,0 +1,109 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSMENU_H
+#define BSMENU_H
+
+#include "sword1/sworddefs.h"
+#include "sword1/object.h"
+
+namespace Sword1 {
+
+class Screen;
+class Mouse;
+class ResMan;
+
+#define MENU_TOP 0
+#define MENU_BOT 1
+
+struct Subject {
+ uint32 subjectRes;
+ uint32 frameNo;
+};
+
+struct MenuObject {
+ int32 textDesc;
+ uint32 bigIconRes;
+ uint32 bigIconFrame;
+ uint32 luggageIconRes;
+ uint32 useScript;
+};
+
+class MenuIcon {
+public:
+ MenuIcon(uint8 menuType, uint8 menuPos, uint32 resId, uint32 frame, Screen *screen);
+ bool wasClicked(uint16 mouseX, uint16 mouseY);
+ void setSelect(bool pSel);
+ void draw(const byte *fadeMask = NULL, int8 fadeStatus = 0);
+
+private:
+ uint8 _menuType, _menuPos;
+ uint32 _resId, _frame;
+ bool _selected;
+ Screen *_screen;
+};
+
+class Menu {
+public:
+ Menu(Screen *pScreen, Mouse *pMouse);
+ ~Menu(void);
+ void fnChooser(Object *compact);
+ void fnEndChooser(void);
+ void fnAddSubject(int32 sub);
+ void cfnReleaseMenu(void);
+ int logicChooser(Object *compact);
+ void engine(void);
+ void refresh(uint8 menuType);
+ void fnStartMenu(void);
+ void fnEndMenu(void);
+ void checkTopMenu(void);
+ static const MenuObject _objectDefs[TOTAL_pockets + 1];
+
+private:
+ void buildSubjects(void);
+ void buildMenu(void);
+ void showMenu(uint8 menuType);
+ byte _subjectBarStatus;
+ byte _objectBarStatus;
+ int8 _fadeSubject;
+ int8 _fadeObject;
+ void refreshMenus(void);
+ uint8 checkMenuClick(uint8 menuType);
+ //- lower menu, speech subjects:
+ MenuIcon *_subjects[16];
+ uint32 _subjectBar[16];
+ //- top menu, items
+ MenuIcon *_objects[TOTAL_pockets];
+ uint32 _menuList[TOTAL_pockets];
+ uint32 _inMenu;
+
+ Screen *_screen;
+ Mouse *_mouse;
+ static const Subject _subjectList[TOTAL_subjects];
+
+ static const byte _fadeEffectTop[64];
+ static const byte _fadeEffectBottom[64];
+};
+
+} // End of namespace Sword1
+
+#endif //BSMENU_H
diff --git a/engines/sword1/module.mk b/engines/sword1/module.mk
new file mode 100644
index 0000000000..9f4b3f2c3a
--- /dev/null
+++ b/engines/sword1/module.mk
@@ -0,0 +1,32 @@
+MODULE := engines/sword1
+
+MODULE_OBJS := \
+ engines/sword1/animation.o \
+ engines/sword1/control.o \
+ engines/sword1/credits.o \
+ engines/sword1/debug.o \
+ engines/sword1/eventman.o \
+ engines/sword1/logic.o \
+ engines/sword1/memman.o \
+ engines/sword1/menu.o \
+ engines/sword1/mouse.o \
+ engines/sword1/music.o \
+ engines/sword1/objectman.o \
+ engines/sword1/resman.o \
+ engines/sword1/router.o \
+ engines/sword1/screen.o \
+ engines/sword1/sound.o \
+ engines/sword1/staticres.o \
+ engines/sword1/sword1.o \
+ engines/sword1/text.o
+
+MODULE_DIRS += \
+ engines/sword1
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/sword1/mouse.cpp b/engines/sword1/mouse.cpp
new file mode 100644
index 0000000000..a84708c6b9
--- /dev/null
+++ b/engines/sword1/mouse.cpp
@@ -0,0 +1,313 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/mouse.h"
+#include "sword1/menu.h"
+#include "sword1/screen.h"
+#include "sword1/logic.h"
+#include "sword1/resman.h"
+#include "sword1/objectman.h"
+#include "sword1/sworddefs.h"
+#include "common/system.h"
+#include "sword1/swordres.h"
+#include "sword1/menu.h"
+
+namespace Sword1 {
+
+Mouse::Mouse(OSystem *system, ResMan *pResMan, ObjectMan *pObjMan) {
+ _resMan = pResMan;
+ _objMan = pObjMan;
+ _system = system;
+ _currentPtr = NULL;
+}
+
+Mouse::~Mouse(void) {
+ setLuggage(0, 0);
+ setPointer(0, 0);
+
+ for (uint8 cnt = 0; cnt < 17; cnt++) // close mouse cursor resources
+ _resMan->resClose(MSE_POINTER + cnt);
+}
+
+void Mouse::initialize(void) {
+ _numObjs = 0;
+ Logic::_scriptVars[MOUSE_STATUS] = 0; // mouse off and unlocked
+ _getOff = 0;
+ _inTopMenu = false;
+ _lastState = 0;
+ _mouseOverride = false;
+ _currentPtrId = _currentLuggageId = 0;
+
+ for (uint8 cnt = 0; cnt < 17; cnt++) // force res manager to keep mouse
+ _resMan->resOpen(MSE_POINTER + cnt); // cursors in memory all the time
+
+ _system->showMouse(false);
+ createPointer(0, 0);
+}
+
+void Mouse::controlPanel(bool on) { // true on entering cpanel, false when leaving
+ static uint32 savedPtrId = 0;
+ if (on) {
+ savedPtrId = _currentPtrId;
+ _mouseOverride = true;
+ setLuggage(0, 0);
+ setPointer(MSE_POINTER, 0);
+ } else {
+ _currentPtrId = savedPtrId;
+ _mouseOverride = false;
+ setLuggage(_currentLuggageId, 0);
+ setPointer(_currentPtrId, 0);
+ }
+}
+
+void Mouse::useLogicAndMenu(Logic *pLogic, Menu *pMenu) {
+ _logic = pLogic;
+ _menu = pMenu;
+}
+
+void Mouse::addToList(int id, Object *compact) {
+ _objList[_numObjs].id = id;
+ _objList[_numObjs].compact = compact;
+ _numObjs++;
+}
+
+void Mouse::engine(uint16 x, uint16 y, uint16 eventFlags) {
+ _state = 0; // all mouse events are flushed after one cycle.
+ if (_lastState) { // delay all events by one cycle to notice L_button + R_button clicks correctly.
+ _state = _lastState | eventFlags;
+ _lastState = 0;
+ } else if (eventFlags)
+ _lastState = eventFlags;
+
+ // if we received both, mouse down and mouse up event in this cycle, resort them so that
+ // we'll receive the up event in the next one.
+ if ((_state & MOUSE_DOWN_MASK) && (_state & MOUSE_UP_MASK)) {
+ _lastState = _state & MOUSE_UP_MASK;
+ _state &= MOUSE_DOWN_MASK;
+ }
+
+ _mouseX = x;
+ _mouseY = y;
+ if (!(Logic::_scriptVars[MOUSE_STATUS] & 1)) { // no human?
+ _numObjs = 0;
+ return; // no human, so we don't want the mouse engine
+ }
+
+ if (!Logic::_scriptVars[TOP_MENU_DISABLED]) {
+ if (y < 40) { // okay, we are in the top menu.
+ if (!_inTopMenu) { // are we just entering it?
+ if (!Logic::_scriptVars[OBJECT_HELD])
+ _menu->fnStartMenu();
+ setPointer(MSE_POINTER, 0);
+ }
+ _menu->checkTopMenu();
+ _inTopMenu = true;
+ } else if (_inTopMenu) { // we're not in the menu. did we just leave it?
+ if (!Logic::_scriptVars[OBJECT_HELD])
+ _menu->fnEndMenu();
+ _inTopMenu = false;
+ }
+ } else if (_inTopMenu) {
+ _menu->fnEndMenu();
+ _inTopMenu = false;
+ }
+
+ Logic::_scriptVars[MOUSE_X] = Logic::_scriptVars[SCROLL_OFFSET_X] + x + 128;
+ Logic::_scriptVars[MOUSE_Y] = Logic::_scriptVars[SCROLL_OFFSET_Y] + y + 128 - 40;
+
+ //-
+ int32 touchedId = 0;
+ uint16 clicked = 0;
+ if (y > 40) {
+ for (uint16 priority = 0; (priority < 10) && (!touchedId); priority++) {
+ for (uint16 cnt = 0; (cnt < _numObjs) && (!touchedId); cnt++) {
+ if ((_objList[cnt].compact->o_priority == priority) &&
+ (Logic::_scriptVars[MOUSE_X] >= (uint32)_objList[cnt].compact->o_mouse_x1) &&
+ (Logic::_scriptVars[MOUSE_X] <= (uint32)_objList[cnt].compact->o_mouse_x2) &&
+ (Logic::_scriptVars[MOUSE_Y] >= (uint32)_objList[cnt].compact->o_mouse_y1) &&
+ (Logic::_scriptVars[MOUSE_Y] <= (uint32)_objList[cnt].compact->o_mouse_y2)) {
+ touchedId = _objList[cnt].id;
+ clicked = cnt;
+ }
+ }
+ }
+ if (touchedId != (int)Logic::_scriptVars[SPECIAL_ITEM]) { //the mouse collision situation has changed in one way or another
+ Logic::_scriptVars[SPECIAL_ITEM] = touchedId;
+ if (_getOff) { // there was something else selected before, run its get-off script
+ _logic->runMouseScript(NULL, _getOff);
+ _getOff = 0;
+ }
+ if (touchedId) { // there's something new selected, now.
+ if (_objList[clicked].compact->o_mouse_on) //run its get on
+ _logic->runMouseScript(_objList[clicked].compact, _objList[clicked].compact->o_mouse_on);
+
+ _getOff = _objList[clicked].compact->o_mouse_off; //setup get-off for later
+ }
+ }
+ } else
+ Logic::_scriptVars[SPECIAL_ITEM] = 0;
+ if (_state & MOUSE_DOWN_MASK) {
+ if (_inTopMenu) {
+ if (Logic::_scriptVars[SECOND_ITEM])
+ _logic->runMouseScript(NULL, _menu->_objectDefs[Logic::_scriptVars[SECOND_ITEM]].useScript);
+ if (Logic::_scriptVars[MENU_LOOKING])
+ _logic->cfnPresetScript(NULL, -1, PLAYER, SCR_menu_look, 0, 0, 0, 0);
+ }
+
+ Logic::_scriptVars[MOUSE_BUTTON] = _state & MOUSE_DOWN_MASK;
+ if (Logic::_scriptVars[SPECIAL_ITEM]) {
+ Object *compact = _objMan->fetchObject(Logic::_scriptVars[SPECIAL_ITEM]);
+ _logic->runMouseScript(compact, compact->o_mouse_click);
+ }
+ }
+ _numObjs = 0;
+}
+
+uint16 Mouse::testEvent(void) {
+ return _state;
+}
+
+void Mouse::createPointer(uint32 ptrId, uint32 luggageId) {
+ if (_currentPtr) {
+ free(_currentPtr);
+ _currentPtr = NULL;
+ }
+ if (ptrId) {
+ MousePtr *lugg = NULL;
+ MousePtr *ptr = (MousePtr*)_resMan->openFetchRes(ptrId);
+ uint16 resSizeX = FROM_LE_16(ptr->sizeX);
+ uint16 resSizeY = FROM_LE_16(ptr->sizeY);
+ uint16 noFrames = FROM_LE_16(ptr->numFrames);
+ if (luggageId) {
+ lugg = (MousePtr*)_resMan->openFetchRes(luggageId);
+ resSizeX = MAX(resSizeX, (uint16)((resSizeX / 2) + FROM_LE_16(lugg->sizeX)));
+ resSizeY = MAX(resSizeY, (uint16)((resSizeY / 2) + FROM_LE_16(lugg->sizeY)));
+ }
+ _currentPtr = (MousePtr*)malloc(sizeof(MousePtr) + resSizeX * resSizeY * noFrames);
+ _currentPtr->hotSpotX = FROM_LE_16(ptr->hotSpotX);
+ _currentPtr->hotSpotY = FROM_LE_16(ptr->hotSpotY);
+ _currentPtr->numFrames = noFrames;
+ _currentPtr->sizeX = resSizeX;
+ _currentPtr->sizeY = resSizeY;
+ uint8 *ptrData = (uint8*)_currentPtr + sizeof(MousePtr);
+ memset(ptrData, 255, resSizeX * resSizeY * noFrames);
+ if (luggageId) {
+ uint8 *dstData = ptrData + resSizeX - FROM_LE_16(lugg->sizeX);
+ for (uint32 frameCnt = 0; frameCnt < noFrames; frameCnt++) {
+ uint8 *luggSrc = (uint8*)lugg + sizeof(MousePtr);
+ dstData += (resSizeY - FROM_LE_16(lugg->sizeY)) * resSizeX;
+ for (uint32 cnty = 0; cnty < FROM_LE_16(lugg->sizeY); cnty++) {
+ for (uint32 cntx = 0; cntx < FROM_LE_16(lugg->sizeX); cntx++)
+ if (luggSrc[cntx])
+ dstData[cntx] = luggSrc[cntx];
+ dstData += resSizeX;
+ luggSrc += FROM_LE_16(lugg->sizeX);
+ }
+ }
+ _resMan->resClose(luggageId);
+ }
+ uint8 *dstData = ptrData;
+ uint8 *srcData = (uint8*)ptr + sizeof(MousePtr);
+ for (uint32 frameCnt = 0; frameCnt < noFrames; frameCnt++) {
+ for (uint32 cnty = 0; cnty < FROM_LE_16(ptr->sizeY); cnty++) {
+ for (uint32 cntx = 0; cntx < FROM_LE_16(ptr->sizeX); cntx++)
+ if (srcData[cntx])
+ dstData[cntx] = srcData[cntx];
+ srcData += FROM_LE_16(ptr->sizeX);
+ dstData += resSizeX;
+ }
+ dstData += (resSizeY - FROM_LE_16(ptr->sizeY)) * resSizeX;
+ }
+ _resMan->resClose(ptrId);
+ }
+}
+
+void Mouse::setPointer(uint32 resId, uint32 rate) {
+ _currentPtrId = resId;
+ _frame = 0;
+
+ createPointer(resId, _currentLuggageId);
+
+ if ((resId == 0) || (!(Logic::_scriptVars[MOUSE_STATUS] & 1) && (!_mouseOverride))) {
+ _system->setMouseCursor(NULL, 0, 0, 0, 0);
+ _system->showMouse(false);
+ } else {
+ animate();
+ _system->showMouse(true);
+ }
+}
+
+void Mouse::setLuggage(uint32 resId, uint32 rate) {
+ _currentLuggageId = resId;
+ _frame = 0;
+ createPointer(_currentPtrId, resId);
+}
+
+void Mouse::animate(void) {
+ if ((Logic::_scriptVars[MOUSE_STATUS] == 1) || (_mouseOverride && _currentPtr)) {
+ _frame = (_frame + 1) % _currentPtr->numFrames;
+ uint8 *ptrData = (uint8*)_currentPtr + sizeof(MousePtr);
+ ptrData += _frame * _currentPtr->sizeX * _currentPtr->sizeY;
+ _system->setMouseCursor(ptrData, _currentPtr->sizeX, _currentPtr->sizeY, _currentPtr->hotSpotX, _currentPtr->hotSpotY);
+ }
+}
+
+void Mouse::fnNoHuman(void) {
+ if (Logic::_scriptVars[MOUSE_STATUS] & 2) // locked, can't do anything
+ return ;
+ Logic::_scriptVars[MOUSE_STATUS] = 0; // off & unlocked
+ setLuggage(0, 0);
+ setPointer(0, 0);
+}
+
+void Mouse::fnAddHuman(void) {
+ if (Logic::_scriptVars[MOUSE_STATUS] & 2) // locked, can't do anything
+ return ;
+ Logic::_scriptVars[MOUSE_STATUS] = 1;
+ Logic::_scriptVars[SPECIAL_ITEM] = 0;
+ _getOff = SCR_std_off;
+ setPointer(MSE_POINTER, 0);
+}
+
+void Mouse::fnBlankMouse(void) {
+ setPointer(0, 0);
+}
+
+void Mouse::fnNormalMouse(void) {
+ setPointer(MSE_POINTER, 0);
+}
+
+void Mouse::fnLockMouse(void) {
+ Logic::_scriptVars[MOUSE_STATUS] |= 2;
+}
+
+void Mouse::fnUnlockMouse(void) {
+ Logic::_scriptVars[MOUSE_STATUS] &= 1;
+}
+
+void Mouse::giveCoords(uint16 *x, uint16 *y) {
+ *x = _mouseX;
+ *y = _mouseY;
+}
+
+} // End of namespace Sword1
diff --git a/engines/sword1/mouse.h b/engines/sword1/mouse.h
new file mode 100644
index 0000000000..8512d85791
--- /dev/null
+++ b/engines/sword1/mouse.h
@@ -0,0 +1,113 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSMOUSE_H
+#define BSMOUSE_H
+
+#include "common/scummsys.h"
+#include "sword1/sworddefs.h"
+#include "sword1/object.h"
+
+class OSystem;
+
+namespace Sword1 {
+
+#define MAX_MOUSE 30
+
+#define BS1L_BUTTON_DOWN 2
+#define BS1L_BUTTON_UP 4
+#define BS1R_BUTTON_DOWN 8
+#define BS1R_BUTTON_UP 16
+#define BS1_WHEEL_UP 32
+#define BS1_WHEEL_DOWN 64
+#define MOUSE_BOTH_BUTTONS (BS1L_BUTTON_DOWN | BS1R_BUTTON_DOWN)
+#define MOUSE_DOWN_MASK (BS1L_BUTTON_DOWN | BS1R_BUTTON_DOWN)
+#define MOUSE_UP_MASK (BS1L_BUTTON_UP | BS1R_BUTTON_UP)
+
+struct MouseObj {
+ int id;
+ Object *compact;
+};
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct MousePtr {
+ uint16 numFrames;
+ uint16 sizeX;
+ uint16 sizeY;
+ uint16 hotSpotX;
+ uint16 hotSpotY;
+ uint8 dummyData[0x30];
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+class Logic;
+class Menu;
+class ResMan;
+class ObjectMan;
+
+class Mouse {
+public:
+ Mouse(OSystem *system, ResMan *pResMan, ObjectMan *pObjMan);
+ ~Mouse(void);
+ void initialize(void);
+ void addToList(int id, Object *compact);
+ void useLogicAndMenu(Logic *pLogic, Menu *pMenu);
+ void setLuggage(uint32 resID, uint32 rate);
+ void setPointer(uint32 resID, uint32 rate);
+ void animate(void);
+ void engine(uint16 x, uint16 y, uint16 eventFlags);
+ uint16 testEvent(void);
+ void giveCoords(uint16 *x, uint16 *y);
+ void fnNoHuman(void);
+ void fnAddHuman(void);
+ void fnBlankMouse(void);
+ void fnNormalMouse(void);
+ void fnLockMouse(void);
+ void fnUnlockMouse(void);
+ void controlPanel(bool on);
+private:
+ void createPointer(uint32 ptrId, uint32 luggageId);
+ OSystem *_system;
+ Logic *_logic;
+ Menu *_menu;
+ MouseObj _objList[MAX_MOUSE];
+ ResMan *_resMan;
+ ObjectMan *_objMan;
+ uint16 _mouseX, _mouseY;
+
+ uint32 _currentPtrId, _currentLuggageId, _frame;
+ MousePtr *_currentPtr;
+ uint16 _numObjs;
+ uint16 _lastState, _state;
+ uint32 _getOff;
+ bool _inTopMenu, _mouseOverride;
+};
+
+} // End of namespace Sword1
+
+#endif //BSMOUSE_H
diff --git a/engines/sword1/music.cpp b/engines/sword1/music.cpp
new file mode 100644
index 0000000000..a5ed77d30d
--- /dev/null
+++ b/engines/sword1/music.cpp
@@ -0,0 +1,352 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "sword1/music.h"
+
+#include "common/util.h"
+#include "common/file.h"
+#include "common/system.h"
+
+#include "sound/mixer.h"
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
+#include "sound/wave.h"
+
+#define SMP_BUFSIZE 8192
+
+namespace Sword1 {
+
+WaveAudioStream *makeWaveStream(Common::File *source, uint32 size) {
+ return new WaveAudioStream(source, size);
+}
+
+WaveAudioStream::WaveAudioStream(Common::File *source, uint32 pSize) {
+ int rate, size;
+ byte flags;
+
+ _sourceFile = source;
+ _sampleBuf = (uint8*)malloc(SMP_BUFSIZE);
+ _sourceFile->incRef();
+ if (_sourceFile->isOpen() && loadWAVFromStream(*_sourceFile, size, rate, flags)) {
+ _isStereo = (flags & Audio::Mixer::FLAG_STEREO) != 0;
+ _rate = rate;
+ if (pSize && (int)pSize < size)
+ size = pSize;
+ assert((uint32)size <= (source->size() - source->pos()));
+ _bitsPerSample = ((flags & Audio::Mixer::FLAG_16BITS) != 0) ? 16 : 8;
+ _samplesLeft = (size * 8) / _bitsPerSample;
+ if ((_bitsPerSample != 16) && (_bitsPerSample != 8))
+ error("WaveAudioStream: unknown wave type");
+ } else {
+ _samplesLeft = 0;
+ _isStereo = false;
+ _bitsPerSample = 16;
+ _rate = 22050;
+ }
+}
+
+WaveAudioStream::~WaveAudioStream(void) {
+ free(_sampleBuf);
+ _sourceFile->decRef();
+}
+
+int WaveAudioStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samples = MIN((int)_samplesLeft, numSamples);
+ int retVal = samples;
+
+ while (samples > 0) {
+ int readBytes = MIN(samples * (_bitsPerSample >> 3), SMP_BUFSIZE);
+ _sourceFile->read(_sampleBuf, readBytes);
+ if (_bitsPerSample == 16) {
+ readBytes >>= 1;
+ samples -= readBytes;
+ int16 *src = (int16*)_sampleBuf;
+ while (readBytes--)
+ *buffer++ = (int16)READ_LE_UINT16(src++);
+ } else {
+ samples -= readBytes;
+ int8 *src = (int8*)_sampleBuf;
+ while (readBytes--)
+ *buffer++ = (int16)*src++ << 8;
+ }
+ }
+ _samplesLeft -= retVal;
+ return retVal;
+}
+
+bool WaveAudioStream::endOfData(void) const {
+ if (_samplesLeft == 0)
+ return true;
+ else
+ return false;
+}
+
+// This means fading takes 3 seconds.
+#define FADE_LENGTH 3
+
+// These functions are only called from Music, so I'm just going to
+// assume that if locking is needed it has already been taken care of.
+
+AudioStream *MusicHandle::createAudioSource(void) {
+ _file.seek(0);
+ switch (_musicMode) {
+#ifdef USE_MAD
+ case MusicMp3:
+ return makeMP3Stream(&_file, _file.size());
+#endif
+#ifdef USE_VORBIS
+ case MusicVorbis:
+ return makeVorbisStream(&_file, _file.size());
+#endif
+ case MusicWave:
+ return makeWaveStream(&_file, 0);
+ case MusicNone: // shouldn't happen
+ warning("createAudioSource ran into null create\n");
+ return NULL;
+ default:
+ error("MusicHandle::createAudioSource: called with illegal MusicMode");
+ }
+ return NULL; // never reached
+}
+
+bool MusicHandle::play(const char *fileBase, bool loop) {
+ char fileName[30];
+ stop();
+ _musicMode = MusicNone;
+#ifdef USE_MAD
+ sprintf(fileName, "%s.mp3", fileBase);
+ if (_file.open(fileName))
+ _musicMode = MusicMp3;
+#endif
+#ifdef USE_VORBIS
+ if (!_file.isOpen()) { // mp3 doesn't exist (or not compiled with MAD support)
+ sprintf(fileName, "%s.ogg", fileBase);
+ if (_file.open(fileName))
+ _musicMode = MusicVorbis;
+ }
+#endif
+ if (!_file.isOpen()) {
+ sprintf(fileName, "%s.wav", fileBase);
+ if (_file.open(fileName))
+ _musicMode = MusicWave;
+ else
+ return false;
+ }
+ _audioSource = createAudioSource();
+ _looping = loop;
+ fadeUp();
+ return true;
+}
+
+void MusicHandle::fadeDown() {
+ if (streaming()) {
+ if (_fading < 0)
+ _fading = -_fading;
+ else if (_fading == 0)
+ _fading = FADE_LENGTH * getRate();
+ _fadeSamples = FADE_LENGTH * getRate();
+ }
+}
+
+void MusicHandle::fadeUp() {
+ if (streaming()) {
+ if (_fading > 0)
+ _fading = -_fading;
+ else if (_fading == 0)
+ _fading = -1;
+ _fadeSamples = FADE_LENGTH * getRate();
+ }
+}
+
+bool MusicHandle::endOfData() const {
+ return !streaming();
+}
+
+// is we don't have an audiosource, return some dummy values.
+// shouldn't happen anyways.
+bool MusicHandle::streaming(void) const {
+ return (_audioSource) ? (!_audioSource->endOfStream()) : false;
+}
+
+bool MusicHandle::isStereo(void) const {
+ return (_audioSource) ? _audioSource->isStereo() : false;
+}
+
+int MusicHandle::getRate(void) const {
+ return (_audioSource) ? _audioSource->getRate() : 11025;
+}
+
+int MusicHandle::readBuffer(int16 *buffer, const int numSamples) {
+ int totalSamples = 0;
+ int16 *bufStart = buffer;
+ if (!_audioSource)
+ return 0;
+ int expectedSamples = numSamples;
+ while ((expectedSamples > 0) && _audioSource) { // _audioSource becomes NULL if we reach EOF and aren't looping
+ int samplesReturned = _audioSource->readBuffer(buffer, expectedSamples);
+ buffer += samplesReturned;
+ totalSamples += samplesReturned;
+ expectedSamples -= samplesReturned;
+ if ((expectedSamples > 0) && _audioSource->endOfData()) {
+ debug(2, "Music reached EOF");
+ _audioSource->endOfData();
+ if (_looping) {
+ delete _audioSource; // recreate same source.
+ _audioSource = createAudioSource();
+ }
+ if ((!_looping) || (!_audioSource))
+ stop();
+ }
+ }
+ // buffer was filled, now do the fading (if necessary)
+ int samplePos = 0;
+ while ((_fading > 0) && (samplePos < totalSamples)) { // fade down
+ bufStart[samplePos] = (bufStart[samplePos] * --_fading) / _fadeSamples;
+ samplePos++;
+ if (_fading == 0) {
+ stop();
+ // clear the rest of the buffer
+ memset(bufStart + samplePos, 0, (totalSamples - samplePos) * 2);
+ return samplePos;
+ }
+ }
+ while ((_fading < 0) && (samplePos < totalSamples)) { // fade up
+ bufStart[samplePos] = -(bufStart[samplePos] * --_fading) / _fadeSamples;
+ if (_fading <= -_fadeSamples)
+ _fading = 0;
+ }
+ return totalSamples;
+}
+
+void MusicHandle::stop() {
+ if (_audioSource) {
+ delete _audioSource;
+ _audioSource = NULL;
+ }
+ if (_file.isOpen())
+ _file.close();
+ _fading = 0;
+ _looping = false;
+}
+
+Music::Music(Audio::Mixer *pMixer) {
+ _mixer = pMixer;
+ _sampleRate = pMixer->getOutputRate();
+ _converter[0] = NULL;
+ _converter[1] = NULL;
+ _volumeL = _volumeR = 192;
+ _mixer->setupPremix(this);
+}
+
+Music::~Music() {
+ _mixer->setupPremix(0);
+ delete _converter[0];
+ delete _converter[1];
+}
+
+void Music::mixer(int16 *buf, uint32 len) {
+ Common::StackLock lock(_mutex);
+ memset(buf, 0, 2 * len * sizeof(int16));
+ for (int i = 0; i < ARRAYSIZE(_handles); i++)
+ if (_handles[i].streaming() && _converter[i])
+ _converter[i]->flow(_handles[i], buf, len, _volumeL, _volumeR);
+}
+
+void Music::setVolume(uint8 volL, uint8 volR) {
+ _volumeL = (Audio::st_volume_t)volL;
+ _volumeR = (Audio::st_volume_t)volR;
+}
+
+void Music::giveVolume(uint8 *volL, uint8 *volR) {
+ *volL = (uint8)_volumeL;
+ *volR = (uint8)_volumeR;
+}
+
+void Music::startMusic(int32 tuneId, int32 loopFlag) {
+ if (strlen(_tuneList[tuneId]) > 0) {
+ int newStream = 0;
+ _mutex.lock();
+ if (_handles[0].streaming() && _handles[1].streaming()) {
+ int streamToStop;
+ // Both streams playing - one must be forced to stop.
+ if (!_handles[0].fading() && !_handles[1].fading()) {
+ // None of them are fading. Shouldn't happen,
+ // so it doesn't matter which one we pick.
+ streamToStop = 0;
+ } else if (_handles[0].fading() && !_handles[1].fading()) {
+ // Stream 0 is fading, so pick that one.
+ streamToStop = 0;
+ } else if (!_handles[0].fading() && _handles[1].fading()) {
+ // Stream 1 is fading, so pick that one.
+ streamToStop = 1;
+ } else {
+ // Both streams are fading. Pick the one that
+ // is closest to silent.
+ if (ABS(_handles[0].fading()) < ABS(_handles[1].fading()))
+ streamToStop = 0;
+ else
+ streamToStop = 1;
+ }
+ _handles[streamToStop].stop();
+ }
+ if (_handles[0].streaming()) {
+ _handles[0].fadeDown();
+ newStream = 1;
+ } else if (_handles[1].streaming()) {
+ _handles[1].fadeDown();
+ newStream = 0;
+ }
+ delete _converter[newStream];
+ _converter[newStream] = NULL;
+ _mutex.unlock();
+
+ /* The handle will load the music file now. It can take a while, so unlock
+ the mutex before, to have the soundthread playing normally.
+ As the corresponding _converter is NULL, the handle will be ignored by the playing thread */
+ if (_handles[newStream].play(_tuneList[tuneId], loopFlag != 0)) {
+ _mutex.lock();
+ _converter[newStream] = Audio::makeRateConverter(_handles[newStream].getRate(), _mixer->getOutputRate(), _handles[newStream].isStereo(), false);
+ _mutex.unlock();
+ } else {
+ if (tuneId != 81) // file 81 was apparently removed from BS.
+ warning("Can't find music file %s", _tuneList[tuneId]);
+ }
+ } else {
+ _mutex.lock();
+ if (_handles[0].streaming())
+ _handles[0].fadeDown();
+ if (_handles[1].streaming())
+ _handles[1].fadeDown();
+ _mutex.unlock();
+ }
+}
+
+void Music::fadeDown() {
+ Common::StackLock lock(_mutex);
+ for (int i = 0; i < ARRAYSIZE(_handles); i++)
+ if (_handles[i].streaming())
+ _handles[i].fadeDown();
+}
+
+} // End of namespace Sword1
diff --git a/engines/sword1/music.h b/engines/sword1/music.h
new file mode 100644
index 0000000000..4f355d3931
--- /dev/null
+++ b/engines/sword1/music.h
@@ -0,0 +1,122 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSMUSIC_H
+#define BSMUSIC_H
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "common/file.h"
+#include "sound/audiostream.h"
+#include "sound/rate.h"
+
+namespace Audio {
+ class Mixer;
+}
+
+namespace Sword1 {
+
+#define TOTAL_TUNES 270
+
+enum MusicMode {
+ MusicNone = 0,
+ MusicWave,
+ MusicMp3,
+ MusicVorbis
+};
+
+class WaveAudioStream : public AudioStream {
+public:
+ WaveAudioStream(Common::File *source, uint32 pSize);
+ virtual ~WaveAudioStream();
+ virtual int readBuffer(int16 *buffer, const int numSamples);
+ virtual bool isStereo(void) const { return _isStereo; };
+ virtual bool endOfData(void) const;
+ virtual int getRate(void) const { return _rate; };
+private:
+ Common::File *_sourceFile;
+ uint8 *_sampleBuf;
+ uint32 _rate;
+ bool _isStereo;
+ uint32 _samplesLeft;
+ uint16 _bitsPerSample;
+};
+
+class MusicHandle : public AudioStream {
+private:
+ Common::File _file;
+ bool _looping;
+ int32 _fading;
+ int32 _fadeSamples;
+ MusicMode _musicMode;
+ AudioStream *_audioSource;
+ AudioStream *createAudioSource(void);
+public:
+ MusicHandle() : _looping(false), _fading(0), _audioSource(NULL) {}
+ virtual int readBuffer(int16 *buffer, const int numSamples);
+ bool play(const char *filename, bool loop);
+ void stop();
+ void fadeUp();
+ void fadeDown();
+ bool streaming() const;
+ int32 fading() { return _fading; }
+ bool endOfData() const;
+ bool endOfStream() const { return false; }
+ bool isStereo() const;
+ int getRate() const;
+};
+
+class Music : public AudioStream {
+public:
+ Music(Audio::Mixer *pMixer);
+ ~Music();
+ void startMusic(int32 tuneId, int32 loopFlag);
+ void fadeDown();
+ void setVolume(uint8 volL, uint8 volR);
+ void giveVolume(uint8 *volL, uint8 *volR);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples) {
+ mixer(buffer, numSamples / 2);
+ return numSamples;
+ }
+ bool isStereo() const { return true; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _sampleRate; }
+
+private:
+ Audio::st_volume_t _volumeL, _volumeR;
+ MusicHandle _handles[2];
+ Audio::RateConverter *_converter[2];
+ Audio::Mixer *_mixer;
+ uint32 _sampleRate;
+ Common::Mutex _mutex;
+
+ static void passMixerFunc(void *param, int16 *buf, uint len);
+ void mixer(int16 *buf, uint32 len);
+
+ static const char _tuneList[TOTAL_TUNES][8]; // in staticres.cpp
+};
+
+} // End of namespace Sword1
+
+#endif // BSMUSIC_H
diff --git a/engines/sword1/object.h b/engines/sword1/object.h
new file mode 100644
index 0000000000..3d2cde4a6d
--- /dev/null
+++ b/engines/sword1/object.h
@@ -0,0 +1,130 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSOBJECT_H
+#define BSOBJECT_H
+
+#include "common/scummsys.h"
+
+namespace Sword1 {
+
+#define O_TOTAL_EVENTS 5
+#define O_WALKANIM_SIZE 600 //max number of nodes in router output
+#define O_GRID_SIZE 200
+#define EXTRA_GRID_SIZE 20
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct OEventSlot { //receiving event list in the compact -
+ int32 o_event; //array of these with O_TOTAL_EVENTS elements
+ int32 o_event_script;
+} GCC_PACK; // size = 2*int32 = 8 bytes
+
+#define TOTAL_script_levels 5
+
+struct ScriptTree { //this is a logic tree, used by OBJECTs
+ int32 o_script_level; //logic level
+ int32 o_script_id[TOTAL_script_levels]; //script id's (are unique to each level)
+ int32 o_script_pc[TOTAL_script_levels]; //pc of script for each (if script_manager)
+} GCC_PACK; // size = 11*int32 = 44 bytes
+
+struct TalkOffset {
+ int32 x;
+ int32 y;
+} GCC_PACK; // size = 2*int32 = 8 bytes
+
+struct WalkData {
+ int32 frame;
+ int32 x;
+ int32 y;
+ int32 step;
+ int32 dir;
+} GCC_PACK; // size = 5*int32 = 20 bytes
+
+struct Object {
+ int32 o_type; // 0 broad description of type - object, floor, etc.
+ int32 o_status; // 4 bit flags for logic, graphics, mouse, etc.
+ int32 o_logic; // 8 logic type
+ int32 o_place; // 12 where is the mega character
+ int32 o_down_flag; // 16 pass back down with this - with C possibly both are unnecessary?
+ int32 o_target; // 20 target object for the GTM *these are linked to script
+ int32 o_screen; // 24 physical screen/section
+ int32 o_frame; // 28 frame number &
+ int32 o_resource; // 32 id of spr file it comes from
+ int32 o_sync; // 36 receive sync here
+ int32 o_pause; // 40 logic_engine() pauses these cycles
+ int32 o_xcoord; // 44
+ int32 o_ycoord; // 48
+ int32 o_mouse_x1; // 52 top-left of mouse area is (x1,y1)
+ int32 o_mouse_y1; // 56
+ int32 o_mouse_x2; // 60 bottom-right of area is (x2,y2) (these coords are inclusive)
+ int32 o_mouse_y2; // 64
+ int32 o_priority; // 68
+ int32 o_mouse_on; // 72
+ int32 o_mouse_off; // 76
+ int32 o_mouse_click; // 80
+ int32 o_interact; // 84
+ int32 o_get_to_script; // 88
+ int32 o_scale_a; // 92 used by floors
+ int32 o_scale_b; // 96
+ int32 o_anim_x; // 100
+ int32 o_anim_y; // 104
+
+ ScriptTree o_tree; // 108 size = 44 bytes
+ ScriptTree o_bookmark; // 152 size = 44 bytes
+
+ int32 o_dir; // 196
+ int32 o_speech_pen; // 200
+ int32 o_speech_width; // 204
+ int32 o_speech_time; // 208
+ int32 o_text_id; // 212 working back from o_ins1
+ int32 o_tag; // 216
+ int32 o_anim_pc; // 220 position within an animation structure
+ int32 o_anim_resource; // 224 cdt or anim table
+
+ int32 o_walk_pc; // 228
+
+ TalkOffset talk_table[6]; // 232 size = 6*8 bytes = 48
+
+ OEventSlot o_event_list[O_TOTAL_EVENTS]; // 280 size = 5*8 bytes = 40
+
+ int32 o_ins1; // 320
+ int32 o_ins2; // 324
+ int32 o_ins3; // 328
+
+ int32 o_mega_resource; // 332
+ int32 o_walk_resource; // 336
+
+ WalkData o_route[O_WALKANIM_SIZE]; // 340 size = 600*20 bytes = 12000
+ // mega size = 12340 bytes (+ 8 byte offset table + 20 byte header = 12368)
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+} // End of namespace Sword1
+
+#endif //BSOBJECT_H
+
diff --git a/engines/sword1/objectman.cpp b/engines/sword1/objectman.cpp
new file mode 100644
index 0000000000..330e5566ee
--- /dev/null
+++ b/engines/sword1/objectman.cpp
@@ -0,0 +1,163 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/objectman.h"
+#include "common/scummsys.h"
+#include "common/util.h"
+#include "sword1/sworddefs.h"
+#include "sword1/swordres.h"
+#include "sword1/sword1.h"
+
+namespace Sword1 {
+
+ObjectMan::ObjectMan(ResMan *pResourceMan) {
+ _resMan = pResourceMan;
+}
+
+void ObjectMan::initialize(void) {
+ uint16 cnt;
+ for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
+ _liveList[cnt] = 0; // we don't need to close the files here. When this routine is
+ // called, the memory was flushed() anyways, so these resources
+ // already *are* closed.
+
+ _liveList[128] = _liveList[129] = _liveList[130] = _liveList[131] = _liveList[133] =
+ _liveList[134] = _liveList[145] = _liveList[146] = _liveList[TEXT_sect] = 1;
+
+ for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++) {
+ if (_liveList[cnt])
+ _cptData[cnt] = (uint8*)_resMan->cptResOpen(_objectList[cnt]) + sizeof(Header);
+ else
+ _cptData[cnt] = NULL;
+ }
+}
+
+ObjectMan::~ObjectMan(void) {
+ for (uint16 cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
+ if (_liveList[cnt])
+ _resMan->resClose(_objectList[cnt]);
+}
+
+bool ObjectMan::sectionAlive(uint16 section) {
+ return (_liveList[section] > 0);
+}
+
+void ObjectMan::megaEntering(uint16 section) {
+ _liveList[section]++;
+ if (_liveList[section] == 1)
+ _cptData[section] = ((uint8*)_resMan->cptResOpen(_objectList[section])) + sizeof(Header);
+}
+
+void ObjectMan::megaLeaving(uint16 section, int id) {
+ if (_liveList[section] == 0)
+ error("mega %d is leaving empty section %d", id, section);
+ _liveList[section]--;
+ if ((_liveList[section] == 0) && (id != PLAYER)) {
+ _resMan->resClose(_objectList[section]);
+ _cptData[section] = NULL;
+ }
+ /* if the player is leaving the section then we have to close the resources after
+ mainloop ends, because the screen will still need the resources*/
+}
+
+uint8 ObjectMan::fnCheckForTextLine(uint32 textId) {
+ uint8 retVal = 0;
+ if (!_textList[textId / ITM_PER_SEC][0])
+ return 0; // section does not exist
+
+ uint8 lang = SwordEngine::_systemVars.language;
+ uint32 *textData = (uint32*)((uint8*)_resMan->openFetchRes(_textList[textId / ITM_PER_SEC][lang]) + sizeof(Header));
+ if ((textId & ITM_ID) < READ_LE_UINT32(textData)) {
+ textData++;
+ if (textData[textId & ITM_ID])
+ retVal = 1;
+ }
+ _resMan->resClose(_textList[textId / ITM_PER_SEC][lang]);
+ return retVal;
+}
+
+char *ObjectMan::lockText(uint32 textId) {
+ uint8 lang = SwordEngine::_systemVars.language;
+ char *addr = (char*)_resMan->openFetchRes(_textList[textId / ITM_PER_SEC][lang]) + sizeof(Header);
+ if ((textId & ITM_ID) >= READ_LE_UINT32(addr)) {
+ warning("ObjectMan::lockText(%d): only %d texts in file", textId & ITM_ID, READ_LE_UINT32(addr));
+ textId = 0; // get first line instead
+ }
+ uint32 offset = READ_LE_UINT32(addr + ((textId & ITM_ID) + 1)* 4);
+ if (offset == 0) {
+ warning("ObjectMan::lockText(%d): text number has no text lines", textId);
+ return _errorStr;
+ }
+ return addr + offset;
+}
+
+void ObjectMan::unlockText(uint32 textId) {
+ _resMan->resClose(_textList[textId / ITM_PER_SEC][SwordEngine::_systemVars.language]);
+}
+
+uint32 ObjectMan::lastTextNumber(int section) {
+ uint8 *data = (uint8*)_resMan->openFetchRes(_textList[section][SwordEngine::_systemVars.language]) + sizeof(Header);
+ uint32 result = READ_LE_UINT32(data) - 1;
+ _resMan->resClose(_textList[section][SwordEngine::_systemVars.language]);
+ return result;
+}
+
+Object *ObjectMan::fetchObject(uint32 id) {
+ uint8 *addr = _cptData[id / ITM_PER_SEC];
+ if (!addr)
+ error("fetchObject: section %d is not open!", id / ITM_PER_SEC);
+ id &= ITM_ID;
+ // DON'T do endian conversion here. it's already done.
+ return (Object*)(addr + *(uint32*)(addr + (id + 1)*4));
+}
+
+uint32 ObjectMan::fetchNoObjects(int section) {
+ if (_cptData[section] == NULL)
+ error("fetchNoObjects: section %d is not open!", section);
+ return *(uint32*)_cptData[section];
+}
+
+void ObjectMan::closeSection(uint32 screen) {
+ if (_liveList[screen] == 0) // close the section that PLAYER has just left, if it's empty now
+ _resMan->resClose(_objectList[screen]);
+}
+
+void ObjectMan::loadLiveList(uint16 *src) {
+ for (uint16 cnt = 0; cnt < TOTAL_SECTIONS; cnt++) {
+ if (_liveList[cnt]) {
+ _resMan->resClose(_objectList[cnt]);
+ _cptData[cnt] = NULL;
+ }
+ _liveList[cnt] = src[cnt];
+ if (_liveList[cnt])
+ _cptData[cnt] = ((uint8*)_resMan->cptResOpen(_objectList[cnt])) + sizeof(Header);
+ }
+}
+
+void ObjectMan::saveLiveList(uint16 *dest) {
+ memcpy(dest, _liveList, TOTAL_SECTIONS * sizeof(uint16));
+}
+
+char ObjectMan::_errorStr[] = "Error: Text not found.";
+
+} // End of namespace Sword1
diff --git a/engines/sword1/objectman.h b/engines/sword1/objectman.h
new file mode 100644
index 0000000000..398d7d85a4
--- /dev/null
+++ b/engines/sword1/objectman.h
@@ -0,0 +1,66 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+// this is the object manager. our equivalent to protocol.c and coredata.c
+
+#ifndef OBJECTMAN_H
+#define OBJECTMAN_H
+
+#include "sword1/resman.h"
+#include "sword1/sworddefs.h"
+#include "sword1/object.h"
+
+namespace Sword1 {
+
+class ObjectMan {
+public:
+ ObjectMan(ResMan *pResourceMan);
+ ~ObjectMan(void);
+ void initialize(void);
+
+ Object *fetchObject(uint32 id);
+ uint32 fetchNoObjects(int section);
+ bool sectionAlive(uint16 section);
+ void megaEntering(uint16 section);
+ void megaLeaving(uint16 section, int id);
+
+ uint8 fnCheckForTextLine(uint32 textId);
+ char *lockText(uint32 textId);
+ void unlockText(uint32 textId);
+ uint32 lastTextNumber(int section);
+
+ void closeSection(uint32 screen);
+
+ void saveLiveList(uint16 *dest); // for loading/saving
+ void loadLiveList(uint16 *src);
+private:
+ ResMan *_resMan;
+ static const uint32 _objectList[TOTAL_SECTIONS]; //a table of pointers to object files
+ static const uint32 _textList[TOTAL_SECTIONS][7]; //a table of pointers to text files
+ uint16 _liveList[TOTAL_SECTIONS]; //which sections are active
+ uint8 *_cptData[TOTAL_SECTIONS];
+ static char _errorStr[];
+};
+
+} // End of namespace Sword1
+
+#endif //OBJECTMAN_H
diff --git a/engines/sword1/resman.cpp b/engines/sword1/resman.cpp
new file mode 100644
index 0000000000..fd86993dfa
--- /dev/null
+++ b/engines/sword1/resman.cpp
@@ -0,0 +1,421 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "sword1/memman.h"
+#include "sword1/resman.h"
+#include "sword1/sworddefs.h"
+#include "base/engine.h"
+#include "common/config-manager.h"
+#include "common/util.h"
+#include "common/str.h"
+#include "sword1/swordres.h"
+
+#include "gui/message.h"
+#include "gui/newgui.h"
+
+namespace Sword1 {
+ void guiFatalError(char *msg) {
+ // Displays a dialog on-screen before terminating the engine.
+ // TODO: We really need to setup a special palette for cases when
+ // the engine is erroring before setting one... otherwise invisible cursor :)
+
+ GUI::MessageDialog dialog(msg);
+ dialog.runModal();
+ error(msg);
+}
+
+#define MAX_PATH_LEN 260
+
+ResMan::ResMan(const char *fileName) {
+ _openCluStart = _openCluEnd = NULL;
+ _openClus = 0;
+ _memMan = new MemMan();
+ loadCluDescript(fileName);
+}
+
+ResMan::~ResMan(void) {
+#if 0
+ for (uint32 clusCnt = 0; clusCnt < _prj.noClu; clusCnt++) {
+ Clu *cluster = _prj.clu[clusCnt];
+ if (cluster) {
+ for (uint32 grpCnt = 0; grpCnt < cluster->noGrp; grpCnt++) {
+ Grp *group = cluster->grp[grpCnt];
+ if (group) {
+ for (uint32 resCnt = 0; resCnt < group->noRes; resCnt++) {
+ if (group->resHandle[resCnt].cond == MEM_DONT_FREE) {
+ warning("ResMan::~ResMan: Resource %02X.%04X.%02X is still open",
+ clusCnt + 1, grpCnt, resCnt);
+ }
+ }
+ }
+ }
+ }
+ }
+ debug(0, "ResMan closed\n");
+#endif
+ flush();
+ freeCluDescript();
+ delete _memMan;
+}
+
+void ResMan::loadCluDescript(const char *fileName) {
+ Common::File file;
+ file.open(fileName);
+
+ if (!file.isOpen()) {
+ char msg[512];
+ sprintf(msg, "Couldn't open CLU description '%s'\n\nIf you are running from CD, please ensure you have read the ScummVM documentation regarding multi-cd games.", fileName);
+ guiFatalError(msg);
+ }
+
+
+ _prj.noClu = file.readUint32LE();
+ _prj.clu = new Clu[_prj.noClu];
+ memset(_prj.clu, 0, _prj.noClu * sizeof(Clu));
+
+ uint32 *cluIndex = (uint32*)malloc(_prj.noClu * 4);
+ file.read(cluIndex, _prj.noClu * 4);
+
+ for (uint32 clusCnt = 0; clusCnt < _prj.noClu; clusCnt++)
+ if (cluIndex[clusCnt]) {
+ Clu *cluster = _prj.clu + clusCnt;
+ file.read(cluster->label, MAX_LABEL_SIZE);
+
+ cluster->file = NULL;
+ cluster->noGrp = file.readUint32LE();
+ cluster->grp = new Grp[cluster->noGrp];
+ cluster->nextOpen = NULL;
+ memset(cluster->grp, 0, cluster->noGrp * sizeof(Grp));
+ cluster->refCount = 0;
+
+ uint32 *grpIndex = (uint32*)malloc(cluster->noGrp * 4);
+ file.read(grpIndex, cluster->noGrp * 4);
+
+ for (uint32 grpCnt = 0; grpCnt < cluster->noGrp; grpCnt++)
+ if (grpIndex[grpCnt]) {
+ Grp *group = cluster->grp + grpCnt;
+ group->noRes = file.readUint32LE();
+ group->resHandle = new MemHandle[group->noRes];
+ group->offset = new uint32[group->noRes];
+ group->length = new uint32[group->noRes];
+ uint32 *resIdIdx = (uint32*)malloc(group->noRes * 4);
+ file.read(resIdIdx, group->noRes * 4);
+
+ for (uint32 resCnt = 0; resCnt < group->noRes; resCnt++) {
+ if (resIdIdx[resCnt]) {
+ group->offset[resCnt] = file.readUint32LE();
+ group->length[resCnt] = file.readUint32LE();
+ _memMan->initHandle(group->resHandle + resCnt);
+ } else {
+ group->offset[resCnt] = 0xFFFFFFFF;
+ group->length[resCnt] = 0;
+ _memMan->initHandle(group->resHandle + resCnt);
+ }
+ }
+ free(resIdIdx);
+ }
+ free(grpIndex);
+ }
+ free(cluIndex);
+
+ if (_prj.clu[3].grp[5].noRes == 29)
+ for (uint8 cnt = 0; cnt < 29; cnt++)
+ _srIdList[cnt] = 0x04050000 | cnt;
+}
+
+void ResMan::freeCluDescript(void) {
+
+ for (uint32 clusCnt = 0; clusCnt < _prj.noClu; clusCnt++) {
+ Clu *cluster = _prj.clu + clusCnt;
+ for (uint32 grpCnt = 0; grpCnt < cluster->noGrp; grpCnt++) {
+ Grp *group = cluster->grp + grpCnt;
+ if (group->resHandle != NULL) {
+ for (uint32 resCnt = 0; resCnt < group->noRes; resCnt++)
+ _memMan->freeNow(group->resHandle + resCnt);
+
+ delete[] group->resHandle;
+ delete[] group->offset;
+ delete[] group->length;
+ }
+ }
+ delete[] cluster->grp;
+
+ if (cluster->file != NULL)
+ delete cluster->file;
+ }
+ delete[] _prj.clu;
+}
+
+void ResMan::flush(void) {
+ for (uint32 clusCnt = 0; clusCnt < _prj.noClu; clusCnt++) {
+ Clu *cluster = _prj.clu + clusCnt;
+ for (uint32 grpCnt = 0; grpCnt < cluster->noGrp; grpCnt++) {
+ Grp *group = cluster->grp + grpCnt;
+ for (uint32 resCnt = 0; resCnt < group->noRes; resCnt++)
+ if (group->resHandle[resCnt].cond != MEM_FREED) {
+ _memMan->setCondition(group->resHandle + resCnt, MEM_CAN_FREE);
+ group->resHandle[resCnt].refCount = 0;
+ }
+ }
+ if (cluster->file) {
+ cluster->file->close();
+ delete cluster->file;
+ cluster->file = NULL;
+ cluster->refCount = 0;
+ }
+ }
+ _openClus = 0;
+ _openCluStart = _openCluEnd = NULL;
+ // the memory manager cached the blocks we asked it to free, so explicitly make it free them
+ _memMan->flush();
+}
+
+void *ResMan::fetchRes(uint32 id) {
+ MemHandle *memHandle = resHandle(id);
+ if (!memHandle->data)
+ error("fetchRes:: resource %d is not open!", id);
+ return memHandle->data;
+}
+
+void *ResMan::openFetchRes(uint32 id) {
+ resOpen(id);
+ return fetchRes(id);
+}
+
+void ResMan::dumpRes(uint32 id) {
+ char outn[30];
+ sprintf(outn, "DUMP%08X.BIN", id);
+ Common::File outf;
+ if (outf.open(outn, Common::File::kFileWriteMode)) {
+ resOpen(id);
+ MemHandle *memHandle = resHandle(id);
+ outf.write(memHandle->data, memHandle->size);
+ outf.close();
+ resClose(id);
+ }
+}
+
+Header *ResMan::lockScript(uint32 scrID) {
+ if (!_scriptList[scrID / ITM_PER_SEC])
+ error("Script id %d not found.\n", scrID);
+ scrID = _scriptList[scrID / ITM_PER_SEC];
+#ifdef SCUMM_BIG_ENDIAN
+ MemHandle *memHandle = resHandle(scrID);
+ if (memHandle->cond == MEM_FREED)
+ openScriptResourceBigEndian(scrID);
+ else
+ resOpen(scrID);
+#else
+ resOpen(scrID);
+#endif
+ return (Header*)resHandle(scrID)->data;
+}
+
+void ResMan::unlockScript(uint32 scrID) {
+ resClose(_scriptList[scrID / ITM_PER_SEC]);
+}
+
+void *ResMan::cptResOpen(uint32 id) {
+#ifdef SCUMM_BIG_ENDIAN
+ MemHandle *memHandle = resHandle(id);
+ if (memHandle->cond == MEM_FREED)
+ openCptResourceBigEndian(id);
+ else
+ resOpen(id);
+#else
+ resOpen(id);
+#endif
+ return resHandle(id)->data;
+}
+
+void ResMan::resOpen(uint32 id) { // load resource ID into memory
+ MemHandle *memHandle = resHandle(id);
+ if (memHandle->cond == MEM_FREED) { // memory has been freed
+ uint32 size = resLength(id);
+ _memMan->alloc(memHandle, size);
+ Common::File *clusFile = resFile(id);
+ assert(clusFile);
+ clusFile->seek( resOffset(id) );
+ clusFile->read( memHandle->data, size);
+ if (clusFile->ioFailed()) {
+ error("Can't read %d bytes from offset %d from cluster file %s\nResource ID: %d (%08X)\n", size, resOffset(id), _prj.clu[(id >> 24) - 1].label, id, id);
+ }
+ } else
+ _memMan->setCondition(memHandle, MEM_DONT_FREE);
+
+ memHandle->refCount++;
+ if (memHandle->refCount > 20) {
+ debug(1, "%d references to id %d. Guess there's something wrong.", memHandle->refCount, id);
+ }
+}
+
+void ResMan::resClose(uint32 id) {
+ MemHandle *handle = resHandle(id);
+ if (!handle->refCount) {
+ warning("Resource Manager fail: unlocking object with refCount 0. Id: %d\n", id);
+ } else {
+ handle->refCount--;
+ if (!handle->refCount)
+ _memMan->setCondition( handle, MEM_CAN_FREE);
+ }
+}
+
+FrameHeader *ResMan::fetchFrame(void *resourceData, uint32 frameNo) {
+ uint8 *frameFile = (uint8*)resourceData;
+ uint8 *idxData = frameFile + sizeof(Header);
+ if (frameNo >= READ_LE_UINT32(idxData))
+ error("fetchFrame:: frame %d doesn't exist in resource.", frameNo);
+ frameFile += READ_LE_UINT32(idxData + (frameNo+1) * 4);
+ return (FrameHeader*)frameFile;
+}
+
+Common::File *ResMan::resFile(uint32 id) {
+ Clu *cluster = _prj.clu + ((id >> 24) - 1);
+ if (cluster->file == NULL) {
+ _openClus++;
+ if (_openCluEnd == NULL) {
+ _openCluStart = _openCluEnd = cluster;
+ } else {
+ _openCluEnd->nextOpen = cluster;
+ _openCluEnd = cluster;
+ }
+ cluster->file = new Common::File();
+ char fileName[15];
+ sprintf(fileName, "%s.CLU", _prj.clu[(id >> 24)-1].label);
+ cluster->file->open(fileName);
+
+ if (!cluster->file->isOpen()) {
+ char msg[512];
+ sprintf(msg, "Couldn't open game cluster file '%s'\n\nIf you are running from CD, please ensure you have read the ScummVM documentation regarding multi-cd games.", fileName);
+ guiFatalError(msg);
+ }
+ while (_openClus > MAX_OPEN_CLUS) {
+ assert(_openCluStart);
+ Clu *closeClu = _openCluStart;
+ _openCluStart = _openCluStart->nextOpen;
+
+ closeClu->file->close();
+ delete closeClu->file;
+ closeClu->file = NULL;
+ closeClu->nextOpen = NULL;
+ _openClus--;
+ }
+ }
+ return cluster->file;
+}
+
+MemHandle *ResMan::resHandle(uint32 id) {
+ if ((id >> 16) == 0x0405)
+ id = _srIdList[id & 0xFFFF];
+ uint8 cluster = (uint8)((id >> 24) - 1);
+ uint8 group = (uint8)(id >> 16);
+
+ return &(_prj.clu[cluster].grp[group].resHandle[id & 0xFFFF]);
+}
+
+uint32 ResMan::resLength(uint32 id) {
+ if ((id >> 16) == 0x0405)
+ id = _srIdList[id & 0xFFFF];
+ uint8 cluster = (uint8)((id >> 24) - 1);
+ uint8 group = (uint8)(id >> 16);
+
+ return _prj.clu[cluster].grp[group].length[id & 0xFFFF];
+}
+
+uint32 ResMan::resOffset(uint32 id) {
+ if ((id >> 16) == 0x0405)
+ id = _srIdList[id & 0xFFFF];
+ uint8 cluster = (uint8)((id >> 24) - 1);
+ uint8 group = (uint8)(id >> 16);
+
+ return _prj.clu[cluster].grp[group].offset[id & 0xFFFF];
+}
+
+void ResMan::openCptResourceBigEndian(uint32 id) {
+ resOpen(id);
+ MemHandle *handle = resHandle(id);
+ uint32 totSize = handle->size;
+ uint32 *data = (uint32*)((uint8*)handle->data + sizeof(Header));
+ totSize -= sizeof(Header);
+ if (totSize & 3)
+ error("Illegal compact size for id %d: %d", id, totSize);
+ totSize /= 4;
+ for (uint32 cnt = 0; cnt < totSize; cnt++) {
+ *data = READ_LE_UINT32(data);
+ data++;
+ }
+}
+
+void ResMan::openScriptResourceBigEndian(uint32 id) {
+ resOpen(id);
+ MemHandle *handle = resHandle(id);
+ // uint32 totSize = handle->size;
+ Header *head = (Header*)handle->data;
+ head->comp_length = FROM_LE_32(head->comp_length);
+ head->decomp_length = FROM_LE_32(head->decomp_length);
+ head->version = FROM_LE_16(head->version);
+ uint32 *data = (uint32*)((uint8*)handle->data + sizeof(Header));
+ uint32 size = handle->size - sizeof(Header);
+ if (size & 3)
+ error("Odd size during script endian conversion. Resource ID =%d, size = %d", id, size);
+ size >>= 2;
+ for (uint32 cnt = 0; cnt < size; cnt++) {
+ *data = READ_LE_UINT32(data);
+ data++;
+ }
+}
+
+uint32 ResMan::_srIdList[29] = { // the file numbers differ for the control panel file IDs, so we need this array
+ OTHER_SR_FONT, // SR_FONT
+ 0x04050000, // SR_BUTTON
+ OTHER_SR_REDFONT, // SR_REDFONT
+ 0x04050001, // SR_PALETTE
+ 0x04050002, // SR_PANEL_ENGLISH
+ 0x04050003, // SR_PANEL_FRENCH
+ 0x04050004, // SR_PANEL_GERMAN
+ 0x04050005, // SR_PANEL_ITALIAN
+ 0x04050006, // SR_PANEL_SPANISH
+ 0x04050007, // SR_PANEL_AMERICAN
+ 0x04050008, // SR_TEXT_BUTTON
+ 0x04050009, // SR_SPEED
+ 0x0405000A, // SR_SCROLL1
+ 0x0405000B, // SR_SCROLL2
+ 0x0405000C, // SR_CONFIRM
+ 0x0405000D, // SR_VOLUME
+ 0x0405000E, // SR_VLIGHT
+ 0x0405000F, // SR_VKNOB
+ 0x04050010, // SR_WINDOW
+ 0x04050011, // SR_SLAB1
+ 0x04050012, // SR_SLAB2
+ 0x04050013, // SR_SLAB3
+ 0x04050014, // SR_SLAB4
+ 0x04050015, // SR_BUTUF
+ 0x04050016, // SR_BUTUS
+ 0x04050017, // SR_BUTDS
+ 0x04050018, // SR_BUTDF
+ 0x04050019, // SR_DEATHPANEL
+ 0,
+};
+
+} // End of namespace Sword1
diff --git a/engines/sword1/resman.h b/engines/sword1/resman.h
new file mode 100644
index 0000000000..e2920b0a81
--- /dev/null
+++ b/engines/sword1/resman.h
@@ -0,0 +1,98 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef RESMAN_H
+#define RESMAN_H
+
+#include "sword1/memman.h"
+#include "common/file.h"
+#include "sword1/sworddefs.h"
+
+namespace Sword1 {
+
+#define MAX_LABEL_SIZE (31+1)
+
+#if defined(__PSP__)
+#define MAX_OPEN_CLUS 4 // the PSP can't have more than 8 files open simultaneously
+ // since we also need filehandles for music and sometimes savegames
+ // set the maximum number of open clusters to 4.
+#else
+#define MAX_OPEN_CLUS 8 // don't open more than 8 files at once
+#endif
+
+struct Grp {
+ uint32 noRes;
+ MemHandle *resHandle;
+ uint32 *offset;
+ uint32 *length;
+};
+
+struct Clu {
+ uint32 refCount;
+ Common::File *file;
+ char label[MAX_LABEL_SIZE];
+ uint32 noGrp;
+ Grp *grp;
+ Clu *nextOpen;
+};
+
+struct Prj {
+ uint32 noClu;
+ Clu *clu;
+};
+
+class ResMan {
+public:
+ ResMan(const char *fileName);
+ ~ResMan(void);
+ void flush(void);
+ void resClose(uint32 id);
+ void resOpen(uint32 id);
+ void *fetchRes(uint32 id);
+ void dumpRes(uint32 id);
+ void *openFetchRes(uint32 id);
+ void *cptResOpen(uint32 id);
+ Header *lockScript(uint32 scrID);
+ void unlockScript(uint32 scrID);
+ FrameHeader *fetchFrame(void *resourceData, uint32 frameNo);
+private:
+ uint32 resLength(uint32 id);
+ MemHandle *resHandle(uint32 id);
+ uint32 resOffset(uint32 id);
+ Common::File *resFile(uint32 id);
+
+ void openCptResourceBigEndian(uint32 id);
+ void openScriptResourceBigEndian(uint32 id);
+
+ void loadCluDescript(const char *fileName);
+ void freeCluDescript(void);
+ Prj _prj;
+ MemMan *_memMan;
+ static const uint32 _scriptList[TOTAL_SECTIONS]; //a table of resource tags
+ static uint32 _srIdList[29];
+ Clu *_openCluStart, *_openCluEnd;
+ int _openClus;
+};
+
+} // End of namespace Sword1
+
+#endif //RESMAN_H
diff --git a/engines/sword1/router.cpp b/engines/sword1/router.cpp
new file mode 100644
index 0000000000..d4a1b26648
--- /dev/null
+++ b/engines/sword1/router.cpp
@@ -0,0 +1,2606 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/router.h"
+#include "common/util.h"
+#include "common/scummsys.h"
+#include "sword1/swordres.h"
+#include "sword1/sworddefs.h"
+#include "sword1/objectman.h"
+#include "sword1/resman.h"
+
+namespace Sword1 {
+
+/****************************************************************************
+ * JROUTER.C polygon router with modular walks
+ * using a tree of modules
+ * 21 july 94
+ * 3 november 94
+ * System currently works by scanning grid data and coming up with a ROUTE
+ * as a series of way points(nodes), the smoothest eight directional PATH
+ * through these nodes is then found, and a WALK created to fit the PATH.
+ *
+ * Two funtions are called by the user, RouteFinder creates a route as a
+ * module list, HardWalk creates an animation list from the module list.
+ * The split is only provided to allow the possibility of turning the
+ * autorouter over two game cycles.
+ ****************************************************************************
+ *
+ * Routine timings on osborne 486
+ *
+ * Read floor resource (file already loaded) 112 pixels
+ *
+ * Read mega resource (file already loaded) 112 pixels
+ *
+ *
+ *
+ ****************************************************************************
+ *
+ * Modified 12 Oct 95
+ *
+ * Target Points within 1 pixel of a line are ignored ???
+ *
+ * Modules split into Points within 1 pixel of a line are ignored ???
+ *
+ ****************************************************************************/
+
+#define NO_DIRECTIONS 8
+#define SLOW_IN 3
+#define SLOW_OUT 7
+#define ROUTE_END_FLAG 255
+//#define PLOT_PATHS 1
+#undef PLOT_PATHS
+
+Router::Router(ObjectMan *pObjMan, ResMan *pResMan) {
+ _objMan = pObjMan;
+ _resMan = pResMan;
+ _numExtraBars = _numExtraNodes = 0;
+ nnodes = nbars = 0;
+ _playerTargetX = _playerTargetY = _playerTargetDir = _playerTargetStance = 0;
+ diagonalx = diagonaly = 0;
+}
+
+/*
+ * CODE
+ */
+
+int32 Router::routeFinder(int32 id, Object *megaObject, int32 x, int32 y, int32 dir)
+{
+/****************************************************************************
+ * RouteFinder.C polygon router with modular walks
+ * 21 august 94
+ * 3 november 94
+ * RouteFinder creates a list of modules that enables HardWalk to create
+ * an animation list.
+ *
+ * RouteFinder currently works by scanning grid data and coming up with a ROUTE
+ * as a series of way points(nodes), the smoothest eight directional PATH
+ * through these nodes is then found, this information is made available to
+ * HardWalk for a WALK to be created to fit the PATH.
+ *
+ * 30 november 94 return values modified
+ *
+ * return 0 = failed to find a route
+ *
+ * 1 = found a route
+ *
+ * 2 = mega already at target
+ *
+ ****************************************************************************/
+
+ int32 routeFlag = 0;
+ int32 solidFlag = 0;
+
+ megaId = id;
+
+ LoadWalkResources(megaObject, x, y, dir);
+
+ framesPerStep = nWalkFrames/2;
+ framesPerChar = nWalkFrames * NO_DIRECTIONS;
+
+ // offset pointers added Oct 30 95 JPS
+ standFrames = framesPerChar;
+ turnFramesLeft = standFrames;
+ turnFramesRight = standFrames;
+ walkFramesLeft = 0;
+ walkFramesRight = 0;
+ slowInFrames = 0;
+ slowOutFrames = 0;
+
+ if (megaId == GEORGE)
+ {
+ turnFramesLeft = 3 * framesPerChar + NO_DIRECTIONS + 2 * SLOW_IN + 4 * SLOW_OUT;
+ turnFramesRight = 3 * framesPerChar + NO_DIRECTIONS + 2 * SLOW_IN + 4 * SLOW_OUT + NO_DIRECTIONS;
+ walkFramesLeft = framesPerChar + NO_DIRECTIONS;
+ walkFramesRight = 2 * framesPerChar + NO_DIRECTIONS;
+ slowInFrames = 3 * framesPerChar + NO_DIRECTIONS;
+ slowOutFrames = 3 * framesPerChar + NO_DIRECTIONS + 2 * SLOW_IN;
+ }
+ else if (megaId == NICO)
+ {
+ turnFramesLeft = framesPerChar + NO_DIRECTIONS;
+ turnFramesRight = framesPerChar + 2 * NO_DIRECTIONS;
+ walkFramesLeft = 0;
+ walkFramesRight = 0;
+ slowInFrames = 0;
+ slowOutFrames = 0;
+ }
+
+// **************************************************************************
+// All route data now loaded start finding a route
+// **************************************************************************
+// **************************************************************************
+// Check if we can get a route through the floor changed 12 Oct95 JPS
+// **************************************************************************
+
+ routeFlag = GetRoute();
+
+ if (routeFlag == 2) //special case for zero length route
+ {
+ if (targetDir >7)// if target direction specified as any
+ {
+ targetDir = startDir;
+ }
+ // just a turn on the spot is required set an end module for the route let the animator deal with it
+ // modularPath is normally set by ExtractRoute
+ modularPath[0].dir = startDir;
+ modularPath[0].num = 0;
+ modularPath[0].x = startX;
+ modularPath[0].y = startY;
+ modularPath[1].dir = targetDir;
+ modularPath[1].num = 0;
+ modularPath[1].x = startX;
+ modularPath[1].y = startY;
+ modularPath[2].dir = 9;
+ modularPath[2].num = ROUTE_END_FLAG;
+
+ SlidyWalkAnimator(megaObject->o_route);
+ routeFlag = 2;
+ }
+ else if (routeFlag == 1) // a normal route
+ {
+ SmoothestPath();//Converts the route to an exact path
+ // The Route had waypoints and direction options
+ // The Path is an exact set of lines in 8 directions that reach the target.
+ // The path is in module format, but steps taken in each direction are not accurate
+ // if target dir = 8 then the walk isn't linked to an anim so
+ // we can create a route without sliding and miss the exact target
+ if (targetDir == NO_DIRECTIONS)
+ {
+ SolidPath();
+ solidFlag = SolidWalkAnimator(megaObject->o_route);
+ }
+
+ if (!solidFlag)
+ {
+ SlidyPath();
+ SlidyWalkAnimator(megaObject->o_route);
+ }
+ }
+ else // Route didn't reach target so assume point was off the floor
+ {
+// routeFlag = 0;
+ }
+ return routeFlag; // send back null route
+}
+
+// ****************************************************************************
+// * GET A ROUTE
+// ****************************************************************************
+
+int32 Router::GetRoute()
+{
+ /****************************************************************************
+ * GetRoute.C extract a path from walk grid
+ * 12 october 94
+ *
+ * GetRoute currently works by scanning grid data and coming up with a ROUTE
+ * as a series of way points(nodes).
+ * static _routeData route[O_ROUTE_SIZE];
+ *
+ * return 0 = failed to find a route
+ *
+ * 1 = found a route
+ *
+ * 2 = mega already at target
+ *
+ * 3 = failed to find a route because target was on a line
+ *
+ ****************************************************************************/
+ int32 routeGot = 0;
+ int32 level;
+ int32 changed;
+
+ if ((startX == targetX) && (startY == targetY))
+ routeGot = 2;
+
+ else // 'else' added by JEL (23jan96) otherwise 'routeGot' affected even when already set to '2' above - causing some 'turns' to walk downwards on the spot
+ routeGot = CheckTarget(targetX,targetY);// returns 3 if target on a line ( +- 1 pixel )
+
+
+ if (routeGot == 0) //still looking for a route check if target is within a pixel of a line
+ {
+ // scan through the nodes linking each node to its nearest neighbour until no more nodes change
+ // This is the routine that finds a route using Scan()
+ level = 1;
+ do
+ {
+ changed = Scan(level);
+ level =level + 1;
+ }
+ while (changed == 1);
+
+ // Check to see if the route reached the target
+ if (node[nnodes].dist < 9999)
+ {
+ routeGot = 1;
+ ExtractRoute(); // it did so extract the route as nodes and the directions to go between each node
+ // route.X,route.Y and route.Dir now hold all the route infomation with the target dir or route continuation
+ }
+ }
+
+ return routeGot;
+}
+
+// ****************************************************************************
+// * THE SLIDY PATH ROUTINES
+// ****************************************************************************
+
+int32 Router::SmoothestPath()
+{
+/*
+ * This is the second big part of the route finder and the the only bit that tries to be clever
+ * (the other bits are clever).
+ * This part of the autorouter creates a list of modules from a set of lines running across the screen
+ * The task is complicated by two things;
+ * Firstly in chosing a route through the maze of nodes the routine tries to minimise the amount of each
+ * individual turn avoiding 90 degree and greater turns (where possible) and reduces the total nuber of
+ * turns (subject to two 45 degree turns being better than one 90 degree turn).
+ * Secondly when walking in a given direction the number of steps required to reach the end of that run
+ * is not calculated accurately. This is because I was unable to derive a function to relate number of
+ * steps taken between two points to the shrunken step size
+ *
+ */
+ int32 p;
+ int32 dirS;
+ int32 dirD;
+ int32 dS;
+ int32 dD;
+ int32 dSS;
+ int32 dSD;
+ int32 dDS;
+ int32 dDD;
+ int32 SS;
+ int32 SD;
+ int32 DS;
+ int32 DD;
+ int32 i;
+ int32 j;
+ int32 temp;
+ int32 steps;
+ int32 option;
+ int32 options;
+ int32 lastDir;
+ int32 nextDirS;
+ int32 nextDirD;
+ int32 tempturns[4];
+ int32 turns[4];
+ int32 turntable[NO_DIRECTIONS] = {0,1,3,5,7,5,3,1};
+
+// targetDir;// no warnings
+
+ // route.X route.Y and route.Dir start at far end
+ smoothPath[0].x = startX;
+ smoothPath[0].y = startY;
+ smoothPath[0].dir = startDir;
+ smoothPath[0].num = 0;
+ p = 0;
+ lastDir = startDir;
+ // for each section of the route
+ do
+ {
+ dirS = route[p].dirS;
+ dirD = route[p].dirD;
+ nextDirS = route[p+1].dirS;
+ nextDirD = route[p+1].dirD;
+
+ // Check directions into and out of a pair of nodes
+ // going in
+ dS = dirS - lastDir;
+ if ( dS < 0)
+ dS = dS + NO_DIRECTIONS;
+
+ dD = dirD - lastDir;
+ if ( dD < 0)
+ dD = dD + NO_DIRECTIONS;
+
+ // coming out
+ dSS = dirS - nextDirS;
+ if ( dSS < 0)
+ dSS = dSS + NO_DIRECTIONS;
+
+ dDD = dirD - nextDirD;
+ if ( dDD < 0)
+ dDD = dDD + NO_DIRECTIONS;
+
+ dSD = dirS - nextDirD;
+ if ( dSD < 0)
+ dSD = dSD + NO_DIRECTIONS;
+
+ dDS = dirD - nextDirS;
+ if ( dDS < 0)
+ dDS = dDS + NO_DIRECTIONS;
+
+ // Determine the amount of turning involved in each possible path
+ dS = turntable[dS];
+ dD = turntable[dD];
+ dSS = turntable[dSS];
+ dDD = turntable[dDD];
+ dSD = turntable[dSD];
+ dDS = turntable[dDS];
+ // get the best path out ie assume next section uses best direction
+ if (dSD < dSS)
+ {
+ dSS = dSD;
+ }
+ if (dDS < dDD)
+ {
+ dDD = dDS;
+ }
+ // rate each option
+ SS = dS + dSS + 3; // Split routes look crap so weight against them
+ SD = dS + dDD;
+ DS = dD + dSS;
+ DD = dD + dDD + 3;
+ // set up turns as a sorted array of the turn values
+ tempturns[0] = SS;
+ turns[0] = 0;
+ tempturns[1] = SD;
+ turns[1] = 1;
+ tempturns[2] = DS;
+ turns[2] = 2;
+ tempturns[3] = DD;
+ turns[3] = 3;
+ i = 0;
+ do
+ {
+ j = 0;
+ do
+ {
+ if (tempturns[j] > tempturns[j + 1])
+ {
+ temp = turns[j];
+ turns[j] = turns[j+1];
+ turns[j+1] = temp;
+ temp = tempturns[j];
+ tempturns[j] = tempturns[j+1];
+ tempturns[j+1] = temp;
+ }
+ j = j + 1;
+ }
+ while (j < 3);
+ i = i + 1;
+ }
+ while (i < 3);
+
+ // best option matched in order of the priority we would like to see on the screen
+ // but each option must be checked to see if it can be walked
+
+ options = NewCheck(1, route[p].x, route[p].y, route[p + 1].x, route[p + 1].y);
+
+ if (options == 0)
+ {
+ /*Tdebug("BestTurns fail %d %d %d %d",route[p].x, route[p].y, route[p + 1].x, route[p + 1].y);
+ Tdebug("BestTurns fail %d %d %d %d",turns[0],turns[1],turns[2],options);
+ Go_dos("BestTurns failed");*/
+ error("BestTurns failed");
+ }
+ i = 0;
+ steps = 0;
+ do
+ {
+ option = 1 << turns[i];
+ if (option & options)
+ steps = SmoothCheck(turns[i],p,dirS,dirD);
+ i = i + 1;
+ }
+ while ((steps == 0) && (i < 4));
+
+#ifdef PLOT_PATHS // plot the best path
+ if (steps != 0)
+ {
+ i = 0;
+ do
+ {
+ RouteLine(smoothPath[i].x, smoothPath[i].y, smoothPath[i+1].x, smoothPath[i+1].y, 228);
+ i = i + 1;
+ }
+ while (i < steps);
+ }
+#endif
+
+ if (steps == 0)
+ {
+ /*Tdebug("BestTurns failed %d %d %d %d",route[p].x, route[p].y, route[p + 1].x, route[p + 1].y);
+ Tdebug("BestTurns failed %d %d %d %d",turns[0],turns[1],turns[2],options);
+ Go_dos("BestTurns failed");*/
+ error("BestTurns failed");
+ }
+ // route.X route.Y route.dir and bestTurns start at far end
+ p = p + 1;
+
+
+ }
+ while (p < (routeLength));
+ // best turns will end heading as near as possible to target dir rest is down to anim for now
+ smoothPath[steps].dir = 9;
+ smoothPath[steps].num = ROUTE_END_FLAG;
+ return 1;
+}
+
+
+
+
+int32 Router::SmoothCheck(int32 best, int32 p, int32 dirS, int32 dirD)
+/****************************************************************************
+ * Slip sliding away
+ * This path checker checks to see if a walk that exactly follows the path
+ * would be valid. This should be inherently true for atleast one of the turn
+ * options.
+ * No longer checks the data it only creates the smoothPath array JPS
+ ****************************************************************************/
+{
+ static int32 k;
+ int32 tempK;
+ int32 x;
+ int32 y;
+ int32 x2;
+ int32 y2;
+ int32 dx;
+ int32 dy;
+ int32 dsx;
+ int32 dsy;
+ int32 ddx;
+ int32 ddy;
+ int32 dirX;
+ int32 dirY;
+ int32 ss0;
+ int32 ss1;
+ int32 ss2;
+ int32 sd0;
+ int32 sd1;
+ int32 sd2;
+
+ if (p == 0)
+ {
+ k = 1;
+ }
+ tempK = 0;
+ x = route[p].x;
+ y = route[p].y;
+ x2 = route[p + 1].x;
+ y2 = route[p + 1].y;
+ dx = x2 - x;
+ dy = y2 - y;
+ dirX = 1;
+ dirY = 1;
+ if (dx < 0)
+ {
+ dx = -dx;
+ dirX = -1;
+ }
+
+ if (dy < 0)
+ {
+ dy = -dy;
+ dirY = -1;
+ }
+
+// set up sd0-ss2 to reflect possible movement in each direction
+ if ((dirS == 0) || (dirS == 4))// vert and diag
+ {
+ ddx = dx;
+ ddy = (dx*diagonaly)/diagonalx;
+ dsy = dy - ddy;
+ ddx = ddx * dirX;
+ ddy = ddy * dirY;
+ dsy = dsy * dirY;
+ dsx = 0;
+
+ sd0 = (ddx + modX[dirD]/2)/ modX[dirD];
+ ss0 = (dsy + modY[dirS]/2) / modY[dirS];
+ sd1 = sd0/2;
+ ss1 = ss0/2;
+ sd2 = sd0 - sd1;
+ ss2 = ss0 - ss1;
+ }
+ else
+ {
+ ddy = dy;
+ ddx = (dy*diagonalx)/diagonaly;
+ dsx = dx - ddx;
+ ddy = ddy * dirY;
+ ddx = ddx * dirX;
+ dsx = dsx * dirX;
+ dsy = 0;
+
+ sd0 = (ddy + modY[dirD]/2)/ modY[dirD];
+ ss0 = (dsx + modX[dirS]/2)/ modX[dirS];
+ sd1 = sd0/2;
+ ss1 = ss0/2;
+ sd2 = sd0 - sd1;
+ ss2 = ss0 - ss1;
+ }
+
+ if (best == 0) //halfsquare, diagonal, halfsquare
+ {
+ smoothPath[k].x = x+dsx/2;
+ smoothPath[k].y = y+dsy/2;
+ smoothPath[k].dir = dirS;
+ smoothPath[k].num = ss1;
+ k = k + 1;
+ smoothPath[k].x = x+dsx/2+ddx;
+ smoothPath[k].y = y+dsy/2+ddy;
+ smoothPath[k].dir = dirD;
+ smoothPath[k].num = sd0;
+ k = k + 1;
+ smoothPath[k].x = x+dsx+ddx;
+ smoothPath[k].y = y+dsy+ddy;
+ smoothPath[k].dir = dirS;
+ smoothPath[k].num = ss2;
+ k = k + 1;
+ tempK = k;
+ }
+ else if (best == 1) //square, diagonal
+ {
+ smoothPath[k].x = x+dsx;
+ smoothPath[k].y = y+dsy;
+ smoothPath[k].dir = dirS;
+ smoothPath[k].num = ss0;
+ k = k + 1;
+ smoothPath[k].x = x2;
+ smoothPath[k].y = y2;
+ smoothPath[k].dir = dirD;
+ smoothPath[k].num = sd0;
+ k = k + 1;
+ tempK = k;
+ }
+ else if (best == 2) //diagonal square
+ {
+ smoothPath[k].x = x+ddx;
+ smoothPath[k].y = y+ddy;
+ smoothPath[k].dir = dirD;
+ smoothPath[k].num = sd0;
+ k = k + 1;
+ smoothPath[k].x = x2;
+ smoothPath[k].y = y2;
+ smoothPath[k].dir = dirS;
+ smoothPath[k].num = ss0;
+ k = k + 1;
+ tempK = k;
+ }
+ else //halfdiagonal, square, halfdiagonal
+ {
+ smoothPath[k].x = x+ddx/2;
+ smoothPath[k].y = y+ddy/2;
+ smoothPath[k].dir = dirD;
+ smoothPath[k].num = sd1;
+ k = k + 1;
+ smoothPath[k].x = x+dsx+ddx/2;
+ smoothPath[k].y = y+dsy+ddy/2;
+ smoothPath[k].dir = dirS;
+ smoothPath[k].num = ss0;
+ k = k + 1;
+ smoothPath[k].x = x2;
+ smoothPath[k].y = y2;
+ smoothPath[k].dir = dirD;
+ smoothPath[k].num = sd2;
+ k = k + 1;
+ tempK = k;
+ }
+
+ return tempK;
+}
+
+int32 Router::SlidyPath()
+{
+/****************************************************************************
+ * SlidyPath creates a path based on part steps with no sliding to get
+ * as near as possible to the target without any sliding this routine is
+ * currently unused, but is intended for use when just clicking about.
+ *
+ * produce a module list from the line data
+ *
+ ****************************************************************************/
+ int32 smooth;
+ int32 slidy;
+ int32 scale;
+ int32 stepX;
+ int32 stepY;
+ int32 deltaX;
+ int32 deltaY;
+
+ // strip out the short sections
+ slidy = 1;
+ smooth = 1;
+ modularPath[0].x = smoothPath[0].x;
+ modularPath[0].y = smoothPath[0].y;
+ modularPath[0].dir = smoothPath[0].dir;
+ modularPath[0].num = 0;
+
+ while (smoothPath[smooth].num < ROUTE_END_FLAG)
+ {
+ scale = scaleA * smoothPath[smooth].y + scaleB;
+ deltaX = smoothPath[smooth].x - modularPath[slidy-1].x;
+ deltaY = smoothPath[smooth].y - modularPath[slidy-1].y;
+ stepX = modX[smoothPath[smooth].dir];
+ stepY = modY[smoothPath[smooth].dir];
+ stepX = stepX * scale;
+ stepY = stepY * scale;
+ stepX = stepX >> 19;// quarter a step minimum
+ stepY = stepY >> 19;
+ if ((ABS(deltaX)>=ABS(stepX)) && (ABS(deltaY)>=ABS(stepY)))
+ {
+ modularPath[slidy].x = smoothPath[smooth].x;
+ modularPath[slidy].y = smoothPath[smooth].y;
+ modularPath[slidy].dir = smoothPath[smooth].dir;
+ modularPath[slidy].num = 1;
+ slidy += 1;
+ }
+ smooth += 1;
+ }
+ // in case the last bit had no steps
+ if (slidy > 1)
+ {
+ modularPath[slidy-1].x = smoothPath[smooth-1].x;
+ modularPath[slidy-1].y = smoothPath[smooth-1].y;
+ }
+ // set up the end of the walk
+ modularPath[slidy].x = smoothPath[smooth-1].x;
+ modularPath[slidy].y = smoothPath[smooth-1].y;
+ modularPath[slidy].dir = targetDir;
+ modularPath[slidy].num = 0;
+ slidy += 1;
+ modularPath[slidy].x = smoothPath[smooth-1].x;
+ modularPath[slidy].y = smoothPath[smooth-1].y;
+ modularPath[slidy].dir = 9;
+ modularPath[slidy].num = ROUTE_END_FLAG;
+ return 1;
+
+}
+
+void Router::SlidyWalkAnimator(WalkData *walkAnim)
+/****************************************************************************
+ * Skidding every where HardWalk creates an animation that exactly fits the
+ * smoothPath and uses foot slipping to fit whole steps into the route
+ * Parameters: georgeg,mouseg
+ * Returns: rout
+ *
+ * produce a module list from the line data
+ *
+ ****************************************************************************/
+{
+
+ static int32 left = 0;
+ int32 p;
+ int32 lastDir;
+ int32 lastRealDir;
+ int32 currentDir;
+ int32 turnDir;
+ int32 scale;
+ int32 step;
+ int32 module;
+ int32 moduleEnd;
+ int32 moduleX;
+ int32 moduleY;
+ int32 module16X = 0;
+ int32 module16Y = 0;
+ int32 stepX;
+ int32 stepY;
+ int32 errorX;
+ int32 errorY;
+ int32 lastErrorX;
+ int32 lastErrorY;
+ int32 lastCount;
+ int32 stepCount;
+ int32 frameCount;
+ int32 frames;
+ int32 frame;
+
+ // start at the begining for a change
+ p = 0;
+ lastDir = modularPath[0].dir;
+ currentDir = modularPath[1].dir;
+ if (currentDir == NO_DIRECTIONS)
+ {
+ currentDir = lastDir;
+ }
+ moduleX = startX;
+ moduleY = startY;
+ module16X = moduleX << 16;
+ module16Y = moduleY << 16;
+ stepCount = 0;
+
+ //****************************************************************************
+ // SLIDY
+ // START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY
+ // BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED
+ //****************************************************************************
+ module = framesPerChar + lastDir;
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+
+ //****************************************************************************
+ // SLIDY
+ // TURN TO START THE WALK
+ //****************************************************************************
+ // rotate if we need to
+ if (lastDir != currentDir)
+ {
+ // get the direction to turn
+ turnDir = currentDir - lastDir;
+ if ( turnDir < 0)
+ turnDir += NO_DIRECTIONS;
+
+ if (turnDir > 4)
+ turnDir = -1;
+ else if (turnDir > 0)
+ turnDir = 1;
+
+ // rotate to new walk direction
+ // for george and nico put in a head turn at the start
+ if ((megaId == GEORGE) || (megaId == NICO))
+ {
+ if ( turnDir < 0) // new frames for turn frames 29oct95jps
+ {
+ module = turnFramesLeft + lastDir;
+ }
+ else
+ {
+ module = turnFramesRight + lastDir;
+ }
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+
+ // rotate till were facing new dir then go back 45 degrees
+ while (lastDir != currentDir)
+ {
+ lastDir += turnDir;
+ if ( turnDir < 0) // new frames for turn frames 29oct95jps
+ {
+ if ( lastDir < 0)
+ lastDir += NO_DIRECTIONS;
+ module = turnFramesLeft + lastDir;
+ }
+ else
+ {
+ if ( lastDir > 7)
+ lastDir -= NO_DIRECTIONS;
+ module = turnFramesRight + lastDir;
+ }
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ // the back 45 degrees bit
+ stepCount -= 1;// step back one because new head turn for george takes us past the new dir
+ }
+ // his head is in the right direction
+ lastRealDir = currentDir;
+
+ //****************************************************************************
+ // SLIDY
+ // THE WALK
+ //****************************************************************************
+
+ if (left == 0)
+ left = framesPerStep;
+ else
+ left = 0;
+
+ lastCount = stepCount;
+ lastDir = 99;// this ensures that we don't put in turn frames for the start
+ currentDir = 99;// this ensures that we don't put in turn frames for the start
+ do
+ {
+ while (modularPath[p].num == 0)
+ {
+ p = p + 1;
+ if (currentDir != 99)
+ lastRealDir = currentDir;
+ lastDir = currentDir;
+ lastCount = stepCount;
+ }
+ //calculate average amount to lose in each step on the way to the next node
+ currentDir = modularPath[p].dir;
+ if (currentDir < NO_DIRECTIONS)
+ {
+ module = currentDir * framesPerStep * 2 + left;
+ if (left == 0)
+ left = framesPerStep;
+ else
+ left = 0;
+ moduleEnd = module + framesPerStep;
+ step = 0;
+ scale = (scaleA * moduleY + scaleB);
+ do
+ {
+ module16X += _dx[module]*scale;
+ module16Y += _dy[module]*scale;
+ moduleX = module16X >> 16;
+ moduleY = module16Y >> 16;
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = step;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ step += 1;
+ module += 1;
+ }
+ while ( module < moduleEnd) ;
+ stepX = modX[modularPath[p].dir];
+ stepY = modY[modularPath[p].dir];
+ errorX = modularPath[p].x - moduleX;
+ errorX = errorX * stepX;
+ errorY = modularPath[p].y - moduleY;
+ errorY = errorY * stepY;
+ if ((errorX < 0) || (errorY < 0))
+ {
+ modularPath[p].num = 0; // the end of the path
+ // okay those last steps took us past our target but do we want to scoot or moonwalk
+ frames = stepCount - lastCount;
+ errorX = modularPath[p].x - walkAnim[stepCount-1].x;
+ errorY = modularPath[p].y - walkAnim[stepCount-1].y;
+
+ if (frames > framesPerStep)
+ {
+ lastErrorX = modularPath[p].x - walkAnim[stepCount-7].x;
+ lastErrorY = modularPath[p].y - walkAnim[stepCount-7].y;
+ if (stepX==0)
+ {
+ if (3*ABS(lastErrorY) < ABS(errorY)) //the last stop was closest
+ {
+ stepCount -= framesPerStep;
+ if (left == 0)
+ left = framesPerStep;
+ else
+ left = 0;
+ }
+ }
+ else
+ {
+ if (3*ABS(lastErrorX) < ABS(errorX)) //the last stop was closest
+ {
+ stepCount -= framesPerStep;
+ if (left == 0)
+ left = framesPerStep;
+ else
+ left = 0;
+ }
+ }
+ }
+ errorX = modularPath[p].x - walkAnim[stepCount-1].x;
+ errorY = modularPath[p].y - walkAnim[stepCount-1].y;
+ // okay we've reached the end but we still have an error
+ if (errorX != 0)
+ {
+ frameCount = 0;
+ frames = stepCount - lastCount;
+ do
+ {
+ frameCount += 1;
+ walkAnim[lastCount + frameCount - 1].x += errorX*frameCount/frames;
+ }
+ while (frameCount<frames);
+ }
+ if (errorY != 0)
+ {
+ frameCount = 0;
+ frames = stepCount - lastCount;
+ do
+ {
+ frameCount += 1;
+ walkAnim[lastCount + frameCount-1].y += errorY*frameCount/frames;
+ }
+ while (frameCount<frames);
+ }
+ // Now is the time to put in the turn frames for the last turn
+ if (frames < framesPerStep)
+ currentDir = 99;// this ensures that we don't put in turn frames for this walk or the next
+ if (currentDir != 99)
+ lastRealDir = currentDir;
+ // check each turn condition in turn
+ if (((lastDir != 99) && (currentDir != 99)) && (megaId == GEORGE)) // only for george
+ {
+ lastDir = currentDir - lastDir;//1 and -7 going right -1 and 7 going left
+ if (((lastDir == -1) || (lastDir == 7)) || ((lastDir == -2) || (lastDir == 6)))
+ {
+ // turn at the end of the last walk
+ frame = lastCount - framesPerStep;
+ do
+ {
+ walkAnim[frame].frame += 104;//turning left
+ frame += 1;
+ }
+ while (frame < lastCount );
+ }
+ if (((lastDir == 1) || (lastDir == -7)) || ((lastDir == 2) || (lastDir == -6)))
+ {
+ // turn at the end of the current walk
+ frame = lastCount - framesPerStep;
+ do
+ {
+ walkAnim[frame].frame += 200; //was 60 now 116
+ frame += 1;
+ }
+ while (frame < lastCount );
+ }
+ lastDir = currentDir;
+ }
+ // all turns checked
+
+ lastCount = stepCount;
+ moduleX = walkAnim[stepCount-1].x;
+ moduleY = walkAnim[stepCount-1].y;
+ module16X = moduleX << 16;
+ module16Y = moduleY << 16;
+ }
+ }
+ }
+ while (modularPath[p].dir < NO_DIRECTIONS);
+
+
+
+ if (lastRealDir == 99)
+ {
+ error("SlidyWalkAnimatorlast direction error\n");
+ }
+ //****************************************************************************
+ // SLIDY
+ // TURNS TO END THE WALK ?
+ //****************************************************************************
+
+ // We've done the walk now put in any turns at the end
+
+
+ if (targetDir == NO_DIRECTIONS) // stand in the last direction
+ {
+ module = standFrames + lastRealDir;
+ targetDir = lastRealDir;
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastRealDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ if (targetDir == 9)
+ {
+ if (stepCount == 0)
+ {
+ module = framesPerChar + lastRealDir;
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastRealDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ }
+ else if (targetDir != lastRealDir) // rotate to targetDir
+ {
+ // rotate to target direction
+ turnDir = targetDir - lastRealDir;
+ if ( turnDir < 0)
+ turnDir += NO_DIRECTIONS;
+
+ if (turnDir > 4)
+ turnDir = -1;
+ else if (turnDir > 0)
+ turnDir = 1;
+
+ // rotate to target direction
+ // for george and nico put in a head turn at the start
+ if ((megaId == GEORGE) || (megaId == NICO))
+ {
+ if ( turnDir < 0) // new frames for turn frames 29oct95jps
+ {
+ module = turnFramesLeft + lastDir;
+ }
+ else
+ {
+ module = turnFramesRight + lastDir;
+ }
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastRealDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+
+ // rotate if we need to
+ while (lastRealDir != targetDir)
+ {
+ lastRealDir += turnDir;
+ if ( turnDir < 0) // new frames for turn frames 29oct95jps
+ {
+ if ( lastRealDir < 0)
+ lastRealDir += NO_DIRECTIONS;
+ module = turnFramesLeft + lastRealDir;
+ }
+ else
+ {
+ if ( lastRealDir > 7)
+ lastRealDir -= NO_DIRECTIONS;
+ module = turnFramesRight + lastRealDir;
+ }
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastRealDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ module = standFrames + lastRealDir;
+ walkAnim[stepCount-1].frame = module;
+ }
+ else // just stand at the end
+ {
+ module = standFrames + lastRealDir;
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastRealDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+
+ walkAnim[stepCount].frame = 512;
+ stepCount += 1;
+ walkAnim[stepCount].frame = 512;
+ stepCount += 1;
+ walkAnim[stepCount].frame = 512;
+// Tdebug("RouteFinder RouteSize is %d", stepCount);
+ return;
+}
+
+// ****************************************************************************
+// * THE SOLID PATH ROUTINES
+// ****************************************************************************
+
+int32 Router::SolidPath()
+{
+/****************************************************************************
+ * SolidPath creates a path based on whole steps with no sliding to get
+ * as near as possible to the target without any sliding this routine is
+ * currently unused, but is intended for use when just clicking about.
+ *
+ * produce a module list from the line data
+ *
+ ****************************************************************************/
+ int32 smooth;
+ int32 solid;
+ int32 scale;
+ int32 stepX;
+ int32 stepY;
+ int32 deltaX;
+ int32 deltaY;
+
+ // strip out the short sections
+ solid = 1;
+ smooth = 1;
+ modularPath[0].x = smoothPath[0].x;
+ modularPath[0].y = smoothPath[0].y;
+ modularPath[0].dir = smoothPath[0].dir;
+ modularPath[0].num = 0;
+
+ do
+ {
+ scale = scaleA * smoothPath[smooth].y + scaleB;
+ deltaX = smoothPath[smooth].x - modularPath[solid-1].x;
+ deltaY = smoothPath[smooth].y - modularPath[solid-1].y;
+ stepX = modX[smoothPath[smooth].dir];
+ stepY = modY[smoothPath[smooth].dir];
+ stepX = stepX * scale;
+ stepY = stepY * scale;
+ stepX = stepX >> 16;
+ stepY = stepY >> 16;
+ if ((ABS(deltaX)>=ABS(stepX)) && (ABS(deltaY)>=ABS(stepY)))
+ {
+ modularPath[solid].x = smoothPath[smooth].x;
+ modularPath[solid].y = smoothPath[smooth].y;
+ modularPath[solid].dir = smoothPath[smooth].dir;
+ modularPath[solid].num = 1;
+ solid += 1;
+ }
+ smooth += 1;
+ }
+ while (smoothPath[smooth].num < ROUTE_END_FLAG);
+ // in case the last bit had no steps
+ if (solid == 1) //there were no paths so put in a dummy end
+ {
+ solid = 2;
+ modularPath[1].dir = smoothPath[0].dir;
+ modularPath[1].num = 0;
+ }
+ modularPath[solid-1].x = smoothPath[smooth-1].x;
+ modularPath[solid-1].y = smoothPath[smooth-1].y;
+ // set up the end of the walk
+ modularPath[solid].x = smoothPath[smooth-1].x;
+ modularPath[solid].y = smoothPath[smooth-1].y;
+ modularPath[solid].dir = 9;
+ modularPath[solid].num = ROUTE_END_FLAG;
+ return 1;
+
+}
+
+int32 Router::SolidWalkAnimator(WalkData *walkAnim)
+{
+/****************************************************************************
+ * SolidWalk creates an animation based on whole steps with no sliding to get
+ * as near as possible to the target without any sliding this routine is
+ * is intended for use when just clicking about.
+ *
+ * produce a module list from the line data
+ *
+ * returns 0 if solid route not found
+ ****************************************************************************/
+ int32 p;
+ int32 i;
+ int32 left;
+ int32 lastDir;
+ int32 currentDir;
+ int32 turnDir;
+ int32 scale;
+ int32 step;
+ int32 module;
+ int32 moduleX;
+ int32 moduleY;
+ int32 module16X;
+ int32 module16Y;
+ int32 errorX;
+ int32 errorY;
+ int32 moduleEnd;
+ int32 slowStart;
+ int32 stepCount;
+ int32 lastCount;
+ int32 frame;
+
+ // start at the begining for a change
+ lastDir = modularPath[0].dir;
+ p = 1;
+ currentDir = modularPath[1].dir;
+ module = framesPerChar + lastDir;
+ moduleX = startX;
+ moduleY = startY;
+ module16X = moduleX << 16;
+ module16Y = moduleY << 16;
+ slowStart = 0;
+ stepCount = 0;
+
+ //****************************************************************************
+ // SOLID
+ // START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY
+ // BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED
+ //****************************************************************************
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+
+ //****************************************************************************
+ // SOLID
+ // TURN TO START THE WALK
+ //****************************************************************************
+ // rotate if we need to
+ if (lastDir != currentDir)
+ {
+ // get the direction to turn
+ turnDir = currentDir - lastDir;
+ if ( turnDir < 0)
+ turnDir += NO_DIRECTIONS;
+
+ if (turnDir > 4)
+ turnDir = -1;
+ else if (turnDir > 0)
+ turnDir = 1;
+
+ // rotate to new walk direction
+ // for george and nico put in a head turn at the start
+ if ((megaId == GEORGE) || (megaId == NICO))
+ {
+ if ( turnDir < 0) // new frames for turn frames 29oct95jps
+ {
+ module = turnFramesLeft + lastDir;
+ }
+ else
+ {
+ module = turnFramesRight + lastDir;
+ }
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+
+ // rotate till were facing new dir then go back 45 degrees
+ while (lastDir != currentDir)
+ {
+ lastDir += turnDir;
+ if ( turnDir < 0) // new frames for turn frames 29oct95jps
+ {
+ if ( lastDir < 0)
+ lastDir += NO_DIRECTIONS;
+ module = turnFramesLeft + lastDir;
+ }
+ else
+ {
+ if ( lastDir > 7)
+ lastDir -= NO_DIRECTIONS;
+ module = turnFramesRight + lastDir;
+ }
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = lastDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ // the back 45 degrees bit
+ stepCount -= 1;// step back one because new head turn for george takes us past the new dir
+ }
+
+ //****************************************************************************
+ // SOLID
+ // THE SLOW IN
+ //****************************************************************************
+
+ // do start frames if its george and left or right
+ if (megaId == GEORGE)
+ {
+ if (modularPath[1].num > 0)
+ {
+ if (currentDir == 2) // only for george
+ {
+ slowStart = 1;
+ walkAnim[stepCount].frame = 296;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ walkAnim[stepCount].frame = 297;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ walkAnim[stepCount].frame = 298;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ else if (currentDir == 6) // only for george
+ {
+ slowStart = 1;
+ walkAnim[stepCount].frame = 299;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ walkAnim[stepCount].frame = 300;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ walkAnim[stepCount].frame = 301;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ }
+ }
+ //****************************************************************************
+ // SOLID
+ // THE WALK
+ //****************************************************************************
+
+ if (currentDir > 4)
+ left = framesPerStep;
+ else
+ left = 0;
+
+ lastCount = stepCount;
+ lastDir = 99;// this ensures that we don't put in turn frames for the start
+ currentDir = 99;// this ensures that we don't put in turn frames for the start
+
+ do
+ {
+ while (modularPath[p].num > 0)
+ {
+ currentDir = modularPath[p].dir;
+ if (currentDir< NO_DIRECTIONS)
+ {
+
+ module = currentDir * framesPerStep * 2 + left;
+ if (left == 0)
+ left = framesPerStep;
+ else
+ left = 0;
+ moduleEnd = module + framesPerStep;
+ step = 0;
+ scale = (scaleA * moduleY + scaleB);
+ do
+ {
+ module16X += _dx[module]*scale;
+ module16Y += _dy[module]*scale;
+ moduleX = module16X >> 16;
+ moduleY = module16Y >> 16;
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = step;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ module += 1;
+ step += 1;
+ }
+ while ( module < moduleEnd) ;
+ errorX = modularPath[p].x - moduleX;
+ errorX = errorX * modX[modularPath[p].dir];
+ errorY = modularPath[p].y - moduleY;
+ errorY = errorY * modY[modularPath[p].dir];
+ if ((errorX < 0) || (errorY < 0))
+ {
+ modularPath[p].num = 0;
+ stepCount -= framesPerStep;
+ if (left == 0)
+ left = framesPerStep;
+ else
+ left = 0;
+ // Okay this is the end of a section
+ moduleX = walkAnim[stepCount-1].x;
+ moduleY = walkAnim[stepCount-1].y;
+ module16X = moduleX << 16;
+ module16Y = moduleY << 16;
+ modularPath[p].x =moduleX;
+ modularPath[p].y =moduleY;
+ // Now is the time to put in the turn frames for the last turn
+ if ((stepCount - lastCount) < framesPerStep)// no step taken
+ {
+ currentDir = 99;// this ensures that we don't put in turn frames for this walk or the next
+ if (slowStart == 1)// clean up if a slow in but no walk
+ {
+ stepCount -= 3;
+ lastCount -= 3;
+ slowStart = 0;
+ }
+ }
+ // check each turn condition in turn
+ if (((lastDir != 99) && (currentDir != 99)) && (megaId == GEORGE)) // only for george
+ {
+ lastDir = currentDir - lastDir;//1 and -7 going right -1 and 7 going left
+ if (((lastDir == -1) || (lastDir == 7)) || ((lastDir == -2) || (lastDir == 6)))
+ {
+ // turn at the end of the last walk
+ frame = lastCount - framesPerStep;
+ do
+ {
+ walkAnim[frame].frame += 104;//turning left
+ frame += 1;
+ }
+ while (frame < lastCount );
+ }
+ if (((lastDir == 1) || (lastDir == -7)) || ((lastDir == 2) || (lastDir == -6)))
+ {
+ // turn at the end of the current walk
+ frame = lastCount - framesPerStep;
+ do
+ {
+ walkAnim[frame].frame += 200; //was 60 now 116
+ frame += 1;
+ }
+ while (frame < lastCount );
+ }
+ }
+ // all turns checked
+ lastCount = stepCount;
+ }
+ }
+ }
+ p = p + 1;
+ lastDir = currentDir;
+ slowStart = 0; //can only be valid first time round
+ }
+ while (modularPath[p].dir < NO_DIRECTIONS);
+
+ //****************************************************************************
+ // SOLID
+ // THE SLOW OUT
+ //****************************************************************************
+
+ if ((currentDir == 2) && (megaId == GEORGE)) // only for george
+ {
+ // place stop frames here
+ // slowdown at the end of the last walk
+ frame = lastCount - framesPerStep;
+ if (walkAnim[frame].frame == 24)
+ {
+ do
+ {
+ walkAnim[frame].frame += 278;//stopping right
+ frame += 1;
+ }
+ while (frame < lastCount );
+ walkAnim[stepCount].frame = 308;
+ walkAnim[stepCount].step = 7;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ else if (walkAnim[frame].frame == 30)
+ {
+ do
+ {
+ walkAnim[frame].frame += 279;//stopping right
+ frame += 1;
+ }
+ while (frame < lastCount );
+ walkAnim[stepCount].frame = 315;
+ walkAnim[stepCount].step = 7;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ }
+ else if ((currentDir == 6) && (megaId == GEORGE)) // only for george
+ {
+ // place stop frames here
+ // slowdown at the end of the last walk
+ frame = lastCount - framesPerStep;
+ if (walkAnim[frame].frame == 72)
+ {
+ do
+ {
+ walkAnim[frame].frame += 244;//stopping left
+ frame += 1;
+ }
+ while (frame < lastCount );
+ walkAnim[stepCount].frame = 322;
+ walkAnim[stepCount].step = 7;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ else if (walkAnim[frame].frame == 78)
+ {
+ do
+ {
+ walkAnim[frame].frame += 245;//stopping left
+ frame += 1;
+ }
+ while (frame < lastCount );
+ walkAnim[stepCount].frame = 329;
+ walkAnim[stepCount].step = 7;
+ walkAnim[stepCount].dir = currentDir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+ }
+ }
+ module = framesPerChar + modularPath[p-1].dir;
+ walkAnim[stepCount].frame = module;
+ walkAnim[stepCount].step = 0;
+ walkAnim[stepCount].dir = modularPath[p-1].dir;
+ walkAnim[stepCount].x = moduleX;
+ walkAnim[stepCount].y = moduleY;
+ stepCount += 1;
+
+ walkAnim[stepCount].frame = 512;
+ stepCount += 1;
+ walkAnim[stepCount].frame = 512;
+ stepCount += 1;
+ walkAnim[stepCount].frame = 512;
+
+ //****************************************************************************
+ // SOLID
+ // NO END TURNS
+ //****************************************************************************
+
+// Tdebug("RouteFinder RouteSize is %d", stepCount);
+// now check the route
+ i = 0;
+ do
+ {
+ if (!Check(modularPath[i].x, modularPath[i].y, modularPath[i+1].x, modularPath[i+1].y))
+ p=0;
+#ifdef PLOT_PATHS
+ RouteLine(modularPath[i].x, modularPath[i].y, modularPath[i+1].x, modularPath[i+1].y, 227);
+#endif
+ i += 1;
+ }
+ while (i<p-1);
+ if (p != 0)
+ {
+ targetDir = modularPath[p-1].dir;
+ }
+ if (p != 0)
+ {
+ if (CheckTarget(moduleX,moduleY) == 3)// new target on a line
+ {
+ p = 0;
+ //Tdebug("Solid walk target was on a line %d %d", moduleX, moduleY);
+ }
+ }
+
+ return p;
+}
+
+// ****************************************************************************
+// * THE SCAN ROUTINES
+// ****************************************************************************
+
+int32 Router::Scan(int32 level)
+/******************************************************************************
+ * Called successively from RouteFinder until no more changes take place in the
+ * grid array ie he best path has been found
+ *
+ * Scans through every point in the node array and checks if there is a route
+ * between each point and if this route gives a new route.
+ *
+ * This routine could probably halve its processing time if it doubled up on
+ * the checks after each route check
+ *****************************************************************************/
+{
+ int32 i;
+ int32 k;
+ int32 x1;
+ int32 y1;
+ int32 x2;
+ int32 y2;
+ int32 distance;
+ int32 changed = 0;
+ // For all the nodes that have new values and a distance less than enddist
+ // ie dont check for new routes from a point we checked before or from a point
+ // that is already further away than the best route so far.
+ i = 0;
+ do
+ {
+ if ((node[i].dist < node[nnodes].dist) && (node[i].level == level))
+ {
+ x1 = node[i].x;
+ y1 = node[i].y;
+ k=nnodes;
+ do
+ {
+ if (node[k].dist > node[i].dist)
+ {
+ x2 = node[k].x;
+ y2 = node[k].y;
+
+ if (ABS(x2-x1)>(4.5*ABS(y2-y1)))
+ {
+ distance = (8*ABS(x2-x1)+18*ABS(y2-y1))/(54*8)+1;
+ }
+ else
+ {
+ distance = (6*ABS(x2-x1)+36*ABS(y2-y1))/(36*14)+1;
+ }
+
+ if ((distance + node[i].dist < node[nnodes].dist) && (distance + node[i].dist < node[k].dist))
+ {
+ if (NewCheck(0, x1,y1,x2,y2))
+ {
+ node[k].level = level + 1;
+ node[k].dist = distance + node[i].dist;
+ node[k].prev = i;
+ changed = 1;
+ }
+ }
+ }
+ k-=1;
+ }
+ while (k > 0);
+ }
+ i=i+1;
+ }
+ while (i < nnodes);
+ return changed;
+}
+
+
+int32 Router::NewCheck(int32 status, int32 x1 , int32 y1 , int32 x2 ,int32 y2)
+/******************************************************************************
+ * NewCheck routine checks if the route between two points can be achieved
+ * without crossing any of the bars in the Bars array.
+ *
+ * NewCheck differs from check in that that 4 route options are considered
+ * corresponding to actual walked routes.
+ *
+ * Note distance doesnt take account of shrinking ???
+ *
+ * Note Bars array must be properly calculated ie min max dx dy co
+ *****************************************************************************/
+{
+ int32 dx;
+ int32 dy;
+ int32 dlx;
+ int32 dly;
+ int32 dirX;
+ int32 dirY;
+ int32 step1;
+ int32 step2;
+ int32 step3;
+ int32 steps;
+ int32 options;
+
+ steps = 0;
+ options = 0;
+ dx = x2 - x1;
+ dy = y2 - y1;
+ dirX = 1;
+ dirY = 1;
+ if (dx < 0)
+ {
+ dx = -dx;
+ dirX = -1;
+ }
+
+ if (dy < 0)
+ {
+ dy = -dy;
+ dirY = -1;
+ }
+
+ //make the route options
+ if ((diagonaly * dx) > (diagonalx * dy)) // dir = 1,2 or 2,3 or 5,6 or 6,7
+ {
+ dly = dy;
+ dlx = (dy*diagonalx)/diagonaly;
+ dx = dx - dlx;
+ dlx = dlx * dirX;
+ dly = dly * dirY;
+ dx = dx * dirX;
+ dy = 0;
+
+ //options are
+ //square, diagonal a code 1 route
+ step1 = Check(x1, y1, x1+dx, y1);
+ if (step1 != 0)
+ {
+ step2 = Check(x1+dx, y1, x2, y2);
+ if (step2 != 0)
+ {
+ steps = step1 + step2; // yes
+ options = options + 2;
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1, y1, x1+dx, y1, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1+dx, y1, x2, y2, 231);
+#endif
+ }
+ }
+ //diagonal, square a code 2 route
+ if ((steps == 0) || (status == 1))
+ {
+ step1 = Check(x1, y1, x1+dlx,y1+dly);
+ if (step1 != 0)
+ {
+ step2 = Check(x1+dlx, y2, x2, y2);
+ if (step2 != 0)
+ {
+ steps = step1 + step2; // yes
+ options = options + 4;
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1, y1, x1+dlx,y1+dly, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1+dlx, y2, x2, y2, 231);
+#endif
+ }
+ }
+ }
+ //halfsquare, diagonal, halfsquare a code 0 route
+ if ((steps == 0) || (status == 1))
+ {
+ step1 = Check(x1, y1, x1+dx/2, y1);
+ if (step1 != 0)
+ {
+ step2 = Check(x1+dx/2, y1, x1+dx/2+dlx, y2);
+ if (step2 != 0)
+ {
+ step3 = Check(x1+dx/2+dlx, y2, x2, y2);
+ if (step3 != 0)
+ {
+ steps = step1 + step2 + step3; // yes
+ options = options + 1;
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1, y1, x1+dx/2, y1, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1+dx/2, y1, x1+dx/2+dlx, y2, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1+dx/2+dlx, y2, x2, y2, 231);
+#endif
+ }
+ }
+ }
+ }
+ //halfdiagonal, square, halfdiagonal a code 3 route
+ if ((steps == 0) || (status == 1))
+ {
+ step1 = Check(x1, y1, x1+dlx/2, y1+dly/2);
+ if (step1 != 0)
+ {
+ step2 = Check(x1+dlx/2, y1+dly/2, x1+dx+dlx/2, y1+dly/2);
+ if (step2 != 0)
+ {
+ step3 = Check(x1+dx+dlx/2, y1+dly/2, x2, y2);
+ if (step3 != 0)
+ {
+ steps = step1 + step2 + step3; // yes
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1, y1, x1+dlx/2, y1+dly/2, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1+dlx/2, y1+dly/2, x1+dx+dlx/2, y1+dly/2, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1+dx+dlx/2, y1+dly/2, x2, y2, 231);
+#endif
+ options = options + 8;
+ }
+ }
+ }
+ }
+ }
+ else // dir = 7,0 or 0,1 or 3,4 or 4,5
+ {
+ dlx = dx;
+ dly = (dx*diagonaly)/diagonalx;
+ dy = dy - dly;
+ dlx = dlx * dirX;
+ dly = dly * dirY;
+ dy = dy * dirY;
+ dx = 0;
+
+ //options are
+ //square, diagonal a code 1 route
+ step1 = Check(x1 ,y1 ,x1 ,y1+dy );
+ if (step1 != 0)
+ {
+ step2 = Check(x1 ,y1+dy ,x2,y2);
+ if (step2 != 0)
+ {
+ steps = step1 + step2; // yes
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1 ,y1 ,x1 ,y1+dy, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1 ,y1+dy ,x2, y2, 231);
+#endif
+ options = options + 2;
+ }
+ }
+ //diagonal, square a code 2 route
+ if ((steps == 0) || (status == 1))
+ {
+ step1 = Check(x1, y1, x2, y1+dly);
+ if (step1 != 0)
+ {
+ step2 = Check(x2, y1+dly, x2, y2);
+ if (step2 != 0)
+ {
+ steps = step1 + step2; // yes
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1, y1, x2, y1+dly, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x2, y1+dly, x2, y2, 231);
+#endif
+ options = options + 4;
+ }
+ }
+ }
+ //halfsquare, diagonal, halfsquare a code 0 route
+ if ((steps == 0) || (status == 1))
+ {
+ step1 = Check(x1, y1, x1, y1+dy/2);
+ if (step1 != 0)
+ {
+ step2 = Check(x1, y1+dy/2, x2, y1+dy/2+dly);
+ if (step2 != 0)
+ {
+ step3 = Check(x2, y1+dy/2+dly, x2, y2);
+ if (step3 != 0)
+ {
+ steps = step1 + step2 + step3; // yes
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1, y1, x1, y1+dy/2, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1, y1+dy/2, x2, y1+dy/2+dly, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x2, y1+dy/2+dly, x2, y2, 231);
+#endif
+ options = options + 1;
+ }
+ }
+ }
+ }
+ //halfdiagonal, square, halfdiagonal a code 3 route
+ if ((steps == 0) || (status == 1))
+ {
+ step1 = Check(x1, y1, x1+dlx/2, y1+dly/2);
+ if (step1 != 0)
+ {
+ step2 = Check(x1+dlx/2, y1+dly/2, x1+dlx/2, y1+dy+dly/2);
+ if (step2 != 0)
+ {
+ step3 = Check(x1+dlx/2, y1+dy+dly/2, x2, y2);
+ if (step3 != 0)
+ {
+ steps = step1 + step2 + step3; // yes
+ options = options + 8;
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1, y1, x1+dlx/2, y1+dly/2, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1+dlx/2, y1+dly/2, x1+dlx/2, y1+dy+dly/2, 231);
+#endif
+#ifdef PLOT_PATHS
+ if (status == 1)
+ RouteLine(x1+dlx/2, y1+dy+dly/2, x2, y2, 231);
+#endif
+ }
+ }
+ }
+ }
+ }
+ if (status == 0)
+ {
+ status = steps;
+ }
+ else
+ {
+ status = options;
+ }
+ return status;
+}
+
+// ****************************************************************************
+// * CHECK ROUTINES
+// ****************************************************************************
+
+int32 Router::Check(int32 x1 , int32 y1 , int32 x2 ,int32 y2)
+{
+//call the fastest line check for the given line
+//returns 1 if line didn't cross any bars
+ int32 steps;
+
+ if ((x1 == x2) && (y1 == y2))
+ {
+ steps = 1;
+ }
+ else if (x1 == x2)
+ {
+ steps = VertCheck(x1, y1, y2);
+ }
+ else if (y1 == y2)
+ {
+ steps = HorizCheck(x1, y1, x2);
+ }
+ else
+ {
+ steps = LineCheck(x1, y1, x2, y2);
+ }
+ return steps;
+
+}
+
+
+int32 Router::LineCheck(int32 x1 , int32 y1 , int32 x2 ,int32 y2)
+{
+ int32 dirx;
+ int32 diry;
+ int32 co;
+ int32 slope;
+ int32 i;
+ int32 xc;
+ int32 yc;
+ int32 xmin;
+ int32 ymin;
+ int32 xmax;
+ int32 ymax;
+ int32 linesCrossed = 1;
+
+
+ if (x1 > x2)
+ {
+ xmin = x2;
+ xmax = x1;
+ }
+ else
+ {
+ xmin = x1;
+ xmax = x2;
+ }
+ if (y1 > y2)
+ {
+ ymin = y2;
+ ymax = y1;
+ }
+ else
+ {
+ ymin = y1;
+ ymax = y2;
+ }
+ //line set to go one step in chosen direction
+ //so ignore if it hits anything
+ dirx = x2 - x1;
+ diry = y2 - y1;
+ co = (y1 *dirx)- (x1*diry); //new line equation
+
+ i = 0;
+ do
+ {
+ // this is the inner inner loop
+ if ((xmax >= bars[i].xmin) && ( xmin <= bars[i].xmax)) //skip if not on module
+ {
+ if ((ymax >= bars[i].ymin) && ( ymin <= bars[i].ymax)) //skip if not on module
+ {
+ // okay its a valid line calculate an intersept
+ // wow but all this arithmatic we must have loads of time
+ slope = (bars[i].dx * diry) - (bars[i].dy *dirx);// slope it he slope between the two lines
+ if (slope != 0)//assuming parallel lines don't cross
+ {
+ //calculate x intercept and check its on both lines
+ xc = ((bars[i].co * dirx) - (co * bars[i].dx)) / slope;
+
+ if ((xc >= xmin-1) && (xc <= xmax+1)) //skip if not on module
+ {
+ if ((xc >= bars[i].xmin-1) && (xc <= bars[i].xmax+1)) //skip if not on line
+ {
+
+ yc = ((bars[i].co * diry) - (co * bars[i].dy)) / slope;
+
+ if ((yc >= ymin-1) && (yc <= ymax+1)) //skip if not on module
+ {
+ if ((yc >= bars[i].ymin-1) && (yc <= bars[i].ymax+1)) //skip if not on line
+ {
+ linesCrossed = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ i = i + 1;
+ }
+ while ((i < nbars) && linesCrossed);
+
+ return linesCrossed;
+}
+
+int32 Router::HorizCheck(int32 x1 , int32 y , int32 x2)
+{
+ int32 dy;
+ int32 i;
+ int32 xc;
+ int32 xmin;
+ int32 xmax;
+ int32 linesCrossed = 1;
+
+ if (x1 > x2)
+ {
+ xmin = x2;
+ xmax = x1;
+ }
+ else
+ {
+ xmin = x1;
+ xmax = x2;
+ }
+ //line set to go one step in chosen direction
+ //so ignore if it hits anything
+
+ i = 0;
+ do
+ {
+ // this is the inner inner loop
+ if ((xmax >= bars[i].xmin) && ( xmin <= bars[i].xmax)) //skip if not on module
+ {
+ if ((y >= bars[i].ymin) && ( y <= bars[i].ymax)) //skip if not on module
+ {
+ // okay its a valid line calculate an intersept
+ // wow but all this arithmatic we must have loads of time
+ if (bars[i].dy == 0)
+ {
+ linesCrossed = 0;
+ }
+ else
+ {
+ dy = y-bars[i].y1;
+ xc = bars[i].x1 + (bars[i].dx * dy)/bars[i].dy;
+ if ((xc >= xmin-1) && (xc <= xmax+1)) //skip if not on module
+ {
+ linesCrossed = 0;
+ }
+ }
+ }
+ }
+ i = i + 1;
+ }
+ while ((i < nbars) && linesCrossed);
+
+ return linesCrossed;
+}
+
+
+int32 Router::VertCheck(int32 x, int32 y1, int32 y2)
+{
+ int32 dx;
+ int32 i;
+ int32 yc;
+ int32 ymin;
+ int32 ymax;
+ int32 linesCrossed = 1;
+
+ if (y1 > y2)
+ {
+ ymin = y2;
+ ymax = y1;
+ }
+ else
+ {
+ ymin = y1;
+ ymax = y2;
+ }
+ //line set to go one step in chosen direction
+ //so ignore if it hits anything
+ i = 0;
+ do // this is the inner inner loop
+ {
+ if ((x >= bars[i].xmin) && ( x <= bars[i].xmax)) //overlapping
+ {
+ if ((ymax >= bars[i].ymin) && ( ymin <= bars[i].ymax)) //skip if not on module
+ {
+ // okay its a valid line calculate an intersept
+ // wow but all this arithmatic we must have loads of time
+ if (bars[i].dx == 0)//both lines vertical and overlap in x and y so they cross
+ {
+ linesCrossed = 0;
+ }
+ else
+ {
+ dx = x-bars[i].x1;
+ yc = bars[i].y1 + (bars[i].dy * dx)/bars[i].dx;
+ if ((yc >= ymin-1) && (yc <= ymax+1)) //the intersept overlaps
+ {
+ linesCrossed = 0;
+ }
+ }
+ }
+ }
+ i = i + 1;
+ }
+ while ((i < nbars) && linesCrossed);
+
+ return linesCrossed;
+}
+
+int32 Router::CheckTarget(int32 x , int32 y)
+{
+ int32 dx;
+ int32 dy;
+ int32 i;
+ int32 xc;
+ int32 yc;
+ int32 xmin;
+ int32 xmax;
+ int32 ymin;
+ int32 ymax;
+ int32 onLine = 0;
+
+ xmin = x - 1;
+ xmax = x + 1;
+ ymin = y - 1;
+ ymax = y + 1;
+
+ // check if point +- 1 is on the line
+ //so ignore if it hits anything
+
+ i = 0;
+ do
+ {
+
+ // this is the inner inner loop
+
+ if ((xmax >= bars[i].xmin) && ( xmin <= bars[i].xmax)) //overlapping line
+ {
+ if ((ymax >= bars[i].ymin) && ( ymin <= bars[i].ymax)) //overlapping line
+ {
+
+ // okay this line overlaps the target calculate an y intersept for x
+
+ if (bars[i].dx == 0)// vertical line so we know it overlaps y
+ {
+ yc = 0;
+ }
+ else
+ {
+ dx = x-bars[i].x1;
+ yc = bars[i].y1 + (bars[i].dy * dx)/bars[i].dx;
+ }
+
+ if ((yc >= ymin) && (yc <= ymax)) //overlapping point for y
+ {
+ onLine = 3;// target on a line so drop out
+ //Tdebug("RouteFail due to target on a line %d %d",x,y);
+ }
+ else
+ {
+ if (bars[i].dy == 0)// vertical line so we know it overlaps y
+ {
+ xc = 0;
+ }
+ else
+ {
+ dy = y-bars[i].y1;
+ xc = bars[i].x1 + (bars[i].dx * dy)/bars[i].dy;
+ }
+
+ if ((xc >= xmin) && (xc <= xmax)) //skip if not on module
+ {
+ onLine = 3;// target on a line so drop out
+ //Tdebug("RouteFail due to target on a line %d %d",x,y);
+ }
+ }
+ }
+ }
+ i = i + 1;
+ }
+ while ((i < nbars) && (onLine == 0));
+
+ return onLine;
+}
+
+// ****************************************************************************
+// * THE SETUP ROUTINES
+// ****************************************************************************
+
+int32 Router::LoadWalkResources(Object *megaObject, int32 x, int32 y, int32 dir)
+{
+ WalkGridHeader floorHeader;
+ int32 i;
+ int32 j;
+ uint8 *fPolygrid;
+ uint8 *fMegaWalkData;
+
+ int32 floorId;
+ int32 walkGridResourceId;
+ Object *floorObject;
+
+ int32 cnt;
+ uint32 cntu;
+
+ // load in floor grid for current mega
+
+ floorId = megaObject->o_place;
+
+ //floorObject = (object *) Lock_object(floorId);
+ floorObject = _objMan->fetchObject(floorId);
+ walkGridResourceId = floorObject->o_resource;
+ //Unlock_object(floorId);
+
+ //ResOpen(walkGridResourceId); // mouse wiggle
+ //fPolygrid = ResLock(walkGridResourceId); // mouse wiggle
+ fPolygrid = (uint8*)_resMan->openFetchRes(walkGridResourceId);
+
+
+ fPolygrid += sizeof(Header);
+ memcpy(&floorHeader,fPolygrid,sizeof(WalkGridHeader));
+ fPolygrid += sizeof(WalkGridHeader);
+ nbars = FROM_LE_32(floorHeader.numBars);
+
+ if (nbars >= O_GRID_SIZE)
+ {
+ #ifdef DEBUG //check for id > number in file,
+ error("RouteFinder Error too many bars %d", nbars);
+ #endif
+ nbars = 0;
+ }
+
+ nnodes = FROM_LE_32(floorHeader.numNodes)+1; //array starts at 0 begins at a start node has nnodes nodes and a target node
+
+ if (nnodes >= O_GRID_SIZE)
+ {
+ #ifdef DEBUG //check for id > number in file,
+ error("RouteFinder Error too many nodes %d", nnodes);
+ #endif
+ nnodes = 0;
+ }
+
+ /*memmove(&bars[0],fPolygrid,nbars*sizeof(BarData));
+ fPolygrid += nbars*sizeof(BarData);//move pointer to start of node data*/
+ for (cnt = 0; cnt < nbars; cnt++) {
+ bars[cnt].x1 = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].y1 = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].x2 = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].y2 = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].xmin = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].ymin = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].xmax = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].ymax = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].dx = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].dy = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ bars[cnt].co = READ_LE_UINT32(fPolygrid); fPolygrid += 4;
+ }
+
+ /*j = 1;// leave node 0 for start node
+ do
+ {
+ memmove(&node[j].x,fPolygrid,2*sizeof(int16));
+ fPolygrid += 2*sizeof(int16);
+ j ++;
+ }
+ while (j < nnodes);//array starts at 0*/
+ for (cnt = 1; cnt < nnodes; cnt++) {
+ node[cnt].x = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ node[cnt].y = READ_LE_UINT16(fPolygrid); fPolygrid += 2;
+ }
+
+ //ResUnlock(walkGridResourceId); // mouse wiggle
+ //ResClose(walkGridResourceId); // mouse wiggle
+ _resMan->resClose(walkGridResourceId);
+
+
+ // floor grid loaded
+ // if its george copy extra bars and nodes
+
+ if (megaId == GEORGE)
+ {
+ // copy any extra bars from extraBars array
+
+ //Zdebug("%d", nExtraBars);
+
+ memmove(&bars[nbars], &_extraBars[0], _numExtraBars*sizeof(BarData));
+ nbars += _numExtraBars;
+
+ // copy any extra nodes from extraNode array
+ j = 0;
+ while (j < _numExtraNodes)//array starts at 0
+ {
+ node[nnodes+j].x = _extraNodes[j].x ;
+ node[nnodes+j].y = _extraNodes[j].y ;
+ j++;
+ }
+
+ nnodes += _numExtraNodes;
+ }
+
+// copy the mega structure into the local variables for use in all subroutines
+
+ startX = megaObject->o_xcoord;
+ startY = megaObject->o_ycoord;
+ startDir = megaObject->o_dir;
+ targetX = x;
+ targetY= y;
+ targetDir = dir;
+
+ scaleA = megaObject->o_scale_a;
+ scaleB = megaObject->o_scale_b;
+
+ //ResOpen(megaObject->o_mega_resource); // mouse wiggle
+ //fMegaWalkData = ResLock(megaObject->o_mega_resource); // mouse wiggle
+ fMegaWalkData = (uint8*)_resMan->openFetchRes(megaObject->o_mega_resource);
+
+ nWalkFrames = fMegaWalkData[0];
+ nTurnFrames = fMegaWalkData[1];
+ fMegaWalkData += 2;
+
+ for (cnt = 0; cnt < NO_DIRECTIONS * (nWalkFrames + 1 + nTurnFrames); cnt++) {
+ _dx[cnt] = (int32)READ_LE_UINT32(fMegaWalkData);
+ fMegaWalkData += 4;
+ }
+ for (cnt = 0; cnt < NO_DIRECTIONS * (nWalkFrames + 1 + nTurnFrames); cnt++) {
+ _dy[cnt] = (int32)READ_LE_UINT32(fMegaWalkData);
+ fMegaWalkData += 4;
+ }
+ /*memmove(&_dx[0],fMegaWalkData,NO_DIRECTIONS*(nWalkFrames+1+nTurnFrames)*sizeof(int32));
+ fMegaWalkData += NO_DIRECTIONS*(nWalkFrames+1+nTurnFrames)*sizeof(int32);
+ memmove(&_dy[0],fMegaWalkData,NO_DIRECTIONS*(nWalkFrames+1+nTurnFrames)*sizeof(int32));
+ fMegaWalkData += NO_DIRECTIONS*(nWalkFrames+1+nTurnFrames)*sizeof(int32);*/
+
+ for (cntu = 0; cntu < NO_DIRECTIONS; cntu++) {
+ modX[cntu] = (int32)READ_LE_UINT32(fMegaWalkData);
+ fMegaWalkData += 4;
+ }
+ for (cntu = 0; cntu < NO_DIRECTIONS; cntu++) {
+ modY[cntu] = (int32)READ_LE_UINT32(fMegaWalkData);
+ fMegaWalkData += 4;
+ }
+ /*memmove(&modX[0],fMegaWalkData,NO_DIRECTIONS*sizeof(int32));
+ fMegaWalkData += NO_DIRECTIONS*sizeof(int32);
+ memmove(&modY[0],fMegaWalkData,NO_DIRECTIONS*sizeof(int32));
+ fMegaWalkData += NO_DIRECTIONS*sizeof(int32);*/
+
+ //ResUnlock(megaObject->o_mega_resource); // mouse wiggle
+ //ResClose(megaObject->o_mega_resource); // mouse wiggle
+ _resMan->resClose(megaObject->o_mega_resource);
+
+ diagonalx = modX[3] ;//36
+ diagonaly = modY[3] ;//8
+
+// mega data ready
+
+// finish setting grid by putting mega node at begining
+// and target node at end and reset current values
+ node[0].x = startX;
+ node[0].y = startY;
+ node[0].level = 1;
+ node[0].prev = 0;
+ node[0].dist = 0;
+ i=1;
+ do
+ {
+ node[i].level = 0;
+ node[i].prev = 0;
+ node[i].dist = 9999;
+ i=i+1;
+ }
+ while (i < nnodes);
+ node[nnodes].x = targetX;
+ node[nnodes].y = targetY;
+ node[nnodes].level = 0;
+ node[nnodes].prev = 0;
+ node[nnodes].dist = 9999;
+
+ return 1;
+}
+
+// ****************************************************************************
+// * THE ROUTE EXTRACTOR
+// ****************************************************************************
+
+void Router::ExtractRoute()
+/****************************************************************************
+ * ExtractRoute gets route from the node data after a full scan, route is
+ * written with just the basic way points and direction options for heading
+ * to the next point.
+ ****************************************************************************/
+{
+ int32 prev;
+ int32 prevx;
+ int32 prevy;
+ int32 last;
+ int32 point;
+ int32 p;
+ int32 dirx;
+ int32 diry;
+ int32 dir;
+ int32 dx;
+ int32 dy;
+
+
+ // extract the route from the node data
+ prev = nnodes;
+ last = prev;
+ point = O_ROUTE_SIZE - 1;
+ route[point].x = node[last].x;
+ route[point].y = node[last].y;
+ do
+ {
+ point = point - 1;
+ prev = node[last].prev;
+ prevx = node[prev].x;
+ prevy = node[prev].y;
+ route[point].x = prevx;
+ route[point].y = prevy;
+ last = prev;
+ }
+ while (prev > 0);
+
+ // now shuffle route down in the buffer
+ routeLength = 0;
+ do
+ {
+ route[routeLength].x = route[point].x;
+ route[routeLength].y = route[point].y;
+ point = point + 1;
+ routeLength = routeLength + 1;
+ }
+ while (point < O_ROUTE_SIZE);
+ routeLength = routeLength - 1;
+
+ // okay the route exists as a series point now put in some directions
+ p = 0;
+ do
+ {
+#ifdef PLOT_PATHS
+ BresenhamLine(route[p+1].x-128,route[p+1].y-128, route[p].x-128,route[p].y-128, (uint8*)screen_ad, true_pixel_size_x, pixel_size_y, ROUTE_END_FLAG);
+#endif
+ dx = route[p+1].x - route[p].x;
+ dy = route[p+1].y - route[p].y;
+ dirx = 1;
+ diry = 1;
+ if (dx < 0)
+ {
+ dx = -dx;
+ dirx = -1;
+ }
+ if (dy < 0)
+ {
+ dy = -dy;
+ diry = -1;
+ }
+
+ if ((diagonaly * dx) > (diagonalx * dy)) // dir = 1,2 or 2,3 or 5,6 or 6,7
+ {
+ dir = 4 - 2 * dirx; // 2 or 6
+ route[p].dirS = dir;
+ dir = dir + diry * dirx; // 1,3,5 or 7
+ route[p].dirD = dir;
+ }
+ else // dir = 7,0 or 0,1 or 3,4 or 4,5
+ {
+ dir = 2 + 2 * diry; // 0 or 4
+ route[p].dirS = dir;
+ dir = 4 - 2 * dirx; // 2 or 6
+ dir = dir + diry * dirx; // 1,3,5 or 7
+ route[p].dirD = dir;
+ }
+ p = p + 1;
+ }
+ while (p < (routeLength));
+ // set the last dir to continue previous route unless specified
+ if (targetDir == NO_DIRECTIONS)
+ {
+ route[p].dirS = route[p-1].dirS;
+ route[p].dirD = route[p-1].dirD;
+ }
+ else
+ {
+ route[p].dirS = targetDir;
+ route[p].dirD = targetDir;
+ }
+ return;
+}
+
+#define screen_ad NULL
+#define pixel_size_y 1
+#define true_pixel_size_x 1
+void Router::RouteLine(int32 x1,int32 y1,int32 x2,int32 y2 ,int32 colour)
+{
+ BresenhamLine(x1-128, y1-128, x2-128, y2-128, (uint8*)screen_ad, true_pixel_size_x, pixel_size_y, colour);
+ return;
+}
+
+void Router::BresenhamLine(int32 x1,int32 y1,int32 x2,int32 y2, uint8 *screen, int32 width, int32 height, int32 colour) {
+
+}
+
+#define DIAGONALX 36
+#define DIAGONALY 8
+int whatTarget(int32 startX, int32 startY, int32 destX, int32 destY) {
+ int tar_dir;
+//setting up
+ int deltaX = destX-startX;
+ int deltaY = destY-startY;
+ int signX = (deltaX > 0);
+ int signY = (deltaY > 0);
+ int slope;
+
+ if ( (ABS(deltaY) * DIAGONALX ) < (ABS(deltaX) * DIAGONALY / 2))
+ slope = 0;// its flat
+ else if ( (ABS(deltaY) * DIAGONALX / 2) > (ABS(deltaX) * DIAGONALY ) )
+ slope = 2;// its vertical
+ else
+ slope = 1;// its diagonal
+
+ if (slope == 0) { //flat
+ if (signX == 1) // going right
+ tar_dir = 2;
+ else
+ tar_dir = 6;
+ } else if (slope == 2) { //vertical
+ if (signY == 1) // going down
+ tar_dir = 4;
+ else
+ tar_dir = 0;
+ } else if (signX == 1) { //right diagonal
+ if (signY == 1) // going down
+ tar_dir = 3;
+ else
+ tar_dir = 1;
+ } else { //left diagonal
+ if (signY == 1) // going down
+ tar_dir = 5;
+ else
+ tar_dir = 7;
+ }
+ return tar_dir;
+}
+
+void Router::resetExtraData(void) {
+ _numExtraBars = _numExtraNodes = 0;
+}
+
+void Router::setPlayerTarget(int32 x, int32 y, int32 dir, int32 stance) {
+ _playerTargetX = x;
+ _playerTargetY = y;
+ _playerTargetDir = dir;
+ _playerTargetStance = stance;
+}
+
+} // End of namespace Sword1
diff --git a/engines/sword1/router.h b/engines/sword1/router.h
new file mode 100644
index 0000000000..e7c835f6d2
--- /dev/null
+++ b/engines/sword1/router.h
@@ -0,0 +1,180 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) Revolution Software Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSROUTER_H
+#define BSROUTER_H
+
+#include "common/scummsys.h"
+#include "sword1/object.h"
+
+namespace Sword1 {
+
+#define EXTRA_GRID_SIZE 20
+#define O_GRID_SIZE 200
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct BarData {
+ int16 x1;
+ int16 y1;
+ int16 x2;
+ int16 y2;
+ int16 xmin;
+ int16 ymin;
+ int16 xmax;
+ int16 ymax;
+ int16 dx; // x2 - x1
+ int16 dy; // y2 - y1
+ int32 co; // co = (y1 *dx)- (x1*dy) from an equation for a line y*dx = x*dy + co
+} GCC_PACK;
+
+struct NodeData {
+ int16 x;
+ int16 y;
+ int16 level;
+ int16 prev;
+ int16 dist;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+struct FloorData {
+ int32 nbars;
+ BarData *bars;
+ int32 nnodes;
+ NodeData *node;
+};
+
+struct RouteData {
+ int32 x;
+ int32 y;
+ int32 dirS;
+ int32 dirD;
+};
+
+struct PathData {
+ int32 x;
+ int32 y;
+ int32 dir;
+ int32 num;
+};
+
+#define ROUTE_END_FLAG 255
+#define NO_DIRECTIONS 8
+#define MAX_FRAMES_PER_CYCLE 16
+#define MAX_FRAMES_PER_CHAR (MAX_FRAMES_PER_CYCLE * NO_DIRECTIONS)
+#define O_ROUTE_SIZE 50
+
+class ObjectMan;
+class ResMan;
+class Screen;
+
+extern int whatTarget(int32 startX, int32 startY, int32 destX, int32 destY);
+
+class Router {
+public:
+ Router(ObjectMan *pObjMan, ResMan *pResMan);
+ int32 routeFinder(int32 id, Object *mega, int32 x, int32 y, int32 dir);
+ void setPlayerTarget(int32 x, int32 y, int32 dir, int32 stance);
+ void resetExtraData(void);
+
+ // these should be private but are read by Screen for debugging:
+ BarData bars[O_GRID_SIZE+EXTRA_GRID_SIZE];
+ NodeData node[O_GRID_SIZE+EXTRA_GRID_SIZE];
+ int32 nbars, nnodes;
+private:
+ // when the player collides with another mega, we'll receive a ReRouteRequest here.
+ // that's why we need to remember the player's target coordinates
+ int32 _playerTargetX, _playerTargetY, _playerTargetDir, _playerTargetStance;
+ // additional route data to block parts of the floor and enable routine around megas:
+ int32 _numExtraBars, _numExtraNodes;
+ BarData _extraBars[EXTRA_GRID_SIZE];
+ NodeData _extraNodes[EXTRA_GRID_SIZE];
+ ObjectMan *_objMan;
+ ResMan *_resMan;
+
+ /*uint8 _nWalkFrames, _nTurnFrames;
+ int32 _dx[NO_DIRECTIONS + MAX_FRAMES_PER_CHAR];
+ int32 _dy[NO_DIRECTIONS + MAX_FRAMES_PER_CHAR];
+ int32 _modX[NO_DIRECTIONS];
+ int32 _modY[NO_DIRECTIONS];
+ int32 _diagonalx, _diagonaly;*/
+
+ int32 startX, startY, startDir;
+ int32 targetX, targetY, targetDir;
+ int32 scaleA, scaleB;
+ int32 megaId;
+
+ /*RouteData _route[O_ROUTE_SIZE];
+ //int32 _routeLength;
+ PathData _smoothPath[O_ROUTE_SIZE];
+ PathData _modularPath[O_ROUTE_SIZE];*/
+ RouteData route[O_ROUTE_SIZE];
+ PathData smoothPath[O_ROUTE_SIZE];
+ PathData modularPath[O_ROUTE_SIZE];
+ int32 routeLength;
+
+ int32 framesPerStep, framesPerChar;
+ uint8 nWalkFrames, nTurnFrames;
+ int32 _dx[NO_DIRECTIONS + MAX_FRAMES_PER_CHAR];
+ int32 _dy[NO_DIRECTIONS + MAX_FRAMES_PER_CHAR];
+ int32 modX[NO_DIRECTIONS];
+ int32 modY[NO_DIRECTIONS];
+ int32 diagonalx, diagonaly;
+ int32 standFrames;
+ int32 turnFramesLeft, turnFramesRight;
+ int32 walkFramesLeft, walkFramesRight; // left/right walking turn
+ int32 slowInFrames, slowOutFrames;
+
+
+ int32 LoadWalkResources(Object *mega, int32 x, int32 y, int32 targetDir);
+ int32 GetRoute(void);
+ int32 CheckTarget(int32 x, int32 y);
+
+ int32 Scan(int32 level);
+ int32 NewCheck(int32 status, int32 x1, int32 x2, int32 y1, int32 y2);
+ int32 Check(int32 x1, int32 y1, int32 x2, int32 y2);
+ int32 HorizCheck(int32 x1, int32 y, int32 x2);
+ int32 VertCheck(int32 x, int32 y1, int32 y2);
+ int32 LineCheck(int32 x1, int32 y1, int32 x2, int32 y2);
+
+ void ExtractRoute();
+
+ int32 SlidyPath();
+ void SlidyWalkAnimator(WalkData *walkAnim);
+
+ int32 SmoothestPath();
+ int32 SmoothCheck(int32 best, int32 p, int32 dirS, int32 dirD);
+
+ int32 SolidPath();
+ int32 SolidWalkAnimator(WalkData *walkAnim);
+ void RouteLine(int32 x1,int32 y1,int32 x2,int32 y2 ,int32 colour);
+ void BresenhamLine(int32 x1,int32 y1,int32 x2,int32 y2, uint8 *screen, int32 width, int32 height, int32 colour);
+};
+
+} // End of namespace Sword1
+
+#endif //BSROUTER_H
diff --git a/engines/sword1/screen.cpp b/engines/sword1/screen.cpp
new file mode 100644
index 0000000000..248f1d3227
--- /dev/null
+++ b/engines/sword1/screen.cpp
@@ -0,0 +1,986 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/util.h"
+#include "sword1/screen.h"
+#include "sword1/logic.h"
+#include "sword1/sworddefs.h"
+#include "sword1/text.h"
+#include "sword1/resman.h"
+#include "sword1/objectman.h"
+#include "sword1/menu.h"
+#include "sword1/sword1.h"
+
+#ifdef BACKEND_8BIT
+#include "sword1/animation.h"
+#endif
+
+namespace Sword1 {
+
+#define SCROLL_FRACTION 16
+#define MAX_SCROLL_DISTANCE 8
+#define FADE_UP 1
+#define FADE_DOWN -1
+
+Screen::Screen(OSystem *system, ResMan *pResMan, ObjectMan *pObjMan) {
+ _system = system;
+ _resMan = pResMan;
+ _objMan = pObjMan;
+ _screenBuf = _screenGrid = NULL;
+ _backLength = _foreLength = _sortLength = 0;
+ _fadingStep = 0;
+ _currentScreen = 0xFFFF;
+}
+
+Screen::~Screen(void) {
+ if (_screenBuf)
+ free(_screenBuf);
+ if (_screenGrid)
+ free(_screenGrid);
+ if (_currentScreen != 0xFFFF)
+ quitScreen();
+}
+
+void Screen::useTextManager(Text *pTextMan) {
+ _textMan = pTextMan;
+}
+
+int32 Screen::inRange(int32 a, int32 b, int32 c) { // return b(!) so that: a <= b <= c
+ return (a > b) ? (a) : ((b < c) ? b : c);
+}
+
+void Screen::setScrolling(int16 offsetX, int16 offsetY) {
+ offsetX = inRange(0, offsetX, Logic::_scriptVars[MAX_SCROLL_OFFSET_X]);
+ offsetY = inRange(0, offsetY, Logic::_scriptVars[MAX_SCROLL_OFFSET_Y]);
+
+ if (Logic::_scriptVars[SCROLL_FLAG] == 2) { // first time on this screen - need absolute scroll immediately!
+ _oldScrollX = Logic::_scriptVars[SCROLL_OFFSET_X] = (uint32)offsetX;
+ _oldScrollY = Logic::_scriptVars[SCROLL_OFFSET_Y] = (uint32)offsetY;
+ Logic::_scriptVars[SCROLL_FLAG] = 1;
+ _fullRefresh = true;
+ } else if (Logic::_scriptVars[SCROLL_FLAG] == 1) {
+ // Because parallax layers may be drawn on the old scroll offset, we
+ // want a full refresh not only when the scroll offset changes, but
+ // also on the frame where they become the same.
+ if (_oldScrollX != Logic::_scriptVars[SCROLL_OFFSET_X] || _oldScrollY != Logic::_scriptVars[SCROLL_OFFSET_Y])
+ _fullRefresh = true;
+ _oldScrollX = Logic::_scriptVars[SCROLL_OFFSET_X];
+ _oldScrollY = Logic::_scriptVars[SCROLL_OFFSET_Y];
+ int dx = offsetX - Logic::_scriptVars[SCROLL_OFFSET_X];
+ int dy = offsetY - Logic::_scriptVars[SCROLL_OFFSET_Y];
+ int scrlDistX = inRange(-MAX_SCROLL_DISTANCE, (((SCROLL_FRACTION - 1) + ABS(dx)) / SCROLL_FRACTION) * ((dx > 0) ? 1 : -1), MAX_SCROLL_DISTANCE);
+ int scrlDistY = inRange(-MAX_SCROLL_DISTANCE, (((SCROLL_FRACTION - 1) + ABS(dy)) / SCROLL_FRACTION) * ((dy > 0) ? 1 : -1), MAX_SCROLL_DISTANCE);
+ if ((scrlDistX != 0) || (scrlDistY != 0))
+ _fullRefresh = true;
+ Logic::_scriptVars[SCROLL_OFFSET_X] = inRange(0, Logic::_scriptVars[SCROLL_OFFSET_X] + scrlDistX, Logic::_scriptVars[MAX_SCROLL_OFFSET_X]);
+ Logic::_scriptVars[SCROLL_OFFSET_Y] = inRange(0, Logic::_scriptVars[SCROLL_OFFSET_Y] + scrlDistY, Logic::_scriptVars[MAX_SCROLL_OFFSET_Y]);
+ } else {
+ // SCROLL_FLAG == 0, this usually means that the screen is smaller than 640x400 and doesn't need scrolling at all
+ // however, it can also mean that the gamescript overwrote the scrolling flag to take care of scrolling directly,
+ // (see bug report #1345130) so we ignore the offset arguments in this case
+ Logic::_scriptVars[SCROLL_OFFSET_X] = inRange(0, Logic::_scriptVars[SCROLL_OFFSET_X], Logic::_scriptVars[MAX_SCROLL_OFFSET_X]);
+ Logic::_scriptVars[SCROLL_OFFSET_Y] = inRange(0, Logic::_scriptVars[SCROLL_OFFSET_Y], Logic::_scriptVars[MAX_SCROLL_OFFSET_Y]);
+ if ((Logic::_scriptVars[SCROLL_OFFSET_X] != _oldScrollX) || (Logic::_scriptVars[SCROLL_OFFSET_Y] != _oldScrollY)) {
+ _fullRefresh = true;
+ _oldScrollX = Logic::_scriptVars[SCROLL_OFFSET_X];
+ _oldScrollY = Logic::_scriptVars[SCROLL_OFFSET_Y];
+ }
+ }
+}
+
+void Screen::fadeDownPalette(void) {
+ if (!_isBlack) { // don't fade down twice
+ _fadingStep = 15;
+ _fadingDirection = FADE_DOWN;
+ }
+}
+
+void Screen::fadeUpPalette(void) {
+ _fadingStep = 1;
+ _fadingDirection = FADE_UP;
+}
+
+void Screen::fnSetPalette(uint8 start, uint16 length, uint32 id, bool fadeUp) {
+ uint8 *palData = (uint8*)_resMan->openFetchRes(id);
+ if (start == 0) // force color 0 to black
+ palData[0] = palData[1] = palData[2] = 0;
+ for (uint32 cnt = 0; cnt < length; cnt++) {
+ _targetPalette[(start + cnt) * 4 + 0] = palData[cnt * 3 + 0] << 2;
+ _targetPalette[(start + cnt) * 4 + 1] = palData[cnt * 3 + 1] << 2;
+ _targetPalette[(start + cnt) * 4 + 2] = palData[cnt * 3 + 2] << 2;
+ }
+ _resMan->resClose(id);
+ _isBlack = false;
+ if (fadeUp) {
+ _fadingStep = 1;
+ _fadingDirection = FADE_UP;
+ memset(_currentPalette, 0, 256 * 4);
+ _system->setPalette(_currentPalette, 0, 256);
+ } else
+ _system->setPalette(_targetPalette + 4 * start, start, length);
+}
+
+void Screen::fullRefresh(void) {
+ _fullRefresh = true;
+ _system->setPalette(_targetPalette, 0, 256);
+}
+
+bool Screen::stillFading(void) {
+ return (_fadingStep != 0);
+}
+
+bool Screen::showScrollFrame(void) {
+ if ((!_fullRefresh) || Logic::_scriptVars[NEW_PALETTE])
+ return false; // don't draw an additional frame if we aren't scrolling or have to change the palette
+ if ((_oldScrollX == Logic::_scriptVars[SCROLL_OFFSET_X]) &&
+ (_oldScrollY == Logic::_scriptVars[SCROLL_OFFSET_Y]))
+ return false; // check again if we *really* are scrolling.
+
+ uint16 avgScrlX = (uint16)(_oldScrollX + Logic::_scriptVars[SCROLL_OFFSET_X]) / 2;
+ uint16 avgScrlY = (uint16)(_oldScrollY + Logic::_scriptVars[SCROLL_OFFSET_Y]) / 2;
+
+ _system->copyRectToScreen(_screenBuf + avgScrlY * _scrnSizeX + avgScrlX, _scrnSizeX, 0, 40, SCREEN_WIDTH, SCREEN_DEPTH);
+ _system->updateScreen();
+ return true;
+}
+
+void Screen::updateScreen(void) {
+ if (Logic::_scriptVars[NEW_PALETTE]) {
+ _fadingStep = 1;
+ _fadingDirection = FADE_UP;
+ fnSetPalette(0, 184, _roomDefTable[_currentScreen].palettes[0], true);
+ fnSetPalette(184, 72, _roomDefTable[_currentScreen].palettes[1], true);
+ Logic::_scriptVars[NEW_PALETTE] = 0;
+ }
+ if (_fadingStep) {
+ fadePalette();
+ _system->setPalette(_currentPalette, 0, 256);
+ }
+
+ uint16 scrlX = (uint16)Logic::_scriptVars[SCROLL_OFFSET_X];
+ uint16 scrlY = (uint16)Logic::_scriptVars[SCROLL_OFFSET_Y];
+ if (_fullRefresh) {
+ _fullRefresh = false;
+ uint16 copyWidth = SCREEN_WIDTH;
+ uint16 copyHeight = SCREEN_DEPTH;
+ if (scrlX + copyWidth > _scrnSizeX)
+ copyWidth = _scrnSizeX - scrlX;
+ if (scrlY + copyHeight > _scrnSizeY)
+ copyHeight = _scrnSizeY - scrlY;
+ _system->copyRectToScreen(_screenBuf + scrlY * _scrnSizeX + scrlX, _scrnSizeX, 0, 40, copyWidth, copyHeight);
+ } else {
+ // partial screen update only. The screen coordinates probably won't fit to the
+ // grid holding the informations on which blocks have to be updated.
+ // as the grid will be X pixel higher and Y pixel more to the left, this can be cured
+ // by first checking the top border, then the left column and then the remaining (aligned) part.
+ uint8 *gridPos = _screenGrid + (scrlX / SCRNGRID_X) + (scrlY / SCRNGRID_Y) * _gridSizeX;
+ uint8 *scrnBuf = _screenBuf + scrlY * _scrnSizeX + scrlX;
+ uint8 diffX = (uint8)(scrlX % SCRNGRID_X);
+ uint8 diffY = (uint8)(scrlY % SCRNGRID_Y);
+ uint16 gridW = SCREEN_WIDTH / SCRNGRID_X;
+ uint16 gridH = SCREEN_DEPTH / SCRNGRID_Y;
+ if (diffY) {
+ diffY = SCRNGRID_Y - diffY;
+ uint16 cpWidth = 0;
+ for (uint16 cntx = 0; cntx < gridW; cntx++)
+ if (gridPos[cntx]) {
+ gridPos[cntx] >>= 1;
+ cpWidth++;
+ } else if (cpWidth) {
+ int16 xPos = (cntx - cpWidth) * SCRNGRID_X - diffX;
+ if (xPos < 0)
+ xPos = 0;
+ _system->copyRectToScreen(scrnBuf + xPos, _scrnSizeX, xPos, 40, cpWidth * SCRNGRID_X, diffY);
+ cpWidth = 0;
+ }
+ if (cpWidth) {
+ int16 xPos = (gridW - cpWidth) * SCRNGRID_X - diffX;
+ if (xPos < 0)
+ xPos = 0;
+ _system->copyRectToScreen(scrnBuf + xPos, _scrnSizeX, xPos, 40, SCREEN_WIDTH - xPos, diffY);
+ }
+ scrlY += diffY;
+ }
+ // okay, y scrolling is compensated. check x now.
+ gridPos = _screenGrid + (scrlX / SCRNGRID_X) + (scrlY / SCRNGRID_Y) * _gridSizeX;
+ scrnBuf = _screenBuf + scrlY * _scrnSizeX + scrlX;
+ if (diffX) {
+ diffX = SCRNGRID_X - diffX;
+ uint16 cpHeight = 0;
+ for (uint16 cnty = 0; cnty < gridH; cnty++) {
+ if (*gridPos) {
+ *gridPos >>= 1;
+ cpHeight++;
+ } else if (cpHeight) {
+ uint16 yPos = (cnty - cpHeight) * SCRNGRID_Y;
+ _system->copyRectToScreen(scrnBuf + yPos * _scrnSizeX, _scrnSizeX, 0, yPos + diffY + 40, diffX, cpHeight * SCRNGRID_Y);
+ cpHeight = 0;
+ }
+ gridPos += _gridSizeX;
+ }
+ if (cpHeight) {
+ uint16 yPos = (gridH - cpHeight) * SCRNGRID_Y;
+ _system->copyRectToScreen(scrnBuf + yPos * _scrnSizeX, _scrnSizeX, 0, yPos + diffY + 40, diffX, SCREEN_DEPTH - (yPos + diffY));
+ }
+ scrlX += diffX;
+ }
+ // x scroll is compensated, too. check the rest of the screen, now.
+ scrnBuf = _screenBuf + scrlY * _scrnSizeX + scrlX;
+ gridPos = _screenGrid + (scrlX / SCRNGRID_X) + (scrlY / SCRNGRID_Y) * _gridSizeX;
+ for (uint16 cnty = 0; cnty < gridH; cnty++) {
+ uint16 cpWidth = 0;
+ uint16 cpHeight = SCRNGRID_Y;
+ if (cnty == gridH - 1)
+ cpHeight = SCRNGRID_Y - diffY;
+ for (uint16 cntx = 0; cntx < gridW; cntx++)
+ if (gridPos[cntx]) {
+ gridPos[cntx] >>= 1;
+ cpWidth++;
+ } else if (cpWidth) {
+ _system->copyRectToScreen(scrnBuf + (cntx - cpWidth) * SCRNGRID_X, _scrnSizeX, (cntx - cpWidth) * SCRNGRID_X + diffX, cnty * SCRNGRID_Y + diffY + 40, cpWidth * SCRNGRID_X, cpHeight);
+ cpWidth = 0;
+ }
+ if (cpWidth) {
+ uint16 xPos = (gridW - cpWidth) * SCRNGRID_X;
+ _system->copyRectToScreen(scrnBuf + xPos, _scrnSizeX, xPos + diffX, cnty * SCRNGRID_Y + diffY + 40, SCREEN_WIDTH - (xPos + diffX), cpHeight);
+ }
+ gridPos += _gridSizeX;
+ scrnBuf += _scrnSizeX * SCRNGRID_Y;
+ }
+ }
+ _system->updateScreen();
+}
+
+void Screen::newScreen(uint32 screen) {
+ uint8 cnt;
+ // set sizes and scrolling, initialize/load screengrid, force screen refresh
+ _currentScreen = screen;
+ _scrnSizeX = _roomDefTable[screen].sizeX;
+ _scrnSizeY = _roomDefTable[screen].sizeY;
+ _gridSizeX = _scrnSizeX / SCRNGRID_X;
+ _gridSizeY = _scrnSizeY / SCRNGRID_Y;
+ if ((_scrnSizeX % SCRNGRID_X) || (_scrnSizeY % SCRNGRID_Y))
+ error("Illegal screensize: %d: %d/%d", screen, _scrnSizeX, _scrnSizeY);
+ if ((_scrnSizeX > SCREEN_WIDTH) || (_scrnSizeY > SCREEN_DEPTH)) {
+ Logic::_scriptVars[SCROLL_FLAG] = 2;
+ Logic::_scriptVars[MAX_SCROLL_OFFSET_X] = _scrnSizeX - SCREEN_WIDTH;
+ Logic::_scriptVars[MAX_SCROLL_OFFSET_Y] = _scrnSizeY - SCREEN_DEPTH;
+ } else {
+ Logic::_scriptVars[SCROLL_FLAG] = 0;
+ Logic::_scriptVars[MAX_SCROLL_OFFSET_X] = 0;
+ Logic::_scriptVars[MAX_SCROLL_OFFSET_Y] = 0;
+ }
+ Logic::_scriptVars[SCROLL_OFFSET_X] = 0;
+ Logic::_scriptVars[SCROLL_OFFSET_Y] = 0;
+
+ if (_screenBuf)
+ free(_screenBuf);
+ if (_screenGrid)
+ free(_screenGrid);
+ _screenBuf = (uint8*)malloc(_scrnSizeX * _scrnSizeY);
+ _screenGrid = (uint8*)malloc(_gridSizeX * _gridSizeY);
+ memset(_screenGrid, 0, _gridSizeX * _gridSizeY);
+ for (cnt = 0; cnt < _roomDefTable[_currentScreen].totalLayers; cnt++) {
+ // open and lock all resources, will be closed in quitScreen()
+ _layerBlocks[cnt] = (uint8*)_resMan->openFetchRes(_roomDefTable[_currentScreen].layers[cnt]);
+ if (cnt > 0)
+ _layerBlocks[cnt] += sizeof(Header);
+ }
+ for (cnt = 0; cnt < _roomDefTable[_currentScreen].totalLayers - 1; cnt++) {
+ // there's no grid for the background layer, so it's totalLayers - 1
+ _layerGrid[cnt] = (uint16*)_resMan->openFetchRes(_roomDefTable[_currentScreen].grids[cnt]);
+ _layerGrid[cnt] += 14;
+ }
+ _parallax[0] = _parallax[1] = NULL;
+ if (_roomDefTable[_currentScreen].parallax[0])
+ _parallax[0] = (uint8*)_resMan->openFetchRes(_roomDefTable[_currentScreen].parallax[0]);
+ if (_roomDefTable[_currentScreen].parallax[1])
+ _parallax[1] = (uint8*)_resMan->openFetchRes(_roomDefTable[_currentScreen].parallax[1]);
+
+ fnSetPalette(0, 184, _roomDefTable[_currentScreen].palettes[0], SwordEngine::_systemVars.wantFade);
+ fnSetPalette(184, 72, _roomDefTable[_currentScreen].palettes[1], SwordEngine::_systemVars.wantFade);
+ _fullRefresh = true;
+}
+
+void Screen::quitScreen(void) {
+ uint8 cnt;
+ for (cnt = 0; cnt < _roomDefTable[_currentScreen].totalLayers; cnt++)
+ _resMan->resClose(_roomDefTable[_currentScreen].layers[cnt]);
+ for (cnt = 0; cnt < _roomDefTable[_currentScreen].totalLayers - 1; cnt++)
+ _resMan->resClose(_roomDefTable[_currentScreen].grids[cnt]);
+ if (_roomDefTable[_currentScreen].parallax[0])
+ _resMan->resClose(_roomDefTable[_currentScreen].parallax[0]);
+ if (_roomDefTable[_currentScreen].parallax[1])
+ _resMan->resClose(_roomDefTable[_currentScreen].parallax[1]);
+ _currentScreen = 0xFFFF;
+}
+
+void Screen::draw(void) {
+ uint8 cnt;
+ if (_currentScreen == 54) {
+ // rm54 has a BACKGROUND parallax layer in parallax[0]
+ if (_parallax[0])
+ renderParallax(_parallax[0]);
+ uint8 *src = _layerBlocks[0];
+ uint8 *dest = _screenBuf;
+
+ for (uint16 cnty = 0; cnty < _scrnSizeY; cnty++)
+ for (uint16 cntx = 0; cntx < _scrnSizeX; cntx++) {
+ if (*src)
+ *dest = *src;
+ dest++;
+ src++;
+ }
+
+ } else
+ memcpy(_screenBuf, _layerBlocks[0], _scrnSizeX * _scrnSizeY);
+
+ for (cnt = 0; cnt < _backLength; cnt++)
+ processImage(_backList[cnt]);
+
+ for (cnt = 0; cnt < _sortLength - 1; cnt++)
+ for (uint8 sCnt = 0; sCnt < _sortLength - 1; sCnt++)
+ if (_sortList[sCnt].y > _sortList[sCnt + 1].y) {
+ SWAP(_sortList[sCnt], _sortList[sCnt + 1]);
+ }
+ for (cnt = 0; cnt < _sortLength; cnt++)
+ processImage(_sortList[cnt].id);
+
+ if ((_currentScreen != 54) && _parallax[0])
+ renderParallax(_parallax[0]); // screens other than 54 have FOREGROUND parallax layer in parallax[0]
+ if (_parallax[1])
+ renderParallax(_parallax[1]);
+
+ for (cnt = 0; cnt < _foreLength; cnt++)
+ processImage(_foreList[cnt]);
+
+ _backLength = _sortLength = _foreLength = 0;
+}
+
+void Screen::processImage(uint32 id) {
+ Object *compact;
+ FrameHeader *frameHead;
+ int scale;
+
+ compact = _objMan->fetchObject(id);
+ if (compact->o_type == TYPE_TEXT)
+ frameHead = _textMan->giveSpriteData((uint8)compact->o_target);
+ else
+ frameHead = _resMan->fetchFrame(_resMan->openFetchRes(compact->o_resource), compact->o_frame);
+
+ uint8 *sprData = ((uint8*)frameHead) + sizeof(FrameHeader);
+
+ uint16 spriteX = compact->o_anim_x;
+ uint16 spriteY = compact->o_anim_y;
+ if (compact->o_status & STAT_SHRINK) {
+ scale = (compact->o_scale_a * compact->o_ycoord + compact->o_scale_b) / 256;
+ spriteX += ((int16)READ_LE_UINT16(&frameHead->offsetX) * scale) / 256;
+ spriteY += ((int16)READ_LE_UINT16(&frameHead->offsetY) * scale) / 256;
+ } else {
+ scale = 256;
+ spriteX += (int16)READ_LE_UINT16(&frameHead->offsetX);
+ spriteY += (int16)READ_LE_UINT16(&frameHead->offsetY);
+ }
+
+ uint8 *tonyBuf = NULL;
+ if (frameHead->runTimeComp[3] == '7') { // RLE7 encoded?
+ decompressRLE7(sprData, READ_LE_UINT32(&frameHead->compSize), _rleBuffer);
+ sprData = _rleBuffer;
+ } else if (frameHead->runTimeComp[3] == '0') { // RLE0 encoded?
+ decompressRLE0(sprData, READ_LE_UINT32(&frameHead->compSize), _rleBuffer);
+ sprData = _rleBuffer;
+ } else if (frameHead->runTimeComp[1] == 'I') { // new type
+ tonyBuf = (uint8*)malloc(READ_LE_UINT16(&frameHead->width) * READ_LE_UINT16(&frameHead->height));
+ decompressTony(sprData, READ_LE_UINT32(&frameHead->compSize), tonyBuf);
+ sprData = tonyBuf;
+ }
+
+ uint16 sprSizeX, sprSizeY;
+ if (compact->o_status & STAT_SHRINK) {
+ sprSizeX = (scale * READ_LE_UINT16(&frameHead->width)) / 256;
+ sprSizeY = (scale * READ_LE_UINT16(&frameHead->height)) / 256;
+ fastShrink(sprData, READ_LE_UINT16(&frameHead->width), READ_LE_UINT16(&frameHead->height), scale, _shrinkBuffer);
+ sprData = _shrinkBuffer;
+ } else {
+ sprSizeX = READ_LE_UINT16(&frameHead->width);
+ sprSizeY = READ_LE_UINT16(&frameHead->height);
+ }
+ if (!(compact->o_status & STAT_OVERRIDE)) {
+ //mouse size linked to exact size & coordinates of sprite box - shrink friendly
+ if (READ_LE_UINT16(&frameHead->offsetX) || READ_LE_UINT16(&frameHead->offsetY)) {
+ //for megas the mouse area is reduced to account for sprite not
+ //filling the box size is reduced to 1/2 width, 4/5 height
+ compact->o_mouse_x1 = spriteX + sprSizeX / 4;
+ compact->o_mouse_x2 = spriteX + (3 * sprSizeX) / 4;
+ compact->o_mouse_y1 = spriteY + sprSizeY / 10;
+ compact->o_mouse_y2 = spriteY + (9 * sprSizeY) / 10;
+ } else {
+ compact->o_mouse_x1 = spriteX;
+ compact->o_mouse_x2 = spriteX + sprSizeX;
+ compact->o_mouse_y1 = spriteY;
+ compact->o_mouse_y2 = spriteY + sprSizeY;
+ }
+ }
+ uint16 sprPitch = sprSizeX;
+ uint16 incr;
+ spriteClipAndSet(&spriteX, &spriteY, &sprSizeX, &sprSizeY, &incr);
+ if ((sprSizeX > 0) && (sprSizeY > 0)) {
+ drawSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch);
+ if (!(compact->o_status&STAT_FORE))
+ verticalMask(spriteX, spriteY, sprSizeX, sprSizeY);
+ }
+ if (compact->o_type != TYPE_TEXT)
+ _resMan->resClose(compact->o_resource);
+ if (tonyBuf)
+ free(tonyBuf);
+}
+
+void Screen::verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight) {
+ if (_roomDefTable[_currentScreen].totalLayers <= 1)
+ return;
+
+ bWidth = (bWidth + (x & (SCRNGRID_X - 1)) + (SCRNGRID_X - 1)) / SCRNGRID_X;
+ bHeight = (bHeight + (y & (SCRNGRID_Y - 1)) + (SCRNGRID_Y - 1)) / SCRNGRID_Y;
+
+ x /= SCRNGRID_X;
+ y /= SCRNGRID_Y;
+ if (x + bWidth > _gridSizeX)
+ bWidth = _gridSizeX - x;
+ if (y + bHeight > _gridSizeY)
+ bHeight = _gridSizeY - y;
+
+ uint16 gridY = y + SCREEN_TOP_EDGE / SCRNGRID_Y; // imaginary screen on top
+ gridY += bHeight - 1; // we start from the bottom edge
+ uint16 gridX = x + SCREEN_LEFT_EDGE / SCRNGRID_X; // imaginary screen left
+ uint16 lGridSizeX = _gridSizeX + 2 * (SCREEN_LEFT_EDGE / SCRNGRID_X); // width of the grid for the imaginary screen
+
+ for (uint16 blkx = 0; blkx < bWidth; blkx++) {
+ // A sprite can be masked by several layers at the same time,
+ // so we have to check them all. See bug #917427.
+ for (int16 level = _roomDefTable[_currentScreen].totalLayers - 2; level >= 0; level--) {
+ if (_layerGrid[level][gridX + blkx + gridY * lGridSizeX]) {
+ uint16 *grid = _layerGrid[level] + gridX + blkx + gridY * lGridSizeX;
+ for (int16 blky = bHeight - 1; blky >= 0; blky--) {
+ if (*grid) {
+ uint8 *blkData = _layerBlocks[level + 1] + (READ_LE_UINT16(grid) - 1) * 128;
+ blitBlockClear(x + blkx, y + blky, blkData);
+ } else
+ break;
+ grid -= lGridSizeX;
+ }
+ }
+ }
+ }
+}
+
+void Screen::blitBlockClear(uint16 x, uint16 y, uint8 *data) {
+ uint8 *dest = _screenBuf + (y * SCRNGRID_Y) * _scrnSizeX + (x * SCRNGRID_X);
+ for (uint8 cnty = 0; cnty < SCRNGRID_Y; cnty++) {
+ for (uint8 cntx = 0; cntx < SCRNGRID_X; cntx++)
+ if (data[cntx])
+ dest[cntx] = data[cntx];
+ data += SCRNGRID_X;
+ dest += _scrnSizeX;
+ }
+}
+
+void Screen::renderParallax(uint8 *data) {
+ ParallaxHeader *header = (ParallaxHeader*)data;
+ uint32 *lineIndexes = (uint32*)(data + sizeof(ParallaxHeader));
+ assert((FROM_LE_16(header->sizeX) >= SCREEN_WIDTH) && (FROM_LE_16(header->sizeY) >= SCREEN_DEPTH));
+
+ uint16 paraScrlX, paraScrlY;
+ uint16 scrnScrlX, scrnScrlY;
+ uint16 scrnWidth, scrnHeight;
+
+ // we have to render more than the visible screen part for displaying scroll frames
+ scrnScrlX = MIN((uint32)_oldScrollX, Logic::_scriptVars[SCROLL_OFFSET_X]);
+ scrnWidth = SCREEN_WIDTH + ABS((int32)_oldScrollX - (int32)Logic::_scriptVars[SCROLL_OFFSET_X]);
+ scrnScrlY = MIN((uint32)_oldScrollY, Logic::_scriptVars[SCROLL_OFFSET_Y]);
+ scrnHeight = SCREEN_DEPTH + ABS((int32)_oldScrollY - (int32)Logic::_scriptVars[SCROLL_OFFSET_Y]);
+
+ if (_scrnSizeX != SCREEN_WIDTH) {
+ double scrlfx = (FROM_LE_16(header->sizeX) - SCREEN_WIDTH) / ((double)(_scrnSizeX - SCREEN_WIDTH));
+ paraScrlX = (uint16)(scrnScrlX * scrlfx);
+ } else
+ paraScrlX = 0;
+
+ if (_scrnSizeY != SCREEN_DEPTH) {
+ double scrlfy = (FROM_LE_16(header->sizeY) - SCREEN_DEPTH) / ((double)(_scrnSizeY - SCREEN_DEPTH));
+ paraScrlY = (uint16)(scrnScrlY * scrlfy);
+ } else
+ paraScrlY = 0;
+
+ for (uint16 cnty = 0; cnty < scrnHeight; cnty++) {
+ uint8 *src = data + READ_LE_UINT32(lineIndexes + cnty + paraScrlY);
+ uint8 *dest = _screenBuf + scrnScrlX + (cnty + scrnScrlY) * _scrnSizeX;
+ uint16 remain = paraScrlX;
+ uint16 xPos = 0;
+ while (remain) { // skip past the first part of the parallax to get to the right scrolling position
+ uint8 doSkip = *src++;
+ if (doSkip <= remain)
+ remain -= doSkip;
+ else {
+ xPos = doSkip - remain;
+ dest += xPos;
+ remain = 0;
+ }
+ uint8 doCopy = *src++;
+ if (doCopy <= remain) {
+ remain -= doCopy;
+ src += doCopy;
+ } else {
+ uint16 remCopy = doCopy - remain;
+ memcpy(dest, src + remain, remCopy);
+ dest += remCopy;
+ src += doCopy;
+ xPos = remCopy;
+ remain = 0;
+ }
+ }
+ while (xPos < scrnWidth) {
+ if (uint8 skip = *src++) {
+ dest += skip;
+ xPos += skip;
+ }
+ if (xPos < scrnWidth) {
+ if (uint8 doCopy = *src++) {
+ if (xPos + doCopy > scrnWidth)
+ doCopy = scrnWidth - xPos;
+ memcpy(dest, src, doCopy);
+ dest += doCopy;
+ xPos += doCopy;
+ src += doCopy;
+ }
+ }
+ }
+ }
+}
+
+void Screen::drawSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) {
+ uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX;
+
+ for (uint16 cnty = 0; cnty < sprHeight; cnty++) {
+ for (uint16 cntx = 0; cntx < sprWidth; cntx++)
+ if (sprData[cntx])
+ dest[cntx] = sprData[cntx];
+ sprData += sprPitch;
+ dest += _scrnSizeX;
+ }
+}
+
+// nearest neighbor filter:
+void Screen::fastShrink(uint8 *src, uint32 width, uint32 height, uint32 scale, uint8 *dest) {
+ uint32 resHeight = (height * scale) >> 8;
+ uint32 resWidth = (width * scale) >> 8;
+ uint32 step = 0x10000 / scale;
+ uint8 columnTab[160];
+ uint32 res = step >> 1;
+ for (uint16 cnt = 0; cnt < resWidth; cnt++) {
+ columnTab[cnt] = (uint8)(res >> 8);
+ res += step;
+ }
+
+ uint32 newRow = step >> 1;
+ uint32 oldRow = 0;
+
+ uint8 *destPos = dest;
+ uint16 lnCnt;
+ for (lnCnt = 0; lnCnt < resHeight; lnCnt++) {
+ while (oldRow < (newRow >> 8)) {
+ oldRow++;
+ src += width;
+ }
+ for (uint16 colCnt = 0; colCnt < resWidth; colCnt++) {
+ *destPos++ = src[columnTab[colCnt]];
+ }
+ newRow += step;
+ }
+ // scaled, now stipple shadows if there are any
+ for (lnCnt = 0; lnCnt < resHeight; lnCnt++) {
+ uint16 xCnt = lnCnt & 1;
+ destPos = dest + lnCnt * resWidth + (lnCnt & 1);
+ while (xCnt < resWidth) {
+ if (*destPos == 200)
+ *destPos = 0;
+ destPos += 2;
+ xCnt += 2;
+ }
+ }
+}
+
+void Screen::addToGraphicList(uint8 listId, uint32 objId) {
+ if (listId == 0) {
+ assert(_foreLength < MAX_FORE);
+ _foreList[_foreLength++] = objId;
+ }
+ if (listId == 1) {
+ assert(_sortLength < MAX_SORT);
+ Object *cpt = _objMan->fetchObject(objId);
+ _sortList[_sortLength].id = objId;
+ _sortList[_sortLength].y = cpt->o_anim_y; // gives feet coords if boxed mega, otherwise top of sprite box
+ if (!(cpt->o_status & STAT_SHRINK)) { // not a boxed mega using shrinking
+ Header *frameRaw = (Header*)_resMan->openFetchRes(cpt->o_resource);
+ FrameHeader *frameHead = _resMan->fetchFrame(frameRaw, cpt->o_frame);
+ _sortList[_sortLength].y += READ_LE_UINT16(&frameHead->height) - 1; // now pointing to base of sprite
+ _resMan->resClose(cpt->o_resource);
+ }
+ _sortLength++;
+ }
+ if (listId == 2) {
+ assert(_backLength < MAX_BACK);
+ _backList[_backLength++] = objId;
+ }
+}
+
+void Screen::decompressTony(uint8 *src, uint32 compSize, uint8 *dest) {
+ uint8 *endOfData = src + compSize;
+ while (src < endOfData) {
+ uint8 numFlat = *src++;
+ if (numFlat) {
+ memset(dest, *src, numFlat);
+ src++;
+ dest += numFlat;
+ }
+ if (src < endOfData) {
+ uint8 numNoFlat = *src++;
+ memcpy(dest, src, numNoFlat);
+ src += numNoFlat;
+ dest += numNoFlat;
+ }
+ }
+}
+
+void Screen::decompressRLE7(uint8 *src, uint32 compSize, uint8 *dest) {
+ uint8 *compBufEnd = src + compSize;
+ while (src < compBufEnd) {
+ uint8 code = *src++;
+ if ((code > 127) || (code == 0))
+ *dest++ = code;
+ else {
+ code++;
+ memset(dest, *src++, code);
+ dest += code;
+ }
+ }
+}
+
+void Screen::decompressRLE0(uint8 *src, uint32 compSize, uint8 *dest) {
+ uint8 *srcBufEnd = src + compSize;
+ while (src < srcBufEnd) {
+ uint8 color = *src++;
+ if (color) {
+ *dest++ = color;
+ } else {
+ uint8 skip = *src++;
+ memset(dest, 0, skip);
+ dest += skip;
+ }
+ }
+}
+
+void Screen::fadePalette(void) {
+ if (_fadingStep == 16)
+ memcpy(_currentPalette, _targetPalette, 256 * 4);
+ else if ((_fadingStep == 1) && (_fadingDirection == FADE_DOWN)) {
+ memset(_currentPalette, 0, 4 * 256);
+ } else
+ for (uint16 cnt = 0; cnt < 256 * 4; cnt++)
+ _currentPalette[cnt] = (_targetPalette[cnt] * _fadingStep) >> 4;
+
+ _fadingStep += _fadingDirection;
+ if (_fadingStep == 17) {
+ _fadingStep = 0;
+ _isBlack = false;
+ } else if (_fadingStep == 0)
+ _isBlack = true;
+}
+
+void Screen::fnSetParallax(uint32 screen, uint32 resId) {
+ _roomDefTable[screen].parallax[0] = resId;
+}
+
+void Screen::spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *pSprWidth, uint16 *pSprHeight, uint16 *incr) {
+ int16 sprX = *pSprX - SCREEN_LEFT_EDGE;
+ int16 sprY = *pSprY - SCREEN_TOP_EDGE;
+ int16 sprW = *pSprWidth;
+ int16 sprH = *pSprHeight;
+
+ if (sprY < 0) {
+ *incr = (uint16)((-sprY) * sprW);
+ sprH += sprY;
+ sprY = 0;
+ } else
+ *incr = 0;
+ if (sprX < 0) {
+ *incr -= sprX;
+ sprW += sprX;
+ sprX = 0;
+ }
+
+ if (sprY + sprH > _scrnSizeY)
+ sprH = _scrnSizeY - sprY;
+ if (sprX + sprW > _scrnSizeX)
+ sprW = _scrnSizeX - sprX;
+
+ if (sprH < 0)
+ *pSprHeight = 0;
+ else
+ *pSprHeight = (uint16)sprH;
+ if (sprW < 0)
+ *pSprWidth = 0;
+ else
+ *pSprWidth = (uint16)sprW;
+ *pSprX = (uint16)sprX;
+ *pSprY = (uint16)sprY;
+
+ if (*pSprWidth && *pSprHeight) {
+ // sprite will be drawn, so mark it in the grid buffer
+ uint16 gridH = (*pSprHeight + (sprY & (SCRNGRID_Y - 1)) + (SCRNGRID_Y - 1)) / SCRNGRID_Y;
+ uint16 gridW = (*pSprWidth + (sprX & (SCRNGRID_X - 1)) + (SCRNGRID_X - 1)) / SCRNGRID_X;
+ uint16 gridX = sprX / SCRNGRID_X;
+ uint16 gridY = sprY / SCRNGRID_Y;
+ uint8 *gridBuf = _screenGrid + gridX + gridY * _gridSizeX;
+ if (gridX + gridW > _gridSizeX)
+ gridW = _gridSizeX - gridX;
+ if (gridY + gridH > _gridSizeY)
+ gridH = _gridSizeY - gridY;
+
+ for (uint16 cnty = 0; cnty < gridH; cnty++) {
+ for (uint16 cntx = 0; cntx < gridW; cntx++)
+ gridBuf[cntx] = 2;
+ gridBuf += _gridSizeX;
+ }
+ }
+}
+
+void Screen::fnFlash(uint8 color) {
+ warning("stub: Screen::fnFlash(%d)", color);
+}
+
+// ------------------- Menu screen interface ---------------------------
+
+void Screen::showFrame(uint16 x, uint16 y, uint32 resId, uint32 frameNo, const byte *fadeMask, int8 fadeStatus) {
+ uint8 frame[40 * 40];
+ int i, j;
+
+ memset(frame, 199, sizeof(frame)); // Dark gray background
+
+ if (resId != 0xffffffff) {
+ FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(resId), frameNo);
+ uint8 *frameData = ((uint8*)frameHead) + sizeof(FrameHeader);
+
+ for (i = 0; i < FROM_LE_16(frameHead->height); i++) {
+ for (j = 0; j < FROM_LE_16(frameHead->height); j++) {
+ frame[(i + 4) * 40 + j + 2] = frameData[i * FROM_LE_16(frameHead->width) + j];
+ }
+ }
+
+ _resMan->resClose(resId);
+ }
+
+ if (fadeMask) {
+ for (i = 0; i < 40; i++) {
+ for (j = 0; j < 40; j++) {
+ if (fadeMask[((i % 8) * 8) + (j % 8)] >= fadeStatus)
+ frame[i * 40 + j] = 0;
+ }
+ }
+ }
+
+ _system->copyRectToScreen(frame, 40, x, y, 40, 40);
+}
+
+// ------------------- router debugging code --------------------------------
+
+void Screen::vline(uint16 x, uint16 y1, uint16 y2) {
+ for (uint16 cnty = y1; cnty <= y2; cnty++)
+ _screenBuf[x + _scrnSizeX * cnty] = 0;
+}
+
+void Screen::hline(uint16 x1, uint16 x2, uint16 y) {
+ for (uint16 cntx = x1; cntx <= x2; cntx++)
+ _screenBuf[y * _scrnSizeX + cntx] = 0;
+}
+
+void Screen::bsubline_1(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
+ int x, y, ddx, ddy, e;
+ ddx = ABS(x2 - x1);
+ ddy = ABS(y2 - y1) << 1;
+ e = ddx - ddy;
+ ddx <<= 1;
+
+ if (x1 > x2) {
+ uint16 tmp;
+ tmp = x1; x1 = x2; x2 = tmp;
+ tmp = y1; y1 = y2; y2 = tmp;
+ }
+
+ for (x = x1, y = y1; x <= x2; x++) {
+ _screenBuf[y * _scrnSizeX + x] = 0;
+ if (e < 0) {
+ y++;
+ e += ddx - ddy;
+ } else {
+ e -= ddy;
+ }
+ }
+}
+
+void Screen::bsubline_2(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
+ int x, y, ddx, ddy, e;
+ ddx = ABS(x2 - x1) << 1;
+ ddy = ABS(y2 - y1);
+ e = ddy - ddx;
+ ddy <<= 1;
+
+ if (y1 > y2) {
+ uint16 tmp;
+ tmp = x1; x1 = x2; x2 = tmp;
+ tmp = y1; y1 = y2; y2 = tmp;
+ }
+
+ for (y = y1, x = x1; y <= y2; y++) {
+ _screenBuf[y * _scrnSizeX + x] = 0;
+ if (e < 0) {
+ x++;
+ e += ddy - ddx;
+ } else {
+ e -= ddx;
+ }
+ }
+}
+
+void Screen::bsubline_3(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
+ int x, y, ddx, ddy, e;
+ ddx = ABS(x1 - x2) << 1;
+ ddy = ABS(y2 - y1);
+ e = ddy - ddx;
+ ddy <<= 1;
+
+ if (y1 > y2) {
+ uint16 tmp;
+ tmp = x1; x1 = x2; x2 = tmp;
+ tmp = y1; y1 = y2; y2 = tmp;
+ }
+
+ for (y = y1, x = x1; y <= y2; y++) {
+ _screenBuf[y * _scrnSizeX + x] = 0;
+ if (e < 0) {
+ x--;
+ e += ddy - ddx;
+ } else {
+ e -= ddx;
+ }
+ }
+}
+
+void Screen::bsubline_4(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
+ int x, y, ddx, ddy, e;
+ ddy = ABS(y2 - y1) << 1;
+ ddx = ABS(x1 - x2);
+ e = ddx - ddy;
+ ddx <<= 1;
+
+ if (x1 > x2) {
+ uint16 tmp;
+ tmp = x1; x1 = x2; x2 = tmp;
+ tmp = y1; y1 = y2; y2 = tmp;
+ }
+
+ for (x = x1, y = y1; x <= x2; x++) {
+ _screenBuf[y * _scrnSizeX + x] = 0;
+ if (e < 0) {
+ y--;
+ e += ddx - ddy;
+ } else {
+ e -= ddy;
+ }
+ }
+}
+
+void Screen::drawLine(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
+ if ((x1 == x2) && (y1 == y2)) {
+ _screenBuf[x1 + y1 * _scrnSizeX] = 0;
+ }
+ if (x1 == x2) {
+ vline(x1, MIN(y1, y2), MAX(y1, y2));
+ return;
+ }
+
+ if (y1 == y2) {
+ hline(MIN(x1, x2), MAX(x1, x2), y1);
+ return;
+ }
+
+ float k = float(y2 - y1) / float(x2 - x1);
+
+ if ((k >= 0) && (k <= 1)) {
+ bsubline_1(x1, y1, x2, y2);
+ } else if (k > 1) {
+ bsubline_2(x1, y1, x2, y2);
+ } else if ((k < 0) && (k >= -1)) {
+ bsubline_4(x1, y1, x2, y2);
+ } else {
+ bsubline_3(x1, y1, x2, y2);
+ }
+}
+
+#ifdef BACKEND_8BIT
+void Screen::plotYUV(byte *lut, int width, int height, byte *const *dat) {
+
+ byte * buf = (uint8*)malloc(width * height);
+
+ int x, y;
+
+ int ypos = 0;
+ int cpos = 0;
+ int linepos = 0;
+
+ for (y = 0; y < height; y += 2) {
+ for (x = 0; x < width; x += 2) {
+ int i = ((((dat[2][cpos] + ROUNDADD) >> SHIFT) * (BITDEPTH+1)) + ((dat[1][cpos] + ROUNDADD)>>SHIFT)) * (BITDEPTH+1);
+ cpos++;
+
+ buf[linepos ] = lut[i + ((dat[0][ ypos ] + ROUNDADD) >> SHIFT)];
+ buf[width + linepos++] = lut[i + ((dat[0][width + ypos++] + ROUNDADD) >> SHIFT)];
+ buf[linepos ] = lut[i + ((dat[0][ ypos ] + ROUNDADD) >> SHIFT)];
+ buf[width + linepos++] = lut[i + ((dat[0][width + ypos++] + ROUNDADD) >> SHIFT)];
+ }
+ linepos += (2 * width - width);
+ ypos += width;
+ }
+
+ _system->copyRectToScreen(buf, width, (640-width)/2, (480-height)/2, width, height);
+ _system->updateScreen();
+
+ free(buf);
+
+}
+#endif
+
+
+
+} // End of namespace Sword1
diff --git a/engines/sword1/screen.h b/engines/sword1/screen.h
new file mode 100644
index 0000000000..43a0b74cab
--- /dev/null
+++ b/engines/sword1/screen.h
@@ -0,0 +1,157 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSSCREEN_H
+#define BSSCREEN_H
+
+#include "sword1/sworddefs.h"
+
+class OSystem;
+
+namespace Sword1 {
+
+#define MAX_FORE 20
+#define MAX_BACK 20
+#define MAX_SORT 20
+
+struct SortSpr {
+ int32 id, y;
+};
+
+struct RoomDef {
+ int totalLayers;
+ int sizeX;
+ int sizeY;
+ int gridWidth; //number of 16*16 grid blocks across - including off screen edges.
+ uint32 layers[4];
+ uint32 grids[3];
+ uint32 palettes[2];
+ uint32 parallax[2];
+};
+
+#define SCRNGRID_X 16
+#define SCRNGRID_Y 8
+#define SHRINK_BUFFER_SIZE 50000
+#define RLE_BUFFER_SIZE 50000
+
+#define FLASH_RED 0
+#define FLASH_BLUE 1
+#define BORDER_YELLOW 2
+#define BORDER_GREEN 3
+#define BORDER_PURPLE 4
+#define BORDER_BLACK 5
+
+class ResMan;
+class ObjectMan;
+class Text; // Text objects use sprites that are created internally at run-time
+ // the buffer belongs to Text, so we need a reference here.
+
+class Screen {
+public:
+ Screen(OSystem *system, ResMan *pResMan, ObjectMan *pObjMan);
+ ~Screen(void);
+ void useTextManager(Text *pTextMan);
+ void draw(void);
+
+ void quitScreen(void);
+ void newScreen(uint32 screen);
+
+ void setScrolling(int16 offsetX, int16 offsetY);
+ void addToGraphicList(uint8 listId, uint32 objId);
+
+ void fadeDownPalette(void);
+ void fadeUpPalette(void);
+ void fnSetPalette(uint8 start, uint16 length, uint32 id, bool fadeUp);
+ bool stillFading(void);
+ void fullRefresh(void);
+
+ bool showScrollFrame(void);
+ void updateScreen(void);
+ void showFrame(uint16 x, uint16 y, uint32 resId, uint32 frameNo, const byte *fadeMask = NULL, int8 fadeStatus = 0);
+
+ void fnSetParallax(uint32 screen, uint32 resId);
+ void fnFlash(uint8 color);
+ void fnBorder(uint8 color);
+
+#ifdef BACKEND_8BIT
+ void plotYUV(byte *lut, int width, int height, byte *const *dat);
+#endif
+
+private:
+ // for router debugging
+ void drawLine(uint16 x1, uint16 y1, uint16 x2, uint16 y2);
+ void vline(uint16 x, uint16 y1, uint16 y2);
+ void hline(uint16 x1, uint16 x2, uint16 y);
+ void bsubline_1(uint16 x1, uint16 y1, uint16 x2, uint16 y2);
+ void bsubline_2(uint16 x1, uint16 y1, uint16 x2, uint16 y2);
+ void bsubline_3(uint16 x1, uint16 y1, uint16 x2, uint16 y2);
+ void bsubline_4(uint16 x1, uint16 y1, uint16 x2, uint16 y2);
+
+ void verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight);
+ void blitBlockClear(uint16 x, uint16 y, uint8 *data);
+ void renderParallax(uint8 *data);
+ void processImage(uint32 id);
+ void spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *sprWidth, uint16 *sprHeight, uint16 *incr);
+ void drawSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch);
+ void decompressRLE7(uint8 *src, uint32 compSize, uint8 *dest);
+ void decompressRLE0(uint8 *src, uint32 compSize, uint8 *dest);
+ void decompressTony(uint8 *src, uint32 compSize, uint8 *dest);
+ void fastShrink(uint8 *src, uint32 width, uint32 height, uint32 scale, uint8 *dest);
+ int32 inRange(int32 a, int32 b, int32 c);
+ void fadePalette(void);
+
+ OSystem *_system;
+ ResMan *_resMan;
+ ObjectMan *_objMan;
+ Text *_textMan;
+
+ uint16 _currentScreen;
+ uint8 *_screenBuf;
+ uint8 *_screenGrid;
+ uint16 *_layerGrid[4];
+ uint8 *_layerBlocks[4];
+ uint8 *_parallax[2];
+ uint8 _rleBuffer[RLE_BUFFER_SIZE];
+ uint8 _shrinkBuffer[SHRINK_BUFFER_SIZE];
+ bool _fullRefresh;
+ uint16 _oldScrollX, _oldScrollY; // for drawing additional frames
+
+ uint32 _foreList[MAX_FORE];
+ uint32 _backList[MAX_BACK];
+ SortSpr _sortList[MAX_SORT];
+ uint8 _foreLength, _backLength, _sortLength;
+ uint16 _scrnSizeX, _scrnSizeY, _gridSizeX, _gridSizeY;
+
+ static RoomDef _roomDefTable[TOTAL_ROOMS]; // from ROOMS.C (not const, see fnSetParallax)
+
+ uint8 _targetPalette[256 * 4];
+ uint8 _currentPalette[256 * 4]; // for fading
+ uint8 _fadingStep;
+ int8 _fadingDirection; // 1 for fade up, -1 for fade down
+ bool _isBlack; // if the logic already faded down the palette, this is set to show the
+ // mainloop that no further fading is necessary.
+};
+
+} // End of namespace Sword1
+
+#endif //BSSCREEN_H
+
diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp
new file mode 100644
index 0000000000..5275c5683e
--- /dev/null
+++ b/engines/sword1/sound.cpp
@@ -0,0 +1,382 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/sound.h"
+#include "common/util.h"
+#include "sword1/resman.h"
+#include "sword1/logic.h"
+#include "sword1/sword1.h"
+
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
+#include "sound/wave.h"
+
+namespace Sword1 {
+
+#define SOUND_SPEECH_ID 1
+#define SPEECH_FLAGS (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_LITTLE_ENDIAN)
+
+Sound::Sound(const char *searchPath, Audio::Mixer *mixer, ResMan *pResMan) {
+ strcpy(_filePath, searchPath);
+ _mixer = mixer;
+ _resMan = pResMan;
+ _cowHeader = NULL;
+ _endOfQueue = 0;
+ _currentCowFile = 0;
+ _speechVolL = _speechVolR = _sfxVolL = _sfxVolR = 192;
+}
+
+Sound::~Sound(void) {
+ // clean up fx queue
+ _mixer->stopAll();
+ for (uint8 cnt = 0; cnt < _endOfQueue; cnt++)
+ if (_fxQueue[cnt].delay == 0)
+ _resMan->resClose(_fxList[_fxQueue[cnt].id].sampleId);
+ _endOfQueue = 0;
+ closeCowSystem();
+}
+
+int Sound::addToQueue(int32 fxNo) {
+ bool alreadyInQueue = false;
+ for (uint8 cnt = 0; (cnt < _endOfQueue) && (!alreadyInQueue); cnt++)
+ if (_fxQueue[cnt].id == (uint32)fxNo)
+ alreadyInQueue = true;
+ if (!alreadyInQueue) {
+ if (_endOfQueue == MAX_FXQ_LENGTH) {
+ warning("Sound queue overflow");
+ return 0;
+ }
+ if ((fxNo == 168) && (SwordEngine::_systemVars.isDemo)) {
+ // this sound doesn't exist in demo
+ return 0;
+ }
+ _resMan->resOpen(_fxList[fxNo].sampleId);
+ _fxQueue[_endOfQueue].id = fxNo;
+ if (_fxList[fxNo].type == FX_SPOT)
+ _fxQueue[_endOfQueue].delay = _fxList[fxNo].delay + 1;
+ else
+ _fxQueue[_endOfQueue].delay = 1;
+ _endOfQueue++;
+ return 1;
+ }
+ return 0;
+}
+
+void Sound::engine(void) {
+ // first of all, add any random sfx to the queue...
+ for (uint16 cnt = 0; cnt < TOTAL_FX_PER_ROOM; cnt++) {
+ uint16 fxNo = _roomsFixedFx[Logic::_scriptVars[SCREEN]][cnt];
+ if (fxNo) {
+ if (_fxList[fxNo].type == FX_RANDOM) {
+ if (_rnd.getRandomNumber(_fxList[fxNo].delay) == 0)
+ addToQueue(fxNo);
+ }
+ } else
+ break;
+ }
+ // now process the queue
+ for (uint8 cnt2 = 0; cnt2 < _endOfQueue; cnt2++) {
+ if (_fxQueue[cnt2].delay > 0) {
+ _fxQueue[cnt2].delay--;
+ if (_fxQueue[cnt2].delay == 0)
+ playSample(&_fxQueue[cnt2]);
+ } else {
+ if (!_mixer->isSoundHandleActive(_fxQueue[cnt2].handle)) { // sound finished
+ _resMan->resClose(_fxList[_fxQueue[cnt2].id].sampleId);
+ if (cnt2 != _endOfQueue-1)
+ _fxQueue[cnt2] = _fxQueue[_endOfQueue - 1];
+ _endOfQueue--;
+ }
+ }
+ }
+}
+
+void Sound::fnStopFx(int32 fxNo) {
+ _mixer->stopID(fxNo);
+ for (uint8 cnt = 0; cnt < _endOfQueue; cnt++)
+ if (_fxQueue[cnt].id == (uint32)fxNo) {
+ if (!_fxQueue[cnt].delay) // sound was started
+ _resMan->resClose(_fxList[_fxQueue[cnt].id].sampleId);
+ if (cnt != _endOfQueue-1)
+ _fxQueue[cnt] = _fxQueue[_endOfQueue-1];
+ _endOfQueue--;
+ return ;
+ }
+ debug(8, "fnStopFx: id not found in queue");
+}
+
+bool Sound::amISpeaking(void) {
+ _waveVolPos++;
+ return _waveVolume[_waveVolPos - 1];
+}
+
+bool Sound::speechFinished(void) {
+ return !_mixer->isSoundHandleActive(_speechHandle);
+}
+
+void Sound::newScreen(uint32 screen) {
+ if (_currentCowFile != SwordEngine::_systemVars.currentCD) {
+ if (_currentCowFile)
+ closeCowSystem();
+ initCowSystem();
+ }
+}
+
+void Sound::quitScreen(void) {
+ // stop all running SFX
+ while (_endOfQueue)
+ fnStopFx(_fxQueue[0].id);
+}
+
+void Sound::playSample(QueueElement *elem) {
+ uint8 *sampleData = (uint8*)_resMan->fetchRes(_fxList[elem->id].sampleId);
+ for (uint16 cnt = 0; cnt < MAX_ROOMS_PER_FX; cnt++) {
+ if (_fxList[elem->id].roomVolList[cnt].roomNo) {
+ if ((_fxList[elem->id].roomVolList[cnt].roomNo == (int)Logic::_scriptVars[SCREEN]) ||
+ (_fxList[elem->id].roomVolList[cnt].roomNo == -1)) {
+
+ uint8 volL = (_fxList[elem->id].roomVolList[cnt].leftVol * 10 * _sfxVolL) / 255;
+ uint8 volR = (_fxList[elem->id].roomVolList[cnt].rightVol * 10 * _sfxVolR) / 255;
+ int8 pan = (volR - volL) / 2;
+ uint8 volume = (volR + volL) / 2;
+ uint32 size = READ_LE_UINT32(sampleData + 0x28);
+ uint8 flags;
+ if (READ_LE_UINT16(sampleData + 0x22) == 16)
+ flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN;
+ else
+ flags = Audio::Mixer::FLAG_UNSIGNED;
+ if (READ_LE_UINT16(sampleData + 0x16) == 2)
+ flags |= Audio::Mixer::FLAG_STEREO;
+ if (_fxList[elem->id].type == FX_LOOP)
+ flags |= Audio::Mixer::FLAG_LOOP;
+ _mixer->playRaw(&elem->handle, sampleData + 0x2C, size, 11025, flags, elem->id, volume, pan);
+ }
+ } else
+ break;
+ }
+}
+
+bool Sound::startSpeech(uint16 roomNo, uint16 localNo) {
+ if (_cowHeader == NULL) {
+ warning("Sound::startSpeech: COW file isn't open!");
+ return false;
+ }
+
+ uint32 locIndex = _cowHeader[roomNo] >> 2;
+ uint32 sampleSize = _cowHeader[locIndex + (localNo * 2)];
+ uint32 index = _cowHeader[locIndex + (localNo * 2) - 1];
+ debug(6, "startSpeech(%d, %d): locIndex %d, sampleSize %d, index %d", roomNo, localNo, locIndex, sampleSize, index);
+ if (sampleSize) {
+ uint8 speechVol = (_speechVolR + _speechVolL) / 2;
+ int8 speechPan = (_speechVolR - _speechVolL) / 2;
+ if ((_cowMode == CowWave) || (_cowMode == CowDemo)) {
+ uint32 size;
+ int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size);
+ if (data)
+ _mixer->playRaw(&_speechHandle, data, size, 11025, SPEECH_FLAGS, SOUND_SPEECH_ID, speechVol, speechPan);
+ }
+#ifdef USE_MAD
+ else if (_cowMode == CowMp3) {
+ _cowFile.seek(index);
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_speechHandle, makeMP3Stream(&_cowFile, sampleSize), SOUND_SPEECH_ID, speechVol, speechPan);
+ // with compressed audio, we can't calculate the wave volume.
+ // so default to talking.
+ for (int cnt = 0; cnt < 480; cnt++)
+ _waveVolume[cnt] = true;
+ _waveVolPos = 0;
+ }
+#endif
+#ifdef USE_VORBIS
+ else if (_cowMode == CowVorbis) {
+ _cowFile.seek(index);
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_speechHandle, makeVorbisStream(&_cowFile, sampleSize), SOUND_SPEECH_ID, speechVol, speechPan);
+ for (int cnt = 0; cnt < 480; cnt++)
+ _waveVolume[cnt] = true;
+ _waveVolPos = 0;
+ }
+#endif
+ return true;
+ } else
+ return false;
+}
+
+int16 *Sound::uncompressSpeech(uint32 index, uint32 cSize, uint32 *size) {
+ uint8 *fBuf = (uint8*)malloc(cSize);
+ _cowFile.seek(index);
+ _cowFile.read(fBuf, cSize);
+ uint32 headerPos = 0;
+
+ // TODO: use loadWAVFromStream to load the WAVE data!
+ /*
+ int rate, size;
+ bye flags;
+ Common::MemoryReadStream stream(fBuf, cSize);
+ isValidWAV = loadWAVFromStream(stream, size, rate, flags);
+ */
+
+ while ((READ_BE_UINT32(fBuf + headerPos) != 'data') && (headerPos < 100))
+ headerPos++;
+ if (headerPos < 100) {
+ int32 resSize;
+ headerPos += 4; // skip 'data' tag
+ if (_cowMode != CowDemo) {
+ resSize = READ_LE_UINT32(fBuf + headerPos) >> 1;
+ headerPos += 4;
+ } else {
+ // the demo speech files have the uncompressed size embedded
+ // in the compressed stream *sigh*
+ if (READ_LE_UINT16(fBuf + headerPos) == 1) {
+ resSize = READ_LE_UINT16(fBuf + headerPos + 2);
+ resSize |= READ_LE_UINT16(fBuf + headerPos + 6) << 16;
+ } else
+ resSize = READ_LE_UINT32(fBuf + headerPos + 2);
+ resSize >>= 1;
+ }
+ assert(!(headerPos & 1));
+ int16 *srcData = (int16*)fBuf;
+ uint32 srcPos = headerPos >> 1;
+ cSize /= 2;
+ uint32 dstPos = 0;
+ /* alloc 200 additional bytes, as the demo sometimes has ASCII junk
+ at the end of the wave data */
+ int16 *dstData = (int16*)malloc(resSize * 2 + 200);
+ while (srcPos < cSize) {
+ int16 length = (int16)READ_LE_UINT16(srcData + srcPos);
+ srcPos++;
+ if (length < 0) {
+ length = -length;
+ for (uint16 cnt = 0; cnt < (uint16)length; cnt++)
+ dstData[dstPos++] = srcData[srcPos];
+ srcPos++;
+ } else {
+ memcpy(dstData + dstPos, srcData + srcPos, length * 2);
+ dstPos += length;
+ srcPos += length;
+ }
+ }
+ assert(dstPos < (uint32)resSize + 100);
+ if (_cowMode == CowDemo) // demo has wave output size embedded in the compressed data
+ *(uint32*)dstData = 0;
+ free(fBuf);
+ *size = resSize * 2;
+ calcWaveVolume(dstData, resSize);
+ return dstData;
+ } else {
+ free(fBuf);
+ warning("Sound::uncompressSpeech(): DATA tag not found in wave header");
+ *size = 0;
+ return NULL;
+ }
+}
+
+void Sound::calcWaveVolume(int16 *data, uint32 length) {
+ int16 *blkPos = data + 918;
+ uint32 cnt;
+ for (cnt = 0; cnt < WAVE_VOL_TAB_LENGTH; cnt++)
+ _waveVolume[cnt] = false;
+ _waveVolPos = 0;
+ for (uint32 blkCnt = 1; blkCnt < length / 918; blkCnt++) {
+ if (blkCnt >= WAVE_VOL_TAB_LENGTH) {
+ warning("Wave vol tab too small.");
+ return;
+ }
+ int32 average = 0;
+ for (cnt = 0; cnt < 918; cnt++)
+ average += blkPos[cnt];
+ average /= 918;
+ uint32 diff = 0;
+ for (cnt = 0; cnt < 918; cnt++) {
+ int16 smpDiff = *blkPos - average;
+ diff += (uint32)ABS(smpDiff);
+ blkPos++;
+ }
+ if (diff > WAVE_VOL_THRESHOLD)
+ _waveVolume[blkCnt - 1] = true;
+ }
+}
+
+void Sound::stopSpeech(void) {
+ _mixer->stopID(SOUND_SPEECH_ID);
+}
+
+void Sound::initCowSystem(void) {
+ char cowName[25];
+ /* look for speech1/2.clu in the data dir
+ and speech/speech.clu (running from cd or using cd layout)
+ */
+#ifdef USE_MAD
+ sprintf(cowName, "SPEECH%d.CL3", SwordEngine::_systemVars.currentCD);
+ _cowFile.open(cowName);
+ if (_cowFile.isOpen()) {
+ debug(1, "Using MP3 compressed Speech Cluster");
+ _cowMode = CowMp3;
+ }
+#endif
+#ifdef USE_VORBIS
+ if (!_cowFile.isOpen()) {
+ sprintf(cowName, "SPEECH%d.CLV", SwordEngine::_systemVars.currentCD);
+ _cowFile.open(cowName);
+ if (_cowFile.isOpen()) {
+ debug(1, "Using Vorbis compressed Speech Cluster");
+ _cowMode = CowVorbis;
+ }
+ }
+#endif
+ if (!_cowFile.isOpen()) {
+ sprintf(cowName, "SPEECH%d.CLU", SwordEngine::_systemVars.currentCD);
+ _cowFile.open(cowName);
+ if (!_cowFile.isOpen()) {
+ _cowFile.open("speech.clu");
+ }
+ debug(1, "Using uncompressed Speech Cluster");
+ _cowMode = CowWave;
+ }
+ if (!_cowFile.isOpen())
+ _cowFile.open("speech.clu");
+ if (!_cowFile.isOpen()) {
+ _cowFile.open("cows.mad");
+ if (_cowFile.isOpen())
+ _cowMode = CowDemo;
+ }
+ if (_cowFile.isOpen()) {
+ _cowHeaderSize = _cowFile.readUint32LE();
+ _cowHeader = (uint32*)malloc(_cowHeaderSize);
+ if (_cowHeaderSize & 3)
+ error("Unexpected cow header size %d", _cowHeaderSize);
+ for (uint32 cnt = 0; cnt < (_cowHeaderSize / 4) - 1; cnt++)
+ _cowHeader[cnt] = _cowFile.readUint32LE();
+ _currentCowFile = SwordEngine::_systemVars.currentCD;
+ } else
+ warning("Sound::initCowSystem: Can't open SPEECH%d.CLU", SwordEngine::_systemVars.currentCD);
+}
+
+void Sound::closeCowSystem(void) {
+ if (_cowFile.isOpen())
+ _cowFile.close();
+ if (_cowHeader)
+ free(_cowHeader);
+ _cowHeader = NULL;
+ _currentCowFile = 0;
+}
+
+} // End of namespace Sword1
diff --git a/engines/sword1/sound.h b/engines/sword1/sound.h
new file mode 100644
index 0000000000..10fed8b33c
--- /dev/null
+++ b/engines/sword1/sound.h
@@ -0,0 +1,127 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSSOUND_H
+#define BSSOUND_H
+
+#include "sword1/object.h"
+#include "sword1/sworddefs.h"
+#include "common/file.h"
+#include "sound/mixer.h"
+#include "common/util.h"
+
+namespace Audio {
+ class Mixer;
+}
+
+namespace Sword1 {
+
+#define TOTAL_FX_PER_ROOM 7 // total loop & random fx per room (see fx_list.c)
+#define MAX_ROOMS_PER_FX 7 // max no. of rooms in the fx's room,vol list
+#define MAX_FXQ_LENGTH 32 // max length of sound queue - ie. max number of fx that can be stored up/playing together
+
+#define FX_SPOT 1
+#define FX_LOOP 2
+#define FX_RANDOM 3
+
+struct QueueElement {
+ uint32 id, delay;
+ Audio::SoundHandle handle;
+};
+
+struct RoomVol {
+ int32 roomNo, leftVol, rightVol;
+};
+
+struct FxDef {
+ uint32 sampleId, type, delay;
+ RoomVol roomVolList[MAX_ROOMS_PER_FX];
+};
+
+class ResMan;
+#define WAVE_VOL_TAB_LENGTH 480
+#define WAVE_VOL_THRESHOLD 190000 //120000
+
+enum CowMode {
+ CowWave = 0,
+ CowMp3,
+ CowVorbis,
+ CowDemo
+};
+
+class Sound {
+public:
+ Sound(const char *searchPath, Audio::Mixer *mixer, ResMan *pResMan);
+ ~Sound(void);
+ void setSpeechVol(uint8 volL, uint8 volR) { _speechVolL = volL; _speechVolR = volR; };
+ void setSfxVol(uint8 volL, uint8 volR) { _sfxVolL = volL; _sfxVolR = volR; };
+ void giveSpeechVol(uint8 *volL, uint8 *volR) { *volL = _speechVolL; *volR = _speechVolR; };
+ void giveSfxVol(uint8 *volL, uint8 *volR) { *volL = _sfxVolL; *volR = _sfxVolR; };
+ void newScreen(uint32 screen);
+ void quitScreen(void);
+ void closeCowSystem(void);
+
+ bool startSpeech(uint16 roomNo, uint16 localNo);
+ bool speechFinished(void);
+ void stopSpeech();
+ bool amISpeaking(void);
+
+ void fnStopFx(int32 fxNo);
+ int addToQueue(int32 fxNo);
+
+ void engine(void);
+
+private:
+ uint8 _sfxVolL, _sfxVolR, _speechVolL, _speechVolR;
+ void playSample(QueueElement *elem);
+ void initCowSystem(void);
+
+ int16 *uncompressSpeech(uint32 index, uint32 cSize, uint32 *size);
+ void calcWaveVolume(int16 *data, uint32 length);
+ bool _waveVolume[WAVE_VOL_TAB_LENGTH];
+ uint16 _waveVolPos;
+ Common::File _cowFile;
+ uint32 *_cowHeader;
+ uint32 _cowHeaderSize;
+ uint8 _currentCowFile;
+ CowMode _cowMode;
+ Audio::SoundHandle _speechHandle, _fxHandle;
+ Common::RandomSource _rnd;
+
+ QueueElement _fxQueue[MAX_FXQ_LENGTH];
+ uint8 _endOfQueue;
+ Audio::Mixer *_mixer;
+ ResMan *_resMan;
+ char _filePath[100];
+ static const char _musicList[270];
+ static const uint16 _roomsFixedFx[TOTAL_ROOMS][TOTAL_FX_PER_ROOM];
+#ifdef PALMOS_68K
+public:
+ static const FxDef *_fxList;
+#else
+ static const FxDef _fxList[312];
+#endif
+};
+
+} // End of namespace Sword1
+
+#endif //BSSOUND_H
diff --git a/engines/sword1/staticres.cpp b/engines/sword1/staticres.cpp
new file mode 100644
index 0000000000..9991d0e826
--- /dev/null
+++ b/engines/sword1/staticres.cpp
@@ -0,0 +1,7168 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/sworddefs.h"
+#include "sword1/swordres.h"
+#include "sword1/screen.h"
+#include "sword1/resman.h"
+#include "sword1/objectman.h"
+#include "sword1/menu.h"
+#include "sword1/music.h"
+#include "sword1/sound.h"
+#include "sword1/sword1.h"
+#include "sword1/logic.h"
+
+namespace Sword1 {
+
+const uint8 SwordEngine::_cdList[TOTAL_SECTIONS] = {
+ 0, // 0 inventory
+
+ 1, // 1 PARIS 1
+ 1, // 2
+ 1, // 3
+ 1, // 4
+ 1, // 5
+ 1, // 6
+ 1, // 7
+ 1, // 8
+
+ 1, // 9 PARIS 2
+ 1, // 10
+ 1, // 11
+ 1, // 12
+ 1, // 13
+ 1, // 14
+ 1, // 15
+ 1, // 16
+ 1, // 17
+ 1, // 18
+
+ 2, // 19 IRELAND
+ 2, // 20
+ 2, // 21
+ 2, // 22
+ 2, // 23
+ 2, // 24
+ 2, // 25
+ 2, // 26
+
+ 1, // 27 PARIS 3
+ 1, // 28
+ 1, // 29
+ 1, // 30 - Heart Monitor
+ 1, // 31
+ 1, // 32
+ 1, // 33
+ 1, // 34
+ 1, // 35
+
+ 1, // 36 PARIS 4
+ 1, // 37
+ 1, // 38
+ 1, // 39
+ 1, // 40
+ 1, // 41
+ 1, // 42
+ 1, // 43
+ 0, // 44 <NOT USED>
+
+ 2, // 45 SYRIA
+ 1, // 46 PARIS 4
+ 2, // 47
+ 1, // 48 PARIS 4
+ 2, // 49
+ 2, // 50
+ 0, // 51 <NOT USED>
+ 0, // 52 <NOT USED>
+ 2, // 53
+ 2, // 54
+ 2, // 55
+
+ 2, // 56 SPAIN
+ 2, // 57
+ 2, // 58
+ 2, // 59
+ 2, // 60
+ 2, // 61
+ 2, // 62
+
+ 2, // 63 NIGHT TRAIN
+ 0, // 64 <NOT USED>
+ 2, // 65
+ 2, // 66
+ 2, // 67
+ 0, // 68 <NOT USED>
+ 2, // 69
+ 0, // 70 <NOT USED>
+
+ 2, // 71 SCOTLAND
+ 2, // 72
+ 2, // 73
+ 2, // 74 END SEQUENCE IN SECRET_CRYPT
+ 2, // 75
+ 2, // 76
+ 2, // 77
+ 2, // 78
+ 2, // 79
+
+ 1, // 80 PARIS MAP
+
+ 1, // 81 Full-screen for "Asstair" in Paris2
+
+ 2, // 82 Full-screen BRITMAP in sc55 (Syrian Cave)
+ 0, // 83 <NOT USED>
+ 0, // 84 <NOT USED>
+ 0, // 85 <NOT USED>
+
+ 1, // 86 EUROPE MAP
+ 1, // 87 fudged in for normal window (sc48)
+ 1, // 88 fudged in for filtered window (sc48)
+ 0, // 89 <NOT USED>
+
+ 0, // 90 PHONE SCREEN
+ 0, // 91 ENVELOPE SCREEN
+ 1, // 92 fudged in for George close-up surprised in sc17 wardrobe
+ 1, // 93 fudged in for George close-up inquisitive in sc17 wardrobe
+ 1, // 94 fudged in for George close-up in sc29 sarcophagus
+ 1, // 95 fudged in for George close-up in sc29 sarcophagus
+ 1, // 96 fudged in for chalice close-up from sc42
+ 0, // 97 <NOT USED>
+ 0, // 98 <NOT USED>
+ 0, // 99 MESSAGE SCREEN (BLANK)
+
+ 0, // 100
+ 0, // 101
+ 0, // 102
+ 0, // 103
+ 0, // 104
+ 0, // 105
+ 0, // 106
+ 0, // 107
+ 0, // 108
+ 0, // 109
+
+ 0, // 110
+ 0, // 111
+ 0, // 112
+ 0, // 113
+ 0, // 114
+ 0, // 115
+ 0, // 116
+ 0, // 117
+ 0, // 118
+ 0, // 119
+
+ 0, // 120
+ 0, // 121
+ 0, // 122
+ 0, // 123
+ 0, // 124
+ 0, // 125
+ 0, // 126
+ 0, // 127
+ 0, // 128 GEORGE'S GAME SECTION
+ 0, // 129 NICO'S TEXT - on both CD's
+
+ 0, // 130
+ 1, // 131 BENOIR'S TEXT - on CD1
+ 0, // 132
+ 1, // 133 ROSSO'S TEXT - on CD1
+ 0, // 134
+ 0, // 135
+ 0, // 136
+ 0, // 137
+ 0, // 138
+ 0, // 139
+
+ 0, // 140
+ 0, // 141
+ 0, // 142
+ 0, // 143
+ 0, // 144
+ 1, // 145 MOUE'S TEXT - on CD1
+ 1, // 146 ALBERT'S TEXT - on CD1
+};
+
+const MenuObject Menu::_objectDefs[TOTAL_pockets + 1] = {
+ { // 0 can't use
+ 0, 0, 0, 0, 0
+ },
+ { // 1 NEWSPAPER
+ menu_newspaper, // text_desc
+ ICON_NEWSPAPER, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_NEWSPAPER, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 2 HAZEL_WAND
+ menu_hazel_wand, // text_desc
+ ICON_HAZEL_WAND, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_HAZEL_WAND, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 3 BEER_TOWEL
+ 0, // text_desc - SEE MENU.SCR
+ ICON_BEER_TOWEL, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_BEER_TOWEL, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 4 HOTEL_KEY
+ menu_hotel_key, // text_desc
+ ICON_HOTEL_KEY, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_HOTEL_KEY, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 5 BALL
+ menu_ball, // text_desc
+ ICON_BALL, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_BALL, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 6 STATUETTE
+ menu_statuette, // text_desc
+ ICON_STATUETTE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_STATUETTE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 7 RED_NOSE
+ 0, // text_desc - SEE MENU.SCR
+ ICON_RED_NOSE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_RED_NOSE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 8 POLISHED_CHALICE
+ menu_polished_chalice, // text_desc
+ ICON_POLISHED_CHALICE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_POLISHED_CHALICE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 9 DOLLAR_BILL
+ menu_dollar_bill, // text_desc
+ ICON_DOLLAR_BILL, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_DOLLAR_BILL, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 10 PHOTO
+ menu_photograph, // text_desc
+ ICON_PHOTOGRAPH, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_PHOTOGRAPH, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 11 FLASHLIGHT
+ menu_flashlight, // text_desc
+ ICON_FLASHLIGHT, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_FLASHLIGHT, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 12 FUSE_WIRE
+ menu_fuse_wire, // text_desc
+ ICON_FUSE_WIRE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_FUSE_WIRE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 13 GEM
+ menu_gem, // text_desc
+ ICON_GEM, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_GEM, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 14 STATUETTE_PAINT
+ menu_statuette_paint, // text_desc
+ ICON_STATUETTE_PAINT, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_STATUETTE_PAINT, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 15 STICK
+ menu_stick, // text_desc
+ ICON_STICK, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_STICK, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 16 EXCAV_KEY
+ menu_excav_key, // text_desc
+ ICON_EXCAV_KEY, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_EXCAV_KEY, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 17 LAB_PASS
+ menu_lab_pass, // text_desc
+ ICON_LAB_PASS, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_LAB_PASS, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 18 LIFTING_KEYS
+ menu_lifting_keys, // text_desc
+ ICON_LIFTING_KEYS, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_LIFTING_KEYS, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 19 MANUSCRIPT
+ menu_manuscript, // text_desc
+ ICON_MANUSCRIPT, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_MANUSCRIPT, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 20 MATCH_BOOK
+ menu_match_book, // text_desc
+ ICON_MATCHBOOK, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_MATCHBOOK, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 21 SUIT_MATERIAL
+ menu_suit_material, // text_desc
+ ICON_SUIT_MATERIAL, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_SUIT_MATERIAL, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 22 STICK_TOWEL
+ menu_stick_towel, // text_desc
+ ICON_STICK_TOWEL, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_STICK_TOWEL, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 23 PLASTER
+ menu_plaster, // text_desc
+ ICON_PLASTER, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_PLASTER, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 24 PRESSURE_GAUGE
+ menu_pressure_gauge, // text_desc
+ ICON_PRESSURE_GAUGE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_PRESSURE_GAUGE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 25 RAILWAY_TICKET
+ menu_railway_ticket, // text_desc
+ ICON_RAILWAY_TICKET, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_RAILWAY_TICKET, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 26 BUZZER
+ menu_buzzer, // text_desc
+ ICON_BUZZER, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_BUZZER, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 27 ROSSO_CARD
+ menu_rosso_card, // text_desc
+ ICON_ROSSO_CARD, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_ROSSO_CARD, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 28 TOILET_KEY
+ menu_toilet_key, // text_desc
+ ICON_TOILET_KEY, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_TOILET_KEY, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 29 SOAP
+ menu_soap, // text_desc
+ ICON_SOAP, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_SOAP, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 30 STONE_KEY
+ menu_stone_key, // text_desc
+ ICON_STONE_KEY, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_STONE_KEY, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 31 CHALICE
+ menu_chalice, // text_desc
+ ICON_CHALICE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_CHALICE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 32 TISSUE
+ menu_tissue, // text_desc
+ ICON_TISSUE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_TISSUE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 33 TOILET_BRUSH
+ menu_toilet_brush, // text_desc
+ ICON_TOILET_BRUSH, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_TOILET_BRUSH, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 34 TOILET_CHAIN
+ menu_toilet_chain, // text_desc
+ ICON_TOILET_CHAIN, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_TOILET_CHAIN, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 35 TOWEL
+ menu_towel, // text_desc
+ ICON_TOWEL, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_TOWEL, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 36 TRIPOD
+ menu_tripod, // text_desc
+ ICON_TRIPOD, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_TRIPOD, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 37 LENS
+ menu_lens, // text_desc
+ ICON_LENS, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_LENS, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 38 MIRROR
+ menu_mirror, // text_desc
+ ICON_MIRROR, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_MIRROR, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 39 TOWEL_CUT
+ menu_towel_cut, // text_desc
+ ICON_TOWEL_CUT, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_TOWEL_CUT, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 40 BIBLE
+ menu_bible, // text_desc
+ ICON_BIBLE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_BIBLE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 41 TISSUE_CHARRED
+ menu_tissue_charred, // text_desc
+ ICON_TISSUE_CHARRED, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_TISSUE_CHARRED, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 42 FALSE_KEY
+ menu_false_key, // text_desc
+ ICON_FALSE_KEY, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_FALSE_KEY, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 43 PAINTED_KEY - looks identical to excav key, so uses that icon & luggage
+ menu_painted_key, // text_desc
+ ICON_EXCAV_KEY, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_EXCAV_KEY, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 44 KEYRING
+ 0, // text_desc - SEE MENU.SCR
+ ICON_KEYRING, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_KEYRING, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 45 SOAP_IMP
+ menu_soap_imp, // text_desc
+ ICON_SOAP_IMP, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_SOAP_IMP, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 46 SOAP_PLAS
+ menu_soap_plas, // text_desc
+ ICON_SOAP_PLAS, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_SOAP_PLAS, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 47 COG_1 - the larger cog with spindle attached
+ menu_cog_1, // text_desc
+ ICON_COG_1, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_COG_1, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 48 COG_2 - the smaller cog, found in the rubble
+ menu_cog_2, // text_desc
+ ICON_COG_2, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_COG_2, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 49 HANDLE
+ menu_handle, // text_desc
+ ICON_HANDLE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_HANDLE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 50 COIN
+ menu_coin, // text_desc
+ ICON_COIN, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_COIN, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 51 BIRO
+ menu_biro, // text_desc
+ ICON_BIRO, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_BIRO, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ },
+ { // 52 PIPE
+ menu_pipe, // text_desc
+ ICON_PIPE, // big_icon_res
+ 0, // big_icon_frame
+ LUGG_PIPE, // luggage_icon_res
+ SCR_icon_combine_script, // use_script
+ }
+};
+
+const Subject Menu::_subjectList[TOTAL_subjects] = {
+ { // 256
+ 0, // subject_res
+ 0 // subject_frame
+ },
+ { // 257
+ ICON_BEER, // subject_res
+ 0 // subject_frame
+ },
+ { // 258
+ ICON_CASTLE, // subject_res
+ 0 // subject_frame
+ },
+ { // 259
+ ICON_YES, // subject_res
+ 0 // subject_frame
+ },
+ { // 260
+ ICON_NO, // subject_res
+ 0 // subject_frame
+ },
+ { // 261
+ ICON_PEAGRAM, // subject_res
+ 0 // subject_frame
+ },
+ { // 262
+ ICON_DIG, // subject_res
+ 0 // subject_frame
+ },
+ { // 263
+ ICON_SEAN, // subject_res
+ 0 // subject_frame
+ },
+ { // 264
+ ICON_GEM, // subject_res
+ 0 // subject_frame
+ },
+ { // 265
+ ICON_TEMPLARS, // subject_res
+ 0 // subject_frame
+ },
+ { // 266
+ ICON_LEPRECHAUN, // subject_res
+ 0 // subject_frame
+ },
+ { // 267
+ ICON_GOODBYE, // subject_res
+ 0 // subject_frame
+ },
+ { // 268
+ ICON_GEORGE, // subject_res
+ 0 // subject_frame
+ },
+ { // 269
+ ICON_ROSSO, // subject_res
+ 0 // subject_frame
+ },
+ { // 270
+ ICON_GHOST, // subject_res
+ 0 // subject_frame
+ },
+ { // 271
+ ICON_CLOWN, // subject_res
+ 0 // subject_frame
+ },
+ { // 272
+ ICON_CAR, // subject_res
+ 0 // subject_frame
+ },
+ { // 273
+ ICON_MOUE, // subject_res
+ 0 // subject_frame
+ },
+ { // 274
+ ICON_NICO, // subject_res
+ 0 // subject_frame
+ },
+ { // 275
+ ICON_MOB, // subject_res
+ 0 // subject_frame
+ },
+ { // 276
+ ICON_CHANTELLE, // subject_res
+ 0 // subject_frame
+ },
+ { // 277
+ ICON_PLANTARD, // subject_res
+ 0 // subject_frame
+ },
+ { // 278
+ ICON_JACKET, // subject_res
+ 0 // subject_frame
+ },
+ { // 279
+ ICON_BRIEFCASE, // subject_res
+ 0 // subject_frame
+ },
+ {
+ 0, //ICON_GLASS, // subject_res
+ 0 // subject_frame
+ },
+ { // 281
+ ICON_GLASS_EYE, // subject_res
+ 0 // subject_frame
+ },
+ { // 282
+ ICON_BULL, // subject_res
+ 0 // subject_frame
+ },
+ { // 283
+ ICON_KLAUSNER, // subject_res
+ 0 // subject_frame
+ },
+ { // 284
+ 0, //ICON_LOOM, // subject_res
+ 0 // subject_frame
+ },
+ { // 285
+ ICON_ULTAR, // subject_res
+ 0 // subject_frame
+ },
+ { // 286
+ ICON_PHONE, // subject_res
+ 0 // subject_frame
+ },
+ { // 287
+ ICON_PHOTOGRAPH, // subject_res
+ 0 // subject_frame
+ },
+ { // 288
+ ICON_CREST, // subject_res
+ 0 // subject_frame
+ },
+ { // 289
+ ICON_LOBINEAU, // subject_res
+ 0 // subject_frame
+ },
+ { // 290
+ ICON_BOOK, // subject_res
+ 0 // subject_frame
+ },
+ { // 291 (SNARE)
+ ICON_FUSE_WIRE, // subject_res
+ 0 // subject_frame
+ },
+ { // 292
+ ICON_LEARY, // subject_res
+ 0 // subject_frame
+ },
+ { // 293
+ ICON_LADDER, // subject_res
+ 0 // subject_frame
+ },
+ { // 294
+ ICON_GOAT, // subject_res
+ 0 // subject_frame
+ },
+ { // 295
+ ICON_TOOLBOX, // subject_res
+ 0 // subject_frame
+ },
+ { // 296
+ ICON_PACKAGE, // subject_res
+ 0 // subject_frame
+ },
+ { // 297
+ ICON_FISH, // subject_res
+ 0 // subject_frame
+ },
+ { // 298
+ ICON_NEJO, // subject_res
+ 0 // subject_frame
+ },
+ { // 299
+ ICON_CAT, // subject_res
+ 0 // subject_frame
+ },
+ { // 300
+ ICON_AYUB, // subject_res
+ 0 // subject_frame
+ },
+ { // 301
+ ICON_STATUETTE, // subject_res
+ 0 // subject_frame
+ },
+ { // 302
+ ICON_NEJO_STALL, // subject_res
+ 0 // subject_frame
+ },
+ { // 303
+ ICON_TEMPLARS, // subject_res
+ 0 // subject_frame
+ },
+ { // 304
+ ICON_ARTO, // subject_res
+ 0 // subject_frame
+ },
+ { // 305
+ ICON_HENDERSONS, // subject_res
+ 0 // subject_frame
+ },
+ { // 306
+ ICON_CLUB, // subject_res
+ 0 // subject_frame
+ },
+ { // 307
+ ICON_SIGN, // subject_res
+ 0 // subject_frame
+ },
+ { // 308
+ ICON_TAXI, // subject_res
+ 0 // subject_frame
+ },
+ { // 309
+ ICON_BULLS_HEAD, // subject_res
+ 0 // subject_frame
+ },
+ { // 310
+ ICON_DUANE, // subject_res
+ 0 // subject_frame
+ },
+ { // 311
+ ICON_PEARL, // subject_res
+ 0 // subject_frame
+ },
+ { // 312
+ ICON_TRUTH, // subject_res
+ 0 // subject_frame
+ },
+ { // 313
+ ICON_LIE, // subject_res
+ 0 // subject_frame
+ },
+ { // 314
+ 0, //ICON_SUBJECT,// subject_res
+ 0 // subject_frame
+ },
+ { // 315 KEY
+ ICON_HOTEL_KEY, // subject_res
+ 0 // subject_frame
+ },
+ { // 316
+ ICON_FLOWERS, // subject_res
+ 0 // subject_frame
+ },
+ { // 317
+ ICON_BUST, // subject_res
+ 0 // subject_frame
+ },
+ { // 318
+ ICON_MANUSCRIPT, // subject_res
+ 0 // subject_frame
+ },
+ { // 319
+ ICON_WEASEL, // subject_res
+ 0 // subject_frame
+ },
+ { // 320
+ ICON_BANANA, // subject_res
+ 0 // subject_frame
+ },
+ { // 321
+ ICON_WEAVER, // subject_res
+ 0 // subject_frame
+ },
+ { // 322
+ ICON_KNIGHT, // subject_res
+ 0 // subject_frame
+ },
+ { // 323
+ ICON_QUEEN, // subject_res
+ 0 // subject_frame
+ },
+ { // 324
+ ICON_PIERMONT, // subject_res
+ 0 // subject_frame
+ },
+ { // 325
+ ICON_BALL, // subject_res
+ 0 // subject_frame
+ },
+ { // 326
+ ICON_COUNTESS, // subject_res
+ 0 // subject_frame
+ },
+ { // 327
+ ICON_MARQUET, // subject_res
+ 0 // subject_frame
+ },
+ { // 328
+ ICON_SAFE, // subject_res
+ 0 // subject_frame
+ },
+ { // 329
+ ICON_COINS, // subject_res
+ 0 // subject_frame
+ },
+ { // 330
+ ICON_CHESS_SET, // subject_res
+ 0 // subject_frame
+ },
+ { // 331
+ ICON_TOMB, // subject_res
+ 0 // subject_frame
+ },
+ { // 332
+ ICON_CANDLE, // subject_res
+ 0 // subject_frame
+ },
+ { // 333
+ ICON_MARY, // subject_res
+ 0 // subject_frame
+ },
+ { // 334
+ ICON_CHESSBOARD, // subject_res
+ 0 // subject_frame
+ },
+ { // 335
+ ICON_TRIPOD, // subject_res
+ 0 // subject_frame
+ },
+ { // 336
+ ICON_POTS, // subject_res
+ 0 // subject_frame
+ },
+ { // 337
+ ICON_ALARM, // subject_res
+ 0 // subject_frame
+ },
+ { // 338
+ ICON_BAPHOMET, // subject_res
+ 0 // subject_frame
+ },
+ { // 339
+ ICON_PHRASE, // subject_res
+ 0 // subject_frame
+ },
+ { // 340
+ ICON_POLISHED_CHALICE, // subject_res
+ 0 // subject_frame
+ },
+ { // 341
+ ICON_NURSE, // subject_res
+ 0 // subject_frame
+ },
+ { // 342
+ ICON_WELL, // subject_res
+ 0 // subject_frame
+ },
+ { // 343
+ ICON_WELL2, // subject_res
+ 0 // subject_frame
+ },
+ { // 344
+ ICON_HAZEL_WAND, // subject_res
+ 0 // subject_frame
+ },
+ { // 345
+ ICON_CHALICE, // subject_res
+ 0 // subject_frame
+ },
+ { // 346
+ ICON_MR_SHINY, // subject_res
+ 0 // subject_frame
+ },
+ { // 347
+ ICON_PHOTOGRAPH, // subject_res
+ 0 // subject_frame
+ },
+ { // 348
+ ICON_PHILIPPE, // subject_res
+ 0 // subject_frame
+ },
+ { // 349
+ ICON_ERIC, // subject_res
+ 0 // subject_frame
+ },
+ { // 350
+ ICON_ROZZER, // subject_res
+ 0 // subject_frame
+ },
+ { // 351
+ ICON_JUGGLER, // subject_res
+ 0 // subject_frame
+ },
+ { // 352
+ ICON_PRIEST, // subject_res
+ 0 // subject_frame
+ },
+ { // 353
+ ICON_WINDOW, // subject_res
+ 0 // subject_frame
+ },
+ { // 354
+ ICON_SCROLL, // subject_res
+ 0 // subject_frame
+ },
+ { // 355
+ ICON_PRESSURE_GAUGE, // subject_res
+ 0 // subject_frame
+ },
+ { // 356
+ ICON_RENEE, // subject_res
+ 0 // subject_frame
+ },
+ { // 357
+ ICON_CHURCH, // subject_res
+ 0 // subject_frame
+ },
+ { // 358
+ ICON_EKLUND, // subject_res
+ 0 // subject_frame
+ },
+ { // 359
+ ICON_FORTUNE, // subject_res
+ 0 // subject_frame
+ },
+ { // 360
+ ICON_PAINTER, // subject_res
+ 0 // subject_frame
+ },
+ { // 361
+ 0, //ICON_SWORD, // subject_res
+ 0 // subject_frame
+ },
+ { // 362
+ ICON_GUARD, // subject_res
+ 0 // subject_frame
+ },
+ { // 363
+ ICON_THERMOSTAT, // subject_res
+ 0 // subject_frame
+ },
+ { // 364
+ ICON_TOILET, // subject_res
+ 0 // subject_frame
+ },
+ { // 365
+ ICON_MONTFAUCON, // subject_res
+ 0 // subject_frame
+ },
+ { // 366
+ ICON_ASSASSIN, // subject_res
+ 0 // subject_frame
+ },
+ { // 367
+ ICON_HASH, // subject_res
+ 0 // subject_frame
+ },
+ { // 368
+ ICON_DOG, // subject_res
+ 0 // subject_frame
+ },
+ { // 369
+ ICON_AYUB, // subject_res
+ 0 // subject_frame
+ },
+ { // 370
+ ICON_LENS, // subject_res
+ 0 // subject_frame
+ },
+ { // 371
+ ICON_RED_NOSE, // subject_res
+ 0 // subject_frame
+ },
+ { // 372
+ 0, // subject_res
+ 0 // subject_frame
+ },
+ { // 373
+ 0, // subject_res
+ 0 // subject_frame
+ },
+ { // 374
+ 0, // subject_res
+ 0 // subject_frame
+ }
+};
+
+const uint32 ResMan::_scriptList[TOTAL_SECTIONS] = { //a table of resource tags
+ SCRIPT0, // 0 STANDARD SCRIPTS
+
+ SCRIPT1, // 1 PARIS 1
+ SCRIPT2, // 2
+ SCRIPT3, // 3
+ SCRIPT4, // 4
+ SCRIPT5, // 5
+ SCRIPT6, // 6
+ SCRIPT7, // 7
+ SCRIPT8, // 8
+
+ SCRIPT9, // 9 PARIS 2
+ SCRIPT10, // 10
+ SCRIPT11, // 11
+ SCRIPT12, // 12
+ SCRIPT13, // 13
+ SCRIPT14, // 14
+ SCRIPT15, // 15
+ SCRIPT16, // 16
+ SCRIPT17, // 17
+ SCRIPT18, // 18
+
+ SCRIPT19, // 19 IRELAND
+ SCRIPT20, // 20
+ SCRIPT21, // 21
+ SCRIPT22, // 22
+ SCRIPT23, // 23
+ SCRIPT24, // 24
+ SCRIPT25, // 25
+ SCRIPT26, // 26
+
+ SCRIPT27, // 27 PARIS 3
+ SCRIPT28, // 28
+ SCRIPT29, // 29
+ 0, // 30
+ SCRIPT31, // 31
+ SCRIPT32, // 32
+ SCRIPT33, // 33
+ SCRIPT34, // 34
+ SCRIPT35, // 35
+
+ SCRIPT36, // 36 PARIS 4
+ SCRIPT37, // 37
+ SCRIPT38, // 38
+ SCRIPT39, // 39
+ SCRIPT40, // 40
+ SCRIPT41, // 41
+ SCRIPT42, // 42
+ SCRIPT43, // 43
+ 0, // 44
+
+ SCRIPT45, // 45 SYRIA
+ SCRIPT46, // 46 PARIS 4
+ SCRIPT47, // 47
+ SCRIPT48, // 48 PARIS 4
+ SCRIPT49, // 49
+ SCRIPT50, // 50
+ 0, // 51
+ 0, // 52
+ 0, // 53
+ SCRIPT54, // 54
+ SCRIPT55, // 55
+
+ SCRIPT56, // 56 SPAIN
+ SCRIPT57, // 57
+ SCRIPT58, // 58
+ SCRIPT59, // 59
+ SCRIPT60, // 60
+ SCRIPT61, // 61
+ SCRIPT62, // 62
+
+ SCRIPT63, // 63 NIGHT TRAIN
+ 0, // 64
+ SCRIPT65, // 65
+ SCRIPT66, // 66
+ SCRIPT67, // 67
+ 0, // 68
+ SCRIPT69, // 69
+ 0, // 70
+
+ SCRIPT71, // 71 SCOTLAND
+ SCRIPT72, // 72
+ SCRIPT73, // 73
+ SCRIPT74, // 74
+
+ 0, // 75
+ 0, // 76
+ 0, // 77
+ 0, // 78
+ 0, // 79
+ SCRIPT80, // 80
+ 0, // 81
+ 0, // 82
+ 0, // 83
+ 0, // 84
+ 0, // 85
+ SCRIPT86, // 86
+ 0, // 87
+ 0, // 88
+ 0, // 89
+ SCRIPT90, // 90
+ 0, // 91
+ 0, // 92
+ 0, // 93
+ 0, // 94
+ 0, // 95
+ 0, // 96
+ 0, // 97
+ 0, // 98
+ 0, // 99
+ 0, // 100
+ 0, // 101
+ 0, // 102
+ 0, // 103
+ 0, // 104
+ 0, // 105
+ 0, // 106
+ 0, // 107
+ 0, // 108
+ 0, // 109
+ 0, // 110
+ 0, // 111
+ 0, // 112
+ 0, // 113
+ 0, // 114
+ 0, // 115
+ 0, // 116
+ 0, // 117
+ 0, // 118
+ 0, // 119
+ 0, // 120
+ 0, // 121
+ 0, // 122
+ 0, // 123
+ 0, // 124
+ 0, // 125
+ 0, // 126
+ 0, // 127
+ SCRIPT128, // 128
+
+ SCRIPT129, // 129
+ SCRIPT130, // 130
+ SCRIPT131, // 131
+ 0, // 132
+ SCRIPT133, // 133
+ SCRIPT134, // 134
+ 0, // 135
+ 0, // 136
+ 0, // 137
+ 0, // 138
+ 0, // 139
+ 0, // 140
+ 0, // 141
+ 0, // 142
+ 0, // 143
+ 0, // 144
+ SCRIPT145, // 145
+ SCRIPT146, // 146
+ 0, // 147
+ 0, // 148
+ 0, // 149
+};
+
+const uint32 ObjectMan::_objectList[TOTAL_SECTIONS] = { //a table of pointers to object files
+ 0, // 0
+
+ COMP1, // 1 PARIS 1
+ COMP2, // 2
+ COMP3, // 3
+ COMP4, // 4
+ COMP5, // 5
+ COMP6, // 6
+ COMP7, // 7
+ COMP8, // 8
+
+ COMP9, // 9 PARIS 2
+ COMP10, // 10
+ COMP11, // 11
+ COMP12, // 12
+ COMP13, // 13
+ COMP14, // 14
+ COMP15, // 15
+ COMP16, // 16
+ COMP17, // 17
+ COMP18, // 18
+
+ COMP19, // 19 IRELAND
+ COMP20, // 20
+ COMP21, // 21
+ COMP22, // 22
+ COMP23, // 23
+ COMP24, // 24
+ COMP25, // 25
+ COMP26, // 26
+
+ COMP27, // 27 PARIS 3
+ COMP28, // 28
+ COMP29, // 29
+ COMP30, // 30 - Heart Monitor
+ COMP31, // 31
+ COMP32, // 32
+ COMP33, // 33
+ COMP34, // 34
+ COMP35, // 35
+
+ COMP36, // 36 PARIS 4
+ COMP37, // 37
+ COMP38, // 38
+ COMP39, // 39
+ COMP40, // 40
+ COMP41, // 41
+ COMP42, // 42
+ COMP43, // 43
+ 0, // 44
+
+ COMP45, // 45 SYRIA
+ COMP46, // 46 PARIS 4
+ COMP47, // 47
+ COMP48, // 48 PARIS 4
+ COMP49, // 49
+ COMP50, // 50
+ 0, // 51
+ 0, // 52
+ COMP53, // 53
+ COMP54, // 54
+ COMP55, // 55
+
+ COMP56, // 56 SPAIN
+ COMP57, // 57
+ COMP58, // 58
+ COMP59, // 59
+ COMP60, // 60
+ COMP61, // 61
+ COMP62, // 62
+
+ COMP63, // 63 NIGHT TRAIN
+ 0, // 64
+ COMP65, // 65
+ COMP66, // 66
+ COMP67, // 67
+ 0, // 68
+ COMP69, // 69
+ 0, // 70
+
+ COMP71, // 71 SCOTLAND
+ COMP72, // 72
+ COMP73, // 73
+ COMP74, // 74 END SEQUENCE IN SECRET_CRYPT
+ COMP75, // 75
+ COMP76, // 76
+ COMP77, // 77
+ COMP78, // 78
+ COMP79, // 79
+
+ COMP80, // 80 PARIS MAP
+
+ COMP81, // 81 Full-screen for "Asstair" in Paris2
+
+ COMP55, // 82 Full-screen BRITMAP in sc55 (Syrian Cave)
+ 0, // 83
+ 0, // 84
+ 0, // 85
+
+ COMP86, // 86 EUROPE MAP
+ COMP48, // 87 fudged in for normal window (sc48)
+ COMP48, // 88 fudged in for filtered window (sc48)
+ 0, // 89
+
+ COMP90, // 90 PHONE SCREEN
+ COMP91, // 91 ENVELOPE SCREEN
+ COMP17, // 92 fudged in for George close-up surprised in sc17 wardrobe
+ COMP17, // 93 fudged in for George close-up inquisitive in sc17 wardrobe
+ COMP29, // 94 fudged in for George close-up in sc29 sarcophagus
+ COMP38, // 95 fudged in for George close-up in sc29 sarcophagus
+ COMP42, // 96 fudged in for chalice close-up from sc42
+ 0, // 97
+ 0, // 98
+ COMP99, // 99 MESSAGE SCREEN (BLANK)
+
+ 0, // 100
+ 0, // 101
+ 0, // 102
+ 0, // 103
+ 0, // 104
+ 0, // 105
+ 0, // 106
+ 0, // 107
+ 0, // 108
+ 0, // 109
+
+ 0, // 110
+ 0, // 111
+ 0, // 112
+ 0, // 113
+ 0, // 114
+ 0, // 115
+ 0, // 116
+ 0, // 117
+ 0, // 118
+ 0, // 119
+
+ 0, // 120
+ 0, // 121
+ 0, // 122
+ 0, // 123
+ 0, // 124
+ 0, // 125
+ 0, // 126
+ 0, // 127
+
+//mega sections
+ MEGA_GEO, // 128 mega_one the player
+ MEGA_NICO, // 129 mega_two
+ MEGA_MUS, // 130
+ MEGA_BENOIR, // 131
+ 0, // 132
+ MEGA_ROSSO, // 133
+ MEGA_DUANE, // 134
+// james megas
+ 0, // 135
+ 0, // 136
+ 0, // 137
+ 0, // 138
+ 0, // 139
+ 0, // 140
+ 0, // 141
+ 0, // 142
+ 0, // 143
+
+// jeremy megas
+ 0, // 144 mega_phone
+ MEGA_MOUE, // 145 mega_moue
+ MEGA_ALBERT, // 146 mega_albert
+ 0, // 147
+ 0, // 148
+ TEXT_OBS, // 149
+};
+
+const uint32 ObjectMan::_textList[TOTAL_SECTIONS][7] = {
+ {ENGLISH0, FRENCH0, GERMAN0, ITALIAN0, SPANISH0, CZECH0, PORT0}, // 0 INVENTORY BOTH CD'S - used in almost all locations
+ {ENGLISH1, FRENCH1, GERMAN1, ITALIAN1, SPANISH1, CZECH1, PORT1}, // 1 PARIS 1 CD1
+ {ENGLISH2, FRENCH2, GERMAN2, ITALIAN2, SPANISH2, CZECH2, PORT2}, // 2 CD1
+ {ENGLISH3, FRENCH3, GERMAN3, ITALIAN3, SPANISH3, CZECH3, PORT3}, // 3 CD1
+ {ENGLISH4, FRENCH4, GERMAN4, ITALIAN4, SPANISH4, CZECH4, PORT4}, // 4 CD1
+ {ENGLISH5, FRENCH5, GERMAN5, ITALIAN5, SPANISH5, CZECH5, PORT5}, // 5 CD1
+ {ENGLISH6, FRENCH6, GERMAN6, ITALIAN6, SPANISH6, CZECH6, PORT6}, // 6 CD1
+ {ENGLISH7, FRENCH7, GERMAN7, ITALIAN7, SPANISH7, CZECH7, PORT7}, // 7 CD1
+ {0}, // 8 -
+ {ENGLISH9, FRENCH9, GERMAN9, ITALIAN9, SPANISH9, CZECH9, PORT9}, // 9 PARIS 2 CD1
+ {0}, // 10 -
+ {ENGLISH11, FRENCH11, GERMAN11, ITALIAN11, SPANISH11, CZECH11, PORT11}, // 11 CD1
+ {ENGLISH12, FRENCH12, GERMAN12, ITALIAN12, SPANISH12, CZECH12, PORT12}, // 12 CD1
+ {ENGLISH13, FRENCH13, GERMAN13, ITALIAN13, SPANISH13, CZECH13, PORT13}, // 13 CD1
+ {ENGLISH14, FRENCH14, GERMAN14, ITALIAN14, SPANISH14, CZECH14, PORT14}, // 14 CD1
+ {ENGLISH15, FRENCH15, GERMAN15, ITALIAN15, SPANISH15, CZECH15, PORT15}, // 15 CD1
+ {ENGLISH16, FRENCH16, GERMAN16, ITALIAN16, SPANISH16, CZECH16, PORT16}, // 16 CD1
+ {ENGLISH17, FRENCH17, GERMAN17, ITALIAN17, SPANISH17, CZECH17, PORT17}, // 17 CD1
+ {ENGLISH18, FRENCH18, GERMAN18, ITALIAN18, SPANISH18, CZECH18, PORT18}, // 18 CD1
+ {ENGLISH19, FRENCH19, GERMAN19, ITALIAN19, SPANISH19, CZECH19, PORT19}, // 19 IRELAND CD2
+ {ENGLISH20, FRENCH20, GERMAN20, ITALIAN20, SPANISH20, CZECH20, PORT20}, // 20 CD2
+ {ENGLISH21, FRENCH21, GERMAN21, ITALIAN21, SPANISH21, CZECH21, PORT21}, // 21 CD2
+ {ENGLISH22, FRENCH22, GERMAN22, ITALIAN22, SPANISH22, CZECH22, PORT22}, // 22 CD2
+ {ENGLISH23, FRENCH23, GERMAN23, ITALIAN23, SPANISH23, CZECH23, PORT23}, // 23 CD2
+ {ENGLISH24, FRENCH24, GERMAN24, ITALIAN24, SPANISH24, CZECH24, PORT24}, // 24 CD2
+ {ENGLISH25, FRENCH25, GERMAN25, ITALIAN25, SPANISH25, CZECH25, PORT25}, // 25 CD2
+ {0}, // 26 -
+ {ENGLISH27, FRENCH27, GERMAN27, ITALIAN27, SPANISH27, CZECH27, PORT27}, // 27 PARIS 3 CD1
+ {ENGLISH28, FRENCH28, GERMAN28, ITALIAN28, SPANISH28, CZECH28, PORT28}, // 28 CD1
+ {ENGLISH29, FRENCH29, GERMAN29, ITALIAN29, SPANISH29, CZECH29, PORT29}, // 29 CD1
+ {0}, // 30 -
+ {ENGLISH31, FRENCH31, GERMAN31, ITALIAN31, SPANISH31, CZECH31, PORT31}, // 31 CD1
+ {ENGLISH32, FRENCH32, GERMAN32, ITALIAN32, SPANISH32, CZECH32, PORT32}, // 32 CD1
+ {ENGLISH33, FRENCH33, GERMAN33, ITALIAN33, SPANISH33, CZECH33, PORT33}, // 33 CD1
+ {ENGLISH34, FRENCH34, GERMAN34, ITALIAN34, SPANISH34, CZECH34, PORT34}, // 34 CD1
+ {ENGLISH35, FRENCH35, GERMAN35, ITALIAN35, SPANISH35, CZECH35, PORT35}, // 35 CD1
+ {ENGLISH36, FRENCH36, GERMAN36, ITALIAN36, SPANISH36, CZECH36, PORT36}, // 36 PARIS 4 CD1
+ {ENGLISH37, FRENCH37, GERMAN37, ITALIAN37, SPANISH37, CZECH37, PORT37}, // 37 CD1
+ {ENGLISH38, FRENCH38, GERMAN38, ITALIAN38, SPANISH38, CZECH38, PORT38}, // 38 CD1
+ {ENGLISH39, FRENCH39, GERMAN39, ITALIAN39, SPANISH39, CZECH39, PORT39}, // 39 CD1
+ {ENGLISH40, FRENCH40, GERMAN40, ITALIAN40, SPANISH40, CZECH40, PORT40}, // 40 CD1
+ {ENGLISH41, FRENCH41, GERMAN41, ITALIAN41, SPANISH41, CZECH41, PORT41}, // 41 CD1
+ {ENGLISH42, FRENCH42, GERMAN42, ITALIAN42, SPANISH42, CZECH42, PORT42}, // 42 CD1
+ {ENGLISH43, FRENCH43, GERMAN43, ITALIAN43, SPANISH43, CZECH43, PORT43}, // 43 CD1
+ {0}, // 44 -
+ {ENGLISH45, FRENCH45, GERMAN45, ITALIAN45, SPANISH45, CZECH45, PORT45}, // 45 SYRIA CD2
+ {0}, // 46 (PARIS 4) -
+ {ENGLISH47, FRENCH47, GERMAN47, ITALIAN47, SPANISH47, CZECH47, PORT47}, // 47 CD2
+ {ENGLISH48, FRENCH48, GERMAN48, ITALIAN48, SPANISH48, CZECH48, PORT48}, // 48 (PARIS 4) CD1
+ {ENGLISH49, FRENCH49, GERMAN49, ITALIAN49, SPANISH49, CZECH49, PORT49}, // 49 CD2
+ {ENGLISH50, FRENCH50, GERMAN50, ITALIAN50, SPANISH50, CZECH50, PORT50}, // 50 CD2
+ {0}, // 51 -
+ {0}, // 52 -
+ {0}, // 53 -
+ {ENGLISH54, FRENCH54, GERMAN54, ITALIAN54, SPANISH54, CZECH54, PORT54}, // 54 CD2
+ {ENGLISH55, FRENCH55, GERMAN55, ITALIAN55, SPANISH55, CZECH55, PORT55}, // 55 CD2
+ {ENGLISH56, FRENCH56, GERMAN56, ITALIAN56, SPANISH56, CZECH56, PORT56}, // 56 SPAIN CD2
+ {ENGLISH57, FRENCH57, GERMAN57, ITALIAN57, SPANISH57, CZECH57, PORT57}, // 57 CD2
+ {ENGLISH58, FRENCH58, GERMAN58, ITALIAN58, SPANISH58, CZECH58, PORT58}, // 58 CD2
+ {ENGLISH59, FRENCH59, GERMAN59, ITALIAN59, SPANISH59, CZECH59, PORT59}, // 59 CD2
+ {ENGLISH60, FRENCH60, GERMAN60, ITALIAN60, SPANISH60, CZECH60, PORT60}, // 60 CD2
+ {ENGLISH61, FRENCH61, GERMAN61, ITALIAN61, SPANISH61, CZECH61, PORT61}, // 61 CD2
+ {0}, // 62 -
+ {ENGLISH63, FRENCH63, GERMAN63, ITALIAN63, SPANISH63, CZECH63, PORT63}, // 63 TRAIN CD2
+ {0}, // 64 -
+ {ENGLISH65, FRENCH65, GERMAN65, ITALIAN65, SPANISH65, CZECH65, PORT65}, // 65 CD2
+ {ENGLISH66, FRENCH66, GERMAN66, ITALIAN66, SPANISH66, CZECH66, PORT66}, // 66 CD2
+ {0}, // 67 -
+ {0}, // 68 -
+ {ENGLISH69, FRENCH69, GERMAN69, ITALIAN69, SPANISH69, CZECH69, PORT69}, // 69 CD2
+ {0}, // 70 -
+ {ENGLISH71, FRENCH71, GERMAN71, ITALIAN71, SPANISH71, CZECH71, PORT71}, // 71 SCOTLAND CD2
+ {ENGLISH72, FRENCH72, GERMAN72, ITALIAN72, SPANISH72, CZECH72, PORT72}, // 72 CD2
+ {ENGLISH73, FRENCH73, GERMAN73, ITALIAN73, SPANISH73, CZECH73, PORT73}, // 73 CD2
+ {ENGLISH74, FRENCH74, GERMAN74, ITALIAN74, SPANISH74, CZECH74, PORT74}, // 74 CD2
+ {0}, // 75 -
+ {0}, // 76 -
+ {0}, // 77 -
+ {0}, // 78 -
+ {0}, // 79 -
+ {0}, // 80 -
+ {0}, // 81 -
+ {0}, // 82 -
+ {0}, // 83 -
+ {0}, // 84 -
+ {0}, // 85 -
+ {0}, // 86 -
+ {0}, // 87 -
+ {0}, // 88 -
+ {0}, // 89 -
+ {ENGLISH90, FRENCH90, GERMAN90, ITALIAN90, SPANISH90, CZECH90, PORT90}, // 90 PHONE BOTH CD'S (NICO & TODRYK PHONE TEXT - can phone nico from a number of sections
+ {0}, // 91 -
+ {0}, // 92 -
+ {0}, // 93 -
+ {0}, // 94 -
+ {0}, // 95 -
+ {0}, // 96 -
+ {0}, // 97 -
+ {0}, // 98 -
+ {ENGLISH99, FRENCH99, GERMAN99, ITALIAN99, SPANISH99, CZECH99, PORT99}, // 99 MESSAGES BOTH CD'S - contains general text, most not requiring samples, but includes demo samples
+ {0}, // 100 -
+ {0}, // 101 -
+ {0}, // 102 -
+ {0}, // 103 -
+ {0}, // 104 -
+ {0}, // 105 -
+ {0}, // 106 -
+ {0}, // 107 -
+ {0}, // 108 -
+ {0}, // 109 -
+ {0}, // 110 -
+ {0}, // 111 -
+ {0}, // 112 -
+ {0}, // 113 -
+ {0}, // 114 -
+ {0}, // 115 -
+ {0}, // 116 -
+ {0}, // 117 -
+ {0}, // 118 -
+ {0}, // 119 -
+ {0}, // 120 -
+ {0}, // 121 -
+ {0}, // 122 -
+ {0}, // 123 -
+ {0}, // 124 -
+ {0}, // 125 -
+ {0}, // 126 -
+ {0}, // 127 -
+ {0}, // 128 -
+ {ENGLISH129, FRENCH129, GERMAN129, ITALIAN129, SPANISH129, CZECH129, PORT129}, // 129 NICO BOTH CD'S - used in screens 1,10,71,72,73
+ {0}, // 130 -
+ {ENGLISH131, FRENCH131, GERMAN131, ITALIAN131, SPANISH131, CZECH131, PORT131}, // 131 BENOIR CD1 - used in screens 31..35
+ {0}, // 132 -
+ {ENGLISH133, FRENCH133, GERMAN133, ITALIAN133, SPANISH133, CZECH133, PORT133}, // 133 ROSSO CD1 - used in screen 18
+ {0}, // 134 -
+ {0}, // 135 -
+ {0}, // 136 -
+ {0}, // 137 -
+ {0}, // 138 -
+ {0}, // 139 -
+ {0}, // 140 -
+ {0}, // 141 -
+ {0}, // 142 -
+ {0}, // 143 -
+ {0}, // 144 -
+ {ENGLISH145, FRENCH145, GERMAN145, ITALIAN145, SPANISH145, CZECH145, PORT145}, // 145 MOUE CD1 - used in screens 1 & 18
+ {ENGLISH146, FRENCH146, GERMAN146, ITALIAN146, SPANISH146, CZECH146, PORT146}, // 146 ALBERT CD1 - used in screens 4 & 5
+ {0}, // 147 -
+ {0}, // 148 -
+ {0}, // 149 -
+};
+
+
+RoomDef Screen::_roomDefTable[TOTAL_ROOMS] = { // these are NOT const
+ {
+ 0, //total_layers --- room 0 NOT USED
+ 0, //size_x = width
+ 0, //size_y = height
+ 0, //grid_width = width/16 + 16
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes { background palette [0..183], sprite palette [184..255] }
+ {0,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // PARIS 1
+
+ {
+ 3, //total_layers //room 1
+ 784, //size_x
+ 400, //size_y
+ 65, //grid_width
+ {room1_l0,room1_l1,room1_l2}, //layers
+ {room1_gd1,room1_gd2}, //grids
+ {room1_PAL,PARIS1_PAL}, //palettes
+ {room1_plx,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 2
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room2_l0,room2_l1,room2_l2,0}, //layers
+ {room2_gd1,room2_gd2,0}, //grids
+ {room2_PAL,PARIS1_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 3
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room3_l0,room3_l1,room3_l2,0}, //layers
+ {room3_gd1,room3_gd2,0}, //grids
+ {room3_PAL,PARIS1_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 4
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room4_l0,room4_l1,room4_l2,0}, //layers
+ {room4_gd1,room4_gd2,0}, //grids
+ {room4_PAL,PARIS1_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 5
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room5_l0,room5_l1,room5_l2,0}, //layers
+ {room5_gd1,room5_gd2,0}, //grids
+ {room5_PAL,PARIS1_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 6
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room6_l0,room6_l1,0,0}, //layers
+ {room6_gd1,0,0}, //grids
+ {room6_PAL,SEWER_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 7
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room7_l0,room7_l1,room7_l2,0}, //layers
+ {room7_gd1,room7_gd2,0}, //grids
+ {room7_PAL,SEWER_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 8
+ 784, //size_x
+ 400, //size_y
+ 65, //grid_width
+ {room8_l0,room8_l1,room8_l2,0}, //layers
+ {room8_gd1,room8_gd2,0}, //grids
+ {room8_PAL,PARIS1_PAL}, //palettes
+ {room8_plx,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // PARIS 2
+
+ {
+ 3, //total_layers //room 9
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room9_l0,room9_l1,room9_l2,0}, //layers
+ {room9_gd1,room9_gd2,0}, //grids
+ {room9_PAL,PARIS2_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 10
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room10_l0,room10_l1,0,0}, //layers
+ {room10_gd1,0,0}, //grids
+ {room10_PAL,R10SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 11
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room11_l0,room11_l1,room11_l2,0}, //layers
+ {room11_gd1,room11_gd2,0}, //grids
+ {room11_PAL,PARIS2_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 12
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room12_l0,room12_l1,0,0}, //layers
+ {room12_gd1,0,0}, //grids
+ {room12_PAL,PARIS2_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 13
+ 976, //size_x
+ 400, //size_y
+ 77, //grid_width
+ {room13_l0,room13_l1,room13_l2,0}, //layers
+ {room13_gd1,room13_gd2,0}, //grids
+ {room13_PAL,R13SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 14
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room14_l0,room14_l1,0,0}, //layers
+ {room14_gd1,0,0}, //grids
+ {room14_PAL,PARIS2_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 15
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room15_l0,room15_l1,room15_l2,0}, //layers
+ {room15_gd1,room15_gd2,0}, //grids
+ {room15_PAL,PARIS2_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 16
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R16L0,R16L1,0,0}, //layers
+ {R16G1,0,0}, //grids
+ {room16_PAL,PARIS2_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 17
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room17_l0,room17_l1,room17_l2,0}, //layers
+ {room17_gd1,room17_gd2,0}, //grids
+ {room17_PAL,PARIS2_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 18
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room18_l0,room18_l1,room18_l2,0}, //layers
+ {room18_gd1,room18_gd2,0}, //grids
+ {room18_PAL,R18SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // IRELAND
+
+ {
+ 3, //total_layers //room 19 - Ireland Street
+ 848, //size_x
+ 864, //size_y
+ 69, //grid_width
+ {R19L0,R19L1,R19L2,0}, //layers
+ {R19G1,R19G2,0}, //grids
+ {R19PAL,R19SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 4, //total_layers //room 20 - Macdevitts
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R20L0,R20L1,R20L2,R20L3}, //layers
+ {R20G1,R20G2,R20G3}, //grids
+ {R20PAL,R20SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 21 - Pub Cellar
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R21L0,R21L1,R21L2,0}, //layers
+ {R21G1,R21G2,0}, //grids
+ {R21PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 22 - Castle Gate
+ 784, //size_x
+ 400, //size_y
+ 65, //grid_width
+ {R22L0,R22L1,0,0}, //layers
+ {R22G1,0,0}, //grids
+ {R22PAL,R22SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 23 - Castle Hay Top
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R23L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R23PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 24 - Castle Yard
+ 880, //size_x
+ 400, //size_y
+ 71, //grid_width
+ {R24L0,R24L1,0,0}, //layers
+ {R24G1,0,0}, //grids
+ {R24PAL,SPRITE_PAL}, //palettes
+ {R24PLX,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 25 - Castle Dig
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R25L0,R25L1,0,0}, //layers
+ {R25G1,0,0}, //grids
+ {R25PAL,R25SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 26 - Cellar Dark
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R26L0,R26L1,R26L2,0}, //layers
+ {R26G1,R26G2,0}, //grids
+ {R26PAL,R26SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // PARIS 3
+
+ {
+ 3, //total_layers //room 27
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R27L0,R27L1,R27L2,0}, //layers
+ {R27G1,R27G2,0}, //grids
+ {room27_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 28
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R28L0,R28L1,R28L2,0}, //layers
+ {R28G1,R28G2,0}, //grids
+ {R28PAL,R28SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 29
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R29L0,R29L1,0,0}, //layers
+ {R29G1,0,0}, //grids
+ {R29PAL,R29SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 30 - for MONITOR seen while player in rm34
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {MONITOR,0,0,0}, //layers
+ {0,0,0}, //grids
+ {MONITOR_PAL,PARIS3_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 31
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room31_l0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {room31_PAL,PARIS3_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 32
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room32_l0,room32_l1,room32_l2,0}, //layers
+ {room32_gd1,room32_gd2,0}, //grids
+ {room32_PAL,PARIS3_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 33
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room33_l0,room33_l1,room33_l2,0}, //layers
+ {room33_gd1,room33_gd2,0}, //grids
+ {room33_PAL,PARIS3_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 4, //total_layers //room 34
+ 1120, //size_x
+ 400, //size_y
+ 86, //grid_width
+ {room34_l0,room34_l1,room34_l2,room34_l3}, //layers
+ {room34_gd1,room34_gd2,room34_gd3}, //grids
+ {room34_PAL,PARIS3_PAL}, //palettes
+ {R34PLX,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 35
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room35_l0,room35_l1,0}, //layers
+ {room35_gd1,0}, //grids
+ {room35_PAL,PARIS3_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // PARIS 4
+
+ {
+ 2, //total_layers //room 36
+ 960, //size_x
+ 400, //size_y
+ 76, //grid_width
+ {R36L0,R36L1,0,0}, //layers
+ {R36G1,0,0}, //grids
+ {room36_PAL,R36SPRPAL}, //palettes
+ {R36PLX,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 37
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R37L0,R37L1,0,0}, //layers
+ {R37G1,0,0}, //grids
+ {room37_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 38
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R38L0,R38L1,0,0}, //layers
+ {R38G1,0,0}, //grids
+ {room38_PAL,R38SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 39
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R39L0,R39L1,0,0}, //layers
+ {R39G1,0,0}, //grids
+ {room39_PAL,R39SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 40
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R40L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {room40_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 41
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R41L0,R41L1,0,0}, //layers
+ {R41G1,0,0}, //grids
+ {room41_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 42
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R42L0,R42L1,R42L2,0}, //layers
+ {R42G1,R42G2,0}, //grids
+ {room42_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 43
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R43L0,R43L1,0,0}, //layers
+ {R43G1,0,0}, //grids
+ {room43_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 44
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // SYRIA
+
+ {
+ 2, //total_layers //room 45 - Syria Stall
+ 1152, //size_x
+ 400, //size_y
+ 88, //grid_width
+ {R45L0,R45L1,0,0}, //layers
+ {R45G1,0,0}, //grids
+ {R45PAL,R45SPRPAL}, //palettes
+ {R45PLX,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 46 (Hotel Alley, Paris 2)
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room46_l0,room46_l1,room46_l2,0}, //layers
+ {room46_gd1,room46_gd2,0}, //grids
+ {room46_PAL,PARIS2_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 47 - Syria Carpet
+ 640, //size_x
+ 800, //size_y
+ 56, //grid_width
+ {R47L0,R47L1,R47L2,0}, //layers
+ {R47G1,R47G2,0}, //grids
+ {R47PAL,SYRIA_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 48 (Templar Church, Paris 4)
+ 1184, //size_x
+ 400, //size_y
+ 90, //grid_width
+ {R48L0,R48L1,R48L2,0}, //layers
+ {R48G1,R48G2,0}, //grids
+ {R48PAL,R48SPRPAL}, //palettes
+ {R48PLX,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 49 - Syria Club
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R49L0,R49L1,R49L2,0}, //layers
+ {R49G1,R49G2,0}, //grids
+ {R49PAL,SYRIA_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 4, //total_layers //room 50 - Syria Toilet
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R50L0,R50L1,R50L2,R50L3}, //layers
+ {R50G1,R50G2,R50G3}, //grids
+ {R50PAL,SYRIA_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 51 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 52 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 53 - Bull Head Pan
+ 880, //size_x
+ 1736, //size_y
+ 71, //grid_width
+ {R53L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R53PAL,R53SPRPAL}, //palettes
+ {FRONT53PLX,BACK53PLX}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 54 - Bull Head
+ 896, //size_x
+ 1112, //size_y
+ 72, //grid_width
+ {R54L0,R54L1,0,0}, //layers
+ {R54G1,0,0}, //grids
+ {R54PAL,SYRIA_PAL}, //palettes
+ {R54PLX,0}, //parallax layers - SPECIAL BACKGROUND PARALLAX - MUST GO IN FIRST SLOT
+ },
+ {
+ 1, //total_layers //room 55 - Bull Secret
+ 1040, //size_x
+ 400, //size_y
+ 81, //grid_width
+ {R55L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R55PAL,R55SPRPAL}, //palettes
+ {R55PLX,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // SPAIN
+
+ {
+ 3, //total_layers //room 56 - Countess' room
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R56L0,R56L1,R56L2,0}, //layers
+ {R56G1,R56G2,0}, //grids
+ {R56PAL,SPAIN_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 57 - Spain Drive
+ 1760, //size_x
+ 400, //size_y
+ 126, //grid_width
+ {R57L0,R57L1,0,0}, //layers
+ {R57G1,0,0}, //grids
+ {R57PAL,SPAIN_PAL}, //palettes
+ {R57PLX,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 58 - Mausoleum Exterior
+ 864, //size_x
+ 400, //size_y
+ 70, //grid_width
+ {R58L0,R58L1,0,0}, //layers
+ {R58G1,0,0}, //grids
+ {R58PAL,SPAIN_PAL}, //palettes
+ {R58PLX,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 59 - Mausoleum Interior
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R59L0,R59L1,R59L2,0}, //layers
+ {R59G1,R59G2,0}, //grids
+ {R59PAL,SPAIN_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 60 - Spain Reception
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R60L0,R60L1,R60L2,0}, //layers
+ {R60G1,R60G2,0}, //grids
+ {R60PAL,SPAIN_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 61 - Spain Well
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R61L0,R61L1,0,0}, //layers
+ {R61G1,0,0}, //grids
+ {R61PAL,SPAIN_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 62 - CHESS PUZZLE
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R62L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R62PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // NIGHT TRAIN
+
+ {
+ 2, //total_layers //room 63 - train_one
+ 2160, //size_x
+ 400, //size_y
+ 151, //grid_width
+ {R63L0,R63L1,0,0}, //layers
+ {R63G1,0,0}, //grids
+ {R63PAL,TRAIN_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 64 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 65 - compt_one
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R65L0,R65L1,0,0}, //layers
+ {R65G1,0,0}, //grids
+ {R65PAL,TRAIN_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 66 - compt_two
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R66L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R66PAL,TRAIN_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 67 - compt_three
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R67L0,R67L1,0,0}, //layers
+ {R67G1,0,0}, //grids
+ {R67PAL,TRAIN_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 68 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 69 - train_guard
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R69L0,R69L1,0,0}, //layers
+ {R69G1,0,0}, //grids
+ {R69PAL,R69SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 70 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // SCOTLAND
+
+ {
+ 2, //total_layers //room 71 - churchyard
+ 1760, //size_x
+ 400, //size_y
+ 126, //grid_width
+ {R71L0,R71L1,0,0}, //layers
+ {R71G1,0,0}, //grids
+ {R71PAL,SPRITE_PAL}, //palettes
+ {R71PLX,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 72 - church_tower
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R72L0,R72L1,0,0}, //layers
+ {R72G1,0,0}, //grids
+ {R72PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 3, //total_layers //room 73 - crypt
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R73L0,R73L1,R73L2,0}, //layers
+ {R73G1,R73G2,0}, //grids
+ {R73PAL,R73SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 2, //total_layers //room 74 - secret_crypt
+ 1136, //size_x
+ 400, //size_y
+ 87, //grid_width
+ {R74L0,R74L1,0,0}, //layers
+ {R74G1,0,0}, //grids
+ {R74PAL,ENDSPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 75 - secret_crypt
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R75L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R75PAL,ENDSPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 76 - secret_crypt
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R76L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R76PAL,ENDSPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 77 - secret_crypt
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R77L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R77PAL,ENDSPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 78 - secret_crypt
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R78L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R78PAL,ENDSPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 79 - secret_crypt
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R79L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R79PAL,ENDSPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+
+ //------------------------------------------------------------------------
+ // MAPS
+
+ {
+ 1, //total_layers //room 80 - paris map
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room80_l0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {room80_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 81 - for sequence of Assassin coming up stairs to rm17
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {ASSTAIR2,0,0,0}, //layers
+ {0,0,0}, //grids
+ {ASSTAIR2_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 82 - Map of Britain, viewed frrom sc55 (Syria Cave)
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {BRITMAP,0,0,0}, //layers
+ {0,0,0}, //grids
+ {BRITMAP_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 83 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 84 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 85 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 86 - europe map
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room86_l0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {room86_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 87 - normal window in sc48
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {WINDOW1,0,0,0}, //layers
+ {0,0,0}, //grids
+ {WINDOW1_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 88 - filtered window in sc48
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {WINDOW2,0,0,0}, //layers
+ {0,0,0}, //grids
+ {WINDOW2_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 89 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 90 - phone screen
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R90L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R90PAL,PHONE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 91 - envelope screen
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {R91L0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {R91PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 92 - for close up of George surprised in wardrobe in sc17
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {SBACK17,0,0,0}, //layers
+ {0,0,0}, //grids
+ {SBACK17PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 93 - for close up of George inquisitive in wardrobe in sc17
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {IBACK17,0,0,0}, //layers
+ {0,0,0}, //grids
+ {IBACK17PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 94 - for close up of George in sarcophagus in sc29
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {BBACK29,0,0,0}, //layers
+ {0,0,0}, //grids
+ {BBACK29PAL,BBACK29SPRPAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 95 - for close up of George during templar meeting, in sc38
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {BBACK38,0,0,0}, //layers
+ {0,0,0}, //grids
+ {BBACK38PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 96 - close up of chalice projection
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {CHALICE42,0,0,0}, //layers
+ {0,0,0}, //grids
+ {CHALICE42_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 97 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 0, //total_layers //room 98 - NOT USED
+ 0, //size_x
+ 0, //size_y
+ 0, //grid_width
+ {0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {0,0}, //palettes
+ {0,0}, //parallax layers
+ },
+ {
+ 1, //total_layers //room 99 - blank screen
+ 640, //size_x
+ 400, //size_y
+ 56, //grid_width
+ {room99_l0,0,0,0}, //layers
+ {0,0,0}, //grids
+ {room99_PAL,SPRITE_PAL}, //palettes
+ {0,0}, //parallax layers
+ }
+};
+
+const char Music::_tuneList[TOTAL_TUNES][8] = {
+ "", // 0 SPARE
+ "1m2", // DONE 1 George picks up the newspaper
+ "1m3", // DONE 2 In the alley for the first time
+ "1m4", // DONE 3 Alleycat surprises George
+ "1m6", // DONE 4 George fails to remove manhole cover. Even numbered attempts
+ "1m7", // !!!! 5 George fails to remove manhole cover. Odd numbered attempts
+ "1m8", // DONE 6 George leaves alley
+ "1m9", // DONE 7 George enters cafe for the first time
+ "1m10", // DONE 8 Waitress
+ "1m11", // DONE 9 Lying doctor
+
+ "1m12", // DONE 10 Truthful George
+ "1m13", // DONE 11 Yes, drink brandy
+ "1m14", // DONE 12 Yes, he's dead (Maybe 1m14a)
+ "1m15", // DONE 13 From, "...clown entered?"
+ "1m16", // DONE 14 From, "How did the old man behave?"
+ "1m17", // DONE 15 Salah-eh-Din
+ "1m18", // DONE 16 From, "Stay here, mademoiselle"
+ "1m19", // DONE 17 Leaving the cafe
+ "1m20", // DONE 18 Stick-up on Moue's gun
+ "1m21", // DONE 19 From, "Stop that, monsieur"
+
+ "1m22", // DONE 20 From, "If you can"
+ "1m23", // DONE 21 From, "Yeah,...clown"
+ "1m24", // DONE 22 From, he claimed to be a doctor
+ "1m25", // DONE 23 First time George meets Nico
+ "1m26", // DONE 24 From, "Oh God, him again." (Read notes on list)
+ "1m27", // DONE 25 From, "He's inside"
+ "1m28", // DONE 26 From, "You speak very good English"
+ "1m29", // DONE 27 From, "Why won't you tell me about this clown?"
+ "1m28a", // DONE 28 Costumed killers from, "How did Plantard get your name?"
+ "1m30", // DONE 29 From, "I really did see the clown" when talking to Moue at cafe doorway
+
+ "1m31", // DONE 30 From, "I found this (paper) in the street" (talking to Moue)
+ "1m32", // DONE 31 From, "What's the difference?"
+ "1m34", // DONE 32 Roadworker "Did you see a clown?"
+ "1m35", // DONE 33 Worker re: explosion, "I guess not"
+ "2m1", // DONE 34 From, "What about the waitress?"
+ "2m2", // DONE 35 From, "Did you see the old guy with the briefcase?"
+ "2m4", // DONE 36 "Would you like to read my newspaper?" (2M3 is at position 144)
+ "2m5", // DONE 37 From, "Ah, what's this Saleh-eh-Din?"
+ "2m6", // DONE 38 From, "It was a battered old tool box".
+ "2m7", // DONE 39 George "borrows" the lifting key
+
+ "2m8", // DONE 40 From 'phone page. Call Nico
+ "2m9", // DONE 41 Leaving the workman's tent
+ "2m10", // DONE 42 Use lifting keys on manhole
+ "2m11", // DONE 43 Into sewer no.1 from George on his knees (Alternative: 2m12)
+ "2m12", // DONE 44 Into sewer (alternative to 2m11)
+ "2m13", // DONE 45 George bends to pick up the red nose
+ "2m14", // DONE 46 Paper tissue, "It was a soggy..."
+ "2m15", // DONE 47 Cloth, as George picks it up. (Alternative: 2m16)
+ "2m16", // !!!! 48 Alternative cloth music
+ "2m17", // DONE 49 George climbs out of sewer.
+
+ "2m18", // DONE 50 From, "The man I chased..."
+ "2m19", // DONE 51 Spooky music for sewers.
+ "2m20", // DONE 52 "She isn't hurt, is she?"
+ "2m21", // DONE 53 Click on material icon
+ "2m22", // DONE 54 Spooky music for sewers.
+ "2m23", // DONE 55 From, "So you don't want to hear my experiences in the desert?"
+ "2m24", // DONE 56 On the material icon with Albert (suit icon instead, because material icon done)
+ "2m25", // DONE 57 After "What was on the label?" i.e. the 'phone number.
+ "2m26", // DONE 58 Leaving yard, after, "I hope you catch that killer soon." Also for the Musee Crune icon on the map (5M7).
+ "2m27", // DONE 59 As George starts to 'phone Todryk. (Repeated every time he calls him). Also, when the aeroport is clicked on (5M21).
+
+ "2m28", // DONE 60 Todryk conversation after, "Truth and justice"
+ "2m29", // DONE 61 'Phoning Nico from the roadworks. Also, 'phoning her from Ireland, ideally looping and fading on finish (6M10).
+ "2m30", // DONE 62 First time on Paris map
+ "2m31", // DONE 63 Click on Rue d'Alfred Jarry
+ "2m32", // DONE 64 From, "Will you tell me my fortune?"
+ "2m33", // DONE 65 After "Can you really tell my future?"
+ "1m28", // DONE 66 "What about the tall yellow ones?" Copy from 1M28.
+ "2m24", // DONE 67 Material Icon. Copy from 2M24
+ "2m6", // DONE 68 Exit icon on "See you later". Copy from 2M6.
+ "1m25", // DONE 69 On opening the front foor to Nico's. Copy from 1M25. .
+
+ "2m38", // DONE 70 Victim 1: From, "Tell me more about the clown's previous victims."
+ "2m39", // DONE 71 Victim 2: After, "What about the clown's second victim?"
+ "2m40", // DONE 72 Victim 3: On clown icon for 3rd victim.
+ "3m1", // DONE 73 George passes Nico the nose.
+ "3m2", // DONE 74 With Nico. From, "I found a piece of material..."
+ "3m3", // DONE 75 After George says, "... or clowns?"
+ "3m4", // DONE 76 After, "Did you live with your father?"
+ "1m28", // DONE 77 After, "Do you have a boyfriend?". Copy from 1M28.
+ "2m26", // DONE 78 After, "Good idea" (about going to costumier's). Copy from 2M26.
+ "3m7", // DONE 79 On costumier's icon on map.
+
+ "3m8", // DONE 80 Costumier's, after, "Come in, welcome."
+ "3m9", // DONE 81 On entering costumier's on later visits
+ "3m10", // DONE 82 After, "A description, perhaps."
+ "2m13", // DONE 83 Red nose icon at costumier's. Copy 2M13.
+ "3m12", // DONE 84 Tissue icon. Also, after Nico's "No, I write it (the magazine) 5M19.
+ "3m13", // DONE 85 Photo icon over, "Do you recognise this man?"
+ "3m14", // DONE 86 Exit icon, over, "Thanks for your help, buddy."
+ "2m9", // DONE 87 Clicking on police station on the map.
+ "3m17", // DONE 88 Police station on, "I've tracked down the clown's movements."
+ "3m18", // DONE 89 "One moment, m'sieur," as Moue turns.
+
+ "3m19", // DONE 90 G. on Rosso. "If he was trying to impress me..."
+ "3m20", // DONE 91 G. thinks, "He looked at me as if I'd farted."
+ "3m21", // DONE 92 Over Rosso's, "I've washed my hands of the whole affair."
+ "3m22", // DONE 93 Played over, "So long, inspector."
+ "3m24", // DONE 94 Conversation with Todrk, "He bought a suit from you, remember?"
+ "3m26", // !!!! 95 This piece is a problem. Don't worry about it for present.
+ "3m27", // DONE 96 George to Nico (in the flat): "Have you found out anymore?" [about the murders? or about the templars? JEL]
+ "2m26", // DONE 97 After, "Don't worry, I will." on leaving Nico's.
+ "3m29", // DONE 98 Ubu icon on the map.
+ "3m30", // DONE 99 G and Flap. After, "I love the clowns. Don't you?" AND "after "Not if you see me first" (3M31)
+
+ "3m32", // DONE 100 Source music for Lady Piermont.
+ "3m33", // DONE 101 More music for Lady P.
+ "2m13", // DONE 102 Red Nose music Copy 2M13
+ "4m3", // DONE 103 On photo, "Do you recognise the man in this photograph"
+ "4m4", // DONE 104 With Lady P. After, "Hi there, ma'am."
+ "4m5", // DONE 105 After, "I think the word you're looking for is...dick"
+ "4m6", // DONE 106 After, "English arrogance might do the trick." Also for "More English arrogance" (4M27)
+ "4m8", // !!!! 107 As George grabs key.
+ "4m9", // DONE 108 Room 21, on "Maybe it wasn't the right room"
+ "4m10", // DONE 109 On coming into 21 on subsequent occasions.
+
+ "4m11", // DONE 110 As George steps upto window.
+ "4m12", // DONE 111 Alternative times he steps up to the window.
+ "4m13", // DONE 112 In Moerlin's room
+ "4m14", // DONE 113 Sees "Moerlin" on the Stairs
+ "4m15", // DONE 114 George closing wardrobe door aftre Moerlin's gone.
+ "4m17", // DONE 115 After, "take your mind off the inquest"
+ "4m18", // DONE 116 "It was just as I'd imagined."
+ "4m19", // DONE 117 Show photo to Lady P
+ "4m20", // DONE 118 Lady P is "shocked" after the name "Khan".
+ "4m21", // DONE 119 After, "A bundle of papers, perhaps".
+
+ "4m22", // DONE 120 After, "Plantard's briefcase"
+ "4m24", // DONE 121 On fade to black as George leaves the hotel (prior to being searched)
+ "4m25", // DONE 122 After, "I break your fingers"
+ "4m28", // DONE 123 After clerk says, "Voila, m'sieur. Le manuscript..."
+ "4m29", // DONE 124 Onto the window sill after getting the manuscript.
+ "4m31", // DONE 125 Searched after he's dumped the manuscript in the alleyway.
+ "4m32", // DONE 126 Recovering the manuscript in the alley, "If the manuscript was..."
+ "5m1", // DONE 127 The manuscript, just after, "It's worth enough to kill for."
+ "5m2", // !SMK 128 The Templars after, "...over 800 years ago."
+ "5m3", // DONE 129 After, "Let's take another look at that manuscript"
+
+ "5m4", // DONE 130 On "Knight with a crystal ball" icon
+ "5m5", // DONE 131 On Nico's, "Patience"
+ "5m6", // DONE 132 After "I'm sure it will come in useful" when George leaves. Also, George leaving Nico after, "Keep me informed if you find anything new" (5M20). + "just take care of yourself"
+ "5m8", // DONE 133 Entering the museum for the first time on the fade.
+ "5m9", // DONE 134 George with guard after, "park their cars." Guard saying "No, no, no"
+ "5m10", // DONE 135 Incidental looking around the museum music. + fading from map to museum street, when lobineau is in museum
+ "5m11", // DONE 136 From "In the case was a spindly tripod, blackened with age and pitted with rust...". George answers Tripod ((?)That's what the cue list says). Also 5M15 and 5M16)
+ "5m12", // DONE 137 More looking around music.
+ "5m13", // DONE 138 Opening the mummy case.
+ "5m14", // DONE 139 High above me was a window
+
+ "5m17", // DONE 140 "As I reached toward the display case" (5M18 is in slot 165)
+ "5m22", // !SMK 141 From Ireland on the Europe map.
+ "5m23", // !!!! 142 IN front of the pub, searching.
+ "5m24", // DONE 143 Cheeky Maguire, "Wait 'til I get back"
+ "2m3", // DONE 144 Before, "Did anybody at the village work at the dig?" Loop and fade.
+ "6m1", // DONE 145 After, "You know something ... not telling me, don't you?"
+ "6m2", // DONE 146 On, "Mister, I seen it with my own eyes."
+ "6m3", // DONE 147 After, "Did you get to see the ghost" + On George's, "As soon as I saw the flickering torches..." in SCR_SC73.txt.
+ "6m4", // DONE 148 "the bloody place is haunted", just after G's "rational explanation... the castle"
+ "6m5", // DONE 149 Pub fiddler 1. Please programme stops between numbers - about 20" and a longer one every four or five minutes.
+
+ "6m6", // DONE 150 Pub fiddler 2.
+ "6m7", // DONE 151 Pub fiddler 3.
+ "6m8", // DONE 152 Pub fiddler 4.
+ "6m12", // DONE 153 Exit pub (as door opens). Copy from 2M6.
+ "2m6", // DONE 154 Going to the castle, start on the path blackout.
+ "5m1", // DONE 155 On, "Where was the Templar preceptory?" Copy 5M1
+ "6m15", // DONE 156 "On, "Do you mind if I climb up your hay stack..."
+ "7m1", // DONE 157 On plastic box, "It was a featureless..."
+ "7m2", // DONE 158 On tapping the plastic box with the sewer key
+ "7m4", // !!!! 159 "Shame on you, Patrick!" Fitzgerald was at the dig
+
+ "7m5", // !!!! 160 On the icon that leads to, "Maguire says that he saw you at the dig"
+ "7m6", // !!!! 161 On "The man from Paris"
+ "7m7", // !!!! 162 On, "I wish I'd never heard of..."
+ "7m8", // DONE 163 Exit pub
+ "7m11", // DONE 164 George picks up gem.
+ "7m14", // DONE 165 On George's icon, "the driver of the Ferrari..."
+ "7m15", // DONE 166 After George, "His name is Sean Fitzgerald"
+ "5m18", // DONE 167 Leaving museum after discovering tripod.
+ "6m11", // !!!! 168 With Fitz. On G's, "Did you work at...?". This is triggered here and on each subsequent question, fading at the end.
+ "7m17", // DONE 169 "You don't have to demolish the haystack"
+
+ "7m18", // DONE 170 George begins to climb the haystack.
+ "7m19", // DONE 171 Alternative climbing haystack music. These two tracks can be rotated with an ascent with FX only).
+ "7m20", // DONE 172 Attempting to get over the wall.
+ "7m21", // DONE 173 Descending the haystack
+ "7m22", // !!!! 174 Useful general purpose walking about music.
+ "7m23", // DONE 175 "Plastic cover" The exposed box, LB and RB.
+ "7m28", // !!!! 176 "No return"
+ "7m30", // !!!! 177 Picking up drink music (This will definitely clash with the fiddle music. We'll use it for something else). *
+ "7m31", // !!!! 178 Showing the landlord the electrician's ID.
+ "7m32", // !!!! 179 Stealing the wire (Probable clash again) *
+
+ "7m33", // DONE 180 On fade to black before going down into dark cellar.
+ "7m34", // DONE 181 On opening the grate, as George says, "I lifted the..." Khan's entrance.
+ "8m1", // DONE 182 Going down into the light cellar, starting as he goes through bar door.
+ "8m2", // DONE 183 General cellar music on, "It was an empty carton".
+ "8m4", // !!!! 184 Trying to get the bar towel. On, "The man's arm lay across..." *
+ "8m7", // DONE 185 Squeeze towel into drain. On, "Silly boy..."
+ "8m10", // DONE 186 Entering the castle as he places his foot on the tool embedded into the wall.
+ "8m11", // DONE 187 On, "Hey, billy." Goat confrontation music.
+ "8m12", // DONE 188 First butt from goat at moment of impact.
+ "8m13", // DONE 189 On examining the plough.
+
+ "8m14", // DONE 190 Second butt from goat.
+ "8m15", // DONE 191 Third butt from goat.
+ "8m16", // DONE 192 All subsequent butts, alternating with no music.
+ "8m18", // DONE 193 Poking around in the excavation music. I'd trigger it as he starts to descend the ladder into the dig.
+ "8m19", // DONE 194 "There was a pattern..." The five holes.
+ "8m20", // DONE 195 George actually touches the stone. Cooling carving (?)
+ "8m21", // DONE 196 "As I swung the stone upright" coming in on "Upright"
+ "8m22", // DONE 197 "The sack contained"
+ "8m24", // DONE 198 Down wall. As screen goes black. George over wall to haystack.
+ "8m26", // DONE 199 Wetting the towel
+
+ "8m28", // DONE 200 Wetting plaster. As George reaches for the towel prior to wringing water onto the plaster.
+ "8m29", // DONE 201 Mould in "The hardened plaster..."
+ "8m30", // DONE 202 Entering castle. As George steps in.
+ "8m31", // DONE 203 After George, "Hardly - he was dead." in nico_scr.txt
+ "8m37", // !!!! 204 Talking to Lobineau about the Templars. 5M2Keep looping and fade at the end.
+ // The problem is that it's an enormous sample and will have to be reduced in volume.
+ // I suggest forgetting about this one for the time being.
+ // If there's room when the rest of the game's in, then I'll re-record it more quietly and call it 8M37, okay?
+ "8m38", // DONE 205 "A female friend"
+ "8m39", // DONE 206 "Public toilet"
+ "8m40", // DONE 207 When George asks, "Where was the site at Montfaucon?" (to Lobineau, I suppose)
+ "8m41", // DONE 208 On matchbox icon. "Does this matchbook mean anything to you?"
+ "9m1", // DONE 209 On George, "It was the king of France" in ross_scr.txt
+
+ "9m2", // DONE 210 George, "Why do you get wound up...?" in ross_scr.txt
+ "9m3", // DONE 211 Ever heard of a guy called Marquet? Jacques Marquet?
+ "9m5", // DONE 212 On fade up at the hospital drive
+ "9m6", // DONE 213 On fade up inside the hospital
+ "9m7", // DONE 214 With Eva talking about Marquet. Before, "I'm conducting a private investigation."
+ "9m8", // DONE 215 With Eva, showing her the ID card.
+ "9m9", // DONE 216 With Eva, second NURSE click, "If nurse Grendel is that bad..."
+ "9m10", // DONE 217 Saying goodbye to Eva on the conversation where he discovers Marquet's location + on fade up on Sam's screen after being kicked off the ward
+ "9m11", // DONE 218 Talking to Sam. On, "Oh - hiya!" + first click on MR_SHINY
+ "9m13", // DONE 219 When George drinks from the cooler.
+
+ "9m14", // DONE 220 To Grendel, third MARQUET click. On "Do you know who paid for Marquet's room?"
+ "9m15", // DONE 221 To Grendel on first CLOWN click, "Do you have any clowns on the ward?"
+ "9m17", // DONE 222 When George pulls Shiny's plug the first time, on "As I tugged the plug..."
+ "9m18", // DONE 223 On subsequent plug tuggings if George has failed to get the white coat.
+ "9m19", // DONE 224 With the consultant, on "Excuse me, sir..."
+ "9m20", // DONE 225 Talking to Grendel. Launch immediately after she gives him the long metal box and "a stunning smile"
+ "9m21", // DONE 226 On Eric's, "Doctor!" when George is trying to get by for the first time, i.e. ward_stop_flag==0.
+ "9m22", // DONE 227 On Eric's, "Oh, Doctor!" when George is trying to get by for the second time, i.e. ward_stop_flag==1.
+ "9m23", // DONE 228 On Eric's, "You haven't finished taking my blood pressure!" when George is trying to get by for the third+ time, i.e. ward_stop_flag>1.
+ "9m24", // DONE 229 Giving the pressure gauge to Benoir, on, "Here, take this pressure gauge."
+
+ "9m25", // DONE 230 With Benoir, suggesting he use the gauge on the nurse. On, "Use it on Nurse Grendel."
+ "10m1", // DONE 231 Immediately after Marquet's, "Well, what are you waiting for? Get it over with!"
+ "10m2", // DONE 232 When George pulls open the sarcophagus lid prior to his successful hiding before the raid.
+ "10m3", // DONE 233 On fade to black as George spies on the Neo-Templars.
+ "10m4", // DONE 234 On second peer through the hole at the "Templars"
+ "11m1", // DONE 235 On clicking on the Marib button.
+ "11m3", // DONE 236 Loop in the Club Alamut, alternating with...
+ "11m4", // DONE 237 Loop in the Club Alamut.
+ "11m7", // DONE 238 When the door in the Bull's Mouth closes on George.
+ "11m8", // DONE 239 When the door opens to reveal Khan, immediately after, "You!" in KHAN_55.TXT.
+
+ "11m9", // !SMK 240 Over the "Going to the Bull's Head" smacker. Probably.
+ "12m1", // DONE 241 Clicking on the Spain icon from the aeroport. (AFTER CHANGING CD!)
+ "11m2", // DONE 242 Loop in the marketplace of Marib.
+ "spm2", // DONE 243 On fade up in the Countess' room for the first time.
+ "spm3", // DONE 244 At the end of VAS1SC56, triggered immediately before the Countess says, "Senor Stobbart, if I find that you are wasting my time..."
+ "spm4", // DONE 245 Immediately before Lopez enters the mausoleum with the chess set.
+ "spm5", // DONE 246 (This is actually 5m2 copied for CD2) Played through the chess puzzle. Ideally, when it finsishes, give it a couple of seconds and then launch 12m1. When that finishes, a couple of seconds and then back to this and so on and so forth.
+ "spm6", // DONE 247 On fade up from completing the chess puzzle. The climax is now "spm6b"
+ "scm1", // DONE 248 This is used whenever George goes out of a carriage and onto the corridor.
+ "scm2", // DONE 249 As George climbs out of the train window.
+
+ "scm3", // DONE 250 As George lands inside the guard's van.
+ "scm4", // DONE 251 On Khan's death speech, "A noble foe..."
+ "scm5", // DONE 252 George to Khan. On, "You're talking in riddles!"
+ "scm6", // DONE 253 Before, "He's dead"
+ "scm7", // DONE 254 Kissing Nico. After, "Where do you think you're going?"
+ "scm8", // DONE 255 In the churchyard after Nico's, "I rather hope it did"
+ "scm11", // DONE 256 Click on the opened secret door.
+ "rm3a", // DONE 257 Immediately they fade up in the great cave.
+ "rm3b", // DONE 258 The scene change immediately after Eklund says, "If you wish to live much longer..."
+ "scm16", // DONE 259 The big end sequence from when the torch hits the gunpowder. Cross fade with the shortened version that fits on the Smacker.
+
+ "scm1b", // DONE 260 When George passes the trigger point toward the back of the train and he sees Guido.
+ "spm6b", // DONE 261 The climax of "spm6", which should coincide with the Countess holding up the chalice.
+ "marquet", // DONE 262 Starts at the fade down when George is asked to leave Jacques' room
+ "rm4", // DONE 263 "On crystal stand icon. As George walks to the centre of the cavern." I'd do this on the first LMB on the stump.
+ "rm5", // DONE 264 "On icon. As George places the crystal on top of the stand." When the player places the gem luggage on the emplaced tripod.
+ "rm6", // DONE 265 "Chalice reflection. On icon as George places Chalice on floor of Church" i.e. the mosaic in the Baphomet dig. It's over thirty seconds long so it had best start running when the chalice luggage is placed on the mosaic so it runs through the big screen of the reflection.
+ "rm7", // DONE 266 "Burning candle. On icon as George sets about lighting candle." One minute forty-eight, this one. I've no idea how long the burning candle Smacker is but the cue description seems to indicate it should run from the moment the burning tissue successfully lights the candle, i.e. the window is shut.
+ "rm8", // DONE 267 "Down well. George descends into circus trap well." I think the circus reference refers to the lion. Run it from the moment that George gets off the rope and has a look around. Run it once only.
+
+ "rm3c", // DONE 268 On the scene change to the Grand Master standing between the pillars as the earth power whomps him.
+ "rm3d", // DONE 269 ONe the scene change after the Grand Master says, "George, we have watched you..." This one might need a bit of fiddling to get it to match to the fisticuffs.
+};
+
+#ifdef PALMOS_68K
+const FxDef *Sound::_fxList;
+#else
+const FxDef Sound::_fxList[312] = {
+ // 0
+ {
+ 0, // sampleId
+ 0, // type (FX_LOOP, FX_RANDOM or FX_SPOT)
+ 0, // delay (random chance for FX_RANDOM sound fx)
+ { // roomVolList
+ {0,0,0}, // {roomNo,leftVol,rightVol}
+ },
+ },
+ //------------------------
+ // 1 Newton's cradle. Anim=NEWTON.
+ {
+ FX_NEWTON, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance)
+ { // roomVolList
+ {45,4,2}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 2
+ {
+ FX_TRAFFIC2, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {1,12,12}, // {roomNo,leftVol,rightVol}
+ {2,1,1},
+ {3,1,1},
+ {4,13,13},
+ {5,1,1},
+ {8,7,7},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 3
+ {
+ FX_HORN1, // sampleId
+ FX_RANDOM, // type
+ 1200, // delay (or random chance)
+ { // roomVolList
+ {1,3,3}, // {roomNo,leftVol,rightVol}
+ {3,1,1},
+ {4,1,1},
+ {5,2,2},
+ {8,4,4},
+ {18,2,3},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 4
+ {
+ FX_HORN2, // sampleId
+ FX_RANDOM, // type
+ 1200, // delay (or random chance)
+ { // roomVolList
+ {1,4,4}, // {roomNo,leftVol,rightVol}
+ {3,2,2},
+ {4,3,3},
+ {5,2,2},
+ {8,4,4},
+ {18,1,1},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 5
+ {
+ FX_HORN3, // sampleId
+ FX_RANDOM, // type
+ 1200, // delay (or random chance)
+ { // roomVolList
+ {1,4,4}, // {roomNo,leftVol,rightVol}
+ {2,4,4},
+ {3,2,2},
+ {4,3,3},
+ {5,2,2},
+ {8,4,4},
+ {18,1,1},
+ },
+ },
+ //------------------------
+ // 6
+ {
+ FX_CAMERA1, // sampleId
+ FX_SPOT, // type
+ 25, // delay (or random chance)
+ { // roomVolList
+ {1,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 7
+ {
+ FX_CAMERA2, // sampleId
+ FX_SPOT, // type
+ 25, // delay (or random chance)
+ { // roomVolList
+ {1,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 8
+ {
+ FX_CAMERA3, // sampleId
+ FX_SPOT, // type
+ 25, // delay (or random chance)
+ { // roomVolList
+ {1,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 9
+ {
+ FX_SWATER1, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {7,12,12}, // {roomNo,leftVol,rightVol}
+ {6,12,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 10 Mad dogs in Spain, triggered by George going around the corner in the villa hall.
+ // In 56 and 57, the dogs will continue barking after George has either been ejected or sneaked up stairs
+ // for a couple of loops before stopping.
+ {
+ FX_DOGS56, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {60,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0} // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 11
+ {
+ FX_DRIP1, // sampleId
+ FX_RANDOM, // type
+ 20, // delay (or random chance)
+ { // roomVolList
+ {7,15,15}, // {roomNo,leftVol,rightVol}
+ {6,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 12
+ {
+ FX_DRIP2, // sampleId
+ FX_RANDOM, // type
+ 30, // delay (or random chance)
+ { // roomVolList
+ {7,15,15}, // {roomNo,leftVol,rightVol}
+ {6,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 13
+ {
+ FX_DRIP3, // sampleId
+ FX_RANDOM, // type
+ 40, // delay (or random chance)
+ { // roomVolList
+ {7,15,15}, // {roomNo,leftVol,rightVol}
+ {6,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 14
+ {
+ FX_TWEET1, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {1,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 15
+ {
+ FX_TWEET2, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {1,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 16
+ {
+ FX_TWEET3, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {1,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 17
+ {
+ FX_TWEET4, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {1,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 18
+ {
+ FX_TWEET5, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {1,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 19 Tied to large bird flying up screen anim
+ {
+ FX_CAW1, // sampleId
+ FX_SPOT, // type
+ 20, // delay (or random chance)
+ { // roomVolList
+ {1,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 20 George picking the canopy up: GEOCAN
+ {
+ FX_CANUP, // sampleId
+ FX_SPOT, // type
+ 5, // delay (or random chance) *
+ { // roomVolList
+ {1,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 21 George dropping the canopy: GEOCAN
+ {
+ FX_CANDO, // sampleId
+ FX_SPOT, // type
+ 52, // delay (or random chance) *
+ { // roomVolList
+ {1,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 22 George dusts himself down: GEOCAN
+ {
+ FX_DUST, // sampleId
+ FX_SPOT, // type
+ 58, // delay (or random chance) *
+ { // roomVolList
+ {1,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 23 George picks up the paper and opens it: GEOPAP
+ {
+ FX_PAP1, // sampleId
+ FX_SPOT, // type
+ 23, // delay (or random chance) *
+ { // roomVolList
+ {1,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 24 George puts the paper away: GEOPAP2
+ {
+ FX_PAP2, // sampleId
+ FX_SPOT, // type
+ 3, // delay (or random chance) *
+ { // roomVolList
+ {1,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 25 George gives the paper away: GEOWRK8
+ {
+ FX_PAP3, // sampleId
+ FX_SPOT, // type
+ 13, // delay (or random chance) *
+ { // roomVolList
+ {4,14,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 26 Workman examines paper: WRKOPN - it's now just WRKPPR
+ {
+ FX_PAP4, // sampleId
+ FX_SPOT, // type
+ 15, // delay (or random chance) *
+ { // roomVolList
+ {4,14,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 27 Workman puts paper down: WRKOPN (REVERSED) - now just WRKCLM
+ {
+ FX_PAP5, // sampleId
+ FX_SPOT, // type
+ 2, // delay (or random chance)*
+ { // roomVolList
+ {4,14,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 28 Pickaxe sound 1:, Screen 4 - WRKDIG
+ {
+ FX_PICK1, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {4,10,10},
+ {0,0,0} // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 29 Pickaxe sound 2:, Screen 4 - WRKDIG
+ {
+ FX_PICK2, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {4,10,10},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 30 Pickaxe sound 3:, Screen 4 - WRKDIG
+ {
+ FX_PICK3, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {4,10,10},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 31 Pickaxe sound 4:, Screen 4 - WRKDIG
+ {
+ FX_PICK4, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {4,10,10},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 32 Shorting light: FLICKER
+ {
+ FX_LIGHT, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {3,15,15}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 33 Cat leaps out of bin and runs: CATJMP!
+ {
+ FX_CAT, // sampleId
+ FX_SPOT, // type
+ 20, // delay (or random chance) *
+ { // roomVolList
+ {2,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 34 George rocks plastic crate: GEOCRT
+ {
+ FX_CRATE, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance) *
+ { // roomVolList
+ {2,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 35 George tries to climb drainpipe: GEOCLM02
+ {
+ FX_DRAIN, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {2,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 36 George removes manhole cover: GEOMAN8
+ {
+ FX_HOLE, // sampleId
+ FX_SPOT, // type
+ 19, // delay (or random chance) ?
+ { // roomVolList
+ {2,12,11}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 37 Brandy bottle put down: CHNDRN
+ {
+ FX_BOTDN, // sampleId
+ FX_SPOT, // type
+ 43, // delay (or random chance) *
+ { // roomVolList
+ {3,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 38 Brandy bottle picked up: GEOBOT3
+ {
+ FX_BOTUP, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {3,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 39 Chantelle gulps on brandy: CHNDRN
+ {
+ FX_GULP, // sampleId
+ FX_SPOT, // type
+ 23, // delay (or random chance) *
+ { // roomVolList
+ {3,4,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 40 Chantelle picked up off the floor: GEOCHN
+ {
+ FX_PIKUP, // sampleId
+ FX_SPOT, // type
+ 28, // delay (or random chance) *
+ { // roomVolList
+ {3,11,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 41 George searches Plantard's body: GEOCPS
+ {
+ FX_BODY, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance) *
+ { // roomVolList
+ {3,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 42 Moue cocks handgun. MOUENT
+ {
+ FX_PISTOL, // sampleId
+ FX_SPOT, // type
+ 23, // delay (or random chance) *
+ { // roomVolList
+ {4,4,7}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 43 George rummages in toolbox: GEOTBX
+ {
+ FX_TBOX, // sampleId
+ FX_SPOT, // type
+ 12, // delay (or random chance) *
+ { // roomVolList
+ {4,12,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 44 rat squeak 1
+ {
+ FX_RAT1, // sampleId
+ FX_RANDOM, // type
+ 193, // delay (or random chance)
+ { // roomVolList
+ {6,5,7}, // {roomNo,leftVol,rightVol}
+ {7,5,3},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 45 rat squeak 2
+ {
+ FX_RAT2, // sampleId
+ FX_RANDOM, // type
+ 201, // delay (or random chance)
+ { // roomVolList
+ {6,3,5}, // {roomNo,leftVol,rightVol}
+ {7,4,6},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 46 George climbs down ladder:
+ {
+ FX_LADD1, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {6,10,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 47 Rushing water loop
+ {
+ FX_SWATER3, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {6,10,11}, // {roomNo,leftVol,rightVol}
+ {7,12,11},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 48 Left hand bin being opened: GEOCAT?
+ {
+ FX_BIN3, // sampleId
+ FX_SPOT, // type
+ 12, // delay (or random chance)
+ { // roomVolList
+ {2,12,11}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 49 Middle bin being opened: GEOBIN
+ {
+ FX_BIN2, // sampleId
+ FX_SPOT, // type
+ 12, // delay (or random chance)
+ { // roomVolList
+ {2,11,11}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 50 Right hand bin being opened: GEOLID?
+ {
+ FX_BIN1, // sampleId
+ FX_SPOT, // type
+ 12, // delay (or random chance)
+ { // roomVolList
+ {2,10,11}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 51 Passing car sound
+ {
+ FX_CARS, // sampleId
+ FX_RANDOM, // type
+ 120, // delay (or random chance)
+ { // roomVolList
+ {10,8,1},
+ {12,7,7},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 52 Passing car sound
+ {
+ FX_FIESTA, // sampleId
+ FX_RANDOM, // type
+ 127, // delay (or random chance)
+ { // roomVolList
+ {10,8,1},
+ {12,7,7},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 53 Passing car sound
+ {
+ FX_CARLTON , // sampleId
+ FX_RANDOM, // type
+ 119, // delay (or random chance)
+ { // roomVolList
+ {10,8,1},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 54 Bird
+ {
+ FX_BIRD, // sampleId
+ FX_RANDOM, // type
+ 500, // delay (or random chance)
+ { // roomVolList
+ {9,10,10}, // {roomNo,leftVol,rightVol}
+ {10,2,1},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 55 George tries the door: GEOTRY
+ {
+ FX_DOORTRY, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance)
+ { // roomVolList
+ {9,9,9}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 56 George opens the door: GEODOOR9
+ {
+ FX_FLATDOOR, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {9,9,9}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 57 George picks the 'phone up: GEOPHN10
+ {
+ FX_FONEUP, // sampleId
+ FX_SPOT, // type
+ 15, // delay (or random chance)
+ { // roomVolList
+ {10,9,9}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 58 George puts the 'phone down: GEPDWN10
+ {
+ FX_FONEDN, // sampleId
+ FX_SPOT, // type
+ 4, // delay (or random chance)
+ { // roomVolList
+ {10,9,9}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 59 Albert opens the door: ALBOPEN
+ {
+ FX_ALBOP, // sampleId
+ FX_SPOT, // type
+ 13, // delay (or random chance)
+ { // roomVolList
+ {5,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 60 Albert closes the door: ALBCLOSE
+ {
+ FX_ALBCLO, // sampleId
+ FX_SPOT, // type
+ 20, // delay (or random chance)
+ { // roomVolList
+ {5,9,9}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 61 George enter Nico's flat. GEOENT10
+ {
+ FX_NICOPEN, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {10,7,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 62 George leaves Nico's. GEOLVS10
+ {
+ FX_NICLOSE, // sampleId
+ FX_SPOT, // type
+ 13, // delay (or random chance)
+ { // roomVolList
+ {10,7,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 63 Another bird for the street.
+ {
+ FX_BIRD2, // sampleId
+ FX_RANDOM, // type
+ 500, // WAS 15 (TOO LATE)
+ { // roomVolList
+ {9,10,10}, // {roomNo,leftVol,rightVol}
+ {10,2,1},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 64 George sits in the chair: GEOCHR
+ {
+ FX_GEOCHAIR, // sampleId
+ FX_SPOT, // type
+ 14, // delay (or random chance)
+ { // roomVolList
+ {10,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 65 George sits on the couch: GEOCCH
+ {
+ FX_GEOCCH, // sampleId
+ FX_SPOT, // type
+ 14, // delay (or random chance)
+ { // roomVolList
+ {10,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 66 George gets up from the chair: GEOCHR9
+ {
+ FX_GEOCHR9, // sampleId
+ FX_SPOT, // type
+ 5, // delay (or random chance)
+ { // roomVolList
+ {10,3,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 67 George is electrocuted: COSSHK
+ {
+ FX_SHOCK1, // sampleId
+ FX_SPOT, // type
+ 19, // delay (or random chance)
+ { // roomVolList
+ {11,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 68 George plays record: GEOWIND
+ {
+ FX_GRAMOFON, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {11,11,13}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 69 George is frisked: GORFRK
+ {
+ FX_FRISK, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance)
+ { // roomVolList
+ {12,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 70 Traffic sound
+ {
+ FX_TRAFFIC3, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {11,5,4},
+ {12,1,1},
+ {16,4,4},
+ {18,2,3},
+ {46,4,3},
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 71 Latvian reading: LATRDS
+ {
+ FX_PAPER6, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance)
+ { // roomVolList
+ {13,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 72 Deskbell
+ {
+ FX_DESKBELL, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {13,10,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 73 George picks up hotel 'phone: GEOTEL
+ {
+ FX_PHONEUP2, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {13,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 74 George puts down hotel 'phone: GEOTEL9
+ {
+ FX_PHONEDN2, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {13,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 75 George tries doors in corridor: GEODOR
+ {
+ FX_TRYDOR14, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {14,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 76 George opens bedside cabinet: BEDDOR
+ {
+ FX_CABOPEN, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance)
+ { // roomVolList
+ {15,10,14}, // {roomNo,leftVol,rightVol}
+ {17,10,14},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 77 George closes bedside cabinet: BEDDOR (reversed)
+ {
+ FX_CABCLOSE, // sampleId
+ FX_SPOT, // type
+ 5, // delay (or random chance)
+ { // roomVolList
+ {15,10,14}, // {roomNo,leftVol,rightVol}
+ {17,10,14},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 78 George opens the window: WINDOW
+ {
+ FX_WINDOPEN, // sampleId
+ FX_SPOT, // type
+ 19, // delay (or random chance)
+ { // roomVolList
+ {15,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 79 George goes right along the ledge: GEOIRW
+ {
+ FX_LEDGE1, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {16,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 80 George goes left along the ledge: GEOILW
+ {
+ FX_LEDGE2, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {16,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 81 Pigeon noises
+ {
+ FX_COO, // sampleId
+ FX_RANDOM, // type
+ 80, // delay (or random chance)
+ { // roomVolList
+ {16,7,9}, // {roomNo,leftVol,rightVol}
+ {46,5,4},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 82 Pigeon noises
+ {
+ FX_COO2, // sampleId
+ FX_RANDOM, // type
+ 60, // delay (or random chance)
+ { // roomVolList
+ {15,3,4}, // {roomNo,leftVol,rightVol}
+ {16,8,5}, // {roomNo,leftVol,rightVol}
+ {17,3,4},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 83 George picks up and opens case: GEOBFC
+ {
+ FX_BRIEFON, // sampleId
+ FX_SPOT, // type
+ 16, // delay (or random chance)
+ { // roomVolList
+ {17,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 84 George closes and puts down case: GEOBFC (reversed)
+ {
+ FX_BRIEFOFF, // sampleId
+ FX_SPOT, // type
+ 12, // delay (or random chance)
+ { // roomVolList
+ {17,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 85 George gets into wardrobe. GEOWRB2 Attention, James. This is new as of 15/7/96
+ {
+ FX_WARDIN, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance)
+ { // roomVolList
+ {17,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 86 George gets out of wardrobe. GEOWRB2 (Reversed). Attention, James. This is new as of 15/7/96
+ {
+ FX_WARDOUT, // sampleId
+ FX_SPOT, // type
+ 41, // delay (or random chance)
+ { // roomVolList
+ {17,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 87 George jumps in through window: GEOWIN2
+ {
+ FX_JUMPIN, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance)
+ { // roomVolList
+ {15,8,10},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 88 George climbs in: GEOWIN2/GEOWIN8
+ {
+ FX_CLIMBIN, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {17,8,16}, // {roomNo,leftVol,rightVol}
+ {15,8,16},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 89 George climbs out: GEOWIN1/GEOWIN9
+ {
+ FX_CLIMBOUT, // sampleId
+ FX_SPOT, // type
+ 17, // delay (or random chance)
+ { // roomVolList
+ {17,9,10}, // {roomNo,leftVol,rightVol}
+ {15,9,10},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 90 George picks the 'phone up: GEOTEL18
+ {
+ FX_FONEUP, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {18,4,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 91 George puts the 'phone down: GEOTL18A
+ {
+ FX_FONEDN, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance)
+ { // roomVolList
+ {18,4,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 92 George tries to get keys. GEOKEY
+ {
+ FX_KEY13, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance)
+ { // roomVolList
+ {13,3,2}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 93 George manages to get keys. GEOKEY13
+ {
+ FX_KEY13, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance)
+ { // roomVolList
+ {13,3,2}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 94 George electrocutes Maguire: MAGSHK
+ {
+ FX_SHOCK2, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance)
+ { // roomVolList
+ {19,9,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 95 George opens dray door : GEOTRP8
+ {
+ FX_TRAPOPEN, // sampleId
+ FX_SPOT, // type
+ 20, // delay (or random chance)
+ { // roomVolList
+ {19,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 96 George breaks switch : Which anim?
+ {
+ FX_SWITCH19, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {19,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 97 Leary pulls pint: LESPMP
+ {
+ FX_PULLPINT, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance)
+ { // roomVolList
+ {20,10,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 98 Glasswasher fuse blows (and the glass washer grinds to a halt)
+ {
+ FX_FUSE20, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {20,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 99 Fitz leaps to his feet: FTZSTD
+ {
+ FX_FITZUP, // sampleId
+ FX_SPOT, // type
+ 5, // delay (or random chance)
+ { // roomVolList
+ {20,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 100 Fitz runs for it: FTZRUN
+ {
+ FX_FITZRUN, // sampleId
+ FX_SPOT, // type
+ 15, // delay (or random chance)
+ { // roomVolList
+ {20,12,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 101 George pulls lever: GEOLVR & GEOLVR26
+ {
+ FX_LEVER, // sampleId
+ FX_SPOT, // type
+ 26, // delay (or random chance)
+ { // roomVolList
+ {21,8,10}, // {roomNo,leftVol,rightVol}
+ {26,8,10},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 102 George pulls lever: GEOLVR8 & GEOLVR08
+ {
+ FX_LEVER2, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance)
+ { // roomVolList
+ {21,8,10}, // {roomNo,leftVol,rightVol}
+ {26,8,10},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 103 George opens tap: No idea what the anim is
+ {
+ FX_TAP, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {21,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 104 George closes tap: No idea what this anim is either
+ {
+ FX_TAP2, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {21,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 105 Bar flap: FLPOPN
+ {
+ FX_BARFLAP, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {20,6,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 106 Farmer leaves: FRMWLK
+ {
+ FX_FARMERGO, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance)
+ { // roomVolList
+ {22,6,9}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 107 George climbs haystack: GEOCLM
+ {
+ FX_CLIMBHAY, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance)
+ { // roomVolList
+ {22,14,14}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 108 George drives sewer key into wall: GEOKEY23
+ {
+ FX_KEYSTEP, // sampleId
+ FX_SPOT, // type
+ 39, // delay (or random chance)
+ { // roomVolList
+ {23,8,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 109 George climbs over wall: GEOCLM23
+ {
+ FX_CASTLWAL, // sampleId
+ FX_SPOT, // type
+ 17, // delay (or random chance)
+ { // roomVolList
+ {23,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 110 George falls from wall: GEOTRY23
+ {
+ FX_CLIMBFAL, // sampleId
+ FX_SPOT, // type
+ 43, // delay (or random chance)
+ { // roomVolList
+ {23,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 111 Goat chewing: GOTEAT
+ {
+ FX_GOATCHEW, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {24,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 112 George moves plough: GEOPLW
+ {
+ FX_PLOUGH, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance)
+ { // roomVolList
+ {24,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 113 George drops slab: STNFALL
+ {
+ FX_SLABFALL, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance)
+ { // roomVolList
+ {25,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 114 George picks up slab: GEOSTN8
+ {
+ FX_SLABUP, // sampleId
+ FX_SPOT, // type
+ 29, // delay (or random chance)
+ { // roomVolList
+ {25,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 115 Secret door opens: ALTOPN
+ {
+ FX_SECDOR25, // sampleId
+ FX_SPOT, // type
+ 17, // delay (or random chance)
+ { // roomVolList
+ {25,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 116 George wrings out cloth: GEOTWL25
+ {
+ FX_WRING, // sampleId
+ FX_SPOT, // type
+ 24, // delay (or random chance)
+ { // roomVolList
+ {25,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 117 Rat running across barrels: RATJMP
+ {
+ FX_RAT3A, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {26,8,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 118 Rat running across barrels: RATJMP
+ {
+ FX_RAT3B, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance)
+ { // roomVolList
+ {26,7,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 119 Rat running across barrels: RATJMP
+ {
+ FX_RAT3C, // sampleId
+ FX_SPOT, // type
+ 26, // delay (or random chance)
+ { // roomVolList
+ {26,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 120 Irish bird song 1:
+ {
+ FX_EIRBIRD1, // sampleId
+ FX_RANDOM, // type
+ 720, // delay (or random chance)
+ { // roomVolList
+ {19,6,8}, // {roomNo,leftVol,rightVol}
+ {21,2,3},
+ {22,8,5},
+ {23,6,5},
+ {24,8,8},
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 121 Irish bird song 2:
+ {
+ FX_EIRBIRD2, // sampleId
+ FX_RANDOM, // type
+ 720, // delay (or random chance)
+ { // roomVolList
+ {19,8,6}, // {roomNo,leftVol,rightVol}
+ {21,2,3},
+ {22,6,8},
+ {23,5,5},
+ {24,8,8},
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 122 Irish bird song 3:
+ {
+ FX_EIRBIRD3, // sampleId
+ FX_RANDOM, // type
+ 720, // delay (or random chance)
+ { // roomVolList
+ {19,8,8}, // {roomNo,leftVol,rightVol}
+ {21,3,4},
+ {22,8,8},
+ {23,5,6},
+ {24,6,8},
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 123 Rat 3D:
+ {
+ FX_RAT3D, // sampleId
+ FX_RANDOM, // type
+ 600, // delay (or random chance)
+ { // roomVolList
+ {26,2,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 124 Wind atop the battlements
+ {
+ FX_WIND, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {23,6,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 125 Glasswasher in the pub (Room 20) *JEL* Stops after fuse blows and starts when george fixes it.
+ {
+ FX_WASHER, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {20,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 126 Running tap in the cellar: (Room 21) *JEL* Only when the tap is on.
+ {
+ FX_CELTAP, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {21,3,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 127 Lopez's hose. Basically a loop but stops when George cuts the water supply. Replaces MUTTER1.
+ {
+ FX_HOSE57, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {57,3,1}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 128 Lopez's hose being switched off. Anim GARD05. Replaces MUTTER2.
+ {
+ FX_HOSE57B, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {57,3,2}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 129 Nejo bouncing the ball off the door. NEJ8
+ {
+ FX_BALLPLAY, // sampleId
+ FX_SPOT, // type
+ 13, // delay (or random chance)
+ { // roomVolList
+ {45,5,1}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ //------------------------
+ // 130 Cricket loop for Syrian desert Only audible in 55 when the cave door is open.
+ {
+ FX_CRICKET, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {54,8,8}, // {roomNo,leftVol,rightVol}
+ {55,3,5},
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 131 Display case shatters: GEOTOTB
+ {
+ FX_SMASHGLA, // sampleId
+ FX_SPOT, // type
+ 35, // delay (or random chance)
+ { // roomVolList
+ {29,16,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 132 Burglar alarm: Once the case is smashed (see 131)
+ {
+ FX_ALARM, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {28,12,12}, // {roomNo,leftVol,rightVol}
+ {29,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 133 Guido fires: GUIGUN
+ {
+ FX_GUN1, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance)
+ { // roomVolList
+ {29,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 134 Guido knocked down: NICPUS1
+ {
+ FX_GUI_HIT, // sampleId
+ FX_SPOT, // type
+ 40, // delay (or random chance)
+ { // roomVolList
+ {29,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 135 Museum exterior ambience
+ {
+ FX_MUESEXT, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {27,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 136 Cat gets nowty: CAT3
+ {
+ FX_STALLCAT, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {45,10,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 137 Cat gets very nowty: CAT5
+ {
+ FX_CATHIT, // sampleId
+ FX_SPOT, // type
+ 4, // delay (or random chance)
+ { // roomVolList
+ {45,10,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 138 Desert wind: Only audible in 55 when the cave door is open.
+ {
+ FX_SYRIWIND, // sampleId
+ FX_RANDOM, // type
+ 720, // delay (or random chance)
+ { // roomVolList
+ {54,10,10}, // {roomNo,leftVol,rightVol}
+ {55,5,7},
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ // 139 Bell on Nejo's stall: GEOSYR7
+ {
+ FX_STALLBEL, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance)
+ { // roomVolList
+ {45,10,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ }
+ },
+ //------------------------
+ //------------------------
+ // 140 George electrocutes Khan: GEOSYR40
+ {
+ FX_SHOCK3, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance)
+ { // roomVolList
+ {54,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 141 George thumps Khan: GEOSYR40
+ {
+ FX_THUMP1, // sampleId
+ FX_SPOT, // type
+ 22, // delay (or random chance)
+ { // roomVolList
+ {54,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 142 Khan hits the floor: KHS9
+ {
+ FX_KHANDOWN, // sampleId
+ FX_SPOT, // type
+ 24, // delay (or random chance)
+ { // roomVolList
+ {54,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 143 Hospital ambience
+ {
+ FX_HOSPNOIS, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {32,6,4}, // {roomNo,leftVol,rightVol}
+ {33,7,7},
+ {34,3,4},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 144 Mr Shiny switched on: DOMPLG (Start FX_SHINY)
+ {
+ FX_SHINYON, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {33,12,14}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 145 Mr Shiny running
+ {
+ FX_SHINY, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {32,4,3}, // {roomNo,leftVol,rightVol}
+ {33,12,14},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 146 Mr Shiny switched off: GEOPLG33 (Turn off FX_SHINY at the same time)
+ {
+ FX_SHINYOFF, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {33,12,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 147 Benoir takes blood pressure: BENBP1 or BENBP2
+ {
+ FX_BLOODPRE, // sampleId
+ FX_SPOT, // type
+ 39, // delay (or random chance)
+ { // roomVolList
+ {34,14,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 148 George takes blood pressure: GEOBP1 or GEOBP2
+ {
+ FX_BLOODPRE, // sampleId
+ FX_SPOT, // type
+ 62, // delay (or random chance)
+ { // roomVolList
+ {34,14,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 149 Goat baas as it attacks: GOTCR and GOTCL
+ {
+ FX_GOATBAA, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {24,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 150 Goat peeved at being trapped: GOTPLW (I'd advise triggering this anim randomly if you haven't done that)
+ {
+ FX_GOATDOH, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance)
+ { // roomVolList
+ {24,7,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 151 George triggers the Irish secret door: GEOPUT
+ {
+ FX_TRIGER25, // sampleId
+ FX_SPOT, // type
+ 35, // delay (or random chance)
+ { // roomVolList
+ {25,6,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 152 George winds up gramophone: GEOWIND
+ {
+ FX_WINDUP11, // sampleId
+ FX_SPOT, // type
+ 16, // delay (or random chance)
+ { // roomVolList
+ {11,7,7}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 153 Marib ambience
+ {
+ FX_MARIB, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {45,7,7}, // {roomNo,leftVol,rightVol}
+ {47,5,5},
+ {50,5,4},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 154 Statuette breaks: STA2
+ {
+ FX_STATBREK, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {45,7,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 155 George opens toilet door: CUBDOR50
+ {
+ FX_CUBDOR, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance)
+ { // roomVolList
+ {50,6,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 156 Crowd goes, "Ooh!": CRO36APP
+ {
+ FX_OOH, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance)
+ { // roomVolList
+ {36,6,7}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 157 Phone rings: When Nico calls back in room 41. Loops until the guard answers it.
+ {
+ FX_PHONCALL, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {41,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 158 Phone picked up in 41: GUA41ANS
+ {
+ FX_FONEUP41, // sampleId
+ FX_SPOT, // type
+ 18, // delay (or random chance)
+ { // roomVolList
+ {41,5,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 159 George turns thermostat: GEO41THE (another dummy). Also used on the reverse.
+ {
+ FX_THERMO1, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance)
+ { // roomVolList
+ {41,6,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 160 Low echoing rumble of large church
+ {
+ FX_CHURCHFX, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance)
+ { // roomVolList
+ {38,5,5}, // {roomNo,leftVol,rightVol}
+ {48,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 161 George drys hand: GEO43HAN
+ {
+ FX_DRIER1, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance)
+ { // roomVolList
+ {43,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 162 George jumps in through window: GEOWIN8
+ {
+ FX_JUMPIN, // sampleId
+ FX_SPOT, // type
+ 49, // delay (or random chance)
+ { // roomVolList
+ {17,8,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 163 Khan fires: KHS12
+ {
+ FX_SHOTKHAN, // sampleId
+ FX_SPOT, // type
+ 30, // delay (or random chance)
+ { // roomVolList
+ {54,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 164 Khan fires: KHS5
+ {
+ FX_SHOTKHAN, // sampleId
+ FX_SPOT, // type
+ 5, // delay (or random chance)
+ { // roomVolList
+ {54,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 165 George falls: GEOSYR37
+ {
+ FX_GEOFAL54, // sampleId
+ FX_SPOT, // type
+ 25, // delay (or random chance)
+ { // roomVolList
+ {54,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 166 George falls after going for the gun (GEOSYR42)
+ {
+ FX_GEOFAL54, // sampleId
+ FX_SPOT, // type
+ 46, // delay (or random chance)
+ { // roomVolList
+ {54,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 167 Pickaxe sound 5: Screen 1 - WRKDIG01
+ {
+ FX_PICK5, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {1,3,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 168 George climbs ladder in 7: GEOASC07
+ {
+ FX_SEWLADU7, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance) *
+ { // roomVolList
+ {7,8,9}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 169 George picks keys up in Alamut: GEOKEYS1
+ {
+ FX_KEYS49, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {49,8,7}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 170 George puts down keys up in Alamut: GEOKEYS2
+ {
+ FX_KEYS49, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance) *
+ { // roomVolList
+ {49,8,7}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 171 George unlocks toilet door: GEOSYR43
+ {
+ FX_UNLOCK49, // sampleId
+ FX_SPOT, // type
+ 16, // delay (or random chance) *
+ { // roomVolList
+ {49,6,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 172 George breaks the toilet chain. GEOSYR48
+ {
+ FX_WCCHAIN, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance) *
+ { // roomVolList
+ {50,6,7}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 173 George breaks the branch of the cliff edge tree. GEOSYR20
+ {
+ FX_BREKSTIK, // sampleId
+ FX_SPOT, // type
+ 16, // delay (or random chance) *
+ { // roomVolList
+ {54,6,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 174 George climbs down the cliff face. GEOSYR23
+ {
+ FX_CLIMBDWN, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance) *
+ { // roomVolList
+ {54,6,7}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 175 George pulls ring: GEOSYR26
+ {
+ FX_RINGPULL, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {54,7,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 176 Bull's Head door opens: SECDOR
+ {
+ FX_SECDOR54, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {54,7,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 177 Inside Bull's Head door opens: DOOR55 (and its reverse).
+ {
+ FX_SECDOR55, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {55,4,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 178 Ayub opens door. AYU1
+ {
+ FX_AYUBDOOR, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {45,8,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 179 George knocks at the door in location 4: GEONOK followed by reverse of GEONOK
+ {
+ FX_KNOKKNOK, // sampleId
+ FX_SPOT, // type
+ 13, // delay (or random chance) *
+ { // roomVolList
+ {4,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 180 George knocks at the door in location 5: GEONOK05
+ {
+ FX_KNOKKNOK, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance) *
+ { // roomVolList
+ {5,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 181 Those pesky Irish birds turn up in Spain, too.
+ {
+ FX_SPNBIRD1, // sampleId
+ FX_RANDOM, // type
+ 720, // delay (or random chance) *
+ { // roomVolList
+ {57,1,4},
+ {58,8,4},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 182 Those pesky Irish birds turn up in Spain, too.
+ {
+ FX_SPNBIRD2, // sampleId
+ FX_RANDOM, // type
+ 697, // delay (or random chance) *
+ { // roomVolList
+ {57,4,8}, // {roomNo,leftVol,rightVol}
+ {58,4,1},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 183 The secret door in the well: SECDOR61 anim
+ {
+ FX_SECDOR61, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {61,4,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 184 Spanish countryside ambience
+ {
+ FX_SPAIN, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {57,1,2}, //
+ {58,2,2}, //
+ {60,1,1}, //
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 185 Spanish well ambience
+ {
+ FX_WELLDRIP, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {61,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 186 Fish falls on George's head: GEOTOT29
+ {
+ FX_FISHFALL, // sampleId
+ FX_SPOT, // type
+ 60, // delay (or random chance) *
+ { // roomVolList
+ {29,10,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 187 Hospital exterior ambience
+ {
+ FX_HOSPEXT, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {31,3,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 188 Hospital exterior gravel footstep #1
+ {
+ FX_GRAVEL1, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {31,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 189 Hospital exterior gravel footstep #2
+ {
+ FX_GRAVEL2, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {31,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 190 George opens sarcophagus: GEOSAR
+ {
+ FX_SARCO28A, // sampleId
+ FX_SPOT, // type
+ 26, // delay (or random chance) *
+ { // roomVolList
+ {28,6,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 191 George closes sarcophagus: GEOSAR2
+ {
+ FX_SARCO28B, // sampleId
+ FX_SPOT, // type
+ 24, // delay (or random chance) *
+ { // roomVolList
+ {28,3,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 192 Guard opens sarcophagus: MUSOPN
+ {
+ FX_SARCO28C, // sampleId
+ FX_SPOT, // type
+ 14, // delay (or random chance) *
+ { // roomVolList
+ {28,3,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 193 George peeks out of sarcophagus: GEOPEEK
+ {
+ FX_SARCO29, // sampleId
+ FX_SPOT, // type
+ 4, // delay (or random chance) *
+ { // roomVolList
+ {29,5,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 194 The rope drops into the room: ROPE29
+ {
+ FX_ROPEDOWN, // sampleId
+ FX_SPOT, // type
+ 3, // delay (or random chance) *
+ { // roomVolList
+ {29,3,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 195 George pushes the totem pole: GEOTOT29
+ {
+ FX_TOTEM29A, // sampleId
+ FX_SPOT, // type
+ 30, // delay (or random chance) *
+ { // roomVolList
+ {29,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 196 George pushes the totem pole over: GEOTOTB
+ {
+ FX_TOTEM29B, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {29,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 197 George rocks the totem pole in museum hours: TOTEM28
+ {
+ FX_TOTEM28A, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance) *
+ { // roomVolList
+ {28,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 198 Ambient sound for Montfauçon Square
+ {
+ FX_MONTAMB, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {36,6,6}, // {roomNo,leftVol,rightVol}
+ {40,6,6},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 199 Ambient sound churchyard.
+ {
+ FX_WIND71, // sampleId
+ FX_RANDOM, // type
+ 720, // delay (or random chance) *
+ { // roomVolList
+ {71,10,10}, // {roomNo,leftVol,rightVol}
+ {72,7,7},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 200 Owl cry #1 in churchyard
+ {
+ FX_OWL71A, // sampleId
+ FX_RANDOM, // type
+ 720, // delay (or random chance) *
+ { // roomVolList
+ {71,8,8}, // {roomNo,leftVol,rightVol}
+ {72,6,4},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 201 Owl cry #2 in churchyard
+ {
+ FX_OWL71B, // sampleId
+ FX_RANDOM, // type
+ 1080, // delay (or random chance) *
+ { // roomVolList
+ {71,8,8}, // {roomNo,leftVol,rightVol}
+ {72,7,6},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 202 Air conditioner in the museum
+ {
+ FX_AIRCON28, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {28,6,6}, // {roomNo,leftVol,rightVol}
+ {29,3,3},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 203 George breaks the handle off in the church tower. GEOWND72
+ {
+ FX_COG72A, // sampleId
+ FX_SPOT, // type
+ 5, // delay (or random chance) *
+ { // roomVolList
+ {72,10,10},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 204 Countess' room ambience
+ {
+ FX_AMBIEN56, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {56,3,2}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 205 Musical effect for George drinking beer. GEODRN20
+ {
+ FX_DRINK, // sampleId
+ FX_SPOT, // type
+ 17, // delay (or random chance) *
+ { // roomVolList
+ {20,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 206 Torch thrown through the air. GEOTHROW
+ {
+ FX_TORCH73, // sampleId
+ FX_SPOT, // type
+ 14, // delay (or random chance) *
+ { // roomVolList
+ {73,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 207 Internal train ambience.
+ {
+ FX_TRAININT, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {63,3,3}, // {roomNo,leftVol,rightVol}
+ {65,2,2},
+ {66,2,2},
+ {67,2,2},
+ {69,2,2},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 208 Countess' clock. PENDULUM. Note: Trigger the sound effect on alternate runs of the pendulum animation.
+ {
+ FX_PENDULUM, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance) *
+ { // roomVolList
+ {56,2,2}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 209 Compartment door. DOOR65
+ {
+ FX_DOOR65, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {65,3,3}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 210 Opening window. GEOOPN1
+ {
+ FX_WINDOW66, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance) *
+ { // roomVolList
+ {66,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 211 Wind rip by the open window. Triggered at the end of effect 210.
+ {
+ FX_WIND66, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {66,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 212 George electrocutes himself on the pantograph. Fool. GEOSHK64
+ {
+ FX_SHOCK63, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {63,12,14}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 213 The train brakes violently. GEOSTP69
+ {
+ FX_BRAKES, // sampleId
+ FX_SPOT, // type
+ 13, // delay (or random chance) *
+ { // roomVolList
+ {69,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 214 The train ticks over. From the end of BRAKE.
+ {
+ FX_TICK69, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {69,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 215 Eklund shoot Khan. FIGHT69
+ {
+ FX_EKSHOOT, // sampleId
+ FX_SPOT, // type
+ 120, // delay (or random chance) *
+ { // roomVolList
+ {69,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 216 Eklund shoots George. GEODIE69
+ {
+ FX_EKSHOOT, // sampleId
+ FX_SPOT, // type
+ 21, // delay (or random chance) *
+ { // roomVolList
+ {69,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 217 Khan pulls the door open. FIGHT69
+ {
+ FX_DOOR69, // sampleId
+ FX_SPOT, // type
+ 42, // delay (or random chance) *
+ { // roomVolList
+ {69,8,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 218 Wind shriek. Loops from the end of DOOR69 wav to the beginning of BRAKES.
+ {
+ FX_WIND66, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {69,8,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 219 Brakes releasing pressure. Only after BRAKE has been run.
+ {
+ FX_PNEUMO69, // sampleId
+ FX_RANDOM, // type
+ 720, // delay (or random chance) *
+ { // roomVolList
+ {69,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 220 External train sound. Played while George is on the top of the train.
+ {
+ FX_TRAINEXT, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {63,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 221 The passing train. FIGHT69
+ {
+ FX_TRNPASS, // sampleId
+ FX_SPOT, // type
+ 102, // delay (or random chance) *
+ { // roomVolList
+ {69,4,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 222 George descends into sewer. GEODESO6
+ {
+ FX_LADD2, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {6,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 223 George ascends into alley. GEOASC06
+ {
+ FX_LADD3, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance) *
+ { // roomVolList
+ {6,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 224 George replaces manhole cover. GEOMAN9
+ {
+ FX_COVERON2, // sampleId
+ FX_SPOT, // type
+ 19, // delay (or random chance) *
+ { // roomVolList
+ {2,12,11}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 225 Montfaucon sewer ambience.
+ {
+ FX_AMBIEN37, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {37,5,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 226 George's winning smile. GEOJMP72.
+ {
+ FX_PING, // sampleId
+ FX_SPOT, // type
+ 26, // delay (or random chance) *
+ { // roomVolList
+ {72,10,14}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 227 George starts to open the manhole. GEO36KNE
+ {
+ FX_MANOP36, // sampleId
+ FX_SPOT, // type
+ 19, // delay (or random chance) *
+ { // roomVolList
+ {36,4,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 228 George opens the manhole. GEO36OPE
+ {
+ FX_PULLUP36, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {36,4,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 229 George replaces the manhole cover. GEO36CLO
+ {
+ FX_REPLCE36, // sampleId
+ FX_SPOT, // type
+ 20, // delay (or random chance) *
+ { // roomVolList
+ {36,4,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 230 George knocks at righthand arch. GEO37TA3
+ {
+ FX_KNOCK37, // sampleId
+ FX_SPOT, // type
+ 20, // delay (or random chance) *
+ { // roomVolList
+ {37,6,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 231 George knocks at middle or lefthand arch. GEO37TA1 or GEO37TA2.
+ {
+ FX_KNOCK37B, // sampleId
+ FX_SPOT, // type
+ 20, // delay (or random chance) *
+ { // roomVolList
+ {37,4,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 232 George winds the chain down HOO37LBO
+ {
+ FX_CHAIN37, // sampleId
+ FX_SPOT, // type
+ 14, // delay (or random chance) *
+ { // roomVolList
+ {37,6,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 233 George winds the chain up. HOO37LBO (In reverse)
+ {
+ FX_CHAIN37B, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {37,6,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 234 George breaks hole in door. GEO37TA4
+ {
+ FX_HOLE37, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {37,6,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 235 Plaster door collapses. DOR37COL
+ {
+ FX_DOOR37, // sampleId
+ FX_SPOT, // type
+ 23, // delay (or random chance) *
+ { // roomVolList
+ {37,8,15}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 236 Barge winch. GEO37TUL (If it runs more than once, trigger the effect on frame one. Incidentally, this is a reversible so the effect must launch on frame one of the .cdr version as well. )
+ {
+ FX_WINCH37, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {37,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 237 George places chess piece. GEOSPA17
+ {
+ FX_CHESS, // sampleId
+ FX_SPOT, // type
+ 23, // delay (or random chance) *
+ { // roomVolList
+ {59,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 238 Piano loop for the upstairs hotel corridor.
+ {
+ FX_PIANO14, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {14,2,2}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 239 Door opens in church tower. PANEL72
+ {
+ FX_SECDOR72, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {72,8,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 240 George rummages through debris. Tied to the end of the whichever crouch is used. Use either this one or RUMMAGE2 alternatively or randomly. Same kind of schtick as the pick axe noises, I suppose.
+ {
+ FX_RUMMAGE1, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {72,8,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 241 George rummages through debris. See above for notes.
+ {
+ FX_RUMMAGE2, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {72,8,6}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 242 Gust of wind in the graveyard.
+ {
+ FX_GUST71, // sampleId
+ FX_RANDOM, // type
+ 1080, // delay (or random chance) *
+ { // roomVolList
+ {71,3,3}, // {roomNo,leftVol,rightVol}
+ {72,2,1},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 243 Violin ambience for Ireland.
+ {
+ FX_VIOLIN19, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {19,3,3}, // {roomNo,leftVol,rightVol}
+ {21,2,2},
+ {26,2,2},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 244 Footstep #1 for underground locations. Same schtick as for 188 and 189.
+ {
+ FX_SEWSTEP1, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {6,8,8}, // {roomNo,leftVol,rightVol}
+ {7,8,8},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 245 Footstep #2 for underground locations. Same schtick as for 188 and 189.
+ {
+ FX_SEWSTEP2, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {6,16,16}, // {roomNo,leftVol,rightVol}
+ {7,16,16},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 246 Nico's carabiner as she descends into the museum. NICPUS1
+ {
+ FX_CARABINE, // sampleId
+ FX_SPOT, // type
+ 4, // delay (or random chance) *
+ { // roomVolList
+ {29,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 247 Rosso is shot (with a piece of field artillery). ROSSHOT
+ {
+ FX_GUN79, // sampleId
+ FX_SPOT, // type
+ 2, // delay (or random chance) *
+ { // roomVolList
+ {79,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 248 George is hit by the thrown stilletto. GEODIE1
+ {
+ FX_DAGGER1, // sampleId
+ FX_SPOT, // type
+ 2, // delay (or random chance) *
+ { // roomVolList
+ {73,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 249 George is hit by the thrown stilletto after walking forward. GEODIE2
+ {
+ FX_DAGGER1, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {73,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 250 Can hits the well water. The cue is in GAR2SC57.TXT immediately after the line, "over: Lopez threw the can away. It seemed to fall an awfully long way."
+ {
+ FX_CANFALL, // sampleId
+ FX_SPOT, // type
+ 4, // delay (or random chance) *
+ { // roomVolList
+ {57,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 251 Mad, fizzing damp and ancient gunpowder after the application of a torch.
+ {
+ FX_GUNPOWDR, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {73,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 252 Maguire whistling. MAGSLK. Plays while Maguire is idling, stops abruptly when he does something else.
+ {
+ FX_WHISTLE, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {19,2,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 253 George is hit by the goat. GEOHITR and GEOHITL.
+ {
+ FX_GEOGOAT, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {24,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 254 Manager says, "Hello". MAN2
+ {
+ FX_MANG1, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance) *
+ { // roomVolList
+ {49,6,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 255 Manager says, Don't go in there!" MAN3
+ {
+ FX_MANG2, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {49,6,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 256 Manager says, "Here are the keys." MAN4
+ {
+ FX_MANG3, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance) *
+ { // roomVolList
+ {49,6,5}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 257 George pulls the lion's tooth. GEOSPA26
+ {
+ FX_TOOTHPUL, // sampleId
+ FX_SPOT, // type
+ 19, // delay (or random chance) *
+ { // roomVolList
+ {61,8,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 258 George escapes the lion. LION1
+ {
+ FX_LIONFALL, // sampleId
+ FX_SPOT, // type
+ 7, // delay (or random chance) *
+ { // roomVolList
+ {61,8,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 259 George gets flattened. LION2
+ {
+ FX_LIONFAL2, // sampleId
+ FX_SPOT, // type
+ 4, // delay (or random chance) *
+ { // roomVolList
+ {61,8,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 260 Rosso dies. ROSSFALL
+ {
+ FX_ROSSODIE, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {74,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 261 Eklund chokes George. FIGHT79
+ {
+ FX_CHOKE1, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {79,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 262 Eklund chokes George some more. FIGHT79
+ {
+ FX_CHOKE2, // sampleId
+ FX_SPOT, // type
+ 54, // delay (or random chance) *
+ { // roomVolList
+ {79,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 263 Eklund dies. FIGHT79
+ {
+ FX_FIGHT2, // sampleId
+ FX_SPOT, // type
+ 44, // delay (or random chance) *
+ { // roomVolList
+ {79,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 264 George hears museum break-in. GEOSUR29
+ {
+ FX_DOOR29, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance) *
+ { // roomVolList
+ {94,14,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 265 George hits the floor having been shot. GEODED.
+ {
+ FX_GDROP29, // sampleId
+ FX_SPOT, // type
+ 27, // delay (or random chance) *
+ { // roomVolList
+ {29,10,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 266 George hits the floor having been stunned. GEOFISH
+ {
+ FX_GDROP29, // sampleId
+ FX_SPOT, // type
+ 27, // delay (or random chance) *
+ { // roomVolList
+ {29,10,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 267 Fitz being knocked down as heard from inside the pub. Triggered from the script, I think. This is just a stopgap until Hackenbacker do the full version for the Smacker, then I'll sample the requisite bit and put it in here.
+ {
+ FX_FITZHIT, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {20,16,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 268 Gendarme shoots lock off. GENSHOT
+ {
+ FX_GUN34, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {34,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 269 ECG alarm, Marquet in trouble. Start looping imeediately before George says, "Thanks, Bunny".
+ // Incidentally, James, please switch off Mr Shiney permanently when George first gets into Marquet's room. He gets in the way when they're figuring out that Eklund's an imposter.
+ {
+ FX_PULSE2, // sampleId
+ FX_LOOP, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {30,16,16}, // {roomNo,leftVol,rightVol}
+ {34,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 270 ECG alarm, Marquet dead. Switch off the previous effect and replace with this immediately before the gendarme says, "Stand back, messieurs."
+ {
+ FX_PULSE3, // sampleId
+ FX_LOOP, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {30,16,16}, // {roomNo,leftVol,rightVol}
+ {34,13,13},
+ {35,13,13},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 271 Door closing. GEOENT15
+ {
+ FX_DORCLOSE, // sampleId
+ FX_SPOT, // type
+ 4, // delay (or random chance) *
+ { // roomVolList
+ {15,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 272 Cupboard opening. GEOCOT
+ {
+ FX_CUPBOPEN, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance) *
+ { // roomVolList
+ {33,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 273 Cupboard closing. GEOCOT
+ {
+ FX_CUPBCLOS, // sampleId
+ FX_SPOT, // type
+ 33, // delay (or random chance) *
+ { // roomVolList
+ {33,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 274 Closing door when George leaves hotel room. GEOLVS15 and GEODOR17 (they're identical).
+ {
+ FX_DORCLOSE, // sampleId
+ FX_SPOT, // type
+ 44, // delay (or random chance) *
+ { // roomVolList
+ {15,12,12}, // {roomNo,leftVol,rightVol}
+ {17,12,12},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 275 Closing door when George leaves the pub. DOROPN20 (Reversed)
+ {
+ FX_DORCLOSE20,// sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {20,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 276 Nico call for a cab. NICPHN10
+ {
+ FX_PHONICO1, // sampleId
+ FX_SPOT, // type
+ 15, // delay (or random chance) *
+ { // roomVolList
+ {10,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 277 Nico puts down the phone. NICDWN10
+ {
+ FX_FONEDN, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance) *
+ { // roomVolList
+ {10,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 278 Painter puts down the phone. PAI41HAN
+ {
+ FX_FONEDN41, // sampleId
+ FX_SPOT, // type
+ 5, // delay (or random chance) *
+ { // roomVolList
+ {41,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 279 Mechanical hum of heating system in the dig lobby.
+ {
+ FX_AIRCON41, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {41,6,6}, // {roomNo,leftVol,rightVol}
+ {43,8,8},
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ //------------------------
+ // 280 The Sword is Reforged (Grandmaster gets zapped) GMPOWER
+ {
+ FX_REFORGE1, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {78,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 281 The Sword is Reforged (G&N gawp at the spectacle) There's no anim I know of to tie it to unless the flickering blue light is one.
+ {
+ FX_REFORGE2, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {75,12,12}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 282 The Sword is Reforged (We watch over G&N's heads as the Grandmaster gets zapped) GMWRIT74
+ {
+ FX_REFORGE2, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {74,14,14}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 283 The Sword is Reforged (Grandmaster finishes being zapped) GMWRITH
+ {
+ FX_REFORGE4, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {78,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 284 Baphomet Cavern Ambience
+ {
+ FX_BAPHAMB, // sampleId
+ FX_LOOP, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {74,6,8}, // {roomNo,leftVol,rightVol}
+ {75,7,8}, // {roomNo,leftVol,rightVol}
+ {76,8,8}, // {roomNo,leftVol,rightVol}
+ {77,8,8}, // {roomNo,leftVol,rightVol}
+ {78,8,8}, // {roomNo,leftVol,rightVol}
+ {79,7,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 285 Duane's Happy-Snappy Camera. XDNEPHO3 and XDNEPHO5.
+ {
+ FX_CAMERA45, // sampleId
+ FX_SPOT, // type
+ 30, // delay (or random chance) *
+ { // roomVolList
+ {45,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 286 Grand Master strikes the floor with his cane. GMENTER
+ {
+ FX_STAFF, // sampleId
+ FX_SPOT, // type
+ 28, // delay (or random chance) *
+ { // roomVolList
+ {73,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 287 George descends ladder in 7: GEOASC07 (Reversed) This used to be handled by effect #46 but it didn't fit at all.
+ {
+ FX_SEWLADD7, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {7,8,9}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 288 Sam kicks the recalcitrant Mr. Shiny. DOMKIK
+ {
+ FX_KIKSHINY, // sampleId
+ FX_SPOT, // type
+ 16, // delay (or random chance) *
+ { // roomVolList
+ {33,9,9}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 289 Gust of wind outside bombed cafe. LVSFLY
+ {
+ FX_LVSFLY, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {1,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 290 Ron's disgusting No.1 Sneeze. Either this or the next effect (randomly chosen) is used for the following animations, RONSNZ & RONSNZ2
+ {
+ FX_SNEEZE1, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {20,10,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 291 Ron's disgusting No.2 Sneeze. Either this or the previous effect (randomly chosen) is used for the following animations, RONSNZ & RONSNZ2
+ {
+ FX_SNEEZE2, // sampleId
+ FX_SPOT, // type
+ 11, // delay (or random chance) *
+ { // roomVolList
+ {20,10,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 292 Dripping tap in the pub cellar. TAPDRP
+ {
+ FX_DRIPIRE, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {21,4,4}, // {roomNo,leftVol,rightVol}
+ {26,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 293 Dripping tap in the pub cellar. TAPDRP
+ {
+ FX_DRIPIRE2, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {21,4,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 294 Dripping tap in the excavation toilet. (see WATER43 - but it's looped anyway, not triggered with anim)
+ {
+ FX_TAPDRIP, // sampleId
+ FX_SPOT, // type
+ 6, // delay (or random chance) *
+ { // roomVolList
+ {43,8,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 295 George closes the mausoleum window. GEOSPA23
+ {
+ FX_WINDOW59, // sampleId
+ FX_SPOT, // type
+ 24, // delay (or random chance) *
+ { // roomVolList
+ {59,10,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 296 George opens the mausoleum window, the feebleminded loon. GEOSPA23 reversed.
+ {
+ FX_WINDOW59, // sampleId
+ FX_SPOT, // type
+ 14, // delay (or random chance) *
+ { // roomVolList
+ {59,10,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 297 When George & Nico hear chanting from sc73
+ {
+ FX_CHANT, // sampleId
+ FX_SPOT, // type
+ 10, // delay (or random chance) *
+ { // roomVolList
+ {73,2,4}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 298 EKFIGHT
+ {
+ FX_FIGHT1, // sampleId
+ FX_SPOT, // type
+ 31, // delay (or random chance) *
+ { // roomVolList
+ {74,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 299 Small van passes, left to right. CARA9 and CARC9
+ {
+ FX_LITEVEHR, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {9,16,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 300 Small van passes, right to left to right. CARB9
+ {
+ FX_LITEVEHL, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {9,16,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 301 Truck passes, left to right. TRUCKA9 and TRUCKB9
+ {
+ FX_HVYVEHR, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {9,14,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 302 Truck passes, right to left. TRUCKC9
+ {
+ FX_HVYVEHL, // sampleId
+ FX_SPOT, // type
+ 1, // delay (or random chance) *
+ { // roomVolList
+ {9,14,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 303 With anim FIGHT69
+ {
+ FX_FIGHT69, // sampleId
+ FX_SPOT, // type
+ 78, // delay (or random chance) *
+ { // roomVolList
+ {69,12,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 304 With anim GEODIE1 in sc73
+ {
+ FX_GDROP73, // sampleId
+ FX_SPOT, // type
+ 14, // delay (or random chance) *
+ { // roomVolList
+ {73,12,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 305 With anim GEODIE2 in sc73
+ {
+ FX_GDROP73, // sampleId
+ FX_SPOT, // type
+ 21, // delay (or random chance) *
+ { // roomVolList
+ {73,12,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 306 With anim GEODES25
+ {
+ FX_LADDWN25, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {25,12,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 307 With anim GEOASC25
+ {
+ FX_LADDUP25, // sampleId
+ FX_SPOT, // type
+ 8, // delay (or random chance) *
+ { // roomVolList
+ {25,12,8}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 308 With anim GKSWORD in sc76
+ {
+ FX_GKSWORD, // sampleId
+ FX_SPOT, // type
+ 9, // delay (or random chance) *
+ { // roomVolList
+ {76,10,10}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 309 With anim GEO36KNE in sc36
+ {
+ FX_KEYIN, // sampleId
+ FX_SPOT, // type
+ 18, // delay (or random chance) *
+ { // roomVolList
+ {36,14,14}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 310 With anim GEO36ENT in sc36
+ {
+ FX_COVDWN, // sampleId
+ FX_SPOT, // type
+ 85, // delay (or random chance) *
+ { // roomVolList
+ {36,14,14}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+ // 311 With anim SECDOR59 in sc59
+ {
+ FX_SECDOR59, // sampleId
+ FX_SPOT, // type
+ 0, // delay (or random chance) *
+ { // roomVolList
+ {59,16,16}, // {roomNo,leftVol,rightVol}
+ {0,0,0}, // NULL-TERMINATOR
+ },
+ },
+ //------------------------
+};
+#endif
+//--------------------------------------------------------------------------------------
+// Continuous & random background sound effects for each location
+
+// NB. There must be a list for each room number, even if location doesn't exist in game
+
+const uint16 Sound::_roomsFixedFx[TOTAL_ROOMS][TOTAL_FX_PER_ROOM] =
+{
+ {0}, // 0
+
+ // PARIS 1
+ {2,3,4,5,0}, // 1
+ {2,0}, // 2
+ {2,3,4,5,32,0}, // 3
+ {2,3,4,5,0}, // 4
+ {2,3,4,5,0}, // 5
+ {9,11,12,13,44,45,47}, // 6
+ {9,11,12,13,44,45,47}, // 7
+ {2,3,4,5,0}, // 8
+
+ // PARIS 2
+ {54,63,0}, // 9
+ {51,52,53,54,63,0}, // 10
+ {70,0}, // 11
+ {51,52,70,0}, // 12
+ {0}, // 13
+ {238,0}, // 14
+ {82,0}, // 15
+ {70,81,82,0}, // 16
+ {82,0}, // 17
+ {3,4,5,70,0}, // 18
+
+ // IRELAND
+ {120,121,122,243,0}, // 19
+ {0}, // 20 Violin makes the ambience..
+ {120,121,122,243,0}, // 21
+ {120,121,122,0}, // 22
+ {120,121,122,124,0}, // 23
+ {120,121,122,0}, // 24
+ {0}, // 25
+ {123,243,0}, // 26
+
+ // PARIS 3
+ {135,0}, // 27
+ {202,0}, // 28
+ {202,0}, // 29
+ {0}, // 30
+ {187,0}, // 31
+ {143,145,0}, // 32
+ {143,0}, // 33
+ {143,0}, // 34
+ {0}, // 35
+
+ // PARIS 4
+ {198,0}, // 36
+ {225,0}, // 37
+ {160,0}, // 38
+ {0}, // 39
+ {198,0}, // 40
+ {279,0}, // 41
+ {0}, // 42
+ {279,0}, // 43
+ {0}, // 44 Doesn't exist
+
+ // SYRIA
+ {153,0}, // 45
+ {70,81,0}, // 46 - PARIS 2
+ {153,0}, // 47
+ {160,0}, // 48 - PARIS 4
+ {0}, // 49
+ {153,0}, // 50
+ {0}, // 51
+ {0}, // 52
+ {0}, // 53
+ {130,138,0}, // 54
+ {0}, // 55
+
+ // SPAIN
+ {204,0}, // 56
+ {181,182,184,0}, // 57
+ {181,182,184,0}, // 58
+ {0}, // 59
+ {184,0}, // 60
+ {185,0}, // 61
+ {0}, // 62 Just music
+
+ // NIGHT TRAIN
+ {207,0,0}, // 63
+ {0}, // 64 Doesn't exist
+ {207,0}, // 65
+ {207,0}, // 66
+ {207,0}, // 67
+ {0}, // 68 Disnae exist
+ {0}, // 69
+
+ // SCOTLAND + FINALE
+ {0}, // 70 Disnae exist
+ {199,200,201,242,0}, // 71
+ {199,200,201,242,0}, // 72
+ {0}, // 73
+ {284,0}, // 74
+ {284,0}, // 75
+ {284,0}, // 76
+ {284,0}, // 77
+ {284,0}, // 78
+ {284,0}, // 79
+ {0}, // 80
+ {0}, // 81
+ {0}, // 82
+ {0}, // 83
+ {0}, // 84
+ {0}, // 85
+ {0}, // 86
+ {0}, // 87
+ {0}, // 88
+ {0}, // 89
+ {0}, // 90
+ {0}, // 91
+ {0}, // 92
+ {0}, // 93
+ {0}, // 94
+ {0}, // 95
+ {0}, // 96
+ {0}, // 97
+ {0}, // 98
+ {0}, // 99
+};
+
+#define ENCODE8(VAL) \
+ (uint8)(VAL & 0xFF)
+#define ENCODE16(VAL) \
+ (uint8)(VAL & 0xFF), (uint8)(VAL >> 8)
+#define ENCODE24(VAL) \
+ (uint8)(VAL & 0xFF), (uint8)((VAL >> 8) & 0xFF), (uint8)(VAL >> 16)
+#define ENCODE32(VAL) \
+ (uint8)(VAL & 0xFF), (uint8)((VAL >> 8) & 0xFF), (uint8)((VAL >> 16) & 0xFF), (uint8)(VAL >> 24)
+
+#define LOGIC_CALL_FN(FN_ID, PARAM) \
+ opcCallFn, ENCODE8(FN_ID), ENCODE8(PARAM)
+#define LOGIC_CALL_FN_LONG(FN_ID, PARAM1, PARAM2, PARAM3) \
+ opcCallFnLong, ENCODE8(FN_ID), ENCODE32(PARAM1), ENCODE32(PARAM2), ENCODE32(PARAM3)
+#define LOGIC_SET_VAR8(VAR_ID, VAL) \
+ opcSetVar8, ENCODE16(VAR_ID), ENCODE8(VAL)
+#define LOGIC_SET_VAR16(VAR_ID, VAL) \
+ opcSetVar16, ENCODE16(VAR_ID), ENCODE16(VAL)
+#define LOGIC_SET_VAR32(VAR_ID, VAL) \
+ opcSetVar32, ENCODE16(VAR_ID), ENCODE32(VAL)
+#define GEORGE_POS(POS_X, POS_Y, DIR, PLACE) \
+ opcGeorge, ENCODE16(POS_X), ENCODE16(POS_Y), DIR, ENCODE24(PLACE)
+
+#define INIT_SEQ_END \
+ opcSeqEnd
+#define RUN_START_SCRIPT(SCR_ID) \
+ opcRunStart, ENCODE8(SCR_ID)
+#define RUN_HELPER_SCRIPT(SCR_ID) \
+ opcRunHelper, ENCODE8(SCR_ID)
+
+const uint8 g_startPos0[] = { // Intro with sequence
+ LOGIC_CALL_FN(opcPlaySequence, 4),
+ GEORGE_POS(481, 413, DOWN, FLOOR_1),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos1[] = { // Intro without sequence
+ GEORGE_POS(481, 413, DOWN, FLOOR_1),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos2[] = { // blind alley
+ GEORGE_POS(480, 388, DOWN_LEFT, FLOOR_2),
+ LOGIC_CALL_FN(opcAddObject, LIFTING_KEYS),
+ LOGIC_CALL_FN(opcAddObject, ROSSO_CARD),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos3[] = { // cafe
+ GEORGE_POS(660, 368, DOWN_LEFT, FLOOR_3),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos4[] = { // ready to use the phone
+ GEORGE_POS(463, 391, DOWN, FLOOR_4),
+ LOGIC_SET_VAR8(MOUE_TEXT, 1),
+ LOGIC_SET_VAR8(MOUE_NICO_FLAG, 1),
+ LOGIC_SET_VAR8(PARIS_FLAG, 5),
+ LOGIC_SET_VAR8(NICO_PHONE_FLAG, 1),
+ LOGIC_SET_VAR8(TAILOR_PHONE_FLAG, 1),
+ LOGIC_SET_VAR8(WORKMAN_GONE_FLAG, 1),
+ LOGIC_SET_VAR8(ALBERT_INFO_FLAG, 1),
+ LOGIC_SET_VAR8(SEEN_SEWERS_FLAG, 1),
+ // item stuff missing
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos5[] = { // court yard
+ GEORGE_POS(400, 400, DOWN_LEFT, FLOOR_5),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos7[] = { // sewer two
+ GEORGE_POS(520, 310, DOWN_LEFT, FLOOR_7),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos8[] = { // cafe repaired
+ GEORGE_POS(481, 413, DOWN, FLOOR_8),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos11[] = { // costumier
+ GEORGE_POS(264, 436, DOWN_RIGHT, FLOOR_11),
+ LOGIC_CALL_FN(opcAddObject, TISSUE),
+ LOGIC_CALL_FN(opcAddObject, PHOTOGRAPH),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos12[] = { // hotel street
+ GEORGE_POS(730, 460, LEFT, FLOOR_12),
+ LOGIC_SET_VAR8(NICO_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(NICO_PHONE_FLAG, 1),
+ LOGIC_SET_VAR8(COSTUMES_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(HOTEL_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(AEROPORT_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(TAILOR_PHONE_FLAG, 1),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos14[] = { // hotel corridor
+ GEORGE_POS(528, 484, UP, FLOOR_14),
+ LOGIC_CALL_FN(opcAddObject, HOTEL_KEY),
+ LOGIC_CALL_FN(opcAddObject, MANUSCRIPT),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos17[] = { // hotel assassin
+ GEORGE_POS(714, 484, LEFT, FLOOR_17),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos18[] = { // gendarmerie
+ GEORGE_POS(446, 408, DOWN_LEFT, FLOOR_18),
+ LOGIC_SET_VAR8(PARIS_FLAG, 5),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos19[] = { // ireland street
+ GEORGE_POS(256, 966, UP_RIGHT, FLOOR_19),
+ RUN_HELPER_SCRIPT(HELP_IRELAND)
+};
+
+const uint8 g_startPos20[] = { // macdevitts
+ GEORGE_POS(194, 417, DOWN_RIGHT, FLOOR_20),
+ LOGIC_SET_VAR8(FARMER_MOVED_FLAG, 1),
+ LOGIC_SET_VAR8(FARMER_SEAN_FLAG, 5),
+ LOGIC_SET_VAR8(PUB_FLAP_FLAG, 1),
+ LOGIC_SET_VAR8(PUB_TRAP_DOOR, 2),
+ LOGIC_SET_VAR8(KNOWS_PEAGRAM_FLAG, 1),
+ RUN_HELPER_SCRIPT(HELP_IRELAND)
+};
+
+const uint8 g_startPos21[] = { // pub cellar
+ GEORGE_POS(291, 444, DOWN_RIGHT, FLOOR_21),
+ LOGIC_CALL_FN(opcAddObject, BEER_TOWEL),
+ LOGIC_SET_VAR8(FARMER_MOVED_FLAG, 1),
+ LOGIC_SET_VAR8(FLEECY_STUCK, 1),
+ LOGIC_SET_VAR8(LIFTING_KEYS_IN_HOLE_23, 1),
+ RUN_HELPER_SCRIPT(HELP_IRELAND)
+};
+
+const uint8 g_startPos22[] = { // castle gate
+ GEORGE_POS(547, 500, UP_LEFT, FLOOR_22),
+ LOGIC_SET_VAR8(IRELAND_FLAG, 4),
+ RUN_HELPER_SCRIPT(HELP_IRELAND)
+};
+
+const uint8 g_startPos23[] = { // castle hay top
+ GEORGE_POS(535, 510, UP, FLOOR_23),
+ RUN_HELPER_SCRIPT(HELP_IRELAND)
+};
+
+const uint8 g_startPos24[] = { // castle yard
+ GEORGE_POS(815, 446, DOWN_LEFT, FLOOR_24),
+ RUN_HELPER_SCRIPT(HELP_IRELAND)
+};
+
+const uint8 g_startPos25[] = { // castle dig
+ GEORGE_POS(369, 492, LEFT, FLOOR_25),
+ LOGIC_CALL_FN(opcAddObject, BEER_TOWEL),
+ LOGIC_SET_VAR8(BEER_TOWEL_BEEN_WET, 1),
+ LOGIC_SET_VAR16(WET_BEER_TOWEL_TIMER, 1000),
+ RUN_HELPER_SCRIPT(HELP_IRELAND)
+};
+
+const uint8 g_startPos26[] = { // cellar dark
+ GEORGE_POS(291, 444, DOWN_RIGHT, FLOOR_26),
+ RUN_HELPER_SCRIPT(HELP_IRELAND)
+};
+
+const uint8 g_startPos27[] = { // museum street
+ GEORGE_POS(300, 510, UP_RIGHT, FLOOR_27),
+ LOGIC_SET_VAR8(PARIS_FLAG, 12),
+ LOGIC_SET_VAR8(MANUSCRIPT_ON_TABLE_10_FLAG, 1),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos31[] = { // hospital street
+ GEORGE_POS(400, 500, UP_RIGHT, FLOOR_31),
+ LOGIC_SET_VAR8(PARIS_FLAG, 11),
+ LOGIC_CALL_FN(opcAddObject, PHOTOGRAPH),
+ LOGIC_CALL_FN(opcAddObject, LAB_PASS),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos32[] = { // hospital desk (after we've found out where Marquet is)
+ GEORGE_POS(405, 446, UP_RIGHT, FLOOR_32),
+ LOGIC_SET_VAR8(GOT_BENOIR_FLAG, 1),
+ LOGIC_CALL_FN(opcAddObject, PHOTOGRAPH),
+ LOGIC_CALL_FN(opcAddObject, LAB_PASS),
+ RUN_HELPER_SCRIPT(HELP_WHITECOAT)
+};
+
+const uint8 g_startPos35[] = { // hospital jacques
+ GEORGE_POS(640, 500, LEFT, FLOOR_35),
+ LOGIC_SET_VAR8(DOOR_34_OPEN, 1),
+ LOGIC_SET_VAR8(GOT_BENOIR_FLAG, 2),
+ LOGIC_SET_VAR8(HOS_POS_FLAG, 26),
+ LOGIC_SET_VAR8(BENOIR_FLAG, 24),
+ RUN_HELPER_SCRIPT(HELP_WHITECOAT)
+};
+
+const uint8 g_startPos36[] = { // montfaucon
+ GEORGE_POS(300, 480, RIGHT, FLOOR_36),
+ LOGIC_CALL_FN(opcAddObject, LENS),
+ LOGIC_CALL_FN(opcAddObject, RED_NOSE),
+ LOGIC_CALL_FN(opcAddObject, LIFTING_KEYS),
+ LOGIC_SET_VAR8(MONTFAUCON_CONTROL_FLAG, 1),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos37[] = { // catacomb sewer
+ GEORGE_POS(592, 386, RIGHT, FLOOR_37),
+ LOGIC_CALL_FN(opcAddObject, LIFTING_KEYS),
+ LOGIC_CALL_FN(opcAddObject, TRIPOD),
+ LOGIC_CALL_FN(opcAddObject, GEM),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos38[] = { // catacomb room
+ GEORGE_POS(200, 390, RIGHT, FLOOR_38),
+ LOGIC_CALL_FN(opcAddObject, TRIPOD),
+ LOGIC_CALL_FN(opcAddObject, GEM),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos39[] = { // catacomb meeting
+ GEORGE_POS(636, 413, DOWN_LEFT, FLOOR_39),
+ LOGIC_SET_VAR8(MEETING_FLAG, 3), // meeting finished
+ LOGIC_CALL_FN(opcAddObject, TRIPOD),
+ LOGIC_CALL_FN(opcAddObject, GEM),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos40[] = { // excavation exterior
+ GEORGE_POS(648, 492, LEFT, FLOOR_40),
+ LOGIC_SET_VAR8(NICO_PHONE_FLAG, 1),
+ LOGIC_SET_VAR8(PARIS_FLAG, 16),
+ LOGIC_CALL_FN(opcAddObject, PLASTER),
+ LOGIC_CALL_FN(opcAddObject, POLISHED_CHALICE),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos45[] = { // syria stall
+ GEORGE_POS(410, 490, DOWN_RIGHT, FLOOR_45),
+ RUN_HELPER_SCRIPT(HELP_SYRIA)
+};
+
+const uint8 g_startPos47[] = { // syria carpet
+ GEORGE_POS(225, 775, RIGHT, FLOOR_47),
+ RUN_HELPER_SCRIPT(HELP_SYRIA)
+};
+
+const uint8 g_startPos48[] = { // templar church
+ GEORGE_POS(315, 392, DOWN, FLOOR_48),
+ LOGIC_SET_VAR8(CHALICE_FLAG, 2),
+ LOGIC_SET_VAR8(NEJO_TEXT, 1),
+ LOGIC_CALL_FN(opcAddObject, CHALICE),
+ LOGIC_CALL_FN(opcAddObject, LENS),
+ INIT_SEQ_END
+};
+
+const uint8 g_startPos49[] = { // syria club
+ GEORGE_POS(438, 400, DOWN_RIGHT, FLOOR_49),
+ LOGIC_CALL_FN(opcAddObject, TOILET_BRUSH),
+ RUN_HELPER_SCRIPT(HELP_SYRIA)
+};
+
+const uint8 g_startPos50[] = { // syria toilet
+ GEORGE_POS(313, 440, DOWN_RIGHT, FLOOR_50),
+ LOGIC_CALL_FN(opcAddObject, TOILET_KEY),
+ RUN_HELPER_SCRIPT(HELP_SYRIA)
+};
+
+const uint8 g_startPos53[] = { // bull's head pan
+ LOGIC_SET_VAR32(CHANGE_PLACE, FLOOR_53),
+ LOGIC_CALL_FN(opcAddObject, TOWEL_CUT),
+ RUN_HELPER_SCRIPT(HELP_SYRIA)
+};
+
+const uint8 g_startPos54[] = { // bull's head
+ GEORGE_POS(680, 425, DOWN_LEFT, FLOOR_54),
+ LOGIC_CALL_FN(opcAddObject, TOWEL_CUT),
+ RUN_HELPER_SCRIPT(HELP_SYRIA)
+};
+
+const uint8 g_startPos55[] = { // bull secret
+ GEORGE_POS(825, 373, DOWN_LEFT, FLOOR_55),
+ RUN_HELPER_SCRIPT(HELP_SYRIA)
+};
+
+const uint8 g_startPos56[] = { // contess' room
+ GEORGE_POS(572, 443, LEFT, FLOOR_56),
+ RUN_HELPER_SCRIPT(HELP_SPAIN)
+};
+
+const uint8 g_startPos57[] = { // Spain drive
+ GEORGE_POS(1630, 460, DOWN_LEFT, FLOOR_57),
+ RUN_HELPER_SCRIPT(HELP_SPAIN)
+};
+
+const uint8 g_startPos58[] = { // Mausoleum Exterior
+ GEORGE_POS(SC58_PATH_X, SC58_PATH_Y, UP_RIGHT, FLOOR_58),
+ RUN_HELPER_SCRIPT(HELP_SPAIN)
+};
+
+const uint8 g_startPos59[] = { // Mausoleum interior
+ GEORGE_POS(750, 455, LEFT, FLOOR_59),
+ RUN_HELPER_SCRIPT(HELP_SPAIN)
+};
+
+const uint8 g_startPos60[] = { // Spain reception
+ GEORGE_POS(750, 475, DOWN_LEFT, FLOOR_60),
+ RUN_HELPER_SCRIPT(HELP_SPAIN)
+};
+
+const uint8 g_startPos61[] = { // Spain well
+ GEORGE_POS(400, 345, DOWN, LEFT_FLOOR_61),
+ LOGIC_CALL_FN(opcAddObject, STONE_KEY),
+ LOGIC_CALL_FN(opcAddObject, MIRROR),
+ RUN_HELPER_SCRIPT(HELP_SPAIN)
+};
+
+const uint8 g_startPos62[] = { // chess puzzle
+ LOGIC_SET_VAR32(CHANGE_PLACE, FLOOR_62),
+ LOGIC_SET_VAR8(TOP_MENU_DISABLED, 1),
+ LOGIC_SET_VAR8(GEORGE_ALLOWED_REST_ANIMS, 0),
+ LOGIC_CALL_FN_LONG(opcNoSprite, PLAYER, 0, 0),
+ RUN_HELPER_SCRIPT(HELP_SPAIN)
+};
+
+const uint8 g_startPos63[] = { // train one
+ GEORGE_POS(710, 450, LEFT, FLOOR_63),
+ LOGIC_SET_VAR8(DOOR_SC65_FLAG, 2),
+ LOGIC_SET_VAR8(DOOR_ONE_63_OPEN, 0),
+ LOGIC_SET_VAR8(DOOR_65_OPEN, 1),
+ LOGIC_SET_VAR8(VAIL_TEXT, 1),
+ RUN_HELPER_SCRIPT(HELP_NIGHTTRAIN)
+};
+
+const uint8 g_startPos65[] = { // compt one
+ GEORGE_POS(460, 430, DOWN, FLOOR_65),
+ RUN_HELPER_SCRIPT(HELP_NIGHTTRAIN)
+};
+
+const uint8 g_startPos66[] = { // compt two
+ GEORGE_POS(460, 430, DOWN, FLOOR_66),
+ RUN_HELPER_SCRIPT(HELP_NIGHTTRAIN)
+};
+
+const uint8 g_startPos67[] = { // compt three
+ GEORGE_POS(460, 430, DOWN, FLOOR_67),
+ RUN_HELPER_SCRIPT(HELP_NIGHTTRAIN)
+};
+
+const uint8 g_startPos69[] = { // train_guard
+ GEORGE_POS(310, 430, DOWN, FLOOR_69),
+ RUN_HELPER_SCRIPT(HELP_NIGHTTRAIN)
+};
+
+const uint8 g_startPos71[] = { // churchyard
+ GEORGE_POS(1638, 444, LEFT, RIGHT_FLOOR_71),
+ LOGIC_SET_VAR8(NICO_SCOT_SCREEN, 71),
+ LOGIC_SET_VAR8(NICO_POSITION_71, 1),
+ RUN_HELPER_SCRIPT(HELP_SCOTLAND)
+};
+
+const uint8 g_startPos72[] = { // church tower
+ GEORGE_POS(150, 503, RIGHT, FLOOR_72),
+ LOGIC_SET_VAR8(NICO_SCOT_SCREEN, 72),
+ RUN_HELPER_SCRIPT(HELP_SCOTLAND)
+};
+
+const uint8 g_startPos73[] = { // crypt
+ GEORGE_POS(250, 390, DOWN_RIGHT, FLOOR_73),
+ LOGIC_SET_VAR8(NICO_SCOT_SCREEN, 73),
+ LOGIC_SET_VAR8(NICO_POSITION_73, 1)
+};
+
+const uint8 g_startPos80[] = { // Paris map
+ GEORGE_POS(645, 160, DOWN, FLOOR_80),
+ LOGIC_SET_VAR8(PARIS_FLAG, 3),
+ LOGIC_SET_VAR8(NICO_CLOWN_FLAG, 3),
+ LOGIC_SET_VAR8(NICO_DOOR_FLAG, 2),
+
+ LOGIC_CALL_FN(opcAddObject, RED_NOSE),
+ LOGIC_CALL_FN(opcAddObject, PHOTOGRAPH),
+ LOGIC_CALL_FN(opcAddObject, PLASTER),
+ LOGIC_CALL_FN(opcAddObject, LAB_PASS),
+
+ LOGIC_SET_VAR8(MANUSCRIPT_FLAG, 1),
+ LOGIC_SET_VAR8(NICO_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(NICO_PHONE_FLAG, 1),
+ LOGIC_SET_VAR8(COSTUMES_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(HOTEL_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(MUSEUM_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(HOSPITAL_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(MONTFACN_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(AEROPORT_ADDRESS_FLAG, 1),
+ LOGIC_SET_VAR8(NERVAL_ADDRESS_FLAG, 1),
+
+ LOGIC_SET_VAR8(IRELAND_MAP_FLAG, 1),
+ LOGIC_SET_VAR8(SPAIN_MAP_FLAG, 1),
+ LOGIC_SET_VAR8(SYRIA_FLAG, 2),
+
+ LOGIC_SET_VAR8(TAILOR_PHONE_FLAG, 1),
+ INIT_SEQ_END
+};
+
+const uint8 g_genIreland[] = {
+ LOGIC_SET_VAR8(PARIS_FLAG, 9),
+ LOGIC_CALL_FN(opcAddObject, RED_NOSE),
+ LOGIC_CALL_FN(opcAddObject, PHOTOGRAPH),
+ LOGIC_CALL_FN(opcAddObject, LAB_PASS),
+ LOGIC_CALL_FN(opcAddObject, LIFTING_KEYS),
+ LOGIC_CALL_FN(opcAddObject, MATCHBOOK),
+ LOGIC_CALL_FN(opcAddObject, BUZZER),
+ LOGIC_CALL_FN(opcAddObject, TISSUE),
+ INIT_SEQ_END
+};
+
+const uint8 g_genSyria[] = {
+ LOGIC_SET_VAR8(PARIS_FLAG, 1),
+ LOGIC_CALL_FN(opcAddObject, BALL),
+ LOGIC_CALL_FN(opcAddObject, RED_NOSE),
+ LOGIC_CALL_FN(opcAddObject, PHOTOGRAPH),
+ LOGIC_CALL_FN(opcAddObject, LIFTING_KEYS),
+ LOGIC_CALL_FN(opcAddObject, MATCHBOOK),
+ LOGIC_CALL_FN(opcAddObject, BUZZER),
+ LOGIC_CALL_FN(opcAddObject, TISSUE),
+ LOGIC_SET_VAR8(CHANGE_STANCE, STAND),
+ INIT_SEQ_END
+};
+
+const uint8 g_genSpain[] = {
+ LOGIC_SET_VAR8(PARIS_FLAG, 1),
+ LOGIC_SET_VAR8(SPAIN_VISIT, 1), // default to 1st spain visit, may get overwritten later
+ LOGIC_CALL_FN(opcAddObject, RED_NOSE),
+ LOGIC_CALL_FN(opcAddObject, PHOTOGRAPH),
+ LOGIC_CALL_FN(opcAddObject, LAB_PASS),
+ LOGIC_CALL_FN(opcAddObject, LIFTING_KEYS),
+ LOGIC_CALL_FN(opcAddObject, BUZZER),
+ LOGIC_CALL_FN(opcAddObject, TISSUE),
+ LOGIC_CALL_FN(opcAddObject, BALL),
+ LOGIC_CALL_FN(opcAddObject, MATCHBOOK),
+ LOGIC_CALL_FN(opcAddObject, PRESSURE_GAUGE),
+ INIT_SEQ_END
+};
+
+const uint8 g_genSpain2[] = { // 2nd spain visit
+ LOGIC_SET_VAR8(SPAIN_VISIT, 2),
+ LOGIC_CALL_FN(opcRemoveObject, PRESSURE_GAUGE),
+ LOGIC_CALL_FN(opcAddObject, POLISHED_CHALICE),
+ INIT_SEQ_END
+};
+
+const uint8 g_genNightTrain[] = {
+ LOGIC_SET_VAR8(PARIS_FLAG, 18),
+ INIT_SEQ_END
+};
+
+const uint8 g_genScotland[] = {
+ LOGIC_SET_VAR8(PARIS_FLAG, 1),
+ LOGIC_CALL_FN(opcAddObject, RED_NOSE),
+ LOGIC_CALL_FN(opcAddObject, PHOTOGRAPH),
+ LOGIC_CALL_FN(opcAddObject, LAB_PASS),
+ LOGIC_CALL_FN(opcAddObject, LIFTING_KEYS),
+ LOGIC_CALL_FN(opcAddObject, BUZZER),
+ INIT_SEQ_END
+};
+
+const uint8 g_genWhiteCoat[] = {
+ LOGIC_SET_VAR8(PARIS_FLAG, 11),
+ LOGIC_SET_VAR8(EVA_TEXT, 1),
+ LOGIC_SET_VAR8(EVA_MARQUET_FLAG, 2),
+ LOGIC_SET_VAR8(EVA_NURSE_FLAG, 4),
+ LOGIC_SET_VAR8(FOUND_WARD_FLAG, 1),
+ LOGIC_SET_VAR8(CONSULTANT_HERE, 1),
+
+ LOGIC_CALL_FN_LONG(opcMegaSet, PLAYER, GEORGE_WLK, MEGA_WHITE),
+
+ LOGIC_SET_VAR32(GEORGE_CDT_FLAG, WHT_TLK_TABLE),
+ LOGIC_SET_VAR8(GEORGE_TALK_FLAG, 0),
+ LOGIC_SET_VAR8(WHITE_COAT_FLAG, 1),
+ LOGIC_SET_VAR8(GEORGE_ALLOWED_REST_ANIMS, 0),
+ INIT_SEQ_END
+};
+
+const uint8 *Logic::_startData[] = {
+ g_startPos0,
+ g_startPos1,
+ g_startPos2,
+ g_startPos3,
+ g_startPos4,
+ g_startPos5,
+ NULL, //g_startPos6,
+ g_startPos7,
+ g_startPos8,
+ NULL, //g_startPos9,
+ NULL, //g_startPos10,
+ g_startPos11,
+ g_startPos12,
+ NULL, //g_startPos13,
+ g_startPos14,
+ NULL, //g_startPos15,
+ NULL, //g_startPos16,
+ g_startPos17,
+ g_startPos18,
+ g_startPos19,
+ g_startPos20,
+ g_startPos21,
+ g_startPos22,
+ g_startPos23,
+ g_startPos24,
+ g_startPos25,
+ g_startPos26,
+ g_startPos27,
+ NULL, //g_startPos28,
+ NULL, //g_startPos29,
+ NULL, //g_startPos30,
+ g_startPos31,
+ g_startPos32,
+ NULL, //g_startPos33,
+ NULL, //g_startPos34,
+ g_startPos35,
+ g_startPos36,
+ g_startPos37,
+ g_startPos38,
+ g_startPos39,
+ g_startPos40,
+ NULL, //g_startPos41,
+ NULL, //g_startPos42,
+ NULL, //g_startPos43,
+ NULL, //g_startPos44,
+ g_startPos45,
+ NULL, //g_startPos46,
+ g_startPos47,
+ g_startPos48,
+ g_startPos49,
+ g_startPos50,
+ NULL, //g_startPos51,
+ NULL, //g_startPos52,
+ g_startPos53,
+ g_startPos54,
+ g_startPos55,
+ g_startPos56,
+ g_startPos57,
+ g_startPos58,
+ g_startPos59,
+ g_startPos60,
+ g_startPos61,
+ g_startPos62,
+ g_startPos63,
+ NULL, //g_startPos64,
+ g_startPos65,
+ g_startPos66,
+ g_startPos67,
+ NULL, //g_startPos68,
+ g_startPos69,
+ NULL, //g_startPos70,
+ g_startPos71,
+ g_startPos72,
+ g_startPos73,
+ NULL, //g_startPos74,
+ NULL, //g_startPos75,
+ NULL, //g_startPos76
+ NULL, //g_startPos77
+ NULL, //g_startPos78
+ NULL, //g_startPos79
+ g_startPos80
+};
+
+const uint8 *Logic::_helperData[] = {
+ g_genIreland,
+ g_genSyria,
+ g_genSpain,
+ g_genNightTrain,
+ g_genScotland,
+ g_genWhiteCoat,
+ g_genSpain2
+};
+
+} // End of namespace Sword1
+
+#ifdef PALMOS_68K
+#include "scumm_globals.h"
+
+_GINIT(Sword1_fxList)
+_GSETPTR(Sword1::Sound::_fxList, GBVARS_FXLIST_INDEX, Sword1::FxDef, GBVARS_SWORD1)
+_GEND
+
+_GRELEASE(Sword1_fxList)
+_GRELEASEPTR(GBVARS_FXLIST_INDEX, GBVARS_SWORD1)
+_GEND
+
+#endif
diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp
new file mode 100644
index 0000000000..1f49426262
--- /dev/null
+++ b/engines/sword1/sword1.cpp
@@ -0,0 +1,615 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/sword1.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/plugins.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/timer.h"
+#include "common/system.h"
+
+#include "sword1/resman.h"
+#include "sword1/objectman.h"
+#include "sword1/mouse.h"
+#include "sword1/logic.h"
+#include "sword1/sound.h"
+#include "sword1/screen.h"
+#include "sword1/swordres.h"
+#include "sword1/menu.h"
+#include "sword1/music.h"
+#include "sword1/control.h"
+
+#include "gui/message.h"
+#include "gui/newgui.h"
+
+using namespace Sword1;
+
+/* Broken Sword 1 */
+static const GameSettings sword1FullSettings =
+ {"sword1", "Broken Sword 1: The Shadow of the Templars", GF_DEFAULT_TO_1X_SCALER};
+static const GameSettings sword1DemoSettings =
+ {"sword1demo", "Broken Sword 1: The Shadow of the Templars (Demo)", GF_DEFAULT_TO_1X_SCALER | Sword1::GF_DEMO };
+
+// check these subdirectories (if present)
+static const char *g_dirNames[] = { "clusters", "speech" };
+
+#define NUM_FILES_TO_CHECK 5
+static const char *g_filesToCheck[NUM_FILES_TO_CHECK] = { // these files have to be found
+ "swordres.rif",
+ "general.clu",
+ "compacts.clu",
+ "scripts.clu",
+ "cows.mad", // this one should only exist in the demo version
+ // the engine needs several more files to work, but checking these should be sufficient
+};
+
+GameList Engine_SWORD1_gameList() {
+ GameList games;
+ games.push_back(sword1FullSettings);
+ games.push_back(sword1DemoSettings);
+ return games;
+}
+
+void Sword1CheckDirectory(const FSList &fslist, bool *filesFound) {
+ for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (!file->isDirectory()) {
+ const char *fileName = file->displayName().c_str();
+ for (int cnt = 0; cnt < NUM_FILES_TO_CHECK; cnt++)
+ if (scumm_stricmp(fileName, g_filesToCheck[cnt]) == 0)
+ filesFound[cnt] = true;
+ } else {
+ for (int cnt = 0; cnt < ARRAYSIZE(g_dirNames); cnt++)
+ if (scumm_stricmp(file->displayName().c_str(), g_dirNames[cnt]) == 0)
+ Sword1CheckDirectory(file->listDir(AbstractFilesystemNode::kListFilesOnly), filesFound);
+ }
+ }
+}
+
+DetectedGameList Engine_SWORD1_detectGames(const FSList &fslist) {
+ int i;
+ DetectedGameList detectedGames;
+ bool filesFound[NUM_FILES_TO_CHECK];
+ for (i = 0; i < NUM_FILES_TO_CHECK; i++)
+ filesFound[i] = false;
+
+ Sword1CheckDirectory(fslist, filesFound);
+ bool mainFilesFound = true;
+ for (i = 0; i < NUM_FILES_TO_CHECK -1; i++)
+ if (!filesFound[i])
+ mainFilesFound = false;
+
+ if (mainFilesFound && filesFound[NUM_FILES_TO_CHECK - 1])
+ detectedGames.push_back(sword1DemoSettings);
+ else if (mainFilesFound)
+ detectedGames.push_back(sword1FullSettings);
+
+ return detectedGames;
+}
+
+Engine *Engine_SWORD1_create(GameDetector *detector, OSystem *syst) {
+ return new SwordEngine(detector, syst);
+}
+
+REGISTER_PLUGIN(SWORD1, "Broken Sword")
+
+namespace Sword1 {
+
+SystemVars SwordEngine::_systemVars;
+
+void SwordEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+}
+
+SwordEngine::SwordEngine(GameDetector *detector, OSystem *syst)
+ : Engine(syst) {
+
+ _features = detector->_game.features;
+
+ if (!_mixer->isReady())
+ warning("Sound initialization failed");
+
+ // Add default file directories
+ Common::File::addDefaultDirectory(_gameDataPath + "CLUSTERS/");
+ Common::File::addDefaultDirectory(_gameDataPath + "MUSIC/");
+ Common::File::addDefaultDirectory(_gameDataPath + "SPEECH/");
+ Common::File::addDefaultDirectory(_gameDataPath + "VIDEO/");
+ Common::File::addDefaultDirectory(_gameDataPath + "clusters/");
+ Common::File::addDefaultDirectory(_gameDataPath + "music/");
+ Common::File::addDefaultDirectory(_gameDataPath + "speech/");
+ Common::File::addDefaultDirectory(_gameDataPath + "video/");
+}
+
+SwordEngine::~SwordEngine() {
+ delete _control;
+ delete _logic;
+ delete _menu;
+ delete _sound;
+ delete _music;
+ delete _screen;
+ delete _mouse;
+ delete _objectMan;
+ delete _resMan;
+}
+
+int SwordEngine::init(GameDetector &detector) {
+
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ _system->initSize(640, 480);
+ _system->endGFXTransaction();
+
+ checkCdFiles();
+
+ debug(5, "Starting resource manager");
+ _resMan = new ResMan("swordres.rif");
+ debug(5, "Starting object manager");
+ _objectMan = new ObjectMan(_resMan);
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, Audio::Mixer::kMaxMixerVolume);
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, Audio::Mixer::kMaxMixerVolume);
+ _mouse = new Mouse(_system, _resMan, _objectMan);
+ _screen = new Screen(_system, _resMan, _objectMan);
+ _music = new Music(_mixer);
+ _sound = new Sound("", _mixer, _resMan);
+ _menu = new Menu(_screen, _mouse);
+ _logic = new Logic(_objectMan, _resMan, _screen, _mouse, _sound, _music, _menu, _system, _mixer);
+ _mouse->useLogicAndMenu(_logic, _menu);
+
+ uint musicVol = ConfMan.getInt("music_volume");
+ uint speechVol = ConfMan.getInt("speech_volume");
+ uint sfxVol = ConfMan.getInt("sfx_volume");
+ if (musicVol > 255)
+ musicVol = 255;
+ if (speechVol > 255)
+ speechVol = 255;
+ if (sfxVol > 255)
+ sfxVol = 255;
+
+ _music->setVolume(musicVol, musicVol); // these routines expect left and right volume,
+ _sound->setSpeechVol(speechVol, speechVol); // but our config manager doesn't support it.
+ _sound->setSfxVol(sfxVol, sfxVol);
+
+ _systemVars.justRestoredGame = 0;
+ _systemVars.currentCD = 0;
+ _systemVars.controlPanelMode = CP_NEWGAME;
+ _systemVars.forceRestart = false;
+ _systemVars.wantFade = true;
+ _systemVars.engineQuit = false;
+
+ switch (Common::parseLanguage(ConfMan.get("language"))) {
+ case Common::DE_DEU:
+ _systemVars.language = BS1_GERMAN;
+ break;
+ case Common::FR_FRA:
+ _systemVars.language = BS1_FRENCH;
+ break;
+ case Common::IT_ITA:
+ _systemVars.language = BS1_ITALIAN;
+ break;
+ case Common::ES_ESP:
+ _systemVars.language = BS1_SPANISH;
+ break;
+ case Common::PT_BRA:
+ _systemVars.language = BS1_PORT;
+ break;
+ case Common::CZ_CZE:
+ _systemVars.language = BS1_CZECH;
+ break;
+ default:
+ _systemVars.language = BS1_ENGLISH;
+ }
+
+ _systemVars.showText = ConfMan.getBool("subtitles");
+
+ _systemVars.playSpeech = 1;
+ _mouseState = 0;
+
+ _logic->initialize();
+ _objectMan->initialize();
+ _mouse->initialize();
+ _control = new Control(_saveFileMan, _resMan, _objectMan, _system, _mouse, _sound, _music);
+
+ return 0;
+}
+
+void SwordEngine::reinitialize(void) {
+ _resMan->flush(); // free everything that's currently alloced and opened. (*evil*)
+
+ _logic->initialize(); // now reinitialize these objects as they (may) have locked
+ _objectMan->initialize(); // resources which have just been wiped.
+ _mouse->initialize();
+ _system->warpMouse(320, 240);
+ _systemVars.wantFade = true;
+}
+
+void SwordEngine::flagsToBool(bool *dest, uint8 flags) {
+ uint8 bitPos = 0;
+ while (flags) {
+ if (flags & 1)
+ dest[bitPos] = true;
+ flags >>= 1;
+ bitPos++;
+ }
+}
+
+static const char *errorMsgs[] = {
+ "The file \"%s\" is missing and the game doesn't work without it.\n"
+ "Please copy it from CD %d and try starting the game again.\n"
+ "The Readme file also contains further information.",
+
+ "%d important files are missing, the game can't start without them.\n"
+ "Please copy these files from their corresponding CDs:\n",
+
+ "The file \"%s\" is missing.\n"
+ "Even though the game may initially seem to\n"
+ "work fine, it will crash when it needs the\n"
+ "data from this file and you will be thrown back to your last savegame.\n"
+ "Please copy the file from CD %d and start the game again.",
+
+ "%d files are missing.\n"
+ "Even though the game may initially seem to\n"
+ "work fine, it will crash when it needs the\n"
+ "data from these files and you will be thrown back to your last savegame.\n"
+ "Please copy these files from their corresponding CDs:\n"
+};
+
+const CdFile SwordEngine::_cdFileList[] = {
+ { "paris2.clu", FLAG_CD1 },
+ { "ireland.clu", FLAG_CD2 },
+ { "paris3.clu", FLAG_CD1 },
+ { "paris4.clu", FLAG_CD1 },
+ { "scotland.clu", FLAG_CD2 },
+ { "spain.clu", FLAG_CD2 },
+ { "syria.clu", FLAG_CD2 },
+ { "train.clu", FLAG_CD2 },
+ { "compacts.clu", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
+ { "general.clu", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
+ { "maps.clu", FLAG_CD1 | FLAG_DEMO },
+ { "paris1.clu", FLAG_CD1 | FLAG_DEMO },
+ { "scripts.clu", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
+ { "swordres.rif", FLAG_CD1 | FLAG_DEMO | FLAG_IMMED },
+ { "text.clu", FLAG_CD1 | FLAG_DEMO },
+ { "cows.mad", FLAG_DEMO },
+ { "speech1.clu", FLAG_SPEECH1 },
+ { "speech2.clu", FLAG_SPEECH2 }
+#ifdef USE_MAD
+ ,{ "speech1.cl3", FLAG_SPEECH1 },
+ { "speech2.cl3", FLAG_SPEECH2 }
+#endif
+#ifdef USE_VORBIS
+ ,{ "speech1.clv", FLAG_SPEECH1 },
+ { "speech2.clv", FLAG_SPEECH2 }
+#endif
+};
+
+void SwordEngine::showFileErrorMsg(uint8 type, bool *fileExists) {
+ char msg[1024];
+ int missCnt = 0, missNum = 0;
+ for (int i = 0; i < ARRAYSIZE(_cdFileList); i++)
+ if (!fileExists[i]) {
+ missCnt++;
+ missNum = i;
+ }
+ assert(missCnt > 0); // this function shouldn't get called if there's nothing missing.
+ warning("%d files missing", missCnt);
+ int msgId = (type == TYPE_IMMED) ? 0 : 2;
+ if (missCnt == 1) {
+ sprintf(msg, errorMsgs[msgId],
+ _cdFileList[missNum].name, (_cdFileList[missNum].flags & FLAG_CD2) ? 2 : 1);
+ warning(msg);
+ } else {
+ char *pos = msg + sprintf(msg, errorMsgs[msgId + 1], missCnt);
+ warning(msg);
+ for (int i = 0; i < ARRAYSIZE(_cdFileList); i++)
+ if (!fileExists[i]) {
+ warning("\"%s\" (CD %d)", _cdFileList[i].name, (_cdFileList[i].flags & FLAG_CD2) ? 2 : 1);
+ pos += sprintf(pos, "\"%s\" (CD %d)\n", _cdFileList[i].name, (_cdFileList[i].flags & FLAG_CD2) ? 2 : 1);
+ }
+ }
+ GUI::MessageDialog dialog(msg);
+ dialog.runModal();
+ if (type == TYPE_IMMED) // we can't start without this file, so error() out.
+ error(msg);
+}
+
+void SwordEngine::checkCdFiles(void) { // check if we're running from cd, hdd or what...
+ Common::File test;
+ bool fileExists[30];
+ bool isFullVersion = false; // default to demo version
+ bool missingTypes[8] = { false, false, false, false, false, false, false, false };
+ bool foundTypes[8] = { false, false, false, false, false, false, false, false };
+ bool cd2FilesFound = false;
+ _systemVars.runningFromCd = false;
+ _systemVars.playSpeech = true;
+
+ // check all files and look out if we can find a file that wouldn't exist if this was the demo version
+ for (int fcnt = 0; fcnt < ARRAYSIZE(_cdFileList); fcnt++) {
+ if (test.open(_cdFileList[fcnt].name)) {
+ test.close();
+ fileExists[fcnt] = true;
+ flagsToBool(foundTypes, _cdFileList[fcnt].flags);
+ if (!(_cdFileList[fcnt].flags & FLAG_DEMO))
+ isFullVersion = true;
+ if (_cdFileList[fcnt].flags & FLAG_CD2)
+ cd2FilesFound = true;
+ } else {
+ flagsToBool(missingTypes, _cdFileList[fcnt].flags);
+ fileExists[fcnt] = false;
+ }
+ }
+
+ if (((_features & GF_DEMO) == 0) != isFullVersion) // shouldn't happen...
+ warning("Your Broken Sword 1 version looks like a %s version but you are starting it as a %s version", isFullVersion ? "full" : "demo", (_features & GF_DEMO) ? "demo" : "full");
+
+ if (foundTypes[TYPE_SPEECH1]) // we found some kind of speech1 file (.clu, .cl3, .clv)
+ missingTypes[TYPE_SPEECH1] = false; // so we don't care if there's a different kind missing
+ if (foundTypes[TYPE_SPEECH2]) // same for speech2
+ missingTypes[TYPE_SPEECH2] = false;
+
+ if (isFullVersion) // if this is the full version...
+ missingTypes[TYPE_DEMO] = false; // then we don't need demo files...
+ else // and vice versa
+ missingTypes[TYPE_SPEECH1] = missingTypes[TYPE_SPEECH2] = missingTypes[TYPE_CD1] = missingTypes[TYPE_CD2] = false;
+
+ bool somethingMissing = false;
+ for (int i = 0; i < 8; i++)
+ somethingMissing |= missingTypes[i];
+ if (somethingMissing) { // okay, there *are* files missing
+ // first, update the fileExists[] array depending on our changed missingTypes
+ for (int fileCnt = 0; fileCnt < ARRAYSIZE(_cdFileList); fileCnt++)
+ if (!fileExists[fileCnt]) {
+ fileExists[fileCnt] = true;
+ for (int flagCnt = 0; flagCnt < 8; flagCnt++)
+ if (missingTypes[flagCnt] && ((_cdFileList[fileCnt].flags & (1 << flagCnt)) != 0))
+ fileExists[fileCnt] = false; // this is one of the files we were looking for
+ }
+ if (missingTypes[TYPE_IMMED]) {
+ // important files missing, can't start the game without them
+ showFileErrorMsg(TYPE_IMMED, fileExists);
+ } else if ((!missingTypes[TYPE_CD1]) && !cd2FilesFound) {
+ /* we have all the data from cd one, but not a single one from CD2.
+ I'm not sure how we should handle this, for now I'll just assume that the
+ user has set up the extrapath correctly and copied the necessary files to HDD.
+ A quite optimistic assumption, I'd say. Maybe we should change this for the release
+ to warn the user? */
+ warning("CD2 data files not found. I hope you know what you're doing and that\n"
+ "you have set up the extrapath and additional data correctly.\n"
+ "If you didn't, you should better read the ScummVM readme file");
+ _systemVars.runningFromCd = true;
+ _systemVars.playSpeech = true;
+ } else if (missingTypes[TYPE_CD1] || missingTypes[TYPE_CD2]) {
+ // several files from CD1 both CDs are missing. we can probably start, but it'll crash sooner or later
+ showFileErrorMsg(TYPE_CD1, fileExists);
+ } else if (missingTypes[TYPE_SPEECH1] || missingTypes[TYPE_SPEECH2]) {
+ // not so important, but there won't be any voices
+ if (missingTypes[TYPE_SPEECH1] && missingTypes[TYPE_SPEECH2])
+ warning("Unable to find the speech files. The game will work, but you won't hear any voice output.\n"
+ "Please copy the SPEECH.CLU files from both CDs and rename them to SPEECH1.CLU and SPEECH2.CLU,\n"
+ "corresponding to the CD number.\n"
+ "Please read the ScummVM Readme file for more information");
+ else
+ warning("Unable to find the speech file from CD %d.\n"
+ "You won't hear any voice output in that part of the game.\n"
+ "Please read the ScummVM Readme file for more information", missingTypes[TYPE_SPEECH1] ? 1 : 2);
+ } else if (missingTypes[TYPE_DEMO]) {
+ // for the demo version, we simply expect to have all files immediately
+ showFileErrorMsg(TYPE_IMMED, fileExists);
+ }
+ } // everything's fine, let's play.
+ /*if (!isFullVersion)
+ _systemVars.isDemo = true;
+ */
+ // make the demo flag depend on the Gamesettings for now, and not on what the datafiles look like
+ _systemVars.isDemo = (_features & GF_DEMO) != 0;
+ _systemVars.cutscenePackVersion = 0;
+#ifdef USE_MPEG2
+ if (test.open("intro.snd")) {
+ _systemVars.cutscenePackVersion = 1;
+ test.close();
+ }
+#endif
+}
+
+int SwordEngine::go() {
+
+ uint16 startPos = ConfMan.getInt("boot_param");
+ if (startPos)
+ _logic->startPositions(startPos);
+ else {
+ if (_control->savegamesExist()) {
+ _systemVars.controlPanelMode = CP_NEWGAME;
+ if (_control->runPanel() == CONTROL_GAME_RESTORED)
+ _control->doRestore();
+ else if (!_systemVars.engineQuit)
+ _logic->startPositions(0);
+ } else // no savegames, start new game.
+ _logic->startPositions(0);
+ }
+ _systemVars.controlPanelMode = CP_NORMAL;
+
+ while (!_systemVars.engineQuit) {
+ uint8 action = mainLoop();
+
+ if (!_systemVars.engineQuit) {
+ // the mainloop was left, we have to reinitialize.
+ reinitialize();
+ if (action == CONTROL_GAME_RESTORED)
+ _control->doRestore();
+ else if (action == CONTROL_RESTART_GAME)
+ _logic->startPositions(1);
+ _systemVars.forceRestart = false;
+ _systemVars.controlPanelMode = CP_NORMAL;
+ }
+ }
+
+ return 0;
+}
+
+void SwordEngine::checkCd(void) {
+ uint8 needCd = _cdList[Logic::_scriptVars[NEW_SCREEN]];
+ if (_systemVars.runningFromCd) { // are we running from cd?
+ if (needCd == 0) { // needCd == 0 means we can use either CD1 or CD2.
+ if (_systemVars.currentCD == 0) {
+ _systemVars.currentCD = 1; // if there is no CD currently inserted, ask for CD1.
+ _control->askForCd();
+ } // else: there is already a cd inserted and we don't care if it's cd1 or cd2.
+ } else if (needCd != _systemVars.currentCD) { // we need a different CD than the one in drive.
+ _music->startMusic(0, 0); //
+ _sound->closeCowSystem(); // close music and sound files before changing CDs
+ _systemVars.currentCD = needCd; // askForCd will ask the player to insert _systemVars.currentCd,
+ _control->askForCd(); // so it has to be updated before calling it.
+ }
+ } else { // we're running from HDD, we don't have to care about music files and Sound will take care of
+ if (needCd) // switching sound.clu files on Sound::newScreen by itself, so there's nothing to be done.
+ _systemVars.currentCD = needCd;
+ else if (_systemVars.currentCD == 0)
+ _systemVars.currentCD = 1;
+ }
+}
+
+uint8 SwordEngine::mainLoop(void) {
+ uint8 retCode = 0;
+ _keyPressed = 0;
+
+ while ((retCode == 0) && (!_systemVars.engineQuit)) {
+ // do we need the section45-hack from sword.c here?
+ checkCd();
+
+ _screen->newScreen(Logic::_scriptVars[NEW_SCREEN]);
+ _logic->newScreen(Logic::_scriptVars[NEW_SCREEN]);
+ _sound->newScreen(Logic::_scriptVars[NEW_SCREEN]);
+ Logic::_scriptVars[SCREEN] = Logic::_scriptVars[NEW_SCREEN];
+
+ do {
+ uint32 newTime;
+ bool scrollFrameShown = false;
+
+ uint32 frameTime = _system->getMillis();
+ _logic->engine();
+ _logic->updateScreenParams(); // sets scrolling
+
+ _screen->draw();
+ _mouse->animate();
+ _sound->engine();
+ _menu->refresh(MENU_TOP);
+ _menu->refresh(MENU_BOT);
+
+ newTime = _system->getMillis();
+ if (newTime - frameTime < 1000 / FRAME_RATE) {
+ scrollFrameShown = _screen->showScrollFrame();
+ delay((1000 / (FRAME_RATE * 2)) - (_system->getMillis() - frameTime));
+ }
+
+ newTime = _system->getMillis();
+ if ((newTime - frameTime < 1000 / FRAME_RATE) || (!scrollFrameShown))
+ _screen->updateScreen();
+ delay((1000 / FRAME_RATE) - (_system->getMillis() - frameTime));
+
+ _mouse->engine( _mouseX, _mouseY, _mouseState);
+
+ if (_systemVars.forceRestart)
+ retCode = CONTROL_RESTART_GAME;
+
+ // The control panel is triggered by F5 or ESC.
+ // FIXME: This is a very strange way of detecting F5...
+ else if (((_keyPressed == 63 || _keyPressed == 27) && (Logic::_scriptVars[MOUSE_STATUS] & 1)) || (_systemVars.controlPanelMode)) {
+ retCode = _control->runPanel();
+ if (!retCode)
+ _screen->fullRefresh();
+ }
+ _mouseState = _keyPressed = 0;
+ } while ((Logic::_scriptVars[SCREEN] == Logic::_scriptVars[NEW_SCREEN]) && (retCode == 0) && (!_systemVars.engineQuit));
+
+ if ((retCode == 0) && (Logic::_scriptVars[SCREEN] != 53) && _systemVars.wantFade && (!_systemVars.engineQuit)) {
+ _screen->fadeDownPalette();
+ int32 relDelay = (int32)_system->getMillis();
+ while (_screen->stillFading()) {
+ relDelay += (1000 / FRAME_RATE);
+ _screen->updateScreen();
+ delay(relDelay - (int32)_system->getMillis());
+ }
+ }
+
+ _sound->quitScreen();
+ _screen->quitScreen(); // close graphic resources
+ _objectMan->closeSection(Logic::_scriptVars[SCREEN]); // close the section that PLAYER has just left, if it's empty now
+ }
+ return retCode;
+}
+
+void SwordEngine::delay(int32 amount) { //copied and mutilated from sky.cpp
+
+ OSystem::Event event;
+ uint32 start = _system->getMillis();
+
+ do {
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ // Make sure backspace works right (this fixes a small issue on OS X)
+ if (event.kbd.keycode == 8)
+ _keyPressed = 8;
+ else
+ _keyPressed = (uint8)event.kbd.ascii;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ _mouseState |= BS1L_BUTTON_DOWN;
+#if defined(_WIN32_WCE) || defined(PALMOS_MODE)
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+#endif
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+ _mouseState |= BS1R_BUTTON_DOWN;
+#if defined(_WIN32_WCE) || defined(PALMOS_MODE)
+ _mouseX = event.mouse.x;
+ _mouseY = event.mouse.y;
+#endif
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _mouseState |= BS1L_BUTTON_UP;
+ break;
+ case OSystem::EVENT_RBUTTONUP:
+ _mouseState |= BS1R_BUTTON_UP;
+ break;
+ case OSystem::EVENT_QUIT:
+ _systemVars.engineQuit = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (amount > 0)
+ _system->delayMillis(10);
+
+ } while (_system->getMillis() < start + amount);
+}
+
+} // End of namespace Sword1
diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h
new file mode 100644
index 0000000000..a41685ad75
--- /dev/null
+++ b/engines/sword1/sword1.h
@@ -0,0 +1,110 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSSWORD1_H
+#define BSSWORD1_H
+
+#include "base/engine.h"
+#include "common/util.h"
+#include "base/gameDetector.h"
+#include "sword1/sworddefs.h"
+
+namespace Sword1 {
+
+enum {
+ GF_DEMO = 1 << 0
+};
+
+enum ControlPanelMode {
+ CP_NORMAL = 0,
+ CP_DEATHSCREEN,
+ CP_THEEND,
+ CP_NEWGAME
+};
+
+class Screen;
+class Sound;
+class Logic;
+class Mouse;
+class ResMan;
+class ObjectMan;
+class Menu;
+class Music;
+class Control;
+
+struct SystemVars {
+ bool runningFromCd;
+ uint32 currentCD; // starts at zero, then either 1 or 2 depending on section being played
+ uint32 justRestoredGame; // see main() in sword.c & New_screen() in gtm_core.c
+ bool engineQuit;
+
+ uint8 controlPanelMode; // 1 death screen version of the control panel, 2 = successful end of game, 3 = force restart
+ bool forceRestart;
+ bool wantFade; // when true => fade during scene change, else cut.
+ uint8 playSpeech;
+ uint8 showText;
+ uint8 language;
+ bool isDemo;
+
+ uint8 cutscenePackVersion;
+};
+
+class SwordEngine : public Engine {
+ void errorString(const char *buf_input, char *buf_output);
+public:
+ SwordEngine(GameDetector *detector, OSystem *syst);
+ virtual ~SwordEngine();
+ static SystemVars _systemVars;
+ void reinitialize(void);
+
+ uint32 _features;
+protected:
+ int go();
+ int init(GameDetector &detector);
+private:
+ void delay(int32 amount);
+
+ void checkCdFiles(void);
+ void checkCd(void);
+ void showFileErrorMsg(uint8 type, bool *fileExists);
+ void flagsToBool(bool *dest, uint8 flags);
+ uint8 mainLoop(void);
+
+ uint16 _mouseX, _mouseY, _mouseState;
+ uint8 _keyPressed;
+
+ ResMan *_resMan;
+ ObjectMan *_objectMan;
+ Screen *_screen;
+ Mouse *_mouse;
+ Logic *_logic;
+ Sound *_sound;
+ Menu *_menu;
+ Music *_music;
+ Control *_control;
+ static const uint8 _cdList[TOTAL_SECTIONS];
+ static const CdFile _cdFileList[];
+};
+
+} // End of namespace Sword1
+
+#endif //BSSWORD1_H
diff --git a/engines/sword1/sworddefs.h b/engines/sword1/sworddefs.h
new file mode 100644
index 0000000000..4b057245c5
--- /dev/null
+++ b/engines/sword1/sworddefs.h
@@ -0,0 +1,1606 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SWORDDEFS_H
+#define SWORDDEFS_H
+
+#include "common/scummsys.h"
+
+namespace Sword1 {
+
+#define LOOPED 1
+
+#define FRAME_RATE 12 // number of frames per second (max rate)
+#define SCREEN_WIDTH 640
+#define SCREEN_DEPTH 400
+#define SCREEN_LEFT_EDGE 128
+#define SCREEN_RIGHT_EDGE (128+SCREEN_WIDTH-1)
+#define SCREEN_TOP_EDGE 128
+#define SCREEN_BOTTOM_EDGE (128+SCREEN_DEPTH-1)
+#define TYPE_FLOOR 1
+#define TYPE_MOUSE 2
+#define TYPE_SPRITE 3
+#define TYPE_NON_MEGA 4
+#define TYPE_MEGA 5
+#define TYPE_PLAYER 6
+#define TYPE_TEXT 7
+#define STAT_MOUSE 1
+#define STAT_LOGIC 2
+#define STAT_EVENTS 4
+#define STAT_FORE 8
+#define STAT_BACK 16
+#define STAT_SORT 32
+#define STAT_SHRINK 64
+#define STAT_BOOKMARK 128
+#define STAT_TALK_WAIT 256
+#define STAT_OVERRIDE 512
+
+#define LOGIC_idle 0
+#define LOGIC_script 1
+#define LOGIC_AR_animate 2
+#define LOGIC_interaction 3
+#define LOGIC_speech 4
+#define LOGIC_full_anim 5
+#define LOGIC_anim 6
+#define LOGIC_pause 7
+#define LOGIC_wait_for_sync 8
+#define LOGIC_quit 9
+#define LOGIC_restart 10
+#define LOGIC_bookmark 11
+#define LOGIC_wait_for_talk 12
+#define LOGIC_start_talk 13
+#define LOGIC_choose 14
+#define LOGIC_new_script 15
+#define LOGIC_pause_for_event 16
+
+#define SCRIPT_CONT 1
+#define SCRIPT_STOP 0
+
+#define INS_talk 1
+
+#define TOTAL_pockets 52
+#define TOTAL_subjects (375-256+1)
+#define BASE_SUBJECT 256
+
+#define TOTAL_SECTIONS 150 //number of sections, rooms + mega sections
+#define TOTAL_ROOMS 100 //total number of rooms
+#define ITM_PER_SEC 0x10000 //65536 items per section -> was originally called "SIZE"
+#define ITM_ID 0xFFFF //& with this -> originally "NuSIZE"
+
+#define MAX_text_obs 2 //text compacts
+#define TEXT_sect 149 //text compacts exist in section 149, probably after all the megas
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+struct Header {
+ char type[6];
+ uint16 version;
+ uint32 comp_length;
+ char compression[4];
+ uint32 decomp_length;
+} GCC_PACK;
+
+struct FrameHeader {
+ uint8 runTimeComp[4];
+ uint32 compSize;
+ uint16 width;
+ uint16 height;
+ int16 offsetX;
+ int16 offsetY;
+} GCC_PACK;
+
+struct ParallaxHeader {
+ char type[16];
+ uint16 sizeX;
+ uint16 sizeY;
+} GCC_PACK;
+
+struct AnimUnit {
+ uint32 animX;
+ uint32 animY;
+ uint32 animFrame;
+} GCC_PACK;
+
+struct AnimSet {
+ uint32 cdt;
+ uint32 spr;
+} GCC_PACK;
+
+struct WalkGridHeader {
+ int32 scaleA;
+ int32 scaleB;
+ int32 numBars;
+ int32 numNodes;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+enum fileTypes {
+ TYPE_CD1 = 0,
+ TYPE_CD2,
+ TYPE_DEMO,
+ TYPE_IMMED,
+ TYPE_SPEECH1,
+ TYPE_SPEECH2
+};
+
+enum fileFlags {
+ FLAG_CD1 = (1 << TYPE_CD1), // this file is on cd1
+ FLAG_CD2 = (1 << TYPE_CD2), // this file is on cd2
+ FLAG_DEMO = (1 << TYPE_DEMO), // file for the demo version
+ FLAG_IMMED = (1 << TYPE_IMMED), // this file is needed immediately, game won't start without it
+ FLAG_SPEECH1 = (1 << TYPE_SPEECH1),
+ FLAG_SPEECH2 = (1 << TYPE_SPEECH2)
+};
+
+struct CdFile {
+ const char *name;
+ uint8 flags;
+};
+
+enum Language {
+ BS1_ENGLISH = 0,
+ BS1_FRENCH,
+ BS1_GERMAN,
+ BS1_ITALIAN,
+ BS1_SPANISH,
+ BS1_CZECH,
+ BS1_PORT
+};
+
+#define SAM 2162689
+#define PLAYER 8388608
+#define GEORGE 8388608
+#define NICO 8454144
+#define BENOIR 8585216
+#define ROSSO 8716288
+#define DUANE 8781824
+#define MOUE 9502720
+#define ALBERT 9568256
+
+#define STAND 0
+#define UP 0
+#define UP_RIGHT 1
+#define U_R 1
+#define RIGHT 2
+#define DOWN_RIGHT 3
+#define D_R 3
+#define DOWN 4
+#define DOWN_LEFT 5
+#define D_L 5
+#define LEFT 6
+#define UP_LEFT 7
+#define U_L 7
+
+#define BEER_TOWEL 3
+#define HOTEL_KEY 4
+#define BALL 5
+#define RED_NOSE 7
+#define POLISHED_CHALICE 8
+#define PHOTOGRAPH 10
+#define GEM 13
+#define LAB_PASS 17
+#define LIFTING_KEYS 18
+#define MANUSCRIPT 19
+#define PLASTER 23
+#define ROSSO_CARD 27
+#define TISSUE 32
+#define LENS 37
+#define TRIPOD 36
+#define CHALICE 31
+#define MATCHBOOK 20
+#define PRESSURE_GAUGE 24
+#define BUZZER 26
+#define TOILET_KEY 28
+#define STONE_KEY 30
+#define TOILET_BRUSH 33
+#define MIRROR 38
+#define TOWEL_CUT 39
+
+#define SC58_PATH_X 225
+#define SC58_PATH_Y 369
+
+#define FLOOR_1 65536
+#define FLOOR_2 131072
+#define FLOOR_3 196608
+#define FLOOR_4 262144
+#define FLOOR_5 327680
+#define FLOOR_6 393216
+#define FLOOR_7 458752
+#define FLOOR_8 524288
+#define FLOOR_9 589824
+#define FLOOR_10 655360
+#define FLOOR_11 720896
+#define FLOOR_12 786432
+#define FLOOR_13 851968
+#define FLOOR_14 917504
+#define FLOOR_15 983040
+#define FLOOR_16 1048576
+#define FLOOR_17 1114112
+#define FLOOR_18 1179648
+#define FLOOR_19 1245184
+#define FLOOR_20 1310720
+#define FLOOR_21 1376256
+#define FLOOR_22 1441792
+#define FLOOR_23 1507328
+#define FLOOR_24 1572864
+#define FLOOR_25 1638400
+#define FLOOR_26 1703936
+#define FLOOR_27 1769472
+#define FLOOR_28 1835008
+#define FLOOR_29 1900544
+#define FLOOR_31 2031616
+#define FLOOR_32 2097152
+#define FLOOR_33 2162688
+#define FLOOR_34 2228224
+#define FLOOR_35 2293760
+#define FLOOR_36 2359296
+#define FLOOR_37 2424832
+#define FLOOR_38 2490368
+#define FLOOR_39 2555904
+#define FLOOR_40 2621440
+#define FLOOR_41 2686976
+#define FLOOR_42 2752512
+#define FLOOR_43 2818048
+#define FLOOR_45 2949120
+#define FLOOR_46 3014656
+#define FLOOR_47 3080192
+#define FLOOR_48 3145728
+#define FLOOR_49 3211264
+#define FLOOR_50 3276800
+#define FLOOR_53 3473408
+#define FLOOR_54 3538944
+#define FLOOR_55 3604480
+#define FLOOR_56 3670016
+#define FLOOR_57 3735552
+#define FLOOR_58 3801088
+#define FLOOR_59 3866624
+#define FLOOR_60 3932160
+#define LEFT_FLOOR_61 3997697
+#define FLOOR_62 4063232
+#define FLOOR_63 4128768
+#define FLOOR_65 4259840
+#define FLOOR_66 4325376
+#define FLOOR_67 4390912
+#define FLOOR_69 4521984
+#define RIGHT_FLOOR_71 4653060
+#define FLOOR_72 4718592
+#define FLOOR_73 4784128
+#define FLOOR_74 4849664
+#define FLOOR_75 4915200
+#define FLOOR_76 4980736
+#define FLOOR_77 5046272
+#define FLOOR_78 5111808
+#define FLOOR_79 5177344
+#define FLOOR_80 5242880
+#define FLOOR_86 5636096
+#define FLOOR_91 5963776
+#define FLOOR_99 6488064
+
+
+#define menu_bible 69
+#define menu_newspaper 1
+#define menu_hazel_wand 2
+#define menu_beer_towel 68
+#define menu_beer_towel_wet 4
+#define menu_beer_towel_damp 5
+#define menu_beer_towel_dried 6
+#define menu_hotel_key 7
+#define menu_ball 8
+#define menu_statuette 9
+#define menu_red_nose_first 10
+#define menu_red_nose_second 11
+#define menu_polished_chalice 12
+#define menu_dollar_bill 13
+#define menu_photograph 14
+#define menu_keyring_first 15
+#define menu_keyring_second 70
+#define menu_keyring_third 17
+#define menu_fuse_wire 18
+#define menu_gem 19
+#define menu_statuette_paint 20
+#define menu_stick 21
+#define menu_excav_key 71
+#define menu_false_key 72
+#define menu_painted_key 73
+#define menu_lab_pass 25
+#define menu_lifting_keys 26
+#define menu_manuscript 27
+#define menu_match_book 28
+#define menu_suit_material 29
+#define menu_stick_towel 30
+#define menu_plaster 31
+#define menu_pressure_gauge 32
+#define menu_railway_ticket 33
+#define menu_buzzer 74
+#define menu_rosso_card 75
+#define menu_toilet_key 36
+#define menu_soap 76
+#define menu_soap_imp 77
+#define menu_soap_plas 78
+#define menu_stone_key 79
+#define menu_chalice 41
+#define menu_tissue 42
+#define menu_toilet_brush 80
+#define menu_toilet_chain 44
+#define menu_towel 45
+#define menu_tripod 46
+#define menu_lens 81
+#define menu_towel_cut 48
+#define menu_mirror 82
+#define menu_tissue_charred 50
+#define menu_cog_1 51
+#define menu_cog_2 52
+#define menu_handle 83
+#define menu_coin 84
+#define menu_biro 55
+#define menu_pipe 56
+#define menu_flashlight 57
+
+#define IT_MCODE 1 // Call an mcode routine
+#define IT_PUSHNUMBER 2 // push a number on the stack
+#define IT_PUSHVARIABLE 3 // push a variable on the stack
+
+#define IT_FIRSTOPERATOR 4 // Operators come after this and must stay in the same order for precedence table
+
+#define IT_NOTEQUAL 4
+#define IT_ISEQUAL 5
+#define IT_PLUS 6
+#define IT_TIMES 7
+#define IT_ANDAND 8
+#define IT_OROR 9
+#define IT_LESSTHAN 10
+#define IT_NOT 11
+#define IT_MINUS 12
+#define IT_AND 13
+#define IT_OR 14
+#define IT_GTE 15 // >=
+#define IT_LTE 16 // <=
+#define IT_DEVIDE 17 // <=
+#define IT_GT 18 // >
+
+#define IT_SCRIPTEND 20
+#define IT_POPVAR 21
+#define IT_POPLONGOFFSET 22
+#define IT_PUSHLONGOFFSET 23
+#define IT_SKIPONFALSE 24
+#define IT_SKIP 25
+#define IT_SWITCH 26
+#define IT_SKIPONTRUE 27
+#define IT_PRINTF 28
+#define IT_RESTARTSCRIPT 30
+#define IT_POPWORDOFFSET 31
+#define IT_PUSHWORDOFFSET 32
+
+enum ScriptVariableNames {
+ RETURN_VALUE = 0,
+ RETURN_VALUE_2,
+ RETURN_VALUE_3,
+ RETURN_VALUE_4,
+ DEFAULT_ICON_TEXT,
+ MENU_LOOKING,
+ TOP_MENU_DISABLED,
+ GEORGE_DOING_REST_ANIM,
+ GEORGE_WALKING,
+ ADVISOR_188_FLAG,
+ MEGA_ON_GRID,
+ REROUTE_GEORGE,
+ WALK_FLAG,
+ WALK_ATTEMPT,
+ TARGET_X,
+ TARGET_Y,
+ DISTANCE_APART,
+ ID_LOW_FLOOR,
+ NEW_SCREEN,
+ CUR_ID,
+ MOUSE_STATUS,
+ PALETTE,
+ NEW_PALETTE,
+ MOUSE_X,
+ MOUSE_Y,
+ SPECIAL_ITEM,
+ CLICK_ID,
+ MOUSE_BUTTON,
+ BUTTON,
+ BOTH_BUTTONS, // not used anymore
+ SAFE_X,
+ SAFE_Y,
+ CHANGE_X,
+ CHANGE_Y,
+ CHANGE_PLACE,
+ CHANGE_DIR,
+ CHANGE_STANCE,
+ SCROLL_FLAG,
+ SCROLL_OFFSET_X,
+ SCROLL_OFFSET_Y,
+ MAX_SCROLL_OFFSET_X,
+ MAX_SCROLL_OFFSET_Y,
+ FEET_X,
+ FEET_Y,
+ SECOND_ITEM, //SECOND_ICON,
+ SUBJECT_CHOSEN,
+ IN_SUBJECT,
+ DEBUG_FLAG_1,
+ DEBUG_FLAG_2,
+ DEBUG_FLAG_3,
+ FIRST_WATCH,
+ GEORGE_ALLOWED_REST_ANIMS,
+ CURRENT_MUSIC,
+ TESTLINENO,
+ LASTLINENO,
+ WANTPREVIOUSLINE,
+ PLAYINGDEMO,
+ TEMP_FLAG,
+ PHOTOS_FLAG,
+ PHONE_FLOOR_FLAG,
+ PHONE_ROOM_FLAG,
+ BENOIR_FLAG,
+ GUARD_FLAG,
+ MOUE_DOOR_FLAG,
+ CANOPY_FLAG,
+ GOT_NEWSPAPER_FLAG,
+ DEMO_NICO_FLAG,
+ NICO_TARGET,
+ NICO_DIR,
+ BEEN_TO_ALLEY,
+ DUSTBIN_FLAG,
+ DUSTBIN_2_FLAG,
+ TRIED_MANHOLE_FLAG,
+ MANHOLE_FLAG,
+ DRAINPIPE_FLAG,
+ OPENED_MANHOLE_2_BEFORE,
+ SEARCHED_PLANTARD_FLAG,
+ ENTERED_CAFE_ONCE,
+ BOTTLE_3_FLAG,
+ TOOLBOX_4_FLAG,
+ CALL_ALB_FLAG,
+ CALL_ALBERT_FLAG,
+ GOT_NOSE_FLAG,
+ GOT_MATERIAL_FLAG,
+ GOT_TISSUE_FLAG,
+ RAILING_7_FLAG,
+ SEEN_FLOWERS_FLAG,
+ SEEN_DRESS_SHOP_FLAG,
+ DOOR_9_FLAG,
+ PHONE_10_FLAG,
+ MANUSCRIPT_ON_TABLE_10_FLAG,
+ DOG_TURD_FLAG,
+ PIERMONT_AT_PIANO_FLAG,
+ GOT_KEY_FLAG,
+ USED_HOTEL_KEY_ONCE,
+ WINDOW_15_OPEN,
+ CLIMBED_OUT_15_FLAG,
+ WINDOW_16_FLAG,
+ HOTEL_ASSASSIN_BEEN,
+ WARDROBE_17_OPEN,
+ SEARCHED_TROUSERS_17,
+ ENTERED_17_FLAG,
+ WINDOW_27_FLAG,
+ CASE_1_LOCKED_FLAG,
+ CASE_2_LOCKED_FLAG,
+ CASE_3_LOCKED_FLAG,
+ CASE_4_LOCKED_FLAG,
+ SEEN_ARMOUR_28_FLAG,
+ CLOSED_WINDOW_28_FLAG,
+ WINDOW_28_FLAG,
+ WINDOW_DRAUGHT_FLAG,
+ SEEN_WINDOW_28_FLAG,
+ FACING_WINDOW_FLAG,
+ CLOSING_WINDOW_FLAG,
+ SARCOPHAGUS_FLAG,
+ ENTERED_MUSEUM_28_FLAG,
+ SARCOPHAGUS_DOOR_29_OPEN,
+ AMBULANCE_31_FLAG,
+ CONSULTANT_HERE,
+ SEEN_MR_SHINY_FLAG,
+ SEEN_CUPBOARD_FLAG,
+ PLUG_33_UNPLUGGED,
+ SAM_RETURNING,
+ PULLED_PLUG_33,
+ PULSE_34_FLAG,
+ DOOR_34_OPEN,
+ MARQUET_AWAKE_FLAG,
+ JUGGLER_FLAG,
+ JUGGLE_FLAG,
+ CROWD_FLAG,
+ MANHOLE_36_FLAG,
+ DOOR_37_FLAG,
+ IN_BOAT_FLAG,
+ GOT_HOOK_FLAG,
+ HOOK_FLAG,
+ STEPS_38_FLAG,
+ TRIPOD_PUZZLE_FLAG,
+ SOAP_43_FLAG,
+ SEEN_WASHBASIN_43,
+ HOSPITAL_FLAG,
+ SEEN_PARIS_MAP,
+ PHONE_SCREEN_FLAG,
+ PHONE_PLACE_FLAG,
+ SEAN_DEAD,
+ SPAIN_VISIT,
+ WET_BEER_TOWEL_TIMER,
+ BEER_TOWEL_BEEN_WET,
+ NICO_SCOT_SCREEN,
+ NICO_AT_PANEL_72,
+ NICO_POSITION_71,
+ SEEN_DRAIN_19,
+ SEEN_MENU_19,
+ PUB_TRAP_DOOR,
+ ASSASSIN_EIRE_DONE,
+ BAR_TOWEL_TAKEN,
+ GLASS_WASH_FLAG,
+ PUB_DOOR_FLAG,
+ PUB_FLAP_FLAG,
+ DOYLE_DRINKING,
+ RON_SNEEZING,
+ FUSE_WIRE_TAKEN,
+ FUSE_WIRE_ON_TABLE,
+ GLASS_20_FLAG,
+ MAGUIRE_PUB_DONE,
+ PINT_LEVEL_FLAG,
+ GEM_21_TAKEN,
+ MAGUIRE_CEL_DONE,
+ TORCH_21_TAKEN,
+ BEEN_UP_HAYBAILS,
+ LIFTING_KEYS_IN_HOLE_23,
+ SEEN_STEPS_SEQUENCE,
+ SEEN_GOAT_24,
+ FLEECY_TANGLED,
+ FLEECY_STUCK,
+ FLEECY_BACKING_OFF,
+ SEEN_LADDER_SEQUENCE,
+ BUTT_COUNT_24,
+ KEYSTONE_FLAG,
+ PANEL_25_MOVED,
+ SACK_25_FLAG,
+ SAND_FLAG,
+ SEEN_HOLES_25,
+ REPLICA_IN_CAVITY,
+ SEEN_RAT_26,
+ ENTERED_CELLAR_BEFORE,
+ CAT_ON_SHELF,
+ CAT_RAN_OFF,
+ CAT_TIMER,
+ STATUETTE_FLAG,
+ SEEN_TOP_SHELF_45,
+ DUANE_TARGET,
+ AYUB_OPENING_DOOR,
+ GEORGE_TALKING_TO_PEARL,
+ CARPET_DOOR_47_OPEN,
+ TOILET_KEYS_ON_BAR,
+ EXPLAINED_RETURNING_KEYS,
+ DOOR_49_OPEN,
+ TOILET_CHAIN_50_TAKEN,
+ TOWEL_DISPENSER_50_OPEN,
+ TOWEL_50_TAKEN,
+ CUBICLE_DOOR_50_OPEN,
+ DOOR_50_OPEN,
+ MAX_ITERATION,
+ ITERATION,
+ STICK_54_FLAG,
+ TOWEL_IN_CRACK_54,
+ CAVE_54_OPEN,
+ GUN_54_FLAG,
+ KHAN_54_HERE,
+ DOOR_55_OPEN,
+ READ_INSCRIPTION_55,
+ SEEN_STATUE_55,
+ VISITED_COUNTESS_56_AGAIN,
+ CHALICE_56_GIVEN,
+ CHESS_PIECE_56_GIVEN,
+ GARDENER_57_HERE,
+ PRESSURE_GAUGE_57_FLAG,
+ FOUND_WELL_57,
+ DOOR_58_OPEN,
+ COUNTESS_58_HERE,
+ GARDENER_58_HERE,
+ COUNTESS_59_HERE,
+ BIBLE_59_FLAG,
+ WINDOW_59_SHUT,
+ CHALICE_59_TAKEN,
+ SECRET_DOOR_59_OPEN,
+ HOLDING_SNUFFER,
+ TISSUE_ON_SNUFFER,
+ TISSUE_59_CHARRED,
+ TISSUE_59_BURNING,
+ CANDLE_59_BURNT,
+ LECTERN_CANDLES_59_LIT,
+ TISSUE_FLAME_59_ON,
+ GARDENER_60_POSITION,
+ GARDENER_60_CHECKING_DOGS,
+ DOGS_DISTURBED,
+ MIRROR_60_TAKEN,
+ SEEN_LEFT_ROCKFALL_61,
+ LION_HEAD_FALLING,
+ LION_FANG_FLAG,
+ DOOR_61_FLAG,
+ GEORGE_HOLDING_PIECE,
+ CHESS_SQUARE_1_FLAG,
+ CHESS_SQUARE_2_FLAG,
+ CHESS_SQUARE_3_FLAG,
+ CHESS_SQUARE_4_FLAG,
+ CHESS_SQUARE_5_FLAG,
+ DOOR_ONE_63_OPEN,
+ DOOR_TWO_63_OPEN,
+ DOOR_THREE_63_OPEN,
+ GEORGE_ON_ROOF,
+ SEEN_EKLUND_63,
+ DOOR_65_OPEN,
+ DOOR_67_OPEN,
+ WINDOW_66_OPEN,
+ SEQUENCE_69_FLAG,
+ SC69_TIMER,
+ LEFT_TREE_POINTER_71_FLAG,
+ RIGHT_TREE_POINTER_71_FLAG,
+ RUBBLE_72_FLAG,
+ MACHINERY_HANDLE_FLAG,
+ MACHINERY_COG_FLAG,
+ DEMON_RB_FLAG,
+ DEMON_LB_FLAG,
+ DEMON_COGS_FLAG,
+ DEMON_PIPE_FLAG,
+ DEMON_NOSE_FLAG,
+ DEMON_LEFT_COG_FLAG,
+ DEMON_RIGHT_COG_FLAG,
+ PANEL_72_FLAG,
+ SEEN_CRYPT_73,
+ SEEN_GUNPOWDER_73,
+ GUIDO_73_HERE,
+ NICO_POSITION_73,
+ ALBERT_ANNOYED_FLAG,
+ ALBERT_BRIEFCASE_FLAG,
+ ALBERT_BUZZER_FLAG,
+ ALBERT_CDT_FLAG,
+ ALBERT_CHANTELLE_FLAG,
+ ALBERT_CHAT_FLAG,
+ ALBERT_CLOWN_FLAG,
+ ALBERT_JACKET_FLAG,
+ ALBERT_KEYS_FLAG,
+ ALBERT_NOSE_FLAG,
+ ALBERT_PLANTARD_FLAG,
+ ALBERT_POLICE_FLAG,
+ ALBERT_POS_FLAG,
+ ALBERT_TALK_FLAG,
+ ALBERT_TISSUE_FLAG,
+ ALBERT_TEXT,
+ ALBERT_INFO_FLAG,
+ ARTO_BULL_FLAG,
+ ARTO_BRUSH_FLAG,
+ ARTO_IRRITATION_FLAG,
+ ARTO_KLAUSNER_FLAG,
+ ARTO_LOOM_FLAG,
+ ARTO_OBJECT_FLAG,
+ ARTO_PHRASE_FLAG,
+ ARTO_TEXT,
+ ASSASSIN_BOOK_FLAG,
+ ASSASSIN_BULL_FLAG,
+ ASSASSIN_CHURCH_FLAG,
+ ASSASSIN_EIRE_TEXT,
+ ASSASSIN_SWORD_FLAG,
+ ASSASSIN_TEMPLAR_FLAG,
+ ASSASSIN_TEXT,
+ AYUB_BULL_FLAG,
+ AYUB_KLAUSNER_FLAG,
+ AYUB_LOOM_FLAG,
+ AYUB_ULTAR_FLAG,
+ AYUB_TEXT,
+ BASHER_BEER_FLAG,
+ BASHER_COMPLAIN_FLAG,
+ BASHER_EKLUND_FLAG,
+ BASHER_HELP_FLAG,
+ BASHER_NICO_FLAG,
+ BASHER_STOP_FLAG,
+ BASHER_WEASEL_FLAG,
+ BASHER_WINDOW_FLAG,
+ BASHER_TEXT,
+ BENOIR_BUZZER_FLAG,
+ BENOIR_GAUGE_FLAG,
+ BENOIR_MARQUET_FLAG,
+ BENOIR_NURSE_FLAG,
+ BENOIR_RENEE_FLAG,
+ BENOIR_TEXT,
+ CARPET_TEXT,
+ CARPET_OBJECT_FLAG,
+ CHANTELLE_BRIEFCASE_FLAG,
+ CHANTELLE_CLOWN_FLAG,
+ CHANTELLE_DOCTOR_FLAG,
+ CHANTELLE_EYE_FLAG,
+ CHANTELLE_FAINT_FLAG,
+ CHANTELLE_NEWSPAPER_FLAG,
+ CHANTELLE_PLANTARD_FLAG,
+ CHANTELLE_TEXT,
+ CHANTELLE_WAKE_COUNTER,
+ CLERK_ASSASSIN_FLAG,
+ CLERK_BUZZER_FLAG,
+ CLERK_CLOWN_FLAG,
+ CLERK_ENOUGH_FLAG,
+ CLERK_HKEY_FLAG,
+ CLERK_KEY_FLAG,
+ CLERK_KEY_STOP_FLAG,
+ CLERK_NOSE_FLAG,
+ CLERK_PASS_FLAG,
+ CLERK_PHOTO_FLAG,
+ CLERK_PIERMONT_FLAG,
+ CLERK_PLANTARD_FLAG,
+ CLERK_POLITE_FLAG,
+ CLERK_SAFE_FLAG,
+ CLERK_TEMPLAR_FLAG,
+ CLERK_TEXT,
+ CLERK_TISSUE_FLAG,
+ CLERK_WEASEL_FLAG,
+ CONSULT_CHALICE_FLAG,
+ CONSULT_GAUGE_FLAG,
+ CONSULT_GEM_FLAG,
+ CONSULT_LIFTKEY_FLAG,
+ CONSULT_MARQUET_FLAG,
+ CONSULT_NOSE_FLAG,
+ CONSULT_PHOTO_FLAG,
+ CONSULT_TEXT,
+ CONSULT_TISSUE_FLAG,
+ COSTUMIER_BALL_FLAG,
+ COSTUMIER_BUZZER_FLAG,
+ COSTUMIER_CLOWN_FLAG,
+ COSTUMIER_PHOTO_FLAG,
+ COSTUMIER_PLANTARD_FLAG,
+ COSTUMIER_TISSUE_FLAG,
+ COSTUMIER_TEXT,
+ DOYLE_BEER_FLAG,
+ DOYLE_BUZZER_FLAG,
+ DOYLE_CASTLE_FLAG,
+ DOYLE_DIG_FLAG,
+ DOYLE_FLASHLIGHT_FLAG,
+ DOYLE_GEM_FLAG,
+ DOYLE_JEWEL_FLAG,
+ DOYLE_JOB_FLAG,
+ DOYLE_KEYS_FLAG,
+ DOYLE_LEPRECHAUN_FLAG,
+ DOYLE_NOSE_FLAG,
+ DOYLE_PEAGRAM_FLAG,
+ DOYLE_PHOTOGRAPH_FLAG,
+ DOYLE_SEAN_FLAG,
+ DOYLE_TEMPLAR_FLAG,
+ DOYLE_TEXT,
+ DOYLE_TISSUE_FLAG,
+ DOYLE_TOWEL_FLAG,
+ DUANE_ARTO_FLAG,
+ DUANE_BULL_FLAG,
+ DUANE_CLEVE_FLAG,
+ DUANE_DUANE_FLAG,
+ DUANE_PEARL_FLAG,
+ DUANE_PHOTO_FLAG,
+ DUANE_KEYS_FLAG,
+ DUANE_MANUSCRIPT_FLAG,
+ DUANE_NEJO_FLAG,
+ DUANE_PHRASE_FLAG,
+ DUANE_QUEEN_FLAG,
+ DUANE_STATUETTE_FLAG,
+ DUANE_TEMPLAR_FLAG,
+ DUANE_TEXT,
+ DUANE_ULTAR_FLAG,
+ ERIC_MARQUET_FLAG,
+ ERIC_NURSE_FLAG,
+ ERIC_PHOTO_FLAG,
+ EVA_CLOWN_FLAG,
+ EVA_LENS_FLAG,
+ EVA_MARQUET_FLAG,
+ EVA_MOB_FLAG,
+ EVA_NURSE_FLAG,
+ EVA_TEXT,
+ FARMER_BEER_FLAG,
+ FARMER_BOOK_FLAG,
+ FARMER_BUZZER_FLAG,
+ FARMER_CAR_FLAG,
+ FARMER_CASTLE_FLAG,
+ FARMER_FLASHLIGHT_FLAG,
+ FARMER_GEM_FLAG,
+ FARMER_GHOST_FLAG,
+ FARMER_LAST_STRAW,
+ FARMER_LIFTKEYS_FLAG,
+ FARMER_MOVED_FLAG,
+ FARMER_NOSE_FLAG,
+ FARMER_PASS_FLAG,
+ FARMER_PEAGRAM_FLAG,
+ FARMER_PHOTO_FLAG,
+ FARMER_SEAN_FLAG,
+ FARMER_TEMPLAR_FLAG,
+ FARMER_TEXT,
+ FARMER_TISSUE_FLAG,
+ FARMER_WIRE_FLAG,
+ FLEECY_TEXT,
+ FLOWER_FLOWER_FLAG,
+ FLOWER_FORTUNE_FLAG,
+ FLOWER_GAUGE_FLAG,
+ FLOWER_GEM_FLAG,
+ FLOWER_LIFTKEYS_FLAG,
+ FLOWER_NICO_FLAG,
+ FLOWER_PASS_FLAG,
+ FLOWER_PHOTO_FLAG,
+ FLOWER_TEXT,
+ GARD_ATTEMPT,
+ GARD_BY_WELL,
+ GARDENER_COUNTESS_FLAG,
+ GARDENER_CHALICE_FLAG,
+ GARDENER_FLOPPO_FLAG,
+ GARDENER_GOODBYE_FLAG,
+ GARDENER_HOSE_FLAG,
+ GARDENER_IRRITATION,
+ GARDENER_SPEECH_FLAG,
+ GARDENER_TEMPLAR_FLAG,
+ GARDENER_TEXT,
+ GATEKEEPER_TALK_FLAG,
+ GATEKEEPER_CDT_FLAG,
+ GMASTER_TALK_FLAG,
+ GMASTER_CDT_FLAG,
+ GENDARME_CARD_FLAG,
+ GENDARME_CLOWN_FLAG,
+ GENDARME_MOUE_FLAG,
+ GENDARME_NOSE_FLAG,
+ GEND_PAPER_FLAG,
+ GENDARME_PHOTO_FLAG,
+ GENDARME_ROSSO_FLAG,
+ GENDARME_TEXT,
+ GENDARME_TISSUE_FLAG,
+ GENDARME_WEASEL_FLAG,
+ GOINFRE_ALARM_FLAG,
+ GOINFRE_EXHIBIT_FLAG,
+ GOINFRE_GEM_FLAG,
+ GOINFRE_KEYS_FLAG,
+ GOINFRE_LOBINEAU_FLAG,
+ GOINFRE_MS_FLAG,
+ GOINFRE_SARCOPHAGUS_FLAG,
+ GOINFRE_SCOLD_FLAG,
+ GOINFRE_TEMPLAR_FLAG,
+ GOINFRE_TEXT,
+ GOINFRE_TISSUE_FLAG,
+ GOINFRE_TRIPOD_FLAG,
+ GOINFRE_WINDOW_FLAG,
+ GORILLA_CLOWN_FLAG,
+ GORILLA_KHAN_FLAG,
+ GORILLA_PASS_FLAG,
+ GORILLA_PLANTARD_FLAG,
+ GORILLA_SEARCH_FLAG,
+ GORILLA_TEXT,
+ GORILLA_TISSUE_FLAG,
+ GORILLA_WEASEL_FLAG,
+ HOSCOP_ALERT_FLAG,
+ HOSCOP_MARQUET_FLAG,
+ HOSCOP_MOB_FLAG,
+ HOSCOP_TEXT,
+ JUGGLER_JUGGLER_FLAG,
+ JUGGLER_TEMPLAR_FLAG,
+ JUGGLER_GEM_FLAG,
+ JUGGLER_TEXT,
+ KHAN_SUBJECT_FLAG,
+ KHAN_PREAMBLE_FLAG,
+ LATVIAN_CLOWN_FLAG,
+ LATVIAN_EYE_FLAG,
+ LATVIAN_LIFTKEYS_FLAG,
+ LATVIAN_MATCHBOOK_FLAG,
+ LATVIAN_MS_FLAG,
+ LATVIAN_NOSE_FLAG,
+ LATVIAN_PHOTO_FLAG,
+ LATVIAN_PLANTARD_FLAG,
+ LATVIAN_TEXT,
+ LEARY_BEER_FLAG,
+ LEARY_BUZZER_FLAG,
+ LEARY_CASTLE_FLAG,
+ LEARY_CLOWN_FLAG,
+ LEARY_FISH_FLAG,
+ LEARY_FLAP_FLAG,
+ LEARY_FLAPALERT_FLAG,
+ LEARY_KEYS_FLAG,
+ LEARY_NOSE_FLAG,
+ LEARY_PASS_FLAG,
+ LEARY_PEAGRAM_FLAG,
+ LEARY_PHONE_FLAG,
+ LEARY_PHOTO_FLAG,
+ LEARY_PLASTER_FLAG,
+ LEARY_PLUG_FLAG,
+ LEARY_SEAN_FLAG,
+ LEARY_SNARE_FLAG,
+ LEARY_TEMPLAR_FLAG,
+ LEARY_TEXT,
+ LEARY_TISSUE_FLAG,
+ LEARY_TOWEL_FLAG,
+ LEARY_WASHER_FLAG,
+ LEARY_WILD_FLAG,
+ LEARY_WIRE_FLAG,
+ LOBINEAU_ARTEFACT_FLAG,
+ LOBINEAU_BALL_FLAG,
+ LOBINEAU_BEL_FLAG,
+ LOBINEAU_GEM_FLAG,
+ LOBINEAU_HASH_FLAG,
+ LOBINEAU_KEYS_FLAG,
+ LOBINEAU_MANUSCRIPT_FLAG,
+ LOBINEAU_MATCHBOOK_FLAG,
+ LOBINEAU_MONTFAUCON_FLAG,
+ LOBINEAU_NICO_FLAG,
+ LOBINEAU_PANTS_FLAG,
+ LOBINEAU_PEAGRAM_FLAG,
+ LOBINEAU_STATUE_FLAG,
+ LOBINEAU_SYRIA_FLAG,
+ LOBINEAU_TEMPLAR_FLAG,
+ LOBINEAU_TEXT,
+ LOBINEAU_TRIPOD_FLAG,
+ MAGUIRE_CAR_FLAG,
+ MAGUIRE_CASTLE_FLAG,
+ MAGUIRE_CDT_FLAG,
+ MAGUIRE_CLOWN_FLAG,
+ MAGUIRE_COP_FLAG,
+ MAGUIRE_DIG_FLAG,
+ MAGUIRE_GEM_FLAG,
+ MAGUIRE_GHOST_FLAG,
+ MAGUIRE_JEWEL_FLAG,
+ MAGUIRE_KEYS_FLAG,
+ MAGUIRE_LEPRECHAUN_FLAG,
+ MAGUIRE_NOSE_FLAG,
+ MAGUIRE_PEAGRAM_FLAG,
+ MAGUIRE_SEAN_FLAG,
+ MAGUIRE_SHOCK_FLAG,
+ MAGUIRE_TALK_FLAG,
+ MAGUIRE_TEXT,
+ MAGUIRE_WIRE_FLAG,
+ MANAGER_TEXT,
+ MANAGER_BRUSH_FLAG,
+ MANAGER_SPEECH_FLAG,
+ MOUE_BALL_FLAG,
+ MOUE_BRIEFCASE_FLAG,
+ MOUE_CARD_FLAG,
+ MOUE_CDT_FLAG,
+ MOUE_CLOWN_FLAG,
+ MOUE_EYE_FLAG,
+ MOUE_FETCH_FLAG,
+ MOUE_HASH_FLAG,
+ MOUE_KEY_FLAG,
+ MOUE_MARQUET_FLAG,
+ MOUE_MATCHBOOK_FLAG,
+ MOUE_MATERIAL_FLAG,
+ MOUE_MOB_FLAG,
+ MOUE_NEWSPAPER_FLAG,
+ MOUE_NICO_FLAG,
+ MOUE_NOSE_FLAG,
+ MOUE_PHOTO_FLAG,
+ MOUE_PLANTARD_FLAG,
+ MOUE_ROSSO_FLAG,
+ MOUE_STOP_FLAG,
+ MOUE_TALK_FLAG,
+ MOUE_TEXT,
+ MOUE_TISSUE_FLAG,
+ NEJO_ARTO_FLAG,
+ NEJO_AYUB_FLAG,
+ NEJO_BALL_FLAG,
+ NEJO_BALL_TALK,
+ NEJO_BULL_FLAG,
+ NEJO_CAT_FLAG,
+ NEJO_CHALICE_FLAG,
+ NEJO_DOLLAR_FLAG,
+ NEJO_GOODBYE_FLAG,
+ NEJO_HENDERSONS_FLAG,
+ NEJO_LOOM_FLAG,
+ NEJO_NEJO_FLAG,
+ NEJO_PHRASE_FLAG,
+ NEJO_PLASTER_FLAG,
+ NEJO_PRESSURE_GAUGE_FLAG,
+ NEJO_STALL_FLAG,
+ NEJO_STATUE_FLAG,
+ NEJO_TEMPLAR_FLAG,
+ NEJO_TEXT,
+ NEJO_ULTAR_FLAG,
+ NICO_ALBERT_FLAG,
+ NICO_ASSASSIN_FLAG,
+ NICO_BALL_FLAG,
+ NICO_BRIEFCASE_FLAG,
+ NICO_BULL_FLAG,
+ NICO_BUZZER_FLAG,
+ NICO_CHALICE_FLAG,
+ NICO_CDT_FLAG,
+ NICO_CLOWN_FLAG,
+ NICO_EKLUND_FLAG,
+ NICO_GAUGE_FLAG,
+ NICO_GEM_FLAG,
+ NICO_GOODBYE_FLAG,
+ NICO_GUIDO_FLAG,
+ NICO_HASH_FLAG,
+ NICO_IRELAND_FLAG,
+ NICO_KNIGHT_FLAG,
+ NICO_LIFTKEYS_FLAG,
+ NICO_LENS_FLAG,
+ NICO_LOBINEAU_FLAG,
+ NICO_MANUSCRIPT_FLAG,
+ NICO_MARQUET_FLAG,
+ NICO_MATCHBOOK_FLAG,
+ NICO_MATERIAL_FLAG,
+ NICO_NEWSPAPER_FLAG,
+ NICO_NICO_FLAG,
+ NICO_NOSE_FLAG,
+ NICO_PASS_FLAG,
+ NICO_PEAGRAM_FLAG,
+ NICO_PLANTARD_FLAG,
+ NICO_PLASTER_FLAG,
+ NICO_PHOTO_FLAG,
+ NICO_PHONE_TEXT,
+ NICO_POS_FLAG,
+ NICO_QUEEN_FLAG,
+ NICO_RINGING_BACK_FLAG,
+ NICO_ROSSO_FLAG,
+ NICO_SEWER_FLAG,
+ NICO_SPAIN_FLAG,
+ NICO_SYRIA_FLAG,
+ NICO_TALK_FLAG,
+ NICO_TEMPLAR_FLAG,
+ NICO_TEXT,
+ NICO_TISSUE_FLAG,
+ NICO_TRAIN_FLAG,
+ NICO_TRIPOD_FLAG,
+ NICO_WEAVER_FLAG,
+ NIC_BAG_TALK_FLAG,
+ NIC_BAG_CDT_FLAG,
+ NICO_LEAVING_CAFE_SCREEN,
+ NURSE_BENOIR_FLAG,
+ NURSE_CLOWN_FLAG,
+ NURSE_GAUGE_FLAG,
+ NURSE_MARQUET_FLAG,
+ NURSE_INTERRUPTION_FLAG,
+ NURSE_TEXT,
+ OBRIEN_BUZZER_FLAG,
+ OBRIEN_CASTLE_FLAG,
+ OBRIEN_FLASHLIGHT_FLAG,
+ OBRIEN_GEM_FLAG,
+ OBRIEN_JEWEL_FLAG,
+ OBRIEN_JOB_FLAG,
+ OBRIEN_KEYS_FLAG,
+ OBRIEN_LEARY_FLAG,
+ OBRIEN_MAGUIRE_FLAG,
+ OBRIEN_NOSE_FLAG,
+ OBRIEN_PEAGRAM_FLAG,
+ OBRIEN_SEAN_FLAG,
+ OBRIEN_TEMPLAR_FLAG,
+ OBRIEN_TEXT,
+ OBRIEN_TISSUE_FLAG,
+ OBRIEN_TOWEL_FLAG,
+ OLD_NOSE_FLAG,
+ OLD_PHOTO_FLAG,
+ OLD_LIFT_FLAG,
+ OLD_BUZZER_FLAG,
+ PAINTER_DIG_FLAG,
+ PAINTER_DISTRACTION_FLAG,
+ PAINTER_PAINTER_FLAG,
+ PAINTER_TEMPLAR_FLAG,
+ PAINTER_CONTROL_FLAG,
+ PAINTER_TEXT,
+ PEARL_AKRON_FLAG,
+ PEARL_ARTO_FLAG,
+ PEARL_BULL_FLAG,
+ PEARL_DUANE_FLAG,
+ PEARL_NEJO_FLAG,
+ PEARL_PEARL_FLAG,
+ PEARL_PHRASE_FLAG,
+ PEARL_POEMS_FLAG,
+ PEARL_STATUE_FLAG,
+ PEARL_TEMPLAR_FLAG,
+ PEARL_TEXT,
+ PEARL_ULTAR_FLAG,
+ PEARL_TALK_FLAG,
+ PEARL_CDT_FLAG,
+ PEARL_STALL_FLAG,
+ PEARL_WEAVER_FLAG,
+ PIERMONT_ASSASSIN_FLAG,
+ PIERMONT_BUZZER_FLAG,
+ PIERMONT_CLOWN_FLAG,
+ PIERMONT_GEM_FLAG,
+ PIERMONT_HKEY_FLAG,
+ PIERMONT_KEY_FLAG,
+ PIERMONT_KEY_ALERT_FLAG,
+ PIERMONT_MS_FLAG,
+ PIERMONT_NOSE_FLAG,
+ PIERMONT_PASS_FLAG,
+ PIERMONT_PHOTO_FLAG,
+ PIERMONT_PIERMONT_FLAG,
+ PIERMONT_TEMPLAR_FLAG,
+ PIERMONT_TEXT,
+ PIERMONT_TISSUE_FLAG,
+ PIERMONT_WEASEL_FLAG,
+ PRIEST_TEXT,
+ PRIEST_CHALICE_FLAG,
+ PRIEST_CHALICE2_FLAG,
+ PRIEST_TEMPLAR_FLAG,
+ PRIEST_PRIEST_FLAG,
+ PRIEST_WINDO1_FLAG,
+ PRIEST_WINDO2_FLAG,
+ PRIEST_WINDO3_FLAG,
+ RENEE_MARQUET_FLAG,
+ RENEE_PHOTO_FLAG,
+ RENEE_RENEE_FLAG,
+ RENEE_TEXT,
+ RON_ALERT_FLAG,
+ RON_BEER_FLAG,
+ RON_CASTLE_FLAG,
+ RON_DIG_FLAG,
+ RON_FLASHLIGHT_FLAG,
+ RON_GHOST_FLAG,
+ RON_NOSE_FLAG,
+ RON_PASS_FLAG,
+ RON_PEAGRAM_FLAG,
+ RON_PHOTO_FLAG,
+ RON_POLICE_FLAG,
+ RON_SEAN_FLAG,
+ RON_SNARE_FLAG,
+ RON_STOP_FLAG,
+ RON_TEXT,
+ RON_UPSET_FLAG,
+ ROSSO_CDT_FLAG,
+ ROSSO_CLOWN_FLAG,
+ ROSSO_DOCTOR_FLAG,
+ ROSSO_FORTUNE_FLAG,
+ ROSSO_GEM_FLAG,
+ ROSSO_MARQUET_FLAG,
+ ROSSO_MATCHBOOK_FLAG,
+ ROSSO_MOUE_FLAG,
+ ROSSO_OPINION_FLAG,
+ ROSSO_PASS_FLAG,
+ ROSSO_PEAGRAM_FLAG,
+ ROSSO_PHOTO_FLAG,
+ ROSSO_PLANTARD_FLAG,
+ ROSSO_ROSSO_FLAG,
+ ROSSO_TALK_FLAG,
+ ROSSO_TEMPLAR_FLAG,
+ ROSSO_TEXT,
+ ROSSO_THUGS_FLAG,
+ ROZZER_36_FLAG,
+ ROZZER_JUGGLER_FLAG,
+ ROZZER_MANHOLE_FLAG,
+ ROZZER_PLASTER_FLAG,
+ ROZZER_ROZZER_FLAG,
+ ROZZER_TEMPLAR_FLAG,
+ ROZZER_TEXT,
+ SAM_BREAKDOWN_FLAG,
+ SAM_BUZZER_FLAG,
+ SAM_CUPBOARD_FLAG,
+ SAM_GEM_FLAG,
+ SAM_MARQUET_FLAG,
+ SAM_MATCHBOOK_FLAG,
+ SAM_MOB_FLAG,
+ SAM_NOSE_FLAG,
+ SAM_NURSE_FLAG,
+ SAM_PHOTO_FLAG,
+ SAM_PLASTER_FLAG,
+ SAM_SHINY_FLAG,
+ SAM_SOCKET_FLAG,
+ SAM_STOP_FLAG,
+ SAM_TEXT,
+ SEAN_ASSASSIN_FLAG,
+ SEAN_BEER_FLAG,
+ SEAN_CASTLE_FLAG,
+ SEAN_DIG_FLAG,
+ SEAN_GEM_FLAG,
+ SEAN_LKEYS_FLAG,
+ SEAN_NOSE_FLAG,
+ SEAN_OPINION,
+ SEAN_PACKAGE_FLAG,
+ SEAN_PEAGRAM_FLAG,
+ SEAN_SELF_FLAG,
+ SEAN_SNAP_FLAG,
+ SEAN_TEXT,
+ STATUE_GUARD_CONTROL_FLAG,
+ STATUE_GUARD_FLAG,
+ STATUE_GUARD_GUARD_FLAG,
+ STATUE_GUARD_KEY,
+ GUARD_GLOVE_FLAG,
+ STATUE_GUARD_TEMPLAR_FLAG,
+ STATUE_GUARD_THERMO_FLAG,
+ STATUE_GUARD_TEXT,
+ STATUE_GUARD_TALK_FLAG,
+ STATUE_GUARD_CDT_FLAG,
+ TCLERK_PIERMONT_FLAG,
+ TNIC_ENQUIRY_FLAG,
+ TODRYK_CLOWN_FLAG,
+ TODRYK_EYE_FLAG,
+ TODRYK_GEORGE_FLAG,
+ TODRYK_OPINION_FLAG,
+ TODRYK_PHOTO_FLAG,
+ TODRYK_PLANTARD_FLAG,
+ TODRYK_ROSSO_FLAG,
+ TODRYK_TEXT,
+ ULTAR_ARTO_FLAG,
+ ULTAR_BALL_FLAG,
+ ULTAR_BULL_FLAG,
+ ULTAR_BUZZER_FLAG,
+ ULTAR_CHALICE_FLAG,
+ ULTAR_CLUB_FLAG,
+ ULTAR_DOLLARS_FLAG,
+ ULTAR_GOODBYE_FLAG,
+ ULTAR_HENDERSONS_FLAG,
+ ULTAR_KLAUSNER_FLAG,
+ ULTAR_LAB_PASS_FLAG,
+ ULTAR_LIFTING_KEYS_FLAG,
+ ULTAR_LOOM_FLAG,
+ ULTAR_NEJO_FLAG,
+ ULTAR_PHOTOGRAPH_FLAG,
+ ULTAR_PHRASE_FLAG,
+ ULTAR_PRESSURE_GAUGE_FLAG,
+ ULTAR_RED_NOSE_FLAG,
+ ULTAR_SIGN_FLAG,
+ ULTAR_STATUETTE_FLAG,
+ ULTAR_STATUETTE_PAINT_FLAG,
+ ULTAR_TISSUE_FLAG,
+ ULTAR_TEMPLAR_FLAG,
+ ULTAR_TAXI_FLAG,
+ ULTAR_TOILET_BRUSH_FLAG,
+ ULTAR_TOILET_CHAIN_FLAG,
+ ULTAR_TOILET_KEY_FLAG,
+ ULTAR_TOWEL_FLAG,
+ ULTAR_PLASTER_FLAG,
+ ULTAR_TEXT,
+ COUNTESS_56A_SUBJECT_FLAG,
+ COUNTESS_56A_GOODBYE_FLAG,
+ COUNTESS_56B_GOODBYE_FLAG,
+ COUNTESS_TALK_FLAG,
+ COUNTESS_CDT_FLAG,
+ VAS_BALL_FLAG,
+ VAS_COUNTESS_FLAG,
+ VAS_GOODBYE_FLAG,
+ VAS_KEY_FLAG,
+ VAS_PHOTO_FLAG,
+ VAS_TALK,
+ VAS_TEXT,
+ VAS_TEXT_TOGGLE,
+ VAS_TEMPLAR_FLAG,
+ VAS_CURSE_FLAG,
+ VAS_PCHALICE_FLAG,
+ GEORGE59A,
+ VAIL_TEXT,
+ VAIL_TALK_FLAG,
+ VAIL_CDT_FLAG,
+ WEASEL_CLOWN_FLAG,
+ WEASEL_KHAN_FLAG,
+ WEASEL_GUIDO_FLAG,
+ WEASEL_PLANTARD_FLAG,
+ WEASEL_ROSSO_FLAG,
+ WEASEL_STOP_FLAG,
+ WEASEL_TEXT,
+ WORKMAN_CLOWN_FLAG,
+ WORKMAN_COP_FLAG,
+ WORKMAN_PHONE_ALERT_FLAG,
+ WORKMAN_PLANTARD_FLAG,
+ WORKMAN_ROSSO_CARD,
+ WORKMAN_STOP_FLAG,
+ WORKMAN_TOOL_FLAG,
+ WORKMAN_TOOLBOX_FLAG,
+ WORKMAN_TEXT,
+ GEORGE_TALK_FLAG,
+ GEORGE_CDT_FLAG,
+ CHOOSER_COUNT_FLAG,
+ HURRY_FLAG,
+ IRELAND_FLAG,
+ IRELAND_MAP_FLAG,
+ KNOWS_PEAGRAM_FLAG,
+ KNOWS_PHILIP_FLAG,
+ MANUSCRIPT_FLAG,
+ OBJECT_HELD,
+ OBJECT_ICON,
+ OBJECT_TALK,
+ PARIS_FLAG,
+ RESPONSERECEIVED,
+ SCENE_FLAG,
+ SCREEN,
+ SCORE_FLAG,
+ SCOTLAND_MAP_FLAG,
+ SPAIN_MAP_FLAG,
+ SYRIA_FLAG,
+ TALK_FLAG,
+ WEIRD_ZONE,
+ TARGET_MEGA,
+ CHURCH_ARRIVAL_FLAG,
+ SHH_ALERT_FLAG,
+ AEROPORT_ADDRESS_FLAG,
+ CHANTELLE_BRANDY_FLAG,
+ CHURCH_FLAG,
+ CHOOSE_GAUGE_FLAG,
+ CLERK_AT_DESK_FLAG,
+ CONSULTANT_STOP_FLAG,
+ COSTUMES_ADDRESS_FLAG,
+ COSTUMES_PHONE_FLAG,
+ FOUND_WARD_FLAG,
+ GEORGE_POS_FLAG,
+ GOT_BENOIR_FLAG,
+ HOLE_FLAG,
+ HOSPITAL_ADDRESS_FLAG,
+ HOSPITAL_VISIT_FLAG,
+ HOS_POS_FLAG,
+ HOTEL_ADDRESS_FLAG,
+ IRELAND_ALERT_FLAG,
+ KEY_ALERT_FLAG,
+ KEYRING_FLAG,
+ KEY_TALK,
+ KNOWS_MOERLIN_FLAG,
+ LENS_FLAG,
+ MACDEVITTS_PHONE_FLAG,
+ MANUSCRIPT_ALERT_FLAG,
+ MANUSCRIPT_VIEW_FLAG,
+ MEETING_FLAG,
+ MESSAGE_FLAG,
+ MONTFACN_ADDRESS_FLAG,
+ MONTFAUCON_CONTROL_FLAG,
+ MUSEUM_ADDRESS_FLAG,
+ MUSEUM_CLOSING_FLAG,
+ MUSEUM_PHONE_FLAG,
+ NERVAL_ADDRESS_FLAG,
+ NICO_ADDRESS_FLAG,
+ NICO_APT_FLAG,
+ NICO_DOOR_FLAG,
+ NICO_GONE_HOME_FLAG,
+ NICO_PHONE_FLAG,
+ NICO_VISIT_FLAG,
+ NURSE_TELEPHONE_FLAG,
+ PAINT_TALK,
+ PAINTPOT_FLAG,
+ PARIS_STATUE_FLAG,
+ PHONE_CHECK,
+ PHONE_REQUEST,
+ POLICE_ADDRESS_FLAG,
+ POLICE_PHONE_FLAG,
+ POLISHER_PLUG_FLAG,
+ POS_FLAG,
+ RADIO_ALERT_FLAG,
+ READ_NEWSPAPER,
+ READ_NOSE_FLAG,
+ SARCOPHAGUS_ALERT_FLAG,
+ SC28_COIN_FLAG,
+ SC28_POTTERY_FLAG,
+ SC48_SCROLL_FLAG,
+ SEEN_BRIEFCASE_FLAG,
+ SEEN_DOOR22_FLAG,
+ SEEN_KEY_FLAG,
+ SEEN_MANHOLE_FLAG,
+ SEEN_PLANTARD_FLAG,
+ SEEN_REGISTER_FLAG,
+ SEEN_SEWERS_FLAG,
+ SEEN_TRIPOD_FLAG,
+ SEWER_EXIT_FLAG,
+ SKIP_TALK,
+ SOAP_FLAG,
+ ERIC_TEXT,
+ TAILOR_PHONE_FLAG,
+ THERMO_FLAG,
+ TOILET_TALK,
+ TOMB_FLAG,
+ TORCH_ALERT_FLAG,
+ TOTEM_ALERT_FLAG,
+ TRIPOD_FLAG,
+ TRIPOD_ALERT_FLAG,
+ TRIPOD_STOLEN_FLAG,
+ WARD_STOP_FLAG,
+ WHITE_COAT_FLAG,
+ WINDOW_ALERT_FLAG,
+ WORKMAN_GONE_FLAG,
+ CLIMBING_CART_FLAG,
+ FIDDLER_TEXT,
+ PEAGRAM_GONE_FLAG,
+ PINT_FLAG,
+ PUB_ELEC_FLAG,
+ PUB_INTERRUPTION_FLAG,
+ PUB_TAP_FLAG,
+ SEEN_GOAT_FLAG,
+ SYRIA_BOOK_FLAG,
+ SEEN_BRUSH_FLAG,
+ SEEN_STATUE_FLAG,
+ SYRIA_DEAD_FLAG,
+ SYRIA_NICHE_FLAG,
+ ARMOUR_HIDE_FLAG,
+ CANDLE59_FLAG,
+ CANDLE_BURNT,
+ CHALICE_FLAG,
+ CHESSET_FLAG,
+ CHESSBOARD_FLAG,
+ DOOR_REVEALED,
+ DOWSE_FLAG,
+ GEORGE_POSITION,
+ GEORGE_SAFE,
+ GEORGE_WELL_FLAG,
+ HAZEL_FLAG,
+ INTRO_FLAG,
+ LION_FANG,
+ LOGS_56_FLAG,
+ MARY_FLAG,
+ MIRROR_HINT,
+ ROCKFALL_1,
+ ROCKFALL_2,
+ SECOND_CURSE_FLAG,
+ SPAIN_CODA,
+ TOMBS59_FLAG,
+ ASSASSIN_KILLED_FLAG,
+ AXE_ALERT_FLAG,
+ DOOR_SC69_ALERT_FLAG,
+ DOOR_SC65_FLAG,
+ EKLUND_KILLED,
+ FINALE_OPTION_FLAG,
+ NICO_GONE_FLAG,
+ NICO_TIED_FLAG,
+ PIPE_ALERT_FLAG,
+ SEEN_GUIDO_63,
+ END_SCENE,
+ MASTER_39_TALK_FLAG,
+ MASTER_39_CDT_FLAG,
+ COLONEL_TALK_FLAG,
+ COLONEL_CDT_FLAG,
+ EXEC_TALK_FLAG,
+ EXEC_CDT_FLAG,
+ CIVIL_TALK_FLAG,
+ CIVIL_CDT_FLAG,
+ LATVIAN_39_TALK_FLAG,
+ LATVIAN_39_CDT_FLAG,
+ EKLUND_39_TALK_FLAG,
+ EKLUND_39_CDT_FLAG,
+ CAFE_BOMBED,
+ BLIND_ALLEY,
+ CAFE_INTERIOR,
+ ROAD_WORKS,
+ COURT_YARD,
+ SEWER_ONE,
+ SEWER_TWO,
+ CAFE_REPAIRED,
+ APT_STREET,
+ APT_NICO,
+ COSTUME_SHOP,
+ HOTEL_STREET,
+ HOTEL_DESK,
+ HOTEL_CORRIDOR,
+ HOTEL_EMPTY,
+ HOTEL_LEDGE,
+ HOTEL_ASSASSIN,
+ GENDARMERIE,
+ IRELAND_STREET,
+ MACDEVITTS,
+ PUB_CELLAR,
+ CASTLE_GATE,
+ CASTLE_HAY_TOP,
+ CASTLE_YARD,
+ CASTLE_DIG,
+ CELLAR_DARK,
+ MUSEUM_STREET,
+ MUSEUM_ONE,
+ MUSEUM_TWO,
+ MUSEUM_HIDING,
+ HOSPITAL_STREET,
+ HOSPITAL_DESK,
+ HOSPITAL_CORRIDOR,
+ HOSPITAL_WARD,
+ HOSPITAL_JACQUES,
+ MONTFAUCON,
+ CATACOMB_SEWER,
+ CATACOMB_ROOM,
+ CATACOMB_MEETING,
+ EXCAVATION_EXT,
+ EXCAVATION_LOBBY,
+ EXCAVATION_DIG,
+ EXCAVATION_TOILET,
+ EXCAVATION_SECRET,
+ TEMPLAR_CHURCH,
+ SYRIA_STALL,
+ SYRIA_CARPET,
+ SYRIA_CLUB,
+ SYRIA_TOILET,
+ BULL_CLIFF,
+ BULL_INTERIOR,
+ MAUSOLEUM_EXT,
+ SPAIN_DRIVE,
+ SPAIN_GARDEN,
+ MAUSOLEUM_INT,
+ SPAIN_RECEPTION,
+ SPAIN_WELL,
+ SPAIN_SECRET,
+ TRAIN_ONE,
+ TRAIN_TWO,
+ COMPT_ONE,
+ COMPT_TWO,
+ COMPT_THREE,
+ COMPT_FOUR,
+ TRAIN_GUARD,
+ CHURCHYARD,
+ CHURCH_TOWER,
+ CRYPT,
+ SECRET_CRYPT,
+ POCKET_1,
+ POCKET_2,
+ POCKET_3,
+ POCKET_4,
+ POCKET_5,
+ POCKET_6,
+ POCKET_7,
+ POCKET_8,
+ POCKET_9,
+ POCKET_10,
+ POCKET_11,
+ POCKET_12,
+ POCKET_13,
+ POCKET_14,
+ POCKET_15,
+ POCKET_16,
+ POCKET_17,
+ POCKET_18,
+ POCKET_19,
+ POCKET_20,
+ POCKET_21,
+ POCKET_22,
+ POCKET_23,
+ POCKET_24,
+ POCKET_25,
+ POCKET_26,
+ POCKET_27,
+ POCKET_28,
+ POCKET_29,
+ POCKET_30,
+ POCKET_31,
+ POCKET_32,
+ POCKET_33,
+ POCKET_34,
+ POCKET_35,
+ POCKET_36,
+ POCKET_37,
+ POCKET_38,
+ POCKET_39,
+ POCKET_40,
+ POCKET_41,
+ POCKET_42,
+ POCKET_43,
+ POCKET_44,
+ POCKET_45,
+ POCKET_46,
+ POCKET_47,
+ POCKET_48,
+ POCKET_49,
+ POCKET_50,
+ POCKET_51,
+ POCKET_52
+};
+
+#define SAND_25 1638407
+#define HOLDING_REPLICA_25 1638408
+#define GMASTER_79 5177345
+#define SCR_std_off (0*0x10000 + 6)
+#define SCR_exit0 (0*0x10000 + 7)
+#define SCR_exit1 (0*0x10000 + 8)
+#define SCR_exit2 (0*0x10000 + 9)
+#define SCR_exit3 (0*0x10000 + 10)
+#define SCR_exit4 (0*0x10000 + 11)
+#define SCR_exit5 (0*0x10000 + 12)
+#define SCR_exit6 (0*0x10000 + 13)
+#define SCR_exit7 (0*0x10000 + 14)
+#define SCR_exit8 (0*0x10000 + 15)
+#define SCR_exit9 (0*0x10000 + 16)
+#define LEFT_SCROLL_POINTER 8388610
+#define RIGHT_SCROLL_POINTER 8388611
+#define FLOOR_63 4128768
+#define ROOF_63 4128779
+#define GUARD_ROOF_63 4128781
+#define LEFT_TREE_POINTER_71 4653058
+#define RIGHT_TREE_POINTER_71 4653059
+#define SCR_menu_look (0*0x10000 + 24)
+#define SCR_icon_combine_script (0*0x10000 + 25)
+
+} // End of namespace Sword1
+
+#endif //SWORDDEFS_H
diff --git a/engines/sword1/swordres.h b/engines/sword1/swordres.h
new file mode 100644
index 0000000000..dca5330350
--- /dev/null
+++ b/engines/sword1/swordres.h
@@ -0,0 +1,5223 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SWORDRES_H
+#define SWORDRES_H
+
+namespace Sword1 {
+
+// scripts
+ // standard
+#define SCRIPT0 0x01000000
+ // 1 entities in TXTs, 1 in datafiles.
+ // megas
+#define SCRIPT128 0x01010000
+#define SCRIPT129 0x01010001
+#define SCRIPT130 0x01010002
+#define SCRIPT131 0x01010003
+#define SCRIPT133 0x01010004
+#define SCRIPT134 0x01010005
+#define SCRIPT145 0x01010006
+#define SCRIPT146 0x01010007
+ // 8 entities in TXTs, 8 in datafiles.
+ // maps
+#define SCRIPT80 0x01020000
+#define SCRIPT86 0x01020001
+#define SCRIPT90 0x01020002
+ // 3 entities in TXTs, 3 in datafiles.
+ // paris1
+#define SCRIPT1 0x01030000
+#define SCRIPT2 0x01030001
+#define SCRIPT3 0x01030002
+#define SCRIPT4 0x01030003
+#define SCRIPT5 0x01030004
+#define SCRIPT6 0x01030005
+#define SCRIPT7 0x01030006
+#define SCRIPT8 0x01030007
+ // 8 entities in TXTs, 8 in datafiles.
+ // paris2
+#define SCRIPT9 0x01040000
+#define SCRIPT10 0x01040001
+#define SCRIPT11 0x01040002
+#define SCRIPT12 0x01040003
+#define SCRIPT13 0x01040004
+#define SCRIPT14 0x01040005
+#define SCRIPT15 0x01040006
+#define SCRIPT16 0x01040007
+#define SCRIPT17 0x01040008
+#define SCRIPT18 0x01040009
+#define SCRIPT46 0x0104000A
+ // 11 entities in TXTs, 11 in datafiles.
+ // paris3
+#define SCRIPT27 0x01050000
+#define SCRIPT28 0x01050001
+#define SCRIPT29 0x01050002
+#define SCRIPT31 0x01050003
+#define SCRIPT32 0x01050004
+#define SCRIPT33 0x01050005
+#define SCRIPT34 0x01050006
+#define SCRIPT35 0x01050007
+ // 8 entities in TXTs, 8 in datafiles.
+ // paris4
+#define SCRIPT36 0x01060000
+#define SCRIPT37 0x01060001
+#define SCRIPT38 0x01060002
+#define SCRIPT39 0x01060003
+#define SCRIPT40 0x01060004
+#define SCRIPT41 0x01060005
+#define SCRIPT42 0x01060006
+#define SCRIPT43 0x01060007
+#define SCRIPT48 0x01060008
+ // 9 entities in TXTs, 9 in datafiles.
+ // ireland
+#define SCRIPT19 0x01070000
+#define SCRIPT20 0x01070001
+#define SCRIPT21 0x01070002
+#define SCRIPT22 0x01070003
+#define SCRIPT23 0x01070004
+#define SCRIPT24 0x01070005
+#define SCRIPT25 0x01070006
+#define SCRIPT26 0x01070007
+ // 8 entities in TXTs, 8 in datafiles.
+ // spain
+#define SCRIPT56 0x01080000
+#define SCRIPT57 0x01080001
+#define SCRIPT58 0x01080002
+#define SCRIPT59 0x01080003
+#define SCRIPT60 0x01080004
+#define SCRIPT61 0x01080005
+#define SCRIPT62 0x01080006
+ // 7 entities in TXTs, 7 in datafiles.
+ // syria
+#define SCRIPT45 0x01090000
+#define SCRIPT47 0x01090001
+#define SCRIPT49 0x01090002
+#define SCRIPT50 0x01090003
+#define SCRIPT54 0x01090004
+#define SCRIPT55 0x01090005
+ // 6 entities in TXTs, 6 in datafiles.
+ // train
+#define SCRIPT63 0x010A0000
+#define SCRIPT65 0x010A0001
+#define SCRIPT66 0x010A0002
+#define SCRIPT67 0x010A0003
+#define SCRIPT69 0x010A0004
+ // 5 entities in TXTs, 5 in datafiles.
+ // scotland
+#define SCRIPT71 0x010B0000
+#define SCRIPT72 0x010B0001
+#define SCRIPT73 0x010B0002
+#define SCRIPT74 0x010B0003
+ // 4 entities in TXTs, 4 in datafiles.
+// compacts
+ // standard
+#define TEXT_OBS 0x02000000
+ // 1 entities in TXTs, 1 in datafiles.
+ // megas
+#define MEGA_GEO 0x02010000
+#define GEO_TLK_TABLE 0x02010001
+#define WHT_TLK_TABLE 0x02010002
+#define GEO_SHRUG_TABLE 0x02010003
+#define GEO_GIVE_TABLE 0x02010004
+#define GEO_GIVER_TABLE 0x02010005
+#define GEO_CROUCH_TABLE 0x02010006
+#define GEO_CROUCHR_TABLE 0x02010007
+#define GEO_PICKUP_TABLE 0x02010008
+#define GEO_RELAX_TABLE 0x02010009
+#define GEO_RELAXR_TABLE 0x0201000A
+#define GEO_HAIR_TABLE 0x0201000B
+#define GEO_BLINK_TABLE 0x0201000C
+#define GEOPHN_TABLE 0x0201000D
+#define GEO_GAUGE_TABLE 0x0201000E
+#define GEO_SHOW_TABLE 0x0201000F
+#define GEO_SHOWR_TABLE 0x02010010
+#define GEOSTA_TABLE 0x02010011
+#define GEOMON_TABLE 0x02010012
+#define MEGA_DUANE 0x02010013
+#define DUA5_TABLE 0x02010014
+#define DUA6_TABLE 0x02010015
+#define DUA7_TABLE 0x02010016
+#define DUA8_TABLE 0x02010017
+#define DUA9_TABLE 0x02010018
+#define MEGA_NICO 0x02010019
+#define NIC_TLK_TABLE 0x0201001A
+#define NIC_PHOTO_TABLE 0x0201001B
+#define NICPHN_TABLE 0x0201001C
+#define MEGA_MUS 0x0201001D
+#define MUS_TLK_TABLE 0x0201001E
+#define MEGA_BENOIR 0x0201001F
+#define BEN_GAUGE_TABLE 0x02010020
+#define BEN_TLK_TABLE 0x02010021
+#define MEGA_ROSSO 0x02010022
+#define MEGA_MOUE 0x02010023
+#define MEGA_ALBERT 0x02010024
+ // 37 entities in TXTs, 37 in datafiles.
+ // maps
+#define COMP80 0x02020000
+#define COMP86 0x02020001
+#define COMP90 0x02020002
+#define COMP91 0x02020003
+#define COMP99 0x02020004
+ // 5 entities in TXTs, 5 in datafiles.
+ // paris1
+#define COMP1 0x02030000
+#define COMP2 0x02030001
+#define COMP3 0x02030002
+#define COMP4 0x02030003
+#define COMP5 0x02030004
+#define COMP6 0x02030005
+#define COMP7 0x02030006
+#define COMP8 0x02030007
+ // 8 entities in TXTs, 8 in datafiles.
+ // paris2
+#define COMP9 0x02040000
+#define COMP10 0x02040001
+#define COMP11 0x02040002
+#define COMP12 0x02040003
+#define COMP13 0x02040004
+#define COMP14 0x02040005
+#define COMP15 0x02040006
+#define COMP16 0x02040007
+#define COMP17 0x02040008
+#define COMP18 0x02040009
+#define COMP81 0x0204000A
+#define COMP46 0x0204000B
+ // 12 entities in TXTs, 12 in datafiles.
+ // paris3
+#define COMP27 0x02050000
+#define COMP28 0x02050001
+#define COMP29 0x02050002
+#define COMP30 0x02050003
+#define COMP31 0x02050004
+#define COMP32 0x02050005
+#define COMP33 0x02050006
+#define COMP34 0x02050007
+#define COMP35 0x02050008
+ // 9 entities in TXTs, 9 in datafiles.
+ // paris4
+#define COMP36 0x02060000
+#define COMP37 0x02060001
+#define COMP38 0x02060002
+#define COMP39 0x02060003
+#define COMP40 0x02060004
+#define COMP41 0x02060005
+#define COMP42 0x02060006
+#define COMP43 0x02060007
+#define COMP48 0x02060008
+ // 9 entities in TXTs, 9 in datafiles.
+ // ireland
+#define COMP19 0x02070000
+#define COMP20 0x02070001
+#define COMP21 0x02070002
+#define COMP22 0x02070003
+#define COMP23 0x02070004
+#define COMP24 0x02070005
+#define COMP25 0x02070006
+#define COMP26 0x02070007
+ // 8 entities in TXTs, 8 in datafiles.
+ // spain
+#define COMP56 0x02080000
+#define COMP57 0x02080001
+#define COMP58 0x02080002
+#define COMP59 0x02080003
+#define COMP60 0x02080004
+#define COMP61 0x02080005
+#define COMP62 0x02080006
+ // 7 entities in TXTs, 7 in datafiles.
+ // syria
+#define COMP45 0x02090000
+#define COMP47 0x02090001
+#define COMP49 0x02090002
+#define COMP50 0x02090003
+#define COMP53 0x02090004
+#define COMP54 0x02090005
+#define COMP55 0x02090006
+ // 7 entities in TXTs, 7 in datafiles.
+ // train
+#define COMP63 0x020A0000
+#define COMP65 0x020A0001
+#define COMP66 0x020A0002
+#define COMP67 0x020A0003
+#define COMP69 0x020A0004
+ // 5 entities in TXTs, 5 in datafiles.
+ // scotland
+#define COMP71 0x020B0000
+#define COMP72 0x020B0001
+#define COMP73 0x020B0002
+#define COMP74 0x020B0003
+#define COMP75 0x020B0004
+#define COMP76 0x020B0005
+#define COMP77 0x020B0006
+#define COMP78 0x020B0007
+#define COMP79 0x020B0008
+ // 9 entities in TXTs, 9 in datafiles.
+// text
+ // english
+#define ENGLISH0 0x03000000
+#define ENGLISH1 0x03000001
+#define ENGLISH2 0x03000002
+#define ENGLISH3 0x03000003
+#define ENGLISH4 0x03000004
+#define ENGLISH5 0x03000005
+#define ENGLISH6 0x03000006
+#define ENGLISH7 0x03000007
+#define ENGLISH9 0x03000008
+#define ENGLISH11 0x03000009
+#define ENGLISH12 0x0300000A
+#define ENGLISH13 0x0300000B
+#define ENGLISH14 0x0300000C
+#define ENGLISH15 0x0300000D
+#define ENGLISH16 0x0300000E
+#define ENGLISH17 0x0300000F
+#define ENGLISH18 0x03000010
+#define ENGLISH19 0x03000011
+#define ENGLISH20 0x03000012
+#define ENGLISH21 0x03000013
+#define ENGLISH22 0x03000014
+#define ENGLISH23 0x03000015
+#define ENGLISH24 0x03000016
+#define ENGLISH25 0x03000017
+#define ENGLISH27 0x03000018
+#define ENGLISH28 0x03000019
+#define ENGLISH29 0x0300001A
+#define ENGLISH31 0x0300001B
+#define ENGLISH32 0x0300001C
+#define ENGLISH33 0x0300001D
+#define ENGLISH34 0x0300001E
+#define ENGLISH35 0x0300001F
+#define ENGLISH36 0x03000020
+#define ENGLISH37 0x03000021
+#define ENGLISH38 0x03000022
+#define ENGLISH39 0x03000023
+#define ENGLISH40 0x03000024
+#define ENGLISH41 0x03000025
+#define ENGLISH42 0x03000026
+#define ENGLISH43 0x03000027
+#define ENGLISH45 0x03000028
+#define ENGLISH47 0x03000029
+#define ENGLISH48 0x0300002A
+#define ENGLISH49 0x0300002B
+#define ENGLISH50 0x0300002C
+#define ENGLISH54 0x0300002D
+#define ENGLISH55 0x0300002E
+#define ENGLISH56 0x0300002F
+#define ENGLISH57 0x03000030
+#define ENGLISH58 0x03000031
+#define ENGLISH59 0x03000032
+#define ENGLISH60 0x03000033
+#define ENGLISH61 0x03000034
+#define ENGLISH63 0x03000035
+#define ENGLISH65 0x03000036
+#define ENGLISH66 0x03000037
+#define ENGLISH69 0x03000038
+#define ENGLISH71 0x03000039
+#define ENGLISH72 0x0300003A
+#define ENGLISH73 0x0300003B
+#define ENGLISH74 0x0300003C
+#define ENGLISH90 0x0300003D
+#define ENGLISH99 0x0300003E
+#define ENGLISH129 0x0300003F
+#define ENGLISH131 0x03000040
+#define ENGLISH133 0x03000041
+#define ENGLISH145 0x03000042
+#define ENGLISH146 0x03000043
+ // 68 entities in TXTs, 68 in datafiles.
+ // french
+#define FRENCH0 0x03010000
+#define FRENCH1 0x03010001
+#define FRENCH2 0x03010002
+#define FRENCH3 0x03010003
+#define FRENCH4 0x03010004
+#define FRENCH5 0x03010005
+#define FRENCH6 0x03010006
+#define FRENCH7 0x03010007
+#define FRENCH9 0x03010008
+#define FRENCH11 0x03010009
+#define FRENCH12 0x0301000A
+#define FRENCH13 0x0301000B
+#define FRENCH14 0x0301000C
+#define FRENCH15 0x0301000D
+#define FRENCH16 0x0301000E
+#define FRENCH17 0x0301000F
+#define FRENCH18 0x03010010
+#define FRENCH19 0x03010011
+#define FRENCH20 0x03010012
+#define FRENCH21 0x03010013
+#define FRENCH22 0x03010014
+#define FRENCH23 0x03010015
+#define FRENCH24 0x03010016
+#define FRENCH25 0x03010017
+#define FRENCH27 0x03010018
+#define FRENCH28 0x03010019
+#define FRENCH29 0x0301001A
+#define FRENCH31 0x0301001B
+#define FRENCH32 0x0301001C
+#define FRENCH33 0x0301001D
+#define FRENCH34 0x0301001E
+#define FRENCH35 0x0301001F
+#define FRENCH36 0x03010020
+#define FRENCH37 0x03010021
+#define FRENCH38 0x03010022
+#define FRENCH39 0x03010023
+#define FRENCH40 0x03010024
+#define FRENCH41 0x03010025
+#define FRENCH42 0x03010026
+#define FRENCH43 0x03010027
+#define FRENCH45 0x03010028
+#define FRENCH47 0x03010029
+#define FRENCH48 0x0301002A
+#define FRENCH49 0x0301002B
+#define FRENCH50 0x0301002C
+#define FRENCH54 0x0301002D
+#define FRENCH55 0x0301002E
+#define FRENCH56 0x0301002F
+#define FRENCH57 0x03010030
+#define FRENCH58 0x03010031
+#define FRENCH59 0x03010032
+#define FRENCH60 0x03010033
+#define FRENCH61 0x03010034
+#define FRENCH63 0x03010035
+#define FRENCH65 0x03010036
+#define FRENCH66 0x03010037
+#define FRENCH69 0x03010038
+#define FRENCH71 0x03010039
+#define FRENCH72 0x0301003A
+#define FRENCH73 0x0301003B
+#define FRENCH74 0x0301003C
+#define FRENCH90 0x0301003D
+#define FRENCH99 0x0301003E
+#define FRENCH129 0x0301003F
+#define FRENCH131 0x03010040
+#define FRENCH133 0x03010041
+#define FRENCH145 0x03010042
+#define FRENCH146 0x03010043
+ // 68 entities in TXTs, 68 in datafiles.
+ // german
+#define GERMAN0 0x03020000
+#define GERMAN1 0x03020001
+#define GERMAN2 0x03020002
+#define GERMAN3 0x03020003
+#define GERMAN4 0x03020004
+#define GERMAN5 0x03020005
+#define GERMAN6 0x03020006
+#define GERMAN7 0x03020007
+#define GERMAN9 0x03020008
+#define GERMAN11 0x03020009
+#define GERMAN12 0x0302000A
+#define GERMAN13 0x0302000B
+#define GERMAN14 0x0302000C
+#define GERMAN15 0x0302000D
+#define GERMAN16 0x0302000E
+#define GERMAN17 0x0302000F
+#define GERMAN18 0x03020010
+#define GERMAN19 0x03020011
+#define GERMAN20 0x03020012
+#define GERMAN21 0x03020013
+#define GERMAN22 0x03020014
+#define GERMAN23 0x03020015
+#define GERMAN24 0x03020016
+#define GERMAN25 0x03020017
+#define GERMAN27 0x03020018
+#define GERMAN28 0x03020019
+#define GERMAN29 0x0302001A
+#define GERMAN31 0x0302001B
+#define GERMAN32 0x0302001C
+#define GERMAN33 0x0302001D
+#define GERMAN34 0x0302001E
+#define GERMAN35 0x0302001F
+#define GERMAN36 0x03020020
+#define GERMAN37 0x03020021
+#define GERMAN38 0x03020022
+#define GERMAN39 0x03020023
+#define GERMAN40 0x03020024
+#define GERMAN41 0x03020025
+#define GERMAN42 0x03020026
+#define GERMAN43 0x03020027
+#define GERMAN45 0x03020028
+#define GERMAN47 0x03020029
+#define GERMAN48 0x0302002A
+#define GERMAN49 0x0302002B
+#define GERMAN50 0x0302002C
+#define GERMAN54 0x0302002D
+#define GERMAN55 0x0302002E
+#define GERMAN56 0x0302002F
+#define GERMAN57 0x03020030
+#define GERMAN58 0x03020031
+#define GERMAN59 0x03020032
+#define GERMAN60 0x03020033
+#define GERMAN61 0x03020034
+#define GERMAN63 0x03020035
+#define GERMAN65 0x03020036
+#define GERMAN66 0x03020037
+#define GERMAN69 0x03020038
+#define GERMAN71 0x03020039
+#define GERMAN72 0x0302003A
+#define GERMAN73 0x0302003B
+#define GERMAN74 0x0302003C
+#define GERMAN90 0x0302003D
+#define GERMAN99 0x0302003E
+#define GERMAN129 0x0302003F
+#define GERMAN131 0x03020040
+#define GERMAN133 0x03020041
+#define GERMAN145 0x03020042
+#define GERMAN146 0x03020043
+ // 68 entities in TXTs, 68 in datafiles.
+ // italian
+#define ITALIAN0 0x03030000
+#define ITALIAN1 0x03030001
+#define ITALIAN2 0x03030002
+#define ITALIAN3 0x03030003
+#define ITALIAN4 0x03030004
+#define ITALIAN5 0x03030005
+#define ITALIAN6 0x03030006
+#define ITALIAN7 0x03030007
+#define ITALIAN9 0x03030008
+#define ITALIAN11 0x03030009
+#define ITALIAN12 0x0303000A
+#define ITALIAN13 0x0303000B
+#define ITALIAN14 0x0303000C
+#define ITALIAN15 0x0303000D
+#define ITALIAN16 0x0303000E
+#define ITALIAN17 0x0303000F
+#define ITALIAN18 0x03030010
+#define ITALIAN19 0x03030011
+#define ITALIAN20 0x03030012
+#define ITALIAN21 0x03030013
+#define ITALIAN22 0x03030014
+#define ITALIAN23 0x03030015
+#define ITALIAN24 0x03030016
+#define ITALIAN25 0x03030017
+#define ITALIAN27 0x03030018
+#define ITALIAN28 0x03030019
+#define ITALIAN29 0x0303001A
+#define ITALIAN31 0x0303001B
+#define ITALIAN32 0x0303001C
+#define ITALIAN33 0x0303001D
+#define ITALIAN34 0x0303001E
+#define ITALIAN35 0x0303001F
+#define ITALIAN36 0x03030020
+#define ITALIAN37 0x03030021
+#define ITALIAN38 0x03030022
+#define ITALIAN39 0x03030023
+#define ITALIAN40 0x03030024
+#define ITALIAN41 0x03030025
+#define ITALIAN42 0x03030026
+#define ITALIAN43 0x03030027
+#define ITALIAN45 0x03030028
+#define ITALIAN47 0x03030029
+#define ITALIAN48 0x0303002A
+#define ITALIAN49 0x0303002B
+#define ITALIAN50 0x0303002C
+#define ITALIAN54 0x0303002D
+#define ITALIAN55 0x0303002E
+#define ITALIAN56 0x0303002F
+#define ITALIAN57 0x03030030
+#define ITALIAN58 0x03030031
+#define ITALIAN59 0x03030032
+#define ITALIAN60 0x03030033
+#define ITALIAN61 0x03030034
+#define ITALIAN63 0x03030035
+#define ITALIAN65 0x03030036
+#define ITALIAN66 0x03030037
+#define ITALIAN69 0x03030038
+#define ITALIAN71 0x03030039
+#define ITALIAN72 0x0303003A
+#define ITALIAN73 0x0303003B
+#define ITALIAN74 0x0303003C
+#define ITALIAN90 0x0303003D
+#define ITALIAN99 0x0303003E
+#define ITALIAN129 0x0303003F
+#define ITALIAN131 0x03030040
+#define ITALIAN133 0x03030041
+#define ITALIAN145 0x03030042
+#define ITALIAN146 0x03030043
+ // 68 entities in TXTs, 68 in datafiles.
+ // spanish
+#define SPANISH0 0x03040000
+#define SPANISH1 0x03040001
+#define SPANISH2 0x03040002
+#define SPANISH3 0x03040003
+#define SPANISH4 0x03040004
+#define SPANISH5 0x03040005
+#define SPANISH6 0x03040006
+#define SPANISH7 0x03040007
+#define SPANISH9 0x03040008
+#define SPANISH11 0x03040009
+#define SPANISH12 0x0304000A
+#define SPANISH13 0x0304000B
+#define SPANISH14 0x0304000C
+#define SPANISH15 0x0304000D
+#define SPANISH16 0x0304000E
+#define SPANISH17 0x0304000F
+#define SPANISH18 0x03040010
+#define SPANISH19 0x03040011
+#define SPANISH20 0x03040012
+#define SPANISH21 0x03040013
+#define SPANISH22 0x03040014
+#define SPANISH23 0x03040015
+#define SPANISH24 0x03040016
+#define SPANISH25 0x03040017
+#define SPANISH27 0x03040018
+#define SPANISH28 0x03040019
+#define SPANISH29 0x0304001A
+#define SPANISH31 0x0304001B
+#define SPANISH32 0x0304001C
+#define SPANISH33 0x0304001D
+#define SPANISH34 0x0304001E
+#define SPANISH35 0x0304001F
+#define SPANISH36 0x03040020
+#define SPANISH37 0x03040021
+#define SPANISH38 0x03040022
+#define SPANISH39 0x03040023
+#define SPANISH40 0x03040024
+#define SPANISH41 0x03040025
+#define SPANISH42 0x03040026
+#define SPANISH43 0x03040027
+#define SPANISH45 0x03040028
+#define SPANISH47 0x03040029
+#define SPANISH48 0x0304002A
+#define SPANISH49 0x0304002B
+#define SPANISH50 0x0304002C
+#define SPANISH54 0x0304002D
+#define SPANISH55 0x0304002E
+#define SPANISH56 0x0304002F
+#define SPANISH57 0x03040030
+#define SPANISH58 0x03040031
+#define SPANISH59 0x03040032
+#define SPANISH60 0x03040033
+#define SPANISH61 0x03040034
+#define SPANISH63 0x03040035
+#define SPANISH65 0x03040036
+#define SPANISH66 0x03040037
+#define SPANISH69 0x03040038
+#define SPANISH71 0x03040039
+#define SPANISH72 0x0304003A
+#define SPANISH73 0x0304003B
+#define SPANISH74 0x0304003C
+#define SPANISH90 0x0304003D
+#define SPANISH99 0x0304003E
+#define SPANISH129 0x0304003F
+#define SPANISH131 0x03040040
+#define SPANISH133 0x03040041
+#define SPANISH145 0x03040042
+#define SPANISH146 0x03040043
+ // 68 entities in TXTs, 68 in datafiles.
+ // czech
+#define CZECH0 0x03050000
+#define CZECH1 0x03050001
+#define CZECH2 0x03050002
+#define CZECH3 0x03050003
+#define CZECH4 0x03050004
+#define CZECH5 0x03050005
+#define CZECH6 0x03050006
+#define CZECH7 0x03050007
+#define CZECH9 0x03050008
+#define CZECH11 0x03050009
+#define CZECH12 0x0305000A
+#define CZECH13 0x0305000B
+#define CZECH14 0x0305000C
+#define CZECH15 0x0305000D
+#define CZECH16 0x0305000E
+#define CZECH17 0x0305000F
+#define CZECH18 0x03050010
+#define CZECH19 0x03050011
+#define CZECH20 0x03050012
+#define CZECH21 0x03050013
+#define CZECH22 0x03050014
+#define CZECH23 0x03050015
+#define CZECH24 0x03050016
+#define CZECH25 0x03050017
+#define CZECH27 0x03050018
+#define CZECH28 0x03050019
+#define CZECH29 0x0305001A
+#define CZECH31 0x0305001B
+#define CZECH32 0x0305001C
+#define CZECH33 0x0305001D
+#define CZECH34 0x0305001E
+#define CZECH35 0x0305001F
+#define CZECH36 0x03050020
+#define CZECH37 0x03050021
+#define CZECH38 0x03050022
+#define CZECH39 0x03050023
+#define CZECH40 0x03050024
+#define CZECH41 0x03050025
+#define CZECH42 0x03050026
+#define CZECH43 0x03050027
+#define CZECH45 0x03050028
+#define CZECH47 0x03050029
+#define CZECH48 0x0305002A
+#define CZECH49 0x0305002B
+#define CZECH50 0x0305002C
+#define CZECH54 0x0305002D
+#define CZECH55 0x0305002E
+#define CZECH56 0x0305002F
+#define CZECH57 0x03050030
+#define CZECH58 0x03050031
+#define CZECH59 0x03050032
+#define CZECH60 0x03050033
+#define CZECH61 0x03050034
+#define CZECH63 0x03050035
+#define CZECH65 0x03050036
+#define CZECH66 0x03050037
+#define CZECH69 0x03050038
+#define CZECH71 0x03050039
+#define CZECH72 0x0305003A
+#define CZECH73 0x0305003B
+#define CZECH74 0x0305003C
+#define CZECH90 0x0305003D
+#define CZECH99 0x0305003E
+#define CZECH129 0x0305003F
+#define CZECH131 0x03050040
+#define CZECH133 0x03050041
+#define CZECH145 0x03050042
+#define CZECH146 0x03050043
+ // 68 entities in TXTs, ??? in datafiles.
+ // portugese
+#define PORT0 0x03060000
+#define PORT1 0x03060001
+#define PORT2 0x03060002
+#define PORT3 0x03060003
+#define PORT4 0x03060004
+#define PORT5 0x03060005
+#define PORT6 0x03060006
+#define PORT7 0x03060007
+#define PORT9 0x03060008
+#define PORT11 0x03060009
+#define PORT12 0x0306000A
+#define PORT13 0x0306000B
+#define PORT14 0x0306000C
+#define PORT15 0x0306000D
+#define PORT16 0x0306000E
+#define PORT17 0x0306000F
+#define PORT18 0x03060010
+#define PORT19 0x03060011
+#define PORT20 0x03060012
+#define PORT21 0x03060013
+#define PORT22 0x03060014
+#define PORT23 0x03060015
+#define PORT24 0x03060016
+#define PORT25 0x03060017
+#define PORT27 0x03060018
+#define PORT28 0x03060019
+#define PORT29 0x0306001A
+#define PORT31 0x0306001B
+#define PORT32 0x0306001C
+#define PORT33 0x0306001D
+#define PORT34 0x0306001E
+#define PORT35 0x0306001F
+#define PORT36 0x03060020
+#define PORT37 0x03060021
+#define PORT38 0x03060022
+#define PORT39 0x03060023
+#define PORT40 0x03060024
+#define PORT41 0x03060025
+#define PORT42 0x03060026
+#define PORT43 0x03060027
+#define PORT45 0x03060028
+#define PORT47 0x03060029
+#define PORT48 0x0306002A
+#define PORT49 0x0306002B
+#define PORT50 0x0306002C
+#define PORT54 0x0306002D
+#define PORT55 0x0306002E
+#define PORT56 0x0306002F
+#define PORT57 0x03060030
+#define PORT58 0x03060031
+#define PORT59 0x03060032
+#define PORT60 0x03060033
+#define PORT61 0x03060034
+#define PORT63 0x03060035
+#define PORT65 0x03060036
+#define PORT66 0x03060037
+#define PORT69 0x03060038
+#define PORT71 0x03060039
+#define PORT72 0x0306003A
+#define PORT73 0x0306003B
+#define PORT74 0x0306003C
+#define PORT90 0x0306003D
+#define PORT99 0x0306003E
+#define PORT129 0x0306003F
+#define PORT131 0x03060040
+#define PORT133 0x03060041
+#define PORT145 0x03060042
+#define PORT146 0x03060043
+ // 68 entities in TXTs, ??? in datafiles.
+// general
+ // fonts
+#define GAME_FONT 0x04000000
+#define OTHER_SR_FONT 0x04000001
+#define OTHER_SR_REDFONT 0x04000002
+#define SR_DEATHFONT 0x04000003
+#define CZECH_GAME_FONT 0x04000004
+#define CZECH_SR_FONT 0x04000005
+#define CZECH_SR_REDFONT 0x04000006
+#define CZECH_SR_DEATHFONT 0x04000007
+ // 8 entities in TXTs, 1 in datafiles.
+ // pointers
+#define MSE_POINTER 0x04010000
+#define MSE_OPERATE 0x04010001
+#define MSE_PICKUP 0x04010002
+#define MSE_EXAMINE 0x04010003
+#define MSE_MOUTH 0x04010004
+#define MSE_BECKON_L 0x04010005
+#define MSE_BECKON_R 0x04010006
+#define MSE_ARROW0 0x04010007
+#define MSE_ARROW1 0x04010008
+#define MSE_ARROW2 0x04010009
+#define MSE_ARROW3 0x0401000A
+#define MSE_ARROW4 0x0401000B
+#define MSE_ARROW5 0x0401000C
+#define MSE_ARROW6 0x0401000D
+#define MSE_ARROW7 0x0401000E
+#define MSE_ARROW8 0x0401000F
+#define MSE_ARROW9 0x04010010
+ // 17 entities in TXTs, 17 in datafiles.
+ // luggage
+#define LUGG_NEWSPAPER 0x04020000
+#define LUGG_HAZEL_WAND 0x04020001
+#define LUGG_BEER_TOWEL 0x04020002
+#define LUGG_HOTEL_KEY 0x04020003
+#define LUGG_BALL 0x04020004
+#define LUGG_STATUETTE 0x04020005
+#define LUGG_RED_NOSE 0x04020006
+#define LUGG_POLISHED_CHALICE 0x04020007
+#define LUGG_DOLLAR_BILL 0x04020008
+#define LUGG_PHOTOGRAPH 0x04020009
+#define LUGG_FLASHLIGHT 0x0402000A
+#define LUGG_FUSE_WIRE 0x0402000B
+#define LUGG_GEM 0x0402000C
+#define LUGG_STATUETTE_PAINT 0x0402000D
+#define LUGG_STICK 0x0402000E
+#define LUGG_EXCAV_KEY 0x0402000F
+#define LUGG_LAB_PASS 0x04020010
+#define LUGG_LIFTING_KEYS 0x04020011
+#define LUGG_MANUSCRIPT 0x04020012
+#define LUGG_MATCHBOOK 0x04020013
+#define LUGG_SUIT_MATERIAL 0x04020014
+#define LUGG_STICK_TOWEL 0x04020015
+#define LUGG_PLASTER 0x04020016
+#define LUGG_PRESSURE_GAUGE 0x04020017
+#define LUGG_RAILWAY_TICKET 0x04020018
+#define LUGG_BUZZER 0x04020019
+#define LUGG_ROSSO_CARD 0x0402001A
+#define LUGG_TOILET_KEY 0x0402001B
+#define LUGG_SOAP 0x0402001C
+#define LUGG_STONE_KEY 0x0402001D
+#define LUGG_CHALICE 0x0402001E
+#define LUGG_TISSUE 0x0402001F
+#define LUGG_TOILET_BRUSH 0x04020020
+#define LUGG_TOILET_CHAIN 0x04020021
+#define LUGG_TOWEL 0x04020022
+#define LUGG_TRIPOD 0x04020023
+#define LUGG_LENS 0x04020024
+#define LUGG_MIRROR 0x04020025
+#define LUGG_TOWEL_CUT 0x04020026
+#define LUGG_BIBLE 0x04020027
+#define LUGG_TISSUE_CHARRED 0x04020028
+#define LUGG_FALSE_KEY 0x04020029
+#define LUGG_KEYRING 0x0402002A
+#define LUGG_SOAP_IMP 0x0402002B
+#define LUGG_SOAP_PLAS 0x0402002C
+#define LUGG_COG_1 0x0402002D
+#define LUGG_COG_2 0x0402002E
+#define LUGG_HANDLE 0x0402002F
+#define LUGG_COIN 0x04020030
+#define LUGG_BIRO 0x04020031
+#define LUGG_PIPE 0x04020032
+#define LUGG_KING 0x04020033
+#define LUGG_KNIGHT 0x04020034
+#define LUGG_BISHOP 0x04020035
+ // 54 entities in TXTs, 54 in datafiles.
+ // object_icons
+#define ICON_LEFT_ARROW 0x04030000
+#define ICON_RIGHT_ARROW 0x04030001
+#define ICON_NEWSPAPER 0x04030002
+#define ICON_HAZEL_WAND 0x04030003
+#define ICON_BEER_TOWEL 0x04030004
+#define ICON_HOTEL_KEY 0x04030005
+#define ICON_BALL 0x04030006
+#define ICON_STATUETTE 0x04030007
+#define ICON_RED_NOSE 0x04030008
+#define ICON_POLISHED_CHALICE 0x04030009
+#define ICON_DOLLAR_BILL 0x0403000A
+#define ICON_PHOTOGRAPH 0x0403000B
+#define ICON_FLASHLIGHT 0x0403000C
+#define ICON_FUSE_WIRE 0x0403000D
+#define ICON_GEM 0x0403000E
+#define ICON_STATUETTE_PAINT 0x0403000F
+#define ICON_STICK 0x04030010
+#define ICON_EXCAV_KEY 0x04030011
+#define ICON_LAB_PASS 0x04030012
+#define ICON_LIFTING_KEYS 0x04030013
+#define ICON_MANUSCRIPT 0x04030014
+#define ICON_MATCHBOOK 0x04030015
+#define ICON_SUIT_MATERIAL 0x04030016
+#define ICON_STICK_TOWEL 0x04030017
+#define ICON_PLASTER 0x04030018
+#define ICON_PRESSURE_GAUGE 0x04030019
+#define ICON_RAILWAY_TICKET 0x0403001A
+#define ICON_BUZZER 0x0403001B
+#define ICON_ROSSO_CARD 0x0403001C
+#define ICON_TOILET_KEY 0x0403001D
+#define ICON_SOAP 0x0403001E
+#define ICON_STONE_KEY 0x0403001F
+#define ICON_CHALICE 0x04030020
+#define ICON_TISSUE 0x04030021
+#define ICON_TOILET_BRUSH 0x04030022
+#define ICON_TOILET_CHAIN 0x04030023
+#define ICON_TOWEL 0x04030024
+#define ICON_TRIPOD 0x04030025
+#define ICON_LENS 0x04030026
+#define ICON_MIRROR 0x04030027
+#define ICON_TOWEL_CUT 0x04030028
+#define ICON_BIBLE 0x04030029
+#define ICON_TISSUE_CHARRED 0x0403002A
+#define ICON_FALSE_KEY 0x0403002B
+#define ICON_KEYRING 0x0403002C
+#define ICON_SOAP_IMP 0x0403002D
+#define ICON_SOAP_PLAS 0x0403002E
+#define ICON_COG_1 0x0403002F
+#define ICON_COG_2 0x04030030
+#define ICON_HANDLE 0x04030031
+#define ICON_COIN 0x04030032
+#define ICON_BIRO 0x04030033
+#define ICON_PIPE 0x04030034
+ // 53 entities in TXTs, 53 in datafiles.
+ // subject_icons
+#define ICON_ALARM 0x04040000
+#define ICON_ARTO 0x04040001
+#define ICON_ASSASSIN 0x04040002
+#define ICON_AYUB 0x04040003
+#define ICON_BANANA 0x04040004
+#define ICON_BAPHOMET 0x04040005
+#define ICON_BEER 0x04040006
+#define ICON_BOOK 0x04040007
+#define ICON_BRIEFCASE 0x04040008
+#define ICON_BULL 0x04040009
+#define ICON_BULLS_HEAD 0x0404000A
+#define ICON_BUST 0x0404000B
+#define ICON_CANDLE 0x0404000C
+#define ICON_CAR 0x0404000D
+#define ICON_CASTLE 0x0404000E
+#define ICON_CAT 0x0404000F
+#define ICON_CHANTELLE 0x04040010
+#define ICON_CHESSBOARD 0x04040011
+#define ICON_CHESS_SET 0x04040012
+#define ICON_CHURCH 0x04040013
+#define ICON_CLOWN 0x04040014
+#define ICON_CLUB 0x04040015
+#define ICON_COINS 0x04040016
+#define ICON_COUNTESS 0x04040017
+#define ICON_CREST 0x04040018
+#define ICON_DIG 0x04040019
+#define ICON_DOG 0x0404001A
+#define ICON_DUANE 0x0404001B
+#define ICON_EKLUND 0x0404001C
+#define ICON_ERIC 0x0404001D
+#define ICON_FISH 0x0404001E
+#define ICON_FLOWERS 0x0404001F
+#define ICON_FORTUNE 0x04040020
+#define ICON_GEORGE 0x04040021
+#define ICON_GHOST 0x04040022
+#define ICON_GLASS_EYE 0x04040023
+#define ICON_GOAT 0x04040024
+#define ICON_GOODBYE 0x04040025
+#define ICON_GUARD 0x04040026
+#define ICON_HASH 0x04040027
+#define ICON_HENDERSONS 0x04040028
+#define ICON_JACKET 0x04040029
+#define ICON_JUGGLER 0x0404002A
+#define ICON_KLAUSNER 0x0404002B
+#define ICON_KNIGHT 0x0404002C
+#define ICON_LADDER 0x0404002D
+#define ICON_LEARY 0x0404002E
+#define ICON_LEPRECHAUN 0x0404002F
+#define ICON_LIE 0x04040030
+#define ICON_LOBINEAU 0x04040031
+#define ICON_MARQUET 0x04040032
+#define ICON_MARY 0x04040033
+#define ICON_MOB 0x04040034
+#define ICON_MONTFAUCON 0x04040035
+#define ICON_MOUE 0x04040036
+#define ICON_MR_SHINY 0x04040037
+#define ICON_NEJO 0x04040038
+#define ICON_NEJO_STALL 0x04040039
+#define ICON_NICO 0x0404003A
+#define ICON_NO 0x0404003B
+#define ICON_NURSE 0x0404003C
+#define ICON_PACKAGE 0x0404003D
+#define ICON_PAINTER 0x0404003E
+#define ICON_PEAGRAM 0x0404003F
+#define ICON_PEARL 0x04040040
+#define ICON_PHILIPPE 0x04040041
+#define ICON_PHONE 0x04040042
+#define ICON_PHRASE 0x04040043
+#define ICON_PIERMONT 0x04040044
+#define ICON_PLANTARD 0x04040045
+#define ICON_POTS 0x04040046
+#define ICON_PRIEST 0x04040047
+#define ICON_QUEEN 0x04040048
+#define ICON_RENEE 0x04040049
+#define ICON_ROSSO 0x0404004A
+#define ICON_ROZZER 0x0404004B
+#define ICON_SAFE 0x0404004C
+#define ICON_SCROLL 0x0404004D
+#define ICON_SEAN 0x0404004E
+#define ICON_SIGN 0x0404004F
+#define ICON_TAXI 0x04040050
+#define ICON_TEMPLARS 0x04040051
+#define ICON_THERMOSTAT 0x04040052
+#define ICON_TOILET 0x04040053
+#define ICON_TOMB 0x04040054
+#define ICON_TOOLBOX 0x04040055
+#define ICON_TRUTH 0x04040056
+#define ICON_ULTAR 0x04040057
+#define ICON_WEASEL 0x04040058
+#define ICON_WEAVER 0x04040059
+#define ICON_WELL 0x0404005A
+#define ICON_WELL2 0x0404005B
+#define ICON_WINDOW 0x0404005C
+#define ICON_YES 0x0404005D
+ // 94 entities in TXTs, 94 in datafiles.
+ // save_menu
+#define SR_FONT 0x04050000
+#define SR_BUTTON 0x04050001
+#define SR_REDFONT 0x04050002
+#define SR_PALETTE 0x04050003
+#define SR_PANEL_ENGLISH 0x04050004
+#define SR_PANEL_FRENCH 0x04050005
+#define SR_PANEL_GERMAN 0x04050006
+#define SR_PANEL_ITALIAN 0x04050007
+#define SR_PANEL_SPANISH 0x04050008
+#define SR_PANEL_AMERICAN 0x04050009
+#define SR_TEXT_BUTTON 0x0405000A
+#define SR_SPEED 0x0405000B
+#define SR_SCROLL1 0x0405000C
+#define SR_SCROLL2 0x0405000D
+#define SR_CONFIRM 0x0405000E
+#define SR_VOLUME 0x0405000F
+#define SR_VLIGHT 0x04050010
+#define SR_VKNOB 0x04050011
+#define SR_WINDOW 0x04050012
+#define SR_SLAB1 0x04050013
+#define SR_SLAB2 0x04050014
+#define SR_SLAB3 0x04050015
+#define SR_SLAB4 0x04050016
+#define SR_BUTUF 0x04050017
+#define SR_BUTUS 0x04050018
+#define SR_BUTDS 0x04050019
+#define SR_BUTDF 0x0405001A
+#define SR_DEATHPANEL 0x0405001B // 0x04050019
+ // 26 entities in TXTs, 29 in datafiles.
+ // george
+#define GEORGE_MEGA 0x04060000
+#define GEORGE_WLK 0x04060001
+#define GEOSHO1 0x04060002
+#define GEOSHO1CDT 0x04060003
+#define GEOSHO1CDR 0x04060004
+#define GEOSHO2 0x04060005
+#define GEOSHO2CDT 0x04060006
+#define GEOSHO2CDR 0x04060007
+#define GEOSHO3 0x04060008
+#define GEOSHO3CDT 0x04060009
+#define GEOSHO3CDR 0x0406000A
+#define GEOSHO5 0x0406000B
+#define GEOSHO5CDT 0x0406000C
+#define GEOSHO5CDR 0x0406000D
+#define GEOSHO6 0x0406000E
+#define GEOSHO6CDT 0x0406000F
+#define GEOSHO6CDR 0x04060010
+#define GEOSHO7 0x04060011
+#define GEOSHO7CDT 0x04060012
+#define GEOSHO7CDR 0x04060013
+#define GEOTLK0 0x04060014
+#define GEOTLK0CDT 0x04060015
+#define GEOTLK1 0x04060016
+#define GEOTLK1CDT 0x04060017
+#define GEOTLK2 0x04060018
+#define GEOTLK2CDT 0x04060019
+#define GEOTLK3 0x0406001A
+#define GEOTLK3CDT 0x0406001B
+#define GEOTLK4 0x0406001C
+#define GEOTLK4CDT 0x0406001D
+#define GEOTLK5 0x0406001E
+#define GEOTLK5CDT 0x0406001F
+#define GEOTLK6 0x04060020
+#define GEOTLK6CDT 0x04060021
+#define GEOTLK7 0x04060022
+#define GEOTLK7CDT 0x04060023
+#define XGEOCRW0 0x04060024
+#define XGEOCRW0CDT 0x04060025
+#define XGEOCRW0CDR 0x04060026
+#define XGEOCRW1 0x04060027
+#define XGEOCRW1CDT 0x04060028
+#define XGEOCRW1CDR 0x04060029
+#define XGEOCRW2 0x0406002A
+#define XGEOCRW2CDT 0x0406002B
+#define XGEOCRW2CDR 0x0406002C
+#define XGEOCRW3 0x0406002D
+#define XGEOCRW3CDT 0x0406002E
+#define XGEOCRW3CDR 0x0406002F
+#define XGEOCRW4 0x04060030
+#define XGEOCRW4CDT 0x04060031
+#define XGEOCRW4CDR 0x04060032
+#define XGEOCRW5 0x04060033
+#define XGEOCRW5CDT 0x04060034
+#define XGEOCRW5CDR 0x04060035
+#define XGEOCRW6 0x04060036
+#define XGEOCRW6CDT 0x04060037
+#define XGEOCRW6CDR 0x04060038
+#define XGEOCRW7 0x04060039
+#define XGEOCRW7CDT 0x0406003A
+#define XGEOCRW7CDR 0x0406003B
+#define XGEOGIV0 0x0406003C
+#define XGEOGIV0CDT 0x0406003D
+#define XGEOGIV0CDR 0x0406003E
+#define XGEOGIV1 0x0406003F
+#define XGEOGIV1CDT 0x04060040
+#define XGEOGIV1CDR 0x04060041
+#define XGEOGIV2 0x04060042
+#define XGEOGIV2CDT 0x04060043
+#define XGEOGIV2CDR 0x04060044
+#define XGEOGIV3 0x04060045
+#define XGEOGIV3CDT 0x04060046
+#define XGEOGIV3CDR 0x04060047
+#define XGEOGIV4 0x04060048
+#define XGEOGIV4CDT 0x04060049
+#define XGEOGIV4CDR 0x0406004A
+#define XGEOGIV5 0x0406004B
+#define XGEOGIV5CDT 0x0406004C
+#define XGEOGIV5CDR 0x0406004D
+#define XGEOGIV6 0x0406004E
+#define XGEOGIV6CDT 0x0406004F
+#define XGEOGIV6CDR 0x04060050
+#define XGEOGIV7 0x04060051
+#define XGEOGIV7CDT 0x04060052
+#define XGEOGIV7CDR 0x04060053
+#define XGEOLUK3 0x04060054
+#define XGEOLUK3CDT 0x04060055
+#define XGEOLUK5 0x04060056
+#define XGEOLUK5CDT 0x04060057
+#define XGEOPIC0 0x04060058
+#define XGEOPIC0CDT 0x04060059
+#define XGEOPIC1 0x0406005A
+#define XGEOPIC1CDT 0x0406005B
+#define XGEOPIC2 0x0406005C
+#define XGEOPIC2CDT 0x0406005D
+#define XGEOPIC3 0x0406005E
+#define XGEOPIC3CDT 0x0406005F
+#define XGEOPIC4 0x04060060
+#define XGEOPIC4CDT 0x04060061
+#define XGEOPIC5 0x04060062
+#define XGEOPIC5CDT 0x04060063
+#define XGEOPIC6 0x04060064
+#define XGEOPIC6CDT 0x04060065
+#define XGEOPIC7 0x04060066
+#define XGEOPIC7CDT 0x04060067
+#define XGEOSHG0 0x04060068
+#define XGEOSHG0CDT 0x04060069
+#define XGEOSHG1 0x0406006A
+#define XGEOSHG1CDT 0x0406006B
+#define XGEOSHG2 0x0406006C
+#define XGEOSHG2CDT 0x0406006D
+#define XGEOSHG3 0x0406006E
+#define XGEOSHG3CDT 0x0406006F
+#define XGEOSHG4 0x04060070
+#define XGEOSHG4CDT 0x04060071
+#define XGEOSHG5 0x04060072
+#define XGEOSHG5CDT 0x04060073
+#define XGEOSHG6 0x04060074
+#define XGEOSHG6CDT 0x04060075
+#define XGEOSHG7 0x04060076
+#define XGEOSHG7CDT 0x04060077
+#define XGHAIR2 0x04060078
+#define XGHAIR2CDT 0x04060079
+#define XGHAIR6 0x0406007A
+#define XGHAIR6CDT 0x0406007B
+#define GEOBLNK2 0x0406007C
+#define GEOBLNK2CDT 0x0406007D
+#define GEOBLNK3 0x0406007E
+#define GEOBLNK3CDT 0x0406007F
+#define GEOBLNK4 0x04060080
+#define GEOBLNK4CDT 0x04060081
+#define GEOBLNK5 0x04060082
+#define GEOBLNK5CDT 0x04060083
+#define GEOBLNK6 0x04060084
+#define GEOBLNK6CDT 0x04060085
+#define XGRELAX0 0x04060086
+#define XGRELAX0CDT 0x04060087
+#define XGRELAX0CDR 0x04060088
+#define XGRELAX1 0x04060089
+#define XGRELAX1CDT 0x0406008A
+#define XGRELAX1CDR 0x0406008B
+#define XGRELAX2 0x0406008C
+#define XGRELAX2CDT 0x0406008D
+#define XGRELAX2CDR 0x0406008E
+#define XGRELAX3 0x0406008F
+#define XGRELAX3CDT 0x04060090
+#define XGRELAX3CDR 0x04060091
+#define XGRELAX4 0x04060092
+#define XGRELAX4CDT 0x04060093
+#define XGRELAX4CDR 0x04060094
+#define XGRELAX5 0x04060095
+#define XGRELAX5CDT 0x04060096
+#define XGRELAX5CDR 0x04060097
+#define XGRELAX6 0x04060098
+#define XGRELAX6CDT 0x04060099
+#define XGRELAX6CDR 0x0406009A
+#define XGRELAX7 0x0406009B
+#define XGRELAX7CDT 0x0406009C
+#define XGRELAX7CDR 0x0406009D
+ // 158 entities in TXTs, 158 in datafiles.
+ // nico
+#define MEGANICO 0x04070000
+#define NICO_WLK 0x04070001
+#define NICTLK0 0x04070002
+#define NICTLK0CDT 0x04070003
+#define NICTLK1 0x04070004
+#define NICTLK1CDT 0x04070005
+#define NICTLK2 0x04070006
+#define NICTLK2CDT 0x04070007
+#define NICTLK3 0x04070008
+#define NICTLK3CDT 0x04070009
+#define NICTLK4 0x0407000A
+#define NICTLK4CDT 0x0407000B
+#define NICTLK5 0x0407000C
+#define NICTLK5CDT 0x0407000D
+#define NICTLK6 0x0407000E
+#define NICTLK6CDT 0x0407000F
+#define NICTLK7 0x04070010
+#define NICTLK7CDT 0x04070011
+ // 18 entities in TXTs, 18 in datafiles.
+// maps
+ // room80
+#define room80_PAL 0x05000000
+#define room80_l0 0x05000001
+#define TEST_FLR 0x05000002
+#define SPRITE_PAL 0x05000003
+#define AEROPORTCDT 0x05000004
+#define AEROPORT 0x05000005
+#define APRTSTRTCDT 0x05000006
+#define APRTSTRT 0x05000007
+#define BAPHOMETCDT 0x05000008
+#define BAPHOMET 0x05000009
+#define CAFECDT 0x0500000A
+#define CAFE 0x0500000B
+#define COSTUMESCDT 0x0500000C
+#define COSTUMES 0x0500000D
+#define HOSPITALCDT 0x0500000E
+#define HOSPITAL 0x0500000F
+#define HOTELCDT 0x05000010
+#define HOTEL 0x05000011
+#define MONTFACNCDT 0x05000012
+#define MONTFACN 0x05000013
+#define MUSEUMCDT 0x05000014
+#define MUSEUM 0x05000015
+#define POLICECDT 0x05000016
+#define POLICE 0x05000017
+ // 24 entities in TXTs, 24 in datafiles.
+ // room86
+#define room86_PAL 0x05010000
+#define room86_l0 0x05010001
+#define BANNOCKCDT 0x05010002
+#define BANNOCK 0x05010003
+#define MARIBCDT 0x05010004
+#define MARIB 0x05010005
+#define LOCHMARNCDT 0x05010006
+#define LOCHMARN 0x05010007
+#define PARISCDT 0x05010008
+#define PARIS 0x05010009
+#define VILLAVASCDT 0x0501000A
+#define VILLAVAS 0x0501000B
+ // 12 entities in TXTs, 12 in datafiles.
+ // room90
+#define R90L0 0x05020000
+#define R90PAL 0x05020001
+#define PHONE_PAL 0x05020002
+#define GBLINK 0x05020003
+#define GBLINKCDT 0x05020004
+#define GRISE 0x05020005
+#define GRISECDT 0x05020006
+#define GRISECDR 0x05020007
+#define GTALK 0x05020008
+#define GTALKCDT 0x05020009
+#define NBLINK 0x0502000A
+#define NBLINKCDT 0x0502000B
+#define NRISE 0x0502000C
+#define NRISECDT 0x0502000D
+#define NRISECDR 0x0502000E
+#define NTALK 0x0502000F
+#define NTALKCDT 0x05020010
+#define TODTALK 0x05020011
+#define TODTALKCDT 0x05020012
+ // 19 entities in TXTs, 19 in datafiles.
+ // room91
+#define R91L0 0x05030000
+#define R91PAL 0x05030001
+#define CLOWN 0x05030002
+#define CLOWNCDT 0x05030003
+#define DESVAS 0x05030004
+#define DESVASCDT 0x05030005
+#define EIFTOWER 0x05030006
+#define EIFTOWERCDT 0x05030007
+#define HORSETIP 0x05030008
+#define HORSETIPCDT 0x05030009
+#define LOBIN91 0x0503000A
+#define LOBIN91CDT 0x0503000B
+#define MONTFCON 0x0503000C
+#define MONTFCONCDT 0x0503000D
+#define NICOADD 0x0503000E
+#define NICOADDCDT 0x0503000F
+#define NICOPHN 0x05030010
+#define NICOPHNCDT 0x05030011
+#define TAILOR 0x05030012
+#define TAILORCDT 0x05030013
+ // 20 entities in TXTs, 20 in datafiles.
+ // room99
+#define room99_PAL 0x05040000
+#define room99_l0 0x05040001
+ // 2 entities in TXTs, 2 in datafiles.
+// paris_1
+ // sound_fx
+#define FX_CAMERA1 0x06000000
+#define FX_CAMERA2 0x06000001
+#define FX_CAMERA3 0x06000002
+#define FX_CANDO 0x06000003
+#define FX_CANUP 0x06000004
+#define FX_CAW1 0x06000005
+#define FX_DUST 0x06000006
+#define FX_HORN1 0x06000007
+#define FX_HORN2 0x06000008
+#define FX_HORN3 0x06000009
+#define FX_LVSFLY 0x0600000A
+#define FX_PAP1 0x0600000B
+#define FX_PAP2 0x0600000C
+#define FX_PICK1 0x0600000D
+#define FX_PICK2 0x0600000E
+#define FX_PICK3 0x0600000F
+#define FX_PICK4 0x06000010
+#define FX_PICK5 0x06000011
+#define FX_TRAFFIC2 0x06000012
+#define FX_TWEET1 0x06000013
+#define FX_TWEET2 0x06000014
+#define FX_TWEET3 0x06000015
+#define FX_TWEET4 0x06000016
+#define FX_TWEET5 0x06000017
+#define FX_BIN1 0x06000018
+#define FX_BIN2 0x06000019
+#define FX_BIN3 0x0600001A
+#define FX_CAT 0x0600001B
+#define FX_COVERON2 0x0600001C
+#define FX_CRATE 0x0600001D
+#define FX_DRAIN 0x0600001E
+#define FX_HOLE 0x0600001F
+#define FX_BODY 0x06000020
+#define FX_BOTDN 0x06000021
+#define FX_BOTUP 0x06000022
+#define FX_GULP 0x06000023
+#define FX_LIGHT 0x06000024
+#define FX_PIKUP 0x06000025
+#define FX_PAP3 0x06000026
+#define FX_PAP4 0x06000027
+#define FX_PAP5 0x06000028
+#define FX_PISTOL 0x06000029
+#define FX_TBOX 0x0600002A
+#define FX_KNOKKNOK 0x0600002B
+#define FX_ALBCLO 0x0600002C
+#define FX_ALBOP 0x0600002D
+#define FX_LADD1 0x0600002E
+#define FX_LADD2 0x0600002F
+#define FX_LADD3 0x06000030
+#define FX_RAT1 0x06000031
+#define FX_RAT2 0x06000032
+#define FX_SEWSTEP1 0x06000033
+#define FX_SEWSTEP2 0x06000034
+#define FX_SWATER3 0x06000035
+#define FX_DRIP1 0x06000036
+#define FX_DRIP2 0x06000037
+#define FX_DRIP3 0x06000038
+#define FX_SWATER1 0x06000039
+#define FX_SEWLADD7 0x0600003A
+#define FX_SEWLADU7 0x0600003B
+ // 60 entities in TXTs, 60 in datafiles.
+ // room1
+#define PARIS1_PAL 0x06010000
+#define room1_PAL 0x06010001
+#define room1_l0 0x06010002
+#define room1_l1 0x06010003
+#define room1_l2 0x06010004
+#define room1_gd1 0x06010005
+#define room1_gd2 0x06010006
+#define room1_flr 0x06010007
+#define room1_plx 0x06010008
+#define BIRDS 0x06010009
+#define BIRDSCDT 0x0601000A
+#define BIRDSA 0x0601000B
+#define BIRDSACDT 0x0601000C
+#define BIRDSB 0x0601000D
+#define BIRDSBCDT 0x0601000E
+#define BIRDSC 0x0601000F
+#define BIRDSCCDT 0x06010010
+#define BIRDSD 0x06010011
+#define BIRDSDCDT 0x06010012
+#define BIRDSE 0x06010013
+#define BIRDSECDT 0x06010014
+#define BIRDSF 0x06010015
+#define BIRDSFCDT 0x06010016
+#define BIRDSG 0x06010017
+#define BIRDSGCDT 0x06010018
+#define BIRDSH 0x06010019
+#define BIRDSHCDT 0x0601001A
+#define BIRDSI 0x0601001B
+#define BIRDSICDT 0x0601001C
+#define CANOPY 0x0601001D
+#define CANOPYCDT 0x0601001E
+#define GEOCAN 0x0601001F
+#define GEOCANCDT 0x06010020
+#define GEONIC 0x06010021
+#define GEONICCDT 0x06010022
+#define GEOPAP 0x06010023
+#define GEOPAPCDT 0x06010024
+#define GEOPAP2 0x06010025
+#define GEOPAP2CDT 0x06010026
+#define LVSFLY 0x06010027
+#define LVSFLYCDT 0x06010028
+#define MOUGRD 0x06010029
+#define MOUGRDCDT 0x0601002A
+#define MOUTLK01 0x0601002B
+#define MOUTLK01CDT 0x0601002C
+#define MOUWLK01 0x0601002D
+#define MOUWLK01CDT 0x0601002E
+#define NICPHT1CDT 0x0601002F
+#define NICPHT1 0x06010030
+#define NICPHT2CDT 0x06010031
+#define NICPHT2 0x06010032
+#define NICPHT3CDT 0x06010033
+#define NICPHT3 0x06010034
+#define NICPHT5CDT 0x06010035
+#define NICPHT5 0x06010036
+#define NICPHT6CDT 0x06010037
+#define NICPHT6 0x06010038
+#define NICPHT7CDT 0x06010039
+#define NICPHT7 0x0601003A
+#define NICWLK01 0x0601003B
+#define NICWLK01CDT 0x0601003C
+#define NWPAPER 0x0601003D
+#define NWPAPERCDT 0x0601003E
+#define WRKDIG01 0x0601003F
+#define WRKDIG01CDT 0x06010040
+#define XGEOPHN3 0x06010041
+#define XGEOPHN3CDT 0x06010042
+#define XGEOPHN5 0x06010043
+#define XGEOPHN5CDT 0x06010044
+#define XNICPHN3CDT 0x06010045
+#define XNICPHN3 0x06010046
+#define XNICPHN5CDT 0x06010047
+#define XNICPHN5 0x06010048
+ // 73 entities in TXTs, 73 in datafiles.
+ // room2
+#define room2_PAL 0x06020000
+#define room2_l0 0x06020001
+#define room2_l1 0x06020002
+#define room2_l2 0x06020003
+#define room2_gd1 0x06020004
+#define room2_gd2 0x06020005
+#define room2_flr 0x06020006
+#define BIN1 0x06020007
+#define BIN1CDT 0x06020008
+#define BIN2 0x06020009
+#define BIN2CDT 0x0602000A
+#define BIN3 0x0602000B
+#define BIN3CDT 0x0602000C
+#define CATJMP 0x0602000D
+#define CATJMPCDT 0x0602000E
+#define CRATE 0x0602000F
+#define CRATECDT 0x06020010
+#define DRNBRK 0x06020011
+#define DRNBRKCDT 0x06020012
+#define GEOBIN 0x06020013
+#define GEOBINCDT 0x06020014
+#define GEOBOX 0x06020015
+#define GEOBOXCDT 0x06020016
+#define GEOBRS 0x06020017
+#define GEOBRSCDT 0x06020018
+#define GEOCAT 0x06020019
+#define GEOCATCDT 0x0602001A
+#define GEOCATE 0x0602001B
+#define GEOCATECDT 0x0602001C
+#define GEOCLM02 0x0602001D
+#define GEOCLM02CDT 0x0602001E
+#define GEOCRT 0x0602001F
+#define GEOCRTCDT 0x06020020
+#define GEODWN 0x06020021
+#define GEODWNCDT 0x06020022
+#define GEOLID 0x06020023
+#define GEOLIDCDT 0x06020024
+#define GEOLIDCDR 0x06020025
+#define GEOMAN8 0x06020026
+#define GEOMAN8CDT 0x06020027
+#define GEOMAN9 0x06020028
+#define GEOMAN9CDT 0x06020029
+#define GEOOUT 0x0602002A
+#define GEOOUTCDT 0x0602002B
+#define GEOPLL02 0x0602002C
+#define GEOPLL02CDT 0x0602002D
+#define GEOTRY02 0x0602002E
+#define GEOTRY02CDT 0x0602002F
+#define MANCOV 0x06020030
+#define MANCOVCDT 0x06020031
+#define MANCVOFF 0x06020032
+#define MANCVOFFCDT 0x06020033
+ // 52 entities in TXTs, 52 in datafiles.
+ // room3
+#define room3_PAL 0x06030000
+#define room3_l0 0x06030001
+#define room3_l1 0x06030002
+#define room3_l2 0x06030003
+#define room3_gd1 0x06030004
+#define room3_gd2 0x06030005
+#define room3_flr 0x06030006
+#define BOTTLE 0x06030007
+#define BOTTLECDT 0x06030008
+#define BOTTLE1 0x06030009
+#define BOTTLE1CDT 0x0603000A
+#define CHNDRN 0x0603000B
+#define CHNDRNCDT 0x0603000C
+#define CHNFNT 0x0603000D
+#define CHNFNTCDT 0x0603000E
+#define CHNSITA 0x0603000F
+#define CHNSITACDT 0x06030010
+#define CHNSIT 0x06030011
+#define CHNSITCDT 0x06030012
+#define CHNTLK 0x06030013
+#define CHNTLKCDT 0x06030014
+#define CHNUPP 0x06030015
+#define CHNUPPCDT 0x06030016
+#define CHNUPPCDR 0x06030017
+#define CORPSE 0x06030018
+#define CORPSECDT 0x06030019
+#define FLICKER 0x0603001A
+#define FLICKERCDT 0x0603001B
+#define GEOBOT3 0x0603001C
+#define GEOBOT3CDT 0x0603001D
+#define GEOCARD3 0x0603001E
+#define GEOCARD3CDT 0x0603001F
+#define GEOCHN 0x06030020
+#define GEOCHNCDT 0x06030021
+#define GEOCPS 0x06030022
+#define GEOCPSCDT 0x06030023
+#define GEOCPSCDR 0x06030024
+#define MOUCHN 0x06030025
+#define MOUCHNCDT 0x06030026
+#define MOUCHN8 0x06030027
+#define MOUCHN8CDT 0x06030028
+#define MOUCPS 0x06030029
+#define MOUCPSCDT 0x0603002A
+#define MOUCPSTK 0x0603002B
+#define MOUCPSTKCDT 0x0603002C
+#define MOUDOR03 0x0603002D
+#define MOUDOR03CDT 0x0603002E
+#define MOUROS 0x0603002F
+#define MOUROSCDT 0x06030030
+#define MOUROS1 0x06030031
+#define MOUROS1CDT 0x06030032
+#define MOUROS1CDR 0x06030033
+#define MOUROS2 0x06030034
+#define MOUROS2CDT 0x06030035
+#define MOUSTD 0x06030036
+#define MOUSTDCDT 0x06030037
+#define MOUTALK 0x06030038
+#define MOUTALKCDT 0x06030039
+#define MOUTLK1 0x0603003A
+#define MOUTLK1CDT 0x0603003B
+#define MOUTRN03 0x0603003C
+#define MOUTRN03CDT 0x0603003D
+#define MOUTRN03CDR 0x0603003E
+#define MOUTRN2 0x0603003F
+#define MOUTRN2CDT 0x06030040
+#define MOUTRN2CDR 0x06030041
+#define MOUWLK 0x06030042
+#define MOUWLKCDT 0x06030043
+#define MOUWLK03 0x06030044
+#define MOUWLK03CDT 0x06030045
+#define ROSCARD 0x06030046
+#define ROSCARDCDT 0x06030047
+#define ROSCHN 0x06030048
+#define ROSCHNCDT 0x06030049
+#define ROSCHNCDR 0x0603004A
+#define ROSGEO 0x0603004B
+#define ROSGEOCDT 0x0603004C
+#define ROSGEOA 0x0603004D
+#define ROSGEOACDT 0x0603004E
+#define ROSGEOB 0x0603004F
+#define ROSGEOBCDT 0x06030050
+#define ROSGEOC 0x06030051
+#define ROSGEOCCDT 0x06030052
+#define ROSLIK 0x06030053
+#define ROSLIKCDT 0x06030054
+#define ROSLUK 0x06030055
+#define ROSLUKCDT 0x06030056
+#define ROSMOU 0x06030057
+#define ROSMOUCDT 0x06030058
+#define ROSNBK 0x06030059
+#define ROSNBKCDT 0x0603005A
+#define ROSNBK8 0x0603005B
+#define ROSNBK8CDT 0x0603005C
+#define ROSPKT 0x0603005D
+#define ROSPKTCDT 0x0603005E
+#define ROSTMP 0x0603005F
+#define ROSTMPCDT 0x06030060
+#define ROSTMP8 0x06030061
+#define ROSTMP8CDT 0x06030062
+#define ROSTURNA 0x06030063
+#define ROSTURNACDT 0x06030064
+#define ROSTURNB 0x06030065
+#define ROSTURNBCDT 0x06030066
+#define ROSTRN2 0x06030067
+#define ROSTRN2CDT 0x06030068
+#define ROSWRT 0x06030069
+#define ROSWRTCDT 0x0603006A
+ // 107 entities in TXTs, 107 in datafiles.
+ // room4
+#define room4_PAL 0x06040000
+#define room4_l0 0x06040001
+#define room4_l1 0x06040002
+#define room4_l2 0x06040003
+#define room4_gd1 0x06040004
+#define room4_gd2 0x06040005
+#define room4_flr 0x06040006
+#define ALBDOR 0x06040007
+#define ALBDORCDR 0x06040008
+#define ALBDORCDT 0x06040009
+#define ALBDOR8 0x0604000A
+#define ALBDOR8CDT 0x0604000B
+#define GEONOK 0x0604000C
+#define GEONOKCDT 0x0604000D
+#define GEONOKCDR 0x0604000E
+#define GEOTBX 0x0604000F
+#define GEOTBXCDT 0x06040010
+#define GEOWRK 0x06040011
+#define GEOWRKCDT 0x06040012
+#define GEOWRK8 0x06040013
+#define GEOWRK8CDT 0x06040014
+#define MOUENT 0x06040015
+#define MOUENTCDT 0x06040016
+#define MOUSHG 0x06040017
+#define MOUSHGCDT 0x06040018
+#define MOUSTR 0x06040019
+#define MOUSTRCDT 0x0604001A
+#define MOUTLK04 0x0604001B
+#define MOUTLK04CDT 0x0604001C
+#define MOUWLK04 0x0604001D
+#define MOUWLK04CDT 0x0604001E
+#define ROSENT 0x0604001F
+#define ROSENTCDT 0x06040020
+#define ROSTLK04 0x06040021
+#define ROSTLK04CDT 0x06040022
+#define ROSWLK 0x06040023
+#define ROSWLKCDT 0x06040024
+#define PHONE4 0x06040025
+#define PHONE4CDT 0x06040026
+#define WRKAXE 0x06040027
+#define WRKAXECDT 0x06040028
+#define WRKBLINK 0x06040029
+#define WRKBLINKCDT 0x0604002A
+#define WRKCLM 0x0604002B
+#define WRKCLMCDT 0x0604002C
+#define WRKDIG 0x0604002D
+#define WRKDIGCDT 0x0604002E
+#define WRKPPR 0x0604002F
+#define WRKPPRCDT 0x06040030
+#define WRKTLK 0x06040031
+#define WRKTLKCDT 0x06040032
+#define WRKTLK8 0x06040033
+#define WRKTLK8CDT 0x06040034
+#define WRKTLK9 0x06040035
+#define WRKTLK9CDT 0x06040036
+#define WRKTRN 0x06040037
+#define WRKTRNCDR 0x06040038
+#define WRKTRNCDT 0x06040039
+#define WRKXIT 0x0604003A
+#define WRKXITCDT 0x0604003B
+ // 60 entities in TXTs, 60 in datafiles.
+ // room5
+#define room5_PAL 0x06050000
+#define room5_l0 0x06050001
+#define room5_l1 0x06050002
+#define room5_l2 0x06050003
+#define room5_gd1 0x06050004
+#define room5_gd2 0x06050005
+#define room5_flr 0x06050006
+#define ALBAPT 0x06050007
+#define ALBAPTCDT 0x06050008
+#define ALBAPTCDR 0x06050009
+#define ALBCLOSECDT 0x0605000A
+#define ALBCLOSE 0x0605000B
+#define ALBCRD8 0x0605000C
+#define ALBCRD8CDT 0x0605000D
+#define ALBDORTKCDT 0x0605000E
+#define ALBDORTK 0x0605000F
+#define ALBDRTK2CDT 0x06050010
+#define ALBDRTK2 0x06050011
+#define ALBHND 0x06050012
+#define ALBHNDCDT 0x06050013
+#define ALBOBJ 0x06050014
+#define ALBOBJCDT 0x06050015
+#define ALBOPENCDT 0x06050016
+#define ALBOPEN 0x06050017
+#define ALBPKT 0x06050018
+#define ALBPKTCDT 0x06050019
+#define ALBTLK 0x0605001A
+#define ALBTLKCDT 0x0605001B
+#define ALBWLK9CDT 0x0605001C
+#define ALBWLK9 0x0605001D
+#define GEOCRD05 0x0605001E
+#define GEOCRD05CDT 0x0605001F
+#define GEODOR05CDT 0x06050020
+#define GEODOR05 0x06050021
+#define GEOEMG 0x06050022
+#define GEOEMGCDT 0x06050023
+#define GEOEMGCDR 0x06050024
+#define GEOEMGA 0x06050025
+#define GEOEMGACDT 0x06050026
+#define GEOEMGACDR 0x06050027
+#define GEOEMGB 0x06050028
+#define GEOEMGBCDT 0x06050029
+#define GEOEMGBCDR 0x0605002A
+#define GEOEXITCDT 0x0605002B
+#define GEOEXIT 0x0605002C
+#define GEOHND05 0x0605002D
+#define GEOHND05CDT 0x0605002E
+#define GEONOK05CDT 0x0605002F
+#define GEONOK05 0x06050030
+#define GEOTLK05 0x06050031
+#define GEOTLK05CDT 0x06050032
+ // 51 entities in TXTs, 51 in datafiles.
+ // room6
+#define SEWER_PAL 0x06060000
+#define room6_PAL 0x06060001
+#define room6_l0 0x06060002
+#define room6_l1 0x06060003
+#define room6_gd1 0x06060004
+#define room6_flr 0x06060005
+#define GEOASC06 0x06060006
+#define GEOASC06CDT 0x06060007
+#define GEODES06 0x06060008
+#define GEODES06CDT 0x06060009
+#define GEONOSE 0x0606000A
+#define GEONOSECDT 0x0606000B
+#define REDNOSE 0x0606000C
+#define REDNOSECDT 0x0606000D
+#define WATER06 0x0606000E
+#define WATER06CDT 0x0606000F
+ // 16 entities in TXTs, 16 in datafiles.
+ // room7
+#define room7_PAL 0x06070000
+#define room7_l0 0x06070001
+#define room7_l1 0x06070002
+#define room7_l2 0x06070003
+#define room7_gd1 0x06070004
+#define room7_gd2 0x06070005
+#define room7_flr 0x06070006
+#define GEOASC07 0x06070007
+#define GEOASC07CDT 0x06070008
+#define GEOASC07CDR 0x06070009
+#define GEOPLL07 0x0607000A
+#define GEOPLL07CDT 0x0607000B
+#define GEOPLL07CDR 0x0607000C
+#define GEOSPK 0x0607000D
+#define GEOSPKCDT 0x0607000E
+#define GEOTIS 0x0607000F
+#define GEOTISCDT 0x06070010
+#define MATERIAL 0x06070011
+#define MATERIALCDT 0x06070012
+#define TISSUE7 0x06070013
+#define TISSUE7CDT 0x06070014
+#define WATER07 0x06070015
+#define WATER07CDT 0x06070016
+ // 23 entities in TXTs, 23 in datafiles.
+ // room8
+#define room8_PAL 0x06080000
+#define room8_l0 0x06080001
+#define room8_l1 0x06080002
+#define room8_l2 0x06080003
+#define room8_gd1 0x06080004
+#define room8_gd2 0x06080005
+#define room8_flr 0x06080006
+#define room8_plx 0x06080007
+ // 8 entities in TXTs, 8 in datafiles.
+// paris_2
+ // sound_fx
+#define FX_BIRD 0x07000000
+#define FX_BIRD2 0x07000001
+#define FX_CARLTON 0x07000002
+#define FX_CARS 0x07000003
+#define FX_DOORTRY 0x07000004
+#define FX_FIESTA 0x07000005
+#define FX_FLATDOOR 0x07000006
+#define FX_HVYVEHL 0x07000007
+#define FX_HVYVEHR 0x07000008
+#define FX_LITEVEHL 0x07000009
+#define FX_LITEVEHR 0x0700000A
+#define FX_FONEUP 0x0700000B
+#define FX_FONEDN 0x0700000C
+#define FX_GEOCCH 0x0700000D
+#define FX_GEOCHAIR 0x0700000E
+#define FX_GEOCHR9 0x0700000F
+#define FX_NICOPEN 0x07000010
+#define FX_NICLOSE 0x07000011
+#define FX_PHONICO1 0x07000012
+#define FX_GRAMOFON 0x07000013
+#define FX_SHOCK1 0x07000014
+#define FX_WINDUP11 0x07000015
+#define FX_FRISK 0x07000016
+#define FX_TRAFFIC3 0x07000017
+#define FX_DESKBELL 0x07000018
+#define FX_KEY13 0x07000019
+#define FX_PAPER6 0x0700001A
+#define FX_PHONEDN2 0x0700001B
+#define FX_PHONEUP2 0x0700001C
+#define FX_PIANO14 0x0700001D
+#define FX_TRYDOR14 0x0700001E
+#define FX_CABCLOSE 0x0700001F
+#define FX_CABOPEN 0x07000020
+#define FX_DORCLOSE 0x07000021
+#define FX_WINDOPEN 0x07000022
+#define FX_COO 0x07000023
+#define FX_COO2 0x07000024
+#define FX_LEDGE1 0x07000025
+#define FX_LEDGE2 0x07000026
+#define FX_BRIEFOFF 0x07000027
+#define FX_BRIEFON 0x07000028
+#define FX_JUMPIN 0x07000029
+#define FX_WARDIN 0x0700002A
+#define FX_WARDOUT 0x0700002B
+#define FX_CLIMBIN 0x0700002C
+#define FX_CLIMBOUT 0x0700002D
+ // 46 entities in TXTs, 46 in datafiles.
+ // room9
+#define PARIS2_PAL 0x07010000
+#define room9_PAL 0x07010001
+#define room9_l0 0x07010002
+#define room9_l1 0x07010003
+#define room9_l2 0x07010004
+#define room9_gd1 0x07010005
+#define room9_gd2 0x07010006
+#define room9_flr 0x07010007
+#define CARA9CDT 0x07010008
+#define CARA9 0x07010009
+#define CARB9CDT 0x0701000A
+#define CARB9 0x0701000B
+#define CARC9CDT 0x0701000C
+#define CARC9 0x0701000D
+#define DOROPN09CDR 0x0701000E
+#define DOROPN09CDT 0x0701000F
+#define DOROPN09 0x07010010
+#define FLWLUKCDR 0x07010011
+#define FLWLUKCDT 0x07010012
+#define FLWLUK 0x07010013
+#define FLWNITCDT 0x07010014
+#define FLWNIT 0x07010015
+#define FLWTLKCDT 0x07010016
+#define FLWTLK 0x07010017
+#define GEODOOR9 0x07010018
+#define GEODOOR9CDT 0x07010019
+#define GEOSTPCDT 0x0701001A
+#define GEOSTP 0x0701001B
+#define GEOSTP8CDT 0x0701001C
+#define GEOSTP8 0x0701001D
+#define GEOTRY 0x0701001E
+#define GEOTRYCDT 0x0701001F
+#define TRUCKA9CDT 0x07010020
+#define TRUCKA9 0x07010021
+#define TRUCKB9CDT 0x07010022
+#define TRUCKB9 0x07010023
+#define TRUCKC9CDT 0x07010024
+#define TRUCKC9 0x07010025
+ // 38 entities in TXTs, 38 in datafiles.
+ // room10
+#define R10SPRPAL 0x07020000
+#define room10_PAL 0x07020001
+#define room10_l0 0x07020002
+#define room10_l1 0x07020003
+#define room10_gd1 0x07020004
+#define room10_flr 0x07020005
+#define DOORCDT 0x07020006
+#define DOOR 0x07020007
+#define GEOCCHCDT 0x07020008
+#define GEOCCHCDR 0x07020009
+#define GEOCCH 0x0702000A
+#define GEOCCH8CDT 0x0702000B
+#define GEOCCH8 0x0702000C
+#define GEOCCH9CDT 0x0702000D
+#define GEOCCH9 0x0702000E
+#define GEOCHRCDT 0x0702000F
+#define GEOCHR 0x07020010
+#define GEOCHR8CDT 0x07020011
+#define GEOCHR8 0x07020012
+#define GEOCHR9CDT 0x07020013
+#define GEOCHR9 0x07020014
+#define GEODWN10CDT 0x07020015
+#define GEODWN10 0x07020016
+#define GEOENT10CDT 0x07020017
+#define GEOENT10 0x07020018
+#define GEOGEM10CDT 0x07020019
+#define GEOGEM10 0x0702001A
+#define GEOGEMBCDT 0x0702001B
+#define GEOGEMB 0x0702001C
+#define GEOLUK10CDT 0x0702001D
+#define GEOLUK10 0x0702001E
+#define GEOLVS10CDT 0x0702001F
+#define GEOLVS10 0x07020020
+#define GEOMAGTKCDT 0x07020021
+#define GEOMAGTK 0x07020022
+#define GEOMAT10CDT 0x07020023
+#define GEOMAT10 0x07020024
+#define GEOMATBCDT 0x07020025
+#define GEOMATB 0x07020026
+#define GEOMATCCDT 0x07020027
+#define GEOMATC 0x07020028
+#define GEONIC10CDR 0x07020029
+#define GEONIC10CDT 0x0702002A
+#define GEONIC10 0x0702002B
+#define GEONOSCDT 0x0702002C
+#define GEONOS 0x0702002D
+#define GEONOS10CDT 0x0702002E
+#define GEONOS10 0x0702002F
+#define GEONWPAPCDT 0x07020030
+#define GEONWPAP 0x07020031
+#define GEOPHN10CDT 0x07020032
+#define GEOPHN10 0x07020033
+#define GEOPHTCDT 0x07020034
+#define GEOPHT 0x07020035
+#define GEOPHT10CDT 0x07020036
+#define GEOPHT10 0x07020037
+#define GEOPHTLKCDT 0x07020038
+#define GEOPHTLK 0x07020039
+#define GEOPPR10CDT 0x0702003A
+#define GEOPPR10 0x0702003B
+#define GEOPRDCDT 0x0702003C
+#define GEOPRD 0x0702003D
+#define GEOREDCDT 0x0702003E
+#define GEORED 0x0702003F
+#define GEORELCDR 0x07020040
+#define GEORELCDT 0x07020041
+#define GEOREL 0x07020042
+#define GEORXTCDT 0x07020043
+#define GEORXT 0x07020044
+#define GEOTIS10CDT 0x07020045
+#define GEOTIS10 0x07020046
+#define GEOTISBCDT 0x07020047
+#define GEOTISB 0x07020048
+#define GEOTRIP1CDT 0x07020049
+#define GEOTRIP1 0x0702004A
+#define GEOTRIP2CDT 0x0702004B
+#define GEOTRIP2 0x0702004C
+#define LOBTLK10CDT 0x0702004D
+#define LOBTLK10 0x0702004E
+#define MANUCDT 0x0702004F
+#define MANU 0x07020050
+#define NICCHR9CDR 0x07020051
+#define NICCHR9CDT 0x07020052
+#define NICCHR9 0x07020053
+#define NICDWN10CDT 0x07020054
+#define NICDWN10 0x07020055
+#define NICLUK10CDT 0x07020056
+#define NICLUK10 0x07020057
+#define NICMAGCDR 0x07020058
+#define NICMAGCDT 0x07020059
+#define NICMAG 0x0702005A
+#define NICNOSCDT 0x0702005B
+#define NICNOS 0x0702005C
+#define NICNOS10CDT 0x0702005D
+#define NICNOS10 0x0702005E
+#define NICNOSBCDT 0x0702005F
+#define NICNOSB 0x07020060
+#define NICPHN10CDT 0x07020061
+#define NICPHN10 0x07020062
+#define NICPHT10CDT 0x07020063
+#define NICPHT10 0x07020064
+#define NICPPRCDT 0x07020065
+#define NICPPR 0x07020066
+#define NICPPR2CDT 0x07020067
+#define NICPPR2 0x07020068
+#define NICREDCDT 0x07020069
+#define NICRED 0x0702006A
+#define NICRED2CDR 0x0702006B
+#define NICRED2CDT 0x0702006C
+#define NICRED2 0x0702006D
+#define NICSIT 0x0702006E
+#define NICSITCDT 0x0702006F
+#define NICTLK10CDT 0x07020070
+#define NICTLK10 0x07020071
+#define NICTLKL 0x07020072
+#define NICTLKLCDT 0x07020073
+#define NICTLKR 0x07020074
+#define NICTLKRCDT 0x07020075
+#define NICTLKX 0x07020076
+#define NICTLKXCDT 0x07020077
+#define NICTLKXCDR 0x07020078
+#define NICTRIPCDT 0x07020079
+#define NICTRIP 0x0702007A
+#define NICTURN 0x0702007B
+#define NICTURNCDT 0x0702007C
+#define NICTURNCDR 0x0702007D
+#define NICWRT 0x0702007E
+#define NICWRTCDT 0x0702007F
+#define PHONE10CDT 0x07020080
+#define PHONE10 0x07020081
+#define PEAPAGECDT 0x07020082
+#define PEAPAGE 0x07020083
+#define PEAPAGE_PAL 0x07020084
+#define MANUSCRPCDT 0x07020085
+#define MANUSCRP 0x07020086
+#define MANUSCRP_PAL 0x07020087
+#define TRIPOD10CDT 0x07020088
+#define TRIPOD10 0x07020089
+ // 138 entities in TXTs, 138 in datafiles.
+ // room11
+#define room11_PAL 0x07030000
+#define room11_l0 0x07030001
+#define room11_l1 0x07030002
+#define room11_l2 0x07030003
+#define room11_gd1 0x07030004
+#define room11_gd2 0x07030005
+#define room11_flr 0x07030006
+#define COSGIVCDR 0x07030007
+#define COSGIVCDT 0x07030008
+#define COSGIV 0x07030009
+#define COSLOOPCDT 0x0703000A
+#define COSLOOP 0x0703000B
+#define COSSHKCDT 0x0703000C
+#define COSSHK 0x0703000D
+#define COSSNFCDT 0x0703000E
+#define COSSNF 0x0703000F
+#define COSTLKCDT 0x07030010
+#define COSTLK 0x07030011
+#define DISCSPINCDT 0x07030012
+#define DISCSPIN 0x07030013
+#define GEOPIC11CDR 0x07030014
+#define GEOPIC11CDT 0x07030015
+#define GEOPIC11 0x07030016
+#define GEOSHK11CDT 0x07030017
+#define GEOSHK11 0x07030018
+#define GEOTURDCDT 0x07030019
+#define GEOTURD 0x0703001A
+#define GEOTIS11CDT 0x0703001B
+#define GEOTIS11 0x0703001C
+#define GEOWINDCDT 0x0703001D
+#define GEOWIND 0x0703001E
+#define WINDUPCDT 0x0703001F
+#define WINDUP 0x07030020
+ // 33 entities in TXTs, 33 in datafiles.
+ // room12
+#define room12_PAL 0x07040000
+#define room12_l0 0x07040001
+#define room12_l1 0x07040002
+#define room12_gd1 0x07040003
+#define room12_flr 0x07040004
+#define FLAGSCDT 0x07040005
+#define FLAGS 0x07040006
+#define GEOAPPCDT 0x07040007
+#define GEOAPP 0x07040008
+#define GEOARMCDT 0x07040009
+#define GEOARM 0x0704000A
+#define GEOASC12CDT 0x0704000B
+#define GEOASC12 0x0704000C
+#define GEODES12CDT 0x0704000D
+#define GEODES12 0x0704000E
+#define GORCOINCDT 0x0704000F
+#define GORCOIN 0x07040010
+#define GORFRKCDT 0x07040011
+#define GORFRK 0x07040012
+#define GORMANCDT 0x07040013
+#define GORMAN 0x07040014
+#define GORTALKCDT 0x07040015
+#define GORTALK 0x07040016
+#define GORTLKCDT 0x07040017
+#define GORTLK 0x07040018
+#define GORTURNCDR 0x07040019
+#define GORTURNCDT 0x0704001A
+#define GORTURN 0x0704001B
+#define LATVIAN12CDT 0x0704001C
+#define LATVIAN12 0x0704001D
+#define WEABAKCDT 0x0704001E
+#define WEABAK 0x0704001F
+#define WEASMKCDT 0x07040020
+#define WEASMK 0x07040021
+#define WEATLKCDT 0x07040022
+#define WEATLK 0x07040023
+#define WEATLK12CDT 0x07040024
+#define WEATLK12 0x07040025
+#define WEATRNCDR 0x07040026
+#define WEATRNCDT 0x07040027
+#define WEATRN 0x07040028
+ // 41 entities in TXTs, 41 in datafiles.
+ // room13
+#define R13SPRPAL 0x07050000
+#define room13_PAL 0x07050001
+#define room13_l0 0x07050002
+#define room13_l1 0x07050003
+#define room13_l2 0x07050004
+#define room13_gd1 0x07050005
+#define room13_gd2 0x07050006
+#define room13_flr 0x07050007
+#define CLKIDLCDT 0x07050008
+#define CLKIDL 0x07050009
+#define CLKMANCDT 0x0705000A
+#define CLKMAN 0x0705000B
+#define CLKTAKCDT 0x0705000C
+#define CLKTAK 0x0705000D
+#define CLKTLKCDT 0x0705000E
+#define CLKTLK 0x0705000F
+#define CLKTLK2CDT 0x07050010
+#define CLKTLK2 0x07050011
+#define CLKTLK3CDT 0x07050012
+#define CLKTLK3 0x07050013
+#define CLKTURNCDR 0x07050014
+#define CLKTURNCDT 0x07050015
+#define CLKTURN 0x07050016
+#define CLKWLKCDT 0x07050017
+#define CLKWLK 0x07050018
+#define CLKWLK2CDT 0x07050019
+#define CLKWLK2 0x0705001A
+#define CLKWLK3CDT 0x0705001B
+#define CLKWLK3 0x0705001C
+#define CLKWULCDT 0x0705001D
+#define CLKWUL 0x0705001E
+#define GEOASC13CDT 0x0705001F
+#define GEOASC13 0x07050020
+#define GEODES13CDT 0x07050021
+#define GEODES13 0x07050022
+#define GEOKEYCDT 0x07050023
+#define GEOKEY 0x07050024
+#define GEOKEY13CDT 0x07050025
+#define GEOKEY13 0x07050026
+#define GEOMAN13CDT 0x07050027
+#define GEOMAN13 0x07050028
+#define GEOTELCDT 0x07050029
+#define GEOTEL 0x0705002A
+#define GEOTEL9CDT 0x0705002B
+#define GEOTEL9 0x0705002C
+#define KEY13CDT 0x0705002D
+#define KEY13 0x0705002E
+#define LATLUKCDR 0x0705002F
+#define LATLUKCDT 0x07050030
+#define LATLUK 0x07050031
+#define LATRDSCDT 0x07050032
+#define LATRDS 0x07050033
+#define LATTLKCDT 0x07050034
+#define LATTLK 0x07050035
+#define PMTGIVCDT 0x07050036
+#define PMTGIV 0x07050037
+#define PMTPNOCDT 0x07050038
+#define PMTPNO 0x07050039
+#define PMTSTDCDT 0x0705003A
+#define PMTSTD 0x0705003B
+#define PMTTLKCDT 0x0705003C
+#define PMTTLK 0x0705003D
+#define PMTTLK8CDT 0x0705003E
+#define PMTTLK8 0x0705003F
+#define PMTTLK9CDT 0x07050040
+#define PMTTLK9 0x07050041
+#define PMTTOTLKCDR 0x07050042
+#define PMTTOTLKCDT 0x07050043
+#define PMTTOTLK 0x07050044
+#define PMTTURNCDR 0x07050045
+#define PMTTURNCDT 0x07050046
+#define PMTTURN 0x07050047
+#define PMTWLK1CDT 0x07050048
+#define PMTWLK1 0x07050049
+#define PMTWLK2CDT 0x0705004A
+#define PMTWLK2 0x0705004B
+ // 76 entities in TXTs, 76 in datafiles.
+ // room14
+#define room14_PAL 0x07060000
+#define room14_l0 0x07060001
+#define room14_l1 0x07060002
+#define room14_gd1 0x07060003
+#define room14_flr 0x07060004
+#define GEOASC14CDT 0x07060005
+#define GEOASC14 0x07060006
+#define GEODES14CDT 0x07060007
+#define GEODES14 0x07060008
+#define GEODORCDR 0x07060009
+#define GEODORCDT 0x0706000A
+#define GEODOR 0x0706000B
+ // 12 entities in TXTs, 12 in datafiles.
+ // room15
+#define room15_PAL 0x07070000
+#define room15_l0 0x07070001
+#define room15_l1 0x07070002
+#define room15_l2 0x07070003
+#define room15_gd1 0x07070004
+#define room15_gd2 0x07070005
+#define room15_flr 0x07070006
+#define DOROPN15CDT 0x07070007
+#define DOROPN15 0x07070008
+#define GEOBEDCDR 0x07070009
+#define GEOBEDCDT 0x0707000A
+#define GEOBED 0x0707000B
+#define GEODOR15CDT 0x0707000C
+#define GEODOR15 0x0707000D
+#define GEOENT15CDT 0x0707000E
+#define GEOENT15 0x0707000F
+#define GEOWIN1CDT 0x07070010
+#define GEOWIN1 0x07070011
+#define GEOWIN15CDT 0x07070012
+#define GEOWIN15 0x07070013
+#define GEOWIN2CDT 0x07070014
+#define GEOWIN2 0x07070015
+#define GEOWRBCDR 0x07070016
+#define GEOWRBCDT 0x07070017
+#define GEOWRB 0x07070018
+#define WINDOWCDT 0x07070019
+#define WINDOW 0x0707001A
+ // 27 entities in TXTs, 27 in datafiles.
+ // room16
+#define room16_PAL 0x07080000
+#define R16L0 0x07080001
+#define R16L1 0x07080002
+#define R16G1 0x07080003
+#define GEOILWCDT 0x07080004
+#define GEOILW 0x07080005
+#define GEOIRWCDT 0x07080006
+#define GEOIRW 0x07080007
+#define GEOMANLCDT 0x07080008
+#define GEOMANL 0x07080009
+#define GEOMANRCDT 0x0708000A
+#define GEOMANR 0x0708000B
+#define GEOMLWCDT 0x0708000C
+#define GEOMLW 0x0708000D
+#define GEOMRWCDT 0x0708000E
+#define GEOMRW 0x0708000F
+#define GEOOLWCDT 0x07080010
+#define GEOOLWCDR 0x07080011
+#define GEOOLW 0x07080012
+#define GEOORWCDT 0x07080013
+#define GEOORWCDR 0x07080014
+#define GEOORW 0x07080015
+#define MANLCDT 0x07080016
+#define MANL 0x07080017
+#define MANRCDT 0x07080018
+#define MANR 0x07080019
+ // 26 entities in TXTs, 26 in datafiles.
+ // room17
+#define room17_PAL 0x07090000
+#define room17_l0 0x07090001
+#define room17_l1 0x07090002
+#define room17_l2 0x07090003
+#define room17_gd1 0x07090004
+#define room17_gd2 0x07090005
+#define room17_flr 0x07090006
+#define IBACK17 0x07090007
+#define IBACK17PAL 0x07090008
+#define GEOINQ17 0x07090009
+#define GEOINQ17CDT 0x0709000A
+#define SBACK17 0x0709000B
+#define SBACK17PAL 0x0709000C
+#define GEOSUR17 0x0709000D
+#define GEOSUR17CDT 0x0709000E
+#define ASSTAIR2_PAL 0x0709000F
+#define ASSTAIR2 0x07090010
+#define ASSTAIRCDT 0x07090011
+#define ASSTAIR 0x07090012
+#define ASSDORCDT 0x07090013
+#define ASSDOR 0x07090014
+#define ASSDOR2CDT 0x07090015
+#define ASSDOR2 0x07090016
+#define ASSTRWCDT 0x07090017
+#define ASSTRW 0x07090018
+#define ASSWRBCDT 0x07090019
+#define ASSWRB 0x0709001A
+#define ASWLK17CDT 0x0709001B
+#define ASWLK17 0x0709001C
+#define BRFCASECDT 0x0709001D
+#define BRFCASE 0x0709001E
+#define GEOBCBCDR 0x0709001F
+#define GEOBCBCDT 0x07090020
+#define GEOBCB 0x07090021
+#define GEOBED17CDT 0x07090022
+#define GEOBED17 0x07090023
+#define GEOBFCCDR 0x07090024
+#define GEOBFCCDT 0x07090025
+#define GEOBFC 0x07090026
+#define GEODOR17CDT 0x07090027
+#define GEODOR17 0x07090028
+#define GEODOR2CDR 0x07090029
+#define GEODOR2CDT 0x0709002A
+#define GEODOR2 0x0709002B
+#define GEOPANTSCDT 0x0709002C
+#define GEOPANTS 0x0709002D
+#define GEOWIN8CDT 0x0709002E
+#define GEOWIN8 0x0709002F
+#define GEOWIN9CDT 0x07090030
+#define GEOWIN9 0x07090031
+#define GEOWRB1CDR 0x07090032
+#define GEOWRB1CDT 0x07090033
+#define GEOWRB1 0x07090034
+#define GEOWRB2CDT 0x07090035
+#define GEOWRB2 0x07090036
+#define GEOWRB3CDR 0x07090037
+#define GEOWRB3CDT 0x07090038
+#define GEOWRB3 0x07090039
+#define GEOWRB4CDT 0x0709003A
+#define GEOWRB4 0x0709003B
+#define PANTS2CDT 0x0709003C
+#define PANTS2 0x0709003D
+#define PANTWRBCDT 0x0709003E
+#define PANTWRB 0x0709003F
+#define STATDOORCDT 0x07090040
+#define STATDOOR 0x07090041
+#define WRBDOR17CDT 0x07090042
+#define WRBDOR17 0x07090043
+ // 68 entities in TXTs, 68 in datafiles.
+ // room18
+#define R18SPRPAL 0x070A0000
+#define room18_PAL 0x070A0001
+#define room18_l0 0x070A0002
+#define room18_l1 0x070A0003
+#define room18_l2 0x070A0004
+#define room18_gd1 0x070A0005
+#define room18_gd2 0x070A0006
+#define room18_flr 0x070A0007
+#define FAN18CDT 0x070A0008
+#define FAN18 0x070A0009
+#define GENBNDCDT 0x070A000A
+#define GENBND 0x070A000B
+#define GENSTRCDT 0x070A000C
+#define GENSTR 0x070A000D
+#define GENTLK18CDT 0x070A000E
+#define GENTLK18 0x070A000F
+#define GEOLVE18CDT 0x070A0010
+#define GEOLVE18 0x070A0011
+#define MOUBAK18CDT 0x070A0012
+#define MOUBAK18 0x070A0013
+#define MOUBUSYCDT 0x070A0014
+#define MOUBUSY 0x070A0015
+#define MOUTKROSCDT 0x070A0016
+#define MOUTKROS 0x070A0017
+#define MOUTLK18CDT 0x070A0018
+#define MOUTLK18 0x070A0019
+#define MOUWLK18CDT 0x070A001A
+#define MOUWLK18 0x070A001B
+#define ROSBAK18CDT 0x070A001C
+#define ROSBAK18 0x070A001D
+#define ROSBUSYCDT 0x070A001E
+#define ROSBUSY 0x070A001F
+#define ROSSTDCDT 0x070A0020
+#define ROSSTD 0x070A0021
+#define ROSTLK18CDT 0x070A0022
+#define ROSTLK18 0x070A0023
+#define ROSTLKXCDT 0x070A0024
+#define ROSTLKX 0x070A0025
+#define ROSTURNCDR 0x070A0026
+#define ROSTURNCDT 0x070A0027
+#define ROSTURN 0x070A0028
+#define ROSWLK18CDT 0x070A0029
+#define ROSWLK18 0x070A002A
+ // 43 entities in TXTs, 43 in datafiles.
+ // room46
+#define room46_PAL 0x070B0000
+#define room46_l0 0x070B0001
+#define room46_l1 0x070B0002
+#define room46_l2 0x070B0003
+#define room46_gd1 0x070B0004
+#define room46_gd2 0x070B0005
+#define room46_flr 0x070B0006
+#define MANU46CDT 0x070B0007
+#define MANU46 0x070B0008
+ // 9 entities in TXTs, 9 in datafiles.
+// paris_3
+ // sound_fx
+#define FX_MUESEXT 0x08000000
+#define FX_AIRCON28 0x08000001
+#define FX_SARCO28A 0x08000002
+#define FX_SARCO28B 0x08000003
+#define FX_SARCO28C 0x08000004
+#define FX_TOTEM28A 0x08000005
+#define FX_ALARM 0x08000006
+#define FX_CARABINE 0x08000007
+#define FX_DOOR29 0x08000008
+#define FX_FISHFALL 0x08000009
+#define FX_GDROP29 0x0800000A
+#define FX_GUI_HIT 0x0800000B
+#define FX_GUN1 0x0800000C
+#define FX_ROPEDOWN 0x0800000D
+#define FX_SARCO29 0x0800000E
+#define FX_SMASHGLA 0x0800000F
+#define FX_TOTEM29A 0x08000010
+#define FX_TOTEM29B 0x08000011
+#define FX_HOSPEXT 0x08000012
+#define FX_GRAVEL1 0x08000013
+#define FX_GRAVEL2 0x08000014
+#define FX_HOSPNOIS 0x08000015
+#define FX_CUPBOPEN 0x08000016
+#define FX_CUPBCLOS 0x08000017
+#define FX_KIKSHINY 0x08000018
+#define FX_SHINY 0x08000019
+#define FX_SHINYOFF 0x0800001A
+#define FX_SHINYON 0x0800001B
+#define FX_BLOODPRE 0x0800001C
+#define FX_GUN34 0x0800001D
+#define FX_PULSE2 0x0800001E
+#define FX_PULSE3 0x0800001F
+ // 32 entities in TXTs, 32 in datafiles.
+ // benoir
+#define MEGABEN 0x08010000
+#define BEN_WLK 0x08010001
+#define STDTAL1 0x08010002
+#define STDTAL1CDT 0x08010003
+#define STDTAL3 0x08010004
+#define STDTAL3CDT 0x08010005
+#define STDTAL5 0x08010006
+#define STDTAL5CDT 0x08010007
+#define XBENPG3 0x08010008
+#define XBENPG3CDT 0x08010009
+#define XBENPG5 0x0801000A
+#define XBENPG5CDT 0x0801000B
+ // 12 entities in TXTs, 12 in datafiles.
+ // mus
+#define MEGAMUS 0x08020000
+#define MUS_WLK 0x08020001
+#define MUSTLK0 0x08020002
+#define MUSTLK0CDT 0x08020003
+#define MUSTLK1 0x08020004
+#define MUSTLK1CDT 0x08020005
+#define MUSTLK2 0x08020006
+#define MUSTLK2CDT 0x08020007
+#define MUSTLK3 0x08020008
+#define MUSTLK3CDT 0x08020009
+#define MUSTLK4 0x0802000A
+#define MUSTLK4CDT 0x0802000B
+#define MUSTLK5 0x0802000C
+#define MUSTLK5CDT 0x0802000D
+#define MUSTLK6 0x0802000E
+#define MUSTLK6CDT 0x0802000F
+#define MUSTLK7 0x08020010
+#define MUSTLK7CDT 0x08020011
+ // 18 entities in TXTs, 18 in datafiles.
+ // white
+#define MEGA_WHITE 0x08030000
+#define WHTTLK0 0x08030001
+#define WHTTLK0CDT 0x08030002
+#define WHTTLK1 0x08030003
+#define WHTTLK1CDT 0x08030004
+#define WHTTLK2 0x08030005
+#define WHTTLK2CDT 0x08030006
+#define WHTTLK3 0x08030007
+#define WHTTLK3CDT 0x08030008
+#define WHTTLK4 0x08030009
+#define WHTTLK4CDT 0x0803000A
+#define WHTTLK5 0x0803000B
+#define WHTTLK5CDT 0x0803000C
+#define WHTTLK6 0x0803000D
+#define WHTTLK6CDT 0x0803000E
+#define WHTTLK7 0x0803000F
+#define WHTTLK7CDT 0x08030010
+#define WHTSHRUG 0x08030011
+#define WHTSHRUGCDT 0x08030012
+#define XGEOPG3 0x08030013
+#define XGEOPG3CDT 0x08030014
+#define XGEOPG5 0x08030015
+#define XGEOPG5CDT 0x08030016
+ // 23 entities in TXTs, 23 in datafiles.
+ // room27
+#define PARIS3_PAL 0x08040000
+#define room27_PAL 0x08040001
+#define R27L0 0x08040002
+#define R27L1 0x08040003
+#define R27L2 0x08040004
+#define R27G1 0x08040005
+#define R27G2 0x08040006
+#define room27_flr 0x08040007
+#define GEOWIN27CDR 0x08040008
+#define GEOWIN27CDT 0x08040009
+#define GEOWIN27 0x0804000A
+ // 11 entities in TXTs, 11 in datafiles.
+ // room28
+#define R28SPRPAL 0x08050000
+#define R28PAL 0x08050001
+#define R28L0 0x08050002
+#define R28L1 0x08050003
+#define R28L2 0x08050004
+#define R28G1 0x08050005
+#define R28G2 0x08050006
+#define room28_flr 0x08050007
+#define GEOALMCDT 0x08050008
+#define GEOALM 0x08050009
+#define GEOSARCDT 0x0805000A
+#define GEOSAR 0x0805000B
+#define GEOSAR1CDT 0x0805000C
+#define GEOSAR1 0x0805000D
+#define GEOSAR2CDR 0x0805000E
+#define GEOSAR2CDT 0x0805000F
+#define GEOSAR2 0x08050010
+#define GEOSHK28CDT 0x08050011
+#define GEOSHK28 0x08050012
+#define GEOTOTCDT 0x08050013
+#define GEOTOT 0x08050014
+#define GEOWINCDT 0x08050015
+#define GEOWIN 0x08050016
+#define GUAWINCDT 0x08050017
+#define GUAWIN 0x08050018
+#define LOBEXITCDT 0x08050019
+#define LOBEXIT 0x0805001A
+#define LOBMANCDT 0x0805001B
+#define LOBMAN 0x0805001C
+#define LOBSTUCDT 0x0805001D
+#define LOBSTU 0x0805001E
+#define LOBTLKCDT 0x0805001F
+#define LOBTLK 0x08050020
+#define LOBTURN1CDT 0x08050021
+#define LOBTURN1 0x08050022
+#define LOBTURN2CDT 0x08050023
+#define LOBTURN2 0x08050024
+#define MUSOPNCDT 0x08050025
+#define MUSOPN 0x08050026
+#define SARDOR28CDT 0x08050027
+#define SARDOR28 0x08050028
+#define TOTEM28CDT 0x08050029
+#define TOTEM28 0x0805002A
+#define TRIPOD28CDT 0x0805002B
+#define TRIPOD28 0x0805002C
+#define WINDO28CDR 0x0805002D
+#define WINDO28CDT 0x0805002E
+#define WINDO28 0x0805002F
+#define WINROD28CDT 0x08050030
+#define WINROD28 0x08050031
+ // 50 entities in TXTs, 50 in datafiles.
+ // room29
+#define R29SPRPAL 0x08060000
+#define BBACK29SPRPAL 0x08060001
+#define R29PAL 0x08060002
+#define R29L0 0x08060003
+#define R29L1 0x08060004
+#define R29G1 0x08060005
+#define room29_flr 0x08060006
+#define BBACK29 0x08060007
+#define BBACK29PAL 0x08060008
+#define GEOSUR29CDT 0x08060009
+#define GEOSUR29 0x0806000A
+#define CASECDT 0x0806000B
+#define CASE 0x0806000C
+#define DOOR29CDT 0x0806000D
+#define DOOR29 0x0806000E
+#define FISH29CDT 0x0806000F
+#define FISH29 0x08060010
+#define FLAPENTCDT 0x08060011
+#define FLAPENT 0x08060012
+#define FLAPTLK1CDT 0x08060013
+#define FLAPTLK1 0x08060014
+#define FLAPTLK3CDT 0x08060015
+#define FLAPTLK3 0x08060016
+#define FLAPTOR1CDR 0x08060017
+#define FLAPTOR1CDT 0x08060018
+#define FLAPTOR1 0x08060019
+#define FLAPTOR4CDT 0x0806001A
+#define FLAPTOR4 0x0806001B
+#define FLAPWLKCDT 0x0806001C
+#define FLAPWLK 0x0806001D
+#define GEODEDCDT 0x0806001E
+#define GEODED 0x0806001F
+#define GEOFISHCDT 0x08060020
+#define GEOFISH 0x08060021
+#define GEOFWDCDT 0x08060022
+#define GEOFWD 0x08060023
+#define GEOPEEKCDT 0x08060024
+#define GEOPEEK 0x08060025
+#define GEOSAR6CDT 0x08060026
+#define GEOSAR6 0x08060027
+#define GEOTOT29CDT 0x08060028
+#define GEOTOT29 0x08060029
+#define GEOTOTBCDT 0x0806002A
+#define GEOTOTB 0x0806002B
+#define GEOTPTOCDT 0x0806002C
+#define GEOTPTO 0x0806002D
+#define GUIENTCDT 0x0806002E
+#define GUIENT 0x0806002F
+#define GUIGUNCDT 0x08060030
+#define GUIGUN 0x08060031
+#define GUIKNECDT 0x08060032
+#define GUIKNE 0x08060033
+#define GUIKNE1CDT 0x08060034
+#define GUIKNE1 0x08060035
+#define GUITLK1CDT 0x08060036
+#define GUITLK1 0x08060037
+#define GUITLK3CDT 0x08060038
+#define GUITLK3 0x08060039
+#define GUITLK4CDT 0x0806003A
+#define GUITLK4 0x0806003B
+#define GUIWLKCDT 0x0806003C
+#define GUIWLK 0x0806003D
+#define NICPUS1CDT 0x0806003E
+#define NICPUS1 0x0806003F
+#define ROPE29CDT 0x08060040
+#define ROPE29 0x08060041
+#define SARDOORCDT 0x08060042
+#define SARDOOR 0x08060043
+#define TOR1CDT 0x08060044
+#define TOR1 0x08060045
+#define TOR3CDR 0x08060046
+#define TOR3CDT 0x08060047
+#define TOR3 0x08060048
+#define TOR4CDT 0x08060049
+#define TOR4 0x0806004A
+#define TOR6CDT 0x0806004B
+#define TOR6 0x0806004C
+#define TOR7CDT 0x0806004D
+#define TOR7 0x0806004E
+#define TOTFALLCDT 0x0806004F
+#define TOTFALL 0x08060050
+ // 81 entities in TXTs, 81 in datafiles.
+ // room31
+#define room31_PAL 0x08070000
+#define room31_l0 0x08070001
+#define room31_flr 0x08070002
+#define GEOAMBCDT 0x08070003
+#define GEOAMB 0x08070004
+#define GEODOR31CDT 0x08070005
+#define GEODOR31 0x08070006
+ // 7 entities in TXTs, 7 in datafiles.
+ // room32
+#define room32_PAL 0x08080000
+#define room32_l0 0x08080001
+#define room32_l1 0x08080002
+#define room32_l2 0x08080003
+#define room32_gd1 0x08080004
+#define room32_gd2 0x08080005
+#define room32_flr 0x08080006
+#define CONSTPCDT 0x08080007
+#define CONSTP 0x08080008
+#define CONTLKCDT 0x08080009
+#define CONTLK 0x0808000A
+#define CONTLK2CDT 0x0808000B
+#define CONTLK2 0x0808000C
+#define CONWLKCDT 0x0808000D
+#define CONWLK 0x0808000E
+#define RECCOMCDT 0x0808000F
+#define RECCOM 0x08080010
+#define RECKEYCDT 0x08080011
+#define RECKEY 0x08080012
+#define RECTLKCDT 0x08080013
+#define RECTLK 0x08080014
+#define RECTLK2CDT 0x08080015
+#define RECTLK2 0x08080016
+#define RECTRNCDT 0x08080017
+#define RECTRN 0x08080018
+ // 25 entities in TXTs, 25 in datafiles.
+ // room33
+#define room33_PAL 0x08090000
+#define room33_l0 0x08090001
+#define room33_l1 0x08090002
+#define room33_l2 0x08090003
+#define room33_gd1 0x08090004
+#define room33_gd2 0x08090005
+#define room33_flr 0x08090006
+#define DOMDRNCDT 0x08090007
+#define DOMDRN 0x08090008
+#define DOMDUHCDT 0x08090009
+#define DOMDUH 0x0809000A
+#define DOMKIKCDT 0x0809000B
+#define DOMKIK 0x0809000C
+#define DOMPLGCDT 0x0809000D
+#define DOMPLG 0x0809000E
+#define DOMTLKCDT 0x0809000F
+#define DOMTLK 0x08090010
+#define DOMVACCDT 0x08090011
+#define DOMVAC 0x08090012
+#define DOMWLKCDT 0x08090013
+#define DOMWLK 0x08090014
+#define DOMWLK2CDT 0x08090015
+#define DOMWLK2 0x08090016
+#define DOMWLK3CDT 0x08090017
+#define DOMWLK3 0x08090018
+#define DOMWLK4CDT 0x08090019
+#define DOMWLK4 0x0809001A
+#define GEOCOTCDT 0x0809001B
+#define GEOCOT 0x0809001C
+#define GEOPLG33CDT 0x0809001D
+#define GEOPLG33 0x0809001E
+#define GEOWATCDT 0x0809001F
+#define GEOWAT 0x08090020
+#define GEOWWATCDT 0x08090021
+#define GEOWWAT 0x08090022
+#define LEAD1CDT 0x08090023
+#define LEAD1 0x08090024
+#define LEAD2CDT 0x08090025
+#define LEAD2 0x08090026
+#define SHINY33CDT 0x08090027
+#define SHINY33 0x08090028
+ // 41 entities in TXTs, 41 in datafiles.
+ // room34
+#define room34_PAL 0x080A0000
+#define room34_l0 0x080A0001
+#define room34_l1 0x080A0002
+#define room34_l2 0x080A0003
+#define room34_l3 0x080A0004
+#define room34_gd1 0x080A0005
+#define room34_gd2 0x080A0006
+#define room34_gd3 0x080A0007
+#define room34_flr 0x080A0008
+#define R34PLX 0x080A0009
+#define BENBP1CDT 0x080A000A
+#define BENBP1 0x080A000B
+#define BENBP2CDT 0x080A000C
+#define BENBP2 0x080A000D
+#define DOOR34CDR 0x080A000E
+#define DOOR34CDT 0x080A000F
+#define DOOR34 0x080A0010
+#define GENDTLKCDT 0x080A0011
+#define GENDTLK 0x080A0012
+#define GENENTCDT 0x080A0013
+#define GENENT 0x080A0014
+#define GENSHOTCDT 0x080A0015
+#define GENSHOT 0x080A0016
+#define GENSTLKCDT 0x080A0017
+#define GENSTLK 0x080A0018
+#define GENSUPCDT 0x080A0019
+#define GENSUP 0x080A001A
+#define GENTRNCDT 0x080A001B
+#define GENTRN 0x080A001C
+#define GEOBAKCDT 0x080A001D
+#define GEOBAK 0x080A001E
+#define GEOBP2CDT 0x080A001F
+#define GEOBP2 0x080A0020
+#define GEOCH1CDR 0x080A0021
+#define GEOCH1CDT 0x080A0022
+#define GEOCH1 0x080A0023
+#define GEOCH2CDR 0x080A0024
+#define GEOCH2CDT 0x080A0025
+#define GEOCH2 0x080A0026
+#define GEOCH3CDR 0x080A0027
+#define GEOCH3CDT 0x080A0028
+#define GEOCH3 0x080A0029
+#define GEODOR34CDT 0x080A002A
+#define GEODOR34 0x080A002B
+#define GEOENT34CDT 0x080A002C
+#define GEOENT34 0x080A002D
+#define GEONURBPCDT 0x080A002E
+#define GEONURBP 0x080A002F
+#define GEOTAKCDT 0x080A0030
+#define GEOTAK 0x080A0031
+#define GEOTRYDCDT 0x080A0032
+#define GEOTRYD 0x080A0033
+#define GEOTRYTKCDT 0x080A0034
+#define GEOTRYTK 0x080A0035
+#define HATGIVCDT 0x080A0036
+#define HATGIV 0x080A0037
+#define HATSTPCDT 0x080A0038
+#define HATSTP 0x080A0039
+#define HATTLKCDT 0x080A003A
+#define HATTLK 0x080A003B
+#define HATWLKCDT 0x080A003C
+#define HATWLK 0x080A003D
+#define HATWLK2CDT 0x080A003E
+#define HATWLK2 0x080A003F
+#define MONITOR 0x080A0040
+#define MONITOR_PAL 0x080A0041
+#define PULSE1CDT 0x080A0042
+#define PULSE1 0x080A0043
+#define PULSE2CDT 0x080A0044
+#define PULSE2 0x080A0045
+#define PULSE3CDT 0x080A0046
+#define PULSE3 0x080A0047
+#define RENTLKCDT 0x080A0048
+#define RENTLK 0x080A0049
+#define SMARMCDR 0x080A004A
+#define SMARMCDT 0x080A004B
+#define SMARM 0x080A004C
+#define SMSTPCDR 0x080A004D
+#define SMSTPCDT 0x080A004E
+#define SMSTP 0x080A004F
+#define SMTALKCDT 0x080A0050
+#define SMTALK 0x080A0051
+#define SMTALK2CDT 0x080A0052
+#define SMTALK2 0x080A0053
+#define STUDENTCDT 0x080A0054
+#define STUDENT 0x080A0055
+#define STUDGIVCDT 0x080A0056
+#define STUDGIV 0x080A0057
+#define STUDMOVCDT 0x080A0058
+#define STUDMOV 0x080A0059
+ // 90 entities in TXTs, 90 in datafiles.
+ // room35
+#define room35_PAL 0x080B0000
+#define room35_l0 0x080B0001
+#define room35_l1 0x080B0002
+#define room35_gd1 0x080B0003
+#define room35_flr 0x080B0004
+#define ALSHOCKCDT 0x080B0005
+#define ALSHOCK 0x080B0006
+#define CURFLATCDT 0x080B0007
+#define CURFLAT 0x080B0008
+#define EKLBED35CDT 0x080B0009
+#define EKLBED35 0x080B000A
+#define EKLTLK35CDT 0x080B000B
+#define EKLTLK35 0x080B000C
+#define EKLWLK35CDT 0x080B000D
+#define EKLWLK35 0x080B000E
+#define GEORCT35CDT 0x080B000F
+#define GEORCT35 0x080B0010
+#define GEOSIT35CDR 0x080B0011
+#define GEOSIT35CDT 0x080B0012
+#define GEOSIT35 0x080B0013
+#define GEOTLKCDT 0x080B0014
+#define GEOTLK 0x080B0015
+#define GEOTLK35CDT 0x080B0016
+#define GEOTLK35 0x080B0017
+#define GEOTRN35CDT 0x080B0018
+#define GEOTRN35 0x080B0019
+#define JAQTLK1CDT 0x080B001A
+#define JAQTLK1 0x080B001B
+#define JAQTLK2CDT 0x080B001C
+#define JAQTLK2 0x080B001D
+ // 30 entities in TXTs, 30 in datafiles.
+// paris_4
+ // sound_fx
+#define FX_COVDWN 0x09000000
+#define FX_KEYIN 0x09000001
+#define FX_MANOP36 0x09000002
+#define FX_MONTAMB 0x09000003
+#define FX_OOH 0x09000004
+#define FX_PULLUP36 0x09000005
+#define FX_REPLCE36 0x09000006
+#define FX_AMBIEN37 0x09000007
+#define FX_CHAIN37 0x09000008
+#define FX_CHAIN37B 0x09000009
+#define FX_DOOR37 0x0900000A
+#define FX_HOLE37 0x0900000B
+#define FX_KNOCK37 0x0900000C
+#define FX_KNOCK37B 0x0900000D
+#define FX_WINCH37 0x0900000E
+#define FX_AIRCON41 0x0900000F
+#define FX_FONEDN41 0x09000010
+#define FX_FONEUP41 0x09000011
+#define FX_PHONCALL 0x09000012
+#define FX_THERMO1 0x09000013
+#define FX_TAPDRIP 0x09000014
+#define FX_DRIER1 0x09000015
+#define FX_CHURCHFX 0x09000016
+ // 23 entities in TXTs, 23 in datafiles.
+ // room36
+#define R36SPRPAL 0x09010000
+#define R36L0 0x09010001
+#define R36L1 0x09010002
+#define R36G1 0x09010003
+#define room36_PAL 0x09010004
+#define room36_flr 0x09010005
+#define R36PLX 0x09010006
+#define BAL36CDT 0x09010007
+#define BAL36 0x09010008
+#define CRO36APPCDT 0x09010009
+#define CRO36APP 0x0901000A
+#define CRO36GEOCDT 0x0901000B
+#define CRO36GEO 0x0901000C
+#define CRO36IDLCDT 0x0901000D
+#define CRO36IDL 0x0901000E
+#define CRO36JUGCDT 0x0901000F
+#define CRO36JUG 0x09010010
+#define CRO36UNICDT 0x09010011
+#define CRO36UNI 0x09010012
+#define GEN36DRICDT 0x09010013
+#define GEN36DRI 0x09010014
+#define GEN36FEHCDT 0x09010015
+#define GEN36FEH 0x09010016
+#define GEN36HURCDT 0x09010017
+#define GEN36HUR 0x09010018
+#define GEN36LEACDT 0x09010019
+#define GEN36LEA 0x0901001A
+#define GEN36POICDR 0x0901001B
+#define GEN36POICDT 0x0901001C
+#define GEN36POI 0x0901001D
+#define GEN36SPECDT 0x0901001E
+#define GEN36SPE 0x0901001F
+#define GEN36TALCDT 0x09010020
+#define GEN36TAL 0x09010021
+#define GEO36CLOCDT 0x09010022
+#define GEO36CLO 0x09010023
+#define GEO36ENTCDT 0x09010024
+#define GEO36ENT 0x09010025
+#define GEO36EXTCDT 0x09010026
+#define GEO36EXT 0x09010027
+#define GEO36JUGCDT 0x09010028
+#define GEO36JUG 0x09010029
+#define GEO36KNECDR 0x0901002A
+#define GEO36KNECDT 0x0901002B
+#define GEO36KNE 0x0901002C
+#define GEO36LUKCDR 0x0901002D
+#define GEO36LUKCDT 0x0901002E
+#define GEO36LUK 0x0901002F
+#define GEO36NOSCDT 0x09010030
+#define GEO36NOS 0x09010031
+#define GEO36OPECDT 0x09010032
+#define GEO36OPE 0x09010033
+#define GEO36POPCDT 0x09010034
+#define GEO36POP 0x09010035
+#define GEO36SHOCDR 0x09010036
+#define GEO36SHOCDT 0x09010037
+#define GEO36SHO 0x09010038
+#define GEO36SNTCDT 0x09010039
+#define GEO36SNT 0x0901003A
+#define GEO36STDCDT 0x0901003B
+#define GEO36STD 0x0901003C
+#define GEO36STUCDT 0x0901003D
+#define GEO36STU 0x0901003E
+#define GEO36TAKCDT 0x0901003F
+#define GEO36TAK 0x09010040
+#define HAT36CDT 0x09010041
+#define HAT36 0x09010042
+#define JUG36FINCDT 0x09010043
+#define JUG36FIN 0x09010044
+#define JUG36FURCDT 0x09010045
+#define JUG36FUR 0x09010046
+#define JUG36GIVCDT 0x09010047
+#define JUG36GIV 0x09010048
+#define JUG36GOCDT 0x09010049
+#define JUG36GO 0x0901004A
+#define JUG36JUGCDT 0x0901004B
+#define JUG36JUG 0x0901004C
+#define JUG36LAFCDT 0x0901004D
+#define JUG36LAF 0x0901004E
+#define JUG36STACDT 0x0901004F
+#define JUG36STA 0x09010050
+#define JUG36TAKCDT 0x09010051
+#define JUG36TAK 0x09010052
+#define JUG36TALCDT 0x09010053
+#define JUG36TAL 0x09010054
+#define MANCOV36CDT 0x09010055
+#define MANCOV36 0x09010056
+#define MANOFF36CDT 0x09010057
+#define MANOFF36 0x09010058
+#define VIN36CDT 0x09010059
+#define VIN36 0x0901005A
+ // 91 entities in TXTs, 91 in datafiles.
+ // room37
+#define room37_PAL 0x09020000
+#define R37L0 0x09020001
+#define R37L1 0x09020002
+#define R37G1 0x09020003
+#define room37_flr 0x09020004
+#define DOR37COLCDT 0x09020005
+#define DOR37COL 0x09020006
+#define GEO37ASCCDT 0x09020007
+#define GEO37ASC 0x09020008
+#define GEO37COGCDT 0x09020009
+#define GEO37COG 0x0902000A
+#define GEO37DESCDT 0x0902000B
+#define GEO37DES 0x0902000C
+#define GEO37HUKCDR 0x0902000D
+#define GEO37HUKCDT 0x0902000E
+#define GEO37HUK 0x0902000F
+#define GEO37INBCDT 0x09020010
+#define GEO37INB 0x09020011
+#define GEO37LEVCDT 0x09020012
+#define GEO37LEV 0x09020013
+#define GEO37OUTCDT 0x09020014
+#define GEO37OUT 0x09020015
+#define GEO37RUNCDT 0x09020016
+#define GEO37RUN 0x09020017
+#define GEO37TA1CDT 0x09020018
+#define GEO37TA1 0x09020019
+#define GEO37TA2CDT 0x0902001A
+#define GEO37TA2 0x0902001B
+#define GEO37TA3CDT 0x0902001C
+#define GEO37TA3 0x0902001D
+#define GEO37TA4CDT 0x0902001E
+#define GEO37TA4 0x0902001F
+#define GEO37TU1CDR 0x09020020
+#define GEO37TU1CDT 0x09020021
+#define GEO37TU1 0x09020022
+#define GEO37TU2CDT 0x09020023
+#define GEO37TU2 0x09020024
+#define GEO37WLCDR 0x09020025
+#define GEO37WLCDT 0x09020026
+#define GEO37WL 0x09020027
+#define HOO37LBOCDR 0x09020028
+#define HOO37LBOCDT 0x09020029
+#define HOO37LBO 0x0902002A
+#define HOO37PULCDT 0x0902002B
+#define HOO37PUL 0x0902002C
+#define ROCKS1CDT 0x0902002D
+#define ROCKS1 0x0902002E
+#define WAL37BRECDT 0x0902002F
+#define WAL37BRE 0x09020030
+#define WAL37SLICDT 0x09020031
+#define WAL37SLI 0x09020032
+#define WATER37CDT 0x09020033
+#define WATER37 0x09020034
+#define WATER37ACDT 0x09020035
+#define WATER37A 0x09020036
+#define WHEEL37CDT 0x09020037
+#define WHEEL37 0x09020038
+ // 57 entities in TXTs, 57 in datafiles.
+ // room38
+#define R38SPRPAL 0x09030000
+#define room38_PAL 0x09030001
+#define R38L0 0x09030002
+#define R38L1 0x09030003
+#define R38G1 0x09030004
+#define room38_flr 0x09030005
+#define BBACK38 0x09030006
+#define BBACK38PAL 0x09030007
+#define GEOINQ38CDT 0x09030008
+#define GEOINQ38 0x09030009
+#define GEO38AS1CDT 0x0903000A
+#define GEO38AS1 0x0903000B
+#define GEO38AS2CDT 0x0903000C
+#define GEO38AS2 0x0903000D
+#define GEO38DE1CDT 0x0903000E
+#define GEO38DE1 0x0903000F
+#define GEO38DE2CDT 0x09030010
+#define GEO38DE2 0x09030011
+#define GEO38LUKCDR 0x09030012
+#define GEO38LUKCDT 0x09030013
+#define GEO38LUK 0x09030014
+ // 21 entities in TXTs, 21 in datafiles.
+ // room39
+#define R39SPRPAL 0x09040000
+#define R39L0 0x09040001
+#define R39L1 0x09040002
+#define R39G1 0x09040003
+#define room39_PAL 0x09040004
+#define room39_flr 0x09040005
+#define R39PLX 0x09040006
+#define BOA39GOACDT 0x09040007
+#define BOA39GOA 0x09040008
+#define BOA39SPRCDT 0x09040009
+#define BOA39SPR 0x0904000A
+#define CIV391T2CDR 0x0904000B
+#define CIV391T2CDT 0x0904000C
+#define CIV391T2 0x0904000D
+#define CIV391T3CDR 0x0904000E
+#define CIV391T3CDT 0x0904000F
+#define CIV391T3 0x09040010
+#define CIV392T3CDR 0x09040011
+#define CIV392T3CDT 0x09040012
+#define CIV392T3 0x09040013
+#define CIV39T1CDT 0x09040014
+#define CIV39T1 0x09040015
+#define CIV39T2CDT 0x09040016
+#define CIV39T2 0x09040017
+#define CIV39T3CDT 0x09040018
+#define CIV39T3 0x09040019
+#define CIVLIST1CDT 0x0904001A
+#define CIVLIST1 0x0904001B
+#define CIVLIST2CDT 0x0904001C
+#define CIVLIST2 0x0904001D
+#define CIVLIST3CDT 0x0904001E
+#define CIVLIST3 0x0904001F
+#define COL391T2CDR 0x09040020
+#define COL391T2CDT 0x09040021
+#define COL391T2 0x09040022
+#define COL391T3CDR 0x09040023
+#define COL391T3CDT 0x09040024
+#define COL391T3 0x09040025
+#define COL392T3CDR 0x09040026
+#define COL392T3CDT 0x09040027
+#define COL392T3 0x09040028
+#define COL39T1CDT 0x09040029
+#define COL39T1 0x0904002A
+#define COL39T2CDT 0x0904002B
+#define COL39T2 0x0904002C
+#define COL39T3CDT 0x0904002D
+#define COL39T3 0x0904002E
+#define COLLIST2CDT 0x0904002F
+#define COLLIST2 0x09040030
+#define COLLIST3CDT 0x09040031
+#define COLLIST3 0x09040032
+#define CON39TALCDT 0x09040033
+#define CON39TAL 0x09040034
+#define EKL391T2CDR 0x09040035
+#define EKL391T2CDT 0x09040036
+#define EKL391T2 0x09040037
+#define EKL391T3CDR 0x09040038
+#define EKL391T3CDT 0x09040039
+#define EKL391T3 0x0904003A
+#define EKL392T3CDR 0x0904003B
+#define EKL392T3CDT 0x0904003C
+#define EKL392T3 0x0904003D
+#define EKL39T1CDT 0x0904003E
+#define EKL39T1 0x0904003F
+#define EKL39T2CDT 0x09040040
+#define EKL39T2 0x09040041
+#define EKL39T3CDT 0x09040042
+#define EKL39T3 0x09040043
+#define EKLLIST1CDT 0x09040044
+#define EKLLIST1 0x09040045
+#define EKLLIST2CDT 0x09040046
+#define EKLLIST2 0x09040047
+#define EKLLIST3CDT 0x09040048
+#define EKLLIST3 0x09040049
+#define EXE391T2CDR 0x0904004A
+#define EXE391T2CDT 0x0904004B
+#define EXE391T2 0x0904004C
+#define EXE391T3CDR 0x0904004D
+#define EXE391T3CDT 0x0904004E
+#define EXE391T3 0x0904004F
+#define EXE392T3CDR 0x09040050
+#define EXE392T3CDT 0x09040051
+#define EXE392T3 0x09040052
+#define EXE39T1CDT 0x09040053
+#define EXE39T1 0x09040054
+#define EXE39T2CDT 0x09040055
+#define EXE39T2 0x09040056
+#define EXE39T3CDT 0x09040057
+#define EXE39T3 0x09040058
+#define GEO39ENTCDT 0x09040059
+#define GEO39ENT 0x0904005A
+#define GEO39EXTCDT 0x0904005B
+#define GEO39EXT 0x0904005C
+#define GEO39GEMCDR 0x0904005D
+#define GEO39GEMCDT 0x0904005E
+#define GEO39GEM 0x0904005F
+#define GEO39TRICDR 0x09040060
+#define GEO39TRICDT 0x09040061
+#define GEO39TRI 0x09040062
+#define LAT391T2CDR 0x09040063
+#define LAT391T2CDT 0x09040064
+#define LAT391T2 0x09040065
+#define LAT391T3CDR 0x09040066
+#define LAT391T3CDT 0x09040067
+#define LAT391T3 0x09040068
+#define LAT392T3CDR 0x09040069
+#define LAT392T3CDT 0x0904006A
+#define LAT392T3 0x0904006B
+#define LAT39T1CDT 0x0904006C
+#define LAT39T1 0x0904006D
+#define LAT39T2CDT 0x0904006E
+#define LAT39T2 0x0904006F
+#define LAT39T3CDT 0x09040070
+#define LAT39T3 0x09040071
+#define LATLIST1CDT 0x09040072
+#define LATLIST1 0x09040073
+#define LATLIST2CDT 0x09040074
+#define LATLIST2 0x09040075
+#define LATLIST3CDT 0x09040076
+#define LATLIST3 0x09040077
+#define MAS391T2CDR 0x09040078
+#define MAS391T2CDT 0x09040079
+#define MAS391T2 0x0904007A
+#define MAS391T3CDR 0x0904007B
+#define MAS391T3CDT 0x0904007C
+#define MAS391T3 0x0904007D
+#define MAS392T3CDR 0x0904007E
+#define MAS392T3CDT 0x0904007F
+#define MAS392T3 0x09040080
+#define MAS39T1CDT 0x09040081
+#define MAS39T1 0x09040082
+#define MAS39T2CDT 0x09040083
+#define MAS39T2 0x09040084
+#define MAS39T3CDT 0x09040085
+#define MAS39T3 0x09040086
+#define MASLIST1CDT 0x09040087
+#define MASLIST1 0x09040088
+#define MASLIST2CDT 0x09040089
+#define MASLIST2 0x0904008A
+#define MASLIST3CDT 0x0904008B
+#define MASLIST3 0x0904008C
+#define RAYS39CDT 0x0904008D
+#define RAYS39 0x0904008E
+#define TRI39ALOCDT 0x0904008F
+#define TRI39ALO 0x09040090
+#define TRI39GEMCDT 0x09040091
+#define TRI39GEM 0x09040092
+ // 147 entities in TXTs, 147 in datafiles.
+ // room40
+#define R40L0 0x09050000
+#define room40_PAL 0x09050001
+#define room40_flr 0x09050002
+#define GEO40ASCCDT 0x09050003
+#define GEO40ASC 0x09050004
+#define GEO40DESCDT 0x09050005
+#define GEO40DES 0x09050006
+#define GEO40DIPCDT 0x09050007
+#define GEO40DIP 0x09050008
+#define GUA40ENTCDT 0x09050009
+#define GUA40ENT 0x0905000A
+#define GUA40STACDT 0x0905000B
+#define GUA40STA 0x0905000C
+#define GUA40TALCDT 0x0905000D
+#define GUA40TAL 0x0905000E
+#define PAI40FAGCDT 0x0905000F
+#define PAI40FAG 0x09050010
+#define PAI40IDLCDT 0x09050011
+#define PAI40IDL 0x09050012
+#define PAI40LEACDT 0x09050013
+#define PAI40LEA 0x09050014
+#define PAI40STACDR 0x09050015
+#define PAI40STACDT 0x09050016
+#define PAI40STA 0x09050017
+#define PAI40TALCDT 0x09050018
+#define PAI40TAL 0x09050019
+ // 26 entities in TXTs, 26 in datafiles.
+ // room41
+#define R41L0 0x09060000
+#define R41L1 0x09060001
+#define R41G1 0x09060002
+#define room41_PAL 0x09060003
+#define room41_flr 0x09060004
+#define GEO41ENTCDT 0x09060005
+#define GEO41ENT 0x09060006
+#define GEO41OP2CDT 0x09060007
+#define GEO41OP2 0x09060008
+#define GEO41OPECDT 0x09060009
+#define GEO41OPE 0x0906000A
+#define GEO41THECDR 0x0906000B
+#define GEO41THECDT 0x0906000C
+#define GEO41THE 0x0906000D
+#define GLO41IDLCDT 0x0906000E
+#define GLO41IDL 0x0906000F
+#define GLO41KEYCDR 0x09060010
+#define GLO41KEYCDT 0x09060011
+#define GLO41KEY 0x09060012
+#define GLO41TALCDT 0x09060013
+#define GLO41TAL 0x09060014
+#define GUA41ANSCDT 0x09060015
+#define GUA41ANS 0x09060016
+#define GUA41GLOCDT 0x09060017
+#define GUA41GLO 0x09060018
+#define GUA41IDLCDT 0x09060019
+#define GUA41IDL 0x0906001A
+#define GUA41KEYCDR 0x0906001B
+#define GUA41KEYCDT 0x0906001C
+#define GUA41KEY 0x0906001D
+#define GUA41PHOCDT 0x0906001E
+#define GUA41PHO 0x0906001F
+#define GUA41TALCDT 0x09060020
+#define GUA41TAL 0x09060021
+#define PAI41HANCDT 0x09060022
+#define PAI41HAN 0x09060023
+#define PAI41LEACDT 0x09060024
+#define PAI41LEA 0x09060025
+#define PAI41TALCDT 0x09060026
+#define PAI41TAL 0x09060027
+#define PHONE41CDT 0x09060028
+#define PHONE41 0x09060029
+ // 42 entities in TXTs, 42 in datafiles.
+ // room42
+#define room42_PAL 0x09070000
+#define R42L0 0x09070001
+#define R42L1 0x09070002
+#define R42L2 0x09070003
+#define R42G1 0x09070004
+#define R42G2 0x09070005
+#define room42_flr 0x09070006
+#define CHALICE42 0x09070007
+#define CHALICE42_PAL 0x09070008
+#define GEO42CHACDT 0x09070009
+#define GEO42CHA 0x0907000A
+#define GEO42DESCDR 0x0907000B
+#define GEO42DESCDT 0x0907000C
+#define GEO42DES 0x0907000D
+ // 14 entities in TXTs, 14 in datafiles.
+ // room43
+#define room43_PAL 0x09080000
+#define R43L0 0x09080001
+#define R43L1 0x09080002
+#define R43G1 0x09080003
+#define room43_flr 0x09080004
+#define GEO43DR2CDT 0x09080005
+#define GEO43DR2 0x09080006
+#define GEO43DRYCDT 0x09080007
+#define GEO43DRY 0x09080008
+#define GEO43HAN 0x09080009
+#define GEO43HANCDT 0x0908000A
+#define GEO43KEYCDT 0x0908000B
+#define GEO43KEY 0x0908000C
+#define GEO43PLACDT 0x0908000D
+#define GEO43PLA 0x0908000E
+#define GEO43PRECDT 0x0908000F
+#define GEO43PRE 0x09080010
+#define GEO43SOACDT 0x09080011
+#define GEO43SOA 0x09080012
+#define GEO43SWPCDT 0x09080013
+#define GEO43SWP 0x09080014
+#define GEO43WATCDT 0x09080015
+#define GEO43WAT 0x09080016
+#define SOAP43CDT 0x09080017
+#define SOAP43 0x09080018
+#define WATER43CDT 0x09080019
+#define WATER43 0x0908001A
+ // 27 entities in TXTs, 27 in datafiles.
+ // room48
+#define R48SPRPAL 0x09090000
+#define R48L0 0x09090001
+#define R48L1 0x09090002
+#define R48L2 0x09090003
+#define R48G1 0x09090004
+#define R48G2 0x09090005
+#define R48PAL 0x09090006
+#define room48_flr 0x09090007
+#define R48PLX 0x09090008
+#define GEO48BENCDR 0x09090009
+#define GEO48BENCDT 0x0909000A
+#define GEO48BEN 0x0909000B
+#define GEO48GIVCDR 0x0909000C
+#define GEO48GIVCDT 0x0909000D
+#define GEO48GIV 0x0909000E
+#define GEO48LENCDT 0x0909000F
+#define GEO48LEN 0x09090010
+#define PRI48CANCDR 0x09090011
+#define PRI48CANCDT 0x09090012
+#define PRI48CAN 0x09090013
+#define PRI48IDLCDT 0x09090014
+#define PRI48IDL 0x09090015
+#define PRI48PO1CDT 0x09090016
+#define PRI48PO1 0x09090017
+#define PRI48PO2CDT 0x09090018
+#define PRI48PO2 0x09090019
+#define PRI48RETCDT 0x0909001A
+#define PRI48RET 0x0909001B
+#define PRI48TAKCDT 0x0909001C
+#define PRI48TAK 0x0909001D
+#define PRI48TALCDT 0x0909001E
+#define PRI48TAL 0x0909001F
+#define PRI48TURCDR 0x09090020
+#define PRI48TURCDT 0x09090021
+#define PRI48TUR 0x09090022
+#define WINDOW1 0x09090023
+#define WINDOW1_PAL 0x09090024
+#define WINDOW2 0x09090025
+#define WINDOW2_PAL 0x09090026
+ // 39 entities in TXTs, 39 in datafiles.
+// ireland
+ // sound_fx
+#define FX_EIRBIRD3 0x0A000000
+#define FX_SHOCK2 0x0A000001
+#define FX_EIRBIRD1 0x0A000002
+#define FX_EIRBIRD2 0x0A000003
+#define FX_SWITCH19 0x0A000004
+#define FX_TRAPOPEN 0x0A000005
+#define FX_VIOLIN19 0x0A000006
+#define FX_WHISTLE 0x0A000007
+#define FX_BARFLAP 0x0A000008
+#define FX_DORCLOSE20 0x0A000009
+#define FX_DRINK 0x0A00000A
+#define FX_FITZHIT 0x0A00000B
+#define FX_FITZRUN 0x0A00000C
+#define FX_FITZUP 0x0A00000D
+#define FX_FUSE20 0x0A00000E
+#define FX_PULLPINT 0x0A00000F
+#define FX_SNEEZE1 0x0A000010
+#define FX_SNEEZE2 0x0A000011
+#define FX_WASHER 0x0A000012
+#define FX_CELTAP 0x0A000013
+#define FX_DRIPIRE 0x0A000014
+#define FX_DRIPIRE2 0x0A000015
+#define FX_TAP 0x0A000016
+#define FX_TAP2 0x0A000017
+#define FX_CLIMBHAY 0x0A000018
+#define FX_FARMERGO 0x0A000019
+#define FX_CASTLWAL 0x0A00001A
+#define FX_CLIMBFAL 0x0A00001B
+#define FX_KEYSTEP 0x0A00001C
+#define FX_WIND 0x0A00001D
+#define FX_GEOGOAT 0x0A00001E
+#define FX_GOATBAA 0x0A00001F
+#define FX_GOATCHEW 0x0A000020
+#define FX_GOATDOH 0x0A000021
+#define FX_PLOUGH 0x0A000022
+#define FX_EIRDRIP1 0x0A000023
+#define FX_EIRDRIP2 0x0A000024
+#define FX_LADDWN25 0x0A000025
+#define FX_LADDUP25 0x0A000026
+#define FX_SECDOR25 0x0A000027
+#define FX_SLABFALL 0x0A000028
+#define FX_SLABUP 0x0A000029
+#define FX_TRIGER25 0x0A00002A
+#define FX_WRING 0x0A00002B
+#define FX_LEVER 0x0A00002C
+#define FX_LEVER2 0x0A00002D
+#define FX_RAT3A 0x0A00002E
+#define FX_RAT3B 0x0A00002F
+#define FX_RAT3C 0x0A000030
+#define FX_RAT3D 0x0A000031
+ // 50 entities in TXTs, 50 in datafiles.
+ // room19
+#define R19SPRPAL 0x0A010000
+#define R19L0 0x0A010001
+#define R19L1 0x0A010002
+#define R19L2 0x0A010003
+#define R19G1 0x0A010004
+#define R19G2 0x0A010005
+#define R19PAL 0x0A010006
+#define R19FLR 0x0A010007
+#define ASSTAP 0x0A010008
+#define ASSTAPCDT 0x0A010009
+#define ASSTLK19 0x0A01000A
+#define ASSTLK19CDT 0x0A01000B
+#define ASSWLK2 0x0A01000C
+#define ASSWLK2CDT 0x0A01000D
+#define ASSWLK6 0x0A01000E
+#define ASSWLK6CDT 0x0A01000F
+#define BOXCVR 0x0A010010
+#define BOXCVRCDT 0x0A010011
+#define GEODRN 0x0A010012
+#define GEODRNCDT 0x0A010013
+#define GEOMAG 0x0A010014
+#define GEOMAGCDT 0x0A010015
+#define GEORCT 0x0A010016
+#define GEORCTCDT 0x0A010017
+#define GEOTRP 0x0A010018
+#define GEOTRPCDT 0x0A010019
+#define GEOTRP8 0x0A01001A
+#define GEOTRP8CDT 0x0A01001B
+#define GEOWRN 0x0A01001C
+#define GEOWRNCDT 0x0A01001D
+#define LID19 0x0A01001E
+#define LID19CDT 0x0A01001F
+#define MAGCON 0x0A010020
+#define MAGCONCDT 0x0A010021
+#define MAGCONCDR 0x0A010022
+#define MAGMOV 0x0A010023
+#define MAGMOVCDT 0x0A010024
+#define MAGMOVCDR 0x0A010025
+#define MAGSHK 0x0A010026
+#define MAGSHKCDT 0x0A010027
+#define MAGSHY 0x0A010028
+#define MAGSHYCDT 0x0A010029
+#define MAGSHYCDR 0x0A01002A
+#define MAGSLK 0x0A01002B
+#define MAGSLKCDT 0x0A01002C
+#define MAGTALK 0x0A01002D
+#define MAGTALKCDT 0x0A01002E
+#define MAGTLK 0x0A01002F
+#define MAGTLKCDT 0x0A010030
+#define MAGTLK8 0x0A010031
+#define MAGTLK8CDT 0x0A010032
+#define TRPOPN 0x0A010033
+#define TRPOPNCDT 0x0A010034
+ // 53 entities in TXTs, 53 in datafiles.
+ // room20
+#define R20SPRPAL 0x0A020000
+#define R20L0 0x0A020001
+#define R20L1 0x0A020002
+#define R20L2 0x0A020003
+#define R20L3 0x0A020004
+#define R20G1 0x0A020005
+#define R20G2 0x0A020006
+#define R20G3 0x0A020007
+#define R20PAL 0x0A020008
+#define R20FLR 0x0A020009
+#define BARTPS 0x0A02000A
+#define BARTPSCDT 0x0A02000B
+#define BARTWL 0x0A02000C
+#define BARTWLCDT 0x0A02000D
+#define BRIDRN 0x0A02000E
+#define BRIDRNCDT 0x0A02000F
+#define BRIDRNCDR 0x0A020010
+#define BRIFTZ 0x0A020011
+#define BRIFTZCDT 0x0A020012
+#define BRIFTZCDR 0x0A020013
+#define BRITLK 0x0A020014
+#define BRITLKCDT 0x0A020015
+#define BRITLK8 0x0A020016
+#define BRITLK8CDT 0x0A020017
+#define BRITLK9 0x0A020018
+#define BRITLK9CDT 0x0A020019
+#define BRITRN 0x0A02001A
+#define BRITRNCDT 0x0A02001B
+#define BRITRNCDR 0x0A02001C
+#define DOROPN20 0x0A02001D
+#define DOROPN20CDT 0x0A02001E
+#define DOROPN20CDR 0x0A02001F
+#define DOYDRN 0x0A020020
+#define DOYDRNCDT 0x0A020021
+#define DOYDRNCDR 0x0A020022
+#define DOYFTZ 0x0A020023
+#define DOYFTZCDT 0x0A020024
+#define DOYFTZCDR 0x0A020025
+#define DOYTLK 0x0A020026
+#define DOYTLKCDT 0x0A020027
+#define DOYTLK20 0x0A020028
+#define DOYTLK20CDT 0x0A020029
+#define DOYTLK8 0x0A02002A
+#define DOYTLK8CDT 0x0A02002B
+#define DOYTLK9 0x0A02002C
+#define DOYTLK9CDT 0x0A02002D
+#define DOYTRN 0x0A02002E
+#define DOYTRNCDT 0x0A02002F
+#define DOYTRNCDR 0x0A020030
+#define DOYTRN20 0x0A020031
+#define DOYTRN20CDT 0x0A020032
+#define DOYTRN20CDR 0x0A020033
+#define DOYTRN8 0x0A020034
+#define DOYTRN8CDT 0x0A020035
+#define DOYTRN8CDR 0x0A020036
+#define DOYTRN9 0x0A020037
+#define DOYTRN9CDT 0x0A020038
+#define DOYTRN9CDR 0x0A020039
+#define FIDPLY 0x0A02003A
+#define FIDPLYCDT 0x0A02003B
+#define FLPOPN 0x0A02003C
+#define FLPOPNCDT 0x0A02003D
+#define FRMTLK20 0x0A02003E
+#define FRMTLK20CDT 0x0A02003F
+#define FTZAGT 0x0A020040
+#define FTZAGTCDT 0x0A020041
+#define FTZGET 0x0A020042
+#define FTZGETCDT 0x0A020043
+#define FTZGETCDR 0x0A020044
+#define FTZRUN 0x0A020045
+#define FTZRUNCDT 0x0A020046
+#define FTZSTD 0x0A020047
+#define FTZSTDCDT 0x0A020048
+#define FTZTALK 0x0A020049
+#define FTZTALKCDT 0x0A02004A
+#define FTZTLK 0x0A02004B
+#define FTZTLKCDT 0x0A02004C
+#define FTZTLK8 0x0A02004D
+#define FTZTLK8CDT 0x0A02004E
+#define GEODRN20 0x0A02004F
+#define GEODRN20CDT 0x0A020050
+#define GEOPHN20 0x0A020051
+#define GEOPHN20CDT 0x0A020052
+#define GEOPHN20CDR 0x0A020053
+#define GEOPLG 0x0A020054
+#define GEOPLGCDT 0x0A020055
+#define GEOPLG8 0x0A020056
+#define GEOPLG8CDT 0x0A020057
+#define GEOSEE 0x0A020058
+#define GEOSEECDT 0x0A020059
+#define GEOSNR 0x0A02005A
+#define GEOSNRCDT 0x0A02005B
+#define GEOSNR8 0x0A02005C
+#define GEOSNR8CDT 0x0A02005D
+#define GEOTWL20 0x0A02005E
+#define GEOTWL20CDT 0x0A02005F
+#define GEOXIT20 0x0A020060
+#define GEOXIT20CDT 0x0A020061
+#define GLSGEO 0x0A020062
+#define GLSGEOCDT 0x0A020063
+#define GLSWSH 0x0A020064
+#define GLSWSHCDT 0x0A020065
+#define LESDOR 0x0A020066
+#define LESDORCDT 0x0A020067
+#define LESDOY 0x0A020068
+#define LESDOYCDT 0x0A020069
+#define LESFLP 0x0A02006A
+#define LESFLPCDT 0x0A02006B
+#define LESFTZ 0x0A02006C
+#define LESFTZCDT 0x0A02006D
+#define LESFTZCDR 0x0A02006E
+#define LESGLS 0x0A02006F
+#define LESGLSCDT 0x0A020070
+#define LESGLSCDR 0x0A020071
+#define LESLNS 0x0A020072
+#define LESLNSCDT 0x0A020073
+#define LESLNSCDR 0x0A020074
+#define LESMOV 0x0A020075
+#define LESMOVCDT 0x0A020076
+#define LESMOVCDR 0x0A020077
+#define LESPMP 0x0A020078
+#define LESPMPCDT 0x0A020079
+#define LESPNT 0x0A02007A
+#define LESPNTCDT 0x0A02007B
+#define LESPNT20 0x0A02007C
+#define LESPNT20CDT 0x0A02007D
+#define LESPNT8 0x0A02007E
+#define LESPNT8CDT 0x0A02007F
+#define LESPNT8CDR 0x0A020080
+#define LESTALK 0x0A020081
+#define LESTALKCDT 0x0A020082
+#define LESTLK 0x0A020083
+#define LESTLKCDT 0x0A020084
+#define LESTLK20 0x0A020085
+#define LESTLK20CDT 0x0A020086
+#define LESTLK8 0x0A020087
+#define LESTLK8CDT 0x0A020088
+#define LESTLK9 0x0A020089
+#define LESTLK9CDT 0x0A02008A
+#define LESTURN 0x0A02008B
+#define LESTURNCDT 0x0A02008C
+#define LESTURNCDR 0x0A02008D
+#define LESWLK 0x0A02008E
+#define LESWLKCDT 0x0A02008F
+#define LESWLK8 0x0A020090
+#define LESWLK8CDT 0x0A020091
+#define LESWSH 0x0A020092
+#define LESWSHCDT 0x0A020093
+#define LESWSHCDR 0x0A020094
+#define MAGENT 0x0A020095
+#define MAGENTCDT 0x0A020096
+#define MAGEXIT 0x0A020097
+#define MAGEXITCDT 0x0A020098
+#define MAGMOV20 0x0A020099
+#define MAGMOV20CDT 0x0A02009A
+#define MAGTEST 0x0A02009B
+#define MAGTESTCDT 0x0A02009C
+#define MAGTLK20 0x0A02009D
+#define MAGTLK20CDT 0x0A02009E
+#define PLGWLL 0x0A02009F
+#define PLGWLLCDT 0x0A0200A0
+#define RONDRN 0x0A0200A1
+#define RONDRNCDT 0x0A0200A2
+#define RONDRNCDR 0x0A0200A3
+#define RONDRP 0x0A0200A4
+#define RONDRPCDT 0x0A0200A5
+#define RONFTZ 0x0A0200A6
+#define RONFTZCDT 0x0A0200A7
+#define RONFTZCDR 0x0A0200A8
+#define RONGEO 0x0A0200A9
+#define RONGEOCDT 0x0A0200AA
+#define RONGEOCDR 0x0A0200AB
+#define RONGLS 0x0A0200AC
+#define RONGLSCDT 0x0A0200AD
+#define RONGLSCDR 0x0A0200AE
+#define RONGLS8 0x0A0200AF
+#define RONGLS8CDT 0x0A0200B0
+#define RONPIK 0x0A0200B1
+#define RONPIKCDT 0x0A0200B2
+#define RONPKT 0x0A0200B3
+#define RONPKTCDT 0x0A0200B4
+#define RONPKTCDR 0x0A0200B5
+#define RONSNR 0x0A0200B6
+#define RONSNRCDT 0x0A0200B7
+#define RONSNZ 0x0A0200B8
+#define RONSNZCDT 0x0A0200B9
+#define RONSNZ2 0x0A0200BA
+#define RONSNZ2CDT 0x0A0200BB
+#define RONTLK 0x0A0200BC
+#define RONTLKCDT 0x0A0200BD
+#define RONTLK2 0x0A0200BE
+#define RONTLK2CDT 0x0A0200BF
+#define RONWIR 0x0A0200C0
+#define RONWIRCDT 0x0A0200C1
+#define RONWIRCDR 0x0A0200C2
+#define SNRTBL 0x0A0200C3
+#define SNRTBLCDT 0x0A0200C4
+ // 197 entities in TXTs, 197 in datafiles.
+ // room21
+#define R21L0 0x0A030000
+#define R21L1 0x0A030001
+#define R21L2 0x0A030002
+#define R21G1 0x0A030003
+#define R21G2 0x0A030004
+#define R21PAL 0x0A030005
+#define R21FLR 0x0A030006
+#define CALBLO 0x0A030007
+#define CALBLOCDT 0x0A030008
+#define GEMSHN 0x0A030009
+#define GEMSHNCDT 0x0A03000A
+#define GEOGEM 0x0A03000B
+#define GEOGEMCDT 0x0A03000C
+#define GEOGEM8 0x0A03000D
+#define GEOGEM8CDT 0x0A03000E
+#define GEOLVR 0x0A03000F
+#define GEOLVRCDT 0x0A030010
+#define GEOLVR21 0x0A030011
+#define GEOLVR21CDT 0x0A030012
+#define GEOLVR8 0x0A030013
+#define GEOLVR8CDT 0x0A030014
+#define GEOTAP 0x0A030015
+#define GEOTAPCDT 0x0A030016
+#define GEOTLK21 0x0A030017
+#define GEOTLK21CDT 0x0A030018
+#define GEOTORCH 0x0A030019
+#define GEOTORCHCDT 0x0A03001A
+#define GEOTRN21 0x0A03001B
+#define GEOTRN21CDT 0x0A03001C
+#define GEOTRN21CDR 0x0A03001D
+#define GEOTWL21 0x0A03001E
+#define GEOTWL21CDT 0x0A03001F
+#define LVRPSH 0x0A030020
+#define LVRPSHCDT 0x0A030021
+#define MAGCEL 0x0A030022
+#define MAGCELCDT 0x0A030023
+#define MAGCELCDR 0x0A030024
+#define MAGTLK21 0x0A030025
+#define MAGTLK21CDT 0x0A030026
+#define TAPDRP 0x0A030027
+#define TAPDRPCDT 0x0A030028
+#define TAPRUN 0x0A030029
+#define TAPRUNCDT 0x0A03002A
+#define TORCH21 0x0A03002B
+#define TORCH21CDT 0x0A03002C
+ // 45 entities in TXTs, 45 in datafiles.
+ // room22
+#define R22SPRPAL 0x0A040000
+#define R22L0 0x0A040001
+#define R22L1 0x0A040002
+#define R22G1 0x0A040003
+#define R22PAL 0x0A040004
+#define R22FLR 0x0A040005
+#define FRMBUK 0x0A040006
+#define FRMBUKCDT 0x0A040007
+#define FRMSTD 0x0A040008
+#define FRMSTDCDT 0x0A040009
+#define FRMTLK 0x0A04000A
+#define FRMTLKCDT 0x0A04000B
+#define FRMWLK 0x0A04000C
+#define FRMWLKCDT 0x0A04000D
+#define GEOCLM 0x0A04000E
+#define GEOCLMCDT 0x0A04000F
+#define GEOCLMCDR 0x0A040010
+#define GEOPSH22 0x0A040011
+#define GEOPSH22CDT 0x0A040012
+ // 19 entities in TXTs, 19 in datafiles.
+ // room23
+#define R23L0 0x0A050000
+#define R23PAL 0x0A050001
+#define GEOCLM23 0x0A050002
+#define GEOCLM23CDT 0x0A050003
+#define GEOCLM23CDR 0x0A050004
+#define GEOKEY23 0x0A050005
+#define GEOKEY23CDT 0x0A050006
+#define GEOTOP 0x0A050007
+#define GEOTOPCDT 0x0A050008
+#define GEOTOPCDR 0x0A050009
+#define GEOTRY23 0x0A05000A
+#define GEOTRY23CDT 0x0A05000B
+#define GEOTUG23 0x0A05000C
+#define GEOTUG23CDT 0x0A05000D
+#define LFTKEY23 0x0A05000E
+#define LFTKEY23CDT 0x0A05000F
+ // 16 entities in TXTs, 16 in datafiles.
+ // room24
+#define R24L0 0x0A060000
+#define R24L1 0x0A060001
+#define R24G1 0x0A060002
+#define R24PLX 0x0A060003
+#define R24PAL 0x0A060004
+#define R24FLR 0x0A060005
+#define GEOASC24 0x0A060006
+#define GEOASC24CDT 0x0A060007
+#define GEODES24 0x0A060008
+#define GEODES24CDT 0x0A060009
+#define GEOHITL 0x0A06000A
+#define GEOHITLCDT 0x0A06000B
+#define GEOHITR 0x0A06000C
+#define GEOHITRCDT 0x0A06000D
+#define GEOLAD 0x0A06000E
+#define GEOLADCDT 0x0A06000F
+#define GEOLAD8 0x0A060010
+#define GEOLAD8CDT 0x0A060011
+#define GEOPLW 0x0A060012
+#define GEOPLWCDT 0x0A060013
+#define GEORUN 0x0A060014
+#define GEORUNCDT 0x0A060015
+#define GEOUPL24 0x0A060016
+#define GEOUPL24CDT 0x0A060017
+#define GEOUPR24 0x0A060018
+#define GEOUPR24CDT 0x0A060019
+#define GOTBAKL 0x0A06001A
+#define GOTBAKLCDT 0x0A06001B
+#define GOTBAKR 0x0A06001C
+#define GOTBAKRCDT 0x0A06001D
+#define GOTCL 0x0A06001E
+#define GOTCLCDT 0x0A06001F
+#define GOTCR 0x0A060020
+#define GOTCRCDT 0x0A060021
+#define GOTEAT 0x0A060022
+#define GOTEATCDT 0x0A060023
+#define GOTPLW 0x0A060024
+#define GOTPLWCDT 0x0A060025
+#define GOTPLW1 0x0A060026
+#define GOTPLW1CDT 0x0A060027
+#define GOTRIS 0x0A060028
+#define GOTRISCDT 0x0A060029
+#define GOTRISCDR 0x0A06002A
+#define PLWMOV 0x0A06002B
+#define PLWMOVCDT 0x0A06002C
+ // 45 entities in TXTs, 45 in datafiles.
+ // room25
+#define R25SPRPAL 0x0A070000
+#define R25L0 0x0A070001
+#define R25L1 0x0A070002
+#define R25G1 0x0A070003
+#define R25PAL 0x0A070004
+#define R25FLR 0x0A070005
+#define ALTOPN 0x0A070006
+#define ALTOPNCDT 0x0A070007
+#define GEOASC25 0x0A070008
+#define GEOASC25CDT 0x0A070009
+#define GEOCLM25 0x0A07000A
+#define GEOCLM25CDT 0x0A07000B
+#define GEODES25 0x0A07000C
+#define GEODES25CDT 0x0A07000D
+#define GEODRY25 0x0A07000E
+#define GEODRY25CDT 0x0A07000F
+#define GEOFIN25 0x0A070010
+#define GEOFIN25CDT 0x0A070011
+#define GEOPLAS 0x0A070012
+#define GEOPLASCDT 0x0A070013
+#define GEOPLASCDR 0x0A070014
+#define GEOPLS 0x0A070015
+#define GEOPLSCDT 0x0A070016
+#define GEOPSH25 0x0A070017
+#define GEOPSH25CDT 0x0A070018
+#define GEOPUT 0x0A070019
+#define GEOPUTCDT 0x0A07001A
+#define GEOSAC 0x0A07001B
+#define GEOSACCDT 0x0A07001C
+#define GEOSAC25 0x0A07001D
+#define GEOSAC25CDT 0x0A07001E
+#define GEOSTD25 0x0A07001F
+#define GEOSTD25CDT 0x0A070020
+#define GEOSTN 0x0A070021
+#define GEOSTNCDT 0x0A070022
+#define GEOSTN8 0x0A070023
+#define GEOSTN8CDT 0x0A070024
+#define GEOTWL25 0x0A070025
+#define GEOTWL25CDT 0x0A070026
+#define GEOWALF2 0x0A070027
+#define GEOWALF2CDT 0x0A070028
+#define IMPFLR 0x0A070029
+#define IMPFLRCDT 0x0A07002A
+#define IMPPLS 0x0A07002B
+#define IMPPLSCDT 0x0A07002C
+#define PLASWALL 0x0A07002D
+#define PLASWALLCDT 0x0A07002E
+#define STNFALL 0x0A07002F
+#define STNFALLCDT 0x0A070030
+ // 49 entities in TXTs, 49 in datafiles.
+ // room26
+#define R26SPRPAL 0x0A080000
+#define R26L0 0x0A080001
+#define R26L1 0x0A080002
+#define R26L2 0x0A080003
+#define R26G1 0x0A080004
+#define R26G2 0x0A080005
+#define R26PAL 0x0A080006
+#define R26FLR 0x0A080007
+#define GEOLVR08 0x0A080008
+#define GEOLVR08CDT 0x0A080009
+#define GEOLVR26 0x0A08000A
+#define GEOLVR26CDT 0x0A08000B
+#define LVRDRK 0x0A08000C
+#define LVRDRKCDT 0x0A08000D
+#define RATJMP 0x0A08000E
+#define RATJMPCDT 0x0A08000F
+ // 16 entities in TXTs, 16 in datafiles.
+// spain
+ // sound_fx
+#define FX_SPNBIRD1 0x0B000000
+#define FX_SPNBIRD2 0x0B000001
+#define FX_AMBIEN56 0x0B000002
+#define FX_DOGS56 0x0B000003
+#define FX_PENDULUM 0x0B000004
+#define FX_CANFALL 0x0B000005
+#define FX_HOSE57 0x0B000006
+#define FX_HOSE57B 0x0B000007
+#define FX_SPAIN 0x0B000008
+#define FX_CHESS 0x0B000009
+#define FX_SECDOR59 0x0B00000A
+#define FX_WINDOW59 0x0B00000B
+#define FX_LIONFALL 0x0B00000C
+#define FX_LIONFAL2 0x0B00000D
+#define FX_TOOTHPUL 0x0B00000E
+#define FX_SECDOR61 0x0B00000F
+#define FX_WELLDRIP 0x0B000010
+ // 17 entities in TXTs, 17 in datafiles.
+ // room56
+#define SPAIN_PAL 0x0B010000
+#define R56L0 0x0B010001
+#define R56L1 0x0B010002
+#define R56L2 0x0B010003
+#define R56G1 0x0B010004
+#define R56G2 0x0B010005
+#define R56PAL 0x0B010006
+#define R56FLR 0x0B010007
+#define CHALIC56 0x0B010008
+#define CHALIC56CDT 0x0B010009
+#define GARD20 0x0B01000A
+#define GARD20CDT 0x0B01000B
+#define GARD21 0x0B01000C
+#define GARD21CDT 0x0B01000D
+#define GARD22 0x0B01000E
+#define GARD22CDT 0x0B01000F
+#define GEOSPA10 0x0B010010
+#define GEOSPA10CDT 0x0B010011
+#define GEOSPA10CDR 0x0B010012
+#define GEOSPA11 0x0B010013
+#define GEOSPA11CDT 0x0B010014
+#define GEOSPA13 0x0B010015
+#define GEOSPA13CDT 0x0B010016
+#define GEOSPA13CDR 0x0B010017
+#define GEOSPA19 0x0B010018
+#define GEOSPA19CDT 0x0B010019
+#define GEOSPA44 0x0B01001A
+#define GEOSPA44CDT 0x0B01001B
+#define GEOSPA48 0x0B01001C
+#define GEOSPA48CDT 0x0B01001D
+#define PENDULUM 0x0B01001E
+#define PENDULUMCDT 0x0B01001F
+#define PIECE56 0x0B010020
+#define PIECE56CDT 0x0B010021
+#define VASC01 0x0B010022
+#define VASC01CDT 0x0B010023
+#define VASC02 0x0B010024
+#define VASC02CDT 0x0B010025
+#define VASC04 0x0B010026
+#define VASC04CDT 0x0B010027
+#define VASC05 0x0B010028
+#define VASC05CDT 0x0B010029
+#define VASC05CDR 0x0B01002A
+#define VASC06 0x0B01002B
+#define VASC06CDT 0x0B01002C
+#define VASC26 0x0B01002D
+#define VASC26CDT 0x0B01002E
+#define VASC26CDR 0x0B01002F
+#define VASC27 0x0B010030
+#define VASC27CDT 0x0B010031
+#define VASC28 0x0B010032
+#define VASC28CDT 0x0B010033
+#define VASC29 0x0B010034
+#define VASC29CDT 0x0B010035
+#define VASC29CDR 0x0B010036
+#define VASC30 0x0B010037
+#define VASC30CDT 0x0B010038
+#define VASC30CDR 0x0B010039
+#define VASC32 0x0B01003A
+#define VASC32CDT 0x0B01003B
+ // 60 entities in TXTs, 60 in datafiles.
+ // room57
+#define R57L0 0x0B020000
+#define R57L1 0x0B020001
+#define R57G1 0x0B020002
+#define R57PAL 0x0B020003
+#define R57FLR 0x0B020004
+#define R57PLX 0x0B020005
+#define MEGA_DOWSE 0x0B020006
+#define GAR57GOB 0x0B020007
+#define GAR57GOBCDT 0x0B020008
+#define GARD01 0x0B020009
+#define GARD01CDT 0x0B02000A
+#define GARD02 0x0B02000B
+#define GARD02CDT 0x0B02000C
+#define GARD03 0x0B02000D
+#define GARD03CDT 0x0B02000E
+#define GARD04 0x0B02000F
+#define GARD04CDT 0x0B020010
+#define GARD04CDR 0x0B020011
+#define GARD05 0x0B020012
+#define GARD05CDT 0x0B020013
+#define GARD06 0x0B020014
+#define GARD06CDT 0x0B020015
+#define GARD07 0x0B020016
+#define GARD07CDT 0x0B020017
+#define GARD07CDR 0x0B020018
+#define GARD08 0x0B020019
+#define GARD08CDT 0x0B02001A
+#define GARD09 0x0B02001B
+#define GARD09CDT 0x0B02001C
+#define GARD17 0x0B02001D
+#define GARD17CDT 0x0B02001E
+#define GARD17CDR 0x0B02001F
+#define GARD18 0x0B020020
+#define GARD18CDT 0x0B020021
+#define GARD31 0x0B020022
+#define GARD31CDT 0x0B020023
+#define GARD31CDR 0x0B020024
+#define GARD32 0x0B020025
+#define GARD32CDT 0x0B020026
+#define GARD34 0x0B020027
+#define GARD34CDT 0x0B020028
+#define GARD35 0x0B020029
+#define GARD35CDT 0x0B02002A
+#define GARD36 0x0B02002B
+#define GARD36CDT 0x0B02002C
+#define GARD37 0x0B02002D
+#define GARD37CDT 0x0B02002E
+#define GARD38 0x0B02002F
+#define GARD38CDT 0x0B020030
+#define GARD39 0x0B020031
+#define GARD39CDT 0x0B020032
+#define GARD40 0x0B020033
+#define GARD40CDT 0x0B020034
+#define GARD40CDR 0x0B020035
+#define GARD41 0x0B020036
+#define GARD41CDT 0x0B020037
+#define GARD42 0x0B020038
+#define GARD42CDT 0x0B020039
+#define GARD43 0x0B02003A
+#define GARD43CDT 0x0B02003B
+#define GARD44 0x0B02003C
+#define GARD44CDT 0x0B02003D
+#define GARD44CDR 0x0B02003E
+#define GARD45 0x0B02003F
+#define GARD45CDT 0x0B020040
+#define GARDKNE 0x0B020041
+#define GARDKNECDT 0x0B020042
+#define GAUGE57 0x0B020043
+#define GAUGE57CDT 0x0B020044
+#define GEOSPA01 0x0B020045
+#define GEOSPA01CDT 0x0B020046
+#define GEOSPA20 0x0B020047
+#define GEOSPA20CDT 0x0B020048
+#define GEOSPA22 0x0B020049
+#define GEOSPA22CDT 0x0B02004A
+#define GEOSPA42 0x0B02004B
+#define GEOSPA42CDT 0x0B02004C
+#define GEOSPA47 0x0B02004D
+#define GEOSPA47CDT 0x0B02004E
+#define HOLE57 0x0B02004F
+#define HOLE57CDT 0x0B020050
+#define HOSE57 0x0B020051
+#define HOSE57CDT 0x0B020052
+ // 83 entities in TXTs, 83 in datafiles.
+ // room58
+#define R58L0 0x0B030000
+#define R58L1 0x0B030001
+#define R58G1 0x0B030002
+#define R58PAL 0x0B030003
+#define R58FLR 0x0B030004
+#define R58PLX 0x0B030005
+#define GARD24 0x0B030006
+#define GARD24CDT 0x0B030007
+#define GARD25 0x0B030008
+#define GARD25CDT 0x0B030009
+#define GARD26 0x0B03000A
+#define GARD26CDT 0x0B03000B
+#define MAUSDOOR 0x0B03000C
+#define MAUSDOORCDT 0x0B03000D
+#define VASC07 0x0B03000E
+#define VASC07CDT 0x0B03000F
+#define VASC08 0x0B030010
+#define VASC08CDT 0x0B030011
+#define VASC09 0x0B030012
+#define VASC09CDT 0x0B030013
+#define VASC10 0x0B030014
+#define VASC10CDT 0x0B030015
+#define VASC11 0x0B030016
+#define VASC11CDT 0x0B030017
+#define VASC12 0x0B030018
+#define VASC12CDT 0x0B030019
+ // 26 entities in TXTs, 26 in datafiles.
+ // room59
+#define R59L0 0x0B040000
+#define R59L1 0x0B040001
+#define R59L2 0x0B040002
+#define R59G1 0x0B040003
+#define R59G2 0x0B040004
+#define R59PAL 0x0B040005
+#define R59FLR 0x0B040006
+#define BIBLE59 0x0B040007
+#define BIBLE59CDT 0x0B040008
+#define BLOWOUT 0x0B040009
+#define BLOWOUTCDT 0x0B04000A
+#define CANDLE59 0x0B04000B
+#define CANDLE59CDT 0x0B04000C
+#define CHAL59 0x0B04000D
+#define CHAL59CDT 0x0B04000E
+#define FLAMEL59 0x0B04000F
+#define FLAMEL59CDT 0x0B040010
+#define FLAMER59 0x0B040011
+#define FLAMER59CDT 0x0B040012
+#define FLAMET59 0x0B040013
+#define FLAMET59CDT 0x0B040014
+#define GARD27 0x0B040015
+#define GARD27CDT 0x0B040016
+#define GARD28 0x0B040017
+#define GARD28CDT 0x0B040018
+#define GARD29 0x0B040019
+#define GARD29CDT 0x0B04001A
+#define GARD30 0x0B04001B
+#define GARD30CDT 0x0B04001C
+#define GEOBIBLE 0x0B04001D
+#define GEOBIBLECDT 0x0B04001E
+#define GEOSPA12 0x0B04001F
+#define GEOSPA12CDT 0x0B040020
+#define GEOSPA16 0x0B040021
+#define GEOSPA16CDT 0x0B040022
+#define GEOSPA17 0x0B040023
+#define GEOSPA17CDT 0x0B040024
+#define GEOSPA18 0x0B040025
+#define GEOSPA18CDT 0x0B040026
+#define GEOSPA18CDR 0x0B040027
+#define GEOSPA23 0x0B040028
+#define GEOSPA23CDT 0x0B040029
+#define GEOSPA23CDR 0x0B04002A
+#define GEOSPA37 0x0B04002B
+#define GEOSPA37CDT 0x0B04002C
+#define GEOSPA37CDR 0x0B04002D
+#define GEOSPA38 0x0B04002E
+#define GEOSPA38CDT 0x0B04002F
+#define GEOSPA39 0x0B040030
+#define GEOSPA39CDT 0x0B040031
+#define GEOSPA40 0x0B040032
+#define GEOSPA40CDT 0x0B040033
+#define GEOSPA41 0x0B040034
+#define GEOSPA41CDT 0x0B040035
+#define GEOSPA43 0x0B040036
+#define GEOSPA43CDT 0x0B040037
+#define GEOSPA45 0x0B040038
+#define GEOSPA45CDT 0x0B040039
+#define GEOSPA46 0x0B04003A
+#define GEOSPA46CDT 0x0B04003B
+#define SECDOR59 0x0B04003C
+#define SECDOR59CDT 0x0B04003D
+#define SNUFF59 0x0B04003E
+#define SNUFF59CDT 0x0B04003F
+#define TISSUE59 0x0B040040
+#define TISSUE59CDT 0x0B040041
+#define VASC13 0x0B040042
+#define VASC13CDT 0x0B040043
+#define VASC14 0x0B040044
+#define VASC14CDT 0x0B040045
+#define VASC15 0x0B040046
+#define VASC15CDT 0x0B040047
+#define VASC16 0x0B040048
+#define VASC16CDT 0x0B040049
+#define VASC17 0x0B04004A
+#define VASC17CDT 0x0B04004B
+#define VASC18 0x0B04004C
+#define VASC18CDT 0x0B04004D
+#define VASC19 0x0B04004E
+#define VASC19CDT 0x0B04004F
+#define VASC20 0x0B040050
+#define VASC20CDT 0x0B040051
+#define VASC20CDR 0x0B040052
+#define VASC21 0x0B040053
+#define VASC21CDT 0x0B040054
+#define VASC22 0x0B040055
+#define VASC22CDT 0x0B040056
+#define VASC23 0x0B040057
+#define VASC23CDT 0x0B040058
+#define VASC24 0x0B040059
+#define VASC24CDT 0x0B04005A
+#define VASC25 0x0B04005B
+#define VASC25CDT 0x0B04005C
+#define VASC31 0x0B04005D
+#define VASC31CDT 0x0B04005E
+#define WINDSHUT 0x0B04005F
+#define WINDSHUTCDT 0x0B040060
+#define WINDSHUTCDR 0x0B040061
+ // 98 entities in TXTs, 98 in datafiles.
+ // room60
+#define R60L0 0x0B050000
+#define R60L1 0x0B050001
+#define R60L2 0x0B050002
+#define R60G1 0x0B050003
+#define R60G2 0x0B050004
+#define R60PAL 0x0B050005
+#define R60FLR 0x0B050006
+#define GARD10 0x0B050007
+#define GARD10CDT 0x0B050008
+#define GARD11 0x0B050009
+#define GARD11CDT 0x0B05000A
+#define GARD13 0x0B05000B
+#define GARD13CDT 0x0B05000C
+#define GARD16 0x0B05000D
+#define GARD16CDT 0x0B05000E
+#define GARD19 0x0B05000F
+#define GARD19CDT 0x0B050010
+#define GEOSPA05 0x0B050011
+#define GEOSPA05CDT 0x0B050012
+#define GEOSPA05CDR 0x0B050013
+#define GEOSPA08 0x0B050014
+#define GEOSPA08CDT 0x0B050015
+#define GEOSPA09 0x0B050016
+#define GEOSPA09CDT 0x0B050017
+#define GEOSPA09CDR 0x0B050018
+#define GEOSPA21 0x0B050019
+#define GEOSPA21CDT 0x0B05001A
+#define MIRROR60 0x0B05001B
+#define MIRROR60CDT 0x0B05001C
+ // 29 entities in TXTs, 29 in datafiles.
+ // room61
+#define R61L0 0x0B060000
+#define R61L1 0x0B060001
+#define R61G1 0x0B060002
+#define R61PAL 0x0B060003
+#define R61FLR 0x0B060004
+#define DUST 0x0B060005
+#define DUSTCDT 0x0B060006
+#define GEOSPA24 0x0B060007
+#define GEOSPA24CDT 0x0B060008
+#define GEOSPA25 0x0B060009
+#define GEOSPA25CDT 0x0B06000A
+#define GEOSPA26 0x0B06000B
+#define GEOSPA26CDT 0x0B06000C
+#define GEOSPA27 0x0B06000D
+#define GEOSPA27CDT 0x0B06000E
+#define GEOSPA30 0x0B06000F
+#define GEOSPA30CDT 0x0B060010
+#define GEOSPA31 0x0B060011
+#define GEOSPA31CDT 0x0B060012
+#define GEOSPA32 0x0B060013
+#define GEOSPA32CDT 0x0B060014
+#define GEOSPA33 0x0B060015
+#define GEOSPA33CDT 0x0B060016
+#define GEOSPA35 0x0B060017
+#define GEOSPA35CDT 0x0B060018
+#define LION1 0x0B060019
+#define LION1CDT 0x0B06001A
+#define LION2 0x0B06001B
+#define LION2CDT 0x0B06001C
+#define LIONDOOR 0x0B06001D
+#define LIONDOORCDT 0x0B06001E
+#define LIONFLOR 0x0B06001F
+#define LIONFLORCDT 0x0B060020
+#define ROPE61 0x0B060021
+#define ROPE61CDT 0x0B060022
+#define SECDOR61 0x0B060023
+#define SECDOR61CDT 0x0B060024
+ // 37 entities in TXTs, 37 in datafiles.
+ // room62
+#define R62L0 0x0B070000
+#define R62PAL 0x0B070001
+#define BISHOP62 0x0B070002
+#define BISHOP62CDT 0x0B070003
+#define KING62 0x0B070004
+#define KING62CDT 0x0B070005
+#define KNIGHT62 0x0B070006
+#define KNIGHT62CDT 0x0B070007
+ // 8 entities in TXTs, 8 in datafiles.
+// syria
+ // sound_fx
+#define FX_CAMERA45 0x0C000000
+#define FX_SHOCK3 0x0C000001
+#define FX_STALLBEL 0x0C000002
+#define FX_AYUBDOOR 0x0C000003
+#define FX_BALLPLAY 0x0C000004
+#define FX_CATHIT 0x0C000005
+#define FX_MARIB 0x0C000006
+#define FX_NEWTON 0x0C000007
+#define FX_STALLCAT 0x0C000008
+#define FX_STATBREK 0x0C000009
+#define FX_KEYS49 0x0C00000A
+#define FX_MANG1 0x0C00000B
+#define FX_MANG2 0x0C00000C
+#define FX_MANG3 0x0C00000D
+#define FX_UNLOCK49 0x0C00000E
+#define FX_WCCHAIN 0x0C00000F
+#define FX_CUBDOR 0x0C000010
+#define FX_BREKSTIK 0x0C000011
+#define FX_CLIMBDWN 0x0C000012
+#define FX_CRICKET 0x0C000013
+#define FX_GEOFAL54 0x0C000014
+#define FX_KHANDOWN 0x0C000015
+#define FX_RINGPULL 0x0C000016
+#define FX_SECDOR54 0x0C000017
+#define FX_SHOTKHAN 0x0C000018
+#define FX_SYRIWIND 0x0C000019
+#define FX_THUMP1 0x0C00001A
+#define FX_SECDOR55 0x0C00001B
+ // 28 entities in TXTs, 28 in datafiles.
+ // duane
+#define DUANE_MEGA 0x0C010000
+#define DUANE_WLK 0x0C010001
+#define DNETLK3 0x0C010002
+#define DNETLK3CDT 0x0C010003
+#define DNETLK5 0x0C010004
+#define DNETLK5CDT 0x0C010005
+#define XDNEMON3 0x0C010006
+#define XDNEMON3CDT 0x0C010007
+#define XDNEMON5 0x0C010008
+#define XDNEMON5CDT 0x0C010009
+#define XDNESTA3 0x0C01000A
+#define XDNESTA3CDT 0x0C01000B
+#define XDNESTA5 0x0C01000C
+#define XDNESTA5CDT 0x0C01000D
+#define XDNEPHO3 0x0C01000E
+#define XDNEPHO3CDT 0x0C01000F
+#define XDNEPHO5 0x0C010010
+#define XDNEPHO5CDT 0x0C010011
+ // 18 entities in TXTs, 18 in datafiles.
+ // room45
+#define SYRIA_PAL 0x0C020000
+#define R45SPRPAL 0x0C020001
+#define R45L0 0x0C020002
+#define R45L1 0x0C020003
+#define R45G1 0x0C020004
+#define R45PAL 0x0C020005
+#define R45FLR 0x0C020006
+#define R45PLX 0x0C020007
+#define ART1 0x0C020008
+#define ART1CDT 0x0C020009
+#define ART2 0x0C02000A
+#define ART2CDT 0x0C02000B
+#define ART3A 0x0C02000C
+#define ART3ACDT 0x0C02000D
+#define ART3ACDR 0x0C02000E
+#define ART4 0x0C02000F
+#define ART4CDT 0x0C020010
+#define ART5 0x0C020011
+#define ART5CDT 0x0C020012
+#define ART6 0x0C020013
+#define ART6CDT 0x0C020014
+#define ART7 0x0C020015
+#define ART7CDT 0x0C020016
+#define AYU1 0x0C020017
+#define AYU1CDT 0x0C020018
+#define AYU1A 0x0C020019
+#define AYU1ACDT 0x0C02001A
+#define AYU1B 0x0C02001B
+#define AYU1BCDT 0x0C02001C
+#define AYU5 0x0C02001D
+#define AYU5CDT 0x0C02001E
+#define BRUSH 0x0C02001F
+#define BRUSHCDT 0x0C020020
+#define CAT1 0x0C020021
+#define CAT1CDT 0x0C020022
+#define CAT3 0x0C020023
+#define CAT3CDT 0x0C020024
+#define CAT4 0x0C020025
+#define CAT4CDT 0x0C020026
+#define CAT5 0x0C020027
+#define CAT5CDT 0x0C020028
+#define GEOCHA 0x0C020029
+#define GEOCHACDT 0x0C02002A
+#define GEOSYR1 0x0C02002B
+#define GEOSYR1CDT 0x0C02002C
+#define GEOSYR10 0x0C02002D
+#define GEOSYR10CDT 0x0C02002E
+#define GEOSYR10CDR 0x0C02002F
+#define GEOSYR18 0x0C020030
+#define GEOSYR18CDT 0x0C020031
+#define GEOSYR2 0x0C020032
+#define GEOSYR2CDT 0x0C020033
+#define GEOSYR5 0x0C020034
+#define GEOSYR5CDT 0x0C020035
+#define GEOSYR6 0x0C020036
+#define GEOSYR6CDT 0x0C020037
+#define GEOSYR7 0x0C020038
+#define GEOSYR7CDT 0x0C020039
+#define NEJ1 0x0C02003A
+#define NEJ1CDT 0x0C02003B
+#define NEJ10 0x0C02003C
+#define NEJ10CDT 0x0C02003D
+#define NEJ12 0x0C02003E
+#define NEJ12CDT 0x0C02003F
+#define NEJ13 0x0C020040
+#define NEJ13CDT 0x0C020041
+#define NEJ13CDR 0x0C020042
+#define NEJ2 0x0C020043
+#define NEJ2CDT 0x0C020044
+#define NEJ2B 0x0C020045
+#define NEJ2BCDT 0x0C020046
+#define NEJ4 0x0C020047
+#define NEJ4CDT 0x0C020048
+#define NEJ6 0x0C020049
+#define NEJ6CDT 0x0C02004A
+#define NEJ7 0x0C02004B
+#define NEJ7CDT 0x0C02004C
+#define NEJ7B 0x0C02004D
+#define NEJ7BCDT 0x0C02004E
+#define NEJ8 0x0C02004F
+#define NEJ8CDT 0x0C020050
+#define NEJ9 0x0C020051
+#define NEJ9CDT 0x0C020052
+#define NEWTON 0x0C020053
+#define NEWTONCDT 0x0C020054
+#define PRL1 0x0C020055
+#define PRL1CDT 0x0C020056
+#define PRL2 0x0C020057
+#define PRL2CDT 0x0C020058
+#define PRL6 0x0C020059
+#define PRL6CDT 0x0C02005A
+#define PRL7 0x0C02005B
+#define PRL7CDT 0x0C02005C
+#define PRL8 0x0C02005D
+#define PRL8CDT 0x0C02005E
+#define SHO1 0x0C02005F
+#define SHO1CDT 0x0C020060
+#define SHO2 0x0C020061
+#define SHO2CDT 0x0C020062
+#define SHT1 0x0C020063
+#define SHT1CDT 0x0C020064
+#define SHT2 0x0C020065
+#define SHT2CDT 0x0C020066
+#define SHT3 0x0C020067
+#define SHT3CDT 0x0C020068
+#define STA2 0x0C020069
+#define STA2CDT 0x0C02006A
+#define STEAM45 0x0C02006B
+#define STEAM45CDT 0x0C02006C
+#define TRUCK45 0x0C02006D
+#define TRUCK45CDT 0x0C02006E
+#define TRUCKEMP 0x0C02006F
+#define TRUCKEMPCDT 0x0C020070
+#define ULT6 0x0C020071
+#define ULT6CDT 0x0C020072
+#define ULT7 0x0C020073
+#define ULT7CDT 0x0C020074
+#define ULT8 0x0C020075
+#define ULT8CDT 0x0C020076
+#define ULT9 0x0C020077
+#define ULT9CDT 0x0C020078
+#define XGEOMON3 0x0C020079
+#define XGEOMON3CDT 0x0C02007A
+#define XGEOMON5 0x0C02007B
+#define XGEOMON5CDT 0x0C02007C
+#define XGEOSTA3 0x0C02007D
+#define XGEOSTA3CDT 0x0C02007E
+#define XGEOSTA5 0x0C02007F
+#define XGEOSTA5CDT 0x0C020080
+#define XGEOSTAT 0x0C020081
+#define XGEOSTATCDT 0x0C020082
+ // 131 entities in TXTs, 131 in datafiles.
+ // room47
+#define R47L0 0x0C030000
+#define R47L1 0x0C030001
+#define R47L2 0x0C030002
+#define R47G1 0x0C030003
+#define R47G2 0x0C030004
+#define R47PAL 0x0C030005
+#define R47FLR 0x0C030006
+#define CAR1 0x0C030007
+#define CAR1CDT 0x0C030008
+#define CAR2 0x0C030009
+#define CAR2CDT 0x0C03000A
+#define CAR3 0x0C03000B
+#define CAR3CDT 0x0C03000C
+#define CAR4 0x0C03000D
+#define CAR4CDT 0x0C03000E
+#define CAR5 0x0C03000F
+#define CAR5CDT 0x0C030010
+#define CAR6 0x0C030011
+#define CAR6CDT 0x0C030012
+#define CRPMOV 0x0C030013
+#define CRPMOVCDT 0x0C030014
+#define GEOSYR3 0x0C030015
+#define GEOSYR3CDT 0x0C030016
+#define GEOSYR3A 0x0C030017
+#define GEOSYR3ACDT 0x0C030018
+#define GEOSYR4 0x0C030019
+#define GEOSYR4CDT 0x0C03001A
+#define GEOSYR4A 0x0C03001B
+#define GEOSYR4ACDT 0x0C03001C
+ // 29 entities in TXTs, 29 in datafiles.
+ // room49
+#define R49L0 0x0C040000
+#define R49L1 0x0C040001
+#define R49L2 0x0C040002
+#define R49G1 0x0C040003
+#define R49G2 0x0C040004
+#define R49PAL 0x0C040005
+#define R49FLR 0x0C040006
+#define DOOR49 0x0C040007
+#define DOOR49CDT 0x0C040008
+#define DOOR49CDR 0x0C040009
+#define GEOKEYS1 0x0C04000A
+#define GEOKEYS1CDT 0x0C04000B
+#define GEOSYR43 0x0C04000C
+#define GEOSYR43CDT 0x0C04000D
+#define KEYS 0x0C04000E
+#define KEYSCDT 0x0C04000F
+#define MAN1 0x0C040010
+#define MAN1CDT 0x0C040011
+#define MAN2 0x0C040012
+#define MAN2CDT 0x0C040013
+#define MAN3 0x0C040014
+#define MAN3CDT 0x0C040015
+#define MAN4 0x0C040016
+#define MAN4CDT 0x0C040017
+#define MAN4A 0x0C040018
+#define MAN4ACDT 0x0C040019
+#define ULT10 0x0C04001A
+#define ULT10CDT 0x0C04001B
+#define ULT15 0x0C04001C
+#define ULT15CDT 0x0C04001D
+#define ULT3 0x0C04001E
+#define ULT3CDT 0x0C04001F
+#define ULTTAK 0x0C040020
+#define ULTTAKCDT 0x0C040021
+ // 34 entities in TXTs, 34 in datafiles.
+ // room50
+#define R50L0 0x0C050000
+#define R50L1 0x0C050001
+#define R50L2 0x0C050002
+#define R50L3 0x0C050003
+#define R50G1 0x0C050004
+#define R50G2 0x0C050005
+#define R50G3 0x0C050006
+#define R50PAL 0x0C050007
+#define R50FLR 0x0C050008
+#define CHAIN50 0x0C050009
+#define CHAIN50CDT 0x0C05000A
+#define CUBDOR50 0x0C05000B
+#define CUBDOR50CDT 0x0C05000C
+#define DOOR50 0x0C05000D
+#define DOOR50CDT 0x0C05000E
+#define DOOR50CDR 0x0C05000F
+#define GEOSYR48 0x0C050010
+#define GEOSYR48CDT 0x0C050011
+#define GEOSYR49 0x0C050012
+#define GEOSYR49CDT 0x0C050013
+#define GEOSYR50 0x0C050014
+#define GEOSYR50CDT 0x0C050015
+#define GEOSYR51 0x0C050016
+#define GEOSYR51CDT 0x0C050017
+#define GEOSYR51CDR 0x0C050018
+#define GEOSYR52 0x0C050019
+#define GEOSYR52CDT 0x0C05001A
+#define GEOSYR53 0x0C05001B
+#define GEOSYR53CDT 0x0C05001C
+#define GEOSYR54 0x0C05001D
+#define GEOSYR54CDT 0x0C05001E
+#define TOWEL1 0x0C05001F
+#define TOWEL1CDT 0x0C050020
+#define TOWEL4 0x0C050021
+#define TOWEL4CDT 0x0C050022
+ // 35 entities in TXTs, 35 in datafiles.
+ // room53
+#define R53L0 0x0C060000
+#define R53PAL 0x0C060001
+#define BACK53PLX 0x0C060002
+#define FRONT53PLX 0x0C060003
+#define R53SPRPAL 0x0C060004
+ // 5 entities in TXTs, 5 in datafiles.
+ // room54
+#define R54L0 0x0C070000
+#define R54L1 0x0C070001
+#define R54G1 0x0C070002
+#define R54PAL 0x0C070003
+#define R54FLR 0x0C070004
+#define R54PLX 0x0C070005
+#define GEOSYR19 0x0C070006
+#define GEOSYR19CDT 0x0C070007
+#define GEOSYR20 0x0C070008
+#define GEOSYR20CDT 0x0C070009
+#define GEOSYR22 0x0C07000A
+#define GEOSYR22CDT 0x0C07000B
+#define GEOSYR23 0x0C07000C
+#define GEOSYR23CDT 0x0C07000D
+#define GEOSYR25 0x0C07000E
+#define GEOSYR25CDT 0x0C07000F
+#define GEOSYR26 0x0C070010
+#define GEOSYR26CDT 0x0C070011
+#define GEOSYR27 0x0C070012
+#define GEOSYR27CDT 0x0C070013
+#define GEOSYR37 0x0C070014
+#define GEOSYR37CDT 0x0C070015
+#define GEOSYR39 0x0C070016
+#define GEOSYR39CDT 0x0C070017
+#define GEOSYR40 0x0C070018
+#define GEOSYR40CDT 0x0C070019
+#define GEOSYR41 0x0C07001A
+#define GEOSYR41CDT 0x0C07001B
+#define GEOSYR42 0x0C07001C
+#define GEOSYR42CDT 0x0C07001D
+#define GEOSYR99 0x0C07001E
+#define GEOSYR99CDT 0x0C07001F
+#define GUN54 0x0C070020
+#define GUN54CDT 0x0C070021
+#define KHS10 0x0C070022
+#define KHS10CDT 0x0C070023
+#define KHS12 0x0C070024
+#define KHS12CDT 0x0C070025
+#define KHS5 0x0C070026
+#define KHS5CDT 0x0C070027
+#define KHS7 0x0C070028
+#define KHS7CDT 0x0C070029
+#define KHS8 0x0C07002A
+#define KHS8CDT 0x0C07002B
+#define KHS9 0x0C07002C
+#define KHS9CDT 0x0C07002D
+#define KHSTLK54 0x0C07002E
+#define KHSTLK54CDT 0x0C07002F
+#define SECDOR 0x0C070030
+#define SECDORCDT 0x0C070031
+#define STCKTREE 0x0C070032
+#define STCKTREECDT 0x0C070033
+#define STICKIN 0x0C070034
+#define STICKINCDT 0x0C070035
+#define XGEOSY21 0x0C070036
+#define XGEOSY21CDT 0x0C070037
+ // 56 entities in TXTs, 56 in datafiles.
+ // room55
+#define R55SPRPAL 0x0C080000
+#define R55L0 0x0C080001
+#define R55PAL 0x0C080002
+#define R55FLR 0x0C080003
+#define R55PLX 0x0C080004
+#define BODY55 0x0C080005
+#define BODY55CDT 0x0C080006
+#define BRITMAP 0x0C080007
+#define BRITMAP_PAL 0x0C080008
+#define DOOR55 0x0C080009
+#define DOOR55CDT 0x0C08000A
+#define DOOR55CDR 0x0C08000B
+#define GEOSY30T 0x0C08000C
+#define GEOSY30TCDT 0x0C08000D
+#define GEOSY30U 0x0C08000E
+#define GEOSY30UCDT 0x0C08000F
+#define GEOSYR29 0x0C080010
+#define GEOSYR29CDT 0x0C080011
+#define GEOSYR30 0x0C080012
+#define GEOSYR30CDT 0x0C080013
+#define GEOSYR31 0x0C080014
+#define GEOSYR31CDT 0x0C080015
+#define GEOSYR33 0x0C080016
+#define GEOSYR33CDT 0x0C080017
+#define GEOSYR34 0x0C080018
+#define GEOSYR34CDT 0x0C080019
+#define GEOSYR34CDR 0x0C08001A
+#define GEOSYR35 0x0C08001B
+#define GEOSYR35CDT 0x0C08001C
+#define GEOSYR56 0x0C08001D
+#define GEOSYR56CDT 0x0C08001E
+#define KHS2 0x0C08001F
+#define KHS2CDT 0x0C080020
+#define KHS3 0x0C080021
+#define KHS3CDT 0x0C080022
+#define KHS6 0x0C080023
+#define KHS6CDT 0x0C080024
+ // 37 entities in TXTs, 37 in datafiles.
+// train
+ // sound_fx
+#define FX_SHOCK63 0x0D000000
+#define FX_TRAINEXT 0x0D000001
+#define FX_TRAININT 0x0D000002
+#define FX_DOOR65 0x0D000003
+#define FX_WIND66 0x0D000004
+#define FX_WINDOW66 0x0D000005
+#define FX_BRAKES 0x0D000006
+#define FX_DOOR69 0x0D000007
+#define FX_EKSHOOT 0x0D000008
+#define FX_FIGHT69 0x0D000009
+#define FX_PNEUMO69 0x0D00000A
+#define FX_TICK69 0x0D00000B
+#define FX_TRNPASS 0x0D00000C
+ // 13 entities in TXTs, 13 in datafiles.
+ // room63
+#define TRAIN_PAL 0x0D010000
+#define R63L0 0x0D010001
+#define R63L1 0x0D010002
+#define R63G1 0x0D010003
+#define R63PAL 0x0D010004
+#define R63FLR 0x0D010005
+#define GEOTRAIN_MEGA 0x0D010006
+#define GEOTRAIN_WLK 0x0D010007
+#define BASHER63 0x0D010008
+#define BASHER63CDT 0x0D010009
+#define BOXDOR64 0x0D01000A
+#define BOXDOR64CDT 0x0D01000B
+#define BUSHA63 0x0D01000C
+#define BUSHA63CDT 0x0D01000D
+#define BUSHB63 0x0D01000E
+#define BUSHB63CDT 0x0D01000F
+#define BUSHC63 0x0D010010
+#define BUSHC63CDT 0x0D010011
+#define DOOR63C1 0x0D010012
+#define DOOR63C1CDT 0x0D010013
+#define DOOR63C1CDR 0x0D010014
+#define DOOR63C2 0x0D010015
+#define DOOR63C2CDT 0x0D010016
+#define DOOR63C2CDR 0x0D010017
+#define DOOR63C3 0x0D010018
+#define DOOR63C3CDT 0x0D010019
+#define DOOR63C3CDR 0x0D01001A
+#define GEOCLI64 0x0D01001B
+#define GEOCLI64CDT 0x0D01001C
+#define GEOJGL64 0x0D01001D
+#define GEOJGL64CDT 0x0D01001E
+#define GEOJGR64 0x0D01001F
+#define GEOJGR64CDT 0x0D010020
+#define GEOLAD64 0x0D010021
+#define GEOLAD64CDT 0x0D010022
+#define GEOSHK64 0x0D010023
+#define GEOSHK64CDT 0x0D010024
+#define GEOSTDL 0x0D010025
+#define GEOSTDLCDT 0x0D010026
+#define GEOSTDR 0x0D010027
+#define GEOSTDRCDT 0x0D010028
+#define GUIDOR63 0x0D010029
+#define GUIDOR63CDT 0x0D01002A
+#define GUIDSM63 0x0D01002B
+#define GUIDSM63CDT 0x0D01002C
+#define LADY63 0x0D01002D
+#define LADY63CDT 0x0D01002E
+#define LIGHT63A 0x0D01002F
+#define LIGHT63ACDT 0x0D010030
+#define LIGHT63B 0x0D010031
+#define LIGHT63BCDT 0x0D010032
+#define LIGHT63C 0x0D010033
+#define LIGHT63CCDT 0x0D010034
+#define LIGHT63D 0x0D010035
+#define LIGHT63DCDT 0x0D010036
+#define LIGHT63E 0x0D010037
+#define LIGHT63ECDT 0x0D010038
+#define NICO63 0x0D010039
+#define NICO63CDT 0x0D01003A
+#define POLE63 0x0D01003B
+#define POLE63CDT 0x0D01003C
+#define WHEELA63 0x0D01003D
+#define WHEELA63CDT 0x0D01003E
+#define WHEELB63 0x0D01003F
+#define WHEELB63CDT 0x0D010040
+#define WHEELC63 0x0D010041
+#define WHEELC63CDT 0x0D010042
+#define WHEELD63 0x0D010043
+#define WHEELD63CDT 0x0D010044
+#define WHEELE63 0x0D010045
+#define WHEELE63CDT 0x0D010046
+#define WHEELF63 0x0D010047
+#define WHEELF63CDT 0x0D010048
+ // 73 entities in TXTs, 73 in datafiles.
+ // room65
+#define R65L0 0x0D020000
+#define R65L1 0x0D020001
+#define R65G1 0x0D020002
+#define R65PAL 0x0D020003
+#define R65FLR 0x0D020004
+#define DOOR65 0x0D020005
+#define DOOR65CDT 0x0D020006
+#define DOOR65CDR 0x0D020007
+#define GEOGUA 0x0D020008
+#define GEOGUACDT 0x0D020009
+#define GEOLNK3 0x0D02000A
+#define GEOLNK3CDT 0x0D02000B
+#define GEONIC65 0x0D02000C
+#define GEONIC65CDT 0x0D02000D
+#define GEOSIT 0x0D02000E
+#define GEOSITCDT 0x0D02000F
+#define GEOSIT65 0x0D020010
+#define GEOSIT65CDT 0x0D020011
+#define GEOSTD65 0x0D020012
+#define GEOSTD65CDT 0x0D020013
+#define GEOSTD65CDR 0x0D020014
+#define GEOTIK 0x0D020015
+#define GEOTIKCDT 0x0D020016
+#define GEOTN652 0x0D020017
+#define GEOTN652CDT 0x0D020018
+#define GEOTN652CDR 0x0D020019
+#define GEOTRN65 0x0D02001A
+#define GEOTRN65CDT 0x0D02001B
+#define GEOTRN65CDR 0x0D02001C
+#define GUADOR65 0x0D02001D
+#define GUADOR65CDT 0x0D02001E
+#define GUALEA65 0x0D02001F
+#define GUALEA65CDT 0x0D020020
+#define GUATGN 0x0D020021
+#define GUATGNCDT 0x0D020022
+#define GUATGNCDR 0x0D020023
+#define GUATIK1 0x0D020024
+#define GUATIK1CDT 0x0D020025
+#define GUATIK2 0x0D020026
+#define GUATIK2CDT 0x0D020027
+#define GUATLK1 0x0D020028
+#define GUATLK1CDT 0x0D020029
+#define GUATLK2 0x0D02002A
+#define GUATLK2CDT 0x0D02002B
+#define NICGEO65 0x0D02002C
+#define NICGEO65CDT 0x0D02002D
+#define NICSIT65 0x0D02002E
+#define NICSIT65CDT 0x0D02002F
+#define NICTRN65 0x0D020030
+#define NICTRN65CDT 0x0D020031
+#define NICTRN65CDR 0x0D020032
+#define OLDREAD 0x0D020033
+#define OLDREADCDT 0x0D020034
+#define OLDREADCDR 0x0D020035
+#define OLDTIK1 0x0D020036
+#define OLDTIK1CDT 0x0D020037
+#define OLDTLK1 0x0D020038
+#define OLDTLK1CDT 0x0D020039
+ // 58 entities in TXTs, 58 in datafiles.
+ // room66
+#define R66L0 0x0D030000
+#define R66PAL 0x0D030001
+#define R66FLR 0x0D030002
+#define BASHBURP 0x0D030003
+#define BASHBURPCDT 0x0D030004
+#define BASHCAN 0x0D030005
+#define BASHCANCDT 0x0D030006
+#define BASHCLA 0x0D030007
+#define BASHCLACDT 0x0D030008
+#define BASHDRI 0x0D030009
+#define BASHDRICDT 0x0D03000A
+#define BASHSTD 0x0D03000B
+#define BASHSTDCDT 0x0D03000C
+#define BASHTLK1 0x0D03000D
+#define BASHTLK1CDT 0x0D03000E
+#define CAN66 0x0D03000F
+#define CAN66CDT 0x0D030010
+#define GEOLEA 0x0D030011
+#define GEOLEACDT 0x0D030012
+#define GEOOPN1 0x0D030013
+#define GEOOPN1CDT 0x0D030014
+#define GEOOUT66 0x0D030015
+#define GEOOUT66CDT 0x0D030016
+#define SLEEPY1 0x0D030017
+#define SLEEPY1CDT 0x0D030018
+#define SLEEPY1CDR 0x0D030019
+#define SLEEPY2 0x0D03001A
+#define SLEEPY2CDT 0x0D03001B
+ // 28 entities in TXTs, 28 in datafiles.
+ // room67
+#define R67L0 0x0D040000
+#define R67L1 0x0D040001
+#define R67G1 0x0D040002
+#define R67PAL 0x0D040003
+#define R67FLR 0x0D040004
+#define DOOR67 0x0D040005
+#define DOOR67CDT 0x0D040006
+#define DOOR67CDR 0x0D040007
+ // 8 entities in TXTs, 8 in datafiles.
+ // room69
+#define R69SPRPAL 0x0D050000
+#define R69L0 0x0D050001
+#define R69L1 0x0D050002
+#define R69G1 0x0D050003
+#define R69PAL 0x0D050004
+#define R69FLR 0x0D050005
+#define ASSDIE69 0x0D050006
+#define ASSDIE69CDT 0x0D050007
+#define ASSDWN69 0x0D050008
+#define ASSDWN69CDT 0x0D050009
+#define ASSLIFT 0x0D05000A
+#define ASSLIFTCDT 0x0D05000B
+#define ASSTLK69 0x0D05000C
+#define ASSTLK69CDT 0x0D05000D
+#define BOXES69 0x0D05000E
+#define BOXES69CDT 0x0D05000F
+#define EKLBOX69 0x0D050010
+#define EKLBOX69CDT 0x0D050011
+#define EKLGUN 0x0D050012
+#define EKLGUNCDT 0x0D050013
+#define EKLGUN69 0x0D050014
+#define EKLGUN69CDT 0x0D050015
+#define EKLTLK69 0x0D050016
+#define EKLTLK69CDT 0x0D050017
+#define EKLFIGHT 0x0D050018
+#define EKLFIGHTCDT 0x0D050019
+#define FIGHT69 0x0D05001A
+#define FIGHT69CDT 0x0D05001B
+#define GEODIE69 0x0D05001C
+#define GEODIE69CDT 0x0D05001D
+#define GEOENT69 0x0D05001E
+#define GEOENT69CDT 0x0D05001F
+#define GEOSTD69 0x0D050020
+#define GEOSTD69CDT 0x0D050021
+#define GEOSTP69 0x0D050022
+#define GEOSTP69CDT 0x0D050023
+#define GEOTLK69 0x0D050024
+#define GEOTLK69CDT 0x0D050025
+#define GEOUNTIE 0x0D050026
+#define GEOUNTIECDT 0x0D050027
+#define GEOXIT69 0x0D050028
+#define GEOXIT69CDT 0x0D050029
+#define NICTKB69 0x0D05002A
+#define NICTKB69CDT 0x0D05002B
+#define NICTLK69 0x0D05002C
+#define NICTLK69CDT 0x0D05002D
+#define NICTRN69 0x0D05002E
+#define NICTRN69CDT 0x0D05002F
+#define NICTRN69CDR 0x0D050030
+#define NICTUG69 0x0D050031
+#define NICTUG69CDT 0x0D050032
+#define NICWLK69 0x0D050033
+#define NICWLK69CDT 0x0D050034
+#define NICXIT69 0x0D050035
+#define NICXIT69CDT 0x0D050036
+#define RGTDOR69 0x0D050037
+#define RGTDOR69CDT 0x0D050038
+ // 57 entities in TXTs, 57 in datafiles.
+// scotland
+ // sound_fx
+#define FX_WIND71 0x0E000000
+#define FX_GUST71 0x0E000001
+#define FX_OWL71A 0x0E000002
+#define FX_OWL71B 0x0E000003
+#define FX_COG72A 0x0E000004
+#define FX_PING 0x0E000005
+#define FX_RUMMAGE1 0x0E000006
+#define FX_RUMMAGE2 0x0E000007
+#define FX_SECDOR72 0x0E000008
+#define FX_CHANT 0x0E000009
+#define FX_DAGGER1 0x0E00000A
+#define FX_GDROP73 0x0E00000B
+#define FX_GUNPOWDR 0x0E00000C
+#define FX_STAFF 0x0E00000D
+#define FX_TORCH73 0x0E00000E
+#define FX_BAPHAMB 0x0E00000F
+#define FX_FIGHT1 0x0E000010
+#define FX_REFORGE2 0x0E000011
+#define FX_ROSSODIE 0x0E000012
+#define FX_GKSWORD 0x0E000013
+#define FX_REFORGE1 0x0E000014
+#define FX_REFORGE4 0x0E000015
+#define FX_CHOKE1 0x0E000016
+#define FX_CHOKE2 0x0E000017
+#define FX_EKDIES 0x0E000018
+#define FX_FIGHT2 0x0E000019
+#define FX_GUN79 0x0E00001A
+ // 27 entities in TXTs, 27 in datafiles.
+ // room71
+#define R71L0 0x0E010000
+#define R71L1 0x0E010001
+#define R71G1 0x0E010002
+#define R71PAL 0x0E010003
+#define R71FLR 0x0E010004
+#define R71PLX 0x0E010005
+ // 6 entities in TXTs, 6 in datafiles.
+ // room72
+#define R72L0 0x0E020000
+#define R72L1 0x0E020001
+#define R72G1 0x0E020002
+#define R72PAL 0x0E020003
+#define R72FLR 0x0E020004
+#define COGL72 0x0E020005
+#define COGL72CDT 0x0E020006
+#define COGR72 0x0E020007
+#define COGR72CDT 0x0E020008
+#define GEOJMP72 0x0E020009
+#define GEOJMP72CDT 0x0E02000A
+#define GEOPIPE 0x0E02000B
+#define GEOPIPECDT 0x0E02000C
+#define GEOPIPECDR 0x0E02000D
+#define GEOPSH72 0x0E02000E
+#define GEOPSH72CDT 0x0E02000F
+#define GEOPUT72 0x0E020010
+#define GEOPUT72CDT 0x0E020011
+#define GEOTAK72 0x0E020012
+#define GEOTAK72CDT 0x0E020013
+#define GEOTAK72CDR 0x0E020014
+#define GEOTRY72 0x0E020015
+#define GEOTRY72CDT 0x0E020016
+#define GEOWIND2 0x0E020017
+#define GEOWIND2CDT 0x0E020018
+#define GEOWIND3 0x0E020019
+#define GEOWIND3CDT 0x0E02001A
+#define GEOWIND4 0x0E02001B
+#define GEOWIND4CDT 0x0E02001C
+#define GEOWND72 0x0E02001D
+#define GEOWND72CDT 0x0E02001E
+#define HANDLE72 0x0E02001F
+#define HANDLE72CDT 0x0E020020
+#define NICGEO72 0x0E020021
+#define NICGEO72CDT 0x0E020022
+#define NICJMP72 0x0E020023
+#define NICJMP72CDT 0x0E020024
+#define NICLNK72 0x0E020025
+#define NICLNK72CDT 0x0E020026
+#define NICLNK72CDR 0x0E020027
+#define NICOPY72 0x0E020028
+#define NICOPY72CDT 0x0E020029
+#define NICOPY72CDR 0x0E02002A
+#define NICTLK72 0x0E02002B
+#define NICTLK72CDT 0x0E02002C
+#define NOSE72 0x0E02002D
+#define NOSE72CDT 0x0E02002E
+#define PANEL72 0x0E02002F
+#define PANEL72CDT 0x0E020030
+#define PIPE72 0x0E020031
+#define PIPE72CDT 0x0E020032
+#define SPINDL72 0x0E020033
+#define SPINDL72CDT 0x0E020034
+#define WHEEL72 0x0E020035
+#define WHEEL72CDT 0x0E020036
+ // 55 entities in TXTs, 55 in datafiles.
+ // room73
+#define R73SPRPAL 0x0E030000
+#define R73L0 0x0E030001
+#define R73L1 0x0E030002
+#define R73L2 0x0E030003
+#define R73G1 0x0E030004
+#define R73G2 0x0E030005
+#define R73PAL 0x0E030006
+#define R73FLR 0x0E030007
+#define DAGGER1 0x0E030008
+#define DAGGER1CDT 0x0E030009
+#define DAGGER2 0x0E03000A
+#define DAGGER2CDT 0x0E03000B
+#define DARKWALL 0x0E03000C
+#define DARKWALLCDT 0x0E03000D
+#define FLAME73 0x0E03000E
+#define FLAME73CDT 0x0E03000F
+#define GEOCRCH 0x0E030010
+#define GEOCRCHCDT 0x0E030011
+#define GEOCREEP 0x0E030012
+#define GEOCREEPCDT 0x0E030013
+#define GEODIE1 0x0E030014
+#define GEODIE1CDT 0x0E030015
+#define GEODIE2 0x0E030016
+#define GEODIE2CDT 0x0E030017
+#define GEOTHROW 0x0E030018
+#define GEOTHROWCDT 0x0E030019
+#define GEOTLK73 0x0E03001A
+#define GEOTLK73CDT 0x0E03001B
+#define GEOTN73 0x0E03001C
+#define GEOTN73CDT 0x0E03001D
+#define GEOTN73CDR 0x0E03001E
+#define GMENTER 0x0E03001F
+#define GMENTERCDT 0x0E030020
+#define GMSPEAK 0x0E030021
+#define GMSPEAKCDT 0x0E030022
+#define GUIDLE 0x0E030023
+#define GUIDLECDT 0x0E030024
+#define GUITALK 0x0E030025
+#define GUITALKCDT 0x0E030026
+#define GUITHROW 0x0E030027
+#define GUITHROWCDT 0x0E030028
+#define GUITURN7 0x0E030029
+#define GUITURN7CDT 0x0E03002A
+#define GUITURN7CDR 0x0E03002B
+#define NICENT73 0x0E03002C
+#define NICENT73CDT 0x0E03002D
+#define NICEX73 0x0E03002E
+#define NICEX73CDT 0x0E03002F
+#define NICOTHRO 0x0E030030
+#define NICOTHROCDT 0x0E030031
+#define NICPANIC 0x0E030032
+#define NICPANICCDT 0x0E030033
+#define NICTLK73 0x0E030034
+#define NICTLK73CDT 0x0E030035
+#define NICTRN73 0x0E030036
+#define NICTRN73CDT 0x0E030037
+#define NICTRN73CDR 0x0E030038
+#define NITALK73 0x0E030039
+#define NITALK73CDT 0x0E03003A
+#define SPARKING 0x0E03003B
+#define SPARKINGCDT 0x0E03003C
+#define TORCH1 0x0E03003D
+#define TORCH1CDT 0x0E03003E
+#define TORCH2 0x0E03003F
+#define TORCH2CDT 0x0E030040
+#define TORCH3 0x0E030041
+#define TORCH3CDT 0x0E030042
+ // 67 entities in TXTs, 67 in datafiles.
+ // room74
+#define ENDSPRPAL 0x0E040000
+#define R74L0 0x0E040001
+#define R74L1 0x0E040002
+#define R74G1 0x0E040003
+#define R74PAL 0x0E040004
+#define R74APAL 0x0E040005
+#define R74BPAL 0x0E040006
+#define R74CPAL 0x0E040007
+#define R74DPAL 0x0E040008
+#define CHANT74 0x0E040009
+#define CHANT74CDT 0x0E04000A
+#define EKFIGHT 0x0E04000B
+#define EKFIGHTCDT 0x0E04000C
+#define EKGUN74 0x0E04000D
+#define EKGUN74CDT 0x0E04000E
+#define GEOFIGHT 0x0E04000F
+#define GEOFIGHTCDT 0x0E040010
+#define GEOLUK74 0x0E040011
+#define GEOLUK74CDT 0x0E040012
+#define GEOLUK74CDR 0x0E040013
+#define GEORCT74 0x0E040014
+#define GEORCT74CDT 0x0E040015
+#define GEORUN74 0x0E040016
+#define GEORUN74CDT 0x0E040017
+#define GEOSTND 0x0E040018
+#define GEOSTNDCDT 0x0E040019
+#define GEOTLK74 0x0E04001A
+#define GEOTLK74CDT 0x0E04001B
+#define GKKNEE 0x0E04001C
+#define GKKNEECDT 0x0E04001D
+#define GMGUN 0x0E04001E
+#define GMGUNCDT 0x0E04001F
+#define GMMOV1 0x0E040020
+#define GMMOV1CDT 0x0E040021
+#define GMMOV2 0x0E040022
+#define GMMOV2CDT 0x0E040023
+#define GMMOV3 0x0E040024
+#define GMMOV3CDT 0x0E040025
+#define GMMOV4 0x0E040026
+#define GMMOV4CDT 0x0E040027
+#define GMMOV5 0x0E040028
+#define GMMOV5CDT 0x0E040029
+#define GMMOV6 0x0E04002A
+#define GMMOV6CDT 0x0E04002B
+#define GMTLK1 0x0E04002C
+#define GMTLK1CDT 0x0E04002D
+#define GMTLK2 0x0E04002E
+#define GMTLK2CDT 0x0E04002F
+#define GMTLK3 0x0E040030
+#define GMTLK3CDT 0x0E040031
+#define GMTLK4 0x0E040032
+#define GMTLK4CDT 0x0E040033
+#define GMTLK5 0x0E040034
+#define GMTLK5CDT 0x0E040035
+#define GMTLK6 0x0E040036
+#define GMTLK6CDT 0x0E040037
+#define GMTLK7 0x0E040038
+#define GMTLK7CDT 0x0E040039
+#define GMTLK74 0x0E04003A
+#define GMTLK74CDT 0x0E04003B
+#define GMWLK 0x0E04003C
+#define GMWLKCDT 0x0E04003D
+#define GMWRIT74 0x0E04003E
+#define GMWRIT74CDT 0x0E04003F
+#define MONKFALL 0x0E040040
+#define MONKFALLCDT 0x0E040041
+#define MONKRISE 0x0E040042
+#define MONKRISECDT 0x0E040043
+#define MONKSTRS 0x0E040044
+#define MONKSTRSCDT 0x0E040045
+#define MONKTURN 0x0E040046
+#define MONKTURNCDT 0x0E040047
+#define NICFIGHT 0x0E040048
+#define NICFIGHTCDT 0x0E040049
+#define NICLUK74 0x0E04004A
+#define NICLUK74CDT 0x0E04004B
+#define NICLUK74CDR 0x0E04004C
+#define NICRCVR 0x0E04004D
+#define NICRCVRCDT 0x0E04004E
+#define NICRUN74 0x0E04004F
+#define NICRUN74CDT 0x0E040050
+#define NICSTND 0x0E040051
+#define NICSTNDCDT 0x0E040052
+#define NICTLK74 0x0E040053
+#define NICTLK74CDT 0x0E040054
+#define ROSSFALL 0x0E040055
+#define ROSSFALLCDT 0x0E040056
+#define ROSSTAIR 0x0E040057
+#define ROSSTAIRCDT 0x0E040058
+#define ROSSTND 0x0E040059
+#define ROSSTNDCDT 0x0E04005A
+#define ROSSWLK 0x0E04005B
+#define ROSSWLKCDT 0x0E04005C
+#define RUBBLE 0x0E04005D
+#define RUBBLECDT 0x0E04005E
+#define STONLIT1 0x0E04005F
+#define STONLIT1CDT 0x0E040060
+#define STONLIT2 0x0E040061
+#define STONLIT2CDT 0x0E040062
+#define STONWHT1 0x0E040063
+#define STONWHT1CDT 0x0E040064
+#define STONWHT2 0x0E040065
+#define STONWHT2CDT 0x0E040066
+ // 103 entities in TXTs, 103 in datafiles.
+ // room75
+#define R75L0 0x0E050000
+#define R75PAL 0x0E050001
+#define R75APAL 0x0E050002
+#define R75BPAL 0x0E050003
+#define R75CPAL 0x0E050004
+#define R75DPAL 0x0E050005
+#define EKGUN 0x0E050006
+#define EKGUNCDT 0x0E050007
+#define EKGUN2 0x0E050008
+#define EKGUN2CDT 0x0E050009
+#define EKLITE 0x0E05000A
+#define EKLITECDT 0x0E05000B
+#define EKRELAX 0x0E05000C
+#define EKRELAXCDT 0x0E05000D
+#define EKTLK75 0x0E05000E
+#define EKTLK75CDT 0x0E05000F
+#define GEOLUK75 0x0E050010
+#define GEOLUK75CDT 0x0E050011
+#define GEOLUK75CDR 0x0E050012
+#define GTMP 0x0E050013
+#define GTMPCDT 0x0E050014
+#define NICLUK75 0x0E050015
+#define NICLUK75CDT 0x0E050016
+#define NICLUK75CDR 0x0E050017
+#define NTMP 0x0E050018
+#define NTMPCDT 0x0E050019
+#define TORCH75 0x0E05001A
+#define TORCH75CDT 0x0E05001B
+ // 28 entities in TXTs, 28 in datafiles.
+ // room76
+#define R76L0 0x0E060000
+#define R76PAL 0x0E060001
+#define CHANT76 0x0E060002
+#define CHANT76CDT 0x0E060003
+#define EKGUN76 0x0E060004
+#define EKGUN76CDT 0x0E060005
+#define GKBAK 0x0E060006
+#define GKBAKCDT 0x0E060007
+#define GKKNEE76 0x0E060008
+#define GKKNEE76CDT 0x0E060009
+#define GKLOWER 0x0E06000A
+#define GKLOWERCDT 0x0E06000B
+#define GKSWORD 0x0E06000C
+#define GKSWORDCDT 0x0E06000D
+#define GKTLK 0x0E06000E
+#define GKTLKCDT 0x0E06000F
+#define GKTLK2 0x0E060010
+#define GKTLK2CDT 0x0E060011
+#define GMARMS 0x0E060012
+#define GMARMSCDT 0x0E060013
+#define GMSTONES 0x0E060014
+#define GMSTONESCDT 0x0E060015
+#define GMTLK76 0x0E060016
+#define GMTLK76CDT 0x0E060017
+#define GMTRN76 0x0E060018
+#define GMTRN76CDT 0x0E060019
+#define LSTONE76 0x0E06001A
+#define LSTONE76CDT 0x0E06001B
+#define MONKNEE 0x0E06001C
+#define MONKNEECDT 0x0E06001D
+#define ROSSTND76 0x0E06001E
+#define ROSSTND76CDT 0x0E06001F
+#define RSTONE76 0x0E060020
+#define RSTONE76CDT 0x0E060021
+ // 34 entities in TXTs, 34 in datafiles.
+ // room77
+#define R77L0 0x0E070000
+#define R77PAL 0x0E070001
+#define CHANT77 0x0E070002
+#define CHANT77CDT 0x0E070003
+#define GEOPEER 0x0E070004
+#define GEOPEERCDT 0x0E070005
+#define GMHEAD 0x0E070006
+#define GMHEADCDT 0x0E070007
+#define NICPEER 0x0E070008
+#define NICPEERCDT 0x0E070009
+#define NICTLK77 0x0E07000A
+#define NICTLK77CDT 0x0E07000B
+#define ROSLUK77 0x0E07000C
+#define ROSLUK77CDT 0x0E07000D
+ // 14 entities in TXTs, 14 in datafiles.
+ // room78
+#define R78L0 0x0E080000
+#define R78PAL 0x0E080001
+#define R78APAL 0x0E080002
+#define R78BPAL 0x0E080003
+#define R78CPAL 0x0E080004
+#define R78DPAL 0x0E080005
+#define R78EPAL 0x0E080006
+#define R78FPAL 0x0E080007
+#define LSTONE78 0x0E080008
+#define LSTONE78CDT 0x0E080009
+#define LSTONE78CDR 0x0E08000A
+#define GMPOWER 0x0E08000B
+#define GMPOWERCDT 0x0E08000C
+#define GMWRITH 0x0E08000D
+#define GMWRITHCDT 0x0E08000E
+#define RSTONE78 0x0E08000F
+#define RSTONE78CDT 0x0E080010
+#define RSTONE78CDR 0x0E080011
+ // 18 entities in TXTs, 18 in datafiles.
+ // room79
+#define R79L0 0x0E090000
+#define R79PAL 0x0E090001
+#define EKSTD79 0x0E090002
+#define EKSTD79CDT 0x0E090003
+#define FIGHT79 0x0E090004
+#define FIGHT79CDT 0x0E090005
+#define GEOANG79 0x0E090006
+#define GEOANG79CDT 0x0E090007
+#define GEOTLK79 0x0E090008
+#define GEOTLK79CDT 0x0E090009
+#define NICSTD79 0x0E09000A
+#define NICSTD79CDT 0x0E09000B
+#define ROSENT79 0x0E09000C
+#define ROSENT79CDT 0x0E09000D
+#define ROSSHOT 0x0E09000E
+#define ROSSHOTCDT 0x0E09000F
+#define ROSTLK79 0x0E090010
+#define ROSTLK79CDT 0x0E090011
+ // 18 entities in TXTs, 18 in datafiles.
+
+} // End of namespace Sword1
+
+#endif //SWORDRES_H
diff --git a/engines/sword1/text.cpp b/engines/sword1/text.cpp
new file mode 100644
index 0000000000..d7e14073e9
--- /dev/null
+++ b/engines/sword1/text.cpp
@@ -0,0 +1,191 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "sword1/text.h"
+#include "sword1/resman.h"
+#include "sword1/objectman.h"
+#include "common/util.h"
+#include "sword1/swordres.h"
+#include "sword1/sworddefs.h"
+
+namespace Sword1 {
+
+#define OVERLAP 3
+#define SPACE ' '
+#define BORDER_COL 200
+#define LETTER_COL 193
+#define NO_COL 0 // sprite background - 0 for transparency
+#define MAX_LINES 30
+
+
+Text::Text(ObjectMan *pObjMan, ResMan *pResMan, bool czechVersion) {
+ _objMan = pObjMan;
+ _resMan = pResMan;
+ _textCount = 0;
+ _fontId = (czechVersion) ? CZECH_GAME_FONT : GAME_FONT;
+ _font = (uint8*)_resMan->openFetchRes(_fontId);
+
+ _joinWidth = charWidth( SPACE ) - 2 * OVERLAP;
+ _charHeight = FROM_LE_16(_resMan->fetchFrame(_font, 0)->height); // all chars have the same height
+ _textBlocks[0] = _textBlocks[1] = NULL;
+}
+
+Text::~Text(void) {
+ if (_textBlocks[0])
+ free(_textBlocks[0]);
+ if (_textBlocks[1])
+ free(_textBlocks[1]);
+ //_resMan->resClose(_fontId); => wiped automatically by _resMan->flush();
+}
+
+uint32 Text::lowTextManager(uint8 *ascii, int32 width, uint8 pen) {
+ _textCount++;
+ if (_textCount > MAX_TEXT_OBS)
+ error("Text::lowTextManager: MAX_TEXT_OBS exceeded!");
+ uint32 textObjId = (TEXT_sect * ITM_PER_SEC) - 1;
+ do {
+ textObjId++;
+ } while (_objMan->fetchObject(textObjId)->o_status);
+ // okay, found a free text object
+
+ _objMan->fetchObject(textObjId)->o_status = STAT_FORE;
+ makeTextSprite((uint8)textObjId, ascii, (uint16)width, pen);
+
+ return textObjId;
+}
+
+void Text::makeTextSprite(uint8 slot, uint8 *text, uint16 maxWidth, uint8 pen) {
+ LineInfo lines[MAX_LINES];
+ uint16 numLines = analyzeSentence(text, maxWidth, lines);
+
+ uint16 sprWidth = 0;
+ uint16 lineCnt;
+ for (lineCnt = 0; lineCnt < numLines; lineCnt++)
+ if (lines[lineCnt].width > sprWidth)
+ sprWidth = lines[lineCnt].width;
+ uint16 sprHeight = _charHeight * numLines;
+ uint32 sprSize = sprWidth * sprHeight;
+ assert(!_textBlocks[slot]); // if this triggers, the speechDriver failed to call Text::releaseText.
+ _textBlocks[slot] = (FrameHeader*)malloc(sprSize + sizeof(FrameHeader));
+
+ memcpy( _textBlocks[slot]->runTimeComp, "Nu ", 4);
+ _textBlocks[slot]->compSize = 0;
+ _textBlocks[slot]->width = TO_LE_16(sprWidth);
+ _textBlocks[slot]->height = TO_LE_16(sprHeight);
+ _textBlocks[slot]->offsetX = 0;
+ _textBlocks[slot]->offsetY = 0;
+
+ uint8 *linePtr = ((uint8*)_textBlocks[slot]) + sizeof(FrameHeader);
+ memset(linePtr, NO_COL, sprSize);
+ for (lineCnt = 0; lineCnt < numLines; lineCnt++) {
+ uint8 *sprPtr = linePtr + (sprWidth - lines[lineCnt].width) / 2; // center the text
+ for (uint16 pos = 0; pos < lines[lineCnt].length; pos++)
+ sprPtr += copyChar(*text++, sprPtr, sprWidth, pen) - OVERLAP;
+ text++; // skip space at the end of the line
+ linePtr += _charHeight * sprWidth;
+ }
+}
+
+uint16 Text::charWidth(uint8 ch) {
+ if (ch < SPACE)
+ ch = 64;
+ return FROM_LE_16(_resMan->fetchFrame(_font, ch - SPACE)->width);
+}
+
+uint16 Text::analyzeSentence(uint8 *text, uint16 maxWidth, LineInfo *line) {
+ uint16 lineNo = 0;
+
+ bool firstWord = true;
+ while (*text) {
+ uint16 wordWidth = 0;
+ uint16 wordLength = 0;
+
+ while ((*text != SPACE) && *text) {
+ wordWidth += charWidth(*text) - OVERLAP;
+ wordLength++;
+ text++;
+ }
+ if (*text == SPACE)
+ text++;
+
+ wordWidth += OVERLAP; // no overlap on final letter of word!
+ if ( firstWord ) { // first word on first line, so no separating SPACE needed
+ line[0].width = wordWidth;
+ line[0].length = wordLength;
+ firstWord = false;
+ } else {
+ // see how much extra space this word will need to fit on current line
+ // (with a separating space character - also overlapped)
+ uint16 spaceNeeded = _joinWidth + wordWidth;
+
+ if (line[lineNo].width + spaceNeeded <= maxWidth ) {
+ line[lineNo].width += spaceNeeded;
+ line[lineNo].length += 1 + wordLength; // NB. space+word characters
+ } else { // put word (without separating SPACE) at start of next line
+ lineNo++;
+ assert( lineNo < MAX_LINES );
+ line[lineNo].width = wordWidth;
+ line[lineNo].length = wordLength;
+ }
+ }
+ }
+ return lineNo+1; // return no of lines
+}
+
+uint16 Text::copyChar(uint8 ch, uint8 *sprPtr, uint16 sprWidth, uint8 pen) {
+ FrameHeader *chFrame = _resMan->fetchFrame(_font, ch - SPACE);
+ uint8 *chData = ((uint8*)chFrame) + sizeof(FrameHeader);
+ uint8 *dest = sprPtr;
+ for (uint16 cnty = 0; cnty < FROM_LE_16(chFrame->height); cnty++) {
+ for (uint16 cntx = 0; cntx < FROM_LE_16(chFrame->width); cntx++) {
+ if (*chData == LETTER_COL)
+ dest[cntx] = pen;
+ else if ((*chData == BORDER_COL) && (!dest[cntx])) // don't do a border if there's already a color underneath (chars can overlap)
+ dest[cntx] = BORDER_COL;
+ chData++;
+ }
+ dest += sprWidth;
+ }
+ return FROM_LE_16(chFrame->width);
+}
+
+FrameHeader *Text::giveSpriteData(uint32 textTarget) {
+ // textTarget is the resource ID of the Compact linking the textdata.
+ // that's 0x950000 for slot 0 and 0x950001 for slot 1. easy, huh? :)
+ textTarget &= ITM_ID;
+ assert(textTarget <= 1);
+
+ return _textBlocks[textTarget];
+}
+
+void Text::releaseText(uint32 id) {
+ id &= ITM_ID;
+ assert(id <= 1);
+ if (_textBlocks[id]) {
+ free(_textBlocks[id]);
+ _textBlocks[id] = NULL;
+ _textCount--;
+ }
+}
+
+} // End of namespace Sword1
diff --git a/engines/sword1/text.h b/engines/sword1/text.h
new file mode 100644
index 0000000000..34b106518a
--- /dev/null
+++ b/engines/sword1/text.h
@@ -0,0 +1,65 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef BSTEXT_H
+#define BSTEXT_H
+
+#include "sword1/object.h"
+#include "sword1/sworddefs.h"
+
+namespace Sword1 {
+
+#define MAX_TEXT_OBS 2
+
+class ObjectMan;
+class ResMan;
+
+struct LineInfo {
+ uint16 width; // width of line in pixels
+ uint16 length; // length of line in characters
+};
+
+class Text {
+public:
+ Text(ObjectMan *pObjMan, ResMan *pResMan, bool czechVersion);
+ ~Text(void);
+ FrameHeader *giveSpriteData(uint32 textTarget);
+ uint32 lowTextManager(uint8 *text, int32 width, uint8 pen);
+ void releaseText(uint32 id);
+
+private:
+ void makeTextSprite(uint8 slot, uint8 *text, uint16 maxWidth, uint8 pen);
+ uint16 analyzeSentence(uint8 *text, uint16 maxWidth, LineInfo *info);
+ uint16 charWidth(uint8 ch);
+ uint16 copyChar(uint8 ch, uint8 *sprPtr, uint16 sprWidth, uint8 pen);
+ uint8 *_font;
+ uint8 _textCount;
+ uint16 _charHeight, _joinWidth;
+ ObjectMan *_objMan;
+ ResMan *_resMan;
+ FrameHeader *_textBlocks[MAX_TEXT_OBS];
+ uint32 _fontId;
+};
+
+} // End of namespace Sword1
+
+#endif //BSTEXT_H
diff --git a/engines/sword2/_mouse.cpp b/engines/sword2/_mouse.cpp
new file mode 100644
index 0000000000..db0cf00f73
--- /dev/null
+++ b/engines/sword2/_mouse.cpp
@@ -0,0 +1,247 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/stream.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/mouse.h"
+
+namespace Sword2 {
+
+// This is the maximum mouse cursor size in the SDL backend
+#define MAX_MOUSE_W 80
+#define MAX_MOUSE_H 80
+
+#define MOUSEFLASHFRAME 6
+
+void Mouse::decompressMouse(byte *decomp, byte *comp, uint8 frame, int width, int height, int pitch, int xOff, int yOff) {
+ int32 size = width * height;
+ int32 i = 0;
+ int x = 0;
+ int y = 0;
+
+ comp = comp + READ_LE_UINT32(comp + frame * 4) - MOUSE_ANIM_HEADER_SIZE;
+
+ while (i < size) {
+ if (*comp > 183) {
+ decomp[(y + yOff) * pitch + x + xOff] = *comp++;
+ if (++x >= width) {
+ x = 0;
+ y++;
+ }
+ i++;
+ } else {
+ x += *comp;
+ while (x >= width) {
+ y++;
+ x -= width;
+ }
+ i += *comp++;
+ }
+ }
+}
+
+void Mouse::drawMouse() {
+ byte mouseData[MAX_MOUSE_W * MAX_MOUSE_H];
+
+ if (!_mouseAnim.data && !_luggageAnim.data)
+ return;
+
+ // When an object is used in the game, the mouse cursor should be a
+ // combination of a standard mouse cursor and a luggage cursor.
+ //
+ // However, judging by the original code luggage cursors can also
+ // appear on their own. I have no idea which cases though.
+
+ uint16 mouse_width = 0;
+ uint16 mouse_height = 0;
+ uint16 hotspot_x = 0;
+ uint16 hotspot_y = 0;
+ int deltaX = 0;
+ int deltaY = 0;
+
+ if (_mouseAnim.data) {
+ hotspot_x = _mouseAnim.xHotSpot;
+ hotspot_y = _mouseAnim.yHotSpot;
+ mouse_width = _mouseAnim.mousew;
+ mouse_height = _mouseAnim.mouseh;
+ }
+
+ if (_luggageAnim.data) {
+ if (!_mouseAnim.data) {
+ hotspot_x = _luggageAnim.xHotSpot;
+ hotspot_y = _luggageAnim.yHotSpot;
+ }
+ if (_luggageAnim.mousew > mouse_width)
+ mouse_width = _luggageAnim.mousew;
+ if (_luggageAnim.mouseh > mouse_height)
+ mouse_height = _luggageAnim.mouseh;
+ }
+
+ if (_mouseAnim.data && _luggageAnim.data) {
+ deltaX = _mouseAnim.xHotSpot - _luggageAnim.xHotSpot;
+ deltaY = _mouseAnim.yHotSpot - _luggageAnim.yHotSpot;
+ }
+
+ assert(deltaX >= 0);
+ assert(deltaY >= 0);
+
+ // HACK for maximum cursor size. (The SDL backend imposes this
+ // restriction)
+
+ if (mouse_width + deltaX > MAX_MOUSE_W)
+ deltaX = 80 - mouse_width;
+ if (mouse_height + deltaY > MAX_MOUSE_H)
+ deltaY = 80 - mouse_height;
+
+ mouse_width += deltaX;
+ mouse_height += deltaY;
+
+ if ((uint32)(mouse_width * mouse_height) > sizeof(mouseData)) {
+ warning("Mouse cursor too large");
+ return;
+ }
+
+ memset(mouseData, 0, mouse_width * mouse_height);
+
+ if (_luggageAnim.data)
+ decompressMouse(mouseData, _luggageAnim.data, 0,
+ _luggageAnim.mousew, _luggageAnim.mouseh,
+ mouse_width, deltaX, deltaY);
+
+ if (_mouseAnim.data)
+ decompressMouse(mouseData, _mouseAnim.data, _mouseFrame,
+ _mouseAnim.mousew, _mouseAnim.mouseh, mouse_width);
+
+ _vm->_system->setMouseCursor(mouseData, mouse_width, mouse_height, hotspot_x, hotspot_y, 0);
+}
+
+/**
+ * Animates the current mouse pointer
+ */
+
+int32 Mouse::animateMouse() {
+ uint8 prevMouseFrame = _mouseFrame;
+
+ if (!_mouseAnim.data)
+ return RDERR_UNKNOWN;
+
+ if (++_mouseFrame == _mouseAnim.noAnimFrames)
+ _mouseFrame = MOUSEFLASHFRAME;
+
+ if (_mouseFrame != prevMouseFrame)
+ drawMouse();
+
+ return RD_OK;
+}
+
+/**
+ * Sets the mouse cursor animation.
+ * @param ma a pointer to the animation data, or NULL to clear the current one
+ * @param size the size of the mouse animation data
+ * @param mouseFlash RDMOUSE_FLASH or RDMOUSE_NOFLASH, depending on whether
+ * or not there is a lead-in animation
+ */
+
+int32 Mouse::setMouseAnim(byte *ma, int32 size, int32 mouseFlash) {
+ free(_mouseAnim.data);
+ _mouseAnim.data = NULL;
+
+ if (ma) {
+ if (mouseFlash == RDMOUSE_FLASH)
+ _mouseFrame = 0;
+ else
+ _mouseFrame = MOUSEFLASHFRAME;
+
+ Common::MemoryReadStream readS(ma, size);
+
+ _mouseAnim.runTimeComp = readS.readByte();
+ _mouseAnim.noAnimFrames = readS.readByte();
+ _mouseAnim.xHotSpot = readS.readSByte();
+ _mouseAnim.yHotSpot = readS.readSByte();
+ _mouseAnim.mousew = readS.readByte();
+ _mouseAnim.mouseh = readS.readByte();
+
+ _mouseAnim.data = (byte *)malloc(size - MOUSE_ANIM_HEADER_SIZE);
+ if (!_mouseAnim.data)
+ return RDERR_OUTOFMEMORY;
+
+ readS.read(_mouseAnim.data, size - MOUSE_ANIM_HEADER_SIZE);
+
+ animateMouse();
+ drawMouse();
+
+ _vm->_system->showMouse(true);
+ } else {
+ if (_luggageAnim.data)
+ drawMouse();
+ else
+ _vm->_system->showMouse(false);
+ }
+
+ return RD_OK;
+}
+
+/**
+ * Sets the "luggage" animation to accompany the mouse animation. Luggage
+ * sprites are of the same format as mouse sprites.
+ * @param ma a pointer to the animation data, or NULL to clear the current one
+ * @param size the size of the animation data
+ */
+
+int32 Mouse::setLuggageAnim(byte *ma, int32 size) {
+ free(_luggageAnim.data);
+ _luggageAnim.data = NULL;
+
+ if (ma) {
+ Common::MemoryReadStream readS(ma, size);
+
+ _luggageAnim.runTimeComp = readS.readByte();
+ _luggageAnim.noAnimFrames = readS.readByte();
+ _luggageAnim.xHotSpot = readS.readSByte();
+ _luggageAnim.yHotSpot = readS.readSByte();
+ _luggageAnim.mousew = readS.readByte();
+ _luggageAnim.mouseh = readS.readByte();
+
+ _luggageAnim.data = (byte *)malloc(size - MOUSE_ANIM_HEADER_SIZE);
+ if (!_luggageAnim.data)
+ return RDERR_OUTOFMEMORY;
+
+ readS.read(_luggageAnim.data, size - MOUSE_ANIM_HEADER_SIZE);
+
+ animateMouse();
+ drawMouse();
+
+ _vm->_system->showMouse(true);
+ } else {
+ if (_mouseAnim.data)
+ drawMouse();
+ else
+ _vm->_system->showMouse(false);
+ }
+
+ return RD_OK;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp
new file mode 100644
index 0000000000..2ba5df6792
--- /dev/null
+++ b/engines/sword2/animation.cpp
@@ -0,0 +1,535 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "sound/vorbis.h"
+#include "sound/mp3.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/maketext.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+#include "sword2/animation.h"
+
+namespace Sword2 {
+
+AnimationState::AnimationState(Sword2Engine *vm)
+ : BaseAnimationState(vm->_mixer, vm->_system, 640, 480), _vm(vm) {
+}
+
+AnimationState::~AnimationState() {
+}
+
+#ifdef BACKEND_8BIT
+
+void AnimationState::setPalette(byte *pal) {
+ _vm->_screen->setPalette(0, 256, pal, RDPAL_INSTANT);
+}
+
+#else
+
+void AnimationState::drawTextObject(SpriteInfo *s, byte *src) {
+ OverlayColor *dst = _overlay + RENDERWIDE * s->y + s->x;
+
+ // FIXME: These aren't the "right" colours, but look good to me.
+
+ OverlayColor pen = _sys->RGBToColor(255, 255, 255);
+ OverlayColor border = _sys->RGBToColor(0, 0, 0);
+
+ for (int y = 0; y < s->h; y++) {
+ for (int x = 0; x < s->w; x++) {
+ switch (src[x]) {
+ case 1:
+ dst[x] = border;
+ break;
+ case 255:
+ dst[x] = pen;
+ break;
+ default:
+ break;
+ }
+ }
+ dst += RENDERWIDE;
+ src += s->w;
+ }
+}
+
+#endif
+
+void AnimationState::clearScreen() {
+#ifdef BACKEND_8BIT
+ memset(_vm->_screen->getScreen(), 0, _movieWidth * _movieHeight);
+#else
+ OverlayColor black = _sys->RGBToColor(0, 0, 0);
+
+ for (int i = 0; i < _movieWidth * _movieHeight; i++)
+ _overlay[i] = black;
+#endif
+}
+
+void AnimationState::updateScreen() {
+#ifdef BACKEND_8BIT
+ byte *buf = _vm->_screen->getScreen() + ((480 - _movieHeight) / 2) * RENDERWIDE + (640 - _movieWidth) / 2;
+
+ _vm->_system->copyRectToScreen(buf, _movieWidth, (640 - _movieWidth) / 2, (480 - _movieHeight) / 2, _movieWidth, _movieHeight);
+#else
+ _sys->copyRectToOverlay(_overlay, _movieWidth, 0, 0, _movieWidth, _movieHeight);
+#endif
+ _vm->_system->updateScreen();
+}
+
+void AnimationState::drawYUV(int width, int height, byte *const *dat) {
+#ifdef BACKEND_8BIT
+ _vm->_screen->plotYUV(_lut, width, height, dat);
+#else
+ plotYUV(width, height, dat);
+#endif
+}
+
+MovieInfo MoviePlayer::_movies[] = {
+ { "carib", 222, false },
+ { "escape", 187, false },
+ { "eye", 248, false },
+ { "finale", 1485, false },
+ { "guard", 75, false },
+ { "intro", 1800, false },
+ { "jungle", 186, false },
+ { "museum", 167, false },
+ { "pablo", 75, false },
+ { "pyramid", 60, false },
+ { "quaram", 184, false },
+ { "river", 656, false },
+ { "sailing", 138, false },
+ { "shaman", 788, true },
+ { "stone1", 34, false },
+ { "stone2", 282, false },
+ { "stone3", 65, false },
+ { "demo", 60, false },
+ { "enddemo", 110, false }
+};
+
+MoviePlayer::MoviePlayer(Sword2Engine *vm)
+ : _vm(vm), _snd(_vm->_mixer), _sys(_vm->_system), _textSurface(NULL) {
+}
+
+void MoviePlayer::openTextObject(MovieTextObject *obj) {
+ if (obj->textSprite)
+ _vm->_screen->createSurface(obj->textSprite, &_textSurface);
+}
+
+void MoviePlayer::closeTextObject(MovieTextObject *obj) {
+ if (_textSurface) {
+ _vm->_screen->deleteSurface(_textSurface);
+ _textSurface = NULL;
+ }
+}
+
+void MoviePlayer::drawTextObject(AnimationState *anim, MovieTextObject *obj) {
+ if (obj->textSprite && _textSurface) {
+#ifdef BACKEND_8BIT
+ _vm->_screen->drawSurface(obj->textSprite, _textSurface);
+#else
+ if (anim)
+ anim->drawTextObject(obj->textSprite, _textSurface);
+ else
+ _vm->_screen->drawSurface(obj->textSprite, _textSurface);
+#endif
+ }
+}
+
+/**
+ * Plays an animated cutscene.
+ * @param filename the file name of the cutscene file
+ * @param text the subtitles and voiceovers for the cutscene
+ * @param leadInRes lead-in music resource id
+ * @param leadOutRes lead-out music resource id
+ */
+
+int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], int32 leadInRes, int32 leadOutRes) {
+ Audio::SoundHandle leadInHandle;
+
+ // This happens if the user quits during the "eye" smacker
+ if (_vm->_quit)
+ return RD_OK;
+
+ if (leadInRes) {
+ byte *leadIn = _vm->_resman->openResource(leadInRes);
+ uint32 leadInLen = _vm->_resman->fetchLen(leadInRes) - ResHeader::size();
+
+ assert(_vm->_resman->fetchType(leadIn) == WAV_FILE);
+
+ leadIn += ResHeader::size();
+
+ _vm->_sound->playFx(&leadInHandle, leadIn, leadInLen, Audio::Mixer::kMaxChannelVolume, 0, false, Audio::Mixer::kMusicSoundType);
+ }
+
+ byte *leadOut = NULL;
+ uint32 leadOutLen = 0;
+
+ if (leadOutRes) {
+ leadOut = _vm->_resman->openResource(leadOutRes);
+ leadOutLen = _vm->_resman->fetchLen(leadOutRes) - ResHeader::size();
+
+ assert(_vm->_resman->fetchType(leadOut) == WAV_FILE);
+
+ leadOut += ResHeader::size();
+ }
+
+ _leadOutFrame = (uint)-1;
+
+ int i;
+
+ for (i = 0; i < ARRAYSIZE(_movies); i++) {
+ if (scumm_stricmp(filename, _movies[i].name) == 0) {
+ _seamless = _movies[i].seamless;
+ if (_movies[i].frames > 60)
+ _leadOutFrame = _movies[i].frames - 60;
+ break;
+ }
+ }
+
+ if (i == ARRAYSIZE(_movies))
+ warning("Unknown movie, '%s'", filename);
+
+#ifdef USE_MPEG2
+ playMPEG(filename, text, leadOut, leadOutLen);
+#else
+ // No MPEG2? Use the old 'Narration Only' hack
+ playDummy(filename, text, leadOut, leadOutLen);
+#endif
+
+ _snd->stopHandle(leadInHandle);
+
+ // Wait for the lead-out to stop, if there is any. Though don't do it
+ // for seamless movies, since they are meant to blend into the rest of
+ // the game.
+
+ if (!_seamless) {
+ while (_vm->_mixer->isSoundHandleActive(_leadOutHandle)) {
+ _vm->_screen->updateDisplay();
+ _vm->_system->delayMillis(30);
+ }
+ }
+
+ if (leadInRes)
+ _vm->_resman->closeResource(leadInRes);
+
+ if (leadOutRes)
+ _vm->_resman->closeResource(leadOutRes);
+
+ return RD_OK;
+}
+
+void MoviePlayer::playMPEG(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen) {
+ uint frameCounter = 0, textCounter = 0;
+ Audio::SoundHandle handle;
+ bool skipCutscene = false, textVisible = false;
+ uint32 flags = Audio::Mixer::FLAG_16BITS;
+ bool startNextText = false;
+
+ byte oldPal[256 * 4];
+ memcpy(oldPal, _vm->_screen->getPalette(), sizeof(oldPal));
+
+ AnimationState *anim = new AnimationState(_vm);
+
+ if (!anim->init(filename)) {
+ delete anim;
+ // Missing Files? Use the old 'Narration Only' hack
+ playDummy(filename, text, leadOut, leadOutLen);
+ return;
+ }
+
+ // Clear the screen, because whatever is on it will be visible when the
+ // overlay is removed. And if there isn't an overlay, we don't want it
+ // to be visible during the cutscene. (Not all cutscenes cover the
+ // entire screen.)
+ _vm->_screen->clearScene();
+ _vm->_screen->updateDisplay();
+
+#ifndef SCUMM_BIG_ENDIAN
+ flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+
+ while (!skipCutscene && anim->decodeFrame()) {
+ // The frame has been drawn. Now draw the subtitles, if any,
+ // before updating the screen.
+
+ if (text && text[textCounter]) {
+ if (frameCounter == text[textCounter]->startFrame) {
+ openTextObject(text[textCounter]);
+ textVisible = true;
+
+ if (text[textCounter]->speech) {
+ startNextText = true;
+ }
+ }
+
+ if (startNextText && !_snd->isSoundHandleActive(handle)) {
+ _snd->playRaw(&handle, text[textCounter]->speech, text[textCounter]->speechBufferSize, 22050, flags);
+ startNextText = false;
+ }
+
+ if (frameCounter == text[textCounter]->endFrame) {
+ closeTextObject(text[textCounter]);
+ textCounter++;
+ textVisible = false;
+ }
+
+ if (textVisible)
+ drawTextObject(anim, text[textCounter]);
+ }
+
+ anim->updateScreen();
+ frameCounter++;
+
+ if (frameCounter == _leadOutFrame && leadOut)
+ _vm->_sound->playFx(&_leadOutHandle, leadOut, leadOutLen, Audio::Mixer::kMaxChannelVolume, 0, false, Audio::Mixer::kMusicSoundType);
+
+ OSystem::Event event;
+ while (_sys->pollEvent(event)) {
+ switch (event.type) {
+#ifndef BACKEND_8BIT
+ case OSystem::EVENT_SCREEN_CHANGED:
+ anim->buildLookup();
+ break;
+#endif
+ case OSystem::EVENT_KEYDOWN:
+ if (event.kbd.keycode == 27)
+ skipCutscene = true;
+ break;
+ case OSystem::EVENT_QUIT:
+ _vm->closeGame();
+ skipCutscene = true;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (!skipCutscene) {
+ // Sleep for one frame so that the last frame is displayed.
+ _sys->delayMillis(1000 / 12);
+ }
+
+ if (!_seamless) {
+ // Most movies fade to black on their own, but not all of them.
+ // Since we may be hanging around in the cutscene player for a
+ // while longer, waiting for the lead-out sound to finish,
+ // paint the overlay black.
+
+ anim->clearScreen();
+ }
+
+ // If the speech is still playing, redraw the subtitles. At least in
+ // the English version this is most noticeable in the "carib" cutscene.
+
+ if (textVisible && _snd->isSoundHandleActive(handle))
+ drawTextObject(anim, text[textCounter]);
+
+ if (text)
+ closeTextObject(text[textCounter]);
+
+ anim->updateScreen();
+
+ // Wait for the voice to stop playing. This is to make sure that we
+ // don't cut off the speech in mid-sentence, and - even more
+ // importantly - that we don't free the sound buffer while it's in use.
+
+ if (skipCutscene)
+ _snd->stopHandle(handle);
+
+ while (_snd->isSoundHandleActive(handle)) {
+ _vm->_screen->updateDisplay(false);
+ _sys->delayMillis(100);
+ }
+
+ if (!_seamless) {
+ // Clear the screen again
+ anim->clearScreen();
+ anim->updateScreen();
+ }
+
+ _vm->_screen->setPalette(0, 256, oldPal, RDPAL_INSTANT);
+
+ delete anim;
+}
+
+/**
+ * This just plays the cutscene with voiceovers / subtitles, in case the files
+ * are missing.
+ */
+
+void MoviePlayer::playDummy(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen) {
+ if (!text)
+ return;
+
+ int frameCounter = 0, textCounter = 0;
+
+ byte oldPal[256 * 4];
+ byte tmpPal[256 * 4];
+
+ _vm->_screen->clearScene();
+
+ // HACK: Draw instructions
+ //
+ // I'm using the the menu area, because that's unlikely to be touched
+ // by anything else during the cutscene.
+
+ memset(_vm->_screen->getScreen(), 0, _vm->_screen->getScreenWide() * MENUDEEP);
+
+ byte *data;
+
+ // Russian version substituted latin characters with cyrillic. That's
+ // why it renders completely unreadable with default message
+ if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) {
+ byte msg[] = "Po\344uk - to\344\345ko pev\345: hagmute k\344abuwy Ucke\343n, u\344u nocetute ca\343t npoekta u ckava\343te budeo po\344uku";
+ data = _vm->_fontRenderer->makeTextSprite(msg, RENDERWIDE, 255, _vm->_speechFontId);
+ } else {
+ // TODO: Translate message to other languages?
+#ifdef USE_MPEG2
+ byte msg[] = "Cutscene - Narration Only: Press ESC to exit, or visit www.scummvm.org to download cutscene videos";
+#else
+ byte msg[] = "Cutscene - Narration Only: Press ESC to exit, or recompile ScummVM with MPEG2 support";
+#endif
+
+ data = _vm->_fontRenderer->makeTextSprite(msg, RENDERWIDE, 255, _vm->_speechFontId);
+ }
+
+ FrameHeader frame_head;
+ SpriteInfo msgSprite;
+ byte *msgSurface;
+
+ frame_head.read(data);
+
+ msgSprite.x = _vm->_screen->getScreenWide() / 2 - frame_head.width / 2;
+ msgSprite.y = MENUDEEP / 2 - frame_head.height / 2;
+ msgSprite.w = frame_head.width;
+ msgSprite.h = frame_head.height;
+ msgSprite.type = RDSPR_NOCOMPRESSION;
+ msgSprite.data = data + FrameHeader::size();
+
+ _vm->_screen->createSurface(&msgSprite, &msgSurface);
+ _vm->_screen->drawSurface(&msgSprite, msgSurface);
+ _vm->_screen->deleteSurface(msgSurface);
+
+ free(data);
+
+ // In case the cutscene has a long lead-in, start just before the first
+ // line of text.
+
+ frameCounter = text[0]->startFrame - 12;
+
+ // Fake a palette that will hopefully make the text visible. In the
+ // opening cutscene it seems to use colours 1 (black) and 255 (white).
+
+ memcpy(oldPal, _vm->_screen->getPalette(), sizeof(oldPal));
+ memset(tmpPal, 0, sizeof(tmpPal));
+ tmpPal[255 * 4 + 0] = 255;
+ tmpPal[255 * 4 + 1] = 255;
+ tmpPal[255 * 4 + 2] = 255;
+ _vm->_screen->setPalette(0, 256, tmpPal, RDPAL_INSTANT);
+
+ Audio::SoundHandle handle;
+
+ bool skipCutscene = false;
+
+ uint32 flags = Audio::Mixer::FLAG_16BITS;
+
+#ifndef SCUMM_BIG_ENDIAN
+ flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+
+ while (1) {
+ if (!text[textCounter])
+ break;
+
+ if (frameCounter == text[textCounter]->startFrame) {
+ _vm->_screen->clearScene();
+ openTextObject(text[textCounter]);
+ drawTextObject(NULL, text[textCounter]);
+ if (text[textCounter]->speech) {
+ _snd->playRaw(&handle, text[textCounter]->speech, text[textCounter]->speechBufferSize, 22050, flags);
+ }
+ }
+
+ if (frameCounter == text[textCounter]->endFrame) {
+ closeTextObject(text[textCounter]);
+ _vm->_screen->clearScene();
+ _vm->_screen->setNeedFullRedraw();
+ textCounter++;
+ }
+
+ frameCounter++;
+ _vm->_screen->updateDisplay();
+
+ KeyboardEvent *ke = _vm->keyboardEvent();
+
+ if ((ke && ke->keycode == 27) || _vm->_quit) {
+ _snd->stopHandle(handle);
+ skipCutscene = true;
+ break;
+ }
+
+ // Simulate ~12 frames per second. I don't know what frame rate
+ // the original movies had, or even if it was constant, but
+ // this seems to work reasonably.
+
+ _sys->delayMillis(90);
+ }
+
+ // Wait for the voice to stop playing. This is to make sure that we
+ // don't cut off the speech in mid-sentence, and - even more
+ // importantly - that we don't free the sound buffer while it's in use.
+
+ while (_snd->isSoundHandleActive(handle)) {
+ _vm->_screen->updateDisplay(false);
+ _sys->delayMillis(100);
+ }
+
+ closeTextObject(text[textCounter]);
+
+ _vm->_screen->clearScene();
+ _vm->_screen->setNeedFullRedraw();
+
+ // HACK: Remove the instructions created above
+ Common::Rect r;
+
+ memset(_vm->_screen->getScreen(), 0, _vm->_screen->getScreenWide() * MENUDEEP);
+ r.left = r.top = 0;
+ r.right = _vm->_screen->getScreenWide();
+ r.bottom = MENUDEEP;
+ _vm->_screen->updateRect(&r);
+
+ // FIXME: For now, only play the lead-out music for cutscenes that have
+ // subtitles.
+
+ if (!skipCutscene && leadOut)
+ _vm->_sound->playFx(&_leadOutHandle, leadOut, leadOutLen, Audio::Mixer::kMaxChannelVolume, 0, false, Audio::Mixer::kMusicSoundType);
+
+ _vm->_screen->setPalette(0, 256, oldPal, RDPAL_INSTANT);
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h
new file mode 100644
index 0000000000..e3a554ccd7
--- /dev/null
+++ b/engines/sword2/animation.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef ANIMATION_H
+#define ANIMATION_H
+
+#include "graphics/animation.h"
+#include "sound/mixer.h"
+
+namespace Sword2 {
+
+struct SpriteInfo;
+
+// This is the structure which is passed to the sequence player. It includes
+// the smack to play, and any text lines which are to be displayed over the top
+// of the sequence.
+
+struct MovieTextObject {
+ uint16 startFrame;
+ uint16 endFrame;
+ SpriteInfo *textSprite;
+ uint32 speechBufferSize;
+ uint16 *speech;
+};
+
+class AnimationState : public ::Graphics::BaseAnimationState {
+private:
+ Sword2Engine *_vm;
+
+public:
+ AnimationState(Sword2Engine *vm);
+ ~AnimationState();
+
+#ifndef BACKEND_8BIT
+ void drawTextObject(SpriteInfo *s, byte *src);
+#endif
+
+ void clearScreen();
+ void updateScreen();
+
+private:
+ void drawYUV(int width, int height, byte *const *dat);
+
+#ifdef BACKEND_8BIT
+ void setPalette(byte *pal);
+#endif
+};
+
+struct MovieInfo {
+ char name[9];
+ uint frames;
+ bool seamless;
+};
+
+class MoviePlayer {
+private:
+ Sword2Engine *_vm;
+ Audio::Mixer *_snd;
+ OSystem *_sys;
+
+ byte *_textSurface;
+
+ Audio::SoundHandle _leadOutHandle;
+
+ uint _leadOutFrame;
+ bool _seamless;
+
+ static struct MovieInfo _movies[];
+
+ void openTextObject(MovieTextObject *obj);
+ void closeTextObject(MovieTextObject *obj);
+ void drawTextObject(AnimationState *anim, MovieTextObject *obj);
+
+ void playMPEG(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen);
+ void playDummy(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen);
+
+public:
+ MoviePlayer(Sword2Engine *vm);
+ int32 play(const char *filename, MovieTextObject *text[], int32 leadInRes, int32 leadOutRes);
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/anims.cpp b/engines/sword2/anims.cpp
new file mode 100644
index 0000000000..e64903a61f
--- /dev/null
+++ b/engines/sword2/anims.cpp
@@ -0,0 +1,308 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// ---------------------------------------------------------------------------
+// A more intelligent version of the old ANIMS.C
+// All this stuff by James
+// DON'T TOUCH!
+// ---------------------------------------------------------------------------
+
+#include "common/stdafx.h"
+#include "common/file.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/build_display.h"
+#include "sword2/interpreter.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/resman.h"
+#include "sword2/router.h"
+#include "sword2/sound.h"
+#include "sword2/animation.h"
+
+namespace Sword2 {
+
+int Router::doAnimate(byte *ob_logic, byte *ob_graph, int32 animRes, bool reverse) {
+ AnimHeader anim_head;
+ byte *anim_file;
+
+ ObjectLogic obLogic(ob_logic);
+ ObjectGraphic obGraph(ob_graph);
+
+ if (obLogic.getLooping() == 0) {
+ byte *ptr;
+
+ // This is the start of the anim - set up the first frame
+
+ // For testing all anims!
+ // A script loop can send every resource number to the anim
+ // function & it will only run the valid ones. See
+ // 'testing_routines' object in George's Player Character
+ // section of linc
+
+ if (_vm->_logic->readVar(SYSTEM_TESTING_ANIMS)) {
+ if (!_vm->_resman->checkValid(animRes)) {
+ // Not a valid resource number. Switch off
+ // the sprite. Don't animate - just continue
+ // script next cycle.
+ setSpriteStatus(ob_graph, NO_SPRITE);
+ return IR_STOP;
+ }
+
+ ptr = _vm->_resman->openResource(animRes);
+
+ // if it's not an animation file
+ if (_vm->_resman->fetchType(animRes) != ANIMATION_FILE) {
+ _vm->_resman->closeResource(animRes);
+
+ // switch off the sprite
+ // don't animate - just continue
+ // script next cycle
+ setSpriteStatus(ob_graph, NO_SPRITE);
+ return IR_STOP;
+ }
+
+ _vm->_resman->closeResource(animRes);
+
+ // switch on the sprite
+ setSpriteStatus(ob_graph, SORT_SPRITE);
+ }
+
+ assert(animRes);
+
+ // open anim file
+ anim_file = _vm->_resman->openResource(animRes);
+
+ assert(_vm->_resman->fetchType(animRes) == ANIMATION_FILE);
+
+ // point to anim header
+ anim_head.read(_vm->fetchAnimHeader(anim_file));
+
+ // now running an anim, looping back to this call again
+ obLogic.setLooping(1);
+ obGraph.setAnimResource(animRes);
+
+ if (reverse)
+ obGraph.setAnimPc(anim_head.noAnimFrames - 1);
+ else
+ obGraph.setAnimPc(0);
+ } else if (_vm->_logic->getSync() != -1) {
+ // We've received a sync - return to script immediately
+ debug(5, "**sync stopped %d**", _vm->_logic->readVar(ID));
+
+ // If sync received, anim finishes right now (remaining on
+ // last frame). Quit animation, but continue script.
+ obLogic.setLooping(0);
+ return IR_CONT;
+ } else {
+ // Not first frame, and no sync received - set up the next
+ // frame of the anim.
+
+ // open anim file and point to anim header
+ anim_file = _vm->_resman->openResource(obGraph.getAnimResource());
+ anim_head.read(_vm->fetchAnimHeader(anim_file));
+
+ if (reverse)
+ obGraph.setAnimPc(obGraph.getAnimPc() - 1);
+ else
+ obGraph.setAnimPc(obGraph.getAnimPc() + 1);
+ }
+
+ // check for end of anim
+
+ if (reverse) {
+ if (obGraph.getAnimPc() == 0)
+ obLogic.setLooping(0);
+ } else {
+ if (obGraph.getAnimPc() == anim_head.noAnimFrames - 1)
+ obLogic.setLooping(0);
+ }
+
+ // close the anim file
+ _vm->_resman->closeResource(obGraph.getAnimResource());
+
+ // check if we want the script to loop back & call this function again
+ return obLogic.getLooping() ? IR_REPEAT : IR_STOP;
+}
+
+int Router::megaTableAnimate(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *animTable, bool reverse) {
+ int32 animRes = 0;
+
+ // If this is the start of the anim, read the anim table to get the
+ // appropriate anim resource
+
+ ObjectLogic obLogic(ob_logic);
+
+ if (obLogic.getLooping() == 0) {
+ ObjectMega obMega(ob_mega);
+
+ // Appropriate anim resource is in 'table[direction]'
+ animRes = READ_LE_UINT32(animTable + 4 * obMega.getCurDir());
+ }
+
+ return doAnimate(ob_logic, ob_graph, animRes, reverse);
+}
+
+void Router::setSpriteStatus(byte *ob_graph, uint32 type) {
+ ObjectGraphic obGraph(ob_graph);
+
+ // Remove the previous status, but don't affect the shading upper-word
+ obGraph.setType((obGraph.getType() & 0xffff0000) | type);
+}
+
+void Router::setSpriteShading(byte *ob_graph, uint32 type) {
+ ObjectGraphic obGraph(ob_graph);
+
+ // Remove the previous shading, but don't affect the status lower-word.
+ // Note that mega frames may still be shaded automatically, even when
+ // not sent 'RDSPR_SHADOW'.
+ obGraph.setType((obGraph.getType() & 0x0000ffff) | type);
+}
+
+void Logic::createSequenceSpeech(MovieTextObject *sequenceText[]) {
+ uint32 line;
+ uint32 local_text;
+ uint32 text_res;
+ byte *text;
+ uint32 wavId; // ie. offical text number (actor text number)
+ bool speechRunning;
+
+ // for each sequence text line that's been logged
+ for (line = 0; line < _sequenceTextLines; line++) {
+ // allocate this structure
+ sequenceText[line] = new MovieTextObject;
+
+ sequenceText[line]->startFrame = _sequenceTextList[line].startFrame;
+ sequenceText[line]->endFrame = _sequenceTextList[line].endFrame;
+
+ // pull out the text line to get the official text number
+ // (for wav id)
+
+ text_res = _sequenceTextList[line].textNumber / SIZE;
+ local_text = _sequenceTextList[line].textNumber & 0xffff;
+
+ // open text resource & get the line
+ text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
+ wavId = (int32)READ_LE_UINT16(text);
+
+ // now ok to close the text file
+ _vm->_resman->closeResource(text_res);
+
+ // 1st word of text line is the official line number
+ debug(5,"(%d) SEQUENCE TEXT: %s", READ_LE_UINT16(text), text + 2);
+
+ // is it to be speech or subtitles or both?
+ // assume speech is not running until know otherwise
+
+ speechRunning = false;
+ _sequenceTextList[line].speech_mem = NULL;
+ sequenceText[line]->speech = NULL;
+
+ if (!_vm->_sound->isSpeechMute()) {
+ _sequenceTextList[line].speechBufferSize = _vm->_sound->preFetchCompSpeech(wavId, &_sequenceTextList[line].speech_mem);
+ if (_sequenceTextList[line].speechBufferSize) {
+ // ok, we've got speech!
+ speechRunning = true;
+ }
+ }
+
+ // if we want subtitles, or speech failed to load
+
+ if (_vm->getSubtitles() || !speechRunning) {
+ // open text resource & get the line
+ text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
+ // make the sprite
+ // 'text+2' to skip the first 2 bytes which form the
+ // line reference number
+
+ // NB. The mem block containing the text sprite is
+ // currently FLOATING!
+
+ // When rendering text over a sequence we need a
+ // different colour for the border.
+
+ _sequenceTextList[line].text_mem = _vm->_fontRenderer->makeTextSprite(text + 2, 600, 255, _vm->_speechFontId, 1);
+
+ // ok to close the text resource now
+ _vm->_resman->closeResource(text_res);
+ } else {
+ _sequenceTextList[line].text_mem = NULL;
+ sequenceText[line]->textSprite = NULL;
+ }
+ }
+
+ // for drivers: NULL-terminate the array of pointers to
+ // MovieTextObject's
+ sequenceText[_sequenceTextLines] = NULL;
+
+ for (line = 0; line < _sequenceTextLines; line++) {
+ // if we've made a text sprite for this line...
+
+ if (_sequenceTextList[line].text_mem) {
+ // now fill out the SpriteInfo structure in the
+ // MovieTextObjectStructure
+ FrameHeader frame;
+
+ frame.read(_sequenceTextList[line].text_mem);
+
+ sequenceText[line]->textSprite = new SpriteInfo;
+
+ // center text at bottom of screen
+ sequenceText[line]->textSprite->x = 320 - frame.width / 2;
+ sequenceText[line]->textSprite->y = 440 - frame.height;
+ sequenceText[line]->textSprite->w = frame.width;
+ sequenceText[line]->textSprite->h = frame.height;
+ sequenceText[line]->textSprite->type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION;
+ sequenceText[line]->textSprite->data = _sequenceTextList[line].text_mem + FrameHeader::size();
+ }
+
+ // if we've loaded a speech sample for this line...
+
+ if (_sequenceTextList[line].speech_mem) {
+ // for drivers: set up pointer to decompressed wav in
+ // memory
+
+ sequenceText[line]->speechBufferSize = _sequenceTextList[line].speechBufferSize;
+ sequenceText[line]->speech = _sequenceTextList[line].speech_mem;
+ }
+ }
+}
+
+void Logic::clearSequenceSpeech(MovieTextObject *sequenceText[]) {
+ for (uint i = 0; i < _sequenceTextLines; i++) {
+ // free up the memory used by this MovieTextObject
+ delete sequenceText[i];
+
+ // free up the mem block containing this text sprite
+ if (_sequenceTextList[i].text_mem)
+ free(_sequenceTextList[i].text_mem);
+
+ // free up the mem block containing this speech sample
+ if (_sequenceTextList[i].speech_mem)
+ free(_sequenceTextList[i].speech_mem);
+ }
+
+ // IMPORTANT! Reset the line count ready for the next sequence!
+ _sequenceTextLines = 0;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/build_display.cpp b/engines/sword2/build_display.cpp
new file mode 100644
index 0000000000..bc5a31a1f9
--- /dev/null
+++ b/engines/sword2/build_display.cpp
@@ -0,0 +1,1070 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// ---------------------------------------------------------------------------
+// BUILD_DISPLAY.CPP like the old spr_engi but slightly more aptly named
+// ---------------------------------------------------------------------------
+
+#include "common/stdafx.h"
+#include "common/system.h"
+
+#include "sword2/sword2.h"
+#include "sword2/console.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+
+namespace Sword2 {
+
+Screen::Screen(Sword2Engine *vm, int16 width, int16 height) {
+ _vm = vm;
+
+ _dirtyGrid = _buffer = NULL;
+
+ _vm->_system->initSize(width, height);
+
+ _screenWide = width;
+ _screenDeep = height;
+
+ _gridWide = width / CELLWIDE;
+ _gridDeep = height / CELLDEEP;
+
+ if ((width % CELLWIDE) || (height % CELLDEEP))
+ error("Bad cell size");
+
+ _dirtyGrid = (byte *)calloc(_gridWide, _gridDeep);
+ if (!_dirtyGrid)
+ error("Could not initialise dirty grid");
+
+ _buffer = (byte *)malloc(width * height);
+ if (!_buffer)
+ error("Could not initialise display");
+
+ for (int i = 0; i < ARRAYSIZE(_blockSurfaces); i++)
+ _blockSurfaces[i] = NULL;
+
+ _lightMask = NULL;
+ _needFullRedraw = false;
+
+ memset(&_thisScreen, 0, sizeof(_thisScreen));
+
+ _fps = 0;
+ _frameCount = 0;
+ _cycleTime = 0;
+
+ _lastPaletteRes = 0;
+
+ _scrollFraction = 16;
+
+ _largestLayerArea = 0;
+ _largestSpriteArea = 0;
+
+ strcpy(_largestLayerInfo, "largest layer: none registered");
+ strcpy(_largestSpriteInfo, "largest sprite: none registered");
+
+ _fadeStatus = RDFADE_NONE;
+ _renderAverageTime = 60;
+
+ _layer = 0;
+}
+
+Screen::~Screen() {
+ free(_buffer);
+ free(_dirtyGrid);
+ closeBackgroundLayer();
+ free(_lightMask);
+}
+
+void Screen::buildDisplay() {
+ if (_thisScreen.new_palette) {
+ // start the layer palette fading up
+ startNewPalette();
+
+ // should be reset to zero at start of each screen change
+ _largestLayerArea = 0;
+ _largestSpriteArea = 0;
+ }
+
+ // Does this ever happen?
+ if (!_thisScreen.background_layer_id)
+ return;
+
+ // there is a valid screen to run
+
+ setScrollTarget(_thisScreen.scroll_offset_x, _thisScreen.scroll_offset_y);
+ _vm->_mouse->animateMouse();
+ startRenderCycle();
+
+ byte *file = _vm->_resman->openResource(_thisScreen.background_layer_id);
+
+ MultiScreenHeader screenLayerTable;
+
+ screenLayerTable.read(file + ResHeader::size());
+
+ // Render at least one frame, but if the screen is scrolling, and if
+ // there is time left, we will render extra frames to smooth out the
+ // scrolling.
+
+ do {
+ // first background parallax + related anims
+ if (screenLayerTable.bg_parallax[0]) {
+ renderParallax(_vm->fetchBackgroundParallaxLayer(file, 0), 0);
+ drawBackPar0Frames();
+ }
+
+ // second background parallax + related anims
+ if (screenLayerTable.bg_parallax[1]) {
+ renderParallax(_vm->fetchBackgroundParallaxLayer(file, 1), 1);
+ drawBackPar1Frames();
+ }
+
+ // normal backround layer (just the one!)
+ renderParallax(_vm->fetchBackgroundLayer(file), 2);
+
+ // sprites & layers
+ drawBackFrames(); // background sprites
+ drawSortFrames(file); // sorted sprites & layers
+ drawForeFrames(); // foreground sprites
+
+ // first foreground parallax + related anims
+
+ if (screenLayerTable.fg_parallax[0]) {
+ renderParallax(_vm->fetchForegroundParallaxLayer(file, 0), 3);
+ drawForePar0Frames();
+ }
+
+ // second foreground parallax + related anims
+
+ if (screenLayerTable.fg_parallax[1]) {
+ renderParallax(_vm->fetchForegroundParallaxLayer(file, 1), 4);
+ drawForePar1Frames();
+ }
+
+ _vm->_debugger->drawDebugGraphics();
+ _vm->_fontRenderer->printTextBlocs();
+ _vm->_mouse->processMenu();
+
+ updateDisplay();
+
+ _frameCount++;
+ if (_vm->getMillis() > _cycleTime) {
+ _fps = _frameCount;
+ _frameCount = 0;
+ _cycleTime = _vm->getMillis() + 1000;
+ }
+ } while (!endRenderCycle());
+
+ _vm->_resman->closeResource(_thisScreen.background_layer_id);
+}
+
+/**
+ * Fades down and displays a message on the screen.
+ * @param text The message
+ * @param time The number of seconds to display the message, or 0 to display it
+ * until the user clicks the mouse or presses a key.
+ */
+
+void Screen::displayMsg(byte *text, int time) {
+ byte pal[256 * 4];
+ byte oldPal[256 * 4];
+
+ debug(2, "DisplayMsg: %s", text);
+
+ if (getFadeStatus() != RDFADE_BLACK) {
+ fadeDown();
+ waitForFade();
+ }
+
+ _vm->_mouse->setMouse(0);
+ _vm->_mouse->setLuggage(0);
+ _vm->_mouse->closeMenuImmediately();
+
+ clearScene();
+
+ byte *text_spr = _vm->_fontRenderer->makeTextSprite(text, 640, 187, _vm->_speechFontId);
+
+ FrameHeader frame;
+
+ frame.read(text_spr);
+
+ SpriteInfo spriteInfo;
+
+ spriteInfo.x = _screenWide / 2 - frame.width / 2;
+ if (!time)
+ spriteInfo.y = _screenDeep / 2 - frame.height / 2 - MENUDEEP;
+ else
+ spriteInfo.y = 400 - frame.height;
+ spriteInfo.w = frame.width;
+ spriteInfo.h = frame.height;
+ spriteInfo.scale = 0;
+ spriteInfo.scaledWidth = 0;
+ spriteInfo.scaledHeight = 0;
+ spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS;
+ spriteInfo.blend = 0;
+ spriteInfo.data = text_spr + FrameHeader::size();
+ spriteInfo.colourTable = 0;
+
+ uint32 rv = drawSprite(&spriteInfo);
+ if (rv)
+ error("Driver Error %.8x (in DisplayMsg)", rv);
+
+ memcpy(oldPal, _palette, sizeof(oldPal));
+ memset(pal, 0, sizeof(pal));
+
+ pal[187 * 4 + 0] = 255;
+ pal[187 * 4 + 1] = 255;
+ pal[187 * 4 + 2] = 255;
+
+ setPalette(0, 256, pal, RDPAL_FADE);
+ fadeUp();
+ free(text_spr);
+ waitForFade();
+
+ if (time > 0) {
+ uint32 targetTime = _vm->getMillis() + (time * 1000);
+ _vm->sleepUntil(targetTime);
+ } else {
+ while (!_vm->_quit) {
+ MouseEvent *me = _vm->mouseEvent();
+ if (me && (me->buttons & (RD_LEFTBUTTONDOWN | RD_RIGHTBUTTONDOWN)))
+ break;
+
+ if (_vm->keyboardEvent())
+ break;
+
+ updateDisplay();
+ _vm->_system->delayMillis(50);
+ }
+ }
+
+ fadeDown();
+ waitForFade();
+ clearScene();
+ setPalette(0, 256, oldPal, RDPAL_FADE);
+ fadeUp();
+}
+
+void Screen::drawBackPar0Frames() {
+ // frame attached to 1st background parallax
+ for (uint i = 0; i < _curBgp0; i++)
+ processImage(&_bgp0List[i]);
+}
+
+void Screen::drawBackPar1Frames() {
+ // frame attached to 2nd background parallax
+ for (uint i = 0; i < _curBgp1; i++)
+ processImage(&_bgp1List[i]);
+}
+
+void Screen::drawBackFrames() {
+ // background sprite, fixed to main background
+ for (uint i = 0; i < _curBack; i++)
+ processImage(&_backList[i]);
+}
+
+void Screen::drawSortFrames(byte *file) {
+ uint i, j;
+
+ // Sort the sort list. Used to be a separate function, but it was only
+ // called once, right before calling drawSortFrames().
+
+ if (_curSort > 1) {
+ for (i = 0; i < _curSort - 1; i++) {
+ for (j = 0; j < _curSort - 1; j++) {
+ if (_sortList[_sortOrder[j]].sort_y > _sortList[_sortOrder[j + 1]].sort_y) {
+ SWAP(_sortOrder[j], _sortOrder[j + 1]);
+ }
+ }
+ }
+ }
+
+ // Draw the sorted frames - layers, shrinkers & normal flat sprites
+
+ for (i = 0; i < _curSort; i++) {
+ if (_sortList[_sortOrder[i]].layer_number) {
+ // it's a layer - minus 1 for true layer number
+ // we need to know from the BuildUnit because the
+ // layers will have been sorted in random order
+ processLayer(file, _sortList[_sortOrder[i]].layer_number - 1);
+ } else {
+ // it's a sprite
+ processImage(&_sortList[_sortOrder[i]]);
+ }
+ }
+}
+
+void Screen::drawForeFrames() {
+ // foreground sprite, fixed to main background
+ for (uint i = 0; i < _curFore; i++)
+ processImage(&_foreList[i]);
+}
+
+void Screen::drawForePar0Frames() {
+ // frame attached to 1st foreground parallax
+ for (uint i = 0; i < _curFgp0; i++)
+ processImage(&_fgp0List[i]);
+}
+
+void Screen::drawForePar1Frames() {
+ // frame attached to 2nd foreground parallax
+ for (uint i = 0; i < _curFgp1; i++)
+ processImage(&_fgp1List[i]);
+}
+
+void Screen::processLayer(byte *file, uint32 layer_number) {
+ LayerHeader layer_head;
+
+ layer_head.read(_vm->fetchLayerHeader(file, layer_number));
+
+ SpriteInfo spriteInfo;
+
+ spriteInfo.x = layer_head.x;
+ spriteInfo.y = layer_head.y;
+ spriteInfo.w = layer_head.width;
+ spriteInfo.scale = 0;
+ spriteInfo.scaledWidth = 0;
+ spriteInfo.scaledHeight = 0;
+ spriteInfo.h = layer_head.height;
+ spriteInfo.type = RDSPR_TRANS | RDSPR_RLE256FAST;
+ spriteInfo.blend = 0;
+ spriteInfo.data = file + ResHeader::size() + layer_head.offset;
+ spriteInfo.colourTable = 0;
+
+ // check for largest layer for debug info
+
+ uint32 current_layer_area = layer_head.width * layer_head.height;
+
+ if (current_layer_area > _largestLayerArea) {
+ byte buf[NAME_LEN];
+
+ _largestLayerArea = current_layer_area;
+ sprintf(_largestLayerInfo,
+ "largest layer: %s layer(%d) is %dx%d",
+ _vm->_resman->fetchName(_thisScreen.background_layer_id, buf),
+ layer_number, layer_head.width, layer_head.height);
+ }
+
+ uint32 rv = drawSprite(&spriteInfo);
+ if (rv)
+ error("Driver Error %.8x in processLayer(%d)", rv, layer_number);
+}
+
+void Screen::processImage(BuildUnit *build_unit) {
+ byte *file = _vm->_resman->openResource(build_unit->anim_resource);
+ byte *colTablePtr = NULL;
+
+ byte *frame = _vm->fetchFrameHeader(file, build_unit->anim_pc);
+
+ AnimHeader anim_head;
+ CdtEntry cdt_entry;
+ FrameHeader frame_head;
+
+ anim_head.read(_vm->fetchAnimHeader(file));
+ cdt_entry.read(_vm->fetchCdtEntry(file, build_unit->anim_pc));
+ frame_head.read(frame);
+
+ // so that 0-colour is transparent
+ uint32 spriteType = RDSPR_TRANS;
+
+ if (anim_head.blend)
+ spriteType |= RDSPR_BLEND;
+
+ // if the frame is to be flipped (only really applicable to frames
+ // using offsets)
+ if (cdt_entry.frameType & FRAME_FLIPPED)
+ spriteType |= RDSPR_FLIP;
+
+ if (cdt_entry.frameType & FRAME_256_FAST) {
+ // scaling, shading & blending don't work with RLE256FAST
+ // but the same compression can be decompressed using the
+ // RLE256 routines!
+
+ // NOTE: If this restriction refers to drawSprite(), I don't
+ // think we have it any more. But I'm not sure.
+
+ if (build_unit->scale || anim_head.blend || build_unit->shadingFlag)
+ spriteType |= RDSPR_RLE256;
+ else
+ spriteType |= RDSPR_RLE256FAST;
+ } else {
+ switch (anim_head.runTimeComp) {
+ case NONE:
+ spriteType |= RDSPR_NOCOMPRESSION;
+ break;
+ case RLE256:
+ spriteType |= RDSPR_RLE256;
+ break;
+ case RLE16:
+ spriteType |= RDSPR_RLE16;
+ // points to just after last cdt_entry, ie.
+ // start of colour table
+ colTablePtr = _vm->fetchAnimHeader(file) + AnimHeader::size() + anim_head.noAnimFrames * CdtEntry::size();
+ break;
+ }
+ }
+
+ // if we want this frame to be affected by the shading mask,
+ // add the status bit
+ if (build_unit->shadingFlag)
+ spriteType |= RDSPR_SHADOW;
+
+ SpriteInfo spriteInfo;
+
+ spriteInfo.x = build_unit->x;
+ spriteInfo.y = build_unit->y;
+ spriteInfo.w = frame_head.width;
+ spriteInfo.h = frame_head.height;
+ spriteInfo.scale = build_unit->scale;
+ spriteInfo.scaledWidth = build_unit->scaled_width;
+ spriteInfo.scaledHeight = build_unit->scaled_height;
+ spriteInfo.type = spriteType;
+ spriteInfo.blend = anim_head.blend;
+ // points to just after frame header, ie. start of sprite data
+ spriteInfo.data = frame + FrameHeader::size();
+ spriteInfo.colourTable = colTablePtr;
+
+ // check for largest layer for debug info
+ uint32 current_sprite_area = frame_head.width * frame_head.height;
+
+ if (current_sprite_area > _largestSpriteArea) {
+ byte buf[NAME_LEN];
+
+ _largestSpriteArea = current_sprite_area;
+ sprintf(_largestSpriteInfo,
+ "largest sprite: %s frame(%d) is %dx%d",
+ _vm->_resman->fetchName(build_unit->anim_resource, buf),
+ build_unit->anim_pc,
+ frame_head.width,
+ frame_head.height);
+ }
+
+ if (_vm->_logic->readVar(SYSTEM_TESTING_ANIMS)) { // see anims.cpp
+ // bring the anim into the visible screen
+ // but leave extra pixel at edge for box
+ if (spriteInfo.x + spriteInfo.scaledWidth >= 639)
+ spriteInfo.x = 639 - spriteInfo.scaledWidth;
+
+ if (spriteInfo.y + spriteInfo.scaledHeight >= 399)
+ spriteInfo.y = 399 - spriteInfo.scaledHeight;
+
+ if (spriteInfo.x < 1)
+ spriteInfo.x = 1;
+
+ if (spriteInfo.y < 1)
+ spriteInfo.y = 1;
+
+ // create box to surround sprite - just outside sprite box
+ _vm->_debugger->_rectX1 = spriteInfo.x - 1;
+ _vm->_debugger->_rectY1 = spriteInfo.y - 1;
+ _vm->_debugger->_rectX2 = spriteInfo.x + spriteInfo.scaledWidth;
+ _vm->_debugger->_rectY2 = spriteInfo.y + spriteInfo.scaledHeight;
+ }
+
+ uint32 rv = drawSprite(&spriteInfo);
+ if (rv) {
+ byte buf[NAME_LEN];
+
+ error("Driver Error %.8x with sprite %s (%d) in processImage",
+ rv,
+ _vm->_resman->fetchName(build_unit->anim_resource, buf),
+ build_unit->anim_resource);
+ }
+
+ // release the anim resource
+ _vm->_resman->closeResource(build_unit->anim_resource);
+}
+
+void Screen::resetRenderLists() {
+ // reset the sort lists - do this before a logic loop
+ // takes into account the fact that the start of the list is pre-built
+ // with the special sortable layers
+
+ _curBgp0 = 0;
+ _curBgp1 = 0;
+ _curBack = 0;
+ // beginning of sort list is setup with the special sort layers
+ _curSort = _thisScreen.number_of_layers;
+ _curFore = 0;
+ _curFgp0 = 0;
+ _curFgp1 = 0;
+
+ if (_curSort) {
+ // there are some layers - so rebuild the sort order list
+ for (uint i = 0; i < _curSort; i++)
+ _sortOrder[i] = i;
+ }
+}
+
+void Screen::registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega, BuildUnit *build_unit) {
+ ObjectGraphic obGraph(ob_graph);
+ ObjectMega obMega(ob_mega);
+
+ assert(obGraph.getAnimResource());
+
+ byte *file = _vm->_resman->openResource(obGraph.getAnimResource());
+
+ AnimHeader anim_head;
+ CdtEntry cdt_entry;
+ FrameHeader frame_head;
+
+ anim_head.read(_vm->fetchAnimHeader(file));
+ cdt_entry.read(_vm->fetchCdtEntry(file, obGraph.getAnimPc()));
+ frame_head.read(_vm->fetchFrameHeader(file, obGraph.getAnimPc()));
+
+ // update player graphic details for on-screen debug info
+ if (_vm->_logic->readVar(ID) == CUR_PLAYER_ID) {
+ _vm->_debugger->_graphType = obGraph.getType();
+ _vm->_debugger->_graphAnimRes = obGraph.getAnimResource();
+ // counting 1st frame as 'frame 1'
+ _vm->_debugger->_graphAnimPc = obGraph.getAnimPc() + 1;
+ _vm->_debugger->_graphNoFrames = anim_head.noAnimFrames;
+ }
+
+ // fill in the BuildUnit structure for this frame
+
+ build_unit->anim_resource = obGraph.getAnimResource();
+ build_unit->anim_pc = obGraph.getAnimPc();
+ build_unit->layer_number = 0;
+
+ // Affected by shading mask?
+ if (obGraph.getType() & SHADED_SPRITE)
+ build_unit->shadingFlag = true;
+ else
+ build_unit->shadingFlag = false;
+
+ // Check if this frame has offsets ie. this is a scalable mega frame
+
+ int scale = 0;
+
+ if (cdt_entry.frameType & FRAME_OFFSET) {
+ scale = obMega.calcScale();
+
+ // calc final render coordinates (top-left of sprite), based
+ // on feet coords & scaled offsets
+
+ // add scaled offsets to feet coords
+ build_unit->x = obMega.getFeetX() + (cdt_entry.x * scale) / 256;
+ build_unit->y = obMega.getFeetY() + (cdt_entry.y * scale) / 256;
+
+ // Work out new width and height. Always divide by 256 after
+ // everything else, to maintain accurary
+ build_unit->scaled_width = ((scale * frame_head.width) / 256);
+ build_unit->scaled_height = ((scale * frame_head.height) / 256);
+ } else {
+ // It's a non-scaling anim. Get render coords for sprite, from cdt
+ build_unit->x = cdt_entry.x;
+ build_unit->y = cdt_entry.y;
+
+ // Get width and height
+ build_unit->scaled_width = frame_head.width;
+ build_unit->scaled_height = frame_head.height;
+ }
+
+ // either 0 or required scale, depending on whether 'scale' computed
+ build_unit->scale = scale;
+
+ // calc the bottom y-coord for sorting purposes
+ build_unit->sort_y = build_unit->y + build_unit->scaled_height - 1;
+
+ if (ob_mouse) {
+ // passed a mouse structure, so add to the _mouseList
+ _vm->_mouse->registerMouse(ob_mouse, build_unit);
+
+ }
+
+ _vm->_resman->closeResource(obGraph.getAnimResource());
+}
+
+void Screen::registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega) {
+ ObjectGraphic obGraph(ob_graph);
+
+ // check low word for sprite type
+ switch (obGraph.getType() & 0x0000ffff) {
+ case BGP0_SPRITE:
+ assert(_curBgp0 < MAX_bgp0_sprites);
+ registerFrame(ob_mouse, ob_graph, ob_mega, &_bgp0List[_curBgp0]);
+ _curBgp0++;
+ break;
+ case BGP1_SPRITE:
+ assert(_curBgp1 < MAX_bgp1_sprites);
+ registerFrame(ob_mouse, ob_graph, ob_mega, &_bgp1List[_curBgp1]);
+ _curBgp1++;
+ break;
+ case BACK_SPRITE:
+ assert(_curBack < MAX_back_sprites);
+ registerFrame(ob_mouse, ob_graph, ob_mega, &_backList[_curBack]);
+ _curBack++;
+ break;
+ case SORT_SPRITE:
+ assert(_curSort < MAX_sort_sprites);
+ _sortOrder[_curSort] = _curSort;
+ registerFrame(ob_mouse, ob_graph, ob_mega, &_sortList[_curSort]);
+ _curSort++;
+ break;
+ case FORE_SPRITE:
+ assert(_curFore < MAX_fore_sprites);
+ registerFrame(ob_mouse, ob_graph, ob_mega, &_foreList[_curFore]);
+ _curFore++;
+ break;
+ case FGP0_SPRITE:
+ assert(_curFgp0 < MAX_fgp0_sprites);
+ registerFrame(ob_mouse, ob_graph, ob_mega, &_fgp0List[_curFgp0]);
+ _curFgp0++;
+ break;
+ case FGP1_SPRITE:
+ assert(_curFgp1 < MAX_fgp1_sprites);
+ registerFrame(ob_mouse, ob_graph, ob_mega, &_fgp1List[_curFgp1]);
+ _curFgp1++;
+ break;
+ default:
+ // NO_SPRITE no registering!
+ break;
+ }
+}
+
+// FIXME:
+//
+// The original credits used a different font. I think it's stored in the
+// font.clu file, but I don't know how to interpret it.
+//
+// The original used the entire screen. This version cuts off the top and
+// bottom of the screen, because that's where the menus would usually be.
+//
+// The original had some sort of smoke effect at the bottom of the screen.
+
+enum {
+ LINE_LEFT,
+ LINE_CENTER,
+ LINE_RIGHT
+};
+
+struct CreditsLine {
+ char *str;
+ byte type;
+ int top;
+ int height;
+ byte *sprite;
+
+ CreditsLine() {
+ str = NULL;
+ sprite = NULL;
+ };
+
+ ~CreditsLine() {
+ free(str);
+ free(sprite);
+ str = NULL;
+ sprite = NULL;
+ }
+};
+
+#define CREDITS_FONT_HEIGHT 25
+#define CREDITS_LINE_SPACING 20
+
+void Screen::rollCredits() {
+ uint32 loopingMusicId = _vm->_sound->getLoopingMusicId();
+
+ // Prepare for the credits by fading down, stoping the music, etc.
+
+ _vm->_mouse->setMouse(0);
+
+ _vm->_sound->muteFx(true);
+ _vm->_sound->muteSpeech(true);
+
+ waitForFade();
+ fadeDown();
+ waitForFade();
+
+ _vm->_mouse->closeMenuImmediately();
+
+ // There are three files which I believe are involved in showing the
+ // credits:
+ //
+ // credits.bmp - The "Smacker" logo, stored as follows:
+ //
+ // width 2 bytes, little endian
+ // height 2 bytes, little endian
+ // palette 3 * 256 bytes
+ // data width * height bytes
+ //
+ // Note that the maximum colour component in the palette is 0x3F.
+ // This is the same resolution as the _paletteMatch table. I doubt
+ // that this is a coincidence, but let's use the image palette
+ // directly anyway, just to be safe.
+ //
+ // credits.clu - The credits text
+ //
+ // This is simply a text file with CRLF line endings.
+ // '^' is not shown, but used to mark the center of the line.
+ // '@' is used as a placeholder for the "Smacker" logo. At least
+ // when it appears alone.
+ // Remaining lines are centered.
+ // The German version also contains character code 9 for no
+ // apparent reason. We ignore them.
+ //
+ // fonts.clu - The credits font?
+ //
+ // FIXME: At this time I don't know how to interpret fonts.clu. For
+ // now, let's just the standard speech font instead.
+
+ SpriteInfo spriteInfo;
+ Common::File f;
+ int i;
+
+ // Read the "Smacker" logo
+
+ uint16 logoWidth = 0;
+ uint16 logoHeight = 0;
+ byte *logoData = NULL;
+ byte palette[256 * 4];
+
+ if (f.open("credits.bmp")) {
+ logoWidth = f.readUint16LE();
+ logoHeight = f.readUint16LE();
+
+ for (i = 0; i < 256; i++) {
+ palette[i * 4 + 0] = f.readByte() << 2;
+ palette[i * 4 + 1] = f.readByte() << 2;
+ palette[i * 4 + 2] = f.readByte() << 2;
+ palette[i * 4 + 3] = 0;
+ }
+
+ logoData = (byte *)malloc(logoWidth * logoHeight);
+
+ f.read(logoData, logoWidth * logoHeight);
+ f.close();
+ } else {
+ warning("Can't find credits.bmp");
+ memset(palette, 0, sizeof(palette));
+ palette[14 * 4 + 0] = 252;
+ palette[14 * 4 + 1] = 252;
+ palette[14 * 4 + 2] = 252;
+ palette[14 * 4 + 3] = 0;
+ }
+
+ setPalette(0, 256, palette, RDPAL_INSTANT);
+
+ // Read the credits text
+
+ Common::Array<CreditsLine *> creditsLines;
+
+ int lineCount = 0;
+ int lineTop = 400;
+ int paragraphStart = 0;
+ bool hasCenterMark = false;
+
+ if (!f.open("credits.clu")) {
+ warning("Can't find credits.clu");
+ return;
+ }
+
+ while (1) {
+ char buffer[80];
+ char *line = f.readLine(buffer, sizeof(buffer));
+
+ if (!line || *line == 0) {
+ if (!hasCenterMark) {
+ for (i = paragraphStart; i < lineCount; i++)
+ creditsLines[i]->type = LINE_CENTER;
+ }
+ paragraphStart = lineCount;
+ hasCenterMark = false;
+ if (paragraphStart == lineCount)
+ lineTop += CREDITS_LINE_SPACING;
+
+ if (!line)
+ break;
+
+ continue;
+ }
+
+ // The German credits contains character code 9. We don't want
+ // the credits to show the 'dud' symbol, so we replace them
+ // with spaces.
+
+ for (char *ptr = line; *ptr; ptr++) {
+ if (*ptr < 32)
+ *ptr = 32;
+ }
+
+ char *center_mark = strchr(line, '^');
+
+ if (center_mark) {
+ // The current paragraph has at least one center mark.
+ hasCenterMark = true;
+
+ if (center_mark != line) {
+ creditsLines.push_back(new CreditsLine);
+
+ // The center mark is somewhere inside the
+ // line. Split it into left and right side.
+ *center_mark = 0;
+
+ creditsLines[lineCount]->top = lineTop;
+ creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT;
+ creditsLines[lineCount]->type = LINE_LEFT;
+ creditsLines[lineCount]->str = strdup(line);
+
+ lineCount++;
+ *center_mark = '^';
+ }
+
+ line = center_mark;
+ }
+
+ creditsLines.push_back(new CreditsLine);
+
+ creditsLines[lineCount]->top = lineTop;
+
+ if (*line == '^') {
+ creditsLines[lineCount]->type = LINE_RIGHT;
+ line++;
+ } else
+ creditsLines[lineCount]->type = LINE_LEFT;
+
+ if (strcmp(line, "@") == 0) {
+ creditsLines[lineCount]->height = logoHeight;
+ lineTop += logoHeight;
+ } else {
+ creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT;
+ lineTop += CREDITS_LINE_SPACING;
+ }
+
+ creditsLines[lineCount]->str = strdup(line);
+ lineCount++;
+ }
+
+ f.close();
+
+ // We could easily add some ScummVM stuff to the credits, if we wanted
+ // to. On the other hand, anyone with the attention span to actually
+ // read all the credits probably already knows. :-)
+
+ // Start the music and roll the credits
+
+ // The credits music (which can also be heard briefly in the "carib"
+ // cutscene) is played once.
+
+ _vm->_sound->streamCompMusic(309, false);
+
+ clearScene();
+ fadeUp(0);
+
+ spriteInfo.scale = 0;
+ spriteInfo.scaledWidth = 0;
+ spriteInfo.scaledHeight = 0;
+ spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS;
+ spriteInfo.blend = 0;
+
+ int startLine = 0;
+ int scrollPos = 0;
+
+ bool abortCredits = false;
+
+ int scrollSteps = lineTop + CREDITS_FONT_HEIGHT;
+ uint32 musicStart = _vm->getMillis();
+
+ // Ideally the music should last just a tiny bit longer than the
+ // credits. Note that musicTimeRemaining() will return 0 if the music
+ // is muted, so we need a sensible fallback for that case.
+
+ uint32 musicLength = MAX((int32)(1000 * (_vm->_sound->musicTimeRemaining() - 3)), 25 * (int32)scrollSteps);
+
+ while (scrollPos < scrollSteps && !_vm->_quit) {
+ clearScene();
+
+ for (i = startLine; i < lineCount; i++) {
+ if (!creditsLines[i])
+ continue;
+
+ // Free any sprites that have scrolled off the screen
+
+ if (creditsLines[i]->top + creditsLines[i]->height < scrollPos) {
+ debug(2, "Freeing line %d: '%s'", i, creditsLines[i]->str);
+
+ delete creditsLines[i];
+ creditsLines[i] = NULL;
+
+ startLine = i + 1;
+ } else if (creditsLines[i]->top < scrollPos + 400) {
+ if (!creditsLines[i]->sprite) {
+ debug(2, "Creating line %d: '%s'", i, creditsLines[i]->str);
+ creditsLines[i]->sprite = _vm->_fontRenderer->makeTextSprite((byte *)creditsLines[i]->str, 600, 14, _vm->_speechFontId, 0);
+ }
+
+ FrameHeader frame;
+
+ frame.read(creditsLines[i]->sprite);
+
+ spriteInfo.y = creditsLines[i]->top - scrollPos;
+ spriteInfo.w = frame.width;
+ spriteInfo.h = frame.height;
+ spriteInfo.data = creditsLines[i]->sprite + FrameHeader::size();
+
+ switch (creditsLines[i]->type) {
+ case LINE_LEFT:
+ spriteInfo.x = RENDERWIDE / 2 - 5 - frame.width;
+ break;
+ case LINE_RIGHT:
+ spriteInfo.x = RENDERWIDE / 2 + 5;
+ break;
+ case LINE_CENTER:
+ if (strcmp(creditsLines[i]->str, "@") == 0) {
+ spriteInfo.data = logoData;
+ spriteInfo.x = (RENDERWIDE - logoWidth) / 2;
+ spriteInfo.w = logoWidth;
+ spriteInfo.h = logoHeight;
+ } else
+ spriteInfo.x = (RENDERWIDE - frame.width) / 2;
+ break;
+ }
+
+ if (spriteInfo.data)
+ drawSprite(&spriteInfo);
+ } else
+ break;
+ }
+
+ updateDisplay();
+
+ KeyboardEvent *ke = _vm->keyboardEvent();
+
+ if (ke && ke->keycode == 27) {
+ if (!abortCredits) {
+ abortCredits = true;
+ fadeDown();
+ }
+ }
+
+ if (abortCredits && getFadeStatus() == RDFADE_BLACK)
+ break;
+
+ _vm->sleepUntil(musicStart + (musicLength * scrollPos) / scrollSteps);
+ scrollPos++;
+ }
+
+ // We're done. Clean up and try to put everything back where it was
+ // before the credits.
+
+ for (i = 0; i < lineCount; i++) {
+ delete creditsLines[i];
+ }
+
+ free(logoData);
+
+ if (!abortCredits) {
+ fadeDown();
+
+ // The music should either have stopped or be about to stop, so
+ // wait for it to really happen.
+
+ while (_vm->_sound->musicTimeRemaining() && !_vm->_quit) {
+ updateDisplay(false);
+ _vm->_system->delayMillis(100);
+ }
+ }
+
+ if (_vm->_quit)
+ return;
+
+ waitForFade();
+
+ _vm->_sound->muteFx(false);
+ _vm->_sound->muteSpeech(false);
+
+ if (loopingMusicId)
+ _vm->_sound->streamCompMusic(loopingMusicId, true);
+ else
+ _vm->_sound->stopMusic(false);
+
+ if (!_vm->_mouse->getMouseStatus() || _vm->_mouse->isChoosing())
+ _vm->_mouse->setMouse(NORMAL_MOUSE_ID);
+
+ if (_vm->_logic->readVar(DEAD))
+ _vm->_mouse->buildSystemMenu();
+}
+
+// This image used to be shown by CacheNewCluster() while copying a data file
+// from the CD to the hard disk. ScummVM doesn't do that, so the image is never
+// shown. It'd be nice if we could do something useful with it some day...
+
+void Screen::splashScreen() {
+ byte *bgfile = _vm->_resman->openResource(2950);
+
+ initialiseBackgroundLayer(NULL);
+ initialiseBackgroundLayer(NULL);
+ initialiseBackgroundLayer(_vm->fetchBackgroundLayer(bgfile));
+ initialiseBackgroundLayer(NULL);
+ initialiseBackgroundLayer(NULL);
+
+ setPalette(0, 256, _vm->fetchPalette(bgfile), RDPAL_FADE);
+ renderParallax(_vm->fetchBackgroundLayer(bgfile), 2);
+
+ closeBackgroundLayer();
+
+ byte *loadingBar = _vm->_resman->openResource(2951);
+ byte *frame = _vm->fetchFrameHeader(loadingBar, 0);
+
+ AnimHeader animHead;
+ CdtEntry cdt;
+ FrameHeader frame_head;
+
+ animHead.read(_vm->fetchAnimHeader(loadingBar));
+ cdt.read(_vm->fetchCdtEntry(loadingBar, 0));
+ frame_head.read(_vm->fetchFrameHeader(loadingBar, 0));
+
+ SpriteInfo barSprite;
+
+ barSprite.x = cdt.x;
+ barSprite.y = cdt.y;
+ barSprite.w = frame_head.width;
+ barSprite.h = frame_head.height;
+ barSprite.scale = 0;
+ barSprite.scaledWidth = 0;
+ barSprite.scaledHeight = 0;
+ barSprite.type = RDSPR_RLE256FAST | RDSPR_TRANS;
+ barSprite.blend = 0;
+ barSprite.colourTable = 0;
+ barSprite.data = frame + FrameHeader::size();
+
+ drawSprite(&barSprite);
+
+ fadeUp();
+ waitForFade();
+
+ for (int i = 0; i < animHead.noAnimFrames; i++) {
+ frame = _vm->fetchFrameHeader(loadingBar, i);
+ barSprite.data = frame + FrameHeader::size();
+ drawSprite(&barSprite);
+ updateDisplay();
+ _vm->_system->delayMillis(30);
+ }
+
+ _vm->_resman->closeResource(2951);
+
+ fadeDown();
+ waitForFade();
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/build_display.h b/engines/sword2/build_display.h
new file mode 100644
index 0000000000..1a362da137
--- /dev/null
+++ b/engines/sword2/build_display.h
@@ -0,0 +1,441 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _BUILD_DISPLAY
+#define _BUILD_DISPLAY
+
+#include "common/rect.h"
+#include "common/stream.h"
+
+#define MAX_bgp0_sprites 6
+#define MAX_bgp1_sprites 6
+#define MAX_back_sprites 30
+#define MAX_sort_sprites 30
+#define MAX_fore_sprites 30
+#define MAX_fgp0_sprites 6
+#define MAX_fgp1_sprites 6
+
+#define PALTABLESIZE (64 * 64 * 64)
+
+#define BLOCKWIDTH 64
+#define BLOCKHEIGHT 64
+#define MAXLAYERS 5
+
+#define MENUDEEP 40
+#define RENDERWIDE 640
+#define RENDERDEEP (480 - (MENUDEEP * 2))
+
+// Maximum scaled size of a sprite
+#define SCALE_MAXWIDTH 512
+#define SCALE_MAXHEIGHT 512
+
+// Dirty grid cell size
+#define CELLWIDE 10
+#define CELLDEEP 20
+
+namespace Sword2 {
+
+class Sword2Engine;
+
+// Sprite defines
+
+enum {
+ // This is the low byte part of the sprite type.
+
+ RDSPR_TRANS = 0x0001,
+ RDSPR_BLEND = 0x0004,
+ RDSPR_FLIP = 0x0008,
+ RDSPR_SHADOW = 0x0010,
+ RDSPR_DISPLAYALIGN = 0x0020,
+ RDSPR_NOCOMPRESSION = 0x0040,
+ RDSPR_EDGEBLEND = 0x0080, // Unused
+
+ // This is the high byte part of the sprite type, which defines what
+ // type of compression is used. Unless RDSPR_NOCOMPRESSION is set.
+
+ RDSPR_RLE16 = 0x0000,
+ RDSPR_RLE256 = 0x0100,
+ RDSPR_RLE256FAST = 0x0200
+};
+
+// Fading defines
+
+enum {
+ RDFADE_NONE,
+ RDFADE_UP,
+ RDFADE_DOWN,
+ RDFADE_BLACK
+};
+
+// Palette defines
+
+enum {
+ RDPAL_FADE,
+ RDPAL_INSTANT
+};
+
+// Blitting FX defines
+
+enum {
+ RDBLTFX_SPRITEBLEND = 0x01,
+ RDBLTFX_SHADOWBLEND = 0x02,
+ RDBLTFX_EDGEBLEND = 0x04
+};
+
+// Structure filled out by each object to register its graphic printing
+// requrements
+
+struct BuildUnit {
+ int16 x;
+ int16 y;
+ uint16 scaled_width;
+ uint16 scaled_height;
+ int16 sort_y;
+ uint32 anim_resource;
+ uint16 anim_pc;
+
+ // Denotes a scaling sprite at print time - and holds the scaling value
+ // for the shrink routine
+
+ uint16 scale;
+
+ // Non-zero means this item is a layer - retrieve from background layer
+ // and send to special renderer
+
+ uint16 layer_number;
+
+ // True means we want this frame to be affected by the shading mask
+
+ bool shadingFlag;
+};
+
+struct ScreenInfo {
+ uint16 scroll_offset_x; // Position x
+ uint16 scroll_offset_y; // Position y
+ uint16 max_scroll_offset_x; // Calc'ed in fnInitBackground
+ uint16 max_scroll_offset_y;
+ int16 player_feet_x; // Feet coordinates to use - cant just
+ int16 player_feet_y; // fetch the player compact anymore
+ int16 feet_x; // Special offset-to-player position -
+ int16 feet_y; // tweek as desired - always set in
+ // screen manager object startup
+ uint16 screen_wide; // Size of background layer - hence
+ uint16 screen_deep; // size of back buffer itself (Paul
+ // actually malloc's it)
+ uint32 background_layer_id; // Id of the normal background layer
+ // from the header of the main
+ // background layer
+ uint16 number_of_layers;
+ uint8 new_palette; // Set to non zero to start the
+ // palette held within layer file
+ // fading up after a build_display
+ uint8 scroll_flag; // Scroll mode 0 off 1 on
+ bool mask_flag; // Using shading mask
+};
+
+// The SpriteInfo structure is used to tell the driver96 code what attributes
+// are linked to a sprite for drawing. These include position, scaling and
+// compression.
+
+struct SpriteInfo {
+ int16 x; // coords for top-left of sprite
+ int16 y;
+ uint16 w; // dimensions of sprite (before scaling)
+ uint16 h;
+ uint16 scale; // scale at which to draw, given in 256ths ['0' or '256' MEANS DON'T SCALE]
+ uint16 scaledWidth; // new dimensions (we calc these for the mouse area, so may as well pass to you to save time)
+ uint16 scaledHeight; //
+ uint16 type; // mask containing 'RDSPR_' bits specifying compression type, flip, transparency, etc
+ uint16 blend; // holds the blending values.
+ byte *data; // pointer to the sprite data
+ byte *colourTable; // pointer to 16-byte colour table, only applicable to 16-col compression type
+};
+
+struct BlockSurface {
+ byte data[BLOCKWIDTH * BLOCKHEIGHT];
+ bool transparent;
+};
+
+struct Parallax {
+ uint16 w;
+ uint16 h;
+
+ // The dimensions are followed by an offset table, but we don't know in
+ // advance how big it is. See initializeBackgroundLayer().
+
+ static const int size() {
+ return 4;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ w = readS.readUint16LE();
+ h = readS.readUint16LE();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(w);
+ writeS.writeUint16LE(h);
+ }
+};
+
+class Screen {
+private:
+ Sword2Engine *_vm;
+
+ // _thisScreen describes the current back buffer and its in-game scroll
+ // positions, etc.
+
+ ScreenInfo _thisScreen;
+
+ int32 _renderCaps;
+ int8 _renderLevel;
+
+ byte *_buffer;
+ byte *_lightMask;
+
+ // Game screen metrics
+ int16 _screenWide;
+ int16 _screenDeep;
+
+ bool _needFullRedraw;
+
+ // Scroll variables. _scrollX and _scrollY hold the current scroll
+ // position, and _scrollXTarget and _scrollYTarget are the target
+ // position for the end of the game cycle.
+
+ int16 _scrollX;
+ int16 _scrollY;
+
+ int16 _scrollXTarget;
+ int16 _scrollYTarget;
+ int16 _scrollXOld;
+ int16 _scrollYOld;
+
+ int16 _parallaxScrollX; // current x offset to link a sprite to the
+ // parallax layer
+ int16 _parallaxScrollY; // current y offset to link a sprite to the
+ // parallax layer
+ int16 _locationWide;
+ int16 _locationDeep;
+
+ // Dirty grid handling
+ byte *_dirtyGrid;
+
+ uint16 _gridWide;
+ uint16 _gridDeep;
+
+ byte _palette[256 * 4];
+ byte _paletteMatch[PALTABLESIZE];
+
+ uint8 _fadeStatus;
+ int32 _fadeStartTime;
+ int32 _fadeTotalTime;
+
+ // 'frames per second' counting stuff
+ uint32 _fps;
+ uint32 _cycleTime;
+ uint32 _frameCount;
+
+ int32 _initialTime;
+ int32 _startTime;
+ int32 _totalTime;
+ int32 _renderAverageTime;
+ int32 _framesPerGameCycle;
+ bool _renderTooSlow;
+
+ void startNewPalette();
+
+ void resetRenderEngine();
+
+ void startRenderCycle();
+ bool endRenderCycle();
+
+ // Holds the order of the sort list, i.e. the list stays static and we
+ // sort this array.
+
+ uint16 _sortOrder[MAX_sort_sprites];
+
+ BuildUnit _bgp0List[MAX_bgp0_sprites];
+ BuildUnit _bgp1List[MAX_bgp1_sprites];
+ BuildUnit _backList[MAX_back_sprites];
+ BuildUnit _sortList[MAX_sort_sprites];
+ BuildUnit _foreList[MAX_fore_sprites];
+ BuildUnit _fgp0List[MAX_fgp0_sprites];
+ BuildUnit _fgp1List[MAX_fgp1_sprites];
+
+ uint32 _curBgp0;
+ uint32 _curBgp1;
+ uint32 _curBack;
+ uint32 _curSort;
+ uint32 _curFore;
+ uint32 _curFgp0;
+ uint32 _curFgp1;
+
+ void drawBackPar0Frames();
+ void drawBackPar1Frames();
+ void drawBackFrames();
+ void drawSortFrames(byte *file);
+ void drawForeFrames();
+ void drawForePar0Frames();
+ void drawForePar1Frames();
+
+ void processLayer(byte *file, uint32 layer_number);
+ void processImage(BuildUnit *build_unit);
+
+ uint8 _scrollFraction;
+
+ // Last palette used - so that we can restore the correct one after a
+ // pause (which dims the screen) and it's not always the main screen
+ // palette that we want, eg. during the eclipse
+
+ // This flag gets set in startNewPalette() and setFullPalette()
+
+ uint32 _lastPaletteRes;
+
+ // Debugging stuff
+ uint32 _largestLayerArea;
+ uint32 _largestSpriteArea;
+ char _largestLayerInfo[128];
+ char _largestSpriteInfo[128];
+
+ void registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega, BuildUnit *build_unit);
+
+ void mirrorSprite(byte *dst, byte *src, int16 w, int16 h);
+ int32 decompressRLE256(byte *dst, byte *src, int32 decompSize);
+ void unwindRaw16(byte *dst, byte *src, uint8 blockSize, byte *colTable);
+ int32 decompressRLE16(byte *dst, byte *src, int32 decompSize, byte *colTable);
+ void renderParallax(byte *ptr, int16 layer);
+
+ void markAsDirty(int16 x0, int16 y0, int16 x1, int16 y1);
+
+ uint8 _xBlocks[MAXLAYERS];
+ uint8 _yBlocks[MAXLAYERS];
+
+ // An array of sub-blocks, one for each of the parallax layers.
+
+ BlockSurface **_blockSurfaces[MAXLAYERS];
+
+ uint16 _xScale[SCALE_MAXWIDTH];
+ uint16 _yScale[SCALE_MAXHEIGHT];
+
+ void blitBlockSurface(BlockSurface *s, Common::Rect *r, Common::Rect *clipRect);
+
+ uint16 _layer;
+
+public:
+ Screen(Sword2Engine *vm, int16 width, int16 height);
+ ~Screen();
+
+ int8 getRenderLevel();
+ void setRenderLevel(int8 level);
+
+ byte *getScreen() { return _buffer; }
+ byte *getPalette() { return _palette; }
+ ScreenInfo *getScreenInfo() { return &_thisScreen; }
+
+ int16 getScreenWide() { return _screenWide; }
+ int16 getScreenDeep() { return _screenDeep; }
+
+ uint32 getCurBgp0() { return _curBgp0; }
+ uint32 getCurBgp1() { return _curBgp1; }
+ uint32 getCurBack() { return _curBack; }
+ uint32 getCurSort() { return _curSort; }
+ uint32 getCurFore() { return _curFore; }
+ uint32 getCurFgp0() { return _curFgp0; }
+ uint32 getCurFgp1() { return _curFgp1; }
+
+ uint32 getFps() { return _fps; }
+
+ uint32 getLargestLayerArea() { return _largestLayerArea; }
+ uint32 getLargestSpriteArea() { return _largestSpriteArea; }
+ char *getLargestLayerInfo() { return _largestLayerInfo; }
+ char *getLargestSpriteInfo() { return _largestSpriteInfo; }
+
+ void setNeedFullRedraw();
+
+ void clearScene();
+
+ void resetRenderLists();
+
+ void setLocationMetrics(uint16 w, uint16 h);
+ int32 initialiseBackgroundLayer(byte *parallax);
+ void closeBackgroundLayer();
+
+ void initialiseRenderCycle();
+
+ void initBackground(int32 res, int32 new_palette);
+ void registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega);
+
+ void setScrollFraction(uint8 f) { _scrollFraction = f; }
+ void setScrollTarget(int16 x, int16 y);
+ void setScrolling();
+
+ void setFullPalette(int32 palRes);
+ void setPalette(int16 startEntry, int16 noEntries, byte *palette, uint8 setNow);
+ uint8 quickMatch(uint8 r, uint8 g, uint8 b);
+ int32 fadeUp(float time = 0.75);
+ int32 fadeDown(float time = 0.75);
+ uint8 getFadeStatus();
+ void dimPalette();
+ void waitForFade();
+ void fadeServer();
+
+ void updateDisplay(bool redrawScene = true);
+
+ void displayMsg(byte *text, int time);
+
+ int32 createSurface(SpriteInfo *s, byte **surface);
+ void drawSurface(SpriteInfo *s, byte *surface, Common::Rect *clipRect = NULL);
+ void deleteSurface(byte *surface);
+ int32 drawSprite(SpriteInfo *s);
+
+ void scaleImageFast(byte *dst, uint16 dstPitch, uint16 dstWidth,
+ uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth,
+ uint16 srcHeight);
+ void scaleImageGood(byte *dst, uint16 dstPitch, uint16 dstWidth,
+ uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth,
+ uint16 srcHeight, byte *backbuf);
+
+ void updateRect(Common::Rect *r);
+
+ int32 openLightMask(SpriteInfo *s);
+ int32 closeLightMask();
+
+ void buildDisplay();
+
+ void plotPoint(int x, int y, uint8 colour);
+ void drawLine(int x0, int y0, int x1, int y1, uint8 colour);
+
+#ifdef BACKEND_8BIT
+ void plotYUV(byte *lut, int width, int height, byte *const *dat);
+#endif
+
+ void rollCredits();
+ void splashScreen();
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/console.cpp b/engines/sword2/console.cpp
new file mode 100644
index 0000000000..b465dc369f
--- /dev/null
+++ b/engines/sword2/console.cpp
@@ -0,0 +1,826 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/console.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/memory.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+
+#include "common/debugger.cpp"
+
+namespace Sword2 {
+
+Debugger::Debugger(Sword2Engine *vm)
+ : Common::Debugger<Debugger>() {
+ _vm = vm;
+
+ memset(_debugTextBlocks, 0, sizeof(_debugTextBlocks));
+ memset(_showVar, 0, sizeof(_showVar));
+
+ _displayDebugText = false; // "INFO"
+ _displayWalkGrid = false; // "WALKGRID"
+ _displayMouseMarker = false; // "MOUSE"
+ _displayTime = false; // "TIME"
+ _displayPlayerMarker = false; // "PLAYER"
+ _displayTextNumbers = false; // "TEXT"
+
+ _definingRectangles = false; // "RECT"
+ _draggingRectangle = 0; // 0 = waiting to start new rect
+ // 1 = currently dragging a rectangle
+
+ _rectX1 = _rectY1 = 0;
+ _rectX2 = _rectY2 = 0;
+ _rectFlicker = false;
+
+ _testingSnR = false; // "SAVEREST" - for system to kill all
+ // object resources (except player) in
+ // fnAddHuman()
+
+ _speechScriptWaiting = 0; // The id of whoever we're waiting for
+ // in a speech script. See fnTheyDo(),
+ // fnTheyDoWeWait(), fnWeWait(), and
+ // fnTimedWait().
+
+ _startTime = 0; // "TIMEON" & "TIMEOFF" - system start
+ // time
+
+ _textNumber = 0; // Current system text line number
+
+ _graphNoFrames = 0; // No. of frames in currently displayed
+ // anim
+
+ // Register commands
+
+ DCmd_Register("continue", &Debugger::Cmd_Exit);
+ DCmd_Register("exit", &Debugger::Cmd_Exit);
+ DCmd_Register("quit", &Debugger::Cmd_Exit);
+ DCmd_Register("q", &Debugger::Cmd_Exit);
+ DCmd_Register("help", &Debugger::Cmd_Help);
+ DCmd_Register("mem", &Debugger::Cmd_Mem);
+ DCmd_Register("tony", &Debugger::Cmd_Tony);
+ DCmd_Register("res", &Debugger::Cmd_Res);
+ DCmd_Register("reslist", &Debugger::Cmd_ResList);
+ DCmd_Register("starts", &Debugger::Cmd_Starts);
+ DCmd_Register("start", &Debugger::Cmd_Start);
+ DCmd_Register("s", &Debugger::Cmd_Start);
+ DCmd_Register("info", &Debugger::Cmd_Info);
+ DCmd_Register("walkgrid", &Debugger::Cmd_WalkGrid);
+ DCmd_Register("mouse", &Debugger::Cmd_Mouse);
+ DCmd_Register("player", &Debugger::Cmd_Player);
+ DCmd_Register("reslook", &Debugger::Cmd_ResLook);
+ DCmd_Register("cur", &Debugger::Cmd_CurrentInfo);
+ DCmd_Register("runlist", &Debugger::Cmd_RunList);
+ DCmd_Register("kill", &Debugger::Cmd_Kill);
+ DCmd_Register("nuke", &Debugger::Cmd_Nuke);
+ DCmd_Register("var", &Debugger::Cmd_Var);
+ DCmd_Register("rect", &Debugger::Cmd_Rect);
+ DCmd_Register("clear", &Debugger::Cmd_Clear);
+ DCmd_Register("debugon", &Debugger::Cmd_DebugOn);
+ DCmd_Register("debugoff", &Debugger::Cmd_DebugOff);
+ DCmd_Register("saverest", &Debugger::Cmd_SaveRest);
+ DCmd_Register("timeon", &Debugger::Cmd_TimeOn);
+ DCmd_Register("timeoff", &Debugger::Cmd_TimeOff);
+ DCmd_Register("text", &Debugger::Cmd_Text);
+ DCmd_Register("showvar", &Debugger::Cmd_ShowVar);
+ DCmd_Register("hidevar", &Debugger::Cmd_HideVar);
+ DCmd_Register("version", &Debugger::Cmd_Version);
+ DCmd_Register("animtest", &Debugger::Cmd_AnimTest);
+ DCmd_Register("texttest", &Debugger::Cmd_TextTest);
+ DCmd_Register("linetest", &Debugger::Cmd_LineTest);
+ DCmd_Register("events", &Debugger::Cmd_Events);
+ DCmd_Register("sfx", &Debugger::Cmd_Sfx);
+ DCmd_Register("english", &Debugger::Cmd_English);
+ DCmd_Register("finnish", &Debugger::Cmd_Finnish);
+ DCmd_Register("polish", &Debugger::Cmd_Polish);
+}
+
+void Debugger::varGet(int var) {
+ DebugPrintf("%d\n", _vm->_logic->readVar(var));
+}
+
+void Debugger::varSet(int var, int val) {
+ DebugPrintf("was %d, ", _vm->_logic->readVar(var));
+ _vm->_logic->writeVar(var, val);
+ DebugPrintf("now %d\n", _vm->_logic->readVar(var));
+}
+
+void Debugger::preEnter() {
+ // Pause sound output
+ if (_vm->_sound) {
+ _vm->_sound->pauseFx();
+ _vm->_sound->pauseSpeech();
+ _vm->_sound->pauseMusic();
+ }
+}
+
+void Debugger::postEnter() {
+ if (_vm->_sound) {
+ // Resume previous sound state
+ _vm->_sound->unpauseFx();
+ _vm->_sound->unpauseSpeech();
+ _vm->_sound->unpauseMusic();
+ }
+
+ if (_vm->_mouse) {
+ // Restore old mouse cursor
+ _vm->_mouse->drawMouse();
+ }
+}
+
+// Now the fun stuff: Commands
+
+bool Debugger::Cmd_Exit(int argc, const char **argv) {
+ _detach_now = true;
+ _vm->clearInputEvents();
+ return false;
+}
+
+bool Debugger::Cmd_Help(int argc, const char **argv) {
+ // console normally has 78 line width
+ // wrap around nicely
+ int width = 0;
+
+ DebugPrintf("Commands are:\n");
+ for (int i = 0 ; i < _dcmd_count ; i++) {
+ int size = strlen(_dcmds[i].name) + 1;
+
+ if (width + size >= 75) {
+ DebugPrintf("\n");
+ width = size;
+ } else
+ width += size;
+
+ DebugPrintf("%s ", _dcmds[i].name);
+ }
+
+ DebugPrintf("\n");
+ return true;
+}
+
+static int compare_blocks(const void *p1, const void *p2) {
+ const MemBlock *m1 = *(const MemBlock * const *)p1;
+ const MemBlock *m2 = *(const MemBlock * const *)p2;
+
+ if (m1->size < m2->size)
+ return 1;
+ if (m1->size > m2->size)
+ return -1;
+ return 0;
+}
+
+bool Debugger::Cmd_Mem(int argc, const char **argv) {
+ int16 numBlocks = _vm->_memory->getNumBlocks();
+ MemBlock *memBlocks = _vm->_memory->getMemBlocks();
+
+ MemBlock **blocks = (MemBlock **)malloc(numBlocks * sizeof(MemBlock));
+
+ int i, j;
+
+ for (i = 0, j = 0; i < MAX_MEMORY_BLOCKS; i++) {
+ if (memBlocks[i].ptr)
+ blocks[j++] = &memBlocks[i];
+ }
+
+ qsort(blocks, numBlocks, sizeof(MemBlock *), compare_blocks);
+
+ DebugPrintf(" size id res type name\n");
+ DebugPrintf("---------------------------------------------------------------------------\n");
+
+ for (i = 0; i < numBlocks; i++) {
+ const char *type;
+
+ switch (_vm->_resman->fetchType(blocks[i]->ptr)) {
+ case ANIMATION_FILE:
+ type = "ANIMATION_FILE";
+ break;
+ case SCREEN_FILE:
+ type = "SCREEN_FILE";
+ break;
+ case GAME_OBJECT:
+ type = "GAME_OBJECT";
+ break;
+ case WALK_GRID_FILE:
+ type = "WALK_GRID_FILE";
+ break;
+ case GLOBAL_VAR_FILE:
+ type = "GLOBAL_VAR_FILE";
+ break;
+ case PARALLAX_FILE_null:
+ type = "PARALLAX_FILE_null";
+ break;
+ case RUN_LIST:
+ type = "RUN_LIST";
+ break;
+ case TEXT_FILE:
+ type = "TEXT_FILE";
+ break;
+ case SCREEN_MANAGER:
+ type = "SCREEN_MANAGER";
+ break;
+ case MOUSE_FILE:
+ type = "MOUSE_FILE";
+ break;
+ case WAV_FILE:
+ type = "WAV_FILE";
+ break;
+ case ICON_FILE:
+ type = "ICON_FILE";
+ break;
+ case PALETTE_FILE:
+ type = "PALETTE_FILE";
+ break;
+ default:
+ type = "<unknown>";
+ break;
+ }
+
+ DebugPrintf("%9ld %-3d %-4d %-20s %s\n", blocks[i]->size, blocks[i]->id, blocks[i]->uid, type, _vm->_resman->fetchName(blocks[i]->ptr));
+ }
+
+ free(blocks);
+
+ DebugPrintf("---------------------------------------------------------------------------\n");
+ DebugPrintf("%9ld\n", _vm->_memory->getTotAlloc());
+
+ return true;
+}
+
+bool Debugger::Cmd_Tony(int argc, const char **argv) {
+ DebugPrintf("What about him?\n");
+ return true;
+}
+
+bool Debugger::Cmd_Res(int argc, const char **argv) {
+ uint32 numClusters = _vm->_resman->getNumClusters();
+
+ if (!numClusters) {
+ DebugPrintf("Argh! No resources!\n");
+ return true;
+ }
+
+ ResourceFile *resFiles = _vm->_resman->getResFiles();
+
+ for (uint i = 0; i < numClusters; i++) {
+ const char *locStr[3] = { "HDD", "CD1", "CD2" };
+
+ DebugPrintf("%-20s %d\n", resFiles[i].fileName, locStr[resFiles[i].cd]);
+ }
+
+ DebugPrintf("%d resources\n", _vm->_resman->getNumResFiles());
+ return true;
+}
+
+bool Debugger::Cmd_ResList(int argc, const char **argv) {
+ // By default, list only resources that are being held open.
+ uint minCount = 1;
+
+ if (argc > 1)
+ minCount = atoi(argv[1]);
+
+ uint32 numResFiles = _vm->_resman->getNumResFiles();
+ Resource *resList = _vm->_resman->getResList();
+
+ for (uint i = 0; i < numResFiles; i++) {
+ if (resList[i].ptr && resList[i].refCount >= minCount) {
+ DebugPrintf("%-4d: %-35s refCount: %-3d\n", i, _vm->_resman->fetchName(resList[i].ptr), resList[i].refCount);
+ }
+ }
+
+ return true;
+}
+
+bool Debugger::Cmd_Starts(int argc, const char **argv) {
+ uint32 numStarts = _vm->getNumStarts();
+
+ if (!numStarts) {
+ DebugPrintf("Sorry - no startup positions registered?\n");
+
+ uint32 numScreenManagers = _vm->getNumScreenManagers();
+
+ if (!numScreenManagers)
+ DebugPrintf("There is a problem with startup.inf\n");
+ else
+ DebugPrintf(" (%d screen managers found in startup.inf)\n", numScreenManagers);
+ return true;
+ }
+
+ StartUp *startList = _vm->getStartList();
+
+ for (uint i = 0; i < numStarts; i++)
+ DebugPrintf("%d (%s)\n", i, startList[i].description);
+
+ return true;
+}
+
+bool Debugger::Cmd_Start(int argc, const char **argv) {
+ uint8 pal[4] = { 255, 255, 255, 0 };
+
+ if (argc != 2) {
+ DebugPrintf("Usage: %s number\n", argv[0]);
+ return true;
+ }
+
+ uint32 numStarts = _vm->getNumStarts();
+
+ if (!numStarts) {
+ DebugPrintf("Sorry - there are no startups!\n");
+ return true;
+ }
+
+ int start = atoi(argv[1]);
+
+ if (start < 0 || start >= (int)numStarts) {
+ DebugPrintf("Not a legal start position\n");
+ return true;
+ }
+
+ DebugPrintf("Running start %d\n", start);
+
+ _vm->runStart(start);
+ _vm->_screen->setPalette(187, 1, pal, RDPAL_INSTANT);
+ return true;
+}
+
+bool Debugger::Cmd_Info(int argc, const char **argv) {
+ _displayDebugText = !_displayDebugText;
+
+ if (_displayDebugText)
+ DebugPrintf("Info text on\n");
+ else
+ DebugPrintf("Info Text off\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_WalkGrid(int argc, const char **argv) {
+ _displayWalkGrid = !_displayWalkGrid;
+
+ if (_displayWalkGrid)
+ DebugPrintf("Walk-grid display on\n");
+ else
+ DebugPrintf("Walk-grid display off\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_Mouse(int argc, const char **argv) {
+ _displayMouseMarker = !_displayMouseMarker;
+
+ if (_displayMouseMarker)
+ DebugPrintf("Mouse marker on\n");
+ else
+ DebugPrintf("Mouse marker off\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_Player(int argc, const char **argv) {
+ _displayPlayerMarker = !_displayPlayerMarker;
+
+ if (_displayPlayerMarker)
+ DebugPrintf("Player feet marker on\n");
+ else
+ DebugPrintf("Player feet marker off\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_ResLook(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Usage: %s number\n", argv[0]);
+ return true;
+ }
+
+ int res = atoi(argv[1]);
+ uint32 numResFiles = _vm->_resman->getNumResFiles();
+
+ if (res < 0 || res >= (int)numResFiles) {
+ DebugPrintf("Illegal resource %d. There are %d resources, 0-%d.\n",
+ res, numResFiles, numResFiles - 1);
+ return true;
+ }
+
+ if (!_vm->_resman->checkValid(res)) {
+ DebugPrintf("%d is a null & void resource number\n", res);
+ return true;
+ }
+
+ // Open up the resource and take a look inside!
+ uint8 type = _vm->_resman->fetchType(res);;
+ byte name[NAME_LEN];
+
+ _vm->_resman->fetchName(res, name);
+
+ switch (type) {
+ case ANIMATION_FILE:
+ DebugPrintf("<anim> %s\n", name);
+ break;
+ case SCREEN_FILE:
+ DebugPrintf("<layer> %s\n", name);
+ break;
+ case GAME_OBJECT:
+ DebugPrintf("<game object> %s\n", name);
+ break;
+ case WALK_GRID_FILE:
+ DebugPrintf("<walk grid> %s\n", name);
+ break;
+ case GLOBAL_VAR_FILE:
+ DebugPrintf("<global variables> %s\n", name);
+ break;
+ case PARALLAX_FILE_null:
+ DebugPrintf("<parallax file NOT USED!> %s\n", name);
+ break;
+ case RUN_LIST:
+ DebugPrintf("<run list> %s\n", name);
+ break;
+ case TEXT_FILE:
+ DebugPrintf("<text file> %s\n", name);
+ break;
+ case SCREEN_MANAGER:
+ DebugPrintf("<screen manager> %s\n", name);
+ break;
+ case MOUSE_FILE:
+ DebugPrintf("<mouse pointer> %s\n", name);
+ break;
+ case ICON_FILE:
+ DebugPrintf("<menu icon> %s\n", name);
+ break;
+ default:
+ DebugPrintf("unrecognised fileType %d\n", type);
+ break;
+ }
+
+ return true;
+}
+
+bool Debugger::Cmd_CurrentInfo(int argc, const char **argv) {
+ // prints general stuff about the screen, etc.
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ if (screenInfo->background_layer_id) {
+ DebugPrintf("background layer id %d\n", screenInfo->background_layer_id);
+ DebugPrintf("%d wide, %d high\n", screenInfo->screen_wide, screenInfo->screen_deep);
+ DebugPrintf("%d normal layers\n", screenInfo->number_of_layers);
+
+ Cmd_RunList(argc, argv);
+ } else
+ DebugPrintf("No screen\n");
+ return true;
+}
+
+bool Debugger::Cmd_RunList(int argc, const char **argv) {
+ uint32 runList = _vm->_logic->getRunList();
+
+ if (runList) {
+ Common::MemoryReadStream readS(_vm->_resman->openResource(runList), _vm->_resman->fetchLen(runList));
+
+ readS.seek(ResHeader::size());
+
+ DebugPrintf("Runlist number %d\n", runList);
+
+ while (1) {
+ uint32 res = readS.readUint32LE();
+ if (!res)
+ break;
+
+ byte name[NAME_LEN];
+
+ DebugPrintf("%d %s\n", res, _vm->_resman->fetchName(res, name));
+ }
+
+ _vm->_resman->closeResource(runList);
+ } else
+ DebugPrintf("No run list set\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_Kill(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Usage: %s number\n", argv[0]);
+ return true;
+ }
+
+ int res = atoi(argv[1]);
+ uint32 numResFiles = _vm->_resman->getNumResFiles();
+
+ if (res < 0 || res >= (int)numResFiles) {
+ DebugPrintf("Illegal resource %d. There are %d resources, 0-%d.\n",
+ res, numResFiles, numResFiles - 1);
+ return true;
+ }
+
+ Resource *resList = _vm->_resman->getResList();
+
+ if (!resList[res].ptr) {
+ DebugPrintf("Resource %d is not in memory\n", res);
+ return true;
+ }
+
+ if (resList[res].refCount) {
+ DebugPrintf("Resource %d is open - cannot remove\n", res);
+ return true;
+ }
+
+ _vm->_resman->remove(res);
+ DebugPrintf("Trashed %d\n", res);
+ return true;
+}
+
+bool Debugger::Cmd_Nuke(int argc, const char **argv) {
+ DebugPrintf("Killing all resources except variable file and player object\n");
+ _vm->_resman->killAll(true);
+ return true;
+}
+
+bool Debugger::Cmd_Var(int argc, const char **argv) {
+ switch (argc) {
+ case 2:
+ varGet(atoi(argv[1]));
+ break;
+ case 3:
+ varSet(atoi(argv[1]), atoi(argv[2]));
+ break;
+ default:
+ DebugPrintf("Usage: %s number value\n", argv[0]);
+ break;
+ }
+
+ return true;
+}
+
+bool Debugger::Cmd_Rect(int argc, const char **argv) {
+ uint32 filter = _vm->setInputEventFilter(0);
+
+ _definingRectangles = !_definingRectangles;
+
+ if (_definingRectangles) {
+ _vm->setInputEventFilter(filter & ~(RD_LEFTBUTTONUP | RD_RIGHTBUTTONUP));
+ DebugPrintf("Mouse rectangles enabled\n");
+ } else {
+ _vm->setInputEventFilter(filter | RD_LEFTBUTTONUP | RD_RIGHTBUTTONUP);
+ DebugPrintf("Mouse rectangles disabled\n");
+ }
+
+ _draggingRectangle = 0;
+ return true;
+}
+
+bool Debugger::Cmd_Clear(int argc, const char **argv) {
+ _vm->_resman->killAllObjects(true);
+ return true;
+}
+
+bool Debugger::Cmd_DebugOn(int argc, const char **argv) {
+ _displayDebugText = true;
+ _displayWalkGrid = true;
+ _displayMouseMarker = true;
+ _displayPlayerMarker = true;
+ _displayTextNumbers = true;
+ DebugPrintf("Enabled all on-screen debug info\n");
+ return true;
+}
+
+bool Debugger::Cmd_DebugOff(int argc, const char **argv) {
+ _displayDebugText = false;
+ _displayWalkGrid = false;
+ _displayMouseMarker = false;
+ _displayPlayerMarker = false;
+ _displayTextNumbers = false;
+ DebugPrintf("Disabled all on-screen debug info\n");
+ return true;
+}
+
+bool Debugger::Cmd_SaveRest(int argc, const char **argv) {
+ _testingSnR = !_testingSnR;
+
+ if (_testingSnR)
+ DebugPrintf("Enabled S&R logic_script stability checking\n");
+ else
+ DebugPrintf("Disabled S&R logic_script stability checking\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_TimeOn(int argc, const char **argv) {
+ if (argc == 2)
+ _startTime = _vm->_system->getMillis() - atoi(argv[1]) * 1000;
+ else if (_startTime == 0)
+ _startTime = _vm->_system->getMillis();
+ _displayTime = true;
+ DebugPrintf("Timer display on\n");
+ return true;
+}
+
+bool Debugger::Cmd_TimeOff(int argc, const char **argv) {
+ _displayTime = false;
+ DebugPrintf("Timer display off\n");
+ return true;
+}
+
+bool Debugger::Cmd_Text(int argc, const char **argv) {
+ _displayTextNumbers = !_displayTextNumbers;
+
+ if (_displayTextNumbers)
+ DebugPrintf("Text numbers on\n");
+ else
+ DebugPrintf("Text numbers off\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_ShowVar(int argc, const char **argv) {
+ int32 showVarNo = 0;
+ int32 varNo;
+
+ if (argc != 2) {
+ DebugPrintf("Usage: %s number\n", argv[0]);
+ return true;
+ }
+
+ varNo = atoi(argv[1]);
+
+ // search for a spare slot in the watch-list, but also watch out for
+ // this variable already being in the list
+
+ while (showVarNo < MAX_SHOWVARS && _showVar[showVarNo] != 0 && _showVar[showVarNo] != varNo)
+ showVarNo++;
+
+ // if we've found a spare slot or the variable's already there
+ if (showVarNo < MAX_SHOWVARS) {
+ if (_showVar[showVarNo] == 0) {
+ // empty slot - add it to the list at this slot
+ _showVar[showVarNo] = varNo;
+ DebugPrintf("var(%d) added to the watch-list\n", varNo);
+ } else
+ DebugPrintf("var(%d) already in the watch-list!\n", varNo);
+ } else
+ DebugPrintf("Sorry - no more allowed - hide one or extend the system watch-list\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_HideVar(int argc, const char **argv) {
+ int32 showVarNo = 0;
+ int32 varNo;
+
+ if (argc != 2) {
+ DebugPrintf("Usage: %s number\n", argv[0]);
+ return true;
+ }
+
+ varNo = atoi(argv[1]);
+
+ // search for 'varNo' in the watch-list
+ while (showVarNo < MAX_SHOWVARS && _showVar[showVarNo] != varNo)
+ showVarNo++;
+
+ if (showVarNo < MAX_SHOWVARS) {
+ // We've found 'varNo' in the list - clear this slot
+ _showVar[showVarNo] = 0;
+ DebugPrintf("var(%d) removed from watch-list\n", varNo);
+ } else
+ DebugPrintf("Sorry - can't find var(%d) in the list\n", varNo);
+
+ return true;
+}
+
+bool Debugger::Cmd_Version(int argc, const char **argv) {
+ // This function used to print more information, but nothing we
+ // particularly care about.
+
+ DebugPrintf("\"Broken Sword II\" (c) Revolution Software 1997.\n");
+ return true;
+}
+
+bool Debugger::Cmd_AnimTest(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Usage: %s value\n", argv[0]);
+ return true;
+ }
+
+ // Automatically do "s 32" to run the animation testing start script
+ _vm->runStart(32);
+
+ // Same as typing "VAR 912 <value>" at the console
+ varSet(912, atoi(argv[1]));
+
+ DebugPrintf("Setting flag 'system_testing_anims'\n");
+ return true;
+}
+
+bool Debugger::Cmd_TextTest(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Usage: %s value\n", argv[0]);
+ return true;
+ }
+
+ // Automatically do "s 33" to run the text/speech testing start script
+ _vm->runStart(33);
+
+ // Same as typing "VAR 1230 <value>" at the console
+ varSet(1230, atoi(argv[1]));
+
+ _displayTextNumbers = true;
+
+ DebugPrintf("Setting flag 'system_testing_text'\n");
+ DebugPrintf("Text numbers on\n");
+ return true;
+}
+
+bool Debugger::Cmd_LineTest(int argc, const char **argv) {
+ if (argc != 3) {
+ DebugPrintf("Usage: %s value1 value2\n", argv[0]);
+ return true;
+ }
+
+ // Automatically do "s 33" to run the text/speech testing start script
+ _vm->runStart(33);
+
+ // Same as typing "VAR 1230 <value>" at the console
+ varSet(1230, atoi(argv[1]));
+
+ // Same as typing "VAR 1264 <value>" at the console
+ varSet(1264, atoi(argv[2]));
+
+ _displayTextNumbers = true;
+
+ DebugPrintf("Setting flag 'system_testing_text'\n");
+ DebugPrintf("Setting flag 'system_test_line_no'\n");
+ DebugPrintf("Text numbers on\n");
+ return true;
+}
+
+bool Debugger::Cmd_Events(int argc, const char **argv) {
+ EventUnit *eventList = _vm->_logic->getEventList();
+
+ DebugPrintf("EVENT LIST:\n");
+
+ for (uint32 i = 0; i < MAX_events; i++) {
+ if (eventList[i].id) {
+ byte buf[NAME_LEN];
+ uint32 target = eventList[i].id;
+ uint32 script = eventList[i].interact_id;
+
+ DebugPrintf("slot %2d: id = %s (%d)\n", i, _vm->_resman->fetchName(target, buf), target);
+ DebugPrintf(" script = %s (%d) pos %d\n", _vm->_resman->fetchName(script / 65536, buf), script / 65536, script % 65536);
+ }
+ }
+
+ return true;
+}
+
+bool Debugger::Cmd_Sfx(int argc, const char **argv) {
+ _vm->_wantSfxDebug = !_vm->_wantSfxDebug;
+
+ if (_vm->_wantSfxDebug)
+ DebugPrintf("SFX logging activated\n");
+ else
+ DebugPrintf("SFX logging deactivated\n");
+
+ return true;
+}
+
+bool Debugger::Cmd_English(int argc, const char **argv) {
+ _vm->initialiseFontResourceFlags(DEFAULT_TEXT);
+ DebugPrintf("Default fonts selected\n");
+ return true;
+}
+
+bool Debugger::Cmd_Finnish(int argc, const char **argv) {
+ _vm->initialiseFontResourceFlags(FINNISH_TEXT);
+ DebugPrintf("Finnish fonts selected\n");
+ return true;
+}
+
+bool Debugger::Cmd_Polish(int argc, const char **argv) {
+ _vm->initialiseFontResourceFlags(POLISH_TEXT);
+ DebugPrintf("Polish fonts selected\n");
+ return true;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/console.h b/engines/sword2/console.h
new file mode 100644
index 0000000000..95bfbe946d
--- /dev/null
+++ b/engines/sword2/console.h
@@ -0,0 +1,127 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef C_ONSOLE_H
+#define C_ONSOLE_H
+
+#include "common/debugger.h"
+#include "sword2/debug.h"
+
+namespace Sword2 {
+
+class Debugger : public Common::Debugger<Debugger> {
+private:
+ void varGet(int var);
+ void varSet(int var, int val);
+
+ bool _displayDebugText;
+ bool _displayWalkGrid;
+ bool _displayMouseMarker;
+ bool _displayTime;
+ bool _displayPlayerMarker;
+ bool _displayTextNumbers;
+
+ bool _rectFlicker;
+
+ int32 _startTime;
+
+ int32 _showVar[MAX_SHOWVARS];
+
+ byte _debugTextBlocks[MAX_DEBUG_TEXTS];
+
+ void clearDebugTextBlocks();
+ void makeDebugTextBlock(char *text, int16 x, int16 y);
+
+ void plotCrossHair(int16 x, int16 y, uint8 pen);
+ void drawRect(int16 x, int16 y, int16 x2, int16 y2, uint8 pen);
+
+public:
+ Debugger(Sword2Engine *vm);
+
+ int16 _rectX1, _rectY1;
+ int16 _rectX2, _rectY2;
+
+ uint8 _draggingRectangle;
+ bool _definingRectangles;
+
+ bool _testingSnR;
+
+ int32 _speechScriptWaiting;
+
+ int32 _textNumber;
+
+ int32 _graphType;
+ int32 _graphAnimRes;
+ int32 _graphAnimPc;
+ uint32 _graphNoFrames;
+
+ void buildDebugText();
+ void drawDebugGraphics();
+
+protected:
+ Sword2Engine *_vm;
+
+ virtual void preEnter();
+ virtual void postEnter();
+
+ // Commands
+ bool Cmd_Exit(int argc, const char **argv);
+ bool Cmd_Help(int argc, const char **argv);
+ bool Cmd_Mem(int argc, const char **argv);
+ bool Cmd_Tony(int argc, const char **argv);
+ bool Cmd_Res(int argc, const char **argv);
+ bool Cmd_ResList(int argc, const char **argv);
+ bool Cmd_Starts(int argc, const char **argv);
+ bool Cmd_Start(int argc, const char **argv);
+ bool Cmd_Info(int argc, const char **argv);
+ bool Cmd_WalkGrid(int argc, const char **argv);
+ bool Cmd_Mouse(int argc, const char **argv);
+ bool Cmd_Player(int argc, const char **argv);
+ bool Cmd_ResLook(int argc, const char **argv);
+ bool Cmd_CurrentInfo(int argc, const char **argv);
+ bool Cmd_RunList(int argc, const char **argv);
+ bool Cmd_Kill(int argc, const char **argv);
+ bool Cmd_Nuke(int argc, const char **argv);
+ bool Cmd_Var(int argc, const char **argv);
+ bool Cmd_Rect(int argc, const char **argv);
+ bool Cmd_Clear(int argc, const char **argv);
+ bool Cmd_DebugOn(int argc, const char **argv);
+ bool Cmd_DebugOff(int argc, const char **argv);
+ bool Cmd_SaveRest(int argc, const char **argv);
+ bool Cmd_TimeOn(int argc, const char **argv);
+ bool Cmd_TimeOff(int argc, const char **argv);
+ bool Cmd_Text(int argc, const char **argv);
+ bool Cmd_ShowVar(int argc, const char **argv);
+ bool Cmd_HideVar(int argc, const char **argv);
+ bool Cmd_Version(int argc, const char **argv);
+ bool Cmd_AnimTest(int argc, const char **argv);
+ bool Cmd_TextTest(int argc, const char **argv);
+ bool Cmd_LineTest(int argc, const char **argv);
+ bool Cmd_Events(int argc, const char **argv);
+ bool Cmd_Sfx(int argc, const char **argv);
+ bool Cmd_English(int argc, const char **argv);
+ bool Cmd_Finnish(int argc, const char **argv);
+ bool Cmd_Polish(int argc, const char **argv);
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/controls.cpp b/engines/sword2/controls.cpp
new file mode 100644
index 0000000000..df1b38c83e
--- /dev/null
+++ b/engines/sword2/controls.cpp
@@ -0,0 +1,1416 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/rect.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/controls.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+
+#define MAX_STRING_LEN 64 // 20 was too low; better to be safe ;)
+#define CHARACTER_OVERLAP 2 // overlap characters by 3 pixels
+
+// our fonts start on SPACE character (32)
+#define SIZE_OF_CHAR_SET (256 - 32)
+
+namespace Sword2 {
+
+static int baseSlot = 0;
+
+class Widget;
+
+/**
+ * Base class for all widgets.
+ */
+
+class Widget {
+protected:
+ Sword2Engine *_vm;
+ Dialog *_parent;
+
+ SpriteInfo *_sprites;
+
+ struct WidgetSurface {
+ byte *_surface;
+ bool _original;
+ };
+
+ WidgetSurface *_surfaces;
+ int _numStates;
+ int _state;
+
+ Common::Rect _hitRect;
+
+public:
+ Widget(Dialog *parent, int states);
+
+ virtual ~Widget();
+
+ void createSurfaceImage(int state, uint32 res, int x, int y, uint32 pc);
+ void linkSurfaceImage(Widget *from, int state, int x, int y);
+
+ void createSurfaceImages(uint32 res, int x, int y);
+ void linkSurfaceImages(Widget *from, int x, int y);
+
+ void setHitRect(int x, int y, int width, int height);
+ bool isHit(int16 x, int16 y);
+
+ void setState(int state);
+ int getState();
+
+ virtual void paint(Common::Rect *clipRect = NULL);
+
+ virtual void onMouseEnter() {}
+ virtual void onMouseExit() {}
+ virtual void onMouseMove(int x, int y) {}
+ virtual void onMouseDown(int x, int y) {}
+ virtual void onMouseUp(int x, int y) {}
+ virtual void onWheelUp(int x, int y) {}
+ virtual void onWheelDown(int x, int y) {}
+ virtual void onKey(KeyboardEvent *ke) {}
+ virtual void onTick() {}
+
+ virtual void releaseMouse(int x, int y) {}
+};
+
+/**
+ * This class is used to draw text in dialogs, buttons, etc.
+ */
+
+class FontRendererGui {
+private:
+ Sword2Engine *_vm;
+
+ struct Glyph {
+ byte *_data;
+ int _width;
+ int _height;
+ };
+
+ Glyph _glyph[SIZE_OF_CHAR_SET];
+
+ int _fontId;
+
+public:
+ enum {
+ kAlignLeft,
+ kAlignRight,
+ kAlignCenter
+ };
+
+ FontRendererGui(Sword2Engine *vm, int fontId);
+ ~FontRendererGui();
+
+ void fetchText(uint32 textId, byte *buf);
+
+ int getCharWidth(byte c);
+ int getCharHeight(byte c);
+
+ int getTextWidth(byte *text);
+ int getTextWidth(uint32 textId);
+
+ void drawText(byte *text, int x, int y, int alignment = kAlignLeft);
+ void drawText(uint32 textId, int x, int y, int alignment = kAlignLeft);
+};
+
+FontRendererGui::FontRendererGui(Sword2Engine *vm, int fontId)
+ : _vm(vm), _fontId(fontId) {
+ byte *font = _vm->_resman->openResource(fontId);
+ SpriteInfo sprite;
+
+ sprite.type = RDSPR_NOCOMPRESSION | RDSPR_TRANS;
+
+ for (int i = 0; i < SIZE_OF_CHAR_SET; i++) {
+ byte *frame = _vm->fetchFrameHeader(font, i);
+
+ FrameHeader frame_head;
+
+ frame_head.read(frame);
+
+ sprite.data = frame + FrameHeader::size();
+ sprite.w = frame_head.width;
+ sprite.h = frame_head.height;
+ _vm->_screen->createSurface(&sprite, &_glyph[i]._data);
+ _glyph[i]._width = frame_head.width;
+ _glyph[i]._height = frame_head.height;
+ }
+
+ _vm->_resman->closeResource(fontId);
+}
+
+FontRendererGui::~FontRendererGui() {
+ for (int i = 0; i < SIZE_OF_CHAR_SET; i++)
+ _vm->_screen->deleteSurface(_glyph[i]._data);
+}
+
+void FontRendererGui::fetchText(uint32 textId, byte *buf) {
+ byte *data = _vm->fetchTextLine(_vm->_resman->openResource(textId / SIZE), textId & 0xffff);
+ int i;
+
+ for (i = 0; data[i + 2]; i++) {
+ if (buf)
+ buf[i] = data[i + 2];
+ }
+
+ buf[i] = 0;
+ _vm->_resman->closeResource(textId / SIZE);
+}
+
+int FontRendererGui::getCharWidth(byte c) {
+ if (c < 32)
+ return 0;
+ return _glyph[c - 32]._width;
+}
+
+int FontRendererGui::getCharHeight(byte c) {
+ if (c < 32)
+ return 0;
+ return _glyph[c - 32]._height;
+}
+
+int FontRendererGui::getTextWidth(byte *text) {
+ int textWidth = 0;
+
+ for (int i = 0; text[i]; i++)
+ if (text[i] >= ' ')
+ textWidth += (getCharWidth(text[i]) - CHARACTER_OVERLAP);
+ return textWidth;
+}
+
+int FontRendererGui::getTextWidth(uint32 textId) {
+ byte text[MAX_STRING_LEN];
+
+ fetchText(textId, text);
+ return getTextWidth(text);
+}
+
+void FontRendererGui::drawText(byte *text, int x, int y, int alignment) {
+ SpriteInfo sprite;
+ int i;
+
+ if (alignment != kAlignLeft) {
+ int textWidth = getTextWidth(text);
+
+ switch (alignment) {
+ case kAlignRight:
+ x -= textWidth;
+ break;
+ case kAlignCenter:
+ x -= (textWidth / 2);
+ break;
+ }
+ }
+
+ sprite.x = x;
+ sprite.y = y;
+
+ for (i = 0; text[i]; i++) {
+ if (text[i] >= ' ') {
+ sprite.w = getCharWidth(text[i]);
+ sprite.h = getCharHeight(text[i]);
+
+ _vm->_screen->drawSurface(&sprite, _glyph[text[i] - 32]._data);
+
+ sprite.x += (getCharWidth(text[i]) - CHARACTER_OVERLAP);
+ }
+ }
+}
+
+void FontRendererGui::drawText(uint32 textId, int x, int y, int alignment) {
+ byte text[MAX_STRING_LEN];
+
+ fetchText(textId, text);
+ drawText(text, x, y, alignment);
+}
+
+//
+// Dialog class functions
+//
+
+Dialog::Dialog(Sword2Engine *vm)
+ : _numWidgets(0), _finish(false), _result(0), _vm(vm) {
+ _vm->_screen->setFullPalette(CONTROL_PANEL_PALETTE);
+ _vm->_screen->clearScene();
+ _vm->_screen->updateDisplay();
+
+ // Usually the mouse pointer will already be "normal", but not always.
+ _vm->_mouse->setMouse(NORMAL_MOUSE_ID);
+}
+
+Dialog::~Dialog() {
+ for (int i = 0; i < _numWidgets; i++)
+ delete _widgets[i];
+ _vm->_screen->clearScene();
+ _vm->_screen->updateDisplay();
+}
+
+void Dialog::registerWidget(Widget *widget) {
+ if (_numWidgets < MAX_WIDGETS)
+ _widgets[_numWidgets++] = widget;
+}
+
+void Dialog::paint() {
+ _vm->_screen->clearScene();
+ for (int i = 0; i < _numWidgets; i++)
+ _widgets[i]->paint();
+}
+
+void Dialog::setResult(int result) {
+ _result = result;
+ _finish = true;
+}
+
+int Dialog::runModal() {
+ uint32 oldFilter = _vm->setInputEventFilter(0);
+
+ int i;
+
+ paint();
+
+ int oldMouseX = -1;
+ int oldMouseY = -1;
+
+ while (!_finish) {
+ // So that the menu icons will reach their full size
+ _vm->_mouse->processMenu();
+ _vm->_screen->updateDisplay(false);
+
+ int newMouseX, newMouseY;
+
+ _vm->_mouse->getPos(newMouseX, newMouseY);
+
+ newMouseY += 40;
+
+ MouseEvent *me = _vm->mouseEvent();
+ KeyboardEvent *ke = _vm->keyboardEvent();
+
+ if (ke) {
+ if (ke->keycode == 27)
+ setResult(0);
+ else if (ke->keycode == '\n' || ke->keycode == '\r')
+ setResult(1);
+ }
+
+ int oldHit = -1;
+ int newHit = -1;
+
+ // Find out which widget the mouse was over the last time, and
+ // which it is currently over. This assumes the widgets do not
+ // overlap.
+
+ for (i = 0; i < _numWidgets; i++) {
+ if (_widgets[i]->isHit(oldMouseX, oldMouseY))
+ oldHit = i;
+ if (_widgets[i]->isHit(newMouseX, newMouseY))
+ newHit = i;
+ }
+
+ // Was the mouse inside a widget the last time?
+
+ if (oldHit >= 0) {
+ if (newHit != oldHit)
+ _widgets[oldHit]->onMouseExit();
+ }
+
+ // Is the mouse currently in a widget?
+
+ if (newHit >= 0) {
+ if (newHit != oldHit)
+ _widgets[newHit]->onMouseEnter();
+
+ if (me) {
+ switch (me->buttons) {
+ case RD_LEFTBUTTONDOWN:
+ _widgets[newHit]->onMouseDown(newMouseX, newMouseY);
+ break;
+ case RD_LEFTBUTTONUP:
+ _widgets[newHit]->onMouseUp(newMouseX, newMouseY);
+ break;
+ case RD_WHEELUP:
+ _widgets[newHit]->onWheelUp(newMouseX, newMouseY);
+ break;
+ case RD_WHEELDOWN:
+ _widgets[newHit]->onWheelDown(newMouseX, newMouseY);
+ break;
+ }
+ }
+ }
+
+ // Some events are passed to the widgets regardless of where
+ // the mouse cursor is.
+
+ for (i = 0; i < _numWidgets; i++) {
+ if (me && me->buttons == RD_LEFTBUTTONUP) {
+ // So that slider widgets will know when the
+ // user releases the mouse button, even if the
+ // cursor is outside of the slider's hit area.
+ _widgets[i]->releaseMouse(newMouseX, newMouseY);
+ }
+
+ // This is to make it easier to drag the slider widget
+
+ if (newMouseX != oldMouseX || newMouseY != oldMouseY)
+ _widgets[i]->onMouseMove(newMouseX, newMouseY);
+
+ if (ke)
+ _widgets[i]->onKey(ke);
+
+ _widgets[i]->onTick();
+ }
+
+ oldMouseX = newMouseX;
+ oldMouseY = newMouseY;
+
+ _vm->_system->delayMillis(20);
+
+ if (_vm->_quit)
+ setResult(0);
+ }
+
+ _vm->setInputEventFilter(oldFilter);
+ return _result;
+}
+
+//
+// Widget functions
+//
+
+Widget::Widget(Dialog *parent, int states)
+ : _vm(parent->_vm), _parent(parent), _numStates(states), _state(0) {
+ _sprites = (SpriteInfo *)calloc(states, sizeof(SpriteInfo));
+ _surfaces = (WidgetSurface *)calloc(states, sizeof(WidgetSurface));
+
+ _hitRect.left = _hitRect.right = _hitRect.top = _hitRect.bottom = -1;
+}
+
+Widget::~Widget() {
+ for (int i = 0; i < _numStates; i++) {
+ if (_surfaces[i]._original)
+ _vm->_screen->deleteSurface(_surfaces[i]._surface);
+ }
+ free(_sprites);
+ free(_surfaces);
+}
+
+void Widget::createSurfaceImage(int state, uint32 res, int x, int y, uint32 pc) {
+ byte *file, *colTablePtr = NULL;
+ AnimHeader anim_head;
+ FrameHeader frame_head;
+ CdtEntry cdt_entry;
+ uint32 spriteType = RDSPR_TRANS;
+
+ // open anim resource file, point to base
+ file = _vm->_resman->openResource(res);
+
+ byte *frame = _vm->fetchFrameHeader(file, pc);
+
+ anim_head.read(_vm->fetchAnimHeader(file));
+ cdt_entry.read(_vm->fetchCdtEntry(file, pc));
+ frame_head.read(frame);
+
+ // If the frame is flipped. (Only really applicable to frames using
+ // offsets.)
+
+ if (cdt_entry.frameType & FRAME_FLIPPED)
+ spriteType |= RDSPR_FLIP;
+
+ // Which compression was used?
+
+ switch (anim_head.runTimeComp) {
+ case NONE:
+ spriteType |= RDSPR_NOCOMPRESSION;
+ break;
+ case RLE256:
+ spriteType |= RDSPR_RLE256;
+ break;
+ case RLE16:
+ spriteType |= RDSPR_RLE256;
+ // Points to just after last cdt_entry, i.e. start of colour
+ // table
+ colTablePtr = _vm->fetchAnimHeader(file) + AnimHeader::size()
+ + anim_head.noAnimFrames * CdtEntry::size();
+ break;
+ }
+
+ _sprites[state].x = x;
+ _sprites[state].y = y;
+ _sprites[state].w = frame_head.width;
+ _sprites[state].h = frame_head.height;
+ _sprites[state].scale = 0;
+ _sprites[state].type = spriteType;
+ _sprites[state].blend = anim_head.blend;
+
+ // Points to just after frame header, ie. start of sprite data
+ _sprites[state].data = frame + FrameHeader::size();
+
+ _vm->_screen->createSurface(&_sprites[state], &_surfaces[state]._surface);
+ _surfaces[state]._original = true;
+
+ // Release the anim resource
+ _vm->_resman->closeResource(res);
+}
+
+void Widget::linkSurfaceImage(Widget *from, int state, int x, int y) {
+ _sprites[state].x = x;
+ _sprites[state].y = y;
+ _sprites[state].w = from->_sprites[state].w;
+ _sprites[state].h = from->_sprites[state].h;
+ _sprites[state].scale = from->_sprites[state].scale;
+ _sprites[state].type = from->_sprites[state].type;
+ _sprites[state].blend = from->_sprites[state].blend;
+
+ _surfaces[state]._surface = from->_surfaces[state]._surface;
+ _surfaces[state]._original = false;
+}
+
+void Widget::createSurfaceImages(uint32 res, int x, int y) {
+ for (int i = 0; i < _numStates; i++)
+ createSurfaceImage(i, res, x, y, i);
+}
+
+void Widget::linkSurfaceImages(Widget *from, int x, int y) {
+ for (int i = 0; i < from->_numStates; i++)
+ linkSurfaceImage(from, i, x, y);
+}
+
+void Widget::setHitRect(int x, int y, int width, int height) {
+ _hitRect.left = x;
+ _hitRect.right = x + width;
+ _hitRect.top = y;
+ _hitRect.bottom = y + height;
+}
+
+bool Widget::isHit(int16 x, int16 y) {
+ return _hitRect.left >= 0 && _hitRect.contains(x, y);
+}
+
+void Widget::setState(int state) {
+ if (state != _state) {
+ _state = state;
+ paint();
+ }
+}
+
+int Widget::getState() {
+ return _state;
+}
+
+void Widget::paint(Common::Rect *clipRect) {
+ _vm->_screen->drawSurface(&_sprites[_state], _surfaces[_state]._surface, clipRect);
+}
+
+/**
+ * Standard button class.
+ */
+
+class Button : public Widget {
+public:
+ Button(Dialog *parent, int x, int y, int w, int h)
+ : Widget(parent, 2) {
+ setHitRect(x, y, w, h);
+ }
+
+ virtual void onMouseExit() {
+ setState(0);
+ }
+
+ virtual void onMouseDown(int x, int y) {
+ setState(1);
+ }
+
+ virtual void onMouseUp(int x, int y) {
+ if (getState() != 0) {
+ setState(0);
+ _parent->onAction(this);
+ }
+ }
+};
+
+/**
+ * Scroll buttons are used to scroll the savegame list. The difference between
+ * this and a normal button is that we want this to repeat.
+ */
+
+class ScrollButton : public Widget {
+private:
+ uint32 _holdCounter;
+
+public:
+ ScrollButton(Dialog *parent, int x, int y, int w, int h)
+ : Widget(parent, 2), _holdCounter(0) {
+ setHitRect(x, y, w, h);
+ }
+
+ virtual void onMouseExit() {
+ setState(0);
+ }
+
+ virtual void onMouseDown(int x, int y) {
+ setState(1);
+ _parent->onAction(this);
+ _holdCounter = 0;
+ }
+
+ virtual void onMouseUp(int x, int y) {
+ setState(0);
+ }
+
+ virtual void onTick() {
+ if (getState() != 0) {
+ _holdCounter++;
+ if (_holdCounter > 16 && (_holdCounter % 4) == 0)
+ _parent->onAction(this);
+ }
+ }
+};
+
+/**
+ * A switch is a button that changes state when clicked, and keeps that state
+ * until clicked again.
+ */
+
+class Switch : public Widget {
+private:
+ bool _holding, _value;
+ int _upState, _downState;
+
+public:
+ Switch(Dialog *parent, int x, int y, int w, int h)
+ : Widget(parent, 2), _holding(false), _value(false),
+ _upState(0), _downState(1) {
+ setHitRect(x, y, w, h);
+ }
+
+ // The sound mute switches have 0 as their "down" state and 1 as
+ // their "up" state, so this function is needed to get consistent
+ // behaviour.
+
+ void reverseStates() {
+ _upState = 1;
+ _downState = 0;
+ }
+
+ void setValue(bool value) {
+ _value = value;
+ if (_value)
+ setState(_downState);
+ else
+ setState(_upState);
+ }
+
+ bool getValue() {
+ return _value;
+ }
+
+ virtual void onMouseExit() {
+ if (_holding && !_value)
+ setState(_upState);
+ _holding = false;
+ }
+
+ virtual void onMouseDown(int x, int y) {
+ _holding = true;
+ setState(_downState);
+ }
+
+ virtual void onMouseUp(int x, int y) {
+ if (_holding) {
+ _holding = false;
+ _value = !_value;
+ if (_value)
+ setState(_downState);
+ else
+ setState(_upState);
+ _parent->onAction(this, getState());
+ }
+ }
+};
+
+/**
+ * A slider is used to specify a value within a pre-defined range.
+ */
+
+class Slider : public Widget {
+private:
+ Widget *_background;
+ bool _dragging;
+ int _value, _targetValue;
+ int _maxValue;
+ int _valueStep;
+ int _dragOffset;
+
+ int posFromValue(int value) {
+ return _hitRect.left + (value * (_hitRect.width() - 38)) / _maxValue;
+ }
+
+ int valueFromPos(int x) {
+ return (int)((double)(_maxValue * (x - _hitRect.left)) / (double)(_hitRect.width() - 38) + 0.5);
+ }
+
+public:
+ Slider(Dialog *parent, Widget *background, int max,
+ int x, int y, int w, int h, int step, Widget *base = NULL)
+ : Widget(parent, 1), _background(background),
+ _dragging(false), _value(0), _targetValue(0),
+ _maxValue(max), _valueStep(step) {
+ setHitRect(x, y, w, h);
+
+ if (_valueStep <= 0)
+ _valueStep = 1;
+
+ if (base)
+ linkSurfaceImages(base, x, y);
+ else
+ createSurfaceImages(3406, x, y);
+ }
+
+ virtual void paint(Common::Rect *clipRect = NULL) {
+ // This will redraw a bit more than is strictly necessary,
+ // but I doubt that will make any noticeable difference.
+
+ _background->paint(&_hitRect);
+ Widget::paint(clipRect);
+ }
+
+ void setValue(int value) {
+ _value = value;
+ _targetValue = value;
+ _sprites[0].x = posFromValue(_value);
+ paint();
+ }
+
+ int getValue() {
+ return _value;
+ }
+
+ virtual void onMouseMove(int x, int y) {
+ if (_dragging) {
+ int newX = x - _dragOffset;
+ int newValue;
+
+ if (newX < _hitRect.left)
+ newX = _hitRect.left;
+ else if (newX + 38 > _hitRect.right)
+ newX = _hitRect.right - 38;
+
+ _sprites[0].x = newX;
+
+ newValue = valueFromPos(newX);
+ if (newValue != _value) {
+ _value = newValue;
+ _targetValue = newValue;
+ _parent->onAction(this, newValue);
+ }
+
+ paint();
+ }
+ }
+
+ virtual void onMouseDown(int x, int y) {
+ if (x >= _sprites[0].x && x < _sprites[0].x + 38) {
+ _dragging = true;
+ _dragOffset = x - _sprites[0].x;
+ } else if (x < _sprites[0].x) {
+ if (_targetValue >= _valueStep)
+ _targetValue -= _valueStep;
+ else
+ _targetValue = 0;
+ } else {
+ if (_targetValue < _maxValue - _valueStep)
+ _targetValue += _valueStep;
+ else
+ _targetValue = _maxValue;
+ }
+ }
+
+ virtual void releaseMouse(int x, int y) {
+ if (_dragging)
+ _dragging = false;
+ }
+
+ virtual void onTick() {
+ if (!_dragging) {
+ int target = posFromValue(_targetValue);
+
+ if (target != _sprites[0].x) {
+ if (target < _sprites[0].x) {
+ _sprites[0].x -= 4;
+ if (_sprites[0].x < target)
+ _sprites[0].x = target;
+ } else if (target > _sprites[0].x) {
+ _sprites[0].x += 4;
+ if (_sprites[0].x > target)
+ _sprites[0].x = target;
+ }
+
+ int newValue = valueFromPos(_sprites[0].x);
+ if (newValue != _value) {
+ _value = newValue;
+ _parent->onAction(this, newValue);
+ }
+
+ paint();
+ }
+ }
+ }
+};
+
+/**
+ * The "mini" dialog.
+ */
+
+MiniDialog::MiniDialog(Sword2Engine *vm, uint32 headerTextId, uint32 okTextId, uint32 cancelTextId) : Dialog(vm) {
+ _headerTextId = headerTextId;
+ _okTextId = okTextId;
+ _cancelTextId = cancelTextId;
+
+ _fr = new FontRendererGui(_vm, _vm->_controlsFontId);
+
+ _panel = new Widget(this, 1);
+ _panel->createSurfaceImages(1996, 203, 104);
+
+ _okButton = new Button(this, 243, 214, 24, 24);
+ _okButton->createSurfaceImages(2002, 243, 214);
+
+ _cancelButton = new Button(this, 243, 276, 24, 24);
+ _cancelButton->linkSurfaceImages(_okButton, 243, 276);
+
+ registerWidget(_panel);
+ registerWidget(_okButton);
+ registerWidget(_cancelButton);
+}
+
+MiniDialog::~MiniDialog() {
+ delete _fr;
+}
+
+void MiniDialog::paint() {
+ Dialog::paint();
+
+ if (_headerTextId)
+ _fr->drawText(_headerTextId, 310, 134, FontRendererGui::kAlignCenter);
+ _fr->drawText(_okTextId, 270, 214);
+ _fr->drawText(_cancelTextId, 270, 276);
+}
+
+void MiniDialog::onAction(Widget *widget, int result) {
+ if (widget == _okButton)
+ setResult(1);
+ else if (widget == _cancelButton)
+ setResult(0);
+}
+
+StartDialog::StartDialog(Sword2Engine *vm) : MiniDialog(vm, 0) {}
+
+int StartDialog::runModal() {
+ while (1) {
+ MiniDialog startDialog(_vm, 0, TEXT_RESTART, TEXT_RESTORE);
+
+ if (startDialog.runModal())
+ return 1;
+
+ if (_vm->_quit)
+ return 0;
+
+ RestoreDialog restoreDialog(_vm);
+
+ if (restoreDialog.runModal())
+ return 0;
+
+ if (_vm->_quit)
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * The restart dialog.
+ */
+
+RestartDialog::RestartDialog(Sword2Engine *vm) : MiniDialog(vm, TEXT_RESTART) {}
+
+int RestartDialog::runModal() {
+ int result = MiniDialog::runModal();
+
+ if (result)
+ _vm->restartGame();
+
+ return result;
+}
+
+/**
+ * The quit dialog.
+ */
+
+QuitDialog::QuitDialog(Sword2Engine *vm) : MiniDialog(vm, TEXT_QUIT) {}
+
+int QuitDialog::runModal() {
+ int result = MiniDialog::runModal();
+
+ if (result)
+ _vm->closeGame();
+
+ return result;
+}
+
+/**
+ * The game settings dialog.
+ */
+
+OptionsDialog::OptionsDialog(Sword2Engine *vm) : Dialog(vm) {
+ _fr = new FontRendererGui(_vm, _vm->_controlsFontId);
+
+ _mixer = _vm->_mixer;
+
+ _panel = new Widget(this, 1);
+ _panel->createSurfaceImages(3405, 0, 40);
+
+ _objectLabelsSwitch = new Switch(this, 304, 100, 53, 32);
+ _objectLabelsSwitch->createSurfaceImages(3687, 304, 100);
+
+ _subtitlesSwitch = new Switch(this, 510, 100, 53, 32);
+ _subtitlesSwitch->linkSurfaceImages(_objectLabelsSwitch, 510, 100);
+
+ _reverseStereoSwitch = new Switch(this, 304, 293, 53, 32);
+ _reverseStereoSwitch->linkSurfaceImages(_objectLabelsSwitch, 304, 293);
+
+ _musicSwitch = new Switch(this, 516, 157, 40, 32);
+ _musicSwitch->createSurfaceImages(3315, 516, 157);
+ _musicSwitch->reverseStates();
+
+ _speechSwitch = new Switch(this, 516, 205, 40, 32);
+ _speechSwitch->linkSurfaceImages(_musicSwitch, 516, 205);
+ _speechSwitch->reverseStates();
+
+ _fxSwitch = new Switch(this, 516, 250, 40, 32);
+ _fxSwitch->linkSurfaceImages(_musicSwitch, 516, 250);
+ _fxSwitch->reverseStates();
+
+ int volStep = Audio::Mixer::kMaxMixerVolume / 10;
+
+ _musicSlider = new Slider(this, _panel, Audio::Mixer::kMaxMixerVolume, 309, 161, 170, 27, volStep);
+ _speechSlider = new Slider(this, _panel, Audio::Mixer::kMaxMixerVolume, 309, 208, 170, 27, volStep, _musicSlider);
+ _fxSlider = new Slider(this, _panel, Audio::Mixer::kMaxMixerVolume, 309, 254, 170, 27, volStep, _musicSlider);
+ _gfxSlider = new Slider(this, _panel, 3, 309, 341, 170, 27, 1, _musicSlider);
+
+ _gfxPreview = new Widget(this, 4);
+ _gfxPreview->createSurfaceImages(256, 495, 310);
+
+ _okButton = new Button(this, 203, 382, 53, 32);
+ _okButton->createSurfaceImages(901, 203, 382);
+
+ _cancelButton = new Button(this, 395, 382, 53, 32);
+ _cancelButton->linkSurfaceImages(_okButton, 395, 382);
+
+ registerWidget(_panel);
+ registerWidget(_objectLabelsSwitch);
+ registerWidget(_subtitlesSwitch);
+ registerWidget(_reverseStereoSwitch);
+ registerWidget(_musicSwitch);
+ registerWidget(_speechSwitch);
+ registerWidget(_fxSwitch);
+ registerWidget(_musicSlider);
+ registerWidget(_speechSlider);
+ registerWidget(_fxSlider);
+ registerWidget(_gfxSlider);
+ registerWidget(_gfxPreview);
+ registerWidget(_okButton);
+ registerWidget(_cancelButton);
+
+ _objectLabelsSwitch->setValue(_vm->_mouse->getObjectLabels());
+ _subtitlesSwitch->setValue(_vm->getSubtitles());
+ _reverseStereoSwitch->setValue(_vm->_sound->isReverseStereo());
+ _musicSwitch->setValue(!_vm->_sound->isMusicMute());
+ _speechSwitch->setValue(!_vm->_sound->isSpeechMute());
+ _fxSwitch->setValue(!_vm->_sound->isFxMute());
+
+ _musicSlider->setValue(_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType));
+ _speechSlider->setValue(_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType));
+ _fxSlider->setValue(_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType));
+
+ _gfxSlider->setValue(_vm->_screen->getRenderLevel());
+ _gfxPreview->setState(_vm->_screen->getRenderLevel());
+}
+
+OptionsDialog::~OptionsDialog() {
+ delete _fr;
+}
+
+void OptionsDialog::paint() {
+ Dialog::paint();
+
+ int maxWidth = 0;
+ int width;
+
+ uint32 alignTextIds[] = {
+ TEXT_OBJECT_LABELS,
+ TEXT_MUSIC_VOLUME,
+ TEXT_SPEECH_VOLUME,
+ TEXT_FX_VOLUME,
+ TEXT_GFX_QUALITY,
+ TEXT_REVERSE_STEREO
+ };
+
+ for (int i = 0; i < ARRAYSIZE(alignTextIds); i++) {
+ width = _fr->getTextWidth(alignTextIds[i]);
+ if (width > maxWidth)
+ maxWidth = width;
+ }
+
+ _fr->drawText(TEXT_OPTIONS, 321, 55, FontRendererGui::kAlignCenter);
+ _fr->drawText(TEXT_SUBTITLES, 500, 103, FontRendererGui::kAlignRight);
+ _fr->drawText(TEXT_OBJECT_LABELS, 299 - maxWidth, 103);
+ _fr->drawText(TEXT_MUSIC_VOLUME, 299 - maxWidth, 161);
+ _fr->drawText(TEXT_SPEECH_VOLUME, 299 - maxWidth, 208);
+ _fr->drawText(TEXT_FX_VOLUME, 299 - maxWidth, 254);
+ _fr->drawText(TEXT_REVERSE_STEREO, 299 - maxWidth, 296);
+ _fr->drawText(TEXT_GFX_QUALITY, 299 - maxWidth, 341);
+ _fr->drawText(TEXT_OK, 193, 382, FontRendererGui::kAlignRight);
+ _fr->drawText(TEXT_CANCEL, 385, 382, FontRendererGui::kAlignRight);
+}
+
+void OptionsDialog::onAction(Widget *widget, int result) {
+ // Since there is music playing while the dialog is displayed we need
+ // to update music volume immediately.
+
+ if (widget == _musicSwitch) {
+ _vm->_sound->muteMusic(result != 0);
+ } else if (widget == _musicSlider) {
+ _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, result);
+ _vm->_sound->muteMusic(result == 0);
+ _musicSwitch->setValue(result != 0);
+ } else if (widget == _speechSlider) {
+ _speechSwitch->setValue(result != 0);
+ } else if (widget == _fxSlider) {
+ _fxSwitch->setValue(result != 0);
+ } else if (widget == _gfxSlider) {
+ _gfxPreview->setState(result);
+ _vm->_screen->setRenderLevel(result);
+ } else if (widget == _okButton) {
+ // Apply the changes
+ _vm->setSubtitles(_subtitlesSwitch->getValue());
+ _vm->_mouse->setObjectLabels(_objectLabelsSwitch->getValue());
+ _vm->_sound->muteMusic(!_musicSwitch->getValue());
+ _vm->_sound->muteSpeech(!_speechSwitch->getValue());
+ _vm->_sound->muteFx(!_fxSwitch->getValue());
+ _vm->_sound->setReverseStereo(_reverseStereoSwitch->getValue());
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, _musicSlider->getValue());
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _speechSlider->getValue());
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _fxSlider->getValue());
+ _vm->_screen->setRenderLevel(_gfxSlider->getValue());
+
+ _vm->writeSettings();
+ setResult(1);
+ } else if (widget == _cancelButton) {
+ // Revert the changes
+ _vm->readSettings();
+ setResult(0);
+ }
+}
+
+// Slot button actions. Note that keyboard input generates positive actions
+
+enum {
+ kSelectSlot = -1,
+ kDeselectSlot = -2,
+ kWheelDown = -3,
+ kWheelUp = -4,
+ kStartEditing = -5,
+ kCursorTick = -6
+};
+
+class Slot : public Widget {
+private:
+ int _mode;
+ FontRendererGui *_fr;
+ byte _text[SAVE_DESCRIPTION_LEN];
+ bool _clickable;
+ bool _editable;
+
+public:
+ Slot(Dialog *parent, int x, int y, int w, int h)
+ : Widget(parent, 2), _clickable(false), _editable(false) {
+ setHitRect(x, y, w, h);
+ _text[0] = 0;
+ }
+
+ void setMode(int mode) {
+ _mode = mode;
+ }
+
+ void setClickable(bool clickable) {
+ _clickable = clickable;
+ }
+
+ void setEditable(bool editable) {
+ _editable = editable;
+ _vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, editable);
+ }
+
+ bool isEditable() {
+ return _editable;
+ }
+
+ void setText(FontRendererGui *fr, int slot, byte *text) {
+ _fr = fr;
+ if (text)
+ sprintf((char *)_text, "%d. %s", slot, text);
+ else
+ sprintf((char *)_text, "%d. ", slot);
+ }
+
+ byte *getText() {
+ return &_text[0];
+ }
+
+ virtual void paint(Common::Rect *clipRect = NULL) {
+ Widget::paint();
+
+ // HACK: The main dialog is responsible for drawing the text
+ // when in editing mode.
+
+ if (!_editable)
+ _fr->drawText(_text, _sprites[0].x + 16, _sprites[0].y + 4 + 2 * getState());
+ }
+
+ virtual void onMouseDown(int x, int y) {
+ if (_clickable) {
+ if (getState() == 0) {
+ setState(1);
+ _parent->onAction(this, kSelectSlot);
+ if (_mode == kSaveDialog)
+ _parent->onAction(this, kStartEditing);
+ } else if (_mode == kRestoreDialog) {
+ setState(0);
+ _parent->onAction(this, kDeselectSlot);
+ }
+ }
+ }
+
+ virtual void onWheelUp(int x, int y) {
+ _parent->onAction(this, kWheelUp);
+ }
+
+ virtual void onWheelDown(int x, int y) {
+ _parent->onAction(this, kWheelDown);
+ }
+
+ virtual void onKey(KeyboardEvent *ke) {
+ if (_editable) {
+ if (ke->keycode == 8)
+ _parent->onAction(this, 8);
+ else if (ke->ascii >= ' ' && ke->ascii <= 255) {
+ // Accept the character if the font renderer
+ // has what looks like a valid glyph for it.
+ if (_fr->getCharWidth(ke->ascii))
+ _parent->onAction(this, ke->ascii);
+ }
+ }
+ }
+
+ virtual void onTick() {
+ if (_editable)
+ _parent->onAction(this, kCursorTick);
+ }
+
+ void setY(int y) {
+ for (int i = 0; i < _numStates; i++)
+ _sprites[i].y = y;
+ setHitRect(_hitRect.left, y, _hitRect.width(), _hitRect.height());
+ }
+
+ int getY() {
+ return _sprites[0].y;
+ }
+};
+
+SaveRestoreDialog::SaveRestoreDialog(Sword2Engine *vm, int mode) : Dialog(vm) {
+ int i;
+
+ _mode = mode;
+ _selectedSlot = -1;
+
+ // FIXME: The "control font" and the "red font" are currently always
+ // the same font, so one should be eliminated.
+
+ _fr1 = new FontRendererGui(_vm, _vm->_controlsFontId);
+ _fr2 = new FontRendererGui(_vm, _vm->_redFontId);
+
+ _panel = new Widget(this, 1);
+ _panel->createSurfaceImages(2016, 0, 40);
+
+ for (i = 0; i < 4; i++) {
+ _slotButton[i] = new Slot(this, 114, 0, 384, 36);
+ _slotButton[i]->createSurfaceImages(2006 + i, 114, 0);
+ _slotButton[i]->setMode(mode);
+ _slotButton[i + 4] = new Slot(this, 114, 0, 384, 36);
+ _slotButton[i + 4]->linkSurfaceImages(_slotButton[i], 114, 0);
+ _slotButton[i + 4]->setMode(mode);
+ }
+
+ updateSlots();
+
+ _zupButton = new ScrollButton(this, 516, 65, 17, 17);
+ _zupButton->createSurfaceImages(1982, 516, 65);
+
+ _upButton = new ScrollButton(this, 516, 85, 17, 17);
+ _upButton->createSurfaceImages(2067, 516, 85);
+
+ _downButton = new ScrollButton(this, 516, 329, 17, 17);
+ _downButton->createSurfaceImages(1986, 516, 329);
+
+ _zdownButton = new ScrollButton(this, 516, 350, 17, 17);
+ _zdownButton->createSurfaceImages(1988, 516, 350);
+
+ _okButton = new Button(this, 130, 377, 24, 24);
+ _okButton->createSurfaceImages(2002, 130, 377);
+
+ _cancelButton = new Button(this, 350, 377, 24, 24);
+ _cancelButton->linkSurfaceImages(_okButton, 350, 377);
+
+ registerWidget(_panel);
+
+ for (i = 0; i < 8; i++)
+ registerWidget(_slotButton[i]);
+
+ registerWidget(_zupButton);
+ registerWidget(_upButton);
+ registerWidget(_downButton);
+ registerWidget(_zdownButton);
+ registerWidget(_okButton);
+ registerWidget(_cancelButton);
+}
+
+SaveRestoreDialog::~SaveRestoreDialog() {
+ delete _fr1;
+ delete _fr2;
+}
+
+// There aren't really a hundred different button objects of course, there are
+// only eight. Re-arrange them to simulate scrolling.
+
+void SaveRestoreDialog::updateSlots() {
+ for (int i = 0; i < 8; i++) {
+ Slot *slot = _slotButton[(baseSlot + i) % 8];
+ FontRendererGui *fr;
+ byte description[SAVE_DESCRIPTION_LEN];
+
+ slot->setY(72 + i * 36);
+
+ if (baseSlot + i == _selectedSlot) {
+ slot->setEditable(_mode == kSaveDialog);
+ slot->setState(1);
+ fr = _fr2;
+ } else {
+ slot->setEditable(false);
+ slot->setState(0);
+ fr = _fr1;
+ }
+
+ if (_vm->getSaveDescription(baseSlot + i, description) == SR_OK) {
+ slot->setText(fr, baseSlot + i, description);
+ slot->setClickable(true);
+ } else {
+ slot->setText(fr, baseSlot + i, NULL);
+ slot->setClickable(_mode == kSaveDialog);
+ }
+
+ if (slot->isEditable())
+ drawEditBuffer(slot);
+ else
+ slot->paint();
+ }
+}
+
+void SaveRestoreDialog::drawEditBuffer(Slot *slot) {
+ if (_selectedSlot == -1)
+ return;
+
+ // This will redraw a bit more than is strictly necessary, but I doubt
+ // that will make any noticeable difference.
+
+ slot->paint();
+ _fr2->drawText(_editBuffer, 130, 78 + (_selectedSlot - baseSlot) * 36);
+}
+
+void SaveRestoreDialog::onAction(Widget *widget, int result) {
+ if (widget == _zupButton) {
+ if (baseSlot > 0) {
+ if (baseSlot >= 8)
+ baseSlot -= 8;
+ else
+ baseSlot = 0;
+ updateSlots();
+ }
+ } else if (widget == _upButton) {
+ if (baseSlot > 0) {
+ baseSlot--;
+ updateSlots();
+ }
+ } else if (widget == _downButton) {
+ if (baseSlot < 92) {
+ baseSlot++;
+ updateSlots();
+ }
+ } else if (widget == _zdownButton) {
+ if (baseSlot < 92) {
+ if (baseSlot <= 84)
+ baseSlot += 8;
+ else
+ baseSlot = 92;
+ updateSlots();
+ }
+ } else if (widget == _okButton) {
+ setResult(1);
+ } else if (widget == _cancelButton) {
+ setResult(0);
+ } else {
+ Slot *slot = (Slot *)widget;
+ int textWidth;
+ byte tmp;
+ int i;
+ int j;
+
+ switch (result) {
+ case kWheelUp:
+ onAction(_upButton);
+ break;
+ case kWheelDown:
+ onAction(_downButton);
+ break;
+ case kSelectSlot:
+ case kDeselectSlot:
+ if (result == kSelectSlot)
+ _selectedSlot = baseSlot + (slot->getY() - 72) / 35;
+ else if (result == kDeselectSlot)
+ _selectedSlot = -1;
+
+ for (i = 0; i < 8; i++)
+ if (widget == _slotButton[i])
+ break;
+
+ for (j = 0; j < 8; j++) {
+ if (j != i) {
+ _slotButton[j]->setEditable(false);
+ _slotButton[j]->setState(0);
+ }
+ }
+ break;
+ case kStartEditing:
+ if (_selectedSlot >= 10)
+ _firstPos = 5;
+ else
+ _firstPos = 4;
+
+ strcpy((char *)_editBuffer, (char *)slot->getText());
+ _editPos = strlen((char *)_editBuffer);
+ _cursorTick = 0;
+ _editBuffer[_editPos] = '_';
+ _editBuffer[_editPos + 1] = 0;
+ slot->setEditable(true);
+ drawEditBuffer(slot);
+ break;
+ case kCursorTick:
+ _cursorTick++;
+ if (_cursorTick == 7) {
+ _editBuffer[_editPos] = ' ';
+ drawEditBuffer(slot);
+ } else if (_cursorTick == 14) {
+ _cursorTick = 0;
+ _editBuffer[_editPos] = '_';
+ drawEditBuffer(slot);
+ }
+ break;
+ case 8:
+ if (_editPos > _firstPos) {
+ _editBuffer[_editPos - 1] = _editBuffer[_editPos];
+ _editBuffer[_editPos--] = 0;
+ drawEditBuffer(slot);
+ }
+ break;
+ default:
+ tmp = _editBuffer[_editPos];
+ _editBuffer[_editPos] = 0;
+ textWidth = _fr2->getTextWidth(_editBuffer);
+ _editBuffer[_editPos] = tmp;
+
+ if (textWidth < 340 && _editPos < SAVE_DESCRIPTION_LEN - 2) {
+ _editBuffer[_editPos + 1] = _editBuffer[_editPos];
+ _editBuffer[_editPos + 2] = 0;
+ _editBuffer[_editPos++] = result;
+ drawEditBuffer(slot);
+ }
+ break;
+ }
+ }
+}
+
+void SaveRestoreDialog::paint() {
+ Dialog::paint();
+
+ _fr1->drawText((_mode == kRestoreDialog) ? TEXT_RESTORE : TEXT_SAVE, 165, 377);
+ _fr1->drawText(TEXT_CANCEL, 382, 377);
+}
+
+void SaveRestoreDialog::setResult(int result) {
+ if (result) {
+ if (_selectedSlot == -1)
+ return;
+
+ if (_mode == kSaveDialog) {
+ if (_editPos <= _firstPos)
+ return;
+ }
+ }
+
+ Dialog::setResult(result);
+}
+
+int SaveRestoreDialog::runModal() {
+ int result = Dialog::runModal();
+
+ if (result) {
+ switch (_mode) {
+ case kSaveDialog:
+ // Remove the cursor character from the savegame name
+ _editBuffer[_editPos] = 0;
+
+ if (_vm->saveGame(_selectedSlot, (byte *)&_editBuffer[_firstPos]) != SR_OK)
+ result = 0;
+ break;
+ case kRestoreDialog:
+ if (_vm->restoreGame(_selectedSlot) != SR_OK)
+ result = 0;
+ break;
+ }
+ }
+
+ return result;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/controls.h b/engines/sword2/controls.h
new file mode 100644
index 0000000000..719489b3ae
--- /dev/null
+++ b/engines/sword2/controls.h
@@ -0,0 +1,183 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _CONTROL_S
+#define _CONTROL_S
+
+#include "sword2/defs.h"
+
+#define MAX_WIDGETS 25
+
+namespace Sword2 {
+
+class Sword2Engine;
+class FontRendererGui;
+class Widget;
+class Switch;
+class Slider;
+class Button;
+class ScrollButton;
+class Slot;
+
+enum {
+ kSaveDialog,
+ kRestoreDialog
+};
+
+/**
+ * Base class for all dialogs.
+ */
+
+class Dialog {
+private:
+ int _numWidgets;
+ Widget *_widgets[MAX_WIDGETS];
+ bool _finish;
+ int _result;
+
+public:
+ Sword2Engine *_vm;
+
+ Dialog(Sword2Engine *vm);
+ virtual ~Dialog();
+
+ void registerWidget(Widget *widget);
+
+ virtual void paint();
+ virtual void setResult(int result);
+
+ virtual int runModal();
+
+ virtual void onAction(Widget *widget, int result = 0) {}
+};
+
+class OptionsDialog : public Dialog {
+private:
+ FontRendererGui *_fr;
+ Widget *_panel;
+ Switch *_objectLabelsSwitch;
+ Switch *_subtitlesSwitch;
+ Switch *_reverseStereoSwitch;
+ Switch *_musicSwitch;
+ Switch *_speechSwitch;
+ Switch *_fxSwitch;
+ Slider *_musicSlider;
+ Slider *_speechSlider;
+ Slider *_fxSlider;
+ Slider *_gfxSlider;
+ Widget *_gfxPreview;
+ Button *_okButton;
+ Button *_cancelButton;
+
+ Audio::Mixer *_mixer;
+
+public:
+ OptionsDialog(Sword2Engine *vm);
+ ~OptionsDialog();
+
+ virtual void paint();
+ virtual void onAction(Widget *widget, int result = 0);
+};
+
+class SaveRestoreDialog : public Dialog {
+private:
+ int _mode, _selectedSlot;
+ byte _editBuffer[SAVE_DESCRIPTION_LEN];
+ int _editPos, _firstPos;
+ int _cursorTick;
+
+ FontRendererGui *_fr1;
+ FontRendererGui *_fr2;
+ Widget *_panel;
+ Slot *_slotButton[8];
+ ScrollButton *_zupButton;
+ ScrollButton *_upButton;
+ ScrollButton *_downButton;
+ ScrollButton *_zdownButton;
+ Button *_okButton;
+ Button *_cancelButton;
+
+public:
+ SaveRestoreDialog(Sword2Engine *vm, int mode);
+ ~SaveRestoreDialog();
+
+ void updateSlots();
+ void drawEditBuffer(Slot *slot);
+
+ virtual void onAction(Widget *widget, int result = 0);
+ virtual void paint();
+ virtual void setResult(int result);
+ virtual int runModal();
+};
+
+/**
+ * A "mini" dialog is usually a yes/no question, but also used for the
+ * restart/restore dialog at the beginning of the game.
+ */
+
+class MiniDialog : public Dialog {
+private:
+ uint32 _headerTextId;
+ uint32 _okTextId;
+ uint32 _cancelTextId;
+ FontRendererGui *_fr;
+ Widget *_panel;
+ Button *_okButton;
+ Button *_cancelButton;
+
+public:
+ MiniDialog(Sword2Engine *vm, uint32 headerTextId, uint32 okTextId = TEXT_OK, uint32 cancelTextId = TEXT_CANCEL);
+ virtual ~MiniDialog();
+ virtual void paint();
+ virtual void onAction(Widget *widget, int result = 0);
+};
+
+class StartDialog : public MiniDialog {
+public:
+ StartDialog(Sword2Engine *vm);
+ virtual int runModal();
+};
+
+class RestartDialog : public MiniDialog {
+public:
+ RestartDialog(Sword2Engine *vm);
+ virtual int runModal();
+};
+
+class QuitDialog : public MiniDialog {
+public:
+ QuitDialog(Sword2Engine *vm);
+ virtual int runModal();
+};
+
+class SaveDialog : public SaveRestoreDialog {
+public:
+ SaveDialog(Sword2Engine *vm) : SaveRestoreDialog(vm, kSaveDialog) {}
+};
+
+class RestoreDialog : public SaveRestoreDialog {
+public:
+ RestoreDialog(Sword2Engine *vm) : SaveRestoreDialog(vm, kRestoreDialog) {}
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/d_draw.cpp b/engines/sword2/d_draw.cpp
new file mode 100644
index 0000000000..7f278996fb
--- /dev/null
+++ b/engines/sword2/d_draw.cpp
@@ -0,0 +1,71 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "sword2/sword2.h"
+#include "sword2/mouse.h"
+
+namespace Sword2 {
+
+/**
+ * @return the graphics detail setting
+ */
+
+int8 Screen::getRenderLevel() {
+ return _renderLevel;
+}
+
+void Screen::setRenderLevel(int8 level) {
+ _renderLevel = level;
+
+ switch (_renderLevel) {
+ case 0:
+ // Lowest setting: no fancy stuff
+ _renderCaps = 0;
+ break;
+ case 1:
+ // Medium-low setting: transparency-blending
+ _renderCaps = RDBLTFX_SPRITEBLEND;
+ break;
+ case 2:
+ // Medium-high setting: transparency-blending + shading
+ _renderCaps = RDBLTFX_SPRITEBLEND | RDBLTFX_SHADOWBLEND;
+ break;
+ case 3:
+ // Highest setting: transparency-blending + shading +
+ // edge-blending + improved stretching
+ _renderCaps = RDBLTFX_SPRITEBLEND | RDBLTFX_SHADOWBLEND | RDBLTFX_EDGEBLEND;
+ break;
+ }
+}
+
+/**
+ * Fill the screen buffer with palette colour zero. Note that it does not
+ * touch the menu areas of the screen.
+ */
+
+void Screen::clearScene() {
+ memset(_buffer + MENUDEEP * _screenWide, 0, _screenWide * RENDERDEEP);
+ _needFullRedraw = true;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/debug.cpp b/engines/sword2/debug.cpp
new file mode 100644
index 0000000000..53fc200241
--- /dev/null
+++ b/engines/sword2/debug.cpp
@@ -0,0 +1,377 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/console.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/memory.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+#include "sword2/router.h"
+
+namespace Sword2 {
+
+void Debugger::clearDebugTextBlocks() {
+ uint8 blockNo = 0;
+
+ while (blockNo < MAX_DEBUG_TEXTS && _debugTextBlocks[blockNo] > 0) {
+ // kill the system text block
+ _vm->_fontRenderer->killTextBloc(_debugTextBlocks[blockNo]);
+
+ // clear this element of our array of block numbers
+ _debugTextBlocks[blockNo] = 0;
+
+ blockNo++;
+ }
+}
+
+void Debugger::makeDebugTextBlock(char *text, int16 x, int16 y) {
+ uint8 blockNo = 0;
+
+ while (blockNo < MAX_DEBUG_TEXTS && _debugTextBlocks[blockNo] > 0)
+ blockNo++;
+
+ assert(blockNo < MAX_DEBUG_TEXTS);
+
+ _debugTextBlocks[blockNo] = _vm->_fontRenderer->buildNewBloc((byte *)text, x, y, 640 - x, 0, RDSPR_DISPLAYALIGN, CONSOLE_FONT_ID, NO_JUSTIFICATION);
+}
+
+void Debugger::buildDebugText() {
+ char buf[128];
+
+ int32 showVarNo; // for variable watching
+ int32 showVarPos;
+ int32 varNo;
+
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ // clear the array of text block numbers for the debug text
+ clearDebugTextBlocks();
+
+ // mouse coords
+ // print mouse coords beside mouse-marker, if it's being displayed
+ if (_displayMouseMarker) {
+ int mouseX, mouseY;
+
+ _vm->_mouse->getPos(mouseX, mouseY);
+
+ sprintf(buf, "%d,%d", mouseX + screenInfo->scroll_offset_x, mouseY + screenInfo->scroll_offset_y);
+ if (mouseX > 560)
+ makeDebugTextBlock(buf, mouseX - 50, mouseY - 15);
+ else
+ makeDebugTextBlock(buf, mouseX + 5, mouseY - 15);
+ }
+
+ // mouse area coords
+
+ // defining a mouse area the easy way, by creating a box on-screen
+ if (_draggingRectangle || _vm->_logic->readVar(SYSTEM_TESTING_ANIMS)) {
+ // so we can see what's behind the lines
+ _rectFlicker = !_rectFlicker;
+
+ sprintf(buf, "x1=%d", _rectX1);
+ makeDebugTextBlock(buf, 0, 120);
+
+ sprintf(buf, "y1=%d", _rectY1);
+ makeDebugTextBlock(buf, 0, 135);
+
+ sprintf(buf, "x2=%d", _rectX2);
+ makeDebugTextBlock(buf, 0, 150);
+
+ sprintf(buf, "y2=%d", _rectY2);
+ makeDebugTextBlock(buf, 0, 165);
+ }
+
+ // testingSnR indicator
+
+ if (_testingSnR) { // see fnAddHuman()
+ sprintf(buf, "TESTING LOGIC STABILITY!");
+ makeDebugTextBlock(buf, 0, 105);
+ }
+
+#ifdef SWORD2_DEBUG
+ // speed-up indicator
+
+ if (_vm->_renderSkip) { // see sword2.cpp
+ sprintf(buf, "SKIPPING FRAMES FOR SPEED-UP!");
+ makeDebugTextBlock(buf, 0, 120);
+ }
+#endif
+
+ // debug info at top of screen - enabled/disabled as one complete unit
+
+ if (_displayTime) {
+ int32 time = _vm->getMillis();
+
+ if ((time - _startTime) / 1000 >= 10000)
+ _startTime = time;
+
+ time -= _startTime;
+ sprintf(buf, "Time %.2d:%.2d:%.2d.%.3d", (time / 3600000) % 60, (time / 60000) % 60, (time / 1000) % 60, time % 1000);
+ makeDebugTextBlock(buf, 500, 360);
+ sprintf(buf, "Game %d", _vm->_gameCycle);
+ makeDebugTextBlock(buf, 500, 380);
+ }
+
+ // current text number & speech-sample resource id
+
+ if (_displayTextNumbers) {
+ if (_textNumber) {
+ if (_vm->_logic->readVar(SYSTEM_TESTING_TEXT)) {
+ if (_vm->_logic->readVar(SYSTEM_WANT_PREVIOUS_LINE))
+ sprintf(buf, "backwards");
+ else
+ sprintf(buf, "forwards");
+
+ makeDebugTextBlock(buf, 0, 340);
+ }
+
+ sprintf(buf, "res: %d", _textNumber / SIZE);
+ makeDebugTextBlock(buf, 0, 355);
+
+ sprintf(buf, "pos: %d", _textNumber & 0xffff);
+ makeDebugTextBlock(buf, 0, 370);
+
+ sprintf(buf, "TEXT: %d", _vm->_logic->_officialTextNumber);
+ makeDebugTextBlock(buf, 0, 385);
+ }
+ }
+
+ // resource number currently being checking for animation
+
+ if (_vm->_logic->readVar(SYSTEM_TESTING_ANIMS)) {
+ sprintf(buf, "trying resource %d", _vm->_logic->readVar(SYSTEM_TESTING_ANIMS));
+ makeDebugTextBlock(buf, 0, 90);
+ }
+
+ // general debug info
+
+ if (_displayDebugText) {
+ byte name[NAME_LEN];
+
+/*
+ // CD in use
+ sprintf(buf, "CD-%d", currentCD);
+ makeDebugTextBlock(buf, 0, 0);
+*/
+
+ // mouse coords & object pointed to
+
+ if (_vm->_logic->readVar(CLICKED_ID))
+ sprintf(buf, "last click at %d,%d (id %d: %s)",
+ _vm->_logic->readVar(MOUSE_X),
+ _vm->_logic->readVar(MOUSE_Y),
+ _vm->_logic->readVar(CLICKED_ID),
+ _vm->_resman->fetchName(_vm->_logic->readVar(CLICKED_ID), name));
+ else
+ sprintf(buf, "last click at %d,%d (---)",
+ _vm->_logic->readVar(MOUSE_X),
+ _vm->_logic->readVar(MOUSE_Y));
+
+ makeDebugTextBlock(buf, 0, 15);
+
+ uint32 mouseTouching = _vm->_mouse->getMouseTouching();
+
+ int mouseX, mouseY;
+
+ _vm->_mouse->getPos(mouseX, mouseY);
+
+ if (mouseTouching)
+ sprintf(buf, "mouse %d,%d (id %d: %s)",
+ mouseX + screenInfo->scroll_offset_x,
+ mouseY + screenInfo->scroll_offset_y,
+ mouseTouching,
+ _vm->_resman->fetchName(mouseTouching, name));
+ else
+ sprintf(buf, "mouse %d,%d (not touching)",
+ mouseX + screenInfo->scroll_offset_x,
+ mouseY + screenInfo->scroll_offset_y);
+
+ makeDebugTextBlock(buf, 0, 30);
+
+ // player coords & graphic info
+ // if player objct has a graphic
+
+ if (_graphAnimRes)
+ sprintf(buf, "player %d,%d %s (%d) #%d/%d",
+ screenInfo->player_feet_x,
+ screenInfo->player_feet_y,
+ _vm->_resman->fetchName(_graphAnimRes, name),
+ _graphAnimRes,
+ _graphAnimPc,
+ _graphNoFrames);
+ else
+ sprintf(buf, "player %d,%d --- %d",
+ screenInfo->player_feet_x,
+ screenInfo->player_feet_y,
+ _graphAnimPc);
+
+ makeDebugTextBlock(buf, 0, 45);
+
+ // frames-per-second counter
+
+ sprintf(buf, "fps %d", _vm->_screen->getFps());
+ makeDebugTextBlock(buf, 440, 0);
+
+ // location number
+
+ sprintf(buf, "location=%d", _vm->_logic->readVar(LOCATION));
+ makeDebugTextBlock(buf, 440, 15);
+
+ // "result" variable
+
+ sprintf(buf, "result=%d", _vm->_logic->readVar(RESULT));
+ makeDebugTextBlock(buf, 440, 30);
+
+ // no. of events in event list
+
+ sprintf(buf, "events=%d", _vm->_logic->countEvents());
+ makeDebugTextBlock(buf, 440, 45);
+
+ // sprite list usage
+
+ sprintf(buf, "bgp0: %d/%d", _vm->_screen->getCurBgp0(), MAX_bgp0_sprites);
+ makeDebugTextBlock(buf, 560, 0);
+
+ sprintf(buf, "bgp1: %d/%d", _vm->_screen->getCurBgp1(), MAX_bgp1_sprites);
+ makeDebugTextBlock(buf, 560, 15);
+
+ sprintf(buf, "back: %d/%d", _vm->_screen->getCurBack(), MAX_back_sprites);
+ makeDebugTextBlock(buf, 560, 30);
+
+ sprintf(buf, "sort: %d/%d", _vm->_screen->getCurSort(), MAX_sort_sprites);
+ makeDebugTextBlock(buf, 560, 45);
+
+ sprintf(buf, "fore: %d/%d", _vm->_screen->getCurFore(), MAX_fore_sprites);
+ makeDebugTextBlock(buf, 560, 60);
+
+ sprintf(buf, "fgp0: %d/%d", _vm->_screen->getCurFgp0(), MAX_fgp0_sprites);
+ makeDebugTextBlock(buf, 560, 75);
+
+ sprintf(buf, "fgp1: %d/%d", _vm->_screen->getCurFgp1(), MAX_fgp1_sprites);
+ makeDebugTextBlock(buf, 560, 90);
+
+ // largest layer & sprite
+
+ // NB. Strings already constructed in Build_display.cpp
+ makeDebugTextBlock(_vm->_screen->getLargestLayerInfo(), 0, 60);
+ makeDebugTextBlock(_vm->_screen->getLargestSpriteInfo(), 0, 75);
+
+ // "waiting for person" indicator - set form fnTheyDo and
+ // fnTheyDoWeWait
+
+ if (_speechScriptWaiting) {
+ sprintf(buf, "script waiting for %s (%d)",
+ _vm->_resman->fetchName(_speechScriptWaiting, name),
+ _speechScriptWaiting);
+ makeDebugTextBlock(buf, 0, 90);
+ }
+
+ // variable watch display
+
+ showVarPos = 115; // y-coord for first showVar
+
+ for (showVarNo = 0; showVarNo < MAX_SHOWVARS; showVarNo++) {
+ varNo = _showVar[showVarNo]; // get variable number
+
+ // if non-zero ie. cannot watch 'id' but not needed
+ // anyway because it changes throughout the logic loop
+
+ if (varNo) {
+ sprintf(buf, "var(%d) = %d", varNo, _vm->_logic->readVar(varNo));
+ makeDebugTextBlock(buf, 530, showVarPos);
+ showVarPos += 15; // next line down
+ }
+ }
+
+ // memory indicator - this should come last, to show all the
+ // sprite blocks above!
+
+ uint32 totAlloc = _vm->_memory->getTotAlloc();
+ int16 numBlocks = _vm->_memory->getNumBlocks();
+
+ if (totAlloc < 1024)
+ sprintf(buf, "%u bytes in %d memory blocks", totAlloc, numBlocks);
+ else if (totAlloc < 1024 * 1024)
+ sprintf(buf, "%uK in %d memory blocks", totAlloc / 1024, numBlocks);
+ else
+ sprintf(buf, "%.02fM in %d memory blocks", totAlloc / 1048576., numBlocks);
+
+ makeDebugTextBlock(buf, 0, 0);
+ }
+}
+
+void Debugger::drawDebugGraphics() {
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+ // walk-grid
+
+ if (_displayWalkGrid)
+ _vm->_logic->_router->plotWalkGrid();
+
+ // player feet coord marker
+
+ if (_displayPlayerMarker)
+ plotCrossHair(screenInfo->player_feet_x, screenInfo->player_feet_y, 215);
+
+ // mouse marker & coords
+
+ if (_displayMouseMarker) {
+ int mouseX, mouseY;
+
+ _vm->_mouse->getPos(mouseX, mouseY);
+
+ plotCrossHair(mouseX + screenInfo->scroll_offset_x, mouseY + screenInfo->scroll_offset_y, 215);
+ }
+
+ // mouse area rectangle / sprite box rectangle when testing anims
+
+ if (_vm->_logic->readVar(SYSTEM_TESTING_ANIMS)) {
+ // draw box around current frame
+ drawRect(_rectX1, _rectY1, _rectX2, _rectY2, 184);
+ } else if (_draggingRectangle) {
+ // defining a mouse area the easy way, by creating a box
+ // on-screen
+ if (_rectFlicker)
+ drawRect(_rectX1, _rectY1, _rectX2, _rectY2, 184);
+ }
+}
+
+void Debugger::plotCrossHair(int16 x, int16 y, uint8 pen) {
+ _vm->_screen->plotPoint(x, y, pen);
+
+ _vm->_screen->drawLine(x - 2, y, x - 5, y, pen);
+ _vm->_screen->drawLine(x + 2, y, x + 5, y, pen);
+
+ _vm->_screen->drawLine(x, y - 2, x, y - 5, pen);
+ _vm->_screen->drawLine(x, y + 2, x, y + 5, pen);
+}
+
+void Debugger::drawRect(int16 x1, int16 y1, int16 x2, int16 y2, uint8 pen) {
+ _vm->_screen->drawLine(x1, y1, x2, y1, pen); // top edge
+ _vm->_screen->drawLine(x1, y2, x2, y2, pen); // bottom edge
+ _vm->_screen->drawLine(x1, y1, x1, y2, pen); // left edge
+ _vm->_screen->drawLine(x2, y1, x2, y2, pen); // right edge
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/debug.h b/engines/sword2/debug.h
new file mode 100644
index 0000000000..9b0b40f9ec
--- /dev/null
+++ b/engines/sword2/debug.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef D_DEBUG
+#define D_DEBUG
+
+// FIXME: I don't know how large this constant used to be
+#define MAX_DEBUG_TEXTS 55
+
+#define MAX_SHOWVARS 15
+
+namespace Sword2 {
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/defs.h b/engines/sword2/defs.h
new file mode 100644
index 0000000000..4acb484f34
--- /dev/null
+++ b/engines/sword2/defs.h
@@ -0,0 +1,205 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef DEFS
+#define DEFS
+
+#define SIZE 0x10000 // 65536 items per section
+#define NuSIZE 0xffff // & with this
+
+// Return codes
+
+enum {
+ // Generic error codes
+ RD_OK = 0x00000000,
+ RDERR_UNKNOWN = 0x00000001,
+ RDERR_OUTOFMEMORY = 0x00000003,
+ RDERR_INVALIDFILENAME = 0x00000004,
+
+ // Drawing error codes
+ RDERR_DECOMPRESSION = 0x00010007,
+
+ // Sprite drawing error codes
+ RDERR_NOTIMPLEMENTED = 0x00060001,
+ RDERR_NOTCLOSED = 0x00050005,
+ RDERR_NOTOPEN = 0x00050006,
+
+ // Menubar error codes
+ RDERR_INVALIDMENU = 0x00060000,
+ RDERR_INVALIDPOCKET = 0x00060001,
+ RDERR_INVALIDCOMMAND = 0x00060002,
+
+ // Palette fading error codes
+ RDERR_FADEINCOMPLETE = 0x00070000,
+
+ // Sound engine error codes
+ RDERR_SPEECHPLAYING = 0x00080004,
+ RDERR_SPEECHNOTPLAYING = 0x00080005,
+ RDERR_INVALIDWAV = 0x00080006,
+ RDERR_FXALREADYOPEN = 0x00080009,
+ RDERR_FXNOTOPEN = 0x0008000B,
+ RDERR_INVALIDID = 0x0008000D
+};
+
+// Text ids for the control panel etc.
+
+enum {
+ TEXT_OK = 0x08EB0000,
+ TEXT_CANCEL = 0x08EB0001,
+ TEXT_RESTORE = 0x08EB0002,
+ TEXT_SAVE = 0x08EB0003,
+ TEXT_QUIT = 0x08EB0004,
+ TEXT_RESTART = 0x08EB0005,
+ TEXT_OPTIONS = 0x08EB000A,
+ TEXT_SUBTITLES = 0x08EB000B,
+ TEXT_OBJECT_LABELS = 0x08EB000C,
+ TEXT_MUSIC_VOLUME = 0x08EB000E,
+ TEXT_SPEECH_VOLUME = 0x08EB000F,
+ TEXT_FX_VOLUME = 0x08EB0010,
+ TEXT_GFX_QUALITY = 0x08EB0011,
+ TEXT_REVERSE_STEREO = 0x08EB0015,
+ TEXT_RESTORE_CANT_OPEN = 0x0CBA017E,
+ TEXT_RESTORE_INCOMPATIBLE = 0x0CBA017F,
+ TEXT_RESTORE_FAILED = 0x0CBA0181,
+ TEXT_SAVE_CANT_OPEN = 0x0CBA0182,
+ TEXT_SAVE_FAILED = 0x0CBA0184
+};
+
+// Always 8 (George object used for Nico player character as well)
+#define CUR_PLAYER_ID 8
+
+// Global variable references
+
+enum {
+ ID = 0,
+ RESULT = 1,
+ PLAYER_ACTION = 2,
+ // CUR_PLAYER_ID = 3,
+ PLAYER_ID = 305,
+ TALK_FLAG = 13,
+
+ MOUSE_X = 4,
+ MOUSE_Y = 5,
+ LEFT_BUTTON = 109,
+ RIGHT_BUTTON = 110,
+ CLICKED_ID = 178,
+
+ IN_SUBJECT = 6,
+ COMBINE_BASE = 7,
+ OBJECT_HELD = 14,
+
+ SPEECH_ID = 9,
+ INS1 = 10,
+ INS2 = 11,
+ INS3 = 12,
+ INS4 = 60,
+ INS5 = 61,
+ INS_COMMAND = 59,
+
+ PLAYER_FEET_X = 141,
+ PLAYER_FEET_Y = 142,
+ PLAYER_CUR_DIR = 937,
+
+ // for debug.cpp
+ LOCATION = 62,
+
+ // so scripts can force scroll offsets
+ SCROLL_X = 345,
+ SCROLL_Y = 346,
+
+ EXIT_CLICK_ID = 710,
+ EXIT_FADING = 713,
+
+ SYSTEM_TESTING_ANIMS = 912,
+ SYSTEM_TESTING_TEXT = 1230,
+ SYSTEM_WANT_PREVIOUS_LINE = 1245,
+
+ // 1=on 0=off (set in fnAddHuman and fnNoHuman)
+ MOUSE_AVAILABLE = 686,
+
+ // used in fnChoose
+ AUTO_SELECTED = 1115,
+
+ // see fnStartConversation and fnChooser
+ CHOOSER_COUNT_FLAG = 15,
+
+ // signifies a demo mode
+ DEMO = 1153,
+
+ // Indicates to script whether this is the Playstation version.
+ // PSXFLAG = 1173,
+
+ // for the poor PSX so it knows what language is running.
+ // GAME_LANGUAGE = 111,
+
+ // 1 = dead
+ DEAD = 1256,
+
+ // If set indicates that the speech anim is to run through only once.
+ SPEECHANIMFLAG = 1278,
+
+ // for the engine
+ SCROLL_OFFSET_X = 1314
+};
+
+// Resource IDs
+
+enum {
+ // mouse mointers - It's pretty much safe to do it like this
+ NORMAL_MOUSE_ID = 17,
+ SCROLL_LEFT_MOUSE_ID = 1440,
+ SCROLL_RIGHT_MOUSE_ID = 1441,
+
+ // Console Font - does not use game text - only English required
+ CONSOLE_FONT_ID = 340,
+
+ // Speech Font
+ ENGLISH_SPEECH_FONT_ID = 341,
+ FINNISH_SPEECH_FONT_ID = 956,
+ POLISH_SPEECH_FONT_ID = 955,
+
+ // Control Panel Font (and un-selected savegame descriptions)
+ ENGLISH_CONTROLS_FONT_ID = 2005,
+ FINNISH_CONTROLS_FONT_ID = 959,
+ POLISH_CONTROLS_FONT_ID = 3686,
+
+ // Red Font (for selected savegame descriptions)
+ // BS2 doesn't draw selected savegames in red, so I guess this is a
+ // left-over from BS1
+ ENGLISH_RED_FONT_ID = 2005, // 1998 // Redfont
+ FINNISH_RED_FONT_ID = 959, // 960 // FinRedFn
+ POLISH_RED_FONT_ID = 3686, // 3688 // PolRedFn
+
+ // Control panel palette resource id
+ CONTROL_PANEL_PALETTE = 261,
+
+ // res id's of the system menu icons
+ OPTIONS_ICON = 344,
+ QUIT_ICON = 335,
+ SAVE_ICON = 366,
+ RESTORE_ICON = 364,
+ RESTART_ICON = 342,
+
+ // conversation exit icon, 'EXIT' menu icon (used in fnChoose)
+ EXIT_ICON = 65
+};
+
+#endif
diff --git a/engines/sword2/events.cpp b/engines/sword2/events.cpp
new file mode 100644
index 0000000000..e8090414aa
--- /dev/null
+++ b/engines/sword2/events.cpp
@@ -0,0 +1,99 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+
+namespace Sword2 {
+
+void Logic::sendEvent(uint32 id, uint32 interact_id) {
+ for (int i = 0; i < ARRAYSIZE(_eventList); i++) {
+ if (_eventList[i].id == id || !_eventList[i].id) {
+ _eventList[i].id = id;
+ _eventList[i].interact_id = interact_id;
+ return;
+ }
+ }
+
+ error("sendEvent() ran out of event slots");
+}
+
+void Logic::setPlayerActionEvent(uint32 id, uint32 interact_id) {
+ // Full script id of action script number 2
+ sendEvent(id, (interact_id << 16) | 2);
+}
+
+int Logic::checkEventWaiting() {
+ for (int i = 0; i < ARRAYSIZE(_eventList); i++) {
+ if (_eventList[i].id == readVar(ID))
+ return 1;
+ }
+
+ return 0;
+}
+
+void Logic::startEvent() {
+ // call this from stuff like fnWalk
+ // you must follow with a return IR_TERMINATE
+
+ for (int i = 0; i < ARRAYSIZE(_eventList); i++) {
+ if (_eventList[i].id == readVar(ID)) {
+ logicOne(_eventList[i].interact_id);
+ _eventList[i].id = 0;
+ return;
+ }
+ }
+
+ error("startEvent() can't find event for id %d", readVar(ID));
+}
+
+void Logic::clearEvent(uint32 id) {
+ for (int i = 0; i < ARRAYSIZE(_eventList); i++) {
+ if (_eventList[i].id == id) {
+ _eventList[i].id = 0;
+ return;
+ }
+ }
+}
+
+void Logic::killAllIdsEvents(uint32 id) {
+ for (int i = 0; i < ARRAYSIZE(_eventList); i++) {
+ if (_eventList[i].id == id)
+ _eventList[i].id = 0;
+ }
+}
+
+// For the debugger
+
+uint32 Logic::countEvents() {
+ uint32 count = 0;
+
+ for (int i = 0; i < ARRAYSIZE(_eventList); i++) {
+ if (_eventList[i].id)
+ count++;
+ }
+
+ return count;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/function.cpp b/engines/sword2/function.cpp
new file mode 100644
index 0000000000..e749e7b497
--- /dev/null
+++ b/engines/sword2/function.cpp
@@ -0,0 +1,2479 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "common/file.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/build_display.h"
+#include "sword2/console.h"
+#include "sword2/interpreter.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+#include "sword2/router.h"
+#include "sword2/sound.h"
+#include "sword2/animation.h"
+
+namespace Sword2 {
+
+int32 Logic::fnTestFunction(int32 *params) {
+ // params: 0 address of a flag
+ return IR_CONT;
+}
+
+int32 Logic::fnTestFlags(int32 *params) {
+ // params: 0 value of flag
+ return IR_CONT;
+}
+
+int32 Logic::fnRegisterStartPoint(int32 *params) {
+ // params: 0 id of startup script to call - key
+ // 1 pointer to ascii message
+
+ int32 key = params[0];
+ char *name = (char *)decodePtr(params[1]);
+
+ _vm->registerStartPoint(key, name);
+ return IR_CONT;
+}
+
+int32 Logic::fnInitBackground(int32 *params) {
+ // this screen defines the size of the back buffer
+
+ // params: 0 res id of normal background layer - cannot be 0
+ // 1 1 yes 0 no for a new palette
+
+ _vm->_screen->initBackground(params[0], params[1]);
+ return IR_CONT;
+}
+
+/**
+ * This function is used by start scripts.
+ */
+
+int32 Logic::fnSetSession(int32 *params) {
+ // params: 0 id of new run list
+
+ expressChangeSession(params[0]);
+ return IR_CONT;
+}
+
+int32 Logic::fnBackSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteStatus(decodePtr(params[0]), BACK_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnSortSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteStatus(decodePtr(params[0]), SORT_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnForeSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteStatus(decodePtr(params[0]), FORE_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnRegisterMouse(int32 *params) {
+ // this call would be made from an objects service script 0
+ // the object would be one with no graphic but with a mouse - i.e. a
+ // floor or one whose mouse area is manually defined rather than
+ // intended to fit sprite shape
+
+ // params: 0 pointer to ObjectMouse or 0 for no write to mouse
+ // list
+
+ _vm->_mouse->registerMouse(decodePtr(params[0]), NULL);
+ return IR_CONT;
+}
+
+int32 Logic::fnAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 resource id of animation file
+
+ // Normal forward animation
+ return _router->doAnimate(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ params[2], false);
+}
+
+int32 Logic::fnRandom(int32 *params) {
+ // params: 0 min
+ // 1 max
+
+ writeVar(RESULT, _vm->_rnd.getRandomNumberRng(params[0], params[1]));
+ return IR_CONT;
+}
+
+int32 Logic::fnPreLoad(int32 *params) {
+ // Forces a resource into memory before it's "officially" opened for
+ // use. eg. if an anim needs to run on smoothly from another,
+ // "preloading" gets it into memory in advance to avoid the cacheing
+ // delay that normally occurs before the first frame.
+
+ // params: 0 resource to preload
+
+ _vm->_resman->openResource(params[0]);
+ _vm->_resman->closeResource(params[0]);
+ return IR_CONT;
+}
+
+int32 Logic::fnAddSubject(int32 *params) {
+ // params: 0 id
+ // 1 daves reference number
+ _vm->_mouse->addSubject(params[0], params[1]);
+ return IR_CONT;
+}
+
+int32 Logic::fnInteract(int32 *params) {
+ // Run targets action on a subroutine. Called by player on his base
+ // level 0 idle, for example.
+
+ // params: 0 id of target from which we derive action script
+ // reference
+
+ writeVar(PLAYER_ACTION, 0); // must clear this
+ logicUp((params[0] << 16) | 2); // 3rd script of clicked on id
+
+ // Out, up and around again - pc is saved for current level to be
+ // returned to.
+ return IR_GOSUB;
+}
+
+int32 Logic::fnChoose(int32 *params) {
+ // params: none
+
+ // This opcode is used to open the conversation menu. The human is
+ // switched off so there will be no normal mouse engine.
+
+ // The player's choice is piggy-backed on the standard opcode return
+ // values, to be used with the CP_JUMP_ON_RETURNED opcode. As far as I
+ // can tell, this is the only function that uses that feature.
+
+ uint32 response = _vm->_mouse->chooseMouse();
+
+ if (response == (uint32)-1)
+ return IR_REPEAT;
+
+ return IR_CONT | (response << 3);
+}
+
+/**
+ * Walk mega to (x,y,dir). Set RESULT to 0 if it succeeded. Otherwise, set
+ * RESULT to 1.
+ */
+
+int32 Logic::fnWalk(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 target x-coord
+ // 5 target y-coord
+ // 6 target direction (8 means end walk on ANY direction)
+
+ return _router->doWalk(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ decodePtr(params[2]),
+ decodePtr(params[3]),
+ params[4], params[5], params[6]);
+}
+
+/**
+ * Walk mega to start position of anim
+ */
+
+int32 Logic::fnWalkToAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 anim resource id
+
+ return _router->walkToAnim(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ decodePtr(params[2]),
+ decodePtr(params[3]),
+ params[4]);
+}
+
+/**
+ * Turn mega to the specified direction.
+ */
+
+int32 Logic::fnTurn(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 target direction
+
+ return _router->doFace(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ decodePtr(params[2]),
+ decodePtr(params[3]),
+ params[4]);
+}
+
+/**
+ * Stand mega at (x,y,dir)
+ * Sets up the graphic object, but also needs to set the new 'current_dir' in
+ * the mega object, so the router knows in future
+ */
+
+int32 Logic::fnStandAt(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 pointer to object's mega structure
+ // 2 target x-coord
+ // 3 target y-coord
+ // 4 target direction
+
+ _router->standAt(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ params[2], params[3], params[4]);
+ return IR_CONT;
+}
+
+/**
+ * Stand mega into the specified direction at current feet coords.
+ * Just needs to call standAt() with current feet coords.
+ */
+
+int32 Logic::fnStand(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 pointer to object's mega structure
+ // 2 target direction
+ byte *ob_mega = decodePtr(params[1]);
+
+ ObjectMega obMega(ob_mega);
+
+ _router->standAt(
+ decodePtr(params[0]),
+ ob_mega, obMega.getFeetX(), obMega.getFeetY(), params[2]);
+ return IR_CONT;
+}
+
+/**
+ * stand mega at end position of anim
+ */
+
+int32 Logic::fnStandAfterAnim(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 pointer to object's mega structure
+ // 2 anim resource id
+
+ _router->standAfterAnim(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ params[2]);
+ return IR_CONT;
+}
+
+int32 Logic::fnPause(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 number of game-cycles to pause
+
+ // NB. Pause-value of 0 causes script to continue, 1 causes a 1-cycle
+ // quit, 2 gives 2 cycles, etc.
+
+ ObjectLogic obLogic(decodePtr(params[0]));
+
+ if (obLogic.getLooping() == 0) {
+ obLogic.setLooping(1);
+ obLogic.setPause(params[1]);
+ }
+
+ if (obLogic.getPause()) {
+ obLogic.setPause(obLogic.getPause() - 1);
+ return IR_REPEAT;
+ }
+
+ obLogic.setLooping(0);
+ return IR_CONT;
+}
+
+int32 Logic::fnMegaTableAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to animation table
+
+ // Normal forward anim
+ return _router->megaTableAnimate(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ decodePtr(params[2]),
+ decodePtr(params[3]),
+ false);
+}
+
+int32 Logic::fnAddMenuObject(int32 *params) {
+ // params: 0 pointer to a MenuObject structure to copy down
+
+ _vm->_mouse->addMenuObject(decodePtr(params[0]));
+ return IR_CONT;
+}
+
+/**
+ * Start a conversation.
+ *
+ * Note that fnStartConversation() might accidentally be called every time the
+ * script loops back for another chooser, but we only want to reset the chooser
+ * count flag the first time this function is called, i.e. when the talk flag
+ * is zero.
+ */
+
+int32 Logic::fnStartConversation(int32 *params) {
+ // params: none
+
+ _vm->_mouse->startConversation();
+ return IR_CONT;
+}
+
+/**
+ * End a conversation.
+ */
+
+int32 Logic::fnEndConversation(int32 *params) {
+ // params: none
+
+ _vm->_mouse->endConversation();
+ return IR_CONT;
+}
+
+int32 Logic::fnSetFrame(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 resource id of animation file
+ // 2 frame flag (0=first 1=last)
+
+ int32 res = params[1];
+ assert(res);
+
+ // open the resource (& check it's valid)
+ byte *anim_file = _vm->_resman->openResource(res);
+
+ assert(_vm->_resman->fetchType(res) == ANIMATION_FILE);
+
+ // set up pointer to the animation header
+ AnimHeader anim_head;
+
+ anim_head.read(_vm->fetchAnimHeader(anim_file));
+
+ // set up anim resource in graphic object
+ ObjectGraphic obGraph(decodePtr(params[0]));
+
+ obGraph.setAnimResource(res);
+ obGraph.setAnimPc(params[2] ? anim_head.noAnimFrames - 1 : 0);
+
+ // Close the anim file and drop out of script
+ _vm->_resman->closeResource(obGraph.getAnimResource());
+ return IR_CONT;
+}
+
+int32 Logic::fnRandomPause(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 minimum number of game-cycles to pause
+ // 2 maximum number of game-cycles to pause
+
+ ObjectLogic obLogic(decodePtr(params[0]));
+ int32 pars[2];
+
+ if (obLogic.getLooping() == 0) {
+ pars[0] = params[1];
+ pars[1] = params[2];
+ fnRandom(pars);
+ pars[1] = readVar(RESULT);
+ }
+
+ pars[0] = params[0];
+ return fnPause(pars);
+}
+
+int32 Logic::fnRegisterFrame(int32 *params) {
+ // this call would be made from an objects service script 0
+
+ // params: 0 pointer to mouse structure or NULL for no write to
+ // mouse list (non-zero means write sprite-shape to
+ // mouse list)
+ // 1 pointer to graphic structure
+ // 2 pointer to mega structure or NULL if not a mega
+
+ _vm->_screen->registerFrame(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ decodePtr(params[2]));
+ return IR_CONT;
+}
+
+int32 Logic::fnNoSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteStatus(decodePtr(params[0]), NO_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnSendSync(int32 *params) {
+ // params: 0 sync's recipient
+ // 1 sync value
+
+ sendSync(params[0], params[1]);
+ return IR_CONT;
+}
+
+int32 Logic::fnUpdatePlayerStats(int32 *params) {
+ // engine needs to know certain info about the player
+
+ // params: 0 pointer to mega structure
+
+ ObjectMega obMega(decodePtr(params[0]));
+
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ screenInfo->player_feet_x = obMega.getFeetX();
+ screenInfo->player_feet_y = obMega.getFeetY();
+
+ // for the script
+ writeVar(PLAYER_FEET_X, obMega.getFeetX());
+ writeVar(PLAYER_FEET_Y, obMega.getFeetY());
+ writeVar(PLAYER_CUR_DIR, obMega.getCurDir());
+ writeVar(SCROLL_OFFSET_X, screenInfo->scroll_offset_x);
+
+ debug(5, "fnUpdatePlayerStats: %d %d",
+ obMega.getFeetX(), obMega.getFeetY());
+
+ return IR_CONT;
+}
+
+int32 Logic::fnPassGraph(int32 *params) {
+ // makes an engine local copy of passed ObjectGraphic - run script 4
+ // of an object to request this used by fnTurnTo(id) etc
+ //
+ // remember, we cannot simply read a compact any longer but instead
+ // must request it from the object itself
+
+ // params: 0 pointer to an ObjectGraphic structure
+
+ warning("fnPassGraph() is a no-op now");
+ return IR_CONT;
+}
+
+int32 Logic::fnInitFloorMouse(int32 *params) {
+ // params: 0 pointer to object's mouse structure
+
+ byte *ob_mouse = decodePtr(params[0]);
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ // floor is always lowest priority
+
+ ObjectMouse mouse;
+
+ mouse.x1 = 0;
+ mouse.y1 = 0;
+ mouse.x2 = screenInfo->screen_wide - 1;
+ mouse.y2 = screenInfo->screen_deep - 1;
+ mouse.priority = 9;
+ mouse.pointer = NORMAL_MOUSE_ID;
+
+ mouse.write(ob_mouse);
+ return IR_CONT;
+}
+
+int32 Logic::fnPassMega(int32 *params) {
+ // makes an engine local copy of passed mega_structure - run script 4
+ // of an object to request this used by fnTurnTo(id) etc
+ //
+ // remember, we cannot simply read a compact any longer but instead
+ // must request it from the object itself
+
+ // params: 0 pointer to a mega structure
+
+ memcpy(_engineMega, decodePtr(params[0]), ObjectMega::size());
+ return IR_CONT;
+}
+
+/**
+ * Turn mega to face point (x,y) on the floor
+ */
+
+int32 Logic::fnFaceXY(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 target x-coord
+ // 5 target y-coord
+
+ return _router->faceXY(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ decodePtr(params[2]),
+ decodePtr(params[3]),
+ params[4], params[5]);
+}
+
+/**
+ * Causes no more objects in this logic loop to be processed. The logic engine
+ * will restart at the beginning of the new list. The current screen will not
+ * be drawn!
+ */
+
+int32 Logic::fnEndSession(int32 *params) {
+ // params: 0 id of new run-list
+
+ // terminate current and change to next run-list
+ expressChangeSession(params[0]);
+
+ // stop the script - logic engine will now go around and the new
+ // screen will begin
+ return IR_STOP;
+}
+
+int32 Logic::fnNoHuman(int32 *params) {
+ // params: none
+
+ _vm->_mouse->noHuman();
+ return IR_CONT;
+}
+
+int32 Logic::fnAddHuman(int32 *params) {
+ // params: none
+
+ _vm->_mouse->addHuman();
+ return IR_CONT;
+}
+
+/**
+ * Wait for a target to become waiting, i.e. not busy.
+ */
+
+int32 Logic::fnWeWait(int32 *params) {
+ // params: 0 target
+
+ assert(_vm->_resman->fetchType(params[0]) == GAME_OBJECT);
+
+ // Run the target's get-speech-state script
+ runResScript(params[0], 5);
+
+ if (readVar(RESULT) == 0) {
+ // The target is busy. Try again.
+ _vm->_debugger->_speechScriptWaiting = params[0];
+ return IR_REPEAT;
+ }
+
+ // The target is waiting, i.e. not busy.
+
+ _vm->_debugger->_speechScriptWaiting = 0;
+ return IR_CONT;
+}
+
+/**
+ * Wait for a target to become waiting, i.e. not busy, send a command to it,
+ * then wait for it to finish.
+ */
+
+int32 Logic::fnTheyDoWeWait(int32 *params) {
+ // params: 0 pointer to ob_logic
+ // 1 target
+ // 2 command
+ // 3 ins1
+ // 4 ins2
+ // 5 ins3
+ // 6 ins4
+ // 7 ins5
+
+ assert(_vm->_resman->fetchType(params[1]) == GAME_OBJECT);
+
+ // Run the target's get-speech-state script
+ runResScript(params[1], 5);
+
+ ObjectLogic obLogic(decodePtr(params[0]));
+
+ if (readVar(RESULT) == 1 && readVar(INS_COMMAND) == 0 && obLogic.getLooping() == 0) {
+ // The target is waiting, i.e. not busy, and there is no other
+ // command queued. We haven't sent the command yet, so do it.
+
+ debug(5, "fnTheyDoWeWait: sending command to %d", params[1]);
+
+ _vm->_debugger->_speechScriptWaiting = params[1];
+ obLogic.setLooping(1);
+
+ writeVar(SPEECH_ID, params[1]);
+ writeVar(INS_COMMAND, params[2]);
+ writeVar(INS1, params[3]);
+ writeVar(INS2, params[4]);
+ writeVar(INS3, params[5]);
+ writeVar(INS4, params[6]);
+ writeVar(INS5, params[7]);
+
+ return IR_REPEAT;
+ }
+
+ if (obLogic.getLooping() == 0) {
+ // The command has not been sent yet. Keep waiting.
+ _vm->_debugger->_speechScriptWaiting = params[1];
+ return IR_REPEAT;
+ }
+
+ if (readVar(RESULT) == 0) {
+ // The command has been sent, and the target is busy doing it.
+ // Wait for it to finish.
+
+ debug(5, "fnTheyDoWeWait: Waiting for %d to finish", params[1]);
+
+ _vm->_debugger->_speechScriptWaiting = params[1];
+ return IR_REPEAT;
+ }
+
+ debug(5, "fnTheyDoWeWait: %d finished", params[1]);
+
+ obLogic.setLooping(0);
+ _vm->_debugger->_speechScriptWaiting = 0;
+ return IR_CONT;
+}
+
+/**
+ * Wait for a target to become waiting, i.e. not busy, then send a command to
+ * it.
+ */
+
+int32 Logic::fnTheyDo(int32 *params) {
+ // params: 0 target
+ // 1 command
+ // 2 ins1
+ // 3 ins2
+ // 4 ins3
+ // 5 ins4
+ // 6 ins5
+
+ assert(_vm->_resman->fetchType(params[0]) == GAME_OBJECT);
+
+ // Run the target's get-speech-state script
+ runResScript(params[0], 5);
+
+ if (readVar(RESULT) == 1 && !readVar(INS_COMMAND)) {
+ // The target is waiting, i.e. not busy, and there is no other
+ // command queued. Send the command.
+
+ debug(5, "fnTheyDo: sending command to %d", params[0]);
+
+ _vm->_debugger->_speechScriptWaiting = 0;
+
+ writeVar(SPEECH_ID, params[0]);
+ writeVar(INS_COMMAND, params[1]);
+ writeVar(INS1, params[2]);
+ writeVar(INS2, params[3]);
+ writeVar(INS3, params[4]);
+ writeVar(INS4, params[5]);
+ writeVar(INS5, params[6]);
+
+ return IR_CONT;
+ }
+
+ // The target is busy. Come back again next cycle.
+
+ _vm->_debugger->_speechScriptWaiting = params[0];
+ return IR_REPEAT;
+}
+
+/**
+ * Route to the left or right hand side of target id, if possible.
+ */
+
+int32 Logic::fnWalkToTalkToMega(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 id of target mega to face
+ // 5 separation
+
+ return _router->walkToTalkToMega(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ decodePtr(params[2]),
+ decodePtr(params[3]),
+ params[4], params[5]);
+}
+
+int32 Logic::fnFadeDown(int32 *params) {
+ // NONE means up! can only be called when screen is fully faded up -
+ // multiple calls wont have strange effects
+
+ // params: none
+
+ if (_vm->_screen->getFadeStatus() == RDFADE_NONE)
+ _vm->_screen->fadeDown();
+
+ return IR_CONT;
+}
+
+enum {
+ S_OB_GRAPHIC = 0,
+ S_OB_SPEECH = 1,
+ S_OB_LOGIC = 2,
+ S_OB_MEGA = 3,
+
+ S_TEXT = 4,
+ S_WAV = 5,
+ S_ANIM = 6,
+ S_DIR_TABLE = 7,
+ S_ANIM_MODE = 8
+};
+
+/**
+ * It's the super versatile fnSpeak. Text and wavs can be selected in any
+ * combination.
+ *
+ * @note We can assume no human - there should be no human, at least!
+ */
+
+int32 Logic::fnISpeak(int32 *params) {
+ // params: 0 pointer to ob_graphic
+ // 1 pointer to ob_speech
+ // 2 pointer to ob_logic
+ // 3 pointer to ob_mega
+ // 4 encoded text number
+ // 5 wav res id
+ // 6 anim res id
+ // 7 anim table res id
+ // 8 animation mode 0 lip synced,
+ // 1 just straight animation
+
+ static bool cycle_skip = false;
+ static bool speechRunning;
+
+ // Set up the pointers which we know we'll always need
+
+ ObjectLogic obLogic(decodePtr(params[S_OB_LOGIC]));
+ ObjectGraphic obGraph(decodePtr(params[S_OB_GRAPHIC]));
+
+ // FIRST TIME ONLY: create the text, load the wav, set up the anim,
+ // etc.
+
+ if (obLogic.getLooping() == 0) {
+ // New fudge to wait for smacker samples to finish
+ // since they can over-run into the game
+
+ if (_vm->_sound->getSpeechStatus() != RDSE_SAMPLEFINISHED)
+ return IR_REPEAT;
+
+ // New fudge for 'fx' subtitles: If subtitles switched off, and
+ // we don't want to use a wav for this line either, then just
+ // quit back to script right now!
+
+ if (!_vm->getSubtitles() && !wantSpeechForLine(params[S_WAV]))
+ return IR_CONT;
+
+ // Drop out for 1st cycle to allow walks/anims to end and
+ // display last frame before system locks while speech loaded
+
+ if (!cycle_skip) {
+ cycle_skip = true;
+ return IR_REPEAT;
+ }
+
+ cycle_skip = false;
+
+ _vm->_debugger->_textNumber = params[S_TEXT];
+
+ // Pull out the text line to get the official text number
+ // (for wav id). Once the wav id's go into all script text
+ // commands, we'll only need this for debugging.
+
+ uint32 text_res = params[S_TEXT] / SIZE;
+ uint32 local_text = params[S_TEXT] & 0xffff;
+
+ // For testing all text & speech!
+ //
+ // A script loop can send any text number to fnISpeak and it
+ // will only run the valid ones or return with 'result' equal
+ // to '1' or '2' to mean 'invalid text resource' and 'text
+ // number out of range' respectively
+ //
+ // See 'testing_routines' object in George's Player Character
+ // section of linc
+
+ if (readVar(SYSTEM_TESTING_TEXT)) {
+ if (!_vm->_resman->checkValid(text_res)) {
+ // Not a valid resource number - invalid (null
+ // resource)
+ writeVar(RESULT, 1);
+ return IR_CONT;
+ }
+
+ if (_vm->_resman->fetchType(text_res) != TEXT_FILE) {
+ // Invalid - not a text resource
+ _vm->_resman->closeResource(text_res);
+ writeVar(RESULT, 1);
+ return IR_CONT;
+ }
+
+ if (!_vm->checkTextLine(_vm->_resman->openResource(text_res), local_text)) {
+ // Line number out of range
+ _vm->_resman->closeResource(text_res);
+ writeVar(RESULT, 2);
+ return IR_CONT;
+ }
+
+ _vm->_resman->closeResource(text_res);
+ writeVar(RESULT, 0);
+ }
+
+ byte *text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
+ _officialTextNumber = READ_LE_UINT16(text);
+ _vm->_resman->closeResource(text_res);
+
+ // Prevent dud lines from appearing while testing text & speech
+ // since these will not occur in the game anyway
+
+ if (readVar(SYSTEM_TESTING_TEXT)) {
+ // If actor number is 0 and text line is just a 'dash'
+ // character
+ if (_officialTextNumber == 0 && text[2] == '-' && text[3] == 0) {
+ writeVar(RESULT, 3);
+ return IR_CONT;
+ }
+ }
+
+ // Set the 'looping_flag' and the text-click-delays. We can
+ // left-click past the text after half a second, and
+ // right-click past it after a quarter of a second.
+
+ obLogic.setLooping(1);
+ _leftClickDelay = 6;
+ _rightClickDelay = 3;
+
+ if (readVar(PLAYER_ID) != CUR_PLAYER_ID)
+ debug(5, "(%d) Nico: %s", _officialTextNumber, text + 2);
+ else {
+ byte buf[NAME_LEN];
+
+ debug(5, "(%d) %s: %s", _officialTextNumber, _vm->_resman->fetchName(readVar(ID), buf), text + 2);
+ }
+
+ // Set up the speech animation
+
+ if (params[S_ANIM]) {
+ // Just a straight anim.
+ _animId = params[6];
+ } else if (params[S_DIR_TABLE]) {
+ // Use this direction table to derive the anim
+ // NB. ASSUMES WE HAVE A MEGA OBJECT!!
+
+ ObjectMega obMega(decodePtr(params[S_OB_MEGA]));
+ byte *anim_table = decodePtr(params[S_DIR_TABLE]);
+
+ _animId = READ_LE_UINT32(anim_table + 4 * obMega.getCurDir());
+ } else {
+ // No animation choosen
+ _animId = 0;
+ }
+
+ if (_animId) {
+ // Set the talker's graphic to the first frame of this
+ // speech anim for now.
+
+ _speechAnimType = readVar(SPEECHANIMFLAG);
+ obGraph.setAnimResource(_animId);
+ obGraph.setAnimPc(0);
+ }
+
+ // Default back to looped lip synced anims.
+ writeVar(SPEECHANIMFLAG, 0);
+
+ // Set up _textX and _textY for speech panning and/or text
+ // sprite position.
+
+ locateTalker(params);
+
+ // Is it to be speech or subtitles or both?
+
+ // Assume not running until know otherwise
+ speechRunning = false;
+
+ // New fudge for 'fx' subtitles: If speech is selected, and
+ // this line is allowed speech (not if it's an fx subtitle!)
+
+ if (!_vm->_sound->isSpeechMute() && wantSpeechForLine(_officialTextNumber)) {
+ // If the wavId parameter is zero because not yet
+ // compiled into speech command, we can still get it
+ // from the 1st 2 chars of the text line.
+
+ if (!params[S_WAV])
+ params[S_WAV] = (int32)_officialTextNumber;
+
+ // Panning goes from -16 (left) to 16 (right)
+ int8 speech_pan = ((_textX - 320) * 16) / 320;
+
+ if (speech_pan < -16)
+ speech_pan = -16;
+ else if (speech_pan > 16)
+ speech_pan = 16;
+
+ uint32 rv = _vm->_sound->playCompSpeech(params[S_WAV], 16, speech_pan);
+
+ if (rv == RD_OK) {
+ // Ok, we've got something to play. Set it
+ // playing now. (We might want to do this the
+ // next cycle, don't know yet.)
+
+ speechRunning = true;
+ _vm->_sound->unpauseSpeech();
+ } else {
+ debug(5, "ERROR: PlayCompSpeech(wav=%d (res=%d pos=%d)) returned %.8x", params[S_WAV], text_res, local_text, rv);
+ }
+ }
+
+ if (_vm->getSubtitles() || !speechRunning) {
+ // We want subtitles, or the speech failed to load.
+ // Either way, we're going to show the text so create
+ // the text sprite.
+
+ formText(params);
+ }
+ }
+
+ // EVERY TIME: run a cycle of animation, if there is one
+
+ if (_animId) {
+ // There is an animation - Increment the anim frame number.
+ obGraph.setAnimPc(obGraph.getAnimPc() + 1);
+
+ byte *anim_file = _vm->_resman->openResource(obGraph.getAnimResource());
+ AnimHeader anim_head;
+
+ anim_head.read(_vm->fetchAnimHeader(anim_file));
+
+ if (!_speechAnimType) {
+ // ANIM IS TO BE LIP-SYNC'ED & REPEATING
+
+ if (obGraph.getAnimPc() == (int32)anim_head.noAnimFrames) {
+ // End of animation - restart from frame 0
+ obGraph.setAnimPc(0);
+ } else if (speechRunning && _vm->_sound->amISpeaking() == RDSE_QUIET) {
+ // The speech is running, but we're at a quiet
+ // bit. Restart from frame 0 (closed mouth).
+ obGraph.setAnimPc(0);
+ }
+ } else {
+ // ANIM IS TO PLAY ONCE ONLY
+ if (obGraph.getAnimPc() == (int32)anim_head.noAnimFrames - 1) {
+ // Reached the last frame of the anim. Hold
+ // anim on this last frame
+ _animId = 0;
+ }
+ }
+
+ _vm->_resman->closeResource(obGraph.getAnimResource());
+ } else if (_speechAnimType) {
+ // Placed here so we actually display the last frame of the
+ // anim.
+ _speechAnimType = 0;
+ }
+
+ // EVERY TIME: FIND OUT IF WE NEED TO STOP THE SPEECH NOW...
+
+ // If there is a wav then we're using that to end the speech naturally
+
+ bool speechFinished = false;
+
+ // If playing a sample
+
+ if (speechRunning) {
+ // Has it finished?
+ if (_vm->_sound->getSpeechStatus() == RDSE_SAMPLEFINISHED)
+ speechFinished = true;
+ } else if (!speechRunning && _speechTime) {
+ // Counting down text time because there is no sample - this
+ // ends the speech
+
+ // if no sample then we're using _speechTime to end speech
+ // naturally
+
+ _speechTime--;
+ if (!_speechTime)
+ speechFinished = true;
+ }
+
+ // Ok, all is running along smoothly - but a click means stop
+ // unnaturally
+
+ int mouseX, mouseY;
+
+ _vm->_mouse->getPos(mouseX, mouseY);
+
+ // So that we can go to the options panel while text & speech is
+ // being tested
+ if (readVar(SYSTEM_TESTING_TEXT) == 0 || mouseY > 0) {
+ MouseEvent *me = _vm->mouseEvent();
+
+ // Note that we now have TWO click-delays - one for LEFT
+ // button, one for RIGHT BUTTON
+
+ if ((!_leftClickDelay && me && (me->buttons & RD_LEFTBUTTONDOWN)) ||
+ (!_rightClickDelay && me && (me->buttons & RD_RIGHTBUTTONDOWN))) {
+ // Mouse click, after click_delay has expired -> end
+ // the speech.
+
+ // if testing text & speech
+ if (readVar(SYSTEM_TESTING_TEXT)) {
+ // and RB used to click past text
+ if (me->buttons & RD_RIGHTBUTTONDOWN) {
+ // then we want the previous line again
+ writeVar(SYSTEM_WANT_PREVIOUS_LINE, 1);
+ } else {
+ // LB just want next line again
+ writeVar(SYSTEM_WANT_PREVIOUS_LINE, 0);
+ }
+ }
+
+ speechFinished = true;
+
+ // if speech sample playing, halt it prematurely
+ if (speechRunning)
+ _vm->_sound->stopSpeech();
+ }
+ }
+
+ // If we are finishing the speech this cycle, do the business
+
+ // !speechAnimType, as we want an anim which is playing once to have
+ // finished.
+
+ if (speechFinished && !_speechAnimType) {
+ // If there is text, kill it
+ if (_speechTextBlocNo) {
+ _vm->_fontRenderer->killTextBloc(_speechTextBlocNo);
+ _speechTextBlocNo = 0;
+ }
+
+ // if there is a speech anim, end it on closed mouth frame
+ if (_animId) {
+ _animId = 0;
+ obGraph.setAnimPc(0);
+ }
+
+ speechRunning = false;
+
+ // no longer in a script function loop
+ obLogic.setLooping(0);
+
+ _vm->_debugger->_textNumber = 0;
+
+ // reset to zero, in case text line not even extracted (since
+ // this number comes from the text line)
+ _officialTextNumber = 0;
+
+ writeVar(RESULT, 0);
+ return IR_CONT;
+ }
+
+ // Speech still going, so decrement the click_delay if it's still
+ // active
+
+ if (_leftClickDelay)
+ _leftClickDelay--;
+
+ if (_rightClickDelay)
+ _rightClickDelay--;
+
+ return IR_REPEAT;
+}
+
+/**
+ * Reset the object and restart script 1 on level 0
+ */
+
+int32 Logic::fnTotalRestart(int32 *params) {
+ // mega runs this to restart its base logic again - like being cached
+ // in again
+
+ // params: none
+
+ _curObjectHub.setLogicLevel(0);
+ _curObjectHub.setScriptPc(0, 1);
+
+ return IR_TERMINATE;
+}
+
+int32 Logic::fnSetWalkGrid(int32 *params) {
+ // params: none
+
+ warning("fnSetWalkGrid() is no longer a valid opcode");
+ return IR_CONT;
+}
+
+/**
+ * Receive and sequence the commands sent from the conversation script. We have
+ * to do this in a slightly tweeky manner as we can no longer have generic
+ * scripts.
+ */
+
+enum {
+ INS_talk = 1,
+ INS_anim = 2,
+ INS_reverse_anim = 3,
+ INS_walk = 4,
+ INS_turn = 5,
+ INS_face = 6,
+ INS_trace = 7,
+ INS_no_sprite = 8,
+ INS_sort = 9,
+ INS_foreground = 10,
+ INS_background = 11,
+ INS_table_anim = 12,
+ INS_reverse_table_anim = 13,
+ INS_walk_to_anim = 14,
+ INS_set_frame = 15,
+ INS_stand_after_anim = 16,
+ INS_quit = 42
+};
+
+int32 Logic::fnSpeechProcess(int32 *params) {
+ // params: 0 pointer to ob_graphic
+ // 1 pointer to ob_speech
+ // 2 pointer to ob_logic
+ // 3 pointer to ob_mega
+ // 4 pointer to ob_walkdata
+
+ ObjectSpeech obSpeech(decodePtr(params[1]));
+
+ while (1) {
+ int32 pars[9];
+
+ // Check which command we're waiting for, and call the
+ // appropriate function. Once we're done, clear the command
+ // and set wait_state to 1.
+ //
+ // Note: we could save a var and ditch wait_state and check
+ // 'command' for non zero means busy
+ //
+ // Note: I can't see that we ever check the value of wait_state
+ // but perhaps it accesses that memory location directly?
+
+ switch (obSpeech.getCommand()) {
+ case 0:
+ break;
+ case INS_talk:
+ pars[0] = params[0]; // ob_graphic
+ pars[1] = params[1]; // ob_speech
+ pars[2] = params[2]; // ob_logic
+ pars[3] = params[3]; // ob_mega
+ pars[4] = obSpeech.getIns1(); // encoded text number
+ pars[5] = obSpeech.getIns2(); // wav res id
+ pars[6] = obSpeech.getIns3(); // anim res id
+ pars[7] = obSpeech.getIns4(); // anim table res id
+ pars[8] = obSpeech.getIns5(); // animation mode - 0 lip synced, 1 just straight animation
+
+ if (fnISpeak(pars) != IR_REPEAT) {
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ }
+
+ return IR_REPEAT;
+ case INS_turn:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = params[4]; // ob_walkdata
+ pars[4] = obSpeech.getIns1(); // direction to turn to
+
+ if (fnTurn(pars) != IR_REPEAT) {
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ }
+
+ return IR_REPEAT;
+ case INS_face:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = params[4]; // ob_walkdata
+ pars[4] = obSpeech.getIns1(); // target
+
+ if (fnFaceMega(pars) != IR_REPEAT) {
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ }
+
+ return IR_REPEAT;
+ case INS_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = obSpeech.getIns1(); // anim res
+
+ if (fnAnim(pars) != IR_REPEAT) {
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ }
+
+ return IR_REPEAT;
+ case INS_reverse_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = obSpeech.getIns1(); // anim res
+
+ if (fnReverseAnim(pars) != IR_REPEAT) {
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ }
+
+ return IR_REPEAT;
+ case INS_table_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = obSpeech.getIns1(); // pointer to anim table
+
+ if (fnMegaTableAnim(pars) != IR_REPEAT) {
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ }
+
+ return IR_REPEAT;
+ case INS_reverse_table_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = obSpeech.getIns1(); // pointer to anim table
+
+ if (fnReverseMegaTableAnim(pars) != IR_REPEAT) {
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ }
+
+ return IR_REPEAT;
+ case INS_no_sprite:
+ fnNoSprite(params); // ob_graphic
+
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ return IR_REPEAT ;
+ case INS_sort:
+ fnSortSprite(params); // ob_graphic
+
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ return IR_REPEAT;
+ case INS_foreground:
+ fnForeSprite(params); // ob_graphic
+
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ return IR_REPEAT;
+ case INS_background:
+ fnBackSprite(params); // ob_graphic
+
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ return IR_REPEAT;
+ case INS_walk:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = params[4]; // ob_walkdata
+ pars[4] = obSpeech.getIns1(); // target x
+ pars[5] = obSpeech.getIns2(); // target y
+ pars[6] = obSpeech.getIns3(); // target direction
+
+ if (fnWalk(pars) != IR_REPEAT) {
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ }
+
+ return IR_REPEAT;
+ case INS_walk_to_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = params[4]; // ob_walkdata
+ pars[4] = obSpeech.getIns1(); // anim resource
+
+ if (fnWalkToAnim(pars) != IR_REPEAT) {
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ }
+
+ return IR_REPEAT;
+ case INS_stand_after_anim:
+ pars[0] = params[0]; // ob_graphic
+ pars[1] = params[3]; // ob_mega
+ pars[2] = obSpeech.getIns1(); // anim resource
+
+ fnStandAfterAnim(pars);
+
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ return IR_REPEAT;
+ case INS_set_frame:
+ pars[0] = params[0]; // ob_graphic
+ pars[1] = obSpeech.getIns1(); // anim_resource
+ pars[2] = obSpeech.getIns2(); // FIRST_FRAME or LAST_FRAME
+ fnSetFrame(pars);
+
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ return IR_REPEAT;
+ case INS_quit:
+ // That's it - we're finished with this
+ obSpeech.setCommand(0);
+ // obSpeech.setWaitState(0);
+ return IR_CONT;
+ default:
+ // Unimplemented command - just cancel
+ obSpeech.setCommand(0);
+ obSpeech.setWaitState(1);
+ break;
+ }
+
+ if (readVar(SPEECH_ID) == readVar(ID)) {
+ // There's a new command for us! Grab the command -
+ // potentially we only have this cycle to do this - and
+ // set things up so that the command will be picked up
+ // on the next iteration of the while loop.
+
+ debug(5, "fnSpeechProcess: Received new command %d", readVar(INS_COMMAND));
+
+ writeVar(SPEECH_ID, 0);
+
+ obSpeech.setCommand(readVar(INS_COMMAND));
+ obSpeech.setIns1(readVar(INS1));
+ obSpeech.setIns2(readVar(INS2));
+ obSpeech.setIns3(readVar(INS3));
+ obSpeech.setIns4(readVar(INS4));
+ obSpeech.setIns5(readVar(INS5));
+ obSpeech.setWaitState(0);
+
+ writeVar(INS_COMMAND, 0);
+ } else {
+ // No new command. We could run a blink anim (or
+ // something) here.
+
+ obSpeech.setWaitState(1);
+ return IR_REPEAT;
+ }
+ }
+}
+
+int32 Logic::fnSetScaling(int32 *params) {
+ // params: 0 pointer to object's mega structure
+ // 1 scale constant A
+ // 2 scale constant B
+
+ // 256 * s = A * y + B
+
+ // Where s is system scale, which itself is (256 * actual_scale) ie.
+ // s == 128 is half size
+
+ ObjectMega obMega(decodePtr(params[0]));
+
+ obMega.setScaleA(params[1]);
+ obMega.setScaleB(params[2]);
+
+ return IR_CONT;
+}
+
+int32 Logic::fnStartEvent(int32 *params) {
+ // params: none
+
+ startEvent();
+ return IR_TERMINATE;
+}
+
+int32 Logic::fnCheckEventWaiting(int32 *params) {
+ // params: none
+
+ writeVar(RESULT, checkEventWaiting());
+ return IR_CONT;
+}
+
+int32 Logic::fnRequestSpeech(int32 *params) {
+ // change current script - must be followed by a TERMINATE script
+ // directive
+
+ // params: 0 id of target to catch the event and startup speech
+ // servicing
+
+ // Full script id to interact with - megas run their own 7th script
+ sendEvent(params[0], (params[0] << 16) | 6);
+ return IR_CONT;
+}
+
+int32 Logic::fnGosub(int32 *params) {
+ // params: 0 id of script
+
+ // Hurray, script subroutines. Logic goes up - pc is saved for current
+ // level.
+ logicUp(params[0]);
+ return IR_GOSUB;
+}
+
+/**
+ * Wait for a target to become waiting, i.e. not busy, or until we time out.
+ * This is useful when clicking on a target to talk to it, and it doesn't
+ * reply. This way, we won't lock up.
+ *
+ * If the target becomes waiting, RESULT is set to 0. If we time out, RESULT is
+ * set to 1.
+ */
+
+int32 Logic::fnTimedWait(int32 *params) {
+ // params: 0 ob_logic
+ // 1 target
+ // 2 number of cycles before give up
+
+ assert(_vm->_resman->fetchType(params[1]) == GAME_OBJECT);
+
+ ObjectLogic obLogic(decodePtr(params[0]));
+
+ if (obLogic.getLooping() == 0) {
+ // This is the first time, so set up the time-out.
+ obLogic.setLooping(params[2]);
+ }
+
+ // Run the target's get-speech-state script
+ runResScript(params[1], 5);
+
+ if (readVar(RESULT) == 1) {
+ // The target is waiting, i.e. not busy
+
+ _vm->_debugger->_speechScriptWaiting = 0;
+
+ obLogic.setLooping(0);
+ writeVar(RESULT, 0);
+ return IR_CONT;
+ }
+
+ obLogic.setLooping(obLogic.getLooping() - 1);
+
+ if (obLogic.getLooping() == 0) {
+ // Time's up.
+
+ debug(5, "fnTimedWait: Timed out waiting for %d", params[1]);
+ _vm->_debugger->_speechScriptWaiting = 0;
+
+ // Clear the event that hasn't been picked up - in theory,
+ // none of this should ever happen.
+
+ killAllIdsEvents(params[1]);
+ writeVar(RESULT, 1);
+ return IR_CONT;
+ }
+
+ // Target is busy. Keep trying.
+
+ _vm->_debugger->_speechScriptWaiting = params[1];
+ return IR_REPEAT;
+}
+
+int32 Logic::fnPlayFx(int32 *params) {
+ // params: 0 sample resource id
+ // 1 type (FX_SPOT, FX_RANDOM, FX_LOOP)
+ // 2 delay (0..65535)
+ // 3 volume (0..16)
+ // 4 pan (-16..16)
+
+ // example script:
+ // fnPlayFx (FXWATER, FX_LOOP, 0, 10, 15);
+ // // fx_water is just a local script flag
+ // fx_water = result;
+ // .
+ // .
+ // .
+ // fnStopFx (fx_water);
+
+ int32 res = params[0];
+ int32 type = params[1];
+ int32 delay = params[2];
+ int32 volume = params[3];
+ int32 pan = params[4];
+
+ _vm->_sound->queueFx(res, type, delay, volume, pan);
+ return IR_CONT;
+}
+
+int32 Logic::fnStopFx(int32 *params) {
+ // params: 0 position in queue
+ if (_vm->_sound->stopFx(params[0]) != RD_OK)
+ debug(5, "SFX ERROR: Trying to stop an inactive sound slot");
+
+ return IR_CONT;
+}
+
+/**
+ * Start a tune playing, to play once or to loop until stopped or next one
+ * played.
+ */
+
+int32 Logic::fnPlayMusic(int32 *params) {
+ // params: 0 tune id
+ // 1 loop flag (0 or 1)
+
+ char filename[128];
+ bool loopFlag;
+ uint32 rv;
+
+ loopFlag = (params[1] == FX_LOOP);
+
+ rv = _vm->_sound->streamCompMusic(params[0], loopFlag);
+
+ if (rv)
+ debug(5, "ERROR: streamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv);
+
+ return IR_CONT;
+}
+
+int32 Logic::fnStopMusic(int32 *params) {
+ // params: none
+
+ _vm->_sound->stopMusic(false);
+ return IR_CONT;
+}
+
+int32 Logic::fnSetValue(int32 *params) {
+ // temp. function!
+
+ // used for setting far-referenced megaset resource field in mega
+ // object, from start script
+
+ // params: 0 pointer to object's mega structure
+ // 1 value to set it to
+
+ ObjectMega obMega(decodePtr(params[0]));
+
+ obMega.setMegasetRes(params[1]);
+ return IR_CONT;
+}
+
+int32 Logic::fnNewScript(int32 *params) {
+ // change current script - must be followed by a TERMINATE script
+ // directive
+
+ // params: 0 id of script
+
+ writeVar(PLAYER_ACTION, 0); // must clear this
+ logicReplace(params[0]);
+ return IR_TERMINATE;
+}
+
+/**
+ * Like getSync(), but called from scripts. Sets the RESULT variable to
+ * the sync value, or 0 if none is found.
+ */
+
+int32 Logic::fnGetSync(int32 *params) {
+ // params: none
+
+ int slot = getSync();
+
+ writeVar(RESULT, (slot != -1) ? _syncList[slot].sync : 0);
+ return IR_CONT;
+}
+
+/**
+ * Wait for sync to happen. Sets the RESULT variable to the sync value, once
+ * it has been found.
+ */
+
+int32 Logic::fnWaitSync(int32 *params) {
+ // params: none
+
+ debug(6, "fnWaitSync: %d waits", readVar(ID));
+
+ int slot = getSync();
+
+ if (slot == -1)
+ return IR_REPEAT;
+
+ debug(5, "fnWaitSync: %d got sync %d", readVar(ID), _syncList[slot].sync);
+ writeVar(RESULT, _syncList[slot].sync);
+ return IR_CONT;
+}
+
+int32 Logic::fnRegisterWalkGrid(int32 *params) {
+ // params: none
+
+ warning("fnRegisterWalkGrid() is no longer a valid opcode");
+ return IR_CONT;
+}
+
+int32 Logic::fnReverseMegaTableAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to animation table
+
+ // Reverse anim
+ return _router->megaTableAnimate(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ decodePtr(params[2]),
+ decodePtr(params[3]),
+ true);
+}
+
+int32 Logic::fnReverseAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 resource id of animation file
+
+ // Reverse anim
+ return _router->doAnimate(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ params[2], true);
+}
+
+/**
+ * Mark this object for killing - to be killed when player leaves this screen.
+ * Object reloads and script restarts upon re-entry to screen, which causes
+ * this object's startup logic to be re-run every time we enter the screen.
+ * "Which is nice."
+ *
+ * @note Call ONCE from object's logic script, i.e. in startup code, so not
+ * re-called every time script frops off and restarts!
+ */
+
+int32 Logic::fnAddToKillList(int32 *params) {
+ // params: none
+ uint32 id = readVar(ID);
+
+ // DON'T EVER KILL GEORGE!
+ if (id == CUR_PLAYER_ID)
+ return IR_CONT;
+
+ // Scan the list to see if it's already included
+
+ for (uint32 i = 0; i < _kills; i++) {
+ if (_objectKillList[i] == id)
+ return IR_CONT;
+ }
+
+ assert(_kills < OBJECT_KILL_LIST_SIZE); // no room at the inn
+
+ _objectKillList[_kills++] = id;
+
+ // "another one bites the dust"
+
+ // When we leave the screen, all these object resources are to be
+ // cleaned out of memory and the kill list emptied by doing
+ // '_kills = 0', ensuring that all resources are in fact still in
+ // memory and, more importantly, closed before killing!
+
+ return IR_CONT;
+}
+
+/**
+ * Set the standby walk coords to be used by fnWalkToAnim() and
+ * fnStandAfterAnim() when the anim header's start/end coords are zero.
+ * Useful during development; can stay in final game anyway.
+ */
+
+int32 Logic::fnSetStandbyCoords(int32 *params) {
+ // params: 0 x-coord
+ // 1 y-coord
+ // 2 direction (0..7)
+
+ _router->setStandbyCoords(params[0], params[1], params[2]);
+ return IR_CONT;
+}
+
+int32 Logic::fnBackPar0Sprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteStatus(decodePtr(params[0]), BGP0_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnBackPar1Sprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteStatus(decodePtr(params[0]), BGP1_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnForePar0Sprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteStatus(decodePtr(params[0]), FGP0_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnForePar1Sprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteStatus(decodePtr(params[0]), FGP1_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnSetPlayerActionEvent(int32 *params) {
+ // we want to intercept the player character and have him interact
+ // with an object - from script this code is the same as the mouse
+ // engine calls when you click on an object - here, a third party
+ // does the clicking IYSWIM
+
+ // note - this routine used CUR_PLAYER_ID as the target
+
+ // params: 0 id to interact with
+
+ setPlayerActionEvent(CUR_PLAYER_ID, params[0]);
+ return IR_CONT;
+}
+
+/**
+ * Set the special scroll offset variables
+ *
+ * Call when starting screens and to change the camera within screens
+ *
+ * call AFTER fnInitBackground() to override the defaults
+ */
+
+int32 Logic::fnSetScrollCoordinate(int32 *params) {
+ // params: 0 feet_x value
+ // 1 feet_y value
+
+ // Called feet_x and feet_y to retain intellectual compatibility with
+ // Sword1!
+ //
+ // feet_x & feet_y refer to the physical screen coords where the
+ // system will try to maintain George's feet
+
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ screenInfo->feet_x = params[0];
+ screenInfo->feet_y = params[1];
+ return IR_CONT;
+}
+
+/**
+ * Stand mega at start position of anim
+ */
+
+int32 Logic::fnStandAtAnim(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 pointer to object's mega structure
+ // 2 anim resource id
+
+ _router->standAtAnim(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ params[2]);
+ return IR_CONT;
+}
+
+#define SCROLL_MOUSE_WIDTH 20
+
+int32 Logic::fnSetScrollLeftMouse(int32 *params) {
+ // params: 0 pointer to object's mouse structure
+
+ byte *ob_mouse = decodePtr(params[0]);
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ // Highest priority
+
+ ObjectMouse mouse;
+
+ mouse.x1 = 0;
+ mouse.y1 = 0;
+ mouse.x2 = screenInfo->scroll_offset_x + SCROLL_MOUSE_WIDTH;
+ mouse.y2 = screenInfo->screen_deep - 1;
+ mouse.priority = 0;
+
+ if (screenInfo->scroll_offset_x > 0) {
+ // not fully scrolled to the left
+ mouse.pointer = SCROLL_LEFT_MOUSE_ID;
+ } else {
+ // so the mouse area doesn't get registered
+ mouse.pointer = 0;
+ }
+
+ mouse.write(ob_mouse);
+ return IR_CONT;
+}
+
+int32 Logic::fnSetScrollRightMouse(int32 *params) {
+ // params: 0 pointer to object's mouse structure
+
+ byte *ob_mouse = decodePtr(params[0]);
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ // Highest priority
+
+ ObjectMouse mouse;
+
+ mouse.x1 = screenInfo->scroll_offset_x + _vm->_screen->getScreenWide() - SCROLL_MOUSE_WIDTH;
+ mouse.y1 = 0;
+ mouse.x2 = screenInfo->screen_wide - 1;
+ mouse.y2 = screenInfo->screen_deep - 1;
+ mouse.priority = 0;
+
+ if (screenInfo->scroll_offset_x < screenInfo->max_scroll_offset_x) {
+ // not fully scrolled to the right
+ mouse.pointer = SCROLL_RIGHT_MOUSE_ID;
+ } else {
+ // so the mouse area doesn't get registered
+ mouse.pointer = 0;
+ }
+
+ mouse.write(ob_mouse);
+ return IR_CONT;
+}
+
+int32 Logic::fnColour(int32 *params) {
+ // set border colour - useful during script development
+ // eg. set to colour during a timer situation, then black when timed
+ // out
+
+ // params 0: colour (see defines above)
+
+#ifdef SWORD2_DEBUG
+ // what colour?
+ switch (params[0]) {
+ case BLACK:
+ _vm->_screen->setPalette(0, 1, black, RDPAL_INSTANT);
+ break;
+ case WHITE:
+ _vm->_screen->setPalette(0, 1, white, RDPAL_INSTANT);
+ break;
+ case RED:
+ _vm->_screen->setPalette(0, 1, red, RDPAL_INSTANT);
+ break;
+ case GREEN:
+ _vm->_screen->setPalette(0, 1, green, RDPAL_INSTANT);
+ break;
+ case BLUE:
+ _vm->_screen->setPalette(0, 1, blue, RDPAL_INSTANT);
+ break;
+ }
+#endif
+
+ return IR_CONT;
+}
+
+#ifdef SWORD2_DEBUG
+#define BLACK 0
+#define WHITE 1
+#define RED 2
+#define GREEN 3
+#define BLUE 4
+
+static uint8 black[4] = { 0, 0, 0, 0 };
+static uint8 white[4] = { 255, 255, 255, 0 };
+static uint8 red[4] = { 255, 0, 0, 0 };
+static uint8 green[4] = { 0, 255, 0, 0 };
+static uint8 blue[4] = { 0, 0, 255, 0 };
+#endif
+
+int32 Logic::fnFlash(int32 *params) {
+ // flash colour 0 (ie. border) - useful during script development
+ // eg. fnFlash(BLUE) where a text line is missed; RED when some code
+ // missing, etc
+
+ // params: 0 colour to flash
+
+#ifdef SWORD2_DEBUG
+ // what colour?
+ switch (params[0]) {
+ case WHITE:
+ _vm->_screen->setPalette(0, 1, white, RDPAL_INSTANT);
+ break;
+ case RED:
+ _vm->_screen->setPalette(0, 1, red, RDPAL_INSTANT);
+ break;
+ case GREEN:
+ _vm->_screen->setPalette(0, 1, green, RDPAL_INSTANT);
+ break;
+ case BLUE:
+ _vm->_screen->setPalette(0, 1, blue, RDPAL_INSTANT);
+ break;
+ }
+
+ // There used to be a busy-wait loop here, so I don't know how long
+ // the delay was meant to be. Probably doesn't matter much.
+
+ _vm->_screen->updateDisplay();
+ _vm->_system->delayMillis(250);
+ _vm->_screen->setPalette(0, 1, black, RDPAL_INSTANT);
+#endif
+
+ return IR_CONT;
+}
+
+int32 Logic::fnPreFetch(int32 *params) {
+ // Go fetch resource in the background.
+
+ // params: 0 resource to fetch [guess]
+
+ return IR_CONT;
+}
+
+/**
+ * Reverse of fnPassPlayerSaveData() - run script 8 of player object.
+ */
+
+int32 Logic::fnGetPlayerSaveData(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+
+ byte *ob_logic = decodePtr(params[0]);
+ byte *ob_graph = decodePtr(params[1]);
+ byte *ob_mega = decodePtr(params[2]);
+
+ // Copy from savegame buffers to player object
+
+ memcpy(ob_logic, _saveLogic, ObjectLogic::size());
+ memcpy(ob_graph, _saveGraphic, ObjectGraphic::size());
+ memcpy(ob_mega, _saveMega, ObjectMega::size());
+
+ // Any walk-data must be cleared - the player will be set to stand if
+ // he was walking when saved.
+
+ ObjectMega obMega(ob_mega);
+
+ if (obMega.getIsWalking()) {
+ ObjectLogic obLogic(ob_logic);
+
+ obMega.setIsWalking(0);
+
+ int32 pars[3];
+
+ pars[0] = params[1]; // ob_graphic;
+ pars[1] = params[2]; // ob_mega
+ pars[2] = obMega.getCurDir();
+
+ fnStand(pars);
+
+ // Reset looping flag (which would have been 1 during fnWalk)
+ obLogic.setLooping(0);
+ }
+
+ return IR_CONT;
+}
+
+/**
+ * Copies the 4 essential player structures into the savegame header - run
+ * script 7 of player object to request this.
+ *
+ * Remember, we cannot simply read a compact any longer but instead must
+ * request it from the object itself.
+ */
+
+int32 Logic::fnPassPlayerSaveData(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+
+ // Copy from player object to savegame buffers
+
+ memcpy(_saveLogic, decodePtr(params[0]), ObjectLogic::size());
+ memcpy(_saveGraphic, decodePtr(params[1]), ObjectGraphic::size());
+ memcpy(_saveMega, decodePtr(params[2]), ObjectMega::size());
+
+ return IR_CONT;
+}
+
+int32 Logic::fnSendEvent(int32 *params) {
+ // we want to intercept the player character and have him interact
+ // with an object - from script
+
+ // params: 0 id to receive event
+ // 1 script to run
+
+ sendEvent(params[0], params[1]);
+ return IR_CONT;
+}
+
+/**
+ * Add this walkgrid resource to the list of those used for routing in this
+ * location. Note that this is ignored if the resource is already in the list.
+ */
+
+int32 Logic::fnAddWalkGrid(int32 *params) {
+ // params: 0 id of walkgrid resource
+
+ // All objects that add walkgrids must be restarted whenever we
+ // re-enter a location.
+
+ // DON'T EVER KILL GEORGE!
+ if (readVar(ID) != CUR_PLAYER_ID) {
+ // Need to call this in case it wasn't called in script!
+ fnAddToKillList(NULL);
+ }
+
+ _router->addWalkGrid(params[0]);
+ fnPreLoad(params);
+ return IR_CONT;
+}
+
+/**
+ * Remove this walkgrid resource from the list of those used for routing in
+ * this location. Note that this is ignored if the resource isn't actually
+ * in the list.
+ */
+
+int32 Logic::fnRemoveWalkGrid(int32 *params) {
+ // params: 0 id of walkgrid resource
+
+ _router->removeWalkGrid(params[0]);
+ return IR_CONT;
+}
+
+// like fnCheckEventWaiting, but starts the event rather than setting RESULT
+// to 1
+
+int32 Logic::fnCheckForEvent(int32 *params) {
+ // params: none
+
+ if (checkEventWaiting()) {
+ startEvent();
+ return IR_TERMINATE;
+ }
+
+ return IR_CONT;
+}
+
+// combination of fnPause and fnCheckForEvent
+// - ie. does a pause, but also checks for event each cycle
+
+int32 Logic::fnPauseForEvent(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 number of game-cycles to pause
+
+ ObjectLogic obLogic(decodePtr(params[0]));
+
+ if (checkEventWaiting()) {
+ obLogic.setLooping(0);
+ startEvent();
+ return IR_TERMINATE;
+ }
+
+ return fnPause(params);
+}
+
+int32 Logic::fnClearEvent(int32 *params) {
+ // params: none
+
+ clearEvent(readVar(ID));
+ return IR_CONT;
+}
+
+int32 Logic::fnFaceMega(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 id of target mega to face
+
+ return _router->faceMega(
+ decodePtr(params[0]),
+ decodePtr(params[1]),
+ decodePtr(params[2]),
+ decodePtr(params[3]),
+ params[4]);
+}
+
+int32 Logic::fnPlaySequence(int32 *params) {
+ // params: 0 pointer to null-terminated ascii filename
+ // 1 number of frames in the sequence, used for PSX.
+
+ char filename[30];
+ MovieTextObject *sequenceSpeechArray[MAX_SEQUENCE_TEXT_LINES + 1];
+
+ // The original code had some #ifdef blocks for skipping or muting the
+ // cutscenes - fondly described as "the biggest fudge in the history
+ // of computer games" - but at the very least we want to show the
+ // cutscene subtitles, so I removed them.
+
+ debug(5, "fnPlaySequence(\"%s\");", (const char *)decodePtr(params[0]));
+
+ // add the appropriate file extension & play it
+
+ strcpy(filename, (const char *)decodePtr(params[0]));
+
+ // Write to walkthrough file (zebug0.txt)
+ debug(5, "PLAYING SEQUENCE \"%s\"", filename);
+
+ // now create the text sprites, if any
+
+ if (_sequenceTextLines)
+ createSequenceSpeech(sequenceSpeechArray);
+
+ // don't want to carry on streaming game music when smacker starts!
+ fnStopMusic(NULL);
+
+ // pause sfx during sequence
+ _vm->_sound->pauseFx();
+
+ MoviePlayer player(_vm);
+ uint32 rv;
+
+ if (_sequenceTextLines && !readVar(DEMO))
+ rv = player.play(filename, sequenceSpeechArray, _smackerLeadIn, _smackerLeadOut);
+ else
+ rv = player.play(filename, NULL, _smackerLeadIn, _smackerLeadOut);
+
+ // check the error return-value
+ if (rv)
+ debug(5, "MoviePlayer.play(\"%s\") returned 0x%.8x", filename, rv);
+
+ // unpause sound fx again, in case we're staying in same location
+ _vm->_sound->unpauseFx();
+
+ _smackerLeadIn = 0;
+ _smackerLeadOut = 0;
+
+ // now clear the text sprites, if any
+
+ if (_sequenceTextLines)
+ clearSequenceSpeech(sequenceSpeechArray);
+
+ // now clear the screen in case the Sequence was quitted (using ESC)
+ // rather than fading down to black
+
+ _vm->_screen->clearScene();
+
+ // zero the entire palette in case we're about to fade up!
+
+ byte pal[4 * 256];
+
+ memset(pal, 0, sizeof(pal));
+ _vm->_screen->setPalette(0, 256, pal, RDPAL_INSTANT);
+
+ debug(5, "fnPlaySequence FINISHED");
+ return IR_CONT;
+}
+
+int32 Logic::fnShadedSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteShading(decodePtr(params[0]), SHADED_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnUnshadedSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ _router->setSpriteShading(decodePtr(params[0]), UNSHADED_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnFadeUp(int32 *params) {
+ // params: none
+
+ _vm->_screen->waitForFade();
+
+ if (_vm->_screen->getFadeStatus() == RDFADE_BLACK)
+ _vm->_screen->fadeUp();
+
+ return IR_CONT;
+}
+
+int32 Logic::fnDisplayMsg(int32 *params) {
+ // Display a message to the user on the screen.
+
+ // params: 0 Text number of message to be displayed.
+
+ uint32 local_text = params[0] & 0xffff;
+ uint32 text_res = params[0] / SIZE;
+
+ // Display message for three seconds.
+
+ // +2 to skip the encoded text number in the first 2 chars; 3 is
+ // duration in seconds
+
+ _vm->_screen->displayMsg(_vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text) + 2, 3);
+ _vm->_resman->closeResource(text_res);
+
+ return IR_CONT;
+}
+
+int32 Logic::fnSetObjectHeld(int32 *params) {
+ // params: 0 luggage icon to set
+ uint32 res = (uint32)params[0];
+
+ _vm->_mouse->setObjectHeld(res);
+ return IR_CONT;
+}
+
+int32 Logic::fnAddSequenceText(int32 *params) {
+ // params: 0 text number
+ // 1 frame number to start the text displaying
+ // 2 frame number to stop the text dispalying
+
+ assert(_sequenceTextLines < MAX_SEQUENCE_TEXT_LINES);
+
+ _sequenceTextList[_sequenceTextLines].textNumber = params[0];
+ _sequenceTextList[_sequenceTextLines].startFrame = params[1];
+ _sequenceTextList[_sequenceTextLines].endFrame = params[2];
+ _sequenceTextLines++;
+ return IR_CONT;
+}
+
+int32 Logic::fnResetGlobals(int32 *params) {
+ // fnResetGlobals is used by the demo - so it can loop back & restart
+ // itself
+
+ // params: none
+
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ byte *globals = _vm->_resman->openResource(1) + ResHeader::size();
+ int32 size = _vm->_resman->fetchLen(1) - ResHeader::size();
+
+ debug(5, "globals size: %d", size);
+
+ // blank each global variable
+ memset(globals, 0, size);
+
+ _vm->_resman->closeResource(1);
+
+ // all objects but george
+ _vm->_resman->killAllObjects(false);
+
+ // FOR THE DEMO - FORCE THE SCROLLING TO BE RESET!
+ // - this is taken from fnInitBackground
+
+ // switch on scrolling (2 means first time on screen)
+ screenInfo->scroll_flag = 2;
+
+ // Used to be IR_CONT, but that's a bad idea. We may just have killed
+ // our own script resource -- continuing will cause a bad memory read
+ // access.
+ return IR_STOP;
+}
+
+int32 Logic::fnSetPalette(int32 *params) {
+ // params: 0 resource number of palette file, or 0 if it's to be
+ // the palette from the current screen
+
+ _vm->_screen->setFullPalette(params[0]);
+ return IR_CONT;
+}
+
+// use this in the object's service script prior to registering the mouse area
+// ie. before fnRegisterMouse or fnRegisterFrame
+// - best if kept at very top of service script
+
+int32 Logic::fnRegisterPointerText(int32 *params) {
+ // params: 0 local id of text line to use as pointer text
+
+ _vm->_mouse->registerPointerText(params[0]);
+ return IR_CONT;
+}
+
+int32 Logic::fnFetchWait(int32 *params) {
+ // Fetches a resource in the background but prevents the script from
+ // continuing until the resource is in memory.
+
+ // params: 0 resource to fetch [guess]
+
+ return IR_CONT;
+}
+
+int32 Logic::fnRelease(int32 *params) {
+ // Releases a resource from memory. Used for freeing memory for
+ // sprites that have just been used and will not be used again.
+ // Sometimes it is better to kick out a sprite straight away so that
+ // the memory can be used for more frequent animations.
+
+ // params: 0 resource to release [guess]
+
+ return IR_CONT;
+}
+
+int32 Logic::fnPrepareMusic(int32 *params) {
+ // params: 1 id of music to prepare [guess]
+ return IR_CONT;
+}
+
+int32 Logic::fnSoundFetch(int32 *params) {
+ // params: 0 id of sound to fetch [guess]
+ return IR_CONT;
+}
+
+int32 Logic::fnSmackerLeadIn(int32 *params) {
+ // params: 0 id of lead-in music
+
+ // ready for use in fnPlaySequence
+ _smackerLeadIn = params[0];
+ return IR_CONT;
+}
+
+int32 Logic::fnSmackerLeadOut(int32 *params) {
+ // params: 0 id of lead-out music
+
+ // ready for use in fnPlaySequence
+ _smackerLeadOut = params[0];
+ return IR_CONT;
+}
+
+/**
+ * Stops all FX and clears the entire FX queue.
+ */
+
+int32 Logic::fnStopAllFx(int32 *params) {
+ // params: none
+
+ _vm->_sound->clearFxQueue();
+ return IR_CONT;
+}
+
+int32 Logic::fnCheckPlayerActivity(int32 *params) {
+ // Used to decide when to trigger music cues described as "no player
+ // activity for a while"
+
+ // params: 0 threshold delay in seconds, ie. what we want to
+ // check the actual delay against
+
+ uint32 seconds = (uint32)params[0];
+
+ _vm->_mouse->checkPlayerActivity(seconds);
+ return IR_CONT;
+}
+
+int32 Logic::fnResetPlayerActivityDelay(int32 *params) {
+ // Use if you want to deliberately reset the "no player activity"
+ // counter for any reason
+
+ // params: none
+
+ _vm->_mouse->resetPlayerActivityDelay();
+ return IR_CONT;
+}
+
+int32 Logic::fnCheckMusicPlaying(int32 *params) {
+ // params: none
+
+ // sets result to no. of seconds of current tune remaining
+ // or 0 if no music playing
+
+ // in seconds, rounded up to the nearest second
+ writeVar(RESULT, _vm->_sound->musicTimeRemaining());
+ return IR_CONT;
+}
+
+int32 Logic::fnPlayCredits(int32 *params) {
+ // This function just quits the game if this is the playable demo, ie.
+ // credits are NOT played in the demo any more!
+
+ // params: none
+
+ if (readVar(DEMO)) {
+ _vm->closeGame();
+ return IR_STOP;
+ }
+
+ _vm->_screen->rollCredits();
+ return IR_CONT;
+}
+
+int32 Logic::fnSetScrollSpeedNormal(int32 *params) {
+ // params: none
+
+ _vm->_screen->setScrollFraction(16);
+ return IR_CONT;
+}
+
+int32 Logic::fnSetScrollSpeedSlow(int32 *params) {
+ // params: none
+
+ _vm->_screen->setScrollFraction(32);
+ return IR_CONT;
+}
+
+// Called from speech scripts to remove the chooser bar when it's not
+// appropriate to keep it displayed
+
+int32 Logic::fnRemoveChooser(int32 *params) {
+ // params: none
+
+ _vm->_mouse->hideMenu(RDMENU_BOTTOM);
+ return IR_CONT;
+}
+
+/**
+ * Alter the volume and pan of a currently playing FX
+ */
+
+int32 Logic::fnSetFxVolAndPan(int32 *params) {
+ // params: 0 id of fx (ie. the id returned in 'result' from
+ // fnPlayFx
+ // 1 new volume (0..16)
+ // 2 new pan (-16..16)
+
+ debug(5, "fnSetFxVolAndPan(%d, %d, %d)", params[0], params[1], params[2]);
+
+ _vm->_sound->setFxIdVolumePan(params[0], params[1], params[2]);
+ return IR_CONT;
+}
+
+/**
+ * Alter the volume of a currently playing FX
+ */
+
+int32 Logic::fnSetFxVol(int32 *params) {
+ // params: 0 id of fx (ie. the id returned in 'result' from
+ // fnPlayFx
+ // 1 new volume (0..16)
+
+ _vm->_sound->setFxIdVolumePan(params[0], params[1]);
+ return IR_CONT;
+}
+
+int32 Logic::fnRestoreGame(int32 *params) {
+ // params: none
+ return IR_CONT;
+}
+
+int32 Logic::fnRefreshInventory(int32 *params) {
+ // Called from 'menu_look_or_combine' script in 'menu_master' object
+ // to update the menu to display a combined object while George runs
+ // voice-over. Note that 'object_held' must be set to the graphic of
+ // the combined object
+
+ // params: none
+
+ _vm->_mouse->refreshInventory();
+ return IR_CONT;
+}
+
+int32 Logic::fnChangeShadows(int32 *params) {
+ // params: none
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ // if last screen was using a shading mask (see below)
+ if (screenInfo->mask_flag) {
+ uint32 rv = _vm->_screen->closeLightMask();
+ if (rv)
+ error("Driver Error %.8x", rv);
+ screenInfo->mask_flag = false;
+ }
+
+ return IR_CONT;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/header.h b/engines/sword2/header.h
new file mode 100644
index 0000000000..9aefa52832
--- /dev/null
+++ b/engines/sword2/header.h
@@ -0,0 +1,502 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _HEADER
+#define _HEADER
+
+#include "common/stream.h"
+
+namespace Sword2 {
+
+//----------------------------------------------------------
+// SYSTEM FILE & FRAME HEADERS
+//----------------------------------------------------------
+
+//----------------------------------------------------------
+// ALL FILES
+//----------------------------------------------------------
+
+// Standard File Header
+
+#define NAME_LEN 34
+
+struct ResHeader {
+ uint8 fileType; // Byte to define file type (see below)
+ uint8 compType; // Type of file compression used ie.
+ // on whole file (see below)
+ uint32 compSize; // Length of compressed file (ie.
+ // length on disk)
+ uint32 decompSize; // Length of decompressed file held in
+ // memory (NB. frames still held
+ // compressed)
+ byte name[NAME_LEN]; // Name of object
+
+ static const int size() {
+ return 44;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ fileType = readS.readByte();
+ compType = readS.readByte();
+ compSize = readS.readUint32LE();
+ decompSize = readS.readUint32LE();
+ readS.read(name, NAME_LEN);
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeByte(fileType);
+ writeS.writeByte(compType);
+ writeS.writeUint32LE(compSize);
+ writeS.writeUint32LE(decompSize);
+ writeS.write(name, NAME_LEN);
+ }
+};
+
+// fileType
+
+enum {
+ // 0 something's wrong!
+ ANIMATION_FILE = 1, // All normal animations & sprites
+ // including mega-sets & font files
+ // which are the same format (but all
+ // frames always uncompressed)
+ SCREEN_FILE = 2, // Each contains background, palette,
+ // layer sprites, parallax layers &
+ // shading mask
+ GAME_OBJECT = 3, // Each contains object hub +
+ // structures + script data
+ WALK_GRID_FILE = 4, // Walk-grid data
+ GLOBAL_VAR_FILE = 5, // All the global script variables in
+ // one file; "there can be only one"
+ PARALLAX_FILE_null = 6, // NOT USED
+ RUN_LIST = 7, // Each contains a list of object
+ // resource id's
+ TEXT_FILE = 8, // Each contains all the lines of text
+ // for a location or a character's
+ // conversation script
+ SCREEN_MANAGER = 9, // One for each location; this contains
+ // special startup scripts
+ MOUSE_FILE = 10, // Mouse pointers and luggage icons
+ // (sprites in General / Mouse pointers
+ // & Luggage icons)
+ WAV_FILE = 11, // WAV file
+ ICON_FILE = 12, // Menu icon (sprites in General / Menu
+ // icons)
+ PALETTE_FILE = 13 // separate palette file (see also
+ // _paletteHeader)
+};
+
+// compType
+
+enum {
+ NO_COMPRESSION = 0,
+ FILE_COMPRESSION = 1 // standard whole-file compression
+ // (not yet devised!)
+};
+
+//----------------------------------------------------------
+// (1) ANIMATION FILES
+//----------------------------------------------------------
+
+// an animation file consists of:
+//
+// standard file header
+// animation header
+// a string of CDT entries (one per frame of the anim)
+// a 16-byte colour table ONLY if (runTimeComp==RLE16)
+// a string of groups of (frame header + frame data)
+
+// Animation Header
+
+struct AnimHeader {
+ uint8 runTimeComp; // Type of runtime compression used for the
+ // frame data (see below)
+ uint16 noAnimFrames; // Number of frames in the anim (ie. no. of
+ // CDT entries)
+ uint16 feetStartX; // Start coords for mega to walk to, before
+ uint16 feetStartY; // running anim
+ uint8 feetStartDir; // Direction to start in before running anim
+ uint16 feetEndX; // End coords for mega to stand at after
+ uint16 feetEndY; // running anim (vital if anim starts from an
+ // off-screen position, or ends in a different
+ // place from the start)
+ uint8 feetEndDir; // Direction to start in after running anim
+ uint16 blend;
+
+ static const int size() {
+ return 15;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ runTimeComp = readS.readByte();
+ noAnimFrames = readS.readUint16LE();
+ feetStartX = readS.readUint16LE();
+ feetStartY = readS.readUint16LE();
+ feetStartDir = readS.readByte();
+ feetEndX = readS.readUint16LE();
+ feetEndY = readS.readUint16LE();
+ feetEndDir = readS.readByte();
+ blend = readS.readUint16LE();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeByte(runTimeComp);
+ writeS.writeUint16LE(noAnimFrames);
+ writeS.writeUint16LE(feetStartX);
+ writeS.writeUint16LE(feetStartY);
+ writeS.writeByte(feetStartDir);
+ writeS.writeUint16LE(feetEndX);
+ writeS.writeUint16LE(feetEndY);
+ writeS.writeByte(feetEndDir);
+ writeS.writeUint16LE(blend);
+ }
+
+};
+
+// runtimeComp - compression used on each frame of the anim
+
+enum {
+ NONE = 0, // No frame compression
+ RLE256 = 1, // James's RLE for 256-colour sprites
+ RLE16 = 2 // James's RLE for 16- or 17-colour sprites
+ // (raw blocks have max 16 colours for 2 pixels
+ // per byte, so '0's are encoded only as FLAT
+ // for 17-colour sprites eg. George's mega-set)
+};
+
+// CDT Entry
+
+struct CdtEntry {
+ int16 x; // sprite x-coord OR offset to add to mega's
+ // feet x-coord to calc sprite y-coord
+ int16 y; // sprite y-coord OR offset to add to mega's
+ // feet y-coord to calc sprite y-coord
+ uint32 frameOffset; // points to start of frame header (from start
+ // of file header)
+ uint8 frameType; // 0 = print sprite normally with top-left
+ // corner at (x,y), otherwise see below...
+
+ static const int size() {
+ return 9;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ x = readS.readUint16LE();
+ y = readS.readUint16LE();
+ frameOffset = readS.readUint32LE();
+ frameType = readS.readByte();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(x);
+ writeS.writeUint16LE(y);
+ writeS.writeUint32LE(frameOffset);
+ writeS.writeByte(frameType);
+ }
+};
+
+// 'frameType' bit values
+
+enum {
+ FRAME_OFFSET = 1, // Print at (feetX + x, feetY + y), with
+ // scaling according to feetY
+ FRAME_FLIPPED = 2, // Print the frame flipped Left->Right
+ FRAME_256_FAST = 4 // Frame has been compressed using Pauls fast
+ // RLE 256 compression.
+};
+
+// Frame Header
+
+struct FrameHeader {
+ uint32 compSize; // Compressed size of frame - NB. compression
+ // type is now in Anim Header
+ uint16 width; // Dimensions of frame
+ uint16 height;
+
+ static const int size() {
+ return 8;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ compSize = readS.readUint32LE();
+ width = readS.readUint16LE();
+ height = readS.readUint16LE();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint32LE(compSize);
+ writeS.writeUint16LE(width);
+ writeS.writeUint16LE(height);
+ }
+};
+
+//----------------------------------------------------------
+// (2) SCREEN FILES
+//----------------------------------------------------------
+// a screen file consists of:
+//
+// standard file header
+// multi screen header
+// 4 * 256 bytes of palette data
+// 256k palette match table
+// 2 background parallax layers
+// 1 background layer with screen header
+// 2 foreground parallax layers
+// a string of layer headers
+// a string of layer masks
+
+// Multi screen header
+// Goes at the beginning of a screen file after the standard header.
+// Gives offsets from start of table of each of the components
+
+struct MultiScreenHeader {
+ uint32 palette;
+ uint32 bg_parallax[2];
+ uint32 screen;
+ uint32 fg_parallax[2];
+ uint32 layers;
+ uint32 paletteTable;
+ uint32 maskOffset;
+
+ static const int size() {
+ return 36;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ palette = readS.readUint32LE();
+ bg_parallax[0] = readS.readUint32LE();
+ bg_parallax[1] = readS.readUint32LE();
+ screen = readS.readUint32LE();
+ fg_parallax[0] = readS.readUint32LE();
+ fg_parallax[1] = readS.readUint32LE();
+ layers = readS.readUint32LE();
+ paletteTable = readS.readUint32LE();
+ maskOffset = readS.readUint32LE();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint32LE(palette);
+ writeS.writeUint32LE(bg_parallax[0]);
+ writeS.writeUint32LE(bg_parallax[1]);
+ writeS.writeUint32LE(screen);
+ writeS.writeUint32LE(fg_parallax[0]);
+ writeS.writeUint32LE(fg_parallax[1]);
+ writeS.writeUint32LE(layers);
+ writeS.writeUint32LE(paletteTable);
+ writeS.writeUint32LE(maskOffset);
+ }
+};
+
+// Screen Header
+
+struct ScreenHeader {
+ uint16 width; // dimensions of the background screen
+ uint16 height;
+ uint16 noLayers; // number of layer areas
+
+ static const int size() {
+ return 6;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ width = readS.readUint16LE();
+ height = readS.readUint16LE();
+ noLayers = readS.readUint16LE();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(width);
+ writeS.writeUint16LE(height);
+ writeS.writeUint16LE(noLayers);
+ }
+};
+
+// Layer Header
+
+// Note that all the layer headers are kept together, rather than being placed
+// before each layer mask, in order to simplify the sort routine.
+
+struct LayerHeader {
+ uint16 x; // coordinates of top-left pixel of area
+ uint16 y;
+ uint16 width;
+ uint16 height;
+ uint32 maskSize;
+ uint32 offset; // where to find mask data (from start of
+ // standard file header)
+
+ static const int size() {
+ return 16;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ x = readS.readUint16LE();
+ y = readS.readUint16LE();
+ width = readS.readUint16LE();
+ height = readS.readUint16LE();
+ maskSize = readS.readUint32LE();
+ offset = readS.readUint32LE();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint16LE(x);
+ writeS.writeUint16LE(y);
+ writeS.writeUint16LE(width);
+ writeS.writeUint16LE(height);
+ writeS.writeUint32LE(maskSize);
+ writeS.writeUint32LE(offset);
+ }
+};
+
+//----------------------------------------------------------
+// (3) SCRIPT OBJECT FILES
+//----------------------------------------------------------
+// a script object file consists of:
+//
+// standard file header
+// script object header
+// script object data
+
+//----------------------------------------------------------
+// (5) PALETTE FILES
+//----------------------------------------------------------
+// a palette file consists of:
+//
+// standard file header
+// 4 * 256 bytes of palette data
+// 256k palette match table
+
+// an object hub - which represents all that remains of the compact concept
+
+class ObjectHub {
+ // int32 type; // type of object
+ // uint32 logic_level; // what level?
+ // uint32 logic[3] // NOT USED
+ // uint32 script_id[3] // need this if script
+ // uint32 script_pc[3] // need this also
+
+private:
+ byte *_addr;
+
+public:
+ ObjectHub() {
+ _addr = NULL;
+ }
+
+ static const int size() {
+ return 44;
+ }
+
+ byte *data() {
+ return _addr;
+ }
+
+ void setAddress(byte *addr) {
+ _addr = addr;
+ }
+
+ byte *getScriptPcPtr(int level) {
+ return _addr + 32 + 4 * level;
+ }
+
+ uint32 getLogicLevel() {
+ return READ_LE_UINT32(_addr + 4);
+ }
+ uint32 getScriptId(int level) {
+ return READ_LE_UINT32(_addr + 20 + 4 * level);
+ }
+ uint32 getScriptPc(int level) {
+ return READ_LE_UINT32(_addr + 32 + 4 * level);
+ }
+
+ void setLogicLevel(uint32 x) {
+ WRITE_LE_UINT32(_addr + 4, x);
+ }
+ void setScriptId(int level, uint32 x) {
+ WRITE_LE_UINT32(_addr + 20 + 4 * level, x);
+ }
+ void setScriptPc(int level, uint32 x) {
+ WRITE_LE_UINT32(_addr + 32 + 4 * level, x);
+ }
+};
+
+// (6) text module header
+
+struct TextHeader {
+ uint32 noOfLines; // how many lines of text are there in this
+ // module
+
+ static const int size() {
+ return 4;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ noOfLines = readS.readUint32LE();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint32LE(noOfLines);
+ }
+};
+
+// a text file has:
+//
+// ResHeader
+// TextHeader
+// look up table, to
+// line of text,0
+// line of text,0
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/icons.cpp b/engines/sword2/icons.cpp
new file mode 100644
index 0000000000..980c20e3b4
--- /dev/null
+++ b/engines/sword2/icons.cpp
@@ -0,0 +1,216 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/stream.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+
+namespace Sword2 {
+
+void Mouse::addMenuObject(byte *ptr) {
+ assert(_totalTemp < TOTAL_engine_pockets);
+
+ Common::MemoryReadStream readS(ptr, 2 * sizeof(int32));
+
+ _tempList[_totalTemp].icon_resource = readS.readSint32LE();
+ _tempList[_totalTemp].luggage_resource = readS.readSint32LE();
+ _totalTemp++;
+}
+
+void Mouse::addSubject(int32 id, int32 ref) {
+ uint32 in_subject = _vm->_logic->readVar(IN_SUBJECT);
+
+ if (in_subject == 0) {
+ // This is the start of the new subject list. Set the default
+ // repsonse id to zero in case we're never passed one.
+ _defaultResponseId = 0;
+ }
+
+ if (id == -1) {
+ // Id -1 is used for setting the default response, i.e. the
+ // response when someone uses an object on a person and he
+ // doesn't know anything about it. See fnChoose().
+
+ _defaultResponseId = ref;
+ } else {
+ debug(5, "fnAddSubject res %d, uid %d", id, ref);
+ _subjectList[in_subject].res = id;
+ _subjectList[in_subject].ref = ref;
+ _vm->_logic->writeVar(IN_SUBJECT, in_subject + 1);
+ }
+}
+
+/**
+ * Create and start the inventory (bottom) menu
+ */
+
+void Mouse::buildMenu() {
+ uint32 i, j;
+
+ // Clear the temporary inventory list, since we are going to build a
+ // new one from scratch.
+
+ for (i = 0; i < TOTAL_engine_pockets; i++)
+ _tempList[i].icon_resource = 0;
+
+ _totalTemp = 0;
+
+ // Run the 'build_menu' script in the 'menu_master' object. This will
+ // register all carried menu objects.
+
+ _vm->_logic->runResScript(MENU_MASTER_OBJECT, 0);
+
+ // Create a new master list based on the old master inventory list and
+ // the new temporary inventory list. The purpose of all this is, as
+ // far as I can tell, that the new list is ordered in the same way as
+ // the old list, with new objects added to the end of it.
+
+ // Compare new with old. Anything in master thats not in new gets
+ // removed from master - if found in new too, remove from temp
+
+ for (i = 0; i < _totalMasters; i++) {
+ bool found_in_temp = false;
+
+ for (j = 0; j < TOTAL_engine_pockets; j++) {
+ if (_masterMenuList[i].icon_resource == _tempList[j].icon_resource) {
+ // We alread know about this object, so kill it
+ // in the temporary list.
+ _tempList[j].icon_resource = 0;
+ found_in_temp = true;
+ break;
+ }
+ }
+
+ if (!found_in_temp) {
+ // The object is in the master list, but not in the
+ // temporary list. The player must have lost the object
+ // since the last time we checked, so kill it in the
+ // master list.
+ _masterMenuList[i].icon_resource = 0;
+ }
+ }
+
+ // Eliminate blank entries from the master list.
+
+ _totalMasters = 0;
+
+ for (i = 0; i < TOTAL_engine_pockets; i++) {
+ if (_masterMenuList[i].icon_resource) {
+ if (i != _totalMasters) {
+ memcpy(&_masterMenuList[_totalMasters], &_masterMenuList[i], sizeof(MenuObject));
+ _masterMenuList[i].icon_resource = 0;
+ }
+ _totalMasters++;
+ }
+ }
+
+ // Add the new objects - i.e. the ones still in the temporary list but
+ // not yet in the master list - to the end of the master.
+
+ for (i = 0; i < TOTAL_engine_pockets; i++) {
+ if (_tempList[i].icon_resource) {
+ memcpy(&_masterMenuList[_totalMasters++], &_tempList[i], sizeof(MenuObject));
+ }
+ }
+
+ // Initialise the menu from the master list.
+
+ for (i = 0; i < 15; i++) {
+ uint32 res = _masterMenuList[i].icon_resource;
+ byte *icon = NULL;
+
+ if (res) {
+ bool icon_coloured;
+
+ uint32 object_held = _vm->_logic->readVar(OBJECT_HELD);
+ uint32 combine_base = _vm->_logic->readVar(COMBINE_BASE);
+
+ if (_examiningMenuIcon) {
+ // When examining an object, that object is
+ // coloured. The rest are greyed out.
+ icon_coloured = (res == object_held);
+ } else if (combine_base) {
+ // When combining two menu object (i.e. using
+ // one on another), both are coloured. The rest
+ // are greyed out.
+ icon_coloured = (res == object_held || combine_base);
+ } else {
+ // If an object is selected but we are not yet
+ // doing anything with it, the selected object
+ // is greyed out. The rest are coloured.
+ icon_coloured = (res != object_held);
+ }
+
+ icon = _vm->_resman->openResource(res) + ResHeader::size();
+
+ // The coloured icon is stored directly after the
+ // greyed out one.
+
+ if (icon_coloured)
+ icon += (RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+ }
+
+ setMenuIcon(RDMENU_BOTTOM, i, icon);
+
+ if (res)
+ _vm->_resman->closeResource(res);
+ }
+
+ showMenu(RDMENU_BOTTOM);
+}
+
+/**
+ * Build a fresh system (top) menu.
+ */
+
+void Mouse::buildSystemMenu() {
+ uint32 icon_list[5] = {
+ OPTIONS_ICON,
+ QUIT_ICON,
+ SAVE_ICON,
+ RESTORE_ICON,
+ RESTART_ICON
+ };
+
+ // Build them all high in full colour - when one is clicked on all the
+ // rest will grey out.
+
+ for (int i = 0; i < ARRAYSIZE(icon_list); i++) {
+ byte *icon = _vm->_resman->openResource(icon_list[i]) + ResHeader::size();
+
+ // The only case when an icon is grayed is when the player
+ // is dead. Then SAVE is not available.
+
+ if (!_vm->_logic->readVar(DEAD) || icon_list[i] != SAVE_ICON)
+ icon += (RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+
+ setMenuIcon(RDMENU_TOP, i, icon);
+ _vm->_resman->closeResource(icon_list[i]);
+ }
+
+ showMenu(RDMENU_TOP);
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/icons.h b/engines/sword2/icons.h
new file mode 100644
index 0000000000..ab5ea578ca
--- /dev/null
+++ b/engines/sword2/icons.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _ICONS
+#define _ICONS
+
+#define MENU_MASTER_OBJECT 44
+#define TOTAL_subjects (375 - 256 + 1) // the speech subject bar
+#define TOTAL_engine_pockets (15 + 10) // +10 for overflow
+
+namespace Sword2 {
+
+// define these in a script and then register them with the system
+
+struct MenuObject {
+ int32 icon_resource; // icon graphic graphic
+ int32 luggage_resource; // luggage icon resource (for attaching to
+ // mouse pointer)
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/interpreter.cpp b/engines/sword2/interpreter.cpp
new file mode 100644
index 0000000000..1a6e7080b8
--- /dev/null
+++ b/engines/sword2/interpreter.cpp
@@ -0,0 +1,753 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/interpreter.h"
+#include "sword2/logic.h"
+#include "sword2/memory.h"
+#include "sword2/resman.h"
+
+namespace Sword2 {
+
+#define STACK_SIZE 10
+
+// The machine code table
+
+#ifndef REDUCE_MEMORY_USAGE
+# define OPCODE(x) { &Logic::x, #x }
+#else
+# define OPCODE(x) { &Logic::x, "" }
+#endif
+
+typedef int32 (Logic::*OpcodeProc)(int32 *);
+struct OpcodeEntry {
+ OpcodeProc proc;
+ const char *desc;
+};
+
+static const OpcodeEntry opcodes[] = {
+ /* 00 */
+ OPCODE(fnTestFunction),
+ OPCODE(fnTestFlags),
+ OPCODE(fnRegisterStartPoint),
+ OPCODE(fnInitBackground),
+ /* 04 */
+ OPCODE(fnSetSession),
+ OPCODE(fnBackSprite),
+ OPCODE(fnSortSprite),
+ OPCODE(fnForeSprite),
+ /* 08 */
+ OPCODE(fnRegisterMouse),
+ OPCODE(fnAnim),
+ OPCODE(fnRandom),
+ OPCODE(fnPreLoad),
+ /* 0C */
+ OPCODE(fnAddSubject),
+ OPCODE(fnInteract),
+ OPCODE(fnChoose),
+ OPCODE(fnWalk),
+ /* 10 */
+ OPCODE(fnWalkToAnim),
+ OPCODE(fnTurn),
+ OPCODE(fnStandAt),
+ OPCODE(fnStand),
+ /* 14 */
+ OPCODE(fnStandAfterAnim),
+ OPCODE(fnPause),
+ OPCODE(fnMegaTableAnim),
+ OPCODE(fnAddMenuObject),
+ /* 18 */
+ OPCODE(fnStartConversation),
+ OPCODE(fnEndConversation),
+ OPCODE(fnSetFrame),
+ OPCODE(fnRandomPause),
+ /* 1C */
+ OPCODE(fnRegisterFrame),
+ OPCODE(fnNoSprite),
+ OPCODE(fnSendSync),
+ OPCODE(fnUpdatePlayerStats),
+ /* 20 */
+ OPCODE(fnPassGraph),
+ OPCODE(fnInitFloorMouse),
+ OPCODE(fnPassMega),
+ OPCODE(fnFaceXY),
+ /* 24 */
+ OPCODE(fnEndSession),
+ OPCODE(fnNoHuman),
+ OPCODE(fnAddHuman),
+ OPCODE(fnWeWait),
+ /* 28 */
+ OPCODE(fnTheyDoWeWait),
+ OPCODE(fnTheyDo),
+ OPCODE(fnWalkToTalkToMega),
+ OPCODE(fnFadeDown),
+ /* 2C */
+ OPCODE(fnISpeak),
+ OPCODE(fnTotalRestart),
+ OPCODE(fnSetWalkGrid),
+ OPCODE(fnSpeechProcess),
+ /* 30 */
+ OPCODE(fnSetScaling),
+ OPCODE(fnStartEvent),
+ OPCODE(fnCheckEventWaiting),
+ OPCODE(fnRequestSpeech),
+ /* 34 */
+ OPCODE(fnGosub),
+ OPCODE(fnTimedWait),
+ OPCODE(fnPlayFx),
+ OPCODE(fnStopFx),
+ /* 38 */
+ OPCODE(fnPlayMusic),
+ OPCODE(fnStopMusic),
+ OPCODE(fnSetValue),
+ OPCODE(fnNewScript),
+ /* 3C */
+ OPCODE(fnGetSync),
+ OPCODE(fnWaitSync),
+ OPCODE(fnRegisterWalkGrid),
+ OPCODE(fnReverseMegaTableAnim),
+ /* 40 */
+ OPCODE(fnReverseAnim),
+ OPCODE(fnAddToKillList),
+ OPCODE(fnSetStandbyCoords),
+ OPCODE(fnBackPar0Sprite),
+ /* 44 */
+ OPCODE(fnBackPar1Sprite),
+ OPCODE(fnForePar0Sprite),
+ OPCODE(fnForePar1Sprite),
+ OPCODE(fnSetPlayerActionEvent),
+ /* 48 */
+ OPCODE(fnSetScrollCoordinate),
+ OPCODE(fnStandAtAnim),
+ OPCODE(fnSetScrollLeftMouse),
+ OPCODE(fnSetScrollRightMouse),
+ /* 4C */
+ OPCODE(fnColour),
+ OPCODE(fnFlash),
+ OPCODE(fnPreFetch),
+ OPCODE(fnGetPlayerSaveData),
+ /* 50 */
+ OPCODE(fnPassPlayerSaveData),
+ OPCODE(fnSendEvent),
+ OPCODE(fnAddWalkGrid),
+ OPCODE(fnRemoveWalkGrid),
+ /* 54 */
+ OPCODE(fnCheckForEvent),
+ OPCODE(fnPauseForEvent),
+ OPCODE(fnClearEvent),
+ OPCODE(fnFaceMega),
+ /* 58 */
+ OPCODE(fnPlaySequence),
+ OPCODE(fnShadedSprite),
+ OPCODE(fnUnshadedSprite),
+ OPCODE(fnFadeUp),
+ /* 5C */
+ OPCODE(fnDisplayMsg),
+ OPCODE(fnSetObjectHeld),
+ OPCODE(fnAddSequenceText),
+ OPCODE(fnResetGlobals),
+ /* 60 */
+ OPCODE(fnSetPalette),
+ OPCODE(fnRegisterPointerText),
+ OPCODE(fnFetchWait),
+ OPCODE(fnRelease),
+ /* 64 */
+ OPCODE(fnPrepareMusic),
+ OPCODE(fnSoundFetch),
+ OPCODE(fnPrepareMusic), // Again, apparently
+ OPCODE(fnSmackerLeadIn),
+ /* 68 */
+ OPCODE(fnSmackerLeadOut),
+ OPCODE(fnStopAllFx),
+ OPCODE(fnCheckPlayerActivity),
+ OPCODE(fnResetPlayerActivityDelay),
+ /* 6C */
+ OPCODE(fnCheckMusicPlaying),
+ OPCODE(fnPlayCredits),
+ OPCODE(fnSetScrollSpeedNormal),
+ OPCODE(fnSetScrollSpeedSlow),
+ /* 70 */
+ OPCODE(fnRemoveChooser),
+ OPCODE(fnSetFxVolAndPan),
+ OPCODE(fnSetFxVol),
+ OPCODE(fnRestoreGame),
+ /* 74 */
+ OPCODE(fnRefreshInventory),
+ OPCODE(fnChangeShadows)
+};
+
+#define push(value) \
+do { \
+ assert(stackPtr < ARRAYSIZE(stack)); \
+ stack[stackPtr++] = (value); \
+} while (false)
+
+#define push_ptr(ptr) push(_vm->_memory->encodePtr(ptr))
+
+#define pop() (assert(stackPtr < ARRAYSIZE(stack)), stack[--stackPtr])
+
+int Logic::runResScript(uint32 scriptRes, uint32 offset) {
+ byte *scriptAddr;
+ int result;
+
+ scriptAddr = _vm->_resman->openResource(scriptRes);
+ result = runScript(scriptAddr, scriptAddr, offset);
+ _vm->_resman->closeResource(scriptRes);
+
+ return result;
+}
+
+int Logic::runResObjScript(uint32 scriptRes, uint32 objRes, uint32 offset) {
+ byte *scriptAddr, *objAddr;
+ int result;
+
+ scriptAddr = _vm->_resman->openResource(scriptRes);
+ objAddr = _vm->_resman->openResource(objRes);
+ result = runScript(scriptAddr, objAddr, offset);
+ _vm->_resman->closeResource(objRes);
+ _vm->_resman->closeResource(scriptRes);
+
+ return result;
+}
+
+int Logic::runScript(byte *scriptData, byte *objectData, uint32 offset) {
+ byte pc[4];
+
+ WRITE_LE_UINT32(pc, offset);
+ return runScript2(scriptData, objectData, pc);
+}
+
+// This form of the runScript function is only called directly from
+// the processSession() function, which uses it to update the script PC in the
+// current object hub. For reasons which I do not understand, I couldn't get it
+// to work if I called the function first with a dummy offset variable, and
+// and then updated the object hub myself.
+
+int Logic::runScript2(byte *scriptData, byte *objectData, byte *offsetPtr) {
+ // Interestingly, unlike our BASS engine the stack is a local variable.
+ // I don't know whether or not this is relevant to the working of the
+ // BS2 engine.
+
+ int32 stack[STACK_SIZE];
+ int32 stackPtr = 0;
+
+ uint32 offset = READ_LE_UINT32(offsetPtr);
+
+ ResHeader header;
+
+ header.read(scriptData);
+
+ scriptData += ResHeader::size() + ObjectHub::size();
+
+ // The script data format:
+ // int32_TYPE 1 Size of variable space in bytes
+ // ... The variable space
+ // int32_TYPE 1 numberOfScripts
+ // int32_TYPE numberOfScripts The offsets for each script
+
+ // Initialise some stuff
+
+ uint32 ip = 0; // Code pointer
+ int scriptNumber;
+
+ // Get the start of variables and start of code
+
+ byte *localVars = scriptData + 4;
+ byte *code = scriptData + READ_LE_UINT32(scriptData) + 4;
+ uint32 noScripts = READ_LE_UINT32(code);
+
+ code += 4;
+
+ byte *offsetTable = code;
+
+ if (offset < noScripts) {
+ ip = READ_LE_UINT32(offsetTable + offset * 4);
+ scriptNumber = offset;
+ debug(8, "Starting script %d from %d", scriptNumber, ip);
+ } else {
+ uint i;
+
+ ip = offset;
+
+ for (i = 1; i < noScripts; i++) {
+ if (READ_LE_UINT32(offsetTable + 4 * i) >= ip)
+ break;
+ }
+
+ scriptNumber = i - 1;
+ debug(8, "Resuming script %d from %d", scriptNumber, ip);
+ }
+
+ // There are a couple of known script bugs related to interacting with
+ // certain objects. We try to work around a few of them.
+
+ bool checkMopBug = false;
+ bool checkPyramidBug = false;
+ bool checkElevatorBug = false;
+
+ if (scriptNumber == 2) {
+ if (strcmp((char *)header.name, "mop_73") == 0)
+ checkMopBug = true;
+ else if (strcmp((char *)header.name, "titipoco_81") == 0)
+ checkPyramidBug = true;
+ else if (strcmp((char *)header.name, "lift_82") == 0)
+ checkElevatorBug = true;
+ }
+
+ code += noScripts * 4;
+
+ // Code should now be pointing at an identifier and a checksum
+ byte *checksumBlock = code;
+
+ code += 4 * 3;
+
+ if (READ_LE_UINT32(checksumBlock) != 12345678) {
+ error("Invalid script in object %s", header.name);
+ return 0;
+ }
+
+ int32 codeLen = READ_LE_UINT32(checksumBlock + 4);
+ int32 checksum = 0;
+
+ for (int i = 0; i < codeLen; i++)
+ checksum += (unsigned char) code[i];
+
+ if (checksum != (int32)READ_LE_UINT32(checksumBlock + 8)) {
+ debug(1, "Checksum error in object %s", header.name);
+ // This could be bad, but there has been a report about someone
+ // who had problems running the German version because of
+ // checksum errors. Could there be a version where checksums
+ // weren't properly calculated?
+ }
+
+ bool runningScript = true;
+
+ int parameterReturnedFromMcodeFunction = 0; // Allow scripts to return things
+ int savedStartOfMcode = 0; // For saving start of mcode commands
+
+ while (runningScript) {
+ int i;
+ int32 a, b;
+ int curCommand, parameter, value; // Command and parameter variables
+ int retVal;
+ int caseCount;
+ bool foundCase;
+ byte *ptr;
+
+ curCommand = code[ip++];
+
+ switch (curCommand) {
+
+ // Script-related opcodes
+
+ case CP_END_SCRIPT:
+ // End the script
+ runningScript = false;
+
+ // WORKAROUND: The dreaded pyramid bug makes the torch
+ // untakeable when you speak to Titipoco. This is
+ // because one of the conditions for the torch to be
+ // takeable is that Titipoco isn't doing anything out
+ // of the ordinary. Global variable 913 has to be 0 to
+ // signify that he is in his "idle" state.
+ //
+ // Unfortunately, simply the act of speaking to him
+ // sets variable 913 to 1 (probably to stop him from
+ // turning around every now and then). The script may
+ // then go on to set the variable to different values
+ // to trigger various behaviours in him, but if you
+ // have run out of these cases the script won't ever
+ // set it back to 0 again.
+ //
+ // So if his click hander finishes, and variable 913 is
+ // 1, we set it back to 0 manually.
+
+ if (checkPyramidBug && readVar(913) == 1) {
+ warning("Working around pyramid bug: Resetting Titipoco's state");
+ writeVar(913, 0);
+ }
+
+ // WORKAROUND: The not-so-known-but-should-be-dreaded
+ // elevator bug.
+ //
+ // The click handler for the top of the elevator only
+ // handles using the elevator, not examining it. When
+ // examining it, the mouse cursor is removed but never
+ // restored.
+
+ if (checkElevatorBug && readVar(RIGHT_BUTTON)) {
+ warning("Working around elevator bug: Restoring mouse pointer");
+ fnAddHuman(NULL);
+ }
+
+ debug(9, "CP_END_SCRIPT");
+ break;
+ case CP_QUIT:
+ // Quit out for a cycle
+ WRITE_LE_UINT32(offsetPtr, ip);
+ debug(9, "CP_QUIT");
+ return 0;
+ case CP_TERMINATE:
+ // Quit out immediately without affecting the offset
+ // pointer
+ debug(9, "CP_TERMINATE");
+ return 3;
+ case CP_RESTART_SCRIPT:
+ // Start the script again
+ ip = FROM_LE_32(offsetTable[scriptNumber]);
+ debug(9, "CP_RESTART_SCRIPT");
+ break;
+
+ // Stack-related opcodes
+
+ case CP_PUSH_INT32:
+ // Push a long word value on to the stack
+ Read32ip(parameter);
+ push(parameter);
+ debug(9, "CP_PUSH_INT32: %d", parameter);
+ break;
+ case CP_PUSH_LOCAL_VAR32:
+ // Push the contents of a local variable
+ Read16ip(parameter);
+ push(READ_LE_UINT32(localVars + parameter));
+ debug(9, "CP_PUSH_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, READ_LE_UINT32(localVars + parameter));
+ break;
+ case CP_PUSH_GLOBAL_VAR32:
+ // Push a global variable
+ Read16ip(parameter);
+ push(readVar(parameter));
+ debug(9, "CP_PUSH_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, readVar(parameter));
+ break;
+ case CP_PUSH_LOCAL_ADDR:
+ // Push the address of a local variable
+
+ // From what I understand, some scripts store data
+ // (e.g. mouse pointers) in their local variable space
+ // from the very beginning, and use this mechanism to
+ // pass that data to the opcode function. I don't yet
+ // know the conceptual difference between this and the
+ // CP_PUSH_DEREFERENCED_STRUCTURE opcode.
+
+ Read16ip(parameter);
+ push_ptr(localVars + parameter);
+ debug(9, "CP_PUSH_LOCAL_ADDR: &localVars[%d] => %p", parameter / 4, localVars + parameter);
+ break;
+ case CP_PUSH_STRING:
+ // Push the address of a string on to the stack
+ // Get the string size
+ Read8ip(parameter);
+
+ // ip now points to the string
+ ptr = code + ip;
+ push_ptr(ptr);
+ debug(9, "CP_PUSH_STRING: \"%s\"", ptr);
+ ip += (parameter + 1);
+ break;
+ case CP_PUSH_DEREFERENCED_STRUCTURE:
+ // Push the address of a dereferenced structure
+ Read32ip(parameter);
+ ptr = objectData + 4 + ResHeader::size() + ObjectHub::size() + parameter;
+ push_ptr(ptr);
+ debug(9, "CP_PUSH_DEREFERENCED_STRUCTURE: %d => %p", parameter, ptr);
+ break;
+ case CP_POP_LOCAL_VAR32:
+ // Pop a value into a local word variable
+ Read16ip(parameter);
+ value = pop();
+ WRITE_LE_UINT32(localVars + parameter, value);
+ debug(9, "CP_POP_LOCAL_VAR32: localVars[%d] = %d", parameter / 4, value);
+ break;
+ case CP_POP_GLOBAL_VAR32:
+ // Pop a global variable
+ Read16ip(parameter);
+ value = pop();
+
+ // WORKAROUND for bug #1214168: The not-at-all dreaded
+ // mop bug.
+ //
+ // At the London Docks, global variable 1003 keeps
+ // track of Nico:
+ //
+ // 0: Hiding behind the first crate.
+ // 1: Hiding behind the second crate.
+ // 2: Standing in plain view on the deck.
+ // 3: Hiding on the roof.
+ //
+ // The bug happens when trying to pick up the mop while
+ // hiding on the roof. Nico climbs down, the mop is
+ // picked up, but the variable remains set to 3.
+ // Visually, everything looks ok. But as far as the
+ // scripts are concerned, she's still hiding up on the
+ // roof. This is not fatal, but leads to a number of
+ // glitches until the state is corrected. E.g. trying
+ // to climb back up the ladder will cause Nico to climb
+ // down again.
+ //
+ // Global variable 1017 keeps track of the mop. Setting
+ // it to 2 means that the mop has been picked up. We
+ // use that as the signal that Nico's state needs to be
+ // updated as well.
+
+ if (checkMopBug && parameter == 1017 && readVar(1003) != 2) {
+ warning("Working around mop bug: Setting Nico's state");
+ writeVar(1003, 2);
+ }
+
+ writeVar(parameter, value);
+ debug(9, "CP_POP_GLOBAL_VAR32: scriptsVars[%d] = %d", parameter, value);
+ break;
+ case CP_ADDNPOP_LOCAL_VAR32:
+ Read16ip(parameter);
+ value = READ_LE_UINT32(localVars + parameter) + pop();
+ WRITE_LE_UINT32(localVars + parameter, value);
+ debug(9, "CP_ADDNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value);
+ break;
+ case CP_SUBNPOP_LOCAL_VAR32:
+ Read16ip(parameter);
+ value = READ_LE_UINT32(localVars + parameter) - pop();
+ WRITE_LE_UINT32(localVars + parameter, value);
+ debug(9, "CP_SUBNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value);
+ break;
+ case CP_ADDNPOP_GLOBAL_VAR32:
+ // Add and pop a global variable
+ Read16ip(parameter);
+ value = readVar(parameter) + pop();
+ writeVar(parameter, value);
+ debug(9, "CP_ADDNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value);
+ break;
+ case CP_SUBNPOP_GLOBAL_VAR32:
+ // Sub and pop a global variable
+ Read16ip(parameter);
+ value = readVar(parameter) - pop();
+ writeVar(parameter, value);
+ debug(9, "CP_SUBNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value);
+ break;
+
+ // Jump opcodes
+
+ case CP_SKIPONTRUE:
+ // Skip if the value on the stack is true
+ Read32ipLeaveip(parameter);
+ value = pop();
+ if (!value) {
+ ip += 4;
+ debug(9, "CP_SKIPONTRUE: %d (IS FALSE (NOT SKIPPED))", parameter);
+ } else {
+ ip += parameter;
+ debug(9, "CP_SKIPONTRUE: %d (IS TRUE (SKIPPED))", parameter);
+ }
+ break;
+ case CP_SKIPONFALSE:
+ // Skip if the value on the stack is false
+ Read32ipLeaveip(parameter);
+ value = pop();
+ if (value) {
+ ip += 4;
+ debug(9, "CP_SKIPONFALSE: %d (IS TRUE (NOT SKIPPED))", parameter);
+ } else {
+ ip += parameter;
+ debug(9, "CP_SKIPONFALSE: %d (IS FALSE (SKIPPED))", parameter);
+ }
+ break;
+ case CP_SKIPALWAYS:
+ // skip a block
+ Read32ipLeaveip(parameter);
+ ip += parameter;
+ debug(9, "CP_SKIPALWAYS: %d", parameter);
+ break;
+ case CP_SWITCH:
+ // switch
+ value = pop();
+ Read32ip(caseCount);
+
+ // Search the cases
+ foundCase = false;
+ for (i = 0; i < caseCount && !foundCase; i++) {
+ if (value == (int32)READ_LE_UINT32(code + ip)) {
+ // We have found the case, so lets
+ // jump to it
+ foundCase = true;
+ ip += READ_LE_UINT32(code + ip + 4);
+ } else
+ ip += 4 * 2;
+ }
+
+ // If we found no matching case then use the default
+
+ if (!foundCase)
+ ip += READ_LE_UINT32(code + ip);
+
+ debug(9, "CP_SWITCH: [SORRY, NO DEBUG INFO]");
+ break;
+ case CP_SAVE_MCODE_START:
+ // Save the start position on an mcode instruction in
+ // case we need to restart it again
+ savedStartOfMcode = ip - 1;
+ debug(9, "CP_SAVE_MCODE_START");
+ break;
+ case CP_CALL_MCODE:
+ // Call an mcode routine
+ Read16ip(parameter);
+ assert(parameter < ARRAYSIZE(opcodes));
+ // amount to adjust stack by (no of parameters)
+ Read8ip(value);
+ debug(9, "CP_CALL_MCODE: '%s', %d", opcodes[parameter].desc, value);
+ stackPtr -= value;
+ assert(stackPtr >= 0);
+ retVal = (this->*opcodes[parameter].proc)(&stack[stackPtr]);
+
+ switch (retVal & 7) {
+ case IR_STOP:
+ // Quit out for a cycle
+ WRITE_LE_UINT32(offsetPtr, ip);
+ return 0;
+ case IR_CONT:
+ // Continue as normal
+ break;
+ case IR_TERMINATE:
+ // Return without updating the offset
+ return 2;
+ case IR_REPEAT:
+ // Return setting offset to start of this
+ // function call
+ WRITE_LE_UINT32(offsetPtr, savedStartOfMcode);
+ return 0;
+ case IR_GOSUB:
+ // that's really neat
+ WRITE_LE_UINT32(offsetPtr, ip);
+ return 2;
+ default:
+ error("Bad return code (%d) from '%s'", retVal & 7, opcodes[parameter].desc);
+ }
+ parameterReturnedFromMcodeFunction = retVal >> 3;
+ break;
+ case CP_JUMP_ON_RETURNED:
+ // Jump to a part of the script depending on
+ // the return value from an mcode routine
+
+ // Get the maximum value
+ Read8ip(parameter);
+ debug(9, "CP_JUMP_ON_RETURNED: %d => %d",
+ parameterReturnedFromMcodeFunction,
+ READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4));
+ ip += READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4);
+ break;
+
+ // Operators
+
+ case OP_ISEQUAL:
+ b = pop();
+ a = pop();
+ push(a == b);
+ debug(9, "OP_ISEQUAL: RESULT = %d", a == b);
+ break;
+ case OP_NOTEQUAL:
+ b = pop();
+ a = pop();
+ push(a != b);
+ debug(9, "OP_NOTEQUAL: RESULT = %d", a != b);
+ break;
+ case OP_GTTHAN:
+ b = pop();
+ a = pop();
+ push(a > b);
+ debug(9, "OP_GTTHAN: RESULT = %d", a > b);
+ break;
+ case OP_LSTHAN:
+ b = pop();
+ a = pop();
+ push(a < b);
+ debug(9, "OP_LSTHAN: RESULT = %d", a < b);
+ break;
+ case OP_GTTHANE:
+ b = pop();
+ a = pop();
+ push(a >= b);
+ debug(9, "OP_GTTHANE: RESULT = %d", a >= b);
+ break;
+ case OP_LSTHANE:
+ b = pop();
+ a = pop();
+ push(a <= b);
+ debug(9, "OP_LSTHANE: RESULT = %d", a <= b);
+ break;
+ case OP_PLUS:
+ b = pop();
+ a = pop();
+ push(a + b);
+ debug(9, "OP_PLUS: RESULT = %d", a + b);
+ break;
+ case OP_MINUS:
+ b = pop();
+ a = pop();
+ push(a - b);
+ debug(9, "OP_MINUS: RESULT = %d", a - b);
+ break;
+ case OP_TIMES:
+ b = pop();
+ a = pop();
+ push(a * b);
+ debug(9, "OP_TIMES: RESULT = %d", a * b);
+ break;
+ case OP_DIVIDE:
+ b = pop();
+ a = pop();
+ push(a / b);
+ debug(9, "OP_DIVIDE: RESULT = %d", a / b);
+ break;
+ case OP_ANDAND:
+ b = pop();
+ a = pop();
+ push(a && b);
+ debug(9, "OP_ANDAND: RESULT = %d", a && b);
+ break;
+ case OP_OROR:
+ b = pop();
+ a = pop();
+ push(a || b);
+ debug(9, "OP_OROR: RESULT = %d", a || b);
+ break;
+
+ // Debugging opcodes, I think
+
+ case CP_DEBUGON:
+ debug(9, "CP_DEBUGON");
+ break;
+ case CP_DEBUGOFF:
+ debug(9, "CP_DEBUGOFF");
+ break;
+ case CP_TEMP_TEXT_PROCESS:
+ Read32ip(parameter);
+ debug(9, "CP_TEMP_TEXT_PROCESS: %d", parameter);
+ break;
+ default:
+ error("Invalid script command %d", curCommand);
+ return 3;
+ }
+ }
+
+ return 1;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/interpreter.h b/engines/sword2/interpreter.h
new file mode 100644
index 0000000000..365f818a98
--- /dev/null
+++ b/engines/sword2/interpreter.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _INTERPRETER
+#define _INTERPRETER
+
+namespace Sword2 {
+
+// Interpreter return codes
+
+enum {
+ IR_STOP = 0, // Quit for a cycle
+ IR_CONT = 1, // Continue as normal
+ IR_TERMINATE = 2, // Return without updating the offset
+ IR_REPEAT = 3, // Return; offset at start of function call
+ IR_GOSUB = 4 // Return with updated offset
+};
+
+// Get parameter fix so that the playstation version can handle words not on
+// word boundaries
+
+#define Read8ip(var) { var = code[ip]; ip++; }
+#define Read16ip(var) { var = (int16)READ_LE_UINT16(code + ip); ip += 2; }
+#define Read32ip(var) { var = (int32)READ_LE_UINT32(code + ip); ip += 4; }
+#define Read32ipLeaveip(var) { var = (int32)READ_LE_UINT32(code + ip); }
+
+enum {
+ // Compiled tokens
+
+ CP_END_SCRIPT = 0,
+ CP_PUSH_LOCAL_VAR32 = 1, // Push a local variable on to the stack
+ CP_PUSH_GLOBAL_VAR32 = 2, // Push a global variable
+ CP_POP_LOCAL_VAR32 = 3, // Pop a local variable from the stack
+ CP_CALL_MCODE = 4, // Call a machine code function
+ CP_PUSH_LOCAL_ADDR = 5, // Push the address of a local variable
+ CP_PUSH_INT32 = 6, // Adjust the stack after calling an fn function
+ CP_SKIPONFALSE = 7, // Skip if the bottom value on the stack is false
+ CP_SKIPALWAYS = 8, // Skip a block of code
+ CP_SWITCH = 9, // Switch on last stack value
+ CP_ADDNPOP_LOCAL_VAR32 = 10, // Add to a local varible
+ CP_SUBNPOP_LOCAL_VAR32 = 11, // Subtract from a local variable
+ CP_SKIPONTRUE = 12, // Skip if the bottom value on the stack is true
+ CP_POP_GLOBAL_VAR32 = 13, // Pop a global variable
+ CP_ADDNPOP_GLOBAL_VAR32 = 14, // Add to a global variable
+ CP_SUBNPOP_GLOBAL_VAR32 = 15, // Subtract from a global variable
+ CP_DEBUGON = 16, // Turn debugging on
+ CP_DEBUGOFF = 17, // Turn debugging off
+ CP_QUIT = 18, // Quit for a cycle
+ CP_TERMINATE = 19, // Quit script completely
+
+ // Operators
+
+ OP_ISEQUAL = 20, // '=='
+ OP_PLUS = 21, // '+'
+ OP_MINUS = 22, // '-'
+ OP_TIMES = 23, // '*'
+ OP_DIVIDE = 24, // '/'
+ OP_NOTEQUAL = 25, // '=='
+ OP_ANDAND = 26, // '&&'
+ OP_GTTHAN = 27, // '>'
+ OP_LSTHAN = 28, // '<'
+
+ // More tokens, mixed types
+
+ CP_JUMP_ON_RETURNED = 29, // Use table of jumps with value returned from fn_mcode
+ CP_TEMP_TEXT_PROCESS = 30, // A dummy text process command for me
+ CP_SAVE_MCODE_START = 31, // Save the mcode code start for restarting when necessary
+ CP_RESTART_SCRIPT = 32, // Start the script from the beginning
+ CP_PUSH_STRING = 33, // Push a pointer to a string on the stack
+ CP_PUSH_DEREFERENCED_STRUCTURE = 34, // Push the address of a structure thing
+ OP_GTTHANE = 35, // >=
+ OP_LSTHANE = 36, // <=
+ OP_OROR = 37 // || or OR
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/layers.cpp b/engines/sword2/layers.cpp
new file mode 100644
index 0000000000..0b59b5a9b1
--- /dev/null
+++ b/engines/sword2/layers.cpp
@@ -0,0 +1,191 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// high level layer initialising
+
+// the system supports:
+// 1 optional background parallax layer
+// 1 not optional normal backdrop layer
+// 3 normal sorted layers
+// up to 2 foreground parallax layers
+
+#include "common/stdafx.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+
+namespace Sword2 {
+
+/**
+ * This function is called when entering a new room.
+ * @param res resource id of the normal background layer
+ * @param new_palette 1 for new palette, otherwise 0
+ */
+
+void Screen::initBackground(int32 res, int32 new_palette) {
+ byte buf[NAME_LEN];
+ int i;
+
+ assert(res);
+
+ _vm->_sound->clearFxQueue();
+ waitForFade();
+
+ debug(1, "CHANGED TO LOCATION \"%s\"", _vm->_resman->fetchName(res, buf));
+
+ // if last screen was using a shading mask (see below)
+ if (_thisScreen.mask_flag) {
+ if (closeLightMask() != RD_OK)
+ error("Could not close light mask");
+ }
+
+ // Close the previous screen, if one is open
+ if (_thisScreen.background_layer_id)
+ closeBackgroundLayer();
+
+ _thisScreen.background_layer_id = res;
+ _thisScreen.new_palette = new_palette;
+
+ // ok, now read the resource and pull out all the normal sort layer
+ // info/and set them up at the beginning of the sort list - why do it
+ // each cycle
+
+ byte *file = _vm->_resman->openResource(_thisScreen.background_layer_id);
+ ScreenHeader screen_head;
+
+ screen_head.read(_vm->fetchScreenHeader(file));
+
+ // set number of special sort layers
+ _thisScreen.number_of_layers = screen_head.noLayers;
+ _thisScreen.screen_wide = screen_head.width;
+ _thisScreen.screen_deep = screen_head.height;
+
+ debug(2, "layers=%d width=%d depth=%d", screen_head.noLayers, screen_head.width, screen_head.height);
+
+ // initialise the driver back buffer
+ setLocationMetrics(screen_head.width, screen_head.height);
+
+ for (i = 0; i < screen_head.noLayers; i++) {
+ debug(3, "init layer %d", i);
+
+ LayerHeader layer;
+
+ layer.read(_vm->fetchLayerHeader(file, i));
+
+ // Add the layer to the sort list. We only provide just enough
+ // information so that it's clear that it's a layer, and where
+ // to sort it in relation to other things in the list.
+
+ _sortList[i].layer_number = i + 1;
+ _sortList[i].sort_y = layer.y + layer.height;
+ }
+
+ // reset scroll offsets
+ _thisScreen.scroll_offset_x = 0;
+ _thisScreen.scroll_offset_y = 0;
+
+ if (screen_head.width > _screenWide || screen_head.height > _screenDeep) {
+ // The layer is larger than the physical screen. Switch on
+ // scrolling. (2 means first time on screen)
+ _thisScreen.scroll_flag = 2;
+
+ // Note: if we've already set the player up then we could do
+ // the initial scroll set here
+
+ // Calculate the maximum scroll offsets to prevent scrolling
+ // off the edge. The minimum offsets are both 0.
+
+ _thisScreen.max_scroll_offset_x = screen_head.width - _screenWide;
+ _thisScreen.max_scroll_offset_y = screen_head.height - (_screenDeep - (MENUDEEP * 2));
+ } else {
+ // The later fits on the phyiscal screen. Switch off scrolling.
+ _thisScreen.scroll_flag = 0;
+ }
+
+ resetRenderEngine();
+
+ // These are the physical screen coords where the system will try to
+ // maintain George's actual feet coords.
+
+ _thisScreen.feet_x = 320;
+ _thisScreen.feet_y = 340;
+
+ // shading mask
+
+ MultiScreenHeader screenLayerTable;
+
+ screenLayerTable.read(file + ResHeader::size());
+
+ if (screenLayerTable.maskOffset) {
+ SpriteInfo spriteInfo;
+
+ spriteInfo.x = 0;
+ spriteInfo.y = 0;
+ spriteInfo.w = screen_head.width;
+ spriteInfo.h = screen_head.height;
+ spriteInfo.scale = 0;
+ spriteInfo.scaledWidth = 0;
+ spriteInfo.scaledHeight = 0;
+ spriteInfo.type = 0;
+ spriteInfo.blend = 0;
+ spriteInfo.data = _vm->fetchShadingMask(file);
+ spriteInfo.colourTable = 0;
+
+ if (openLightMask(&spriteInfo) != RD_OK)
+ error("Could not open light mask");
+
+ // so we know to close it later! (see above)
+ _thisScreen.mask_flag = true;
+ } else {
+ // no need to close a mask later
+ _thisScreen.mask_flag = false;
+ }
+
+ // Background parallax layers
+
+ for (i = 0; i < 2; i++) {
+ if (screenLayerTable.bg_parallax[i])
+ initialiseBackgroundLayer(_vm->fetchBackgroundParallaxLayer(file, i));
+ else
+ initialiseBackgroundLayer(NULL);
+ }
+
+ // Normal backround layer
+
+ initialiseBackgroundLayer(_vm->fetchBackgroundLayer(file));
+
+ // Foreground parallax layers
+
+ for (i = 0; i < 2; i++) {
+ if (screenLayerTable.fg_parallax[i])
+ initialiseBackgroundLayer(_vm->fetchForegroundParallaxLayer(file, i));
+ else
+ initialiseBackgroundLayer(NULL);
+ }
+
+ _vm->_resman->closeResource(_thisScreen.background_layer_id);
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/layers.h b/engines/sword2/layers.h
new file mode 100644
index 0000000000..88aff933b3
--- /dev/null
+++ b/engines/sword2/layers.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _LAYERS
+#define _LAYERS
+
+namespace Sword2 {
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/logic.cpp b/engines/sword2/logic.cpp
new file mode 100644
index 0000000000..c26d5615b9
--- /dev/null
+++ b/engines/sword2/logic.cpp
@@ -0,0 +1,267 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/resman.h"
+#include "sword2/router.h"
+#include "sword2/sound.h"
+
+namespace Sword2 {
+
+Logic::Logic(Sword2Engine *vm) :
+ _vm(vm), _kills(0), _currentRunList(0), _smackerLeadIn(0),
+ _smackerLeadOut(0), _sequenceTextLines(0), _speechTime(0), _animId(0),
+ _speechAnimType(0), _leftClickDelay(0), _rightClickDelay(0),
+ _officialTextNumber(0), _speechTextBlocNo(0) {
+
+ _scriptVars = NULL;
+ memset(_eventList, 0, sizeof(_eventList));
+ memset(_syncList, 0, sizeof(_syncList));
+ _router = new Router(_vm);
+}
+
+Logic::~Logic() {
+ delete _router;
+}
+
+/**
+ * Do one cycle of the current session.
+ */
+
+int Logic::processSession() {
+ // might change during the session, so take a copy here
+ uint32 run_list = _currentRunList;
+
+ _pc = 0; // first object in list
+
+ // by minusing the pc we can cause an immediate cessation of logic
+ // processing on the current list
+
+ while (_pc != 0xffffffff) {
+ byte *game_object_list, *head, *raw_script_ad, *raw_data_ad;
+ uint32 level, ret, script, id;
+
+ game_object_list = _vm->_resman->openResource(run_list) + ResHeader::size();
+
+ assert(_vm->_resman->fetchType(run_list) == RUN_LIST);
+
+ // read the next id
+ id = READ_LE_UINT32(game_object_list + 4 * _pc);
+ _pc++;
+
+ writeVar(ID, id);
+
+ _vm->_resman->closeResource(run_list);
+
+ if (!id) {
+ // End of list - end the session naturally
+ return 0;
+ }
+
+ assert(_vm->_resman->fetchType(id) == GAME_OBJECT);
+
+ head = _vm->_resman->openResource(id);
+ _curObjectHub.setAddress(head + ResHeader::size());
+
+ level = _curObjectHub.getLogicLevel();
+
+ debug(5, "Level %d id(%d) pc(%d)",
+ level,
+ _curObjectHub.getScriptId(level),
+ _curObjectHub.getScriptPc(level));
+
+ // Do the logic for this object. We keep going until a function
+ // says to stop - remember, system operations are run via
+ // function calls to drivers now.
+
+ do {
+ // There is a distinction between running one of our
+ // own scripts and that of another object.
+
+ level = _curObjectHub.getLogicLevel();
+ script = _curObjectHub.getScriptId(level);
+
+ if (script / SIZE == readVar(ID)) {
+ // It's our own script
+
+ debug(5, "Run script %d pc=%d",
+ script / SIZE,
+ _curObjectHub.getScriptPc(level));
+
+ // This is the script data. Script and data
+ // object are the same.
+
+ raw_script_ad = head;
+
+ ret = runScript2(raw_script_ad, raw_script_ad, _curObjectHub.getScriptPcPtr(level));
+ } else {
+ // We're running the script of another game
+ // object - get our data object address.
+
+ uint8 type = _vm->_resman->fetchType(script / SIZE);
+
+ assert(type == GAME_OBJECT || type == SCREEN_MANAGER);
+
+ raw_script_ad = _vm->_resman->openResource(script / SIZE);
+ raw_data_ad = head;
+
+ ret = runScript2(raw_script_ad, raw_data_ad, _curObjectHub.getScriptPcPtr(level));
+
+ _vm->_resman->closeResource(script / SIZE);
+
+ // reset to us for service script
+ raw_script_ad = raw_data_ad;
+ }
+
+ if (ret == 1) {
+ level = _curObjectHub.getLogicLevel();
+
+ // The script finished - drop down a level
+ if (level) {
+ _curObjectHub.setLogicLevel(level - 1);
+ } else {
+ // Hmmm, level 0 terminated :-| Let's
+ // be different this time and simply
+ // let it restart next go :-)
+
+ // Note that this really does happen a
+ // lot, so don't make it a warning.
+
+ debug(5, "object %d script 0 terminated!", id);
+
+ // reset to rerun, drop out for a cycle
+ _curObjectHub.setScriptPc(level, _curObjectHub.getScriptId(level) & 0xffff);
+ ret = 0;
+ }
+ } else if (ret > 2) {
+ error("processSession: illegal script return type %d", ret);
+ }
+
+ // if ret == 2 then we simply go around again - a new
+ // script or subroutine will kick in and run
+
+ // keep processing scripts until 0 for quit is returned
+ } while (ret);
+
+ // Any post logic system requests to go here
+
+ // Clear any syncs that were waiting for this character - it
+ // has used them or now looses them
+
+ clearSyncs(readVar(ID));
+
+ if (_pc != 0xffffffff) {
+ // The session is still valid so run the graphics/mouse
+ // service script
+ runScript(raw_script_ad, raw_script_ad, 0);
+ }
+
+ // and that's it so close the object resource
+
+ _vm->_resman->closeResource(readVar(ID));
+ }
+
+ // Leaving a room so remove all ids that must reboot correctly. Then
+ // restart the loop.
+
+ for (uint32 i = 0; i < _kills; i++)
+ _vm->_resman->remove(_objectKillList[i]);
+
+ resetKillList();
+ return 1;
+}
+
+/**
+ * Bring an immediate halt to the session and cause a new one to start without
+ * a screen update.
+ */
+
+void Logic::expressChangeSession(uint32 sesh_id) {
+ // Set new session and force the old one to quit.
+ _currentRunList = sesh_id;
+ _pc = 0xffffffff;
+
+ // Reset now in case we double-clicked an exit prior to changing screen
+ writeVar(EXIT_FADING, 0);
+
+ // We're trashing the list - presumably to change room. In theory,
+ // sync waiting in the list could be left behind and never removed -
+ // so we trash the lot
+ memset(_syncList, 0, sizeof(_syncList));
+
+ // Various clean-ups
+ _router->clearWalkGridList();
+ _vm->_sound->clearFxQueue();
+ _router->freeAllRouteMem();
+}
+
+/**
+ * @return The private _currentRunList variable.
+ */
+
+uint32 Logic::getRunList() {
+ return _currentRunList;
+}
+
+/**
+ * Move the current object up a level. Called by fnGosub command. Remember:
+ * only the logic object has access to _curObjectHub.
+ */
+
+void Logic::logicUp(uint32 new_script) {
+ debug(5, "new pc = %d", new_script & 0xffff);
+
+ // going up a level - and we'll keep going this cycle
+ _curObjectHub.setLogicLevel(_curObjectHub.getLogicLevel() + 1);
+
+ assert(_curObjectHub.getLogicLevel() < 3); // Can be 0, 1, 2
+ logicReplace(new_script);
+}
+
+/**
+ * Force the level to one.
+ */
+
+void Logic::logicOne(uint32 new_script) {
+ _curObjectHub.setLogicLevel(1);
+ logicReplace(new_script);
+}
+
+/**
+ * Change current logic. Script must quit with a TERMINATE directive, which
+ * does not write to &pc
+ */
+
+void Logic::logicReplace(uint32 new_script) {
+ uint32 level = _curObjectHub.getLogicLevel();
+
+ _curObjectHub.setScriptId(level, new_script);
+ _curObjectHub.setScriptPc(level, new_script & 0xffff);
+}
+
+void Logic::resetKillList() {
+ _kills = 0;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/logic.h b/engines/sword2/logic.h
new file mode 100644
index 0000000000..9e264f603c
--- /dev/null
+++ b/engines/sword2/logic.h
@@ -0,0 +1,316 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// logic management
+
+#ifndef _LOGIC
+#define _LOGIC
+
+#include "sword2/memory.h"
+
+namespace Sword2 {
+
+struct MovieTextObject;
+
+#define MAX_events 10
+
+#define TREE_SIZE 3
+
+// This must allow for the largest number of objects in a screen
+#define OBJECT_KILL_LIST_SIZE 50
+
+#define MAX_SEQUENCE_TEXT_LINES 15
+
+class Sword2Engine;
+class Router;
+
+struct EventUnit {
+ uint32 id;
+ uint32 interact_id;
+};
+
+class Logic {
+private:
+ Sword2Engine *_vm;
+
+ inline byte *decodePtr(int32 n) {
+ return _vm->_memory->decodePtr(n);
+ }
+
+ uint32 _objectKillList[OBJECT_KILL_LIST_SIZE];
+
+ // keeps note of no. of objects in the kill list
+ uint32 _kills;
+
+ // denotes the res id of the game-object-list in current use
+ uint32 _currentRunList;
+
+ //pc during logic loop
+ uint32 _pc;
+
+ // each object has one of these tacked onto the beginning
+ ObjectHub _curObjectHub;
+
+ EventUnit _eventList[MAX_events];
+
+ // Resource id of the wav to use as lead-in/lead-out from smacker
+ uint32 _smackerLeadIn;
+ uint32 _smackerLeadOut;
+
+ // keeps count of number of text lines to disaply during the sequence
+ uint32 _sequenceTextLines;
+
+ // FOR TEXT LINES IN SEQUENCE PLAYER
+
+ struct SequenceTextInfo {
+ uint32 textNumber;
+ uint16 startFrame;
+ uint16 endFrame;
+ byte *text_mem;
+ uint32 speechBufferSize;
+ uint16 *speech_mem;
+ };
+
+ SequenceTextInfo _sequenceTextList[MAX_SEQUENCE_TEXT_LINES];
+
+ void createSequenceSpeech(MovieTextObject *sequenceText[]);
+ void clearSequenceSpeech(MovieTextObject *sequenceText[]);
+
+ // when not playing a wav we calculate the speech time based upon
+ // length of ascii
+
+ uint32 _speechTime;
+
+ uint32 _animId;
+
+ // 0 lip synced and repeating - 1 normal once through
+ uint32 _speechAnimType;
+
+ uint32 _leftClickDelay; // click-delay for LEFT mouse button
+ uint32 _rightClickDelay; // click-delay for RIGHT mouse button
+
+ // calculated by locateTalker() for use in speech-panning & text-sprite
+ // positioning
+
+ int16 _textX, _textY;
+
+ void locateTalker(int32 *params);
+ void formText(int32 *params);
+ bool wantSpeechForLine(uint32 wavId);
+
+ // Set by fnPassMega()
+ byte _engineMega[56];
+
+public:
+ Logic(Sword2Engine *vm);
+ ~Logic();
+
+ EventUnit *getEventList() { return _eventList; }
+ byte *getEngineMega() { return _engineMega; }
+
+ byte _saveLogic[8];
+ byte _saveGraphic[12];
+ byte _saveMega[56];
+
+ // Point to the global variable data
+ byte *_scriptVars;
+
+ // "TEXT" - current official text line number - will match the wav
+ // filenames
+
+ int16 _officialTextNumber;
+
+ // so speech text cleared when running a new start-script
+ uint32 _speechTextBlocNo;
+
+ uint32 readVar(int n) {
+ return READ_LE_UINT32(_scriptVars + 4 * n);
+ }
+
+ void writeVar(int n, uint32 value) {
+ WRITE_LE_UINT32(_scriptVars + 4 * n, value);
+ }
+
+ int runResScript(uint32 scriptRes, uint32 offset);
+ int runResObjScript(uint32 scriptRes, uint32 objRes, uint32 offset);
+ int runScript(byte *scriptData, byte *objectData, uint32 offset);
+ int runScript2(byte *scriptData, byte *objectData, byte *offset);
+
+ void sendEvent(uint32 id, uint32 interact_id);
+ void setPlayerActionEvent(uint32 id, uint32 interact_id);
+ void startEvent();
+ int checkEventWaiting();
+ void clearEvent(uint32 id);
+ void killAllIdsEvents(uint32 id);
+
+ uint32 countEvents();
+
+ struct SyncUnit {
+ uint32 id;
+ uint32 sync;
+ };
+
+ // There won't be many, will there? Probably 2 at most i reckon
+ SyncUnit _syncList[10];
+
+ void clearSyncs(uint32 id);
+ void sendSync(uint32 id, uint32 sync);
+ int getSync();
+
+ Router *_router;
+
+ int32 fnTestFunction(int32 *params);
+ int32 fnTestFlags(int32 *params);
+ int32 fnRegisterStartPoint(int32 *params);
+ int32 fnInitBackground(int32 *params);
+ int32 fnSetSession(int32 *params);
+ int32 fnBackSprite(int32 *params);
+ int32 fnSortSprite(int32 *params);
+ int32 fnForeSprite(int32 *params);
+ int32 fnRegisterMouse(int32 *params);
+ int32 fnAnim(int32 *params);
+ int32 fnRandom(int32 *params);
+ int32 fnPreLoad(int32 *params);
+ int32 fnAddSubject(int32 *params);
+ int32 fnInteract(int32 *params);
+ int32 fnChoose(int32 *params);
+ int32 fnWalk(int32 *params);
+ int32 fnWalkToAnim(int32 *params);
+ int32 fnTurn(int32 *params);
+ int32 fnStandAt(int32 *params);
+ int32 fnStand(int32 *params);
+ int32 fnStandAfterAnim(int32 *params);
+ int32 fnPause(int32 *params);
+ int32 fnMegaTableAnim(int32 *params);
+ int32 fnAddMenuObject(int32 *params);
+ int32 fnStartConversation(int32 *params);
+ int32 fnEndConversation(int32 *params);
+ int32 fnSetFrame(int32 *params);
+ int32 fnRandomPause(int32 *params);
+ int32 fnRegisterFrame(int32 *params);
+ int32 fnNoSprite(int32 *params);
+ int32 fnSendSync(int32 *params);
+ int32 fnUpdatePlayerStats(int32 *params);
+ int32 fnPassGraph(int32 *params);
+ int32 fnInitFloorMouse(int32 *params);
+ int32 fnPassMega(int32 *params);
+ int32 fnFaceXY(int32 *params);
+ int32 fnEndSession(int32 *params);
+ int32 fnNoHuman(int32 *params);
+ int32 fnAddHuman(int32 *params);
+ int32 fnWeWait(int32 *params);
+ int32 fnTheyDoWeWait(int32 *params);
+ int32 fnTheyDo(int32 *params);
+ int32 fnWalkToTalkToMega(int32 *params);
+ int32 fnFadeDown(int32 *params);
+ int32 fnISpeak(int32 *params);
+ int32 fnTotalRestart(int32 *params);
+ int32 fnSetWalkGrid(int32 *params);
+ int32 fnSpeechProcess(int32 *params);
+ int32 fnSetScaling(int32 *params);
+ int32 fnStartEvent(int32 *params);
+ int32 fnCheckEventWaiting(int32 *params);
+ int32 fnRequestSpeech(int32 *params);
+ int32 fnGosub(int32 *params);
+ int32 fnTimedWait(int32 *params);
+ int32 fnPlayFx(int32 *params);
+ int32 fnStopFx(int32 *params);
+ int32 fnPlayMusic(int32 *params);
+ int32 fnStopMusic(int32 *params);
+ int32 fnSetValue(int32 *params);
+ int32 fnNewScript(int32 *params);
+ int32 fnGetSync(int32 *params);
+ int32 fnWaitSync(int32 *params);
+ int32 fnRegisterWalkGrid(int32 *params);
+ int32 fnReverseMegaTableAnim(int32 *params);
+ int32 fnReverseAnim(int32 *params);
+ int32 fnAddToKillList(int32 *params);
+ int32 fnSetStandbyCoords(int32 *params);
+ int32 fnBackPar0Sprite(int32 *params);
+ int32 fnBackPar1Sprite(int32 *params);
+ int32 fnForePar0Sprite(int32 *params);
+ int32 fnForePar1Sprite(int32 *params);
+ int32 fnSetPlayerActionEvent(int32 *params);
+ int32 fnSetScrollCoordinate(int32 *params);
+ int32 fnStandAtAnim(int32 *params);
+ int32 fnSetScrollLeftMouse(int32 *params);
+ int32 fnSetScrollRightMouse(int32 *params);
+ int32 fnColour(int32 *params);
+ int32 fnFlash(int32 *params);
+ int32 fnPreFetch(int32 *params);
+ int32 fnGetPlayerSaveData(int32 *params);
+ int32 fnPassPlayerSaveData(int32 *params);
+ int32 fnSendEvent(int32 *params);
+ int32 fnAddWalkGrid(int32 *params);
+ int32 fnRemoveWalkGrid(int32 *params);
+ int32 fnCheckForEvent(int32 *params);
+ int32 fnPauseForEvent(int32 *params);
+ int32 fnClearEvent(int32 *params);
+ int32 fnFaceMega(int32 *params);
+ int32 fnPlaySequence(int32 *params);
+ int32 fnShadedSprite(int32 *params);
+ int32 fnUnshadedSprite(int32 *params);
+ int32 fnFadeUp(int32 *params);
+ int32 fnDisplayMsg(int32 *params);
+ int32 fnSetObjectHeld(int32 *params);
+ int32 fnAddSequenceText(int32 *params);
+ int32 fnResetGlobals(int32 *params);
+ int32 fnSetPalette(int32 *params);
+ int32 fnRegisterPointerText(int32 *params);
+ int32 fnFetchWait(int32 *params);
+ int32 fnRelease(int32 *params);
+ int32 fnPrepareMusic(int32 *params);
+ int32 fnSoundFetch(int32 *params);
+ int32 fnSmackerLeadIn(int32 *params);
+ int32 fnSmackerLeadOut(int32 *params);
+ int32 fnStopAllFx(int32 *params);
+ int32 fnCheckPlayerActivity(int32 *params);
+ int32 fnResetPlayerActivityDelay(int32 *params);
+ int32 fnCheckMusicPlaying(int32 *params);
+ int32 fnPlayCredits(int32 *params);
+ int32 fnSetScrollSpeedNormal(int32 *params);
+ int32 fnSetScrollSpeedSlow(int32 *params);
+ int32 fnRemoveChooser(int32 *params);
+ int32 fnSetFxVolAndPan(int32 *params);
+ int32 fnSetFxVol(int32 *params);
+ int32 fnRestoreGame(int32 *params);
+ int32 fnRefreshInventory(int32 *params);
+ int32 fnChangeShadows(int32 *params);
+
+ // do one cycle of the current session
+ int processSession();
+
+ // cause the logic loop to terminate and drop out
+ void expressChangeSession(uint32 sesh_id);
+
+ uint32 getRunList();
+
+ // setup script_id and script_pc in _curObjectHub - called by fnGosub()
+ void logicUp(uint32 new_script);
+
+ void logicReplace(uint32 new_script);
+ void logicOne(uint32 new_script);
+ void resetKillList();
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/maketext.cpp b/engines/sword2/maketext.cpp
new file mode 100644
index 0000000000..5edaf5e41e
--- /dev/null
+++ b/engines/sword2/maketext.cpp
@@ -0,0 +1,579 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// MAKETEXT - Constructs a single-frame text sprite: returns a handle to a
+// FLOATING memory block containing the sprite, given a
+// null-terminated string, max width allowed, pen colour and
+// pointer to required character set.
+//
+// NB 1) The routine does not create a standard file header or
+// an anim header for the text sprite - the data simply begins
+// with the frame header.
+//
+// NB 2) If pen colour is zero, it copies the characters into
+// the sprite without remapping the colours.
+// ie. It can handle both the standard 2-colour font for speech
+// and any multicoloured fonts for control panels, etc.
+//
+// Based on textsprt.c as used for Broken Sword 1, but updated
+// for new system by JEL on 9oct96 and updated again (for font
+// as a resource) on 5dec96.
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/resman.h"
+
+namespace Sword2 {
+
+#define MAX_LINES 30 // max character lines in output sprite
+
+#define BORDER_COL 200 // source colour for character border (only
+ // needed for remapping colours)
+#define LETTER_COL 193 // source colour for bulk of character ( " )
+#define SPACE ' '
+#define FIRST_CHAR SPACE // first character in character set
+#define LAST_CHAR 255 // last character in character set
+#define DUD 64 // the first "chequered flag" (dud) symbol in
+ // our character set is in the '@' position
+
+/**
+ * This function creates a new text sprite. The sprite data contains a
+ * FrameHeader, but not a standard file header.
+ *
+ * @param sentence pointer to a null-terminated string
+ * @param maxWidth the maximum allowed text sprite width in pixels
+ * @param pen the text colour, or zero to use the source colours
+ * @param fontRes the font resource id
+ * @param border the border colour; black by default
+ * @return a handle to a floating memory block containing the text sprite
+ * @note The sentence must contain no leading, trailing or extra spaces.
+ * Out-of-range characters in the string are replaced by a special
+ * error-signal character (chequered flag)
+ */
+
+byte *FontRenderer::makeTextSprite(byte *sentence, uint16 maxWidth, uint8 pen, uint32 fontRes, uint8 border) {
+ debug(5, "makeTextSprite(\"%s\", maxWidth=%u)", sentence, maxWidth);
+
+ _borderPen = border;
+
+ // Line- and character spacing are hard-wired, rather than being part
+ // of the resource.
+
+ if (fontRes == _vm->_speechFontId) {
+ _lineSpacing = -6;
+ _charSpacing = -3;
+ } else if (fontRes == CONSOLE_FONT_ID) {
+ _lineSpacing = 0;
+ _charSpacing = 1;
+ } else {
+ _lineSpacing = 0;
+ _charSpacing = 0;
+ }
+
+ // Allocate memory for array of lineInfo structures
+
+ byte *line = (byte *)malloc(MAX_LINES * sizeof(LineInfo));
+
+ // Get details of sentence breakdown into array of LineInfo structures
+ // and get the number of lines involved
+
+ uint16 noOfLines = analyseSentence(sentence, maxWidth, fontRes, (LineInfo *)line);
+
+ // Construct the sprite based on the info gathered - returns floating
+ // mem block
+
+ byte *textSprite = buildTextSprite(sentence, fontRes, pen, (LineInfo *)line, noOfLines);
+
+ free(line);
+ return textSprite;
+}
+
+uint16 FontRenderer::analyseSentence(byte *sentence, uint16 maxWidth, uint32 fontRes, LineInfo *line) {
+ // joinWidth = how much extra space is needed to append a word to a
+ // line. NB. SPACE requires TWICE the '_charSpacing' to join a word
+ // to line
+
+ uint16 joinWidth = charWidth(SPACE, fontRes) + 2 * _charSpacing;
+
+ uint16 lineNo = 0;
+ uint16 pos = 0;
+ bool firstWord = true;
+
+ byte ch;
+
+ do {
+ uint16 wordWidth = 0;
+ uint16 wordLength = 0;
+
+ // Calculate the width of the word.
+
+ ch = sentence[pos++];
+
+ while (ch && ch != SPACE) {
+ wordWidth += charWidth(ch, fontRes) + _charSpacing;
+ wordLength++;
+ ch = sentence[pos++];
+ }
+
+ // Don't include any character spacing at the end of the word.
+ wordWidth -= _charSpacing;
+
+ // 'ch' is now the SPACE or NULL following the word
+ // 'pos' indexes to the position following 'ch'
+
+ if (firstWord) {
+ // This is the first word on the line, so no separating
+ // space is needed.
+
+ line[0].width = wordWidth;
+ line[0].length = wordLength;
+ firstWord = false;
+ } else {
+ // See how much extra space this word will need to
+ // fit on current line (with a separating space
+ // character - also overlapped)
+
+ uint16 spaceNeeded = joinWidth + wordWidth;
+
+ if (line[lineNo].width + spaceNeeded <= maxWidth) {
+ // The word fits on this line.
+ line[lineNo].width += spaceNeeded;
+ line[lineNo].length += (1 + wordLength);
+ } else {
+ // The word spills over to the next line, i.e.
+ // no separating space.
+
+ lineNo++;
+
+ assert(lineNo < MAX_LINES);
+
+ line[lineNo].width = wordWidth;
+ line[lineNo].length = wordLength;
+ }
+ }
+ } while (ch);
+
+ return lineNo + 1;
+}
+
+/**
+ * This function creates a new text sprite in a movable memory block. It must
+ * be locked before use, i.e. lock, draw sprite, unlock/free. The sprite data
+ * contains a FrameHeader, but not a standard file header.
+ *
+ * @param sentence pointer to a null-terminated string
+ * @param fontRes the font resource id
+ * @param pen the text colour, or zero to use the source colours
+ * @param line array of LineInfo structures, created by analyseSentence()
+ * @param noOfLines the number of lines, i.e. the number of elements in 'line'
+ * @return a handle to a floating memory block containing the text sprite
+ * @note The sentence must contain no leading, trailing or extra spaces.
+ * Out-of-range characters in the string are replaced by a special
+ * error-signal character (chequered flag)
+ */
+
+byte *FontRenderer::buildTextSprite(byte *sentence, uint32 fontRes, uint8 pen, LineInfo *line, uint16 noOfLines) {
+ uint16 i;
+
+ // Find the width of the widest line in the output text
+
+ uint16 spriteWidth = 0;
+
+ for (i = 0; i < noOfLines; i++)
+ if (line[i].width > spriteWidth)
+ spriteWidth = line[i].width;
+
+ // Find the total height of the text sprite: the total height of the
+ // text lines, plus the total height of the spacing between them.
+
+ uint16 char_height = charHeight(fontRes);
+ uint16 spriteHeight = char_height * noOfLines + _lineSpacing * (noOfLines - 1);
+
+ // Allocate memory for the text sprite
+
+ uint32 sizeOfSprite = spriteWidth * spriteHeight;
+ byte *textSprite = (byte *)malloc(FrameHeader::size() + sizeOfSprite);
+
+ // At this stage, textSprite points to an unmovable memory block. Set
+ // up the frame header.
+
+ FrameHeader frame_head;
+
+ frame_head.compSize = 0;
+ frame_head.width = spriteWidth;
+ frame_head.height = spriteHeight;
+
+ frame_head.write(textSprite);
+
+ debug(4, "Text sprite size: %ux%u", spriteWidth, spriteHeight);
+
+ // Clear the entire sprite to make it transparent.
+
+ byte *linePtr = textSprite + FrameHeader::size();
+ memset(linePtr, 0, sizeOfSprite);
+
+ byte *charSet = _vm->_resman->openResource(fontRes);
+
+ // Build the sprite, one line at a time
+
+ uint16 pos = 0;
+
+ for (i = 0; i < noOfLines; i++) {
+ // Center each line
+ byte *spritePtr = linePtr + (spriteWidth - line[i].width) / 2;
+
+ // copy the sprite for each character in this line to the
+ // text sprite and inc the sprite ptr by the character's
+ // width minus the 'overlap'
+
+ for (uint j = 0; j < line[i].length; j++) {
+ byte *charPtr = findChar(sentence[pos++], charSet);
+
+ frame_head.read(charPtr);
+
+ assert(frame_head.height == char_height);
+ copyChar(charPtr, spritePtr, spriteWidth, pen);
+ spritePtr += frame_head.width + _charSpacing;
+ }
+
+ // Skip space at end of last word in this line
+ pos++;
+
+ linePtr += (char_height + _lineSpacing) * spriteWidth;
+ }
+
+ _vm->_resman->closeResource(fontRes);
+
+ return textSprite;
+}
+
+/**
+ * @param ch the ASCII code of the character
+ * @param fontRes the font resource id
+ * @return the width of the character
+ */
+
+uint16 FontRenderer::charWidth(byte ch, uint32 fontRes) {
+ byte *charSet = _vm->_resman->openResource(fontRes);
+
+ FrameHeader frame_head;
+
+ frame_head.read(findChar(ch, charSet));
+ _vm->_resman->closeResource(fontRes);
+
+ return frame_head.width;
+}
+
+/**
+ * @param fontRes the font resource id
+ * @return the height of a character sprite
+ * @note All characters in a font are assumed to have the same height, so
+ * there is no need to specify which one to look at.
+ */
+
+// Returns the height of a character sprite, given the character's ASCII code
+// and a pointer to the start of the character set.
+
+uint16 FontRenderer::charHeight(uint32 fontRes) {
+ byte *charSet = _vm->_resman->openResource(fontRes);
+
+ FrameHeader frame_head;
+
+ frame_head.read(findChar(FIRST_CHAR, charSet));
+ _vm->_resman->closeResource(fontRes);
+
+ return frame_head.height;
+}
+
+/**
+ * @param ch the ASCII code of the character to find
+ * @param charSet pointer to the start of the character set
+ * @return pointer to the requested character or, if it's out of range, the
+ * 'dud' character (chequered flag)
+ */
+
+byte *FontRenderer::findChar(byte ch, byte *charSet) {
+ if (ch < FIRST_CHAR)
+ ch = DUD;
+ return _vm->fetchFrameHeader(charSet, ch - FIRST_CHAR);
+}
+
+/**
+ * Copies a character sprite to the sprite buffer.
+ * @param charPtr pointer to the character sprite
+ * @param spritePtr pointer to the sprite buffer
+ * @param spriteWidth the width of the character
+ * @param pen If zero, copy the data directly. Otherwise remap the
+ * sprite's colours from BORDER_COL to _borderPen and from
+ * LETTER_COL to pen.
+ */
+
+void FontRenderer::copyChar(byte *charPtr, byte *spritePtr, uint16 spriteWidth, uint8 pen) {
+ FrameHeader frame;
+
+ frame.read(charPtr);
+
+ byte *source = charPtr + FrameHeader::size();
+ byte *rowPtr = spritePtr;
+
+ for (uint i = 0; i < frame.height; i++) {
+ byte *dest = rowPtr;
+
+ if (pen) {
+ // Use the specified colours
+ for (uint j = 0; j < frame.width; j++) {
+ switch (*source++) {
+ case LETTER_COL:
+ *dest = pen;
+ break;
+ case BORDER_COL:
+ // Don't do a border pixel if there's
+ // already a bit of another character
+ // underneath (for overlapping!)
+ if (!*dest)
+ *dest = _borderPen;
+ break;
+ default:
+ // Do nothing if source pixel is zero,
+ // ie. transparent
+ break;
+ }
+ dest++;
+ }
+ } else {
+ // Pen is zero, so just copy character sprites
+ // directly into text sprite without remapping colours.
+ // Apparently overlapping is never considered here?
+ memcpy(dest, source, frame.width);
+ source += frame.width;
+ }
+ rowPtr += spriteWidth;
+ }
+}
+
+// Distance to keep speech text from edges of screen
+#define TEXT_MARGIN 12
+
+/**
+ * Creates a text bloc in the list and returns the bloc number. The list of
+ * blocs is read and blitted at render time. Choose alignment type
+ * RDSPR_DISPLAYALIGN or 0
+ */
+
+uint32 FontRenderer::buildNewBloc(byte *ascii, int16 x, int16 y, uint16 width, uint8 pen, uint32 type, uint32 fontRes, uint8 justification) {
+ uint32 i = 0;
+
+ while (i < MAX_text_blocs && _blocList[i].text_mem)
+ i++;
+
+ assert(i < MAX_text_blocs);
+
+ // Create and position the sprite
+
+ _blocList[i].text_mem = makeTextSprite(ascii, width, pen, fontRes);
+
+ // 'NO_JUSTIFICATION' means print sprite with top-left at (x,y)
+ // without margin checking - used for debug text
+
+ if (justification != NO_JUSTIFICATION) {
+ FrameHeader frame_head;
+
+ frame_head.read(_blocList[i].text_mem);
+
+ switch (justification) {
+ case POSITION_AT_CENTRE_OF_BASE:
+ // This one is always used for SPEECH TEXT; possibly
+ // also for pointer text
+ x -= (frame_head.width / 2);
+ y -= frame_head.height;
+ break;
+ case POSITION_AT_CENTRE_OF_TOP:
+ x -= (frame_head.width / 2);
+ break;
+ case POSITION_AT_LEFT_OF_TOP:
+ // The given coords are already correct for this!
+ break;
+ case POSITION_AT_RIGHT_OF_TOP:
+ x -= frame_head.width;
+ break;
+ case POSITION_AT_LEFT_OF_BASE:
+ y -= frame_head.height;
+ break;
+ case POSITION_AT_RIGHT_OF_BASE:
+ x -= frame_head.width;
+ y -= frame_head.height;
+ break;
+ case POSITION_AT_LEFT_OF_CENTRE:
+ y -= (frame_head.height / 2);
+ break;
+ case POSITION_AT_RIGHT_OF_CENTRE:
+ x -= frame_head.width;
+ y -= (frame_head.height) / 2;
+ break;
+ }
+
+ // Ensure text sprite is a few pixels inside the visible screen
+ // remember - it's RDSPR_DISPLAYALIGN
+
+ uint16 text_left_margin = TEXT_MARGIN;
+ uint16 text_right_margin = 640 - TEXT_MARGIN - frame_head.width;
+ uint16 text_top_margin = TEXT_MARGIN;
+ uint16 text_bottom_margin = 400 - TEXT_MARGIN - frame_head.height;
+
+ // Move if too far left or too far right
+
+ if (x < text_left_margin)
+ x = text_left_margin;
+ else if (x > text_right_margin)
+ x = text_right_margin;
+
+ // Move if too high or too low
+
+ if (y < text_top_margin)
+ y = text_top_margin;
+ else if (y > text_bottom_margin)
+ y = text_bottom_margin;
+ }
+
+ // The sprite is always uncompressed
+ _blocList[i].type = type | RDSPR_NOCOMPRESSION;
+
+ _blocList[i].x = x;
+ _blocList[i].y = y;
+
+ return i + 1;
+}
+
+/**
+ * Called by buildDisplay()
+ */
+
+void FontRenderer::printTextBlocs() {
+ for (uint i = 0; i < MAX_text_blocs; i++) {
+ if (_blocList[i].text_mem) {
+ FrameHeader frame_head;
+ SpriteInfo spriteInfo;
+
+ frame_head.read(_blocList[i].text_mem);
+
+ spriteInfo.x = _blocList[i].x;
+ spriteInfo.y = _blocList[i].y;
+ spriteInfo.w = frame_head.width;
+ spriteInfo.h = frame_head.height;
+ spriteInfo.scale = 0;
+ spriteInfo.scaledWidth = 0;
+ spriteInfo.scaledHeight = 0;
+ spriteInfo.type = _blocList[i].type;
+ spriteInfo.blend = 0;
+ spriteInfo.data = _blocList[i].text_mem + FrameHeader::size();
+ spriteInfo.colourTable = 0;
+
+ uint32 rv = _vm->_screen->drawSprite(&spriteInfo);
+ if (rv)
+ error("Driver Error %.8x in printTextBlocs", rv);
+ }
+ }
+}
+
+void FontRenderer::killTextBloc(uint32 bloc_number) {
+ bloc_number--;
+ free(_blocList[bloc_number].text_mem);
+ _blocList[bloc_number].text_mem = NULL;
+}
+
+// Resource 3258 contains text from location script for 152 (install, save &
+// restore text, etc)
+
+#define TEXT_RES 3258
+
+// Local line number of "save" (actor no. 1826)
+
+#define SAVE_LINE_NO 1
+
+void Sword2Engine::initialiseFontResourceFlags() {
+ byte *textFile = _resman->openResource(TEXT_RES);
+
+ // If language is Polish or Finnish it requires alternate fonts.
+ // Otherwise, use regular fonts
+
+ // "tallenna" Finnish for "save"
+ // "zapisz" Polish for "save"
+
+ // Get the text line (& skip the 2 chars containing the wavId)
+ char *textLine = (char *)fetchTextLine(textFile, SAVE_LINE_NO) + 2;
+
+ if (strcmp(textLine, "tallenna") == 0)
+ initialiseFontResourceFlags(FINNISH_TEXT);
+ else if (strcmp(textLine, "zapisz") == 0)
+ initialiseFontResourceFlags(POLISH_TEXT);
+ else
+ initialiseFontResourceFlags(DEFAULT_TEXT);
+
+ // Get the game name for the windows application
+
+ // According to the GetNameFunction(), which was never called and has
+ // therefore been removed, the name of the game is:
+ //
+ // ENGLISH: "Broken Sword II"
+ // AMERICAN: "Circle of Blood II"
+ // GERMAN: "Baphomet's Fluch II"
+ // default: "Some game or other, part 86"
+ //
+ // But we get it from the text resource instead.
+
+ if (_logic->readVar(DEMO))
+ textLine = (char *)fetchTextLine(textFile, 451) + 2;
+ else
+ textLine = (char *)fetchTextLine(textFile, 54) + 2;
+
+ _system->setWindowCaption(textLine);
+ _resman->closeResource(TEXT_RES);
+}
+
+/**
+ * Called from initialiseFontResourceFlags(), and also from console.cpp
+ */
+
+void Sword2Engine::initialiseFontResourceFlags(uint8 language) {
+ switch (language) {
+ case FINNISH_TEXT:
+ _speechFontId = FINNISH_SPEECH_FONT_ID;
+ _controlsFontId = FINNISH_CONTROLS_FONT_ID;
+ _redFontId = FINNISH_RED_FONT_ID;
+ break;
+ case POLISH_TEXT:
+ _speechFontId = POLISH_SPEECH_FONT_ID;
+ _controlsFontId = POLISH_CONTROLS_FONT_ID;
+ _redFontId = POLISH_RED_FONT_ID;
+ break;
+ default:
+ _speechFontId = ENGLISH_SPEECH_FONT_ID;
+ _controlsFontId = ENGLISH_CONTROLS_FONT_ID;
+ _redFontId = ENGLISH_RED_FONT_ID;
+ break;
+ }
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/maketext.h b/engines/sword2/maketext.h
new file mode 100644
index 0000000000..364a412857
--- /dev/null
+++ b/engines/sword2/maketext.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _MAKETEXT_H
+#define _MAKETEXT_H
+
+#include "sword2/debug.h"
+
+namespace Sword2 {
+
+// Output colour for character border - should be be black but note that we
+// have to use a different pen number during sequences
+
+#define BORDER_PEN 194
+
+// Usually the only texts on screen are the subtitles and the mouse-over text,
+// but there can also be a considerable number of debugging messages...
+
+#define MAX_text_blocs MAX_DEBUG_TEXTS + 1
+
+enum {
+ // Doesn't keep the text inside the screen - only for debug text!
+ NO_JUSTIFICATION = 0,
+
+ // These all force text inside the screen edge margin when necessary
+ POSITION_AT_CENTRE_OF_BASE = 1,
+ POSITION_AT_CENTRE_OF_TOP = 2,
+ POSITION_AT_LEFT_OF_TOP = 3,
+ POSITION_AT_RIGHT_OF_TOP = 4,
+ POSITION_AT_LEFT_OF_BASE = 5,
+ POSITION_AT_RIGHT_OF_BASE = 6,
+ POSITION_AT_LEFT_OF_CENTRE = 7,
+ POSITION_AT_RIGHT_OF_CENTRE = 8
+};
+
+enum {
+ DEFAULT_TEXT = 0,
+ FINNISH_TEXT = 1,
+ POLISH_TEXT = 2
+};
+
+// Info about the text, used to create the SpriteInfo struct
+
+ struct TextBloc {
+ int16 x;
+ int16 y;
+ uint16 type;
+ byte *text_mem;
+};
+
+// Info for each line of words in the output text sprite
+
+struct LineInfo {
+ uint16 width; // Width in pixels
+ uint16 length; // Length in characters
+};
+
+class FontRenderer {
+private:
+ Sword2Engine *_vm;
+ TextBloc _blocList[MAX_text_blocs];
+
+ // Layout variables - these used to be defines, but now we're dealing
+ // with three character sets
+
+ int8 _lineSpacing; // no. of pixels to separate lines of
+ // characters in the output sprite - negative
+ // for overlap
+ int8 _charSpacing; // no. of pixels to separate characters along
+ // each line - negative for overlap
+ uint8 _borderPen; // output pen colour of character borders
+
+ uint16 analyseSentence(byte *sentence, uint16 maxWidth, uint32 fontRes, LineInfo *line);
+ byte *buildTextSprite(byte *sentence, uint32 fontRes, uint8 pen, LineInfo *line, uint16 noOfLines);
+ uint16 charWidth(byte ch, uint32 fontRes);
+ uint16 charHeight(uint32 fontRes);
+ byte *findChar(byte ch, byte *charSet);
+ void copyChar(byte *charPtr, byte *spritePtr, uint16 spriteWidth, uint8 pen);
+
+public:
+ FontRenderer(Sword2Engine *vm) : _vm(vm) {
+ for (int i = 0; i < MAX_text_blocs; i++)
+ _blocList[i].text_mem = NULL;
+ }
+
+ ~FontRenderer() {
+ for (int i = 0; i < MAX_text_blocs; i++)
+ free(_blocList[i].text_mem);
+ }
+
+ byte *makeTextSprite(byte *sentence, uint16 maxWidth, uint8 pen, uint32 fontRes, uint8 border = BORDER_PEN);
+
+ void killTextBloc(uint32 bloc_number);
+ void printTextBlocs();
+
+ uint32 buildNewBloc(byte *ascii, int16 x, int16 y, uint16 width, uint8 pen, uint32 type, uint32 fontRes, uint8 justification);
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/memory.cpp b/engines/sword2/memory.cpp
new file mode 100644
index 0000000000..7da4e86b51
--- /dev/null
+++ b/engines/sword2/memory.cpp
@@ -0,0 +1,244 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// The new memory manager, now only used by the resource manager. The original
+// one would allocated a 12 MB memory pool at startup, which may have been
+// appropriate for the original Playstation version but didn't work very well
+// with our PocketPC version.
+//
+// There is one thing that prevents us from replacing the whole memory manager
+// with the standard memory allocation functions: Broken Sword 2 absolutely,
+// positively needs to be able to encode pointers as 32-bit integers. The
+// original engine did this simply by casting between pointers and integers,
+// but as far as I know that's not a very portable thing to do.
+//
+// If it had only used pointers as opcode parameters it would have been
+// possible, albeit messy, to extend the stack data type. However, there is
+// code in walker.cpp that obviously violates that assumption, and there are
+// probably other cases as well.
+//
+// Instead, we take advantage of the fact that the original memory manager
+// could only handle up to 999 blocks of memory. That means we can encode a
+// pointer as a 10-bit id and a 22-bit offset into the block. Judging by early
+// testing, both should be plenty.
+//
+// The number zero is used to represent the NULL pointer.
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/memory.h"
+
+namespace Sword2 {
+
+MemoryManager::MemoryManager(Sword2Engine *vm) : _vm(vm) {
+ // The id stack contains all the possible ids for the memory blocks.
+ // We use this to ensure that no two blocks ever have the same id.
+
+ // The memory blocks are stored in an array, indexed on the block's
+ // id. This means that given a block id we can find the pointer with a
+ // simple array lookup.
+
+ // The memory block index is an array of pointers to the memory block
+ // array, sorted on the memory block's pointer. This means that given
+ // a pointer into a memory block we can find its id with binary
+ // searching.
+ //
+ // A balanced tree might have been more efficient - the index has to
+ // be re-sorted every time a block is allocated or freed - but such
+ // beasts are tricky to implement. Anyway, it wouldn't have made
+ // encoding or decoding pointers any faster, and these are by far the
+ // most common operations.
+
+ _idStack = (int16 *)malloc(MAX_MEMORY_BLOCKS * sizeof(int16));
+ _memBlocks = (MemBlock *)malloc(MAX_MEMORY_BLOCKS * sizeof(MemBlock));
+ _memBlockIndex = (MemBlock **)malloc(MAX_MEMORY_BLOCKS * sizeof(MemBlock *));
+
+ _totAlloc = 0;
+ _numBlocks = 0;
+
+ for (int i = 0; i < MAX_MEMORY_BLOCKS; i++) {
+ _idStack[i] = MAX_MEMORY_BLOCKS - i - 1;
+ _memBlocks[i].ptr = NULL;
+ _memBlockIndex[i] = NULL;
+ }
+
+ _idStackPtr = MAX_MEMORY_BLOCKS;
+}
+
+MemoryManager::~MemoryManager() {
+ for (int i = 0; i < MAX_MEMORY_BLOCKS; i++)
+ free(_memBlocks[i].ptr);
+ free(_memBlocks);
+ free(_memBlockIndex);
+ free(_idStack);
+}
+
+int32 MemoryManager::encodePtr(byte *ptr) {
+ if (ptr == NULL)
+ return 0;
+
+ int idx = findPointerInIndex(ptr);
+
+ assert(idx != -1);
+
+ uint32 id = _memBlockIndex[idx]->id;
+ uint32 offset = ptr - _memBlocks[id].ptr;
+
+ assert(id < 0x03ff);
+ assert(offset <= 0x003fffff);
+ assert(offset < _memBlocks[id].size);
+
+ return ((id + 1) << 22) | (ptr - _memBlocks[id].ptr);
+}
+
+byte *MemoryManager::decodePtr(int32 n) {
+ if (n == 0)
+ return NULL;
+
+ uint32 id = ((n & 0xffc00000) >> 22) - 1;
+ uint32 offset = n & 0x003fffff;
+
+ assert(_memBlocks[id].ptr);
+ assert(offset < _memBlocks[id].size);
+
+ return _memBlocks[id].ptr + offset;
+}
+
+int16 MemoryManager::findExactPointerInIndex(byte *ptr) {
+ int left = 0;
+ int right = _numBlocks - 1;
+
+ while (right >= left) {
+ int n = (left + right) / 2;
+
+ if (_memBlockIndex[n]->ptr == ptr)
+ return n;
+
+ if (_memBlockIndex[n]->ptr > ptr)
+ right = n - 1;
+ else
+ left = n + 1;
+ }
+
+ return -1;
+}
+
+int16 MemoryManager::findPointerInIndex(byte *ptr) {
+ int left = 0;
+ int right = _numBlocks - 1;
+
+ while (right >= left) {
+ int n = (left + right) / 2;
+
+ if (_memBlockIndex[n]->ptr <= ptr && _memBlockIndex[n]->ptr + _memBlockIndex[n]->size > ptr)
+ return n;
+
+ if (_memBlockIndex[n]->ptr > ptr)
+ right = n - 1;
+ else
+ left = n + 1;
+ }
+
+ return -1;
+}
+
+int16 MemoryManager::findInsertionPointInIndex(byte *ptr) {
+ if (_numBlocks == 0)
+ return 0;
+
+ int left = 0;
+ int right = _numBlocks - 1;
+ int n = 0;
+
+ while (right >= left) {
+ n = (left + right) / 2;
+
+ if (_memBlockIndex[n]->ptr == ptr)
+ return -1;
+
+ if (_memBlockIndex[n]->ptr > ptr)
+ right = n - 1;
+ else
+ left = n + 1;
+ }
+
+ if (_memBlockIndex[n]->ptr < ptr)
+ n++;
+
+ return n;
+}
+
+byte *MemoryManager::memAlloc(uint32 size, int16 uid) {
+ assert(_idStackPtr > 0);
+
+ // Get the new block's id from the stack.
+ int16 id = _idStack[--_idStackPtr];
+
+ // Allocate the new memory block
+ byte *ptr = (byte *)malloc(size);
+
+ assert(ptr);
+
+ _memBlocks[id].id = id;
+ _memBlocks[id].uid = uid;
+ _memBlocks[id].ptr = ptr;
+ _memBlocks[id].size = size;
+
+ // Update the memory block index.
+ int16 idx = findInsertionPointInIndex(ptr);
+
+ assert(idx != -1);
+
+ for (int i = _numBlocks; i > idx; i--)
+ _memBlockIndex[i] = _memBlockIndex[i - 1];
+
+ _memBlockIndex[idx] = &_memBlocks[id];
+ _numBlocks++;
+ _totAlloc += size;
+
+ return _memBlocks[id].ptr;
+}
+
+void MemoryManager::memFree(byte *ptr) {
+ int16 idx = findExactPointerInIndex(ptr);
+
+ if (idx == -1) {
+ warning("Freeing non-allocated pointer %p", ptr);
+ return;
+ }
+
+ // Put back the id on the stack
+ _idStack[_idStackPtr++] = _memBlockIndex[idx]->id;
+
+ // Release the memory block
+ free(_memBlockIndex[idx]->ptr);
+ _memBlockIndex[idx]->ptr = NULL;
+
+ _totAlloc -= _memBlockIndex[idx]->size;
+
+ // Remove the memory block from the index
+ _numBlocks--;
+
+ for (int i = idx; i < _numBlocks; i++)
+ _memBlockIndex[i] = _memBlockIndex[i + 1];
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/memory.h b/engines/sword2/memory.h
new file mode 100644
index 0000000000..3154842cd9
--- /dev/null
+++ b/engines/sword2/memory.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef MEMORY_H
+#define MEMORY_H
+
+#define MAX_MEMORY_BLOCKS 999
+
+namespace Sword2 {
+
+struct MemBlock {
+ int16 id;
+ int16 uid;
+ byte *ptr;
+ uint32 size;
+};
+
+class MemoryManager {
+private:
+ Sword2Engine *_vm;
+
+ MemBlock *_memBlocks;
+ MemBlock **_memBlockIndex;
+ int16 _numBlocks;
+
+ uint32 _totAlloc;
+
+ int16 *_idStack;
+ int16 _idStackPtr;
+
+ int16 findExactPointerInIndex(byte *ptr);
+ int16 findPointerInIndex(byte *ptr);
+ int16 findInsertionPointInIndex(byte *ptr);
+
+public:
+ MemoryManager(Sword2Engine *vm);
+ ~MemoryManager();
+
+ int16 getNumBlocks() { return _numBlocks; }
+ uint32 getTotAlloc() { return _totAlloc; }
+ MemBlock *getMemBlocks() { return _memBlocks; }
+
+ int32 encodePtr(byte *ptr);
+ byte *decodePtr(int32 n);
+
+ byte *memAlloc(uint32 size, int16 uid);
+ void memFree(byte *ptr);
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/menu.cpp b/engines/sword2/menu.cpp
new file mode 100644
index 0000000000..07e00accb6
--- /dev/null
+++ b/engines/sword2/menu.cpp
@@ -0,0 +1,291 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/mouse.h"
+
+namespace Sword2 {
+
+#define MENUDEEP 40
+#define MAXMENUANIMS 8
+
+void Mouse::clearIconArea(int menu, int pocket, Common::Rect *r) {
+ byte *buf = _vm->_screen->getScreen();
+ int16 screenWide = _vm->_screen->getScreenWide();
+
+ r->top = menu * (RENDERDEEP + MENUDEEP) + (MENUDEEP - RDMENU_ICONDEEP) / 2;
+ r->bottom = r->top + RDMENU_ICONDEEP;
+ r->left = RDMENU_ICONSTART + pocket * (RDMENU_ICONWIDE + RDMENU_ICONSPACING);
+ r->right = r->left + RDMENU_ICONWIDE;
+
+ byte *dst = buf + r->top * screenWide + r->left;
+
+ for (int i = 0; i < RDMENU_ICONDEEP; i++) {
+ memset(dst, 0, RDMENU_ICONWIDE);
+ dst += screenWide;
+ }
+}
+
+/**
+ * This function should be called regularly to process the menubar system. The
+ * rate at which this function is called will dictate how smooth the menu
+ * system is.
+ */
+
+void Mouse::processMenu() {
+ uint8 menu;
+ uint8 i, j;
+ uint8 frameCount;
+ Common::Rect r1, r2;
+ static int32 lastTime = 0;
+
+ byte *buf = _vm->_screen->getScreen();
+ int16 screenWide = _vm->_screen->getScreenWide();
+
+ if (lastTime == 0) {
+ lastTime = _vm->getMillis();
+ frameCount = 1;
+ } else {
+ int32 delta = _vm->getMillis() - lastTime;
+
+ if (delta > 250) {
+ lastTime += delta;
+ delta = 250;
+ frameCount = 1;
+ } else {
+ frameCount = (uint8) ((_iconCount + 8) * delta / 750);
+ lastTime += frameCount * 750 / (_iconCount + 8);
+ }
+ }
+
+ // Note: The "almost hidden" menu state exists only so that the menu
+ // will be redrawn one last time before it's completely hidden. We do
+ // not need a corresponding "almost shown" state because the menu will
+ // always be redrawn while it's shown anyway. (We may want to change
+ // this later.)
+
+ while (frameCount-- > 0) {
+ for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) {
+ if (_menuStatus[menu] == RDMENU_HIDDEN || _menuStatus[menu] == RDMENU_ALMOST_HIDDEN || _menuStatus[menu] == RDMENU_SHOWN)
+ continue;
+
+ int target, direction, nextState;
+
+ if (_menuStatus[menu] == RDMENU_OPENING) {
+ target = MAXMENUANIMS;
+ direction = 1;
+ nextState = RDMENU_SHOWN;
+ } else {
+ target = 0;
+ direction = -1;
+ nextState = RDMENU_ALMOST_HIDDEN;
+ }
+
+ bool complete = true;
+
+ // Propagate animation from the first icon...
+ for (i = RDMENU_MAXPOCKETS - 1; i > 0; i--) {
+ _pocketStatus[menu][i] = _pocketStatus[menu][i - 1];
+
+ if (_pocketStatus[menu][i] != target)
+ complete = false;
+ }
+
+ if (_pocketStatus[menu][i] != target)
+ complete = false;
+
+ // ...and animate the first icon
+ if (_pocketStatus[menu][0] != target)
+ _pocketStatus[menu][0] += direction;
+
+ if (complete)
+ _menuStatus[menu] = nextState;
+ }
+ }
+
+ for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) {
+ if (_menuStatus[menu] == RDMENU_HIDDEN)
+ continue;
+
+ if (_menuStatus[menu] == RDMENU_ALMOST_HIDDEN)
+ _menuStatus[menu] = RDMENU_HIDDEN;
+
+ // Draw the menu here.
+ int32 curx = RDMENU_ICONSTART + RDMENU_ICONWIDE / 2;
+ int32 cury = (MENUDEEP / 2) + (RENDERDEEP + MENUDEEP) * menu;
+
+ for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
+ if (_icons[menu][i]) {
+ int32 xoff, yoff;
+
+ // Since we no longer clear the screen after
+ // each frame we need to clear the icon area.
+
+ clearIconArea(menu, i, &r1);
+
+ if (_pocketStatus[menu][i] == MAXMENUANIMS) {
+ xoff = (RDMENU_ICONWIDE / 2);
+ r2.left = curx - xoff;
+ r2.right = r2.left + RDMENU_ICONWIDE;
+ yoff = (RDMENU_ICONDEEP / 2);
+ r2.top = cury - yoff;
+ r2.bottom = r2.top + RDMENU_ICONDEEP;
+ } else {
+ xoff = (RDMENU_ICONWIDE / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
+ r2.left = curx - xoff;
+ r2.right = curx + xoff;
+ yoff = (RDMENU_ICONDEEP / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
+ r2.top = cury - yoff;
+ r2.bottom = cury + yoff;
+ }
+
+ if (xoff != 0 && yoff != 0) {
+ byte *dst = buf + r2.top * screenWide + r2.left;
+ byte *src = _icons[menu][i];
+
+ if (_pocketStatus[menu][i] != MAXMENUANIMS) {
+ _vm->_screen->scaleImageFast(
+ dst, screenWide, r2.right - r2.left, r2.bottom - r2.top,
+ src, RDMENU_ICONWIDE, RDMENU_ICONWIDE, RDMENU_ICONDEEP);
+ } else {
+ for (j = 0; j < RDMENU_ICONDEEP; j++) {
+ memcpy(dst, src, RDMENU_ICONWIDE);
+ src += RDMENU_ICONWIDE;
+ dst += screenWide;
+ }
+ }
+ }
+ _vm->_screen->updateRect(&r1);
+ }
+ curx += (RDMENU_ICONSPACING + RDMENU_ICONWIDE);
+ }
+ }
+}
+
+/**
+ * This function brings a specified menu into view.
+ * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to show
+ * @return RD_OK, or an error code
+ */
+
+int32 Mouse::showMenu(uint8 menu) {
+ // Check for invalid menu parameter
+ if (menu > RDMENU_BOTTOM)
+ return RDERR_INVALIDMENU;
+
+ // Check that the menu is not currently shown, or in the process of
+ // being shown.
+ if (_menuStatus[menu] == RDMENU_SHOWN || _menuStatus[menu] == RDMENU_OPENING)
+ return RDERR_INVALIDCOMMAND;
+
+ _menuStatus[menu] = RDMENU_OPENING;
+ return RD_OK;
+}
+
+/**
+ * This function hides a specified menu.
+ * @param menu RDMENU_TOP or RDMENU_BOTTOM depending on which menu to hide
+ * @return RD_OK, or an error code
+ */
+
+int32 Mouse::hideMenu(uint8 menu) {
+ // Check for invalid menu parameter
+ if (menu > RDMENU_BOTTOM)
+ return RDERR_INVALIDMENU;
+
+ // Check that the menu is not currently hidden, or in the process of
+ // being hidden.
+ if (_menuStatus[menu] == RDMENU_HIDDEN || _menuStatus[menu] == RDMENU_CLOSING)
+ return RDERR_INVALIDCOMMAND;
+
+ _menuStatus[menu] = RDMENU_CLOSING;
+ return RD_OK;
+}
+
+/**
+ * This function hides both menus immediately.
+ */
+
+void Mouse::closeMenuImmediately() {
+ Common::Rect r;
+ int i;
+
+ _menuStatus[RDMENU_TOP] = RDMENU_HIDDEN;
+ _menuStatus[RDMENU_BOTTOM] = RDMENU_HIDDEN;
+
+ for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
+ if (_icons[RDMENU_TOP][i]) {
+ clearIconArea(RDMENU_TOP, i, &r);
+ _vm->_screen->updateRect(&r);
+ }
+ if (_icons[RDMENU_BOTTOM][i]) {
+ clearIconArea(RDMENU_BOTTOM, i, &r);
+ _vm->_screen->updateRect(&r);
+ }
+ }
+
+ memset(_pocketStatus, 0, sizeof(uint8) * 2 * RDMENU_MAXPOCKETS);
+}
+
+/**
+ * This function sets a menubar icon.
+ * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to change
+ * @param pocket the menu pocket to change
+ * @param icon icon data, or NULL to clear the icon
+ * @return RD_OK, or an error code
+ */
+
+int32 Mouse::setMenuIcon(uint8 menu, uint8 pocket, byte *icon) {
+ Common::Rect r;
+
+ // Check for invalid menu parameter.
+ if (menu > RDMENU_BOTTOM)
+ return RDERR_INVALIDMENU;
+
+ // Check for invalid pocket parameter
+ if (pocket >= RDMENU_MAXPOCKETS)
+ return RDERR_INVALIDPOCKET;
+
+ // If there is an icon in the requested menu/pocket, clear it out.
+ if (_icons[menu][pocket]) {
+ _iconCount--;
+ free(_icons[menu][pocket]);
+ _icons[menu][pocket] = NULL;
+ clearIconArea(menu, pocket, &r);
+ _vm->_screen->updateRect(&r);
+ }
+
+ // Only put the icon in the pocket if it is not NULL
+ if (icon != NULL) {
+ _iconCount++;
+ _icons[menu][pocket] = (byte *)malloc(RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+ if (_icons[menu][pocket] == NULL)
+ return RDERR_OUTOFMEMORY;
+ memcpy(_icons[menu][pocket], icon, RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+ }
+
+ return RD_OK;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/module.mk b/engines/sword2/module.mk
new file mode 100644
index 0000000000..2ee3f39ebb
--- /dev/null
+++ b/engines/sword2/module.mk
@@ -0,0 +1,48 @@
+MODULE := engines/sword2
+
+MODULE_OBJS := \
+ engines/sword2/_mouse.o \
+ engines/sword2/animation.o \
+ engines/sword2/anims.o \
+ engines/sword2/build_display.o \
+ engines/sword2/console.o \
+ engines/sword2/controls.o \
+ engines/sword2/d_draw.o \
+ engines/sword2/debug.o \
+ engines/sword2/events.o \
+ engines/sword2/function.o \
+ engines/sword2/icons.o \
+ engines/sword2/interpreter.o \
+ engines/sword2/layers.o \
+ engines/sword2/logic.o \
+ engines/sword2/maketext.o \
+ engines/sword2/memory.o \
+ engines/sword2/menu.o \
+ engines/sword2/mouse.o \
+ engines/sword2/music.o \
+ engines/sword2/palette.o \
+ engines/sword2/protocol.o \
+ engines/sword2/rdwin.o \
+ engines/sword2/render.o \
+ engines/sword2/resman.o \
+ engines/sword2/router.o \
+ engines/sword2/save_rest.o \
+ engines/sword2/scroll.o \
+ engines/sword2/sound.o \
+ engines/sword2/speech.o \
+ engines/sword2/sprite.o \
+ engines/sword2/startup.o \
+ engines/sword2/sword2.o \
+ engines/sword2/sync.o \
+ engines/sword2/walker.o
+
+MODULE_DIRS += \
+ engines/sword2
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
diff --git a/engines/sword2/mouse.cpp b/engines/sword2/mouse.cpp
new file mode 100644
index 0000000000..f8c315a47f
--- /dev/null
+++ b/engines/sword2/mouse.cpp
@@ -0,0 +1,1437 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/console.h"
+#include "sword2/controls.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+
+namespace Sword2 {
+
+// Pointer resource id's
+
+enum {
+ CROSHAIR = 18,
+ EXIT0 = 788,
+ EXIT1 = 789,
+ EXIT2 = 790,
+ EXIT3 = 791,
+ EXIT4 = 792,
+ EXIT5 = 793,
+ EXIT6 = 794,
+ EXIT7 = 795,
+ EXITDOWN = 796,
+ EXITUP = 797,
+ MOUTH = 787,
+ NORMAL = 17,
+ PICKUP = 3099,
+ SCROLL_L = 1440,
+ SCROLL_R = 1441,
+ USE = 3100
+};
+
+Mouse::Mouse(Sword2Engine *vm) {
+ _vm = vm;
+
+ setPos(0, 0);
+ resetMouseList();
+
+ _mouseTouching = 0;
+ _oldMouseTouching = 0;
+ _menuSelectedPos = 0;
+ _examiningMenuIcon = false;
+ _mousePointerRes = 0;
+ _mouseMode = 0;
+ _mouseStatus = false;
+ _mouseModeLocked = false;
+ _currentLuggageResource = 0;
+ _oldButton = 0;
+ _buttonClick = 0;
+ _pointerTextBlocNo = 0;
+ _playerActivityDelay = 0;
+ _realLuggageItem = 0;
+
+ _mouseAnim.data = NULL;
+ _luggageAnim.data = NULL;
+
+ // For the menus
+ _totalTemp = 0;
+ memset(_tempList, 0, sizeof(_tempList));
+
+ _totalMasters = 0;
+ memset(_masterMenuList, 0, sizeof(_masterMenuList));
+ memset(_mouseList, 0, sizeof(_mouseList));
+ memset(_subjectList, 0, sizeof(_subjectList));
+
+ _defaultResponseId = 0;
+ _choosing = false;
+
+ _iconCount = 0;
+
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < RDMENU_MAXPOCKETS; j++) {
+ _icons[i][j] = NULL;
+ _pocketStatus[i][j] = 0;
+ }
+
+ _menuStatus[i] = RDMENU_HIDDEN;
+ }
+}
+
+Mouse::~Mouse() {
+ free(_mouseAnim.data);
+ free(_luggageAnim.data);
+ for (int i = 0; i < 2; i++)
+ for (int j = 0; j < RDMENU_MAXPOCKETS; j++)
+ free(_icons[i][j]);
+}
+
+void Mouse::getPos(int &x, int &y) {
+ x = _pos.x;
+ y = _pos.y;
+}
+
+void Mouse::setPos(int x, int y) {
+ _pos.x = x;
+ _pos.y = y;
+}
+
+/**
+ * Call at beginning of game loop
+ */
+
+void Mouse::resetMouseList() {
+ _curMouse = 0;
+}
+
+void Mouse::registerMouse(byte *ob_mouse, BuildUnit *build_unit) {
+ assert(_curMouse < TOTAL_mouse_list);
+
+ ObjectMouse mouse;
+
+ mouse.read(ob_mouse);
+
+ if (!mouse.pointer)
+ return;
+
+ if (build_unit) {
+ _mouseList[_curMouse].rect.left = build_unit->x;
+ _mouseList[_curMouse].rect.top = build_unit->y;
+ _mouseList[_curMouse].rect.right = 1 + build_unit->x + build_unit->scaled_width;
+ _mouseList[_curMouse].rect.bottom = 1 + build_unit->y + build_unit->scaled_height;
+ } else {
+ _mouseList[_curMouse].rect.left = mouse.x1;
+ _mouseList[_curMouse].rect.top = mouse.y1;
+ _mouseList[_curMouse].rect.right = 1 + mouse.x2;
+ _mouseList[_curMouse].rect.bottom = 1 + mouse.y2;
+ }
+
+ _mouseList[_curMouse].priority = mouse.priority;
+ _mouseList[_curMouse].pointer = mouse.pointer;
+
+ // Change all COGS pointers to CROSHAIR. I'm guessing that this was a
+ // design decision made in mid-development and they didn't want to go
+ // back and re-generate the resource files.
+
+ if (_mouseList[_curMouse].pointer == USE)
+ _mouseList[_curMouse].pointer = CROSHAIR;
+
+ // Check if pointer text field is set due to previous object using this
+ // slot (ie. not correct for this one)
+
+ // If 'pointer_text' field is set, but the 'id' field isn't same is
+ // current id then we don't want this "left over" pointer text
+
+ if (_mouseList[_curMouse].pointer_text && _mouseList[_curMouse].id != (int32)_vm->_logic->readVar(ID))
+ _mouseList[_curMouse].pointer_text = 0;
+
+ // Get id from system variable 'id' which is correct for current object
+ _mouseList[_curMouse].id = _vm->_logic->readVar(ID);
+
+ _curMouse++;
+}
+
+void Mouse::registerPointerText(int32 text_id) {
+ assert(_curMouse < TOTAL_mouse_list);
+
+ // current object id - used for checking pointer_text when mouse area
+ // registered (in fnRegisterMouse and fnRegisterFrame)
+
+ _mouseList[_curMouse].id = _vm->_logic->readVar(ID);
+ _mouseList[_curMouse].pointer_text = text_id;
+}
+
+/**
+ * This function is called every game cycle.
+ */
+
+void Mouse::mouseEngine() {
+ monitorPlayerActivity();
+ clearPointerText();
+
+ // If George is dead, the system menu is visible all the time, and is
+ // the only thing that can be used.
+
+ if (_vm->_logic->readVar(DEAD)) {
+ if (_mouseMode != MOUSE_system_menu) {
+ _mouseMode = MOUSE_system_menu;
+
+ if (_mouseTouching) {
+ _oldMouseTouching = 0;
+ _mouseTouching = 0;
+ }
+
+ setMouse(NORMAL_MOUSE_ID);
+ buildSystemMenu();
+ }
+ systemMenuMouse();
+ return;
+ }
+
+ // If the mouse is not visible, do nothing
+
+ if (_mouseStatus)
+ return;
+
+ switch (_mouseMode) {
+ case MOUSE_normal:
+ normalMouse();
+ break;
+ case MOUSE_menu:
+ menuMouse();
+ break;
+ case MOUSE_drag:
+ dragMouse();
+ break;
+ case MOUSE_system_menu:
+ systemMenuMouse();
+ break;
+ case MOUSE_holding:
+ if (_pos.y < 400) {
+ _mouseMode = MOUSE_normal;
+ debug(5, " releasing");
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+#if RIGHT_CLICK_CLEARS_LUGGAGE
+bool Mouse::heldIsInInventory() {
+ int32 object_held = (int32)_vm->_logic->readVar(OBJECT_HELD);
+
+ for (uint i = 0; i < _totalMasters; i++) {
+ if (_masterMenuList[i].icon_resource == object_held)
+ return true;
+ }
+ return false;
+}
+#endif
+
+int Mouse::menuClick(int menu_items) {
+ if (_pos.x < RDMENU_ICONSTART)
+ return -1;
+
+ if (_pos.x > RDMENU_ICONSTART + menu_items * (RDMENU_ICONWIDE + RDMENU_ICONSPACING) - RDMENU_ICONSPACING)
+ return -1;
+
+ return (_pos.x - RDMENU_ICONSTART) / (RDMENU_ICONWIDE + RDMENU_ICONSPACING);
+}
+
+void Mouse::systemMenuMouse() {
+ uint32 safe_looping_music_id;
+ MouseEvent *me;
+ int hit;
+ byte *icon;
+ int32 pars[2];
+ uint32 icon_list[5] = {
+ OPTIONS_ICON,
+ QUIT_ICON,
+ SAVE_ICON,
+ RESTORE_ICON,
+ RESTART_ICON
+ };
+
+ // If the mouse is moved off the menu, close it. Unless the player is
+ // dead, in which case the menu should always be visible.
+
+ if (_pos.y > 0 && !_vm->_logic->readVar(DEAD)) {
+ _mouseMode = MOUSE_normal;
+ hideMenu(RDMENU_TOP);
+ return;
+ }
+
+ // Check if the user left-clicks anywhere in the menu area.
+
+ me = _vm->mouseEvent();
+
+ if (!me || !(me->buttons & RD_LEFTBUTTONDOWN))
+ return;
+
+ if (_pos.y > 0)
+ return;
+
+ hit = menuClick(ARRAYSIZE(icon_list));
+
+ if (hit < 0)
+ return;
+
+ // No save when dead
+
+ if (icon_list[hit] == SAVE_ICON && _vm->_logic->readVar(DEAD))
+ return;
+
+ // Gray out all he icons, except the one that was clicked
+
+ for (int i = 0; i < ARRAYSIZE(icon_list); i++) {
+ if (i != hit) {
+ icon = _vm->_resman->openResource(icon_list[i]) + ResHeader::size();
+ setMenuIcon(RDMENU_TOP, i, icon);
+ _vm->_resman->closeResource(icon_list[i]);
+ }
+ }
+
+ _vm->_sound->pauseFx();
+
+ // NB. Need to keep a safe copy of '_loopingMusicId' for savegame & for
+ // playing when returning from control panels because control panel
+ // music will overwrite it!
+
+ safe_looping_music_id = _vm->_sound->getLoopingMusicId();
+
+ pars[0] = 221;
+ pars[1] = FX_LOOP;
+ _vm->_logic->fnPlayMusic(pars);
+
+ // HACK: Restore proper looping_music_id
+ _vm->_sound->setLoopingMusicId(safe_looping_music_id);
+
+ processMenu();
+
+ // call the relevant screen
+
+ switch (hit) {
+ case 0:
+ {
+ OptionsDialog dialog(_vm);
+ dialog.runModal();
+ }
+ break;
+ case 1:
+ {
+ QuitDialog dialog(_vm);
+ dialog.runModal();
+ }
+ break;
+ case 2:
+ {
+ SaveDialog dialog(_vm);
+ dialog.runModal();
+ }
+ break;
+ case 3:
+ {
+ RestoreDialog dialog(_vm);
+ dialog.runModal();
+ }
+ break;
+ case 4:
+ {
+ RestartDialog dialog(_vm);
+ dialog.runModal();
+ }
+ break;
+ }
+
+ // Menu stays open on death screen. Otherwise it's closed.
+
+ if (!_vm->_logic->readVar(DEAD)) {
+ _mouseMode = MOUSE_normal;
+ hideMenu(RDMENU_TOP);
+ } else {
+ setMouse(NORMAL_MOUSE_ID);
+ buildSystemMenu();
+ }
+
+ // Back to the game again
+
+ processMenu();
+
+ // Reset game palette, but not after a successful restore or restart!
+ // See RestoreFromBuffer() in save_rest.cpp
+
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ if (screenInfo->new_palette != 99) {
+ // 0 means put back game screen palette; see build_display.cpp
+ _vm->_screen->setFullPalette(0);
+
+ // Stop the engine fading in the restored screens palette
+ screenInfo->new_palette = 0;
+ } else
+ screenInfo->new_palette = 1;
+
+ _vm->_sound->unpauseFx();
+
+ // If there was looping music before coming into the control panels
+ // then restart it! NB. If a game has been restored the music will be
+ // restarted twice, but this shouldn't cause any harm.
+
+ if (_vm->_sound->getLoopingMusicId()) {
+ pars[0] = _vm->_sound->getLoopingMusicId();
+ pars[1] = FX_LOOP;
+ _vm->_logic->fnPlayMusic(pars);
+ } else
+ _vm->_logic->fnStopMusic(NULL);
+}
+
+void Mouse::dragMouse() {
+ byte buf1[NAME_LEN], buf2[NAME_LEN];
+ MouseEvent *me;
+ int hit;
+
+ // We can use dragged object both on other inventory objects, or on
+ // objects in the scene, so if the mouse moves off the inventory menu,
+ // then close it.
+
+ if (_pos.y < 400) {
+ _mouseMode = MOUSE_normal;
+ hideMenu(RDMENU_BOTTOM);
+ return;
+ }
+
+ // Handles cursors and the luggage on/off according to type
+
+ mouseOnOff();
+
+ // Now do the normal click stuff
+
+ me = _vm->mouseEvent();
+
+ if (!me)
+ return;
+
+#if RIGHT_CLICK_CLEARS_LUGGAGE
+ if ((me->buttons & RD_RIGHTBUTTONDOWN) && heldIsInInventory()) {
+ _vm->_logic->writeVar(OBJECT_HELD, 0);
+ _menuSelectedPos = 0;
+ _mouseMode = MOUSE_menu;
+ setLuggage(0);
+ buildMenu();
+ return;
+ }
+#endif
+
+ if (!(me->buttons & RD_LEFTBUTTONDOWN))
+ return;
+
+ // there's a mouse event to be processed
+
+ // could be clicking on an on screen object or on the menu
+ // which is currently displayed
+
+ if (_mouseTouching) {
+ // mouse is over an on screen object - and we have luggage
+
+ // Depending on type we'll maybe kill the object_held - like
+ // for exits
+
+ // Set global script variable 'button'. We know that it was the
+ // left button, not the right one.
+
+ _vm->_logic->writeVar(LEFT_BUTTON, 1);
+ _vm->_logic->writeVar(RIGHT_BUTTON, 0);
+
+ // These might be required by the action script about to be run
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ _vm->_logic->writeVar(MOUSE_X, _pos.x + screenInfo->scroll_offset_x);
+ _vm->_logic->writeVar(MOUSE_Y, _pos.y + screenInfo->scroll_offset_y);
+
+ // For scripts to know what's been clicked. First used for
+ // 'room_13_turning_script' in object 'biscuits_13'
+
+ _vm->_logic->writeVar(CLICKED_ID, _mouseTouching);
+
+ _vm->_logic->setPlayerActionEvent(CUR_PLAYER_ID, _mouseTouching);
+
+ debug(2, "Used \"%s\" on \"%s\"",
+ _vm->_resman->fetchName(_vm->_logic->readVar(OBJECT_HELD), buf1),
+ _vm->_resman->fetchName(_vm->_logic->readVar(CLICKED_ID), buf2));
+
+ // Hide menu - back to normal menu mode
+
+ hideMenu(RDMENU_BOTTOM);
+ _mouseMode = MOUSE_normal;
+
+ return;
+ }
+
+ // Better check for combine/cancel. Cancel puts us back in MOUSE_menu
+ // mode
+
+ hit = menuClick(TOTAL_engine_pockets);
+
+ if (hit < 0 || !_masterMenuList[hit].icon_resource)
+ return;
+
+ // Always back into menu mode. Remove the luggage as well.
+
+ _mouseMode = MOUSE_menu;
+ setLuggage(0);
+
+ if ((uint)hit == _menuSelectedPos) {
+ // If we clicked on the same icon again, reset the first icon
+
+ _vm->_logic->writeVar(OBJECT_HELD, 0);
+ _menuSelectedPos = 0;
+ } else {
+ // Otherwise, combine the two icons
+
+ _vm->_logic->writeVar(COMBINE_BASE, _masterMenuList[hit].icon_resource);
+ _vm->_logic->setPlayerActionEvent(CUR_PLAYER_ID, MENU_MASTER_OBJECT);
+
+ // Turn off mouse now, to prevent player trying to click
+ // elsewhere BUT leave the bottom menu open
+
+ hideMouse();
+
+ debug(2, "Used \"%s\" on \"%s\"",
+ _vm->_resman->fetchName(_vm->_logic->readVar(OBJECT_HELD), buf1),
+ _vm->_resman->fetchName(_vm->_logic->readVar(COMBINE_BASE), buf2));
+ }
+
+ // Refresh the menu
+
+ buildMenu();
+}
+
+void Mouse::menuMouse() {
+ byte buf[NAME_LEN];
+ MouseEvent *me;
+ int hit;
+
+ // If the mouse is moved off the menu, close it.
+
+ if (_pos.y < 400) {
+ _mouseMode = MOUSE_normal;
+ hideMenu(RDMENU_BOTTOM);
+ return;
+ }
+
+ me = _vm->mouseEvent();
+
+ if (!me)
+ return;
+
+ hit = menuClick(TOTAL_engine_pockets);
+
+ // Check if we clicked on an actual icon.
+
+ if (hit < 0 || !_masterMenuList[hit].icon_resource)
+ return;
+
+ if (me->buttons & RD_RIGHTBUTTONDOWN) {
+ // Right button - examine an object, identified by its icon
+ // resource id.
+
+ _examiningMenuIcon = true;
+ _vm->_logic->writeVar(OBJECT_HELD, _masterMenuList[hit].icon_resource);
+
+ // Must clear this so next click on exit becomes 1st click
+ // again
+
+ _vm->_logic->writeVar(EXIT_CLICK_ID, 0);
+
+ _vm->_logic->setPlayerActionEvent(CUR_PLAYER_ID, MENU_MASTER_OBJECT);
+
+ // Refresh the menu
+
+ buildMenu();
+
+ // Turn off mouse now, to prevent player trying to click
+ // elsewhere BUT leave the bottom menu open
+
+ hideMouse();
+
+ debug(2, "Right-click on \"%s\" icon",
+ _vm->_resman->fetchName(_vm->_logic->readVar(OBJECT_HELD), buf));
+
+ return;
+ }
+
+ if (me->buttons & RD_LEFTBUTTONDOWN) {
+ // Left button - bung us into drag luggage mode. The object is
+ // identified by its icon resource id. We need the luggage
+ // resource id for mouseOnOff
+
+ _mouseMode = MOUSE_drag;
+
+ _menuSelectedPos = hit;
+ _vm->_logic->writeVar(OBJECT_HELD, _masterMenuList[hit].icon_resource);
+ _currentLuggageResource = _masterMenuList[hit].luggage_resource;
+
+ // Must clear this so next click on exit becomes 1st click
+ // again
+
+ _vm->_logic->writeVar(EXIT_CLICK_ID, 0);
+
+ // Refresh the menu
+
+ buildMenu();
+
+ setLuggage(_masterMenuList[hit].luggage_resource);
+
+ debug(2, "Left-clicked on \"%s\" icon - switch to drag mode",
+ _vm->_resman->fetchName(_vm->_logic->readVar(OBJECT_HELD), buf));
+ }
+}
+
+void Mouse::normalMouse() {
+ // The gane is playing and none of the menus are activated - but, we
+ // need to check if a menu is to start. Note, won't have luggage
+
+ MouseEvent *me;
+
+ // Check if the cursor has moved onto the system menu area. No save in
+ // big-object menu lock situation, of if the player is dragging an
+ // object.
+
+ if (_pos.y < 0 && !_mouseModeLocked && !_vm->_logic->readVar(OBJECT_HELD)) {
+ _mouseMode = MOUSE_system_menu;
+
+ if (_mouseTouching) {
+ // We were on something, but not anymore
+ _oldMouseTouching = 0;
+ _mouseTouching = 0;
+ }
+
+ // Reset mouse cursor - in case we're between mice
+
+ setMouse(NORMAL_MOUSE_ID);
+ buildSystemMenu();
+ return;
+ }
+
+ // Check if the cursor has moved onto the inventory menu area. No
+ // inventory in big-object menu lock situation,
+
+ if (_pos.y > 399 && !_mouseModeLocked) {
+ // If an object is being held, i.e. if the mouse cursor has a
+ // luggage, go to drag mode instead of menu mode, but the menu
+ // is still opened.
+ //
+ // That way, we can still use an object on another inventory
+ // object, even if the inventory menu was closed after the
+ // first object was selected.
+
+ if (!_vm->_logic->readVar(OBJECT_HELD))
+ _mouseMode = MOUSE_menu;
+ else
+ _mouseMode = MOUSE_drag;
+
+ // If mouse is moving off an object and onto the menu then do a
+ // standard get-off
+
+ if (_mouseTouching) {
+ _oldMouseTouching = 0;
+ _mouseTouching = 0;
+ }
+
+ // Reset mouse cursor
+
+ setMouse(NORMAL_MOUSE_ID);
+ buildMenu();
+ return;
+ }
+
+ // Check for moving the mouse on or off things
+
+ mouseOnOff();
+
+ me = _vm->mouseEvent();
+
+ if (!me)
+ return;
+
+ bool button_down = (me->buttons & (RD_LEFTBUTTONDOWN | RD_RIGHTBUTTONDOWN)) != 0;
+
+ // For debugging. We can draw a rectangle on the screen and see its
+ // coordinates. This was probably used to help defining hit areas.
+
+ if (_vm->_debugger->_definingRectangles) {
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ if (_vm->_debugger->_draggingRectangle == 0) {
+ // Not yet dragging a rectangle, so need click to start
+
+ if (button_down) {
+ // set both (x1,y1) and (x2,y2) to this point
+ _vm->_debugger->_rectX1 = _vm->_debugger->_rectX2 = (uint32)_pos.x + screenInfo->scroll_offset_x;
+ _vm->_debugger->_rectY1 = _vm->_debugger->_rectY2 = (uint32)_pos.y + screenInfo->scroll_offset_y;
+ _vm->_debugger->_draggingRectangle = 1;
+ }
+ } else if (_vm->_debugger->_draggingRectangle == 1) {
+ // currently dragging a rectangle - click means reset
+
+ if (button_down) {
+ // lock rectangle, so you can let go of mouse
+ // to type in the coords
+ _vm->_debugger->_draggingRectangle = 2;
+ } else {
+ // drag rectangle
+ _vm->_debugger->_rectX2 = (uint32)_pos.x + screenInfo->scroll_offset_x;
+ _vm->_debugger->_rectY2 = (uint32)_pos.y + screenInfo->scroll_offset_y;
+ }
+ } else {
+ // currently locked to avoid knocking out of place
+ // while reading off the coords
+
+ if (button_down) {
+ // click means reset - back to start again
+ _vm->_debugger->_draggingRectangle = 0;
+ }
+ }
+
+ return;
+ }
+
+#if RIGHT_CLICK_CLEARS_LUGGAGE
+ if (_vm->_logic->readVar(OBJECT_HELD) && (me->buttons & RD_RIGHTBUTTONDOWN) && heldIsInInventory()) {
+ _vm->_logic->writeVar(OBJECT_HELD, 0);
+ _menuSelectedPos = 0;
+ setLuggage(0);
+ return;
+ }
+#endif
+
+ // Now do the normal click stuff
+
+ // We only care about down clicks when the mouse is over an object.
+
+ if (!_mouseTouching || !button_down)
+ return;
+
+ // There's a mouse event to be processed and the mouse is on something.
+ // Notice that the floor itself is considered an object.
+
+ // There are no menus about so its nice and simple. This is as close to
+ // the old advisor_188 script as we get, I'm sorry to say.
+
+ // If player is walking or relaxing then those need to terminate
+ // correctly. Otherwise set player run the targets action script or, do
+ // a special walk if clicking on the scroll-more icon
+
+ // PLAYER_ACTION script variable - whatever catches this must reset to
+ // 0 again
+ // _vm->_logic->writeVar(PLAYER_ACTION, _mouseTouching);
+
+ // Idle or router-anim will catch it
+
+ // Set global script variable 'button'
+
+ if (me->buttons & RD_LEFTBUTTONDOWN) {
+ _vm->_logic->writeVar(LEFT_BUTTON, 1);
+ _vm->_logic->writeVar(RIGHT_BUTTON, 0);
+ _buttonClick = 0; // for re-click
+ } else {
+ _vm->_logic->writeVar(LEFT_BUTTON, 0);
+ _vm->_logic->writeVar(RIGHT_BUTTON, 1);
+ _buttonClick = 1; // for re-click
+ }
+
+ // These might be required by the action script about to be run
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ _vm->_logic->writeVar(MOUSE_X, _pos.x + screenInfo->scroll_offset_x);
+ _vm->_logic->writeVar(MOUSE_Y, _pos.y + screenInfo->scroll_offset_y);
+
+ if (_mouseTouching == _vm->_logic->readVar(EXIT_CLICK_ID) && (me->buttons & RD_LEFTBUTTONDOWN)) {
+ // It's the exit double click situation. Let the existing
+ // interaction continue and start fading down. Switch the human
+ // off too
+
+ noHuman();
+ _vm->_logic->fnFadeDown(NULL);
+
+ // Tell the walker
+
+ _vm->_logic->writeVar(EXIT_FADING, 1);
+ } else if (_oldButton == _buttonClick && _mouseTouching == _vm->_logic->readVar(CLICKED_ID) && _mousePointerRes != NORMAL_MOUSE_ID) {
+ // Re-click. Do nothing, except on floors
+ } else {
+ // For re-click
+
+ _oldButton = _buttonClick;
+
+ // For scripts to know what's been clicked. First used for
+ // 'room_13_turning_script' in object 'biscuits_13'
+
+ _vm->_logic->writeVar(CLICKED_ID, _mouseTouching);
+
+ // Must clear these two double-click control flags - do it here
+ // so reclicks after exit clicks are cleared up
+
+ _vm->_logic->writeVar(EXIT_CLICK_ID, 0);
+ _vm->_logic->writeVar(EXIT_FADING, 0);
+
+ _vm->_logic->setPlayerActionEvent(CUR_PLAYER_ID, _mouseTouching);
+
+ byte buf1[NAME_LEN], buf2[NAME_LEN];
+
+ if (_vm->_logic->readVar(OBJECT_HELD))
+ debug(2, "Used \"%s\" on \"%s\"",
+ _vm->_resman->fetchName(_vm->_logic->readVar(OBJECT_HELD), buf1),
+ _vm->_resman->fetchName(_vm->_logic->readVar(CLICKED_ID), buf2));
+ else if (_vm->_logic->readVar(LEFT_BUTTON))
+ debug(2, "Left-clicked on \"%s\"",
+ _vm->_resman->fetchName(_vm->_logic->readVar(CLICKED_ID), buf1));
+ else // RIGHT BUTTON
+ debug(2, "Right-clicked on \"%s\"",
+ _vm->_resman->fetchName(_vm->_logic->readVar(CLICKED_ID), buf1));
+ }
+}
+
+uint32 Mouse::chooseMouse() {
+ // Unlike the other mouse "engines", this one is called directly by the
+ // fnChoose() opcode.
+
+ uint i;
+
+ _vm->_logic->writeVar(AUTO_SELECTED, 0);
+
+ uint32 in_subject = _vm->_logic->readVar(IN_SUBJECT);
+ uint32 object_held = _vm->_logic->readVar(OBJECT_HELD);
+
+ if (object_held) {
+ // The player used an object on a person. In this case it
+ // triggered a conversation menu. Act as if the user tried to
+ // talk to the person about that object. If the person doesn't
+ // know anything about it, use the default response.
+
+ uint32 response = _defaultResponseId;
+
+ for (i = 0; i < in_subject; i++) {
+ if (_subjectList[i].res == object_held) {
+ response = _subjectList[i].ref;
+ break;
+ }
+ }
+
+ // The user won't be holding the object any more, and the
+ // conversation menu will be closed.
+
+ _vm->_logic->writeVar(OBJECT_HELD, 0);
+ _vm->_logic->writeVar(IN_SUBJECT, 0);
+ return response;
+ }
+
+ if (_vm->_logic->readVar(CHOOSER_COUNT_FLAG) == 0 && in_subject == 1 && _subjectList[0].res == EXIT_ICON) {
+ // This is the first time the chooser is coming up in this
+ // conversation, there is only one subject and that's the
+ // EXIT icon.
+ //
+ // In other words, the player doesn't have anything to talk
+ // about. Skip it.
+
+ // The conversation menu will be closed. We set AUTO_SELECTED
+ // because the speech script depends on it.
+
+ _vm->_logic->writeVar(AUTO_SELECTED, 1);
+ _vm->_logic->writeVar(IN_SUBJECT, 0);
+ return _subjectList[0].ref;
+ }
+
+ byte *icon;
+
+ if (!_choosing) {
+ // This is a new conversation menu.
+
+ if (!in_subject)
+ error("fnChoose with no subjects");
+
+ for (i = 0; i < in_subject; i++) {
+ icon = _vm->_resman->openResource(_subjectList[i].res) + ResHeader::size() + RDMENU_ICONWIDE * RDMENU_ICONDEEP;
+ setMenuIcon(RDMENU_BOTTOM, i, icon);
+ _vm->_resman->closeResource(_subjectList[i].res);
+ }
+
+ for (; i < 15; i++)
+ setMenuIcon(RDMENU_BOTTOM, (uint8) i, NULL);
+
+ showMenu(RDMENU_BOTTOM);
+ setMouse(NORMAL_MOUSE_ID);
+ _choosing = true;
+ return (uint32)-1;
+ }
+
+ // The menu is there - we're just waiting for a click. We only care
+ // about left clicks.
+
+ MouseEvent *me = _vm->mouseEvent();
+ int mouseX, mouseY;
+
+ getPos(mouseX, mouseY);
+
+ if (!me || !(me->buttons & RD_LEFTBUTTONDOWN) || mouseY < 400)
+ return (uint32)-1;
+
+ // Check for click on a menu.
+
+ int hit = _vm->_mouse->menuClick(in_subject);
+ if (hit < 0)
+ return (uint32)-1;
+
+ // Hilight the clicked icon by greying the others. This can look a bit
+ // odd when you click on the exit icon, but there are also cases when
+ // it looks strange if you don't do it.
+
+ for (i = 0; i < in_subject; i++) {
+ if ((int)i != hit) {
+ icon = _vm->_resman->openResource(_subjectList[i].res) + ResHeader::size();
+ _vm->_mouse->setMenuIcon(RDMENU_BOTTOM, i, icon);
+ _vm->_resman->closeResource(_subjectList[i].res);
+ }
+ }
+
+ // For non-speech scripts that manually call the chooser
+ _vm->_logic->writeVar(RESULT, _subjectList[hit].res);
+
+ // The conversation menu will be closed
+
+ _choosing = false;
+ _vm->_logic->writeVar(IN_SUBJECT, 0);
+ setMouse(0);
+
+ return _subjectList[hit].ref;
+}
+
+void Mouse::mouseOnOff() {
+ // this handles the cursor graphic when moving on and off mouse areas
+ // it also handles the luggage thingy
+
+ uint32 pointer_type;
+ static uint8 mouse_flicked_off = 0;
+
+ _oldMouseTouching = _mouseTouching;
+
+ // don't detect objects that are hidden behind the menu bars (ie. in
+ // the scrolled-off areas of the screen)
+
+ if (_pos.y < 0 || _pos.y > 399) {
+ pointer_type = 0;
+ _mouseTouching = 0;
+ } else {
+ // set '_mouseTouching' & return pointer_type
+ pointer_type = checkMouseList();
+ }
+
+ // same as previous cycle?
+ if (!mouse_flicked_off && _oldMouseTouching == _mouseTouching) {
+ // yes, so nothing to do
+ // BUT CARRY ON IF MOUSE WAS FLICKED OFF!
+ return;
+ }
+
+ // can reset this now
+ mouse_flicked_off = 0;
+
+ //the cursor has moved onto something
+ if (!_oldMouseTouching && _mouseTouching) {
+ // make a copy of the object we've moved onto because one day
+ // we'll move back off again! (but the list positioning could
+ // theoretically have changed)
+
+ // we can only move onto something from being on nothing - we
+ // stop the system going from one to another when objects
+ // overlap
+
+ _oldMouseTouching = _mouseTouching;
+
+ // run get on
+
+ if (pointer_type) {
+ // 'pointer_type' holds the resource id of the
+ // pointer anim
+
+ setMouse(pointer_type);
+
+ // setup luggage icon
+ if (_vm->_logic->readVar(OBJECT_HELD)) {
+ setLuggage(_currentLuggageResource);
+ }
+ } else {
+ byte buf[NAME_LEN];
+
+ error("ERROR: mouse.pointer==0 for object %d (%s) - update logic script!", _mouseTouching, _vm->_resman->fetchName(_mouseTouching, buf));
+ }
+ } else if (_oldMouseTouching && !_mouseTouching) {
+ // the cursor has moved off something - reset cursor to
+ // normal pointer
+
+ _oldMouseTouching = 0;
+ setMouse(NORMAL_MOUSE_ID);
+
+ // reset luggage only when necessary
+ } else if (_oldMouseTouching && _mouseTouching) {
+ // The cursor has moved off something and onto something
+ // else. Flip to a blank cursor for a cycle.
+
+ // ignore the new id this cycle - should hit next cycle
+ _mouseTouching = 0;
+ _oldMouseTouching = 0;
+ setMouse(0);
+
+ // so we know to set the mouse pointer back to normal if 2nd
+ // hot-spot doesn't register because mouse pulled away
+ // quickly (onto nothing)
+
+ mouse_flicked_off = 1;
+
+ // reset luggage only when necessary
+ } else {
+ // Mouse was flicked off for one cycle, but then moved onto
+ // nothing before 2nd hot-spot registered
+
+ // both '_oldMouseTouching' & '_mouseTouching' will be zero
+ // reset cursor to normal pointer
+
+ setMouse(NORMAL_MOUSE_ID);
+ }
+
+ // possible check for edge of screen more-to-scroll here on large
+ // screens
+}
+
+void Mouse::setMouse(uint32 res) {
+ // high level - whats the mouse - for the engine
+ _mousePointerRes = res;
+
+ if (res) {
+ byte *icon = _vm->_resman->openResource(res) + ResHeader::size();
+ uint32 len = _vm->_resman->fetchLen(res) - ResHeader::size();
+
+ // don't pulse the normal pointer - just do the regular anim
+ // loop
+
+ if (res == NORMAL_MOUSE_ID)
+ setMouseAnim(icon, len, RDMOUSE_NOFLASH);
+ else
+ setMouseAnim(icon, len, RDMOUSE_FLASH);
+
+ _vm->_resman->closeResource(res);
+ } else {
+ // blank cursor
+ setMouseAnim(NULL, 0, 0);
+ }
+}
+
+void Mouse::setLuggage(uint32 res) {
+ _realLuggageItem = res;
+
+ if (res) {
+ byte *icon = _vm->_resman->openResource(res) + ResHeader::size();
+ uint32 len = _vm->_resman->fetchLen(res) - ResHeader::size();
+
+ setLuggageAnim(icon, len);
+ _vm->_resman->closeResource(res);
+ } else
+ setLuggageAnim(NULL, 0);
+}
+
+void Mouse::setObjectHeld(uint32 res) {
+ setLuggage(res);
+
+ _vm->_logic->writeVar(OBJECT_HELD, res);
+ _currentLuggageResource = res;
+
+ // mode locked - no menu available
+ _mouseModeLocked = true;
+}
+
+uint32 Mouse::checkMouseList() {
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ Common::Point mousePos(_pos.x + screenInfo->scroll_offset_x, _pos.y + screenInfo->scroll_offset_y);
+
+ // Number of priorities subject to implementation needs
+ for (int priority = 0; priority < 10; priority++) {
+ for (uint i = 0; i < _curMouse; i++) {
+ // If the mouse pointer is over this
+ // mouse-detection-box
+
+ if (_mouseList[i].priority == priority && _mouseList[i].rect.contains(mousePos)) {
+ // Record id
+ _mouseTouching = _mouseList[i].id;
+
+ createPointerText(_mouseList[i].pointer_text, _mouseList[i].pointer);
+
+ // Return pointer type
+ return _mouseList[i].pointer;
+ }
+ }
+ }
+
+ // Touching nothing; no pointer to return
+ _mouseTouching = 0;
+ return 0;
+}
+
+#define POINTER_TEXT_WIDTH 640 // just in case!
+#define POINTER_TEXT_PEN 184 // white
+
+void Mouse::createPointerText(uint32 text_id, uint32 pointer_res) {
+ uint32 local_text;
+ uint32 text_res;
+ byte *text;
+ // offsets for pointer text sprite from pointer position
+ int16 xOffset, yOffset;
+ uint8 justification;
+
+ if (!_objectLabels || !text_id)
+ return;
+
+ // Check what the pointer is, to set offsets correctly for text
+ // position
+
+ switch (pointer_res) {
+ case CROSHAIR:
+ yOffset = -7;
+ xOffset = +10;
+ break;
+ case EXIT0:
+ yOffset = +15;
+ xOffset = +20;
+ break;
+ case EXIT1:
+ yOffset = +16;
+ xOffset = -10;
+ break;
+ case EXIT2:
+ yOffset = +10;
+ xOffset = -22;
+ break;
+ case EXIT3:
+ yOffset = -16;
+ xOffset = -10;
+ break;
+ case EXIT4:
+ yOffset = -15;
+ xOffset = +15;
+ break;
+ case EXIT5:
+ yOffset = -12;
+ xOffset = +10;
+ break;
+ case EXIT6:
+ yOffset = +10;
+ xOffset = +25;
+ break;
+ case EXIT7:
+ yOffset = +16;
+ xOffset = +20;
+ break;
+ case EXITDOWN:
+ yOffset = -20;
+ xOffset = -10;
+ break;
+ case EXITUP:
+ yOffset = +20;
+ xOffset = +20;
+ break;
+ case MOUTH:
+ yOffset = -10;
+ xOffset = +15;
+ break;
+ case NORMAL:
+ yOffset = -10;
+ xOffset = +15;
+ break;
+ case PICKUP:
+ yOffset = -40;
+ xOffset = +10;
+ break;
+ case SCROLL_L:
+ yOffset = -20;
+ xOffset = +20;
+ break;
+ case SCROLL_R:
+ yOffset = -20;
+ xOffset = -20;
+ break;
+ case USE:
+ yOffset = -8;
+ xOffset = +20;
+ break;
+ default:
+ // Shouldn't happen if we cover all the different mouse
+ // pointers above
+ yOffset = -10;
+ xOffset = +10;
+ break;
+ }
+
+ // Set up justification for text sprite, based on its offsets from the
+ // pointer position
+
+ if (yOffset < 0) {
+ // Above pointer
+ if (xOffset < 0) {
+ // Above left
+ justification = POSITION_AT_RIGHT_OF_BASE;
+ } else if (xOffset > 0) {
+ // Above right
+ justification = POSITION_AT_LEFT_OF_BASE;
+ } else {
+ // Above centre
+ justification = POSITION_AT_CENTRE_OF_BASE;
+ }
+ } else if (yOffset > 0) {
+ // Below pointer
+ if (xOffset < 0) {
+ // Below left
+ justification = POSITION_AT_RIGHT_OF_TOP;
+ } else if (xOffset > 0) {
+ // Below right
+ justification = POSITION_AT_LEFT_OF_TOP;
+ } else {
+ // Below centre
+ justification = POSITION_AT_CENTRE_OF_TOP;
+ }
+ } else {
+ // Same y-coord as pointer
+ if (xOffset < 0) {
+ // Centre left
+ justification = POSITION_AT_RIGHT_OF_CENTRE;
+ } else if (xOffset > 0) {
+ // Centre right
+ justification = POSITION_AT_LEFT_OF_CENTRE;
+ } else {
+ // Centre centre - shouldn't happen anyway!
+ justification = POSITION_AT_LEFT_OF_CENTRE;
+ }
+ }
+
+ // Text resource number, and line number within the resource
+
+ text_res = text_id / SIZE;
+ local_text = text_id & 0xffff;
+
+ // open text file & get the line
+ text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
+
+ // 'text+2' to skip the first 2 bytes which form the
+ // line reference number
+
+ _pointerTextBlocNo = _vm->_fontRenderer->buildNewBloc(
+ text + 2, _pos.x + xOffset,
+ _pos.y + yOffset,
+ POINTER_TEXT_WIDTH, POINTER_TEXT_PEN,
+ RDSPR_TRANS | RDSPR_DISPLAYALIGN,
+ _vm->_speechFontId, justification);
+
+ // now ok to close the text file
+ _vm->_resman->closeResource(text_res);
+}
+
+void Mouse::clearPointerText() {
+ if (_pointerTextBlocNo) {
+ _vm->_fontRenderer->killTextBloc(_pointerTextBlocNo);
+ _pointerTextBlocNo = 0;
+ }
+}
+
+void Mouse::hideMouse() {
+ // leaves the menus open
+ // used by the system when clicking right on a menu item to examine
+ // it and when combining objects
+
+ // for logic scripts
+ _vm->_logic->writeVar(MOUSE_AVAILABLE, 0);
+
+ // human/mouse off
+ _mouseStatus = true;
+
+ setMouse(0);
+ setLuggage(0);
+}
+
+void Mouse::noHuman() {
+ hideMouse();
+ clearPointerText();
+
+ // Must be normal mouse situation or a largely neutral situation -
+ // special menus use hideMouse()
+
+ // Don't hide menu in conversations
+ if (_vm->_logic->readVar(TALK_FLAG) == 0)
+ hideMenu(RDMENU_BOTTOM);
+
+ if (_mouseMode == MOUSE_system_menu) {
+ // Close menu
+ _mouseMode = MOUSE_normal;
+ hideMenu(RDMENU_TOP);
+ }
+}
+
+void Mouse::addHuman() {
+ // For logic scripts
+ _vm->_logic->writeVar(MOUSE_AVAILABLE, 1);
+
+ if (_mouseStatus) {
+ // Force engine to choose a cursor
+ _mouseStatus = false;
+ _mouseTouching = 1;
+ }
+
+ // Clear this to reset no-second-click system
+ _vm->_logic->writeVar(CLICKED_ID, 0);
+
+ // This is now done outside the OBJECT_HELD check in case it's set to
+ // zero before now!
+
+ // Unlock the mouse from possible large object lock situtations - see
+ // syphon in rm 3
+
+ _mouseModeLocked = false;
+
+ if (_vm->_logic->readVar(OBJECT_HELD)) {
+ // Was dragging something around - need to clear this again
+ _vm->_logic->writeVar(OBJECT_HELD, 0);
+
+ // And these may also need clearing, just in case
+ _examiningMenuIcon = false;
+ _vm->_logic->writeVar(COMBINE_BASE, 0);
+
+ setLuggage(0);
+ }
+
+ // If mouse is over menu area
+ if (_pos.y > 399) {
+ if (_mouseMode != MOUSE_holding) {
+ // VITAL - reset things & rebuild the menu
+ _mouseMode = MOUSE_normal;
+ }
+ setMouse(NORMAL_MOUSE_ID);
+ }
+
+ // Enabled/disabled from console; status printed with on-screen debug
+ // info
+
+ if (_vm->_debugger->_testingSnR) {
+ uint8 black[4] = { 0, 0, 0, 0 };
+ uint8 white[4] = { 255, 255, 255, 0 };
+
+ // Testing logic scripts by simulating instant Save & Restore
+
+ _vm->_screen->setPalette(0, 1, white, RDPAL_INSTANT);
+
+ // Stops all fx & clears the queue - eg. when leaving a room
+ _vm->_sound->clearFxQueue();
+
+ // Trash all object resources so they load in fresh & restart
+ // their logic scripts
+
+ _vm->_resman->killAllObjects(false);
+
+ _vm->_screen->setPalette(0, 1, black, RDPAL_INSTANT);
+ }
+}
+
+void Mouse::refreshInventory() {
+ // Can reset this now
+ _vm->_logic->writeVar(COMBINE_BASE, 0);
+
+ // Cause 'object_held' icon to be greyed. The rest are coloured.
+ _examiningMenuIcon = true;
+ buildMenu();
+ _examiningMenuIcon = false;
+}
+
+void Mouse::startConversation() {
+ if (_vm->_logic->readVar(TALK_FLAG) == 0) {
+ // See fnChooser & speech scripts
+ _vm->_logic->writeVar(CHOOSER_COUNT_FLAG, 0);
+ }
+
+ noHuman();
+}
+
+void Mouse::endConversation() {
+ hideMenu(RDMENU_BOTTOM);
+
+ if (_pos.y > 399) {
+ // Will wait for cursor to move off the bottom menu
+ _mouseMode = MOUSE_holding;
+ }
+
+ // In case DC forgets
+ _vm->_logic->writeVar(TALK_FLAG, 0);
+}
+
+void Mouse::monitorPlayerActivity() {
+ // if there is at least one mouse event outstanding
+ if (_vm->checkForMouseEvents()) {
+ // reset activity delay counter
+ _playerActivityDelay = 0;
+ } else {
+ // no. of game cycles since mouse event queue last empty
+ _playerActivityDelay++;
+ }
+}
+
+void Mouse::checkPlayerActivity(uint32 seconds) {
+ // Convert seconds to game cycles
+ uint32 threshold = seconds * 12;
+
+ // If the actual delay is at or above the given threshold, reset the
+ // activity delay counter now that we've got a positive check.
+
+ if (_playerActivityDelay >= threshold) {
+ _playerActivityDelay = 0;
+ _vm->_logic->writeVar(RESULT, 1);
+ } else
+ _vm->_logic->writeVar(RESULT, 0);
+}
+
+void Mouse::pauseGame() {
+ // Make the mouse cursor normal. This is the only place where we are
+ // allowed to clear the luggage this way.
+
+ clearPointerText();
+ setLuggageAnim(NULL, 0);
+ setMouse(0);
+ setMouseTouching(1);
+}
+
+void Mouse::unpauseGame() {
+ if (_vm->_logic->readVar(OBJECT_HELD) && _realLuggageItem)
+ setLuggage(_realLuggageItem);
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/mouse.h b/engines/sword2/mouse.h
new file mode 100644
index 0000000000..bed5e032ba
--- /dev/null
+++ b/engines/sword2/mouse.h
@@ -0,0 +1,257 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef MOUSE_H
+#define MOUSE_H
+
+#define MAX_SUBJECT_LIST 30 // is that enough?
+
+#define TOTAL_mouse_list 50
+
+namespace Sword2 {
+
+struct BuildUnit;
+
+// Menubar defines.
+
+#define RDMENU_TOP 0
+#define RDMENU_BOTTOM 1
+
+enum {
+ MOUSE_normal = 0, // normal in game
+ MOUSE_menu = 1, // menu chooser
+ MOUSE_drag = 2, // dragging luggage
+ MOUSE_system_menu = 3, // system menu chooser
+ MOUSE_holding = 4 // special
+};
+
+enum {
+ RDMOUSE_NOFLASH,
+ RDMOUSE_FLASH
+};
+
+enum {
+ RDMENU_HIDDEN,
+ RDMENU_SHOWN,
+ RDMENU_OPENING,
+ RDMENU_CLOSING,
+ RDMENU_ALMOST_HIDDEN
+};
+
+#define RDMENU_ICONWIDE 35
+#define RDMENU_ICONDEEP 30
+#define RDMENU_ICONSTART 24
+#define RDMENU_ICONSPACING 5
+#define RDMENU_MAXPOCKETS 15
+
+#define MOUSE_ANIM_HEADER_SIZE 6
+
+struct MouseAnim {
+ uint8 runTimeComp; // type of runtime compression used for the
+ // frame data
+ uint8 noAnimFrames; // number of frames in the anim
+ int8 xHotSpot;
+ int8 yHotSpot;
+ uint8 mousew;
+ uint8 mouseh;
+
+ byte *data;
+};
+
+// The MOUSE_holding mode is entered when the conversation menu is closed, and
+// exited when the mouse cursor moves off that menu area. I don't know why yet.
+
+// mouse unit - like ObjectMouse, but with anim resource & pc (needed if
+// sprite is to act as mouse detection mask)
+
+struct MouseUnit {
+ // Basically the same information as in ObjectMouse, except the
+ // coordinates are adjusted to conform to standard ScummVM usage.
+
+ Common::Rect rect;
+ int32 priority;
+ int32 pointer;
+
+ // In addition, we need an id when checking the mouse list, and a
+ // text id for mouse-overs.
+
+ int32 id;
+ int32 pointer_text;
+};
+
+// Array of these for subject menu build up
+
+ struct SubjectUnit {
+ uint32 res;
+ uint32 ref;
+};
+
+class Mouse {
+private:
+ Sword2Engine *_vm;
+
+ Common::Point _pos;
+
+ MouseUnit _mouseList[TOTAL_mouse_list];
+ uint32 _curMouse;
+
+ MenuObject _tempList[TOTAL_engine_pockets];
+ uint32 _totalTemp;
+
+ MenuObject _masterMenuList[TOTAL_engine_pockets];
+ uint32 _totalMasters;
+
+ SubjectUnit _subjectList[MAX_SUBJECT_LIST];
+
+ // ref number for default response when luggage icon is used on a
+ // person & it doesn't match any of the icons which would have been in
+ // the chooser
+
+ uint32 _defaultResponseId;
+
+ // could alternately use logic->looping of course
+ bool _choosing;
+
+ uint8 _menuStatus[2];
+ byte *_icons[2][RDMENU_MAXPOCKETS];
+ uint8 _pocketStatus[2][RDMENU_MAXPOCKETS];
+
+ uint8 _iconCount;
+
+ // If it's NORMAL_MOUSE_ID (ie. normal pointer) then it's over a floor
+ // area (or hidden hot-zone)
+
+ uint32 _mousePointerRes;
+
+ MouseAnim _mouseAnim;
+ MouseAnim _luggageAnim;
+
+ uint8 _mouseFrame;
+
+ uint32 _mouseMode;
+
+ bool _mouseStatus; // Human 0 on/1 off
+ bool _mouseModeLocked; // 0 not !0 mode cannot be changed from
+ // normal mouse to top menu (i.e. when
+ // carrying big objects)
+ uint32 _realLuggageItem; // Last minute for pause mode
+ uint32 _currentLuggageResource;
+ uint32 _oldButton; // For the re-click stuff - must be
+ // the same button you see
+ uint32 _buttonClick;
+ uint32 _pointerTextBlocNo;
+ uint32 _playerActivityDelay; // Player activity delay counter
+
+ bool _examiningMenuIcon;
+
+ // Set by checkMouseList()
+ uint32 _mouseTouching;
+ uint32 _oldMouseTouching;
+
+ bool _objectLabels;
+
+ uint32 _menuSelectedPos;
+
+ void decompressMouse(byte *decomp, byte *comp, uint8 frame, int width, int height, int pitch, int xOff = 0, int yOff = 0);
+
+ int32 setMouseAnim(byte *ma, int32 size, int32 mouseFlash);
+ int32 setLuggageAnim(byte *la, int32 size);
+
+ void clearIconArea(int menu, int pocket, Common::Rect *r);
+
+public:
+ Mouse(Sword2Engine *vm);
+ ~Mouse();
+
+ void getPos(int &x, int &y);
+ void setPos(int x, int y);
+
+ bool getObjectLabels() { return _objectLabels; }
+ void setObjectLabels(bool b) { _objectLabels = b; }
+
+ bool getMouseStatus() { return _mouseStatus; }
+ uint32 getMouseTouching() { return _mouseTouching; }
+ void setMouseTouching(uint32 touching) { _mouseTouching = touching; }
+
+ void pauseGame();
+ void unpauseGame();
+
+ void setMouse(uint32 res);
+ void setLuggage(uint32 res);
+
+ void setObjectHeld(uint32 res);
+
+ void resetMouseList();
+
+ void registerMouse(byte *ob_mouse, BuildUnit *build_unit);
+ void registerPointerText(int32 text_id);
+
+ void createPointerText(uint32 text_id, uint32 pointer_res);
+ void clearPointerText();
+
+ void drawMouse();
+ int32 animateMouse();
+
+ void processMenu();
+
+ void addMenuObject(byte *ptr);
+ void addSubject(int32 id, int32 ref);
+
+ void buildMenu();
+ void buildSystemMenu();
+
+ int32 showMenu(uint8 menu);
+ int32 hideMenu(uint8 menu);
+ int32 setMenuIcon(uint8 menu, uint8 pocket, byte *icon);
+
+ void closeMenuImmediately();
+
+ void refreshInventory();
+
+ void startConversation();
+ void endConversation();
+
+ void hideMouse();
+ void noHuman();
+ void addHuman();
+
+ void resetPlayerActivityDelay() { _playerActivityDelay = 0; }
+ void monitorPlayerActivity();
+ void checkPlayerActivity(uint32 seconds);
+
+ void mouseOnOff();
+ uint32 checkMouseList();
+ void mouseEngine();
+
+ void normalMouse();
+ void menuMouse();
+ void dragMouse();
+ void systemMenuMouse();
+
+ bool isChoosing() { return _choosing; }
+ uint32 chooseMouse();
+
+ int menuClick(int menu_items);
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/music.cpp b/engines/sword2/music.cpp
new file mode 100644
index 0000000000..847f4808cf
--- /dev/null
+++ b/engines/sword2/music.cpp
@@ -0,0 +1,856 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// One feature still missing is the original's DipMusic() function which, as
+// far as I can understand, softened the music volume when someone was
+// speaking, but only (?) if the music was playing loudly at the time.
+//
+// All things considered, I think this is more bother than it's worth.
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
+#include "sound/flac.h"
+#include "sound/rate.h"
+#include "sound/wave.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+
+namespace Sword2 {
+
+static AudioStream *makeCLUStream(Common::File *fp, int size);
+
+static AudioStream *getAudioStream(SoundFileHandle *fh, const char *base, int cd, uint32 id, uint32 *numSamples) {
+ debug(3, "Playing %s from CD %d", base, cd);
+
+ if (!fh->file.isOpen()) {
+ struct {
+ const char *ext;
+ int mode;
+ } file_types[] = {
+ #ifdef USE_MAD
+ { "cl3", kMP3Mode },
+ #endif
+ #ifdef USE_VORBIS
+ { "clg", kVorbisMode },
+ #endif
+ #ifdef USE_FLAC
+ { "clf", kFlacMode },
+ #endif
+ { "clu", kCLUMode }
+ };
+
+ int soundMode = 0;
+ char filename[20];
+
+ for (int i = 0; i < ARRAYSIZE(file_types); i++) {
+ Common::File f;
+
+ sprintf(filename, "%s%d.%s", base, cd, file_types[i].ext);
+ if (f.open(filename)) {
+ soundMode = file_types[i].mode;
+ break;
+ }
+
+ sprintf(filename, "%s.%s", base, file_types[i].ext);
+ if (f.open(filename)) {
+ soundMode = file_types[i].mode;
+ break;
+ }
+ }
+
+ if (soundMode == 0)
+ return NULL;
+
+ fh->file.open(filename);
+ fh->fileType = soundMode;
+ if (!fh->file.isOpen()) {
+ warning("Very strange fopen error");
+ return NULL;
+ }
+ if (fh->fileSize != fh->file.size()) {
+ if (fh->idxTab) {
+ free(fh->idxTab);
+ fh->idxTab = NULL;
+ }
+ }
+ }
+
+ uint32 entrySize = (fh->fileType == kCLUMode) ? 2 : 3;
+
+ if (!fh->idxTab) {
+ fh->file.seek(0);
+ fh->idxLen = fh->file.readUint32LE();
+ fh->file.seek(entrySize * 4);
+
+ fh->idxTab = (uint32*)malloc(fh->idxLen * 3 * sizeof(uint32));
+ for (uint32 cnt = 0; cnt < fh->idxLen; cnt++) {
+ fh->idxTab[cnt * 3 + 0] = fh->file.readUint32LE();
+ fh->idxTab[cnt * 3 + 1] = fh->file.readUint32LE();
+ if (fh->fileType == kCLUMode) {
+ fh->idxTab[cnt * 3 + 2] = fh->idxTab[cnt * 3 + 1];
+ fh->idxTab[cnt * 3 + 1]--;
+ } else
+ fh->idxTab[cnt * 3 + 2] = fh->file.readUint32LE();
+ }
+ }
+
+ uint32 pos = fh->idxTab[id * 3 + 0];
+ uint32 len = fh->idxTab[id * 3 + 1];
+ uint32 enc_len = fh->idxTab[id * 3 + 2];
+
+ if (numSamples)
+ *numSamples = len;
+
+ if (!pos || !len) {
+ fh->file.close();
+ return NULL;
+ }
+
+ fh->file.seek(pos, SEEK_SET);
+
+ switch (fh->fileType) {
+ case kCLUMode:
+ return makeCLUStream(&fh->file, enc_len);
+#ifdef USE_MAD
+ case kMP3Mode:
+ return makeMP3Stream(&fh->file, enc_len);
+#endif
+#ifdef USE_VORBIS
+ case kVorbisMode:
+ return makeVorbisStream(&fh->file, enc_len);
+#endif
+#ifdef USE_FLAC
+ case kFlacMode:
+ return makeFlacStream(&fh->file, enc_len);
+#endif
+ default:
+ return NULL;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Custom AudioStream class to handle Broken Sword 2's audio compression.
+// ----------------------------------------------------------------------------
+
+#define GetCompressedShift(n) ((n) >> 4)
+#define GetCompressedSign(n) (((n) >> 3) & 1)
+#define GetCompressedAmplitude(n) ((n) & 7)
+
+CLUInputStream::CLUInputStream(Common::File *file, int size)
+ : _file(file), _firstTime(true), _bufferEnd(_outbuf + BUFFER_SIZE) {
+
+ _file->incRef();
+
+ // Determine the end position.
+ _file_pos = _file->pos();
+ _end_pos = _file_pos + size;
+
+ // Read in initial data
+ refill();
+}
+
+CLUInputStream::~CLUInputStream() {
+ _file->decRef();
+}
+
+int CLUInputStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samples = 0;
+ while (samples < numSamples && !eosIntern()) {
+ const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
+ memcpy(buffer, _pos, len * 2);
+ buffer += len;
+ _pos += len;
+ samples += len;
+ if (_pos >= _bufferEnd) {
+ refill();
+ }
+ }
+ return samples;
+}
+
+void CLUInputStream::refill() {
+ byte *in = _inbuf;
+ int16 *out = _outbuf;
+
+ _file->seek(_file_pos, SEEK_SET);
+
+ uint len_left = _file->read(in, MIN((uint32)BUFFER_SIZE, _end_pos - _file->pos()));
+
+ _file_pos = _file->pos();
+
+ while (len_left > 0) {
+ uint16 sample;
+
+ if (_firstTime) {
+ _firstTime = false;
+ _prev = READ_LE_UINT16(in);
+ sample = _prev;
+ len_left -= 2;
+ in += 2;
+ } else {
+ uint16 delta = GetCompressedAmplitude(*in) << GetCompressedShift(*in);
+ if (GetCompressedSign(*in))
+ sample = _prev - delta;
+ else
+ sample = _prev + delta;
+
+ _prev = sample;
+ len_left--;
+ in++;
+ }
+
+ *out++ = sample;
+ }
+
+ _pos = _outbuf;
+ _bufferEnd = out;
+}
+
+AudioStream *makeCLUStream(Common::File *file, int size) {
+ return new CLUInputStream(file, size);
+}
+
+// ----------------------------------------------------------------------------
+// Another custom AudioStream class, to wrap around the various AudioStream
+// classes used for music decompression, and to add looping, fading, etc.
+// ----------------------------------------------------------------------------
+
+// The length of a fade-in/out, in milliseconds.
+#define FADE_LENGTH 3000
+
+MusicInputStream::MusicInputStream(int cd, SoundFileHandle *fh, uint32 musicId, bool looping) {
+ _cd = cd;
+ _fh = fh;
+ _musicId = musicId;
+ _looping = looping;
+
+ _bufferEnd = _buffer + BUFFER_SIZE;
+ _remove = false;
+ _fading = 0;
+
+ _decoder = getAudioStream(_fh, "music", _cd, _musicId, &_numSamples);
+ if (_decoder) {
+ _samplesLeft = _numSamples;
+ _fadeSamples = (getRate() * FADE_LENGTH) / 1000;
+ fadeUp();
+
+ // Read in initial data
+ refill();
+ }
+}
+
+MusicInputStream::~MusicInputStream() {
+ delete _decoder;
+}
+
+int MusicInputStream::readBuffer(int16 *buffer, const int numSamples) {
+ if (!_decoder)
+ return 0;
+
+ int samples = 0;
+ while (samples < numSamples && !eosIntern()) {
+ const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
+ memcpy(buffer, _pos, len * 2);
+ buffer += len;
+ _pos += len;
+ samples += len;
+ if (_pos >= _bufferEnd) {
+ refill();
+ }
+ }
+ return samples;
+}
+
+void MusicInputStream::refill() {
+ int16 *buf = _buffer;
+ uint32 numSamples = 0;
+ uint32 len_left;
+ bool endFade = false;
+
+ len_left = BUFFER_SIZE;
+
+ if (_fading > 0 && (uint32)_fading < len_left)
+ len_left = _fading;
+
+ if (_samplesLeft < len_left)
+ len_left = _samplesLeft;
+
+ if (!_looping) {
+ // Non-looping music is faded out at the end. If this fade
+ // out would have started somewhere within the len_left samples
+ // to read, we only read up to that point. This way, we can
+ // treat this fade as any other.
+
+ if (!_fading) {
+ uint32 currentlyAt = _numSamples - _samplesLeft;
+ uint32 fadeOutAt = _numSamples - _fadeSamples;
+ uint32 readTo = currentlyAt + len_left;
+
+ if (fadeOutAt == currentlyAt)
+ fadeDown();
+ else if (fadeOutAt > currentlyAt && fadeOutAt <= readTo) {
+ len_left = fadeOutAt - currentlyAt;
+ endFade = true;
+ }
+ }
+ }
+
+ int desired = len_left - numSamples;
+ int len = _decoder->readBuffer(buf, desired);
+
+ // Shouldn't happen, but if it does it could cause an infinite loop.
+ // Of course there were bugs that caused it to happen several times
+ // during development. :-)
+
+ if (len < desired) {
+ warning("Expected %d samples, but got %d", desired, len);
+ _samplesLeft = len;
+ }
+
+ buf += len;
+ numSamples += len;
+ len_left -= len;
+ _samplesLeft -= len;
+
+ int16 *ptr;
+
+ if (_fading > 0) {
+ // Fade down
+ for (ptr = _buffer; ptr < buf; ptr++) {
+ if (_fading > 0) {
+ _fading--;
+ *ptr = (*ptr * _fading) / _fadeSamples;
+ }
+ if (_fading == 0) {
+ _looping = false;
+ _remove = true;
+ *ptr = 0;
+ }
+ }
+ } else if (_fading < 0) {
+ // Fade up
+ for (ptr = _buffer; ptr < buf; ptr++) {
+ _fading--;
+ *ptr = -(*ptr * _fading) / _fadeSamples;
+ if (_fading <= -_fadeSamples) {
+ _fading = 0;
+ break;
+ }
+ }
+ }
+
+ if (endFade)
+ fadeDown();
+
+ if (!_samplesLeft) {
+ if (_looping) {
+ delete _decoder;
+ _decoder = getAudioStream(_fh, "music", _cd, _musicId, &_numSamples);
+ _samplesLeft = _numSamples;
+ } else
+ _remove = true;
+ }
+
+ _pos = _buffer;
+ _bufferEnd = buf;
+}
+
+void MusicInputStream::fadeUp() {
+ if (_fading > 0)
+ _fading = -_fading;
+ else if (_fading == 0)
+ _fading = -1;
+}
+
+void MusicInputStream::fadeDown() {
+ if (_fading < 0)
+ _fading = -_fading;
+ else if (_fading == 0)
+ _fading = _fadeSamples;
+}
+
+bool MusicInputStream::readyToRemove() {
+ return _remove;
+}
+
+int32 MusicInputStream::getTimeRemaining() {
+ // This is far from exact, but it doesn't have to be.
+ return (_samplesLeft + BUFFER_SIZE) / getRate();
+}
+
+// ----------------------------------------------------------------------------
+// Main sound class
+// ----------------------------------------------------------------------------
+
+// AudioStream API
+
+int Sound::readBuffer(int16 *buffer, const int numSamples) {
+ Common::StackLock lock(_mutex);
+ int i;
+
+ if (_musicPaused)
+ return 0;
+
+ for (i = 0; i < MAXMUS; i++) {
+ if (_music[i] && _music[i]->readyToRemove()) {
+ delete _music[i];
+ _music[i] = NULL;
+ }
+ }
+
+ memset(buffer, 0, 2 * numSamples);
+
+ if (!_mixBuffer || numSamples > _mixBufferLen) {
+ if (_mixBuffer)
+ _mixBuffer = (int16 *)realloc(_mixBuffer, 2 * numSamples);
+ else
+ _mixBuffer = (int16 *)malloc(2 * numSamples);
+
+ _mixBufferLen = numSamples;
+ }
+
+ if (!_mixBuffer)
+ return 0;
+
+ for (i = 0; i < MAXMUS; i++) {
+ if (!_music[i])
+ continue;
+
+ int len = _music[i]->readBuffer(_mixBuffer, numSamples);
+
+ if (!_musicMuted) {
+ for (int j = 0; j < len; j++) {
+ Audio::clampedAdd(buffer[j], _mixBuffer[j]);
+ }
+ }
+ }
+
+ bool inUse[MAXMUS];
+
+ for (i = 0; i < MAXMUS; i++)
+ inUse[i] = false;
+
+ for (i = 0; i < MAXMUS; i++) {
+ if (_music[i]) {
+ if (_music[i]->getCD() == 1)
+ inUse[0] = true;
+ else
+ inUse[1] = true;
+ }
+ }
+
+ for (i = 0; i < MAXMUS; i++) {
+ if (!inUse[i] && !_musicFile[i].inUse && _musicFile[i].file.isOpen())
+ _musicFile[i].file.close();
+ }
+
+ return numSamples;
+}
+
+bool Sound::endOfData() const {
+ for (int i = 0; i < MAXMUS; i++) {
+ if (_musicFile[i].file.isOpen())
+ return false;
+ }
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+// MUSIC
+// ----------------------------------------------------------------------------
+
+/**
+ * Stops the music dead in its tracks. Any music that is currently being
+ * streamed is paused.
+ */
+
+void Sound::pauseMusic() {
+ Common::StackLock lock(_mutex);
+
+ _musicPaused = true;
+}
+
+/**
+ * Restarts the music from where it was stopped.
+ */
+
+void Sound::unpauseMusic() {
+ Common::StackLock lock(_mutex);
+
+ _musicPaused = false;
+}
+
+/**
+ * Fades out and stops the music.
+ */
+
+void Sound::stopMusic(bool immediately) {
+ Common::StackLock lock(_mutex);
+
+ _loopingMusicId = 0;
+
+ for (int i = 0; i < MAXMUS; i++) {
+ if (_music[i]) {
+ if (immediately) {
+ delete _music[i];
+ _music[i] = NULL;
+ } else
+ _music[i]->fadeDown();
+ }
+ }
+}
+
+/**
+ * Streams music from a cluster file.
+ * @param musicId the id of the music to stream
+ * @param loop true if the music is to loop back to the start
+ * @return RD_OK or an error code
+ */
+int32 Sound::streamCompMusic(uint32 musicId, bool loop) {
+ //Common::StackLock lock(_mutex);
+
+ _mutex.lock();
+ int cd = _vm->_resman->getCD();
+
+ if (loop)
+ _loopingMusicId = musicId;
+ else
+ _loopingMusicId = 0;
+
+ int primary = -1;
+ int secondary = -1;
+
+ // If both music streams are active, one of them will have to go.
+
+ if (_music[0] && _music[1]) {
+ int32 fade0 = _music[0]->isFading();
+ int32 fade1 = _music[1]->isFading();
+
+ if (!fade0 && !fade1) {
+ // Neither is fading. This shouldn't happen, so just
+ // pick one and be done with it.
+ primary = 0;
+ } else if (fade0 && !fade1) {
+ // Stream 0 is fading, so pick that one.
+ primary = 0;
+ } else if (!fade0 && fade1) {
+ // Stream 1 is fading, so pick that one.
+ primary = 1;
+ } else {
+ // Both streams are fading. Pick the one that is
+ // closest to silent.
+ if (ABS(fade0) < ABS(fade1))
+ primary = 0;
+ else
+ primary = 1;
+ }
+
+ delete _music[primary];
+ _music[primary] = NULL;
+ }
+
+ // Pick the available music stream. If no music is playing it doesn't
+ // matter which we use.
+
+ if (_music[0] || _music[1]) {
+ if (_music[0]) {
+ primary = 1;
+ secondary = 0;
+ } else {
+ primary = 0;
+ secondary = 1;
+ }
+ } else
+ primary = 0;
+
+ // Don't start streaming if the volume is off.
+ if (isMusicMute()) {
+ _mutex.unlock();
+ return RD_OK;
+ }
+
+ if (secondary != -1)
+ _music[secondary]->fadeDown();
+ SoundFileHandle *fh = (cd == 1) ? &_musicFile[0] : &_musicFile[1];
+ fh->inUse = true;
+ _mutex.unlock();
+
+ MusicInputStream *tmp = new MusicInputStream(cd, fh, musicId, loop);
+
+ if (tmp->isReady()) {
+ _mutex.lock();
+ _music[primary] = tmp;
+ fh->inUse = false;
+ _mutex.unlock();
+ return RD_OK;
+ } else {
+ _mutex.lock();
+ fh->inUse = false;
+ _mutex.unlock();
+ delete tmp;
+ return RDERR_INVALIDFILENAME;
+ }
+}
+
+/**
+ * @return the time left for the current music, in seconds.
+ */
+
+int32 Sound::musicTimeRemaining() {
+ Common::StackLock lock(_mutex);
+
+ for (int i = 0; i < MAXMUS; i++) {
+ if (_music[i] && _music[i]->isFading() <= 0)
+ return _music[i]->getTimeRemaining();
+ }
+
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+// SPEECH
+// ----------------------------------------------------------------------------
+
+/**
+ * Mutes/Unmutes the speech.
+ * @param mute If mute is false, restore the volume to the last set master
+ * level. Otherwise the speech is muted (volume 0).
+ */
+
+void Sound::muteSpeech(bool mute) {
+ _speechMuted = mute;
+
+ if (_vm->_mixer->isSoundHandleActive(_soundHandleSpeech)) {
+ uint volume = mute ? 0 : Audio::Mixer::kMaxChannelVolume;
+
+ _vm->_mixer->setChannelVolume(_soundHandleSpeech, volume);
+ }
+}
+
+/**
+ * Stops the speech dead in its tracks.
+ */
+
+void Sound::pauseSpeech() {
+ _speechPaused = true;
+ _vm->_mixer->pauseHandle(_soundHandleSpeech, true);
+}
+
+/**
+ * Restarts the speech from where it was stopped.
+ */
+
+void Sound::unpauseSpeech() {
+ _speechPaused = false;
+ _vm->_mixer->pauseHandle(_soundHandleSpeech, false);
+}
+
+/**
+ * Stops the speech from playing.
+ */
+
+int32 Sound::stopSpeech() {
+ if (_vm->_mixer->isSoundHandleActive(_soundHandleSpeech)) {
+ _vm->_mixer->stopHandle(_soundHandleSpeech);
+ return RD_OK;
+ }
+
+ return RDERR_SPEECHNOTPLAYING;
+}
+
+/**
+ * @return Either RDSE_SAMPLEPLAYING or RDSE_SAMPLEFINISHED
+ */
+
+int32 Sound::getSpeechStatus() {
+ return _vm->_mixer->isSoundHandleActive(_soundHandleSpeech) ? RDSE_SAMPLEPLAYING : RDSE_SAMPLEFINISHED;
+}
+
+/**
+ * Returns either RDSE_QUIET or RDSE_SPEAKING
+ */
+
+int32 Sound::amISpeaking() {
+ if (!_speechMuted && !_speechPaused && _vm->_mixer->isSoundHandleActive(_soundHandleSpeech))
+ return RDSE_SPEAKING;
+
+ return RDSE_QUIET;
+}
+
+/**
+ * This function loads and decompresses a list of speech from a cluster, but
+ * does not play it. This is used for cutscene voice-overs, presumably to
+ * avoid having to read from more than one file on the CD during playback.
+ * @param speechId the text line id used to reference the speech
+ * @param buf a pointer to the buffer that will be allocated for the sound
+ */
+
+uint32 Sound::preFetchCompSpeech(uint32 speechId, uint16 **buf) {
+ int cd = _vm->_resman->getCD();
+ uint32 numSamples;
+
+ SoundFileHandle *fh = (cd == 1) ? &_speechFile[0] : &_speechFile[1];
+
+ AudioStream *input = getAudioStream(fh, "speech", cd, speechId, &numSamples);
+
+ if (!input)
+ return 0;
+
+ *buf = NULL;
+
+ // Decompress data into speech buffer.
+
+ uint32 bufferSize = 2 * numSamples;
+
+ *buf = (uint16 *)malloc(bufferSize);
+ if (!*buf) {
+ delete input;
+ fh->file.close();
+ return 0;
+ }
+
+ uint32 readSamples = input->readBuffer((int16 *)*buf, numSamples);
+
+ fh->file.close();
+ delete input;
+
+ return 2 * readSamples;
+}
+
+/**
+ * This function loads, decompresses and plays a line of speech. An error
+ * occurs if speech is already playing.
+ * @param speechId the text line id used to reference the speech
+ * @param vol volume, 0 (minimum) to 16 (maximum)
+ * @param pan panning, -16 (full left) to 16 (full right)
+ */
+
+int32 Sound::playCompSpeech(uint32 speechId, uint8 vol, int8 pan) {
+ if (_speechMuted)
+ return RD_OK;
+
+ if (getSpeechStatus() == RDERR_SPEECHPLAYING)
+ return RDERR_SPEECHPLAYING;
+
+ int cd = _vm->_resman->getCD();
+ SoundFileHandle *fh = (cd == 1) ? &_speechFile[0] : &_speechFile[1];
+
+ AudioStream *input = getAudioStream(fh, "speech", cd, speechId, NULL);
+
+ if (!input)
+ return RDERR_INVALIDID;
+
+ // Modify the volume according to the master volume
+
+ byte volume = _speechMuted ? 0 : vol * Audio::Mixer::kMaxChannelVolume / 16;
+ int8 p = (pan * 127) / 16;
+
+ if (isReverseStereo())
+ p = -p;
+
+ // Start the speech playing
+ _vm->_mixer->playInputStream(Audio::Mixer::kSpeechSoundType, &_soundHandleSpeech, input, -1, volume, p);
+ return RD_OK;
+}
+
+// ----------------------------------------------------------------------------
+// SOUND EFFECTS
+// ----------------------------------------------------------------------------
+
+/**
+ * Mutes/Unmutes the sound effects.
+ * @param mute If mute is false, restore the volume to the last set master
+ * level. Otherwise the sound effects are muted (volume 0).
+ */
+
+void Sound::muteFx(bool mute) {
+ _fxMuted = mute;
+
+ // Now update the volume of any fxs playing
+ for (int i = 0; i < FXQ_LENGTH; i++) {
+ if (_fxQueue[i].resource) {
+ _vm->_mixer->setChannelVolume(_fxQueue[i].handle, mute ? 0 : _fxQueue[i].volume);
+ }
+ }
+}
+
+/**
+ * Sets the volume and pan of the sample which is currently playing
+ * @param id the id of the sample
+ * @param vol volume
+ * @param pan panning
+ */
+
+int32 Sound::setFxIdVolumePan(int32 id, int vol, int pan) {
+ if (!_fxQueue[id].resource)
+ return RDERR_FXNOTOPEN;
+
+ if (vol > 16)
+ vol = 16;
+
+ _fxQueue[id].volume = (vol * Audio::Mixer::kMaxChannelVolume) / 16;
+
+ if (pan != 255) {
+ if (isReverseStereo())
+ pan = -pan;
+ _fxQueue[id].pan = (pan * 127) / 16;
+ }
+
+ if (!_fxMuted && _vm->_mixer->isSoundHandleActive(_fxQueue[id].handle)) {
+ _vm->_mixer->setChannelVolume(_fxQueue[id].handle, _fxQueue[id].volume);
+ if (pan != -1)
+ _vm->_mixer->setChannelBalance(_fxQueue[id].handle, _fxQueue[id].pan);
+ }
+
+ return RD_OK;
+}
+
+void Sound::pauseFx() {
+ if (_fxPaused)
+ return;
+
+ for (int i = 0; i < FXQ_LENGTH; i++) {
+ if (_fxQueue[i].resource)
+ _vm->_mixer->pauseHandle(_fxQueue[i].handle, true);
+ }
+
+ _fxPaused = true;
+}
+
+void Sound::unpauseFx() {
+ if (!_fxPaused)
+ return;
+
+ for (int i = 0; i < FXQ_LENGTH; i++)
+ if (_fxQueue[i].resource)
+ _vm->_mixer->pauseHandle(_fxQueue[i].handle, false);
+
+ _fxPaused = false;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/object.h b/engines/sword2/object.h
new file mode 100644
index 0000000000..1fd434f8a0
--- /dev/null
+++ b/engines/sword2/object.h
@@ -0,0 +1,342 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _SCRIPT_STRUCTURES
+#define _SCRIPT_STRUCTURES
+
+#include "common/stream.h"
+
+namespace Sword2 {
+
+// these structures represent the broken up compact components
+// these here declared to the system must be the same as those declared to
+// LINC (or it wont work)
+
+// mouse structure - defines mouse detection area, detection priority &
+// 'type' flag
+
+struct ObjectMouse {
+ int32 x1; // Top-left and bottom-right of mouse
+ int32 y1; // area. (These coords are inclusive.)
+ int32 x2;
+ int32 y2;
+ int32 priority;
+ int32 pointer; // type (or resource id?) of pointer used over this area
+
+ static const int size() {
+ return 24;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ x1 = readS.readSint32LE();
+ y1 = readS.readSint32LE();
+ x2 = readS.readSint32LE();
+ y2 = readS.readSint32LE();
+ priority = readS.readSint32LE();
+ pointer = readS.readSint32LE();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeSint32LE(x1);
+ writeS.writeSint32LE(y1);
+ writeS.writeSint32LE(x2);
+ writeS.writeSint32LE(y2);
+ writeS.writeSint32LE(priority);
+ writeS.writeSint32LE(pointer);
+ }
+};
+
+// logic structure - contains fields used in logic script processing
+
+class ObjectLogic {
+ // int32 looping; // 0 when first calling fn<function>;
+ // 1 when calling subsequent times in
+ // same loop
+ // int32 pause; // pause count, used by fnPause()
+
+private:
+ byte *_addr;
+
+public:
+ ObjectLogic(byte *addr) {
+ _addr = addr;
+ }
+
+ static const int size() {
+ return 8;
+ }
+
+ byte *data() {
+ return _addr;
+ }
+
+ int32 getLooping() { return READ_LE_UINT32(_addr); }
+ int32 getPause() { return READ_LE_UINT32(_addr + 4); }
+
+ void setLooping(int32 x) { WRITE_LE_UINT32(_addr, x); }
+ void setPause(int32 x) { WRITE_LE_UINT32(_addr + 4, x); }
+};
+
+// status bits for 'type' field of ObjectGraphic)
+
+// in low word:
+
+#define NO_SPRITE 0x00000000 // don't print
+#define BGP0_SPRITE 0x00000001 // fixed to background parallax[0]
+#define BGP1_SPRITE 0x00000002 // fixed to background parallax[1]
+#define BACK_SPRITE 0x00000004 // 'background' sprite, fixed to main background
+#define SORT_SPRITE 0x00000008 // 'sorted' sprite, fixed to main background
+#define FORE_SPRITE 0x00000010 // 'foreground' sprite, fixed to main background
+#define FGP0_SPRITE 0x00000020 // fixed to foreground parallax[0]
+#define FGP1_SPRITE 0x00000040 // fixed to foreground parallax[0]
+
+// in high word:
+
+#define UNSHADED_SPRITE 0x00000000 // not to be shaded
+#define SHADED_SPRITE 0x00010000 // to be shaded, based on shading mask
+
+// graphic structure - contains fields appropriate to sprite output
+
+class ObjectGraphic {
+ // int32 type; // see above
+ // int32 anim_resource; // resource id of animation file
+ // int32 anim_pc; // current frame number of animation
+
+private:
+ byte *_addr;
+
+public:
+ ObjectGraphic(byte *addr) {
+ _addr = addr;
+ }
+
+ static const int size() {
+ return 12;
+ }
+
+ byte *data() {
+ return _addr;
+ }
+
+ int32 getType() { return READ_LE_UINT32(_addr); }
+ int32 getAnimResource() { return READ_LE_UINT32(_addr + 4); }
+ int32 getAnimPc() { return READ_LE_UINT32(_addr + 8); }
+
+ void setType(int32 x) { WRITE_LE_UINT32(_addr, x); }
+ void setAnimResource(int32 x) { WRITE_LE_UINT32(_addr + 4, x); }
+ void setAnimPc(int32 x) { WRITE_LE_UINT32(_addr + 8, x); }
+};
+
+// speech structure - contains fields used by speech scripts & text output
+
+class ObjectSpeech {
+ // int32 pen; // colour to use for body of characters
+ // int32 width; // max width of text sprite
+ // int32 command; // speech script command id
+ // int32 ins1; // speech script instruction parameters
+ // int32 ins2;
+ // int32 ins3;
+ // int32 ins4;
+ // int32 ins5;
+ // int32 wait_state; // 0 not waiting,
+ // 1 waiting for next speech command
+
+private:
+ byte *_addr;
+
+public:
+ ObjectSpeech(byte *addr) {
+ _addr = addr;
+ }
+
+ static const int size() {
+ return 36;
+ }
+
+ byte *data() {
+ return _addr;
+ }
+
+ int32 getPen() { return READ_LE_UINT32(_addr); }
+ int32 getWidth() { return READ_LE_UINT32(_addr + 4); }
+ int32 getCommand() { return READ_LE_UINT32(_addr + 8); }
+ int32 getIns1() { return READ_LE_UINT32(_addr + 12); }
+ int32 getIns2() { return READ_LE_UINT32(_addr + 16); }
+ int32 getIns3() { return READ_LE_UINT32(_addr + 20); }
+ int32 getIns4() { return READ_LE_UINT32(_addr + 24); }
+ int32 getIns5() { return READ_LE_UINT32(_addr + 28); }
+ int32 getWaitState() { return READ_LE_UINT32(_addr + 32); }
+
+ void setPen(int32 x) { WRITE_LE_UINT32(_addr, x); }
+ void setWidth(int32 x) { WRITE_LE_UINT32(_addr + 4, x); }
+ void setCommand(int32 x) { WRITE_LE_UINT32(_addr + 8, x); }
+ void setIns1(int32 x) { WRITE_LE_UINT32(_addr + 12, x); }
+ void setIns2(int32 x) { WRITE_LE_UINT32(_addr + 16, x); }
+ void setIns3(int32 x) { WRITE_LE_UINT32(_addr + 20, x); }
+ void setIns4(int32 x) { WRITE_LE_UINT32(_addr + 24, x); }
+ void setIns5(int32 x) { WRITE_LE_UINT32(_addr + 28, x); }
+ void setWaitState(int32 x) { WRITE_LE_UINT32(_addr + 32, x); }
+};
+
+// mega structure - contains fields used for mega-character & mega-set
+// processing
+
+class ObjectMega {
+ // int32 NOT_USED_1; // only free roaming megas need to
+ // check this before registering their
+ // graphics for drawing
+ // int32 NOT_USED_2; // id of floor on which we are standing
+ // int32 NOT_USED_3; // id of object which we are getting to
+ // int32 NOT_USED_4; // pixel distance to stand from player
+ // character when in conversation
+ // int32 currently_walking; // number given us by the auto router
+ // int32 walk_pc; // current frame number of walk-anim
+ // int32 scale_a; // current scale factors, taken from
+ // int32 scale_b; // floor data
+ // int32 feet_x; // mega feet coords - frame-offsets are
+ // int32 feet_y; // added to these position mega frames
+ // int32 current_dir; // current dirction faced by mega; used
+ // by autorouter to determine turns
+ // required
+ // int32 NOT_USED_5; // means were currently avoiding a
+ // collision (see fnWalk)
+ // int32 megaset_res; // resource id of mega-set file
+ // int32 NOT_USED_6; // NOT USED
+
+private:
+ byte *_addr;
+
+public:
+ ObjectMega(byte *addr) {
+ _addr = addr;
+ }
+
+ static const int size() {
+ return 56;
+ }
+
+ byte *data() {
+ return _addr;
+ }
+
+ int32 getIsWalking() { return READ_LE_UINT32(_addr + 16); }
+ int32 getWalkPc() { return READ_LE_UINT32(_addr + 20); }
+ int32 getScaleA() { return READ_LE_UINT32(_addr + 24); }
+ int32 getScaleB() { return READ_LE_UINT32(_addr + 28); }
+ int32 getFeetX() { return READ_LE_UINT32(_addr + 32); }
+ int32 getFeetY() { return READ_LE_UINT32(_addr + 36); }
+ int32 getCurDir() { return READ_LE_UINT32(_addr + 40); }
+ int32 getMegasetRes() { return READ_LE_UINT32(_addr + 48); }
+
+ void setIsWalking(int32 x) { WRITE_LE_UINT32(_addr + 16, x); }
+ void setWalkPc(int32 x) { WRITE_LE_UINT32(_addr + 20, x); }
+ void setScaleA(int32 x) { WRITE_LE_UINT32(_addr + 24, x); }
+ void setScaleB(int32 x) { WRITE_LE_UINT32(_addr + 28, x); }
+ void setFeetX(int32 x) { WRITE_LE_UINT32(_addr + 32, x); }
+ void setFeetY(int32 x) { WRITE_LE_UINT32(_addr + 36, x); }
+ void setCurDir(int32 x) { WRITE_LE_UINT32(_addr + 40, x); }
+ void setMegasetRes(int32 x) { WRITE_LE_UINT32(_addr + 48, x); }
+
+ int32 calcScale() {
+ // Calc scale at which to print the sprite, based on feet
+ // y-coord & scaling constants (NB. 'scale' is actually
+ // 256 * true_scale, to maintain accuracy)
+
+ // Ay+B gives 256 * scale ie. 256 * 256 * true_scale for even
+ // better accuracy, ie. scale = (Ay + B) / 256
+ return (getScaleA() * getFeetY() + getScaleB()) / 256;
+ }
+};
+
+// walk-data structure - contains details of layout of frames in the
+// mega-set, and how they are to be used
+
+struct ObjectWalkdata {
+ int32 nWalkFrames; // no. of frames per walk-cycle
+ int32 usingStandingTurnFrames; // 0 = no 1 = yes
+ int32 usingWalkingTurnFrames; // 0 = no 1 = yes
+ int32 usingSlowInFrames; // 0 = no 1 = yes
+ int32 usingSlowOutFrames; // 0 = no !0 = number of slow-out frames in each direction
+ int32 nSlowInFrames[8]; // no. of slow-in frames in each direction
+ int32 leadingLeg[8]; // leading leg for walk in each direction (0 = left 1 = right)
+ int32 dx[8 * (12 + 1)]; // walk step distances in x direction
+ int32 dy[8 * (12 + 1)]; // walk step distances in y direction
+
+ static const int size() {
+ return 916;
+ }
+
+ void read(byte *addr) {
+ Common::MemoryReadStream readS(addr, size());
+
+ nWalkFrames = readS.readUint32LE();
+ usingStandingTurnFrames = readS.readUint32LE();
+ usingWalkingTurnFrames = readS.readUint32LE();
+ usingSlowInFrames = readS.readUint32LE();
+ usingSlowOutFrames = readS.readUint32LE();
+
+ int i;
+
+ for (i = 0; i < ARRAYSIZE(nSlowInFrames); i++)
+ nSlowInFrames[i] = readS.readUint32LE();
+
+ for (i = 0; i < ARRAYSIZE(leadingLeg); i++)
+ leadingLeg[i] = readS.readUint32LE();
+
+ for (i = 0; i < ARRAYSIZE(dx); i++)
+ dx[i] = readS.readUint32LE();
+
+ for (i = 0; i < ARRAYSIZE(dy); i++)
+ dy[i] = readS.readUint32LE();
+ }
+
+ void write(byte *addr) {
+ Common::MemoryWriteStream writeS(addr, size());
+
+ writeS.writeUint32LE(nWalkFrames);
+ writeS.writeUint32LE(usingStandingTurnFrames);
+ writeS.writeUint32LE(usingWalkingTurnFrames);
+ writeS.writeUint32LE(usingSlowInFrames);
+ writeS.writeUint32LE(usingSlowOutFrames);
+
+ int i;
+
+ for (i = 0; i < ARRAYSIZE(nSlowInFrames); i++)
+ writeS.writeUint32LE(nSlowInFrames[i]);
+
+ for (i = 0; i < ARRAYSIZE(leadingLeg); i++)
+ writeS.writeUint32LE(leadingLeg[i]);
+
+ for (i = 0; i < ARRAYSIZE(dx); i++)
+ writeS.writeUint32LE(dx[i]);
+
+ for (i = 0; i < ARRAYSIZE(dy); i++)
+ writeS.writeUint32LE(dy[i]);
+ }
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/palette.cpp b/engines/sword2/palette.cpp
new file mode 100644
index 0000000000..0b6b7a8ca2
--- /dev/null
+++ b/engines/sword2/palette.cpp
@@ -0,0 +1,267 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/resman.h"
+
+namespace Sword2 {
+
+/**
+ * Start layer palette fading up
+ */
+
+void Screen::startNewPalette() {
+ // If the screen is still fading down then wait for black - could
+ // happen when everythings cached into a large memory model
+ waitForFade();
+
+ byte *screenFile = _vm->_resman->openResource(_thisScreen.background_layer_id);
+
+ memcpy(_paletteMatch, _vm->fetchPaletteMatchTable(screenFile), PALTABLESIZE);
+ setPalette(0, 256, _vm->fetchPalette(screenFile), RDPAL_FADE);
+
+ // Indicating that it's a screen palette
+ _lastPaletteRes = 0;
+
+ _vm->_resman->closeResource(_thisScreen.background_layer_id);
+ fadeUp();
+ _thisScreen.new_palette = 0;
+}
+
+void Screen::setFullPalette(int32 palRes) {
+ // fudge for hut interior
+ // - unpausing should restore last palette as normal (could be screen
+ // palette or 'dark_palette_13')
+ // - but restoring the screen palette after 'dark_palette_13' should
+ // now work properly too!
+
+ // "Hut interior" refers to the watchman's hut in Marseille, and this
+ // is apparently needed for the palette to be restored properly when
+ // you turn the light off. (I didn't even notice the light switch!)
+
+ if (_vm->_logic->readVar(LOCATION) == 13) {
+ // unpausing
+ if (palRes == -1) {
+ // restore whatever palette was last set (screen
+ // palette or 'dark_palette_13')
+ palRes = _lastPaletteRes;
+ }
+ } else {
+ // check if we're just restoring the current screen palette
+ // because we might actually need to use a separate palette
+ // file anyway eg. for pausing & unpausing during the eclipse
+
+ // unpausing (fudged for location 13)
+ if (palRes == -1) {
+ // we really meant '0'
+ palRes = 0;
+ }
+
+ if (palRes == 0 && _lastPaletteRes)
+ palRes = _lastPaletteRes;
+ }
+
+ // If non-zero, set palette to this separate palette file. Otherwise,
+ // set palette to current screen palette.
+
+ if (palRes) {
+ byte *pal = _vm->_resman->openResource(palRes);
+
+ assert(_vm->_resman->fetchType(pal) == PALETTE_FILE);
+
+ pal += ResHeader::size();
+
+ // always set colour 0 to black because most background screen
+ // palettes have a bright colour 0 although it should come out
+ // as black in the game!
+
+ pal[0] = 0;
+ pal[1] = 0;
+ pal[2] = 0;
+ pal[3] = 0;
+
+ setPalette(0, 256, pal, RDPAL_INSTANT);
+ _vm->_resman->closeResource(palRes);
+ } else {
+ if (_thisScreen.background_layer_id) {
+ byte *data = _vm->_resman->openResource(_thisScreen.background_layer_id);
+ memcpy(_paletteMatch, _vm->fetchPaletteMatchTable(data), PALTABLESIZE);
+ setPalette(0, 256, _vm->fetchPalette(data), RDPAL_INSTANT);
+ _vm->_resman->closeResource(_thisScreen.background_layer_id);
+ } else
+ error("setFullPalette(0) called, but no current screen available!");
+ }
+
+ if (palRes != CONTROL_PANEL_PALETTE)
+ _lastPaletteRes = palRes;
+}
+
+/**
+ * Matches a colour triplet to a palette index.
+ * @param r red colour component
+ * @param g green colour component
+ * @param b blue colour component
+ * @return the palette index of the closest matching colour in the palette
+ */
+
+// FIXME: This used to be inlined - probably a good idea - but the
+// linker complained when I tried to use it in sprite.cpp.
+
+uint8 Screen::quickMatch(uint8 r, uint8 g, uint8 b) {
+ return _paletteMatch[((int32)(r >> 2) << 12) + ((int32)(g >> 2) << 6) + (b >> 2)];
+}
+
+/**
+ * Sets the palette.
+ * @param startEntry the first colour entry to set
+ * @param noEntries the number of colour entries to set
+ * @param colourTable the new colour entries
+ * @param fadeNow whether to perform the change immediately or delayed
+ */
+
+void Screen::setPalette(int16 startEntry, int16 noEntries, byte *colourTable, uint8 fadeNow) {
+ assert(noEntries > 0);
+
+ memcpy(&_palette[4 * startEntry], colourTable, noEntries * 4);
+
+ if (fadeNow == RDPAL_INSTANT) {
+ _vm->_system->setPalette(_palette, startEntry, noEntries);
+ setNeedFullRedraw();
+ }
+}
+
+void Screen::dimPalette() {
+ byte *p = _palette;
+
+ for (int i = 0; i < 256; i++) {
+ p[i * 4 + 0] /= 2;
+ p[i * 4 + 1] /= 2;
+ p[i * 4 + 2] /= 2;
+ }
+
+ _vm->_system->setPalette(p, 0, 256);
+ setNeedFullRedraw();
+}
+
+/**
+ * Fades the palette up from black to the current palette.
+ * @param time the time it will take the palette to fade up
+ */
+
+int32 Screen::fadeUp(float time) {
+ if (getFadeStatus() != RDFADE_BLACK && getFadeStatus() != RDFADE_NONE)
+ return RDERR_FADEINCOMPLETE;
+
+ _fadeTotalTime = (int32)(time * 1000);
+ _fadeStatus = RDFADE_UP;
+ _fadeStartTime = _vm->_system->getMillis();
+
+ return RD_OK;
+}
+
+/**
+ * Fades the palette down to black from the current palette.
+ * @param time the time it will take the palette to fade down
+ */
+
+int32 Screen::fadeDown(float time) {
+ if (getFadeStatus() != RDFADE_BLACK && getFadeStatus() != RDFADE_NONE)
+ return RDERR_FADEINCOMPLETE;
+
+ _fadeTotalTime = (int32)(time * 1000);
+ _fadeStatus = RDFADE_DOWN;
+ _fadeStartTime = _vm->_system->getMillis();
+
+ return RD_OK;
+}
+
+/**
+ * Get the current fade status
+ * @return RDFADE_UP (fading up), RDFADE_DOWN (fading down), RDFADE_NONE
+ * (not faded), or RDFADE_BLACK (completely faded down)
+ */
+
+uint8 Screen::getFadeStatus() {
+ return _fadeStatus;
+}
+
+void Screen::waitForFade() {
+ while (getFadeStatus() != RDFADE_NONE && getFadeStatus() != RDFADE_BLACK && !_vm->_quit) {
+ updateDisplay();
+ _vm->_system->delayMillis(20);
+ }
+}
+
+void Screen::fadeServer() {
+ static int32 previousTime = 0;
+ byte fadePalette[256 * 4];
+ byte *newPalette = fadePalette;
+ int32 currentTime;
+ int16 fadeMultiplier;
+ int16 i;
+
+ // If we're not in the process of fading, do nothing.
+ if (getFadeStatus() != RDFADE_UP && getFadeStatus() != RDFADE_DOWN)
+ return;
+
+ // I don't know if this is necessary, but let's limit how often the
+ // palette is updated, just to be safe.
+ currentTime = _vm->_system->getMillis();
+ if (currentTime - previousTime <= 25)
+ return;
+
+ previousTime = currentTime;
+
+ if (getFadeStatus() == RDFADE_UP) {
+ if (currentTime >= _fadeStartTime + _fadeTotalTime) {
+ _fadeStatus = RDFADE_NONE;
+ newPalette = _palette;
+ } else {
+ fadeMultiplier = (int16)(((int32)(currentTime - _fadeStartTime) * 256) / _fadeTotalTime);
+ for (i = 0; i < 256; i++) {
+ newPalette[i * 4 + 0] = (_palette[i * 4 + 0] * fadeMultiplier) >> 8;
+ newPalette[i * 4 + 1] = (_palette[i * 4 + 1] * fadeMultiplier) >> 8;
+ newPalette[i * 4 + 2] = (_palette[i * 4 + 2] * fadeMultiplier) >> 8;
+ }
+ }
+ } else {
+ if (currentTime >= _fadeStartTime + _fadeTotalTime) {
+ _fadeStatus = RDFADE_BLACK;
+ memset(newPalette, 0, sizeof(fadePalette));
+ } else {
+ fadeMultiplier = (int16)(((int32)(_fadeTotalTime - (currentTime - _fadeStartTime)) * 256) / _fadeTotalTime);
+ for (i = 0; i < 256; i++) {
+ newPalette[i * 4 + 0] = (_palette[i * 4 + 0] * fadeMultiplier) >> 8;
+ newPalette[i * 4 + 1] = (_palette[i * 4 + 1] * fadeMultiplier) >> 8;
+ newPalette[i * 4 + 2] = (_palette[i * 4 + 2] * fadeMultiplier) >> 8;
+ }
+ }
+ }
+
+ _vm->_system->setPalette(newPalette, 0, 256);
+ setNeedFullRedraw();
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/protocol.cpp b/engines/sword2/protocol.cpp
new file mode 100644
index 0000000000..23010e27a9
--- /dev/null
+++ b/engines/sword2/protocol.cpp
@@ -0,0 +1,216 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/resman.h"
+
+namespace Sword2 {
+
+/**
+ * Returns a pointer to the first palette entry, given the pointer to the start
+ * of the screen file.
+ */
+
+byte *Sword2Engine::fetchPalette(byte *screenFile) {
+ MultiScreenHeader mscreenHeader;
+
+ mscreenHeader.read(screenFile + ResHeader::size());
+
+ byte *palette = screenFile + ResHeader::size() + mscreenHeader.palette;
+
+ // Always set colour 0 to black, because while most background screen
+ // palettes have a bright colour 0 it should come out as black in the
+ // game.
+
+ palette[0] = 0;
+ palette[1] = 0;
+ palette[2] = 0;
+ palette[3] = 0;
+
+ return palette;
+}
+
+/**
+ * Returns a pointer to the start of the palette match table, given the pointer
+ * to the start of the screen file.
+ */
+
+byte *Sword2Engine::fetchPaletteMatchTable(byte *screenFile) {
+ MultiScreenHeader mscreenHeader;
+
+ mscreenHeader.read(screenFile + ResHeader::size());
+
+ return screenFile + ResHeader::size() + mscreenHeader.paletteTable;
+}
+
+/**
+ * Returns a pointer to the screen header, given the pointer to the start of
+ * the screen file.
+ */
+
+byte *Sword2Engine::fetchScreenHeader(byte *screenFile) {
+ MultiScreenHeader mscreenHeader;
+
+ mscreenHeader.read(screenFile + ResHeader::size());
+
+ return screenFile + ResHeader::size() + mscreenHeader.screen;
+}
+
+/**
+ * Returns a pointer to the requested layer header, given the pointer to the
+ * start of the screen file. Drops out if the requested layer number exceeds
+ * the number of layers on this screen.
+ */
+
+byte *Sword2Engine::fetchLayerHeader(byte *screenFile, uint16 layerNo) {
+#ifdef SWORD2_DEBUG
+ ScreenHeader screenHead;
+
+ screenHead.read(fetchScreenHeader(screenFile));
+ assert(layerNo < screenHead.noLayers);
+#endif
+
+ MultiScreenHeader mscreenHeader;
+
+ mscreenHeader.read(screenFile + ResHeader::size());
+
+ return screenFile + ResHeader::size() + mscreenHeader.layers + layerNo * LayerHeader::size();
+}
+
+/**
+ * Returns a pointer to the start of the shading mask, given the pointer to the
+ * start of the screen file.
+ */
+
+byte *Sword2Engine::fetchShadingMask(byte *screenFile) {
+ MultiScreenHeader mscreenHeader;
+
+ mscreenHeader.read(screenFile + ResHeader::size());
+
+ return screenFile + ResHeader::size() + mscreenHeader.maskOffset;
+}
+
+/**
+ * Returns a pointer to the anim header, given the pointer to the start of the
+ * anim file.
+ */
+
+byte *Sword2Engine::fetchAnimHeader(byte *animFile) {
+ return animFile + ResHeader::size();
+}
+
+/**
+ * Returns a pointer to the requested frame number's cdtEntry, given the
+ * pointer to the start of the anim file. Drops out if the requested frame
+ * number exceeds the number of frames in this anim.
+ */
+
+byte *Sword2Engine::fetchCdtEntry(byte *animFile, uint16 frameNo) {
+#ifdef SWORD2_DEBUG
+ AnimHeader animHead;
+
+ animHead.read(fetchAnimHeader(animFile));
+
+ if (frameNo > animHead->noAnimFrames - 1)
+ error("fetchCdtEntry(animFile,%d) - anim only %d frames", frameNo, animHead.noAnimFrames);
+#endif
+
+ return fetchAnimHeader(animFile) + AnimHeader::size() + frameNo * CdtEntry::size();
+}
+
+/**
+ * Returns a pointer to the requested frame number's header, given the pointer
+ * to the start of the anim file. Drops out if the requested frame number
+ * exceeds the number of frames in this anim
+ */
+
+byte *Sword2Engine::fetchFrameHeader(byte *animFile, uint16 frameNo) {
+ // required address = (address of the start of the anim header) + frameOffset
+ CdtEntry cdt;
+
+ cdt.read(fetchCdtEntry(animFile, frameNo));
+
+ return animFile + ResHeader::size() + cdt.frameOffset;
+}
+
+/**
+ * Returns a pointer to the requested parallax layer data.
+ */
+
+byte *Sword2Engine::fetchBackgroundParallaxLayer(byte *screenFile, int layer) {
+ MultiScreenHeader mscreenHeader;
+
+ mscreenHeader.read(screenFile + ResHeader::size());
+ assert(mscreenHeader.bg_parallax[layer]);
+
+ return screenFile + ResHeader::size() + mscreenHeader.bg_parallax[layer];
+}
+
+byte *Sword2Engine::fetchBackgroundLayer(byte *screenFile) {
+ MultiScreenHeader mscreenHeader;
+
+ mscreenHeader.read(screenFile + ResHeader::size());
+ assert(mscreenHeader.screen);
+
+ return screenFile + ResHeader::size() + mscreenHeader.screen + ScreenHeader::size();
+}
+
+byte *Sword2Engine::fetchForegroundParallaxLayer(byte *screenFile, int layer) {
+ MultiScreenHeader mscreenHeader;
+
+ mscreenHeader.read(screenFile + ResHeader::size());
+ assert(mscreenHeader.fg_parallax[layer]);
+
+ return screenFile + ResHeader::size() + mscreenHeader.fg_parallax[layer];
+}
+
+byte *Sword2Engine::fetchTextLine(byte *file, uint32 text_line) {
+ TextHeader text_header;
+ static byte errorLine[128];
+
+ text_header.read(file + ResHeader::size());
+
+ if (text_line >= text_header.noOfLines) {
+ sprintf((char *)errorLine, "xxMissing line %d of %s (only 0..%d)", text_line, _resman->fetchName(file), text_header.noOfLines - 1);
+
+ // first 2 chars are NULL so that actor-number comes out as '0'
+ errorLine[0] = 0;
+ errorLine[1] = 0;
+ return errorLine;
+ }
+
+ // The "number of lines" field is followed by a lookup table
+
+ return file + READ_LE_UINT32(file + ResHeader::size() + 4 + 4 * text_line);
+}
+
+// Used for testing text & speech (see fnISpeak in speech.cpp)
+
+bool Sword2Engine::checkTextLine(byte *file, uint32 text_line) {
+ TextHeader text_header;
+
+ text_header.read(file + ResHeader::size());
+
+ return text_line < text_header.noOfLines;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/rdwin.cpp b/engines/sword2/rdwin.cpp
new file mode 100644
index 0000000000..310b66abf7
--- /dev/null
+++ b/engines/sword2/rdwin.cpp
@@ -0,0 +1,118 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "sword2/sword2.h"
+
+namespace Sword2 {
+
+/**
+ * Tell updateDisplay() that the scene needs to be completely updated.
+ */
+
+void Screen::setNeedFullRedraw() {
+ _needFullRedraw = true;
+}
+
+/**
+ * Mark an area of the screen as dirty, first generation.
+ */
+
+void Screen::markAsDirty(int16 x0, int16 y0, int16 x1, int16 y1) {
+ int16 gridX0 = x0 / CELLWIDE;
+ int16 gridY0 = y0 / CELLDEEP;
+ int16 gridX1 = x1 / CELLWIDE;
+ int16 gridY1 = y1 / CELLDEEP;
+
+ for (int16 i = gridY0; i <= gridY1; i++)
+ for (int16 j = gridX0; j <= gridX1; j++)
+ _dirtyGrid[i * _gridWide + j] = 2;
+}
+
+/**
+ * This function has two purposes: It redraws the scene, and it handles input
+ * events, palette fading, etc. It should be called at a high rate (> 20 per
+ * second), but the scene is usually only redrawn about 12 times per second,
+ * except when then screen is scrolling.
+ *
+ * @param redrawScene If true, redraw the scene.
+ */
+
+void Screen::updateDisplay(bool redrawScene) {
+ _vm->parseInputEvents();
+ fadeServer();
+
+ if (redrawScene) {
+ int i;
+
+ // Note that the entire scene is always rendered, which is less
+ // than optimal, but at least we can try to be intelligent
+ // about updating the screen afterwards.
+
+ if (_needFullRedraw) {
+ // Update the entire screen. This is necessary when
+ // scrolling, fading, etc.
+
+ _vm->_system->copyRectToScreen(_buffer + MENUDEEP * _screenWide, _screenWide, 0, MENUDEEP, _screenWide, _screenDeep - 2 * MENUDEEP);
+ _needFullRedraw = false;
+ } else {
+ // Update only the dirty areas of the screen
+
+ int j, x, y;
+ int stripWide;
+
+ for (i = 0; i < _gridDeep; i++) {
+ stripWide = 0;
+
+ for (j = 0; j < _gridWide; j++) {
+ if (_dirtyGrid[i * _gridWide + j]) {
+ stripWide++;
+ } else if (stripWide) {
+ x = CELLWIDE * (j - stripWide);
+ y = CELLDEEP * i;
+ _vm->_system->copyRectToScreen(_buffer + y * _screenWide + x, _screenWide, x, y, stripWide * CELLWIDE, CELLDEEP);
+ stripWide = 0;
+ }
+ }
+
+ if (stripWide) {
+ x = CELLWIDE * (j - stripWide);
+ y = CELLDEEP * i;
+ _vm->_system->copyRectToScreen(_buffer + y * _screenWide + x, _screenWide, x, y, stripWide * CELLWIDE, CELLDEEP);
+ stripWide = 0;
+ }
+ }
+ }
+
+ // Age the dirty cells one generation. This way we keep track
+ // of both the cells that were updated this time, and the ones
+ // that were updated the last time.
+
+ for (i = 0; i < _gridWide * _gridDeep; i++)
+ _dirtyGrid[i] >>= 1;
+ }
+
+ // We always need to update because of fades, menu animations, etc.
+ _vm->_system->updateScreen();
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/render.cpp b/engines/sword2/render.cpp
new file mode 100644
index 0000000000..da60ecd6d9
--- /dev/null
+++ b/engines/sword2/render.cpp
@@ -0,0 +1,584 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+
+#include "graphics/primitives.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/build_display.h"
+
+#ifdef BACKEND_8BIT
+#include "sword2/animation.h"
+#endif
+
+namespace Sword2 {
+
+#define MILLISECSPERCYCLE 83
+#define RENDERAVERAGETOTAL 4
+
+void Screen::updateRect(Common::Rect *r) {
+ _vm->_system->copyRectToScreen(_buffer + r->top * _screenWide + r->left,
+ _screenWide, r->left, r->top, r->right - r->left,
+ r->bottom - r->top);
+}
+
+void Screen::blitBlockSurface(BlockSurface *s, Common::Rect *r, Common::Rect *clipRect) {
+ if (!r->intersects(*clipRect))
+ return;
+
+ byte *src = s->data;
+
+ if (r->top < clipRect->top) {
+ src -= BLOCKWIDTH * (r->top - clipRect->top);
+ r->top = clipRect->top;
+ }
+ if (r->left < clipRect->left) {
+ src -= (r->left - clipRect->left);
+ r->left = clipRect->left;
+ }
+ if (r->bottom > clipRect->bottom)
+ r->bottom = clipRect->bottom;
+ if (r->right > clipRect->right)
+ r->right = clipRect->right;
+
+ byte *dst = _buffer + r->top * _screenWide + r->left;
+ int i;
+
+ if (s->transparent) {
+ for (i = 0; i < r->bottom - r->top; i++) {
+ for (int j = 0; j < r->right - r->left; j++) {
+ if (src[j])
+ dst[j] = src[j];
+ }
+ src += BLOCKWIDTH;
+ dst += _screenWide;
+ }
+ } else {
+ for (i = 0; i < r->bottom - r->top; i++) {
+ memcpy(dst, src, r->right - r->left);
+ src += BLOCKWIDTH;
+ dst += _screenWide;
+ }
+ }
+}
+
+// There are two different separate functions for scaling the image - one fast
+// and one good. Or at least that's the theory. I'm sure there are better ways
+// to scale an image than this. The latter is used at the highest graphics
+// quality setting. Note that the "good" scaler takes an extra parameter, a
+// pointer to the area of the screen where the sprite will be drawn.
+//
+// This code isn't quite like the original DrawSprite(), but should be close
+// enough.
+
+void Screen::scaleImageFast(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight) {
+ int x, y;
+
+ for (x = 0; x < dstWidth; x++)
+ _xScale[x] = (x * srcWidth) / dstWidth;
+
+ for (y = 0; y < dstHeight; y++)
+ _yScale[y] = (y * srcHeight) / dstHeight;
+
+ for (y = 0; y < dstHeight; y++) {
+ for (x = 0; x < dstWidth; x++) {
+ dst[x] = src[_yScale[y] * srcPitch + _xScale[x]];
+ }
+ dst += dstPitch;
+ }
+}
+
+void Screen::scaleImageGood(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight, byte *backbuf) {
+ for (int y = 0; y < dstHeight; y++) {
+ for (int x = 0; x < dstWidth; x++) {
+ uint8 c1, c2, c3, c4;
+
+ uint32 xPos = (x * srcWidth) / dstWidth;
+ uint32 yPos = (y * srcHeight) / dstHeight;
+ uint32 xFrac = dstWidth - (x * srcWidth) % dstWidth;
+ uint32 yFrac = dstHeight - (y * srcHeight) % dstHeight;
+
+ byte *srcPtr = src + yPos * srcPitch + xPos;
+ byte *backPtr = backbuf + y * _screenWide + x;
+
+ bool transparent = true;
+
+ if (*srcPtr) {
+ c1 = *srcPtr;
+ transparent = false;
+ } else
+ c1 = *backPtr;
+
+ if (x < dstWidth - 1) {
+ if (*(srcPtr + 1)) {
+ c2 = *(srcPtr + 1);
+ transparent = false;
+ } else
+ c2 = *(backPtr + 1);
+ } else
+ c2 = c1;
+
+ if (y < dstHeight - 1) {
+ if (*(srcPtr + srcPitch)) {
+ c3 = *(srcPtr + srcPitch);
+ transparent = false;
+ } else
+ c3 = *(backPtr + _screenWide);
+ } else
+ c3 = c1;
+
+ if (x < dstWidth - 1 && y < dstHeight - 1) {
+ if (*(srcPtr + srcPitch + 1)) {
+ c4 = *(srcPtr + srcPitch + 1);
+ transparent = false;
+ } else
+ c4 = *(backPtr + _screenWide + 1);
+ } else
+ c4 = c3;
+
+ if (!transparent) {
+ uint32 r1 = _palette[c1 * 4 + 0];
+ uint32 g1 = _palette[c1 * 4 + 1];
+ uint32 b1 = _palette[c1 * 4 + 2];
+
+ uint32 r2 = _palette[c2 * 4 + 0];
+ uint32 g2 = _palette[c2 * 4 + 1];
+ uint32 b2 = _palette[c2 * 4 + 2];
+
+ uint32 r3 = _palette[c3 * 4 + 0];
+ uint32 g3 = _palette[c3 * 4 + 1];
+ uint32 b3 = _palette[c3 * 4 + 2];
+
+ uint32 r4 = _palette[c4 * 4 + 0];
+ uint32 g4 = _palette[c4 * 4 + 1];
+ uint32 b4 = _palette[c4 * 4 + 2];
+
+ uint32 r5 = (r1 * xFrac + r2 * (dstWidth - xFrac)) / dstWidth;
+ uint32 g5 = (g1 * xFrac + g2 * (dstWidth - xFrac)) / dstWidth;
+ uint32 b5 = (b1 * xFrac + b2 * (dstWidth - xFrac)) / dstWidth;
+
+ uint32 r6 = (r3 * xFrac + r4 * (dstWidth - xFrac)) / dstWidth;
+ uint32 g6 = (g3 * xFrac + g4 * (dstWidth - xFrac)) / dstWidth;
+ uint32 b6 = (b3 * xFrac + b4 * (dstWidth - xFrac)) / dstWidth;
+
+ uint32 r = (r5 * yFrac + r6 * (dstHeight - yFrac)) / dstHeight;
+ uint32 g = (g5 * yFrac + g6 * (dstHeight - yFrac)) / dstHeight;
+ uint32 b = (b5 * yFrac + b6 * (dstHeight - yFrac)) / dstHeight;
+
+ dst[y * dstWidth + x] = quickMatch(r, g, b);
+ } else
+ dst[y * dstWidth + x] = 0;
+ }
+ }
+}
+
+/**
+ * Plots a point relative to the top left corner of the screen. This is only
+ * used for debugging.
+ * @param x x-coordinate of the point
+ * @param y y-coordinate of the point
+ * @param colour colour of the point
+ */
+
+void Screen::plotPoint(int x, int y, uint8 colour) {
+ byte *buf = _buffer + MENUDEEP * RENDERWIDE;
+
+ x -= _scrollX;
+ y -= _scrollY;
+
+ if (x >= 0 && x < RENDERWIDE && y >= 0 && y < RENDERDEEP) {
+ buf[y * RENDERWIDE + x] = colour;
+ markAsDirty(x, y + MENUDEEP, x, y + MENUDEEP);
+ }
+}
+
+static void plot(int x, int y, int colour, void *data) {
+ Screen *screen = (Screen *)data;
+ screen->plotPoint(x, y, (uint8) colour);
+}
+
+/**
+ * Draws a line from one point to another. This is only used for debugging.
+ * @param x0 x-coordinate of the start point
+ * @param y0 y-coordinate of the start point
+ * @param x1 x-coordinate of the end point
+ * @param y1 y-coordinate of the end point
+ * @param colour colour of the line
+ */
+
+void Screen::drawLine(int x0, int y0, int x1, int y1, uint8 colour) {
+ Graphics::drawLine(x0, y0, x1, y1, colour, &plot, this);
+}
+
+/**
+ * This function tells the driver the size of the background screen for the
+ * current location.
+ * @param w width of the current location
+ * @param h height of the current location
+ */
+
+void Screen::setLocationMetrics(uint16 w, uint16 h) {
+ _locationWide = w;
+ _locationDeep = h;
+ setNeedFullRedraw();
+}
+
+/**
+ * Draws a parallax layer at the current position determined by the scroll. A
+ * parallax can be either foreground, background or the main screen.
+ */
+
+void Screen::renderParallax(byte *ptr, int16 l) {
+ Parallax p;
+ int16 x, y;
+ Common::Rect r;
+
+ p.read(ptr);
+
+ if (_locationWide == _screenWide)
+ x = 0;
+ else
+ x = ((int32)((p.w - _screenWide) * _scrollX) / (int32)(_locationWide - _screenWide));
+
+ if (_locationDeep == _screenDeep - MENUDEEP * 2)
+ y = 0;
+ else
+ y = ((int32)((p.h - (_screenDeep - MENUDEEP * 2)) * _scrollY) / (int32)(_locationDeep - (_screenDeep - MENUDEEP * 2)));
+
+ Common::Rect clipRect;
+
+ // Leave enough space for the top and bottom menues
+
+ clipRect.left = 0;
+ clipRect.right = _screenWide;
+ clipRect.top = MENUDEEP;
+ clipRect.bottom = _screenDeep - MENUDEEP;
+
+ for (int j = 0; j < _yBlocks[l]; j++) {
+ for (int i = 0; i < _xBlocks[l]; i++) {
+ if (_blockSurfaces[l][i + j * _xBlocks[l]]) {
+ r.left = i * BLOCKWIDTH - x;
+ r.right = r.left + BLOCKWIDTH;
+ r.top = j * BLOCKHEIGHT - y + MENUDEEP;
+ r.bottom = r.top + BLOCKHEIGHT;
+ blitBlockSurface(_blockSurfaces[l][i + j * _xBlocks[l]], &r, &clipRect);
+ }
+ }
+ }
+
+ _parallaxScrollX = _scrollX - x;
+ _parallaxScrollY = _scrollY - y;
+}
+
+// Uncomment this when benchmarking the drawing routines.
+#define LIMIT_FRAME_RATE
+
+/**
+ * Initialises the timers before the render loop is entered.
+ */
+
+void Screen::initialiseRenderCycle() {
+ _initialTime = _vm->_system->getMillis();
+ _totalTime = _initialTime + MILLISECSPERCYCLE;
+}
+
+/**
+ * This function should be called when the game engine is ready to start the
+ * render cycle.
+ */
+
+void Screen::startRenderCycle() {
+ _scrollXOld = _scrollX;
+ _scrollYOld = _scrollY;
+
+ _startTime = _vm->_system->getMillis();
+
+ if (_startTime + _renderAverageTime >= _totalTime) {
+ _scrollX = _scrollXTarget;
+ _scrollY = _scrollYTarget;
+ _renderTooSlow = true;
+ } else {
+ _scrollX = (int16)(_scrollXOld + ((_scrollXTarget - _scrollXOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
+ _scrollY = (int16)(_scrollYOld + ((_scrollYTarget - _scrollYOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
+ _renderTooSlow = false;
+ }
+
+ if (_scrollXOld != _scrollX || _scrollYOld != _scrollY)
+ setNeedFullRedraw();
+
+ _framesPerGameCycle = 0;
+}
+
+/**
+ * This function should be called at the end of the render cycle.
+ * @return true if the render cycle is to be terminated,
+ * or false if it should continue
+ */
+
+bool Screen::endRenderCycle() {
+ static int32 renderTimeLog[4] = { 60, 60, 60, 60 };
+ static int32 renderCountIndex = 0;
+ int32 time;
+
+ time = _vm->_system->getMillis();
+ renderTimeLog[renderCountIndex] = time - _startTime;
+ _startTime = time;
+ _renderAverageTime = (renderTimeLog[0] + renderTimeLog[1] + renderTimeLog[2] + renderTimeLog[3]) >> 2;
+
+ _framesPerGameCycle++;
+
+ if (++renderCountIndex == RENDERAVERAGETOTAL)
+ renderCountIndex = 0;
+
+ if (_renderTooSlow) {
+ initialiseRenderCycle();
+ return true;
+ }
+
+ if (_startTime + _renderAverageTime >= _totalTime) {
+ _totalTime += MILLISECSPERCYCLE;
+ _initialTime = time;
+ return true;
+ }
+
+#ifdef LIMIT_FRAME_RATE
+ if (_scrollXTarget == _scrollX && _scrollYTarget == _scrollY) {
+ // If we have already reached the scroll target sleep for the
+ // rest of the render cycle.
+ _vm->sleepUntil(_totalTime);
+ _initialTime = _vm->_system->getMillis();
+ _totalTime += MILLISECSPERCYCLE;
+ return true;
+ }
+#endif
+
+ // This is an attempt to ensure that we always reach the scroll target.
+ // Otherwise the game frequently tries to pump out new interpolation
+ // frames without ever getting anywhere.
+
+ if (ABS(_scrollX - _scrollXTarget) <= 1 && ABS(_scrollY - _scrollYTarget) <= 1) {
+ _scrollX = _scrollXTarget;
+ _scrollY = _scrollYTarget;
+ } else {
+ _scrollX = (int16)(_scrollXOld + ((_scrollXTarget - _scrollXOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
+ _scrollY = (int16)(_scrollYOld + ((_scrollYTarget - _scrollYOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
+ }
+
+ if (_scrollX != _scrollXOld || _scrollY != _scrollYOld)
+ setNeedFullRedraw();
+
+#ifdef LIMIT_FRAME_RATE
+ // Give the other threads some breathing space. This apparently helps
+ // against bug #875683, though I was never able to reproduce it for
+ // myself.
+ _vm->_system->delayMillis(10);
+#endif
+
+ return false;
+}
+
+/**
+ * Reset scrolling stuff. This function is called from initBackground()
+ */
+
+void Screen::resetRenderEngine() {
+ _parallaxScrollX = 0;
+ _parallaxScrollY = 0;
+ _scrollX = 0;
+ _scrollY = 0;
+}
+
+/**
+ * This function should be called five times with either the parallax layer
+ * or a NULL pointer in order of background parallax to foreground parallax.
+ */
+
+int32 Screen::initialiseBackgroundLayer(byte *parallax) {
+ Parallax p;
+ uint16 i, j, k;
+ byte *data;
+ byte *dst;
+
+ debug(2, "initialiseBackgroundLayer");
+
+ assert(_layer < MAXLAYERS);
+
+ if (!parallax) {
+ _layer++;
+ return RD_OK;
+ }
+
+ p.read(parallax);
+
+ _xBlocks[_layer] = (p.w + BLOCKWIDTH - 1) / BLOCKWIDTH;
+ _yBlocks[_layer] = (p.h + BLOCKHEIGHT - 1) / BLOCKHEIGHT;
+
+ _blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
+ if (!_blockSurfaces[_layer])
+ return RDERR_OUTOFMEMORY;
+
+ // Decode the parallax layer into a large chunk of memory
+
+ byte *memchunk = (byte *)calloc(_xBlocks[_layer] * _yBlocks[_layer], BLOCKWIDTH * BLOCKHEIGHT);
+ if (!memchunk)
+ return RDERR_OUTOFMEMORY;
+
+ for (i = 0; i < p.h; i++) {
+ uint32 p_offset = READ_LE_UINT32(parallax + Parallax::size() + 4 * i);
+
+ if (!p_offset)
+ continue;
+
+ byte *pLine = parallax + p_offset;
+ uint16 packets = READ_LE_UINT16(pLine);
+ uint16 offset = READ_LE_UINT16(pLine + 2);
+
+ data = pLine + 4;
+ dst = memchunk + i * p.w + offset;
+
+ if (!packets) {
+ memcpy(dst, data, p.w);
+ continue;
+ }
+
+ bool zeros = false;
+
+ for (j = 0; j < packets; j++) {
+ if (zeros) {
+ dst += *data;
+ offset += *data;
+ data++;
+ zeros = false;
+ } else if (!*data) {
+ data++;
+ zeros = true;
+ } else {
+ uint16 count = *data++;
+ memcpy(dst, data, count);
+ data += count;
+ dst += count;
+ offset += count;
+ zeros = true;
+ }
+ }
+ }
+
+ // The large memory chunk is now divided into a number of smaller
+ // surfaces. For most parallax layers, we'll end up using less memory
+ // this way, and it will be faster to draw since completely transparent
+ // surfaces are discarded.
+
+ for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
+ bool block_has_data = false;
+ bool block_is_transparent = false;
+
+ int x = BLOCKWIDTH * (i % _xBlocks[_layer]);
+ int y = BLOCKHEIGHT * (i / _xBlocks[_layer]);
+
+ data = memchunk + p.w * y + x;
+
+ for (j = 0; j < BLOCKHEIGHT; j++) {
+ for (k = 0; k < BLOCKWIDTH; k++) {
+ if (x + k < p.w && y + j < p.h) {
+ if (data[j * p.w + k])
+ block_has_data = true;
+ else
+ block_is_transparent = true;
+ }
+ }
+ }
+
+ // Only assign a surface to the block if it contains data.
+
+ if (block_has_data) {
+ _blockSurfaces[_layer][i] = (BlockSurface *)malloc(sizeof(BlockSurface));
+
+ // Copy the data into the surfaces.
+ dst = _blockSurfaces[_layer][i]->data;
+ for (j = 0; j < BLOCKHEIGHT; j++) {
+ memcpy(dst, data, BLOCKWIDTH);
+ data += p.w;
+ dst += BLOCKWIDTH;
+ }
+
+ _blockSurfaces[_layer][i]->transparent = block_is_transparent;
+
+ } else
+ _blockSurfaces[_layer][i] = NULL;
+ }
+
+ free(memchunk);
+ _layer++;
+
+ return RD_OK;
+}
+
+/**
+ * Should be called once after leaving the room to free up memory.
+ */
+
+void Screen::closeBackgroundLayer() {
+ debug(2, "CloseBackgroundLayer");
+
+ for (int i = 0; i < MAXLAYERS; i++) {
+ if (_blockSurfaces[i]) {
+ for (int j = 0; j < _xBlocks[i] * _yBlocks[i]; j++)
+ if (_blockSurfaces[i][j])
+ free(_blockSurfaces[i][j]);
+ free(_blockSurfaces[i]);
+ _blockSurfaces[i] = NULL;
+ }
+ }
+
+ _layer = 0;
+}
+
+#ifdef BACKEND_8BIT
+void Screen::plotYUV(byte *lut, int width, int height, byte *const *dat) {
+ byte *buf = _buffer + ((480 - height) / 2) * RENDERWIDE + (640 - width) / 2;
+
+ int x, y;
+
+ int ypos = 0;
+ int cpos = 0;
+ int linepos = 0;
+
+ for (y = 0; y < height; y += 2) {
+ for (x = 0; x < width; x += 2) {
+ int i = ((((dat[2][cpos] + ROUNDADD) >> SHIFT) * (BITDEPTH + 1)) + ((dat[1][cpos] + ROUNDADD) >> SHIFT)) * (BITDEPTH + 1);
+ cpos++;
+
+ buf[linepos ] = lut[i + ((dat[0][ ypos ] + ROUNDADD) >> SHIFT)];
+ buf[RENDERWIDE + linepos++] = lut[i + ((dat[0][width + ypos++] + ROUNDADD) >> SHIFT)];
+ buf[linepos ] = lut[i + ((dat[0][ ypos ] + ROUNDADD) >> SHIFT)];
+ buf[RENDERWIDE + linepos++] = lut[i + ((dat[0][width + ypos++] + ROUNDADD) >> SHIFT)];
+ }
+ linepos += (2 * RENDERWIDE - width);
+ ypos += width;
+ }
+}
+#endif
+
+
+} // End of namespace Sword2
diff --git a/engines/sword2/resman.cpp b/engines/sword2/resman.cpp
new file mode 100644
index 0000000000..7387dc8f50
--- /dev/null
+++ b/engines/sword2/resman.cpp
@@ -0,0 +1,633 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/console.h"
+#include "sword2/logic.h"
+#include "sword2/memory.h"
+#include "sword2/resman.h"
+#include "sword2/router.h"
+#include "sword2/sound.h"
+
+#define Debug_Printf _vm->_debugger->DebugPrintf
+
+namespace Sword2 {
+
+// Welcome to the easy resource manager - written in simple code for easy
+// maintenance
+//
+// The resource compiler will create two files
+//
+// resource.inf which is a list of ascii cluster file names
+// resource.tab which is a table which tells us which cluster a resource
+// is located in and the number within the cluster
+
+enum {
+ BOTH = 0x0, // Cluster is on both CDs
+ CD1 = 0x1, // Cluster is on CD1 only
+ CD2 = 0x2, // Cluster is on CD2 only
+ LOCAL_CACHE = 0x4, // Cluster is cached on HDD
+ LOCAL_PERM = 0x8 // Cluster is on HDD.
+};
+
+struct CdInf {
+ uint8 clusterName[20]; // Null terminated cluster name.
+ uint8 cd; // Cd cluster is on and whether it is on the local drive or not.
+};
+
+ResourceManager::ResourceManager(Sword2Engine *vm) {
+ _vm = vm;
+
+ // Until proven differently, assume we're on CD 1. This is so the start
+ // dialog will be able to play any music at all.
+ setCD(1);
+
+ // We read in the resource info which tells us the names of the
+ // resource cluster files ultimately, although there might be groups
+ // within the clusters at this point it makes no difference. We only
+ // wish to know what resource files there are and what is in each
+
+ Common::File file;
+ uint32 size;
+ byte *temp;
+
+ _totalClusters = 0;
+ _resConvTable = NULL;
+
+ if (!file.open("resource.inf"))
+ error("Cannot open resource.inf");
+
+ size = file.size();
+
+ // Get some space for the incoming resource file - soon to be trashed
+ temp = (byte *)malloc(size);
+
+ if (file.read(temp, size) != size) {
+ file.close();
+ error("init cannot *READ* resource.inf");
+ }
+
+ file.close();
+
+ // Ok, we've loaded in the resource.inf file which contains a list of
+ // all the files now extract the filenames.
+
+ // Using this method the Gode generated resource.inf must have #0d0a on
+ // the last entry
+
+ uint32 i = 0;
+ uint32 j = 0;
+
+ do {
+ // item must have an #0d0a
+ while (temp[i] != 13) {
+ _resFiles[_totalClusters].fileName[j] = temp[i];
+ i++;
+ j++;
+ }
+
+ // NULL terminate our extracted string
+ _resFiles[_totalClusters].fileName[j] = '\0';
+ _resFiles[_totalClusters].numEntries = -1;
+ _resFiles[_totalClusters].entryTab = NULL;
+
+ // Reset position in current slot between entries, skip the
+ // 0x0a in the source and increase the number of clusters.
+
+ j = 0;
+ i += 2;
+ _totalClusters++;
+
+ // TODO: put overload check here
+ } while (i != size);
+
+ free(temp);
+
+ // Now load in the binary id to res conversion table
+ if (!file.open("resource.tab"))
+ error("Cannot open resource.tab");
+
+ // Find how many resources
+ size = file.size();
+
+ _totalResFiles = size / 4;
+
+ // Table seems ok so malloc some space
+ _resConvTable = (uint16 *)malloc(size);
+
+ for (i = 0; i < size / 2; i++)
+ _resConvTable[i] = file.readUint16LE();
+
+ if (file.ioFailed()) {
+ file.close();
+ error("Cannot read resource.tab");
+ }
+
+ file.close();
+
+ if (!file.open("cd.inf"))
+ error("Cannot open cd.inf");
+
+ CdInf *cdInf = new CdInf[_totalClusters];
+
+ for (i = 0; i < _totalClusters; i++) {
+ file.read(cdInf[i].clusterName, sizeof(cdInf[i].clusterName));
+
+ cdInf[i].cd = file.readByte();
+
+ if (file.ioFailed())
+ error("Cannot read cd.inf");
+
+ // It has been reported that there are two different versions
+ // of the cd.inf file: One where all clusters on CD also have
+ // the LOCAL_CACHE bit set. This bit is no longer used. To
+ // avoid future problems, let's normalize the flag once and for
+ // all here.
+
+ if (cdInf[i].cd & LOCAL_PERM)
+ cdInf[i].cd = 0;
+ else if (cdInf[i].cd & CD1)
+ cdInf[i].cd = 1;
+ else if (cdInf[i].cd & CD2)
+ cdInf[i].cd = 2;
+ else
+ cdInf[i].cd = 0;
+ }
+
+ file.close();
+
+ for (i = 0; i < _totalClusters; i++) {
+ for (j = 0; j < _totalClusters; j++) {
+ if (scumm_stricmp((char *)cdInf[j].clusterName, _resFiles[i].fileName) == 0)
+ break;
+ }
+
+ if (j == _totalClusters)
+ error("%s is not in cd.inf", _resFiles[i].fileName);
+
+ _resFiles[i].cd = cdInf[j].cd;
+ }
+
+ delete [] cdInf;
+
+ debug(1, "%d resources in %d cluster files", _totalResFiles, _totalClusters);
+ for (i = 0; i < _totalClusters; i++)
+ debug(2, "filename of cluster %d: -%s (%d)", i, _resFiles[i].fileName, _resFiles[i].cd);
+
+ _resList = (Resource *)malloc(_totalResFiles * sizeof(Resource));
+
+ for (i = 0; i < _totalResFiles; i++) {
+ _resList[i].ptr = NULL;
+ _resList[i].size = 0;
+ _resList[i].refCount = 0;
+ _resList[i].prev = _resList[i].next = NULL;
+ }
+ _cacheStart = _cacheEnd = NULL;
+ _usedMem = 0;
+}
+
+ResourceManager::~ResourceManager() {
+ Resource *res = _cacheStart;
+ while (res) {
+ _vm->_memory->memFree(res->ptr);
+ res = res->next;
+ }
+ for (uint i = 0; i < _totalClusters; i++)
+ free(_resFiles[i].entryTab);
+ free(_resList);
+ free(_resConvTable);
+}
+
+/**
+ * Returns the address of a resource. Loads if not in memory. Retains a count.
+ */
+
+byte *ResourceManager::openResource(uint32 res, bool dump) {
+ assert(res < _totalResFiles);
+
+ // Is the resource in memory already? If not, load it.
+
+ if (!_resList[res].ptr) {
+ // Fetch the correct file and read in the correct portion.
+ uint16 cluFileNum = _resConvTable[res * 2]; // points to the number of the ascii filename
+ assert(cluFileNum != 0xffff);
+
+ // Relative resource within the file
+ // First we have to find the file via the _resConvTable
+ uint16 actual_res = _resConvTable[(res * 2) + 1];
+
+ debug(5, "openResource %s res %d", _resFiles[cluFileNum].fileName, res);
+
+ // If we're loading a cluster that's only available from one
+ // of the CDs, remember which one so that we can play the
+ // correct speech and music.
+
+ setCD(_resFiles[cluFileNum].cd);
+
+ // Actually, as long as the file can be found we don't really
+ // care which CD it's on. But if we can't find it, keep asking
+ // for the CD until we do.
+
+ Common::File *file = openCluFile(cluFileNum);
+
+ if (_resFiles[cluFileNum].entryTab == NULL) {
+ // we didn't read from this file before, get its index table
+ readCluIndex(cluFileNum, file);
+ }
+
+ uint32 pos = _resFiles[cluFileNum].entryTab[actual_res * 2 + 0];
+ uint32 len = _resFiles[cluFileNum].entryTab[actual_res * 2 + 1];
+
+ file->seek(pos, SEEK_SET);
+
+ debug(6, "res len %d", len);
+
+ // Ok, we know the length so try and allocate the memory.
+ _resList[res].ptr = _vm->_memory->memAlloc(len, res);
+ _resList[res].size = len;
+ _resList[res].refCount = 0;
+
+ file->read(_resList[res].ptr, len);
+
+ debug(3, "Loaded resource '%s' from '%s' on CD %d (%d)", fetchName(_resList[res].ptr), _resFiles[cluFileNum].fileName, getCD(), _resFiles[cluFileNum].cd);
+
+ if (dump) {
+ char buf[256];
+ const char *tag;
+ Common::File out;
+
+ switch (fetchType(_resList[res].ptr)) {
+ case ANIMATION_FILE:
+ tag = "anim";
+ break;
+ case SCREEN_FILE:
+ tag = "layer";
+ break;
+ case GAME_OBJECT:
+ tag = "object";
+ break;
+ case WALK_GRID_FILE:
+ tag = "walkgrid";
+ break;
+ case GLOBAL_VAR_FILE:
+ tag = "globals";
+ break;
+ case PARALLAX_FILE_null:
+ tag = "parallax"; // Not used!
+ break;
+ case RUN_LIST:
+ tag = "runlist";
+ break;
+ case TEXT_FILE:
+ tag = "text";
+ break;
+ case SCREEN_MANAGER:
+ tag = "screen";
+ break;
+ case MOUSE_FILE:
+ tag = "mouse";
+ break;
+ case WAV_FILE:
+ tag = "wav";
+ break;
+ case ICON_FILE:
+ tag = "icon";
+ break;
+ case PALETTE_FILE:
+ tag = "palette";
+ break;
+ default:
+ tag = "unknown";
+ break;
+ }
+
+#if defined(MACOS_CARBON)
+ sprintf(buf, ":dumps:%s-%d.dmp", tag, res);
+#else
+ sprintf(buf, "dumps/%s-%d.dmp", tag, res);
+#endif
+
+ if (!out.exists(buf, "")) {
+ if (out.open(buf, Common::File::kFileWriteMode, ""))
+ out.write(_resList[res].ptr, len);
+ }
+ }
+
+ // close the cluster
+ file->close();
+ delete file;
+
+ _usedMem += len;
+ checkMemUsage();
+ } else if (_resList[res].refCount == 0)
+ removeFromCacheList(_resList + res);
+
+ _resList[res].refCount++;
+
+ return _resList[res].ptr;
+}
+
+void ResourceManager::closeResource(uint32 res) {
+ assert(res < _totalResFiles);
+
+ // Don't try to close the resource if it has already been forcibly
+ // closed, e.g. by fnResetGlobals().
+
+ if (_resList[res].ptr == NULL)
+ return;
+
+ assert(_resList[res].refCount > 0);
+
+ _resList[res].refCount--;
+ if (_resList[res].refCount == 0)
+ addToCacheList(_resList + res);
+
+ // It's tempting to free the resource immediately when refCount
+ // reaches zero, but that'd be a mistake. Closing a resource does not
+ // mean "I'm not going to use this resource any more". It means that
+ // "the next time I use this resource I'm going to ask for a new
+ // pointer to it".
+ //
+ // Since the original memory manager had to deal with memory
+ // fragmentation, keeping a resource open - and thus locked down to a
+ // specific memory address - was considered a bad thing.
+}
+
+void ResourceManager::removeFromCacheList(Resource *res) {
+ if (_cacheStart == res)
+ _cacheStart = res->next;
+
+ if (_cacheEnd == res)
+ _cacheEnd = res->prev;
+
+ if (res->prev)
+ res->prev->next = res->next;
+ if (res->next)
+ res->next->prev = res->prev;
+ res->prev = res->next = NULL;
+}
+
+void ResourceManager::addToCacheList(Resource *res) {
+ res->prev = NULL;
+ res->next = _cacheStart;
+ if (_cacheStart)
+ _cacheStart->prev = res;
+ _cacheStart = res;
+ if (!_cacheEnd)
+ _cacheEnd = res;
+}
+
+Common::File *ResourceManager::openCluFile(uint16 fileNum) {
+ Common::File *file = new Common::File;
+ while (!file->open(_resFiles[fileNum].fileName)) {
+ // HACK: We have to check for this, or it'll be impossible to
+ // quit while the game is asking for the user to insert a CD.
+ // But recovering from this situation gracefully is just too
+ // much trouble, so quit now.
+ if (_vm->_quit)
+ g_system->quit();
+
+ // If the file is supposed to be on hard disk, or we're
+ // playing a demo, then we're in trouble if the file
+ // can't be found!
+
+ if ((_vm->_features & GF_DEMO) || _resFiles[fileNum].cd == 0)
+ error("Could not find '%s'", _resFiles[fileNum].fileName);
+
+ askForCD(_resFiles[fileNum].cd);
+ }
+ return file;
+}
+
+void ResourceManager::readCluIndex(uint16 fileNum, Common::File *file) {
+ if (_resFiles[fileNum].entryTab == NULL) {
+ // we didn't read from this file before, get its index table
+ if (file == NULL)
+ file = openCluFile(fileNum);
+ else
+ file->incRef();
+
+ // 1st DWORD of a cluster is an offset to the look-up table
+ uint32 table_offset = file->readUint32LE();
+ debug(6, "table offset = %d", table_offset);
+ uint32 tableSize = file->size() - table_offset; // the table is stored at the end of the file
+ file->seek(table_offset);
+
+ assert((tableSize % 8) == 0);
+ _resFiles[fileNum].entryTab = (uint32*)malloc(tableSize);
+ _resFiles[fileNum].numEntries = tableSize / 8;
+ file->read(_resFiles[fileNum].entryTab, tableSize);
+ if (file->ioFailed())
+ error("unable to read index table from file %s\n", _resFiles[fileNum].fileName);
+#ifdef SCUMM_BIG_ENDIAN
+ for (int tabCnt = 0; tabCnt < _resFiles[fileNum].numEntries * 2; tabCnt++)
+ _resFiles[fileNum].entryTab[tabCnt] = FROM_LE_32(_resFiles[fileNum].entryTab[tabCnt]);
+#endif
+ file->decRef();
+ }
+}
+
+/**
+ * Returns true if resource is valid, otherwise false.
+ */
+
+bool ResourceManager::checkValid(uint32 res) {
+ // Resource number out of range
+ if (res >= _totalResFiles)
+ return false;
+
+ // Points to the number of the ascii filename
+ uint16 parent_res_file = _resConvTable[res * 2];
+
+ // Null & void resource
+ if (parent_res_file == 0xffff)
+ return false;
+
+ return true;
+}
+
+/**
+ * Returns the total file length of a resource - i.e. all headers are included
+ * too.
+ */
+
+uint32 ResourceManager::fetchLen(uint32 res) {
+ if (_resList[res].ptr)
+ return _resList[res].size;
+
+ // Does this ever happen?
+ warning("fetchLen: Resource %u is not loaded; reading length from file", res);
+
+ // Points to the number of the ascii filename
+ uint16 parent_res_file = _resConvTable[res * 2];
+
+ // relative resource within the file
+ uint16 actual_res = _resConvTable[(res * 2) + 1];
+
+ // first we have to find the file via the _resConvTable
+ // open the cluster file
+
+ if (_resFiles[parent_res_file].entryTab == NULL) {
+ readCluIndex(parent_res_file);
+ }
+ return _resFiles[parent_res_file].entryTab[actual_res * 2 + 1];
+}
+
+void ResourceManager::checkMemUsage() {
+ while (_usedMem > MAX_MEM_CACHE) {
+ // we're using up more memory than we wanted to. free some old stuff.
+ // Newly loaded objects are added to the start of the list,
+ // we start freeing from the end, to free the oldest items first
+ if (_cacheEnd) {
+ Resource *tmp = _cacheEnd;
+ assert((tmp->refCount == 0) && (tmp->ptr) && (tmp->next == NULL));
+ removeFromCacheList(tmp);
+
+ _vm->_memory->memFree(tmp->ptr);
+ tmp->ptr = NULL;
+ _usedMem -= tmp->size;
+ } else {
+ warning("%d bytes of memory used, but cache list is empty!\n");
+ return;
+ }
+ }
+}
+
+void ResourceManager::remove(int res) {
+ if (_resList[res].ptr) {
+ removeFromCacheList(_resList + res);
+
+ _vm->_memory->memFree(_resList[res].ptr);
+ _resList[res].ptr = NULL;
+ _resList[res].refCount = 0;
+ _usedMem -= _resList[res].size;
+ }
+}
+
+/**
+ * Remove all res files from memory - ready for a total restart. This includes
+ * the player object and global variables resource.
+ */
+
+void ResourceManager::removeAll() {
+ // We need to clear the FX queue, because otherwise the sound system
+ // will still believe that the sound resources are in memory, and that
+ // it's ok to close them.
+
+ _vm->_sound->clearFxQueue();
+
+ for (uint i = 0; i < _totalResFiles; i++)
+ remove(i);
+}
+
+/**
+ * Remove all resources from memory.
+ */
+
+void ResourceManager::killAll(bool wantInfo) {
+ int nuked = 0;
+
+ // We need to clear the FX queue, because otherwise the sound system
+ // will still believe that the sound resources are in memory, and that
+ // it's ok to close them.
+
+ _vm->_sound->clearFxQueue();
+
+ for (uint i = 0; i < _totalResFiles; i++) {
+ // Don't nuke the global variables or the player object!
+ if (i == 1 || i == CUR_PLAYER_ID)
+ continue;
+
+ if (_resList[i].ptr) {
+ if (wantInfo)
+ Debug_Printf("Nuked %5d: %s\n", i, fetchName(_resList[i].ptr));
+
+ remove(i);
+ nuked++;
+ }
+ }
+
+ if (wantInfo)
+ Debug_Printf("Expelled %d resources\n", nuked);
+}
+
+/**
+ * Like killAll but only kills objects (except George & the variable table of
+ * course) - ie. forcing them to reload & restart their scripts, which
+ * simulates the effect of a save & restore, thus checking that each object's
+ * re-entrant logic works correctly, and doesn't cause a statuette to
+ * disappear forever, or some plaster-filled holes in sand to crash the game &
+ * get James in trouble again.
+ */
+
+void ResourceManager::killAllObjects(bool wantInfo) {
+ int nuked = 0;
+
+ for (uint i = 0; i < _totalResFiles; i++) {
+ // Don't nuke the global variables or the player object!
+ if (i == 1 || i == CUR_PLAYER_ID)
+ continue;
+
+ if (_resList[i].ptr) {
+ if (fetchType(_resList[i].ptr) == GAME_OBJECT) {
+ if (wantInfo)
+ Debug_Printf("Nuked %5d: %s\n", i, fetchName(_resList[i].ptr));
+
+ remove(i);
+ nuked++;
+ }
+ }
+ }
+
+ if (wantInfo)
+ Debug_Printf("Expelled %d resources\n", nuked);
+}
+
+void ResourceManager::askForCD(int cd) {
+ byte *textRes;
+
+ // Stop any music from playing - so the system no longer needs the
+ // current CD - otherwise when we take out the CD, Windows will
+ // complain!
+
+ _vm->_sound->stopMusic(true);
+
+ textRes = openResource(2283);
+ _vm->_screen->displayMsg(_vm->fetchTextLine(textRes, 5 + cd) + 2, 0);
+ closeResource(2283);
+
+ // The original code probably determined automagically when the correct
+ // CD had been inserted, but our backend doesn't support that, and
+ // anyway I don't know if all systems allow that sort of thing. So we
+ // wait for the user to press any key instead, or click the mouse.
+ //
+ // But just in case we ever try to identify the CDs by their labels,
+ // they should be:
+ //
+ // CD1: "RBSII1" (or "PCF76" for the PCF76 version, whatever that is)
+ // CD2: "RBSII2"
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/resman.h b/engines/sword2/resman.h
new file mode 100644
index 0000000000..18b5cb8765
--- /dev/null
+++ b/engines/sword2/resman.h
@@ -0,0 +1,134 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef RESMAN_H
+#define RESMAN_H
+
+namespace Common {
+ class File;
+}
+
+#define MAX_MEM_CACHE (8 * 1024 * 1024) // we keep up to 8 megs of resource data files in memory
+#define MAX_res_files 20
+
+namespace Sword2 {
+
+class Sword2Engine;
+
+struct Resource {
+ byte *ptr;
+ uint32 size;
+ uint32 refCount;
+ Resource *next, *prev;
+};
+
+struct ResourceFile {
+ char fileName[20];
+ int32 numEntries;
+ uint32 *entryTab;
+ uint8 cd;
+};
+
+class ResourceManager {
+private:
+ Common::File *openCluFile(uint16 fileNum);
+ void readCluIndex(uint16 fileNum, Common::File *file = NULL);
+ void removeFromCacheList(Resource *res);
+ void addToCacheList(Resource *res);
+ void checkMemUsage();
+
+ Sword2Engine *_vm;
+
+ int _curCD;
+ uint32 _totalResFiles;
+ uint32 _totalClusters;
+
+ // Gode generated res-id to res number/rel number conversion table
+
+ uint16 *_resConvTable;
+ ResourceFile _resFiles[MAX_res_files];
+ Resource *_resList;
+
+ Resource *_cacheStart, *_cacheEnd;
+ uint32 _usedMem; // amount of used memory in bytes
+
+public:
+ ResourceManager(Sword2Engine *vm); // read in the config file
+ ~ResourceManager();
+
+ uint32 getNumResFiles() { return _totalResFiles; }
+ uint32 getNumClusters() { return _totalClusters; }
+ ResourceFile *getResFiles() { return _resFiles; }
+ Resource *getResList() { return _resList; }
+
+ byte *openResource(uint32 res, bool dump = false);
+ void closeResource(uint32 res);
+
+ bool checkValid(uint32 res);
+ uint32 fetchLen(uint32 res);
+ uint8 fetchType(uint32 res) {
+ byte *ptr = openResource(res);
+ uint8 type = ptr[0];
+ closeResource(res);
+
+ return type;
+ }
+
+ uint8 fetchType(byte *ptr) {
+ return ptr[0];
+ }
+
+ byte *fetchName(uint32 res, byte *buf) {
+ byte *ptr = openResource(res);
+ memcpy(buf, ptr + 10, NAME_LEN);
+ closeResource(res);
+
+ return buf;
+ }
+
+ byte *fetchName(byte *ptr) {
+ return ptr + 10;
+ }
+
+ // Prompts the user for the specified CD.
+ void askForCD(int cd);
+
+ void setCD(int cd) {
+ if (cd)
+ _curCD = cd;
+ }
+
+ int getCD() {
+ return _curCD;
+ }
+
+ void remove(int res);
+ void removeAll();
+
+ // ----console commands
+
+ void killAll(bool wantInfo);
+ void killAllObjects(bool wantInfo);
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/router.cpp b/engines/sword2/router.cpp
new file mode 100644
index 0000000000..bbca609e3c
--- /dev/null
+++ b/engines/sword2/router.cpp
@@ -0,0 +1,2506 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// ---------------------------------------------------------------------------
+// ROUTER.CPP by James
+//
+// A rehash of Jeremy's original jrouter.c, containing low-level system
+// routines for calculating routes between points inside a walk-grid, and
+// constructing walk animations from mega-sets.
+//
+// jrouter.c underwent 2 major reworks from the original:
+// (1) Restructured to allow more flexibility in the mega-sets, ie. more info
+// taken from the walk-data
+// - the new George & Nico mega-sets & walk-data were then tested &
+// tweaked in the Sword1 system
+// (2) Updated for the new Sword2 system, ie. new object structures
+// - now compatible with Sword2, the essential code already having been
+// tested
+//
+// ---------------------------------------------------------------------------
+
+/****************************************************************************
+ * JROUTER.C polygon router with modular walks
+ * using a tree of modules
+ * 21 july 94
+ * 3 november 94
+ * System currently works by scanning grid data and coming up with a ROUTE
+ * as a series of way points(nodes), the smoothest eight directional PATH
+ * through these nodes is then found, and a WALK created to fit the PATH.
+ *
+ * Two funtions are called by the user, RouteFinder creates a route as a
+ * module list, HardWalk creates an animation list from the module list.
+ * The split is only provided to allow the possibility of turning the
+ * autorouter over two game cycles.
+ ****************************************************************************
+ *
+ * Routine timings on osborne 486
+ *
+ * Read floor resource (file already loaded) 112 pixels
+ *
+ * Read mega resource (file already loaded) 112 pixels
+ *
+ *
+ *
+ ****************************************************************************
+ *
+ * Modified 12 Oct 95
+ *
+ * Target Points within 1 pixel of a line are ignored ???
+ *
+ * Modules split into Points within 1 pixel of a line are ignored ???
+ *
+ ****************************************************************************
+ *
+ * TOTALLY REHASHED BY JAMES FOR NEW MEGAS USING OLD SYSTEM
+ * THEN REINCARNATED BY JAMES FOR NEW MEGAS USING NEW SYSTEM
+ *
+ ****************************************************************************/
+
+#include "common/stdafx.h"
+#include "common/stream.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/resman.h"
+#include "sword2/router.h"
+
+namespace Sword2 {
+
+//----------------------------------------------------------
+// (4) WALK-GRID FILES
+//----------------------------------------------------------
+// a walk-grid file consists of:
+//
+// standard file header
+// walk-grid file header
+// walk-grid data
+
+// Walk-Grid Header - taken directly from old "header.h" in STD_INC
+
+struct WalkGridHeader {
+ int32 numBars; // number of bars on the floor
+ int32 numNodes; // number of nodes
+};
+
+uint8 Router::returnSlotNo(uint32 megaId) {
+ if (_vm->_logic->readVar(ID) == CUR_PLAYER_ID) {
+ // George (8)
+ return 0;
+ } else {
+ // One of Nico's mega id's
+ return 1;
+ }
+}
+
+void Router::allocateRouteMem() {
+ uint8 slotNo;
+
+ // Player character always always slot 0, while the other mega
+ // (normally Nico) always uses slot 1
+ // Better this way, so that if mega object removed from memory while
+ // in middle of route, the old route will be safely cleared from
+ // memory just before they create a new one
+
+ slotNo = returnSlotNo(_vm->_logic->readVar(ID));
+
+ // if this slot is already used, then it can't be needed any more
+ // because this id is creating a new route!
+
+ if (_routeSlots[slotNo])
+ freeRouteMem();
+
+ _routeSlots[slotNo] = (WalkData *)malloc(sizeof(WalkData) * O_WALKANIM_SIZE);
+
+ // 12000 bytes were used for this in Sword1 mega compacts, based on
+ // 20 bytes per 'WalkData' frame
+ // ie. allowing for 600 frames including end-marker
+ // Now 'WalkData' is 8 bytes, so 8*600 = 4800 bytes.
+ // Note that a 600 frame walk lasts about 48 seconds!
+ // (600fps / 12.5s = 48s)
+
+ // mega keeps note of which slot contains the pointer to it's walk
+ // animation mem block
+ // +1 so that '0' can mean "not walking"
+ // megaObject->route_slot_id = slotNo + 1;
+}
+
+WalkData *Router::getRouteMem() {
+ uint8 slotNo = returnSlotNo(_vm->_logic->readVar(ID));
+
+ return (WalkData *)_routeSlots[slotNo];
+}
+
+void Router::freeRouteMem() {
+ uint8 slotNo = returnSlotNo(_vm->_logic->readVar(ID));
+
+ free(_routeSlots[slotNo]);
+ _routeSlots[slotNo] = NULL;
+}
+
+void Router::freeAllRouteMem() {
+ for (int i = 0; i < TOTAL_ROUTE_SLOTS; i++) {
+ free(_routeSlots[i]);
+ _routeSlots[i] = NULL;
+ }
+}
+
+int32 Router::routeFinder(byte *ob_mega, byte *ob_walkdata, int32 x, int32 y, int32 dir) {
+ /*********************************************************************
+ * RouteFinder.C polygon router with modular walks
+ * 21 august 94
+ * 3 november 94
+ * routeFinder creates a list of modules that enables HardWalk to
+ * create an animation list.
+ *
+ * routeFinder currently works by scanning grid data and coming up
+ * with a ROUTE as a series of way points(nodes), the smoothest eight
+ * directional PATH through these nodes is then found, this
+ * information is made available to HardWalk for a WALK to be created
+ * to fit the PATH.
+ *
+ * 30 november 94 return values modified
+ *
+ * return 0 = failed to find a route
+ *
+ * 1 = found a route
+ *
+ * 2 = mega already at target
+ *
+ *********************************************************************/
+
+ int32 routeFlag = 0;
+ int32 solidFlag = 0;
+ WalkData *walkAnim;
+
+ // megaId = id;
+
+ setUpWalkGrid(ob_mega, x, y, dir);
+ loadWalkData(ob_walkdata);
+
+ walkAnim = getRouteMem();
+
+ // All route data now loaded start finding a route
+
+ // Check if we can get a route through the floor. changed 12 Oct95 JPS
+
+ routeFlag = getRoute();
+
+ switch (routeFlag) {
+ case 2:
+ // special case for zero length route
+
+ // if target direction specified as any
+ if (_targetDir > 7)
+ _targetDir = _startDir;
+
+ // just a turn on the spot is required set an end module for
+ // the route let the animator deal with it
+ // modularPath is normally set by extractRoute
+
+ _modularPath[0].dir = _startDir;
+ _modularPath[0].num = 0;
+ _modularPath[0].x = _startX;
+ _modularPath[0].y = _startY;
+ _modularPath[1].dir = _targetDir;
+ _modularPath[1].num = 0;
+ _modularPath[1].x = _startX;
+ _modularPath[1].y = _startY;
+ _modularPath[2].dir = 9;
+ _modularPath[2].num = ROUTE_END_FLAG;
+
+ slidyWalkAnimator(walkAnim);
+ routeFlag = 2;
+ break;
+ case 1:
+ // A normal route. Convert the route to an exact path
+ smoothestPath();
+
+ // The Route had waypoints and direction options
+
+ // The Path is an exact set of lines in 8 directions that
+ // reach the target.
+
+ // The path is in module format, but steps taken in each
+ // direction are not accurate
+
+ // if target dir = 8 then the walk isn't linked to an anim so
+ // we can create a route without sliding and miss the exact
+ // target
+
+#ifndef FORCE_SLIDY
+ if (_targetDir == 8) {
+ // can end facing ANY direction (ie. exact end
+ // position not vital) - so use SOLID walk to
+ // avoid sliding to exact position
+
+ solidPath();
+ solidFlag = solidWalkAnimator(walkAnim);
+ }
+#endif
+
+ if (!solidFlag) {
+ // if we failed to create a SOLID route, do a SLIDY
+ // one instead
+
+ slidyPath();
+ slidyWalkAnimator(walkAnim);
+ }
+
+ break;
+ default:
+ // Route didn't reach target so assume point was off the floor
+ // routeFlag = 0;
+ break;
+ }
+
+ return routeFlag; // send back null route
+}
+
+int32 Router::getRoute() {
+ /*********************************************************************
+ * GetRoute.C extract a path from walk grid
+ * 12 october 94
+ *
+ * GetRoute currently works by scanning grid data and coming up with
+ * a ROUTE as a series of way points(nodes).
+ *
+ * static routeData _route[O_ROUTE_SIZE];
+ *
+ * return 0 = failed to find a route
+ *
+ * 1 = found a route
+ *
+ * 2 = mega already at target
+ *
+ * 3 = failed to find a route because target was on a line
+ *
+ *********************************************************************/
+
+ int32 routeGot = 0;
+
+ if (_startX == _targetX && _startY == _targetY)
+ routeGot = 2;
+ else {
+ // 'else' added by JEL (23jan96) otherwise 'routeGot' affected
+ // even when already set to '2' above - causing some 'turns'
+ // to walk downwards on the spot
+
+ // returns 3 if target on a line ( +- 1 pixel )
+ routeGot = checkTarget(_targetX, _targetY);
+ }
+
+ if (routeGot == 0) {
+ // still looking for a route check if target is within a pixel
+ // of a line
+
+ // scan through the nodes linking each node to its nearest
+ // neighbour until no more nodes change
+
+ // This is the routine that finds a route using scan()
+
+ int32 level = 1;
+
+ while (scan(level))
+ level++;
+
+ // Check to see if the route reached the target
+
+ if (_node[_nNodes].dist < 9999) {
+ // it did so extract the route as nodes and the
+ // directions to go between each node
+
+ routeGot = 1;
+ extractRoute();
+
+ // route.X,route.Y and route.Dir now hold all the
+ // route infomation with the target dir or route
+ // continuation
+ }
+ }
+
+ return routeGot;
+}
+
+// THE SLIDY PATH ROUTINES
+
+int32 Router::smoothestPath() {
+ // This is the second big part of the route finder and the the only
+ // bit that tries to be clever (the other bits are clever).
+ //
+ // This part of the autorouter creates a list of modules from a set of
+ // lines running across the screen. The task is complicated by two
+ // things:
+ //
+ // Firstly in choosing a route through the maze of nodes the routine
+ // tries to minimise the amount of each individual turn avoiding 90
+ // degree and greater turns (where possible) and reduces the total
+ // number of turns (subject to two 45 degree turns being better than
+ // one 90 degree turn).
+ //
+ // Secondly when walking in a given direction the number of steps
+ // required to reach the end of that run is not calculated accurately.
+ // This is because I was unable to derive a function to relate number
+ // of steps taken between two points to the shrunken step size
+
+ int i;
+ int32 steps = 0;
+ int32 lastDir;
+ int32 tempturns[4];
+ int32 turns[4];
+ int32 turntable[NO_DIRECTIONS] = { 0, 1, 3, 5, 7, 5, 3, 1 };
+
+ // route.X route.Y and route.Dir start at far end
+
+ _smoothPath[0].x = _startX;
+ _smoothPath[0].y = _startY;
+ _smoothPath[0].dir = _startDir;
+ _smoothPath[0].num = 0;
+
+ lastDir = _startDir;
+
+ // for each section of the route
+
+ for (int p = 0; p < _routeLength; p++) {
+ int32 dirS = _route[p].dirS;
+ int32 dirD = _route[p].dirD;
+ int32 nextDirS = _route[p + 1].dirS;
+ int32 nextDirD = _route[p + 1].dirD;
+
+ // Check directions into and out of a pair of nodes going in
+ int32 dS = dirS - lastDir;
+ if (dS < 0)
+ dS = dS + NO_DIRECTIONS;
+
+ int32 dD = dirD - lastDir;
+ if (dD < 0)
+ dD = dD + NO_DIRECTIONS;
+
+ // coming out
+ int32 dSS = dirS - nextDirS;
+ if (dSS < 0)
+ dSS = dSS + NO_DIRECTIONS;
+
+ int32 dDD = dirD - nextDirD;
+ if (dDD < 0)
+ dDD = dDD + NO_DIRECTIONS;
+
+ int32 dSD = dirS - nextDirD;
+ if (dSD < 0)
+ dSD = dSD + NO_DIRECTIONS;
+
+ int32 dDS = dirD - nextDirS;
+ if (dDS < 0)
+ dDS = dDS + NO_DIRECTIONS;
+
+ // Determine the amount of turning involved in each possible
+ // path
+
+ dS = turntable[dS];
+ dD = turntable[dD];
+ dSS = turntable[dSS];
+ dDD = turntable[dDD];
+ dSD = turntable[dSD];
+ dDS = turntable[dDS];
+
+ // get the best path out ie assume next section uses best
+ // direction
+
+ if (dSD < dSS)
+ dSS = dSD;
+
+ if (dDS < dDD)
+ dDD = dDS;
+
+ // Rate each option. Split routes look crap so weight against
+ // them
+
+ int32 SS = dS + dSS + 3;
+ int32 SD = dS + dDD;
+ int32 DS = dD + dSS;
+ int32 DD = dD + dDD + 3;
+
+ // set up turns as a sorted array of the turn values
+
+ tempturns[0] = SS;
+ turns[0] = 0;
+ tempturns[1] = SD;
+ turns[1] = 1;
+ tempturns[2] = DS;
+ turns[2] = 2;
+ tempturns[3] = DD;
+ turns[3] = 3;
+
+ for (i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ if (tempturns[j] > tempturns[j + 1]) {
+ SWAP(turns[j], turns[j + 1]);
+ SWAP(tempturns[j], tempturns[j + 1]);
+ }
+ }
+ }
+
+ // best option matched in order of the priority we would like
+ // to see on the screen but each option must be checked to see
+ // if it can be walked
+
+ int32 options = newCheck(1, _route[p].x, _route[p].y, _route[p + 1].x, _route[p + 1].y);
+
+ assert(options);
+
+ i = 0;
+ steps = 0;
+
+ do {
+ int32 opt = 1 << turns[i];
+ if (options & opt)
+ steps = smoothCheck(turns[i], p, dirS, dirD);
+ i++;
+ } while (steps == 0 && i < 4);
+
+ assert(steps);
+
+ // route.X route.Y route.dir and bestTurns start at far end
+ }
+
+ // best turns will end heading as near as possible to target dir rest
+ // is down to anim for now
+
+ _smoothPath[steps].dir = 9;
+ _smoothPath[steps].num = ROUTE_END_FLAG;
+ return 1;
+}
+
+int32 Router::smoothCheck(int32 best, int32 p, int32 dirS, int32 dirD) {
+ /*********************************************************************
+ * Slip sliding away
+ * This path checker checks to see if a walk that exactly follows the
+ * path would be valid. This should be inherently true for atleast one
+ * of the turn options.
+ * No longer checks the data it only creates the smoothPath array JPS
+ *********************************************************************/
+
+ static int32 k;
+ int32 dsx, dsy;
+ int32 ddx, ddy;
+ int32 ss0, ss1, ss2;
+ int32 sd0, sd1, sd2;
+
+ if (p == 0)
+ k = 1;
+
+ int32 x = _route[p].x;
+ int32 y = _route[p].y;
+ int32 x2 = _route[p + 1].x;
+ int32 y2 = _route[p + 1].y;
+ int32 ldx = x2 - x;
+ int32 ldy = y2 - y;
+ int32 dirX = 1;
+ int32 dirY = 1;
+
+ if (ldx < 0) {
+ ldx = -ldx;
+ dirX = -1;
+ }
+
+ if (ldy < 0) {
+ ldy = -ldy;
+ dirY = -1;
+ }
+
+ // set up sd0-ss2 to reflect possible movement in each direction
+
+ if (dirS == 0 || dirS == 4) { // vert and diag
+ ddx = ldx;
+ ddy = (ldx * _diagonaly) / _diagonalx;
+ dsy = ldy - ddy;
+ ddx = ddx * dirX;
+ ddy = ddy * dirY;
+ dsy = dsy * dirY;
+ dsx = 0;
+
+ sd0 = (ddx + _modX[dirD] / 2) / _modX[dirD];
+ ss0 = (dsy + _modY[dirS] / 2) / _modY[dirS];
+ sd1 = sd0 / 2;
+ ss1 = ss0 / 2;
+ sd2 = sd0 - sd1;
+ ss2 = ss0 - ss1;
+ } else {
+ ddy = ldy;
+ ddx = (ldy * _diagonalx) / _diagonaly;
+ dsx = ldx - ddx;
+ ddy = ddy * dirY;
+ ddx = ddx * dirX;
+ dsx = dsx * dirX;
+ dsy = 0;
+
+ sd0 = (ddy + _modY[dirD] / 2) / _modY[dirD];
+ ss0 = (dsx + _modX[dirS] / 2) / _modX[dirS];
+ sd1 = sd0 / 2;
+ ss1 = ss0 / 2;
+ sd2 = sd0 - sd1;
+ ss2 = ss0 - ss1;
+ }
+
+ switch (best) {
+ case 0: // halfsquare, diagonal, halfsquare
+ _smoothPath[k].x = x + dsx / 2;
+ _smoothPath[k].y = y + dsy / 2;
+ _smoothPath[k].dir = dirS;
+ _smoothPath[k].num = ss1;
+ k++;
+
+ _smoothPath[k].x = x + dsx / 2 + ddx;
+ _smoothPath[k].y = y + dsy / 2 + ddy;
+ _smoothPath[k].dir = dirD;
+ _smoothPath[k].num = sd0;
+ k++;
+
+ _smoothPath[k].x = x + dsx + ddx;
+ _smoothPath[k].y = y + dsy + ddy;
+ _smoothPath[k].dir = dirS;
+ _smoothPath[k].num = ss2;
+ k++;
+
+ break;
+ case 1: // square, diagonal
+ _smoothPath[k].x = x + dsx;
+ _smoothPath[k].y = y + dsy;
+ _smoothPath[k].dir = dirS;
+ _smoothPath[k].num = ss0;
+ k++;
+
+ _smoothPath[k].x = x2;
+ _smoothPath[k].y = y2;
+ _smoothPath[k].dir = dirD;
+ _smoothPath[k].num = sd0;
+ k++;
+
+ break;
+ case 2: // diagonal square
+ _smoothPath[k].x = x + ddx;
+ _smoothPath[k].y = y + ddy;
+ _smoothPath[k].dir = dirD;
+ _smoothPath[k].num = sd0;
+ k++;
+
+ _smoothPath[k].x = x2;
+ _smoothPath[k].y = y2;
+ _smoothPath[k].dir = dirS;
+ _smoothPath[k].num = ss0;
+ k++;
+
+ break;
+ default: // halfdiagonal, square, halfdiagonal
+ _smoothPath[k].x = x + ddx / 2;
+ _smoothPath[k].y = y + ddy / 2;
+ _smoothPath[k].dir = dirD;
+ _smoothPath[k].num = sd1;
+ k++;
+
+ _smoothPath[k].x = x + dsx + ddx / 2;
+ _smoothPath[k].y = y + dsy + ddy / 2;
+ _smoothPath[k].dir = dirS;
+ _smoothPath[k].num = ss0;
+ k++;
+
+ _smoothPath[k].x = x2;
+ _smoothPath[k].y = y2;
+ _smoothPath[k].dir = dirD;
+ _smoothPath[k].num = sd2;
+ k++;
+
+ break;
+ }
+
+ return k;
+}
+
+void Router::slidyPath() {
+ /*********************************************************************
+ * slidyPath creates a path based on part steps with no sliding to get
+ * as near as possible to the target without any sliding this routine
+ * is intended for use when just clicking about.
+ *
+ * produce a module list from the line data
+ *********************************************************************/
+
+ int32 smooth = 1;
+ int32 slidy = 1;
+
+ // strip out the short sections
+
+ _modularPath[0].x = _smoothPath[0].x;
+ _modularPath[0].y = _smoothPath[0].y;
+ _modularPath[0].dir = _smoothPath[0].dir;
+ _modularPath[0].num = 0;
+
+ while (_smoothPath[smooth].num < ROUTE_END_FLAG) {
+ int32 scale = _scaleA * _smoothPath[smooth].y + _scaleB;
+ int32 deltaX = _smoothPath[smooth].x - _modularPath[slidy - 1].x;
+ int32 deltaY = _smoothPath[smooth].y - _modularPath[slidy - 1].y;
+ // quarter a step minimum
+ int32 stepX = (scale * _modX[_smoothPath[smooth].dir]) >> 19;
+ int32 stepY = (scale * _modY[_smoothPath[smooth].dir]) >> 19;
+
+ if (ABS(deltaX) >= ABS(stepX) && ABS(deltaY) >= ABS(stepY)) {
+ _modularPath[slidy].x = _smoothPath[smooth].x;
+ _modularPath[slidy].y = _smoothPath[smooth].y;
+ _modularPath[slidy].dir = _smoothPath[smooth].dir;
+ _modularPath[slidy].num = 1;
+ slidy++;
+ }
+ smooth++;
+ }
+
+ // in case the last bit had no steps
+
+ if (slidy > 1) {
+ _modularPath[slidy - 1].x = _smoothPath[smooth - 1].x;
+ _modularPath[slidy - 1].y = _smoothPath[smooth - 1].y;
+ }
+
+ // set up the end of the walk
+
+ _modularPath[slidy].x = _smoothPath[smooth - 1].x;
+ _modularPath[slidy].y = _smoothPath[smooth - 1].y;
+ _modularPath[slidy].dir = _targetDir;
+ _modularPath[slidy].num = 0;
+ slidy++;
+
+ _modularPath[slidy].x = _smoothPath[smooth - 1].x;
+ _modularPath[slidy].y = _smoothPath[smooth - 1].y;
+ _modularPath[slidy].dir = 9;
+ _modularPath[slidy].num = ROUTE_END_FLAG;
+ slidy++;
+}
+
+// SLOW IN
+
+bool Router::addSlowInFrames(WalkData *walkAnim) {
+ if (_walkData.usingSlowInFrames && _modularPath[1].num > 0) {
+ for (int slowInFrameNo = 0; slowInFrameNo < _walkData.nSlowInFrames[_currentDir]; slowInFrameNo++) {
+ walkAnim[_stepCount].frame = _firstSlowInFrame[_currentDir] + slowInFrameNo;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = _currentDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void Router::earlySlowOut(byte *ob_mega, byte *ob_walkdata) {
+ int32 slowOutFrameNo;
+ int32 walk_pc;
+ WalkData *walkAnim;
+
+ ObjectMega obMega(ob_mega);
+
+ debug(5, "EARLY SLOW-OUT");
+
+ loadWalkData(ob_walkdata);
+
+ debug(5, "********************************");
+ debug(5, "_framesPerStep = %d", _framesPerStep);
+ debug(5, "_numberOfSlowOutFrames = %d", _numberOfSlowOutFrames);
+ debug(5, "_firstWalkingTurnLeftFrame = %d", _firstWalkingTurnLeftFrame);
+ debug(5, "_firstWalkingTurnRightFrame = %d", _firstWalkingTurnRightFrame);
+ debug(5, "_firstSlowOutFrame = %d", _firstSlowOutFrame);
+ debug(5, "********************************");
+
+ walk_pc = obMega.getWalkPc();
+
+ walkAnim = getRouteMem();
+
+ // if this mega does actually have slow-out frames
+ if (_walkData.usingSlowOutFrames) {
+ // overwrite the next step (half a cycle) of the walk
+ // (ie .step - 0..5)
+
+ do {
+ debug(5, "STEP NUMBER: walkAnim[%d].step = %d", walk_pc, walkAnim[walk_pc].step);
+ debug(5, "ORIGINAL FRAME: walkAnim[%d].frame = %d", walk_pc, walkAnim[walk_pc].frame);
+
+ // map from existing walk frame across to correct
+ // frame number of slow-out - remember, there may be
+ // more slow-out frames than walk-frames!
+
+ if (walkAnim[walk_pc].frame >= _firstWalkingTurnRightFrame) {
+ // if it's a walking turn-right, rather than a
+ // normal step, then map it to a normal step
+ // frame first
+
+ walkAnim[walk_pc].frame -= _firstWalkingTurnRightFrame;
+ debug(5, "MAPPED TO WALK: walkAnim[%d].frame = %d (walking turn-right frame --> walk frame)", walk_pc, walkAnim[walk_pc].frame);
+ } else if (walkAnim[walk_pc].frame >= _firstWalkingTurnLeftFrame) {
+ // if it's a walking turn-left, rather than a
+ // normal step, then map it to a normal step
+ // frame first
+
+ walkAnim[walk_pc].frame -= _firstWalkingTurnLeftFrame;
+ debug(5, "MAPPED TO WALK: walkAnim[%d].frame = %d (walking turn-left frame --> walk frame)", walk_pc, walkAnim[walk_pc].frame);
+ }
+
+ walkAnim[walk_pc].frame += _firstSlowOutFrame + ((walkAnim[walk_pc].frame / _framesPerStep) * (_numberOfSlowOutFrames - _framesPerStep));
+ walkAnim[walk_pc].step = 0;
+ debug(5, "SLOW-OUT FRAME: walkAnim[%d].frame = %d",walk_pc, walkAnim[walk_pc].frame);
+ walk_pc++;
+ } while (walkAnim[walk_pc].step > 0);
+
+ // add stationary frame(s) (OPTIONAL)
+
+ for (slowOutFrameNo = _framesPerStep; slowOutFrameNo < _numberOfSlowOutFrames; slowOutFrameNo++) {
+ walkAnim[walk_pc].frame = walkAnim[walk_pc - 1].frame + 1;
+ debug(5, "EXTRA FRAME: walkAnim[%d].frame = %d", walk_pc, walkAnim[walk_pc].frame);
+ walkAnim[walk_pc].step = 0;
+ walkAnim[walk_pc].dir = walkAnim[walk_pc - 1].dir;
+ walkAnim[walk_pc].x = walkAnim[walk_pc - 1].x;
+ walkAnim[walk_pc].y = walkAnim[walk_pc - 1].y;
+ walk_pc++;
+ }
+ } else {
+ // this mega doesn't have slow-out frames
+ // stand in current direction
+
+ walkAnim[walk_pc].frame = _firstStandFrame + walkAnim[walk_pc - 1].dir;
+ walkAnim[walk_pc].step = 0;
+ walkAnim[walk_pc].dir = walkAnim[walk_pc - 1].dir;
+ walkAnim[walk_pc].x = walkAnim[walk_pc - 1].x;
+ walkAnim[walk_pc].y = walkAnim[walk_pc - 1].y;
+ walk_pc++;
+ }
+
+ // end of sequence
+ walkAnim[walk_pc].frame = 512;
+
+ // so that this doesn't happen again while 'george_walking' is still
+ // '2'
+ walkAnim[walk_pc].step = 99;
+}
+
+// SLOW OUT
+
+void Router::addSlowOutFrames(WalkData *walkAnim) {
+ int32 slowOutFrameNo;
+
+ // if the mega did actually walk, we overwrite the last step (half a
+ // cycle) with slow-out frames + add any necessary stationary frames
+
+ if (_walkData.usingSlowOutFrames && _lastCount >= _framesPerStep) {
+ // place stop frames here
+ // slowdown at the end of the last walk
+
+ slowOutFrameNo = _lastCount - _framesPerStep;
+
+ debug(5, "SLOW OUT: slowOutFrameNo(%d) = _lastCount(%d) - _framesPerStep(%d)", slowOutFrameNo, _lastCount, _framesPerStep);
+
+ // overwrite the last step (half a cycle) of the walk
+
+ do {
+ // map from existing walk frame across to correct
+ // frame number of slow-out - remember, there may be
+ // more slow-out frames than walk-frames!
+
+ walkAnim[slowOutFrameNo].frame += _firstSlowOutFrame + ((walkAnim[slowOutFrameNo].frame / _framesPerStep) * (_numberOfSlowOutFrames - _framesPerStep));
+
+ // because no longer a normal walk-step
+ walkAnim[slowOutFrameNo].step = 0;
+
+ debug(5, "walkAnim[%d].frame = %d",slowOutFrameNo,walkAnim[slowOutFrameNo].frame);
+ slowOutFrameNo++;
+ } while (slowOutFrameNo < _lastCount);
+
+ // add stationary frame(s) (OPTIONAL)
+
+ for (slowOutFrameNo = _framesPerStep; slowOutFrameNo < _numberOfSlowOutFrames; slowOutFrameNo++) {
+ walkAnim[_stepCount].frame = walkAnim[_stepCount - 1].frame + 1;
+
+ debug(5, "EXTRA FRAMES: walkAnim[%d].frame = %d", _stepCount, walkAnim[_stepCount].frame);
+
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = walkAnim[_stepCount - 1].dir;
+ walkAnim[_stepCount].x = walkAnim[_stepCount - 1].x;
+ walkAnim[_stepCount].y = walkAnim[_stepCount - 1].y;
+ _stepCount++;
+ }
+ }
+}
+
+void Router::slidyWalkAnimator(WalkData *walkAnim) {
+ /*********************************************************************
+ * Skidding every where HardWalk creates an animation that exactly
+ * fits the smoothPath and uses foot slipping to fit whole steps into
+ * the route
+ *
+ * Parameters: georgeg, mouseg
+ * Returns: rout
+ *
+ * produce a module list from the line data
+ *********************************************************************/
+
+ static int32 left = 0;
+ int32 p;
+ int32 lastDir;
+ int32 lastRealDir;
+ int32 turnDir;
+ int32 scale;
+ int32 step;
+ int32 module;
+ int32 moduleEnd;
+ int32 module16X;
+ int32 module16Y;
+ int32 stepX;
+ int32 stepY;
+ int32 errorX;
+ int32 errorY;
+ int32 lastErrorX;
+ int32 lastErrorY;
+ int32 frameCount;
+ int32 frames;
+
+ p = 0;
+ lastDir = _modularPath[0].dir;
+ _currentDir = _modularPath[1].dir;
+
+ if (_currentDir == NO_DIRECTIONS)
+ _currentDir = lastDir;
+
+ _moduleX = _startX;
+ _moduleY = _startY;
+ module16X = _moduleX << 16;
+ module16Y = _moduleY << 16;
+ _stepCount = 0;
+
+ // START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY
+ // BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED
+
+ debug(5, "SLIDY: STARTING THE WALK");
+
+ module = _framesPerChar + lastDir;
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+
+ // TURN TO START THE WALK
+
+ debug(5, "SLIDY: TURNING TO START THE WALK");
+ // rotate if we need to
+
+ if (lastDir != _currentDir) {
+ // get the direction to turn
+ turnDir = _currentDir - lastDir;
+ if (turnDir < 0)
+ turnDir += NO_DIRECTIONS;
+
+ if (turnDir > 4)
+ turnDir = -1;
+ else if (turnDir > 0)
+ turnDir = 1;
+
+ // rotate to new walk direction
+ // for george and nico put in a head turn at the start
+
+ if (_walkData.usingStandingTurnFrames) {
+ // new frames for turn frames 29oct95jps
+ if (turnDir < 0)
+ module = _firstStandingTurnLeftFrame + lastDir;
+ else
+ module = _firstStandingTurnRightFrame + lastDir;
+
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+
+ // rotate till were facing new dir then go back 45 degrees
+ while (lastDir != _currentDir) {
+ lastDir += turnDir;
+
+ // new frames for turn frames 29oct95jps
+ if (turnDir < 0) {
+ if ( lastDir < 0)
+ lastDir += NO_DIRECTIONS;
+ module = _firstStandingTurnLeftFrame + lastDir;
+ } else {
+ if ( lastDir > 7)
+ lastDir -= NO_DIRECTIONS;
+ module = _firstStandingTurnRightFrame + lastDir;
+ }
+
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+
+ // the back 45 degrees bit
+ // step back one because new head turn for george takes us
+ // past the new dir
+ _stepCount--;
+ }
+
+ // his head is in the right direction
+ lastRealDir = _currentDir;
+
+ // SLIDY: THE SLOW IN
+
+ addSlowInFrames(walkAnim);
+
+ // THE WALK
+
+ debug(5, "SLIDY: THE WALK");
+
+ // start the walk on the left or right leg, depending on how the
+ // slow-in frames were drawn
+
+ // (0 = left; 1 = right)
+
+ if (_walkData.leadingLeg[_currentDir] == 0) {
+ // start the walk on the left leg (ie. at beginning of the
+ // first step of the walk cycle)
+ left = 0;
+ } else {
+ // start the walk on the right leg (ie. at beginning of the
+ // second step of the walk cycle)
+ left = _framesPerStep;
+ }
+
+ _lastCount = _stepCount;
+
+ // this ensures that we don't put in turn frames for the start
+ lastDir = 99;
+
+ // this ensures that we don't put in turn frames for the start
+ _currentDir = 99;
+
+ do {
+ assert(_stepCount < O_WALKANIM_SIZE);
+ while (_modularPath[p].num == 0) {
+ p++;
+ if (_currentDir != 99)
+ lastRealDir = _currentDir;
+ lastDir = _currentDir;
+ _lastCount = _stepCount;
+ }
+
+ // calculate average amount to lose in each step on the way
+ // to the next node
+
+ _currentDir = _modularPath[p].dir;
+
+ if (_currentDir < NO_DIRECTIONS) {
+ module = _currentDir * _framesPerStep * 2 + left;
+
+ if (left == 0)
+ left = _framesPerStep;
+ else
+ left = 0;
+
+ moduleEnd = module + _framesPerStep;
+ step = 0;
+ scale = (_scaleA * _moduleY + _scaleB);
+
+ do {
+ module16X += _walkData.dx[module] * scale;
+ module16Y += _walkData.dy[module] * scale;
+ _moduleX = module16X >> 16;
+ _moduleY = module16Y >> 16;
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = step; // normally 0,1,2,3,4,5,0,1,2,etc
+ walkAnim[_stepCount].dir = _currentDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ step++;
+ module++;
+ } while (module < moduleEnd);
+
+ stepX = _modX[_modularPath[p].dir];
+ stepY = _modY[_modularPath[p].dir];
+ errorX = _modularPath[p].x - _moduleX;
+ errorX = errorX * stepX;
+ errorY = _modularPath[p].y - _moduleY;
+ errorY = errorY * stepY;
+
+ if (errorX < 0 || errorY < 0) {
+ _modularPath[p].num = 0; // the end of the path
+
+ // okay those last steps took us past our
+ // target but do we want to scoot or moonwalk
+
+ frames = _stepCount - _lastCount;
+ errorX = _modularPath[p].x - walkAnim[_stepCount - 1].x;
+ errorY = _modularPath[p].y - walkAnim[_stepCount - 1].y;
+
+ if (frames > _framesPerStep) {
+ lastErrorX = _modularPath[p].x - walkAnim[_stepCount - 7].x;
+ lastErrorY = _modularPath[p].y - walkAnim[_stepCount - 7].y;
+
+ if (stepX == 0) {
+ if (3 * ABS(lastErrorY) < ABS(errorY)) {
+ // the last stop was
+ // closest
+ _stepCount -= _framesPerStep;
+ if (left == 0)
+ left = _framesPerStep;
+ else
+ left = 0;
+ }
+ } else {
+ if (3 * ABS(lastErrorX) < ABS(errorX)) {
+ //the last stop was
+ // closest
+ _stepCount -= _framesPerStep;
+ if (left == 0)
+ left = _framesPerStep;
+ else
+ left = 0;
+ }
+ }
+ }
+
+ errorX = _modularPath[p].x - walkAnim[_stepCount-1].x;
+ errorY = _modularPath[p].y - walkAnim[_stepCount-1].y;
+
+ // okay we've reached the end but we still
+ // have an error
+
+ if (errorX != 0) {
+ frameCount = 0;
+ frames = _stepCount - _lastCount;
+
+ do {
+ frameCount++;
+ walkAnim[_lastCount + frameCount - 1].x += errorX * frameCount / frames;
+ } while (frameCount < frames);
+ }
+
+ if (errorY != 0) {
+ frameCount = 0;
+ frames = _stepCount - _lastCount;
+ do {
+ frameCount++;
+ walkAnim[_lastCount + frameCount - 1].y += errorY * frameCount / frames;
+ } while (frameCount < frames);
+ }
+
+ // Now is the time to put in the turn frames
+ // for the last turn
+
+ if (frames < _framesPerStep) {
+ // this ensures that we don't put in
+ // turn frames for this walk or the
+ // next
+ _currentDir = 99;
+ }
+
+ if (_currentDir != 99)
+ lastRealDir = _currentDir;
+
+ // check each turn condition in turn
+
+ // only for george
+ if (lastDir != 99 && _currentDir != 99 && _walkData.usingWalkingTurnFrames) {
+ // 1 and -7 going right -1 and 7 going
+ // left
+ lastDir = _currentDir - lastDir;
+
+ if (lastDir == -1 || lastDir == 7 || lastDir == -2 || lastDir == 6) {
+ // turn at the end of the last
+ // walk
+
+ _frame = _lastCount - _framesPerStep;
+ do {
+ // turning left
+ walkAnim[_frame].frame += _firstWalkingTurnLeftFrame;
+ _frame++;
+ } while (_frame < _lastCount);
+ } else if (lastDir == 1 || lastDir == -7 || lastDir == 2 || lastDir == -6) {
+ // turn at the end of the
+ // current walk
+
+ _frame = _lastCount - _framesPerStep;
+ do {
+ // turning right
+ walkAnim[_frame].frame += _firstWalkingTurnRightFrame;
+ _frame++;
+ } while (_frame < _lastCount);
+ }
+ lastDir = _currentDir;
+ }
+
+ // all turns checked
+
+ _lastCount = _stepCount;
+ _moduleX = walkAnim[_stepCount - 1].x;
+ _moduleY = walkAnim[_stepCount - 1].y;
+ module16X = _moduleX << 16;
+ module16Y = _moduleY << 16;
+ }
+ }
+ } while (_modularPath[p].dir < NO_DIRECTIONS);
+
+#ifdef SWORD2_DEBUG
+ if (lastRealDir == 99)
+ error("slidyWalkAnimatorlast direction error");
+#endif
+
+ // THE SLOW OUT
+ addSlowOutFrames(walkAnim);
+
+ // TURNS TO END THE WALK ?
+
+ // We've done the walk now put in any turns at the end
+
+ if (_targetDir == 8) {
+ // ANY direction -> stand in the last direction
+
+ module = _firstStandFrame + lastRealDir;
+ _targetDir = lastRealDir;
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastRealDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+
+ if (_targetDir == 9) {
+ // 'stance' was non-zero
+ if (_stepCount == 0) {
+ module = _framesPerChar + lastRealDir;
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastRealDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+ } else if (_targetDir != lastRealDir) {
+ // rotate to target direction
+ turnDir = _targetDir - lastRealDir;
+ if ( turnDir < 0)
+ turnDir += NO_DIRECTIONS;
+
+ if (turnDir > 4)
+ turnDir = -1;
+ else if (turnDir > 0)
+ turnDir = 1;
+
+ // rotate to target direction
+ // for george and nico put in a head turn at the start
+
+ if (_walkData.usingStandingTurnFrames) {
+ // new frames for turn frames 29oct95jps
+ if (turnDir < 0)
+ module = _firstStandingTurnLeftFrame + lastDir;
+ else
+ module = _firstStandingTurnRightFrame + lastDir;
+
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastRealDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+
+ // rotate if we need to
+
+ while (lastRealDir != _targetDir) {
+ lastRealDir += turnDir;
+
+ // new frames for turn frames 29oct95jps
+ if (turnDir < 0) {
+ if (lastRealDir < 0)
+ lastRealDir += NO_DIRECTIONS;
+ module = _firstStandingTurnLeftFrame + lastRealDir;
+ } else {
+ if (lastRealDir > 7)
+ lastRealDir -= NO_DIRECTIONS;
+ module = _firstStandingTurnRightFrame + lastRealDir;
+ }
+
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastRealDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+
+ module = _firstStandFrame + lastRealDir;
+ walkAnim[_stepCount - 1].frame = module;
+ } else {
+ // just stand at the end
+ module = _firstStandFrame + lastRealDir;
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastRealDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+
+ walkAnim[_stepCount].frame = 512;
+ walkAnim[_stepCount].step = 99;
+ _stepCount++;
+
+ walkAnim[_stepCount].frame = 512;
+ walkAnim[_stepCount].step = 99;
+ _stepCount++;
+
+ walkAnim[_stepCount].frame = 512;
+ walkAnim[_stepCount].step = 99;
+
+ // write all the frames to "debug.txt"
+ debug(5, "THE WALKDATA:");
+
+ for (_frame = 0; _frame <= _stepCount; _frame++)
+ debug(5, "walkAnim[%d].frame=%d", _frame, walkAnim[_frame].frame);
+
+ debug(5, "routeFinder RouteSize is %d", _stepCount);
+ return;
+}
+
+#ifndef FORCE_SLIDY
+
+// THE SOLID PATH ROUTINES
+
+int32 Router::solidPath() {
+ /*********************************************************************
+ * SolidPath creates a path based on whole steps with no sliding to
+ * get as near as possible to the target without any sliding this
+ * routine is currently unused, but is intended for use when just
+ * clicking about.
+ *
+ * produce a module list from the line data
+ *********************************************************************/
+
+ int32 smooth;
+ int32 solid;
+ int32 scale;
+ int32 stepX;
+ int32 stepY;
+ int32 deltaX;
+ int32 deltaY;
+
+ // strip out the short sections
+
+ solid = 1;
+ smooth = 1;
+ _modularPath[0].x = _smoothPath[0].x;
+ _modularPath[0].y = _smoothPath[0].y;
+ _modularPath[0].dir = _smoothPath[0].dir;
+ _modularPath[0].num = 0;
+
+ do {
+ scale = _scaleA * _smoothPath[smooth].y + _scaleB;
+ deltaX = _smoothPath[smooth].x - _modularPath[solid - 1].x;
+ deltaY = _smoothPath[smooth].y - _modularPath[solid - 1].y;
+ stepX = _modX[_smoothPath[smooth].dir];
+ stepY = _modY[_smoothPath[smooth].dir];
+ stepX = stepX * scale;
+ stepY = stepY * scale;
+ stepX = stepX >> 16;
+ stepY = stepY >> 16;
+
+ if (ABS(deltaX) >= ABS(stepX) && ABS(deltaY) >= ABS(stepY)) {
+ _modularPath[solid].x = _smoothPath[smooth].x;
+ _modularPath[solid].y = _smoothPath[smooth].y;
+ _modularPath[solid].dir = _smoothPath[smooth].dir;
+ _modularPath[solid].num = 1;
+ solid++;
+ }
+
+ smooth++;
+ } while (_smoothPath[smooth].num < ROUTE_END_FLAG);
+
+ // in case the last bit had no steps
+
+ if (solid == 1) {
+ // there were no paths so put in a dummy end
+ solid = 2;
+ _modularPath[1].dir = _smoothPath[0].dir;
+ _modularPath[1].num = 0;
+ }
+
+ _modularPath[solid - 1].x = _smoothPath[smooth - 1].x;
+ _modularPath[solid - 1].y = _smoothPath[smooth - 1].y;
+
+ // set up the end of the walk
+ _modularPath[solid].x = _smoothPath[smooth - 1].x;
+ _modularPath[solid].y = _smoothPath[smooth - 1].y;
+ _modularPath[solid].dir = 9;
+ _modularPath[solid].num = ROUTE_END_FLAG;
+
+ return 1;
+}
+
+int32 Router::solidWalkAnimator(WalkData *walkAnim) {
+ /*********************************************************************
+ * SolidWalk creates an animation based on whole steps with no sliding
+ * to get as near as possible to the target without any sliding. This
+ * routine is is intended for use when just clicking about.
+ *
+ * produce a module list from the line data
+ *
+ * returns 0 if solid route not found
+ *********************************************************************/
+
+ int32 left;
+ int32 turnDir;
+ int32 scale;
+ int32 step;
+ int32 errorX;
+ int32 errorY;
+ int32 moduleEnd;
+ bool slowStart = false;
+
+ // start at the beginning for a change
+
+ int32 lastDir = _modularPath[0].dir;
+ int32 module = _framesPerChar + lastDir;
+
+ _currentDir = _modularPath[1].dir;
+ _moduleX = _startX;
+ _moduleY = _startY;
+ _stepCount = 0;
+
+ int32 module16X = _moduleX << 16;
+ int32 module16Y = _moduleY << 16;
+
+ // START THE WALK WITH THE FIRST STANDFRAME THIS MAY CAUSE A DELAY
+ // BUT IT STOPS THE PLAYER MOVING FOR COLLISIONS ARE DETECTED
+
+ debug(5, "SOLID: STARTING THE WALK");
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+
+ // TURN TO START THE WALK
+
+ debug(5, "SOLID: TURNING TO START THE WALK");
+
+ // rotate if we need to
+
+ if (lastDir != _currentDir) {
+ // get the direction to turn
+ turnDir = _currentDir - lastDir;
+ if (turnDir < 0)
+ turnDir += NO_DIRECTIONS;
+
+ if (turnDir > 4)
+ turnDir = -1;
+ else if (turnDir > 0)
+ turnDir = 1;
+
+ // rotate to new walk direction
+ // for george and nico put in a head turn at the start
+
+ if (_walkData.usingStandingTurnFrames) {
+ // new frames for turn frames 29oct95jps
+ if (turnDir < 0)
+ module = _firstStandingTurnLeftFrame + lastDir;
+ else
+ module = _firstStandingTurnRightFrame + lastDir;
+
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+
+ // rotate till were facing new dir then go back 45 degrees
+
+ while (lastDir != _currentDir) {
+ lastDir += turnDir;
+
+ // new frames for turn frames
+ if (turnDir < 0) {
+ if (lastDir < 0)
+ lastDir += NO_DIRECTIONS;
+ module = _firstStandingTurnLeftFrame + lastDir;
+ } else {
+ if (lastDir > 7)
+ lastDir -= NO_DIRECTIONS;
+ module = _firstStandingTurnRightFrame + lastDir;
+ }
+
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = lastDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ }
+
+ // the back 45 degrees bit
+ // step back one because new head turn for george takes us
+ // past the new dir
+
+ _stepCount--;
+ }
+
+ // THE SLOW IN
+
+ slowStart = addSlowInFrames(walkAnim);
+
+ // THE WALK
+
+ debug(5, "SOLID: THE WALK");
+
+ // start the walk on the left or right leg, depending on how the
+ // slow-in frames were drawn
+
+ // (0 = left; 1 = right)
+ if (_walkData.leadingLeg[_currentDir] == 0) {
+ // start the walk on the left leg (ie. at beginning of the
+ // first step of the walk cycle)
+ left = 0;
+ } else {
+ // start the walk on the right leg (ie. at beginning of the
+ // second step of the walk cycle)
+ left = _framesPerStep;
+ }
+
+ _lastCount = _stepCount;
+
+ // this ensures that we don't put in turn frames for the start
+ lastDir = 99;
+
+ // this ensures that we don't put in turn frames for the start
+ _currentDir = 99;
+
+ int32 p = 1;
+
+ do {
+ while (_modularPath[p].num > 0) {
+ _currentDir = _modularPath[p].dir;
+ if (_currentDir < NO_DIRECTIONS) {
+ module = _currentDir * _framesPerStep * 2 + left;
+
+ if (left == 0)
+ left = _framesPerStep;
+ else
+ left = 0;
+
+ moduleEnd = module + _framesPerStep;
+ step = 0;
+ scale = (_scaleA * _moduleY + _scaleB);
+
+ do {
+ module16X += _walkData.dx[module] * scale;
+ module16Y += _walkData.dy[module] * scale;
+ _moduleX = module16X >> 16;
+ _moduleY = module16Y >> 16;
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = step; // normally 0,1,2,3,4,5,0,1,2,etc
+ walkAnim[_stepCount].dir = _currentDir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+ module++;
+ step++;
+ } while (module < moduleEnd);
+
+ errorX = _modularPath[p].x - _moduleX;
+ errorX = errorX * _modX[_modularPath[p].dir];
+ errorY = _modularPath[p].y - _moduleY;
+ errorY = errorY * _modY[_modularPath[p].dir];
+
+ if (errorX < 0 || errorY < 0) {
+ _modularPath[p].num = 0;
+ _stepCount -= _framesPerStep;
+
+ if (left == 0)
+ left = _framesPerStep;
+ else
+ left = 0;
+
+ // Okay this is the end of a section
+
+ _moduleX = walkAnim[_stepCount - 1].x;
+ _moduleY = walkAnim[_stepCount - 1].y;
+ module16X = _moduleX << 16;
+ module16Y = _moduleY << 16;
+ _modularPath[p].x = _moduleX;
+ _modularPath[p].y = _moduleY;
+
+ // Now is the time to put in the turn
+ // frames for the last turn
+
+ if (_stepCount - _lastCount < _framesPerStep) {
+ // no step taken
+
+ // clean up if a slow in but no
+ // walk
+
+ if (slowStart) {
+ _stepCount -= _walkData.nSlowInFrames[_currentDir];
+ _lastCount -= _walkData.nSlowInFrames[_currentDir];
+ slowStart = false;
+ }
+
+ // this ensures that we don't
+ // put in turn frames for this
+ // walk or the next
+
+ _currentDir = 99;
+ }
+
+ // check each turn condition in turn
+ if (lastDir != 99 && _currentDir != 99 && _walkData.usingWalkingTurnFrames) {
+ // only for george
+ // 1 and -7 going right -1 and
+ // 7 going left
+
+ lastDir = _currentDir - lastDir;
+
+ if (lastDir == -1 || lastDir == 7 || lastDir == -2 || lastDir == 6) {
+ // turn at the end of
+ // the last walk
+
+ _frame = _lastCount - _framesPerStep;
+
+ do {
+ // turning left
+ walkAnim[_frame].frame += _firstWalkingTurnLeftFrame;
+ _frame++;
+ } while (_frame < _lastCount);
+ } else if (lastDir == 1 || lastDir == -7 || lastDir == 2 || lastDir == -6) {
+ // turn at the end of
+ // the current walk
+
+ _frame = _lastCount - _framesPerStep;
+ do {
+ // turning right
+ walkAnim[_frame].frame += _firstWalkingTurnRightFrame;
+ _frame++;
+ } while (_frame < _lastCount);
+ }
+ }
+
+ // all turns checked
+ _lastCount = _stepCount;
+ }
+ }
+ }
+ p++;
+ lastDir = _currentDir;
+
+ // can only be valid first time round
+ slowStart = false;
+ } while (_modularPath[p].dir < NO_DIRECTIONS);
+
+ // THE SLOW OUT
+
+ addSlowOutFrames(walkAnim);
+
+ module = _framesPerChar + _modularPath[p - 1].dir;
+ walkAnim[_stepCount].frame = module;
+ walkAnim[_stepCount].step = 0;
+ walkAnim[_stepCount].dir = _modularPath[p - 1].dir;
+ walkAnim[_stepCount].x = _moduleX;
+ walkAnim[_stepCount].y = _moduleY;
+ _stepCount++;
+
+ walkAnim[_stepCount].frame = 512;
+ walkAnim[_stepCount].step = 99;
+ _stepCount++;
+
+ walkAnim[_stepCount].frame = 512;
+ walkAnim[_stepCount].step = 99;
+ _stepCount++;
+
+ walkAnim[_stepCount].frame = 512;
+ walkAnim[_stepCount].step = 99;
+
+ debug(5, "THE WALKDATA:");
+
+ for (_frame = 0; _frame <= _stepCount; _frame++)
+ debug(5, "walkAnim[%d].frame=%d", _frame, walkAnim[_frame].frame);
+
+ // NO END TURNS
+
+ debug(5, "routeFinder RouteSize is %d", _stepCount);
+ // now check the route
+
+ int i = 0;
+
+ do {
+ if (!check(_modularPath[i].x, _modularPath[i].y, _modularPath[i + 1].x, _modularPath[i + 1].y))
+ p = 0;
+ i++;
+ } while (i < p - 1);
+
+ if (p != 0) {
+ _targetDir = _modularPath[p - 1].dir;
+ if (checkTarget(_moduleX, _moduleY) == 3) {
+ // new target on a line
+ p = 0;
+ debug(5, "Solid walk target was on a line %d %d", _moduleX, _moduleY);
+ }
+ }
+
+ return p;
+}
+#endif
+
+// THE SCAN ROUTINES
+
+bool Router::scan(int32 level) {
+ /*********************************************************************
+ * Called successively from routeFinder until no more changes take
+ * place in the grid array, ie he best path has been found
+ *
+ * Scans through every point in the node array and checks if there is
+ * a route between each point and if this route gives a new route.
+ *
+ * This routine could probably halve its processing time if it doubled
+ * up on the checks after each route check
+ *
+ *********************************************************************/
+
+ int32 x1, y1, x2, y2;
+ int32 distance;
+ bool changed = false;
+
+ // For all the nodes that have new values and a distance less than
+ // enddist, ie dont check for new routes from a point we checked
+ // before or from a point that is already further away than the best
+ // route so far.
+
+ for (int i = 0; i < _nNodes; i++) {
+ if (_node[i].dist < _node[_nNodes].dist && _node[i].level == level) {
+ x1 = _node[i].x;
+ y1 = _node[i].y;
+
+ for (int j = _nNodes; j > 0; j--) {
+ if (_node[j].dist > _node[i].dist) {
+ x2 = _node[j].x;
+ y2 = _node[j].y;
+
+ if (ABS(x2 - x1) > 4.5 * ABS(y2 - y1))
+ distance = (8 * ABS(x2 - x1) + 18 * ABS(y2 - y1)) / (54 * 8) + 1;
+ else
+ distance = (6 * ABS(x2 - x1) + 36 * ABS(y2 - y1)) / (36 * 14) + 1;
+
+ if (distance + _node[i].dist < _node[_nNodes].dist && distance + _node[i].dist < _node[j].dist) {
+ if (newCheck(0, x1, y1, x2, y2)) {
+ _node[j].level = level + 1;
+ _node[j].dist = distance + _node[i].dist;
+ _node[j].prev = i;
+ changed = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return changed;
+}
+
+int32 Router::newCheck(int32 status, int32 x1, int32 y1, int32 x2, int32 y2) {
+ /*********************************************************************
+ * newCheck routine checks if the route between two points can be
+ * achieved without crossing any of the bars in the Bars array.
+ *
+ * newCheck differs from check in that that 4 route options are
+ * considered corresponding to actual walked routes.
+ *
+ * Note distance doesnt take account of shrinking ???
+ *
+ * Note Bars array must be properly calculated ie min max dx dy co
+ *********************************************************************/
+
+ int32 ldx;
+ int32 ldy;
+ int32 dlx;
+ int32 dly;
+ int32 dirX;
+ int32 dirY;
+ int32 step1;
+ int32 step2;
+ int32 step3;
+ int32 steps;
+ int32 options;
+
+ steps = 0;
+ options = 0;
+ ldx = x2 - x1;
+ ldy = y2 - y1;
+ dirX = 1;
+ dirY = 1;
+
+ if (ldx < 0) {
+ ldx = -ldx;
+ dirX = -1;
+ }
+
+ if (ldy < 0) {
+ ldy = -ldy;
+ dirY = -1;
+ }
+
+ // make the route options
+
+ if (_diagonaly * ldx > _diagonalx * ldy) {
+ // dir = 1,2 or 2,3 or 5,6 or 6,7
+
+ dly = ldy;
+ dlx = (ldy * _diagonalx) / _diagonaly;
+ ldx = ldx - dlx;
+ dlx = dlx * dirX;
+ dly = dly * dirY;
+ ldx = ldx * dirX;
+ ldy = 0;
+
+ // options are square, diagonal a code 1 route
+
+ step1 = check(x1, y1, x1 + ldx, y1);
+ if (step1 != 0) {
+ step2 = check(x1 + ldx, y1, x2, y2);
+ if (step2 != 0) {
+ steps = step1 + step2;
+ options |= 2;
+ }
+ }
+
+ // diagonal, square a code 2 route
+
+ if (steps == 0 || status == 1) {
+ step1 = check(x1, y1, x1 + dlx, y1 + dly);
+ if (step1 != 0) {
+ step2 = check(x1 + dlx, y2, x2, y2);
+ if (step2 != 0) {
+ steps = step1 + step2;
+ options |= 4;
+ }
+ }
+ }
+
+ // halfsquare, diagonal, halfsquare a code 0 route
+
+ if (steps == 0 || status == 1) {
+ step1 = check(x1, y1, x1 + ldx / 2, y1);
+ if (step1 != 0) {
+ step2 = check(x1 + ldx / 2, y1, x1 + ldx / 2 + dlx, y2);
+ if (step2 != 0) {
+ step3 = check(x1 + ldx / 2 + dlx, y2, x2, y2);
+ if (step3 != 0) {
+ steps = step1 + step2 + step3;
+ options |= 1;
+ }
+ }
+ }
+ }
+
+ //halfdiagonal, square, halfdiagonal a code 3 route
+
+ if (steps == 0 || status == 1) {
+ step1 = check(x1, y1, x1 + dlx / 2, y1 + dly / 2);
+ if (step1 != 0) {
+ step2 = check(x1 + dlx / 2, y1 + dly / 2, x1 + ldx + dlx / 2, y1 + dly / 2);
+ if (step2 != 0) {
+ step3 = check(x1 + ldx + dlx / 2, y1 + dly / 2, x2, y2);
+ if (step3 != 0) {
+ steps = step1 + step2 + step3;
+ options |= 8;
+ }
+ }
+ }
+ }
+ } else {
+ // dir = 7,0 or 0,1 or 3,4 or 4,5
+
+ dlx = ldx;
+ dly = (ldx * _diagonaly) / _diagonalx;
+ ldy = ldy - dly;
+ dlx = dlx * dirX;
+ dly = dly * dirY;
+ ldy = ldy * dirY;
+ ldx = 0;
+
+ // options are square, diagonal a code 1 route
+
+ step1 = check(x1 ,y1, x1, y1 + ldy);
+ if (step1 != 0) {
+ step2 = check(x1, y1 + ldy, x2, y2);
+ if (step2 != 0) {
+ steps = step1 + step2;
+ options |= 2;
+ }
+ }
+
+ // diagonal, square a code 2 route
+
+ if (steps == 0 || status == 1) {
+ step1 = check(x1, y1, x2, y1 + dly);
+ if (step1 != 0) {
+ step2 = check(x2, y1 + dly, x2, y2);
+ if (step2 != 0) {
+ steps = step1 + step2;
+ options |= 4;
+ }
+ }
+ }
+
+ // halfsquare, diagonal, halfsquare a code 0 route
+
+ if (steps == 0 || status == 1) {
+ step1 = check(x1, y1, x1, y1 + ldy / 2);
+ if (step1 != 0) {
+ step2 = check(x1, y1 + ldy / 2, x2, y1 + ldy / 2 + dly);
+ if (step2 != 0) {
+ step3 = check(x2, y1 + ldy / 2 + dly, x2, y2);
+ if (step3 != 0) {
+ steps = step1 + step2 + step3;
+ options |= 1;
+ }
+ }
+ }
+ }
+
+ // halfdiagonal, square, halfdiagonal a code 3 route
+
+ if (steps == 0 || status == 1) {
+ step1 = check(x1, y1, x1 + dlx / 2, y1 + dly / 2);
+ if (step1 != 0) {
+ step2 = check(x1 + dlx / 2, y1 + dly / 2, x1 + dlx / 2, y1 + ldy + dly / 2);
+ if (step2 != 0) {
+ step3 = check(x1 + dlx / 2, y1 + ldy + dly / 2, x2, y2);
+ if (step3 != 0) {
+ steps = step1 + step2 + step3;
+ options |= 8;
+ }
+ }
+ }
+ }
+ }
+
+ if (status == 0)
+ status = steps;
+ else
+ status = options;
+
+ return status;
+}
+
+// CHECK ROUTINES
+
+bool Router::check(int32 x1, int32 y1, int32 x2, int32 y2) {
+ // call the fastest line check for the given line
+ // returns true if line didn't cross any bars
+
+ if (x1 == x2 && y1 == y2)
+ return true;
+
+ if (x1 == x2)
+ return vertCheck(x1, y1, y2);
+
+ if (y1 == y2)
+ return horizCheck(x1, y1, x2);
+
+ return lineCheck(x1, y1, x2, y2);
+}
+
+bool Router::lineCheck(int32 x1, int32 y1, int32 x2, int32 y2) {
+ bool linesCrossed = true;
+
+ int32 xmin = MIN(x1, x2);
+ int32 xmax = MAX(x1, x2);
+ int32 ymin = MIN(y1, y2);
+ int32 ymax = MAX(y1, y2);
+
+ // Line set to go one step in chosen direction so ignore if it hits
+ // anything
+
+ int32 dirx = x2 - x1;
+ int32 diry = y2 - y1;
+
+ int32 co = (y1 * dirx) - (x1 * diry); // new line equation
+
+ for (int i = 0; i < _nBars && linesCrossed; i++) {
+ // skip if not on module
+ if (xmax >= _bars[i].xmin && xmin <= _bars[i].xmax && ymax >= _bars[i].ymin && ymin <= _bars[i].ymax) {
+ // Okay, it's a valid line. Calculate an intercept. Wow
+ // but all this arithmetic we must have loads of time
+
+ // slope it he slope between the two lines
+ int32 slope = (_bars[i].dx * diry) - (_bars[i].dy *dirx);
+ // assuming parallel lines don't cross
+ if (slope != 0) {
+ // calculate x intercept and check its on both
+ // lines
+ int32 xc = ((_bars[i].co * dirx) - (co * _bars[i].dx)) / slope;
+
+ // skip if not on module
+ if (xc >= xmin - 1 && xc <= xmax + 1) {
+ // skip if not on line
+ if (xc >= _bars[i].xmin - 1 && xc <= _bars[i].xmax + 1) {
+ int32 yc = ((_bars[i].co * diry) - (co * _bars[i].dy)) / slope;
+
+ // skip if not on module
+ if (yc >= ymin - 1 && yc <= ymax + 1) {
+ // skip if not on line
+ if (yc >= _bars[i].ymin - 1 && yc <= _bars[i].ymax + 1) {
+ linesCrossed = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return linesCrossed;
+}
+
+bool Router::horizCheck(int32 x1, int32 y, int32 x2) {
+ bool linesCrossed = true;
+
+ int32 xmin = MIN(x1, x2);
+ int32 xmax = MAX(x1, x2);
+
+ // line set to go one step in chosen direction so ignore if it hits
+ // anything
+
+ for (int i = 0; i < _nBars && linesCrossed; i++) {
+ // skip if not on module
+ if (xmax >= _bars[i].xmin && xmin <= _bars[i].xmax && y >= _bars[i].ymin && y <= _bars[i].ymax) {
+ // Okay, it's a valid line calculate an intercept. Wow
+ // but all this arithmetic we must have loads of time
+
+ if (_bars[i].dy == 0)
+ linesCrossed = false;
+ else {
+ int32 ldy = y - _bars[i].y1;
+ int32 xc = _bars[i].x1 + (_bars[i].dx * ldy) / _bars[i].dy;
+ // skip if not on module
+ if (xc >= xmin - 1 && xc <= xmax + 1)
+ linesCrossed = false;
+ }
+ }
+ }
+
+ return linesCrossed;
+}
+
+bool Router::vertCheck(int32 x, int32 y1, int32 y2) {
+ bool linesCrossed = true;
+
+ int32 ymin = MIN(y1, y2);
+ int32 ymax = MAX(y1, y2);
+
+ // Line set to go one step in chosen direction so ignore if it hits
+ // anything
+
+ for (int i = 0; i < _nBars && linesCrossed; i++) {
+ // skip if not on module
+ if (x >= _bars[i].xmin && x <= _bars[i].xmax && ymax >= _bars[i].ymin && ymin <= _bars[i].ymax) {
+ // Okay, it's a valid line calculate an intercept. Wow
+ // but all this arithmetic we must have loads of time
+
+ // both lines vertical and overlap in x and y so they
+ // cross
+
+ if (_bars[i].dx == 0)
+ linesCrossed = false;
+ else {
+ int32 ldx = x - _bars[i].x1;
+ int32 yc = _bars[i].y1 + (_bars[i].dy * ldx) / _bars[i].dx;
+ // the intercept overlaps
+ if (yc >= ymin - 1 && yc <= ymax + 1)
+ linesCrossed = false;
+ }
+ }
+ }
+
+ return linesCrossed;
+}
+
+int32 Router::checkTarget(int32 x, int32 y) {
+ int32 onLine = 0;
+
+ int32 xmin = x - 1;
+ int32 xmax = x + 1;
+ int32 ymin = y - 1;
+ int32 ymax = y + 1;
+
+ // check if point +- 1 is on the line
+ // so ignore if it hits anything
+
+ for (int i = 0; i < _nBars && onLine == 0; i++) {
+ // overlapping line
+ if (xmax >= _bars[i].xmin && xmin <= _bars[i].xmax && ymax >= _bars[i].ymin && ymin <= _bars[i].ymax) {
+ int32 xc, yc;
+
+ // okay this line overlaps the target calculate
+ // an y intercept for x
+
+ // vertical line so we know it overlaps y
+ if (_bars[i].dx == 0)
+ yc = 0;
+ else {
+ int ldx = x - _bars[i].x1;
+ yc = _bars[i].y1 + (_bars[i].dy * ldx) / _bars[i].dx;
+ }
+
+ // overlapping point for y
+ if (yc >= ymin && yc <= ymax) {
+ // target on a line so drop out
+ onLine = 3;
+ debug(5, "RouteFail due to target on a line %d %d", x, y);
+ } else {
+ // vertical line so we know it overlaps y
+ if (_bars[i].dy == 0)
+ xc = 0;
+ else {
+ int32 ldy = y - _bars[i].y1;
+ xc = _bars[i].x1 + (_bars[i].dx * ldy) / _bars[i].dy;
+ }
+
+ // skip if not on module
+ if (xc >= xmin && xc <= xmax) {
+ // target on a line so drop out
+ onLine = 3;
+ debug(5, "RouteFail due to target on a line %d %d", x, y);
+ }
+ }
+ }
+ }
+
+ return onLine;
+}
+
+// THE SETUP ROUTINES
+
+void Router::loadWalkData(byte *ob_walkdata) {
+ uint16 firstFrameOfDirection;
+ uint16 walkFrameNo;
+ uint32 frameCounter = 0; // starts at frame 0 of mega set
+ int i;
+
+ _walkData.read(ob_walkdata);
+
+ // 0 = not using slow out frames; non-zero = using that many frames
+ // for each leading leg for each direction
+
+ _numberOfSlowOutFrames = _walkData.usingSlowOutFrames;
+
+ for (i = 0; i < NO_DIRECTIONS; i++) {
+ firstFrameOfDirection = i * _walkData.nWalkFrames;
+
+ _modX[i] = 0;
+ _modY[i] = 0;
+
+ for (walkFrameNo = firstFrameOfDirection; walkFrameNo < firstFrameOfDirection + _walkData.nWalkFrames / 2; walkFrameNo++) {
+ // eg. _modX[0] is the sum of the x-step sizes for the
+ // first half of the walk cycle for direction 0
+ _modX[i] += _walkData.dx[walkFrameNo];
+ _modY[i] += _walkData.dy[walkFrameNo];
+ }
+ }
+
+ _diagonalx = _modX[3];
+ _diagonaly = _modY[3];
+
+ // interpret the walk data
+
+ _framesPerStep = _walkData.nWalkFrames / 2;
+ _framesPerChar = _walkData.nWalkFrames * NO_DIRECTIONS;
+
+ // offset pointers added Oct 30 95 JPS
+ // mega id references removed 16sep96 by JEL
+
+ // WALK FRAMES
+ // start on frame 0
+
+ frameCounter += _framesPerChar;
+
+ // STAND FRAMES
+ // stand frames come after the walk frames
+ // one stand frame for each direction
+
+ _firstStandFrame = frameCounter;
+ frameCounter += NO_DIRECTIONS;
+
+ // STANDING TURN FRAMES - OPTIONAL!
+ // standing turn-left frames come after the slow-out frames
+ // one for each direction
+ // standing turn-left frames come after the standing turn-right frames
+ // one for each direction
+
+ if (_walkData.usingStandingTurnFrames) {
+ _firstStandingTurnLeftFrame = frameCounter;
+ frameCounter += NO_DIRECTIONS;
+
+ _firstStandingTurnRightFrame = frameCounter;
+ frameCounter += NO_DIRECTIONS;
+ } else {
+ // refer instead to the normal stand frames
+ _firstStandingTurnLeftFrame = _firstStandFrame;
+ _firstStandingTurnRightFrame = _firstStandFrame;
+ }
+
+ // WALKING TURN FRAMES - OPTIONAL!
+ // walking left-turn frames come after the stand frames
+ // walking right-turn frames come after the walking left-turn frames
+
+ if (_walkData.usingWalkingTurnFrames) {
+ _firstWalkingTurnLeftFrame = frameCounter;
+ frameCounter += _framesPerChar;
+
+ _firstWalkingTurnRightFrame = frameCounter;
+ frameCounter += _framesPerChar;
+ } else {
+ _firstWalkingTurnLeftFrame = 0;
+ _firstWalkingTurnRightFrame = 0;
+ }
+
+ // SLOW-IN FRAMES - OPTIONAL!
+ // slow-in frames come after the walking right-turn frames
+
+ if (_walkData.usingSlowInFrames) {
+ // Make note of frame number of first slow-in frame for each
+ // direction. There may be a different number of slow-in
+ // frames in each direction
+
+ for (i = 0; i < NO_DIRECTIONS; i++) {
+ _firstSlowInFrame[i] = frameCounter;
+ frameCounter += _walkData.nSlowInFrames[i];
+ }
+ }
+
+ // SLOW-OUT FRAMES - OPTIONAL!
+ // slow-out frames come after the slow-in frames
+
+ if (_walkData.usingSlowOutFrames)
+ _firstSlowOutFrame = frameCounter;
+}
+
+// THE ROUTE EXTRACTOR
+
+void Router::extractRoute() {
+ /*********************************************************************
+ * extractRoute gets route from the node data after a full scan, route
+ * is written with just the basic way points and direction options for
+ * heading to the next point.
+ *********************************************************************/
+
+ int32 prev;
+ int32 prevx;
+ int32 prevy;
+ int32 last;
+ int32 point;
+ int32 p;
+ int32 dirx;
+ int32 diry;
+ int32 dir;
+ int32 ldx;
+ int32 ldy;
+
+ // extract the route from the node data
+
+ prev = _nNodes;
+ last = prev;
+ point = O_ROUTE_SIZE - 1;
+ _route[point].x = _node[last].x;
+ _route[point].y = _node[last].y;
+
+ do {
+ point--;
+ prev = _node[last].prev;
+ prevx = _node[prev].x;
+ prevy = _node[prev].y;
+ _route[point].x = prevx;
+ _route[point].y = prevy;
+ last = prev;
+ } while (prev > 0);
+
+ // now shuffle route down in the buffer
+
+ _routeLength = 0;
+
+ do {
+ _route[_routeLength].x = _route[point].x;
+ _route[_routeLength].y = _route[point].y;
+ point++;
+ _routeLength++;
+ } while (point < O_ROUTE_SIZE);
+
+ _routeLength--;
+
+ // okay the route exists as a series point now put in some directions
+
+ p = 0;
+
+ do {
+ ldx = _route[p + 1].x - _route[p].x;
+ ldy = _route[p + 1].y - _route[p].y;
+ dirx = 1;
+ diry = 1;
+
+ if (ldx < 0) {
+ ldx = -ldx;
+ dirx = -1;
+ }
+
+ if (ldy < 0) {
+ ldy = -ldy;
+ diry = -1;
+ }
+
+ if (_diagonaly * ldx > _diagonalx * ldy) {
+ // dir = 1,2 or 2,3 or 5,6 or 6,7
+
+ // 2 or 6
+ dir = 4 - 2 * dirx;
+ _route[p].dirS = dir;
+
+ // 1, 3, 5 or 7
+ dir = dir + diry * dirx;
+ _route[p].dirD = dir;
+ } else {
+ // dir = 7,0 or 0,1 or 3,4 or 4,5
+
+ // 0 or 4
+ dir = 2 + 2 * diry;
+ _route[p].dirS = dir;
+
+ // 2 or 6
+ dir = 4 - 2 * dirx;
+
+ // 1, 3, 5 or 7
+ dir = dir + diry * dirx;
+ _route[p].dirD = dir;
+ }
+ p++;
+ } while (p < _routeLength);
+
+ // set the last dir to continue previous route unless specified
+
+ if (_targetDir == 8) {
+ // ANY direction
+ _route[p].dirS = _route[p - 1].dirS;
+ _route[p].dirD = _route[p - 1].dirD;
+ } else {
+ _route[p].dirS = _targetDir;
+ _route[p].dirD = _targetDir;
+ }
+
+ return;
+}
+
+void Router::setUpWalkGrid(byte *ob_mega, int32 x, int32 y, int32 dir) {
+ ObjectMega obMega(ob_mega);
+
+ // get walk grid file + extra grid into 'bars' & 'node' arrays
+ loadWalkGrid();
+
+ // copy the mega structure into the local variables for use in all
+ // subroutines
+
+ _startX = obMega.getFeetX();
+ _startY = obMega.getFeetY();
+ _startDir = obMega.getCurDir();
+ _targetX = x;
+ _targetY = y;
+ _targetDir = dir;
+
+ _scaleA = obMega.getScaleA();
+ _scaleB = obMega.getScaleB();
+
+ // mega's current position goes into first node
+
+ _node[0].x = _startX;
+ _node[0].y = _startY;
+ _node[0].level = 1;
+ _node[0].prev = 0;
+ _node[0].dist = 0;
+
+ // reset other nodes
+
+ for (int i = 1; i < _nNodes; i++) {
+ _node[i].level = 0;
+ _node[i].prev = 0;
+ _node[i].dist = 9999;
+ }
+
+ // target position goes into final node
+ _node[_nNodes].x = _targetX;
+ _node[_nNodes].y = _targetY;
+ _node[_nNodes].level = 0;
+ _node[_nNodes].prev = 0;
+ _node[_nNodes].dist = 9999;
+}
+
+void Router::plotWalkGrid() {
+ int32 i;
+
+ // get walk grid file + extra grid into 'bars' & 'node' arrays
+ loadWalkGrid();
+
+ // lines
+
+ for (i = 0; i < _nBars; i++)
+ _vm->_screen->drawLine(_bars[i].x1, _bars[i].y1, _bars[i].x2, _bars[i].y2, 254);
+
+ // nodes
+
+ // leave node 0 for start node
+ for (i = 1; i < _nNodes; i++)
+ plotCross(_node[i].x, _node[i].y, 184);
+}
+
+void Router::plotCross(int16 x, int16 y, uint8 colour) {
+ _vm->_screen->drawLine(x - 1, y - 1, x + 1, y + 1, colour);
+ _vm->_screen->drawLine(x + 1, y - 1, x - 1, y + 1, colour);
+}
+
+void Router::loadWalkGrid() {
+ WalkGridHeader floorHeader;
+ byte *fPolygrid;
+ uint16 fPolygridLen;
+
+ _nBars = 0; // reset counts
+ _nNodes = 1; // leave node 0 for start-node
+
+ // STATIC GRIDS (added/removed by object logics)
+
+ // go through walkgrid list
+ for (int i = 0; i < MAX_WALKGRIDS; i++) {
+ if (_walkGridList[i]) {
+ int j;
+
+ // open walk grid file
+ fPolygrid = _vm->_resman->openResource(_walkGridList[i]);
+ fPolygridLen = _vm->_resman->fetchLen(_walkGridList[i]);
+
+ Common::MemoryReadStream readS(fPolygrid, fPolygridLen);
+
+ readS.seek(ResHeader::size());
+
+ floorHeader.numBars = readS.readSint32LE();
+ floorHeader.numNodes = readS.readSint32LE();
+
+ // check that we're not going to exceed the max
+ // allowed in the complete walkgrid arrays
+
+ assert(_nBars + floorHeader.numBars < O_GRID_SIZE);
+ assert(_nNodes + floorHeader.numNodes < O_GRID_SIZE);
+
+ // lines
+
+ for (j = 0; j < floorHeader.numBars; j++) {
+ _bars[_nBars + j].x1 = readS.readSint16LE();
+ _bars[_nBars + j].y1 = readS.readSint16LE();
+ _bars[_nBars + j].x2 = readS.readSint16LE();
+ _bars[_nBars + j].y2 = readS.readSint16LE();
+ _bars[_nBars + j].xmin = readS.readSint16LE();
+ _bars[_nBars + j].ymin = readS.readSint16LE();
+ _bars[_nBars + j].xmax = readS.readSint16LE();
+ _bars[_nBars + j].ymax = readS.readSint16LE();
+ _bars[_nBars + j].dx = readS.readSint16LE();
+ _bars[_nBars + j].dy = readS.readSint16LE();
+ _bars[_nBars + j].co = readS.readSint32LE();
+ }
+
+ // nodes
+
+ // leave node 0 for start node
+ for (j = 0; j < floorHeader.numNodes; j++) {
+ _node[_nNodes + j].x = readS.readSint16LE();
+ _node[_nNodes + j].y = readS.readSint16LE();
+ }
+
+ // close walk grid file
+ _vm->_resman->closeResource(_walkGridList[i]);
+
+ // increment counts of total bars & nodes in whole
+ // walkgrid
+
+ _nBars += floorHeader.numBars;
+ _nNodes += floorHeader.numNodes;
+ }
+ }
+}
+
+void Router::clearWalkGridList() {
+ memset(_walkGridList, 0, sizeof(_walkGridList));
+}
+
+// called from fnAddWalkGrid
+
+void Router::addWalkGrid(int32 gridResource) {
+ int i;
+ // First, scan the list to see if this grid is already included
+
+ for (i = 0; i < MAX_WALKGRIDS; i++) {
+ if (_walkGridList[i] == gridResource)
+ return;
+ }
+
+ // Scan the list for a free slot
+
+ for (i = 0; i < MAX_WALKGRIDS; i++) {
+ if (_walkGridList[i] == 0) {
+ _walkGridList[i] = gridResource;
+ return;
+ }
+ }
+
+ error("_walkGridList[] full");
+}
+
+// called from fnRemoveWalkGrid
+
+void Router::removeWalkGrid(int32 gridResource) {
+ for (int i = 0; i < MAX_WALKGRIDS; i++) {
+ if (_walkGridList[i] == gridResource) {
+ // If we've found it in the list, reset entry to zero.
+ // Otherwise just ignore the request.
+ _walkGridList[i] = 0;
+ break;
+ }
+ }
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/router.h b/engines/sword2/router.h
new file mode 100644
index 0000000000..45efd046bd
--- /dev/null
+++ b/engines/sword2/router.h
@@ -0,0 +1,255 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _ROUTER_H
+#define _ROUTER_H
+
+// This used to be a variable, but it was never set. Actually, it wasn't even
+// initialised!
+//
+// Define this to force the use of slidy router (so solid path not used when
+// ending walk in ANY direction)
+//
+// #define FORCE_SLIDY
+
+#include "sword2/object.h"
+
+namespace Sword2 {
+
+struct WalkData {
+ uint16 frame;
+ int16 x;
+ int16 y;
+ uint8 step;
+ uint8 dir;
+};
+
+struct BarData {
+ int16 x1;
+ int16 y1;
+ int16 x2;
+ int16 y2;
+ int16 xmin;
+ int16 ymin;
+ int16 xmax;
+ int16 ymax;
+ int16 dx; // x2 - x1
+ int16 dy; // y2 - y1
+ int32 co; // co = (y1 * dx) - (x1 * dy) from an equation for a
+ // line y * dx = x * dy + co
+};
+
+struct NodeData {
+ int16 x;
+ int16 y;
+ int16 level;
+ int16 prev;
+ int16 dist;
+};
+
+// because we only have 2 megas in the game!
+#define TOTAL_ROUTE_SLOTS 2
+
+#define MAX_FRAMES_PER_CYCLE 16
+#define NO_DIRECTIONS 8
+#define MAX_FRAMES_PER_CHAR (MAX_FRAMES_PER_CYCLE * NO_DIRECTIONS)
+#define ROUTE_END_FLAG 255
+
+#define MAX_WALKGRIDS 10
+
+#define O_WALKANIM_SIZE 600 // max number of nodes in router output
+#define O_GRID_SIZE 200 // max 200 lines & 200 points
+#define O_ROUTE_SIZE 50 // max number of modules in a route
+
+struct RouteData {
+ int32 x;
+ int32 y;
+ int32 dirS;
+ int32 dirD;
+};
+
+struct PathData {
+ int32 x;
+ int32 y;
+ int32 dir;
+ int32 num;
+};
+
+class Router {
+private:
+ Sword2Engine *_vm;
+
+ int16 _standbyX; // see fnSetStandbyCoords()
+ int16 _standbyY;
+ int16 _standbyDir;
+
+ // stores pointers to mem blocks containing routes created & used by
+ // megas (NULL if slot not in use)
+ WalkData *_routeSlots[TOTAL_ROUTE_SLOTS];
+
+ BarData _bars[O_GRID_SIZE];
+ NodeData _node[O_GRID_SIZE];
+
+ int32 _walkGridList[MAX_WALKGRIDS];
+
+ int32 _nBars;
+ int32 _nNodes;
+
+ int32 _startX;
+ int32 _startY;
+ int32 _startDir;
+ int32 _targetX;
+ int32 _targetY;
+ int32 _targetDir;
+ int32 _scaleA;
+ int32 _scaleB;
+
+ RouteData _route[O_ROUTE_SIZE];
+ PathData _smoothPath[O_ROUTE_SIZE];
+ PathData _modularPath[O_ROUTE_SIZE];
+ int32 _routeLength;
+
+ int32 _framesPerStep;
+ int32 _framesPerChar;
+
+ ObjectWalkdata _walkData;
+
+ int8 _modX[NO_DIRECTIONS];
+ int8 _modY[NO_DIRECTIONS];
+ int32 _diagonalx;
+ int32 _diagonaly;
+
+ int32 _firstStandFrame;
+
+ int32 _firstStandingTurnLeftFrame;
+ int32 _firstStandingTurnRightFrame;
+
+ int32 _firstWalkingTurnLeftFrame; // left walking turn
+ int32 _firstWalkingTurnRightFrame; // right walking turn
+
+ uint32 _firstSlowInFrame[NO_DIRECTIONS];
+
+ int32 _firstSlowOutFrame;
+
+ // number of slow-out frames on for each leading-leg in each direction
+ // ie. total number of slow-out frames = (numberOfSlowOutFrames * 2 *
+ // NO_DIRECTIONS)
+
+ int32 _numberOfSlowOutFrames;
+
+ int32 _stepCount;
+
+ int32 _moduleX;
+ int32 _moduleY;
+ int32 _currentDir;
+ int32 _lastCount;
+ int32 _frame;
+
+ uint8 returnSlotNo(uint32 megaId);
+
+ int32 getRoute();
+ void extractRoute();
+ void loadWalkGrid();
+ void setUpWalkGrid(byte *ob_mega, int32 x, int32 y, int32 dir);
+ void loadWalkData(byte *ob_walkdata);
+ bool scan(int32 level);
+
+ int32 newCheck(int32 status, int32 x1, int32 y1, int32 x2, int32 y2);
+ bool lineCheck(int32 x1, int32 x2, int32 y1, int32 y2);
+ bool vertCheck(int32 x, int32 y1, int32 y2);
+ bool horizCheck(int32 x1, int32 y, int32 x2);
+ bool check(int32 x1, int32 y1, int32 x2, int32 y2);
+ int32 checkTarget(int32 x, int32 y);
+
+ int32 smoothestPath();
+ void slidyPath();
+
+ int32 smoothCheck(int32 best, int32 p, int32 dirS, int32 dirD);
+
+ bool addSlowInFrames(WalkData *walkAnim);
+ void addSlowOutFrames(WalkData *walkAnim);
+ void slidyWalkAnimator(WalkData *walkAnim);
+
+#ifndef FORCE_SLIDY
+ int32 solidPath();
+ int32 solidWalkAnimator(WalkData *walkAnim);
+#endif
+
+ void plotCross(int16 x, int16 y, uint8 colour);
+
+public:
+ Router(Sword2Engine *vm) : _vm(vm), _diagonalx(0), _diagonaly(0) {
+ memset(_routeSlots, 0, sizeof(_routeSlots));
+ memset(_bars, 0, sizeof(_bars));
+ memset(_node, 0, sizeof(_node));
+ memset(_walkGridList, 0, sizeof(_walkGridList));
+ memset(_route, 0, sizeof(_route));
+ memset(_smoothPath, 0, sizeof(_smoothPath));
+ memset(_modularPath, 0, sizeof(_modularPath));
+ memset(_modX, 0, sizeof(_modX));
+ memset(_modY, 0, sizeof(_modY));
+ memset(_firstSlowInFrame, 0, sizeof(_firstSlowInFrame));
+ }
+
+ void setStandbyCoords(int16 x, int16 y, uint8 dir);
+ int whatTarget(int startX, int startY, int destX, int destY);
+
+ // Sprites
+ void setSpriteStatus(byte *ob_graph, uint32 type);
+ void setSpriteShading(byte *ob_graph, uint32 type);
+
+ // Animation
+ int doAnimate(byte *ob_logic, byte *ob_graph, int32 animRes, bool reverse);
+ int megaTableAnimate(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *animTable, bool reverse);
+
+ // Walking
+ int doWalk(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, int16 target_x, int16 target_y, uint8 target_dir);
+ int walkToAnim(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 animRes);
+ int walkToTalkToMega(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 megaId, uint32 separation);
+
+ // Turning
+ int doFace(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint8 target_dir);
+ int faceXY(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, int16 target_x, int16 target_y);
+ int faceMega(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 megaId);
+
+ // Standing
+ void standAt(byte *ob_graph, byte *ob_mega, int32 x, int32 y, int32 dir);
+ void standAfterAnim(byte *ob_graph, byte *ob_mega, uint32 animRes);
+ void standAtAnim(byte *ob_graph, byte *ob_mega, uint32 animRes);
+
+ int32 routeFinder(byte *ob_mega, byte *ob_walkdata, int32 x, int32 y, int32 dir);
+
+ void earlySlowOut(byte *ob_mega, byte *ob_walkdata);
+
+ void allocateRouteMem();
+ WalkData *getRouteMem();
+ void freeRouteMem();
+ void freeAllRouteMem();
+ void addWalkGrid(int32 gridResource);
+ void removeWalkGrid(int32 gridResource);
+ void clearWalkGridList();
+
+ void plotWalkGrid();
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/save_rest.cpp b/engines/sword2/save_rest.cpp
new file mode 100644
index 0000000000..df03c21be3
--- /dev/null
+++ b/engines/sword2/save_rest.cpp
@@ -0,0 +1,414 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// ---------------------------------------------------------------------------
+// SAVE_REST.CPP save, restore & restart functions
+//
+// James 05feb97
+//
+// "Jesus Saves", but could he Restore or Restart? He can now...
+//
+// ---------------------------------------------------------------------------
+
+#include "common/stdafx.h"
+#include "common/savefile.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+
+namespace Sword2 {
+
+// A savegame consists of a header and the global variables
+
+// Max length of a savegame filename
+#define MAX_FILENAME_LEN 128
+
+/**
+ * Calculate size of required savegame buffer
+ */
+
+uint32 Sword2Engine::findBufferSize() {
+ // Size of savegame header + size of global variables
+ return 212 + _resman->fetchLen(1);
+}
+
+/**
+ * Save the game.
+ */
+
+uint32 Sword2Engine::saveGame(uint16 slotNo, byte *desc) {
+ char description[SAVE_DESCRIPTION_LEN];
+ uint32 bufferSize = findBufferSize();
+ byte *saveBuffer = (byte *)malloc(bufferSize);
+ ScreenInfo *screenInfo = _screen->getScreenInfo();
+
+ memset(description, 0, sizeof(description));
+ strncpy(description, (char *)desc, SAVE_DESCRIPTION_LEN - 1);
+
+ Common::MemoryWriteStream writeS(saveBuffer, bufferSize);
+
+ byte *globalVars = _resman->openResource(1);
+ byte *objectHub = _resman->openResource(CUR_PLAYER_ID) + ResHeader::size();
+
+ // Script no. 7 - 'george_savedata_request' calls fnPassPlayerSaveData
+ _logic->runResScript(CUR_PLAYER_ID, 7);
+
+ writeS.writeUint32LE(0); // Checksum
+ writeS.write(description, SAVE_DESCRIPTION_LEN);
+ writeS.writeUint32LE(_resman->fetchLen(1));
+ writeS.writeUint32LE(screenInfo->background_layer_id);
+ writeS.writeUint32LE(_logic->getRunList());
+ writeS.writeUint32LE(screenInfo->feet_x);
+ writeS.writeUint32LE(screenInfo->feet_y);
+ writeS.writeUint32LE(_sound->getLoopingMusicId());
+ writeS.write(objectHub, ObjectHub::size());
+ writeS.write(_logic->_saveLogic, ObjectLogic::size());
+ writeS.write(_logic->_saveGraphic, ObjectGraphic::size());
+ writeS.write(_logic->_saveMega, ObjectMega::size());
+ writeS.write(globalVars, _resman->fetchLen(1));
+
+ WRITE_LE_UINT32(saveBuffer, calcChecksum(saveBuffer + 4, bufferSize - 4));
+
+ _resman->closeResource(CUR_PLAYER_ID);
+ _resman->closeResource(1);
+
+ uint32 errorCode = saveData(slotNo, saveBuffer, bufferSize);
+
+ free(saveBuffer);
+
+ if (errorCode != SR_OK) {
+ uint32 textId;
+
+ switch (errorCode) {
+ case SR_ERR_FILEOPEN:
+ textId = TEXT_SAVE_CANT_OPEN;
+ break;
+ default:
+ textId = TEXT_SAVE_FAILED;
+ break;
+ }
+
+ _screen->displayMsg(fetchTextLine(_resman->openResource(textId / SIZE), textId & 0xffff) + 2, 0);
+ }
+
+ return errorCode;
+}
+
+uint32 Sword2Engine::saveData(uint16 slotNo, byte *buffer, uint32 bufferSize) {
+ char saveFileName[MAX_FILENAME_LEN];
+
+ sprintf(saveFileName, "%s.%.3d", _targetName.c_str(), slotNo);
+
+ Common::OutSaveFile *out;
+
+ if (!(out = _saveFileMan->openForSaving(saveFileName))) {
+ return SR_ERR_FILEOPEN;
+ }
+
+ out->write(buffer, bufferSize);
+ out->flush();
+
+ if (!out->ioFailed()) {
+ delete out;
+ return SR_OK;
+ }
+
+ delete out;
+ return SR_ERR_WRITEFAIL;
+}
+
+/**
+ * Restore the game.
+ */
+
+uint32 Sword2Engine::restoreGame(uint16 slotNo) {
+ uint32 bufferSize = findBufferSize();
+ byte *saveBufferMem = (byte *)malloc(bufferSize);
+
+ uint32 errorCode = restoreData(slotNo, saveBufferMem, bufferSize);
+
+ // If it was read in successfully, then restore the game from the
+ // buffer & free the buffer. Note that restoreFromBuffer() frees the
+ // buffer in order to clear it from memory before loading in the new
+ // screen and runlist, so we only need to free it in case of failure.
+
+ if (errorCode == SR_OK)
+ errorCode = restoreFromBuffer(saveBufferMem, bufferSize);
+ else
+ free(saveBufferMem);
+
+ if (errorCode != SR_OK) {
+ uint32 textId;
+
+ switch (errorCode) {
+ case SR_ERR_FILEOPEN:
+ textId = TEXT_RESTORE_CANT_OPEN;
+ break;
+ case SR_ERR_INCOMPATIBLE:
+ textId = TEXT_RESTORE_INCOMPATIBLE;
+ break;
+ default:
+ textId = TEXT_RESTORE_FAILED;
+ break;
+ }
+
+ _screen->displayMsg(fetchTextLine(_resman->openResource(textId / SIZE), textId & 0xffff) + 2, 0);
+ } else {
+ // Prime system with a game cycle
+
+ // Reset the graphic 'BuildUnit' list before a new logic list
+ // (see fnRegisterFrame)
+ _screen->resetRenderLists();
+
+ // Reset the mouse hot-spot list. See fnRegisterMouse()
+ // and fnRegisterFrame()
+ _mouse->resetMouseList();
+
+ if (_logic->processSession())
+ error("restore 1st cycle failed??");
+ }
+
+ // Force the game engine to pick a cursor. This appears to be needed
+ // when using the -x command-line option to restore a game.
+ _mouse->setMouseTouching(1);
+ return errorCode;
+}
+
+uint32 Sword2Engine::restoreData(uint16 slotNo, byte *buffer, uint32 bufferSize) {
+ char saveFileName[MAX_FILENAME_LEN];
+
+ sprintf(saveFileName, "%s.%.3d", _targetName.c_str(), slotNo);
+
+ Common::InSaveFile *in;
+
+ if (!(in = _saveFileMan->openForLoading(saveFileName))) {
+ // error: couldn't open file
+ return SR_ERR_FILEOPEN;
+ }
+
+ // Read savegame into the buffer
+ uint32 itemsRead = in->read(buffer, bufferSize);
+
+ delete in;
+
+ if (itemsRead != bufferSize) {
+ // We didn't get all of it. At the moment we have no way of
+ // knowing why, so assume that it's an incompatible savegame.
+
+ return SR_ERR_INCOMPATIBLE;
+ }
+
+ return SR_OK;
+}
+
+uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
+ Common::MemoryReadStream readS(buffer, size);
+
+ // Calc checksum & check that aginst the value stored in the header
+
+ if (readS.readUint32LE() != calcChecksum(buffer + 4, size - 4)) {
+ free(buffer);
+ return SR_ERR_INCOMPATIBLE;
+ }
+
+ readS.seek(SAVE_DESCRIPTION_LEN, SEEK_CUR);
+
+ // Check savegame against length of current global variables resource
+ // This would most probably be trapped by the checksum test anyway,
+ // but it doesn't do any harm to check this as well.
+
+ // Historical note: During development, earlier savegames would often
+ // be shorter than the current expected length.
+
+ if (readS.readUint32LE() != _resman->fetchLen(1)) {
+ free(buffer);
+ return SR_ERR_INCOMPATIBLE;
+ }
+
+ byte *globalVars = _resman->openResource(1);
+ byte *objectHub = _resman->openResource(CUR_PLAYER_ID) + ResHeader::size();
+
+ uint32 screenId = readS.readUint32LE();
+ uint32 runListId = readS.readUint32LE();
+ uint32 feetX = readS.readUint32LE();
+ uint32 feetY = readS.readUint32LE();
+ uint32 musicId = readS.readUint32LE();
+
+ // Trash all resources from memory except player object & global vars
+ _resman->killAll(false);
+ _logic->resetKillList();
+
+ readS.read(objectHub, ObjectHub::size());
+ readS.read(_logic->_saveLogic, ObjectLogic::size());
+ readS.read(_logic->_saveGraphic, ObjectGraphic::size());
+ readS.read(_logic->_saveMega, ObjectMega::size());
+
+ // Fill out the player object structures from the savegame structures.
+ // Also run the appropriate scripts to set up George's anim tables and
+ // walkdata, and Nico's anim tables.
+
+ // Script no. 8 - 'george_savedata_return' calls fnGetPlayerSaveData
+ _logic->runResScript(CUR_PLAYER_ID, 8);
+
+ // Script no. 14 - 'set_up_nico_anim_tables'
+ _logic->runResScript(CUR_PLAYER_ID, 14);
+
+ // Which megaset was the player at the time of saving?
+ ObjectMega obMega(_logic->_saveMega);
+
+ uint32 scriptNo = 0;
+
+ switch (obMega.getMegasetRes()) {
+ case 36: // GeoMega:
+ scriptNo = 9; // script no.9 - 'player_is_george'
+ break;
+ case 2003: // GeoMegaB:
+ scriptNo = 13; // script no.13 - 'player_is_georgeB'
+ break;
+ case 1366: // NicMegaA:
+ scriptNo = 11; // script no.11 - 'player_is_nicoA'
+ break;
+ case 1437: // NicMegaB:
+ scriptNo = 12; // script no.12 - 'player_is_nicoB'
+ break;
+ case 1575: // NicMegaC:
+ scriptNo = 10; // script no.10 - 'player_is_nicoC'
+ break;
+ }
+
+ _logic->runResScript(CUR_PLAYER_ID, scriptNo);
+
+ // Copy variables from savegame buffer to memory
+ readS.read(globalVars, _resman->fetchLen(1));
+
+ _resman->closeResource(CUR_PLAYER_ID);
+ _resman->closeResource(1);
+
+ free(buffer);
+
+ int32 pars[2];
+
+ pars[0] = screenId;
+ pars[1] = 1;
+ _logic->fnInitBackground(pars);
+
+ ScreenInfo *screenInfo = _screen->getScreenInfo();
+
+ // So palette not restored immediately after control panel - we want to
+ // fade up instead!
+ screenInfo->new_palette = 99;
+
+ // These need setting after the defaults get set in fnInitBackground.
+ // Remember that these can change through the game, so need saving &
+ // restoring too.
+
+ screenInfo->feet_x = feetX;
+ screenInfo->feet_y = feetY;
+
+ // Start the new run list
+ _logic->expressChangeSession(runListId);
+
+ // Force in the new scroll position, so unsightly scroll-catch-up does
+ // not occur when screen first draws after returning from restore panel
+
+ // Set the screen record of player position - ready for setScrolling()
+
+ screenInfo->player_feet_x = obMega.getFeetX();
+ screenInfo->player_feet_y = obMega.getFeetY();
+
+ // if this screen is wide, recompute the scroll offsets now
+ if (screenInfo->scroll_flag)
+ _screen->setScrolling();
+
+ // Any music required will be started after we've returned from
+ // restoreControl() - see systemMenuMouse() in mouse.cpp!
+
+ // Restart any looping music. Originally this was - and still is - done
+ // in systemMenuMouse(), but with ScummVM we have other ways of
+ // restoring savegames so it's easier to put it here as well.
+
+ if (musicId) {
+ pars[0] = musicId;
+ pars[1] = FX_LOOP;
+ _logic->fnPlayMusic(pars);
+ } else
+ _logic->fnStopMusic(NULL);
+
+ return SR_OK;
+}
+
+/**
+ * Get the description of a savegame
+ */
+
+uint32 Sword2Engine::getSaveDescription(uint16 slotNo, byte *description) {
+ char saveFileName[MAX_FILENAME_LEN];
+
+ sprintf(saveFileName, "%s.%.3d", _targetName.c_str(), slotNo);
+
+ Common::InSaveFile *in;
+
+ if (!(in = _saveFileMan->openForLoading(saveFileName))) {
+ return SR_ERR_FILEOPEN;
+ }
+
+ in->readUint32LE();
+ in->read(description, SAVE_DESCRIPTION_LEN);
+
+ delete in;
+ return SR_OK;
+}
+
+bool Sword2Engine::saveExists() {
+ for (int i = 0; i <= 99; i++)
+ if (saveExists(i))
+ return true;
+ return false;
+}
+
+bool Sword2Engine::saveExists(uint16 slotNo) {
+ char saveFileName[MAX_FILENAME_LEN];
+
+ sprintf(saveFileName, "%s.%.3d", _targetName.c_str(), slotNo);
+
+ Common::InSaveFile *in;
+
+ if (!(in = _saveFileMan->openForLoading(saveFileName))) {
+ return false;
+ }
+
+ delete in;
+ return true;
+}
+
+uint32 Sword2Engine::calcChecksum(byte *buffer, uint32 size) {
+ uint32 total = 0;
+
+ for (uint32 pos = 0; pos < size; pos++)
+ total += buffer[pos];
+
+ return total;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/save_rest.h b/engines/sword2/save_rest.h
new file mode 100644
index 0000000000..9463f3fd4a
--- /dev/null
+++ b/engines/sword2/save_rest.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef SAVE_REST_H
+#define SAVE_REST_H
+
+namespace Sword2 {
+
+#define SAVE_DESCRIPTION_LEN 64
+
+// Save & Restore error codes
+
+enum {
+ SR_OK, // No worries
+ SR_ERR_FILEOPEN, // Can't open file - Couldn't create file for
+ // saving, or couldn't find file for loading.
+ SR_ERR_INCOMPATIBLE, // (Restore) Incompatible savegame data.
+ // Savegame file is obsolete. (Won't happen
+ // after development stops)
+ SR_ERR_READFAIL, // (Restore) Failed on reading savegame file -
+ // Something screwed up during the read
+ SR_ERR_WRITEFAIL // (Save) Failed on writing savegame file -
+ // Something screwed up during the write -
+ // could be hard-drive full..?
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/scroll.cpp b/engines/sword2/scroll.cpp
new file mode 100644
index 0000000000..3d0a263bc6
--- /dev/null
+++ b/engines/sword2/scroll.cpp
@@ -0,0 +1,151 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+
+namespace Sword2 {
+
+// Max no of pixel allowed to scroll per cycle
+#define MAX_SCROLL_DISTANCE 8
+
+/**
+ * Sets the scroll target position for the end of the game cycle. The driver
+ * will then automatically scroll as many times as it can to reach this
+ * position in the allotted time.
+ */
+
+void Screen::setScrollTarget(int16 sx, int16 sy) {
+ _scrollXTarget = sx;
+ _scrollYTarget = sy;
+}
+
+/**
+ * If the room is larger than the physical screen, this function is called
+ * every game cycle to update the scroll offsets.
+ */
+
+void Screen::setScrolling() {
+ // Normally we aim to get George's feet at (320,250) from top left
+ // of screen window
+ // feet_x = 128 + 320
+ // feet_y = 128 + 250
+
+ // Set scroll offsets according to the player's coords
+
+ // If the scroll offsets are being forced in script, ensure that they
+ // are neither too far to the right nor too far down.
+ uint32 scrollX = _vm->_logic->readVar(SCROLL_X);
+ uint32 scrollY = _vm->_logic->readVar(SCROLL_Y);
+
+ if (scrollX || scrollY) {
+ _thisScreen.scroll_offset_x = MIN((uint16)scrollX, _thisScreen.max_scroll_offset_x);
+ _thisScreen.scroll_offset_y = MIN((uint16)scrollY, _thisScreen.max_scroll_offset_y);
+ return;
+ }
+
+ // George's offset from the centre - the desired position for him
+
+ int16 offset_x = _thisScreen.player_feet_x - _thisScreen.feet_x;
+ int16 offset_y = _thisScreen.player_feet_y - _thisScreen.feet_y;
+
+ // Prevent scrolling too far left/right/up/down
+
+ if (offset_x < 0)
+ offset_x = 0;
+ else if (offset_x > _thisScreen.max_scroll_offset_x)
+ offset_x = _thisScreen.max_scroll_offset_x;
+
+ if (offset_y < 0)
+ offset_y = 0;
+ else if (offset_y > _thisScreen.max_scroll_offset_y)
+ offset_y = _thisScreen.max_scroll_offset_y;
+
+ // First time on this screen - need absolute scroll immediately!
+
+ if (_thisScreen.scroll_flag == 2) {
+ debug(5, "init scroll");
+ _thisScreen.scroll_offset_x = offset_x;
+ _thisScreen.scroll_offset_y = offset_y;
+ _thisScreen.scroll_flag = 1;
+ return;
+ }
+
+ // Catch up with required scroll offsets - speed depending on distance
+ // to catch up (dx and dy) and _scrollFraction used, but limit to
+ // certain number of pixels per cycle (MAX_SCROLL_DISTANCE)
+
+ int16 dx = _thisScreen.scroll_offset_x - offset_x;
+ int16 dy = _thisScreen.scroll_offset_y - offset_y;
+
+ uint16 scroll_distance_x; // how much we want to scroll
+ uint16 scroll_distance_y;
+
+ if (dx < 0) {
+ // Current scroll_offset_x is less than the required value
+
+ // NB. I'm adding 1 to the result of dx / SCROLL_FRACTION,
+ // because it would otherwise not scroll at all when
+ // dx < SCROLL_FRACTION
+
+ // => inc by (fraction of the differnce) NB. dx is -ve, so we
+ // subtract dx / SCROLL_FRACTION
+
+ scroll_distance_x = 1 - dx / _scrollFraction;
+
+ if (scroll_distance_x > MAX_SCROLL_DISTANCE)
+ scroll_distance_x = MAX_SCROLL_DISTANCE;
+
+ _thisScreen.scroll_offset_x += scroll_distance_x;
+ } else if (dx > 0) {
+ // Current scroll_offset_x is greater than
+ // the required value
+
+ // => dec by (fraction of the differnce)
+
+ scroll_distance_x = 1 + dx / _scrollFraction;
+
+ if (scroll_distance_x > MAX_SCROLL_DISTANCE)
+ scroll_distance_x = MAX_SCROLL_DISTANCE;
+
+ _thisScreen.scroll_offset_x -= scroll_distance_x;
+ }
+
+ if (dy < 0) {
+ scroll_distance_y = 1 - dy / _scrollFraction;
+
+ if (scroll_distance_y > MAX_SCROLL_DISTANCE)
+ scroll_distance_y = MAX_SCROLL_DISTANCE;
+
+ _thisScreen.scroll_offset_y += scroll_distance_y;
+ } else if (dy > 0) {
+ scroll_distance_y = 1 + dy / _scrollFraction;
+
+ if (scroll_distance_y > MAX_SCROLL_DISTANCE)
+ scroll_distance_y = MAX_SCROLL_DISTANCE;
+
+ _thisScreen.scroll_offset_y -= scroll_distance_y;
+ }
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/sound.cpp b/engines/sword2/sound.cpp
new file mode 100644
index 0000000000..d855e88afb
--- /dev/null
+++ b/engines/sword2/sound.cpp
@@ -0,0 +1,319 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// ---------------------------------------------------------------------------
+// BROKEN SWORD 2
+//
+// SOUND.CPP Contains the sound engine, fx & music functions
+// Some very 'sound' code in here ;)
+//
+// (16Dec96 JEL)
+//
+// ---------------------------------------------------------------------------
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/system.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+
+#include "sound/wave.h"
+
+namespace Sword2 {
+
+Sound::Sound(Sword2Engine *vm) {
+ int i;
+
+ _vm = vm;
+
+ for (i = 0; i < FXQ_LENGTH; i++)
+ _fxQueue[i].resource = 0;
+
+ for (i = 0; i < MAXMUS; i++) {
+ _music[i] = NULL;
+
+ _musicFile[i].idxTab = NULL;
+ _musicFile[i].idxLen = 0;
+ _musicFile[i].fileSize = 0;
+ _musicFile[i].fileType = 0;
+ _musicFile[i].inUse = false;
+
+ _speechFile[i].idxTab = NULL;
+ _speechFile[i].idxLen = 0;
+ _speechFile[i].fileSize = 0;
+ _speechFile[i].fileType = 0;
+ _speechFile[i].inUse = false;
+ }
+
+ _speechPaused = false;
+ _musicPaused = false;
+ _fxPaused = false;
+
+ _speechMuted = false;
+ _musicMuted = false;
+ _fxMuted = false;
+
+ _reverseStereo = false;
+
+ _loopingMusicId = 0;
+
+ _mixBuffer = NULL;
+ _mixBufferLen = 0;
+
+ _vm->_mixer->setupPremix(this, Audio::Mixer::kMusicSoundType);
+}
+
+Sound::~Sound() {
+ _vm->_mixer->setupPremix(0);
+
+ clearFxQueue();
+ stopMusic(true);
+ stopSpeech();
+
+ free(_mixBuffer);
+
+ for (int i = 0; i < MAXMUS; i++) {
+ if (_musicFile[i].file.isOpen())
+ _musicFile[i].file.close();
+ if (_speechFile[i].file.isOpen())
+ _speechFile[i].file.close();
+
+ free(_musicFile[i].idxTab);
+ free(_speechFile[i].idxTab);
+ }
+}
+
+void Sound::setReverseStereo(bool reverse) {
+ if (reverse != _reverseStereo) {
+ _reverseStereo = reverse;
+
+ for (int i = 0; i < FXQ_LENGTH; i++) {
+ if (!_fxQueue[i].resource)
+ continue;
+
+ _fxQueue[i].pan = -_fxQueue[i].pan;
+ _vm->_mixer->setChannelBalance(_fxQueue[i].handle, _fxQueue[i].pan);
+ }
+ }
+}
+
+/**
+ * Stop all sounds, close their resources and clear the FX queue.
+ */
+
+void Sound::clearFxQueue() {
+ for (int i = 0; i < FXQ_LENGTH; i++) {
+ if (_fxQueue[i].resource) {
+ stopFx(i);
+ }
+ }
+}
+
+/**
+ * Process the FX queue. This function is called once every game cycle.
+ */
+
+void Sound::processFxQueue() {
+ for (int i = 0; i < FXQ_LENGTH; i++) {
+ if (!_fxQueue[i].resource)
+ continue;
+
+ switch (_fxQueue[i].type) {
+ case FX_RANDOM:
+ // 1 in 'delay' chance of this fx occurring
+ if (_vm->_rnd.getRandomNumber(_fxQueue[i].delay) == 0)
+ playFx(&_fxQueue[i]);
+ break;
+ case FX_SPOT:
+ if (_fxQueue[i].delay)
+ _fxQueue[i].delay--;
+ else {
+ playFx(&_fxQueue[i]);
+ _fxQueue[i].type = FX_SPOT2;
+ }
+ break;
+ case FX_LOOP:
+ playFx(&_fxQueue[i]);
+ _fxQueue[i].type = FX_LOOPING;
+ break;
+ case FX_SPOT2:
+ // Once the FX has finished remove it from the queue.
+ if (!_vm->_mixer->isSoundHandleActive(_fxQueue[i].handle)) {
+ _vm->_resman->closeResource(_fxQueue[i].resource);
+ _fxQueue[i].resource = 0;
+ }
+ break;
+ case FX_LOOPING:
+ // Once the looped FX has started we can ignore it,
+ // but we can't close it since the WAV data is in use.
+ break;
+ }
+ }
+}
+
+/**
+ * Queue a sound effect for playing later.
+ * @param res the sound resource number
+ * @param type the type of sound effect
+ * @param delay when to play the sound effect
+ * @param volume the sound effect volume (0 through 16)
+ * @param pan the sound effect panning (-16 through 16)
+ */
+
+void Sound::queueFx(int32 res, int32 type, int32 delay, int32 volume, int32 pan) {
+ if (_vm->_wantSfxDebug) {
+ const char *typeStr;
+
+ switch (type) {
+ case FX_SPOT:
+ typeStr = "SPOT";
+ break;
+ case FX_LOOP:
+ typeStr = "LOOPED";
+ break;
+ case FX_RANDOM:
+ typeStr = "RANDOM";
+ break;
+ default:
+ typeStr = "INVALID";
+ break;
+ }
+
+ byte buf[NAME_LEN];
+
+ debug(0, "SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", _vm->_resman->fetchName(res, buf), volume, pan, delay, typeStr);
+ }
+
+ for (int i = 0; i < FXQ_LENGTH; i++) {
+ if (!_fxQueue[i].resource) {
+ byte *data = _vm->_resman->openResource(res);
+
+ assert(_vm->_resman->fetchType(data) == WAV_FILE);
+
+ uint32 len = _vm->_resman->fetchLen(res) - ResHeader::size();
+
+ if (type == FX_RANDOM) {
+ // For spot effects and loops the delay is the
+ // number of frames to wait. For random
+ // effects, however, it's the average number of
+ // seconds between playing the sound, so we
+ // have to multiply by the frame rate.
+ delay *= 12;
+ }
+
+ volume = (volume * Audio::Mixer::kMaxChannelVolume) / 16;
+ pan = (pan * 127) / 16;
+
+ if (isReverseStereo())
+ pan = -pan;
+
+ _fxQueue[i].resource = res;
+ _fxQueue[i].data = data + ResHeader::size();
+ _fxQueue[i].len = len;
+ _fxQueue[i].delay = delay;
+ _fxQueue[i].volume = volume;
+ _fxQueue[i].pan = pan;
+ _fxQueue[i].type = type;
+
+ // Keep track of the index in the loop so that
+ // fnStopFx() can be used later to kill this sound.
+ // Mainly for FX_LOOP and FX_RANDOM.
+
+ _vm->_logic->writeVar(RESULT, i);
+ return;
+ }
+ }
+
+ warning("No free slot in FX queue");
+}
+
+int32 Sound::playFx(FxQueueEntry *fx) {
+ return playFx(&fx->handle, fx->data, fx->len, fx->volume, fx->pan, (fx->type == FX_LOOP), Audio::Mixer::kSFXSoundType);
+}
+
+int32 Sound::playFx(Audio::SoundHandle *handle, byte *data, uint32 len, uint8 vol, int8 pan, bool loop, Audio::Mixer::SoundType soundType) {
+ if (_fxMuted)
+ return RD_OK;
+
+ if (_vm->_mixer->isSoundHandleActive(*handle))
+ return RDERR_FXALREADYOPEN;
+
+ Common::MemoryReadStream stream(data, len);
+ int rate, size;
+ byte flags;
+
+ if (!loadWAVFromStream(stream, size, rate, flags)) {
+ warning("playFX: Not a valid WAV file");
+ return RDERR_INVALIDWAV;
+ }
+
+ // The resource manager must have complete control over when resources
+ // are freed, or reference counting will break horribly. Besides, the
+ // data pointer is not valid for passing to free(). Why the hell is the
+ // AUTOFREE flag set by default anyway?
+
+ flags &= ~Audio::Mixer::FLAG_AUTOFREE;
+
+ if (isReverseStereo())
+ flags |= Audio::Mixer::FLAG_REVERSE_STEREO;
+
+ if (loop)
+ flags |= Audio::Mixer::FLAG_LOOP;
+
+ _vm->_mixer->playRaw(handle, data + stream.pos(), size, rate, flags, -1, vol, pan, 0, 0, soundType);
+ return RD_OK;
+}
+
+/**
+ * This function closes a sound effect which has been previously opened for
+ * playing. Sound effects must be closed when they are finished with, otherwise
+ * you will run out of sound effect buffers.
+ * @param i the index of the sound to close
+ */
+
+int32 Sound::stopFx(int32 i) {
+ if (!_fxQueue[i].resource)
+ return RDERR_FXNOTOPEN;
+
+ _vm->_mixer->stopHandle(_fxQueue[i].handle);
+
+ _vm->_resman->closeResource(_fxQueue[i].resource);
+ _fxQueue[i].resource = 0;
+ return RD_OK;
+}
+
+void Sound::pauseAllSound() {
+ pauseMusic();
+ pauseSpeech();
+ pauseFx();
+}
+
+void Sound::unpauseAllSound() {
+ unpauseMusic();
+ unpauseSpeech();
+ unpauseFx();
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/sound.h b/engines/sword2/sound.h
new file mode 100644
index 0000000000..7390a60bbc
--- /dev/null
+++ b/engines/sword2/sound.h
@@ -0,0 +1,272 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+/*****************************************************************************
+ * SOUND.H Sound engine
+ *
+ * SOUND.CPP Contains the sound engine, fx & music functions
+ * Some very 'sound' code in here ;)
+ *
+ * (16Dec96 JEL)
+ *
+ ****************************************************************************/
+
+#ifndef SOUND_H
+#define SOUND_H
+
+#include "common/file.h"
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+// Max number of sound fx
+#define MAXMUS 2
+
+// Max number of fx in queue at once
+#define FXQ_LENGTH 32
+
+#define BUFFER_SIZE 4096
+
+namespace Sword2 {
+
+enum {
+ kCLUMode = 1,
+ kMP3Mode,
+ kVorbisMode,
+ kFlacMode
+};
+
+enum {
+ // These three types correspond to types set by the scripts
+ FX_SPOT = 0,
+ FX_LOOP = 1,
+ FX_RANDOM = 2,
+
+ // These are used for FX queue bookkeeping
+ FX_SPOT2 = 3,
+ FX_LOOPING = 4
+};
+
+// Sound defines
+
+enum {
+ RDSE_SAMPLEFINISHED = 0,
+ RDSE_SAMPLEPLAYING = 1,
+ RDSE_FXTOCLEAR = 0, // Unused
+ RDSE_FXCACHED = 1, // Unused
+ RDSE_FXSPOT = 0,
+ RDSE_FXLOOP = 1,
+ RDSE_FXLEADIN = 2,
+ RDSE_FXLEADOUT = 3,
+ RDSE_QUIET = 1,
+ RDSE_SPEAKING = 0
+};
+
+class CLUInputStream : public AudioStream {
+private:
+ Common::File *_file;
+ bool _firstTime;
+ uint32 _file_pos;
+ uint32 _end_pos;
+ int16 _outbuf[BUFFER_SIZE];
+ byte _inbuf[BUFFER_SIZE];
+ const int16 *_bufferEnd;
+ const int16 *_pos;
+
+ uint16 _prev;
+
+ void refill();
+
+ inline bool eosIntern() const {
+ return _pos >= _bufferEnd;
+ }
+
+public:
+ CLUInputStream(Common::File *file, int size);
+ ~CLUInputStream();
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool endOfData() const { return eosIntern(); }
+ bool isStereo() const { return false; }
+ int getRate() const { return 22050; }
+};
+
+struct SoundFileHandle {
+ Common::File file;
+ uint32 *idxTab;
+ uint32 idxLen;
+ uint32 fileSize;
+ uint32 fileType;
+ volatile bool inUse;
+};
+
+class MusicInputStream : public AudioStream {
+private:
+ int _cd;
+ SoundFileHandle *_fh;
+ uint32 _musicId;
+ AudioStream *_decoder;
+ int16 _buffer[BUFFER_SIZE];
+ const int16 *_bufferEnd;
+ const int16 *_pos;
+ bool _remove;
+ uint32 _numSamples;
+ uint32 _samplesLeft;
+ bool _looping;
+ int32 _fading;
+ int32 _fadeSamples;
+ bool _paused;
+
+ void refill();
+
+ inline bool eosIntern() const {
+ if (_looping)
+ return false;
+ return _remove || _pos >= _bufferEnd;
+ }
+
+public:
+ MusicInputStream(int cd, SoundFileHandle *fh, uint32 musicId, bool looping);
+ ~MusicInputStream();
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool endOfData() const { return eosIntern(); }
+ bool isStereo() const { return _decoder->isStereo(); }
+ int getRate() const { return _decoder->getRate(); }
+
+ int getCD() { return _cd; }
+
+ void fadeUp();
+ void fadeDown();
+
+ bool isReady() { return _decoder != NULL; }
+ int32 isFading() { return _fading; }
+
+ bool readyToRemove();
+ int32 getTimeRemaining();
+};
+
+class Sound : public AudioStream {
+private:
+ Sword2Engine *_vm;
+
+ Common::Mutex _mutex;
+
+ struct FxQueueEntry {
+ Audio::SoundHandle handle; // sound handle
+ uint32 resource; // resource id of sample
+ byte *data; // pointer to WAV data
+ uint32 len; // WAV data length
+ uint16 delay; // cycles to wait before playing (or 'random chance' if FX_RANDOM)
+ uint8 volume; // sound volume
+ int8 pan; // sound panning
+ uint8 type; // FX_SPOT, FX_RANDOM, FX_LOOP
+ };
+
+ FxQueueEntry _fxQueue[FXQ_LENGTH];
+
+ void triggerFx(uint8 i);
+
+ bool _reverseStereo;
+
+ bool _speechMuted;
+ bool _fxMuted;
+ bool _musicMuted;
+
+ bool _speechPaused;
+ bool _fxPaused;
+ bool _musicPaused;
+
+ int32 _loopingMusicId;
+
+ Audio::SoundHandle _soundHandleSpeech;
+
+ MusicInputStream *_music[MAXMUS];
+ SoundFileHandle _musicFile[MAXMUS];
+ SoundFileHandle _speechFile[MAXMUS];
+
+ int16 *_mixBuffer;
+ int _mixBufferLen;
+
+public:
+ Sound(Sword2Engine *vm);
+ ~Sound();
+
+ // AudioStream API
+
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return false; }
+ bool endOfData() const;
+ int getRate() const { return 22050; }
+
+ // End of AudioStream API
+
+ void clearFxQueue();
+ void processFxQueue();
+
+ void setReverseStereo(bool reverse);
+ bool isReverseStereo() const { return _reverseStereo; }
+
+ void muteSpeech(bool mute);
+ bool isSpeechMute() const { return _speechMuted; }
+
+ void muteFx(bool mute);
+ bool isFxMute() const { return _fxMuted; }
+
+ void muteMusic(bool mute) { _musicMuted = mute; }
+ bool isMusicMute() const { return _musicMuted; }
+
+ void setLoopingMusicId(int32 id) { _loopingMusicId = id; }
+ int32 getLoopingMusicId() const { return _loopingMusicId; }
+
+ void pauseSpeech();
+ void unpauseSpeech();
+
+ void pauseFx();
+ void unpauseFx();
+
+ void pauseMusic();
+ void unpauseMusic();
+
+ void pauseAllSound();
+ void unpauseAllSound();
+
+ void queueFx(int32 res, int32 type, int32 delay, int32 volume, int32 pan);
+ int32 playFx(FxQueueEntry *fx);
+ int32 playFx(Audio::SoundHandle *handle, byte *data, uint32 len, uint8 vol, int8 pan, bool loop, Audio::Mixer::SoundType soundType);
+ int32 stopFx(int32 i);
+ int32 setFxIdVolumePan(int32 id, int vol, int pan = 255);
+
+ int32 getSpeechStatus();
+ int32 amISpeaking();
+ int32 playCompSpeech(uint32 speechId, uint8 vol, int8 pan);
+ uint32 preFetchCompSpeech(uint32 speechId, uint16 **buf);
+ int32 stopSpeech();
+
+ int32 streamCompMusic(uint32 musicId, bool loop);
+ void stopMusic(bool immediately);
+ int32 musicTimeRemaining();
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/speech.cpp b/engines/sword2/speech.cpp
new file mode 100644
index 0000000000..da6bd258ea
--- /dev/null
+++ b/engines/sword2/speech.cpp
@@ -0,0 +1,229 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+
+#include "sword2/sword2.h"
+#include "sword2/console.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/resman.h"
+
+namespace Sword2 {
+
+// To request the status of a target, we run its 4th script, get-speech-state.
+// This will cause RESULT to be set to either 1 (target is waiting) or 0
+// (target is busy).
+
+// Distance kept above talking sprite
+#define GAP_ABOVE_HEAD 20
+
+enum {
+ S_OB_GRAPHIC = 0,
+ S_OB_SPEECH = 1,
+ S_OB_LOGIC = 2,
+ S_OB_MEGA = 3,
+
+ S_TEXT = 4,
+ S_WAV = 5,
+ S_ANIM = 6,
+ S_DIR_TABLE = 7,
+ S_ANIM_MODE = 8
+};
+
+/**
+ * Sets _textX and _textY for position of text sprite. Note that _textX is
+ * also used to calculate speech pan.
+ */
+
+void Logic::locateTalker(int32 *params) {
+ // params: 0 pointer to ob_graphic
+ // 1 pointer to ob_speech
+ // 2 pointer to ob_logic
+ // 3 pointer to ob_mega
+ // 4 encoded text number
+ // 5 wav res id
+ // 6 anim res id
+ // 7 pointer to anim table
+ // 8 animation mode 0 lip synced,
+ // 1 just straight animation
+
+ if (!_animId) {
+ // There is no animation. Assume it's voice-over text, and put
+ // it at the bottom of the screen.
+
+ _textX = 320;
+ _textY = 400;
+ return;
+ }
+
+ byte *file = _vm->_resman->openResource(_animId);
+
+ // '0' means 1st frame
+
+ CdtEntry cdt_entry;
+ FrameHeader frame_head;
+
+ cdt_entry.read(_vm->fetchCdtEntry(file, 0));
+ frame_head.read(_vm->fetchFrameHeader(file, 0));
+
+ // Note: This part of the code is quite similar to registerFrame().
+
+ if (cdt_entry.frameType & FRAME_OFFSET) {
+ // The frame has offsets, i.e. it's a scalable mega frame
+ ObjectMega obMega(decodePtr(params[S_OB_MEGA]));
+
+ uint16 scale = obMega.calcScale();
+
+ // Calc suitable centre point above the head, based on scaled
+ // height
+
+ // just use 'feet_x' as centre
+ _textX = obMega.getFeetX();
+
+ // Add scaled y-offset to feet_y coord to get top of sprite
+ _textY = obMega.getFeetY() + (cdt_entry.y * scale) / 256;
+ } else {
+ // It's a non-scaling anim - calc suitable centre point above
+ // the head, based on scaled width
+
+ // x-coord + half of width
+ _textX = cdt_entry.x + frame_head.width / 2;
+ _textY = cdt_entry.y;
+ }
+
+ _vm->_resman->closeResource(_animId);
+
+ // Leave space above their head
+ _textY -= GAP_ABOVE_HEAD;
+
+ // Adjust the text coords for RDSPR_DISPLAYALIGN
+
+ ScreenInfo *screenInfo = _vm->_screen->getScreenInfo();
+
+ _textX -= screenInfo->scroll_offset_x;
+ _textY -= screenInfo->scroll_offset_y;
+}
+
+/**
+ * This function is called the first time to build the text, if we need one. If
+ * If necessary it also brings in the wav and sets up the animation.
+ *
+ * If there is an animation it can be repeating lip-sync or run-once.
+ *
+ * If there is no wav, then the text comes up instead. There can be any
+ * combination of text/wav playing.
+ */
+
+void Logic::formText(int32 *params) {
+ // params 0 pointer to ob_graphic
+ // 1 pointer to ob_speech
+ // 2 pointer to ob_logic
+ // 3 pointer to ob_mega
+ // 4 encoded text number
+ // 5 wav res id
+ // 6 anim res id
+ // 7 pointer to anim table
+ // 8 animation mode 0 lip synced,
+ // 1 just straight animation
+
+ // There should always be a text line, as all text is derived from it.
+ // If there is none, that's bad...
+
+ if (!params[S_TEXT]) {
+ warning("No text line for speech wav %d", params[S_WAV]);
+ return;
+ }
+
+ ObjectSpeech obSpeech(decodePtr(params[S_OB_SPEECH]));
+
+ // Establish the max width allowed for this text sprite.
+ uint32 textWidth = obSpeech.getWidth();
+
+ if (!textWidth)
+ textWidth = 400;
+
+ // Pull out the text line, and make the sprite and text block
+
+ uint32 text_res = params[S_TEXT] / SIZE;
+ uint32 local_text = params[S_TEXT] & 0xffff;
+ byte *text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
+
+ // 'text + 2' to skip the first 2 bytes which form the line reference
+ // number
+
+ _speechTextBlocNo = _vm->_fontRenderer->buildNewBloc(
+ text + 2, _textX, _textY,
+ textWidth, obSpeech.getPen(),
+ RDSPR_TRANS | RDSPR_DISPLAYALIGN,
+ _vm->_speechFontId, POSITION_AT_CENTRE_OF_BASE);
+
+ _vm->_resman->closeResource(text_res);
+
+ // Set speech duration, in case not using a wav.
+ _speechTime = strlen((char *)text) + 30;
+}
+
+/**
+ * There are some hard-coded cases where speech is used to illustrate a sound
+ * effect. In this case there is no sound associated with the speech itself.
+ */
+
+bool Logic::wantSpeechForLine(uint32 wavId) {
+ switch (wavId) {
+ case 1328: // AttendantSpeech
+ // SFX(Phone71);
+ // FX <Telephone rings>
+ case 2059: // PabloSpeech
+ // SFX (2059);
+ // FX <Sound of sporadic gunfire from below>
+ case 4082: // DuaneSpeech
+ // SFX (4082);
+ // FX <Pffffffffffft! Frp. (Unimpressive, flatulent noise.)>
+ case 4214: // cat_52
+ // SFX (4214);
+ // 4214FXMeow!
+ case 4568: // trapdoor_13
+ // SFX (4568);
+ // 4568fx<door slamming>
+ case 4913: // LobineauSpeech
+ // SFX (tone2);
+ // FX <Lobineau hangs up>
+ case 5120: // bush_66
+ // SFX (5120);
+ // 5120FX<loud buzzing>
+ case 528: // PresidentaSpeech
+ // SFX (528);
+ // FX <Nearby Crash of Collapsing Masonry>
+ case 920: // Zombie Island forest maze (bird)
+ case 923: // Zombie Island forest maze (monkey)
+ case 926: // Zombie Island forest maze (zombie)
+ // Don't want speech for these lines!
+ return false;
+ default:
+ // Ok for all other lines
+ return true;
+ }
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/sprite.cpp b/engines/sword2/sprite.cpp
new file mode 100644
index 0000000000..a0ba7f7189
--- /dev/null
+++ b/engines/sword2/sprite.cpp
@@ -0,0 +1,655 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/build_display.h"
+
+namespace Sword2 {
+
+/**
+ * This function takes a sprite and creates a mirror image of it.
+ * @param dst destination buffer
+ * @param src source buffer
+ * @param w width of the sprite
+ * @param h height of the sprite
+ */
+
+void Screen::mirrorSprite(byte *dst, byte *src, int16 w, int16 h) {
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ *dst++ = *(src + w - x - 1);
+ }
+ src += w;
+ }
+}
+
+/**
+ * This function takes a compressed frame of a sprite with up to 256 colours
+ * and decompresses it.
+ * @param dst destination buffer
+ * @param src source buffer
+ * @param decompSize the expected size of the decompressed sprite
+ */
+
+int32 Screen::decompressRLE256(byte *dst, byte *src, int32 decompSize) {
+ // PARAMETERS:
+ // source points to the start of the sprite data for input
+ // decompSize gives size of decompressed data in bytes
+ // dest points to start of destination buffer for decompressed
+ // data
+
+ byte headerByte; // block header byte
+ byte *endDest = dst + decompSize; // pointer to byte after end of decomp buffer
+ int32 rv;
+
+ while (1) {
+ // FLAT block
+ // read FLAT block header & increment 'scan' to first pixel
+ // of block
+ headerByte = *src++;
+
+ // if this isn't a zero-length block
+ if (headerByte) {
+ if (dst + headerByte > endDest) {
+ rv = 1;
+ break;
+ }
+
+ // set the next 'headerByte' pixels to the next colour
+ // at 'source'
+ memset(dst, *src, headerByte);
+
+ // increment destination pointer to just after this
+ // block
+ dst += headerByte;
+
+ // increment source pointer to just after this colour
+ src++;
+
+ // if we've decompressed all of the data
+ if (dst == endDest) {
+ rv = 0; // return "OK"
+ break;
+ }
+ }
+
+ // RAW block
+ // read RAW block header & increment 'scan' to first pixel of
+ // block
+ headerByte = *src++;
+
+ // if this isn't a zero-length block
+ if (headerByte) {
+ if (dst + headerByte > endDest) {
+ rv = 1;
+ break;
+ }
+
+ // copy the next 'headerByte' pixels from source to
+ // destination
+ memcpy(dst, src, headerByte);
+
+ // increment destination pointer to just after this
+ // block
+ dst += headerByte;
+
+ // increment source pointer to just after this block
+ src += headerByte;
+
+ // if we've decompressed all of the data
+ if (dst == endDest) {
+ rv = 0; // return "OK"
+ break;
+ }
+ }
+ }
+
+ return rv;
+}
+
+/**
+ * Unwinds a run of 16-colour data into 256-colour palette data.
+ */
+
+void Screen::unwindRaw16(byte *dst, byte *src, uint8 blockSize, byte *colTable) {
+ // for each pair of pixels
+ while (blockSize > 1) {
+ // 1st colour = number in table at position given by upper
+ // nibble of source byte
+ *dst++ = colTable[(*src) >> 4];
+
+ // 2nd colour = number in table at position given by lower
+ // nibble of source byte
+ *dst++ = colTable[(*src) & 0x0f];
+
+ // point to next source byte
+ src++;
+
+ // decrement count of how many pixels left to read
+ blockSize -= 2;
+ }
+
+ // if there's a final odd pixel
+ if (blockSize) {
+ // colour = number in table at position given by upper nibble
+ // of source byte
+ *dst++ = colTable[(*src) >> 4];
+ }
+}
+
+/**
+ * This function takes a compressed frame of a sprite (with up to 16 colours)
+ * and decompresses it.
+ * @param dst destination buffer
+ * @param src source buffer
+ * @param decompSize the expected size of the uncompressed sprite
+ * @param colTable mapping from the 16 encoded colours to the current palette
+ */
+
+int32 Screen::decompressRLE16(byte *dst, byte *src, int32 decompSize, byte *colTable) {
+ byte headerByte; // block header byte
+ byte *endDest = dst + decompSize; // pointer to byte after end of decomp buffer
+ int32 rv;
+
+ while (1) {
+ // FLAT block
+ // read FLAT block header & increment 'scan' to first pixel
+ // of block
+ headerByte = *src++;
+
+ // if this isn't a zero-length block
+ if (headerByte) {
+ if (dst + headerByte > endDest) {
+ rv = 1;
+ break;
+ }
+
+ // set the next 'headerByte' pixels to the next
+ // colour at 'source'
+ memset(dst, *src, headerByte);
+
+ // increment destination pointer to just after this
+ // block
+ dst += headerByte;
+
+ // increment source pointer to just after this colour
+ src++;
+
+ // if we've decompressed all of the data
+ if (dst == endDest) {
+ rv = 0; // return "OK"
+ break;
+ }
+ }
+
+ // RAW block
+ // read RAW block header & increment 'scan' to first pixel of
+ // block
+ headerByte = *src++;
+
+ // if this isn't a zero-length block
+ if (headerByte) {
+ if (dst + headerByte > endDest) {
+ rv = 1;
+ break;
+ }
+
+ // copy the next 'headerByte' pixels from source to
+ // destination (NB. 2 pixels per byte)
+ unwindRaw16(dst, src, headerByte, colTable);
+
+ // increment destination pointer to just after this
+ // block
+ dst += headerByte;
+
+ // increment source pointer to just after this block
+ // (NB. headerByte gives pixels, so /2 for bytes)
+ src += (headerByte + 1) / 2;
+
+ // if we've decompressed all of the data
+ if (dst >= endDest) {
+ rv = 0; // return "OK"
+ break;
+ }
+ }
+ }
+
+ return rv;
+}
+
+/**
+ * Creates a sprite surface. Sprite surfaces are used by the in-game dialogs
+ * and for displaying cutscene subtitles, which makes them much easier to draw
+ * than standard sprites.
+ * @param s information about how to decode the sprite
+ * @param sprite the buffer that will be created to store the surface
+ * @return RD_OK, or an error code
+ */
+
+int32 Screen::createSurface(SpriteInfo *s, byte **sprite) {
+ *sprite = (byte *)malloc(s->w * s->h);
+ if (!*sprite)
+ return RDERR_OUTOFMEMORY;
+
+ // Surfaces are either uncompressed or RLE256-compressed. No need to
+ // test for anything else.
+
+ if (s->type & RDSPR_NOCOMPRESSION) {
+ memcpy(*sprite, s->data, s->w * s->h);
+ } else if (decompressRLE256(*sprite, s->data, s->w * s->h)) {
+ free(*sprite);
+ return RDERR_DECOMPRESSION;
+ }
+
+ return RD_OK;
+}
+
+/**
+ * Draws the sprite surface created earlier.
+ * @param s information about how to place the sprite
+ * @param surface pointer to the surface created earlier
+ * @param clipRect the clipping rectangle
+ */
+
+void Screen::drawSurface(SpriteInfo *s, byte *surface, Common::Rect *clipRect) {
+ Common::Rect rd, rs;
+ uint16 x, y;
+ byte *src, *dst;
+
+ rs.left = 0;
+ rs.right = s->w;
+ rs.top = 0;
+ rs.bottom = s->h;
+
+ rd.left = s->x;
+ rd.right = rd.left + rs.right;
+ rd.top = s->y;
+ rd.bottom = rd.top + rs.bottom;
+
+ Common::Rect defClipRect(0, 0, _screenWide, _screenDeep);
+
+ if (!clipRect) {
+ clipRect = &defClipRect;
+ }
+
+ if (clipRect->left > rd.left) {
+ rs.left += (clipRect->left - rd.left);
+ rd.left = clipRect->left;
+ }
+
+ if (clipRect->top > rd.top) {
+ rs.top += (clipRect->top - rd.top);
+ rd.top = clipRect->top;
+ }
+
+ if (clipRect->right < rd.right) {
+ rd.right = clipRect->right;
+ }
+
+ if (clipRect->bottom < rd.bottom) {
+ rd.bottom = clipRect->bottom;
+ }
+
+ if (rd.width() <= 0 || rd.height() <= 0)
+ return;
+
+ src = surface + rs.top * s->w + rs.left;
+ dst = _buffer + _screenWide * rd.top + rd.left;
+
+ // Surfaces are always transparent.
+
+ for (y = 0; y < rd.height(); y++) {
+ for (x = 0; x < rd.width(); x++) {
+ if (src[x])
+ dst[x] = src[x];
+ }
+ src += s->w;
+ dst += _screenWide;
+ }
+
+ updateRect(&rd);
+}
+
+/**
+ * Destroys a surface.
+ */
+
+void Screen::deleteSurface(byte *surface) {
+ free(surface);
+}
+
+/**
+ * Draws a sprite onto the screen. The type of the sprite can be a combination
+ * of the following flags, some of which are mutually exclusive:
+ * RDSPR_DISPLAYALIGN The sprite is drawn relative to the top left corner
+ * of the screen
+ * RDSPR_FLIP The sprite is mirrored
+ * RDSPR_TRANS The sprite has a transparent colour zero
+ * RDSPR_BLEND The sprite is translucent
+ * RDSPR_SHADOW The sprite is affected by the light mask. (Scaled
+ * sprites always are.)
+ * RDSPR_NOCOMPRESSION The sprite data is not compressed
+ * RDSPR_RLE16 The sprite data is a 16-colour compressed sprite
+ * RDSPR_RLE256 The sprite data is a 256-colour compressed sprite
+ * @param s all the information needed to draw the sprite
+ * @warning Sprites will only be drawn onto the background, not over menubar
+ * areas.
+ */
+
+// FIXME: I'm sure this could be optimized. There's plenty of data copying and
+// mallocing here.
+
+int32 Screen::drawSprite(SpriteInfo *s) {
+ byte *src, *dst;
+ byte *sprite, *newSprite;
+ uint16 scale;
+ int16 i, j;
+ uint16 srcPitch;
+ bool freeSprite = false;
+ Common::Rect rd, rs;
+
+ // -----------------------------------------------------------------
+ // Decompression and mirroring
+ // -----------------------------------------------------------------
+
+ if (s->type & RDSPR_NOCOMPRESSION)
+ sprite = s->data;
+ else {
+ sprite = (byte *)malloc(s->w * s->h);
+ freeSprite = true;
+ if (!sprite)
+ return RDERR_OUTOFMEMORY;
+ if ((s->type & 0xff00) == RDSPR_RLE16) {
+ if (decompressRLE16(sprite, s->data, s->w * s->h, s->colourTable)) {
+ free(sprite);
+ return RDERR_DECOMPRESSION;
+ }
+ } else {
+ if (decompressRLE256(sprite, s->data, s->w * s->h)) {
+ free(sprite);
+ return RDERR_DECOMPRESSION;
+ }
+ }
+ }
+
+ if (s->type & RDSPR_FLIP) {
+ newSprite = (byte *)malloc(s->w * s->h);
+ if (newSprite == NULL) {
+ if (freeSprite)
+ free(sprite);
+ return RDERR_OUTOFMEMORY;
+ }
+ mirrorSprite(newSprite, sprite, s->w, s->h);
+ if (freeSprite)
+ free(sprite);
+ sprite = newSprite;
+ freeSprite = true;
+ }
+
+ // -----------------------------------------------------------------
+ // Positioning and clipping.
+ // -----------------------------------------------------------------
+
+ int16 spriteX = s->x;
+ int16 spriteY = s->y;
+
+ if (!(s->type & RDSPR_DISPLAYALIGN)) {
+ spriteX += _parallaxScrollX;
+ spriteY += _parallaxScrollY;
+ }
+
+ spriteY += MENUDEEP;
+
+ // A scale factor 0 or 256 means don't scale. Why do they use two
+ // different values to mean the same thing? Normalize it here for
+ // convenience.
+
+ scale = (s->scale == 0) ? 256 : s->scale;
+
+ rs.top = 0;
+ rs.left = 0;
+
+ if (scale != 256) {
+ rs.right = s->scaledWidth;
+ rs.bottom = s->scaledHeight;
+ srcPitch = s->scaledWidth;
+ } else {
+ rs.right = s->w;
+ rs.bottom = s->h;
+ srcPitch = s->w;
+ }
+
+ rd.top = spriteY;
+ rd.left = spriteX;
+
+ if (!(s->type & RDSPR_DISPLAYALIGN)) {
+ rd.top -= _scrollY;
+ rd.left -= _scrollX;
+ }
+
+ rd.right = rd.left + rs.right;
+ rd.bottom = rd.top + rs.bottom;
+
+ // Check if the sprite would end up completely outside the screen.
+
+ if (rd.left > RENDERWIDE || rd.top > RENDERDEEP + MENUDEEP || rd.right < 0 || rd.bottom < MENUDEEP) {
+ if (freeSprite)
+ free(sprite);
+ return RD_OK;
+ }
+
+ if (rd.top < MENUDEEP) {
+ rs.top = MENUDEEP - rd.top;
+ rd.top = MENUDEEP;
+ }
+ if (rd.bottom > RENDERDEEP + MENUDEEP) {
+ rd.bottom = RENDERDEEP + MENUDEEP;
+ rs.bottom = rs.top + (rd.bottom - rd.top);
+ }
+ if (rd.left < 0) {
+ rs.left = -rd.left;
+ rd.left = 0;
+ }
+ if (rd.right > RENDERWIDE) {
+ rd.right = RENDERWIDE;
+ rs.right = rs.left + (rd.right - rd.left);
+ }
+
+ // -----------------------------------------------------------------
+ // Scaling
+ // -----------------------------------------------------------------
+
+ if (scale != 256) {
+ if (s->scaledWidth > SCALE_MAXWIDTH || s->scaledHeight > SCALE_MAXHEIGHT) {
+ if (freeSprite)
+ free(sprite);
+ return RDERR_NOTIMPLEMENTED;
+ }
+
+ newSprite = (byte *)malloc(s->scaledWidth * s->scaledHeight);
+ if (newSprite == NULL) {
+ if (freeSprite)
+ free(sprite);
+ return RDERR_OUTOFMEMORY;
+ }
+
+ if (_renderCaps & RDBLTFX_EDGEBLEND)
+ scaleImageGood(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h, _buffer + _screenWide * rd.top + rd.left);
+ else
+ scaleImageFast(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h);
+
+ if (freeSprite)
+ free(sprite);
+ sprite = newSprite;
+ freeSprite = true;
+ }
+
+ // -----------------------------------------------------------------
+ // Light masking
+ // -----------------------------------------------------------------
+
+ // The light mask is an optional layer that covers the entire room
+ // and which is used to simulate light and shadows. Scaled sprites
+ // (actors, presumably) are always affected.
+
+ if ((_renderCaps & RDBLTFX_SHADOWBLEND) && _lightMask && (scale != 256 || (s->type & RDSPR_SHADOW))) {
+ byte *lightMap;
+
+ // Make sure that we never apply the shadow to the original
+ // resource data. This could only ever happen in the
+ // RDSPR_NOCOMPRESSION case.
+
+ if (!freeSprite) {
+ newSprite = (byte *)malloc(s->w * s->h);
+ memcpy(newSprite, sprite, s->w * s->h);
+ sprite = newSprite;
+ freeSprite = true;
+ }
+
+ src = sprite + rs.top * srcPitch + rs.left;
+ lightMap = _lightMask + (rd.top + _scrollY - MENUDEEP) * _locationWide + rd.left + _scrollX;
+
+ for (i = 0; i < rs.height(); i++) {
+ for (j = 0; j < rs.width(); j++) {
+ if (src[j] && lightMap[j]) {
+ uint8 r = ((32 - lightMap[j]) * _palette[src[j] * 4 + 0]) >> 5;
+ uint8 g = ((32 - lightMap[j]) * _palette[src[j] * 4 + 1]) >> 5;
+ uint8 b = ((32 - lightMap[j]) * _palette[src[j] * 4 + 2]) >> 5;
+ src[j] = quickMatch(r, g, b);
+ }
+ }
+ src += srcPitch;
+ lightMap += _locationWide;
+ }
+ }
+
+ // -----------------------------------------------------------------
+ // Drawing
+ // -----------------------------------------------------------------
+
+ src = sprite + rs.top * srcPitch + rs.left;
+ dst = _buffer + _screenWide * rd.top + rd.left;
+
+ if (s->type & RDSPR_BLEND) {
+ // The original code had two different blending cases. One for
+ // s->blend & 0x01 and one for s->blend & 0x02. However, the
+ // only values that actually appear in the cluster files are
+ // 0, 513 and 1025 so the s->blend & 0x02 case was never used.
+ // Which is just as well since that code made no sense to me.
+
+ if (!(_renderCaps & RDBLTFX_SPRITEBLEND)) {
+ for (i = 0; i < rs.height(); i++) {
+ for (j = 0; j < rs.width(); j++) {
+ if (src[j] && ((i & 1) == (j & 1)))
+ dst[j] = src[j];
+ }
+ src += srcPitch;
+ dst += _screenWide;
+ }
+ } else {
+ uint8 n = s->blend >> 8;
+
+ for (i = 0; i < rs.height(); i++) {
+ for (j = 0; j < rs.width(); j++) {
+ if (src[j]) {
+ uint8 r1 = _palette[src[j] * 4 + 0];
+ uint8 g1 = _palette[src[j] * 4 + 1];
+ uint8 b1 = _palette[src[j] * 4 + 2];
+ uint8 r2 = _palette[dst[j] * 4 + 0];
+ uint8 g2 = _palette[dst[j] * 4 + 1];
+ uint8 b2 = _palette[dst[j] * 4 + 2];
+
+ uint8 r = (r1 * n + r2 * (8 - n)) >> 3;
+ uint8 g = (g1 * n + g2 * (8 - n)) >> 3;
+ uint8 b = (b1 * n + b2 * (8 - n)) >> 3;
+ dst[j] = quickMatch(r, g, b);
+ }
+ }
+ src += srcPitch;
+ dst += _screenWide;
+ }
+ }
+ } else {
+ if (s->type & RDSPR_TRANS) {
+ for (i = 0; i < rs.height(); i++) {
+ for (j = 0; j < rs.width(); j++) {
+ if (src[j])
+ dst[j] = src[j];
+ }
+ src += srcPitch;
+ dst += _screenWide;
+ }
+ } else {
+ for (i = 0; i < rs.height(); i++) {
+ memcpy(dst, src, rs.width());
+ src += srcPitch;
+ dst += _screenWide;
+ }
+ }
+ }
+
+ if (freeSprite)
+ free(sprite);
+
+ markAsDirty(rd.left, rd.top, rd.right - 1, rd.bottom - 1);
+ return RD_OK;
+}
+
+/**
+ * Opens the light masking sprite for a room.
+ */
+
+int32 Screen::openLightMask(SpriteInfo *s) {
+ // FIXME: The light mask is only needed on higher graphics detail
+ // settings, so to save memory we could simply ignore it on lower
+ // settings. But then we need to figure out how to ensure that it
+ // is properly loaded if the user changes the settings in mid-game.
+
+ if (_lightMask)
+ return RDERR_NOTCLOSED;
+
+ _lightMask = (byte *)malloc(s->w * s->h);
+ if (!_lightMask)
+ return RDERR_OUTOFMEMORY;
+
+ if (decompressRLE256(_lightMask, s->data, s->w * s->h))
+ return RDERR_DECOMPRESSION;
+
+ return RD_OK;
+}
+
+/**
+ * Closes the light masking sprite for a room.
+ */
+
+int32 Screen::closeLightMask() {
+ if (!_lightMask)
+ return RDERR_NOTOPEN;
+
+ free(_lightMask);
+ _lightMask = NULL;
+ return RD_OK;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/startup.cpp b/engines/sword2/startup.cpp
new file mode 100644
index 0000000000..2e1dd7b0ae
--- /dev/null
+++ b/engines/sword2/startup.cpp
@@ -0,0 +1,173 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/memory.h"
+#include "sword2/resman.h"
+#include "sword2/router.h"
+#include "sword2/sound.h"
+
+namespace Sword2 {
+
+bool Sword2Engine::initStartMenu() {
+ // Print out a list of all the start points available.
+ // There should be a linc produced file called startup.txt.
+ // This file should contain ascii numbers of all the resource game
+ // objects that are screen managers.
+ // We query each in turn and setup an array of start structures.
+ // If the file doesn't exist then we say so and return a 0.
+
+ Common::File fp;
+
+ // ok, load in the master screen manager file
+
+ _totalStartups = 0;
+ _totalScreenManagers = 0;
+
+ if (!fp.open("startup.inf")) {
+ warning("Cannot open startup.inf - the debugger won't have a start menu");
+ return false;
+ }
+
+ // The startup.inf file which contains a list of all the files. Now
+ // extract the filenames
+
+ int start_ids[MAX_starts];
+
+ while (1) {
+ bool done = false;
+
+ start_ids[_totalScreenManagers] = 0;
+
+ // Scan the string until the LF in CRLF
+
+ int b;
+
+ do {
+ b = fp.readByte();
+
+ if (fp.ioFailed()) {
+ done = true;
+ break;
+ }
+
+ if (isdigit(b)) {
+ start_ids[_totalScreenManagers] *= 10;
+ start_ids[_totalScreenManagers] += (b - '0');
+ }
+ } while (b != 10);
+
+ if (done)
+ break;
+
+ _totalScreenManagers++;
+
+ if (_totalScreenManagers == MAX_starts) {
+ warning("MAX_starts exceeded");
+ break;
+ }
+ }
+
+ fp.close();
+
+ // Using this method the Gode generated resource.inf must have #0d0a
+ // on the last entry
+
+ debug(1, "%d screen manager objects", _totalScreenManagers);
+
+ // Open each object and make a query call. The object must fill in a
+ // startup structure. It may fill in several if it wishes - for
+ // instance a startup could be set for later in the game where
+ // specific vars are set
+
+ for (uint i = 0; i < _totalScreenManagers; i++) {
+ _startRes = start_ids[i];
+
+ debug(2, "Querying screen manager %d", _startRes);
+
+ // Open each one and run through the interpreter. Script 0 is
+ // the query request script
+
+ // if the resource number is within range & it's not a null
+ // resource
+ // - need to check in case un-built sections included in
+ // start list
+
+ if (_resman->checkValid(_startRes)) {
+ _logic->runResScript(_startRes, 0);
+ } else
+ warning("Start menu resource %d invalid", _startRes);
+ }
+
+ return 1;
+}
+
+void Sword2Engine::registerStartPoint(int32 key, char *name) {
+ assert(_totalStartups < MAX_starts);
+
+ _startList[_totalStartups].start_res_id = _startRes;
+ _startList[_totalStartups].key = key;
+
+ strncpy(_startList[_totalStartups].description, name, MAX_description);
+ _startList[_totalStartups].description[MAX_description - 1] = 0;
+
+ _totalStartups++;
+}
+
+void Sword2Engine::runStart(int start) {
+ // Restarting - stop sfx, music & speech!
+
+ _sound->clearFxQueue();
+ _logic->fnStopMusic(NULL);
+ _sound->unpauseSpeech();
+ _sound->stopSpeech();
+
+ // Remove all resources from memory, including player object and global
+ // variables
+
+ _resman->removeAll();
+
+ // Reopen global variables resource and player object
+ setupPersistentResources();
+
+ // Free all the route memory blocks from previous game
+ _logic->_router->freeAllRouteMem();
+
+ // If there was speech text, kill the text block
+ if (_logic->_speechTextBlocNo) {
+ _fontRenderer->killTextBloc(_logic->_speechTextBlocNo);
+ _logic->_speechTextBlocNo = 0;
+ }
+
+ _logic->runResObjScript(_startList[start].start_res_id, CUR_PLAYER_ID, _startList[start].key & 0xffff);
+
+ // Make sure there's a mouse, in case restarting while mouse not
+ // available
+ _logic->fnAddHuman(NULL);
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
new file mode 100644
index 0000000000..6056d636b7
--- /dev/null
+++ b/engines/sword2/sword2.cpp
@@ -0,0 +1,689 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/system.h"
+
+#include "sword2/sword2.h"
+#include "sword2/console.h"
+#include "sword2/controls.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/memory.h"
+#include "sword2/mouse.h"
+#include "sword2/resman.h"
+#include "sword2/router.h"
+#include "sword2/sound.h"
+
+#ifdef _WIN32_WCE
+extern bool isSmartphone();
+#endif
+
+struct Sword2GameSettings {
+ const char *gameid;
+ const char *description;
+ uint32 features;
+ const char *detectname;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { gameid, description, features };
+ return dummy;
+ }
+};
+
+static const Sword2GameSettings sword2_settings[] = {
+ /* Broken Sword 2 */
+ {"sword2", "Broken Sword 2: The Smoking Mirror", GF_DEFAULT_TO_1X_SCALER, "players.clu" },
+ {"sword2alt", "Broken Sword 2: The Smoking Mirror (alt)", GF_DEFAULT_TO_1X_SCALER, "r2ctlns.ocx" },
+ {"sword2demo", "Broken Sword 2: The Smoking Mirror (Demo)", GF_DEFAULT_TO_1X_SCALER | Sword2::GF_DEMO, "players.clu" },
+ {NULL, NULL, 0, NULL}
+};
+
+GameList Engine_SWORD2_gameList() {
+ const Sword2GameSettings *g = sword2_settings;
+ GameList games;
+ while (g->gameid) {
+ games.push_back(g->toGameSettings());
+ g++;
+ }
+ return games;
+}
+
+DetectedGameList Engine_SWORD2_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+ const Sword2GameSettings *g;
+
+ // TODO: It would be nice if we had code here which distinguishes
+ // between the 'sword2' and 'sword2demo' targets. The current code
+ // can't do that since they use the same detectname.
+
+ for (g = sword2_settings; g->gameid; ++g) {
+ // Iterate over all files in the given directory
+ for (FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (!file->isDirectory()) {
+ const char *gameName = file->displayName().c_str();
+
+ if (0 == scumm_stricmp(g->detectname, gameName)) {
+ // Match found, add to list of candidates, then abort inner loop.
+ detectedGames.push_back(g->toGameSettings());
+ break;
+ }
+ }
+ }
+ }
+ return detectedGames;
+}
+
+Engine *Engine_SWORD2_create(GameDetector *detector, OSystem *syst) {
+ return new Sword2::Sword2Engine(detector, syst);
+}
+
+REGISTER_PLUGIN(SWORD2, "Broken Sword 2")
+
+namespace Sword2 {
+
+Sword2Engine::Sword2Engine(GameDetector *detector, OSystem *syst) : Engine(syst) {
+ // Add default file directories
+ Common::File::addDefaultDirectory(_gameDataPath + "CLUSTERS/");
+ Common::File::addDefaultDirectory(_gameDataPath + "SWORD2/");
+ Common::File::addDefaultDirectory(_gameDataPath + "VIDEO/");
+ Common::File::addDefaultDirectory(_gameDataPath + "clusters/");
+ Common::File::addDefaultDirectory(_gameDataPath + "sword2/");
+ Common::File::addDefaultDirectory(_gameDataPath + "video/");
+
+ _features = detector->_game.features;
+ _targetName = detector->_targetName;
+
+ _bootParam = ConfMan.getInt("boot_param");
+ _saveSlot = ConfMan.getInt("save_slot");
+
+ _memory = NULL;
+ _resman = NULL;
+ _sound = NULL;
+ _screen = NULL;
+ _mouse = NULL;
+ _logic = NULL;
+ _fontRenderer = NULL;
+ _debugger = NULL;
+
+ _keyboardEvent.pending = false;
+ _keyboardEvent.repeat = 0;
+ _mouseEvent.pending = false;
+
+ _wantSfxDebug = false;
+
+#ifdef SWORD2_DEBUG
+ _stepOneCycle = false;
+ _renderSkip = false;
+#endif
+
+ _gamePaused = false;
+ _graphicsLevelFudged = false;
+
+ _gameCycle = 0;
+
+ _quit = false;
+}
+
+Sword2Engine::~Sword2Engine() {
+ delete _debugger;
+ delete _sound;
+ delete _fontRenderer;
+ delete _screen;
+ delete _mouse;
+ delete _logic;
+ delete _resman;
+ delete _memory;
+}
+
+void Sword2Engine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+
+#ifdef _WIN32_WCE
+ if (isSmartphone())
+ return;
+#endif
+
+ // Unless an error -originated- within the debugger, spawn the
+ // debugger. Otherwise exit out normally.
+ if (_debugger && !_debugger->isAttached()) {
+ // (Print it again in case debugger segfaults)
+ printf("%s\n", buf2);
+ _debugger->attach(buf2);
+ _debugger->onFrame();
+ }
+}
+
+void Sword2Engine::registerDefaultSettings() {
+ ConfMan.registerDefault("music_mute", false);
+ ConfMan.registerDefault("speech_mute", false);
+ ConfMan.registerDefault("sfx_mute", false);
+ ConfMan.registerDefault("gfx_details", 2);
+ ConfMan.registerDefault("subtitles", false);
+ ConfMan.registerDefault("reverse_stereo", false);
+}
+
+void Sword2Engine::readSettings() {
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ setSubtitles(ConfMan.getBool("subtitles"));
+ _sound->muteMusic(ConfMan.getBool("music_mute"));
+ _sound->muteSpeech(ConfMan.getBool("speech_mute"));
+ _sound->muteFx(ConfMan.getBool("sfx_mute"));
+ _sound->setReverseStereo(ConfMan.getBool("reverse_stereo"));
+ _mouse->setObjectLabels(ConfMan.getBool("object_labels"));
+ _screen->setRenderLevel(ConfMan.getInt("gfx_details"));
+}
+
+void Sword2Engine::writeSettings() {
+ ConfMan.set("music_volume", _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType));
+ ConfMan.set("speech_volume", _mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType));
+ ConfMan.set("sfx_volume", _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType));
+ ConfMan.set("music_mute", _sound->isMusicMute());
+ ConfMan.set("speech_mute", _sound->isSpeechMute());
+ ConfMan.set("sfx_mute", _sound->isFxMute());
+ ConfMan.set("gfx_details", _screen->getRenderLevel());
+ ConfMan.set("subtitles", getSubtitles());
+ ConfMan.set("object_labels", _mouse->getObjectLabels());
+ ConfMan.set("reverse_stereo", _sound->isReverseStereo());
+
+ ConfMan.flushToDisk();
+}
+
+/**
+ * The global script variables and player object should be kept open throughout
+ * the game, so that they are never expelled by the resource manager.
+ */
+
+void Sword2Engine::setupPersistentResources() {
+ _logic->_scriptVars = _resman->openResource(1) + ResHeader::size();
+ _resman->openResource(CUR_PLAYER_ID);
+}
+
+int Sword2Engine::init(GameDetector &detector) {
+ // Get some falling RAM and put it in your pocket, never let it slip
+ // away
+
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ _screen = new Screen(this, 640, 480);
+ _system->endGFXTransaction();
+
+ // Create the debugger as early as possible (but not before the
+ // screen object!) so that errors can be displayed in it. In
+ // particular, we want errors about missing files to be clearly
+ // visible to the user.
+
+ _debugger = new Debugger(this);
+
+ _memory = new MemoryManager(this);
+ _resman = new ResourceManager(this);
+ _logic = new Logic(this);
+ _fontRenderer = new FontRenderer(this);
+ _sound = new Sound(this);
+ _mouse = new Mouse(this);
+
+ // Setup mixer
+ if (!_mixer->isReady())
+ warning("Sound initialization failed");
+
+ registerDefaultSettings();
+ readSettings();
+
+ initStartMenu();
+
+ // During normal gameplay, we care neither about mouse button releases
+ // nor the scroll wheel.
+ setInputEventFilter(RD_LEFTBUTTONUP | RD_RIGHTBUTTONUP | RD_WHEELUP | RD_WHEELDOWN);
+
+ setupPersistentResources();
+ initialiseFontResourceFlags();
+
+ if (_features & GF_DEMO)
+ _logic->writeVar(DEMO, 1);
+ else
+ _logic->writeVar(DEMO, 0);
+
+ if (_saveSlot != -1) {
+ if (saveExists(_saveSlot))
+ restoreGame(_saveSlot);
+ else {
+ RestoreDialog dialog(this);
+ if (!dialog.runModal())
+ startGame();
+ }
+ } else if (!_bootParam && saveExists()) {
+ int32 pars[2] = { 221, FX_LOOP };
+ bool result;
+
+ _mouse->setMouse(NORMAL_MOUSE_ID);
+ _logic->fnPlayMusic(pars);
+
+ StartDialog dialog(this);
+
+ result = (dialog.runModal() != 0);
+
+ // If the game is started from the beginning, the cutscene
+ // player will kill the music for us. Otherwise, the restore
+ // will either have killed the music, or done a crossfade.
+
+ if (_quit)
+ return 0;
+
+ if (result)
+ startGame();
+ } else
+ startGame();
+
+ _screen->initialiseRenderCycle();
+
+ return 0;
+}
+
+int Sword2Engine::go() {
+ while (1) {
+ if (_debugger->isAttached())
+ _debugger->onFrame();
+
+#ifdef SWORD2_DEBUG
+ if (_stepOneCycle) {
+ pauseGame();
+ _stepOneCycle = false;
+ }
+#endif
+
+ KeyboardEvent *ke = keyboardEvent();
+
+ if (ke) {
+ if ((ke->modifiers == OSystem::KBD_CTRL && ke->keycode == 'd') || ke->ascii == '#' || ke->ascii == '~') {
+ _debugger->attach();
+ } else if (ke->modifiers == 0 || ke->modifiers == OSystem::KBD_SHIFT) {
+ switch (ke->keycode) {
+ case 'p':
+ if (_gamePaused)
+ unpauseGame();
+ else
+ pauseGame();
+ break;
+ case 'c':
+ if (!_logic->readVar(DEMO) && !_mouse->isChoosing()) {
+ ScreenInfo *screenInfo = _screen->getScreenInfo();
+ _logic->fnPlayCredits(NULL);
+ screenInfo->new_palette = 99;
+ }
+ break;
+#ifdef SWORD2_DEBUG
+ case ' ':
+ if (_gamePaused) {
+ _stepOneCycle = true;
+ unpauseGame();
+ }
+ break;
+ case 's':
+ _renderSkip = !_renderSkip;
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+
+ // skip GameCycle if we're paused
+ if (!_gamePaused) {
+ _gameCycle++;
+ gameCycle();
+ }
+
+ // We can't use this as termination condition for the loop,
+ // because we want the break to happen before updating the
+ // screen again.
+
+ if (_quit)
+ break;
+
+ // creates the debug text blocks
+ _debugger->buildDebugText();
+
+#ifdef SWORD2_DEBUG
+ // if not in console & '_renderSkip' is set, only render
+ // display once every 4 game-cycles
+
+ if (!_renderSkip || (_gameCycle % 4) == 0)
+ _screen->buildDisplay();
+#else
+ _screen->buildDisplay();
+#endif
+ }
+
+ return 0;
+}
+
+void Sword2Engine::closeGame() {
+ _quit = true;
+}
+
+void Sword2Engine::restartGame() {
+ ScreenInfo *screenInfo = _screen->getScreenInfo();
+ uint32 temp_demo_flag;
+
+ _mouse->closeMenuImmediately();
+
+ // Restart the game. To do this, we must...
+
+ // Stop music instantly!
+ _sound->stopMusic(true);
+
+ // In case we were dead - well we're not anymore!
+ _logic->writeVar(DEAD, 0);
+
+ // Restart the game. Clear all memory and reset the globals
+ temp_demo_flag = _logic->readVar(DEMO);
+
+ // Remove all resources from memory, including player object and
+ // global variables
+ _resman->removeAll();
+
+ // Reopen global variables resource and player object
+ setupPersistentResources();
+
+ _logic->writeVar(DEMO, temp_demo_flag);
+
+ // Free all the route memory blocks from previous game
+ _logic->_router->freeAllRouteMem();
+
+ // Call the same function that first started us up
+ startGame();
+
+ // Prime system with a game cycle
+
+ // Reset the graphic 'BuildUnit' list before a new logic list
+ // (see fnRegisterFrame)
+ _screen->resetRenderLists();
+
+ // Reset the mouse hot-spot list (see fnRegisterMouse and
+ // fnRegisterFrame)
+ _mouse->resetMouseList();
+
+ _mouse->closeMenuImmediately();
+
+ // FOR THE DEMO - FORCE THE SCROLLING TO BE RESET!
+ // - this is taken from fnInitBackground
+ // switch on scrolling (2 means first time on screen)
+ screenInfo->scroll_flag = 2;
+
+ if (_logic->processSession())
+ error("restart 1st cycle failed??");
+
+ // So palette not restored immediately after control panel - we want
+ // to fade up instead!
+ screenInfo->new_palette = 99;
+}
+
+bool Sword2Engine::checkForMouseEvents() {
+ return _mouseEvent.pending;
+}
+
+MouseEvent *Sword2Engine::mouseEvent() {
+ if (!_mouseEvent.pending)
+ return NULL;
+
+ _mouseEvent.pending = false;
+ return &_mouseEvent;
+}
+
+KeyboardEvent *Sword2Engine::keyboardEvent() {
+ if (!_keyboardEvent.pending)
+ return NULL;
+
+ _keyboardEvent.pending = false;
+ return &_keyboardEvent;
+}
+
+uint32 Sword2Engine::setInputEventFilter(uint32 filter) {
+ uint32 oldFilter = _inputEventFilter;
+
+ _inputEventFilter = filter;
+ return oldFilter;
+}
+
+/**
+ * Clear the input events. This is so that we won't get any keyboard repeat
+ * right after using the debugging console.
+ */
+
+void Sword2Engine::clearInputEvents() {
+ _keyboardEvent.pending = false;
+ _keyboardEvent.repeat = 0;
+ _mouseEvent.pending = false;
+}
+
+/**
+ * OSystem Event Handler. Full of cross platform goodness and 99% fat free!
+ */
+
+void Sword2Engine::parseInputEvents() {
+ OSystem::Event event;
+
+ uint32 now = _system->getMillis();
+
+ while (_system->pollEvent(event)) {
+ switch (event.type) {
+ case OSystem::EVENT_KEYDOWN:
+ if (!(_inputEventFilter & RD_KEYDOWN)) {
+ _keyboardEvent.pending = true;
+ _keyboardEvent.repeat = now + 400;
+ _keyboardEvent.ascii = event.kbd.ascii;
+ _keyboardEvent.keycode = event.kbd.keycode;
+ _keyboardEvent.modifiers = event.kbd.flags;
+ }
+ break;
+ case OSystem::EVENT_KEYUP:
+ _keyboardEvent.repeat = 0;
+ break;
+ case OSystem::EVENT_MOUSEMOVE:
+ if (!(_inputEventFilter & RD_KEYDOWN)) {
+ _mouse->setPos(event.mouse.x, event.mouse.y - MENUDEEP);
+ }
+ break;
+ case OSystem::EVENT_LBUTTONDOWN:
+ if (!(_inputEventFilter & RD_LEFTBUTTONDOWN)) {
+ _mouseEvent.pending = true;
+ _mouseEvent.buttons = RD_LEFTBUTTONDOWN;
+ }
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+ if (!(_inputEventFilter & RD_RIGHTBUTTONDOWN)) {
+ _mouseEvent.pending = true;
+ _mouseEvent.buttons = RD_RIGHTBUTTONDOWN;
+ }
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ if (!(_inputEventFilter & RD_LEFTBUTTONUP)) {
+ _mouseEvent.pending = true;
+ _mouseEvent.buttons = RD_LEFTBUTTONUP;
+ }
+ break;
+ case OSystem::EVENT_RBUTTONUP:
+ if (!(_inputEventFilter & RD_RIGHTBUTTONUP)) {
+ _mouseEvent.pending = true;
+ _mouseEvent.buttons = RD_RIGHTBUTTONUP;
+ }
+ break;
+ case OSystem::EVENT_WHEELUP:
+ if (!(_inputEventFilter & RD_WHEELUP)) {
+ _mouseEvent.pending = true;
+ _mouseEvent.buttons = RD_WHEELUP;
+ }
+ break;
+ case OSystem::EVENT_WHEELDOWN:
+ if (!(_inputEventFilter & RD_WHEELDOWN)) {
+ _mouseEvent.pending = true;
+ _mouseEvent.buttons = RD_WHEELDOWN;
+ }
+ break;
+ case OSystem::EVENT_QUIT:
+ closeGame();
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Handle keyboard auto-repeat
+ if (!_keyboardEvent.pending && _keyboardEvent.repeat && now >= _keyboardEvent.repeat) {
+ _keyboardEvent.pending = true;
+ _keyboardEvent.repeat = now + 100;
+ }
+}
+
+void Sword2Engine::gameCycle() {
+ // Do one game cycle, that is run the logic session until a full loop
+ // has been performed.
+
+ if (_logic->getRunList()) {
+ do {
+ // Reset the 'BuildUnit' and mouse hot-spot lists
+ // before each new logic list. The service scripts
+ // will fill thrm through fnRegisterFrame() and
+ // fnRegisterMouse().
+
+ _screen->resetRenderLists();
+ _mouse->resetMouseList();
+
+ // Keep going as long as new lists keep getting put in
+ // - i.e. screen changes.
+ } while (_logic->processSession());
+ } else {
+ // Start the console and print the start options perhaps?
+ _debugger->attach("AWAITING START COMMAND: (Enter 's 1' then 'q' to start from beginning)");
+ }
+
+ // If this screen is wide, recompute the scroll offsets every cycle
+ ScreenInfo *screenInfo = _screen->getScreenInfo();
+
+ if (screenInfo->scroll_flag)
+ _screen->setScrolling();
+
+ _mouse->mouseEngine();
+ _sound->processFxQueue();
+}
+
+void Sword2Engine::startGame() {
+ // Boot the game straight into a start script. It's always George's
+ // script #1, but with different ScreenManager objects depending on
+ // if it's the demo or the full game, or if we're using a boot param.
+
+ int screen_manager_id = 0;
+
+ debug(5, "startGame() STARTING:");
+
+ if (!_bootParam) {
+ if (_logic->readVar(DEMO))
+ screen_manager_id = 19; // DOCKS SECTION START
+ else
+ screen_manager_id = 949; // INTRO & PARIS START
+ } else {
+ // FIXME this could be validated against startup.inf for valid
+ // numbers to stop people shooting themselves in the foot
+
+ if (_bootParam != 0)
+ screen_manager_id = _bootParam;
+ }
+
+ _logic->runResObjScript(screen_manager_id, CUR_PLAYER_ID, 1);
+}
+
+// FIXME: Move this to some better place?
+
+void Sword2Engine::sleepUntil(uint32 time) {
+ while (getMillis() < time) {
+ // Make sure menu animations and fades don't suffer, but don't
+ // redraw the entire scene.
+ _mouse->processMenu();
+ _screen->updateDisplay(false);
+ _system->delayMillis(10);
+ }
+}
+
+void Sword2Engine::pauseGame() {
+ // Don't allow Pause while screen fading or while black
+ if (_screen->getFadeStatus() != RDFADE_NONE)
+ return;
+
+ _sound->pauseAllSound();
+ _mouse->pauseGame();
+
+ // If render level is at max, turn it down because palette-matching
+ // won't work when the palette is dimmed.
+
+ if (_screen->getRenderLevel() == 3) {
+ _screen->setRenderLevel(2);
+ _graphicsLevelFudged = true;
+ }
+
+#ifdef SWORD2_DEBUG
+ // Don't dim it if we're single-stepping through frames
+ // dim the palette during the pause
+
+ if (!_stepOneCycle)
+ _screen->dimPalette();
+#else
+ _screen->dimPalette();
+#endif
+
+ _gamePaused = true;
+}
+
+void Sword2Engine::unpauseGame() {
+ _mouse->unpauseGame();
+ _sound->unpauseAllSound();
+
+ // Put back game screen palette; see build_display.cpp
+ _screen->setFullPalette(-1);
+
+ // If graphics level at max, turn up again
+ if (_graphicsLevelFudged) {
+ _screen->setRenderLevel(3);
+ _graphicsLevelFudged = false;
+ }
+
+ _gamePaused = false;
+
+ // If mouse is about or we're in a chooser menu
+ if (!_mouse->getMouseStatus() || _mouse->isChoosing())
+ _mouse->setMouse(NORMAL_MOUSE_ID);
+}
+
+uint32 Sword2Engine::getMillis() {
+ return _system->getMillis();
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/sword2.h b/engines/sword2/sword2.h
new file mode 100644
index 0000000000..bcb80ec957
--- /dev/null
+++ b/engines/sword2/sword2.h
@@ -0,0 +1,240 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef _SWORD2
+#define _SWORD2
+
+// Enable this to make it possible to clear the mouse cursor luggage by
+// right-clicking. The original didn't do this, but it feels natural to me.
+// However, I'm afraid that it'll interfer badly with parts of the game, so
+// for now I'll keep it disabled.
+
+#define RIGHT_CLICK_CLEARS_LUGGAGE 0
+
+#include "base/engine.h"
+
+#include "common/util.h"
+
+#include "sword2/build_display.h"
+#include "sword2/header.h"
+#include "sword2/icons.h"
+#include "sword2/object.h"
+#include "sword2/save_rest.h"
+
+#define MAX_starts 100
+#define MAX_description 100
+
+class GameDetector;
+class OSystem;
+
+namespace Sword2 {
+
+enum {
+ GF_DEMO = 1 << 0
+};
+
+class MemoryManager;
+class ResourceManager;
+class Sound;
+class Screen;
+class Mouse;
+class Logic;
+class FontRenderer;
+class Gui;
+class Debugger;
+
+enum {
+ RD_LEFTBUTTONDOWN = 0x01,
+ RD_LEFTBUTTONUP = 0x02,
+ RD_RIGHTBUTTONDOWN = 0x04,
+ RD_RIGHTBUTTONUP = 0x08,
+ RD_WHEELUP = 0x10,
+ RD_WHEELDOWN = 0x20,
+ RD_KEYDOWN = 0x40,
+ RD_MOUSEMOVE = 0x80
+};
+
+struct MouseEvent {
+ bool pending;
+ uint16 buttons;
+};
+
+struct KeyboardEvent {
+ bool pending;
+ uint32 repeat;
+ uint16 ascii;
+ int keycode;
+ int modifiers;
+};
+
+struct StartUp {
+ char description[MAX_description];
+
+ // id of screen manager object
+ uint32 start_res_id;
+
+ // Tell the manager which startup you want (if there are more than 1)
+ // (i.e more than 1 entrance to a screen and/or separate game boots)
+ uint32 key;
+};
+
+class Sword2Engine : public Engine {
+private:
+ uint32 _inputEventFilter;
+
+ // The event "buffers"
+ MouseEvent _mouseEvent;
+ KeyboardEvent _keyboardEvent;
+
+ uint32 _bootParam;
+ int32 _saveSlot;
+
+ void getPlayerStructures();
+ void putPlayerStructures();
+
+ uint32 saveData(uint16 slotNo, byte *buffer, uint32 bufferSize);
+ uint32 restoreData(uint16 slotNo, byte *buffer, uint32 bufferSize);
+
+ uint32 calcChecksum(byte *buffer, uint32 size);
+
+ void pauseGame();
+ void unpauseGame();
+
+ uint32 _totalStartups;
+ uint32 _totalScreenManagers;
+ uint32 _startRes;
+
+ bool _useSubtitles;
+
+ StartUp _startList[MAX_starts];
+
+public:
+ Sword2Engine(GameDetector *detector, OSystem *syst);
+ ~Sword2Engine();
+ int go();
+ int init(GameDetector &detector);
+
+ void registerDefaultSettings();
+ void readSettings();
+ void writeSettings();
+
+ void setupPersistentResources();
+
+ bool getSubtitles() { return _useSubtitles; }
+ void setSubtitles(bool b) { _useSubtitles = b; }
+
+ bool _quit;
+
+ uint32 _features;
+ Common::String _targetName; // target name for saves
+
+ MemoryManager *_memory;
+ ResourceManager *_resman;
+ Sound *_sound;
+ Screen *_screen;
+ Mouse *_mouse;
+ Logic *_logic;
+ FontRenderer *_fontRenderer;
+
+ Debugger *_debugger;
+
+ Common::RandomSource _rnd;
+
+ uint32 _speechFontId;
+ uint32 _controlsFontId;
+ uint32 _redFontId;
+
+ uint32 setInputEventFilter(uint32 filter);
+
+ void clearInputEvents();
+ void parseInputEvents();
+
+ bool checkForMouseEvents();
+ MouseEvent *mouseEvent();
+ KeyboardEvent *keyboardEvent();
+
+ bool _wantSfxDebug;
+
+ int32 _gameCycle;
+
+#ifdef SWORD2_DEBUG
+ bool _renderSkip;
+ bool _stepOneCycle;
+#endif
+
+#if RIGHT_CLICK_CLEARS_LUGGAGE
+ bool heldIsInInventory();
+#endif
+
+ byte *fetchPalette(byte *screenFile);
+ byte *fetchScreenHeader(byte *screenFile);
+ byte *fetchLayerHeader(byte *screenFile, uint16 layerNo);
+ byte *fetchShadingMask(byte *screenFile);
+
+ byte *fetchAnimHeader(byte *animFile);
+ byte *fetchCdtEntry(byte *animFile, uint16 frameNo);
+ byte *fetchFrameHeader(byte *animFile, uint16 frameNo);
+ byte *fetchBackgroundParallaxLayer(byte *screenFile, int layer);
+ byte *fetchBackgroundLayer(byte *screenFile);
+ byte *fetchForegroundParallaxLayer(byte *screenFile, int layer);
+ byte *fetchTextLine(byte *file, uint32 text_line);
+ bool checkTextLine(byte *file, uint32 text_line);
+ byte *fetchPaletteMatchTable(byte *screenFile);
+
+ uint32 saveGame(uint16 slotNo, byte *description);
+ uint32 restoreGame(uint16 slotNo);
+ uint32 getSaveDescription(uint16 slotNo, byte *description);
+ bool saveExists();
+ bool saveExists(uint16 slotNo);
+ uint32 restoreFromBuffer(byte *buffer, uint32 size);
+ uint32 findBufferSize();
+
+ bool _gamePaused;
+ bool _graphicsLevelFudged;
+
+ void startGame();
+ void gameCycle();
+ void closeGame();
+ void restartGame();
+
+ void sleepUntil(uint32 time);
+
+ void errorString(const char *buf_input, char *buf_output);
+ void initialiseFontResourceFlags();
+ void initialiseFontResourceFlags(uint8 language);
+
+ bool initStartMenu();
+ void registerStartPoint(int32 key, char *name);
+
+ uint32 getNumStarts() { return _totalStartups; }
+ uint32 getNumScreenManagers() { return _totalScreenManagers; }
+ StartUp *getStartList() { return _startList; }
+
+ void runStart(int start);
+
+ // Convenience alias for OSystem::getMillis().
+ // This is a bit hackish, of course :-).
+ uint32 getMillis();
+};
+
+} // End of namespace Sword2
+
+#endif
diff --git a/engines/sword2/sync.cpp b/engines/sword2/sync.cpp
new file mode 100644
index 0000000000..205e273873
--- /dev/null
+++ b/engines/sword2/sync.cpp
@@ -0,0 +1,76 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+
+namespace Sword2 {
+
+/**
+ * Clear any syncs registered for this id. Call this just after the id has been
+ * processed. Theoretically there could be more than one sync waiting for us,
+ * so clear the lot.
+ */
+
+void Logic::clearSyncs(uint32 id) {
+ for (int i = 0; i < ARRAYSIZE(_syncList); i++) {
+ if (_syncList[i].id == id) {
+ debug(5, "removing sync %d for %d", i, id);
+ _syncList[i].id = 0;
+ }
+ }
+}
+
+void Logic::sendSync(uint32 id, uint32 sync) {
+ for (int i = 0; i < ARRAYSIZE(_syncList); i++) {
+ if (_syncList[i].id == 0) {
+ debug(5, "%d sends sync %d to %d", readVar(ID), sync, id);
+ _syncList[i].id = id;
+ _syncList[i].sync = sync;
+ return;
+ }
+ }
+
+ // The original code didn't even check for this condition, so maybe
+ // it should be a fatal error?
+
+ warning("No free sync slot");
+}
+
+/**
+ * Check for a sync waiting for this character. Called from fnAnim() to see if
+ * animation is to be finished. Returns an index into _syncList[], or -1.
+ */
+
+int Logic::getSync() {
+ uint32 id = readVar(ID);
+
+ for (int i = 0; i < ARRAYSIZE(_syncList); i++) {
+ if (_syncList[i].id == id)
+ return i;
+ }
+
+ return -1;
+}
+
+} // End of namespace Sword2
diff --git a/engines/sword2/walker.cpp b/engines/sword2/walker.cpp
new file mode 100644
index 0000000000..82ef80f65f
--- /dev/null
+++ b/engines/sword2/walker.cpp
@@ -0,0 +1,454 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// WALKER.CPP by James (14nov96)
+
+// Functions for moving megas about the place & also for keeping tabs on them
+
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/interpreter.h"
+#include "sword2/logic.h"
+#include "sword2/resman.h"
+#include "sword2/router.h"
+
+namespace Sword2 {
+
+void Router::setStandbyCoords(int16 x, int16 y, uint8 dir) {
+ assert(dir <= 7);
+
+ _standbyX = x;
+ _standbyY = y;
+ _standbyDir = dir;
+}
+
+/**
+ * Work out direction from start to dest.
+ */
+
+// Used in whatTarget(); not valid for all megas
+#define diagonalx 36
+#define diagonaly 8
+
+int Router::whatTarget(int startX, int startY, int destX, int destY) {
+ int deltaX = destX - startX;
+ int deltaY = destY - startY;
+
+ // 7 0 1
+ // 6 2
+ // 5 4 3
+
+ // Flat route
+
+ if (ABS(deltaY) * diagonalx < ABS(deltaX) * diagonaly / 2)
+ return (deltaX > 0) ? 2 : 6;
+
+ // Vertical route
+
+ if (ABS(deltaY) * diagonalx / 2 > ABS(deltaX) * diagonaly)
+ return (deltaY > 0) ? 4 : 0;
+
+ // Diagonal route
+
+ if (deltaX > 0)
+ return (deltaY > 0) ? 3 : 1;
+
+ return (deltaY > 0) ? 5 : 7;
+}
+
+/**
+ * Walk meta to (x,y,dir). Set RESULT to 0 if it succeeded. Otherwise, set
+ * RESULT to 1. Return true if the mega has finished walking.
+ */
+
+int Router::doWalk(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, int16 target_x, int16 target_y, uint8 target_dir) {
+ ObjectLogic obLogic(ob_logic);
+ ObjectGraphic obGraph(ob_graph);
+ ObjectMega obMega(ob_mega);
+
+ // If this is the start of the walk, calculate the route.
+
+ if (obLogic.getLooping() == 0) {
+ // If we're already there, don't even bother allocating
+ // memory and calling the router, just quit back & continue
+ // the script! This avoids an embarassing mega stand frame
+ // appearing for one cycle when we're already in position for
+ // an anim eg. repeatedly clicking on same object to repeat
+ // an anim - no mega frame will appear in between runs of the
+ // anim.
+
+ if (obMega.getFeetX() == target_x && obMega.getFeetY() == target_y && obMega.getCurDir() == target_dir) {
+ _vm->_logic->writeVar(RESULT, 0);
+ return IR_CONT;
+ }
+
+ assert(target_dir <= 8);
+
+ obMega.setWalkPc(0);
+
+ // Set up mem for _walkData in route_slots[] & set mega's
+ // 'route_slot_id' accordingly
+ allocateRouteMem();
+
+ int32 route = routeFinder(ob_mega, ob_walkdata, target_x, target_y, target_dir);
+
+ // 0 = can't make route to target
+ // 1 = created route
+ // 2 = zero route but may need to turn
+
+ if (route != 1 && route != 2) {
+ freeRouteMem();
+ _vm->_logic->writeVar(RESULT, 1);
+ return IR_CONT;
+ }
+
+ // Walk is about to start
+
+ obMega.setIsWalking(1);
+ obLogic.setLooping(1);
+ obGraph.setAnimResource(obMega.getMegasetRes());
+ } else if (_vm->_logic->readVar(EXIT_FADING) && _vm->_screen->getFadeStatus() == RDFADE_BLACK) {
+ // Double clicked an exit, and the screen has faded down to
+ // black. Ok, that's it. Back to script and change screen.
+
+ // We have to clear te EXIT_CLICK_ID variable in case there's a
+ // walk instruction on the new screen, or it'd be cut short.
+
+ freeRouteMem();
+
+ obLogic.setLooping(0);
+ obMega.setIsWalking(0);
+ _vm->_logic->writeVar(EXIT_CLICK_ID, 0);
+ _vm->_logic->writeVar(RESULT, 0);
+
+ return IR_CONT;
+ }
+
+ // Get pointer to walkanim & current frame position
+
+ WalkData *walkAnim = getRouteMem();
+ int32 walk_pc = obMega.getWalkPc();
+
+ // If stopping the walk early, overwrite the next step with a
+ // slow-out, then finish
+
+ if (_vm->_logic->checkEventWaiting() && walkAnim[walk_pc].step == 0 && walkAnim[walk_pc + 1].step == 1) {
+ // At the beginning of a step
+ earlySlowOut(ob_mega, ob_walkdata);
+ }
+
+ // Get new frame of walk
+
+ obGraph.setAnimPc(walkAnim[walk_pc].frame);
+ obMega.setCurDir(walkAnim[walk_pc].dir);
+ obMega.setFeetX(walkAnim[walk_pc].x);
+ obMega.setFeetY(walkAnim[walk_pc].y);
+
+ // Is the NEXT frame is the end-marker (512) of the walk sequence?
+
+ if (walkAnim[walk_pc + 1].frame != 512) {
+ // No, it wasn't. Increment the walk-anim frame number and
+ // come back next cycle.
+ obMega.setWalkPc(obMega.getWalkPc() + 1);
+ return IR_REPEAT;
+ }
+
+ // We have reached the end-marker, which means we can return to the
+ // script just as the final (stand) frame of the walk is set.
+
+ freeRouteMem();
+ obLogic.setLooping(0);
+ obMega.setIsWalking(0);
+
+ // If George's walk has been interrupted to run a new action script for
+ // instance or Nico's walk has been interrupted by player clicking on
+ // her to talk
+
+ // There used to be code here for checking if two megas were colliding,
+ // but it had been commented out, and it was only run if a function
+ // that always returned zero returned non-zero.
+
+ if (_vm->_logic->checkEventWaiting()) {
+ _vm->_logic->startEvent();
+ _vm->_logic->writeVar(RESULT, 1);
+ return IR_TERMINATE;
+ }
+
+ _vm->_logic->writeVar(RESULT, 0);
+
+ // CONTINUE the script so that RESULT can be checked! Also, if an anim
+ // command follows the fnWalk command, the 1st frame of the anim (which
+ // is always a stand frame itself) can replace the final stand frame of
+ // the walk, to hide the slight difference between the shrinking on the
+ // mega frames and the pre-shrunk anim start-frame.
+
+ return IR_CONT;
+}
+
+/**
+ * Walk mega to start position of anim
+ */
+
+int Router::walkToAnim(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 animRes) {
+ int16 target_x = 0;
+ int16 target_y = 0;
+ uint8 target_dir = 0;
+
+ // Walkdata is needed for earlySlowOut if player clicks elsewhere
+ // during the walk.
+
+ // If this is the start of the walk, read anim file to get start coords
+
+ ObjectLogic obLogic(ob_logic);
+
+ if (obLogic.getLooping() == 0) {
+ byte *anim_file = _vm->_resman->openResource(animRes);
+ AnimHeader anim_head;
+
+ anim_head.read(_vm->fetchAnimHeader(anim_file));
+
+ target_x = anim_head.feetStartX;
+ target_y = anim_head.feetStartY;
+ target_dir = anim_head.feetStartDir;
+
+ _vm->_resman->closeResource(animRes);
+
+ // If start coords not yet set in anim header, use the standby
+ // coords (which should be set beforehand in the script).
+
+ if (target_x == 0 && target_y == 0) {
+ target_x = _standbyX;
+ target_y = _standbyY;
+ target_dir = _standbyDir;
+ }
+
+ assert(target_dir <= 7);
+ }
+
+ return doWalk(ob_logic, ob_graph, ob_mega, ob_walkdata, target_x, target_y, target_dir);
+}
+
+/**
+ * Route to the left or right hand side of target id, if possible.
+ */
+
+int Router::walkToTalkToMega(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 megaId, uint32 separation) {
+ ObjectMega obMega(ob_mega);
+
+ int16 target_x = 0;
+ int16 target_y = 0;
+ uint8 target_dir = 0;
+
+ // If this is the start of the walk, calculate the route.
+
+ ObjectLogic obLogic(ob_logic);
+
+ if (obLogic.getLooping() == 0) {
+ assert(_vm->_resman->fetchType(megaId) == GAME_OBJECT);
+
+ // Call the base script. This is the graphic/mouse service
+ // call, and will set _engineMega to the ObjectMega of mega we
+ // want to route to.
+
+ _vm->_logic->runResScript(megaId, 3);
+
+ ObjectMega targetMega(_vm->_logic->getEngineMega());
+
+ // Stand exactly beside the mega, ie. at same y-coord
+ target_y = targetMega.getFeetY();
+
+ int scale = obMega.calcScale();
+ int mega_separation = (separation * scale) / 256;
+
+ debug(4, "Target is at (%d, %d), separation %d", targetMega.getFeetX(), targetMega.getFeetY(), mega_separation);
+
+ if (targetMega.getFeetX() < obMega.getFeetX()) {
+ // Target is left of us, so aim to stand to their
+ // right. Face down_left
+
+ target_x = targetMega.getFeetX() + mega_separation;
+ target_dir = 5;
+ } else {
+ // Ok, must be right of us so aim to stand to their
+ // left. Face down_right.
+
+ target_x = targetMega.getFeetX() - mega_separation;
+ target_dir = 3;
+ }
+ }
+
+ return doWalk(ob_logic, ob_graph, ob_mega, ob_walkdata, target_x, target_y, target_dir);
+}
+/**
+ * Turn mega to the specified direction. Just needs to call doWalk() with
+ * current feet coords, so router can produce anim of turn frames.
+ */
+
+int Router::doFace(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint8 target_dir) {
+ int16 target_x = 0;
+ int16 target_y = 0;
+
+ // If this is the start of the turn, get the mega's current feet
+ // coords + the required direction
+
+ ObjectLogic obLogic(ob_logic);
+
+ if (obLogic.getLooping() == 0) {
+ assert(target_dir <= 7);
+
+ ObjectMega obMega(ob_mega);
+
+ target_x = obMega.getFeetX();
+ target_y = obMega.getFeetY();
+ }
+
+ return doWalk(ob_logic, ob_graph, ob_mega, ob_walkdata, target_x, target_y, target_dir);
+}
+
+/**
+ * Turn mega to face point (x,y) on the floor
+ */
+
+int Router::faceXY(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, int16 target_x, int16 target_y) {
+ uint8 target_dir = 0;
+
+ // If this is the start of the turn, get the mega's current feet
+ // coords + the required direction
+
+ ObjectLogic obLogic(ob_logic);
+
+ if (obLogic.getLooping() == 0) {
+ ObjectMega obMega(ob_mega);
+
+ target_dir = whatTarget(obMega.getFeetX(), obMega.getFeetY(), target_x, target_y);
+ }
+
+ return doFace(ob_logic, ob_graph, ob_mega, ob_walkdata, target_dir);
+}
+
+/**
+ * Turn mega to face another mega.
+ */
+
+int Router::faceMega(byte *ob_logic, byte *ob_graph, byte *ob_mega, byte *ob_walkdata, uint32 megaId) {
+ uint8 target_dir = 0;
+
+ // If this is the start of the walk, decide where to walk to.
+
+ ObjectLogic obLogic(ob_logic);
+
+ if (obLogic.getLooping() == 0) {
+ assert(_vm->_resman->fetchType(megaId) == GAME_OBJECT);
+
+ // Call the base script. This is the graphic/mouse service
+ // call, and will set _engineMega to the ObjectMega of mega we
+ // want to turn to face.
+
+ _vm->_logic->runResScript(megaId, 3);
+
+ ObjectMega obMega(ob_mega);
+ ObjectMega targetMega(_vm->_logic->getEngineMega());
+
+ target_dir = whatTarget(obMega.getFeetX(), obMega.getFeetY(), targetMega.getFeetX(), targetMega.getFeetY());
+ }
+
+ return doFace(ob_logic, ob_graph, ob_mega, ob_walkdata, target_dir);
+}
+
+/**
+ * Stand mega at (x,y,dir)
+ * Sets up the graphic object, but also needs to set the new 'current_dir' in
+ * the mega object, so the router knows in future
+ */
+
+void Router::standAt(byte *ob_graph, byte *ob_mega, int32 x, int32 y, int32 dir) {
+ assert(dir >= 0 && dir <= 7);
+
+ ObjectGraphic obGraph(ob_graph);
+ ObjectMega obMega(ob_mega);
+
+ // Set up the stand frame & set the mega's new direction
+
+ obMega.setFeetX(x);
+ obMega.setFeetY(y);
+ obMega.setCurDir(dir);
+
+ // Mega-set animation file
+ obGraph.setAnimResource(obMega.getMegasetRes());
+
+ // Dir + first stand frame (always frame 96)
+ obGraph.setAnimPc(dir + 96);
+}
+
+/**
+ * Stand mega at end position of anim
+ */
+
+void Router::standAfterAnim(byte *ob_graph, byte *ob_mega, uint32 animRes) {
+ byte *anim_file = _vm->_resman->openResource(animRes);
+ AnimHeader anim_head;
+
+ anim_head.read(_vm->fetchAnimHeader(anim_file));
+
+ int32 x = anim_head.feetEndX;
+ int32 y = anim_head.feetEndY;
+ int32 dir = anim_head.feetEndDir;
+
+ _vm->_resman->closeResource(animRes);
+
+ // If start coords not available either use the standby coords (which
+ // should be set beforehand in the script)
+
+ if (x == 0 && y == 0) {
+ x = _standbyX;
+ y = _standbyY;
+ dir = _standbyDir;
+ }
+
+ standAt(ob_graph, ob_mega, x, y, dir);
+}
+
+void Router::standAtAnim(byte *ob_graph, byte *ob_mega, uint32 animRes) {
+ byte *anim_file = _vm->_resman->openResource(animRes);
+ AnimHeader anim_head;
+
+ anim_head.read(_vm->fetchAnimHeader(anim_file));
+
+ int32 x = anim_head.feetStartX;
+ int32 y = anim_head.feetStartY;
+ int32 dir = anim_head.feetStartDir;
+
+ _vm->_resman->closeResource(animRes);
+
+ // If start coords not available use the standby coords (which should
+ // be set beforehand in the script)
+
+ if (x == 0 && y == 0) {
+ x = _standbyX;
+ y = _standbyY;
+ dir = _standbyDir;
+ }
+
+ standAt(ob_graph, ob_mega, x, y, dir);
+}
+
+} // End of namespace Sword2